Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
c795486
Parse enforcement tags and add software operation updateAad scaffolding
MhmRdd Mar 18, 2026
e005fcb
Add operation lifecycle tracking, concurrency guard, and input limits
MhmRdd Mar 18, 2026
72c0401
Populate CreateOperationResponse.parameters for GCM operations
MhmRdd Mar 18, 2026
aeb85ee
Use ServiceSpecificException with AOSP error codes for operation errors
MhmRdd Mar 18, 2026
b611728
Implement AOSP enforcements.rs authorize_create for software operations
MhmRdd Mar 18, 2026
49167b1
Handle Domain.APP in createOperation for software-generated keys
MhmRdd Mar 18, 2026
87cc02a
Filter intercepted transaction codes and gate device ID attestation c…
MhmRdd Mar 18, 2026
dd611f4
Reject AGREE_KEY for all non-EC algorithms with UNSUPPORTED_PURPOSE
MhmRdd Mar 19, 2026
3f6f5e7
Support symmetric key generation (AES, HMAC) in software mode
MhmRdd Mar 19, 2026
bc52ef8
Align KeyMetadata authorizations and operation semantics with AOSP
MhmRdd Mar 19, 2026
f14501f
Handle IV/nonce, OAEP, GCM tags, CTR mode, and ECDH in software opera…
MhmRdd Mar 21, 2026
49d7983
Fix silent error paths, challenge error code, and null handling
MhmRdd Mar 21, 2026
f3dd980
Add dir class to sepolicy and crash safety for binder interceptors
MhmRdd Mar 21, 2026
2160e25
Align patched metadata and createOperation bookkeeping
XiaoTong6666 Mar 29, 2026
8255838
Hide DO_NOT_REPORT patch levels in generated KeyMetadata
XiaoTong6666 Mar 29, 2026
599f3bc
Clean up AOSP references in software operation comments
JingMatrix Aug 8, 2026
979c72f
Reformat with ktfmt 0.27.0 after rebasing onto main
JingMatrix Aug 8, 2026
bd8bb6a
Drop changes unrelated to operation interception
JingMatrix Aug 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,25 @@ data class KeyMintAttestation(
val manufacturer: ByteArray?,
val model: ByteArray?,
val secondImei: ByteArray?,
// Enforcement tags used by createOperation authorization and KeyMetadata population.
val userAuthType: Int? = null,
val userConfirmationRequired: Boolean? = null,
val activeDateTime: Date? = null,
val originationExpireDateTime: Date? = null,
val usageExpireDateTime: Date? = null,
val usageCountLimit: Int? = null,
val callerNonce: Boolean? = null,
val unlockedDeviceRequired: Boolean? = null,
val includeUniqueId: Boolean? = null,
val rollbackResistance: Boolean? = null,
val earlyBootOnly: Boolean? = null,
val allowWhileOnBody: Boolean? = null,
val trustedUserPresenceRequired: Boolean? = null,
val trustedConfirmationRequired: Boolean? = null,
val maxUsesPerBoot: Int? = null,
val maxBootLevel: Int? = null,
val minMacLength: Int? = null,
val rsaOaepMgfDigest: List<Int> = emptyList(),
) {
/** Secondary constructor that populates the fields by parsing an array of `KeyParameter`. */
constructor(
Expand Down Expand Up @@ -104,6 +123,25 @@ data class KeyMintAttestation(
manufacturer = params.findBlob(Tag.ATTESTATION_ID_MANUFACTURER),
model = params.findBlob(Tag.ATTESTATION_ID_MODEL),
secondImei = params.findBlob(Tag.ATTESTATION_ID_SECOND_IMEI),
// Enforcement tags consulted by createOperation authorization.
userAuthType = params.findInteger(Tag.USER_AUTH_TYPE),
userConfirmationRequired = params.findBoolean(Tag.USER_SECURE_ID),
activeDateTime = params.findDate(Tag.ACTIVE_DATETIME),
originationExpireDateTime = params.findDate(Tag.ORIGINATION_EXPIRE_DATETIME),
usageExpireDateTime = params.findDate(Tag.USAGE_EXPIRE_DATETIME),
usageCountLimit = params.findInteger(Tag.USAGE_COUNT_LIMIT),
callerNonce = params.findBoolean(Tag.CALLER_NONCE),
unlockedDeviceRequired = params.findBoolean(Tag.UNLOCKED_DEVICE_REQUIRED),
includeUniqueId = params.findBoolean(Tag.INCLUDE_UNIQUE_ID),
rollbackResistance = params.findBoolean(Tag.ROLLBACK_RESISTANCE),
earlyBootOnly = params.findBoolean(Tag.EARLY_BOOT_ONLY),
allowWhileOnBody = params.findBoolean(Tag.ALLOW_WHILE_ON_BODY),
trustedUserPresenceRequired = params.findBoolean(Tag.TRUSTED_USER_PRESENCE_REQUIRED),
trustedConfirmationRequired = params.findBoolean(Tag.TRUSTED_CONFIRMATION_REQUIRED),
maxUsesPerBoot = params.findInteger(Tag.MAX_USES_PER_BOOT),
maxBootLevel = params.findInteger(Tag.MAX_BOOT_LEVEL),
minMacLength = params.findInteger(Tag.MIN_MAC_LENGTH),
rsaOaepMgfDigest = params.findAllDigests(Tag.RSA_OAEP_MGF_DIGEST),
) {
// Log all parsed parameters for debugging purposes.
params.forEach { KeyMintParameterLogger.logParameter(it) }
Expand Down Expand Up @@ -168,7 +206,12 @@ private fun Array<KeyParameter>.findAllKeyPurpose(tag: Int): List<Int> =
private fun Array<KeyParameter>.findAllDigests(tag: Int): List<Int> =
this.filter { it.tag == tag }.map { it.value.digest }

/** Derives keySize from EC_CURVE tag when KEY_SIZE is not explicitly provided. */
/**
* Derives keySize from EC_CURVE tag when KEY_SIZE is not explicitly provided, mirroring how AOSP
* keymaster infers the key size from the curve in `EcKeyFactory`.
*
* https://cs.android.com/android/platform/superproject/main/+/main:system/keymaster/km_openssl/ec_key_factory.cpp
*/
private fun Array<KeyParameter>.deriveKeySizeFromCurve(): Int {
val curveId = this.find { it.tag == Tag.EC_CURVE }?.value?.ecCurve ?: return 0
return when (curveId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ object InterceptorUtils {
}
}

/** Creates an `KeystoreResponse` parcel that indicates success with no data. */
fun createSuccessKeystoreResponse(): KeystoreResponse {
val parcel = Parcel.obtain()
try {
Expand Down Expand Up @@ -91,6 +90,28 @@ object InterceptorUtils {
return BinderInterceptor.TransactionResult.OverrideReply(parcel)
}

/**
* Creates an `OverrideReply` that writes a `ServiceSpecificException` with the given error
* code. Uses the C++ binder::Status wire format which includes a remote stack trace header
* between the message and the error code. Java's Parcel.writeException omits this header,
* making it incompatible with native C++ AIDL clients on Android 12+.
*
* Wire format: [int32 exceptionCode] [String16 message] [int32 stackTraceSize=0] [int32
* errorCode]
*/
fun createServiceSpecificErrorReply(
errorCode: Int
): BinderInterceptor.TransactionResult.OverrideReply {
val parcel =
Parcel.obtain().apply {
writeInt(-8) // EX_SERVICE_SPECIFIC
writeString(null) // message (null → writeInt(-1) as String16 null marker)
writeInt(0) // remote stack trace header size (empty)
writeInt(errorCode) // service-specific error code
}
return BinderInterceptor.TransactionResult.OverrideReply(parcel)
}

/**
* Extracts the base alias from a potentially prefixed alias string. For example, it converts
* "USRCERT_my_key" to "my_key".
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,10 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() {
KeyMintSecurityLevelInterceptor.generatedKeys[keyId] =
KeyMintSecurityLevelInterceptor.GeneratedKeyInfo(
keyData.first,
null,
key.nspace,
response,
parsedParameters,
)
KeyMintSecurityLevelInterceptor.attestationKeys.add(keyId)
return InterceptorUtils.createTypedObjectReply(response)
Expand Down
Loading
Loading