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 1.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"); 278 } else { 279 ivBytes = ((IvParameterSpec) params).getIV(); 280 if (ivBytes.length != blockSize) { 281 throw new InvalidAlgorithmParameterException 282 ("Wrong IV length: must be " + blockSize + 283 " bytes long"); 284 } 285 } 286 } else { 287 if (encrypt) { 288 // generate IV if none supplied for encryption 289 ivBytes = new byte[blockSize]; 290 new SecureRandom().nextBytes(ivBytes); 291 } else { 292 throw new InvalidAlgorithmParameterException 293 ("Parameters required for decryption"); 294 } 295 } 296 } 297 init(doEncrypt, key.getEncoded().clone(), ivBytes); 298 } 299 300 // see JCE spec 301 @Override 302 protected synchronized void engineInit(int opmode, Key key, 303 AlgorithmParameters params, SecureRandom random) 304 throws InvalidKeyException, InvalidAlgorithmParameterException { 305 AlgorithmParameterSpec spec = null; 306 if (params != null) { 307 try { 308 spec = params.getParameterSpec(IvParameterSpec.class); 309 } catch (InvalidParameterSpecException iaps) { 310 throw new InvalidAlgorithmParameterException(iaps); 311 } 312 } 313 engineInit(opmode, key, spec, random); 314 } 315 316 // see JCE spec 317 @Override 318 protected synchronized byte[] engineUpdate(byte[] in, int ofs, int len) { 319 byte[] out = new byte[getOutputSizeByOperation(len, false)]; 320 int n = update(in, ofs, len, out, 0); 321 if (n == 0) { 322 return null; 323 } else if (out.length != n) { 324 out = Arrays.copyOf(out, n); 325 } 326 return out; 327 } 328 329 // see JCE spec 330 @Override 331 protected synchronized int engineUpdate(byte[] in, int inOfs, int inLen, 332 byte[] out, int outOfs) throws ShortBufferException { 333 int min = getOutputSizeByOperation(inLen, false); 334 if (out.length - outOfs < min) { 335 throw new ShortBufferException("min " + min + "-byte buffer needed"); 336 } 337 return update(in, inOfs, inLen, out, outOfs); 338 } 339 340 // see JCE spec 341 @Override 342 protected synchronized void engineUpdateAAD(byte[] src, int ofs, int len) 343 throws IllegalStateException { 344 throw new IllegalStateException("No AAD can be supplied"); 345 } 346 347 // see JCE spec 348 @Override 349 protected void engineUpdateAAD(ByteBuffer src) 350 throws IllegalStateException { 351 throw new IllegalStateException("No AAD can be supplied"); 352 } 353 354 // see JCE spec 355 @Override 356 protected synchronized byte[] engineDoFinal(byte[] in, int ofs, int len) 357 throws IllegalBlockSizeException, BadPaddingException { 358 byte[] out = new byte[getOutputSizeByOperation(len, true)]; 359 try { 360 // delegate to the other engineDoFinal(...) method 361 int k = engineDoFinal(in, ofs, len, out, 0); 362 if (out.length != k) { 363 out = Arrays.copyOf(out, k); 364 } 365 return out; 366 } catch (ShortBufferException e) { 367 throw new UcryptoException("Internal Error", e); 368 } 369 } 370 371 // see JCE spec 372 @Override 373 protected synchronized int engineDoFinal(byte[] in, int inOfs, int inLen, 374 byte[] out, int outOfs) 375 throws ShortBufferException, IllegalBlockSizeException, 376 BadPaddingException { 377 int k = 0; 378 int min = getOutputSizeByOperation(inLen, true); 379 if (out.length - outOfs < min) { 380 throw new ShortBufferException("min " + min + "-byte buffer needed"); 381 } 382 if (inLen > 0) { 383 k = update(in, inOfs, inLen, out, outOfs); 384 outOfs += k; 385 } 386 k += doFinal(out, outOfs); 387 return k; 388 } 389 390 391 // see JCE spec 392 @Override 393 protected synchronized byte[] engineWrap(Key key) 394 throws IllegalBlockSizeException, InvalidKeyException { 395 byte[] result = null; 396 try { 397 byte[] encodedKey = key.getEncoded(); 398 if ((encodedKey == null) || (encodedKey.length == 0)) { 399 throw new InvalidKeyException("Cannot get an encoding of " + 400 "the key to be wrapped"); 401 } 402 result = engineDoFinal(encodedKey, 0, encodedKey.length); 403 } catch (BadPaddingException e) { 404 // Should never happen for key wrapping 405 throw new UcryptoException("Internal Error" , e); 406 } 407 return result; 408 } 409 410 // see JCE spec 411 @Override 412 protected synchronized Key engineUnwrap(byte[] wrappedKey, 413 String wrappedKeyAlgorithm, int wrappedKeyType) 414 throws InvalidKeyException, NoSuchAlgorithmException { 415 416 byte[] encodedKey; 417 Key result = null; 418 try { 419 encodedKey = engineDoFinal(wrappedKey, 0, 420 wrappedKey.length); 421 } catch (Exception e) { 422 throw (InvalidKeyException) 423 (new InvalidKeyException()).initCause(e); 424 } 425 426 return constructKey(wrappedKeyType, encodedKey, wrappedKeyAlgorithm); 427 } 428 429 final int checkKey(Key key) throws InvalidKeyException { 430 if (key == null || key.getEncoded() == null) { 431 throw new InvalidKeyException("Key cannot be null"); 432 } else { 433 // check key algorithm and format 434 if (!keyAlgo.equalsIgnoreCase(key.getAlgorithm())) { 435 throw new InvalidKeyException("Key algorithm must be " + 436 keyAlgo); 437 } 438 if (!"RAW".equalsIgnoreCase(key.getFormat())) { 439 throw new InvalidKeyException("Key format must be RAW"); 440 } 441 int keyLen = key.getEncoded().length; 442 if (fixedKeySize == -1) { 443 // all 3 AES key lengths are allowed 444 if (keyLen != 16 && keyLen != 24 && keyLen != 32) { 445 throw new InvalidKeyException("Key size is not valid"); 446 } 447 } else { 448 if (keyLen != fixedKeySize) { 449 throw new InvalidKeyException("Only " + fixedKeySize + 450 "-byte keys are accepted"); 451 } 452 } 453 // return the validated key length in bytes 454 return keyLen; 455 } 456 } 457 458 protected void reset(boolean doCancel) { 459 initialized = false; 460 bytesBuffered = 0; 461 if (pCtxt != null) { 462 pCtxt.dispose(doCancel); 463 pCtxt = null; 464 } 465 } 466 467 /** 468 * calls ucrypto_encrypt_init(...) or ucrypto_decrypt_init(...) 469 * @return pointer to the context 470 */ 471 protected native static long nativeInit(int mech, boolean encrypt, 472 byte[] key, byte[] iv, 473 int tagLen, byte[] aad); 474 475 /** 476 * calls ucrypto_encrypt_update(...) or ucrypto_decrypt_update(...) 477 * @returns the length of output or if negative, an error status code 478 */ 479 private native static int nativeUpdate(long pContext, boolean encrypt, 480 byte[] in, int inOfs, int inLen, 481 byte[] out, int outOfs); 482 483 /** 484 * calls ucrypto_encrypt_final(...) or ucrypto_decrypt_final(...) 485 * @returns the length of output or if negative, an error status code 486 */ 487 native static int nativeFinal(long pContext, boolean encrypt, 488 byte[] out, int outOfs); 489 490 protected void ensureInitialized() { 491 if (!initialized) { 492 init(encrypt, keyValue, iv); 493 if (!initialized) { 494 throw new UcryptoException("Cannot initialize Cipher"); 495 } 496 } 497 } 498 499 protected int getOutputSizeByOperation(int inLen, boolean isDoFinal) { 500 if (inLen <= 0) { 501 inLen = 0; 502 } 503 if (!isDoFinal && (inLen == 0)) { 504 return 0; 505 } 506 return inLen + bytesBuffered; 507 } 508 509 // actual init() implementation - caller should clone key and iv if needed 510 protected void init(boolean encrypt, byte[] keyVal, byte[] ivVal) { 511 reset(true); 512 this.encrypt = encrypt; 513 this.keyValue = keyVal; 514 this.iv = ivVal; 515 long pCtxtVal = nativeInit(mech.value(), encrypt, keyValue, iv, 0, null); 516 initialized = (pCtxtVal != 0L); 517 if (initialized) { 518 pCtxt = new CipherContextRef(this, pCtxtVal, encrypt); 519 } else { 520 throw new UcryptoException("Cannot initialize Cipher"); 521 } 522 } 523 524 // Caller MUST check and ensure output buffer has enough capacity 525 private int update(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) { 526 ensureInitialized(); 527 if (inLen <= 0) { return 0; } 528 529 int k = nativeUpdate(pCtxt.id, encrypt, in, inOfs, inLen, out, outOfs); 530 if (k < 0) { 531 reset(false); 532 // cannot throw ShortBufferException here since it's too late 533 // native context is invalid upon any failure 534 throw new UcryptoException(-k); 535 } 536 bytesBuffered += (inLen - k); 537 return k; 538 } 539 540 // Caller MUST check and ensure output buffer has enough capacity 541 private int doFinal(byte[] out, int outOfs) throws IllegalBlockSizeException, 542 BadPaddingException { 543 try { 544 ensureInitialized(); 545 546 int k = nativeFinal(pCtxt.id, encrypt, out, outOfs); 547 if (k < 0) { 548 String cause = UcryptoException.getErrorMessage(-k); 549 if (cause.endsWith("_LEN_RANGE")) { 550 throw new IllegalBlockSizeException(cause); 551 } else if (cause.endsWith("_DATA_INVALID")) { 552 throw new BadPaddingException(cause); 553 } else { 554 throw new UcryptoException(-k); 555 } 556 } 557 return k; 558 } finally { 559 reset(false); 560 } 561 } 562 }