x.crypto: add an Ascon-AEAD128 functionalities into x.crypto.ascon

This commit is contained in:
blackshirt 2025-09-09 05:10:40 +00:00 committed by Delyan Angelov
parent f6867a8ee9
commit ca7d17ad86
No known key found for this signature in database
GPG key ID: 66886C0F12D595ED
6 changed files with 745 additions and 19 deletions

View file

@ -0,0 +1,466 @@
// 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 an Authenticated Encryption with Associated Data based on Ascon-AEAD128,
// AEAD Scheme defined in NIST.SP.800-232 standard.
module ascon
import encoding.binary
import crypto.internal.subtle
// The constants for Ascon-AEAD128
//
// key_size is 128-bit size of Ascon-AEAD128 key
pub const key_size = 16
// nonce_size is 128-bit size of Ascon-AEAD128 nonce
pub const nonce_size = 16
// tag_size is 128-bit size of Ascon-AEAD128 authentication tag
pub const tag_size = 16
// aead128_iv is a precomputed initialization phase values for Ascon-AEAD128
// See Table 14 of NIST SP 800-232 doc.
const aead128_iv = u64(0x0000_1000_808c_0001)
// aead128_data_limit is a limit amount of data processed during encryption and decryption,
// including the nonce, shall not exceed 2⁵⁴ bytes for a given key.
const aead128_data_limit = u64(1) << 54 - 1
// aead128_block_size is a number (rate) of input bytes processed per invocation of the underlying of state.
// Ascon-AEAD128 working with 128-bit rate
const aead128_block_size = 16
// encrypt encrypts the message under provided key and nonce and supplied additional data in ad.
// It returns an authenticated output of Ascon-AEAD128 ciphertext where authentication tag
// was stored within the end of ciphertext.
// Note: The Ascon-AEAD128 key shall be kept secret,
@[direct_array_access]
pub fn encrypt(key []u8, nonce []u8, ad []u8, msg []u8) ![]u8 {
// Preliminary check
if key.len != key_size {
return error('encrypt: invalid key size')
}
if nonce.len != nonce_size {
return error('encrypt: invalid nonce size')
}
// The key shall be updated to a new key once the total amount of input data reaches the limit
data_length := u64(nonce.len) + u64(msg.len) + u64(ad.len)
if data_length > aead128_data_limit {
return error('encrypt: exceed data limit')
}
mut s := State{}
mut out := []u8{len: msg.len + tag_size}
// Ascon-AEAD128 comprises four phases:
// - initialization of the state,
// - associated data processing,
// - plaintext processing,
// - and finalization (includes writing the tag).
k0, k1 := aead128_init(mut s, key, nonce)
aead128_process_ad(mut s, ad)
loc := aead128_process_msg(mut out, mut s, msg)
aead128_finalize(mut s, k0, k1)
aead128_write_tag(mut out, s, loc)
// clean out the intermediate Ascon state
reset_state(mut s)
return out
}
// decrypt decrypts authenticated encrypted messages in ciphertext that encrypted under
// provided key and nonce with additional data in `ad`.
// It would check if authentication tag mas matching and return decrypted message
// if success or error on fails.
@[direct_array_access]
pub fn decrypt(key []u8, nonce []u8, ad []u8, ciphertext []u8) ![]u8 {
// Preliminary check
if key.len != key_size {
return error('decrypt: invalid key size')
}
if nonce.len != nonce_size {
return error('decrypt: invalid nonce size')
}
if ciphertext.len < tag_size {
return error('decrypt: invalid ciphertext size')
}
data_length := u64(nonce.len) + u64(ciphertext.len) + u64(ad.len)
if data_length > aead128_data_limit {
return error('decrypt: exceed data limit')
}
mut s := State{}
// Initialization phase and additional data processing
k0, k1 := aead128_init(mut s, key, nonce)
aead128_process_ad(mut s, ad)
// Decryption phase, start by slicing the ciphertext
cmsg := ciphertext[0..ciphertext.len - tag_size]
stag := ciphertext[ciphertext.len - tag_size..ciphertext.len]
mut msg := []u8{len: ciphertext.len - tag_size}
// Partially decrypt the cmsg and stored into msg buffer
aead128_partial_dec(mut msg, mut s, cmsg)
// Finalizes the state and calc the tag and compares with expected tag.
// It would return error if the tag was unmatching.
aead128_finalize(mut s, k0, k1)
mut ctag := []u8{len: tag_size}
aead128_write_tag(mut ctag, s, 0)
if subtle.constant_time_compare(ctag, stag) != 1 {
// clean up
unsafe {
msg.reset()
ctag.reset()
}
reset_state(mut s)
return error('decrypt: unmatching tag')
}
return msg
}
// Aead128 is an opaque provides an implementation of Ascon-AEAD128 from NIST.SP.800-232 standard.
// Its implements `x.crypto.chacha20poly1305.AEAD` interfaces.
@[noinit]
pub struct Aead128 {
State
mut:
// 32-bytes of underlying key
key [2]u64
}
// new_aead128 creates a new Aead128 instance and initialized with supplied key.
@[direct_array_access]
pub fn new_aead128(key []u8) !&Aead128 {
if key.len != key_size {
return error('invalid cipher key size')
}
k0 := binary.little_endian_u64(key[0..8])
k1 := binary.little_endian_u64(key[8..16])
mut c := &Aead128{}
// Partially initializes state
c.State.e0 = aead128_iv
c.State.e1 = k0
c.State.e2 = k1
// stores the key
c.key[0] = k0
c.key[1] = k1
return c
}
// nonce_size returns the nonce size of Ascon-AEAD128 Aead128.
pub fn (c &Aead128) nonce_size() int {
return nonce_size
}
// overhead returns the maximum difference between the lengths of a plaintext and its ciphertext.
pub fn (c &Aead128) overhead() int {
return tag_size
}
// encrypt encrypts the message under provided key and nonce and supplied additional data in ad.
// It returns an authenticated output of Ascon-AEAD128 ciphertext where authentication tag
// was stored within the end of ciphertext.
@[direct_array_access]
pub fn (mut c Aead128) encrypt(msg []u8, nonce []u8, ad []u8) ![]u8 {
// Check for the nonce
if nonce.len != nonce_size {
return error('encrypt: invalid nonce size')
}
data_length := u64(msg.len) + u64(ad.len) + 32
if data_length > aead128_data_limit {
return error('encrypt: exceed data limit')
}
// Initialization phase
n0 := binary.little_endian_u64(nonce[0..8])
n1 := binary.little_endian_u64(nonce[8..16])
// setup state
c.State.e0 = aead128_iv
c.State.e1 = c.key[0]
c.State.e2 = c.key[1]
c.State.e3 = n0
c.State.e4 = n1
// Update state by permutation
ascon_pnr(mut c.State, 12)
// XOR-ing with the cipher's key
c.State.e3 ^= c.key[0]
c.State.e4 ^= c.key[1]
// Associated data processing
aead128_process_ad(mut c.State, ad)
// Message processing
mut dst := []u8{len: msg.len + tag_size}
n := aead128_process_msg(mut dst, mut c.State, msg)
// Finalization and writes out the tag into dst
aead128_finalize(mut c.State, c.key[0], c.key[1])
aead128_write_tag(mut dst, c.State, n)
return dst
}
// decrypt decrypts the ciphertext and validates authentication tag with
// provided nonce and additional data ad. It returns error on fails or tag unmatching.
@[direct_array_access]
fn (mut c Aead128) decrypt(ciphertext []u8, nonce []u8, ad []u8) ![]u8 {
// Check for nonce
if nonce.len != nonce_size {
return error('bad nonce size')
}
// Check for ciphertext length, its ahould have length >= tag_size
if ciphertext.len < tag_size {
return error('bad ciphertext size')
}
// check for data limit overflow
data_length := u64(ciphertext.len) + u64(ad.len) + 32
if data_length > aead128_data_limit {
return error('decrypt: exceed data limit')
}
// load nonce
n0 := binary.little_endian_u64(nonce[0..8])
n1 := binary.little_endian_u64(nonce[8..16])
// Reinitialize internal state
c.State.e0 = aead128_iv
c.State.e1 = c.key[0]
c.State.e2 = c.key[1]
c.State.e3 = n0
c.State.e4 = n1
// scrambled with permutation routine
ascon_pnr(mut c.State, 12)
// xor-ing with the cipher's key
c.State.e3 ^= c.key[0]
c.State.e4 ^= c.key[1]
// Associated data processing
//
aead128_process_ad(mut c.State, ad)
// As we know, ciphertext length was sum of encrypted mesage length plus tag_size
// Lets slicing it
cxt_len := ciphertext.len
cmsg := ciphertext[0..cxt_len - tag_size]
ctag := ciphertext[cxt_len - tag_size..cxt_len]
mut out := []u8{len: cxt_len - tag_size}
aead128_partial_dec(mut out, mut c.State, cmsg)
aead128_finalize(mut c.State, c.key[0], c.key[1])
// tag verification
mut tag := []u8{len: tag_size}
aead128_write_tag(mut tag, c.State, 0)
if subtle.constant_time_compare(ctag, tag) != 1 {
// Cleans up previously produces bytes (state)
reset_state(mut c.State)
unsafe {
tag.free()
out.free()
}
return error('Aead128.decrypt: unmatching tag')
}
return out
}
// Helpers for Ascon-AEAD128
//
// aead128_init initializes Ascon-AEAD128 state by provided key and nonce.
// Its return two's u64 values from deserialized key bytes in little-endian form.
@[direct_array_access; inline]
fn aead128_init(mut s State, key []u8, nonce []u8) (u64, u64) {
// load key and nonce into state in little-endian form,
// The endianness has been switched from big endian to little endian
k0 := binary.little_endian_u64(key[0..8])
k1 := binary.little_endian_u64(key[8..16])
n0 := binary.little_endian_u64(nonce[0..8])
n1 := binary.little_endian_u64(nonce[8..16])
// Given a 128-bit 𝐾 and a 128-bit 𝑁, the 320-bit internal
// state S is initialized as the concatenation of 𝐼𝑉, 𝐾, and 𝑁:
// S ← 𝐼𝑉 || 𝐾 || 𝑁,
s.e0 = aead128_iv
s.e1 = k0
s.e2 = k1
s.e3 = n0
s.e4 = n1
// updates State using the permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[12], S ← 𝐴𝑠𝑐𝑜𝑛-𝑝[12](S)
ascon_pnr(mut s, 12)
// Then XORing the secret key 𝐾 into the last 128 bits of internal state:
// S ← S ⊕ (0¹⁹² ∥ 𝐾).
s.e3 ^= k0
s.e4 ^= k1
return k0, k1
}
// aead128_process_ad absorbs associated data into Ascon-AEAD128 state.
@[direct_array_access; inline]
fn aead128_process_ad(mut s State, ad []u8) {
mut ad_length := ad.len
mut ad_idx := 0
if ad_length > 0 {
for ad_length >= aead128_block_size {
// Each associated data block 𝐴𝑖 (0 ≤ 𝑖 ≤ 𝑚) is absorbed into the first 128 bits of
// state as S[0127] ← S[0127] ⊕ 𝐴𝑖,
block := unsafe { ad[ad_idx..ad_idx + aead128_block_size] }
s.e0 ^= binary.little_endian_u64(block[0..8])
s.e1 ^= binary.little_endian_u64(block[8..16])
// Apply permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[8] to the state
ascon_pnr(mut s, 8)
// Updates index
ad_length -= aead128_block_size
ad_idx += aead128_block_size
}
// process partial block if it exists
if ad_length >= 8 {
first_block := unsafe { ad[ad_idx..ad_idx + 8] }
s.e0 ^= binary.little_endian_u64(first_block)
// Is there more bytes to process on?
last_block := unsafe { ad[ad_idx + 8..] }
s.e1 ^= pad(last_block.len)
if last_block.len > 0 {
s.e1 ^= u64_from_partial_bytes(last_block)
}
// update index
ad_length -= first_block.len + last_block.len
ad_idx += first_block.len + last_block.len
} else {
last_block := unsafe { ad[ad_idx..] }
s.e0 ^= pad(last_block.len)
if last_block.len > 0 {
s.e0 ^= u64_from_partial_bytes(last_block)
}
}
// Apply permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[8] to the state
ascon_pnr(mut s, 8)
}
// The final step of processing associated data is to update the state
// with a constant that provides domain separation.
s.e4 ^= u64(0x8000_0000_0000_0000)
}
// aead128_process_msg process (encrypt) the messages msg and asborb it into Ascon-AEAD128 state
// Its written the result into out buffer and return the number of bytes has been written.
@[direct_array_access; inline]
fn aead128_process_msg(mut out []u8, mut s State, msg []u8) int {
mut pos := 0
mut mlen := msg.len
mut midx := 0
for mlen >= aead128_block_size {
block := unsafe { msg[midx..midx + aead128_block_size] }
s.e0 ^= binary.little_endian_u64(block[0..8])
s.e1 ^= binary.little_endian_u64(block[8..16])
// stores
binary.little_endian_put_u64(mut out[pos..pos + 8], s.e0)
binary.little_endian_put_u64(mut out[pos + 8..], s.e1)
// apply permutation
ascon_pnr(mut s, 8)
// updates index
mlen -= aead128_block_size
pos += aead128_block_size
midx += aead128_block_size
}
// process partial block if it exists
if mlen >= 8 {
mut first_block := unsafe { msg[midx..] }
s.e0 ^= load_bytes(first_block[0..8], 8)
binary.little_endian_put_u64(mut out[pos..pos + 8], s.e0)
// Is there are reminder bytes to process on?
last_block := unsafe { msg[midx + 8..] }
if last_block.len > 0 {
// We use `load_bytes` to handle length < 8
s.e1 ^= load_bytes(last_block, last_block.len)
store_bytes(mut out[pos + 8..], s.e1, last_block.len)
s.e1 ^= pad(last_block.len)
}
} else {
last_block := unsafe { msg[midx..] }
s.e0 ^= load_bytes(last_block, last_block.len)
store_bytes(mut out[pos..], s.e0, last_block.len)
s.e0 ^= pad(last_block.len)
}
// how much we have written
pos += mlen
return pos
}
// aead128_partial_dec partially decrypts the encrypted part of the message in the cmsg,
// and stored into out buffer.
// Note: The output buffer should have the same length with cmsg, ie, out.len == cmsg.len
@[direct_array_access]
fn aead128_partial_dec(mut out []u8, mut s State, cmsg []u8) {
mut cmsg_len := cmsg.len
mut pos := 0
// assert out.len == cmsg.len
for cmsg_len >= aead128_block_size {
block := unsafe { cmsg[pos..pos + aead128_block_size] }
c0 := binary.little_endian_u64(block[0..8])
c1 := binary.little_endian_u64(block[8..16])
binary.little_endian_put_u64(mut out[pos..pos + 8], s.e0 ^ c0)
binary.little_endian_put_u64(mut out[pos + 8..pos + 16], s.e1 ^ c1)
s.e0 = c0
s.e1 = c1
ascon_pnr(mut s, 8)
// updates index
pos += aead128_block_size
cmsg_len -= aead128_block_size
}
// partial block
if cmsg_len >= 8 {
mut first_block := unsafe { cmsg[pos..pos + 8] }
c0 := binary.little_endian_u64(first_block)
binary.little_endian_put_u64(mut out[pos..pos + 8], c0 ^ s.e0)
last_block := unsafe { cmsg[pos + 8..] }
c1 := load_bytes(last_block, last_block.len)
store_bytes(mut out[pos + 8..], c1 ^ s.e1, last_block.len)
s.e0 = c0
s.e1 = clear_bytes(s.e1, last_block.len)
s.e1 |= c1
s.e0 ^= pad(last_block.len)
} else {
last_block := unsafe { cmsg[pos..] }
c0 := load_bytes(last_block, last_block.len)
store_bytes(mut out[pos..], s.e0 ^ c0, last_block.len)
s.e0 = clear_bytes(s.e0, last_block.len)
s.e0 |= c0
s.e0 ^= pad(last_block.len)
}
}
// aead128_finalize does finalization step and generates tag value.
@[direct_array_access]
fn aead128_finalize(mut s State, k0 u64, k1 u64) {
// Load the key into state, S ← S ⊕ (0¹²⁸ ∥ 𝐾 ∥ 0⁶⁴),
s.e2 ^= k0
s.e3 ^= k1
// then updated using the permutation 𝐴𝑠𝑐𝑜𝑛-𝑝[12]
ascon_pnr(mut s, 12)
// Finally, the tag 𝑇 is generated by XORing the key with the last 128 bits of the state:
// 𝑇𝑆[192319] ⊕ 𝐾.
s.e3 ^= k0
s.e4 ^= k1
}
// aead128_write_tag writes tag from state into out at loc offset.
@[direct_array_access; inline]
fn aead128_write_tag(mut out []u8, s State, loc int) {
binary.little_endian_put_u64(mut out[loc..loc + 8], s.e3)
binary.little_endian_put_u64(mut out[loc + 8..loc + 16], s.e4)
}

