From e3b0cc0b3e9ddb6a01536a25967e8c7b84498280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damian=20Kr=C3=B3lik?= <66667989+Damian-Nordic@users.noreply.github.com> Date: Tue, 3 Oct 2023 00:35:38 +0200 Subject: [PATCH] [crypto] Extract common X509 functions for mbedTLS and PSA (#29484) * [crypto] Extract X509 functions for mbedTLS and PSA PSA crypto backend uses mbedTLS API to implement functions for certificate generation and parsing, so the functions are duplicated between PSA and mbedTLS backends. Extract the functions to reduce the code duplication. * Restyled by whitespace * Restyled by gn * Code review --------- Co-authored-by: Restyled.io --- src/crypto/BUILD.gn | 12 +- src/crypto/CHIPCryptoPALPSA.cpp | 1089 +---------------------- src/crypto/CHIPCryptoPALmbedTLS.cpp | 1079 +--------------------- src/crypto/CHIPCryptoPALmbedTLS.h | 71 ++ src/crypto/CHIPCryptoPALmbedTLSCert.cpp | 1075 ++++++++++++++++++++++ 5 files changed, 1163 insertions(+), 2163 deletions(-) create mode 100644 src/crypto/CHIPCryptoPALmbedTLS.h create mode 100644 src/crypto/CHIPCryptoPALmbedTLSCert.cpp diff --git a/src/crypto/BUILD.gn b/src/crypto/BUILD.gn index d0fe7190175ed6..8cc7ba400f0ef0 100644 --- a/src/crypto/BUILD.gn +++ b/src/crypto/BUILD.gn @@ -100,7 +100,11 @@ if (chip_crypto == "openssl") { import("//build_overrides/mbedtls.gni") source_set("cryptopal_mbedtls") { - sources = [ "CHIPCryptoPALmbedTLS.cpp" ] + sources = [ + "CHIPCryptoPALmbedTLS.cpp", + "CHIPCryptoPALmbedTLS.h", + "CHIPCryptoPALmbedTLSCert.cpp", + ] public_deps = [ ":public_headers" ] if (!chip_external_mbedtls) { @@ -114,8 +118,8 @@ if (chip_crypto == "openssl") { sources = [ "CHIPCryptoPALPSA.cpp", "CHIPCryptoPALPSA.h", - "PSAOperationalKeystore.cpp", - "PSAOperationalKeystore.h", + "CHIPCryptoPALmbedTLS.h", + "CHIPCryptoPALmbedTLSCert.cpp", ] public_deps = [ ":public_headers" ] @@ -139,6 +143,8 @@ static_library("crypto") { if (chip_crypto == "psa") { sources += [ + "PSAOperationalKeystore.cpp", + "PSAOperationalKeystore.h", "PSASessionKeystore.cpp", "PSASessionKeystore.h", ] diff --git a/src/crypto/CHIPCryptoPALPSA.cpp b/src/crypto/CHIPCryptoPALPSA.cpp index 5f21030eb69a72..af5d375e987e42 100644 --- a/src/crypto/CHIPCryptoPALPSA.cpp +++ b/src/crypto/CHIPCryptoPALPSA.cpp @@ -21,6 +21,7 @@ */ #include "CHIPCryptoPALPSA.h" +#include "CHIPCryptoPALmbedTLS.h" #include #include @@ -37,51 +38,16 @@ #include #include #include -#include -#include #include -#if defined(MBEDTLS_X509_CRT_PARSE_C) -#include -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) #include #include -constexpr size_t kMaxErrorStrLen = 128; - -// In mbedTLS 3.0.0 direct access to structure fields was replaced with using MBEDTLS_PRIVATE macro. -#if (MBEDTLS_VERSION_NUMBER >= 0x03000000) -#define CHIP_CRYPTO_PAL_PRIVATE(x) MBEDTLS_PRIVATE(x) -#else -#define CHIP_CRYPTO_PAL_PRIVATE(x) x -#endif - -#if (MBEDTLS_VERSION_NUMBER >= 0x03000000 && MBEDTLS_VERSION_NUMBER < 0x03010000) -#define CHIP_CRYPTO_PAL_PRIVATE_X509(x) MBEDTLS_PRIVATE(x) -#else -#define CHIP_CRYPTO_PAL_PRIVATE_X509(x) x -#endif - namespace chip { namespace Crypto { namespace { -void logMbedTLSError(int errorCode) -{ - if (errorCode != 0) - { -#if defined(MBEDTLS_ERROR_C) - char errorStr[kMaxErrorStrLen]; - mbedtls_strerror(errorCode, errorStr, sizeof(errorStr)); - ChipLogError(Crypto, "mbedTLS error: %s", errorStr); -#else - // Error codes defined in 16-bit negative hex numbers. Ease lookup by printing likewise - ChipLogError(Crypto, "mbedTLS error: -0x%04X", static_cast(-errorCode)); -#endif - } -} - void logPsaError(psa_status_t status) { if (status != 0) @@ -511,17 +477,6 @@ static int CryptoRNG(void * ctxt, uint8_t * out_buffer, size_t out_length) return (chip::Crypto::DRBG_get_bytes(out_buffer, out_length) == CHIP_NO_ERROR) ? 0 : 1; } -mbedtls_ecp_group_id MapECPGroupId(SupportedECPKeyTypes keyType) -{ - switch (keyType) - { - case SupportedECPKeyTypes::ECP256R1: - return MBEDTLS_ECP_DP_SECP256R1; - default: - return MBEDTLS_ECP_DP_NONE; - } -} - CHIP_ERROR P256Keypair::ECDSA_sign_msg(const uint8_t * msg, const size_t msg_length, P256ECDSASignature & out_signature) const { VerifyOrReturnError(mInitialized, CHIP_ERROR_WELL_UNINITIALIZED); @@ -782,67 +737,6 @@ CHIP_ERROR P256Keypair::NewCertificateSigningRequest(uint8_t * out_csr, size_t & return CHIP_NO_ERROR; } -CHIP_ERROR VerifyCertificateSigningRequest(const uint8_t * csr_buf, size_t csr_length, P256PublicKey & pubkey) -{ -#if defined(MBEDTLS_X509_CSR_PARSE_C) - ReturnErrorOnFailure(VerifyCertificateSigningRequestFormat(csr_buf, csr_length)); - - // TODO: For some embedded targets, mbedTLS library doesn't have mbedtls_x509_csr_parse_der, and mbedtls_x509_csr_parse_free. - // Taking a step back, embedded targets likely will not process CSR requests. Adding this action item to reevaluate - // this if there's a need for this processing for embedded targets. - CHIP_ERROR error = CHIP_NO_ERROR; - size_t pubkey_size = 0; - - mbedtls_ecp_keypair * keypair = nullptr; - - P256ECDSASignature signature; - MutableByteSpan out_raw_sig_span(signature.Bytes(), signature.Capacity()); - - mbedtls_x509_csr csr; - mbedtls_x509_csr_init(&csr); - - int result = mbedtls_x509_csr_parse_der(&csr, csr_buf, csr_length); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - // Verify the signature algorithm and public key type - VerifyOrExit(csr.CHIP_CRYPTO_PAL_PRIVATE(sig_md) == MBEDTLS_MD_SHA256, error = CHIP_ERROR_UNSUPPORTED_SIGNATURE_TYPE); - VerifyOrExit(csr.CHIP_CRYPTO_PAL_PRIVATE(sig_pk) == MBEDTLS_PK_ECDSA, error = CHIP_ERROR_WRONG_KEY_TYPE); - - keypair = mbedtls_pk_ec(csr.CHIP_CRYPTO_PAL_PRIVATE_X509(pk)); - - // Copy the public key from the CSR - result = mbedtls_ecp_point_write_binary(&keypair->CHIP_CRYPTO_PAL_PRIVATE(grp), &keypair->CHIP_CRYPTO_PAL_PRIVATE(Q), - MBEDTLS_ECP_PF_UNCOMPRESSED, &pubkey_size, Uint8::to_uchar(pubkey), pubkey.Length()); - - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - VerifyOrExit(pubkey_size == pubkey.Length(), error = CHIP_ERROR_INTERNAL); - - // Convert DER signature to raw signature - error = EcdsaAsn1SignatureToRaw(kP256_FE_Length, - ByteSpan{ csr.CHIP_CRYPTO_PAL_PRIVATE(sig).CHIP_CRYPTO_PAL_PRIVATE_X509(p), - csr.CHIP_CRYPTO_PAL_PRIVATE(sig).CHIP_CRYPTO_PAL_PRIVATE_X509(len) }, - out_raw_sig_span); - - VerifyOrExit(error == CHIP_NO_ERROR, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(out_raw_sig_span.size() == (kP256_FE_Length * 2), error = CHIP_ERROR_INTERNAL); - signature.SetLength(out_raw_sig_span.size()); - - // Verify the signature using the public key - error = pubkey.ECDSA_validate_msg_signature(csr.CHIP_CRYPTO_PAL_PRIVATE_X509(cri).CHIP_CRYPTO_PAL_PRIVATE_X509(p), - csr.CHIP_CRYPTO_PAL_PRIVATE_X509(cri).CHIP_CRYPTO_PAL_PRIVATE_X509(len), signature); - - SuccessOrExit(error); - -exit: - mbedtls_x509_csr_free(&csr); - logMbedTLSError(result); - return error; -#else - ChipLogError(Crypto, "MBEDTLS_X509_CSR_PARSE_C is not enabled. CSR cannot be parsed"); - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; -#endif -} - typedef struct Spake2p_Context { mbedtls_ecp_group curve; @@ -910,7 +804,7 @@ CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::InitInternal(void) return error; exit: - logMbedTLSError(result); + _log_mbedTLS_error(result); Clear(); return error; } @@ -963,7 +857,7 @@ CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::MacVerify(const uint8_t * key, size_t VerifyOrExit(IsBufferContentEqualConstantTime(mac, computed_mac, kSHA256_Hash_Length), error = CHIP_ERROR_INTERNAL); exit: - logMbedTLSError(result); + _log_mbedTLS_error(result); return error; } @@ -979,7 +873,7 @@ CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::FELoad(const uint8_t * in, size_t in_l VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); exit: - logMbedTLSError(result); + _log_mbedTLS_error(result); return error; } @@ -1003,7 +897,7 @@ CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::FEGenerate(void * fe) VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); exit: - logMbedTLSError(result); + _log_mbedTLS_error(result); return error; } @@ -1019,7 +913,7 @@ CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::FEMul(void * fer, const void * fe1, co VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); exit: - logMbedTLSError(result); + _log_mbedTLS_error(result); return error; } @@ -1128,7 +1022,7 @@ CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::ComputeL(uint8_t * Lout, size_t * L_le VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); exit: - logMbedTLSError(result); + _log_mbedTLS_error(result); mbedtls_ecp_point_free(&Ltemp); mbedtls_mpi_free(&w1_bn); mbedtls_ecp_group_free(&curve); @@ -1148,974 +1042,5 @@ CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointIsValid(void * R) return CHIP_NO_ERROR; } -namespace { - -#if defined(MBEDTLS_X509_CRT_PARSE_C) -bool IsTimeGreaterThanEqual(const mbedtls_x509_time * const timeA, const mbedtls_x509_time * const timeB) -{ - - // checks if two values are different and if yes, then returns first > second. -#define RETURN_STRICTLY_GREATER_IF_DIFFERENT(component) \ - { \ - auto valueA = timeA->CHIP_CRYPTO_PAL_PRIVATE_X509(component); \ - auto valueB = timeB->CHIP_CRYPTO_PAL_PRIVATE_X509(component); \ - \ - if (valueA != valueB) \ - { \ - return valueA > valueB; \ - } \ - } - - RETURN_STRICTLY_GREATER_IF_DIFFERENT(year); - RETURN_STRICTLY_GREATER_IF_DIFFERENT(mon); - RETURN_STRICTLY_GREATER_IF_DIFFERENT(day); - RETURN_STRICTLY_GREATER_IF_DIFFERENT(hour); - RETURN_STRICTLY_GREATER_IF_DIFFERENT(min); - RETURN_STRICTLY_GREATER_IF_DIFFERENT(sec); - - // all above are equal - return true; -} - -CHIP_ERROR IsCertificateValidAtIssuance(const mbedtls_x509_crt * candidateCertificate, const mbedtls_x509_crt * issuerCertificate) -{ - mbedtls_x509_time candidateNotBeforeTime = candidateCertificate->CHIP_CRYPTO_PAL_PRIVATE_X509(valid_from); - mbedtls_x509_time issuerNotBeforeTime = issuerCertificate->CHIP_CRYPTO_PAL_PRIVATE_X509(valid_from); - mbedtls_x509_time issuerNotAfterTime = issuerCertificate->CHIP_CRYPTO_PAL_PRIVATE_X509(valid_to); - - // check if candidateCertificate is issued at or after issuerCertificate's notBefore timestamp - VerifyOrReturnError(IsTimeGreaterThanEqual(&candidateNotBeforeTime, &issuerNotBeforeTime), CHIP_ERROR_CERT_EXPIRED); - - // check if candidateCertificate is issued at or before issuerCertificate's notAfter timestamp - VerifyOrReturnError(IsTimeGreaterThanEqual(&issuerNotAfterTime, &candidateNotBeforeTime), CHIP_ERROR_CERT_EXPIRED); - - return CHIP_NO_ERROR; -} - -int CallbackForCustomValidityCheck(void * data, mbedtls_x509_crt * crt, int depth, uint32_t * flags) -{ - mbedtls_x509_crt * leafCert = reinterpret_cast(data); - mbedtls_x509_crt * issuerCert = crt; - - // Ignore any time validy error performed by the standard mbedTLS code. - *flags &= ~(static_cast(MBEDTLS_X509_BADCERT_EXPIRED | MBEDTLS_X509_BADCERT_FUTURE)); - - // Verify that the leaf certificate has a notBefore time valid within the validity period of the issuerCertificate. - // Note that this callback is invoked for each certificate in the chain. - if (IsCertificateValidAtIssuance(leafCert, issuerCert) != CHIP_NO_ERROR) - { - return MBEDTLS_ERR_X509_INVALID_DATE; - } - - return 0; -} - -constexpr uint8_t sOID_AttributeType_CommonName[] = { 0x55, 0x04, 0x03 }; -constexpr uint8_t sOID_AttributeType_MatterVendorId[] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x02, 0x01 }; -constexpr uint8_t sOID_AttributeType_MatterProductId[] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x02, 0x02 }; -constexpr uint8_t sOID_SigAlgo_ECDSAWithSHA256[] = { 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02 }; -constexpr uint8_t sOID_Extension_BasicConstraints[] = { 0x55, 0x1D, 0x13 }; -constexpr uint8_t sOID_Extension_KeyUsage[] = { 0x55, 0x1D, 0x0F }; -constexpr uint8_t sOID_Extension_SubjectKeyIdentifier[] = { 0x55, 0x1D, 0x0E }; -constexpr uint8_t sOID_Extension_AuthorityKeyIdentifier[] = { 0x55, 0x1D, 0x23 }; -constexpr uint8_t sOID_Extension_CRLDistributionPoint[] = { 0x55, 0x1D, 0x1F }; - -/** - * Compares an mbedtls_asn1_buf structure (oidBuf) to a reference OID represented as uint8_t array (oid). - */ -#define OID_CMP(oid, oidBuf) \ - ((MBEDTLS_ASN1_OID == (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(tag)) && \ - (sizeof(oid) == (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(len)) && \ - (memcmp((oid), (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(p), (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(len)) == 0)) - -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - -} // anonymous namespace - -CHIP_ERROR VerifyAttestationCertificateFormat(const ByteSpan & cert, AttestationCertType certType) -{ -#if defined(MBEDTLS_X509_CRT_PARSE_C) - CHIP_ERROR error = CHIP_NO_ERROR; - int result = 0; - mbedtls_x509_crt mbed_cert; - unsigned char * p = nullptr; - const unsigned char * end = nullptr; - size_t len = 0; - bool extBasicPresent = false; - bool extKeyUsagePresent = false; - - VerifyOrReturnError(!cert.empty(), CHIP_ERROR_INVALID_ARGUMENT); - - mbedtls_x509_crt_init(&mbed_cert); - - result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(cert.data()), cert.size()); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - // "version" value is 1 higher than the actual encoded value. - VerifyOrExit(mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(version) - 1 == 2, error = CHIP_ERROR_INTERNAL); - - // Verify signature algorithms is ECDSA with SHA256. - VerifyOrExit(OID_CMP(sOID_SigAlgo_ECDSAWithSHA256, mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(sig_oid)), - error = CHIP_ERROR_INTERNAL); - - // Verify public key presence and format. - { - Crypto::P256PublicKey pubkey; - SuccessOrExit(error = ExtractPubkeyFromX509Cert(cert, pubkey)); - } - - p = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p); - end = p + mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(len); - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - while (p < end) - { - mbedtls_x509_buf extOID = { 0, 0, nullptr }; - int extCritical = 0; - - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - /* Get extension ID */ - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - extOID.CHIP_CRYPTO_PAL_PRIVATE_X509(tag) = MBEDTLS_ASN1_OID; - extOID.CHIP_CRYPTO_PAL_PRIVATE_X509(len) = len; - extOID.CHIP_CRYPTO_PAL_PRIVATE_X509(p) = p; - p += len; - - /* Get optional critical */ - result = mbedtls_asn1_get_bool(&p, end, &extCritical); - VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_INTERNAL); - - /* Data should be octet string type */ - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - if (OID_CMP(sOID_Extension_BasicConstraints, extOID)) - { - int isCA = 0; - int pathLen = -1; - - VerifyOrExit(extCritical, error = CHIP_ERROR_INTERNAL); - extBasicPresent = true; - - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - if (len > 0) - { - unsigned char * seqStart = p; - result = mbedtls_asn1_get_bool(&p, end, &isCA); - VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_INTERNAL); - - // Check if pathLen is there by validating if the cursor didn't get to the end of - // of the internal SEQUENCE for the basic constraints encapsulation. - // Missing pathLen optional tag will leave pathLen == -1 for following checks. - bool hasPathLen = (p != (seqStart + len)); - if (hasPathLen) - { - // Extract pathLen value, making sure it's a valid format. - result = mbedtls_asn1_get_int(&p, end, &pathLen); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - } - } - - if (certType == AttestationCertType::kDAC) - { - VerifyOrExit(!isCA && pathLen == -1, error = CHIP_ERROR_INTERNAL); - } - else if (certType == AttestationCertType::kPAI) - { - VerifyOrExit(isCA && pathLen == 0, error = CHIP_ERROR_INTERNAL); - } - else - { - // For PAA, pathlen must be absent or equal to 1 (see Matter 1.1 spec 6.2.2.5) - VerifyOrExit(isCA && (pathLen == -1 || pathLen == 1), error = CHIP_ERROR_INTERNAL); - } - } - else if (OID_CMP(sOID_Extension_KeyUsage, extOID)) - { - mbedtls_x509_bitstring bs = { 0, 0, nullptr }; - unsigned int keyUsage = 0; - - VerifyOrExit(extCritical, error = CHIP_ERROR_INTERNAL); - extKeyUsagePresent = true; - - result = mbedtls_asn1_get_bitstring(&p, p + len, &bs); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - for (size_t i = 0; i < bs.CHIP_CRYPTO_PAL_PRIVATE_X509(len) && i < sizeof(unsigned int); i++) - { - keyUsage |= static_cast(bs.CHIP_CRYPTO_PAL_PRIVATE_X509(p)[i]) << (8 * i); - } - - if (certType == AttestationCertType::kDAC) - { - // SHALL only have the digitalSignature bit set. - VerifyOrExit(keyUsage == MBEDTLS_X509_KU_DIGITAL_SIGNATURE, error = CHIP_ERROR_INTERNAL); - } - else - { - bool keyCertSignFlag = keyUsage & MBEDTLS_X509_KU_KEY_CERT_SIGN; - bool crlSignFlag = keyUsage & MBEDTLS_X509_KU_CRL_SIGN; - bool otherFlags = - keyUsage & ~(MBEDTLS_X509_KU_CRL_SIGN | MBEDTLS_X509_KU_KEY_CERT_SIGN | MBEDTLS_X509_KU_DIGITAL_SIGNATURE); - VerifyOrExit(keyCertSignFlag && crlSignFlag && !otherFlags, error = CHIP_ERROR_INTERNAL); - } - } - else - { - p += len; - } - } - - // Verify basic and key usage extensions are present. - VerifyOrExit(extBasicPresent && extKeyUsagePresent, error = CHIP_ERROR_INTERNAL); - - // Verify that SKID and AKID extensions are present. - { - uint8_t kidBuf[kSubjectKeyIdentifierLength]; - MutableByteSpan kid(kidBuf); - SuccessOrExit(error = ExtractSKIDFromX509Cert(cert, kid)); - if (certType == AttestationCertType::kDAC || certType == AttestationCertType::kPAI) - { - // Mandatory extension for DAC and PAI certs. - SuccessOrExit(error = ExtractAKIDFromX509Cert(cert, kid)); - } - } - -exit: - logMbedTLSError(result); - mbedtls_x509_crt_free(&mbed_cert); - -#else - (void) cert; - (void) certType; - CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - - return error; -} - -CHIP_ERROR ValidateCertificateChain(const uint8_t * rootCertificate, size_t rootCertificateLen, const uint8_t * caCertificate, - size_t caCertificateLen, const uint8_t * leafCertificate, size_t leafCertificateLen, - CertificateChainValidationResult & result) -{ -#if defined(MBEDTLS_X509_CRT_PARSE_C) - CHIP_ERROR error = CHIP_NO_ERROR; - mbedtls_x509_crt certChain; - mbedtls_x509_crt rootCert; - int mbedResult; - uint32_t flags = 0; - - result = CertificateChainValidationResult::kInternalFrameworkError; - - VerifyOrReturnError(rootCertificate != nullptr && rootCertificateLen != 0, - (result = CertificateChainValidationResult::kRootArgumentInvalid, CHIP_ERROR_INVALID_ARGUMENT)); - VerifyOrReturnError(leafCertificate != nullptr && leafCertificateLen != 0, - (result = CertificateChainValidationResult::kLeafArgumentInvalid, CHIP_ERROR_INVALID_ARGUMENT)); - - mbedtls_x509_crt_init(&certChain); - mbedtls_x509_crt_init(&rootCert); - - /* Start of chain */ - mbedResult = mbedtls_x509_crt_parse(&certChain, Uint8::to_const_uchar(leafCertificate), leafCertificateLen); - VerifyOrExit(mbedResult == 0, (result = CertificateChainValidationResult::kLeafFormatInvalid, error = CHIP_ERROR_INTERNAL)); - - /* Add the intermediate to the chain, if present */ - if (caCertificate != nullptr && caCertificateLen > 0) - { - mbedResult = mbedtls_x509_crt_parse(&certChain, Uint8::to_const_uchar(caCertificate), caCertificateLen); - VerifyOrExit(mbedResult == 0, (result = CertificateChainValidationResult::kICAFormatInvalid, error = CHIP_ERROR_INTERNAL)); - } - - /* Parse the root cert */ - mbedResult = mbedtls_x509_crt_parse(&rootCert, Uint8::to_const_uchar(rootCertificate), rootCertificateLen); - VerifyOrExit(mbedResult == 0, (result = CertificateChainValidationResult::kRootFormatInvalid, error = CHIP_ERROR_INTERNAL)); - - /* Verify the chain against the root */ - mbedResult = - mbedtls_x509_crt_verify(&certChain, &rootCert, nullptr, nullptr, &flags, CallbackForCustomValidityCheck, &certChain); - - switch (mbedResult) - { - case 0: - VerifyOrExit(flags == 0, (result = CertificateChainValidationResult::kInternalFrameworkError, error = CHIP_ERROR_INTERNAL)); - result = CertificateChainValidationResult::kSuccess; - break; - case MBEDTLS_ERR_X509_INVALID_DATE: - case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED: - result = CertificateChainValidationResult::kChainInvalid; - error = CHIP_ERROR_CERT_NOT_TRUSTED; - break; - default: - result = CertificateChainValidationResult::kInternalFrameworkError; - error = CHIP_ERROR_INTERNAL; - break; - } - -exit: - logMbedTLSError(mbedResult); - mbedtls_x509_crt_free(&certChain); - mbedtls_x509_crt_free(&rootCert); - -#else - (void) rootCertificate; - (void) rootCertificateLen; - (void) caCertificate; - (void) caCertificateLen; - (void) leafCertificate; - (void) leafCertificateLen; - (void) result; - CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - - return error; -} - -CHIP_ERROR IsCertificateValidAtIssuance(const ByteSpan & candidateCertificate, const ByteSpan & issuerCertificate) -{ -#if defined(MBEDTLS_X509_CRT_PARSE_C) - CHIP_ERROR error = CHIP_NO_ERROR; - mbedtls_x509_crt mbedCandidateCertificate; - mbedtls_x509_crt mbedIssuerCertificate; - int result; - - VerifyOrReturnError(!candidateCertificate.empty() && !issuerCertificate.empty(), CHIP_ERROR_INVALID_ARGUMENT); - - mbedtls_x509_crt_init(&mbedCandidateCertificate); - mbedtls_x509_crt_init(&mbedIssuerCertificate); - - result = mbedtls_x509_crt_parse(&mbedCandidateCertificate, Uint8::to_const_uchar(candidateCertificate.data()), - candidateCertificate.size()); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - result = - mbedtls_x509_crt_parse(&mbedIssuerCertificate, Uint8::to_const_uchar(issuerCertificate.data()), issuerCertificate.size()); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - // Verify that the candidateCertificate has a notBefore time valid within the validity period of the issuerCertificate. - SuccessOrExit(error = IsCertificateValidAtIssuance(&mbedCandidateCertificate, &mbedIssuerCertificate)); - -exit: - logMbedTLSError(result); - mbedtls_x509_crt_free(&mbedCandidateCertificate); - mbedtls_x509_crt_free(&mbedIssuerCertificate); - -#else - (void) candidateCertificate; - (void) issuerCertificate; - CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - - return error; -} - -CHIP_ERROR IsCertificateValidAtCurrentTime(const ByteSpan & certificate) -{ -#if defined(MBEDTLS_X509_CRT_PARSE_C) - CHIP_ERROR error = CHIP_NO_ERROR; - mbedtls_x509_crt mbedCertificate; - int result; - - VerifyOrReturnError(!certificate.empty(), CHIP_ERROR_INVALID_ARGUMENT); - - mbedtls_x509_crt_init(&mbedCertificate); - - result = mbedtls_x509_crt_parse(&mbedCertificate, Uint8::to_const_uchar(certificate.data()), certificate.size()); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - // check if certificate's notBefore timestamp is earlier than or equal to current time. - result = mbedtls_x509_time_is_past(&mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(valid_from)); - VerifyOrExit(result == 1, error = CHIP_ERROR_CERT_EXPIRED); - - // check if certificate's notAfter timestamp is later than current time. - result = mbedtls_x509_time_is_future(&mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(valid_to)); - VerifyOrExit(result == 1, error = CHIP_ERROR_CERT_EXPIRED); - -exit: - logMbedTLSError(result); - mbedtls_x509_crt_free(&mbedCertificate); - -#else - (void) certificate; - CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - - return error; -} - -CHIP_ERROR ExtractPubkeyFromX509Cert(const ByteSpan & certificate, Crypto::P256PublicKey & pubkey) -{ -#if defined(MBEDTLS_X509_CRT_PARSE_C) - CHIP_ERROR error = CHIP_NO_ERROR; - mbedtls_x509_crt mbed_cert; - mbedtls_ecp_keypair * keypair = nullptr; - size_t pubkey_size = 0; - - mbedtls_x509_crt_init(&mbed_cert); - - int result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - VerifyOrExit(mbedtls_pk_get_type(&(mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(pk))) == MBEDTLS_PK_ECKEY, - error = CHIP_ERROR_INVALID_ARGUMENT); - - keypair = mbedtls_pk_ec(mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(pk)); - VerifyOrExit(keypair->CHIP_CRYPTO_PAL_PRIVATE(grp).id == MapECPGroupId(pubkey.Type()), error = CHIP_ERROR_INVALID_ARGUMENT); - // Copy the public key from the cert in raw point format - result = - mbedtls_ecp_point_write_binary(&keypair->CHIP_CRYPTO_PAL_PRIVATE(grp), &keypair->CHIP_CRYPTO_PAL_PRIVATE(Q), - MBEDTLS_ECP_PF_UNCOMPRESSED, &pubkey_size, Uint8::to_uchar(pubkey.Bytes()), pubkey.Length()); - - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - VerifyOrExit(pubkey_size == pubkey.Length(), error = CHIP_ERROR_INTERNAL); - -exit: - logMbedTLSError(result); - mbedtls_x509_crt_free(&mbed_cert); - -#else - (void) certificate; - (void) pubkey; - CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - - return error; -} - -namespace { - -CHIP_ERROR ExtractKIDFromX509Cert(bool extractSKID, const ByteSpan & certificate, MutableByteSpan & kid) -{ -#if defined(MBEDTLS_X509_CRT_PARSE_C) - CHIP_ERROR error = CHIP_ERROR_NOT_FOUND; - mbedtls_x509_crt mbed_cert; - unsigned char * p = nullptr; - const unsigned char * end = nullptr; - size_t len = 0; - - mbedtls_x509_crt_init(&mbed_cert); - - int result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - // TODO: The mbedTLS team is working on supporting SKID and AKID extensions processing. - // Once it is supported, this code should be updated. - - p = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p); - end = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p) + - mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(len); - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - - while (p < end) - { - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - - mbedtls_x509_buf extOID = { MBEDTLS_ASN1_OID, len, p }; - bool extractCurrentExtSKID = extractSKID && OID_CMP(sOID_Extension_SubjectKeyIdentifier, extOID); - bool extractCurrentExtAKID = !extractSKID && OID_CMP(sOID_Extension_AuthorityKeyIdentifier, extOID); - p += len; - - int is_critical = 0; - result = mbedtls_asn1_get_bool(&p, end, &is_critical); - VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_WRONG_CERT_TYPE); - - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - - if (extractCurrentExtSKID || extractCurrentExtAKID) - { - if (extractCurrentExtSKID) - { - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - } - else - { - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - // Other optional fields, authorityCertIssuer and authorityCertSerialNumber, - // will be skipped if present. - } - VerifyOrExit(len == kSubjectKeyIdentifierLength, error = CHIP_ERROR_WRONG_CERT_TYPE); - VerifyOrExit(len <= kid.size(), error = CHIP_ERROR_BUFFER_TOO_SMALL); - memcpy(kid.data(), p, len); - if (kid.size() > len) - { - kid.reduce_size(len); - } - ExitNow(error = CHIP_NO_ERROR); - break; - } - p += len; - } - -exit: - logMbedTLSError(result); - mbedtls_x509_crt_free(&mbed_cert); - -#else - (void) certificate; - (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 ExtractCRLDistributionPointURIFromX509Cert(const ByteSpan & certificate, MutableCharSpan & cdpurl) -{ -#if defined(MBEDTLS_X509_CRT_PARSE_C) - CHIP_ERROR error = CHIP_ERROR_NOT_FOUND; - mbedtls_x509_crt mbed_cert; - unsigned char * p = nullptr; - const unsigned char * end = nullptr; - size_t len = 0; - size_t cdpExtCount = 0; - - VerifyOrReturnError(!certificate.empty() && CanCastTo(certificate.size()), CHIP_ERROR_INVALID_ARGUMENT); - - mbedtls_x509_crt_init(&mbed_cert); - - int 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(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p); - end = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p) + - mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(len); - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - - while (p < end) - { - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - - mbedtls_x509_buf extOID = { MBEDTLS_ASN1_OID, len, p }; - bool isCurrentExtCDP = OID_CMP(sOID_Extension_CRLDistributionPoint, extOID); - p += len; - - int is_critical = 0; - result = mbedtls_asn1_get_bool(&p, end, &is_critical); - VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_WRONG_CERT_TYPE); - - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - - unsigned char * end_of_ext = p + len; - - if (isCurrentExtCDP) - { - // Only one CRL Distribution Point Extension is allowed. - cdpExtCount++; - VerifyOrExit(cdpExtCount <= 1, error = CHIP_ERROR_NOT_FOUND); - - // CRL Distribution Point Extension is encoded as a sequence of DistributionPoint: - // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint - // - // This implementation only supports a single DistributionPoint (sequence of size 1), - // which is verified by comparing (p + len == end_of_ext) - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); - VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); - - // The DistributionPoint is a sequence of three optional elements: - // DistributionPoint ::= SEQUENCE { - // distributionPoint [0] DistributionPointName OPTIONAL, - // reasons [1] ReasonFlags OPTIONAL, - // cRLIssuer [2] GeneralNames OPTIONAL } - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); - VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); - - // The DistributionPointName is: - // DistributionPointName ::= CHOICE { - // fullName [0] GeneralNames, - // nameRelativeToCRLIssuer [1] RelativeDistinguishedName } - // - // The URI should be encoded in the fullName element. - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0); - VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); - - // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED); - VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); - - unsigned char * end_of_general_names = p + len; - - // The CDP URI is encoded as a uniformResourceIdentifier field of the GeneralName: - // GeneralName ::= CHOICE { - // otherName [0] OtherName, - // rfc822Name [1] IA5String, - // dNSName [2] IA5String, - // x400Address [3] ORAddress, - // directoryName [4] Name, - // ediPartyName [5] EDIPartyName, - // uniformResourceIdentifier [6] IA5String, - // iPAddress [7] OCTET STRING, - // registeredID [8] OBJECT IDENTIFIER } - result = - mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER); - VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); - - // Only single URI instance in the GeneralNames is supported - VerifyOrExit(p + len == end_of_general_names, error = CHIP_ERROR_NOT_FOUND); - - const char * urlptr = reinterpret_cast(p); - VerifyOrExit((len > strlen(kValidCDPURIHttpPrefix) && - strncmp(urlptr, kValidCDPURIHttpPrefix, strlen(kValidCDPURIHttpPrefix)) == 0) || - (len > strlen(kValidCDPURIHttpsPrefix) && - strncmp(urlptr, kValidCDPURIHttpsPrefix, strlen(kValidCDPURIHttpsPrefix)) == 0), - error = CHIP_ERROR_NOT_FOUND); - error = CopyCharSpanToMutableCharSpan(CharSpan(urlptr, len), cdpurl); - SuccessOrExit(error); - } - p = end_of_ext; - } - - VerifyOrExit(cdpExtCount == 1, error = CHIP_ERROR_NOT_FOUND); - -exit: - logMbedTLSError(result); - mbedtls_x509_crt_free(&mbed_cert); - -#else - (void) certificate; - (void) cdpurl; - CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - - return error; -} - -CHIP_ERROR ExtractCDPExtensionCRLIssuerFromX509Cert(const ByteSpan & certificate, MutableByteSpan & crlIssuer) -{ -#if defined(MBEDTLS_X509_CRT_PARSE_C) - CHIP_ERROR error = CHIP_ERROR_NOT_FOUND; - mbedtls_x509_crt mbed_cert; - unsigned char * p = nullptr; - const unsigned char * end = nullptr; - size_t len = 0; - size_t cdpExtCount = 0; - - VerifyOrReturnError(!certificate.empty() && CanCastTo(certificate.size()), CHIP_ERROR_INVALID_ARGUMENT); - - mbedtls_x509_crt_init(&mbed_cert); - - int 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(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p); - end = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p) + - mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(len); - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - - while (p < end) - { - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - - mbedtls_x509_buf extOID = { MBEDTLS_ASN1_OID, len, p }; - bool isCurrentExtCDP = OID_CMP(sOID_Extension_CRLDistributionPoint, extOID); - p += len; - - int is_critical = 0; - result = mbedtls_asn1_get_bool(&p, end, &is_critical); - VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_WRONG_CERT_TYPE); - - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - - unsigned char * end_of_ext = p + len; - - if (isCurrentExtCDP) - { - // Only one CRL Distribution Point Extension is allowed. - cdpExtCount++; - VerifyOrExit(cdpExtCount <= 1, error = CHIP_ERROR_NOT_FOUND); - - // CRL Distribution Point Extension is encoded as a sequence of DistributionPoint: - // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint - // - // This implementation only supports a single DistributionPoint (sequence of size 1), - // which is verified by comparing (p + len == end_of_ext) - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); - VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); - - // The DistributionPoint is a sequence of three optional elements: - // DistributionPoint ::= SEQUENCE { - // distributionPoint [0] DistributionPointName OPTIONAL, - // reasons [1] ReasonFlags OPTIONAL, - // cRLIssuer [2] GeneralNames OPTIONAL } - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); - VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); - - // If distributionPoint element presents, ignore it - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0); - if (result == 0) - { - p += len; - VerifyOrExit(p < end_of_ext, error = CHIP_ERROR_NOT_FOUND); - } - - // Check if cRLIssuer element present - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 2); - VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); - - // The CRL Issuer is encoded as a directoryName field of the GeneralName: - // GeneralName ::= CHOICE { - // otherName [0] OtherName, - // rfc822Name [1] IA5String, - // dNSName [2] IA5String, - // x400Address [3] ORAddress, - // directoryName [4] Name, - // ediPartyName [5] EDIPartyName, - // uniformResourceIdentifier [6] IA5String, - // iPAddress [7] OCTET STRING, - // registeredID [8] OBJECT IDENTIFIER } - result = mbedtls_asn1_get_tag( - &p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_X509_SAN_DIRECTORY_NAME); - VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); - VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); - - error = CopySpanToMutableSpan(ByteSpan(p, len), crlIssuer); - SuccessOrExit(error); - } - p = end_of_ext; - } - - VerifyOrExit(cdpExtCount == 1, error = CHIP_ERROR_NOT_FOUND); - -exit: - logMbedTLSError(result); - mbedtls_x509_crt_free(&mbed_cert); - -#else - (void) certificate; - (void) crlIssuer; - CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - - return error; -} - -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: - logMbedTLSError(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) - CHIP_ERROR error = CHIP_NO_ERROR; - mbedtls_x509_crt mbed_cert; - mbedtls_asn1_named_data * dnIterator = nullptr; - AttestationCertVidPid vidpidFromCN; - - mbedtls_x509_crt_init(&mbed_cert); - - int result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - for (dnIterator = &mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(subject); dnIterator != nullptr; - dnIterator = dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(next)) - { - DNAttrType attrType = DNAttrType::kUnspecified; - if (OID_CMP(sOID_AttributeType_CommonName, dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(oid))) - { - attrType = DNAttrType::kCommonName; - } - else if (OID_CMP(sOID_AttributeType_MatterVendorId, dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(oid))) - { - attrType = DNAttrType::kMatterVID; - } - else if (OID_CMP(sOID_AttributeType_MatterProductId, dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(oid))) - { - attrType = DNAttrType::kMatterPID; - } - - size_t val_len = dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(val).CHIP_CRYPTO_PAL_PRIVATE_X509(len); - uint8_t * val_p = dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(val).CHIP_CRYPTO_PAL_PRIVATE_X509(p); - error = ExtractVIDPIDFromAttributeString(attrType, ByteSpan(val_p, val_len), vidpid, vidpidFromCN); - SuccessOrExit(error); - } - - // If Matter Attributes were not found use values extracted from the CN Attribute, - // which might be uninitialized as well. - if (!vidpid.Initialized()) - { - vidpid = vidpidFromCN; - } - -exit: - logMbedTLSError(result); - mbedtls_x509_crt_free(&mbed_cert); - -#else - (void) certificate; - (void) vidpid; - CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - - return error; -} - -namespace { -CHIP_ERROR ExtractRawDNFromX509Cert(bool extractSubject, const ByteSpan & certificate, MutableByteSpan & dn) -{ -#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 mbedCertificate; - - ReturnErrorCodeIf(certificate.empty(), CHIP_ERROR_INVALID_ARGUMENT); - - mbedtls_x509_crt_init(&mbedCertificate); - result = mbedtls_x509_crt_parse(&mbedCertificate, Uint8::to_const_uchar(certificate.data()), certificate.size()); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - if (extractSubject) - { - len = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(subject_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(len); - p = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(subject_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(p); - } - else - { - len = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(issuer_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(len); - p = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(issuer_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(p); - } - - VerifyOrExit(len <= dn.size(), error = CHIP_ERROR_BUFFER_TOO_SMALL); - memcpy(dn.data(), p, len); - dn.reduce_size(len); - -exit: - logMbedTLSError(result); - mbedtls_x509_crt_free(&mbedCertificate); - -#else - (void) certificate; - (void) dn; - CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - - return error; -} -} // namespace - -CHIP_ERROR ExtractSubjectFromX509Cert(const ByteSpan & certificate, MutableByteSpan & subject) -{ - return ExtractRawDNFromX509Cert(true, certificate, subject); -} - -CHIP_ERROR ExtractIssuerFromX509Cert(const ByteSpan & certificate, MutableByteSpan & issuer) -{ - return ExtractRawDNFromX509Cert(false, certificate, issuer); -} - -CHIP_ERROR ReplaceCertIfResignedCertFound(const ByteSpan & referenceCertificate, const ByteSpan * candidateCertificates, - size_t candidateCertificatesCount, ByteSpan & outCertificate) -{ -#if defined(MBEDTLS_X509_CRT_PARSE_C) - uint8_t referenceSubjectBuf[kMaxCertificateDistinguishedNameLength]; - uint8_t referenceSKIDBuf[kSubjectKeyIdentifierLength]; - MutableByteSpan referenceSubject(referenceSubjectBuf); - MutableByteSpan referenceSKID(referenceSKIDBuf); - - outCertificate = referenceCertificate; - - ReturnErrorCodeIf(candidateCertificates == nullptr || candidateCertificatesCount == 0, CHIP_NO_ERROR); - - ReturnErrorOnFailure(ExtractSubjectFromX509Cert(referenceCertificate, referenceSubject)); - ReturnErrorOnFailure(ExtractSKIDFromX509Cert(referenceCertificate, referenceSKID)); - - for (size_t i = 0; i < candidateCertificatesCount; i++) - { - const ByteSpan candidateCertificate = candidateCertificates[i]; - uint8_t candidateSubjectBuf[kMaxCertificateDistinguishedNameLength]; - uint8_t candidateSKIDBuf[kSubjectKeyIdentifierLength]; - MutableByteSpan candidateSubject(candidateSubjectBuf); - MutableByteSpan candidateSKID(candidateSKIDBuf); - - ReturnErrorOnFailure(ExtractSubjectFromX509Cert(candidateCertificate, candidateSubject)); - ReturnErrorOnFailure(ExtractSKIDFromX509Cert(candidateCertificate, candidateSKID)); - - if (referenceSKID.data_equal(candidateSKID) && referenceSubject.data_equal(candidateSubject)) - { - outCertificate = candidateCertificate; - return CHIP_NO_ERROR; - } - } - - return CHIP_NO_ERROR; -#else - (void) referenceCertificate; - (void) candidateCertificates; - (void) candidateCertificatesCount; - (void) outCertificate; - return CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) -} - } // namespace Crypto } // namespace chip diff --git a/src/crypto/CHIPCryptoPALmbedTLS.cpp b/src/crypto/CHIPCryptoPALmbedTLS.cpp index 0207e303bd9ba6..b8b526fba7e1f9 100644 --- a/src/crypto/CHIPCryptoPALmbedTLS.cpp +++ b/src/crypto/CHIPCryptoPALmbedTLS.cpp @@ -20,6 +20,7 @@ * mbedTLS based implementation of CHIP crypto primitives */ +#include "CHIPCryptoPALmbedTLS.h" #include "CHIPCryptoPAL.h" #include @@ -38,11 +39,6 @@ #include #include #include -#if defined(MBEDTLS_X509_CRT_PARSE_C) -#include -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) -#include -#include #include #include @@ -59,22 +55,6 @@ namespace chip { namespace Crypto { -#define MAX_ERROR_STR_LEN 128 -#define NUM_BYTES_IN_SHA256_HASH 32 - -// In mbedTLS 3.0.0 direct access to structure fields was replaced with using MBEDTLS_PRIVATE macro. -#if (MBEDTLS_VERSION_NUMBER >= 0x03000000) -#define CHIP_CRYPTO_PAL_PRIVATE(x) MBEDTLS_PRIVATE(x) -#else -#define CHIP_CRYPTO_PAL_PRIVATE(x) x -#endif - -#if (MBEDTLS_VERSION_NUMBER >= 0x03000000 && MBEDTLS_VERSION_NUMBER < 0x03010000) -#define CHIP_CRYPTO_PAL_PRIVATE_X509(x) MBEDTLS_PRIVATE(x) -#else -#define CHIP_CRYPTO_PAL_PRIVATE_X509(x) x -#endif - typedef struct { bool mInitialized; @@ -85,21 +65,6 @@ typedef struct static EntropyContext gsEntropyContext; -static void _log_mbedTLS_error(int error_code) -{ - if (error_code != 0) - { -#if defined(MBEDTLS_ERROR_C) - char error_str[MAX_ERROR_STR_LEN]; - mbedtls_strerror(error_code, error_str, sizeof(error_str)); - ChipLogError(Crypto, "mbedTLS error: %s", error_str); -#else - // Error codes defined in 16-bit negative hex numbers. Ease lookup by printing likewise - ChipLogError(Crypto, "mbedTLS error: -0x%04X", -static_cast(error_code)); -#endif - } -} - static bool _isValidTagLength(size_t tag_length) { if (tag_length == 8 || tag_length == 12 || tag_length == 16) @@ -473,17 +438,6 @@ static int CryptoRNG(void * ctxt, uint8_t * out_buffer, size_t out_length) return (chip::Crypto::DRBG_get_bytes(out_buffer, out_length) == CHIP_NO_ERROR) ? 0 : 1; } -mbedtls_ecp_group_id MapECPGroupId(SupportedECPKeyTypes keyType) -{ - switch (keyType) - { - case SupportedECPKeyTypes::ECP256R1: - return MBEDTLS_ECP_DP_SECP256R1; - default: - return MBEDTLS_ECP_DP_NONE; - } -} - static inline mbedtls_ecp_keypair * to_keypair(P256KeypairContext * context) { return SafePointerCast(context); @@ -874,67 +828,6 @@ CHIP_ERROR P256Keypair::NewCertificateSigningRequest(uint8_t * out_csr, size_t & #endif } -CHIP_ERROR VerifyCertificateSigningRequest(const uint8_t * csr_buf, size_t csr_length, P256PublicKey & pubkey) -{ -#if defined(MBEDTLS_X509_CSR_PARSE_C) - ReturnErrorOnFailure(VerifyCertificateSigningRequestFormat(csr_buf, csr_length)); - - // TODO: For some embedded targets, mbedTLS library doesn't have mbedtls_x509_csr_parse_der, and mbedtls_x509_csr_parse_free. - // Taking a step back, embedded targets likely will not process CSR requests. Adding this action item to reevaluate - // this if there's a need for this processing for embedded targets. - CHIP_ERROR error = CHIP_NO_ERROR; - size_t pubkey_size = 0; - - mbedtls_ecp_keypair * keypair = nullptr; - - P256ECDSASignature signature; - MutableByteSpan out_raw_sig_span(signature.Bytes(), signature.Capacity()); - - mbedtls_x509_csr csr; - mbedtls_x509_csr_init(&csr); - - int result = mbedtls_x509_csr_parse_der(&csr, csr_buf, csr_length); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - // Verify the signature algorithm and public key type - VerifyOrExit(csr.CHIP_CRYPTO_PAL_PRIVATE(sig_md) == MBEDTLS_MD_SHA256, error = CHIP_ERROR_UNSUPPORTED_SIGNATURE_TYPE); - VerifyOrExit(csr.CHIP_CRYPTO_PAL_PRIVATE(sig_pk) == MBEDTLS_PK_ECDSA, error = CHIP_ERROR_WRONG_KEY_TYPE); - - keypair = mbedtls_pk_ec(csr.CHIP_CRYPTO_PAL_PRIVATE_X509(pk)); - - // Copy the public key from the CSR - result = mbedtls_ecp_point_write_binary(&keypair->CHIP_CRYPTO_PAL_PRIVATE(grp), &keypair->CHIP_CRYPTO_PAL_PRIVATE(Q), - MBEDTLS_ECP_PF_UNCOMPRESSED, &pubkey_size, Uint8::to_uchar(pubkey), pubkey.Length()); - - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - VerifyOrExit(pubkey_size == pubkey.Length(), error = CHIP_ERROR_INTERNAL); - - // Convert DER signature to raw signature - error = EcdsaAsn1SignatureToRaw(kP256_FE_Length, - ByteSpan{ csr.CHIP_CRYPTO_PAL_PRIVATE(sig).CHIP_CRYPTO_PAL_PRIVATE_X509(p), - csr.CHIP_CRYPTO_PAL_PRIVATE(sig).CHIP_CRYPTO_PAL_PRIVATE_X509(len) }, - out_raw_sig_span); - - VerifyOrExit(error == CHIP_NO_ERROR, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(out_raw_sig_span.size() == (kP256_FE_Length * 2), error = CHIP_ERROR_INTERNAL); - signature.SetLength(out_raw_sig_span.size()); - - // Verify the signature using the public key - error = pubkey.ECDSA_validate_msg_signature(csr.CHIP_CRYPTO_PAL_PRIVATE_X509(cri).CHIP_CRYPTO_PAL_PRIVATE_X509(p), - csr.CHIP_CRYPTO_PAL_PRIVATE_X509(cri).CHIP_CRYPTO_PAL_PRIVATE_X509(len), signature); - - SuccessOrExit(error); - -exit: - mbedtls_x509_csr_free(&csr); - _log_mbedTLS_error(result); - return error; -#else - ChipLogError(Crypto, "MBEDTLS_X509_CSR_PARSE_C is not enabled. CSR cannot be parsed"); - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; -#endif -} - typedef struct Spake2p_Context { mbedtls_ecp_group curve; @@ -1240,975 +1133,5 @@ CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointIsValid(void * R) return CHIP_NO_ERROR; } -namespace { - -#if defined(MBEDTLS_X509_CRT_PARSE_C) -bool IsTimeGreaterThanEqual(const mbedtls_x509_time * const timeA, const mbedtls_x509_time * const timeB) -{ - - // checks if two values are different and if yes, then returns first > second. -#define RETURN_STRICTLY_GREATER_IF_DIFFERENT(component) \ - { \ - auto valueA = timeA->CHIP_CRYPTO_PAL_PRIVATE_X509(component); \ - auto valueB = timeB->CHIP_CRYPTO_PAL_PRIVATE_X509(component); \ - \ - if (valueA != valueB) \ - { \ - return valueA > valueB; \ - } \ - } - - RETURN_STRICTLY_GREATER_IF_DIFFERENT(year); - RETURN_STRICTLY_GREATER_IF_DIFFERENT(mon); - RETURN_STRICTLY_GREATER_IF_DIFFERENT(day); - RETURN_STRICTLY_GREATER_IF_DIFFERENT(hour); - RETURN_STRICTLY_GREATER_IF_DIFFERENT(min); - RETURN_STRICTLY_GREATER_IF_DIFFERENT(sec); - - // all above are equal - return true; -} - -CHIP_ERROR IsCertificateValidAtIssuance(const mbedtls_x509_crt * candidateCertificate, const mbedtls_x509_crt * issuerCertificate) -{ - mbedtls_x509_time candidateNotBeforeTime = candidateCertificate->CHIP_CRYPTO_PAL_PRIVATE_X509(valid_from); - mbedtls_x509_time issuerNotBeforeTime = issuerCertificate->CHIP_CRYPTO_PAL_PRIVATE_X509(valid_from); - mbedtls_x509_time issuerNotAfterTime = issuerCertificate->CHIP_CRYPTO_PAL_PRIVATE_X509(valid_to); - - // check if candidateCertificate is issued at or after issuerCertificate's notBefore timestamp - VerifyOrReturnError(IsTimeGreaterThanEqual(&candidateNotBeforeTime, &issuerNotBeforeTime), CHIP_ERROR_CERT_EXPIRED); - - // check if candidateCertificate is issued at or before issuerCertificate's notAfter timestamp - VerifyOrReturnError(IsTimeGreaterThanEqual(&issuerNotAfterTime, &candidateNotBeforeTime), CHIP_ERROR_CERT_EXPIRED); - - return CHIP_NO_ERROR; -} - -int CallbackForCustomValidityCheck(void * data, mbedtls_x509_crt * crt, int depth, uint32_t * flags) -{ - mbedtls_x509_crt * leafCert = reinterpret_cast(data); - mbedtls_x509_crt * issuerCert = crt; - - // Ignore any time validy error performed by the standard mbedTLS code. - *flags &= ~(static_cast(MBEDTLS_X509_BADCERT_EXPIRED | MBEDTLS_X509_BADCERT_FUTURE)); - - // Verify that the leaf certificate has a notBefore time valid within the validity period of the issuerCertificate. - // Note that this callback is invoked for each certificate in the chain. - if (IsCertificateValidAtIssuance(leafCert, issuerCert) != CHIP_NO_ERROR) - { - return MBEDTLS_ERR_X509_INVALID_DATE; - } - - return 0; -} - -constexpr uint8_t sOID_AttributeType_CommonName[] = { 0x55, 0x04, 0x03 }; -constexpr uint8_t sOID_AttributeType_MatterVendorId[] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x02, 0x01 }; -constexpr uint8_t sOID_AttributeType_MatterProductId[] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x02, 0x02 }; -constexpr uint8_t sOID_SigAlgo_ECDSAWithSHA256[] = { 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02 }; -constexpr uint8_t sOID_Extension_BasicConstraints[] = { 0x55, 0x1D, 0x13 }; -constexpr uint8_t sOID_Extension_KeyUsage[] = { 0x55, 0x1D, 0x0F }; -constexpr uint8_t sOID_Extension_SubjectKeyIdentifier[] = { 0x55, 0x1D, 0x0E }; -constexpr uint8_t sOID_Extension_AuthorityKeyIdentifier[] = { 0x55, 0x1D, 0x23 }; -constexpr uint8_t sOID_Extension_CRLDistributionPoint[] = { 0x55, 0x1D, 0x1F }; - -/** - * Compares an mbedtls_asn1_buf structure (oidBuf) to a reference OID represented as uint8_t array (oid). - */ -#define OID_CMP(oid, oidBuf) \ - ((MBEDTLS_ASN1_OID == (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(tag)) && \ - (sizeof(oid) == (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(len)) && \ - (memcmp((oid), (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(p), (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(len)) == 0)) - -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - -} // anonymous namespace - -CHIP_ERROR VerifyAttestationCertificateFormat(const ByteSpan & cert, AttestationCertType certType) -{ -#if defined(MBEDTLS_X509_CRT_PARSE_C) - CHIP_ERROR error = CHIP_NO_ERROR; - int result = 0; - mbedtls_x509_crt mbed_cert; - unsigned char * p = nullptr; - const unsigned char * end = nullptr; - size_t len = 0; - bool extBasicPresent = false; - bool extKeyUsagePresent = false; - - VerifyOrReturnError(!cert.empty(), CHIP_ERROR_INVALID_ARGUMENT); - - mbedtls_x509_crt_init(&mbed_cert); - - result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(cert.data()), cert.size()); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - // "version" value is 1 higher than the actual encoded value. - VerifyOrExit(mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(version) - 1 == 2, error = CHIP_ERROR_INTERNAL); - - // Verify signature algorithms is ECDSA with SHA256. - VerifyOrExit(OID_CMP(sOID_SigAlgo_ECDSAWithSHA256, mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(sig_oid)), - error = CHIP_ERROR_INTERNAL); - - // Verify public key presence and format. - { - Crypto::P256PublicKey pubkey; - SuccessOrExit(error = ExtractPubkeyFromX509Cert(cert, pubkey)); - } - - p = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p); - end = p + mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(len); - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - while (p < end) - { - mbedtls_x509_buf extOID = { 0, 0, nullptr }; - int extCritical = 0; - - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - /* Get extension ID */ - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - extOID.CHIP_CRYPTO_PAL_PRIVATE_X509(tag) = MBEDTLS_ASN1_OID; - extOID.CHIP_CRYPTO_PAL_PRIVATE_X509(len) = len; - extOID.CHIP_CRYPTO_PAL_PRIVATE_X509(p) = p; - p += len; - - /* Get optional critical */ - result = mbedtls_asn1_get_bool(&p, end, &extCritical); - VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_INTERNAL); - - /* Data should be octet string type */ - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - if (OID_CMP(sOID_Extension_BasicConstraints, extOID)) - { - int isCA = 0; - int pathLen = -1; - - VerifyOrExit(extCritical, error = CHIP_ERROR_INTERNAL); - extBasicPresent = true; - - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - if (len > 0) - { - unsigned char * seqStart = p; - result = mbedtls_asn1_get_bool(&p, end, &isCA); - VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_INTERNAL); - - // Check if pathLen is there by validating if the cursor didn't get to the end of - // of the internal SEQUENCE for the basic constraints encapsulation. - // Missing pathLen optional tag will leave pathLen == -1 for following checks. - bool hasPathLen = (p != (seqStart + len)); - if (hasPathLen) - { - // Extract pathLen value, making sure it's a valid format. - result = mbedtls_asn1_get_int(&p, end, &pathLen); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - } - } - - if (certType == AttestationCertType::kDAC) - { - VerifyOrExit(!isCA && pathLen == -1, error = CHIP_ERROR_INTERNAL); - } - else if (certType == AttestationCertType::kPAI) - { - VerifyOrExit(isCA && pathLen == 0, error = CHIP_ERROR_INTERNAL); - } - else - { - // For PAA, pathlen must be absent or equal to 1 (see Matter 1.1 spec 6.2.2.5) - VerifyOrExit(isCA && (pathLen == -1 || pathLen == 1), error = CHIP_ERROR_INTERNAL); - } - } - else if (OID_CMP(sOID_Extension_KeyUsage, extOID)) - { - mbedtls_x509_bitstring bs = { 0, 0, nullptr }; - unsigned int keyUsage = 0; - - VerifyOrExit(extCritical, error = CHIP_ERROR_INTERNAL); - extKeyUsagePresent = true; - - result = mbedtls_asn1_get_bitstring(&p, p + len, &bs); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - for (size_t i = 0; i < bs.CHIP_CRYPTO_PAL_PRIVATE_X509(len) && i < sizeof(unsigned int); i++) - { - keyUsage |= static_cast(bs.CHIP_CRYPTO_PAL_PRIVATE_X509(p)[i]) << (8 * i); - } - - if (certType == AttestationCertType::kDAC) - { - // SHALL only have the digitalSignature bit set. - VerifyOrExit(keyUsage == MBEDTLS_X509_KU_DIGITAL_SIGNATURE, error = CHIP_ERROR_INTERNAL); - } - else - { - bool keyCertSignFlag = keyUsage & MBEDTLS_X509_KU_KEY_CERT_SIGN; - bool crlSignFlag = keyUsage & MBEDTLS_X509_KU_CRL_SIGN; - bool otherFlags = keyUsage & - ~static_cast(MBEDTLS_X509_KU_CRL_SIGN | MBEDTLS_X509_KU_KEY_CERT_SIGN | - MBEDTLS_X509_KU_DIGITAL_SIGNATURE); - VerifyOrExit(keyCertSignFlag && crlSignFlag && !otherFlags, error = CHIP_ERROR_INTERNAL); - } - } - else - { - p += len; - } - } - - // Verify basic and key usage extensions are present. - VerifyOrExit(extBasicPresent && extKeyUsagePresent, error = CHIP_ERROR_INTERNAL); - - // Verify that SKID and AKID extensions are present. - { - uint8_t kidBuf[kSubjectKeyIdentifierLength]; - MutableByteSpan kid(kidBuf); - SuccessOrExit(error = ExtractSKIDFromX509Cert(cert, kid)); - if (certType == AttestationCertType::kDAC || certType == AttestationCertType::kPAI) - { - // Mandatory extension for DAC and PAI certs. - SuccessOrExit(error = ExtractAKIDFromX509Cert(cert, kid)); - } - } - -exit: - _log_mbedTLS_error(result); - mbedtls_x509_crt_free(&mbed_cert); - -#else - (void) cert; - (void) certType; - CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - - return error; -} - -CHIP_ERROR ValidateCertificateChain(const uint8_t * rootCertificate, size_t rootCertificateLen, const uint8_t * caCertificate, - size_t caCertificateLen, const uint8_t * leafCertificate, size_t leafCertificateLen, - CertificateChainValidationResult & result) -{ -#if defined(MBEDTLS_X509_CRT_PARSE_C) - CHIP_ERROR error = CHIP_NO_ERROR; - mbedtls_x509_crt certChain; - mbedtls_x509_crt rootCert; - int mbedResult; - uint32_t flags = 0; - - result = CertificateChainValidationResult::kInternalFrameworkError; - - VerifyOrReturnError(rootCertificate != nullptr && rootCertificateLen != 0, - (result = CertificateChainValidationResult::kRootArgumentInvalid, CHIP_ERROR_INVALID_ARGUMENT)); - VerifyOrReturnError(leafCertificate != nullptr && leafCertificateLen != 0, - (result = CertificateChainValidationResult::kLeafArgumentInvalid, CHIP_ERROR_INVALID_ARGUMENT)); - - mbedtls_x509_crt_init(&certChain); - mbedtls_x509_crt_init(&rootCert); - - /* Start of chain */ - mbedResult = mbedtls_x509_crt_parse(&certChain, Uint8::to_const_uchar(leafCertificate), leafCertificateLen); - VerifyOrExit(mbedResult == 0, (result = CertificateChainValidationResult::kLeafFormatInvalid, error = CHIP_ERROR_INTERNAL)); - - /* Add the intermediate to the chain, if present */ - if (caCertificate != nullptr && caCertificateLen > 0) - { - mbedResult = mbedtls_x509_crt_parse(&certChain, Uint8::to_const_uchar(caCertificate), caCertificateLen); - VerifyOrExit(mbedResult == 0, (result = CertificateChainValidationResult::kICAFormatInvalid, error = CHIP_ERROR_INTERNAL)); - } - - /* Parse the root cert */ - mbedResult = mbedtls_x509_crt_parse(&rootCert, Uint8::to_const_uchar(rootCertificate), rootCertificateLen); - VerifyOrExit(mbedResult == 0, (result = CertificateChainValidationResult::kRootFormatInvalid, error = CHIP_ERROR_INTERNAL)); - - /* Verify the chain against the root */ - mbedResult = - mbedtls_x509_crt_verify(&certChain, &rootCert, nullptr, nullptr, &flags, CallbackForCustomValidityCheck, &certChain); - - switch (mbedResult) - { - case 0: - VerifyOrExit(flags == 0, (result = CertificateChainValidationResult::kInternalFrameworkError, error = CHIP_ERROR_INTERNAL)); - result = CertificateChainValidationResult::kSuccess; - break; - case MBEDTLS_ERR_X509_INVALID_DATE: - case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED: - result = CertificateChainValidationResult::kChainInvalid; - error = CHIP_ERROR_CERT_NOT_TRUSTED; - break; - default: - result = CertificateChainValidationResult::kInternalFrameworkError; - error = CHIP_ERROR_INTERNAL; - break; - } - -exit: - _log_mbedTLS_error(mbedResult); - mbedtls_x509_crt_free(&certChain); - mbedtls_x509_crt_free(&rootCert); - -#else - (void) rootCertificate; - (void) rootCertificateLen; - (void) caCertificate; - (void) caCertificateLen; - (void) leafCertificate; - (void) leafCertificateLen; - (void) result; - CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - - return error; -} - -CHIP_ERROR IsCertificateValidAtIssuance(const ByteSpan & candidateCertificate, const ByteSpan & issuerCertificate) -{ -#if defined(MBEDTLS_X509_CRT_PARSE_C) - CHIP_ERROR error = CHIP_NO_ERROR; - mbedtls_x509_crt mbedCandidateCertificate; - mbedtls_x509_crt mbedIssuerCertificate; - int result; - - VerifyOrReturnError(!candidateCertificate.empty() && !issuerCertificate.empty(), CHIP_ERROR_INVALID_ARGUMENT); - - mbedtls_x509_crt_init(&mbedCandidateCertificate); - mbedtls_x509_crt_init(&mbedIssuerCertificate); - - result = mbedtls_x509_crt_parse(&mbedCandidateCertificate, Uint8::to_const_uchar(candidateCertificate.data()), - candidateCertificate.size()); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - result = - mbedtls_x509_crt_parse(&mbedIssuerCertificate, Uint8::to_const_uchar(issuerCertificate.data()), issuerCertificate.size()); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - // Verify that the candidateCertificate has a notBefore time valid within the validity period of the issuerCertificate. - SuccessOrExit(error = IsCertificateValidAtIssuance(&mbedCandidateCertificate, &mbedIssuerCertificate)); - -exit: - _log_mbedTLS_error(result); - mbedtls_x509_crt_free(&mbedCandidateCertificate); - mbedtls_x509_crt_free(&mbedIssuerCertificate); - -#else - (void) candidateCertificate; - (void) issuerCertificate; - CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - - return error; -} - -CHIP_ERROR IsCertificateValidAtCurrentTime(const ByteSpan & certificate) -{ -#if defined(MBEDTLS_X509_CRT_PARSE_C) - CHIP_ERROR error = CHIP_NO_ERROR; - mbedtls_x509_crt mbedCertificate; - int result; - - VerifyOrReturnError(!certificate.empty(), CHIP_ERROR_INVALID_ARGUMENT); - - mbedtls_x509_crt_init(&mbedCertificate); - - result = mbedtls_x509_crt_parse(&mbedCertificate, Uint8::to_const_uchar(certificate.data()), certificate.size()); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - // check if certificate's notBefore timestamp is earlier than or equal to current time. - result = mbedtls_x509_time_is_past(&mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(valid_from)); - VerifyOrExit(result == 1, error = CHIP_ERROR_CERT_EXPIRED); - - // check if certificate's notAfter timestamp is later than current time. - result = mbedtls_x509_time_is_future(&mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(valid_to)); - VerifyOrExit(result == 1, error = CHIP_ERROR_CERT_EXPIRED); - -exit: - _log_mbedTLS_error(result); - mbedtls_x509_crt_free(&mbedCertificate); - -#else - (void) certificate; - CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - - return error; -} - -CHIP_ERROR ExtractPubkeyFromX509Cert(const ByteSpan & certificate, Crypto::P256PublicKey & pubkey) -{ -#if defined(MBEDTLS_X509_CRT_PARSE_C) - CHIP_ERROR error = CHIP_NO_ERROR; - mbedtls_x509_crt mbed_cert; - mbedtls_ecp_keypair * keypair = nullptr; - size_t pubkey_size = 0; - - mbedtls_x509_crt_init(&mbed_cert); - - int result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - VerifyOrExit(mbedtls_pk_get_type(&(mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(pk))) == MBEDTLS_PK_ECKEY, - error = CHIP_ERROR_INVALID_ARGUMENT); - - keypair = mbedtls_pk_ec(mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(pk)); - VerifyOrExit(keypair->CHIP_CRYPTO_PAL_PRIVATE(grp).id == MapECPGroupId(pubkey.Type()), error = CHIP_ERROR_INVALID_ARGUMENT); - // Copy the public key from the cert in raw point format - result = - mbedtls_ecp_point_write_binary(&keypair->CHIP_CRYPTO_PAL_PRIVATE(grp), &keypair->CHIP_CRYPTO_PAL_PRIVATE(Q), - MBEDTLS_ECP_PF_UNCOMPRESSED, &pubkey_size, Uint8::to_uchar(pubkey.Bytes()), pubkey.Length()); - - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - VerifyOrExit(pubkey_size == pubkey.Length(), error = CHIP_ERROR_INTERNAL); - -exit: - _log_mbedTLS_error(result); - mbedtls_x509_crt_free(&mbed_cert); - -#else - (void) certificate; - (void) pubkey; - CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - - return error; -} - -namespace { - -CHIP_ERROR ExtractKIDFromX509Cert(bool extractSKID, const ByteSpan & certificate, MutableByteSpan & kid) -{ -#if defined(MBEDTLS_X509_CRT_PARSE_C) - CHIP_ERROR error = CHIP_ERROR_NOT_FOUND; - mbedtls_x509_crt mbed_cert; - unsigned char * p = nullptr; - const unsigned char * end = nullptr; - size_t len = 0; - - mbedtls_x509_crt_init(&mbed_cert); - - int result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - // TODO: The mbedTLS team is working on supporting SKID and AKID extensions processing. - // Once it is supported, this code should be updated. - - p = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p); - end = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p) + - mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(len); - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - - while (p < end) - { - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - - mbedtls_x509_buf extOID = { MBEDTLS_ASN1_OID, len, p }; - bool extractCurrentExtSKID = extractSKID && OID_CMP(sOID_Extension_SubjectKeyIdentifier, extOID); - bool extractCurrentExtAKID = !extractSKID && OID_CMP(sOID_Extension_AuthorityKeyIdentifier, extOID); - p += len; - - int is_critical = 0; - result = mbedtls_asn1_get_bool(&p, end, &is_critical); - VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_WRONG_CERT_TYPE); - - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - - if (extractCurrentExtSKID || extractCurrentExtAKID) - { - if (extractCurrentExtSKID) - { - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - } - else - { - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - // Other optional fields, authorityCertIssuer and authorityCertSerialNumber, - // will be skipped if present. - } - VerifyOrExit(len == kSubjectKeyIdentifierLength, error = CHIP_ERROR_WRONG_CERT_TYPE); - VerifyOrExit(len <= kid.size(), error = CHIP_ERROR_BUFFER_TOO_SMALL); - memcpy(kid.data(), p, len); - if (kid.size() > len) - { - kid.reduce_size(len); - } - ExitNow(error = CHIP_NO_ERROR); - break; - } - p += len; - } - -exit: - _log_mbedTLS_error(result); - mbedtls_x509_crt_free(&mbed_cert); - -#else - (void) certificate; - (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 ExtractCRLDistributionPointURIFromX509Cert(const ByteSpan & certificate, MutableCharSpan & cdpurl) -{ -#if defined(MBEDTLS_X509_CRT_PARSE_C) - CHIP_ERROR error = CHIP_ERROR_NOT_FOUND; - mbedtls_x509_crt mbed_cert; - unsigned char * p = nullptr; - const unsigned char * end = nullptr; - size_t len = 0; - size_t cdpExtCount = 0; - - VerifyOrReturnError(!certificate.empty() && CanCastTo(certificate.size()), CHIP_ERROR_INVALID_ARGUMENT); - - mbedtls_x509_crt_init(&mbed_cert); - - int 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(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p); - end = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p) + - mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(len); - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - - while (p < end) - { - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - - mbedtls_x509_buf extOID = { MBEDTLS_ASN1_OID, len, p }; - bool isCurrentExtCDP = OID_CMP(sOID_Extension_CRLDistributionPoint, extOID); - p += len; - - int is_critical = 0; - result = mbedtls_asn1_get_bool(&p, end, &is_critical); - VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_WRONG_CERT_TYPE); - - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - - unsigned char * end_of_ext = p + len; - - if (isCurrentExtCDP) - { - // Only one CRL Distribution Point Extension is allowed. - cdpExtCount++; - VerifyOrExit(cdpExtCount <= 1, error = CHIP_ERROR_NOT_FOUND); - - // CRL Distribution Point Extension is encoded as a sequence of DistributionPoint: - // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint - // - // This implementation only supports a single DistributionPoint (sequence of size 1), - // which is verified by comparing (p + len == end_of_ext) - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); - VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); - - // The DistributionPoint is a sequence of three optional elements: - // DistributionPoint ::= SEQUENCE { - // distributionPoint [0] DistributionPointName OPTIONAL, - // reasons [1] ReasonFlags OPTIONAL, - // cRLIssuer [2] GeneralNames OPTIONAL } - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); - VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); - - // The DistributionPointName is: - // DistributionPointName ::= CHOICE { - // fullName [0] GeneralNames, - // nameRelativeToCRLIssuer [1] RelativeDistinguishedName } - // - // The URI should be encoded in the fullName element. - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0); - VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); - - // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED); - VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); - - unsigned char * end_of_general_names = p + len; - - // The CDP URI is encoded as a uniformResourceIdentifier field of the GeneralName: - // GeneralName ::= CHOICE { - // otherName [0] OtherName, - // rfc822Name [1] IA5String, - // dNSName [2] IA5String, - // x400Address [3] ORAddress, - // directoryName [4] Name, - // ediPartyName [5] EDIPartyName, - // uniformResourceIdentifier [6] IA5String, - // iPAddress [7] OCTET STRING, - // registeredID [8] OBJECT IDENTIFIER } - result = - mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER); - VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); - - // Only single URI instance in the GeneralNames is supported - VerifyOrExit(p + len == end_of_general_names, error = CHIP_ERROR_NOT_FOUND); - - const char * urlptr = reinterpret_cast(p); - VerifyOrExit((len > strlen(kValidCDPURIHttpPrefix) && - strncmp(urlptr, kValidCDPURIHttpPrefix, strlen(kValidCDPURIHttpPrefix)) == 0) || - (len > strlen(kValidCDPURIHttpsPrefix) && - strncmp(urlptr, kValidCDPURIHttpsPrefix, strlen(kValidCDPURIHttpsPrefix)) == 0), - error = CHIP_ERROR_NOT_FOUND); - error = CopyCharSpanToMutableCharSpan(CharSpan(urlptr, len), cdpurl); - SuccessOrExit(error); - } - p = end_of_ext; - } - - VerifyOrExit(cdpExtCount == 1, error = CHIP_ERROR_NOT_FOUND); - -exit: - _log_mbedTLS_error(result); - mbedtls_x509_crt_free(&mbed_cert); - -#else - (void) certificate; - (void) cdpurl; - CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - - return error; -} - -CHIP_ERROR ExtractCDPExtensionCRLIssuerFromX509Cert(const ByteSpan & certificate, MutableByteSpan & crlIssuer) -{ -#if defined(MBEDTLS_X509_CRT_PARSE_C) - CHIP_ERROR error = CHIP_ERROR_NOT_FOUND; - mbedtls_x509_crt mbed_cert; - unsigned char * p = nullptr; - const unsigned char * end = nullptr; - size_t len = 0; - size_t cdpExtCount = 0; - - VerifyOrReturnError(!certificate.empty() && CanCastTo(certificate.size()), CHIP_ERROR_INVALID_ARGUMENT); - - mbedtls_x509_crt_init(&mbed_cert); - - int 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(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p); - end = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p) + - mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(len); - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - - while (p < end) - { - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - - mbedtls_x509_buf extOID = { MBEDTLS_ASN1_OID, len, p }; - bool isCurrentExtCDP = OID_CMP(sOID_Extension_CRLDistributionPoint, extOID); - p += len; - - int is_critical = 0; - result = mbedtls_asn1_get_bool(&p, end, &is_critical); - VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_WRONG_CERT_TYPE); - - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); - VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); - - unsigned char * end_of_ext = p + len; - - if (isCurrentExtCDP) - { - // Only one CRL Distribution Point Extension is allowed. - cdpExtCount++; - VerifyOrExit(cdpExtCount <= 1, error = CHIP_ERROR_NOT_FOUND); - - // CRL Distribution Point Extension is encoded as a sequence of DistributionPoint: - // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint - // - // This implementation only supports a single DistributionPoint (sequence of size 1), - // which is verified by comparing (p + len == end_of_ext) - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); - VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); - - // The DistributionPoint is a sequence of three optional elements: - // DistributionPoint ::= SEQUENCE { - // distributionPoint [0] DistributionPointName OPTIONAL, - // reasons [1] ReasonFlags OPTIONAL, - // cRLIssuer [2] GeneralNames OPTIONAL } - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); - VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); - - // If distributionPoint element presents, ignore it - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0); - if (result == 0) - { - p += len; - VerifyOrExit(p < end_of_ext, error = CHIP_ERROR_NOT_FOUND); - } - - // Check if cRLIssuer element present - result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 2); - VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); - - // The CRL Issuer is encoded as a directoryName field of the GeneralName: - // GeneralName ::= CHOICE { - // otherName [0] OtherName, - // rfc822Name [1] IA5String, - // dNSName [2] IA5String, - // x400Address [3] ORAddress, - // directoryName [4] Name, - // ediPartyName [5] EDIPartyName, - // uniformResourceIdentifier [6] IA5String, - // iPAddress [7] OCTET STRING, - // registeredID [8] OBJECT IDENTIFIER } - result = mbedtls_asn1_get_tag( - &p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_X509_SAN_DIRECTORY_NAME); - VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); - VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); - - error = CopySpanToMutableSpan(ByteSpan(p, len), crlIssuer); - SuccessOrExit(error); - } - p = end_of_ext; - } - - VerifyOrExit(cdpExtCount == 1, error = CHIP_ERROR_NOT_FOUND); - -exit: - _log_mbedTLS_error(result); - mbedtls_x509_crt_free(&mbed_cert); - -#else - (void) certificate; - (void) crlIssuer; - CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - - return error; -} - -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) - CHIP_ERROR error = CHIP_NO_ERROR; - mbedtls_x509_crt mbed_cert; - mbedtls_asn1_named_data * dnIterator = nullptr; - AttestationCertVidPid vidpidFromCN; - - mbedtls_x509_crt_init(&mbed_cert); - - int result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - for (dnIterator = &mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(subject); dnIterator != nullptr; - dnIterator = dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(next)) - { - DNAttrType attrType = DNAttrType::kUnspecified; - if (OID_CMP(sOID_AttributeType_CommonName, dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(oid))) - { - attrType = DNAttrType::kCommonName; - } - else if (OID_CMP(sOID_AttributeType_MatterVendorId, dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(oid))) - { - attrType = DNAttrType::kMatterVID; - } - else if (OID_CMP(sOID_AttributeType_MatterProductId, dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(oid))) - { - attrType = DNAttrType::kMatterPID; - } - - size_t val_len = dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(val).CHIP_CRYPTO_PAL_PRIVATE_X509(len); - uint8_t * val_p = dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(val).CHIP_CRYPTO_PAL_PRIVATE_X509(p); - error = ExtractVIDPIDFromAttributeString(attrType, ByteSpan(val_p, val_len), vidpid, vidpidFromCN); - SuccessOrExit(error); - } - - // If Matter Attributes were not found use values extracted from the CN Attribute, - // which might be uninitialized as well. - if (!vidpid.Initialized()) - { - vidpid = vidpidFromCN; - } - -exit: - _log_mbedTLS_error(result); - mbedtls_x509_crt_free(&mbed_cert); - -#else - (void) certificate; - (void) vidpid; - CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - - return error; -} - -namespace { -CHIP_ERROR ExtractRawDNFromX509Cert(bool extractSubject, const ByteSpan & certificate, MutableByteSpan & dn) -{ -#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 mbedCertificate; - - ReturnErrorCodeIf(certificate.empty(), CHIP_ERROR_INVALID_ARGUMENT); - - mbedtls_x509_crt_init(&mbedCertificate); - result = mbedtls_x509_crt_parse(&mbedCertificate, Uint8::to_const_uchar(certificate.data()), certificate.size()); - VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); - - if (extractSubject) - { - len = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(subject_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(len); - p = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(subject_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(p); - } - else - { - len = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(issuer_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(len); - p = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(issuer_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(p); - } - - VerifyOrExit(len <= dn.size(), error = CHIP_ERROR_BUFFER_TOO_SMALL); - memcpy(dn.data(), p, len); - dn.reduce_size(len); - -exit: - _log_mbedTLS_error(result); - mbedtls_x509_crt_free(&mbedCertificate); - -#else - (void) certificate; - (void) dn; - CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) - - return error; -} -} // namespace - -CHIP_ERROR ExtractSubjectFromX509Cert(const ByteSpan & certificate, MutableByteSpan & subject) -{ - return ExtractRawDNFromX509Cert(true, certificate, subject); -} - -CHIP_ERROR ExtractIssuerFromX509Cert(const ByteSpan & certificate, MutableByteSpan & issuer) -{ - return ExtractRawDNFromX509Cert(false, certificate, issuer); -} - -CHIP_ERROR ReplaceCertIfResignedCertFound(const ByteSpan & referenceCertificate, const ByteSpan * candidateCertificates, - size_t candidateCertificatesCount, ByteSpan & outCertificate) -{ -#if defined(MBEDTLS_X509_CRT_PARSE_C) - uint8_t referenceSubjectBuf[kMaxCertificateDistinguishedNameLength]; - uint8_t referenceSKIDBuf[kSubjectKeyIdentifierLength]; - MutableByteSpan referenceSubject(referenceSubjectBuf); - MutableByteSpan referenceSKID(referenceSKIDBuf); - - outCertificate = referenceCertificate; - - ReturnErrorCodeIf(candidateCertificates == nullptr || candidateCertificatesCount == 0, CHIP_NO_ERROR); - - ReturnErrorOnFailure(ExtractSubjectFromX509Cert(referenceCertificate, referenceSubject)); - ReturnErrorOnFailure(ExtractSKIDFromX509Cert(referenceCertificate, referenceSKID)); - - for (size_t i = 0; i < candidateCertificatesCount; i++) - { - const ByteSpan candidateCertificate = candidateCertificates[i]; - uint8_t candidateSubjectBuf[kMaxCertificateDistinguishedNameLength]; - uint8_t candidateSKIDBuf[kSubjectKeyIdentifierLength]; - MutableByteSpan candidateSubject(candidateSubjectBuf); - MutableByteSpan candidateSKID(candidateSKIDBuf); - - ReturnErrorOnFailure(ExtractSubjectFromX509Cert(candidateCertificate, candidateSubject)); - ReturnErrorOnFailure(ExtractSKIDFromX509Cert(candidateCertificate, candidateSKID)); - - if (referenceSKID.data_equal(candidateSKID) && referenceSubject.data_equal(candidateSubject)) - { - outCertificate = candidateCertificate; - return CHIP_NO_ERROR; - } - } - - return CHIP_NO_ERROR; -#else - (void) referenceCertificate; - (void) candidateCertificates; - (void) candidateCertificatesCount; - (void) outCertificate; - return CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(MBEDTLS_X509_CRT_PARSE_C) -} - } // namespace Crypto } // namespace chip diff --git a/src/crypto/CHIPCryptoPALmbedTLS.h b/src/crypto/CHIPCryptoPALmbedTLS.h new file mode 100644 index 00000000000000..9afa5ca4001757 --- /dev/null +++ b/src/crypto/CHIPCryptoPALmbedTLS.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +#include "CHIPCryptoPAL.h" + +#include + +#include +#include +#include + +namespace chip { +namespace Crypto { + +// In mbedTLS 3.0.0 direct access to structure fields was replaced with using MBEDTLS_PRIVATE macro. +#if (MBEDTLS_VERSION_NUMBER >= 0x03000000) +#define CHIP_CRYPTO_PAL_PRIVATE(x) MBEDTLS_PRIVATE(x) +#else +#define CHIP_CRYPTO_PAL_PRIVATE(x) x +#endif + +#if (MBEDTLS_VERSION_NUMBER >= 0x03000000 && MBEDTLS_VERSION_NUMBER < 0x03010000) +#define CHIP_CRYPTO_PAL_PRIVATE_X509(x) MBEDTLS_PRIVATE(x) +#else +#define CHIP_CRYPTO_PAL_PRIVATE_X509(x) x +#endif + +static inline void _log_mbedTLS_error(int errorCode) +{ + + if (errorCode != 0) + { +#if defined(MBEDTLS_ERROR_C) + constexpr size_t kMaxErrorStrLen = 128; + char errorStr[kMaxErrorStrLen]; + mbedtls_strerror(errorCode, errorStr, sizeof(errorStr)); + ChipLogError(Crypto, "mbedTLS error: %s", errorStr); +#else + // Error codes defined in 16-bit negative hex numbers. Ease lookup by printing likewise + ChipLogError(Crypto, "mbedTLS error: -0x%04X", -static_cast(errorCode)); +#endif + } +} + +static inline mbedtls_ecp_group_id MapECPGroupId(SupportedECPKeyTypes keyType) +{ + switch (keyType) + { + case SupportedECPKeyTypes::ECP256R1: + return MBEDTLS_ECP_DP_SECP256R1; + default: + return MBEDTLS_ECP_DP_NONE; + } +} + +} // namespace Crypto +} // namespace chip diff --git a/src/crypto/CHIPCryptoPALmbedTLSCert.cpp b/src/crypto/CHIPCryptoPALmbedTLSCert.cpp new file mode 100644 index 00000000000000..11eab025193135 --- /dev/null +++ b/src/crypto/CHIPCryptoPALmbedTLSCert.cpp @@ -0,0 +1,1075 @@ +/* + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +/** + * @file + * mbedTLS based implementation of CHIP crypto primitives related to certificate + * generation and validation. + */ + +#include "CHIPCryptoPAL.h" +#include "CHIPCryptoPALmbedTLS.h" + +#include +#include +#include + +#include +#include +#include +#include + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +#include +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + +namespace chip { +namespace Crypto { + +CHIP_ERROR VerifyCertificateSigningRequest(const uint8_t * csr_buf, size_t csr_length, P256PublicKey & pubkey) +{ +#if defined(MBEDTLS_X509_CSR_PARSE_C) + ReturnErrorOnFailure(VerifyCertificateSigningRequestFormat(csr_buf, csr_length)); + + // TODO: For some embedded targets, mbedTLS library doesn't have mbedtls_x509_csr_parse_der, and mbedtls_x509_csr_parse_free. + // Taking a step back, embedded targets likely will not process CSR requests. Adding this action item to reevaluate + // this if there's a need for this processing for embedded targets. + CHIP_ERROR error = CHIP_NO_ERROR; + size_t pubkey_size = 0; + + mbedtls_ecp_keypair * keypair = nullptr; + + P256ECDSASignature signature; + MutableByteSpan out_raw_sig_span(signature.Bytes(), signature.Capacity()); + + mbedtls_x509_csr csr; + mbedtls_x509_csr_init(&csr); + + int result = mbedtls_x509_csr_parse_der(&csr, csr_buf, csr_length); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + // Verify the signature algorithm and public key type + VerifyOrExit(csr.CHIP_CRYPTO_PAL_PRIVATE(sig_md) == MBEDTLS_MD_SHA256, error = CHIP_ERROR_UNSUPPORTED_SIGNATURE_TYPE); + VerifyOrExit(csr.CHIP_CRYPTO_PAL_PRIVATE(sig_pk) == MBEDTLS_PK_ECDSA, error = CHIP_ERROR_WRONG_KEY_TYPE); + + keypair = mbedtls_pk_ec(csr.CHIP_CRYPTO_PAL_PRIVATE_X509(pk)); + + // Copy the public key from the CSR + result = mbedtls_ecp_point_write_binary(&keypair->CHIP_CRYPTO_PAL_PRIVATE(grp), &keypair->CHIP_CRYPTO_PAL_PRIVATE(Q), + MBEDTLS_ECP_PF_UNCOMPRESSED, &pubkey_size, Uint8::to_uchar(pubkey), pubkey.Length()); + + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + VerifyOrExit(pubkey_size == pubkey.Length(), error = CHIP_ERROR_INTERNAL); + + // Convert DER signature to raw signature + error = EcdsaAsn1SignatureToRaw(kP256_FE_Length, + ByteSpan{ csr.CHIP_CRYPTO_PAL_PRIVATE(sig).CHIP_CRYPTO_PAL_PRIVATE_X509(p), + csr.CHIP_CRYPTO_PAL_PRIVATE(sig).CHIP_CRYPTO_PAL_PRIVATE_X509(len) }, + out_raw_sig_span); + + VerifyOrExit(error == CHIP_NO_ERROR, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(out_raw_sig_span.size() == (kP256_FE_Length * 2), error = CHIP_ERROR_INTERNAL); + signature.SetLength(out_raw_sig_span.size()); + + // Verify the signature using the public key + error = pubkey.ECDSA_validate_msg_signature(csr.CHIP_CRYPTO_PAL_PRIVATE_X509(cri).CHIP_CRYPTO_PAL_PRIVATE_X509(p), + csr.CHIP_CRYPTO_PAL_PRIVATE_X509(cri).CHIP_CRYPTO_PAL_PRIVATE_X509(len), signature); + + SuccessOrExit(error); + +exit: + mbedtls_x509_csr_free(&csr); + _log_mbedTLS_error(result); + return error; +#else + ChipLogError(Crypto, "MBEDTLS_X509_CSR_PARSE_C is not enabled. CSR cannot be parsed"); + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +#endif +} + +namespace { + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +bool IsTimeGreaterThanEqual(const mbedtls_x509_time * const timeA, const mbedtls_x509_time * const timeB) +{ + + // checks if two values are different and if yes, then returns first > second. +#define RETURN_STRICTLY_GREATER_IF_DIFFERENT(component) \ + { \ + auto valueA = timeA->CHIP_CRYPTO_PAL_PRIVATE_X509(component); \ + auto valueB = timeB->CHIP_CRYPTO_PAL_PRIVATE_X509(component); \ + \ + if (valueA != valueB) \ + { \ + return valueA > valueB; \ + } \ + } + + RETURN_STRICTLY_GREATER_IF_DIFFERENT(year); + RETURN_STRICTLY_GREATER_IF_DIFFERENT(mon); + RETURN_STRICTLY_GREATER_IF_DIFFERENT(day); + RETURN_STRICTLY_GREATER_IF_DIFFERENT(hour); + RETURN_STRICTLY_GREATER_IF_DIFFERENT(min); + RETURN_STRICTLY_GREATER_IF_DIFFERENT(sec); + + // all above are equal + return true; +} + +CHIP_ERROR IsCertificateValidAtIssuance(const mbedtls_x509_crt * candidateCertificate, const mbedtls_x509_crt * issuerCertificate) +{ + mbedtls_x509_time candidateNotBeforeTime = candidateCertificate->CHIP_CRYPTO_PAL_PRIVATE_X509(valid_from); + mbedtls_x509_time issuerNotBeforeTime = issuerCertificate->CHIP_CRYPTO_PAL_PRIVATE_X509(valid_from); + mbedtls_x509_time issuerNotAfterTime = issuerCertificate->CHIP_CRYPTO_PAL_PRIVATE_X509(valid_to); + + // check if candidateCertificate is issued at or after issuerCertificate's notBefore timestamp + VerifyOrReturnError(IsTimeGreaterThanEqual(&candidateNotBeforeTime, &issuerNotBeforeTime), CHIP_ERROR_CERT_EXPIRED); + + // check if candidateCertificate is issued at or before issuerCertificate's notAfter timestamp + VerifyOrReturnError(IsTimeGreaterThanEqual(&issuerNotAfterTime, &candidateNotBeforeTime), CHIP_ERROR_CERT_EXPIRED); + + return CHIP_NO_ERROR; +} + +int CallbackForCustomValidityCheck(void * data, mbedtls_x509_crt * crt, int depth, uint32_t * flags) +{ + mbedtls_x509_crt * leafCert = reinterpret_cast(data); + mbedtls_x509_crt * issuerCert = crt; + + // Ignore any time validy error performed by the standard mbedTLS code. + *flags &= ~(static_cast(MBEDTLS_X509_BADCERT_EXPIRED | MBEDTLS_X509_BADCERT_FUTURE)); + + // Verify that the leaf certificate has a notBefore time valid within the validity period of the issuerCertificate. + // Note that this callback is invoked for each certificate in the chain. + if (IsCertificateValidAtIssuance(leafCert, issuerCert) != CHIP_NO_ERROR) + { + return MBEDTLS_ERR_X509_INVALID_DATE; + } + + return 0; +} + +constexpr uint8_t sOID_AttributeType_CommonName[] = { 0x55, 0x04, 0x03 }; +constexpr uint8_t sOID_AttributeType_MatterVendorId[] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x02, 0x01 }; +constexpr uint8_t sOID_AttributeType_MatterProductId[] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x02, 0x02 }; +constexpr uint8_t sOID_SigAlgo_ECDSAWithSHA256[] = { 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02 }; +constexpr uint8_t sOID_Extension_BasicConstraints[] = { 0x55, 0x1D, 0x13 }; +constexpr uint8_t sOID_Extension_KeyUsage[] = { 0x55, 0x1D, 0x0F }; +constexpr uint8_t sOID_Extension_SubjectKeyIdentifier[] = { 0x55, 0x1D, 0x0E }; +constexpr uint8_t sOID_Extension_AuthorityKeyIdentifier[] = { 0x55, 0x1D, 0x23 }; +constexpr uint8_t sOID_Extension_CRLDistributionPoint[] = { 0x55, 0x1D, 0x1F }; + +/** + * Compares an mbedtls_asn1_buf structure (oidBuf) to a reference OID represented as uint8_t array (oid). + */ +#define OID_CMP(oid, oidBuf) \ + ((MBEDTLS_ASN1_OID == (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(tag)) && \ + (sizeof(oid) == (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(len)) && \ + (memcmp((oid), (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(p), (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(len)) == 0)) + +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + +} // anonymous namespace + +CHIP_ERROR VerifyAttestationCertificateFormat(const ByteSpan & cert, AttestationCertType certType) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + mbedtls_x509_crt mbed_cert; + unsigned char * p = nullptr; + const unsigned char * end = nullptr; + size_t len = 0; + bool extBasicPresent = false; + bool extKeyUsagePresent = false; + + VerifyOrReturnError(!cert.empty(), CHIP_ERROR_INVALID_ARGUMENT); + + mbedtls_x509_crt_init(&mbed_cert); + + result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(cert.data()), cert.size()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + // "version" value is 1 higher than the actual encoded value. + VerifyOrExit(mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(version) - 1 == 2, error = CHIP_ERROR_INTERNAL); + + // Verify signature algorithms is ECDSA with SHA256. + VerifyOrExit(OID_CMP(sOID_SigAlgo_ECDSAWithSHA256, mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(sig_oid)), + error = CHIP_ERROR_INTERNAL); + + // Verify public key presence and format. + { + Crypto::P256PublicKey pubkey; + SuccessOrExit(error = ExtractPubkeyFromX509Cert(cert, pubkey)); + } + + p = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p); + end = p + mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(len); + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + while (p < end) + { + mbedtls_x509_buf extOID = { 0, 0, nullptr }; + int extCritical = 0; + + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + /* Get extension ID */ + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + extOID.CHIP_CRYPTO_PAL_PRIVATE_X509(tag) = MBEDTLS_ASN1_OID; + extOID.CHIP_CRYPTO_PAL_PRIVATE_X509(len) = len; + extOID.CHIP_CRYPTO_PAL_PRIVATE_X509(p) = p; + p += len; + + /* Get optional critical */ + result = mbedtls_asn1_get_bool(&p, end, &extCritical); + VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_INTERNAL); + + /* Data should be octet string type */ + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + if (OID_CMP(sOID_Extension_BasicConstraints, extOID)) + { + int isCA = 0; + int pathLen = -1; + + VerifyOrExit(extCritical, error = CHIP_ERROR_INTERNAL); + extBasicPresent = true; + + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + if (len > 0) + { + unsigned char * seqStart = p; + result = mbedtls_asn1_get_bool(&p, end, &isCA); + VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_INTERNAL); + + // Check if pathLen is there by validating if the cursor didn't get to the end of + // of the internal SEQUENCE for the basic constraints encapsulation. + // Missing pathLen optional tag will leave pathLen == -1 for following checks. + bool hasPathLen = (p != (seqStart + len)); + if (hasPathLen) + { + // Extract pathLen value, making sure it's a valid format. + result = mbedtls_asn1_get_int(&p, end, &pathLen); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + } + } + + if (certType == AttestationCertType::kDAC) + { + VerifyOrExit(!isCA && pathLen == -1, error = CHIP_ERROR_INTERNAL); + } + else if (certType == AttestationCertType::kPAI) + { + VerifyOrExit(isCA && pathLen == 0, error = CHIP_ERROR_INTERNAL); + } + else + { + // For PAA, pathlen must be absent or equal to 1 (see Matter 1.1 spec 6.2.2.5) + VerifyOrExit(isCA && (pathLen == -1 || pathLen == 1), error = CHIP_ERROR_INTERNAL); + } + } + else if (OID_CMP(sOID_Extension_KeyUsage, extOID)) + { + mbedtls_x509_bitstring bs = { 0, 0, nullptr }; + unsigned int keyUsage = 0; + + VerifyOrExit(extCritical, error = CHIP_ERROR_INTERNAL); + extKeyUsagePresent = true; + + result = mbedtls_asn1_get_bitstring(&p, p + len, &bs); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + for (size_t i = 0; i < bs.CHIP_CRYPTO_PAL_PRIVATE_X509(len) && i < sizeof(unsigned int); i++) + { + keyUsage |= static_cast(bs.CHIP_CRYPTO_PAL_PRIVATE_X509(p)[i]) << (8 * i); + } + + if (certType == AttestationCertType::kDAC) + { + // SHALL only have the digitalSignature bit set. + VerifyOrExit(keyUsage == MBEDTLS_X509_KU_DIGITAL_SIGNATURE, error = CHIP_ERROR_INTERNAL); + } + else + { + bool keyCertSignFlag = keyUsage & MBEDTLS_X509_KU_KEY_CERT_SIGN; + bool crlSignFlag = keyUsage & MBEDTLS_X509_KU_CRL_SIGN; + bool otherFlags = keyUsage & + ~static_cast(MBEDTLS_X509_KU_CRL_SIGN | MBEDTLS_X509_KU_KEY_CERT_SIGN | + MBEDTLS_X509_KU_DIGITAL_SIGNATURE); + VerifyOrExit(keyCertSignFlag && crlSignFlag && !otherFlags, error = CHIP_ERROR_INTERNAL); + } + } + else + { + p += len; + } + } + + // Verify basic and key usage extensions are present. + VerifyOrExit(extBasicPresent && extKeyUsagePresent, error = CHIP_ERROR_INTERNAL); + + // Verify that SKID and AKID extensions are present. + { + uint8_t kidBuf[kSubjectKeyIdentifierLength]; + MutableByteSpan kid(kidBuf); + SuccessOrExit(error = ExtractSKIDFromX509Cert(cert, kid)); + if (certType == AttestationCertType::kDAC || certType == AttestationCertType::kPAI) + { + // Mandatory extension for DAC and PAI certs. + SuccessOrExit(error = ExtractAKIDFromX509Cert(cert, kid)); + } + } + +exit: + _log_mbedTLS_error(result); + mbedtls_x509_crt_free(&mbed_cert); + +#else + (void) cert; + (void) certType; + CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + + return error; +} + +CHIP_ERROR ValidateCertificateChain(const uint8_t * rootCertificate, size_t rootCertificateLen, const uint8_t * caCertificate, + size_t caCertificateLen, const uint8_t * leafCertificate, size_t leafCertificateLen, + CertificateChainValidationResult & result) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + CHIP_ERROR error = CHIP_NO_ERROR; + mbedtls_x509_crt certChain; + mbedtls_x509_crt rootCert; + int mbedResult; + uint32_t flags = 0; + + result = CertificateChainValidationResult::kInternalFrameworkError; + + VerifyOrReturnError(rootCertificate != nullptr && rootCertificateLen != 0, + (result = CertificateChainValidationResult::kRootArgumentInvalid, CHIP_ERROR_INVALID_ARGUMENT)); + VerifyOrReturnError(leafCertificate != nullptr && leafCertificateLen != 0, + (result = CertificateChainValidationResult::kLeafArgumentInvalid, CHIP_ERROR_INVALID_ARGUMENT)); + + mbedtls_x509_crt_init(&certChain); + mbedtls_x509_crt_init(&rootCert); + + /* Start of chain */ + mbedResult = mbedtls_x509_crt_parse(&certChain, Uint8::to_const_uchar(leafCertificate), leafCertificateLen); + VerifyOrExit(mbedResult == 0, (result = CertificateChainValidationResult::kLeafFormatInvalid, error = CHIP_ERROR_INTERNAL)); + + /* Add the intermediate to the chain, if present */ + if (caCertificate != nullptr && caCertificateLen > 0) + { + mbedResult = mbedtls_x509_crt_parse(&certChain, Uint8::to_const_uchar(caCertificate), caCertificateLen); + VerifyOrExit(mbedResult == 0, (result = CertificateChainValidationResult::kICAFormatInvalid, error = CHIP_ERROR_INTERNAL)); + } + + /* Parse the root cert */ + mbedResult = mbedtls_x509_crt_parse(&rootCert, Uint8::to_const_uchar(rootCertificate), rootCertificateLen); + VerifyOrExit(mbedResult == 0, (result = CertificateChainValidationResult::kRootFormatInvalid, error = CHIP_ERROR_INTERNAL)); + + /* Verify the chain against the root */ + mbedResult = + mbedtls_x509_crt_verify(&certChain, &rootCert, nullptr, nullptr, &flags, CallbackForCustomValidityCheck, &certChain); + + switch (mbedResult) + { + case 0: + VerifyOrExit(flags == 0, (result = CertificateChainValidationResult::kInternalFrameworkError, error = CHIP_ERROR_INTERNAL)); + result = CertificateChainValidationResult::kSuccess; + break; + case MBEDTLS_ERR_X509_INVALID_DATE: + case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED: + result = CertificateChainValidationResult::kChainInvalid; + error = CHIP_ERROR_CERT_NOT_TRUSTED; + break; + default: + result = CertificateChainValidationResult::kInternalFrameworkError; + error = CHIP_ERROR_INTERNAL; + break; + } + +exit: + _log_mbedTLS_error(mbedResult); + mbedtls_x509_crt_free(&certChain); + mbedtls_x509_crt_free(&rootCert); + +#else + (void) rootCertificate; + (void) rootCertificateLen; + (void) caCertificate; + (void) caCertificateLen; + (void) leafCertificate; + (void) leafCertificateLen; + (void) result; + CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + + return error; +} + +CHIP_ERROR IsCertificateValidAtIssuance(const ByteSpan & candidateCertificate, const ByteSpan & issuerCertificate) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + CHIP_ERROR error = CHIP_NO_ERROR; + mbedtls_x509_crt mbedCandidateCertificate; + mbedtls_x509_crt mbedIssuerCertificate; + int result; + + VerifyOrReturnError(!candidateCertificate.empty() && !issuerCertificate.empty(), CHIP_ERROR_INVALID_ARGUMENT); + + mbedtls_x509_crt_init(&mbedCandidateCertificate); + mbedtls_x509_crt_init(&mbedIssuerCertificate); + + result = mbedtls_x509_crt_parse(&mbedCandidateCertificate, Uint8::to_const_uchar(candidateCertificate.data()), + candidateCertificate.size()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + result = + mbedtls_x509_crt_parse(&mbedIssuerCertificate, Uint8::to_const_uchar(issuerCertificate.data()), issuerCertificate.size()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + // Verify that the candidateCertificate has a notBefore time valid within the validity period of the issuerCertificate. + SuccessOrExit(error = IsCertificateValidAtIssuance(&mbedCandidateCertificate, &mbedIssuerCertificate)); + +exit: + _log_mbedTLS_error(result); + mbedtls_x509_crt_free(&mbedCandidateCertificate); + mbedtls_x509_crt_free(&mbedIssuerCertificate); + +#else + (void) candidateCertificate; + (void) issuerCertificate; + CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + + return error; +} + +CHIP_ERROR IsCertificateValidAtCurrentTime(const ByteSpan & certificate) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + CHIP_ERROR error = CHIP_NO_ERROR; + mbedtls_x509_crt mbedCertificate; + int result; + + VerifyOrReturnError(!certificate.empty(), CHIP_ERROR_INVALID_ARGUMENT); + + mbedtls_x509_crt_init(&mbedCertificate); + + result = mbedtls_x509_crt_parse(&mbedCertificate, Uint8::to_const_uchar(certificate.data()), certificate.size()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + // check if certificate's notBefore timestamp is earlier than or equal to current time. + result = mbedtls_x509_time_is_past(&mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(valid_from)); + VerifyOrExit(result == 1, error = CHIP_ERROR_CERT_EXPIRED); + + // check if certificate's notAfter timestamp is later than current time. + result = mbedtls_x509_time_is_future(&mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(valid_to)); + VerifyOrExit(result == 1, error = CHIP_ERROR_CERT_EXPIRED); + +exit: + _log_mbedTLS_error(result); + mbedtls_x509_crt_free(&mbedCertificate); + +#else + (void) certificate; + CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + + return error; +} + +CHIP_ERROR ExtractPubkeyFromX509Cert(const ByteSpan & certificate, Crypto::P256PublicKey & pubkey) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + CHIP_ERROR error = CHIP_NO_ERROR; + mbedtls_x509_crt mbed_cert; + mbedtls_ecp_keypair * keypair = nullptr; + size_t pubkey_size = 0; + + mbedtls_x509_crt_init(&mbed_cert); + + int result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + VerifyOrExit(mbedtls_pk_get_type(&(mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(pk))) == MBEDTLS_PK_ECKEY, + error = CHIP_ERROR_INVALID_ARGUMENT); + + keypair = mbedtls_pk_ec(mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(pk)); + VerifyOrExit(keypair->CHIP_CRYPTO_PAL_PRIVATE(grp).id == MapECPGroupId(pubkey.Type()), error = CHIP_ERROR_INVALID_ARGUMENT); + // Copy the public key from the cert in raw point format + result = + mbedtls_ecp_point_write_binary(&keypair->CHIP_CRYPTO_PAL_PRIVATE(grp), &keypair->CHIP_CRYPTO_PAL_PRIVATE(Q), + MBEDTLS_ECP_PF_UNCOMPRESSED, &pubkey_size, Uint8::to_uchar(pubkey.Bytes()), pubkey.Length()); + + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + VerifyOrExit(pubkey_size == pubkey.Length(), error = CHIP_ERROR_INTERNAL); + +exit: + _log_mbedTLS_error(result); + mbedtls_x509_crt_free(&mbed_cert); + +#else + (void) certificate; + (void) pubkey; + CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + + return error; +} + +namespace { + +CHIP_ERROR ExtractKIDFromX509Cert(bool extractSKID, const ByteSpan & certificate, MutableByteSpan & kid) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + CHIP_ERROR error = CHIP_ERROR_NOT_FOUND; + mbedtls_x509_crt mbed_cert; + unsigned char * p = nullptr; + const unsigned char * end = nullptr; + size_t len = 0; + + mbedtls_x509_crt_init(&mbed_cert); + + int result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + // TODO: The mbedTLS team is working on supporting SKID and AKID extensions processing. + // Once it is supported, this code should be updated. + + p = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p); + end = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p) + + mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(len); + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + + while (p < end) + { + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + + mbedtls_x509_buf extOID = { MBEDTLS_ASN1_OID, len, p }; + bool extractCurrentExtSKID = extractSKID && OID_CMP(sOID_Extension_SubjectKeyIdentifier, extOID); + bool extractCurrentExtAKID = !extractSKID && OID_CMP(sOID_Extension_AuthorityKeyIdentifier, extOID); + p += len; + + int is_critical = 0; + result = mbedtls_asn1_get_bool(&p, end, &is_critical); + VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_WRONG_CERT_TYPE); + + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + + if (extractCurrentExtSKID || extractCurrentExtAKID) + { + if (extractCurrentExtSKID) + { + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + } + else + { + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + // Other optional fields, authorityCertIssuer and authorityCertSerialNumber, + // will be skipped if present. + } + VerifyOrExit(len == kSubjectKeyIdentifierLength, error = CHIP_ERROR_WRONG_CERT_TYPE); + VerifyOrExit(len <= kid.size(), error = CHIP_ERROR_BUFFER_TOO_SMALL); + memcpy(kid.data(), p, len); + if (kid.size() > len) + { + kid.reduce_size(len); + } + ExitNow(error = CHIP_NO_ERROR); + break; + } + p += len; + } + +exit: + _log_mbedTLS_error(result); + mbedtls_x509_crt_free(&mbed_cert); + +#else + (void) certificate; + (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 ExtractCRLDistributionPointURIFromX509Cert(const ByteSpan & certificate, MutableCharSpan & cdpurl) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + CHIP_ERROR error = CHIP_ERROR_NOT_FOUND; + mbedtls_x509_crt mbed_cert; + unsigned char * p = nullptr; + const unsigned char * end = nullptr; + size_t len = 0; + size_t cdpExtCount = 0; + + VerifyOrReturnError(!certificate.empty() && CanCastTo(certificate.size()), CHIP_ERROR_INVALID_ARGUMENT); + + mbedtls_x509_crt_init(&mbed_cert); + + int 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(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p); + end = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p) + + mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(len); + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + + while (p < end) + { + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + + mbedtls_x509_buf extOID = { MBEDTLS_ASN1_OID, len, p }; + bool isCurrentExtCDP = OID_CMP(sOID_Extension_CRLDistributionPoint, extOID); + p += len; + + int is_critical = 0; + result = mbedtls_asn1_get_bool(&p, end, &is_critical); + VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_WRONG_CERT_TYPE); + + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + + unsigned char * end_of_ext = p + len; + + if (isCurrentExtCDP) + { + // Only one CRL Distribution Point Extension is allowed. + cdpExtCount++; + VerifyOrExit(cdpExtCount <= 1, error = CHIP_ERROR_NOT_FOUND); + + // CRL Distribution Point Extension is encoded as a sequence of DistributionPoint: + // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint + // + // This implementation only supports a single DistributionPoint (sequence of size 1), + // which is verified by comparing (p + len == end_of_ext) + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); + VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); + + // The DistributionPoint is a sequence of three optional elements: + // DistributionPoint ::= SEQUENCE { + // distributionPoint [0] DistributionPointName OPTIONAL, + // reasons [1] ReasonFlags OPTIONAL, + // cRLIssuer [2] GeneralNames OPTIONAL } + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); + VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); + + // The DistributionPointName is: + // DistributionPointName ::= CHOICE { + // fullName [0] GeneralNames, + // nameRelativeToCRLIssuer [1] RelativeDistinguishedName } + // + // The URI should be encoded in the fullName element. + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0); + VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); + + // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED); + VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); + + unsigned char * end_of_general_names = p + len; + + // The CDP URI is encoded as a uniformResourceIdentifier field of the GeneralName: + // GeneralName ::= CHOICE { + // otherName [0] OtherName, + // rfc822Name [1] IA5String, + // dNSName [2] IA5String, + // x400Address [3] ORAddress, + // directoryName [4] Name, + // ediPartyName [5] EDIPartyName, + // uniformResourceIdentifier [6] IA5String, + // iPAddress [7] OCTET STRING, + // registeredID [8] OBJECT IDENTIFIER } + result = + mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER); + VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); + + // Only single URI instance in the GeneralNames is supported + VerifyOrExit(p + len == end_of_general_names, error = CHIP_ERROR_NOT_FOUND); + + const char * urlptr = reinterpret_cast(p); + VerifyOrExit((len > strlen(kValidCDPURIHttpPrefix) && + strncmp(urlptr, kValidCDPURIHttpPrefix, strlen(kValidCDPURIHttpPrefix)) == 0) || + (len > strlen(kValidCDPURIHttpsPrefix) && + strncmp(urlptr, kValidCDPURIHttpsPrefix, strlen(kValidCDPURIHttpsPrefix)) == 0), + error = CHIP_ERROR_NOT_FOUND); + error = CopyCharSpanToMutableCharSpan(CharSpan(urlptr, len), cdpurl); + SuccessOrExit(error); + } + p = end_of_ext; + } + + VerifyOrExit(cdpExtCount == 1, error = CHIP_ERROR_NOT_FOUND); + +exit: + _log_mbedTLS_error(result); + mbedtls_x509_crt_free(&mbed_cert); + +#else + (void) certificate; + (void) cdpurl; + CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + + return error; +} + +CHIP_ERROR ExtractCDPExtensionCRLIssuerFromX509Cert(const ByteSpan & certificate, MutableByteSpan & crlIssuer) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + CHIP_ERROR error = CHIP_ERROR_NOT_FOUND; + mbedtls_x509_crt mbed_cert; + unsigned char * p = nullptr; + const unsigned char * end = nullptr; + size_t len = 0; + size_t cdpExtCount = 0; + + VerifyOrReturnError(!certificate.empty() && CanCastTo(certificate.size()), CHIP_ERROR_INVALID_ARGUMENT); + + mbedtls_x509_crt_init(&mbed_cert); + + int 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(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p); + end = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p) + + mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(len); + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + + while (p < end) + { + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + + mbedtls_x509_buf extOID = { MBEDTLS_ASN1_OID, len, p }; + bool isCurrentExtCDP = OID_CMP(sOID_Extension_CRLDistributionPoint, extOID); + p += len; + + int is_critical = 0; + result = mbedtls_asn1_get_bool(&p, end, &is_critical); + VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_WRONG_CERT_TYPE); + + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + + unsigned char * end_of_ext = p + len; + + if (isCurrentExtCDP) + { + // Only one CRL Distribution Point Extension is allowed. + cdpExtCount++; + VerifyOrExit(cdpExtCount <= 1, error = CHIP_ERROR_NOT_FOUND); + + // CRL Distribution Point Extension is encoded as a sequence of DistributionPoint: + // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint + // + // This implementation only supports a single DistributionPoint (sequence of size 1), + // which is verified by comparing (p + len == end_of_ext) + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); + VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); + + // The DistributionPoint is a sequence of three optional elements: + // DistributionPoint ::= SEQUENCE { + // distributionPoint [0] DistributionPointName OPTIONAL, + // reasons [1] ReasonFlags OPTIONAL, + // cRLIssuer [2] GeneralNames OPTIONAL } + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); + VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); + + // If distributionPoint element presents, ignore it + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0); + if (result == 0) + { + p += len; + VerifyOrExit(p < end_of_ext, error = CHIP_ERROR_NOT_FOUND); + } + + // Check if cRLIssuer element present + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 2); + VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); + + // The CRL Issuer is encoded as a directoryName field of the GeneralName: + // GeneralName ::= CHOICE { + // otherName [0] OtherName, + // rfc822Name [1] IA5String, + // dNSName [2] IA5String, + // x400Address [3] ORAddress, + // directoryName [4] Name, + // ediPartyName [5] EDIPartyName, + // uniformResourceIdentifier [6] IA5String, + // iPAddress [7] OCTET STRING, + // registeredID [8] OBJECT IDENTIFIER } + result = mbedtls_asn1_get_tag( + &p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_X509_SAN_DIRECTORY_NAME); + VerifyOrExit(result == 0, error = CHIP_ERROR_NOT_FOUND); + VerifyOrExit(p + len == end_of_ext, error = CHIP_ERROR_NOT_FOUND); + + error = CopySpanToMutableSpan(ByteSpan(p, len), crlIssuer); + SuccessOrExit(error); + } + p = end_of_ext; + } + + VerifyOrExit(cdpExtCount == 1, error = CHIP_ERROR_NOT_FOUND); + +exit: + _log_mbedTLS_error(result); + mbedtls_x509_crt_free(&mbed_cert); + +#else + (void) certificate; + (void) crlIssuer; + CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + + return error; +} + +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) + CHIP_ERROR error = CHIP_NO_ERROR; + mbedtls_x509_crt mbed_cert; + mbedtls_asn1_named_data * dnIterator = nullptr; + AttestationCertVidPid vidpidFromCN; + + mbedtls_x509_crt_init(&mbed_cert); + + int result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + for (dnIterator = &mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(subject); dnIterator != nullptr; + dnIterator = dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(next)) + { + DNAttrType attrType = DNAttrType::kUnspecified; + if (OID_CMP(sOID_AttributeType_CommonName, dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(oid))) + { + attrType = DNAttrType::kCommonName; + } + else if (OID_CMP(sOID_AttributeType_MatterVendorId, dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(oid))) + { + attrType = DNAttrType::kMatterVID; + } + else if (OID_CMP(sOID_AttributeType_MatterProductId, dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(oid))) + { + attrType = DNAttrType::kMatterPID; + } + + size_t val_len = dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(val).CHIP_CRYPTO_PAL_PRIVATE_X509(len); + uint8_t * val_p = dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(val).CHIP_CRYPTO_PAL_PRIVATE_X509(p); + error = ExtractVIDPIDFromAttributeString(attrType, ByteSpan(val_p, val_len), vidpid, vidpidFromCN); + SuccessOrExit(error); + } + + // If Matter Attributes were not found use values extracted from the CN Attribute, + // which might be uninitialized as well. + if (!vidpid.Initialized()) + { + vidpid = vidpidFromCN; + } + +exit: + _log_mbedTLS_error(result); + mbedtls_x509_crt_free(&mbed_cert); + +#else + (void) certificate; + (void) vidpid; + CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + + return error; +} + +namespace { +CHIP_ERROR ExtractRawDNFromX509Cert(bool extractSubject, const ByteSpan & certificate, MutableByteSpan & dn) +{ +#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 mbedCertificate; + + ReturnErrorCodeIf(certificate.empty(), CHIP_ERROR_INVALID_ARGUMENT); + + mbedtls_x509_crt_init(&mbedCertificate); + result = mbedtls_x509_crt_parse(&mbedCertificate, Uint8::to_const_uchar(certificate.data()), certificate.size()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + if (extractSubject) + { + len = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(subject_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(len); + p = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(subject_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(p); + } + else + { + len = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(issuer_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(len); + p = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(issuer_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(p); + } + + VerifyOrExit(len <= dn.size(), error = CHIP_ERROR_BUFFER_TOO_SMALL); + memcpy(dn.data(), p, len); + dn.reduce_size(len); + +exit: + _log_mbedTLS_error(result); + mbedtls_x509_crt_free(&mbedCertificate); + +#else + (void) certificate; + (void) dn; + CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + + return error; +} +} // namespace + +CHIP_ERROR ExtractSubjectFromX509Cert(const ByteSpan & certificate, MutableByteSpan & subject) +{ + return ExtractRawDNFromX509Cert(true, certificate, subject); +} + +CHIP_ERROR ExtractIssuerFromX509Cert(const ByteSpan & certificate, MutableByteSpan & issuer) +{ + return ExtractRawDNFromX509Cert(false, certificate, issuer); +} + +CHIP_ERROR ReplaceCertIfResignedCertFound(const ByteSpan & referenceCertificate, const ByteSpan * candidateCertificates, + size_t candidateCertificatesCount, ByteSpan & outCertificate) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + uint8_t referenceSubjectBuf[kMaxCertificateDistinguishedNameLength]; + uint8_t referenceSKIDBuf[kSubjectKeyIdentifierLength]; + MutableByteSpan referenceSubject(referenceSubjectBuf); + MutableByteSpan referenceSKID(referenceSKIDBuf); + + outCertificate = referenceCertificate; + + ReturnErrorCodeIf(candidateCertificates == nullptr || candidateCertificatesCount == 0, CHIP_NO_ERROR); + + ReturnErrorOnFailure(ExtractSubjectFromX509Cert(referenceCertificate, referenceSubject)); + ReturnErrorOnFailure(ExtractSKIDFromX509Cert(referenceCertificate, referenceSKID)); + + for (size_t i = 0; i < candidateCertificatesCount; i++) + { + const ByteSpan candidateCertificate = candidateCertificates[i]; + uint8_t candidateSubjectBuf[kMaxCertificateDistinguishedNameLength]; + uint8_t candidateSKIDBuf[kSubjectKeyIdentifierLength]; + MutableByteSpan candidateSubject(candidateSubjectBuf); + MutableByteSpan candidateSKID(candidateSKIDBuf); + + ReturnErrorOnFailure(ExtractSubjectFromX509Cert(candidateCertificate, candidateSubject)); + ReturnErrorOnFailure(ExtractSKIDFromX509Cert(candidateCertificate, candidateSKID)); + + if (referenceSKID.data_equal(candidateSKID) && referenceSubject.data_equal(candidateSubject)) + { + outCertificate = candidateCertificate; + return CHIP_NO_ERROR; + } + } + + return CHIP_NO_ERROR; +#else + (void) referenceCertificate; + (void) candidateCertificates; + (void) candidateCertificatesCount; + (void) outCertificate; + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) +} + +} // namespace Crypto +} // namespace chip