]> git.saurik.com Git - apple/security.git/blob - KeychainCircle/Tests/KCPairingTest.m
Security-58286.60.28.tar.gz
[apple/security.git] / KeychainCircle / Tests / KCPairingTest.m
1 //
2 // SecurityPairing.m
3 // Security_ios
4 //
5 // Created by Love Hörnquist Åstrand on 2017-02-28.
6 //
7
8 #import <Foundation/Foundation.h>
9 #import <Security/Security.h>
10 #import <Security/SecKeyPriv.h>
11 #import <Security/SecItemPriv.h>
12 #import <Security/SecureObjectSync/SOSAccount.h>
13 #import <Security/SecureObjectSync/SOSAccountPriv.h>
14 #import <Security/SecureObjectSync/SOSCircle.h>
15 #import <KeychainCircle/KeychainCircle.h>
16 #import <XCTest/XCTest.h>
17 #import "SecCFWrappers.h"
18 #import "SOSRegressionUtilities.h"
19
20 @interface FakeNSXPCConnection : NSObject
21 - (instancetype) initWithControl:(id<SOSControlProtocol>)control;
22 - (id)remoteObjectProxyWithErrorHandler:(void(^)(NSError * _Nonnull error))failureHandler;
23 @end
24 @interface FakeNSXPCConnection ()
25 @property id<SOSControlProtocol> control;
26 @end
27 @implementation FakeNSXPCConnection
28 - (instancetype) initWithControl:(id<SOSControlProtocol>)control
29 {
30 self = [super init];
31 if (self) {
32 _control = control;
33 }
34 return self;
35 }
36 - (id)remoteObjectProxyWithErrorHandler:(void(^)(NSError * _Nonnull error))failureHandler
37 {
38 (void)failureHandler;
39 return _control;
40 }
41 @end
42
43
44 @interface KCPairingTest : XCTestCase
45
46 @end
47
48 @interface FCPairingFakeSOSControl : NSObject <SOSControlProtocol>
49 @property (assign) SecKeyRef accountPrivateKey;
50 @property (assign) SecKeyRef accountPublicKey;
51 @property (assign) SecKeyRef deviceKey;
52 @property (assign) SecKeyRef octagonSigningKey;
53 @property (assign) SecKeyRef octagonEncryptionKey;
54 @property (assign) SOSCircleRef circle;
55 @property (assign) SOSFullPeerInfoRef fullPeerInfo;
56 @property (assign) bool application;
57 @end
58
59 @implementation FCPairingFakeSOSControl
60
61 - (instancetype)initWithRandomAccountKey:(bool)randomAccountKey circle:(SOSCircleRef)circle
62 {
63 if ((self = [super init])) {
64 SecKeyRef publicKey = NULL;
65 NSDictionary* parameters = @{
66 (__bridge NSString*)kSecAttrKeyType:(__bridge NSString*) kSecAttrKeyTypeEC,
67 (__bridge NSString*)kSecAttrKeySizeInBits: @(256),
68 (__bridge NSString*)kSecAttrNoLegacy : @YES,
69 (__bridge NSString*)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleAfterFirstUnlock,
70 (__bridge id)kSecPrivateKeyAttrs : @{
71 (__bridge NSString*)kSecAttrLabel : @"delete me test case - private",
72 (__bridge NSString*)kSecAttrIsPermanent : @YES,
73 (__bridge NSString*)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleAfterFirstUnlock,
74 },
75 (__bridge id)kSecPublicKeyAttrs : @{
76 (__bridge NSString*)kSecAttrLabel : @"delete me test case - public",
77 (__bridge NSString*)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleAfterFirstUnlock,
78 }
79 };
80 if(SecKeyGeneratePair((__bridge CFDictionaryRef)parameters, &publicKey, &_deviceKey) != 0) {
81 NSLog(@"failed to create device key");
82 return nil;
83 }
84 CFReleaseNull(publicKey);
85
86 NSMutableDictionary* octagonParameters = [parameters mutableCopy];
87 octagonParameters[(__bridge NSString*)kSecAttrKeySizeInBits] = @(384);
88 if(SecKeyGeneratePair((__bridge CFDictionaryRef)octagonParameters, &publicKey, &_octagonSigningKey) != 0) {
89 NSLog(@"failed to create octagon signing key");
90 return nil;
91 }
92 CFReleaseNull(publicKey);
93
94 if(SecKeyGeneratePair((__bridge CFDictionaryRef)octagonParameters, &publicKey, &_octagonEncryptionKey) != 0) {
95 NSLog(@"failed to create octagon signing key");
96 return nil;
97 }
98 CFReleaseNull(publicKey);
99
100
101 _circle = (SOSCircleRef)CFRetain(circle);
102
103 CFErrorRef error = NULL;
104
105 CFDictionaryRef gestalt = (__bridge CFDictionaryRef)@{
106 @"ComputerName" : @"name",
107 };
108
109 _fullPeerInfo = SOSFullPeerInfoCreate(NULL, gestalt, NULL, _deviceKey, _octagonSigningKey, _octagonEncryptionKey, &error);
110 CFReleaseNull(error);
111
112 if (randomAccountKey) {
113
114 NSDictionary* accountParams = @{
115 (__bridge NSString*)kSecAttrKeyType:(__bridge NSString*) kSecAttrKeyTypeEC,
116 (__bridge NSString*)kSecAttrKeySizeInBits: @(256),
117 (__bridge NSString*)kSecAttrNoLegacy : @YES,
118 (__bridge NSString*)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleAfterFirstUnlock,
119 };
120
121 if(SecKeyGeneratePair((__bridge CFDictionaryRef)accountParams, &publicKey, &_accountPrivateKey) != 0) {
122 NSLog(@"failed to create account signing key");
123 return nil;
124 }
125 CFReleaseNull(publicKey);
126
127 _accountPublicKey = SecKeyCopyPublicKey(_accountPrivateKey);
128
129 [self signApplicationIfNeeded];
130 }
131 }
132 return self;
133 }
134
135 - (void)dealloc
136 {
137 if (_accountPrivateKey) {
138 SecItemDelete((__bridge CFTypeRef)@{ (__bridge id)kSecValueRef : (__bridge id)_accountPrivateKey });
139 CFReleaseNull(_accountPrivateKey);
140 }
141 if (_deviceKey) {
142 SecItemDelete((__bridge CFTypeRef)@{ (__bridge id)kSecValueRef : (__bridge id)_deviceKey });
143 CFReleaseNull(_deviceKey);
144 }
145 if (_octagonSigningKey) {
146 SecItemDelete((__bridge CFTypeRef)@{ (__bridge id)kSecValueRef : (__bridge id)_octagonSigningKey });
147 CFReleaseNull(_octagonSigningKey);
148 }
149 if (_octagonEncryptionKey) {
150 SecItemDelete((__bridge CFTypeRef)@{ (__bridge id)kSecValueRef : (__bridge id)_octagonEncryptionKey });
151 CFReleaseNull(_octagonEncryptionKey);
152 }
153 CFReleaseNull(_circle);
154 CFReleaseNull(_fullPeerInfo);
155 }
156
157 - (SOSPeerInfoRef)peerInfo
158 {
159 return SOSFullPeerInfoGetPeerInfo(_fullPeerInfo);
160 }
161
162 - (void)signApplicationIfNeeded
163 {
164 CFErrorRef error = NULL;
165
166 _application = SOSFullPeerInfoPromoteToApplication(_fullPeerInfo, _accountPrivateKey, &error);
167 if (!_application)
168 abort();
169 }
170
171 - (void)initialSyncCredentials:(uint32_t)flags complete:(void (^)(NSArray *, NSError *))complete
172 {
173 complete(@[], NULL);
174 }
175
176 - (void)importInitialSyncCredentials:(NSArray *)items complete:(void (^)(bool success, NSError *))complete
177 {
178 complete(true, NULL);
179 }
180
181 - (void)triggerSync:(NSArray<NSString *> *)peers complete:(void(^)(bool success, NSError *))complete
182 {
183 complete(true, NULL);
184 }
185
186 //MARK - FCPairingFakeSOSControl SOSControlProtocol
187
188 - (void)userPublicKey:(void ((^))(BOOL trusted, NSData *spki, NSError *error))complete
189 {
190 complete(false, NULL, NULL);
191 }
192
193 - (void)performanceCounters:(void(^)(NSDictionary <NSString *, NSNumber *> *))complete
194 {
195 complete(@{});
196 }
197 - (void)kvsPerformanceCounters:(void(^)(NSDictionary <NSString *, NSNumber *> *))complete
198 {
199 complete(@{});
200 }
201 - (void)idsPerformanceCounters:(void(^)(NSDictionary <NSString *, NSNumber *> *))complete
202 {
203 complete(@{});
204 }
205 - (void)rateLimitingPerformanceCounters:(void(^)(NSDictionary <NSString *, NSString *> *))complete
206 {
207 complete(@{});
208 }
209 - (void)stashedCredentialPublicKey:(void(^)(NSData *, NSError *error))complete
210 {
211 NSData *publicKey = NULL;
212 NSError *error = NULL;
213 if (self.accountPrivateKey) {
214 publicKey = CFBridgingRelease(SecKeyCopySubjectPublicKeyInfo(self.accountPrivateKey));
215 } else {
216 error = [NSError errorWithDomain:@"FCPairingFakeSOSControl" code:2 userInfo:NULL];
217 }
218 complete(publicKey, error);
219 }
220
221 - (void)assertStashedAccountCredential:(void(^)(BOOL result, NSError *error))complete
222 {
223 complete(self.accountPrivateKey != NULL, NULL);
224 }
225
226 - (void)validatedStashedAccountCredential:(void(^)(NSData *credential, NSError *error))complete
227 {
228 NSData *key = NULL;
229 CFErrorRef error = NULL;
230 if (self.accountPrivateKey) {
231 key = CFBridgingRelease(SecKeyCopyExternalRepresentation(self.accountPrivateKey, &error));
232 } else {
233 error = (CFErrorRef)CFBridgingRetain([NSError errorWithDomain:@"FCPairingFakeSOSControl" code:1 userInfo:NULL]);
234 }
235 complete(key, (__bridge NSError *)error);
236 CFReleaseNull(error);
237 }
238
239 - (void)stashAccountCredential:(NSData *)credential complete:(void(^)(bool success, NSError *error))complete
240 {
241 SecKeyRef accountPrivateKey = NULL;
242 CFErrorRef error = NULL;
243 NSDictionary *attributes = @{
244 (__bridge id)kSecAttrKeyClass : (__bridge id)kSecAttrKeyClassPrivate,
245 (__bridge id)kSecAttrKeyType : (__bridge id)kSecAttrKeyTypeEC,
246 };
247
248 accountPrivateKey = SecKeyCreateWithData((__bridge CFDataRef)credential, (__bridge CFDictionaryRef)attributes, &error);
249 if (accountPrivateKey == NULL) {
250 complete(false, (__bridge NSError *)error);
251 CFReleaseNull(error);
252 return;
253 }
254
255 _accountPrivateKey = accountPrivateKey;
256 _accountPublicKey = SecKeyCopyPublicKey(_accountPrivateKey);
257
258 [self signApplicationIfNeeded];
259
260 complete(true, NULL);
261 }
262
263 - (void)myPeerInfo:(void(^)(NSData *application, NSError *error))complete
264 {
265 CFErrorRef error = NULL;
266
267 [self signApplicationIfNeeded];
268
269 NSData *application = CFBridgingRelease(SOSPeerInfoCopyEncodedData([self peerInfo], NULL, &error));
270 complete(application, (__bridge NSError *)error);
271
272 CFReleaseNull(error);
273 }
274
275 - (void)circleJoiningBlob:(NSData *)applicantData complete:(void (^)(NSData *blob, NSError *))complete
276 {
277 CFErrorRef error = NULL;
278 CFDataRef signature = NULL;
279 SOSCircleRef prunedCircle = SOSCircleCopyCircle(NULL, _circle, &error);
280 (void)SOSCirclePreGenerationSign(prunedCircle, _accountPublicKey, &error);
281
282 SOSGenCountRef gencount = SOSGenerationIncrementAndCreate(SOSCircleGetGeneration(prunedCircle));
283 if (gencount == NULL)
284 abort();
285
286
287 SOSPeerInfoRef applicant = SOSPeerInfoCreateFromData(NULL, &error, (__bridge CFDataRef)applicantData);
288 if (applicant == NULL)
289 abort();
290
291 signature = SOSCircleCopyNextGenSignatureWithPeerAdded(prunedCircle, applicant, _deviceKey, &error);
292 if(applicant) {
293 CFRelease(applicant);
294 applicant = NULL;
295 }
296
297 NSData *pbblob = CFBridgingRelease(SOSPiggyBackBlobCopyEncodedData(gencount, _deviceKey, signature, &error));
298
299 CFReleaseNull(signature);
300 CFReleaseNull(gencount);
301 CFReleaseNull(prunedCircle);
302
303 complete(pbblob, NULL);
304 }
305
306 - (void)joinCircleWithBlob:(NSData *)blob version:(PiggyBackProtocolVersion)version complete:(void (^)(bool success, NSError *))complete
307 {
308 SOSGenCountRef gencount = NULL;
309 SecKeyRef pubKey = NULL;
310 CFDataRef signature = NULL;
311 CFErrorRef error = NULL;
312 bool setInitialSyncTimeoutToV0 = false;
313
314 if (!SOSPiggyBackBlobCreateFromData(&gencount, &pubKey, &signature, (__bridge CFDataRef)blob, kPiggyV1, &setInitialSyncTimeoutToV0, &error)) {
315 complete(true, (__bridge NSError *)error);
316 CFReleaseNull(error);
317 return;
318 }
319
320 (void)SOSCircleAcceptPeerFromHSA2(_circle,
321 _accountPrivateKey,
322 gencount,
323 pubKey,
324 signature,
325 _fullPeerInfo,
326 &error);
327
328 CFReleaseNull(gencount);
329 CFReleaseNull(pubKey);
330 CFReleaseNull(signature);
331
332 complete(true, (__bridge NSError *)error);
333
334 CFReleaseNull(error);
335
336 }
337
338 - (void)getWatchdogParameters:(void (^)(NSDictionary*, NSError*))complete
339 {
340 // intentionally left blank
341 // these are used by the security/2 tool and are only declared here to make the compiler happy about conforming the protocol we shoved the methods into
342 }
343
344
345 - (void)setWatchdogParmeters:(NSDictionary*)parameters complete:(void (^)(NSError*))complete
346 {
347 // intentionally left blank
348 // these are used by the security/2 tool and are only declared here to make the compiler happy about conforming the protocol we shoved the methods into
349 }
350 @end
351
352 @implementation KCPairingTest
353
354 - (void)testSecPairBasicTest
355 {
356 bool sp1compete = false, sp2compete = false;
357 NSData *sp1data = NULL;
358 NSData *sp2data = NULL;
359 SOSCircleRef circle = NULL;
360 unsigned count = 0;
361 CFErrorRef cferror = NULL;
362 KCPairingChannel *sp1, *sp2;
363
364 circle = SOSCircleCreate(NULL, CFSTR("TEST DOMAIN"), NULL);
365 XCTAssert(circle, "circle");
366
367 FCPairingFakeSOSControl *fc1 = [[FCPairingFakeSOSControl alloc] initWithRandomAccountKey:false circle:circle];
368 XCTAssert(fc1, "create fake soscontrol 1");
369
370 FCPairingFakeSOSControl *fc2 = [[FCPairingFakeSOSControl alloc] initWithRandomAccountKey:true circle:circle];
371 XCTAssert(fc2, "create fake soscontrol 2");
372
373
374 XCTAssert(SOSCircleRequestAdmission(circle, fc2.accountPrivateKey, fc2.fullPeerInfo, &cferror), "SOSCircleRequestAdmission: %@", cferror);
375 CFReleaseNull(cferror);
376
377 XCTAssert(SOSCircleAcceptRequest(circle, fc2.accountPrivateKey, fc2.fullPeerInfo, [fc2 peerInfo], &cferror), "SOSCircleAcceptRequest device 1: %@", cferror);
378 CFReleaseNull(cferror);
379
380 XCTAssert(SOSCircleHasPeer(circle, [fc2 peerInfo], &cferror), "HasPeer 2: %@", cferror);
381 CFReleaseNull(cferror);
382
383
384 sp1 = [KCPairingChannel pairingChannelInitiator:NULL];
385 [sp1 setXPCConnectionObject:(NSXPCConnection *)[[FakeNSXPCConnection alloc] initWithControl:fc1]];
386
387 sp2 = [KCPairingChannel pairingChannelAcceptor:NULL];
388 [sp2 setXPCConnectionObject:(NSXPCConnection *)[[FakeNSXPCConnection alloc] initWithControl:fc2]] ;
389
390 while(1) {
391 NSError *error = NULL;
392
393 sp1data = [sp1 exchangePacket:sp2data complete:&sp1compete error:&error];
394
395 if (sp1compete && sp2compete) {
396 XCTAssert(sp1data == NULL, "sp1 done, yet there is data");
397 break;
398 }
399 XCTAssert(!sp2compete, "sp2 completed w/o sp1");
400
401 XCTAssert(sp1data != NULL, "sp1 not done, yet there is no data: %@", error);
402 if (sp1data == NULL)
403 break;
404
405 /* send sp1data to peer : BOB CHANNEL HERE */
406
407 sp2data = [sp2 exchangePacket:sp1data complete:&sp2compete error:&error];
408 XCTAssert(sp2data != NULL, "sp2 didn't return data: %@", error);
409 if (sp2data == NULL)
410 break;
411
412 if (sp1compete && sp2compete)
413 break;
414
415 XCTAssert(!sp1compete, "sp2 completed w/o sp1");
416
417 count++;
418 if (count > 10)
419 abort();
420 };
421
422 XCTAssert(sp1compete && sp2compete, "both parties not completed");
423
424 XCTAssert(fc1.accountPrivateKey, "no accountPrivateKey in fc1");
425 XCTAssert(fc2.accountPrivateKey, "no accountPrivateKey in fc2");
426 XCTAssert(CFEqualSafe(fc1.accountPrivateKey, fc2.accountPrivateKey), "no accountPrivateKey not same in both");
427
428 if (sp1compete && sp2compete)
429 NSLog(@"pairing complete");
430
431 XCTAssert(SOSCircleHasPeer(circle, [fc1 peerInfo], &cferror), "HasPeer 1: %@", cferror);
432 CFReleaseNull(cferror);
433 XCTAssert(SOSCircleHasPeer(circle, [fc2 peerInfo], &cferror), "HasPeer 2: %@", cferror);
434 CFReleaseNull(cferror);
435
436 XCTAssert(sp1.needInitialSync == false, "no longer need initial sync");
437
438
439 }
440
441 @end