Matter SDK Coverage Report
Current view: top level - credentials/attestation_verifier - TestDACRevocationDelegateImpl.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 82.8 % 145 120
Test Date: 2025-01-17 19:00:11 Functions: 87.5 % 16 14

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2024 Project CHIP Authors
       4              :  *
       5              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6              :  *    you may not use this file except in compliance with the License.
       7              :  *    You may obtain a copy of the License at
       8              :  *
       9              :  *        http://www.apache.org/licenses/LICENSE-2.0
      10              :  *
      11              :  *    Unless required by applicable law or agreed to in writing, software
      12              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14              :  *    See the License for the specific language governing permissions and
      15              :  *    limitations under the License.
      16              :  */
      17              : 
      18              : #include <credentials/attestation_verifier/DeviceAttestationVerifier.h>
      19              : #include <credentials/attestation_verifier/TestDACRevocationDelegateImpl.h>
      20              : #include <lib/support/Base64.h>
      21              : #include <lib/support/BytesToHex.h>
      22              : #include <lib/support/logging/CHIPLogging.h>
      23              : 
      24              : #include <algorithm>
      25              : #include <fstream>
      26              : #include <json/json.h>
      27              : 
      28              : using namespace chip::Crypto;
      29              : 
      30              : namespace chip {
      31              : namespace Credentials {
      32              : 
      33              : namespace {
      34              : 
      35           51 : CHIP_ERROR BytesToHexStr(const ByteSpan & bytes, std::string & outHexStr)
      36              : {
      37           51 :     size_t hexLength = bytes.size() * 2;
      38           51 :     outHexStr.resize(hexLength);
      39              : 
      40           51 :     Encoding::HexFlags flags = Encoding::HexFlags::kUppercase;
      41           51 :     return BytesToHex(bytes.data(), bytes.size(), &outHexStr[0], hexLength, flags);
      42              : }
      43              : 
      44              : } // anonymous namespace
      45              : 
      46            0 : CHIP_ERROR TestDACRevocationDelegateImpl::SetDeviceAttestationRevocationSetPath(std::string_view path)
      47              : {
      48            0 :     VerifyOrReturnError(path.empty() != true, CHIP_ERROR_INVALID_ARGUMENT);
      49            0 :     mDeviceAttestationRevocationSetPath = path;
      50            0 :     return CHIP_NO_ERROR;
      51              : }
      52              : 
      53           11 : CHIP_ERROR TestDACRevocationDelegateImpl::SetDeviceAttestationRevocationData(const std::string & jsonData)
      54              : {
      55           11 :     mRevocationData = jsonData;
      56           11 :     return CHIP_NO_ERROR;
      57              : }
      58              : 
      59            0 : void TestDACRevocationDelegateImpl::ClearDeviceAttestationRevocationSetPath()
      60              : {
      61              :     // clear the string_view
      62            0 :     mDeviceAttestationRevocationSetPath = mDeviceAttestationRevocationSetPath.substr(0, 0);
      63            0 : }
      64              : 
      65           11 : void TestDACRevocationDelegateImpl::ClearDeviceAttestationRevocationData()
      66              : {
      67           11 :     mRevocationData.clear();
      68           11 : }
      69              : 
      70              : // Check if issuer and AKID matches with the crl signer OR crl signer delegator's subject and SKID
      71            8 : bool TestDACRevocationDelegateImpl::CrossValidateCert(const Json::Value & revokedSet, const std::string & akidHexStr,
      72              :                                                       const std::string & issuerNameBase64Str)
      73              : {
      74            8 :     std::string certBase64;
      75            8 :     [[maybe_unused]] std::string certType;
      76              : 
      77            8 :     if (revokedSet.isMember("crl_signer_delegator"))
      78              :     {
      79            1 :         certBase64 = revokedSet["crl_signer_delegator"].asString();
      80            1 :         certType   = "CRL Signer delegator";
      81              :     }
      82              :     else
      83              :     {
      84            7 :         certBase64 = revokedSet["crl_signer_cert"].asString();
      85            7 :         certType   = "CRL Signer";
      86              :     }
      87              : 
      88            8 :     uint8_t certDerBuf[kMax_x509_Certificate_Length] = { 0 };
      89            8 :     MutableByteSpan certDER(certDerBuf);
      90              : 
      91              :     // Verify we have enough room to store the decoded certificate
      92            8 :     size_t maxDecodeLen = BASE64_MAX_DECODED_LEN(certBase64.size());
      93            8 :     VerifyOrReturnValue(certDER.size() >= maxDecodeLen, false);
      94              : 
      95            8 :     uint16_t derLen = Base64Decode(certBase64.c_str(), static_cast<uint16_t>(certBase64.size()), certDER.data());
      96            8 :     VerifyOrReturnValue(derLen != UINT16_MAX, false);
      97            8 :     certDER.reduce_size(derLen);
      98              : 
      99            8 :     std::string subject;
     100            8 :     std::string keyId;
     101              : 
     102            8 :     VerifyOrReturnValue(CHIP_NO_ERROR == GetSubjectNameBase64Str(certDER, subject), false);
     103            7 :     VerifyOrReturnValue(CHIP_NO_ERROR == GetSKIDHexStr(certDER, keyId), false);
     104              : 
     105            7 :     ChipLogDetail(NotSpecified, "%s: Subject: %s", certType.c_str(), subject.c_str());
     106            7 :     ChipLogDetail(NotSpecified, "%s: SKID: %s", certType.c_str(), keyId.c_str());
     107              : 
     108            7 :     return (akidHexStr == keyId && issuerNameBase64Str == subject);
     109            8 : }
     110              : 
     111              : // This method parses the below JSON Scheme
     112              : // [
     113              : //   {
     114              : //     "type": "revocation_set",
     115              : //     "issuer_subject_key_id": "<issuer subject key ID as uppercase hex, 20 bytes>",
     116              : //     "issuer_name": "<ASN.1 SEQUENCE of Issuer of the CRL as base64>",
     117              : //     "revoked_serial_numbers: [
     118              : //       "serial1 bytes as base64",
     119              : //       "serial2 bytes as base64"
     120              : //     ]
     121              : //     "crl_signer_cert": "<base64 encoded DER certificate>",
     122              : //     "crl_signer_delegator": "<base64 encoded DER certificate>",
     123              : //   }
     124              : // ]
     125              : //
     126           22 : bool TestDACRevocationDelegateImpl::IsEntryInRevocationSet(const std::string & akidHexStr, const std::string & issuerNameBase64Str,
     127              :                                                            const std::string & serialNumberHexStr)
     128              : {
     129           22 :     Json::Value jsonData;
     130              : 
     131              :     // Try direct data first, then fall back to file
     132           22 :     if (!mRevocationData.empty())
     133              :     {
     134           22 :         std::string errs;
     135           44 :         std::istringstream jsonStream(!mRevocationData.empty() ? mRevocationData : "[]");
     136           22 :         if (!Json::parseFromStream(Json::CharReaderBuilder(), jsonStream, &jsonData, &errs))
     137              :         {
     138            2 :             ChipLogError(NotSpecified, "Failed to parse JSON data: %s", errs.c_str());
     139            2 :             return false;
     140              :         }
     141           24 :     }
     142            0 :     else if (!mDeviceAttestationRevocationSetPath.empty())
     143              :     {
     144            0 :         std::string errs;
     145            0 :         std::ifstream file(mDeviceAttestationRevocationSetPath.c_str());
     146            0 :         if (!file.is_open())
     147              :         {
     148            0 :             ChipLogError(NotSpecified, "Failed to open file: %s", mDeviceAttestationRevocationSetPath.c_str());
     149            0 :             return false;
     150              :         }
     151              : 
     152            0 :         bool parsingSuccessful = Json::parseFromStream(Json::CharReaderBuilder(), file, &jsonData, &errs);
     153            0 :         file.close();
     154              : 
     155            0 :         if (!parsingSuccessful)
     156              :         {
     157            0 :             ChipLogError(NotSpecified, "Failed to parse JSON from file: %s", errs.c_str());
     158            0 :             return false;
     159              :         }
     160            0 :     }
     161              :     else
     162              :     {
     163            0 :         ChipLogDetail(NotSpecified, "No revocation data available");
     164              :         // No revocation data available
     165            0 :         return false;
     166              :     }
     167              : 
     168              :     // 6.2.4.2. Determining Revocation Status of an Entity
     169           37 :     for (const auto & revokedSet : jsonData)
     170              :     {
     171           23 :         if (revokedSet["issuer_name"].asString() != issuerNameBase64Str)
     172              :         {
     173           13 :             continue;
     174              :         }
     175           10 :         if (revokedSet["issuer_subject_key_id"].asString() != akidHexStr)
     176              :         {
     177            2 :             continue;
     178              :         }
     179              : 
     180              :         // 4.a cross validate PAI with crl signer OR crl signer delegator
     181              :         // 4.b cross validate DAC with crl signer OR crl signer delegator
     182           13 :         VerifyOrReturnValue(CrossValidateCert(revokedSet, akidHexStr, issuerNameBase64Str), false);
     183              : 
     184              :         // 4.c check if serial number is revoked
     185           10 :         for (const auto & revokedSerialNumber : revokedSet["revoked_serial_numbers"])
     186              :         {
     187            8 :             if (revokedSerialNumber.asString() == serialNumberHexStr)
     188              :             {
     189            5 :                 return true;
     190              :             }
     191              :         }
     192              :     }
     193           14 :     return false;
     194           22 : }
     195              : 
     196           29 : CHIP_ERROR TestDACRevocationDelegateImpl::GetKeyIDHexStr(const ByteSpan & certDer, std::string & outKeyIDHexStr,
     197              :                                                          KeyIdType keyIdType)
     198              : {
     199              :     static_assert(kAuthorityKeyIdentifierLength == kSubjectKeyIdentifierLength, "AKID and SKID length mismatch");
     200              : 
     201              :     uint8_t keyIdBuf[kAuthorityKeyIdentifierLength];
     202           29 :     MutableByteSpan keyId(keyIdBuf);
     203              : 
     204           29 :     switch (keyIdType)
     205              :     {
     206           22 :     case KeyIdType::kAKID:
     207           22 :         ReturnErrorOnFailure(ExtractAKIDFromX509Cert(certDer, keyId));
     208           22 :         break;
     209              : 
     210            7 :     case KeyIdType::kSKID:
     211            7 :         ReturnErrorOnFailure(ExtractSKIDFromX509Cert(certDer, keyId));
     212            7 :         break;
     213              : 
     214            0 :     default:
     215            0 :         return CHIP_ERROR_INVALID_ARGUMENT;
     216              :     }
     217              : 
     218           29 :     return BytesToHexStr(keyId, outKeyIDHexStr);
     219              : }
     220              : 
     221           22 : CHIP_ERROR TestDACRevocationDelegateImpl::GetAKIDHexStr(const ByteSpan & certDer, std::string & outAKIDHexStr)
     222              : {
     223           22 :     return GetKeyIDHexStr(certDer, outAKIDHexStr, KeyIdType::kAKID);
     224              : }
     225              : 
     226            7 : CHIP_ERROR TestDACRevocationDelegateImpl::GetSKIDHexStr(const ByteSpan & certDer, std::string & outSKIDHexStr)
     227              : {
     228            7 :     return GetKeyIDHexStr(certDer, outSKIDHexStr, KeyIdType::kSKID);
     229              : }
     230              : 
     231           22 : CHIP_ERROR TestDACRevocationDelegateImpl::GetSerialNumberHexStr(const ByteSpan & certDer, std::string & outSerialNumberHexStr)
     232              : {
     233           22 :     uint8_t serialNumberBuf[kMaxCertificateSerialNumberLength] = { 0 };
     234           22 :     MutableByteSpan serialNumber(serialNumberBuf);
     235              : 
     236           22 :     ReturnErrorOnFailure(ExtractSerialNumberFromX509Cert(certDer, serialNumber));
     237           22 :     return BytesToHexStr(serialNumber, outSerialNumberHexStr);
     238              : }
     239              : 
     240           30 : CHIP_ERROR TestDACRevocationDelegateImpl::GetRDNBase64Str(const ByteSpan & certDer, std::string & outRDNBase64String,
     241              :                                                           RDNType rdnType)
     242              : {
     243           30 :     uint8_t rdnBuf[kMaxCertificateDistinguishedNameLength] = { 0 };
     244           30 :     MutableByteSpan rdn(rdnBuf);
     245              : 
     246           30 :     switch (rdnType)
     247              :     {
     248           22 :     case RDNType::kIssuer:
     249           22 :         ReturnErrorOnFailure(ExtractIssuerFromX509Cert(certDer, rdn));
     250           22 :         break;
     251              : 
     252            8 :     case RDNType::kSubject:
     253            8 :         ReturnErrorOnFailure(ExtractSubjectFromX509Cert(certDer, rdn));
     254            7 :         break;
     255              : 
     256            0 :     default:
     257            0 :         return CHIP_ERROR_INVALID_ARGUMENT;
     258              :     }
     259              : 
     260              :     // calculate the b64 length needed
     261           29 :     size_t b64LenNeeded = BASE64_ENCODED_LEN(rdn.size());
     262              : 
     263              :     // Ensure string has enough capacity for base64 encoded data
     264           29 :     outRDNBase64String.resize(b64LenNeeded);
     265              : 
     266           29 :     uint16_t encodedLen = Base64Encode(rdn.data(), static_cast<uint16_t>(rdn.size()), &outRDNBase64String[0]);
     267           29 :     outRDNBase64String.resize(encodedLen);
     268              : 
     269           29 :     return CHIP_NO_ERROR;
     270              : }
     271              : 
     272           22 : CHIP_ERROR TestDACRevocationDelegateImpl::GetIssuerNameBase64Str(const ByteSpan & certDer, std::string & outIssuerNameBase64String)
     273              : {
     274           22 :     return GetRDNBase64Str(certDer, outIssuerNameBase64String, RDNType::kIssuer);
     275              : }
     276              : 
     277            8 : CHIP_ERROR TestDACRevocationDelegateImpl::GetSubjectNameBase64Str(const ByteSpan & certDer,
     278              :                                                                   std::string & outSubjectNameBase64String)
     279              : {
     280            8 :     return GetRDNBase64Str(certDer, outSubjectNameBase64String, RDNType::kSubject);
     281              : }
     282              : 
     283              : // @param certDer Certificate, in DER format, to check for revocation
     284           22 : bool TestDACRevocationDelegateImpl::IsCertificateRevoked(const ByteSpan & certDer)
     285              : {
     286           22 :     std::string serialNumber;
     287           22 :     std::string akid;
     288           22 :     std::string issuerName;
     289              : 
     290           22 :     VerifyOrReturnValue(CHIP_NO_ERROR == GetIssuerNameBase64Str(certDer, issuerName), false);
     291           22 :     ChipLogDetail(NotSpecified, "Issuer: %.*s", static_cast<int>(issuerName.size()), issuerName.data());
     292              : 
     293           22 :     VerifyOrReturnValue(CHIP_NO_ERROR == GetSerialNumberHexStr(certDer, serialNumber), false);
     294           22 :     ChipLogDetail(NotSpecified, "Serial Number: %.*s", static_cast<int>(serialNumber.size()), serialNumber.data());
     295              : 
     296           22 :     VerifyOrReturnValue(CHIP_NO_ERROR == GetAKIDHexStr(certDer, akid), false);
     297           22 :     ChipLogDetail(NotSpecified, "AKID: %.*s", static_cast<int>(akid.size()), akid.data());
     298              : 
     299           22 :     return IsEntryInRevocationSet(akid, issuerName, serialNumber);
     300           22 : }
     301              : 
     302           13 : void TestDACRevocationDelegateImpl::CheckForRevokedDACChain(
     303              :     const DeviceAttestationVerifier::AttestationInfo & info,
     304              :     Callback::Callback<DeviceAttestationVerifier::OnAttestationInformationVerification> * onCompletion)
     305              : {
     306           13 :     AttestationVerificationResult attestationError = AttestationVerificationResult::kSuccess;
     307              : 
     308           13 :     if (mDeviceAttestationRevocationSetPath.empty() && mRevocationData.empty())
     309              :     {
     310            2 :         onCompletion->mCall(onCompletion->mContext, info, attestationError);
     311            2 :         return;
     312              :     }
     313              : 
     314           11 :     ChipLogDetail(NotSpecified, "Checking for revoked DAC in %s", mDeviceAttestationRevocationSetPath.c_str());
     315              : 
     316           11 :     if (IsCertificateRevoked(info.dacDerBuffer))
     317              :     {
     318            3 :         ChipLogProgress(NotSpecified, "Found revoked DAC in %s", mDeviceAttestationRevocationSetPath.c_str());
     319            3 :         attestationError = AttestationVerificationResult::kDacRevoked;
     320              :     }
     321              : 
     322           11 :     ChipLogDetail(NotSpecified, "Checking for revoked PAI in %s", mDeviceAttestationRevocationSetPath.c_str());
     323              : 
     324           11 :     if (IsCertificateRevoked(info.paiDerBuffer))
     325              :     {
     326            2 :         ChipLogProgress(NotSpecified, "Found revoked PAI in %s", mDeviceAttestationRevocationSetPath.c_str());
     327              : 
     328            2 :         if (attestationError == AttestationVerificationResult::kDacRevoked)
     329              :         {
     330            1 :             attestationError = AttestationVerificationResult::kPaiAndDacRevoked;
     331              :         }
     332              :         else
     333              :         {
     334            1 :             attestationError = AttestationVerificationResult::kPaiRevoked;
     335              :         }
     336              :     }
     337              : 
     338           11 :     onCompletion->mCall(onCompletion->mContext, info, attestationError);
     339              : }
     340              : 
     341              : } // namespace Credentials
     342              : } // namespace chip
        

Generated by: LCOV version 2.0-1