From d5e476f8ffab19cbbc22af471fcf66ea82918d91 Mon Sep 17 00:00:00 2001 From: Marty Leisner Date: Wed, 8 Sep 2021 22:17:47 -0400 Subject: [PATCH] Added CertChainRequest and AttestationRequest commands to lighting-app ZAP script Introduced CertChainRequest, CertChainResponse, AttestationRequest and AttestationResponse commands into operational-credentials-cluster.xml Added the above commands' callbacks to operational-credentials-server.cpp Introduced the Attestation Nonce buffer to CHIPDevice Class Added Certificate Chain Request and Attestation Request Commands to CHIPDeviceController Added global method to Server.cpp in order to retrieve Server's SecureSessionMgr (needed to retrieve Attestation Challenge) Added support to DAC/PAI certificates in CHIPDevice Include files from the DA constructor PR src/credentials/DeviceAttestationConstructor.cpp src/credentials/DeviceAttestationConstructor.h --- .../lighting-common/lighting-app.zap | 32 +++ .../operational-credentials-server.cpp | 139 ++++++++++ .../chip/operational-credentials-cluster.xml | 21 ++ src/controller/CHIPDevice.cpp | 78 ++++++ src/controller/CHIPDevice.h | 31 ++- src/controller/CHIPDeviceController.cpp | 201 +++++++++++++- src/controller/CHIPDeviceController.h | 31 +++ .../data_model/controller-clusters.zap | 32 +++ .../java/zap-generated/CHIPClusters-JNI.cpp | 258 ++++++++++++++++++ .../chip/devicecontroller/ChipClusters.java | 26 ++ .../python/chip/clusters/CHIPClusters.cpp | 20 ++ .../python/chip/clusters/CHIPClusters.py | 28 ++ src/credentials/BUILD.gn | 2 + .../DeviceAttestationConstructor.cpp | 173 ++++++++++++ .../DeviceAttestationConstructor.h | 61 +++++ src/credentials/DeviceAttestationVerifier.h | 4 +- .../DeviceAttestationVerifierExample.cpp | 19 +- .../TestDeviceAttestationCredentials.cpp | 29 +- .../CHIP/zap-generated/CHIPCallbackBridge.mm | 16 ++ .../CHIPCallbackBridge_internal.h | 24 ++ .../CHIP/zap-generated/CHIPClustersObjc.h | 2 + .../CHIP/zap-generated/CHIPClustersObjc.mm | 16 ++ .../secure_channel/RendezvousParameters.h | 10 + src/transport/SecureSession.h | 2 + 24 files changed, 1223 insertions(+), 32 deletions(-) create mode 100644 src/credentials/DeviceAttestationConstructor.cpp create mode 100644 src/credentials/DeviceAttestationConstructor.h 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