blob: c1a57e00e95af24706fb62d123a1bcdd69307406 [file] [log] [blame]
/*
*
* 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 <inttypes.h>
#include <credentials/PersistentStorageOpCertStore.h>
#include <gtest/gtest.h>
#include <lib/support/CHIPMem.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/DefaultStorageKeyAllocator.h>
#include <lib/support/Span.h>
#include <lib/support/TestPersistentStorageDelegate.h>
using namespace chip;
using namespace chip::Credentials;
using CertChainElement = OperationalCertificateStore::CertChainElement;
namespace {
constexpr FabricIndex kFabricIndex1 = 1;
constexpr FabricIndex kFabricIndex2 = 2;
constexpr FabricIndex kOtherFabricIndex = static_cast<FabricIndex>(kFabricIndex1 + 10u);
// The PersistentStorageOpCertStore does not validate cert contents, so we can use simple constants
const uint8_t kTestRcacBuf[] = { 'r', 'c', 'a', 'c' };
const ByteSpan kTestRcacSpan{ kTestRcacBuf };
const uint8_t kTestIcacBuf[] = { 'i', 'c', 'a', 'c' };
const ByteSpan kTestIcacSpan{ kTestIcacBuf };
const uint8_t kTestNocBuf[] = { 'n', 'o', 'c' };
const ByteSpan kTestNocSpan{ kTestNocBuf };
struct TestPersistentStorageOpCertStore : public ::testing::Test
{
static void SetUpTestSuite() { ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); }
static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); }
};
TEST_F(TestPersistentStorageOpCertStore, TestAddNocFlow)
{
TestPersistentStorageDelegate storageDelegate;
PersistentStorageOpCertStore opCertStore;
// Failure before Init
CHIP_ERROR err = opCertStore.AddNewTrustedRootCertForFabric(kFabricIndex1, kTestRcacSpan);
EXPECT_EQ(err, CHIP_ERROR_INCORRECT_STATE);
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u);
// Init succeeds
err = opCertStore.Init(&storageDelegate);
EXPECT_EQ(err, CHIP_NO_ERROR);
// Manually add existing root for the FabricIndex, should fail AddNewTrustedRootCertForFabric for
// same fabric but succeed GetCertificate.
const uint8_t kTestRcacBufExists[] = { 'r', 'c', 'a', 'c', ' ', 'e', 'x', 'i', 's', 't', 's' };
err = storageDelegate.SyncSetKeyValue(DefaultStorageKeyAllocator::FabricRCAC(kFabricIndex1).KeyName(), kTestRcacBufExists,
sizeof(kTestRcacBufExists));
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_EQ(storageDelegate.GetNumKeys(), 1u);
EXPECT_TRUE(opCertStore.HasCertificateForFabric(kFabricIndex1, CertChainElement::kRcac)); //< From manual add
err = opCertStore.AddNewTrustedRootCertForFabric(kFabricIndex1, kTestRcacSpan);
EXPECT_EQ(err, CHIP_ERROR_INCORRECT_STATE);
uint8_t largeBuf[400];
MutableByteSpan largeSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kNoc, largeSpan);
EXPECT_EQ(err, CHIP_ERROR_NOT_FOUND);
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kRcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestRcacBufExists }));
// Adding root for another FabricIndex should work
err = opCertStore.AddNewTrustedRootCertForFabric(kUndefinedFabricIndex, kTestRcacSpan);
EXPECT_EQ(err, CHIP_ERROR_INVALID_FABRIC_INDEX);
err = opCertStore.AddNewTrustedRootCertForFabric(kFabricIndex2, kTestRcacSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_EQ(storageDelegate.GetNumKeys(), 1u); //< Storage count did not yet increase
EXPECT_FALSE(opCertStore.HasPendingNocChain());
EXPECT_TRUE(opCertStore.HasPendingRootCert());
// Should be able to read pending RCAC right away
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex2, CertChainElement::kRcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestRcacSpan }));
// Trying to commit with pending RCAC but no NOC should fail but leave everything as-is
err = opCertStore.CommitOpCertsForFabric(kFabricIndex2);
EXPECT_EQ(err, CHIP_ERROR_INCORRECT_STATE);
EXPECT_FALSE(opCertStore.HasPendingNocChain());
EXPECT_TRUE(opCertStore.HasPendingRootCert());
// Trying to do AddNewOpCertsForFabric for fabric different that with pending RCAC should fail
err = opCertStore.AddNewOpCertsForFabric(kOtherFabricIndex, kTestNocSpan, kTestIcacSpan);
EXPECT_EQ(err, CHIP_ERROR_INVALID_FABRIC_INDEX);
EXPECT_FALSE(opCertStore.HasPendingNocChain());
EXPECT_TRUE(opCertStore.HasPendingRootCert());
// Clear other bad cases from storage for now
storageDelegate.ClearStorage();
// Trying to do AddNewOpCertsForFabric for same fabric as that with pending RCAC should fail
// if there are already existing NOC chain elements for the given fabric.
const uint8_t kTestIcacBufExists[] = { 'i', 'c', 'a', 'c', ' ', 'e', 'x', 'i', 's', 't', 's' };
const uint8_t kTestNocBufExists[] = { 'n', 'o', 'c', ' ', 'e', 'x', 'i', 's', 't', 's' };
err = storageDelegate.SyncSetKeyValue(DefaultStorageKeyAllocator::FabricICAC(kFabricIndex2).KeyName(), kTestIcacBufExists,
sizeof(kTestIcacBufExists));
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_EQ(storageDelegate.GetNumKeys(), 1u);
err = storageDelegate.SyncSetKeyValue(DefaultStorageKeyAllocator::FabricNOC(kFabricIndex2).KeyName(), kTestNocBufExists,
sizeof(kTestNocBufExists));
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_EQ(storageDelegate.GetNumKeys(), 2u);
EXPECT_TRUE(opCertStore.HasCertificateForFabric(kFabricIndex2, CertChainElement::kIcac)); //< From manual add
EXPECT_TRUE(opCertStore.HasCertificateForFabric(kFabricIndex2, CertChainElement::kNoc)); //< From manual add
err = opCertStore.AddNewOpCertsForFabric(kFabricIndex2, kTestNocSpan, kTestIcacSpan);
EXPECT_EQ(err, CHIP_ERROR_INCORRECT_STATE);
EXPECT_FALSE(opCertStore.HasPendingNocChain());
EXPECT_TRUE(opCertStore.HasPendingRootCert());
storageDelegate.SyncDeleteKeyValue(DefaultStorageKeyAllocator::FabricICAC(kFabricIndex2).KeyName());
storageDelegate.SyncDeleteKeyValue(DefaultStorageKeyAllocator::FabricNOC(kFabricIndex2).KeyName());
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u);
// Trying to do AddNewOpCertsForFabric for same fabric as that with pending RCAC should succeed
err = opCertStore.AddNewOpCertsForFabric(kFabricIndex2, kTestNocSpan, kTestIcacSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(opCertStore.HasPendingNocChain());
EXPECT_TRUE(opCertStore.HasPendingRootCert());
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
// Should be able to get the pending cert even if not in persisted storage
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex2, CertChainElement::kIcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestIcacSpan }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex2, CertChainElement::kNoc, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestNocSpan }));
// Trying to do AddNewOpCertsForFabric a second time after success before commit should fail,
// but leave state as-is
err = opCertStore.AddNewOpCertsForFabric(kFabricIndex2, kTestNocSpan, kTestIcacSpan);
EXPECT_EQ(err, CHIP_ERROR_INCORRECT_STATE);
EXPECT_TRUE(opCertStore.HasPendingNocChain());
EXPECT_TRUE(opCertStore.HasPendingRootCert());
// Should be able to get the pending cert even if not in persisted storage, after an API error
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex2, CertChainElement::kIcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestIcacSpan }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex2, CertChainElement::kNoc, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestNocSpan }));
// Trying to commit with wrong FabricIndex should fail
err = opCertStore.CommitOpCertsForFabric(kOtherFabricIndex);
EXPECT_EQ(err, CHIP_ERROR_INVALID_FABRIC_INDEX);
EXPECT_TRUE(opCertStore.HasPendingNocChain());
EXPECT_TRUE(opCertStore.HasPendingRootCert());
// Commiting new certs should succeed on correct fabric
err = opCertStore.CommitOpCertsForFabric(kFabricIndex2);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_FALSE(opCertStore.HasPendingNocChain());
EXPECT_FALSE(opCertStore.HasPendingRootCert());
EXPECT_EQ(storageDelegate.GetNumKeys(), 3u); //< All certs now committed
// Should be able to get the committed certs
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex2, CertChainElement::kRcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestRcacSpan }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex2, CertChainElement::kIcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestIcacSpan }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex2, CertChainElement::kNoc, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestNocSpan }));
opCertStore.Finish();
}
TEST_F(TestPersistentStorageOpCertStore, TestUpdateNocFlow)
{
TestPersistentStorageDelegate storageDelegate;
PersistentStorageOpCertStore opCertStore;
// Failure before Init
CHIP_ERROR err = opCertStore.UpdateOpCertsForFabric(kFabricIndex1, kTestNocSpan, kTestIcacSpan);
EXPECT_EQ(err, CHIP_ERROR_INCORRECT_STATE);
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u);
// Init succeeds
err = opCertStore.Init(&storageDelegate);
EXPECT_EQ(err, CHIP_NO_ERROR);
// Add a new pending trusted root to test for UpdateOpCertsForFabric failure on new root present
err = opCertStore.AddNewTrustedRootCertForFabric(kFabricIndex1, kTestRcacSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
EXPECT_TRUE(opCertStore.HasPendingRootCert());
EXPECT_FALSE(opCertStore.HasPendingNocChain());
// Trying to do an UpdateOpCertsForFabric with new root pending should fail
err = opCertStore.UpdateOpCertsForFabric(kFabricIndex1, kTestNocSpan, kTestIcacSpan);
EXPECT_EQ(err, CHIP_ERROR_INCORRECT_STATE);
EXPECT_TRUE(opCertStore.HasPendingRootCert());
EXPECT_FALSE(opCertStore.HasPendingNocChain());
// Revert state for next tests
opCertStore.RevertPendingOpCerts();
EXPECT_FALSE(opCertStore.HasPendingNocChain());
EXPECT_FALSE(opCertStore.HasPendingRootCert());
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
// Manually add root, ICAC and NOC to validate update since existing chain required
const uint8_t kTestRcacBufExists[] = { 'r', 'c', 'a', 'c', ' ', 'e', 'x', 'i', 's', 't', 's' };
const uint8_t kTestIcacBufExists[] = { 'i', 'c', 'a', 'c', ' ', 'e', 'x', 'i', 's', 't', 's' };
const uint8_t kTestNocBufExists[] = { 'n', 'o', 'c', ' ', 'e', 'x', 'i', 's', 't', 's' };
uint8_t largeBuf[400];
MutableByteSpan largeSpan{ largeBuf };
{
err = storageDelegate.SyncSetKeyValue(DefaultStorageKeyAllocator::FabricRCAC(kFabricIndex1).KeyName(), kTestRcacBufExists,
sizeof(kTestRcacBufExists));
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_EQ(storageDelegate.GetNumKeys(), 1u);
EXPECT_TRUE(opCertStore.HasCertificateForFabric(kFabricIndex1, CertChainElement::kRcac)); //< From manual add
err = storageDelegate.SyncSetKeyValue(DefaultStorageKeyAllocator::FabricICAC(kFabricIndex1).KeyName(), kTestIcacBufExists,
sizeof(kTestIcacBufExists));
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_EQ(storageDelegate.GetNumKeys(), 2u);
EXPECT_TRUE(opCertStore.HasCertificateForFabric(kFabricIndex1, CertChainElement::kIcac)); //< From manual add
err = storageDelegate.SyncSetKeyValue(DefaultStorageKeyAllocator::FabricNOC(kFabricIndex1).KeyName(), kTestNocBufExists,
sizeof(kTestNocBufExists));
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_EQ(storageDelegate.GetNumKeys(), 3u);
EXPECT_TRUE(opCertStore.HasCertificateForFabric(kFabricIndex1, CertChainElement::kNoc)); //< From manual add
// Test that we can manually stored certs
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kRcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestRcacBufExists }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kIcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestIcacBufExists }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kNoc, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestNocBufExists }));
}
// Update fails on fabric with wrong FabricIndex
err = opCertStore.UpdateOpCertsForFabric(kOtherFabricIndex, kTestNocSpan, kTestIcacSpan);
EXPECT_EQ(err, CHIP_ERROR_INCORRECT_STATE);
EXPECT_FALSE(opCertStore.HasPendingRootCert());
EXPECT_FALSE(opCertStore.HasPendingNocChain());
// Update succeeds on fabric with existing data
err = opCertStore.UpdateOpCertsForFabric(kFabricIndex1, kTestNocSpan, kTestIcacSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_FALSE(opCertStore.HasPendingRootCert());
EXPECT_TRUE(opCertStore.HasPendingNocChain());
EXPECT_EQ(storageDelegate.GetNumKeys(), 3u);
// Can read back existing root unchanged
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kRcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestRcacBufExists }));
// NOC chain elements see the pending updated certs
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kIcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestIcacBuf }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kNoc, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestNocBuf }));
// Trying update again fails
err = opCertStore.UpdateOpCertsForFabric(kFabricIndex1, kTestNocSpan, kTestIcacSpan);
EXPECT_EQ(err, CHIP_ERROR_INCORRECT_STATE);
EXPECT_FALSE(opCertStore.HasPendingRootCert());
EXPECT_TRUE(opCertStore.HasPendingNocChain());
EXPECT_EQ(storageDelegate.GetNumKeys(), 3u);
// Trying to add a new root after update, before commit/revert fails
err = opCertStore.AddNewTrustedRootCertForFabric(kFabricIndex1, kTestRcacSpan);
EXPECT_EQ(err, CHIP_ERROR_INCORRECT_STATE);
EXPECT_FALSE(opCertStore.HasPendingRootCert());
EXPECT_TRUE(opCertStore.HasPendingNocChain());
EXPECT_EQ(storageDelegate.GetNumKeys(), 3u);
// Trying to add new opcerts for any fabric after update, before commit/revert fails
err = opCertStore.AddNewOpCertsForFabric(kFabricIndex1, kTestNocSpan, kTestIcacSpan);
EXPECT_EQ(err, CHIP_ERROR_INCORRECT_STATE);
err = opCertStore.AddNewOpCertsForFabric(kFabricIndex2, kTestNocSpan, kTestIcacSpan);
EXPECT_EQ(err, CHIP_ERROR_INCORRECT_STATE);
// Committing writes the new values (we even "background-remove" the old ICAC/NOC before commit)
storageDelegate.SyncDeleteKeyValue(DefaultStorageKeyAllocator::FabricICAC(kFabricIndex1).KeyName());
storageDelegate.SyncDeleteKeyValue(DefaultStorageKeyAllocator::FabricNOC(kFabricIndex1).KeyName());
EXPECT_EQ(storageDelegate.GetNumKeys(), 1u); //< Root remains
err = opCertStore.CommitOpCertsForFabric(kFabricIndex1);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_FALSE(opCertStore.HasPendingNocChain());
EXPECT_FALSE(opCertStore.HasPendingRootCert());
EXPECT_EQ(storageDelegate.GetNumKeys(), 3u); //< All certs now committed
// Should be able to get the committed cert even if not in persisted storage, after an API error
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kRcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestRcacBufExists }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kIcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestIcacSpan }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kNoc, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestNocSpan }));
// Calling revert doesn't undo the work we just did
opCertStore.RevertPendingOpCerts();
EXPECT_EQ(storageDelegate.GetNumKeys(), 3u); //< All certs now committed
// Verify the revert after commit left all data alone
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kRcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestRcacBufExists }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kIcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestIcacSpan }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kNoc, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestNocSpan }));
// Verify that RemoveOpCertsForFabric fails on fabric with no data
err = opCertStore.RemoveOpCertsForFabric(kFabricIndex2);
EXPECT_EQ(err, CHIP_ERROR_INVALID_FABRIC_INDEX);
EXPECT_EQ(storageDelegate.GetNumKeys(), 3u);
// Verify that RemoveOpCertsForFabric works for fabric we just updated
err = opCertStore.RemoveOpCertsForFabric(kFabricIndex1);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); // All keys gone
opCertStore.Finish();
}
TEST_F(TestPersistentStorageOpCertStore, TestReverts)
{
TestPersistentStorageDelegate storageDelegate;
PersistentStorageOpCertStore opCertStore;
// Failure before Init
CHIP_ERROR err = opCertStore.RemoveOpCertsForFabric(kFabricIndex1);
EXPECT_EQ(err, CHIP_ERROR_INCORRECT_STATE);
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u);
// Init succeeds
err = opCertStore.Init(&storageDelegate);
EXPECT_EQ(err, CHIP_NO_ERROR);
// Add a new pending trusted root
uint8_t largeBuf[400];
MutableByteSpan largeSpan{ largeBuf };
err = opCertStore.AddNewTrustedRootCertForFabric(kFabricIndex1, kTestRcacSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
EXPECT_TRUE(opCertStore.HasPendingRootCert());
EXPECT_FALSE(opCertStore.HasPendingNocChain());
// Verify we can see the new trusted root
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kRcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(kTestRcacSpan));
// Verify that after revert, we can't see the root anymore
opCertStore.RevertPendingOpCerts();
EXPECT_FALSE(opCertStore.HasPendingRootCert());
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kRcac, largeSpan);
EXPECT_EQ(err, CHIP_ERROR_NOT_FOUND);
{
// Add new root again, to then test review of AddNewTrustedCertificates
err = opCertStore.AddNewTrustedRootCertForFabric(kFabricIndex1, kTestRcacSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
EXPECT_TRUE(opCertStore.HasPendingRootCert());
EXPECT_FALSE(opCertStore.HasPendingNocChain());
// Add NOC chain
err = opCertStore.AddNewOpCertsForFabric(kFabricIndex1, kTestNocSpan, kTestIcacSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(opCertStore.HasPendingNocChain());
EXPECT_TRUE(opCertStore.HasPendingRootCert());
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
}
// Make sure we can see all pending certs before revert
{
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kRcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(kTestRcacSpan));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kIcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(kTestIcacSpan));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kNoc, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(kTestNocSpan));
}
// Revert
opCertStore.RevertPendingOpCerts();
EXPECT_FALSE(opCertStore.HasPendingRootCert());
EXPECT_FALSE(opCertStore.HasPendingNocChain());
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
// Verify that after revert, we can't see the root or chain anymore
{
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kRcac, largeSpan);
EXPECT_EQ(err, CHIP_ERROR_NOT_FOUND);
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kIcac, largeSpan);
EXPECT_EQ(err, CHIP_ERROR_NOT_FOUND);
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kNoc, largeSpan);
EXPECT_EQ(err, CHIP_ERROR_NOT_FOUND);
}
// Start again to add a new set, but then let's commit
{
// Add new root again, to then test review of AddNewTrustedCertificates
err = opCertStore.AddNewTrustedRootCertForFabric(kFabricIndex1, kTestRcacSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
EXPECT_TRUE(opCertStore.HasPendingRootCert());
EXPECT_FALSE(opCertStore.HasPendingNocChain());
// Add NOC chain
err = opCertStore.AddNewOpCertsForFabric(kFabricIndex1, kTestNocSpan, kTestIcacSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(opCertStore.HasPendingNocChain());
EXPECT_TRUE(opCertStore.HasPendingRootCert());
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
}
// Commiting new certs should succeed on correct fabric
err = opCertStore.CommitOpCertsForFabric(kFabricIndex1);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_FALSE(opCertStore.HasPendingNocChain());
EXPECT_FALSE(opCertStore.HasPendingRootCert());
EXPECT_EQ(storageDelegate.GetNumKeys(), 3u); //< All certs now committed
// Should be able to get the committed certs
{
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kRcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestRcacSpan }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kIcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestIcacSpan }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kNoc, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestNocSpan }));
}
const uint8_t kNewNoc[] = { 'n', 'o', 'c', ' ', 'n', 'e', 'w' };
// Updating certs should work (NO ICAC)
err = opCertStore.UpdateOpCertsForFabric(kFabricIndex1, ByteSpan{ kNewNoc }, ByteSpan{});
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_FALSE(opCertStore.HasPendingRootCert());
EXPECT_TRUE(opCertStore.HasPendingNocChain());
EXPECT_EQ(storageDelegate.GetNumKeys(), 3u); //< No change to keys
// Should see committed root, pending NOC, absent ICAC
{
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kRcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestRcacSpan }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kIcac, largeSpan);
EXPECT_EQ(err, CHIP_ERROR_NOT_FOUND);
EXPECT_FALSE(opCertStore.HasCertificateForFabric(kFabricIndex1, CertChainElement::kIcac));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kNoc, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kNewNoc }));
}
// Revert, should be back at previous state
opCertStore.RevertPendingOpCerts();
EXPECT_FALSE(opCertStore.HasPendingRootCert());
EXPECT_FALSE(opCertStore.HasPendingNocChain());
EXPECT_EQ(storageDelegate.GetNumKeys(), 3u); //< Storage count did not yet change
// Should be able to get the previously committed certs
{
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kRcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestRcacSpan }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kIcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestIcacSpan }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kNoc, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestNocSpan }));
}
// Try again to update with missing ICAC and commit
err = opCertStore.UpdateOpCertsForFabric(kFabricIndex1, ByteSpan{ kNewNoc }, ByteSpan{});
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_FALSE(opCertStore.HasPendingRootCert());
EXPECT_TRUE(opCertStore.HasPendingNocChain());
EXPECT_EQ(storageDelegate.GetNumKeys(), 3u); //< No change to keys
err = opCertStore.CommitOpCertsForFabric(kFabricIndex1);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_FALSE(opCertStore.HasPendingRootCert());
EXPECT_FALSE(opCertStore.HasPendingNocChain());
EXPECT_EQ(storageDelegate.GetNumKeys(), 2u); //< ICAC cert should be gone
// Should see committed root, new NOC, absent ICAC
{
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kRcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestRcacSpan }));
EXPECT_TRUE(opCertStore.HasCertificateForFabric(kFabricIndex1, CertChainElement::kRcac));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kIcac, largeSpan);
EXPECT_EQ(err, CHIP_ERROR_NOT_FOUND);
EXPECT_FALSE(opCertStore.HasCertificateForFabric(kFabricIndex1, CertChainElement::kIcac));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kNoc, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kNewNoc }));
EXPECT_TRUE(opCertStore.HasCertificateForFabric(kFabricIndex1, CertChainElement::kNoc));
}
opCertStore.Finish();
}
TEST_F(TestPersistentStorageOpCertStore, TestRevertAddNoc)
{
TestPersistentStorageDelegate storageDelegate;
PersistentStorageOpCertStore opCertStore;
// Init succeeds
CHIP_ERROR err = opCertStore.Init(&storageDelegate);
EXPECT_EQ(err, CHIP_NO_ERROR);
// Add a new pending trusted root
uint8_t largeBuf[400];
MutableByteSpan largeSpan{ largeBuf };
{
// Add new root
err = opCertStore.AddNewTrustedRootCertForFabric(kFabricIndex1, kTestRcacSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
EXPECT_TRUE(opCertStore.HasPendingRootCert());
EXPECT_FALSE(opCertStore.HasPendingNocChain());
// Add NOC chain, with NO ICAC
err = opCertStore.AddNewOpCertsForFabric(kFabricIndex1, kTestNocSpan, ByteSpan{});
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(opCertStore.HasPendingNocChain());
EXPECT_TRUE(opCertStore.HasPendingRootCert());
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
}
// Make sure we get expected pending state before revert
{
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kRcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(kTestRcacSpan));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kIcac, largeSpan);
EXPECT_EQ(err, CHIP_ERROR_NOT_FOUND);
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kNoc, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(kTestNocSpan));
}
// Revert using RemoveOpCertsForFabric
err = opCertStore.RemoveOpCertsForFabric(kFabricIndex1);
EXPECT_FALSE(opCertStore.HasPendingRootCert());
EXPECT_FALSE(opCertStore.HasPendingNocChain());
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
// Add again, and commit
{
// Add new root
err = opCertStore.AddNewTrustedRootCertForFabric(kFabricIndex1, kTestRcacSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
EXPECT_TRUE(opCertStore.HasPendingRootCert());
EXPECT_FALSE(opCertStore.HasPendingNocChain());
// Add NOC chain, with NO ICAC
err = opCertStore.AddNewOpCertsForFabric(kFabricIndex1, kTestNocSpan, ByteSpan{});
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(opCertStore.HasPendingNocChain());
EXPECT_TRUE(opCertStore.HasPendingRootCert());
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
err = opCertStore.CommitOpCertsForFabric(kFabricIndex1);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_FALSE(opCertStore.HasPendingRootCert());
EXPECT_FALSE(opCertStore.HasPendingNocChain());
EXPECT_EQ(storageDelegate.GetNumKeys(), 2u); //< We have RCAC, NOC, no ICAC
}
// Update to add an ICAC
const uint8_t kNewIcac[] = { 'i', 'c', 'a', 'c', ' ', 'n', 'e', 'w' };
const uint8_t kNewNoc[] = { 'n', 'o', 'c', ' ', 'n', 'e', 'w' };
// Updating certs should work (NO ICAC)
err = opCertStore.UpdateOpCertsForFabric(kFabricIndex1, ByteSpan{ kNewNoc }, ByteSpan{ kNewIcac });
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_FALSE(opCertStore.HasPendingRootCert());
EXPECT_TRUE(opCertStore.HasPendingNocChain());
EXPECT_EQ(storageDelegate.GetNumKeys(), 2u); //< No change to keys
// Should see committed root, pending NOC, pending ICAC
{
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kRcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestRcacSpan }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kIcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kNewIcac }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kNoc, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kNewNoc }));
}
// Commit, should see the new ICAC appear.
err = opCertStore.CommitOpCertsForFabric(kFabricIndex1);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_FALSE(opCertStore.HasPendingRootCert());
EXPECT_FALSE(opCertStore.HasPendingNocChain());
EXPECT_EQ(storageDelegate.GetNumKeys(), 3u); //< We have RCAC, NOC, ICAC
// Should see committed root, new NOC, new ICAC
{
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kRcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kTestRcacSpan }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kIcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kNewIcac }));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kNoc, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(ByteSpan{ kNewNoc }));
}
opCertStore.Finish();
}
TEST_F(TestPersistentStorageOpCertStore, TestRevertPendingOpCertsExceptRoot)
{
TestPersistentStorageDelegate storageDelegate;
PersistentStorageOpCertStore opCertStore;
// Init succeeds
CHIP_ERROR err = opCertStore.Init(&storageDelegate);
EXPECT_EQ(err, CHIP_NO_ERROR);
// Add a new pending trusted root
uint8_t largeBuf[400];
MutableByteSpan largeSpan{ largeBuf };
{
// Add new root
err = opCertStore.AddNewTrustedRootCertForFabric(kFabricIndex1, kTestRcacSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
EXPECT_TRUE(opCertStore.HasPendingRootCert());
EXPECT_FALSE(opCertStore.HasPendingNocChain());
// Add NOC chain, with NO ICAC
err = opCertStore.AddNewOpCertsForFabric(kFabricIndex1, kTestNocSpan, ByteSpan{});
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(opCertStore.HasPendingNocChain());
EXPECT_TRUE(opCertStore.HasPendingRootCert());
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
}
// Make sure we get expected pending state before revert
{
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kRcac, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(kTestRcacSpan));
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kIcac, largeSpan);
EXPECT_EQ(err, CHIP_ERROR_NOT_FOUND);
largeSpan = MutableByteSpan{ largeBuf };
err = opCertStore.GetCertificate(kFabricIndex1, CertChainElement::kNoc, largeSpan);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(largeSpan.data_equal(kTestNocSpan));
}
// Revert using RevertPendingOpCertsExceptRoot
opCertStore.RevertPendingOpCertsExceptRoot();
EXPECT_TRUE(opCertStore.HasPendingRootCert());
EXPECT_FALSE(opCertStore.HasPendingNocChain());
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
// Add again, and commit
{
// Add new root: should fail, since it should still be pending
err = opCertStore.AddNewTrustedRootCertForFabric(kFabricIndex1, kTestRcacSpan);
EXPECT_EQ(err, CHIP_ERROR_INCORRECT_STATE);
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
EXPECT_TRUE(opCertStore.HasPendingRootCert());
EXPECT_FALSE(opCertStore.HasPendingNocChain());
// Add NOC chain, with NO ICAC
err = opCertStore.AddNewOpCertsForFabric(kFabricIndex1, kTestNocSpan, ByteSpan{});
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_TRUE(opCertStore.HasPendingNocChain());
EXPECT_TRUE(opCertStore.HasPendingRootCert());
EXPECT_EQ(storageDelegate.GetNumKeys(), 0u); //< Storage count did not yet increase
err = opCertStore.CommitOpCertsForFabric(kFabricIndex1);
EXPECT_EQ(err, CHIP_NO_ERROR);
EXPECT_FALSE(opCertStore.HasPendingRootCert());
EXPECT_FALSE(opCertStore.HasPendingNocChain());
EXPECT_EQ(storageDelegate.GetNumKeys(), 2u); //< We have RCAC, NOC, no ICAC
}
opCertStore.Finish();
}
} // namespace