diff --git a/app/src/main/java/org/matrix/TEESimulator/pki/CertificateGenerator.kt b/app/src/main/java/org/matrix/TEESimulator/pki/CertificateGenerator.kt index a5b3517..0f1b503 100644 --- a/app/src/main/java/org/matrix/TEESimulator/pki/CertificateGenerator.kt +++ b/app/src/main/java/org/matrix/TEESimulator/pki/CertificateGenerator.kt @@ -13,10 +13,12 @@ import java.security.spec.ECGenParameterSpec import java.security.spec.RSAKeyGenParameterSpec import java.util.Date import org.bouncycastle.asn1.x500.X500Name +import org.bouncycastle.asn1.x509.BasicConstraints import org.bouncycastle.asn1.x509.Extension import org.bouncycastle.asn1.x509.KeyUsage import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder @@ -28,20 +30,8 @@ import org.matrix.TEESimulator.interception.keystore.KeyIdentifier import org.matrix.TEESimulator.interception.keystore.shim.KeyMintSecurityLevelInterceptor import org.matrix.TEESimulator.logging.SystemLogger -/** - * Responsible for generating new cryptographic key pairs and X.509 certificate chains. - * - * This object simulates the behavior of the Android KeyMint/Keymaster HAL by creating certificates - * that include a fully-featured, simulated attestation extension. - */ object CertificateGenerator { - /** - * Generates a software-based cryptographic key pair. - * - * @param params The parameters specifying the key's algorithm, size, and other properties. - * @return A new [KeyPair], or `null` on failure. - */ fun generateSoftwareKeyPair(params: KeyMintAttestation): KeyPair? { return runCatching { val (algorithm, spec) = @@ -64,17 +54,6 @@ object CertificateGenerator { .getOrNull() } - /** - * 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 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 [List] of [Certificate] forming the new chain, or `null` on failure. - */ fun generateCertificateChain( uid: Int, subjectKeyPair: KeyPair, @@ -91,22 +70,26 @@ object CertificateGenerator { 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) = + val (signingKey, issuer, issuerCert) = 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)) + getAttestationKeyInfo(uid, attestKeyAlias)?.let { + Triple(it.first, it.second, null as X509Certificate?) + } ?: Triple( + keybox.keyPair, + getIssuerFromKeybox(keybox), + keybox.certificates.firstOrNull() as? X509Certificate + ) } else { - keybox.keyPair to getIssuerFromKeybox(keybox) + Triple( + keybox.keyPair, + getIssuerFromKeybox(keybox), + keybox.certificates.firstOrNull() as? X509Certificate + ) } - // Build the new leaf certificate with the simulated attestation. val leafCert = - buildCertificate(subjectKeyPair, signingKey, issuer, params, uid, securityLevel) + buildCertificate(subjectKeyPair, signingKey, issuer, issuerCert, params, uid, securityLevel) - // If not self-attesting, the chain is just the leaf. Otherwise, append the keybox - // chain. if (attestKeyAlias != null) { listOf(leafCert) } else { @@ -117,10 +100,6 @@ object CertificateGenerator { .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, alias: String, @@ -166,11 +145,9 @@ object CertificateGenerator { ?: throw Exception("Could not load keybox for UID $uid and algorithm $algorithmName") } - /** Retrieves the key pair and issuer name for a given attestation key alias. */ private fun getAttestationKeyInfo(uid: Int, attestKeyAlias: String): Pair? { SystemLogger.debug("Looking for attestation key: uid=$uid alias=$attestKeyAlias") val keyId = KeyIdentifier(uid, attestKeyAlias) - // Access the public map of generated keys val keyInfo = KeyMintSecurityLevelInterceptor.generatedKeys[keyId] return if (keyInfo != null) { val certChain = CertificateHelper.getCertificateChain(keyInfo.response) @@ -188,7 +165,6 @@ object CertificateGenerator { } } - /** Maps KeyPurpose values to X.509 KeyUsage bits per KeyCreationResult.aidl spec */ private fun buildKeyUsageFromPurposes(purposes: List): Int { var bits = 0 for (purpose in purposes) { @@ -204,19 +180,22 @@ object CertificateGenerator { return bits } - /** Constructs a new X.509 certificate with a simulated attestation extension. */ private fun buildCertificate( subjectKeyPair: KeyPair, signingKeyPair: KeyPair, issuer: X500Name, + issuerCert: X509Certificate?, params: KeyMintAttestation, uid: Int, securityLevel: Int, ): Certificate { - val subject = params.certificateSubject ?: X500Name("CN=Android KeyStore Key") - val leafNotAfter = - (signingKeyPair.public as? X509Certificate)?.notAfter - ?: Date(System.currentTimeMillis() + 31536000000L) + val subject = params.certificateSubject ?: X500Name("CN=Android Keystore Key") + SystemLogger.debug("[CertGen] Subject: $subject") + // Real TEEs use 25-30 year validity periods (per AOSP Beanpod KeyMaster observation) + val THIRTY_YEARS_MS = 30L * 365 * 24 * 60 * 60 * 1000 + val leafNotAfter = issuerCert?.notAfter + ?: Date(System.currentTimeMillis() + THIRTY_YEARS_MS) + SystemLogger.debug("[CertGen] Validity: ${params.certificateNotBefore ?: Date()} to $leafNotAfter") val builder = JcaX509v3CertificateBuilder( @@ -232,8 +211,33 @@ object CertificateGenerator { val keyUsageBits = buildKeyUsageFromPurposes(params.purpose) if (keyUsageBits != 0) { builder.addExtension(Extension.keyUsage, true, KeyUsage(keyUsageBits)) + SystemLogger.debug("[CertGen] Added KeyUsage extension: bits=0x${keyUsageBits.toString(16)}") } - // Add our custom, simulated attestation extension. + + // RFC 5280 Section 4.2.1.9: end-entity cert must not act as CA + builder.addExtension(Extension.basicConstraints, true, BasicConstraints(false)) + SystemLogger.debug("[CertGen] Added BasicConstraints: CA=false") + + val extUtils = JcaX509ExtensionUtils() + + // RFC 5280 Section 4.2.1.2: SHA-1 hash of subject public key + builder.addExtension( + Extension.subjectKeyIdentifier, + false, + extUtils.createSubjectKeyIdentifier(subjectKeyPair.public) + ) + SystemLogger.debug("[CertGen] Added SubjectKeyIdentifier (SKI)") + + // RFC 5280 Section 4.2.1.1: links certificate to issuer's signing key + if (issuerCert != null) { + builder.addExtension( + Extension.authorityKeyIdentifier, + false, + extUtils.createAuthorityKeyIdentifier(issuerCert) + ) + SystemLogger.debug("[CertGen] Added AuthorityKeyIdentifier (AKI) from issuer") + } + builder.addExtension( AttestationBuilder.buildAttestationExtension(params, uid, securityLevel) )