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