| 1 | // Copyright ©2025 blackshirt. |
| 2 | // Use of this source code is governed by an MIT license |
| 3 | // that can be found in the LICENSE file. |
| 4 | // |
| 5 | module ascon |
| 6 | |
| 7 | // max_nr_perm is the maximum number of permutations round supported on this module. |
| 8 | // The previous Ascon submission defined three Ascon permutations with 6, 8, and 12 rounds. |
| 9 | // The NIST SP 800-232 standard specifies additional Ascon permutations by providing round |
| 10 | // constants for up to 16 rounds to accommodate potential functionality extensions in the future. |
| 11 | const max_nr_perm = 16 |
| 12 | |
| 13 | // The number how many round(s) for the Ascon permutation routine called. |
| 14 | enum PrndEnum { |
| 15 | ascon_prnd_6 = 6 |
| 16 | ascon_prnd_8 = 8 |
| 17 | ascon_prnd_12 = 12 |
| 18 | } |
| 19 | |
| 20 | // The constants to derive round constants of the Ascon permutations |
| 21 | // See Table 5. of NIST SP 800-232 docs |
| 22 | // |
| 23 | // 0 0x000000000000003c 8 0x00000000000000b4 |
| 24 | // 1 0x000000000000002d 9 0x00000000000000a5 |
| 25 | // 2 0x000000000000001e 10 0x0000000000000096 |
| 26 | // 3 0x000000000000000f 11 0x0000000000000087 |
| 27 | // 4 0x00000000000000f0 12 0x0000000000000078 |
| 28 | // 5 0x00000000000000e1 13 0x0000000000000069 |
| 29 | // 6 0x00000000000000d2 14 0x000000000000005a |
| 30 | // 7 0x00000000000000c3 15 0x000000000000004b |
| 31 | // |
| 32 | // We use u8 instead, since the first 56 bits of the constants are zero |
| 33 | const rnc = [u8(0x3c), 0x2d, 0x1e, 0x0f, 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, |
| 34 | 0x69, 0x5a, 0x4b] |
| 35 | |
| 36 | // ascon_pnr is the core of Ascon family permutation routine with specified numbers of round nr, where 1 ≤ nr ≤ 16 |
| 37 | // Its consist of iterations of the round function that is defined as the composition of three steps, ie: |
| 38 | // 1. the constant-addition layer (see Sec. 3.2), |
| 39 | // 2. the substitution layer (see Sec.3.3), and, |
| 40 | // 3. the linear diffusion layer (Sec 3.4) |
| 41 | @[direct_array_access] |
| 42 | fn ascon_pnr(mut s State, nr PrndEnum) { |
| 43 | // Allocate temporary vars to reduce allocation within loop |
| 44 | mut x0 := u64(0) |
| 45 | mut y0 := u64(0) |
| 46 | // Ascon permutation routine |
| 47 | for i := max_nr_perm - int(nr); i < max_nr_perm; i++ { |
| 48 | // 3.2 Constant-Addition Layer step |
| 49 | // |
| 50 | // The constant-addition layer adds a 64-bit round constant 𝑐𝑖 |
| 51 | // to 𝑆₂ in round 𝑖, for 𝑖 ≥ 0, ie, this is equivalent to applying |
| 52 | // the constant to only the least significant eight bits of 𝑆₂ |
| 53 | s.e2 ^= rnc[i] |
| 54 | |
| 55 | // 3.3. Substitution Layer |
| 56 | // The substitution layer updates the state S with 64 parallel applications of the 5-bit |
| 57 | // substitution box SBOX |
| 58 | s.e0 ^= s.e4 |
| 59 | s.e4 ^= s.e3 |
| 60 | s.e2 ^= s.e1 |
| 61 | |
| 62 | // Set temp vars to values |
| 63 | x0 = s.e0 |
| 64 | y0 = s.e4 ^ (~s.e0 & s.e1) |
| 65 | |
| 66 | s.e0 = s.e0 ^ (~s.e1 & s.e2) // t1 |
| 67 | s.e1 = s.e1 ^ (~s.e2 & s.e3) // t2 |
| 68 | s.e2 = s.e2 ^ (~s.e3 & s.e4) // t3 |
| 69 | s.e3 = s.e3 ^ (~s.e4 & x0) // t4, change s.e0 to x0 |
| 70 | s.e4 = y0 |
| 71 | |
| 72 | s.e1 ^= s.e0 |
| 73 | s.e0 ^= s.e4 |
| 74 | s.e3 ^= s.e2 |
| 75 | s.e2 = ~(s.e2) |
| 76 | |
| 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 << 45) ^ (s.e0 >> 28 | s.e0 << 36) |
| 94 | s.e1 ^= (s.e1 >> 61 | s.e1 << 3) ^ (s.e1 >> 39 | s.e1 << 25) |
| 95 | s.e2 ^= (s.e2 >> 1 | s.e2 << 63) ^ (s.e2 >> 6 | s.e2 << 58) |
| 96 | s.e3 ^= (s.e3 >> 10 | s.e3 << 54) ^ (s.e3 >> 17 | s.e3 << 47) |
| 97 | s.e4 ^= (s.e4 >> 7 | s.e4 << 57) ^ (s.e4 >> 41 | s.e4 << 23) |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | // State is structure represents Ascon state. Its operates on the 320-bit opaque, |
| 102 | // which is represented as five of 64-bit words. |
| 103 | @[noinit] |
| 104 | struct State { |
| 105 | mut: |
| 106 | e0 u64 |
| 107 | e1 u64 |
| 108 | e2 u64 |
| 109 | e3 u64 |
| 110 | e4 u64 |
| 111 | } |
| 112 | |
| 113 | // clone returns a clone of current state. |
| 114 | @[inline] |
| 115 | fn clone_state(s State) State { |
| 116 | return State{ |
| 117 | e0: s.e0 |
| 118 | e1: s.e1 |
| 119 | e2: s.e2 |
| 120 | e3: s.e3 |
| 121 | e4: s.e4 |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | // reset this state with default value |
| 126 | @[inline] |
| 127 | fn reset_state(mut s State) { |
| 128 | s.e0 = 0 |
| 129 | s.e1 = 0 |
| 130 | s.e2 = 0 |
| 131 | s.e3 = 0 |
| 132 | s.e4 = 0 |
| 133 | } |
| 134 | |