Restores pre-PR157 custom features:
- deletedSoftwareKeys tracking in Keystore2Interceptor (returns
KEY_NOT_FOUND for getKeyEntry after software key deletion instead
of falling through to hardware)
- invalidatePatchedChains() for bulk cert chain cache clearing
- Per-UID LRU operation pruning (MAX_CONCURRENT_OPS_PER_UID=15)
prevents resource exhaustion from concurrent software operations
- SoftwareOperation.isFinalized made public for LRU eviction checks
Restores pre-PR157 custom hardening that was lost during the reset:
- StrongBox capability check (RSA<=2048, EC=P256)
- StrongBox keygen latency floor (250ms) and op latency floor (80ms)
- StrongBox concurrent op limit (4 ops in 10s sliding window)
- MAX_ALIAS_LENGTH (256KB) binder buffer guard in handleGenerateKey
Real StrongBox hardware has these constraints. Without simulation,
detectors identify the software shim by its unrealistic performance.
NO_AUTH_REQUIRED should be added to authorizations when not explicitly
disabled (!= false), matching AOSP default behavior. The != null check
from PR157 drops the tag for keys where noAuthRequired was parsed as
null (e.g. persisted keys), creating an attestation/authorization
mismatch that detectors can spot.
Also restores CALLER_NONCE in softwareEnforced attestation list.
KeyMint boolean tags (NO_AUTH_REQUIRED, CALLER_NONCE, etc.) use
presence-based semantics: tag exists = true, tag absent = null.
The .value?.boolValue approach fails on some AIDL implementations
where the boolValue field isn't populated despite the tag being
present, silently dropping boolean tags from attestations.
FileObserver DELETE events pass null for the file parameter. The
force-unwrap (file!!) from PR157 crashes the daemon when config
files are deleted. Restores safe-call with warning log.
Also restores system=prop cross-component consistency: when system
patch level is set to "prop", boot and vendor are forced to derive
from the same device property to prevent date mismatches.
Boot hash was changing every reboot because the file-based fallback
from pre-PR157 was lost during the reset to upstream. Restores the
4-step resolution chain: sysprop -> TEE attestation -> persistent
file -> random (with persistence at each step). Also restores the
"prop" keyword for patch level resolution from system properties.
keystore2 may return a different BBinder for each getSecurityLevel call,
so the initial registration during setup might not cover all binder
instances that client apps receive. Intercept getSecurityLevel replies
to register our hook on every new BBinder, deduplicated by identity hash.
Commit b6f9d7b introduced G2-specific fixes (ratio dropped from 5.00x
to 2.10x) that were lost when resetting to upstream PR #157 at 94c8e5b.
Restores: try-catch safety in BinderInterceptor.onTransact, shouldPatch
early-exit in getKeyEntry post-transact, safe parcel reads (!! to ?:)
at 6 sites, teeResponses cache population in generateKey/importKey
post-transact, uncaught exception handler in App.kt, and removes the
pingBinder liveness check that added ~1.8x overhead per pre-transact.
Ported challenge length validation, forced op rejection, system
transaction skip, dead interceptor blocking, and createErrorReply.
Removed size guards that broke G10. Fixed F14 error code to KeyMint
space (-38).
PR 157 wholesale replacement dropped libsupervisor.so and
libcertgen.so from both the ZIP include filter and the
customize.sh extraction. service.sh calls ./supervisor and
App.kt loads libcertgen.so, so the module would fail on install.
Restores our versions with the Rust build task, 4-lib include
filter, and supervisor/certgen extraction. Adopts PR 157's
nativeLibsDir simplification (always use stripped libs).
Software keys receive their final cert chain at generation time
and never go through onPostTransact patching, so re-persisting
after patching is unreachable in PR 157's architecture.
NativeCertGen serves as a fast path before BouncyCastle in
doSoftwareGeneration, falling back gracefully when libcertgen.so
is absent. GeneratedKeyPersistence saves software keys to disk
asynchronously and restores them on daemon restart.
Bump version to v5.1, restore TEESimulator-RS naming convention.
Our reimplementation of PR 157's logic had a silent divergence causing
G10 to still fail under binder stress. Instead of hunting line-by-line,
replace all shared Kotlin/Java/C++ files with PR 157's exact proven
versions that pass all 63 conformance tests. Our Rust-exclusive files
(NativeCertGen, GeneratedKeyPersistence, native-certgen crate) remain
in the repo but are dormant until re-wired in a follow-up commit.
AUTO mode now races TEE hardware against software generation via
CompletableFuture. If TEE succeeds, the cert chain is patched and
cached in teeResponses before returning, making attestation
stress-resilient. If TEE fails, software fallback is used.
ConfigurationManager no longer resolves AUTO at config time; it
passes Mode.AUTO through to KeyMintSecurityLevelInterceptor for
runtime dispatch. shouldPatch() returns true for both PATCH and
AUTO modes. TEE status file persistence removed entirely.
Aligns handleGenerateKey with upstream PR #157 three-way dispatch:
forceGenerate, raceTeePatch, or hardware forwarding with post-patch.
Hardware keygen rate limiting removed (replaced by raceTeePatch for
AUTO, plain Continue for PATCH). Attest key override in
Keystore2Interceptor now patches authorizations and uses null-safe
nspace assignment.
BinderInterceptor.onTransact now catches Throwable, preventing any
exception on a binder thread from killing the daemon. Adds a global
uncaught exception handler as defense in depth.
Replace Thread.sleep with TeeLatencySimulator (LockSupport.parkNanos +
statistical delay model) for keygen latency, reducing binder thread
blocking. Move GeneratedKeyPersistence.save to a background executor
to avoid disk I/O on binder threads.
Convert force-unwrap parcel reads to safe calls with early returns in
onPreTransact/onPostTransact hot paths. Add -DNDEBUG to native release
builds to compile out verbose logging from the ioctl hook.
Targets G2 (ping overhead) and G10 (stress attestation consistency).
Extend Rust native cert gen with software-enforced attestation tags
(CALLER_NONCE, ACTIVE_DATETIME, ORIGINATION_EXPIRE_DATETIME,
USAGE_EXPIRE_DATETIME, USAGE_COUNT_LIMIT, UNLOCKED_DEVICE_REQUIRED)
and make NO_AUTH_REQUIRED conditional in teeEnforced. Fixes F5/F6
test failures where these tags were missing from NativeCertGen path.
Add teeResponses cache so PATCH mode keys patched in onPostTransact
return consistent attestation via getKeyEntry. Without this, getKeyEntry
fell through to real keystore2, returning unpatched metadata.
Remove dead Rust enums (KeyPurpose, SecurityLevel, VerifiedBootState)
that were never referenced by the DER encoder.
Upstream removed the unconditional NO_AUTH_REQUIRED from toAuthorizations.
A key generated with auth requirements would incorrectly report
NO_AUTH_REQUIRED in metadata, creating a detectable inconsistency
with the attestation extension.
ConfigurationManager gains checkSELinuxPermission (reads /proc/pid/attr)
and hasPermissionForUid (delegates to IPackageManager.checkPermission)
for AOSP-compliant access control. TeeLatencySimulator provides log-normal
distribution matching real QTEE/Trustonic hardware timing profiles.
Module customize.sh now generates a device-unique hardware-bound key seed
(32 bytes from /dev/random) and clears stale tee_status.txt on install.
SoftwareOperation now throws ServiceSpecificException for all error paths
instead of raw Java exceptions, matching AIDL wire format. updateAad on
non-AEAD operations returns INVALID_TAG (-76) per AOSP operation.rs.
SoftwareOperationBinder methods are @Synchronized to match AOSP Mutex
semantics. GCM encrypt operations return the generated IV in
CreateOperationResponse.parameters.
AuthorizeCreate enforces PURPOSE validation, algorithm-purpose
compatibility (EC rejects ENCRYPT/DECRYPT, RSA rejects AGREE_KEY),
temporal constraints (ACTIVE_DATETIME, ORIGINATION_EXPIRE, USAGE_EXPIRE),
and CALLER_NONCE prohibition. GeneratedKeyInfo carries keyParams for
authorize_create enforcement on software createOperation.
Native binder_interceptor now accepts a filtered_codes vector per
registration, skipping JNI round-trip for non-intercepted transaction
codes. Keystore2Interceptor adds getNumberOfEntries software key counting,
deleteKey KEY_ID domain resolution, patchAuthorizations for OS/VENDOR/BOOT
patch levels, importedKeys tracking to prevent stale attest-key overrides,
and nspace consistency fix in the attest-key override path.
InterceptorUtils gains createServiceSpecificErrorReply for AIDL-compliant
error serialization and patchAuthorizations for authorization array patching.
KeyMintAttestation now carries all 17 enforcement tags that AOSP's
authorize_create and buildKeyDescription paths expect. AttestationBuilder
populates BLOCK_MODE as SET OF INTEGER, gates version-guarded tags
(RSA_OAEP_MGF_DIGEST >=100, ROLLBACK_RESISTANCE >=3, EARLY_BOOT_ONLY >=4),
computes INCLUDE_UNIQUE_ID via HMAC-SHA256 per KeyMint HAL spec, and
gates AAID on challenge presence.
CertificateGenerator uses AOSP cert validity defaults (epoch notBefore,
9999-12-31 notAfter), returns ServiceSpecificException(-75) for missing
keybox, and adds RSA exponent null safety.
trackAndEnforceOpLimit was only called in the Domain.KEY_ID not-found
path, so software-generated keys (found via Domain.APP) bypassed the
STRONGBOX_MAX_CONCURRENT_OPS=4 limit entirely. DuckDetector's concurrent
signing handles test created 24+ operations that all succeeded via LRU
pruning instead of being rejected with TOO_MANY_OPERATIONS (-29).
DuckDetector flags several behavioral anomalies that real TEE/StrongBox
hardware exhibits but our software interceptor did not:
- LRU pruning: cap concurrent ops at 15 (TEE) / 4 (StrongBox) per UID,
aborting oldest when exceeded — matches AOSP keystore2 malus scoring
- StrongBox param guard: forward unsupported params (RSA>2048, non-P256)
to real HAL for proper rejection instead of generating in software
- StrongBox latency floors: 250ms keygen, 80ms sign to match real SE
timing characteristics
- Sliding-window op limit for hardware-generated StrongBox keys that
bypass the software pruning path
- Domain.APP lookup path for createOperation to find software-generated
keys that never reach keystore2's database
Some Android 10 devices (e.g. Sony H8296) report EC private key
algorithm as "ECDSA" instead of "EC", causing IllegalArgumentException
in certificate signing and a SIGSEGV crash in the keystore process.
Closes#4
Symmetric keys (AES/HMAC/3DES) don't have KeyPairs or attestation
certs — routing them through doSoftwareKeyGen crashes with
"Unsupported algorithm: 32". Skip the software path entirely and
let the real HAL handle them.
Also adds CTR block mode, RSA_PKCS1_1_5_SIGN cipher padding, and
RSA_PSS signature padding to JcaAlgorithmMapper.
1. Add permission checks for KeyMintSecurityLevelInterceptor to ensure that only authorized users can access sensitive information about the security level of the key mint.
2. Fix regression where device id attestation was allowed for all users by adding appropriate permission checks.
3. Update .gitignore to exclude build artifacts and generated files to keep the repository clean and prevent accidental commits of unnecessary files.
KeyDetector's OperationErrorPathChecker (flag 0x400000) probes three
error-path behaviors that real keystore2 operations expose. Our
SoftwareOperationBinder was missing all three, plus had no updateAad
implementation which caused AbstractMethodError on Android 16 where
the runtime Stub declares it abstract.
SoftwareOperation changes:
- Add finalized state tracking; post-abort calls now throw
INVALID_OPERATION_HANDLE (-28) matching AOSP operation.rs
- Add input length guard (0x8000) throwing TOO_MUCH_DATA (29)
matching AOSP operation.rs MAX_RECEIVE_DATA
- Add updateAad to CryptoPrimitive interface and SoftwareOperationBinder
- Add KeystoreErrorCodes with runtime reflection + AOSP fallback values
KeyMintSecurityLevelInterceptor changes:
- Infer algorithm from stored key pair when operation params omit
ALGORITHM tag, matching AOSP behavior where createOperation uses
the key's stored algorithm rather than requiring it in op params
Stub addition:
- ServiceSpecificException compile stub (framework-internal class
resolved at runtime on device)
Tested on OnePlus Android 16 (SDK 36) — KeyDetector passes all three
probes: updateAad succeeds, TOO_MUCH_DATA returns code=21,
INVALID_OPERATION_HANDLE returns after abort.
PADDING (tag 6) is ENUM_REP in Tag.aidl, meaning SET OF INTEGER in the
attestation extension ASN.1 — same as PURPOSE and DIGEST. Commit f8bfa0d
added it as individual [6] INTEGER entries, causing parsers to fail with
CertificateParsingException on any RSA key attestation.
Fork identity: rename across module metadata, CI pipeline, and build
scripts. Version scheme changed from v4.5-115-7e87766 to v4.6-117
format — commit count auto-increments, git hash dropped from filenames.
The old Gaussian sleep (mean=55ms, stddev=12ms) triggered detection on
Chunqiu Native Check 2.8. A flat 15ms floor satisfies the minimum RTT
threshold without creating a detectable delay pattern — both attested
and non-attested paths get identical treatment, keeping the D50 ratio
at ~1.0 while staying above the >=15ms requirement.
signerAlgorithm was derived from params.algorithm (the generated key)
instead of the signing key, causing BouncyCastle to throw when signing
RSA keys with an EC attestation key. Now reads signingKeyPair.private.algorithm.
Device ID tags (serial/imei/meid/secondImei) were blanket-rejected
instead of flowing through to software cert gen like AOSP does.
Narrowed rejection to DEVICE_UNIQUE_ATTESTATION only.
toAuthorizations() was missing OS_VERSION, OS_PATCHLEVEL, VENDOR_PATCHLEVEL,
BOOT_PATCHLEVEL, CREATION_DATETIME, USER_ID, PADDING, and RSA_PUBLIC_EXPONENT
tags that real TEE-generated KeyMetadata always includes. EC_CURVE was also
hardcoded unconditionally, producing invalid authorizations for RSA keys.
Additionally, live-patched certificate chains in getKeyEntry weren't cached,
causing re-patching on every call with potentially different signatures.
Ports upstream JingMatrix/TEESimulator#148 and #150.
The deletion guard must not shadow re-generated keys. If an app
deletes a key then re-creates it, getKeyEntry was still returning
KEY_NOT_FOUND because deletedSoftwareKeys was checked first.
After deleting a software-generated key, getKeyEntry was falling
through to the real keystore2 service which could return a stale
hardware key with the same alias. The post-transact live-patch
fallback would then resurrect the key with a patched chain —
detectors flag this as binder inconsistency.
Track deleted software key aliases and return KEY_NOT_FOUND (7) for
subsequent getKeyEntry calls. Also always invoke cleanupKeyData on
delete to clear stale patchedChains entries for hardware keys.