OpenSSLCipher.java revision fac659c013ec9c2783f60afce39e83eb107f117d
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.apache.harmony.xnet.provider.jsse; 18 19import java.security.AlgorithmParameters; 20import java.security.InvalidAlgorithmParameterException; 21import java.security.InvalidKeyException; 22import java.security.InvalidParameterException; 23import java.security.Key; 24import java.security.KeyFactory; 25import java.security.NoSuchAlgorithmException; 26import java.security.SecureRandom; 27import java.security.spec.AlgorithmParameterSpec; 28import java.security.spec.InvalidKeySpecException; 29import java.security.spec.InvalidParameterSpecException; 30import java.security.spec.PKCS8EncodedKeySpec; 31import java.security.spec.X509EncodedKeySpec; 32import java.util.Arrays; 33import java.util.Locale; 34 35import javax.crypto.BadPaddingException; 36import javax.crypto.Cipher; 37import javax.crypto.CipherSpi; 38import javax.crypto.IllegalBlockSizeException; 39import javax.crypto.NoSuchPaddingException; 40import javax.crypto.SecretKey; 41import javax.crypto.ShortBufferException; 42import javax.crypto.spec.IvParameterSpec; 43import javax.crypto.spec.SecretKeySpec; 44 45import libcore.util.EmptyArray; 46 47public abstract class OpenSSLCipher extends CipherSpi { 48 49 /** 50 * Modes that a block cipher may support. 51 */ 52 protected static enum Mode { 53 CBC, 54 CFB, CFB1, CFB8, CFB128, 55 CTR, 56 CTS, 57 ECB, 58 OFB, OFB64, OFB128, 59 PCBC, 60 } 61 62 /** 63 * Paddings that a block cipher may support. 64 */ 65 protected static enum Padding { 66 NOPADDING, 67 PKCS5PADDING, 68 ISO10126PADDING, 69 } 70 71 /** 72 * Native pointer for the OpenSSL EVP_CIPHER context. 73 */ 74 private OpenSSLCipherContext cipherCtx = new OpenSSLCipherContext( 75 NativeCrypto.EVP_CIPHER_CTX_new()); 76 77 /** 78 * The current cipher mode. 79 */ 80 private Mode mode = Mode.ECB; 81 82 /** 83 * The current cipher padding. 84 */ 85 private Padding padding = Padding.PKCS5PADDING; 86 87 /** 88 * The Initial Vector (IV) used for the current cipher. 89 */ 90 private byte[] iv; 91 92 /** 93 * Current cipher mode: encrypting or decrypting. 94 */ 95 private boolean encrypting; 96 97 /** 98 * The block size of the current cipher. 99 */ 100 private int blockSize; 101 102 /** 103 * The block size of the current mode. 104 */ 105 private int modeBlockSize; 106 107 /** 108 * Whether the cipher has processed any data yet. OpenSSL doesn't like 109 * calling "doFinal()" in decryption mode without processing any updates. 110 */ 111 private boolean calledUpdate; 112 113 protected OpenSSLCipher() { 114 } 115 116 protected OpenSSLCipher(Mode mode, Padding padding) { 117 this.mode = mode; 118 this.padding = padding; 119 blockSize = getCipherBlockSize(); 120 } 121 122 /** 123 * Returns the OpenSSL cipher name for the particular {@code keySize} and 124 * cipher {@code mode}. 125 */ 126 protected abstract String getCipherName(int keySize, Mode mode); 127 128 /** 129 * Checks whether the cipher supports this particular {@code keySize} (in 130 * bytes) and throws {@code InvalidKeyException} if it doesn't. 131 */ 132 protected abstract void checkSupportedKeySize(int keySize) throws InvalidKeyException; 133 134 /** 135 * Checks whether the cipher supports this particular cipher {@code mode} 136 * and throws {@code NoSuchAlgorithmException} if it doesn't. 137 */ 138 protected abstract void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException; 139 140 /** 141 * Checks whether the cipher supports this particular cipher {@code padding} 142 * and throws {@code NoSuchPaddingException} if it doesn't. 143 */ 144 protected abstract void checkSupportedPadding(Padding padding) throws NoSuchPaddingException; 145 146 protected abstract int getCipherBlockSize(); 147 148 @Override 149 protected void engineSetMode(String modeStr) throws NoSuchAlgorithmException { 150 final Mode mode; 151 try { 152 mode = Mode.valueOf(modeStr.toUpperCase(Locale.US)); 153 } catch (IllegalArgumentException e) { 154 NoSuchAlgorithmException newE = new NoSuchAlgorithmException("No such mode: " 155 + modeStr); 156 newE.initCause(e); 157 throw newE; 158 } 159 checkSupportedMode(mode); 160 this.mode = mode; 161 } 162 163 @Override 164 protected void engineSetPadding(String paddingStr) throws NoSuchPaddingException { 165 final String paddingStrUpper = paddingStr.toUpperCase(Locale.US); 166 final Padding padding; 167 try { 168 padding = Padding.valueOf(paddingStrUpper); 169 } catch (IllegalArgumentException e) { 170 NoSuchPaddingException newE = new NoSuchPaddingException("No such padding: " 171 + paddingStr); 172 newE.initCause(e); 173 throw newE; 174 } 175 checkSupportedPadding(padding); 176 this.padding = padding; 177 } 178 179 @Override 180 protected int engineGetBlockSize() { 181 return blockSize; 182 } 183 184 /** 185 * The size of output if {@code doFinal()} is called with this 186 * {@code inputLen}. If padding is enabled and the size of the input puts it 187 * right at the block size, it will add another block for the padding. 188 */ 189 private final int getFinalOutputSize(int inputLen) { 190 if (modeBlockSize == 1) { 191 return getUpdateOutputSize(inputLen); 192 } else { 193 return getUpdateOutputSize(inputLen) + modeBlockSize; 194 } 195 } 196 197 @Override 198 protected int engineGetOutputSize(int inputLen) { 199 return getFinalOutputSize(inputLen); 200 } 201 202 @Override 203 protected byte[] engineGetIV() { 204 return iv; 205 } 206 207 @Override 208 protected AlgorithmParameters engineGetParameters() { 209 return null; 210 } 211 212 private void engineInitInternal(int opmode, Key key, byte[] iv) throws InvalidKeyException, InvalidAlgorithmParameterException { 213 if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) { 214 encrypting = true; 215 } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) { 216 encrypting = false; 217 } else { 218 throw new InvalidParameterException("Unsupported opmode " + opmode); 219 } 220 221 if (!(key instanceof SecretKey)) { 222 throw new InvalidKeyException("Only SecretKey is supported"); 223 } 224 225 final byte[] encodedKey = key.getEncoded(); 226 if (encodedKey == null) { 227 throw new InvalidKeyException("key.getEncoded() == null"); 228 } 229 230 checkSupportedKeySize(encodedKey.length); 231 232 final int cipherType = NativeCrypto.EVP_get_cipherbyname(getCipherName(encodedKey.length, 233 mode)); 234 if (cipherType == 0) { 235 throw new InvalidAlgorithmParameterException("Cannot find name for key length = " 236 + (encodedKey.length * 8) + " and mode = " + mode); 237 } 238 239 final int ivLength = NativeCrypto.EVP_CIPHER_iv_length(cipherType); 240 if (iv == null) { 241 iv = new byte[ivLength]; 242 } else if (iv.length != ivLength) { 243 throw new InvalidAlgorithmParameterException("expected IV length of " + ivLength); 244 } 245 246 this.iv = iv; 247 248 NativeCrypto.EVP_CipherInit_ex(cipherCtx.getContext(), cipherType, encodedKey, iv, 249 encrypting); 250 251 // OpenSSL only supports PKCS5 Padding. 252 NativeCrypto.EVP_CIPHER_CTX_set_padding(cipherCtx.getContext(), 253 padding == Padding.PKCS5PADDING); 254 modeBlockSize = NativeCrypto.EVP_CIPHER_CTX_block_size(cipherCtx.getContext()); 255 calledUpdate = false; 256 } 257 258 @Override 259 protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException { 260 try { 261 engineInitInternal(opmode, key, null); 262 } catch (InvalidAlgorithmParameterException e) { 263 throw new RuntimeException(e); 264 } 265 } 266 267 @Override 268 protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, 269 SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { 270 final byte[] iv; 271 if (params instanceof IvParameterSpec) { 272 IvParameterSpec ivParams = (IvParameterSpec) params; 273 iv = ivParams.getIV(); 274 } else { 275 iv = null; 276 } 277 278 engineInitInternal(opmode, key, iv); 279 } 280 281 @Override 282 protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) 283 throws InvalidKeyException, InvalidAlgorithmParameterException { 284 final AlgorithmParameterSpec spec; 285 try { 286 spec = params.getParameterSpec(IvParameterSpec.class); 287 } catch (InvalidParameterSpecException e) { 288 throw new InvalidAlgorithmParameterException(e); 289 } 290 291 engineInit(opmode, key, spec, random); 292 } 293 294 private final int updateInternal(byte[] input, int inputOffset, int inputLen, byte[] output, 295 int outputOffset) throws ShortBufferException { 296 final int intialOutputOffset = outputOffset; 297 298 final int bytesLeft = output.length - outputOffset; 299 if (bytesLeft < getUpdateOutputSize(inputLen)) { 300 throw new ShortBufferException("output buffer too small during update: " + bytesLeft 301 + " < " + output.length); 302 } 303 304 outputOffset += NativeCrypto.EVP_CipherUpdate(cipherCtx.getContext(), output, outputOffset, 305 input, inputOffset, inputLen); 306 307 calledUpdate = true; 308 309 return outputOffset - intialOutputOffset; 310 } 311 312 private int getUpdateOutputSize(int inputLen) { 313 if (encrypting) { 314 return inputLen + modeBlockSize - 1; 315 } else if (modeBlockSize == 1) { 316 return inputLen; 317 } else { 318 return inputLen + modeBlockSize; 319 } 320 } 321 322 @Override 323 protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { 324 final int maximumLen = getUpdateOutputSize(inputLen); 325 326 /* See how large our output buffer would need to be. */ 327 final byte[] output; 328 if (maximumLen > 0) { 329 output = new byte[maximumLen]; 330 } else { 331 output = EmptyArray.BYTE; 332 } 333 334 final int bytesWritten; 335 try { 336 bytesWritten = updateInternal(input, inputOffset, inputLen, output, 0); 337 } catch (ShortBufferException e) { 338 /* This shouldn't happen. */ 339 throw new RuntimeException("calculated buffer size was wrong: " + maximumLen); 340 } 341 342 if (output.length == bytesWritten) { 343 return output; 344 } else if (bytesWritten == 0) { 345 return EmptyArray.BYTE; 346 } else { 347 return Arrays.copyOfRange(output, 0, bytesWritten); 348 } 349 } 350 351 @Override 352 protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, 353 int outputOffset) throws ShortBufferException { 354 return updateInternal(input, inputOffset, inputLen, output, outputOffset); 355 } 356 357 /** 358 * Reset this Cipher instance state to process a new chunk of data. 359 */ 360 private void reset() { 361 NativeCrypto.EVP_CipherInit_ex(cipherCtx.getContext(), 0, null, null, encrypting); 362 calledUpdate = false; 363 } 364 365 private int doFinalInternal(byte[] input, int inputOffset, int inputLen, byte[] output, 366 int outputOffset) throws IllegalBlockSizeException, BadPaddingException, 367 ShortBufferException { 368 /* Remember this so we can tell how many characters were written. */ 369 final int initialOutputOffset = outputOffset; 370 371 if (inputLen > 0) { 372 final int updateBytesWritten = updateInternal(input, inputOffset, inputLen, output, 373 outputOffset); 374 outputOffset += updateBytesWritten; 375 } 376 377 /* 378 * If we're decrypting and haven't had any input, we should return null. 379 * Otherwise OpenSSL will complain if we call final. 380 */ 381 if (!encrypting && !calledUpdate) { 382 return 0; 383 } 384 385 /* Allow OpenSSL to pad if necessary and clean up state. */ 386 final int bytesLeft = output.length - outputOffset; 387 final int writtenBytes; 388 if (bytesLeft >= blockSize) { 389 writtenBytes = NativeCrypto.EVP_CipherFinal_ex(cipherCtx.getContext(), output, 390 outputOffset); 391 } else { 392 final byte[] lastBlock = new byte[modeBlockSize]; 393 writtenBytes = NativeCrypto.EVP_CipherFinal_ex(cipherCtx.getContext(), lastBlock, 0); 394 if (writtenBytes > bytesLeft) { 395 throw new ShortBufferException("buffer is too short: " + writtenBytes + " > " 396 + bytesLeft); 397 } else if (writtenBytes > 0) { 398 System.arraycopy(lastBlock, 0, output, outputOffset, writtenBytes); 399 } 400 } 401 outputOffset += writtenBytes; 402 403 reset(); 404 405 return outputOffset - initialOutputOffset; 406 } 407 408 @Override 409 protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) 410 throws IllegalBlockSizeException, BadPaddingException { 411 /* 412 * Other implementations return null if we've never called update() 413 * while decrypting. 414 */ 415 if (!encrypting && !calledUpdate && inputLen == 0) { 416 reset(); 417 return null; 418 } 419 420 final int maximumSize = getFinalOutputSize(inputLen); 421 /* Assume that we'll output exactly on a byte boundary. */ 422 byte[] output = new byte[maximumSize]; 423 final int bytesWritten; 424 try { 425 bytesWritten = doFinalInternal(input, inputOffset, inputLen, output, 0); 426 } catch (ShortBufferException e) { 427 /* This should not happen since we sized our own buffer. */ 428 throw new RuntimeException("our calculated buffer was too small", e); 429 } 430 431 if (bytesWritten == output.length) { 432 return output; 433 } else if (bytesWritten == 0) { 434 return EmptyArray.BYTE; 435 } else { 436 return Arrays.copyOfRange(output, 0, bytesWritten); 437 } 438 } 439 440 @Override 441 protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, 442 int outputOffset) throws ShortBufferException, IllegalBlockSizeException, 443 BadPaddingException { 444 if (output == null) { 445 throw new NullPointerException("output == null"); 446 } 447 448 return doFinalInternal(input, inputOffset, inputLen, output, outputOffset); 449 } 450 451 @Override 452 protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException { 453 try { 454 byte[] encoded = key.getEncoded(); 455 return engineDoFinal(encoded, 0, encoded.length); 456 } catch (BadPaddingException e) { 457 IllegalBlockSizeException newE = new IllegalBlockSizeException(); 458 newE.initCause(e); 459 throw newE; 460 } 461 } 462 463 @Override 464 protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType) 465 throws InvalidKeyException, NoSuchAlgorithmException { 466 try { 467 byte[] encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length); 468 if (wrappedKeyType == Cipher.PUBLIC_KEY) { 469 KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); 470 return keyFactory.generatePublic(new X509EncodedKeySpec(encoded)); 471 } else if (wrappedKeyType == Cipher.PRIVATE_KEY) { 472 KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); 473 return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded)); 474 } else if (wrappedKeyType == Cipher.SECRET_KEY) { 475 return new SecretKeySpec(encoded, wrappedKeyAlgorithm); 476 } else { 477 throw new UnsupportedOperationException("wrappedKeyType == " + wrappedKeyType); 478 } 479 } catch (IllegalBlockSizeException e) { 480 throw new InvalidKeyException(e); 481 } catch (BadPaddingException e) { 482 throw new InvalidKeyException(e); 483 } catch (InvalidKeySpecException e) { 484 throw new InvalidKeyException(e); 485 } 486 } 487 488 public static class AES extends OpenSSLCipher { 489 private static final int AES_BLOCK_SIZE = 16; 490 491 protected AES(Mode mode, Padding padding) { 492 super(mode, padding); 493 } 494 495 public static class CBC extends AES { 496 public CBC(Padding padding) { 497 super(Mode.CBC, padding); 498 } 499 500 public static class NoPadding extends CBC { 501 public NoPadding() { 502 super(Padding.NOPADDING); 503 } 504 } 505 506 public static class PKCS5Padding extends CBC { 507 public PKCS5Padding() { 508 super(Padding.PKCS5PADDING); 509 } 510 } 511 } 512 513 public static class CFB extends AES { 514 public CFB(Padding padding) { 515 super(Mode.CFB, padding); 516 } 517 518 public static class NoPadding extends CFB { 519 public NoPadding() { 520 super(Padding.NOPADDING); 521 } 522 } 523 524 public static class PKCS5Padding extends CFB { 525 public PKCS5Padding() { 526 super(Padding.PKCS5PADDING); 527 } 528 } 529 } 530 531 public static class CTR extends AES { 532 public CTR(Padding padding) { 533 super(Mode.CTR, padding); 534 } 535 536 public static class NoPadding extends CTR { 537 public NoPadding() { 538 super(Padding.NOPADDING); 539 } 540 } 541 542 public static class PKCS5Padding extends CTR { 543 public PKCS5Padding() { 544 super(Padding.PKCS5PADDING); 545 } 546 } 547 } 548 549 public static class ECB extends AES { 550 public ECB(Padding padding) { 551 super(Mode.ECB, padding); 552 } 553 554 public static class NoPadding extends ECB { 555 public NoPadding() { 556 super(Padding.NOPADDING); 557 } 558 } 559 560 public static class PKCS5Padding extends ECB { 561 public PKCS5Padding() { 562 super(Padding.PKCS5PADDING); 563 } 564 } 565 } 566 567 public static class OFB extends AES { 568 public OFB(Padding padding) { 569 super(Mode.OFB, padding); 570 } 571 572 public static class NoPadding extends OFB { 573 public NoPadding() { 574 super(Padding.NOPADDING); 575 } 576 } 577 578 public static class PKCS5Padding extends OFB { 579 public PKCS5Padding() { 580 super(Padding.PKCS5PADDING); 581 } 582 } 583 } 584 585 @Override 586 protected void checkSupportedKeySize(int keyLength) throws InvalidKeyException { 587 switch (keyLength) { 588 case 16: // AES 128 589 case 24: // AES 192 590 case 32: // AES 256 591 return; 592 default: 593 throw new InvalidKeyException("Unsupported key size: " + keyLength + " bytes"); 594 } 595 } 596 597 @Override 598 protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException { 599 switch (mode) { 600 case CBC: 601 case CFB: 602 case CFB1: 603 case CFB8: 604 case CFB128: 605 case CTR: 606 case ECB: 607 case OFB: 608 return; 609 default: 610 throw new NoSuchAlgorithmException("Unsupported mode " + mode.toString()); 611 } 612 } 613 614 @Override 615 protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException { 616 switch (padding) { 617 case NOPADDING: 618 case PKCS5PADDING: 619 return; 620 default: 621 throw new NoSuchPaddingException("Unsupported padding " + padding.toString()); 622 } 623 } 624 625 @Override 626 protected String getCipherName(int keyLength, Mode mode) { 627 return "aes-" + (keyLength * 8) + "-" + mode.toString().toLowerCase(Locale.US); 628 } 629 630 @Override 631 protected int getCipherBlockSize() { 632 return AES_BLOCK_SIZE; 633 } 634 } 635 636 public static class DESEDE extends OpenSSLCipher { 637 private static int DES_BLOCK_SIZE = 8; 638 639 public DESEDE(Mode mode, Padding padding) { 640 super(mode, padding); 641 } 642 643 public static class CBC extends DESEDE { 644 public CBC(Padding padding) { 645 super(Mode.CBC, padding); 646 } 647 648 public static class NoPadding extends CBC { 649 public NoPadding() { 650 super(Padding.NOPADDING); 651 } 652 } 653 654 public static class PKCS5Padding extends CBC { 655 public PKCS5Padding() { 656 super(Padding.PKCS5PADDING); 657 } 658 } 659 } 660 661 public static class CFB extends DESEDE { 662 public CFB(Padding padding) { 663 super(Mode.CFB, padding); 664 } 665 666 public static class NoPadding extends CFB { 667 public NoPadding() { 668 super(Padding.NOPADDING); 669 } 670 } 671 672 public static class PKCS5Padding extends CFB { 673 public PKCS5Padding() { 674 super(Padding.PKCS5PADDING); 675 } 676 } 677 } 678 679 public static class ECB extends DESEDE { 680 public ECB(Padding padding) { 681 super(Mode.ECB, padding); 682 } 683 684 public static class NoPadding extends ECB { 685 public NoPadding() { 686 super(Padding.NOPADDING); 687 } 688 } 689 690 public static class PKCS5Padding extends ECB { 691 public PKCS5Padding() { 692 super(Padding.PKCS5PADDING); 693 } 694 } 695 } 696 697 public static class OFB extends DESEDE { 698 public OFB(Padding padding) { 699 super(Mode.OFB, padding); 700 } 701 702 public static class NoPadding extends OFB { 703 public NoPadding() { 704 super(Padding.NOPADDING); 705 } 706 } 707 708 public static class PKCS5Padding extends OFB { 709 public PKCS5Padding() { 710 super(Padding.PKCS5PADDING); 711 } 712 } 713 } 714 715 @Override 716 protected String getCipherName(int keySize, Mode mode) { 717 final String baseCipherName; 718 if (keySize == 16) { 719 baseCipherName = "des-ede"; 720 } else { 721 baseCipherName = "des-ede3"; 722 } 723 724 if (mode == Mode.ECB) { 725 return baseCipherName; 726 } else { 727 return baseCipherName + "-" + mode.toString().toLowerCase(Locale.US); 728 } 729 } 730 731 @Override 732 protected void checkSupportedKeySize(int keySize) throws InvalidKeyException { 733 if (keySize != 16 && keySize != 24) { 734 throw new InvalidKeyException("key size must be 128 or 192 bits"); 735 } 736 } 737 738 @Override 739 protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException { 740 switch (mode) { 741 case CBC: 742 case CFB: 743 case CFB1: 744 case CFB8: 745 case ECB: 746 case OFB: 747 return; 748 default: 749 throw new NoSuchAlgorithmException("Unsupported mode " + mode.toString()); 750 } 751 } 752 753 @Override 754 protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException { 755 switch (padding) { 756 case NOPADDING: 757 case PKCS5PADDING: 758 return; 759 default: 760 throw new NoSuchPaddingException("Unsupported padding " + padding.toString()); 761 } 762 } 763 764 @Override 765 protected int getCipherBlockSize() { 766 return DES_BLOCK_SIZE; 767 } 768 } 769} 770