| 1 | module chacha20 |
| 2 | |
| 3 | import rand |
| 4 | import encoding.hex |
| 5 | |
| 6 | // Test for Stream counter handling. |
| 7 | // See the discussion at [here](https://discord.com/channels/592103645835821068/592114487759470596/1417900997090607215) |
| 8 | fn 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 | |
| 36 | fn 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 | |
| 50 | fn 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 | |
| 69 | fn 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 | |
| 85 | struct BlockCase { |
| 86 | key string |
| 87 | nonce string |
| 88 | counter u32 |
| 89 | output string |
| 90 | } |
| 91 | |
| 92 | const 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 | |