Skip to content

Commit f7592b2

Browse files
Alex KlyubinAndroid (Google) Code Review
authored andcommitted
Merge "HMAC keys are authorized for exactly one digest." into mnc-dev
2 parents d4b566b + c58153b commit f7592b2

4 files changed

Lines changed: 55 additions & 71 deletions

File tree

keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java

Lines changed: 21 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -197,48 +197,36 @@ protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
197197
}
198198
}
199199
}
200-
if (spec.isDigestsSpecified()) {
201-
// Digest(s) explicitly specified in the spec
202-
mKeymasterDigests = KeyProperties.Digest.allToKeymaster(spec.getDigests());
203-
if (mKeymasterDigest != -1) {
204-
// Key algorithm implies a digest -- ensure it's specified in the spec as
205-
// first digest.
206-
if (!com.android.internal.util.ArrayUtils.contains(
207-
mKeymasterDigests, mKeymasterDigest)) {
200+
201+
if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
202+
// JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm
203+
// implies SHA-256 digest). Because keymaster HMAC key is authorized only for
204+
// one digest, we don't let algorithm parameter spec override the digest implied
205+
// by the key. If the spec specifies digests at all, it must specify only one
206+
// digest, the only implied by key algorithm.
207+
mKeymasterDigests = new int[] {mKeymasterDigest};
208+
if (spec.isDigestsSpecified()) {
209+
// Digest(s) explicitly specified in the spec. Check that the list
210+
// consists of exactly one digest, the one implied by key algorithm.
211+
int[] keymasterDigestsFromSpec =
212+
KeyProperties.Digest.allToKeymaster(spec.getDigests());
213+
if ((keymasterDigestsFromSpec.length != 1)
214+
|| (keymasterDigestsFromSpec[0] != mKeymasterDigest)) {
208215
throw new InvalidAlgorithmParameterException(
209-
"Digests specified in algorithm parameters ("
210-
+ Arrays.asList(spec.getDigests()) + ") must include "
211-
+ " the digest "
216+
"Unsupported digests specification: "
217+
+ Arrays.asList(spec.getDigests()) + ". Only "
212218
+ KeyProperties.Digest.fromKeymaster(mKeymasterDigest)
213-
+ " implied by key algorithm");
214-
}
215-
if (mKeymasterDigests[0] != mKeymasterDigest) {
216-
// The first digest is not the one implied by the key algorithm.
217-
// Swap the implied digest with the first one.
218-
for (int i = 0; i < mKeymasterDigests.length; i++) {
219-
if (mKeymasterDigests[i] == mKeymasterDigest) {
220-
mKeymasterDigests[i] = mKeymasterDigests[0];
221-
mKeymasterDigests[0] = mKeymasterDigest;
222-
break;
223-
}
224-
}
219+
+ " supported for this HMAC key algorithm");
225220
}
226221
}
227222
} else {
228-
// No digest specified in the spec
229-
if (mKeymasterDigest != -1) {
230-
// Key algorithm implies a digest -- use that digest
231-
mKeymasterDigests = new int[] {mKeymasterDigest};
223+
// Key algorithm does not imply a digest.
224+
if (spec.isDigestsSpecified()) {
225+
mKeymasterDigests = KeyProperties.Digest.allToKeymaster(spec.getDigests());
232226
} else {
233227
mKeymasterDigests = EmptyArray.INT;
234228
}
235229
}
236-
if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
237-
if (mKeymasterDigests.length == 0) {
238-
throw new InvalidAlgorithmParameterException(
239-
"At least one digest algorithm must be specified");
240-
}
241-
}
242230

243231
// Check that user authentication related parameters are acceptable. This method
244232
// will throw an IllegalStateException if there are issues (e.g., secure lock screen

keystore/java/android/security/keystore/AndroidKeyStoreSpi.java

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import java.security.KeyStoreSpi;
4242
import java.security.NoSuchAlgorithmException;
4343
import java.security.PrivateKey;
44+
import java.security.ProviderException;
4445
import java.security.PublicKey;
4546
import java.security.UnrecoverableKeyException;
4647
import java.security.cert.Certificate;
@@ -605,50 +606,43 @@ private void setSecretKeyEntry(String entryAlias, SecretKey key,
605606
args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm);
606607

607608
int[] keymasterDigests;
608-
int keymasterDigest = KeyProperties.KeyAlgorithm.toKeymasterDigest(key.getAlgorithm());
609-
if (params.isDigestsSpecified()) {
610-
// Digest(s) specified in parameters
611-
keymasterDigests = KeyProperties.Digest.allToKeymaster(params.getDigests());
612-
if (keymasterDigest != -1) {
613-
// Digest also specified in the JCA key algorithm name.
614-
if (!com.android.internal.util.ArrayUtils.contains(
615-
keymasterDigests, keymasterDigest)) {
616-
throw new KeyStoreException("Digest specified in key algorithm "
617-
+ key.getAlgorithm() + " not specified in protection parameters: "
618-
+ Arrays.asList(params.getDigests()));
619-
}
620-
// When the key is read back from keystore we reconstruct the JCA key algorithm
621-
// name from the KM_TAG_ALGORITHM and the first KM_TAG_DIGEST. Thus we need to
622-
// ensure that the digest reflected in the JCA key algorithm name is the first
623-
// KM_TAG_DIGEST tag.
624-
if (keymasterDigests[0] != keymasterDigest) {
625-
// The first digest is not the one implied by the JCA key algorithm name.
626-
// Swap the implied digest with the first one.
627-
for (int i = 0; i < keymasterDigests.length; i++) {
628-
if (keymasterDigests[i] == keymasterDigest) {
629-
keymasterDigests[i] = keymasterDigests[0];
630-
keymasterDigests[0] = keymasterDigest;
631-
break;
632-
}
633-
}
609+
if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
610+
// JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm
611+
// implies SHA-256 digest). Because keymaster HMAC key is authorized only for one
612+
// digest, we don't let import parameters override the digest implied by the key.
613+
// If the parameters specify digests at all, they must specify only one digest, the
614+
// only implied by key algorithm.
615+
int keymasterImpliedDigest =
616+
KeyProperties.KeyAlgorithm.toKeymasterDigest(key.getAlgorithm());
617+
if (keymasterImpliedDigest == -1) {
618+
throw new ProviderException(
619+
"HMAC key algorithm digest unknown for key algorithm "
620+
+ key.getAlgorithm());
621+
}
622+
keymasterDigests = new int[] {keymasterImpliedDigest};
623+
if (params.isDigestsSpecified()) {
624+
// Digest(s) explicitly specified in params -- check that the list consists of
625+
// exactly one digest, the one implied by key algorithm.
626+
int[] keymasterDigestsFromParams =
627+
KeyProperties.Digest.allToKeymaster(params.getDigests());
628+
if ((keymasterDigestsFromParams.length != 1)
629+
|| (keymasterDigestsFromParams[0] != keymasterImpliedDigest)) {
630+
throw new KeyStoreException(
631+
"Unsupported digests specification: "
632+
+ Arrays.asList(params.getDigests()) + ". Only "
633+
+ KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest)
634+
+ " supported for HMAC key algorithm " + key.getAlgorithm());
634635
}
635636
}
636637
} else {
637-
// No digest specified in parameters
638-
if (keymasterDigest != -1) {
639-
// Digest specified in the JCA key algorithm name.
640-
keymasterDigests = new int[] {keymasterDigest};
638+
// Key algorithm does not imply a digest.
639+
if (params.isDigestsSpecified()) {
640+
keymasterDigests = KeyProperties.Digest.allToKeymaster(params.getDigests());
641641
} else {
642642
keymasterDigests = EmptyArray.INT;
643643
}
644644
}
645645
args.addEnums(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests);
646-
if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
647-
if (keymasterDigests.length == 0) {
648-
throw new KeyStoreException("At least one digest algorithm must be specified"
649-
+ " for key algorithm " + key.getAlgorithm());
650-
}
651-
}
652646

653647
@KeyProperties.PurposeEnum int purposes = params.getPurposes();
654648
int[] keymasterBlockModes =

keystore/java/android/security/keystore/KeyGenParameterSpec.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,8 @@ public Builder setKeyValidityForConsumptionEnd(Date endDate) {
642642
* <p>This must be specified for signing/verification keys and RSA encryption/decryption
643643
* keys used with RSA OAEP padding scheme because these operations involve a digest. For
644644
* HMAC keys, the default is the digest associated with the key algorithm (e.g.,
645-
* {@code SHA-256} for key algorithm {@code HmacSHA256}).
645+
* {@code SHA-256} for key algorithm {@code HmacSHA256}). HMAC keys cannot be authorized
646+
* for more than one digest.
646647
*
647648
* <p>For private keys used for TLS/SSL client or server authentication it is usually
648649
* necessary to authorize the use of no digest ({@link KeyProperties#DIGEST_NONE}). This is

keystore/java/android/security/keystore/KeyProtection.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,8 @@ public Builder setSignaturePaddings(
423423
* <p>This must be specified for signing/verification keys and RSA encryption/decryption
424424
* keys used with RSA OAEP padding scheme because these operations involve a digest. For
425425
* HMAC keys, the default is the digest specified in {@link Key#getAlgorithm()} (e.g.,
426-
* {@code SHA-256} for key algorithm {@code HmacSHA256}).
426+
* {@code SHA-256} for key algorithm {@code HmacSHA256}). HMAC keys cannot be authorized
427+
* for more than one digest.
427428
*
428429
* <p>For private keys used for TLS/SSL client or server authentication it is usually
429430
* necessary to authorize the use of no digest ({@link KeyProperties#DIGEST_NONE}). This is

0 commit comments

Comments
 (0)