/* * Copyright 2020, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "EicOpsImpl" #include #include #include #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "EicOps.h" using ::std::map; using ::std::optional; using ::std::string; using ::std::tuple; using ::std::vector; void* eicMemSet(void* s, int c, size_t n) { return memset(s, c, n); } void* eicMemCpy(void* dest, const void* src, size_t n) { return memcpy(dest, src, n); } size_t eicStrLen(const char* s) { return strlen(s); } void* eicMemMem(const uint8_t* haystack, size_t haystackLen, const uint8_t* needle, size_t needleLen) { return memmem(haystack, haystackLen, needle, needleLen); } int eicCryptoMemCmp(const void* s1, const void* s2, size_t n) { return CRYPTO_memcmp(s1, s2, n); } void eicOpsHmacSha256Init(EicHmacSha256Ctx* ctx, const uint8_t* key, size_t keySize) { HMAC_CTX* realCtx = (HMAC_CTX*)ctx; HMAC_CTX_init(realCtx); if (HMAC_Init_ex(realCtx, key, keySize, EVP_sha256(), nullptr /* impl */) != 1) { LOG(ERROR) << "Error initializing HMAC_CTX"; } } void eicOpsHmacSha256Update(EicHmacSha256Ctx* ctx, const uint8_t* data, size_t len) { HMAC_CTX* realCtx = (HMAC_CTX*)ctx; if (HMAC_Update(realCtx, data, len) != 1) { LOG(ERROR) << "Error updating HMAC_CTX"; } } void eicOpsHmacSha256Final(EicHmacSha256Ctx* ctx, uint8_t digest[EIC_SHA256_DIGEST_SIZE]) { HMAC_CTX* realCtx = (HMAC_CTX*)ctx; unsigned int size = 0; if (HMAC_Final(realCtx, digest, &size) != 1) { LOG(ERROR) << "Error finalizing HMAC_CTX"; } if (size != EIC_SHA256_DIGEST_SIZE) { LOG(ERROR) << "Expected 32 bytes from HMAC_Final, got " << size; } HMAC_CTX_cleanup(realCtx); } void eicOpsSha256Init(EicSha256Ctx* ctx) { SHA256_CTX* realCtx = (SHA256_CTX*)ctx; SHA256_Init(realCtx); } void eicOpsSha256Update(EicSha256Ctx* ctx, const uint8_t* data, size_t len) { SHA256_CTX* realCtx = (SHA256_CTX*)ctx; SHA256_Update(realCtx, data, len); } void eicOpsSha256Final(EicSha256Ctx* ctx, uint8_t digest[EIC_SHA256_DIGEST_SIZE]) { SHA256_CTX* realCtx = (SHA256_CTX*)ctx; SHA256_Final(digest, realCtx); } bool eicOpsRandom(uint8_t* buf, size_t numBytes) { optional> bytes = ::android::hardware::identity::support::getRandom(numBytes); if (!bytes.has_value()) { return false; } memcpy(buf, bytes.value().data(), numBytes); return true; } bool eicNextId(uint32_t* id) { uint32_t oldId = *id; uint32_t newId = 0; do { union { uint8_t value8; uint32_t value32; } value; if (!eicOpsRandom(&value.value8, sizeof(value))) { return false; } newId = value.value32; } while (newId == oldId && newId == 0); *id = newId; return true; } bool eicOpsEncryptAes128Gcm( const uint8_t* key, // Must be 16 bytes const uint8_t* nonce, // Must be 12 bytes const uint8_t* data, // May be NULL if size is 0 size_t dataSize, const uint8_t* additionalAuthenticationData, // May be NULL if size is 0 size_t additionalAuthenticationDataSize, uint8_t* encryptedData) { vector cppKey; cppKey.resize(16); memcpy(cppKey.data(), key, 16); vector cppData; cppData.resize(dataSize); if (dataSize > 0) { memcpy(cppData.data(), data, dataSize); } vector cppAAD; cppAAD.resize(additionalAuthenticationDataSize); if (additionalAuthenticationDataSize > 0) { memcpy(cppAAD.data(), additionalAuthenticationData, additionalAuthenticationDataSize); } vector cppNonce; cppNonce.resize(12); memcpy(cppNonce.data(), nonce, 12); optional> cppEncryptedData = android::hardware::identity::support::encryptAes128Gcm(cppKey, cppNonce, cppData, cppAAD); if (!cppEncryptedData.has_value()) { return false; } memcpy(encryptedData, cppEncryptedData.value().data(), cppEncryptedData.value().size()); return true; } // Decrypts |encryptedData| using |key| and |additionalAuthenticatedData|, // returns resulting plaintext in |data| must be of size |encryptedDataSize| - 28. // // The format of |encryptedData| must be as specified in the // encryptAes128Gcm() function. bool eicOpsDecryptAes128Gcm(const uint8_t* key, // Must be 16 bytes const uint8_t* encryptedData, size_t encryptedDataSize, const uint8_t* additionalAuthenticationData, size_t additionalAuthenticationDataSize, uint8_t* data) { vector keyVec; keyVec.resize(16); memcpy(keyVec.data(), key, 16); vector encryptedDataVec; encryptedDataVec.resize(encryptedDataSize); if (encryptedDataSize > 0) { memcpy(encryptedDataVec.data(), encryptedData, encryptedDataSize); } vector aadVec; aadVec.resize(additionalAuthenticationDataSize); if (additionalAuthenticationDataSize > 0) { memcpy(aadVec.data(), additionalAuthenticationData, additionalAuthenticationDataSize); } optional> decryptedDataVec = android::hardware::identity::support::decryptAes128Gcm(keyVec, encryptedDataVec, aadVec); if (!decryptedDataVec.has_value()) { eicDebug("Error decrypting data"); return false; } if (decryptedDataVec.value().size() != encryptedDataSize - 28) { eicDebug("Decrypted data is size %zd, expected %zd", decryptedDataVec.value().size(), encryptedDataSize - 28); return false; } if (decryptedDataVec.value().size() > 0) { memcpy(data, decryptedDataVec.value().data(), decryptedDataVec.value().size()); } return true; } bool eicOpsCreateEcKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], uint8_t publicKey[EIC_P256_PUB_KEY_SIZE]) { optional> keyPair = android::hardware::identity::support::createEcKeyPair(); if (!keyPair) { eicDebug("Error creating EC keypair"); return false; } optional> privKey = android::hardware::identity::support::ecKeyPairGetPrivateKey(keyPair.value()); if (!privKey) { eicDebug("Error extracting private key"); return false; } if (privKey.value().size() != EIC_P256_PRIV_KEY_SIZE) { eicDebug("Private key is %zd bytes, expected %zd", privKey.value().size(), (size_t)EIC_P256_PRIV_KEY_SIZE); return false; } optional> pubKey = android::hardware::identity::support::ecKeyPairGetPublicKey(keyPair.value()); if (!pubKey) { eicDebug("Error extracting public key"); return false; } // ecKeyPairGetPublicKey() returns 0x04 | x | y, we don't want the leading 0x04. if (pubKey.value().size() != EIC_P256_PUB_KEY_SIZE + 1) { eicDebug("Public key is %zd bytes long, expected %zd", pubKey.value().size(), (size_t)EIC_P256_PRIV_KEY_SIZE + 1); return false; } memcpy(privateKey, privKey.value().data(), EIC_P256_PRIV_KEY_SIZE); memcpy(publicKey, pubKey.value().data() + 1, EIC_P256_PUB_KEY_SIZE); return true; } bool eicOpsCreateCredentialKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], const uint8_t* challenge, size_t challengeSize, const uint8_t* applicationId, size_t applicationIdSize, bool testCredential, const uint8_t* attestationKeyBlob, size_t attestationKeyBlobSize, const uint8_t* attestationKeyCert, size_t attestationKeyCertSize, uint8_t* cert, size_t* certSize) { vector flatChain; vector keyPair; vector challengeVec(challenge, challenge + challengeSize); vector applicationIdVec(applicationId, applicationId + applicationIdSize); if (attestationKeyBlob && attestationKeyBlobSize > 0 && attestationKeyCert && attestationKeyCertSize > 0) { vector attestationKeyBlobVec(attestationKeyBlob, attestationKeyBlob + attestationKeyBlobSize); vector attestationKeyCertVec(attestationKeyCert, attestationKeyCert + attestationKeyCertSize); optional, vector>> keyAndCert = android::hardware::identity::support::createEcKeyPairWithAttestationKey( challengeVec, applicationIdVec, attestationKeyBlobVec, attestationKeyCertVec, testCredential); if (!keyAndCert) { eicDebug("Error generating CredentialKey and attestation"); return false; } keyPair = std::move(keyAndCert->first); flatChain = std::move(keyAndCert->second); } else { optional, vector>>> ret = android::hardware::identity::support::createEcKeyPairAndAttestation( challengeVec, applicationIdVec, testCredential); if (!ret) { eicDebug("Error generating CredentialKey and attestation"); return false; } keyPair = std::move(ret->first); flatChain = android::hardware::identity::support::certificateChainJoin(ret->second); } if (*certSize < flatChain.size()) { eicDebug("Buffer for certificate is only %zd bytes long, need %zd bytes", *certSize, flatChain.size()); return false; } memcpy(cert, flatChain.data(), flatChain.size()); *certSize = flatChain.size(); // Extract private key. optional> privKey = android::hardware::identity::support::ecKeyPairGetPrivateKey(keyPair); if (!privKey) { eicDebug("Error extracting private key"); return false; } if (privKey.value().size() != EIC_P256_PRIV_KEY_SIZE) { eicDebug("Private key is %zd bytes, expected %zd", privKey.value().size(), (size_t)EIC_P256_PRIV_KEY_SIZE); return false; } memcpy(privateKey, privKey.value().data(), EIC_P256_PRIV_KEY_SIZE); return true; } bool eicOpsSignEcKey(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE], const uint8_t signingKey[EIC_P256_PRIV_KEY_SIZE], unsigned int serial, const char* issuerName, const char* subjectName, time_t validityNotBefore, time_t validityNotAfter, const uint8_t* proofOfBinding, size_t proofOfBindingSize, uint8_t* cert, size_t* certSize) { // inout vector signingKeyVec(EIC_P256_PRIV_KEY_SIZE); memcpy(signingKeyVec.data(), signingKey, EIC_P256_PRIV_KEY_SIZE); vector pubKeyVec(EIC_P256_PUB_KEY_SIZE + 1); pubKeyVec[0] = 0x04; memcpy(pubKeyVec.data() + 1, publicKey, EIC_P256_PUB_KEY_SIZE); string serialDecimal = android::base::StringPrintf("%d", serial); map> extensions; if (proofOfBinding != nullptr) { vector proofOfBindingVec(proofOfBinding, proofOfBinding + proofOfBindingSize); extensions["1.3.6.1.4.1.11129.2.1.26"] = proofOfBindingVec; } optional> certVec = android::hardware::identity::support::ecPublicKeyGenerateCertificate( pubKeyVec, signingKeyVec, serialDecimal, issuerName, subjectName, validityNotBefore, validityNotAfter, extensions); if (!certVec) { eicDebug("Error generating certificate"); return false; } if (*certSize < certVec.value().size()) { eicDebug("Buffer for certificate is only %zd bytes long, need %zd bytes", *certSize, certVec.value().size()); return false; } memcpy(cert, certVec.value().data(), certVec.value().size()); *certSize = certVec.value().size(); return true; } bool eicOpsEcDsa(const uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], const uint8_t digestOfData[EIC_SHA256_DIGEST_SIZE], uint8_t signature[EIC_ECDSA_P256_SIGNATURE_SIZE]) { vector privKeyVec(EIC_P256_PRIV_KEY_SIZE); memcpy(privKeyVec.data(), privateKey, EIC_P256_PRIV_KEY_SIZE); vector digestVec(EIC_SHA256_DIGEST_SIZE); memcpy(digestVec.data(), digestOfData, EIC_SHA256_DIGEST_SIZE); optional> derSignature = android::hardware::identity::support::signEcDsaDigest(privKeyVec, digestVec); if (!derSignature) { eicDebug("Error signing data"); return false; } ECDSA_SIG* sig; const unsigned char* p = derSignature.value().data(); sig = d2i_ECDSA_SIG(nullptr, &p, derSignature.value().size()); if (sig == nullptr) { eicDebug("Error decoding DER signature"); return false; } if (BN_bn2binpad(sig->r, signature, 32) != 32) { ECDSA_SIG_free(sig); eicDebug("Error encoding r"); return false; } if (BN_bn2binpad(sig->s, signature + 32, 32) != 32) { ECDSA_SIG_free(sig); eicDebug("Error encoding s"); return false; } ECDSA_SIG_free(sig); return true; } static const uint8_t hbkTest[16] = {0}; static const uint8_t hbkReal[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; const uint8_t* eicOpsGetHardwareBoundKey(bool testCredential) { if (testCredential) { return hbkTest; } return hbkReal; } bool eicOpsValidateAuthToken(uint64_t /* challenge */, uint64_t /* secureUserId */, uint64_t /* authenticatorId */, int /* hardwareAuthenticatorType */, uint64_t /* timeStamp */, const uint8_t* /* mac */, size_t /* macSize */, uint64_t /* verificationTokenChallenge */, uint64_t /* verificationTokenTimeStamp */, int /* verificationTokenSecurityLevel */, const uint8_t* /* verificationTokenMac */, size_t /* verificationTokenMacSize */) { // Here's where we would validate the passed-in |authToken| to assure ourselves // that it comes from the e.g. biometric hardware and wasn't made up by an attacker. // // However this involves calculating the MAC which requires access to the to // a pre-shared key which we don't have... // return true; } bool eicOpsX509GetPublicKey(const uint8_t* x509Cert, size_t x509CertSize, uint8_t* publicKey, size_t* publicKeySize) { vector chain; chain.resize(x509CertSize); memcpy(chain.data(), x509Cert, x509CertSize); optional> res = android::hardware::identity::support::certificateChainGetTopMostKey(chain); if (!res) { return false; } if (res.value().size() > *publicKeySize) { eicDebug("Public key size is %zd but buffer only has room for %zd bytes", res.value().size(), *publicKeySize); return false; } *publicKeySize = res.value().size(); memcpy(publicKey, res.value().data(), *publicKeySize); eicDebug("Extracted %zd bytes public key from %zd bytes X.509 cert", *publicKeySize, x509CertSize); return true; } bool eicOpsX509CertSignedByPublicKey(const uint8_t* x509Cert, size_t x509CertSize, const uint8_t* publicKey, size_t publicKeySize) { vector certVec(x509Cert, x509Cert + x509CertSize); vector publicKeyVec(publicKey, publicKey + publicKeySize); return android::hardware::identity::support::certificateSignedByPublicKey(certVec, publicKeyVec); } bool eicOpsEcDsaVerifyWithPublicKey(const uint8_t* digest, size_t digestSize, const uint8_t* signature, size_t signatureSize, const uint8_t* publicKey, size_t publicKeySize) { vector digestVec(digest, digest + digestSize); vector signatureVec(signature, signature + signatureSize); vector publicKeyVec(publicKey, publicKey + publicKeySize); vector derSignature; if (!android::hardware::identity::support::ecdsaSignatureCoseToDer(signatureVec, derSignature)) { LOG(ERROR) << "Error convering signature to DER format"; return false; } if (!android::hardware::identity::support::checkEcDsaSignature(digestVec, derSignature, publicKeyVec)) { LOG(ERROR) << "Signature check failed"; return false; } return true; } bool eicOpsEcdh(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE], const uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], uint8_t sharedSecret[EIC_P256_COORDINATE_SIZE]) { vector pubKeyVec(EIC_P256_PUB_KEY_SIZE + 1); pubKeyVec[0] = 0x04; memcpy(pubKeyVec.data() + 1, publicKey, EIC_P256_PUB_KEY_SIZE); vector privKeyVec(EIC_P256_PRIV_KEY_SIZE); memcpy(privKeyVec.data(), privateKey, EIC_P256_PRIV_KEY_SIZE); optional> shared = android::hardware::identity::support::ecdh(pubKeyVec, privKeyVec); if (!shared) { LOG(ERROR) << "Error performing ECDH"; return false; } if (shared.value().size() != EIC_P256_COORDINATE_SIZE) { LOG(ERROR) << "Unexpected size of shared secret " << shared.value().size() << " expected " << EIC_P256_COORDINATE_SIZE << " bytes"; return false; } memcpy(sharedSecret, shared.value().data(), EIC_P256_COORDINATE_SIZE); return true; } bool eicOpsHkdf(const uint8_t* sharedSecret, size_t sharedSecretSize, const uint8_t* salt, size_t saltSize, const uint8_t* info, size_t infoSize, uint8_t* output, size_t outputSize) { vector sharedSecretVec(sharedSecretSize); memcpy(sharedSecretVec.data(), sharedSecret, sharedSecretSize); vector saltVec(saltSize); memcpy(saltVec.data(), salt, saltSize); vector infoVec(infoSize); memcpy(infoVec.data(), info, infoSize); optional> result = android::hardware::identity::support::hkdf( sharedSecretVec, saltVec, infoVec, outputSize); if (!result) { LOG(ERROR) << "Error performing HKDF"; return false; } if (result.value().size() != outputSize) { LOG(ERROR) << "Unexpected size of HKDF " << result.value().size() << " expected " << outputSize; return false; } memcpy(output, result.value().data(), outputSize); return true; } #ifdef EIC_DEBUG void eicPrint(const char* format, ...) { char buf[1024]; va_list args; va_start(args, format); vsnprintf(buf, sizeof(buf), format, args); va_end(args); LOG(INFO) << buf; } void eicHexdump(const char* message, const uint8_t* data, size_t dataSize) { vector dataVec(dataSize); memcpy(dataVec.data(), data, dataSize); android::hardware::identity::support::hexdump(message, dataVec); } void eicCborPrettyPrint(const uint8_t* cborData, size_t cborDataSize, size_t maxBStrSize) { vector cborDataVec(cborDataSize); memcpy(cborDataVec.data(), cborData, cborDataSize); string str = android::hardware::identity::support::cborPrettyPrint(cborDataVec, maxBStrSize, {}); fprintf(stderr, "%s\n", str.c_str()); } #endif // EIC_DEBUG