1 /// Implementation of Salsa20 digester. 2 module tern.digest.salsa20; 3 4 import tern.digest; 5 import tern.digest.circe; 6 7 /** 8 * Implementation of Salsa20 digester. 9 * 10 * Salsa20 is a stream cipher designed to be highly efficient and secure. It operates 11 * on 512-bit (64-byte) blocks and accepts a 256-bit (32-byte) key and a 64-bit (8-byte) 12 * nonce. 13 * 14 * Example: 15 * ```d 16 * import tern.digest.salsa20; 17 * 18 * ubyte[] data = [1, 2, 3, 4, 5]; 19 * string key = "my_secret_key"; // Must be exactly 256 bits (32 bytes) in length. 20 * ubyte[8] nonce = [0, 0, 0, 0, 0, 0, 0, 0]; // Must be exactly 64 bits (8 bytes) in length. 21 * Salsa20.encrypt(data, key, nonce); 22 * ``` 23 */ 24 public static @digester class Salsa20 25 { 26 private: 27 static: 28 void quarterRound(ref uint[16] block, uint a, uint b, uint c, uint d) 29 { 30 block[a] += block[b]; block[d] = (block[d] ^ block[a]) << 7 | (block[d] ^ block[a]) >>> (32 - 7); 31 block[c] += block[d]; block[b] = (block[b] ^ block[c]) << 9 | (block[b] ^ block[c]) >>> (32 - 9); 32 block[a] += block[b]; block[d] = (block[d] ^ block[a]) << 13 | (block[d] ^ block[a]) >>> (32 - 13); 33 block[c] += block[d]; block[b] = (block[b] ^ block[c]) << 18 | (block[b] ^ block[c]) >>> (32 - 18); 34 } 35 36 uint[16] innerRound(ref uint[16] block) 37 { 38 foreach (i; 0 .. 10) 39 { 40 quarterRound(block, 0, 4, 8, 12); 41 quarterRound(block, 1, 5, 9, 13); 42 quarterRound(block, 2, 6, 10, 14); 43 quarterRound(block, 3, 7, 11, 15); 44 quarterRound(block, 0, 5, 10, 15); 45 quarterRound(block, 1, 6, 11, 12); 46 quarterRound(block, 2, 7, 8, 13); 47 quarterRound(block, 3, 4, 9, 14); 48 } 49 return block; 50 } 51 52 public: 53 /** 54 * Encrypts the given byte array `data`. 55 * 56 * Params: 57 * data: Reference to the input byte array to be encrypted. 58 * key: The encryption key. Must be exactly 256 bits (32 bytes) in length. 59 * nonce: The nonce value. Must be exactly 64 bits (8 bytes) in length. 60 */ 61 void encrypt(ref ubyte[] data, string key, ubyte[8] nonce = (ubyte[8]).init, uint counter = 0) 62 { 63 assert(key.length == 32, "Key must be 256 bits!"); 64 key = cast(string)Circe.hash(cast(ubyte[])key); 65 66 uint[16] state; 67 state[0..4] = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]; 68 state[4..12] = *cast(uint[8]*)key.ptr; 69 state[12..14] = counter; 70 state[14..16] = cast(uint[2])nonce; 71 72 uint[16] keyStream; 73 ubyte offset = 64; 74 75 foreach (ref octet; data) 76 { 77 if (offset >= 64) 78 { 79 keyStream = state.dup; 80 innerRound(keyStream); 81 // Counter 82 state[12]++; 83 offset = 0; 84 } 85 86 octet ^= (cast(ubyte[64])keyStream)[offset]; 87 offset++; 88 } 89 } 90 91 /** 92 * Decrypts the given byte array `data`. 93 * 94 * Params: 95 * data: Reference to the input byte array to be encrypted. 96 * key: The encryption key. Must be exactly 256 bits (32 bytes) in length. 97 * nonce: The nonce value. Must be exactly 64 bits (8 bytes) in length. 98 */ 99 void decrypt(ref ubyte[] data, string key, ubyte[8] nonce = (ubyte[8]).init, uint counter = 0) => encrypt(data, key, nonce, counter); 100 }