Compare commits

...

5 commits

Author SHA1 Message Date
Delyan Angelov
c2e2aac0c9
tests: use os.vtmp_dir() in autofree_toml.vv, so it can be cleaned up automatically; simplify the code
Some checks are pending
Graphics CI / gg-regressions (push) Waiting to run
vlib modules CI / build-module-docs (push) Waiting to run
native backend CI / native-backend-ubuntu (push) Waiting to run
native backend CI / native-backend-windows (push) Waiting to run
Sanitized CI / sanitize-undefined-clang (push) Waiting to run
Sanitized CI / sanitize-undefined-gcc (push) Waiting to run
Sanitized CI / tests-sanitize-address-clang (push) Waiting to run
Sanitized CI / sanitize-address-msvc (push) Waiting to run
Sanitized CI / sanitize-address-gcc (push) Waiting to run
Sanitized CI / sanitize-memory-clang (push) Waiting to run
sdl CI / v-compiles-sdl-examples (push) Waiting to run
Time CI / time-linux (push) Waiting to run
Time CI / time-macos (push) Waiting to run
Time CI / time-windows (push) Waiting to run
toml CI / toml-module-pass-external-test-suites (push) Waiting to run
Tools CI / tools-linux (clang) (push) Waiting to run
Tools CI / tools-linux (gcc) (push) Waiting to run
Tools CI / tools-linux (tcc) (push) Waiting to run
Tools CI / tools-macos (clang) (push) Waiting to run
Tools CI / tools-windows (gcc) (push) Waiting to run
Tools CI / tools-windows (msvc) (push) Waiting to run
Tools CI / tools-windows (tcc) (push) Waiting to run
Tools CI / tools-docker-ubuntu-musl (push) Waiting to run
vab CI / vab-compiles-v-examples (push) Waiting to run
vab CI / v-compiles-os-android (push) Waiting to run
wasm backend CI / wasm-backend (ubuntu-22.04) (push) Waiting to run
wasm backend CI / wasm-backend (windows-2022) (push) Waiting to run
2025-09-11 13:08:51 +03:00
Delyan Angelov
97145ca3a8
v.builder: support -d trace_type_symbols_after_checker 2025-09-11 12:47:58 +03:00
Eliyaan (Nopana)
b653d65676
native: structs multi_assign and multi_return (#25281) 2025-09-11 11:36:14 +03:00
blackshirt
a10c59704b
x.crypto.ascon: improve single-shot functions of ascon hashing variant; add benchmark (#25282) 2025-09-11 11:04:21 +03:00
blackshirt
f16452d3a6
x.crypto.ascon: improve the core of Ascon permutation routine (#25278) 2025-09-11 05:56:17 +03:00
17 changed files with 538 additions and 147 deletions

View file

@ -91,7 +91,7 @@ fn get_all_commands() []Command {
rmfile: 'examples/hello_world' rmfile: 'examples/hello_world'
} }
res << Command{ res << Command{
line: '${vexe} -W -Wimpure-v run examples/hello_world.v' line: '${vexe} -W -Wimpure-v examples/hello_world.v'
okmsg: 'V can compile hello world with the stricter `-W -Wimpure-v` mode .' okmsg: 'V can compile hello world with the stricter `-W -Wimpure-v` mode .'
rmfile: 'examples/hello_world' rmfile: 'examples/hello_world'
} }

View file

@ -131,6 +131,11 @@ pub fn (mut b Builder) middle_stages() ! {
b.checker.check_files(b.parsed_files) b.checker.check_files(b.parsed_files)
util.timing_measure('CHECK') util.timing_measure('CHECK')
$if trace_type_symbols_after_checker ? {
for t, s in b.table.type_symbols {
println('> t: ${t:10} | s.mod: ${s.mod:-40} | s.name: ${'${s.name#[..30]}':-30} | s.is_builtin: ${s.is_builtin:6} | s.is_pub: ${s.is_pub}')
}
}
if b.pref.dump_defines != '' { if b.pref.dump_defines != '' {
b.dump_defines() b.dump_defines()
} }

View file

@ -2,14 +2,8 @@
import toml import toml
import os import os
fn main() { config_fname := os.join_path(os.vtmp_dir(), 'config.toml')
config_fname := 'config.toml' tab_title := 'test tab title'
tab_title := 'test tab title' os.write_file(config_fname, 'tab_title = "${tab_title}"')!
if !os.exists(config_fname) { doc := toml.parse_file(config_fname)!
mut f := os.create(config_fname) or { panic(err) } assert doc.value('tab_title').string() == tab_title
f.writeln('tab_title = "${tab_title}"') or { panic(err) }
f.close()
}
doc := toml.parse_file(config_fname) or { panic(err) }
assert doc.value('tab_title').string() == tab_title
}

View file

@ -953,7 +953,7 @@ fn (mut c Amd64) lea_var_to_reg(r Register, var_offset i32) {
is_far_var := var_offset > 0x80 || var_offset < -0x7f is_far_var := var_offset > 0x80 || var_offset < -0x7f
match reg { match reg {
.rax, .rbx, .rsi, .rdi { .rax, .rbx, .rsi, .rdi, .rdx, .rcx {
c.g.write8(0x48) c.g.write8(0x48)
} }
else {} else {}
@ -2753,6 +2753,7 @@ fn (mut c Amd64) gen_type_promotion(from ast.Type, to ast.Type, option Amd64Regi
} }
fn (mut c Amd64) return_stmt(node ast.Return) { fn (mut c Amd64) return_stmt(node ast.Return) {
c.g.println('; return statement {')
mut s := '?' //${node.exprs[0].val.str()}' mut s := '?' //${node.exprs[0].val.str()}'
if node.exprs.len == 1 { if node.exprs.len == 1 {
match node.exprs[0] { match node.exprs[0] {
@ -2812,6 +2813,8 @@ fn (mut c Amd64) return_stmt(node ast.Return) {
c.add(Amd64Register.rax, size % 8) c.add(Amd64Register.rax, size % 8)
c.add(Amd64Register.rdx, size % 8) c.add(Amd64Register.rdx, size % 8)
c.mov_deref(Amd64Register.rcx, Amd64Register.rax, ast.i64_type_idx) c.mov_deref(Amd64Register.rcx, Amd64Register.rax, ast.i64_type_idx)
// TODO: check if it does not write too far as the size of
// the remaining data is not 64bits
c.mov_store(.rdx, .rcx, ._64) c.mov_store(.rdx, .rcx, ._64)
} }
c.mov_var_to_reg(c.main_reg(), LocalVar{ c.mov_var_to_reg(c.main_reg(), LocalVar{
@ -2841,7 +2844,14 @@ fn (mut c Amd64) return_stmt(node ast.Return) {
offset := c.g.structs[typ.idx()].offsets[i] offset := c.g.structs[typ.idx()].offsets[i]
c.g.expr(expr) c.g.expr(expr)
// TODO: expr not on rax // TODO: expr not on rax
c.mov_reg_to_var(var, Amd64Register.rax, offset: offset, typ: ts.mr_info().types[i]) e_typ := ts.mr_info().types[i]
e_ts := c.g.table.sym(e_typ)
if e_ts.info is ast.Struct {
c.lea_var_to_reg(Amd64Register.rdx, var.offset - offset)
c.move_struct(.rdx, .rax, c.g.get_type_size(e_typ))
} else {
c.mov_reg_to_var(var, Amd64Register.rax, offset: offset, typ: ts.mr_info().types[i])
}
} }
// store the multi return struct value // store the multi return struct value
c.lea_var_to_reg(Amd64Register.rax, var.offset) c.lea_var_to_reg(Amd64Register.rax, var.offset)
@ -2897,6 +2907,7 @@ fn (mut c Amd64) return_stmt(node ast.Return) {
pos: pos pos: pos
} }
c.g.println('; jump to label ${label}') c.g.println('; jump to label ${label}')
c.g.println('; return statement }')
} }
fn (mut c Amd64) multi_assign_stmt(node ast.AssignStmt) { fn (mut c Amd64) multi_assign_stmt(node ast.AssignStmt) {
@ -2926,7 +2937,7 @@ fn (mut c Amd64) multi_assign_stmt(node ast.AssignStmt) {
} else { } else {
c.g.expr(node.right[0]) c.g.expr(node.right[0])
} }
c.mov_reg(Amd64Register.rdx, Amd64Register.rax) c.mov_reg(Amd64Register.rdx, Amd64Register.rax) // value of right expr(s)
mut current_offset := i32(0) mut current_offset := i32(0)
for i, offset in multi_return.offsets { for i, offset in multi_return.offsets {
@ -2943,7 +2954,7 @@ fn (mut c Amd64) multi_assign_stmt(node ast.AssignStmt) {
c.add(Amd64Register.rdx, offset - current_offset) c.add(Amd64Register.rdx, offset - current_offset)
current_offset = offset current_offset = offset
} }
c.g.gen_left_value(node.left[i]) c.g.gen_left_value(node.left[i]) // in rax
left_type := node.left_types[i] left_type := node.left_types[i]
right_type := node.right_types[i] right_type := node.right_types[i]
if c.g.is_register_type(right_type) { if c.g.is_register_type(right_type) {
@ -3001,11 +3012,36 @@ fn (mut c Amd64) multi_assign_stmt(node ast.AssignStmt) {
c.g.println('movsd [rax], xmm0') c.g.println('movsd [rax], xmm0')
} }
} else { } else {
c.g.n_error('${@LOCATION} multi return for struct is not supported yet') c.move_struct(.rax, .rdx, c.g.get_type_size(left_type))
} }
} }
} }
// Moves a struct of size `_size` (in bytes) from the address stored in input to the address stored in output
fn (mut c Amd64) move_struct(output Amd64Register, input Amd64Register, _size i32) {
mut size := _size
for size != 0 {
c.mov_deref(Amd64Register.rcx, input, ast.i64_type_idx)
// mov_store can only move powers of 2 bytes at once
// the remainder will then get handled the next iteration for simplicity
data_size := i32(match true {
size < 2 { 1 }
size < 4 { 2 }
size < 8 { 4 }
else { 8 }
})
c.mov_store(output, .rcx, match data_size {
1 { ._8 }
2 { ._16 }
4 { ._32 }
else { ._64 }
})
size -= data_size
c.add(output, data_size)
c.add(input, data_size)
}
}
fn (mut c Amd64) assign_stmt(node ast.AssignStmt) { fn (mut c Amd64) assign_stmt(node ast.AssignStmt) {
// `a, b := foo()` // `a, b := foo()`
// `a, b := if cond { 1, 2 } else { 3, 4 }` // `a, b := if cond { 1, 2 } else { 3, 4 }`

View file

@ -61,7 +61,44 @@ fn cross_assign_of_struct_test() { // from cross_assign_test.v
assert x.b == 1 assert x.b == 1
} }
struct MyStruct {
a int
b u64
c u16
d u8
}
struct MyStruct2 {
a u8
b u8
c u8
}
fn struct_multi_return() (int, MyStruct) {
return 3, MyStruct{4, 5, 6, 7}
}
fn struct_multi_return2() (int, MyStruct2) {
return 3, MyStruct2{4, 5, 6}
}
fn struct_multi_return_test() {
a, b := struct_multi_return()
assert a == 3
assert b.a == 4
assert b.b == 5
assert b.c == 6
assert b.d == 7
c, d := struct_multi_return2()
assert c == 3
assert d.a == 4
assert d.b == 5
assert d.c == 6
}
fn main() { fn main() {
fn_multi_return_test() fn_multi_return_test()
cross_assign_of_struct_test() cross_assign_of_struct_test()
struct_multi_return_test()
} }

View file

@ -1,15 +1,17 @@
# ascon # ascon
`ascon` is a implementation of Ascon-Based Cryptography module implemented in pure V language. `ascon` is an 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. 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 Its describes an Ascon-Based Lightweight Cryptography Standards for Constrained Devices
thats provides Authenticated Encryption, Hash, and Extendable Output Functions. 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. 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. This module mostly implements all the features availables on the document.
Its currently implements: It currently implements:
- `Ascon-Hash256`, Ascon-based hashing implementation that produces 256-bits output. - `Ascon-Hash256`, Ascon-based hashing implementation that produces 256-bits output.
- `Ascon-XOF128`, Ascon-based eXtendible Output Function (XOF) where the output size of - `Ascon-XOF128`, Ascon-based eXtendable Output Function (XOF) where the output size of
the hash of the message can be selected by the user. the hash of the message can be selected by the user.
- `Ascon-CXOF128`, a customized XOF that allows users to specify a customization - `Ascon-CXOF128`, a customized XOF that allows users to specify a customization
string and choose the output size of the message hash. string and choose the output size of the message hash.
- `Ascon-AEAD128`, an Authenticated Encryption with Additional Data (AEAD) Scheme based
on Ascon-family crypto.

View file

@ -181,7 +181,7 @@ pub fn (mut c Aead128) encrypt(msg []u8, nonce []u8, ad []u8) ![]u8 {
c.State.e4 = n1 c.State.e4 = n1
// Update state by permutation // Update state by permutation
ascon_pnr(mut c.State, 12) ascon_pnr(mut c.State, ascon_prnd_12)
// XOR-ing with the cipher's key // XOR-ing with the cipher's key
c.State.e3 ^= c.key[0] c.State.e3 ^= c.key[0]
c.State.e4 ^= c.key[1] 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 c.State.e4 = n1
// scrambled with permutation routine // scrambled with permutation routine
ascon_pnr(mut c.State, 12) ascon_pnr(mut c.State, ascon_prnd_12)
// xor-ing with the cipher's key // xor-ing with the cipher's key
c.State.e3 ^= c.key[0] c.State.e3 ^= c.key[0]
c.State.e4 ^= c.key[1] 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 s.e4 = n1
// updates State using the permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[12], S ← 𝐴𝑠𝑐𝑜𝑛-𝑝[12](S) // updates State using the permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[12], S ← 𝐴𝑠𝑐𝑜𝑛-𝑝[12](S)
ascon_pnr(mut s, 12) ascon_pnr(mut s, ascon_prnd_12)
// Then XORing the secret key 𝐾 into the last 128 bits of internal state: // Then XORing the secret key 𝐾 into the last 128 bits of internal state:
// S ← S ⊕ (0¹⁹² ∥ 𝐾). // 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]) s.e1 ^= binary.little_endian_u64(block[8..16])
// Apply permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[8] to the state // Apply permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[8] to the state
ascon_pnr(mut s, 8) ascon_pnr(mut s, ascon_prnd_8)
// Updates index // Updates index
ad_length -= aead128_block_size ad_length -= aead128_block_size
ad_idx += 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 // Apply permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[8] to the state
ascon_pnr(mut s, 8) ascon_pnr(mut s, ascon_prnd_8)
} }
// The final step of processing associated data is to update the state // The final step of processing associated data is to update the state
// with a constant that provides domain separation. // 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..pos + 8], s.e0)
binary.little_endian_put_u64(mut out[pos + 8..], s.e1) binary.little_endian_put_u64(mut out[pos + 8..], s.e1)
// apply permutation // apply permutation
ascon_pnr(mut s, 8) ascon_pnr(mut s, ascon_prnd_8)
// updates index // updates index
mlen -= aead128_block_size mlen -= aead128_block_size
@ -413,7 +413,7 @@ fn aead128_partial_dec(mut out []u8, mut s State, cmsg []u8) {
s.e0 = c0 s.e0 = c0
s.e1 = c1 s.e1 = c1
ascon_pnr(mut s, 8) ascon_pnr(mut s, ascon_prnd_8)
// updates index // updates index
pos += aead128_block_size pos += aead128_block_size
cmsg_len -= 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.e2 ^= k0
s.e3 ^= k1 s.e3 ^= k1
// then updated using the permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[12] // then updated using the permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[12]
ascon_pnr(mut s, 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: // Finally, the tag 𝑇 is generated by XORing the key with the last 128 bits of the state:
// 𝑇𝑆[192319] ⊕ 𝐾. // 𝑇𝑆[192319] ⊕ 𝐾.

View file

@ -2,8 +2,9 @@
// Use of this source code is governed by an MIT license // Use of this source code is governed by an MIT license
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
// //
module ascon
import encoding.hex import encoding.hex
import x.crypto.ascon
// This test materials was taken and adapted into v from references implementation of Ascon-aead128 // This test materials was taken and adapted into v from references implementation of Ascon-aead128
// especially for the known answer test data, but, its not all fully-taken, just randomly choosen item. // especially for the known answer test data, but, its not all fully-taken, just randomly choosen item.
@ -26,14 +27,14 @@ fn test_ascon_aead128_enc_dec() ! {
ad := hex.decode(item.ad)! ad := hex.decode(item.ad)!
ct := hex.decode(item.ct)! ct := hex.decode(item.ct)!
out := ascon.encrypt(key, nonce, ad, pt)! out := encrypt(key, nonce, ad, pt)!
assert out == ct assert out == ct
msg := ascon.decrypt(key, nonce, ad, ct)! msg := decrypt(key, nonce, ad, ct)!
assert msg == pt assert msg == pt
// Work with object-based Cipher // Work with object-based Cipher
mut c := ascon.new_aead128(key)! mut c := new_aead128(key)!
// Lets encrypt the message // Lets encrypt the message
exp_ct := c.encrypt(msg, nonce, ad)! exp_ct := c.encrypt(msg, nonce, ad)!
assert exp_ct == ct assert exp_ct == ct

View file

@ -10,6 +10,10 @@ module ascon
// constants for up to 16 rounds to accommodate potential functionality extensions in the future. // constants for up to 16 rounds to accommodate potential functionality extensions in the future.
const max_nr_perm = 16 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
// The constants to derive round constants of the Ascon permutations // The constants to derive round constants of the Ascon permutations
// See Table 5. of NIST SP 800-232 docs // See Table 5. of NIST SP 800-232 docs
// //
@ -26,72 +30,74 @@ const max_nr_perm = 16
const rnc = [u8(0x3c), 0x2d, 0x1e, 0x0f, 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, const rnc = [u8(0x3c), 0x2d, 0x1e, 0x0f, 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78,
0x69, 0x5a, 0x4b] 0x69, 0x5a, 0x4b]
// ascon_pnr is ascon permutation routine with specified numbers of round nr, where 1 ≤ nr ≤ 16 // ascon_pnr is the core of Ascon family permutation routine with specified numbers of round nr, where 1 ≤ nr ≤ 16
// 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 (Sec 3.4)
@[direct_array_access] @[direct_array_access]
fn ascon_pnr(mut s State, nr int) { fn ascon_pnr(mut s State, nr int) {
// We dont allow nr == 0 // We dont allow nr == 0
if nr < 1 || nr > 16 { if nr < 1 || nr > 16 {
panic('Invalid round number') panic('Invalid round number')
} }
// Ascon permutation routine
for i := max_nr_perm - nr; i < max_nr_perm; i++ { for i := max_nr_perm - nr; i < max_nr_perm; i++ {
ascon_perm(mut s, rnc[i]) // 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 ^= rnc[i]
// 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)
//
// This diffusion layer, especially on the bits right rotation part is a most widely called
// for Ascon permutation routine. So, even bits rotation almost efficient on most platform,
// to reduce overhead on function call, we work on the raw bits right rotation here.
// 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)))
} }
} }
// 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
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 ^= 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, // State is structure represents Ascon state. Its operates on the 320-bit opaque,
// which is represented as five of 64-bit words. // which is represented as five of 64-bit words.
@[noinit] @[noinit]

View file

@ -5,23 +5,6 @@
module ascon module ascon
// This test mostly taken from https://docs.rs/ascon/latest/src/ascon/lib.rs.html // 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() { fn test_ascon_round_p6() {
mut s := State{ mut s := State{
e0: u64(0x0123456789abcdef) e0: u64(0x0123456789abcdef)

View file

@ -0,0 +1,75 @@
// This benchmark is for Ascon-AEAD128 in `x.crypto.ascon` compared to
// already stocked `x.crypto.cacha20poly1305 for AEAD functionalities.
//
// Here is output in my tests, first item was `x.crypto.ascon` and the later
// for `x.crypto.chacha20poly1305` on encryption or decryption part.
//
// Encryption..
// -----------
// Iterations: 10000 Total Duration: 26.008ms ns/op: 2600 B/op: 16 allocs/op: 17
// Iterations: 10000 Total Duration: 158.865ms ns/op: 15886 B/op: 16 allocs/op: 16
//
// Decryption..
// -----------
// Iterations: 10000 Total Duration: 29.091ms ns/op: 2909 B/op: 6 allocs/op: 8
// Iterations: 10000 Total Duration: 158.373ms ns/op: 15837 B/op: 8 allocs/op: 12
//
import encoding.hex
import x.benchmark
import x.crypto.ascon
import x.crypto.chacha20poly1305
// randomly generated key and nonce, 16-bytes of ascon key and 32-bytes of chacha20poly1305 key.
const key_ascon = hex.decode('7857bfb462c654d1d1b02971be021235')!
const key_cpoly = hex.decode('9d9603f4fc460e273b80795ea50eab5873c04f589226c7d591b5336feb32fcba')!
// 16-bytes ascon-nonce
const ascon_nonce = hex.decode('8b521028fb54591472d8d8ee14430835')!
// 12-bytes chacha20poly1305 nonce
const cpoly_nonce = hex.decode('9a3c83e4236ea9a2c4e482da')!
const ad = 'Ascon-AEAD128 additional data'.bytes()
const msg = 'Ascon-AEAD128 benchmarking message'.bytes()
// expected ciphertext for aead128 := 4b21a18cbca65b11aaf73dc74241c89bfcec96a4c8973ae696a938e0a591e846c4eb7b2906664f2318c0fd6ec1c56424aa9b
const ciphertext_aead128 = hex.decode('4b21a18cbca65b11aaf73dc74241c89bfcec96a4c8973ae696a938e0a591e846c4eb7b2906664f2318c0fd6ec1c56424aa9b')!
fn bench_ascon_aead128_encrypt() ! {
_ := ascon.encrypt(key_ascon, ascon_nonce, ad, msg)!
}
fn bench_ascon_aead128_decrypt() ! {
_ := ascon.decrypt(key_ascon, ascon_nonce, ad, ciphertext_aead128)!
}
// expected ciphertext for chacha20poly1305
const ciphertext_chachapoly1305 = hex.decode('67dea3c65f0f326bcf587f024140a85d9535790d9b16129210a2289eda43bb9b62746450026fc1baf466bcb8a181843cd424')!
fn bench_chacha20poly1305_encrypt() ! {
_ := chacha20poly1305.encrypt(msg, key_cpoly, cpoly_nonce, ad)!
}
fn bench_chacha20poly1305_decrypt() ! {
_ := chacha20poly1305.decrypt(ciphertext_chachapoly1305, key_cpoly, cpoly_nonce, ad)!
}
fn main() {
cf := benchmark.BenchmarkDefaults{
n: 10000
}
println('Encryption..')
println('-----------')
mut b0 := benchmark.setup(bench_ascon_aead128_encrypt, cf)!
b0.run()
mut b1 := benchmark.setup(bench_chacha20poly1305_encrypt, cf)!
b1.run()
println('')
println('Decryption..')
println('-----------')
mut b2 := benchmark.setup(bench_ascon_aead128_decrypt, cf)!
b2.run()
mut b3 := benchmark.setup(bench_chacha20poly1305_decrypt, cf)!
b3.run()
}

View file

@ -0,0 +1,197 @@
// Ascon-Hash256 (and Ascon-XOF128) benchmark compared to builtin
// crypto.sha256 (for sum256) and sha3.shake256 (for xof outputing 256-bits)
//
// This benchmark code was adapted from argon2 benchmark by @fleximus, the creator argon2 module.
// Credit tributed to @fleximus
// See https://gist.github.com/fleximus/db5b867a9a37da46340db61bdac6e696
//
// Output
// ======
// Sum and Xof 256-bits output performance comparison
// ============================================================
// Iterations per test: 10000
// --------------------------------------------------------------------------------------------------
// Data Size | Ascon256 | Sha256 | Ratio 256 || AsconXof128 | Shake256 | Ratio (Xof) |
// --------------------------------------------------------------------------------------------------
// 4 B | 24.00ms | 33.00ms | 0.73x || 24.00ms | 208.00ms | 0.12x |
// 6 B | 23.00ms | 53.00ms | 0.45x || 25.00ms | 287.00ms | 0.08x |
// 8 B | 35.00ms | 37.00ms | 0.95x || 26.00ms | 202.00ms | 0.18x |
// 16 B | 30.00ms | 37.00ms | 0.83x || 30.00ms | 205.00ms | 0.15x |
// 64 B | 55.00ms | 61.00ms | 0.89x || 53.00ms | 241.00ms | 0.23x |
// 75 B | 61.00ms | 57.00ms | 1.07x || 58.00ms | 182.00ms | 0.34x |
// 256 B | 154.00ms | 123.00ms | 1.25x || 144.00ms | 398.00ms | 0.39x |
// 512 B | 273.00ms | 216.00ms | 1.26x || 265.00ms | 779.00ms | 0.35x |
// 1025 B | 610.00ms | 401.00ms | 1.52x || 509.00ms | 1.37s | 0.45x |
// --------------------------------------------------------------------------------------------------
// Total | 1.27s | 1.02s | 1.24x || 1.14s | 3.87s | 0.294x|
// --------------------------------------------------------------------------------------------------
//
// Per-operation averages:
// Ascon256: 14108 ns per hash
// Sha256: 11360 ns per hash
// AsconXof128: 12648 ns per hash
// Shake256: 43036 ns per hash
//
module main
import time
import crypto.sha3
import crypto.sha256
import x.crypto.ascon
const benchmark_iterations = 10000
// We include more small size because, Ascon-Hash256 working with more smaller block size.
const test_data_sizes = [
4, // below Ascon-Hash256 block size
6, // Still below Ascon-Hash256 block size
8, // align with Ascon-Hash256 block size
16, // Small data
64, // Medium data
75, // above 64-bytes block
256, // Large data
512,
1025,
]
fn generate_test_data(size int) []u8 {
mut data := []u8{len: size}
for i in 0 .. size {
data[i] = u8(i % 256)
}
return data
}
fn benchmark_ascon_sha256(data []u8, iterations int) time.Duration {
start := time.now()
for _ in 0 .. iterations {
_ := ascon.sum256(data)
}
return time.since(start)
}
fn benchmark_sha256_sum256(data []u8, iterations int) time.Duration {
start := time.now()
for _ in 0 .. iterations {
_ := sha256.sum256(data)
}
return time.since(start)
}
// for eXtendable output functions (XOF)
fn benchmark_ascon_xof128_32(data []u8, iterations int) time.Duration {
start := time.now()
for _ in 0 .. iterations {
_ := ascon.xof128(data, 32) or { panic(err) }
}
return time.since(start)
}
fn benchmark_sha3_shake256(data []u8, iterations int) time.Duration {
start := time.now()
for _ in 0 .. iterations {
_ := sha3.shake256(data, 32)
}
return time.since(start)
}
fn format_duration(d time.Duration) string {
if d.microseconds() < 1000 {
return '${d.microseconds():6}μs'
} else if d.milliseconds() < 1000 {
return '${f64(d.milliseconds()):6.2f}ms'
} else {
return '${f64(d.seconds()):6.2f}s'
}
}
const data_title = 'Data Size'
const ascon_sum256_title = 'Ascon256'
const sha256_title = 'Sha256'
const ascon_xof128_title = 'AsconXof128'
const sha3_shake256_title = 'Shake256'
const ratio_ascon256_w_sha256 = 'Ratio 256'
const ratio_asconxof128_w_shake256 = 'Ratio (Xof)'
fn main() {
println('')
println('Sum and Xof 256-bits output performance comparison')
println('============================================================')
println('Iterations per test: ${benchmark_iterations}')
println('${'-'.repeat(98)}')
println('${data_title:12} | ${ascon_sum256_title:10} | ${sha256_title:10} | ${ratio_ascon256_w_sha256:12} || ${ascon_xof128_title:10} | ${sha3_shake256_title:10} | ${ratio_asconxof128_w_shake256:12} |')
println('${'-'.repeat(98)}')
mut total_ascon256 := time.Duration(0)
mut total_sha256 := time.Duration(0)
mut total_shake256 := time.Duration(0)
mut total_asconxof128 := time.Duration(0)
for size in test_data_sizes {
test_data := generate_test_data(size)
// Warm up
_ := ascon.sum256(test_data)
_ := sha256.sum256(test_data)
_ := ascon.xof128(test_data, 32)!
_ := sha3.shake256(test_data, 32)
// Benchmark Ascon-HASH256
ascon256_time := benchmark_ascon_sha256(test_data, benchmark_iterations)
// Benchmark Sha256 implementation
sha256_time := benchmark_sha256_sum256(test_data, benchmark_iterations)
// Benchmark Sha3 shake256 implementation
shake256_time := benchmark_sha3_shake256(test_data, benchmark_iterations)
// Benchmark AsconXof128 256-bits output
asconxof128_time := benchmark_ascon_xof128_32(test_data, benchmark_iterations)
// Calculate ratio ascon256 / sha256
ratio_ascon256_sha256 := f64(ascon256_time.nanoseconds()) / f64(sha256_time.nanoseconds())
// Calculate ratio asconxof128 / shake256
ratio_asconxof128_shake256 := f64(asconxof128_time.nanoseconds()) / f64(shake256_time.nanoseconds())
ascon256_str := format_duration(ascon256_time)
sha256_str := format_duration(sha256_time)
asconxof128_str := format_duration(asconxof128_time)
shake256_str := format_duration(shake256_time)
ratio_ascon256_sha256_str := '${ratio_ascon256_sha256:6.2f}x'
ratio_asconxof128_shake256_str := '${ratio_asconxof128_shake256:6.2f}x'
println('${size:10} B | ${ascon256_str:10} | ${sha256_str:10} | ${ratio_ascon256_sha256_str:12} || ${asconxof128_str:11} | ${shake256_str:10} | ${ratio_asconxof128_shake256_str:12} |')
total_ascon256 += ascon256_time
total_sha256 += sha256_time
total_asconxof128 += asconxof128_time
total_shake256 += shake256_time
}
println('${'-'.repeat(98)}')
// Overall performance comparison
overall_ascon256_w_sha256_ratio := f64(total_ascon256.nanoseconds()) / f64(total_sha256.nanoseconds())
overall_asconxof128_w_shake256_ratio := f64(total_asconxof128.nanoseconds()) / f64(total_shake256.nanoseconds())
total_title := 'Total'
println('${total_title:12} | ${format_duration(total_ascon256):10} | ${format_duration(total_sha256):10} | ${overall_ascon256_w_sha256_ratio:11.2f}x || ${format_duration(total_asconxof128):11} | ${format_duration(total_shake256):10} | ${overall_asconxof128_w_shake256_ratio:12.2f}x|')
println('${'-'.repeat(98)}')
println('')
println('Per-operation averages:')
avg_ascon256 := total_ascon256.nanoseconds() / (benchmark_iterations * test_data_sizes.len)
avg_sha256 := total_sha256.nanoseconds() / (benchmark_iterations * test_data_sizes.len)
avg_shake256 := total_shake256.nanoseconds() / (benchmark_iterations * test_data_sizes.len)
avg_asconxof128 := total_asconxof128.nanoseconds() / (benchmark_iterations * test_data_sizes.len)
println(' Ascon256:\t ${avg_ascon256:8} ns per hash')
println(' Sha256:\t ${avg_sha256:8} ns per hash')
println(' AsconXof128:\t ${avg_asconxof128:8} ns per hash')
println(' Shake256:\t ${avg_shake256:8} ns per hash')
println('')
}

View file

@ -0,0 +1,36 @@
import time
import x.crypto.ascon
// Before:
// Benchmarking ascon.sum256 ...
// Average ascon.sum256 time: 8 µs
// Benchmarking ascon.sum256 ...
// Average ascon.sum256 time: 6 µs
// For xof128 (32 bytes)
// Benchmarking ascon.xof128 ...
// Average ascon.xof128 time: 7 µs
// Benchmarking ascon.xof128 ...
// Average ascon.xof128 time: 6 µs
// For cxof128 32 bytes
// Benchmarking ascon.cxof128 ...
// Average ascon.cxof128 time: 9 µs
// Benchmarking ascon.sum256 ...
// Average ascon.cxof128 time: 7 µs
//
fn main() {
iterations := 1000
msg := [u8(0xff)].repeat(100)
println('Benchmarking ascon.sum256 ...')
mut total_sum_time := i64(0)
for _ in 0 .. iterations {
sw := time.new_stopwatch()
_ := ascon.sum256(msg)
elapsed := sw.elapsed().microseconds()
total_sum_time += elapsed
}
avg_sum_time := total_sum_time / iterations
println('Average ascon.sum256 time: ${avg_sum_time} µs')
}

View file

@ -33,7 +33,7 @@ fn (mut d Digest) finish() {
d.State.e0 ^= load_bytes(d.buf[..d.length], d.length) d.State.e0 ^= load_bytes(d.buf[..d.length], d.length)
// Permutation step was done in squeezing-phase // Permutation step was done in squeezing-phase
// ascon_pnr(mut d.State, 12) // ascon_pnr(mut d.State, ascon_prnd_12)
// zeroing Digest buffer // zeroing Digest buffer
d.length = 0 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 this d.buf length has reached block_size bytes, absorb it.
if d.length == block_size { if d.length == block_size {
d.State.e0 ^= binary.little_endian_u64(d.buf) d.State.e0 ^= binary.little_endian_u64(d.buf)
ascon_pnr(mut d.State, 12) ascon_pnr(mut d.State, ascon_prnd_12)
// reset the internal buffer // reset the internal buffer
d.length = 0 d.length = 0
d.buf.reset() d.buf.reset()
@ -87,7 +87,7 @@ fn (mut d Digest) absorb(msg_ []u8) int {
for msg.len >= block_size { for msg.len >= block_size {
d.State.e0 ^= binary.little_endian_u64(msg[0..block_size]) d.State.e0 ^= binary.little_endian_u64(msg[0..block_size])
msg = msg[block_size..] msg = msg[block_size..]
ascon_pnr(mut d.State, 12) ascon_pnr(mut d.State, ascon_prnd_12)
} }
// If there are partial block, just stored into buffer. // If there are partial block, just stored into buffer.
if msg.len > 0 { 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 // The squeezing phase begins after msg is absorbed with an
// permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[12] to the state: // permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[12] to the state:
ascon_pnr(mut d.State, 12) ascon_pnr(mut d.State, ascon_prnd_12)
mut pos := 0 mut pos := 0
mut clen := dst.len mut clen := dst.len
// process for full block size // process for full block size
for clen >= block_size { for clen >= block_size {
binary.little_endian_put_u64(mut dst[pos..pos + 8], d.State.e0) binary.little_endian_put_u64(mut dst[pos..pos + 8], d.State.e0)
ascon_pnr(mut d.State, 12) ascon_pnr(mut d.State, ascon_prnd_12)
pos += block_size pos += block_size
clen -= block_size clen -= block_size
} }
@ -133,3 +133,39 @@ fn (mut d Digest) squeeze(mut dst []u8) int {
return pos return pos
} }
@[direct_array_access; inline]
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..]
}
ascon_pnr(mut s, ascon_prnd_12)
}
// Absorb the last partial message block
s.e0 ^= load_bytes(msg, msg.len)
s.e0 ^= pad(msg.len)
// Squeezing phase
//
// The squeezing phase begins after msg is absorbed with an
// 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)
ascon_pnr(mut s, ascon_prnd_12)
pos += block_size
clen -= block_size
}
// final output, the resulting 256-bit digest is the concatenation of hash blocks
store_bytes(mut out[pos..], s.e0, clen)
return out
}

View file

@ -42,13 +42,13 @@ const hash256_initial_state = State{
} }
// sum256 creates an Ascon-Hash256 checksum for bytes on msg and produces a 256-bit hash. // sum256 creates an Ascon-Hash256 checksum for bytes on msg and produces a 256-bit hash.
pub fn sum256(msg []u8) []u8 { pub fn sum256(msg_ []u8) []u8 {
mut h := new_hash256() // This is single-shot function, so, no need to use Hash256 opaque that process
_ := h.write(msg) or { panic(err) } // message in streaming way. To reduce this overhead, use raw processing instead.
h.Digest.finish() //
mut dst := []u8{len: hash256_size} // Initialize state
_ := h.Digest.squeeze(mut dst) mut s := hash256_initial_state
return dst return ascon_generic_hash(mut s, msg_, hash256_size)
} }
// Hash256 is an opaque provides an implementation of Ascon-Hash256 from NIST.SP.800-232 standard. // Hash256 is an opaque provides an implementation of Ascon-Hash256 from NIST.SP.800-232 standard.

View file

@ -5,15 +5,8 @@
// Utility helpers used across the module // Utility helpers used across the module
module ascon module ascon
import math.bits
import encoding.binary 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 // clear_bytes clears the bytes of x in n byte
@[inline] @[inline]
fn clear_bytes(x u64, n int) u64 { fn clear_bytes(x u64, n int) u64 {
@ -100,8 +93,3 @@ fn store_bytes(mut out []u8, x u64, n int) {
out[i] = get_byte(x, i) out[i] = get_byte(x, i)
} }
} }
@[inline]
fn ascon_rotate_right(x u64, n int) u64 {
return (x >> n) | x << (64 - n)
}

View file

@ -30,13 +30,11 @@ const xof128_initial_state = State{
// xof128 creates an Ascon-XOF128 checksum of msg with specified desired size of output. // xof128 creates an Ascon-XOF128 checksum of msg with specified desired size of output.
pub fn xof128(msg []u8, size int) ![]u8 { pub fn xof128(msg []u8, size int) ![]u8 {
mut x := new_xof128(size) if size > max_hash_size {
_ := x.write(msg)! return error('xof128: invalid size')
x.Digest.finish() }
mut out := []u8{len: size} mut s := xof128_initial_state
_ := x.Digest.squeeze(mut out) return ascon_generic_hash(mut s, msg, size)
x.reset()
return out
} }
// xof128_64 creates a 64-bytes of Ascon-XOF128 checksum of msg. // xof128_64 creates a 64-bytes of Ascon-XOF128 checksum of msg.
@ -170,13 +168,10 @@ const cxof128_initial_state = State{
// cxof128 creates an Ascon-CXOF128 checksum of msg with supplied size and custom string cs. // cxof128 creates an Ascon-CXOF128 checksum of msg with supplied size and custom string cs.
pub fn cxof128(msg []u8, size int, cs []u8) ![]u8 { pub fn cxof128(msg []u8, size int, cs []u8) ![]u8 {
mut cx := new_cxof128(size, cs)! // Initialize CXof128 state with precomputed-value and absorb the customization string
_ := cx.write(msg)! mut s := cxof128_initial_state
cx.Digest.finish() cxof128_absorb_custom_string(mut s, cs)
mut out := []u8{len: size} return ascon_generic_hash(mut s, msg, size)
_ := cx.Digest.squeeze(mut out)
cx.reset()
return out
} }
// cxof128_64 creates a 64-bytes of Ascon-CXOF128 checksum of msg with supplied custom string in cs. // cxof128_64 creates a 64-bytes of Ascon-CXOF128 checksum of msg with supplied custom string in cs.
@ -305,7 +300,7 @@ pub fn (mut x CXof128) free() {
fn cxof128_absorb_custom_string(mut s State, cs []u8) { fn cxof128_absorb_custom_string(mut s State, cs []u8) {
// absorb Z0, the length of the customization string (in bits) encoded as a u64 // absorb Z0, the length of the customization string (in bits) encoded as a u64
s.e0 ^= u64(cs.len) << 3 s.e0 ^= u64(cs.len) << 3
ascon_pnr(mut s, 12) ascon_pnr(mut s, ascon_prnd_12)
// absorb the customization string // absorb the customization string
mut zlen := cs.len mut zlen := cs.len
@ -313,7 +308,7 @@ fn cxof128_absorb_custom_string(mut s State, cs []u8) {
for zlen >= block_size { for zlen >= block_size {
block := unsafe { cs[zidx..zidx + block_size] } block := unsafe { cs[zidx..zidx + block_size] }
s.e0 ^= binary.little_endian_u64(block) s.e0 ^= binary.little_endian_u64(block)
ascon_pnr(mut s, 12) ascon_pnr(mut s, ascon_prnd_12)
// updates a index // updates a index
zlen -= block_size zlen -= block_size
@ -323,5 +318,5 @@ fn cxof128_absorb_custom_string(mut s State, cs []u8) {
last_block := unsafe { cs[zidx..] } last_block := unsafe { cs[zidx..] }
s.e0 ^= load_bytes(last_block, last_block.len) s.e0 ^= load_bytes(last_block, last_block.len)
s.e0 ^= pad(last_block.len) s.e0 ^= pad(last_block.len)
ascon_pnr(mut s, 12) ascon_pnr(mut s, ascon_prnd_12)
} }