1 module dcrypt.crypto.engines.salsa; 2 3 public import dcrypt.crypto.streamcipher; 4 5 import dcrypt.bitmanip: rotl=rotateLeft; 6 import dcrypt.util: wipe; 7 8 import dcrypt.bitmanip; 9 10 import std.algorithm: min; 11 import std.conv: text; 12 13 // Test Salsa20 14 unittest { 15 16 // test vectors generated with bouncycastle Salsa20 implementation 17 string[] keys = [ 18 x"00000000000000000000000000000000", 19 x"01010101010101010101010101010101", 20 x"0202020202020202020202020202020202020202020202020202020202020202", 21 x"0303030303030303030303030303030303030303030303030303030303030303", 22 ]; 23 24 string[] ivs = [ 25 x"0101010101010101", 26 x"0202020202020202", 27 x"0303030303030303", 28 x"0404040404040404", 29 ]; 30 31 string[] plains = [ 32 x"0202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202", 33 x"0303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303", 34 x"0404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404", 35 x"0505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505", 36 ]; 37 38 string[] ciphers = [ 39 x"4280c83f61325a4b1c2ba27d5693c016a08b4eb5afe070991307a89a4f6787e734ef067bb35521ea44a4e07d90e64140959e10db4de238918956fc9a8b45245c6df266aed0f064f8be9d769380049b173f3ea0a665dcb46b85f5a9f01406e0cb7186fab2328ddc39051722caeaf2166c00a93e447df93eae9610b82af86c0ed9", 40 x"3f1d4e1227cd3483068269ed5914a61dedce4e0fe2736d29e9dac4404a994423feb51748ba99e1f94599c53ee97f58d5b002c9bf04f9a0c2cc4d186620b7a23e312bfaf3d90b76f1fabaaa7a1526160d38fe64acb4af703e2d084424efcdf975825f5fcf39f3286375532ff7206b64b81ae65a0ada0005a5af6d035fb6b38221", 41 x"e390de08ceb0eca5c373e7fbc62ae1a76eea14b3302bda97833801da174ef6464f1592d563c30b3da9c99c2b7850a6f93d036bdaf82788aa870a20e2fc384c61f33e45f52e03903a0ab55807b18920db7481bf5f4975faa4e0fc595dc3e8d23145b288e48ae4b895ce58198624b660867eb8226582f72879f4b90eb9a5e67067", 42 x"a64037f78e86e53cade29afa3533719ea5b7b71162911bd06e351f172692602cc5c90b022652292f0a78530e4961004cd38b14a22c8a483f386987293c1c24fe1882cb13221d4ec6fa63082c586aab1e2d9fed293b17b1159f1c2f4fdc007df5c9a8c1b026d254262c6e28c0d2f0c8b3cfa83be5b3dbc29166f74123c7dfc54c", 43 ]; 44 45 streamCipherTest(new Salsa20Engine, keys, plains, ciphers, ivs); 46 } 47 48 // Test XSalsa 49 unittest { 50 51 // test vectors generated with bouncycastle XSalsa20 implementation 52 string[] keys = [ 53 x"86e2f31305b14bc42caf3f9c7fb4112cc7ae64cf43e0d429a27fa63b70d0939e", 54 x"b96baefe0fa3144455926da4a8583643107bda7926b2ea4577776f9ca89a9d00", 55 x"a5f519c06ede84d97d0d6dc8a9cfa52cfe532908e0dfdb03a875a948866cd77b", 56 x"c758bac8a6a4a405aa057a9d90621afa9f23eeb5157f65474e0cdb11284bea77", 57 ]; 58 string[] ivs = [ 59 x"24eaeaac41c512e3eb77bc051c4e98ab565122ea4d274b4f", 60 x"17af8f7d0a89e15c6587d3a4f6bb20a75a7ea9d70c96e01c", 61 x"0a702d29a25362429c5a5f5e3dd86580c733fb94aff70037", 62 x"4201c53a16675ee4b95a85572c59c6e6cdc2faad8c77bc49", 63 ]; 64 string[] plains = [ 65 x"dfe73782b7b40e000084dcd8170f95549180c8b546b8cce7823a38b11fd78f0fb94fa0720cffae5411e5108c9bd186b9f6ab7630477d8b2ca4160c9deeda271ded4a6dd782962b68315c0b9220b122a70bfee4d426eda7f1a44b562659c525bf23a5c692c8c79bb37d11e4386e5a8a096c333038ac590598ef59b6dd7b8969d6", 66 x"1107c901f7330c80f24d02581bcd027c6a58cbc2809eebfd1c9182227875571da45c9db20e51421d2b970846e5c72ef1b5b8fdae6e7b59ad9d583dc87133c47686b123627b98e0422fb86495f73060c882302b9c20d310e4e0eddd5daca2f028952d925394afe04a9718f3e5fbe7879665c618a9e05c86bb286dc455898f53da", 67 x"a2262bc781d520f799b31a18ad51501072670fa33e6d1c799c7e9c97322485b44800f33f81d9c85c750045f8acedfd61b31f064c4c36771586ea86b2441273936af2644644c3aa8a521ee03ddabdcb6a05177ebab78143ea0dcfd98ef3301f5b76f1f847fb24464f41bba616feb75c2cbb8629807b3dc9fe63bb7b4abcf94a60", 68 x"a840111122d4db70f5fbfdea485a37f3ab621855ff44b29843b49d26499f6acbc809bd51d19f7ebab48d99265dddab8832795b526ab688048ce80a0f6b2e938e9568bdb7e90aae58f665f653a1c5b606b0cfac4a8fdb48e340a9c128a5aadea33c8258ea57d1660096cf0858c0e5bb98c7431121b7435e82c62df79ce11a99a1", 69 ]; 70 string[] ciphers = [ 71 x"7b64de1c7c6dec7eb70b79edf5c9d5812309a344e0f9ca53f18c922f03604d616a8008363bd82fc53341c32825ddb8ae371b242fa8eed90afdd38659a2304c13c774816e6c1b3022eebb8092971d3393406f8c70c8a02471146813906ac74e66751bb3dbb21a07913a69c1fcd8e9af0d3b23f13c74872da21eeef0a8578e5873", 72 x"80d4be693aa49d763ec1dffa251a6bb0f83402902a8175f5759d40046bc2e0ced8a8239f5a2d2caf28846f8b0e8e0b471ec6d61ed19b268c5d4ed2aff87bb1f07adf0297d305767b70eda08a29c16f04825b7edefdcbc77fcfbd2c9fad63e0d8409dc7a661add37babf814d76aec15ad435b8d9393793189c76f3e51cce31e3b", 73 x"8303327859df863abf0b932e3609b862b0e2399f277bbdc194fe19d9f6ad83685f0f2881db383677962d0ef5ae15e30c80cd03b994abc20a5e27a2b7c4c23ab2b045df862a315e5b5329e41183c98acfb2434ebccdf19005204b4d0c7541c3c517bbfc555c54c5d164be5b50ce22182dcb37b9e1a42a19390107683160e97c00", 74 x"b8c77c22f789d71679afd50aeb51dcbca26066fc55cee32e5ce3647d89de1bc664f9760ca6ae3037104387ffd1ae6aaae76f7ea1a3c2cac7dc5e5fedf581f8ba3c5025c163cfe7f03337a5ada2e34c573da2149994e805101f829e774e91338e730f07ad870b94bf71a575af3dd029fabe8e874eb655843d8f37bc01a5cfc818", 75 ]; 76 77 streamCipherTest(new XSalsa20Engine, keys, plains, ciphers, ivs); 78 } 79 80 alias Salsa!20 Salsa20; 81 alias StreamCipherWrapper!Salsa20 Salsa20Engine; 82 83 alias Salsa!(20, true) XSalsa20; 84 alias StreamCipherWrapper!XSalsa20 XSalsa20Engine; 85 86 static assert(isStreamCipher!Salsa20, "Salsa20 is not a stream cipher!"); 87 static assert(isStreamCipher!XSalsa20, "XSalsa20 is not a stream cipher!"); 88 89 /// 90 /// implementation of the Salsa20/20 stream cipher 91 /// 92 /// Params: 93 /// rounds = Number of rounds. 12 and 20 are allowed. Default is 20. 94 /// 95 @safe 96 public struct Salsa(uint rounds = 20, bool xsalsa = false) 97 if(rounds == 12 || rounds == 20) 98 { 99 100 @nogc nothrow: 101 102 public enum name = text(xsalsa ? "X" : "", "Salsa20/", rounds); 103 104 private { 105 106 enum stateSize = 16; // 16, 32 bit ints = 64 bytes 107 108 /* 109 * variables to hold the state of the engine 110 * during encryption and decryption 111 */ 112 uint index = 0; 113 uint[stateSize] engineState; /// state 114 ubyte[stateSize*4] keyStream; /// expanded state, 64 bytes 115 bool initialized = false; 116 117 /* 118 * internal counter 119 */ 120 uint cW0, cW1, cW2; 121 122 } 123 124 @safe 125 ~this() { 126 wipe(engineState); 127 wipe(keyStream); 128 } 129 130 /// Initialize the cipher. 131 /// 132 /// Params: 133 /// forEncryption = Not used because encryption and decryption is actually the same. 134 /// key = secret key 135 /// iv = Use a unique nonce per key. 136 public void start(bool forEncryption, in ubyte[] key, in ubyte[] iv) nothrow @nogc 137 in { 138 static if(xsalsa) { 139 assert(key.length == 32, "XSalsa requires a 256 bit key."); 140 assert(iv.length == 24, "XSalsa needs a 192 bit nonce."); 141 } else { 142 assert(key.length == 16 || key.length == 32, "Salsa20 needs 128 or 256 bit keys."); 143 assert(iv.length == 8, "Salsa20 needs a 8 byte IV."); 144 } 145 } 146 body { 147 static if(xsalsa) { 148 // XSalsa 149 ubyte[32] xkey = HSalsa(key, iv[0..16]); 150 initState(engineState, xkey, 0, iv[16..24]); 151 wipe(xkey); 152 } else { 153 // Salsa 154 initState(engineState, key, 0, iv); 155 } 156 index = 0; 157 resetCounter(); 158 initialized = true; 159 } 160 161 /// 162 /// Throws: Error if limit of 2^70 bytes is exceeded. 163 /// 164 public ubyte returnByte(ubyte input) 165 { 166 if (limitExceeded()) 167 { 168 assert(false, "2^70 byte limit per IV. Change IV"); 169 } 170 171 if (index == 0) 172 { 173 generateKeyStream(keyStream); 174 175 if (++engineState[8] == 0) 176 { 177 ++engineState[9]; 178 } 179 } 180 181 ubyte output = keyStream[index]^input; 182 index = (index + 1) % keyStream.length; 183 184 return output; 185 } 186 187 /// 188 /// encrypt or decrypt input bytes but no more than 2^70! 189 /// 190 /// Params: 191 /// input = input bytes 192 /// output = buffer for output bytes. length must match input length. 193 /// 194 /// Returns: Slice pointing to processed data which might be smaller than `output`. 195 /// 196 /// Throws: Error if limit of 2^70 bytes is exceeded. 197 /// 198 public ubyte[] processBytes(in ubyte[] input, ubyte[] output) 199 in { 200 assert(output.length >= input.length, "output buffer too short"); 201 assert(initialized, "Salsa20Engine not initialized!"); 202 } 203 body { 204 205 // can't encrypt more than 2^70 bytes per iv 206 if (limitExceeded(input.length)) 207 { 208 assert(false, "2^70 byte limit per IV would be exceeded. Change IV!"); 209 } 210 211 ubyte[] initialOutputSlice = output; 212 const (ubyte)[] inp = input; 213 214 while(inp.length > 0) { 215 216 if (index == 0) 217 { 218 generateKeyStream(keyStream); 219 220 // increment counter 221 // engineState[9] += ++engineState[8] == 0; 222 if (++engineState[8] == 0) 223 { 224 ++engineState[9]; 225 } 226 } 227 228 size_t len = min(keyStream.length-index, inp.length); 229 output[0..len] = inp[0..len]^keyStream[index..index+len]; 230 index = (index + len) % keyStream.length; 231 inp = inp[len..$]; 232 output = output[len..$]; 233 } 234 235 236 return initialOutputSlice[0..input.length]; 237 } 238 239 /// reset the cipher to its initial state 240 deprecated("The reset() function might lead to insecure use of a stream cipher.") 241 public void reset() nothrow @nogc 242 in { 243 assert(initialized, "not yet initialized"); 244 } 245 body { 246 //setKey(workingKey, workingIV); 247 // reset counter 248 engineState[8..10] = 0; 249 } 250 251 /// Salsa20/rounds function 252 /// 253 /// Params: 254 /// rounds = number of rounds (20 in default implementation) 255 /// input = input data 256 /// x = output buffer where keystream gets written to 257 public static void block(uint rounds = 20)(in uint[] input, uint[] output) pure nothrow @nogc 258 if(rounds % 2 == 0 || rounds > 0) 259 in { 260 assert(input.length == 16, "invalid input length"); 261 assert(output.length == 16, "invalid output buffer length"); 262 } body { 263 264 uint[16] x = input; 265 266 salsaDoubleRound!rounds(x); 267 268 // element wise addition 269 x[] += input[]; 270 output[] = x[]; 271 } 272 273 public static void block(uint rounds = 20)(in ref uint[16] input, ref uint[16] output) pure nothrow @nogc 274 if(rounds % 2 == 0 || rounds > 0) 275 in { 276 assert(input.length == 16, "invalid input length"); 277 assert(output.length == 16, "invalid output buffer length"); 278 } body { 279 280 uint[16] x = input; 281 282 salsaDoubleRound!rounds(x); 283 284 // element wise addition 285 x[] += input[]; 286 output[] = x[]; 287 } 288 289 /// Params: 290 /// keyBytes = key, 16 or 32 bytes. 291 /// ivBytes = iv, exactly 8 bytes. 292 public static void initState(ref uint[16] state, in ubyte[] keyBytes, in uint counter, in ubyte[] ivBytes) nothrow @nogc 293 in { 294 assert(keyBytes.length == 16 || keyBytes.length == 32, "invalid key length"); 295 assert(ivBytes.length == 8, "invalid iv length"); 296 } 297 body { 298 299 uint[4] constants; 300 301 // Key 302 fromLittleEndian(keyBytes[0..16], state[1..5]); 303 304 if (keyBytes.length == 32) 305 { 306 constants = sigma; 307 308 fromLittleEndian(keyBytes[16..32], state[11..15]); 309 } 310 else 311 { 312 // repeat the 128 bit key 313 constants = tau; 314 fromLittleEndian(keyBytes[0..16], state[11..15]); 315 } 316 317 state[0] = constants[0]; 318 state[5] = constants[1]; 319 state[10] = constants[2]; 320 state[15] = constants[3]; 321 322 // IV 323 fromLittleEndian!uint(ivBytes[0..$], state[6..8]); 324 325 // counter 326 state[8] = counter; 327 state[9] = 0; 328 } 329 330 331 // 332 // Private implementation 333 // 334 335 private: 336 337 /// generate a block (64 bytes) of keystream 338 void generateKeyStream(ubyte[] output) nothrow @nogc 339 in { 340 assert(output.length == stateSize*4, "invalid length of output buffer: 64 bytes required"); 341 } 342 body { 343 uint[stateSize] x; 344 block!rounds(engineState, x); 345 toLittleEndian!uint(x, output); 346 } 347 348 void resetCounter() nothrow @nogc 349 { 350 cW0 = 0; 351 cW1 = 0; 352 cW2 = 0; 353 } 354 355 bool limitExceeded() nothrow @nogc 356 { 357 if (++cW0 == 0) 358 { 359 if (++cW1 == 0) 360 { 361 return (++cW2 & 0x20) != 0; // 2^(32 + 32 + 6) 362 } 363 } 364 365 return false; 366 } 367 368 /* 369 * test if limit will be exceeded for input of size len 370 */ 371 bool limitExceeded(size_t len) nothrow @nogc 372 { 373 cW0 += len; 374 if (cW0 < len && cW0 >= 0) 375 { 376 if (++cW1 == 0) 377 { 378 return (++cW2 & 0x20) != 0; // 2^(32 + 32 + 6) 379 } 380 } 381 382 return false; 383 } 384 } 385 386 private { 387 // constants 388 389 enum uint[4] sigma = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]; //cast(ubyte[16])"expand 32-byte k"; 390 enum uint[4] tau = [0x61707865, 0x3120646e, 0x79622d36, 0x6b206574]; //cast(ubyte[16])"expand 16-byte k"; 391 392 } 393 394 /// Executes the double round function rounds/2 times. 395 /// 396 /// Params: 397 /// rounds = number of rounds. 398 /// x = the state. 399 private void salsaDoubleRound(uint rounds)(uint[] x) pure nothrow @nogc 400 if(rounds % 2 == 0 || rounds > 0) 401 in { 402 assert(x.length == 16, "invalid state length"); 403 } body { 404 foreach (i; 0..rounds/2) 405 { 406 x[ 4] ^= rotl((x[ 0]+x[12]), 7); 407 x[ 8] ^= rotl((x[ 4]+x[ 0]), 9); 408 x[12] ^= rotl((x[ 8]+x[ 4]),13); 409 x[ 0] ^= rotl((x[12]+x[ 8]),18); 410 x[ 9] ^= rotl((x[ 5]+x[ 1]), 7); 411 x[13] ^= rotl((x[ 9]+x[ 5]), 9); 412 x[ 1] ^= rotl((x[13]+x[ 9]),13); 413 x[ 5] ^= rotl((x[ 1]+x[13]),18); 414 x[14] ^= rotl((x[10]+x[ 6]), 7); 415 x[ 2] ^= rotl((x[14]+x[10]), 9); 416 x[ 6] ^= rotl((x[ 2]+x[14]),13); 417 x[10] ^= rotl((x[ 6]+x[ 2]),18); 418 x[ 3] ^= rotl((x[15]+x[11]), 7); 419 x[ 7] ^= rotl((x[ 3]+x[15]), 9); 420 x[11] ^= rotl((x[ 7]+x[ 3]),13); 421 x[15] ^= rotl((x[11]+x[ 7]),18); 422 x[ 1] ^= rotl((x[ 0]+x[ 3]), 7); 423 x[ 2] ^= rotl((x[ 1]+x[ 0]), 9); 424 x[ 3] ^= rotl((x[ 2]+x[ 1]),13); 425 x[ 0] ^= rotl((x[ 3]+x[ 2]),18); 426 x[ 6] ^= rotl((x[ 5]+x[ 4]), 7); 427 x[ 7] ^= rotl((x[ 6]+x[ 5]), 9); 428 x[ 4] ^= rotl((x[ 7]+x[ 6]),13); 429 x[ 5] ^= rotl((x[ 4]+x[ 7]),18); 430 x[11] ^= rotl((x[10]+x[ 9]), 7); 431 x[ 8] ^= rotl((x[11]+x[10]), 9); 432 x[ 9] ^= rotl((x[ 8]+x[11]),13); 433 x[10] ^= rotl((x[ 9]+x[ 8]),18); 434 x[12] ^= rotl((x[15]+x[14]), 7); 435 x[13] ^= rotl((x[12]+x[15]), 9); 436 x[14] ^= rotl((x[13]+x[12]),13); 437 x[15] ^= rotl((x[14]+x[13]),18); 438 } 439 } 440 441 /// HSalsa as defined in http://cr.yp.to/snuffle/xsalsa-20110204.pdf 442 /// Params: 443 /// key = 32 byte key. 444 /// nonce = 24 byte nonce. 445 /// 446 /// Returns: 256 bit value. 447 ubyte[32] HSalsa(uint rounds = 20)(in ubyte[] key, in ubyte[] nonce) pure nothrow @nogc 448 if(rounds == 12 || rounds == 20) 449 in { 450 assert(key.length == 32, "HSalsa requires 256 bit key."); 451 assert(nonce.length == 16, "HSalsa requires 128 bit nonce."); 452 } body { 453 uint[16] x; 454 uint[8] z; 455 456 scope(exit) { 457 wipe(x); 458 wipe(z); 459 } 460 461 x[0] = sigma[0]; 462 x[5] = sigma[1]; 463 x[10] = sigma[2]; 464 x[15] = sigma[3]; 465 466 fromLittleEndian!uint(key[0*4..4*4], x[1..5]); 467 fromLittleEndian!uint(key[4*4..8*4], x[11..15]); 468 469 fromLittleEndian!uint(nonce, x[6..10]); 470 471 salsaDoubleRound!rounds(x); 472 473 z[0] = x[0]; 474 z[1] = x[5]; 475 z[2] = x[10]; 476 z[3] = x[15]; 477 z[4..8] = x[6..10]; 478 479 ubyte[32] output; 480 toLittleEndian!uint(z, output); 481 return output; 482 }