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