1 /** 2 * Salsa20 / XSalsa20 3 * 4 * Copyright: 5 * (C) 1999-2010 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.stream.salsa20; 12 13 import botan.constants; 14 static if (BOTAN_HAS_SALSA20): 15 16 import botan.stream.stream_cipher; 17 import botan.utils.loadstor; 18 import botan.utils.rotate; 19 import botan.utils.xor_buf; 20 import botan.utils.types; 21 import botan.utils.mem_ops; 22 import std.format : format; 23 24 /** 25 * DJB's Salsa20 (and XSalsa20) 26 */ 27 final class Salsa20 : StreamCipher, SymmetricAlgorithm 28 { 29 public: 30 /* 31 * Combine cipher stream with message 32 */ 33 override void cipher(const(ubyte)* input, ubyte* output, size_t length) 34 { 35 while (length >= m_buffer.length - m_position) 36 { 37 xorBuf(output, input, &m_buffer[m_position], m_buffer.length - m_position); 38 length -= (m_buffer.length - m_position); 39 input += (m_buffer.length - m_position); 40 output += (m_buffer.length - m_position); 41 salsa20(*cast(ubyte[64]*) m_buffer.ptr, *cast(uint[16]*) m_state.ptr); 42 43 ++m_state[8]; 44 m_state[9] += (m_state[8] == 0); 45 46 m_position = 0; 47 } 48 49 xorBuf(output, input, &m_buffer[m_position], length); 50 51 m_position += length; 52 } 53 54 55 /* 56 * Return the name of this type 57 */ 58 override void setIv(const(ubyte)* iv, size_t length) 59 { 60 if (!validIvLength(length)) 61 throw new InvalidIVLength(name(), length); 62 63 if (length == 8) 64 { 65 // Salsa20 66 m_state[6] = loadLittleEndian!uint(iv, 0); 67 m_state[7] = loadLittleEndian!uint(iv, 1); 68 } 69 else 70 { 71 // XSalsa20 72 m_state[6] = loadLittleEndian!uint(iv, 0); 73 m_state[7] = loadLittleEndian!uint(iv, 1); 74 m_state[8] = loadLittleEndian!uint(iv, 2); 75 m_state[9] = loadLittleEndian!uint(iv, 3); 76 77 SecureVector!uint hsalsa = SecureVector!uint(8); 78 hsalsa20(*cast(uint[8]*) hsalsa.ptr, *cast(uint[16]*) m_state.ptr); 79 80 m_state[ 1] = hsalsa[0]; 81 m_state[ 2] = hsalsa[1]; 82 m_state[ 3] = hsalsa[2]; 83 m_state[ 4] = hsalsa[3]; 84 m_state[ 6] = loadLittleEndian!uint(iv, 4); 85 m_state[ 7] = loadLittleEndian!uint(iv, 5); 86 m_state[11] = hsalsa[4]; 87 m_state[12] = hsalsa[5]; 88 m_state[13] = hsalsa[6]; 89 m_state[14] = hsalsa[7]; 90 } 91 92 m_state[8] = 0; 93 m_state[9] = 0; 94 95 salsa20(*cast(ubyte[64]*) m_buffer.ptr, *cast(uint[16]*) m_state.ptr); 96 ++m_state[8]; 97 m_state[9] += (m_state[8] == 0); 98 99 m_position = 0; 100 } 101 102 override bool validIvLength(size_t iv_len) const 103 { return (iv_len == 8 || iv_len == 24); } 104 105 KeyLengthSpecification keySpec() const 106 { 107 return KeyLengthSpecification(16, 32, 16); 108 } 109 110 /* 111 * Clear memory of sensitive data 112 */ 113 void clear() 114 { 115 zap(m_state); 116 zap(m_buffer); 117 m_position = 0; 118 } 119 120 /* 121 * Return the name of this type 122 */ 123 @property string name() const 124 { 125 return "Salsa20"; 126 } 127 128 override Salsa20 clone() const { return new Salsa20; } 129 protected: 130 /* 131 * Salsa20 Key Schedule 132 */ 133 override void keySchedule(const(ubyte)* key, size_t length) 134 { 135 __gshared immutable uint[] TAU = [ 0x61707865, 0x3120646e, 0x79622d36, 0x6b206574 ]; 136 137 __gshared immutable uint[] SIGMA = [ 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 ]; 138 139 const uint[] CONSTANTS = (length == 16) ? TAU : SIGMA; 140 141 m_state.resize(16); 142 m_buffer.resize(64); 143 144 m_state[0] = CONSTANTS[0]; 145 m_state[5] = CONSTANTS[1]; 146 m_state[10] = CONSTANTS[2]; 147 m_state[15] = CONSTANTS[3]; 148 149 m_state[1] = loadLittleEndian!uint(key, 0); 150 m_state[2] = loadLittleEndian!uint(key, 1); 151 m_state[3] = loadLittleEndian!uint(key, 2); 152 m_state[4] = loadLittleEndian!uint(key, 3); 153 154 if (length == 32) 155 key += 16; 156 157 m_state[11] = loadLittleEndian!uint(key, 0); 158 m_state[12] = loadLittleEndian!uint(key, 1); 159 m_state[13] = loadLittleEndian!uint(key, 2); 160 m_state[14] = loadLittleEndian!uint(key, 3); 161 162 m_position = 0; 163 164 const ubyte[8] ZERO; 165 setIv(ZERO.ptr, ZERO.length); 166 } 167 168 SecureVector!uint m_state; 169 SecureVector!ubyte m_buffer; 170 size_t m_position; 171 } 172 173 174 private: 175 176 /* 177 * Generate HSalsa20 cipher stream (for XSalsa20 IV setup) 178 */ 179 void hsalsa20(ref uint[8] output, in uint[16] input) 180 { 181 uint x00 = input[ 0], x01 = input[ 1], x02 = input[ 2], x03 = input[ 3], 182 x04 = input[ 4], x05 = input[ 5], x06 = input[ 6], x07 = input[ 7], 183 x08 = input[ 8], x09 = input[ 9], x10 = input[10], x11 = input[11], 184 x12 = input[12], x13 = input[13], x14 = input[14], x15 = input[15]; 185 186 foreach (size_t i; 0 .. 10) 187 { 188 mixin( SALSA20_QUARTER_ROUND!(x00, x04, x08, x12) ~ 189 SALSA20_QUARTER_ROUND!(x05, x09, x13, x01) ~ 190 SALSA20_QUARTER_ROUND!(x10, x14, x02, x06) ~ 191 SALSA20_QUARTER_ROUND!(x15, x03, x07, x11) ~ 192 193 SALSA20_QUARTER_ROUND!(x00, x01, x02, x03) ~ 194 SALSA20_QUARTER_ROUND!(x05, x06, x07, x04) ~ 195 SALSA20_QUARTER_ROUND!(x10, x11, x08, x09) ~ 196 SALSA20_QUARTER_ROUND!(x15, x12, x13, x14) 197 ); 198 } 199 200 output[0] = x00; 201 output[1] = x05; 202 output[2] = x10; 203 output[3] = x15; 204 output[4] = x06; 205 output[5] = x07; 206 output[6] = x08; 207 output[7] = x09; 208 } 209 210 /* 211 * Generate Salsa20 cipher stream 212 */ 213 void salsa20(ref ubyte[64] output, in uint[16] input) 214 { 215 uint x00 = input[ 0], x01 = input[ 1], x02 = input[ 2], x03 = input[ 3], 216 x04 = input[ 4], x05 = input[ 5], x06 = input[ 6], x07 = input[ 7], 217 x08 = input[ 8], x09 = input[ 9], x10 = input[10], x11 = input[11], 218 x12 = input[12], x13 = input[13], x14 = input[14], x15 = input[15]; 219 220 foreach (size_t i; 0 .. 10) 221 { 222 mixin( SALSA20_QUARTER_ROUND!(x00, x04, x08, x12) ~ 223 SALSA20_QUARTER_ROUND!(x05, x09, x13, x01) ~ 224 SALSA20_QUARTER_ROUND!(x10, x14, x02, x06) ~ 225 SALSA20_QUARTER_ROUND!(x15, x03, x07, x11) ~ 226 227 SALSA20_QUARTER_ROUND!(x00, x01, x02, x03) ~ 228 SALSA20_QUARTER_ROUND!(x05, x06, x07, x04) ~ 229 SALSA20_QUARTER_ROUND!(x10, x11, x08, x09) ~ 230 SALSA20_QUARTER_ROUND!(x15, x12, x13, x14) 231 ); 232 } 233 234 storeLittleEndian(x00 + input[ 0], output.ptr + 4 * 0); 235 storeLittleEndian(x01 + input[ 1], output.ptr + 4 * 1); 236 storeLittleEndian(x02 + input[ 2], output.ptr + 4 * 2); 237 storeLittleEndian(x03 + input[ 3], output.ptr + 4 * 3); 238 storeLittleEndian(x04 + input[ 4], output.ptr + 4 * 4); 239 storeLittleEndian(x05 + input[ 5], output.ptr + 4 * 5); 240 storeLittleEndian(x06 + input[ 6], output.ptr + 4 * 6); 241 storeLittleEndian(x07 + input[ 7], output.ptr + 4 * 7); 242 storeLittleEndian(x08 + input[ 8], output.ptr + 4 * 8); 243 storeLittleEndian(x09 + input[ 9], output.ptr + 4 * 9); 244 storeLittleEndian(x10 + input[10], output.ptr + 4 * 10); 245 storeLittleEndian(x11 + input[11], output.ptr + 4 * 11); 246 storeLittleEndian(x12 + input[12], output.ptr + 4 * 12); 247 storeLittleEndian(x13 + input[13], output.ptr + 4 * 13); 248 storeLittleEndian(x14 + input[14], output.ptr + 4 * 14); 249 storeLittleEndian(x15 + input[15], output.ptr + 4 * 15); 250 } 251 252 enum string SALSA20_QUARTER_ROUND(alias _x1, alias _x2, alias _x3, alias _x4) = q{ 253 %2$s ^= rotateLeft(%1$s + %4$s, 7); 254 %3$s ^= rotateLeft(%2$s + %1$s, 9); 255 %4$s ^= rotateLeft(%3$s + %2$s, 13); 256 %1$s ^= rotateLeft(%4$s + %3$s, 18); 257 }.format(__traits(identifier, _x1), __traits(identifier, _x2), __traits(identifier, _x3), __traits(identifier, _x4));