Skip to content

Commit 4557266

Browse files
bzbarsky-applepull[bot]
authored andcommitted
Stop validating certificate lifetimes in Matter.framework. (#26546)
1 parent b618377 commit 4557266

File tree

3 files changed

+340
-0
lines changed

3 files changed

+340
-0
lines changed

src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm

+13
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
static NSString * const kErrorDACVerifierInit = @"Init failure while creating the device attestation verifier";
5858
static NSString * const kErrorGroupProviderInit = @"Init failure while initializing group data provider";
5959
static NSString * const kErrorControllersInit = @"Init controllers array failure";
60+
static NSString * const kErrorCertificateValidityPolicyInit = @"Init certificate validity policy failure";
6061
static NSString * const kErrorControllerFactoryInit = @"Init failure while initializing controller factory";
6162
static NSString * const kErrorKeystoreInit = @"Init failure while initializing persistent storage keystore";
6263
static NSString * const kErrorCertStoreInit = @"Init failure while initializing persistent storage operational certificate store";
@@ -86,6 +87,7 @@ @interface MTRDeviceControllerFactory ()
8687
@property (readonly) MTROperationalBrowser * operationalBrowser;
8788
@property () chip::Credentials::DeviceAttestationVerifier * deviceAttestationVerifier;
8889
@property (readonly) BOOL advertiseOperational;
90+
@property (nonatomic, readonly) Credentials::IgnoreCertificateValidityPeriodPolicy * certificateValidityPolicy;
8991

9092
- (BOOL)findMatchingFabric:(FabricTable &)fabricTable
9193
params:(MTRDeviceControllerStartupParams *)params
@@ -150,6 +152,11 @@ - (instancetype)init
150152
return nil;
151153
}
152154

155+
_certificateValidityPolicy = new Credentials::IgnoreCertificateValidityPeriodPolicy();
156+
if ([self checkForInitError:(_certificateValidityPolicy != nil) logMsg:kErrorCertificateValidityPolicyInit]) {
157+
return nil;
158+
}
159+
153160
return self;
154161
}
155162

@@ -204,6 +211,11 @@ - (void)cleanupInitObjects
204211
delete _sessionKeystore;
205212
_sessionKeystore = nullptr;
206213
}
214+
215+
if (_certificateValidityPolicy) {
216+
delete _certificateValidityPolicy;
217+
_certificateValidityPolicy = nullptr;
218+
}
207219
}
208220

