/* * Copyright (C) 2014 The Android Open Source Project * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.crypto; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.regex.*; import static java.util.Locale.ENGLISH; import java.security.*; import java.security.Provider.Service; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.InvalidParameterSpecException; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import javax.crypto.spec.*; import java.nio.ByteBuffer; import java.nio.ReadOnlyBufferException; import sun.security.jca.*; /** * This class provides the functionality of a cryptographic cipher for * encryption and decryption. It forms the core of the Java Cryptographic * Extension (JCE) framework. * *

In order to create a Cipher object, the application calls the * Cipher's getInstance method, and passes the name of the * requested transformation to it. Optionally, the name of a provider * may be specified. * *

A transformation is a string that describes the operation (or * set of operations) to be performed on the given input, to produce some * output. A transformation always includes the name of a cryptographic * algorithm (e.g., DES), and may be followed by a feedback mode and * padding scheme. * *

A transformation is of the form: * *

* *

(in the latter case, * provider-specific default values for the mode and padding scheme are used). * For example, the following is a valid transformation: * *

 *     Cipher c = Cipher.getInstance("DES/CBC/PKCS5Padding");
 * 
* * Using modes such as CFB and OFB, block * ciphers can encrypt data in units smaller than the cipher's actual * block size. When requesting such a mode, you may optionally specify * the number of bits to be processed at a time by appending this number * to the mode name as shown in the "DES/CFB8/NoPadding" and * "DES/OFB32/PKCS5Padding" transformations. If no such * number is specified, a provider-specific default is used. (For * example, the SunJCE provider uses a default of 64 bits for DES.) * Thus, block ciphers can be turned into byte-oriented stream ciphers by * using an 8 bit mode such as CFB8 or OFB8. *

* Modes such as Authenticated Encryption with Associated Data (AEAD) * provide authenticity assurances for both confidential data and * Additional Associated Data (AAD) that is not encrypted. (Please see * RFC 5116 for more * information on AEAD and AEAD algorithms such as GCM/CCM.) Both * confidential and AAD data can be used when calculating the * authentication tag (similar to a {@link Mac}). This tag is appended * to the ciphertext during encryption, and is verified on decryption. *

* AEAD modes such as GCM/CCM perform all AAD authenticity calculations * before starting the ciphertext authenticity calculations. To avoid * implementations having to internally buffer ciphertext, all AAD data * must be supplied to GCM/CCM implementations (via the {@code * updateAAD} methods) before the ciphertext is processed (via * the {@code update} and {@code doFinal} methods). *

* Note that GCM mode has a uniqueness requirement on IVs used in * encryption with a given key. When IVs are repeated for GCM * encryption, such usages are subject to forgery attacks. Thus, after * each encryption operation using GCM mode, callers should re-initialize * the cipher objects with GCM parameters which has a different IV value. *

 *     GCMParameterSpec s = ...;
 *     cipher.init(..., s);
 *
 *     // If the GCM parameters were generated by the provider, it can
 *     // be retrieved by:
 *     // cipher.getParameters().getParameterSpec(GCMParameterSpec.class);
 *
 *     cipher.updateAAD(...);  // AAD
 *     cipher.update(...);     // Multi-part update
 *     cipher.doFinal(...);    // conclusion of operation
 *
 *     // Use a different IV value for every encryption
 *     byte[] newIv = ...;
 *     s = new GCMParameterSpec(s.getTLen(), newIv);
 *     cipher.init(..., s);
 *     ...
 *
 * 
*

Android provides the following Cipher transformations: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
AlgorithmModesPaddingsSupported API LevelsNotes
AESCBC
CFB
CTR
CTS
ECB
OFB
ISO10126Padding
NoPadding
PKCS5Padding
1+
GCMNoPadding10+
AES_128CBC
ECB
NoPadding
PKCS5Padding
26+
GCMNoPadding26+
AES_256CBC
ECB
NoPadding
PKCS5Padding
26+
GCMNoPadding26+
ARC4ECBNoPadding10+
NONENoPadding28+
BLOWFISHCBC
CFB
CTR
CTS
ECB
OFB
ISO10126Padding
NoPadding
PKCS5Padding
10+
ChaCha20NONE
Poly1305
NoPadding28+ChaCha with 20 rounds, 96-bit nonce, and 32-bit counter as described in RFC 7539.
DESCBC
CFB
CTR
CTS
ECB
OFB
ISO10126Padding
NoPadding
PKCS5Padding
1+
DESedeCBC
CFB
CTR
CTS
ECB
OFB
ISO10126Padding
NoPadding
PKCS5Padding
1+
RSAECB
NONE
NoPadding
OAEPPadding
PKCS1Padding
1+
OAEPwithSHA-1andMGF1Padding
OAEPwithSHA-256andMGF1Padding
10+
OAEPwithSHA-224andMGF1Padding
OAEPwithSHA-384andMGF1Padding
OAEPwithSHA-512andMGF1Padding
23+
* * These transformations are described in the * * Cipher section of the * Java Cryptography Architecture Standard Algorithm Name Documentation. * * @author Jan Luehe * @see KeyGenerator * @see SecretKey * @since 1.4 */ public class Cipher { // Android-note: Android reimplements provider selection. // // Android uses different provider/impl selection code than upstream does. Provider // selection permeates much of this class, so this class is forked significantly // from the upstream version. Not every change is marked, and any changes to upstream code // should be evaluated to see if they should be merged. // // The changes are chiefly in construction (constructors, getInstance, and createCipher) and // initialization (init and chooseProvider). Most of the actual implementation is in the // classes and methods at the bottom of this file. // Android-removed: this debugging mechanism is not used in Android. /* private static final Debug debug = Debug.getInstance("jca", "Cipher"); private static final Debug pdebug = Debug.getInstance("provider", "Provider"); private static final boolean skipDebug = Debug.isOn("engine=") && !Debug.isOn("cipher"); */ /** * Constant used to initialize cipher to encryption mode. */ public static final int ENCRYPT_MODE = 1; /** * Constant used to initialize cipher to decryption mode. */ public static final int DECRYPT_MODE = 2; /** * Constant used to initialize cipher to key-wrapping mode. */ public static final int WRAP_MODE = 3; /** * Constant used to initialize cipher to key-unwrapping mode. */ public static final int UNWRAP_MODE = 4; /** * Constant used to indicate the to-be-unwrapped key is a "public key". */ public static final int PUBLIC_KEY = 1; /** * Constant used to indicate the to-be-unwrapped key is a "private key". */ public static final int PRIVATE_KEY = 2; /** * Constant used to indicate the to-be-unwrapped key is a "secret key". */ public static final int SECRET_KEY = 3; // The provider private Provider provider; // The provider implementation (delegate) private CipherSpi spi; // The transformation // Android-changed: Made final. final private String transformation; // Android-added: Added tokenizedTransformation. // The tokenized version of transformation final private String[] tokenizedTransformation; // Android-removed: Removed cryptoPerm. /* // Crypto permission representing the maximum allowable cryptographic // strength that this Cipher object can be used for. (The cryptographic // strength is a function of the keysize and algorithm parameters encoded // in the crypto permission.) private CryptoPermission cryptoPerm; */ // The exemption mechanism that needs to be enforced private ExemptionMechanism exmech; // Flag which indicates whether or not this cipher has been initialized private boolean initialized = false; // The operation mode - store the operation mode after the // cipher has been initialized. private int opmode = 0; // The OID for the KeyUsage extension in an X.509 v3 certificate private static final String KEY_USAGE_EXTENSION_OID = "2.5.29.15"; // BEGIN Android-changed: Reimplement provider selection. // See note at top of class. private final SpiAndProviderUpdater spiAndProviderUpdater; /* // next SPI to try in provider selection // null once provider is selected private CipherSpi firstSpi; // next service to try in provider selection // null once provider is selected private Service firstService; // remaining services to try in provider selection // null once provider is selected private Iterator serviceIterator; // list of transform Strings to lookup in the provider private List transforms; private final Object lock; */ // END Android-changed: Reimplement provider selection. /** * Creates a Cipher object. * * @param cipherSpi the delegate * @param provider the provider * @param transformation the transformation */ protected Cipher(CipherSpi cipherSpi, Provider provider, String transformation) { if (cipherSpi == null) { throw new NullPointerException("cipherSpi == null"); } if (!(cipherSpi instanceof NullCipherSpi) && provider == null) { throw new NullPointerException("provider == null"); } this.spi = cipherSpi; this.provider = provider; this.transformation = transformation; this.tokenizedTransformation = null; this.spiAndProviderUpdater = new SpiAndProviderUpdater(provider, cipherSpi); } private Cipher(CipherSpi cipherSpi, Provider provider, String transformation, String[] tokenizedTransformation) { this.spi = cipherSpi; this.provider = provider; this.transformation = transformation; this.tokenizedTransformation = tokenizedTransformation; this.spiAndProviderUpdater = new SpiAndProviderUpdater(provider, cipherSpi); } private static String[] tokenizeTransformation(String transformation) throws NoSuchAlgorithmException { if (transformation == null || transformation.isEmpty()) { throw new NoSuchAlgorithmException("No transformation given"); } /* * array containing the components of a Cipher transformation: * * index 0: algorithm component (e.g., DES) * index 1: feedback component (e.g., CFB) * index 2: padding component (e.g., PKCS5Padding) */ String[] parts = new String[3]; int count = 0; StringTokenizer parser = new StringTokenizer(transformation, "/"); try { while (parser.hasMoreTokens() && count < 3) { parts[count++] = parser.nextToken().trim(); } if (count == 0 || count == 2 || parser.hasMoreTokens()) { throw new NoSuchAlgorithmException("Invalid transformation" + " format:" + transformation); } } catch (NoSuchElementException e) { throw new NoSuchAlgorithmException("Invalid transformation " + "format:" + transformation); } if ((parts[0] == null) || (parts[0].length() == 0)) { throw new NoSuchAlgorithmException("Invalid transformation:" + "algorithm not specified-" + transformation); } return parts; } // BEGIN Android-removed: Reimplement provider selection. // See note at top of class. /* // Provider attribute name for supported chaining mode private final static String ATTR_MODE = "SupportedModes"; // Provider attribute name for supported padding names private final static String ATTR_PAD = "SupportedPaddings"; // constants indicating whether the provider supports // a given mode or padding private final static int S_NO = 0; // does not support private final static int S_MAYBE = 1; // unable to determine private final static int S_YES = 2; // does support /** * Nested class to deal with modes and paddings. * private static class Transform { // transform string to lookup in the provider final String transform; // the mode/padding suffix in upper case. for example, if the algorithm // to lookup is "DES/CBC/PKCS5Padding" suffix is "/CBC/PKCS5PADDING" // if loopup is "DES", suffix is the empty string // needed because aliases prevent straight transform.equals() final String suffix; // value to pass to setMode() or null if no such call required final String mode; // value to pass to setPadding() or null if no such call required final String pad; Transform(String alg, String suffix, String mode, String pad) { this.transform = alg + suffix; this.suffix = suffix.toUpperCase(Locale.ENGLISH); this.mode = mode; this.pad = pad; } // set mode and padding for the given SPI void setModePadding(CipherSpi spi) throws NoSuchAlgorithmException, NoSuchPaddingException { if (mode != null) { spi.engineSetMode(mode); } if (pad != null) { spi.engineSetPadding(pad); } } // check whether the given services supports the mode and // padding described by this Transform int supportsModePadding(Service s) { int smode = supportsMode(s); if (smode == S_NO) { return smode; } int spad = supportsPadding(s); // our constants are defined so that Math.min() is a tri-valued AND return Math.min(smode, spad); } // separate methods for mode and padding // called directly by Cipher only to throw the correct exception int supportsMode(Service s) { return supports(s, ATTR_MODE, mode); } int supportsPadding(Service s) { return supports(s, ATTR_PAD, pad); } private static int supports(Service s, String attrName, String value) { if (value == null) { return S_YES; } String regexp = s.getAttribute(attrName); if (regexp == null) { return S_MAYBE; } return matches(regexp, value) ? S_YES : S_NO; } // ConcurrentMap for previously compiled patterns private final static ConcurrentMap patternCache = new ConcurrentHashMap(); private static boolean matches(String regexp, String str) { Pattern pattern = patternCache.get(regexp); if (pattern == null) { pattern = Pattern.compile(regexp); patternCache.putIfAbsent(regexp, pattern); } return pattern.matcher(str.toUpperCase(Locale.ENGLISH)).matches(); } } private static List getTransforms(String transformation) throws NoSuchAlgorithmException { String[] parts = tokenizeTransformation(transformation); String alg = parts[0]; String mode = parts[1]; String pad = parts[2]; if ((mode != null) && (mode.length() == 0)) { mode = null; } if ((pad != null) && (pad.length() == 0)) { pad = null; } if ((mode == null) && (pad == null)) { // DES Transform tr = new Transform(alg, "", null, null); return Collections.singletonList(tr); } else { // if ((mode != null) && (pad != null)) { // DES/CBC/PKCS5Padding List list = new ArrayList<>(4); list.add(new Transform(alg, "/" + mode + "/" + pad, null, null)); list.add(new Transform(alg, "/" + mode, null, pad)); list.add(new Transform(alg, "//" + pad, mode, null)); list.add(new Transform(alg, "", mode, pad)); return list; } } // get the transform matching the specified service private static Transform getTransform(Service s, List transforms) { String alg = s.getAlgorithm().toUpperCase(Locale.ENGLISH); for (Transform tr : transforms) { if (alg.endsWith(tr.suffix)) { return tr; } } return null; } */ // END Android-removed: Reimplement provider selection. /** * Returns a Cipher object that implements the specified * transformation. * *

This method traverses the list of registered security Providers, * starting with the most preferred Provider. * A new Cipher object encapsulating the * CipherSpi implementation from the first * Provider that supports the specified algorithm is returned. * *

Note that the list of registered providers may be retrieved via * the {@link Security#getProviders() Security.getProviders()} method. * * @param transformation the name of the transformation, e.g., * DES/CBC/PKCS5Padding. * See the Cipher section in the * Java Cryptography Architecture Standard Algorithm Name Documentation * for information about standard transformation names. * * @return a cipher that implements the requested transformation. * * @exception NoSuchAlgorithmException if transformation * is null, empty, in an invalid format, * or if no Provider supports a CipherSpi implementation for the * specified algorithm. * * @exception NoSuchPaddingException if transformation * contains a padding scheme that is not available. * * @see java.security.Provider */ public static final Cipher getInstance(String transformation) throws NoSuchAlgorithmException, NoSuchPaddingException { return createCipher(transformation, null); } /** * Returns a Cipher object that implements the specified * transformation. * *

A new Cipher object encapsulating the * CipherSpi implementation from the specified provider * is returned. The specified provider must be registered * in the security provider list. * *

Note that the list of registered providers may be retrieved via * the {@link Security#getProviders() Security.getProviders()} method. * * @param transformation the name of the transformation, * e.g., DES/CBC/PKCS5Padding. * See the Cipher section in the * Java Cryptography Architecture Standard Algorithm Name Documentation * for information about standard transformation names. * * @param provider the name of the provider. * * @return a cipher that implements the requested transformation. * * @exception NoSuchAlgorithmException if transformation * is null, empty, in an invalid format, * or if a CipherSpi implementation for the specified algorithm * is not available from the specified provider. * * @exception NoSuchProviderException if the specified provider is not * registered in the security provider list. * * @exception NoSuchPaddingException if transformation * contains a padding scheme that is not available. * * @exception IllegalArgumentException if the provider * is null or empty. * * @see java.security.Provider */ public static final Cipher getInstance(String transformation, String provider) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException { if ((provider == null) || (provider.length() == 0)) { throw new IllegalArgumentException("Missing provider"); } Provider p = Security.getProvider(provider); if (p == null) { throw new NoSuchProviderException("No such provider: " + provider); } return getInstance(transformation, p); } /** * Returns a Cipher object that implements the specified * transformation. * *

A new Cipher object encapsulating the * CipherSpi implementation from the specified Provider * object is returned. Note that the specified Provider object * does not have to be registered in the provider list. * * @param transformation the name of the transformation, * e.g., DES/CBC/PKCS5Padding. * See the Cipher section in the * Java Cryptography Architecture Standard Algorithm Name Documentation * for information about standard transformation names. * * @param provider the provider. * * @return a cipher that implements the requested transformation. * * @exception NoSuchAlgorithmException if transformation * is null, empty, in an invalid format, * or if a CipherSpi implementation for the specified algorithm * is not available from the specified Provider object. * * @exception NoSuchPaddingException if transformation * contains a padding scheme that is not available. * * @exception IllegalArgumentException if the provider * is null. * * @see java.security.Provider */ public static final Cipher getInstance(String transformation, Provider provider) throws NoSuchAlgorithmException, NoSuchPaddingException { if (provider == null) { throw new IllegalArgumentException("Missing provider"); } return createCipher(transformation, provider); } static final Cipher createCipher(String transformation, Provider provider) throws NoSuchAlgorithmException, NoSuchPaddingException { Providers.checkBouncyCastleDeprecation(provider, "Cipher", transformation); String[] tokenizedTransformation = tokenizeTransformation(transformation); CipherSpiAndProvider cipherSpiAndProvider = null; try { cipherSpiAndProvider = tryCombinations(null /*params*/, provider, tokenizedTransformation); } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { // Shouldn't happen. throw new IllegalStateException("Key/Algorithm excepton despite not passing one", e); } if (cipherSpiAndProvider == null) { if (provider == null) { throw new NoSuchAlgorithmException("No provider found for " + transformation); } else { throw new NoSuchAlgorithmException("Provider " + provider.getName() + " does not provide " + transformation); } } // exceptions and stuff return new Cipher(null, provider, transformation, tokenizedTransformation); } /** * Choose the Spi from the first provider available. Used if * delayed provider selection is not possible because init() * is not the first method called. */ void updateProviderIfNeeded() { try { spiAndProviderUpdater.updateAndGetSpiAndProvider(null, spi, provider); } catch (Exception lastException) { ProviderException e = new ProviderException ("Could not construct CipherSpi instance"); if (lastException != null) { e.initCause(lastException); } throw e; } } private void chooseProvider(InitType initType, int opmode, Key key, AlgorithmParameterSpec paramSpec, AlgorithmParameters params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { try { final InitParams initParams = new InitParams(initType, opmode, key, random, paramSpec, params); spiAndProviderUpdater.updateAndGetSpiAndProvider(initParams, spi, provider); } catch (Exception lastException) { // no working provider found, fail if (lastException instanceof InvalidKeyException) { throw (InvalidKeyException)lastException; } if (lastException instanceof InvalidAlgorithmParameterException) { throw (InvalidAlgorithmParameterException)lastException; } if (lastException instanceof RuntimeException) { throw (RuntimeException)lastException; } String kName = (key != null) ? key.getClass().getName() : "(null)"; throw new InvalidKeyException ("No installed provider supports this key: " + kName, lastException); } } /** * Returns the provider of this Cipher object. * * @return the provider of this Cipher object */ public final Provider getProvider() { updateProviderIfNeeded(); return this.provider; } /** * Returns the algorithm name of this Cipher object. * *

This is the same name that was specified in one of the * getInstance calls that created this Cipher * object.. * * @return the algorithm name of this Cipher object. */ public final String getAlgorithm() { return this.transformation; } /** * Returns the block size (in bytes). * * @return the block size (in bytes), or 0 if the underlying algorithm is * not a block cipher */ public final int getBlockSize() { updateProviderIfNeeded(); return spi.engineGetBlockSize(); } /** * Returns the length in bytes that an output buffer would need to be in * order to hold the result of the next update or * doFinal operation, given the input length * inputLen (in bytes). * *

This call takes into account any unprocessed (buffered) data from a * previous update call, padding, and AEAD tagging. * *

The actual output length of the next update or * doFinal call may be smaller than the length returned by * this method. * * @param inputLen the input length (in bytes) * * @return the required output buffer size (in bytes) * * @exception IllegalStateException if this cipher is in a wrong state * (e.g., has not yet been initialized) */ public final int getOutputSize(int inputLen) { if (!initialized && !(this instanceof NullCipher)) { throw new IllegalStateException("Cipher not initialized"); } if (inputLen < 0) { throw new IllegalArgumentException("Input size must be equal " + "to or greater than zero"); } updateProviderIfNeeded(); return spi.engineGetOutputSize(inputLen); } /** * Returns the initialization vector (IV) in a new buffer. * *

This is useful in the case where a random IV was created, * or in the context of password-based encryption or * decryption, where the IV is derived from a user-supplied password. * * @return the initialization vector in a new buffer, or null if the * underlying algorithm does not use an IV, or if the IV has not yet * been set. */ public final byte[] getIV() { updateProviderIfNeeded(); return spi.engineGetIV(); } /** * Returns the parameters used with this cipher. * *

The returned parameters may be the same that were used to initialize * this cipher, or may contain a combination of default and random * parameter values used by the underlying cipher implementation if this * cipher requires algorithm parameters but was not initialized with any. * * @return the parameters used with this cipher, or null if this cipher * does not use any parameters. */ public final AlgorithmParameters getParameters() { updateProviderIfNeeded(); return spi.engineGetParameters(); } /** * Returns the exemption mechanism object used with this cipher. * * @return the exemption mechanism object used with this cipher, or * null if this cipher does not use any exemption mechanism. */ public final ExemptionMechanism getExemptionMechanism() { updateProviderIfNeeded(); return exmech; } // BEGIN Android-removed: Eliminate crypto permission checking. // Android doesn't implement SecurityManager permissions. /* // // Crypto permission check code below // private void checkCryptoPerm(CipherSpi checkSpi, Key key) throws InvalidKeyException { if (cryptoPerm == CryptoAllPermission.INSTANCE) { return; } // Check if key size and default parameters are within legal limits AlgorithmParameterSpec params; try { params = getAlgorithmParameterSpec(checkSpi.engineGetParameters()); } catch (InvalidParameterSpecException ipse) { throw new InvalidKeyException ("Unsupported default algorithm parameters"); } if (!passCryptoPermCheck(checkSpi, key, params)) { throw new InvalidKeyException( "Illegal key size or default parameters"); } } private void checkCryptoPerm(CipherSpi checkSpi, Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException { if (cryptoPerm == CryptoAllPermission.INSTANCE) { return; } // Determine keysize and check if it is within legal limits if (!passCryptoPermCheck(checkSpi, key, null)) { throw new InvalidKeyException("Illegal key size"); } if ((params != null) && (!passCryptoPermCheck(checkSpi, key, params))) { throw new InvalidAlgorithmParameterException("Illegal parameters"); } } private void checkCryptoPerm(CipherSpi checkSpi, Key key, AlgorithmParameters params) throws InvalidKeyException, InvalidAlgorithmParameterException { if (cryptoPerm == CryptoAllPermission.INSTANCE) { return; } // Convert the specified parameters into specs and then delegate. AlgorithmParameterSpec pSpec; try { pSpec = getAlgorithmParameterSpec(params); } catch (InvalidParameterSpecException ipse) { throw new InvalidAlgorithmParameterException ("Failed to retrieve algorithm parameter specification"); } checkCryptoPerm(checkSpi, key, pSpec); } private boolean passCryptoPermCheck(CipherSpi checkSpi, Key key, AlgorithmParameterSpec params) throws InvalidKeyException { String em = cryptoPerm.getExemptionMechanism(); int keySize = checkSpi.engineGetKeySize(key); // Use the "algorithm" component of the cipher // transformation so that the perm check would // work when the key has the "aliased" algo. String algComponent; int index = transformation.indexOf('/'); if (index != -1) { algComponent = transformation.substring(0, index); } else { algComponent = transformation; } CryptoPermission checkPerm = new CryptoPermission(algComponent, keySize, params, em); if (!cryptoPerm.implies(checkPerm)) { if (debug != null) { debug.println("Crypto Permission check failed"); debug.println("granted: " + cryptoPerm); debug.println("requesting: " + checkPerm); } return false; } if (exmech == null) { return true; } try { if (!exmech.isCryptoAllowed(key)) { if (debug != null) { debug.println(exmech.getName() + " isn't enforced"); } return false; } } catch (ExemptionMechanismException eme) { if (debug != null) { debug.println("Cannot determine whether "+ exmech.getName() + " has been enforced"); eme.printStackTrace(); } return false; } return true; } */ // END Android-removed: Eliminate crypto permission checking. // check if opmode is one of the defined constants // throw InvalidParameterExeption if not private static void checkOpmode(int opmode) { if ((opmode < ENCRYPT_MODE) || (opmode > UNWRAP_MODE)) { throw new InvalidParameterException("Invalid operation mode"); } } private static String getOpmodeString(int opmode) { switch (opmode) { case ENCRYPT_MODE: return "encryption"; case DECRYPT_MODE: return "decryption"; case WRAP_MODE: return "key wrapping"; case UNWRAP_MODE: return "key unwrapping"; default: return ""; } } /** * Initializes this cipher with a key. * *

The cipher is initialized for one of the following four operations: * encryption, decryption, key wrapping or key unwrapping, depending * on the value of opmode. * *

If this cipher requires any algorithm parameters that cannot be * derived from the given key, the underlying cipher * implementation is supposed to generate the required parameters itself * (using provider-specific default or random values) if it is being * initialized for encryption or key wrapping, and raise an * InvalidKeyException if it is being * initialized for decryption or key unwrapping. * The generated parameters can be retrieved using * {@link #getParameters() getParameters} or * {@link #getIV() getIV} (if the parameter is an IV). * *

If this cipher requires algorithm parameters that cannot be * derived from the input parameters, and there are no reasonable * provider-specific default values, initialization will * necessarily fail. * *

If this cipher (including its underlying feedback or padding scheme) * requires any random bytes (e.g., for parameter generation), it will get * them using the {@link java.security.SecureRandom} * implementation of the highest-priority * installed provider as the source of randomness. * (If none of the installed providers supply an implementation of * SecureRandom, a system-provided source of randomness will be used.) * *

Note that when a Cipher object is initialized, it loses all * previously-acquired state. In other words, initializing a Cipher is * equivalent to creating a new instance of that Cipher and initializing * it. * * @param opmode the operation mode of this cipher (this is one of * the following: * ENCRYPT_MODE, DECRYPT_MODE, * WRAP_MODE or UNWRAP_MODE) * @param key the key * * @exception InvalidKeyException if the given key is inappropriate for * initializing this cipher, or requires * algorithm parameters that cannot be * determined from the given key, or if the given key has a keysize that * exceeds the maximum allowable keysize (as determined from the * configured jurisdiction policy files). * @throws UnsupportedOperationException if (@code opmode} is * {@code WRAP_MODE} or {@code UNWRAP_MODE} but the mode is not implemented * by the underlying {@code CipherSpi}. */ public final void init(int opmode, Key key) throws InvalidKeyException { init(opmode, key, JceSecurity.RANDOM); } /** * Initializes this cipher with a key and a source of randomness. * *

The cipher is initialized for one of the following four operations: * encryption, decryption, key wrapping or key unwrapping, depending * on the value of opmode. * *

If this cipher requires any algorithm parameters that cannot be * derived from the given key, the underlying cipher * implementation is supposed to generate the required parameters itself * (using provider-specific default or random values) if it is being * initialized for encryption or key wrapping, and raise an * InvalidKeyException if it is being * initialized for decryption or key unwrapping. * The generated parameters can be retrieved using * {@link #getParameters() getParameters} or * {@link #getIV() getIV} (if the parameter is an IV). * *

If this cipher requires algorithm parameters that cannot be * derived from the input parameters, and there are no reasonable * provider-specific default values, initialization will * necessarily fail. * *

If this cipher (including its underlying feedback or padding scheme) * requires any random bytes (e.g., for parameter generation), it will get * them from random. * *

Note that when a Cipher object is initialized, it loses all * previously-acquired state. In other words, initializing a Cipher is * equivalent to creating a new instance of that Cipher and initializing * it. * * @param opmode the operation mode of this cipher (this is one of the * following: * ENCRYPT_MODE, DECRYPT_MODE, * WRAP_MODE or UNWRAP_MODE) * @param key the encryption key * @param random the source of randomness * * @exception InvalidKeyException if the given key is inappropriate for * initializing this cipher, or requires * algorithm parameters that cannot be * determined from the given key, or if the given key has a keysize that * exceeds the maximum allowable keysize (as determined from the * configured jurisdiction policy files). * @throws UnsupportedOperationException if (@code opmode} is * {@code WRAP_MODE} or {@code UNWRAP_MODE} but the mode is not implemented * by the underlying {@code CipherSpi}. */ public final void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException { initialized = false; checkOpmode(opmode); try { chooseProvider(InitType.KEY, opmode, key, null, null, random); } catch (InvalidAlgorithmParameterException e) { // should never occur throw new InvalidKeyException(e); } initialized = true; this.opmode = opmode; // Android-removed: this debugging mechanism is not used in Android. /* if (!skipDebug && pdebug != null) { pdebug.println("Cipher." + transformation + " " + getOpmodeString(opmode) + " algorithm from: " + this.provider.getName()); } */ } /** * Initializes this cipher with a key and a set of algorithm * parameters. * *

The cipher is initialized for one of the following four operations: * encryption, decryption, key wrapping or key unwrapping, depending * on the value of opmode. * *

If this cipher requires any algorithm parameters and * params is null, the underlying cipher implementation is * supposed to generate the required parameters itself (using * provider-specific default or random values) if it is being * initialized for encryption or key wrapping, and raise an * InvalidAlgorithmParameterException if it is being * initialized for decryption or key unwrapping. * The generated parameters can be retrieved using * {@link #getParameters() getParameters} or * {@link #getIV() getIV} (if the parameter is an IV). * *

If this cipher requires algorithm parameters that cannot be * derived from the input parameters, and there are no reasonable * provider-specific default values, initialization will * necessarily fail. * *

If this cipher (including its underlying feedback or padding scheme) * requires any random bytes (e.g., for parameter generation), it will get * them using the {@link java.security.SecureRandom} * implementation of the highest-priority * installed provider as the source of randomness. * (If none of the installed providers supply an implementation of * SecureRandom, a system-provided source of randomness will be used.) * *

Note that when a Cipher object is initialized, it loses all * previously-acquired state. In other words, initializing a Cipher is * equivalent to creating a new instance of that Cipher and initializing * it. * * @param opmode the operation mode of this cipher (this is one of the * following: * ENCRYPT_MODE, DECRYPT_MODE, * WRAP_MODE or UNWRAP_MODE) * @param key the encryption key * @param params the algorithm parameters * * @exception InvalidKeyException if the given key is inappropriate for * initializing this cipher, or its keysize exceeds the maximum allowable * keysize (as determined from the configured jurisdiction policy files). * @exception InvalidAlgorithmParameterException if the given algorithm * parameters are inappropriate for this cipher, * or this cipher requires * algorithm parameters and params is null, or the given * algorithm parameters imply a cryptographic strength that would exceed * the legal limits (as determined from the configured jurisdiction * policy files). * @throws UnsupportedOperationException if (@code opmode} is * {@code WRAP_MODE} or {@code UNWRAP_MODE} but the mode is not implemented * by the underlying {@code CipherSpi}. */ public final void init(int opmode, Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException { init(opmode, key, params, JceSecurity.RANDOM); } /** * Initializes this cipher with a key, a set of algorithm * parameters, and a source of randomness. * *

The cipher is initialized for one of the following four operations: * encryption, decryption, key wrapping or key unwrapping, depending * on the value of opmode. * *

If this cipher requires any algorithm parameters and * params is null, the underlying cipher implementation is * supposed to generate the required parameters itself (using * provider-specific default or random values) if it is being * initialized for encryption or key wrapping, and raise an * InvalidAlgorithmParameterException if it is being * initialized for decryption or key unwrapping. * The generated parameters can be retrieved using * {@link #getParameters() getParameters} or * {@link #getIV() getIV} (if the parameter is an IV). * *

If this cipher requires algorithm parameters that cannot be * derived from the input parameters, and there are no reasonable * provider-specific default values, initialization will * necessarily fail. * *

If this cipher (including its underlying feedback or padding scheme) * requires any random bytes (e.g., for parameter generation), it will get * them from random. * *

Note that when a Cipher object is initialized, it loses all * previously-acquired state. In other words, initializing a Cipher is * equivalent to creating a new instance of that Cipher and initializing * it. * * @param opmode the operation mode of this cipher (this is one of the * following: * ENCRYPT_MODE, DECRYPT_MODE, * WRAP_MODE or UNWRAP_MODE) * @param key the encryption key * @param params the algorithm parameters * @param random the source of randomness * * @exception InvalidKeyException if the given key is inappropriate for * initializing this cipher, or its keysize exceeds the maximum allowable * keysize (as determined from the configured jurisdiction policy files). * @exception InvalidAlgorithmParameterException if the given algorithm * parameters are inappropriate for this cipher, * or this cipher requires * algorithm parameters and params is null, or the given * algorithm parameters imply a cryptographic strength that would exceed * the legal limits (as determined from the configured jurisdiction * policy files). * @throws UnsupportedOperationException if (@code opmode} is * {@code WRAP_MODE} or {@code UNWRAP_MODE} but the mode is not implemented * by the underlying {@code CipherSpi}. */ public final void init(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { initialized = false; checkOpmode(opmode); chooseProvider(InitType.ALGORITHM_PARAM_SPEC, opmode, key, params, null, random); initialized = true; this.opmode = opmode; // Android-removed: this debugging mechanism is not used in Android. /* if (!skipDebug && pdebug != null) { pdebug.println("Cipher." + transformation + " " + getOpmodeString(opmode) + " algorithm from: " + this.provider.getName()); } */ } /** * Initializes this cipher with a key and a set of algorithm * parameters. * *

The cipher is initialized for one of the following four operations: * encryption, decryption, key wrapping or key unwrapping, depending * on the value of opmode. * *

If this cipher requires any algorithm parameters and * params is null, the underlying cipher implementation is * supposed to generate the required parameters itself (using * provider-specific default or random values) if it is being * initialized for encryption or key wrapping, and raise an * InvalidAlgorithmParameterException if it is being * initialized for decryption or key unwrapping. * The generated parameters can be retrieved using * {@link #getParameters() getParameters} or * {@link #getIV() getIV} (if the parameter is an IV). * *

If this cipher requires algorithm parameters that cannot be * derived from the input parameters, and there are no reasonable * provider-specific default values, initialization will * necessarily fail. * *

If this cipher (including its underlying feedback or padding scheme) * requires any random bytes (e.g., for parameter generation), it will get * them using the {@link java.security.SecureRandom} * implementation of the highest-priority * installed provider as the source of randomness. * (If none of the installed providers supply an implementation of * SecureRandom, a system-provided source of randomness will be used.) * *

Note that when a Cipher object is initialized, it loses all * previously-acquired state. In other words, initializing a Cipher is * equivalent to creating a new instance of that Cipher and initializing * it. * * @param opmode the operation mode of this cipher (this is one of the * following: ENCRYPT_MODE, * DECRYPT_MODE, WRAP_MODE * or UNWRAP_MODE) * @param key the encryption key * @param params the algorithm parameters * * @exception InvalidKeyException if the given key is inappropriate for * initializing this cipher, or its keysize exceeds the maximum allowable * keysize (as determined from the configured jurisdiction policy files). * @exception InvalidAlgorithmParameterException if the given algorithm * parameters are inappropriate for this cipher, * or this cipher requires * algorithm parameters and params is null, or the given * algorithm parameters imply a cryptographic strength that would exceed * the legal limits (as determined from the configured jurisdiction * policy files). * @throws UnsupportedOperationException if (@code opmode} is * {@code WRAP_MODE} or {@code UNWRAP_MODE} but the mode is not implemented * by the underlying {@code CipherSpi}. */ public final void init(int opmode, Key key, AlgorithmParameters params) throws InvalidKeyException, InvalidAlgorithmParameterException { init(opmode, key, params, JceSecurity.RANDOM); } /** * Initializes this cipher with a key, a set of algorithm * parameters, and a source of randomness. * *

The cipher is initialized for one of the following four operations: * encryption, decryption, key wrapping or key unwrapping, depending * on the value of opmode. * *

If this cipher requires any algorithm parameters and * params is null, the underlying cipher implementation is * supposed to generate the required parameters itself (using * provider-specific default or random values) if it is being * initialized for encryption or key wrapping, and raise an * InvalidAlgorithmParameterException if it is being * initialized for decryption or key unwrapping. * The generated parameters can be retrieved using * {@link #getParameters() getParameters} or * {@link #getIV() getIV} (if the parameter is an IV). * *

If this cipher requires algorithm parameters that cannot be * derived from the input parameters, and there are no reasonable * provider-specific default values, initialization will * necessarily fail. * *

If this cipher (including its underlying feedback or padding scheme) * requires any random bytes (e.g., for parameter generation), it will get * them from random. * *

Note that when a Cipher object is initialized, it loses all * previously-acquired state. In other words, initializing a Cipher is * equivalent to creating a new instance of that Cipher and initializing * it. * * @param opmode the operation mode of this cipher (this is one of the * following: ENCRYPT_MODE, * DECRYPT_MODE, WRAP_MODE * or UNWRAP_MODE) * @param key the encryption key * @param params the algorithm parameters * @param random the source of randomness * * @exception InvalidKeyException if the given key is inappropriate for * initializing this cipher, or its keysize exceeds the maximum allowable * keysize (as determined from the configured jurisdiction policy files). * @exception InvalidAlgorithmParameterException if the given algorithm * parameters are inappropriate for this cipher, * or this cipher requires * algorithm parameters and params is null, or the given * algorithm parameters imply a cryptographic strength that would exceed * the legal limits (as determined from the configured jurisdiction * policy files). * @throws UnsupportedOperationException if (@code opmode} is * {@code WRAP_MODE} or {@code UNWRAP_MODE} but the mode is not implemented * by the underlying {@code CipherSpi}. */ public final void init(int opmode, Key key, AlgorithmParameters params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { initialized = false; checkOpmode(opmode); chooseProvider(InitType.ALGORITHM_PARAMS, opmode, key, null, params, random); initialized = true; this.opmode = opmode; // Android-removed: this debugging mechanism is not used in Android. /* if (!skipDebug && pdebug != null) { pdebug.println("Cipher." + transformation + " " + getOpmodeString(opmode) + " algorithm from: " + this.provider.getName()); } */ } /** * Initializes this cipher with the public key from the given certificate. *

The cipher is initialized for one of the following four operations: * encryption, decryption, key wrapping or key unwrapping, depending * on the value of opmode. * *

If the certificate is of type X.509 and has a key usage * extension field marked as critical, and the value of the key usage * extension field implies that the public key in * the certificate and its corresponding private key are not * supposed to be used for the operation represented by the value * of opmode, * an InvalidKeyException * is thrown. * *

If this cipher requires any algorithm parameters that cannot be * derived from the public key in the given certificate, the underlying * cipher * implementation is supposed to generate the required parameters itself * (using provider-specific default or random values) if it is being * initialized for encryption or key wrapping, and raise an * InvalidKeyException if it is being initialized for decryption or * key unwrapping. * The generated parameters can be retrieved using * {@link #getParameters() getParameters} or * {@link #getIV() getIV} (if the parameter is an IV). * *

If this cipher requires algorithm parameters that cannot be * derived from the input parameters, and there are no reasonable * provider-specific default values, initialization will * necessarily fail. * *

If this cipher (including its underlying feedback or padding scheme) * requires any random bytes (e.g., for parameter generation), it will get * them using the * SecureRandom * implementation of the highest-priority * installed provider as the source of randomness. * (If none of the installed providers supply an implementation of * SecureRandom, a system-provided source of randomness will be used.) * *

Note that when a Cipher object is initialized, it loses all * previously-acquired state. In other words, initializing a Cipher is * equivalent to creating a new instance of that Cipher and initializing * it. * * @param opmode the operation mode of this cipher (this is one of the * following: * ENCRYPT_MODE, DECRYPT_MODE, * WRAP_MODE or UNWRAP_MODE) * @param certificate the certificate * * @exception InvalidKeyException if the public key in the given * certificate is inappropriate for initializing this cipher, or this * cipher requires algorithm parameters that cannot be determined from the * public key in the given certificate, or the keysize of the public key * in the given certificate has a keysize that exceeds the maximum * allowable keysize (as determined by the configured jurisdiction policy * files). * @throws UnsupportedOperationException if (@code opmode} is * {@code WRAP_MODE} or {@code UNWRAP_MODE} but the mode is not implemented * by the underlying {@code CipherSpi}. */ public final void init(int opmode, Certificate certificate) throws InvalidKeyException { init(opmode, certificate, JceSecurity.RANDOM); } /** * Initializes this cipher with the public key from the given certificate * and * a source of randomness. * *

The cipher is initialized for one of the following four operations: * encryption, decryption, key wrapping * or key unwrapping, depending on * the value of opmode. * *

If the certificate is of type X.509 and has a key usage * extension field marked as critical, and the value of the key usage * extension field implies that the public key in * the certificate and its corresponding private key are not * supposed to be used for the operation represented by the value of * opmode, * an InvalidKeyException * is thrown. * *

If this cipher requires any algorithm parameters that cannot be * derived from the public key in the given certificate, * the underlying cipher * implementation is supposed to generate the required parameters itself * (using provider-specific default or random values) if it is being * initialized for encryption or key wrapping, and raise an * InvalidKeyException if it is being * initialized for decryption or key unwrapping. * The generated parameters can be retrieved using * {@link #getParameters() getParameters} or * {@link #getIV() getIV} (if the parameter is an IV). * *

If this cipher requires algorithm parameters that cannot be * derived from the input parameters, and there are no reasonable * provider-specific default values, initialization will * necessarily fail. * *

If this cipher (including its underlying feedback or padding scheme) * requires any random bytes (e.g., for parameter generation), it will get * them from random. * *

Note that when a Cipher object is initialized, it loses all * previously-acquired state. In other words, initializing a Cipher is * equivalent to creating a new instance of that Cipher and initializing * it. * * @param opmode the operation mode of this cipher (this is one of the * following: * ENCRYPT_MODE, DECRYPT_MODE, * WRAP_MODE or UNWRAP_MODE) * @param certificate the certificate * @param random the source of randomness * * @exception InvalidKeyException if the public key in the given * certificate is inappropriate for initializing this cipher, or this * cipher * requires algorithm parameters that cannot be determined from the * public key in the given certificate, or the keysize of the public key * in the given certificate has a keysize that exceeds the maximum * allowable keysize (as determined by the configured jurisdiction policy * files). * @throws UnsupportedOperationException if (@code opmode} is * {@code WRAP_MODE} or {@code UNWRAP_MODE} but the mode is not implemented * by the underlying {@code CipherSpi}. */ public final void init(int opmode, Certificate certificate, SecureRandom random) throws InvalidKeyException { initialized = false; checkOpmode(opmode); // Check key usage if the certificate is of // type X.509. if (certificate instanceof java.security.cert.X509Certificate) { // Check whether the cert has a key usage extension // marked as a critical extension. X509Certificate cert = (X509Certificate)certificate; Set critSet = cert.getCriticalExtensionOIDs(); if (critSet != null && !critSet.isEmpty() && critSet.contains(KEY_USAGE_EXTENSION_OID)) { boolean[] keyUsageInfo = cert.getKeyUsage(); // keyUsageInfo[2] is for keyEncipherment; // keyUsageInfo[3] is for dataEncipherment. if ((keyUsageInfo != null) && (((opmode == Cipher.ENCRYPT_MODE) && (keyUsageInfo.length > 3) && (keyUsageInfo[3] == false)) || ((opmode == Cipher.WRAP_MODE) && (keyUsageInfo.length > 2) && (keyUsageInfo[2] == false)))) { throw new InvalidKeyException("Wrong key usage"); } } } PublicKey publicKey = (certificate==null? null:certificate.getPublicKey()); try { chooseProvider(InitType.KEY, opmode, (Key) publicKey, null, null, random); } catch (InvalidAlgorithmParameterException e) { // should never occur throw new InvalidKeyException(e); } initialized = true; this.opmode = opmode; // Android-removed: this debugging mechanism is not used in Android. /* if (!skipDebug && pdebug != null) { pdebug.println("Cipher." + transformation + " " + getOpmodeString(opmode) + " algorithm from: " + this.provider.getName()); } */ } /** * Ensures that Cipher is in a valid state for update() and doFinal() * calls - should be initialized and in ENCRYPT_MODE or DECRYPT_MODE. * @throws IllegalStateException if Cipher object is not in valid state. */ private void checkCipherState() { if (!(this instanceof NullCipher)) { if (!initialized) { throw new IllegalStateException("Cipher not initialized"); } if ((opmode != Cipher.ENCRYPT_MODE) && (opmode != Cipher.DECRYPT_MODE)) { throw new IllegalStateException("Cipher not initialized " + "for encryption/decryption"); } } } /** * Continues a multiple-part encryption or decryption operation * (depending on how this cipher was initialized), processing another data * part. * *

The bytes in the input buffer are processed, and the * result is stored in a new buffer. * *

If input has a length of zero, this method returns * null. * * @param input the input buffer * * @return the new buffer with the result, or null if the underlying * cipher is a block cipher and the input data is too short to result in a * new block. * * @exception IllegalStateException if this cipher is in a wrong state * (e.g., has not been initialized) */ public final byte[] update(byte[] input) { checkCipherState(); // Input sanity check if (input == null) { throw new IllegalArgumentException("Null input buffer"); } updateProviderIfNeeded(); if (input.length == 0) { return null; } return spi.engineUpdate(input, 0, input.length); } /** * Continues a multiple-part encryption or decryption operation * (depending on how this cipher was initialized), processing another data * part. * *

The first inputLen bytes in the input * buffer, starting at inputOffset inclusive, are processed, * and the result is stored in a new buffer. * *

If inputLen is zero, this method returns * null. * * @param input the input buffer * @param inputOffset the offset in input where the input * starts * @param inputLen the input length * * @return the new buffer with the result, or null if the underlying * cipher is a block cipher and the input data is too short to result in a * new block. * * @exception IllegalStateException if this cipher is in a wrong state * (e.g., has not been initialized) */ public final byte[] update(byte[] input, int inputOffset, int inputLen) { checkCipherState(); // Input sanity check if (input == null || inputOffset < 0 || inputLen > (input.length - inputOffset) || inputLen < 0) { throw new IllegalArgumentException("Bad arguments"); } updateProviderIfNeeded(); if (inputLen == 0) { return null; } return spi.engineUpdate(input, inputOffset, inputLen); } /** * Continues a multiple-part encryption or decryption operation * (depending on how this cipher was initialized), processing another data * part. * *

The first inputLen bytes in the input * buffer, starting at inputOffset inclusive, are processed, * and the result is stored in the output buffer. * *

If the output buffer is too small to hold the result, * a ShortBufferException is thrown. In this case, repeat this * call with a larger output buffer. Use * {@link #getOutputSize(int) getOutputSize} to determine how big * the output buffer should be. * *

If inputLen is zero, this method returns * a length of zero. * *

Note: this method should be copy-safe, which means the * input and output buffers can reference * the same byte array and no unprocessed input data is overwritten * when the result is copied into the output buffer. * * @param input the input buffer * @param inputOffset the offset in input where the input * starts * @param inputLen the input length * @param output the buffer for the result * * @return the number of bytes stored in output * * @exception IllegalStateException if this cipher is in a wrong state * (e.g., has not been initialized) * @exception ShortBufferException if the given output buffer is too small * to hold the result */ public final int update(byte[] input, int inputOffset, int inputLen, byte[] output) throws ShortBufferException { checkCipherState(); // Input sanity check if (input == null || inputOffset < 0 || inputLen > (input.length - inputOffset) || inputLen < 0) { throw new IllegalArgumentException("Bad arguments"); } updateProviderIfNeeded(); if (inputLen == 0) { return 0; } return spi.engineUpdate(input, inputOffset, inputLen, output, 0); } /** * Continues a multiple-part encryption or decryption operation * (depending on how this cipher was initialized), processing another data * part. * *

The first inputLen bytes in the input * buffer, starting at inputOffset inclusive, are processed, * and the result is stored in the output buffer, starting at * outputOffset inclusive. * *

If the output buffer is too small to hold the result, * a ShortBufferException is thrown. In this case, repeat this * call with a larger output buffer. Use * {@link #getOutputSize(int) getOutputSize} to determine how big * the output buffer should be. * *

If inputLen is zero, this method returns * a length of zero. * *

Note: this method should be copy-safe, which means the * input and output buffers can reference * the same byte array and no unprocessed input data is overwritten * when the result is copied into the output buffer. * * @param input the input buffer * @param inputOffset the offset in input where the input * starts * @param inputLen the input length * @param output the buffer for the result * @param outputOffset the offset in output where the result * is stored * * @return the number of bytes stored in output * * @exception IllegalStateException if this cipher is in a wrong state * (e.g., has not been initialized) * @exception ShortBufferException if the given output buffer is too small * to hold the result */ public final int update(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException { checkCipherState(); // Input sanity check if (input == null || inputOffset < 0 || inputLen > (input.length - inputOffset) || inputLen < 0 || outputOffset < 0) { throw new IllegalArgumentException("Bad arguments"); } updateProviderIfNeeded(); if (inputLen == 0) { return 0; } return spi.engineUpdate(input, inputOffset, inputLen, output, outputOffset); } /** * Continues a multiple-part encryption or decryption operation * (depending on how this cipher was initialized), processing another data * part. * *

All input.remaining() bytes starting at * input.position() are processed. The result is stored * in the output buffer. * Upon return, the input buffer's position will be equal * to its limit; its limit will not have changed. The output buffer's * position will have advanced by n, where n is the value returned * by this method; the output buffer's limit will not have changed. * *

If output.remaining() bytes are insufficient to * hold the result, a ShortBufferException is thrown. * In this case, repeat this call with a larger output buffer. Use * {@link #getOutputSize(int) getOutputSize} to determine how big * the output buffer should be. * *

Note: this method should be copy-safe, which means the * input and output buffers can reference * the same block of memory and no unprocessed input data is overwritten * when the result is copied into the output buffer. * * @param input the input ByteBuffer * @param output the output ByteByffer * * @return the number of bytes stored in output * * @exception IllegalStateException if this cipher is in a wrong state * (e.g., has not been initialized) * @exception IllegalArgumentException if input and output are the * same object * @exception ReadOnlyBufferException if the output buffer is read-only * @exception ShortBufferException if there is insufficient space in the * output buffer * @since 1.5 */ public final int update(ByteBuffer input, ByteBuffer output) throws ShortBufferException { checkCipherState(); if ((input == null) || (output == null)) { throw new IllegalArgumentException("Buffers must not be null"); } if (input == output) { throw new IllegalArgumentException("Input and output buffers must " + "not be the same object, consider using buffer.duplicate()"); } if (output.isReadOnly()) { throw new ReadOnlyBufferException(); } updateProviderIfNeeded(); return spi.engineUpdate(input, output); } /** * Finishes a multiple-part encryption or decryption operation, depending * on how this cipher was initialized. * *

Input data that may have been buffered during a previous * update operation is processed, with padding (if requested) * being applied. * If an AEAD mode such as GCM/CCM is being used, the authentication * tag is appended in the case of encryption, or verified in the * case of decryption. * The result is stored in a new buffer. * *

Upon finishing, this method resets this cipher object to the state * it was in when previously initialized via a call to init. * That is, the object is reset and available to encrypt or decrypt * (depending on the operation mode that was specified in the call to * init) more data. * *

Note: if any exception is thrown, this cipher object may need to * be reset before it can be used again. * * @return the new buffer with the result * * @exception IllegalStateException if this cipher is in a wrong state * (e.g., has not been initialized) * @exception IllegalBlockSizeException if this cipher is a block cipher, * no padding has been requested (only in encryption mode), and the total * input length of the data processed by this cipher is not a multiple of * block size; or if this encryption algorithm is unable to * process the input data provided. * @exception BadPaddingException if this cipher is in decryption mode, * and (un)padding has been requested, but the decrypted data is not * bounded by the appropriate padding bytes * @exception AEADBadTagException if this cipher is decrypting in an * AEAD mode (such as GCM/CCM), and the received authentication tag * does not match the calculated value */ public final byte[] doFinal() throws IllegalBlockSizeException, BadPaddingException { checkCipherState(); updateProviderIfNeeded(); return spi.engineDoFinal(null, 0, 0); } /** * Finishes a multiple-part encryption or decryption operation, depending * on how this cipher was initialized. * *

Input data that may have been buffered during a previous * update operation is processed, with padding (if requested) * being applied. * If an AEAD mode such as GCM/CCM is being used, the authentication * tag is appended in the case of encryption, or verified in the * case of decryption. * The result is stored in the output buffer, starting at * outputOffset inclusive. * *

If the output buffer is too small to hold the result, * a ShortBufferException is thrown. In this case, repeat this * call with a larger output buffer. Use * {@link #getOutputSize(int) getOutputSize} to determine how big * the output buffer should be. * *

Upon finishing, this method resets this cipher object to the state * it was in when previously initialized via a call to init. * That is, the object is reset and available to encrypt or decrypt * (depending on the operation mode that was specified in the call to * init) more data. * *

Note: if any exception is thrown, this cipher object may need to * be reset before it can be used again. * * @param output the buffer for the result * @param outputOffset the offset in output where the result * is stored * * @return the number of bytes stored in output * * @exception IllegalStateException if this cipher is in a wrong state * (e.g., has not been initialized) * @exception IllegalBlockSizeException if this cipher is a block cipher, * no padding has been requested (only in encryption mode), and the total * input length of the data processed by this cipher is not a multiple of * block size; or if this encryption algorithm is unable to * process the input data provided. * @exception ShortBufferException if the given output buffer is too small * to hold the result * @exception BadPaddingException if this cipher is in decryption mode, * and (un)padding has been requested, but the decrypted data is not * bounded by the appropriate padding bytes * @exception AEADBadTagException if this cipher is decrypting in an * AEAD mode (such as GCM/CCM), and the received authentication tag * does not match the calculated value */ public final int doFinal(byte[] output, int outputOffset) throws IllegalBlockSizeException, ShortBufferException, BadPaddingException { checkCipherState(); // Input sanity check if ((output == null) || (outputOffset < 0)) { throw new IllegalArgumentException("Bad arguments"); } updateProviderIfNeeded(); return spi.engineDoFinal(null, 0, 0, output, outputOffset); } /** * Encrypts or decrypts data in a single-part operation, or finishes a * multiple-part operation. The data is encrypted or decrypted, * depending on how this cipher was initialized. * *

The bytes in the input buffer, and any input bytes that * may have been buffered during a previous update operation, * are processed, with padding (if requested) being applied. * If an AEAD mode such as GCM/CCM is being used, the authentication * tag is appended in the case of encryption, or verified in the * case of decryption. * The result is stored in a new buffer. * *

Upon finishing, this method resets this cipher object to the state * it was in when previously initialized via a call to init. * That is, the object is reset and available to encrypt or decrypt * (depending on the operation mode that was specified in the call to * init) more data. * *

Note: if any exception is thrown, this cipher object may need to * be reset before it can be used again. * * @param input the input buffer * * @return the new buffer with the result * * @exception IllegalStateException if this cipher is in a wrong state * (e.g., has not been initialized) * @exception IllegalBlockSizeException if this cipher is a block cipher, * no padding has been requested (only in encryption mode), and the total * input length of the data processed by this cipher is not a multiple of * block size; or if this encryption algorithm is unable to * process the input data provided. * @exception BadPaddingException if this cipher is in decryption mode, * and (un)padding has been requested, but the decrypted data is not * bounded by the appropriate padding bytes * @exception AEADBadTagException if this cipher is decrypting in an * AEAD mode (such as GCM/CCM), and the received authentication tag * does not match the calculated value */ public final byte[] doFinal(byte[] input) throws IllegalBlockSizeException, BadPaddingException { checkCipherState(); // Input sanity check if (input == null) { throw new IllegalArgumentException("Null input buffer"); } updateProviderIfNeeded(); return spi.engineDoFinal(input, 0, input.length); } /** * Encrypts or decrypts data in a single-part operation, or finishes a * multiple-part operation. The data is encrypted or decrypted, * depending on how this cipher was initialized. * *

The first inputLen bytes in the input * buffer, starting at inputOffset inclusive, and any input * bytes that may have been buffered during a previous update * operation, are processed, with padding (if requested) being applied. * If an AEAD mode such as GCM/CCM is being used, the authentication * tag is appended in the case of encryption, or verified in the * case of decryption. * The result is stored in a new buffer. * *

Upon finishing, this method resets this cipher object to the state * it was in when previously initialized via a call to init. * That is, the object is reset and available to encrypt or decrypt * (depending on the operation mode that was specified in the call to * init) more data. * *

Note: if any exception is thrown, this cipher object may need to * be reset before it can be used again. * * @param input the input buffer * @param inputOffset the offset in input where the input * starts * @param inputLen the input length * * @return the new buffer with the result * * @exception IllegalStateException if this cipher is in a wrong state * (e.g., has not been initialized) * @exception IllegalBlockSizeException if this cipher is a block cipher, * no padding has been requested (only in encryption mode), and the total * input length of the data processed by this cipher is not a multiple of * block size; or if this encryption algorithm is unable to * process the input data provided. * @exception BadPaddingException if this cipher is in decryption mode, * and (un)padding has been requested, but the decrypted data is not * bounded by the appropriate padding bytes * @exception AEADBadTagException if this cipher is decrypting in an * AEAD mode (such as GCM/CCM), and the received authentication tag * does not match the calculated value */ public final byte[] doFinal(byte[] input, int inputOffset, int inputLen) throws IllegalBlockSizeException, BadPaddingException { checkCipherState(); // Input sanity check if (input == null || inputOffset < 0 || inputLen > (input.length - inputOffset) || inputLen < 0) { throw new IllegalArgumentException("Bad arguments"); } updateProviderIfNeeded(); return spi.engineDoFinal(input, inputOffset, inputLen); } /** * Encrypts or decrypts data in a single-part operation, or finishes a * multiple-part operation. The data is encrypted or decrypted, * depending on how this cipher was initialized. * *

The first inputLen bytes in the input * buffer, starting at inputOffset inclusive, and any input * bytes that may have been buffered during a previous update * operation, are processed, with padding (if requested) being applied. * If an AEAD mode such as GCM/CCM is being used, the authentication * tag is appended in the case of encryption, or verified in the * case of decryption. * The result is stored in the output buffer. * *

If the output buffer is too small to hold the result, * a ShortBufferException is thrown. In this case, repeat this * call with a larger output buffer. Use * {@link #getOutputSize(int) getOutputSize} to determine how big * the output buffer should be. * *

Upon finishing, this method resets this cipher object to the state * it was in when previously initialized via a call to init. * That is, the object is reset and available to encrypt or decrypt * (depending on the operation mode that was specified in the call to * init) more data. * *

Note: if any exception is thrown, this cipher object may need to * be reset before it can be used again. * *

Note: this method should be copy-safe, which means the * input and output buffers can reference * the same byte array and no unprocessed input data is overwritten * when the result is copied into the output buffer. * * @param input the input buffer * @param inputOffset the offset in input where the input * starts * @param inputLen the input length * @param output the buffer for the result * * @return the number of bytes stored in output * * @exception IllegalStateException if this cipher is in a wrong state * (e.g., has not been initialized) * @exception IllegalBlockSizeException if this cipher is a block cipher, * no padding has been requested (only in encryption mode), and the total * input length of the data processed by this cipher is not a multiple of * block size; or if this encryption algorithm is unable to * process the input data provided. * @exception ShortBufferException if the given output buffer is too small * to hold the result * @exception BadPaddingException if this cipher is in decryption mode, * and (un)padding has been requested, but the decrypted data is not * bounded by the appropriate padding bytes * @exception AEADBadTagException if this cipher is decrypting in an * AEAD mode (such as GCM/CCM), and the received authentication tag * does not match the calculated value */ public final int doFinal(byte[] input, int inputOffset, int inputLen, byte[] output) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { checkCipherState(); // Input sanity check if (input == null || inputOffset < 0 || inputLen > (input.length - inputOffset) || inputLen < 0) { throw new IllegalArgumentException("Bad arguments"); } updateProviderIfNeeded(); return spi.engineDoFinal(input, inputOffset, inputLen, output, 0); } /** * Encrypts or decrypts data in a single-part operation, or finishes a * multiple-part operation. The data is encrypted or decrypted, * depending on how this cipher was initialized. * *

The first inputLen bytes in the input * buffer, starting at inputOffset inclusive, and any input * bytes that may have been buffered during a previous * update operation, are processed, with padding * (if requested) being applied. * If an AEAD mode such as GCM/CCM is being used, the authentication * tag is appended in the case of encryption, or verified in the * case of decryption. * The result is stored in the output buffer, starting at * outputOffset inclusive. * *

If the output buffer is too small to hold the result, * a ShortBufferException is thrown. In this case, repeat this * call with a larger output buffer. Use * {@link #getOutputSize(int) getOutputSize} to determine how big * the output buffer should be. * *

Upon finishing, this method resets this cipher object to the state * it was in when previously initialized via a call to init. * That is, the object is reset and available to encrypt or decrypt * (depending on the operation mode that was specified in the call to * init) more data. * *

Note: if any exception is thrown, this cipher object may need to * be reset before it can be used again. * *

Note: this method should be copy-safe, which means the * input and output buffers can reference * the same byte array and no unprocessed input data is overwritten * when the result is copied into the output buffer. * * @param input the input buffer * @param inputOffset the offset in input where the input * starts * @param inputLen the input length * @param output the buffer for the result * @param outputOffset the offset in output where the result * is stored * * @return the number of bytes stored in output * * @exception IllegalStateException if this cipher is in a wrong state * (e.g., has not been initialized) * @exception IllegalBlockSizeException if this cipher is a block cipher, * no padding has been requested (only in encryption mode), and the total * input length of the data processed by this cipher is not a multiple of * block size; or if this encryption algorithm is unable to * process the input data provided. * @exception ShortBufferException if the given output buffer is too small * to hold the result * @exception BadPaddingException if this cipher is in decryption mode, * and (un)padding has been requested, but the decrypted data is not * bounded by the appropriate padding bytes * @exception AEADBadTagException if this cipher is decrypting in an * AEAD mode (such as GCM/CCM), and the received authentication tag * does not match the calculated value */ public final int doFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { checkCipherState(); // Input sanity check if (input == null || inputOffset < 0 || inputLen > (input.length - inputOffset) || inputLen < 0 || outputOffset < 0) { throw new IllegalArgumentException("Bad arguments"); } updateProviderIfNeeded(); return spi.engineDoFinal(input, inputOffset, inputLen, output, outputOffset); } /** * Encrypts or decrypts data in a single-part operation, or finishes a * multiple-part operation. The data is encrypted or decrypted, * depending on how this cipher was initialized. * *

All input.remaining() bytes starting at * input.position() are processed. * If an AEAD mode such as GCM/CCM is being used, the authentication * tag is appended in the case of encryption, or verified in the * case of decryption. * The result is stored in the output buffer. * Upon return, the input buffer's position will be equal * to its limit; its limit will not have changed. The output buffer's * position will have advanced by n, where n is the value returned * by this method; the output buffer's limit will not have changed. * *

If output.remaining() bytes are insufficient to * hold the result, a ShortBufferException is thrown. * In this case, repeat this call with a larger output buffer. Use * {@link #getOutputSize(int) getOutputSize} to determine how big * the output buffer should be. * *

Upon finishing, this method resets this cipher object to the state * it was in when previously initialized via a call to init. * That is, the object is reset and available to encrypt or decrypt * (depending on the operation mode that was specified in the call to * init) more data. * *

Note: if any exception is thrown, this cipher object may need to * be reset before it can be used again. * *

Note: this method should be copy-safe, which means the * input and output buffers can reference * the same byte array and no unprocessed input data is overwritten * when the result is copied into the output buffer. * * @param input the input ByteBuffer * @param output the output ByteBuffer * * @return the number of bytes stored in output * * @exception IllegalStateException if this cipher is in a wrong state * (e.g., has not been initialized) * @exception IllegalArgumentException if input and output are the * same object * @exception ReadOnlyBufferException if the output buffer is read-only * @exception IllegalBlockSizeException if this cipher is a block cipher, * no padding has been requested (only in encryption mode), and the total * input length of the data processed by this cipher is not a multiple of * block size; or if this encryption algorithm is unable to * process the input data provided. * @exception ShortBufferException if there is insufficient space in the * output buffer * @exception BadPaddingException if this cipher is in decryption mode, * and (un)padding has been requested, but the decrypted data is not * bounded by the appropriate padding bytes * @exception AEADBadTagException if this cipher is decrypting in an * AEAD mode (such as GCM/CCM), and the received authentication tag * does not match the calculated value * * @since 1.5 */ public final int doFinal(ByteBuffer input, ByteBuffer output) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { checkCipherState(); if ((input == null) || (output == null)) { throw new IllegalArgumentException("Buffers must not be null"); } if (input == output) { throw new IllegalArgumentException("Input and output buffers must " + "not be the same object, consider using buffer.duplicate()"); } if (output.isReadOnly()) { throw new ReadOnlyBufferException(); } updateProviderIfNeeded(); return spi.engineDoFinal(input, output); } /** * Wrap a key. * * @param key the key to be wrapped. * * @return the wrapped key. * * @exception IllegalStateException if this cipher is in a wrong * state (e.g., has not been initialized). * * @exception IllegalBlockSizeException if this cipher is a block * cipher, no padding has been requested, and the length of the * encoding of the key to be wrapped is not a * multiple of the block size. * * @exception InvalidKeyException if it is impossible or unsafe to * wrap the key with this cipher (e.g., a hardware protected key is * being passed to a software-only cipher). * * @throws UnsupportedOperationException if the corresponding method in the * {@code CipherSpi} is not supported. */ public final byte[] wrap(Key key) throws IllegalBlockSizeException, InvalidKeyException { if (!(this instanceof NullCipher)) { if (!initialized) { throw new IllegalStateException("Cipher not initialized"); } if (opmode != Cipher.WRAP_MODE) { throw new IllegalStateException("Cipher not initialized " + "for wrapping keys"); } } updateProviderIfNeeded(); return spi.engineWrap(key); } /** * Unwrap a previously wrapped key. * * @param wrappedKey the key to be unwrapped. * * @param wrappedKeyAlgorithm the algorithm associated with the wrapped * key. * * @param wrappedKeyType the type of the wrapped key. This must be one of * SECRET_KEY, PRIVATE_KEY, or * PUBLIC_KEY. * * @return the unwrapped key. * * @exception IllegalStateException if this cipher is in a wrong state * (e.g., has not been initialized). * * @exception NoSuchAlgorithmException if no installed providers * can create keys of type wrappedKeyType for the * wrappedKeyAlgorithm. * * @exception InvalidKeyException if wrappedKey does not * represent a wrapped key of type wrappedKeyType for * the wrappedKeyAlgorithm. * * @throws UnsupportedOperationException if the corresponding method in the * {@code CipherSpi} is not supported. */ public final Key unwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException { if (!(this instanceof NullCipher)) { if (!initialized) { throw new IllegalStateException("Cipher not initialized"); } if (opmode != Cipher.UNWRAP_MODE) { throw new IllegalStateException("Cipher not initialized " + "for unwrapping keys"); } } if ((wrappedKeyType != SECRET_KEY) && (wrappedKeyType != PRIVATE_KEY) && (wrappedKeyType != PUBLIC_KEY)) { throw new InvalidParameterException("Invalid key type"); } updateProviderIfNeeded(); return spi.engineUnwrap(wrappedKey, wrappedKeyAlgorithm, wrappedKeyType); } private AlgorithmParameterSpec getAlgorithmParameterSpec( AlgorithmParameters params) throws InvalidParameterSpecException { if (params == null) { return null; } String alg = params.getAlgorithm().toUpperCase(Locale.ENGLISH); if (alg.equalsIgnoreCase("RC2")) { return params.getParameterSpec(RC2ParameterSpec.class); } if (alg.equalsIgnoreCase("RC5")) { return params.getParameterSpec(RC5ParameterSpec.class); } if (alg.startsWith("PBE")) { return params.getParameterSpec(PBEParameterSpec.class); } if (alg.startsWith("DES")) { return params.getParameterSpec(IvParameterSpec.class); } return null; } /** * Returns the maximum key length for the specified transformation * according to the installed JCE jurisdiction policy files. If * JCE unlimited strength jurisdiction policy files are installed, * Integer.MAX_VALUE will be returned. * For more information on default key size in JCE jurisdiction * policy files, please see Appendix E in the * * Java Cryptography Architecture Reference Guide. * * @param transformation the cipher transformation. * @return the maximum key length in bits or Integer.MAX_VALUE. * @exception NullPointerException if transformation is null. * @exception NoSuchAlgorithmException if transformation * is not a valid transformation, i.e. in the form of "algorithm" or * "algorithm/mode/padding". * @since 1.5 */ public static final int getMaxAllowedKeyLength(String transformation) throws NoSuchAlgorithmException { // Android-changed: Remove references to CryptoPermission. // Throw early if transformation == null or isn't valid. // // CryptoPermission cp = getConfiguredPermission(transformation); // return cp.getMaxAllowedKeyLength(); if (transformation == null) { throw new NullPointerException("transformation == null"); } // Throws NoSuchAlgorithmException if necessary. tokenizeTransformation(transformation); return Integer.MAX_VALUE; } /** * Returns an AlgorithmParameterSpec object which contains * the maximum cipher parameter value according to the * jurisdiction policy file. If JCE unlimited strength jurisdiction * policy files are installed or there is no maximum limit on the * parameters for the specified transformation in the policy file, * null will be returned. * * @param transformation the cipher transformation. * @return an AlgorithmParameterSpec which holds the maximum * value or null. * @exception NullPointerException if transformation * is null. * @exception NoSuchAlgorithmException if transformation * is not a valid transformation, i.e. in the form of "algorithm" or * "algorithm/mode/padding". * @since 1.5 */ public static final AlgorithmParameterSpec getMaxAllowedParameterSpec( String transformation) throws NoSuchAlgorithmException { // Android-changed: Remove references to CryptoPermission. // Throw early if transformation == null or isn't valid. // // CryptoPermission cp = getConfiguredPermission(transformation); // return cp.getAlgorithmParameterSpec(); if (transformation == null) { throw new NullPointerException("transformation == null"); } // Throws NoSuchAlgorithmException if necessary. tokenizeTransformation(transformation); return null; } /** * Continues a multi-part update of the Additional Authentication * Data (AAD). *

* Calls to this method provide AAD to the cipher when operating in * modes such as AEAD (GCM/CCM). If this cipher is operating in * either GCM or CCM mode, all AAD must be supplied before beginning * operations on the ciphertext (via the {@code update} and {@code * doFinal} methods). * * @param src the buffer containing the Additional Authentication Data * * @throws IllegalArgumentException if the {@code src} * byte array is null * @throws IllegalStateException if this cipher is in a wrong state * (e.g., has not been initialized), does not accept AAD, or if * operating in either GCM or CCM mode and one of the {@code update} * methods has already been called for the active * encryption/decryption operation * @throws UnsupportedOperationException if the corresponding method * in the {@code CipherSpi} has not been overridden by an * implementation * * @since 1.7 */ public final void updateAAD(byte[] src) { if (src == null) { throw new IllegalArgumentException("src buffer is null"); } updateAAD(src, 0, src.length); } /** * Continues a multi-part update of the Additional Authentication * Data (AAD), using a subset of the provided buffer. *

* Calls to this method provide AAD to the cipher when operating in * modes such as AEAD (GCM/CCM). If this cipher is operating in * either GCM or CCM mode, all AAD must be supplied before beginning * operations on the ciphertext (via the {@code update} and {@code * doFinal} methods). * * @param src the buffer containing the AAD * @param offset the offset in {@code src} where the AAD input starts * @param len the number of AAD bytes * * @throws IllegalArgumentException if the {@code src} * byte array is null, or the {@code offset} or {@code length} * is less than 0, or the sum of the {@code offset} and * {@code len} is greater than the length of the * {@code src} byte array * @throws IllegalStateException if this cipher is in a wrong state * (e.g., has not been initialized), does not accept AAD, or if * operating in either GCM or CCM mode and one of the {@code update} * methods has already been called for the active * encryption/decryption operation * @throws UnsupportedOperationException if the corresponding method * in the {@code CipherSpi} has not been overridden by an * implementation * * @since 1.7 */ public final void updateAAD(byte[] src, int offset, int len) { checkCipherState(); // Input sanity check if ((src == null) || (offset < 0) || (len < 0) || ((len + offset) > src.length)) { throw new IllegalArgumentException("Bad arguments"); } updateProviderIfNeeded(); if (len == 0) { return; } spi.engineUpdateAAD(src, offset, len); } /** * Continues a multi-part update of the Additional Authentication * Data (AAD). *

* Calls to this method provide AAD to the cipher when operating in * modes such as AEAD (GCM/CCM). If this cipher is operating in * either GCM or CCM mode, all AAD must be supplied before beginning * operations on the ciphertext (via the {@code update} and {@code * doFinal} methods). *

* All {@code src.remaining()} bytes starting at * {@code src.position()} are processed. * Upon return, the input buffer's position will be equal * to its limit; its limit will not have changed. * * @param src the buffer containing the AAD * * @throws IllegalArgumentException if the {@code src ByteBuffer} * is null * @throws IllegalStateException if this cipher is in a wrong state * (e.g., has not been initialized), does not accept AAD, or if * operating in either GCM or CCM mode and one of the {@code update} * methods has already been called for the active * encryption/decryption operation * @throws UnsupportedOperationException if the corresponding method * in the {@code CipherSpi} has not been overridden by an * implementation * * @since 1.7 */ public final void updateAAD(ByteBuffer src) { checkCipherState(); // Input sanity check if (src == null) { throw new IllegalArgumentException("src ByteBuffer is null"); } updateProviderIfNeeded(); if (src.remaining() == 0) { return; } spi.engineUpdateAAD(src); } // BEGIN Android-added: Bulk of the new provider implementation. // See note at top of class. /** * Returns the {@code CipherSpi} backing this {@code Cipher} or {@code null} if no * {@code CipherSpi} is backing this {@code Cipher}. * * @hide */ public CipherSpi getCurrentSpi() { return spi; } /** The attribute used for supported paddings. */ private static final String ATTRIBUTE_PADDINGS = "SupportedPaddings"; /** The attribute used for supported modes. */ private static final String ATTRIBUTE_MODES = "SupportedModes"; /** * If the attribute listed exists, check that it matches the regular * expression. */ static boolean matchAttribute(Provider.Service service, String attr, String value) { if (value == null) { return true; } final String pattern = service.getAttribute(attr); if (pattern == null) { return true; } final String valueUc = value.toUpperCase(Locale.US); return valueUc.matches(pattern.toUpperCase(Locale.US)); } /** Items that need to be set on the Cipher instance. */ enum NeedToSet { NONE, MODE, PADDING, BOTH, } /** * Expresses the various types of transforms that may be used during * initialization. */ static class Transform { private final String name; private final NeedToSet needToSet; public Transform(String name, NeedToSet needToSet) { this.name = name; this.needToSet = needToSet; } } /** * Keeps track of the possible arguments to {@code Cipher#init(...)}. */ static class InitParams { final InitType initType; final int opmode; final Key key; final SecureRandom random; final AlgorithmParameterSpec spec; final AlgorithmParameters params; InitParams(InitType initType, int opmode, Key key, SecureRandom random, AlgorithmParameterSpec spec, AlgorithmParameters params) { this.initType = initType; this.opmode = opmode; this.key = key; this.random = random; this.spec = spec; this.params = params; } } /** * Used to keep track of which underlying {@code CipherSpi#engineInit(...)} * variant to call when testing suitability. */ static enum InitType { KEY, ALGORITHM_PARAMS, ALGORITHM_PARAM_SPEC, } class SpiAndProviderUpdater { /** * Lock held while the SPI is initializing. */ private final Object initSpiLock = new Object(); /** * The provider specified when instance created. */ private final Provider specifiedProvider; /** * The SPI implementation. */ private final CipherSpi specifiedSpi; SpiAndProviderUpdater(Provider specifiedProvider, CipherSpi specifiedSpi) { this.specifiedProvider = specifiedProvider; this.specifiedSpi = specifiedSpi; } void setCipherSpiImplAndProvider(CipherSpi cipherSpi, Provider provider) { Cipher.this.spi = cipherSpi; Cipher.this.provider = provider; } /** * Makes sure a CipherSpi that matches this type is selected. If * {@code key != null} then it assumes that a suitable provider exists for * this instance (used by {@link Cipher#init}. If the {@code initParams} is passed * in, then the {@code CipherSpi} returned will be initialized. * * @throws InvalidKeyException if the specified key cannot be used to * initialize this cipher. */ CipherSpiAndProvider updateAndGetSpiAndProvider( InitParams initParams, CipherSpi spiImpl, Provider provider) throws InvalidKeyException, InvalidAlgorithmParameterException { if (specifiedSpi != null) { return new CipherSpiAndProvider(specifiedSpi, provider); } synchronized (initSpiLock) { // This is not only a matter of performance. Many methods like update, doFinal, etc. // call {@code #getSpi()} (ie, {@code #getSpi(null /* params */)}) and without this // shortcut they would override an spi that was chosen using the key. if (spiImpl != null && initParams == null) { return new CipherSpiAndProvider(spiImpl, provider); } final CipherSpiAndProvider sap = tryCombinations( initParams, specifiedProvider, tokenizedTransformation); if (sap == null) { throw new ProviderException("No provider found for " + Arrays.toString(tokenizedTransformation)); } setCipherSpiImplAndProvider(sap.cipherSpi, sap.provider); return new CipherSpiAndProvider(sap.cipherSpi, sap.provider); } } /** * Convenience call when the Key is not available. */ CipherSpiAndProvider updateAndGetSpiAndProvider(CipherSpi spiImpl, Provider provider) { try { return updateAndGetSpiAndProvider(null, spiImpl, provider); } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { throw new ProviderException("Exception thrown when params == null", e); } } CipherSpi getCurrentSpi(CipherSpi spiImpl) { if (specifiedSpi != null) { return specifiedSpi; } synchronized (initSpiLock) { return spiImpl; } } } /** * Tries to find the correct {@code Cipher} transform to use. Returns a * {@link org.apache.harmony.security.fortress.Engine.SpiAndProvider}, throws the first exception that was * encountered during attempted initialization, or {@code null} if there are * no providers that support the {@code initParams}. *

* {@code tokenizedTransformation} must be in the format returned by * {@link Cipher#checkTransformation(String)}. The combinations of mode strings * tried are as follows: *

* {@code services} is a list of cipher services. Needs to be non-null only if * {@code provider != null} */ static CipherSpiAndProvider tryCombinations(InitParams initParams, Provider provider, String[] tokenizedTransformation) throws InvalidKeyException, InvalidAlgorithmParameterException { // Enumerate all the transforms we need to try ArrayList transforms = new ArrayList(); if (tokenizedTransformation[1] != null && tokenizedTransformation[2] != null) { transforms.add(new Transform(tokenizedTransformation[0] + "/" + tokenizedTransformation[1] + "/" + tokenizedTransformation[2], NeedToSet.NONE)); } if (tokenizedTransformation[1] != null) { transforms.add(new Transform(tokenizedTransformation[0] + "/" + tokenizedTransformation[1], NeedToSet.PADDING)); } if (tokenizedTransformation[2] != null) { transforms.add(new Transform(tokenizedTransformation[0] + "//" + tokenizedTransformation[2], NeedToSet.MODE)); } transforms.add(new Transform(tokenizedTransformation[0], NeedToSet.BOTH)); // Try each of the transforms and keep track of the first exception // encountered. Exception cause = null; if (provider != null) { for (Transform transform : transforms) { Provider.Service service = provider.getService("Cipher", transform.name); if (service == null) { continue; } return tryTransformWithProvider(initParams, tokenizedTransformation, transform.needToSet, service); } } else { for (Provider prov : Security.getProviders()) { for (Transform transform : transforms) { Provider.Service service = prov.getService("Cipher", transform.name); if (service == null) { continue; } if (initParams == null || initParams.key == null || service.supportsParameter(initParams.key)) { try { CipherSpiAndProvider sap = tryTransformWithProvider(initParams, tokenizedTransformation, transform.needToSet, service); if (sap != null) { return sap; } } catch (Exception e) { if (cause == null) { cause = e; } } } } } } if (cause instanceof InvalidKeyException) { throw (InvalidKeyException) cause; } else if (cause instanceof InvalidAlgorithmParameterException) { throw (InvalidAlgorithmParameterException) cause; } else if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } else if (cause != null) { throw new InvalidKeyException("No provider can be initialized with given key", cause); } else if (initParams == null || initParams.key == null) { return null; } else { // Since the key is not null, a suitable provider exists, // and it is an InvalidKeyException. throw new InvalidKeyException( "No provider offers " + Arrays.toString(tokenizedTransformation) + " for " + initParams.key.getAlgorithm() + " key of class " + initParams.key.getClass().getName() + " and export format " + initParams.key.getFormat()); } } static class CipherSpiAndProvider { CipherSpi cipherSpi; Provider provider; CipherSpiAndProvider(CipherSpi cipherSpi, Provider provider) { this.cipherSpi = cipherSpi; this.provider = provider; } } /** * Tries to initialize the {@code Cipher} from a given {@code service}. If * initialization is successful, the initialized {@code spi} is returned. If * the {@code service} cannot be initialized with the specified * {@code initParams}, then it's expected to throw * {@code InvalidKeyException} or {@code InvalidAlgorithmParameterException} * as a hint to the caller that it should continue searching for a * {@code Service} that will work. */ static CipherSpiAndProvider tryTransformWithProvider(InitParams initParams, String[] tokenizedTransformation, NeedToSet type, Provider.Service service) throws InvalidKeyException, InvalidAlgorithmParameterException { try { /* * Check to see if the Cipher even supports the attributes before * trying to instantiate it. */ if (!matchAttribute(service, ATTRIBUTE_MODES, tokenizedTransformation[1]) || !matchAttribute(service, ATTRIBUTE_PADDINGS, tokenizedTransformation[2])) { return null; } CipherSpiAndProvider sap = new CipherSpiAndProvider( (CipherSpi) service.newInstance(null), service.getProvider()); if (sap.cipherSpi == null || sap.provider == null) { return null; } CipherSpi spi = sap.cipherSpi; if (((type == NeedToSet.MODE) || (type == NeedToSet.BOTH)) && (tokenizedTransformation[1] != null)) { spi.engineSetMode(tokenizedTransformation[1]); } if (((type == NeedToSet.PADDING) || (type == NeedToSet.BOTH)) && (tokenizedTransformation[2] != null)) { spi.engineSetPadding(tokenizedTransformation[2]); } if (initParams != null) { switch (initParams.initType) { case ALGORITHM_PARAMS: spi.engineInit(initParams.opmode, initParams.key, initParams.params, initParams.random); break; case ALGORITHM_PARAM_SPEC: spi.engineInit(initParams.opmode, initParams.key, initParams.spec, initParams.random); break; case KEY: spi.engineInit(initParams.opmode, initParams.key, initParams.random); break; default: throw new AssertionError("This should never be reached"); } } return new CipherSpiAndProvider(spi, sap.provider); } catch (NoSuchAlgorithmException ignored) { } catch (NoSuchPaddingException ignored) { } return null; } // END Android-added: Bulk of the new provider implementation. }