From d1968140c7d2c19f48953df70afaa5542d3928f6 Mon Sep 17 00:00:00 2001 From: Scott Date: Sun, 28 Jun 2026 19:35:28 -0500 Subject: [PATCH 1/3] Fix big-endian IV initialization in opt8/opt8_lowsize AEAD128 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 #25. --- crypto_aead/asconaead128/opt8/aead.c | 4 ++-- crypto_aead/asconaead128/opt8_lowsize/aead.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crypto_aead/asconaead128/opt8/aead.c b/crypto_aead/asconaead128/opt8/aead.c index eb2cb1de2..f525cca44 100644 --- a/crypto_aead/asconaead128/opt8/aead.c +++ b/crypto_aead/asconaead128/opt8/aead.c @@ -25,8 +25,8 @@ forceinline void ascon_loadkey(ascon_key_t* key, const uint8_t* k) { forceinline void ascon_initaead(ascon_state_t* s, const ascon_key_t* key, const uint8_t* npub) { #if CRYPTO_KEYBYTES == 16 - if (ASCON_AEAD_RATE == 8) s->x[0] = ASCON_128_IV; - if (ASCON_AEAD_RATE == 16) s->x[0] = ASCON_128A_IV; + if (ASCON_AEAD_RATE == 8) s->x[0] = U64TOWORD(ASCON_128_IV); + if (ASCON_AEAD_RATE == 16) s->x[0] = U64TOWORD(ASCON_128A_IV); memcpy(s->b[1], key->b[0], 16); #else /* CRYPTO_KEYBYTES == 20 */ s->x[0] = key->x[0] | ASCON_80PQ_IV; diff --git a/crypto_aead/asconaead128/opt8_lowsize/aead.c b/crypto_aead/asconaead128/opt8_lowsize/aead.c index e91033437..6b5279df5 100644 --- a/crypto_aead/asconaead128/opt8_lowsize/aead.c +++ b/crypto_aead/asconaead128/opt8_lowsize/aead.c @@ -25,8 +25,8 @@ forceinline void ascon_loadkey(ascon_key_t* key, const uint8_t* k) { forceinline void ascon_initaead(ascon_state_t* s, const ascon_key_t* key, const uint8_t* npub) { #if CRYPTO_KEYBYTES == 16 - if (ASCON_AEAD_RATE == 8) s->x[0] = ASCON_128_IV; - if (ASCON_AEAD_RATE == 16) s->x[0] = ASCON_128A_IV; + if (ASCON_AEAD_RATE == 8) s->x[0] = U64TOWORD(ASCON_128_IV); + if (ASCON_AEAD_RATE == 16) s->x[0] = U64TOWORD(ASCON_128A_IV); memcpy(s->b[1], key->b[0], 16); #else /* CRYPTO_KEYBYTES == 20 */ s->x[0] = key->x[0] | ASCON_80PQ_IV; From b16ff1969c96c17bba1a4b21f8ed498eecef5b6d Mon Sep 17 00:00:00 2001 From: Scott Date: Sun, 28 Jun 2026 20:21:35 -0500 Subject: [PATCH 2/3] Extend big-endian IV fix to Hash256 and XOF128 opt8 implementations 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. --- crypto_hash/asconhash256/opt8/hash.c | 8 ++++---- crypto_hash/asconhash256/opt8_lowsize/hash.c | 8 ++++---- crypto_hash/asconxof128/opt8/hash.c | 8 ++++---- crypto_hash/asconxof128/opt8_lowsize/hash.c | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/crypto_hash/asconhash256/opt8/hash.c b/crypto_hash/asconhash256/opt8/hash.c index fd64b2f08..a9c410a6b 100644 --- a/crypto_hash/asconhash256/opt8/hash.c +++ b/crypto_hash/asconhash256/opt8/hash.c @@ -12,13 +12,13 @@ #ifdef ASCON_HASH_BYTES #if ASCON_HASH_BYTES == 32 && ASCON_HASH_ROUNDS == 12 -#define IV(i) ASCON_HASH_IV##i +#define IV(i) U64TOWORD(ASCON_HASH_IV##i) #elif ASCON_HASH_BYTES == 32 && ASCON_HASH_ROUNDS == 8 -#define IV(i) ASCON_HASHA_IV##i +#define IV(i) U64TOWORD(ASCON_HASHA_IV##i) #elif ASCON_HASH_BYTES == 0 && ASCON_HASH_ROUNDS == 12 -#define IV(i) ASCON_XOF_IV##i +#define IV(i) U64TOWORD(ASCON_XOF_IV##i) #elif ASCON_HASH_BYTES == 0 && ASCON_HASH_ROUNDS == 8 -#define IV(i) ASCON_XOFA_IV##i +#define IV(i) U64TOWORD(ASCON_XOFA_IV##i) #endif forceinline void ascon_inithash(ascon_state_t* s) { diff --git a/crypto_hash/asconhash256/opt8_lowsize/hash.c b/crypto_hash/asconhash256/opt8_lowsize/hash.c index e933041b9..771572b38 100644 --- a/crypto_hash/asconhash256/opt8_lowsize/hash.c +++ b/crypto_hash/asconhash256/opt8_lowsize/hash.c @@ -5,13 +5,13 @@ #include "printstate.h" #if ASCON_HASH_BYTES == 32 && ASCON_HASH_ROUNDS == 12 -#define IV(i) ASCON_HASH_IV##i +#define IV(i) U64TOWORD(ASCON_HASH_IV##i) #elif ASCON_HASH_BYTES == 32 && ASCON_HASH_ROUNDS == 8 -#define IV(i) ASCON_HASHA_IV##i +#define IV(i) U64TOWORD(ASCON_HASHA_IV##i) #elif ASCON_HASH_BYTES == 0 && ASCON_HASH_ROUNDS == 12 -#define IV(i) ASCON_XOF_IV##i +#define IV(i) U64TOWORD(ASCON_XOF_IV##i) #elif ASCON_HASH_BYTES == 0 && ASCON_HASH_ROUNDS == 8 -#define IV(i) ASCON_XOFA_IV##i +#define IV(i) U64TOWORD(ASCON_XOFA_IV##i) #endif forceinline void ascon_inithash(ascon_state_t* s) { diff --git a/crypto_hash/asconxof128/opt8/hash.c b/crypto_hash/asconxof128/opt8/hash.c index fd64b2f08..a9c410a6b 100644 --- a/crypto_hash/asconxof128/opt8/hash.c +++ b/crypto_hash/asconxof128/opt8/hash.c @@ -12,13 +12,13 @@ #ifdef ASCON_HASH_BYTES #if ASCON_HASH_BYTES == 32 && ASCON_HASH_ROUNDS == 12 -#define IV(i) ASCON_HASH_IV##i +#define IV(i) U64TOWORD(ASCON_HASH_IV##i) #elif ASCON_HASH_BYTES == 32 && ASCON_HASH_ROUNDS == 8 -#define IV(i) ASCON_HASHA_IV##i +#define IV(i) U64TOWORD(ASCON_HASHA_IV##i) #elif ASCON_HASH_BYTES == 0 && ASCON_HASH_ROUNDS == 12 -#define IV(i) ASCON_XOF_IV##i +#define IV(i) U64TOWORD(ASCON_XOF_IV##i) #elif ASCON_HASH_BYTES == 0 && ASCON_HASH_ROUNDS == 8 -#define IV(i) ASCON_XOFA_IV##i +#define IV(i) U64TOWORD(ASCON_XOFA_IV##i) #endif forceinline void ascon_inithash(ascon_state_t* s) { diff --git a/crypto_hash/asconxof128/opt8_lowsize/hash.c b/crypto_hash/asconxof128/opt8_lowsize/hash.c index e933041b9..771572b38 100644 --- a/crypto_hash/asconxof128/opt8_lowsize/hash.c +++ b/crypto_hash/asconxof128/opt8_lowsize/hash.c @@ -5,13 +5,13 @@ #include "printstate.h" #if ASCON_HASH_BYTES == 32 && ASCON_HASH_ROUNDS == 12 -#define IV(i) ASCON_HASH_IV##i +#define IV(i) U64TOWORD(ASCON_HASH_IV##i) #elif ASCON_HASH_BYTES == 32 && ASCON_HASH_ROUNDS == 8 -#define IV(i) ASCON_HASHA_IV##i +#define IV(i) U64TOWORD(ASCON_HASHA_IV##i) #elif ASCON_HASH_BYTES == 0 && ASCON_HASH_ROUNDS == 12 -#define IV(i) ASCON_XOF_IV##i +#define IV(i) U64TOWORD(ASCON_XOF_IV##i) #elif ASCON_HASH_BYTES == 0 && ASCON_HASH_ROUNDS == 8 -#define IV(i) ASCON_XOFA_IV##i +#define IV(i) U64TOWORD(ASCON_XOFA_IV##i) #endif forceinline void ascon_inithash(ascon_state_t* s) { From 220da3b968629837ff744bf6fd59a6c5a2e68ab7 Mon Sep 17 00:00:00 2001 From: Scott Date: Sun, 28 Jun 2026 21:23:50 -0500 Subject: [PATCH 3/3] Apply the same big-endian IV fix to the canonical src/ sources 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. --- src/opt8/aead.c | 4 ++-- src/opt8/hash.c | 8 ++++---- src/opt8_lowsize/aead.c | 4 ++-- src/opt8_lowsize/hash.c | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/opt8/aead.c b/src/opt8/aead.c index eb2cb1de2..f525cca44 100644 --- a/src/opt8/aead.c +++ b/src/opt8/aead.c @@ -25,8 +25,8 @@ forceinline void ascon_loadkey(ascon_key_t* key, const uint8_t* k) { forceinline void ascon_initaead(ascon_state_t* s, const ascon_key_t* key, const uint8_t* npub) { #if CRYPTO_KEYBYTES == 16 - if (ASCON_AEAD_RATE == 8) s->x[0] = ASCON_128_IV; - if (ASCON_AEAD_RATE == 16) s->x[0] = ASCON_128A_IV; + if (ASCON_AEAD_RATE == 8) s->x[0] = U64TOWORD(ASCON_128_IV); + if (ASCON_AEAD_RATE == 16) s->x[0] = U64TOWORD(ASCON_128A_IV); memcpy(s->b[1], key->b[0], 16); #else /* CRYPTO_KEYBYTES == 20 */ s->x[0] = key->x[0] | ASCON_80PQ_IV; diff --git a/src/opt8/hash.c b/src/opt8/hash.c index fd64b2f08..a9c410a6b 100644 --- a/src/opt8/hash.c +++ b/src/opt8/hash.c @@ -12,13 +12,13 @@ #ifdef ASCON_HASH_BYTES #if ASCON_HASH_BYTES == 32 && ASCON_HASH_ROUNDS == 12 -#define IV(i) ASCON_HASH_IV##i +#define IV(i) U64TOWORD(ASCON_HASH_IV##i) #elif ASCON_HASH_BYTES == 32 && ASCON_HASH_ROUNDS == 8 -#define IV(i) ASCON_HASHA_IV##i +#define IV(i) U64TOWORD(ASCON_HASHA_IV##i) #elif ASCON_HASH_BYTES == 0 && ASCON_HASH_ROUNDS == 12 -#define IV(i) ASCON_XOF_IV##i +#define IV(i) U64TOWORD(ASCON_XOF_IV##i) #elif ASCON_HASH_BYTES == 0 && ASCON_HASH_ROUNDS == 8 -#define IV(i) ASCON_XOFA_IV##i +#define IV(i) U64TOWORD(ASCON_XOFA_IV##i) #endif forceinline void ascon_inithash(ascon_state_t* s) { diff --git a/src/opt8_lowsize/aead.c b/src/opt8_lowsize/aead.c index e91033437..6b5279df5 100644 --- a/src/opt8_lowsize/aead.c +++ b/src/opt8_lowsize/aead.c @@ -25,8 +25,8 @@ forceinline void ascon_loadkey(ascon_key_t* key, const uint8_t* k) { forceinline void ascon_initaead(ascon_state_t* s, const ascon_key_t* key, const uint8_t* npub) { #if CRYPTO_KEYBYTES == 16 - if (ASCON_AEAD_RATE == 8) s->x[0] = ASCON_128_IV; - if (ASCON_AEAD_RATE == 16) s->x[0] = ASCON_128A_IV; + if (ASCON_AEAD_RATE == 8) s->x[0] = U64TOWORD(ASCON_128_IV); + if (ASCON_AEAD_RATE == 16) s->x[0] = U64TOWORD(ASCON_128A_IV); memcpy(s->b[1], key->b[0], 16); #else /* CRYPTO_KEYBYTES == 20 */ s->x[0] = key->x[0] | ASCON_80PQ_IV; diff --git a/src/opt8_lowsize/hash.c b/src/opt8_lowsize/hash.c index e933041b9..771572b38 100644 --- a/src/opt8_lowsize/hash.c +++ b/src/opt8_lowsize/hash.c @@ -5,13 +5,13 @@ #include "printstate.h" #if ASCON_HASH_BYTES == 32 && ASCON_HASH_ROUNDS == 12 -#define IV(i) ASCON_HASH_IV##i +#define IV(i) U64TOWORD(ASCON_HASH_IV##i) #elif ASCON_HASH_BYTES == 32 && ASCON_HASH_ROUNDS == 8 -#define IV(i) ASCON_HASHA_IV##i +#define IV(i) U64TOWORD(ASCON_HASHA_IV##i) #elif ASCON_HASH_BYTES == 0 && ASCON_HASH_ROUNDS == 12 -#define IV(i) ASCON_XOF_IV##i +#define IV(i) U64TOWORD(ASCON_XOF_IV##i) #elif ASCON_HASH_BYTES == 0 && ASCON_HASH_ROUNDS == 8 -#define IV(i) ASCON_XOFA_IV##i +#define IV(i) U64TOWORD(ASCON_XOFA_IV##i) #endif forceinline void ascon_inithash(ascon_state_t* s) {