|  | /* | 
|  | * | 
|  | *    Copyright (c) 2022 Project CHIP Authors | 
|  | *    All rights reserved. | 
|  | * | 
|  | *    Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | *    you may not use this file except in compliance with the License. | 
|  | *    You may obtain a copy of the License at | 
|  | * | 
|  | *        http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | *    Unless required by applicable law or agreed to in writing, software | 
|  | *    distributed under the License is distributed on an "AS IS" BASIS, | 
|  | *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | *    See the License for the specific language governing permissions and | 
|  | *    limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include <pw_unit_test/framework.h> | 
|  |  | 
|  | #include <crypto/CHIPCryptoPAL.h> | 
|  |  | 
|  | #include <app/tests/suites/credentials/TestHarnessDACProvider.h> | 
|  | #include <controller/CHIPDeviceController.h> | 
|  | #include <controller/CommissioneeDeviceProxy.h> | 
|  | #include <credentials/CertificationDeclaration.h> | 
|  | #include <credentials/DeviceAttestationCredsProvider.h> | 
|  | #include <credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h> | 
|  | #include <credentials/attestation_verifier/DeviceAttestationVerifier.h> | 
|  | #include <credentials/examples/DeviceAttestationCredsExample.h> | 
|  |  | 
|  | #include <lib/core/CHIPError.h> | 
|  | #include <lib/core/StringBuilderAdapters.h> | 
|  | #include <lib/support/CHIPMem.h> | 
|  | #include <lib/support/Span.h> | 
|  |  | 
|  | #include <dirent.h> | 
|  | #include <stdio.h> | 
|  | #include <string> | 
|  |  | 
|  | using namespace chip; | 
|  | using namespace chip::Crypto; | 
|  | using namespace chip::Credentials; | 
|  |  | 
|  | static void OnAttestationInformationVerificationCallback(void * context, const DeviceAttestationVerifier::AttestationInfo & info, | 
|  | AttestationVerificationResult result) | 
|  | { | 
|  | AttestationVerificationResult * pResult = reinterpret_cast<AttestationVerificationResult *>(context); | 
|  | *pResult                                = result; | 
|  | } | 
|  |  | 
|  | struct TestCommissionerDUTVectors : public ::testing::Test | 
|  | { | 
|  | static void SetUpTestSuite() { ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); } | 
|  |  | 
|  | static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); } | 
|  | }; | 
|  |  | 
|  | TEST_F(TestCommissionerDUTVectors, TestCommissionerDUTVectors) | 
|  | { | 
|  | DeviceAttestationVerifier * example_dac_verifier = GetDefaultDACVerifier(GetTestAttestationTrustStore()); | 
|  | ASSERT_NE(example_dac_verifier, nullptr); | 
|  |  | 
|  | std::string dirPath("../../../../../credentials/development/commissioner_dut/"); | 
|  | DIR * dir = opendir(dirPath.c_str()); | 
|  | while (dir == nullptr && (dirPath.find("../") == 0)) | 
|  | { | 
|  | dirPath = dirPath.substr(3); | 
|  | dir     = opendir(dirPath.c_str()); | 
|  | } | 
|  | if (dir == nullptr) | 
|  | { | 
|  | ChipLogError(Crypto, "Couldn't open folder with Commissioner DUT Test Vectors."); | 
|  | return; | 
|  | } | 
|  |  | 
|  | dirent * entry; | 
|  | while ((entry = readdir(dir)) != nullptr) | 
|  | { | 
|  | if (entry->d_name[0] == '.') | 
|  | continue; | 
|  |  | 
|  | // Skip this test case because the code below will fail to use secp256k1 to sign attestation data. | 
|  | if (strstr(entry->d_name, "struct_dac_sig_curve_secp256k1")) | 
|  | continue; | 
|  |  | 
|  | std::string jsonFilePath = dirPath + std::string(entry->d_name) + std::string("/test_case_vector.json"); | 
|  |  | 
|  | chip::Credentials::Examples::TestHarnessDACProvider dacProvider; | 
|  | dacProvider.Init(jsonFilePath.c_str()); | 
|  |  | 
|  | uint8_t attestationChallengeBuf[Crypto::kAES_CCM128_Key_Length]; | 
|  | MutableByteSpan attestationChallengeSpan(attestationChallengeBuf); | 
|  | Crypto::DRBG_get_bytes(attestationChallengeBuf, sizeof(attestationChallengeBuf)); | 
|  |  | 
|  | Crypto::P256ECDSASignature signature; | 
|  | MutableByteSpan attestationSignatureSpan{ signature.Bytes(), signature.Capacity() }; | 
|  |  | 
|  | uint8_t certDeclBuf[Credentials::kMaxCMSSignedCDMessage]; | 
|  | MutableByteSpan certDeclSpan(certDeclBuf); | 
|  |  | 
|  | uint8_t dacCertBuf[kMaxDERCertLength]; | 
|  | MutableByteSpan dacCertSpan(dacCertBuf); | 
|  |  | 
|  | uint8_t paiCertBuf[kMaxDERCertLength]; | 
|  | MutableByteSpan paiCertSpan(paiCertBuf); | 
|  |  | 
|  | uint8_t attestationNonceBuf[kAttestationNonceLength]; | 
|  | MutableByteSpan attestationNonceSpan(attestationNonceBuf); | 
|  | Crypto::DRBG_get_bytes(attestationNonceBuf, sizeof(attestationNonceBuf)); | 
|  |  | 
|  | VendorId vid = TestVendor1; | 
|  | uint16_t pid = dacProvider.GetPid(); | 
|  |  | 
|  | EXPECT_EQ(dacProvider.GetCertificationDeclaration(certDeclSpan), CHIP_NO_ERROR); | 
|  | EXPECT_EQ(dacProvider.GetDeviceAttestationCert(dacCertSpan), CHIP_NO_ERROR); | 
|  | EXPECT_EQ(dacProvider.GetProductAttestationIntermediateCert(paiCertSpan), CHIP_NO_ERROR); | 
|  |  | 
|  | size_t attestationElementsLen = | 
|  | TLV::EstimateStructOverhead(certDeclSpan.size(), attestationNonceSpan.size(), sizeof(uint64_t) * 8); | 
|  | Platform::ScopedMemoryBuffer<uint8_t> attestationElements; | 
|  | EXPECT_TRUE(attestationElements.Alloc(attestationElementsLen + attestationChallengeSpan.size())); | 
|  | MutableByteSpan attestationElementsSpan(attestationElements.Get(), attestationElementsLen); | 
|  |  | 
|  | // Construct attestation elements | 
|  | { | 
|  | uint32_t timestamp = 0; | 
|  | Credentials::DeviceAttestationVendorReservedConstructor emptyVendorReserved(nullptr, 0); | 
|  | const ByteSpan kEmptyFirmwareInfo; | 
|  |  | 
|  | EXPECT_EQ(Credentials::ConstructAttestationElements(certDeclSpan, attestationNonceSpan, timestamp, kEmptyFirmwareInfo, | 
|  | emptyVendorReserved, attestationElementsSpan), | 
|  | CHIP_NO_ERROR); | 
|  | } | 
|  |  | 
|  | // Generate attestation signature | 
|  | { | 
|  | // Append attestation challenge in the back of the reserved space for the signature | 
|  | memcpy(attestationElementsSpan.data() + attestationElementsSpan.size(), attestationChallengeSpan.data(), | 
|  | attestationChallengeSpan.size()); | 
|  | ByteSpan tbsSpan(attestationElementsSpan.data(), attestationElementsSpan.size() + attestationChallengeSpan.size()); | 
|  |  | 
|  | EXPECT_EQ(dacProvider.SignWithDeviceAttestationKey(tbsSpan, attestationSignatureSpan), CHIP_NO_ERROR); | 
|  | EXPECT_EQ(attestationSignatureSpan.size(), signature.Capacity()); | 
|  | } | 
|  |  | 
|  | AttestationVerificationResult attestationResult = AttestationVerificationResult::kNotImplemented; | 
|  | Callback::Callback<DeviceAttestationVerifier::OnAttestationInformationVerification> | 
|  | attestationInformationVerificationCallback(OnAttestationInformationVerificationCallback, &attestationResult); | 
|  |  | 
|  | DeviceAttestationVerifier::AttestationInfo info(attestationElementsSpan, attestationChallengeSpan, attestationSignatureSpan, | 
|  | paiCertSpan, dacCertSpan, attestationNonceSpan, vid, pid); | 
|  |  | 
|  | example_dac_verifier->VerifyAttestationInformation(info, &attestationInformationVerificationCallback); | 
|  |  | 
|  | bool isSuccessCase = dacProvider.IsSuccessCase(); | 
|  | // The following test vectors are success conditions for an SDK commissioner for the following reasons: | 
|  | // struct_cd_device_type_id_mismatch - requires DCL access, which the SDK does not have and is not required | 
|  | // struct_cd_security_info_wrong - while devices are required to set this to 0, commissioners are required to ignore it | 
|  | //                                 (see 6.3.1) | 
|  | //                                 hence this is marked as a failure for devices, but should be a success case for | 
|  | //                                 commissioners | 
|  | // struct_cd_security_level_wrong - as with security info, commissioners are required to ignore this value (see 6.3.1) | 
|  | // struct_cd_version_number_wrong - this value is not meant to be interpreted by commissioners, so errors here should be | 
|  | //                                  ignored (6.3.1) | 
|  | // struct_cd_cert_id_mismatch - requires DCL access, which the SDK does not have and is not required. | 
|  | if (strstr(entry->d_name, "struct_cd_device_type_id_mismatch") || strstr(entry->d_name, "struct_cd_security_info_wrong") || | 
|  | strstr(entry->d_name, "struct_cd_security_level_wrong") || strstr(entry->d_name, "struct_cd_version_number_wrong") || | 
|  | strstr(entry->d_name, "struct_cd_cert_id_mismatch")) | 
|  | { | 
|  | isSuccessCase = true; | 
|  | } | 
|  |  | 
|  | if (isSuccessCase) | 
|  | { | 
|  | EXPECT_EQ(attestationResult, AttestationVerificationResult::kSuccess); | 
|  | } | 
|  | else | 
|  | { | 
|  | EXPECT_NE(attestationResult, AttestationVerificationResult::kSuccess); | 
|  | } | 
|  | } | 
|  | closedir(dir); | 
|  | } |