Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca3978888e | ||
|
|
023d7f929d | ||
|
|
bd40f4b950 |
@@ -29,7 +29,7 @@ val gitExecutor = objects.newInstance(GitExecutor::class.java)
|
|||||||
|
|
||||||
val gitCommitCount = gitExecutor.execute("git rev-list HEAD --count", rootDir).toInt()
|
val gitCommitCount = gitExecutor.execute("git rev-list HEAD --count", rootDir).toInt()
|
||||||
val gitCommitHash = gitExecutor.execute("git rev-parse --verify --short HEAD", rootDir)
|
val gitCommitHash = gitExecutor.execute("git rev-parse --verify --short HEAD", rootDir)
|
||||||
val verName = "v4.6"
|
val verName = "v4.7"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "org.matrix.TEESimulator"
|
namespace = "org.matrix.TEESimulator"
|
||||||
|
|||||||
@@ -194,9 +194,13 @@ object AttestationBuilder {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
params.padding.forEach {
|
if (params.padding.isNotEmpty()) {
|
||||||
list.add(
|
list.add(
|
||||||
DERTaggedObject(true, AttestationConstants.TAG_PADDING, ASN1Integer(it.toLong()))
|
DERTaggedObject(
|
||||||
|
true,
|
||||||
|
AttestationConstants.TAG_PADDING,
|
||||||
|
DERSet(params.padding.map { ASN1Integer(it.toLong()) }.toTypedArray()),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-1
@@ -218,7 +218,14 @@ class KeyMintSecurityLevelInterceptor(
|
|||||||
SystemLogger.info("[TX_ID: $txId] Creating SOFTWARE operation for KeyId $nspace.")
|
SystemLogger.info("[TX_ID: $txId] Creating SOFTWARE operation for KeyId $nspace.")
|
||||||
|
|
||||||
val params = data.createTypedArray(KeyParameter.CREATOR)!!
|
val params = data.createTypedArray(KeyParameter.CREATOR)!!
|
||||||
val parsedParams = KeyMintAttestation(params)
|
val parsedParams = KeyMintAttestation(params).let { p ->
|
||||||
|
if (p.algorithm != 0) p
|
||||||
|
else p.copy(algorithm = when (generatedKeyInfo.keyPair.private.algorithm) {
|
||||||
|
"EC" -> Algorithm.EC
|
||||||
|
"RSA" -> Algorithm.RSA
|
||||||
|
else -> p.algorithm
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
val softwareOperation = SoftwareOperation(txId, generatedKeyInfo.keyPair, parsedParams)
|
val softwareOperation = SoftwareOperation(txId, generatedKeyInfo.keyPair, parsedParams)
|
||||||
val operationBinder = SoftwareOperationBinder(softwareOperation)
|
val operationBinder = SoftwareOperationBinder(softwareOperation)
|
||||||
|
|||||||
+55
-14
@@ -6,6 +6,7 @@ import android.hardware.security.keymint.Digest
|
|||||||
import android.hardware.security.keymint.KeyPurpose
|
import android.hardware.security.keymint.KeyPurpose
|
||||||
import android.hardware.security.keymint.PaddingMode
|
import android.hardware.security.keymint.PaddingMode
|
||||||
import android.os.RemoteException
|
import android.os.RemoteException
|
||||||
|
import android.os.ServiceSpecificException
|
||||||
import android.system.keystore2.IKeystoreOperation
|
import android.system.keystore2.IKeystoreOperation
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.Signature
|
import java.security.Signature
|
||||||
@@ -17,10 +18,9 @@ import org.matrix.TEESimulator.logging.SystemLogger
|
|||||||
|
|
||||||
// A sealed interface to represent the different cryptographic operations we can perform.
|
// A sealed interface to represent the different cryptographic operations we can perform.
|
||||||
private sealed interface CryptoPrimitive {
|
private sealed interface CryptoPrimitive {
|
||||||
|
fun updateAad(aadInput: ByteArray?) {}
|
||||||
fun update(data: ByteArray?): ByteArray?
|
fun update(data: ByteArray?): ByteArray?
|
||||||
|
|
||||||
fun finish(data: ByteArray?, signature: ByteArray?): ByteArray?
|
fun finish(data: ByteArray?, signature: ByteArray?): ByteArray?
|
||||||
|
|
||||||
fun abort()
|
fun abort()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,17 +142,11 @@ private class CipherPrimitive(
|
|||||||
override fun abort() {}
|
override fun abort() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A software-only implementation of a cryptographic operation. This class acts as a controller,
|
|
||||||
* delegating to a specific cryptographic primitive based on the operation's purpose.
|
|
||||||
*/
|
|
||||||
class SoftwareOperation(private val txId: Long, keyPair: KeyPair, params: KeyMintAttestation) {
|
class SoftwareOperation(private val txId: Long, keyPair: KeyPair, params: KeyMintAttestation) {
|
||||||
// This now holds the specific strategy object (Signer, Verifier, etc.)
|
|
||||||
private val primitive: CryptoPrimitive
|
private val primitive: CryptoPrimitive
|
||||||
|
@Volatile private var finalized = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// The "Strategy" pattern: choose the implementation based on the purpose.
|
|
||||||
// For simplicity, we only consider the first purpose listed.
|
|
||||||
val purpose = params.purpose.firstOrNull()
|
val purpose = params.purpose.firstOrNull()
|
||||||
val purposeName = KeyMintParameterLogger.purposeNames[purpose] ?: "UNKNOWN"
|
val purposeName = KeyMintParameterLogger.purposeNames[purpose] ?: "UNKNOWN"
|
||||||
SystemLogger.debug("[SoftwareOp TX_ID: $txId] Initializing for purpose: $purposeName.")
|
SystemLogger.debug("[SoftwareOp TX_ID: $txId] Initializing for purpose: $purposeName.")
|
||||||
@@ -168,9 +162,28 @@ class SoftwareOperation(private val txId: Long, keyPair: KeyPair, params: KeyMin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkActive() {
|
||||||
|
if (finalized) throw ServiceSpecificException(KeystoreErrorCodes.invalidOperationHandle)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkInputLength(data: ByteArray?) {
|
||||||
|
if (data != null && data.size > MAX_RECEIVE_DATA)
|
||||||
|
throw ServiceSpecificException(KeystoreErrorCodes.tooMuchData)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateAad(aadInput: ByteArray?) {
|
||||||
|
checkActive()
|
||||||
|
checkInputLength(aadInput)
|
||||||
|
primitive.updateAad(aadInput)
|
||||||
|
}
|
||||||
|
|
||||||
fun update(data: ByteArray?): ByteArray? {
|
fun update(data: ByteArray?): ByteArray? {
|
||||||
|
checkActive()
|
||||||
|
checkInputLength(data)
|
||||||
try {
|
try {
|
||||||
return primitive.update(data)
|
return primitive.update(data)
|
||||||
|
} catch (e: ServiceSpecificException) {
|
||||||
|
throw e
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
SystemLogger.error("[SoftwareOp TX_ID: $txId] Failed to update operation.", e)
|
SystemLogger.error("[SoftwareOp TX_ID: $txId] Failed to update operation.", e)
|
||||||
throw e
|
throw e
|
||||||
@@ -178,38 +191,66 @@ class SoftwareOperation(private val txId: Long, keyPair: KeyPair, params: KeyMin
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun finish(data: ByteArray?, signature: ByteArray?): ByteArray? {
|
fun finish(data: ByteArray?, signature: ByteArray?): ByteArray? {
|
||||||
|
checkActive()
|
||||||
|
checkInputLength(data)
|
||||||
try {
|
try {
|
||||||
val result = primitive.finish(data, signature)
|
val result = primitive.finish(data, signature)
|
||||||
|
finalized = true
|
||||||
SystemLogger.info("[SoftwareOp TX_ID: $txId] Finished operation successfully.")
|
SystemLogger.info("[SoftwareOp TX_ID: $txId] Finished operation successfully.")
|
||||||
return result
|
return result
|
||||||
|
} catch (e: ServiceSpecificException) {
|
||||||
|
throw e
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
SystemLogger.error("[SoftwareOp TX_ID: $txId] Failed to finish operation.", e)
|
SystemLogger.error("[SoftwareOp TX_ID: $txId] Failed to finish operation.", e)
|
||||||
// Re-throw the exception so the binder can report it to the client.
|
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun abort() {
|
fun abort() {
|
||||||
|
finalized = true
|
||||||
primitive.abort()
|
primitive.abort()
|
||||||
SystemLogger.debug("[SoftwareOp TX_ID: $txId] Operation aborted.")
|
SystemLogger.debug("[SoftwareOp TX_ID: $txId] Operation aborted.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
// AOSP keystore2 operation.rs: const MAX_RECEIVE_DATA: usize = 0x8000
|
||||||
|
private const val MAX_RECEIVE_DATA = 0x8000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private object KeystoreErrorCodes {
|
||||||
|
val tooMuchData: Int by lazy {
|
||||||
|
resolveField("android.system.keystore2.ResponseCode", "TOO_MUCH_DATA", 29)
|
||||||
|
}
|
||||||
|
|
||||||
|
val invalidOperationHandle: Int by lazy {
|
||||||
|
resolveField("android.hardware.security.keymint.ErrorCode", "INVALID_OPERATION_HANDLE", -28)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveField(className: String, fieldName: String, fallback: Int): Int =
|
||||||
|
runCatching {
|
||||||
|
Class.forName(className).getField(fieldName).getInt(null)
|
||||||
|
}.getOrElse {
|
||||||
|
SystemLogger.debug("Resolved $className.$fieldName via fallback: $fallback")
|
||||||
|
fallback
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The Binder interface for our [SoftwareOperation]. */
|
|
||||||
class SoftwareOperationBinder(private val operation: SoftwareOperation) :
|
class SoftwareOperationBinder(private val operation: SoftwareOperation) :
|
||||||
IKeystoreOperation.Stub() {
|
IKeystoreOperation.Stub() {
|
||||||
|
|
||||||
@Throws(RemoteException::class)
|
override fun updateAad(aadInput: ByteArray?) {
|
||||||
|
operation.updateAad(aadInput)
|
||||||
|
}
|
||||||
|
|
||||||
override fun update(input: ByteArray?): ByteArray? {
|
override fun update(input: ByteArray?): ByteArray? {
|
||||||
return operation.update(input)
|
return operation.update(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(RemoteException::class)
|
|
||||||
override fun finish(input: ByteArray?, signature: ByteArray?): ByteArray? {
|
override fun finish(input: ByteArray?, signature: ByteArray?): ByteArray? {
|
||||||
return operation.finish(input, signature)
|
return operation.finish(input, signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(RemoteException::class)
|
|
||||||
override fun abort() {
|
override fun abort() {
|
||||||
operation.abort()
|
operation.abort()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
## TEESimulator-RS v4.7: Operation & Attestation Fixes
|
||||||
|
|
||||||
|
Tested against [KeyDetector](https://github.com/XiaoTong6666/KeyDetector) and [Key Attestation](https://github.com/nickel-lang/nickel) on OnePlus (Android 16) and Xiaomi Redmi 14C (Android 14).
|
||||||
|
|
||||||
|
- **PADDING encoding** — Fixed ASN.1 encoding of PADDING tag in attestation extension from individual `[6] INTEGER` entries to `[6] SET OF INTEGER`, matching AOSP `attestation_record.h` schema. Broke all RSA key attestation since v4.6.
|
||||||
|
- **Operation error-path conformance** — Software operations now track finalized state and return `INVALID_OPERATION_HANDLE (-28)` on post-abort calls. Input length guard (32KB) returns `TOO_MUCH_DATA` matching AOSP `operation.rs`. Passes KeyDetector's OperationErrorPathChecker.
|
||||||
|
- **updateAad support** — Added `updateAad` to `SoftwareOperationBinder`, fixing `AbstractMethodError` on Android 16 where the runtime Stub declares it abstract.
|
||||||
|
- **Algorithm inference** — `createOperation` now infers algorithm from the stored key pair when operation params omit the ALGORITHM tag, matching AOSP behavior.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## TEESimulator-RS v4.6: Rebrand & Detection Fix
|
## TEESimulator-RS v4.6: Rebrand & Detection Fix
|
||||||
|
|
||||||
- **RTT normalization rework** — Replaced Gaussian sleep (mean=55ms) with a 15ms floor fence. The old approach triggered Chunqiu Native Check 2.8 timing analysis; the floor-only approach satisfies the minimum RTT threshold without creating a detectable delay pattern.
|
- **RTT normalization rework** — Replaced Gaussian sleep (mean=55ms) with a 15ms floor fence. The old approach triggered Chunqiu Native Check 2.8 timing analysis; the floor-only approach satisfies the minimum RTT threshold without creating a detectable delay pattern.
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package android.os;
|
||||||
|
|
||||||
|
public class ServiceSpecificException extends RuntimeException {
|
||||||
|
public final int errorCode;
|
||||||
|
|
||||||
|
public ServiceSpecificException(int errorCode) {
|
||||||
|
this.errorCode = errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceSpecificException(int errorCode, String message) {
|
||||||
|
super(message);
|
||||||
|
this.errorCode = errorCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user