Skip to content

Fix big-endian IV initialization in opt8/opt8_lowsize AEAD128 - #30

Open
Scottcjn wants to merge 3 commits into
ascon:mainfrom
Scottcjn:fix-be-iv-opt8
Open

Fix big-endian IV initialization in opt8/opt8_lowsize AEAD128#30
Scottcjn wants to merge 3 commits into
ascon:mainfrom
Scottcjn:fix-be-iv-opt8

Conversation

@Scottcjn

Copy link
Copy Markdown

This addresses #25 for the byte-oriented opt8 and opt8_lowsize AEAD128 implementations.

Root cause

These implementations keep the sponge state as a little-endian byte array: the key and nonce go in as raw bytes (INSERT/memcpy) and the permutation operates on the byte view. The IV, though, is assigned as a numeric 64-bit constant:

if (ASCON_AEAD_RATE == 8) s->x[0] = ASCON_128_IV;

On little-endian the numeric word and the byte-array view agree, so it works. On big-endian they disagree: the IV bytes get laid out reversed relative to the rest of the state, the initial state is wrong, and every test vector fails.

Fix

Route the IV through the same U64TOWORD conversion that LOADBYTES already applies to every other word, so the IV byte layout matches the rest of the state on both endiannesses. U64TOWORD is identity on little-endian, so this is a no-op there.

Verification

Reproduced and verified with the project's own genkat self-test, built with -DREL_FLAGS="-std=c99;-O2;-fomit-frame-pointer" (dropping -march=native for cross builds):

  • native ppc64 big-endian hardware (POWER8 LPAR)
  • big-endian MIPS under qemu

Before the fix opt8/opt8_lowsize fail the KAT; after, they pass. ref passes throughout as a control.

Scope / follow-up

This PR covers the AEAD128 byte-oriented impls. The same root cause affects the Hash256/XOF128 byte-oriented impls (different IV-init path) and the bit-interleaved bi8/bi32 family (which needs an interleaving-aware fix, not just the IV wrap). Happy to extend this PR to those or send follow-ups, whichever you prefer.

The byte-oriented opt8 and opt8_lowsize implementations keep the sponge
state as a little-endian byte array: the key and nonce are inserted as raw
bytes and the permutation operates on the byte view. The IV, however, is
assigned as a numeric 64-bit constant (s->x[0] = ASCON_128_IV). On a
little-endian host the numeric word and the byte-array view agree, so the
code works. On big-endian they disagree, the initial state is laid out
with the IV bytes reversed, and every test vector fails.

Route the IV through the same U64TOWORD conversion that LOADBYTES already
applies to every other word, so the IV byte layout matches the rest of the
state on both endiannesses. U64TOWORD is identity on little-endian, so this
is a no-op there.

Verified against the NIST KAT on native ppc64 big-endian hardware and on
big-endian MIPS under qemu: the genkat self-test goes from failing to
passing for both implementations, with ref unchanged as a control.

Part of ascon#25.
The byte-oriented opt8 and opt8_lowsize implementations of Ascon-Hash256
and Ascon-XOF128 had the same big-endian defect as the AEAD128 ones: the
initial state words come from numeric IV constants (via the IV() macro)
without the U64TOWORD conversion the byte path uses everywhere else, so on
big-endian the initial state is laid out wrong and the KAT fails.

Wrap the IV macro in U64TOWORD so the constants match the byte-array
layout on both endiannesses. No-op on little-endian.

Verified with genkat on big-endian MIPS under qemu: Hash256 and XOF128
opt8/opt8_lowsize go from failing to passing.
@Scottcjn

Copy link
Copy Markdown
Author

Expanded this PR to cover the full byte-oriented opt8 family, since they share the exact same root cause:

  • AEAD128 opt8 / opt8_lowsize (original)
  • Hash256 opt8 / opt8_lowsize
  • XOF128 opt8 / opt8_lowsize

For the hash/XOF ones the IV enters via the IV() macro into the initial state, so the fix there wraps that macro in U64TOWORD. All six are verified FAIL to PASS with genkat on big-endian (native ppc64 and big-endian MIPS under qemu); ref is unchanged as a control, and little-endian is a no-op.

Still outstanding (happy to follow up or extend here, your call)

The bit-interleaved bi8 / bi32 / bi32_lowsize / bi32_lowreg family has a separate big-endian issue, so I left it out to keep this set cleanly verified. In the non-external-interleave path their U64TOWORD is TOBI (interleave only) with no U64LE, so LOADBYTES does memcpy then TOBI and on big-endian interleaves a byte-swapped value. That needs a more careful change to the byte/word conversion (and matching IV handling) than a one-line wrap, so it deserves its own focused change.

The crypto_*/.../opt8 trees are generated from src/ via
scripts/copy_src_to_crypto.sh, so fixing only the generated copies would
be reverted on the next regeneration. Apply the identical U64TOWORD wrap
to the canonical src/opt8 and src/opt8_lowsize aead.c and hash.c so the
source of truth matches and the fix survives a copy_src_to_crypto run.
@Scottcjn

Copy link
Copy Markdown
Author

Follow-up: I also applied the identical fix to the canonical src/opt8 and src/opt8_lowsize sources (aead.c + hash.c). The crypto_*/.../opt8 trees are generated from src/ via scripts/copy_src_to_crypto.sh, so patching only the generated copies would be reverted on the next regeneration. With this commit the source of truth is fixed too and src/ matches the generated trees, so a copy_src_to_crypto run is a no-op. (Note: the #else CRYPTO_KEYBYTES == 20 / ascon80pq branch is left as-is since it is not built by any current algorithm; happy to wrap its IV too for consistency if you'd prefer.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants