mirror of
https://github.com/vlang/v.git
synced 2025-09-13 22:42:26 +03:00
x.crypto
: addition of Ascon-based lightweight cryptographic module
This commit is contained in:
parent
e650d6b7f0
commit
c3eba936d8
14 changed files with 1540 additions and 0 deletions
15
vlib/x/crypto/ascon/README.md
Normal file
15
vlib/x/crypto/ascon/README.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
# ascon
|
||||
|
||||
`ascon` is a implementation of Ascon-Based Cryptography module implemented in pure V language.
|
||||
This module was mostly based on NIST Special Publication of 800 NIST SP 800-232 document.
|
||||
Its describes an Ascon-Based Lightweight Cryptography Standards for Constrained Devices
|
||||
thats provides Authenticated Encryption, Hash, and Extendable Output Functions.
|
||||
See the [NIST.SP.800-232 Standard](https://doi.org/10.6028/NIST.SP.800-232) for more detail.
|
||||
|
||||
This module does not fully implements all the features availables on the document.
|
||||
Its currently implements:
|
||||
- `Ascon-Hash256`, Ascon-based hashing implementation that produces 256-bits output.
|
||||
- `Ascon-XOF128`, Ascon-based eXtendible Output Function (XOF) where the output size of
|
||||
the hash of the message can be selected by the user.
|
||||
- `Ascon-CXOF128`, a customized XOF that allows users to specify a customization
|
||||
string and choose the output size of the message hash.
|
135
vlib/x/crypto/ascon/ascon.v
Normal file
135
vlib/x/crypto/ascon/ascon.v
Normal file
|
@ -0,0 +1,135 @@
|
|||
// Copyright ©2025 blackshirt.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
//
|
||||
module ascon
|
||||
|
||||
// max_nr_perm is the maximum number of permutations round supported on this module.
|
||||
// The previous Ascon submission defined three Ascon permutations with 6, 8, and 12 rounds.
|
||||
// The NIST SP 800-232 standard specifies additional Ascon permutations by providing round
|
||||
// constants for up to 16 rounds to accommodate potential functionality extensions in the future.
|
||||
const max_nr_perm = 16
|
||||
|
||||
// The constants to derive round constants of the Ascon permutations
|
||||
// See Table 5. of NIST SP 800-232 docs
|
||||
//
|
||||
// 0 0x000000000000003c 8 0x00000000000000b4
|
||||
// 1 0x000000000000002d 9 0x00000000000000a5
|
||||
// 2 0x000000000000001e 10 0x0000000000000096
|
||||
// 3 0x000000000000000f 11 0x0000000000000087
|
||||
// 4 0x00000000000000f0 12 0x0000000000000078
|
||||
// 5 0x00000000000000e1 13 0x0000000000000069
|
||||
// 6 0x00000000000000d2 14 0x000000000000005a
|
||||
// 7 0x00000000000000c3 15 0x000000000000004b
|
||||
//
|
||||
// We use u8 instead, since the first 56 bits of the constants are zero
|
||||
const rnc = [u8(0x3c), 0x2d, 0x1e, 0x0f, 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78,
|
||||
0x69, 0x5a, 0x4b]
|
||||
|
||||
// ascon_pnr is ascon permutation routine with specified numbers of round nr, where 1 ≤ nr ≤ 16
|
||||
@[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')
|
||||
}
|
||||
// handle other number
|
||||
for i := max_nr_perm - nr; i < max_nr_perm; i++ {
|
||||
ascon_perm(mut s, rnc[i])
|
||||
}
|
||||
}
|
||||
|
||||
// ascon_perm was the main permutations routine in Ascon-family crypto. Its consist of
|
||||
// iterations of the round function that is defined as the composition of three steps, ie:
|
||||
// 1. the constant-addition layer (see Sec. 3.2),
|
||||
// 2. the substitution layer (see Sec.3.3), and,
|
||||
// 3. the linear diffusion layer
|
||||
@[direct_array_access]
|
||||
fn ascon_perm(mut s State, c u8) {
|
||||
// 3.2 Constant-Addition Layer step
|
||||
//
|
||||
// The constant-addition layer adds a 64-bit round constant 𝑐𝑖
|
||||
// to 𝑆₂ in round 𝑖, for 𝑖 ≥ 0, ie, this is equivalent to applying
|
||||
// the constant to only the least significant eight bits of 𝑆₂
|
||||
s.e2 ^= c
|
||||
|
||||
// 3.3. Substitution Layer
|
||||
// The substitution layer updates the state S with 64 parallel applications of the 5-bit
|
||||
// substitution box SBOX
|
||||
s.e0 ^= s.e4
|
||||
s.e4 ^= s.e3
|
||||
s.e2 ^= 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.e1 ^= s.e0
|
||||
s.e0 ^= s.e4
|
||||
s.e3 ^= s.e2
|
||||
s.e2 = ~(s.e2)
|
||||
|
||||
// 3.4. Linear Diffusion Layer
|
||||
//
|
||||
// The linear diffusion layer provides diffusion within each 64-bit word S,
|
||||
// defined as :
|
||||
// Σ0(𝑆0) = 𝑆0 ⊕ (𝑆0 ⋙ 19) ⊕ (𝑆0 ⋙ 28)
|
||||
// Σ1(𝑆1) = 𝑆1 ⊕ (𝑆1 ⋙ 61) ⊕ (𝑆1 ⋙ 39)
|
||||
// Σ2(𝑆2) = 𝑆2 ⊕ (𝑆2 ⋙ 1) ⊕ (𝑆2 ⋙ 6)
|
||||
// Σ3(𝑆3) = 𝑆3 ⊕ (𝑆3 ⋙ 10) ⊕ (𝑆3 ⋙ 17)
|
||||
// Σ4(𝑆4) = 𝑆4 ⊕ (𝑆4 ⋙ 7) ⊕ (𝑆4 ⋙ 41)
|
||||
/*
|
||||
s.e0 ^= rotate_right_64(s.e0, 19) ^ rotate_right_64(s.e0, 28)
|
||||
s.e1 ^= rotate_right_64(s.e1, 61) ^ rotate_right_64(s.e1, 39)
|
||||
s.e2 ^= rotate_right_64(s.e2, 1) ^ rotate_right_64(s.e2, 6)
|
||||
s.e3 ^= rotate_right_64(s.e3, 10) ^ rotate_right_64(s.e3, 17)
|
||||
s.e4 ^= rotate_right_64(s.e4, 7) ^ rotate_right_64(s.e4, 41)
|
||||
*/
|
||||
s.e0 ^= ascon_rotate_right(s.e0, 19) ^ ascon_rotate_right(s.e0, 28)
|
||||
s.e1 ^= ascon_rotate_right(s.e1, 61) ^ ascon_rotate_right(s.e1, 39)
|
||||
s.e2 ^= ascon_rotate_right(s.e2, 1) ^ ascon_rotate_right(s.e2, 6)
|
||||
s.e3 ^= ascon_rotate_right(s.e3, 10) ^ ascon_rotate_right(s.e3, 17)
|
||||
s.e4 ^= ascon_rotate_right(s.e4, 7) ^ ascon_rotate_right(s.e4, 41)
|
||||
}
|
||||
|
||||
// State is structure represents Ascon state. Its operates on the 320-bit opaque,
|
||||
// which is represented as five of 64-bit words.
|
||||
@[noinit]
|
||||
struct State {
|
||||
mut:
|
||||
e0 u64
|
||||
e1 u64
|
||||
e2 u64
|
||||
e3 u64
|
||||
e4 u64
|
||||
}
|
||||
|
||||
// clone returns a clone of current state.
|
||||
@[inline]
|
||||
fn clone_state(s State) State {
|
||||
return State{
|
||||
e0: s.e0
|
||||
e1: s.e1
|
||||
e2: s.e2
|
||||
e3: s.e3
|
||||
e4: s.e4
|
||||
}
|
||||
}
|
||||
|
||||
// reset this state with default value
|
||||
@[inline]
|
||||
fn reset_state(mut s State) {
|
||||
s.e0 = 0
|
||||
s.e1 = 0
|
||||
s.e2 = 0
|
||||
s.e3 = 0
|
||||
s.e4 = 0
|
||||
}
|
71
vlib/x/crypto/ascon/ascon_test.v
Normal file
71
vlib/x/crypto/ascon/ascon_test.v
Normal file
|
@ -0,0 +1,71 @@
|
|||
// Copyright ©2025 blackshirt.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
//
|
||||
module ascon
|
||||
|
||||
// This test mostly taken from https://docs.rs/ascon/latest/src/ascon/lib.rs.html
|
||||
fn test_ascon_round_one() {
|
||||
mut s := State{
|
||||
e0: u64(0x0123456789abcdef)
|
||||
e1: 0x23456789abcdef01
|
||||
e2: 0x456789abcdef0123
|
||||
e3: 0x6789abcdef012345
|
||||
e4: 0x89abcde01234567f
|
||||
}
|
||||
ascon_perm(mut s, 0x1f)
|
||||
|
||||
assert s.e0 == u64(0x3c1748c9be2892ce)
|
||||
assert s.e1 == u64(0x5eafb305cd26164f)
|
||||
assert s.e2 == u64(0xf9470254bb3a4213)
|
||||
assert s.e3 == u64(0xf0428daf0c5d3948)
|
||||
assert s.e4 == u64(0x281375af0b294899)
|
||||
}
|
||||
|
||||
fn test_ascon_round_p6() {
|
||||
mut s := State{
|
||||
e0: u64(0x0123456789abcdef)
|
||||
e1: 0xef0123456789abcd
|
||||
e2: 0xcdef0123456789ab
|
||||
e3: 0xabcdef0123456789
|
||||
e4: 0x89abcdef01234567
|
||||
}
|
||||
ascon_pnr(mut s, 6)
|
||||
assert s.e0 == u64(0xc27b505c635eb07f)
|
||||
assert s.e1 == u64(0xd388f5d2a72046fa)
|
||||
assert s.e2 == u64(0x9e415c204d7b15e7)
|
||||
assert s.e3 == u64(0xce0d71450fe44581)
|
||||
assert s.e4 == u64(0xdd7c5fef57befe48)
|
||||
}
|
||||
|
||||
fn test_ascon_round_p8() {
|
||||
mut s := State{
|
||||
e0: u64(0x0123456789abcdef)
|
||||
e1: 0xef0123456789abcd
|
||||
e2: 0xcdef0123456789ab
|
||||
e3: 0xabcdef0123456789
|
||||
e4: 0x89abcdef01234567
|
||||
}
|
||||
ascon_pnr(mut s, 8)
|
||||
assert s.e0 == u64(0x67ed228272f46eee)
|
||||
assert s.e1 == u64(0x80bc0b097aad7944)
|
||||
assert s.e2 == u64(0x2fa599382c6db215)
|
||||
assert s.e3 == u64(0x368133fae2f7667a)
|
||||
assert s.e4 == u64(0x28cefb195a7c651c)
|
||||
}
|
||||
|
||||
fn test_ascon_round_p12() {
|
||||
mut s := State{
|
||||
e0: u64(0x0123456789abcdef)
|
||||
e1: 0xef0123456789abcd
|
||||
e2: 0xcdef0123456789ab
|
||||
e3: 0xabcdef0123456789
|
||||
e4: 0x89abcdef01234567
|
||||
}
|
||||
ascon_pnr(mut s, 12)
|
||||
assert s.e0 == u64(0x206416dfc624bb14)
|
||||
assert s.e1 == u64(0x1b0c47a601058aab)
|
||||
assert s.e2 == u64(0x8934cfc93814cddd)
|
||||
assert s.e3 == u64(0xa9738d287a748e4b)
|
||||
assert s.e4 == u64(0xddd934f058afc7e1)
|
||||
}
|
146
vlib/x/crypto/ascon/cxof_test.v
Normal file
146
vlib/x/crypto/ascon/cxof_test.v
Normal file
|
@ -0,0 +1,146 @@
|
|||
// Copyright ©2025 blackshirt.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
//
|
||||
module ascon
|
||||
|
||||
import arrays
|
||||
import encoding.hex
|
||||
|
||||
struct CxofTest {
|
||||
count int
|
||||
msg string
|
||||
z string
|
||||
md string
|
||||
}
|
||||
|
||||
// Known Answer Tests (KAT) with 512-bits output for Ascon-CXOF128
|
||||
fn test_cxof128() ! {
|
||||
for item in cxof128_test_data {
|
||||
msg := hex.decode(item.msg)!
|
||||
z := hex.decode(item.z)!
|
||||
md := hex.decode(item.md)!
|
||||
|
||||
out := cxof128_64(msg, z)!
|
||||
assert md == out
|
||||
|
||||
// With Xof128Digest opaque
|
||||
mut cx := new_cxof128(default_xof_size, z)!
|
||||
md0 := cx.sum(msg)
|
||||
assert md0 == md
|
||||
|
||||
// test .read()
|
||||
cx.reset()
|
||||
mut dst := []u8{len: 64}
|
||||
_ := cx.write(msg)!
|
||||
nr := cx.read(mut dst)!
|
||||
assert nr == 64
|
||||
assert dst == md
|
||||
|
||||
// with chunked messages
|
||||
cx.reset()
|
||||
chunks := arrays.chunk[u8](msg, 20)
|
||||
mut tot := 0
|
||||
for chunk in chunks {
|
||||
n := cx.write(chunk)!
|
||||
tot += n
|
||||
}
|
||||
assert msg.len == tot
|
||||
|
||||
chunked_md := cx.sum([]u8{})
|
||||
assert chunked_md == md
|
||||
}
|
||||
}
|
||||
|
||||
const cxof128_test_data = [
|
||||
CxofTest{
|
||||
count: 1
|
||||
msg: ''
|
||||
z: ''
|
||||
md: '4f50159ef70bb3dad8807e034eaebd44c4fa2cbbc8cf1f05511ab66cdcc529905ca12083fc186ad899b270b1473dc5f7ec88d1052082dcdfe69fb75d269e7b74'
|
||||
},
|
||||
CxofTest{
|
||||
count: 2
|
||||
msg: ''
|
||||
z: '10'
|
||||
md: '0c93a483e7d574d49fe52cce03ee646117977d57a8aa57704ab4daf44b501430ff6ac11a5d1fd6f2154b5c65728268270c8bb578508487b8965718ada6272fd6'
|
||||
},
|
||||
CxofTest{
|
||||
count: 3
|
||||
msg: ''
|
||||
z: '1011'
|
||||
md: 'd1106c7622e79fe955bd9d79e03b918e770fe0e0cddde28beb924b02c5fc936b33acca299c89eca5d71886cbbfa4d54a21c55fde2b679f5e2488063a1719dc32'
|
||||
},
|
||||
CxofTest{
|
||||
count: 4
|
||||
msg: ''
|
||||
z: '101112'
|
||||
md: '6a53a6dbf1bec15a79ce1214ff76a4d6bb16f60cfa56bf2c218aec5e160372117d2a2e647b128624e9b1d2259faf083f2bedd0fc751a2e2ff268d0ee026b6449'
|
||||
},
|
||||
CxofTest{
|
||||
count: 5
|
||||
msg: ''
|
||||
z: '10111213'
|
||||
md: 'cc333dd5b4ea61abe4376d61058b16df5eda7056299865ed7d25f43ac5b8541574608bd95ab0a3c3b74abd4abf9e50e63be6efe1b836b58595d8c47705c4dffb'
|
||||
},
|
||||
CxofTest{
|
||||
count: 6
|
||||
msg: ''
|
||||
z: '1011121314'
|
||||
md: '8ee69d28b1bf3eafacf1e169fd10b6b7b72a7e2aaf0625e8e7c00153833b7224ed8c8c127b9808352c5647f9e862958d6de9eb93c4a236d59ecd84665e7164d9'
|
||||
},
|
||||
CxofTest{
|
||||
count: 7
|
||||
msg: ''
|
||||
z: '101112131415'
|
||||
md: '3681695c40d83f60b401ecfa14bc03780ad474438f74b823eec9f0d5a375c13488803d3b4b8c8d4acd03186039f905fa15c7860dd0e9d566f31cd9e5822a937c'
|
||||
},
|
||||
CxofTest{
|
||||
count: 8
|
||||
msg: ''
|
||||
z: '10111213141516'
|
||||
md: '717a9ba3b3c00f4078572b2d3fb3f0a86d45f70bc4e1cd89cb7a952bfa64162383735534ecbe0a7e62e7592cf447404db0361d98c2237245688ead15c05ae59b'
|
||||
},
|
||||
CxofTest{
|
||||
count: 9
|
||||
msg: ''
|
||||
z: '1011121314151617'
|
||||
md: '61324766441dd6c11e1736bad1d2185820885ed76fe2ce537775a6e855eeafd2a6651b5e862a44982765f8b4c7cbe9c8b354f569ead6abc62cc9b7cdd72e0cb3'
|
||||
},
|
||||
CxofTest{
|
||||
count: 10
|
||||
msg: ''
|
||||
z: '101112131415161718'
|
||||
md: '32fde6b9d290f56fc74aac9368f32c69973e1bab35d96118db7181aae577687673c01a9e35327aded556987eed3441d4f42ec36b0c198498d9e7f357b948d560'
|
||||
},
|
||||
CxofTest{
|
||||
count: 11
|
||||
msg: ''
|
||||
z: '10111213141516171819'
|
||||
md: '690fc893055910d7d1d38055cf5589bbbe6b82bf175847ab3e0fd9a578b044dcb42be2932067eaa563a09e634581f34c2b4cfb38e1b06841b45b7b34c746d6dd'
|
||||
},
|
||||
CxofTest{
|
||||
count: 12
|
||||
msg: ''
|
||||
z: '101112131415161718191a'
|
||||
md: 'ecdab5b15324f99a1709be26fc329d305bd475e5f39bc2b63788792166ad08fe720ccd14e0a4de7d83ede1c7744929dc509c73748d6661a3d3215995357d3f88'
|
||||
},
|
||||
CxofTest{
|
||||
count: 13
|
||||
msg: ''
|
||||
z: '101112131415161718191a1b'
|
||||
md: 'ec2ba3309cfa6d6d0b581374e7c020ad17c330ea2b76d48724a415dceca3859c11146c2f64e52e44d27b1c44fd27476990a2e959b9998827a527a7e69089895f'
|
||||
},
|
||||
CxofTest{
|
||||
count: 14
|
||||
msg: ''
|
||||
z: '101112131415161718191a1b1c'
|
||||
md: '659a59bc7fdece2f1bafa9f1bfb4c262043f74da550f85c902c9c4302adfcf898fbcd74c92d67bded153137e0d32ccba88767354be99103dfeb59c686ca98dea'
|
||||
},
|
||||
CxofTest{
|
||||
count: 15
|
||||
msg: ''
|
||||
z: '101112131415161718191a1b1c1d'
|
||||
md: '17756044b742028b508d797c2c75a0722dde763c59d3fe5f70435b82faca5a80fe9c5ec9f3c59072ae48f37a241281c25d2e903c9d9290128265f1fe92b80bed'
|
||||
},
|
||||
]
|
122
vlib/x/crypto/ascon/digest.v
Normal file
122
vlib/x/crypto/ascon/digest.v
Normal file
|
@ -0,0 +1,122 @@
|
|||
// Copyright ©2025 blackshirt.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
//
|
||||
// Common helpers used by Ascon-Hash256, Ascon-XOF128 and Ascon-CXOF128
|
||||
module ascon
|
||||
|
||||
import encoding.binary
|
||||
|
||||
// The Digest is an internal structure used by Ascon-hashing variants.
|
||||
@[noinit]
|
||||
struct Digest {
|
||||
State
|
||||
mut:
|
||||
// buffer for storing unprocessed data for streaming-way
|
||||
buf []u8 = []u8{len: block_size}
|
||||
// length of leftover unprocessed bytes on the digest buffer
|
||||
length int
|
||||
|
||||
// internal flag
|
||||
done bool
|
||||
}
|
||||
|
||||
// finish finalizes state by writing last block in the Digest internal buffer.
|
||||
// and consequently updates Digest state.
|
||||
@[direct_array_access; inline]
|
||||
fn (mut d Digest) finish() {
|
||||
if d.length >= d.buf.len {
|
||||
panic('Digest.finish: internal error')
|
||||
}
|
||||
// Process for the last block stored on the internal buffer
|
||||
block := unsafe { d.buf[..d.length] }
|
||||
d.State.e0 ^= pad(block.len)
|
||||
d.State.e0 ^= load_bytes(block, block.len)
|
||||
|
||||
// Permutation step was done in squeezing-phase
|
||||
// ascon_pnr(mut d.State, 12)
|
||||
|
||||
// zeroing Digest buffer
|
||||
d.length = 0
|
||||
unsafe { d.buf.reset() }
|
||||
|
||||
// After finishing phase, we can't write data anymore into state
|
||||
d.done = true
|
||||
}
|
||||
|
||||
// absorb absorbs message msg_ into Ascon-HASH256 state
|
||||
@[direct_array_access]
|
||||
fn (mut d Digest) absorb(msg_ []u8) int {
|
||||
// nothing to absorb, just return
|
||||
if msg_.len == 0 {
|
||||
return 0
|
||||
}
|
||||
mut msg := msg_.clone()
|
||||
unsafe {
|
||||
// Check if internal buffer has previous unprocessed bytes.
|
||||
// If its on there, try to empty the buffer.
|
||||
if d.length > 0 {
|
||||
// There are bytes in the d.buf, append it with bytes taken from msg
|
||||
if d.length + msg.len >= block_size {
|
||||
n := copy(mut d.buf[d.length..], msg)
|
||||
msg = msg[n..]
|
||||
d.length += n
|
||||
// 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, 12)
|
||||
// reset the internal buffer
|
||||
d.length = 0
|
||||
d.buf.reset()
|
||||
}
|
||||
} else {
|
||||
// Otherwise, still fit to buffer, but nof fully fills the d.buf
|
||||
// just stores into buffer without processing
|
||||
n := copy(mut d.buf[d.length..], msg)
|
||||
msg = msg[n..]
|
||||
d.length += n
|
||||
}
|
||||
}
|
||||
// process for full block
|
||||
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, 12)
|
||||
}
|
||||
// If there are partial block, just stored into buffer.
|
||||
if msg.len > 0 {
|
||||
n := copy(mut d.buf[d.length..], msg)
|
||||
msg = msg[n..]
|
||||
d.length += n
|
||||
}
|
||||
return msg_.len - msg.len
|
||||
}
|
||||
}
|
||||
|
||||
// squeeze squeezes the Digest's state and calculates checksum output for the current state.
|
||||
// It accepts destination buffer with desired buffer length you want to output.
|
||||
@[direct_array_access; inline]
|
||||
fn (mut d Digest) squeeze(mut dst []u8) int {
|
||||
// check
|
||||
if dst.len < 1 || dst.len > max_hash_size {
|
||||
panic('Digest.squeeze: invalid dst.len')
|
||||
}
|
||||
// The squeezing phase begins after msg is absorbed with an
|
||||
// permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[12] to the state:
|
||||
ascon_pnr(mut d.State, 12)
|
||||
// mut out := []u8{len: size}
|
||||
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, 12)
|
||||
pos += block_size
|
||||
clen -= block_size
|
||||
}
|
||||
// final output, the resulting 256-bit digest is the concatenation of hash blocks
|
||||
store_bytes(mut dst[pos..], d.State.e0, clen)
|
||||
pos += clen
|
||||
|
||||
return pos
|
||||
}
|
42
vlib/x/crypto/ascon/example/cxof_example.v
Normal file
42
vlib/x/crypto/ascon/example/cxof_example.v
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright ©2025 blackshirt.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
//
|
||||
import encoding.hex
|
||||
import x.crypto.ascon
|
||||
|
||||
// The material was generated from https://hashing.tools/ascon/ascon-hash
|
||||
fn main() {
|
||||
msg := 'Example of CXof128 message'.bytes()
|
||||
cs := 'custom-string-cxof128'.bytes()
|
||||
|
||||
// expected output generated from the tool, with 32, 64 dan 75-bytes output
|
||||
digest32 := hex.decode('d71492b816b1ac27f53f9c13be45c1d2d0530b8dde7fde8d34cb563f79b3d3d3')!
|
||||
digest64 := hex.decode('d71492b816b1ac27f53f9c13be45c1d2d0530b8dde7fde8d34cb563f79b3d3d3601d03474ec6fe1f6b8dc5dd79bea20aff4c95ca3549202b1aaeb9e66b5df398')!
|
||||
digest75 := hex.decode('d71492b816b1ac27f53f9c13be45c1d2d0530b8dde7fde8d34cb563f79b3d3d3601d03474ec6fe1f6b8dc5dd79bea20aff4c95ca3549202b1aaeb9e66b5df3985a88fd8bce0f9570962321')!
|
||||
|
||||
out32 := ascon.cxof128(msg, 32, cs)!
|
||||
out64 := ascon.cxof128(msg, 64, cs)!
|
||||
out75 := ascon.cxof128(msg, 75, cs)!
|
||||
dump(out32 == digest32) // out32 == digest32: true
|
||||
dump(out64 == digest64) // out64 == digest64: true
|
||||
dump(out75 == digest75) // out75 == digest75: true
|
||||
|
||||
// With object based
|
||||
mut x := ascon.new_cxof128(32, cs)!
|
||||
s32 := x.sum(msg)
|
||||
dump(s32 == digest32) // s32 == digest32: true
|
||||
|
||||
// with sized output
|
||||
x.reset()
|
||||
_ := x.write(msg)!
|
||||
mut b64 := []u8{len: 64}
|
||||
_ := x.read(mut b64)!
|
||||
dump(b64 == digest64) // b64 == digest64: true
|
||||
|
||||
x.reset()
|
||||
_ := x.write(msg)!
|
||||
mut b75 := []u8{len: 75}
|
||||
_ := x.read(mut b75)!
|
||||
dump(b75 == digest75) // b75 == digest75: true
|
||||
}
|
16
vlib/x/crypto/ascon/example/hash256_example.v
Normal file
16
vlib/x/crypto/ascon/example/hash256_example.v
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright ©2025 blackshirt.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
//
|
||||
import encoding.hex
|
||||
import x.crypto.ascon
|
||||
|
||||
// The material was generated from https://hashing.tools/ascon/ascon-hash
|
||||
fn main() {
|
||||
msg := 'Example of hash256 message'.bytes()
|
||||
// expected output generated from the tool
|
||||
digest := hex.decode('0889515a9cfe28ab3a43882884d5933bb74aa09f3c767f8c699b5d7114811340')!
|
||||
|
||||
out := ascon.sum256(msg)
|
||||
dump(out == digest) // out == digest: true
|
||||
}
|
40
vlib/x/crypto/ascon/example/xof_example.v
Normal file
40
vlib/x/crypto/ascon/example/xof_example.v
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Copyright ©2025 blackshirt.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
//
|
||||
import encoding.hex
|
||||
import x.crypto.ascon
|
||||
|
||||
// The material was generated from https://hashing.tools/ascon/ascon-hash
|
||||
fn main() {
|
||||
msg := 'Example of Xof128 message'.bytes()
|
||||
// expected output generated from the tool, with 32, 64 dan 75-bytes output
|
||||
digest32 := hex.decode('424caaf68eb94aa251536bb6b565c0695b8944f932d011b1049df85b7f27d2f3')!
|
||||
digest64 := hex.decode('424caaf68eb94aa251536bb6b565c0695b8944f932d011b1049df85b7f27d2f3bf704643a643c3f2dcfb1e0bc73ec55781b5283966d2d1da85d89794ca5c292e')!
|
||||
digest75 := hex.decode('424caaf68eb94aa251536bb6b565c0695b8944f932d011b1049df85b7f27d2f3bf704643a643c3f2dcfb1e0bc73ec55781b5283966d2d1da85d89794ca5c292e36260815a8f10088e3804c')!
|
||||
|
||||
out32 := ascon.xof128(msg, 32)!
|
||||
out64 := ascon.xof128(msg, 64)!
|
||||
out75 := ascon.xof128(msg, 75)!
|
||||
dump(out32 == digest32) // out32 == digest32: true
|
||||
dump(out64 == digest64) // out64 == digest64: true
|
||||
dump(out75 == digest75) // out75 == digest75: true
|
||||
|
||||
// With object based
|
||||
mut x := ascon.new_xof128(32)
|
||||
s32 := x.sum(msg)
|
||||
dump(s32 == digest32) // s32 == digest32: true
|
||||
|
||||
// with sized output
|
||||
x.reset()
|
||||
_ := x.write(msg)!
|
||||
mut b64 := []u8{len: 64}
|
||||
_ := x.read(mut b64)!
|
||||
dump(b64 == digest64) // b64 == digest64: true
|
||||
|
||||
x.reset()
|
||||
_ := x.write(msg)!
|
||||
mut b75 := []u8{len: 75}
|
||||
_ := x.read(mut b75)!
|
||||
dump(b75 == digest75) // b75 == digest75: true
|
||||
}
|
136
vlib/x/crypto/ascon/hash.v
Normal file
136
vlib/x/crypto/ascon/hash.v
Normal file
|
@ -0,0 +1,136 @@
|
|||
// Copyright ©2025 blackshirt.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
//
|
||||
// This file implement hashing routines based on Ascon-Hash256 schema defined in NIST SP 800-232 standard,
|
||||
// Ascon-Hash256 hashing produces 256-bits output.
|
||||
module ascon
|
||||
|
||||
// block_size is the size (rate) of Ascon-Hash256, Ascon-XOF128 and Ascon-CXOF128 operates on.
|
||||
const block_size = 8
|
||||
|
||||
// hash256_size is the length of the Ascon-Hash256 checksum output, in bytes
|
||||
const hash256_size = 32
|
||||
|
||||
// hash256_initial_state is a precomputed value for Ascon-Hash256 state.
|
||||
//
|
||||
// The 320-bit internal state of Ascon-Hash256 is initialized with the
|
||||
// concatenation of the 64-bit 𝐼𝑉 = 0x0000080100cc0002 and 256 zeroes, followed
|
||||
// by the 𝐴𝑠𝑐𝑜𝑛-𝑝[12] permutation as S ← 𝐴𝑠𝑐𝑜𝑛-𝑝[12](𝐼𝑉 ∥0256).
|
||||
//
|
||||
// s.e0 = 0x0000080100cc0002
|
||||
// s.e1 = 0
|
||||
// s.e2 = 0
|
||||
// s.e3 = 0
|
||||
// s.e4 = 0
|
||||
// ascon_pnr(mut s, 12)
|
||||
//
|
||||
// Above step can be replaced with precomputed value to reduce runtime computations.
|
||||
// See the detail on the NIST SP 800-232 standard on Sec A.3. Precomputation
|
||||
// 𝑆0 ← 0x9b1e5494e934d681
|
||||
// 𝑆1 ← 0x4bc3a01e333751d2
|
||||
// 𝑆2 ← 0xae65396c6b34b81a
|
||||
// 𝑆3 ← 0x3c7fd4a4d56a4db3
|
||||
// 𝑆4 ← 0x1a5c464906c5976d
|
||||
//
|
||||
const hash256_initial_state = State{
|
||||
e0: u64(0x9b1e5494e934d681)
|
||||
e1: 0x4bc3a01e333751d2
|
||||
e2: 0xae65396c6b34b81a
|
||||
e3: 0x3c7fd4a4d56a4db3
|
||||
e4: 0x1a5c464906c5976d
|
||||
}
|
||||
|
||||
// sum256 creates an Ascon-Hash256 checksum for bytes on msg and produces a 256-bit hash.
|
||||
@[direct_array_access]
|
||||
pub fn sum256(msg []u8) []u8 {
|
||||
mut h := new_hash256()
|
||||
_ := h.write(msg) or { panic(err) }
|
||||
h.Digest.finish()
|
||||
mut dst := []u8{len: hash256_size}
|
||||
n := h.Digest.squeeze(mut dst)
|
||||
return dst[..n]
|
||||
}
|
||||
|
||||
// Hash256 is an opaque provides an implementation of Ascon-Hash256 from NIST.SP.800-232 standard.
|
||||
// Its implements `hash.Hash` interface.
|
||||
@[noinit]
|
||||
pub struct Hash256 {
|
||||
Digest
|
||||
}
|
||||
|
||||
// new_hash256 creates a new Ascon-Hash256 instance.
|
||||
@[direct_array_access]
|
||||
pub fn new_hash256() &Hash256 {
|
||||
return &Hash256{
|
||||
Digest: Digest{
|
||||
State: hash256_initial_state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// size returns an underlying size of Hash256 checksum, ie, 32-bytes
|
||||
pub fn (h &Hash256) size() int {
|
||||
return hash256_size
|
||||
}
|
||||
|
||||
// block_size returns an underlying Hash256 block size operates on, ie, 8-bytes
|
||||
pub fn (h &Hash256) block_size() int {
|
||||
return block_size
|
||||
}
|
||||
|
||||
// reset reinits internal Hash256 state into default initialized state.
|
||||
@[direct_array_access]
|
||||
pub fn (mut h Hash256) reset() {
|
||||
h.Digest.State = hash256_initial_state
|
||||
unsafe { h.Digest.buf.reset() }
|
||||
h.Digest.length = 0
|
||||
h.Digest.done = false
|
||||
}
|
||||
|
||||
// free releases out the resources taken by the `h`. Dont use x after .free call.
|
||||
@[unsafe]
|
||||
pub fn (mut h Hash256) free() {
|
||||
$if prealloc {
|
||||
return
|
||||
}
|
||||
unsafe {
|
||||
h.Digest.buf.free()
|
||||
}
|
||||
// Mark it as unusable
|
||||
h.Digest.done = true
|
||||
}
|
||||
|
||||
// write writes out the content of message and updates internal Hash256 state.
|
||||
@[direct_array_access]
|
||||
pub fn (mut h Hash256) write(msg []u8) !int {
|
||||
if h.Digest.done {
|
||||
panic('Digest: writing after done ')
|
||||
}
|
||||
return h.absorb(msg)
|
||||
}
|
||||
|
||||
// clone returns the clone of the current Hash256
|
||||
@[direct_array_access]
|
||||
fn (h &Hash256) clone() &Hash256 {
|
||||
digest := Digest{
|
||||
State: h.Digest.State
|
||||
buf: h.Digest.buf.clone()
|
||||
length: h.Digest.length
|
||||
done: h.Digest.done
|
||||
}
|
||||
return &Hash256{digest}
|
||||
}
|
||||
|
||||
// sum returns an Ascon-Hash256 checksum of the bytes in data.
|
||||
@[direct_array_access]
|
||||
pub fn (mut h Hash256) sum(data []u8) []u8 {
|
||||
// working on the clone of the h, so we can keep writing
|
||||
mut h0 := h.clone()
|
||||
_ := h0.write(data) or { panic(err) }
|
||||
h0.Digest.finish()
|
||||
mut dst := []u8{len: hash256_size}
|
||||
n := h0.Digest.squeeze(mut dst)
|
||||
h0.reset()
|
||||
return dst[..n]
|
||||
}
|
169
vlib/x/crypto/ascon/hash_test.v
Normal file
169
vlib/x/crypto/ascon/hash_test.v
Normal file
|
@ -0,0 +1,169 @@
|
|||
// Copyright ©2025 blackshirt.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
//
|
||||
module ascon
|
||||
|
||||
import arrays
|
||||
import encoding.hex
|
||||
|
||||
struct HashTest {
|
||||
count int
|
||||
msg string
|
||||
md string
|
||||
}
|
||||
|
||||
// This test material mostly taken and adapted from Known-Answer-Test (KAT) of reference implementation.
|
||||
// See at https://github.com/ascon/ascon-c/blob/main/crypto_hash/asconhash256/LWC_HASH_KAT_128_256.txt
|
||||
fn test_hash256_sum_chunked() ! {
|
||||
item := HashTest{
|
||||
count: 500
|
||||
msg: '000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2'
|
||||
md: '8174F5E8DFC9441DBB7A183D56F431B1AEF9513E747A3878BB806BAE27655E3E'
|
||||
}
|
||||
msg := hex.decode(item.msg)!
|
||||
md := hex.decode(item.md)!
|
||||
// One shoot sum256 function
|
||||
md0 := sum256(msg)
|
||||
assert md0 == md
|
||||
|
||||
// Digest based
|
||||
mut h := new_hash256()
|
||||
expected_md := h.sum(msg)
|
||||
assert expected_md == md
|
||||
|
||||
// with multistep
|
||||
h.reset()
|
||||
_ := h.Digest.absorb(msg)
|
||||
h.Digest.finish()
|
||||
mut dst := []u8{len: hash256_size}
|
||||
h.Digest.squeeze(mut dst)
|
||||
assert dst == md
|
||||
|
||||
// with splitted message
|
||||
msg0 := msg[0..200]
|
||||
msg1 := msg[200..400]
|
||||
msg2 := msg[400..]
|
||||
h.reset()
|
||||
_ := h.Digest.absorb(msg0)
|
||||
_ := h.Digest.absorb(msg1)
|
||||
_ := h.Digest.absorb(msg2)
|
||||
h.Digest.finish()
|
||||
h.Digest.squeeze(mut dst)
|
||||
assert dst == md
|
||||
|
||||
// with arrays chunk
|
||||
h.reset()
|
||||
chunks := arrays.chunk[u8](msg, 200)
|
||||
mut n := 0
|
||||
for chunk in chunks {
|
||||
n += h.Digest.absorb(chunk)
|
||||
}
|
||||
assert n == msg.len
|
||||
h.Digest.finish()
|
||||
h.Digest.squeeze(mut dst)
|
||||
assert dst == md
|
||||
|
||||
// with sum
|
||||
h.reset()
|
||||
for chunk in chunks {
|
||||
n += h.write(chunk)!
|
||||
}
|
||||
chunked_md := h.sum([]u8{})
|
||||
assert chunked_md == md
|
||||
}
|
||||
|
||||
fn test_hash256_sum_kat() ! {
|
||||
for item in ascon_hash256_test_data {
|
||||
msg := hex.decode(item.msg)!
|
||||
md := hex.decode(item.md)!
|
||||
out := sum256(msg)
|
||||
assert out == md
|
||||
|
||||
// work with Digest opaque
|
||||
mut h := new_hash256()
|
||||
exp_md := h.sum(msg)
|
||||
assert exp_md == md
|
||||
|
||||
// Lets work in streaming-way
|
||||
chunks := arrays.chunk[u8](msg, 7)
|
||||
h.reset()
|
||||
mut tot := 0
|
||||
for chunk in chunks {
|
||||
n := h.write(chunk)!
|
||||
tot += n
|
||||
}
|
||||
assert msg.len == tot
|
||||
|
||||
chunked_md := h.sum([]u8{})
|
||||
assert chunked_md == md
|
||||
}
|
||||
}
|
||||
|
||||
const ascon_hash256_test_data = [
|
||||
HashTest{
|
||||
count: 1
|
||||
msg: ''
|
||||
md: '0b3be5850f2f6b98caf29f8fdea89b64a1fa70aa249b8f839bd53baa304d92b2'
|
||||
},
|
||||
HashTest{
|
||||
count: 2
|
||||
msg: '00'
|
||||
md: '0728621035af3ed2bca03bf6fde900f9456f5330e4b5ee23e7f6a1e70291bc80'
|
||||
},
|
||||
HashTest{
|
||||
count: 3
|
||||
msg: '0001'
|
||||
md: '6115e7c9c4081c2797fc8fe1bc57a836afa1c5381e556dd583860ca2dfb48dd2'
|
||||
},
|
||||
HashTest{
|
||||
count: 4
|
||||
msg: '000102'
|
||||
md: '265ab89a609f5a05dca57e83fbba700f9a2d2c4211ba4cc9f0a1a369e17b915c'
|
||||
},
|
||||
HashTest{
|
||||
count: 5
|
||||
msg: '00010203'
|
||||
md: 'd7e4c7ed9b8a325cd08b9ef259f8877054ecd8304fe1b2d7fd847137df6727ee'
|
||||
},
|
||||
HashTest{
|
||||
count: 6
|
||||
msg: '0001020304'
|
||||
md: 'c7b28962d4f5c2211f466f83d3c57ae1504387e2a326949747a8376447a6bb51'
|
||||
},
|
||||
HashTest{
|
||||
count: 7
|
||||
msg: '000102030405'
|
||||
md: 'dc0c6748af8ffe63e1084aa3e5786a194685c88c21348b29e184fb50409703bc'
|
||||
},
|
||||
HashTest{
|
||||
count: 8
|
||||
msg: '00010203040506'
|
||||
md: '3e4d273ba69b3b9c53216107e88b75cdbeedbcbf8faf0219c3928ab62b116577'
|
||||
},
|
||||
HashTest{
|
||||
count: 9
|
||||
msg: '0001020304050607'
|
||||
md: 'b88e497ae8e6fb641b87ef622eb8f2fca0ed95383f7ffebe167acf1099ba764f'
|
||||
},
|
||||
HashTest{
|
||||
count: 10
|
||||
msg: '000102030405060708'
|
||||
md: '94269C30E0296E1EC86655041841823EFA1927F520FD58C8E9BCE6197878C1A6'
|
||||
},
|
||||
HashTest{
|
||||
count: 500
|
||||
msg: '000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2'
|
||||
md: '8174F5E8DFC9441DBB7A183D56F431B1AEF9513E747A3878BB806BAE27655E3E'
|
||||
},
|
||||
HashTest{
|
||||
count: 501
|
||||
msg: '000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3'
|
||||
md: 'E73D4DDB9D248BF2C0F8D49892D7455A4C3053153DE7F79BA4487C7D823F605C'
|
||||
},
|
||||
HashTest{
|
||||
count: 502
|
||||
msg: '000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4'
|
||||
md: 'B73FA04079263F6A733B67466552784B436138F41F80B72C4D5D03934B72207D'
|
||||
},
|
||||
]
|
93
vlib/x/crypto/ascon/profile.txt
Normal file
93
vlib/x/crypto/ascon/profile.txt
Normal file
|
@ -0,0 +1,93 @@
|
|||
2 0.016ms 0.011ms 8196ns strings__new_builder
|
||||
6 0.002ms 0.001ms 334ns strings__Builder_write_string
|
||||
2 0.002ms 0.001ms 1062ns strings__Builder_str
|
||||
2 0.000ms 0.000ms 130ns strings__Builder_free
|
||||
2880000 103.704ms 103.704ms 36ns math__bits__rotate_left_32
|
||||
2 0.000ms 0.000ms 50ns array_clear
|
||||
1000 0.134ms 0.087ms 134ns array_reset
|
||||
4 0.000ms 0.000ms 76ns array_get
|
||||
61000 3.190ms 3.190ms 52ns array_slice
|
||||
1000 0.426ms 0.159ms 426ns array_clone_to_depth
|
||||
8000 0.952ms 0.657ms 119ns array_set
|
||||
6 0.001ms 0.001ms 194ns array_push_many
|
||||
2 0.001ms 0.000ms 270ns array_free
|
||||
4000 0.560ms 0.370ms 140ns copy
|
||||
14007 0.518ms 0.518ms 37ns panic_on_negative_len
|
||||
14007 0.538ms 0.538ms 38ns panic_on_negative_cap
|
||||
2000 0.625ms 0.383ms 313ns __new_array_noscan
|
||||
12005 4.232ms 2.128ms 353ns __new_array_with_default_noscan
|
||||
2 0.008ms 0.001ms 3762ns new_array_from_c_array_noscan
|
||||
6 0.001ms 0.001ms 170ns array_push_noscan
|
||||
1 0.000ms 0.000ms 241ns at_exit
|
||||
4 0.044ms 0.001ms 11080ns println
|
||||
4 0.043ms 0.001ms 10807ns _writeln_to_fd
|
||||
8 0.042ms 0.042ms 5286ns _write_buf_to_fd
|
||||
2003 0.279ms 0.279ms 139ns _v_malloc
|
||||
4 0.001ms 0.001ms 358ns malloc_noscan
|
||||
1000 0.211ms 0.211ms 211ns vcalloc
|
||||
14007 1.297ms 1.297ms 93ns vcalloc_noscan
|
||||
6 0.000ms 0.000ms 49ns _v_free
|
||||
2003 0.459ms 0.180ms 229ns memdup
|
||||
2 0.000ms 0.000ms 165ns memdup_noscan
|
||||
1 0.000ms 0.000ms 81ns gc_set_warn_proc
|
||||
1 0.005ms 0.005ms 5009ns builtin_init
|
||||
13015 0.523ms 0.523ms 40ns vmemcpy
|
||||
4002 0.190ms 0.190ms 48ns vmemmove
|
||||
1000 0.047ms 0.047ms 47ns vmemset
|
||||
4001 0.547ms 0.376ms 137ns _result_ok
|
||||
2 0.003ms 0.000ms 1492ns i64_str
|
||||
2 0.003ms 0.001ms 1287ns impl_i64_to_string
|
||||
2 0.000ms 0.000ms 130ns tos
|
||||
2 0.000ms 0.000ms 135ns u8_vstring_with_len
|
||||
2 0.000ms 0.000ms 226ns string_free
|
||||
2 0.005ms 0.001ms 2505ns StrIntpData_process_str_intp_data
|
||||
2 0.027ms 0.001ms 13294ns str_intp
|
||||
2 0.000ms 0.000ms 40ns ArrayFlags_has
|
||||
2 0.000ms 0.000ms 45ns ArrayFlags_set
|
||||
2000 0.082ms 0.082ms 41ns time__Duration_microseconds
|
||||
2000 0.321ms 0.179ms 160ns time__new_stopwatch
|
||||
2000 0.317ms 0.174ms 158ns time__StopWatch_elapsed
|
||||
4002 0.285ms 0.285ms 71ns time__sys_mono_now
|
||||
8000 0.330ms 0.330ms 41ns encoding__binary__big_endian_put_u32
|
||||
1000 0.049ms 0.049ms 49ns encoding__binary__big_endian_put_u64
|
||||
37000 1.497ms 1.497ms 40ns encoding__binary__little_endian_u64
|
||||
4000 0.197ms 0.197ms 49ns encoding__binary__little_endian_put_u64
|
||||
38 0.002ms 0.002ms 43ns hash__wymum
|
||||
1000 2.113ms 0.177ms 2113ns crypto__sha256__Digest_init
|
||||
1000 1.337ms 0.384ms 1337ns crypto__sha256__Digest_reset
|
||||
1000 3.111ms 0.223ms 3111ns crypto__sha256__new
|
||||
3000 229.010ms 0.827ms 76337ns crypto__sha256__Digest_write
|
||||
1000 50.366ms 1.068ms 50366ns crypto__sha256__Digest_checksum
|
||||
1000 234.793ms 0.171ms 234793ns crypto__sha256__sum256
|
||||
2000 226.598ms 0.162ms 113299ns crypto__sha256__block
|
||||
2000 226.436ms 120.919ms 113218ns crypto__sha256__block_generic
|
||||
42000 449.579ms 22.965ms 10704ns x__crypto__ascon__ascon_pnr
|
||||
504000 426.614ms 241.540ms 846ns x__crypto__ascon__ascon_perm
|
||||
1000 0.929ms 0.232ms 929ns x__crypto__ascon__Digest_finish
|
||||
1000 55.255ms 0.781ms 55255ns x__crypto__ascon__Digest_squeeze
|
||||
1000 405.510ms 5.310ms 405510ns x__crypto__ascon__Digest_absorb_without_idx
|
||||
1000 462.994ms 0.236ms 462994ns x__crypto__ascon__sum256
|
||||
1000 0.763ms 0.143ms 763ns x__crypto__ascon__new_hash256
|
||||
1000 405.811ms 0.150ms 405811ns x__crypto__ascon__Hash256_write
|
||||
1000 0.040ms 0.040ms 40ns x__crypto__ascon__pad
|
||||
1000 0.046ms 0.046ms 46ns x__crypto__ascon__u64le
|
||||
4000 0.148ms 0.148ms 37ns x__crypto__ascon__set_byte
|
||||
1000 0.471ms 0.277ms 471ns x__crypto__ascon__load_bytes
|
||||
1000 0.038ms 0.038ms 38ns x__crypto__ascon__store_bytes
|
||||
5040000 185.074ms 185.074ms 37ns x__crypto__ascon__ascon_rotate_right
|
||||
8 0.000ms 0.000ms 42ns rand__seed__nr_next
|
||||
2 0.007ms 0.004ms 3732ns rand__seed__time_seed_array
|
||||
1 0.005ms 0.003ms 4879ns rand__seed__time_seed_64
|
||||
1 0.000ms 0.000ms 220ns rand__wyrand__WyRandRNG_seed
|
||||
4 0.000ms 0.000ms 85ns rand__wyrand__WyRandRNG_u8
|
||||
38 0.005ms 0.003ms 129ns rand__wyrand__WyRandRNG_u64
|
||||
1 0.000ms 0.000ms 50ns rand__wyrand__WyRandRNG_block_size
|
||||
1 0.019ms 0.000ms 19066ns rand__init
|
||||
1 0.007ms 0.002ms 7243ns rand__read_64
|
||||
1 0.008ms 0.000ms 7544ns rand__read_internal
|
||||
1 0.011ms 0.000ms 10600ns rand__PRNG_bytes
|
||||
1 0.012ms 0.000ms 12234ns rand__new_default
|
||||
1 0.000ms 0.000ms 40ns rand__bytes
|
||||
1 235.369ms 0.205ms 235368978ns main__bench_crypto_sha256
|
||||
1 463.604ms 0.190ms 463603787ns main__bench_ascon_sum256
|
||||
1 698.984ms 0.000ms 698983845ns main__main
|
110
vlib/x/crypto/ascon/util.v
Normal file
110
vlib/x/crypto/ascon/util.v
Normal file
|
@ -0,0 +1,110 @@
|
|||
// Copyright ©2025 blackshirt.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
//
|
||||
// Utility helpers used across the module
|
||||
module ascon
|
||||
|
||||
import math.bits
|
||||
import encoding.binary
|
||||
|
||||
// rotate_right_64 rotates x right by k bits
|
||||
fn rotate_right_64(x u64, k int) u64 {
|
||||
// call rotate_left_64(x, -k).
|
||||
return bits.rotate_left_64(x, -k)
|
||||
}
|
||||
|
||||
// clear_bytes clears the bytes of x in n byte
|
||||
@[inline]
|
||||
fn clear_bytes(x u64, n int) u64 {
|
||||
mut c := x
|
||||
for i := 0; i < n; i++ {
|
||||
c &= ~set_byte(0xff, i)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// pad appends a one followed by one or more zeroes to data
|
||||
@[inline]
|
||||
fn pad(n int) u64 {
|
||||
return u64(0x01) << (8 * n)
|
||||
}
|
||||
|
||||
// assume input.len < 8
|
||||
@[direct_array_access]
|
||||
fn u64_from_partial_bytes(input []u8) u64 {
|
||||
mut tmp := []u8{len: 8}
|
||||
ct_copy_first(mut tmp, input)
|
||||
return binary.little_endian_u64(tmp)
|
||||
}
|
||||
|
||||
// ct_copy_at copies of y into x start from at position in constant-time manner.
|
||||
@[direct_array_access]
|
||||
fn ct_copy_at(mut x []u8, y []u8, at int) {
|
||||
ct_copy_internal(1, mut x, y, at)
|
||||
}
|
||||
|
||||
// ct_copy_first copies of y into first y.len of x in constant-time manner.
|
||||
@[direct_array_access]
|
||||
fn ct_copy_first(mut x []u8, y []u8) {
|
||||
ct_copy_internal(1, mut x, y, 0)
|
||||
}
|
||||
|
||||
@[direct_array_access]
|
||||
fn ct_copy_internal(v int, mut x []u8, y []u8, at int) {
|
||||
if at > x.len {
|
||||
panic('at > x.len')
|
||||
}
|
||||
if i64(x.len) < i64(y.len) + i64(at) {
|
||||
panic('invalid pos')
|
||||
}
|
||||
if x.len < y.len {
|
||||
panic('length x < y')
|
||||
}
|
||||
xmask := u8(v - 1)
|
||||
ymask := u8(~(v - 1))
|
||||
for i := 0; i < y.len; i++ {
|
||||
x[i + at] = x[i + at] & xmask | y[i] & ymask
|
||||
}
|
||||
}
|
||||
|
||||
// portable little-endian helper
|
||||
@[inline]
|
||||
fn u64le(x u64) u64 {
|
||||
$if little_endian {
|
||||
return x
|
||||
}
|
||||
// otherwise, change into little-endian format
|
||||
return ((u64(0x00000000000000FF) & x) << 56) | ((u64(0x000000000000FF00) & x) << 40) | ((u64(0x0000000000FF0000) & x) << 24) | ((u64(0x00000000FF000000) & x) << 8) | ((u64(0x000000FF00000000) & x) >> 8) | ((u64(0x0000FF0000000000) & x) >> 24) | ((u64(0x00FF000000000000) & x) >> 40) | ((u64(0xFF00000000000000) & x) >> 56)
|
||||
}
|
||||
|
||||
@[inline]
|
||||
fn get_byte(x u64, i int) u8 {
|
||||
return u8(x >> (8 * i))
|
||||
}
|
||||
|
||||
@[inline]
|
||||
fn set_byte(b u8, i int) u64 {
|
||||
return u64(b) << (8 * i)
|
||||
}
|
||||
|
||||
@[direct_array_access]
|
||||
fn load_bytes(bytes []u8, n int) u64 {
|
||||
mut x := u64(0)
|
||||
for i := 0; i < n; i++ {
|
||||
x |= set_byte(bytes[i], i)
|
||||
}
|
||||
return u64le(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)
|
||||
}
|
||||
}
|
||||
|
||||
@[direct_array_access; inline]
|
||||
fn ascon_rotate_right(x u64, n int) u64 {
|
||||
return (x >> n) | x << (64 - n)
|
||||
}
|
332
vlib/x/crypto/ascon/xof.v
Normal file
332
vlib/x/crypto/ascon/xof.v
Normal file
|
@ -0,0 +1,332 @@
|
|||
// Copyright ©2025 blackshirt.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
//
|
||||
// This file implements Ascon-XOF128, an Ascon-based Extendable-output Function (XOF)
|
||||
// and their variant, Ascon-CXOF128 from NIST.SP.800-232 standard.
|
||||
// Ascon-XOF128 is similar to Ascon-Hash256 where Ascon-XOF128 accepts an additional input
|
||||
// which specifies the desired output length in bits.
|
||||
module ascon
|
||||
|
||||
import encoding.binary
|
||||
|
||||
// max_hash_size is a maximum size of Ascon-XOF128 (and Ascon-CXOF128) checksum output.
|
||||
// Its very rare where checksum output bigger than 512-bits, So, we limiting it to prevent unconditional thing.
|
||||
// This limitation was only occurs on this module, wee can change it later.
|
||||
const max_hash_size = 4096 // in bytes
|
||||
|
||||
// default_xof_size is the size of Ascon-XOF128 (and Ascon-CXOF128) checksum that has 512-bits length.
|
||||
const default_xof_size = 64
|
||||
|
||||
// xof128_initial_state is a precomputed value for Ascon-XOF128 state.
|
||||
// See the comment on `hash256_initial_state` about the values
|
||||
const xof128_initial_state = State{
|
||||
e0: 0xda82ce768d9447eb
|
||||
e1: 0xcc7ce6c75f1ef969
|
||||
e2: 0xe7508fd780085631
|
||||
e3: 0x0ee0ea53416b58cc
|
||||
e4: 0xe0547524db6f0bde
|
||||
}
|
||||
|
||||
// xof128 creates an Ascon-XOF128 checksum of msg with specified desired size of output.
|
||||
@[direct_array_access]
|
||||
pub fn xof128(msg []u8, size int) ![]u8 {
|
||||
mut x := new_xof128(size)
|
||||
_ := x.write(msg)!
|
||||
x.Digest.finish()
|
||||
mut out := []u8{len: size}
|
||||
n := x.Digest.squeeze(mut out)
|
||||
x.reset()
|
||||
return out[..n]
|
||||
}
|
||||
|
||||
// xof128_64 creates a 64-bytes of Ascon-XOF128 checksum of msg.
|
||||
@[direct_array_access]
|
||||
pub fn xof128_64(msg []u8) ![]u8 {
|
||||
return xof128(msg, default_xof_size)!
|
||||
}
|
||||
|
||||
// Xof128 is an opaque provides an implementation of Ascon-XOF128 from NIST.SP.800-232 standard.
|
||||
// Its implements `hash.Hash` interface with checksum size stored on instance creation.
|
||||
@[noinit]
|
||||
pub struct Xof128 {
|
||||
Digest
|
||||
mut:
|
||||
// The size of Xof128 cheksum, when you dont specify it
|
||||
size int = default_xof_size
|
||||
}
|
||||
|
||||
// new_xof128 creates a new Ascon-XOF128 instance with provided size parameter.
|
||||
@[direct_array_access]
|
||||
pub fn new_xof128(size int) &Xof128 {
|
||||
if size < 1 || size > max_hash_size {
|
||||
panic('new_xof128: invalid size')
|
||||
}
|
||||
return &Xof128{
|
||||
Digest: Digest{
|
||||
State: xof128_initial_state
|
||||
}
|
||||
size: size
|
||||
}
|
||||
}
|
||||
|
||||
// size returns an underlying size of Xof128 checksum in fixed-sized manner.
|
||||
// Internally, its return underlying size stored on current Xof128 instance.
|
||||
pub fn (x &Xof128) size() int {
|
||||
return x.size
|
||||
}
|
||||
|
||||
// block_size returns an underlying Xof128 block size operates on, ie, 8-bytes
|
||||
pub fn (x &Xof128) block_size() int {
|
||||
return block_size
|
||||
}
|
||||
|
||||
// clone returns a clone of x on the current state.
|
||||
fn (x &Xof128) clone() &Xof128 {
|
||||
return &Xof128{
|
||||
Digest: x.Digest
|
||||
size: x.size
|
||||
}
|
||||
}
|
||||
|
||||
// write writes out the content of message and updates internal Xof128 state.
|
||||
@[direct_array_access]
|
||||
pub fn (mut x Xof128) write(msg []u8) !int {
|
||||
if x.Digest.done {
|
||||
panic('Digest: writing after done ')
|
||||
}
|
||||
return x.Digest.absorb(msg)
|
||||
}
|
||||
|
||||
// sum returns an Ascon-XOF128 checksum of the bytes in msg.
|
||||
// Its produces `x.size` of checksum bytes. If you want to more
|
||||
// extendible output, use `.read` call instead.
|
||||
@[direct_array_access]
|
||||
pub fn (mut x Xof128) sum(msg []u8) []u8 {
|
||||
// working on the clone of the h, so we can keep writing
|
||||
mut x0 := x.clone()
|
||||
_ := x0.write(msg) or { panic(err) }
|
||||
x0.Digest.finish()
|
||||
mut dst := []u8{len: x.size}
|
||||
x0.Digest.squeeze(mut dst)
|
||||
x0.reset()
|
||||
return dst
|
||||
}
|
||||
|
||||
// read tries to read `dst.len` bytes of hash output from current Xof128 state and stored into dst.
|
||||
// Note: 1 ≤ dst.len ≤ max_hash_size.
|
||||
@[direct_array_access]
|
||||
pub fn (mut x Xof128) read(mut dst []u8) !int {
|
||||
if dst.len < 1 || dst.len > max_hash_size {
|
||||
panic('Xof128.read: invalid size')
|
||||
}
|
||||
mut x0 := x.clone()
|
||||
x0.Digest.finish()
|
||||
n := x0.Digest.squeeze(mut dst)
|
||||
x0.reset()
|
||||
return n
|
||||
}
|
||||
|
||||
// reset resets internal Xof128 state into default initialized state.
|
||||
@[direct_array_access]
|
||||
pub fn (mut x Xof128) reset() {
|
||||
// we dont reset the x.size
|
||||
unsafe { x.Digest.buf.reset() }
|
||||
x.Digest.length = 0
|
||||
x.Digest.done = false
|
||||
x.Digest.State = xof128_initial_state
|
||||
}
|
||||
|
||||
// free releases out the resources taken by the `x`. Dont use x after .free call.
|
||||
@[unsafe]
|
||||
pub fn (mut x Xof128) free() {
|
||||
$if prealloc {
|
||||
return
|
||||
}
|
||||
unsafe {
|
||||
x.Digest.buf.free()
|
||||
}
|
||||
// Mark it as unusable
|
||||
x.Digest.done = true
|
||||
}
|
||||
|
||||
// Ascon-CXOF128
|
||||
//
|
||||
// Ascon-CXOF128 is customized variant of Ascon-XOF128 that extends its
|
||||
// functionality by incorporating a user-defined customization string.
|
||||
// In addition to the message 𝑀 and output length 𝐿, Ascon-CXOF128 takes
|
||||
// the customization string 𝑍 as input.
|
||||
|
||||
// The length of the customization string shall be at most 2048 bits (i.e., 256 bytes)
|
||||
const max_cxof128_cstring = 256
|
||||
|
||||
// cxof128_initial_state is a precomputed value for Ascon-CXOF128 state.
|
||||
// See the comment on `hash256_initial_state` about the values
|
||||
const cxof128_initial_state = State{
|
||||
e0: 0x675527c2a0e8de03
|
||||
e1: 0x43d12d7dc0377bbc
|
||||
e2: 0xe9901dec426e81b5
|
||||
e3: 0x2ab14907720780b6
|
||||
e4: 0x8f3f1d02d432bc46
|
||||
}
|
||||
|
||||
// cxof128 creates an Ascon-CXOF128 checksum of msg with supplied size and custom string cs.
|
||||
@[direct_array_access]
|
||||
pub fn cxof128(msg []u8, size int, cs []u8) ![]u8 {
|
||||
mut cx := new_cxof128(size, cs)!
|
||||
_ := cx.write(msg)!
|
||||
cx.Digest.finish()
|
||||
mut out := []u8{len: size}
|
||||
n := cx.Digest.squeeze(mut out)
|
||||
cx.reset()
|
||||
return out[..n]
|
||||
}
|
||||
|
||||
// cxof128_64 creates a 64-bytes of Ascon-CXOF128 checksum of msg with supplied custom string in cs.
|
||||
@[direct_array_access]
|
||||
pub fn cxof128_64(msg []u8, cs []u8) ![]u8 {
|
||||
return cxof128(msg, default_xof_size, cs)!
|
||||
}
|
||||
|
||||
// CXof128 is an opaque provides an implementation of Ascon-CXOF128 from NIST.SP.800-232 standard.
|
||||
// Its implements `hash.Hash` interface with checksum-size stored on instance creation.
|
||||
@[noinit]
|
||||
pub struct CXof128 {
|
||||
Digest
|
||||
mut:
|
||||
// Customization string
|
||||
cs []u8
|
||||
// checksum size, for fixed-output
|
||||
size int = default_xof_size
|
||||
}
|
||||
|
||||
// new_cxof128 creates a new Ascon-CXOF128 instanace with cheksum size
|
||||
// was set to size and custom string in cs. It returns error on fails.
|
||||
@[direct_array_access]
|
||||
pub fn new_cxof128(size int, cs []u8) !&CXof128 {
|
||||
if cs.len > max_cxof128_cstring {
|
||||
return error('CXof128: custom string length exceed limit')
|
||||
}
|
||||
if size < 1 || size > max_hash_size {
|
||||
return error('CXof128: invalid size')
|
||||
}
|
||||
// Initialize CXof128 state with precomputed-value and absorb the customization string
|
||||
mut s := cxof128_initial_state
|
||||
cxof128_absorb_custom_string(mut s, cs)
|
||||
|
||||
return &CXof128{
|
||||
Digest: Digest{
|
||||
State: s
|
||||
}
|
||||
cs: cs
|
||||
size: size
|
||||
}
|
||||
}
|
||||
|
||||
// size returns an underlying size of CXof128 checksum in fixed-sized manner.
|
||||
// Internally, its return underlying size stored on current CXof128 instance.
|
||||
pub fn (x &CXof128) size() int {
|
||||
return x.size
|
||||
}
|
||||
|
||||
// block_size returns an underlying CXof128 block size operates on, ie, 8-bytes
|
||||
pub fn (x &CXof128) block_size() int {
|
||||
return block_size
|
||||
}
|
||||
|
||||
// write writes out the content of message and updates internal CXof128 state.
|
||||
@[direct_array_access]
|
||||
pub fn (mut x CXof128) write(msg []u8) !int {
|
||||
if x.Digest.done {
|
||||
panic('CXof128: writing after done ')
|
||||
}
|
||||
return x.Digest.absorb(msg)
|
||||
}
|
||||
|
||||
// sum returns an Ascon-CXOF128 checksum of the bytes in msg.
|
||||
// Its produces `x.size` of checksum bytes. If you want to more
|
||||
// extendible output, use `.read` call instead.
|
||||
@[direct_array_access]
|
||||
pub fn (mut x CXof128) sum(msg []u8) []u8 {
|
||||
// working on the clone of the h, so we can keep writing
|
||||
mut x0 := x.clone()
|
||||
_ := x0.write(msg) or { panic(err) }
|
||||
x0.Digest.finish()
|
||||
mut dst := []u8{len: x.size}
|
||||
x0.Digest.squeeze(mut dst)
|
||||
x0.reset()
|
||||
return dst
|
||||
}
|
||||
|
||||
// read tries to read `dst.len` bytes of hash output from current CXof128 state and stored into dst.
|
||||
@[direct_array_access]
|
||||
pub fn (mut x CXof128) read(mut dst []u8) !int {
|
||||
if dst.len < 1 || dst.len > max_hash_size {
|
||||
panic('CXof128.read: invalid size')
|
||||
}
|
||||
mut x0 := x.clone()
|
||||
x0.Digest.finish()
|
||||
n := x0.Digest.squeeze(mut dst)
|
||||
x0.reset()
|
||||
return n
|
||||
}
|
||||
|
||||
// clone returns a clone of x on the current state.
|
||||
fn (x &CXof128) clone() &CXof128 {
|
||||
return &CXof128{
|
||||
Digest: x.Digest
|
||||
size: x.size
|
||||
cs: x.cs
|
||||
}
|
||||
}
|
||||
|
||||
// reset resets internal CXof128 state into default initialized state.
|
||||
@[direct_array_access]
|
||||
pub fn (mut x CXof128) reset() {
|
||||
// we dont reset the x.size and custom string
|
||||
unsafe { x.Digest.buf.reset() }
|
||||
x.Digest.length = 0
|
||||
x.Digest.done = false
|
||||
x.Digest.State = cxof128_initial_state
|
||||
// reabsorbs custom string
|
||||
cxof128_absorb_custom_string(mut x.Digest.State, x.cs)
|
||||
}
|
||||
|
||||
// free releases out the resources taken by the `x`. Dont use x after .free call.
|
||||
@[unsafe]
|
||||
pub fn (mut x CXof128) free() {
|
||||
$if prealloc {
|
||||
return
|
||||
}
|
||||
unsafe {
|
||||
x.Digest.buf.free()
|
||||
}
|
||||
// Mark it as unusable
|
||||
x.Digest.done = true
|
||||
}
|
||||
|
||||
// cxof128_absorb_custom_string performs absorbing phase of custom string in cs for Ascon-CXOF128.
|
||||
@[direct_array_access; inline]
|
||||
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, 12)
|
||||
|
||||
// absorb the customization string
|
||||
mut zlen := cs.len
|
||||
mut zidx := 0
|
||||
for zlen >= block_size {
|
||||
block := unsafe { cs[zidx..zidx + block_size] }
|
||||
s.e0 ^= binary.little_endian_u64(block)
|
||||
ascon_pnr(mut s, 12)
|
||||
|
||||
// updates a index
|
||||
zlen -= block_size
|
||||
zidx += block_size
|
||||
}
|
||||
// absorb final customization string
|
||||
last_block := unsafe { cs[zidx..] }
|
||||
s.e0 ^= load_bytes(last_block, last_block.len)
|
||||
s.e0 ^= pad(last_block.len)
|
||||
ascon_pnr(mut s, 12)
|
||||
}
|
113
vlib/x/crypto/ascon/xof_test.v
Normal file
113
vlib/x/crypto/ascon/xof_test.v
Normal file
|
@ -0,0 +1,113 @@
|
|||
// Copyright ©2025 blackshirt.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
//
|
||||
module ascon
|
||||
|
||||
import arrays
|
||||
import encoding.hex
|
||||
|
||||
// This test for Xof128 with 512-bits outputs
|
||||
struct XofTest {
|
||||
count int
|
||||
msg string
|
||||
md string
|
||||
}
|
||||
|
||||
fn test_xof128() ! {
|
||||
for item in xof128_test_data {
|
||||
msg := hex.decode(item.msg)!
|
||||
md := hex.decode(item.md)!
|
||||
out := xof128_64(msg)!
|
||||
assert out == md
|
||||
|
||||
// With Xof128Digest opaque
|
||||
mut x := new_xof128(default_xof_size)
|
||||
md0 := x.sum(msg)
|
||||
assert md0 == md
|
||||
|
||||
// test .read()
|
||||
x.reset()
|
||||
mut dst := []u8{len: 64}
|
||||
_ := x.write(msg)!
|
||||
nr := x.read(mut dst)!
|
||||
assert nr == 64
|
||||
assert dst == md
|
||||
|
||||
// with chunked messages
|
||||
x.reset()
|
||||
chunks := arrays.chunk[u8](msg, 20)
|
||||
mut tot := 0
|
||||
for chunk in chunks {
|
||||
n := x.write(chunk)!
|
||||
tot += n
|
||||
}
|
||||
assert msg.len == tot
|
||||
|
||||
chunked_md := x.sum([]u8{})
|
||||
assert chunked_md == md
|
||||
}
|
||||
}
|
||||
|
||||
const xof128_test_data = [
|
||||
XofTest{
|
||||
count: 1
|
||||
msg: ''
|
||||
md: '473d5e6164f58b39dfd84aacdb8ae42ec2d91fed33388ee0d960d9b3993295c6ad77855a5d3b13fe6ad9e6098988373af7d0956d05a8f1665d2c67d1a3ad10ff'
|
||||
},
|
||||
XofTest{
|
||||
count: 2
|
||||
msg: '00'
|
||||
md: '51430e0438ecdf642b393630d977625f5f337656ba58ab1e960784ac32a16e0d446405551f5469384f8ea283cf12e64fa72c426bfebaea3aa1529e2c4ab23a2f'
|
||||
},
|
||||
XofTest{
|
||||
count: 3
|
||||
msg: '0001'
|
||||
md: 'a05383077af971d3830bd37e7b981497a773d441db077c6494cc73125953846eb6427fba4cd308ff90a11385d51101341bf5379249217bfdace9cca1148cc966'
|
||||
},
|
||||
XofTest{
|
||||
count: 4
|
||||
msg: '000102'
|
||||
md: '9c96f31c3e7bdfdc5ef6ba836f760a0d6548d94dd0a512033022c9242e8ba916c30c3961d37d7dd7282e2191494d60dc5058588b276c60c90be2aaa7e7013d96'
|
||||
},
|
||||
XofTest{
|
||||
count: 5
|
||||
msg: '00010203'
|
||||
md: '21f7fd74588e244af45f9016b8db19b857ec5e6208978cfc1b4611ed91fb38f87e8f82a6409fb2b77acfbba8862aa22a7b0c98c1c01d5a4fdf64827b450fa1eb'
|
||||
},
|
||||
XofTest{
|
||||
count: 6
|
||||
msg: '0001020304'
|
||||
md: 'd647cc91aafff06a486f00a33fdfe9222f08b94da3b17804da9aaae167b4285dd6395e2a61fded3cf73c99774aff7066f74f7698f4824ba538602087d7c267fa'
|
||||
},
|
||||
XofTest{
|
||||
count: 7
|
||||
msg: '000102030405'
|
||||
md: '4793fbe6aa7688e52cd3a97a2685c68b218e0ca8754307956509974ab107d8ba19070424d5dfd336c3fc1250a273b9146f9f26d7658b9e213c37aebbe74abc6e'
|
||||
},
|
||||
XofTest{
|
||||
count: 8
|
||||
msg: '00010203040506'
|
||||
md: '7ae562db37212a9acd2673ecfd5b4f1c5cb2e6f64ebf00aa7f6ef8dc82c448d5fe11cd91f4368c37690d79e5de0ca8ad419e1918ce8dab2d42363e9476638a7b'
|
||||
},
|
||||
XofTest{
|
||||
count: 9
|
||||
msg: '0001020304050607'
|
||||
md: '8d1886f5d3ec4af8d15b44bc62b74da6ea91bc28fb82f9c34079b5ed6e38b6c951803d7dfb3c5e512a0ef5e4060062a6fd067f9c73ef9bee527411bda67fc896'
|
||||
},
|
||||
XofTest{
|
||||
count: 10
|
||||
msg: '000102030405060708'
|
||||
md: 'db3013bfbbd132dc1d3152fd955ed48f7cbb675e9ad2a2fecf92b74c957592e0c89959e81c16fd07ead9eeb8e40359c497aa20258b43d87ec69ad0bb0993fd38'
|
||||
},
|
||||
XofTest{
|
||||
count: 601
|
||||
msg: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f5051525354555657'
|
||||
md: '8fbbce48a72faf257e06df9408d4b6e11d35a86dfc29c0f106cb86128a2fe94208dcc8df4a439978cdb77f53317cbe5507b7f33da590be10a424d4ef60b37036'
|
||||
},
|
||||
XofTest{
|
||||
count: 602
|
||||
msg: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758'
|
||||
md: 'b4ca30047dc94ed770e45f4fa07b25a049d4a149a78227e25625cec2cbee7cfdd7d3cfb2635a38770e04009de348726364f648984ef47e9bc90c2a3c0526b227'
|
||||
},
|
||||
]
|
Loading…
Add table
Add a link
Reference in a new issue