Skip to content

Commit 1726524

Browse files
bzbarsky-applepull[bot]
authored andcommitted
Add Darwin framework API for creating operational certificates. (#18510)
Will be useful for writing tests, if nothing else.
1 parent a2f7ee3 commit 1726524

5 files changed

+264
-8
lines changed

src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.h

+11
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,20 @@ class CHIPOperationalCredentialsDelegate : public chip::Controller::OperationalC
8484
SecKeyRef intermediatePublicKey, NSNumber * _Nullable issuerId, NSNumber * _Nullable fabricId,
8585
NSData * _Nullable __autoreleasing * _Nonnull intermediateCert);
8686

87+
// Generate an operational DER-encoded X.509 certificate for the given
88+
// signing certificate and operational public key, using the given fabric
89+
// id, node id, and CATs.
90+
static CHIP_ERROR GenerateOperationalCertificate(id<CHIPKeypair> signingKeypair, NSData * signingCertificate,
91+
SecKeyRef operationalPublicKey, NSNumber * fabricId, NSNumber * nodeId,
92+
NSArray<NSNumber *> * _Nullable caseAuthenticatedTags, NSData * _Nullable __autoreleasing * _Nonnull operationalCert);
93+
8794
private:
8895
static bool ToChipEpochTime(uint32_t offset, uint32_t & epoch);
8996

97+
static CHIP_ERROR GenerateNOC(chip::Crypto::P256Keypair & signingKeypair, NSData * signingCertificate, chip::NodeId nodeId,
98+
chip::FabricId fabricId, const chip::CATValues & cats, const chip::Crypto::P256PublicKey & pubkey,
99+
chip::MutableByteSpan & noc);
100+
90101
ChipP256KeypairPtr mIssuerKey;
91102

92103
chip::Crypto::AesCcm128Key mIPK;

src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm

+60-6
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@
7676

7777
CHIP_ERROR CHIPOperationalCredentialsDelegate::GenerateNOC(
7878
NodeId nodeId, FabricId fabricId, const chip::CATValues & cats, const Crypto::P256PublicKey & pubkey, MutableByteSpan & noc)
79+
{
80+
return GenerateNOC(
81+
*mIssuerKey, (mIntermediateCert != nil) ? mIntermediateCert : mRootCert, nodeId, fabricId, cats, pubkey, noc);
82+
}
83+
84+
CHIP_ERROR CHIPOperationalCredentialsDelegate::GenerateNOC(P256Keypair & signingKeypair, NSData * signingCertificate, NodeId nodeId,
85+
FabricId fabricId, const CATValues & cats, const P256PublicKey & pubkey, MutableByteSpan & noc)
7986
{
8087
uint32_t validityStart, validityEnd;
8188

@@ -90,16 +97,15 @@
9097
}
9198

9299
ChipDN signerSubject;
93-
NSData * signer = (mIntermediateCert != nil) ? mIntermediateCert : mRootCert;
94-
ReturnErrorOnFailure(ExtractSubjectDNFromX509Cert(AsByteSpan(signer), signerSubject));
100+
ReturnErrorOnFailure(ExtractSubjectDNFromX509Cert(AsByteSpan(signingCertificate), signerSubject));
95101

96102
ChipDN noc_dn;
97103
ReturnErrorOnFailure(noc_dn.AddAttribute_MatterFabricId(fabricId));
98104
ReturnErrorOnFailure(noc_dn.AddAttribute_MatterNodeId(nodeId));
99105
ReturnErrorOnFailure(noc_dn.AddCATs(cats));
100106

101107
X509CertRequestParams noc_request = { 1, validityStart, validityEnd, noc_dn, signerSubject };
102-
return NewNodeOperationalX509Cert(noc_request, pubkey, *mIssuerKey, noc);
108+
return NewNodeOperationalX509Cert(noc_request, pubkey, signingKeypair, noc);
103109
}
104110

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

201207
if (fabricId != nil) {
202-
ReturnErrorOnFailure(rcac_dn.AddAttribute_MatterFabricId([fabricId unsignedLongLongValue]));
208+
FabricId fabric = [fabricId unsignedLongLongValue];
209+
VerifyOrReturnError(fabric != kUndefinedFabricId, CHIP_ERROR_INVALID_ARGUMENT);
210+
ReturnErrorOnFailure(rcac_dn.AddAttribute_MatterFabricId(fabric));
203211
}
204212

