diff options
Diffstat (limited to 'keystore/java/android/security/KeyStoreKeyProperties.java')
-rw-r--r-- | keystore/java/android/security/KeyStoreKeyProperties.java | 520 |
1 files changed, 519 insertions, 1 deletions
diff --git a/keystore/java/android/security/KeyStoreKeyProperties.java b/keystore/java/android/security/KeyStoreKeyProperties.java index b85ec53..1c3e300 100644 --- a/keystore/java/android/security/KeyStoreKeyProperties.java +++ b/keystore/java/android/security/KeyStoreKeyProperties.java @@ -17,13 +17,23 @@ 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. @@ -37,7 +47,7 @@ public abstract class KeyStoreKeyProperties { public @interface PurposeEnum {} /** - * Purpose of key. + * Purposes of key. */ public static abstract class Purpose { private Purpose() {} @@ -122,6 +132,514 @@ public abstract class KeyStoreKeyProperties { } @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 {} |