1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// ---------------- [ File: bitcoin-chacha/src/chacha20_keystream.rs ]
crate::ix!();
impl ChaCha20 {
/**
| outputs the keystream of size `bytes` into `c`
*/
pub fn keystream(&mut self, mut c: *mut u8, mut bytes: usize) {
// copy the current input (j*) so that we can keep track of the counter
let mut state = self.input().clone();
let mut tmp = [0u8; 64];
unsafe {
while bytes != 0 {
// decide where this 64‑byte chunk will be written
let (out_ptr, ctarget) = if bytes < 64 {
(tmp.as_mut_ptr(), Some(c))
} else {
(c, None)
};
// working variables
let mut x = state;
// 20 ChaCha rounds (10 × double‑round)
for _ in 0..10 {
quarterround!(x[0], x[4], x[8], x[12]);
quarterround!(x[1], x[5], x[9], x[13]);
quarterround!(x[2], x[6], x[10], x[14]);
quarterround!(x[3], x[7], x[11], x[15]);
quarterround!(x[0], x[5], x[10], x[15]);
quarterround!(x[1], x[6], x[11], x[12]);
quarterround!(x[2], x[7], x[8], x[13]);
quarterround!(x[3], x[4], x[9], x[14]);
}
// add the original state
for i in 0..16 {
x[i] = x[i].wrapping_add(state[i]);
}
// serialize little‑endian words
for (i, chunk) in (0..64).step_by(4).enumerate() {
write_le32(
core::slice::from_raw_parts_mut(out_ptr.add(chunk), 4),
x[i].into(),
);
}
// increment 64‑byte counter
state[12] = state[12].wrapping_add(1);
if state[12] == 0 {
state[13] = state[13].wrapping_add(1);
}
// copy partial block if necessary
if let Some(dst) = ctarget {
core::ptr::copy_nonoverlapping(tmp.as_ptr(), dst, bytes);
self.input_mut()[12] = state[12];
self.input_mut()[13] = state[13];
return;
}
bytes -= 64;
c = c.add(64);
}
}
self.input_mut()[12] = state[12];
self.input_mut()[13] = state[13];
}
}
#[cfg(test)]
mod keystream_exhaustive_small_block {
use super::*;
#[traced_test]
fn generates_partial_block() {
let mut c = ChaCha20::new([0u8; 32].as_ptr(), 32);
c.setiv(0);
let mut buf = [0u8; 7]; // less than 64
c.keystream(buf.as_mut_ptr(), buf.len());
// second call should continue (not repeat) stream
let mut next7 = [0u8; 7];
c.keystream(next7.as_mut_ptr(), next7.len());
assert_ne!(buf, next7, "stream must advance across calls");
}
}