diff --git a/examples/lighting-app/lighting-common/lighting-app.zap b/examples/lighting-app/lighting-common/lighting-app.zap index 69c1661de2ff4a..63009fc2823757 100644 --- a/examples/lighting-app/lighting-common/lighting-app.zap +++ b/examples/lighting-app/lighting-common/lighting-app.zap @@ -2981,6 +2981,22 @@ "side": "client", "enabled": 0, "commands": [ + { + "name": "AttestationRequest", + "code": 0, + "mfgCode": null, + "source": "client", + "incoming": 1, + "outgoing": 1 + }, + { + "name": "CertChainRequest", + "code": 2, + "mfgCode": null, + "source": "client", + "incoming": 1, + "outgoing": 1 + }, { "name": "OpCSRRequest", "code": 4, @@ -3064,6 +3080,22 @@ "side": "server", "enabled": 1, "commands": [ + { + "name": "AttestationResponse", + "code": 1, + "mfgCode": null, + "source": "server", + "incoming": 1, + "outgoing": 1 + }, + { + "name": "CertChainResponse", + "code": 3, + "mfgCode": null, + "source": "server", + "incoming": 1, + "outgoing": 1 + }, { "name": "OpCSRResponse", "code": 5, diff --git a/src/app/clusters/operational-credentials-server/operational-credentials-server.cpp b/src/app/clusters/operational-credentials-server/operational-credentials-server.cpp index 2f137f9848ae6a..407a8791d690f2 100644 --- a/src/app/clusters/operational-credentials-server/operational-credentials-server.cpp +++ b/src/app/clusters/operational-credentials-server/operational-credentials-server.cpp @@ -32,6 +32,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -45,6 +49,9 @@ using namespace chip; using namespace ::chip::DeviceLayer; using namespace ::chip::Transport; +constexpr uint16_t kDACCertificate = 1; +constexpr uint16_t kPAICertificate = 2; + // As per specifications section 11.22.5.1. Constant RESP_MAX constexpr uint16_t kMaxRspLen = 900; @@ -417,6 +424,138 @@ bool emberAfOperationalCredentialsClusterUpdateNOCCallback(EndpointId endpoint, return true; } +bool emberAfOperationalCredentialsClusterCertChainRequestCallback(EndpointId endpoint, app::CommandHandler * commandObj, + uint16_t certChainType) +{ + EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS; + CHIP_ERROR err = CHIP_NO_ERROR; + + emberAfPrintln(EMBER_AF_PRINT_DEBUG, "OpCreds: commissioner has requested Device Attestation Credentials"); + + app::CommandPathParams cmdParams = { emberAfCurrentEndpoint(), /* group id */ 0, ZCL_OPERATIONAL_CREDENTIALS_CLUSTER_ID, + ZCL_CERT_CHAIN_RESPONSE_COMMAND_ID, (app::CommandPathFlags::kEndpointIdValid) }; + + TLV::TLVWriter * writer = nullptr; + uint8_t derBuf[Credentials::kMaxDERCertLength]; + MutableByteSpan derBufSpan(derBuf); + + // TODO: remove line below + Credentials::SetDeviceAttestationCredentialsProvider(Credentials::Examples::GetExampleDACProvider()); + Credentials::DeviceAttestationCredentialsProvider * dacProvider = Credentials::GetDeviceAttestationCredentialsProvider(); + + VerifyOrExit(commandObj != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + + SuccessOrExit(err = commandObj->PrepareCommand(cmdParams)); + writer = commandObj->GetCommandDataElementTLVWriter(); + if (certChainType == kDACCertificate) + { + SuccessOrExit(err = dacProvider->GetDeviceAttestationCert(derBufSpan)); + } + else if (certChainType == kPAICertificate) + { + SuccessOrExit(err = dacProvider->GetProductAttestationIntermediateCert(derBufSpan)); + } + else + { + SuccessOrExit(status = EMBER_ZCL_STATUS_FAILURE); + } + SuccessOrExit(err = writer->Put(TLV::ContextTag(0), derBufSpan)); + SuccessOrExit(err = commandObj->FinishCommand()); + +exit: + if (status == EMBER_ZCL_STATUS_FAILURE) + { + emberAfPrintln(EMBER_AF_PRINT_DEBUG, "OpCreds: Failed CertChainRequest."); + emberAfSendImmediateDefaultResponse(status); + } + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Failed to encode response command: %s", ErrorStr(err)); + } + + return true; +} + +bool emberAfOperationalCredentialsClusterAttestationRequestCallback(EndpointId endpoint, app::CommandHandler * commandObj, + ByteSpan attestationNonce) +{ + EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS; + CHIP_ERROR err = CHIP_NO_ERROR; + TLV::TLVWriter * writer = nullptr; + Platform::ScopedMemoryBuffer attestationElements; + size_t attestationElementsLen; + Crypto::P256ECDSASignature signature; + PeerConnectionState * state; + uint8_t md[Crypto::kSHA256_Hash_Length]; + MutableByteSpan messageDigestSpan(md); + + emberAfPrintln(EMBER_AF_PRINT_DEBUG, "OpCreds: commissioner has requested Attestation"); + + app::CommandPathParams cmdParams = { emberAfCurrentEndpoint(), /* group id */ 0, ZCL_OPERATIONAL_CREDENTIALS_CLUSTER_ID, + ZCL_ATTESTATION_RESPONSE_COMMAND_ID, (app::CommandPathFlags::kEndpointIdValid) }; + + // TODO: remove line below + Credentials::SetDeviceAttestationCredentialsProvider(Credentials::Examples::GetExampleDACProvider()); + Credentials::DeviceAttestationCredentialsProvider * dacProvider = Credentials::GetDeviceAttestationCredentialsProvider(); + + VerifyOrExit(commandObj != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(attestationNonce.size() == 32, status = EMBER_ZCL_STATUS_FAILURE); + + { + uint16_t vendorId = 0xFFF1; + uint16_t profileNum = 0x003E; + std::vector vendorReserved; + uint8_t certDeclBuf[512]; + MutableByteSpan certDeclSpan(certDeclBuf); + + SuccessOrExit(err = dacProvider->GetCertificationDeclaration(certDeclSpan)); + + attestationElementsLen = certDeclSpan.size() + attestationNonce.size() + sizeof(uint64_t) * 8; + VerifyOrExit(attestationElements.Alloc(attestationElementsLen), err = CHIP_ERROR_NO_MEMORY); + + MutableByteSpan attestationElementsSpan(attestationElements.Get(), attestationElementsLen); + SuccessOrExit(err = Credentials::ConstructAttestationElements(certDeclSpan, attestationNonce, 0, ByteSpan(), vendorReserved, + vendorId, profileNum, attestationElementsSpan)); + attestationElementsLen = attestationElementsSpan.size(); + } + + // Retrieve attestation challenge + state = commandObj->GetExchangeContext()->GetExchangeMgr()->GetSessionMgr()->GetPeerConnectionState( + commandObj->GetExchangeContext()->GetSecureSession()); + VerifyOrExit(state != nullptr, status = EMBER_ZCL_STATUS_FAILURE); + + { + Hash_SHA256_stream hashStream; + SuccessOrExit(err = hashStream.Begin()); + SuccessOrExit(err = hashStream.AddData(ByteSpan(attestationElements.Get(), attestationElementsLen))); + SuccessOrExit(err = hashStream.AddData(state->GetSecureSession().GetAttestationChallenge())); + SuccessOrExit(err = hashStream.Finish(messageDigestSpan)); + + MutableByteSpan signatureSpan(signature, signature.Capacity()); + SuccessOrExit(err = dacProvider->SignWithDeviceAttestationKey(messageDigestSpan, signatureSpan)); + SuccessOrExit(err = signature.SetLength(signatureSpan.size())); + } + + SuccessOrExit(err = commandObj->PrepareCommand(cmdParams)); + writer = commandObj->GetCommandDataElementTLVWriter(); + SuccessOrExit(err = writer->Put(TLV::ContextTag(0), ByteSpan(attestationElements.Get(), attestationElementsLen))); + SuccessOrExit(err = writer->Put(TLV::ContextTag(1), ByteSpan(signature, signature.Length()))); + SuccessOrExit(err = commandObj->FinishCommand()); + +exit: + if (status == EMBER_ZCL_STATUS_FAILURE) + { + emberAfPrintln(EMBER_AF_PRINT_DEBUG, "OpCreds: Failed AttestationRequest."); + emberAfSendImmediateDefaultResponse(status); + } + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Failed to encode response command: %s", ErrorStr(err)); + } + + return true; +} + bool emberAfOperationalCredentialsClusterOpCSRRequestCallback(EndpointId endpoint, app::CommandHandler * commandObj, ByteSpan CSRNonce) { diff --git a/src/app/zap-templates/zcl/data-model/chip/operational-credentials-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/operational-credentials-cluster.xml index 592a5ca466b8c6..b62be6a529678b 100644 --- a/src/app/zap-templates/zcl/data-model/chip/operational-credentials-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/operational-credentials-cluster.xml @@ -57,6 +57,27 @@ limitations under the License. TrustedRootCertificates + + Sender is requesting attestation information from the receiver. + + + + + An attestation information confirmation from the server. + + + + + + Sender is requesting a device attestation certificate from the receiver. + + + + + A device attestation certificate (DAC) from the server. + + + Sender is requesting a certificate signing request (CSR) from the receiver. diff --git a/src/controller/CHIPDevice.cpp b/src/controller/CHIPDevice.cpp index bc863012ece384..ebb41fa10a9b91 100644 --- a/src/controller/CHIPDevice.cpp +++ b/src/controller/CHIPDevice.cpp @@ -475,6 +475,9 @@ void Device::Reset() mExchangeMgr->CloseAllContextsForDelegate(this); } mExchangeMgr = nullptr; + + ReleaseDAC(); + ReleasePAI(); } CHIP_ERROR Device::LoadSecureSessionParameters(ResetTransport resetNeeded) @@ -624,6 +627,78 @@ void Device::OnSessionEstablished() } } +void Device::ReleaseDAC() +{ + if (mDAC != nullptr) + { + Platform::MemoryFree(mDAC); + } + mDACLen = 0; + mDAC = nullptr; +} + +CHIP_ERROR Device::SetDAC(const ByteSpan & dac) +{ + if (dac.size() == 0) + { + ReleaseDAC(); + return CHIP_NO_ERROR; + } + + VerifyOrReturnError(dac.size() <= Crypto::kMax_x509_Certificate_Length, 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 Device::ReleasePAI() +{ + if (mPAI != nullptr) + { + chip::Platform::MemoryFree(mPAI); + } + mPAILen = 0; + mPAI = nullptr; +} + +CHIP_ERROR Device::SetPAI(const chip::ByteSpan & pai) +{ + if (pai.size() == 0) + { + ReleasePAI(); + return CHIP_NO_ERROR; + } + + VerifyOrReturnError(pai.size() <= Crypto::kMax_x509_Certificate_Length, 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; +} + CHIP_ERROR Device::EstablishConnectivity(Callback::Callback * onConnection, Callback::Callback * onFailure) { @@ -752,6 +827,9 @@ Device::~Device() // point. mExchangeMgr->CloseAllContextsForDelegate(this); } + + ReleaseDAC(); + ReleasePAI(); } CHIP_ERROR Device::ReduceNOCChainBufferSize(size_t new_size) diff --git a/src/controller/CHIPDevice.h b/src/controller/CHIPDevice.h index 45dec79408001a..ef3a2de0ce2417 100644 --- a/src/controller/CHIPDevice.h +++ b/src/controller/CHIPDevice.h @@ -62,8 +62,9 @@ class DeviceController; class DeviceStatusDelegate; struct SerializedDevice; -constexpr size_t kMaxBlePendingPackets = 1; -constexpr uint32_t kOpCSRNonceLength = 32; +constexpr size_t kMaxBlePendingPackets = 1; +constexpr size_t kOpCSRNonceLength = 32; +constexpr size_t kAttestationNonceLength = 32; using DeviceTransportMgr = TransportMgr #include #include +#include +#include +#include +#include #include #include #include @@ -84,6 +88,7 @@ using namespace chip::Inet; using namespace chip::System; +using namespace chip::Transport; using namespace chip::Credentials; // For some applications those does not implement IMDelegate, the DeviceControllerInteractionModelDelegate will dispatch the @@ -715,12 +720,14 @@ ControllerDeviceInitParams DeviceController::GetControllerDeviceInitParams() } DeviceCommissioner::DeviceCommissioner() : - mSuccess(BasicSuccess, this), mFailure(BasicFailure, this), - mOpCSRResponseCallback(OnOperationalCertificateSigningRequest, this), + mSuccess(BasicSuccess, this), mFailure(BasicFailure, this), mCertChainResponseCallback(OnCertificateChainResponse, this), + mAttestationResponseCallback(OnAttestationResponse, this), mOpCSRResponseCallback(OnOperationalCertificateSigningRequest, this), mNOCResponseCallback(OnOperationalCertificateAddResponse, this), mRootCertResponseCallback(OnRootCertSuccessResponse, this), - mOnCSRFailureCallback(OnCSRFailureResponse, this), mOnCertFailureCallback(OnAddNOCFailureResponse, this), - mOnRootCertFailureCallback(OnRootCertFailureResponse, this), mOnDeviceConnectedCallback(OnDeviceConnectedFn, this), - mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureFn, this), mDeviceNOCChainCallback(OnDeviceNOCChainGeneration, this) + mOnCertChainFailureCallback(OnCertChainFailureResponse, this), + mOnAttestationFailureCallback(OnAttestationFailureResponse, this), mOnCSRFailureCallback(OnCSRFailureResponse, this), + mOnCertFailureCallback(OnAddNOCFailureResponse, this), mOnRootCertFailureCallback(OnRootCertFailureResponse, this), + mOnDeviceConnectedCallback(OnDeviceConnectedFn, this), mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureFn, this), + mDeviceNOCChainCallback(OnDeviceNOCChainGeneration, this) { mPairingDelegate = nullptr; mDeviceBeingPaired = kNumMaxActiveDevices; @@ -745,12 +752,12 @@ CHIP_ERROR DeviceCommissioner::Init(CommissionerInitParams params) mUdcTransportMgr = chip::Platform::New(); ReturnErrorOnFailure(mUdcTransportMgr->Init(Transport::UdpListenParameters(mInetLayer) .SetAddressType(Inet::kIPAddressType_IPv6) - .SetListenPort((uint16_t)(mUdcListenPort)) + .SetListenPort((uint16_t) (mUdcListenPort)) #if INET_CONFIG_ENABLE_IPV4 , Transport::UdpListenParameters(mInetLayer) .SetAddressType(Inet::kIPAddressType_IPv4) - .SetListenPort((uint16_t)(mUdcListenPort)) + .SetListenPort((uint16_t) (mUdcListenPort)) #endif // INET_CONFIG_ENABLE_IPV4 #if CONFIG_NETWORK_LAYER_BLE , @@ -853,6 +860,18 @@ CHIP_ERROR DeviceCommissioner::PairDevice(NodeId remoteDeviceId, RendezvousParam 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))); + } + mIsIPRendezvous = (params.GetPeerAddress().GetTransportType() != Transport::Type::kBle); err = mPairingSession.MessageDispatch().Init(mTransportMgr); @@ -1176,10 +1195,10 @@ void DeviceCommissioner::OnSessionEstablished() if (sendOperationalCertsImmediately) { - err = SendOperationalCertificateSigningRequestCommand(device); + err = SendCertificateChainRequestCommand(device, CertificateChainType::kPAI); if (err != CHIP_NO_ERROR) { - ChipLogError(Ble, "Failed in sending 'CSR request' command to the device: err %s", ErrorStr(err)); + ChipLogError(Ble, "Failed in sending 'Certificate Chain request' command to the device: err %s", ErrorStr(err)); OnSessionEstablishmentError(err); return; } @@ -1190,6 +1209,170 @@ void DeviceCommissioner::OnSessionEstablished() } } +CHIP_ERROR DeviceCommissioner::SendCertificateChainRequestCommand(Device * device, CertificateChainType certificateChainType) +{ + ChipLogDetail(Controller, "Sending Certificate Chain request to %p device", device); + VerifyOrReturnError(device != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + chip::Controller::OperationalCredentialsCluster cluster; + cluster.Associate(device, 0); + + mCertificateChainBeingRequested = certificateChainType; + + Callback::Cancelable * successCallback = mCertChainResponseCallback.Cancel(); + Callback::Cancelable * failureCallback = mOnCertChainFailureCallback.Cancel(); + + ReturnErrorOnFailure(cluster.CertChainRequest(successCallback, failureCallback, certificateChainType)); + ChipLogDetail(Controller, "Sent Certificate Chain request, waiting for the DAC Certificate"); + return CHIP_NO_ERROR; +} + +void DeviceCommissioner::OnCertChainFailureResponse(void * context, uint8_t status) +{ + ChipLogProgress(Controller, "Device failed to receive the Certificate Chain request Response: 0x%02x", status); + DeviceCommissioner * commissioner = reinterpret_cast(context); + commissioner->mCertChainResponseCallback.Cancel(); + commissioner->mOnCertChainFailureCallback.Cancel(); + // TODO: Map error status to correct error code + commissioner->OnSessionEstablishmentError(CHIP_ERROR_INTERNAL); +} + +void DeviceCommissioner::OnCertificateChainResponse(void * context, ByteSpan certificate) +{ + ChipLogProgress(Controller, "Received certificate chain from the device"); + DeviceCommissioner * commissioner = reinterpret_cast(context); + + commissioner->mCertChainResponseCallback.Cancel(); + commissioner->mOnCertChainFailureCallback.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(mDeviceBeingPaired < kNumMaxActiveDevices, CHIP_ERROR_INCORRECT_STATE); + + Device * device = &mActiveDevices[mDeviceBeingPaired]; + + switch (mCertificateChainBeingRequested) + { + case CertificateChainType::kDAC: { + device->SetDAC(certificate); + break; + } + case CertificateChainType::kPAI: { + device->SetPAI(certificate); + break; + } + case CertificateChainType::kUnknown: + default: { + return CHIP_ERROR_INTERNAL; + } + } + + if (device->AreCredentialsAvailable()) + { + ChipLogProgress(Controller, "Sending Attestation Request to the device."); + ReturnErrorOnFailure(SendAttestationRequestCommand(device)); + } + else + { + CHIP_ERROR err = SendCertificateChainRequestCommand(device, CertificateChainType::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; + } + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR DeviceCommissioner::SendAttestationRequestCommand(Device * device) +{ + ChipLogDetail(Controller, "Sending Attestation request to %p device", device); + VerifyOrReturnError(device != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + chip::Controller::OperationalCredentialsCluster cluster; + cluster.Associate(device, 0); + + Callback::Cancelable * successCallback = mAttestationResponseCallback.Cancel(); + Callback::Cancelable * failureCallback = mOnAttestationFailureCallback.Cancel(); + + ReturnErrorOnFailure(cluster.AttestationRequest(successCallback, failureCallback, device->GetAttestationNonce())); + ChipLogDetail(Controller, "Sent Attestation request, waiting for the Attestation Information"); + return CHIP_NO_ERROR; +} + +void DeviceCommissioner::OnAttestationFailureResponse(void * context, uint8_t status) +{ + ChipLogProgress(Controller, "Device failed to receive the Attestation Information Response: 0x%02x", status); + DeviceCommissioner * commissioner = reinterpret_cast(context); + commissioner->mAttestationResponseCallback.Cancel(); + commissioner->mOnAttestationFailureCallback.Cancel(); + // TODO: Map error status to correct error code + commissioner->OnSessionEstablishmentError(CHIP_ERROR_INTERNAL); +} + +void DeviceCommissioner::OnAttestationResponse(void * context, chip::ByteSpan attestationElements, chip::ByteSpan signature) +{ + ChipLogProgress(Controller, "Received Attestation Information from the device"); + DeviceCommissioner * commissioner = reinterpret_cast(context); + + commissioner->mAttestationResponseCallback.Cancel(); + commissioner->mOnAttestationFailureCallback.Cancel(); + + CHIP_ERROR err = commissioner->ValidateAttestationInfo(attestationElements, signature); + if (err != CHIP_NO_ERROR) + { + // Handle error, and notify session failure to the commissioner application. + ChipLogError(Controller, "Failed to validate the Attestation Information"); + // TODO: Map error status to correct error code + commissioner->OnSessionEstablishmentError(CHIP_ERROR_INTERNAL); + } +} + +CHIP_ERROR DeviceCommissioner::ValidateAttestationInfo(chip::ByteSpan attestationElements, chip::ByteSpan signature) +{ + VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mDeviceBeingPaired < kNumMaxActiveDevices, CHIP_ERROR_INCORRECT_STATE); + + Device * device = &mActiveDevices[mDeviceBeingPaired]; + + SetDeviceAttestationVerifier(Examples::GetExampleDACVerifier()); + DeviceAttestationVerifier * dac_verifier = GetDeviceAttestationVerifier(); + + // Retrieve attestation challenge + PeerConnectionState * state = mSessionMgr->GetPeerConnectionState( + { mPairingSession.GetPeerNodeId(), mPairingSession.GetLocalKeyId(), mPairingSession.GetPeerKeyId(), mFabricIndex }); + VerifyOrReturnError(state != nullptr, CHIP_ERROR_NOT_CONNECTED); + + VerifyOrReturnError(dac_verifier->VerifyAttestationInformation( + attestationElements, state->GetSecureSession().GetAttestationChallenge(), signature, device->GetPAI(), + device->GetDAC(), device->GetAttestationNonce()) == AttestationVerificationResult::kSuccess, + CHIP_ERROR_INTERNAL); + + // TODO: Step g/h validate CertDeclaration + // TODO: Step i: validate firmware information + + 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 error; + } + + return CHIP_NO_ERROR; +} + CHIP_ERROR DeviceCommissioner::SendOperationalCertificateSigningRequestCommand(Device * device) { ChipLogDetail(Controller, "Sending OpCSR request to %p device", device); diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h index 4f67d5ba35c883..07413aa6674a72 100644 --- a/src/controller/CHIPDeviceController.h +++ b/src/controller/CHIPDeviceController.h @@ -617,6 +617,15 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, If no device is currently being paired, this value will be kNumMaxPairedDevices. */ uint16_t mDeviceBeingPaired; + enum CertificateChainType : uint16_t + { + kUnknown = 0, + kDAC = 1, + kPAI = 2, + }; + + CertificateChainType mCertificateChainBeingRequested = CertificateChainType::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 @@ -649,6 +658,14 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, static void OnSessionEstablishmentTimeoutCallback(System::Layer * aLayer, void * aAppState); + /* 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(Device * device, CertificateChainType certificateChainType); + /* 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 SendAttestationRequestCommand(Device * device); /* This function sends an OpCSR request to the device. The function does not hold a refernce to the device object. */ @@ -671,6 +688,12 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, /* Callback when the previously sent CSR request results in failure */ static void OnCSRFailureResponse(void * context, uint8_t status); + static void OnCertChainFailureResponse(void * context, uint8_t status); + static void OnCertificateChainResponse(void * context, ByteSpan certificate); + + static void OnAttestationFailureResponse(void * context, uint8_t status); + static void OnAttestationResponse(void * context, chip::ByteSpan attestationElements, chip::ByteSpan signature); + /** * @brief * This function is called by the IM layer when the commissioner receives the CSR from the device. @@ -708,6 +731,10 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, */ CHIP_ERROR ProcessOpCSR(const ByteSpan & NOCSRElements, const ByteSpan & AttestationSignature); + CHIP_ERROR ProcessCertificateChain(const ByteSpan & certificate); + + CHIP_ERROR ValidateAttestationInfo(chip::ByteSpan attestationElements, chip::ByteSpan signature); + // Cluster callbacks for advancing commissioning flows Callback::Callback mSuccess; Callback::Callback mFailure; @@ -715,9 +742,13 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, CommissioningStage GetNextCommissioningStage(); static CHIP_ERROR ConvertFromNodeOperationalCertStatus(uint8_t err); + Callback::Callback mCertChainResponseCallback; + Callback::Callback mAttestationResponseCallback; Callback::Callback mOpCSRResponseCallback; Callback::Callback mNOCResponseCallback; Callback::Callback mRootCertResponseCallback; + Callback::Callback mOnCertChainFailureCallback; + Callback::Callback mOnAttestationFailureCallback; Callback::Callback mOnCSRFailureCallback; Callback::Callback mOnCertFailureCallback; Callback::Callback mOnRootCertFailureCallback; diff --git a/src/controller/data_model/controller-clusters.zap b/src/controller/data_model/controller-clusters.zap index 34cc0e768e0cd8..a76888623d370a 100644 --- a/src/controller/data_model/controller-clusters.zap +++ b/src/controller/data_model/controller-clusters.zap @@ -3799,6 +3799,22 @@ "side": "client", "enabled": 1, "commands": [ + { + "name": "AttestationRequest", + "code": 0, + "mfgCode": null, + "source": "client", + "incoming": 0, + "outgoing": 1 + }, + { + "name": "CertChainRequest", + "code": 2, + "mfgCode": null, + "source": "client", + "incoming": 0, + "outgoing": 1 + }, { "name": "OpCSRRequest", "code": 4, @@ -3882,6 +3898,22 @@ "side": "server", "enabled": 0, "commands": [ + { + "name": "AttestationResponse", + "code": 1, + "mfgCode": null, + "source": "server", + "incoming": 1, + "outgoing": 0 + }, + { + "name": "CertChainResponse", + "code": 3, + "mfgCode": null, + "source": "server", + "incoming": 1, + "outgoing": 0 + }, { "name": "OpCSRResponse", "code": 5, diff --git a/src/controller/java/zap-generated/CHIPClusters-JNI.cpp b/src/controller/java/zap-generated/CHIPClusters-JNI.cpp index 5dbb9c9e44eff1..3f8400c4e37ceb 100644 --- a/src/controller/java/zap-generated/CHIPClusters-JNI.cpp +++ b/src/controller/java/zap-generated/CHIPClusters-JNI.cpp @@ -4856,6 +4856,170 @@ class CHIPOtaSoftwareUpdateProviderClusterQueryImageResponseCallback jobject javaCallbackRef; }; +class CHIPOperationalCredentialsClusterAttestationResponseCallback + : public Callback::Callback +{ +public: + CHIPOperationalCredentialsClusterAttestationResponseCallback(jobject javaCallback) : + Callback::Callback(CallbackFn, this) + { + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + if (env == nullptr) + { + ChipLogError(Zcl, "Could not create global reference for Java callback"); + return; + } + + javaCallbackRef = env->NewGlobalRef(javaCallback); + if (javaCallbackRef == nullptr) + { + ChipLogError(Zcl, "Could not create global reference for Java callback"); + } + } + ~CHIPOperationalCredentialsClusterAttestationResponseCallback() + { + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + if (env == nullptr) + { + ChipLogError(Zcl, "Could not create global reference for Java callback"); + return; + } + env->DeleteGlobalRef(javaCallbackRef); + }; + + static void CallbackFn(void * context, chip::ByteSpan AttestationElements, chip::ByteSpan Signature) + { + StackUnlockGuard unlockGuard(JniReferences::GetInstance().GetStackLock()); + CHIP_ERROR err = CHIP_NO_ERROR; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + jobject javaCallbackRef; + jmethodID javaMethod; + CHIPOperationalCredentialsClusterAttestationResponseCallback * cppCallback = nullptr; + jbyteArray AttestationElementsArr; + jbyteArray SignatureArr; + + VerifyOrExit(env != nullptr, err = CHIP_JNI_ERROR_NO_ENV); + + cppCallback = reinterpret_cast(context); + VerifyOrExit(cppCallback != nullptr, err = CHIP_JNI_ERROR_NULL_OBJECT); + + javaCallbackRef = cppCallback->javaCallbackRef; + VerifyOrExit(javaCallbackRef != nullptr, err = CHIP_NO_ERROR); + + err = JniReferences::GetInstance().FindMethod(env, javaCallbackRef, "onSuccess", "([B[B)V", &javaMethod); + SuccessOrExit(err); + + AttestationElementsArr = env->NewByteArray(AttestationElements.size()); + VerifyOrExit(AttestationElementsArr != nullptr, err = CHIP_ERROR_NO_MEMORY); + env->ExceptionClear(); + env->SetByteArrayRegion(AttestationElementsArr, 0, AttestationElements.size(), + reinterpret_cast(AttestationElements.data())); + VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); + SignatureArr = env->NewByteArray(Signature.size()); + VerifyOrExit(SignatureArr != nullptr, err = CHIP_ERROR_NO_MEMORY); + env->ExceptionClear(); + env->SetByteArrayRegion(SignatureArr, 0, Signature.size(), reinterpret_cast(Signature.data())); + VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); + + env->CallVoidMethod(javaCallbackRef, javaMethod, AttestationElementsArr, SignatureArr); + + env->DeleteLocalRef(AttestationElementsArr); + env->DeleteLocalRef(SignatureArr); + + exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Error invoking Java callback: %" CHIP_ERROR_FORMAT, err.Format()); + } + if (cppCallback != nullptr) + { + cppCallback->Cancel(); + delete cppCallback; + } + } + +private: + jobject javaCallbackRef; +}; + +class CHIPOperationalCredentialsClusterCertChainResponseCallback + : public Callback::Callback +{ +public: + CHIPOperationalCredentialsClusterCertChainResponseCallback(jobject javaCallback) : + Callback::Callback(CallbackFn, this) + { + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + if (env == nullptr) + { + ChipLogError(Zcl, "Could not create global reference for Java callback"); + return; + } + + javaCallbackRef = env->NewGlobalRef(javaCallback); + if (javaCallbackRef == nullptr) + { + ChipLogError(Zcl, "Could not create global reference for Java callback"); + } + } + ~CHIPOperationalCredentialsClusterCertChainResponseCallback() + { + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + if (env == nullptr) + { + ChipLogError(Zcl, "Could not create global reference for Java callback"); + return; + } + env->DeleteGlobalRef(javaCallbackRef); + }; + + static void CallbackFn(void * context, chip::ByteSpan Certificate) + { + StackUnlockGuard unlockGuard(JniReferences::GetInstance().GetStackLock()); + CHIP_ERROR err = CHIP_NO_ERROR; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + jobject javaCallbackRef; + jmethodID javaMethod; + CHIPOperationalCredentialsClusterCertChainResponseCallback * cppCallback = nullptr; + jbyteArray CertificateArr; + + VerifyOrExit(env != nullptr, err = CHIP_JNI_ERROR_NO_ENV); + + cppCallback = reinterpret_cast(context); + VerifyOrExit(cppCallback != nullptr, err = CHIP_JNI_ERROR_NULL_OBJECT); + + javaCallbackRef = cppCallback->javaCallbackRef; + VerifyOrExit(javaCallbackRef != nullptr, err = CHIP_NO_ERROR); + + err = JniReferences::GetInstance().FindMethod(env, javaCallbackRef, "onSuccess", "([B)V", &javaMethod); + SuccessOrExit(err); + + CertificateArr = env->NewByteArray(Certificate.size()); + VerifyOrExit(CertificateArr != nullptr, err = CHIP_ERROR_NO_MEMORY); + env->ExceptionClear(); + env->SetByteArrayRegion(CertificateArr, 0, Certificate.size(), reinterpret_cast(Certificate.data())); + VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); + + env->CallVoidMethod(javaCallbackRef, javaMethod, CertificateArr); + + env->DeleteLocalRef(CertificateArr); + + exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Error invoking Java callback: %" CHIP_ERROR_FORMAT, err.Format()); + } + if (cppCallback != nullptr) + { + cppCallback->Cancel(); + delete cppCallback; + } + } + +private: + jobject javaCallbackRef; +}; + class CHIPOperationalCredentialsClusterNOCResponseCallback : public Callback::Callback { @@ -21483,6 +21647,100 @@ JNI_METHOD(void, OperationalCredentialsCluster, addTrustedRootCertificate) chip::ByteSpan((const uint8_t *) rootCertificateArr.data(), rootCertificateArr.size())); SuccessOrExit(err); +exit: + if (err != CHIP_NO_ERROR) + { + delete onSuccess; + delete onFailure; + + jthrowable exception; + jmethodID method; + + err = JniReferences::GetInstance().FindMethod(env, callback, "onError", "(Ljava/lang/Exception;)V", &method); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Error throwing IllegalStateException %" CHIP_ERROR_FORMAT, err.Format()); + return; + } + + err = CreateIllegalStateException(env, "Error invoking cluster", err.Format(), exception); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Error throwing IllegalStateException %" CHIP_ERROR_FORMAT, err.Format()); + return; + } + env->CallVoidMethod(callback, method, exception); + } +} +JNI_METHOD(void, OperationalCredentialsCluster, attestationRequest) +(JNIEnv * env, jobject self, jlong clusterPtr, jobject callback, jbyteArray attestationNonce) +{ + StackLockGuard lock(JniReferences::GetInstance().GetStackLock()); + CHIP_ERROR err = CHIP_NO_ERROR; + OperationalCredentialsCluster * cppCluster; + + JniByteArray attestationNonceArr(env, attestationNonce); + CHIPOperationalCredentialsClusterAttestationResponseCallback * onSuccess; + CHIPDefaultFailureCallback * onFailure; + + cppCluster = reinterpret_cast(clusterPtr); + VerifyOrExit(cppCluster != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + + onSuccess = new CHIPOperationalCredentialsClusterAttestationResponseCallback(callback); + VerifyOrExit(onSuccess != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + onFailure = new CHIPDefaultFailureCallback(callback); + VerifyOrExit(onFailure != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + + err = cppCluster->AttestationRequest(onSuccess->Cancel(), onFailure->Cancel(), + chip::ByteSpan((const uint8_t *) attestationNonceArr.data(), attestationNonceArr.size())); + SuccessOrExit(err); + +exit: + if (err != CHIP_NO_ERROR) + { + delete onSuccess; + delete onFailure; + + jthrowable exception; + jmethodID method; + + err = JniReferences::GetInstance().FindMethod(env, callback, "onError", "(Ljava/lang/Exception;)V", &method); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Error throwing IllegalStateException %" CHIP_ERROR_FORMAT, err.Format()); + return; + } + + err = CreateIllegalStateException(env, "Error invoking cluster", err.Format(), exception); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Error throwing IllegalStateException %" CHIP_ERROR_FORMAT, err.Format()); + return; + } + env->CallVoidMethod(callback, method, exception); + } +} +JNI_METHOD(void, OperationalCredentialsCluster, certChainRequest) +(JNIEnv * env, jobject self, jlong clusterPtr, jobject callback, jint certChainType) +{ + StackLockGuard lock(JniReferences::GetInstance().GetStackLock()); + CHIP_ERROR err = CHIP_NO_ERROR; + OperationalCredentialsCluster * cppCluster; + + CHIPOperationalCredentialsClusterCertChainResponseCallback * onSuccess; + CHIPDefaultFailureCallback * onFailure; + + cppCluster = reinterpret_cast(clusterPtr); + VerifyOrExit(cppCluster != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + + onSuccess = new CHIPOperationalCredentialsClusterCertChainResponseCallback(callback); + VerifyOrExit(onSuccess != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + onFailure = new CHIPDefaultFailureCallback(callback); + VerifyOrExit(onFailure != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + + err = cppCluster->CertChainRequest(onSuccess->Cancel(), onFailure->Cancel(), certChainType); + SuccessOrExit(err); + exit: if (err != CHIP_NO_ERROR) { diff --git a/src/controller/java/zap-generated/chip/devicecontroller/ChipClusters.java b/src/controller/java/zap-generated/chip/devicecontroller/ChipClusters.java index 2939bc7c172b9b..187bb793f34c1e 100644 --- a/src/controller/java/zap-generated/chip/devicecontroller/ChipClusters.java +++ b/src/controller/java/zap-generated/chip/devicecontroller/ChipClusters.java @@ -3848,6 +3848,14 @@ public void addTrustedRootCertificate(DefaultClusterCallback callback, byte[] ro addTrustedRootCertificate(chipClusterPtr, callback, rootCertificate); } + public void attestationRequest(AttestationResponseCallback callback, byte[] attestationNonce) { + attestationRequest(chipClusterPtr, callback, attestationNonce); + } + + public void certChainRequest(CertChainResponseCallback callback, int certChainType) { + certChainRequest(chipClusterPtr, callback, certChainType); + } + public void opCSRRequest(OpCSRResponseCallback callback, byte[] cSRNonce) { opCSRRequest(chipClusterPtr, callback, cSRNonce); } @@ -3880,6 +3888,12 @@ private native void addNOC( private native void addTrustedRootCertificate( long chipClusterPtr, DefaultClusterCallback callback, byte[] rootCertificate); + private native void attestationRequest( + long chipClusterPtr, AttestationResponseCallback callback, byte[] attestationNonce); + + private native void certChainRequest( + long chipClusterPtr, CertChainResponseCallback callback, int certChainType); + private native void opCSRRequest( long chipClusterPtr, OpCSRResponseCallback callback, byte[] cSRNonce); @@ -3895,6 +3909,18 @@ private native void updateFabricLabel( private native void updateNOC( long chipClusterPtr, NOCResponseCallback callback, byte[] nOCArray); + public interface AttestationResponseCallback { + void onSuccess(byte[] AttestationElements, byte[] Signature); + + void onError(Exception error); + } + + public interface CertChainResponseCallback { + void onSuccess(byte[] Certificate); + + void onError(Exception error); + } + public interface NOCResponseCallback { void onSuccess(int StatusCode, int FabricIndex, byte[] DebugText); diff --git a/src/controller/python/chip/clusters/CHIPClusters.cpp b/src/controller/python/chip/clusters/CHIPClusters.cpp index 49c92376101e76..6a77b4de6bc9fd 100644 --- a/src/controller/python/chip/clusters/CHIPClusters.cpp +++ b/src/controller/python/chip/clusters/CHIPClusters.cpp @@ -3898,6 +3898,26 @@ chip::ChipError::StorageType chip_ime_AppendCommand_OperationalCredentials_AddTr cluster.Associate(device, ZCLendpointId); return cluster.AddTrustedRootCertificate(nullptr, nullptr, chip::ByteSpan(rootCertificate, rootCertificate_Len)).AsInteger(); } +chip::ChipError::StorageType chip_ime_AppendCommand_OperationalCredentials_AttestationRequest(chip::Controller::Device * device, + chip::EndpointId ZCLendpointId, + chip::GroupId, + const uint8_t * attestationNonce, + uint32_t attestationNonce_Len) +{ + VerifyOrReturnError(device != nullptr, CHIP_ERROR_INVALID_ARGUMENT.AsInteger()); + chip::Controller::OperationalCredentialsCluster cluster; + cluster.Associate(device, ZCLendpointId); + return cluster.AttestationRequest(nullptr, nullptr, chip::ByteSpan(attestationNonce, attestationNonce_Len)).AsInteger(); +} +chip::ChipError::StorageType chip_ime_AppendCommand_OperationalCredentials_CertChainRequest(chip::Controller::Device * device, + chip::EndpointId ZCLendpointId, + chip::GroupId, uint16_t certChainType) +{ + VerifyOrReturnError(device != nullptr, CHIP_ERROR_INVALID_ARGUMENT.AsInteger()); + chip::Controller::OperationalCredentialsCluster cluster; + cluster.Associate(device, ZCLendpointId); + return cluster.CertChainRequest(nullptr, nullptr, certChainType).AsInteger(); +} chip::ChipError::StorageType chip_ime_AppendCommand_OperationalCredentials_OpCSRRequest(chip::Controller::Device * device, chip::EndpointId ZCLendpointId, chip::GroupId, const uint8_t * cSRNonce, diff --git a/src/controller/python/chip/clusters/CHIPClusters.py b/src/controller/python/chip/clusters/CHIPClusters.py index 9c255daabb6eba..039c4d2d33496c 100644 --- a/src/controller/python/chip/clusters/CHIPClusters.py +++ b/src/controller/python/chip/clusters/CHIPClusters.py @@ -2241,6 +2241,20 @@ class ChipClusters: "rootCertificate": "bytes", }, }, + 0x00000000: { + "commandId": 0x00000000, + "commandName": "AttestationRequest", + "args": { + "attestationNonce": "bytes", + }, + }, + 0x00000002: { + "commandId": 0x00000002, + "commandName": "CertChainRequest", + "args": { + "certChainType": "int", + }, + }, 0x00000004: { "commandId": 0x00000004, "commandName": "OpCSRRequest", @@ -4204,6 +4218,14 @@ def ClusterOperationalCredentials_CommandAddTrustedRootCertificate(self, device: return self._chipLib.chip_ime_AppendCommand_OperationalCredentials_AddTrustedRootCertificate( device, ZCLendpoint, ZCLgroupid, rootCertificate, len(rootCertificate) ) + def ClusterOperationalCredentials_CommandAttestationRequest(self, device: ctypes.c_void_p, ZCLendpoint: int, ZCLgroupid: int, attestationNonce: bytes): + return self._chipLib.chip_ime_AppendCommand_OperationalCredentials_AttestationRequest( + device, ZCLendpoint, ZCLgroupid, attestationNonce, len(attestationNonce) + ) + def ClusterOperationalCredentials_CommandCertChainRequest(self, device: ctypes.c_void_p, ZCLendpoint: int, ZCLgroupid: int, certChainType: int): + return self._chipLib.chip_ime_AppendCommand_OperationalCredentials_CertChainRequest( + device, ZCLendpoint, ZCLgroupid, certChainType + ) def ClusterOperationalCredentials_CommandOpCSRRequest(self, device: ctypes.c_void_p, ZCLendpoint: int, ZCLgroupid: int, cSRNonce: bytes): return self._chipLib.chip_ime_AppendCommand_OperationalCredentials_OpCSRRequest( device, ZCLendpoint, ZCLgroupid, cSRNonce, len(cSRNonce) @@ -6317,6 +6339,12 @@ def InitLib(self, chipLib): # Cluster OperationalCredentials Command AddTrustedRootCertificate self._chipLib.chip_ime_AppendCommand_OperationalCredentials_AddTrustedRootCertificate.argtypes = [ctypes.c_void_p, ctypes.c_uint8, ctypes.c_uint16, ctypes.c_char_p, ctypes.c_uint32] self._chipLib.chip_ime_AppendCommand_OperationalCredentials_AddTrustedRootCertificate.restype = ctypes.c_uint32 + # Cluster OperationalCredentials Command AttestationRequest + self._chipLib.chip_ime_AppendCommand_OperationalCredentials_AttestationRequest.argtypes = [ctypes.c_void_p, ctypes.c_uint8, ctypes.c_uint16, ctypes.c_char_p, ctypes.c_uint32] + self._chipLib.chip_ime_AppendCommand_OperationalCredentials_AttestationRequest.restype = ctypes.c_uint32 + # Cluster OperationalCredentials Command CertChainRequest + self._chipLib.chip_ime_AppendCommand_OperationalCredentials_CertChainRequest.argtypes = [ctypes.c_void_p, ctypes.c_uint8, ctypes.c_uint16, ctypes.c_uint16] + self._chipLib.chip_ime_AppendCommand_OperationalCredentials_CertChainRequest.restype = ctypes.c_uint32 # Cluster OperationalCredentials Command OpCSRRequest self._chipLib.chip_ime_AppendCommand_OperationalCredentials_OpCSRRequest.argtypes = [ctypes.c_void_p, ctypes.c_uint8, ctypes.c_uint16, ctypes.c_char_p, ctypes.c_uint32] self._chipLib.chip_ime_AppendCommand_OperationalCredentials_OpCSRRequest.restype = ctypes.c_uint32 diff --git a/src/credentials/BUILD.gn b/src/credentials/BUILD.gn index b76a11234e981c..9e22879014314a 100644 --- a/src/credentials/BUILD.gn +++ b/src/credentials/BUILD.gn @@ -25,6 +25,8 @@ static_library("credentials") { "CHIPCertToX509.cpp", "CHIPOperationalCredentials.cpp", "CHIPOperationalCredentials.h", + "DeviceAttestationConstructor.h", + "DeviceAttestationConstructor.cpp", "DeviceAttestationCredsProvider.cpp", "DeviceAttestationCredsProvider.h", "DeviceAttestationVerifier.cpp", diff --git a/src/credentials/DeviceAttestationConstructor.cpp b/src/credentials/DeviceAttestationConstructor.cpp new file mode 100644 index 00000000000000..6def0c8f27eff2 --- /dev/null +++ b/src/credentials/DeviceAttestationConstructor.cpp @@ -0,0 +1,173 @@ +/* + * + * Copyright (c) 2021 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 "DeviceAttestationConstructor.h" + +#include +#include +#include + +#include + +namespace chip { +namespace Credentials { + +// context tag positions +enum +{ + CERTIFICATION_DECLARATION = 1, + ATTESTATION_NONCE = 2, + TIMESTAMP = 3, + FIRMWARE_INFO = 4, + NUMBER_OF_VALID_TAGS = FIRMWARE_INFO, +}; + +CHIP_ERROR DeconstructAttestationElements(const ByteSpan & attestationElements, ByteSpan & certificationDeclaration, + ByteSpan & attestationNonce, uint32_t & timestamp, ByteSpan & firmwareInfo, + std::vector & vendorReserved, uint16_t & vendorId, uint16_t & profileNum) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + uint32_t currentDecodeTagId = 0; + bool argExists[NUMBER_OF_VALID_TAGS + 1] = { false }; // the 0 element is not used so it aligns with tag numbers + TLV::TLVReader tlvReader; + TLV::TLVType containerType = TLV::kTLVType_Structure; + + vendorReserved.clear(); + + tlvReader.Init(attestationElements.data(), static_cast(attestationElements.size())); + ReturnErrorOnFailure(tlvReader.Next(containerType, TLV::AnonymousTag)); + ReturnErrorOnFailure(tlvReader.EnterContainer(containerType)); + + while ((error = tlvReader.Next()) == CHIP_NO_ERROR) + { + uint64_t tag; + tag = tlvReader.GetTag(); + + currentDecodeTagId = TLV::TagNumFromTag(tag); + VerifyOrReturnError(currentDecodeTagId != 0, CHIP_ERROR_INVALID_TLV_TAG); + + if (TLV::IsContextTag(tag)) + { + VerifyOrReturnError(currentDecodeTagId <= NUMBER_OF_VALID_TAGS, CHIP_ERROR_INVALID_TLV_TAG); + + if (argExists[currentDecodeTagId]) + { + ChipLogProgress(Zcl, "Duplicate TLV tag %" PRIx32, TLV::TagNumFromTag(tag)); + return CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT; + } + else + { + argExists[currentDecodeTagId] = true; + } + + switch (currentDecodeTagId) + { + case CERTIFICATION_DECLARATION: + ReturnErrorOnFailure(tlvReader.Get(certificationDeclaration)); + break; + case ATTESTATION_NONCE: + ReturnErrorOnFailure(tlvReader.Get(attestationNonce)); + break; + case TIMESTAMP: + ReturnErrorOnFailure(tlvReader.Get(timestamp)); + break; + case FIRMWARE_INFO: + ReturnErrorOnFailure(tlvReader.Get(firmwareInfo)); + break; + default: + return CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT; + } + } + else if (TLV::IsProfileTag(tag)) + { + // vendor fields + bool seenProfile = false; + uint16_t currentVendorId; + uint16_t currentProfileNum; + + currentVendorId = TLV::VendorIdFromTag(tag); + currentProfileNum = TLV::ProfileNumFromTag(tag); + if (false == seenProfile) + { + seenProfile = true; + vendorId = currentVendorId; + profileNum = currentProfileNum; + } + else + { + // check that vendorId and profileNum match in every Vendor Reserved entry + VerifyOrReturnError(currentVendorId == vendorId && currentProfileNum == profileNum, + CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT); + } + + ByteSpan vendorReservedEntry; + ReturnErrorOnFailure(tlvReader.Get(vendorReservedEntry)); + vendorReserved.push_back(vendorReservedEntry); + } + else + { + return CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT; + } + } + + VerifyOrReturnError(error == CHIP_END_OF_TLV, error); + VerifyOrReturnError(argExists[CERTIFICATION_DECLARATION] && argExists[ATTESTATION_NONCE] && argExists[TIMESTAMP], + CHIP_ERROR_MISSING_TLV_ELEMENT); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR ConstructAttestationElements(const ByteSpan & certificationDeclaration, const ByteSpan & attestationNonce, + uint32_t timestamp, const ByteSpan & firmwareInfo, std::vector & vendorReserved, + uint16_t vendorId, uint16_t profileNum, MutableByteSpan & attestationElements) +{ + TLV::TLVWriter tlvWriter; + TLV::TLVType outerContainerType = TLV::kTLVType_NotSpecified; + + VerifyOrReturnError(!certificationDeclaration.empty() && !attestationNonce.empty(), CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(attestationNonce.size() == 32, CHIP_ERROR_INVALID_MESSAGE_LENGTH); + + tlvWriter.Init(attestationElements.data(), static_cast(attestationElements.size())); + outerContainerType = TLV::kTLVType_NotSpecified; + ReturnErrorOnFailure(tlvWriter.StartContainer(TLV::AnonymousTag, TLV::kTLVType_Structure, outerContainerType)); + ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(1), certificationDeclaration)); + ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(2), attestationNonce)); + ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(3), timestamp)); + if (!firmwareInfo.empty()) + { + ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(4), firmwareInfo)); + } + + uint8_t vendorTagNum = 1; + for (auto & vendorItem : vendorReserved) + { + if (!vendorItem.empty()) + { + ReturnErrorOnFailure(tlvWriter.Put(TLV::ProfileTag(vendorId, profileNum, vendorTagNum), vendorItem)); + } + vendorTagNum++; + } + + ReturnErrorOnFailure(tlvWriter.EndContainer(outerContainerType)); + ReturnErrorOnFailure(tlvWriter.Finalize()); + attestationElements = attestationElements.SubSpan(0, tlvWriter.GetLengthWritten()); + + return CHIP_NO_ERROR; +} + +} // namespace Credentials + +} // namespace chip diff --git a/src/credentials/DeviceAttestationConstructor.h b/src/credentials/DeviceAttestationConstructor.h new file mode 100644 index 00000000000000..b0060a7b40d2ce --- /dev/null +++ b/src/credentials/DeviceAttestationConstructor.h @@ -0,0 +1,61 @@ +/* + * + * Copyright (c) 2021 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. + */ +#pragma once + +#include +#include + +#include + +namespace chip { +namespace Credentials { + +/** + * @brief Take the attestation elements buffer and return each component seperately. + * + * @param[in] attestationElements Buffer containg source of attestion + * @param[out] certificationDeclaration + * @param[out] attestationNonce + * @param[out] timestamp + * @param[out] firmwareInfo + * @param[out] vendorReserved elements + * @param[out] vendorId (from vendor reserved elements) + * @param[out] profileNum (from vendor reserved elements) + */ +CHIP_ERROR DeconstructAttestationElements(const ByteSpan & attestationElements, ByteSpan & certificationDeclaration, + ByteSpan & attestationNonce, uint32_t & timestamp, ByteSpan & firmwareInfo, + std::vector & vendorReserved, uint16_t & vendorId, uint16_t & profileNum); + +/** + * @brief Take each component separately and form the Attestation Elements buffer. + * + * @param[in] certificationDeclaration + * @param[in] attestationNonce + * @param[in] timestamp + * @param[in] firmwareInfo + * @param[in] vendorReserved elements + * @param[in] vendorId (from vendor reserved elements) + * @param[in] profileNum (from vendor reserved elements) + * @param[out] attestationElements Buffer containg source of attestion + */ + +CHIP_ERROR ConstructAttestationElements(const ByteSpan & certificationDeclaration, const ByteSpan & attestationNonce, + uint32_t timestamp, const ByteSpan & firmwareInfo, std::vector & vendorReserved, + uint16_t vendorId, uint16_t profileNum, MutableByteSpan & attestationElements); + +} // namespace Credentials +} // namespace chip diff --git a/src/credentials/DeviceAttestationVerifier.h b/src/credentials/DeviceAttestationVerifier.h index 70d7b25722b633..a6fa3e29d02892 100644 --- a/src/credentials/DeviceAttestationVerifier.h +++ b/src/credentials/DeviceAttestationVerifier.h @@ -60,7 +60,9 @@ enum class AttestationVerificationResult : uint16_t kAttestationSignatureInvalid = 800, - kNoMemory = 900, + kAttestationElementsMalformed = 900, + + kNoMemory = 1000, kNotImplemented = 0xFFFFU, diff --git a/src/credentials/examples/DeviceAttestationVerifierExample.cpp b/src/credentials/examples/DeviceAttestationVerifierExample.cpp index 32254161db8aca..b9bf08153004bf 100644 --- a/src/credentials/examples/DeviceAttestationVerifierExample.cpp +++ b/src/credentials/examples/DeviceAttestationVerifierExample.cpp @@ -17,6 +17,7 @@ #include "DeviceAttestationVerifierExample.h" #include +#include #include #include @@ -198,14 +199,20 @@ AttestationVerificationResult ExampleDACVerifier::VerifyAttestationInformation(c dacCertDerBuffer.data(), dacCertDerBuffer.size()) == CHIP_NO_ERROR, AttestationVerificationResult::kDacSignatureInvalid); - // TODO: Re-enable this when Construct/DeconstructAttestationElements methods are introduced -#if 0 - ReturnErrorOnFailure( - DeconstructAttestationElements(attestationElements, certDeclaration, attestationNonce, timestamp, firmwareInfo)); + ByteSpan certificationDeclarationSpan; + ByteSpan attestationNonceSpan; + uint32_t timestampDeconstructed; + ByteSpan firmwareInfoSpan; + std::vector vendorReservedDeconstructed; + uint16_t vendorIdDeconstructed; + uint16_t profileNumDeconstructed; + VerifyOrReturnError(DeconstructAttestationElements(attestationInfoBuffer, certificationDeclarationSpan, attestationNonceSpan, + timestampDeconstructed, firmwareInfoSpan, vendorReservedDeconstructed, + vendorIdDeconstructed, profileNumDeconstructed) == CHIP_NO_ERROR, + AttestationVerificationResult::kAttestationElementsMalformed); // Verify that Nonce matches with what we sent - VerifyOrReturnError(attestation_nonce.data_equal(attestationNonce), AttestationVerificationResult::kNonceMismatch); -#endif + VerifyOrReturnError(attestationNonceSpan.data_equal(attestationNonce), AttestationVerificationResult::kNonceMismatch); return AttestationVerificationResult::kSuccess; } diff --git a/src/credentials/tests/TestDeviceAttestationCredentials.cpp b/src/credentials/tests/TestDeviceAttestationCredentials.cpp index d8263792f8ff31..9ad44eddd6450b 100644 --- a/src/credentials/tests/TestDeviceAttestationCredentials.cpp +++ b/src/credentials/tests/TestDeviceAttestationCredentials.cpp @@ -156,25 +156,26 @@ static void TestDACVerifierExample_AttestationInfoVerification(nlTestSuite * inS CHIP_ERROR err = CHIP_NO_ERROR; uint8_t attestationElementsTestVector[] = { - 0x15, 0x30, 0x01, 0x70, 0xd2, 0x84, 0x4b, 0xa2, 0x01, 0x26, 0x04, 0x46, 0x63, 0x73, 0x61, 0x63, 0x64, 0x30, 0xa0, 0x58, - 0x1d, 0x15, 0x25, 0x01, 0xf1, 0xff, 0x25, 0x02, 0x01, 0x80, 0x25, 0x03, 0xd2, 0x04, 0x25, 0x04, 0x2e, 0x16, 0x24, 0x05, - 0xaa, 0x25, 0x06, 0xde, 0xc0, 0x25, 0x07, 0x94, 0x26, 0x18, 0x58, 0x40, 0x1f, 0x37, 0x2c, 0xaf, 0x6e, 0x78, 0x4d, 0x78, - 0x55, 0xd7, 0x81, 0x17, 0xeb, 0xef, 0x1c, 0x12, 0x98, 0x0f, 0xa6, 0xc2, 0x25, 0xc4, 0xad, 0x2e, 0x68, 0x44, 0x3d, 0x50, - 0x9e, 0xff, 0x20, 0xa7, 0x22, 0x7d, 0xe7, 0x0e, 0x80, 0xcb, 0x9b, 0x8b, 0xa0, 0xd4, 0x3f, 0x91, 0xba, 0xe1, 0xdd, 0xfb, - 0x3d, 0x59, 0xa7, 0x34, 0xb5, 0x37, 0xea, 0x41, 0x42, 0x0e, 0xb3, 0xe8, 0x6b, 0x3e, 0xbc, 0xbd, 0x30, 0x02, 0x20, 0xe0, - 0x42, 0x1b, 0x91, 0xc6, 0xfd, 0xcd, 0xb4, 0x0e, 0x2a, 0x4d, 0x2c, 0xf3, 0x1d, 0xb2, 0xb4, 0xe1, 0x8b, 0x41, 0x1b, 0x1d, - 0x3a, 0xd4, 0xd1, 0x2a, 0x9d, 0x90, 0xaa, 0x8e, 0x52, 0xfa, 0xe2, 0x26, 0x03, 0xfd, 0xc6, 0x5b, 0x28, 0x30, 0x05, 0x17, - 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x64, 0x31, 0x30, 0x07, 0x18, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x64, 0x33, 0x5f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18 + 0x15, 0x30, 0x01, 0x70, 0xd2, 0x84, 0x4b, 0xa2, 0x01, 0x26, 0x04, 0x46, 0x63, 0x73, 0x61, 0x63, 0x64, 0x30, 0xa0, + 0x58, 0x1d, 0x15, 0x25, 0x01, 0x88, 0x99, 0x25, 0x02, 0xfe, 0xff, 0x25, 0x03, 0xd2, 0x04, 0x25, 0x04, 0x2e, 0x16, + 0x24, 0x05, 0xaa, 0x25, 0x06, 0xde, 0xc0, 0x25, 0x07, 0x94, 0x26, 0x18, 0x58, 0x40, 0x96, 0x57, 0x2d, 0xd6, 0x3c, + 0x03, 0x64, 0x0b, 0x28, 0x67, 0x02, 0xbd, 0x6b, 0xba, 0x48, 0xac, 0x7c, 0x83, 0x54, 0x9b, 0x68, 0x73, 0x29, 0x47, + 0x48, 0xb9, 0x51, 0xd5, 0xab, 0x66, 0x62, 0x2e, 0x9d, 0x26, 0x10, 0x41, 0xf8, 0x0e, 0x97, 0x49, 0xfe, 0xff, 0x78, + 0x10, 0x02, 0x49, 0x67, 0xae, 0xdf, 0x41, 0x38, 0x36, 0x5b, 0x0a, 0x22, 0x57, 0x14, 0x9c, 0x9a, 0x12, 0x3e, 0x0d, + 0x30, 0xaa, 0x30, 0x02, 0x20, 0xe0, 0x42, 0x1b, 0x91, 0xc6, 0xfd, 0xcd, 0xb4, 0x0e, 0x2a, 0x4d, 0x2c, 0xf3, 0x1d, + 0xb2, 0xb4, 0xe1, 0x8b, 0x41, 0x1b, 0x1d, 0x3a, 0xd4, 0xd1, 0x2a, 0x9d, 0x90, 0xaa, 0x8e, 0x52, 0xfa, 0xe2, 0x26, + 0x03, 0xfd, 0xc6, 0x5b, 0x28, 0xd0, 0xf1, 0xff, 0x3e, 0x00, 0x01, 0x00, 0x17, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, + 0x5f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x31, 0xd0, 0xf1, + 0xff, 0x3e, 0x00, 0x03, 0x00, 0x18, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x64, 0x33, 0x5f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18 }; uint8_t attestationChallengeTestVector[] = { 0x7a, 0x49, 0x53, 0x05, 0xd0, 0x77, 0x79, 0xa4, 0x94, 0xdd, 0x39, 0xa0, 0x85, 0x1b, 0x66, 0x0d }; uint8_t attestationSignatureTestVector[] = { 0x79, 0x82, 0x53, 0x5d, 0x24, 0xcf, 0xe1, 0x4a, 0x71, 0xab, 0x04, 0x24, 0xcf, 0x0b, 0xac, 0xf1, 0xe3, 0x45, 0x48, 0x7e, 0xd5, 0x0f, 0x1a, 0xc0, 0xbc, 0x25, - 0x9e, 0xcc, 0xfb, 0x39, 0x08, 0x1e, 0x82, 0x0f, 0x43, 0x1c, 0x0d, 0x91, 0x49, - 0x7a, 0xd1, 0xb5, 0x00, 0xdc, 0x46, 0x7d, 0x7b, 0xc9, 0xf8, 0x68, 0x58, 0x7f, - 0xf8, 0x43, 0xee, 0x78, 0x15, 0xf4, 0x88, 0x98, 0x31, 0x30, 0xc6, 0x9d }; + 0x9e, 0xcc, 0xfb, 0x39, 0x08, 0x1e, 0x23, 0x71, 0xd1, 0x82, 0xfe, 0x46, 0x03, + 0x9b, 0x7b, 0xf2, 0x0f, 0x78, 0x72, 0x2f, 0xdb, 0x8f, 0x6d, 0x29, 0xd9, 0x8c, + 0xca, 0x55, 0x55, 0xb4, 0x1b, 0x6c, 0x3e, 0x96, 0x0d, 0x97, 0x14, 0x41 }; uint8_t attestationNonceTestVector[] = { 0xe0, 0x42, 0x1b, 0x91, 0xc6, 0xfd, 0xcd, 0xb4, 0x0e, 0x2a, 0x4d, 0x2c, 0xf3, 0x1d, 0xb2, 0xb4, 0xe1, 0x8b, 0x41, 0x1b, 0x1d, 0x3a, 0xd4, 0xd1, 0x2a, 0x9d, 0x90, 0xaa, 0x8e, 0x52, 0xfa, 0xe2 }; diff --git a/src/darwin/Framework/CHIP/zap-generated/CHIPCallbackBridge.mm b/src/darwin/Framework/CHIP/zap-generated/CHIPCallbackBridge.mm index f3f5ae1336db04..a1d04c85dd0a7b 100644 --- a/src/darwin/Framework/CHIP/zap-generated/CHIPCallbackBridge.mm +++ b/src/darwin/Framework/CHIP/zap-generated/CHIPCallbackBridge.mm @@ -922,6 +922,22 @@ }); }; +void CHIPOperationalCredentialsClusterAttestationResponseCallbackBridge::OnSuccessFn( + void * context, chip::ByteSpan AttestationElements, chip::ByteSpan Signature) +{ + DispatchSuccess(context, @ { + @"AttestationElements" : [NSData dataWithBytes:AttestationElements.data() length:AttestationElements.size()], + @"Signature" : [NSData dataWithBytes:Signature.data() length:Signature.size()], + }); +}; + +void CHIPOperationalCredentialsClusterCertChainResponseCallbackBridge::OnSuccessFn(void * context, chip::ByteSpan Certificate) +{ + DispatchSuccess(context, @ { + @"Certificate" : [NSData dataWithBytes:Certificate.data() length:Certificate.size()], + }); +}; + void CHIPOperationalCredentialsClusterNOCResponseCallbackBridge::OnSuccessFn( void * context, uint8_t StatusCode, uint8_t FabricIndex, chip::ByteSpan DebugText) { diff --git a/src/darwin/Framework/CHIP/zap-generated/CHIPCallbackBridge_internal.h b/src/darwin/Framework/CHIP/zap-generated/CHIPCallbackBridge_internal.h index 554bd5cf9675fd..1719496ff25d5e 100644 --- a/src/darwin/Framework/CHIP/zap-generated/CHIPCallbackBridge_internal.h +++ b/src/darwin/Framework/CHIP/zap-generated/CHIPCallbackBridge_internal.h @@ -1054,6 +1054,30 @@ class CHIPOtaSoftwareUpdateProviderClusterQueryImageResponseCallbackBridge chip::ByteSpan metadataForRequestor); }; +class CHIPOperationalCredentialsClusterAttestationResponseCallbackBridge + : public CHIPCallbackBridge +{ +public: + CHIPOperationalCredentialsClusterAttestationResponseCallbackBridge(dispatch_queue_t queue, ResponseHandler handler, + CHIPActionBlock action, bool keepAlive = false) : + CHIPCallbackBridge(queue, handler, action, OnSuccessFn, + keepAlive){}; + + static void OnSuccessFn(void * context, chip::ByteSpan AttestationElements, chip::ByteSpan Signature); +}; + +class CHIPOperationalCredentialsClusterCertChainResponseCallbackBridge + : public CHIPCallbackBridge +{ +public: + CHIPOperationalCredentialsClusterCertChainResponseCallbackBridge(dispatch_queue_t queue, ResponseHandler handler, + CHIPActionBlock action, bool keepAlive = false) : + CHIPCallbackBridge(queue, handler, action, OnSuccessFn, + keepAlive){}; + + static void OnSuccessFn(void * context, chip::ByteSpan Certificate); +}; + class CHIPOperationalCredentialsClusterNOCResponseCallbackBridge : public CHIPCallbackBridge { diff --git a/src/darwin/Framework/CHIP/zap-generated/CHIPClustersObjc.h b/src/darwin/Framework/CHIP/zap-generated/CHIPClustersObjc.h index 08c29203977e78..5f34f9b98df941 100644 --- a/src/darwin/Framework/CHIP/zap-generated/CHIPClustersObjc.h +++ b/src/darwin/Framework/CHIP/zap-generated/CHIPClustersObjc.h @@ -1098,6 +1098,8 @@ NS_ASSUME_NONNULL_BEGIN adminVendorId:(uint16_t)adminVendorId responseHandler:(ResponseHandler)responseHandler; - (void)addTrustedRootCertificate:(NSData *)rootCertificate responseHandler:(ResponseHandler)responseHandler; +- (void)attestationRequest:(NSData *)attestationNonce responseHandler:(ResponseHandler)responseHandler; +- (void)certChainRequest:(uint16_t)certChainType responseHandler:(ResponseHandler)responseHandler; - (void)opCSRRequest:(NSData *)cSRNonce responseHandler:(ResponseHandler)responseHandler; - (void)removeFabric:(uint8_t)fabricIndex responseHandler:(ResponseHandler)responseHandler; - (void)removeTrustedRootCertificate:(NSData *)trustedRootIdentifier responseHandler:(ResponseHandler)responseHandler; diff --git a/src/darwin/Framework/CHIP/zap-generated/CHIPClustersObjc.mm b/src/darwin/Framework/CHIP/zap-generated/CHIPClustersObjc.mm index 48aca92da368f4..ce477aa0396adf 100644 --- a/src/darwin/Framework/CHIP/zap-generated/CHIPClustersObjc.mm +++ b/src/darwin/Framework/CHIP/zap-generated/CHIPClustersObjc.mm @@ -3145,6 +3145,22 @@ new CHIPDefaultSuccessCallbackBridge(self.callbackQueue, responseHandler, ^(Canc }); } +- (void)attestationRequest:(NSData *)attestationNonce responseHandler:(ResponseHandler)responseHandler +{ + new CHIPOperationalCredentialsClusterAttestationResponseCallbackBridge( + self.callbackQueue, responseHandler, ^(Cancelable * success, Cancelable * failure) { + return self.cppCluster.AttestationRequest(success, failure, [self asSpan:attestationNonce]); + }); +} + +- (void)certChainRequest:(uint16_t)certChainType responseHandler:(ResponseHandler)responseHandler +{ + new CHIPOperationalCredentialsClusterCertChainResponseCallbackBridge( + self.callbackQueue, responseHandler, ^(Cancelable * success, Cancelable * failure) { + return self.cppCluster.CertChainRequest(success, failure, certChainType); + }); +} + - (void)opCSRRequest:(NSData *)cSRNonce responseHandler:(ResponseHandler)responseHandler { new CHIPOperationalCredentialsClusterOpCSRResponseCallbackBridge( diff --git a/src/protocols/secure_channel/RendezvousParameters.h b/src/protocols/secure_channel/RendezvousParameters.h index 17bb455d49e7f0..992012e435f53c 100644 --- a/src/protocols/secure_channel/RendezvousParameters.h +++ b/src/protocols/secure_channel/RendezvousParameters.h @@ -64,6 +64,7 @@ class RendezvousParameters bool HasPeerAddress() const { return mPeerAddress.IsInitialized(); } Transport::PeerAddress GetPeerAddress() const { return mPeerAddress; } const Optional GetCSRNonce() const { return mCSRNonce; } + const Optional GetAttestationNonce() const { return mAttestationNonce; } RendezvousParameters & SetPeerAddress(const Transport::PeerAddress & peerAddress) { mPeerAddress = peerAddress; @@ -77,6 +78,13 @@ class RendezvousParameters return *this; } + // The lifetime of the buffer attestationNonce is pointing to, should exceed the lifetime of RendezvousParameter object. + RendezvousParameters & SetAttestationNonce(ByteSpan attestationNonce) + { + mAttestationNonce.SetValue(attestationNonce); + return *this; + } + bool HasDiscriminator() const { return mDiscriminator <= kMaxRendezvousDiscriminatorValue; } uint16_t GetDiscriminator() const { return mDiscriminator; } RendezvousParameters & SetDiscriminator(uint16_t discriminator) @@ -103,6 +111,7 @@ class RendezvousParameters bool HasPASEVerifier() const { return mHasPASEVerifier; } bool HasCSRNonce() const { return mCSRNonce.HasValue(); } + bool HasAttestationNonce() const { return mAttestationNonce.HasValue(); } const PASEVerifier & GetPASEVerifier() const { return mPASEVerifier; } RendezvousParameters & SetPASEVerifier(PASEVerifier & verifier) { @@ -148,6 +157,7 @@ class RendezvousParameters uint32_t mSetupPINCode = 0; ///< the target peripheral setup PIN Code uint16_t mDiscriminator = UINT16_MAX; ///< the target peripheral discriminator Optional mCSRNonce; ///< CSR Nonce passed by the commissioner + Optional mAttestationNonce; ///< Attestation Nonce passed by the commissioner PASEVerifier mPASEVerifier; bool mHasPASEVerifier = false; diff --git a/src/transport/SecureSession.h b/src/transport/SecureSession.h index 2b31fdeed7eb57..7e3d8fb01422d1 100644 --- a/src/transport/SecureSession.h +++ b/src/transport/SecureSession.h @@ -113,6 +113,8 @@ class DLL_EXPORT SecureSession CHIP_ERROR Decrypt(const uint8_t * input, size_t input_length, uint8_t * output, const PacketHeader & header, const MessageAuthenticationCode & mac) const; + ByteSpan GetAttestationChallenge() const { return ByteSpan(mKeys[kAttestationChallengeKey], kAES_CCM128_Key_Length); } + /** * @brief * Memory overhead of encrypting data. The overhead is independent of size of