Mercurial > projects > dcrypt
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); |