Files
frameworks_base/keystore/java/android/security/KeyStoreKeyConstraints.java
Alex Klyubin c46e9e7da4 Make the new AndroidKeyStore API conformant.
This makes the new AndroidKeyStore API conform with the latest
Keymaster API changes as well as the latest Android framework API
design guidelines.

Keymaster changes:
* Multiple paddings, block modes, and digests can be set on a key.
* "max uses per boot" and "min seconds between use" restrictions will
  not be exposed in the framework API.
* Padding scheme ZERO will not be exposed.

Changes due to Android framework design guidelines:
* Sets of enum values have been replaced with bitsets represented as
  ints.
* Integer has been replaced with int, with null being represented
  with a special value (e.g., -1 or 0) where possible.

Bug: 18088752
Change-Id: Ib21739aa9b42d48895cb7a681e836a5c6d972ac6
2015-04-07 09:18:00 -07:00

699 lines
21 KiB
Java

/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.security;
import android.annotation.IntDef;
import android.security.keymaster.KeymasterDefs;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collection;
import java.util.Locale;
/**
* Constraints for {@code AndroidKeyStore} keys.
*
* @hide
*/
public abstract class KeyStoreKeyConstraints {
private KeyStoreKeyConstraints() {}
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true,
value = {Purpose.ENCRYPT, Purpose.DECRYPT, Purpose.SIGN, Purpose.VERIFY})
public @interface PurposeEnum {}
/**
* Purpose of key.
*/
public static abstract class Purpose {
private Purpose() {}
/**
* Purpose: encryption.
*/
public static final int ENCRYPT = 1 << 0;
/**
* Purpose: decryption.
*/
public static final int DECRYPT = 1 << 1;
/**
* Purpose: signing.
*/
public static final int SIGN = 1 << 2;
/**
* Purpose: signature verification.
*/
public static final int VERIFY = 1 << 3;
/**
* @hide
*/
public static int toKeymaster(@PurposeEnum int purpose) {
switch (purpose) {
case ENCRYPT:
return KeymasterDefs.KM_PURPOSE_ENCRYPT;
case DECRYPT:
return KeymasterDefs.KM_PURPOSE_DECRYPT;
case SIGN:
return KeymasterDefs.KM_PURPOSE_SIGN;
case VERIFY:
return KeymasterDefs.KM_PURPOSE_VERIFY;
default:
throw new IllegalArgumentException("Unknown purpose: " + purpose);
}
}
/**
* @hide
*/
public static @PurposeEnum int fromKeymaster(int purpose) {
switch (purpose) {
case KeymasterDefs.KM_PURPOSE_ENCRYPT:
return ENCRYPT;
case KeymasterDefs.KM_PURPOSE_DECRYPT:
return DECRYPT;
case KeymasterDefs.KM_PURPOSE_SIGN:
return SIGN;
case KeymasterDefs.KM_PURPOSE_VERIFY:
return VERIFY;
default:
throw new IllegalArgumentException("Unknown purpose: " + purpose);
}
}
/**
* @hide
*/
public static int[] allToKeymaster(@PurposeEnum int purposes) {
int[] result = getSetFlags(purposes);
for (int i = 0; i < result.length; i++) {
result[i] = toKeymaster(result[i]);
}
return result;
}
/**
* @hide
*/
public static @PurposeEnum int allFromKeymaster(Collection<Integer> purposes) {
@PurposeEnum int result = 0;
for (int keymasterPurpose : purposes) {
result |= fromKeymaster(keymasterPurpose);
}
return result;
}
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({Algorithm.AES, Algorithm.HMAC})
public @interface AlgorithmEnum {}
/**
* Key algorithm.
*/
public static abstract class Algorithm {
private Algorithm() {}
/**
* Key algorithm: AES.
*/
public static final int AES = 0;
/**
* Key algorithm: HMAC.
*/
public static final int HMAC = 1;
/**
* @hide
*/
public static int toKeymaster(@AlgorithmEnum int algorithm) {
switch (algorithm) {
case AES:
return KeymasterDefs.KM_ALGORITHM_AES;
case HMAC:
return KeymasterDefs.KM_ALGORITHM_HMAC;
default:
throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
}
}
/**
* @hide
*/
public static @AlgorithmEnum int fromKeymaster(int algorithm) {
switch (algorithm) {
case KeymasterDefs.KM_ALGORITHM_AES:
return AES;
case KeymasterDefs.KM_ALGORITHM_HMAC:
return HMAC;
default:
throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
}
}
/**
* @hide
*/
public static String toString(@AlgorithmEnum int algorithm) {
switch (algorithm) {
case AES:
return "AES";
case HMAC:
return "HMAC";
default:
throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
}
}
/**
* @hide
*/
public static @AlgorithmEnum int fromJCASecretKeyAlgorithm(String algorithm) {
if (algorithm == null) {
throw new NullPointerException("algorithm == null");
} else if ("AES".equalsIgnoreCase(algorithm)) {
return AES;
} else if (algorithm.toLowerCase(Locale.US).startsWith("hmac")) {
return HMAC;
} else {
throw new IllegalArgumentException(
"Unsupported secret key algorithm: " + algorithm);
}
}
/**
* @hide
*/
public static String toJCASecretKeyAlgorithm(@AlgorithmEnum int algorithm,
@DigestEnum Integer digest) {
switch (algorithm) {
case AES:
return "AES";
case HMAC:
if (digest == null) {
throw new IllegalArgumentException("HMAC digest not specified");
}
switch (digest) {
case Digest.SHA256:
return "HmacSHA256";
default:
throw new IllegalArgumentException(
"Unsupported HMAC digest: " + digest);
}
default:
throw new IllegalArgumentException("Unsupported key algorithm: " + algorithm);
}
}
}
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true,
value = {Padding.NONE, Padding.PKCS7})
public @interface PaddingEnum {}
/**
* Padding for signing and encryption.
*/
public static abstract class Padding {
private Padding() {}
/**
* No padding.
*/
public static final int NONE = 1 << 0;
/**
* PKCS#7 padding.
*/
public static final int PKCS7 = 1 << 1;
/**
* @hide
*/
public static int toKeymaster(int padding) {
switch (padding) {
case NONE:
return KeymasterDefs.KM_PAD_NONE;
case PKCS7:
return KeymasterDefs.KM_PAD_PKCS7;
default:
throw new IllegalArgumentException("Unknown padding: " + padding);
}
}
/**
* @hide
*/
public static @PaddingEnum int fromKeymaster(int padding) {
switch (padding) {
case KeymasterDefs.KM_PAD_NONE:
return NONE;
case KeymasterDefs.KM_PAD_PKCS7:
return PKCS7;
default:
throw new IllegalArgumentException("Unknown padding: " + padding);
}
}
/**
* @hide
*/
public static String toString(@PaddingEnum int padding) {
switch (padding) {
case NONE:
return "NONE";
case PKCS7:
return "PKCS#7";
default:
throw new IllegalArgumentException("Unknown padding: " + padding);
}
}
/**
* @hide
*/
public static @PaddingEnum int fromJCAPadding(String padding) {
String paddingLower = padding.toLowerCase(Locale.US);
if ("nopadding".equals(paddingLower)) {
return NONE;
} else if ("pkcs7padding".equals(paddingLower)) {
return PKCS7;
} else {
throw new IllegalArgumentException("Unknown padding: " + padding);
}
}
/**
* @hide
*/
public static int[] allToKeymaster(@PaddingEnum int paddings) {
int[] result = getSetFlags(paddings);
for (int i = 0; i < result.length; i++) {
result[i] = toKeymaster(result[i]);
}
return result;
}
/**
* @hide
*/
public static @PaddingEnum int allFromKeymaster(Collection<Integer> paddings) {
@PaddingEnum int result = 0;
for (int keymasterPadding : paddings) {
result |= fromKeymaster(keymasterPadding);
}
return result;
}
}
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true,
value = {Digest.NONE, Digest.SHA256})
public @interface DigestEnum {}
/**
* Digests that can be used with a key when signing or generating Message Authentication
* Codes (MACs).
*/
public static abstract class Digest {
private Digest() {}
/**
* No digest: sign/authenticate the raw message.
*/
public static final int NONE = 1 << 0;
/**
* SHA-256 digest.
*/
public static final int SHA256 = 1 << 1;
/**
* @hide
*/
public static String toString(@DigestEnum int digest) {
switch (digest) {
case NONE:
return "NONE";
case SHA256:
return "SHA256";
default:
throw new IllegalArgumentException("Unknown digest: " + digest);
}
}
/**
* @hide
*/
public static String[] allToString(@DigestEnum int digests) {
int[] values = getSetFlags(digests);
String[] result = new String[values.length];
for (int i = 0; i < values.length; i++) {
result[i] = toString(values[i]);
}
return result;
}
/**
* @hide
*/
public static int toKeymaster(@DigestEnum int digest) {
switch (digest) {
case NONE:
return KeymasterDefs.KM_DIGEST_NONE;
case SHA256:
return KeymasterDefs.KM_DIGEST_SHA_2_256;
default:
throw new IllegalArgumentException("Unknown digest: " + digest);
}
}
/**
* @hide
*/
public static @DigestEnum int fromKeymaster(int digest) {
switch (digest) {
case KeymasterDefs.KM_DIGEST_NONE:
return NONE;
case KeymasterDefs.KM_DIGEST_SHA_2_256:
return SHA256;
default:
throw new IllegalArgumentException("Unknown digest: " + digest);
}
}
/**
* @hide
*/
public static int[] allToKeymaster(@DigestEnum int digests) {
int[] result = getSetFlags(digests);
for (int i = 0; i < result.length; i++) {
result[i] = toKeymaster(result[i]);
}
return result;
}
/**
* @hide
*/
public static @DigestEnum int allFromKeymaster(Collection<Integer> digests) {
@DigestEnum int result = 0;
for (int keymasterDigest : digests) {
result |= fromKeymaster(keymasterDigest);
}
return result;
}
/**
* @hide
*/
public static @DigestEnum Integer fromJCASecretKeyAlgorithm(String algorithm) {
String algorithmLower = algorithm.toLowerCase(Locale.US);
if (algorithmLower.startsWith("hmac")) {
if ("hmacsha256".equals(algorithmLower)) {
return SHA256;
} else {
throw new IllegalArgumentException("Unsupported digest: "
+ algorithmLower.substring("hmac".length()));
}
} else {
return null;
}
}
/**
* @hide
*/
public static String toJCASignatureAlgorithmDigest(@DigestEnum int digest) {
switch (digest) {
case NONE:
return "NONE";
case SHA256:
return "SHA256";
default:
throw new IllegalArgumentException("Unknown digest: " + digest);
}
}
/**
* @hide
*/
public static Integer getOutputSizeBytes(@DigestEnum int digest) {
switch (digest) {
case NONE:
return null;
case SHA256:
return 256 / 8;
default:
throw new IllegalArgumentException("Unknown digest: " + digest);
}
}
}
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true,
value = {BlockMode.ECB, BlockMode.CBC, BlockMode.CTR})
public @interface BlockModeEnum {}
/**
* Block modes that can be used when encrypting/decrypting using a key.
*/
public static abstract class BlockMode {
private BlockMode() {}
/** Electronic Codebook (ECB) block mode. */
public static final int ECB = 1 << 0;
/** Cipher Block Chaining (CBC) block mode. */
public static final int CBC = 1 << 1;
/** Counter (CTR) block mode. */
public static final int CTR = 1 << 2;
/**
* @hide
*/
public static int toKeymaster(@BlockModeEnum int mode) {
switch (mode) {
case ECB:
return KeymasterDefs.KM_MODE_ECB;
case CBC:
return KeymasterDefs.KM_MODE_CBC;
case CTR:
return KeymasterDefs.KM_MODE_CTR;
default:
throw new IllegalArgumentException("Unknown block mode: " + mode);
}
}
/**
* @hide
*/
public static @BlockModeEnum int fromKeymaster(int mode) {
switch (mode) {
case KeymasterDefs.KM_MODE_ECB:
return ECB;
case KeymasterDefs.KM_MODE_CBC:
return CBC;
case KeymasterDefs.KM_MODE_CTR:
return CTR;
default:
throw new IllegalArgumentException("Unknown block mode: " + mode);
}
}
/**
* @hide
*/
public static int[] allToKeymaster(@BlockModeEnum int modes) {
int[] result = getSetFlags(modes);
for (int i = 0; i < result.length; i++) {
result[i] = toKeymaster(result[i]);
}
return result;
}
/**
* @hide
*/
public static @BlockModeEnum int allFromKeymaster(Collection<Integer> modes) {
@BlockModeEnum int result = 0;
for (int keymasterMode : modes) {
result |= fromKeymaster(keymasterMode);
}
return result;
}
/**
* @hide
*/
public static String toString(@BlockModeEnum int mode) {
switch (mode) {
case ECB:
return "ECB";
case CBC:
return "CBC";
case CTR:
return "CTR";
default:
throw new IllegalArgumentException("Unknown block mode: " + mode);
}
}
/**
* @hide
*/
public static @BlockModeEnum int fromJCAMode(String mode) {
String modeLower = mode.toLowerCase(Locale.US);
if ("ecb".equals(modeLower)) {
return ECB;
} else if ("cbc".equals(modeLower)) {
return CBC;
} else if ("ctr".equals(modeLower)) {
return CTR;
} else {
throw new IllegalArgumentException("Unknown block mode: " + mode);
}
}
}
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true,
value = {UserAuthenticator.LOCK_SCREEN})
public @interface UserAuthenticatorEnum {}
/**
* User authenticators which can be used to restrict/protect access to keys.
*/
public static abstract class UserAuthenticator {
private UserAuthenticator() {}
/** Lock screen. */
public static final int LOCK_SCREEN = 1 << 0;
/**
* @hide
*/
public static int toKeymaster(@UserAuthenticatorEnum int userAuthenticator) {
switch (userAuthenticator) {
case LOCK_SCREEN:
return KeymasterDefs.HW_AUTH_PASSWORD;
default:
throw new IllegalArgumentException(
"Unknown user authenticator: " + userAuthenticator);
}
}
/**
* @hide
*/
public static @UserAuthenticatorEnum int fromKeymaster(int userAuthenticator) {
switch (userAuthenticator) {
case KeymasterDefs.HW_AUTH_PASSWORD:
return LOCK_SCREEN;
default:
throw new IllegalArgumentException(
"Unknown user authenticator: " + userAuthenticator);
}
}
/**
* @hide
*/
public static int allToKeymaster(@UserAuthenticatorEnum int userAuthenticators) {
int result = 0;
int userAuthenticator = 1;
while (userAuthenticators != 0) {
if ((userAuthenticators & 1) != 0) {
result |= toKeymaster(userAuthenticator);
}
userAuthenticators >>>= 1;
userAuthenticator <<= 1;
}
return result;
}
/**
* @hide
*/
public static @UserAuthenticatorEnum int allFromKeymaster(int userAuthenticators) {
@UserAuthenticatorEnum int result = 0;
int userAuthenticator = 1;
while (userAuthenticators != 0) {
if ((userAuthenticators & 1) != 0) {
result |= fromKeymaster(userAuthenticator);
}
userAuthenticators >>>= 1;
userAuthenticator <<= 1;
}
return result;
}
/**
* @hide
*/
public static String toString(@UserAuthenticatorEnum int userAuthenticator) {
switch (userAuthenticator) {
case LOCK_SCREEN:
return "LOCK_SCREEN";
default:
throw new IllegalArgumentException(
"Unknown user authenticator: " + userAuthenticator);
}
}
}
private static final int[] EMPTY_INT_ARRAY = new int[0];
private static int[] getSetFlags(int flags) {
if (flags == 0) {
return EMPTY_INT_ARRAY;
}
int result[] = new int[getSetBitCount(flags)];
int resultOffset = 0;
int flag = 1;
while (flags != 0) {
if ((flags & 1) != 0) {
result[resultOffset] = flag;
resultOffset++;
}
flags >>>= 1;
flag <<= 1;
}
return result;
}
private static int getSetBitCount(int value) {
if (value == 0) {
return 0;
}
int result = 0;
while (value != 0) {
if ((value & 1) != 0) {
result++;
}
value >>>= 1;
}
return result;
}
}