From 29c01ee60a9d63805a2d8a29cc08257ca4006167 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas Date: Thu, 1 Sep 2022 14:46:21 +0200 Subject: [PATCH 1/8] Add AttestationTrustStore::GetCertificationDeclarationCert virtual method to allow controllers passing in some CD certs --- .../DefaultDeviceAttestationVerifier.cpp | 18 ++++++++++----- .../DeviceAttestationVerifier.h | 22 +++++++++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp b/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp index 07db1d3726d9b9..458b1c650c76e3 100644 --- a/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp +++ b/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp @@ -93,7 +93,7 @@ AttestationVerificationResult MapError(CertificateChainValidationResult certific * * Current version uses only test key/cert provided in spec. */ -CHIP_ERROR GetCertificationDeclarationCertificate(const ByteSpan & skid, MutableByteSpan & outCertificate) +CHIP_ERROR GetDevelopmentCDSigningKey(const ByteSpan & skid, Crypto::P256PublicKey & pubKey) { struct CertChainLookupTable { @@ -142,7 +142,7 @@ CHIP_ERROR GetCertificationDeclarationCertificate(const ByteSpan & skid, Mutable VerifyOrReturnError(certChainLookupTableIdx < ArraySize(sCertChainLookupTable), CHIP_ERROR_INVALID_ARGUMENT); - return CopySpanToMutableSpan(ByteSpan{ sCertChainLookupTable[certChainLookupTableIdx].mCertificate }, outCertificate); + return Crypto::ExtractPubkeyFromX509Cert(ByteSpan{ sCertChainLookupTable[certChainLookupTableIdx].mCertificate }, pubKey); } } // namespace @@ -283,17 +283,23 @@ void DefaultDACVerifier::VerifyAttestationInformation(const DeviceAttestationVer AttestationVerificationResult DefaultDACVerifier::ValidateCertificationDeclarationSignature(const ByteSpan & cmsEnvelopeBuffer, ByteSpan & certDeclBuffer) { - uint8_t certificate[Credentials::kMaxDERCertLength]; - MutableByteSpan certificateSpan(certificate); ByteSpan skid; VerifyOrReturnError(CMS_ExtractKeyId(cmsEnvelopeBuffer, skid) == CHIP_NO_ERROR, AttestationVerificationResult::kCertificationDeclarationNoKeyId); - VerifyOrReturnError(GetCertificationDeclarationCertificate(skid, certificateSpan) == CHIP_NO_ERROR, + Crypto::P256PublicKey pubKey; + auto err = mAttestationTrustStore->GetCertificationDeclarationSigningKey(skid, pubKey); + VerifyOrReturnError(err == CHIP_NO_ERROR || err == CHIP_ERROR_NOT_IMPLEMENTED, AttestationVerificationResult::kCertificationDeclarationNoCertificateFound); - VerifyOrReturnError(CMS_Verify(cmsEnvelopeBuffer, certificateSpan, certDeclBuffer) == CHIP_NO_ERROR, + if (err == CHIP_ERROR_NOT_IMPLEMENTED) + { + VerifyOrReturnError(GetDevelopmentCDSigningKey(skid, pubKey) == CHIP_NO_ERROR, + AttestationVerificationResult::kCertificationDeclarationNoCertificateFound); + } + + VerifyOrReturnError(CMS_Verify(cmsEnvelopeBuffer, pubKey, certDeclBuffer) == CHIP_NO_ERROR, AttestationVerificationResult::kCertificationDeclarationInvalidSignature); return AttestationVerificationResult::kSuccess; diff --git a/src/credentials/attestation_verifier/DeviceAttestationVerifier.h b/src/credentials/attestation_verifier/DeviceAttestationVerifier.h index 756759d32ebe03..2d0198e71bcf79 100644 --- a/src/credentials/attestation_verifier/DeviceAttestationVerifier.h +++ b/src/credentials/attestation_verifier/DeviceAttestationVerifier.h @@ -146,6 +146,28 @@ class AttestationTrustStore * */ virtual CHIP_ERROR GetProductAttestationAuthorityCert(const ByteSpan & skid, MutableByteSpan & outPaaDerBuffer) const = 0; + + /** + * @brief Look-up a CD signing key by SKID + * + * The implementations of this interface must have access to a set of CDs to trust. + * + * Interface is synchronous, and therefore this should not be used unless to expose a CD + * store that is both fully local and quick to access. + * + * @param[in] skid Key identifier (SKID) of the CD key to look-up (SHA-1 of public key) + * @param[in,out] pubKey Reference to public key object that gets updated on success. + * + * @returns CHIP_NO_ERROR on success, CHIP_INVALID_ARGUMENT if `skid` is not usable + * CHIP_ERROR_CA_CERT_NOT_FOUND if no CD signing key found that matches `skid. + * By default CHIP_ERROR_NOT_IMPLEMENTED is returned if the method is not overriden. Depending on the + * implementation it may results into using the development CD signing key. + * + */ + virtual CHIP_ERROR GetCertificationDeclarationSigningKey(const ByteSpan & skid, Crypto::P256PublicKey & pubKey) const + { + return CHIP_ERROR_NOT_IMPLEMENTED; + } }; /** From 88fdd34b7227f0bcfbd0cb3913870fdbcfbbea24 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas Date: Thu, 1 Sep 2022 14:47:40 +0200 Subject: [PATCH 2/8] Add cdCerts member to MTRControllerFactoryparams and override AttestationTrustStore::GetCertificationDeclarationCert --- .../CHIP/MTRAttestationTrustStoreBridge.h | 9 ++++- .../CHIP/MTRAttestationTrustStoreBridge.mm | 37 +++++++++++++++++-- .../Framework/CHIP/MTRControllerFactory.h | 6 +++ .../Framework/CHIP/MTRControllerFactory.mm | 5 ++- 4 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/darwin/Framework/CHIP/MTRAttestationTrustStoreBridge.h b/src/darwin/Framework/CHIP/MTRAttestationTrustStoreBridge.h index ced955cb1c8b1d..c12b0cc149ff2f 100644 --- a/src/darwin/Framework/CHIP/MTRAttestationTrustStoreBridge.h +++ b/src/darwin/Framework/CHIP/MTRAttestationTrustStoreBridge.h @@ -22,8 +22,9 @@ NS_ASSUME_NONNULL_BEGIN class MTRAttestationTrustStoreBridge : public chip::Credentials::AttestationTrustStore { public: - MTRAttestationTrustStoreBridge(NSArray * paaCerts) + MTRAttestationTrustStoreBridge(NSArray * _Nullable paaCerts, NSArray * _Nullable cdCerts) : mPaaCerts(paaCerts) + , mCDCerts(cdCerts) { } ~MTRAttestationTrustStoreBridge() {}; @@ -31,8 +32,12 @@ class MTRAttestationTrustStoreBridge : public chip::Credentials::AttestationTrus CHIP_ERROR GetProductAttestationAuthorityCert( const chip::ByteSpan & skid, chip::MutableByteSpan & outPaaDerBuffer) const override; + CHIP_ERROR GetCertificationDeclarationSigningKey( + const chip::ByteSpan & skid, chip::Crypto::P256PublicKey & pubKey) const override; + private: - NSArray * mPaaCerts; + NSArray * _Nullable mPaaCerts; + NSArray * _Nullable mCDCerts; }; NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRAttestationTrustStoreBridge.mm b/src/darwin/Framework/CHIP/MTRAttestationTrustStoreBridge.mm index ee88d420624d4a..5dae1736de2ba8 100644 --- a/src/darwin/Framework/CHIP/MTRAttestationTrustStoreBridge.mm +++ b/src/darwin/Framework/CHIP/MTRAttestationTrustStoreBridge.mm @@ -16,12 +16,15 @@ */ #import "MTRAttestationTrustStoreBridge.h" - -static chip::ByteSpan asByteSpan(NSData * value) { return chip::ByteSpan(static_cast(value.bytes), value.length); } +#import "NSDataSpanConversion.h" CHIP_ERROR MTRAttestationTrustStoreBridge::GetProductAttestationAuthorityCert( const chip::ByteSpan & skid, chip::MutableByteSpan & outPaaDerBuffer) const { + if (mPaaCerts == nil) { + return CHIP_ERROR_NOT_IMPLEMENTED; + } + VerifyOrReturnError(skid.size() == chip::Crypto::kSubjectKeyIdentifierLength, CHIP_ERROR_INVALID_ARGUMENT); size_t paaIdx; @@ -29,7 +32,7 @@ for (paaIdx = 0; paaIdx < mPaaCerts.count; ++paaIdx) { uint8_t skidBuf[chip::Crypto::kSubjectKeyIdentifierLength] = { 0 }; - candidate = asByteSpan(mPaaCerts[paaIdx]); + candidate = AsByteSpan(mPaaCerts[paaIdx]); chip::MutableByteSpan candidateSkidSpan { skidBuf }; VerifyOrReturnError( CHIP_NO_ERROR == chip::Crypto::ExtractSKIDFromX509Cert(candidate, candidateSkidSpan), CHIP_ERROR_INTERNAL); @@ -41,3 +44,31 @@ } return CHIP_ERROR_CA_CERT_NOT_FOUND; } + +CHIP_ERROR MTRAttestationTrustStoreBridge::GetCertificationDeclarationSigningKey( + const chip::ByteSpan & skid, chip::Crypto::P256PublicKey & pubKey) const +{ + if (mCDCerts == nil) { + return CHIP_ERROR_NOT_IMPLEMENTED; + } + + VerifyOrReturnError(skid.size() == chip::Crypto::kSubjectKeyIdentifierLength, CHIP_ERROR_INVALID_ARGUMENT); + + size_t cdIdx; + chip::ByteSpan candidate; + + for (cdIdx = 0; cdIdx < mCDCerts.count; ++cdIdx) { + uint8_t skidBuf[chip::Crypto::kSubjectKeyIdentifierLength] = { 0 }; + candidate = AsByteSpan(mCDCerts[cdIdx]); + chip::MutableByteSpan candidateSkidSpan { skidBuf }; + VerifyOrReturnError( + CHIP_NO_ERROR == chip::Crypto::ExtractSKIDFromX509Cert(candidate, candidateSkidSpan), CHIP_ERROR_INTERNAL); + + if (skid.data_equal(candidateSkidSpan)) { + // Found a match + return chip::Crypto::ExtractPubkeyFromX509Cert(candidate, pubKey); + } + } + + return CHIP_ERROR_CA_CERT_NOT_FOUND; +} diff --git a/src/darwin/Framework/CHIP/MTRControllerFactory.h b/src/darwin/Framework/CHIP/MTRControllerFactory.h index 8e8eb8a9fcf318..508cf369374bae 100644 --- a/src/darwin/Framework/CHIP/MTRControllerFactory.h +++ b/src/darwin/Framework/CHIP/MTRControllerFactory.h @@ -51,6 +51,12 @@ NS_ASSUME_NONNULL_BEGIN * */ @property (nonatomic, copy, nullable) NSArray * paaCerts; +/* + * The Certificate Declaration certificates that are trusted to sign + * device attestation information. Defaults to nil. + * + */ +@property (nonatomic, copy, nullable) NSArray * cdCerts; /* * The network port to bind to. If not specified, an ephemeral port will be * used. diff --git a/src/darwin/Framework/CHIP/MTRControllerFactory.mm b/src/darwin/Framework/CHIP/MTRControllerFactory.mm index 337299bde13570..35084164f50878 100644 --- a/src/darwin/Framework/CHIP/MTRControllerFactory.mm +++ b/src/darwin/Framework/CHIP/MTRControllerFactory.mm @@ -257,8 +257,8 @@ - (BOOL)startup:(MTRControllerFactoryParams *)startupParams // Initialize device attestation verifier const Credentials::AttestationTrustStore * trustStore; - if (startupParams.paaCerts) { - _attestationTrustStoreBridge = new MTRAttestationTrustStoreBridge(startupParams.paaCerts); + if (startupParams.paaCerts || startupParams.cdCerts) { + _attestationTrustStoreBridge = new MTRAttestationTrustStoreBridge(startupParams.paaCerts, startupParams.cdCerts); if (_attestationTrustStoreBridge == nullptr) { MTR_LOG_ERROR("Error: %@", kErrorAttestationTrustStoreInit); return; @@ -612,6 +612,7 @@ - (instancetype)initWithStorage:(id)storageDelegat _storageDelegate = storageDelegate; _otaProviderDelegate = nil; _paaCerts = nil; + _cdCerts = nil; _port = nil; _startServer = NO; From edc8b208bb0a4b89411bebcfcdf2574425b52af5 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas Date: Thu, 1 Sep 2022 20:23:14 +0200 Subject: [PATCH 3/8] Implement ArrayTrustStore::GetCertificationDeclarationSigningKey and initialize the test ArrayTrustStore store with the test CD cert --- .../DefaultDeviceAttestationVerifier.cpp | 89 ++++++------------- .../DeviceAttestationVerifier.h | 32 ++++++- 2 files changed, 60 insertions(+), 61 deletions(-) diff --git a/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp b/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp index 458b1c650c76e3..708a723d58411b 100644 --- a/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp +++ b/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp @@ -50,7 +50,34 @@ static const ByteSpan kTestPaaRoots[] = { TestCerts::sTestCert_PAA_NoVID_Cert, }; -const ArrayAttestationTrustStore kTestAttestationTrustStore{ &kTestPaaRoots[0], ArraySize(kTestPaaRoots) }; +constexpr uint8_t sTestCert_CD_Cert_Array[] = { + 0x30, 0x82, 0x01, 0xb3, 0x30, 0x82, 0x01, 0x5a, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x08, 0x45, 0xda, 0xf3, 0x9d, 0xe4, 0x7a, + 0xa0, 0x8f, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x2b, 0x31, 0x29, 0x30, 0x27, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x0c, 0x20, 0x4d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x44, 0x20, + 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x20, 0x17, 0x0d, + 0x32, 0x31, 0x30, 0x36, 0x32, 0x38, 0x31, 0x34, 0x32, 0x33, 0x34, 0x33, 0x5a, 0x18, 0x0f, 0x39, 0x39, 0x39, 0x39, 0x31, 0x32, + 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x2b, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, + 0x20, 0x4d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x44, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, + 0x6e, 0x67, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, + 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x3c, 0x39, 0x89, + 0x22, 0x45, 0x2b, 0x55, 0xca, 0xf3, 0x89, 0xc2, 0x5b, 0xd1, 0xbc, 0xa4, 0x65, 0x69, 0x52, 0xcc, 0xb9, 0x0e, 0x88, 0x69, 0x24, + 0x9a, 0xd8, 0x47, 0x46, 0x53, 0x01, 0x4c, 0xbf, 0x95, 0xd6, 0x87, 0x96, 0x5e, 0x03, 0x6b, 0x52, 0x1c, 0x51, 0x03, 0x7e, 0x6b, + 0x8c, 0xed, 0xef, 0xca, 0x1e, 0xb4, 0x40, 0x46, 0x69, 0x4f, 0xa0, 0x88, 0x82, 0xee, 0xd6, 0x51, 0x9d, 0xec, 0xba, 0xa3, 0x66, + 0x30, 0x64, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, + 0x01, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x62, 0xfa, 0x82, 0x33, 0x59, 0xac, 0xfa, 0xa9, 0x96, 0x3e, 0x1c, 0xfa, 0x14, 0x0a, + 0xdd, 0xf5, 0x04, 0xf3, 0x71, 0x60, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x62, 0xfa, + 0x82, 0x33, 0x59, 0xac, 0xfa, 0xa9, 0x96, 0x3e, 0x1c, 0xfa, 0x14, 0x0a, 0xdd, 0xf5, 0x04, 0xf3, 0x71, 0x60, 0x30, 0x0a, 0x06, + 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, 0x2c, 0x54, 0x5c, 0xe4, 0xe4, + 0x57, 0xd8, 0xa6, 0xf0, 0xd9, 0xd9, 0xbb, 0xeb, 0xd6, 0xec, 0xe1, 0xdd, 0xfe, 0x7f, 0x8c, 0x6d, 0x9a, 0x6c, 0xf3, 0x75, 0x32, + 0x1f, 0xc6, 0xfa, 0xc7, 0x13, 0x84, 0x02, 0x20, 0x54, 0x07, 0x78, 0xe8, 0x74, 0x39, 0x72, 0x52, 0x7e, 0xed, 0xeb, 0xaf, 0x58, + 0x68, 0x62, 0x20, 0xb5, 0x40, 0x78, 0xf2, 0xcd, 0x4e, 0x62, 0xa7, 0x6a, 0xe7, 0xcb, 0xb9, 0x2f, 0xf5, 0x4c, 0x8b +}; + +static const ByteSpan kTestCDCerts[] = { ByteSpan(sTestCert_CD_Cert_Array) }; + +const ArrayAttestationTrustStore kTestAttestationTrustStore{ &kTestPaaRoots[0], ArraySize(kTestPaaRoots), &kTestCDCerts[0], + ArraySize(kTestCDCerts) }; AttestationVerificationResult MapError(CertificateChainValidationResult certificateChainValidationResult) { @@ -87,64 +114,6 @@ AttestationVerificationResult MapError(CertificateChainValidationResult certific return AttestationVerificationResult::kInternalError; } } - -/** - * @brief Look-up of well-known keys used for CD signing by CSA. - * - * Current version uses only test key/cert provided in spec. - */ -CHIP_ERROR GetDevelopmentCDSigningKey(const ByteSpan & skid, Crypto::P256PublicKey & pubKey) -{ - struct CertChainLookupTable - { - const uint8_t mCertificate[kMax_x509_Certificate_Length]; - const uint8_t mSKID[Crypto::kSubjectKeyIdentifierLength]; - }; - - static CertChainLookupTable - sCertChainLookupTable[] = { - { { 0x30, 0x82, 0x01, 0xb3, 0x30, 0x82, 0x01, 0x5a, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x08, 0x45, 0xda, 0xf3, 0x9d, - 0xe4, 0x7a, 0xa0, 0x8f, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x2b, 0x31, - 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x20, 0x4d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x20, 0x54, 0x65, - 0x73, 0x74, 0x20, 0x43, 0x44, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x74, 0x79, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x36, 0x32, 0x38, 0x31, 0x34, 0x32, 0x33, 0x34, - 0x33, 0x5a, 0x18, 0x0f, 0x39, 0x39, 0x39, 0x39, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, - 0x30, 0x2b, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x20, 0x4d, 0x61, 0x74, 0x74, 0x65, 0x72, - 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x44, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x41, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, - 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x3c, 0x39, 0x89, 0x22, - 0x45, 0x2b, 0x55, 0xca, 0xf3, 0x89, 0xc2, 0x5b, 0xd1, 0xbc, 0xa4, 0x65, 0x69, 0x52, 0xcc, 0xb9, 0x0e, 0x88, 0x69, - 0x24, 0x9a, 0xd8, 0x47, 0x46, 0x53, 0x01, 0x4c, 0xbf, 0x95, 0xd6, 0x87, 0x96, 0x5e, 0x03, 0x6b, 0x52, 0x1c, 0x51, - 0x03, 0x7e, 0x6b, 0x8c, 0xed, 0xef, 0xca, 0x1e, 0xb4, 0x40, 0x46, 0x69, 0x4f, 0xa0, 0x88, 0x82, 0xee, 0xd6, 0x51, - 0x9d, 0xec, 0xba, 0xa3, 0x66, 0x30, 0x64, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, - 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, - 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x62, 0xfa, 0x82, - 0x33, 0x59, 0xac, 0xfa, 0xa9, 0x96, 0x3e, 0x1c, 0xfa, 0x14, 0x0a, 0xdd, 0xf5, 0x04, 0xf3, 0x71, 0x60, 0x30, 0x1f, - 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x62, 0xfa, 0x82, 0x33, 0x59, 0xac, 0xfa, 0xa9, - 0x96, 0x3e, 0x1c, 0xfa, 0x14, 0x0a, 0xdd, 0xf5, 0x04, 0xf3, 0x71, 0x60, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, - 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, 0x2c, 0x54, 0x5c, 0xe4, 0xe4, 0x57, 0xd8, - 0xa6, 0xf0, 0xd9, 0xd9, 0xbb, 0xeb, 0xd6, 0xec, 0xe1, 0xdd, 0xfe, 0x7f, 0x8c, 0x6d, 0x9a, 0x6c, 0xf3, 0x75, 0x32, - 0x1f, 0xc6, 0xfa, 0xc7, 0x13, 0x84, 0x02, 0x20, 0x54, 0x07, 0x78, 0xe8, 0x74, 0x39, 0x72, 0x52, 0x7e, 0xed, 0xeb, - 0xaf, 0x58, 0x68, 0x62, 0x20, 0xb5, 0x40, 0x78, 0xf2, 0xcd, 0x4e, 0x62, 0xa7, 0x6a, 0xe7, 0xcb, 0xb9, 0x2f, 0xf5, - 0x4c, 0x8b }, - { 0x62, 0xfa, 0x82, 0x33, 0x59, 0xac, 0xfa, 0xa9, 0x96, 0x3e, - 0x1c, 0xfa, 0x14, 0x0a, 0xdd, 0xf5, 0x04, 0xf3, 0x71, 0x60 } } - }; - - size_t certChainLookupTableIdx; - for (certChainLookupTableIdx = 0; certChainLookupTableIdx < ArraySize(sCertChainLookupTable); ++certChainLookupTableIdx) - { - if (skid.data_equal(ByteSpan(sCertChainLookupTable[certChainLookupTableIdx].mSKID))) - { - break; - } - } - - VerifyOrReturnError(certChainLookupTableIdx < ArraySize(sCertChainLookupTable), CHIP_ERROR_INVALID_ARGUMENT); - - return Crypto::ExtractPubkeyFromX509Cert(ByteSpan{ sCertChainLookupTable[certChainLookupTableIdx].mCertificate }, pubKey); -} - } // namespace void DefaultDACVerifier::VerifyAttestationInformation(const DeviceAttestationVerifier::AttestationInfo & info, @@ -295,7 +264,7 @@ AttestationVerificationResult DefaultDACVerifier::ValidateCertificationDeclarati if (err == CHIP_ERROR_NOT_IMPLEMENTED) { - VerifyOrReturnError(GetDevelopmentCDSigningKey(skid, pubKey) == CHIP_NO_ERROR, + VerifyOrReturnError(kTestAttestationTrustStore.GetCertificationDeclarationSigningKey(skid, pubKey) == CHIP_NO_ERROR, AttestationVerificationResult::kCertificationDeclarationNoCertificateFound); } diff --git a/src/credentials/attestation_verifier/DeviceAttestationVerifier.h b/src/credentials/attestation_verifier/DeviceAttestationVerifier.h index 2d0198e71bcf79..5116c64e5434c6 100644 --- a/src/credentials/attestation_verifier/DeviceAttestationVerifier.h +++ b/src/credentials/attestation_verifier/DeviceAttestationVerifier.h @@ -180,7 +180,9 @@ class AttestationTrustStore class ArrayAttestationTrustStore : public AttestationTrustStore { public: - ArrayAttestationTrustStore(const ByteSpan * derCerts, size_t numCerts) : mDerCerts(derCerts), mNumCerts(numCerts) {} + ArrayAttestationTrustStore(const ByteSpan * derCerts, size_t numCerts, const ByteSpan * cdDerCerts, size_t numCDCerts) : + mDerCerts(derCerts), mNumCerts(numCerts), mCDDerCerts(cdDerCerts), mNumCDCerts(numCDCerts) + {} CHIP_ERROR GetProductAttestationAuthorityCert(const ByteSpan & skid, MutableByteSpan & outPaaDerBuffer) const override { @@ -208,9 +210,37 @@ class ArrayAttestationTrustStore : public AttestationTrustStore return CHIP_ERROR_CA_CERT_NOT_FOUND; } + CHIP_ERROR GetCertificationDeclarationSigningKey(const ByteSpan & skid, Crypto::P256PublicKey & pubKey) const override + { + VerifyOrReturnError(!skid.empty() && (skid.data() != nullptr), CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(skid.size() == Crypto::kSubjectKeyIdentifierLength, CHIP_ERROR_INVALID_ARGUMENT); + + size_t cdIdx; + ByteSpan candidate; + + for (cdIdx = 0; cdIdx < mNumCDCerts; ++cdIdx) + { + uint8_t skidBuf[Crypto::kSubjectKeyIdentifierLength] = { 0 }; + candidate = mCDDerCerts[cdIdx]; + MutableByteSpan candidateSkidSpan{ skidBuf }; + VerifyOrReturnError(CHIP_NO_ERROR == Crypto::ExtractSKIDFromX509Cert(candidate, candidateSkidSpan), + CHIP_ERROR_INTERNAL); + + if (skid.data_equal(candidateSkidSpan)) + { + // Found a match + return Crypto::ExtractPubkeyFromX509Cert(candidate, pubKey); + } + } + + return CHIP_ERROR_CA_CERT_NOT_FOUND; + } + protected: const ByteSpan * mDerCerts; const size_t mNumCerts; + const ByteSpan * mCDDerCerts; + const size_t mNumCDCerts; }; class DeviceAttestationVerifier From d91f683073a2a0a1ef8ca5b2bc8b77e3fca8cb21 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas Date: Fri, 2 Sep 2022 11:19:50 +0200 Subject: [PATCH 4/8] Update the FileAttestationTrustStore to read a directory with der certs for certification declaration verification --- .../DefaultDeviceAttestationVerifier.cpp | 10 ++- .../FileAttestationTrustStore.cpp | 77 ++++++++++++++++--- .../FileAttestationTrustStore.h | 15 ++-- 3 files changed, 87 insertions(+), 15 deletions(-) diff --git a/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp b/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp index 708a723d58411b..2966fb4b047420 100644 --- a/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp +++ b/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp @@ -172,6 +172,7 @@ void DefaultDACVerifier::VerifyAttestationInformation(const DeviceAttestationVer uint8_t akidBuf[Crypto::kAuthorityKeyIdentifierLength]; MutableByteSpan akid(akidBuf); constexpr size_t paaCertAllocatedLen = kMaxDERCertLength; + CHIP_ERROR err = CHIP_NO_ERROR; VerifyOrExit(ExtractAKIDFromX509Cert(info.paiDerBuffer, akid) == CHIP_NO_ERROR, attestationError = AttestationVerificationResult::kPaiFormatInvalid); @@ -179,9 +180,16 @@ void DefaultDACVerifier::VerifyAttestationInformation(const DeviceAttestationVer VerifyOrExit(paaCert.Alloc(paaCertAllocatedLen), attestationError = AttestationVerificationResult::kNoMemory); paaDerBuffer = MutableByteSpan(paaCert.Get(), paaCertAllocatedLen); - VerifyOrExit(mAttestationTrustStore->GetProductAttestationAuthorityCert(akid, paaDerBuffer) == CHIP_NO_ERROR, + err = mAttestationTrustStore->GetProductAttestationAuthorityCert(akid, paaDerBuffer); + VerifyOrExit(err == CHIP_NO_ERROR || err == CHIP_ERROR_NOT_IMPLEMENTED, attestationError = AttestationVerificationResult::kPaaNotFound); + if (err == CHIP_ERROR_NOT_IMPLEMENTED) + { + VerifyOrExit(kTestAttestationTrustStore.GetProductAttestationAuthorityCert(akid, paaDerBuffer) == CHIP_NO_ERROR, + attestationError = AttestationVerificationResult::kPaaNotFound); + } + VerifyOrExit(ExtractVIDPIDFromX509Cert(paaDerBuffer, paaVidPid) == CHIP_NO_ERROR, attestationError = AttestationVerificationResult::kPaaFormatInvalid); diff --git a/src/credentials/attestation_verifier/FileAttestationTrustStore.cpp b/src/credentials/attestation_verifier/FileAttestationTrustStore.cpp index 147f97ce08bd04..67c210260f082d 100644 --- a/src/credentials/attestation_verifier/FileAttestationTrustStore.cpp +++ b/src/credentials/attestation_verifier/FileAttestationTrustStore.cpp @@ -39,11 +39,31 @@ const char * GetFilenameExtension(const char * filename) } } // namespace -FileAttestationTrustStore::FileAttestationTrustStore(const char * paaTrustStorePath) +FileAttestationTrustStore::FileAttestationTrustStore(const char * paaTrustStorePath, const char * cdTrustStorePath) +{ + VerifyOrReturn(paaTrustStorePath != nullptr || cdTrustStorePath != nullptr); + + if (paaTrustStorePath != nullptr) + { + LoadTrustStore(paaTrustStorePath, mPAADerCerts); + VerifyOrReturn(paaCount()); + } + + if (cdTrustStorePath != nullptr) + { + LoadTrustStore(cdTrustStorePath, mCDDerCerts); + VerifyOrReturn(cdCount()); + } + + mIsInitialized = true; +} + +void FileAttestationTrustStore::LoadTrustStore(const char * trustStorePath, + std::vector> & certs) { DIR * dir; - dir = opendir(paaTrustStorePath); + dir = opendir(trustStorePath); if (dir != nullptr) { // Nested directories are not handled. @@ -56,7 +76,7 @@ FileAttestationTrustStore::FileAttestationTrustStore(const char * paaTrustStoreP FILE * file; std::array certificate; - std::string filename(paaTrustStorePath); + std::string filename(trustStorePath); filename += std::string("/") + std::string(entry->d_name); @@ -66,14 +86,13 @@ FileAttestationTrustStore::FileAttestationTrustStore(const char * paaTrustStoreP uint32_t certificateLength = fread(certificate.data(), sizeof(uint8_t), kMaxDERCertLength, file); if (certificateLength > 0) { - mDerCerts.push_back(certificate); - mIsInitialized = true; + certs.push_back(certificate); } fclose(file); } else { - Cleanup(); + certs.clear(); break; } } @@ -89,18 +108,26 @@ FileAttestationTrustStore::~FileAttestationTrustStore() void FileAttestationTrustStore::Cleanup() { - mDerCerts.clear(); + mPAADerCerts.clear(); + mCDDerCerts.clear(); mIsInitialized = false; } CHIP_ERROR FileAttestationTrustStore::GetProductAttestationAuthorityCert(const ByteSpan & skid, MutableByteSpan & outPaaDerBuffer) const { - VerifyOrReturnError(!mDerCerts.empty(), CHIP_ERROR_CA_CERT_NOT_FOUND); + // If the constructor has not tried to initialize the PAA certificates database, return CHIP_ERROR_NOT_IMPLEMENTED to use the + // testing trust store if the DefaultAttestationVerifier is in use. + if (mIsInitialized && paaCount() == 0) + { + return CHIP_ERROR_NOT_IMPLEMENTED; + } + + VerifyOrReturnError(!mPAADerCerts.empty(), CHIP_ERROR_CA_CERT_NOT_FOUND); VerifyOrReturnError(!skid.empty() && (skid.data() != nullptr), CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(skid.size() == Crypto::kSubjectKeyIdentifierLength, CHIP_ERROR_INVALID_ARGUMENT); - for (auto candidate : mDerCerts) + for (auto candidate : mPAADerCerts) { uint8_t skidBuf[Crypto::kSubjectKeyIdentifierLength] = { 0 }; MutableByteSpan candidateSkidSpan{ skidBuf }; @@ -119,5 +146,37 @@ CHIP_ERROR FileAttestationTrustStore::GetProductAttestationAuthorityCert(const B return CHIP_ERROR_CA_CERT_NOT_FOUND; } +CHIP_ERROR FileAttestationTrustStore::GetCertificationDeclarationSigningKey(const ByteSpan & skid, + Crypto::P256PublicKey & pubKey) const +{ + // If the constructor has not tried to initialize the certification declaration certificates database, return + // CHIP_ERROR_NOT_IMPLEMENTED to use the testing trust store if the DefaultAttestationVerifier is in use. + if (mIsInitialized && cdCount() == 0) + { + return CHIP_ERROR_NOT_IMPLEMENTED; + } + + VerifyOrReturnError(!mCDDerCerts.empty(), CHIP_ERROR_CA_CERT_NOT_FOUND); + VerifyOrReturnError(!skid.empty() && (skid.data() != nullptr), CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(skid.size() == Crypto::kSubjectKeyIdentifierLength, CHIP_ERROR_INVALID_ARGUMENT); + + for (auto candidate : mCDDerCerts) + { + uint8_t skidBuf[Crypto::kSubjectKeyIdentifierLength] = { 0 }; + MutableByteSpan candidateSkidSpan{ skidBuf }; + VerifyOrReturnError(CHIP_NO_ERROR == + Crypto::ExtractSKIDFromX509Cert(ByteSpan{ candidate.data(), candidate.size() }, candidateSkidSpan), + CHIP_ERROR_INTERNAL); + + if (skid.data_equal(candidateSkidSpan)) + { + // Found a match + return Crypto::ExtractPubkeyFromX509Cert(ByteSpan{ candidate.data(), candidate.size() }, pubKey); + } + } + + return CHIP_ERROR_CA_CERT_NOT_FOUND; +} + } // namespace Credentials } // namespace chip diff --git a/src/credentials/attestation_verifier/FileAttestationTrustStore.h b/src/credentials/attestation_verifier/FileAttestationTrustStore.h index 090be169367c63..c39619177e8131 100644 --- a/src/credentials/attestation_verifier/FileAttestationTrustStore.h +++ b/src/credentials/attestation_verifier/FileAttestationTrustStore.h @@ -28,18 +28,23 @@ namespace Credentials { class FileAttestationTrustStore : public AttestationTrustStore { public: - FileAttestationTrustStore(const char * paaTrustStorePath); + FileAttestationTrustStore(const char * paaTrustStorePath = nullptr, const char * cdTrustStorePath = nullptr); ~FileAttestationTrustStore(); - bool IsInitialized() { return mIsInitialized; } - CHIP_ERROR GetProductAttestationAuthorityCert(const ByteSpan & skid, MutableByteSpan & outPaaDerBuffer) const override; - size_t size() const { return mDerCerts.size(); } + CHIP_ERROR GetCertificationDeclarationSigningKey(const ByteSpan & skid, Crypto::P256PublicKey & pubKey) const override; + + bool IsInitialized() const { return mIsInitialized; } + size_t paaCount() const { return mPAADerCerts.size(); }; + size_t cdCount() const { return mCDDerCerts.size(); }; protected: - std::vector> mDerCerts; + std::vector> mPAADerCerts; + std::vector> mCDDerCerts; private: + void LoadTrustStore(const char * trustStorePath, std::vector> & certs); + bool mIsInitialized = false; void Cleanup(); From 587f9b9e60f6417ef1640e455fe6e7df401d48d7 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas Date: Fri, 2 Sep 2022 11:05:26 +0200 Subject: [PATCH 5/8] Add credentials/development/cd-certs/ and update chip-tool to use it if desired --- .../cd-certs/Chip-Test-CD-Cert.der | Bin 0 -> 439 bytes .../chip-tool/commands/common/CHIPCommand.cpp | 87 +++++++++++------- .../chip-tool/commands/common/CHIPCommand.h | 6 +- 3 files changed, 57 insertions(+), 36 deletions(-) create mode 100644 credentials/development/cd-certs/Chip-Test-CD-Cert.der diff --git a/credentials/development/cd-certs/Chip-Test-CD-Cert.der b/credentials/development/cd-certs/Chip-Test-CD-Cert.der new file mode 100644 index 0000000000000000000000000000000000000000..6a7732980d7c27cc9c00132b76e0d50da419020f GIT binary patch literal 439 zcmXqLV%%)d#2B@JnTe5!iNp2Q=ebX+7W5l%v2kd%d7QIlVP-PWHq6)$}FigP!Q)eGBhwVvM@9;GBz=elHj)l z0z)HXATqTywTvRoxJUzGHg>Q}m>8i>VrFD#c4A<$vFucG)eb%Nx${u;#XU<>GlS0T z(nRUb6Ett_~|I};k)8d%3gJc4k>#}>^zCR_m#lbDpe?dpnyK8}S-|SkPW{_ea z1oW1yFeBrC77hb8AjQPUXut;&;|GZWJieKj$HF=G4sSAZ0h%ZE=&qKAu&&$gx^^9;pWZVudls%c=um@PwuRo x&!tB4$9^3bZedagVXt^mVp$Ya_xAPrh>RqKtqv8R&iW-S&w74(r~X%;ZUDT0mhb=o literal 0 HcmV?d00001 diff --git a/examples/chip-tool/commands/common/CHIPCommand.cpp b/examples/chip-tool/commands/common/CHIPCommand.cpp index 89dd1d536e4550..112103eeaf37c1 100644 --- a/examples/chip-tool/commands/common/CHIPCommand.cpp +++ b/examples/chip-tool/commands/common/CHIPCommand.cpp @@ -36,27 +36,63 @@ std::set CHIPCommand::sDeferredCleanups; using DeviceControllerFactory = chip::Controller::DeviceControllerFactory; -constexpr chip::FabricId kIdentityNullFabricId = chip::kUndefinedFabricId; -constexpr chip::FabricId kIdentityAlphaFabricId = 1; -constexpr chip::FabricId kIdentityBetaFabricId = 2; -constexpr chip::FabricId kIdentityGammaFabricId = 3; -constexpr chip::FabricId kIdentityOtherFabricId = 4; -constexpr const char * kTrustStorePathVariable = "CHIPTOOL_PAA_TRUST_STORE_PATH"; - -const chip::Credentials::AttestationTrustStore * CHIPCommand::sPaaTrustStore = nullptr; +constexpr chip::FabricId kIdentityNullFabricId = chip::kUndefinedFabricId; +constexpr chip::FabricId kIdentityAlphaFabricId = 1; +constexpr chip::FabricId kIdentityBetaFabricId = 2; +constexpr chip::FabricId kIdentityGammaFabricId = 3; +constexpr chip::FabricId kIdentityOtherFabricId = 4; +constexpr const char * kPAATrustStorePathVariable = "CHIPTOOL_PAA_TRUST_STORE_PATH"; +constexpr const char * kCDTrustStorePathVariable = "CHIPTOOL_CD_TRUST_STORE_PATH"; + +const chip::Credentials::AttestationTrustStore * CHIPCommand::sTrustStore = nullptr; chip::Credentials::GroupDataProviderImpl CHIPCommand::sGroupDataProvider{ kMaxGroupsPerFabric, kMaxGroupKeysPerFabric }; namespace { -const chip::Credentials::AttestationTrustStore * GetTestFileAttestationTrustStore(const char * paaTrustStorePath) +const CHIP_ERROR GetAttestationTrustStore(const char * paaTrustStorePath, const char * cdTrustStorePath, + const chip::Credentials::AttestationTrustStore ** trustStore) { - static chip::Credentials::FileAttestationTrustStore attestationTrustStore{ paaTrustStorePath }; + if (paaTrustStorePath == nullptr) + { + paaTrustStorePath = getenv(kPAATrustStorePathVariable); + } + + if (cdTrustStorePath == nullptr) + { + cdTrustStorePath = getenv(kCDTrustStorePathVariable); + } + + if (paaTrustStorePath == nullptr && cdTrustStorePath == nullptr) + { + *trustStore = chip::Credentials::GetTestAttestationTrustStore(); + return CHIP_NO_ERROR; + } + + static chip::Credentials::FileAttestationTrustStore attestationTrustStore{ paaTrustStorePath, cdTrustStorePath }; + + if (paaTrustStorePath != nullptr && attestationTrustStore.paaCount() == 0) + { + ChipLogError(chipTool, "No PAAs found in path: %s", paaTrustStorePath); + ChipLogError(chipTool, + "Please specify a valid path containing trusted PAA certificates using " + "the argument [--paa-trust-store-path paa/file/path] " + "or environment variable [%s=paa/file/path]", + kPAATrustStorePathVariable); + return CHIP_ERROR_INVALID_ARGUMENT; + } - if (attestationTrustStore.IsInitialized()) + if (cdTrustStorePath != nullptr && attestationTrustStore.cdCount() == 0) { - return &attestationTrustStore; + ChipLogError(chipTool, "No CDs found in path: %s", cdTrustStorePath); + ChipLogError(chipTool, + "Please specify a valid path containing trusted CD certificates using " + "the argument [--cd-trust-store-path cd/file/path] " + "or environment variable [%s=cd/file/path]", + kCDTrustStorePathVariable); + return CHIP_ERROR_INVALID_ARGUMENT; } - return nullptr; + *trustStore = &attestationTrustStore; + return CHIP_NO_ERROR; } } // namespace @@ -103,27 +139,8 @@ CHIP_ERROR CHIPCommand::MaybeSetUpStack() factoryInitParams.listenPort = port; ReturnLogErrorOnFailure(DeviceControllerFactory::GetInstance().Init(factoryInitParams)); - if (!mPaaTrustStorePath.HasValue()) - { - char * const trust_store_path = getenv(kTrustStorePathVariable); - if (trust_store_path != nullptr) - { - mPaaTrustStorePath.SetValue(trust_store_path); - } - } - sPaaTrustStore = mPaaTrustStorePath.HasValue() ? GetTestFileAttestationTrustStore(mPaaTrustStorePath.Value()) - : chip::Credentials::GetTestAttestationTrustStore(); - ; - if (mPaaTrustStorePath.HasValue() && sPaaTrustStore == nullptr) - { - ChipLogError(chipTool, "No PAAs found in path: %s", mPaaTrustStorePath.Value()); - ChipLogError(chipTool, - "Please specify a valid path containing trusted PAA certificates using" - "the argument [--paa-trust-store-path paa/file/path]" - "or environment variable [%s=paa/file/path]", - kTrustStorePathVariable); - return CHIP_ERROR_INVALID_ARGUMENT; - } + ReturnErrorOnFailure( + GetAttestationTrustStore(mPaaTrustStorePath.ValueOr(nullptr), mCDTrustStorePath.ValueOr(nullptr), &sTrustStore)); ReturnLogErrorOnFailure(InitializeCommissioner(kIdentityNull, kIdentityNullFabricId)); @@ -343,7 +360,7 @@ CHIP_ERROR CHIPCommand::InitializeCommissioner(std::string key, chip::FabricId f std::unique_ptr commissioner = std::make_unique(); chip::Controller::SetupParams commissionerParams; - ReturnLogErrorOnFailure(mCredIssuerCmds->SetupDeviceAttestation(commissionerParams, sPaaTrustStore)); + ReturnLogErrorOnFailure(mCredIssuerCmds->SetupDeviceAttestation(commissionerParams, sTrustStore)); VerifyOrReturnError(noc.Alloc(chip::Controller::kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY); VerifyOrReturnError(icac.Alloc(chip::Controller::kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY); diff --git a/examples/chip-tool/commands/common/CHIPCommand.h b/examples/chip-tool/commands/common/CHIPCommand.h index 7dd36a7c7d6214..148f79129101fd 100644 --- a/examples/chip-tool/commands/common/CHIPCommand.h +++ b/examples/chip-tool/commands/common/CHIPCommand.h @@ -65,6 +65,9 @@ class CHIPCommand : public Command AddArgument("paa-trust-store-path", &mPaaTrustStorePath, "Path to directory holding PAA certificate information. Can be absolute or relative to the current working " "directory."); + AddArgument("cd-trust-store-path", &mCDTrustStorePath, + "Path to directory holding CD certificate information. Can be absolute or relative to the current working " + "directory."); AddArgument("commissioner-name", &mCommissionerName, "Name of fabric to use. Valid values are \"alpha\", \"beta\", \"gamma\", and integers greater than or equal to " "4. The default if not specified is \"alpha\"."); @@ -156,11 +159,12 @@ class CHIPCommand : public Command chip::Optional mCommissionerNodeId; chip::Optional mBleAdapterId; chip::Optional mPaaTrustStorePath; + chip::Optional mCDTrustStorePath; chip::Optional mUseMaxSizedCerts; // Cached trust store so commands other than the original startup command // can spin up commissioners as needed. - static const chip::Credentials::AttestationTrustStore * sPaaTrustStore; + static const chip::Credentials::AttestationTrustStore * sTrustStore; static void RunQueuedCommand(intptr_t commandArg); From aa32ccd2433edccfb9ff5b0f22939c2873a30d1b Mon Sep 17 00:00:00 2001 From: Tennessee Carmel-Veilleux Date: Tue, 6 Sep 2022 17:58:25 +0200 Subject: [PATCH 6/8] Update API to match conversation - Remove CD stuff from FileAttestationTrustStore - Refactor FileAttestationTrustStore to allow loading of any X.509 cert directory - Add a command line to chip-tool to disallow test keys (`only-allow-trusted-cd-keys`) - Add plumbing to enable CD keys lookup properly without mixing-up with PAA semantics - Add official CD verifying key and official SDK CD test key in the default CD trust store as-is --- .../chip-tool/commands/common/CHIPCommand.cpp | 49 ++--- .../chip-tool/commands/common/CHIPCommand.h | 4 + .../common/CredentialIssuerCommands.h | 12 ++ .../example/ExampleCredentialIssuerCommands.h | 30 +++- .../DefaultDeviceAttestationVerifier.cpp | 167 ++++++++++++++---- .../DefaultDeviceAttestationVerifier.h | 36 ++++ .../DeviceAttestationVerifier.h | 124 ++++++++----- .../FileAttestationTrustStore.cpp | 92 ++++------ .../FileAttestationTrustStore.h | 21 ++- src/lib/core/CHIPConfig.h | 9 + 10 files changed, 375 insertions(+), 169 deletions(-) diff --git a/examples/chip-tool/commands/common/CHIPCommand.cpp b/examples/chip-tool/commands/common/CHIPCommand.cpp index 112103eeaf37c1..4baf3e15c8d07d 100644 --- a/examples/chip-tool/commands/common/CHIPCommand.cpp +++ b/examples/chip-tool/commands/common/CHIPCommand.cpp @@ -48,7 +48,7 @@ const chip::Credentials::AttestationTrustStore * CHIPCommand::sTrustStore = null chip::Credentials::GroupDataProviderImpl CHIPCommand::sGroupDataProvider{ kMaxGroupsPerFabric, kMaxGroupKeysPerFabric }; namespace { -const CHIP_ERROR GetAttestationTrustStore(const char * paaTrustStorePath, const char * cdTrustStorePath, +const CHIP_ERROR GetAttestationTrustStore(const char * paaTrustStorePath, const chip::Credentials::AttestationTrustStore ** trustStore) { if (paaTrustStorePath == nullptr) @@ -56,18 +56,13 @@ const CHIP_ERROR GetAttestationTrustStore(const char * paaTrustStorePath, const paaTrustStorePath = getenv(kPAATrustStorePathVariable); } - if (cdTrustStorePath == nullptr) - { - cdTrustStorePath = getenv(kCDTrustStorePathVariable); - } - - if (paaTrustStorePath == nullptr && cdTrustStorePath == nullptr) + if (paaTrustStorePath == nullptr) { *trustStore = chip::Credentials::GetTestAttestationTrustStore(); return CHIP_NO_ERROR; } - static chip::Credentials::FileAttestationTrustStore attestationTrustStore{ paaTrustStorePath, cdTrustStorePath }; + static chip::Credentials::FileAttestationTrustStore attestationTrustStore{ paaTrustStorePath }; if (paaTrustStorePath != nullptr && attestationTrustStore.paaCount() == 0) { @@ -80,17 +75,6 @@ const CHIP_ERROR GetAttestationTrustStore(const char * paaTrustStorePath, const return CHIP_ERROR_INVALID_ARGUMENT; } - if (cdTrustStorePath != nullptr && attestationTrustStore.cdCount() == 0) - { - ChipLogError(chipTool, "No CDs found in path: %s", cdTrustStorePath); - ChipLogError(chipTool, - "Please specify a valid path containing trusted CD certificates using " - "the argument [--cd-trust-store-path cd/file/path] " - "or environment variable [%s=cd/file/path]", - kCDTrustStorePathVariable); - return CHIP_ERROR_INVALID_ARGUMENT; - } - *trustStore = &attestationTrustStore; return CHIP_NO_ERROR; } @@ -139,11 +123,34 @@ CHIP_ERROR CHIPCommand::MaybeSetUpStack() factoryInitParams.listenPort = port; ReturnLogErrorOnFailure(DeviceControllerFactory::GetInstance().Init(factoryInitParams)); - ReturnErrorOnFailure( - GetAttestationTrustStore(mPaaTrustStorePath.ValueOr(nullptr), mCDTrustStorePath.ValueOr(nullptr), &sTrustStore)); + ReturnErrorOnFailure(GetAttestationTrustStore(mPaaTrustStorePath.ValueOr(nullptr), &sTrustStore)); ReturnLogErrorOnFailure(InitializeCommissioner(kIdentityNull, kIdentityNullFabricId)); + // After initializing first commissioner, add the additional CD certs once + { + const char * cdTrustStorePath = mCDTrustStorePath.ValueOr(nullptr); + if (cdTrustStorePath == nullptr) + { + cdTrustStorePath = getenv(kCDTrustStorePathVariable); + } + + auto additionalCdCerts = chip::Credentials::LoadAllX509DerCerts(cdTrustStorePath); + if (cdTrustStorePath != nullptr && additionalCdCerts.size() == 0) + { + ChipLogError(chipTool, "Warning: no CD signing certs found in path: %s, only defaults will be used", cdTrustStorePath); + ChipLogError(chipTool, + "Please specify a path containing trusted CD verifying key certificates using " + "the argument [--cd-trust-store-path cd/file/path] " + "or environment variable [%s=cd/file/path]", + kCDTrustStorePathVariable); + } + ReturnErrorOnFailure(mCredIssuerCmds->AddAdditionalCDVerifyingCerts(additionalCdCerts)); + } + bool allowTestCdSigningKey = !mOnlyAllowTrustedCdKeys.ValueOr(false); + mCredIssuerCmds->SetCredentialIssuerOption(CredentialIssuerCommands::CredentialIssuerOptions::kAllowTestCdSigningKey, + allowTestCdSigningKey); + return CHIP_NO_ERROR; } diff --git a/examples/chip-tool/commands/common/CHIPCommand.h b/examples/chip-tool/commands/common/CHIPCommand.h index 148f79129101fd..35f562ce2ce7e8 100644 --- a/examples/chip-tool/commands/common/CHIPCommand.h +++ b/examples/chip-tool/commands/common/CHIPCommand.h @@ -76,6 +76,9 @@ class CHIPCommand : public Command AddArgument("use-max-sized-certs", 0, 1, &mUseMaxSizedCerts, "Maximize the size of operational certificates. If not provided or 0 (\"false\"), normally sized operational " "certificates are generated."); + AddArgument("only-allow-trusted-cd-keys", 0, 1, &mOnlyAllowTrustedCdKeys, + "Only allow trusted CD verifying keys (disallow test keys). If not provided or 0 (\"false\"), untrusted CD " + "verifying keys are allowed. If 1 (\"true\"), test keys are disallowed."); #if CHIP_CONFIG_TRANSPORT_TRACE_ENABLED AddArgument("trace_file", &mTraceFile); AddArgument("trace_log", 0, 1, &mTraceLog); @@ -161,6 +164,7 @@ class CHIPCommand : public Command chip::Optional mPaaTrustStorePath; chip::Optional mCDTrustStorePath; chip::Optional mUseMaxSizedCerts; + chip::Optional mOnlyAllowTrustedCdKeys; // Cached trust store so commands other than the original startup command // can spin up commissioners as needed. diff --git a/examples/chip-tool/commands/common/CredentialIssuerCommands.h b/examples/chip-tool/commands/common/CredentialIssuerCommands.h index cc04863ee2f8b6..2a5df5a42abe63 100644 --- a/examples/chip-tool/commands/common/CredentialIssuerCommands.h +++ b/examples/chip-tool/commands/common/CredentialIssuerCommands.h @@ -23,6 +23,7 @@ #include #include #include +#include class CredentialIssuerCommands { @@ -54,6 +55,16 @@ class CredentialIssuerCommands virtual CHIP_ERROR SetupDeviceAttestation(chip::Controller::SetupParams & setupParams, const chip::Credentials::AttestationTrustStore * trustStore) = 0; + /** + * @brief Add a list of additional non-default CD verifying keys (by certificate) + * + * Must be called AFTER SetupDeviceAttestation. + * + * @param additionalCdCerts - vector of X.509 DER verifying cert bodies + * @return CHIP_NO_ERROR on succes, another CHIP_ERROR on internal failures. + */ + virtual CHIP_ERROR AddAdditionalCDVerifyingCerts(const std::vector> & additionalCdCerts) = 0; + virtual chip::Controller::OperationalCredentialsDelegate * GetCredentialIssuer() = 0; /** @@ -79,6 +90,7 @@ class CredentialIssuerCommands enum CredentialIssuerOptions : uint8_t { kMaximizeCertificateSizes = 0, // If set, certificate chains will be maximized for testing via padding + kAllowTestCdSigningKey = 1, // If set, allow development/test SDK CD verifying key to be used }; virtual void SetCredentialIssuerOption(CredentialIssuerOptions option, bool isEnabled) diff --git a/examples/chip-tool/commands/example/ExampleCredentialIssuerCommands.h b/examples/chip-tool/commands/example/ExampleCredentialIssuerCommands.h index 40a2871b19437b..da4716e957e921 100644 --- a/examples/chip-tool/commands/example/ExampleCredentialIssuerCommands.h +++ b/examples/chip-tool/commands/example/ExampleCredentialIssuerCommands.h @@ -37,7 +37,9 @@ class ExampleCredentialIssuerCommands : public CredentialIssuerCommands { chip::Credentials::SetDeviceAttestationCredentialsProvider(chip::Credentials::Examples::GetExampleDACProvider()); - setupParams.deviceAttestationVerifier = chip::Credentials::GetDefaultDACVerifier(trustStore); + mDacVerifier = chip::Credentials::GetDefaultDACVerifier(trustStore); + setupParams.deviceAttestationVerifier = mDacVerifier; + mDacVerifier->EnableCdTestKeySupport(mAllowTestCdSigningKey); return CHIP_NO_ERROR; } @@ -49,6 +51,20 @@ class ExampleCredentialIssuerCommands : public CredentialIssuerCommands return mOpCredsIssuer.GenerateNOCChainAfterValidation(nodeId, fabricId, cats, keypair.Pubkey(), rcac, icac, noc); } + CHIP_ERROR AddAdditionalCDVerifyingCerts(const std::vector> & additionalCdCerts) override + { + VerifyOrReturnError(mDacVerifier != nullptr, CHIP_ERROR_INCORRECT_STATE); + + for (const auto & cert : additionalCdCerts) + { + auto cdTrustStore = mDacVerifier->GetCertificationDeclarationTrustStore(); + VerifyOrReturnError(cdTrustStore != nullptr, CHIP_ERROR_INCORRECT_STATE); + ReturnErrorOnFailure(cdTrustStore->AddTrustedKey(chip::ByteSpan(cert.data(), cert.size()))); + } + + return CHIP_NO_ERROR; + } + void SetCredentialIssuerOption(CredentialIssuerOptions option, bool isEnabled) override { switch (option) @@ -57,6 +73,13 @@ class ExampleCredentialIssuerCommands : public CredentialIssuerCommands mUsesMaxSizedCerts = isEnabled; mOpCredsIssuer.SetMaximallyLargeCertsUsed(mUsesMaxSizedCerts); break; + case CredentialIssuerOptions::kAllowTestCdSigningKey: + mAllowTestCdSigningKey = isEnabled; + if (mDacVerifier != nullptr) + { + mDacVerifier->EnableCdTestKeySupport(isEnabled); + } + default: break; } @@ -68,6 +91,8 @@ class ExampleCredentialIssuerCommands : public CredentialIssuerCommands { case CredentialIssuerOptions::kMaximizeCertificateSizes: return mUsesMaxSizedCerts; + case CredentialIssuerOptions::kAllowTestCdSigningKey: + return mAllowTestCdSigningKey; default: return false; } @@ -75,7 +100,10 @@ class ExampleCredentialIssuerCommands : public CredentialIssuerCommands protected: bool mUsesMaxSizedCerts = false; + // Starts true for legacy purposes + bool mAllowTestCdSigningKey = true; private: chip::Controller::ExampleOperationalCredentialsIssuer mOpCredsIssuer; + chip::Credentials::DeviceAttestationVerifier * mDacVerifier; }; diff --git a/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp b/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp index 2966fb4b047420..e8c6c5a8a16e72 100644 --- a/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp +++ b/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -50,34 +51,63 @@ static const ByteSpan kTestPaaRoots[] = { TestCerts::sTestCert_PAA_NoVID_Cert, }; -constexpr uint8_t sTestCert_CD_Cert_Array[] = { - 0x30, 0x82, 0x01, 0xb3, 0x30, 0x82, 0x01, 0x5a, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x08, 0x45, 0xda, 0xf3, 0x9d, 0xe4, 0x7a, - 0xa0, 0x8f, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x2b, 0x31, 0x29, 0x30, 0x27, 0x06, - 0x03, 0x55, 0x04, 0x03, 0x0c, 0x20, 0x4d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x44, 0x20, - 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x20, 0x17, 0x0d, - 0x32, 0x31, 0x30, 0x36, 0x32, 0x38, 0x31, 0x34, 0x32, 0x33, 0x34, 0x33, 0x5a, 0x18, 0x0f, 0x39, 0x39, 0x39, 0x39, 0x31, 0x32, - 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x2b, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, - 0x20, 0x4d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x44, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, - 0x6e, 0x67, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, - 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x3c, 0x39, 0x89, - 0x22, 0x45, 0x2b, 0x55, 0xca, 0xf3, 0x89, 0xc2, 0x5b, 0xd1, 0xbc, 0xa4, 0x65, 0x69, 0x52, 0xcc, 0xb9, 0x0e, 0x88, 0x69, 0x24, - 0x9a, 0xd8, 0x47, 0x46, 0x53, 0x01, 0x4c, 0xbf, 0x95, 0xd6, 0x87, 0x96, 0x5e, 0x03, 0x6b, 0x52, 0x1c, 0x51, 0x03, 0x7e, 0x6b, - 0x8c, 0xed, 0xef, 0xca, 0x1e, 0xb4, 0x40, 0x46, 0x69, 0x4f, 0xa0, 0x88, 0x82, 0xee, 0xd6, 0x51, 0x9d, 0xec, 0xba, 0xa3, 0x66, - 0x30, 0x64, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, - 0x01, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, - 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x62, 0xfa, 0x82, 0x33, 0x59, 0xac, 0xfa, 0xa9, 0x96, 0x3e, 0x1c, 0xfa, 0x14, 0x0a, - 0xdd, 0xf5, 0x04, 0xf3, 0x71, 0x60, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x62, 0xfa, - 0x82, 0x33, 0x59, 0xac, 0xfa, 0xa9, 0x96, 0x3e, 0x1c, 0xfa, 0x14, 0x0a, 0xdd, 0xf5, 0x04, 0xf3, 0x71, 0x60, 0x30, 0x0a, 0x06, - 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, 0x2c, 0x54, 0x5c, 0xe4, 0xe4, - 0x57, 0xd8, 0xa6, 0xf0, 0xd9, 0xd9, 0xbb, 0xeb, 0xd6, 0xec, 0xe1, 0xdd, 0xfe, 0x7f, 0x8c, 0x6d, 0x9a, 0x6c, 0xf3, 0x75, 0x32, - 0x1f, 0xc6, 0xfa, 0xc7, 0x13, 0x84, 0x02, 0x20, 0x54, 0x07, 0x78, 0xe8, 0x74, 0x39, 0x72, 0x52, 0x7e, 0xed, 0xeb, 0xaf, 0x58, - 0x68, 0x62, 0x20, 0xb5, 0x40, 0x78, 0xf2, 0xcd, 0x4e, 0x62, 0xa7, 0x6a, 0xe7, 0xcb, 0xb9, 0x2f, 0xf5, 0x4c, 0x8b +// Test CD Signing Key from `credentials/test/certification-declaration/Chip-Test-CD-Signing-Cert.pem` +// used to verify any in-SDK development CDs. The associated keypair to do actual signing is in +// `credentials/test/certification-declaration/Chip-Test-CD-Signing-Key.pem`. +// +// -----BEGIN CERTIFICATE----- +// MIIBszCCAVqgAwIBAgIIRdrzneR6oI8wCgYIKoZIzj0EAwIwKzEpMCcGA1UEAwwg +// TWF0dGVyIFRlc3QgQ0QgU2lnbmluZyBBdXRob3JpdHkwIBcNMjEwNjI4MTQyMzQz +// WhgPOTk5OTEyMzEyMzU5NTlaMCsxKTAnBgNVBAMMIE1hdHRlciBUZXN0IENEIFNp +// Z25pbmcgQXV0aG9yaXR5MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPDmJIkUr +// VcrzicJb0bykZWlSzLkOiGkkmthHRlMBTL+V1oeWXgNrUhxRA35rjO3vyh60QEZp +// T6CIgu7WUZ3suqNmMGQwEgYDVR0TAQH/BAgwBgEB/wIBATAOBgNVHQ8BAf8EBAMC +// AQYwHQYDVR0OBBYEFGL6gjNZrPqplj4c+hQK3fUE83FgMB8GA1UdIwQYMBaAFGL6 +// gjNZrPqplj4c+hQK3fUE83FgMAoGCCqGSM49BAMCA0cAMEQCICxUXOTkV9im8NnZ +// u+vW7OHd/n+MbZps83UyH8b6xxOEAiBUB3jodDlyUn7t669YaGIgtUB48s1OYqdq +// 58u5L/VMiw== +// -----END CERTIFICATE----- +// +constexpr uint8_t gTestCdPubkeyBytes[65] = { 0x04, 0x3c, 0x39, 0x89, 0x22, 0x45, 0x2b, 0x55, 0xca, 0xf3, 0x89, 0xc2, 0x5b, + 0xd1, 0xbc, 0xa4, 0x65, 0x69, 0x52, 0xcc, 0xb9, 0x0e, 0x88, 0x69, 0x24, 0x9a, + 0xd8, 0x47, 0x46, 0x53, 0x01, 0x4c, 0xbf, 0x95, 0xd6, 0x87, 0x96, 0x5e, 0x03, + 0x6b, 0x52, 0x1c, 0x51, 0x03, 0x7e, 0x6b, 0x8c, 0xed, 0xef, 0xca, 0x1e, 0xb4, + 0x40, 0x46, 0x69, 0x4f, 0xa0, 0x88, 0x82, 0xee, 0xd6, 0x51, 0x9d, 0xec, 0xba }; + +constexpr uint8_t gTestCdPubkeyKid[20] = { 0x62, 0xfa, 0x82, 0x33, 0x59, 0xac, 0xfa, 0xa9, 0x96, 0x3e, + 0x1c, 0xfa, 0x14, 0x0a, 0xdd, 0xf5, 0x04, 0xf3, 0x71, 0x60 }; + +// Official CD "Signing Key 001" +// +// -----BEGIN CERTIFICATE----- +// MIICCDCCAa2gAwIBAgIHY3NhY2RzMTAKBggqhkjOPQQDAjBSMQwwCgYDVQQKDAND +// U0ExLDAqBgNVBAMMI01hdHRlciBDZXJ0aWZpY2F0aW9uIGFuZCBUZXN0aW5nIENB +// MRQwEgYKKwYBBAGConwCAQwEQzVBMDAgFw0yMjA4MTExOTMxMTVaGA8yMDcyMDcy +// OTE5MzExNVowWDEMMAoGA1UECgwDQ1NBMTIwMAYDVQQDDClDZXJ0aWZpY2F0aW9u +// IERlY2xhcmF0aW9uIFNpZ25pbmcgS2V5IDAwMTEUMBIGCisGAQQBgqJ8AgEMBEM1 +// QTAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARbW8Ou1rqjg/3Pm51ac/rqfmXr +// WSfBxcArHPpLi9trm36yUlE/I/IqWDOdyK24gEYKySHTdte5cMUMO+bm0jbwo2Yw +// ZDASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQU +// g/rXgegtAYtPFPChx/aEAYzF0Z8wHwYDVR0jBBgwFoAUl+Rp0MUEFMJvxwH3fpR3 +// OQmN9qUwCgYIKoZIzj0EAwIDSQAwRgIhAIbSu8KoWTj5792UxtJ/uSgQXVTLRRsm +// 09ys2m37JxDvAiEA8WMKDbRbwOtkabIyqwDgmiR3KwkyYwaqN4GPsRKfxwQ= +// -----END CERTIFICATE----- +// +constexpr uint8_t gCdSigningKey001PubkeyBytes[65] = { + 0x04, 0x5b, 0x5b, 0xc3, 0xae, 0xd6, 0xba, 0xa3, 0x83, 0xfd, 0xcf, 0x9b, 0x9d, 0x5a, 0x73, 0xfa, 0xea, + 0x7e, 0x65, 0xeb, 0x59, 0x27, 0xc1, 0xc5, 0xc0, 0x2b, 0x1c, 0xfa, 0x4b, 0x8b, 0xdb, 0x6b, 0x9b, 0x7e, + 0xb2, 0x52, 0x51, 0x3f, 0x23, 0xf2, 0x2a, 0x58, 0x33, 0x9d, 0xc8, 0xad, 0xb8, 0x80, 0x46, 0x0a, 0xc9, + 0x21, 0xd3, 0x76, 0xd7, 0xb9, 0x70, 0xc5, 0x0c, 0x3b, 0xe6, 0xe6, 0xd2, 0x36, 0xf0 }; -static const ByteSpan kTestCDCerts[] = { ByteSpan(sTestCert_CD_Cert_Array) }; +constexpr uint8_t gCdSigningKey001Kid[20] = { 0x83, 0xfa, 0xd7, 0x81, 0xe8, 0x2d, 0x01, 0x8b, 0x4f, 0x14, + 0xf0, 0xa1, 0xc7, 0xf6, 0x84, 0x01, 0x8c, 0xc5, 0xd1, 0x9f }; -const ArrayAttestationTrustStore kTestAttestationTrustStore{ &kTestPaaRoots[0], ArraySize(kTestPaaRoots), &kTestCDCerts[0], - ArraySize(kTestCDCerts) }; +std::array gCdKids = { ByteSpan{ gTestCdPubkeyKid }, ByteSpan{ gCdSigningKey001Kid } }; +std::array gCdPubkeys = { Crypto::P256PublicKey{ gTestCdPubkeyBytes }, + Crypto::P256PublicKey{ gCdSigningKey001PubkeyBytes } }; + +const ArrayAttestationTrustStore kTestAttestationTrustStore{ &kTestPaaRoots[0], ArraySize(kTestPaaRoots) }; AttestationVerificationResult MapError(CertificateChainValidationResult certificateChainValidationResult) { @@ -135,6 +165,9 @@ void DefaultDACVerifier::VerifyAttestationInformation(const DeviceAttestationVer VerifyOrExit(info.attestationElementsBuffer.size() <= kMaxResponseLength, attestationError = AttestationVerificationResult::kInvalidArgument); + // Ensure PAI is present + VerifyOrExit(!info.paiDerBuffer.empty(), attestationError = AttestationVerificationResult::kPaiMissing); + // match DAC and PAI VIDs { VerifyOrExit(ExtractVIDPIDFromX509Cert(info.dacDerBuffer, dacVidPid) == CHIP_NO_ERROR, @@ -260,23 +293,21 @@ void DefaultDACVerifier::VerifyAttestationInformation(const DeviceAttestationVer AttestationVerificationResult DefaultDACVerifier::ValidateCertificationDeclarationSignature(const ByteSpan & cmsEnvelopeBuffer, ByteSpan & certDeclBuffer) { - ByteSpan skid; - - VerifyOrReturnError(CMS_ExtractKeyId(cmsEnvelopeBuffer, skid) == CHIP_NO_ERROR, + ByteSpan kid; + VerifyOrReturnError(CMS_ExtractKeyId(cmsEnvelopeBuffer, kid) == CHIP_NO_ERROR, AttestationVerificationResult::kCertificationDeclarationNoKeyId); - Crypto::P256PublicKey pubKey; - auto err = mAttestationTrustStore->GetCertificationDeclarationSigningKey(skid, pubKey); - VerifyOrReturnError(err == CHIP_NO_ERROR || err == CHIP_ERROR_NOT_IMPLEMENTED, - AttestationVerificationResult::kCertificationDeclarationNoCertificateFound); + Crypto::P256PublicKey verifyingKey; + CHIP_ERROR err = mCdKeysTrustStore.LookupVerifyingKey(kid, verifyingKey); + VerifyOrReturnError(err == CHIP_NO_ERROR, AttestationVerificationResult::kCertificationDeclarationNoCertificateFound); - if (err == CHIP_ERROR_NOT_IMPLEMENTED) + // Disallow test key if support not enabled + if (mCdKeysTrustStore.IsCdTestKey(kid) && !IsCdTestKeySupported()) { - VerifyOrReturnError(kTestAttestationTrustStore.GetCertificationDeclarationSigningKey(skid, pubKey) == CHIP_NO_ERROR, - AttestationVerificationResult::kCertificationDeclarationNoCertificateFound); + return AttestationVerificationResult::kCertificationDeclarationNoCertificateFound; } - VerifyOrReturnError(CMS_Verify(cmsEnvelopeBuffer, pubKey, certDeclBuffer) == CHIP_NO_ERROR, + VerifyOrReturnError(CMS_Verify(cmsEnvelopeBuffer, verifyingKey, certDeclBuffer) == CHIP_NO_ERROR, AttestationVerificationResult::kCertificationDeclarationInvalidSignature); return AttestationVerificationResult::kSuccess; @@ -293,7 +324,7 @@ AttestationVerificationResult DefaultDACVerifier::ValidateCertificateDeclaration if (!firmwareInfo.empty()) { - // TODO: check if version_number field in Certification Declaration matches the one in Firmware Information. + // TODO: validate contents based on DCL } // The vendor_id field in the Certification Declaration SHALL match the VendorID attribute found in the Basic Information @@ -396,6 +427,68 @@ CHIP_ERROR DefaultDACVerifier::VerifyNodeOperationalCSRInformation(const ByteSpa return CHIP_NO_ERROR; } +bool CsaCdKeysTrustStore::IsCdTestKey(const ByteSpan & kid) const +{ + return kid.data_equal(ByteSpan{ gTestCdPubkeyKid }); +} + +CHIP_ERROR CsaCdKeysTrustStore::AddTrustedKey(const ByteSpan & kid, const Crypto::P256PublicKey & pubKey) +{ + ReturnErrorCodeIf(kid.size() > SingleKeyEntry::kMaxKidSize, CHIP_ERROR_INVALID_ARGUMENT); + ReturnErrorCodeIf(kid.empty(), CHIP_ERROR_INVALID_ARGUMENT); + ReturnErrorCodeIf(mNumTrustedKeys == kMaxNumTrustedKeys, CHIP_ERROR_NO_MEMORY); + + auto & entry = mTrustedKeys[mNumTrustedKeys]; + + entry.kidSize = kid.size(); + memcpy(&entry.kidBuffer[0], kid.data(), kid.size()); + entry.publicKey = pubKey; + + ++mNumTrustedKeys; + return CHIP_NO_ERROR; +} + +CHIP_ERROR CsaCdKeysTrustStore::AddTrustedKey(const ByteSpan & derCertBytes) +{ + // TODO: Verify cert against CD root of trust (i.e. verify signatures). To do so, + // we are missing primitives to validate X.509 paths of total length 2. + + uint8_t kidBuf[Crypto::kSubjectKeyIdentifierLength] = { 0 }; + MutableByteSpan kidSpan{ kidBuf }; + P256PublicKey pubKey; + + VerifyOrReturnError(CHIP_NO_ERROR == Crypto::ExtractSKIDFromX509Cert(derCertBytes, kidSpan), CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(CHIP_NO_ERROR == Crypto::ExtractPubkeyFromX509Cert(derCertBytes, pubKey), CHIP_ERROR_INVALID_ARGUMENT); + return AddTrustedKey(kidSpan, pubKey); +} + +CHIP_ERROR CsaCdKeysTrustStore::LookupVerifyingKey(const ByteSpan & kid, Crypto::P256PublicKey & outPubKey) const +{ + // First, search for the well known keys + for (size_t keyIdx = 0; keyIdx < gCdKids.size(); keyIdx++) + { + if (kid.data_equal(gCdKids[keyIdx])) + { + outPubKey = gCdPubkeys[keyIdx]; + return CHIP_NO_ERROR; + } + } + + // Seconds, search externally added keys + for (size_t keyIdx = 0; keyIdx < mNumTrustedKeys; keyIdx++) + { + auto & entry = mTrustedKeys[mNumTrustedKeys]; + if (kid.data_equal(entry.GetKid())) + { + outPubKey = entry.publicKey; + return CHIP_NO_ERROR; + } + } + + // If we get here, the desired key was not found + return CHIP_ERROR_KEY_NOT_FOUND; +} + const AttestationTrustStore * GetTestAttestationTrustStore() { return &kTestAttestationTrustStore; diff --git a/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h b/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h index 99f2fb5f2b85ae..bdf7f705dbd0f1 100644 --- a/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h +++ b/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h @@ -16,11 +16,44 @@ */ #pragma once +#include #include +#include +#include +#include +#include +#include namespace chip { namespace Credentials { +class CsaCdKeysTrustStore : public WellKnownKeysTrustStore +{ +public: + CsaCdKeysTrustStore() = default; + virtual ~CsaCdKeysTrustStore() = default; + + CHIP_ERROR AddTrustedKey(const ByteSpan & kid, const Crypto::P256PublicKey & pubKey) override; + CHIP_ERROR AddTrustedKey(const ByteSpan & derCertBytes) override; + CHIP_ERROR LookupVerifyingKey(const ByteSpan & kid, Crypto::P256PublicKey & outPubKey) const override; + bool IsCdTestKey(const ByteSpan & kid) const override; + +protected: + struct SingleKeyEntry + { + static constexpr size_t kMaxKidSize = 32u; + uint8_t kidBuffer[kMaxKidSize]; + size_t kidSize; + Crypto::P256PublicKey publicKey; + + ByteSpan GetKid() const { return ByteSpan{ &kidBuffer[0], kidSize }; } + }; + + static constexpr size_t kMaxNumTrustedKeys = CHIP_CONFIG_NUM_CD_KEY_SLOTS; + std::array mTrustedKeys; + size_t mNumTrustedKeys = 0; +}; + class DefaultDACVerifier : public DeviceAttestationVerifier { public: @@ -41,9 +74,12 @@ class DefaultDACVerifier : public DeviceAttestationVerifier const ByteSpan & attestationSignatureBuffer, const Crypto::P256PublicKey & dacPublicKey, const ByteSpan & csrNonce) override; + CsaCdKeysTrustStore * GetCertificationDeclarationTrustStore() override { return &mCdKeysTrustStore; } + protected: DefaultDACVerifier() {} + CsaCdKeysTrustStore mCdKeysTrustStore; const AttestationTrustStore * mAttestationTrustStore; }; diff --git a/src/credentials/attestation_verifier/DeviceAttestationVerifier.h b/src/credentials/attestation_verifier/DeviceAttestationVerifier.h index 5116c64e5434c6..6f12afed6189b7 100644 --- a/src/credentials/attestation_verifier/DeviceAttestationVerifier.h +++ b/src/credentials/attestation_verifier/DeviceAttestationVerifier.h @@ -21,6 +21,7 @@ #include #include #include +#include namespace chip { namespace Credentials { @@ -44,6 +45,7 @@ enum class AttestationVerificationResult : uint16_t kPaiArgumentInvalid = 204, kPaiVendorIdMismatch = 205, kPaiAuthorityNotFound = 206, + kPaiMissing = 207, kDacExpired = 300, kDacSignatureInvalid = 301, @@ -146,28 +148,73 @@ class AttestationTrustStore * */ virtual CHIP_ERROR GetProductAttestationAuthorityCert(const ByteSpan & skid, MutableByteSpan & outPaaDerBuffer) const = 0; +}; + +/** + * @brief Helper utility to model obtaining verifying keys by Key ID + * + * API is synchronous. Real commissioner implementations may entirely + * hide key lookup behind the DeviceAttestationVerifier and never use this interface at all. + * It is provided as a utility to help build DeviceAttestationVerifier + * implementations suitable for testing or examples. + */ +class WellKnownKeysTrustStore +{ +public: + WellKnownKeysTrustStore() = default; + virtual ~WellKnownKeysTrustStore() = default; + + // Not copyable + WellKnownKeysTrustStore(const WellKnownKeysTrustStore &) = delete; + WellKnownKeysTrustStore & operator=(const WellKnownKeysTrustStore &) = delete; /** - * @brief Look-up a CD signing key by SKID + * @brief Add a trusted key directly * - * The implementations of this interface must have access to a set of CDs to trust. + * @param[in] kid - Key ID to use. Usually 20 bytes long, max 32 bytes. + * @param[in] pubKey - Verifying public key to attach to the key ID. * - * Interface is synchronous, and therefore this should not be used unless to expose a CD - * store that is both fully local and quick to access. + * @return CHIP_NO_ERROR on success, CHIP_INVALID_ARGUMENT if `kid` or `pubKey` arguments + * are not usable. CHIP_ERROR_NO_MEMORY if the trust store is full. + */ + virtual CHIP_ERROR AddTrustedKey(const ByteSpan & kid, const Crypto::P256PublicKey & pubKey) = 0; + + /** + * @brief Add a trusted key via a public certificate. + * + * The subject public key of the certificate will be used. + * The subject key ID extensions of the certificate will be the `kid`. * - * @param[in] skid Key identifier (SKID) of the CD key to look-up (SHA-1 of public key) - * @param[in,out] pubKey Reference to public key object that gets updated on success. + * Verification of trust chaining is at the discretion of the implementation. * - * @returns CHIP_NO_ERROR on success, CHIP_INVALID_ARGUMENT if `skid` is not usable - * CHIP_ERROR_CA_CERT_NOT_FOUND if no CD signing key found that matches `skid. - * By default CHIP_ERROR_NOT_IMPLEMENTED is returned if the method is not overriden. Depending on the - * implementation it may results into using the development CD signing key. + * @param[in] derCertBytes - Certificate containing the X.509 DER certificate with the key. * + * @return CHIP_NO_ERROR on success, CHIP_INVALID_ARGUMENT if derCertBytes is improperly + * formatted or not trusted. CHIP_ERROR_NO_MEMORY if the trust store is full. */ - virtual CHIP_ERROR GetCertificationDeclarationSigningKey(const ByteSpan & skid, Crypto::P256PublicKey & pubKey) const - { - return CHIP_ERROR_NOT_IMPLEMENTED; - } + virtual CHIP_ERROR AddTrustedKey(const ByteSpan & derCertBytes) = 0; + + /** + * @brief Look-up a verifying key by Key ID + * + * Interface is synchronous. + * + * @param[in] kid Buffer containing the key identifier (KID) of the verifying key to look-up. Usually + * a SHA-1-sized buffer (20 bytes). + * @param[out] outPubKey Reference to where the verifying key found will be stored on CHIP_NO_ERROR + * + * @returns CHIP_NO_ERROR on success, CHIP_INVALID_ARGUMENT if `kid` or `pubKey` arguments + * are not usable, CHIP_ERROR_KEY_NOT_FOUND if no key is found that matches `kid`. + */ + virtual CHIP_ERROR LookupVerifyingKey(const ByteSpan & kid, Crypto::P256PublicKey & outPubKey) const = 0; + + /** + * @brief Returns true if `kid` identifies a known test key. + * + * @param kid - Key ID to use. Usually 20 bytes long, max 32 bytes. + * @return true if it's a test/development-only signing key identifier, false otherwise + */ + virtual bool IsCdTestKey(const ByteSpan & kid) const = 0; }; /** @@ -180,9 +227,7 @@ class AttestationTrustStore class ArrayAttestationTrustStore : public AttestationTrustStore { public: - ArrayAttestationTrustStore(const ByteSpan * derCerts, size_t numCerts, const ByteSpan * cdDerCerts, size_t numCDCerts) : - mDerCerts(derCerts), mNumCerts(numCerts), mCDDerCerts(cdDerCerts), mNumCDCerts(numCDCerts) - {} + ArrayAttestationTrustStore(const ByteSpan * derCerts, size_t numCerts) : mDerCerts(derCerts), mNumCerts(numCerts) {} CHIP_ERROR GetProductAttestationAuthorityCert(const ByteSpan & skid, MutableByteSpan & outPaaDerBuffer) const override { @@ -210,37 +255,9 @@ class ArrayAttestationTrustStore : public AttestationTrustStore return CHIP_ERROR_CA_CERT_NOT_FOUND; } - CHIP_ERROR GetCertificationDeclarationSigningKey(const ByteSpan & skid, Crypto::P256PublicKey & pubKey) const override - { - VerifyOrReturnError(!skid.empty() && (skid.data() != nullptr), CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrReturnError(skid.size() == Crypto::kSubjectKeyIdentifierLength, CHIP_ERROR_INVALID_ARGUMENT); - - size_t cdIdx; - ByteSpan candidate; - - for (cdIdx = 0; cdIdx < mNumCDCerts; ++cdIdx) - { - uint8_t skidBuf[Crypto::kSubjectKeyIdentifierLength] = { 0 }; - candidate = mCDDerCerts[cdIdx]; - MutableByteSpan candidateSkidSpan{ skidBuf }; - VerifyOrReturnError(CHIP_NO_ERROR == Crypto::ExtractSKIDFromX509Cert(candidate, candidateSkidSpan), - CHIP_ERROR_INTERNAL); - - if (skid.data_equal(candidateSkidSpan)) - { - // Found a match - return Crypto::ExtractPubkeyFromX509Cert(candidate, pubKey); - } - } - - return CHIP_ERROR_CA_CERT_NOT_FOUND; - } - protected: const ByteSpan * mDerCerts; const size_t mNumCerts; - const ByteSpan * mCDDerCerts; - const size_t mNumCDCerts; }; class DeviceAttestationVerifier @@ -328,9 +345,26 @@ class DeviceAttestationVerifier const Crypto::P256PublicKey & dacPublicKey, const ByteSpan & csrNonce) = 0; + /** + * @brief Get the trust store used for the attestation verifier. + * + * Returns nullptr if not supported. Be careful not to hold-on to the trust store + * for too long. It is only expected to have same lifetime as the DeviceAttestationVerifier. + * + * @return a pointer to the trust store or nullptr if none is directly accessible. + */ + virtual WellKnownKeysTrustStore * GetCertificationDeclarationTrustStore() { return nullptr; } + + void EnableCdTestKeySupport(bool enabled) { mEnableCdTestKeySupport = enabled; } + bool IsCdTestKeySupported() const { return mEnableCdTestKeySupport; } + protected: CHIP_ERROR ValidateAttestationSignature(const Crypto::P256PublicKey & pubkey, const ByteSpan & attestationElements, const ByteSpan & attestationChallenge, const Crypto::P256ECDSASignature & signature); + + // Default to support the "development" test key for legacy purposes (since the DefaultDACVerifier) + // always supported development keys. + bool mEnableCdTestKeySupport = true; }; /** diff --git a/src/credentials/attestation_verifier/FileAttestationTrustStore.cpp b/src/credentials/attestation_verifier/FileAttestationTrustStore.cpp index 67c210260f082d..67c729014e9536 100644 --- a/src/credentials/attestation_verifier/FileAttestationTrustStore.cpp +++ b/src/credentials/attestation_verifier/FileAttestationTrustStore.cpp @@ -16,6 +16,7 @@ */ #include "FileAttestationTrustStore.h" +#include #include #include #include @@ -39,28 +40,27 @@ const char * GetFilenameExtension(const char * filename) } } // namespace -FileAttestationTrustStore::FileAttestationTrustStore(const char * paaTrustStorePath, const char * cdTrustStorePath) +FileAttestationTrustStore::FileAttestationTrustStore(const char * paaTrustStorePath) { - VerifyOrReturn(paaTrustStorePath != nullptr || cdTrustStorePath != nullptr); + VerifyOrReturn(paaTrustStorePath != nullptr); if (paaTrustStorePath != nullptr) { - LoadTrustStore(paaTrustStorePath, mPAADerCerts); + mPAADerCerts = LoadAllX509DerCerts(paaTrustStorePath); VerifyOrReturn(paaCount()); } - if (cdTrustStorePath != nullptr) - { - LoadTrustStore(cdTrustStorePath, mCDDerCerts); - VerifyOrReturn(cdCount()); - } - mIsInitialized = true; } -void FileAttestationTrustStore::LoadTrustStore(const char * trustStorePath, - std::vector> & certs) +std::vector> LoadAllX509DerCerts(const char * trustStorePath) { + std::vector> certs; + if (trustStorePath == nullptr) + { + return certs; + } + DIR * dir; dir = opendir(trustStorePath); @@ -73,32 +73,41 @@ void FileAttestationTrustStore::LoadTrustStore(const char * trustStorePath, const char * fileExtension = GetFilenameExtension(entry->d_name); if (strncmp(fileExtension, "der", strlen("der")) == 0) { - FILE * file; - - std::array certificate; + std::vector certificate(kMaxDERCertLength + 1); std::string filename(trustStorePath); filename += std::string("/") + std::string(entry->d_name); - file = fopen(filename.c_str(), "rb"); - if (file != nullptr) + FILE * file = fopen(filename.c_str(), "rb"); + if (file == nullptr) { - uint32_t certificateLength = fread(certificate.data(), sizeof(uint8_t), kMaxDERCertLength, file); - if (certificateLength > 0) - { - certs.push_back(certificate); - } - fclose(file); + // On bad files, just skip. + continue; } - else + + size_t certificateLength = fread(certificate.data(), sizeof(uint8_t), certificate.size(), file); + if ((certificateLength > 0) && (certificateLength <= kMaxDERCertLength)) { - certs.clear(); - break; + certificate.resize(certificateLength); + // Only accumulate certificate if it has a subject key ID extension + { + uint8_t kidBuf[Crypto::kSubjectKeyIdentifierLength] = { 0 }; + MutableByteSpan kidSpan{ kidBuf }; + ByteSpan certSpan{ certificate.data(), certificate.size() }; + + if (CHIP_NO_ERROR == Crypto::ExtractSKIDFromX509Cert(certSpan, kidSpan)) + { + certs.push_back(certificate); + } + } } + fclose(file); } } closedir(dir); } + + return certs; } FileAttestationTrustStore::~FileAttestationTrustStore() @@ -109,7 +118,6 @@ FileAttestationTrustStore::~FileAttestationTrustStore() void FileAttestationTrustStore::Cleanup() { mPAADerCerts.clear(); - mCDDerCerts.clear(); mIsInitialized = false; } @@ -146,37 +154,5 @@ CHIP_ERROR FileAttestationTrustStore::GetProductAttestationAuthorityCert(const B return CHIP_ERROR_CA_CERT_NOT_FOUND; } -CHIP_ERROR FileAttestationTrustStore::GetCertificationDeclarationSigningKey(const ByteSpan & skid, - Crypto::P256PublicKey & pubKey) const -{ - // If the constructor has not tried to initialize the certification declaration certificates database, return - // CHIP_ERROR_NOT_IMPLEMENTED to use the testing trust store if the DefaultAttestationVerifier is in use. - if (mIsInitialized && cdCount() == 0) - { - return CHIP_ERROR_NOT_IMPLEMENTED; - } - - VerifyOrReturnError(!mCDDerCerts.empty(), CHIP_ERROR_CA_CERT_NOT_FOUND); - VerifyOrReturnError(!skid.empty() && (skid.data() != nullptr), CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrReturnError(skid.size() == Crypto::kSubjectKeyIdentifierLength, CHIP_ERROR_INVALID_ARGUMENT); - - for (auto candidate : mCDDerCerts) - { - uint8_t skidBuf[Crypto::kSubjectKeyIdentifierLength] = { 0 }; - MutableByteSpan candidateSkidSpan{ skidBuf }; - VerifyOrReturnError(CHIP_NO_ERROR == - Crypto::ExtractSKIDFromX509Cert(ByteSpan{ candidate.data(), candidate.size() }, candidateSkidSpan), - CHIP_ERROR_INTERNAL); - - if (skid.data_equal(candidateSkidSpan)) - { - // Found a match - return Crypto::ExtractPubkeyFromX509Cert(ByteSpan{ candidate.data(), candidate.size() }, pubKey); - } - } - - return CHIP_ERROR_CA_CERT_NOT_FOUND; -} - } // namespace Credentials } // namespace chip diff --git a/src/credentials/attestation_verifier/FileAttestationTrustStore.h b/src/credentials/attestation_verifier/FileAttestationTrustStore.h index c39619177e8131..0446d7a6ebb6b8 100644 --- a/src/credentials/attestation_verifier/FileAttestationTrustStore.h +++ b/src/credentials/attestation_verifier/FileAttestationTrustStore.h @@ -25,26 +25,33 @@ namespace chip { namespace Credentials { +/** + * @brief Load all X.509 DER certificates in a given path. + * + * Silently ignores non-X.509 files and X.509 files without a subject key identifier. + * + * Returns an empty vector if no files are found or unrecoverable errors arise. + * + * @param trustStorePath - path from where to search for certificates. + * @return a vector of certificate DER data + */ +std::vector> LoadAllX509DerCerts(const char * trustStorePath); + class FileAttestationTrustStore : public AttestationTrustStore { public: - FileAttestationTrustStore(const char * paaTrustStorePath = nullptr, const char * cdTrustStorePath = nullptr); + FileAttestationTrustStore(const char * paaTrustStorePath = nullptr); ~FileAttestationTrustStore(); CHIP_ERROR GetProductAttestationAuthorityCert(const ByteSpan & skid, MutableByteSpan & outPaaDerBuffer) const override; - CHIP_ERROR GetCertificationDeclarationSigningKey(const ByteSpan & skid, Crypto::P256PublicKey & pubKey) const override; bool IsInitialized() const { return mIsInitialized; } size_t paaCount() const { return mPAADerCerts.size(); }; - size_t cdCount() const { return mCDDerCerts.size(); }; protected: - std::vector> mPAADerCerts; - std::vector> mCDDerCerts; + std::vector> mPAADerCerts; private: - void LoadTrustStore(const char * trustStorePath, std::vector> & certs); - bool mIsInitialized = false; void Cleanup(); diff --git a/src/lib/core/CHIPConfig.h b/src/lib/core/CHIPConfig.h index 95fea208f584b2..60865613b45efe 100644 --- a/src/lib/core/CHIPConfig.h +++ b/src/lib/core/CHIPConfig.h @@ -1282,6 +1282,15 @@ extern const char CHIP_NON_PRODUCTION_MARKER[]; #define CHIP_CONFIG_SETUP_CODE_PAIRER_DISCOVERY_TIMEOUT_SECS 30 #endif // CHIP_CONFIG_SETUP_CODE_PAIRER_DISCOVERY_TIMEOUT_SECS +/** + * @def CHIP_CONFIG_NUM_CD_KEY_SLOTS + * + * @brief Number of custom CD signing keys supported by default CD keystore + * + */ +#ifndef CHIP_CONFIG_NUM_CD_KEY_SLOTS +#define CHIP_CONFIG_NUM_CD_KEY_SLOTS 5 +#endif // CHIP_CONFIG_NUM_CD_KEY_SLOTS /** * @} */ From a9e5662fba19e8894ac6a9718416671fb4c0b01a Mon Sep 17 00:00:00 2001 From: Vivien Nicolas Date: Tue, 6 Sep 2022 19:35:54 +0200 Subject: [PATCH 7/8] Update src/darwin to take into account the proposed changes --- .../DefaultDeviceAttestationVerifier.cpp | 2 +- .../CHIP/MTRAttestationTrustStoreBridge.h | 9 ++---- .../CHIP/MTRAttestationTrustStoreBridge.mm | 32 ------------------- .../Framework/CHIP/MTRControllerFactory.mm | 21 ++++++++++-- 4 files changed, 22 insertions(+), 42 deletions(-) diff --git a/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp b/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp index e8c6c5a8a16e72..81bbec5e675999 100644 --- a/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp +++ b/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp @@ -477,7 +477,7 @@ CHIP_ERROR CsaCdKeysTrustStore::LookupVerifyingKey(const ByteSpan & kid, Crypto: // Seconds, search externally added keys for (size_t keyIdx = 0; keyIdx < mNumTrustedKeys; keyIdx++) { - auto & entry = mTrustedKeys[mNumTrustedKeys]; + auto & entry = mTrustedKeys[keyIdx]; if (kid.data_equal(entry.GetKid())) { outPubKey = entry.publicKey; diff --git a/src/darwin/Framework/CHIP/MTRAttestationTrustStoreBridge.h b/src/darwin/Framework/CHIP/MTRAttestationTrustStoreBridge.h index c12b0cc149ff2f..ced955cb1c8b1d 100644 --- a/src/darwin/Framework/CHIP/MTRAttestationTrustStoreBridge.h +++ b/src/darwin/Framework/CHIP/MTRAttestationTrustStoreBridge.h @@ -22,9 +22,8 @@ NS_ASSUME_NONNULL_BEGIN class MTRAttestationTrustStoreBridge : public chip::Credentials::AttestationTrustStore { public: - MTRAttestationTrustStoreBridge(NSArray * _Nullable paaCerts, NSArray * _Nullable cdCerts) + MTRAttestationTrustStoreBridge(NSArray * paaCerts) : mPaaCerts(paaCerts) - , mCDCerts(cdCerts) { } ~MTRAttestationTrustStoreBridge() {}; @@ -32,12 +31,8 @@ class MTRAttestationTrustStoreBridge : public chip::Credentials::AttestationTrus CHIP_ERROR GetProductAttestationAuthorityCert( const chip::ByteSpan & skid, chip::MutableByteSpan & outPaaDerBuffer) const override; - CHIP_ERROR GetCertificationDeclarationSigningKey( - const chip::ByteSpan & skid, chip::Crypto::P256PublicKey & pubKey) const override; - private: - NSArray * _Nullable mPaaCerts; - NSArray * _Nullable mCDCerts; + NSArray * mPaaCerts; }; NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRAttestationTrustStoreBridge.mm b/src/darwin/Framework/CHIP/MTRAttestationTrustStoreBridge.mm index 5dae1736de2ba8..f968ca4d617de9 100644 --- a/src/darwin/Framework/CHIP/MTRAttestationTrustStoreBridge.mm +++ b/src/darwin/Framework/CHIP/MTRAttestationTrustStoreBridge.mm @@ -21,10 +21,6 @@ CHIP_ERROR MTRAttestationTrustStoreBridge::GetProductAttestationAuthorityCert( const chip::ByteSpan & skid, chip::MutableByteSpan & outPaaDerBuffer) const { - if (mPaaCerts == nil) { - return CHIP_ERROR_NOT_IMPLEMENTED; - } - VerifyOrReturnError(skid.size() == chip::Crypto::kSubjectKeyIdentifierLength, CHIP_ERROR_INVALID_ARGUMENT); size_t paaIdx; @@ -44,31 +40,3 @@ } return CHIP_ERROR_CA_CERT_NOT_FOUND; } - -CHIP_ERROR MTRAttestationTrustStoreBridge::GetCertificationDeclarationSigningKey( - const chip::ByteSpan & skid, chip::Crypto::P256PublicKey & pubKey) const -{ - if (mCDCerts == nil) { - return CHIP_ERROR_NOT_IMPLEMENTED; - } - - VerifyOrReturnError(skid.size() == chip::Crypto::kSubjectKeyIdentifierLength, CHIP_ERROR_INVALID_ARGUMENT); - - size_t cdIdx; - chip::ByteSpan candidate; - - for (cdIdx = 0; cdIdx < mCDCerts.count; ++cdIdx) { - uint8_t skidBuf[chip::Crypto::kSubjectKeyIdentifierLength] = { 0 }; - candidate = AsByteSpan(mCDCerts[cdIdx]); - chip::MutableByteSpan candidateSkidSpan { skidBuf }; - VerifyOrReturnError( - CHIP_NO_ERROR == chip::Crypto::ExtractSKIDFromX509Cert(candidate, candidateSkidSpan), CHIP_ERROR_INTERNAL); - - if (skid.data_equal(candidateSkidSpan)) { - // Found a match - return chip::Crypto::ExtractPubkeyFromX509Cert(candidate, pubKey); - } - } - - return CHIP_ERROR_CA_CERT_NOT_FOUND; -} diff --git a/src/darwin/Framework/CHIP/MTRControllerFactory.mm b/src/darwin/Framework/CHIP/MTRControllerFactory.mm index 35084164f50878..f769548a2d7f24 100644 --- a/src/darwin/Framework/CHIP/MTRControllerFactory.mm +++ b/src/darwin/Framework/CHIP/MTRControllerFactory.mm @@ -54,6 +54,7 @@ static NSString * const kErrorControllerFactoryInit = @"Init failure while initializing controller factory"; static NSString * const kErrorKeystoreInit = @"Init failure while initializing persistent storage keystore"; static NSString * const kErrorCertStoreInit = @"Init failure while initializing persistent storage operational certificate store"; +static NSString * const kErrorCDCertStoreInit = @"Init failure while initializing Certificate Declaration Signing Keys store"; static NSString * const kErrorOtaProviderInit = @"Init failure while creating an OTA provider delegate"; @interface MTRControllerFactory () @@ -257,8 +258,8 @@ - (BOOL)startup:(MTRControllerFactoryParams *)startupParams // Initialize device attestation verifier const Credentials::AttestationTrustStore * trustStore; - if (startupParams.paaCerts || startupParams.cdCerts) { - _attestationTrustStoreBridge = new MTRAttestationTrustStoreBridge(startupParams.paaCerts, startupParams.cdCerts); + if (startupParams.paaCerts) { + _attestationTrustStoreBridge = new MTRAttestationTrustStoreBridge(startupParams.paaCerts); if (_attestationTrustStoreBridge == nullptr) { MTR_LOG_ERROR("Error: %@", kErrorAttestationTrustStoreInit); return; @@ -274,6 +275,22 @@ - (BOOL)startup:(MTRControllerFactoryParams *)startupParams return; } + if (startupParams.cdCerts) { + auto cdTrustStore = _deviceAttestationVerifier->GetCertificationDeclarationTrustStore(); + if (cdTrustStore == nullptr) { + MTR_LOG_ERROR("Error: %@", kErrorCDCertStoreInit); + return; + } + + for (NSData * cdSigningCert in startupParams.cdCerts) { + errorCode = cdTrustStore->AddTrustedKey(AsByteSpan(cdSigningCert)); + if (errorCode != CHIP_NO_ERROR) { + MTR_LOG_ERROR("Error: %@", kErrorCDCertStoreInit); + return; + } + } + } + chip::Controller::FactoryInitParams params; if (startupParams.port != nil) { params.listenPort = [startupParams.port unsignedShortValue]; From 51d87e360d5989e6dcf77eb762d8c9210bc193f6 Mon Sep 17 00:00:00 2001 From: Tennessee Carmel-Veilleux Date: Tue, 6 Sep 2022 15:28:15 -0400 Subject: [PATCH 8/8] Add unit test for `CsaCdKeysTrustStore` --- .../tests/TestCertificationDeclaration.cpp | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/src/credentials/tests/TestCertificationDeclaration.cpp b/src/credentials/tests/TestCertificationDeclaration.cpp index cda5f90b05124e..0a1d3aa33c9a3f 100644 --- a/src/credentials/tests/TestCertificationDeclaration.cpp +++ b/src/credentials/tests/TestCertificationDeclaration.cpp @@ -27,7 +27,10 @@ #include #include +#include +#include #include +#include #include #include @@ -150,6 +153,45 @@ static constexpr uint8_t sTestCMS_SignedMessage02[] = { 0x36, 0x5c, 0x6e, 0xd5, 0x44, 0x08, 0x6d, 0x10, 0x1a, 0xfd, 0xaf, 0x07, 0x9a, 0x2c, 0x23, 0xe0, 0xde }; +// Generated an untrusted CD key, and then a CD +// +// openssl ecparam -name prime256v1 -genkey -noout -out UntrustedCDKey.pem +// openssl req -x509 -key UntrustedCDKey.pem -keyform PEM -out UntrustedCDCert.pem -sha256 -days 3650 +// out/host/chip-cert gen-cd -C UntrustedCDCert.pem -K UntrustedCDKey.pem --out UntrustedCD.bin -f 1 -V FFF1 -p 8000 -p 8001 -p 8002 +// -p 8003 -p 8004 -p 8005 -p 8006 -p 8007 -p 8008 -p 8009 -p 800a -p 800b -p 800c -p 800d -p 800e -p 800f -p 8010 -p 8011 -p 8012 +// -p 8013 -p 8014 -p 8015 -p 8016 -p 8017 -p 8018 -p 8019 -p 801a -p 801b -p 801c -p 801d -p 801e -p 801f -d 0016 -c +// "ZIG0000000000000000" -l 0 -i 0 -n 0001 -t 0 +// +static constexpr uint8_t gUntrustedCd[333] = { + 0x30, 0x82, 0x01, 0x49, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x01, 0x3a, 0x30, 0x82, + 0x01, 0x36, 0x02, 0x01, 0x03, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x30, + 0x81, 0xa2, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x81, 0x94, 0x04, 0x81, 0x91, 0x15, 0x24, + 0x00, 0x01, 0x25, 0x01, 0xf1, 0xff, 0x36, 0x02, 0x05, 0x00, 0x80, 0x05, 0x01, 0x80, 0x05, 0x02, 0x80, 0x05, 0x03, 0x80, 0x05, + 0x04, 0x80, 0x05, 0x05, 0x80, 0x05, 0x06, 0x80, 0x05, 0x07, 0x80, 0x05, 0x08, 0x80, 0x05, 0x09, 0x80, 0x05, 0x0a, 0x80, 0x05, + 0x0b, 0x80, 0x05, 0x0c, 0x80, 0x05, 0x0d, 0x80, 0x05, 0x0e, 0x80, 0x05, 0x0f, 0x80, 0x05, 0x10, 0x80, 0x05, 0x11, 0x80, 0x05, + 0x12, 0x80, 0x05, 0x13, 0x80, 0x05, 0x14, 0x80, 0x05, 0x15, 0x80, 0x05, 0x16, 0x80, 0x05, 0x17, 0x80, 0x05, 0x18, 0x80, 0x05, + 0x19, 0x80, 0x05, 0x1a, 0x80, 0x05, 0x1b, 0x80, 0x05, 0x1c, 0x80, 0x05, 0x1d, 0x80, 0x05, 0x1e, 0x80, 0x05, 0x1f, 0x80, 0x18, + 0x24, 0x03, 0x16, 0x2c, 0x04, 0x13, 0x5a, 0x49, 0x47, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x24, 0x05, 0x00, 0x24, 0x06, 0x00, 0x24, 0x07, 0x01, 0x24, 0x08, 0x00, 0x18, 0x31, 0x7d, 0x30, 0x7b, + 0x02, 0x01, 0x03, 0x80, 0x14, 0x75, 0xe3, 0x06, 0x0e, 0x0f, 0xce, 0x28, 0x69, 0x5d, 0x19, 0x75, 0x43, 0x32, 0xbb, 0xc7, 0x9b, + 0xeb, 0x3d, 0x60, 0x6c, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x30, 0x0a, 0x06, 0x08, + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x04, 0x47, 0x30, 0x45, 0x02, 0x21, 0x00, 0xf9, 0xd5, 0x91, 0x66, 0xca, 0xab, + 0x43, 0x82, 0xa7, 0x47, 0x24, 0xe6, 0x06, 0xbf, 0x1e, 0x2b, 0x4e, 0x7a, 0xb4, 0xf3, 0x04, 0x29, 0x21, 0x2b, 0x60, 0xfd, 0xa0, + 0x16, 0xea, 0xdc, 0x7d, 0x25, 0x02, 0x20, 0x62, 0xf1, 0x89, 0xfe, 0x4e, 0x60, 0x28, 0xd5, 0xc8, 0x42, 0x6a, 0xb7, 0x7e, 0xe5, + 0x1e, 0xf5, 0x68, 0x00, 0x23, 0x86, 0xba, 0x39, 0xe7, 0xab, 0x2d, 0xe5, 0x71, 0xe5, 0x36, 0x4e, 0xd0, 0x38, +}; + +static constexpr uint8_t gUntrustedCdVerifyingKeyBytes[65] = { 0x04, 0xbc, 0x87, 0x13, 0x3a, 0x19, 0x16, 0x87, 0x04, 0x34, 0x1b, + 0x78, 0xc5, 0x70, 0x41, 0x21, 0x14, 0xbb, 0xe5, 0x3f, 0x62, 0x45, + 0x70, 0xe8, 0xf2, 0x37, 0x78, 0x77, 0x1a, 0xf3, 0x5c, 0xd8, 0x04, + 0x21, 0xc8, 0x2d, 0x40, 0x7f, 0xee, 0x37, 0xf5, 0xa5, 0x9f, 0x17, + 0x26, 0x33, 0x00, 0x4d, 0xf7, 0x66, 0xa3, 0x3a, 0x50, 0x75, 0x9f, + 0xcf, 0xd2, 0xb2, 0x1b, 0x5e, 0x58, 0x75, 0x08, 0x82, 0x3e }; +static const P256PublicKey gUntrustedCdVerifyingKey(gUntrustedCdVerifyingKeyBytes); + +static constexpr uint8_t gUntrustedCdKid[20] = { 0x75, 0xE3, 0x06, 0x0E, 0x0F, 0xCE, 0x28, 0x69, 0x5D, 0x19, + 0x75, 0x43, 0x32, 0xBB, 0xC7, 0x9B, 0xEB, 0x3D, 0x60, 0x6C }; + struct TestCase { ByteSpan signerCert; @@ -464,6 +506,53 @@ static void TestCD_EncodeDecode_Random(nlTestSuite * inSuite, void * inContext) } } +static void TestCD_DefaultCdTrustStore(nlTestSuite * inSuite, void * inContext) +{ + chip::Credentials::CsaCdKeysTrustStore trustStore; + + // Make sure that for an untrusted CD, whose key is not in truststore, we cannot find the key. + { + ByteSpan signerKeyIdOut; + NL_TEST_ASSERT_SUCCESS(inSuite, CMS_ExtractKeyId(ByteSpan(gUntrustedCd), signerKeyIdOut)); + NL_TEST_ASSERT(inSuite, !trustStore.IsCdTestKey(signerKeyIdOut)); + + P256PublicKey pubKey; + NL_TEST_ASSERT(inSuite, CHIP_ERROR_KEY_NOT_FOUND == trustStore.LookupVerifyingKey(signerKeyIdOut, pubKey)); + } + + // Verify that a payload known to use the test key passes verification w/ default truststore + { + const auto & testCase = sTestCases[0]; + ByteSpan signerKeyIdOut; + NL_TEST_ASSERT_SUCCESS(inSuite, CMS_ExtractKeyId(testCase.cdCMSSigned, signerKeyIdOut)); + NL_TEST_ASSERT(inSuite, trustStore.IsCdTestKey(signerKeyIdOut)); + + P256PublicKey pubKey; + ByteSpan cdContentOut; + NL_TEST_ASSERT_SUCCESS(inSuite, trustStore.LookupVerifyingKey(signerKeyIdOut, pubKey)); + + NL_TEST_ASSERT(inSuite, CMS_Verify(testCase.cdCMSSigned, pubKey, cdContentOut) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, testCase.cdContent.data_equal(cdContentOut)); + } + + // Verify that after adding the verifying key to the trust store, it is now possible to + // verify the CD signature. + { + P256PublicKey pubKey; + ByteSpan cdContentOut; + NL_TEST_ASSERT_SUCCESS(inSuite, trustStore.AddTrustedKey(ByteSpan(gUntrustedCdKid), gUntrustedCdVerifyingKey)); + + ByteSpan signerKeyIdOut; + NL_TEST_ASSERT_SUCCESS(inSuite, CMS_ExtractKeyId(ByteSpan(gUntrustedCd), signerKeyIdOut)); + NL_TEST_ASSERT(inSuite, signerKeyIdOut.data_equal(ByteSpan(gUntrustedCdKid))); + + NL_TEST_ASSERT_SUCCESS(inSuite, trustStore.LookupVerifyingKey(signerKeyIdOut, pubKey)); + NL_TEST_ASSERT(inSuite, pubKey.Matches(gUntrustedCdVerifyingKey)); + + NL_TEST_ASSERT_SUCCESS(inSuite, CMS_Verify(ByteSpan(gUntrustedCd), pubKey, cdContentOut)); + } +} + #define NL_TEST_DEF_FN(fn) NL_TEST_DEF("Test " #fn, fn) /** * Test Suite. It lists all the test functions. @@ -474,6 +563,7 @@ static const nlTest sTests[] = { NL_TEST_DEF_FN(TestCD_EncodeDecode), NL_TEST_DEF_FN(TestCD_CMSVerifyAndExtract), NL_TEST_DEF_FN(TestCD_CertificationElementsDecoder), NL_TEST_DEF_FN(TestCD_EncodeDecode_Random), + NL_TEST_DEF_FN(TestCD_DefaultCdTrustStore), NL_TEST_SENTINEL() }; int TestCertificationDeclaration(void)