diff --git a/examples/chip-tool/commands/clusters/Commands.h b/examples/chip-tool/commands/clusters/Commands.h index 40b01d7788a1cc..8242bfbd900eac 100644 --- a/examples/chip-tool/commands/clusters/Commands.h +++ b/examples/chip-tool/commands/clusters/Commands.h @@ -597,9 +597,8 @@ static void OnOperationalCredentialsClusterNOCResponse(void * context, uint8_t S command->SetCommandExitStatus(CHIP_NO_ERROR); } -static void OnOperationalCredentialsClusterOpCSRResponse(void * context, chip::ByteSpan CSR, chip::ByteSpan CSRNonce, - chip::ByteSpan VendorReserved1, chip::ByteSpan VendorReserved2, - chip::ByteSpan VendorReserved3, chip::ByteSpan Signature) +static void OnOperationalCredentialsClusterOpCSRResponse(void * context, chip::ByteSpan NOCSRElements, + chip::ByteSpan AttestationSignature) { ChipLogProgress(chipTool, "OperationalCredentialsClusterOpCSRResponse"); 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 7a4293b2b93acf..71bb97e599bf06 100644 --- a/src/app/clusters/operational-credentials-server/operational-credentials-server.cpp +++ b/src/app/clusters/operational-credentials-server/operational-credentials-server.cpp @@ -45,6 +45,9 @@ using namespace chip; using namespace ::chip::DeviceLayer; using namespace ::chip::Transport; +// As per specifications section 11.22.5.1. Constant RESP_MAX +constexpr uint16_t kMaxRspLen = 900; + /* * Temporary flow for fabric management until addOptCert + fabric index are implemented: * 1) When Commissioner pairs with CHIP device, store device nodeId in Fabric table as NodeId @@ -446,12 +449,16 @@ bool emberAfOperationalCredentialsClusterOpCSRRequestCallback(chip::EndpointId e chip::Platform::ScopedMemoryBuffer csr; size_t csrLength = Crypto::kMAX_CSR_Length; + chip::Platform::ScopedMemoryBuffer csrElements; + emberAfPrintln(EMBER_AF_PRINT_DEBUG, "OpCreds: commissioner has requested an OpCSR"); app::CommandPathParams cmdParams = { emberAfCurrentEndpoint(), /* group id */ 0, ZCL_OPERATIONAL_CREDENTIALS_CLUSTER_ID, ZCL_OP_CSR_RESPONSE_COMMAND_ID, (chip::app::CommandPathFlags::kEndpointIdValid) }; TLV::TLVWriter * writer = nullptr; + TLV::TLVWriter csrElementWriter; + TLV::TLVType containerType; // Fetch current fabric FabricInfo * fabric = retrieveCurrentFabric(); @@ -471,16 +478,28 @@ bool emberAfOperationalCredentialsClusterOpCSRRequestCallback(chip::EndpointId e VerifyOrExit(err == CHIP_NO_ERROR, status = EMBER_ZCL_STATUS_FAILURE); VerifyOrExit(csrLength < UINT8_MAX, status = EMBER_ZCL_STATUS_FAILURE); + VerifyOrExit(csrElements.Alloc(kMaxRspLen), status = EMBER_ZCL_STATUS_FAILURE); + csrElementWriter.Init(csrElements.Get(), kMaxRspLen); + + SuccessOrExit(err = csrElementWriter.StartContainer(TLV::AnonymousTag, TLV::TLVType::kTLVType_Structure, containerType)); + SuccessOrExit(err = csrElementWriter.Put(TLV::ContextTag(1), ByteSpan(csr.Get(), csrLength))); + SuccessOrExit(err = csrElementWriter.Put(TLV::ContextTag(2), CSRNonce)); + SuccessOrExit(err = csrElementWriter.Put(TLV::ContextTag(3), ByteSpan())); + SuccessOrExit(err = csrElementWriter.Put(TLV::ContextTag(4), ByteSpan())); + SuccessOrExit(err = csrElementWriter.Put(TLV::ContextTag(5), ByteSpan())); + SuccessOrExit(err = csrElementWriter.EndContainer(containerType)); + SuccessOrExit(err = csrElementWriter.Finalize()); + VerifyOrExit(commandObj != nullptr, err = CHIP_ERROR_INCORRECT_STATE); SuccessOrExit(err = commandObj->PrepareCommand(cmdParams)); writer = commandObj->GetCommandDataElementTLVWriter(); - SuccessOrExit(err = writer->Put(TLV::ContextTag(0), ByteSpan(csr.Get(), csrLength))); - SuccessOrExit(err = writer->Put(TLV::ContextTag(1), CSRNonce)); - SuccessOrExit(err = writer->Put(TLV::ContextTag(2), ByteSpan(nullptr, 0))); - SuccessOrExit(err = writer->Put(TLV::ContextTag(3), ByteSpan(nullptr, 0))); - SuccessOrExit(err = writer->Put(TLV::ContextTag(4), ByteSpan(nullptr, 0))); - SuccessOrExit(err = writer->Put(TLV::ContextTag(5), ByteSpan(nullptr, 0))); + + // Write CSR Elements + SuccessOrExit(err = writer->Put(TLV::ContextTag(0), ByteSpan(csrElements.Get(), csrElementWriter.GetLengthWritten()))); + + // TODO - Write attestation signature using attestation key + SuccessOrExit(err = writer->Put(TLV::ContextTag(1), ByteSpan())); SuccessOrExit(err = commandObj->FinishCommand()); exit: diff --git a/src/app/common/gen/callback.h b/src/app/common/gen/callback.h index 44985de656e632..5f719c72b88fc1 100644 --- a/src/app/common/gen/callback.h +++ b/src/app/common/gen/callback.h @@ -15815,9 +15815,7 @@ bool emberAfOperationalCredentialsClusterOpCSRRequestCallback(chip::EndpointId e * @brief Cluster OpCSRResponse Command callback (from server) */ bool emberAfOperationalCredentialsClusterOpCSRResponseCallback(chip::EndpointId endpoint, chip::app::CommandSender * commandObj, - chip::ByteSpan CSR, chip::ByteSpan CSRNonce, - chip::ByteSpan VendorReserved1, chip::ByteSpan VendorReserved2, - chip::ByteSpan VendorReserved3, chip::ByteSpan Signature); + chip::ByteSpan NOCSRElements, chip::ByteSpan AttestationSignature); /** * @brief Cluster AddNOC Command callback (from client) */ diff --git a/src/app/common/gen/client-command-macro.h b/src/app/common/gen/client-command-macro.h index bf88a7e4190daa..fe0b41818cace1 100644 --- a/src/app/common/gen/client-command-macro.h +++ b/src/app/common/gen/client-command-macro.h @@ -2244,19 +2244,14 @@ /** @brief Command description for OpCSRResponse * * Command: OpCSRResponse - * @param CSR OCTET_STRING - * @param CSRNonce OCTET_STRING - * @param VendorReserved1 OCTET_STRING - * @param VendorReserved2 OCTET_STRING - * @param VendorReserved3 OCTET_STRING - * @param Signature OCTET_STRING + * @param NOCSRElements OCTET_STRING + * @param AttestationSignature OCTET_STRING */ #define emberAfFillCommandOperational \ - CredentialsClusterOpCSRResponse(CSR, CSRNonce, VendorReserved1, VendorReserved2, VendorReserved3, Signature) \ + CredentialsClusterOpCSRResponse(NOCSRElements, AttestationSignature) \ emberAfFillExternalBuffer(mask, \ \ - ZCL_OP_CSR_RESPONSE_COMMAND_ID, "uuuuuu", CSR, CSRNonce, VendorReserved1, VendorReserved2, \ - VendorReserved3, Signature); + ZCL_OP_CSR_RESPONSE_COMMAND_ID, "uu", NOCSRElements, AttestationSignature); /** @brief Command description for AddNOC * 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 b991c6709dde30..e4a83a20d8db90 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 @@ -95,12 +95,8 @@ fabric-scoped data. A certificate signing request (CSR) from the server. - - - - - - + + diff --git a/src/controller/CHIPDevice.cpp b/src/controller/CHIPDevice.cpp index 1325eb5aa08684..743261d67375a1 100644 --- a/src/controller/CHIPDevice.cpp +++ b/src/controller/CHIPDevice.cpp @@ -694,5 +694,12 @@ Device::~Device() } } +CHIP_ERROR Device::ReduceNOCChainBufferSize(size_t new_size) +{ + ReturnErrorCodeIf(new_size > sizeof(mNOCChainBuffer), CHIP_ERROR_INVALID_ARGUMENT); + mNOCChainBufferSize = new_size; + return CHIP_NO_ERROR; +} + } // namespace Controller } // namespace chip diff --git a/src/controller/CHIPDevice.h b/src/controller/CHIPDevice.h index 7f80f79d270e27..eea68fcee71dcc 100644 --- a/src/controller/CHIPDevice.h +++ b/src/controller/CHIPDevice.h @@ -382,6 +382,12 @@ class DLL_EXPORT Device : public Messaging::ExchangeDelegate, public SessionEsta ByteSpan GetCSRNonce() const { return ByteSpan(mCSRNonce, sizeof(mCSRNonce)); } + MutableByteSpan GetMutableNOCChain() { return MutableByteSpan(mNOCChainBuffer, sizeof(mNOCChainBuffer)); } + + CHIP_ERROR ReduceNOCChainBufferSize(size_t new_size); + + ByteSpan GetNOCChain() const { return ByteSpan(mNOCChainBuffer, mNOCChainBufferSize); } + /* * This function can be called to establish a secure session with the device. * @@ -490,6 +496,10 @@ class DLL_EXPORT Device : public Messaging::ExchangeDelegate, public SessionEsta uint8_t mCSRNonce[kOpCSRNonceLength]; + // The chain can contain ICAC and OpCert + uint8_t mNOCChainBuffer[Credentials::kMaxCHIPCertLength * 2]; + size_t mNOCChainBufferSize = 0; + SessionIDAllocator * mIDAllocator = nullptr; Callback::CallbackDeque mConnectionSuccess; diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index 77af68346b9260..b55a927cc025a1 100644 --- a/src/controller/CHIPDeviceController.cpp +++ b/src/controller/CHIPDeviceController.cpp @@ -103,7 +103,7 @@ constexpr uint32_t kSessionEstablishmentTimeout = 30 * kMillisecondsPerSecond; constexpr uint32_t kMaxCHIPCSRLength = 1024; -DeviceController::DeviceController() : mLocalNOCCallback(OnLocalNOCGenerated, this) +DeviceController::DeviceController() : mLocalNOCChainCallback(OnLocalNOCChainGeneration, this) { mState = State::NotInitialized; mSessionMgr = nullptr; @@ -213,38 +213,8 @@ CHIP_ERROR DeviceController::Init(NodeId localDeviceId, ControllerInitParams par return CHIP_NO_ERROR; } -CHIP_ERROR DeviceController::GenerateOperationalCertificates(const ByteSpan & noc, MutableByteSpan & cert) -{ - // This code requires about 1K RAM to generate the certificates. - // The code would run as part of commissioner applications, so RAM requirements should be fine. - // Need to analyze if this requirement could be better managed by using static memory pools. - chip::Platform::ScopedMemoryBuffer ica; - ReturnErrorCodeIf(!ica.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY); - - MutableByteSpan icaCertSpan(ica.Get(), kMaxCHIPDERCertLength); - - ChipLogProgress(Controller, "Getting intermediate CA certificate from the issuer"); - CHIP_ERROR err = mOperationalCredentialsDelegate->GetIntermediateCACertificate(0, icaCertSpan); - ChipLogProgress(Controller, "GetIntermediateCACertificate returned %" CHIP_ERROR_FORMAT, ChipError::FormatError(err)); - if (err == CHIP_ERROR_INTERMEDIATE_CA_NOT_REQUIRED) - { - // This implies that the commissioner application uses root CA to sign the operational - // certificates, and an intermediate CA is not needed. It's not an error condition, so - // let's just send operational certificate and root CA certificate to the device. - icaCertSpan.reduce_size(0); - ChipLogProgress(Controller, "Intermediate CA is not needed"); - } - else if (err != CHIP_NO_ERROR) - { - return err; - } - - ReturnErrorOnFailure(ConvertX509CertsToChipCertArray(noc, icaCertSpan, cert)); - - return CHIP_NO_ERROR; -} - -void DeviceController::OnLocalNOCGenerated(void * context, const ByteSpan & noc) +void DeviceController::OnLocalNOCChainGeneration(void * context, CHIP_ERROR status, const ByteSpan & noc, const ByteSpan & icac, + const ByteSpan & rcac) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -252,25 +222,45 @@ void DeviceController::OnLocalNOCGenerated(void * context, const ByteSpan & noc) Transport::FabricInfo * const fabric = controller->mFabrics.FindFabricWithIndex(controller->mFabricIndex); - uint32_t chipCertAllocatedLen = kMaxCHIPCertLength * 2; + constexpr uint32_t chipCertAllocatedLen = kMaxCHIPCertLength * 2; chip::Platform::ScopedMemoryBuffer chipCert; + uint32_t chipCertLen = 0; + + // Check if the callback returned a failure + VerifyOrExit(status == CHIP_NO_ERROR, err = status); + VerifyOrExit(fabric != nullptr, err = CHIP_ERROR_INCORRECT_STATE); VerifyOrExit(chipCert.Alloc(chipCertAllocatedLen), err = CHIP_ERROR_NO_MEMORY); + err = ConvertX509CertToChipCert(rcac, chipCert.Get(), chipCertAllocatedLen, chipCertLen); + SuccessOrExit(err); + + err = fabric->SetRootCert(ByteSpan(chipCert.Get(), chipCertLen)); + SuccessOrExit(err); + + if (icac.empty()) + { + ChipLogProgress(Controller, "Intermediate CA is not needed"); + } + { MutableByteSpan chipCertSpan(chipCert.Get(), chipCertAllocatedLen); - err = controller->GenerateOperationalCertificates(noc, chipCertSpan); + + err = ConvertX509CertsToChipCertArray(noc, icac, chipCertSpan); SuccessOrExit(err); err = fabric->SetOperationalCertsFromCertArray(chipCertSpan); SuccessOrExit(err); } + err = controller->mFabrics.Store(fabric->GetFabricIndex()); + exit: if (err != CHIP_NO_ERROR) { ChipLogError(Controller, "Failed in generating local operational credentials. Error %s", ErrorStr(err)); + controller->mState = State::NotInitialized; } } @@ -283,37 +273,40 @@ CHIP_ERROR DeviceController::LoadLocalCredentials(Transport::FabricInfo * fabric if (!fabric->AreCredentialsAvailable()) { - chip::Platform::ScopedMemoryBuffer chipCert; - uint32_t chipCertAllocatedLen = kMaxCHIPCertLength * 2; - ReturnErrorCodeIf(!chipCert.Alloc(chipCertAllocatedLen), CHIP_ERROR_NO_MEMORY); - uint32_t chipCertLen = 0; + chip::Platform::ScopedMemoryBuffer CSR; + size_t csrLength = kMaxCHIPCSRLength; + ReturnErrorCodeIf(!CSR.Alloc(csrLength), CHIP_ERROR_NO_MEMORY); - // Get root CA certificate - { - ChipLogProgress(Controller, "Getting root certificate for the controller from the issuer"); - chip::Platform::ScopedMemoryBuffer rootCert; - ReturnErrorCodeIf(!rootCert.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY); - MutableByteSpan rootCertSpan(rootCert.Get(), kMaxCHIPDERCertLength); - ReturnErrorOnFailure(mOperationalCredentialsDelegate->GetRootCACertificate(0, rootCertSpan)); - VerifyOrReturnError(CanCastTo(rootCertSpan.size()), CHIP_ERROR_INVALID_ARGUMENT); - ReturnErrorOnFailure(ConvertX509CertToChipCert(rootCertSpan, chipCert.Get(), chipCertAllocatedLen, chipCertLen)); - ReturnErrorOnFailure(fabric->SetRootCert(ByteSpan(chipCert.Get(), chipCertLen))); - } + ReturnErrorOnFailure(keypair->NewCertificateSigningRequest(CSR.Get(), csrLength)); - // Generate Operational Certificates (NOC and ICAC) - { - chip::Platform::ScopedMemoryBuffer CSR; - size_t csrLength = kMaxCHIPCSRLength; - ReturnErrorCodeIf(!CSR.Alloc(csrLength), CHIP_ERROR_NO_MEMORY); + ChipLogProgress(Controller, "Getting certificate chain for the controller from the issuer"); - ReturnErrorOnFailure(keypair->NewCertificateSigningRequest(CSR.Get(), csrLength)); + // As per specifications section 11.22.5.1. Constant RESP_MAX + constexpr uint16_t kMaxRspLen = 900; + chip::Platform::ScopedMemoryBuffer csrElements; + ReturnErrorCodeIf(!csrElements.Alloc(kMaxRspLen), CHIP_ERROR_NO_MEMORY); - // TODO - Match the generated cert against CSR and operational keypair - // Make sure it chains back to the trusted root. - ChipLogProgress(Controller, "Generating operational certificate for the controller"); - ReturnErrorOnFailure(mOperationalCredentialsDelegate->GenerateNodeOperationalCertificate( - Optional(mLocalDeviceId), 0, ByteSpan(CSR.Get(), csrLength), ByteSpan(), &mLocalNOCCallback)); - } + TLV::TLVWriter csrElementWriter; + TLV::TLVType containerType; + csrElementWriter.Init(csrElements.Get(), kMaxRspLen); + ReturnErrorOnFailure(csrElementWriter.StartContainer(TLV::AnonymousTag, TLV::TLVType::kTLVType_Structure, containerType)); + ReturnErrorOnFailure(csrElementWriter.Put(TLV::ContextTag(1), ByteSpan(CSR.Get(), csrLength))); + + // TODO - Need a mechanism to generate CSRNonce for commissioner's CSR + ReturnErrorOnFailure(csrElementWriter.Put(TLV::ContextTag(2), ByteSpan())); + ReturnErrorOnFailure(csrElementWriter.Put(TLV::ContextTag(3), ByteSpan())); + ReturnErrorOnFailure(csrElementWriter.Put(TLV::ContextTag(4), ByteSpan())); + ReturnErrorOnFailure(csrElementWriter.Put(TLV::ContextTag(5), ByteSpan())); + ReturnErrorOnFailure(csrElementWriter.EndContainer(containerType)); + ReturnErrorOnFailure(csrElementWriter.Finalize()); + + mOperationalCredentialsDelegate->SetNodeIdForNextNOCRequest(mLocalDeviceId); + mOperationalCredentialsDelegate->SetFabricIdForNextNOCRequest(0); + + // TODO - Need a mechanism to generate signature for commissioner's CSR + ReturnErrorOnFailure(mOperationalCredentialsDelegate->GenerateNOCChain( + ByteSpan(csrElements.Get(), csrElementWriter.GetLengthWritten()), ByteSpan(), ByteSpan(), ByteSpan(), ByteSpan(), + &mLocalNOCChainCallback)); ReturnErrorOnFailure(mFabrics.Store(fabric->GetFabricIndex())); } @@ -813,7 +806,7 @@ DeviceCommissioner::DeviceCommissioner() : mNOCResponseCallback(OnOperationalCertificateAddResponse, this), mRootCertResponseCallback(OnRootCertSuccessResponse, this), mOnCSRFailureCallback(OnCSRFailureResponse, this), mOnCertFailureCallback(OnAddNOCFailureResponse, this), mOnRootCertFailureCallback(OnRootCertFailureResponse, this), mOnDeviceConnectedCallback(OnDeviceConnectedFn, this), - mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureFn, this), mDeviceNOCCallback(OnDeviceNOCGenerated, this) + mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureFn, this), mDeviceNOCChainCallback(OnDeviceNOCChainGeneration, this) { mPairingDelegate = nullptr; mDeviceBeingPaired = kNumMaxActiveDevices; @@ -1206,10 +1199,10 @@ void DeviceCommissioner::OnSessionEstablished() if (sendOperationalCertsImmediately) { - err = SendTrustedRootCertificate(device); + err = SendOperationalCertificateSigningRequestCommand(device); if (err != CHIP_NO_ERROR) { - ChipLogError(Ble, "Failed in sending 'add trusted root' command to the device: err %s", ErrorStr(err)); + ChipLogError(Ble, "Failed in sending 'CSR request' command to the device: err %s", ErrorStr(err)); OnSessionEstablishmentError(err); return; } @@ -1248,9 +1241,8 @@ void DeviceCommissioner::OnCSRFailureResponse(void * context, uint8_t status) commissioner->OnSessionEstablishmentError(CHIP_ERROR_INTERNAL); } -void DeviceCommissioner::OnOperationalCertificateSigningRequest(void * context, ByteSpan CSR, ByteSpan CSRNonce, - ByteSpan VendorReserved1, ByteSpan VendorReserved2, - ByteSpan VendorReserved3, ByteSpan Signature) +void DeviceCommissioner::OnOperationalCertificateSigningRequest(void * context, ByteSpan NOCSRElements, + ByteSpan AttestationSignature) { ChipLogProgress(Controller, "Received certificate signing request from the device"); DeviceCommissioner * commissioner = static_cast(context); @@ -1258,7 +1250,7 @@ void DeviceCommissioner::OnOperationalCertificateSigningRequest(void * context, commissioner->mOpCSRResponseCallback.Cancel(); commissioner->mOnCSRFailureCallback.Cancel(); - if (commissioner->ProcessOpCSR(CSR, CSRNonce, VendorReserved1, VendorReserved2, VendorReserved3, Signature) != CHIP_NO_ERROR) + if (commissioner->ProcessOpCSR(NOCSRElements, AttestationSignature) != CHIP_NO_ERROR) { // Handle error, and notify session failure to the commissioner application. ChipLogError(Controller, "Failed to process the certificate signing request"); @@ -1267,32 +1259,46 @@ void DeviceCommissioner::OnOperationalCertificateSigningRequest(void * context, } } -void DeviceCommissioner::OnDeviceNOCGenerated(void * context, const ByteSpan & noc) +void DeviceCommissioner::OnDeviceNOCChainGeneration(void * context, CHIP_ERROR status, const ByteSpan & noc, const ByteSpan & icac, + const ByteSpan & rcac) { CHIP_ERROR err = CHIP_NO_ERROR; DeviceCommissioner * commissioner = static_cast(context); - // The operational certificate array can contain upto 2 certificates (NOC, and ICAC) - // The memory is allocated to account for both these certificates. - uint32_t chipCertAllocatedLen = kMaxCHIPCertLength * 2; - chip::Platform::ScopedMemoryBuffer chipCert; - + ChipLogError(Controller, "Received callback from the CA for NOC Chain generation. Status %s", ErrorStr(status)); Device * device = nullptr; VerifyOrExit(commissioner->mState == State::Initialized, err = CHIP_ERROR_INCORRECT_STATE); VerifyOrExit(commissioner->mDeviceBeingPaired < kNumMaxActiveDevices, err = CHIP_ERROR_INCORRECT_STATE); + // Check if the callback returned a failure + VerifyOrExit(status == CHIP_NO_ERROR, err = status); + + // TODO - Verify that the generated root cert matches with commissioner's root cert + device = &commissioner->mActiveDevices[commissioner->mDeviceBeingPaired]; - VerifyOrExit(chipCert.Alloc(chipCertAllocatedLen), err = CHIP_ERROR_NO_MEMORY); + { + MutableByteSpan rootCert = device->GetMutableNOCChain(); + + uint32_t certLen = (rootCert.size() > UINT32_MAX) ? UINT32_MAX : static_cast(rootCert.size()); + + err = ConvertX509CertToChipCert(rcac, rootCert.data(), certLen, certLen); + SuccessOrExit(err); + + rootCert.reduce_size(certLen); + + err = commissioner->SendTrustedRootCertificate(device, rootCert); + SuccessOrExit(err); + } { - MutableByteSpan chipCertSpan(chipCert.Get(), chipCertAllocatedLen); - err = commissioner->GenerateOperationalCertificates(noc, chipCertSpan); + MutableByteSpan certChain = device->GetMutableNOCChain(); + + err = ConvertX509CertsToChipCertArray(noc, icac, certChain); SuccessOrExit(err); - ChipLogProgress(Controller, "Sending operational certificate to the device"); - err = commissioner->SendOperationalCertificate(device, chipCertSpan); + err = device->ReduceNOCChainBufferSize(certChain.size()); SuccessOrExit(err); } @@ -1304,29 +1310,20 @@ void DeviceCommissioner::OnDeviceNOCGenerated(void * context, const ByteSpan & n } } -CHIP_ERROR DeviceCommissioner::ProcessOpCSR(const ByteSpan & CSR, const ByteSpan & CSRNonce, const ByteSpan & VendorReserved1, - const ByteSpan & VendorReserved2, const ByteSpan & VendorReserved3, - const ByteSpan & Signature) +CHIP_ERROR DeviceCommissioner::ProcessOpCSR(const ByteSpan & NOCSRElements, const ByteSpan & AttestationSignature) { VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mDeviceBeingPaired < kNumMaxActiveDevices, CHIP_ERROR_INCORRECT_STATE); Device * device = &mActiveDevices[mDeviceBeingPaired]; - // Verify that Nonce matches with what we sent - const ByteSpan nonce = device->GetCSRNonce(); - VerifyOrReturnError(CSRNonce.size() == nonce.size(), CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrReturnError(memcmp(CSRNonce.data(), nonce.data(), CSRNonce.size()) == 0, CHIP_ERROR_INVALID_ARGUMENT); + ChipLogProgress(Controller, "Getting certificate chain for the device from the issuer"); - chip::Platform::ScopedMemoryBuffer chipOpCert; - ReturnErrorCodeIf(!chipOpCert.Alloc(kMaxCHIPCertLength * 2), CHIP_ERROR_NO_MEMORY); + mOperationalCredentialsDelegate->SetNodeIdForNextNOCRequest(device->GetDeviceId()); + mOperationalCredentialsDelegate->SetFabricIdForNextNOCRequest(0); - // TODO: Send DAC as input parameter to GenerateNodeOperationalCertificate() - // This will be done when device attestation is implemented. - ReturnErrorOnFailure(mOperationalCredentialsDelegate->GenerateNodeOperationalCertificate( - Optional(device->GetDeviceId()), 0, CSR, ByteSpan(), &mDeviceNOCCallback)); - - return CHIP_NO_ERROR; + return mOperationalCredentialsDelegate->GenerateNOCChain(NOCSRElements, AttestationSignature, ByteSpan(), ByteSpan(), + ByteSpan(), &mDeviceNOCChainCallback); } CHIP_ERROR DeviceCommissioner::SendOperationalCertificate(Device * device, const ByteSpan & opCertBuf) @@ -1415,17 +1412,10 @@ void DeviceCommissioner::OnOperationalCertificateAddResponse(void * context, uin } } -CHIP_ERROR DeviceCommissioner::SendTrustedRootCertificate(Device * device) +CHIP_ERROR DeviceCommissioner::SendTrustedRootCertificate(Device * device, const ByteSpan & rcac) { VerifyOrReturnError(device != nullptr, CHIP_ERROR_INVALID_ARGUMENT); - Transport::FabricInfo * fabric = mFabrics.FindFabricWithIndex(mFabricIndex); - VerifyOrReturnError(fabric != nullptr, CHIP_ERROR_INCORRECT_STATE); - - uint16_t rootCertLen = 0; - const uint8_t * rootCert = fabric->GetTrustedRoot(rootCertLen); - VerifyOrReturnError(rootCert != nullptr, CHIP_ERROR_INCORRECT_STATE); - ChipLogProgress(Controller, "Sending root certificate to the device"); chip::Controller::OperationalCredentialsCluster cluster; @@ -1435,7 +1425,7 @@ CHIP_ERROR DeviceCommissioner::SendTrustedRootCertificate(Device * device) Callback::Cancelable * successCallback = mRootCertResponseCallback.Cancel(); Callback::Cancelable * failureCallback = mOnRootCertFailureCallback.Cancel(); - ReturnErrorOnFailure(cluster.AddTrustedRootCertificate(successCallback, failureCallback, ByteSpan(rootCert, rootCertLen))); + ReturnErrorOnFailure(cluster.AddTrustedRootCertificate(successCallback, failureCallback, rcac)); #endif // CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE ChipLogProgress(Controller, "Sent root certificate to the device"); @@ -1460,7 +1450,8 @@ void DeviceCommissioner::OnRootCertSuccessResponse(void * context) device = &commissioner->mActiveDevices[commissioner->mDeviceBeingPaired]; - err = commissioner->SendOperationalCertificateSigningRequestCommand(device); + ChipLogProgress(Controller, "Sending operational certificate chain to the device"); + err = commissioner->SendOperationalCertificate(device, device->GetNOCChain()); SuccessOrExit(err); exit: @@ -1879,10 +1870,10 @@ void DeviceCommissioner::AdvanceCommissioningStage(CHIP_ERROR err) ChipLogProgress(Controller, "Exchanging certificates"); // TODO(cecille): Once this is implemented through the clusters, it should be moved to the proper stage and the callback // should advance the commissioning stage - CHIP_ERROR status = SendTrustedRootCertificate(device); + CHIP_ERROR status = SendOperationalCertificateSigningRequestCommand(device); if (status != CHIP_NO_ERROR) { - ChipLogError(Controller, "Failed in sending 'add trusted root' command to the device: err %s", ErrorStr(err)); + ChipLogError(Controller, "Failed in sending 'CSR Request' command to the device: err %s", ErrorStr(err)); OnSessionEstablishmentError(err); return; } diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h index 08a72b0e88c502..c0680d69cec1c7 100644 --- a/src/controller/CHIPDeviceController.h +++ b/src/controller/CHIPDeviceController.h @@ -355,13 +355,6 @@ class DLL_EXPORT DeviceController : public Messaging::ExchangeDelegate, DiscoveredNodeList GetDiscoveredNodes() override { return DiscoveredNodeList(mCommissionableNodes); } #endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS - // This function uses `OperationalCredentialsDelegate` to generate the operational certificates - // for the given device. The output is a TLV encoded array of compressed CHIP certificates. The - // array can contain up to two certificates (node operational certificate, and ICA certificate). - // If the certificate issuer doesn't require an ICA (i.e. NOC is signed by the root CA), the array - // will have only one certificate (node operational certificate). - CHIP_ERROR GenerateOperationalCertificates(const ByteSpan & noc, MutableByteSpan & cert); - private: //////////// ExchangeDelegate Implementation /////////////// CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * ec, const PacketHeader & packetHeader, @@ -376,8 +369,9 @@ class DLL_EXPORT DeviceController : public Messaging::ExchangeDelegate, CHIP_ERROR LoadLocalCredentials(Transport::FabricInfo * fabric); - static void OnLocalNOCGenerated(void * context, const ByteSpan & noc); - Callback::Callback mLocalNOCCallback; + static void OnLocalNOCChainGeneration(void * context, CHIP_ERROR status, const ByteSpan & noc, const ByteSpan & icac, + const ByteSpan & rcac); + Callback::Callback mLocalNOCChainCallback; }; /** @@ -630,7 +624,7 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, /* This function sends the trusted root certificate to the device. The function does not hold a refernce to the device object. */ - CHIP_ERROR SendTrustedRootCertificate(Device * device); + CHIP_ERROR SendTrustedRootCertificate(Device * device, const ByteSpan & rcac); /* This function is called by the commissioner code when the device completes the operational credential provisioning process. @@ -646,16 +640,11 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, * This function is called by the IM layer when the commissioner receives the CSR from the device. * (Reference: Specifications section 11.22.5.8. OpCSR Elements) * - * @param[in] context The context provided while registering the callback. - * @param[in] CSR The Certificate Signing Request. - * @param[in] CSRNonce The Nonce sent by us when we requested the CSR. - * @param[in] VendorReserved1 vendor-specific information that may aid in device commissioning. - * @param[in] VendorReserved2 vendor-specific information that may aid in device commissioning. - * @param[in] VendorReserved3 vendor-specific information that may aid in device commissioning. - * @param[in] Signature Cryptographic signature generated for the fields in the response message. + * @param[in] context The context provided while registering the callback. + * @param[in] NOCSRElements CSR elements as per specifications section 11.22.5.6. NOCSR Elements. + * @param[in] AttestationSignature Cryptographic signature generated for the fields in the response message. */ - static void OnOperationalCertificateSigningRequest(void * context, ByteSpan CSR, ByteSpan CSRNonce, ByteSpan VendorReserved1, - ByteSpan VendorReserved2, ByteSpan VendorReserved3, ByteSpan Signature); + static void OnOperationalCertificateSigningRequest(void * context, ByteSpan NOCSRElements, ByteSpan AttestationSignature); /* Callback when adding operational certs to device results in failure */ static void OnAddNOCFailureResponse(void * context, uint8_t status); @@ -670,22 +659,18 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, static void OnDeviceConnectedFn(void * context, Device * device); static void OnDeviceConnectionFailureFn(void * context, NodeId deviceId, CHIP_ERROR error); - static void OnDeviceNOCGenerated(void * context, const ByteSpan & noc); + static void OnDeviceNOCChainGeneration(void * context, CHIP_ERROR status, const ByteSpan & noc, const ByteSpan & icac, + const ByteSpan & rcac); /** * @brief * This function processes the CSR sent by the device. * (Reference: Specifications section 11.22.5.8. OpCSR Elements) * - * @param[in] CSR The Certificate Signing Request. - * @param[in] CSRNonce The Nonce sent by us when we requested the CSR. - * @param[in] VendorReserved1 vendor-specific information that may aid in device commissioning. - * @param[in] VendorReserved2 vendor-specific information that may aid in device commissioning. - * @param[in] VendorReserved3 vendor-specific information that may aid in device commissioning. - * @param[in] Signature Cryptographic signature generated for all the above fields. + * @param[in] NOCSRElements CSR elements as per specifications section 11.22.5.6. NOCSR Elements. + * @param[in] AttestationSignature Cryptographic signature generated for all the above fields. */ - CHIP_ERROR ProcessOpCSR(const ByteSpan & CSR, const ByteSpan & CSRNonce, const ByteSpan & VendorReserved1, - const ByteSpan & VendorReserved2, const ByteSpan & VendorReserved3, const ByteSpan & Signature); + CHIP_ERROR ProcessOpCSR(const ByteSpan & NOCSRElements, const ByteSpan & AttestationSignature); // Cluster callbacks for advancing commissioning flows Callback::Callback mSuccess; @@ -704,7 +689,7 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, Callback::Callback mOnDeviceConnectedCallback; Callback::Callback mOnDeviceConnectionFailureCallback; - Callback::Callback mDeviceNOCCallback; + Callback::Callback mDeviceNOCChainCallback; PASESession mPairingSession; }; diff --git a/src/controller/ExampleOperationalCredentialsIssuer.cpp b/src/controller/ExampleOperationalCredentialsIssuer.cpp index a4ff37faa2f197..2f63d901828d95 100644 --- a/src/controller/ExampleOperationalCredentialsIssuer.cpp +++ b/src/controller/ExampleOperationalCredentialsIssuer.cpp @@ -17,8 +17,12 @@ */ #include +#include #include #include +#include +#include +#include #include namespace chip { @@ -26,9 +30,11 @@ namespace Controller { constexpr const char kOperationalCredentialsIssuerKeypairStorage[] = "ExampleOpCredsCAKey"; constexpr const char kOperationalCredentialsIntermediateIssuerKeypairStorage[] = "ExampleOpCredsICAKey"; +constexpr const char kOperationalCredentialsRootCertificateStorage[] = "ExampleCARootCert"; using namespace Credentials; using namespace Crypto; +using namespace TLV; CHIP_ERROR ExampleOperationalCredentialsIssuer::Initialize(PersistentStorageDelegate & storage) { @@ -78,27 +84,48 @@ CHIP_ERROR ExampleOperationalCredentialsIssuer::Initialize(PersistentStorageDele ReturnErrorOnFailure(mIntermediateIssuer.Deserialize(serializedKey)); } + mStorage = &storage; mInitialized = true; return CHIP_NO_ERROR; } -CHIP_ERROR -ExampleOperationalCredentialsIssuer::GenerateNodeOperationalCertificate(const Optional & nodeId, FabricId fabricId, - const ByteSpan & csr, const ByteSpan & DAC, - Callback::Callback * onNOCGenerated) +CHIP_ERROR ExampleOperationalCredentialsIssuer::GenerateNOCChain(const ByteSpan & csrElements, + const ByteSpan & attestationSignature, const ByteSpan & DAC, + const ByteSpan & PAI, const ByteSpan & PAA, + Callback::Callback * onCompletion) { VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); NodeId assignedId; - if (nodeId.HasValue()) + if (mNodeIdRequested) { - assignedId = nodeId.Value(); + assignedId = mNextRequestedNodeId; + mNodeIdRequested = false; } else { assignedId = mNextAvailableNodeId++; } - X509CertRequestParams request = { 1, mIntermediateIssuerId, mNow, mNow + mValidity, true, fabricId, true, assignedId }; + X509CertRequestParams noc_request = { 1, mIntermediateIssuerId, mNow, mNow + mValidity, true, mNextFabricId, true, assignedId }; + + ChipLogProgress(Controller, "Verifying Certificate Signing Request"); + TLVReader reader; + reader.Init(csrElements.data(), static_cast(csrElements.size())); + + if (reader.GetType() == kTLVType_NotSpecified) + { + ReturnErrorOnFailure(reader.Next()); + } + + VerifyOrReturnError(reader.GetType() == kTLVType_Structure, CHIP_ERROR_WRONG_TLV_TYPE); + VerifyOrReturnError(reader.GetTag() == AnonymousTag, CHIP_ERROR_UNEXPECTED_TLV_ELEMENT); + + TLVType containerType; + ReturnErrorOnFailure(reader.EnterContainer(containerType)); + ReturnErrorOnFailure(reader.Next(kTLVType_ByteString, TLV::ContextTag(1))); + + ByteSpan csr(reader.GetReadPoint(), reader.GetLength()); + reader.ExitContainer(containerType); P256PublicKey pubkey; ReturnErrorOnFailure(VerifyCertificateSigningRequest(csr.data(), csr.size(), pubkey)); @@ -107,38 +134,45 @@ ExampleOperationalCredentialsIssuer::GenerateNodeOperationalCertificate(const Op ReturnErrorCodeIf(!noc.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY); uint32_t nocLen = 0; - ReturnErrorOnFailure(NewNodeOperationalX509Cert(request, CertificateIssuerLevel::kIssuerIsIntermediateCA, pubkey, + ChipLogProgress(Controller, "Generating NOC"); + ReturnErrorOnFailure(NewNodeOperationalX509Cert(noc_request, CertificateIssuerLevel::kIssuerIsIntermediateCA, pubkey, mIntermediateIssuer, noc.Get(), kMaxCHIPDERCertLength, nocLen)); - onNOCGenerated->mCall(onNOCGenerated->mContext, ByteSpan(noc.Get(), nocLen)); + X509CertRequestParams icac_request = { 0, mIssuerId, mNow, mNow + mValidity, true, mNextFabricId, false, 0 }; - return CHIP_NO_ERROR; -} + chip::Platform::ScopedMemoryBuffer icac; + ReturnErrorCodeIf(!icac.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY); + uint32_t icacLen = 0; -CHIP_ERROR ExampleOperationalCredentialsIssuer::GetIntermediateCACertificate(FabricId fabricId, MutableByteSpan & outCert) -{ - VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); - X509CertRequestParams request = { 0, mIssuerId, mNow, mNow + mValidity, true, fabricId, false, 0 }; + ChipLogProgress(Controller, "Generating ICAC"); + ReturnErrorOnFailure(NewICAX509Cert(icac_request, mIntermediateIssuerId, mIntermediateIssuer.Pubkey(), mIssuer, icac.Get(), + kMaxCHIPDERCertLength, icacLen)); - size_t outCertSize = (outCert.size() > UINT32_MAX) ? UINT32_MAX : outCert.size(); - uint32_t outCertLen = 0; - ReturnErrorOnFailure(NewICAX509Cert(request, mIntermediateIssuerId, mIntermediateIssuer.Pubkey(), mIssuer, outCert.data(), - static_cast(outCertSize), outCertLen)); - outCert.reduce_size(outCertLen); + chip::Platform::ScopedMemoryBuffer rcac; + ReturnErrorCodeIf(!rcac.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY); + uint16_t rootCertBufLen = kMaxCHIPDERCertLength; - return CHIP_NO_ERROR; -} - -CHIP_ERROR ExampleOperationalCredentialsIssuer::GetRootCACertificate(FabricId fabricId, MutableByteSpan & outCert) -{ - VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); - X509CertRequestParams request = { 0, mIssuerId, mNow, mNow + mValidity, true, fabricId, false, 0 }; - - size_t outCertSize = (outCert.size() > UINT32_MAX) ? UINT32_MAX : outCert.size(); - uint32_t outCertLen = 0; - ReturnErrorOnFailure(NewRootX509Cert(request, mIssuer, outCert.data(), static_cast(outCertSize), outCertLen)); - outCert.reduce_size(outCertLen); + CHIP_ERROR err = CHIP_NO_ERROR; + PERSISTENT_KEY_OP(mNextFabricId, kOperationalCredentialsRootCertificateStorage, key, + err = mStorage->SyncGetKeyValue(key, rcac.Get(), rootCertBufLen)); + if (err != CHIP_NO_ERROR) + { + // Storage doesn't have an existing root certificate. Let's create one and add it to the storage. + X509CertRequestParams request = { 0, mIssuerId, mNow, mNow + mValidity, true, mNextFabricId, false, 0 }; + uint32_t outCertLen = 0; + ChipLogProgress(Controller, "Generating RCAC"); + ReturnErrorOnFailure(NewRootX509Cert(request, mIssuer, rcac.Get(), kMaxCHIPDERCertLength, outCertLen)); + + VerifyOrReturnError(CanCastTo(outCertLen), CHIP_ERROR_INVALID_ARGUMENT); + rootCertBufLen = static_cast(outCertLen); + PERSISTENT_KEY_OP(mNextFabricId, kOperationalCredentialsRootCertificateStorage, key, + err = mStorage->SyncSetKeyValue(key, rcac.Get(), rootCertBufLen)); + ReturnErrorOnFailure(err); + } + ChipLogProgress(Controller, "Providing certificate chain to the commissioner"); + onCompletion->mCall(onCompletion->mContext, CHIP_NO_ERROR, ByteSpan(noc.Get(), nocLen), ByteSpan(icac.Get(), icacLen), + ByteSpan(rcac.Get(), rootCertBufLen)); return CHIP_NO_ERROR; } diff --git a/src/controller/ExampleOperationalCredentialsIssuer.h b/src/controller/ExampleOperationalCredentialsIssuer.h index 6bad06bed8d10e..ca94f081f57338 100644 --- a/src/controller/ExampleOperationalCredentialsIssuer.h +++ b/src/controller/ExampleOperationalCredentialsIssuer.h @@ -43,12 +43,17 @@ class DLL_EXPORT ExampleOperationalCredentialsIssuer : public OperationalCredent public: virtual ~ExampleOperationalCredentialsIssuer() {} - CHIP_ERROR GenerateNodeOperationalCertificate(const Optional & nodeId, FabricId fabricId, const ByteSpan & csr, - const ByteSpan & DAC, Callback::Callback * onNOCGenerated) override; + CHIP_ERROR GenerateNOCChain(const ByteSpan & csrElements, const ByteSpan & attestationSignature, const ByteSpan & DAC, + const ByteSpan & PAI, const ByteSpan & PAA, + Callback::Callback * onCompletion) override; - CHIP_ERROR GetIntermediateCACertificate(FabricId fabricId, MutableByteSpan & outCert) override; + void SetNodeIdForNextNOCRequest(NodeId nodeId) override + { + mNextRequestedNodeId = nodeId; + mNodeIdRequested = true; + } - CHIP_ERROR GetRootCACertificate(FabricId fabricId, MutableByteSpan & outCert) override; + void SetFabricIdForNextNOCRequest(FabricId fabricId) override { mNextFabricId = fabricId; } /** * @brief Initialize the issuer with the keypair in the storage. @@ -89,7 +94,12 @@ class DLL_EXPORT ExampleOperationalCredentialsIssuer : public OperationalCredent // By default, let's set validity to 10 years uint32_t mValidity = 365 * 24 * 60 * 60 * 10; - NodeId mNextAvailableNodeId = 1; + NodeId mNextAvailableNodeId = 1; + PersistentStorageDelegate * mStorage = nullptr; + + NodeId mNextRequestedNodeId = 1; + FabricId mNextFabricId = 0; + bool mNodeIdRequested = false; }; } // namespace Controller diff --git a/src/controller/OperationalCredentialsDelegate.h b/src/controller/OperationalCredentialsDelegate.h index 27bfb70b75e7d7..0f451d4f7fcfdb 100644 --- a/src/controller/OperationalCredentialsDelegate.h +++ b/src/controller/OperationalCredentialsDelegate.h @@ -29,7 +29,8 @@ namespace chip { namespace Controller { -typedef void (*NOCGenerated)(void * context, const ByteSpan & noc); +typedef void (*OnNOCChainGeneration)(void * context, CHIP_ERROR status, const ByteSpan & noc, const ByteSpan & icac, + const ByteSpan & rcac); constexpr uint32_t kMaxCHIPDERCertLength = 600; @@ -41,63 +42,40 @@ class DLL_EXPORT OperationalCredentialsDelegate /** * @brief - * This function generates an operational certificate for the device. + * This function generates an operational certificate chain for the device. * The API generates the certificate in X.509 DER format. * * The delegate is expected to use the certificate authority whose certificate - * is returned in `GetIntermediateCACertificate()` or `GetRootCACertificate()` - * API calls. + * is returned in `GetRootCACertificate()` API call. * - * The delegate will call `onNOCGenerated` when the NOC is ready. + * The delegate will call `onCompletion` when the NOC certificate chain is ready. * - * @param[in] nodeId Optional node ID. If provided, the generated NOC must use the provided ID. - * If ID is not provided, the delegate must generate one. - * @param[in] fabricId Fabric ID for which the certificate is being requested. - * @param[in] csr Certificate Signing Request from the node in DER format. - * @param[in] DAC Device attestation certificate received from the device being commissioned - * @param[in] onNOCGenerated Callback handler to provide generated NOC to the caller of GenerateNodeOperationalCertificate() + * @param[in] csrElements CSR elements as per specifications section 11.22.5.6. NOCSR Elements. + * @param[in] attestationSignature Attestation signature as per specifications section 11.22.7.6. CSRResponse Command. + * @param[in] DAC Device attestation certificate received from the device being commissioned + * @param[in] PAI Product Attestation Intermediate certificate + * @param[in] PAA Product Attestation Authority certificate + * @param[in] onCompletion Callback handler to provide generated NOC chain to the caller of GenerateNOCChain() * * @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error code. */ - virtual CHIP_ERROR GenerateNodeOperationalCertificate(const Optional & nodeId, FabricId fabricId, const ByteSpan & csr, - const ByteSpan & DAC, - Callback::Callback * onNOCGenerated) = 0; + virtual CHIP_ERROR GenerateNOCChain(const ByteSpan & csrElements, const ByteSpan & attestationSignature, const ByteSpan & DAC, + const ByteSpan & PAI, const ByteSpan & PAA, + Callback::Callback * onCompletion) = 0; /** - * @brief - * This function returns the intermediate certificate authority (ICA) certificate corresponding to the - * provided fabric ID. Intermediate certificate authority is optional. If the controller - * application does not require ICA, this API call will return `CHIP_ERROR_INTERMEDIATE_CA_NOT_REQUIRED`. - * - * The returned certificate is in X.509 DER format. - * - * @param[in] fabricId Fabric ID for which the certificate is being requested. - * @param[in] outCert The API will fill in the cert in this buffer. The buffer is allocated by the caller. - * - * @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error code. - * CHIP_ERROR_INTERMEDIATE_CA_NOT_REQUIRED is not a critical error. It indicates that ICA is not needed. + * This function sets the node ID for which the next NOC Chain would be requested. The node ID is + * provided as a hint, and the delegate implementation may chose to ignore it and pick node ID of + * their choice. */ - virtual CHIP_ERROR GetIntermediateCACertificate(FabricId fabricId, MutableByteSpan & outCert) - { - // By default, let's return CHIP_ERROR_INTERMEDIATE_CA_NOT_REQUIRED status. It'll allow - // commissioner applications to not implement GetIntermediateCACertificate() if they don't require an - // intermediate CA. - return CHIP_ERROR_INTERMEDIATE_CA_NOT_REQUIRED; - } + virtual void SetNodeIdForNextNOCRequest(NodeId nodeId) {} /** - * @brief - * This function returns the root certificate authority (root CA) certificate corresponding to the - * provided fabric ID. - * - * The returned certificate is in X.509 DER format. - * - * @param[in] fabricId Fabric ID for which the certificate is being requested. - * @param[in] outCert The API will fill in the cert in this buffer. The buffer is allocated by the caller. - * - * @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error code. + * This function sets the fabric ID for which the next NOC Chain should be generated. This API is + * not required to be implemented if the delegate implementation has other mechanisms to find the + * fabric ID. */ - virtual CHIP_ERROR GetRootCACertificate(FabricId fabricId, MutableByteSpan & outCert) = 0; + virtual void SetFabricIdForNextNOCRequest(FabricId fabricId) {} }; } // namespace Controller diff --git a/src/controller/data_model/gen/CHIPClientCallbacks.cpp b/src/controller/data_model/gen/CHIPClientCallbacks.cpp index 626b580f7ec438..61735692ffad79 100644 --- a/src/controller/data_model/gen/CHIPClientCallbacks.cpp +++ b/src/controller/data_model/gen/CHIPClientCallbacks.cpp @@ -2809,23 +2809,17 @@ bool emberAfOperationalCredentialsClusterNOCResponseCallback(chip::EndpointId en } bool emberAfOperationalCredentialsClusterOpCSRResponseCallback(chip::EndpointId endpoint, chip::app::CommandSender * commandObj, - chip::ByteSpan CSR, chip::ByteSpan CSRNonce, - chip::ByteSpan VendorReserved1, chip::ByteSpan VendorReserved2, - chip::ByteSpan VendorReserved3, chip::ByteSpan Signature) + chip::ByteSpan NOCSRElements, chip::ByteSpan AttestationSignature) { ChipLogProgress(Zcl, "OpCSRResponse:"); - ChipLogProgress(Zcl, " CSR: %zu", CSR.size()); - ChipLogProgress(Zcl, " CSRNonce: %zu", CSRNonce.size()); - ChipLogProgress(Zcl, " VendorReserved1: %zu", VendorReserved1.size()); - ChipLogProgress(Zcl, " VendorReserved2: %zu", VendorReserved2.size()); - ChipLogProgress(Zcl, " VendorReserved3: %zu", VendorReserved3.size()); - ChipLogProgress(Zcl, " Signature: %zu", Signature.size()); + ChipLogProgress(Zcl, " NOCSRElements: %zu", NOCSRElements.size()); + ChipLogProgress(Zcl, " AttestationSignature: %zu", AttestationSignature.size()); GET_CLUSTER_RESPONSE_CALLBACKS("OperationalCredentialsClusterOpCSRResponseCallback"); Callback::Callback * cb = Callback::Callback::FromCancelable(onSuccessCallback); - cb->mCall(cb->mContext, CSR, CSRNonce, VendorReserved1, VendorReserved2, VendorReserved3, Signature); + cb->mCall(cb->mContext, NOCSRElements, AttestationSignature); return true; } diff --git a/src/controller/data_model/gen/CHIPClientCallbacks.h b/src/controller/data_model/gen/CHIPClientCallbacks.h index e564dddad157ca..115707f9ac794b 100644 --- a/src/controller/data_model/gen/CHIPClientCallbacks.h +++ b/src/controller/data_model/gen/CHIPClientCallbacks.h @@ -162,9 +162,8 @@ typedef void (*OtaSoftwareUpdateProviderClusterQueryImageResponseCallback)(void chip::ByteSpan metadataForRequestor); typedef void (*OperationalCredentialsClusterNOCResponseCallback)(void * context, uint8_t StatusCode, uint8_t FabricIndex, chip::ByteSpan DebugText); -typedef void (*OperationalCredentialsClusterOpCSRResponseCallback)(void * context, chip::ByteSpan CSR, chip::ByteSpan CSRNonce, - chip::ByteSpan VendorReserved1, chip::ByteSpan VendorReserved2, - chip::ByteSpan VendorReserved3, chip::ByteSpan Signature); +typedef void (*OperationalCredentialsClusterOpCSRResponseCallback)(void * context, chip::ByteSpan NOCSRElements, + chip::ByteSpan AttestationSignature); typedef void (*OperationalCredentialsClusterSetFabricResponseCallback)(void * context, chip::FabricId FabricId); typedef void (*ScenesClusterAddSceneResponseCallback)(void * context, uint16_t groupId, uint8_t sceneId); typedef void (*ScenesClusterGetSceneMembershipResponseCallback)(void * context, uint8_t capacity, uint16_t groupId, diff --git a/src/controller/data_model/gen/IMClusterCommandHandler.cpp b/src/controller/data_model/gen/IMClusterCommandHandler.cpp index de4f537e983dd0..6d5c2f6995a9c2 100644 --- a/src/controller/data_model/gen/IMClusterCommandHandler.cpp +++ b/src/controller/data_model/gen/IMClusterCommandHandler.cpp @@ -4338,14 +4338,10 @@ void DispatchClientCommand(app::CommandSender * apCommandObj, CommandId aCommand break; } case Clusters::OperationalCredentials::Commands::Ids::OpCSRResponse: { - expectArgumentCount = 6; - chip::ByteSpan CSR; - chip::ByteSpan CSRNonce; - chip::ByteSpan VendorReserved1; - chip::ByteSpan VendorReserved2; - chip::ByteSpan VendorReserved3; - chip::ByteSpan Signature; - bool argExists[6]; + expectArgumentCount = 2; + chip::ByteSpan NOCSRElements; + chip::ByteSpan AttestationSignature; + bool argExists[2]; memset(argExists, 0, sizeof argExists); @@ -4358,7 +4354,7 @@ void DispatchClientCommand(app::CommandSender * apCommandObj, CommandId aCommand continue; } currentDecodeTagId = TLV::TagNumFromTag(aDataTlv.GetTag()); - if (currentDecodeTagId < 6) + if (currentDecodeTagId < 2) { if (argExists[currentDecodeTagId]) { @@ -4377,37 +4373,13 @@ void DispatchClientCommand(app::CommandSender * apCommandObj, CommandId aCommand case 0: { const uint8_t * data = nullptr; TLVUnpackError = aDataTlv.GetDataPtr(data); - CSR = chip::ByteSpan(data, aDataTlv.GetLength()); + NOCSRElements = chip::ByteSpan(data, aDataTlv.GetLength()); } break; case 1: { const uint8_t * data = nullptr; TLVUnpackError = aDataTlv.GetDataPtr(data); - CSRNonce = chip::ByteSpan(data, aDataTlv.GetLength()); - } - break; - case 2: { - const uint8_t * data = nullptr; - TLVUnpackError = aDataTlv.GetDataPtr(data); - VendorReserved1 = chip::ByteSpan(data, aDataTlv.GetLength()); - } - break; - case 3: { - const uint8_t * data = nullptr; - TLVUnpackError = aDataTlv.GetDataPtr(data); - VendorReserved2 = chip::ByteSpan(data, aDataTlv.GetLength()); - } - break; - case 4: { - const uint8_t * data = nullptr; - TLVUnpackError = aDataTlv.GetDataPtr(data); - VendorReserved3 = chip::ByteSpan(data, aDataTlv.GetLength()); - } - break; - case 5: { - const uint8_t * data = nullptr; - TLVUnpackError = aDataTlv.GetDataPtr(data); - Signature = chip::ByteSpan(data, aDataTlv.GetLength()); + AttestationSignature = chip::ByteSpan(data, aDataTlv.GetLength()); } break; default: @@ -4427,10 +4399,10 @@ void DispatchClientCommand(app::CommandSender * apCommandObj, CommandId aCommand TLVError = CHIP_NO_ERROR; } - if (CHIP_NO_ERROR == TLVError && CHIP_NO_ERROR == TLVUnpackError && 6 == validArgumentCount) + if (CHIP_NO_ERROR == TLVError && CHIP_NO_ERROR == TLVUnpackError && 2 == validArgumentCount) { - wasHandled = emberAfOperationalCredentialsClusterOpCSRResponseCallback( - aEndpointId, apCommandObj, CSR, CSRNonce, VendorReserved1, VendorReserved2, VendorReserved3, Signature); + wasHandled = emberAfOperationalCredentialsClusterOpCSRResponseCallback(aEndpointId, apCommandObj, NOCSRElements, + AttestationSignature); } break; } diff --git a/src/controller/java/AndroidDeviceControllerWrapper.cpp b/src/controller/java/AndroidDeviceControllerWrapper.cpp index ea46b532e643ae..2426da914892de 100644 --- a/src/controller/java/AndroidDeviceControllerWrapper.cpp +++ b/src/controller/java/AndroidDeviceControllerWrapper.cpp @@ -25,16 +25,21 @@ #include "JniReferences.h" #include +#include #include +#include +#include #include #include using namespace chip; using namespace chip::Controller; +using namespace TLV; extern chip::Ble::BleLayer * GetJNIBleLayer(); -constexpr const char kOperationalCredentialsIssuerKeypairStorage[] = "AndroidDeviceControllerKey"; +constexpr const char kOperationalCredentialsIssuerKeypairStorage[] = "AndroidDeviceControllerKey"; +constexpr const char kOperationalCredentialsRootCertificateStorage[] = "AndroidCARootCert"; AndroidDeviceControllerWrapper::~AndroidDeviceControllerWrapper() { if ((mJavaVM != nullptr) && (mJavaObjectRef != nullptr)) @@ -56,18 +61,101 @@ void AndroidDeviceControllerWrapper::CallJavaMethod(const char * methodName, jin argument); } -CHIP_ERROR AndroidDeviceControllerWrapper::GetRootCACertificate(FabricId fabricId, MutableByteSpan & outCert) +// TODO Refactor this API to match latest spec, so that GenerateNodeOperationalCertificate receives the full CSR Elements data +// payload. +CHIP_ERROR AndroidDeviceControllerWrapper::GenerateNOCChain(const ByteSpan & csrElements, const ByteSpan & attestationSignature, + const ByteSpan & DAC, const ByteSpan & PAI, const ByteSpan & PAA, + Callback::Callback * onCompletion) { + jmethodID method; + CHIP_ERROR err = CHIP_NO_ERROR; + err = JniReferences::GetInstance().FindMethod(JniReferences::GetInstance().GetEnvForCurrentThread(), mJavaObjectRef, + "onOpCSRGenerationComplete", "([B)V", &method); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Controller, "Error invoking onOpCSRGenerationComplete: %" CHIP_ERROR_FORMAT, ChipError::FormatError(err)); + return err; + } + + // Initializing the KeyPair. Initialize(); - VerifyOrReturnError(mInitialized, CHIP_ERROR_INCORRECT_STATE); - chip::Credentials::X509CertRequestParams newCertParams = { 0, mIssuerId, mNow, mNow + mValidity, true, fabricId, false, 0 }; - size_t outCertSize = (outCert.size() > UINT32_MAX) ? UINT32_MAX : outCert.size(); - uint32_t outCertLen = 0; - ReturnErrorOnFailure(NewRootX509Cert(newCertParams, mIssuer, outCert.data(), static_cast(outCertSize), outCertLen)); - outCert.reduce_size(outCertLen); + chip::NodeId assignedId; + if (mNodeIdRequested) + { + assignedId = mNextRequestedNodeId; + mNodeIdRequested = false; + } + else + { + assignedId = mNextAvailableNodeId++; + } + + chip::Credentials::X509CertRequestParams request = { + 1, mIssuerId, mNow, mNow + mValidity, true, mNextFabricId, true, assignedId + }; - return CHIP_NO_ERROR; + TLVReader reader; + reader.Init(csrElements.data(), static_cast(csrElements.size())); + + if (reader.GetType() == kTLVType_NotSpecified) + { + ReturnErrorOnFailure(reader.Next()); + } + + VerifyOrReturnError(reader.GetType() == kTLVType_Structure, CHIP_ERROR_WRONG_TLV_TYPE); + VerifyOrReturnError(reader.GetTag() == AnonymousTag, CHIP_ERROR_UNEXPECTED_TLV_ELEMENT); + + TLVType containerType; + ReturnErrorOnFailure(reader.EnterContainer(containerType)); + ReturnErrorOnFailure(reader.Next(kTLVType_ByteString, TLV::ContextTag(1))); + + ByteSpan csr(reader.GetReadPoint(), reader.GetLength()); + reader.ExitContainer(containerType); + + chip::P256PublicKey pubkey; + ReturnErrorOnFailure(VerifyCertificateSigningRequest(csr.data(), csr.size(), pubkey)); + + ChipLogProgress(chipTool, "VerifyCertificateSigningRequest"); + + chip::Platform::ScopedMemoryBuffer noc; + ReturnErrorCodeIf(!noc.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY); + uint32_t nocLen = 0; + + CHIP_ERROR generateCert = NewNodeOperationalX509Cert(request, chip::Credentials::CertificateIssuerLevel::kIssuerIsRootCA, + pubkey, mIssuer, noc.Get(), kMaxCHIPDERCertLength, nocLen); + + chip::Platform::ScopedMemoryBuffer rcac; + ReturnErrorCodeIf(!rcac.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY); + uint16_t rootCertBufLen = kMaxCHIPDERCertLength; + + PERSISTENT_KEY_OP(mNextFabricId, kOperationalCredentialsRootCertificateStorage, key, + err = SyncGetKeyValue(key, rcac.Get(), rootCertBufLen)); + if (err != CHIP_NO_ERROR) + { + // Storage doesn't have an existing root certificate. Let's create one and add it to the storage. + chip::Credentials::X509CertRequestParams rcac_request = { 0, mIssuerId, mNow, mNow + mValidity, + true, mNextFabricId, false, 0 }; + + uint32_t outCertLen = 0; + ReturnErrorOnFailure(NewRootX509Cert(rcac_request, mIssuer, rcac.Get(), kMaxCHIPDERCertLength, outCertLen)); + + VerifyOrReturnError(CanCastTo(outCertLen), CHIP_ERROR_INVALID_ARGUMENT); + rootCertBufLen = static_cast(outCertLen); + PERSISTENT_KEY_OP(mNextFabricId, kOperationalCredentialsRootCertificateStorage, key, + err = SyncSetKeyValue(key, rcac.Get(), rootCertBufLen)); + ReturnErrorOnFailure(err); + } + + onCompletion->mCall(onCompletion->mContext, generateCert, ByteSpan(noc.Get(), nocLen), ByteSpan(), + ByteSpan(rcac.Get(), rootCertBufLen)); + + jbyteArray javaCsr; + JniReferences::GetInstance().GetEnvForCurrentThread()->ExceptionClear(); + JniReferences::GetInstance().N2J_ByteArray(JniReferences::GetInstance().GetEnvForCurrentThread(), csrElements.data(), + csrElements.size(), javaCsr); + JniReferences::GetInstance().GetEnvForCurrentThread()->CallVoidMethod(mJavaObjectRef, method, javaCsr); + return generateCert; } AndroidDeviceControllerWrapper * AndroidDeviceControllerWrapper::AllocateNew(JavaVM * vm, jobject deviceControllerObj, @@ -162,60 +250,6 @@ void AndroidDeviceControllerWrapper::OnCommissioningComplete(NodeId deviceId, CH env->CallVoidMethod(mJavaObjectRef, onCommissioningCompleteMethod, static_cast(deviceId), error); } -// TODO Refactor this API to match latest spec, so that GenerateNodeOperationalCertificate receives the full CSR Elements data -// payload. -CHIP_ERROR -AndroidDeviceControllerWrapper::GenerateNodeOperationalCertificate(const Optional & nodeId, FabricId fabricId, - const ByteSpan & csr, const ByteSpan & DAC, - Callback::Callback * onNOCGenerated) -{ - jmethodID method; - CHIP_ERROR err = CHIP_NO_ERROR; - err = JniReferences::GetInstance().FindMethod(JniReferences::GetInstance().GetEnvForCurrentThread(), mJavaObjectRef, - "onOpCSRGenerationComplete", "([B)V", &method); - if (err != CHIP_NO_ERROR) - { - ChipLogError(Controller, "Error invoking onOpCSRGenerationComplete: %" CHIP_ERROR_FORMAT, ChipError::FormatError(err)); - return err; - } - - // Initializing the KeyPair. - Initialize(); - - chip::NodeId assignedId; - if (nodeId.HasValue()) - { - assignedId = nodeId.Value(); - } - else - { - assignedId = mNextAvailableNodeId++; - } - - chip::Credentials::X509CertRequestParams request = { 1, mIssuerId, mNow, mNow + mValidity, true, fabricId, true, assignedId }; - - chip::P256PublicKey pubkey; - ReturnErrorOnFailure(VerifyCertificateSigningRequest(csr.data(), csr.size(), pubkey)); - - ChipLogProgress(chipTool, "VerifyCertificateSigningRequest"); - - chip::Platform::ScopedMemoryBuffer noc; - ReturnErrorCodeIf(!noc.Alloc(kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY); - uint32_t nocLen = 0; - - CHIP_ERROR generateCert = NewNodeOperationalX509Cert(request, chip::Credentials::CertificateIssuerLevel::kIssuerIsRootCA, - pubkey, mIssuer, noc.Get(), kMaxCHIPDERCertLength, nocLen); - - onNOCGenerated->mCall(onNOCGenerated->mContext, ByteSpan(noc.Get(), nocLen)); - - jbyteArray javaCsr; - JniReferences::GetInstance().GetEnvForCurrentThread()->ExceptionClear(); - JniReferences::GetInstance().N2J_ByteArray(JniReferences::GetInstance().GetEnvForCurrentThread(), csr.data(), csr.size(), - javaCsr); - JniReferences::GetInstance().GetEnvForCurrentThread()->CallVoidMethod(mJavaObjectRef, method, javaCsr); - return generateCert; -} - CHIP_ERROR AndroidDeviceControllerWrapper::Initialize() { chip::Crypto::P256SerializedKeypair serializedKey; diff --git a/src/controller/java/AndroidDeviceControllerWrapper.h b/src/controller/java/AndroidDeviceControllerWrapper.h index 14eb8da123f2f1..5ffcf1bccbb94b 100644 --- a/src/controller/java/AndroidDeviceControllerWrapper.h +++ b/src/controller/java/AndroidDeviceControllerWrapper.h @@ -58,12 +58,17 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel void OnCommissioningComplete(chip::NodeId deviceId, CHIP_ERROR error) override; // OperationalCredentialsDelegate implementation - CHIP_ERROR - GenerateNodeOperationalCertificate(const chip::Optional & nodeId, chip::FabricId fabricId, - const chip::ByteSpan & csr, const chip::ByteSpan & DAC, - chip::Callback::Callback * onNOCGenerated) override; + CHIP_ERROR GenerateNOCChain(const chip::ByteSpan & csrElements, const chip::ByteSpan & attestationSignature, + const chip::ByteSpan & DAC, const chip::ByteSpan & PAI, const chip::ByteSpan & PAA, + chip::Callback::Callback * onCompletion) override; - CHIP_ERROR GetRootCACertificate(chip::FabricId fabricId, chip::MutableByteSpan & outCert) override; + void SetNodeIdForNextNOCRequest(chip::NodeId nodeId) override + { + mNextRequestedNodeId = nodeId; + mNodeIdRequested = true; + } + + void SetFabricIdForNextNOCRequest(chip::FabricId fabricId) override { mNextFabricId = fabricId; } // DeviceStatusDelegate implementation void OnMessage(chip::System::PacketBufferHandle && msg) override; @@ -101,6 +106,10 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel chip::NodeId mNextAvailableNodeId = 1; + chip::NodeId mNextRequestedNodeId = 1; + chip::FabricId mNextFabricId = 0; + bool mNodeIdRequested = false; + AndroidDeviceControllerWrapper(ChipDeviceControllerPtr controller, pthread_mutex_t * stackLock) : mController(std::move(controller)), mStackLock(stackLock) { diff --git a/src/controller/java/gen/CHIPClusters-JNI.cpp b/src/controller/java/gen/CHIPClusters-JNI.cpp index fcefa27c98bc66..e0063c1ceb20e6 100644 --- a/src/controller/java/gen/CHIPClusters-JNI.cpp +++ b/src/controller/java/gen/CHIPClusters-JNI.cpp @@ -4962,8 +4962,7 @@ class CHIPOperationalCredentialsClusterOpCSRResponseCallback env->DeleteGlobalRef(javaCallbackRef); }; - static void CallbackFn(void * context, chip::ByteSpan CSR, chip::ByteSpan CSRNonce, chip::ByteSpan VendorReserved1, - chip::ByteSpan VendorReserved2, chip::ByteSpan VendorReserved3, chip::ByteSpan Signature) + static void CallbackFn(void * context, chip::ByteSpan NOCSRElements, chip::ByteSpan AttestationSignature) { StackUnlockGuard unlockGuard(JniReferences::GetInstance().GetStackLock()); CHIP_ERROR err = CHIP_NO_ERROR; @@ -4971,12 +4970,8 @@ class CHIPOperationalCredentialsClusterOpCSRResponseCallback jobject javaCallbackRef; jmethodID javaMethod; CHIPOperationalCredentialsClusterOpCSRResponseCallback * cppCallback = nullptr; - jbyteArray CSRArr; - jbyteArray CSRNonceArr; - jbyteArray VendorReserved1Arr; - jbyteArray VendorReserved2Arr; - jbyteArray VendorReserved3Arr; - jbyteArray SignatureArr; + jbyteArray NOCSRElementsArr; + jbyteArray AttestationSignatureArr; VerifyOrExit(env != nullptr, err = CHIP_JNI_ERROR_NO_ENV); @@ -4986,52 +4981,25 @@ class CHIPOperationalCredentialsClusterOpCSRResponseCallback javaCallbackRef = cppCallback->javaCallbackRef; VerifyOrExit(javaCallbackRef != nullptr, err = CHIP_NO_ERROR); - err = JniReferences::GetInstance().FindMethod(env, javaCallbackRef, "onSuccess", "([B[B[B[B[B[B)V", &javaMethod); + err = JniReferences::GetInstance().FindMethod(env, javaCallbackRef, "onSuccess", "([B[B)V", &javaMethod); SuccessOrExit(err); - CSRArr = env->NewByteArray(CSR.size()); - VerifyOrExit(CSRArr != nullptr, err = CHIP_ERROR_NO_MEMORY); + NOCSRElementsArr = env->NewByteArray(NOCSRElements.size()); + VerifyOrExit(NOCSRElementsArr != nullptr, err = CHIP_ERROR_NO_MEMORY); env->ExceptionClear(); - env->SetByteArrayRegion(CSRArr, 0, CSR.size(), reinterpret_cast(CSR.data())); + env->SetByteArrayRegion(NOCSRElementsArr, 0, NOCSRElements.size(), reinterpret_cast(NOCSRElements.data())); VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); - CSRNonceArr = env->NewByteArray(CSRNonce.size()); - VerifyOrExit(CSRNonceArr != nullptr, err = CHIP_ERROR_NO_MEMORY); + AttestationSignatureArr = env->NewByteArray(AttestationSignature.size()); + VerifyOrExit(AttestationSignatureArr != nullptr, err = CHIP_ERROR_NO_MEMORY); env->ExceptionClear(); - env->SetByteArrayRegion(CSRNonceArr, 0, CSRNonce.size(), reinterpret_cast(CSRNonce.data())); - VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); - VendorReserved1Arr = env->NewByteArray(VendorReserved1.size()); - VerifyOrExit(VendorReserved1Arr != nullptr, err = CHIP_ERROR_NO_MEMORY); - env->ExceptionClear(); - env->SetByteArrayRegion(VendorReserved1Arr, 0, VendorReserved1.size(), - reinterpret_cast(VendorReserved1.data())); - VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); - VendorReserved2Arr = env->NewByteArray(VendorReserved2.size()); - VerifyOrExit(VendorReserved2Arr != nullptr, err = CHIP_ERROR_NO_MEMORY); - env->ExceptionClear(); - env->SetByteArrayRegion(VendorReserved2Arr, 0, VendorReserved2.size(), - reinterpret_cast(VendorReserved2.data())); - VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); - VendorReserved3Arr = env->NewByteArray(VendorReserved3.size()); - VerifyOrExit(VendorReserved3Arr != nullptr, err = CHIP_ERROR_NO_MEMORY); - env->ExceptionClear(); - env->SetByteArrayRegion(VendorReserved3Arr, 0, VendorReserved3.size(), - reinterpret_cast(VendorReserved3.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())); + env->SetByteArrayRegion(AttestationSignatureArr, 0, AttestationSignature.size(), + reinterpret_cast(AttestationSignature.data())); VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); - env->CallVoidMethod(javaCallbackRef, javaMethod, CSRArr, CSRNonceArr, VendorReserved1Arr, VendorReserved2Arr, - VendorReserved3Arr, SignatureArr); + env->CallVoidMethod(javaCallbackRef, javaMethod, NOCSRElementsArr, AttestationSignatureArr); - env->DeleteLocalRef(CSRArr); - env->DeleteLocalRef(CSRNonceArr); - env->DeleteLocalRef(VendorReserved1Arr); - env->DeleteLocalRef(VendorReserved2Arr); - env->DeleteLocalRef(VendorReserved3Arr); - env->DeleteLocalRef(SignatureArr); + env->DeleteLocalRef(NOCSRElementsArr); + env->DeleteLocalRef(AttestationSignatureArr); exit: if (err != CHIP_NO_ERROR) diff --git a/src/controller/java/gen/ChipClusters.java b/src/controller/java/gen/ChipClusters.java index 4859ed78114839..f82e78d929018c 100644 --- a/src/controller/java/gen/ChipClusters.java +++ b/src/controller/java/gen/ChipClusters.java @@ -3773,13 +3773,7 @@ public interface NOCResponseCallback { } public interface OpCSRResponseCallback { - void onSuccess( - byte[] CSR, - byte[] CSRNonce, - byte[] VendorReserved1, - byte[] VendorReserved2, - byte[] VendorReserved3, - byte[] Signature); + void onSuccess(byte[] NOCSRElements, byte[] AttestationSignature); void onError(Exception error); } diff --git a/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.h b/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.h index 6358895dd59f78..ddad008a3c4d28 100644 --- a/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.h +++ b/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.h @@ -33,11 +33,17 @@ class CHIPOperationalCredentialsDelegate : public chip::Controller::OperationalC CHIP_ERROR init(CHIPPersistentStorageDelegateBridge * storage); - CHIP_ERROR GenerateNodeOperationalCertificate(const chip::Optional & nodeId, chip::FabricId fabricId, - const chip::ByteSpan & csr, const chip::ByteSpan & DAC, - chip::Callback::Callback * onNOCGenerated) override; + CHIP_ERROR GenerateNOCChain(const chip::ByteSpan & csrElements, const chip::ByteSpan & attestationSignature, + const chip::ByteSpan & DAC, const chip::ByteSpan & PAI, const chip::ByteSpan & PAA, + chip::Callback::Callback * onCompletion) override; - CHIP_ERROR GetRootCACertificate(chip::FabricId fabricId, chip::MutableByteSpan & outCert) override; + void SetNodeIdForNextNOCRequest(chip::NodeId nodeId) override + { + mNextRequestedNodeId = nodeId; + mNodeIdRequested = true; + } + + void SetFabricIdForNextNOCRequest(chip::FabricId fabricId) override { mNextFabricId = fabricId; } void SetDeviceID(chip::NodeId deviceId) { mDeviceBeingPaired = deviceId; } void ResetDeviceID() { mDeviceBeingPaired = chip::kUndefinedNodeId; } @@ -70,6 +76,10 @@ class CHIPOperationalCredentialsDelegate : public chip::Controller::OperationalC CHIPPersistentStorageDelegateBridge * mStorage; chip::NodeId mDeviceBeingPaired = chip::kUndefinedNodeId; + + chip::NodeId mNextRequestedNodeId = 1; + chip::FabricId mNextFabricId = 0; + bool mNodeIdRequested = false; }; NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm b/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm index 7d756d2a53ac83..29354223609dab 100644 --- a/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm +++ b/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm @@ -23,9 +23,17 @@ #import "CHIPLogging.h" +#include #include #include #include +#include +#include + +constexpr const char kOperationalCredentialsRootCertificateStorage[] = "MatterCARootCert"; + +using namespace chip; +using namespace TLV; static BOOL isRunningTests(void) { @@ -234,9 +242,9 @@ static BOOL isRunningTests(void) return CHIP_NO_ERROR; } -CHIP_ERROR CHIPOperationalCredentialsDelegate::GenerateNodeOperationalCertificate(const chip::Optional & nodeId, - chip::FabricId fabricId, const chip::ByteSpan & csr, const chip::ByteSpan & DAC, - chip::Callback::Callback * onNOCGenerated) +CHIP_ERROR CHIPOperationalCredentialsDelegate::GenerateNOCChain(const chip::ByteSpan & csrElements, + const chip::ByteSpan & attestationSignature, const chip::ByteSpan & DAC, const chip::ByteSpan & PAI, const chip::ByteSpan & PAA, + chip::Callback::Callback * onCompletion) { uint32_t validityStart, validityEnd; @@ -251,8 +259,9 @@ static BOOL isRunningTests(void) } chip::NodeId assignedId; - if (nodeId.HasValue()) { - assignedId = nodeId.Value(); + if (mNodeIdRequested) { + assignedId = mNextRequestedNodeId; + mNodeIdRequested = false; } else { if (mDeviceBeingPaired == chip::kUndefinedNodeId) { return CHIP_ERROR_INCORRECT_STATE; @@ -260,53 +269,61 @@ static BOOL isRunningTests(void) assignedId = mDeviceBeingPaired; } - chip::Credentials::X509CertRequestParams request - = { 1, mIssuerId, validityStart, validityEnd, true, fabricId, true, assignedId }; + chip::Credentials::X509CertRequestParams noc_request + = { 1, mIssuerId, validityStart, validityEnd, true, mNextFabricId, true, assignedId }; - chip::Crypto::P256PublicKey pubkey; - CHIP_ERROR err = chip::Crypto::VerifyCertificateSigningRequest(csr.data(), csr.size(), pubkey); - if (err != CHIP_NO_ERROR) { - return err; + TLVReader reader; + reader.Init(csrElements.data(), static_cast(csrElements.size())); + + if (reader.GetType() == kTLVType_NotSpecified) { + ReturnErrorOnFailure(reader.Next()); } - NSMutableData * nocBuffer = [[NSMutableData alloc] initWithLength:chip::Controller::kMaxCHIPDERCertLength]; - uint32_t nocLen = 0; + VerifyOrReturnError(reader.GetType() == kTLVType_Structure, CHIP_ERROR_WRONG_TLV_TYPE); + VerifyOrReturnError(reader.GetTag() == AnonymousTag, CHIP_ERROR_UNEXPECTED_TLV_ELEMENT); - uint8_t * noc = (uint8_t *) [nocBuffer mutableBytes]; + TLVType containerType; + ReturnErrorOnFailure(reader.EnterContainer(containerType)); + ReturnErrorOnFailure(reader.Next(kTLVType_ByteString, TLV::ContextTag(1))); - err = chip::Credentials::NewNodeOperationalX509Cert(request, chip::Credentials::CertificateIssuerLevel::kIssuerIsRootCA, pubkey, - mIssuerKey, noc, chip::Controller::kMaxCHIPDERCertLength, nocLen); - if (err != CHIP_NO_ERROR) { - return err; - } + ByteSpan csr(reader.GetReadPoint(), reader.GetLength()); + reader.ExitContainer(containerType); - onNOCGenerated->mCall(onNOCGenerated->mContext, chip::ByteSpan(noc, nocLen)); + chip::Crypto::P256PublicKey pubkey; + ReturnErrorOnFailure(chip::Crypto::VerifyCertificateSigningRequest(csr.data(), csr.size(), pubkey)); - return CHIP_NO_ERROR; -} + NSMutableData * nocBuffer = [[NSMutableData alloc] initWithLength:chip::Controller::kMaxCHIPDERCertLength]; + uint32_t nocLen = 0; + uint8_t * noc = (uint8_t *) [nocBuffer mutableBytes]; -CHIP_ERROR CHIPOperationalCredentialsDelegate::GetRootCACertificate(chip::FabricId fabricId, chip::MutableByteSpan & outCert) -{ - // TODO: Don't generate root certificate unless there's none, or the current is expired. - uint32_t validityStart, validityEnd; + ReturnErrorOnFailure( + chip::Credentials::NewNodeOperationalX509Cert(noc_request, chip::Credentials::CertificateIssuerLevel::kIssuerIsRootCA, + pubkey, mIssuerKey, noc, chip::Controller::kMaxCHIPDERCertLength, nocLen)); - if (!ToChipEpochTime(0, validityStart)) { - NSLog(@"Failed in computing certificate validity start date"); - return CHIP_ERROR_INTERNAL; - } + NSMutableData * rcacBuffer = [[NSMutableData alloc] initWithLength:chip::Controller::kMaxCHIPDERCertLength]; + uint16_t rcacLen = chip::Controller::kMaxCHIPDERCertLength; + uint8_t * rcac = (uint8_t *) [rcacBuffer mutableBytes]; - if (!ToChipEpochTime(kCertificateValiditySecs, validityEnd)) { - NSLog(@"Failed in computing certificate validity end date"); - return CHIP_ERROR_INTERNAL; - } + CHIP_ERROR err = CHIP_NO_ERROR; + PERSISTENT_KEY_OP( + mNextFabricId, kOperationalCredentialsRootCertificateStorage, key, err = mStorage->SyncGetKeyValue(key, rcac, rcacLen)); - chip::Credentials::X509CertRequestParams request = { 0, mIssuerId, validityStart, validityEnd, true, fabricId, false, 0 }; + if (err != CHIP_NO_ERROR) { + chip::Credentials::X509CertRequestParams rcac_request + = { 0, mIssuerId, validityStart, validityEnd, true, mNextFabricId, false, 0 }; + uint32_t outCertLen = 0; + ReturnErrorOnFailure(chip::Credentials::NewRootX509Cert( + rcac_request, mIssuerKey, rcac, chip::Controller::kMaxCHIPDERCertLength, outCertLen)); + + VerifyOrReturnError(CanCastTo(outCertLen), CHIP_ERROR_INVALID_ARGUMENT); + rcacLen = static_cast(outCertLen); + PERSISTENT_KEY_OP( + mNextFabricId, kOperationalCredentialsRootCertificateStorage, key, err = mStorage->SyncSetKeyValue(key, rcac, rcacLen)); + ReturnErrorOnFailure(err); + } - size_t outCertSize = (outCert.size() > UINT32_MAX) ? UINT32_MAX : outCert.size(); - uint32_t outCertLen = 0; - ReturnErrorOnFailure( - chip::Credentials::NewRootX509Cert(request, mIssuerKey, outCert.data(), static_cast(outCertSize), outCertLen)); - outCert.reduce_size(outCertLen); + onCompletion->mCall( + onCompletion->mContext, CHIP_NO_ERROR, chip::ByteSpan(noc, nocLen), chip::ByteSpan(), chip::ByteSpan(rcac, rcacLen)); return CHIP_NO_ERROR; } diff --git a/src/darwin/Framework/CHIP/gen/CHIPClustersObjc.mm b/src/darwin/Framework/CHIP/gen/CHIPClustersObjc.mm index abae09a3ceebc2..9176c8c97af057 100644 --- a/src/darwin/Framework/CHIP/gen/CHIPClustersObjc.mm +++ b/src/darwin/Framework/CHIP/gen/CHIPClustersObjc.mm @@ -2300,20 +2300,15 @@ static void CallbackFn(void * context, uint8_t StatusCode, uint8_t FabricIndex, ~CHIPOperationalCredentialsClusterOpCSRResponseCallbackBridge() {}; - static void CallbackFn(void * context, chip::ByteSpan CSR, chip::ByteSpan CSRNonce, chip::ByteSpan VendorReserved1, - chip::ByteSpan VendorReserved2, chip::ByteSpan VendorReserved3, chip::ByteSpan Signature) + static void CallbackFn(void * context, chip::ByteSpan NOCSRElements, chip::ByteSpan AttestationSignature) { CHIPOperationalCredentialsClusterOpCSRResponseCallbackBridge * callback = reinterpret_cast(context); if (callback && callback->mQueue) { dispatch_async(callback->mQueue, ^{ callback->mHandler(nil, @ { - @"CSR" : [NSData dataWithBytes:CSR.data() length:CSR.size()], - @"CSRNonce" : [NSData dataWithBytes:CSRNonce.data() length:CSRNonce.size()], - @"VendorReserved1" : [NSData dataWithBytes:VendorReserved1.data() length:VendorReserved1.size()], - @"VendorReserved2" : [NSData dataWithBytes:VendorReserved2.data() length:VendorReserved2.size()], - @"VendorReserved3" : [NSData dataWithBytes:VendorReserved3.data() length:VendorReserved3.size()], - @"Signature" : [NSData dataWithBytes:Signature.data() length:Signature.size()], + @"NOCSRElements" : [NSData dataWithBytes:NOCSRElements.data() length:NOCSRElements.size()], + @"AttestationSignature" : [NSData dataWithBytes:AttestationSignature.data() length:AttestationSignature.size()], }); callback->Cancel(); delete callback;