chacha20_test.go raw
1 package chacha20
2
3 import (
4 "bytes"
5 "encoding/binary"
6 "encoding/hex"
7 "testing"
8 )
9
10 // rfcKey and rfcNonce are the inputs from RFC 8439, Section 2.4.2.
11 var (
12 rfcKey = [32]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f}
13 rfcNonce = [12]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00}
14 )
15
16 // TestBlockRFC8439 checks the block function against the state matrices and
17 // keystream published in RFC 8439, Section 2.4.2 (initial counter 1).
18 func TestBlockRFC8439(t *testing.T) {
19 wantWords := [2][16]uint32{
20 {
21 0xf3514f22, 0xe1d91b40, 0x6f27de2f, 0xed1d63b8,
22 0x821f138c, 0xe2062c3d, 0xecca4f7e, 0x78cff39e,
23 0xa30a3b8a, 0x920a6072, 0xcd7479b5, 0x34932bed,
24 0x40ba4c79, 0xcd343ec6, 0x4c2c21ea, 0xb7417df0,
25 },
26 {
27 0x9f74a669, 0x410f633f, 0x28feca22, 0x7ec44dec,
28 0x6d34d426, 0x738cb970, 0x3ac5e9f3, 0x45590cc4,
29 0xda6e8b39, 0x892c831a, 0xcdea67c1, 0x2b7e1d90,
30 0x037463f3, 0xa11a2073, 0xe8bcfb88, 0xedc49139,
31 },
32 }
33
34 for i, counter := range []uint32{1, 2} {
35 got := Block(&rfcKey, counter, &rfcNonce)
36 for j, want := range wantWords[i] {
37 if v := binary.LittleEndian.Uint32(got[4*j : 4*j+4]); v != want {
38 t.Fatalf("counter %d word %d = %08x, want %08x", counter, j, v, want)
39 }
40 }
41 }
42
43 const keystreamHex = "" +
44 "224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fcaec9ef3cf78" +
45 "8a3b0aa372600a92b57974cded2b9334794cba40c63e34cdea212c4cf07d41b7" +
46 "69a6749f3f630f4122cafe28ec4dc47e26d4346d70b98c73f3e9c53ac40c5945" +
47 "398b6eda1a832c89c167eacd901d7e2bf363"
48 wantKeystream, err := hex.DecodeString(keystreamHex)
49 if err != nil {
50 t.Fatal(err)
51 }
52 if len(wantKeystream) != 114 {
53 t.Fatalf("bad test vector length %d", len(wantKeystream))
54 }
55
56 // The RFC keystream spans two blocks starting at counter 1.
57 var got []byte
58 b1 := Block(&rfcKey, 1, &rfcNonce)
59 b2 := Block(&rfcKey, 2, &rfcNonce)
60 got = append(got, b1[:]...)
61 got = append(got, b2[:]...)
62 got = got[:len(wantKeystream)]
63 if !bytes.Equal(got, wantKeystream) {
64 t.Fatalf("keystream mismatch:\n got %x\nwant %x", got, wantKeystream)
65 }
66 }
67
68 // TestXORKeyStreamRFC8439 encrypts the sunscreen plaintext from RFC 8439,
69 // Section 2.4.2 and compares against the published ciphertext.
70 func TestXORKeyStreamRFC8439(t *testing.T) {
71 plaintext := []byte("Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.")
72 const ciphertextHex = "" +
73 "6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0b" +
74 "f91b65c5524733ab8f593dabcd62b3571639d624e65152ab8f530c359f0861d8" +
75 "07ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab7793736" +
76 "5af90bbf74a35be6b40b8eedf2785e42874d"
77 wantCiphertext, err := hex.DecodeString(ciphertextHex)
78 if err != nil {
79 t.Fatal(err)
80 }
81
82 // XORKeyStream always starts at counter 0, so prepend a block's worth of
83 // keystream: XORing the first 64 dummy bytes consumes counter 0, and the
84 // RFC plaintext is then encrypted with counters 1 and 2.
85 dummy := make([]byte, 64)
86 buf := append(append([]byte{}, dummy...), plaintext...)
87 XORKeyStream(&rfcKey, &rfcNonce, buf, buf)
88
89 got := buf[64:]
90 if !bytes.Equal(got, wantCiphertext) {
91 t.Fatalf("ciphertext mismatch:\n got %x\nwant %x", got, wantCiphertext)
92 }
93 }
94
95 // TestXORKeyStreamCounterStartsAtZero verifies that the first block uses
96 // counter 0 and that subsequent blocks increment the counter.
97 func TestXORKeyStreamCounterStartsAtZero(t *testing.T) {
98 var key [32]byte
99 var nonce [12]byte
100 for i := range key {
101 key[i] = byte(i)
102 }
103 for i := range nonce {
104 nonce[i] = byte(0xa0 + i)
105 }
106 src := make([]byte, 3*BlockSize)
107 for i := range src {
108 src[i] = byte(i)
109 }
110
111 dst := make([]byte, len(src))
112 XORKeyStream(&key, &nonce, dst, src)
113
114 want := make([]byte, len(src))
115 for b := 0; b < 3; b++ {
116 ks := Block(&key, uint32(b), &nonce)
117 for i := 0; i < BlockSize; i++ {
118 want[b*BlockSize+i] = src[b*BlockSize+i] ^ ks[i]
119 }
120 }
121 if !bytes.Equal(dst, want) {
122 t.Fatal("keystream is not the counter 0,1,2 sequence")
123 }
124 }
125
126 // TestXORKeyStreamAlias checks in-place operation and non-overlapping output.
127 func TestXORKeyStreamAlias(t *testing.T) {
128 var key [32]byte
129 var nonce [12]byte
130 for i := range key {
131 key[i] = byte(0x11 * i)
132 }
133 for i := range nonce {
134 nonce[i] = byte(0x30 + i)
135 }
136 plaintext := make([]byte, 201) // not a multiple of the block size
137 for i := range plaintext {
138 plaintext[i] = byte(7*i + 3)
139 }
140
141 // dst and src are the same slice.
142 aliased := append([]byte{}, plaintext...)
143 XORKeyStream(&key, &nonce, aliased, aliased)
144 if bytes.Equal(aliased, plaintext) {
145 t.Fatal("in-place XOR did not change the buffer")
146 }
147
148 // dst and src are distinct.
149 separate := make([]byte, len(plaintext))
150 XORKeyStream(&key, &nonce, separate, plaintext)
151 if !bytes.Equal(aliased, separate) {
152 t.Fatal("aliased and non-aliased results differ")
153 }
154 ciphertext := append([]byte{}, separate...)
155
156 // Round trip restores the plaintext in both cases.
157 XORKeyStream(&key, &nonce, aliased, aliased)
158 if !bytes.Equal(aliased, plaintext) {
159 t.Fatal("in-place round trip failed")
160 }
161 XORKeyStream(&key, &nonce, separate, separate)
162 if !bytes.Equal(separate, plaintext) {
163 t.Fatal("round trip failed")
164 }
165
166 // A dst longer than src leaves the tail untouched.
167 long := make([]byte, len(plaintext)+17)
168 for i := range long {
169 long[i] = 0xee
170 }
171 XORKeyStream(&key, &nonce, long, plaintext)
172 if !bytes.Equal(long[len(plaintext):], bytes.Repeat([]byte{0xee}, 17)) {
173 t.Fatal("wrote past len(src)")
174 }
175 if !bytes.Equal(long[:len(plaintext)], ciphertext) {
176 t.Fatal("short-copy mismatch")
177 }
178 }
179
180 // TestXORKeyStreamEmpty checks the zero-length case.
181 func TestXORKeyStreamEmpty(t *testing.T) {
182 var key [32]byte
183 var nonce [12]byte
184 XORKeyStream(&key, &nonce, nil, nil)
185 XORKeyStream(&key, &nonce, []byte{}, []byte{})
186 }
187
188 func TestXORKeyStreamShortDstPanics(t *testing.T) {
189 var key [32]byte
190 var nonce [12]byte
191 defer func() {
192 if recover() == nil {
193 t.Fatal("expected panic for short destination")
194 }
195 }()
196 XORKeyStream(&key, &nonce, make([]byte, 3), make([]byte, 4))
197 }
198