diff --git a/src/crypto/CHIPCryptoPAL.h b/src/crypto/CHIPCryptoPAL.h index 6df3dc3a631572..3640cdbc372ec9 100644 --- a/src/crypto/CHIPCryptoPAL.h +++ b/src/crypto/CHIPCryptoPAL.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020-2022 Project CHIP Authors + * Copyright (c) 2020-2023 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. @@ -43,13 +43,14 @@ namespace Crypto { constexpr size_t kMax_x509_Certificate_Length = 600; -constexpr size_t kP256_FE_Length = 32; -constexpr size_t kP256_ECDSA_Signature_Length_Raw = (2 * kP256_FE_Length); -constexpr size_t kP256_Point_Length = (2 * kP256_FE_Length + 1); -constexpr size_t kSHA256_Hash_Length = 32; -constexpr size_t kSHA1_Hash_Length = 20; -constexpr size_t kSubjectKeyIdentifierLength = kSHA1_Hash_Length; -constexpr size_t kAuthorityKeyIdentifierLength = kSHA1_Hash_Length; +constexpr size_t kP256_FE_Length = 32; +constexpr size_t kP256_ECDSA_Signature_Length_Raw = (2 * kP256_FE_Length); +constexpr size_t kP256_Point_Length = (2 * kP256_FE_Length + 1); +constexpr size_t kSHA256_Hash_Length = 32; +constexpr size_t kSHA1_Hash_Length = 20; +constexpr size_t kSubjectKeyIdentifierLength = kSHA1_Hash_Length; +constexpr size_t kAuthorityKeyIdentifierLength = kSHA1_Hash_Length; +constexpr size_t kMaxCertificateSerialNumberLength = 20; constexpr size_t CHIP_CRYPTO_GROUP_SIZE_BYTES = kP256_FE_Length; constexpr size_t CHIP_CRYPTO_PUBLIC_KEY_SIZE_BYTES = kP256_Point_Length; @@ -1566,6 +1567,11 @@ CHIP_ERROR ExtractSKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan **/ CHIP_ERROR ExtractAKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan & akid); +/** + * @brief Extracts Serial Number from X509 Certificate. + **/ +CHIP_ERROR ExtractSerialNumberFromX509Cert(const ByteSpan & certificate, MutableByteSpan & serialNumber); + /** * @brief Checks for resigned version of the certificate in the list and returns it. * diff --git a/src/crypto/CHIPCryptoPALOpenSSL.cpp b/src/crypto/CHIPCryptoPALOpenSSL.cpp index ad3180447e8022..aa01544a37e852 100644 --- a/src/crypto/CHIPCryptoPALOpenSSL.cpp +++ b/src/crypto/CHIPCryptoPALOpenSSL.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020-2022 Project CHIP Authors + * Copyright (c) 2020-2023 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. @@ -1970,6 +1970,37 @@ CHIP_ERROR ExtractAKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan return ExtractKIDFromX509Cert(false, certificate, akid); } +CHIP_ERROR ExtractSerialNumberFromX509Cert(const ByteSpan & certificate, MutableByteSpan & serialNumber) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + X509 * x509certificate = nullptr; + const unsigned char * pCertificate = certificate.data(); + const unsigned char ** ppCertificate = &pCertificate; + const ASN1_INTEGER * serialNumberASN1 = nullptr; + size_t serialNumberLen = 0; + + VerifyOrReturnError(!certificate.empty() && CanCastTo(certificate.size()), CHIP_ERROR_INVALID_ARGUMENT); + + x509certificate = d2i_X509(nullptr, ppCertificate, static_cast(certificate.size())); + VerifyOrExit(x509certificate != nullptr, err = CHIP_ERROR_NO_MEMORY); + + serialNumberASN1 = X509_get_serialNumber(x509certificate); + VerifyOrExit(serialNumberASN1 != nullptr, err = CHIP_ERROR_INTERNAL); + VerifyOrExit(serialNumberASN1->data != nullptr, err = CHIP_ERROR_INTERNAL); + VerifyOrExit(CanCastTo(serialNumberASN1->length), err = CHIP_ERROR_INTERNAL); + + serialNumberLen = static_cast(serialNumberASN1->length); + VerifyOrExit(serialNumberLen <= serialNumber.size(), err = CHIP_ERROR_BUFFER_TOO_SMALL); + + memcpy(serialNumber.data(), serialNumberASN1->data, serialNumberLen); + serialNumber.reduce_size(serialNumberLen); + +exit: + X509_free(x509certificate); + + return err; +} + CHIP_ERROR ExtractVIDPIDFromX509Cert(const ByteSpan & certificate, AttestationCertVidPid & vidpid) { ASN1_OBJECT * commonNameObj = OBJ_txt2obj("2.5.4.3", 1); diff --git a/src/crypto/CHIPCryptoPALPSA.cpp b/src/crypto/CHIPCryptoPALPSA.cpp index 3fc7c31e5dd4f2..6a5e488031caac 100644 --- a/src/crypto/CHIPCryptoPALPSA.cpp +++ b/src/crypto/CHIPCryptoPALPSA.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2022 Project CHIP Authors + * Copyright (c) 2022-2023 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. @@ -1651,6 +1651,40 @@ CHIP_ERROR ExtractAKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan return ExtractKIDFromX509Cert(false, certificate, akid); } +CHIP_ERROR ExtractSerialNumberFromX509Cert(const ByteSpan & certificate, MutableByteSpan & serialNumber) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + uint8_t * p = nullptr; + size_t len = 0; + mbedtls_x509_crt mbed_cert; + + mbedtls_x509_crt_init(&mbed_cert); + + result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + p = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(serial).CHIP_CRYPTO_PAL_PRIVATE_X509(p); + len = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(serial).CHIP_CRYPTO_PAL_PRIVATE_X509(len); + VerifyOrExit(len <= serialNumber.size(), error = CHIP_ERROR_BUFFER_TOO_SMALL); + + memcpy(serialNumber.data(), p, len); + serialNumber.reduce_size(len); + +exit: + _log_mbedTLS_error(result); + mbedtls_x509_crt_free(&mbed_cert); + +#else + (void) certificate; + (void) serialNumber; + CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + + return error; +} + CHIP_ERROR ExtractVIDPIDFromX509Cert(const ByteSpan & certificate, AttestationCertVidPid & vidpid) { #if defined(MBEDTLS_X509_CRT_PARSE_C) diff --git a/src/crypto/CHIPCryptoPALmbedTLS.cpp b/src/crypto/CHIPCryptoPALmbedTLS.cpp index ae887e755c2330..ec11cd17a9f8aa 100644 --- a/src/crypto/CHIPCryptoPALmbedTLS.cpp +++ b/src/crypto/CHIPCryptoPALmbedTLS.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020-2022 Project CHIP Authors + * Copyright (c) 2020-2023 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. @@ -1768,6 +1768,40 @@ CHIP_ERROR ExtractAKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan return ExtractKIDFromX509Cert(false, certificate, akid); } +CHIP_ERROR ExtractSerialNumberFromX509Cert(const ByteSpan & certificate, MutableByteSpan & serialNumber) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + uint8_t * p = nullptr; + size_t len = 0; + mbedtls_x509_crt mbed_cert; + + mbedtls_x509_crt_init(&mbed_cert); + + result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + p = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(serial).CHIP_CRYPTO_PAL_PRIVATE_X509(p); + len = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(serial).CHIP_CRYPTO_PAL_PRIVATE_X509(len); + VerifyOrExit(len <= serialNumber.size(), error = CHIP_ERROR_BUFFER_TOO_SMALL); + + memcpy(serialNumber.data(), p, len); + serialNumber.reduce_size(len); + +exit: + _log_mbedTLS_error(result); + mbedtls_x509_crt_free(&mbed_cert); + +#else + (void) certificate; + (void) serialNumber; + CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + + return error; +} + CHIP_ERROR ExtractVIDPIDFromX509Cert(const ByteSpan & certificate, AttestationCertVidPid & vidpid) { #if defined(MBEDTLS_X509_CRT_PARSE_C) diff --git a/src/crypto/tests/CHIPCryptoPALTest.cpp b/src/crypto/tests/CHIPCryptoPALTest.cpp index f8d4b476d719c4..269acf11fb428b 100644 --- a/src/crypto/tests/CHIPCryptoPALTest.cpp +++ b/src/crypto/tests/CHIPCryptoPALTest.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020-2022 Project CHIP Authors + * Copyright (c) 2020-2023 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. @@ -2133,6 +2133,47 @@ static void TestAKID_x509Extraction(nlTestSuite * inSuite, void * inContext) } } +static void TestSerialNumber_x509Extraction(nlTestSuite * inSuite, void * inContext) +{ + using namespace TestCerts; + + HeapChecker heapChecker(inSuite); + CHIP_ERROR err = CHIP_NO_ERROR; + + struct SerialNumberTestCase + { + uint8_t Cert; + ByteSpan mExpectedResult; + }; + + const uint8_t serialNumberRoot01[] = { 0x53, 0x4c, 0x45, 0x82, 0x73, 0x62, 0x35, 0x14 }; + const uint8_t serialNumberICA01[] = { 0x69, 0xd8, 0x6a, 0x8d, 0x80, 0xfc, 0x8f, 0x5d }; + const uint8_t serialNumberNode02_08[] = { 0x3e, 0x67, 0x94, 0x70, 0x7a, 0xec, 0xb8, 0x15 }; + + // clang-format off + static SerialNumberTestCase sSerialNumberTestCases[] = { + // Cert Expected Output + // ==================================================== + { TestCert::kRoot01, ByteSpan(serialNumberRoot01) }, + { TestCert::kICA01, ByteSpan(serialNumberICA01) }, + { TestCert::kNode02_08, ByteSpan(serialNumberNode02_08) }, + }; + // clang-format on + + for (auto & testCase : sSerialNumberTestCases) + { + ByteSpan cert; + err = GetTestCert(testCase.Cert, TestCertLoadFlags::kDERForm, cert); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + uint8_t serialNumberBuf[kMaxCertificateSerialNumberLength] = { 0 }; + MutableByteSpan serialNumber(serialNumberBuf); + err = ExtractSerialNumberFromX509Cert(cert, serialNumber); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, serialNumber.data_equal(testCase.mExpectedResult)); + } +} + static void TestVIDPID_StringExtraction(nlTestSuite * inSuite, void * inContext) { HeapChecker heapChecker(inSuite); @@ -2560,6 +2601,7 @@ static const nlTest sTests[] = { NL_TEST_DEF("Test x509 Certificate Timestamp Validation", TestX509_IssuingTimestampValidation), 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 Serial Number Extraction from x509 Certificate", TestSerialNumber_x509Extraction), NL_TEST_DEF("Test Vendor ID and Product ID Extraction from Attribute String", TestVIDPID_StringExtraction), NL_TEST_DEF("Test Vendor ID and Product ID Extraction from x509 Attestation Certificate", TestVIDPID_x509Extraction), NL_TEST_DEF("Test Replace Resigned Certificate Version if Found", TestX509_ReplaceCertIfResignedCertFound),