comparison dcrypt/crypto/ciphers/Salsa20.d @ 27:8b5eaf3c2979

Fixed error in hash message padding reported by Glenn Haecker.
author Thomas Dixon <reikon@reikon.us>
date Sat, 09 May 2009 23:29:20 -0400
parents 176c933827a8
children ad687db713a4
comparison
equal deleted inserted replaced
26:176c933827a8 27:8b5eaf3c2979
12 import dcrypt.crypto.params.ParametersWithIV; 12 import dcrypt.crypto.params.ParametersWithIV;
13 import dcrypt.misc.ByteConverter; 13 import dcrypt.misc.ByteConverter;
14 import dcrypt.misc.Bitwise; 14 import dcrypt.misc.Bitwise;
15 15
16 /** Implementation of Salsa20 designed by Daniel J. Bernstein. */ 16 /** Implementation of Salsa20 designed by Daniel J. Bernstein. */
17 class Salsa20 : StreamCipher { 17 class Salsa20 : StreamCipher
18 protected { 18 {
19 protected
20 {
19 // Constants 21 // Constants
20 const ubyte[] sigma = cast(ubyte[])"expand 32-byte k", 22 const ubyte[] sigma = cast(ubyte[])"expand 32-byte k",
21 tau = cast(ubyte[])"expand 16-byte k"; 23 tau = cast(ubyte[])"expand 16-byte k";
22 24
23 // Counter indexes (added for ChaCha) 25 // Counter indexes (added for ChaCha)
33 // Internal copies of the key and IV for resetting the cipher 35 // Internal copies of the key and IV for resetting the cipher
34 ubyte[] workingKey, 36 ubyte[] workingKey,
35 workingIV; 37 workingIV;
36 } 38 }
37 39
38 this() { 40 this()
41 {
39 state = new uint[16]; 42 state = new uint[16];
40 43
41 // State expanded into bytes 44 // State expanded into bytes
42 keyStream = new ubyte[64]; 45 keyStream = new ubyte[64];
43 46
44 i0 = 8; 47 i0 = 8;
45 i1 = 9; 48 i1 = 9;
46 } 49 }
47 50
48 void init(bool encrypt, CipherParameters params) { 51 void init(bool encrypt, CipherParameters params)
52 {
49 ParametersWithIV ivParams = cast(ParametersWithIV)params; 53 ParametersWithIV ivParams = cast(ParametersWithIV)params;
50 54
51 if (!ivParams) 55 if (!ivParams)
52 throw new InvalidParameterError( 56 throw new InvalidParameterError(
53 name()~": init parameters must include an IV. (use ParametersWithIV)"); 57 name()~": init parameters must include an IV. (use ParametersWithIV)");
55 SymmetricKey keyParams = cast(SymmetricKey)ivParams.parameters; 59 SymmetricKey keyParams = cast(SymmetricKey)ivParams.parameters;
56 60
57 ubyte[] iv = ivParams.iv, 61 ubyte[] iv = ivParams.iv,
58 key = keyParams.key; 62 key = keyParams.key;
59 63
60 if (key) { 64 if (key)
65 {
61 if (key.length != 16 && key.length != 32) 66 if (key.length != 16 && key.length != 32)
62 throw new InvalidKeyError( 67 throw new InvalidKeyError(
63 name()~": Invalid key length. (requires 16 or 32 bytes)"); 68 name()~": Invalid key length. (requires 16 or 32 bytes)");
64 69
65 workingKey = key; 70 workingKey = key;
78 ivSetup(); 83 ivSetup();
79 84
80 _encrypt = _initialized = true; 85 _encrypt = _initialized = true;
81 } 86 }
82 87
83 char[] name() { 88 char[] name()
89 {
84 return "Salsa20"; 90 return "Salsa20";
85 } 91 }
86 92
87 ubyte returnByte(ubyte input) { 93 ubyte returnByte(ubyte input)
94 {
88 if (!_initialized) 95 if (!_initialized)
89 throw new NotInitializedError(name()~": Cipher not initialized"); 96 throw new NotInitializedError(name()~": Cipher not initialized");
90 97
91 if (index == 0) { 98 if (index == 0) {
92 salsa20WordToByte(state, keyStream); 99 salsa20WordToByte(state, keyStream);
101 index = (index + 1) & 0x3f; 108 index = (index + 1) & 0x3f;
102 109
103 return result; 110 return result;
104 } 111 }
105 112
106 uint update(void[] input_, void[] output_) { 113 uint update(void[] input_, void[] output_)
114 {
107 if (!_initialized) 115 if (!_initialized)
108 throw new NotInitializedError(name()~": Cipher not initialized"); 116 throw new NotInitializedError(name()~": Cipher not initialized");
109 117
110 ubyte[] input = cast(ubyte[]) input_, 118 ubyte[] input = cast(ubyte[]) input_,
111 output = cast(ubyte[]) output_; 119 output = cast(ubyte[]) output_;
112 120
113 if (input.length > output.length) 121 if (input.length > output.length)
114 throw new ShortBufferError(name()~": Output buffer too short"); 122 throw new ShortBufferError(name()~": Output buffer too short");
115 123
116 for (int i = 0; i < input.length; i++) { 124 for (int i = 0; i < input.length; i++)
117 if (index == 0) { 125 {
126 if (index == 0)
127 {
118 salsa20WordToByte(state, keyStream); 128 salsa20WordToByte(state, keyStream);
119 state[i0]++; 129 state[i0]++;
120 if (!state[i0]) 130 if (!state[i0])
121 state[i1]++; 131 state[i1]++;
122 // As in djb's, changing the IV after 2^70 bytes is the user's responsibility 132 // As in djb's, changing the IV after 2^70 bytes is the user's responsibility
127 } 137 }
128 138
129 return input.length; 139 return input.length;
130 } 140 }
131 141
132 void reset() { 142 void reset()
143 {
133 keySetup(); 144 keySetup();
134 ivSetup(); 145 ivSetup();
135 index = 0; 146 index = 0;
136 } 147 }
137 148
138 protected void keySetup() { 149 protected void keySetup()
150 {
139 uint offset; 151 uint offset;
140 ubyte[] constants; 152 ubyte[] constants;
141 153
142 state[1] = ByteConverter.LittleEndian.to!(uint)(workingKey[0..4]); 154 state[1] = ByteConverter.LittleEndian.to!(uint)(workingKey[0..4]);
143 state[2] = ByteConverter.LittleEndian.to!(uint)(workingKey[4..8]); 155 state[2] = ByteConverter.LittleEndian.to!(uint)(workingKey[4..8]);
144 state[3] = ByteConverter.LittleEndian.to!(uint)(workingKey[8..12]); 156 state[3] = ByteConverter.LittleEndian.to!(uint)(workingKey[8..12]);
145 state[4] = ByteConverter.LittleEndian.to!(uint)(workingKey[12..16]); 157 state[4] = ByteConverter.LittleEndian.to!(uint)(workingKey[12..16]);
146 158
147 if (workingKey.length == 32) { 159 if (workingKey.length == 32)
160 {
148 constants = sigma; 161 constants = sigma;
149 offset = 16; 162 offset = 16;
150 } else 163 } else
151 constants = tau; 164 constants = tau;
152 165
158 state[ 5] = ByteConverter.LittleEndian.to!(uint)(constants[4..8]); 171 state[ 5] = ByteConverter.LittleEndian.to!(uint)(constants[4..8]);
159 state[10] = ByteConverter.LittleEndian.to!(uint)(constants[8..12]); 172 state[10] = ByteConverter.LittleEndian.to!(uint)(constants[8..12]);
160 state[15] = ByteConverter.LittleEndian.to!(uint)(constants[12..16]); 173 state[15] = ByteConverter.LittleEndian.to!(uint)(constants[12..16]);
161 } 174 }
162 175
163 protected void ivSetup() { 176 protected void ivSetup()
177 {
164 state[6] = ByteConverter.LittleEndian.to!(uint)(workingIV[0..4]); 178 state[6] = ByteConverter.LittleEndian.to!(uint)(workingIV[0..4]);
165 state[7] = ByteConverter.LittleEndian.to!(uint)(workingIV[4..8]); 179 state[7] = ByteConverter.LittleEndian.to!(uint)(workingIV[4..8]);
166 state[8] = state[9] = 0; 180 state[8] = state[9] = 0;
167 } 181 }
168 182
169 protected void salsa20WordToByte(uint[] input, ref ubyte[] output) { 183 protected void salsa20WordToByte(uint[] input, ref ubyte[] output)
184 {
170 uint[] x = new uint[16]; 185 uint[] x = new uint[16];
171 x[] = input; 186 x[] = input;
172 187
173 int i; 188 int i;
174 for (i = 0; i < 10; i++) { 189 for (i = 0; i < 10; i++)
190 {
175 x[ 4] ^= Bitwise.rotateLeft(x[ 0]+x[12], 7); 191 x[ 4] ^= Bitwise.rotateLeft(x[ 0]+x[12], 7);
176 x[ 8] ^= Bitwise.rotateLeft(x[ 4]+x[ 0], 9); 192 x[ 8] ^= Bitwise.rotateLeft(x[ 4]+x[ 0], 9);
177 x[12] ^= Bitwise.rotateLeft(x[ 8]+x[ 4], 13); 193 x[12] ^= Bitwise.rotateLeft(x[ 8]+x[ 4], 13);
178 x[ 0] ^= Bitwise.rotateLeft(x[12]+x[ 8], 18); 194 x[ 0] ^= Bitwise.rotateLeft(x[12]+x[ 8], 18);
179 x[ 9] ^= Bitwise.rotateLeft(x[ 5]+x[ 1], 7); 195 x[ 9] ^= Bitwise.rotateLeft(x[ 5]+x[ 1], 7);
213 for (i = j = 0; i < x.length; i++,j+=int.sizeof) 229 for (i = j = 0; i < x.length; i++,j+=int.sizeof)
214 output[j..j+int.sizeof] = ByteConverter.LittleEndian.from!(uint)(x[i]); 230 output[j..j+int.sizeof] = ByteConverter.LittleEndian.from!(uint)(x[i]);
215 } 231 }
216 232
217 /** Salsa20 test vectors */ 233 /** Salsa20 test vectors */
218 debug (UnitTest) { 234 debug (UnitTest)
219 unittest { 235 {
236 unittest
237 {
220 static const char[][] test_keys = [ 238 static const char[][] test_keys = [
221 "80000000000000000000000000000000", 239 "80000000000000000000000000000000",
222 "0053a6f94c9ff24598eb3e91e4378add", 240 "0053a6f94c9ff24598eb3e91e4378add",
223 "00002000000000000000000000000000"~ 241 "00002000000000000000000000000000"~
224 "00000000000000000000000000000000", 242 "00000000000000000000000000000000",
281 ]; 299 ];
282 300
283 Salsa20 s20 = new Salsa20(); 301 Salsa20 s20 = new Salsa20();
284 ubyte[] buffer = new ubyte[64]; 302 ubyte[] buffer = new ubyte[64];
285 char[] result; 303 char[] result;
286 for (int i = 0; i < test_keys.length; i++) { 304 for (int i = 0; i < test_keys.length; i++)
305 {
287 SymmetricKey key = new SymmetricKey(ByteConverter.hexDecode(test_keys[i])); 306 SymmetricKey key = new SymmetricKey(ByteConverter.hexDecode(test_keys[i]));
288 ParametersWithIV params = new ParametersWithIV(key, ByteConverter.hexDecode(test_ivs[i])); 307 ParametersWithIV params = new ParametersWithIV(key, ByteConverter.hexDecode(test_ivs[i]));
289 308
290 // Encryption 309 // Encryption
291 s20.init(true, params); 310 s20.init(true, params);