Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Darwin framework API for creating operational certificates. #18510

Merged
merged 1 commit into from
May 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,20 @@ class CHIPOperationalCredentialsDelegate : public chip::Controller::OperationalC
SecKeyRef intermediatePublicKey, NSNumber * _Nullable issuerId, NSNumber * _Nullable fabricId,
NSData * _Nullable __autoreleasing * _Nonnull intermediateCert);

// Generate an operational DER-encoded X.509 certificate for the given
// signing certificate and operational public key, using the given fabric
// id, node id, and CATs.
static CHIP_ERROR GenerateOperationalCertificate(id<CHIPKeypair> signingKeypair, NSData * signingCertificate,
SecKeyRef operationalPublicKey, NSNumber * fabricId, NSNumber * nodeId,
NSArray<NSNumber *> * _Nullable caseAuthenticatedTags, NSData * _Nullable __autoreleasing * _Nonnull operationalCert);

private:
static bool ToChipEpochTime(uint32_t offset, uint32_t & epoch);

static CHIP_ERROR GenerateNOC(chip::Crypto::P256Keypair & signingKeypair, NSData * signingCertificate, chip::NodeId nodeId,
chip::FabricId fabricId, const chip::CATValues & cats, const chip::Crypto::P256PublicKey & pubkey,
chip::MutableByteSpan & noc);

ChipP256KeypairPtr mIssuerKey;

chip::Crypto::AesCcm128Key mIPK;
Expand Down
66 changes: 60 additions & 6 deletions src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@

CHIP_ERROR CHIPOperationalCredentialsDelegate::GenerateNOC(
NodeId nodeId, FabricId fabricId, const chip::CATValues & cats, const Crypto::P256PublicKey & pubkey, MutableByteSpan & noc)
{
return GenerateNOC(
*mIssuerKey, (mIntermediateCert != nil) ? mIntermediateCert : mRootCert, nodeId, fabricId, cats, pubkey, noc);
}

CHIP_ERROR CHIPOperationalCredentialsDelegate::GenerateNOC(P256Keypair & signingKeypair, NSData * signingCertificate, NodeId nodeId,
FabricId fabricId, const CATValues & cats, const P256PublicKey & pubkey, MutableByteSpan & noc)
{
uint32_t validityStart, validityEnd;

Expand All @@ -90,16 +97,15 @@
}

ChipDN signerSubject;
NSData * signer = (mIntermediateCert != nil) ? mIntermediateCert : mRootCert;
ReturnErrorOnFailure(ExtractSubjectDNFromX509Cert(AsByteSpan(signer), 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, *mIssuerKey, noc);
return NewNodeOperationalX509Cert(noc_request, pubkey, signingKeypair, noc);
}

