blob: fa280c81e63bab76640c80f5d2bbb7469d7a468e [file] [log] [blame]
/**
*
* Copyright (c) 2021-2023 Project CHIP Authors
*
* 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 <algorithm>
#import "MTROperationalCredentialsDelegate.h"
#import <Security/Security.h>
#import "MTRCertificates.h"
#import "MTRConversion.h"
#import "MTRDeviceController_Internal.h"
#import "MTRLogging_Internal.h"
#import "NSDataSpanConversion.h"
#include <controller/CommissioningDelegate.h>
#include <credentials/CHIPCert.h>
#include <credentials/DeviceAttestationConstructor.h>
#include <credentials/DeviceAttestationVendorReserved.h>
#include <crypto/CHIPCryptoPAL.h>
#include <lib/core/Optional.h>
#include <lib/core/TLV.h>
#include <lib/support/PersistentStorageMacros.h>
#include <platform/LockTracker.h>
using namespace chip;
using namespace TLV;
using namespace Credentials;
using namespace Crypto;
MTROperationalCredentialsDelegate::MTROperationalCredentialsDelegate(MTRDeviceController * deviceController)
: mWeakController(deviceController)
{
}
CHIP_ERROR MTROperationalCredentialsDelegate::Init(
ChipP256KeypairPtr nocSigner, NSData * ipk, NSData * rootCert, NSData * _Nullable icaCert)
{
if (ipk == nil || rootCert == nil) {
return CHIP_ERROR_INVALID_ARGUMENT;
}
mIssuerKey = nocSigner;
if ([ipk length] != mIPK.Length()) {
MTR_LOG_ERROR("MTROperationalCredentialsDelegate::init provided IPK is wrong size");
return CHIP_ERROR_INVALID_ARGUMENT;
}
memcpy(mIPK.Bytes(), [ipk bytes], [ipk length]);
// Make copies of the certificates, just in case the API consumer
// has them as MutableData.
mRootCert = [rootCert copy];
mIntermediateCert = [icaCert copy];
return CHIP_NO_ERROR;
}
CHIP_ERROR MTROperationalCredentialsDelegate::GenerateNOC(
NodeId nodeId, FabricId fabricId, const chip::CATValues & cats, const Crypto::P256PublicKey & pubkey, MutableByteSpan & noc)
{
if (!mIssuerKey) {
return CHIP_ERROR_INCORRECT_STATE;
}
auto * validityPeriod = [[NSDateInterval alloc] initWithStartDate:[NSDate now] endDate:[NSDate distantFuture]];
return GenerateNOC(*mIssuerKey, (mIntermediateCert != nil) ? mIntermediateCert : mRootCert, nodeId, fabricId, cats, pubkey,
validityPeriod, noc);
}
CHIP_ERROR MTROperationalCredentialsDelegate::GenerateNOC(P256Keypair & signingKeypair, NSData * signingCertificate, NodeId nodeId,
FabricId fabricId, const CATValues & cats, const P256PublicKey & pubkey, NSDateInterval * validityPeriod, MutableByteSpan & noc)
{
uint32_t validityStart, validityEnd;
if (!ToChipEpochTime(validityPeriod.startDate, validityStart)) {
MTR_LOG_ERROR("Failed in computing certificate validity start date");
return CHIP_ERROR_INTERNAL;
}
if (!ToChipNotAfterEpochTime(validityPeriod.endDate, validityEnd)) {
MTR_LOG_ERROR("Failed in computing certificate validity end date");
return CHIP_ERROR_INTERNAL;
}
ChipDN signerSubject;
ReturnErrorOnFailure(ExtractSubjectDNFromX509Cert(AsByteSpan(signingCertificate), signerSubject));
ChipDN noc_dn;
ReturnErrorOnFailure(noc_dn.AddAttribute_MatterFabricId(fabricId));
ReturnErrorOnFailure(noc_dn.AddAttribute_MatterNodeId(nodeId));
ReturnErrorOnFailure(noc_dn.AddCATs(cats));
X509CertRequestParams noc_request = { 1, validityStart, validityEnd, noc_dn, signerSubject };
return NewNodeOperationalX509Cert(noc_request, pubkey, signingKeypair, noc);
}
CHIP_ERROR MTROperationalCredentialsDelegate::GenerateNOCChain(const chip::ByteSpan & csrElements, const chip::ByteSpan & csrNonce,
const chip::ByteSpan & attestationSignature, const chip::ByteSpan & attestationChallenge, const chip::ByteSpan & DAC,
const chip::ByteSpan & PAI, chip::Callback::Callback<chip::Controller::OnNOCChainGeneration> * onCompletion)
{
if (mOperationalCertificateIssuer != nil) {
return ExternalGenerateNOCChain(csrElements, csrNonce, attestationSignature, attestationChallenge, DAC, PAI, onCompletion);
} else {
return LocalGenerateNOCChain(csrElements, csrNonce, attestationSignature, attestationChallenge, DAC, PAI, onCompletion);
}
}
CHIP_ERROR MTROperationalCredentialsDelegate::ExternalGenerateNOCChain(const chip::ByteSpan & csrElements,
const chip::ByteSpan & csrNonce, const chip::ByteSpan & csrElementsSignature, const chip::ByteSpan & attestationChallenge,
const chip::ByteSpan & DAC, const chip::ByteSpan & PAI,
chip::Callback::Callback<chip::Controller::OnNOCChainGeneration> * onCompletion)
{
assertChipStackLockedByCurrentThread();
VerifyOrReturnError(mCppCommissioner != nullptr, CHIP_ERROR_INCORRECT_STATE);
MTRDeviceController * strongController = mWeakController;
VerifyOrReturnError(strongController != nil, CHIP_ERROR_INCORRECT_STATE);
mOnNOCCompletionCallback = onCompletion;
auto * csrInfo = [[MTROperationalCSRInfo alloc] initWithCSRNonce:AsData(csrNonce)
csrElementsTLV:AsData(csrElements)
attestationSignature:AsData(csrElementsSignature)];
chip::ByteSpan certificationDeclarationSpan;
chip::ByteSpan attestationNonceSpan;
uint32_t timestampDeconstructed;
chip::ByteSpan firmwareInfoSpan;
chip::Credentials::DeviceAttestationVendorReservedDeconstructor vendorReserved;
chip::Optional<chip::Controller::CommissioningParameters> commissioningParameters
= mCppCommissioner->GetCommissioningParameters();
VerifyOrReturnError(commissioningParameters.HasValue(), CHIP_ERROR_INCORRECT_STATE);
// Attestation Elements, nonce and signature will have a value in Commissioning Params as the CSR needs a signature or else we
// cannot trust it
ReturnErrorOnFailure(
chip::Credentials::DeconstructAttestationElements(commissioningParameters.Value().GetAttestationElements().Value(),
certificationDeclarationSpan, attestationNonceSpan, timestampDeconstructed, firmwareInfoSpan, vendorReserved));
NSData * firmwareInfo = nil;
if (!firmwareInfoSpan.empty()) {
firmwareInfo = AsData(firmwareInfoSpan);
}
MTRDeviceAttestationInfo * attestationInfo = [[MTRDeviceAttestationInfo alloc]
initWithDeviceAttestationChallenge:AsData(attestationChallenge)
nonce:AsData(commissioningParameters.Value().GetAttestationNonce().Value())
elementsTLV:AsData(commissioningParameters.Value().GetAttestationElements().Value())
elementsSignature:AsData(commissioningParameters.Value().GetAttestationSignature().Value())
deviceAttestationCertificate:AsData(DAC)
productAttestationIntermediateCertificate:AsData(PAI)
certificationDeclaration:AsData(certificationDeclarationSpan)
firmwareInfo:firmwareInfo];
MTRDeviceController * __weak weakController = mWeakController;
dispatch_async(mOperationalCertificateIssuerQueue, ^{
[mOperationalCertificateIssuer
issueOperationalCertificateForRequest:csrInfo
attestationInfo:attestationInfo
controller:strongController
completion:^(MTROperationalCertificateChain * _Nullable chain, NSError * _Nullable error) {
MTRDeviceController * strongController = weakController;
if (strongController == nil || !strongController.isRunning) {
// No longer safe to touch "this"
return;
}
this->ExternalNOCChainGenerated(chain, error);
}];
});
return CHIP_NO_ERROR;
}
void MTROperationalCredentialsDelegate::ExternalNOCChainGenerated(
MTROperationalCertificateChain * _Nullable chain, NSError * _Nullable error)
{
// Dispatch will only happen if the controller is still running, which means we
// are safe to touch our members.
[mWeakController
asyncGetCommissionerOnMatterQueue:^(Controller::DeviceCommissioner * commissioner) {
assertChipStackLockedByCurrentThread();
if (mOnNOCCompletionCallback == nullptr) {
return;
}
auto * onCompletion = mOnNOCCompletionCallback;
mOnNOCCompletionCallback = nullptr;
if (mCppCommissioner != commissioner) {
// Quite unexpected!
return;
}
if (chain == nil) {
onCompletion->mCall(onCompletion->mContext, [MTRError errorToCHIPErrorCode:error], ByteSpan(), ByteSpan(),
ByteSpan(), NullOptional, NullOptional);
return;
}
auto commissioningParameters = commissioner->GetCommissioningParameters();
if (!commissioningParameters.HasValue()) {
return;
}
IdentityProtectionKeySpan ipk = commissioningParameters.Value().GetIpk().ValueOr(GetIPK());
Optional<NodeId> adminSubject;
if (chain.adminSubject != nil) {
adminSubject.SetValue(chain.adminSubject.unsignedLongLongValue);
} else {
adminSubject = commissioningParameters.Value().GetAdminSubject();
}
ByteSpan intermediateCertificate;
if (chain.intermediateCertificate != nil) {
intermediateCertificate = AsByteSpan(chain.intermediateCertificate);
}
onCompletion->mCall(onCompletion->mContext, CHIP_NO_ERROR, AsByteSpan(chain.operationalCertificate),
intermediateCertificate, AsByteSpan(chain.rootCertificate), MakeOptional(ipk), adminSubject);
}
// If we can't run the block, we're torn down and should
// just do nothing.
errorHandler:nil];
}
CHIP_ERROR MTROperationalCredentialsDelegate::LocalGenerateNOCChain(const chip::ByteSpan & csrElements,
const chip::ByteSpan & csrNonce, const chip::ByteSpan & attestationSignature, const chip::ByteSpan & attestationChallenge,
const chip::ByteSpan & DAC, const chip::ByteSpan & PAI,
chip::Callback::Callback<chip::Controller::OnNOCChainGeneration> * onCompletion)
{
chip::NodeId assignedId;
if (mNodeIdRequested) {
assignedId = mNextRequestedNodeId;
mNodeIdRequested = false;
} else {
if (mDeviceBeingPaired == chip::kUndefinedNodeId) {
return CHIP_ERROR_INCORRECT_STATE;
}
assignedId = mDeviceBeingPaired;
}
TLVReader reader;
reader.Init(csrElements);
if (reader.GetType() == kTLVType_NotSpecified) {
ReturnErrorOnFailure(reader.Next());
}
ReturnErrorOnFailure(reader.Expect(kTLVType_Structure, AnonymousTag()));
TLVType containerType;
ReturnErrorOnFailure(reader.EnterContainer(containerType));
ReturnErrorOnFailure(reader.Next(kTLVType_ByteString, TLV::ContextTag(1)));
ByteSpan csr(reader.GetReadPoint(), reader.GetLength());
reader.ExitContainer(containerType);
chip::Crypto::P256PublicKey pubkey;
ReturnErrorOnFailure(chip::Crypto::VerifyCertificateSigningRequest(csr.data(), csr.size(), pubkey));
NSMutableData * nocBuffer = [[NSMutableData alloc] initWithLength:chip::Controller::kMaxCHIPDERCertLength];
MutableByteSpan noc((uint8_t *) [nocBuffer mutableBytes], chip::Controller::kMaxCHIPDERCertLength);
ReturnErrorOnFailure(GenerateNOC(assignedId, mNextFabricId, chip::kUndefinedCATs, pubkey, noc));
onCompletion->mCall(onCompletion->mContext, CHIP_NO_ERROR, noc, IntermediateCertSpan(), RootCertSpan(), MakeOptional(GetIPK()),
Optional<NodeId>());
return CHIP_NO_ERROR;
}
ByteSpan MTROperationalCredentialsDelegate::RootCertSpan() const { return AsByteSpan(mRootCert); }
ByteSpan MTROperationalCredentialsDelegate::IntermediateCertSpan() const
{
if (mIntermediateCert == nil) {
return ByteSpan();
}
return AsByteSpan(mIntermediateCert);
}
bool MTROperationalCredentialsDelegate::ToChipNotAfterEpochTime(NSDate * date, uint32_t & epoch)
{
if ([date isEqualToDate:[NSDate distantFuture]]) {
epoch = kNullCertTime;
return true;
}
return ToChipEpochTime(date, epoch);
}
bool MTROperationalCredentialsDelegate::ToChipEpochTime(NSDate * date, uint32_t & epoch)
{
if (DateToMatterEpochSeconds(date, epoch)) {
return true;
}
NSCalendar * calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents * components = [calendar componentsInTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0] fromDate:date];
MTR_LOG_ERROR(
"Year %lu is out of range for Matter epoch time. Please use [NSDate distantFuture] to represent \"never expires\".",
static_cast<unsigned long>(components.year));
return false;
}
namespace {
uint64_t GetIssuerId(NSNumber * _Nullable providedIssuerId)
{
if (providedIssuerId != nil) {
return [providedIssuerId unsignedLongLongValue];
}
return (uint64_t(arc4random()) << 32) | arc4random();
}
} // anonymous namespace
CHIP_ERROR MTROperationalCredentialsDelegate::GenerateRootCertificate(id<MTRKeypair> keypair, NSNumber * _Nullable issuerId,
NSNumber * _Nullable fabricId, NSDateInterval * validityPeriod, NSData * _Nullable __autoreleasing * _Nonnull rootCert)
{
*rootCert = nil;
MTRP256KeypairBridge keypairBridge;
ReturnErrorOnFailure(keypairBridge.Init(keypair));
ChipDN rcac_dn;
ReturnErrorOnFailure(rcac_dn.AddAttribute_MatterRCACId(GetIssuerId(issuerId)));
if (fabricId != nil) {
FabricId fabric = [fabricId unsignedLongLongValue];
VerifyOrReturnError(IsValidFabricId(fabric), CHIP_ERROR_INVALID_ARGUMENT);
ReturnErrorOnFailure(rcac_dn.AddAttribute_MatterFabricId(fabric));
}
uint32_t validityStart, validityEnd;
if (!ToChipEpochTime(validityPeriod.startDate, validityStart)) {
MTR_LOG_ERROR("Failed in computing certificate validity start date");
return CHIP_ERROR_INTERNAL;
}
if (!ToChipNotAfterEpochTime(validityPeriod.endDate, validityEnd)) {
MTR_LOG_ERROR("Failed in computing certificate validity end date");
return CHIP_ERROR_INTERNAL;
}
uint8_t rcacBuffer[Controller::kMaxCHIPDERCertLength];
MutableByteSpan rcac(rcacBuffer);
X509CertRequestParams rcac_request = { 0, validityStart, validityEnd, rcac_dn, rcac_dn };
ReturnErrorOnFailure(NewRootX509Cert(rcac_request, keypairBridge, rcac));
*rootCert = AsData(rcac);
return CHIP_NO_ERROR;
}
CHIP_ERROR MTROperationalCredentialsDelegate::GenerateIntermediateCertificate(id<MTRKeypair> rootKeypair, NSData * rootCertificate,
SecKeyRef intermediatePublicKey, NSNumber * _Nullable issuerId, NSNumber * _Nullable fabricId, NSDateInterval * validityPeriod,
NSData * _Nullable __autoreleasing * _Nonnull intermediateCert)
{
*intermediateCert = nil;
// Verify that the provided root certificate public key matches the root keypair.
if ([MTRCertificates keypair:rootKeypair matchesCertificate:rootCertificate] == NO) {
return CHIP_ERROR_INVALID_ARGUMENT;
}
MTRP256KeypairBridge keypairBridge;
ReturnErrorOnFailure(keypairBridge.Init(rootKeypair));
ByteSpan rcac = AsByteSpan(rootCertificate);
P256PublicKey pubKey;
ReturnErrorOnFailure(MTRP256KeypairBridge::MatterPubKeyFromSecKeyRef(intermediatePublicKey, &pubKey));
ChipDN rcac_dn;
ReturnErrorOnFailure(ExtractSubjectDNFromX509Cert(rcac, rcac_dn));
ChipDN icac_dn;
ReturnErrorOnFailure(icac_dn.AddAttribute_MatterICACId(GetIssuerId(issuerId)));
if (fabricId != nil) {
FabricId fabric = [fabricId unsignedLongLongValue];
VerifyOrReturnError(IsValidFabricId(fabric), CHIP_ERROR_INVALID_ARGUMENT);
ReturnErrorOnFailure(icac_dn.AddAttribute_MatterFabricId(fabric));
}
uint32_t validityStart, validityEnd;
if (!ToChipEpochTime(validityPeriod.startDate, validityStart)) {
MTR_LOG_ERROR("Failed in computing certificate validity start date");
return CHIP_ERROR_INTERNAL;
}
if (!ToChipNotAfterEpochTime(validityPeriod.endDate, validityEnd)) {
MTR_LOG_ERROR("Failed in computing certificate validity end date");
return CHIP_ERROR_INTERNAL;
}
uint8_t icacBuffer[Controller::kMaxCHIPDERCertLength];
MutableByteSpan icac(icacBuffer);
X509CertRequestParams icac_request = { 0, validityStart, validityEnd, icac_dn, rcac_dn };
ReturnErrorOnFailure(NewICAX509Cert(icac_request, pubKey, keypairBridge, icac));
*intermediateCert = AsData(icac);
return CHIP_NO_ERROR;
}
CHIP_ERROR MTROperationalCredentialsDelegate::GenerateOperationalCertificate(id<MTRKeypair> signingKeypair,
NSData * signingCertificate, SecKeyRef operationalPublicKey, NSNumber * fabricId, NSNumber * nodeId,
NSSet<NSNumber *> * _Nullable caseAuthenticatedTags, NSDateInterval * validityPeriod,
NSData * _Nullable __autoreleasing * _Nonnull operationalCert)
{
*operationalCert = nil;
// Verify that the provided signing certificate public key matches the signing keypair.
if ([MTRCertificates keypair:signingKeypair matchesCertificate:signingCertificate] == NO) {
return CHIP_ERROR_INVALID_ARGUMENT;
}
if ([caseAuthenticatedTags count] > kMaxSubjectCATAttributeCount) {
return CHIP_ERROR_INVALID_ARGUMENT;
}
FabricId fabric = [fabricId unsignedLongLongValue];
VerifyOrReturnError(IsValidFabricId(fabric), CHIP_ERROR_INVALID_ARGUMENT);
NodeId node = [nodeId unsignedLongLongValue];
VerifyOrReturnError(IsOperationalNodeId(node), CHIP_ERROR_INVALID_ARGUMENT);
MTRP256KeypairBridge keypairBridge;
ReturnErrorOnFailure(keypairBridge.Init(signingKeypair));
P256PublicKey pubKey;
ReturnErrorOnFailure(MTRP256KeypairBridge::MatterPubKeyFromSecKeyRef(operationalPublicKey, &pubKey));
CATValues cats;
if (caseAuthenticatedTags != nil) {
ReturnErrorOnFailure(SetToCATValues(caseAuthenticatedTags, cats));
}
uint8_t nocBuffer[Controller::kMaxCHIPDERCertLength];
MutableByteSpan noc(nocBuffer);
ReturnErrorOnFailure(GenerateNOC(keypairBridge, signingCertificate, node, fabric, cats, pubKey, validityPeriod, noc));
*operationalCert = AsData(noc);
return CHIP_NO_ERROR;
}