1053 lines
36 KiB
Java
1053 lines
36 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.keystore;
|
||
|
|
||
|
import android.annotation.IntDef;
|
||
|
import android.annotation.NonNull;
|
||
|
import android.annotation.Nullable;
|
||
|
import android.annotation.StringDef;
|
||
|
import android.annotation.SystemApi;
|
||
|
import android.os.Process;
|
||
|
import android.security.keymaster.KeymasterDefs;
|
||
|
|
||
|
import libcore.util.EmptyArray;
|
||
|
|
||
|
import java.lang.annotation.Retention;
|
||
|
import java.lang.annotation.RetentionPolicy;
|
||
|
import java.security.spec.AlgorithmParameterSpec;
|
||
|
import java.security.spec.ECParameterSpec;
|
||
|
import java.security.spec.MGF1ParameterSpec;
|
||
|
import java.util.Collection;
|
||
|
import java.util.Locale;
|
||
|
|
||
|
/**
|
||
|
* Properties of <a href="{@docRoot}training/articles/keystore.html">Android Keystore</a> keys.
|
||
|
*/
|
||
|
public abstract class KeyProperties {
|
||
|
private KeyProperties() {}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
@Retention(RetentionPolicy.SOURCE)
|
||
|
@IntDef(flag = true, prefix = { "AUTH_" }, value = {
|
||
|
AUTH_BIOMETRIC_STRONG,
|
||
|
AUTH_DEVICE_CREDENTIAL,
|
||
|
})
|
||
|
public @interface AuthEnum {}
|
||
|
|
||
|
/**
|
||
|
* The non-biometric credential used to secure the device (i.e., PIN, pattern, or password)
|
||
|
*/
|
||
|
public static final int AUTH_DEVICE_CREDENTIAL = 1 << 0;
|
||
|
|
||
|
/**
|
||
|
* Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
|
||
|
* requirements for <strong>Strong</strong>, as defined by the Android CDD.
|
||
|
*/
|
||
|
public static final int AUTH_BIOMETRIC_STRONG = 1 << 1;
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
@Retention(RetentionPolicy.SOURCE)
|
||
|
@IntDef(flag = true, prefix = { "PURPOSE_" }, value = {
|
||
|
PURPOSE_ENCRYPT,
|
||
|
PURPOSE_DECRYPT,
|
||
|
PURPOSE_SIGN,
|
||
|
PURPOSE_VERIFY,
|
||
|
PURPOSE_WRAP_KEY,
|
||
|
PURPOSE_AGREE_KEY,
|
||
|
PURPOSE_ATTEST_KEY,
|
||
|
})
|
||
|
public @interface PurposeEnum {}
|
||
|
|
||
|
/**
|
||
|
* Purpose of key: encryption.
|
||
|
*/
|
||
|
public static final int PURPOSE_ENCRYPT = 1 << 0;
|
||
|
|
||
|
/**
|
||
|
* Purpose of key: decryption.
|
||
|
*/
|
||
|
public static final int PURPOSE_DECRYPT = 1 << 1;
|
||
|
|
||
|
/**
|
||
|
* Purpose of key: signing or generating a Message Authentication Code (MAC).
|
||
|
*/
|
||
|
public static final int PURPOSE_SIGN = 1 << 2;
|
||
|
|
||
|
/**
|
||
|
* Purpose of key: signature or Message Authentication Code (MAC) verification.
|
||
|
*/
|
||
|
public static final int PURPOSE_VERIFY = 1 << 3;
|
||
|
|
||
|
/**
|
||
|
* Purpose of key: wrapping and unwrapping wrapped keys for secure import.
|
||
|
*/
|
||
|
public static final int PURPOSE_WRAP_KEY = 1 << 5;
|
||
|
|
||
|
/**
|
||
|
* Purpose of key: creating a shared ECDH secret through key agreement.
|
||
|
*
|
||
|
* <p>A key having this purpose can be combined with the elliptic curve public key of another
|
||
|
* party to establish a shared secret over an insecure channel. It should be used as a
|
||
|
* parameter to {@link javax.crypto.KeyAgreement#init(java.security.Key)} (a complete example is
|
||
|
* available <a
|
||
|
* href="{@docRoot}reference/android/security/keystore/KeyGenParameterSpec#example:ecdh"
|
||
|
* >here</a>).
|
||
|
* See <a href="https://en.wikipedia.org/wiki/Elliptic-curve_Diffie%E2%80%93Hellman">this
|
||
|
* article</a> for a more detailed explanation.
|
||
|
*/
|
||
|
public static final int PURPOSE_AGREE_KEY = 1 << 6;
|
||
|
|
||
|
/**
|
||
|
* Purpose of key: Signing attestaions. This purpose is incompatible with all others, meaning
|
||
|
* that when generating a key with PURPOSE_ATTEST_KEY, no other purposes may be specified. In
|
||
|
* addition, PURPOSE_ATTEST_KEY may not be specified for imported keys.
|
||
|
*/
|
||
|
public static final int PURPOSE_ATTEST_KEY = 1 << 7;
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
public static abstract class Purpose {
|
||
|
private Purpose() {}
|
||
|
|
||
|
public static int toKeymaster(@PurposeEnum int purpose) {
|
||
|
switch (purpose) {
|
||
|
case PURPOSE_ENCRYPT:
|
||
|
return KeymasterDefs.KM_PURPOSE_ENCRYPT;
|
||
|
case PURPOSE_DECRYPT:
|
||
|
return KeymasterDefs.KM_PURPOSE_DECRYPT;
|
||
|
case PURPOSE_SIGN:
|
||
|
return KeymasterDefs.KM_PURPOSE_SIGN;
|
||
|
case PURPOSE_VERIFY:
|
||
|
return KeymasterDefs.KM_PURPOSE_VERIFY;
|
||
|
case PURPOSE_WRAP_KEY:
|
||
|
return KeymasterDefs.KM_PURPOSE_WRAP;
|
||
|
case PURPOSE_AGREE_KEY:
|
||
|
return KeymasterDefs.KM_PURPOSE_AGREE_KEY;
|
||
|
case PURPOSE_ATTEST_KEY:
|
||
|
return KeymasterDefs.KM_PURPOSE_ATTEST_KEY;
|
||
|
default:
|
||
|
throw new IllegalArgumentException("Unknown purpose: " + purpose);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static @PurposeEnum int fromKeymaster(int purpose) {
|
||
|
switch (purpose) {
|
||
|
case KeymasterDefs.KM_PURPOSE_ENCRYPT:
|
||
|
return PURPOSE_ENCRYPT;
|
||
|
case KeymasterDefs.KM_PURPOSE_DECRYPT:
|
||
|
return PURPOSE_DECRYPT;
|
||
|
case KeymasterDefs.KM_PURPOSE_SIGN:
|
||
|
return PURPOSE_SIGN;
|
||
|
case KeymasterDefs.KM_PURPOSE_VERIFY:
|
||
|
return PURPOSE_VERIFY;
|
||
|
case KeymasterDefs.KM_PURPOSE_WRAP:
|
||
|
return PURPOSE_WRAP_KEY;
|
||
|
case KeymasterDefs.KM_PURPOSE_AGREE_KEY:
|
||
|
return PURPOSE_AGREE_KEY;
|
||
|
case KeymasterDefs.KM_PURPOSE_ATTEST_KEY:
|
||
|
return PURPOSE_ATTEST_KEY;
|
||
|
default:
|
||
|
throw new IllegalArgumentException("Unknown purpose: " + purpose);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
public static @PurposeEnum int allFromKeymaster(@NonNull Collection<Integer> purposes) {
|
||
|
@PurposeEnum int result = 0;
|
||
|
for (int keymasterPurpose : purposes) {
|
||
|
result |= fromKeymaster(keymasterPurpose);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
@Retention(RetentionPolicy.SOURCE)
|
||
|
@StringDef(prefix = { "KEY_" }, value = {
|
||
|
KEY_ALGORITHM_RSA,
|
||
|
KEY_ALGORITHM_EC,
|
||
|
KEY_ALGORITHM_XDH,
|
||
|
KEY_ALGORITHM_AES,
|
||
|
KEY_ALGORITHM_HMAC_SHA1,
|
||
|
KEY_ALGORITHM_HMAC_SHA224,
|
||
|
KEY_ALGORITHM_HMAC_SHA256,
|
||
|
KEY_ALGORITHM_HMAC_SHA384,
|
||
|
KEY_ALGORITHM_HMAC_SHA512,
|
||
|
})
|
||
|
public @interface KeyAlgorithmEnum {}
|
||
|
|
||
|
/** Rivest Shamir Adleman (RSA) key. */
|
||
|
public static final String KEY_ALGORITHM_RSA = "RSA";
|
||
|
|
||
|
/** Elliptic Curve (EC) Cryptography key. */
|
||
|
public static final String KEY_ALGORITHM_EC = "EC";
|
||
|
|
||
|
/** Curve 25519 based Agreement key.
|
||
|
* @hide
|
||
|
*/
|
||
|
public static final String KEY_ALGORITHM_XDH = "XDH";
|
||
|
|
||
|
/** Advanced Encryption Standard (AES) key. */
|
||
|
public static final String KEY_ALGORITHM_AES = "AES";
|
||
|
|
||
|
/**
|
||
|
* Triple Data Encryption Algorithm (3DES) key.
|
||
|
*
|
||
|
* @deprecated Included for interoperability with legacy systems. Prefer {@link
|
||
|
* KeyProperties#KEY_ALGORITHM_AES} for new development.
|
||
|
*/
|
||
|
@Deprecated
|
||
|
public static final String KEY_ALGORITHM_3DES = "DESede";
|
||
|
|
||
|
/** Keyed-Hash Message Authentication Code (HMAC) key using SHA-1 as the hash. */
|
||
|
public static final String KEY_ALGORITHM_HMAC_SHA1 = "HmacSHA1";
|
||
|
|
||
|
/** Keyed-Hash Message Authentication Code (HMAC) key using SHA-224 as the hash. */
|
||
|
public static final String KEY_ALGORITHM_HMAC_SHA224 = "HmacSHA224";
|
||
|
|
||
|
/** Keyed-Hash Message Authentication Code (HMAC) key using SHA-256 as the hash. */
|
||
|
public static final String KEY_ALGORITHM_HMAC_SHA256 = "HmacSHA256";
|
||
|
|
||
|
/** Keyed-Hash Message Authentication Code (HMAC) key using SHA-384 as the hash. */
|
||
|
public static final String KEY_ALGORITHM_HMAC_SHA384 = "HmacSHA384";
|
||
|
|
||
|
/** Keyed-Hash Message Authentication Code (HMAC) key using SHA-512 as the hash. */
|
||
|
public static final String KEY_ALGORITHM_HMAC_SHA512 = "HmacSHA512";
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
public static abstract class KeyAlgorithm {
|
||
|
private KeyAlgorithm() {}
|
||
|
|
||
|
public static int toKeymasterAsymmetricKeyAlgorithm(
|
||
|
@NonNull @KeyAlgorithmEnum String algorithm) {
|
||
|
if (KEY_ALGORITHM_EC.equalsIgnoreCase(algorithm)
|
||
|
|| KEY_ALGORITHM_XDH.equalsIgnoreCase(algorithm)) {
|
||
|
return KeymasterDefs.KM_ALGORITHM_EC;
|
||
|
} else if (KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) {
|
||
|
return KeymasterDefs.KM_ALGORITHM_RSA;
|
||
|
} else {
|
||
|
throw new IllegalArgumentException("Unsupported key algorithm: " + algorithm);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
public static @KeyAlgorithmEnum String fromKeymasterAsymmetricKeyAlgorithm(
|
||
|
int keymasterAlgorithm) {
|
||
|
switch (keymasterAlgorithm) {
|
||
|
case KeymasterDefs.KM_ALGORITHM_EC:
|
||
|
return KEY_ALGORITHM_EC;
|
||
|
case KeymasterDefs.KM_ALGORITHM_RSA:
|
||
|
return KEY_ALGORITHM_RSA;
|
||
|
default:
|
||
|
throw new IllegalArgumentException(
|
||
|
"Unsupported key algorithm: " + keymasterAlgorithm);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static int toKeymasterSecretKeyAlgorithm(
|
||
|
@NonNull @KeyAlgorithmEnum String algorithm) {
|
||
|
if (KEY_ALGORITHM_AES.equalsIgnoreCase(algorithm)) {
|
||
|
return KeymasterDefs.KM_ALGORITHM_AES;
|
||
|
} else if (KEY_ALGORITHM_3DES.equalsIgnoreCase(algorithm)) {
|
||
|
return KeymasterDefs.KM_ALGORITHM_3DES;
|
||
|
} else if (algorithm.toUpperCase(Locale.US).startsWith("HMAC")) {
|
||
|
return KeymasterDefs.KM_ALGORITHM_HMAC;
|
||
|
} else {
|
||
|
throw new IllegalArgumentException(
|
||
|
"Unsupported secret key algorithm: " + algorithm);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
public static @KeyAlgorithmEnum String fromKeymasterSecretKeyAlgorithm(
|
||
|
int keymasterAlgorithm, int keymasterDigest) {
|
||
|
switch (keymasterAlgorithm) {
|
||
|
case KeymasterDefs.KM_ALGORITHM_AES:
|
||
|
return KEY_ALGORITHM_AES;
|
||
|
case KeymasterDefs.KM_ALGORITHM_3DES:
|
||
|
return KEY_ALGORITHM_3DES;
|
||
|
case KeymasterDefs.KM_ALGORITHM_HMAC:
|
||
|
switch (keymasterDigest) {
|
||
|
case KeymasterDefs.KM_DIGEST_SHA1:
|
||
|
return KEY_ALGORITHM_HMAC_SHA1;
|
||
|
case KeymasterDefs.KM_DIGEST_SHA_2_224:
|
||
|
return KEY_ALGORITHM_HMAC_SHA224;
|
||
|
case KeymasterDefs.KM_DIGEST_SHA_2_256:
|
||
|
return KEY_ALGORITHM_HMAC_SHA256;
|
||
|
case KeymasterDefs.KM_DIGEST_SHA_2_384:
|
||
|
return KEY_ALGORITHM_HMAC_SHA384;
|
||
|
case KeymasterDefs.KM_DIGEST_SHA_2_512:
|
||
|
return KEY_ALGORITHM_HMAC_SHA512;
|
||
|
default:
|
||
|
throw new IllegalArgumentException("Unsupported HMAC digest: "
|
||
|
+ Digest.fromKeymaster(keymasterDigest));
|
||
|
}
|
||
|
default:
|
||
|
throw new IllegalArgumentException(
|
||
|
"Unsupported key algorithm: " + keymasterAlgorithm);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*
|
||
|
* @return keymaster digest or {@code -1} if the algorithm does not involve a digest.
|
||
|
*/
|
||
|
public static int toKeymasterDigest(@NonNull @KeyAlgorithmEnum 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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
@Retention(RetentionPolicy.SOURCE)
|
||
|
@StringDef(prefix = { "BLOCK_MODE_" }, value = {
|
||
|
BLOCK_MODE_ECB,
|
||
|
BLOCK_MODE_CBC,
|
||
|
BLOCK_MODE_CTR,
|
||
|
BLOCK_MODE_GCM,
|
||
|
})
|
||
|
public @interface BlockModeEnum {}
|
||
|
|
||
|
/** Electronic Codebook (ECB) block mode. */
|
||
|
public static final String BLOCK_MODE_ECB = "ECB";
|
||
|
|
||
|
/** Cipher Block Chaining (CBC) block mode. */
|
||
|
public static final String BLOCK_MODE_CBC = "CBC";
|
||
|
|
||
|
/** Counter (CTR) block mode. */
|
||
|
public static final String BLOCK_MODE_CTR = "CTR";
|
||
|
|
||
|
/** Galois/Counter Mode (GCM) block mode. */
|
||
|
public static final String BLOCK_MODE_GCM = "GCM";
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
public static abstract class BlockMode {
|
||
|
private BlockMode() {}
|
||
|
|
||
|
public static int toKeymaster(@NonNull @BlockModeEnum String blockMode) {
|
||
|
if (BLOCK_MODE_ECB.equalsIgnoreCase(blockMode)) {
|
||
|
return KeymasterDefs.KM_MODE_ECB;
|
||
|
} else if (BLOCK_MODE_CBC.equalsIgnoreCase(blockMode)) {
|
||
|
return KeymasterDefs.KM_MODE_CBC;
|
||
|
} else if (BLOCK_MODE_CTR.equalsIgnoreCase(blockMode)) {
|
||
|
return KeymasterDefs.KM_MODE_CTR;
|
||
|
} else if (BLOCK_MODE_GCM.equalsIgnoreCase(blockMode)) {
|
||
|
return KeymasterDefs.KM_MODE_GCM;
|
||
|
} else {
|
||
|
throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
public static @BlockModeEnum String fromKeymaster(int blockMode) {
|
||
|
switch (blockMode) {
|
||
|
case KeymasterDefs.KM_MODE_ECB:
|
||
|
return BLOCK_MODE_ECB;
|
||
|
case KeymasterDefs.KM_MODE_CBC:
|
||
|
return BLOCK_MODE_CBC;
|
||
|
case KeymasterDefs.KM_MODE_CTR:
|
||
|
return BLOCK_MODE_CTR;
|
||
|
case KeymasterDefs.KM_MODE_GCM:
|
||
|
return BLOCK_MODE_GCM;
|
||
|
default:
|
||
|
throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
public static @BlockModeEnum String[] allFromKeymaster(
|
||
|
@NonNull 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;
|
||
|
}
|
||
|
|
||
|
public static int[] allToKeymaster(@Nullable @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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
@Retention(RetentionPolicy.SOURCE)
|
||
|
@StringDef(prefix = { "ENCRYPTION_PADDING_" }, value = {
|
||
|
ENCRYPTION_PADDING_NONE,
|
||
|
ENCRYPTION_PADDING_PKCS7,
|
||
|
ENCRYPTION_PADDING_RSA_PKCS1,
|
||
|
ENCRYPTION_PADDING_RSA_OAEP,
|
||
|
})
|
||
|
public @interface EncryptionPaddingEnum {}
|
||
|
|
||
|
/**
|
||
|
* No encryption padding.
|
||
|
*/
|
||
|
public static final String ENCRYPTION_PADDING_NONE = "NoPadding";
|
||
|
|
||
|
/**
|
||
|
* PKCS#7 encryption padding scheme.
|
||
|
*/
|
||
|
public static final String ENCRYPTION_PADDING_PKCS7 = "PKCS7Padding";
|
||
|
|
||
|
/**
|
||
|
* RSA PKCS#1 v1.5 padding scheme for encryption.
|
||
|
*/
|
||
|
public static final String ENCRYPTION_PADDING_RSA_PKCS1 = "PKCS1Padding";
|
||
|
|
||
|
/**
|
||
|
* RSA Optimal Asymmetric Encryption Padding (OAEP) scheme.
|
||
|
*/
|
||
|
public static final String ENCRYPTION_PADDING_RSA_OAEP = "OAEPPadding";
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
public static abstract class EncryptionPadding {
|
||
|
private EncryptionPadding() {}
|
||
|
|
||
|
public static int toKeymaster(@NonNull @EncryptionPaddingEnum String padding) {
|
||
|
if (ENCRYPTION_PADDING_NONE.equalsIgnoreCase(padding)) {
|
||
|
return KeymasterDefs.KM_PAD_NONE;
|
||
|
} else if (ENCRYPTION_PADDING_PKCS7.equalsIgnoreCase(padding)) {
|
||
|
return KeymasterDefs.KM_PAD_PKCS7;
|
||
|
} else if (ENCRYPTION_PADDING_RSA_PKCS1.equalsIgnoreCase(padding)) {
|
||
|
return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT;
|
||
|
} else if (ENCRYPTION_PADDING_RSA_OAEP.equalsIgnoreCase(padding)) {
|
||
|
return KeymasterDefs.KM_PAD_RSA_OAEP;
|
||
|
} else {
|
||
|
throw new IllegalArgumentException(
|
||
|
"Unsupported encryption padding scheme: " + padding);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
public static @EncryptionPaddingEnum String fromKeymaster(int padding) {
|
||
|
switch (padding) {
|
||
|
case KeymasterDefs.KM_PAD_NONE:
|
||
|
return ENCRYPTION_PADDING_NONE;
|
||
|
case KeymasterDefs.KM_PAD_PKCS7:
|
||
|
return ENCRYPTION_PADDING_PKCS7;
|
||
|
case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
|
||
|
return ENCRYPTION_PADDING_RSA_PKCS1;
|
||
|
case KeymasterDefs.KM_PAD_RSA_OAEP:
|
||
|
return ENCRYPTION_PADDING_RSA_OAEP;
|
||
|
default:
|
||
|
throw new IllegalArgumentException(
|
||
|
"Unsupported encryption padding: " + padding);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
public static int[] allToKeymaster(@Nullable @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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
@Retention(RetentionPolicy.SOURCE)
|
||
|
@StringDef(prefix = { "SIGNATURE_PADDING_" }, value = {
|
||
|
SIGNATURE_PADDING_RSA_PKCS1,
|
||
|
SIGNATURE_PADDING_RSA_PSS,
|
||
|
})
|
||
|
public @interface SignaturePaddingEnum {}
|
||
|
|
||
|
/**
|
||
|
* RSA PKCS#1 v1.5 padding for signatures.
|
||
|
*/
|
||
|
public static final String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
|
||
|
|
||
|
/**
|
||
|
* RSA PKCS#1 v2.1 Probabilistic Signature Scheme (PSS) padding.
|
||
|
*/
|
||
|
public static final String SIGNATURE_PADDING_RSA_PSS = "PSS";
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
public abstract static class SignaturePadding {
|
||
|
private SignaturePadding() {}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
public static int toKeymaster(@NonNull @SignaturePaddingEnum String padding) {
|
||
|
switch (padding.toUpperCase(Locale.US)) {
|
||
|
case SIGNATURE_PADDING_RSA_PKCS1:
|
||
|
return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN;
|
||
|
case SIGNATURE_PADDING_RSA_PSS:
|
||
|
return KeymasterDefs.KM_PAD_RSA_PSS;
|
||
|
default:
|
||
|
throw new IllegalArgumentException(
|
||
|
"Unsupported signature padding scheme: " + padding);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
public static @SignaturePaddingEnum String fromKeymaster(int padding) {
|
||
|
switch (padding) {
|
||
|
case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN:
|
||
|
return SIGNATURE_PADDING_RSA_PKCS1;
|
||
|
case KeymasterDefs.KM_PAD_RSA_PSS:
|
||
|
return SIGNATURE_PADDING_RSA_PSS;
|
||
|
default:
|
||
|
throw new IllegalArgumentException("Unsupported signature padding: " + padding);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
public static int[] allToKeymaster(@Nullable @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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
@Retention(RetentionPolicy.SOURCE)
|
||
|
@StringDef(prefix = { "DIGEST_" }, value = {
|
||
|
DIGEST_NONE,
|
||
|
DIGEST_MD5,
|
||
|
DIGEST_SHA1,
|
||
|
DIGEST_SHA224,
|
||
|
DIGEST_SHA256,
|
||
|
DIGEST_SHA384,
|
||
|
DIGEST_SHA512,
|
||
|
})
|
||
|
public @interface DigestEnum {}
|
||
|
|
||
|
/**
|
||
|
* No digest: sign/authenticate the raw message.
|
||
|
*/
|
||
|
public static final String DIGEST_NONE = "NONE";
|
||
|
|
||
|
/**
|
||
|
* MD5 digest.
|
||
|
*/
|
||
|
public static final String DIGEST_MD5 = "MD5";
|
||
|
|
||
|
/**
|
||
|
* SHA-1 digest.
|
||
|
*/
|
||
|
public static final String DIGEST_SHA1 = "SHA-1";
|
||
|
|
||
|
/**
|
||
|
* SHA-2 224 (aka SHA-224) digest.
|
||
|
*/
|
||
|
public static final String DIGEST_SHA224 = "SHA-224";
|
||
|
|
||
|
/**
|
||
|
* SHA-2 256 (aka SHA-256) digest.
|
||
|
*/
|
||
|
public static final String DIGEST_SHA256 = "SHA-256";
|
||
|
|
||
|
/**
|
||
|
* SHA-2 384 (aka SHA-384) digest.
|
||
|
*/
|
||
|
public static final String DIGEST_SHA384 = "SHA-384";
|
||
|
|
||
|
/**
|
||
|
* SHA-2 512 (aka SHA-512) digest.
|
||
|
*/
|
||
|
public static final String DIGEST_SHA512 = "SHA-512";
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
public static abstract class Digest {
|
||
|
private Digest() {}
|
||
|
|
||
|
public static int toKeymaster(@NonNull @DigestEnum String digest) {
|
||
|
switch (digest.toUpperCase(Locale.US)) {
|
||
|
case DIGEST_SHA1:
|
||
|
return KeymasterDefs.KM_DIGEST_SHA1;
|
||
|
case DIGEST_SHA224:
|
||
|
return KeymasterDefs.KM_DIGEST_SHA_2_224;
|
||
|
case DIGEST_SHA256:
|
||
|
return KeymasterDefs.KM_DIGEST_SHA_2_256;
|
||
|
case DIGEST_SHA384:
|
||
|
return KeymasterDefs.KM_DIGEST_SHA_2_384;
|
||
|
case DIGEST_SHA512:
|
||
|
return KeymasterDefs.KM_DIGEST_SHA_2_512;
|
||
|
case DIGEST_NONE:
|
||
|
return KeymasterDefs.KM_DIGEST_NONE;
|
||
|
case DIGEST_MD5:
|
||
|
return KeymasterDefs.KM_DIGEST_MD5;
|
||
|
default:
|
||
|
throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
public static @DigestEnum String fromKeymaster(int digest) {
|
||
|
switch (digest) {
|
||
|
case KeymasterDefs.KM_DIGEST_NONE:
|
||
|
return DIGEST_NONE;
|
||
|
case KeymasterDefs.KM_DIGEST_MD5:
|
||
|
return DIGEST_MD5;
|
||
|
case KeymasterDefs.KM_DIGEST_SHA1:
|
||
|
return DIGEST_SHA1;
|
||
|
case KeymasterDefs.KM_DIGEST_SHA_2_224:
|
||
|
return DIGEST_SHA224;
|
||
|
case KeymasterDefs.KM_DIGEST_SHA_2_256:
|
||
|
return DIGEST_SHA256;
|
||
|
case KeymasterDefs.KM_DIGEST_SHA_2_384:
|
||
|
return DIGEST_SHA384;
|
||
|
case KeymasterDefs.KM_DIGEST_SHA_2_512:
|
||
|
return DIGEST_SHA512;
|
||
|
default:
|
||
|
throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull public static @DigestEnum
|
||
|
AlgorithmParameterSpec fromKeymasterToMGF1ParameterSpec(int digest) {
|
||
|
switch (digest) {
|
||
|
default:
|
||
|
case KeymasterDefs.KM_DIGEST_SHA1:
|
||
|
return MGF1ParameterSpec.SHA1;
|
||
|
case KeymasterDefs.KM_DIGEST_SHA_2_224:
|
||
|
return MGF1ParameterSpec.SHA224;
|
||
|
case KeymasterDefs.KM_DIGEST_SHA_2_256:
|
||
|
return MGF1ParameterSpec.SHA256;
|
||
|
case KeymasterDefs.KM_DIGEST_SHA_2_384:
|
||
|
return MGF1ParameterSpec.SHA384;
|
||
|
case KeymasterDefs.KM_DIGEST_SHA_2_512:
|
||
|
return MGF1ParameterSpec.SHA512;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
public static @DigestEnum String fromKeymasterToSignatureAlgorithmDigest(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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
public static @DigestEnum String[] allFromKeymaster(@NonNull 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;
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
public static int[] allToKeymaster(@Nullable @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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
@Retention(RetentionPolicy.SOURCE)
|
||
|
@IntDef(prefix = { "ORIGIN_" }, value = {
|
||
|
ORIGIN_GENERATED,
|
||
|
ORIGIN_IMPORTED,
|
||
|
ORIGIN_UNKNOWN,
|
||
|
})
|
||
|
|
||
|
public @interface OriginEnum {}
|
||
|
|
||
|
/** Key was generated inside AndroidKeyStore. */
|
||
|
public static final int ORIGIN_GENERATED = 1 << 0;
|
||
|
|
||
|
/** Key was imported into AndroidKeyStore. */
|
||
|
public static final int ORIGIN_IMPORTED = 1 << 1;
|
||
|
|
||
|
/**
|
||
|
* Origin of the key is unknown. This can occur only for keys backed by an old TEE-backed
|
||
|
* implementation which does not record origin information.
|
||
|
*/
|
||
|
public static final int ORIGIN_UNKNOWN = 1 << 2;
|
||
|
|
||
|
/**
|
||
|
* Key was imported into the AndroidKeyStore in an encrypted wrapper. Unlike imported keys,
|
||
|
* securely imported keys can be imported without appearing as plaintext in the device's host
|
||
|
* memory.
|
||
|
*/
|
||
|
public static final int ORIGIN_SECURELY_IMPORTED = 1 << 3;
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
public static abstract class Origin {
|
||
|
private Origin() {}
|
||
|
|
||
|
public static @OriginEnum int fromKeymaster(int origin) {
|
||
|
switch (origin) {
|
||
|
case KeymasterDefs.KM_ORIGIN_GENERATED:
|
||
|
return ORIGIN_GENERATED;
|
||
|
case KeymasterDefs.KM_ORIGIN_IMPORTED:
|
||
|
return ORIGIN_IMPORTED;
|
||
|
case KeymasterDefs.KM_ORIGIN_UNKNOWN:
|
||
|
return ORIGIN_UNKNOWN;
|
||
|
case KeymasterDefs.KM_ORIGIN_SECURELY_IMPORTED:
|
||
|
return ORIGIN_SECURELY_IMPORTED;
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
@Retention(RetentionPolicy.SOURCE)
|
||
|
@IntDef(prefix = { "SECURITY_LEVEL_" }, value = {
|
||
|
SECURITY_LEVEL_UNKNOWN,
|
||
|
SECURITY_LEVEL_UNKNOWN_SECURE,
|
||
|
SECURITY_LEVEL_SOFTWARE,
|
||
|
SECURITY_LEVEL_TRUSTED_ENVIRONMENT,
|
||
|
SECURITY_LEVEL_STRONGBOX,
|
||
|
})
|
||
|
public @interface SecurityLevelEnum {}
|
||
|
|
||
|
/**
|
||
|
* This security level indicates that no assumptions can be made about the security level of the
|
||
|
* respective key.
|
||
|
*/
|
||
|
public static final int SECURITY_LEVEL_UNKNOWN = -2;
|
||
|
/**
|
||
|
* This security level indicates that due to the target API level of the caller no exact
|
||
|
* statement can be made about the security level of the key, however, the security level
|
||
|
* can be considered is at least equivalent to {@link #SECURITY_LEVEL_TRUSTED_ENVIRONMENT}.
|
||
|
*/
|
||
|
public static final int SECURITY_LEVEL_UNKNOWN_SECURE = -1;
|
||
|
|
||
|
/** Indicates enforcement by system software. */
|
||
|
public static final int SECURITY_LEVEL_SOFTWARE = 0;
|
||
|
|
||
|
/** Indicates enforcement by a trusted execution environment. */
|
||
|
public static final int SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 1;
|
||
|
|
||
|
/**
|
||
|
* Indicates enforcement by environment meeting the Strongbox security profile,
|
||
|
* such as a secure element.
|
||
|
*/
|
||
|
public static final int SECURITY_LEVEL_STRONGBOX = 2;
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
public abstract static class SecurityLevel {
|
||
|
private SecurityLevel() {}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
public static int toKeymaster(int securityLevel) {
|
||
|
switch (securityLevel) {
|
||
|
case SECURITY_LEVEL_SOFTWARE:
|
||
|
return KeymasterDefs.KM_SECURITY_LEVEL_SOFTWARE;
|
||
|
case SECURITY_LEVEL_TRUSTED_ENVIRONMENT:
|
||
|
return KeymasterDefs.KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT;
|
||
|
case SECURITY_LEVEL_STRONGBOX:
|
||
|
return KeymasterDefs.KM_SECURITY_LEVEL_STRONGBOX;
|
||
|
default:
|
||
|
throw new IllegalArgumentException("Unsupported security level: "
|
||
|
+ securityLevel);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull
|
||
|
public static int fromKeymaster(int securityLevel) {
|
||
|
switch (securityLevel) {
|
||
|
case KeymasterDefs.KM_SECURITY_LEVEL_SOFTWARE:
|
||
|
return SECURITY_LEVEL_SOFTWARE;
|
||
|
case KeymasterDefs.KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT:
|
||
|
return SECURITY_LEVEL_TRUSTED_ENVIRONMENT;
|
||
|
case KeymasterDefs.KM_SECURITY_LEVEL_STRONGBOX:
|
||
|
return SECURITY_LEVEL_STRONGBOX;
|
||
|
default:
|
||
|
throw new IllegalArgumentException("Unsupported security level: "
|
||
|
+ securityLevel);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
public abstract static class EcCurve {
|
||
|
private EcCurve() {}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
public static int toKeymasterCurve(ECParameterSpec spec) {
|
||
|
int keySize = spec.getCurve().getField().getFieldSize();
|
||
|
switch (keySize) {
|
||
|
case 224:
|
||
|
return android.hardware.security.keymint.EcCurve.P_224;
|
||
|
case 256:
|
||
|
return android.hardware.security.keymint.EcCurve.P_256;
|
||
|
case 384:
|
||
|
return android.hardware.security.keymint.EcCurve.P_384;
|
||
|
case 521:
|
||
|
return android.hardware.security.keymint.EcCurve.P_521;
|
||
|
default:
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
public static int fromKeymasterCurve(int ecCurve) {
|
||
|
switch (ecCurve) {
|
||
|
case android.hardware.security.keymint.EcCurve.P_224:
|
||
|
return 224;
|
||
|
case android.hardware.security.keymint.EcCurve.P_256:
|
||
|
case android.hardware.security.keymint.EcCurve.CURVE_25519:
|
||
|
return 256;
|
||
|
case android.hardware.security.keymint.EcCurve.P_384:
|
||
|
return 384;
|
||
|
case android.hardware.security.keymint.EcCurve.P_521:
|
||
|
return 521;
|
||
|
default:
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Namespaces provide system developers and vendors with a way to use keystore without
|
||
|
* requiring an applications uid. Namespaces can be configured using SEPolicy.
|
||
|
* See <a href="https://source.android.com/security/keystore#access-control">
|
||
|
* Keystore 2.0 access-control</a>
|
||
|
* {@See KeyGenParameterSpec.Builder#setNamespace}
|
||
|
* {@See android.security.keystore2.AndroidKeyStoreLoadStoreParameter}
|
||
|
* @hide
|
||
|
*/
|
||
|
@Retention(RetentionPolicy.SOURCE)
|
||
|
@IntDef(prefix = { "NAMESPACE_" }, value = {
|
||
|
NAMESPACE_APPLICATION,
|
||
|
NAMESPACE_WIFI,
|
||
|
NAMESPACE_LOCKSETTINGS,
|
||
|
})
|
||
|
public @interface Namespace {}
|
||
|
|
||
|
/**
|
||
|
* This value indicates the implicit keystore namespace of the calling application.
|
||
|
* It is used by default. Only select system components can choose a different namespace
|
||
|
* which it must be configured in SEPolicy.
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public static final int NAMESPACE_APPLICATION = -1;
|
||
|
|
||
|
/**
|
||
|
* The namespace identifier for the WIFI Keystore namespace.
|
||
|
* This must be kept in sync with system/sepolicy/private/keystore2_key_contexts
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public static final int NAMESPACE_WIFI = 102;
|
||
|
|
||
|
/**
|
||
|
* The namespace identifier for the LOCKSETTINGS Keystore namespace.
|
||
|
* This must be kept in sync with system/sepolicy/private/keystore2_key_contexts
|
||
|
* @hide
|
||
|
*/
|
||
|
public static final int NAMESPACE_LOCKSETTINGS = 103;
|
||
|
|
||
|
/**
|
||
|
* The legacy UID that corresponds to {@link #NAMESPACE_APPLICATION}.
|
||
|
* In new code, prefer to work with Keystore namespaces directly.
|
||
|
* @hide
|
||
|
*/
|
||
|
public static final int UID_SELF = -1;
|
||
|
|
||
|
/**
|
||
|
* For legacy support, translate namespaces into known UIDs.
|
||
|
* @hide
|
||
|
*/
|
||
|
public static int namespaceToLegacyUid(@Namespace int namespace) {
|
||
|
switch (namespace) {
|
||
|
case NAMESPACE_APPLICATION:
|
||
|
return UID_SELF;
|
||
|
case NAMESPACE_WIFI:
|
||
|
return Process.WIFI_UID;
|
||
|
default:
|
||
|
throw new IllegalArgumentException("No UID corresponding to namespace "
|
||
|
+ namespace);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* For legacy support, translate namespaces into known UIDs.
|
||
|
* @hide
|
||
|
*/
|
||
|
public static @Namespace int legacyUidToNamespace(int uid) {
|
||
|
switch (uid) {
|
||
|
case UID_SELF:
|
||
|
return NAMESPACE_APPLICATION;
|
||
|
case Process.WIFI_UID:
|
||
|
return NAMESPACE_WIFI;
|
||
|
default:
|
||
|
throw new IllegalArgumentException("No namespace corresponding to uid "
|
||
|
+ uid);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This value indicates that there is no restriction on the number of times the key can be used.
|
||
|
*/
|
||
|
public static final int UNRESTRICTED_USAGE_COUNT = -1;
|
||
|
}
|