CHIP_ERROR CHIPOperationalCredentialsDelegate::GenerateNOCChain(const chip::ByteSpan & csrElements, const chip::ByteSpan & csrNonce,
Expand Down Expand Up @@ -199,7 +205,9 @@ uint64_t GetIssuerId(NSNumber * _Nullable providedIssuerId)
ReturnErrorOnFailure(rcac_dn.AddAttribute_MatterRCACId(GetIssuerId(issuerId)));

if (fabricId != nil) {
ReturnErrorOnFailure(rcac_dn.AddAttribute_MatterFabricId([fabricId unsignedLongLongValue]));
FabricId fabric = [fabricId unsignedLongLongValue];
VerifyOrReturnError(fabric != kUndefinedFabricId, CHIP_ERROR_INVALID_ARGUMENT);
ReturnErrorOnFailure(rcac_dn.AddAttribute_MatterFabricId(fabric));
}

uint32_t validityStart, validityEnd;
Expand Down Expand Up @@ -228,7 +236,7 @@ uint64_t GetIssuerId(NSNumber * _Nullable providedIssuerId)
{
*intermediateCert = nil;

// Verify that the provided certificate public key matches the root keypair.
// Verify that the provided root certificate public key matches the root keypair.
if ([MTRCertificates keypair:rootKeypair matchesCertificate:rootCertificate] == NO) {
return CHIP_ERROR_INVALID_ARGUMENT;
}
Expand All @@ -248,7 +256,9 @@ uint64_t GetIssuerId(NSNumber * _Nullable providedIssuerId)
ChipDN icac_dn;
ReturnErrorOnFailure(icac_dn.AddAttribute_MatterICACId(GetIssuerId(issuerId)));
if (fabricId != nil) {
ReturnErrorOnFailure(icac_dn.AddAttribute_MatterFabricId([fabricId unsignedLongLongValue]));
FabricId fabric = [fabricId unsignedLongLongValue];
VerifyOrReturnError(fabric != kUndefinedFabricId, CHIP_ERROR_INVALID_ARGUMENT);
ReturnErrorOnFailure(icac_dn.AddAttribute_MatterFabricId(fabric));
}

uint32_t validityStart, validityEnd;
Expand All @@ -270,3 +280,47 @@ uint64_t GetIssuerId(NSNumber * _Nullable providedIssuerId)
*intermediateCert = AsData(icac);
return CHIP_NO_ERROR;
}

CHIP_ERROR CHIPOperationalCredentialsDelegate::GenerateOperationalCertificate(id<CHIPKeypair> 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(fabric != kUndefinedFabricId, CHIP_ERROR_INVALID_ARGUMENT);

NodeId node = [nodeId unsignedLongLongValue];
VerifyOrReturnError(IsOperationalNodeId(node), CHIP_ERROR_INVALID_ARGUMENT);

CHIPP256KeypairBridge keypairBridge;
ReturnErrorOnFailure(keypairBridge.Init(signingKeypair));
CHIPP256KeypairNativeBridge nativeSigningKeypair(keypairBridge);

P256PublicKey pubKey;
ReturnErrorOnFailure(CHIPP256KeypairBridge::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(nativeSigningKeypair, signingCertificate, node, fabric, cats, pubKey, noc));

*operationalCert = AsData(noc);
return CHIP_NO_ERROR;
}
33 changes: 31 additions & 2 deletions src/darwin/Framework/CHIP/MTRCertificates.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ NS_ASSUME_NONNULL_BEGIN
* issuer id is used.
*
* If fabricId is not nil, it will be included in the subject DN of the
* certificate.
* certificate. In this case it must be a valid Matter fabric id.
*
* On failure returns nil and if "error" is not null sets *error to the relevant
* error.
Expand All @@ -54,7 +54,7 @@ NS_ASSUME_NONNULL_BEGIN
* issuer id is used.
*
* If fabricId is not nil, it will be included in the subject DN of the
* certificate.
* certificate. In this case it must be a valid Matter fabric id.
*
* On failure returns nil and if "error" is not null sets *error to the relevant
* error.
Expand All @@ -66,6 +66,35 @@ NS_ASSUME_NONNULL_BEGIN
fabricId:(nullable NSNumber *)fabricId
error:(NSError * __autoreleasing _Nullable * _Nullable)error;

/**
* Generate an X.509 DER encoded certificate that has the
* right fields to be a valid Matter operational certificate.
*
* signingKeypair and signingCertificate are the root or intermediate that is
* signing the operational certificate.
*
* nodeId and fabricId are expected to be 64-bit unsigned integers.
*
* nodeId must be a valid Matter operational node id.
*
* fabricId must be a valid Matter fabric id.
*
* caseAuthenticatedTags may be nil to indicate no CASE Authenticated Tags
* should be used. If caseAuthenticatedTags is not nil, it must have length at
* most 3 and the values in the array are expected to be 32-bit unsigned Case
* Authenticated Tag values.
*
* On failure returns nil and if "error" is not null sets *error to the relevant
* error.
*/
+ (nullable NSData *)generateOperationalCertificate:(id<CHIPKeypair>)signingKeypair
signingCertificate:(NSData *)signingCertificate
operationalPublicKey:(SecKeyRef)operationalPublicKey
fabricId:(NSNumber *)fabricId
nodeId:(NSNumber *)nodeId
caseAuthenticatedTags:(NSArray<NSNumber *> * _Nullable)caseAuthenticatedTags
error:(NSError * __autoreleasing _Nullable * _Nullable)error;

/**
* Check whether the given keypair's public key matches the given certificate's
* public key. The certificate is expected to be an X.509 DER encoded
Expand Down
26 changes: 26 additions & 0 deletions src/darwin/Framework/CHIP/MTRCertificates.mm
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,32 @@ + (nullable NSData *)generateIntermediateCertificate:(id<CHIPKeypair>)rootKeypai
return intermediate;
}

+ (nullable NSData *)generateOperationalCertificate:(id<CHIPKeypair>)signingKeypair
signingCertificate:(NSData *)signingCertificate
operationalPublicKey:(SecKeyRef)operationalPublicKey
fabricId:(NSNumber *)fabricId
nodeId:(NSNumber *)nodeId
caseAuthenticatedTags:(NSArray<NSNumber *> * _Nullable)caseAuthenticatedTags
error:(NSError * __autoreleasing _Nullable * _Nullable)error
{
NSLog(@"Generating operational certificate");

AutoPlatformMemory platformMemory;

NSData * opcert = nil;
CHIP_ERROR err = CHIPOperationalCredentialsDelegate::GenerateOperationalCertificate(
signingKeypair, signingCertificate, operationalPublicKey, fabricId, nodeId, caseAuthenticatedTags, &opcert);
if (error) {
*error = [CHIPError errorForCHIPErrorCode:err];
}

if (err != CHIP_NO_ERROR) {
NSLog(@"Generating operational certificate failed: %s", ErrorStr(err));
}

return opcert;
}

+ (BOOL)keypair:(id<CHIPKeypair>)keypair matchesCertificate:(NSData *)certificate
{
P256PublicKey keypairPubKey;
Expand Down
136 changes: 136 additions & 0 deletions src/darwin/Framework/CHIPTests/MatterCertificateTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,140 @@ - (void)testGenerateIntermediateCert
XCTAssertNotNil(intermediateCert);
}

- (void)testGenerateOperationalCertNoIntermediate
{
__auto_type * rootKeys = [[CHIPTestKeys alloc] init];
XCTAssertNotNil(rootKeys);

__auto_type * rootCert = [MTRCertificates generateRootCertificate:rootKeys issuerId:nil fabricId:nil error:nil];
XCTAssertNotNil(rootCert);

__auto_type * operationalKeys = [[CHIPTestKeys alloc] init];
XCTAssertNotNil(operationalKeys);

__auto_type * cats = [[NSMutableArray alloc] initWithCapacity:3];
[cats addObject:@1];
[cats addObject:@2];
[cats addObject:@3];

__auto_type * operationalCert = [MTRCertificates generateOperationalCertificate:rootKeys
signingCertificate:rootCert
operationalPublicKey:operationalKeys.pubkey
fabricId:@1
nodeId:@1
caseAuthenticatedTags:cats
error:nil];
XCTAssertNotNil(operationalCert);
}

- (void)testGenerateOperationalCertWithIntermediate
{
__auto_type * rootKeys = [[CHIPTestKeys alloc] init];
XCTAssertNotNil(rootKeys);

__auto_type * rootCert = [MTRCertificates generateRootCertificate:rootKeys issuerId:nil fabricId:nil error:nil];
XCTAssertNotNil(rootCert);

__auto_type * intermediateKeys = [[CHIPTestKeys alloc] init];
XCTAssertNotNil(intermediateKeys);

__auto_type * intermediateCert = [MTRCertificates generateIntermediateCertificate:rootKeys
rootCertificate:rootCert
intermediatePublicKey:intermediateKeys.pubkey
issuerId:nil
fabricId:nil
error:nil];
XCTAssertNotNil(intermediateCert);

__auto_type * operationalKeys = [[CHIPTestKeys alloc] init];
XCTAssertNotNil(operationalKeys);

__auto_type * operationalCert = [MTRCertificates generateOperationalCertificate:intermediateKeys
signingCertificate:intermediateCert
operationalPublicKey:operationalKeys.pubkey
fabricId:@1
nodeId:@1
caseAuthenticatedTags:nil
error:nil];
XCTAssertNotNil(operationalCert);
}

- (void)testGenerateOperationalCertErrorCases
{
__auto_type * rootKeys = [[CHIPTestKeys alloc] init];
XCTAssertNotNil(rootKeys);

__auto_type * rootCert = [MTRCertificates generateRootCertificate:rootKeys issuerId:nil fabricId:nil error:nil];
XCTAssertNotNil(rootCert);

__auto_type * operationalKeys = [[CHIPTestKeys alloc] init];
XCTAssertNotNil(operationalKeys);

__auto_type * cats = [[NSMutableArray alloc] initWithCapacity:4];
[cats addObject:@1];
[cats addObject:@2];
[cats addObject:@3];
[cats addObject:@4];

// Check basic case works
__auto_type * operationalCert = [MTRCertificates generateOperationalCertificate:rootKeys
signingCertificate:rootCert
operationalPublicKey:operationalKeys.pubkey
fabricId:@1
nodeId:@1
caseAuthenticatedTags:nil
error:nil];
XCTAssertNotNil(operationalCert);

// CATs too long
operationalCert = [MTRCertificates generateOperationalCertificate:rootKeys
signingCertificate:rootCert
operationalPublicKey:operationalKeys.pubkey
fabricId:@1
nodeId:@1
caseAuthenticatedTags:cats
error:nil];
XCTAssertNil(operationalCert);

// Signing key mismatch
operationalCert = [MTRCertificates generateOperationalCertificate:operationalKeys
signingCertificate:rootCert
operationalPublicKey:operationalKeys.pubkey
fabricId:@1
nodeId:@1
caseAuthenticatedTags:nil
error:nil];
XCTAssertNil(operationalCert);

// Invalid fabric id
operationalCert = [MTRCertificates generateOperationalCertificate:rootKeys
signingCertificate:rootCert
operationalPublicKey:operationalKeys.pubkey
fabricId:@0
nodeId:@1
caseAuthenticatedTags:nil
error:nil];
XCTAssertNil(operationalCert);

// Undefined node id
operationalCert = [MTRCertificates generateOperationalCertificate:rootKeys
signingCertificate:rootCert
operationalPublicKey:operationalKeys.pubkey
fabricId:@1
nodeId:@0
caseAuthenticatedTags:nil
error:nil];
XCTAssertNil(operationalCert);

// Non-operational node id
operationalCert = [MTRCertificates generateOperationalCertificate:rootKeys
signingCertificate:rootCert
operationalPublicKey:operationalKeys.pubkey
fabricId:@1
nodeId:@(0xFFFFFFFFFFFFFFFFLLU)
caseAuthenticatedTags:nil
error:nil];
XCTAssertNil(operationalCert);
}

@end