diff --git a/.gitmodules b/.gitmodules index 86981aa68619aa..9f69906a4909aa 100644 --- a/.gitmodules +++ b/.gitmodules @@ -354,3 +354,10 @@ url = https://github.com/paullouisageneau/libdatachannel.git platforms = linux recursive = true +[submodule "third_party/libtrustymatter/repo"] + path = third_party/libtrustymatter/repo + url = https://github.com/nxp-imx/libtrustymatter +[submodule "repo"] + path = third_party/imx-secure-enclave/repo + url = https://github.com/nxp-imx/imx-secure-enclave.git + branch = lf-6.12.3_1.0.0 diff --git a/examples/platform/linux/AppMain.cpp b/examples/platform/linux/AppMain.cpp index 52fd41d79dabe8..6ed837f738b593 100644 --- a/examples/platform/linux/AppMain.cpp +++ b/examples/platform/linux/AppMain.cpp @@ -126,6 +126,21 @@ #include #endif // CHIP_DEVICE_LAYER_TARGET_LINUX +#if CHIP_ATTESTATION_TRUSTY_OS +#include "DeviceAttestationCreds.h" +using namespace chip::Credentials::Trusty; +#endif + +#if CHIP_OP_KEYSTORE_TRUSTY_OS +#include "PersistentStorageOperationalKeystoreTrusty.h" +using namespace chip::Trusty; +#endif + +#if CHIP_OP_KEYSTORE_ELE +#include "PersistentStorageOperationalKeystoreEle.h" +using namespace chip::ele; +#endif + using namespace chip; using namespace chip::ArgParser; using namespace chip::Credentials; @@ -571,6 +586,16 @@ void ChipLinuxAppMainLoop(AppMainLoopImplementation * impl) } #endif // CHIP_CONFIG_TERMS_AND_CONDITIONS_REQUIRED +#if CHIP_OP_KEYSTORE_TRUSTY_OS + static chip::Trusty::PersistentStorageOperationalKeystoreTrusty sPersistentStorageOperationalKeystore; + initParams.operationalKeystore = &sPersistentStorageOperationalKeystore; +#endif + +#if CHIP_OP_KEYSTORE_ELE + static chip::ele::PersistentStorageOperationalKeystoreEle sPersistentStorageOperationalKeystore; + initParams.operationalKeystore = &sPersistentStorageOperationalKeystore; +#endif + #if defined(ENABLE_CHIP_SHELL) Engine::Root().Init(); Shell::RegisterCommissioneeCommands(); @@ -710,7 +735,11 @@ void ChipLinuxAppMainLoop(AppMainLoopImplementation * impl) PrintOnboardingCodes(LinuxDeviceOptions::GetInstance().payload); // Initialize device attestation config +#if CHIP_ATTESTATION_TRUSTY_OS + SetDeviceAttestationCredentialsProvider(&TrustyDACProvider::GetTrustyDACProvider()); +#else SetDeviceAttestationCredentialsProvider(LinuxDeviceOptions::GetInstance().dacProvider); +#endif #if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE ChipLogProgress(AppServer, "Starting commissioner"); @@ -737,9 +766,9 @@ void ChipLinuxAppMainLoop(AppMainLoopImplementation * impl) signal(SIGTERM, StopSignalHandler); // NOLINTEND(bugprone-signal-handler) #else - struct sigaction sa = {}; - sa.sa_handler = StopSignalHandler; - sa.sa_flags = SA_RESETHAND; + struct sigaction sa = {}; + sa.sa_handler = StopSignalHandler; + sa.sa_flags = SA_RESETHAND; sigaction(SIGINT, &sa, nullptr); sigaction(SIGTERM, &sa, nullptr); #endif diff --git a/examples/platform/linux/BUILD.gn b/examples/platform/linux/BUILD.gn index a31a72ed834e15..e3f6de29bb84c3 100644 --- a/examples/platform/linux/BUILD.gn +++ b/examples/platform/linux/BUILD.gn @@ -17,7 +17,9 @@ import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni") import("${chip_root}/src/app/common_flags.gni") import("${chip_root}/src/app/icd/icd.gni") import("${chip_root}/src/lib/core/core.gni") +import("${chip_root}/src/lib/imx_ele.gni") import("${chip_root}/src/lib/lib.gni") +import("${chip_root}/src/lib/trusty.gni") import("${chip_root}/src/tracing/tracing_args.gni") if (current_os != "nuttx") { @@ -96,6 +98,22 @@ source_set("app-main") { "testing/CustomCSRResponseOperationalKeyStore.h", ] + if (chip_with_trusty_os == 1) { + sources += [ + "DeviceAttestationCreds.cpp", + "DeviceAttestationCreds.h", + "PersistentStorageOperationalKeystoreTrusty.cpp", + "PersistentStorageOperationalKeystoreTrusty.h", + ] + } + + if (chip_with_imx_ele == 1) { + sources += [ + "PersistentStorageOperationalKeystoreEle.cpp", + "PersistentStorageOperationalKeystoreEle.h", + ] + } + public_deps = [ ":boolean-state-configuration-test-event-trigger", ":commissioner-main", @@ -122,6 +140,33 @@ source_set("app-main") { public_deps += [ jsoncpp_root ] } + if (chip_with_trusty_os == 1) { + public_deps += [ "${chip_root}/third_party/libtrustymatter" ] + } + + if (chip_with_imx_ele == 1) { + public_deps += + [ "${chip_root}/third_party/imx-secure-enclave:libelematter" ] + } + + if (chip_with_trusty_os == 1) { + defines += [ "CHIP_ATTESTATION_TRUSTY_OS=1" ] + } else { + defines += [ "CHIP_ATTESTATION_TRUSTY_OS=0" ] + } + + if (chip_with_trusty_os == 1) { + defines += [ "CHIP_OP_KEYSTORE_TRUSTY_OS=1" ] + } else { + defines += [ "CHIP_OP_KEYSTORE_TRUSTY_OS=0" ] + } + + if (chip_with_imx_ele == 1) { + defines += [ "CHIP_OP_KEYSTORE_ELE=1" ] + } else { + defines += [ "CHIP_OP_KEYSTORE_ELE=0" ] + } + if (chip_enable_pw_rpc) { defines += [ "PW_RPC_ENABLED" ] } diff --git a/examples/platform/linux/DeviceAttestationCreds.cpp b/examples/platform/linux/DeviceAttestationCreds.cpp new file mode 100644 index 00000000000000..c933136b300017 --- /dev/null +++ b/examples/platform/linux/DeviceAttestationCreds.cpp @@ -0,0 +1,113 @@ +/* + * + * Copyright (c) 2021-2022 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. + * + * Copyright 2023 NXP + */ +#include "DeviceAttestationCreds.h" + +#include +#include +#include +#include + +using namespace matter; + +namespace chip { +namespace Credentials { +namespace Trusty { + +CHIP_ERROR TrustyDACProvider::GetDeviceAttestationCert(MutableByteSpan & out_dac_buffer) +{ + size_t out_size = 0; + int rc; + + rc = trusty_matter.ExportDACCert(out_dac_buffer.data(), out_dac_buffer.size(), out_size); + if (rc == 0) + { + out_dac_buffer.reduce_size(out_size); + return CHIP_NO_ERROR; + } + else + return CHIP_ERROR_CERT_LOAD_FAILED; +} + +CHIP_ERROR TrustyDACProvider::GetProductAttestationIntermediateCert(MutableByteSpan & out_pai_buffer) +{ + size_t out_size = 0; + int rc; + + rc = trusty_matter.ExportPAICert(out_pai_buffer.data(), out_pai_buffer.size(), out_size); + if (rc == 0) + { + out_pai_buffer.reduce_size(out_size); + return CHIP_NO_ERROR; + } + else + return CHIP_ERROR_CERT_LOAD_FAILED; +} + +CHIP_ERROR TrustyDACProvider::GetCertificationDeclaration(MutableByteSpan & out_cd_buffer) +{ + size_t out_size = 0; + int rc; + + rc = trusty_matter.ExportCDCert(out_cd_buffer.data(), out_cd_buffer.size(), out_size); + if (rc == 0) + { + out_cd_buffer.reduce_size(out_size); + return CHIP_NO_ERROR; + } + else + return CHIP_ERROR_CERT_LOAD_FAILED; +} + +CHIP_ERROR TrustyDACProvider::GetFirmwareInformation(MutableByteSpan & out_firmware_info_buffer) +{ + // TODO: We need a real example FirmwareInformation to be populated. + out_firmware_info_buffer.reduce_size(0); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR TrustyDACProvider::SignWithDeviceAttestationKey(const ByteSpan & message_to_sign, MutableByteSpan & out_signature_buffer) +{ + int rc = 0; + size_t out_size = 0; + + VerifyOrReturnError(IsSpanUsable(out_signature_buffer), CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(IsSpanUsable(message_to_sign), CHIP_ERROR_INVALID_ARGUMENT); + + rc = trusty_matter.SignWithDACKey(message_to_sign.data(), message_to_sign.size(), out_signature_buffer.data(), + out_signature_buffer.size(), out_size); + if (rc == 0) + { + out_signature_buffer.reduce_size(out_size); + return CHIP_NO_ERROR; + } + else + return CHIP_ERROR_CERT_LOAD_FAILED; +} + +TrustyDACProvider & TrustyDACProvider::GetTrustyDACProvider() +{ + static TrustyDACProvider trusty_dac_provider; + + return trusty_dac_provider; +} + +} // namespace Trusty +} // namespace Credentials +} // namespace chip diff --git a/examples/platform/linux/DeviceAttestationCreds.h b/examples/platform/linux/DeviceAttestationCreds.h new file mode 100644 index 00000000000000..733fc5133418ff --- /dev/null +++ b/examples/platform/linux/DeviceAttestationCreds.h @@ -0,0 +1,46 @@ +/* + * Copyright 2023 NXP + * + * 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 + +using namespace matter; + +namespace chip { +namespace Credentials { +namespace Trusty { + +class TrustyDACProvider : public DeviceAttestationCredentialsProvider +{ +public: + static TrustyDACProvider & GetTrustyDACProvider(); + + CHIP_ERROR GetCertificationDeclaration(MutableByteSpan & out_cd_buffer) override; + CHIP_ERROR GetFirmwareInformation(MutableByteSpan & out_firmware_info_buffer) override; + CHIP_ERROR GetDeviceAttestationCert(MutableByteSpan & out_dac_buffer) override; + CHIP_ERROR GetProductAttestationIntermediateCert(MutableByteSpan & out_pai_buffer) override; + CHIP_ERROR SignWithDeviceAttestationKey(const ByteSpan & message_to_sign, MutableByteSpan & out_signature_buffer) override; + +private: + TrustyMatter trusty_matter; +}; + +} // namespace Trusty +} // namespace Credentials +} // namespace chip diff --git a/examples/platform/linux/PersistentStorageOperationalKeystoreEle.cpp b/examples/platform/linux/PersistentStorageOperationalKeystoreEle.cpp new file mode 100644 index 00000000000000..d556288dc7d63e --- /dev/null +++ b/examples/platform/linux/PersistentStorageOperationalKeystoreEle.cpp @@ -0,0 +1,638 @@ +/* + * Copyright (c) 2022 Project CHIP Authors + * All rights reserved. + * + * 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. + * + * Copyright 2023 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "PersistentStorageOperationalKeystoreEle.h" + +const uint8_t kTlvHeader = 2; + +#define KEY_STORE_ID (0xAAAA) +#define AUTHEN_NONCE (0x1111) +#define KEY_GROUP (1) +#define PUBKEY_SIZE (65) +#define KEY_GROUP_SYNC (0x1U << 7) + +/* Used for CSR generation */ +// Organisation info. +#define SUBJECT_STR "CSR" +#define ASN1_BIT_STRING 0x03 +#define ASN1_NULL 0x05 +#define ASN1_OID 0x06 +#define ASN1_SEQUENCE 0x10 +#define ASN1_SET 0x11 +#define ASN1_UTF8_STRING 0x0C +#define ASN1_CONSTRUCTED 0x20 +#define ASN1_CONTEXT_SPECIFIC 0x80 + +namespace chip { +namespace ele { + +using namespace chip::Crypto; + +PersistentStorageOperationalKeystoreEle::PersistentStorageOperationalKeystoreEle() +{ + hsm_err_t err; + + // open the session + open_session_args_t open_session_args = { 0 }; + open_session_args.mu_type = HSM1; + err = hsm_open_session(&open_session_args, &hsm_session_hdl); + if (err != HSM_NO_ERROR) + { + ChipLogDetail(Crypto, "ELE session open failed. ret: 0x%x\n", err); + return; + } + else + { + ChipLogDetail(Crypto, "ELE session open successfully.\n"); + } + + // open the keystore + open_svc_key_store_args_t open_svc_key_store_args = { 0 }; + open_svc_key_store_args.key_store_identifier = KEY_STORE_ID; + open_svc_key_store_args.authentication_nonce = AUTHEN_NONCE; + // try to create a new keystore, if it already exist, open it + open_svc_key_store_args.flags = 1; + err = hsm_open_key_store_service(hsm_session_hdl, &open_svc_key_store_args, &key_store_hdl); + if (err == HSM_KEY_STORE_CONFLICT) + { + ChipLogDetail(Crypto, "keystore already existed, open it...\n"); + open_svc_key_store_args.flags = 0; + err = hsm_open_key_store_service(hsm_session_hdl, &open_svc_key_store_args, &key_store_hdl); + if (err != HSM_NO_ERROR) + { + ChipLogDetail(Crypto, "keystore open failed. ret:0x%x\n", err); + return; + } + else + ChipLogDetail(Crypto, "keystore open successfully.\n"); + } + else + { + ChipLogDetail(Crypto, "keystore created successfully.\n"); + } + + // open key managerment service + open_svc_key_management_args_t key_mgmt_args = { 0 }; + memset(&key_mgmt_args, 0, sizeof(key_mgmt_args)); + err = hsm_open_key_management_service(key_store_hdl, &key_mgmt_args, &key_mgmt_hdl); + if (err != HSM_NO_ERROR) + { + ChipLogDetail(Crypto, "key management service open failed. ret:0x%x\n", err); + return; + } + else + ChipLogDetail(Crypto, "key management service open successfully.\n"); +} + +PersistentStorageOperationalKeystoreEle::~PersistentStorageOperationalKeystoreEle() +{ + hsm_err_t err; + + ResetPendingKey(true); + + ChipLogDetail(Crypto, "close all ELE services.\n"); + + err = hsm_close_key_management_service(key_mgmt_hdl); + key_mgmt_hdl = 0; + ChipLogDetail(Crypto, "close key management service returns:0x%x\n", err); + + err = hsm_close_key_store_service(key_store_hdl); + key_store_hdl = 0; + ChipLogDetail(Crypto, "close key store service returns:0x%x\n", err); + + err = hsm_close_session(hsm_session_hdl); + hsm_session_hdl = 0; + ChipLogDetail(Crypto, "close ELE session returns:0x%x\n", err); +} + +void PersistentStorageOperationalKeystoreEle::ResetPendingKey(bool delete_key) +{ + if ((mPendingFabricIndex != kUndefinedFabricIndex) && (delete_key)) + { + EleDeleteKey(mPendingFabricIndex); + } + mIsExternallyOwnedKeypair = false; + mIsPendingKeypairActive = false; + mPendingFabricIndex = kUndefinedFabricIndex; +} + +hsm_err_t PersistentStorageOperationalKeystoreEle::EleDeleteKey(uint32_t keyId) +{ + hsm_err_t err; + op_delete_key_args_t del_args; + + memset(&del_args, 0, sizeof(del_args)); + del_args.key_identifier = keyId; + del_args.flags = 0; + + err = hsm_delete_key(key_mgmt_hdl, &del_args); + if (err != HSM_NO_ERROR) + { + ChipLogDetail(Crypto, "Delete key %d failed. ret:0x%x\n", keyId, err); + } + else + { + ChipLogDetail(Crypto, "Delete key %d successfully.\n", keyId); + } + + return err; +} + +hsm_err_t PersistentStorageOperationalKeystoreEle::EleSignMessage(uint32_t keyId, const uint8_t * msg, size_t msgSize, + uint8_t * sig, size_t sigSize) const +{ + open_svc_sign_gen_args_t open_sig_gen_args; + op_generate_sign_args_t sig_gen_args; + hsm_hdl_t sig_gen_hdl; + hsm_err_t hsmret; + + if ((msg == nullptr) || (sig == nullptr) || (sigSize == 0)) + { + ChipLogDetail(Crypto, "Invalid parameters for generating signature.\n"); + return HSM_INVALID_PARAM; + } + + // open signature generation service + memset(&open_sig_gen_args, 0, sizeof(open_sig_gen_args)); + hsmret = hsm_open_signature_generation_service(key_store_hdl, &open_sig_gen_args, &sig_gen_hdl); + if (hsmret != HSM_NO_ERROR) + { + ChipLogDetail(Crypto, "open signature generation service failed. ret:0x%x\n", hsmret); + return hsmret; + } + + // generate signature + memset(&sig_gen_args, 0, sizeof(sig_gen_args)); + sig_gen_args.key_identifier = keyId; + sig_gen_args.scheme_id = HSM_SIGNATURE_SCHEME_ECDSA_SHA256; + sig_gen_args.message = (uint8_t *) msg; + sig_gen_args.signature = sig; + sig_gen_args.message_size = msgSize; + sig_gen_args.signature_size = sigSize; + sig_gen_args.flags = HSM_OP_GENERATE_SIGN_FLAGS_INPUT_MESSAGE; + hsmret = hsm_generate_signature(sig_gen_hdl, &sig_gen_args); + if (hsmret != HSM_NO_ERROR) + { + ChipLogDetail(Crypto, "generate signature failed. ret:0x%x\n", hsmret); + return hsmret; + } + + // close signature generation service + hsmret = hsm_close_signature_generation_service(sig_gen_hdl); + if (hsmret != HSM_NO_ERROR) + { + ChipLogDetail(Crypto, "close signature generation service failed. ret:0x%x\n", hsmret); + } + + return hsmret; +} + +static void add_tlv(uint8_t * buf, size_t buf_index, uint8_t tag, size_t len, uint8_t * val) +{ + buf[buf_index++] = (uint8_t) tag; + buf[buf_index++] = (uint8_t) len; + if (len > 0 && val != NULL) + { + memcpy(&buf[buf_index], val, len); + buf_index = buf_index + len; + } +} + +/* + * CSR format used in the below function, + * + * + * (ASN1_CONSTRUCTED | ASN1_SEQUENCE) LENGTH + * + * (ASN1_CONSTRUCTED | ASN1_SEQUENCE) LENGTH + * + * VERSION ::= INTEGER { v1(0), v2(1), v3(2) } + * + * (ASN1_CONSTRUCTED | ASN1_SEQUENCE) LENGTH + * + * (ASN1_CONSTRUCTED | ASN1_SET) LENGTH + * + * (ASN1_CONSTRUCTED | ASN1_SEQUENCE) LENGTH + * + * (ASN1_OID) LENGTH VALUE(Organisation OID) + * + * (ASN1_UTF8_STRING) LENGTH VALUE(Subject Str == "CSR") + * + * PUBLIC KEY {WITH HEADER. 91 Bytes} + * + * (ASN1_CONSTRUCTED | ASN1_SEQUENCE) LENGTH + * + * (ASN1_OID) LENGTH VALUE(ECDSA SHA256 OID) + * + * (ASN1_NULL) 0x00 + * + * (ASN1_BIT_STRING) LENGTH VALUE(SIGNATURE) + * + */ +CHIP_ERROR PersistentStorageOperationalKeystoreEle::EleGenerateCSR(uint32_t keyId, uint8_t * csr, size_t & csrLength) +{ + CHIP_ERROR error = CHIP_ERROR_INTERNAL; + hsm_err_t hsmret = HSM_NO_ERROR; + + uint8_t data_to_hash[128] = { 0 }; + size_t data_to_hash_len = sizeof(data_to_hash); + uint8_t pubKey[128] = { 0 }; + size_t pubKeyLen = 0; + uint8_t signature[128] = { 0 }; + size_t signature_len = 0x44; + int signature_index = 0; + uint8_t signature_data[64]; + size_t csrIndex = 0; + size_t buffer_index = data_to_hash_len; + + uint8_t organisation_oid[3] = { 0x55, 0x04, 0x0a }; + // Version ::= INTEGER { v1(0), v2(1), v3(2) } + uint8_t version[3] = { 0x02, 0x01, 0x00 }; + uint8_t signature_oid[8] = { 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02 }; + uint8_t nist256_header[] = { 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, + 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00 }; + uint8_t signature_header_t1[] = { 0x02, 0x20 }; + uint8_t signature_header_t2[] = { 0x02, 0x21, 0x00 }; + + // No extensions are copied + buffer_index -= kTlvHeader; + add_tlv(data_to_hash, buffer_index, (ASN1_CONSTRUCTED | ASN1_CONTEXT_SPECIFIC), 0, NULL); + + // Copy public key (with header) + { + // copy header first + memcpy(pubKey, nist256_header, sizeof(nist256_header)); + pubKeyLen = pubKeyLen + sizeof(nist256_header); + + // public key size is 65 + pubKey[pubKeyLen] = 0x04; + pubKeyLen++; + + op_pub_key_recovery_args_t args = { 0 }; + args.key_identifier = keyId; + args.out_key_size = 64; + args.out_key = (pubKey + pubKeyLen); + hsmret = hsm_pub_key_recovery(key_store_hdl, &args); + VerifyOrExit(hsmret == HSM_NO_ERROR, error = CHIP_ERROR_HSM); + pubKeyLen += 64; + } + + buffer_index -= pubKeyLen; + VerifyOrExit(buffer_index > 0, error = CHIP_ERROR_INTERNAL); + memcpy((void *) &data_to_hash[buffer_index], pubKey, pubKeyLen); + + // Copy subject (in the current implementation only organisation name info is added) and organisation OID + buffer_index -= (kTlvHeader + sizeof(SUBJECT_STR) - 1); + VerifyOrExit(buffer_index > 0, error = CHIP_ERROR_INTERNAL); + add_tlv(data_to_hash, buffer_index, ASN1_UTF8_STRING, sizeof(SUBJECT_STR) - 1, (uint8_t *) SUBJECT_STR); + + buffer_index -= (kTlvHeader + sizeof(organisation_oid)); + VerifyOrExit(buffer_index > 0, error = CHIP_ERROR_INTERNAL); + add_tlv(data_to_hash, buffer_index, ASN1_OID, sizeof(organisation_oid), organisation_oid); + + // Add length + buffer_index -= kTlvHeader; + // Subject TLV ==> 1 + 1 + len(subject) + // Org OID TLV ==> 1 + 1 + len(organisation_oid) + VerifyOrExit(buffer_index > 0, error = CHIP_ERROR_INTERNAL); + add_tlv(data_to_hash, buffer_index, (ASN1_CONSTRUCTED | ASN1_SEQUENCE), + ((2 * kTlvHeader) + (sizeof(SUBJECT_STR) - 1) + sizeof(organisation_oid)), NULL); + + buffer_index -= kTlvHeader; + VerifyOrExit(buffer_index > 0, error = CHIP_ERROR_INTERNAL); + add_tlv(data_to_hash, buffer_index, (ASN1_CONSTRUCTED | ASN1_SET), + ((3 * kTlvHeader) + (sizeof(SUBJECT_STR) - 1) + sizeof(organisation_oid)), NULL); + + buffer_index -= kTlvHeader; + VerifyOrExit(buffer_index > 0, error = CHIP_ERROR_INTERNAL); + add_tlv(data_to_hash, buffer_index, (ASN1_CONSTRUCTED | ASN1_SEQUENCE), + ((4 * kTlvHeader) + (sizeof(SUBJECT_STR) - 1) + sizeof(organisation_oid)), NULL); + + buffer_index -= 3; + VerifyOrExit(buffer_index > 0, error = CHIP_ERROR_INTERNAL); + memcpy((void *) &data_to_hash[buffer_index], version, sizeof(version)); + + buffer_index -= kTlvHeader; + VerifyOrExit(buffer_index > 0, error = CHIP_ERROR_INTERNAL); + add_tlv(data_to_hash, buffer_index, (ASN1_CONSTRUCTED | ASN1_SEQUENCE), (data_to_hash_len - buffer_index - kTlvHeader), NULL); + + // TLV data is created by copying from backwards. move it to start of buffer. + data_to_hash_len = (data_to_hash_len - buffer_index); + memmove(data_to_hash, (data_to_hash + buffer_index), data_to_hash_len); + + hsmret = EleSignMessage(keyId, (uint8_t *) data_to_hash, data_to_hash_len, signature_data, sizeof(signature_data)); + VerifyOrExit(hsmret == HSM_NO_ERROR, error = CHIP_ERROR_HSM); + + if (signature_data[0] > 0x7F) + { + signature_len += 1; + memcpy(signature, signature_header_t2, sizeof(signature_header_t2)); + signature_index += 3; + } + else + { + memcpy(signature, signature_header_t1, sizeof(signature_header_t1)); + signature_index += 2; + } + memcpy(&signature[signature_index], signature_data, 32); + signature_index += 32; + if (signature_data[32] > 0x7F) + { + signature_len += 1; + memcpy(&signature[signature_index], signature_header_t2, 3); + signature_index += 3; + } + else + { + memcpy(&signature[signature_index], signature_header_t1, 2); + signature_index += 2; + } + memcpy(&signature[signature_index], &signature_data[32], 32); + + VerifyOrExit((csrIndex + 3) <= csrLength, error = CHIP_ERROR_INTERNAL); + csr[csrIndex++] = (ASN1_CONSTRUCTED | ASN1_SEQUENCE); + if ((data_to_hash_len + 14 + kTlvHeader + signature_len) >= 0x80) + csr[csrIndex++] = 0x81; + csr[csrIndex++] = (uint8_t) (data_to_hash_len + sizeof(signature_oid) + (kTlvHeader * 2) + 5 + signature_len); + + VerifyOrExit((csrIndex + data_to_hash_len) <= csrLength, error = CHIP_ERROR_INTERNAL); + memcpy((csr + csrIndex), data_to_hash, data_to_hash_len); + csrIndex = csrIndex + data_to_hash_len; + + // ECDSA SHA256 Signature OID TLV ==> 1 + 1 + len(signature_oid) (8) + // ASN_NULL ==> 1 + 1 + VerifyOrExit((csrIndex + kTlvHeader) <= csrLength, error = CHIP_ERROR_INTERNAL); + add_tlv(csr, csrIndex, (ASN1_CONSTRUCTED | ASN1_SEQUENCE), 0x0A, NULL); + csrIndex = csrIndex + kTlvHeader; + + VerifyOrExit((csrIndex + sizeof(signature_oid) + kTlvHeader) <= csrLength, error = CHIP_ERROR_INTERNAL); + add_tlv(csr, csrIndex, ASN1_OID, sizeof(signature_oid), signature_oid); + csrIndex = csrIndex + kTlvHeader + sizeof(signature_oid); + + // VerifyOrExit((csrIndex + kTlvHeader) <= csrLength, error = CHIP_ERROR_INTERNAL); + // add_tlv(csr, csrIndex, ASN1_NULL, 0x00, NULL); + // csrIndex = csrIndex + kTlvHeader; + + VerifyOrExit((csrIndex + 5) <= csrLength, error = CHIP_ERROR_INTERNAL); + csr[csrIndex++] = ASN1_BIT_STRING; + csr[csrIndex++] = signature_len + 3; + csr[csrIndex++] = 0x00; + csr[csrIndex++] = (ASN1_CONSTRUCTED | ASN1_SEQUENCE); + csr[csrIndex++] = signature_len; + + VerifyOrExit((csrIndex + signature_len) <= csrLength, error = CHIP_ERROR_INTERNAL); + memcpy(&csr[csrIndex], signature, signature_len); + csrLength = (csrIndex + signature_len); + + error = CHIP_NO_ERROR; + +exit: + return error; +} + +bool PersistentStorageOperationalKeystoreEle::HasOpKeypairForFabric(FabricIndex fabricIndex) const +{ + op_get_key_attr_args_t keyattr_args; + hsm_err_t err; + + VerifyOrReturnError(IsValidFabricIndex(fabricIndex), false); + // If there was a pending keypair, then there's really a usable key + if (mIsPendingKeypairActive && (fabricIndex == mPendingFabricIndex)) + { + return true; + } + + // get the key attributes, it shall fail if no such key + memset(&keyattr_args, 0, sizeof(keyattr_args)); + keyattr_args.key_identifier = fabricIndex; + err = hsm_get_key_attr(key_mgmt_hdl, &keyattr_args); + if (err != HSM_NO_ERROR) + { + ChipLogDetail(Crypto, "No keypair for fabric: %d found. ELE returns: 0x%x\n", fabricIndex, err); + return false; + } + else + { + ChipLogDetail(Crypto, "Found keypair for fabric: %d.\n", fabricIndex); + return true; + } +} + +CHIP_ERROR PersistentStorageOperationalKeystoreEle::NewOpKeypairForFabric(FabricIndex fabricIndex, + MutableByteSpan & outCertificateSigningRequest) +{ + op_generate_key_args_t key_gen_args; + op_get_key_attr_args_t keyattr_args; + uint32_t keyId; + hsm_err_t hsm_err; + CHIP_ERROR err; + + VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); + // If a key is pending, we cannot generate for a different fabric index until we commit or revert. + if ((mPendingFabricIndex != kUndefinedFabricIndex) && (fabricIndex != mPendingFabricIndex)) + { + return CHIP_ERROR_INVALID_FABRIC_INDEX; + } + VerifyOrReturnError(outCertificateSigningRequest.size() >= Crypto::kMIN_CSR_Buffer_Size, CHIP_ERROR_BUFFER_TOO_SMALL); + + // Replace previous pending keypair + ResetPendingKey(true); + + // Delete the key if alrady existed + memset(&keyattr_args, 0, sizeof(keyattr_args)); + keyattr_args.key_identifier = fabricIndex; + hsm_err = hsm_get_key_attr(key_mgmt_hdl, &keyattr_args); + if (hsm_err == HSM_NO_ERROR) + { + ChipLogDetail(Crypto, "Keypair for fabric: %d exists, delete it...\n", fabricIndex); + EleDeleteKey(fabricIndex); + } + + // Generate new key + memset(&key_gen_args, 0, sizeof(key_gen_args)); + keyId = fabricIndex; + key_gen_args.key_identifier = &keyId; + key_gen_args.key_group = KEY_GROUP; + key_gen_args.key_lifetime = HSM_SE_KEY_STORAGE_PERSISTENT; + key_gen_args.key_usage = + HSM_KEY_USAGE_SIGN_HASH | HSM_KEY_USAGE_VERIFY_HASH | HSM_KEY_USAGE_SIGN_MSG | HSM_KEY_USAGE_VERIFY_MSG; + key_gen_args.permitted_algo = PERMITTED_ALGO_ECDSA_SHA256; + // "sync" flag is not set so the key won't be committed to NVM immediately. + // we will commit the key in the CommitOpKeypairForFabric(). + key_gen_args.flags = 0; + key_gen_args.key_type = HSM_KEY_TYPE_ECC_NIST; + key_gen_args.bit_key_sz = HSM_KEY_SIZE_ECC_NIST_256; + key_gen_args.key_lifecycle = (hsm_key_lifecycle_t) 0; + hsm_err = hsm_generate_key(key_mgmt_hdl, &key_gen_args); + ChipLogDetail(Crypto, "Generate new keypair returns: 0x%x\n", hsm_err); + if (hsm_err != HSM_NO_ERROR) + return CHIP_ERROR_HSM; + + // generate the CSR + size_t csrLength = outCertificateSigningRequest.size(); + err = EleGenerateCSR(keyId, outCertificateSigningRequest.data(), csrLength); + if (err != CHIP_NO_ERROR) + { + ResetPendingKey(true); + return err; + } + + outCertificateSigningRequest.reduce_size(csrLength); + mPendingFabricIndex = fabricIndex; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR PersistentStorageOperationalKeystoreEle::ActivateOpKeypairForFabric(FabricIndex fabricIndex, + const Crypto::P256PublicKey & nocPublicKey) +{ + uint8_t pubkey[PUBKEY_SIZE]; + hsm_err_t hsmret = HSM_NO_ERROR; + + VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && (fabricIndex == mPendingFabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); + + // Validate public key being activated matches last generated pending keypair + // public key size is 65 + pubkey[0] = 0x04; + + op_pub_key_recovery_args_t args = { 0 }; + args.key_identifier = fabricIndex; + args.out_key_size = 64; + args.out_key = &pubkey[1]; + hsmret = hsm_pub_key_recovery(key_store_hdl, &args); + if (hsmret != HSM_NO_ERROR) + { + ChipLogDetail(Crypto, "recover public key failed. ret:0x%x\n", hsmret); + return CHIP_ERROR_HSM; + } + + if (memcmp(pubkey, nocPublicKey.ConstBytes(), sizeof(pubkey))) + { + ChipLogDetail(Crypto, "the public key being activated doesn't match pending key.\n"); + return CHIP_ERROR_INVALID_PUBLIC_KEY; + } + + mIsPendingKeypairActive = true; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR PersistentStorageOperationalKeystoreEle::CommitOpKeypairForFabric(FabricIndex fabricIndex) +{ + hsm_err_t hsmret = HSM_NO_ERROR; + VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && (fabricIndex == mPendingFabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); + VerifyOrReturnError(mIsPendingKeypairActive == true, CHIP_ERROR_INCORRECT_STATE); + + // Try to store persistent key. On failure, leave everything pending as-is + op_manage_key_group_args_t keygroup_args; + memset(&keygroup_args, 0, sizeof(keygroup_args)); + keygroup_args.key_group = KEY_GROUP; + keygroup_args.flags = KEY_GROUP_SYNC; + hsmret = hsm_manage_key_group(key_mgmt_hdl, &keygroup_args); + if (hsmret != HSM_NO_ERROR) + { + ChipLogDetail(Crypto, "commit key failed. ret: 0x%x\n", hsmret); + ReturnErrorOnFailure(CHIP_ERROR_INTERNAL); + } + else + { + ChipLogDetail(Crypto, "commit key successfully.\n"); + } + + // If we got here, we succeeded and can reset the pending key: next `SignWithOpKeypair` will use the stored key. + ResetPendingKey(false); + return CHIP_NO_ERROR; +} + +CHIP_ERROR PersistentStorageOperationalKeystoreEle::RemoveOpKeypairForFabric(FabricIndex fabricIndex) +{ + hsm_err_t hsmret = HSM_NO_ERROR; + op_get_key_attr_args_t keyattr_args; + + VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); + + // Remove pending state if matching + if ((fabricIndex == mPendingFabricIndex)) + { + ResetPendingKey(true); + return CHIP_NO_ERROR; + } + + // get the key attributes, it shall fail if no such key. + memset(&keyattr_args, 0, sizeof(keyattr_args)); + keyattr_args.key_identifier = fabricIndex; + hsmret = hsm_get_key_attr(key_mgmt_hdl, &keyattr_args); + if (hsmret != HSM_NO_ERROR) + { + ChipLogDetail(Crypto, "No keypair for fabric: %d found. No need to delete\n", fabricIndex); + return CHIP_NO_ERROR; + } + else + { + hsmret = EleDeleteKey(fabricIndex); + if (hsmret != HSM_NO_ERROR) + return CHIP_ERROR_HSM; + } + + return CHIP_NO_ERROR; +} + +void PersistentStorageOperationalKeystoreEle::RevertPendingKeypair() +{ + // Just reset the pending key, it hasn't been stored into secure storage. + ResetPendingKey(true); +} + +CHIP_ERROR PersistentStorageOperationalKeystoreEle::SignWithOpKeypair(FabricIndex fabricIndex, const ByteSpan & message, + Crypto::P256ECDSASignature & outSignature) const +{ + uint8_t sig[kP256_ECDSA_Signature_Length_Raw]; + hsm_err_t hsmret = HSM_NO_ERROR; + + VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); + + hsmret = EleSignMessage(fabricIndex, message.data(), message.size(), sig, kP256_ECDSA_Signature_Length_Raw); + VerifyOrReturnError(hsmret == HSM_NO_ERROR, CHIP_ERROR_INTERNAL); + VerifyOrReturnError(outSignature.SetLength(kP256_ECDSA_Signature_Length_Raw) == CHIP_NO_ERROR, CHIP_ERROR_INTERNAL); + memcpy(outSignature.Bytes(), sig, kP256_ECDSA_Signature_Length_Raw); + + return CHIP_NO_ERROR; +} + +Crypto::P256Keypair * PersistentStorageOperationalKeystoreEle::AllocateEphemeralKeypairForCASE() +{ + return Platform::New(); +} + +void PersistentStorageOperationalKeystoreEle::ReleaseEphemeralKeypair(Crypto::P256Keypair * keypair) +{ + Platform::Delete(keypair); +} + +} // namespace ele +} // namespace chip diff --git a/examples/platform/linux/PersistentStorageOperationalKeystoreEle.h b/examples/platform/linux/PersistentStorageOperationalKeystoreEle.h new file mode 100644 index 00000000000000..1a74ab2760415c --- /dev/null +++ b/examples/platform/linux/PersistentStorageOperationalKeystoreEle.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2022 Project CHIP Authors + * All rights reserved. + * + * 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. + * + * Copyright 2023 NXP + */ + +#pragma once + +#include +#include +#include +#include + +#include "hsm_api.h" + +namespace chip { +namespace ele { + +/** + * @brief OperationalKeystore implementation making use of ELE secure storage + * to load/store keypairs. + * + */ +class PersistentStorageOperationalKeystoreEle : public Crypto::OperationalKeystore +{ +public: + PersistentStorageOperationalKeystoreEle(); + ~PersistentStorageOperationalKeystoreEle(); + + // Non-copyable + PersistentStorageOperationalKeystoreEle(PersistentStorageOperationalKeystoreEle const &) = delete; + void operator=(PersistentStorageOperationalKeystoreEle const &) = delete; + + bool HasPendingOpKeypair() const override { return (mPendingFabricIndex != kUndefinedFabricIndex); } + + bool HasOpKeypairForFabric(FabricIndex fabricIndex) const override; + CHIP_ERROR NewOpKeypairForFabric(FabricIndex fabricIndex, MutableByteSpan & outCertificateSigningRequest) override; + CHIP_ERROR ActivateOpKeypairForFabric(FabricIndex fabricIndex, const Crypto::P256PublicKey & nocPublicKey) override; + CHIP_ERROR CommitOpKeypairForFabric(FabricIndex fabricIndex) override; + CHIP_ERROR RemoveOpKeypairForFabric(FabricIndex fabricIndex) override; + void RevertPendingKeypair() override; + CHIP_ERROR SignWithOpKeypair(FabricIndex fabricIndex, const ByteSpan & message, + Crypto::P256ECDSASignature & outSignature) const override; + + Crypto::P256Keypair * AllocateEphemeralKeypairForCASE() override; + void ReleaseEphemeralKeypair(Crypto::P256Keypair * keypair) override; + +protected: + FabricIndex mPendingFabricIndex = kUndefinedFabricIndex; + bool mIsPendingKeypairActive = false; + + // If overridding NewOpKeypairForFabric method in a subclass, set this to true in + // `NewOpKeypairForFabric` if the mPendingKeypair should not be deleted when no longer in use. + bool mIsExternallyOwnedKeypair = false; + +private: + void ResetPendingKey(bool delete_key); + hsm_err_t EleDeleteKey(uint32_t keyId); + CHIP_ERROR EleGenerateCSR(uint32_t keyId, uint8_t * csr, size_t & csrLength); + hsm_err_t EleSignMessage(uint32_t keyId, const uint8_t * msg, size_t msgSize, uint8_t * sig, size_t sigSize) const; + + hsm_hdl_t hsm_session_hdl = 0; + hsm_hdl_t key_store_hdl = 0; + hsm_hdl_t key_mgmt_hdl = 0; +}; + +} // namespace ele +} // namespace chip diff --git a/examples/platform/linux/PersistentStorageOperationalKeystoreTrusty.cpp b/examples/platform/linux/PersistentStorageOperationalKeystoreTrusty.cpp new file mode 100644 index 00000000000000..00f937c17d5349 --- /dev/null +++ b/examples/platform/linux/PersistentStorageOperationalKeystoreTrusty.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2022 Project CHIP Authors + * All rights reserved. + * + * 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. + * + * Copyright 2023 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "PersistentStorageOperationalKeystoreTrusty.h" + +using namespace matter; + +namespace chip { +namespace Trusty { + +using namespace chip::Crypto; + +bool PersistentStorageOperationalKeystoreTrusty::HasOpKeypairForFabric(FabricIndex fabricIndex) const +{ + VerifyOrReturnError(IsValidFabricIndex(fabricIndex), false); + + // If there was a pending keypair, then there's really a usable key + if (mIsPendingKeypairActive && (fabricIndex == mPendingFabricIndex) && (mPendingKeypair != nullptr)) + { + return true; + } + + return trusty_matter.HasOpKeypairForFabric(fabricIndex); +} + +CHIP_ERROR PersistentStorageOperationalKeystoreTrusty::NewOpKeypairForFabric(FabricIndex fabricIndex, + MutableByteSpan & outCertificateSigningRequest) +{ + VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); + // If a key is pending, we cannot generate for a different fabric index until we commit or revert. + if ((mPendingFabricIndex != kUndefinedFabricIndex) && (fabricIndex != mPendingFabricIndex)) + { + return CHIP_ERROR_INVALID_FABRIC_INDEX; + } + VerifyOrReturnError(outCertificateSigningRequest.size() >= Crypto::kMIN_CSR_Buffer_Size, CHIP_ERROR_BUFFER_TOO_SMALL); + + // Replace previous pending keypair, if any was previously allocated + ResetPendingKey(); + + mPendingKeypair = Platform::New(); + VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_NO_MEMORY); + + mPendingKeypair->fabricIndex = fabricIndex; + mPendingKeypair->Initialize(Crypto::ECPKeyTarget::ECDSA); + size_t csrLength = outCertificateSigningRequest.size(); + CHIP_ERROR err = mPendingKeypair->NewCertificateSigningRequest(outCertificateSigningRequest.data(), csrLength); + if (err != CHIP_NO_ERROR) + { + ResetPendingKey(); + return err; + } + + outCertificateSigningRequest.reduce_size(csrLength); + mPendingFabricIndex = fabricIndex; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR PersistentStorageOperationalKeystoreTrusty::ActivateOpKeypairForFabric(FabricIndex fabricIndex, + const Crypto::P256PublicKey & nocPublicKey) +{ + VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX); + VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && (fabricIndex == mPendingFabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); + + // Validate public key being activated matches last generated pending keypair + VerifyOrReturnError(mPendingKeypair->Pubkey().Matches(nocPublicKey), CHIP_ERROR_INVALID_PUBLIC_KEY); + + mIsPendingKeypairActive = true; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR PersistentStorageOperationalKeystoreTrusty::CommitOpKeypairForFabric(FabricIndex fabricIndex) +{ + int rc = 0; + VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX); + VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && (fabricIndex == mPendingFabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); + VerifyOrReturnError(mIsPendingKeypairActive == true, CHIP_ERROR_INCORRECT_STATE); + + // Try to store persistent key. On failure, leave everything pending as-is + rc = trusty_matter.CommitOpKeypairForFabric(mPendingKeypair->p256_handler, fabricIndex); + if (rc != MATTER_ERROR_OK) + ReturnErrorOnFailure(CHIP_ERROR_INTERNAL); + + // If we got here, we succeeded and can reset the pending key: next `SignWithOpKeypair` will use the stored key. + ResetPendingKey(); + return CHIP_NO_ERROR; +} + +CHIP_ERROR PersistentStorageOperationalKeystoreTrusty::RemoveOpKeypairForFabric(FabricIndex fabricIndex) +{ + int rc = 0; + VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); + + // Remove pending state if matching + if ((mPendingKeypair != nullptr) && (fabricIndex == mPendingFabricIndex)) + { + RevertPendingKeypair(); + } + + rc = trusty_matter.RemoveOpKeypairForFabric(fabricIndex); + if (rc != MATTER_ERROR_OK) + return CHIP_ERROR_INVALID_FABRIC_INDEX; + + return CHIP_NO_ERROR; +} + +void PersistentStorageOperationalKeystoreTrusty::RevertPendingKeypair() +{ + // Just reset the pending key, it hasn't been stored into secure storage. + ResetPendingKey(); +} + +CHIP_ERROR PersistentStorageOperationalKeystoreTrusty::SignWithOpKeypair(FabricIndex fabricIndex, const ByteSpan & message, + Crypto::P256ECDSASignature & outSignature) const +{ + int rc = 0; + size_t sig_size = 0; + uint8_t sig[kP256_ECDSA_Signature_Length_Raw]; + + VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); + + if (mIsPendingKeypairActive && (fabricIndex == mPendingFabricIndex)) + { + VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_INTERNAL); + // We have an override key: sign with it! + return mPendingKeypair->ECDSA_sign_msg(message.data(), message.size(), outSignature); + } + + rc = trusty_matter.SignWithStoredOpKey(fabricIndex, message.data(), message.size(), sig, sig_size); + if (rc != MATTER_ERROR_OK) + return CHIP_ERROR_INTERNAL; + + VerifyOrReturnError(sig_size == kP256_ECDSA_Signature_Length_Raw, CHIP_ERROR_INTERNAL); + VerifyOrReturnError(outSignature.SetLength(sig_size) == CHIP_NO_ERROR, CHIP_ERROR_INTERNAL); + memcpy(outSignature.Bytes(), sig, sig_size); + return CHIP_NO_ERROR; +} + +Crypto::P256Keypair * PersistentStorageOperationalKeystoreTrusty::AllocateEphemeralKeypairForCASE() +{ + return Platform::New(); +} + +void PersistentStorageOperationalKeystoreTrusty::ReleaseEphemeralKeypair(Crypto::P256Keypair * keypair) +{ + Platform::Delete(keypair); +} + +} // namespace Trusty +} // namespace chip diff --git a/examples/platform/linux/PersistentStorageOperationalKeystoreTrusty.h b/examples/platform/linux/PersistentStorageOperationalKeystoreTrusty.h new file mode 100644 index 00000000000000..04ff0f8ba709f0 --- /dev/null +++ b/examples/platform/linux/PersistentStorageOperationalKeystoreTrusty.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2022 Project CHIP Authors + * All rights reserved. + * + * 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. + * + * Copyright 2023 NXP + */ + +#pragma once + +#include +#include +#include +#include + +#include + +using namespace matter; + +namespace chip { +namespace Trusty { + +/** + * @brief OperationalKeystore implementation making use of Trusty secure storage + * to load/store keypairs. + * + */ +class PersistentStorageOperationalKeystoreTrusty : public Crypto::OperationalKeystore +{ +public: + PersistentStorageOperationalKeystoreTrusty() = default; + virtual ~PersistentStorageOperationalKeystoreTrusty() { Finish(); } + + // Non-copyable + PersistentStorageOperationalKeystoreTrusty(PersistentStorageOperationalKeystoreTrusty const &) = delete; + void operator=(PersistentStorageOperationalKeystoreTrusty const &) = delete; + + /** + * @brief Finalize the keystore, so that subsequent operations fail + */ + void Finish() { ResetPendingKey(); } + + bool HasPendingOpKeypair() const override { return (mPendingKeypair != nullptr); } + + bool HasOpKeypairForFabric(FabricIndex fabricIndex) const override; + CHIP_ERROR NewOpKeypairForFabric(FabricIndex fabricIndex, MutableByteSpan & outCertificateSigningRequest) override; + CHIP_ERROR ActivateOpKeypairForFabric(FabricIndex fabricIndex, const Crypto::P256PublicKey & nocPublicKey) override; + CHIP_ERROR CommitOpKeypairForFabric(FabricIndex fabricIndex) override; + CHIP_ERROR RemoveOpKeypairForFabric(FabricIndex fabricIndex) override; + void RevertPendingKeypair() override; + CHIP_ERROR SignWithOpKeypair(FabricIndex fabricIndex, const ByteSpan & message, + Crypto::P256ECDSASignature & outSignature) const override; + Crypto::P256Keypair * AllocateEphemeralKeypairForCASE() override; + void ReleaseEphemeralKeypair(Crypto::P256Keypair * keypair) override; + +protected: + void ResetPendingKey() + { + if (!mIsExternallyOwnedKeypair && (mPendingKeypair != nullptr)) + { + Platform::Delete(mPendingKeypair); + } + mPendingKeypair = nullptr; + mIsExternallyOwnedKeypair = false; + mIsPendingKeypairActive = false; + mPendingFabricIndex = kUndefinedFabricIndex; + } + + FabricIndex mPendingFabricIndex = kUndefinedFabricIndex; + Crypto::P256Keypair * mPendingKeypair = nullptr; + bool mIsPendingKeypairActive = false; + + // If overridding NewOpKeypairForFabric method in a subclass, set this to true in + // `NewOpKeypairForFabric` if the mPendingKeypair should not be deleted when no longer in use. + bool mIsExternallyOwnedKeypair = false; + +private: + mutable TrustyMatter trusty_matter; +}; + +} // namespace Trusty +} // namespace chip diff --git a/scripts/examples/imxlinux_example.sh b/scripts/examples/imxlinux_example.sh old mode 100755 new mode 100644 index 36ad7923d8c706..19b09395a75056 --- a/scripts/examples/imxlinux_example.sh +++ b/scripts/examples/imxlinux_example.sh @@ -1,7 +1,7 @@ -#!/usr/bin/env bash - +#!/bin/bash # # Copyright (c) 2022 Project CHIP Authors +# Copyright 2023, 2025 NXP # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,11 +18,73 @@ set -e set -x -if [ "$#" != 2 && "$#" != 3 ]; then - exit -1 +helpFunction() { + cat < -o|--out [-d|--debug] [-n|--no-init] [-t|--trusty] [-m|--imx_ele] + -s, --src Source folder + -o, --out Output folder + -d, --debug Debug build (optional) + -n, --no-init No init mode (optional) + -t, --trusty Build with Trusty OS backed security enhancement (optional) + -m, --imx_ele Build with IMX ELE (EdgeLock Enclave) based security enhancement (optional) +EOF + exit 1 +} + +trusty=0 +imx_ele=0 +imx_ele_path="third_party/imx-secure-enclave/repo/" +release_build=true +PARSED_OPTIONS="$(getopt -o s:o:tdmn --long src:,out:,trusty,imx_ele,debug,no-init -- "$@")" +if [ $? -ne 0 ]; then + helpFunction +fi +eval set -- "$PARSED_OPTIONS" +while true; do + case "$1" in + -s | --src) + src="$2" + shift 2 + ;; + -o | --out) + out="$2" + shift 2 + ;; + -t | --trusty) + trusty=1 + shift + ;; + -m | --imx_ele) + imx_ele=1 + shift + ;; + -d | --debug) + release_build=false + shift + ;; + -n | --no-init) + no_init=1 + shift + ;; + --) + shift + break + ;; + *) + echo "Invalid option: $1" >&2 + exit 1 + ;; + esac +done + +if [ -z "$src" ] || [ -z "$out" ]; then + echo "Some or all of the required -s|--src and -o|--out parameters are empty." + helpFunction fi -source "$(dirname "$0")/../../scripts/activate.sh" +if [ "$no_init" != 1 ]; then + source "$(dirname "$0")/../../scripts/activate.sh" +fi if [ "$IMX_SDK_ROOT" = "" -o ! -d "$IMX_SDK_ROOT" ]; then echo "the Yocto SDK path is not specified with the shell env IMX_SDK_ROOT or an invalid path is specified" @@ -100,13 +162,19 @@ if [ -z "$target_cpu" -o -z "$cross_compile" ]; then exit 1 fi -release_build=true -if [ "$3" = "debug" ]; then - release_build=false +if [ "$imx_ele" -ne 0 ]; then + if [ -f "$imx_ele_path/Makefile" ]; then + make -C "third_party/imx-secure-enclave/repo" "install_version" + else + echo "Makefile does not exist in $imx_ele_path." + exit 1 + fi fi PLATFORM_CFLAGS='-DCHIP_DEVICE_CONFIG_WIFI_STATION_IF_NAME=\"mlan0\"", "-DCHIP_DEVICE_CONFIG_LINUX_DHCPC_CMD=\"udhcpc -b -i %s \"' -gn gen --check --fail-on-unused-args --root="$1" "$2" --args="target_os=\"linux\" target_cpu=\"$target_cpu\" arm_arch=\"$arm_arch\" +gn gen --check --fail-on-unused-args --root="$src" "$out" --args="target_os=\"linux\" target_cpu=\"$target_cpu\" arm_arch=\"$arm_arch\" +chip_with_trusty_os=$trusty +chip_with_imx_ele=$imx_ele treat_warnings_as_errors=false import(\"//build_overrides/build.gni\") sysroot=\"$sdk_target_sysroot\" @@ -117,4 +185,4 @@ target_cxx=\"$IMX_SDK_ROOT/sysroots/x86_64-pokysdk-linux/usr/bin/$cross_compile/ target_ar=\"$IMX_SDK_ROOT/sysroots/x86_64-pokysdk-linux/usr/bin/$cross_compile/$cross_compile-ar\" $(if [ "$release_build" = "true" ]; then echo "is_debug=false"; else echo "optimize_debug=true"; fi)" -ninja -C "$2" +ninja -C "$out" diff --git a/src/crypto/BUILD.gn b/src/crypto/BUILD.gn index 746126873e5b58..29dc2ae7289ca7 100644 --- a/src/crypto/BUILD.gn +++ b/src/crypto/BUILD.gn @@ -1,4 +1,5 @@ # Copyright (c) 2020 Project CHIP Authors +# Copyright 2025 NXP # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,6 +21,9 @@ import("${chip_root}/build/chip/buildconfig_header.gni") import("crypto.gni") +import("${chip_root}/src/lib/imx_ele.gni") +import("${chip_root}/src/lib/trusty.gni") + if (chip_crypto == "") { if (current_os == "android" || current_os == "freertos" || current_os == "zephyr" || current_os == "mbed" || current_os == "webos" || @@ -74,6 +78,12 @@ buildconfig_header("crypto_buildconfig") { "CHIP_CRYPTO_BORINGSSL=${chip_crypto_boringssl}", "CHIP_CRYPTO_PLATFORM=${chip_crypto_platform}", ] + + if (chip_with_trusty_os == 1) { + defines += [ "CHIP_CRYPTO_TRUSTY_OS=1" ] + } else { + defines += [ "CHIP_CRYPTO_TRUSTY_OS=0" ] + } } source_set("public_headers") { @@ -92,6 +102,10 @@ source_set("public_headers") { "${chip_root}/src/lib/support", "${nlassert_root}:nlassert", ] + + if (chip_with_trusty_os == 1) { + public_deps += [ "${chip_root}/third_party/libtrustymatter" ] + } } if (chip_crypto == "openssl") { diff --git a/src/crypto/CHIPCryptoPAL.h b/src/crypto/CHIPCryptoPAL.h index 8d6c8ba6feb21f..c69bc12594de8d 100644 --- a/src/crypto/CHIPCryptoPAL.h +++ b/src/crypto/CHIPCryptoPAL.h @@ -1,6 +1,7 @@ /* * * Copyright (c) 2020-2023 Project CHIP Authors + * Copyright 2025 NXP * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -588,6 +589,11 @@ class P256Keypair : public P256KeypairBase /** Release resources associated with this key pair */ void Clear(); +#if CHIP_CRYPTO_TRUSTY_OS + uint64_t p256_handler = 0; + uint8_t fabricIndex = 0; +#endif + protected: P256PublicKey mPublicKey; mutable P256KeypairContext mKeypair; diff --git a/src/crypto/CHIPCryptoPALOpenSSL.cpp b/src/crypto/CHIPCryptoPALOpenSSL.cpp index 9f2592a12aee0c..67bec953c7ebe0 100644 --- a/src/crypto/CHIPCryptoPALOpenSSL.cpp +++ b/src/crypto/CHIPCryptoPALOpenSSL.cpp @@ -1,6 +1,7 @@ /* * * Copyright (c) 2020-2023 Project CHIP Authors + * Copyright 2025 NXP * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,9 +56,18 @@ #include +#if CHIP_CRYPTO_TRUSTY_OS +#include +using namespace matter; +#endif + namespace chip { namespace Crypto { +#if CHIP_CRYPTO_TRUSTY_OS +TrustyMatter trusty_matter; +#endif + // BoringSSL is designed to implement the same interface as OpenSSL in most // cases. However, it removes some APIs that can allow very weak configuration. // (Example: CCM ciphers with low tag lengths.) In order to support Matter, @@ -744,6 +754,43 @@ static inline const EC_KEY * to_const_EC_KEY(const P256KeypairContext * context) return *SafePointerCast(context); } +#if CHIP_CRYPTO_TRUSTY_OS +CHIP_ERROR P256Keypair::ECDSA_sign_msg(const uint8_t * msg, const size_t msg_length, P256ECDSASignature & out_signature) const +{ + CHIP_ERROR error = CHIP_NO_ERROR; + size_t sig_size = 0; + int rc = 0; + + VerifyOrReturnError((msg != nullptr) && (msg_length > 0), CHIP_ERROR_INVALID_ARGUMENT); + + uint8_t digest[kSHA256_Hash_Length]; + uint8_t sig[kP256_ECDSA_Signature_Length_Raw]; + memset(&digest[0], 0, sizeof(digest)); + memset(&sig[0], 0, sizeof(sig)); + + ReturnErrorOnFailure(Hash_SHA256(msg, msg_length, &digest[0])); + + ERR_clear_error(); + + static_assert(P256ECDSASignature::Capacity() >= kP256_ECDSA_Signature_Length_Raw, "P256ECDSASignature must be large enough"); + VerifyOrExit(mInitialized, error = CHIP_ERROR_UNINITIALIZED); + + rc = trusty_matter.P256KeypairECSignMsg(p256_handler, digest, kSHA256_Hash_Length, sig, sig_size); + VerifyOrExit(rc == MATTER_ERROR_OK, error = CHIP_ERROR_INTERNAL); + VerifyOrExit(sig_size == kP256_ECDSA_Signature_Length_Raw, error = CHIP_ERROR_INTERNAL); + + VerifyOrExit(out_signature.SetLength(kP256_ECDSA_Signature_Length_Raw) == CHIP_NO_ERROR, error = CHIP_ERROR_INTERNAL); + memcpy(out_signature.Bytes() + 0u, sig, sig_size); + +exit: + if (error != CHIP_NO_ERROR) + { + _logSSLError(); + } + + return error; +} +#else CHIP_ERROR P256Keypair::ECDSA_sign_msg(const uint8_t * msg, const size_t msg_length, P256ECDSASignature & out_signature) const { CHIP_ERROR error = CHIP_NO_ERROR; @@ -799,6 +846,7 @@ CHIP_ERROR P256Keypair::ECDSA_sign_msg(const uint8_t * msg, const size_t msg_len return error; } +#endif CHIP_ERROR P256PublicKey::ECDSA_validate_msg_signature(const uint8_t * msg, const size_t msg_length, const P256ECDSASignature & signature) const @@ -968,6 +1016,25 @@ static CHIP_ERROR _create_evp_key_from_binary_p256_key(const P256PublicKey & key return error; } +#if CHIP_CRYPTO_TRUSTY_OS +CHIP_ERROR P256Keypair::ECDH_derive_secret(const P256PublicKey & remote_public_key, P256ECDHDerivedSecret & out_secret) const +{ + ERR_clear_error(); + CHIP_ERROR error = CHIP_NO_ERROR; + int result = -1; + size_t out_buf_length = 0; + + result = trusty_matter.P256KeypairECDH_derive_secret(p256_handler, Uint8::to_const_uchar(remote_public_key), + remote_public_key.Length(), out_secret.Bytes(), out_buf_length); + VerifyOrExit(result == MATTER_ERROR_OK, error = CHIP_ERROR_INTERNAL); + VerifyOrExit((out_buf_length > 0), error = CHIP_ERROR_INTERNAL); + SuccessOrExit(out_secret.SetLength(out_buf_length)); + +exit: + _logSSLError(); + return error; +} +#else CHIP_ERROR P256Keypair::ECDH_derive_secret(const P256PublicKey & remote_public_key, P256ECDHDerivedSecret & out_secret) const { ERR_clear_error(); @@ -1035,6 +1102,7 @@ CHIP_ERROR P256Keypair::ECDH_derive_secret(const P256PublicKey & remote_public_k _logSSLError(); return error; } +#endif void ClearSecretData(uint8_t * buf, size_t len) { @@ -1082,6 +1150,101 @@ static CHIP_ERROR P256PublicKeyFromECKey(EC_KEY * ec_key, P256PublicKey & pubkey return error; } +#if CHIP_CRYPTO_TRUSTY_OS +CHIP_ERROR P256Keypair::Initialize(ECPKeyTarget key_target) +{ + ERR_clear_error(); + CHIP_ERROR error = CHIP_NO_ERROR; + uint8_t public_key[kP256_PublicKey_Length]; + int rc = 0; + + rc = trusty_matter.P256KeypairInitialize(p256_handler, fabricIndex, public_key); + VerifyOrExit(rc == MATTER_ERROR_OK, error = CHIP_ERROR_INTERNAL); + + memcpy(Uint8::to_uchar(mPublicKey), public_key, kP256_PublicKey_Length); + + mInitialized = true; + +exit: + _logSSLError(); + return error; +} + +CHIP_ERROR P256Keypair::Serialize(P256SerializedKeypair & output) const +{ + CHIP_ERROR error = CHIP_NO_ERROR; + uint8_t privkey[kP256_PrivateKey_Length]; + int rc = 0; + + rc = trusty_matter.P256KeypairSerialize(p256_handler, privkey); + VerifyOrExit(rc == MATTER_ERROR_OK, error = CHIP_ERROR_INTERNAL); + + { + size_t len = output.Length() == 0 ? output.Capacity() : output.Length(); + Encoding::BufferWriter bbuf(output.Bytes(), len); + bbuf.Put(mPublicKey, mPublicKey.Length()); + bbuf.Put(privkey, sizeof(privkey)); + VerifyOrExit(bbuf.Fit(), error = CHIP_ERROR_NO_MEMORY); + output.SetLength(bbuf.Needed()); + } + +exit: + ClearSecretData(privkey, sizeof(privkey)); + _logSSLError(); + return error; +} + +CHIP_ERROR P256Keypair::Deserialize(P256SerializedKeypair & input) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + Encoding::BufferWriter bbuf(mPublicKey, mPublicKey.Length()); + int rc = 0; + + uint8_t * pubkey = input.Bytes(); + uint8_t * privkey = input.Bytes() + mPublicKey.Length(); + + VerifyOrExit(input.Length() == mPublicKey.Length() + kP256_PrivateKey_Length, error = CHIP_ERROR_INVALID_ARGUMENT); + bbuf.Put(input.ConstBytes(), mPublicKey.Length()); + VerifyOrExit(bbuf.Fit(), error = CHIP_ERROR_NO_MEMORY); + + rc = trusty_matter.P256KeypairDeserialize(p256_handler, pubkey, mPublicKey.Length(), privkey, kP256_PrivateKey_Length); + VerifyOrExit(rc == MATTER_ERROR_OK, error = CHIP_ERROR_INTERNAL); + + mInitialized = true; + +exit: + _logSSLError(); + return error; +} + +void P256Keypair::Clear() +{ + mInitialized = false; + p256_handler = 0; +} + +P256Keypair::~P256Keypair() +{ + trusty_matter.P256KeypairDestory(p256_handler); + Clear(); +} + +CHIP_ERROR P256Keypair::NewCertificateSigningRequest(uint8_t * out_csr, size_t & csr_length) const +{ + ERR_clear_error(); + CHIP_ERROR error = CHIP_NO_ERROR; + int rc = 0; + + VerifyOrExit(mInitialized, error = CHIP_ERROR_UNINITIALIZED); + + rc = trusty_matter.P256KeypairNewCSR(p256_handler, out_csr, csr_length); + VerifyOrExit(rc == MATTER_ERROR_OK, error = CHIP_ERROR_INTERNAL); + +exit: + _logSSLError(); + return error; +} +#else CHIP_ERROR P256Keypair::Initialize(ECPKeyTarget key_target) { ERR_clear_error(); @@ -1314,6 +1477,7 @@ CHIP_ERROR P256Keypair::NewCertificateSigningRequest(uint8_t * out_csr, size_t & _logSSLError(); return error; } +#endif CHIP_ERROR VerifyCertificateSigningRequest(const uint8_t * csr, size_t csr_length, P256PublicKey & pubkey) { diff --git a/src/lib/imx_ele.gni b/src/lib/imx_ele.gni new file mode 100644 index 00000000000000..35963045eaae6e --- /dev/null +++ b/src/lib/imx_ele.gni @@ -0,0 +1,17 @@ +# Copyright (c) 2023 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. + +declare_args() { + chip_with_imx_ele = 0 +} diff --git a/src/lib/trusty.gni b/src/lib/trusty.gni new file mode 100644 index 00000000000000..f13c9bffe351fd --- /dev/null +++ b/src/lib/trusty.gni @@ -0,0 +1,17 @@ +# Copyright (c) 2023 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. + +declare_args() { + chip_with_trusty_os = 0 +} diff --git a/third_party/libtrustymatter/BUILD.gn b/third_party/libtrustymatter/BUILD.gn new file mode 100644 index 00000000000000..f172f1251a8581 --- /dev/null +++ b/third_party/libtrustymatter/BUILD.gn @@ -0,0 +1,48 @@ +# Copyright (c) 2023 Project CHIP Authors +# Copyright 2025 NXP +# +# 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. + +import("//build_overrides/chip.gni") + +config("libtrustymatter_config") { + include_dirs = [ + "repo/matter_client/include", + "repo/libtrusty/include", + ] +} + +source_set("libtrustymatter") { + sources = [ + "repo/libtrusty/include/trusty/ipc.h", + "repo/libtrusty/include/trusty/tipc.h", + "repo/libtrusty/trusty.c", + "repo/matter_client/include/UniquePtr.h", + "repo/matter_client/include/matter_defs.h", + "repo/matter_client/include/matter_ipc.h", + "repo/matter_client/include/matter_messages.h", + "repo/matter_client/include/mem.h", + "repo/matter_client/include/serializable.h", + "repo/matter_client/include/trusty_matter.h", + "repo/matter_client/include/trusty_matter_ipc.h", + "repo/matter_client/matter_messages.cpp", + "repo/matter_client/serializable.cpp", + "repo/matter_client/trusty_matter.cpp", + "repo/matter_client/trusty_matter_ipc.cpp", + ] + + cflags = [ "-Wno-implicit-fallthrough" ] + cflags_cc = [ "-std=c++17" ] + + public_configs = [ ":libtrustymatter_config" ] +} diff --git a/third_party/libtrustymatter/repo b/third_party/libtrustymatter/repo new file mode 160000 index 00000000000000..2be9d3c10c4ba9 --- /dev/null +++ b/third_party/libtrustymatter/repo @@ -0,0 +1 @@ +Subproject commit 2be9d3c10c4ba947d09e300ca03d0cb69df65143