1 /** 2 * Copyright: Copyright (C) Thomas Dixon 2009. All rights reserved. 3 * License: BSD style: $(LICENSE) 4 * Authors: Thomas Dixon 5 */ 6 7 module tango.util.cipher.Salsa20; 8 9 private import tango.util.cipher.Cipher; 10 11 /** Implementation of Salsa20 designed by Daniel J. Bernstein. */ 12 class Salsa20 : StreamCipher 13 { 14 protected 15 { 16 // Constants 17 __gshared immutable immutable(ubyte)[] sigma = cast(immutable(ubyte)[])"expand 32-byte k"; 18 __gshared immutable immutable(ubyte)[] tau = cast(immutable(ubyte)[])"expand 16-byte k"; 19 20 // Counter indexes (added for ChaCha) 21 uint i0, i1; 22 23 // Internal state 24 uint[] state; 25 26 // Keystream and index marker 27 ubyte[] keyStream; 28 uint index; 29 30 // Internal copies of the key and IV for resetting the cipher 31 const(ubyte)[] workingKey, 32 workingIV; 33 } 34 35 this() 36 { 37 state = new uint[16]; 38 39 // State expanded into bytes 40 keyStream = new ubyte[64]; 41 42 i0 = 8; 43 i1 = 9; 44 } 45 46 this(bool encrypt, ubyte[] key, ubyte[] iv) { 47 this(); 48 init(encrypt, key, iv); 49 } 50 51 void init(bool encrypt, ubyte[] key, ubyte[] iv) 52 { 53 if (key) 54 { 55 if (key.length != 16 && key.length != 32) 56 invalid(name()~": Invalid key length. (requires 16 or 32 bytes)"); 57 58 workingKey = key; 59 keySetup(); 60 61 index = 0; 62 } 63 64 if (!workingKey) 65 invalid(name()~": Key not set."); 66 67 if (!iv || iv.length != 8) 68 invalid(name()~": 8 byte IV required."); 69 70 workingIV = iv; 71 ivSetup(); 72 73 _encrypt = _initialized = true; 74 } 75 76 @property override const(char[]) name() 77 { 78 return "Salsa20"; 79 } 80 81 override ubyte returnByte(ubyte input) 82 { 83 if (!_initialized) 84 invalid (name()~": Cipher not initialized"); 85 86 if (index == 0) { 87 salsa20WordToByte(state, keyStream); 88 state[i0]++; 89 if (!state[i0]) 90 state[i1]++; 91 // As in djb's, changing the IV after 2^70 bytes is the user's responsibility 92 // lol glwt 93 } 94 95 ubyte result = (keyStream[index]^input); 96 index = (index + 1) & 0x3f; 97 98 return result; 99 } 100 101 override uint update(const(void[]) input_, void[] output_) 102 { 103 if (!_initialized) 104 invalid(name()~": Cipher not initialized"); 105 106 const(ubyte[]) input = cast(const(ubyte[])) input_; 107 ubyte[] output = cast(ubyte[]) output_; 108 109 if (input.length > output.length) 110 invalid(name()~": Output buffer too short"); 111 112 for (int i = 0; i < input.length; i++) 113 { 114 if (index == 0) 115 { 116 salsa20WordToByte(state, keyStream); 117 state[i0]++; 118 if (!state[i0]) 119 state[i1]++; 120 // As in djb's, changing the IV after 2^70 bytes is the user's responsibility 121 // lol glwt 122 } 123 output[i] = (keyStream[index]^input[i]); 124 index = (index + 1) & 0x3f; 125 } 126 127 return cast(uint)input.length; 128 } 129 130 override void reset() 131 { 132 keySetup(); 133 ivSetup(); 134 index = 0; 135 } 136 137 protected void keySetup() 138 { 139 uint offset; 140 const(ubyte)[] constants; 141 142 state[1] = ByteConverter.LittleEndian.to!(uint)(workingKey[0..4]); 143 state[2] = ByteConverter.LittleEndian.to!(uint)(workingKey[4..8]); 144 state[3] = ByteConverter.LittleEndian.to!(uint)(workingKey[8..12]); 145 state[4] = ByteConverter.LittleEndian.to!(uint)(workingKey[12..16]); 146 147 if (workingKey.length == 32) 148 { 149 constants = sigma; 150 offset = 16; 151 } else 152 constants = tau; 153 154 state[11] = ByteConverter.LittleEndian.to!(uint)(workingKey[offset..offset+4]); 155 state[12] = ByteConverter.LittleEndian.to!(uint)(workingKey[offset+4..offset+8]); 156 state[13] = ByteConverter.LittleEndian.to!(uint)(workingKey[offset+8..offset+12]); 157 state[14] = ByteConverter.LittleEndian.to!(uint)(workingKey[offset+12..offset+16]); 158 state[ 0] = ByteConverter.LittleEndian.to!(uint)(constants[0..4]); 159 state[ 5] = ByteConverter.LittleEndian.to!(uint)(constants[4..8]); 160 state[10] = ByteConverter.LittleEndian.to!(uint)(constants[8..12]); 161 state[15] = ByteConverter.LittleEndian.to!(uint)(constants[12..16]); 162 } 163 164 protected void ivSetup() 165 { 166 state[6] = ByteConverter.LittleEndian.to!(uint)(workingIV[0..4]); 167 state[7] = ByteConverter.LittleEndian.to!(uint)(workingIV[4..8]); 168 state[8] = state[9] = 0; 169 } 170 171 protected void salsa20WordToByte(const(uint[]) input, ref ubyte[] output) 172 { 173 uint[] x = new uint[16]; 174 x[] = input[0..16]; 175 176 int i; 177 for (i = 0; i < 10; i++) 178 { 179 x[ 4] ^= Bitwise.rotateLeft(x[ 0]+x[12], 7u); 180 x[ 8] ^= Bitwise.rotateLeft(x[ 4]+x[ 0], 9u); 181 x[12] ^= Bitwise.rotateLeft(x[ 8]+x[ 4], 13u); 182 x[ 0] ^= Bitwise.rotateLeft(x[12]+x[ 8], 18u); 183 x[ 9] ^= Bitwise.rotateLeft(x[ 5]+x[ 1], 7u); 184 x[13] ^= Bitwise.rotateLeft(x[ 9]+x[ 5], 9u); 185 x[ 1] ^= Bitwise.rotateLeft(x[13]+x[ 9], 13u); 186 x[ 5] ^= Bitwise.rotateLeft(x[ 1]+x[13], 18u); 187 x[14] ^= Bitwise.rotateLeft(x[10]+x[ 6], 7u); 188 x[ 2] ^= Bitwise.rotateLeft(x[14]+x[10], 9u); 189 x[ 6] ^= Bitwise.rotateLeft(x[ 2]+x[14], 13u); 190 x[10] ^= Bitwise.rotateLeft(x[ 6]+x[ 2], 18u); 191 x[ 3] ^= Bitwise.rotateLeft(x[15]+x[11], 7u); 192 x[ 7] ^= Bitwise.rotateLeft(x[ 3]+x[15], 9u); 193 x[11] ^= Bitwise.rotateLeft(x[ 7]+x[ 3], 13u); 194 x[15] ^= Bitwise.rotateLeft(x[11]+x[ 7], 18u); 195 x[ 1] ^= Bitwise.rotateLeft(x[ 0]+x[ 3], 7u); 196 x[ 2] ^= Bitwise.rotateLeft(x[ 1]+x[ 0], 9u); 197 x[ 3] ^= Bitwise.rotateLeft(x[ 2]+x[ 1], 13u); 198 x[ 0] ^= Bitwise.rotateLeft(x[ 3]+x[ 2], 18u); 199 x[ 6] ^= Bitwise.rotateLeft(x[ 5]+x[ 4], 7u); 200 x[ 7] ^= Bitwise.rotateLeft(x[ 6]+x[ 5], 9u); 201 x[ 4] ^= Bitwise.rotateLeft(x[ 7]+x[ 6], 13u); 202 x[ 5] ^= Bitwise.rotateLeft(x[ 4]+x[ 7], 18u); 203 x[11] ^= Bitwise.rotateLeft(x[10]+x[ 9], 7u); 204 x[ 8] ^= Bitwise.rotateLeft(x[11]+x[10], 9u); 205 x[ 9] ^= Bitwise.rotateLeft(x[ 8]+x[11], 13u); 206 x[10] ^= Bitwise.rotateLeft(x[ 9]+x[ 8], 18u); 207 x[12] ^= Bitwise.rotateLeft(x[15]+x[14], 7u); 208 x[13] ^= Bitwise.rotateLeft(x[12]+x[15], 9u); 209 x[14] ^= Bitwise.rotateLeft(x[13]+x[12], 13u); 210 x[15] ^= Bitwise.rotateLeft(x[14]+x[13], 18u); 211 } 212 213 for (i = 0; i < 16; i++) 214 x[i] += input[i]; 215 216 int j; 217 for (i = j = 0; i < x.length; i++,j+=int.sizeof) 218 ByteConverter.LittleEndian.from!(uint)(x[i], output[j..j+int.sizeof]); 219 } 220 221 /** Salsa20 test vectors */ 222 debug (UnitTest) 223 { 224 unittest 225 { 226 __gshared immutable immutable(char)[][] test_keys = [ 227 "80000000000000000000000000000000", 228 "0053a6f94c9ff24598eb3e91e4378add", 229 "00002000000000000000000000000000"~ 230 "00000000000000000000000000000000", 231 "0f62b5085bae0154a7fa4da0f34699ec"~ 232 "3f92e5388bde3184d72a7dd02376c91c" 233 234 ]; 235 236 __gshared immutable immutable(char)[][] test_ivs = [ 237 "0000000000000000", 238 "0d74db42a91077de", 239 "0000000000000000", 240 "288ff65dc42b92f9" 241 ]; 242 243 __gshared immutable immutable(char)[][] test_plaintexts = [ 244 "00000000000000000000000000000000"~ 245 "00000000000000000000000000000000"~ 246 "00000000000000000000000000000000"~ 247 "00000000000000000000000000000000", 248 249 "00000000000000000000000000000000"~ 250 "00000000000000000000000000000000"~ 251 "00000000000000000000000000000000"~ 252 "00000000000000000000000000000000", 253 254 "00000000000000000000000000000000"~ 255 "00000000000000000000000000000000"~ 256 "00000000000000000000000000000000"~ 257 "00000000000000000000000000000000", 258 259 "00000000000000000000000000000000"~ 260 "00000000000000000000000000000000"~ 261 "00000000000000000000000000000000"~ 262 "00000000000000000000000000000000" 263 264 265 ]; 266 267 __gshared immutable immutable(char)[][] test_ciphertexts = [ 268 "4dfa5e481da23ea09a31022050859936"~ // Expected output 269 "da52fcee218005164f267cb65f5cfd7f"~ 270 "2b4f97e0ff16924a52df269515110a07"~ 271 "f9e460bc65ef95da58f740b7d1dbb0aa", 272 273 "05e1e7beb697d999656bf37c1b978806"~ 274 "735d0b903a6007bd329927efbe1b0e2a"~ 275 "8137c1ae291493aa83a821755bee0b06"~ 276 "cd14855a67e46703ebf8f3114b584cba", 277 278 "c29ba0da9ebebfacdebbdd1d16e5f598"~ 279 "7e1cb12e9083d437eaaaa4ba0cdc909e"~ 280 "53d052ac387d86acda8d956ba9e6f654"~ 281 "3065f6912a7df710b4b57f27809bafe3", 282 283 "5e5e71f90199340304abb22a37b6625b"~ 284 "f883fb89ce3b21f54a10b81066ef87da"~ 285 "30b77699aa7379da595c77dd59542da2"~ 286 "08e5954f89e40eb7aa80a84a6176663f" 287 ]; 288 289 Salsa20 s20 = new Salsa20(); 290 ubyte[] buffer = new ubyte[64]; 291 char[] result; 292 for (int i = 0; i < test_keys.length; i++) 293 { 294 auto key = ByteConverter.hexDecode(test_keys[i]); 295 auto params = ByteConverter.hexDecode(test_ivs[i]); 296 297 // Encryption 298 s20.init(true, key, params); 299 s20.update(ByteConverter.hexDecode(test_plaintexts[i]), buffer); 300 result = ByteConverter.hexEncode(buffer); 301 assert(result == test_ciphertexts[i], 302 s20.name()~": ("~result~") != ("~test_ciphertexts[i]~")"); 303 304 // Decryption 305 s20.init(false, key, params); 306 s20.update(ByteConverter.hexDecode(test_ciphertexts[i]), buffer); 307 result = ByteConverter.hexEncode(buffer); 308 assert(result == test_plaintexts[i], 309 s20.name()~": ("~result~") != ("~test_plaintexts[i]~")"); 310 } 311 } 312 } 313 }