1package org.bouncycastle.crypto.modes; 2 3import org.bouncycastle.crypto.BlockCipher; 4import org.bouncycastle.crypto.CipherParameters; 5import org.bouncycastle.crypto.DataLengthException; 6import org.bouncycastle.crypto.InvalidCipherTextException; 7import org.bouncycastle.crypto.OutputLengthException; 8import org.bouncycastle.crypto.modes.gcm.GCMExponentiator; 9import org.bouncycastle.crypto.modes.gcm.GCMMultiplier; 10import org.bouncycastle.crypto.modes.gcm.GCMUtil; 11import org.bouncycastle.crypto.modes.gcm.Tables1kGCMExponentiator; 12import org.bouncycastle.crypto.modes.gcm.Tables8kGCMMultiplier; 13import org.bouncycastle.crypto.params.AEADParameters; 14import org.bouncycastle.crypto.params.KeyParameter; 15import org.bouncycastle.crypto.params.ParametersWithIV; 16import org.bouncycastle.util.Arrays; 17import org.bouncycastle.util.Pack; 18 19/** 20 * Implements the Galois/Counter mode (GCM) detailed in 21 * NIST Special Publication 800-38D. 22 */ 23public class GCMBlockCipher 24 implements AEADBlockCipher 25{ 26 private static final int BLOCK_SIZE = 16; 27 // BEGIN android-added 28 // 2^36-32 : limitation imposed by NIST GCM as otherwise the counter is wrapped and it can leak 29 // plaintext and authentication key 30 private static final long MAX_INPUT_SIZE = 68719476704L; 31 // END android-added 32 33 // not final due to a compiler bug 34 private BlockCipher cipher; 35 private GCMMultiplier multiplier; 36 private GCMExponentiator exp; 37 38 // These fields are set by init and not modified by processing 39 private boolean forEncryption; 40 private int macSize; 41 private byte[] lastKey; 42 private byte[] nonce; 43 private byte[] initialAssociatedText; 44 private byte[] H; 45 private byte[] J0; 46 47 // These fields are modified during processing 48 private byte[] bufBlock; 49 private byte[] macBlock; 50 private byte[] S, S_at, S_atPre; 51 private byte[] counter; 52 private int blocksRemaining; 53 private int bufOff; 54 private long totalLength; 55 private byte[] atBlock; 56 private int atBlockPos; 57 private long atLength; 58 private long atLengthPre; 59 60 public GCMBlockCipher(BlockCipher c) 61 { 62 this(c, null); 63 } 64 65 public GCMBlockCipher(BlockCipher c, GCMMultiplier m) 66 { 67 if (c.getBlockSize() != BLOCK_SIZE) 68 { 69 throw new IllegalArgumentException( 70 "cipher required with a block size of " + BLOCK_SIZE + "."); 71 } 72 73 if (m == null) 74 { 75 // TODO Consider a static property specifying default multiplier 76 m = new Tables8kGCMMultiplier(); 77 } 78 79 this.cipher = c; 80 this.multiplier = m; 81 } 82 83 public BlockCipher getUnderlyingCipher() 84 { 85 return cipher; 86 } 87 88 public String getAlgorithmName() 89 { 90 return cipher.getAlgorithmName() + "/GCM"; 91 } 92 93 /** 94 * NOTE: MAC sizes from 32 bits to 128 bits (must be a multiple of 8) are supported. The default is 128 bits. 95 * Sizes less than 96 are not recommended, but are supported for specialized applications. 96 */ 97 public void init(boolean forEncryption, CipherParameters params) 98 throws IllegalArgumentException 99 { 100 this.forEncryption = forEncryption; 101 this.macBlock = null; 102 103 KeyParameter keyParam; 104 byte[] newNonce = null; 105 106 if (params instanceof AEADParameters) 107 { 108 AEADParameters param = (AEADParameters)params; 109 110 newNonce = param.getNonce(); 111 initialAssociatedText = param.getAssociatedText(); 112 113 int macSizeBits = param.getMacSize(); 114 if (macSizeBits < 32 || macSizeBits > 128 || macSizeBits % 8 != 0) 115 { 116 throw new IllegalArgumentException("Invalid value for MAC size: " + macSizeBits); 117 } 118 119 macSize = macSizeBits / 8; 120 keyParam = param.getKey(); 121 } 122 else if (params instanceof ParametersWithIV) 123 { 124 ParametersWithIV param = (ParametersWithIV)params; 125 126 newNonce = param.getIV(); 127 initialAssociatedText = null; 128 macSize = 16; 129 keyParam = (KeyParameter)param.getParameters(); 130 } 131 else 132 { 133 throw new IllegalArgumentException("invalid parameters passed to GCM"); 134 } 135 136 int bufLength = forEncryption ? BLOCK_SIZE : (BLOCK_SIZE + macSize); 137 this.bufBlock = new byte[bufLength]; 138 139 if (newNonce == null || newNonce.length < 1) 140 { 141 throw new IllegalArgumentException("IV must be at least 1 byte"); 142 } 143 144 if (forEncryption) 145 { 146 if (nonce != null && Arrays.areEqual(nonce, newNonce)) 147 { 148 if (keyParam == null) 149 { 150 throw new IllegalArgumentException("cannot reuse nonce for GCM encryption"); 151 } 152 if (lastKey != null && Arrays.areEqual(lastKey, keyParam.getKey())) 153 { 154 throw new IllegalArgumentException("cannot reuse nonce for GCM encryption"); 155 } 156 } 157 } 158 159 nonce = newNonce; 160 if (keyParam != null) 161 { 162 lastKey = keyParam.getKey(); 163 } 164 165 // TODO Restrict macSize to 16 if nonce length not 12? 166 167 // Cipher always used in forward mode 168 // if keyParam is null we're reusing the last key. 169 if (keyParam != null) 170 { 171 cipher.init(true, keyParam); 172 173 this.H = new byte[BLOCK_SIZE]; 174 cipher.processBlock(H, 0, H, 0); 175 176 // GCMMultiplier tables don't change unless the key changes (and are expensive to init) 177 multiplier.init(H); 178 exp = null; 179 } 180 else if (this.H == null) 181 { 182 throw new IllegalArgumentException("Key must be specified in initial init"); 183 } 184 185 this.J0 = new byte[BLOCK_SIZE]; 186 187 if (nonce.length == 12) 188 { 189 System.arraycopy(nonce, 0, J0, 0, nonce.length); 190 this.J0[BLOCK_SIZE - 1] = 0x01; 191 } 192 else 193 { 194 gHASH(J0, nonce, nonce.length); 195 byte[] X = new byte[BLOCK_SIZE]; 196 Pack.longToBigEndian((long)nonce.length * 8, X, 8); 197 gHASHBlock(J0, X); 198 } 199 200 this.S = new byte[BLOCK_SIZE]; 201 this.S_at = new byte[BLOCK_SIZE]; 202 this.S_atPre = new byte[BLOCK_SIZE]; 203 this.atBlock = new byte[BLOCK_SIZE]; 204 this.atBlockPos = 0; 205 this.atLength = 0; 206 this.atLengthPre = 0; 207 this.counter = Arrays.clone(J0); 208 this.blocksRemaining = -2; // page 8, len(P) <= 2^39 - 256, 1 block used by tag but done on J0 209 this.bufOff = 0; 210 this.totalLength = 0; 211 212 if (initialAssociatedText != null) 213 { 214 processAADBytes(initialAssociatedText, 0, initialAssociatedText.length); 215 } 216 } 217 218 public byte[] getMac() 219 { 220 if (macBlock == null) 221 { 222 return new byte[macSize]; 223 } 224 return Arrays.clone(macBlock); 225 } 226 227 public int getOutputSize(int len) 228 { 229 int totalData = len + bufOff; 230 231 if (forEncryption) 232 { 233 return totalData + macSize; 234 } 235 236 return totalData < macSize ? 0 : totalData - macSize; 237 } 238 239 // BEGIN android-added 240 /** Helper used to ensure that {@link #MAX_INPUT_SIZE} is not exceeded. */ 241 private long getTotalInputSizeAfterNewInput(int newInputLen) 242 { 243 return totalLength + newInputLen + bufOff; 244 } 245 // END android-added 246 247 public int getUpdateOutputSize(int len) 248 { 249 int totalData = len + bufOff; 250 if (!forEncryption) 251 { 252 if (totalData < macSize) 253 { 254 return 0; 255 } 256 totalData -= macSize; 257 } 258 return totalData - totalData % BLOCK_SIZE; 259 } 260 261 public void processAADByte(byte in) 262 { 263 // BEGIN android-added 264 if (getTotalInputSizeAfterNewInput(1) > MAX_INPUT_SIZE) { 265 throw new DataLengthException("Input exceeded " + MAX_INPUT_SIZE + " bytes"); 266 } 267 // END android-added 268 atBlock[atBlockPos] = in; 269 if (++atBlockPos == BLOCK_SIZE) 270 { 271 // Hash each block as it fills 272 gHASHBlock(S_at, atBlock); 273 atBlockPos = 0; 274 atLength += BLOCK_SIZE; 275 } 276 } 277 278 public void processAADBytes(byte[] in, int inOff, int len) 279 { 280 // BEGIN android-added 281 if (getTotalInputSizeAfterNewInput(len) > MAX_INPUT_SIZE) { 282 throw new DataLengthException("Input exceeded " + MAX_INPUT_SIZE + " bytes"); 283 } 284 // END android-added 285 for (int i = 0; i < len; ++i) 286 { 287 atBlock[atBlockPos] = in[inOff + i]; 288 if (++atBlockPos == BLOCK_SIZE) 289 { 290 // Hash each block as it fills 291 gHASHBlock(S_at, atBlock); 292 atBlockPos = 0; 293 atLength += BLOCK_SIZE; 294 } 295 } 296 } 297 298 private void initCipher() 299 { 300 if (atLength > 0) 301 { 302 System.arraycopy(S_at, 0, S_atPre, 0, BLOCK_SIZE); 303 atLengthPre = atLength; 304 } 305 306 // Finish hash for partial AAD block 307 if (atBlockPos > 0) 308 { 309 gHASHPartial(S_atPre, atBlock, 0, atBlockPos); 310 atLengthPre += atBlockPos; 311 } 312 313 if (atLengthPre > 0) 314 { 315 System.arraycopy(S_atPre, 0, S, 0, BLOCK_SIZE); 316 } 317 } 318 319 public int processByte(byte in, byte[] out, int outOff) 320 throws DataLengthException 321 { 322 // BEGIN android-added 323 if (getTotalInputSizeAfterNewInput(1) > MAX_INPUT_SIZE) { 324 throw new DataLengthException("Input exceeded " + MAX_INPUT_SIZE + " bytes"); 325 } 326 // END android-added 327 bufBlock[bufOff] = in; 328 if (++bufOff == bufBlock.length) 329 { 330 outputBlock(out, outOff); 331 return BLOCK_SIZE; 332 } 333 return 0; 334 } 335 336 public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) 337 throws DataLengthException 338 { 339 // BEGIN android-added 340 if (getTotalInputSizeAfterNewInput(len) > MAX_INPUT_SIZE) { 341 throw new DataLengthException("Input exceeded " + MAX_INPUT_SIZE + " bytes"); 342 } 343 // END android-added 344 if (in.length < (inOff + len)) 345 { 346 throw new DataLengthException("Input buffer too short"); 347 } 348 int resultLen = 0; 349 350 for (int i = 0; i < len; ++i) 351 { 352 bufBlock[bufOff] = in[inOff + i]; 353 if (++bufOff == bufBlock.length) 354 { 355 outputBlock(out, outOff + resultLen); 356 resultLen += BLOCK_SIZE; 357 } 358 } 359 360 return resultLen; 361 } 362 363 private void outputBlock(byte[] output, int offset) 364 { 365 if (output.length < (offset + BLOCK_SIZE)) 366 { 367 throw new OutputLengthException("Output buffer too short"); 368 } 369 if (totalLength == 0) 370 { 371 initCipher(); 372 } 373 gCTRBlock(bufBlock, output, offset); 374 if (forEncryption) 375 { 376 bufOff = 0; 377 } 378 else 379 { 380 System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize); 381 bufOff = macSize; 382 } 383 } 384 385 public int doFinal(byte[] out, int outOff) 386 throws IllegalStateException, InvalidCipherTextException 387 { 388 if (totalLength == 0) 389 { 390 initCipher(); 391 } 392 393 int extra = bufOff; 394 395 if (forEncryption) 396 { 397 if (out.length < (outOff + extra + macSize)) 398 { 399 throw new OutputLengthException("Output buffer too short"); 400 } 401 } 402 else 403 { 404 if (extra < macSize) 405 { 406 throw new InvalidCipherTextException("data too short"); 407 } 408 extra -= macSize; 409 410 if (out.length < (outOff + extra)) 411 { 412 throw new OutputLengthException("Output buffer too short"); 413 } 414 } 415 416 if (extra > 0) 417 { 418 gCTRPartial(bufBlock, 0, extra, out, outOff); 419 } 420 421 atLength += atBlockPos; 422 423 if (atLength > atLengthPre) 424 { 425 /* 426 * Some AAD was sent after the cipher started. We determine the difference b/w the hash value 427 * we actually used when the cipher started (S_atPre) and the final hash value calculated (S_at). 428 * Then we carry this difference forward by multiplying by H^c, where c is the number of (full or 429 * partial) cipher-text blocks produced, and adjust the current hash. 430 */ 431 432 // Finish hash for partial AAD block 433 if (atBlockPos > 0) 434 { 435 gHASHPartial(S_at, atBlock, 0, atBlockPos); 436 } 437 438 // Find the difference between the AAD hashes 439 if (atLengthPre > 0) 440 { 441 GCMUtil.xor(S_at, S_atPre); 442 } 443 444 // Number of cipher-text blocks produced 445 long c = ((totalLength * 8) + 127) >>> 7; 446 447 // Calculate the adjustment factor 448 byte[] H_c = new byte[16]; 449 if (exp == null) 450 { 451 exp = new Tables1kGCMExponentiator(); 452 exp.init(H); 453 } 454 exp.exponentiateX(c, H_c); 455 456 // Carry the difference forward 457 GCMUtil.multiply(S_at, H_c); 458 459 // Adjust the current hash 460 GCMUtil.xor(S, S_at); 461 } 462 463 // Final gHASH 464 byte[] X = new byte[BLOCK_SIZE]; 465 Pack.longToBigEndian(atLength * 8, X, 0); 466 Pack.longToBigEndian(totalLength * 8, X, 8); 467 468 gHASHBlock(S, X); 469 470 // T = MSBt(GCTRk(J0,S)) 471 byte[] tag = new byte[BLOCK_SIZE]; 472 cipher.processBlock(J0, 0, tag, 0); 473 GCMUtil.xor(tag, S); 474 475 int resultLen = extra; 476 477 // We place into macBlock our calculated value for T 478 this.macBlock = new byte[macSize]; 479 System.arraycopy(tag, 0, macBlock, 0, macSize); 480 481 if (forEncryption) 482 { 483 // Append T to the message 484 System.arraycopy(macBlock, 0, out, outOff + bufOff, macSize); 485 resultLen += macSize; 486 } 487 else 488 { 489 // Retrieve the T value from the message and compare to calculated one 490 byte[] msgMac = new byte[macSize]; 491 System.arraycopy(bufBlock, extra, msgMac, 0, macSize); 492 if (!Arrays.constantTimeAreEqual(this.macBlock, msgMac)) 493 { 494 throw new InvalidCipherTextException("mac check in GCM failed"); 495 } 496 } 497 498 reset(false); 499 500 return resultLen; 501 } 502 503 public void reset() 504 { 505 reset(true); 506 } 507 508 private void reset( 509 boolean clearMac) 510 { 511 cipher.reset(); 512 513 // note: we do not reset the nonce. 514 515 S = new byte[BLOCK_SIZE]; 516 S_at = new byte[BLOCK_SIZE]; 517 S_atPre = new byte[BLOCK_SIZE]; 518 atBlock = new byte[BLOCK_SIZE]; 519 atBlockPos = 0; 520 atLength = 0; 521 atLengthPre = 0; 522 counter = Arrays.clone(J0); 523 blocksRemaining = -2; 524 bufOff = 0; 525 totalLength = 0; 526 527 if (bufBlock != null) 528 { 529 Arrays.fill(bufBlock, (byte)0); 530 } 531 532 if (clearMac) 533 { 534 macBlock = null; 535 } 536 537 if (initialAssociatedText != null) 538 { 539 processAADBytes(initialAssociatedText, 0, initialAssociatedText.length); 540 } 541 } 542 543 private void gCTRBlock(byte[] block, byte[] out, int outOff) 544 { 545 byte[] tmp = getNextCounterBlock(); 546 547 GCMUtil.xor(tmp, block); 548 System.arraycopy(tmp, 0, out, outOff, BLOCK_SIZE); 549 550 gHASHBlock(S, forEncryption ? tmp : block); 551 552 totalLength += BLOCK_SIZE; 553 } 554 555 private void gCTRPartial(byte[] buf, int off, int len, byte[] out, int outOff) 556 { 557 byte[] tmp = getNextCounterBlock(); 558 559 GCMUtil.xor(tmp, buf, off, len); 560 System.arraycopy(tmp, 0, out, outOff, len); 561 562 gHASHPartial(S, forEncryption ? tmp : buf, 0, len); 563 564 totalLength += len; 565 } 566 567 private void gHASH(byte[] Y, byte[] b, int len) 568 { 569 for (int pos = 0; pos < len; pos += BLOCK_SIZE) 570 { 571 int num = Math.min(len - pos, BLOCK_SIZE); 572 gHASHPartial(Y, b, pos, num); 573 } 574 } 575 576 private void gHASHBlock(byte[] Y, byte[] b) 577 { 578 GCMUtil.xor(Y, b); 579 multiplier.multiplyH(Y); 580 } 581 582 private void gHASHPartial(byte[] Y, byte[] b, int off, int len) 583 { 584 GCMUtil.xor(Y, b, off, len); 585 multiplier.multiplyH(Y); 586 } 587 588 private byte[] getNextCounterBlock() 589 { 590 if (blocksRemaining == 0) 591 { 592 throw new IllegalStateException("Attempt to process too many blocks"); 593 } 594 blocksRemaining--; 595 596 int c = 1; 597 c += counter[15] & 0xFF; counter[15] = (byte)c; c >>>= 8; 598 c += counter[14] & 0xFF; counter[14] = (byte)c; c >>>= 8; 599 c += counter[13] & 0xFF; counter[13] = (byte)c; c >>>= 8; 600 c += counter[12] & 0xFF; counter[12] = (byte)c; 601 602 byte[] tmp = new byte[BLOCK_SIZE]; 603 // TODO Sure would be nice if ciphers could operate on int[] 604 cipher.processBlock(counter, 0, tmp, 0); 605 return tmp; 606 } 607} 608