OpenSSLCipher.java revision 5b5904640b44fe2fd760b5d427edeffe20f55630
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 int getOutputSize(int inputLen) { 190 if (modeBlockSize == 1) { 191 return inputLen; 192 } else { 193 final int buffered = NativeCrypto.get_EVP_CIPHER_CTX_buf_len(cipherCtx.getContext()); 194 if (padding == Padding.NOPADDING) { 195 return buffered + inputLen; 196 } else { 197 final int totalLen = inputLen + buffered + modeBlockSize; 198 return totalLen - (totalLen % modeBlockSize); 199 } 200 } 201 } 202 203 @Override 204 protected int engineGetOutputSize(int inputLen) { 205 return getOutputSize(inputLen); 206 } 207 208 @Override 209 protected byte[] engineGetIV() { 210 return iv; 211 } 212 213 @Override 214 protected AlgorithmParameters engineGetParameters() { 215 return null; 216 } 217 218 private void engineInitInternal(int opmode, Key key, byte[] iv) throws InvalidKeyException, InvalidAlgorithmParameterException { 219 if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) { 220 encrypting = true; 221 } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) { 222 encrypting = false; 223 } else { 224 throw new InvalidParameterException("Unsupported opmode " + opmode); 225 } 226 227 if (!(key instanceof SecretKey)) { 228 throw new InvalidKeyException("Only SecretKey is supported"); 229 } 230 231 final byte[] encodedKey = key.getEncoded(); 232 if (encodedKey == null) { 233 throw new InvalidKeyException("key.getEncoded() == null"); 234 } 235 236 checkSupportedKeySize(encodedKey.length); 237 238 final int cipherType = NativeCrypto.EVP_get_cipherbyname(getCipherName(encodedKey.length, 239 mode)); 240 if (cipherType == 0) { 241 throw new InvalidAlgorithmParameterException("Cannot find name for key length = " 242 + (encodedKey.length * 8) + " and mode = " + mode); 243 } 244 245 final int ivLength = NativeCrypto.EVP_CIPHER_iv_length(cipherType); 246 if (iv == null) { 247 iv = new byte[ivLength]; 248 } else if (iv.length != ivLength) { 249 throw new InvalidAlgorithmParameterException("expected IV length of " + ivLength); 250 } 251 252 this.iv = iv; 253 254 NativeCrypto.EVP_CipherInit_ex(cipherCtx.getContext(), cipherType, encodedKey, iv, 255 encrypting); 256 257 // OpenSSL only supports PKCS5 Padding. 258 NativeCrypto.EVP_CIPHER_CTX_set_padding(cipherCtx.getContext(), 259 padding == Padding.PKCS5PADDING); 260 modeBlockSize = NativeCrypto.EVP_CIPHER_CTX_block_size(cipherCtx.getContext()); 261 calledUpdate = false; 262 } 263 264 @Override 265 protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException { 266 try { 267 engineInitInternal(opmode, key, null); 268 } catch (InvalidAlgorithmParameterException e) { 269 throw new RuntimeException(e); 270 } 271 } 272 273 @Override 274 protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, 275 SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { 276 final byte[] iv; 277 if (params instanceof IvParameterSpec) { 278 IvParameterSpec ivParams = (IvParameterSpec) params; 279 iv = ivParams.getIV(); 280 } else { 281 iv = null; 282 } 283 284 engineInitInternal(opmode, key, iv); 285 } 286 287 @Override 288 protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) 289 throws InvalidKeyException, InvalidAlgorithmParameterException { 290 final AlgorithmParameterSpec spec; 291 try { 292 spec = params.getParameterSpec(IvParameterSpec.class); 293 } catch (InvalidParameterSpecException e) { 294 throw new InvalidAlgorithmParameterException(e); 295 } 296 297 engineInit(opmode, key, spec, random); 298 } 299 300 private final int updateInternal(byte[] input, int inputOffset, int inputLen, byte[] output, 301 int outputOffset, int maximumLen) throws ShortBufferException { 302 final int intialOutputOffset = outputOffset; 303 304 final int bytesLeft = output.length - outputOffset; 305 if (bytesLeft < maximumLen) { 306 throw new ShortBufferException("output buffer too small during update: " + bytesLeft 307 + " < " + output.length); 308 } 309 310 outputOffset += NativeCrypto.EVP_CipherUpdate(cipherCtx.getContext(), output, outputOffset, 311 input, inputOffset, inputLen); 312 313 calledUpdate = true; 314 315 return outputOffset - intialOutputOffset; 316 } 317 318 @Override 319 protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { 320 final int maximumLen = getOutputSize(inputLen); 321 322 /* See how large our output buffer would need to be. */ 323 final byte[] output; 324 if (maximumLen > 0) { 325 output = new byte[maximumLen]; 326 } else { 327 output = EmptyArray.BYTE; 328 } 329 330 final int bytesWritten; 331 try { 332 bytesWritten = updateInternal(input, inputOffset, inputLen, output, 0, maximumLen); 333 } catch (ShortBufferException e) { 334 /* This shouldn't happen. */ 335 throw new RuntimeException("calculated buffer size was wrong: " + maximumLen); 336 } 337 338 if (output.length == bytesWritten) { 339 return output; 340 } else if (bytesWritten == 0) { 341 return EmptyArray.BYTE; 342 } else { 343 return Arrays.copyOfRange(output, 0, bytesWritten); 344 } 345 } 346 347 @Override 348 protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, 349 int outputOffset) throws ShortBufferException { 350 final int maximumLen = getOutputSize(inputLen); 351 return updateInternal(input, inputOffset, inputLen, output, outputOffset, maximumLen); 352 } 353 354 /** 355 * Reset this Cipher instance state to process a new chunk of data. 356 */ 357 private void reset() { 358 NativeCrypto.EVP_CipherInit_ex(cipherCtx.getContext(), 0, null, null, encrypting); 359 calledUpdate = false; 360 } 361 362 private int doFinalInternal(byte[] input, int inputOffset, int inputLen, byte[] output, 363 int outputOffset, int maximumLen) throws IllegalBlockSizeException, 364 BadPaddingException, ShortBufferException { 365 /* Remember this so we can tell how many characters were written. */ 366 final int initialOutputOffset = outputOffset; 367 368 if (inputLen > 0) { 369 final int updateBytesWritten = updateInternal(input, inputOffset, inputLen, output, 370 outputOffset, maximumLen); 371 outputOffset += updateBytesWritten; 372 maximumLen -= updateBytesWritten; 373 } 374 375 /* 376 * If we're decrypting and haven't had any input, we should return null. 377 * Otherwise OpenSSL will complain if we call final. 378 */ 379 if (!encrypting && !calledUpdate) { 380 return 0; 381 } 382 383 /* Allow OpenSSL to pad if necessary and clean up state. */ 384 final int bytesLeft = output.length - outputOffset; 385 final int writtenBytes; 386 if (bytesLeft >= maximumLen) { 387 writtenBytes = NativeCrypto.EVP_CipherFinal_ex(cipherCtx.getContext(), output, 388 outputOffset); 389 } else { 390 final byte[] lastBlock = new byte[maximumLen]; 391 writtenBytes = NativeCrypto.EVP_CipherFinal_ex(cipherCtx.getContext(), lastBlock, 0); 392 if (writtenBytes > bytesLeft) { 393 throw new ShortBufferException("buffer is too short: " + writtenBytes + " > " 394 + bytesLeft); 395 } else if (writtenBytes > 0) { 396 System.arraycopy(lastBlock, 0, output, outputOffset, writtenBytes); 397 } 398 } 399 outputOffset += writtenBytes; 400 401 reset(); 402 403 return outputOffset - initialOutputOffset; 404 } 405 406 @Override 407 protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) 408 throws IllegalBlockSizeException, BadPaddingException { 409 /* 410 * Other implementations return null if we've never called update() 411 * while decrypting. 412 */ 413 if (!encrypting && !calledUpdate && inputLen == 0) { 414 reset(); 415 return null; 416 } 417 418 final int maximumLen = getOutputSize(inputLen); 419 /* Assume that we'll output exactly on a byte boundary. */ 420 final byte[] output = new byte[maximumLen]; 421 final int bytesWritten; 422 try { 423 bytesWritten = doFinalInternal(input, inputOffset, inputLen, output, 0, maximumLen); 424 } catch (ShortBufferException e) { 425 /* This should not happen since we sized our own buffer. */ 426 throw new RuntimeException("our calculated buffer was too small", e); 427 } 428 429 if (bytesWritten == output.length) { 430 return output; 431 } else if (bytesWritten == 0) { 432 return EmptyArray.BYTE; 433 } else { 434 return Arrays.copyOfRange(output, 0, bytesWritten); 435 } 436 } 437 438 @Override 439 protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, 440 int outputOffset) throws ShortBufferException, IllegalBlockSizeException, 441 BadPaddingException { 442 if (output == null) { 443 throw new NullPointerException("output == null"); 444 } 445 446 final int maximumLen = getOutputSize(inputLen); 447 return doFinalInternal(input, inputOffset, inputLen, output, outputOffset, maximumLen); 448 } 449 450 @Override 451 protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException { 452 try { 453 byte[] encoded = key.getEncoded(); 454 return engineDoFinal(encoded, 0, encoded.length); 455 } catch (BadPaddingException e) { 456 IllegalBlockSizeException newE = new IllegalBlockSizeException(); 457 newE.initCause(e); 458 throw newE; 459 } 460 } 461 462 @Override 463 protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType) 464 throws InvalidKeyException, NoSuchAlgorithmException { 465 try { 466 byte[] encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length); 467 if (wrappedKeyType == Cipher.PUBLIC_KEY) { 468 KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); 469 return keyFactory.generatePublic(new X509EncodedKeySpec(encoded)); 470 } else if (wrappedKeyType == Cipher.PRIVATE_KEY) { 471 KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); 472 return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded)); 473 } else if (wrappedKeyType == Cipher.SECRET_KEY) { 474 return new SecretKeySpec(encoded, wrappedKeyAlgorithm); 475 } else { 476 throw new UnsupportedOperationException("wrappedKeyType == " + wrappedKeyType); 477 } 478 } catch (IllegalBlockSizeException e) { 479 throw new InvalidKeyException(e); 480 } catch (BadPaddingException e) { 481 throw new InvalidKeyException(e); 482 } catch (InvalidKeySpecException e) { 483 throw new InvalidKeyException(e); 484 } 485 } 486 487 public static class AES extends OpenSSLCipher { 488 private static final int AES_BLOCK_SIZE = 16; 489 490 protected AES(Mode mode, Padding padding) { 491 super(mode, padding); 492 } 493 494 public static class CBC extends AES { 495 public CBC(Padding padding) { 496 super(Mode.CBC, padding); 497 } 498 499 public static class NoPadding extends CBC { 500 public NoPadding() { 501 super(Padding.NOPADDING); 502 } 503 } 504 505 public static class PKCS5Padding extends CBC { 506 public PKCS5Padding() { 507 super(Padding.PKCS5PADDING); 508 } 509 } 510 } 511 512 public static class CFB extends AES { 513 public CFB(Padding padding) { 514 super(Mode.CFB, padding); 515 } 516 517 public static class NoPadding extends CFB { 518 public NoPadding() { 519 super(Padding.NOPADDING); 520 } 521 } 522 523 public static class PKCS5Padding extends CFB { 524 public PKCS5Padding() { 525 super(Padding.PKCS5PADDING); 526 } 527 } 528 } 529 530 public static class CTR extends AES { 531 public CTR(Padding padding) { 532 super(Mode.CTR, padding); 533 } 534 535 public static class NoPadding extends CTR { 536 public NoPadding() { 537 super(Padding.NOPADDING); 538 } 539 } 540 541 public static class PKCS5Padding extends CTR { 542 public PKCS5Padding() { 543 super(Padding.PKCS5PADDING); 544 } 545 } 546 } 547 548 public static class ECB extends AES { 549 public ECB(Padding padding) { 550 super(Mode.ECB, padding); 551 } 552 553 public static class NoPadding extends ECB { 554 public NoPadding() { 555 super(Padding.NOPADDING); 556 } 557 } 558 559 public static class PKCS5Padding extends ECB { 560 public PKCS5Padding() { 561 super(Padding.PKCS5PADDING); 562 } 563 } 564 } 565 566 public static class OFB extends AES { 567 public OFB(Padding padding) { 568 super(Mode.OFB, padding); 569 } 570 571 public static class NoPadding extends OFB { 572 public NoPadding() { 573 super(Padding.NOPADDING); 574 } 575 } 576 577 public static class PKCS5Padding extends OFB { 578 public PKCS5Padding() { 579 super(Padding.PKCS5PADDING); 580 } 581 } 582 } 583 584 @Override 585 protected void checkSupportedKeySize(int keyLength) throws InvalidKeyException { 586 switch (keyLength) { 587 case 16: // AES 128 588 case 24: // AES 192 589 case 32: // AES 256 590 return; 591 default: 592 throw new InvalidKeyException("Unsupported key size: " + keyLength + " bytes"); 593 } 594 } 595 596 @Override 597 protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException { 598 switch (mode) { 599 case CBC: 600 case CFB: 601 case CFB1: 602 case CFB8: 603 case CFB128: 604 case CTR: 605 case ECB: 606 case OFB: 607 return; 608 default: 609 throw new NoSuchAlgorithmException("Unsupported mode " + mode.toString()); 610 } 611 } 612 613 @Override 614 protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException { 615 switch (padding) { 616 case NOPADDING: 617 case PKCS5PADDING: 618 return; 619 default: 620 throw new NoSuchPaddingException("Unsupported padding " + padding.toString()); 621 } 622 } 623 624 @Override 625 protected String getCipherName(int keyLength, Mode mode) { 626 return "aes-" + (keyLength * 8) + "-" + mode.toString().toLowerCase(Locale.US); 627 } 628 629 @Override 630 protected int getCipherBlockSize() { 631 return AES_BLOCK_SIZE; 632 } 633 } 634 635 public static class DESEDE extends OpenSSLCipher { 636 private static int DES_BLOCK_SIZE = 8; 637 638 public DESEDE(Mode mode, Padding padding) { 639 super(mode, padding); 640 } 641 642 public static class CBC extends DESEDE { 643 public CBC(Padding padding) { 644 super(Mode.CBC, padding); 645 } 646 647 public static class NoPadding extends CBC { 648 public NoPadding() { 649 super(Padding.NOPADDING); 650 } 651 } 652 653 public static class PKCS5Padding extends CBC { 654 public PKCS5Padding() { 655 super(Padding.PKCS5PADDING); 656 } 657 } 658 } 659 660 public static class CFB extends DESEDE { 661 public CFB(Padding padding) { 662 super(Mode.CFB, padding); 663 } 664 665 public static class NoPadding extends CFB { 666 public NoPadding() { 667 super(Padding.NOPADDING); 668 } 669 } 670 671 public static class PKCS5Padding extends CFB { 672 public PKCS5Padding() { 673 super(Padding.PKCS5PADDING); 674 } 675 } 676 } 677 678 public static class ECB extends DESEDE { 679 public ECB(Padding padding) { 680 super(Mode.ECB, padding); 681 } 682 683 public static class NoPadding extends ECB { 684 public NoPadding() { 685 super(Padding.NOPADDING); 686 } 687 } 688 689 public static class PKCS5Padding extends ECB { 690 public PKCS5Padding() { 691 super(Padding.PKCS5PADDING); 692 } 693 } 694 } 695 696 public static class OFB extends DESEDE { 697 public OFB(Padding padding) { 698 super(Mode.OFB, padding); 699 } 700 701 public static class NoPadding extends OFB { 702 public NoPadding() { 703 super(Padding.NOPADDING); 704 } 705 } 706 707 public static class PKCS5Padding extends OFB { 708 public PKCS5Padding() { 709 super(Padding.PKCS5PADDING); 710 } 711 } 712 } 713 714 @Override 715 protected String getCipherName(int keySize, Mode mode) { 716 final String baseCipherName; 717 if (keySize == 16) { 718 baseCipherName = "des-ede"; 719 } else { 720 baseCipherName = "des-ede3"; 721 } 722 723 if (mode == Mode.ECB) { 724 return baseCipherName; 725 } else { 726 return baseCipherName + "-" + mode.toString().toLowerCase(Locale.US); 727 } 728 } 729 730 @Override 731 protected void checkSupportedKeySize(int keySize) throws InvalidKeyException { 732 if (keySize != 16 && keySize != 24) { 733 throw new InvalidKeyException("key size must be 128 or 192 bits"); 734 } 735 } 736 737 @Override 738 protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException { 739 switch (mode) { 740 case CBC: 741 case CFB: 742 case CFB1: 743 case CFB8: 744 case ECB: 745 case OFB: 746 return; 747 default: 748 throw new NoSuchAlgorithmException("Unsupported mode " + mode.toString()); 749 } 750 } 751 752 @Override 753 protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException { 754 switch (padding) { 755 case NOPADDING: 756 case PKCS5PADDING: 757 return; 758 default: 759 throw new NoSuchPaddingException("Unsupported padding " + padding.toString()); 760 } 761 } 762 763 @Override 764 protected int getCipherBlockSize() { 765 return DES_BLOCK_SIZE; 766 } 767 } 768} 769