summaryrefslogtreecommitdiffstats
path: root/keystore
diff options
context:
space:
mode:
Diffstat (limited to 'keystore')
-rw-r--r--keystore/java/android/security/EcIesParameterSpec.java272
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java41
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java4
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java5
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java60
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java20
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java6
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java24
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreSpi.java24
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreUnauthenticatedAESCipherSpi.java5
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java23
-rw-r--r--keystore/java/android/security/keystore/KeyProperties.java6
-rw-r--r--keystore/java/android/security/keystore/KeyProtection.java11
-rw-r--r--keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java14
-rw-r--r--keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java7
15 files changed, 171 insertions, 351 deletions
diff --git a/keystore/java/android/security/EcIesParameterSpec.java b/keystore/java/android/security/EcIesParameterSpec.java
deleted file mode 100644
index 1cd8784..0000000
--- a/keystore/java/android/security/EcIesParameterSpec.java
+++ /dev/null
@@ -1,272 +0,0 @@
-package android.security;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.security.spec.AlgorithmParameterSpec;
-
-import javax.crypto.Cipher;
-import javax.crypto.Mac;
-
-/**
- * {@link AlgorithmParameterSpec} for ECIES (Integrated Encryption Scheme using Elliptic Curve
- * cryptography) based on {@code ISO/IEC 18033-2}.
- *
- * <p>ECIES is a hybrid authenticated encryption scheme. Encryption is performed using an Elliptic
- * Curve (EC) public key. The resulting ciphertext can be decrypted only using the corresponding EC
- * private key. The scheme is called hybrid because the EC key is only used to securely encapsulate
- * symmetric key material. Encryption of plaintext and authentication of the corresponding
- * ciphertext is performed using symmetric cryptography.
- *
- * <p>Encryption using ECIES consists of two stages:
- * <ol>
- * <li>Key Encapsulation Mechanism (KEM) randomly generates symmetric key material and securely
- * encapsulates it in the output so that it can be extracted by the KEM when decrypting.
- * Encapsulated key material is represented in the output as an EC point.</li>
- * <li>The above symmetric key material is used by Data Encapsulation Mechanism (DEM) to encrypt the
- * provided plaintext and authenticate the ciphertext. The resulting authenticated ciphertext is
- * then output. When decrypting, the DEM first authenticates the ciphertext and, only if it
- * authenticates, decrypts the ciphertext and outputs the plaintext.</li>
- * </ol>
- *
- * <p>Details of KEM:
- * <ul>
- * <li>Only curves with cofactor of {@code 1} are supported.</li>
- * <li>{@code CheckMode}, {@code OldCofactorMode}, {@code CofactorMode}, and {@code SingleHashMode}
- * are {@code 0}.
- * <li>Point format is specified by {@link #getKemPointFormat()}.</li>
- * <li>KDF algorithm is specified by {@link #getKemKdfAlgorithm()}.</li>
- * </ul>
- *
- * <p>Details of DEM:
- * <ul>
- * <li>Only DEM1-like mechanism is supported, with its symmetric cipher (SC) specified by
- * {@link #getDemCipherTransformation()} (e.g., {@code AES/CBC/NoPadding} for standard DEM1) and
- * MAC algorithm specified by {@link #getDemMacAlgorithm()} (e.g., {@code HmacSHA1} for standard
- * DEM1).</li>
- * </ul>
- */
-public class EcIesParameterSpec implements AlgorithmParameterSpec {
-
- /**
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({
- POINT_FORMAT_UNSPECIFIED,
- POINT_FORMAT_UNCOMPRESSED,
- POINT_FORMAT_COMPRESSED,
- })
- public @interface PointFormatEnum {}
-
- /** Unspecified EC point format. */
- public static final int POINT_FORMAT_UNSPECIFIED = -1;
-
- /**
- * Uncompressed EC point format: both coordinates are stored separately.
- *
- * <p>The wire format is byte {@code 0x04} followed by binary representation of the {@code x}
- * coordinate followed by binary representation of the {@code y} coordinate. See
- * {@code ISO 18033-2} section {@code 5.4.3}.
- */
- public static final int POINT_FORMAT_UNCOMPRESSED = 0;
-
- /**
- * Compressed EC point format: only one coordinate is stored.
- *
- * <p>The wire format is byte {@code 0x02} or {@code 0x03} (depending on the value of the stored
- * coordinate) followed by the binary representation of the {@code x} coordinate. See
- * {@code ISO 18033-2} section {@code 5.4.3}.
- */
- public static final int POINT_FORMAT_COMPRESSED = 1;
-
- /**
- * Default parameter spec: compressed point format, {@code HKDFwithSHA256}, DEM uses 128-bit AES
- * GCM.
- */
- public static final EcIesParameterSpec DEFAULT = new EcIesParameterSpec(
- POINT_FORMAT_COMPRESSED,
- "HKDFwithSHA256",
- "AES/GCM/NoPadding",
- 128,
- null,
- 0);
-
- private final @PointFormatEnum int mKemPointFormat;
- private final String mKemKdfAlgorithm;
- private final String mDemCipherTransformation;
- private final int mDemCipherKeySize;
- private final String mDemMacAlgorithm;
- private final int mDemMacKeySize;
-
- private EcIesParameterSpec(
- @PointFormatEnum int kemPointFormat,
- String kemKdfAlgorithm,
- String demCipherTransformation,
- int demCipherKeySize,
- String demMacAlgorithm,
- int demMacKeySize) {
- mKemPointFormat = kemPointFormat;
- mKemKdfAlgorithm = kemKdfAlgorithm;
- mDemCipherTransformation = demCipherTransformation;
- mDemCipherKeySize = demCipherKeySize;
- mDemMacAlgorithm = demMacAlgorithm;
- mDemMacKeySize = demMacKeySize;
- }
-
- /**
- * Returns KEM EC point wire format or {@link #POINT_FORMAT_UNSPECIFIED} if not specified.
- */
- public @PointFormatEnum int getKemPointFormat() {
- return mKemPointFormat;
- }
-
- /**
- * Returns KEM KDF algorithm (e.g., {@code HKDFwithSHA256} or {@code KDF1withSHA1}) or
- * {@code null} if not specified.
- */
- @Nullable
- public String getKemKdfAlgorithm() {
- return mKemKdfAlgorithm;
- }
-
- /**
- * Returns DEM {@link Cipher} transformation (e.g., {@code AES/GCM/NoPadding} or
- * {@code AES/CBC/PKCS7Padding}) or {@code null} if not specified.
- *
- * @see Cipher#getInstance(String)
- * @see #getDemCipherKeySize()
- */
- @Nullable
- public String getDemCipherTransformation() {
- return mDemCipherTransformation;
- }
-
- /**
- * Returns DEM {@link Cipher} key size in bits.
- *
- * @see #getDemCipherTransformation()
- */
- public int getDemCipherKeySize() {
- return mDemCipherKeySize;
- }
-
- /**
- * Returns DEM {@link Mac} algorithm (e.g., {@code HmacSHA256} or {@code HmacSHA1}) or
- * {@code null} if not specified.
- *
- * @see Mac#getInstance(String)
- * @see #getDemMacKeySize()
- */
- @Nullable
- public String getDemMacAlgorithm() {
- return mDemMacAlgorithm;
- }
-
- /**
- * Returns DEM {@link Mac} key size in bits.
- *
- * @see #getDemCipherTransformation()
- */
- public int getDemMacKeySize() {
- return mDemMacKeySize;
- }
-
- /**
- * Builder of {@link EcIesParameterSpec}.
- */
- public static class Builder {
- private @PointFormatEnum int mKemPointFormat = POINT_FORMAT_UNSPECIFIED;
- private String mKemKdfAlgorithm;
- private String mDemCipherTransformation;
- private int mDemCipherKeySize = 128;
- private String mDemMacAlgorithm;
- private int mDemMacKeySize = -1;
-
- /**
- * Sets KEM EC point wire format.
- */
- public Builder setKemPointFormat(@PointFormatEnum int pointFormat) {
- mKemPointFormat = pointFormat;
- return this;
- }
-
- /**
- * Sets KEM KDF algorithm. For example, {@code HKDFwithSHA256}, {@code KDF2withSHA256}, or
- * {@code KDF1withSHA1}.
- */
- @NonNull
- public Builder setKemKdfAlgorithm(@Nullable String algorithm) {
- mKemKdfAlgorithm = algorithm;
- return this;
- }
-
- /**
- * Sets DEM {@link Cipher} transformation. For example, {@code AES/GCM/NoPadding},
- * {@code AES/CBC/PKCS7Padding} or {@code AES/CTR/NoPadding}.
- *
- * @see Cipher#getInstance(String)
- */
- @NonNull
- public Builder setDemCipherTransformation(@Nullable String transformation) {
- mDemCipherTransformation = transformation;
- return this;
- }
-
- /**
- * Returns DEM {@link Cipher} key size in bits.
- *
- * <p>The default value is {@code 128} bits.
- *
- * @see #setDemCipherTransformation(String)
- */
- @NonNull
- public Builder setDemCipherKeySize(int sizeBits) {
- mDemCipherKeySize = sizeBits;
- return this;
- }
-
- /**
- * Sets DEM {@link Mac} algorithm. For example, {@code HmacSHA256} or {@code HmacSHA1}.
- *
- * @see Mac#getInstance(String)
- */
- @NonNull
- public Builder setDemMacAlgorithm(@Nullable String algorithm) {
- mDemMacAlgorithm = algorithm;
- return this;
- }
-
- /**
- * Sets DEM {@link Mac} key size in bits.
- *
- * <p>By default, {@code Mac} key size is the same as the {@code Cipher} key size.
- *
- * @see #setDemCipherKeySize(int)
- */
- @NonNull
- public Builder setDemMacKeySize(int sizeBits) {
- mDemMacKeySize = sizeBits;
- return this;
- }
-
- /**
- * Returns a new {@link EcIesParameterSpec} based on the current state of this builder.
- */
- @NonNull
- public EcIesParameterSpec build() {
- int demMacKeySize = (mDemMacKeySize != -1) ? mDemMacKeySize : mDemCipherKeySize;
- return new EcIesParameterSpec(
- mKemPointFormat,
- mKemKdfAlgorithm,
- mDemCipherTransformation,
- mDemCipherKeySize,
- mDemMacAlgorithm,
- demMacKeySize
- );
- }
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
index 19375a2..d2d5850 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
@@ -368,7 +368,10 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
byte[] output;
try {
- output = mMainDataStreamer.doFinal(input, inputOffset, inputLen);
+ byte[] additionalEntropy =
+ KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
+ mRng, getAdditionalEntropyAmountForFinish());
+ output = mMainDataStreamer.doFinal(input, inputOffset, inputLen, additionalEntropy);
} catch (KeyStoreException e) {
switch (e.getErrorCode()) {
case KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH:
@@ -667,21 +670,37 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
/**
* Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's
- * {@code begin} operation.
+ * {@code begin} operation. This amount of entropy is typically what's consumed to generate
+ * random parameters, such as IV.
*
- * <p>For decryption, this should be {@code 0} because decryption should not be consuming any
- * entropy. For encryption, this value should match (or exceed) the amount of Shannon entropy of
- * the ciphertext produced by this cipher assuming the key, the plaintext, and all explicitly
- * provided parameters to {@code Cipher.init} are known. For example, for AES CBC encryption
- * with an explicitly provided IV this should be {@code 0}, whereas for the case where IV is
- * generated by the KeyStore's {@code begin} operation this should be {@code 16}. For RSA with
- * OAEP this should be the size of the OAEP hash output. For RSA with PKCS#1 padding this should
- * be the size of the padding string or could be raised (for simplicity) to the size of the
- * modulus.
+ * <p>For decryption, the return value should be {@code 0} because decryption should not be
+ * consuming any entropy. For encryption, the value combined with
+ * {@link #getAdditionalEntropyAmountForFinish()} should match (or exceed) the amount of Shannon
+ * entropy of the ciphertext produced by this cipher assuming the key, the plaintext, and all
+ * explicitly provided parameters to {@code Cipher.init} are known. For example, for AES CBC
+ * encryption with an explicitly provided IV the return value should be {@code 0}, whereas for
+ * the case where IV is generated by the KeyStore's {@code begin} operation it should be
+ * {@code 16}.
*/
protected abstract int getAdditionalEntropyAmountForBegin();
/**
+ * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's
+ * {@code finish} operation. This amount of entropy is typically what's consumed by encryption
+ * padding scheme.
+ *
+ * <p>For decryption, the return value should be {@code 0} because decryption should not be
+ * consuming any entropy. For encryption, the value combined with
+ * {@link #getAdditionalEntropyAmountForBegin()} should match (or exceed) the amount of Shannon
+ * entropy of the ciphertext produced by this cipher assuming the key, the plaintext, and all
+ * explicitly provided parameters to {@code Cipher.init} are known. For example, for RSA with
+ * OAEP the return value should be the size of the OAEP hash output. For RSA with PKCS#1 padding
+ * the return value should be the size of the padding string or could be raised (for simplicity)
+ * to the size of the modulus.
+ */
+ protected abstract int getAdditionalEntropyAmountForFinish();
+
+ /**
* Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation.
*
* @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java
index 335da07..d19a766 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java
@@ -117,7 +117,7 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature
}
@Override
- protected int getAdditionalEntropyAmountForBegin() {
- return (isSigning()) ? mGroupSizeBytes : 0;
+ protected int getAdditionalEntropyAmountForSign() {
+ return mGroupSizeBytes;
}
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java
index f31c06d..f7c184c 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java
@@ -232,7 +232,10 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC
byte[] result;
try {
- result = mChunkedStreamer.doFinal(null, 0, 0);
+ result = mChunkedStreamer.doFinal(
+ null, 0, 0,
+ null // no additional entropy needed -- HMAC is deterministic
+ );
} catch (KeyStoreException e) {
throw new ProviderException("Keystore operation failed", e);
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 35af34f..2de60fd 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -215,14 +215,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
legacySpec.getKeystoreAlias(),
KeyProperties.PURPOSE_SIGN
| KeyProperties.PURPOSE_VERIFY);
- specBuilder.setDigests(
- KeyProperties.DIGEST_NONE,
- KeyProperties.DIGEST_MD5,
- KeyProperties.DIGEST_SHA1,
- KeyProperties.DIGEST_SHA224,
- KeyProperties.DIGEST_SHA256,
- KeyProperties.DIGEST_SHA384,
- KeyProperties.DIGEST_SHA512);
+ // Authorized to be used with any digest (including no digest).
+ specBuilder.setDigests(KeyProperties.DIGEST_NONE);
break;
case KeymasterDefs.KM_ALGORITHM_RSA:
specBuilder = new KeyGenParameterSpec.Builder(
@@ -231,19 +225,13 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
| KeyProperties.PURPOSE_DECRYPT
| KeyProperties.PURPOSE_SIGN
| KeyProperties.PURPOSE_VERIFY);
- specBuilder.setDigests(
- KeyProperties.DIGEST_NONE,
- KeyProperties.DIGEST_MD5,
- KeyProperties.DIGEST_SHA1,
- KeyProperties.DIGEST_SHA224,
- KeyProperties.DIGEST_SHA256,
- KeyProperties.DIGEST_SHA384,
- KeyProperties.DIGEST_SHA512);
+ // Authorized to be used with any digest (including no digest).
+ specBuilder.setDigests(KeyProperties.DIGEST_NONE);
specBuilder.setSignaturePaddings(
KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
+ // Authorized to be used with any padding (including no padding).
specBuilder.setEncryptionPaddings(
- KeyProperties.ENCRYPTION_PADDING_NONE,
- KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
+ KeyProperties.ENCRYPTION_PADDING_NONE);
// Disable randomized encryption requirement to support encryption
// padding NONE above.
specBuilder.setRandomizedEncryptionRequired(false);
@@ -636,7 +624,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
int keySizeBits,
KeyGenParameterSpec spec) {
// Constraints:
- // 1. Key must be authorized for signing.
+ // 1. Key must be authorized for signing without user authentication.
// 2. Signature digest must be one of key's authorized digests.
// 3. For RSA keys, the digest output size must not exceed modulus size minus space needed
// for RSA PKCS#1 signature padding (about 29 bytes: minimum 10 bytes of padding + 15--19
@@ -648,6 +636,10 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
// Key not authorized for signing
return null;
}
+ if (spec.isUserAuthenticationRequired()) {
+ // Key not authorized for use without user authentication
+ return null;
+ }
if (!spec.isDigestsSpecified()) {
// Key not authorized for any digests -- can't sign
return null;
@@ -703,6 +695,36 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
}
case KeymasterDefs.KM_ALGORITHM_RSA:
{
+ // Check whether this key is authorized for PKCS#1 signature padding.
+ // We use Bouncy Castle to generate self-signed RSA certificates. Bouncy Castle
+ // only supports RSA certificates signed using PKCS#1 padding scheme. The key needs
+ // to be authorized for PKCS#1 padding or padding NONE which means any padding.
+ boolean pkcs1SignaturePaddingSupported = false;
+ for (int keymasterPadding : KeyProperties.SignaturePadding.allToKeymaster(
+ spec.getSignaturePaddings())) {
+ if ((keymasterPadding == KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN)
+ || (keymasterPadding == KeymasterDefs.KM_PAD_NONE)) {
+ pkcs1SignaturePaddingSupported = true;
+ break;
+ }
+ }
+ if (!pkcs1SignaturePaddingSupported) {
+ // Keymaster doesn't distinguish between encryption padding NONE and signature
+ // padding NONE. In the Android Keystore API only encryption padding NONE is
+ // exposed.
+ for (int keymasterPadding : KeyProperties.EncryptionPadding.allToKeymaster(
+ spec.getEncryptionPaddings())) {
+ if (keymasterPadding == KeymasterDefs.KM_PAD_NONE) {
+ pkcs1SignaturePaddingSupported = true;
+ break;
+ }
+ }
+ }
+ if (!pkcs1SignaturePaddingSupported) {
+ // Key not authorized for PKCS#1 signature padding -- can't sign
+ return null;
+ }
+
Set<Integer> availableKeymasterDigests = getAvailableKeymasterSignatureDigests(
spec.getDigests(),
AndroidKeyStoreBCWorkaroundProvider.getSupportedEcdsaSignatureDigests());
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
index d33692a..6abdf19 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
@@ -99,6 +99,11 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
}
@Override
+ protected final int getAdditionalEntropyAmountForFinish() {
+ return 0;
+ }
+
+ @Override
@NonNull
protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
KeyStore keyStore, IBinder operationToken) {
@@ -142,7 +147,8 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
}
@Override
- public byte[] doFinal(byte[] input, int inputOffset, int inputLength)
+ public byte[] doFinal(byte[] input, int inputOffset, int inputLength,
+ byte[] additionalEntropy)
throws KeyStoreException {
if (inputLength > 0) {
mInputBuffer.write(input, inputOffset, inputLength);
@@ -165,7 +171,7 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
"Message size (" + bufferedInput.length + " bytes) must be smaller than"
+ " modulus (" + mModulusSizeBytes + " bytes)");
}
- return mDelegate.doFinal(paddedInput, 0, paddedInput.length);
+ return mDelegate.doFinal(paddedInput, 0, paddedInput.length, additionalEntropy);
}
}
}
@@ -207,6 +213,11 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
@Override
protected final int getAdditionalEntropyAmountForBegin() {
+ return 0;
+ }
+
+ @Override
+ protected final int getAdditionalEntropyAmountForFinish() {
return (isEncrypting()) ? getModulusSizeBytes() : 0;
}
}
@@ -361,6 +372,11 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
@Override
protected final int getAdditionalEntropyAmountForBegin() {
+ return 0;
+ }
+
+ @Override
+ protected final int getAdditionalEntropyAmountForFinish() {
return (isEncrypting()) ? mDigestOutputSizeBytes : 0;
}
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java
index 898336d..954b71a 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java
@@ -36,7 +36,7 @@ abstract class AndroidKeyStoreRSASignatureSpi extends AndroidKeyStoreSignatureSp
}
@Override
- protected final int getAdditionalEntropyAmountForBegin() {
+ protected final int getAdditionalEntropyAmountForSign() {
// No entropy required for this deterministic signature scheme.
return 0;
}
@@ -92,8 +92,8 @@ abstract class AndroidKeyStoreRSASignatureSpi extends AndroidKeyStoreSignatureSp
}
@Override
- protected final int getAdditionalEntropyAmountForBegin() {
- return (isSigning()) ? SALT_LENGTH_BYTES : 0;
+ protected final int getAdditionalEntropyAmountForSign() {
+ return SALT_LENGTH_BYTES;
}
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java
index f072ae7..5cdcc41 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java
@@ -198,15 +198,14 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
KeymasterArguments keymasterInputArgs = new KeymasterArguments();
addAlgorithmSpecificParametersToBegin(keymasterInputArgs);
- byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
- appRandom, getAdditionalEntropyAmountForBegin());
OperationResult opResult = mKeyStore.begin(
mKey.getAlias(),
mSigning ? KeymasterDefs.KM_PURPOSE_SIGN : KeymasterDefs.KM_PURPOSE_VERIFY,
true, // permit aborting this operation if keystore runs out of resources
keymasterInputArgs,
- additionalEntropy);
+ null // no additional entropy for begin -- only finish might need some
+ );
if (opResult == null) {
throw new KeyStoreConnectException();
}
@@ -311,7 +310,11 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
byte[] signature;
try {
ensureKeystoreOperationInitialized();
- signature = mMessageStreamer.doFinal(EmptyArray.BYTE, 0, 0);
+
+ byte[] additionalEntropy =
+ KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
+ appRandom, getAdditionalEntropyAmountForSign());
+ signature = mMessageStreamer.doFinal(EmptyArray.BYTE, 0, 0, additionalEntropy);
} catch (InvalidKeyException | KeyStoreException e) {
throw new SignatureException(e);
}
@@ -388,15 +391,14 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
/**
* Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's
- * {@code begin} operation.
+ * {@code finish} operation when generating a signature.
*
- * <p>For signature verification, this should be {@code 0} because verification should not be
- * consuming any entropy. For signature generation, this value should match (or exceed) the
- * amount of Shannon entropy of the produced signature assuming the key and the message are
- * known. For example, for ECDSA signature this should be the size of {@code R}, whereas for the
- * RSA signature with PKCS#1 padding this should be {@code 0}.
+ * <p>This value should match (or exceed) the amount of Shannon entropy of the produced
+ * signature assuming the key and the message are known. For example, for ECDSA signature this
+ * should be the size of {@code R}, whereas for the RSA signature with PKCS#1 padding this
+ * should be {@code 0}.
*/
- protected abstract int getAdditionalEntropyAmountForBegin();
+ protected abstract int getAdditionalEntropyAmountForSign();
/**
* Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation.
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index 831a106..3bd9d1d 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -247,14 +247,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
specBuilder =
new KeyProtection.Builder(
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY);
- specBuilder.setDigests(
- KeyProperties.DIGEST_NONE,
- KeyProperties.DIGEST_MD5,
- KeyProperties.DIGEST_SHA1,
- KeyProperties.DIGEST_SHA224,
- KeyProperties.DIGEST_SHA256,
- KeyProperties.DIGEST_SHA384,
- KeyProperties.DIGEST_SHA512);
+ // Authorized to be used with any digest (including no digest).
+ specBuilder.setDigests(KeyProperties.DIGEST_NONE);
} else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
specBuilder =
new KeyProtection.Builder(
@@ -262,19 +256,13 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
| KeyProperties.PURPOSE_DECRYPT
| KeyProperties.PURPOSE_SIGN
| KeyProperties.PURPOSE_VERIFY);
- specBuilder.setDigests(
- KeyProperties.DIGEST_NONE,
- KeyProperties.DIGEST_MD5,
- KeyProperties.DIGEST_SHA1,
- KeyProperties.DIGEST_SHA224,
- KeyProperties.DIGEST_SHA256,
- KeyProperties.DIGEST_SHA384,
- KeyProperties.DIGEST_SHA512);
+ // Authorized to be used with any digest (including no digest).
+ specBuilder.setDigests(KeyProperties.DIGEST_NONE);
specBuilder.setSignaturePaddings(
KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
+ // Authorized to be used with any padding (including no padding).
specBuilder.setEncryptionPaddings(
- KeyProperties.ENCRYPTION_PADDING_NONE,
- KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
+ KeyProperties.ENCRYPTION_PADDING_NONE);
// Disable randomized encryption requirement to support encryption padding NONE
// above.
specBuilder.setRandomizedEncryptionRequired(false);
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreUnauthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
index 47cd1d1..76804a9 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
@@ -210,6 +210,11 @@ class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSp
}
@Override
+ protected final int getAdditionalEntropyAmountForFinish() {
+ return 0;
+ }
+
+ @Override
protected final void addAlgorithmSpecificParametersToBegin(
@NonNull KeymasterArguments keymasterArgs) {
if ((isEncrypting()) && (mIvRequired) && (mIvHasBeenUsed)) {
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 68c9c79..4c0631f 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -59,6 +59,14 @@ import javax.security.auth.x500.X500Principal;
* of the certificate can be customized in this spec. The self-signed certificate may be replaced at
* a later time by a certificate signed by a Certificate Authority (CA).
*
+ * <p>NOTE: If a private key is not authorized to sign the self-signed certificate, then the
+ * certificate will be created with an invalid signature which will not verify. Such a certificate
+ * is still useful because it provides access to the public key. To generate a valid
+ * signature for the certificate the key needs to be authorized for
+ * {@link KeyProperties#PURPOSE_SIGN}, a suitable digest or {@link KeyProperties#DIGEST_NONE}, and
+ * {@link KeyProperties#SIGNATURE_PADDING_RSA_PKCS1} or
+ * {@link KeyProperties#ENCRYPTION_PADDING_NONE}.
+ *
* <p>NOTE: The key material of the generated symmetric and private keys is not accessible. The key
* material of the public keys is accessible.
*
@@ -611,9 +619,14 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
*
* <p>This must be specified for keys which are used for signing/verification. For HMAC
* keys, the set of digests defaults to the digest associated with the key algorithm (e.g.,
- * {@code SHA-256} for key algorithm {@code HmacSHA256}
+ * {@code SHA-256} for key algorithm {@code HmacSHA256}).
+ *
+ * <p>For private keys used for TLS/SSL client or server authentication it is usually
+ * necessary to authorize the use of no digest ({@link KeyProperties#DIGEST_NONE}). This is
+ * because TLS/SSL stacks typically generate the necessary digest(s) themselves and then use
+ * a private key to sign it.
*
- * @see KeyProperties.Digest
+ * <p>See {@link KeyProperties}.{@code DIGEST} constants.
*/
@NonNull
public Builder setDigests(@KeyProperties.DigestEnum String... digests) {
@@ -629,6 +642,12 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
*
* <p>This must be specified for keys which are used for encryption/decryption.
*
+ * <p>For RSA private keys used by TLS/SSL servers to authenticate themselves to clients it
+ * is usually necessary to authorize the use of no/any padding
+ * ({@link KeyProperties#ENCRYPTION_PADDING_NONE}). This is because RSA decryption is
+ * required by some cipher suites, and some stacks request decryption using no padding
+ * whereas others request PKCS#1 padding.
+ *
* <p>See {@link KeyProperties}.{@code ENCRYPTION_PADDING} constants.
*/
@NonNull
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index 5af4181..f9fe176 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -368,6 +368,9 @@ public abstract class KeyProperties {
/**
* No encryption padding.
+ *
+ * <p><b>NOTE</b>: If a key is authorized to be used with no padding, then it can be used with
+ * any padding scheme, both for encryption and signing.
*/
public static final String ENCRYPTION_PADDING_NONE = "NoPadding";
@@ -514,6 +517,9 @@ public abstract class KeyProperties {
/**
* No digest: sign/authenticate the raw message.
+ *
+ * <p><b>NOTE</b>: If a key is authorized to be used with no digest, then it can be used with
+ * any digest.
*/
public static final String DIGEST_NONE = "NONE";
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 48c0ed0..432fc12 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -374,6 +374,12 @@ public final class KeyProtection implements ProtectionParameter {
*
* <p>This must be specified for keys which are used for encryption/decryption.
*
+ * <p>For RSA private keys used by TLS/SSL servers to authenticate themselves to clients it
+ * is usually necessary to authorize the use of no/any padding
+ * ({@link KeyProperties#ENCRYPTION_PADDING_NONE}). This is because RSA decryption is
+ * required by some cipher suites, and some stacks request decryption using no padding
+ * whereas others request PKCS#1 padding.
+ *
* <p>See {@link KeyProperties}.{@code ENCRYPTION_PADDING} constants.
*/
@NonNull
@@ -408,6 +414,11 @@ public final class KeyProtection implements ProtectionParameter {
* {@link Key#getAlgorithm()}. For asymmetric signing keys the set of digest algorithms
* must be specified.
*
+ * <p>For private keys used for TLS/SSL client or server authentication it is usually
+ * necessary to authorize the use of no digest ({@link KeyProperties#DIGEST_NONE}). This is
+ * because TLS/SSL stacks typically generate the necessary digest(s) themselves and then use
+ * a private key to sign it.
+ *
* <p>See {@link KeyProperties}.{@code DIGEST} constants.
*/
@NonNull
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
index 47b4996..9957e79 100644
--- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
+++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
@@ -35,8 +35,8 @@ import java.io.IOException;
* amount of data in one go because the operations are marshalled via Binder. Secondly, the update
* operation may consume less data than provided, in which case the caller has to buffer the
* remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and
- * {@link #doFinal(byte[], int, int) doFinal} operations which can be used to conveniently implement
- * various JCA crypto primitives.
+ * {@link #doFinal(byte[], int, int, byte[]) doFinal} operations which can be used to conveniently
+ * implement various JCA crypto primitives.
*
* <p>Bidirectional chunked streaming of data via a KeyStore crypto operation is abstracted away as
* a {@link Stream} to avoid having this class deal with operation tokens and occasional additional
@@ -60,7 +60,7 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS
* Returns the result of the KeyStore {@code finish} operation or null if keystore couldn't
* be reached.
*/
- OperationResult finish();
+ OperationResult finish(byte[] additionalEntropy);
}
// Binder buffer is about 1MB, but it's shared between all active transactions of the process.
@@ -192,7 +192,7 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS
}
@Override
- public byte[] doFinal(byte[] input, int inputOffset, int inputLength)
+ public byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] additionalEntropy)
throws KeyStoreException {
if (inputLength == 0) {
// No input provided -- simplify the rest of the code
@@ -204,7 +204,7 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS
byte[] output = update(input, inputOffset, inputLength);
output = ArrayUtils.concat(output, flush());
- OperationResult opResult = mKeyStoreStream.finish();
+ OperationResult opResult = mKeyStoreStream.finish(additionalEntropy);
if (opResult == null) {
throw new KeyStoreConnectException();
} else if (opResult.resultCode != KeyStore.NO_ERROR) {
@@ -268,8 +268,8 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS
}
@Override
- public OperationResult finish() {
- return mKeyStore.finish(mOperationToken, null, null);
+ public OperationResult finish(byte[] additionalEntropy) {
+ return mKeyStore.finish(mOperationToken, null, null, additionalEntropy);
}
}
}
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java
index 2fb8f20..1c6de2d 100644
--- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java
+++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java
@@ -28,12 +28,13 @@ import android.security.KeyStoreException;
* amount of data in one go because the operations are marshalled via Binder. Secondly, the update
* operation may consume less data than provided, in which case the caller has to buffer the
* remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and
- * {@link #doFinal(byte[], int, int) doFinal} operations which can be used to conveniently implement
- * various JCA crypto primitives.
+ * {@link #doFinal(byte[], int, int, byte[]) doFinal} operations which can be used to conveniently
+ * implement various JCA crypto primitives.
*
* @hide
*/
interface KeyStoreCryptoOperationStreamer {
byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException;
- byte[] doFinal(byte[] input, int inputOffset, int inputLength) throws KeyStoreException;
+ byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] additionalEntropy)
+ throws KeyStoreException;
}