View file

@ -0,0 +1,248 @@
// 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 encoding.hex
// 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.
// See at https://github.com/ascon/ascon-c/blob/main/crypto_aead/asconaead128/LWC_AEAD_KAT_128_128.txt
struct KatTest {
cnt int
key string
nonce string
pt string
ad string
ct string
}
// testing for Ascon-AEAD128 encryption and decryption.
fn test_ascon_aead128_enc_dec() ! {
for item in aead128_kat_tests_data {
key := hex.decode(item.key)!
nonce := hex.decode(item.nonce)!
pt := hex.decode(item.pt)!
ad := hex.decode(item.ad)!
ct := hex.decode(item.ct)!
out := encrypt(key, nonce, ad, pt)!
assert out == ct
msg := decrypt(key, nonce, ad, ct)!
assert msg == pt
// Work with object-based Cipher
mut c := new_aead128(key)!
// Lets encrypt the message
exp_ct := c.encrypt(msg, nonce, ad)!
assert exp_ct == ct
// Lets decrypt it back
exp_msg := c.decrypt(exp_ct, nonce, ad)!
assert exp_msg == msg
}
}
const aead128_kat_tests_data = [
KatTest{
cnt: 1
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: ''
ad: ''
ct: '4F9C278211BEC9316BF68F46EE8B2EC6'
},
KatTest{
cnt: 2
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: ''
ad: '30'
ct: 'CCCB674FE18A09A285D6AB11B35675C0'
},
KatTest{
cnt: 3
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: ''
ad: '3031'
ct: 'F65B191550C4DF9CFDD4460EBBCCA782'
},
KatTest{
cnt: 4
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: ''
ad: '303132'
ct: 'D127CF7D2CD4DA8930616C70B3619F42'
},
KatTest{
cnt: 5
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: ''
ad: '30313233'
ct: '000BA92E52B5ED6B97C9D913CC4C82DF'
},
KatTest{
cnt: 6
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: ''
ad: '3031323334'
ct: 'F7CC167F8FED3AEEA99B385B8622157E'
},
KatTest{
cnt: 7
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: ''
ad: '303132333435'
ct: '51CCBC46D56E93B89B1A3BFDAD0AA4D5'
},
KatTest{
cnt: 8
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: ''
ad: '30313233343536'
ct: 'B38ABBD573E071C6265EEAC4A68F65AB'
},
KatTest{
cnt: 9
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: ''
ad: '3031323334353637'
ct: '865C594093A9EDEE2C1D6384CCB4939E'
},
KatTest{
cnt: 10
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: ''
ad: '303132333435363738'
ct: '24F13284A0F90F906B18C7E4061C0896'
},
KatTest{
cnt: 27
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: ''
ad: '303132333435363738393A3B3C3D3E3F40414243444546474849'
ct: '4ED362C4407B1D3BE17A51465659DECF'
},
KatTest{
cnt: 28
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: ''
ad: '303132333435363738393A3B3C3D3E3F404142434445464748494A'
ct: 'A35C52EC6E7C78C051B23D03F691916F'
},
KatTest{
cnt: 29
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: ''
ad: '303132333435363738393A3B3C3D3E3F404142434445464748494A4B'
ct: 'F1C946363A21CCFFE291A289202FC64C'
},
KatTest{
cnt: 30
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: ''
ad: '303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C'
ct: 'F1D453E933904578EEC3EA8E85550CE5'
},
KatTest{
cnt: 31
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: ''
ad: '303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D'
ct: '82E22C860881C0485EC5F5E8CEA42CEA'
},
KatTest{
cnt: 32
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: ''
ad: '303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E'
ct: 'C6306F1F154C78833984173360AAE874'
},
KatTest{
cnt: 33
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: ''
ad: '303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F'
ct: 'EFC3E78B02AD9A80A6F0548C5B0BB5BA'
},
KatTest{
cnt: 34
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: '20'
ad: ''
ct: 'E8DD576ABA1CD3E6FC704DE02AEDB79588'
},
KatTest{
cnt: 35
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: '20'
ad: '30'
ct: '962B8016836C75A7D86866588CA245D886'
},
KatTest{
cnt: 49
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: '20'
ad: '303132333435363738393A3B3C3D3E'
ct: '2089CB1DE2AE7D3E45BA7E9CC293548546'
},
KatTest{
cnt: 599
key: '000102030405060708090a0b0c0d0e0f'
nonce: '101112131415161718191a1b1c1d1e1f'
pt: '202122232425262728292a2b2c2d2e2f3031'
ad: '30313233'
ct: 'cf5337fcb70ec45d179e0c3f51bb25ac967a2e7062ee9bd80da6c72e3a9b43aed9e0'
},
KatTest{
cnt: 600
key: '000102030405060708090a0b0c0d0e0f'
nonce: '101112131415161718191a1b1c1d1e1f'
pt: '202122232425262728292a2b2c2d2e2f3031'
ad: '3031323334'
ct: '3076658cba8bf3bb6dccaa2f1255ee2e7db6f6493c7698f65f6860a7433a0f561e6c'
},
KatTest{
cnt: 601
key: '000102030405060708090a0b0c0d0e0f'
nonce: '101112131415161718191a1b1c1d1e1f'
pt: '202122232425262728292a2b2c2d2e2f3031'
ad: '303132333435'
ct: '9310c6dd8e9cbc3e406c0ebfbea312435f2c6975faf3b6b2b17ef1ea2503c3d31ef5'
},
KatTest{
cnt: 602
key: '000102030405060708090a0b0c0d0e0f'
nonce: '101112131415161718191a1b1c1d1e1f'
pt: '202122232425262728292a2b2c2d2e2f3031'
ad: '30313233343536'
ct: '6e024bd403f386eb9d1c56f459cfdcde1b2fdf8fd8be2faf0576c81e8d21c0dd8f8a'
},
KatTest{
cnt: 603
key: '000102030405060708090A0B0C0D0E0F'
nonce: '101112131415161718191A1B1C1D1E1F'
pt: '202122232425262728292A2B2C2D2E2F3031'
ad: '3031323334353637'
ct: 'fabe2cb1e7eba6329a30080f26e7dc72503dfc57f4de06a334b7ebadca03b44b73e9'
},
]