205213
uint32_t validityStart, validityEnd;
@@ -228,7 +236,7 @@ uint64_t GetIssuerId(NSNumber * _Nullable providedIssuerId)
228236
{
229237
*intermediateCert = nil;
230238

231-
// Verify that the provided certificate public key matches the root keypair.
239+
// Verify that the provided root certificate public key matches the root keypair.
232240
if ([MTRCertificates keypair:rootKeypair matchesCertificate:rootCertificate] == NO) {
233241
return CHIP_ERROR_INVALID_ARGUMENT;
234242
}
@@ -248,7 +256,9 @@ uint64_t GetIssuerId(NSNumber * _Nullable providedIssuerId)
248256
ChipDN icac_dn;
249257
ReturnErrorOnFailure(icac_dn.AddAttribute_MatterICACId(GetIssuerId(issuerId)));
250258
if (fabricId != nil) {
251-
ReturnErrorOnFailure(icac_dn.AddAttribute_MatterFabricId([fabricId unsignedLongLongValue]));
259+
FabricId fabric = [fabricId unsignedLongLongValue];
260+
VerifyOrReturnError(fabric != kUndefinedFabricId, CHIP_ERROR_INVALID_ARGUMENT);
261+
ReturnErrorOnFailure(icac_dn.AddAttribute_MatterFabricId(fabric));
252262
}
253263

254264
uint32_t validityStart, validityEnd;
@@ -270,3 +280,47 @@ uint64_t GetIssuerId(NSNumber * _Nullable providedIssuerId)
270280
*intermediateCert = AsData(icac);
271281
return CHIP_NO_ERROR;
272282
}
283+
284+
CHIP_ERROR CHIPOperationalCredentialsDelegate::GenerateOperationalCertificate(id<CHIPKeypair> signingKeypair,
285+
NSData * signingCertificate, SecKeyRef operationalPublicKey, NSNumber * fabricId, NSNumber * nodeId,
286+
NSArray<NSNumber *> * _Nullable caseAuthenticatedTags, NSData * _Nullable __autoreleasing * _Nonnull operationalCert)
287+
{
288+
*operationalCert = nil;
289+
290+
// Verify that the provided signing certificate public key matches the signing keypair.
291+
if ([MTRCertificates keypair:signingKeypair matchesCertificate:signingCertificate] == NO) {
292+
return CHIP_ERROR_INVALID_ARGUMENT;
293+
}
294+
295+
if ([caseAuthenticatedTags count] > kMaxSubjectCATAttributeCount) {
296+
return CHIP_ERROR_INVALID_ARGUMENT;
297+
}
298+
299+
FabricId fabric = [fabricId unsignedLongLongValue];
300+
VerifyOrReturnError(fabric != kUndefinedFabricId, CHIP_ERROR_INVALID_ARGUMENT);
301+
302+
NodeId node = [nodeId unsignedLongLongValue];
303+
VerifyOrReturnError(IsOperationalNodeId(node), CHIP_ERROR_INVALID_ARGUMENT);
304+
305+
CHIPP256KeypairBridge keypairBridge;
306+
ReturnErrorOnFailure(keypairBridge.Init(signingKeypair));
307+
CHIPP256KeypairNativeBridge nativeSigningKeypair(keypairBridge);
308+
309+
P256PublicKey pubKey;
310+
ReturnErrorOnFailure(CHIPP256KeypairBridge::MatterPubKeyFromSecKeyRef(operationalPublicKey, &pubKey));
311+
312+
CATValues cats;
313+
if (caseAuthenticatedTags != nil) {
314+
size_t idx = 0;
315+
for (NSNumber * cat in caseAuthenticatedTags) {
316+
cats.values[idx++] = [cat unsignedIntValue];
317+
}
318+
}
319+
320+
uint8_t nocBuffer[Controller::kMaxCHIPDERCertLength];
321+
MutableByteSpan noc(nocBuffer);
322+
ReturnErrorOnFailure(GenerateNOC(nativeSigningKeypair, signingCertificate, node, fabric, cats, pubKey, noc));
323+
324+
*operationalCert = AsData(noc);
325+
return CHIP_NO_ERROR;
326+
}

src/darwin/Framework/CHIP/MTRCertificates.h

+31-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ NS_ASSUME_NONNULL_BEGIN
3636
* issuer id is used.
3737
*
3838
* If fabricId is not nil, it will be included in the subject DN of the
39-
* certificate.
39+
* certificate. In this case it must be a valid Matter fabric id.
4040
*
4141
* On failure returns nil and if "error" is not null sets *error to the relevant
4242
* error.
@@ -54,7 +54,7 @@ NS_ASSUME_NONNULL_BEGIN
5454
* issuer id is used.
5555
*
5656
* If fabricId is not nil, it will be included in the subject DN of the
57-
* certificate.
57+
* certificate. In this case it must be a valid Matter fabric id.
5858
*
5959
* On failure returns nil and if "error" is not null sets *error to the relevant
6060
* error.
@@ -66,6 +66,35 @@ NS_ASSUME_NONNULL_BEGIN
6666
fabricId:(nullable NSNumber *)fabricId
6767
error:(NSError * __autoreleasing _Nullable * _Nullable)error;
6868

