1 module dcrypt.aead.gcm.ghash; 2 3 import dcrypt.aead.gcm.galoisfield; 4 import dcrypt.aead.gcm.multiplier; 5 6 7 // BUG: llvm crashes when using GCMMultiplier64kTable 8 alias GHashGeneral!GCMMultiplier8kTable GHash; 9 10 /// Params: 11 /// Multiplier = a GCM Multiplier like GCMMultiplier8kTable 12 @safe 13 public struct GHashGeneral(Multiplier) if(isGCMMultiplier!Multiplier) 14 { 15 enum BLOCKSIZE = 16; /// block size in bytes 16 17 alias ubyte T; 18 alias T[BLOCKSIZE/(T.sizeof)] block; 19 20 private { 21 22 block stateCipher; /// state for cipher data hashing 23 block H; 24 block stateAAD; /// state for AAD hashing 25 block stateAADPre; /// stateAAD before first cipher byte is processed 26 27 ubyte stateAADOff = 0; /// offset in stateAAD buffer 28 ubyte stateCipherOff = 0; /// offset in stateCipher buffer 29 30 ulong lenAAD = 0; /// length of additional authenticated data (AAD) in bits 31 ulong lenAADPre = 0; /// length of AAD before first cipher byte is processed 32 ulong lenCipher = 0; /// length of authenticated cipher data in bits 33 34 bool aadInput = true; /// AAD or cipher input 35 36 Multiplier gcmMult; 37 } 38 39 invariant { 40 // offsets should never exceed their boundaries 41 assert(stateAADOff <= BLOCKSIZE); 42 assert(stateCipherOff <= BLOCKSIZE); 43 } 44 45 /// Params: 46 /// H = element of GF(2^128) 47 this(in ubyte[] H) nothrow @nogc 48 in { 49 assert(H.length == BLOCKSIZE, "H must be 16 bytes"); 50 } 51 body { 52 init(H); 53 } 54 55 /// initialize the hash 56 /// Params: 57 /// H = the factor used for multiplication. 58 public void init(in ubyte[] H) nothrow @nogc 59 in { 60 assert(H.length == BLOCKSIZE, "H must be 16 bytes"); 61 } 62 body { 63 this.H[] = H[]; 64 65 // init the multiplier 66 gcmMult.init(H); 67 } 68 69 70 /// add data to the AAD stream 71 /// Params: 72 /// aad = data to be authenticated only 73 public void updateAAD(in ubyte[] aad...) nothrow @nogc 74 { 75 update(stateAAD, stateAADOff, aad); 76 lenAAD += aad.length*8; 77 } 78 79 /// Call this before processing any cipher data for better performance. 80 private void finalizeAAD() nothrow @nogc { 81 82 stateCipher[] = stateAAD[]; 83 84 if(lenAAD > 0) { 85 stateAADPre[] = stateAAD[]; 86 lenAADPre = lenAAD; 87 } 88 89 if(stateAADOff > 0) { 90 // process partial block 91 multiplyH(stateCipher); 92 stateAADOff = 0; 93 } 94 aadInput = false; 95 } 96 97 /** 98 * Params: 99 * input = encrypted data 100 */ 101 public void updateCipherData(in ubyte[] input...) nothrow @nogc 102 { 103 if(aadInput) { 104 finalizeAAD(); // sets aadInput = false 105 } 106 update(stateCipher, stateCipherOff, input); 107 lenCipher += input.length*8; 108 } 109 110 /// do final hash round and copy hash to buf 111 /// resets GHASH 112 /// Params: buf = output buffer for hash value 113 public void doFinal(ubyte[] buf) nothrow @nogc 114 in { 115 assert(buf.length >= BLOCKSIZE, "output buffer too short"); 116 } 117 body { 118 119 if(stateAADOff > 0) { 120 // process last partial AAD block 121 multiplyH(stateAAD); 122 stateAADOff = 0; 123 } 124 125 // process last incomplete block 126 if(stateCipherOff > 0) { 127 multiplyH(stateCipher); 128 stateCipherOff = 0; 129 } 130 131 if(lenAAD > lenAADPre) { 132 // some AAD has been processed after first cipher bytes arrived 133 // need to adjust the MAC state 134 135 // caluculate the difference 136 stateAADPre[] ^= stateAAD[]; 137 138 ulong blockDiff = (lenCipher + 127) / 128; // number of cipher data blocks. 139 // + 127 added for rounding up. 140 141 // calculate H^blockDiff 142 ubyte[BLOCKSIZE] expH; 143 expH[] = H[]; 144 GF128.power(expH, blockDiff); 145 146 // propagate the difference to the current block 147 GF128.multiply(stateAADPre, expH); 148 149 // add the difference to the current block 150 stateCipher[] ^= stateAADPre[]; 151 } 152 153 // Add a block containing the length of both streams: X ^ (len(A)||len(C)) * H 154 foreach(i;0..8) { 155 stateCipher[i] ^= lenAAD >> (56-8*i); 156 stateCipher[i+8] ^= lenCipher >> (56-8*i); 157 } 158 159 multiplyH(stateCipher); 160 161 buf[0..BLOCKSIZE] = stateCipher[]; 162 163 reset(); 164 } 165 166 /// Reset the internal state. 167 public void reset() nothrow @nogc { 168 stateAAD[] = 0; 169 stateAADOff = 0; 170 stateCipher[] = 0; 171 stateCipherOff = 0; 172 lenCipher = 0; 173 lenAAD = 0; 174 175 lenAADPre = 0; 176 stateAADPre[] = 0; 177 178 aadInput = true; 179 } 180 181 /// xor X with input bytes and do GF multiplication by H if buffer is full 182 /// Params: 183 /// input = incoming data 184 /// state = update this state 185 /// statePos = pointer to the location where the next byte gets written 186 private void update(ubyte[] state, ref ubyte statePos, in ubyte[] input...) nothrow @nogc 187 in { 188 assert(state.length == 16); 189 } 190 body { 191 import std.algorithm: min; 192 193 const(ubyte)[] iBuf = input; 194 195 if(statePos == BLOCKSIZE) { 196 multiplyH(state); 197 statePos = 0; 198 } 199 200 while(iBuf.length > 0) { 201 202 size_t procLen = min(iBuf.length, BLOCKSIZE-statePos); 203 204 state[statePos..statePos+procLen] ^= iBuf[0..procLen]; 205 206 statePos += procLen; 207 iBuf = iBuf[procLen..$]; 208 209 if(statePos == BLOCKSIZE) { 210 multiplyH(state); 211 statePos = 0; 212 } 213 } 214 } 215 216 /// Multiply x by H, store result in x. 217 private void multiplyH(ubyte[] x) nothrow @nogc { 218 gcmMult.multiply(x); 219 } 220 221 // unittests 222 223 unittest { 224 GHash gHash = GHash(cast(const(ubyte)[])x"66e94bd4ef8a2c3b884cfa59ca342b2e"); 225 226 ubyte[16] token; 227 gHash.doFinal(token); 228 229 const(ubyte)[] EK0 = cast(const(ubyte)[])x"58e2fccefa7e3061367f1d57a4e7455a"; 230 token[] ^= EK0[]; 231 232 assert(token == x"58e2fccefa7e3061367f1d57a4e7455a"); 233 } 234 235 unittest { 236 237 GHash gHash = GHash(cast(const(ubyte)[])x"66e94bd4ef8a2c3b884cfa59ca342b2e"); 238 239 gHash.updateCipherData(cast(const(ubyte)[])x"0388dace60b6a392f328c2b971b2fe78"); 240 241 // check X1 242 assert(gHash.stateCipher == cast(const(ubyte)[])x"5e2ec746917062882c85b0685353deb7"); 243 244 ubyte[16] hash; 245 gHash.doFinal(hash); 246 247 assert(hash == x"f38cbb1ad69223dcc3457ae5b6b0f885"); 248 } 249 250 // test vectors from 251 // http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf 252 // section 2.1.1 253 unittest { 254 255 GHash gHash = GHash(cast(const(ubyte)[])x"73A23D80121DE2D5A850253FCF43120E"); 256 257 gHash.updateAAD(cast(const(ubyte)[])x"D609B1F056637A0D46DF998D88E5222A"); 258 gHash.updateAAD(cast(const(ubyte)[])x"B2C2846512153524C0895E8108000F10"); 259 gHash.updateAAD(cast(const(ubyte)[])x"1112131415161718191A1B1C1D1E1F20"); 260 gHash.updateAAD(cast(const(ubyte)[])x"2122232425262728292A2B2C2D2E2F30"); 261 gHash.updateAAD(cast(const(ubyte)[])x"313233340001"); 262 263 ubyte[16] token; 264 gHash.doFinal(token); 265 266 assert(token == x"1BDA7DB505D8A165264986A703A6920D"); 267 } 268 269 // test vectors from 270 // http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf 271 // section 2.4.1 272 unittest { 273 274 GHash gHash = GHash(cast(const(ubyte)[])x"E4E01725D724C1215C7309AD34539257"); 275 276 gHash.updateAAD(cast(const(ubyte)[])x"E20106D7CD0DF0761E8DCD3D88E54C2A"); 277 gHash.updateAAD(cast(const(ubyte)[])x"76D457ED"); 278 279 gHash.updateCipherData(cast(const(ubyte)[])x"13B4C72B389DC5018E72A171DD85A5D3"); 280 gHash.updateCipherData(cast(const(ubyte)[])x"752274D3A019FBCAED09A425CD9B2E1C"); 281 gHash.updateCipherData(cast(const(ubyte)[])x"9B72EEE7C9DE7D52B3F3"); 282 283 ubyte[16] ghash; 284 gHash.doFinal(ghash); 285 286 assert(ghash == x"2A807BDE4AF8A462D467D2FFA3E1D868"); 287 } 288 289 // test vectors from 290 // http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf 291 // section 2.8.1 292 unittest { 293 294 GHash gHash = GHash(cast(const(ubyte)[])x"AE19118C3B704FCE42AE0D15D2C15C7A"); 295 296 gHash.updateAAD(cast(const(ubyte)[])x"68F2E77696CE7AE8E2CA4EC588E54D00"); 297 gHash.updateAAD(cast(const(ubyte)[])x"2E58495C"); 298 299 gHash.updateCipherData(cast(const(ubyte)[])x"C31F53D99E5687F7365119B832D2AAE7"); 300 gHash.updateCipherData(cast(const(ubyte)[])x"0741D593F1F9E2AB3455779B078EB8FE"); 301 gHash.updateCipherData(cast(const(ubyte)[])x"ACDFEC1F8E3E5277F8180B43361F6512"); 302 gHash.updateCipherData(cast(const(ubyte)[])x"ADB16D2E38548A2C719DBA7228D840"); 303 304 ubyte[16] ghash; 305 gHash.doFinal(ghash); 306 307 assert(ghash == x"5AAA6FD11F06A18BE6E77EF2BC18AF93"); 308 } 309 310 /// http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf 311 /// test case 16 312 unittest { 313 314 // init gHash with H = acbef... 315 GHash gHash = GHash(cast(const(ubyte)[])x"acbef20579b4b8ebce889bac8732dad7"); 316 317 // process AAD 318 gHash.updateAAD(cast(const(ubyte)[])x"feedfacedeadbeeffeedfacedeadbeef"); 319 gHash.updateAAD(cast(const(ubyte)[])x"abaddad2"); 320 321 // process cipher data 322 gHash.updateCipherData(cast(const(ubyte)[])x"522dc1f099567d07f47f37a32a84427d"); 323 gHash.updateCipherData(cast(const(ubyte)[])x"643a8cdcbfe5c0c97598a2bd2555d1aa"); 324 gHash.updateCipherData(cast(const(ubyte)[])x"8cb08e48590dbb3da7b08b1056828838"); 325 gHash.updateCipherData(cast(const(ubyte)[])x"c5f61e6393ba7a0abcc9f662"); 326 327 // get the final hash value 328 ubyte[16] ghash; 329 gHash.doFinal(ghash); 330 331 assert(ghash == x"8bd0c4d8aacd391e67cca447e8c38f65"); 332 } 333 334 // http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf 335 // test case 16 336 // AAD and cipher data come out of order 337 unittest { 338 339 GHash gHash = GHash(cast(const(ubyte)[])x"acbef20579b4b8ebce889bac8732dad7"); 340 341 gHash.updateAAD(cast(const(ubyte)[])x"feedfacedeadbeeffeedfacedeadbeef"); 342 343 gHash.updateCipherData(cast(const(ubyte)[])x"522dc1f099567d07f47f37a32a84427d"); 344 gHash.updateCipherData(cast(const(ubyte)[])x"643a8cdcbfe5c0c97598a2bd2555d1aa"); 345 gHash.updateCipherData(cast(const(ubyte)[])x"8cb08e48590dbb3da7b08b1056828838"); 346 gHash.updateCipherData(cast(const(ubyte)[])x"c5f61e6393ba7a0abcc9f662"); 347 348 gHash.updateAAD(cast(const(ubyte)[])x"abaddad2"); 349 350 351 ubyte[16] ghash; 352 gHash.doFinal(ghash); 353 354 assert(ghash == cast(const(ubyte)[])x"8bd0c4d8aacd391e67cca447e8c38f65"); 355 356 // gHash should now be resetted, so do the same thing again 357 358 gHash.updateAAD(cast(const(ubyte)[])x"feedfacedeadbeeffeedfacedeadbeef"); 359 360 gHash.updateCipherData(cast(const(ubyte)[])x"522dc1f099567d07f47f37a32a84427d"); 361 gHash.updateCipherData(cast(const(ubyte)[])x"643a8cdcbfe5c0c97598a2bd2555d1aa"); 362 gHash.updateCipherData(cast(const(ubyte)[])x"8cb08e48590dbb3da7b08b1056828838"); 363 364 gHash.updateAAD(cast(const(ubyte)[])x"abaddad2"); 365 366 gHash.updateCipherData(cast(const(ubyte)[])x"c5f61e6393ba7a0abcc9f662"); 367 368 gHash.doFinal(ghash); 369 370 assert(ghash == x"8bd0c4d8aacd391e67cca447e8c38f65"); 371 } 372 373 } 374