diff --git a/src/app/DeviceProxy.h b/src/app/DeviceProxy.h index ddc921530c5a09..aa526e71178d48 100644 --- a/src/app/DeviceProxy.h +++ b/src/app/DeviceProxy.h @@ -69,6 +69,8 @@ class DLL_EXPORT DeviceProxy virtual bool IsActive() const { return true; } + virtual CHIP_ERROR SetPeerId(ByteSpan rcac, ByteSpan noc) { return CHIP_ERROR_NOT_IMPLEMENTED; } + const ReliableMessageProtocolConfig & GetMRPConfig() const { return mMRPConfig; } protected: diff --git a/src/controller/AutoCommissioner.cpp b/src/controller/AutoCommissioner.cpp index f2233ab238b776..d11036e74984ff 100644 --- a/src/controller/AutoCommissioner.cpp +++ b/src/controller/AutoCommissioner.cpp @@ -19,10 +19,23 @@ #include #include +#include +#include namespace chip { namespace Controller { +AutoCommissioner::~AutoCommissioner() +{ + ReleaseDAC(); + ReleasePAI(); +} + +void AutoCommissioner::SetOperationalCredentialsDelegate(OperationalCredentialsDelegate * operationalCredentialsDelegate) +{ + mOperationalCredentialsDelegate = operationalCredentialsDelegate; +} + CHIP_ERROR AutoCommissioner::SetCommissioningParameters(const CommissioningParameters & params) { mParams = params; @@ -31,9 +44,11 @@ CHIP_ERROR AutoCommissioner::SetCommissioningParameters(const CommissioningParam ByteSpan dataset = params.GetThreadOperationalDataset().Value(); if (dataset.size() > CommissioningParameters::kMaxThreadDatasetLen) { + ChipLogError(Controller, "Thread operational data set is too large"); return CHIP_ERROR_INVALID_ARGUMENT; } memcpy(mThreadOperationalDataset, dataset.data(), dataset.size()); + ChipLogProgress(Controller, "Setting thread operational dataset from parameters"); mParams.SetThreadOperationalDataset(ByteSpan(mThreadOperationalDataset, dataset.size())); } if (params.HasWiFiCredentials()) @@ -42,13 +57,42 @@ CHIP_ERROR AutoCommissioner::SetCommissioningParameters(const CommissioningParam if (creds.ssid.size() > CommissioningParameters::kMaxSsidLen || creds.credentials.size() > CommissioningParameters::kMaxCredentialsLen) { + ChipLogError(Controller, "Wifi credentials are too large"); return CHIP_ERROR_INVALID_ARGUMENT; } memcpy(mSsid, creds.ssid.data(), creds.ssid.size()); memcpy(mCredentials, creds.credentials.data(), creds.credentials.size()); + ChipLogProgress(Controller, "Setting wifi credentials from parameters"); mParams.SetWiFiCredentials( WiFiCredentials(ByteSpan(mSsid, creds.ssid.size()), ByteSpan(mCredentials, creds.credentials.size()))); } + // If the AttestationNonce is passed in, using that else using a random one.. + if (params.HasAttestationNonce()) + { + ChipLogProgress(Controller, "Setting attestation nonce from parameters"); + VerifyOrReturnError(params.GetAttestationNonce().Value().size() == sizeof(mAttestationNonce), CHIP_ERROR_INVALID_ARGUMENT); + memcpy(mAttestationNonce, params.GetAttestationNonce().Value().data(), params.GetAttestationNonce().Value().size()); + } + else + { + ChipLogProgress(Controller, "Setting attestation nonce to random value"); + Crypto::DRBG_get_bytes(mAttestationNonce, sizeof(mAttestationNonce)); + } + mParams.SetAttestationNonce(ByteSpan(mAttestationNonce, sizeof(mAttestationNonce))); + + if (params.HasCSRNonce()) + { + ChipLogProgress(Controller, "Setting CSR nonce from parameters"); + VerifyOrReturnError(params.GetCSRNonce().Value().size() == sizeof(mCSRNonce), CHIP_ERROR_INVALID_ARGUMENT); + memcpy(mCSRNonce, params.GetCSRNonce().Value().data(), params.GetCSRNonce().Value().size()); + } + else + { + ChipLogProgress(Controller, "Setting CSR nonce to random value"); + Crypto::DRBG_get_bytes(mCSRNonce, sizeof(mCSRNonce)); + } + mParams.SetCSRNonce(ByteSpan(mCSRNonce, sizeof(mCSRNonce))); + return CHIP_NO_ERROR; } @@ -65,8 +109,22 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStag case CommissioningStage::kArmFailsafe: return CommissioningStage::kConfigRegulatory; case CommissioningStage::kConfigRegulatory: - return CommissioningStage::kDeviceAttestation; - case CommissioningStage::kDeviceAttestation: + return CommissioningStage::kSendPAICertificateRequest; + case CommissioningStage::kSendPAICertificateRequest: + return CommissioningStage::kSendDACCertificateRequest; + case CommissioningStage::kSendDACCertificateRequest: + return CommissioningStage::kSendAttestationRequest; + case CommissioningStage::kSendAttestationRequest: + return CommissioningStage::kAttestationVerification; + case CommissioningStage::kAttestationVerification: + return CommissioningStage::kSendOpCertSigningRequest; + case CommissioningStage::kSendOpCertSigningRequest: + return CommissioningStage::kGenerateNOCChain; + case CommissioningStage::kGenerateNOCChain: + return CommissioningStage::kSendTrustedRootCert; + case CommissioningStage::kSendTrustedRootCert: + return CommissioningStage::kSendNOC; + case CommissioningStage::kSendNOC: // TODO(cecille): device attestation casues operational cert provisioinging to happen, This should be a separate stage. // For thread and wifi, this should go to network setup then enable. For on-network we can skip right to finding the // operational network because the provisioning of certificates will trigger the device to start operational advertising. @@ -123,7 +181,6 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStag // Currently unimplemented. case CommissioningStage::kConfigACL: - case CommissioningStage::kCheckCertificates: return CommissioningStage::kError; // Neither of these have a next stage so return kError; case CommissioningStage::kCleanup: @@ -154,14 +211,91 @@ Optional AutoCommissioner::GetCommandTimeout(Commissioni } } -void AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, CommissioningDelegate::CommissioningReport report) +CHIP_ERROR AutoCommissioner::NOCChainGenerated(ByteSpan noc, ByteSpan icac, ByteSpan rcac) +{ + // Reuse ICA Cert buffer for temporary store Root Cert. + MutableByteSpan rootCert = MutableByteSpan(mICACertBuffer); + ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(rcac, rootCert)); + mParams.SetRootCert(rootCert); + + MutableByteSpan noCert = MutableByteSpan(mNOCertBuffer); + ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(noc, noCert)); + mParams.SetNoc(noCert); + + CommissioningStage nextStage = CommissioningStage::kSendTrustedRootCert; + mCommissioner->PerformCommissioningStep(mCommissioneeDeviceProxy, nextStage, mParams, this, 0, GetCommandTimeout(nextStage)); + + // Trusted root cert has been sent, so we can re-use the icac buffer for the icac. + if (!icac.empty()) + { + MutableByteSpan icaCert = MutableByteSpan(mICACertBuffer); + ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(icac, icaCert)); + mParams.SetIcac(icaCert); + } + else + { + mParams.SetIcac(ByteSpan()); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, CommissioningDelegate::CommissioningReport report) { - if (report.stageCompleted == CommissioningStage::kFindOperational) + if (err != CHIP_NO_ERROR) { - mOperationalDeviceProxy = report.OperationalNodeFoundData.operationalProxy; + ChipLogError(Controller, "Failed to perform commissioning step %d", static_cast(report.stageCompleted)); + return err; } + switch (report.stageCompleted) + { + case CommissioningStage::kSendPAICertificateRequest: + SetPAI(report.Get().certificate); + break; + case CommissioningStage::kSendDACCertificateRequest: + SetDAC(report.Get().certificate); + break; + case CommissioningStage::kSendAttestationRequest: + // These don't need to be deep copied to local memory because they are used in this one step then never again. + mParams.SetAttestationElements(report.Get().attestationElements) + .SetAttestationSignature(report.Get().signature); + // TODO: Does this need to be done at runtime? Seems like this could be done earlier and we woouldn't need to hold a + // reference to the operational credential delegate here + if (mOperationalCredentialsDelegate != nullptr) + { + MutableByteSpan nonce(mCSRNonce); + ReturnErrorOnFailure(mOperationalCredentialsDelegate->ObtainCsrNonce(nonce)); + mParams.SetCSRNonce(ByteSpan(mCSRNonce, sizeof(mCSRNonce))); + } + break; + case CommissioningStage::kSendOpCertSigningRequest: { + NOCChainGenerationParameters nocParams; + nocParams.nocsrElements = report.Get().attestationElements; + nocParams.signature = report.Get().signature; + mParams.SetNOCChainGenerationParameters(nocParams); + } + break; + case CommissioningStage::kGenerateNOCChain: + // For NOC chain generation, we re-use the buffers. NOCChainGenerated triggers the next stage before + // storing the returned certs, so just return here without triggering the next stage. + return NOCChainGenerated(report.Get().noc, report.Get().icac, report.Get().rcac); + case CommissioningStage::kFindOperational: + mOperationalDeviceProxy = report.Get().operationalProxy; + break; + case CommissioningStage::kCleanup: + ReleasePAI(); + ReleaseDAC(); + mCommissioneeDeviceProxy = nullptr; + mOperationalDeviceProxy = nullptr; + mParams = CommissioningParameters(); + return CHIP_NO_ERROR; + default: + break; + } + CommissioningStage nextStage = GetNextCommissioningStage(report.stageCompleted, err); - DeviceProxy * proxy = mCommissioneeDeviceProxy; + + DeviceProxy * proxy = mCommissioneeDeviceProxy; if (nextStage == CommissioningStage::kSendComplete || (nextStage == CommissioningStage::kCleanup && mOperationalDeviceProxy != nullptr)) { @@ -171,12 +305,87 @@ void AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, CommissioningDe if (proxy == nullptr) { ChipLogError(Controller, "Invalid device for commissioning"); - return; + return CHIP_ERROR_INCORRECT_STATE; } mParams.SetCompletionStatus(err); // TODO: Get real endpoint mCommissioner->PerformCommissioningStep(proxy, nextStage, mParams, this, 0, GetCommandTimeout(nextStage)); + return CHIP_NO_ERROR; +} + +void AutoCommissioner::ReleaseDAC() +{ + if (mDAC != nullptr) + { + Platform::MemoryFree(mDAC); + } + mDACLen = 0; + mDAC = nullptr; +} + +CHIP_ERROR AutoCommissioner::SetDAC(const ByteSpan & dac) +{ + if (dac.size() == 0) + { + ReleaseDAC(); + return CHIP_NO_ERROR; + } + + VerifyOrReturnError(dac.size() <= Credentials::kMaxDERCertLength, CHIP_ERROR_INVALID_ARGUMENT); + if (mDACLen != 0) + { + ReleaseDAC(); + } + + VerifyOrReturnError(CanCastTo(dac.size()), CHIP_ERROR_INVALID_ARGUMENT); + if (mDAC == nullptr) + { + mDAC = static_cast(chip::Platform::MemoryAlloc(dac.size())); + } + VerifyOrReturnError(mDAC != nullptr, CHIP_ERROR_NO_MEMORY); + mDACLen = static_cast(dac.size()); + memcpy(mDAC, dac.data(), mDACLen); + mParams.SetDAC(ByteSpan(mDAC, mDACLen)); + + return CHIP_NO_ERROR; +} + +void AutoCommissioner::ReleasePAI() +{ + if (mPAI != nullptr) + { + chip::Platform::MemoryFree(mPAI); + } + mPAILen = 0; + mPAI = nullptr; +} + +CHIP_ERROR AutoCommissioner::SetPAI(const chip::ByteSpan & pai) +{ + if (pai.size() == 0) + { + ReleasePAI(); + return CHIP_NO_ERROR; + } + + VerifyOrReturnError(pai.size() <= Credentials::kMaxDERCertLength, CHIP_ERROR_INVALID_ARGUMENT); + if (mPAILen != 0) + { + ReleasePAI(); + } + + VerifyOrReturnError(CanCastTo(pai.size()), CHIP_ERROR_INVALID_ARGUMENT); + if (mPAI == nullptr) + { + mPAI = static_cast(chip::Platform::MemoryAlloc(pai.size())); + } + VerifyOrReturnError(mPAI != nullptr, CHIP_ERROR_NO_MEMORY); + mPAILen = static_cast(pai.size()); + memcpy(mPAI, pai.data(), mPAILen); + mParams.SetPAI(ByteSpan(mPAI, mPAILen)); + + return CHIP_NO_ERROR; } } // namespace Controller diff --git a/src/controller/AutoCommissioner.h b/src/controller/AutoCommissioner.h index 401870eec23e92..0a4f228e2da9c7 100644 --- a/src/controller/AutoCommissioner.h +++ b/src/controller/AutoCommissioner.h @@ -29,24 +29,49 @@ class AutoCommissioner : public CommissioningDelegate { public: AutoCommissioner(DeviceCommissioner * commissioner) : mCommissioner(commissioner) {} + ~AutoCommissioner(); CHIP_ERROR SetCommissioningParameters(const CommissioningParameters & params); + void SetOperationalCredentialsDelegate(OperationalCredentialsDelegate * operationalCredentialsDelegate); + void StartCommissioning(CommissioneeDeviceProxy * proxy); - void CommissioningStepFinished(CHIP_ERROR err, CommissioningDelegate::CommissioningReport report) override; + // Delegate functions + CHIP_ERROR CommissioningStepFinished(CHIP_ERROR err, CommissioningDelegate::CommissioningReport report) override; private: CommissioningStage GetNextCommissioningStage(CommissioningStage currentStage, CHIP_ERROR lastErr); + void ReleaseDAC(); + void ReleasePAI(); + + CHIP_ERROR SetDAC(const ByteSpan & dac); + CHIP_ERROR SetPAI(const ByteSpan & pai); + + ByteSpan GetDAC() const { return ByteSpan(mDAC, mDACLen); } + ByteSpan GetPAI() const { return ByteSpan(mPAI, mPAILen); } + + CHIP_ERROR NOCChainGenerated(ByteSpan noc, ByteSpan icac, ByteSpan rcac); Optional GetCommandTimeout(CommissioningStage stage); DeviceCommissioner * mCommissioner; - CommissioneeDeviceProxy * mCommissioneeDeviceProxy = nullptr; - OperationalDeviceProxy * mOperationalDeviceProxy = nullptr; - CommissioningParameters mParams = CommissioningParameters(); + CommissioneeDeviceProxy * mCommissioneeDeviceProxy = nullptr; + OperationalDeviceProxy * mOperationalDeviceProxy = nullptr; + OperationalCredentialsDelegate * mOperationalCredentialsDelegate = nullptr; + CommissioningParameters mParams = CommissioningParameters(); // Memory space for the commisisoning parameters that come in as ByteSpans - the caller is not guaranteed to retain this memory // TODO(cecille): Include memory from CommissioneeDeviceProxy once BLE is moved over uint8_t mSsid[CommissioningParameters::kMaxSsidLen]; uint8_t mCredentials[CommissioningParameters::kMaxCredentialsLen]; uint8_t mThreadOperationalDataset[CommissioningParameters::kMaxThreadDatasetLen]; + + // TODO: Why were the nonces statically allocated, but the certs dynamically allocated? + uint8_t * mDAC = nullptr; + uint16_t mDACLen = 0; + uint8_t * mPAI = nullptr; + uint16_t mPAILen = 0; + uint8_t mAttestationNonce[kAttestationNonceLength]; + uint8_t mCSRNonce[kOpCSRNonceLength]; + uint8_t mNOCertBuffer[Credentials::kMaxCHIPCertLength]; + uint8_t mICACertBuffer[Credentials::kMaxCHIPCertLength]; }; } // namespace Controller diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index 990154d629fb76..5a7ac7bcbfbdf3 100644 --- a/src/controller/CHIPDeviceController.cpp +++ b/src/controller/CHIPDeviceController.cpp @@ -892,36 +892,13 @@ CHIP_ERROR DeviceCommissioner::Commission(NodeId remoteDeviceId, CommissioningPa ChipLogError(Controller, "Network commissioning parameters are required for BLE auto commissioning."); return CHIP_ERROR_INVALID_ARGUMENT; } - // If the CSRNonce is passed in, using that else using a random one.. - // TODO(cecille): Once the commissioning stages are separated, this can be removed from the device and moved down into the - // approprirate commissioning step. - if (params.HasCSRNonce()) - { - ReturnErrorOnFailure(device->SetCSRNonce(params.GetCSRNonce().Value())); - } - else - { - uint8_t mCSRNonce[kOpCSRNonceLength]; - Crypto::DRBG_get_bytes(mCSRNonce, sizeof(mCSRNonce)); - ReturnErrorOnFailure(device->SetCSRNonce(ByteSpan(mCSRNonce))); - } - - // If the AttestationNonce is passed in, using that else using a random one.. - if (params.HasAttestationNonce()) - { - ReturnErrorOnFailure(device->SetAttestationNonce(params.GetAttestationNonce().Value())); - } - else - { - uint8_t mAttestationNonce[kAttestationNonceLength]; - Crypto::DRBG_get_bytes(mAttestationNonce, sizeof(mAttestationNonce)); - ReturnErrorOnFailure(device->SetAttestationNonce(ByteSpan(mAttestationNonce))); - } + ChipLogProgress(Controller, "Commission called for node ID 0x" ChipLogFormatX64, ChipLogValueX64(remoteDeviceId)); mSystemState->SystemLayer()->StartTimer(chip::System::Clock::Milliseconds32(kSessionEstablishmentTimeout), OnSessionEstablishmentTimeoutCallback, this); - mAutoCommissioner.SetCommissioningParameters(params); + mAutoCommissioner.SetOperationalCredentialsDelegate(mOperationalCredentialsDelegate); + ReturnErrorOnFailure(mAutoCommissioner.SetCommissioningParameters(params)); if (device->IsSecureConnected()) { mAutoCommissioner.StartCommissioning(device); @@ -1021,7 +998,7 @@ void DeviceCommissioner::OnSessionEstablished() } } -CHIP_ERROR DeviceCommissioner::SendCertificateChainRequestCommand(CommissioneeDeviceProxy * device, +CHIP_ERROR DeviceCommissioner::SendCertificateChainRequestCommand(DeviceProxy * device, Credentials::CertificateType certificateType) { ChipLogDetail(Controller, "Sending Certificate Chain request to %p device", device); @@ -1029,13 +1006,10 @@ CHIP_ERROR DeviceCommissioner::SendCertificateChainRequestCommand(CommissioneeDe chip::Controller::OperationalCredentialsCluster cluster; cluster.Associate(device, 0); - mCertificateTypeBeingRequested = certificateType; - Callback::Cancelable * successCallback = mCertificateChainResponseCallback.Cancel(); Callback::Cancelable * failureCallback = mOnCertificateChainFailureCallback.Cancel(); ReturnErrorOnFailure(cluster.CertificateChainRequest(successCallback, failureCallback, certificateType)); - ChipLogDetail(Controller, "Sent Certificate Chain request, waiting for the DAC Certificate"); return CHIP_NO_ERROR; } @@ -1057,59 +1031,13 @@ void DeviceCommissioner::OnCertificateChainResponse(void * context, ByteSpan cer commissioner->mCertificateChainResponseCallback.Cancel(); commissioner->mOnCertificateChainFailureCallback.Cancel(); - if (commissioner->ProcessCertificateChain(certificate) != CHIP_NO_ERROR) - { - // Handle error, and notify session failure to the commissioner application. - ChipLogError(Controller, "Failed to process the certificate chain request"); - // TODO: Map error status to correct error code - commissioner->OnSessionEstablishmentError(CHIP_ERROR_INTERNAL); - } -} - -CHIP_ERROR DeviceCommissioner::ProcessCertificateChain(const ByteSpan & certificate) -{ - VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE); - VerifyOrReturnError(mDeviceBeingCommissioned != nullptr, CHIP_ERROR_INCORRECT_STATE); - - CommissioneeDeviceProxy * device = mDeviceBeingCommissioned; - - // PAI is being requested first - If PAI is not present, DAC will be requested next anyway. - switch (mCertificateTypeBeingRequested) - { - case CertificateType::kDAC: { - device->SetDAC(certificate); - break; - } - case CertificateType::kPAI: { - device->SetPAI(certificate); - break; - } - case CertificateType::kUnknown: - default: { - return CHIP_ERROR_INTERNAL; - } - } - - if (device->AreCredentialsAvailable()) - { - ChipLogProgress(Controller, "Sending Attestation Request to the device."); - ReturnErrorOnFailure(SendAttestationRequestCommand(device, device->GetAttestationNonce())); - } - else - { - CHIP_ERROR err = SendCertificateChainRequestCommand(device, CertificateType::kDAC); - if (err != CHIP_NO_ERROR) - { - ChipLogError(Controller, "Failed in sending Certificate Chain request command to the device: err %s", ErrorStr(err)); - OnSessionEstablishmentError(err); - return err; - } - } + CommissioningDelegate::CommissioningReport report(commissioner->mCommissioningStage); + report.Set(RequestedCertificate(certificate)); - return CHIP_NO_ERROR; + commissioner->mCommissioningDelegate->CommissioningStepFinished(CHIP_NO_ERROR, report); } -CHIP_ERROR DeviceCommissioner::SendAttestationRequestCommand(CommissioneeDeviceProxy * device, const ByteSpan & attestationNonce) +CHIP_ERROR DeviceCommissioner::SendAttestationRequestCommand(DeviceProxy * device, const ByteSpan & attestationNonce) { ChipLogDetail(Controller, "Sending Attestation request to %p device", device); VerifyOrReturnError(device != nullptr, CHIP_ERROR_INVALID_ARGUMENT); @@ -1142,13 +1070,14 @@ void DeviceCommissioner::OnAttestationResponse(void * context, chip::ByteSpan at commissioner->mAttestationResponseCallback.Cancel(); commissioner->mOnAttestationFailureCallback.Cancel(); - commissioner->ValidateAttestationInfo(attestationElements, signature); + CommissioningDelegate::CommissioningReport report(CommissioningStage::kSendAttestationRequest); + report.Set(AttestationResponse(attestationElements, signature)); + commissioner->mCommissioningDelegate->CommissioningStepFinished(CHIP_NO_ERROR, report); } void DeviceCommissioner::OnDeviceAttestationInformationVerification(void * context, AttestationVerificationResult result) { DeviceCommissioner * commissioner = reinterpret_cast(context); - CHIP_ERROR error = CHIP_NO_ERROR; if (result != AttestationVerificationResult::kSuccess) { @@ -1157,7 +1086,8 @@ void DeviceCommissioner::OnDeviceAttestationInformationVerification(void * conte ChipLogError(Controller, "Failed in verifying 'Attestation Information' command received from the device due to default " "DeviceAttestationVerifier Class not being overridden by a real implementation."); - SuccessOrExit(error = CHIP_ERROR_NOT_IMPLEMENTED); + commissioner->CommissioningStageComplete(CHIP_ERROR_NOT_IMPLEMENTED); + return; } else { @@ -1167,30 +1097,29 @@ void DeviceCommissioner::OnDeviceAttestationInformationVerification(void * conte static_cast(result)); // Go look at AttestationVerificationResult enum in src/credentials/DeviceAttestationVerifier.h to understand the // errors. - SuccessOrExit(error = CHIP_ERROR_INTERNAL); + commissioner->CommissioningStageComplete(CHIP_ERROR_INTERNAL); + return; } } ChipLogProgress(Controller, "Successfully validated 'Attestation Information' command received from the device."); - -exit: - commissioner->HandleAttestationResult(error); + commissioner->CommissioningStageComplete(CHIP_NO_ERROR); } -CHIP_ERROR DeviceCommissioner::ValidateAttestationInfo(const ByteSpan & attestationElements, const ByteSpan & signature) +CHIP_ERROR DeviceCommissioner::ValidateAttestationInfo(const ByteSpan & attestationElements, const ByteSpan & signature, + const ByteSpan & attestationNonce, const ByteSpan & pai, + const ByteSpan & dac, DeviceProxy * proxy) { VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE); - VerifyOrReturnError(mDeviceBeingCommissioned != nullptr, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(proxy != nullptr, CHIP_ERROR_INCORRECT_STATE); DeviceAttestationVerifier * dac_verifier = GetDeviceAttestationVerifier(); // Retrieve attestation challenge ByteSpan attestationChallenge = - mDeviceBeingCommissioned->GetSecureSession().Value()->AsSecureSession()->GetCryptoContext().GetAttestationChallenge(); + proxy->GetSecureSession().Value()->AsSecureSession()->GetCryptoContext().GetAttestationChallenge(); - dac_verifier->VerifyAttestationInformation(attestationElements, attestationChallenge, signature, - mDeviceBeingCommissioned->GetPAI(), mDeviceBeingCommissioned->GetDAC(), - mDeviceBeingCommissioned->GetAttestationNonce(), + dac_verifier->VerifyAttestationInformation(attestationElements, attestationChallenge, signature, pai, dac, attestationNonce, &mDeviceAttestationInformationVerificationCallback); // TODO: Validate Firmware Information @@ -1198,32 +1127,7 @@ CHIP_ERROR DeviceCommissioner::ValidateAttestationInfo(const ByteSpan & attestat return CHIP_NO_ERROR; } -void DeviceCommissioner::HandleAttestationResult(CHIP_ERROR err) -{ - if (err != CHIP_NO_ERROR) - { - // Here we assume the Attestation Information validation always succeeds. - // Spec mandates that commissioning shall continue despite attestation fails (in some cases). - // TODO: Handle failure scenarios where commissioning may progress regardless. - ChipLogError(Controller, "Failed to validate the Attestation Information"); - } - - VerifyOrReturn(mState == State::Initialized); - VerifyOrReturn(mDeviceBeingCommissioned != nullptr); - - CommissioneeDeviceProxy * device = mDeviceBeingCommissioned; - - ChipLogProgress(Controller, "Sending 'CSR request' command to the device."); - CHIP_ERROR error = SendOperationalCertificateSigningRequestCommand(device); - if (error != CHIP_NO_ERROR) - { - ChipLogError(Controller, "Failed in sending 'CSR request' command to the device: err %s", ErrorStr(error)); - OnSessionEstablishmentError(error); - return; - } -} - -CHIP_ERROR DeviceCommissioner::SendOperationalCertificateSigningRequestCommand(CommissioneeDeviceProxy * device) +CHIP_ERROR DeviceCommissioner::SendOperationalCertificateSigningRequestCommand(DeviceProxy * device, const ByteSpan & csrNonce) { ChipLogDetail(Controller, "Sending OpCSR request to %p device", device); VerifyOrReturnError(device != nullptr, CHIP_ERROR_INVALID_ARGUMENT); @@ -1232,13 +1136,7 @@ CHIP_ERROR DeviceCommissioner::SendOperationalCertificateSigningRequestCommand(C Callback::Cancelable * successCallback = mOpCSRResponseCallback.Cancel(); Callback::Cancelable * failureCallback = mOnCSRFailureCallback.Cancel(); - - uint8_t csrNonceBuf[kOpCSRNonceLength]; - MutableByteSpan csrNonce(csrNonceBuf); - ReturnErrorOnFailure(mOperationalCredentialsDelegate->ObtainCsrNonce(csrNonce)); - ReturnErrorOnFailure(device->SetCSRNonce(csrNonce)); - - ReturnErrorOnFailure(cluster.OpCSRRequest(successCallback, failureCallback, device->GetCSRNonce())); + ReturnErrorOnFailure(cluster.OpCSRRequest(successCallback, failureCallback, csrNonce)); ChipLogDetail(Controller, "Sent OpCSR request, waiting for the CSR"); return CHIP_NO_ERROR; @@ -1263,72 +1161,26 @@ void DeviceCommissioner::OnOperationalCertificateSigningRequest(void * context, commissioner->mOpCSRResponseCallback.Cancel(); commissioner->mOnCSRFailureCallback.Cancel(); - if (commissioner->ProcessOpCSR(NOCSRElements, AttestationSignature) != CHIP_NO_ERROR) - { - // Handle error, and notify session failure to the commissioner application. - ChipLogError(Controller, "Failed to process the certificate signing request"); - // TODO: Map error status to correct error code - commissioner->OnSessionEstablishmentError(CHIP_ERROR_INTERNAL); - } + CommissioningDelegate::CommissioningReport report(CommissioningStage::kSendOpCertSigningRequest); + report.Set(AttestationResponse(NOCSRElements, AttestationSignature)); + commissioner->mCommissioningDelegate->CommissioningStepFinished(CHIP_NO_ERROR, report); } void DeviceCommissioner::OnDeviceNOCChainGeneration(void * context, CHIP_ERROR status, const ByteSpan & noc, const ByteSpan & icac, const ByteSpan & rcac) { CHIP_ERROR err = CHIP_NO_ERROR; - Crypto::P256PublicKey rootPubKey; DeviceCommissioner * commissioner = static_cast(context); + CommissioningDelegate::CommissioningReport report(CommissioningStage::kGenerateNOCChain); ChipLogProgress(Controller, "Received callback from the CA for NOC Chain generation. Status %s", ErrorStr(status)); - CommissioneeDeviceProxy * device = nullptr; VerifyOrExit(commissioner->mState == State::Initialized, err = CHIP_ERROR_INCORRECT_STATE); - VerifyOrExit(commissioner->mDeviceBeingCommissioned != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - - // Check if the callback returned a failure - VerifyOrExit(status == CHIP_NO_ERROR, err = status); // TODO - Verify that the generated root cert matches with commissioner's root cert - device = commissioner->mDeviceBeingCommissioned; - - { - Credentials::P256PublicKeySpan rootPubKeySpan; - // Reuse NOC Cert buffer for temporary store Root Cert. - MutableByteSpan rootCert = device->GetMutableNOCCert(); - - err = ConvertX509CertToChipCert(rcac, rootCert); - SuccessOrExit(err); - - SuccessOrExit(err = commissioner->SendTrustedRootCertificate(device, rootCert)); - SuccessOrExit(err = Credentials::ExtractPublicKeyFromChipCert(rootCert, rootPubKeySpan)); - rootPubKey = Crypto::P256PublicKey(rootPubKeySpan); // deep copy - } - - if (!icac.empty()) - { - MutableByteSpan icaCert = device->GetMutableICACert(); - - err = ConvertX509CertToChipCert(icac, icaCert); - SuccessOrExit(err); - - err = device->SetICACertBufferSize(icaCert.size()); - SuccessOrExit(err); - } - - { - MutableByteSpan nocCert = device->GetMutableNOCCert(); - - err = ConvertX509CertToChipCert(noc, nocCert); - SuccessOrExit(err); - - err = device->SetNOCCertBufferSize(nocCert.size()); - SuccessOrExit(err); - } - - SuccessOrExit(err = device->SetPeerId(rootPubKey, device->GetNOCCert())); - VerifyOrExit(IsOperationalNodeId(device->GetDeviceId()), err = CHIP_ERROR_INVALID_ARGUMENT); - + report.Set(NocChain(noc, icac, rcac)); + err = commissioner->mCommissioningDelegate->CommissioningStepFinished(CHIP_NO_ERROR, report); exit: if (err != CHIP_NO_ERROR) { @@ -1337,38 +1189,36 @@ void DeviceCommissioner::OnDeviceNOCChainGeneration(void * context, CHIP_ERROR s } } -CHIP_ERROR DeviceCommissioner::ProcessOpCSR(const ByteSpan & NOCSRElements, const ByteSpan & AttestationSignature) +CHIP_ERROR DeviceCommissioner::ProcessOpCSR(DeviceProxy * proxy, const ByteSpan & NOCSRElements, + const ByteSpan & AttestationSignature, ByteSpan dac, ByteSpan csrNonce) { VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE); - VerifyOrReturnError(mDeviceBeingCommissioned != nullptr, CHIP_ERROR_INCORRECT_STATE); - - CommissioneeDeviceProxy * device = mDeviceBeingCommissioned; ChipLogProgress(Controller, "Getting certificate chain for the device from the issuer"); DeviceAttestationVerifier * dacVerifier = GetDeviceAttestationVerifier(); P256PublicKey dacPubkey; - ReturnErrorOnFailure(ExtractPubkeyFromX509Cert(device->GetDAC(), dacPubkey)); + ReturnErrorOnFailure(ExtractPubkeyFromX509Cert(dac, dacPubkey)); // Retrieve attestation challenge ByteSpan attestationChallenge = - device->GetSecureSession().Value()->AsSecureSession()->GetCryptoContext().GetAttestationChallenge(); + proxy->GetSecureSession().Value()->AsSecureSession()->GetCryptoContext().GetAttestationChallenge(); // The operational CA should also verify this on its end during NOC generation, if end-to-end attestation is desired. ReturnErrorOnFailure(dacVerifier->VerifyNodeOperationalCSRInformation(NOCSRElements, attestationChallenge, AttestationSignature, - dacPubkey, device->GetCSRNonce())); + dacPubkey, csrNonce)); - mOperationalCredentialsDelegate->SetNodeIdForNextNOCRequest(device->GetDeviceId()); + mOperationalCredentialsDelegate->SetNodeIdForNextNOCRequest(proxy->GetDeviceId()); FabricInfo * fabric = mSystemState->Fabrics()->FindFabricWithIndex(mFabricIndex); mOperationalCredentialsDelegate->SetFabricIdForNextNOCRequest(fabric->GetFabricId()); - return mOperationalCredentialsDelegate->GenerateNOCChain(NOCSRElements, AttestationSignature, device->GetDAC(), ByteSpan(), - ByteSpan(), &mDeviceNOCChainCallback); + return mOperationalCredentialsDelegate->GenerateNOCChain(NOCSRElements, AttestationSignature, dac, ByteSpan(), ByteSpan(), + &mDeviceNOCChainCallback); } -CHIP_ERROR DeviceCommissioner::SendOperationalCertificate(CommissioneeDeviceProxy * device, const ByteSpan & nocCertBuf, +CHIP_ERROR DeviceCommissioner::SendOperationalCertificate(DeviceProxy * device, const ByteSpan & nocCertBuf, const ByteSpan & icaCertBuf) { VerifyOrReturnError(device != nullptr, CHIP_ERROR_INVALID_ARGUMENT); @@ -1454,7 +1304,7 @@ void DeviceCommissioner::OnOperationalCertificateAddResponse(void * context, uin } } -CHIP_ERROR DeviceCommissioner::SendTrustedRootCertificate(CommissioneeDeviceProxy * device, const ByteSpan & rcac) +CHIP_ERROR DeviceCommissioner::SendTrustedRootCertificate(DeviceProxy * device, const ByteSpan & rcac) { VerifyOrReturnError(device != nullptr, CHIP_ERROR_INVALID_ARGUMENT); @@ -1478,27 +1328,11 @@ void DeviceCommissioner::OnRootCertSuccessResponse(void * context) ChipLogProgress(Controller, "Device confirmed that it has received the root certificate"); DeviceCommissioner * commissioner = static_cast(context); - CHIP_ERROR err = CHIP_NO_ERROR; - CommissioneeDeviceProxy * device = nullptr; - - VerifyOrExit(commissioner->mState == State::Initialized, err = CHIP_ERROR_INCORRECT_STATE); - commissioner->mRootCertResponseCallback.Cancel(); commissioner->mOnRootCertFailureCallback.Cancel(); - VerifyOrExit(commissioner->mDeviceBeingCommissioned != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - - device = commissioner->mDeviceBeingCommissioned; - - ChipLogProgress(Controller, "Sending operational certificate chain to the device"); - err = commissioner->SendOperationalCertificate(device, device->GetNOCCert(), device->GetICACert()); - SuccessOrExit(err); - -exit: - if (err != CHIP_NO_ERROR) - { - commissioner->OnSessionEstablishmentError(err); - } + CommissioningDelegate::CommissioningReport report(CommissioningStage::kSendTrustedRootCert); + commissioner->mCommissioningDelegate->CommissioningStepFinished(CHIP_NO_ERROR, report); } void DeviceCommissioner::OnRootCertFailureResponse(void * context, uint8_t status) @@ -1640,9 +1474,12 @@ void DeviceCommissioner::CommissioningStageComplete(CHIP_ERROR err) { return; } - CommissioningDelegate::CommissioningReport report; - report.stageCompleted = mCommissioningStage; - mCommissioningDelegate->CommissioningStepFinished(err, report); + CommissioningDelegate::CommissioningReport report(mCommissioningStage); + CHIP_ERROR status = mCommissioningDelegate->CommissioningStepFinished(err, report); + if (status != CHIP_NO_ERROR) + { + OnSessionEstablishmentError(status); + } } #if CHIP_DEVICE_CONFIG_ENABLE_DNSSD @@ -1690,9 +1527,8 @@ void DeviceCommissioner::OnDeviceConnectedFn(void * context, OperationalDevicePr { if (commissioner->mCommissioningDelegate != nullptr) { - CommissioningDelegate::CommissioningReport report; - report.stageCompleted = CommissioningStage::kFindOperational; - report.OperationalNodeFoundData.operationalProxy = device; + CommissioningDelegate::CommissioningReport report(CommissioningStage::kFindOperational); + report.Set(OperationalNodeFoundData(device)); commissioner->mCommissioningDelegate->CommissioningStepFinished(CHIP_NO_ERROR, report); } } @@ -1791,48 +1627,119 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio kCommandTimeoutMs); } break; - case CommissioningStage::kDeviceAttestation: { - ChipLogProgress(Controller, "Exchanging vendor certificates"); - // TODO(cecille): Remove the certificates from the CommissioneeDeviceProxy and take from the commissioning parameters. - CHIP_ERROR status = - SendCertificateChainRequestCommand(reinterpret_cast(proxy), CertificateType::kPAI); - if (status != CHIP_NO_ERROR) + case CommissioningStage::kSendPAICertificateRequest: + ChipLogProgress(Controller, "Sending request for PAI certificate"); + SendCertificateChainRequestCommand(proxy, CertificateType::kPAI); + break; + case CommissioningStage::kSendDACCertificateRequest: + ChipLogProgress(Controller, "Sending request for DAC certificate"); + SendCertificateChainRequestCommand(proxy, CertificateType::kDAC); + break; + case CommissioningStage::kSendAttestationRequest: + ChipLogProgress(Controller, "Sending Attestation Request to the device."); + if (!params.HasAttestationNonce()) { - ChipLogError(Controller, "Failed in sending 'Certificate Chain Request' command to the device: err %s", - ErrorStr(status)); - OnSessionEstablishmentError(status); + ChipLogError(Controller, "No attestation nonce found"); + CommissioningDelegate::CommissioningReport report(step); + mCommissioningDelegate->CommissioningStepFinished(CHIP_ERROR_INVALID_ARGUMENT, report); + return; + } + SendAttestationRequestCommand(proxy, params.GetAttestationNonce().Value()); + break; + case CommissioningStage::kAttestationVerification: + ChipLogProgress(Controller, "Verifying attestation"); + if (!params.HasAttestationElements() || !params.HasAttestationSignature() || !params.HasAttestationNonce() || + !params.HasDAC() || !params.HasPAI()) + { + ChipLogError(Controller, "Missing attestation information"); + CommissioningStageComplete(CHIP_ERROR_INVALID_ARGUMENT); + return; + } + if (ValidateAttestationInfo(params.GetAttestationElements().Value(), params.GetAttestationSignature().Value(), + params.GetAttestationNonce().Value(), params.GetPAI().Value(), params.GetDAC().Value(), + proxy) != CHIP_NO_ERROR) + { + ChipLogError(Controller, "Error validating attestation information"); + CommissioningStageComplete(CHIP_ERROR_INVALID_ARGUMENT); + return; + } + break; + case CommissioningStage::kSendOpCertSigningRequest: + if (!params.HasCSRNonce()) + { + ChipLogError(Controller, "No CSR nonce found"); + CommissioningStageComplete(CHIP_ERROR_INVALID_ARGUMENT); + return; + } + SendOperationalCertificateSigningRequestCommand(proxy, params.GetCSRNonce().Value()); + break; + case CommissioningStage::kGenerateNOCChain: { + if (!params.HasNOCChainGenerationaParameters() || !params.HasDAC() || !params.HasCSRNonce()) + { + ChipLogError(Controller, "Unable to generate NOC chain parameters"); + return CommissioningStageComplete(CHIP_ERROR_INVALID_ARGUMENT); + } + CHIP_ERROR err = ProcessOpCSR(proxy, params.GetNOCChainGenerationParameters().Value().nocsrElements, + params.GetNOCChainGenerationParameters().Value().signature, params.GetDAC().Value(), + params.GetCSRNonce().Value()); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Controller, "Unable to process Op CSR"); + // Handle error, and notify session failure to the commissioner application. + ChipLogError(Controller, "Failed to process the certificate signing request"); + // TODO: Map error status to correct error code + CommissioningStageComplete(err); return; } } break; - case CommissioningStage::kCheckCertificates: { - ChipLogProgress(Controller, "Exchanging certificates"); - // TODO(cecille): Once this is implemented through the clusters, it should be moved to the proper stage and the callback - // should advance the commissioning stage - // TODO(cecille): The pointer re-interpret here is ugly. Once these steps are moved into the commissioning state machine, - // the commissioning state machine can hold the parameters instead of the CommissioneeDeviceProxy and we can use the base - // class here. - CHIP_ERROR status = SendOperationalCertificateSigningRequestCommand(reinterpret_cast(proxy)); - if (status != CHIP_NO_ERROR) + case CommissioningStage::kSendTrustedRootCert: { + if (!params.HasRootCert() || !params.HasNoc()) + { + ChipLogError(Controller, "No trusted root cert or NOC specified"); + CommissioningStageComplete(CHIP_ERROR_INVALID_ARGUMENT); + return; + } + CHIP_ERROR err = SendTrustedRootCertificate(proxy, params.GetRootCert().Value()); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Controller, "Error sending trusted root certificate: %s", err.AsString()); + CommissioningStageComplete(err); + return; + } + err = proxy->SetPeerId(params.GetRootCert().Value(), params.GetNoc().Value()); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Controller, "Error setting peer id: %s", err.AsString()); + CommissioningStageComplete(err); + return; + } + if (!IsOperationalNodeId(proxy->GetDeviceId())) { - ChipLogError(Controller, "Failed in sending 'CSR Request' command to the device: err %s", ErrorStr(status)); - OnSessionEstablishmentError(status); + ChipLogError(Controller, "Given node ID is not an operational node ID"); + CommissioningStageComplete(CHIP_ERROR_INVALID_ARGUMENT); return; } } break; - // TODO: Right now, these stages are not implemented as a separate stage because they are no-ops. - // Once these are implemented through the clusters, these should be moved into their separate stages and the callbacks - // should advance the commissioning stage. + case CommissioningStage::kSendNOC: + if (!params.HasNoc() || !params.HasIcac()) + { + ChipLogError(Controller, "No node operational certs specified"); + CommissioningStageComplete(CHIP_ERROR_INVALID_ARGUMENT); + return; + } + ChipLogProgress(Controller, "Sending operational certificate chain to the device"); + SendOperationalCertificate(proxy, params.GetNoc().Value(), params.GetIcac().Value()); + break; case CommissioningStage::kConfigACL: // TODO: Implement break; case CommissioningStage::kWiFiNetworkSetup: { if (!params.HasWiFiCredentials()) { - CommissioningDelegate::CommissioningReport report; - report.stageCompleted = step; - mCommissioningDelegate->CommissioningStepFinished(CHIP_ERROR_INVALID_ARGUMENT, report); + ChipLogError(Controller, "No wifi credentials specified"); + CommissioningStageComplete(CHIP_ERROR_INVALID_ARGUMENT); return; } ChipLogProgress(Controller, "Adding wifi network"); @@ -1845,9 +1752,8 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio case CommissioningStage::kThreadNetworkSetup: { if (!params.HasThreadOperationalDataset()) { - CommissioningDelegate::CommissioningReport report; - report.stageCompleted = step; - mCommissioningDelegate->CommissioningStepFinished(CHIP_ERROR_INVALID_ARGUMENT, report); + ChipLogError(Controller, "No thread credentials specified"); + CommissioningStageComplete(CHIP_ERROR_INVALID_ARGUMENT); return; } ChipLogProgress(Controller, "Adding thread network"); @@ -1860,9 +1766,8 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio case CommissioningStage::kWiFiNetworkEnable: { if (!params.HasWiFiCredentials()) { - CommissioningDelegate::CommissioningReport report; - report.stageCompleted = step; - mCommissioningDelegate->CommissioningStepFinished(CHIP_ERROR_INVALID_ARGUMENT, report); + ChipLogError(Controller, "No wifi credentials specified"); + CommissioningStageComplete(CHIP_ERROR_INVALID_ARGUMENT); return; } ChipLogProgress(Controller, "Enabling wifi network"); @@ -1879,9 +1784,7 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio operationalDataset.GetExtendedPanIdAsByteSpan(extendedPanId) != CHIP_NO_ERROR) { ChipLogError(Controller, "Unable to get extended pan ID for thread operational dataset\n"); - CommissioningDelegate::CommissioningReport report; - report.stageCompleted = step; - mCommissioningDelegate->CommissioningStepFinished(CHIP_ERROR_INVALID_ARGUMENT, report); + CommissioningStageComplete(CHIP_ERROR_INVALID_ARGUMENT); return; } ChipLogProgress(Controller, "Enabling thread network"); @@ -1907,6 +1810,7 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio { mPairingDelegate->OnCommissioningComplete(proxy->GetDeviceId(), params.GetCompletionStatus()); } + CommissioningStageComplete(CHIP_NO_ERROR); mCommissioningStage = CommissioningStage::kSecurePairing; break; case CommissioningStage::kError: diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h index e6b6013747747d..b06262a7feb6e5 100644 --- a/src/controller/CHIPDeviceController.h +++ b/src/controller/CHIPDeviceController.h @@ -564,6 +564,21 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, void PerformCommissioningStep(DeviceProxy * device, CommissioningStage step, CommissioningParameters & params, CommissioningDelegate * delegate, EndpointId endpoint, Optional timeout); + /** + * @brief + * This function validates the Attestation Information sent by the device. + * + * @param[in] attestationElements Attestation Elements TLV. + * @param[in] signature Attestation signature generated for all the above fields + Attestation Challenge. + * @param[in] attestationNonce Attestation nonce + * @param[in] pai PAI certificate + * @param[in] dac DAC certificates + * @param[in] proxy device proxy that is being attested. + */ + CHIP_ERROR ValidateAttestationInfo(const ByteSpan & attestationElements, const ByteSpan & signature, + const ByteSpan & attestationNonce, const ByteSpan & pai, const ByteSpan & dac, + DeviceProxy * proxy); + void CommissioningStageComplete(CHIP_ERROR err); #if CONFIG_NETWORK_LAYER_BLE @@ -653,8 +668,6 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, CommissioneeDeviceProxy * mDeviceBeingCommissioned = nullptr; - Credentials::CertificateType mCertificateTypeBeingRequested = Credentials::CertificateType::kUnknown; - /* TODO: BLE rendezvous and IP rendezvous should share the same procedure, so this is just a workaround-like flag and should be removed in the future. When using IP rendezvous, we need to disable network provisioning. In the future, network @@ -691,24 +704,23 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, /* This function sends a Device Attestation Certificate chain request to the device. The function does not hold a reference to the device object. */ - CHIP_ERROR SendCertificateChainRequestCommand(CommissioneeDeviceProxy * device, Credentials::CertificateType certificateType); + CHIP_ERROR SendCertificateChainRequestCommand(DeviceProxy * device, Credentials::CertificateType certificateType); /* This function sends an Attestation request to the device. The function does not hold a reference to the device object. */ - CHIP_ERROR SendAttestationRequestCommand(CommissioneeDeviceProxy * device, const ByteSpan & attestationNonce); + CHIP_ERROR SendAttestationRequestCommand(DeviceProxy * device, const ByteSpan & attestationNonce); /* This function sends an OpCSR request to the device. The function does not hold a reference to the device object. */ - CHIP_ERROR SendOperationalCertificateSigningRequestCommand(CommissioneeDeviceProxy * device); + CHIP_ERROR SendOperationalCertificateSigningRequestCommand(DeviceProxy * device, const ByteSpan & csrNonce); /* This function sends the operational credentials to the device. The function does not hold a reference to the device object. */ - CHIP_ERROR SendOperationalCertificate(CommissioneeDeviceProxy * device, const ByteSpan & nocCertBuf, - const ByteSpan & icaCertBuf); + CHIP_ERROR SendOperationalCertificate(DeviceProxy * device, const ByteSpan & nocCertBuf, const ByteSpan & icaCertBuf); /* This function sends the trusted root certificate to the device. The function does not hold a reference to the device object. */ - CHIP_ERROR SendTrustedRootCertificate(CommissioneeDeviceProxy * device, const ByteSpan & rcac); + CHIP_ERROR SendTrustedRootCertificate(DeviceProxy * device, const ByteSpan & rcac); /* This function is called by the commissioner code when the device completes the operational credential provisioning process. @@ -759,10 +771,14 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, * This function processes the CSR sent by the device. * (Reference: Specifications section 11.22.5.8. OpCSR Elements) * + * @param[in] proxy device proxy * @param[in] NOCSRElements CSR elements as per specifications section 11.22.5.6. NOCSR Elements. * @param[in] AttestationSignature Cryptographic signature generated for all the above fields. + * @param[in] dac device attestation certificate + * @param[in] csrNonce certificate signing request nonce */ - CHIP_ERROR ProcessOpCSR(const ByteSpan & NOCSRElements, const ByteSpan & AttestationSignature); + CHIP_ERROR ProcessOpCSR(DeviceProxy * proxy, const ByteSpan & NOCSRElements, const ByteSpan & AttestationSignature, + ByteSpan dac, ByteSpan csrNonce); /** * @brief @@ -770,15 +786,6 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, */ CHIP_ERROR ProcessCertificateChain(const ByteSpan & certificate); - /** - * @brief - * This function validates the Attestation Information sent by the device. - * - * @param[in] attestationElements Attestation Elements TLV. - * @param[in] signature Attestation signature generated for all the above fields + Attestation Challenge. - */ - CHIP_ERROR ValidateAttestationInfo(const ByteSpan & attestationElements, const ByteSpan & signature); - void HandleAttestationResult(CHIP_ERROR err); CommissioneeDeviceProxy * FindCommissioneeDevice(const SessionHandle & session); diff --git a/src/controller/CommissioneeDeviceProxy.cpp b/src/controller/CommissioneeDeviceProxy.cpp index 6deeb182f539e4..51ccf75c27b03e 100644 --- a/src/controller/CommissioneeDeviceProxy.cpp +++ b/src/controller/CommissioneeDeviceProxy.cpp @@ -159,9 +159,6 @@ void CommissioneeDeviceProxy::Reset() mBleLayer = nullptr; #endif mExchangeMgr = nullptr; - - ReleaseDAC(); - ReleasePAI(); } CHIP_ERROR CommissioneeDeviceProxy::LoadSecureSessionParameters() @@ -202,106 +199,15 @@ bool CommissioneeDeviceProxy::GetAddress(Inet::IPAddress & addr, uint16_t & port return true; } -CHIP_ERROR CommissioneeDeviceProxy::SetPeerId(const Crypto::P256PublicKey & rootPublicKey, ByteSpan noc) +CommissioneeDeviceProxy::~CommissioneeDeviceProxy() {} + +CHIP_ERROR CommissioneeDeviceProxy::SetPeerId(ByteSpan rcac, ByteSpan noc) { CompressedFabricId compressedFabricId; NodeId nodeId; - ReturnErrorOnFailure( - Credentials::ExtractNodeIdCompressedFabricIdFromRootPubKeyOpCert(rootPublicKey, noc, compressedFabricId, nodeId)); + ReturnErrorOnFailure(Credentials::ExtractNodeIdCompressedFabricIdFromOpCerts(rcac, noc, compressedFabricId, nodeId)); mPeerId = PeerId().SetCompressedFabricId(compressedFabricId).SetNodeId(nodeId); return CHIP_NO_ERROR; } -void CommissioneeDeviceProxy::ReleaseDAC() -{ - if (mDAC != nullptr) - { - Platform::MemoryFree(mDAC); - } - mDACLen = 0; - mDAC = nullptr; -} - -CHIP_ERROR CommissioneeDeviceProxy::SetDAC(const ByteSpan & dac) -{ - if (dac.size() == 0) - { - ReleaseDAC(); - return CHIP_NO_ERROR; - } - - VerifyOrReturnError(dac.size() <= Credentials::kMaxDERCertLength, CHIP_ERROR_INVALID_ARGUMENT); - if (mDACLen != 0) - { - ReleaseDAC(); - } - - VerifyOrReturnError(CanCastTo(dac.size()), CHIP_ERROR_INVALID_ARGUMENT); - if (mDAC == nullptr) - { - mDAC = static_cast(chip::Platform::MemoryAlloc(dac.size())); - } - VerifyOrReturnError(mDAC != nullptr, CHIP_ERROR_NO_MEMORY); - mDACLen = static_cast(dac.size()); - memcpy(mDAC, dac.data(), mDACLen); - - return CHIP_NO_ERROR; -} - -void CommissioneeDeviceProxy::ReleasePAI() -{ - if (mPAI != nullptr) - { - chip::Platform::MemoryFree(mPAI); - } - mPAILen = 0; - mPAI = nullptr; -} - -CHIP_ERROR CommissioneeDeviceProxy::SetPAI(const chip::ByteSpan & pai) -{ - if (pai.size() == 0) - { - ReleasePAI(); - return CHIP_NO_ERROR; - } - - VerifyOrReturnError(pai.size() <= Credentials::kMaxDERCertLength, CHIP_ERROR_INVALID_ARGUMENT); - if (mPAILen != 0) - { - ReleasePAI(); - } - - VerifyOrReturnError(CanCastTo(pai.size()), CHIP_ERROR_INVALID_ARGUMENT); - if (mPAI == nullptr) - { - mPAI = static_cast(chip::Platform::MemoryAlloc(pai.size())); - } - VerifyOrReturnError(mPAI != nullptr, CHIP_ERROR_NO_MEMORY); - mPAILen = static_cast(pai.size()); - memcpy(mPAI, pai.data(), mPAILen); - - return CHIP_NO_ERROR; -} - -CommissioneeDeviceProxy::~CommissioneeDeviceProxy() -{ - ReleaseDAC(); - ReleasePAI(); -} - -CHIP_ERROR CommissioneeDeviceProxy::SetNOCCertBufferSize(size_t new_size) -{ - ReturnErrorCodeIf(new_size > sizeof(mNOCCertBuffer), CHIP_ERROR_INVALID_ARGUMENT); - mNOCCertBufferSize = new_size; - return CHIP_NO_ERROR; -} - -CHIP_ERROR CommissioneeDeviceProxy::SetICACertBufferSize(size_t new_size) -{ - ReturnErrorCodeIf(new_size > sizeof(mICACertBuffer), CHIP_ERROR_INVALID_ARGUMENT); - mICACertBufferSize = new_size; - return CHIP_NO_ERROR; -} - } // namespace chip diff --git a/src/controller/CommissioneeDeviceProxy.h b/src/controller/CommissioneeDeviceProxy.h index 9f49f5f3fddf18..3f2af4154cd41c 100644 --- a/src/controller/CommissioneeDeviceProxy.h +++ b/src/controller/CommissioneeDeviceProxy.h @@ -212,7 +212,7 @@ class CommissioneeDeviceProxy : public DeviceProxy, public SessionReleaseDelegat NodeId GetDeviceId() const override { return mPeerId.GetNodeId(); } PeerId GetPeerId() const { return mPeerId; } - CHIP_ERROR SetPeerId(const Crypto::P256PublicKey & rootPublicKey, ByteSpan noc); + CHIP_ERROR SetPeerId(ByteSpan rcac, ByteSpan noc) override; bool MatchesSession(const SessionHandle & session) const { return mSecureSession.Contains(session); } @@ -233,44 +233,6 @@ class CommissioneeDeviceProxy : public DeviceProxy, public SessionReleaseDelegat return LoadSecureSessionParametersIfNeeded(loadedSecureSession); }; - CHIP_ERROR SetCSRNonce(ByteSpan csrNonce) - { - VerifyOrReturnError(csrNonce.size() == sizeof(mCSRNonce), CHIP_ERROR_INVALID_ARGUMENT); - memcpy(mCSRNonce, csrNonce.data(), csrNonce.size()); - return CHIP_NO_ERROR; - } - - ByteSpan GetCSRNonce() const { return ByteSpan(mCSRNonce, sizeof(mCSRNonce)); } - - CHIP_ERROR SetAttestationNonce(ByteSpan attestationNonce) - { - VerifyOrReturnError(attestationNonce.size() == sizeof(mAttestationNonce), CHIP_ERROR_INVALID_ARGUMENT); - memcpy(mAttestationNonce, attestationNonce.data(), attestationNonce.size()); - return CHIP_NO_ERROR; - } - - ByteSpan GetAttestationNonce() const { return ByteSpan(mAttestationNonce, sizeof(mAttestationNonce)); } - - bool AreCredentialsAvailable() const { return (mDAC != nullptr && mDACLen != 0); } - - ByteSpan GetDAC() const { return ByteSpan(mDAC, mDACLen); } - ByteSpan GetPAI() const { return ByteSpan(mPAI, mPAILen); } - - CHIP_ERROR SetDAC(const ByteSpan & dac); - CHIP_ERROR SetPAI(const ByteSpan & pai); - - MutableByteSpan GetMutableNOCCert() { return MutableByteSpan(mNOCCertBuffer, sizeof(mNOCCertBuffer)); } - - CHIP_ERROR SetNOCCertBufferSize(size_t new_size); - - ByteSpan GetNOCCert() const { return ByteSpan(mNOCCertBuffer, mNOCCertBufferSize); } - - MutableByteSpan GetMutableICACert() { return MutableByteSpan(mICACertBuffer, sizeof(mICACertBuffer)); } - - CHIP_ERROR SetICACertBufferSize(size_t new_size); - - ByteSpan GetICACert() const { return ByteSpan(mICACertBuffer, mICACertBufferSize); } - Controller::DeviceControllerInteractionModelDelegate * GetInteractionModelDelegate() override { return mpIMDelegate; }; private: @@ -333,26 +295,8 @@ class CommissioneeDeviceProxy : public DeviceProxy, public SessionReleaseDelegat */ CHIP_ERROR LoadSecureSessionParametersIfNeeded(bool & didLoad); - void ReleaseDAC(); - void ReleasePAI(); - FabricIndex mFabricIndex = kUndefinedFabricIndex; - // TODO: Offload Nonces and DAC/PAI into a new struct - uint8_t mCSRNonce[Controller::kOpCSRNonceLength]; - uint8_t mAttestationNonce[kAttestationNonceLength]; - - uint8_t * mDAC = nullptr; - uint16_t mDACLen = 0; - uint8_t * mPAI = nullptr; - uint16_t mPAILen = 0; - - uint8_t mNOCCertBuffer[Credentials::kMaxCHIPCertLength]; - size_t mNOCCertBufferSize = 0; - - uint8_t mICACertBuffer[Credentials::kMaxCHIPCertLength]; - size_t mICACertBufferSize = 0; - SessionIDAllocator * mIDAllocator = nullptr; }; diff --git a/src/controller/CommissioningDelegate.h b/src/controller/CommissioningDelegate.h index 8eb6112e921651..d88133c0353223 100644 --- a/src/controller/CommissioningDelegate.h +++ b/src/controller/CommissioningDelegate.h @@ -19,6 +19,7 @@ #pragma once #include #include +#include namespace chip { namespace Controller { @@ -32,9 +33,14 @@ enum CommissioningStage : uint8_t // kConfigTimeZone, // NOT YET IMPLEMENTED // kConfigDST, // NOT YET IMPLEMENTED kConfigRegulatory, - kDeviceAttestation, - kCheckCertificates, - kConfigACL, + kSendPAICertificateRequest, + kSendDACCertificateRequest, + kSendAttestationRequest, + kAttestationVerification, + kSendOpCertSigningRequest, + kGenerateNOCChain, + kSendTrustedRootCert, + kSendNOC, kWiFiNetworkSetup, kThreadNetworkSetup, kWiFiNetworkEnable, @@ -42,6 +48,7 @@ enum CommissioningStage : uint8_t kFindOperational, kSendComplete, kCleanup, + kConfigACL, }; struct WiFiCredentials @@ -51,6 +58,15 @@ struct WiFiCredentials ByteSpan credentials; WiFiCredentials(ByteSpan newSsid, ByteSpan newCreds) : ssid(newSsid), credentials(newCreds) {} }; + +struct NOCChainGenerationParameters +{ + ByteSpan nocsrElements; + ByteSpan signature; +}; +struct NOCerts +{ +}; class CommissioningParameters { public: @@ -61,11 +77,28 @@ class CommissioningParameters bool HasAttestationNonce() const { return mAttestationNonce.HasValue(); } bool HasWiFiCredentials() const { return mWiFiCreds.HasValue(); } bool HasThreadOperationalDataset() const { return mThreadOperationalDataset.HasValue(); } + bool HasNOCChainGenerationaParameters() const { return mNOCChainGenerationParameters.HasValue(); } + bool HasRootCert() const { return mRootCert.HasValue(); } + bool HasNoc() const { return mNoc.HasValue(); } + bool HasIcac() const { return mIcac.HasValue(); } + bool HasAttestationElements() const { return mAttestationElements.HasValue(); } + bool HasAttestationSignature() const { return mAttestationSignature.HasValue(); } + bool HasPAI() const { return mPAI.HasValue(); } + bool HasDAC() const { return mDAC.HasValue(); } uint16_t GetFailsafeTimerSeconds() const { return mFailsafeTimerSeconds; } const Optional GetCSRNonce() const { return mCSRNonce; } const Optional GetAttestationNonce() const { return mAttestationNonce; } const Optional GetWiFiCredentials() const { return mWiFiCreds; } const Optional GetThreadOperationalDataset() const { return mThreadOperationalDataset; } + const Optional GetNOCChainGenerationParameters() const { return mNOCChainGenerationParameters; } + const Optional GetRootCert() const { return mRootCert; } + const Optional GetNoc() const { return mNoc; } + const Optional GetIcac() const { return mIcac; } + const Optional GetAttestationElements() const { return mAttestationElements; } + const Optional GetAttestationSignature() const { return mAttestationSignature; } + const Optional GetPAI() const { return mPAI; } + const Optional GetDAC() const { return mDAC; } + CHIP_ERROR GetCompletionStatus() { return completionStatus; } CommissioningParameters & SetFailsafeTimerSeconds(uint16_t seconds) { @@ -98,8 +131,52 @@ class CommissioningParameters mThreadOperationalDataset.SetValue(threadOperationalDataset); return *this; } + // This parameter should be set with the information returned from kSendOpCertSigningRequest. It must be set before calling + // kGenerateNOCChain. + CommissioningParameters & SetNOCChainGenerationParameters(const NOCChainGenerationParameters & params) + { + mNOCChainGenerationParameters.SetValue(params); + return *this; + } + // Root certs can be generated from the kGenerateNOCChain step. This must be set before calling kSendTrustedRootCert. + CommissioningParameters & SetRootCert(const ByteSpan & rcac) + { + mRootCert.SetValue(rcac); + return *this; + } + // NOC and intermediate cert can be generated from the kGenerateNOCChain step. NOC must be set before calling + // kSendTrustedRootCert. ICAC and NOC must be set before calling kSendNOC + CommissioningParameters & SetNoc(const ByteSpan & noc) + { + mNoc.SetValue(noc); + return *this; + } + CommissioningParameters & SetIcac(const ByteSpan & icac) + { + mIcac.SetValue(icac); + return *this; + } + CommissioningParameters & SetAttestationElements(const ByteSpan & attestationElements) + { + mAttestationElements = MakeOptional(attestationElements); + return *this; + } + CommissioningParameters & SetAttestationSignature(const ByteSpan & attestationSignature) + { + mAttestationSignature = MakeOptional(attestationSignature); + return *this; + } + CommissioningParameters & SetPAI(const ByteSpan & pai) + { + mPAI = MakeOptional(pai); + return *this; + } + CommissioningParameters & SetDAC(const ByteSpan & dac) + { + mDAC = MakeOptional(dac); + return *this; + } void SetCompletionStatus(CHIP_ERROR err) { completionStatus = err; } - CHIP_ERROR GetCompletionStatus() { return completionStatus; } private: uint16_t mFailsafeTimerSeconds = 60; @@ -107,26 +184,57 @@ class CommissioningParameters Optional mAttestationNonce; ///< Attestation Nonce passed by the commissioner Optional mWiFiCreds; Optional mThreadOperationalDataset; + Optional mNOCChainGenerationParameters; + Optional mRootCert; + Optional mNoc; + Optional mIcac; + Optional mAttestationElements; + Optional mAttestationSignature; + Optional mPAI; + Optional mDAC; CHIP_ERROR completionStatus = CHIP_NO_ERROR; }; +struct RequestedCertificate +{ + RequestedCertificate(ByteSpan newCertificate) : certificate(newCertificate) {} + ByteSpan certificate; +}; + +struct AttestationResponse +{ + AttestationResponse(ByteSpan newAttestationElements, ByteSpan newSignature) : + attestationElements(newAttestationElements), signature(newSignature) + {} + ByteSpan attestationElements; + ByteSpan signature; +}; + +struct NocChain +{ + NocChain(ByteSpan newNoc, ByteSpan newIcac, ByteSpan newRcac) : noc(newNoc), icac(newIcac), rcac(newRcac) {} + ByteSpan noc; + ByteSpan icac; + ByteSpan rcac; +}; + +struct OperationalNodeFoundData +{ + OperationalNodeFoundData(OperationalDeviceProxy * proxy) : operationalProxy(proxy) {} + OperationalDeviceProxy * operationalProxy; +}; class CommissioningDelegate { public: virtual ~CommissioningDelegate(){}; - struct CommissioningReport + + struct CommissioningReport : Variant { + CommissioningReport(CommissioningStage stage) : stageCompleted(stage) {} CommissioningStage stageCompleted; // TODO: Add other things the delegate needs to know. - union - { - struct - { - OperationalDeviceProxy * operationalProxy; - } OperationalNodeFoundData; - }; }; - virtual void CommissioningStepFinished(CHIP_ERROR err, CommissioningReport report) = 0; + virtual CHIP_ERROR CommissioningStepFinished(CHIP_ERROR err, CommissioningReport report) = 0; }; } // namespace Controller diff --git a/src/credentials/CHIPCert.cpp b/src/credentials/CHIPCert.cpp index d88ee92188b623..ba3123dd08db95 100644 --- a/src/credentials/CHIPCert.cpp +++ b/src/credentials/CHIPCert.cpp @@ -867,21 +867,23 @@ CHIP_ERROR ExtractNodeIdFabricIdFromOpCert(const ChipCertificateData & opcert, N return CHIP_NO_ERROR; } -CHIP_ERROR ExtractNodeIdFabricIdCompressedFabricIdFromRootPubKeyOpCert(const Crypto::P256PublicKey & rootPubKey, ByteSpan noc, - CompressedFabricId & compressedFabricId, FabricId & fabricId, - NodeId & nodeId) +CHIP_ERROR ExtractNodeIdFabricIdCompressedFabricIdFromOpCerts(ByteSpan rcac, ByteSpan noc, CompressedFabricId & compressedFabricId, + FabricId & fabricId, NodeId & nodeId) { + Crypto::P256PublicKey rootPubKey; + Credentials::P256PublicKeySpan rootPubKeySpan; + ReturnErrorOnFailure(ExtractPublicKeyFromChipCert(rcac, rootPubKeySpan)); + rootPubKey = Crypto::P256PublicKey(rootPubKeySpan); ReturnErrorOnFailure(Credentials::ExtractNodeIdFabricIdFromOpCert(noc, &nodeId, &fabricId)); ReturnErrorOnFailure(GenerateCompressedFabricId(rootPubKey, fabricId, compressedFabricId)); return CHIP_NO_ERROR; } -CHIP_ERROR ExtractNodeIdCompressedFabricIdFromRootPubKeyOpCert(const Crypto::P256PublicKey & rootPubKey, ByteSpan noc, - CompressedFabricId & compressedFabricId, NodeId & nodeId) +CHIP_ERROR ExtractNodeIdCompressedFabricIdFromOpCerts(ByteSpan rcac, ByteSpan noc, CompressedFabricId & compressedFabricId, + NodeId & nodeId) { FabricId fabricId; - ReturnErrorOnFailure( - ExtractNodeIdFabricIdCompressedFabricIdFromRootPubKeyOpCert(rootPubKey, noc, compressedFabricId, fabricId, nodeId)); + ReturnErrorOnFailure(ExtractNodeIdFabricIdCompressedFabricIdFromOpCerts(rcac, noc, compressedFabricId, fabricId, nodeId)); return CHIP_NO_ERROR; } diff --git a/src/credentials/CHIPCert.h b/src/credentials/CHIPCert.h index e5f4429f8de8d5..00b1427c976e8c 100644 --- a/src/credentials/CHIPCert.h +++ b/src/credentials/CHIPCert.h @@ -800,22 +800,21 @@ CHIP_ERROR ExtractNodeIdFabricIdFromOpCert(const ChipCertificateData & opcert, N /** * Extract Node ID, Fabric ID and Compressed Fabric ID from an operational - * certificate and root public key. + * certificate and its associated root certificate. * * @return CHIP_ERROR on failure or CHIP_NO_ERROR otherwise. */ -CHIP_ERROR ExtractNodeIdFabricIdCompressedFabricIdFromRootPubKeyOpCert(const Crypto::P256PublicKey & rootPubKey, ByteSpan noc, - CompressedFabricId & compressedFabricId, FabricId & fabricId, - NodeId & nodeId); +CHIP_ERROR ExtractNodeIdFabricIdCompressedFabricIdFromOpCerts(ByteSpan rcac, ByteSpan noc, CompressedFabricId & compressedFabricId, + FabricId & fabricId, NodeId & nodeId); /** * Extract Node ID and Compressed Fabric ID from an operational certificate - * and root public key. + * and its associated root certificate. * * @return CHIP_ERROR on failure or CHIP_NO_ERROR otherwise. */ -CHIP_ERROR ExtractNodeIdCompressedFabricIdFromRootPubKeyOpCert(const Crypto::P256PublicKey & rootPubKey, ByteSpan noc, - CompressedFabricId & compressedFabricId, NodeId & nodeId); +CHIP_ERROR ExtractNodeIdCompressedFabricIdFromOpCerts(ByteSpan rcac, ByteSpan noc, CompressedFabricId & compressedFabricId, + NodeId & nodeId); /** * Extract CASE Authenticated Tags from an operational certificate in ByteSpan TLV-encoded form. diff --git a/src/credentials/tests/TestChipCert.cpp b/src/credentials/tests/TestChipCert.cpp index 8642c2df89f2a4..2bc7623d97145e 100644 --- a/src/credentials/tests/TestChipCert.cpp +++ b/src/credentials/tests/TestChipCert.cpp @@ -1153,15 +1153,10 @@ static void TestChipCert_ExtractOperationalDiscoveryId(nlTestSuite * inSuite, vo NL_TEST_ASSERT(inSuite, nodeId == testCase.ExpectedNodeId); NL_TEST_ASSERT(inSuite, fabricId == testCase.ExpectedFabricId); - // Extract the Public key from the root certificate. - Credentials::P256PublicKeySpan rootPubKey; - err = Credentials::ExtractPublicKeyFromChipCert(rcac, rootPubKey); - NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); - - // Extract Node ID and Fabric ID from the NOC, and generate the - // compressed fabric ID from the root CA public Key and fabric ID. + // Extract Node ID, Fabric ID and Compressed Fabric ID from the + // NOC and root certificate. CompressedFabricId compressedFabricId; - err = ExtractNodeIdFabricIdCompressedFabricIdFromRootPubKeyOpCert(rootPubKey, noc, compressedFabricId, fabricId, nodeId); + err = ExtractNodeIdFabricIdCompressedFabricIdFromOpCerts(rcac, noc, compressedFabricId, fabricId, nodeId); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, compressedFabricId == testCase.ExpectedCompressedFabricId); NL_TEST_ASSERT(inSuite, fabricId == testCase.ExpectedFabricId);