diff --git a/examples/platform/linux/AppMain.cpp b/examples/platform/linux/AppMain.cpp index c00a500c589423..15acc38f3a5c0e 100644 --- a/examples/platform/linux/AppMain.cpp +++ b/examples/platform/linux/AppMain.cpp @@ -298,6 +298,13 @@ void ChipLinuxAppMainLoop() initParams.interfaceId = LinuxDeviceOptions::GetInstance().interfaceId; + if (LinuxDeviceOptions::GetInstance().mCSRResponseOptions.csrExistingKeyPair) + { + LinuxDeviceOptions::GetInstance().mCSRResponseOptions.badCsrOperationalKeyStoreForTest.Init( + initParams.persistentStorageDelegate); + initParams.operationalKeystore = &LinuxDeviceOptions::GetInstance().mCSRResponseOptions.badCsrOperationalKeyStoreForTest; + } + // Init ZCL Data Model and CHIP App Server Server::GetInstance().Init(initParams); diff --git a/examples/platform/linux/BUILD.gn b/examples/platform/linux/BUILD.gn index 640d5230b9e2cf..d806ca2c6587aa 100644 --- a/examples/platform/linux/BUILD.gn +++ b/examples/platform/linux/BUILD.gn @@ -41,6 +41,8 @@ source_set("app-main") { "Options.h", "testing/CustomCSRResponse.cpp", "testing/CustomCSRResponse.h", + "testing/CustomCSRResponseOperationalKeyStore.cpp", + "testing/CustomCSRResponseOperationalKeyStore.h", ] defines = [] diff --git a/examples/platform/linux/Options.cpp b/examples/platform/linux/Options.cpp index c8bae87c377e14..6d74aa1dcc0451 100644 --- a/examples/platform/linux/Options.cpp +++ b/examples/platform/linux/Options.cpp @@ -69,6 +69,7 @@ enum kOptionCSRResponseNOCSRElementsTooLong = 0x101b, kOptionCSRResponseAttestationSignatureIncorrectType = 0x101c, kOptionCSRResponseAttestationSignatureInvalid = 0x101d, + kOptionCSRResponseCSRExistingKeyPair = 0x101e, }; constexpr unsigned kAppUsageLength = 64; @@ -106,6 +107,7 @@ OptionDef sDeviceOptionDefs[] = { { "trace_decode", kArgumentRequired, kDeviceOption_TraceDecode }, #endif // CHIP_CONFIG_TRANSPORT_TRACE_ENABLED { "cert_error_csr_incorrect_type", kNoArgument, kOptionCSRResponseCSRIncorrectType }, + { "cert_error_csr_existing_keypair", kNoArgument, kOptionCSRResponseCSRExistingKeyPair }, { "cert_error_csr_nonce_incorrect_type", kNoArgument, kOptionCSRResponseCSRNonceIncorrectType }, { "cert_error_csr_nonce_too_long", kNoArgument, kOptionCSRResponseCSRNonceTooLong }, { "cert_error_csr_nonce_invalid", kNoArgument, kOptionCSRResponseCSRNonceInvalid }, @@ -199,6 +201,8 @@ const char * sDeviceOptionHelp = #endif // CHIP_CONFIG_TRANSPORT_TRACE_ENABLED " --cert_error_csr_incorrect_type\n" " Configure the CSRResponse to be built with an invalid CSR type.\n" + " --cert_error_csr_existing_keypair\n" + " Configure the CSRResponse to be built with a CSR where the keypair already exists.\n" " --cert_error_csr_nonce_incorrect_type\n" " Configure the CSRResponse to be built with an invalid CSRNonce type.\n" " --cert_error_csr_nonce_too_long\n" @@ -417,6 +421,9 @@ bool HandleOption(const char * aProgram, OptionSet * aOptions, int aIdentifier, case kOptionCSRResponseCSRIncorrectType: LinuxDeviceOptions::GetInstance().mCSRResponseOptions.csrIncorrectType = true; break; + case kOptionCSRResponseCSRExistingKeyPair: + LinuxDeviceOptions::GetInstance().mCSRResponseOptions.csrExistingKeyPair = true; + break; case kOptionCSRResponseCSRNonceIncorrectType: LinuxDeviceOptions::GetInstance().mCSRResponseOptions.csrNonceIncorrectType = true; break; diff --git a/examples/platform/linux/testing/CustomCSRResponse.cpp b/examples/platform/linux/testing/CustomCSRResponse.cpp index 39f7e9d2cad6c7..f5009375f1dcd5 100644 --- a/examples/platform/linux/testing/CustomCSRResponse.cpp +++ b/examples/platform/linux/testing/CustomCSRResponse.cpp @@ -147,9 +147,9 @@ namespace DataModel { template <> CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, const CSRResponse::Type & responseData) { - auto tag1 = TLV::ContextTag(to_underlying(CSRResponse::Fields::kNOCSRElements)); - auto tag2 = TLV::ContextTag(to_underlying(CSRResponse::Fields::kAttestationSignature)); - auto options = LinuxDeviceOptions::GetInstance().mCSRResponseOptions; + auto tag1 = TLV::ContextTag(to_underlying(CSRResponse::Fields::kNOCSRElements)); + auto tag2 = TLV::ContextTag(to_underlying(CSRResponse::Fields::kAttestationSignature)); + auto & options = LinuxDeviceOptions::GetInstance().mCSRResponseOptions; TLV::TLVType outer; ReturnErrorOnFailure(writer.StartContainer(tag, TLV::kTLVType_Structure, outer)); diff --git a/examples/platform/linux/testing/CustomCSRResponse.h b/examples/platform/linux/testing/CustomCSRResponse.h index 5de00b0646ae6d..2027a9223e3915 100644 --- a/examples/platform/linux/testing/CustomCSRResponse.h +++ b/examples/platform/linux/testing/CustomCSRResponse.h @@ -16,17 +16,21 @@ * limitations under the License. */ +#include "CustomCSRResponseOperationalKeyStore.h" + namespace chip { struct CSRResponseOptions { bool csrIncorrectType = false; + bool csrExistingKeyPair = false; bool csrNonceIncorrectType = false; bool csrNonceTooLong = false; bool csrNonceInvalid = false; bool nocsrElementsTooLong = false; bool attestationSignatureIncorrectType = false; bool attestationSignatureInvalid = false; + CustomCSRResponseOperationalKeyStore badCsrOperationalKeyStoreForTest; }; } // namespace chip diff --git a/examples/platform/linux/testing/CustomCSRResponseOperationalKeyStore.cpp b/examples/platform/linux/testing/CustomCSRResponseOperationalKeyStore.cpp new file mode 100644 index 00000000000000..cbdb3802f80813 --- /dev/null +++ b/examples/platform/linux/testing/CustomCSRResponseOperationalKeyStore.cpp @@ -0,0 +1,131 @@ +/* + * + * 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. + */ + +#include "CustomCSRResponseOperationalKeyStore.h" + +#include +#include +#include + +namespace chip { + +namespace { +// Tags for our operational keypair storage. +constexpr TLV::Tag kOpKeyVersionTag = TLV::ContextTag(0); +constexpr TLV::Tag kOpKeyDataTag = TLV::ContextTag(1); + +constexpr size_t OpKeyTLVMaxSize() +{ + // Version and serialized key + return TLV::EstimateStructOverhead(sizeof(uint16_t), Crypto::P256SerializedKeypair::Capacity()); +} +} // namespace + +CHIP_ERROR CustomCSRResponseOperationalKeyStore::NewOpKeypairForFabric(FabricIndex fabricIndex, + MutableByteSpan & outCertificateSigningRequest) +{ + if (fabricIndex == 1) + { + return PersistentStorageOperationalKeystore::NewOpKeypairForFabric(fabricIndex, outCertificateSigningRequest); + } + + return ReuseOpKeypair(fabricIndex, outCertificateSigningRequest); +} + +CHIP_ERROR CustomCSRResponseOperationalKeyStore::ReuseOpKeypair(FabricIndex fabricIndex, MutableByteSpan & outCSR) +{ + // + // DO NOT COPY THIS METHOD - IT IS FOR TESTING PURPOSES ONLY + // + + VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE); + + // Replace previous pending keypair, if any was previously allocated + ResetPendingKey(); + + mPendingKeypair = Platform::New(); + VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_NO_MEMORY); + + // Scope 1: Load up the keypair data from storage + { + // Use a CapacityBoundBuffer to get RAII secret data clearing on scope exit. + Crypto::CapacityBoundBuffer buf; + + // Load up the operational key structure from storage + uint16_t size = static_cast(buf.Capacity()); + DefaultStorageKeyAllocator keyAlloc; + + // In order to retrieve a keypair that has already been registered, assume the device + // as already been commissioned and fabric index 1 is the registered fabric. + CHIP_ERROR err = mStorage->SyncGetKeyValue(keyAlloc.FabricOpKey(1 /* fabricIndex */), buf.Bytes(), size); + if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) + { + err = CHIP_ERROR_INVALID_FABRIC_INDEX; + } + ReturnErrorOnFailure(err); + buf.SetLength(static_cast(size)); + + // Read-out the operational key TLV entry. + TLV::ContiguousBufferTLVReader reader; + reader.Init(buf.Bytes(), buf.Length()); + + ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())); + TLV::TLVType containerType; + ReturnErrorOnFailure(reader.EnterContainer(containerType)); + + ReturnErrorOnFailure(reader.Next(kOpKeyVersionTag)); + uint16_t opKeyVersion; + ReturnErrorOnFailure(reader.Get(opKeyVersion)); + + ReturnErrorOnFailure(reader.Next(kOpKeyDataTag)); + { + ByteSpan keyData; + Crypto::P256SerializedKeypair serializedOpKey; + ReturnErrorOnFailure(reader.GetByteView(keyData)); + + // Unfortunately, we have to copy the data into a P256SerializedKeypair. + VerifyOrReturnError(keyData.size() <= serializedOpKey.Capacity(), CHIP_ERROR_BUFFER_TOO_SMALL); + + // Before doing anything with the key, validate format further. + ReturnErrorOnFailure(reader.ExitContainer(containerType)); + ReturnErrorOnFailure(reader.VerifyEndOfContainer()); + + memcpy(serializedOpKey.Bytes(), keyData.data(), keyData.size()); + serializedOpKey.SetLength(keyData.size()); + + // Load-up key material + // WARNING: This makes use of the raw key bits + ReturnErrorOnFailure(mPendingKeypair->Deserialize(serializedOpKey)); + } + } + + size_t outCSRLength = outCSR.size(); + CHIP_ERROR err = mPendingKeypair->NewCertificateSigningRequest(outCSR.data(), outCSRLength); + if (CHIP_NO_ERROR != err) + { + ResetPendingKey(); + return err; + } + + outCSR.reduce_size(outCSRLength); + mPendingFabricIndex = fabricIndex; + + return CHIP_NO_ERROR; +} + +} // namespace chip diff --git a/examples/platform/linux/testing/CustomCSRResponseOperationalKeyStore.h b/examples/platform/linux/testing/CustomCSRResponseOperationalKeyStore.h new file mode 100644 index 00000000000000..4da015f110ab63 --- /dev/null +++ b/examples/platform/linux/testing/CustomCSRResponseOperationalKeyStore.h @@ -0,0 +1,34 @@ +/* + * + * 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. + */ + +#pragma once + +#include + +namespace chip { + +class CustomCSRResponseOperationalKeyStore : public PersistentStorageOperationalKeystore +{ +public: + CHIP_ERROR NewOpKeypairForFabric(FabricIndex fabricIndex, MutableByteSpan & outCertificateSigningRequest) override; + +private: + CHIP_ERROR ReuseOpKeypair(FabricIndex fabricIndex, MutableByteSpan & outCertificateSigningRequest); +}; + +} // namespace chip