GCM mode is not yet implemented. This is just adding a constant to KeyStoreKeyConstraints.BlockMode. Bug: 18088752 Change-Id: Ibba5b393f56ab9f6bb96d994f110687ab8d65ff3
710 lines
21 KiB
Java
710 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, BlockMode.GCM})
|
|
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;
|
|
|
|
/** Galois/Counter Mode (GCM) block mode. */
|
|
public static final int GCM = 1 << 3;
|
|
|
|
/**
|
|
* @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;
|
|
case GCM:
|
|
return KeymasterDefs.KM_MODE_GCM;
|
|
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;
|
|
case KeymasterDefs.KM_MODE_GCM:
|
|
return GCM;
|
|
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";
|
|
case GCM:
|
|
return "GCM";
|
|
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 if ("gcm".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;
|
|
}
|
|
}
|