blob: c86a41490b08c5f9d074dc3eca9cf26bcf36021a [file] [log] [blame]
/*
* Copyright (c) 2024 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.
*
*/
#import "CertificateIssuer.h"
#import "CHIPToolKeypair.h"
#include <lib/support/logging/CHIPLogging.h>
constexpr const uint32_t kIssuerId = 12345678;
@interface CertificateIssuer ()
- (MTRCertificateDERBytes _Nullable)issueOperationalCertificateForNodeID:(NSNumber *)nodeID
fabricID:(NSNumber *)fabricID
publicKey:(SecKeyRef)publicKey
error:(NSError * _Nullable __autoreleasing * _Nonnull)error;
@end
@implementation CertificateIssuer
+ (CertificateIssuer *)sharedInstance
{
static CertificateIssuer * certificateIssuer = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
certificateIssuer = [[CertificateIssuer alloc] init];
});
return certificateIssuer;
}
- (instancetype)init
{
if (!(self = [super init])) {
return nil;
}
_rootCertificate = nil;
_ipk = nil;
_signingKey = nil;
_fabricID = nil;
_nextNodeID = nil;
_shouldSkipAttestationCertificateValidation = NO;
return self;
}
- (void)startWithStorage:(id<MTRStorage>)storage
error:(NSError * _Nullable __autoreleasing * _Nonnull)error
{
__auto_type * signingKey = [[CHIPToolKeypair alloc] init];
__auto_type err = [signingKey createOrLoadKeys:storage];
if (CHIP_NO_ERROR != err) {
*error = [NSError errorWithDomain:@"Error" code:0 userInfo:@{ @"reason" : @"Error creating or loading keys" }];
return;
}
__auto_type * rootCertificate = [MTRCertificates createRootCertificate:signingKey issuerID:@(kIssuerId) fabricID:nil error:error];
if (nil == rootCertificate) {
*error = [NSError errorWithDomain:@"Error" code:0 userInfo:@{ @"reason" : @"Error creating root certificate" }];
return;
}
_rootCertificate = rootCertificate;
_signingKey = signingKey;
_ipk = [signingKey getIPK];
}
- (id<MTRKeypair>)issueOperationalKeypairWithControllerStorage:(ControllerStorage *)storage error:(NSError * _Nullable __autoreleasing * _Nonnull)error
{
__auto_type * keypair = [[CHIPToolKeypair alloc] init];
__auto_type err = [keypair createOrLoadKeys:storage];
if (CHIP_NO_ERROR != err) {
*error = [NSError errorWithDomain:@"Error" code:0 userInfo:@{ @"reason" : @"Error creating or loading keys" }];
return nil;
}
return keypair;
}
- (void)issueOperationalCertificateForRequest:(MTROperationalCSRInfo *)csrInfo
attestationInfo:(MTRDeviceAttestationInfo *)attestationInfo
controller:(MTRDeviceController *)controller
completion:(void (^)(MTROperationalCertificateChain * _Nullable info,
NSError * _Nullable error))completion
{
NSError * error = nil;
if (self.nextNodeID == nil) {
error = [NSError errorWithDomain:@"Error" code:0 userInfo:@{ @"reason" : @"nextNodeID is nil" }];
completion(nil, error);
return;
}
__auto_type * csr = csrInfo.csr;
__auto_type * rawPublicKey = [MTRCertificates publicKeyFromCSR:csr error:&error];
if (error != nil) {
completion(nil, error);
return;
}
NSDictionary * attributes = @{
(__bridge NSString *) kSecAttrKeyType : (__bridge NSString *) kSecAttrKeyTypeECSECPrimeRandom,
(__bridge NSString *) kSecAttrKeyClass : (__bridge NSString *) kSecAttrKeyClassPublic
};
CFErrorRef keyCreationError = NULL;
SecKeyRef publicKey
= SecKeyCreateWithData((__bridge CFDataRef) rawPublicKey, (__bridge CFDictionaryRef) attributes, &keyCreationError);
__auto_type * operationalCert = [self issueOperationalCertificateForNodeID:self.nextNodeID
fabricID:self.fabricID
publicKey:publicKey
error:&error];
// Release no-longer-needed key before we do anything else.
CFRelease(publicKey);
if (error != nil) {
completion(nil, error);
return;
}
__auto_type * certChain = [[MTROperationalCertificateChain alloc] initWithOperationalCertificate:operationalCert
intermediateCertificate:nil
rootCertificate:self.rootCertificate
adminSubject:nil];
completion(certChain, nil);
}
- (MTRCertificateDERBytes _Nullable)issueOperationalCertificateForNodeID:(NSNumber *)nodeID
fabricID:(NSNumber *)fabricID
publicKey:(SecKeyRef)publicKey
error:(NSError * _Nullable __autoreleasing * _Nonnull)error
{
__auto_type * operationalCert = [MTRCertificates createOperationalCertificate:self.signingKey
signingCertificate:self.rootCertificate
operationalPublicKey:publicKey
fabricID:fabricID
nodeID:nodeID
caseAuthenticatedTags:nil
error:error];
return operationalCert;
}
@end