69+
/**
70+
* Generate an X.509 DER encoded certificate that has the
71+
* right fields to be a valid Matter operational certificate.
72+
*
73+
* signingKeypair and signingCertificate are the root or intermediate that is
74+
* signing the operational certificate.
75+
*
76+
* nodeId and fabricId are expected to be 64-bit unsigned integers.
77+
*
78+
* nodeId must be a valid Matter operational node id.
79+
*
80+
* fabricId must be a valid Matter fabric id.
81+
*
82+
* caseAuthenticatedTags may be nil to indicate no CASE Authenticated Tags
83+
* should be used. If caseAuthenticatedTags is not nil, it must have length at
84+
* most 3 and the values in the array are expected to be 32-bit unsigned Case
85+
* Authenticated Tag values.
86+
*
87+
* On failure returns nil and if "error" is not null sets *error to the relevant
88+
* error.
89+
*/
90+
+ (nullable NSData *)generateOperationalCertificate:(id<CHIPKeypair>)signingKeypair
91+
signingCertificate:(NSData *)signingCertificate
92+
operationalPublicKey:(SecKeyRef)operationalPublicKey
93+
fabricId:(NSNumber *)fabricId
94+
nodeId:(NSNumber *)nodeId
95+
caseAuthenticatedTags:(NSArray<NSNumber *> * _Nullable)caseAuthenticatedTags
96+
error:(NSError * __autoreleasing _Nullable * _Nullable)error;
97+
6998
/**
7099
* Check whether the given keypair's public key matches the given certificate's
71100
* public key. The certificate is expected to be an X.509 DER encoded

src/darwin/Framework/CHIP/MTRCertificates.mm

+26
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,32 @@ + (nullable NSData *)generateIntermediateCertificate:(id<CHIPKeypair>)rootKeypai
8686
return intermediate;
8787
}
8888

89+
+ (nullable NSData *)generateOperationalCertificate:(id<CHIPKeypair>)signingKeypair
90+
signingCertificate:(NSData *)signingCertificate
91+
operationalPublicKey:(SecKeyRef)operationalPublicKey
92+
fabricId:(NSNumber *)fabricId
93+
nodeId:(NSNumber *)nodeId
94+
caseAuthenticatedTags:(NSArray<NSNumber *> * _Nullable)caseAuthenticatedTags
95+
error:(NSError * __autoreleasing _Nullable * _Nullable)error
96+
{
97+
NSLog(@"Generating operational certificate");
98+
99+
AutoPlatformMemory platformMemory;
100+
101+
NSData * opcert = nil;
102+
CHIP_ERROR err = CHIPOperationalCredentialsDelegate::GenerateOperationalCertificate(
103+
signingKeypair, signingCertificate, operationalPublicKey, fabricId, nodeId, caseAuthenticatedTags, &opcert);
104+
if (error) {
105+
*error = [CHIPError errorForCHIPErrorCode:err];
106+
}
107+
108+
if (err != CHIP_NO_ERROR) {
109+
NSLog(@"Generating operational certificate failed: %s", ErrorStr(err));
110+
}
111+
112+
return opcert;
113+
}
114+
89115
+ (BOOL)keypair:(id<CHIPKeypair>)keypair matchesCertificate:(NSData *)certificate
90116
{
91117
P256PublicKey keypairPubKey;

src/darwin/Framework/CHIPTests/MatterCertificateTests.m

+136
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,140 @@ - (void)testGenerateIntermediateCert
5656
XCTAssertNotNil(intermediateCert);
5757
}
5858

59+
- (void)testGenerateOperationalCertNoIntermediate
60+
{
61+
__auto_type * rootKeys = [[CHIPTestKeys alloc] init];
62+
XCTAssertNotNil(rootKeys);
63+
64+
__auto_type * rootCert = [MTRCertificates generateRootCertificate:rootKeys issuerId:nil fabricId:nil error:nil];
65+
XCTAssertNotNil(rootCert);
66+
67+
__auto_type * operationalKeys = [[CHIPTestKeys alloc] init];
68+
XCTAssertNotNil(operationalKeys);
69+
70+
__auto_type * cats = [[NSMutableArray alloc] initWithCapacity:3];
71+
[cats addObject:@1];
72+
[cats addObject:@2];
73+
[cats addObject:@3];
74+
75+
__auto_type * operationalCert = [MTRCertificates generateOperationalCertificate:rootKeys
76+
signingCertificate:rootCert
77+
operationalPublicKey:operationalKeys.pubkey
78+
fabricId:@1
79+
nodeId:@1
80+
caseAuthenticatedTags:cats
81+
error:nil];
82+
XCTAssertNotNil(operationalCert);
83+
}
84+
85+
- (void)testGenerateOperationalCertWithIntermediate
86+
{
87+
__auto_type * rootKeys = [[CHIPTestKeys alloc] init];
88+
XCTAssertNotNil(rootKeys);
89+
90+
__auto_type * rootCert = [MTRCertificates generateRootCertificate:rootKeys issuerId:nil fabricId:nil error:nil];
91+
XCTAssertNotNil(rootCert);
92+
93+
__auto_type * intermediateKeys = [[CHIPTestKeys alloc] init];
94+
XCTAssertNotNil(intermediateKeys);
95+
96+
__auto_type * intermediateCert = [MTRCertificates generateIntermediateCertificate:rootKeys
97+
rootCertificate:rootCert
98+
intermediatePublicKey:intermediateKeys.pubkey
99+
issuerId:nil
100+
fabricId:nil
101+
error:nil];
102+
XCTAssertNotNil(intermediateCert);
103+
104+
__auto_type * operationalKeys = [[CHIPTestKeys alloc] init];
105+
XCTAssertNotNil(operationalKeys);
106+
107+
__auto_type * operationalCert = [MTRCertificates generateOperationalCertificate:intermediateKeys
108+
signingCertificate:intermediateCert
109+
operationalPublicKey:operationalKeys.pubkey
110+
fabricId:@1
111+
nodeId:@1
112+
caseAuthenticatedTags:nil
113+
error:nil];
114+
XCTAssertNotNil(operationalCert);
115+
}
116+
117+
- (void)testGenerateOperationalCertErrorCases
118+
{
119+
__auto_type * rootKeys = [[CHIPTestKeys alloc] init];
120+
XCTAssertNotNil(rootKeys);
121+
122+
__auto_type * rootCert = [MTRCertificates generateRootCertificate:rootKeys issuerId:nil fabricId:nil error:nil];
123+
XCTAssertNotNil(rootCert);
124+
125+
__auto_type * operationalKeys = [[CHIPTestKeys alloc] init];
126+
XCTAssertNotNil(operationalKeys);
127+
128+
__auto_type * cats = [[NSMutableArray alloc] initWithCapacity:4];
129+
[cats addObject:@1];
130+
[cats addObject:@2];
131+
[cats addObject:@3];
132+
[cats addObject:@4];
133+
134+
// Check basic case works
135+
__auto_type * operationalCert = [MTRCertificates generateOperationalCertificate:rootKeys
136+
signingCertificate:rootCert
137+
operationalPublicKey:operationalKeys.pubkey
138+
fabricId:@1
139+
nodeId:@1
140+
caseAuthenticatedTags:nil
141+
error:nil];
142+
XCTAssertNotNil(operationalCert);
143+
144+
// CATs too long
145+
operationalCert = [MTRCertificates generateOperationalCertificate:rootKeys
146+
signingCertificate:rootCert
147+
operationalPublicKey:operationalKeys.pubkey
148+
fabricId:@1
149+
nodeId:@1
150+
caseAuthenticatedTags:cats
151+
error:nil];
152+
XCTAssertNil(operationalCert);
153+
154+
// Signing key mismatch
155+
operationalCert = [MTRCertificates generateOperationalCertificate:operationalKeys
156+
signingCertificate:rootCert
157+
operationalPublicKey:operationalKeys.pubkey
158+
fabricId:@1
159+
nodeId:@1
160+
caseAuthenticatedTags:nil
161+
error:nil];
162+
XCTAssertNil(operationalCert);
163+
164+
// Invalid fabric id
165+
operationalCert = [MTRCertificates generateOperationalCertificate:rootKeys
166+
signingCertificate:rootCert
167+
operationalPublicKey:operationalKeys.pubkey
168+
fabricId:@0
169+
nodeId:@1
170+
caseAuthenticatedTags:nil
171+
error:nil];
172+
XCTAssertNil(operationalCert);
173+
174+
// Undefined node id
175+
operationalCert = [MTRCertificates generateOperationalCertificate:rootKeys
176+
signingCertificate:rootCert
177+
operationalPublicKey:operationalKeys.pubkey
178+
fabricId:@1
179+
nodeId:@0
180+
caseAuthenticatedTags:nil
181+
error:nil];
182+
XCTAssertNil(operationalCert);
183+
184+
// Non-operational node id
185+
operationalCert = [MTRCertificates generateOperationalCertificate:rootKeys
186+
signingCertificate:rootCert
187+
operationalPublicKey:operationalKeys.pubkey
188+
fabricId:@1
189+
nodeId:@(0xFFFFFFFFFFFFFFFFLLU)
190+
caseAuthenticatedTags:nil
191+
error:nil];
192+
XCTAssertNil(operationalCert);
193+
}
194+
59195
@end

0 commit comments

Comments
 (0)