x.crypto.chacha20: fix xor_key_stream failing after a while (fix #24043) (#24046)

This commit is contained in:
blackshirt 2025-03-26 04:31:10 +07:00 committed by GitHub
parent e968139439
commit f2b90bfa01
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 64 additions and 3 deletions

View File

@ -141,12 +141,14 @@ pub fn (mut c Cipher) xor_key_stream(mut dst []u8, src []u8) {
if dst.len < src.len {
panic('chacha20/chacha: dst buffer is to small')
}
if subtle.inexact_overlap(dst, src) {
panic('chacha20: invalid buffer overlap')
}
mut idx := 0
mut src_len := src.len
dst = unsafe { dst[..src_len] }
if subtle.inexact_overlap(dst, src) {
panic('chacha20: invalid buffer overlap')
}
// We adapt and ports the go version here
// First, drain any remaining key stream
@ -188,7 +190,9 @@ pub fn (mut c Cipher) xor_key_stream(mut dst []u8, src []u8) {
src_len -= full
// we dont support bufsize
// FIXME: instead generates once block keystream again, i think we can panic here
if u64(c.nonce[0]) + 1 > max_u32 {
c.block = []u8{len: block_size}
numblocks := (src_len + block_size - 1) / block_size
mut buf := c.block[block_size - numblocks * block_size..]
_ := copy(mut buf, src[idx..])
@ -200,6 +204,10 @@ pub fn (mut c Cipher) xor_key_stream(mut dst []u8, src []u8) {
// If we have a partial block, pad it for chacha20_block_generic, and
// keep the leftover keystream for the next invocation.
if src_len > 0 {
// Make sure, internal buffer cleared with the new one
// or the old garbaged data from previous call still there
// See https://github.com/vlang/v/issues/24043
c.block = []u8{len: block_size}
// copy the last src block to internal buffer, and performs
// chacha20_block_generic on this buffer, and stores into remaining dst
_ := copy(mut c.block, src[idx..])

View File

@ -3,6 +3,59 @@ module chacha20
import rand
import encoding.hex
const encoded_data = [
[u8(231), 121, 9, 28],
[u8(178), 221, 62, 9, 153, 189, 106, 12, 117, 47, 192, 81, 65, 112, 85, 57],
[u8(155), 202, 56, 16],
[u8(227), 47, 226, 137],
[u8(162), 77, 218, 52],
[u8(42), 250, 184, 196],
[u8(2), 129, 13, 136, 6, 12, 235, 183, 38, 178, 151, 243, 27, 88, 97, 40],
[u8(248), 170, 168, 206],
[u8(181), 220, 223, 139],
[u8(95), 108, 201, 227],
[u8(38), 221, 147, 230],
[u8(98), 229, 5, 130, 13, 103, 248, 159, 240, 246, 56, 119, 160, 130, 82, 222],
// additional data
'hello hello hello'.bytes(),
'me me me'.bytes(),
]
// expected data obtained with an equivalent Java program
const expected_data = [
[u8(0), 0, 0, 9],
[u8(0), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[u8(0), 0, 0, 1],
[u8(0), 0, 0, 0],
[u8(0), 0, 0, 0],
[u8(0), 0, 0, 9],
[u8(0), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[u8(0), 0, 0, 1],
[u8(0), 0, 0, 0],
[u8(0), 0, 0, 0],
[u8(0), 0, 0, 9],
[u8(0), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
// golang script produces this
[u8(119), 69, 96, 134, 100, 241, 107, 39, 71, 72, 65, 158, 50, 76, 187, 68, 100],
[u8(164), 169, 216, 98, 61, 175, 20, 175],
]
// See https://github.com/vlang/v/issues/24043
fn test_for_more_consecutive_xor_key_stream() {
key := [u8(225), 2, 1, 178, 238, 127, 187, 188, 27, 237, 18, 62, 181, 65, 67, 152, 13, 247,
147, 148, 101, 220, 185, 120, 234, 58, 144, 173, 3, 218, 193, 130]
nonce := [u8(153), 221, 244, 134, 99, 135, 243, 247, 169, 121, 69, 54]
mut cipher := new_cipher(key, nonce)!
for i := 0; i < encoded_data.len; i++ {
p := encoded_data[i]
e := expected_data[i]
mut dst := []u8{len: p.len}
cipher.xor_key_stream(mut dst, p)
assert dst == e
}
}
fn test_xor_key_stream_consecutive() {
// See https://github.com/vlang/v/issues/23977
key := [u8(64), 116, 63, 11, 221, 199, 187, 110, 217, 68, 0, 50, 65, 79, 24, 10, 124, 174,