Skip to content

Commit f16452d

Browse files
authored
x.crypto.ascon: improve the core of Ascon permutation routine (#25278)
1 parent 919c68e commit f16452d

7 files changed

Lines changed: 79 additions & 99 deletions

File tree

‎vlib/x/crypto/ascon/README.md‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
# ascon
22

3-
`ascon` is a implementation of Ascon-Based Cryptography module implemented in pure V language.
3+
`ascon` is an implementation of Ascon-Based Cryptography module implemented in pure V language.
44
This module was mostly based on NIST Special Publication of 800 NIST SP 800-232 document.
55
Its describes an Ascon-Based Lightweight Cryptography Standards for Constrained Devices
66
thats provides Authenticated Encryption, Hash, and Extendable Output Functions.
77
See the [NIST.SP.800-232 Standard](https://doi.org/10.6028/NIST.SP.800-232) for more detail.
88

9-
This module does not fully implements all the features availables on the document.
9+
This module mostly implements all the features availables on the document.
1010
Its currently implements:
1111
- `Ascon-Hash256`, Ascon-based hashing implementation that produces 256-bits output.
12-
- `Ascon-XOF128`, Ascon-based eXtendible Output Function (XOF) where the output size of
12+
- `Ascon-XOF128`, Ascon-based eXtendable Output Function (XOF) where the output size of
1313
the hash of the message can be selected by the user.
1414
- `Ascon-CXOF128`, a customized XOF that allows users to specify a customization
1515
string and choose the output size of the message hash.
16+
- `Ascon-AEAD128`, an Authenticated Encryption with Additional Data (AEAD) Scheme based
17+
on Ascon-family crypto.

‎vlib/x/crypto/ascon/aead128.v‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ pub fn (mut c Aead128) encrypt(msg []u8, nonce []u8, ad []u8) ![]u8 {
181181
c.State.e4 = n1
182182

183183
// Update state by permutation
184-
ascon_pnr(mut c.State, 12)
184+
ascon_pnr(mut c.State, ascon_prnd_12)
185185
// XOR-ing with the cipher's key
186186
c.State.e3 ^= c.key[0]
187187
c.State.e4 ^= c.key[1]
@@ -229,7 +229,7 @@ pub fn (mut c Aead128) decrypt(ciphertext []u8, nonce []u8, ad []u8) ![]u8 {
229229
c.State.e4 = n1
230230

231231
// scrambled with permutation routine
232-
ascon_pnr(mut c.State, 12)
232+
ascon_pnr(mut c.State, ascon_prnd_12)
233233
// xor-ing with the cipher's key
234234
c.State.e3 ^= c.key[0]
235235
c.State.e4 ^= c.key[1]
@@ -288,7 +288,7 @@ fn aead128_init(mut s State, key []u8, nonce []u8) (u64, u64) {
288288
s.e4 = n1
289289

290290
// updates State using the permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[12], S ← 𝐴𝑠𝑐𝑜𝑛-𝑝[12](S)
291-
ascon_pnr(mut s, 12)
291+
ascon_pnr(mut s, ascon_prnd_12)
292292

293293
// Then XORing the secret key 𝐾 into the last 128 bits of internal state:
294294
// S ← S ⊕ (0¹⁹² ∥ 𝐾).
@@ -312,7 +312,7 @@ fn aead128_process_ad(mut s State, ad []u8) {
312312
s.e1 ^= binary.little_endian_u64(block[8..16])
313313

314314
// Apply permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[8] to the state
315-
ascon_pnr(mut s, 8)
315+
ascon_pnr(mut s, ascon_prnd_8)
316316
// Updates index
317317
ad_length -= aead128_block_size
318318
ad_idx += aead128_block_size
@@ -339,7 +339,7 @@ fn aead128_process_ad(mut s State, ad []u8) {
339339
}
340340
}
341341
// Apply permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[8] to the state
342-
ascon_pnr(mut s, 8)
342+
ascon_pnr(mut s, ascon_prnd_8)
343343
}
344344
// The final step of processing associated data is to update the state
345345
// with a constant that provides domain separation.
@@ -361,7 +361,7 @@ fn aead128_process_msg(mut out []u8, mut s State, msg []u8) int {
361361
binary.little_endian_put_u64(mut out[pos..pos + 8], s.e0)
362362
binary.little_endian_put_u64(mut out[pos + 8..], s.e1)
363363
// apply permutation
364-
ascon_pnr(mut s, 8)
364+
ascon_pnr(mut s, ascon_prnd_8)
365365

366366
// updates index
367367
mlen -= aead128_block_size
@@ -413,7 +413,7 @@ fn aead128_partial_dec(mut out []u8, mut s State, cmsg []u8) {
413413
s.e0 = c0
414414
s.e1 = c1
415415

416-
ascon_pnr(mut s, 8)
416+
ascon_pnr(mut s, ascon_prnd_8)
417417
// updates index
418418
pos += aead128_block_size
419419
cmsg_len -= aead128_block_size
@@ -448,7 +448,7 @@ fn aead128_finalize(mut s State, k0 u64, k1 u64) {
448448
s.e2 ^= k0
449449
s.e3 ^= k1
450450
// then updated using the permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[12]
451-
ascon_pnr(mut s, 12)
451+
ascon_pnr(mut s, ascon_prnd_12)
452452

453453
// Finally, the tag 𝑇 is generated by XORing the key with the last 128 bits of the state:
454454
// 𝑇 ← 𝑆[192∶319] ⊕ 𝐾.

‎vlib/x/crypto/ascon/ascon.v‎

Lines changed: 58 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ module ascon
1010
// constants for up to 16 rounds to accommodate potential functionality extensions in the future.
1111
const max_nr_perm = 16
1212

13+
// The number how many round(s) for the Ascon permutation routine called.
14+
const ascon_prnd_8 = 8
15+
const ascon_prnd_12 = 12
16+
1317
// The constants to derive round constants of the Ascon permutations
1418
// See Table 5. of NIST SP 800-232 docs
1519
//
@@ -26,70 +30,72 @@ const max_nr_perm = 16
2630
const rnc = [u8(0x3c), 0x2d, 0x1e, 0x0f, 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78,
2731
0x69, 0x5a, 0x4b]
2832

29-
// ascon_pnr is ascon permutation routine with specified numbers of round nr, where 1 ≤ nr ≤ 16
33+
// ascon_pnr is the core of Ascon family permutation routine with specified numbers of round nr, where 1 ≤ nr ≤ 16
34+
// Its consist of iterations of the round function that is defined as the composition of three steps, ie:
35+
// 1. the constant-addition layer (see Sec. 3.2),
36+
// 2. the substitution layer (see Sec.3.3), and,
37+
// 3. the linear diffusion layer (Sec 3.4)
3038
@[direct_array_access]
3139
fn ascon_pnr(mut s State, nr int) {
3240
// We dont allow nr == 0
3341
if nr < 1 || nr > 16 {
3442
panic('Invalid round number')
3543
}
44+
// Ascon permutation routine
3645
for i := max_nr_perm - nr; i < max_nr_perm; i++ {
37-
ascon_perm(mut s, rnc[i])
38-
}
39-
}
46+
// 3.2 Constant-Addition Layer step
47+
//
48+
// The constant-addition layer adds a 64-bit round constant 𝑐𝑖
49+
// to 𝑆₂ in round 𝑖, for 𝑖 ≥ 0, ie, this is equivalent to applying
50+
// the constant to only the least significant eight bits of 𝑆₂
51+
s.e2 ^= rnc[i]
4052

41-
// ascon_perm was the main permutations routine in Ascon-family crypto. Its consist of
42-
// iterations of the round function that is defined as the composition of three steps, ie:
43-
// 1. the constant-addition layer (see Sec. 3.2),
44-
// 2. the substitution layer (see Sec.3.3), and,
45-
// 3. the linear diffusion layer
46-
fn ascon_perm(mut s State, c u8) {
47-
// 3.2 Constant-Addition Layer step
48-
//
49-
// The constant-addition layer adds a 64-bit round constant 𝑐𝑖
50-
// to 𝑆₂ in round 𝑖, for 𝑖 ≥ 0, ie, this is equivalent to applying
51-
// the constant to only the least significant eight bits of 𝑆₂
52-
s.e2 ^= c
53-
54-
// 3.3. Substitution Layer
55-
// The substitution layer updates the state S with 64 parallel applications of the 5-bit
56-
// substitution box SBOX
57-
s.e0 ^= s.e4
58-
s.e4 ^= s.e3
59-
s.e2 ^= s.e1
53+
// 3.3. Substitution Layer
54+
// The substitution layer updates the state S with 64 parallel applications of the 5-bit
55+
// substitution box SBOX
56+
s.e0 ^= s.e4
57+
s.e4 ^= s.e3
58+
s.e2 ^= s.e1
6059

61-
t0 := s.e4 ^ (~s.e0 & s.e1)
62-
t1 := s.e0 ^ (~s.e1 & s.e2)
63-
t2 := s.e1 ^ (~s.e2 & s.e3)
64-
t3 := s.e2 ^ (~s.e3 & s.e4)
65-
t4 := s.e3 ^ (~s.e4 & s.e0)
60+
t0 := s.e4 ^ (~s.e0 & s.e1)
61+
t1 := s.e0 ^ (~s.e1 & s.e2)
62+
t2 := s.e1 ^ (~s.e2 & s.e3)
63+
t3 := s.e2 ^ (~s.e3 & s.e4)
64+
t4 := s.e3 ^ (~s.e4 & s.e0)
6665

67-
s.e0 = t1
68-
s.e1 = t2
69-
s.e2 = t3
70-
s.e3 = t4
71-
s.e4 = t0
66+
s.e0 = t1
67+
s.e1 = t2
68+
s.e2 = t3
69+
s.e3 = t4
70+
s.e4 = t0
7271

73-
s.e1 ^= s.e0
74-
s.e0 ^= s.e4
75-
s.e3 ^= s.e2
76-
s.e2 = ~(s.e2)
72+
s.e1 ^= s.e0
73+
s.e0 ^= s.e4
74+
s.e3 ^= s.e2
75+
s.e2 = ~(s.e2)
7776

78-
// 3.4. Linear Diffusion Layer
79-
//
80-
// The linear diffusion layer provides diffusion within each 64-bit word S,
81-
// defined as :
82-
// Σ0(𝑆0) = 𝑆0 ⊕ (𝑆0 ⋙ 19) ⊕ (𝑆0 ⋙ 28)
83-
// Σ1(𝑆1) = 𝑆1 ⊕ (𝑆1 ⋙ 61) ⊕ (𝑆1 ⋙ 39)
84-
// Σ2(𝑆2) = 𝑆2 ⊕ (𝑆2 ⋙ 1) ⊕ (𝑆2 ⋙ 6)
85-
// Σ3(𝑆3) = 𝑆3 ⊕ (𝑆3 ⋙ 10) ⊕ (𝑆3 ⋙ 17)
86-
// Σ4(𝑆4) = 𝑆4 ⊕ (𝑆4 ⋙ 7) ⊕ (𝑆4 ⋙ 41)
87-
88-
s.e0 ^= ascon_rotate_right(s.e0, 19) ^ ascon_rotate_right(s.e0, 28)
89-
s.e1 ^= ascon_rotate_right(s.e1, 61) ^ ascon_rotate_right(s.e1, 39)
90-
s.e2 ^= ascon_rotate_right(s.e2, 1) ^ ascon_rotate_right(s.e2, 6)
91-
s.e3 ^= ascon_rotate_right(s.e3, 10) ^ ascon_rotate_right(s.e3, 17)
92-
s.e4 ^= ascon_rotate_right(s.e4, 7) ^ ascon_rotate_right(s.e4, 41)
77+
// 3.4. Linear Diffusion Layer
78+
//
79+
// The linear diffusion layer provides diffusion within each 64-bit word S,
80+
// defined as :
81+
// Σ0(𝑆0) = 𝑆0 ⊕ (𝑆0 ⋙ 19) ⊕ (𝑆0 ⋙ 28)
82+
// Σ1(𝑆1) = 𝑆1 ⊕ (𝑆1 ⋙ 61) ⊕ (𝑆1 ⋙ 39)
83+
// Σ2(𝑆2) = 𝑆2 ⊕ (𝑆2 ⋙ 1) ⊕ (𝑆2 ⋙ 6)
84+
// Σ3(𝑆3) = 𝑆3 ⊕ (𝑆3 ⋙ 10) ⊕ (𝑆3 ⋙ 17)
85+
// Σ4(𝑆4) = 𝑆4 ⊕ (𝑆4 ⋙ 7) ⊕ (𝑆4 ⋙ 41)
86+
//
87+
// This diffusion layer, especially on the bits right rotation part is a most widely called
88+
// for Ascon permutation routine. So, even bits rotation almost efficient on most platform,
89+
// to reduce overhead on function call, we work on the raw bits right rotation here.
90+
// Bits right rotation, basically can be defined as:
91+
// ror = (x >> n) | x << (64 - n) for some u64 x
92+
//
93+
s.e0 ^= (s.e0 >> 19 | (s.e0 << (64 - 19))) ^ (s.e0 >> 28 | (s.e0 << (64 - 28)))
94+
s.e1 ^= (s.e1 >> 61 | (s.e1 << (64 - 61))) ^ (s.e1 >> 39 | (s.e1 << (64 - 39)))
95+
s.e2 ^= (s.e2 >> 1 | (s.e2 << (64 - 1))) ^ (s.e2 >> 6 | (s.e2 << (64 - 6))) //
96+
s.e3 ^= (s.e3 >> 10 | (s.e3 << (64 - 10))) ^ (s.e3 >> 17 | (s.e3 << (64 - 17)))
97+
s.e4 ^= (s.e4 >> 7 | (s.e4 << (64 - 7))) ^ (s.e4 >> 41 | (s.e4 << (64 - 41)))
98+
}
9399
}
94100

95101
// State is structure represents Ascon state. Its operates on the 320-bit opaque,

‎vlib/x/crypto/ascon/ascon_test.v‎

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,6 @@
55
module ascon
66

77
// This test mostly taken from https://docs.rs/ascon/latest/src/ascon/lib.rs.html
8-
fn test_ascon_round_one() {
9-
mut s := State{
10-
e0: u64(0x0123456789abcdef)
11-
e1: 0x23456789abcdef01
12-
e2: 0x456789abcdef0123
13-
e3: 0x6789abcdef012345
14-
e4: 0x89abcde01234567f
15-
}
16-
ascon_perm(mut s, 0x1f)
17-
18-
assert s.e0 == u64(0x3c1748c9be2892ce)
19-
assert s.e1 == u64(0x5eafb305cd26164f)
20-
assert s.e2 == u64(0xf9470254bb3a4213)
21-
assert s.e3 == u64(0xf0428daf0c5d3948)
22-
assert s.e4 == u64(0x281375af0b294899)
23-
}
24-
258
fn test_ascon_round_p6() {
269
mut s := State{
2710
e0: u64(0x0123456789abcdef)

‎vlib/x/crypto/ascon/digest.v‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ fn (mut d Digest) finish() {
3333
d.State.e0 ^= load_bytes(d.buf[..d.length], d.length)
3434

3535
// Permutation step was done in squeezing-phase
36-
// ascon_pnr(mut d.State, 12)
36+
// ascon_pnr(mut d.State, ascon_prnd_12)
3737

3838
// zeroing Digest buffer
3939
d.length = 0
@@ -70,7 +70,7 @@ fn (mut d Digest) absorb(msg_ []u8) int {
7070
// If this d.buf length has reached block_size bytes, absorb it.
7171
if d.length == block_size {
7272
d.State.e0 ^= binary.little_endian_u64(d.buf)
73-
ascon_pnr(mut d.State, 12)
73+
ascon_pnr(mut d.State, ascon_prnd_12)
7474
// reset the internal buffer
7575
d.length = 0
7676
d.buf.reset()
@@ -87,7 +87,7 @@ fn (mut d Digest) absorb(msg_ []u8) int {
8787
for msg.len >= block_size {
8888
d.State.e0 ^= binary.little_endian_u64(msg[0..block_size])
8989
msg = msg[block_size..]
90-
ascon_pnr(mut d.State, 12)
90+
ascon_pnr(mut d.State, ascon_prnd_12)
9191
}
9292
// If there are partial block, just stored into buffer.
9393
if msg.len > 0 {
@@ -113,14 +113,14 @@ fn (mut d Digest) squeeze(mut dst []u8) int {
113113
}
114114
// The squeezing phase begins after msg is absorbed with an
115115
// permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[12] to the state:
116-
ascon_pnr(mut d.State, 12)
116+
ascon_pnr(mut d.State, ascon_prnd_12)
117117

118118
mut pos := 0
119119
mut clen := dst.len
120120
// process for full block size
121121
for clen >= block_size {
122122
binary.little_endian_put_u64(mut dst[pos..pos + 8], d.State.e0)
123-
ascon_pnr(mut d.State, 12)
123+
ascon_pnr(mut d.State, ascon_prnd_12)
124124
pos += block_size
125125
clen -= block_size
126126
}

‎vlib/x/crypto/ascon/util.v‎

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,6 @@ module ascon
88
import math.bits
99
import encoding.binary
1010

11-
// rotate_right_64 rotates x right by k bits
12-
fn rotate_right_64(x u64, k int) u64 {
13-
// call rotate_left_64(x, -k).
14-
return bits.rotate_left_64(x, -k)
15-
}
16-
1711
// clear_bytes clears the bytes of x in n byte
1812
@[inline]
1913
fn clear_bytes(x u64, n int) u64 {
@@ -100,8 +94,3 @@ fn store_bytes(mut out []u8, x u64, n int) {
10094
out[i] = get_byte(x, i)
10195
}
10296
}
103-
104-
@[inline]
105-
fn ascon_rotate_right(x u64, n int) u64 {
106-
return (x >> n) | x << (64 - n)
107-
}

‎vlib/x/crypto/ascon/xof.v‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,15 +305,15 @@ pub fn (mut x CXof128) free() {
305305
fn cxof128_absorb_custom_string(mut s State, cs []u8) {
306306
// absorb Z0, the length of the customization string (in bits) encoded as a u64
307307
s.e0 ^= u64(cs.len) << 3
308-
ascon_pnr(mut s, 12)
308+
ascon_pnr(mut s, ascon_prnd_12)
309309

310310
// absorb the customization string
311311
mut zlen := cs.len
312312
mut zidx := 0
313313
for zlen >= block_size {
314314
block := unsafe { cs[zidx..zidx + block_size] }
315315
s.e0 ^= binary.little_endian_u64(block)
316-
ascon_pnr(mut s, 12)
316+
ascon_pnr(mut s, ascon_prnd_12)
317317

318318
// updates a index
319319
zlen -= block_size
@@ -323,5 +323,5 @@ fn cxof128_absorb_custom_string(mut s State, cs []u8) {
323323
last_block := unsafe { cs[zidx..] }
324324
s.e0 ^= load_bytes(last_block, last_block.len)
325325
s.e0 ^= pad(last_block.len)
326-
ascon_pnr(mut s, 12)
326+
ascon_pnr(mut s, ascon_prnd_12)
327327
}

0 commit comments

Comments
 (0)