From 40877965d2f38336c7ffc5557f5ae206741feb3b Mon Sep 17 00:00:00 2001 From: Evgeny Margolis Date: Wed, 13 Oct 2021 16:12:43 -0700 Subject: [PATCH] Implemented ExtractSKIDFromX509Cert() Method. (#10358) This method will be useful for some usecases. --- .../tests/CHIPCert_test_vectors.cpp | 116 ++++++++++++------ src/credentials/tests/CHIPCert_test_vectors.h | 3 +- src/crypto/CHIPCryptoPAL.h | 5 + src/crypto/CHIPCryptoPALOpenSSL.cpp | 27 +++- src/crypto/CHIPCryptoPALmbedTLS.cpp | 48 ++++++-- src/crypto/tests/CHIPCryptoPALTest.cpp | 38 +++++- 6 files changed, 179 insertions(+), 58 deletions(-) diff --git a/src/credentials/tests/CHIPCert_test_vectors.cpp b/src/credentials/tests/CHIPCert_test_vectors.cpp index 8d21d10421f497..8b6829758dcffd 100644 --- a/src/credentials/tests/CHIPCert_test_vectors.cpp +++ b/src/credentials/tests/CHIPCert_test_vectors.cpp @@ -93,6 +93,7 @@ CHIP_ERROR GetTestCert(uint8_t certType, BitFlags certLoadFla SELECT_CERT(Node02_05); SELECT_CERT(Node02_06); SELECT_CERT(Node02_07); + #undef SELECT_CERT err = CHIP_ERROR_CA_CERT_NOT_FOUND; @@ -128,40 +129,80 @@ const char * GetTestCertName(uint8_t certType) NAME_CERT(Node02_06); NAME_CERT(Node02_07); +#undef NAME_CERT + return nullptr; } -CHIP_ERROR GetTestCertPubkey(uint8_t certType, const uint8_t ** certPubkey, uint32_t & certPubkeyLen) +CHIP_ERROR GetTestCertPubkey(uint8_t certType, ByteSpan & pubkey) { CHIP_ERROR err; -#define SELECT_CERT(NAME) \ +#define SELECT_PUBKEY(NAME) \ do \ { \ if (certType == TestCert::k##NAME) \ { \ - *certPubkey = sTestCert_##NAME##_PublicKey; \ - certPubkeyLen = sTestCert_##NAME##_PublicKey_Len; \ + pubkey = ByteSpan(sTestCert_##NAME##_PublicKey, sTestCert_##NAME##_PublicKey_Len); \ ExitNow(err = CHIP_NO_ERROR); \ } \ } while (0) - SELECT_CERT(Root01); - SELECT_CERT(Root02); - SELECT_CERT(ICA01); - SELECT_CERT(ICA02); - SELECT_CERT(ICA01_1); - SELECT_CERT(FWSign01); - SELECT_CERT(Node01_01); - SELECT_CERT(Node01_02); - SELECT_CERT(Node02_01); - SELECT_CERT(Node02_02); - SELECT_CERT(Node02_03); - SELECT_CERT(Node02_04); - SELECT_CERT(Node02_05); - SELECT_CERT(Node02_06); - SELECT_CERT(Node02_07); -#undef SELECT_CERT + SELECT_PUBKEY(Root01); + SELECT_PUBKEY(Root02); + SELECT_PUBKEY(ICA01); + SELECT_PUBKEY(ICA02); + SELECT_PUBKEY(ICA01_1); + SELECT_PUBKEY(FWSign01); + SELECT_PUBKEY(Node01_01); + SELECT_PUBKEY(Node01_02); + SELECT_PUBKEY(Node02_01); + SELECT_PUBKEY(Node02_02); + SELECT_PUBKEY(Node02_03); + SELECT_PUBKEY(Node02_04); + SELECT_PUBKEY(Node02_05); + SELECT_PUBKEY(Node02_06); + SELECT_PUBKEY(Node02_07); + +#undef SELECT_PUBKEY + + err = CHIP_ERROR_CA_CERT_NOT_FOUND; + +exit: + return err; +} + +CHIP_ERROR GetTestCertSKID(uint8_t certType, ByteSpan & skid) +{ + CHIP_ERROR err; + +#define SELECT_SKID(NAME) \ + do \ + { \ + if (certType == TestCert::k##NAME) \ + { \ + skid = ByteSpan(sTestCert_##NAME##_SubjectKeyId, sTestCert_##NAME##_SubjectKeyId_Len); \ + ExitNow(err = CHIP_NO_ERROR); \ + } \ + } while (0) + + SELECT_SKID(Root01); + SELECT_SKID(Root02); + SELECT_SKID(ICA01); + SELECT_SKID(ICA02); + SELECT_SKID(ICA01_1); + SELECT_SKID(FWSign01); + SELECT_SKID(Node01_01); + SELECT_SKID(Node01_02); + SELECT_SKID(Node02_01); + SELECT_SKID(Node02_02); + SELECT_SKID(Node02_03); + SELECT_SKID(Node02_04); + SELECT_SKID(Node02_05); + SELECT_SKID(Node02_06); + SELECT_SKID(Node02_07); + +#undef SELECT_SKID err = CHIP_ERROR_CA_CERT_NOT_FOUND; @@ -173,7 +214,7 @@ CHIP_ERROR GetTestCertAKID(uint8_t certType, ByteSpan & akid) { CHIP_ERROR err; -#define SELECT_CERT(NAME) \ +#define SELECT_AKID(NAME) \ do \ { \ if (certType == TestCert::k##NAME) \ @@ -183,22 +224,23 @@ CHIP_ERROR GetTestCertAKID(uint8_t certType, ByteSpan & akid) } \ } while (0) - SELECT_CERT(Root01); - SELECT_CERT(Root02); - SELECT_CERT(ICA01); - SELECT_CERT(ICA02); - SELECT_CERT(ICA01_1); - SELECT_CERT(FWSign01); - SELECT_CERT(Node01_01); - SELECT_CERT(Node01_02); - SELECT_CERT(Node02_01); - SELECT_CERT(Node02_02); - SELECT_CERT(Node02_03); - SELECT_CERT(Node02_04); - SELECT_CERT(Node02_05); - SELECT_CERT(Node02_06); - SELECT_CERT(Node02_07); -#undef SELECT_CERT + SELECT_AKID(Root01); + SELECT_AKID(Root02); + SELECT_AKID(ICA01); + SELECT_AKID(ICA02); + SELECT_AKID(ICA01_1); + SELECT_AKID(FWSign01); + SELECT_AKID(Node01_01); + SELECT_AKID(Node01_02); + SELECT_AKID(Node02_01); + SELECT_AKID(Node02_02); + SELECT_AKID(Node02_03); + SELECT_AKID(Node02_04); + SELECT_AKID(Node02_05); + SELECT_AKID(Node02_06); + SELECT_AKID(Node02_07); + +#undef SELECT_AKID err = CHIP_ERROR_CA_CERT_NOT_FOUND; diff --git a/src/credentials/tests/CHIPCert_test_vectors.h b/src/credentials/tests/CHIPCert_test_vectors.h index da284f77610b33..03208e89200607 100644 --- a/src/credentials/tests/CHIPCert_test_vectors.h +++ b/src/credentials/tests/CHIPCert_test_vectors.h @@ -69,7 +69,8 @@ enum class TestCertLoadFlags : uint8_t extern CHIP_ERROR GetTestCert(uint8_t certType, BitFlags certLoadFlags, ByteSpan & cert); extern const char * GetTestCertName(uint8_t certType); -extern CHIP_ERROR GetTestCertPubkey(uint8_t certType, const uint8_t ** certPubkey, uint32_t & certPubkeyLen); +extern CHIP_ERROR GetTestCertPubkey(uint8_t certType, ByteSpan & pubkey); +extern CHIP_ERROR GetTestCertSKID(uint8_t certType, ByteSpan & skid); extern CHIP_ERROR GetTestCertAKID(uint8_t certType, ByteSpan & akid); extern CHIP_ERROR LoadTestCert(ChipCertificateSet & certSet, uint8_t certType, BitFlags certLoadFlags, BitFlags decodeFlags); diff --git a/src/crypto/CHIPCryptoPAL.h b/src/crypto/CHIPCryptoPAL.h index a180b3d45e5c88..4e352c2eca3c65 100644 --- a/src/crypto/CHIPCryptoPAL.h +++ b/src/crypto/CHIPCryptoPAL.h @@ -1198,6 +1198,11 @@ CHIP_ERROR ValidateCertificateChain(const uint8_t * rootCertificate, size_t root CHIP_ERROR ExtractPubkeyFromX509Cert(const ByteSpan & certificate, Crypto::P256PublicKey & pubkey); +/** + * @brief Extracts the Subject Key Identifier from an X509 Certificate. + **/ +CHIP_ERROR ExtractSKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan & skid); + /** * @brief Extracts the Authority Key Identifier from an X509 Certificate. **/ diff --git a/src/crypto/CHIPCryptoPALOpenSSL.cpp b/src/crypto/CHIPCryptoPALOpenSSL.cpp index ba1eacd9602324..f2f629a6c4a622 100644 --- a/src/crypto/CHIPCryptoPALOpenSSL.cpp +++ b/src/crypto/CHIPCryptoPALOpenSSL.cpp @@ -1734,22 +1734,27 @@ CHIP_ERROR ExtractPubkeyFromX509Cert(const ByteSpan & certificate, Crypto::P256P return err; } -CHIP_ERROR ExtractAKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan & akid) +namespace { + +CHIP_ERROR ExtractKIDFromX509Cert(bool isSKID, const ByteSpan & certificate, MutableByteSpan & kid) { CHIP_ERROR err = CHIP_NO_ERROR; X509 * x509certificate = nullptr; const unsigned char * pCertificate = certificate.data(); const unsigned char ** ppCertificate = &pCertificate; - const ASN1_OCTET_STRING * akidString = nullptr; + const ASN1_OCTET_STRING * kidString = nullptr; x509certificate = d2i_X509(NULL, ppCertificate, static_cast(certificate.size())); VerifyOrExit(x509certificate != nullptr, err = CHIP_ERROR_NO_MEMORY); - akidString = X509_get0_authority_key_id(x509certificate); + kidString = isSKID ? X509_get0_subject_key_id(x509certificate) : X509_get0_authority_key_id(x509certificate); + VerifyOrExit(kidString != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(kidString->length <= static_cast(kid.size()), err = CHIP_ERROR_BUFFER_TOO_SMALL); + VerifyOrExit(CanCastTo(kidString->length), err = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(akidString->length == static_cast(akid.size()), err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); + memcpy(kid.data(), kidString->data, static_cast(kidString->length)); - memcpy(akid.data(), akidString->data, static_cast(akidString->length)); + kid.reduce_size(static_cast(kidString->length)); exit: X509_free(x509certificate); @@ -1757,6 +1762,18 @@ CHIP_ERROR ExtractAKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan return err; } +} // namespace + +CHIP_ERROR ExtractSKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan & skid) +{ + return ExtractKIDFromX509Cert(true, certificate, skid); +} + +CHIP_ERROR ExtractAKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan & akid) +{ + return ExtractKIDFromX509Cert(false, certificate, akid); +} + CHIP_ERROR ExtractVIDFromX509Cert(const ByteSpan & certificate, VendorId & vid) { CHIP_ERROR err = CHIP_NO_ERROR; diff --git a/src/crypto/CHIPCryptoPALmbedTLS.cpp b/src/crypto/CHIPCryptoPALmbedTLS.cpp index 6525beed44609c..bf73921fd9c90b 100644 --- a/src/crypto/CHIPCryptoPALmbedTLS.cpp +++ b/src/crypto/CHIPCryptoPALmbedTLS.cpp @@ -1304,7 +1304,9 @@ CHIP_ERROR ExtractPubkeyFromX509Cert(const ByteSpan & certificate, Crypto::P256P return error; } -CHIP_ERROR ExtractAKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan & akid) +namespace { + +CHIP_ERROR ExtractKIDFromX509Cert(bool isSKID, const ByteSpan & certificate, MutableByteSpan & kid) { #if defined(MBEDTLS_X509_CRT_PARSE_C) CHIP_ERROR error = CHIP_NO_ERROR; @@ -1312,6 +1314,8 @@ CHIP_ERROR ExtractAKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan unsigned char * p; const unsigned char * end; size_t len; + + constexpr uint8_t sOID_Extension_SubjectKeyIdentifier[] = { 0x55, 0x1D, 0x0E }; constexpr uint8_t sOID_Extension_AuthorityKeyIdentifier[] = { 0x55, 0x1D, 0x23 }; mbedtls_x509_crt_init(&mbed_cert); @@ -1334,22 +1338,34 @@ CHIP_ERROR ExtractAKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID); VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - if (sizeof(sOID_Extension_AuthorityKeyIdentifier) == len && memcmp(p, sOID_Extension_AuthorityKeyIdentifier, len) == 0) + bool isRequiredKID = false; + if (isSKID) + { + isRequiredKID = + sizeof(sOID_Extension_SubjectKeyIdentifier) == len && memcmp(p, sOID_Extension_SubjectKeyIdentifier, len) == 0; + } + else + { + isRequiredKID = + sizeof(sOID_Extension_AuthorityKeyIdentifier) == len && memcmp(p, sOID_Extension_AuthorityKeyIdentifier, len) == 0; + } + + if (isRequiredKID) { - constexpr size_t keyid_skip = 4; - constexpr size_t akid_size = 20; + size_t keyid_skip = isSKID ? 2 : 4; + constexpr size_t kid_size = 20; p += len; result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - VerifyOrExit((len - keyid_skip) == akid_size, error = CHIP_ERROR_WRONG_CERT_TYPE); - VerifyOrExit((len - keyid_skip) <= akid.size(), error = CHIP_ERROR_BUFFER_TOO_SMALL); + VerifyOrExit((len - keyid_skip) == kid_size, error = CHIP_ERROR_WRONG_CERT_TYPE); + VerifyOrExit((len - keyid_skip) <= kid.size(), error = CHIP_ERROR_BUFFER_TOO_SMALL); - memcpy(akid.data(), p + keyid_skip, akid_size); - if (akid.size() > akid_size) + memcpy(kid.data(), p + keyid_skip, kid_size); + if (kid.size() > kid_size) { - akid.reduce_size(akid_size); + kid.reduce_size(kid_size); } break; @@ -1370,13 +1386,25 @@ CHIP_ERROR ExtractAKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan #else (void) certificate; - (void) akid; + (void) kid; CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; #endif // defined(MBEDTLS_X509_CRT_PARSE_C) return error; } +} // namespace + +CHIP_ERROR ExtractSKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan & skid) +{ + return ExtractKIDFromX509Cert(true, certificate, skid); +} + +CHIP_ERROR ExtractAKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan & akid) +{ + return ExtractKIDFromX509Cert(false, certificate, akid); +} + CHIP_ERROR ExtractVIDFromX509Cert(const ByteSpan & certificate, VendorId & vid) { #if defined(MBEDTLS_X509_CRT_PARSE_C) diff --git a/src/crypto/tests/CHIPCryptoPALTest.cpp b/src/crypto/tests/CHIPCryptoPALTest.cpp index 12f8bbb9efd3ae..d22e12f17f8988 100644 --- a/src/crypto/tests/CHIPCryptoPALTest.cpp +++ b/src/crypto/tests/CHIPCryptoPALTest.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1816,8 +1816,7 @@ static void TestPubkey_x509Extraction(nlTestSuite * inSuite, void * inContext) P256PublicKey publicKey; ByteSpan cert; - const uint8_t * certPubkey; - uint32_t certPubkeyLen; + ByteSpan pubkeySpan; for (size_t i = 0; i < gNumTestCerts; i++) { @@ -1825,12 +1824,13 @@ static void TestPubkey_x509Extraction(nlTestSuite * inSuite, void * inContext) err = GetTestCert(certType, TestCertLoadFlags::kDERForm, cert); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - err = GetTestCertPubkey(certType, &certPubkey, certPubkeyLen); + err = GetTestCertPubkey(certType, pubkeySpan); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); err = ExtractPubkeyFromX509Cert(cert, publicKey); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, memcmp(publicKey.ConstBytes(), certPubkey, certPubkeyLen) == 0); + NL_TEST_ASSERT(inSuite, publicKey.Length() == pubkeySpan.size()); + NL_TEST_ASSERT(inSuite, memcmp(publicKey.ConstBytes(), pubkeySpan.data(), pubkeySpan.size()) == 0); } } @@ -1868,6 +1868,33 @@ static void TestX509_CertChainValidation(nlTestSuite * inSuite, void * inContext NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); } +static void TestSKID_x509Extraction(nlTestSuite * inSuite, void * inContext) +{ + using namespace TestCerts; + + HeapChecker heapChecker(inSuite); + CHIP_ERROR err = CHIP_NO_ERROR; + uint8_t skidBuf[Credentials::kKeyIdentifierLength]; + MutableByteSpan skidOut(skidBuf); + + ByteSpan cert; + ByteSpan skidSpan; + + for (size_t i = 0; i < gNumTestCerts; i++) + { + uint8_t certType = gTestCerts[i]; + + err = GetTestCert(certType, TestCertLoadFlags::kDERForm, cert); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + err = GetTestCertSKID(certType, skidSpan); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + err = ExtractSKIDFromX509Cert(cert, skidOut); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, skidSpan.data_equal(skidOut)); + } +} + static void TestAKID_x509Extraction(nlTestSuite * inSuite, void * inContext) { using namespace TestCerts; @@ -2020,6 +2047,7 @@ static const nlTest sTests[] = { NL_TEST_DEF("Test x509 Certificate Extraction from PKCS7", TestX509_PKCS7Extraction), #endif // CHIP_CRYPTO_OPENSSL NL_TEST_DEF("Test x509 Certificate Chain Validation", TestX509_CertChainValidation), + NL_TEST_DEF("Test Subject Key Id Extraction from x509 Certificate", TestSKID_x509Extraction), NL_TEST_DEF("Test Authority Key Id Extraction from x509 Certificate", TestAKID_x509Extraction), NL_TEST_DEF("Test Vendor ID Extraction from x509 Attestation Certificate", TestVID_x509Extraction), NL_TEST_SENTINEL()