2 // KCJoiningSessionTest.m
7 #import <XCTest/XCTest.h>
9 #import <Foundation/Foundation.h>
11 #import <KeychainCircle/KCJoiningSession.h>
12 #import <KeychainCircle/KCError.h>
13 #import <KeychainCircle/NSError+KCCreationHelpers.h>
14 #import <KeychainCircle/KCAESGCMDuplexSession.h>
16 #include <Security/SecBase.h>
17 #include "keychain/SecureObjectSync/SOSFullPeerInfo.h"
18 #include "keychain/SecureObjectSync/SOSPeerInfoInternal.h"
20 #include <CommonCrypto/CommonRandomSPI.h>
23 static SecKeyRef GenerateFullECKey_internal(int keySize, NSError** error)
25 SecKeyRef full_key = NULL;
27 NSDictionary* keygen_parameters = @{ (__bridge NSString*)kSecAttrKeyType:(__bridge NSString*) kSecAttrKeyTypeEC,
28 (__bridge NSString*)kSecAttrKeySizeInBits: [NSNumber numberWithInt: keySize] };
31 (void) OSStatusError(SecKeyGeneratePair((__bridge CFDictionaryRef)keygen_parameters, NULL, &full_key), error, @"Generate Key failed");
36 static SecKeyRef GenerateFullECKey(int keySize, NSError** error) {
37 return GenerateFullECKey_internal(keySize, error);
41 @interface KCJoiningRequestTestDelegate : NSObject <KCJoiningRequestSecretDelegate, KCJoiningRequestCircleDelegate>
42 @property (readwrite) NSString* sharedSecret;
44 @property (readonly) NSString* accountCode;
45 @property (readonly) NSData* circleJoinData;
46 @property (readwrite) SOSPeerInfoRef peerInfo;
48 @property (readwrite) NSString* incorrectSecret;
49 @property (readwrite) int incorrectTries;
52 + (id) requestDelegateWithSecret:(NSString*) secret;
53 - (id) init NS_UNAVAILABLE;
54 - (id) initWithSecret: (NSString*) secret
55 incorrectSecret: (NSString*) wrongSecret
56 incorrectTries: (int) retries NS_DESIGNATED_INITIALIZER;
58 - (NSString*) verificationFailed: (bool) codeChanged;
59 - (SOSPeerInfoRef) copyPeerInfoError: (NSError**) error;
60 - (bool) processCircleJoinData: (NSData*) circleJoinData version:(PiggyBackProtocolVersion)version error: (NSError**)error ;
61 - (bool) processAccountCode: (NSString*) accountCode error: (NSError**)error;
65 @implementation KCJoiningRequestTestDelegate
73 + (id) requestDelegateWithSecret:(NSString*) secret {
74 return [[KCJoiningRequestTestDelegate alloc] initWithSecret:secret
79 + (id) requestDelegateWithSecret:(NSString*) secret
80 incorrectSecret:(NSString*) wrongSecret
81 incorrectTries:(int) retries {
82 return [[KCJoiningRequestTestDelegate alloc] initWithSecret:secret
83 incorrectSecret:wrongSecret
84 incorrectTries:retries];
88 - (id) initWithSecret: (NSString*) secret
89 incorrectSecret: (NSString*) incorrectSecret
90 incorrectTries: (int) retries {
93 SecKeyRef signingKey = GenerateFullECKey(256, NULL);
94 SecKeyRef octagonSigningKey = GenerateFullECKey(384, NULL);
95 SecKeyRef octagonEncryptionKey = GenerateFullECKey(384, NULL);
97 SOSPeerInfoRef newPeerInfo = SOSPeerInfoCreate(NULL, (__bridge CFDictionaryRef) @{(__bridge NSString*)kPIUserDefinedDeviceNameKey:@"Fakey"}, NULL, signingKey, octagonSigningKey, octagonEncryptionKey, NULL);
99 if (newPeerInfo == NULL) {
102 self.peerInfo = newPeerInfo;
103 CFRelease(newPeerInfo);
106 self.sharedSecret = secret;
107 self.incorrectSecret = incorrectSecret;
108 self.incorrectTries = retries;
113 - (NSString*) nextSecret {
114 if (self.incorrectTries > 0) {
115 self.incorrectTries -= 1;
116 return self.incorrectSecret;
118 return self.sharedSecret;
121 - (NSString*) secret {
122 return [self nextSecret];
125 - (NSString*) verificationFailed: (bool) codeChanged {
126 return [self nextSecret];
129 - (SOSPeerInfoRef) copyPeerInfoError: (NSError**) error {
134 return (SOSPeerInfoRef) CFRetain(self.peerInfo);
137 - (bool) processCircleJoinData: (NSData*) circleJoinData version:(PiggyBackProtocolVersion)version error: (NSError**)error {
138 self->_circleJoinData = circleJoinData;
142 - (bool) processAccountCode: (NSString*) accountCode error: (NSError**)error {
143 self->_accountCode = accountCode;
149 @interface KCJoiningAcceptTestDelegate : NSObject <KCJoiningAcceptSecretDelegate, KCJoiningAcceptCircleDelegate>
150 @property (readonly) NSArray<NSString*>* secrets;
151 @property (readwrite) NSUInteger currentSecret;
152 @property (readwrite) int retriesLeft;
153 @property (readwrite) int retriesPerSecret;
155 @property (readonly) NSString* codeToUse;
156 @property (readonly) NSData* circleJoinData;
157 @property (readonly) SOSPeerInfoRef peerInfo;
159 + (id) acceptDelegateWithSecret: (NSString*) secret code: (NSString*) code;
160 + (id) acceptDelegateWithSecrets: (NSArray<NSString*>*) secrets retries: (int) retries code: (NSString*) code;
161 - (id) initWithSecrets: (NSArray<NSString*>*) secrets retries: (int) retries code: (NSString*) code NS_DESIGNATED_INITIALIZER;
164 - (NSString*) secret;
165 - (NSString*) accountCode;
167 - (KCRetryOrNot) verificationFailed: (NSError**) error;
168 - (NSData*) circleJoinDataFor: (SOSPeerInfoRef) peer
169 error: (NSError**) error;
171 - (id) init NS_UNAVAILABLE;
175 @implementation KCJoiningAcceptTestDelegate
177 + (id) acceptDelegateWithSecrets: (NSArray<NSString*>*) secrets retries: (int) retries code: (NSString*) code {
178 return [[KCJoiningAcceptTestDelegate alloc] initWithSecrets:secrets retries:retries code:code];
182 + (id) acceptDelegateWithSecret: (NSString*) secret code: (NSString*) code {
183 return [[KCJoiningAcceptTestDelegate alloc] initWithSecret:secret code:code];
186 - (id) initWithSecret: (NSString*) secret code: (NSString*) code {
187 return [self initWithSecrets:@[secret] retries:3 code:code];
190 - (id) initWithSecrets: (NSArray<NSString*>*) secrets retries: (int) retries code: (NSString*) code {
193 self->_secrets = secrets;
194 self.currentSecret = 0;
195 self->_retriesPerSecret = retries;
196 self->_retriesLeft = self.retriesPerSecret;
198 self->_codeToUse = code;
200 uint8_t joinDataBuffer[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
201 self->_circleJoinData = [NSData dataWithBytes: joinDataBuffer length: sizeof(joinDataBuffer) ];
206 - (KCRetryOrNot) advanceSecret {
207 if (self.retriesLeft == 0) {
208 self.currentSecret += 1;
209 if (self.currentSecret >= [self.secrets count]) {
210 self.currentSecret = [self.secrets count] - 1;
212 self.retriesLeft = self.retriesPerSecret;
213 return kKCRetryWithNewChallenge;
215 self.retriesLeft -= 1;
216 return kKCRetryWithSameChallenge;
220 - (NSString*) secret {
221 return self.secrets[self.currentSecret];
223 - (NSString*) accountCode {
224 return self.codeToUse;
227 - (KCRetryOrNot) verificationFailed: (NSError**) error {
228 return [self advanceSecret];
231 - (NSData*) circleJoinDataFor: (SOSPeerInfoRef) peer
232 error: (NSError**) error {
233 uint8_t joinDataBuffer[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
235 self->_peerInfo = peer;
236 return [NSData dataWithBytes: joinDataBuffer length: sizeof(joinDataBuffer) ];
239 -(NSData*) circleGetInitialSyncViews:(SOSInitialSyncFlags)flags error:(NSError**) error{
240 return [NSData data];
246 @interface KCJoiningSessionTest : XCTestCase
250 @implementation KCJoiningSessionTest
254 // Put setup code here. This method is called before the invocation of each test method in the class.
258 // Put teardown code here. This method is called after the invocation of each test method in the class.
262 - (void)testJoiningSession {
263 NSError* error = nil;
265 NSString* secret = @"123456";
266 NSString* code = @"987654";
268 uint64_t dsid = 0x1234567887654321;
270 KCJoiningRequestTestDelegate* requestDelegate = [KCJoiningRequestTestDelegate requestDelegateWithSecret: secret];
271 KCJoiningRequestSecretSession *requestSession = [[KCJoiningRequestSecretSession alloc] initWithSecretDelegate:requestDelegate
273 rng:ccDRBGGetRngState()
276 NSData* initialMessage = [requestSession initialMessage: &error];
278 XCTAssertNotNil(initialMessage, @"No initial message");
279 XCTAssertNil(error, @"Got error %@", error);
281 KCJoiningAcceptTestDelegate* acceptDelegate = [KCJoiningAcceptTestDelegate acceptDelegateWithSecret:secret code:code];
282 KCJoiningAcceptSession* acceptSession = [[KCJoiningAcceptSession alloc] initWithSecretDelegate:acceptDelegate
283 circleDelegate:acceptDelegate
285 rng:ccDRBGGetRngState()
289 NSData* challenge = [acceptSession processMessage: initialMessage error: &error];
291 XCTAssertNotNil(challenge, @"No initial message");
292 XCTAssertNil(error, @"Got error %@", error);
295 NSData* response = [requestSession processMessage: challenge error: &error];
297 XCTAssertNotNil(response, @"No response message");
298 XCTAssertNil(error, @"Got error %@", error);
301 NSData* verification = [acceptSession processMessage: response error: &error];
303 XCTAssertNotNil(verification, @"No verification message");
304 XCTAssertNil(error, @"Got error %@", error);
307 NSData* doneMessage = [requestSession processMessage: verification error: &error];
309 XCTAssertNotNil(doneMessage, @"No response message");
310 XCTAssertNil(error, @"Got error %@", error);
312 XCTAssertTrue([requestSession isDone], @"SecretSession done");
313 XCTAssertFalse([acceptSession isDone], @"Unexpected accept session done");
315 KCAESGCMDuplexSession* aesSession = [requestSession session];
316 requestSession = nil;
318 KCJoiningRequestCircleSession* requestSecretSession = [KCJoiningRequestCircleSession sessionWithCircleDelegate:requestDelegate session:aesSession error:&error];
320 XCTAssertNotNil(requestSecretSession, @"No request secret session");
321 XCTAssertNil(error, @"Got error %@", error);
324 NSData* peerInfoMessage = [requestSecretSession initialMessage: &error];
326 XCTAssertNotNil(peerInfoMessage, @"No peerInfo message");
327 XCTAssertNil(error, @"Got error %@", error);
329 XCTAssertEqualObjects(requestDelegate.accountCode, acceptDelegate.codeToUse, @"Code made it");
332 NSData* blobMessage = [acceptSession processMessage:peerInfoMessage error: &error];
334 XCTAssertNotNil(blobMessage, @"No blob message");
335 XCTAssertNil(error, @"Got error %@", error);
337 // We have different peer_info types due to wierd linking of our tests.
338 // Compare the der representations:
339 NSData* rp_der = requestDelegate.peerInfo != nil ? (__bridge_transfer NSData*) SOSPeerInfoCopyEncodedData(requestDelegate.peerInfo, NULL, NULL) : nil;
340 NSData* ap_der = acceptDelegate.peerInfo != nil ? (__bridge_transfer NSData*) SOSPeerInfoCopyEncodedData(acceptDelegate.peerInfo, NULL, NULL) : nil;
342 XCTAssertEqualObjects(rp_der, ap_der, @"Peer infos match");
345 NSData* nothing = [requestSecretSession processMessage:blobMessage error: &error];
347 XCTAssertEqualObjects(requestDelegate.circleJoinData, acceptDelegate.circleJoinData);
349 XCTAssertNotNil(nothing, @"No initial message");
350 XCTAssertNil(error, @"Got error %@", error);
352 XCTAssertTrue([requestSecretSession isDone], @"requesor done");
353 XCTAssertTrue([acceptSession isDone], @"acceptor done");
357 - (void)testJoiningSessionRetry {
358 NSError* error = nil;
360 NSString* secret = @"123456";
361 NSString* code = @"987654";
363 uint64_t dsid = 0x1234567887654321;
365 KCJoiningRequestTestDelegate* requestDelegate = [KCJoiningRequestTestDelegate requestDelegateWithSecret: secret incorrectSecret:@"777888" incorrectTries:3];
366 KCJoiningRequestSecretSession *requestSession = [[KCJoiningRequestSecretSession alloc] initWithSecretDelegate:requestDelegate
368 rng:ccDRBGGetRngState()
371 NSData* initialMessage = [requestSession initialMessage: &error];
373 XCTAssertNotNil(initialMessage, @"No initial message");
374 XCTAssertNil(error, @"Got error %@", error);
376 KCJoiningAcceptTestDelegate* acceptDelegate = [KCJoiningAcceptTestDelegate acceptDelegateWithSecret:secret code:code];
377 KCJoiningAcceptSession* acceptSession = [[KCJoiningAcceptSession alloc] initWithSecretDelegate:acceptDelegate
378 circleDelegate:acceptDelegate
380 rng:ccDRBGGetRngState()
384 NSData* challenge = [acceptSession processMessage: initialMessage error: &error];
386 XCTAssertNotNil(challenge, @"No initial message");
387 XCTAssertNil(error, @"Got error %@", error);
389 NSData* response = nil;
390 NSData* verification = nil;
392 NSData* nextChallenge = challenge;
393 for (int tries = 0; tries < 4; ++tries) {
395 response = [requestSession processMessage: nextChallenge error: &error];
397 XCTAssertNotNil(response, @"No response message");
398 XCTAssertNil(error, @"Got error %@", error);
400 XCTAssertNotEqualObjects(requestDelegate.accountCode, acceptDelegate.codeToUse, @"Code should not make it");
403 verification = [acceptSession processMessage: response error: &error];
405 XCTAssertNotNil(verification, @"No verification message");
406 XCTAssertNil(error, @"Got error %@", error);
408 nextChallenge = verification;
412 NSData* doneMessage = [requestSession processMessage: verification error: &error];
414 XCTAssertNotNil(doneMessage, @"No response message");
415 XCTAssertNil(error, @"Got error %@", error);
417 XCTAssertTrue([requestSession isDone], @"SecretSession done");
418 XCTAssertFalse([acceptSession isDone], @"Unexpected accept session done");
420 KCAESGCMDuplexSession* aesSession = [requestSession session];
421 requestSession = nil;
424 KCJoiningRequestCircleSession* requestSecretSession = [KCJoiningRequestCircleSession sessionWithCircleDelegate:requestDelegate session:aesSession error:&error];
426 XCTAssertNotNil(requestSecretSession, @"No request secret session");
427 XCTAssertNil(error, @"Got error %@", error);
430 NSData* peerInfoMessage = [requestSecretSession initialMessage: &error];
432 XCTAssertNotNil(peerInfoMessage, @"No peerInfo message");
433 XCTAssertNil(error, @"Got error %@", error);
435 XCTAssertEqualObjects(requestDelegate.accountCode, acceptDelegate.codeToUse, @"Code made it");
438 NSData* blobMessage = [acceptSession processMessage:peerInfoMessage error: &error];
440 XCTAssertNotNil(blobMessage, @"No blob message");
441 XCTAssertNil(error, @"Got error %@", error);
443 // We have different peer_info types due to wierd linking of our tests.
444 // Compare the der representations:
445 NSData* rp_der = requestDelegate.peerInfo != nil ? (__bridge_transfer NSData*) SOSPeerInfoCopyEncodedData(requestDelegate.peerInfo, NULL, NULL) : nil;
446 NSData* ap_der = acceptDelegate.peerInfo != nil ? (__bridge_transfer NSData*) SOSPeerInfoCopyEncodedData(acceptDelegate.peerInfo, NULL, NULL) : nil;
448 XCTAssertEqualObjects(rp_der, ap_der, @"Peer infos match");
451 NSData* nothing = [requestSecretSession processMessage:blobMessage error: &error];
453 XCTAssertEqualObjects(requestDelegate.circleJoinData, acceptDelegate.circleJoinData);
455 XCTAssertNotNil(nothing, @"No initial message");
456 XCTAssertNil(error, @"Got error %@", error);
458 XCTAssertTrue([requestSecretSession isDone], @"requesor done");
459 XCTAssertTrue([acceptSession isDone], @"acceptor done");
463 - (void)testJoiningSessionCodeChange {
464 NSError* error = nil;
466 NSString* secret = @"123456";
467 NSString* code = @"987654";
469 uint64_t dsid = 0x1234567887654321;
471 KCJoiningRequestTestDelegate* requestDelegate = [KCJoiningRequestTestDelegate requestDelegateWithSecret: secret];
472 KCJoiningRequestSecretSession *requestSession = [[KCJoiningRequestSecretSession alloc] initWithSecretDelegate:requestDelegate
474 rng:ccDRBGGetRngState()
477 NSData* initialMessage = [requestSession initialMessage: &error];
479 XCTAssertNotNil(initialMessage, @"No initial message");
480 XCTAssertNil(error, @"Got error %@", error);
482 KCJoiningAcceptTestDelegate* acceptDelegate = [KCJoiningAcceptTestDelegate acceptDelegateWithSecrets:@[@"222222", @"3333333", secret] retries:1 code:code];
483 KCJoiningAcceptSession* acceptSession = [[KCJoiningAcceptSession alloc] initWithSecretDelegate:acceptDelegate
484 circleDelegate:acceptDelegate
486 rng:ccDRBGGetRngState()
490 NSData* challenge = [acceptSession processMessage: initialMessage error: &error];
492 XCTAssertNotNil(challenge, @"No initial message");
493 XCTAssertNil(error, @"Got error %@", error);
495 NSData* response = nil;
496 NSData* verification = nil;
498 NSData* nextChallenge = challenge;
499 for (int tries = 0; tries < 5; ++tries) {
501 response = [requestSession processMessage: nextChallenge error: &error];
503 XCTAssertNotNil(response, @"No response message");
504 XCTAssertNil(error, @"Got error %@", error);
506 XCTAssertNotEqualObjects(requestDelegate.accountCode, acceptDelegate.codeToUse, @"Code should not make it");
509 verification = [acceptSession processMessage: response error: &error];
511 XCTAssertNotNil(verification, @"No verification message");
512 XCTAssertNil(error, @"Got error %@", error);
514 nextChallenge = verification;
518 NSData* doneMessage = [requestSession processMessage: verification error: &error];
520 XCTAssertNotNil(doneMessage, @"No response message");
521 XCTAssertNil(error, @"Got error %@", error);
523 XCTAssertTrue([requestSession isDone], @"SecretSession done");
524 XCTAssertFalse([acceptSession isDone], @"Unexpected accept session done");
526 KCAESGCMDuplexSession* aesSession = [requestSession session];
527 requestSession = nil;
530 KCJoiningRequestCircleSession* requestSecretSession = [KCJoiningRequestCircleSession sessionWithCircleDelegate:requestDelegate session:aesSession error:&error];
532 XCTAssertNotNil(requestSecretSession, @"No request secret session");
533 XCTAssertNil(error, @"Got error %@", error);
536 NSData* peerInfoMessage = [requestSecretSession initialMessage: &error];
538 XCTAssertNotNil(peerInfoMessage, @"No peerInfo message");
539 XCTAssertNil(error, @"Got error %@", error);
541 XCTAssertEqualObjects(requestDelegate.accountCode, acceptDelegate.codeToUse, @"Code made it");
544 NSData* blobMessage = [acceptSession processMessage:peerInfoMessage error: &error];
546 XCTAssertNotNil(blobMessage, @"No blob message");
547 XCTAssertNil(error, @"Got error %@", error);
549 // We have different peer_info types due to wierd linking of our tests.
550 // Compare the der representations:
551 NSData* rp_der = requestDelegate.peerInfo != nil ? (__bridge_transfer NSData*) SOSPeerInfoCopyEncodedData(requestDelegate.peerInfo, NULL, NULL) : nil;
552 NSData* ap_der = acceptDelegate.peerInfo != nil ? (__bridge_transfer NSData*) SOSPeerInfoCopyEncodedData(acceptDelegate.peerInfo, NULL, NULL) : nil;
554 XCTAssertEqualObjects(rp_der, ap_der, @"Peer infos match");
557 NSData* nothing = [requestSecretSession processMessage:blobMessage error: &error];
559 XCTAssertEqualObjects(requestDelegate.circleJoinData, acceptDelegate.circleJoinData);
561 XCTAssertNotNil(nothing, @"No initial message");
562 XCTAssertNil(error, @"Got error %@", error);
564 XCTAssertTrue([requestSecretSession isDone], @"requesor done");
565 XCTAssertTrue([acceptSession isDone], @"acceptor done");