diff options
Diffstat (limited to 'keystore')
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; } |