| 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 | // This file implement hashing routines based on Ascon-Hash256 schema defined in NIST SP 800-232 standard, |
| 6 | // Ascon-Hash256 hashing produces 256-bits output. |
| 7 | module ascon |
| 8 | |
| 9 | // block_size is the size (rate) of Ascon-Hash256, Ascon-XOF128 and Ascon-CXOF128 operates on. |
| 10 | const block_size = 8 |
| 11 | |
| 12 | // hash256_size is the length of the Ascon-Hash256 checksum output, in bytes |
| 13 | const hash256_size = 32 |
| 14 | |
| 15 | // hash256_initial_state is a precomputed value for Ascon-Hash256 state. |
| 16 | // |
| 17 | // The 320-bit internal state of Ascon-Hash256 is initialized with the |
| 18 | // concatenation of the 64-bit 𝐼𝑉 = 0x0000080100cc0002 and 256 zeroes, followed |
| 19 | // by the 𝐴𝑠𝑐𝑜𝑛-𝑝[12] permutation as S ← 𝐴𝑠𝑐𝑜𝑛-𝑝[12](𝐼𝑉 ∥0256). |
| 20 | // |
| 21 | // s.e0 = 0x0000080100cc0002 |
| 22 | // s.e1 = 0 |
| 23 | // s.e2 = 0 |
| 24 | // s.e3 = 0 |
| 25 | // s.e4 = 0 |
| 26 | // ascon_pnr(mut s, 12) |
| 27 | // |
| 28 | // Above step can be replaced with precomputed value to reduce runtime computations. |
| 29 | // See the detail on the NIST SP 800-232 standard on Sec A.3. Precomputation |
| 30 | // 𝑆0 ← 0x9b1e5494e934d681 |
| 31 | // 𝑆1 ← 0x4bc3a01e333751d2 |
| 32 | // 𝑆2 ← 0xae65396c6b34b81a |
| 33 | // 𝑆3 ← 0x3c7fd4a4d56a4db3 |
| 34 | // 𝑆4 ← 0x1a5c464906c5976d |
| 35 | // |
| 36 | const hash256_initial_state = State{ |
| 37 | e0: u64(0x9b1e5494e934d681) |
| 38 | e1: 0x4bc3a01e333751d2 |
| 39 | e2: 0xae65396c6b34b81a |
| 40 | e3: 0x3c7fd4a4d56a4db3 |
| 41 | e4: 0x1a5c464906c5976d |
| 42 | } |
| 43 | |
| 44 | // sum256 creates an Ascon-Hash256 checksum for bytes on msg and produces a 256-bit hash. |
| 45 | pub fn sum256(msg_ []u8) []u8 { |
| 46 | // This is single-shot function, so, no need to use Hash256 opaque that process |
| 47 | // message in streaming way. To reduce this overhead, use raw processing instead. |
| 48 | // |
| 49 | // Initialize state |
| 50 | mut s := hash256_initial_state |
| 51 | return ascon_generic_hash(mut s, msg_, hash256_size) |
| 52 | } |
| 53 | |
| 54 | // Hash256 is an opaque provides an implementation of Ascon-Hash256 from NIST.SP.800-232 standard. |
| 55 | // Its implements `hash.Hash` interface. |
| 56 | @[noinit] |
| 57 | pub struct Hash256 { |
| 58 | Digest |
| 59 | } |
| 60 | |
| 61 | // new_hash256 creates a new Ascon-Hash256 instance. |
| 62 | pub fn new_hash256() &Hash256 { |
| 63 | return &Hash256{ |
| 64 | Digest: Digest{ |
| 65 | State: hash256_initial_state |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | // size returns an underlying size of Hash256 checksum, ie, 32-bytes |
| 71 | pub fn (h &Hash256) size() int { |
| 72 | return hash256_size |
| 73 | } |
| 74 | |
| 75 | // block_size returns an underlying Hash256 block size operates on, ie, 8-bytes |
| 76 | pub fn (h &Hash256) block_size() int { |
| 77 | return block_size |
| 78 | } |
| 79 | |
| 80 | // reset resets and reinit internal Hash256 state into default initialized state. |
| 81 | pub fn (mut h Hash256) reset() { |
| 82 | h.Digest.State = hash256_initial_state |
| 83 | unsafe { h.Digest.buf.reset() } |
| 84 | h.Digest.length = 0 |
| 85 | h.Digest.done = false |
| 86 | } |
| 87 | |
| 88 | // free releases out the resources taken by the `h`. Dont use x after .free call. |
| 89 | @[unsafe] |
| 90 | pub fn (mut h Hash256) free() { |
| 91 | $if prealloc { |
| 92 | return |
| 93 | } |
| 94 | unsafe { |
| 95 | h.Digest.buf.free() |
| 96 | } |
| 97 | // Mark it as unusable |
| 98 | h.Digest.done = true |
| 99 | } |
| 100 | |
| 101 | // write writes out the content of message and updates internal Hash256 state. |
| 102 | pub fn (mut h Hash256) write(msg []u8) !int { |
| 103 | if h.Digest.done { |
| 104 | panic('Digest: writing after done ') |
| 105 | } |
| 106 | return h.absorb(msg) |
| 107 | } |
| 108 | |
| 109 | // clone returns the clone of the current Hash256 |
| 110 | fn (h &Hash256) clone() &Hash256 { |
| 111 | digest := Digest{ |
| 112 | State: h.Digest.State |
| 113 | buf: h.Digest.buf.clone() |
| 114 | length: h.Digest.length |
| 115 | done: h.Digest.done |
| 116 | } |
| 117 | return &Hash256{digest} |
| 118 | } |
| 119 | |
| 120 | // sum returns an Ascon-Hash256 checksum of the bytes in data. |
| 121 | pub fn (mut h Hash256) sum(data []u8) []u8 { |
| 122 | // working on the clone of the h, so we can keep writing |
| 123 | mut h0 := h.clone() |
| 124 | _ := h0.write(data) or { panic(err) } |
| 125 | h0.Digest.finish() |
| 126 | mut dst := []u8{len: hash256_size} |
| 127 | _ := h0.Digest.squeeze(mut dst) |
| 128 | h0.reset() |
| 129 | return dst |
| 130 | } |
| 131 | |