1 /* 2 * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.oracle.security.ucrypto; 27 28 import java.nio.ByteBuffer; 29 import java.util.Set; 30 import java.util.Arrays; 31 import java.util.concurrent.ConcurrentSkipListSet; 32 import java.lang.ref.*; 33 34 import java.security.*; 35 import java.security.spec.*; 36 import javax.crypto.*; 37 38 import javax.crypto.spec.SecretKeySpec; 39 import javax.crypto.spec.IvParameterSpec; 40 41 import sun.security.jca.JCAUtil; 42 43 /** 44 * Cipher wrapper class utilizing ucrypto APIs. This class currently supports 45 * - AES/ECB/NOPADDING 46 * - AES/CBC/NOPADDING 47 * - AES/CTR/NOPADDING 48 * - AES/CFB128/NOPADDING 49 * (Support for GCM mode is inside the child class NativeGCMCipher) 50 * 51 * @since 9 52 */ 53 class NativeCipher extends CipherSpi { 54 55 // public implementation classes 56 public static final class AesEcbNoPadding extends NativeCipher { 57 public AesEcbNoPadding() throws NoSuchAlgorithmException { 58 super(UcryptoMech.CRYPTO_AES_ECB); 59 } 60 public AesEcbNoPadding(int keySize) throws NoSuchAlgorithmException { 61 super(UcryptoMech.CRYPTO_AES_ECB, keySize); 62 } 63 } 64 public static final class AesCbcNoPadding extends NativeCipher { 65 public AesCbcNoPadding() throws NoSuchAlgorithmException { 66 super(UcryptoMech.CRYPTO_AES_CBC); 67 } 68 public AesCbcNoPadding(int keySize) throws NoSuchAlgorithmException { 69 super(UcryptoMech.CRYPTO_AES_CBC, keySize); 70 } 71 } 72 public static final class AesCtrNoPadding extends NativeCipher { 73 public AesCtrNoPadding() throws NoSuchAlgorithmException { 74 super(UcryptoMech.CRYPTO_AES_CTR); 75 } 76 } 77 public static final class AesCfb128NoPadding extends NativeCipher { 78 public AesCfb128NoPadding() throws NoSuchAlgorithmException { 79 super(UcryptoMech.CRYPTO_AES_CFB128); 80 } 81 } 82 83 // ok as constants since AES is all we support 84 public static final int AES_BLOCK_SIZE = 16; 85 public static final String AES_KEY_ALGO = "AES"; 86 87 // fields set in constructor 88 protected final UcryptoMech mech; 89 protected String keyAlgo; 90 protected int blockSize; 91 protected int fixedKeySize; 92 93 // 94 // fields (re)set in every init() 95 // 96 protected CipherContextRef pCtxt = null; 97 protected byte[] keyValue = null; 98 protected byte[] iv = null; 99 protected boolean initialized = false; 100 protected boolean encrypt = true; 101 protected int bytesBuffered = 0; 102 103 // private utility methods for key re-construction 104 private static final PublicKey constructPublicKey(byte[] encodedKey, 105 String encodedKeyAlgorithm) 106 throws InvalidKeyException, NoSuchAlgorithmException { 107 108 PublicKey key = null; 109 try { 110 KeyFactory keyFactory = 111 KeyFactory.getInstance(encodedKeyAlgorithm); 112 X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey); 113 key = keyFactory.generatePublic(keySpec); 114 } catch (NoSuchAlgorithmException nsae) { 115 throw new NoSuchAlgorithmException("No provider found for " + 116 encodedKeyAlgorithm + 117 " KeyFactory"); 118 } catch (InvalidKeySpecException ikse) { 119 // Should never happen 120 throw new InvalidKeyException("Cannot construct public key", ikse); 121 } 122 return key; 123 } 124 125 private static final PrivateKey constructPrivateKey(byte[] encodedKey, 126 String encodedKeyAlgorithm) 127 throws InvalidKeyException, NoSuchAlgorithmException { 128 129 PrivateKey key = null; 130 try { 131 KeyFactory keyFactory = 132 KeyFactory.getInstance(encodedKeyAlgorithm); 133 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey); 134 key = keyFactory.generatePrivate(keySpec); 135 } catch (NoSuchAlgorithmException nsae) { 136 throw new NoSuchAlgorithmException("No provider found for " + 137 encodedKeyAlgorithm + 138 " KeyFactory"); 139 } catch (InvalidKeySpecException ikse) { 140 // Should never happen 141 throw new InvalidKeyException("Cannot construct private key", ikse); 142 } 143 return key; 144 } 145 146 private static final SecretKey constructSecretKey(byte[] encodedKey, 147 String encodedKeyAlgorithm) { 148 return new SecretKeySpec(encodedKey, encodedKeyAlgorithm); 149 } 150 151 // package-private utility method for general key re-construction 152 static final Key constructKey(int keyType, byte[] encodedKey, 153 String encodedKeyAlgorithm) 154 throws InvalidKeyException, NoSuchAlgorithmException { 155 Key result = null; 156 switch (keyType) { 157 case Cipher.SECRET_KEY: 158 result = constructSecretKey(encodedKey, 159 encodedKeyAlgorithm); 160 break; 161 case Cipher.PRIVATE_KEY: 162 result = constructPrivateKey(encodedKey, 163 encodedKeyAlgorithm); 164 break; 165 case Cipher.PUBLIC_KEY: 166 result = constructPublicKey(encodedKey, 167 encodedKeyAlgorithm); 168 break; 169 } 170 return result; 171 } 172 173 NativeCipher(UcryptoMech mech, int fixedKeySize) throws NoSuchAlgorithmException { 174 this.mech = mech; 175 // defaults to AES - the only supported symmetric cipher algo 176 this.blockSize = AES_BLOCK_SIZE; 177 this.keyAlgo = AES_KEY_ALGO; 178 this.fixedKeySize = fixedKeySize; 179 } 180 181 NativeCipher(UcryptoMech mech) throws NoSuchAlgorithmException { 182 this(mech, -1); 183 } 184 185 @Override 186 protected void engineSetMode(String mode) throws NoSuchAlgorithmException { 187 // Disallow change of mode for now since currently it's explicitly 188 // defined in transformation strings 189 throw new NoSuchAlgorithmException("Unsupported mode " + mode); 190 } 191 192 // see JCE spec 193 @Override 194 protected void engineSetPadding(String padding) 195 throws NoSuchPaddingException { 196 // Disallow change of padding for now since currently it's explicitly 197 // defined in transformation strings 198 throw new NoSuchPaddingException("Unsupported padding " + padding); 199 } 200 201 // see JCE spec 202 @Override 203 protected int engineGetBlockSize() { 204 return blockSize; 205 } 206 207 // see JCE spec 208 @Override 209 protected synchronized int engineGetOutputSize(int inputLen) { 210 return getOutputSizeByOperation(inputLen, true); 211 } 212 213 // see JCE spec 214 @Override 215 protected synchronized byte[] engineGetIV() { 216 return (iv != null? iv.clone() : null); 217 } 218 219 // see JCE spec 220 @Override 221 protected synchronized AlgorithmParameters engineGetParameters() { 222 AlgorithmParameters params = null; 223 try { 224 if (iv != null) { 225 IvParameterSpec ivSpec = new IvParameterSpec(iv.clone()); 226 params = AlgorithmParameters.getInstance(keyAlgo); 227 params.init(ivSpec); 228 } 229 } catch (GeneralSecurityException e) { 230 // NoSuchAlgorithmException, NoSuchProviderException 231 // InvalidParameterSpecException 232 throw new UcryptoException("Could not encode parameters", e); 233 } 234 return params; 235 } 236 237 @Override 238 protected int engineGetKeySize(Key key) throws InvalidKeyException { 239 return checkKey(key) * 8; 240 } 241 242 // see JCE spec 243 @Override 244 protected synchronized void engineInit(int opmode, Key key, 245 SecureRandom random) throws InvalidKeyException { 246 try { 247 engineInit(opmode, key, (AlgorithmParameterSpec)null, random); 248 } catch (InvalidAlgorithmParameterException e) { 249 throw new InvalidKeyException("init() failed", e); 250 } 251 } 252 253 // see JCE spec 254 @Override 255 protected synchronized void engineInit(int opmode, Key key, 256 AlgorithmParameterSpec params, SecureRandom random) 257 throws InvalidKeyException, InvalidAlgorithmParameterException { 258 checkKey(key); 259 if (opmode != Cipher.ENCRYPT_MODE && 260 opmode != Cipher.DECRYPT_MODE && 261 opmode != Cipher.WRAP_MODE && 262 opmode != Cipher.UNWRAP_MODE) { 263 throw new InvalidAlgorithmParameterException 264 ("Unsupported mode: " + opmode); 265 } 266 boolean doEncrypt = 267 (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE); 268 269 byte[] ivBytes = null; 270 if (mech == UcryptoMech.CRYPTO_AES_ECB) { 271 if (params != null) { 272 throw new InvalidAlgorithmParameterException 273 ("No Parameters for ECB mode"); 274 } 275 } else { 276 if (params != null) { 277 if (!(params instanceof IvParameterSpec)) { 278 throw new InvalidAlgorithmParameterException 279 ("IvParameterSpec required. Received: " + 280 params.getClass().getName()); 281 } else { 282 ivBytes = ((IvParameterSpec) params).getIV(); 283 if (ivBytes.length != blockSize) { 284 throw new InvalidAlgorithmParameterException 285 ("Wrong IV length: must be " + blockSize + 286 " bytes long. Received length:" + ivBytes.length); 287 } 288 } 289 } else { 290 if (encrypt) { 291 // generate IV if none supplied for encryption 292 ivBytes = new byte[blockSize]; 293 if (random == null) { 294 random = JCAUtil.getSecureRandom(); 295 } 296 random.nextBytes(ivBytes); 297 } else { 298 throw new InvalidAlgorithmParameterException 299 ("Parameters required for decryption"); 300 } 301 } 302 } 303 init(doEncrypt, key.getEncoded().clone(), ivBytes); 304 } 305 306 // see JCE spec 307 @Override 308 protected synchronized void engineInit(int opmode, Key key, 309 AlgorithmParameters params, SecureRandom random) 310 throws InvalidKeyException, InvalidAlgorithmParameterException { 311 AlgorithmParameterSpec spec = null; 312 if (params != null) { 313 try { 314 spec = params.getParameterSpec(IvParameterSpec.class); 315 } catch (InvalidParameterSpecException iaps) { 316 throw new InvalidAlgorithmParameterException(iaps); 317 } 318 } 319 engineInit(opmode, key, spec, random); 320 } 321 322 // see JCE spec 323 @Override 324 protected synchronized byte[] engineUpdate(byte[] in, int ofs, int len) { 325 byte[] out = new byte[getOutputSizeByOperation(len, false)]; 326 int n = update(in, ofs, len, out, 0); 327 if (n == 0) { 328 return null; 329 } else if (out.length != n) { 330 out = Arrays.copyOf(out, n); 331 } 332 return out; 333 } 334 335 // see JCE spec 336 @Override 337 protected synchronized int engineUpdate(byte[] in, int inOfs, int inLen, 338 byte[] out, int outOfs) throws ShortBufferException { 339 int min = getOutputSizeByOperation(inLen, false); 340 if (out.length - outOfs < min) { 341 throw new ShortBufferException("min " + min + "-byte buffer needed"); 342 } 343 return update(in, inOfs, inLen, out, outOfs); 344 } 345 346 // see JCE spec 347 @Override 348 protected synchronized void engineUpdateAAD(byte[] src, int ofs, int len) 349 throws IllegalStateException { 350 throw new IllegalStateException("No AAD can be supplied"); 351 } 352 353 // see JCE spec 354 @Override 355 protected void engineUpdateAAD(ByteBuffer src) 356 throws IllegalStateException { 357 throw new IllegalStateException("No AAD can be supplied"); 358 } 359 360 // see JCE spec 361 @Override 362 protected synchronized byte[] engineDoFinal(byte[] in, int ofs, int len) 363 throws IllegalBlockSizeException, BadPaddingException { 364 byte[] out = new byte[getOutputSizeByOperation(len, true)]; 365 try { 366 // delegate to the other engineDoFinal(...) method 367 int k = engineDoFinal(in, ofs, len, out, 0); 368 if (out.length != k) { 369 out = Arrays.copyOf(out, k); 370 } 371 return out; 372 } catch (ShortBufferException e) { 373 throw new UcryptoException("Internal Error", e); 374 } 375 } 376 377 // see JCE spec 378 @Override 379 protected synchronized int engineDoFinal(byte[] in, int inOfs, int inLen, 380 byte[] out, int outOfs) 381 throws ShortBufferException, IllegalBlockSizeException, 382 BadPaddingException { 383 int k = 0; 384 int min = getOutputSizeByOperation(inLen, true); 385 if (out.length - outOfs < min) { 386 throw new ShortBufferException("min " + min + "-byte buffer needed"); 387 } 388 if (inLen > 0) { 389 k = update(in, inOfs, inLen, out, outOfs); 390 outOfs += k; 391 } 392 k += doFinal(out, outOfs); 393 return k; 394 } 395 396 397 // see JCE spec 398 @Override 399 protected synchronized byte[] engineWrap(Key key) 400 throws IllegalBlockSizeException, InvalidKeyException { 401 byte[] result = null; 402 try { 403 byte[] encodedKey = key.getEncoded(); 404 if ((encodedKey == null) || (encodedKey.length == 0)) { 405 throw new InvalidKeyException("Cannot get an encoding of " + 406 "the key to be wrapped"); 407 } 408 result = engineDoFinal(encodedKey, 0, encodedKey.length); 409 } catch (BadPaddingException e) { 410 // Should never happen for key wrapping 411 throw new UcryptoException("Internal Error" , e); 412 } 413 return result; 414 } 415 416 // see JCE spec 417 @Override 418 protected synchronized Key engineUnwrap(byte[] wrappedKey, 419 String wrappedKeyAlgorithm, int wrappedKeyType) 420 throws InvalidKeyException, NoSuchAlgorithmException { 421 422 byte[] encodedKey; 423 Key result = null; 424 try { 425 encodedKey = engineDoFinal(wrappedKey, 0, 426 wrappedKey.length); 427 } catch (Exception e) { 428 throw (InvalidKeyException) 429 (new InvalidKeyException()).initCause(e); 430 } 431 432 return constructKey(wrappedKeyType, encodedKey, wrappedKeyAlgorithm); 433 } 434 435 final int checkKey(Key key) throws InvalidKeyException { 436 if (key == null || key.getEncoded() == null) { 437 throw new InvalidKeyException("Key cannot be null"); 438 } else { 439 // check key algorithm and format 440 if (!keyAlgo.equalsIgnoreCase(key.getAlgorithm())) { 441 throw new InvalidKeyException("Key algorithm must be " + 442 keyAlgo); 443 } 444 if (!"RAW".equalsIgnoreCase(key.getFormat())) { 445 throw new InvalidKeyException("Key format must be RAW"); 446 } 447 int keyLen = key.getEncoded().length; 448 if (fixedKeySize == -1) { 449 // all 3 AES key lengths are allowed 450 if (keyLen != 16 && keyLen != 24 && keyLen != 32) { 451 throw new InvalidKeyException("Key size is not valid." + 452 " Got key length of: " + keyLen); 453 } 454 } else { 455 if (keyLen != fixedKeySize) { 456 throw new InvalidKeyException("Only " + fixedKeySize + 457 "-byte keys are accepted. Got: " + keyLen); 458 } 459 } 460 // return the validated key length in bytes 461 return keyLen; 462 } 463 } 464 465 protected void reset(boolean doCancel) { 466 initialized = false; 467 bytesBuffered = 0; 468 if (pCtxt != null) { 469 pCtxt.dispose(doCancel); 470 pCtxt = null; 471 } 472 } 473 474 /** 475 * calls ucrypto_encrypt_init(...) or ucrypto_decrypt_init(...) 476 * @return pointer to the context 477 */ 478 protected native static long nativeInit(int mech, boolean encrypt, 479 byte[] key, byte[] iv, 480 int tagLen, byte[] aad); 481 482 /** 483 * calls ucrypto_encrypt_update(...) or ucrypto_decrypt_update(...) 484 * @return the length of output or if negative, an error status code 485 */ 486 private native static int nativeUpdate(long pContext, boolean encrypt, 487 byte[] in, int inOfs, int inLen, 488 byte[] out, int outOfs); 489 490 /** 491 * calls ucrypto_encrypt_final(...) or ucrypto_decrypt_final(...) 492 * @return the length of output or if negative, an error status code 493 */ 494 native static int nativeFinal(long pContext, boolean encrypt, 495 byte[] out, int outOfs); 496 497 protected void ensureInitialized() { 498 if (!initialized) { 499 init(encrypt, keyValue, iv); 500 if (!initialized) { 501 throw new UcryptoException("Cannot initialize Cipher"); 502 } 503 } 504 } 505 506 protected int getOutputSizeByOperation(int inLen, boolean isDoFinal) { 507 if (inLen <= 0) { 508 inLen = 0; 509 } 510 if (!isDoFinal && (inLen == 0)) { 511 return 0; 512 } 513 return inLen + bytesBuffered; 514 } 515 516 // actual init() implementation - caller should clone key and iv if needed 517 protected void init(boolean encrypt, byte[] keyVal, byte[] ivVal) { 518 reset(true); 519 this.encrypt = encrypt; 520 this.keyValue = keyVal; 521 this.iv = ivVal; 522 long pCtxtVal = nativeInit(mech.value(), encrypt, keyValue, iv, 0, null); 523 initialized = (pCtxtVal != 0L); 524 if (initialized) { 525 pCtxt = new CipherContextRef(this, pCtxtVal, encrypt); 526 } else { 527 throw new UcryptoException("Cannot initialize Cipher"); 528 } 529 } 530 531 // Caller MUST check and ensure output buffer has enough capacity 532 private int update(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) { 533 ensureInitialized(); 534 if (inLen <= 0) { return 0; } 535 536 int k = nativeUpdate(pCtxt.id, encrypt, in, inOfs, inLen, out, outOfs); 537 if (k < 0) { 538 reset(false); 539 // cannot throw ShortBufferException here since it's too late 540 // native context is invalid upon any failure 541 throw new UcryptoException(-k); 542 } 543 bytesBuffered += (inLen - k); 544 return k; 545 } 546 547 // Caller MUST check and ensure output buffer has enough capacity 548 private int doFinal(byte[] out, int outOfs) throws IllegalBlockSizeException, 549 BadPaddingException { 550 try { 551 ensureInitialized(); 552 553 int k = nativeFinal(pCtxt.id, encrypt, out, outOfs); 554 if (k < 0) { 555 String cause = UcryptoException.getErrorMessage(-k); 556 if (cause.endsWith("_LEN_RANGE")) { 557 throw new IllegalBlockSizeException(cause); 558 } else if (cause.endsWith("_DATA_INVALID")) { 559 throw new BadPaddingException(cause); 560 } else { 561 throw new UcryptoException(-k); 562 } 563 } 564 return k; 565 } finally { 566 reset(false); 567 } 568 } 569 }