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