|
| 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