Loading identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h +78 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ using ::std::optional; using ::std::string; using ::std::tuple; using ::std::vector; using ::std::pair; // --------------------------------------------------------------------------- // Miscellaneous utilities. Loading Loading @@ -119,6 +120,12 @@ optional<vector<uint8_t>> encryptAes128Gcm(const vector<uint8_t>& key, const vec optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAndAttestation( const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId); // Like createEcKeyPairAndAttestation() but allows you to choose the public key. // optional<vector<vector<uint8_t>>> createAttestationForEcPublicKey( const vector<uint8_t>& publicKey, const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId); // Creates an 256-bit EC key using the NID_X9_62_prime256v1 curve, returns the // PKCS#8 encoded key-pair. // Loading Loading @@ -155,6 +162,12 @@ optional<vector<uint8_t>> ecKeyPairGetPkcs12(const vector<uint8_t>& keyPair, con // optional<vector<uint8_t>> signEcDsa(const vector<uint8_t>& key, const vector<uint8_t>& data); // Like signEcDsa() but instead of taking the data to be signed, takes a digest // of it instead. // optional<vector<uint8_t>> signEcDsaDigest(const vector<uint8_t>& key, const vector<uint8_t>& dataDigest); // Calculates the HMAC with SHA-256 for |data| using |key|. The calculated HMAC // is returned and will be 32 bytes. // Loading @@ -175,6 +188,27 @@ bool checkEcDsaSignature(const vector<uint8_t>& digest, const vector<uint8_t>& s // optional<vector<uint8_t>> certificateChainGetTopMostKey(const vector<uint8_t>& certificateChain); // Extracts the public-key from the top-most certificate in |certificateChain| // (which should be a concatenated chain of DER-encoded X.509 certificates). // // Return offset and size of the public-key // optional<pair<size_t, size_t>> certificateFindPublicKey(const vector<uint8_t>& x509Certificate); // Extracts the TbsCertificate from the top-most certificate in |certificateChain| // (which should be a concatenated chain of DER-encoded X.509 certificates). // // Return offset and size of the TbsCertificate // optional<pair<size_t, size_t>> certificateTbsCertificate(const vector<uint8_t>& x509Certificate); // Extracts the Signature from the top-most certificate in |certificateChain| // (which should be a concatenated chain of DER-encoded X.509 certificates). // // Return offset and size of the Signature // optional<pair<size_t, size_t>> certificateFindSignature(const vector<uint8_t>& x509Certificate); // Generates a X.509 certificate for |publicKey| (which must be in the format // returned by ecKeyPairGetPublicKey()). // Loading Loading @@ -231,6 +265,11 @@ optional<vector<vector<uint8_t>>> certificateChainSplit(const vector<uint8_t>& c // bool certificateChainValidate(const vector<uint8_t>& certificateChain); // Returns true if |certificate| is signed by |publicKey|. // bool certificateSignedByPublicKey(const vector<uint8_t>& certificate, const vector<uint8_t>& publicKey); // Signs |data| and |detachedContent| with |key| (which must be in the format // returned by ecKeyPairGetPrivateKey()). // Loading @@ -243,6 +282,21 @@ optional<vector<uint8_t>> coseSignEcDsa(const vector<uint8_t>& key, const vector const vector<uint8_t>& detachedContent, const vector<uint8_t>& certificateChain); // Creates a COSE_Signature1 where |signatureToBeSigned| is the ECDSA signature // of the ToBeSigned CBOR from RFC 8051 "4.4. Signing and Verification Process". // // The |signatureToBeSigned| is expected to be 64 bytes and contain the R value, // then the S value. // // The |data| parameter will be included in the COSE_Sign1 CBOR. // // If |certificateChain| is non-empty it's included in the 'x5chain' // protected header element (as as described in'draft-ietf-cose-x509-04'). // optional<vector<uint8_t>> coseSignEcDsaWithSignature(const vector<uint8_t>& signatureToBeSigned, const vector<uint8_t>& data, const vector<uint8_t>& certificateChain); // Checks that |signatureCoseSign1| (in COSE_Sign1 format) is a valid signature // made with |public_key| (which must be in the format returned by // ecKeyPairGetPublicKey()) where |detachedContent| is the detached content. Loading @@ -251,9 +305,23 @@ bool coseCheckEcDsaSignature(const vector<uint8_t>& signatureCoseSign1, const vector<uint8_t>& detachedContent, const vector<uint8_t>& publicKey); // Converts a DER-encoded signature to the format used in 'signature' bstr in COSE_Sign1. bool ecdsaSignatureDerToCose(const vector<uint8_t>& ecdsaDerSignature, vector<uint8_t>& ecdsaCoseSignature); // Converts from the format in in 'signature' bstr in COSE_Sign1 to DER encoding. bool ecdsaSignatureCoseToDer(const vector<uint8_t>& ecdsaCoseSignature, vector<uint8_t>& ecdsaDerSignature); // Extracts the payload from a COSE_Sign1. optional<vector<uint8_t>> coseSignGetPayload(const vector<uint8_t>& signatureCoseSign1); // Extracts the signature (of the ToBeSigned CBOR) from a COSE_Sign1. optional<vector<uint8_t>> coseSignGetSignature(const vector<uint8_t>& signatureCoseSign1); // Extracts the signature algorithm from a COSE_Sign1. optional<int> coseSignGetAlg(const vector<uint8_t>& signatureCoseSign1); // Extracts the X.509 certificate chain, if present. Returns the data as a // concatenated chain of DER-encoded X.509 certificates // Loading @@ -269,6 +337,16 @@ optional<vector<uint8_t>> coseSignGetX5Chain(const vector<uint8_t>& signatureCos optional<vector<uint8_t>> coseMac0(const vector<uint8_t>& key, const vector<uint8_t>& data, const vector<uint8_t>& detachedContent); // Creates a COSE_Mac0 where |digestToBeMaced| is the HMAC-SHA256 // of the ToBeMaced CBOR from RFC 8051 "6.3. How to Compute and Verify a MAC". // // The |digestToBeMaced| is expected to be 32 bytes. // // The |data| parameter will be included in the COSE_Mac0 CBOR. // optional<vector<uint8_t>> coseMacWithDigest(const vector<uint8_t>& digestToBeMaced, const vector<uint8_t>& data); // --------------------------------------------------------------------------- // Utility functions specific to IdentityCredential. // --------------------------------------------------------------------------- Loading identity/support/src/IdentityCredentialSupport.cpp +362 −3 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ #include <stdarg.h> #include <stdio.h> #include <time.h> #include <chrono> #include <iomanip> #include <openssl/aes.h> Loading Loading @@ -684,6 +685,48 @@ static bool parseX509Certificates(const vector<uint8_t>& certificateChain, return true; } bool certificateSignedByPublicKey(const vector<uint8_t>& certificate, const vector<uint8_t>& publicKey) { const unsigned char* p = certificate.data(); auto x509 = X509_Ptr(d2i_X509(nullptr, &p, certificate.size())); if (x509 == nullptr) { LOG(ERROR) << "Error parsing X509 certificate"; return false; } auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); if (EC_POINT_oct2point(group.get(), point.get(), publicKey.data(), publicKey.size(), nullptr) != 1) { LOG(ERROR) << "Error decoding publicKey"; return false; } auto ecKey = EC_KEY_Ptr(EC_KEY_new()); auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new()); if (ecKey.get() == nullptr || pkey.get() == nullptr) { LOG(ERROR) << "Memory allocation failed"; return false; } if (EC_KEY_set_group(ecKey.get(), group.get()) != 1) { LOG(ERROR) << "Error setting group"; return false; } if (EC_KEY_set_public_key(ecKey.get(), point.get()) != 1) { LOG(ERROR) << "Error setting point"; return false; } if (EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()) != 1) { LOG(ERROR) << "Error setting key"; return false; } if (X509_verify(x509.get(), pkey.get()) != 1) { return false; } return true; } // TODO: Right now the only check we perform is to check that each certificate // is signed by its successor. We should - but currently don't - also check // things like valid dates etc. Loading Loading @@ -770,7 +813,8 @@ vector<uint8_t> sha256(const vector<uint8_t>& data) { return ret; } optional<vector<uint8_t>> signEcDsa(const vector<uint8_t>& key, const vector<uint8_t>& data) { optional<vector<uint8_t>> signEcDsaDigest(const vector<uint8_t>& key, const vector<uint8_t>& dataDigest) { auto bn = BIGNUM_Ptr(BN_bin2bn(key.data(), key.size(), nullptr)); if (bn.get() == nullptr) { LOG(ERROR) << "Error creating BIGNUM"; Loading @@ -783,8 +827,7 @@ optional<vector<uint8_t>> signEcDsa(const vector<uint8_t>& key, const vector<uin return {}; } auto digest = sha256(data); ECDSA_SIG* sig = ECDSA_do_sign(digest.data(), digest.size(), ec_key.get()); ECDSA_SIG* sig = ECDSA_do_sign(dataDigest.data(), dataDigest.size(), ec_key.get()); if (sig == nullptr) { LOG(ERROR) << "Error signing digest"; return {}; Loading @@ -798,6 +841,10 @@ optional<vector<uint8_t>> signEcDsa(const vector<uint8_t>& key, const vector<uin return signature; } optional<vector<uint8_t>> signEcDsa(const vector<uint8_t>& key, const vector<uint8_t>& data) { return signEcDsaDigest(key, sha256(data)); } optional<vector<uint8_t>> hmacSha256(const vector<uint8_t>& key, const vector<uint8_t>& data) { HMAC_CTX ctx; HMAC_CTX_init(&ctx); Loading Loading @@ -955,6 +1002,51 @@ optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAnd return make_pair(keyPair, attestationCert.value()); } optional<vector<vector<uint8_t>>> createAttestationForEcPublicKey( const vector<uint8_t>& publicKey, const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId) { auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); if (EC_POINT_oct2point(group.get(), point.get(), publicKey.data(), publicKey.size(), nullptr) != 1) { LOG(ERROR) << "Error decoding publicKey"; return {}; } auto ecKey = EC_KEY_Ptr(EC_KEY_new()); auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new()); if (ecKey.get() == nullptr || pkey.get() == nullptr) { LOG(ERROR) << "Memory allocation failed"; return {}; } if (EC_KEY_set_group(ecKey.get(), group.get()) != 1) { LOG(ERROR) << "Error setting group"; return {}; } if (EC_KEY_set_public_key(ecKey.get(), point.get()) != 1) { LOG(ERROR) << "Error setting point"; return {}; } if (EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()) != 1) { LOG(ERROR) << "Error setting key"; return {}; } uint64_t now = (std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::system_clock::now().time_since_epoch()). count()/ 1000000000); uint64_t secondsInOneYear = 365 * 24 * 60 * 60; uint64_t expireTimeMs = (now + secondsInOneYear) * 1000; optional<vector<vector<uint8_t>>> attestationCert = createAttestation(pkey.get(), applicationId, challenge, now * 1000, expireTimeMs); if (!attestationCert) { LOG(ERROR) << "Error create attestation from key and challenge"; return {}; } return attestationCert.value(); } optional<vector<uint8_t>> createEcKeyPair() { auto ec_key = EC_KEY_Ptr(EC_KEY_new()); auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new()); Loading Loading @@ -1477,6 +1569,120 @@ optional<vector<uint8_t>> certificateChainGetTopMostKey(const vector<uint8_t>& c return publicKey; } optional<pair<size_t, size_t>> certificateFindPublicKey(const vector<uint8_t>& x509Certificate) { vector<X509_Ptr> certs; if (!parseX509Certificates(x509Certificate, certs)) { return {}; } if (certs.size() < 1) { LOG(ERROR) << "No certificates in chain"; return {}; } auto pkey = EVP_PKEY_Ptr(X509_get_pubkey(certs[0].get())); if (pkey.get() == nullptr) { LOG(ERROR) << "No public key"; return {}; } auto ecKey = EC_KEY_Ptr(EVP_PKEY_get1_EC_KEY(pkey.get())); if (ecKey.get() == nullptr) { LOG(ERROR) << "Failed getting EC key"; return {}; } auto ecGroup = EC_KEY_get0_group(ecKey.get()); auto ecPoint = EC_KEY_get0_public_key(ecKey.get()); int size = EC_POINT_point2oct(ecGroup, ecPoint, POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr); if (size == 0) { LOG(ERROR) << "Error generating public key encoding"; return {}; } vector<uint8_t> publicKey; publicKey.resize(size); EC_POINT_point2oct(ecGroup, ecPoint, POINT_CONVERSION_UNCOMPRESSED, publicKey.data(), publicKey.size(), nullptr); size_t publicKeyOffset = 0; size_t publicKeySize = (size_t)size; void* location = memmem((const void*)x509Certificate.data(), x509Certificate.size(), (const void*)publicKey.data(), publicKey.size()); if (location == NULL) { LOG(ERROR) << "Error finding publicKey from x509Certificate"; return {}; } publicKeyOffset = (size_t)((const char*)location - (const char*)x509Certificate.data()); return std::make_pair(publicKeyOffset, publicKeySize); } optional<pair<size_t, size_t>> certificateTbsCertificate(const vector<uint8_t>& x509Certificate) { vector<X509_Ptr> certs; if (!parseX509Certificates(x509Certificate, certs)) { return {}; } if (certs.size() < 1) { LOG(ERROR) << "No certificates in chain"; return {}; } unsigned char* buf = NULL; int len = i2d_re_X509_tbs(certs[0].get(), &buf); if ((len < 0) || (buf == NULL)) { LOG(ERROR) << "fail to extract tbsCertificate in x509Certificate"; return {}; } vector<uint8_t> tbsCertificate(len); memcpy(tbsCertificate.data(), buf, len); size_t tbsCertificateOffset = 0; size_t tbsCertificateSize = (size_t)len; void* location = memmem((const void*)x509Certificate.data(), x509Certificate.size(), (const void*)tbsCertificate.data(), tbsCertificate.size()); if (location == NULL) { LOG(ERROR) << "Error finding tbsCertificate from x509Certificate"; return {}; } tbsCertificateOffset = (size_t)((const char*)location - (const char*)x509Certificate.data()); return std::make_pair(tbsCertificateOffset, tbsCertificateSize); } optional<pair<size_t, size_t>> certificateFindSignature(const vector<uint8_t>& x509Certificate) { vector<X509_Ptr> certs; if (!parseX509Certificates(x509Certificate, certs)) { return {}; } if (certs.size() < 1) { LOG(ERROR) << "No certificates in chain"; return {}; } ASN1_BIT_STRING* psig; X509_ALGOR* palg; X509_get0_signature((const ASN1_BIT_STRING**)&psig, (const X509_ALGOR**)&palg, certs[0].get()); vector<char> signature(psig->length); memcpy(signature.data(), psig->data, psig->length); size_t signatureOffset = 0; size_t signatureSize = (size_t)psig->length; void* location = memmem((const void*)x509Certificate.data(), x509Certificate.size(), (const void*)signature.data(), signature.size()); if (location == NULL) { LOG(ERROR) << "Error finding signature from x509Certificate"; return {}; } signatureOffset = (size_t)((const char*)location - (const char*)x509Certificate.data()); return std::make_pair(signatureOffset, signatureSize); } // --------------------------------------------------------------------------- // COSE Utility Functions // --------------------------------------------------------------------------- Loading Loading @@ -1574,6 +1780,55 @@ bool ecdsaSignatureDerToCose(const vector<uint8_t>& ecdsaDerSignature, return true; } optional<vector<uint8_t>> coseSignEcDsaWithSignature(const vector<uint8_t>& signatureToBeSigned, const vector<uint8_t>& data, const vector<uint8_t>& certificateChain) { if (signatureToBeSigned.size() != 64) { LOG(ERROR) << "Invalid size for signatureToBeSigned, expected 64 got " << signatureToBeSigned.size(); return {}; } cppbor::Map unprotectedHeaders; cppbor::Map protectedHeaders; protectedHeaders.add(COSE_LABEL_ALG, COSE_ALG_ECDSA_256); if (certificateChain.size() != 0) { optional<vector<vector<uint8_t>>> certs = support::certificateChainSplit(certificateChain); if (!certs) { LOG(ERROR) << "Error splitting certificate chain"; return {}; } if (certs.value().size() == 1) { unprotectedHeaders.add(COSE_LABEL_X5CHAIN, certs.value()[0]); } else { cppbor::Array certArray; for (const vector<uint8_t>& cert : certs.value()) { certArray.add(cert); } unprotectedHeaders.add(COSE_LABEL_X5CHAIN, std::move(certArray)); } } vector<uint8_t> encodedProtectedHeaders = coseEncodeHeaders(protectedHeaders); cppbor::Array coseSign1; coseSign1.add(encodedProtectedHeaders); coseSign1.add(std::move(unprotectedHeaders)); if (data.size() == 0) { cppbor::Null nullValue; coseSign1.add(std::move(nullValue)); } else { coseSign1.add(data); } coseSign1.add(signatureToBeSigned); vector<uint8_t> signatureCoseSign1; signatureCoseSign1 = coseSign1.encode(); return signatureCoseSign1; } optional<vector<uint8_t>> coseSignEcDsa(const vector<uint8_t>& key, const vector<uint8_t>& data, const vector<uint8_t>& detachedContent, const vector<uint8_t>& certificateChain) { Loading Loading @@ -1709,6 +1964,35 @@ bool coseCheckEcDsaSignature(const vector<uint8_t>& signatureCoseSign1, return true; } // Extracts the signature (of the ToBeSigned CBOR) from a COSE_Sign1. optional<vector<uint8_t>> coseSignGetSignature(const vector<uint8_t>& signatureCoseSign1) { auto [item, _, message] = cppbor::parse(signatureCoseSign1); if (item == nullptr) { LOG(ERROR) << "Passed-in COSE_Sign1 is not valid CBOR: " << message; return {}; } const cppbor::Array* array = item->asArray(); if (array == nullptr) { LOG(ERROR) << "Value for COSE_Sign1 is not an array"; return {}; } if (array->size() != 4) { LOG(ERROR) << "Value for COSE_Sign1 is not an array of size 4"; return {}; } vector<uint8_t> signature; const cppbor::Bstr* signatureAsBstr = (*array)[3]->asBstr(); if (signatureAsBstr == nullptr) { LOG(ERROR) << "Value for signature is not a bstr"; return {}; } // Copy payload into |data| signature = signatureAsBstr->value(); return signature; } optional<vector<uint8_t>> coseSignGetPayload(const vector<uint8_t>& signatureCoseSign1) { auto [item, _, message] = cppbor::parse(signatureCoseSign1); if (item == nullptr) { Loading Loading @@ -1746,6 +2030,59 @@ optional<vector<uint8_t>> coseSignGetPayload(const vector<uint8_t>& signatureCos return data; } optional<int> coseSignGetAlg(const vector<uint8_t>& signatureCoseSign1) { auto [item, _, message] = cppbor::parse(signatureCoseSign1); if (item == nullptr) { LOG(ERROR) << "Passed-in COSE_Sign1 is not valid CBOR: " << message; return {}; } const cppbor::Array* array = item->asArray(); if (array == nullptr) { LOG(ERROR) << "Value for COSE_Sign1 is not an array"; return {}; } if (array->size() != 4) { LOG(ERROR) << "Value for COSE_Sign1 is not an array of size 4"; return {}; } const cppbor::Bstr* protectedHeadersBytes = (*array)[0]->asBstr(); if (protectedHeadersBytes == nullptr) { LOG(ERROR) << "Value for protectedHeaders is not a bstr"; return {}; } auto [item2, _2, message2] = cppbor::parse(protectedHeadersBytes->value()); if (item2 == nullptr) { LOG(ERROR) << "Error parsing protectedHeaders: " << message2; return {}; } const cppbor::Map* protectedHeaders = item2->asMap(); if (protectedHeaders == nullptr) { LOG(ERROR) << "Decoded CBOR for protectedHeaders is not a map"; return {}; } for (size_t n = 0; n < protectedHeaders->size(); n++) { auto [keyItem, valueItem] = (*protectedHeaders)[n]; const cppbor::Int* number = keyItem->asInt(); if (number == nullptr) { LOG(ERROR) << "Key item in top-level map is not a number"; return {}; } int label = number->value(); if (label == COSE_LABEL_ALG) { const cppbor::Int* number = valueItem->asInt(); if (number != nullptr) { return number->value(); } LOG(ERROR) << "Value for COSE_LABEL_ALG label is not a number"; return {}; } } LOG(ERROR) << "Did not find COSE_LABEL_ALG label in protected headers"; return {}; } optional<vector<uint8_t>> coseSignGetX5Chain(const vector<uint8_t>& signatureCoseSign1) { auto [item, _, message] = cppbor::parse(signatureCoseSign1); if (item == nullptr) { Loading Loading @@ -1861,6 +2198,28 @@ optional<vector<uint8_t>> coseMac0(const vector<uint8_t>& key, const vector<uint return array.encode(); } optional<vector<uint8_t>> coseMacWithDigest(const vector<uint8_t>& digestToBeMaced, const vector<uint8_t>& data) { cppbor::Map unprotectedHeaders; cppbor::Map protectedHeaders; protectedHeaders.add(COSE_LABEL_ALG, COSE_ALG_HMAC_256_256); vector<uint8_t> encodedProtectedHeaders = coseEncodeHeaders(protectedHeaders); cppbor::Array array; array.add(encodedProtectedHeaders); array.add(std::move(unprotectedHeaders)); if (data.size() == 0) { cppbor::Null nullValue; array.add(std::move(nullValue)); } else { array.add(data); } array.add(digestToBeMaced); return array.encode(); } // --------------------------------------------------------------------------- // Utility functions specific to IdentityCredential. // --------------------------------------------------------------------------- Loading Loading
identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h +78 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ using ::std::optional; using ::std::string; using ::std::tuple; using ::std::vector; using ::std::pair; // --------------------------------------------------------------------------- // Miscellaneous utilities. Loading Loading @@ -119,6 +120,12 @@ optional<vector<uint8_t>> encryptAes128Gcm(const vector<uint8_t>& key, const vec optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAndAttestation( const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId); // Like createEcKeyPairAndAttestation() but allows you to choose the public key. // optional<vector<vector<uint8_t>>> createAttestationForEcPublicKey( const vector<uint8_t>& publicKey, const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId); // Creates an 256-bit EC key using the NID_X9_62_prime256v1 curve, returns the // PKCS#8 encoded key-pair. // Loading Loading @@ -155,6 +162,12 @@ optional<vector<uint8_t>> ecKeyPairGetPkcs12(const vector<uint8_t>& keyPair, con // optional<vector<uint8_t>> signEcDsa(const vector<uint8_t>& key, const vector<uint8_t>& data); // Like signEcDsa() but instead of taking the data to be signed, takes a digest // of it instead. // optional<vector<uint8_t>> signEcDsaDigest(const vector<uint8_t>& key, const vector<uint8_t>& dataDigest); // Calculates the HMAC with SHA-256 for |data| using |key|. The calculated HMAC // is returned and will be 32 bytes. // Loading @@ -175,6 +188,27 @@ bool checkEcDsaSignature(const vector<uint8_t>& digest, const vector<uint8_t>& s // optional<vector<uint8_t>> certificateChainGetTopMostKey(const vector<uint8_t>& certificateChain); // Extracts the public-key from the top-most certificate in |certificateChain| // (which should be a concatenated chain of DER-encoded X.509 certificates). // // Return offset and size of the public-key // optional<pair<size_t, size_t>> certificateFindPublicKey(const vector<uint8_t>& x509Certificate); // Extracts the TbsCertificate from the top-most certificate in |certificateChain| // (which should be a concatenated chain of DER-encoded X.509 certificates). // // Return offset and size of the TbsCertificate // optional<pair<size_t, size_t>> certificateTbsCertificate(const vector<uint8_t>& x509Certificate); // Extracts the Signature from the top-most certificate in |certificateChain| // (which should be a concatenated chain of DER-encoded X.509 certificates). // // Return offset and size of the Signature // optional<pair<size_t, size_t>> certificateFindSignature(const vector<uint8_t>& x509Certificate); // Generates a X.509 certificate for |publicKey| (which must be in the format // returned by ecKeyPairGetPublicKey()). // Loading Loading @@ -231,6 +265,11 @@ optional<vector<vector<uint8_t>>> certificateChainSplit(const vector<uint8_t>& c // bool certificateChainValidate(const vector<uint8_t>& certificateChain); // Returns true if |certificate| is signed by |publicKey|. // bool certificateSignedByPublicKey(const vector<uint8_t>& certificate, const vector<uint8_t>& publicKey); // Signs |data| and |detachedContent| with |key| (which must be in the format // returned by ecKeyPairGetPrivateKey()). // Loading @@ -243,6 +282,21 @@ optional<vector<uint8_t>> coseSignEcDsa(const vector<uint8_t>& key, const vector const vector<uint8_t>& detachedContent, const vector<uint8_t>& certificateChain); // Creates a COSE_Signature1 where |signatureToBeSigned| is the ECDSA signature // of the ToBeSigned CBOR from RFC 8051 "4.4. Signing and Verification Process". // // The |signatureToBeSigned| is expected to be 64 bytes and contain the R value, // then the S value. // // The |data| parameter will be included in the COSE_Sign1 CBOR. // // If |certificateChain| is non-empty it's included in the 'x5chain' // protected header element (as as described in'draft-ietf-cose-x509-04'). // optional<vector<uint8_t>> coseSignEcDsaWithSignature(const vector<uint8_t>& signatureToBeSigned, const vector<uint8_t>& data, const vector<uint8_t>& certificateChain); // Checks that |signatureCoseSign1| (in COSE_Sign1 format) is a valid signature // made with |public_key| (which must be in the format returned by // ecKeyPairGetPublicKey()) where |detachedContent| is the detached content. Loading @@ -251,9 +305,23 @@ bool coseCheckEcDsaSignature(const vector<uint8_t>& signatureCoseSign1, const vector<uint8_t>& detachedContent, const vector<uint8_t>& publicKey); // Converts a DER-encoded signature to the format used in 'signature' bstr in COSE_Sign1. bool ecdsaSignatureDerToCose(const vector<uint8_t>& ecdsaDerSignature, vector<uint8_t>& ecdsaCoseSignature); // Converts from the format in in 'signature' bstr in COSE_Sign1 to DER encoding. bool ecdsaSignatureCoseToDer(const vector<uint8_t>& ecdsaCoseSignature, vector<uint8_t>& ecdsaDerSignature); // Extracts the payload from a COSE_Sign1. optional<vector<uint8_t>> coseSignGetPayload(const vector<uint8_t>& signatureCoseSign1); // Extracts the signature (of the ToBeSigned CBOR) from a COSE_Sign1. optional<vector<uint8_t>> coseSignGetSignature(const vector<uint8_t>& signatureCoseSign1); // Extracts the signature algorithm from a COSE_Sign1. optional<int> coseSignGetAlg(const vector<uint8_t>& signatureCoseSign1); // Extracts the X.509 certificate chain, if present. Returns the data as a // concatenated chain of DER-encoded X.509 certificates // Loading @@ -269,6 +337,16 @@ optional<vector<uint8_t>> coseSignGetX5Chain(const vector<uint8_t>& signatureCos optional<vector<uint8_t>> coseMac0(const vector<uint8_t>& key, const vector<uint8_t>& data, const vector<uint8_t>& detachedContent); // Creates a COSE_Mac0 where |digestToBeMaced| is the HMAC-SHA256 // of the ToBeMaced CBOR from RFC 8051 "6.3. How to Compute and Verify a MAC". // // The |digestToBeMaced| is expected to be 32 bytes. // // The |data| parameter will be included in the COSE_Mac0 CBOR. // optional<vector<uint8_t>> coseMacWithDigest(const vector<uint8_t>& digestToBeMaced, const vector<uint8_t>& data); // --------------------------------------------------------------------------- // Utility functions specific to IdentityCredential. // --------------------------------------------------------------------------- Loading
identity/support/src/IdentityCredentialSupport.cpp +362 −3 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ #include <stdarg.h> #include <stdio.h> #include <time.h> #include <chrono> #include <iomanip> #include <openssl/aes.h> Loading Loading @@ -684,6 +685,48 @@ static bool parseX509Certificates(const vector<uint8_t>& certificateChain, return true; } bool certificateSignedByPublicKey(const vector<uint8_t>& certificate, const vector<uint8_t>& publicKey) { const unsigned char* p = certificate.data(); auto x509 = X509_Ptr(d2i_X509(nullptr, &p, certificate.size())); if (x509 == nullptr) { LOG(ERROR) << "Error parsing X509 certificate"; return false; } auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); if (EC_POINT_oct2point(group.get(), point.get(), publicKey.data(), publicKey.size(), nullptr) != 1) { LOG(ERROR) << "Error decoding publicKey"; return false; } auto ecKey = EC_KEY_Ptr(EC_KEY_new()); auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new()); if (ecKey.get() == nullptr || pkey.get() == nullptr) { LOG(ERROR) << "Memory allocation failed"; return false; } if (EC_KEY_set_group(ecKey.get(), group.get()) != 1) { LOG(ERROR) << "Error setting group"; return false; } if (EC_KEY_set_public_key(ecKey.get(), point.get()) != 1) { LOG(ERROR) << "Error setting point"; return false; } if (EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()) != 1) { LOG(ERROR) << "Error setting key"; return false; } if (X509_verify(x509.get(), pkey.get()) != 1) { return false; } return true; } // TODO: Right now the only check we perform is to check that each certificate // is signed by its successor. We should - but currently don't - also check // things like valid dates etc. Loading Loading @@ -770,7 +813,8 @@ vector<uint8_t> sha256(const vector<uint8_t>& data) { return ret; } optional<vector<uint8_t>> signEcDsa(const vector<uint8_t>& key, const vector<uint8_t>& data) { optional<vector<uint8_t>> signEcDsaDigest(const vector<uint8_t>& key, const vector<uint8_t>& dataDigest) { auto bn = BIGNUM_Ptr(BN_bin2bn(key.data(), key.size(), nullptr)); if (bn.get() == nullptr) { LOG(ERROR) << "Error creating BIGNUM"; Loading @@ -783,8 +827,7 @@ optional<vector<uint8_t>> signEcDsa(const vector<uint8_t>& key, const vector<uin return {}; } auto digest = sha256(data); ECDSA_SIG* sig = ECDSA_do_sign(digest.data(), digest.size(), ec_key.get()); ECDSA_SIG* sig = ECDSA_do_sign(dataDigest.data(), dataDigest.size(), ec_key.get()); if (sig == nullptr) { LOG(ERROR) << "Error signing digest"; return {}; Loading @@ -798,6 +841,10 @@ optional<vector<uint8_t>> signEcDsa(const vector<uint8_t>& key, const vector<uin return signature; } optional<vector<uint8_t>> signEcDsa(const vector<uint8_t>& key, const vector<uint8_t>& data) { return signEcDsaDigest(key, sha256(data)); } optional<vector<uint8_t>> hmacSha256(const vector<uint8_t>& key, const vector<uint8_t>& data) { HMAC_CTX ctx; HMAC_CTX_init(&ctx); Loading Loading @@ -955,6 +1002,51 @@ optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAnd return make_pair(keyPair, attestationCert.value()); } optional<vector<vector<uint8_t>>> createAttestationForEcPublicKey( const vector<uint8_t>& publicKey, const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId) { auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); if (EC_POINT_oct2point(group.get(), point.get(), publicKey.data(), publicKey.size(), nullptr) != 1) { LOG(ERROR) << "Error decoding publicKey"; return {}; } auto ecKey = EC_KEY_Ptr(EC_KEY_new()); auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new()); if (ecKey.get() == nullptr || pkey.get() == nullptr) { LOG(ERROR) << "Memory allocation failed"; return {}; } if (EC_KEY_set_group(ecKey.get(), group.get()) != 1) { LOG(ERROR) << "Error setting group"; return {}; } if (EC_KEY_set_public_key(ecKey.get(), point.get()) != 1) { LOG(ERROR) << "Error setting point"; return {}; } if (EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()) != 1) { LOG(ERROR) << "Error setting key"; return {}; } uint64_t now = (std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::system_clock::now().time_since_epoch()). count()/ 1000000000); uint64_t secondsInOneYear = 365 * 24 * 60 * 60; uint64_t expireTimeMs = (now + secondsInOneYear) * 1000; optional<vector<vector<uint8_t>>> attestationCert = createAttestation(pkey.get(), applicationId, challenge, now * 1000, expireTimeMs); if (!attestationCert) { LOG(ERROR) << "Error create attestation from key and challenge"; return {}; } return attestationCert.value(); } optional<vector<uint8_t>> createEcKeyPair() { auto ec_key = EC_KEY_Ptr(EC_KEY_new()); auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new()); Loading Loading @@ -1477,6 +1569,120 @@ optional<vector<uint8_t>> certificateChainGetTopMostKey(const vector<uint8_t>& c return publicKey; } optional<pair<size_t, size_t>> certificateFindPublicKey(const vector<uint8_t>& x509Certificate) { vector<X509_Ptr> certs; if (!parseX509Certificates(x509Certificate, certs)) { return {}; } if (certs.size() < 1) { LOG(ERROR) << "No certificates in chain"; return {}; } auto pkey = EVP_PKEY_Ptr(X509_get_pubkey(certs[0].get())); if (pkey.get() == nullptr) { LOG(ERROR) << "No public key"; return {}; } auto ecKey = EC_KEY_Ptr(EVP_PKEY_get1_EC_KEY(pkey.get())); if (ecKey.get() == nullptr) { LOG(ERROR) << "Failed getting EC key"; return {}; } auto ecGroup = EC_KEY_get0_group(ecKey.get()); auto ecPoint = EC_KEY_get0_public_key(ecKey.get()); int size = EC_POINT_point2oct(ecGroup, ecPoint, POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr); if (size == 0) { LOG(ERROR) << "Error generating public key encoding"; return {}; } vector<uint8_t> publicKey; publicKey.resize(size); EC_POINT_point2oct(ecGroup, ecPoint, POINT_CONVERSION_UNCOMPRESSED, publicKey.data(), publicKey.size(), nullptr); size_t publicKeyOffset = 0; size_t publicKeySize = (size_t)size; void* location = memmem((const void*)x509Certificate.data(), x509Certificate.size(), (const void*)publicKey.data(), publicKey.size()); if (location == NULL) { LOG(ERROR) << "Error finding publicKey from x509Certificate"; return {}; } publicKeyOffset = (size_t)((const char*)location - (const char*)x509Certificate.data()); return std::make_pair(publicKeyOffset, publicKeySize); } optional<pair<size_t, size_t>> certificateTbsCertificate(const vector<uint8_t>& x509Certificate) { vector<X509_Ptr> certs; if (!parseX509Certificates(x509Certificate, certs)) { return {}; } if (certs.size() < 1) { LOG(ERROR) << "No certificates in chain"; return {}; } unsigned char* buf = NULL; int len = i2d_re_X509_tbs(certs[0].get(), &buf); if ((len < 0) || (buf == NULL)) { LOG(ERROR) << "fail to extract tbsCertificate in x509Certificate"; return {}; } vector<uint8_t> tbsCertificate(len); memcpy(tbsCertificate.data(), buf, len); size_t tbsCertificateOffset = 0; size_t tbsCertificateSize = (size_t)len; void* location = memmem((const void*)x509Certificate.data(), x509Certificate.size(), (const void*)tbsCertificate.data(), tbsCertificate.size()); if (location == NULL) { LOG(ERROR) << "Error finding tbsCertificate from x509Certificate"; return {}; } tbsCertificateOffset = (size_t)((const char*)location - (const char*)x509Certificate.data()); return std::make_pair(tbsCertificateOffset, tbsCertificateSize); } optional<pair<size_t, size_t>> certificateFindSignature(const vector<uint8_t>& x509Certificate) { vector<X509_Ptr> certs; if (!parseX509Certificates(x509Certificate, certs)) { return {}; } if (certs.size() < 1) { LOG(ERROR) << "No certificates in chain"; return {}; } ASN1_BIT_STRING* psig; X509_ALGOR* palg; X509_get0_signature((const ASN1_BIT_STRING**)&psig, (const X509_ALGOR**)&palg, certs[0].get()); vector<char> signature(psig->length); memcpy(signature.data(), psig->data, psig->length); size_t signatureOffset = 0; size_t signatureSize = (size_t)psig->length; void* location = memmem((const void*)x509Certificate.data(), x509Certificate.size(), (const void*)signature.data(), signature.size()); if (location == NULL) { LOG(ERROR) << "Error finding signature from x509Certificate"; return {}; } signatureOffset = (size_t)((const char*)location - (const char*)x509Certificate.data()); return std::make_pair(signatureOffset, signatureSize); } // --------------------------------------------------------------------------- // COSE Utility Functions // --------------------------------------------------------------------------- Loading Loading @@ -1574,6 +1780,55 @@ bool ecdsaSignatureDerToCose(const vector<uint8_t>& ecdsaDerSignature, return true; } optional<vector<uint8_t>> coseSignEcDsaWithSignature(const vector<uint8_t>& signatureToBeSigned, const vector<uint8_t>& data, const vector<uint8_t>& certificateChain) { if (signatureToBeSigned.size() != 64) { LOG(ERROR) << "Invalid size for signatureToBeSigned, expected 64 got " << signatureToBeSigned.size(); return {}; } cppbor::Map unprotectedHeaders; cppbor::Map protectedHeaders; protectedHeaders.add(COSE_LABEL_ALG, COSE_ALG_ECDSA_256); if (certificateChain.size() != 0) { optional<vector<vector<uint8_t>>> certs = support::certificateChainSplit(certificateChain); if (!certs) { LOG(ERROR) << "Error splitting certificate chain"; return {}; } if (certs.value().size() == 1) { unprotectedHeaders.add(COSE_LABEL_X5CHAIN, certs.value()[0]); } else { cppbor::Array certArray; for (const vector<uint8_t>& cert : certs.value()) { certArray.add(cert); } unprotectedHeaders.add(COSE_LABEL_X5CHAIN, std::move(certArray)); } } vector<uint8_t> encodedProtectedHeaders = coseEncodeHeaders(protectedHeaders); cppbor::Array coseSign1; coseSign1.add(encodedProtectedHeaders); coseSign1.add(std::move(unprotectedHeaders)); if (data.size() == 0) { cppbor::Null nullValue; coseSign1.add(std::move(nullValue)); } else { coseSign1.add(data); } coseSign1.add(signatureToBeSigned); vector<uint8_t> signatureCoseSign1; signatureCoseSign1 = coseSign1.encode(); return signatureCoseSign1; } optional<vector<uint8_t>> coseSignEcDsa(const vector<uint8_t>& key, const vector<uint8_t>& data, const vector<uint8_t>& detachedContent, const vector<uint8_t>& certificateChain) { Loading Loading @@ -1709,6 +1964,35 @@ bool coseCheckEcDsaSignature(const vector<uint8_t>& signatureCoseSign1, return true; } // Extracts the signature (of the ToBeSigned CBOR) from a COSE_Sign1. optional<vector<uint8_t>> coseSignGetSignature(const vector<uint8_t>& signatureCoseSign1) { auto [item, _, message] = cppbor::parse(signatureCoseSign1); if (item == nullptr) { LOG(ERROR) << "Passed-in COSE_Sign1 is not valid CBOR: " << message; return {}; } const cppbor::Array* array = item->asArray(); if (array == nullptr) { LOG(ERROR) << "Value for COSE_Sign1 is not an array"; return {}; } if (array->size() != 4) { LOG(ERROR) << "Value for COSE_Sign1 is not an array of size 4"; return {}; } vector<uint8_t> signature; const cppbor::Bstr* signatureAsBstr = (*array)[3]->asBstr(); if (signatureAsBstr == nullptr) { LOG(ERROR) << "Value for signature is not a bstr"; return {}; } // Copy payload into |data| signature = signatureAsBstr->value(); return signature; } optional<vector<uint8_t>> coseSignGetPayload(const vector<uint8_t>& signatureCoseSign1) { auto [item, _, message] = cppbor::parse(signatureCoseSign1); if (item == nullptr) { Loading Loading @@ -1746,6 +2030,59 @@ optional<vector<uint8_t>> coseSignGetPayload(const vector<uint8_t>& signatureCos return data; } optional<int> coseSignGetAlg(const vector<uint8_t>& signatureCoseSign1) { auto [item, _, message] = cppbor::parse(signatureCoseSign1); if (item == nullptr) { LOG(ERROR) << "Passed-in COSE_Sign1 is not valid CBOR: " << message; return {}; } const cppbor::Array* array = item->asArray(); if (array == nullptr) { LOG(ERROR) << "Value for COSE_Sign1 is not an array"; return {}; } if (array->size() != 4) { LOG(ERROR) << "Value for COSE_Sign1 is not an array of size 4"; return {}; } const cppbor::Bstr* protectedHeadersBytes = (*array)[0]->asBstr(); if (protectedHeadersBytes == nullptr) { LOG(ERROR) << "Value for protectedHeaders is not a bstr"; return {}; } auto [item2, _2, message2] = cppbor::parse(protectedHeadersBytes->value()); if (item2 == nullptr) { LOG(ERROR) << "Error parsing protectedHeaders: " << message2; return {}; } const cppbor::Map* protectedHeaders = item2->asMap(); if (protectedHeaders == nullptr) { LOG(ERROR) << "Decoded CBOR for protectedHeaders is not a map"; return {}; } for (size_t n = 0; n < protectedHeaders->size(); n++) { auto [keyItem, valueItem] = (*protectedHeaders)[n]; const cppbor::Int* number = keyItem->asInt(); if (number == nullptr) { LOG(ERROR) << "Key item in top-level map is not a number"; return {}; } int label = number->value(); if (label == COSE_LABEL_ALG) { const cppbor::Int* number = valueItem->asInt(); if (number != nullptr) { return number->value(); } LOG(ERROR) << "Value for COSE_LABEL_ALG label is not a number"; return {}; } } LOG(ERROR) << "Did not find COSE_LABEL_ALG label in protected headers"; return {}; } optional<vector<uint8_t>> coseSignGetX5Chain(const vector<uint8_t>& signatureCoseSign1) { auto [item, _, message] = cppbor::parse(signatureCoseSign1); if (item == nullptr) { Loading Loading @@ -1861,6 +2198,28 @@ optional<vector<uint8_t>> coseMac0(const vector<uint8_t>& key, const vector<uint return array.encode(); } optional<vector<uint8_t>> coseMacWithDigest(const vector<uint8_t>& digestToBeMaced, const vector<uint8_t>& data) { cppbor::Map unprotectedHeaders; cppbor::Map protectedHeaders; protectedHeaders.add(COSE_LABEL_ALG, COSE_ALG_HMAC_256_256); vector<uint8_t> encodedProtectedHeaders = coseEncodeHeaders(protectedHeaders); cppbor::Array array; array.add(encodedProtectedHeaders); array.add(std::move(unprotectedHeaders)); if (data.size() == 0) { cppbor::Null nullValue; array.add(std::move(nullValue)); } else { array.add(data); } array.add(digestToBeMaced); return array.encode(); } // --------------------------------------------------------------------------- // Utility functions specific to IdentityCredential. // --------------------------------------------------------------------------- Loading