/* * * Copyright (c) 2024 Project CHIP Authors * * 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 #include #include #include #include #include #include using namespace chip::Crypto; namespace chip { namespace Credentials { namespace { CHIP_ERROR BytesToHexStr(const ByteSpan & bytes, MutableCharSpan & outHexStr) { Encoding::HexFlags flags = Encoding::HexFlags::kUppercase; ReturnErrorOnFailure(BytesToHex(bytes.data(), bytes.size(), outHexStr.data(), outHexStr.size(), flags)); outHexStr.reduce_size(2 * bytes.size()); return CHIP_NO_ERROR; } } // anonymous namespace CHIP_ERROR TestDACRevocationDelegateImpl::SetDeviceAttestationRevocationSetPath(std::string_view path) { VerifyOrReturnError(path.empty() != true, CHIP_ERROR_INVALID_ARGUMENT); mDeviceAttestationRevocationSetPath = path; return CHIP_NO_ERROR; } void TestDACRevocationDelegateImpl::ClearDeviceAttestationRevocationSetPath() { // clear the string_view mDeviceAttestationRevocationSetPath = mDeviceAttestationRevocationSetPath.substr(0, 0); } // This method parses the below JSON Scheme // [ // { // "type": "revocation_set", // "issuer_subject_key_id": "", // "issuer_name": "", // "revoked_serial_numbers: [ // "serial1 bytes as base64", // "serial2 bytes as base64" // ] // } // ] // bool TestDACRevocationDelegateImpl::IsEntryInRevocationSet(const CharSpan & akidHexStr, const CharSpan & issuerNameBase64Str, const CharSpan & serialNumberHexStr) { std::ifstream file(mDeviceAttestationRevocationSetPath.c_str()); if (!file.is_open()) { ChipLogError(NotSpecified, "Failed to open file: %s", mDeviceAttestationRevocationSetPath.c_str()); return false; } // Parse the JSON data incrementally Json::CharReaderBuilder readerBuilder; Json::Value jsonData; std::string errs; bool parsingSuccessful = Json::parseFromStream(readerBuilder, file, &jsonData, &errs); // Close the file as it's no longer needed file.close(); if (!parsingSuccessful) { ChipLogError(NotSpecified, "Failed to parse JSON: %s", errs.c_str()); return false; } std::string issuerName = std::string(issuerNameBase64Str.data(), issuerNameBase64Str.size()); std::string serialNumber = std::string(serialNumberHexStr.data(), serialNumberHexStr.size()); std::string akid = std::string(akidHexStr.data(), akidHexStr.size()); for (const auto & revokedSet : jsonData) { if (revokedSet["issuer_name"].asString() != issuerName) { continue; } if (revokedSet["issuer_subject_key_id"].asString() != akid) { continue; } for (const auto & revokedSerialNumber : revokedSet["revoked_serial_numbers"]) { if (revokedSerialNumber.asString() == serialNumber) { return true; } } } return false; } CHIP_ERROR TestDACRevocationDelegateImpl::GetAKIDHexStr(const ByteSpan & certDer, MutableCharSpan & outAKIDHexStr) { uint8_t akidBuf[kAuthorityKeyIdentifierLength]; MutableByteSpan akid(akidBuf); ReturnErrorOnFailure(ExtractAKIDFromX509Cert(certDer, akid)); return BytesToHexStr(akid, outAKIDHexStr); } CHIP_ERROR TestDACRevocationDelegateImpl::GetSerialNumberHexStr(const ByteSpan & certDer, MutableCharSpan & outSerialNumberHexStr) { uint8_t serialNumberBuf[kMaxCertificateSerialNumberLength] = { 0 }; MutableByteSpan serialNumber(serialNumberBuf); ReturnErrorOnFailure(ExtractSerialNumberFromX509Cert(certDer, serialNumber)); return BytesToHexStr(serialNumber, outSerialNumberHexStr); } CHIP_ERROR TestDACRevocationDelegateImpl::GetIssuerNameBase64Str(const ByteSpan & certDer, MutableCharSpan & outIssuerNameBase64String) { uint8_t issuerBuf[kMaxCertificateDistinguishedNameLength] = { 0 }; MutableByteSpan issuer(issuerBuf); ReturnErrorOnFailure(ExtractIssuerFromX509Cert(certDer, issuer)); VerifyOrReturnError(outIssuerNameBase64String.size() >= BASE64_ENCODED_LEN(issuer.size()), CHIP_ERROR_BUFFER_TOO_SMALL); uint16_t encodedLen = Base64Encode(issuer.data(), static_cast(issuer.size()), outIssuerNameBase64String.data()); outIssuerNameBase64String.reduce_size(encodedLen); return CHIP_NO_ERROR; } bool TestDACRevocationDelegateImpl::IsCertificateRevoked(const ByteSpan & certDer) { static constexpr uint32_t maxIssuerBase64Len = BASE64_ENCODED_LEN(kMaxCertificateDistinguishedNameLength); char issuerNameBuffer[maxIssuerBase64Len] = { 0 }; char serialNumberHexStrBuffer[2 * kMaxCertificateSerialNumberLength] = { 0 }; char akidHexStrBuffer[2 * kAuthorityKeyIdentifierLength] = { 0 }; MutableCharSpan issuerName(issuerNameBuffer); MutableCharSpan serialNumber(serialNumberHexStrBuffer); MutableCharSpan akid(akidHexStrBuffer); VerifyOrReturnValue(CHIP_NO_ERROR == GetIssuerNameBase64Str(certDer, issuerName), false); ChipLogDetail(NotSpecified, "Issuer: %.*s", static_cast(issuerName.size()), issuerName.data()); VerifyOrReturnValue(CHIP_NO_ERROR == GetSerialNumberHexStr(certDer, serialNumber), false); ChipLogDetail(NotSpecified, "Serial Number: %.*s", static_cast(serialNumber.size()), serialNumber.data()); VerifyOrReturnValue(CHIP_NO_ERROR == GetAKIDHexStr(certDer, akid), false); ChipLogDetail(NotSpecified, "AKID: %.*s", static_cast(akid.size()), akid.data()); // TODO: Cross-validate the CRLSignerCertificate and CRLSignerDelegator per spec: #34587 return IsEntryInRevocationSet(akid, issuerName, serialNumber); } void TestDACRevocationDelegateImpl::CheckForRevokedDACChain( const DeviceAttestationVerifier::AttestationInfo & info, Callback::Callback * onCompletion) { AttestationVerificationResult attestationError = AttestationVerificationResult::kSuccess; if (mDeviceAttestationRevocationSetPath.empty()) { onCompletion->mCall(onCompletion->mContext, info, attestationError); } ChipLogDetail(NotSpecified, "Checking for revoked DAC in %s", mDeviceAttestationRevocationSetPath.c_str()); if (IsCertificateRevoked(info.dacDerBuffer)) { ChipLogProgress(NotSpecified, "Found revoked DAC in %s", mDeviceAttestationRevocationSetPath.c_str()); attestationError = AttestationVerificationResult::kDacRevoked; } ChipLogDetail(NotSpecified, "Checking for revoked PAI in %s", mDeviceAttestationRevocationSetPath.c_str()); if (IsCertificateRevoked(info.paiDerBuffer)) { ChipLogProgress(NotSpecified, "Found revoked PAI in %s", mDeviceAttestationRevocationSetPath.c_str()); if (attestationError == AttestationVerificationResult::kDacRevoked) { attestationError = AttestationVerificationResult::kPaiAndDacRevoked; } else { attestationError = AttestationVerificationResult::kPaiRevoked; } } onCompletion->mCall(onCompletion->mContext, info, attestationError); } } // namespace Credentials } // namespace chip