From e7cec986f60519929c23fb4f1f8d247f89ee5f03 Mon Sep 17 00:00:00 2001 From: blackshirt Date: Thu, 11 Sep 2025 16:42:13 +0000 Subject: [PATCH 1/3] x.crypto.ascon: small bits of cleansup and optimization --- vlib/x/crypto/ascon/ascon.v | 19 +++++++++++++------ vlib/x/crypto/ascon/digest.v | 33 ++++++++++++++++++++++----------- vlib/x/crypto/ascon/util.v | 16 +++++++++++++--- 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/vlib/x/crypto/ascon/ascon.v b/vlib/x/crypto/ascon/ascon.v index 8bb1b1977a..1ebd5bab84 100644 --- a/vlib/x/crypto/ascon/ascon.v +++ b/vlib/x/crypto/ascon/ascon.v @@ -41,6 +41,9 @@ fn ascon_pnr(mut s State, nr int) { if nr < 1 || nr > 16 { panic('Invalid round number') } + // Allocate temporary vars to reduce allocation within loop + mut x0 := u64(0) + mut y0 := u64(0) // Ascon permutation routine for i := max_nr_perm - nr; i < max_nr_perm; i++ { // 3.2 Constant-Addition Layer step @@ -56,18 +59,22 @@ fn ascon_pnr(mut s State, nr int) { s.e0 ^= s.e4 s.e4 ^= s.e3 s.e2 ^= s.e1 - + // Set temp vars to values + x0 = s.e0 + y0 = s.e4 ^ (~s.e0 & s.e1) + /* t0 := s.e4 ^ (~s.e0 & s.e1) t1 := s.e0 ^ (~s.e1 & s.e2) t2 := s.e1 ^ (~s.e2 & s.e3) t3 := s.e2 ^ (~s.e3 & s.e4) t4 := s.e3 ^ (~s.e4 & s.e0) + */ - s.e0 = t1 - s.e1 = t2 - s.e2 = t3 - s.e3 = t4 - s.e4 = t0 + s.e0 = s.e0 ^ (~s.e1 & s.e2) // t1 + s.e1 = s.e1 ^ (~s.e2 & s.e3) // t2 + s.e2 = s.e2 ^ (~s.e3 & s.e4) // t3 + s.e3 = s.e3 ^ (~s.e4 & x0) // t4, change s.e0 to x0 + s.e4 = y0 s.e1 ^= s.e0 s.e0 ^= s.e4 diff --git a/vlib/x/crypto/ascon/digest.v b/vlib/x/crypto/ascon/digest.v index 89a7dac7e1..07ee9ad303 100644 --- a/vlib/x/crypto/ascon/digest.v +++ b/vlib/x/crypto/ascon/digest.v @@ -135,20 +135,32 @@ fn (mut d Digest) squeeze(mut dst []u8) int { } @[direct_array_access; inline] -fn ascon_generic_hash(mut s State, msg_ []u8, size int) []u8 { +fn ascon_generic_hash(mut s State, msg []u8, size int) []u8 { // Assumed state was correctly initialized // Absorbing the message - mut msg := msg_.clone() - for msg.len >= block_size { - s.e0 ^= binary.little_endian_u64(msg[0..block_size]) - unsafe { - msg = msg[block_size..] + mut pos := 0 + // Check if msg has non-null length, if yes, absorb it. + // Otherwise, just pad it + if _likely_(msg.len > 0) { + mut msg_len := msg.len + for msg_len >= block_size { + s.e0 ^= binary.little_endian_u64(msg[pos..pos + block_size]) + pos += block_size + msg_len -= block_size + ascon_pnr(mut s, ascon_prnd_12) } - ascon_pnr(mut s, ascon_prnd_12) + // Absorb the last partial message block + last_block := unsafe { msg[pos..] } + s.e0 ^= u64(0x01) << (8 * last_block.len) // pad(last_block.len) + if last_block.len > 0 { + s.e0 ^= load_bytes(last_block, last_block.len) + } + } else { + // Otherwise, just pad it + s.e0 ^= u64(0x01) } - // Absorb the last partial message block - s.e0 ^= load_bytes(msg, msg.len) - s.e0 ^= pad(msg.len) + // reset pos + pos = 0 // Squeezing phase // @@ -156,7 +168,6 @@ fn ascon_generic_hash(mut s State, msg_ []u8, size int) []u8 { // permutation ๐ด๐‘ ๐‘๐‘œ๐‘›-๐‘[12] to the state: ascon_pnr(mut s, ascon_prnd_12) mut out := []u8{len: size} - mut pos := 0 mut clen := out.len for clen >= block_size { binary.little_endian_put_u64(mut out[pos..pos + 8], s.e0) diff --git a/vlib/x/crypto/ascon/util.v b/vlib/x/crypto/ascon/util.v index 345683ea66..97e70d5e98 100644 --- a/vlib/x/crypto/ascon/util.v +++ b/vlib/x/crypto/ascon/util.v @@ -83,13 +83,23 @@ fn set_byte(b u8, i int) u64 { fn load_bytes(bytes []u8, n int) u64 { mut x := u64(0) for i := 0; i < n; i++ { - x |= set_byte(bytes[i], i) + // This is the same way to store bytes in little-endian way + // x |= u64(bytes[0]) << 8*0 // LSB at lowest index + // x |= u64(bytes[1]) << 8*1 + // x |= u64(bytes[2]) << 8*2 + // x |= u64(bytes[3]) << 8*3 + // ...etc + // x |= u64(bytes[7]) << 8*7 // MSB at highest index + x |= u64(bytes[i]) << (8 * i) } - return u64le(x) + // No need to cast with u64le, its alread le + return x } +@[direct_array_access] fn store_bytes(mut out []u8, x u64, n int) { for i := 0; i < n; i++ { - out[i] = get_byte(x, i) + // use underlying get_byte directly + out[i] = u8(x >> (8 * i)) } } From f487cb77c1ffe7779f938eb7568f292c4efc1c3d Mon Sep 17 00:00:00 2001 From: blackshirt Date: Fri, 12 Sep 2025 03:05:55 +0000 Subject: [PATCH 2/3] x.crypto.asconL small missing a bits of improvement --- vlib/x/crypto/ascon/ascon.v | 34 +++++++++++++--------------------- vlib/x/crypto/ascon/digest.v | 3 ++- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/vlib/x/crypto/ascon/ascon.v b/vlib/x/crypto/ascon/ascon.v index 1ebd5bab84..59e287ec9d 100644 --- a/vlib/x/crypto/ascon/ascon.v +++ b/vlib/x/crypto/ascon/ascon.v @@ -43,7 +43,6 @@ fn ascon_pnr(mut s State, nr int) { } // Allocate temporary vars to reduce allocation within loop mut x0 := u64(0) - mut y0 := u64(0) // Ascon permutation routine for i := max_nr_perm - nr; i < max_nr_perm; i++ { // 3.2 Constant-Addition Layer step @@ -59,22 +58,15 @@ fn ascon_pnr(mut s State, nr int) { s.e0 ^= s.e4 s.e4 ^= s.e3 s.e2 ^= s.e1 - // Set temp vars to values - x0 = s.e0 - y0 = s.e4 ^ (~s.e0 & s.e1) - /* - t0 := s.e4 ^ (~s.e0 & s.e1) - t1 := s.e0 ^ (~s.e1 & s.e2) - t2 := s.e1 ^ (~s.e2 & s.e3) - t3 := s.e2 ^ (~s.e3 & s.e4) - t4 := s.e3 ^ (~s.e4 & s.e0) - */ - s.e0 = s.e0 ^ (~s.e1 & s.e2) // t1 - s.e1 = s.e1 ^ (~s.e2 & s.e3) // t2 - s.e2 = s.e2 ^ (~s.e3 & s.e4) // t3 - s.e3 = s.e3 ^ (~s.e4 & x0) // t4, change s.e0 to x0 - s.e4 = y0 + // Set temp var to value + x0 = s.e0 & ~s.e4 + + s.e0 ^= s.e2 & ~s.e1 + s.e2 ^= s.e4 & ~s.e3 + s.e4 ^= s.e1 & ~s.e0 + s.e1 ^= s.e3 & ~s.e2 + s.e3 ^= x0 s.e1 ^= s.e0 s.e0 ^= s.e4 @@ -97,11 +89,11 @@ fn ascon_pnr(mut s State, nr int) { // Bits right rotation, basically can be defined as: // ror = (x >> n) | x << (64 - n) for some u64 x // - s.e0 ^= (s.e0 >> 19 | (s.e0 << (64 - 19))) ^ (s.e0 >> 28 | (s.e0 << (64 - 28))) - s.e1 ^= (s.e1 >> 61 | (s.e1 << (64 - 61))) ^ (s.e1 >> 39 | (s.e1 << (64 - 39))) - s.e2 ^= (s.e2 >> 1 | (s.e2 << (64 - 1))) ^ (s.e2 >> 6 | (s.e2 << (64 - 6))) // - s.e3 ^= (s.e3 >> 10 | (s.e3 << (64 - 10))) ^ (s.e3 >> 17 | (s.e3 << (64 - 17))) - s.e4 ^= (s.e4 >> 7 | (s.e4 << (64 - 7))) ^ (s.e4 >> 41 | (s.e4 << (64 - 41))) + s.e0 ^= (s.e0 >> 19 | s.e0 << 45) ^ (s.e0 >> 28 | s.e0 << 36) + s.e1 ^= (s.e1 >> 61 | s.e1 << 3) ^ (s.e1 >> 39 | s.e1 << 25) + s.e2 ^= (s.e2 >> 1 | s.e2 << 63) ^ (s.e2 >> 6 | s.e2 << 58) + s.e3 ^= (s.e3 >> 10 | s.e3 << 54) ^ (s.e3 >> 17 | s.e3 << 47) + s.e4 ^= (s.e4 >> 7 | s.e4 << 57) ^ (s.e4 >> 41 | s.e4 << 23) } } diff --git a/vlib/x/crypto/ascon/digest.v b/vlib/x/crypto/ascon/digest.v index 07ee9ad303..55ace29f82 100644 --- a/vlib/x/crypto/ascon/digest.v +++ b/vlib/x/crypto/ascon/digest.v @@ -144,7 +144,8 @@ fn ascon_generic_hash(mut s State, msg []u8, size int) []u8 { if _likely_(msg.len > 0) { mut msg_len := msg.len for msg_len >= block_size { - s.e0 ^= binary.little_endian_u64(msg[pos..pos + block_size]) + block := unsafe { msg[pos..pos + block_size] } + s.e0 ^= binary.little_endian_u64(block) pos += block_size msg_len -= block_size ascon_pnr(mut s, ascon_prnd_12) From afdf137ffb8caaae315bd1b908971c1b825c87da Mon Sep 17 00:00:00 2001 From: blackshirt Date: Fri, 12 Sep 2025 12:05:55 +0000 Subject: [PATCH 3/3] x.crypto.ascon: ascon_pnr revert to previous one and change to use an enum --- vlib/x/crypto/ascon/aead128.v | 16 ++++++++-------- vlib/x/crypto/ascon/ascon.v | 31 ++++++++++++++++--------------- vlib/x/crypto/ascon/ascon_test.v | 6 +++--- vlib/x/crypto/ascon/digest.v | 16 ++++++++-------- vlib/x/crypto/ascon/xof.v | 6 +++--- 5 files changed, 38 insertions(+), 37 deletions(-) diff --git a/vlib/x/crypto/ascon/aead128.v b/vlib/x/crypto/ascon/aead128.v index f80cdb2f2b..3b395ae515 100644 --- a/vlib/x/crypto/ascon/aead128.v +++ b/vlib/x/crypto/ascon/aead128.v @@ -181,7 +181,7 @@ pub fn (mut c Aead128) encrypt(msg []u8, nonce []u8, ad []u8) ![]u8 { c.State.e4 = n1 // Update state by permutation - ascon_pnr(mut c.State, ascon_prnd_12) + ascon_pnr(mut c.State, .ascon_prnd_12) // XOR-ing with the cipher's key c.State.e3 ^= c.key[0] c.State.e4 ^= c.key[1] @@ -229,7 +229,7 @@ pub fn (mut c Aead128) decrypt(ciphertext []u8, nonce []u8, ad []u8) ![]u8 { c.State.e4 = n1 // scrambled with permutation routine - ascon_pnr(mut c.State, ascon_prnd_12) + ascon_pnr(mut c.State, .ascon_prnd_12) // xor-ing with the cipher's key c.State.e3 ^= c.key[0] c.State.e4 ^= c.key[1] @@ -288,7 +288,7 @@ fn aead128_init(mut s State, key []u8, nonce []u8) (u64, u64) { s.e4 = n1 // updates State using the permutation ๐ด๐‘ ๐‘๐‘œ๐‘›-๐‘[12], S โ† ๐ด๐‘ ๐‘๐‘œ๐‘›-๐‘[12](S) - ascon_pnr(mut s, ascon_prnd_12) + ascon_pnr(mut s, .ascon_prnd_12) // Then XORing the secret key ๐พ into the last 128 bits of internal state: // S โ† S โŠ• (0ยนโนยฒ โˆฅ ๐พ). @@ -312,7 +312,7 @@ fn aead128_process_ad(mut s State, ad []u8) { s.e1 ^= binary.little_endian_u64(block[8..16]) // Apply permutation ๐ด๐‘ ๐‘๐‘œ๐‘›-๐‘[8] to the state - ascon_pnr(mut s, ascon_prnd_8) + ascon_pnr(mut s, .ascon_prnd_8) // Updates index ad_length -= aead128_block_size ad_idx += aead128_block_size @@ -339,7 +339,7 @@ fn aead128_process_ad(mut s State, ad []u8) { } } // Apply permutation ๐ด๐‘ ๐‘๐‘œ๐‘›-๐‘[8] to the state - ascon_pnr(mut s, ascon_prnd_8) + ascon_pnr(mut s, .ascon_prnd_8) } // The final step of processing associated data is to update the state // with a constant that provides domain separation. @@ -361,7 +361,7 @@ fn aead128_process_msg(mut out []u8, mut s State, msg []u8) int { binary.little_endian_put_u64(mut out[pos..pos + 8], s.e0) binary.little_endian_put_u64(mut out[pos + 8..], s.e1) // apply permutation - ascon_pnr(mut s, ascon_prnd_8) + ascon_pnr(mut s, .ascon_prnd_8) // updates index mlen -= aead128_block_size @@ -413,7 +413,7 @@ fn aead128_partial_dec(mut out []u8, mut s State, cmsg []u8) { s.e0 = c0 s.e1 = c1 - ascon_pnr(mut s, ascon_prnd_8) + ascon_pnr(mut s, .ascon_prnd_8) // updates index pos += aead128_block_size cmsg_len -= aead128_block_size @@ -448,7 +448,7 @@ fn aead128_finalize(mut s State, k0 u64, k1 u64) { s.e2 ^= k0 s.e3 ^= k1 // then updated using the permutation ๐ด๐‘ ๐‘๐‘œ๐‘›-๐‘[12] - ascon_pnr(mut s, ascon_prnd_12) + ascon_pnr(mut s, .ascon_prnd_12) // Finally, the tag ๐‘‡ is generated by XORing the key with the last 128 bits of the state: // ๐‘‡ โ† ๐‘†[192โˆถ319] โŠ• ๐พ. diff --git a/vlib/x/crypto/ascon/ascon.v b/vlib/x/crypto/ascon/ascon.v index 59e287ec9d..088487eb06 100644 --- a/vlib/x/crypto/ascon/ascon.v +++ b/vlib/x/crypto/ascon/ascon.v @@ -11,8 +11,11 @@ module ascon const max_nr_perm = 16 // The number how many round(s) for the Ascon permutation routine called. -const ascon_prnd_8 = 8 -const ascon_prnd_12 = 12 +enum PrndEnum { + ascon_prnd_6 = 6 + ascon_prnd_8 = 8 + ascon_prnd_12 = 12 +} // The constants to derive round constants of the Ascon permutations // See Table 5. of NIST SP 800-232 docs @@ -36,15 +39,12 @@ const rnc = [u8(0x3c), 0x2d, 0x1e, 0x0f, 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x9 // 2. the substitution layer (see Sec.3.3), and, // 3. the linear diffusion layer (Sec 3.4) @[direct_array_access] -fn ascon_pnr(mut s State, nr int) { - // We dont allow nr == 0 - if nr < 1 || nr > 16 { - panic('Invalid round number') - } +fn ascon_pnr(mut s State, nr PrndEnum) { // Allocate temporary vars to reduce allocation within loop mut x0 := u64(0) + mut y0 := u64(0) // Ascon permutation routine - for i := max_nr_perm - nr; i < max_nr_perm; i++ { + for i := max_nr_perm - int(nr); i < max_nr_perm; i++ { // 3.2 Constant-Addition Layer step // // The constant-addition layer adds a 64-bit round constant ๐‘๐‘– @@ -59,14 +59,15 @@ fn ascon_pnr(mut s State, nr int) { s.e4 ^= s.e3 s.e2 ^= s.e1 - // Set temp var to value - x0 = s.e0 & ~s.e4 + // Set temp vars to values + x0 = s.e0 + y0 = s.e4 ^ (~s.e0 & s.e1) - s.e0 ^= s.e2 & ~s.e1 - s.e2 ^= s.e4 & ~s.e3 - s.e4 ^= s.e1 & ~s.e0 - s.e1 ^= s.e3 & ~s.e2 - s.e3 ^= x0 + s.e0 = s.e0 ^ (~s.e1 & s.e2) // t1 + s.e1 = s.e1 ^ (~s.e2 & s.e3) // t2 + s.e2 = s.e2 ^ (~s.e3 & s.e4) // t3 + s.e3 = s.e3 ^ (~s.e4 & x0) // t4, change s.e0 to x0 + s.e4 = y0 s.e1 ^= s.e0 s.e0 ^= s.e4 diff --git a/vlib/x/crypto/ascon/ascon_test.v b/vlib/x/crypto/ascon/ascon_test.v index ebf5ea0769..1867d0388c 100644 --- a/vlib/x/crypto/ascon/ascon_test.v +++ b/vlib/x/crypto/ascon/ascon_test.v @@ -13,7 +13,7 @@ fn test_ascon_round_p6() { e3: 0xabcdef0123456789 e4: 0x89abcdef01234567 } - ascon_pnr(mut s, 6) + ascon_pnr(mut s, .ascon_prnd_6) assert s.e0 == u64(0xc27b505c635eb07f) assert s.e1 == u64(0xd388f5d2a72046fa) assert s.e2 == u64(0x9e415c204d7b15e7) @@ -29,7 +29,7 @@ fn test_ascon_round_p8() { e3: 0xabcdef0123456789 e4: 0x89abcdef01234567 } - ascon_pnr(mut s, 8) + ascon_pnr(mut s, .ascon_prnd_8) assert s.e0 == u64(0x67ed228272f46eee) assert s.e1 == u64(0x80bc0b097aad7944) assert s.e2 == u64(0x2fa599382c6db215) @@ -45,7 +45,7 @@ fn test_ascon_round_p12() { e3: 0xabcdef0123456789 e4: 0x89abcdef01234567 } - ascon_pnr(mut s, 12) + ascon_pnr(mut s, .ascon_prnd_12) assert s.e0 == u64(0x206416dfc624bb14) assert s.e1 == u64(0x1b0c47a601058aab) assert s.e2 == u64(0x8934cfc93814cddd) diff --git a/vlib/x/crypto/ascon/digest.v b/vlib/x/crypto/ascon/digest.v index 55ace29f82..7cfed74042 100644 --- a/vlib/x/crypto/ascon/digest.v +++ b/vlib/x/crypto/ascon/digest.v @@ -33,7 +33,7 @@ fn (mut d Digest) finish() { d.State.e0 ^= load_bytes(d.buf[..d.length], d.length) // Permutation step was done in squeezing-phase - // ascon_pnr(mut d.State, ascon_prnd_12) + // ascon_pnr(mut d.State, .ascon_prnd_12) // zeroing Digest buffer d.length = 0 @@ -70,7 +70,7 @@ fn (mut d Digest) absorb(msg_ []u8) int { // If this d.buf length has reached block_size bytes, absorb it. if d.length == block_size { d.State.e0 ^= binary.little_endian_u64(d.buf) - ascon_pnr(mut d.State, ascon_prnd_12) + ascon_pnr(mut d.State, .ascon_prnd_12) // reset the internal buffer d.length = 0 d.buf.reset() @@ -87,7 +87,7 @@ fn (mut d Digest) absorb(msg_ []u8) int { for msg.len >= block_size { d.State.e0 ^= binary.little_endian_u64(msg[0..block_size]) msg = msg[block_size..] - ascon_pnr(mut d.State, ascon_prnd_12) + ascon_pnr(mut d.State, .ascon_prnd_12) } // If there are partial block, just stored into buffer. if msg.len > 0 { @@ -113,14 +113,14 @@ fn (mut d Digest) squeeze(mut dst []u8) int { } // The squeezing phase begins after msg is absorbed with an // permutation ๐ด๐‘ ๐‘๐‘œ๐‘›-๐‘[12] to the state: - ascon_pnr(mut d.State, ascon_prnd_12) + ascon_pnr(mut d.State, .ascon_prnd_12) mut pos := 0 mut clen := dst.len // process for full block size for clen >= block_size { binary.little_endian_put_u64(mut dst[pos..pos + 8], d.State.e0) - ascon_pnr(mut d.State, ascon_prnd_12) + ascon_pnr(mut d.State, .ascon_prnd_12) pos += block_size clen -= block_size } @@ -148,7 +148,7 @@ fn ascon_generic_hash(mut s State, msg []u8, size int) []u8 { s.e0 ^= binary.little_endian_u64(block) pos += block_size msg_len -= block_size - ascon_pnr(mut s, ascon_prnd_12) + ascon_pnr(mut s, .ascon_prnd_12) } // Absorb the last partial message block last_block := unsafe { msg[pos..] } @@ -167,12 +167,12 @@ fn ascon_generic_hash(mut s State, msg []u8, size int) []u8 { // // The squeezing phase begins after msg is absorbed with an // permutation ๐ด๐‘ ๐‘๐‘œ๐‘›-๐‘[12] to the state: - ascon_pnr(mut s, ascon_prnd_12) + ascon_pnr(mut s, .ascon_prnd_12) mut out := []u8{len: size} mut clen := out.len for clen >= block_size { binary.little_endian_put_u64(mut out[pos..pos + 8], s.e0) - ascon_pnr(mut s, ascon_prnd_12) + ascon_pnr(mut s, .ascon_prnd_12) pos += block_size clen -= block_size } diff --git a/vlib/x/crypto/ascon/xof.v b/vlib/x/crypto/ascon/xof.v index eea14e35f1..06af36156c 100644 --- a/vlib/x/crypto/ascon/xof.v +++ b/vlib/x/crypto/ascon/xof.v @@ -300,7 +300,7 @@ pub fn (mut x CXof128) free() { fn cxof128_absorb_custom_string(mut s State, cs []u8) { // absorb Z0, the length of the customization string (in bits) encoded as a u64 s.e0 ^= u64(cs.len) << 3 - ascon_pnr(mut s, ascon_prnd_12) + ascon_pnr(mut s, .ascon_prnd_12) // absorb the customization string mut zlen := cs.len @@ -308,7 +308,7 @@ fn cxof128_absorb_custom_string(mut s State, cs []u8) { for zlen >= block_size { block := unsafe { cs[zidx..zidx + block_size] } s.e0 ^= binary.little_endian_u64(block) - ascon_pnr(mut s, ascon_prnd_12) + ascon_pnr(mut s, .ascon_prnd_12) // updates a index zlen -= block_size @@ -318,5 +318,5 @@ fn cxof128_absorb_custom_string(mut s State, cs []u8) { last_block := unsafe { cs[zidx..] } s.e0 ^= load_bytes(last_block, last_block.len) s.e0 ^= pad(last_block.len) - ascon_pnr(mut s, ascon_prnd_12) + ascon_pnr(mut s, .ascon_prnd_12) }