209221
- (void)cleanupStartupObjects
@@ -443,6 +455,7 @@ - (BOOL)startControllerFactory:(MTRDeviceControllerFactoryParams *)startupParams
443455
params.fabricIndependentStorage = _persistentStorageDelegateBridge;
444456
params.operationalKeystore = _keystore;
445457
params.opCertStore = _opCertStore;
458+
params.certificateValidityPolicy = _certificateValidityPolicy;
446459
errorCode = _controllerFactory->Init(params);
447460
if (errorCode != CHIP_NO_ERROR) {
448461
MTR_LOG_ERROR("Error: %@", kErrorControllerFactoryInit);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
/*
2+
*
3+
* Copyright (c) 2022 Project CHIP Authors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
// module headers
19+
#import <Matter/Matter.h>
20+
21+
#import "MTRErrorTestUtils.h"
22+
#import "MTRTestKeys.h"
23+
#import "MTRTestResetCommissioneeHelper.h"
24+
#import "MTRTestStorage.h"
25+
26+
// system dependencies
27+
#import <XCTest/XCTest.h>
28+
29+
static const uint16_t kPairingTimeoutInSeconds = 10;
30+
static const uint16_t kTimeoutInSeconds = 3;
31+
static const uint64_t kDeviceId = 0x12341234;
32+
static const uint64_t kControllerId = 0x56788765;
33+
static NSString * kOnboardingPayload = @"MT:-24J0AFN00KA0648G00";
34+
static const uint16_t kLocalPort = 5541;
35+
static const uint16_t kTestVendorId = 0xFFF1u;
36+
37+
static MTRBaseDevice * sConnectedDevice;
38+
39+
// Singleton controller we use.
40+
static MTRDeviceController * sController = nil;
41+
42+
@interface MTRCertificateValidityTestControllerDelegate : NSObject <MTRDeviceControllerDelegate>
43+
@property (nonatomic, readonly) XCTestExpectation * expectation;
44+
@property (nonatomic, readonly) NSNumber * commissioneeNodeID;
45+
@end
46+
47+
@implementation MTRCertificateValidityTestControllerDelegate
48+
- (id)initWithExpectation:(XCTestExpectation *)expectation commissioneeNodeID:(NSNumber *)nodeID
49+
{
50+
self = [super init];
51+
if (self) {
52+
_expectation = expectation;
53+
_commissioneeNodeID = nodeID;
54+
}
55+
return self;
56+
}
57+
58+
- (void)controller:(MTRDeviceController *)controller commissioningSessionEstablishmentDone:(NSError * _Nullable)error
59+
{
60+
XCTAssertEqual(error.code, 0);
61+
62+
NSError * commissionError = nil;
63+
[sController commissionNodeWithID:self.commissioneeNodeID
64+
commissioningParams:[[MTRCommissioningParameters alloc] init]
65+
error:&commissionError];
66+
XCTAssertNil(commissionError);
67+
68+
// Keep waiting for onCommissioningComplete
69+
}
70+
71+
- (void)controller:(MTRDeviceController *)controller commissioningComplete:(NSError *)error
72+
{
73+
XCTAssertEqual(error.code, 0);
74+
[_expectation fulfill];
75+
_expectation = nil;
76+
}
77+
@end
78+
79+
@interface MTRTestCertificateIssuer : NSObject <MTROperationalCertificateIssuer>
80+
81+
@property (nonatomic, readonly) MTRTestKeys * rootKey;
82+
@property (nonatomic, copy, readonly) MTRCertificateDERBytes rootCertificate;
83+
@property (nonatomic, copy, readonly) NSDateInterval * validityPeriod;
84+
@property (nonatomic, copy, readonly) NSNumber * fabricID;
85+
@property (nonatomic, readonly) BOOL shouldSkipAttestationCertificateValidation;
86+
87+
- (nullable instancetype)initWithRootKey:(MTRTestKeys *)key
88+
fabricID:(NSNumber *)fabricID
89+
validityPeriod:(NSDateInterval *)validityPeriod;
90+
91+
- (nullable MTRCertificateDERBytes)issueOperationalCertificateForNode:(NSNumber *)nodeID
92+
operationalPublicKey:(SecKeyRef)operationalPublicKey;
93+
94+
- (void)issueOperationalCertificateForRequest:(MTROperationalCSRInfo *)csrInfo
95+
attestationInfo:(MTRDeviceAttestationInfo *)attestationInfo
96+
controller:(MTRDeviceController *)controller
97+
completion:(void (^)(MTROperationalCertificateChain * _Nullable info,
98+
NSError * _Nullable error))completion;
99+
@end
100+
101+
@implementation MTRTestCertificateIssuer
102+
- (nullable instancetype)initWithRootKey:(MTRTestKeys *)key
103+
fabricID:(NSNumber *)fabricID
104+
validityPeriod:(NSDateInterval *)validityPeriod
105+
{
106+
if (!(self = [super init])) {
107+
return nil;
108+
}
109+
110+
NSError * error;
111+
__auto_type * rootCertificate = [MTRCertificates createRootCertificate:key
112+
issuerID:nil
113+
fabricID:fabricID
114+
validityPeriod:validityPeriod
115+
error:&error];
116+
XCTAssertNil(error);
117+
XCTAssertNotNil(rootCertificate);
118+
119+
if (rootCertificate == nil) {
120+
return nil;
121+
}
122+
123+
_validityPeriod = validityPeriod;
124+
_rootCertificate = rootCertificate;
125+
_rootKey = key;
126+
_fabricID = fabricID;
127+
_shouldSkipAttestationCertificateValidation = NO;
128+
129+
return self;
130+
}
131+
132+
- (nullable MTRCertificateDERBytes)issueOperationalCertificateForNode:(NSNumber *)nodeID
133+
operationalPublicKey:(SecKeyRef)operationalPublicKey
134+
{
135+
return [MTRCertificates createOperationalCertificate:self.rootKey
136+
signingCertificate:self.rootCertificate
137+
operationalPublicKey:operationalPublicKey
138+
fabricID:self.fabricID
139+
nodeID:nodeID
140+
caseAuthenticatedTags:nil
141+
validityPeriod:self.validityPeriod
142+
error:nil];
143+
}
144+
145+
- (void)issueOperationalCertificateForRequest:(MTROperationalCSRInfo *)csrInfo
146+
attestationInfo:(MTRDeviceAttestationInfo *)attestationInfo
147+
controller:(MTRDeviceController *)controller
148+
completion:(void (^)(MTROperationalCertificateChain * _Nullable info,
149+
NSError * _Nullable error))completion
150+
{
151+
NSError * error;
152+
__auto_type * publicKey = [MTRCertificates publicKeyFromCSR:csrInfo.csr error:&error];
153+
XCTAssertNil(error);
154+
XCTAssertNotNil(publicKey);
155+
156+
NSDictionary * attributes =
157+
@{ (id) kSecAttrKeyType : (id) kSecAttrKeyTypeECSECPrimeRandom, (id) kSecAttrKeyClass : (id) kSecAttrKeyClassPublic };
158+
CFErrorRef keyCreationError = NULL;
159+
SecKeyRef operationalPublicKey
160+
= SecKeyCreateWithData((__bridge CFDataRef) publicKey, (__bridge CFDictionaryRef) attributes, &keyCreationError);
161+
XCTAssertNotNil((__bridge id) operationalPublicKey);
162+
XCTAssertNil((__bridge id) keyCreationError);
163+
164+
__auto_type * operationalCertificate = [self issueOperationalCertificateForNode:@(kDeviceId)
165+
operationalPublicKey:operationalPublicKey];
166+
XCTAssertNotNil(operationalCertificate);
167+
168+
__auto_type * certChain = [[MTROperationalCertificateChain alloc] initWithOperationalCertificate:operationalCertificate
169+
intermediateCertificate:nil
170+
rootCertificate:self.rootCertificate
171+
adminSubject:nil];
172+
XCTAssertNotNil(certChain);
173+
completion(certChain, nil);
174+
}
175+
@end
176+
177+
@interface MTRTestExpiredCertificateIssuer : MTRTestCertificateIssuer
178+
179+
- (nullable instancetype)initWithRootKey:(MTRTestKeys *)key fabricID:(NSNumber *)fabricID;
180+
181+
@end
182+
183+
@implementation MTRTestExpiredCertificateIssuer
184+
- (nullable instancetype)initWithRootKey:(MTRTestKeys *)key fabricID:(NSNumber *)fabricID
185+
{
186+
// Ensure oldDate is before newDate and both are in the past.
187+
__auto_type * oldDate = [NSDate dateWithTimeIntervalSinceNow:-5];
188+
__auto_type * newDate = [NSDate dateWithTimeIntervalSinceNow:-2];
189+
__auto_type * validityPeriod = [[NSDateInterval alloc] initWithStartDate:oldDate endDate:newDate];
190+
191+
return [super initWithRootKey:key fabricID:fabricID validityPeriod:validityPeriod];
192+
}
193+
194+
@end
195+
196+
@interface MTRCertificateValidityTests : XCTestCase
197+
@end
198+
199+
static BOOL sNeedsStackShutdown = YES;
200+
201+
@implementation MTRCertificateValidityTests
202+
203+
+ (void)tearDown
204+
{
205+
// Global teardown, runs once
206+
if (sNeedsStackShutdown) {
207+
// We don't need to worry about ResetCommissionee. If we get here,
208+
// we're running only one of our test methods (using
209+
// -only-testing:MatterTests/MTROTAProviderTests/testMethodName), since
210+
// we did not run test999_TearDown.
211+
[self shutdownStack];
212+
}
213+
}
214+
215+
- (void)setUp
216+
{
217+
// Per-test setup, runs before each test.
218+
[super setUp];
219+
[self setContinueAfterFailure:NO];
220+
}
221+
222+
- (MTRBaseDevice *)commissionDeviceWithPayload:(NSString *)payloadString nodeID:(NSNumber *)nodeID
223+
{
224+
XCTestExpectation * expectation =
225+
[self expectationWithDescription:[NSString stringWithFormat:@"Commissioning Complete for %@", nodeID]];
226+
__auto_type * deviceControllerDelegate = [[MTRCertificateValidityTestControllerDelegate alloc] initWithExpectation:expectation
227+
commissioneeNodeID:nodeID];
228+
dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.device_controller_delegate", DISPATCH_QUEUE_SERIAL);
229+
230+
[sController setDeviceControllerDelegate:deviceControllerDelegate queue:callbackQueue];
231+
232+
NSError * error;
233+
__auto_type * payload = [MTRSetupPayload setupPayloadWithOnboardingPayload:payloadString error:&error];
234+
XCTAssertNotNil(payload);
235+
XCTAssertNil(error);
236+
237+
[sController setupCommissioningSessionWithPayload:payload newNodeID:nodeID error:&error];
238+
XCTAssertNil(error);
239+
240+
[self waitForExpectations:@[ expectation ] timeout:kPairingTimeoutInSeconds];
241+
242+
return [MTRBaseDevice deviceWithNodeID:nodeID controller:sController];
243+
}
244+
245+
- (void)initStack:(MTRTestCertificateIssuer *)certificateIssuer
246+
{
247+
__auto_type * factory = [MTRDeviceControllerFactory sharedInstance];
248+
XCTAssertNotNil(factory);
249+
250+
__auto_type * storage = [[MTRTestStorage alloc] init];
251+
252+
__auto_type * factoryParams = [[MTRDeviceControllerFactoryParams alloc] initWithStorage:storage];
253+
factoryParams.port = @(kLocalPort);
254+
factoryParams.shouldStartServer = YES;
255+
256+
BOOL ok = [factory startControllerFactory:factoryParams error:nil];
257+
XCTAssertTrue(ok);
258+
259+
__auto_type * controllerOperationalKeys = [[MTRTestKeys alloc] init];
260+
XCTAssertNotNil(controllerOperationalKeys);
261+
262+
__auto_type * controllerOperationalCert =
263+
[certificateIssuer issueOperationalCertificateForNode:@(kControllerId)
264+
operationalPublicKey:controllerOperationalKeys.publicKey];
265+
XCTAssertNotNil(controllerOperationalCert);
266+
267+
__auto_type * params = [[MTRDeviceControllerStartupParams alloc] initWithIPK:certificateIssuer.rootKey.ipk
268+
operationalKeypair:controllerOperationalKeys
269+
operationalCertificate:controllerOperationalCert
270+
intermediateCertificate:nil
271+
rootCertificate:certificateIssuer.rootCertificate];
272+
XCTAssertNotNil(params);
273+
274+
params.vendorID = @(kTestVendorId);
275+
params.operationalCertificateIssuer = certificateIssuer;
276+
params.operationalCertificateIssuerQueue = dispatch_get_main_queue();
277+
278+
MTRDeviceController * controller = [factory createControllerOnNewFabric:params error:nil];
279+
XCTAssertNotNil(controller);
280+
281+
sController = controller;
282+
283+
sConnectedDevice = [self commissionDeviceWithPayload:kOnboardingPayload nodeID:@(kDeviceId)];
284+
}
285+
286+
+ (void)shutdownStack
287+
{
288+
sNeedsStackShutdown = NO;
289+
290+
MTRDeviceController * controller = sController;
291+
[controller shutdown];
292+
XCTAssertFalse([controller isRunning]);
293+
294+
[[MTRDeviceControllerFactory sharedInstance] stopControllerFactory];
295+
}
296+
297+
- (void)test001_TestExpiredCertificates
298+
{
299+
__auto_type * testKeys = [[MTRTestKeys alloc] init];
300+
XCTAssertNotNil(testKeys);
301+
302+
__auto_type * certificateIssuer = [[MTRTestExpiredCertificateIssuer alloc] initWithRootKey:testKeys fabricID:@(1)];
303+
XCTAssertNotNil(certificateIssuer);
304+
305+
[self initStack:certificateIssuer];
306+
307+
XCTestExpectation * toggleExpectation = [self expectationWithDescription:@"Toggle command executed"];
308+
309+
__auto_type * onOffCluster = [[MTRBaseClusterOnOff alloc] initWithDevice:sConnectedDevice
310+
endpointID:@(1)
311+
queue:dispatch_get_main_queue()];
312+
[onOffCluster toggleWithCompletion:^(NSError * _Nullable error) {
313+
XCTAssertNil(error);
314+
[toggleExpectation fulfill];
315+
}];
316+
[self waitForExpectations:@[ toggleExpectation ] timeout:kTimeoutInSeconds];
317+
318+
ResetCommissionee(sConnectedDevice, dispatch_get_main_queue(), self, kTimeoutInSeconds);
319+
320+
[[self class] shutdownStack];
321+
}
322+
323+
@end

0 commit comments

Comments
 (0)