Skip to content

Commit

Permalink
[darwin-framework-tool] Update CHIPToolKeyPair implementation to not …
Browse files Browse the repository at this point in the history
…use matter sdk specific APIs but native APIS
  • Loading branch information
vivien-apple committed Dec 4, 2024
1 parent 38ad07d commit 6f97678
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 126 deletions.
16 changes: 8 additions & 8 deletions examples/darwin-framework-tool/commands/common/CHIPToolKeypair.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
*/

#import <Matter/Matter.h>
#include <crypto/CHIPCryptoPAL.h>

@interface CHIPToolKeypair : NSObject <MTRKeypair>
- (BOOL)initialize;
- (NSData *)signMessageECDSA_RAW:(NSData *)message;
NS_ASSUME_NONNULL_BEGIN

@interface Keypair : NSObject <MTRKeypair>
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)createKeypairWithStorage:(id)storage error:(NSError * _Nullable __autoreleasing *)error;
- (NSData *)signMessageECDSA_DER:(NSData *)message;
- (SecKeyRef)copyPublicKey;
- (CHIP_ERROR)Serialize:(chip::Crypto::P256SerializedKeypair &)output;
- (CHIP_ERROR)Deserialize:(chip::Crypto::P256SerializedKeypair &)input;
- (CHIP_ERROR)createOrLoadKeys:(id)storage;
- (NSData *)getIPK;

@end

NS_ASSUME_NONNULL_END
256 changes: 151 additions & 105 deletions examples/darwin-framework-tool/commands/common/CHIPToolKeypair.mm
Original file line number Diff line number Diff line change
Expand Up @@ -17,136 +17,191 @@
*/

#import "CHIPToolKeypair.h"
#import <Matter/Matter.h>
#include <credentials/CHIPCert.h>
#include <crypto/CHIPCryptoPAL.h>
#include <lib/asn1/ASN1.h>
#include <stddef.h>

#import "CHIPCommandStorageDelegate.h"
#import "ControllerStorage.h"

#define CHIPPlugin_CAKeyTag "com.apple.matter.commissioner.ca.issuer.id"
#define Public_KeySize "256"
#define CAKeyTag "com.apple.matter.commissioner.ca.issuer.id"
#define KeySize "256"
#define CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES 16

static NSString * const kCHIPToolKeychainLabel = @"Chip Tool Keypair";
static NSString * const kKeychainLabel = @"Chip Tool Keypair";
static NSString * const kOperationalCredentialsIssuerKeypairStorage = @"ChipToolOpCredsCAKey";
static NSString * const kOperationalCredentialsIPK = @"ChipToolOpCredsIPK";

@implementation CHIPToolKeypair {
chip::Crypto::P256Keypair _mKeyPair;
chip::Crypto::P256Keypair _mIssuer;
NSData * _ipk;
uint32_t _mNow;
SecKeyRef _mPublicKey;
}
// Error Descriptions
NSString * const kErrorDomain = @"Error";
NSString * const kErrorFailedToStoreKeypairData = @"Failed to store keypair data in storage.";
NSString * const kErrorInvalidIPKData = @"Invalid IPK data.";
NSString * const kErrorFailedToGenerateIPK = @"Failed to generate random bytes for IPK.";
NSString * const kErrorFailedToStoreIPK = @"Failed to store IPK in storage.";
NSString * const kPublicKeyRetrievalFailureReason = @"Failed to retrieve the public key from the private key. This may occur if the private key was created without storing the corresponding public key in the keychain, or if the system cannot reconstruct the public key.";

@interface Keypair ()
@property (readonly) SecKeyRef privateKey;
@property (readonly) SecKeyRef publicKey;
@property (readonly) NSData * ipk;
@end

