1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package org.conscrypt; 18 19import java.io.IOException; 20import java.lang.reflect.Constructor; 21import java.lang.reflect.InvocationTargetException; 22import java.lang.reflect.Method; 23import java.security.AlgorithmParameters; 24import java.security.InvalidAlgorithmParameterException; 25import java.security.InvalidKeyException; 26import java.security.InvalidParameterException; 27import java.security.Key; 28import java.security.KeyFactory; 29import java.security.NoSuchAlgorithmException; 30import java.security.SecureRandom; 31import java.security.spec.AlgorithmParameterSpec; 32import java.security.spec.InvalidKeySpecException; 33import java.security.spec.InvalidParameterSpecException; 34import java.security.spec.PKCS8EncodedKeySpec; 35import java.security.spec.X509EncodedKeySpec; 36import java.util.Arrays; 37import java.util.Locale; 38import javax.crypto.BadPaddingException; 39import javax.crypto.Cipher; 40import javax.crypto.CipherSpi; 41import javax.crypto.IllegalBlockSizeException; 42import javax.crypto.NoSuchPaddingException; 43import javax.crypto.SecretKey; 44import javax.crypto.ShortBufferException; 45import javax.crypto.spec.IvParameterSpec; 46import javax.crypto.spec.SecretKeySpec; 47import org.conscrypt.util.ArrayUtils; 48import org.conscrypt.util.EmptyArray; 49import org.conscrypt.NativeConstants; 50import org.conscrypt.NativeRef.EVP_AEAD_CTX; 51import org.conscrypt.NativeRef.EVP_CIPHER_CTX; 52 53public abstract class OpenSSLCipher extends CipherSpi { 54 55 /** 56 * Modes that a block cipher may support. 57 */ 58 protected static enum Mode { 59 CBC, 60 CTR, 61 ECB, 62 GCM, 63 } 64 65 /** 66 * Paddings that a block cipher may support. 67 */ 68 protected static enum Padding { 69 NOPADDING, 70 PKCS5PADDING, 71 ISO10126PADDING, 72 } 73 74 /** 75 * The current cipher mode. 76 */ 77 protected Mode mode = Mode.ECB; 78 79 /** 80 * The current cipher padding. 81 */ 82 private Padding padding = Padding.PKCS5PADDING; 83 84 /** 85 * May be used when reseting the cipher instance after calling 86 * {@code doFinal}. 87 */ 88 protected byte[] encodedKey; 89 90 /** 91 * The Initial Vector (IV) used for the current cipher. 92 */ 93 protected byte[] iv; 94 95 /** 96 * Current cipher mode: encrypting or decrypting. 97 */ 98 private boolean encrypting; 99 100 /** 101 * The block size of the current cipher. 102 */ 103 private int blockSize; 104 105 protected OpenSSLCipher() { 106 } 107 108 protected OpenSSLCipher(Mode mode, Padding padding) { 109 this.mode = mode; 110 this.padding = padding; 111 blockSize = getCipherBlockSize(); 112 } 113 114 /** 115 * API-specific implementation of initializing the cipher. The 116 * {@link #isEncrypting()} function will tell whether it should be 117 * initialized for encryption or decryption. The {@code encodedKey} will be 118 * the bytes of a supported key size. 119 */ 120 protected abstract void engineInitInternal(byte[] encodedKey, AlgorithmParameterSpec params, 121 SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException; 122 123 /** 124 * API-specific implementation of updating the cipher. The 125 * {@code maximumLen} will be the maximum length of the output as returned 126 * by {@link #getOutputSizeForUpdate(int)}. The return value must be the 127 * number of bytes processed and placed into {@code output}. On error, an 128 * exception must be thrown. 129 */ 130 protected abstract int updateInternal(byte[] input, int inputOffset, int inputLen, 131 byte[] output, int outputOffset, int maximumLen) throws ShortBufferException; 132 133 /** 134 * API-specific implementation of the final block. The {@code maximumLen} 135 * will be the maximum length of the possible output as returned by 136 * {@link #getOutputSizeForFinal(int)}. The return value must be the number 137 * of bytes processed and placed into {@code output}. On error, an exception 138 * must be thrown. 139 */ 140 protected abstract int doFinalInternal(byte[] output, int outputOffset, int maximumLen) 141 throws IllegalBlockSizeException, BadPaddingException, ShortBufferException; 142 143 /** 144 * Returns the standard name for the particular algorithm. 145 */ 146 protected abstract String getBaseCipherName(); 147 148 /** 149 * Checks whether the cipher supports this particular {@code keySize} (in 150 * bytes) and throws {@code InvalidKeyException} if it doesn't. 151 */ 152 protected abstract void checkSupportedKeySize(int keySize) throws InvalidKeyException; 153 154 /** 155 * Checks whether the cipher supports this particular cipher {@code mode} 156 * and throws {@code NoSuchAlgorithmException} if it doesn't. 157 */ 158 protected abstract void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException; 159 160 /** 161 * Checks whether the cipher supports this particular cipher {@code padding} 162 * and throws {@code NoSuchPaddingException} if it doesn't. 163 */ 164 protected abstract void checkSupportedPadding(Padding padding) throws NoSuchPaddingException; 165 166 protected abstract int getCipherBlockSize(); 167 168 protected boolean supportsVariableSizeKey() { 169 return false; 170 } 171 172 protected boolean supportsVariableSizeIv() { 173 return false; 174 } 175 176 @Override 177 protected void engineSetMode(String modeStr) throws NoSuchAlgorithmException { 178 final Mode mode; 179 try { 180 mode = Mode.valueOf(modeStr.toUpperCase(Locale.US)); 181 } catch (IllegalArgumentException e) { 182 NoSuchAlgorithmException newE = new NoSuchAlgorithmException("No such mode: " + modeStr); 183 newE.initCause(e); 184 throw newE; 185 } 186 checkSupportedMode(mode); 187 this.mode = mode; 188 } 189 190 @Override 191 protected void engineSetPadding(String paddingStr) throws NoSuchPaddingException { 192 final String paddingStrUpper = paddingStr.toUpperCase(Locale.US); 193 final Padding padding; 194 try { 195 padding = Padding.valueOf(paddingStrUpper); 196 } catch (IllegalArgumentException e) { 197 NoSuchPaddingException newE = new NoSuchPaddingException("No such padding: " 198 + paddingStr); 199 newE.initCause(e); 200 throw newE; 201 } 202 checkSupportedPadding(padding); 203 this.padding = padding; 204 } 205 206 /** 207 * Returns the padding type for which this cipher is initialized. 208 */ 209 protected Padding getPadding() { 210 return padding; 211 } 212 213 @Override 214 protected int engineGetBlockSize() { 215 return blockSize; 216 } 217 218 /** 219 * The size of output if {@code doFinal()} is called with this 220 * {@code inputLen}. If padding is enabled and the size of the input puts it 221 * right at the block size, it will add another block for the padding. 222 */ 223 protected abstract int getOutputSizeForFinal(int inputLen); 224 225 /** 226 * The size of output if {@code update()} is called with this 227 * {@code inputLen}. If padding is enabled and the size of the input puts it 228 * right at the block size, it will add another block for the padding. 229 */ 230 protected abstract int getOutputSizeForUpdate(int inputLen); 231 232 @Override 233 protected int engineGetOutputSize(int inputLen) { 234 return getOutputSizeForFinal(inputLen); 235 } 236 237 @Override 238 protected byte[] engineGetIV() { 239 return iv; 240 } 241 242 @Override 243 protected AlgorithmParameters engineGetParameters() { 244 if (iv != null && iv.length > 0) { 245 try { 246 AlgorithmParameters params = AlgorithmParameters.getInstance(getBaseCipherName()); 247 params.init(iv); 248 return params; 249 } catch (NoSuchAlgorithmException e) { 250 return null; 251 } catch (IOException e) { 252 return null; 253 } 254 } 255 return null; 256 } 257 258 @Override 259 protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException { 260 checkAndSetEncodedKey(opmode, key); 261 try { 262 engineInitInternal(this.encodedKey, null, random); 263 } catch (InvalidAlgorithmParameterException e) { 264 // This can't actually happen since we pass in null. 265 throw new RuntimeException(e); 266 } 267 } 268 269 @Override 270 protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, 271 SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { 272 checkAndSetEncodedKey(opmode, key); 273 engineInitInternal(this.encodedKey, params, random); 274 } 275 276 @Override 277 protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) 278 throws InvalidKeyException, InvalidAlgorithmParameterException { 279 AlgorithmParameterSpec spec; 280 if (params != null) { 281 try { 282 spec = params.getParameterSpec(IvParameterSpec.class); 283 } catch (InvalidParameterSpecException e) { 284 throw new InvalidAlgorithmParameterException( 285 "Params must be convertible to IvParameterSpec", e); 286 } 287 } else { 288 spec = null; 289 } 290 291 engineInit(opmode, key, spec, random); 292 } 293 294 @Override 295 protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { 296 final int maximumLen = getOutputSizeForUpdate(inputLen); 297 298 /* See how large our output buffer would need to be. */ 299 final byte[] output; 300 if (maximumLen > 0) { 301 output = new byte[maximumLen]; 302 } else { 303 output = EmptyArray.BYTE; 304 } 305 306 final int bytesWritten; 307 try { 308 bytesWritten = updateInternal(input, inputOffset, inputLen, output, 0, maximumLen); 309 } catch (ShortBufferException e) { 310 /* This shouldn't happen. */ 311 throw new RuntimeException("calculated buffer size was wrong: " + maximumLen); 312 } 313 314 if (output.length == bytesWritten) { 315 return output; 316 } else if (bytesWritten == 0) { 317 return EmptyArray.BYTE; 318 } else { 319 return Arrays.copyOfRange(output, 0, bytesWritten); 320 } 321 } 322 323 @Override 324 protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, 325 int outputOffset) throws ShortBufferException { 326 final int maximumLen = getOutputSizeForUpdate(inputLen); 327 return updateInternal(input, inputOffset, inputLen, output, outputOffset, maximumLen); 328 } 329 330 @Override 331 protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) 332 throws IllegalBlockSizeException, BadPaddingException { 333 final int maximumLen = getOutputSizeForFinal(inputLen); 334 /* Assume that we'll output exactly on a byte boundary. */ 335 final byte[] output = new byte[maximumLen]; 336 337 int bytesWritten; 338 if (inputLen > 0) { 339 try { 340 bytesWritten = updateInternal(input, inputOffset, inputLen, output, 0, maximumLen); 341 } catch (ShortBufferException e) { 342 /* This should not happen since we sized our own buffer. */ 343 throw new RuntimeException("our calculated buffer was too small", e); 344 } 345 } else { 346 bytesWritten = 0; 347 } 348 349 try { 350 bytesWritten += doFinalInternal(output, bytesWritten, maximumLen - bytesWritten); 351 } catch (ShortBufferException e) { 352 /* This should not happen since we sized our own buffer. */ 353 throw new RuntimeException("our calculated buffer was too small", e); 354 } 355 356 if (bytesWritten == output.length) { 357 return output; 358 } else if (bytesWritten == 0) { 359 return EmptyArray.BYTE; 360 } else { 361 return Arrays.copyOfRange(output, 0, bytesWritten); 362 } 363 } 364 365 @Override 366 protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, 367 int outputOffset) throws ShortBufferException, IllegalBlockSizeException, 368 BadPaddingException { 369 if (output == null) { 370 throw new NullPointerException("output == null"); 371 } 372 373 int maximumLen = getOutputSizeForFinal(inputLen); 374 375 final int bytesWritten; 376 if (inputLen > 0) { 377 bytesWritten = updateInternal(input, inputOffset, inputLen, output, outputOffset, 378 maximumLen); 379 outputOffset += bytesWritten; 380 maximumLen -= bytesWritten; 381 } else { 382 bytesWritten = 0; 383 } 384 385 return bytesWritten + doFinalInternal(output, outputOffset, maximumLen); 386 } 387 388 @Override 389 protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException { 390 try { 391 byte[] encoded = key.getEncoded(); 392 return engineDoFinal(encoded, 0, encoded.length); 393 } catch (BadPaddingException e) { 394 IllegalBlockSizeException newE = new IllegalBlockSizeException(); 395 newE.initCause(e); 396 throw newE; 397 } 398 } 399 400 @Override 401 protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType) 402 throws InvalidKeyException, NoSuchAlgorithmException { 403 try { 404 byte[] encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length); 405 if (wrappedKeyType == Cipher.PUBLIC_KEY) { 406 KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); 407 return keyFactory.generatePublic(new X509EncodedKeySpec(encoded)); 408 } else if (wrappedKeyType == Cipher.PRIVATE_KEY) { 409 KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); 410 return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded)); 411 } else if (wrappedKeyType == Cipher.SECRET_KEY) { 412 return new SecretKeySpec(encoded, wrappedKeyAlgorithm); 413 } else { 414 throw new UnsupportedOperationException("wrappedKeyType == " + wrappedKeyType); 415 } 416 } catch (IllegalBlockSizeException e) { 417 throw new InvalidKeyException(e); 418 } catch (BadPaddingException e) { 419 throw new InvalidKeyException(e); 420 } catch (InvalidKeySpecException e) { 421 throw new InvalidKeyException(e); 422 } 423 } 424 425 private byte[] checkAndSetEncodedKey(int opmode, Key key) throws InvalidKeyException { 426 if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) { 427 encrypting = true; 428 } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) { 429 encrypting = false; 430 } else { 431 throw new InvalidParameterException("Unsupported opmode " + opmode); 432 } 433 434 if (!(key instanceof SecretKey)) { 435 throw new InvalidKeyException("Only SecretKey is supported"); 436 } 437 438 final byte[] encodedKey = key.getEncoded(); 439 if (encodedKey == null) { 440 throw new InvalidKeyException("key.getEncoded() == null"); 441 } 442 checkSupportedKeySize(encodedKey.length); 443 this.encodedKey = encodedKey; 444 return encodedKey; 445 } 446 447 protected boolean isEncrypting() { 448 return encrypting; 449 } 450 451 public static abstract class EVP_CIPHER extends OpenSSLCipher { 452 /** 453 * Native pointer for the OpenSSL EVP_CIPHER context. 454 */ 455 private final EVP_CIPHER_CTX cipherCtx = new EVP_CIPHER_CTX( 456 NativeCrypto.EVP_CIPHER_CTX_new()); 457 458 /** 459 * Whether the cipher has processed any data yet. EVP_CIPHER doesn't 460 * like calling "doFinal()" in decryption mode without processing any 461 * updates. 462 */ 463 protected boolean calledUpdate; 464 465 /** 466 * The block size of the current mode. 467 */ 468 private int modeBlockSize; 469 470 public EVP_CIPHER(Mode mode, Padding padding) { 471 super(mode, padding); 472 } 473 474 @Override 475 protected void engineInitInternal(byte[] encodedKey, AlgorithmParameterSpec params, 476 SecureRandom random) throws InvalidKeyException, 477 InvalidAlgorithmParameterException { 478 byte[] iv; 479 if (params instanceof IvParameterSpec) { 480 IvParameterSpec ivParams = (IvParameterSpec) params; 481 iv = ivParams.getIV(); 482 } else { 483 iv = null; 484 } 485 486 final long cipherType = NativeCrypto.EVP_get_cipherbyname(getCipherName( 487 encodedKey.length, mode)); 488 if (cipherType == 0) { 489 throw new InvalidAlgorithmParameterException("Cannot find name for key length = " 490 + (encodedKey.length * 8) + " and mode = " + mode); 491 } 492 493 final boolean encrypting = isEncrypting(); 494 495 final int expectedIvLength = NativeCrypto.EVP_CIPHER_iv_length(cipherType); 496 if (iv == null && expectedIvLength != 0) { 497 if (!encrypting) { 498 throw new InvalidAlgorithmParameterException("IV must be specified in " + mode 499 + " mode"); 500 } 501 502 iv = new byte[expectedIvLength]; 503 if (random == null) { 504 random = new SecureRandom(); 505 } 506 random.nextBytes(iv); 507 } else if (expectedIvLength == 0 && iv != null) { 508 throw new InvalidAlgorithmParameterException("IV not used in " + mode + " mode"); 509 } else if (iv != null && iv.length != expectedIvLength) { 510 throw new InvalidAlgorithmParameterException("expected IV length of " 511 + expectedIvLength + " but was " + iv.length); 512 } 513 514 this.iv = iv; 515 516 if (supportsVariableSizeKey()) { 517 NativeCrypto.EVP_CipherInit_ex(cipherCtx, cipherType, null, null, encrypting); 518 NativeCrypto.EVP_CIPHER_CTX_set_key_length(cipherCtx, encodedKey.length); 519 NativeCrypto.EVP_CipherInit_ex(cipherCtx, 0, encodedKey, iv, isEncrypting()); 520 } else { 521 NativeCrypto.EVP_CipherInit_ex(cipherCtx, cipherType, encodedKey, iv, encrypting); 522 } 523 524 // OpenSSL only supports PKCS5 Padding. 525 NativeCrypto 526 .EVP_CIPHER_CTX_set_padding(cipherCtx, getPadding() == Padding.PKCS5PADDING); 527 modeBlockSize = NativeCrypto.EVP_CIPHER_CTX_block_size(cipherCtx); 528 calledUpdate = false; 529 } 530 531 @Override 532 protected int updateInternal(byte[] input, int inputOffset, int inputLen, byte[] output, 533 int outputOffset, int maximumLen) throws ShortBufferException { 534 final int intialOutputOffset = outputOffset; 535 536 final int bytesLeft = output.length - outputOffset; 537 if (bytesLeft < maximumLen) { 538 throw new ShortBufferException("output buffer too small during update: " 539 + bytesLeft + " < " + maximumLen); 540 } 541 542 outputOffset += NativeCrypto.EVP_CipherUpdate(cipherCtx, output, outputOffset, input, 543 inputOffset, inputLen); 544 545 calledUpdate = true; 546 547 return outputOffset - intialOutputOffset; 548 } 549 550 @Override 551 protected int doFinalInternal(byte[] output, int outputOffset, int maximumLen) 552 throws IllegalBlockSizeException, BadPaddingException, ShortBufferException { 553 /* Remember this so we can tell how many characters were written. */ 554 final int initialOutputOffset = outputOffset; 555 556 /* 557 * If we're decrypting and haven't had any input, we should return 558 * null. Otherwise OpenSSL will complain if we call final. 559 */ 560 if (!isEncrypting() && !calledUpdate) { 561 return 0; 562 } 563 564 /* Allow OpenSSL to pad if necessary and clean up state. */ 565 final int bytesLeft = output.length - outputOffset; 566 final int writtenBytes; 567 if (bytesLeft >= maximumLen) { 568 writtenBytes = NativeCrypto.EVP_CipherFinal_ex(cipherCtx, output, outputOffset); 569 } else { 570 final byte[] lastBlock = new byte[maximumLen]; 571 writtenBytes = NativeCrypto.EVP_CipherFinal_ex(cipherCtx, lastBlock, 0); 572 if (writtenBytes > bytesLeft) { 573 throw new ShortBufferException("buffer is too short: " + writtenBytes + " > " 574 + bytesLeft); 575 } else if (writtenBytes > 0) { 576 System.arraycopy(lastBlock, 0, output, outputOffset, writtenBytes); 577 } 578 } 579 outputOffset += writtenBytes; 580 581 reset(); 582 583 return outputOffset - initialOutputOffset; 584 } 585 586 @Override 587 protected int getOutputSizeForFinal(int inputLen) { 588 if (modeBlockSize == 1) { 589 return inputLen; 590 } else { 591 final int buffered = NativeCrypto.get_EVP_CIPHER_CTX_buf_len(cipherCtx); 592 593 if (getPadding() == Padding.NOPADDING) { 594 return buffered + inputLen; 595 } else { 596 final boolean finalUsed = NativeCrypto.get_EVP_CIPHER_CTX_final_used(cipherCtx); 597 // There is an additional buffer containing the possible final block. 598 int totalLen = inputLen + buffered + (finalUsed ? modeBlockSize : 0); 599 // Extra block for remainder bytes plus padding. 600 // In case it's encrypting and there are no remainder bytes, add an extra block 601 // consisting only of padding. 602 totalLen += ((totalLen % modeBlockSize != 0) || isEncrypting()) 603 ? modeBlockSize : 0; 604 // The minimum multiple of {@code modeBlockSize} that can hold all the bytes. 605 return totalLen - (totalLen % modeBlockSize); 606 } 607 } 608 } 609 610 @Override 611 protected int getOutputSizeForUpdate(int inputLen) { 612 return getOutputSizeForFinal(inputLen); 613 } 614 615 /** 616 * Returns the OpenSSL cipher name for the particular {@code keySize} 617 * and cipher {@code mode}. 618 */ 619 protected abstract String getCipherName(int keySize, Mode mode); 620 621 /** 622 * Reset this Cipher instance state to process a new chunk of data. 623 */ 624 private void reset() { 625 NativeCrypto.EVP_CipherInit_ex(cipherCtx, 0, encodedKey, iv, isEncrypting()); 626 calledUpdate = false; 627 } 628 629 public static class AES extends EVP_CIPHER { 630 private static final int AES_BLOCK_SIZE = 16; 631 632 protected AES(Mode mode, Padding padding) { 633 super(mode, padding); 634 } 635 636 public static class CBC extends AES { 637 public CBC(Padding padding) { 638 super(Mode.CBC, padding); 639 } 640 641 public static class NoPadding extends CBC { 642 public NoPadding() { 643 super(Padding.NOPADDING); 644 } 645 } 646 647 public static class PKCS5Padding extends CBC { 648 public PKCS5Padding() { 649 super(Padding.PKCS5PADDING); 650 } 651 } 652 } 653 654 public static class CTR extends AES { 655 public CTR() { 656 super(Mode.CTR, Padding.NOPADDING); 657 } 658 } 659 660 public static class ECB extends AES { 661 public ECB(Padding padding) { 662 super(Mode.ECB, padding); 663 } 664 665 public static class NoPadding extends ECB { 666 public NoPadding() { 667 super(Padding.NOPADDING); 668 } 669 } 670 671 public static class PKCS5Padding extends ECB { 672 public PKCS5Padding() { 673 super(Padding.PKCS5PADDING); 674 } 675 } 676 } 677 678 @Override 679 protected void checkSupportedKeySize(int keyLength) throws InvalidKeyException { 680 switch (keyLength) { 681 case 16: // AES 128 682 case 24: // AES 192 683 case 32: // AES 256 684 return; 685 default: 686 throw new InvalidKeyException("Unsupported key size: " + keyLength 687 + " bytes"); 688 } 689 } 690 691 @Override 692 protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException { 693 switch (mode) { 694 case CBC: 695 case CTR: 696 case ECB: 697 return; 698 default: 699 throw new NoSuchAlgorithmException("Unsupported mode " + mode.toString()); 700 } 701 } 702 703 @Override 704 protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException { 705 switch (padding) { 706 case NOPADDING: 707 case PKCS5PADDING: 708 return; 709 default: 710 throw new NoSuchPaddingException("Unsupported padding " 711 + padding.toString()); 712 } 713 } 714 715 @Override 716 protected String getBaseCipherName() { 717 return "AES"; 718 } 719 720 @Override 721 protected String getCipherName(int keyLength, Mode mode) { 722 return "aes-" + (keyLength * 8) + "-" + mode.toString().toLowerCase(Locale.US); 723 } 724 725 @Override 726 protected int getCipherBlockSize() { 727 return AES_BLOCK_SIZE; 728 } 729 } 730 731 public static class DESEDE extends EVP_CIPHER { 732 private static int DES_BLOCK_SIZE = 8; 733 734 public DESEDE(Mode mode, Padding padding) { 735 super(mode, padding); 736 } 737 738 public static class CBC extends DESEDE { 739 public CBC(Padding padding) { 740 super(Mode.CBC, padding); 741 } 742 743 public static class NoPadding extends CBC { 744 public NoPadding() { 745 super(Padding.NOPADDING); 746 } 747 } 748 749 public static class PKCS5Padding extends CBC { 750 public PKCS5Padding() { 751 super(Padding.PKCS5PADDING); 752 } 753 } 754 } 755 756 @Override 757 protected String getBaseCipherName() { 758 return "DESede"; 759 } 760 761 @Override 762 protected String getCipherName(int keySize, Mode mode) { 763 final String baseCipherName; 764 if (keySize == 16) { 765 baseCipherName = "des-ede"; 766 } else { 767 baseCipherName = "des-ede3"; 768 } 769 770 return baseCipherName + "-" + mode.toString().toLowerCase(Locale.US); 771 } 772 773 @Override 774 protected void checkSupportedKeySize(int keySize) throws InvalidKeyException { 775 if (keySize != 16 && keySize != 24) { 776 throw new InvalidKeyException("key size must be 128 or 192 bits"); 777 } 778 } 779 780 @Override 781 protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException { 782 if (mode != Mode.CBC) { 783 throw new NoSuchAlgorithmException("Unsupported mode " + mode.toString()); 784 } 785 } 786 787 @Override 788 protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException { 789 switch (padding) { 790 case NOPADDING: 791 case PKCS5PADDING: 792 return; 793 default: 794 throw new NoSuchPaddingException("Unsupported padding " 795 + padding.toString()); 796 } 797 } 798 799 @Override 800 protected int getCipherBlockSize() { 801 return DES_BLOCK_SIZE; 802 } 803 } 804 805 public static class ARC4 extends EVP_CIPHER { 806 public ARC4() { 807 // Modes and padding don't make sense for ARC4. 808 super(Mode.ECB, Padding.NOPADDING); 809 } 810 811 @Override 812 protected String getBaseCipherName() { 813 return "ARCFOUR"; 814 } 815 816 @Override 817 protected String getCipherName(int keySize, Mode mode) { 818 return "rc4"; 819 } 820 821 @Override 822 protected void checkSupportedKeySize(int keySize) throws InvalidKeyException { 823 } 824 825 @Override 826 protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException { 827 throw new NoSuchAlgorithmException("ARC4 does not support modes"); 828 } 829 830 @Override 831 protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException { 832 throw new NoSuchPaddingException("ARC4 does not support padding"); 833 } 834 835 @Override 836 protected int getCipherBlockSize() { 837 return 0; 838 } 839 840 @Override 841 protected boolean supportsVariableSizeKey() { 842 return true; 843 } 844 } 845 } 846 847 public static abstract class EVP_AEAD extends OpenSSLCipher { 848 /** 849 * The default tag size when one is not specified. Default to 850 * full-length tags (128-bits or 16 octets). 851 */ 852 private static final int DEFAULT_TAG_SIZE_BITS = 16 * 8; 853 854 /** 855 * Keeps track of the last used block size. 856 */ 857 private static int lastGlobalMessageSize = 32; 858 859 /** 860 * The byte array containing the bytes written. 861 */ 862 protected byte[] buf; 863 864 /** 865 * The number of bytes written. 866 */ 867 protected int bufCount; 868 869 /** 870 * AEAD cipher reference. 871 */ 872 protected long evpAead; 873 874 /** 875 * Additional authenticated data. 876 */ 877 private byte[] aad; 878 879 /** 880 * The length of the AEAD cipher tag in bytes. 881 */ 882 private int tagLengthInBytes; 883 884 public EVP_AEAD(Mode mode) { 885 super(mode, Padding.NOPADDING); 886 } 887 888 private void expand(int i) { 889 /* Can the buffer handle i more bytes, if not expand it */ 890 if (bufCount + i <= buf.length) { 891 return; 892 } 893 894 byte[] newbuf = new byte[(bufCount + i) * 2]; 895 System.arraycopy(buf, 0, newbuf, 0, bufCount); 896 buf = newbuf; 897 } 898 899 private void reset() { 900 aad = null; 901 final int lastBufSize = lastGlobalMessageSize; 902 if (buf == null) { 903 buf = new byte[lastBufSize]; 904 } else if (bufCount > 0 && bufCount != lastBufSize) { 905 lastGlobalMessageSize = bufCount; 906 if (buf.length != bufCount) { 907 buf = new byte[bufCount]; 908 } 909 } 910 bufCount = 0; 911 } 912 913 @Override 914 protected void engineInitInternal(byte[] encodedKey, AlgorithmParameterSpec params, 915 SecureRandom random) throws InvalidKeyException, 916 InvalidAlgorithmParameterException { 917 byte[] iv; 918 final int tagLenBits; 919 if (params == null) { 920 iv = null; 921 tagLenBits = DEFAULT_TAG_SIZE_BITS; 922 } else { 923 GCMParameters gcmParams = Platform.fromGCMParameterSpec(params); 924 if (gcmParams != null) { 925 iv = gcmParams.getIV(); 926 tagLenBits = gcmParams.getTLen(); 927 } else if (params instanceof IvParameterSpec) { 928 IvParameterSpec ivParams = (IvParameterSpec) params; 929 iv = ivParams.getIV(); 930 tagLenBits = DEFAULT_TAG_SIZE_BITS; 931 } else { 932 iv = null; 933 tagLenBits = DEFAULT_TAG_SIZE_BITS; 934 } 935 } 936 937 if (tagLenBits % 8 != 0) { 938 throw new InvalidAlgorithmParameterException( 939 "Tag length must be a multiple of 8; was " + tagLengthInBytes); 940 } 941 942 tagLengthInBytes = tagLenBits / 8; 943 944 final boolean encrypting = isEncrypting(); 945 946 evpAead = getEVP_AEAD(encodedKey.length); 947 948 final int expectedIvLength = NativeCrypto.EVP_AEAD_nonce_length(evpAead); 949 if (iv == null && expectedIvLength != 0) { 950 if (!encrypting) { 951 throw new InvalidAlgorithmParameterException("IV must be specified in " + mode 952 + " mode"); 953 } 954 955 iv = new byte[expectedIvLength]; 956 if (random == null) { 957 random = new SecureRandom(); 958 } 959 random.nextBytes(iv); 960 } else if (expectedIvLength == 0 && iv != null) { 961 throw new InvalidAlgorithmParameterException("IV not used in " + mode + " mode"); 962 } else if (iv != null && iv.length != expectedIvLength) { 963 throw new InvalidAlgorithmParameterException("Expected IV length of " 964 + expectedIvLength + " but was " + iv.length); 965 } 966 967 this.iv = iv; 968 reset(); 969 } 970 971 @Override 972 protected int updateInternal(byte[] input, int inputOffset, int inputLen, byte[] output, 973 int outputOffset, int maximumLen) throws ShortBufferException { 974 if (buf == null) { 975 throw new IllegalStateException("Cipher not initialized"); 976 } 977 978 ArrayUtils.checkOffsetAndCount(input.length, inputOffset, inputLen); 979 if (inputLen > 0) { 980 expand(inputLen); 981 System.arraycopy(input, inputOffset, buf, this.bufCount, inputLen); 982 this.bufCount += inputLen; 983 } 984 return 0; 985 } 986 987 @Override 988 protected int doFinalInternal(byte[] output, int outputOffset, int maximumLen) 989 throws IllegalBlockSizeException, BadPaddingException { 990 EVP_AEAD_CTX cipherCtx = new EVP_AEAD_CTX(NativeCrypto.EVP_AEAD_CTX_init(evpAead, 991 encodedKey, tagLengthInBytes)); 992 final int bytesWritten; 993 try { 994 if (isEncrypting()) { 995 bytesWritten = NativeCrypto.EVP_AEAD_CTX_seal(cipherCtx, output, outputOffset, 996 iv, buf, 0, bufCount, aad); 997 } else { 998 bytesWritten = NativeCrypto.EVP_AEAD_CTX_open(cipherCtx, output, outputOffset, 999 iv, buf, 0, bufCount, aad); 1000 } 1001 } catch (BadPaddingException e) { 1002 Constructor<?> aeadBadTagConstructor = null; 1003 try { 1004 aeadBadTagConstructor = Class.forName("javax.crypto.AEADBadTagException") 1005 .getConstructor(String.class); 1006 } catch (ClassNotFoundException | NoSuchMethodException e2) { 1007 } 1008 1009 if (aeadBadTagConstructor != null) { 1010 BadPaddingException badTagException = null; 1011 try { 1012 badTagException = (BadPaddingException) aeadBadTagConstructor.newInstance(e 1013 .getMessage()); 1014 badTagException.initCause(e.getCause()); 1015 } catch (IllegalAccessException | InstantiationException e2) { 1016 // Fall through 1017 } catch (InvocationTargetException e2) { 1018 throw (BadPaddingException) new BadPaddingException().initCause(e2 1019 .getTargetException()); 1020 } 1021 if (badTagException != null) { 1022 throw badTagException; 1023 } 1024 } 1025 throw e; 1026 } 1027 reset(); 1028 return bytesWritten; 1029 } 1030 1031 @Override 1032 protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException { 1033 if (padding != Padding.NOPADDING) { 1034 throw new NoSuchPaddingException("Must be NoPadding for AEAD ciphers"); 1035 } 1036 } 1037 1038 @Override 1039 protected int getOutputSizeForFinal(int inputLen) { 1040 return bufCount + inputLen 1041 + (isEncrypting() ? NativeCrypto.EVP_AEAD_max_overhead(evpAead) : 0); 1042 } 1043 1044 /* @Override */ 1045 protected void engineUpdateAAD(byte[] input, int inputOffset, int inputLen) { 1046 if (aad == null) { 1047 aad = Arrays.copyOfRange(input, inputOffset, inputOffset + inputLen); 1048 } else { 1049 int newSize = aad.length + inputLen; 1050 byte[] newaad = new byte[newSize]; 1051 System.arraycopy(aad, 0, newaad, 0, aad.length); 1052 System.arraycopy(input, inputOffset, newaad, aad.length, inputLen); 1053 aad = newaad; 1054 } 1055 } 1056 1057 @Override 1058 protected AlgorithmParameters engineGetParameters() { 1059 // iv will be non-null after initialization. 1060 if (iv == null) { 1061 return null; 1062 } 1063 1064 AlgorithmParameterSpec spec = Platform.toGCMParameterSpec(tagLengthInBytes * 8, iv); 1065 if (spec == null) { 1066 // The platform doesn't support GCMParameterSpec. Fall back to 1067 // the generic AES parameters so at least the caller can get the 1068 // IV. 1069 return super.engineGetParameters(); 1070 } 1071 1072 try { 1073 AlgorithmParameters params = AlgorithmParameters.getInstance("GCM"); 1074 params.init(spec); 1075 return params; 1076 } catch (NoSuchAlgorithmException | InvalidParameterSpecException e) { 1077 // This may happen since Conscrypt doesn't provide this itself. 1078 return null; 1079 } 1080 } 1081 1082 protected abstract long getEVP_AEAD(int keyLength) throws InvalidKeyException; 1083 1084 public abstract static class AES extends EVP_AEAD { 1085 private static final int AES_BLOCK_SIZE = 16; 1086 1087 protected AES(Mode mode) { 1088 super(mode); 1089 } 1090 1091 @Override 1092 protected void checkSupportedKeySize(int keyLength) throws InvalidKeyException { 1093 switch (keyLength) { 1094 case 16: // AES 128 1095 case 32: // AES 256 1096 return; 1097 default: 1098 throw new InvalidKeyException("Unsupported key size: " + keyLength 1099 + " bytes (must be 16 or 32)"); 1100 } 1101 } 1102 1103 @Override 1104 protected String getBaseCipherName() { 1105 return "AES"; 1106 } 1107 1108 @Override 1109 protected int getCipherBlockSize() { 1110 return AES_BLOCK_SIZE; 1111 } 1112 1113 /** 1114 * AEAD buffers everything until a final output. 1115 */ 1116 @Override 1117 protected int getOutputSizeForUpdate(int inputLen) { 1118 return 0; 1119 } 1120 1121 public static class GCM extends AES { 1122 public GCM() { 1123 super(Mode.GCM); 1124 } 1125 1126 @Override 1127 protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException { 1128 if (mode != Mode.GCM) { 1129 throw new NoSuchAlgorithmException("Mode must be GCM"); 1130 } 1131 } 1132 1133 @Override 1134 protected long getEVP_AEAD(int keyLength) throws InvalidKeyException { 1135 if (keyLength == 16) { 1136 return NativeCrypto.EVP_aead_aes_128_gcm(); 1137 } else if (keyLength == 32) { 1138 return NativeCrypto.EVP_aead_aes_256_gcm(); 1139 } else { 1140 throw new RuntimeException("Unexpected key length: " + keyLength); 1141 } 1142 } 1143 } 1144 } 1145 } 1146} 1147