1 /** 2 * ChaCha20 3 * 4 * Copyright: 5 * (C) 2014 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.chacha; 12 13 import botan.constants; 14 static if (BOTAN_HAS_CHACHA): 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 ChaCha (http://cr.yp.to/chacha.html) 26 */ 27 final class ChaCha : 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.ptr + 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 chacha(*cast(ubyte[64]*) m_buffer.ptr, *cast(uint[16]*) m_state.ptr); 42 43 ++m_state[12]; 44 m_state[13] += (m_state[12] == 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 * Return the name of this type 56 */ 57 override void setIv(const(ubyte)* iv, size_t length) 58 { 59 if (!validIvLength(length)) 60 throw new InvalidIVLength(name, length); 61 62 m_state[12] = 0; 63 64 m_state[13] = 0; 65 66 if (length == 8) { 67 m_state[14] = loadLittleEndian!uint(iv, 0); 68 m_state[15] = loadLittleEndian!uint(iv, 1); 69 } else if (length == 12) { 70 m_state[13] = loadLittleEndian!uint(iv, 0); 71 m_state[14] = loadLittleEndian!uint(iv, 1); 72 m_state[15] = loadLittleEndian!uint(iv, 2); 73 } 74 75 chacha(*cast(ubyte[64]*) m_buffer.ptr, *cast(uint[16]*) m_state.ptr); 76 ++m_state[12]; 77 m_state[13] += (m_state[12] == 0); 78 79 m_position = 0; 80 } 81 82 override bool validIvLength(size_t iv_len) const 83 { return (iv_len == 8 || iv_len == 12); } 84 85 KeyLengthSpecification keySpec() const 86 { 87 return KeyLengthSpecification(16, 32, 16); 88 } 89 90 /* 91 * Clear memory of sensitive data 92 */ 93 void clear() 94 { 95 zap(m_state); 96 zap(m_buffer); 97 m_position = 0; 98 } 99 100 /* 101 * Return the name of this type 102 */ 103 @property string name() const 104 { 105 return "ChaCha"; 106 } 107 108 override StreamCipher clone() const { return new ChaCha; } 109 protected: 110 111 void chacha(ref ubyte[64] output, in uint[16] input) 112 { 113 uint x00 = input[ 0], x01 = input[ 1], x02 = input[ 2], x03 = input[ 3], 114 x04 = input[ 4], x05 = input[ 5], x06 = input[ 6], x07 = input[ 7], 115 x08 = input[ 8], x09 = input[ 9], x10 = input[10], x11 = input[11], 116 x12 = input[12], x13 = input[13], x14 = input[14], x15 = input[15]; 117 118 119 foreach (size_t i; 0 .. 10) 120 { 121 mixin(CHACHA_QUARTER_ROUND!(x00, x04, x08, x12) ~ 122 CHACHA_QUARTER_ROUND!(x01, x05, x09, x13) ~ 123 CHACHA_QUARTER_ROUND!(x02, x06, x10, x14) ~ 124 CHACHA_QUARTER_ROUND!(x03, x07, x11, x15) ~ 125 126 CHACHA_QUARTER_ROUND!(x00, x05, x10, x15) ~ 127 CHACHA_QUARTER_ROUND!(x01, x06, x11, x12) ~ 128 CHACHA_QUARTER_ROUND!(x02, x07, x08, x13) ~ 129 CHACHA_QUARTER_ROUND!(x03, x04, x09, x14) 130 ); 131 } 132 133 storeLittleEndian(x00 + input[ 0], output.ptr + 4 * 0); 134 storeLittleEndian(x01 + input[ 1], output.ptr + 4 * 1); 135 storeLittleEndian(x02 + input[ 2], output.ptr + 4 * 2); 136 storeLittleEndian(x03 + input[ 3], output.ptr + 4 * 3); 137 storeLittleEndian(x04 + input[ 4], output.ptr + 4 * 4); 138 storeLittleEndian(x05 + input[ 5], output.ptr + 4 * 5); 139 storeLittleEndian(x06 + input[ 6], output.ptr + 4 * 6); 140 storeLittleEndian(x07 + input[ 7], output.ptr + 4 * 7); 141 storeLittleEndian(x08 + input[ 8], output.ptr + 4 * 8); 142 storeLittleEndian(x09 + input[ 9], output.ptr + 4 * 9); 143 storeLittleEndian(x10 + input[10], output.ptr + 4 * 10); 144 storeLittleEndian(x11 + input[11], output.ptr + 4 * 11); 145 storeLittleEndian(x12 + input[12], output.ptr + 4 * 12); 146 storeLittleEndian(x13 + input[13], output.ptr + 4 * 13); 147 storeLittleEndian(x14 + input[14], output.ptr + 4 * 14); 148 storeLittleEndian(x15 + input[15], output.ptr + 4 * 15); 149 } 150 151 protected: 152 /* 153 * ChaCha Key Schedule 154 */ 155 override void keySchedule(const(ubyte)* key, size_t length) 156 { 157 __gshared immutable uint[] TAU = [ 0x61707865, 0x3120646e, 0x79622d36, 0x6b206574 ]; 158 159 __gshared immutable uint[] SIGMA = [ 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 ]; 160 161 const uint[] CONSTANTS = (length == 16) ? TAU : SIGMA; 162 163 m_state.resize(16); 164 m_buffer.resize(64); 165 166 m_state[0] = CONSTANTS[0]; 167 m_state[1] = CONSTANTS[1]; 168 m_state[2] = CONSTANTS[2]; 169 m_state[3] = CONSTANTS[3]; 170 171 m_state[4] = loadLittleEndian!uint(key, 0); 172 m_state[5] = loadLittleEndian!uint(key, 1); 173 m_state[6] = loadLittleEndian!uint(key, 2); 174 m_state[7] = loadLittleEndian!uint(key, 3); 175 176 if (length == 32) 177 key += 16; 178 179 m_state[8] = loadLittleEndian!uint(key, 0); 180 m_state[9] = loadLittleEndian!uint(key, 1); 181 m_state[10] = loadLittleEndian!uint(key, 2); 182 m_state[11] = loadLittleEndian!uint(key, 3); 183 184 m_position = 0; 185 186 const ubyte[8] ZERO; 187 setIv(ZERO.ptr, ZERO.length); 188 } 189 190 191 SecureVector!uint m_state; 192 SecureVector!ubyte m_buffer; 193 size_t m_position = 0; 194 } 195 196 enum string CHACHA_QUARTER_ROUND(alias _a, alias _b, alias _c, alias _d) = q{ 197 %1$s += %2$s; %4$s ^= %1$s; %4$s = rotateLeft(%4$s, 16); 198 %3$s += %4$s; %2$s ^= %3$s; %2$s = rotateLeft(%2$s, 12); 199 %1$s += %2$s; %4$s ^= %1$s; %4$s = rotateLeft(%4$s, 8); 200 %3$s += %4$s; %2$s ^= %3$s; %2$s = rotateLeft(%2$s, 7); 201 }.format(__traits(identifier, _a), __traits(identifier, _b), __traits(identifier, _c), __traits(identifier, _d));