- (instancetype)init
@implementation Keypair
+ (instancetype)createKeypairWithStorage:(id)storage error:(NSError * _Nullable __autoreleasing *)error
{
if (self = [super init]) {
_mNow = 0;
__auto_type * keypair = [[self alloc] init];
if (![keypair setupKeys:storage error:error] || ![keypair setupIPK:storage error:error]) {
return nil;
}
return self;
}

- (BOOL)initialize
{
return _mKeyPair.Initialize(chip::Crypto::ECPKeyTarget::ECDSA) == CHIP_NO_ERROR;
return keypair;
}

- (NSData *)signMessageECDSA_RAW:(NSData *)message
- (NSData *)signMessageECDSA_DER:(NSData *)message
{
chip::Crypto::P256ECDSASignature signature;
NSData * out_signature;
CHIP_ERROR signing_error = _mKeyPair.ECDSA_sign_msg((const uint8_t *) [message bytes], (size_t)[message length], signature);
if (signing_error != CHIP_NO_ERROR)
if (!_privateKey) {
NSLog(@"Error: Private key is not available for signing.");
return nil;
out_signature = [NSData dataWithBytes:signature.Bytes() length:signature.Length()];
return out_signature;
}

CFErrorRef cfError = nil;
CFDataRef signatureData = SecKeyCreateSignature(_privateKey,
kSecKeyAlgorithmECDSASignatureMessageX962SHA256,
(__bridge CFDataRef) message,
&cfError);

if (!signatureData) {
NSError * error = (__bridge_transfer NSError *) cfError;
NSLog(@"Error: Failed to sign message: %@", error.localizedDescription);
}

return (__bridge_transfer NSData *) signatureData;
}

- (SecKeyRef)copyPublicKey
{
if (_mPublicKey == nil) {
chip::Crypto::P256PublicKey publicKey = _mKeyPair.Pubkey();
NSData * publicKeyNSData = [NSData dataWithBytes:publicKey.Bytes() length:publicKey.Length()];
NSDictionary * attributes = @{
(__bridge NSString *) kSecAttrKeyClass : (__bridge NSString *) kSecAttrKeyClassPublic,
(NSString *) kSecAttrKeyType : (NSString *) kSecAttrKeyTypeECSECPrimeRandom,
(NSString *) kSecAttrKeySizeInBits : @Public_KeySize,
(NSString *) kSecAttrLabel : kCHIPToolKeychainLabel,
(NSString *) kSecAttrApplicationTag : @CHIPPlugin_CAKeyTag,
};
_mPublicKey = SecKeyCreateWithData((__bridge CFDataRef) publicKeyNSData, (__bridge CFDictionaryRef) attributes, nullptr);
}

if (_mPublicKey) {
CFRetain(_mPublicKey);
return _mPublicKey;
if (_publicKey) {
CFRetain(_publicKey);
return _publicKey;
}

return NULL;
return nil;
}

- (CHIP_ERROR)Deserialize:(chip::Crypto::P256SerializedKeypair &)input
- (BOOL)setupKeys:(id)storage error:(NSError * _Nonnull __autoreleasing *)error
{
return _mKeyPair.Deserialize(input);
__auto_type * keypairData = [self _getValueForKeyWithStorage:storage key:kOperationalCredentialsIssuerKeypairStorage];
return keypairData ? [self loadKeys:keypairData error:error] : [self createKeys:storage error:error];
}

- (CHIP_ERROR)Serialize:(chip::Crypto::P256SerializedKeypair &)output
- (BOOL)loadKeys:(NSData *)keypairData error:(NSError * _Nonnull __autoreleasing *)error
{
return _mKeyPair.Serialize(output);
NSDictionary * const attributes = @{
(NSString *) kSecAttrKeyClass : (NSString *) kSecAttrKeyClassPrivate,
(NSString *) kSecAttrKeyType : (NSString *) kSecAttrKeyTypeECSECPrimeRandom,
(NSString *) kSecAttrKeySizeInBits : @KeySize,
(NSString *) kSecAttrLabel : kKeychainLabel,
(NSString *) kSecAttrApplicationTag : [@CAKeyTag dataUsingEncoding:NSUTF8StringEncoding],
};

CFErrorRef cfError = nil;
__auto_type * privateKey = SecKeyCreateWithData((__bridge CFDataRef) keypairData, (__bridge CFDictionaryRef) attributes, &cfError);
if (!privateKey) {
*error = (__bridge_transfer NSError *) cfError;
return NO;
}

__auto_type * publicKey = SecKeyCopyPublicKey(privateKey);
if (!publicKey) {
CFRelease(privateKey);
*error = [NSError errorWithDomain:kErrorDomain code:0 userInfo:@{ NSLocalizedDescriptionKey : kPublicKeyRetrievalFailureReason }];
return NO;
}

_privateKey = privateKey;
_publicKey = publicKey;
return YES;
}

- (NSData *)getIPK
- (BOOL)createKeys:(id)storage error:(NSError * _Nonnull __autoreleasing *)error
{
return _ipk;
NSDictionary * const attributes = @{
(NSString *) kSecAttrKeyType : (NSString *) kSecAttrKeyTypeECSECPrimeRandom,
(NSString *) kSecAttrKeySizeInBits : @KeySize,
(NSString *) kSecAttrLabel : kKeychainLabel,
(NSString *) kSecAttrApplicationTag : [@CAKeyTag dataUsingEncoding:NSUTF8StringEncoding],
};

CFErrorRef cfError = nil;
__auto_type * privateKey = SecKeyCreateRandomKey((__bridge CFDictionaryRef) attributes, &cfError);
if (!privateKey) {
*error = (__bridge_transfer NSError *) cfError;
return NO;
}

__auto_type * publicKey = SecKeyCopyPublicKey(privateKey);
if (!publicKey) {
CFRelease(privateKey);
*error = [NSError errorWithDomain:kErrorDomain code:0 userInfo:@{ NSLocalizedDescriptionKey : kPublicKeyRetrievalFailureReason }];
return NO;
}

__auto_type * keypairData = (__bridge_transfer NSData *) SecKeyCopyExternalRepresentation(privateKey, &cfError);
if (keypairData == nil) {
CFRelease(privateKey);
CFRelease(publicKey);
*error = (__bridge_transfer NSError *) cfError;
return NO;
}

if (![self _setValueForKeyWithStorage:storage key:kOperationalCredentialsIssuerKeypairStorage value:keypairData]) {
CFRelease(privateKey);
CFRelease(publicKey);
*error = [NSError errorWithDomain:kErrorDomain code:0 userInfo:@{ NSLocalizedDescriptionKey : kErrorFailedToStoreKeypairData }];
return NO;
}

_privateKey = privateKey;
_publicKey = publicKey;
return YES;
}

- (CHIP_ERROR)createOrLoadKeys:(id)storage
- (BOOL)setupIPK:(id)storage error:(NSError * _Nonnull __autoreleasing *)error
{
chip::ASN1::ASN1UniversalTime effectiveTime;
chip::Crypto::P256SerializedKeypair serializedKey;
__auto_type * ipk = [self _getValueForKeyWithStorage:storage key:kOperationalCredentialsIPK];
return ipk ? [self loadIPK:ipk error:error] : [self createIPK:storage error:error];
}

// Initializing the default start validity to start of 2021. The default validity duration is 10 years.
CHIP_ZERO_AT(effectiveTime);
effectiveTime.Year = 2021;
effectiveTime.Month = 1;
effectiveTime.Day = 1;
ReturnErrorOnFailure(chip::Credentials::ASN1ToChipEpochTime(effectiveTime, _mNow));
- (BOOL)loadIPK:(NSData *)ipk error:(NSError * _Nonnull __autoreleasing *)error
{
if (ipk.length != CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES) {
*error = [NSError errorWithDomain:kErrorDomain code:0 userInfo:@{ NSLocalizedDescriptionKey : kErrorInvalidIPKData }];
return NO;
}

__auto_type * value = [self _getValueForKeyWithStorage:storage key:kOperationalCredentialsIssuerKeypairStorage];
__auto_type err = [self initSerializedKeyFromValue:value serializedKey:serializedKey];
_ipk = ipk;
return YES;
}

if (err != CHIP_NO_ERROR) {
// Storage doesn't have an existing keypair. Let's create one and add it to the storage.
if (![self initialize]) {
return CHIP_ERROR_INTERNAL;
}
ReturnErrorOnFailure([self Serialize:serializedKey]);
- (BOOL)createIPK:(id)storage error:(NSError * _Nonnull __autoreleasing *)error
{
uint8_t tempIPK[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES];

NSData * valueData = [NSData dataWithBytes:serializedKey.Bytes() length:serializedKey.Length()];
[self _setValueForKeyWithStorage:storage key:kOperationalCredentialsIssuerKeypairStorage value:valueData];
} else {
ReturnErrorOnFailure([self Deserialize:serializedKey]);
if (errSecSuccess != SecRandomCopyBytes(kSecRandomDefault, (sizeof tempIPK) / (sizeof tempIPK[0]), &tempIPK[0])) {
*error = [NSError errorWithDomain:kErrorDomain code:0 userInfo:@{ NSLocalizedDescriptionKey : kErrorFailedToGenerateIPK }];
return NO;
}

NSData * ipk = [self _getValueForKeyWithStorage:storage key:kOperationalCredentialsIPK];
if (ipk == nil) {
err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND;
__auto_type * ipk = [NSData dataWithBytes:tempIPK length:(sizeof tempIPK)];
if (![self _setValueForKeyWithStorage:storage key:kOperationalCredentialsIPK value:ipk]) {
*error = [NSError errorWithDomain:kErrorDomain code:0 userInfo:@{ NSLocalizedDescriptionKey : kErrorFailedToStoreIPK }];
return NO;
}
if (err != CHIP_NO_ERROR) {
uint8_t tempIPK[chip::Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES];

ReturnLogErrorOnFailure(chip::Crypto::DRBG_get_bytes(tempIPK, sizeof(tempIPK)));

_ipk = [NSData dataWithBytes:tempIPK length:sizeof(tempIPK)];
[self _setValueForKeyWithStorage:storage key:kOperationalCredentialsIPK value:_ipk];
} else {
_ipk = ipk;
}
_ipk = ipk;
return YES;
}

return CHIP_NO_ERROR;
- (NSData *)getIPK
{
return _ipk;
}

- (NSData *)_getValueForKeyWithStorage:(id)storage key:(NSString *)key
Expand All @@ -159,34 +214,25 @@ - (NSData *)_getValueForKeyWithStorage:(id)storage key:(NSString *)key
return nil;
}

- (void)_setValueForKeyWithStorage:(id)storage key:(NSString *)key value:(NSData *)value
- (BOOL)_setValueForKeyWithStorage:(id)storage key:(NSString *)key value:(NSData *)value
{
if ([storage isKindOfClass:[CHIPToolPersistentStorageDelegate class]]) {
[storage setStorageData:value forKey:key];
return [storage setStorageData:value forKey:key];
} else if ([storage isKindOfClass:[ControllerStorage class]]) {
[storage storeValue:value forKey:key];
return YES;
}
return NO;
}

- (CHIP_ERROR)initSerializedKeyFromValue:(NSData *)value serializedKey:(chip::Crypto::P256SerializedKeypair &)serializedKey
- (void)dealloc
{
if (value == nil) {
return CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND;
if (_privateKey) {
CFRelease(_privateKey);
}

if (serializedKey.Capacity() < [value length]) {
return CHIP_ERROR_BUFFER_TOO_SMALL;
}

memcpy(serializedKey.Bytes(), [value bytes], [value length]);
serializedKey.SetLength([value length]);
return CHIP_NO_ERROR;
}

- (void)dealloc
{
if (_mPublicKey) {
CFRelease(_mPublicKey);
if (_publicKey) {
CFRelease(_publicKey);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,13 @@ - (instancetype)init
- (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" }];
__auto_type * signingKey = [Keypair createKeypairWithStorage:storage error:error];
if (!signingKey) {
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" }];
if (!rootCertificate) {
return;
}

Expand All @@ -82,15 +78,12 @@ - (void)startWithStorage:(id<MTRStorage>)storage

- (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" }];
__auto_type * signingKey = [Keypair createKeypairWithStorage:storage error:error];
if (!signingKey) {
return nil;
}

return keypair;
return signingKey;
}

- (void)issueOperationalCertificateForRequest:(MTROperationalCSRInfo *)csrInfo
Expand Down

0 comments on commit 6f97678

Please sign in to comment.