View file

@ -33,7 +33,6 @@ fn ascon_pnr(mut s State, nr int) {
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])
}
@ -86,13 +85,7 @@ fn ascon_perm(mut s State, c u8) {
// Σ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)

View file

@ -29,9 +29,8 @@ fn (mut d Digest) finish() {
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)
d.State.e0 ^= pad(d.length)
d.State.e0 ^= load_bytes(d.buf[..d.length], d.length)
// Permutation step was done in squeezing-phase
// ascon_pnr(mut d.State, 12)
@ -44,13 +43,20 @@ fn (mut d Digest) finish() {
d.done = true
}
// absorb absorbs message msg_ into Ascon-HASH256 state
// absorb absorbs message msg_ into Digest state.
@[direct_array_access]
fn (mut d Digest) absorb(msg_ []u8) int {
// nothing to absorb, just return
if msg_.len == 0 {
return 0
}
// Absorbing messages into Digest state working in streaming-way.
// Its continuesly updates internal state until you call `.finish` or `.free` it.
// Firstly, it would checking unprocessed bytes on internal buffer, and append it
// with bytes from messasge to align with block_size.
// And then absorb this buffered message into state.
// The process continues until the last partial block thats maybe below the block_size.
// If its happens, it will be stored on the Digest internal buffer for later processing.
mut msg := msg_.clone()
unsafe {
// Check if internal buffer has previous unprocessed bytes.
@ -93,18 +99,22 @@ fn (mut d Digest) absorb(msg_ []u8) int {
}
}
// squeeze squeezes the Digest's state and calculates checksum output for the current state.
// squeeze squeezes the 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 {
// nothing to store, just return unchanged
if dst.len == 0 {
return 0
}
// check
if dst.len < 1 || dst.len > max_hash_size {
if 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
@ -114,7 +124,7 @@ fn (mut d Digest) squeeze(mut dst []u8) int {
pos += block_size
clen -= block_size
}
// final output, the resulting 256-bit digest is the concatenation of hash blocks
// final output, the resulting hash is the concatenation of hash blocks
store_bytes(mut dst[pos..], d.State.e0, clen)
pos += clen

View file

@ -79,7 +79,7 @@ pub fn (h &Hash256) block_size() int {
return block_size
}
// reset reinits internal Hash256 state into default initialized state.
// reset resets and reinit internal Hash256 state into default initialized state.
@[direct_array_access]
pub fn (mut h Hash256) reset() {
h.Digest.State = hash256_initial_state

View file

@ -117,7 +117,12 @@ pub fn (mut x Xof128) sum(msg []u8) []u8 {
// 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 {
// Left unchanged
if dst.len == 0 {
return 0
}
// Check output size
if dst.len > max_hash_size {
panic('Xof128.read: invalid size')
}
mut x0 := x.clone()
@ -261,7 +266,11 @@ pub fn (mut x CXof128) sum(msg []u8) []u8 {
// 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 {
// Left unchanged, nothing space to store checksum
if dst.len == 0 {
return 0
}
if dst.len > max_hash_size {
panic('CXof128.read: invalid size')
}
mut x0 := x.clone()