diff --git a/.github/.wordlist.txt b/.github/.wordlist.txt index e01126cf92f958..5ac6294f7a8320 100644 --- a/.github/.wordlist.txt +++ b/.github/.wordlist.txt @@ -252,6 +252,7 @@ ColorControl Comcast Commandline Commissionable +CommissionableDataProvider commissionables commissionee CommissioningFlow @@ -352,6 +353,7 @@ demangle deployable depottools deps +der desc descheduled detokenization @@ -360,6 +362,7 @@ dev devcontainer devCtrl DevelopmentCerts +DeviceAttestationCredentialsProvider DeviceAttestationCredsExample DeviceCaCerts DeviceCert @@ -845,6 +848,7 @@ mydir MyPASSWORD MySSID NAMESERVER +NAMESPACE namespacing nano natively @@ -882,6 +886,7 @@ nullable nullptr NUM NVM +NVS nwk NXP objcopy diff --git a/config/esp32/components/chip/CMakeLists.txt b/config/esp32/components/chip/CMakeLists.txt index db2603b8b47ba3..6aa6e5ce47f7ae 100644 --- a/config/esp32/components/chip/CMakeLists.txt +++ b/config/esp32/components/chip/CMakeLists.txt @@ -129,6 +129,10 @@ if (CONFIG_CHIP_ENABLE_EXTERNAL_PLATFORM) chip_gn_arg_append("chip_platform_target" "\"${CONFIG_CHIP_EXTERNAL_PLATFORM_TARGET}\"") endif() +if (CONFIG_ENABLE_ESP32_FACTORY_DATA_PROVIDER) + chip_gn_arg_append("chip_use_transitional_commissionable_data_provider" "false") +endif() + set(args_gn_input "${CMAKE_CURRENT_BINARY_DIR}/args.gn.in") file(GENERATE OUTPUT "${args_gn_input}" CONTENT "${chip_gn_args}") diff --git a/config/esp32/components/chip/Kconfig b/config/esp32/components/chip/Kconfig index 3229d5d6eaa485..fecedd83dbc9ba 100644 --- a/config/esp32/components/chip/Kconfig +++ b/config/esp32/components/chip/Kconfig @@ -620,6 +620,16 @@ menu "CHIP Device Layer" bool "Enable Rotating Device Identifier Support" default n + config ENABLE_ESP32_FACTORY_DATA_PROVIDER + bool "Use ESP32 Factory Data Provider" + default n + help + If this option is enabled, then ESP32 implementation of CommissionableDataProvider and + DeviceAttestationCredentialsProvider is used. Otherwise, the test-mode CommissionableDataProvider + and Example DeviceAttestationCredentialsProvider is used. ESP32 implementation reads factory data from + nvs partition. Factory partition can be configured using CONFIG_CHIP_FACTORY_NAMESPACE_PARTITION_LABEL + option, default is "nvs". + endmenu diff --git a/examples/lighting-app/esp32/README.md b/examples/lighting-app/esp32/README.md index b534d03f259937..b11e40a11f1a86 100644 --- a/examples/lighting-app/esp32/README.md +++ b/examples/lighting-app/esp32/README.md @@ -111,6 +111,50 @@ make sure the IDF_PATH has been exported(See the manual setup steps above). $ idf.py -p /dev/tty.SLAB_USBtoUART monitor +## Using ESP32 Factory Data Provider + +This application uses test-mode CommissionableDataProvider and Example +DeviceAttestationCredentialsProvider. + +Enabled config option `CONFIG_ENABLE_ESP32_FACTORY_DATA_PROVIDER` to use ESP32 +specific implementation of CommissionableDataProvider and +DeviceAttestationCredentialsProvider. + +ESP32 implementation reads factory data from nvs partition, chip-factory data +must be flashed into the configure nvs partition. Factory partition can be +configured using CONFIG_CHIP_FACTORY_NAMESPACE_PARTITION_LABEL option, default +is "nvs". + +`scripts/tools/generate_esp32_chip_factory_bin.py` script generates the +chip-factory NVS image `partition.bin`. + +Below mentioned command generates the nvs image with test DAC with VID:0xFFF2 +and PID:8001 + +``` +cd third_party/connectedhomeip/scripts/tools + +./generate_esp32_chip_factory_bin.py -d 3434 -p 99663300 \ + --dac-cert ../../credentials/test/attestation/Chip-Test-DAC-FFF2-8001-0008-Cert.der \ + --dac-key ../../credentials/test/attestation/Chip-Test-DAC-FFF2-8001-0008-Key.der \ + --pai-cert ../../credentials/test/attestation/Chip-Test-PAI-FFF2-8001-Cert.der \ + --cd ../../credentials/test/certification-declaration/Chip-Test-CD-FFF2-8001.der +cd - +``` + +This project uses VID:0xFFF1 and PID:0x8000, if you are planning to use the +above command as is please change the VID/PID using menuconfig options. + +Use the following command to flash the NVS image. `0x9000` is default address +for `nvs` partition. + +``` +esptool.py -p write_flash 0x9000 third_party/connectedhomeip/scripts/tools/partition.bin +``` + +NOTE: Please commission the device using above specified discriminator and +passcode + ## Commissioning over BLE using chip-tool - Please build the standalone chip-tool as described [here](../../chip-tool) diff --git a/examples/lighting-app/esp32/main/main.cpp b/examples/lighting-app/esp32/main/main.cpp index f8894fc5d08d6b..7004f6c576d7a6 100644 --- a/examples/lighting-app/esp32/main/main.cpp +++ b/examples/lighting-app/esp32/main/main.cpp @@ -39,6 +39,10 @@ #include #include +#if CONFIG_ENABLE_ESP32_FACTORY_DATA_PROVIDER +#include +#endif // CONFIG_ENABLE_ESP32_FACTORY_DATA_PROVIDER + using namespace ::chip; using namespace ::chip::Credentials; using namespace ::chip::DeviceManager; @@ -62,6 +66,10 @@ namespace { app::Clusters::NetworkCommissioning::Instance sWiFiNetworkCommissioningInstance(0 /* Endpoint Id */, &(NetworkCommissioning::ESPWiFiDriver::GetInstance())); #endif + +#if CONFIG_ENABLE_ESP32_FACTORY_DATA_PROVIDER +ESP32FactoryDataProvider sFactoryDataProvider; +#endif // CONFIG_ENABLE_ESP32_FACTORY_DATA_PROVIDER } // namespace static void InitOTARequestor(void) @@ -86,7 +94,12 @@ static void InitServer(intptr_t context) chip::Server::GetInstance().Init(initParams); // Initialize device attestation config +#if CONFIG_ENABLE_ESP32_FACTORY_DATA_PROVIDER + SetDeviceAttestationCredentialsProvider(&sFactoryDataProvider); +#else SetDeviceAttestationCredentialsProvider(Examples::GetExampleDACProvider()); +#endif // CONFIG_ENABLE_ESP32_FACTORY_DATA_PROVIDER + #if CHIP_DEVICE_CONFIG_ENABLE_WIFI sWiFiNetworkCommissioningInstance.Init(); #endif @@ -98,6 +111,7 @@ static void InitServer(intptr_t context) chip::app::DnssdServer::Instance().StartServer(); } #endif + InitOTARequestor(); } extern "C" void app_main() @@ -114,6 +128,10 @@ extern "C" void app_main() ESP_LOGI(TAG, "chip-esp32-light-example starting"); ESP_LOGI(TAG, "=================================================="); +#if CONFIG_ENABLE_ESP32_FACTORY_DATA_PROVIDER + SetCommissionableDataProvider(&sFactoryDataProvider); +#endif // CONFIG_ENABLE_ESP32_FACTORY_DATA_PROVIDER + #if CONFIG_ENABLE_CHIP_SHELL chip::LaunchShell(); #endif @@ -140,7 +158,5 @@ extern "C" void app_main() #endif AppLED.Init(); - InitOTARequestor(); - chip::DeviceLayer::PlatformMgr().ScheduleWork(InitServer, reinterpret_cast(nullptr)); } diff --git a/scripts/tools/generate_esp32_chip_factory_bin.py b/scripts/tools/generate_esp32_chip_factory_bin.py new file mode 100755 index 00000000000000..51f3f37c38fece --- /dev/null +++ b/scripts/tools/generate_esp32_chip_factory_bin.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 +# +# 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. +# + +import os +import sys +import shutil +import logging +import argparse +import subprocess +import cryptography.x509 +from types import SimpleNamespace + +if os.getenv('IDF_PATH'): + sys.path.insert(0, os.path.join(os.getenv('IDF_PATH'), + 'components', + 'nvs_flash', + 'nvs_partition_generator')) + import nvs_partition_gen +else: + log.error("Please set the IDF_PATH environment variable.") + exit(0) + +INVALID_PASSCODES = [00000000, 11111111, 22222222, 33333333, 44444444, 55555555, + 66666666, 77777777, 88888888, 99999999, 12345678, 87654321] + +TOOLS = {} + + +def check_tools_exists(): + TOOLS['spake2p'] = shutil.which('spake2p') + if TOOLS['spake2p'] is None: + logging.error('spake2p not found, please add spake2p path to PATH environment variable') + sys.exit(1) + + +def validate_args(args): + # Validate the passcode + if args.passcode is not None: + if ((args.passcode < 0x0000001 and args.passcode > 0x5F5E0FE) or (args.passcode in INVALID_PASSCODES)): + logging.error('Invalid passcode:' + str(args.passcode)) + sys.exit(1) + + # Validate the discriminator + if (args.discriminator is not None) and (args.discriminator not in range(0x0000, 0x0FFF)): + logging.error('Invalid discriminator:' + str(args.discriminator)) + sys.exit(1) + + logging.info('Discriminator:{} Passcode:{}'.format(args.discriminator, args.passcode)) + + +def gen_spake2p_params(passcode): + iter_count_max = 10000 + salt_len_max = 32 + + cmd = [ + TOOLS['spake2p'], 'gen-verifier', + '--iteration-count', str(iter_count_max), + '--salt-len', str(salt_len_max), + '--pin-code', str(passcode), + '--out', '-', + ] + + output = subprocess.check_output(cmd) + output = output.decode('utf-8').splitlines() + return dict(zip(output[0].split(','), output[1].split(','))) + + +def gen_raw_ec_keypair_from_der(key_file, pubkey_raw_file, privkey_raw_file): + with open(key_file, 'rb') as f: + key_data = f.read() + + logging.warning('Leaking of DAC private keys may lead to attestation chain revokation') + logging.warning('Please make sure the DAC private is key protected using a password') + + # WARNING: Below line assumes that the DAC private key is not protected by a password, + # please be careful and use the password-protected key if reusing this code + key_der = cryptography.hazmat.primitives.serialization.load_der_private_key(key_data, None) + + private_number_val = key_der.private_numbers().private_value + with open(privkey_raw_file, 'wb') as f: + f.write(private_number_val.to_bytes(32, byteorder='big')) + + public_key_first_byte = 0x04 + public_number_x = key_der.public_key().public_numbers().x + public_number_y = key_der.public_key().public_numbers().y + with open(pubkey_raw_file, 'wb') as f: + f.write(public_key_first_byte.to_bytes(1, byteorder='big')) + f.write(public_number_x.to_bytes(32, byteorder='big')) + f.write(public_number_y.to_bytes(32, byteorder='big')) + + +def generate_nvs_bin(args, spake2p_params): + dac_raw_privkey = 'dac_raw_privkey.bin' + dac_raw_pubkey = 'dac_raw_pubkey.bin' + gen_raw_ec_keypair_from_der(args.dac_key, dac_raw_pubkey, dac_raw_privkey) + + csv_content = 'key,type,encoding,value\n' + csv_content += 'chip-factory,namespace,,\n' + + csv_content += 'discriminator,data,u32,{}\n'.format(args.discriminator) + csv_content += 'iteration-count,data,u32,{}\n'.format(spake2p_params['Iteration Count']) + csv_content += 'salt,data,string,{}\n'.format(spake2p_params['Salt']) + csv_content += 'verifier,data,string,{}\n'.format(spake2p_params['Verifier']) + + csv_content += 'dac-cert,file,binary,{}\n'.format(os.path.abspath(args.dac_cert)) + csv_content += 'dac-key,file,binary,{}\n'.format(os.path.abspath(dac_raw_privkey)) + csv_content += 'dac-pub-key,file,binary,{}\n'.format(os.path.abspath(dac_raw_pubkey)) + csv_content += 'pai-cert,file,binary,{}\n'.format(os.path.abspath(args.pai_cert)) + csv_content += 'cert-dclrn,file,binary,{}\n'.format(os.path.abspath(args.cd)) + + with open('nvs_partition.csv', 'w') as f: + f.write(csv_content) + + nvs_args = SimpleNamespace(input='nvs_partition.csv', + output='partition.bin', + size=hex(args.size), + outdir=os.getcwd(), + version=2) + + nvs_partition_gen.generate(nvs_args) + + os.remove('nvs_partition.csv') + os.remove(dac_raw_privkey) + os.remove(dac_raw_pubkey) + + +def print_flashing_help(): + logging.info('To flash the generated partition.bin, run the following command:') + logging.info('==============================================================') + logging.info('esptool.py -p write_flash partition.bin') + logging.info('==============================================================') + logging.info('default \"nvs\" partition addr is 0x9000') + + +def main(): + def any_base_int(s): return int(s, 0) + + parser = argparse.ArgumentParser(description='Chip Factory NVS binary generator tool') + + parser.add_argument('-p', '--passcode', type=any_base_int, required=True, + help='The discriminator for pairing, range: 0x01-0x5F5E0FE') + parser.add_argument('-d', '--discriminator', type=any_base_int, required=True, + help='The passcode for pairing, range: 0x00-0x0FFF') + parser.add_argument('--dac-cert', type=str, required=True, + help='The path to the DAC certificate in der format') + parser.add_argument('--dac-key', type=str, required=True, + help='The path to the DAC private key in der format') + parser.add_argument('--pai-cert', type=str, required=True, + help='The path to the PAI certificate in der format') + parser.add_argument('--cd', type=str, required=True, + help='The path to the certificate declaration der format') + parser.add_argument('-s', '--size', type=any_base_int, required=False, default=0x6000, + help='The size of the partition.bin, default: 0x6000') + + args = parser.parse_args() + validate_args(args) + check_tools_exists() + spake2p_params = gen_spake2p_params(args.passcode) + generate_nvs_bin(args, spake2p_params) + print_flashing_help() + + +if __name__ == "__main__": + logging.basicConfig(format='[%(asctime)s] [%(levelname)7s] - %(message)s', level=logging.INFO) + main() diff --git a/src/platform/ESP32/BUILD.gn b/src/platform/ESP32/BUILD.gn index fc9323529d8b7e..be663822919304 100644 --- a/src/platform/ESP32/BUILD.gn +++ b/src/platform/ESP32/BUILD.gn @@ -34,6 +34,8 @@ static_library("ESP32") { "DiagnosticDataProviderImpl.h", "ESP32Config.cpp", "ESP32Config.h", + "ESP32FactoryDataProvider.cpp", + "ESP32FactoryDataProvider.h", "ESP32Utils.cpp", "ESP32Utils.h", "KeyValueStoreManagerImpl.cpp", @@ -53,6 +55,8 @@ static_library("ESP32") { "${chip_root}/src/setup_payload", ] + public = [ "${chip_root}/src/credentials/DeviceAttestationCredsProvider.h" ] + public_deps = [ "${chip_root}/src/crypto", "${chip_root}/src/platform:platform_base", diff --git a/src/platform/ESP32/ESP32Config.cpp b/src/platform/ESP32/ESP32Config.cpp index 996d91b111d5c5..047edec41c202c 100644 --- a/src/platform/ESP32/ESP32Config.cpp +++ b/src/platform/ESP32/ESP32Config.cpp @@ -62,6 +62,11 @@ const ESP32Config::Key ESP32Config::kConfigKey_SetupDiscriminator = { kConfig const ESP32Config::Key ESP32Config::kConfigKey_Spake2pIterationCount = { kConfigNamespace_ChipFactory, "iteration-count" }; const ESP32Config::Key ESP32Config::kConfigKey_Spake2pSalt = { kConfigNamespace_ChipFactory, "salt" }; const ESP32Config::Key ESP32Config::kConfigKey_Spake2pVerifier = { kConfigNamespace_ChipFactory, "verifier" }; +const ESP32Config::Key ESP32Config::kConfigKey_DACCert = { kConfigNamespace_ChipFactory, "dac-cert" }; +const ESP32Config::Key ESP32Config::kConfigKey_DACPrivateKey = { kConfigNamespace_ChipFactory, "dac-key" }; +const ESP32Config::Key ESP32Config::kConfigKey_DACPublicKey = { kConfigNamespace_ChipFactory, "dac-pub-key" }; +const ESP32Config::Key ESP32Config::kConfigKey_PAICert = { kConfigNamespace_ChipFactory, "pai-cert" }; +const ESP32Config::Key ESP32Config::kConfigKey_CertDeclaration = { kConfigNamespace_ChipFactory, "cert-dclrn" }; // Keys stored in the chip-config namespace const ESP32Config::Key ESP32Config::kConfigKey_FabricId = { kConfigNamespace_ChipConfig, "fabric-id" }; diff --git a/src/platform/ESP32/ESP32Config.h b/src/platform/ESP32/ESP32Config.h index bfbfe65a0869bb..15156113fd4678 100644 --- a/src/platform/ESP32/ESP32Config.h +++ b/src/platform/ESP32/ESP32Config.h @@ -25,8 +25,6 @@ #pragma once -#include - #include namespace chip { @@ -78,6 +76,11 @@ class ESP32Config static const Key kConfigKey_Spake2pIterationCount; static const Key kConfigKey_Spake2pSalt; static const Key kConfigKey_Spake2pVerifier; + static const Key kConfigKey_DACCert; + static const Key kConfigKey_DACPrivateKey; + static const Key kConfigKey_DACPublicKey; + static const Key kConfigKey_PAICert; + static const Key kConfigKey_CertDeclaration; // CHIP Counter keys static const Key kCounterKey_RebootCount; diff --git a/src/platform/ESP32/ESP32FactoryDataProvider.cpp b/src/platform/ESP32/ESP32FactoryDataProvider.cpp new file mode 100644 index 00000000000000..f73a6f8eaed801 --- /dev/null +++ b/src/platform/ESP32/ESP32FactoryDataProvider.cpp @@ -0,0 +1,158 @@ +/* + * + * Copyright (c) 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. + */ +#include +#include +#include +#include +#include + +namespace chip { +namespace DeviceLayer { + +using namespace chip::Credentials; +using namespace chip::DeviceLayer::Internal; + +namespace { +static constexpr uint32_t kDACPrivateKeySize = 32; +static constexpr uint32_t kDACPublicKeySize = 65; + +CHIP_ERROR LoadKeypairFromRaw(ByteSpan privateKey, ByteSpan publicKey, Crypto::P256Keypair & keypair) +{ + Crypto::P256SerializedKeypair serializedKeypair; + ReturnErrorOnFailure(serializedKeypair.SetLength(privateKey.size() + publicKey.size())); + memcpy(serializedKeypair.Bytes(), publicKey.data(), publicKey.size()); + memcpy(serializedKeypair.Bytes() + publicKey.size(), privateKey.data(), privateKey.size()); + return keypair.Deserialize(serializedKeypair); +} +} // namespace + +CHIP_ERROR ESP32FactoryDataProvider::GetSetupDiscriminator(uint16_t & setupDiscriminator) +{ + uint32_t setupDiscriminator32; + ReturnErrorOnFailure(ESP32Config::ReadConfigValue(ESP32Config::kConfigKey_SetupDiscriminator, setupDiscriminator32)); + VerifyOrReturnLogError(setupDiscriminator32 <= kMaxDiscriminatorValue, CHIP_ERROR_INVALID_ARGUMENT); + setupDiscriminator = static_cast(setupDiscriminator32); + return CHIP_NO_ERROR; +} + +CHIP_ERROR ESP32FactoryDataProvider::GetSpake2pIterationCount(uint32_t & iterationCount) +{ + return ESP32Config::ReadConfigValue(ESP32Config::kConfigKey_Spake2pIterationCount, iterationCount); +} + +CHIP_ERROR ESP32FactoryDataProvider::GetSpake2pSalt(MutableByteSpan & saltBuf) +{ + static constexpr size_t kSpake2pSalt_MaxBase64Len = BASE64_ENCODED_LEN(chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length) + 1; + + CHIP_ERROR err = CHIP_NO_ERROR; + char saltB64[kSpake2pSalt_MaxBase64Len] = { 0 }; + size_t saltB64Len = 0; + + err = ESP32Config::ReadConfigValueStr(ESP32Config::kConfigKey_Spake2pSalt, saltB64, sizeof(saltB64), saltB64Len); + ReturnErrorOnFailure(err); + + size_t saltLen = chip::Base64Decode32(saltB64, saltB64Len, reinterpret_cast(saltB64)); + ReturnErrorCodeIf(saltLen > saltBuf.size(), CHIP_ERROR_BUFFER_TOO_SMALL); + + memcpy(saltBuf.data(), saltB64, saltLen); + saltBuf.reduce_size(saltLen); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR ESP32FactoryDataProvider::GetSpake2pVerifier(MutableByteSpan & verifierBuf, size_t & verifierLen) +{ + static constexpr size_t kSpake2pSerializedVerifier_MaxBase64Len = + BASE64_ENCODED_LEN(chip::Crypto::kSpake2p_VerifierSerialized_Length) + 1; + + CHIP_ERROR err = CHIP_NO_ERROR; + char verifierB64[kSpake2pSerializedVerifier_MaxBase64Len] = { 0 }; + size_t verifierB64Len = 0; + + err = + ESP32Config::ReadConfigValueStr(ESP32Config::kConfigKey_Spake2pVerifier, verifierB64, sizeof(verifierB64), verifierB64Len); + ReturnErrorOnFailure(err); + + verifierLen = chip::Base64Decode32(verifierB64, verifierB64Len, reinterpret_cast(verifierB64)); + ReturnErrorCodeIf(verifierLen > verifierBuf.size(), CHIP_ERROR_BUFFER_TOO_SMALL); + + memcpy(verifierBuf.data(), verifierB64, verifierLen); + verifierBuf.reduce_size(verifierLen); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR ESP32FactoryDataProvider::GetCertificationDeclaration(MutableByteSpan & outBuffer) +{ + size_t certSize; + ReturnErrorOnFailure( + ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_CertDeclaration, outBuffer.data(), outBuffer.size(), certSize)); + outBuffer.reduce_size(certSize); + return CHIP_NO_ERROR; +} + +CHIP_ERROR ESP32FactoryDataProvider::GetFirmwareInformation(MutableByteSpan & out_firmware_info_buffer) +{ + // We do not provide any FirmwareInformation. + out_firmware_info_buffer.reduce_size(0); + return CHIP_NO_ERROR; +} + +CHIP_ERROR ESP32FactoryDataProvider::GetDeviceAttestationCert(MutableByteSpan & outBuffer) +{ + size_t certSize; + ReturnErrorOnFailure( + ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_DACCert, outBuffer.data(), outBuffer.size(), certSize)); + outBuffer.reduce_size(certSize); + return CHIP_NO_ERROR; +} + +CHIP_ERROR ESP32FactoryDataProvider::GetProductAttestationIntermediateCert(MutableByteSpan & outBuffer) +{ + size_t certSize; + ReturnErrorOnFailure( + ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_PAICert, outBuffer.data(), outBuffer.size(), certSize)); + outBuffer.reduce_size(certSize); + return CHIP_NO_ERROR; +} + +CHIP_ERROR ESP32FactoryDataProvider::SignWithDeviceAttestationKey(const ByteSpan & digestToSign, MutableByteSpan & outSignBuffer) +{ + Crypto::P256ECDSASignature signature; + Crypto::P256Keypair keypair; + + VerifyOrReturnError(IsSpanUsable(outSignBuffer), CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(IsSpanUsable(digestToSign), CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(outSignBuffer.size() >= signature.Capacity(), CHIP_ERROR_BUFFER_TOO_SMALL); + + uint8_t privKeyBuf[kDACPrivateKeySize]; + uint8_t pubKeyBuf[kDACPublicKeySize]; + size_t privKeyLen = sizeof(privKeyBuf); + size_t pubKeyLen = sizeof(pubKeyBuf); + + ReturnErrorOnFailure( + ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_DACPrivateKey, privKeyBuf, privKeyLen, privKeyLen)); + ReturnErrorOnFailure(ESP32Config::ReadConfigValueBin(ESP32Config::kConfigKey_DACPublicKey, pubKeyBuf, pubKeyLen, pubKeyLen)); + + ReturnErrorOnFailure(LoadKeypairFromRaw(ByteSpan(privKeyBuf, privKeyLen), ByteSpan(pubKeyBuf, pubKeyLen), keypair)); + ReturnErrorOnFailure(keypair.ECDSA_sign_hash(digestToSign.data(), digestToSign.size(), signature)); + + return CopySpanToMutableSpan(ByteSpan{ signature.ConstBytes(), signature.Length() }, outSignBuffer); +} + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/ESP32/ESP32FactoryDataProvider.h b/src/platform/ESP32/ESP32FactoryDataProvider.h new file mode 100644 index 00000000000000..76ef623fd061be --- /dev/null +++ b/src/platform/ESP32/ESP32FactoryDataProvider.h @@ -0,0 +1,60 @@ +/* + * + * Copyright (c) 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. + */ +#pragma once + +#include +#include + +namespace chip { +namespace DeviceLayer { + +/** + * @brief This class provides Commissionable data and Device Attestation Credentials. + */ + +class ESP32FactoryDataProvider : public CommissionableDataProvider, public chip::Credentials::DeviceAttestationCredentialsProvider +{ +public: + // ===== Members functions that implement the CommissionableDataProvider + CHIP_ERROR GetSetupDiscriminator(uint16_t & setupDiscriminator) override; + + CHIP_ERROR SetSetupDiscriminator(uint16_t setupDiscriminator) override { return CHIP_ERROR_NOT_IMPLEMENTED; } + + CHIP_ERROR GetSpake2pIterationCount(uint32_t & iterationCount) override; + + CHIP_ERROR GetSpake2pSalt(MutableByteSpan & saltBuf) override; + + CHIP_ERROR GetSpake2pVerifier(MutableByteSpan & verifierBuf, size_t & verifierLen) override; + + CHIP_ERROR GetSetupPasscode(uint32_t & setupPasscode) override { return CHIP_ERROR_NOT_IMPLEMENTED; } + + CHIP_ERROR SetSetupPasscode(uint32_t setupPasscode) override { return CHIP_ERROR_NOT_IMPLEMENTED; } + + // ===== Members functions that implement the DeviceAttestationCredentialsProvider + CHIP_ERROR GetCertificationDeclaration(MutableByteSpan & outBuffer) override; + + CHIP_ERROR GetFirmwareInformation(MutableByteSpan & out_firmware_info_buffer) override; + + CHIP_ERROR GetDeviceAttestationCert(MutableByteSpan & outBuffer) override; + + CHIP_ERROR GetProductAttestationIntermediateCert(MutableByteSpan & outBuffer) override; + + CHIP_ERROR SignWithDeviceAttestationKey(const ByteSpan & digestToSign, MutableByteSpan & outSignBuffer) override; +}; + +} // namespace DeviceLayer +} // namespace chip