This defines the String enum values based on JCA standard names for key algorithm, block mode, padding schemes, and digests. This should make it safer to interact with AndroidKeyStore code that uses JCA strings. This was requested by API Council. Bug: 18088752 Change-Id: I241d9225a13b85479d0a84e49d0a98cbc77e5817
713 lines
23 KiB
Java
713 lines
23 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.annotation.StringDef;
|
|
import android.security.keymaster.KeymasterDefs;
|
|
|
|
import libcore.util.EmptyArray;
|
|
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.security.Key;
|
|
import java.security.KeyFactory;
|
|
import java.security.KeyPairGenerator;
|
|
import java.util.Collection;
|
|
import java.util.Locale;
|
|
|
|
import javax.crypto.Cipher;
|
|
import javax.crypto.KeyGenerator;
|
|
import javax.crypto.Mac;
|
|
import javax.crypto.SecretKeyFactory;
|
|
|
|
/**
|
|
* Properties of {@code AndroidKeyStore} keys.
|
|
*/
|
|
public abstract class KeyStoreKeyProperties {
|
|
private KeyStoreKeyProperties() {}
|
|
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef(flag = true,
|
|
value = {Purpose.ENCRYPT, Purpose.DECRYPT, Purpose.SIGN, Purpose.VERIFY})
|
|
public @interface PurposeEnum {}
|
|
|
|
/**
|
|
* Purposes 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)
|
|
@StringDef({
|
|
Algorithm.RSA,
|
|
Algorithm.EC,
|
|
Algorithm.AES,
|
|
Algorithm.HMAC_SHA1,
|
|
Algorithm.HMAC_SHA224,
|
|
Algorithm.HMAC_SHA256,
|
|
Algorithm.HMAC_SHA384,
|
|
Algorithm.HMAC_SHA512,
|
|
})
|
|
public @interface AlgorithmEnum {}
|
|
|
|
/**
|
|
* Key algorithms.
|
|
*
|
|
* <p>These are standard names which can be used to obtain instances of {@link KeyGenerator},
|
|
* {@link KeyPairGenerator}, {@link Cipher} (as part of the transformation string), {@link Mac},
|
|
* {@link KeyFactory}, {@link SecretKeyFactory}. These are also the names used by
|
|
* {@link Key#getAlgorithm()}.
|
|
*/
|
|
public static abstract class Algorithm {
|
|
private Algorithm() {}
|
|
|
|
/** Rivest Shamir Adleman (RSA) key. */
|
|
public static final String RSA = "RSA";
|
|
|
|
/** Elliptic Curve (EC) key. */
|
|
public static final String EC = "EC";
|
|
|
|
/** Advanced Encryption Standard (AES) key. */
|
|
public static final String AES = "AES";
|
|
|
|
/** Keyed-Hash Message Authentication Code (HMAC) key using SHA-1 as the hash. */
|
|
public static final String HMAC_SHA1 = "HmacSHA1";
|
|
|
|
/** Keyed-Hash Message Authentication Code (HMAC) key using SHA-224 as the hash. */
|
|
public static final String HMAC_SHA224 = "HmacSHA224";
|
|
|
|
/** Keyed-Hash Message Authentication Code (HMAC) key using SHA-256 as the hash. */
|
|
public static final String HMAC_SHA256 = "HmacSHA256";
|
|
|
|
/** Keyed-Hash Message Authentication Code (HMAC) key using SHA-384 as the hash. */
|
|
public static final String HMAC_SHA384 = "HmacSHA384";
|
|
|
|
/** Keyed-Hash Message Authentication Code (HMAC) key using SHA-512 as the hash. */
|
|
public static final String HMAC_SHA512 = "HmacSHA512";
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
static int toKeymasterSecretKeyAlgorithm(@AlgorithmEnum String algorithm) {
|
|
if (AES.equalsIgnoreCase(algorithm)) {
|
|
return KeymasterDefs.KM_ALGORITHM_AES;
|
|
} else if (algorithm.toUpperCase(Locale.US).startsWith("HMAC")) {
|
|
return KeymasterDefs.KM_ALGORITHM_HMAC;
|
|
} else {
|
|
throw new IllegalArgumentException(
|
|
"Unsupported secret key algorithm: " + algorithm);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
static @AlgorithmEnum String fromKeymasterSecretKeyAlgorithm(
|
|
int keymasterAlgorithm, int keymasterDigest) {
|
|
switch (keymasterAlgorithm) {
|
|
case KeymasterDefs.KM_ALGORITHM_AES:
|
|
if (keymasterDigest != -1) {
|
|
throw new IllegalArgumentException("Digest not supported for AES key: "
|
|
+ Digest.fromKeymaster(keymasterDigest));
|
|
}
|
|
return AES;
|
|
case KeymasterDefs.KM_ALGORITHM_HMAC:
|
|
switch (keymasterDigest) {
|
|
case KeymasterDefs.KM_DIGEST_SHA1:
|
|
return HMAC_SHA1;
|
|
case KeymasterDefs.KM_DIGEST_SHA_2_224:
|
|
return HMAC_SHA224;
|
|
case KeymasterDefs.KM_DIGEST_SHA_2_256:
|
|
return HMAC_SHA256;
|
|
case KeymasterDefs.KM_DIGEST_SHA_2_384:
|
|
return HMAC_SHA384;
|
|
case KeymasterDefs.KM_DIGEST_SHA_2_512:
|
|
return HMAC_SHA512;
|
|
default:
|
|
throw new IllegalArgumentException("Unsupported HMAC digest: "
|
|
+ Digest.fromKeymaster(keymasterDigest));
|
|
}
|
|
default:
|
|
throw new IllegalArgumentException(
|
|
"Unsupported algorithm: " + keymasterAlgorithm);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*
|
|
* @return keymaster digest or {@code -1} if the algorithm does not involve a digest.
|
|
*/
|
|
static int toKeymasterDigest(@AlgorithmEnum String algorithm) {
|
|
String algorithmUpper = algorithm.toUpperCase(Locale.US);
|
|
if (algorithmUpper.startsWith("HMAC")) {
|
|
String digestUpper = algorithmUpper.substring("HMAC".length());
|
|
switch (digestUpper) {
|
|
case "SHA1":
|
|
return KeymasterDefs.KM_DIGEST_SHA1;
|
|
case "SHA224":
|
|
return KeymasterDefs.KM_DIGEST_SHA_2_224;
|
|
case "SHA256":
|
|
return KeymasterDefs.KM_DIGEST_SHA_2_256;
|
|
case "SHA384":
|
|
return KeymasterDefs.KM_DIGEST_SHA_2_384;
|
|
case "SHA512":
|
|
return KeymasterDefs.KM_DIGEST_SHA_2_512;
|
|
default:
|
|
throw new IllegalArgumentException(
|
|
"Unsupported HMAC digest: " + digestUpper);
|
|
}
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@StringDef({
|
|
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 String ECB = "ECB";
|
|
|
|
/** Cipher Block Chaining (CBC) block mode. */
|
|
public static final String CBC = "CBC";
|
|
|
|
/** Counter (CTR) block mode. */
|
|
public static final String CTR = "CTR";
|
|
|
|
/** Galois/Counter Mode (GCM) block mode. */
|
|
public static final String GCM = "GCM";
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
static int toKeymaster(@BlockModeEnum String blockMode) {
|
|
if (ECB.equalsIgnoreCase(blockMode)) {
|
|
return KeymasterDefs.KM_MODE_ECB;
|
|
} else if (CBC.equalsIgnoreCase(blockMode)) {
|
|
return KeymasterDefs.KM_MODE_CBC;
|
|
} else if (CTR.equalsIgnoreCase(blockMode)) {
|
|
return KeymasterDefs.KM_MODE_CTR;
|
|
} else if (GCM.equalsIgnoreCase(blockMode)) {
|
|
return KeymasterDefs.KM_MODE_GCM;
|
|
} else {
|
|
throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
static @BlockModeEnum String fromKeymaster(int blockMode) {
|
|
switch (blockMode) {
|
|
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("Unsupported block mode: " + blockMode);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
static @BlockModeEnum String[] allFromKeymaster(Collection<Integer> blockModes) {
|
|
if ((blockModes == null) || (blockModes.isEmpty())) {
|
|
return EmptyArray.STRING;
|
|
}
|
|
@BlockModeEnum String[] result = new String[blockModes.size()];
|
|
int offset = 0;
|
|
for (int blockMode : blockModes) {
|
|
result[offset] = fromKeymaster(blockMode);
|
|
offset++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
static int[] allToKeymaster(@BlockModeEnum String[] blockModes) {
|
|
if ((blockModes == null) || (blockModes.length == 0)) {
|
|
return EmptyArray.INT;
|
|
}
|
|
int[] result = new int[blockModes.length];
|
|
for (int i = 0; i < blockModes.length; i++) {
|
|
result[i] = toKeymaster(blockModes[i]);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@StringDef({
|
|
EncryptionPadding.NONE,
|
|
EncryptionPadding.PKCS7,
|
|
EncryptionPadding.RSA_PKCS1,
|
|
EncryptionPadding.RSA_OAEP,
|
|
})
|
|
public @interface EncryptionPaddingEnum {}
|
|
|
|
/**
|
|
* Padding schemes for encryption/decryption.
|
|
*/
|
|
public static abstract class EncryptionPadding {
|
|
private EncryptionPadding() {}
|
|
|
|
/**
|
|
* No padding.
|
|
*/
|
|
public static final String NONE = "NoPadding";
|
|
|
|
/**
|
|
* PKCS#7 padding.
|
|
*/
|
|
public static final String PKCS7 = "PKCS7Padding";
|
|
|
|
/**
|
|
* RSA PKCS#1 v1.5 padding for encryption/decryption.
|
|
*/
|
|
public static final String RSA_PKCS1 = "PKCS1Padding";
|
|
|
|
/**
|
|
* RSA Optimal Asymmetric Encryption Padding (OAEP).
|
|
*/
|
|
public static final String RSA_OAEP = "OAEPPadding";
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
static int toKeymaster(@EncryptionPaddingEnum String padding) {
|
|
if (NONE.equalsIgnoreCase(padding)) {
|
|
return KeymasterDefs.KM_PAD_NONE;
|
|
} else if (PKCS7.equalsIgnoreCase(padding)) {
|
|
return KeymasterDefs.KM_PAD_PKCS7;
|
|
} else if (RSA_PKCS1.equalsIgnoreCase(padding)) {
|
|
return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT;
|
|
} else if (RSA_OAEP.equalsIgnoreCase(padding)) {
|
|
return KeymasterDefs.KM_PAD_RSA_OAEP;
|
|
} else {
|
|
throw new IllegalArgumentException(
|
|
"Unsupported encryption padding scheme: " + padding);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
static @EncryptionPaddingEnum String fromKeymaster(int padding) {
|
|
switch (padding) {
|
|
case KeymasterDefs.KM_PAD_NONE:
|
|
return NONE;
|
|
case KeymasterDefs.KM_PAD_PKCS7:
|
|
return PKCS7;
|
|
case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
|
|
return RSA_PKCS1;
|
|
case KeymasterDefs.KM_PAD_RSA_OAEP:
|
|
return RSA_OAEP;
|
|
default:
|
|
throw new IllegalArgumentException(
|
|
"Unsupported encryption padding: " + padding);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
static int[] allToKeymaster(@EncryptionPaddingEnum String[] paddings) {
|
|
if ((paddings == null) || (paddings.length == 0)) {
|
|
return EmptyArray.INT;
|
|
}
|
|
int[] result = new int[paddings.length];
|
|
for (int i = 0; i < paddings.length; i++) {
|
|
result[i] = toKeymaster(paddings[i]);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@StringDef({
|
|
SignaturePadding.RSA_PKCS1,
|
|
SignaturePadding.RSA_PSS,
|
|
})
|
|
public @interface SignaturePaddingEnum {}
|
|
|
|
/**
|
|
* Padding schemes for signing/verification.
|
|
*/
|
|
public static abstract class SignaturePadding {
|
|
private SignaturePadding() {}
|
|
|
|
/**
|
|
* RSA PKCS#1 v1.5 padding for signatures.
|
|
*/
|
|
public static final String RSA_PKCS1 = "PKCS1";
|
|
|
|
/**
|
|
* RSA PKCS#1 v2.1 Probabilistic Signature Scheme (PSS) padding.
|
|
*/
|
|
public static final String RSA_PSS = "PSS";
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
static int toKeymaster(@SignaturePaddingEnum String padding) {
|
|
switch (padding.toUpperCase(Locale.US)) {
|
|
case RSA_PKCS1:
|
|
return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN;
|
|
case RSA_PSS:
|
|
return KeymasterDefs.KM_PAD_RSA_PSS;
|
|
default:
|
|
throw new IllegalArgumentException(
|
|
"Unsupported signature padding scheme: " + padding);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
static @SignaturePaddingEnum String fromKeymaster(int padding) {
|
|
switch (padding) {
|
|
case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN:
|
|
return RSA_PKCS1;
|
|
case KeymasterDefs.KM_PAD_RSA_PSS:
|
|
return RSA_PSS;
|
|
default:
|
|
throw new IllegalArgumentException("Unsupported signature padding: " + padding);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
static int[] allToKeymaster(@SignaturePaddingEnum String[] paddings) {
|
|
if ((paddings == null) || (paddings.length == 0)) {
|
|
return EmptyArray.INT;
|
|
}
|
|
int[] result = new int[paddings.length];
|
|
for (int i = 0; i < paddings.length; i++) {
|
|
result[i] = toKeymaster(paddings[i]);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@StringDef({
|
|
Digest.NONE,
|
|
Digest.MD5,
|
|
Digest.SHA1,
|
|
Digest.SHA224,
|
|
Digest.SHA256,
|
|
Digest.SHA384,
|
|
Digest.SHA512,
|
|
})
|
|
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 String NONE = "NONE";
|
|
|
|
/**
|
|
* MD5 digest.
|
|
*/
|
|
public static final String MD5 = "MD5";
|
|
|
|
/**
|
|
* SHA-1 digest.
|
|
*/
|
|
public static final String SHA1 = "SHA-1";
|
|
|
|
/**
|
|
* SHA-2 224 (aka SHA-224) digest.
|
|
*/
|
|
public static final String SHA224 = "SHA-224";
|
|
|
|
/**
|
|
* SHA-2 256 (aka SHA-256) digest.
|
|
*/
|
|
public static final String SHA256 = "SHA-256";
|
|
|
|
/**
|
|
* SHA-2 384 (aka SHA-384) digest.
|
|
*/
|
|
public static final String SHA384 = "SHA-384";
|
|
|
|
/**
|
|
* SHA-2 512 (aka SHA-512) digest.
|
|
*/
|
|
public static final String SHA512 = "SHA-512";
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
static int toKeymaster(@DigestEnum String digest) {
|
|
switch (digest.toUpperCase(Locale.US)) {
|
|
case SHA1:
|
|
return KeymasterDefs.KM_DIGEST_SHA1;
|
|
case SHA224:
|
|
return KeymasterDefs.KM_DIGEST_SHA_2_224;
|
|
case SHA256:
|
|
return KeymasterDefs.KM_DIGEST_SHA_2_256;
|
|
case SHA384:
|
|
return KeymasterDefs.KM_DIGEST_SHA_2_384;
|
|
case SHA512:
|
|
return KeymasterDefs.KM_DIGEST_SHA_2_512;
|
|
case NONE:
|
|
return KeymasterDefs.KM_DIGEST_NONE;
|
|
case MD5:
|
|
return KeymasterDefs.KM_DIGEST_MD5;
|
|
default:
|
|
throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
static @DigestEnum String fromKeymaster(int digest) {
|
|
switch (digest) {
|
|
case KeymasterDefs.KM_DIGEST_NONE:
|
|
return NONE;
|
|
case KeymasterDefs.KM_DIGEST_MD5:
|
|
return MD5;
|
|
case KeymasterDefs.KM_DIGEST_SHA1:
|
|
return SHA1;
|
|
case KeymasterDefs.KM_DIGEST_SHA_2_224:
|
|
return SHA224;
|
|
case KeymasterDefs.KM_DIGEST_SHA_2_256:
|
|
return SHA256;
|
|
case KeymasterDefs.KM_DIGEST_SHA_2_384:
|
|
return SHA384;
|
|
case KeymasterDefs.KM_DIGEST_SHA_2_512:
|
|
return SHA512;
|
|
default:
|
|
throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
static @DigestEnum String[] allFromKeymaster(Collection<Integer> digests) {
|
|
if (digests.isEmpty()) {
|
|
return EmptyArray.STRING;
|
|
}
|
|
String[] result = new String[digests.size()];
|
|
int offset = 0;
|
|
for (int digest : digests) {
|
|
result[offset] = fromKeymaster(digest);
|
|
offset++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
static int[] allToKeymaster(@DigestEnum String[] digests) {
|
|
if ((digests == null) || (digests.length == 0)) {
|
|
return EmptyArray.INT;
|
|
}
|
|
int[] result = new int[digests.length];
|
|
int offset = 0;
|
|
for (@DigestEnum String digest : digests) {
|
|
result[offset] = toKeymaster(digest);
|
|
offset++;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef({Origin.GENERATED, Origin.IMPORTED, Origin.UNKNOWN})
|
|
public @interface OriginEnum {}
|
|
|
|
/**
|
|
* Origin of the key.
|
|
*/
|
|
public static abstract class Origin {
|
|
private Origin() {}
|
|
|
|
/** Key was generated inside AndroidKeyStore. */
|
|
public static final int GENERATED = 1 << 0;
|
|
|
|
/** Key was imported into AndroidKeyStore. */
|
|
public static final int IMPORTED = 1 << 1;
|
|
|
|
/**
|
|
* Origin of the key is unknown. This can occur only for keys backed by an old TEE
|
|
* implementation which does not record origin information.
|
|
*/
|
|
public static final int UNKNOWN = 1 << 2;
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
public static @OriginEnum int fromKeymaster(int origin) {
|
|
switch (origin) {
|
|
case KeymasterDefs.KM_ORIGIN_GENERATED:
|
|
return GENERATED;
|
|
case KeymasterDefs.KM_ORIGIN_IMPORTED:
|
|
return IMPORTED;
|
|
case KeymasterDefs.KM_ORIGIN_UNKNOWN:
|
|
return UNKNOWN;
|
|
default:
|
|
throw new IllegalArgumentException("Unknown origin: " + origin);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static int[] getSetFlags(int flags) {
|
|
if (flags == 0) {
|
|
return EmptyArray.INT;
|
|
}
|
|
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;
|
|
}
|
|
}
|