mirror of
https://github.com/vlang/v.git
synced 2025-09-13 14:32:26 +03:00
x.crypto: add an Ascon-AEAD128 functionalities into x.crypto.ascon
This commit is contained in:
parent
f6867a8ee9
commit
ca7d17ad86
6 changed files with 745 additions and 19 deletions
466
vlib/x/crypto/ascon/aead128.v
Normal file
466
vlib/x/crypto/ascon/aead128.v
Normal 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[0∶127] ← S[0∶127] ⊕ 𝐴𝑖,
|
||||||
|
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:
|
||||||
|
// 𝑇 ← 𝑆[192∶319] ⊕ 𝐾.
|
||||||
|
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)
|
||||||
|
}
|
248
vlib/x/crypto/ascon/aead128_test.v
Normal file
248
vlib/x/crypto/ascon/aead128_test.v
Normal 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'
|
||||||
|
},
|
||||||
|
]
|
|
@ -33,7 +33,6 @@ fn ascon_pnr(mut s State, nr int) {
|
||||||
if nr < 1 || nr > 16 {
|
if nr < 1 || nr > 16 {
|
||||||
panic('Invalid round number')
|
panic('Invalid round number')
|
||||||
}
|
}
|
||||||
// handle other number
|
|
||||||
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])
|
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)
|
// Σ2(𝑆2) = 𝑆2 ⊕ (𝑆2 ⋙ 1) ⊕ (𝑆2 ⋙ 6)
|
||||||
// Σ3(𝑆3) = 𝑆3 ⊕ (𝑆3 ⋙ 10) ⊕ (𝑆3 ⋙ 17)
|
// Σ3(𝑆3) = 𝑆3 ⊕ (𝑆3 ⋙ 10) ⊕ (𝑆3 ⋙ 17)
|
||||||
// Σ4(𝑆4) = 𝑆4 ⊕ (𝑆4 ⋙ 7) ⊕ (𝑆4 ⋙ 41)
|
// Σ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.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.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.e2 ^= ascon_rotate_right(s.e2, 1) ^ ascon_rotate_right(s.e2, 6)
|
||||||
|
|
|
@ -29,9 +29,8 @@ fn (mut d Digest) finish() {
|
||||||
panic('Digest.finish: internal error')
|
panic('Digest.finish: internal error')
|
||||||
}
|
}
|
||||||
// Process for the last block stored on the internal buffer
|
// Process for the last block stored on the internal buffer
|
||||||
block := unsafe { d.buf[..d.length] }
|
d.State.e0 ^= pad(d.length)
|
||||||
d.State.e0 ^= pad(block.len)
|
d.State.e0 ^= load_bytes(d.buf[..d.length], d.length)
|
||||||
d.State.e0 ^= load_bytes(block, block.len)
|
|
||||||
|
|
||||||
// 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, 12)
|
||||||
|
@ -44,13 +43,20 @@ fn (mut d Digest) finish() {
|
||||||
d.done = true
|
d.done = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// absorb absorbs message msg_ into Ascon-HASH256 state
|
// absorb absorbs message msg_ into Digest state.
|
||||||
@[direct_array_access]
|
@[direct_array_access]
|
||||||
fn (mut d Digest) absorb(msg_ []u8) int {
|
fn (mut d Digest) absorb(msg_ []u8) int {
|
||||||
// nothing to absorb, just return
|
// nothing to absorb, just return
|
||||||
if msg_.len == 0 {
|
if msg_.len == 0 {
|
||||||
return 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()
|
mut msg := msg_.clone()
|
||||||
unsafe {
|
unsafe {
|
||||||
// Check if internal buffer has previous unprocessed bytes.
|
// 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.
|
// It accepts destination buffer with desired buffer length you want to output.
|
||||||
@[direct_array_access; inline]
|
@[direct_array_access; inline]
|
||||||
fn (mut d Digest) squeeze(mut dst []u8) int {
|
fn (mut d Digest) squeeze(mut dst []u8) int {
|
||||||
|
// nothing to store, just return unchanged
|
||||||
|
if dst.len == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
// check
|
// check
|
||||||
if dst.len < 1 || dst.len > max_hash_size {
|
if dst.len > max_hash_size {
|
||||||
panic('Digest.squeeze: invalid dst.len')
|
panic('Digest.squeeze: invalid dst.len')
|
||||||
}
|
}
|
||||||
// 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, 12)
|
||||||
// mut out := []u8{len: size}
|
|
||||||
mut pos := 0
|
mut pos := 0
|
||||||
mut clen := dst.len
|
mut clen := dst.len
|
||||||
// process for full block size
|
// process for full block size
|
||||||
|
@ -114,7 +124,7 @@ fn (mut d Digest) squeeze(mut dst []u8) int {
|
||||||
pos += block_size
|
pos += block_size
|
||||||
clen -= 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)
|
store_bytes(mut dst[pos..], d.State.e0, clen)
|
||||||
pos += clen
|
pos += clen
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ pub fn (h &Hash256) block_size() int {
|
||||||
return block_size
|
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]
|
@[direct_array_access]
|
||||||
pub fn (mut h Hash256) reset() {
|
pub fn (mut h Hash256) reset() {
|
||||||
h.Digest.State = hash256_initial_state
|
h.Digest.State = hash256_initial_state
|
||||||
|
|
|
@ -117,7 +117,12 @@ pub fn (mut x Xof128) sum(msg []u8) []u8 {
|
||||||
// Note: 1 ≤ dst.len ≤ max_hash_size.
|
// Note: 1 ≤ dst.len ≤ max_hash_size.
|
||||||
@[direct_array_access]
|
@[direct_array_access]
|
||||||
pub fn (mut x Xof128) read(mut dst []u8) !int {
|
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')
|
panic('Xof128.read: invalid size')
|
||||||
}
|
}
|
||||||
mut x0 := x.clone()
|
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.
|
// read tries to read `dst.len` bytes of hash output from current CXof128 state and stored into dst.
|
||||||
@[direct_array_access]
|
@[direct_array_access]
|
||||||
pub fn (mut x CXof128) read(mut dst []u8) !int {
|
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')
|
panic('CXof128.read: invalid size')
|
||||||
}
|
}
|
||||||
mut x0 := x.clone()
|
mut x0 := x.clone()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue