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 /** * @} */