v / vlib / x / crypto / chacha20 / stream_test.v
114 lines · 96 sloc · 3.67 KB · e0d20a0d9f5ecdd919cb3bc4a7d1da50cbc64f0c
Raw
1module chacha20
2
3import rand
4import encoding.hex
5
6// Test for Stream counter handling.
7// See the discussion at [here](https://discord.com/channels/592103645835821068/592114487759470596/1417900997090607215)
8fn test_stream_counter_handling() ! {
9 // creates a original mode of the cipher with 64-bit counter
10 mut ctx := new_cipher(rand.bytes(32)!, rand.bytes(8)!)!
11 // set the cipher's counter near the maximum of 64-bit counter
12 ctr := max_u64 - 2
13 ctx.set_counter(ctr)
14
15 // by setting internal counter into near of max 64-bit counter,
16 // it need a message with minimum length of 2*block_size bytes to reach the limit.
17 // let's build this message with 2 * block_size bytes in size
18 msg0 := []u8{len: 2 * block_size}
19 mut dst := []u8{len: msg0.len}
20 ctx.xor_key_stream(mut dst, msg0)
21 // at this step, the counter has reached the maximum_64bit_counter, but still not overflow
22 assert ctx.Stream.overflow == false
23 assert ctx.Stream.ctr() == max_64bit_counter
24
25 // after above process, the counter should reach the maximum limit
26 // we use keystream_full to test this counter handling, because
27 // xor_key_stream would panic on counter reset
28 msg1 := []u8{len: block_size}
29 ctx.Stream.keystream_full(mut dst[..block_size], msg1) or {
30 assert ctx.Stream.overflow == true
31 assert err == error('chacha20: internal counter overflow')
32 return
33 }
34}
35
36fn test_qround_on_state() {
37 mut s := State{}
38 s[0] = 0x11111111
39 s[1] = 0x01020304
40 s[2] = 0x9b8d6f43
41 s[3] = 0x01234567
42
43 qround_on_state(mut s, 0, 1, 2, 3)
44 assert s[0] == 0xea2a92f4
45 assert s[1] == 0xcb1cf8ce
46 assert s[2] == 0x4581472e
47 assert s[3] == 0x5881c4bb
48}
49
50fn test_state_of_chacha20_block_simple() ! {
51 key := '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'
52 key_bytes := hex.decode(key)!
53
54 nonce := '000000090000004a00000000'
55 nonce_bytes := hex.decode(nonce)!
56
57 mut stream := new_stream_with_options(key_bytes, nonce_bytes)!
58
59 mut block := []u8{len: block_size}
60 stream.set_ctr(1)
61 stream.keystream_full(mut block, block)!
62
63 expected_raw_bytes := '10f1e7e4d13b5915500fdd1fa32071c4c7d1f4c733c068030422aa9ac3d46c4ed2826446079faa0914c2d705d98b02a2b5129cd1de164eb9cbd083e8a2503c4e'
64 exp_bytes := hex.decode(expected_raw_bytes)!
65
66 assert block == exp_bytes
67}
68
69fn test_keystream_encryption() ! {
70 for val in blocks_testcases {
71 key := hex.decode(val.key)!
72 nonce := hex.decode(val.nonce)!
73
74 mut stream := new_stream_with_options(key, nonce)!
75 stream.set_ctr(val.counter)
76
77 mut block := []u8{len: block_size}
78 stream.keystream_full(mut block, block)!
79 exp_bytes := hex.decode(val.output)!
80
81 assert block == exp_bytes
82 }
83}
84
85struct BlockCase {
86 key string
87 nonce string
88 counter u32
89 output string
90}
91
92const blocks_testcases = [
93 // section 2.3.4 https://datatracker.ietf.org/doc/html/rfc8439#section-2.3.2
94 BlockCase{
95 key: '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'
96 nonce: '000000090000004a00000000'
97 counter: u32(1)
98 output: '10f1e7e4d13b5915500fdd1fa32071c4c7d1f4c733c068030422aa9ac3d46c4ed2826446079faa0914c2d705d98b02a2b5129cd1de164eb9cbd083e8a2503c4e'
99 },
100 // https://datatracker.ietf.org/doc/html/rfc8439#appendix-A.1.1
101 BlockCase{
102 key: '0000000000000000000000000000000000000000000000000000000000000000'
103 nonce: '000000000000000000000000'
104 counter: u32(0)
105 output: '76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586'
106 },
107 // #appendix-A.1.2
108 BlockCase{
109 key: '0000000000000000000000000000000000000000000000000000000000000000'
110 nonce: '000000000000000000000000'
111 counter: u32(1)
112 output: '9f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32ee7aed29b721769ce64e43d57133b074d839d531ed1f28510afb45ace10a1f4b794d6f'
113 },
114]
115