blob: 3688c9a08f7433cf15a79f3c29d88852b3267e5b [file] [log] [blame]
/**
*
* Copyright (c) 2021-2022 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>
#include <Security/SecKey.h>
#import "MTRCertificates.h"
#import "MTRLogging.h"
#import "NSDataSpanConversion.h"
#include <credentials/CHIPCert.h>
#include <crypto/CHIPCryptoPAL.h>
#include <lib/core/CHIPTLV.h>
#include <lib/core/Optional.h>
#include <lib/support/PersistentStorageMacros.h>
#include <lib/support/SafeInt.h>
#include <lib/support/TimeUtils.h>
using namespace chip;
using namespace TLV;
using namespace Credentials;
using namespace Crypto;
CHIP_ERROR MTROperationalCredentialsDelegate::Init(MTRPersistentStorageDelegateBridge * storage, ChipP256KeypairPtr nocSigner,
NSData * ipk, NSData * rootCert, NSData * _Nullable icaCert)
{
if (storage == nil || ipk == nil || rootCert == nil) {
return CHIP_ERROR_INVALID_ARGUMENT;
}
mStorage = storage;
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 = [NSData dataWithData:rootCert];
if (mRootCert == nil) {
return CHIP_ERROR_NO_MEMORY;
}
if (icaCert != nil) {
mIntermediateCert = [NSData dataWithData:icaCert];
if (mIntermediateCert == nil) {
return CHIP_ERROR_NO_MEMORY;
}
}
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;
}
return GenerateNOC(
*mIssuerKey, (mIntermediateCert != nil) ? mIntermediateCert : mRootCert, nodeId, fabricId, cats, pubkey, noc);
}
CHIP_ERROR MTROperationalCredentialsDelegate::GenerateNOC(P256Keypair & signingKeypair, NSData * signingCertificate, NodeId nodeId,
FabricId fabricId, const CATValues & cats, const P256PublicKey & pubkey, MutableByteSpan & noc)
{
uint32_t validityStart, validityEnd;
if (!ToChipEpochTime(0, validityStart)) {
NSLog(@"Failed in computing certificate validity start date");
return CHIP_ERROR_INTERNAL;
}
if (!ToChipEpochTime(kCertificateValiditySecs, validityEnd)) {
NSLog(@"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)
{
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());
}
VerifyOrReturnError(reader.GetType() == kTLVType_Structure, CHIP_ERROR_WRONG_TLV_TYPE);
VerifyOrReturnError(reader.GetTag() == AnonymousTag(), CHIP_ERROR_UNEXPECTED_TLV_ELEMENT);
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::ToChipEpochTime(uint32_t offset, uint32_t & epoch)
{
NSDate * date = [NSDate dateWithTimeIntervalSinceNow:offset];
NSCalendar * calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents * components = [calendar componentsInTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0] fromDate:date];
uint16_t year = static_cast<uint16_t>([components year]);
uint8_t month = static_cast<uint8_t>([components month]);
uint8_t day = static_cast<uint8_t>([components day]);
uint8_t hour = static_cast<uint8_t>([components hour]);
uint8_t minute = static_cast<uint8_t>([components minute]);
uint8_t second = static_cast<uint8_t>([components second]);
return chip::CalendarToChipEpochTime(year, month, day, hour, minute, second, epoch);
}
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, 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(0, validityStart)) {
NSLog(@"Failed in computing certificate validity start date");
return CHIP_ERROR_INTERNAL;
}
if (!ToChipEpochTime(kCertificateValiditySecs, validityEnd)) {
NSLog(@"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,
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(0, validityStart)) {
NSLog(@"Failed in computing certificate validity start date");
return CHIP_ERROR_INTERNAL;
}
if (!ToChipEpochTime(kCertificateValiditySecs, validityEnd)) {
NSLog(@"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,
NSArray<NSNumber *> * _Nullable caseAuthenticatedTags, 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) {
size_t idx = 0;
for (NSNumber * cat in caseAuthenticatedTags) {
cats.values[idx++] = [cat unsignedIntValue];
}
}
uint8_t nocBuffer[Controller::kMaxCHIPDERCertLength];
MutableByteSpan noc(nocBuffer);
ReturnErrorOnFailure(GenerateNOC(keypairBridge, signingCertificate, node, fabric, cats, pubKey, noc));
*operationalCert = AsData(noc);
return CHIP_NO_ERROR;
}