Skip to content

Commit

Permalink
[Testing] Add a command line option to generate the CSR with an exist…
Browse files Browse the repository at this point in the history
…ing keypair instead of a new one (#19528)

* [Testing] Add an option to generate the CSR with an existing keypair instead of a new one

* [Testing] Override the operational key store of examples/platform/linux by a custom one for testing purposes

* [Testing] Implement the content of the /CustomCSRResponseOperationalKeyStore
  • Loading branch information
vivien-apple authored Jun 15, 2022
1 parent 85c7b57 commit a99bed0
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 3 deletions.
7 changes: 7 additions & 0 deletions examples/platform/linux/AppMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 2 additions & 0 deletions examples/platform/linux/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ source_set("app-main") {
"Options.h",
"testing/CustomCSRResponse.cpp",
"testing/CustomCSRResponse.h",
"testing/CustomCSRResponseOperationalKeyStore.cpp",
"testing/CustomCSRResponseOperationalKeyStore.h",
]

defines = []
Expand Down
7 changes: 7 additions & 0 deletions examples/platform/linux/Options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ enum
kOptionCSRResponseNOCSRElementsTooLong = 0x101b,
kOptionCSRResponseAttestationSignatureIncorrectType = 0x101c,
kOptionCSRResponseAttestationSignatureInvalid = 0x101d,
kOptionCSRResponseCSRExistingKeyPair = 0x101e,
};

constexpr unsigned kAppUsageLength = 64;
Expand Down Expand Up @@ -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 },
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions examples/platform/linux/testing/CustomCSRResponse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
4 changes: 4 additions & 0 deletions examples/platform/linux/testing/CustomCSRResponse.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
@@ -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 <credentials/FabricTable.h>
#include <lib/core/CHIPTLV.h>
#include <lib/support/DefaultStorageKeyAllocator.h>

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<Crypto::P256Keypair>();
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<OpKeyTLVMaxSize()> buf;

// Load up the operational key structure from storage
uint16_t size = static_cast<uint16_t>(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_t>(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
Original file line number Diff line number Diff line change
@@ -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 <crypto/PersistentStorageOperationalKeystore.h>

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

0 comments on commit a99bed0

Please sign in to comment.