1 /**
2 * ChaCha20
3 * 
4 * Copyright:
5 * (C) 2014 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.stream.chacha;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_CHACHA):
15 
16 import botan.stream.stream_cipher;
17 import botan.utils.loadstor;
18 import botan.utils.rotate;
19 import botan.utils.xor_buf;
20 import botan.utils.types;
21 import botan.utils.mem_ops;
22 import std.format : format;
23 
24 /**
25 * DJB's ChaCha (http://cr.yp.to/chacha.html)
26 */
27 final class ChaCha : StreamCipher, SymmetricAlgorithm
28 {
29 public:
30     /*
31     * Combine cipher stream with message
32     */
33     override void cipher(const(ubyte)* input, ubyte* output, size_t length)
34     {
35         while (length >= m_buffer.length - m_position)
36         {
37             xorBuf(output, input, m_buffer.ptr + m_position, m_buffer.length - m_position);
38             length -= (m_buffer.length - m_position);
39             input += (m_buffer.length - m_position);
40             output += (m_buffer.length - m_position);
41             chacha(*cast(ubyte[64]*) m_buffer.ptr, *cast(uint[16]*) m_state.ptr);
42             
43             ++m_state[12];
44             m_state[13] += (m_state[12] == 0);
45             
46             m_position = 0;
47         }
48         
49         xorBuf(output, input, &m_buffer[m_position], length);
50         
51         m_position += length;
52     }
53 
54     /*
55     * Return the name of this type
56     */
57     override void setIv(const(ubyte)* iv, size_t length)
58     {
59         if (!validIvLength(length))
60             throw new InvalidIVLength(name, length);
61         
62         m_state[12] = 0;
63 
64         m_state[13] = 0;
65         
66 		if (length == 8) {
67 	        m_state[14] = loadLittleEndian!uint(iv, 0);
68 	        m_state[15] = loadLittleEndian!uint(iv, 1);
69 		} else if (length == 12) {
70 			m_state[13] = loadLittleEndian!uint(iv, 0);
71 			m_state[14] = loadLittleEndian!uint(iv, 1);
72 			m_state[15] = loadLittleEndian!uint(iv, 2);
73 		}
74         
75         chacha(*cast(ubyte[64]*) m_buffer.ptr, *cast(uint[16]*) m_state.ptr);
76         ++m_state[12];
77         m_state[13] += (m_state[12] == 0);
78         
79         m_position = 0;
80     }
81 
82     override bool validIvLength(size_t iv_len) const
83     { return (iv_len == 8 || iv_len == 12); }
84 
85     KeyLengthSpecification keySpec() const
86     {
87         return KeyLengthSpecification(16, 32, 16);
88     }
89 
90     /*
91     * Clear memory of sensitive data
92     */
93     void clear()
94     {
95         zap(m_state);
96         zap(m_buffer);
97         m_position = 0;
98     }
99 
100     /*
101     * Return the name of this type
102     */
103     @property string name() const
104     {
105         return "ChaCha";
106     }
107 
108     override StreamCipher clone() const { return new ChaCha; }
109 protected:
110 
111     void chacha(ref ubyte[64] output, in uint[16] input)
112     {
113         uint x00 = input[ 0], x01 = input[ 1], x02 = input[ 2], x03 = input[ 3],
114             x04 = input[ 4], x05 = input[ 5], x06 = input[ 6], x07 = input[ 7],
115             x08 = input[ 8], x09 = input[ 9], x10 = input[10], x11 = input[11],
116             x12 = input[12], x13 = input[13], x14 = input[14], x15 = input[15];
117         
118         
119         foreach (size_t i; 0 .. 10)
120         {
121             mixin(CHACHA_QUARTER_ROUND!(x00, x04, x08, x12) ~
122                   CHACHA_QUARTER_ROUND!(x01, x05, x09, x13) ~
123                   CHACHA_QUARTER_ROUND!(x02, x06, x10, x14) ~
124                   CHACHA_QUARTER_ROUND!(x03, x07, x11, x15) ~
125                   
126                   CHACHA_QUARTER_ROUND!(x00, x05, x10, x15) ~
127                   CHACHA_QUARTER_ROUND!(x01, x06, x11, x12) ~
128                   CHACHA_QUARTER_ROUND!(x02, x07, x08, x13) ~
129                   CHACHA_QUARTER_ROUND!(x03, x04, x09, x14)
130                   );
131         }
132         
133         storeLittleEndian(x00 + input[ 0], output.ptr + 4 *  0);
134         storeLittleEndian(x01 + input[ 1], output.ptr + 4 *  1);
135         storeLittleEndian(x02 + input[ 2], output.ptr + 4 *  2);
136         storeLittleEndian(x03 + input[ 3], output.ptr + 4 *  3);
137         storeLittleEndian(x04 + input[ 4], output.ptr + 4 *  4);
138         storeLittleEndian(x05 + input[ 5], output.ptr + 4 *  5);
139         storeLittleEndian(x06 + input[ 6], output.ptr + 4 *  6);
140         storeLittleEndian(x07 + input[ 7], output.ptr + 4 *  7);
141         storeLittleEndian(x08 + input[ 8], output.ptr + 4 *  8);
142         storeLittleEndian(x09 + input[ 9], output.ptr + 4 *  9);
143         storeLittleEndian(x10 + input[10], output.ptr + 4 * 10);
144         storeLittleEndian(x11 + input[11], output.ptr + 4 * 11);
145         storeLittleEndian(x12 + input[12], output.ptr + 4 * 12);
146         storeLittleEndian(x13 + input[13], output.ptr + 4 * 13);
147         storeLittleEndian(x14 + input[14], output.ptr + 4 * 14);
148         storeLittleEndian(x15 + input[15], output.ptr + 4 * 15);
149     }
150 
151 protected:
152     /*
153     * ChaCha Key Schedule
154     */
155     override void keySchedule(const(ubyte)* key, size_t length)
156     {
157         __gshared immutable uint[] TAU =    [ 0x61707865, 0x3120646e, 0x79622d36, 0x6b206574 ];
158         
159         __gshared immutable uint[] SIGMA = [ 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 ];
160         
161         const uint[] CONSTANTS = (length == 16) ? TAU : SIGMA;
162         
163         m_state.resize(16);
164         m_buffer.resize(64);
165         
166         m_state[0] = CONSTANTS[0];
167         m_state[1] = CONSTANTS[1];
168         m_state[2] = CONSTANTS[2];
169         m_state[3] = CONSTANTS[3];
170         
171         m_state[4] = loadLittleEndian!uint(key, 0);
172         m_state[5] = loadLittleEndian!uint(key, 1);
173         m_state[6] = loadLittleEndian!uint(key, 2);
174         m_state[7] = loadLittleEndian!uint(key, 3);
175         
176         if (length == 32)
177             key += 16;
178         
179         m_state[8] = loadLittleEndian!uint(key, 0);
180         m_state[9] = loadLittleEndian!uint(key, 1);
181         m_state[10] = loadLittleEndian!uint(key, 2);
182         m_state[11] = loadLittleEndian!uint(key, 3);
183         
184         m_position = 0;
185         
186         const ubyte[8] ZERO;
187         setIv(ZERO.ptr, ZERO.length);
188     }
189 
190 
191     SecureVector!uint m_state;
192     SecureVector!ubyte m_buffer;
193     size_t m_position = 0;
194 }
195 
196 enum string CHACHA_QUARTER_ROUND(alias _a, alias _b, alias _c, alias _d) = q{
197     %1$s += %2$s; %4$s ^= %1$s; %4$s = rotateLeft(%4$s, 16);
198     %3$s += %4$s; %2$s ^= %3$s; %2$s = rotateLeft(%2$s, 12);
199     %1$s += %2$s; %4$s ^= %1$s; %4$s = rotateLeft(%4$s, 8);
200     %3$s += %4$s; %2$s ^= %3$s; %2$s = rotateLeft(%2$s, 7);
201 }.format(__traits(identifier, _a), __traits(identifier, _b), __traits(identifier, _c), __traits(identifier, _d));