chacha20.go raw
1 package chacha20
2
3 // XOR encrypts/decrypts data using ChaCha20.
4 // key: 32 bytes, nonce: 12 bytes.
5 func XOR(key [32]byte, nonce [12]byte, data []byte) []byte {
6 out := make([]byte, len(data))
7 var state [16]uint32
8 // "expand 32-byte k"
9 state[0] = 0x61707865
10 state[1] = 0x3320646e
11 state[2] = 0x79622d32
12 state[3] = 0x6b206574
13 state[4] = le32(key[0:4])
14 state[5] = le32(key[4:8])
15 state[6] = le32(key[8:12])
16 state[7] = le32(key[12:16])
17 state[8] = le32(key[16:20])
18 state[9] = le32(key[20:24])
19 state[10] = le32(key[24:28])
20 state[11] = le32(key[28:32])
21 // counter = 0
22 state[12] = 0
23 state[13] = le32(nonce[0:4])
24 state[14] = le32(nonce[4:8])
25 state[15] = le32(nonce[8:12])
26
27 pos := 0
28 for pos < len(data) {
29 var block [64]byte
30 blockFn(&state, &block)
31 state[12]++ // increment counter
32 n := min(len(data)-pos, 64)
33 for i := range n {
34 out[pos+i] = data[pos+i] ^ block[i]
35 }
36 pos += n
37 }
38 return out
39 }
40
41 func blockFn(state *[16]uint32, out *[64]byte) {
42 var s [16]uint32
43 copy(s[:], state[:])
44
45 for range 10 {
46 // column rounds
47 qr(&s, 0, 4, 8, 12)
48 qr(&s, 1, 5, 9, 13)
49 qr(&s, 2, 6, 10, 14)
50 qr(&s, 3, 7, 11, 15)
51 // diagonal rounds
52 qr(&s, 0, 5, 10, 15)
53 qr(&s, 1, 6, 11, 12)
54 qr(&s, 2, 7, 8, 13)
55 qr(&s, 3, 4, 9, 14)
56 }
57
58 for i := range 16 {
59 v := s[i] + state[i]
60 out[i*4] = byte(v)
61 out[i*4+1] = byte(v >> 8)
62 out[i*4+2] = byte(v >> 16)
63 out[i*4+3] = byte(v >> 24)
64 }
65 }
66
67 func qr(s *[16]uint32, a, b, c, d int) {
68 s[a] += s[b]
69 s[d] ^= s[a]
70 s[d] = (s[d] << 16) | (s[d] >> 16)
71 s[c] += s[d]
72 s[b] ^= s[c]
73 s[b] = (s[b] << 12) | (s[b] >> 20)
74 s[a] += s[b]
75 s[d] ^= s[a]
76 s[d] = (s[d] << 8) | (s[d] >> 24)
77 s[c] += s[d]
78 s[b] ^= s[c]
79 s[b] = (s[b] << 7) | (s[b] >> 25)
80 }
81
82 func le32(b []byte) uint32 {
83 return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
84 }
85