blob: fb731f9fc98b2ef590bd3926522ff6adfaa57230 [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.
*/
#pragma once
#include <credentials/CHIPCert.h>
#include <crypto/CHIPCryptoPAL.h>
#include <lib/core/CHIPError.h>
#include <lib/core/DataModelTypes.h>
#include <lib/core/Optional.h>
#include <lib/support/CHIPMem.h>
#include <lib/support/CodeUtils.h>
namespace chip {
namespace Credentials {
class TestOnlyLocalCertificateAuthority
{
public:
TestOnlyLocalCertificateAuthority()
{
// Initializing the default start validity to start of 2021.
chip::ASN1::ASN1UniversalTime effectiveTime;
CHIP_ZERO_AT(effectiveTime);
effectiveTime.Year = 2021;
effectiveTime.Month = 1;
effectiveTime.Day = 1;
VerifyOrDie(ASN1ToChipEpochTime(effectiveTime, mNow) == CHIP_NO_ERROR);
}
~TestOnlyLocalCertificateAuthority()
{
mRootKeypair.reset();
mLastRcac.Free();
mLastNoc.Free();
mLastIcac.Free();
}
// Non-copyable
TestOnlyLocalCertificateAuthority(TestOnlyLocalCertificateAuthority const &) = delete;
void operator=(TestOnlyLocalCertificateAuthority const &) = delete;
TestOnlyLocalCertificateAuthority & Init()
{
Crypto::P256SerializedKeypair emptyKeypair;
return Init(emptyKeypair);
}
TestOnlyLocalCertificateAuthority & Init(Crypto::P256SerializedKeypair & rootKeyPair)
{
SuccessOrExit(mCurrentStatus);
mRootKeypair = Platform::MakeUnique<Crypto::P256Keypair>();
VerifyOrExit(mRootKeypair != nullptr, mCurrentStatus = CHIP_ERROR_NO_MEMORY);
if (rootKeyPair.Length() != 0)
{
mCurrentStatus = mRootKeypair->Deserialize(rootKeyPair);
SuccessOrExit(mCurrentStatus);
}
else
{
mRootKeypair->Initialize(Crypto::ECPKeyTarget::ECDSA);
}
mCurrentStatus = GenerateRootCert(*mRootKeypair.get());
SuccessOrExit(mCurrentStatus);
exit:
return *this;
}
TestOnlyLocalCertificateAuthority & SetIncludeIcac(bool includeIcac)
{
mIncludeIcac = includeIcac;
mCurrentStatus = (mCurrentStatus != CHIP_NO_ERROR) ? mCurrentStatus : CHIP_NO_ERROR;
return *this;
}
void ResetIssuer()
{
mCurrentStatus = CHIP_NO_ERROR;
mIncludeIcac = false;
mLastNoc.Free();
mLastIcac.Free();
}
CHIP_ERROR GetStatus() { return mCurrentStatus; }
bool IsSuccess() { return mCurrentStatus == CHIP_NO_ERROR; }
ByteSpan GetNoc() const { return ByteSpan{ mLastNoc.Get(), mLastNoc.AllocatedSize() }; }
ByteSpan GetIcac() const { return mIncludeIcac ? ByteSpan{ mLastIcac.Get(), mLastIcac.AllocatedSize() } : ByteSpan{}; }
ByteSpan GetRcac() const { return ByteSpan{ mLastRcac.Get(), mLastRcac.AllocatedSize() }; }
TestOnlyLocalCertificateAuthority & GenerateNocChain(FabricId fabricId, NodeId nodeId,
const Crypto::P256PublicKey & nocPublicKey)
{
if (mCurrentStatus != CHIP_NO_ERROR)
{
return *this;
}
if (mRootKeypair.get() == nullptr)
{
mCurrentStatus = CHIP_ERROR_NO_SHARED_TRUSTED_ROOT;
return *this;
}
mLastIcac.Free();
mLastNoc.Free();
mCurrentStatus = GenerateCertChainInternal(fabricId, nodeId, nocPublicKey);
return *this;
}
TestOnlyLocalCertificateAuthority & GenerateNocChain(FabricId fabricId, NodeId nodeId, const ByteSpan & csr)
{
if (mCurrentStatus != CHIP_NO_ERROR)
{
return *this;
}
Crypto::P256PublicKey nocPublicKey;
mCurrentStatus = Crypto::VerifyCertificateSigningRequest(csr.data(), csr.size(), nocPublicKey);
if (mCurrentStatus != CHIP_NO_ERROR)
{
return *this;
}
return GenerateNocChain(fabricId, nodeId, nocPublicKey);
}
protected:
CHIP_ERROR GenerateCertChainInternal(FabricId fabricId, NodeId nodeId, const Crypto::P256PublicKey & nocPublicKey)
{
ChipDN rcac_dn;
ChipDN icac_dn;
ChipDN noc_dn;
// Get subject DN of RCAC as our issuer field for ICAC and/or NOC depending on if ICAC is present
ReturnErrorOnFailure(ExtractSubjectDNFromChipCert(ByteSpan{ mLastRcac.Get(), mLastRcac.AllocatedSize() }, rcac_dn));
Crypto::P256Keypair icacKeypair;
ReturnErrorOnFailure(icacKeypair.Initialize(Crypto::ECPKeyTarget::ECDSA)); // Maybe we won't use it, but it's OK
Crypto::P256Keypair * nocIssuerKeypair = mRootKeypair.get();
ChipDN * issuer_dn = &rcac_dn;
// Generate ICAC if needed
if (mIncludeIcac)
{
Platform::ScopedMemoryBufferWithSize<uint8_t> icacDerBuf;
ReturnErrorCodeIf(!icacDerBuf.Alloc(Credentials::kMaxDERCertLength), CHIP_ERROR_NO_MEMORY);
Platform::ScopedMemoryBufferWithSize<uint8_t> icacChipBuf;
ReturnErrorCodeIf(!icacChipBuf.Alloc(Credentials::kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
ReturnErrorOnFailure(icac_dn.AddAttribute_MatterFabricId(fabricId));
ReturnErrorOnFailure(icac_dn.AddAttribute_MatterICACId(1234));
X509CertRequestParams icac_request = { 0, mNow, mNow + mValidity, icac_dn, rcac_dn };
MutableByteSpan icacDerSpan{ icacDerBuf.Get(), icacDerBuf.AllocatedSize() };
ReturnErrorOnFailure(Credentials::NewICAX509Cert(icac_request, icacKeypair.Pubkey(), *mRootKeypair.get(), icacDerSpan));
MutableByteSpan icacChipSpan{ icacChipBuf.Get(), icacChipBuf.AllocatedSize() };
ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(icacDerSpan, icacChipSpan));
ReturnErrorCodeIf(!mLastIcac.Alloc(icacChipSpan.size()), CHIP_ERROR_NO_MEMORY);
memcpy(mLastIcac.Get(), icacChipSpan.data(), icacChipSpan.size());
nocIssuerKeypair = &icacKeypair;
issuer_dn = &icac_dn;
}
// Generate NOC always, either issued from ICAC if present or from RCAC
{
Platform::ScopedMemoryBufferWithSize<uint8_t> nocDerBuf;
ReturnErrorCodeIf(!nocDerBuf.Alloc(Credentials::kMaxDERCertLength), CHIP_ERROR_NO_MEMORY);
Platform::ScopedMemoryBufferWithSize<uint8_t> nocChipBuf;
ReturnErrorCodeIf(!nocChipBuf.Alloc(Credentials::kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
ReturnErrorOnFailure(noc_dn.AddAttribute_MatterFabricId(fabricId));
ReturnErrorOnFailure(noc_dn.AddAttribute_MatterNodeId(nodeId));
X509CertRequestParams noc_request = { 0, mNow, mNow + mValidity, noc_dn, *issuer_dn };
MutableByteSpan nocDerSpan{ nocDerBuf.Get(), nocDerBuf.AllocatedSize() };
ReturnErrorOnFailure(Credentials::NewNodeOperationalX509Cert(noc_request, nocPublicKey, *nocIssuerKeypair, nocDerSpan));
MutableByteSpan nocChipSpan{ nocChipBuf.Get(), nocChipBuf.AllocatedSize() };
ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(nocDerSpan, nocChipSpan));
ReturnErrorCodeIf(!mLastNoc.Alloc(nocChipSpan.size()), CHIP_ERROR_NO_MEMORY);
memcpy(mLastNoc.Get(), nocChipSpan.data(), nocChipSpan.size());
}
return CHIP_NO_ERROR;
}
CHIP_ERROR GenerateRootCert(Crypto::P256Keypair & rootKeyPair)
{
ChipDN rcac_dn;
const uint64_t kIssuerId = 1234567;
Platform::ScopedMemoryBufferWithSize<uint8_t> rcacDerBuf;
VerifyOrReturnError(rcacDerBuf.Alloc(Credentials::kMaxDERCertLength), CHIP_ERROR_NO_MEMORY);
Platform::ScopedMemoryBufferWithSize<uint8_t> rcacChipBuf;
VerifyOrReturnError(rcacChipBuf.Alloc(Credentials::kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
ReturnErrorOnFailure(rcac_dn.AddAttribute_MatterRCACId(kIssuerId));
X509CertRequestParams rcac_request = { 0, mNow, mNow + mValidity, rcac_dn, rcac_dn };
MutableByteSpan rcacDerSpan{ rcacDerBuf.Get(), rcacDerBuf.AllocatedSize() };
ReturnErrorOnFailure(Credentials::NewRootX509Cert(rcac_request, rootKeyPair, rcacDerSpan));
MutableByteSpan rcacChipSpan{ rcacChipBuf.Get(), rcacChipBuf.AllocatedSize() };
ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(rcacDerSpan, rcacChipSpan));
VerifyOrReturnError(mLastRcac.Alloc(rcacChipSpan.size()), CHIP_ERROR_NO_MEMORY);
memcpy(mLastRcac.Get(), rcacChipSpan.data(), rcacChipSpan.size());
return CHIP_NO_ERROR;
}
uint32_t mNow = 0;
// By default, let's set validity to 10 years
uint32_t mValidity = 365 * 24 * 60 * 60 * 10;
CHIP_ERROR mCurrentStatus = CHIP_NO_ERROR;
bool mIncludeIcac = false;
Platform::ScopedMemoryBufferWithSize<uint8_t> mLastNoc;
Platform::ScopedMemoryBufferWithSize<uint8_t> mLastIcac;
Platform::ScopedMemoryBufferWithSize<uint8_t> mLastRcac;
Platform::UniquePtr<Crypto::P256Keypair> mRootKeypair;
};
} // namespace Credentials
} // namespace chip