/* * * 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 #include #include #include #include #include #include #include #include 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(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