Skip to content

Commit

Permalink
Implemented ExtractSKIDFromX509Cert() Method. (#10358)
Browse files Browse the repository at this point in the history
This method will be useful for some usecases.
  • Loading branch information
emargolis authored and pull[bot] committed Nov 6, 2021
1 parent f666ea8 commit 4087796
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 58 deletions.
116 changes: 79 additions & 37 deletions src/credentials/tests/CHIPCert_test_vectors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ CHIP_ERROR GetTestCert(uint8_t certType, BitFlags<TestCertLoadFlags> certLoadFla
SELECT_CERT(Node02_05);
SELECT_CERT(Node02_06);
SELECT_CERT(Node02_07);

#undef SELECT_CERT

err = CHIP_ERROR_CA_CERT_NOT_FOUND;
Expand Down Expand Up @@ -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;

Expand All @@ -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) \
Expand All @@ -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;

Expand Down
3 changes: 2 additions & 1 deletion src/credentials/tests/CHIPCert_test_vectors.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ enum class TestCertLoadFlags : uint8_t

extern CHIP_ERROR GetTestCert(uint8_t certType, BitFlags<TestCertLoadFlags> 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<TestCertLoadFlags> certLoadFlags,
BitFlags<CertDecodeFlags> decodeFlags);
Expand Down
5 changes: 5 additions & 0 deletions src/crypto/CHIPCryptoPAL.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
**/
Expand Down
27 changes: 22 additions & 5 deletions src/crypto/CHIPCryptoPALOpenSSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1734,29 +1734,46 @@ 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<long>(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<int>(kid.size()), err = CHIP_ERROR_BUFFER_TOO_SMALL);
VerifyOrExit(CanCastTo<size_t>(kidString->length), err = CHIP_ERROR_INVALID_ARGUMENT);

VerifyOrExit(akidString->length == static_cast<int>(akid.size()), err = CHIP_ERROR_INVALID_MESSAGE_LENGTH);
memcpy(kid.data(), kidString->data, static_cast<size_t>(kidString->length));

memcpy(akid.data(), akidString->data, static_cast<size_t>(akidString->length));
kid.reduce_size(static_cast<size_t>(kidString->length));

exit:
X509_free(x509certificate);

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;
Expand Down
48 changes: 38 additions & 10 deletions src/crypto/CHIPCryptoPALmbedTLS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1304,14 +1304,18 @@ 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;
mbedtls_x509_crt mbed_cert;
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);
Expand All @@ -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;
Expand All @@ -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)
Expand Down
38 changes: 33 additions & 5 deletions src/crypto/tests/CHIPCryptoPALTest.cpp
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -1816,21 +1816,21 @@ 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++)
{
uint8_t certType = TestCerts::gTestCerts[i];

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);
}
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit 4087796

Please sign in to comment.