diff --git a/vlib/x/crypto/chacha20/chacha.v b/vlib/x/crypto/chacha20/chacha.v index b3916df650..8a943e385b 100644 --- a/vlib/x/crypto/chacha20/chacha.v +++ b/vlib/x/crypto/chacha20/chacha.v @@ -76,6 +76,7 @@ pub fn decrypt(key []u8, nonce []u8, ciphertext []u8) ![]u8 { // in src and stores the ciphertext result in dst in a single run of encryption. // You must never use the same (key, nonce) pair more than once for encryption. // This would void any confidentiality guarantees for the messages encrypted with the same nonce and key. +@[direct_array_access] pub fn (mut c Cipher) xor_key_stream(mut dst []u8, src []u8) { if src.len == 0 { return @@ -86,14 +87,13 @@ pub fn (mut c Cipher) xor_key_stream(mut dst []u8, src []u8) { if subtle.inexact_overlap(dst, src) { panic('chacha20: invalid buffer overlap') } - mut ciphertext := []u8{} // ChaCha20's encryption mechanism is a relatively simple operation. // for every block_sized block from src bytes, build ChaCha20 keystream block, - // then xor each byte in the block with keystresm block and then append xor-ed bytes + // then xor each byte in the block with keystresm block and then stores xor-ed bytes // to the output buffer. If there are remaining (trailing) partial bytes, // generate one more keystream block, xors keystream block with partial bytes - // and append to the result. + // and stores the result. // // Let's process for multiple blocks // number of blocks the src bytes should be split into @@ -104,33 +104,20 @@ pub fn (mut c Cipher) xor_key_stream(mut dst []u8, src []u8) { // get current src block to be xor-ed block := unsafe { src[i * chacha20.block_size..(i + 1) * chacha20.block_size] } - // xor current block of plaintext with keystream in c.block and store result in out - mut out := []u8{len: block.len} - n := cipher.xor_bytes(mut out, block, c.block) + // instead allocating output buffer for every block, we use dst buffer directly. + // xor current block of plaintext with keystream in c.block + n := cipher.xor_bytes(mut dst[i * chacha20.block_size..(i + 1) * chacha20.block_size], + block, c.block) assert n == c.block.len - - // append current output to the ciphertext buffer - ciphertext << out } // process for partial block if src.len % chacha20.block_size != 0 { c.generic_key_stream() - // get the remaining partial block + // get the remaining last partial block block := unsafe { src[nr_blocks * chacha20.block_size..] } // xor block with keystream - mut out := []u8{len: block.len} - n := cipher.xor_bytes(mut out, block, c.block) - assert n == block.len - - // make sure to take only remaining bytes - out = unsafe { out[0..src.len % chacha20.block_size] } - - // append last output to the ciphertext buffer - ciphertext << out + _ := cipher.xor_bytes(mut dst[nr_blocks * chacha20.block_size..], block, c.block) } - // copy ciphertext message results to the dst buffer - n := copy(mut dst, ciphertext) - assert n == src.len } // free the resources taken by the Cipher `c`. Dont use cipher after .free call @@ -147,16 +134,11 @@ pub fn (mut c Cipher) free() { // reset quickly sets all Cipher's fields to default value @[unsafe] pub fn (mut c Cipher) reset() { - for i, _ in c.key { - c.key[i] = u32(0) - } - for j, _ in c.nonce { - c.nonce[j] = u32(0) - } unsafe { + _ := vmemset(&c.key, 0, 32) + _ := vmemset(&c.nonce, 0, 12) c.block.reset() } - c.counter = u32(0) c.overflow = false c.precomp = false @@ -189,13 +171,13 @@ pub fn (mut c Cipher) set_counter(ctr u32) { } // rekey resets internal Cipher's state and reinitializes state with the provided key and nonce -@[unsafe] pub fn (mut c Cipher) rekey(key []u8, nonce []u8) ! { unsafe { c.reset() } c.do_rekey(key, nonce)! } // do_rekey reinitializes ChaCha20 instance with the provided key and nonce. +@[direct_array_access] fn (mut c Cipher) do_rekey(key []u8, nonce []u8) ! { // check for correctness of key and nonce length if key.len != chacha20.key_size { @@ -242,6 +224,7 @@ fn (mut c Cipher) do_rekey(key []u8, nonce []u8) ! { // chacha20_block transforms a ChaCha20 state by running // multiple quarter rounds. // see https://datatracker.ietf.org/doc/html/rfc8439#section-2.3 +@[direct_array_access] fn (mut c Cipher) chacha20_block() { // initializes ChaCha20 state // 0:cccccccc 1:cccccccc 2:cccccccc 3:cccccccc @@ -334,6 +317,7 @@ fn (mut c Cipher) chacha20_block() { } // generic_key_stream creates generic ChaCha20 keystream block and stores the result in Cipher.block +@[direct_array_access] fn (mut c Cipher) generic_key_stream() { // creates ChaCha20 block stream c.chacha20_block() diff --git a/vlib/x/crypto/chacha20/chacha_test.v b/vlib/x/crypto/chacha20/chacha_test.v index a5c689e4d6..3435aece10 100644 --- a/vlib/x/crypto/chacha20/chacha_test.v +++ b/vlib/x/crypto/chacha20/chacha_test.v @@ -1,7 +1,26 @@ module chacha20 +import rand import encoding.hex +fn test_chacha20_cipher_reset() ! { + mut key := []u8{len: 32} + mut nonce := []u8{len: 12} + rand.read(mut key) + rand.read(mut nonce) + + mut c := new_cipher(key, nonce)! + unsafe { c.reset() } + + for i, _ in c.key { + assert c.key[i] == u32(0) + } + + for i, _ in c.nonce { + assert c.nonce[i] == u32(0) + } +} + struct BlockCase { key string nonce string diff --git a/vlib/x/crypto/chacha20/xchacha.v b/vlib/x/crypto/chacha20/xchacha.v index 036042c128..496e1a97d6 100644 --- a/vlib/x/crypto/chacha20/xchacha.v +++ b/vlib/x/crypto/chacha20/xchacha.v @@ -12,6 +12,7 @@ const h_nonce_size = 16 // xchacha20 are intermediary step to build xchacha20 and initialized the same way as the ChaCha20 cipher, // except xchacha20 use a 128-bit (16 byte) nonce and has no counter to derive subkey // see https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha-03#section-2.2 +@[direct_array_access] fn xchacha20(key []u8, nonce []u8) ![]u8 { // early bound check if key.len != key_size {