1 /** 2 * Derived from poly1305-donna-64.h by Andrew Moon <liquidsun@gmail.com> 3 * in https://github.com/floodyberry/poly1305-donna 4 * 5 * Copyright: 6 * (C) 2014 Jack Lloyd 7 * (C) 2014 Andrew Moon 8 * (C) 2014-2015 Etienne Cimon 9 * 10 * License: 11 * Botan is released under the Simplified BSD License (see LICENSE.md) 12 */ 13 module botan.mac.poly1305; 14 15 import botan.constants; 16 static if (BOTAN_HAS_POLY1305): 17 import botan.mac.mac; 18 import botan.utils.mul128; 19 import botan.utils.donna128; 20 import botan.utils.types; 21 import botan.utils.mem_ops; 22 import botan.utils.loadstor; 23 24 /** 25 * DJB's Poly1305 26 * Important note: each key can only be used once 27 */ 28 class Poly1305 : MessageAuthenticationCode, BufferedComputation, SymmetricAlgorithm 29 { 30 public: 31 /* 32 * Return the name of this type 33 */ 34 override @property string name() const 35 { 36 return "Poly1305"; 37 } 38 39 override @property size_t outputLength() const { return 16; } 40 41 /* 42 * Return a clone of this object 43 */ 44 override MessageAuthenticationCode clone() const 45 { 46 return new Poly1305; 47 } 48 49 /* 50 * Clear memory of sensitive data 51 */ 52 override void clear() { 53 zap(m_poly); 54 zap(m_buf); 55 m_buf_pos = 0; 56 } 57 58 override KeyLengthSpecification keySpec() const 59 { 60 return KeyLengthSpecification(32); 61 } 62 63 protected: 64 override void addData(const(ubyte)* input, size_t length) { 65 assert(m_poly.length == 8, "Initialized"); 66 67 if(m_buf_pos) 68 { 69 bufferInsert(m_buf, m_buf_pos, input, length); 70 71 if(m_buf_pos + length >= m_buf.length) 72 { 73 poly1305_blocks(m_poly.ptr, m_buf.ptr, 1); 74 input += (m_buf.length - m_buf_pos); 75 length -= (m_buf.length - m_buf_pos); 76 m_buf_pos = 0; 77 } 78 } 79 80 const size_t full_blocks = length / m_buf.length; 81 const size_t remaining = length % m_buf.length; 82 83 if(full_blocks) 84 poly1305_blocks(m_poly.ptr, input, full_blocks); 85 86 bufferInsert(m_buf, m_buf_pos, input + full_blocks * m_buf.length, remaining); 87 m_buf_pos += remaining; 88 } 89 90 override void finalResult(ubyte* output) { 91 assert(m_poly.length == 8, "Initialized"); 92 93 if(m_buf_pos != 0) 94 { 95 m_buf[m_buf_pos] = 1; 96 const auto len = m_buf.length - m_buf_pos - 1; 97 if (len > 0) { 98 clearMem(m_buf.ptr + m_buf_pos + 1, len); 99 } 100 poly1305_blocks(m_poly.ptr, m_buf.ptr, 1, true); 101 } 102 103 poly1305_finish(m_poly.ptr, output); 104 105 m_poly.clear(); 106 m_buf_pos = 0; 107 } 108 109 110 override void keySchedule(const(ubyte)* key, size_t length) { 111 assert(length == 32); 112 m_buf_pos = 0; 113 m_buf.resize(16); 114 m_poly.resize(8); 115 poly1305_init(m_poly.ptr, key); 116 } 117 118 SecureVector!ulong m_poly; 119 SecureVector!ubyte m_buf; 120 size_t m_buf_pos; 121 }; 122 123 private: 124 125 void poly1305_init(ulong* X, const ubyte* key) 126 { 127 /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */ 128 const ulong t0 = loadLittleEndian!ulong(key, 0); 129 const ulong t1 = loadLittleEndian!ulong(key, 1); 130 131 X[0] = ( t0 ) & 0xffc0fffffff; 132 X[1] = ((t0 >> 44) | (t1 << 20)) & 0xfffffc0ffff; 133 X[2] = ((t1 >> 24) ) & 0x00ffffffc0f; 134 135 /* h = 0 */ 136 X[3] = 0; 137 X[4] = 0; 138 X[5] = 0; 139 140 /* save pad for later */ 141 X[6] = loadLittleEndian!ulong(key, 2); 142 X[7] = loadLittleEndian!ulong(key, 3); 143 } 144 145 void poly1305_blocks(ulong* X, const(ubyte)* m, size_t blocks, bool is_final = false) 146 { 147 alias uint128_t = donna128; 148 149 const ulong hibit = is_final ? 0 : (1UL << 40); /* 1 << 128 */ 150 151 const ulong r0 = X[0]; 152 const ulong r1 = X[1]; 153 const ulong r2 = X[2]; 154 155 ulong h0 = X[3+0]; 156 ulong h1 = X[3+1]; 157 ulong h2 = X[3+2]; 158 159 const ulong s1 = r1 * (5 << 2); 160 const ulong s2 = r2 * (5 << 2); 161 162 while(blocks--) 163 { 164 /* h += m[i] */ 165 const ulong t0 = loadLittleEndian!ulong(m, 0); 166 const ulong t1 = loadLittleEndian!ulong(m, 1); 167 168 h0 += (( t0 ) & 0xfffffffffff); 169 h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff); 170 h2 += (((t1 >> 24) ) & 0x3ffffffffff) | hibit; 171 172 /* h *= r */ 173 uint128_t d0 = uint128_t(h0) * r0 + uint128_t(h1) * s2 + uint128_t(h2) * s1; 174 uint128_t d1 = uint128_t(h0) * r1 + uint128_t(h1) * r0 + uint128_t(h2) * s2; 175 uint128_t d2 = uint128_t(h0) * r2 + uint128_t(h1) * r1 + uint128_t(h2) * r0; 176 177 /* (partial) h %= p */ 178 ulong c = carry_shift(d0, 44); h0 = d0 & 0xfffffffffff; 179 d1 += c; c = carry_shift(d1, 44); h1 = d1 & 0xfffffffffff; 180 d2 += c; c = carry_shift(d2, 42); h2 = d2 & 0x3ffffffffff; 181 h0 += c * 5; c = carry_shift(uint128_t(h0), 44); h0 = h0 & 0xfffffffffff; 182 h1 += c; 183 184 m += 16; 185 } 186 187 X[3+0] = h0; 188 X[3+1] = h1; 189 X[3+2] = h2; 190 } 191 192 void poly1305_finish(ulong* X, ubyte* mac) 193 { 194 /* fully carry h */ 195 ulong h0 = X[3+0]; 196 ulong h1 = X[3+1]; 197 ulong h2 = X[3+2]; 198 199 ulong c; 200 c = (h1 >> 44); h1 &= 0xfffffffffff; 201 h2 += c; c = (h2 >> 42); h2 &= 0x3ffffffffff; 202 h0 += c * 5; c = (h0 >> 44); h0 &= 0xfffffffffff; 203 h1 += c; c = (h1 >> 44); h1 &= 0xfffffffffff; 204 h2 += c; c = (h2 >> 42); h2 &= 0x3ffffffffff; 205 h0 += c * 5; c = (h0 >> 44); h0 &= 0xfffffffffff; 206 h1 += c; 207 208 /* compute h + -p */ 209 ulong g0 = h0 + 5; c = (g0 >> 44); g0 &= 0xfffffffffff; 210 ulong g1 = h1 + c; c = (g1 >> 44); g1 &= 0xfffffffffff; 211 ulong g2 = h2 + c - (1UL << 42); 212 213 /* select h if h < p, or h + -p if h >= p */ 214 c = (g2 >> ((ulong.sizeof * 8) - 1)) - 1; 215 g0 &= c; 216 g1 &= c; 217 g2 &= c; 218 c = ~c; 219 h0 = (h0 & c) | g0; 220 h1 = (h1 & c) | g1; 221 h2 = (h2 & c) | g2; 222 223 /* h = (h + pad) */ 224 const ulong t0 = X[6]; 225 const ulong t1 = X[7]; 226 227 h0 += (( t0 ) & 0xfffffffffff) ; c = (h0 >> 44); h0 &= 0xfffffffffff; 228 h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff) + c; c = (h1 >> 44); h1 &= 0xfffffffffff; 229 h2 += (((t1 >> 24) ) & 0x3ffffffffff) + c; h2 &= 0x3ffffffffff; 230 231 /* mac = h % (2^128) */ 232 h0 = ((h0 ) | (h1 << 44)); 233 h1 = ((h1 >> 20) | (h2 << 24)); 234 235 storeLittleEndian(mac, h0, h1); 236 237 /* zero out the state */ 238 clearMem(X, 8); 239 }