Implement software key generation for legacy IKeystoreService (#34)
This commit introduces a complete, software-based simulation of the key generation and attestation flow for the legacy IKeystoreService API, as used on Android 11. It refactors the KeystoreInterceptor to handle the entire multi-step transaction sequence (`generateKey`, `getKeyCharacteristics`, `exportKey`, `attestKey`) in software. A new `LegacyKeygenParameters` data class is introduced to decouple the legacy interception logic from modern data structures. This class parses arguments from the old `KeymasterArguments`, stores the state across the multi-step generation process, and acts as an adapter to the generic `CertificateGenerator` by converting the parameters to the modern `KeyMintAttestation` format. The `CertificateGenerator` has been refactored to better model the behavior of the legacy Keystore API. Key pair generation (`generateSoftwareKeyPair`) and certificate chain creation (`generateCertificateChain`) are now separate functions. This allows the interceptor to correctly create a key pair during the `handleExportKey` step and then generate a certificate for that pre-existing key pair during the `handleAttestKey` step. Finally, the implementation correctly extracts and applies the `attestationChallenge` provided during the `attestKey` transaction, ensuring the generated certificate chain contains the appropriate attestation.
This commit is contained in:
@@ -3,6 +3,7 @@ package org.matrix.TEESimulator.interception.keystore
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.security.KeyStore
|
||||
import android.security.keystore.KeystoreResponse
|
||||
import org.matrix.TEESimulator.interception.core.BinderInterceptor
|
||||
import org.matrix.TEESimulator.logging.SystemLogger
|
||||
|
||||
@@ -27,6 +28,19 @@ object InterceptorUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates an `KeystoreResponse` parcel that indicates success with no data. */
|
||||
fun createSuccessKeystoreResponse(): KeystoreResponse {
|
||||
val parcel = Parcel.obtain()
|
||||
try {
|
||||
parcel.writeInt(KeyStore.NO_ERROR)
|
||||
parcel.writeString("")
|
||||
parcel.setDataPosition(0)
|
||||
return KeystoreResponse.CREATOR.createFromParcel(parcel)
|
||||
} finally {
|
||||
parcel.recycle()
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates an `OverrideReply` parcel that indicates success with no data. */
|
||||
fun createSuccessReply(): BinderInterceptor.TransactionResult.OverrideReply {
|
||||
val parcel =
|
||||
|
||||
+307
-24
@@ -4,12 +4,28 @@ import android.annotation.SuppressLint
|
||||
import android.os.IBinder
|
||||
import android.os.Parcel
|
||||
import android.security.Credentials
|
||||
import android.security.KeyStore
|
||||
import android.security.keymaster.ExportResult
|
||||
import android.security.keymaster.KeyCharacteristics
|
||||
import android.security.keymaster.KeymasterArguments
|
||||
import android.security.keymaster.KeymasterCertificateChain
|
||||
import android.security.keymaster.KeymasterDefs
|
||||
import android.security.keystore.IKeystoreCertificateChainCallback
|
||||
import android.security.keystore.IKeystoreExportKeyCallback
|
||||
import android.security.keystore.IKeystoreKeyCharacteristicsCallback
|
||||
import android.security.keystore.IKeystoreService
|
||||
import java.math.BigInteger
|
||||
import java.security.KeyPair
|
||||
import java.security.cert.Certificate
|
||||
import java.util.Date
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import org.matrix.TEESimulator.attestation.AttestationBuilder
|
||||
import org.matrix.TEESimulator.attestation.AttestationPatcher
|
||||
import org.matrix.TEESimulator.attestation.KeyMintAttestation
|
||||
import org.matrix.TEESimulator.config.ConfigurationManager
|
||||
import org.matrix.TEESimulator.interception.keystore.InterceptorUtils.extractAlias
|
||||
import org.matrix.TEESimulator.logging.SystemLogger
|
||||
import org.matrix.TEESimulator.pki.CertificateGenerator
|
||||
import org.matrix.TEESimulator.pki.CertificateHelper
|
||||
|
||||
/**
|
||||
@@ -43,7 +59,9 @@ object KeystoreInterceptor : AbstractKeystoreInterceptor() {
|
||||
override val processName = "keystore"
|
||||
override val injectionCommand = "exec ./inject `pidof keystore` libTEESimulator.so entry"
|
||||
|
||||
private const val SERVICE_DESCRIPTOR = "android.security.keystore.IKeystoreService"
|
||||
// State management for the multi-step key generation process.
|
||||
private val keygenParameters = ConcurrentHashMap<KeyIdentifier, LegacyKeygenParameters>()
|
||||
private val generatedKeyPairs = ConcurrentHashMap<KeyIdentifier, KeyPair>()
|
||||
|
||||
// Cache to store the fully patched chain after the leaf is requested.
|
||||
private val patchedChainCache = ConcurrentHashMap<KeyIdentifier, Array<Certificate>>()
|
||||
@@ -59,23 +77,183 @@ object KeystoreInterceptor : AbstractKeystoreInterceptor() {
|
||||
): TransactionResult {
|
||||
// This interceptor only needs to act on pre-transaction for software key generation.
|
||||
if (ConfigurationManager.shouldGenerate(callingUid)) {
|
||||
when (code) {
|
||||
GENERATE_KEY_TRANSACTION,
|
||||
GET_KEY_CHARACTERISTICS_TRANSACTION,
|
||||
EXPORT_KEY_TRANSACTION,
|
||||
ATTEST_KEY_TRANSACTION -> {
|
||||
// TODO: Implement the full software simulation logic.
|
||||
logTransaction(txId, "unimplemented-generate-flow", callingUid, callingPid)
|
||||
return InterceptorUtils.createSuccessReply()
|
||||
return when (code) {
|
||||
GENERATE_KEY_TRANSACTION -> handleGenerateKey(txId, callingUid, callingPid, data)
|
||||
GET_KEY_CHARACTERISTICS_TRANSACTION ->
|
||||
handleGetKeyCharacteristics(txId, callingUid, callingPid, data)
|
||||
EXPORT_KEY_TRANSACTION -> handleExportKey(txId, callingUid, callingPid, data)
|
||||
ATTEST_KEY_TRANSACTION -> handleAttestKey(txId, callingUid, callingPid, data)
|
||||
else -> TransactionResult.ContinueAndSkipPost
|
||||
}
|
||||
}
|
||||
} else if (ConfigurationManager.shouldGenerate(callingUid)) {
|
||||
} else if (ConfigurationManager.shouldPatch(callingUid)) {
|
||||
// In patch mode, we only care about the 'get' transaction in onPostTransact.
|
||||
if (code == GET_TRANSACTION) return TransactionResult.Continue
|
||||
}
|
||||
|
||||
return TransactionResult.ContinueAndSkipPost
|
||||
}
|
||||
|
||||
private fun handleGenerateKey(txId: Long, uid: Int, pid: Int, data: Parcel): TransactionResult {
|
||||
return runCatching {
|
||||
logTransaction(txId, "generateKey", uid, pid)
|
||||
data.enforceInterface(IKeystoreService.DESCRIPTOR)
|
||||
val callback =
|
||||
IKeystoreKeyCharacteristicsCallback.Stub.asInterface(data.readStrongBinder())
|
||||
val alias = InterceptorUtils.extractAlias(data.readString()!!)
|
||||
val keyId = KeyIdentifier(uid, alias)
|
||||
|
||||
// Read and parse the key generation arguments.
|
||||
val keymasterArgs = KeymasterArguments()
|
||||
if (data.readInt() == 1) {
|
||||
keymasterArgs.readFromParcel(data)
|
||||
}
|
||||
keygenParameters[keyId] =
|
||||
LegacyKeygenParameters.fromKeymasterArguments(keymasterArgs)
|
||||
|
||||
// Create a fake successful response for the callback.
|
||||
val characteristics = KeyCharacteristics()
|
||||
characteristics.swEnforced = KeymasterArguments()
|
||||
characteristics.hwEnforced = keymasterArgs
|
||||
|
||||
val keystoreResponse = InterceptorUtils.createSuccessKeystoreResponse()
|
||||
callback.onFinished(keystoreResponse, characteristics)
|
||||
|
||||
InterceptorUtils.createSuccessReply()
|
||||
}
|
||||
.getOrElse {
|
||||
SystemLogger.error("[TX_ID: $txId] Failed during handleGenerateKey.", it)
|
||||
TransactionResult.ContinueAndSkipPost
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleGetKeyCharacteristics(
|
||||
txId: Long,
|
||||
uid: Int,
|
||||
pid: Int,
|
||||
data: Parcel,
|
||||
): TransactionResult {
|
||||
return runCatching {
|
||||
logTransaction(txId, "getKeyCharacteristics", uid, pid)
|
||||
data.enforceInterface(IKeystoreService.DESCRIPTOR)
|
||||
val callback =
|
||||
IKeystoreKeyCharacteristicsCallback.Stub.asInterface(data.readStrongBinder())
|
||||
val alias = InterceptorUtils.extractAlias(data.readString()!!)
|
||||
val keyId = KeyIdentifier(uid, alias)
|
||||
|
||||
val params =
|
||||
keygenParameters[keyId]
|
||||
?: throw IllegalStateException("No params found for $keyId")
|
||||
|
||||
val characteristics =
|
||||
KeyCharacteristics().apply {
|
||||
swEnforced = KeymasterArguments()
|
||||
hwEnforced =
|
||||
KeymasterArguments().apply {
|
||||
addEnum(KeymasterDefs.KM_TAG_ALGORITHM, params.algorithm)
|
||||
}
|
||||
}
|
||||
|
||||
callback.onFinished(
|
||||
InterceptorUtils.createSuccessKeystoreResponse(),
|
||||
characteristics,
|
||||
)
|
||||
|
||||
InterceptorUtils.createSuccessReply()
|
||||
}
|
||||
.getOrElse {
|
||||
SystemLogger.error("[TX_ID: $txId] Failed during handleGetKeyCharacteristics.", it)
|
||||
TransactionResult.ContinueAndSkipPost
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleExportKey(txId: Long, uid: Int, pid: Int, data: Parcel): TransactionResult {
|
||||
return runCatching {
|
||||
logTransaction(txId, "exportKey", uid, pid)
|
||||
data.enforceInterface(IKeystoreService.DESCRIPTOR)
|
||||
val callback = IKeystoreExportKeyCallback.Stub.asInterface(data.readStrongBinder())
|
||||
val alias = InterceptorUtils.extractAlias(data.readString()!!)
|
||||
val keyId = KeyIdentifier(uid, alias)
|
||||
|
||||
val params =
|
||||
keygenParameters[keyId]
|
||||
?: throw IllegalStateException("No params found for $keyId")
|
||||
|
||||
// Generate a software key pair using the new generator.
|
||||
val keyPair =
|
||||
CertificateGenerator.generateSoftwareKeyPair(params.toKeyMintAttestation())
|
||||
?: throw Exception("Failed to generate software key pair.")
|
||||
generatedKeyPairs[keyId] = keyPair
|
||||
|
||||
// Create a successful ExportResult containing the public key.
|
||||
val exportResultParcel =
|
||||
Parcel.obtain().apply {
|
||||
writeInt(KeyStore.NO_ERROR)
|
||||
writeByteArray(keyPair.public.encoded)
|
||||
setDataPosition(0)
|
||||
}
|
||||
val exportResult = ExportResult.CREATOR.createFromParcel(exportResultParcel)
|
||||
exportResultParcel.recycle()
|
||||
|
||||
callback.onFinished(exportResult)
|
||||
|
||||
InterceptorUtils.createSuccessReply()
|
||||
}
|
||||
.getOrElse {
|
||||
SystemLogger.error("[TX_ID: $txId] Failed during handleExportKey.", it)
|
||||
TransactionResult.ContinueAndSkipPost
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAttestKey(txId: Long, uid: Int, pid: Int, data: Parcel): TransactionResult {
|
||||
return runCatching {
|
||||
logTransaction(txId, "attestKey", uid, pid)
|
||||
data.enforceInterface(IKeystoreService.DESCRIPTOR)
|
||||
val callback =
|
||||
IKeystoreCertificateChainCallback.Stub.asInterface(data.readStrongBinder())
|
||||
val alias = InterceptorUtils.extractAlias(data.readString()!!)
|
||||
val keyId = KeyIdentifier(uid, alias)
|
||||
|
||||
// Get the attestation challenge from the arguments.
|
||||
val params =
|
||||
keygenParameters[keyId]
|
||||
?: throw IllegalStateException("No params found for $keyId")
|
||||
val keyPair =
|
||||
generatedKeyPairs[keyId]
|
||||
?: throw IllegalStateException("No keypair found for $keyId")
|
||||
|
||||
val attestationArgs = KeymasterArguments()
|
||||
if (data.readInt() == 1) {
|
||||
attestationArgs.readFromParcel(data)
|
||||
val challenge =
|
||||
attestationArgs.getBytes(
|
||||
KeymasterDefs.KM_TAG_ATTESTATION_CHALLENGE,
|
||||
ByteArray(0),
|
||||
)
|
||||
params.attestationChallenge = challenge
|
||||
params.attestationChallenge = challenge
|
||||
}
|
||||
|
||||
val certificateChain =
|
||||
CertificateGenerator.generateCertificateChain(
|
||||
uid,
|
||||
keyPair,
|
||||
null, // No attestKeyAlias in legacy flow
|
||||
params.toKeyMintAttestation(), // Convert to modern format
|
||||
1, // SecurityLevel.TRUSTED_ENVIRONMENT
|
||||
) ?: throw Exception("CertificateGenerator failed to create attested key pair.")
|
||||
|
||||
val chainAsByteList = certificateChain.map { it.encoded }
|
||||
val certChain = KeymasterCertificateChain(chainAsByteList)
|
||||
|
||||
callback.onFinished(InterceptorUtils.createSuccessKeystoreResponse(), certChain)
|
||||
InterceptorUtils.createSuccessReply()
|
||||
}
|
||||
.getOrElse {
|
||||
SystemLogger.error("[TX_ID: $txId] Failed during handleAttestKey.", it)
|
||||
TransactionResult.ContinueAndSkipPost
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPostTransact(
|
||||
txId: Long,
|
||||
target: IBinder,
|
||||
@@ -99,7 +277,7 @@ object KeystoreInterceptor : AbstractKeystoreInterceptor() {
|
||||
if (!ConfigurationManager.shouldPatch(callingUid)) return TransactionResult.SkipTransaction
|
||||
|
||||
return try {
|
||||
data.enforceInterface(SERVICE_DESCRIPTOR)
|
||||
data.enforceInterface(IKeystoreService.DESCRIPTOR)
|
||||
val alias = data.readString() ?: ""
|
||||
val extractedAlias = InterceptorUtils.extractAlias(alias)
|
||||
val keyId = KeyIdentifier(callingUid, extractedAlias)
|
||||
@@ -111,13 +289,11 @@ object KeystoreInterceptor : AbstractKeystoreInterceptor() {
|
||||
val originalLeafBytes =
|
||||
reply.createByteArray() ?: return TransactionResult.SkipTransaction
|
||||
|
||||
// The original chain is not available,
|
||||
// so we must pass a temporary one to the patcher.
|
||||
// The patcher only needs the original leaf to extract details.
|
||||
val originalLeafCert =
|
||||
(CertificateHelper.toCertificate(originalLeafBytes)
|
||||
as CertificateHelper.OperationResult.Success)
|
||||
.data
|
||||
val originalLeafCertResult = CertificateHelper.toCertificate(originalLeafBytes)
|
||||
if (originalLeafCertResult !is CertificateHelper.OperationResult.Success) {
|
||||
return TransactionResult.SkipTransaction
|
||||
}
|
||||
val originalLeafCert = originalLeafCertResult.data
|
||||
val tempChain = arrayOf<Certificate>(originalLeafCert)
|
||||
|
||||
// Perform the COMPLETE patch and rebuild operation.
|
||||
@@ -157,11 +333,6 @@ object KeystoreInterceptor : AbstractKeystoreInterceptor() {
|
||||
)
|
||||
InterceptorUtils.createByteArrayReply(caCertsBytes!!)
|
||||
} else {
|
||||
// We have no cached chain.
|
||||
// This could mean the app requested the CA without requesting the leaf
|
||||
// first, or patching failed.
|
||||
// In this case, we cannot safely intervene.
|
||||
// Let the original reply pass through.
|
||||
SystemLogger.warning(
|
||||
"[TX_ID: $txId] No cached chain found for CA request on alias '$extractedAlias'. Skipping."
|
||||
)
|
||||
@@ -177,3 +348,115 @@ object KeystoreInterceptor : AbstractKeystoreInterceptor() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data class to hold key generation parameters parsed from the legacy IKeystoreService's
|
||||
* KeymasterArguments. It is used exclusively by the KeystoreInterceptor to manage state during the
|
||||
* software key generation flow.
|
||||
*/
|
||||
private data class LegacyKeygenParameters(
|
||||
val algorithm: Int,
|
||||
val keySize: Int,
|
||||
val purpose: List<Int>,
|
||||
val digest: List<Int>,
|
||||
val certificateNotBefore: Date?,
|
||||
val rsaPublicExponent: BigInteger?,
|
||||
val ecCurveName: String?, // Derived from keySize
|
||||
) {
|
||||
// The challenge is provided in a separate transaction (attestKey), so it must be mutable.
|
||||
var attestationChallenge: ByteArray? = null
|
||||
|
||||
/**
|
||||
* Converts the legacy parameters into the modern [KeyMintAttestation] data structure, which is
|
||||
* required by the refactored [AttestationBuilder] and [CertificateGenerator].
|
||||
*/
|
||||
fun toKeyMintAttestation(): KeyMintAttestation {
|
||||
// This conversion acts as a bridge, allowing our new generic components
|
||||
// to be used by the legacy interceptor.
|
||||
return KeyMintAttestation(
|
||||
keySize = this.keySize,
|
||||
algorithm = this.algorithm,
|
||||
ecCurve = 0, // Not explicitly available in legacy args, but not critical
|
||||
ecCurveName = this.ecCurveName ?: "",
|
||||
purpose = this.purpose,
|
||||
digest = this.digest,
|
||||
rsaPublicExponent = this.rsaPublicExponent,
|
||||
certificateSerial = null, // Not provided in legacy generateKey
|
||||
certificateSubject = null, // Not provided in legacy generateKey
|
||||
certificateNotBefore = this.certificateNotBefore,
|
||||
certificateNotAfter = null, // Not provided in legacy generateKey
|
||||
attestationChallenge = this.attestationChallenge,
|
||||
// Device identifiers are not passed in legacy args;
|
||||
// AttestationBuilder will fetch them from system properties.
|
||||
brand = null,
|
||||
device = null,
|
||||
product = null,
|
||||
serial = null,
|
||||
imei = null,
|
||||
meid = null,
|
||||
manufacturer = null,
|
||||
model = null,
|
||||
secondImei = null,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
/** Factory method to create an instance from a [KeymasterArguments] object. */
|
||||
fun fromKeymasterArguments(args: KeymasterArguments): LegacyKeygenParameters {
|
||||
val algorithm = args.getEnum(KeymasterDefs.KM_TAG_ALGORITHM, 0)
|
||||
val keySize = args.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, 0).toInt()
|
||||
|
||||
return LegacyKeygenParameters(
|
||||
algorithm = algorithm,
|
||||
keySize = keySize,
|
||||
purpose = args.getEnums(KeymasterDefs.KM_TAG_PURPOSE),
|
||||
digest = args.getEnums(KeymasterDefs.KM_TAG_DIGEST),
|
||||
certificateNotBefore = args.getDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, Date()),
|
||||
rsaPublicExponent =
|
||||
if (algorithm == KeymasterDefs.KM_ALGORITHM_RSA) getRsaExponent(args) else null,
|
||||
ecCurveName =
|
||||
if (algorithm == KeymasterDefs.KM_ALGORITHM_EC) deriveEcCurveName(keySize)
|
||||
else null,
|
||||
)
|
||||
}
|
||||
|
||||
private fun deriveEcCurveName(keySize: Int): String =
|
||||
when (keySize) {
|
||||
224 -> "secp224r1"
|
||||
256 -> "secp256r1"
|
||||
384 -> "secp384r1"
|
||||
521 -> "secp521r1"
|
||||
else -> "secp256r1" // Default fallback
|
||||
}
|
||||
|
||||
/**
|
||||
* The RSA public exponent is not accessible via a public API in KeymasterArguments, so we
|
||||
* must use reflection to extract it.
|
||||
*/
|
||||
private fun getRsaExponent(args: KeymasterArguments): BigInteger? {
|
||||
return runCatching {
|
||||
val getArgumentByTag =
|
||||
KeymasterArguments::class
|
||||
.java
|
||||
.getDeclaredMethod("getArgumentByTag", Int::class.java)
|
||||
getArgumentByTag.isAccessible = true
|
||||
val rsaArgument =
|
||||
getArgumentByTag.invoke(args, KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT)
|
||||
|
||||
val getLongTagValue =
|
||||
KeymasterArguments::class
|
||||
.java
|
||||
.getDeclaredMethod(
|
||||
"getLongTagValue",
|
||||
Class.forName("android.security.keymaster.KeymasterArgument"),
|
||||
)
|
||||
getLongTagValue.isAccessible = true
|
||||
getLongTagValue.invoke(args, rsaArgument) as BigInteger
|
||||
}
|
||||
.onFailure {
|
||||
SystemLogger.error("Failed to read rsaPublicExponent via reflection.", it)
|
||||
}
|
||||
.getOrNull()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,16 +72,55 @@ object CertificateGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new key pair and a corresponding certificate chain containing a simulated
|
||||
* attestation.
|
||||
* Generates a certificate chain for a given key pair. This is the primary function for creating
|
||||
* attested certificates.
|
||||
*
|
||||
* @param uid The UID of the application requesting the key.
|
||||
* @param alias The alias for the new key.
|
||||
* @param subjectKeyPair The key pair for which the certificate will be generated.
|
||||
* @param attestKeyAlias Optional alias of a key to use for attestation signing.
|
||||
* @param params The parameters for the new key and its attestation.
|
||||
* @param securityLevel The security level to embed in the attestation.
|
||||
* @return A [Pair] containing the new [KeyPair] and its certificate chain, or `null` on
|
||||
* failure.
|
||||
* @return A [List] of [Certificate] forming the new chain, or `null` on failure.
|
||||
*/
|
||||
fun generateCertificateChain(
|
||||
uid: Int,
|
||||
subjectKeyPair: KeyPair,
|
||||
attestKeyAlias: String?,
|
||||
params: KeyMintAttestation,
|
||||
securityLevel: Int,
|
||||
): List<Certificate>? {
|
||||
return runCatching {
|
||||
val keybox = getKeyboxForAlgorithm(uid, params.algorithm)
|
||||
|
||||
// Determine the signing key and issuer. If an attestKey is provided, use it.
|
||||
// Otherwise, fall back to the root key from the keybox.
|
||||
val (signingKey, issuer) =
|
||||
if (attestKeyAlias != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
getAttestationKeyInfo(uid, attestKeyAlias)?.let { it.first to it.second }
|
||||
?: (keybox.keyPair to getIssuerFromKeybox(keybox))
|
||||
} else {
|
||||
keybox.keyPair to getIssuerFromKeybox(keybox)
|
||||
}
|
||||
|
||||
// Build the new leaf certificate with the simulated attestation.
|
||||
val leafCert =
|
||||
buildCertificate(subjectKeyPair, signingKey, issuer, params, uid, securityLevel)
|
||||
|
||||
// If not self-attesting, the chain is just the leaf. Otherwise, append the keybox
|
||||
// chain.
|
||||
if (attestKeyAlias != null) {
|
||||
listOf(leafCert)
|
||||
} else {
|
||||
listOf(leafCert) + keybox.certificates
|
||||
}
|
||||
}
|
||||
.onFailure { SystemLogger.error("Failed to generate certificate chain.", it) }
|
||||
.getOrNull()
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience function that combines key pair generation and certificate chain generation.
|
||||
* Primarily used by the modern Keystore2 interceptor where generation is a single step.
|
||||
*/
|
||||
fun generateAttestedKeyPair(
|
||||
uid: Int,
|
||||
@@ -98,30 +137,9 @@ object CertificateGenerator {
|
||||
generateSoftwareKeyPair(params)
|
||||
?: throw Exception("Failed to generate underlying software key pair.")
|
||||
|
||||
val keybox = getKeyboxForAlgorithm(uid, params.algorithm)
|
||||
|
||||
// Determine the signing key and issuer. If an attestKey is provided, use it.
|
||||
// Otherwise, fall back to the root key from the keybox.
|
||||
val (signingKey, issuer) =
|
||||
if (attestKeyAlias != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
getAttestationKeyInfo(uid, attestKeyAlias)?.let { it.first to it.second }
|
||||
?: (keybox.keyPair to getIssuerFromKeybox(keybox))
|
||||
} else {
|
||||
keybox.keyPair to getIssuerFromKeybox(keybox)
|
||||
}
|
||||
|
||||
// Build the new leaf certificate with the simulated attestation.
|
||||
val leafCert =
|
||||
buildCertificate(newKeyPair, signingKey, issuer, params, uid, securityLevel)
|
||||
|
||||
// If not self-attesting, the chain is just the leaf. Otherwise, append the keybox
|
||||
// chain.
|
||||
val chain =
|
||||
if (attestKeyAlias != null) {
|
||||
listOf(leafCert)
|
||||
} else {
|
||||
listOf(leafCert) + keybox.certificates
|
||||
}
|
||||
generateCertificateChain(uid, newKeyPair, attestKeyAlias, params, securityLevel)
|
||||
?: throw Exception("Failed to generate certificate chain for new key pair.")
|
||||
|
||||
SystemLogger.info(
|
||||
"Successfully generated new certificate chain for alias: '$alias'."
|
||||
@@ -134,7 +152,7 @@ object CertificateGenerator {
|
||||
.getOrNull()
|
||||
}
|
||||
|
||||
private fun getIssuerFromKeybox(keybox: KeyBox) =
|
||||
fun getIssuerFromKeybox(keybox: KeyBox) =
|
||||
X509CertificateHolder(keybox.certificates[0].encoded).subject
|
||||
|
||||
private fun getKeyboxForAlgorithm(uid: Int, algorithm: Int): KeyBox {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package android.security.keymaster;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class ExportResult implements Parcelable {
|
||||
public final byte[] exportData;
|
||||
public final int resultCode;
|
||||
|
||||
public ExportResult(int resultCode) {
|
||||
this.resultCode = resultCode;
|
||||
this.exportData = new byte[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public static final Creator<ExportResult> CREATOR = new Creator<ExportResult>() {
|
||||
@Override
|
||||
public ExportResult createFromParcel(Parcel in) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExportResult[] newArray(int size) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package android.security.keymaster;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class KeyCharacteristics implements Parcelable {
|
||||
public KeymasterArguments hwEnforced;
|
||||
public KeymasterArguments swEnforced;
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public static final Creator<KeyCharacteristics> CREATOR = new Creator<KeyCharacteristics>() {
|
||||
@Override
|
||||
public KeyCharacteristics createFromParcel(Parcel in) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyCharacteristics[] newArray(int size) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package android.security.keymaster;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
abstract class KeymasterArgument implements Parcelable {
|
||||
public final int tag;
|
||||
|
||||
protected KeymasterArgument(int tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public static final Creator<KeymasterArgument> CREATOR = new Creator<KeymasterArgument>() {
|
||||
@Override
|
||||
public KeymasterArgument createFromParcel(Parcel in) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeymasterArgument[] newArray(int size) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package android.security.keymaster;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class KeymasterArguments implements Parcelable {
|
||||
|
||||
private static final long UINT32_RANGE = 1L << 32;
|
||||
public static final long UINT32_MAX_VALUE = UINT32_RANGE - 1;
|
||||
|
||||
private static final BigInteger UINT64_RANGE = BigInteger.ONE.shiftLeft(64);
|
||||
public static final BigInteger UINT64_MAX_VALUE = UINT64_RANGE.subtract(BigInteger.ONE);
|
||||
|
||||
private List<KeymasterArgument> mArguments;
|
||||
|
||||
public static final @NonNull Parcelable.Creator<KeymasterArguments> CREATOR = new Parcelable.Creator<KeymasterArguments>() {
|
||||
@Override
|
||||
public KeymasterArguments createFromParcel(Parcel in) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeymasterArguments[] newArray(int size) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
};
|
||||
|
||||
public KeymasterArguments() {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
private KeymasterArguments(Parcel in) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public void addEnum(int tag, int value) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public void addEnums(int tag, int... values) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public int getEnum(int tag, int defaultValue) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public List<Integer> getEnums(int tag) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
private void addEnumTag(int tag, int value) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
private int getEnumTagValue(KeymasterArgument arg) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public void addUnsignedInt(int tag, long value) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public long getUnsignedInt(int tag, long defaultValue) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public void addUnsignedLong(int tag, BigInteger value) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public List<BigInteger> getUnsignedLongs(int tag) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
private void addLongTag(int tag, BigInteger value) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
private BigInteger getLongTagValue(KeymasterArgument arg) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public void addBoolean(int tag) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public boolean getBoolean(int tag) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public void addBytes(int tag, byte[] value) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public byte[] getBytes(int tag, byte[] defaultValue) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public void addDate(int tag, Date value) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public void addDateIfNotNull(int tag, Date value) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public Date getDate(int tag, Date defaultValue) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
private KeymasterArgument getArgumentByTag(int tag) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public boolean containsTag(int tag) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public int size() {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public void readFromParcel(Parcel in) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public static BigInteger toUint64(long value) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package android.security.keymaster;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KeymasterCertificateChain implements Parcelable {
|
||||
private List<byte[]> mCertificates;
|
||||
|
||||
public KeymasterCertificateChain() {
|
||||
this.mCertificates = null;
|
||||
}
|
||||
|
||||
public KeymasterCertificateChain(List<byte[]> mCertificates) {
|
||||
this.mCertificates = mCertificates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public static final Creator<KeymasterCertificateChain> CREATOR = new Creator<KeymasterCertificateChain>() {
|
||||
@Override
|
||||
public KeymasterCertificateChain createFromParcel(Parcel in) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeymasterCertificateChain[] newArray(int size) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
package android.security.keymaster;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class KeymasterDefs {
|
||||
|
||||
private KeymasterDefs() {
|
||||
}
|
||||
|
||||
// Tag types.
|
||||
public static final int KM_INVALID = 0 << 28;
|
||||
public static final int KM_ENUM = 1 << 28;
|
||||
public static final int KM_ENUM_REP = 2 << 28;
|
||||
public static final int KM_UINT = 3 << 28;
|
||||
public static final int KM_UINT_REP = 4 << 28;
|
||||
public static final int KM_ULONG = 5 << 28;
|
||||
public static final int KM_DATE = 6 << 28;
|
||||
public static final int KM_BOOL = 7 << 28;
|
||||
public static final int KM_BIGNUM = 8 << 28;
|
||||
public static final int KM_BYTES = 9 << 28;
|
||||
public static final int KM_ULONG_REP = 10 << 28;
|
||||
|
||||
// Tag values.
|
||||
public static final int KM_TAG_INVALID = KM_INVALID | 0;
|
||||
public static final int KM_TAG_PURPOSE = KM_ENUM_REP | 1;
|
||||
public static final int KM_TAG_ALGORITHM = KM_ENUM | 2;
|
||||
public static final int KM_TAG_KEY_SIZE = KM_UINT | 3;
|
||||
public static final int KM_TAG_BLOCK_MODE = KM_ENUM_REP | 4;
|
||||
public static final int KM_TAG_DIGEST = KM_ENUM_REP | 5;
|
||||
public static final int KM_TAG_PADDING = KM_ENUM_REP | 6;
|
||||
public static final int KM_TAG_CALLER_NONCE = KM_BOOL | 7;
|
||||
public static final int KM_TAG_MIN_MAC_LENGTH = KM_UINT | 8;
|
||||
|
||||
public static final int KM_TAG_RESCOPING_ADD = KM_ENUM_REP | 101;
|
||||
public static final int KM_TAG_RESCOPING_DEL = KM_ENUM_REP | 102;
|
||||
public static final int KM_TAG_BLOB_USAGE_REQUIREMENTS = KM_ENUM | 705;
|
||||
|
||||
public static final int KM_TAG_RSA_PUBLIC_EXPONENT = KM_ULONG | 200;
|
||||
public static final int KM_TAG_INCLUDE_UNIQUE_ID = KM_BOOL | 202;
|
||||
|
||||
public static final int KM_TAG_ACTIVE_DATETIME = KM_DATE | 400;
|
||||
public static final int KM_TAG_ORIGINATION_EXPIRE_DATETIME = KM_DATE | 401;
|
||||
public static final int KM_TAG_USAGE_EXPIRE_DATETIME = KM_DATE | 402;
|
||||
public static final int KM_TAG_MIN_SECONDS_BETWEEN_OPS = KM_UINT | 403;
|
||||
public static final int KM_TAG_MAX_USES_PER_BOOT = KM_UINT | 404;
|
||||
|
||||
public static final int KM_TAG_ALL_USERS = KM_BOOL | 500;
|
||||
public static final int KM_TAG_USER_ID = KM_UINT | 501;
|
||||
public static final int KM_TAG_USER_SECURE_ID = KM_ULONG_REP | 502;
|
||||
public static final int KM_TAG_NO_AUTH_REQUIRED = KM_BOOL | 503;
|
||||
public static final int KM_TAG_USER_AUTH_TYPE = KM_ENUM | 504;
|
||||
public static final int KM_TAG_AUTH_TIMEOUT = KM_UINT | 505;
|
||||
public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506;
|
||||
public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507;
|
||||
public static final int KM_TAG_TRUSTED_CONFIRMATION_REQUIRED = KM_BOOL | 508;
|
||||
public static final int KM_TAG_UNLOCKED_DEVICE_REQUIRED = KM_BOOL | 509;
|
||||
|
||||
public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
|
||||
public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
|
||||
|
||||
public static final int KM_TAG_CREATION_DATETIME = KM_DATE | 701;
|
||||
public static final int KM_TAG_ORIGIN = KM_ENUM | 702;
|
||||
public static final int KM_TAG_ROLLBACK_RESISTANT = KM_BOOL | 703;
|
||||
public static final int KM_TAG_ROOT_OF_TRUST = KM_BYTES | 704;
|
||||
public static final int KM_TAG_UNIQUE_ID = KM_BYTES | 707;
|
||||
public static final int KM_TAG_ATTESTATION_CHALLENGE = KM_BYTES | 708;
|
||||
public static final int KM_TAG_ATTESTATION_ID_BRAND = KM_BYTES | 710;
|
||||
public static final int KM_TAG_ATTESTATION_ID_DEVICE = KM_BYTES | 711;
|
||||
public static final int KM_TAG_ATTESTATION_ID_PRODUCT = KM_BYTES | 712;
|
||||
public static final int KM_TAG_ATTESTATION_ID_SERIAL = KM_BYTES | 713;
|
||||
public static final int KM_TAG_ATTESTATION_ID_IMEI = KM_BYTES | 714;
|
||||
public static final int KM_TAG_ATTESTATION_ID_MEID = KM_BYTES | 715;
|
||||
public static final int KM_TAG_ATTESTATION_ID_MANUFACTURER = KM_BYTES | 716;
|
||||
public static final int KM_TAG_ATTESTATION_ID_MODEL = KM_BYTES | 717;
|
||||
public static final int KM_TAG_DEVICE_UNIQUE_ATTESTATION = KM_BOOL | 720;
|
||||
|
||||
public static final int KM_TAG_ASSOCIATED_DATA = KM_BYTES | 1000;
|
||||
public static final int KM_TAG_NONCE = KM_BYTES | 1001;
|
||||
public static final int KM_TAG_AUTH_TOKEN = KM_BYTES | 1002;
|
||||
public static final int KM_TAG_MAC_LENGTH = KM_UINT | 1003;
|
||||
|
||||
// Algorithm values.
|
||||
public static final int KM_ALGORITHM_RSA = 1;
|
||||
public static final int KM_ALGORITHM_EC = 3;
|
||||
public static final int KM_ALGORITHM_AES = 32;
|
||||
public static final int KM_ALGORITHM_3DES = 33;
|
||||
public static final int KM_ALGORITHM_HMAC = 128;
|
||||
|
||||
// Block modes.
|
||||
public static final int KM_MODE_ECB = 1;
|
||||
public static final int KM_MODE_CBC = 2;
|
||||
public static final int KM_MODE_CTR = 3;
|
||||
public static final int KM_MODE_GCM = 32;
|
||||
|
||||
// Padding modes.
|
||||
public static final int KM_PAD_NONE = 1;
|
||||
public static final int KM_PAD_RSA_OAEP = 2;
|
||||
public static final int KM_PAD_RSA_PSS = 3;
|
||||
public static final int KM_PAD_RSA_PKCS1_1_5_ENCRYPT = 4;
|
||||
public static final int KM_PAD_RSA_PKCS1_1_5_SIGN = 5;
|
||||
public static final int KM_PAD_PKCS7 = 64;
|
||||
|
||||
// Digest modes.
|
||||
public static final int KM_DIGEST_NONE = 0;
|
||||
public static final int KM_DIGEST_MD5 = 1;
|
||||
public static final int KM_DIGEST_SHA1 = 2;
|
||||
public static final int KM_DIGEST_SHA_2_224 = 3;
|
||||
public static final int KM_DIGEST_SHA_2_256 = 4;
|
||||
public static final int KM_DIGEST_SHA_2_384 = 5;
|
||||
public static final int KM_DIGEST_SHA_2_512 = 6;
|
||||
|
||||
// Key origins.
|
||||
public static final int KM_ORIGIN_GENERATED = 0;
|
||||
public static final int KM_ORIGIN_IMPORTED = 2;
|
||||
public static final int KM_ORIGIN_UNKNOWN = 3;
|
||||
public static final int KM_ORIGIN_SECURELY_IMPORTED = 4;
|
||||
|
||||
// Key usability requirements.
|
||||
public static final int KM_BLOB_STANDALONE = 0;
|
||||
public static final int KM_BLOB_REQUIRES_FILE_SYSTEM = 1;
|
||||
|
||||
// Operation Purposes.
|
||||
public static final int KM_PURPOSE_ENCRYPT = 0;
|
||||
public static final int KM_PURPOSE_DECRYPT = 1;
|
||||
public static final int KM_PURPOSE_SIGN = 2;
|
||||
public static final int KM_PURPOSE_VERIFY = 3;
|
||||
public static final int KM_PURPOSE_WRAP = 5;
|
||||
|
||||
// Key formats.
|
||||
public static final int KM_KEY_FORMAT_X509 = 0;
|
||||
public static final int KM_KEY_FORMAT_PKCS8 = 1;
|
||||
public static final int KM_KEY_FORMAT_RAW = 3;
|
||||
|
||||
// User authenticators.
|
||||
public static final int HW_AUTH_PASSWORD = 1 << 0;
|
||||
public static final int HW_AUTH_BIOMETRIC = 1 << 1;
|
||||
|
||||
// Error codes.
|
||||
public static final int KM_ERROR_OK = 0;
|
||||
public static final int KM_ERROR_ROOT_OF_TRUST_ALREADY_SET = -1;
|
||||
public static final int KM_ERROR_UNSUPPORTED_PURPOSE = -2;
|
||||
public static final int KM_ERROR_INCOMPATIBLE_PURPOSE = -3;
|
||||
public static final int KM_ERROR_UNSUPPORTED_ALGORITHM = -4;
|
||||
public static final int KM_ERROR_INCOMPATIBLE_ALGORITHM = -5;
|
||||
public static final int KM_ERROR_UNSUPPORTED_KEY_SIZE = -6;
|
||||
public static final int KM_ERROR_UNSUPPORTED_BLOCK_MODE = -7;
|
||||
public static final int KM_ERROR_INCOMPATIBLE_BLOCK_MODE = -8;
|
||||
public static final int KM_ERROR_UNSUPPORTED_MAC_LENGTH = -9;
|
||||
public static final int KM_ERROR_UNSUPPORTED_PADDING_MODE = -10;
|
||||
public static final int KM_ERROR_INCOMPATIBLE_PADDING_MODE = -11;
|
||||
public static final int KM_ERROR_UNSUPPORTED_DIGEST = -12;
|
||||
public static final int KM_ERROR_INCOMPATIBLE_DIGEST = -13;
|
||||
public static final int KM_ERROR_INVALID_EXPIRATION_TIME = -14;
|
||||
public static final int KM_ERROR_INVALID_USER_ID = -15;
|
||||
public static final int KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT = -16;
|
||||
public static final int KM_ERROR_UNSUPPORTED_KEY_FORMAT = -17;
|
||||
public static final int KM_ERROR_INCOMPATIBLE_KEY_FORMAT = -18;
|
||||
public static final int KM_ERROR_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = -19;
|
||||
public static final int KM_ERROR_UNSUPPORTED_KEY_VERIFICATION_ALGORITHM = -20;
|
||||
public static final int KM_ERROR_INVALID_INPUT_LENGTH = -21;
|
||||
public static final int KM_ERROR_KEY_EXPORT_OPTIONS_INVALID = -22;
|
||||
public static final int KM_ERROR_DELEGATION_NOT_ALLOWED = -23;
|
||||
public static final int KM_ERROR_KEY_NOT_YET_VALID = -24;
|
||||
public static final int KM_ERROR_KEY_EXPIRED = -25;
|
||||
public static final int KM_ERROR_KEY_USER_NOT_AUTHENTICATED = -26;
|
||||
public static final int KM_ERROR_OUTPUT_PARAMETER_NULL = -27;
|
||||
public static final int KM_ERROR_INVALID_OPERATION_HANDLE = -28;
|
||||
public static final int KM_ERROR_INSUFFICIENT_BUFFER_SPACE = -29;
|
||||
public static final int KM_ERROR_VERIFICATION_FAILED = -30;
|
||||
public static final int KM_ERROR_TOO_MANY_OPERATIONS = -31;
|
||||
public static final int KM_ERROR_UNEXPECTED_NULL_POINTER = -32;
|
||||
public static final int KM_ERROR_INVALID_KEY_BLOB = -33;
|
||||
public static final int KM_ERROR_IMPORTED_KEY_NOT_ENCRYPTED = -34;
|
||||
public static final int KM_ERROR_IMPORTED_KEY_DECRYPTION_FAILED = -35;
|
||||
public static final int KM_ERROR_IMPORTED_KEY_NOT_SIGNED = -36;
|
||||
public static final int KM_ERROR_IMPORTED_KEY_VERIFICATION_FAILED = -37;
|
||||
public static final int KM_ERROR_INVALID_ARGUMENT = -38;
|
||||
public static final int KM_ERROR_UNSUPPORTED_TAG = -39;
|
||||
public static final int KM_ERROR_INVALID_TAG = -40;
|
||||
public static final int KM_ERROR_MEMORY_ALLOCATION_FAILED = -41;
|
||||
public static final int KM_ERROR_INVALID_RESCOPING = -42;
|
||||
public static final int KM_ERROR_IMPORT_PARAMETER_MISMATCH = -44;
|
||||
public static final int KM_ERROR_SECURE_HW_ACCESS_DENIED = -45;
|
||||
public static final int KM_ERROR_OPERATION_CANCELLED = -46;
|
||||
public static final int KM_ERROR_CONCURRENT_ACCESS_CONFLICT = -47;
|
||||
public static final int KM_ERROR_SECURE_HW_BUSY = -48;
|
||||
public static final int KM_ERROR_SECURE_HW_COMMUNICATION_FAILED = -49;
|
||||
public static final int KM_ERROR_UNSUPPORTED_EC_FIELD = -50;
|
||||
public static final int KM_ERROR_MISSING_NONCE = -51;
|
||||
public static final int KM_ERROR_INVALID_NONCE = -52;
|
||||
public static final int KM_ERROR_MISSING_MAC_LENGTH = -53;
|
||||
public static final int KM_ERROR_KEY_RATE_LIMIT_EXCEEDED = -54;
|
||||
public static final int KM_ERROR_CALLER_NONCE_PROHIBITED = -55;
|
||||
public static final int KM_ERROR_KEY_MAX_OPS_EXCEEDED = -56;
|
||||
public static final int KM_ERROR_INVALID_MAC_LENGTH = -57;
|
||||
public static final int KM_ERROR_MISSING_MIN_MAC_LENGTH = -58;
|
||||
public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH = -59;
|
||||
public static final int KM_ERROR_CANNOT_ATTEST_IDS = -66;
|
||||
public static final int KM_ERROR_DEVICE_LOCKED = -72;
|
||||
public static final int KM_ERROR_UNIMPLEMENTED = -100;
|
||||
public static final int KM_ERROR_VERSION_MISMATCH = -101;
|
||||
public static final int KM_ERROR_UNKNOWN_ERROR = -1000;
|
||||
|
||||
public static final Map<Integer, String> sErrorCodeToString = new HashMap<Integer, String>();
|
||||
static {
|
||||
sErrorCodeToString.put(KM_ERROR_OK, "OK");
|
||||
sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_PURPOSE, "Unsupported purpose");
|
||||
sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_PURPOSE, "Incompatible purpose");
|
||||
sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_ALGORITHM, "Unsupported algorithm");
|
||||
sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_ALGORITHM, "Incompatible algorithm");
|
||||
sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_KEY_SIZE, "Unsupported key size");
|
||||
sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_BLOCK_MODE, "Unsupported block mode");
|
||||
sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_BLOCK_MODE, "Incompatible block mode");
|
||||
sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_MAC_LENGTH,
|
||||
"Unsupported MAC or authentication tag length");
|
||||
sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_PADDING_MODE, "Unsupported padding mode");
|
||||
sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_PADDING_MODE, "Incompatible padding mode");
|
||||
sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_DIGEST, "Unsupported digest");
|
||||
sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_DIGEST, "Incompatible digest");
|
||||
sErrorCodeToString.put(KM_ERROR_INVALID_EXPIRATION_TIME, "Invalid expiration time");
|
||||
sErrorCodeToString.put(KM_ERROR_INVALID_USER_ID, "Invalid user ID");
|
||||
sErrorCodeToString.put(KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT,
|
||||
"Invalid user authorization timeout");
|
||||
sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_KEY_FORMAT, "Unsupported key format");
|
||||
sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_KEY_FORMAT, "Incompatible key format");
|
||||
sErrorCodeToString.put(KM_ERROR_INVALID_INPUT_LENGTH, "Invalid input length");
|
||||
sErrorCodeToString.put(KM_ERROR_KEY_NOT_YET_VALID, "Key not yet valid");
|
||||
sErrorCodeToString.put(KM_ERROR_KEY_EXPIRED, "Key expired");
|
||||
sErrorCodeToString.put(KM_ERROR_KEY_USER_NOT_AUTHENTICATED, "Key user not authenticated");
|
||||
sErrorCodeToString.put(KM_ERROR_INVALID_OPERATION_HANDLE, "Invalid operation handle");
|
||||
sErrorCodeToString.put(KM_ERROR_VERIFICATION_FAILED, "Signature/MAC verification failed");
|
||||
sErrorCodeToString.put(KM_ERROR_TOO_MANY_OPERATIONS, "Too many operations");
|
||||
sErrorCodeToString.put(KM_ERROR_INVALID_KEY_BLOB, "Invalid key blob");
|
||||
sErrorCodeToString.put(KM_ERROR_INVALID_ARGUMENT, "Invalid argument");
|
||||
sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_TAG, "Unsupported tag");
|
||||
sErrorCodeToString.put(KM_ERROR_INVALID_TAG, "Invalid tag");
|
||||
sErrorCodeToString.put(KM_ERROR_MEMORY_ALLOCATION_FAILED, "Memory allocation failed");
|
||||
sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_EC_FIELD, "Unsupported EC field");
|
||||
sErrorCodeToString.put(KM_ERROR_MISSING_NONCE, "Required IV missing");
|
||||
sErrorCodeToString.put(KM_ERROR_INVALID_NONCE, "Invalid IV");
|
||||
sErrorCodeToString.put(KM_ERROR_CALLER_NONCE_PROHIBITED,
|
||||
"Caller-provided IV not permitted");
|
||||
sErrorCodeToString.put(KM_ERROR_INVALID_MAC_LENGTH,
|
||||
"Invalid MAC or authentication tag length");
|
||||
sErrorCodeToString.put(KM_ERROR_CANNOT_ATTEST_IDS, "Unable to attest device ids");
|
||||
sErrorCodeToString.put(KM_ERROR_DEVICE_LOCKED, "Device locked");
|
||||
sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented");
|
||||
sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error");
|
||||
}
|
||||
|
||||
public static int getTagType(int tag) {
|
||||
return tag & (0xF << 28);
|
||||
}
|
||||
|
||||
public static String getErrorMessage(int errorCode) {
|
||||
String result = sErrorCodeToString.get(errorCode);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
return String.valueOf(errorCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package android.security.keystore;
|
||||
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.security.keymaster.KeymasterCertificateChain;
|
||||
|
||||
public interface IKeystoreCertificateChainCallback {
|
||||
void onFinished(KeystoreResponse keystoreResponse, KeymasterCertificateChain keymasterCertificateChain)
|
||||
throws RemoteException;
|
||||
|
||||
public static abstract class Stub {
|
||||
public static IKeystoreCertificateChainCallback asInterface(IBinder b) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package android.security.keystore;
|
||||
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.security.keymaster.ExportResult;
|
||||
|
||||
public interface IKeystoreExportKeyCallback {
|
||||
void onFinished(ExportResult exportResult) throws RemoteException;
|
||||
|
||||
public static abstract class Stub {
|
||||
public static IKeystoreExportKeyCallback asInterface(IBinder b) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package android.security.keystore;
|
||||
|
||||
import android.os.IBinder;
|
||||
import android.os.IInterface;
|
||||
import android.os.RemoteException;
|
||||
import android.security.keymaster.KeyCharacteristics;
|
||||
|
||||
public interface IKeystoreKeyCharacteristicsCallback extends IInterface {
|
||||
void onFinished(KeystoreResponse keystoreResponse, KeyCharacteristics keyCharacteristics) throws RemoteException;
|
||||
|
||||
public static abstract class Stub {
|
||||
public static IKeystoreKeyCharacteristicsCallback asInterface(IBinder b) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package android.security.keystore;
|
||||
|
||||
import java.lang.String;
|
||||
|
||||
public interface IKeystoreService {
|
||||
public static final String DESCRIPTOR = "android.security.keystore.IKeystoreService";
|
||||
|
||||
class Stub {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package android.security.keystore;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class KeystoreResponse implements Parcelable {
|
||||
public final int error_code_;
|
||||
public final String error_msg_;
|
||||
|
||||
protected KeystoreResponse(int error_code, String error_msg) {
|
||||
this.error_code_ = error_code;
|
||||
this.error_msg_ = error_msg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
public static final Creator<KeystoreResponse> CREATOR = new Creator<KeystoreResponse>() {
|
||||
@Override
|
||||
public KeystoreResponse createFromParcel(Parcel in) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeystoreResponse[] newArray(int size) {
|
||||
throw new UnsupportedOperationException("STUB!");
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user