@interface SFAnalytics (Internal)
- (void)logMetric:(NSNumber*)metric withName:(NSString*)metricName oncePerReport:(BOOL)once;
++ (NSString*)hwModelID;
@end
#import <utilities/SecCoreAnalytics.h>
+#if TARGET_OS_OSX
+#include <sys/sysctl.h>
+#else
+#import <sys/utsname.h>
+#endif
+
// SFAnalyticsDefines constants
NSString* const SFAnalyticsTableSuccessCount = @"success_count";
NSString* const SFAnalyticsTableHardFailures = @"hard_failures";
NSString* const SFAnalyticsColumnSampleValue = @"value";
NSString* const SFAnalyticsColumnSampleName = @"name";
+NSString* const SFAnalyticsPostTime = @"postTime";
NSString* const SFAnalyticsEventTime = @"eventTime";
NSString* const SFAnalyticsEventType = @"eventType";
NSString* const SFAnalyticsEventTypeErrorEvent = @"errorEvent";
// Local constants
NSString* const SFAnalyticsEventBuild = @"build";
NSString* const SFAnalyticsEventProduct = @"product";
+NSString* const SFAnalyticsEventModelID = @"modelid";
NSString* const SFAnalyticsEventInternal = @"internal";
const NSTimeInterval SFAnalyticsSamplerIntervalOncePerReport = -1.0;
return result;
}
++ (NSString*)hwModelID
+{
+ static NSString *hwModel = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+#if TARGET_OS_SIMULATOR
+ // Asking for a real value in the simulator gives the results for the underlying mac. Not particularly useful.
+ hwModel = [NSString stringWithFormat:@"%s", getenv("SIMULATOR_MODEL_IDENTIFIER")];
+#elif TARGET_OS_OSX
+ size_t size;
+ sysctlbyname("hw.model", NULL, &size, NULL, 0);
+ char *sysctlString = malloc(size);
+ sysctlbyname("hw.model", sysctlString, &size, NULL, 0);
+ hwModel = [[NSString alloc] initWithUTF8String:sysctlString];
+ free(sysctlString);
+#else
+ struct utsname systemInfo;
+ uname(&systemInfo);
+
+ hwModel = [NSString stringWithCString:systemInfo.machine
+ encoding:NSUTF8StringEncoding];
+#endif
+ });
+ return hwModel;
+}
+ (void)addOSVersionToEvent:(NSMutableDictionary*)eventDict {
static dispatch_once_t onceToken;
static NSString *build = NULL;
static NSString *product = NULL;
+ static NSString *modelID = nil;
static BOOL internal = NO;
dispatch_once(&onceToken, ^{
NSDictionary *version = CFBridgingRelease(_CFCopySystemVersionDictionary());
build = version[(__bridge NSString *)_kCFSystemVersionBuildVersionKey];
product = version[(__bridge NSString *)_kCFSystemVersionProductNameKey];
internal = os_variant_has_internal_diagnostics("com.apple.security");
+
+ modelID = [self hwModelID];
});
if (build) {
eventDict[SFAnalyticsEventBuild] = build;
if (product) {
eventDict[SFAnalyticsEventProduct] = product;
}
+ if (modelID) {
+ eventDict[SFAnalyticsEventModelID] = modelID;
+ }
if (internal) {
eventDict[SFAnalyticsEventInternal] = @YES;
}
<dict>
<key>KeySyncTopic</key>
<dict>
+ <key>uploadSizeLimit</key>
+ <real>1000000</real>
<key>splunk_allowInsecureCertificate</key>
<false/>
<key>splunk_topic</key>
</dict>
<key>CloudServicesTopic</key>
<dict>
+ <key>uploadSizeLimit</key>
+ <real>1000000</real>
<key>splunk_allowInsecureCertificate</key>
<false/>
<key>splunk_topic</key>
</dict>
<key>TrustTopic</key>
<dict>
+ <key>uploadSizeLimit</key>
+ <real>1000000</real>
<key>splunk_allowInsecureCertificate</key>
<false/>
<key>splunk_topic</key>
</dict>
<key>TransparencyTopic</key>
<dict>
+ <key>uploadSizeLimit</key>
+ <real>10000</real>
<key>splunk_allowInsecureCertificate</key>
<false/>
<key>splunk_topic</key>
extern NSString* const SFAnalyticsColumnSampleValue;
extern NSString* const SFAnalyticsColumnSampleName;
+extern NSString* const SFAnalyticsPostTime;
extern NSString* const SFAnalyticsEventTime;
extern NSString* const SFAnalyticsEventType;
extern NSString* const SFAnalyticsEventTypeErrorEvent;
NSError *localError = nil;
CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair];
CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init];
+
+ secnotice("followup", "Posting a follow up (for SOS) of type repair");
[cdpd postFollowUpWithContext:context error:&localError ];
secnotice("cjr", "account is icdp");
if(localError){
CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init];
NSError *localError = nil;
CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair];
+
+ secnotice("followup", "Posting a follow up (for SOS) of type repair");
[cdpd postFollowUpWithContext:context error:&localError ];
if(localError){
secnotice("cjr", "request to CoreCDP to follow up failed: %@", localError);
NSError *localError = nil;
CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init];
CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair];
+
+ secnotice("followup", "Posting a follow up (for SOS) of type repair");
[cdpd postFollowUpWithContext:context error:&localError ];
if(localError){
secnotice("cjr", "request to CoreCDP to follow up failed: %@", localError);
NSError *localError = nil;
CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init];
CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair];
+
+ secnotice("followup", "Posting a follow up (for SOS) of type repair");
[cdpd postFollowUpWithContext:context error:&localError ];
if(localError){
secnotice("cjr", "request to CoreCDP to follow up failed: %@", localError);
#import "KCJoiningSession.h"
@interface KCJoiningAcceptSession (Internal)
+
+- (KCAESGCMDuplexSession*)accessSession;
+
-(void)setControlObject:(OTControl*)control;
- (void)setConfiguration:(OTJoiningConfiguration *)config;
@end
self->_joiningConfiguration = [[OTJoiningConfiguration alloc]initWithProtocolType:@"OctagonPiggybacking"
uniqueDeviceID:@"acceptor-deviceid"
uniqueClientID:@"requester-deviceid"
+ pairingUUID:[[NSUUID UUID] UUIDString]
containerName:nil
contextID:OTDefaultContext
epoch:0
}
#endif
+- (NSData*) createTLKRequestResponse: (NSError**) error {
+ NSError* localError = NULL;
+ NSData* initialSync = [self.circleDelegate circleGetInitialSyncViews:kSOSInitialSyncFlagTLKs error:&localError];
+ if (!initialSync) {
+ secnotice("joining", "Failed to get initial sync view: %@", localError);
+ if ( error!=NULL && localError != NULL )
+ *error = localError;
+ return nil;
+ }
+
+ NSData* encryptedOutgoing = [self.session encrypt:initialSync error:&localError];
+ if (!encryptedOutgoing) {
+ secnotice("joining", "TLK request failed to encrypt: %@", localError);
+ if ( error!=NULL && localError != NULL )
+ *error = localError;
+ return nil;
+ }
+ self->_state = kAcceptDone;
+
+ secnotice("joining", "TLKRequest done.");
+
+ return [[KCJoiningMessage messageWithType:kTLKRequest
+ data:encryptedOutgoing
+ error:error] der];
+}
+
- (NSData*) processApplication: (KCJoiningMessage*) message error:(NSError**) error {
+
+ if ([message type] == kTLKRequest) {
+ return [self createTLKRequestResponse: error];
+ }
+
if ([message type] != kPeerInfo) {
KCJoiningErrorCreate(kUnexpectedMessage, error, @"Expected peerInfo!");
return nil;
{
self.joiningConfiguration = config;
}
+
+- (KCAESGCMDuplexSession*)accessSession
+{
+ return self.session;
+}
#endif
@end
kPeerInfo = 4,
kCircleBlob = 5,
+ kTLKRequest = 6,
+
kError = 0,
kUnknown = 255,
- (void)setControlObject:(OTControl *)control{
self.otControl = control;
}
-- (void)setJoiningConfigurationObject:(OTJoiningConfiguration *)joiningConfiguration
+- (void)setContextIDOnJoiningConfiguration:(NSString*)contextID
{
- self.joiningConfiguration = joiningConfiguration;
+ self.joiningConfiguration.contextID = contextID;
+}
+- (KCAESGCMDuplexSession*)accessSession
+{
+ return self.session;
}
#endif
OTSponsorToApplicantRound2M2 *voucher = pairingMessage.voucher;
//handle voucher message then join octagon
- [self.otControl rpcJoinWithConfiguration:self.joiningConfiguration vouchData:voucher.voucher vouchSig:voucher.voucherSignature preapprovedKeys:voucher.preapprovedKeys reply:^(NSError * _Nullable err) {
+ [self.otControl rpcJoinWithConfiguration:self.joiningConfiguration
+ vouchData:voucher.voucher
+ vouchSig:voucher.voucherSignature
+ reply:^(NSError * _Nullable err) {
if(err){
secerror("octagon: error joining octagon: %@", err);
localError = err;
@property (readwrite) NSData* challenge;
@property (readwrite) NSData* salt;
#if OCTAGON
-@property (nonatomic, strong) OTJoiningConfiguration* joiningConfiguration;
+@property (readwrite) NSString* sessionUUID;
+
@property (nonatomic, strong) OTControl *otControl;
#endif
@property (nonatomic, strong) NSMutableDictionary *defaults;
}
self->_session = [KCAESGCMDuplexSession sessionAsSender:key context:self.dsid];
- self.session.pairingUUID = self.joiningConfiguration.pairingUUID;
+ self.session.pairingUUID = self.sessionUUID;
self.session.piggybackingVersion = self.piggy_version;
return self.session != nil;
if(self.piggy_version == kPiggyV2){
OTPairingMessage* pairingMessage = [[OTPairingMessage alloc]initWithData: [message secondData]];
-
- if(pairingMessage.epoch.epoch){
- secnotice("octagon", "received epoch");
+ if(pairingMessage.hasEpoch) {
+ secnotice("octagon", "received epoch message: %@", [pairingMessage.epoch dictionaryRepresentation]);
self.epoch = pairingMessage.epoch.epoch;
}
else{
#if OCTAGON
self->_piggy_version = KCJoiningOctagonPiggybackingEnabled() ? kPiggyV2 : kPiggyV1;
self->_otControl = [OTControl controlObject:true error:error];
- self->_joiningConfiguration = [[OTJoiningConfiguration alloc]initWithProtocolType:OTProtocolPiggybacking
- uniqueDeviceID:@"requester-id"
- uniqueClientID:@"requester-id"
- containerName:nil
- contextID:OTDefaultContext
- epoch:0
- isInitiator:true];
+
+ _sessionUUID = [[NSUUID UUID] UUIDString];
#else
self->_piggy_version = kPiggyV1;
#endif
- secnotice("joining", "joining: initWithSecretDelegate called, uuid=%@", self.joiningConfiguration.pairingUUID);
+ secnotice("joining", "joining: initWithSecretDelegate called, uuid=%@", self.sessionUUID);
NSString* name = [NSString stringWithFormat: @"%llu", dsid];
{
self.otControl = control;
}
-
-- (void)setConfiguration:(OTJoiningConfiguration *)config
-{
- self.joiningConfiguration = config;
-}
#endif
@end
@interface KCJoiningRequestSecretSession (Internal)
- (void)setControlObject:(OTControl*)control;
-- (void)setConfiguration:(OTJoiningConfiguration *)config;
@end
@interface KCJoiningRequestCircleSession (Internal)
+- (KCAESGCMDuplexSession*)accessSession;
+
- (void)setControlObject:(OTControl*)control;
-- (void)setJoiningConfigurationObject:(OTJoiningConfiguration *)config;
+- (void)setContextIDOnJoiningConfiguration:(NSString*)contextID;
@end
#endif /* Header_h */
#endif
- (void)setOctagonMessageFailForTesting:(BOOL)value;
+ (bool)isSupportedPlatform;
- (void)setSessionSupportsOctagonForTesting:(bool)value;
+
++ (NSData *)pairingChannelCompressData:(NSData *)data;
++ (NSData *)pairingChannelDecompressData:(NSData *)data;
+
@end
#import "keychain/ot/OctagonControlServer.h"
#import "keychain/ot/OTJoiningConfiguration.h"
#import "keychain/ot/proto/generated_source/OTPairingMessage.h"
-#import "keychain/ot/proto/generated_source/OTSOSMessage.h"
#import "keychain/ot/proto/generated_source/OTApplicantToSponsorRound2M1.h"
#import "keychain/ot/proto/generated_source/OTSponsorToApplicantRound2M2.h"
#import "keychain/ot/proto/generated_source/OTSponsorToApplicantRound1M2.h"
@property (assign) bool initiator;
@property (assign) unsigned counter;
@property (assign) bool acceptorWillSendInitialSyncCredentials;
+@property (assign) uint32_t acceptorInitialSyncCredentialsFlags;
@property (strong) NSXPCConnection *connection;
@property (strong) OTControl *otControl;
@property (strong) NSString* contextID;
_joiningConfiguration = [[OTJoiningConfiguration alloc]initWithProtocolType:OTProtocolPairing
uniqueDeviceID:peerVersionContext.uniqueDeviceID
uniqueClientID:peerVersionContext.uniqueClientID
+ pairingUUID:[[NSUUID UUID] UUIDString]
containerName:nil
contextID:OTDefaultContext
epoch:0
const compression_algorithm pairingCompression = COMPRESSION_LZFSE;
#define EXTRA_SIZE 100
-- (NSData *)compressData:(NSData *)data
++ (NSData *)pairingChannelCompressData:(NSData *)data
{
NSMutableData *scratch = [NSMutableData dataWithLength:compression_encode_scratch_buffer_size(pairingCompression)];
return o;
}
-- (NSData *)decompressData:(NSData *)data
++ (NSData *)pairingChannelDecompressData:(NSData *)data
{
NSMutableData *scratch = [NSMutableData dataWithLength:compression_decode_scratch_buffer_size(pairingCompression)];
OTSponsorToApplicantRound2M2 *voucher = pairingMessage.voucher;
//handle voucher and join octagon
- [self.otControl rpcJoinWithConfiguration:self.joiningConfiguration vouchData:voucher.voucher vouchSig:voucher.voucherSignature preapprovedKeys:voucher.preapprovedKeys reply:^(NSError *error) {
+ [self.otControl rpcJoinWithConfiguration:self.joiningConfiguration
+ vouchData:voucher.voucher
+ vouchSig:voucher.voucherSignature
+ reply:^(NSError *error) {
if (error || self.testFailOctagon) {
secerror("ot-pairing: failed to create %d message: %@", self.counter, error);
complete(true, NULL, error);
}else{
secnotice(pairingScope, "initiatorThirdPacket successfully joined Octagon");
typeof(self) strongSelf = weakSelf;
- if(OctagonPlatformSupportsSOS() && strongSelf->_acceptorWillSendInitialSyncCredentials == true) {
+ if(OctagonPlatformSupportsSOS() && strongSelf->_acceptorWillSendInitialSyncCredentials) {
strongSelf.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
[weakSelf initiatorFourthPacket:nsdata complete:kscomplete];
};
if (self.sessionSupportsSOS && indata[@"d"]) {
secnotice("pairing", "acceptor initialSyncCredentials requested");
self.acceptorWillSendInitialSyncCredentials = true;
+ self.acceptorInitialSyncCredentialsFlags =
+ SOSControlInitialSyncFlagTLK|
+ SOSControlInitialSyncFlagPCS|
+ SOSControlInitialSyncFlagBluetoothMigration;
+
}
if (indata[@"o"] == nil) {
response.voucher = [[OTSponsorToApplicantRound2M2 alloc] init];
response.voucher.voucher = voucher;
response.voucher.voucherSignature = voucherSig;
+
+ if (self.acceptorWillSendInitialSyncCredentials) {
+ // no need to share TLKs over the pairing channel, that's provided by octagon
+ self.acceptorInitialSyncCredentialsFlags &= ~(SOSControlInitialSyncFlagTLK | SOSControlInitialSyncFlagPCS);
+ }
+
reply[@"o"] = response.data;
secnotice("pairing", "acceptor reply to packet 2");
{
secnotice("pairing", "acceptor packet 3");
- const uint32_t initialSyncCredentialsFlags =
- SOSControlInitialSyncFlagTLK|
- SOSControlInitialSyncFlagPCS|
- SOSControlInitialSyncFlagBluetoothMigration;
+ const uint32_t initialSyncCredentialsFlags = self.acceptorInitialSyncCredentialsFlags;
[[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
complete(true, NULL, error);
if (inputCompressedData) {
- NSData *data = [self decompressData:inputCompressedData];
+ NSData *data = [[self class] pairingChannelDecompressData:inputCompressedData];
if (data == NULL) {
secnotice("pairing", "failed to decompress");
complete(true, NULL, NULL);
if (outdata == NULL && error)
error = error2;
if (outdata)
- compressedData = [self compressData:outdata];
+ compressedData = [[self class] pairingChannelCompressData:outdata];
if (compressedData) {
NSString *key = [NSString stringWithFormat:@"com.apple.ckks.pairing.packet-size.%s.%u",
- (void)initialSyncCredentials:(uint32_t)flags complete:(void (^)(NSArray *, NSError *))complete
{
- complete(@[], NULL);
+ // Make up a fake TLK
+ NSMutableArray<NSDictionary *> *items = [NSMutableArray array];
+ if (flags & SOSControlInitialSyncFlagTLK) {
+ NSString *tlkUUID = [[NSUUID UUID] UUIDString];
+ NSDictionary *fakeTLK = @{
+ @"class": @"inet",
+ @"agrp": @"com.apple.security.ckks",
+ @"vwht": @"PCS-master",
+ @"pdmn": @"ck",
+ @"desc": @"tlk",
+ @"srvr": @"fakeZone",
+ @"acct": tlkUUID,
+ @"path": tlkUUID,
+ @"v_Data": [NSData data],
+ };
+ [items addObject:fakeTLK];
+ }
+ complete(items, nil);
}
- (void)importInitialSyncCredentials:(NSArray *)items complete:(void (^)(bool success, NSError *))complete
{
- complete(true, NULL);
+ complete(true, nil);
}
-- (void)rpcTriggerSync:(NSArray<NSString *> *)peers complete:(void(^)(bool success, NSError *))complete
+- (void)triggerSync:(NSArray<NSString *> *)peers complete:(void(^)(bool success, NSError *))complete
{
complete(true, NULL);
}
complete(nil, nil);
}
-- (void)rpcTriggerBackup:(NSArray<NSString *> *)backupPeers complete:(void (^)(NSError *))complete {
- complete(nil);
-}
-
-- (void)rpcTriggerRingUpdate:(void (^)(NSError *))complete {
+- (void)triggerBackup:(NSArray<NSString *> *)backupPeers complete:(void (^)(NSError *))complete {
complete(nil);
}
#include <CommonCrypto/CommonRandomSPI.h>
-__unused static SOSFullPeerInfoRef SOSNSFullPeerInfoCreate(NSDictionary* gestalt,
- NSData* backupKey, SecKeyRef signingKey,
- SecKeyRef octagonSigningKey,
- SecKeyRef octagonEncryptionKey,
- NSError**error)
-{
- CFErrorRef errorRef = NULL;
-
- SOSFullPeerInfoRef result = SOSFullPeerInfoCreate(NULL, (__bridge CFDictionaryRef) gestalt, (__bridge CFDataRef) backupKey, signingKey, octagonSigningKey, octagonEncryptionKey, &errorRef);
-
- if (errorRef && error) {
- *error = (__bridge_transfer NSError*) errorRef;
- errorRef = NULL;
- }
-
- return result;
-}
-
static SecKeyRef GenerateFullECKey_internal(int keySize, NSError** error)
{
SecKeyRef full_key = NULL;
--- /dev/null
+#import <XCTest/XCTest.h>
+
+#import <Foundation/Foundation.h>
+
+#import <KeychainCircle/KCJoiningSession.h>
+#import <KeychainCircle/KCAccountKCCircleDelegate.h>
+#import <KeychainCircle/KCError.h>
+#import <KeychainCircle/KCJoiningMessages.h>
+#import <KeychainCircle/NSError+KCCreationHelpers.h>
+#import <KeychainCircle/KCAESGCMDuplexSession.h>
+
+#include <Security/SecBase.h>
+#include "keychain/SecureObjectSync/SOSFullPeerInfo.h"
+#include "keychain/SecureObjectSync/SOSPeerInfoInternal.h"
+
+#include <CommonCrypto/CommonRandomSPI.h>
+
+
+static SecKeyRef GenerateFullECKey_internal(int keySize, NSError** error)
+{
+ SecKeyRef full_key = NULL;
+
+ NSDictionary* keygen_parameters = @{ (__bridge NSString*)kSecAttrKeyType:(__bridge NSString*) kSecAttrKeyTypeEC,
+ (__bridge NSString*)kSecAttrKeySizeInBits: [NSNumber numberWithInt: keySize] };
+
+
+ (void) OSStatusError(SecKeyGeneratePair((__bridge CFDictionaryRef)keygen_parameters, NULL, &full_key), error, @"Generate Key failed");
+
+ return full_key;
+}
+
+static SecKeyRef GenerateFullECKey(int keySize, NSError** error) {
+ return GenerateFullECKey_internal(keySize, error);
+}
+
+static NSData* createTlkRequestMessage (KCAESGCMDuplexSession* aesSession) {
+ char someData[] = {1,2,3,4,5,6};
+ NSError* error = NULL;
+ NSData* rndPadding = [NSData dataWithBytes:(void*)someData length:sizeof(someData)];
+ KCJoiningMessage* tlkRequestMessage = [KCJoiningMessage messageWithType: kTLKRequest data:rndPadding error:&error];
+ return [tlkRequestMessage der];
+}
+
+@interface KCJoiningRequestTestDelegate : NSObject <KCJoiningRequestSecretDelegate, KCJoiningRequestCircleDelegate>
+@property (readwrite) NSString* sharedSecret;
+
+@property (readonly) NSString* accountCode;
+@property (readonly) NSData* circleJoinData;
+
+@property (readwrite) NSString* incorrectSecret;
+@property (readwrite) int incorrectTries;
+
+
++ (id) requestDelegateWithSecret:(NSString*) secret;
+- (id) init NS_UNAVAILABLE;
+- (id) initWithSecret: (NSString*) secret
+ incorrectSecret: (NSString*) wrongSecret
+ incorrectTries: (int) retries NS_DESIGNATED_INITIALIZER;
+- (NSString*) secret;
+- (NSString*) verificationFailed: (bool) codeChanged;
+- (SOSPeerInfoRef) copyPeerInfoError: (NSError**) error;
+- (bool) processCircleJoinData: (NSData*) circleJoinData version:(PiggyBackProtocolVersion)version error: (NSError**)error ;
+- (bool) processAccountCode: (NSString*) accountCode error: (NSError**)error;
+
+@end
+
+@implementation KCJoiningRequestTestDelegate
+
++ (id) requestDelegateWithSecret:(NSString*) secret {
+ return [[KCJoiningRequestTestDelegate alloc] initWithSecret:secret
+ incorrectSecret:@""
+ incorrectTries:0];
+}
+
++ (id) requestDelegateWithSecret:(NSString*) secret
+ incorrectSecret:(NSString*) wrongSecret
+ incorrectTries:(int) retries {
+ return [[KCJoiningRequestTestDelegate alloc] initWithSecret:secret
+ incorrectSecret:wrongSecret
+ incorrectTries:retries];
+}
+
+
+- (id) initWithSecret: (NSString*) secret
+ incorrectSecret: (NSString*) incorrectSecret
+ incorrectTries: (int) retries {
+ if ( self = [super init] ) {
+ self.sharedSecret = secret;
+ self.incorrectSecret = incorrectSecret;
+ self.incorrectTries = retries;
+ }
+ return self;
+}
+
+- (NSString*) nextSecret {
+ if (self.incorrectTries > 0) {
+ self.incorrectTries -= 1;
+ return self.incorrectSecret;
+ }
+ return self.sharedSecret;
+}
+
+- (NSString*) secret {
+ return [self nextSecret];
+}
+
+- (NSString*) verificationFailed: (bool) codeChanged {
+ return [self nextSecret];
+}
+
+- (SOSPeerInfoRef) copyPeerInfoError: (NSError**) error {
+ return NULL;
+}
+
+- (bool) processCircleJoinData: (NSData*) circleJoinData version:(PiggyBackProtocolVersion)version error: (NSError**)error {
+ self->_circleJoinData = circleJoinData;
+ return true;
+}
+
+- (bool) processAccountCode: (NSString*) accountCode error: (NSError**)error {
+ self->_accountCode = accountCode;
+ return true;
+}
+
+@end
+
+@interface KCJoiningAcceptTestDelegate : NSObject <KCJoiningAcceptSecretDelegate, KCJoiningAcceptCircleDelegate>
+@property (readonly) NSArray<NSString*>* secrets;
+@property (readwrite) NSUInteger currentSecret;
+@property (readwrite) int retriesLeft;
+@property (readwrite) int retriesPerSecret;
+
+@property (readonly) NSString* codeToUse;
+@property (readonly) NSData* circleJoinData;
+@property (readonly) SOSPeerInfoRef peerInfo;
+
++ (id) acceptDelegateWithSecret: (NSString*) secret code: (NSString*) code;
++ (id) acceptDelegateWithSecrets: (NSArray<NSString*>*) secrets retries: (int) retries code: (NSString*) code;
+- (id) initWithSecrets: (NSArray<NSString*>*) secrets retries: (int) retries code: (NSString*) code NS_DESIGNATED_INITIALIZER;
+
+- (NSString*) secret;
+- (NSString*) accountCode;
+
+- (KCRetryOrNot) verificationFailed: (NSError**) error;
+- (NSData*) circleJoinDataFor: (SOSPeerInfoRef) peer
+ error: (NSError**) error;
+
+- (id) init NS_UNAVAILABLE;
+
+@end
+
+@implementation KCJoiningAcceptTestDelegate
+
++ (id) acceptDelegateWithSecrets: (NSArray<NSString*>*) secrets retries: (int) retries code: (NSString*) code {
+ return [[KCJoiningAcceptTestDelegate alloc] initWithSecrets:secrets retries:retries code:code];
+
+}
+
++ (id) acceptDelegateWithSecret: (NSString*) secret code: (NSString*) code {
+ return [[KCJoiningAcceptTestDelegate alloc] initWithSecret:secret code:code];
+}
+
+- (id) initWithSecret: (NSString*) secret code: (NSString*) code {
+ return [self initWithSecrets:@[secret] retries:3 code:code];
+}
+
+- (id) initWithSecrets: (NSArray<NSString*>*) secrets retries: (int) retries code: (NSString*) code {
+ self = [super init];
+
+ self->_secrets = secrets;
+ self.currentSecret = 0;
+ self->_retriesPerSecret = retries;
+ self->_retriesLeft = self.retriesPerSecret;
+
+ self->_codeToUse = code;
+
+ uint8_t joinDataBuffer[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
+ self->_circleJoinData = [NSData dataWithBytes: joinDataBuffer length: sizeof(joinDataBuffer) ];
+
+ return self;
+}
+
+- (KCRetryOrNot) advanceSecret {
+ if (self.retriesLeft == 0) {
+ self.currentSecret += 1;
+ if (self.currentSecret >= [self.secrets count]) {
+ self.currentSecret = [self.secrets count] - 1;
+ }
+ self.retriesLeft = self.retriesPerSecret;
+ return kKCRetryWithNewChallenge;
+ } else {
+ self.retriesLeft -= 1;
+ return kKCRetryWithSameChallenge;
+ }
+}
+
+- (NSString*) secret {
+ return self.secrets[self.currentSecret];
+}
+- (NSString*) accountCode {
+ return self.codeToUse;
+}
+
+- (KCRetryOrNot) verificationFailed: (NSError**) error {
+ return [self advanceSecret];
+}
+
+- (NSData*) circleJoinDataFor: (SOSPeerInfoRef) peer
+ error: (NSError**) error {
+ uint8_t joinDataBuffer[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
+
+ self->_peerInfo = peer;
+ return [NSData dataWithBytes: joinDataBuffer length: sizeof(joinDataBuffer) ];
+}
+
+-(NSData*) circleGetInitialSyncViews:(SOSInitialSyncFlags)flags error:(NSError**) error{
+ char testData[] = {0,1,2,3,4,5,6,7,8,9};
+ return [NSData dataWithBytes:testData length:sizeof(testData)];
+ //return [[KCJoiningAcceptAccountCircleDelegate delegate] circleGetInitialSyncViews:flags error:error]; //Need security entitlements!
+}
+
+@end
+
+@interface KCTLKRequestTest : XCTestCase
+
+@end
+
+@implementation KCTLKRequestTest
+
+- (void)setUp {
+ [super setUp];
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+}
+
+- (void)tearDown {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ [super tearDown];
+}
+
+- (void)testTLKRequest {
+ NSError* error = nil;
+
+ NSString* secret = @"123456";
+ NSString* code = @"987654";
+
+ uint64_t dsid = 0x1234567887654321;
+
+ KCJoiningRequestTestDelegate* requestDelegate = [KCJoiningRequestTestDelegate requestDelegateWithSecret: secret];
+ KCJoiningRequestSecretSession *requestSession = [[KCJoiningRequestSecretSession alloc] initWithSecretDelegate:requestDelegate
+ dsid:dsid
+ rng:ccDRBGGetRngState()
+ error:&error];
+
+ NSData* initialMessage = [requestSession initialMessage: &error];
+
+ XCTAssertNotNil(initialMessage, @"No initial message");
+ XCTAssertNil(error, @"Got error %@", error);
+
+ KCJoiningAcceptTestDelegate* acceptDelegate = [KCJoiningAcceptTestDelegate acceptDelegateWithSecret:secret code:code];
+ KCJoiningAcceptSession* acceptSession = [[KCJoiningAcceptSession alloc] initWithSecretDelegate:acceptDelegate
+ circleDelegate:acceptDelegate
+ dsid:dsid
+ rng:ccDRBGGetRngState()
+ error:&error];
+
+ error = nil;
+ NSData* challenge = [acceptSession processMessage: initialMessage error: &error];
+
+ XCTAssertNotNil(challenge, @"No initial message");
+ XCTAssertNil(error, @"Got error %@", error);
+
+ error = nil;
+ NSData* response = [requestSession processMessage: challenge error: &error];
+
+ XCTAssertNotNil(response, @"No response message");
+ XCTAssertNil(error, @"Got error %@", error);
+
+ error = nil;
+ NSData* verification = [acceptSession processMessage: response error: &error];
+
+ XCTAssertNotNil(verification, @"No verification message");
+ XCTAssertNil(error, @"Got error %@", error);
+
+ error = nil;
+ NSData* doneMessage = [requestSession processMessage: verification error: &error];
+
+ XCTAssertNotNil(doneMessage, @"No response message");
+ XCTAssertNil(error, @"Got error %@", error);
+
+ XCTAssertTrue([requestSession isDone], @"SecretSession done");
+ XCTAssertFalse([acceptSession isDone], @"Unexpected accept session done");
+
+ KCAESGCMDuplexSession* aesSession = [requestSession session];
+ requestSession = nil;
+
+ KCJoiningRequestCircleSession* requestSecretSession = [KCJoiningRequestCircleSession sessionWithCircleDelegate:requestDelegate session:aesSession error:&error];
+
+ XCTAssertNotNil(requestSecretSession, @"No request secret session");
+ XCTAssertNil(error, @"Got error %@", error);
+
+ NSData* tlkRequestMessage = createTlkRequestMessage(aesSession);
+ XCTAssertNotNil(tlkRequestMessage, @"No TLKRequest message");
+
+ NSData* tlkMessage = [acceptSession processMessage:tlkRequestMessage error:&error];
+ XCTAssertNotNil(tlkMessage, @"No tlkData message");
+ XCTAssertNil(error, @"Got error %@", error);
+
+ KCJoiningMessage* receivedKCJoinMessage = [KCJoiningMessage messageWithDER:tlkMessage error:&error];
+ XCTAssertNotNil(receivedKCJoinMessage, @"No receivedKCJoinMessage message");
+ XCTAssertNil(error, @"Got error %@", error);
+
+ NSData* tlkDecryptedData = [aesSession decryptAndVerify:receivedKCJoinMessage.firstData error:&error];
+ XCTAssertNotNil(tlkDecryptedData, @"No tlkDecryptedData message");
+ XCTAssertNil(error, @"Got error %@", error);
+
+ //check for tlkc content
+ NSData* initialSync = [acceptDelegate circleGetInitialSyncViews:kSOSInitialSyncFlagTLKs error:&error];
+ XCTAssertNotNil(initialSync, @"No initialSync data");
+ XCTAssertNil(error, @"Got error %@", error);
+
+ XCTAssertEqualObjects(initialSync, tlkDecryptedData, @"TLK data is different.");
+}
+@end
NSError *localError = NULL;
CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init];
CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair];
+
+ secnotice("followup", "Posting a follow up (for SOS) of type repair");
[cdpd postFollowUpWithContext:context error:&localError ];
if(localError){
secnotice("kcn", "request to CoreCDP to follow up failed: %@", localError);
<key>timeout</key>
<integer>300</integer>
</dict>
- <key>com.apple.system-extensions.admin</key>
+ <key>com.apple.tcc.util.admin</key>
+ <dict>
+ <key>comment</key>
+ <string>For modification of TCC settings.</string>
+ <key>class</key>
+ <string>rule</string>
+ <key>rule</key>
+ <string>authenticate-admin-nonshared</string>
+ <key>shared</key>
+ <false/>
+ </dict>
+ <key>com.apple.system-extensions.admin</key>
<dict>
<key>comment</key>
<string>Authorize a 3rd party application which wants to manipulate system extensions.</string>
"com.apple.configurationprofiles.userenrollment.install" = "__APPNAME__ is trying to enroll you in a remote management (MDM) service.";
"com.apple.system-extensions.admin" = "__APPNAME__ is trying to modify a System Extension.";
+
+"com.apple.tcc.util.admin" = "__APPNAME__ is trying to modify your Security & Privacy settings.";
CssmDlDbHandle *handles() const { return CssmDlDbHandle::overlay(DLDBHandle); }
CssmDlDbHandle * &handles() { return CssmDlDbHandle::overlayVar(DLDBHandle); }
- CssmDlDbHandle &operator [] (uint32 ix) const
- { assert(ix < count()); return CssmDlDbHandle::overlay(DLDBHandle[ix]); }
+ CssmDlDbHandle &operator [] (uint32 ix) const {
+ if (ix >= count()) {
+ secemergency("CssmDlDbList: attempt to index beyond bounds");
+ abort();
+ }
+ return CssmDlDbHandle::overlay(DLDBHandle[ix]);
+ }
void setDlDbList(uint32 n, CSSM_DL_DB_HANDLE *list)
{ count() = n; handles() = CssmDlDbHandle::overlay(list); }
{ return CssmDbAttributeInfo::overlayVar(AttributeInfo); }
CssmDbAttributeInfo *attributes() const
{ return CssmDbAttributeInfo::overlay(AttributeInfo); }
- CssmDbAttributeInfo &at(uint32 ix) const
- { assert(ix < size()); return attributes()[ix]; }
+ CssmDbAttributeInfo &at(uint32 ix) const {
+ if (ix >= size()) {
+ secemergency("CssmDbRecordAttributeInfo: attempt to index beyond bounds");
+ abort();
+ }
+ return attributes()[ix];
+ }
CssmDbAttributeInfo &operator [] (uint32 ix) const { return at(ix); }
};
{ return CssmDbAttributeData::overlay(AttributeData); }
// Attributes by position
- CssmDbAttributeData &at(unsigned int ix) const
- { assert(ix < size()); return attributes()[ix]; }
+ CssmDbAttributeData &at(unsigned int ix) const {
+ if (ix >= size()) {
+ secemergency("CssmDbRecordAttributeData: attempt to index beyond bounds");
+ abort();
+ }
+ return attributes()[ix];
+ }
CssmDbAttributeData &operator [] (unsigned int ix) const { return at(ix); }
CssmSelectionPredicate *predicates() const
{ return CssmSelectionPredicate::overlay(SelectionPredicate); }
- CssmSelectionPredicate &at(uint32 ix) const
- { assert(ix < size()); return predicates()[ix]; }
+ CssmSelectionPredicate &at(uint32 ix) const {
+ if (ix >= size()) {
+ secemergency("CssmDbRecordAttributeData: attempt to index beyond bounds");
+ abort();
+ }
+ return predicates()[ix];
+ }
CssmSelectionPredicate &operator[] (uint32 ix) const { return at(ix); }
// Object management.
//
DiskImageRep::DiskImageRep(const char *path)
- : SingleDiskRep(path)
+ : SingleDiskRep(path), mSigningData(NULL)
{
this->setup();
}
+DiskImageRep::~DiskImageRep()
+{
+ free((void*)mSigningData);
+}
+
void DiskImageRep::setup()
{
+ free((void*)mSigningData);
mSigningData = NULL;
// the UDIF "header" is in fact the last 512 bytes of the file, with no particular alignment
//
void DiskImageRep::Writer::flush()
{
- delete mSigningData; // ditch previous blob just in case
+ free((void*)mSigningData); // ditch previous blob just in case
mSigningData = Maker::make(); // assemble new signature SuperBlob
// write signature superblob
class DiskImageRep : public SingleDiskRep {
public:
DiskImageRep(const char *path);
+ virtual ~DiskImageRep();
CFDataRef identification();
CFDataRef component(CodeDirectory::SpecialSlot slot);
UDIFFileHeader mHeader; // disk image header (all fields NBO)
size_t mEndOfDataOffset; // end of payload data (data fork + XML)
size_t mHeaderOffset; // trailing header offset
- const EmbeddedSignatureBlob *mSigningData; // pointer to signature SuperBlob (in mapped memory)
+ const EmbeddedSignatureBlob *mSigningData; // pointer to signature SuperBlob (malloc'd memory during setup)
};
secinfo("notarization", "Registering stapled ticket with system");
#if TARGET_OS_OSX
- CFErrorRef error = NULL;
- if (!SecAssessmentTicketRegister(data, &error)) {
- secerror("Error registering stapled ticket: %@", data);
+ CFRef<CFErrorRef> error;
+ if (!SecAssessmentTicketRegister(data, &error.aref())) {
+ secerror("Error registering stapled ticket: %@", error.get());
}
#endif // TARGET_OS_OSX
}
goto lb_exit;
}
- data = CFDataCreateWithBytesNoCopy(NULL, ticketData, trailer.length, NULL);
+ data.take(makeCFDataMalloc(ticketData, trailer.length));
if (data.get() == NULL) {
secerror("unable to create cfdata for notarization");
goto lb_exit;
if (fd) {
close(fd);
}
- if (ticketData) {
- free(ticketData);
- }
}
void
goto lb_exit;
}
- data = CFDataCreateWithBytesNoCopy(NULL, ticketData, ticketLength, NULL);
+ data.take(makeCFDataMalloc(ticketData, ticketLength));
if (data.get() == NULL) {
secerror("unable to create cfdata for notarization");
goto lb_exit;
if (fd) {
close(fd);
}
- if (ticketData) {
- free(ticketData);
- }
}
void
resources.scan(^(FTSENT *ent, uint32_t ruleFlags, const std::string relpath, Rule *rule) {
bool isSymlink = (ent->fts_info == FTS_SL);
+ bool isNested = (ruleFlags & ResourceBuilder::nested);
const std::string path(ent->fts_path);
const std::string accpath(ent->fts_accpath);
this->state.mLimitedAsync->perform(groupRef, ^{
CFRef<CFMutableDictionaryRef> seal;
- if (ruleFlags & ResourceBuilder::nested) {
+ if (isNested) {
seal.take(signNested(path, relpath));
} else if (isSymlink) {
char target[PATH_MAX];
} else {
seal.take(resources.hashFile(accpath.c_str(), digestAlgorithms(), signingFlags() & kSecCSSignStrictPreflight));
}
+ if (seal.get() == NULL) {
+ secerror("Failed to generate sealed resource: %d, %d, %s", isNested, isSymlink, accpath.c_str());
+ MacOSError::throwMe(errSecCSBadResource);
+ }
if (ruleFlags & ResourceBuilder::optional)
CFDictionaryAddValue(seal, CFSTR("optional"), kCFBooleanTrue);
CFTypeRef hash;
#include "TrustSettingsSchema.h"
#include <Security/SecTrustPriv.h>
#include "utilities/array_size.h"
+#include "utilities/SecCFWrappers.h"
#include <AssertMacros.h>
#include <syslog.h>
CFTypeRef v;
Item item = Item(item_class, &attrs, 0, "");
- v = CFDictionaryGetValue(refAttributes, kSecValuePersistentRef);
+ v = CFCast(CFData, CFDictionaryGetValue(refAttributes, kSecValuePersistentRef));
if (v) {
item->setPersistentRef((CFDataRef)v);
}
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
- *
+ *
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
- *
+ *
* The Original Code is the Netscape security libraries.
- *
+ *
* The Initial Developer of the Original Code is Netscape
- * Communications Corporation. Portions created by Netscape are
+ * Communications Corporation. Portions created by Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
- *
+ *
* Contributor(s):
- *
+ *
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
- * "GPL"), in which case the provisions of the GPL are applicable
- * instead of those above. If you wish to allow use of your
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
/*
* CMS digesting.
*/
+#include <assert.h>
#include "cmslocal.h"
-#include "secitem.h"
+#include "SecAsn1Item.h"
#include "secoid.h"
#include <security_asn1/secerr.h>
-#include <Security/cssmapi.h>
+#include <security_asn1/secport.h>
+
+#include <CommonCrypto/CommonDigest.h>
#include <Security/SecCmsDigestContext.h>
-/* Return the maximum value between S and T */
+/* Return the maximum value between S and T (and U) */
#define MAX(S, T) ({__typeof__(S) _max_s = S; __typeof__(T) _max_t = T; _max_s > _max_t ? _max_s : _max_t;})
+#define MAX_OF_3(S, T, U) ({__typeof__(U) _max_st = MAX(S,T); MAX(_max_st,U);})
struct SecCmsDigestContextStr {
- Boolean saw_contents;
- int digcnt;
- CSSM_CC_HANDLE * digobjs;
+ PLArenaPool * poolp;
+ Boolean saw_contents;
+ int digcnt;
+ void ** digobjs;
+ SECAlgorithmID ** digestalgs;
};
/*
SecCmsDigestContextRef
SecCmsDigestContextStartMultiple(SECAlgorithmID **digestalgs)
{
- SecCmsDigestContextRef cmsdigcx;
- CSSM_CC_HANDLE digobj;
+ PLArenaPool *poolp;
+ SecCmsDigestContextRef cmsdigcx = NULL;
+ void * digobj;
int digcnt;
int i;
+ poolp = PORT_NewArena(1024);
+ if (poolp == NULL) {
+ goto loser;
+ }
+
digcnt = (digestalgs == NULL) ? 0 : SecCmsArrayCount((void **)digestalgs);
- cmsdigcx = (SecCmsDigestContextRef)PORT_ZAlloc(sizeof(struct SecCmsDigestContextStr));
- if (cmsdigcx == NULL)
- return NULL;
+ cmsdigcx = (SecCmsDigestContextRef)PORT_ArenaAlloc(poolp, sizeof(struct SecCmsDigestContextStr));
+ if (cmsdigcx == NULL) {
+ goto loser;
+ }
+ cmsdigcx->poolp = poolp;
if (digcnt > 0) {
/* Security check to prevent under-allocation */
- if (digcnt >= (int)(INT_MAX/sizeof(CSSM_CC_HANDLE))) {
+ if (digcnt >= (int)((INT_MAX/(MAX(sizeof(void *),sizeof(SECAlgorithmID *))))-1)) {
+ goto loser;
+ }
+ cmsdigcx->digobjs = (void**)PORT_ArenaAlloc(poolp, digcnt * sizeof(void *));
+ if (cmsdigcx->digobjs == NULL) {
+ goto loser;
+ }
+ cmsdigcx->digestalgs = (SECAlgorithmID **)PORT_ArenaZAlloc(poolp, (digcnt + 1) * sizeof(SECAlgorithmID *));
+ if (cmsdigcx->digestalgs == NULL) {
goto loser;
}
- cmsdigcx->digobjs = (CSSM_CC_HANDLE *)PORT_ZAlloc(digcnt * sizeof(CSSM_CC_HANDLE));
- if (cmsdigcx->digobjs == NULL)
- goto loser;
}
cmsdigcx->digcnt = 0;
* Create a digest object context for each algorithm.
*/
for (i = 0; i < digcnt; i++) {
- digobj = SecCmsUtilGetHashObjByAlgID(digestalgs[i]);
- /*
- * Skip any algorithm we do not even recognize; obviously,
- * this could be a problem, but if it is critical then the
- * result will just be that the signature does not verify.
- * We do not necessarily want to error out here, because
- * the particular algorithm may not actually be important,
- * but we cannot know that until later.
- */
- if (digobj)
- {
- CSSM_RETURN result;
- result = CSSM_DigestDataInit(digobj);
- if (result != CSSM_OK)
- {
- goto loser;
- }
+ digobj = SecCmsUtilGetHashObjByAlgID(digestalgs[i]);
+ /*
+ * Skip any algorithm we do not even recognize; obviously,
+ * this could be a problem, but if it is critical then the
+ * result will just be that the signature does not verify.
+ * We do not necessarily want to error out here, because
+ * the particular algorithm may not actually be important,
+ * but we cannot know that until later.
+ */
+
+ cmsdigcx->digobjs[cmsdigcx->digcnt] = digobj;
+ cmsdigcx->digestalgs[cmsdigcx->digcnt] = PORT_ArenaAlloc(poolp, sizeof(SECAlgorithmID));
+ if (SECITEM_CopyItem(poolp,
+ &(cmsdigcx->digestalgs[cmsdigcx->digcnt]->algorithm),
+ &(digestalgs[i]->algorithm))
+ || SECITEM_CopyItem(poolp,
+ &(cmsdigcx->digestalgs[cmsdigcx->digcnt]->parameters),
+ &(digestalgs[i]->parameters))) {
+ goto loser;
}
-
- cmsdigcx->digobjs[cmsdigcx->digcnt] = digobj;
- cmsdigcx->digcnt++;
+ cmsdigcx->digcnt++;
}
cmsdigcx->saw_contents = PR_FALSE;
return cmsdigcx;
loser:
- if (cmsdigcx) {
- if (cmsdigcx->digobjs) {
- PORT_Free(cmsdigcx->digobjs);
- cmsdigcx->digobjs = NULL;
- cmsdigcx->digcnt = 0;
- }
+ if (poolp) {
+ PORT_FreeArena(poolp, PR_FALSE);
+ }
+ if (cmsdigcx && cmsdigcx->digobjs) {
+ PORT_Free(cmsdigcx->digobjs);
+ cmsdigcx->digobjs = NULL;
+ cmsdigcx->digcnt = 0;
}
+
return NULL;
}
SecCmsDigestContextRef
SecCmsDigestContextStartSingle(SECAlgorithmID *digestalg)
{
- SECAlgorithmID *digestalgs[] = { NULL, NULL }; /* fake array */
+ SECAlgorithmID *digestalgs[] = { NULL, NULL }; /* fake array */
digestalgs[0] = digestalg;
return SecCmsDigestContextStartMultiple(digestalgs);
void
SecCmsDigestContextUpdate(SecCmsDigestContextRef cmsdigcx, const unsigned char *data, size_t len)
{
- CSSM_DATA dataBuf;
+ SecAsn1Item dataBuf;
int i;
dataBuf.Length = len;
- dataBuf.Data = (uint8 *)data;
+ dataBuf.Data = (uint8_t *)data;
cmsdigcx->saw_contents = PR_TRUE;
- for (i = 0; i < cmsdigcx->digcnt; i++)
- if (cmsdigcx->digobjs && cmsdigcx->digobjs[i])
- CSSM_DigestDataUpdate(cmsdigcx->digobjs[i], &dataBuf, 1);
+ for (i = 0; i < cmsdigcx->digcnt; i++) {
+ if (cmsdigcx->digobjs[i]) {
+ /* 64 bits cast: worst case is we truncate the length and we dont hash all the data.
+ This may cause an invalid CMS blob larger than 4GB to be validated. Unlikely, but
+ possible security issue. There is no way to return an error here, but a check at
+ the upper level may happen. */
+ /*
+ rdar://problem/20642513
+ Let's just die a horrible death rather than have the security issue.
+ CMS blob over 4GB? Oh well.
+ */
+ if (len > UINT32_MAX) {
+ /* Ugh. */
+ abort();
+ }
+ assert(len<=UINT32_MAX); /* Debug check. Correct as long as CC_LONG is uint32_t */
+ switch (SECOID_GetAlgorithmTag(cmsdigcx->digestalgs[i])) {
+ case SEC_OID_SHA1: CC_SHA1_Update((CC_SHA1_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+ case SEC_OID_MD5: CC_MD5_Update((CC_MD5_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+ case SEC_OID_SHA224: CC_SHA224_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+ case SEC_OID_SHA256: CC_SHA256_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+ case SEC_OID_SHA384: CC_SHA384_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+ case SEC_OID_SHA512: CC_SHA512_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+ default:
+ break;
+ }
+ }
+ }
}
/*
{
int i;
- for (i = 0; i < cmsdigcx->digcnt; i++)
+ for (i = 0; i < cmsdigcx->digcnt; i++) {
if (cmsdigcx->digobjs && cmsdigcx->digobjs[i]) {
- CSSM_DeleteContext(cmsdigcx->digobjs[i]);
- cmsdigcx->digobjs[i] = 0;
+ free(cmsdigcx->digobjs[i]);
+ cmsdigcx->digobjs[i] = NULL;
}
+ }
+
+ PORT_FreeArena(cmsdigcx->poolp, PR_TRUE);
}
/*
- * SecCmsDigestContextFinishMultiple - finish the digests and put them
- * into an array of CSSM_DATAs (allocated on poolp)
+ * SecCmsDigestContextFinishMultiple - finish the digests
+ * Note that on macOS, this call cancels and frees the digest context (because the digests are allocated from an input arena pool).
+ * The iOS version only frees the digest objects and requires a call to SecCmsDisgestContextDestroy
+ * or SecCmsDisgestContextCancel (because the digests are allocated out of the context's pool).
*/
OSStatus
-SecCmsDigestContextFinishMultiple(SecCmsDigestContextRef cmsdigcx, SecArenaPoolRef poolp,
- CSSM_DATA_PTR **digestsp)
+SecCmsDigestContextFinishMultiple(SecCmsDigestContextRef cmsdigcx,
+ SecArenaPoolRef poolp,
+ SecAsn1Item * **digestsp)
{
- CSSM_CC_HANDLE digobj;
- CSSM_DATA_PTR *digests, digest;
+ void * digobj;
+ SecAsn1Item **digests, *digest;
int i;
void *mark;
OSStatus rv = SECFailure;
+ assert(cmsdigcx != NULL);
+
/* no contents? do not update digests */
if (digestsp == NULL || !cmsdigcx->saw_contents) {
- for (i = 0; i < cmsdigcx->digcnt; i++)
+ for (i = 0; i < cmsdigcx->digcnt; i++) {
if (cmsdigcx->digobjs && cmsdigcx->digobjs[i]) {
- CSSM_DeleteContext(cmsdigcx->digobjs[i]);
- cmsdigcx->digobjs[i] = 0;
+ free(cmsdigcx->digobjs[i]);
}
- rv = SECSuccess;
- goto cleanup;
+ }
+ rv = SECSuccess;
+ goto cleanup;
}
+ assert(digestsp != NULL);
+
mark = PORT_ArenaMark ((PLArenaPool *)poolp);
/* Security check to prevent under-allocation */
- if (cmsdigcx->digcnt >= (int)((INT_MAX/(MAX(sizeof(CSSM_DATA_PTR),sizeof(CSSM_DATA))))-1)) {
+ if (cmsdigcx->digcnt >= (int)((INT_MAX/(MAX(sizeof(SecAsn1Item *),sizeof(SecAsn1Item))))-1)) {
goto loser;
}
- /* allocate digest array & CSSM_DATAs on arena */
- digests = (CSSM_DATA_PTR *)PORT_ArenaAlloc((PLArenaPool *)poolp, (cmsdigcx->digcnt+1) * sizeof(CSSM_DATA_PTR));
- digest = (CSSM_DATA_PTR)PORT_ArenaZAlloc((PLArenaPool *)poolp, cmsdigcx->digcnt * sizeof(CSSM_DATA));
+ /* allocate digest array & SecAsn1Items on arena */
+ digests = (SecAsn1Item * *)PORT_ArenaZAlloc((PLArenaPool *)poolp, (cmsdigcx->digcnt+1) * sizeof(SecAsn1Item *));
+ digest = (SecAsn1Item *)PORT_ArenaZAlloc((PLArenaPool *)poolp, cmsdigcx->digcnt * sizeof(SecAsn1Item));
if (digests == NULL || digest == NULL) {
- goto loser;
+ goto loser;
}
for (i = 0; i < cmsdigcx->digcnt; i++, digest++) {
- if (cmsdigcx->digobjs) {
- digobj = cmsdigcx->digobjs[i];
- } else {
- digobj = 0;
+ SECOidTag hash_alg = SECOID_GetAlgorithmTag(cmsdigcx->digestalgs[i]);
+ int diglength = 0;
+
+ switch (hash_alg) {
+ case SEC_OID_SHA1: diglength = CC_SHA1_DIGEST_LENGTH; break;
+ case SEC_OID_MD5: diglength = CC_MD5_DIGEST_LENGTH; break;
+ case SEC_OID_SHA224: diglength = CC_SHA224_DIGEST_LENGTH; break;
+ case SEC_OID_SHA256: diglength = CC_SHA256_DIGEST_LENGTH; break;
+ case SEC_OID_SHA384: diglength = CC_SHA384_DIGEST_LENGTH; break;
+ case SEC_OID_SHA512: diglength = CC_SHA512_DIGEST_LENGTH; break;
+ default: goto loser;
}
- CSSM_QUERY_SIZE_DATA dataSize;
- rv = CSSM_QuerySize(digobj, CSSM_FALSE, 1, &dataSize);
- if (rv != CSSM_OK)
- {
- goto loser;
- }
-
- int diglength = dataSize.SizeOutputBlock;
-
- if (digobj)
- {
- digest->Data = (unsigned char*)PORT_ArenaAlloc((PLArenaPool *)poolp, diglength);
- if (digest->Data == NULL)
- goto loser;
- digest->Length = diglength;
- rv = CSSM_DigestDataFinal(digobj, digest);
- if (rv != CSSM_OK)
- {
+ digobj = cmsdigcx->digobjs[i];
+ if (digobj) {
+ digest->Data = (unsigned char*)PORT_ArenaAlloc((PLArenaPool *)poolp, diglength);
+ if (digest->Data == NULL)
goto loser;
+ digest->Length = diglength;
+ switch (hash_alg) {
+ case SEC_OID_SHA1: CC_SHA1_Final(digest->Data, digobj); break;
+ case SEC_OID_MD5: CC_MD5_Final(digest->Data, digobj); break;
+ case SEC_OID_SHA224: CC_SHA224_Final(digest->Data, digobj); break;
+ case SEC_OID_SHA256: CC_SHA256_Final(digest->Data, digobj); break;
+ case SEC_OID_SHA384: CC_SHA384_Final(digest->Data, digobj); break;
+ case SEC_OID_SHA512: CC_SHA512_Final(digest->Data, digobj); break;
+ default: goto loser;
}
-
- CSSM_DeleteContext(digobj);
- cmsdigcx->digobjs[i] = 0;
- }
- else
- {
- digest->Data = NULL;
- digest->Length = 0;
- }
-
- digests[i] = digest;
- }
+
+ free(digobj);
+ digests[i] = digest;
+ } else {
+ digest->Data = NULL;
+ digest->Length = 0;
+ }
+ }
digests[i] = NULL;
*digestsp = digests;
rv = SECSuccess;
loser:
- if (rv == SECSuccess)
- PORT_ArenaUnmark((PLArenaPool *)poolp, mark);
- else
- PORT_ArenaRelease((PLArenaPool *)poolp, mark);
+ if (rv == SECSuccess) {
+ PORT_ArenaUnmark((PLArenaPool *)poolp, mark);
+ } else {
+ PORT_ArenaRelease((PLArenaPool *)poolp, mark);
+ }
cleanup:
- if (cmsdigcx->digcnt > 0) {
- SecCmsDigestContextCancel(cmsdigcx);
- PORT_Free(cmsdigcx->digobjs);
- cmsdigcx->digobjs = NULL;
- cmsdigcx->digcnt = 0;
- }
- PORT_Free(cmsdigcx);
+ cmsdigcx->digcnt = 0; // We've already freed the digests above
+ SecCmsDigestContextCancel(cmsdigcx);
return rv;
}
*/
OSStatus
SecCmsDigestContextFinishSingle(SecCmsDigestContextRef cmsdigcx, SecArenaPoolRef poolp,
- CSSM_DATA_PTR digest)
+ SecAsn1Item * digest)
{
OSStatus rv = SECFailure;
- CSSM_DATA_PTR *dp;
+ SecAsn1Item **dp;
PLArenaPool *arena = NULL;
if ((arena = PORT_NewArena(1024)) == NULL)
goto loser;
/* get the digests into arena, then copy the first digest into poolp */
- if (SecCmsDigestContextFinishMultiple(cmsdigcx, (SecArenaPoolRef)arena, &dp) != SECSuccess)
- goto loser;
+ if (SecCmsDigestContextFinishMultiple(cmsdigcx, (SecArenaPoolRef)arena, &dp) != SECSuccess) {
+ goto loser;
+ }
/* now copy it into poolp */
- if (SECITEM_CopyItem((PLArenaPool *)poolp, digest, dp[0]) != SECSuccess)
- goto loser;
+ if (SECITEM_CopyItem((PLArenaPool *)poolp, digest, dp[0]) != SECSuccess) {
+ goto loser;
+ }
rv = SECSuccess;
loser:
- if (arena)
- PORT_FreeArena(arena, PR_FALSE);
-
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
return rv;
}
extern int
SecCmsAlgArrayGetIndexByAlgTag(SECAlgorithmID **algorithmArray, SECOidTag algtag);
-extern CSSM_CC_HANDLE
+extern void *
SecCmsUtilGetHashObjByAlgID(SECAlgorithmID *algid);
/*
#include <Security/cssmapi.h>
#include <Security/cssmapple.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
+#include <CommonCrypto/CommonDigest.h>
/*
return i;
}
-CSSM_CC_HANDLE
+void *
SecCmsUtilGetHashObjByAlgID(SECAlgorithmID *algid)
{
SECOidData *oidData = SECOID_FindOID(&(algid->algorithm));
if (oidData)
{
- CSSM_ALGORITHMS alg = oidData->cssmAlgorithm;
- if (alg)
- {
- CSSM_CC_HANDLE digobj;
- CSSM_CSP_HANDLE cspHandle = SecCspHandleForAlgorithm(alg);
-
- if (!CSSM_CSP_CreateDigestContext(cspHandle, alg, &digobj))
- return digobj;
- }
+ void *digobj = NULL;
+ switch (oidData->offset) {
+ case SEC_OID_SHA1:
+ digobj = calloc(1, sizeof(CC_SHA1_CTX));
+ CC_SHA1_Init(digobj);
+ break;
+ case SEC_OID_MD5:
+ digobj = calloc(1, sizeof(CC_MD5_CTX));
+ CC_MD5_Init(digobj);
+ break;
+ case SEC_OID_SHA224:
+ digobj = calloc(1, sizeof(CC_SHA256_CTX));
+ CC_SHA224_Init(digobj);
+ break;
+ case SEC_OID_SHA256:
+ digobj = calloc(1, sizeof(CC_SHA256_CTX));
+ CC_SHA256_Init(digobj);
+ break;
+ case SEC_OID_SHA384:
+ digobj = calloc(1, sizeof(CC_SHA512_CTX));
+ CC_SHA384_Init(digobj);
+ break;
+ case SEC_OID_SHA512:
+ digobj = calloc(1, sizeof(CC_SHA512_CTX));
+ CC_SHA512_Init(digobj);
+ break;
+ default:
+ break;
+ }
+ return digobj;
}
return 0;
{
if (mAddr) {
relocate(mAddr, mBase);
- assert(mAttributes->size() == mAddr->size());
+ if (mAttributes->size() != mAddr->size()) {
+ secemergency("~DataRetrieval: size mismatch, %u != %u", mAttributes->size(), mAddr->size());
+ abort();
+ }
// global (per-record) fields
mAttributes->recordType(mAddr->recordType());
+++ /dev/null
-/*
- * Copyright (c) 2006-2010,2012-2018 Apple Inc. All Rights Reserved.
- */
-
-#include <CoreFoundation/CoreFoundation.h>
-#include <Security/SecCertificate.h>
-#include <Security/SecCertificatePriv.h>
-#include <Security/SecPolicy.h>
-#include <Security/SecTrustPriv.h>
-#include <utilities/array_size.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "shared_regressions.h"
-
-/*
- Serial Number:
- 01:f1:eb:db:ee:d3:1d:7a:b5:72:54:7d:34:43:4b:87
- Signature Algorithm: sha256WithRSAEncryption
- Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 Extended Validation Server CA
- Validity
- Not Before: Mar 22 00:00:00 2018 GMT
- Not After : Mar 23 12:00:00 2019 GMT
- Subject: businessCategory=Private Organization/jurisdictionC=US/jurisdictionST=California/serialNumber=C0806592, C=US, ST=California, L=Cupertino, O=Apple Inc., OU=Internet Services for Akamai, CN=store.apple.com
- */
-static unsigned char _c0[]={
- 0x30,0x82,0x06,0xF7,0x30,0x82,0x05,0xDF,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x01,
- 0xF1,0xEB,0xDB,0xEE,0xD3,0x1D,0x7A,0xB5,0x72,0x54,0x7D,0x34,0x43,0x4B,0x87,0x30,
- 0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,0x75,
- 0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,
- 0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,
- 0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77,
- 0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31,
- 0x34,0x30,0x32,0x06,0x03,0x55,0x04,0x03,0x13,0x2B,0x44,0x69,0x67,0x69,0x43,0x65,
- 0x72,0x74,0x20,0x53,0x48,0x41,0x32,0x20,0x45,0x78,0x74,0x65,0x6E,0x64,0x65,0x64,
- 0x20,0x56,0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x53,0x65,0x72,0x76,
- 0x65,0x72,0x20,0x43,0x41,0x30,0x1E,0x17,0x0D,0x31,0x38,0x30,0x33,0x32,0x32,0x30,
- 0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x31,0x39,0x30,0x33,0x32,0x33,0x31,0x32,
- 0x30,0x30,0x30,0x30,0x5A,0x30,0x81,0xF0,0x31,0x1D,0x30,0x1B,0x06,0x03,0x55,0x04,
- 0x0F,0x0C,0x14,0x50,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x4F,0x72,0x67,0x61,0x6E,
- 0x69,0x7A,0x61,0x74,0x69,0x6F,0x6E,0x31,0x13,0x30,0x11,0x06,0x0B,0x2B,0x06,0x01,
- 0x04,0x01,0x82,0x37,0x3C,0x02,0x01,0x03,0x13,0x02,0x55,0x53,0x31,0x1B,0x30,0x19,
- 0x06,0x0B,0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x3C,0x02,0x01,0x02,0x13,0x0A,0x43,
- 0x61,0x6C,0x69,0x66,0x6F,0x72,0x6E,0x69,0x61,0x31,0x11,0x30,0x0F,0x06,0x03,0x55,
- 0x04,0x05,0x13,0x08,0x43,0x30,0x38,0x30,0x36,0x35,0x39,0x32,0x31,0x0B,0x30,0x09,
- 0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x13,0x30,0x11,0x06,0x03,0x55,
- 0x04,0x08,0x13,0x0A,0x43,0x61,0x6C,0x69,0x66,0x6F,0x72,0x6E,0x69,0x61,0x31,0x12,
- 0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x13,0x09,0x43,0x75,0x70,0x65,0x72,0x74,0x69,
- 0x6E,0x6F,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x13,0x0A,0x41,0x70,0x70,
- 0x6C,0x65,0x20,0x49,0x6E,0x63,0x2E,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04,0x0B,
- 0x13,0x1C,0x49,0x6E,0x74,0x65,0x72,0x6E,0x65,0x74,0x20,0x53,0x65,0x72,0x76,0x69,
- 0x63,0x65,0x73,0x20,0x66,0x6F,0x72,0x20,0x41,0x6B,0x61,0x6D,0x61,0x69,0x31,0x18,
- 0x30,0x16,0x06,0x03,0x55,0x04,0x03,0x13,0x0F,0x73,0x74,0x6F,0x72,0x65,0x2E,0x61,
- 0x70,0x70,0x6C,0x65,0x2E,0x63,0x6F,0x6D,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,
- 0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,
- 0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xB5,0xDF,0x48,0x6A,0xE5,0x5F,0x0C,
- 0x42,0xBF,0x71,0x07,0xAB,0xE6,0x48,0xD2,0x2C,0x13,0x7C,0x29,0xF7,0x90,0xC2,0x45,
- 0x39,0x43,0x48,0x2E,0x16,0x22,0xBF,0xAB,0x48,0x20,0x14,0x27,0xE7,0x87,0x52,0x97,
- 0xF6,0x64,0x0C,0x62,0xF5,0x39,0x54,0x64,0x09,0xE8,0x29,0x56,0xF4,0x03,0x40,0x5A,
- 0xFA,0x9A,0x11,0x2B,0xD0,0x01,0x51,0x1E,0xED,0xB8,0x51,0xCB,0xAB,0x6B,0x7A,0x99,
- 0xF3,0xB0,0x6E,0xA8,0xC4,0xB6,0xAF,0x17,0xEC,0xA6,0xD2,0x4D,0xCA,0x63,0x3D,0x59,
- 0x30,0x25,0x30,0x54,0x86,0xDB,0x70,0x7A,0xEC,0x07,0x83,0x19,0xA0,0x44,0xE7,0x3C,
- 0x68,0xE0,0xFD,0xB9,0xEE,0x3F,0xAC,0xF3,0x32,0x5A,0x5F,0x42,0x31,0x94,0x70,0x2C,
- 0xC5,0x73,0xD2,0x79,0x23,0x5B,0x96,0x45,0xB1,0xB3,0x2A,0xA1,0x5A,0x69,0xFE,0xBE,
- 0x52,0x0D,0x5D,0x79,0x18,0xCA,0xF1,0x44,0x92,0xA0,0x27,0x1F,0xAA,0x6E,0x9D,0x6F,
- 0x1B,0x83,0x5B,0x73,0x28,0x1D,0x87,0xB5,0x70,0x0E,0x3D,0xED,0xE2,0xC2,0x34,0x8A,
- 0x81,0xB2,0x22,0x40,0x98,0x77,0x2F,0x34,0x1B,0x70,0xEC,0x96,0x3F,0x91,0xB9,0xFF,
- 0xC9,0xE5,0x7E,0xE7,0x25,0xEF,0xDB,0x9A,0x58,0x4E,0xB2,0x92,0x19,0xA5,0x8D,0xEB,
- 0x76,0xF8,0xA8,0x48,0x9F,0x3D,0x10,0x0C,0xE4,0x69,0x7B,0xE7,0xB7,0xCA,0xF6,0x14,
- 0x5E,0x93,0x1E,0x20,0x37,0x1B,0xB8,0xB1,0x2C,0xD1,0x46,0x5C,0xD7,0x85,0x4A,0x2E,
- 0x19,0xB2,0x3C,0x31,0xDC,0x3D,0xB5,0x3C,0xEC,0x49,0x8D,0x9C,0xCF,0x75,0x0B,0xFC,
- 0x03,0x31,0x37,0xE7,0xA5,0xD6,0x9D,0x19,0x0D,0x02,0x03,0x01,0x00,0x01,0xA3,0x82,
- 0x03,0x05,0x30,0x82,0x03,0x01,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,
- 0x16,0x80,0x14,0x3D,0xD3,0x50,0xA5,0xD6,0xA0,0xAD,0xEE,0xF3,0x4A,0x60,0x0A,0x65,
- 0xD3,0x21,0xD4,0xF8,0xF8,0xD6,0x0F,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,
- 0x04,0x14,0x6F,0x89,0xAE,0x9B,0xE4,0x8A,0x21,0x3A,0x3B,0xEA,0xAA,0xBE,0x99,0x90,
- 0xC8,0xDB,0x34,0x80,0x21,0xB9,0x30,0x2F,0x06,0x03,0x55,0x1D,0x11,0x04,0x28,0x30,
- 0x26,0x82,0x0F,0x73,0x74,0x6F,0x72,0x65,0x2E,0x61,0x70,0x70,0x6C,0x65,0x2E,0x63,
- 0x6F,0x6D,0x82,0x13,0x77,0x77,0x77,0x2E,0x73,0x74,0x6F,0x72,0x65,0x2E,0x61,0x70,
- 0x70,0x6C,0x65,0x2E,0x63,0x6F,0x6D,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,
- 0xFF,0x04,0x04,0x03,0x02,0x05,0xA0,0x30,0x1D,0x06,0x03,0x55,0x1D,0x25,0x04,0x16,
- 0x30,0x14,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x01,0x06,0x08,0x2B,0x06,
- 0x01,0x05,0x05,0x07,0x03,0x02,0x30,0x75,0x06,0x03,0x55,0x1D,0x1F,0x04,0x6E,0x30,
- 0x6C,0x30,0x34,0xA0,0x32,0xA0,0x30,0x86,0x2E,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,
- 0x63,0x72,0x6C,0x33,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,
- 0x6D,0x2F,0x73,0x68,0x61,0x32,0x2D,0x65,0x76,0x2D,0x73,0x65,0x72,0x76,0x65,0x72,
- 0x2D,0x67,0x32,0x2E,0x63,0x72,0x6C,0x30,0x34,0xA0,0x32,0xA0,0x30,0x86,0x2E,0x68,
- 0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63,0x72,0x6C,0x34,0x2E,0x64,0x69,0x67,0x69,0x63,
- 0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x73,0x68,0x61,0x32,0x2D,0x65,0x76,0x2D,
- 0x73,0x65,0x72,0x76,0x65,0x72,0x2D,0x67,0x32,0x2E,0x63,0x72,0x6C,0x30,0x4B,0x06,
- 0x03,0x55,0x1D,0x20,0x04,0x44,0x30,0x42,0x30,0x37,0x06,0x09,0x60,0x86,0x48,0x01,
- 0x86,0xFD,0x6C,0x02,0x01,0x30,0x2A,0x30,0x28,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,
- 0x07,0x02,0x01,0x16,0x1C,0x68,0x74,0x74,0x70,0x73,0x3A,0x2F,0x2F,0x77,0x77,0x77,
- 0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x43,0x50,
- 0x53,0x30,0x07,0x06,0x05,0x67,0x81,0x0C,0x01,0x01,0x30,0x81,0x88,0x06,0x08,0x2B,
- 0x06,0x01,0x05,0x05,0x07,0x01,0x01,0x04,0x7C,0x30,0x7A,0x30,0x24,0x06,0x08,0x2B,
- 0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x86,0x18,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,
- 0x6F,0x63,0x73,0x70,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,
- 0x6D,0x30,0x52,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x02,0x86,0x46,0x68,
- 0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63,0x61,0x63,0x65,0x72,0x74,0x73,0x2E,0x64,0x69,
- 0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x44,0x69,0x67,0x69,0x43,
- 0x65,0x72,0x74,0x53,0x48,0x41,0x32,0x45,0x78,0x74,0x65,0x6E,0x64,0x65,0x64,0x56,
- 0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x53,0x65,0x72,0x76,0x65,0x72,0x43,
- 0x41,0x2E,0x63,0x72,0x74,0x30,0x09,0x06,0x03,0x55,0x1D,0x13,0x04,0x02,0x30,0x00,
- 0x30,0x82,0x01,0x03,0x06,0x0A,0x2B,0x06,0x01,0x04,0x01,0xD6,0x79,0x02,0x04,0x02,
- 0x04,0x81,0xF4,0x04,0x81,0xF1,0x00,0xEF,0x00,0x75,0x00,0xA4,0xB9,0x09,0x90,0xB4,
- 0x18,0x58,0x14,0x87,0xBB,0x13,0xA2,0xCC,0x67,0x70,0x0A,0x3C,0x35,0x98,0x04,0xF9,
- 0x1B,0xDF,0xB8,0xE3,0x77,0xCD,0x0E,0xC8,0x0D,0xDC,0x10,0x00,0x00,0x01,0x62,0x4E,
- 0xBC,0xC7,0x43,0x00,0x00,0x04,0x03,0x00,0x46,0x30,0x44,0x02,0x20,0x3E,0xD3,0xFE,
- 0xB4,0x45,0x97,0xCE,0xA3,0x05,0xC9,0x29,0x70,0x55,0x3B,0x77,0x9E,0xCC,0x4B,0x06,
- 0xFD,0x76,0x21,0xD0,0x79,0x69,0xD6,0x60,0x01,0xBB,0xC7,0x43,0x5E,0x02,0x20,0x3D,
- 0xB7,0x73,0x91,0x51,0xB7,0xAC,0x40,0xFB,0xA7,0x36,0xCF,0x10,0xE8,0x63,0x79,0xE6,
- 0x06,0xEC,0xFA,0x60,0xFE,0x44,0x90,0x9A,0x53,0x26,0x04,0x27,0x1A,0x4B,0xD4,0x00,
- 0x76,0x00,0x56,0x14,0x06,0x9A,0x2F,0xD7,0xC2,0xEC,0xD3,0xF5,0xE1,0xBD,0x44,0xB2,
- 0x3E,0xC7,0x46,0x76,0xB9,0xBC,0x99,0x11,0x5C,0xC0,0xEF,0x94,0x98,0x55,0xD6,0x89,
- 0xD0,0xDD,0x00,0x00,0x01,0x62,0x4E,0xBC,0xC8,0x6A,0x00,0x00,0x04,0x03,0x00,0x47,
- 0x30,0x45,0x02,0x20,0x18,0xF4,0x40,0x99,0x83,0xA7,0x53,0x6D,0x11,0xBF,0x7C,0xA5,
- 0x64,0x21,0x52,0xD0,0x7C,0xF5,0x96,0xCD,0xB0,0x56,0xAE,0x1E,0x13,0x9C,0xFC,0x08,
- 0x3B,0x56,0x66,0x0F,0x02,0x21,0x00,0x98,0xBE,0xC7,0x01,0x8E,0x88,0xB7,0xB2,0xF9,
- 0xC7,0xE6,0x08,0x46,0x10,0xEA,0x8D,0x01,0x12,0x0D,0x7D,0xA6,0xBA,0xA6,0xD3,0x1F,
- 0xEC,0xA3,0xD6,0x7A,0xE4,0x85,0x89,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,
- 0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x6A,0x52,0xE8,0xB3,0x70,
- 0x25,0xD9,0x95,0x87,0xC4,0xF3,0x14,0xB1,0x8C,0x29,0x6F,0x11,0xA0,0x6D,0xD7,0xE9,
- 0x96,0xFA,0x06,0x57,0x4D,0x86,0xE8,0xD6,0x95,0xC0,0x3A,0x33,0xEC,0x67,0x6C,0x98,
- 0xA2,0x99,0x09,0x28,0x16,0x18,0xEC,0x21,0xED,0x93,0xEF,0xAB,0x69,0x5F,0x98,0x9C,
- 0x62,0x21,0x10,0xEB,0x1C,0xC4,0x20,0x5E,0x3E,0x6F,0x80,0x03,0xC9,0xC5,0xD5,0x2F,
- 0xDA,0x8C,0x86,0x40,0x59,0x71,0x34,0xEE,0xCF,0x54,0x83,0xE1,0x3E,0x11,0x1D,0x12,
- 0x66,0x3D,0x16,0x7F,0xD8,0x1B,0x42,0x35,0x50,0xA6,0xD6,0xE6,0x6B,0x32,0xDE,0xE2,
- 0xD9,0x01,0xD7,0xB1,0xF5,0xE8,0x72,0x44,0x03,0xB5,0xFA,0x19,0x0D,0xF2,0x8D,0x0B,
- 0x75,0xBA,0xD9,0x39,0xFF,0x73,0xF2,0xA0,0xD6,0x49,0xEF,0x9E,0xE9,0x23,0x5D,0xED,
- 0xC5,0x08,0x69,0x10,0xF7,0xF0,0x0B,0x8D,0x80,0xB6,0x43,0xE4,0x2A,0xEC,0x92,0xCF,
- 0x22,0xCA,0xAF,0x1A,0x8A,0x16,0xC7,0xC5,0x88,0xB7,0xCA,0xC6,0x19,0x1A,0xD1,0xFF,
- 0x13,0x93,0x56,0x29,0x1A,0x48,0xA8,0x92,0x21,0xE5,0x7F,0x3E,0x4C,0xB4,0x89,0xD6,
- 0x08,0xF0,0xB0,0x4B,0x22,0x7C,0x1F,0xEC,0xE7,0x09,0x5D,0x23,0xE2,0xD1,0xB8,0xA3,
- 0xDF,0xEC,0x2D,0x87,0x1F,0xCD,0x58,0x1C,0xDD,0x09,0xE8,0xB5,0xD4,0x30,0x1E,0x2A,
- 0x4D,0xA3,0xA1,0x84,0x43,0xD7,0x4A,0x7B,0x15,0x8E,0xEB,0xE2,0x53,0x2B,0x12,0xCB,
- 0xFB,0xF2,0x61,0x7E,0x92,0x0B,0x87,0x07,0x1C,0x8D,0x0A,0xED,0x62,0x10,0x27,0xE3,
- 0xDB,0xC4,0x65,0xDE,0x57,0xFB,0x6D,0x49,0xC8,0x7A,0xF8,
-};
-
-/* subject:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert SHA2 Extended Validation Server CA */
-/* issuer :/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA */
-static unsigned char _c1[]={
- 0x30,0x82,0x04,0xB6,0x30,0x82,0x03,0x9E,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x0C,
- 0x79,0xA9,0x44,0xB0,0x8C,0x11,0x95,0x20,0x92,0x61,0x5F,0xE2,0x6B,0x1D,0x83,0x30,
- 0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,0x6C,
- 0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,
- 0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,
- 0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77,
- 0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31,
- 0x2B,0x30,0x29,0x06,0x03,0x55,0x04,0x03,0x13,0x22,0x44,0x69,0x67,0x69,0x43,0x65,
- 0x72,0x74,0x20,0x48,0x69,0x67,0x68,0x20,0x41,0x73,0x73,0x75,0x72,0x61,0x6E,0x63,
- 0x65,0x20,0x45,0x56,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x30,0x1E,0x17,0x0D,
- 0x31,0x33,0x31,0x30,0x32,0x32,0x31,0x32,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32,
- 0x38,0x31,0x30,0x32,0x32,0x31,0x32,0x30,0x30,0x30,0x30,0x5A,0x30,0x75,0x31,0x0B,
- 0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,0x13,0x06,
- 0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x49,
- 0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77,0x77,0x77,
- 0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31,0x34,0x30,
- 0x32,0x06,0x03,0x55,0x04,0x03,0x13,0x2B,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,
- 0x20,0x53,0x48,0x41,0x32,0x20,0x45,0x78,0x74,0x65,0x6E,0x64,0x65,0x64,0x20,0x56,
- 0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x53,0x65,0x72,0x76,0x65,0x72,
- 0x20,0x43,0x41,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,
- 0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,
- 0x82,0x01,0x01,0x00,0xD7,0x53,0xA4,0x04,0x51,0xF8,0x99,0xA6,0x16,0x48,0x4B,0x67,
- 0x27,0xAA,0x93,0x49,0xD0,0x39,0xED,0x0C,0xB0,0xB0,0x00,0x87,0xF1,0x67,0x28,0x86,
- 0x85,0x8C,0x8E,0x63,0xDA,0xBC,0xB1,0x40,0x38,0xE2,0xD3,0xF5,0xEC,0xA5,0x05,0x18,
- 0xB8,0x3D,0x3E,0xC5,0x99,0x17,0x32,0xEC,0x18,0x8C,0xFA,0xF1,0x0C,0xA6,0x64,0x21,
- 0x85,0xCB,0x07,0x10,0x34,0xB0,0x52,0x88,0x2B,0x1F,0x68,0x9B,0xD2,0xB1,0x8F,0x12,
- 0xB0,0xB3,0xD2,0xE7,0x88,0x1F,0x1F,0xEF,0x38,0x77,0x54,0x53,0x5F,0x80,0x79,0x3F,
- 0x2E,0x1A,0xAA,0xA8,0x1E,0x4B,0x2B,0x0D,0xAB,0xB7,0x63,0xB9,0x35,0xB7,0x7D,0x14,
- 0xBC,0x59,0x4B,0xDF,0x51,0x4A,0xD2,0xA1,0xE2,0x0C,0xE2,0x90,0x82,0x87,0x6A,0xAE,
- 0xEA,0xD7,0x64,0xD6,0x98,0x55,0xE8,0xFD,0xAF,0x1A,0x50,0x6C,0x54,0xBC,0x11,0xF2,
- 0xFD,0x4A,0xF2,0x9D,0xBB,0x7F,0x0E,0xF4,0xD5,0xBE,0x8E,0x16,0x89,0x12,0x55,0xD8,
- 0xC0,0x71,0x34,0xEE,0xF6,0xDC,0x2D,0xEC,0xC4,0x87,0x25,0x86,0x8D,0xD8,0x21,0xE4,
- 0xB0,0x4D,0x0C,0x89,0xDC,0x39,0x26,0x17,0xDD,0xF6,0xD7,0x94,0x85,0xD8,0x04,0x21,
- 0x70,0x9D,0x6F,0x6F,0xFF,0x5C,0xBA,0x19,0xE1,0x45,0xCB,0x56,0x57,0x28,0x7E,0x1C,
- 0x0D,0x41,0x57,0xAA,0xB7,0xB8,0x27,0xBB,0xB1,0xE4,0xFA,0x2A,0xEF,0x21,0x23,0x75,
- 0x1A,0xAD,0x2D,0x9B,0x86,0x35,0x8C,0x9C,0x77,0xB5,0x73,0xAD,0xD8,0x94,0x2D,0xE4,
- 0xF3,0x0C,0x9D,0xEE,0xC1,0x4E,0x62,0x7E,0x17,0xC0,0x71,0x9E,0x2C,0xDE,0xF1,0xF9,
- 0x10,0x28,0x19,0x33,0x02,0x03,0x01,0x00,0x01,0xA3,0x82,0x01,0x49,0x30,0x82,0x01,
- 0x45,0x30,0x12,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x08,0x30,0x06,0x01,
- 0x01,0xFF,0x02,0x01,0x00,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,
- 0x04,0x03,0x02,0x01,0x86,0x30,0x1D,0x06,0x03,0x55,0x1D,0x25,0x04,0x16,0x30,0x14,
- 0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x01,0x06,0x08,0x2B,0x06,0x01,0x05,
- 0x05,0x07,0x03,0x02,0x30,0x34,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x01,
- 0x04,0x28,0x30,0x26,0x30,0x24,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,
- 0x86,0x18,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x6F,0x63,0x73,0x70,0x2E,0x64,0x69,
- 0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x30,0x4B,0x06,0x03,0x55,0x1D,
- 0x1F,0x04,0x44,0x30,0x42,0x30,0x40,0xA0,0x3E,0xA0,0x3C,0x86,0x3A,0x68,0x74,0x74,
- 0x70,0x3A,0x2F,0x2F,0x63,0x72,0x6C,0x34,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,
- 0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x48,0x69,
- 0x67,0x68,0x41,0x73,0x73,0x75,0x72,0x61,0x6E,0x63,0x65,0x45,0x56,0x52,0x6F,0x6F,
- 0x74,0x43,0x41,0x2E,0x63,0x72,0x6C,0x30,0x3D,0x06,0x03,0x55,0x1D,0x20,0x04,0x36,
- 0x30,0x34,0x30,0x32,0x06,0x04,0x55,0x1D,0x20,0x00,0x30,0x2A,0x30,0x28,0x06,0x08,
- 0x2B,0x06,0x01,0x05,0x05,0x07,0x02,0x01,0x16,0x1C,0x68,0x74,0x74,0x70,0x73,0x3A,
- 0x2F,0x2F,0x77,0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,
- 0x6F,0x6D,0x2F,0x43,0x50,0x53,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,
- 0x14,0x3D,0xD3,0x50,0xA5,0xD6,0xA0,0xAD,0xEE,0xF3,0x4A,0x60,0x0A,0x65,0xD3,0x21,
- 0xD4,0xF8,0xF8,0xD6,0x0F,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,
- 0x80,0x14,0xB1,0x3E,0xC3,0x69,0x03,0xF8,0xBF,0x47,0x01,0xD4,0x98,0x26,0x1A,0x08,
- 0x02,0xEF,0x63,0x64,0x2B,0xC3,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,
- 0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x9D,0xB6,0xD0,0x90,0x86,0xE1,
- 0x86,0x02,0xED,0xC5,0xA0,0xF0,0x34,0x1C,0x74,0xC1,0x8D,0x76,0xCC,0x86,0x0A,0xA8,
- 0xF0,0x4A,0x8A,0x42,0xD6,0x3F,0xC8,0xA9,0x4D,0xAD,0x7C,0x08,0xAD,0xE6,0xB6,0x50,
- 0xB8,0xA2,0x1A,0x4D,0x88,0x07,0xB1,0x29,0x21,0xDC,0xE7,0xDA,0xC6,0x3C,0x21,0xE0,
- 0xE3,0x11,0x49,0x70,0xAC,0x7A,0x1D,0x01,0xA4,0xCA,0x11,0x3A,0x57,0xAB,0x7D,0x57,
- 0x2A,0x40,0x74,0xFD,0xD3,0x1D,0x85,0x18,0x50,0xDF,0x57,0x47,0x75,0xA1,0x7D,0x55,
- 0x20,0x2E,0x47,0x37,0x50,0x72,0x8C,0x7F,0x82,0x1B,0xD2,0x62,0x8F,0x2D,0x03,0x5A,
- 0xDA,0xC3,0xC8,0xA1,0xCE,0x2C,0x52,0xA2,0x00,0x63,0xEB,0x73,0xBA,0x71,0xC8,0x49,
- 0x27,0x23,0x97,0x64,0x85,0x9E,0x38,0x0E,0xAD,0x63,0x68,0x3C,0xBA,0x52,0x81,0x58,
- 0x79,0xA3,0x2C,0x0C,0xDF,0xDE,0x6D,0xEB,0x31,0xF2,0xBA,0xA0,0x7C,0x6C,0xF1,0x2C,
- 0xD4,0xE1,0xBD,0x77,0x84,0x37,0x03,0xCE,0x32,0xB5,0xC8,0x9A,0x81,0x1A,0x4A,0x92,
- 0x4E,0x3B,0x46,0x9A,0x85,0xFE,0x83,0xA2,0xF9,0x9E,0x8C,0xA3,0xCC,0x0D,0x5E,0xB3,
- 0x3D,0xCF,0x04,0x78,0x8F,0x14,0x14,0x7B,0x32,0x9C,0xC7,0x00,0xA6,0x5C,0xC4,0xB5,
- 0xA1,0x55,0x8D,0x5A,0x56,0x68,0xA4,0x22,0x70,0xAA,0x3C,0x81,0x71,0xD9,0x9D,0xA8,
- 0x45,0x3B,0xF4,0xE5,0xF6,0xA2,0x51,0xDD,0xC7,0x7B,0x62,0xE8,0x6F,0x0C,0x74,0xEB,
- 0xB8,0xDA,0xF8,0xBF,0x87,0x0D,0x79,0x50,0x91,0x90,0x9B,0x18,0x3B,0x91,0x59,0x27,
- 0xF1,0x35,0x28,0x13,0xAB,0x26,0x7E,0xD5,0xF7,0x7A,
-};
-
-/* subject:/CN=self-signed.ssltest.apple.com/C=US */
-/* issuer :/CN=self-signed.ssltest.apple.com/C=US */
-
-static unsigned char _ss0[]={
- 0x30,0x82,0x03,0x0F,0x30,0x82,0x01,0xF7,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01,
- 0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,
- 0x35,0x31,0x26,0x30,0x24,0x06,0x03,0x55,0x04,0x03,0x0C,0x1D,0x73,0x65,0x6C,0x66,
- 0x2D,0x73,0x69,0x67,0x6E,0x65,0x64,0x2E,0x73,0x73,0x6C,0x74,0x65,0x73,0x74,0x2E,
- 0x61,0x70,0x70,0x6C,0x65,0x2E,0x63,0x6F,0x6D,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,
- 0x04,0x06,0x13,0x02,0x55,0x53,0x30,0x1E,0x17,0x0D,0x31,0x36,0x30,0x36,0x30,0x37,
- 0x32,0x31,0x35,0x33,0x30,0x38,0x5A,0x17,0x0D,0x31,0x37,0x30,0x36,0x30,0x37,0x32,
- 0x31,0x35,0x33,0x30,0x38,0x5A,0x30,0x35,0x31,0x26,0x30,0x24,0x06,0x03,0x55,0x04,
- 0x03,0x0C,0x1D,0x73,0x65,0x6C,0x66,0x2D,0x73,0x69,0x67,0x6E,0x65,0x64,0x2E,0x73,
- 0x73,0x6C,0x74,0x65,0x73,0x74,0x2E,0x61,0x70,0x70,0x6C,0x65,0x2E,0x63,0x6F,0x6D,
- 0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x30,0x82,0x01,
- 0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,
- 0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xCC,0x72,
- 0x7D,0x09,0x36,0x5A,0x6A,0xED,0xC1,0x7A,0x2C,0xF4,0x7C,0x58,0x63,0x05,0x3E,0x91,
- 0x68,0x55,0xB1,0x2A,0x5D,0x57,0xF3,0xA4,0xA7,0x80,0x05,0x41,0x74,0xB2,0xAD,0x5A,
- 0x7F,0x38,0xF6,0xF7,0xFD,0xF9,0x64,0x4D,0xDE,0xF9,0x7A,0xD3,0x8C,0x78,0xE9,0x71,
- 0xCF,0x1D,0x3E,0xF0,0xDB,0x12,0x48,0x74,0x22,0xA8,0x1F,0x3F,0xB9,0xDD,0xB0,0xAD,
- 0x8C,0x10,0x64,0x05,0x0E,0xE2,0x59,0x9A,0xEB,0x3F,0xBF,0xA9,0x48,0x07,0xD9,0x2C,
- 0x07,0x44,0x70,0x14,0x16,0x56,0x9C,0x73,0x01,0x2E,0x0B,0xF1,0x2A,0x9F,0x1C,0xC6,
- 0x78,0x56,0xB7,0x0B,0xDA,0xA6,0xE6,0x99,0x87,0x2D,0x49,0xFB,0xF0,0x47,0x22,0xA6,
- 0x8B,0xF0,0x02,0x37,0x31,0xD0,0x34,0x9F,0x43,0xD1,0x24,0x49,0x94,0x7F,0xFD,0x48,
- 0x9C,0xBA,0x5D,0x6B,0xD4,0xF9,0x9E,0xB5,0x18,0xE4,0xB2,0x06,0x46,0xC3,0xD9,0xE7,
- 0x80,0xD8,0x61,0xA9,0x09,0x5E,0xBA,0x2E,0x58,0x56,0xAE,0x37,0x31,0x6E,0x87,0x98,
- 0xD5,0xC9,0x2B,0x31,0x5C,0x40,0x01,0xDF,0xD5,0x63,0x9E,0x05,0x18,0x21,0x53,0x70,
- 0x62,0x36,0x44,0xCD,0x02,0xC0,0xCC,0x6A,0x58,0xC6,0xF6,0xA4,0xDC,0x89,0x94,0xBD,
- 0x4E,0xC4,0xEE,0xEE,0x40,0x31,0x59,0xC3,0x43,0xAD,0x34,0x30,0xDE,0xA9,0xA7,0x0D,
- 0x85,0xF7,0x96,0x8C,0x45,0xC1,0x6E,0x85,0x39,0x97,0xA6,0x4F,0xEA,0xE8,0x2F,0x01,
- 0x3D,0xC0,0x3B,0x34,0x9F,0x8F,0xCB,0xD6,0x22,0x79,0x2C,0x8C,0x8C,0xE6,0xBB,0x1F,
- 0x89,0x87,0x93,0x3B,0x39,0x4E,0x64,0x7D,0xDA,0x4D,0x52,0x4C,0x97,0xE5,0x02,0x03,
- 0x01,0x00,0x01,0xA3,0x2A,0x30,0x28,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,
- 0xFF,0x04,0x04,0x03,0x02,0x07,0x80,0x30,0x16,0x06,0x03,0x55,0x1D,0x25,0x01,0x01,
- 0xFF,0x04,0x0C,0x30,0x0A,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x01,0x30,
- 0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82,
- 0x01,0x01,0x00,0x36,0x06,0xC9,0xE6,0x98,0xC2,0x84,0x1D,0x13,0x1E,0x54,0x35,0x6D,
- 0xE5,0xCB,0xC5,0xFD,0xD9,0x54,0x45,0x83,0x53,0xB3,0x3B,0xE7,0x30,0x6F,0xAE,0xEA,
- 0x63,0x3F,0xA8,0xFA,0xD9,0x6D,0x0F,0x7D,0xD4,0xB6,0x28,0x66,0xF9,0x57,0x87,0x3E,
- 0x57,0x27,0xB6,0x9A,0x56,0xAE,0xD7,0xE0,0x11,0x20,0x71,0xC1,0xEA,0xF6,0xED,0x74,
- 0x1A,0x5A,0xB1,0x74,0x6C,0xBE,0xAC,0x0E,0x3C,0xD9,0x3E,0xEC,0x17,0x6E,0xF0,0x69,
- 0xC9,0x4D,0xD2,0x7E,0xAE,0x8B,0x01,0xCC,0x1A,0x23,0x7C,0x58,0x07,0x30,0xE4,0x2A,
- 0x12,0xE8,0xA0,0x25,0x65,0x66,0xB5,0xC7,0x5D,0xD8,0x47,0xDF,0xD7,0x51,0xBC,0xA2,
- 0xAA,0xF0,0x2F,0xB5,0x9E,0x20,0x6D,0x1F,0x84,0x00,0xF0,0xD0,0xB8,0x42,0x6A,0x9A,
- 0xE7,0xCA,0x7B,0xE5,0x39,0x09,0x91,0xBF,0xCB,0x4D,0x7A,0x32,0x1E,0x00,0x6E,0xE5,
- 0xF7,0x44,0x80,0x82,0x38,0x53,0x64,0xB7,0x26,0x81,0xCB,0xCE,0xA1,0xAF,0x0C,0x67,
- 0x32,0xC6,0xE4,0x5D,0x09,0x7B,0x37,0xD7,0xC8,0x43,0x44,0xEF,0xC6,0xF8,0x72,0xFF,
- 0x65,0xD4,0x39,0x3D,0xEC,0x72,0xA5,0x28,0xFF,0x70,0x47,0x38,0xA3,0xC7,0xCC,0x5E,
- 0x0F,0xFF,0x43,0x83,0x78,0x49,0x68,0x90,0x48,0x89,0xAD,0xE1,0x2E,0xFA,0x8F,0x59,
- 0xB6,0x08,0x2A,0x72,0x2F,0x52,0x3F,0x73,0x84,0xCA,0xD8,0x18,0x6C,0xDA,0xA3,0x2E,
- 0xF2,0xD7,0x4C,0x21,0xD9,0xF8,0xB1,0x86,0xE9,0x35,0x78,0xE4,0x4F,0xD0,0x93,0x11,
- 0x8F,0xF4,0xB1,0x17,0x4F,0xDE,0xAC,0xBD,0xA9,0xBC,0x94,0xFC,0x2E,0x7D,0xF9,0x05,
- 0x26,0x90,0xF1,
-};
-
-#define CFReleaseSafe(CF) { CFTypeRef _cf = (CF); if (_cf) CFRelease(_cf); }
-#define CFReleaseNull(CF) { CFTypeRef _cf = (CF); if (_cf) { (CF) = NULL; CFRelease(_cf); } }
-
-/* Test basic add delete update copy matching stuff. */
-static void tests(void)
-{
- SecTrustRef trust;
- SecCertificateRef cert0, cert1, sscert0;
- CFArrayRef anchors = NULL;
- isnt(cert0 = SecCertificateCreateWithBytes(NULL, _c0, sizeof(_c0)),
- NULL, "create cert0");
- isnt(cert1 = SecCertificateCreateWithBytes(NULL, _c1, sizeof(_c1)),
- NULL, "create cert1");
- isnt(sscert0 = SecCertificateCreateWithBytes(NULL, _ss0, sizeof(_ss0)),
- NULL, "create sscert0");
- const void *v_certs[] = {
- cert0,
- cert1
- };
- SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("store.apple.com"));
- CFArrayRef certs = CFArrayCreate(NULL, v_certs,
- array_size(v_certs), NULL);
- ok_status(SecTrustCreateWithCertificates(certs, policy, &trust), "create trust");
- CFDateRef date = CFDateCreate(NULL, 545000000.0); /* April 9, 2018 at 1:53:20 PM PDT */
- ok_status(SecTrustSetVerifyDate(trust, date), "set date");
-
- SecTrustResultType trustResult;
- ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
- is_status(trustResult, kSecTrustResultUnspecified,
- "trust is kSecTrustResultUnspecified");
- CFDataRef exceptions;
- ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
- if (!exceptions) { goto errOut; }
- ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
- ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
- is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
-
- CFReleaseNull(trust);
- CFReleaseNull(policy);
- policy = SecPolicyCreateSSL(true, CFSTR("badstore.apple.com"));
- ok_status(SecTrustCreateWithCertificates(certs, policy, &trust), "create trust with hostname mismatch");
- ok_status(SecTrustSetVerifyDate(trust, date), "set date");
- ok(SecTrustSetExceptions(trust, exceptions), "set old exceptions");
- ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
- is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure");
- CFReleaseNull(exceptions);
- ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
- if (!exceptions) { goto errOut; }
- ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
- ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
- is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
-
- CFReleaseNull(trust);
- ok_status(SecTrustCreateWithCertificates(certs, policy, &trust), "create trust");
- ok_status(SecTrustSetVerifyDate(trust, date), "set date");
- ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
- ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
- is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
- anchors = CFArrayCreate(kCFAllocatorDefault, NULL, 0, &kCFTypeArrayCallBacks);
- ok_status(SecTrustSetAnchorCertificates(trust, anchors), "set empty anchor list");
- ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
- is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure");
-
- ok_status(SecTrustSetAnchorCertificatesOnly(trust, false), "trust passed in anchors and system anchors");
- ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
- is_status(trustResult, kSecTrustResultProceed, "trust is now kSecTrustResultProceed");
-
- ok_status(SecTrustSetAnchorCertificatesOnly(trust, true), "only trust passed in anchors (default)");
- ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
- is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure again");
-
- CFReleaseNull(exceptions);
- ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
- if (!exceptions) { goto errOut; }
- ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
- ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
- is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
- CFReleaseNull(date);
- date = CFDateCreate(NULL, 667680000.0);
- ok_status(SecTrustSetVerifyDate(trust, date), "set date to far future so certs are expired");
- ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
- is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure");
-
- CFReleaseNull(trust);
- CFReleaseNull(policy);
- policy = SecPolicyCreateSSL(true, CFSTR("self-signed.ssltest.apple.com"));
- ok_status(SecTrustCreateWithCertificates(sscert0, policy, &trust), "create trust");
- ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
- is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure");
- CFReleaseNull(exceptions);
- ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
- if (!exceptions) { goto errOut; }
- ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
- ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
- is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
- CFReleaseNull(exceptions);
- ok(!SecTrustSetExceptions(trust, NULL), "clear exceptions");
- ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
- is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure");
-
-#if TARGET_OS_IPHONE
- /* Test the extenions epoch feature. */
- CFReleaseNull(exceptions);
- CFReleaseNull(trust);
- CFReleaseNull(policy);
- policy = SecPolicyCreateSSL(false, CFSTR("self-signed.ssltest.apple.com"));
- ok_status(SecTrustCreateWithCertificates(sscert0, policy, &trust), "create trust");
- ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
- if (!exceptions) { goto errOut; }
-
- /* Test the uninitialized extensions epoch. */
- CFErrorRef exceptionResetCountError = NULL;
- uint64_t exceptionResetCount = SecTrustGetExceptionResetCount(&exceptionResetCountError);
- ok(exceptionResetCount == 0, "exception reset count is not set yet");
- ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
- ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
- is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
-
- /* Test increasing the extensions epoch. */
- exceptionResetCountError = NULL;
- ok_status(SecTrustIncrementExceptionResetCount(&exceptionResetCountError), "increase exception reset count");
- exceptionResetCountError = NULL;
- is(SecTrustGetExceptionResetCount(&exceptionResetCountError), 1 + exceptionResetCount, "exception reset count is 1 + previous count");
-
- /* Test trust evaluation under a future extensions epoch. */
- ok(!SecTrustSetExceptions(trust, exceptions), "set exceptions");
- ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
- is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure");
-#endif
-
-errOut:
- CFReleaseSafe(anchors);
- CFReleaseSafe(exceptions);
- CFReleaseSafe(trust);
- CFReleaseSafe(policy);
- CFReleaseSafe(certs);
- CFReleaseSafe(cert0);
- CFReleaseSafe(cert1);
- CFReleaseSafe(sscert0);
- CFReleaseSafe(date);
-}
-
-int si_27_sectrust_exceptions(int argc, char *const *argv)
-{
-#if TARGET_OS_IPHONE
- plan_tests(62);
-#else
- plan_tests(51);
-#endif
-
- tests();
-
- return 0;
-}
if (value == nil) {
// Do nothing.
} else if (CFGetTypeID((__bridge CFTypeRef)value) == CFBooleanGetTypeID()) {
- [target appendString:[value boolValue] ? @"true" : @"false"];
+ NSNumber *boolValue = value;
+ [target appendString:boolValue.boolValue ? @"true" : @"false"];
} else if ([value isKindOfClass:NSNumber.class]) {
- [target appendString:[value string]];
+ NSNumber *numberValue = value;
+ [target appendString:numberValue.stringValue];
} else if ([value isKindOfClass:NSString.class]) {
[target appendString:value];
+ } else if ([value isKindOfClass:NSData.class]) {
+ NSData *dataValue = value;
+ const uint8_t *dataBuffer = dataValue.bytes;
+ NSUInteger dumpLength = dataValue.length > 64 ? 64 : dataValue.length;
+ for (NSUInteger i = 0; i < dumpLength; i++) {
+ [target appendFormat:@"%02X", (unsigned)dataBuffer[i]];
+ }
+ if (dumpLength < dataValue.length) {
+ [target appendFormat:@"...(%db)", (int)dataValue.length];
+ }
} else if ([value isKindOfClass:NSDictionary.class]) {
[value enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
[target appendString:separator];
#include <utilities/array_size.h>
#include <ctkclient/ctkclient.h>
#include <libaks_acl_cf_keys.h>
+#include <coreauthd_spi.h>
#include "OSX/sec/Security/SecItemShim.h"
#include "SecECKey.h"
if (CFEqual(name, kSecUseAuthenticationContext)) {
// Preprocess LAContext to ACMRef value.
if (value != NULL) {
- require_quiet(acm_reference = SecItemAttributesCopyPreparedAuthContext(value, error), out);
+ require_quiet(acm_reference = LACopyACMContext(value, error), out);
value = acm_reference;
}
name = kSecUseCredentialReference;
static const uint8_t appleComponentExtensionArc[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0b };
static const uint8_t appleSigningExtensionArc[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0c };
static const uint8_t appleEncryptionExtensionArc[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0d };
- if (!extnID && !extnID->data && extnID->length <= sizeof(appleExtensionArc)) {
+ if (!extnID || !extnID->data || (extnID->length <= sizeof(appleExtensionArc))) {
return false;
}
return (!memcmp(extnID->data, appleExtensionArc, sizeof(appleExtensionArc)) ||
!memcmp(extnID->data, appleEncryptionExtensionArc, sizeof(appleEncryptionExtensionArc)));
}
+static bool isCCCExtensionOID(const DERItem *extnID)
+{
+ static const uint8_t cccVehicleCA[] = { 0x2B,0x06,0x01,0x04,0x01,0x82,0xC4,0x69,0x05,0x09 };
+ static const uint8_t cccIntermediateCA[] = { 0x2B,0x06,0x01,0x04,0x01,0x82,0xC4,0x69,0x05,0x08 };
+ static const uint8_t cccVehicle[] = { 0x2B,0x06,0x01,0x04,0x01,0x82,0xC4,0x69,0x05,0x01 };
+ if (!extnID || !extnID->data || (extnID->length != sizeof(cccVehicleCA))) {
+ return false;
+ }
+ return (!memcmp(extnID->data, cccVehicleCA, sizeof(cccVehicleCA)) ||
+ !memcmp(extnID->data, cccIntermediateCA, sizeof(cccIntermediateCA)) ||
+ !memcmp(extnID->data, cccVehicle, sizeof(cccVehicle)));
+}
+
/* Given the contents of an X.501 Name return the contents of a normalized
X.501 name. */
CFDataRef createNormalizedX501Name(CFAllocatorRef allocator,
}
require_quiet(parseResult || !certificate->_extensions[ix].critical, badCert);
} else if (certificate->_extensions[ix].critical) {
- if (isAppleExtensionOID(&extn.extnID)) {
+ if (isAppleExtensionOID(&extn.extnID) || isCCCExtensionOID(&extn.extnID)) {
continue;
}
secdebug("cert", "Found unknown critical extension");
_kSecPolicyNameApplePPQService
_kSecPolicyNameApplePushService
_kSecPolicyNameAppleSiriService
+_kSecPolicyNameAppleUpdatesService
_kSecPolicyNameEAPClient
_kSecPolicyNameEAPServer
_kSecPolicyNameIPSecClient
static CFDataRef CreateTokenPersistentRefData(CFTypeRef class, CFDictionaryRef attributes)
{
CFDataRef tokenPersistentRef = NULL;
- CFStringRef tokenId = CFDictionaryGetValue(attributes, kSecAttrTokenID);
+ CFStringRef tokenId;
CFDictionaryRef itemValue = NULL;
+ require_quiet(tokenId = CFCast(CFString, CFDictionaryGetValue(attributes, kSecAttrTokenID)), out);
if (CFEqual(class, kSecClassIdentity)) {
itemValue = SecTokenItemValueCopy(CFDictionaryGetValue(attributes, kSecAttrIdentityCertificateData), NULL);
} else {
itemValue = SecTokenItemValueCopy(CFDictionaryGetValue(attributes, kSecValueData), NULL);
}
- require(itemValue, out);
+ require_quiet(itemValue, out);
CFDataRef oid = CFDictionaryGetValue(itemValue, kSecTokenValueObjectIDKey);
- require(oid, out);
+ require_quiet(oid, out);
CFArrayRef array = CFArrayCreateForCFTypes(kCFAllocatorDefault, class, tokenId, oid, NULL);
tokenPersistentRef = CFPropertyListCreateDERData(kCFAllocatorDefault, array, NULL);
CFRelease(array);
CFDictionaryRef SecTokenItemValueCopy(CFDataRef db_value, CFErrorRef *error) {
CFPropertyListRef plist = NULL;
+ require_quiet(CFCastWithError(CFData, db_value, error), out);
const uint8_t *der = CFDataGetBytePtr(db_value);
const uint8_t *der_end = der + CFDataGetLength(db_value);
require_quiet(der = der_decode_plist(0, kCFPropertyListImmutable, &plist, error, der, der_end), out);
require_action_quiet(der == der_end, out, SecError(errSecDecode, error, CFSTR("trailing garbage at end of token data field")));
- require_action_quiet(CFDictionaryGetValue(plist, kSecTokenValueObjectIDKey) != NULL, out,
+ CFTypeRef value = CFDictionaryGetValue(plist, kSecTokenValueObjectIDKey);
+ require_action_quiet(CFCast(CFData, value) != NULL, out,
SecError(errSecInternal, error, CFSTR("token based item data does not have OID")));
+ value = CFDictionaryGetValue(plist, kSecTokenValueAccessControlKey);
+ require_quiet(value == NULL || CFCastWithError(CFData, value, error), out);
+ value = CFDictionaryGetValue(plist, kSecTokenValueDataKey);
+ require_quiet(value == NULL || CFCastWithError(CFData, value, error), out);
out:
return plist;
CFMutableDictionaryRef attrs = CFDictionaryCreateMutableCopy(NULL, 0, attributes);
CFTypeRef token_id = CFDictionaryGetValue(attributes, kSecAttrTokenID);
if (token_id != NULL && object_id != NULL) {
+ require_quiet(CFCastWithError(CFString, token_id, error), out);
if (CFRetainSafe(token) == NULL) {
require_quiet(token = SecTokenCreate(token_id, &auth_params, error), out);
}
if (token == NULL) {
if (CFGetTypeID(raw_result) == CFDictionaryGetTypeID()) {
token_id = CFDictionaryGetValue(raw_result, kSecAttrTokenID);
+ require_quiet(token_id == NULL || CFCastWithError(CFString, token_id, error) != NULL, out);
token_item = (token_id != NULL);
cert_token_id = CFDictionaryGetValue(raw_result, kSecAttrIdentityCertificateTokenID);
+ require_quiet(cert_token_id == NULL || CFCastWithError(CFString, cert_token_id, error) != NULL, out);
cert_token_item = (cert_token_id != NULL);
}
} else {
return ok;
}
-CFDataRef SecItemAttributesCopyPreparedAuthContext(CFTypeRef la_context, CFErrorRef *error) {
- void *la_lib = NULL;
- CFDataRef acm_context = NULL;
- require_action_quiet(la_lib = dlopen("/System/Library/Frameworks/LocalAuthentication.framework/LocalAuthentication", RTLD_LAZY), out,
- SecError(errSecInternal, error, CFSTR("failed to open LocalAuthentication.framework")));
- LAFunctionCopyExternalizedContext fnCopyExternalizedContext = NULL;
- require_action_quiet(fnCopyExternalizedContext = dlsym(la_lib, "LACopyExternalizedContext"), out,
- SecError(errSecInternal, error, CFSTR("failed to obtain LACopyExternalizedContext")));
- require_action_quiet(acm_context = fnCopyExternalizedContext(la_context), out,
- SecError(errSecInternal, error, CFSTR("failed to get ACM handle from LAContext")));
-out:
- if (la_lib != NULL) {
- dlclose(la_lib);
- }
- return acm_context;
-}
-
static bool SecItemAttributesPrepare(SecCFDictionaryCOW *attrs, bool forQuery, CFErrorRef *error) {
bool ok = false;
CFDataRef ac_data = NULL, acm_context = NULL;
if (la_context) {
require_action_quiet(!CFDictionaryContainsKey(attrs->dictionary, kSecUseCredentialReference), out,
SecError(errSecParam, error, CFSTR("kSecUseAuthenticationContext cannot be used together with kSecUseCredentialReference")));
- require_quiet(acm_context = SecItemAttributesCopyPreparedAuthContext(la_context, error), out);
+ require_quiet(acm_context = LACopyACMContext(la_context, error), out);
CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs), kSecUseAuthenticationContext);
CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs), kSecUseCredentialReference, acm_context);
}
value = CFDictionaryGetValue(attrs->dictionary, kSecAttrIssuer);
if (value) {
/* convert DN to canonical issuer, if value is DN (top level sequence) */
- const DERItem name = { (unsigned char *)CFDataGetBytePtr(value), CFDataGetLength(value) };
+ CFDataRef issuer;
+ require_quiet(issuer = CFCastWithError(CFData, value, error), out);
+ const DERItem name = { (unsigned char *)CFDataGetBytePtr(issuer), CFDataGetLength(issuer) };
DERDecodedInfo content;
if (DERDecodeItem(&name, &content) == DR_Success && content.tag == ASN1_CONSTR_SEQUENCE) {
CFDataRef canonical_issuer = createNormalizedX501Name(kCFAllocatorDefault, &content.content);
// Prepare connection to target token if it is present.
CFStringRef token_id = CFDictionaryGetValue(query->dictionary, kSecAttrTokenID);
+ require_quiet(token_id == NULL || CFCastWithError(CFString, token_id, error) != NULL, out);
if (secItemOperation != SecItemCopyMatching && token_id != NULL) {
require_quiet(CFAssignRetained(token, SecTokenCreate(token_id, &auth_params, error)), out);
}
CFDictionaryRef SecTokenItemValueCopy(CFDataRef db_value, CFErrorRef *error);
-CFDataRef SecItemAttributesCopyPreparedAuthContext(CFTypeRef la_context, CFErrorRef *error);
-
CFArrayRef SecItemCopyParentCertificates_ios(CFDataRef normalizedIssuer, CFArrayRef accessGroups, CFErrorRef *error);
bool SecItemCertificateExists(CFDataRef normalizedIssuer, CFDataRef serialNumber, CFArrayRef accessGroups, CFErrorRef *error);
static CC_NONNULL((1,2,3))
OSStatus CreatePublicKey(const uint8_t**bytesPtr, size_t*sizePtr, SecOTRPublicIdentityRef* publicId);
-static CC_NONNULL((1,2,3))
+static CC_NONNULL((2,3))
CFMutableDataRef CFDataCreateMutableFromOTRDATA(CFAllocatorRef allocator, const uint8_t**bytesPtr, size_t*sizePtr);
static CC_NONNULL((1))
SEC_CONST_DECL (kSecPolicyNameAppleAMPService, "AMP");
SEC_CONST_DECL (kSecPolicyNameAppleSiriService, "Siri");
SEC_CONST_DECL (kSecPolicyNameAppleHomeAppClipUploadService, "HomeAppClipUploadService");
+SEC_CONST_DECL (kSecPolicyNameAppleUpdatesService, "Updates");
#define kSecPolicySHA1Size 20
#define kSecPolicySHA256Size 32
policy = SecPolicyCreateAppleComponentCertificate(rootDigest);
}
/* For a couple of common patterns we use the macro, but some of the
- * policies are deprecated, so we need to ignore the warning. */
+ * policies are deprecated (or not yet available), so we need to ignore the warning. */
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#pragma clang diagnostic ignored "-Wunguarded-availability"
#define _P_OPTION_
#define _P_OPTION_N name
#define _P_PROPERTIES_(NAME, IN_NAME, FUNCTION)
/* This one is special because the intermediate has no marker OID */
SecPolicyRef SecPolicyCreateAppleSoftwareSigning(void) {
CFMutableDictionaryRef options = NULL;
- CFDictionaryRef keySizes = NULL;
- CFNumberRef rsaSize = NULL, ecSize = NULL;
SecPolicyRef result = NULL;
require(options = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
errOut:
CFReleaseSafe(options);
- CFReleaseSafe(keySizes);
- CFReleaseSafe(rsaSize);
- CFReleaseSafe(ecSize);
return result;
}
CFReleaseNull(options);
return result;
}
+
+SecPolicyRef SecPolicyCreateAlisha(void) {
+ CFMutableDictionaryRef options = NULL;
+ SecPolicyRef result = NULL;
+ CFDictionaryRef keySizes = NULL;
+ CFNumberRef ecSize = NULL;
+
+ require(options = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks), errOut);
+
+ /* Alisha certs don't expire */
+ SecPolicyAddBasicCertOptions(options);
+
+ /* RSA key sizes are disallowed. EC key sizes are P-256 or larger. */
+ require(ecSize = CFNumberCreateWithCFIndex(NULL, 256), errOut);
+ require(keySizes = CFDictionaryCreate(NULL, (const void**)&kSecAttrKeyTypeEC,
+ (const void**)&ecSize, 1,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks), errOut);
+ add_element(options, kSecPolicyCheckKeySize, keySizes);
+
+ /* Check for weak hashes */
+ require(SecPolicyRemoveWeakHashOptions(options), errOut);
+
+ require(result = SecPolicyCreate(kSecPolicyAppleAlisha,
+ kSecPolicyNameAlisha, options), errOut);
+errOut:
+ CFReleaseNull(options);
+ CFReleaseNull(keySizes);
+ CFReleaseNull(ecSize);
+ return result;
+}
+
+SecPolicyRef SecPolicyCreateMeasuredBootPolicySigning(void) {
+ CFMutableDictionaryRef options = NULL;
+ SecPolicyRef result = NULL;
+
+ require(options = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks), errOut);
+
+ /* No expiration check. */
+ SecPolicyAddBasicCertOptions(options);
+
+ /* Exactly 3 certs in the chain */
+ require(SecPolicyAddChainLengthOptions(options, 3), errOut);
+
+ /* Corporate Signing subCA */
+ add_element(options, kSecPolicyCheckIntermediateMarkerOid, CFSTR("1.2.840.113635.100.6.24.17"));
+
+ /* Measured Boot Policy Signing Leaf OID */
+ add_leaf_marker_string(options, CFSTR("1.2.840.113635.100.6.26.6.1"));
+
+ /* RSA key sizes are 2048-bit or larger. EC key sizes are P-256 or larger. */
+ require(SecPolicyAddStrongKeySizeOptions(options), errOut);
+
+ require(result = SecPolicyCreate(kSecPolicyAppleMeasuredBootPolicySigning,
+ kSecPolicyNameMeasuredBootPolicySigning, options), errOut);
+
+errOut:
+ CFReleaseSafe(options);
+ return result;
+}
POLICYMACRO(ComponentCertificate, 92, E, Component, , , AppleComponentCertificate)
POLICYMACRO(KeyTransparency, 93, E, KT, N, Y, AppleKeyTransparency)
POLICYMACRO(LegacySSL, 94, E, legacySSL, , , LegacySSL)
+POLICYMACRO(Alisha, 95, E, Alisha, , Y, Alisha)
+POLICYMACRO(MeasuredBootPolicySigning, 96, E, MeasuredBootPolicySigning, , Y, MeasuredBootPolicySigning)
return CFSTR("OTASecExperimentGetAsset");
case kSecXPCOpAcceptApplicants:
return CFSTR("AcceptApplicants");
- case kSecXPCOpApplyToARing:
- return CFSTR("ApplyToARing");
case kSecXPCOpBailFromCircle:
return CFSTR("BailFromCircle");
case kSecXPCOpCanAuthenticate:
return CFSTR("CopyEngineState");
case kSecXPCOpCopyGenerationPeerInfo:
return CFSTR("CopyGenerationPeerInfo");
- case kSecXPCOpCopyIncompatibilityInfo:
- return CFSTR("CopyIncompatibilityInfo");
case kSecXPCOpCopyMyPeerInfo:
return CFSTR("CopyMyPeerInfo");
case kSecXPCOpCopyNotValidPeerPeerInfo:
return CFSTR("CopyViewUnawarePeerInfo");
case kSecXPCOpDeviceInCircle:
return CFSTR("DeviceInCircle");
- case kSecXPCOpEnableRing:
- return CFSTR("EnableRing");
- case kSecXPCOpGetAllTheRings:
- return CFSTR("GetAllTheRings");
case kSecXPCOpGetLastDepartureReason:
return CFSTR("GetLastDepartureReason");
case kSecXPCOpLoggedOutOfAccount:
return CFSTR("RemovePeersFromCircle");
case kSecXPCOpRemovePeersFromCircleWithAnalytics:
return CFSTR("RemovePeersFromCircleWithAnalytics");
- case kSecXPCOpRequestEnsureFreshParameters:
- return CFSTR("RequestEnsureFreshParameters");
case kSecXPCOpRequestToJoin:
return CFSTR("RequestToJoin");
case kSecXPCOpRequestToJoinWithAnalytics:
return CFSTR("ResetToEmptyWithAnalytics");
case kSecXPCOpResetToOffering:
return CFSTR("ResetToOffering");
- case kSecXPCOpRingStatus:
- return CFSTR("RingStatus");
case kSecXPCOpRollKeys:
return CFSTR("RollKeys");
case kSecXPCOpSetBagForAllSlices:
return CFSTR("ValidateUserPublic");
case kSecXPCOpView:
return CFSTR("View");
- case kSecXPCOpWithdrawlFromARing:
- return CFSTR("WithdrawlFromARing");
case sec_add_shared_web_credential_id:
return CFSTR("add_shared_web_credential");
case sec_copy_shared_web_credential_id:
return CFSTR("ocsp_cache_flush");
case soscc_EnsurePeerRegistration_id:
return CFSTR("EnsurePeerRegistration");
- case kSecXPCOpSetEscrowRecord:
- return CFSTR("SetEscrowRecord");
- case kSecXPCOpGetEscrowRecord:
- return CFSTR("GetEscrowRecord");
case kSecXPCOpWhoAmI:
return CFSTR("WhoAmI");
case kSecXPCOpTransmogrifyToSyncBubble:
return CFSTR("TransmogrifyToSyncBubble");
- case kSecXPCOpWrapToBackupSliceKeyBagForView:
- return CFSTR("WrapToBackupSliceKeyBagForView");
- case kSecXPCOpCopyAccountData:
- return CFSTR("CopyAccountDataFromKeychain");
- case kSecXPCOpDeleteAccountData:
- return CFSTR("DeleteAccountDataFromKeychain");
- case kSecXPCOpCopyEngineData:
- return CFSTR("CopyEngineDataFromKeychain");
- case kSecXPCOpDeleteEngineData:
- return CFSTR("DeleteEngineDataFromKeychain");
case sec_item_update_token_items_id:
return CFSTR("UpdateTokenItems");
case sec_delete_items_with_access_groups_id:
return CFSTR("RegisterRecoveryPublicKey");
case kSecXPCOpGetRecoveryPublicKey:
return CFSTR("GetRecoveryPublicKey");
- case kSecXPCOpCopyBackupInformation:
- return CFSTR("CopyBackupInformation");
case kSecXPCOpMessageFromPeerIsPending:
return CFSTR("MessageFromPeerIsPending");
case kSecXPCOpSendToPeerIsPending:
sec_set_xpc_log_settings_id,
sec_set_circle_log_settings_id,
soscc_EnsurePeerRegistration_id,
- kSecXPCOpRequestEnsureFreshParameters,
- kSecXPCOpGetAllTheRings,
- kSecXPCOpApplyToARing,
- kSecXPCOpWithdrawlFromARing,
- kSecXPCOpEnableRing,
- kSecXPCOpRingStatus,
kSecXPCOpRequestDeviceID,
kSecXPCOpSetDeviceID,
kSecXPCOpHandleIDSMessage,
kSecXPCOpCopyGenerationPeerInfo,
kSecXPCOpGetLastDepartureReason,
kSecXPCOpSetLastDepartureReason,
- kSecXPCOpCopyIncompatibilityInfo,
kSecXPCOpCopyRetirementPeerInfo,
kSecXPCOpCopyViewUnawarePeerInfo,
kSecXPCOpCopyEngineState,
kSecXPCOpSetBagForAllSlices,
kSecXPCOpWaitForInitialSync,
kSecXPCOpWaitForInitialSyncWithAnalytics,
- kSecXPCOpCopyYetToSyncViews,
- kSecXPCOpSetEscrowRecord,
- kSecXPCOpGetEscrowRecord,
kSecXPCOpCheckPeerAvailability,
- kSecXPCOpCopyAccountData,
- kSecXPCOpDeleteAccountData,
- kSecXPCOpCopyEngineData,
- kSecXPCOpDeleteEngineData,
kSecXPCOpCopyApplication,
kSecXPCOpCopyCircleJoiningBlob,
kSecXPCOpJoinWithCircleJoiningBlob,
kSecXPCOpKVSKeyCleanup,
- kSecXPCOpPopulateKVS,
kSecXPCOpAccountHasPublicKey,
- kSecXPCOpAccountIsNew,
kSecXPCOpClearKVSPeerMessage,
kSecXPCOpRegisterRecoveryPublicKey,
kSecXPCOpGetRecoveryPublicKey,
- kSecXPCOpCopyBackupInformation,
kSecXPCOpCopyInitialSyncBlob,
/* after this is free for all */
kSecXPCOpWhoAmI,
kSecXPCOpTransmogrifyToSyncBubble,
kSecXPCOpTransmogrifyToSystemKeychain,
- kSecXPCOpWrapToBackupSliceKeyBagForView,
sec_item_update_token_items_id,
kSecXPCOpDeleteUserView,
sec_trust_store_copy_all_id,
sec_trust_store_copy_usage_constraints_id,
sec_ocsp_cache_flush_id,
sec_delete_items_with_access_groups_id,
- kSecXPCOpIsThisDeviceLastBackup,
sec_keychain_backup_keybag_uuid_id,
kSecXPCOpPeersHaveViewsEnabled,
kSecXPCOpProcessSyncWithPeers,
bool (*soscc_RequestToJoinCircleWithAnalytics)(CFDataRef parentEvent, CFErrorRef* error);
bool (*soscc_RequestToJoinCircleAfterRestore)(CFErrorRef* error);
bool (*soscc_RequestToJoinCircleAfterRestoreWithAnalytics)(CFDataRef parentEvent, CFErrorRef* error);
- bool (*soscc_RequestEnsureFreshParameters)(CFErrorRef* error);
- CFStringRef (*soscc_GetAllTheRings)(CFErrorRef *error);
- bool (*soscc_ApplyToARing)(CFStringRef ringName, CFErrorRef* error);
- bool (*soscc_WithdrawlFromARing)(CFStringRef ringName, CFErrorRef* error);
- bool (*soscc_EnableRing)(CFStringRef ringName, CFErrorRef* error);
- SOSRingStatus (*soscc_RingStatus)(CFStringRef ringName, CFErrorRef* error);
bool (*soscc_SetToNew)(CFErrorRef *error);
bool (*soscc_ResetToOffering)(CFErrorRef* error);
bool (*soscc_ResetToEmpty)(CFErrorRef* error);
// Not sure why these are below the last entry in the enum order above, but they are:
CFArrayRef (*soscc_CopyPeerInfo)(CFErrorRef* error);
CFArrayRef (*soscc_CopyConcurringPeerInfo)(CFErrorRef* error);
- CFStringRef (*soscc_CopyIncompatibilityInfo)(CFErrorRef* error);
enum DepartureReason (*soscc_GetLastDepartureReason)(CFErrorRef* error);
bool (*soscc_SetLastDepartureReason)(enum DepartureReason, CFErrorRef* error);
CFSetRef (*soscc_ProcessSyncWithPeers)(CFSetRef peerIDs, CFSetRef backupPeerIDs, CFErrorRef* error);
SOSPeerInfoRef (*soscc_CopyMyPeerInfo)(CFErrorRef*);
bool (*soscc_WaitForInitialSync)(CFErrorRef*);
bool (*soscc_WaitForInitialSyncWithAnalytics)(CFDataRef parentEvent, CFErrorRef *error);
- CFArrayRef (*soscc_CopyYetToSyncViewsList)(CFErrorRef*);
- bool (*soscc_SetEscrowRecords)(CFStringRef escrow_label, uint64_t tries, CFErrorRef *error);
- CFDictionaryRef (*soscc_CopyEscrowRecords)(CFErrorRef *error);
- CFDictionaryRef (*soscc_CopyBackupInformation)(CFErrorRef *error);
bool (*soscc_PeerAvailability)(CFErrorRef *error);
- bool (*sosbskb_WrapToBackupSliceKeyBagForView)(CFStringRef viewName, CFDataRef input, CFDataRef* output, CFDataRef* bskbEncoded, CFErrorRef* error);
- CFDataRef (*soscc_CopyAccountState)(CFErrorRef *error);
- bool (*soscc_DeleteAccountState)(CFErrorRef *error);
- CFDataRef (*soscc_CopyEngineData)(CFErrorRef *error);
- bool (*soscc_DeleteEngineState)(CFErrorRef *error);
SOSPeerInfoRef (*soscc_CopyApplicant)(CFErrorRef *error);
CFDataRef (*soscc_CopyCircleJoiningBlob)(SOSPeerInfoRef applicant, CFErrorRef *error);
CFDataRef (*soscc_CopyInitialSyncData)(SOSInitialSyncFlags flags, CFErrorRef *error);
bool (*soscc_JoinWithCircleJoiningBlob)(CFDataRef joiningBlob, PiggyBackProtocolVersion version, CFErrorRef *error);
bool (*soscc_SOSCCCleanupKVSKeys)(CFErrorRef *error);
- bool (*soscc_SOSCCTestPopulateKVSWithBadKeys)(CFErrorRef *error);
bool (*soscc_AccountHasPublicKey)(CFErrorRef *error);
- bool (*soscc_AccountIsNew)(CFErrorRef *error);
- bool (*soscc_IsThisDeviceLastBackup)(CFErrorRef *error);
bool (*soscc_requestSyncWithPeerOverKVS)(CFStringRef peerID, CFDataRef message, CFErrorRef *error);
CFBooleanRef (*soscc_SOSCCPeersHaveViewsEnabled)(CFArrayRef views, CFErrorRef *error);
bool (*socc_clearPeerMessageKeyInKVS)(CFStringRef peerID, CFErrorRef *error);
SOSCCAccountHasPublicKey_Server(&error));
}
break;
- case kSecXPCOpAccountIsNew:
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult,
- SOSCCAccountIsNew_Server(&error));
- }
- break;
+
case kSecXPCOpRequestToJoinAfterRestore:
if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult,
CFReleaseNull(parentEvent);
}
break;
- case kSecXPCOpRequestEnsureFreshParameters:
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult,
- SOSCCRequestEnsureFreshParameters_Server(&error));
- }
- break;
- case kSecXPCOpGetAllTheRings:
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- CFStringRef ringDescriptions = SOSCCGetAllTheRings_Server(&error);
- xpc_object_t xpc_dictionary = _CFXPCCreateXPCObjectFromCFObject(ringDescriptions);
- xpc_dictionary_set_value(replyMessage, kSecXPCKeyResult, xpc_dictionary);
- xpc_release(xpc_dictionary);
- CFReleaseNull(ringDescriptions);
- }
- break;
- case kSecXPCOpApplyToARing:
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- CFStringRef ringName = SecXPCDictionaryCopyString(event, kSecXPCKeyString, &error);
- xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, SOSCCApplyToARing_Server(ringName, &error));
- CFReleaseNull(ringName);
- }
- break;
- case kSecXPCOpWithdrawlFromARing:
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- CFStringRef ringName = SecXPCDictionaryCopyString(event, kSecXPCKeyString, &error);
- xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, SOSCCWithdrawlFromARing_Server(ringName, &error));
- CFReleaseNull(ringName);
- }
- break;
- case kSecXPCOpRingStatus:
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- CFStringRef ringName = SecXPCDictionaryCopyString(event, kSecXPCKeyString, &error);
- xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, SOSCCRingStatus_Server(ringName, &error));
- CFReleaseNull(ringName);
- }
- break;
- case kSecXPCOpEnableRing:
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- CFStringRef ringName = SecXPCDictionaryCopyString(event, kSecXPCKeyString, &error);
- xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, SOSCCEnableRing_Server(ringName, &error));
- CFReleaseNull(ringName);
- }
- break;
case kSecXPCOpRequestDeviceID:
case kSecXPCOpSetDeviceID:
case kSecXPCOpHandleIDSMessage:
SOSCCLoggedOutOfAccount_Server(&error));
}
break;
- case kSecXPCOpBailFromCircle:
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- // 0 is valid; ok for this parameter to be unset or incorrect type. Note: kSecXPCLimitInMinutes is actually seconds, not minutes
- uint64_t limit_in_seconds = xpc_dictionary_get_uint64(event, kSecXPCLimitInMinutes);
- xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult,
- SOSCCBailFromCircle_Server(limit_in_seconds, &error));
- }
- break;
case kSecXPCOpAcceptApplicants:
if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
xpc_object_t xapplicants = xpc_dictionary_get_value(event, kSecXPCKeyPeerInfoArray);
SOSCCCopyViewUnawarePeerInfo_Server(&error),
&error);
}
- break;
- case kSecXPCOpCopyAccountData:
- {
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- xpc_object_t xpc_account_object = NULL;
- CFDataRef accountData = SOSCCCopyAccountState_Server(&error);
- if(accountData)
- xpc_account_object = _CFXPCCreateXPCObjectFromCFObject(accountData);
-
- xpc_dictionary_set_value(replyMessage, kSecXPCKeyResult, xpc_account_object);
- CFReleaseNull(accountData);
- }
- break;
- }
- case kSecXPCOpDeleteAccountData:
- {
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- bool status = SOSCCDeleteAccountState_Server(&error);
- xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, status);
- }
- break;
- }
- case kSecXPCOpCopyEngineData:
- {
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-
- xpc_object_t xpc_engine_object = NULL;
- CFDataRef engineData = SOSCCCopyEngineData_Server(&error);
- if(engineData)
- xpc_engine_object = _CFXPCCreateXPCObjectFromCFObject(engineData);
-
- xpc_dictionary_set_value(replyMessage, kSecXPCKeyResult, xpc_engine_object);
- CFReleaseNull(engineData);
-
- }
break;
- }
- case kSecXPCOpDeleteEngineData:
- {
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- bool status = SOSCCDeleteEngineState_Server(&error);
- xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, status);
- }
- break;
- }
case kSecXPCOpCopyEngineState:
{
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- CFArrayRef array = SOSCCCopyEngineState_Server(&error);
- CFDataRef derData = NULL;
+ if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
+ CFArrayRef array = SOSCCCopyEngineState_Server(&error);
+ CFDataRef derData = NULL;
- require_quiet(array, done);
- derData = CFPropertyListCreateDERData(kCFAllocatorDefault, array, &error);
+ require_quiet(array, done);
+ derData = CFPropertyListCreateDERData(kCFAllocatorDefault, array, &error);
- require_quiet(derData, done);
- xpc_dictionary_set_data(replyMessage, kSecXPCKeyResult, CFDataGetBytePtr(derData), CFDataGetLength(derData));
+ require_quiet(derData, done);
+ xpc_dictionary_set_data(replyMessage, kSecXPCKeyResult, CFDataGetBytePtr(derData),CFDataGetLength(derData));
done:
CFReleaseNull(derData);
CFReleaseNull(array);
}
}
break;
- case kSecXPCOpCopyPeerPeerInfo:
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- xpc_dictionary_set_and_consume_PeerInfoArray(replyMessage, kSecXPCKeyResult,
- SOSCCCopyPeerPeerInfo_Server(&error),
- &error);
+ case kSecXPCOpCopyPeerPeerInfo:
+ if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
+ xpc_dictionary_set_and_consume_PeerInfoArray(replyMessage, kSecXPCKeyResult,
+ SOSCCCopyPeerPeerInfo_Server(&error),
+ &error);
}
break;
case kSecXPCOpCopyConcurringPeerPeerInfo:
SOSCCProcessEnsurePeerRegistration_Server(&error));
}
break;
- case kSecXPCOpCopyIncompatibilityInfo:
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- CFStringRef iis = SOSCCCopyIncompatibilityInfo_Server(&error);
- SecXPCDictionarySetString(replyMessage, kSecXPCKeyResult, iis, &error);
- CFReleaseSafe(iis);
- }
- break;
case kSecXPCOpRollKeys:
{
// false is valid, so it's safe for this parameter to be unset or incorrect type
CFReleaseNull(parentEvent);
}
break;
- case kSecXPCOpCopyYetToSyncViews:
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- CFArrayRef array = SOSCCCopyYetToSyncViewsList_Server(&error);
- if (array) {
- xpc_object_t xpc_array = _CFXPCCreateXPCObjectFromCFObject(array);
- xpc_dictionary_set_value(replyMessage, kSecXPCKeyResult, xpc_array);
- xpc_release(xpc_array);
- }
- CFReleaseNull(array);
- }
- break;
- case kSecXPCOpSetEscrowRecord:
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- CFStringRef escrow_label = SecXPCDictionaryCopyString(event, kSecXPCKeyEscrowLabel, &error); // NULL checked below
- uint64_t tries = xpc_dictionary_get_int64(event, kSecXPCKeyTriesLabel); // 0 is acceptable; safe for this parameter to be unset or incorrect type
-
- if (escrow_label != NULL) {
- bool result = SOSCCSetEscrowRecord_Server(escrow_label, tries, &error);
- if (result) {
- xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, result);
- }
- CFReleaseNull(escrow_label);
- }
- }
- break;
- case kSecXPCOpGetEscrowRecord:
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- CFDictionaryRef record = SOSCCCopyEscrowRecord_Server(&error);
- if (record) {
- xpc_object_t xpc_dictionary = _CFXPCCreateXPCObjectFromCFObject(record);
- xpc_dictionary_set_value(replyMessage, kSecXPCKeyResult, xpc_dictionary);
- xpc_release(xpc_dictionary);
- }
- CFReleaseNull(record);
- }
- break;
- case kSecXPCOpCopyBackupInformation:
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- CFDictionaryRef record = SOSCCCopyBackupInformation_Server(&error);
- if (record) {
- xpc_object_t xpc_dictionary = _CFXPCCreateXPCObjectFromCFObject(record);
- xpc_dictionary_set_value(replyMessage, kSecXPCKeyResult, xpc_dictionary);
- xpc_release(xpc_dictionary);
- }
- CFReleaseNull(record);
- }
- break;
-
- case kSecXPCOpIsThisDeviceLastBackup:
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, SOSCCkSecXPCOpIsThisDeviceLastBackup_Server(&error));
- }
- break;
case kSecXPCOpPeersHaveViewsEnabled:
{
CFArrayRef viewSet = SecXPCDictionaryCopyArray(event, kSecXPCKeyArray, &error);
}
}
break;
- case kSecXPCOpWrapToBackupSliceKeyBagForView:
- {
- CFStringRef viewname = SecXPCDictionaryCopyString(event, kSecXPCKeyViewName, &error);
- if(viewname) {
- CFDataRef plaintext = SecXPCDictionaryCopyData(event, kSecXPCData, &error);
- if (plaintext) {
- CFDataRef ciphertext = NULL;
- CFDataRef bskbEncoded = NULL;
-
- bool result = SOSWrapToBackupSliceKeyBagForView_Server(viewname, plaintext, &ciphertext, &bskbEncoded, &error);
- xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, result);
-
- if(!error && result) {
- if(ciphertext) {
- xpc_dictionary_set_data(replyMessage, kSecXPCData, CFDataGetBytePtr(ciphertext), CFDataGetLength(ciphertext));
- }
- if(bskbEncoded) {
- xpc_dictionary_set_data(replyMessage, kSecXPCKeyKeybag, CFDataGetBytePtr(bskbEncoded), CFDataGetLength(bskbEncoded));
- }
- }
- CFReleaseSafe(ciphertext);
- CFReleaseSafe(bskbEncoded);
- }
- CFReleaseSafe(plaintext);
- }
- CFReleaseNull(viewname);
- }
- break;
case kSecXPCOpCopyApplication:
if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementCircleJoin, &error)) {
SOSPeerInfoRef peerInfo = SOSCCCopyApplication_Server(&error);
xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, retval);
}
break;
- case kSecXPCOpPopulateKVS:
- if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
- bool retval = SOSCCTestPopulateKVSWithBadKeys_Server(&error);
- xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, retval);
- }
- break;
+
case kSecXPCOpMessageFromPeerIsPending:
{
SOSPeerInfoRef peer = SecXPCDictionaryCopyPeerInfo(event, kSecXPCKeyPeerInfo, &error);
ONE_TEST(si_24_sectrust_passbook)
ONE_TEST(si_25_cms_skid)
ONE_TEST(si_26_sectrust_copyproperties)
-ONE_TEST(si_27_sectrust_exceptions)
ONE_TEST(si_28_sectrustsettings)
ONE_TEST(si_29_cms_chain_mode)
ONE_TEST(si_32_sectrust_pinning_required)
ok([decrypted isEqualToData:message], "Decrypted data differs: %@ vs %@", decrypted, message);
}
+static void keychainTest(void) {
+ id accessControl = CFBridgingRelease(SecAccessControlCreateWithFlags(NULL, kSecAttrAccessibleWhenUnlocked, kSecAccessControlPrivateKeyUsage, NULL));
+ NSDictionary *keyAttributes = @{ (id)kSecAttrTokenID : (id)kSecAttrTokenIDAppleKeyStore,
+ (id)kSecAttrKeyType : (id)kSecAttrKeyTypeECSECPrimeRandom,
+ (id)kSecPrivateKeyAttrs : @{
+ (id)kSecAttrAccessControl : accessControl,
+ (id)kSecAttrIsPermanent : @YES,
+ (id)kSecAttrLabel : @"si_44_seckey_aks_1",
+ },
+ };
+ NSError *error;
+ id key = (__bridge_transfer id)SecKeyCreateRandomKey((CFDictionaryRef)keyAttributes, (void *)&error);
+ ok(key, "failed to create random key %@", error);
+ id pubKey = CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)key));
+ ok(pubKey, "failed to get public key from SEP key");
+ key = nil;
+
+ NSDictionary *query = @{
+ (id)kSecReturnRef: @YES,
+ (id)kSecClass: (id)kSecClassKey,
+ (id)kSecAttrLabel: @"si_44_seckey_aks_1",
+ };
+ OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, (void *)&key);
+ is(status, errSecSuccess, "getting SEP key from keychain failed");
+
+ NSError *err;
+ NSData *data = [@"message" dataUsingEncoding:NSUTF8StringEncoding];
+ NSData *sig = CFBridgingRelease(SecKeyCreateSignature((SecKeyRef)key, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (CFDataRef)data, (void *)&err));
+ ok(sig, "failed to create signature: %@", err);
+ ok(SecKeyVerifySignature((SecKeyRef)pubKey, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (CFDataRef)data, (CFDataRef)sig, (void *)&err), "failed to verify signature: %@", err);
+
+ status = SecItemDelete((CFDictionaryRef)query);
+ is(status, errSecSuccess, "deleting SEP key from keychain failed");
+
+ status = SecItemCopyMatching((CFDictionaryRef)query, (void *)&key);
+ is(status, errSecItemNotFound, "SEP key was not deleted from keychain");
+}
+
+static void secAccessControlDescriptionTest(void) {
+ NSError *error;
+ NSObject *ac = CFBridgingRelease(SecAccessControlCreate(kCFAllocatorDefault, (void *)&error));
+ ok(ac, "failed to create ac: %@", error);
+ ok(SecAccessControlSetProtection((__bridge SecAccessControlRef)ac, kSecAttrAccessibleWhenUnlocked, (void *)&error), "failed to set protection: %@", error);
+
+ NSString *desc = ac.description;
+ ok([desc isEqualToString:@"<SecAccessControlRef: ak>"], "unexpected desc: %@", desc);
+
+ SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{});
+ desc = ac.description;
+ ok([desc isEqualToString:@"<SecAccessControlRef: ak>"], "unexpected desc: %@", desc);
+
+ SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": (__bridge id)kCFBooleanTrue});
+ desc = ac.description;
+ ok([desc isEqualToString:@"<SecAccessControlRef: ak;od(true)>"], "unexpected desc: %@", desc);
+
+ SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": (__bridge id)kCFBooleanTrue, @"oe": (__bridge id)kCFBooleanFalse});
+ desc = ac.description;
+ ok([desc isEqualToString:@"<SecAccessControlRef: ak;od(true);oe(false)>"], "unexpected desc: %@", desc);
+
+ SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": @"huh"});
+ desc = ac.description;
+ ok([desc isEqualToString:@"<SecAccessControlRef: ak;od(huh)>"], "unexpected desc: %@", desc);
+
+ SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": @2});
+ desc = ac.description;
+ ok([desc isEqualToString:@"<SecAccessControlRef: ak;od(2)>"], "unexpected desc: %@", desc);
+
+ NSData *shortData = [NSData dataWithBytes:"\x01\x02\x03" length:3];
+ SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": shortData});
+ desc = ac.description;
+ ok([desc isEqualToString:@"<SecAccessControlRef: ak;od(010203)>"], "unexpected desc: %@", desc);
+
+ NSData *longData = [NSMutableData dataWithLength:128];
+ SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": longData});
+ desc = ac.description;
+ ok([desc isEqualToString:@"<SecAccessControlRef: ak;od(00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000...(128b))>"], "unexpected desc: %@", desc);
+
+ SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": @{@"kofn": @2}});
+ desc = ac.description;
+ ok([desc isEqualToString:@"<SecAccessControlRef: ak;od(kofn(2))>"], "unexpected desc: %@", desc);
+}
+
int si_44_seckey_aks(int argc, char *const *argv) {
@autoreleasepool {
BOOL testPKA = YES;
testPKA = NO;
#endif
- plan_tests(testPKA ? 102 : 87);
+ plan_tests(testPKA ? 119 : 104);
+ secAccessControlDescriptionTest();
secKeySepTest(testPKA);
attestationTest(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, NO);
attestationTest(kSecAttrAccessibleUntilReboot, YES);
keyFromBlobTest();
+ keychainTest();
// Put SEP keys into test-keybag mode. Available only when running in direct-mode, not with extension.
SecKeySetParameter(NULL, kSecAttrTokenIDAppleKeyStore, kCFBooleanTrue, NULL);
return cfType && CFGetTypeID(cfType) == CFNullGetTypeID();
}
+// Usage: void foo(CFTypeRef value) { CFDataRef data = CFCast(CFData, value); }
+#define CFCast(type, value) \
+ ((value != NULL) && CFGetTypeID(value) == type ## GetTypeID() ? (type ## Ref)(value) : NULL)
+
+#define CFCastWithError(type, value, error) \
+ ((value != NULL) && CFGetTypeID(value) == type ## GetTypeID() ? \
+ (type ## Ref)(value) : \
+ (SecError(errSecParam, error, CFSTR("Unexpected type")), NULL))
+
//
// MARK CFEqual Helpers
//
});
}
+// Please make sure you want to do this. Any use of the outstanding connections to this DB will cause a crash.
+void SecDbForceClose(SecDbRef db) {
+ dispatch_sync(db->queue, ^{
+ CFArrayForEach(db->connections, ^(const void* p) {
+ SecDbConnectionRef connection = (SecDbConnectionRef)p;
+
+ // this pointer is claimed to be nonretained
+ connection->db = NULL;
+
+ if(connection->handle) {
+ sqlite3_close(connection->handle);
+ connection->handle = NULL;
+ }
+ });
+ CFArrayRemoveAllValues(db->connections);
+ dispatch_semaphore_signal(db->write_semaphore);
+ dispatch_semaphore_signal(db->read_semaphore);
+ });
+}
+
bool SecDbPerformRead(SecDbRef db, CFErrorRef *error, void (^perform)(SecDbConnectionRef dbconn)) {
SecDbConnectionRef dbconn = SecDbConnectionAcquire(db, true, error);
bool success = false;
CFIndex SecDbIdleConnectionCount(SecDbRef db);
void SecDbReleaseAllConnections(SecDbRef db);
+void SecDbForceClose(SecDbRef db);
+
CFStringRef SecDbGetPath(SecDbRef db);
// MARK: -
@interface SecXPCHelper : NSObject
+ (NSSet<Class> *)safeErrorClasses;
-+ (NSError *)cleanseErrorForXPC:(NSError * _Nullable)error;
++ (NSError * _Nullable)cleanseErrorForXPC:(NSError * _Nullable)error;
/*
* Some NSError objects contain non-NSSecureCoding-compliant userInfo.
return NSStringFromClass([object class]);
}
-+ (NSError *)cleanseErrorForXPC:(NSError * _Nullable)error
++ (NSError * _Nullable)cleanseErrorForXPC:(NSError * _Nullable)error
{
if (!error) {
return nil;
_OTCliqueStatusToString
_OTCliqueStatusFromString
+_OTCDPStatusToString
_OTCliqueCDPContextTypeNone
_OTCliqueCDPContextTypeSignIn
_SFAnalyticsColumnSoftFailureCount
_SFAnalyticsColumnSampleValue
_SFAnalyticsColumnSampleName
+_SFAnalyticsPostTime
_SFAnalyticsEventTime
_SFAnalyticsEventType
_SFAnalyticsEventTypeErrorEvent
name = Security_frameworks_ios;
productName = kernel;
};
+ 0CA378E123876DD100090B7E /* reset_account */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 0CA378E323876DD100090B7E /* Build configuration list for PBXAggregateTarget "reset_account" */;
+ buildPhases = (
+ 0CA378E623876DEC00090B7E /* CopyFiles */,
+ );
+ dependencies = (
+ );
+ name = reset_account;
+ productName = codesigning_DTrace;
+ };
4C541F840F250BF500E508AE /* Security_executables_ios */ = {
isa = PBXAggregateTarget;
buildConfigurationList = 4C541FA30F250C8C00E508AE /* Build configuration list for PBXAggregateTarget "Security_executables_ios" */;
buildPhases = (
);
dependencies = (
+ 0CA378EB23876E1000090B7E /* PBXTargetDependency */,
D4E0E9BC2224E15500A802E0 /* PBXTargetDependency */,
D45D8F5C2224D9F100D6C124 /* PBXTargetDependency */,
D45D8F5A2224D8A100D6C124 /* PBXTargetDependency */,
buildPhases = (
);
dependencies = (
+ 0CA378E923876E0900090B7E /* PBXTargetDependency */,
D4EB53C9223C4AB5009101F8 /* PBXTargetDependency */,
D4E0E9BE2224E15E00A802E0 /* PBXTargetDependency */,
D45D8F902224DC9900D6C124 /* PBXTargetDependency */,
0C9FB40720D872A600864612 /* CoreCDP.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C9FB40120D8729A00864612 /* CoreCDP.framework */; };
0C9FB40920D8735500864612 /* CoreCDP.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C9FB40120D8729A00864612 /* CoreCDP.framework */; };
0CA2282F2187A5CA00A1C56C /* BottledPeer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0C4F84216FB56B00C14C61 /* BottledPeer.swift */; };
+ 0CA378E723876DFC00090B7E /* reset_ick_account in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0C7382F023863AD5004F98CB /* reset_ick_account */; };
0CA4B4722171410200B17169 /* EscrowKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0C4F83216FB55600C14C61 /* EscrowKeys.swift */; };
0CA4EBF3202B8D9C002B1D96 /* CloudKitKeychainSyncingTestsBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CA4EBF2202B8D1D002B1D96 /* CloudKitKeychainSyncingTestsBase.m */; };
0CA4EBF4202B8DBE002B1D96 /* CloudKitKeychainSyncingTestsBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CA4EBF2202B8D1D002B1D96 /* CloudKitKeychainSyncingTestsBase.m */; };
0CAD1E591E1C5CBD00537693 /* secd-52-offering-gencount-reset.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78C4F1D8085D800865A7C /* secd-52-offering-gencount-reset.m */; };
0CAD1E5A1E1C5CD100537693 /* secd-71-engine-save.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78C641D8085D800865A7C /* secd-71-engine-save.m */; };
0CAD1E5D1E1C5CF900537693 /* secd-80-views-alwayson.m in Sources */ = {isa = PBXBuildFile; fileRef = 7281E08B1DFD0A380021E1B7 /* secd-80-views-alwayson.m */; };
- 0CAD1E5E1E1C5D0600537693 /* secd-95-escrow-persistence.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78C741D8085D800865A7C /* secd-95-escrow-persistence.m */; };
0CADDF0721545CF100DF8B06 /* OctagonPairingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CADDF0421545C8E00DF8B06 /* OctagonPairingTests.swift */; };
0CB50A0D20AA486800FE4675 /* SOSAccountTrustClassic+Expansion.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CE760471E12F2F200B4381E /* SOSAccountTrustClassic+Expansion.m */; };
0CB50A0E20AA4C2F00FE4675 /* SOSAccountTrustClassic+Circle.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CE760491E12F30200B4381E /* SOSAccountTrustClassic+Circle.m */; };
0CB5C678218B803C0044F730 /* OTPrivateKey+SF.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C3BB3522188E18A0018FC14 /* OTPrivateKey+SF.m */; };
0CB72D9D21E42FCF00D8BC9B /* OTApplicantToSponsorRound2M1.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE28E214054F6003BFDB5 /* OTApplicantToSponsorRound2M1.m */; };
0CB72D9E21E42FCF00D8BC9B /* OTPairingMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE2A2214055CF003BFDB5 /* OTPairingMessage.m */; };
- 0CB72D9F21E42FCF00D8BC9B /* OTSOSMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CE9C98A21B8891A006BDD80 /* OTSOSMessage.m */; };
0CB72DA021E42FCF00D8BC9B /* OTSponsorToApplicantRound1M2.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE28D214054F6003BFDB5 /* OTSponsorToApplicantRound1M2.m */; };
0CB72DA121E42FCF00D8BC9B /* OTSponsorToApplicantRound2M2.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE290214054F7003BFDB5 /* OTSponsorToApplicantRound2M2.m */; };
0CB8DC9A2194B14C0021A7C8 /* OTVouchWithBottleOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CB8DC992194B1440021A7C8 /* OTVouchWithBottleOperation.m */; };
0CE887D22299A8CF0082D120 /* libMobileGestalt.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = BE53602C209BBF2F0027E25A /* libMobileGestalt.tbd */; };
0CE887D32299A9090082D120 /* libutilities.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC0BCC361D8C684F00070CB0 /* libutilities.a */; };
0CE887D52299A9C70082D120 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E71F3E3016EA69A900FAF9B4 /* SystemConfiguration.framework */; };
+ 0CE902352395D0A3005E3F8C /* AuthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4A7DD7320A26CF900F51F3F /* AuthKit.framework */; };
0CF406522072E422003D6A7F /* SFSignInAnalyticsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF405F42072E2BF003D6A7F /* SFSignInAnalyticsTests.m */; };
0CF70BD9218BED1000EC3515 /* CuttlefishExtensionWorkaround.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CF70BD6218BECF500EC3515 /* CuttlefishExtensionWorkaround.swift */; };
0CF70BDA218BEFAE00EC3515 /* CuttlefishExtensionWorkaround.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CF70BD6218BECF500EC3515 /* CuttlefishExtensionWorkaround.swift */; };
1B5EAADD2252ABCD008D27E7 /* OTFetchViewsOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B5EAADB2252ABCC008D27E7 /* OTFetchViewsOperation.m */; };
1B8341B92239AD3A002BF18A /* TPPBPolicyKeyViewMapping.proto in Sources */ = {isa = PBXBuildFile; fileRef = 1B8341B72239AD39002BF18A /* TPPBPolicyKeyViewMapping.proto */; };
1B8D2D96226E1FA500C94238 /* SetValueTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CE15E2A222DF63500B7EAA5 /* SetValueTransformer.swift */; };
- 1B916CD0223FFF25006657FD /* ProtocolBuffer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6C0B0C441E2537CC007F95E5 /* ProtocolBuffer.framework */; };
1B995259226681FA00A2D6CD /* PolicyReporter.h in Sources */ = {isa = PBXBuildFile; fileRef = 1B995256226681ED00A2D6CD /* PolicyReporter.h */; };
1B99525A226681FA00A2D6CD /* PolicyReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B995258226681EE00A2D6CD /* PolicyReporter.m */; };
1BB1CAB7232C05BD001D0C71 /* CuttlefishXPCWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BB1CAB4232C05BB001D0C71 /* CuttlefishXPCWrapper.m */; };
483E798F1DC87605005C0008 /* secd-67-prefixedKeyIDs.m in Sources */ = {isa = PBXBuildFile; fileRef = 483E79891DC875F2005C0008 /* secd-67-prefixedKeyIDs.m */; };
48AC7B73232B1DA600F02B6F /* SOSIntervalEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 48AC7B71232B1A7000F02B6F /* SOSIntervalEvent.m */; };
48CC589F1DA5FF2700EBD9DB /* secd-66-account-recovery.m in Sources */ = {isa = PBXBuildFile; fileRef = 48CC58971DA5FF0B00EBD9DB /* secd-66-account-recovery.m */; };
- 48E617211DBEC6BA0098EAAD /* SOSBackupInformation.m in Sources */ = {isa = PBXBuildFile; fileRef = 48E6171A1DBEC40D0098EAAD /* SOSBackupInformation.m */; };
- 48E617221DBEC6C60098EAAD /* SOSBackupInformation.h in Headers */ = {isa = PBXBuildFile; fileRef = 48E6171B1DBEC40D0098EAAD /* SOSBackupInformation.h */; };
48FE669620E6E69D00FAEF17 /* SOSAuthKitHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 48FE668F20E6E69B00FAEF17 /* SOSAuthKitHelpers.m */; };
48FE669720E6E69D00FAEF17 /* SOSAuthKitHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 48FE669520E6E69C00FAEF17 /* SOSAuthKitHelpers.h */; };
4AF7000115AFB73800B9D400 /* SecOTRMath.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AF7FFF715AFB73800B9D400 /* SecOTRMath.h */; settings = {ATTRIBUTES = (Private, ); }; };
4C7391790B01745000C4CBFA /* vmdh.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7391770B01745000C4CBFA /* vmdh.h */; settings = {ATTRIBUTES = (Private, ); }; };
4C7416040F1D71A2008E0E4D /* SecSCEP.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7416020F1D71A2008E0E4D /* SecSCEP.h */; settings = {ATTRIBUTES = (Private, ); }; };
4C7608B30AC34A8100980096 /* SecCertificatePriv.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7608B10AC34A8100980096 /* SecCertificatePriv.h */; settings = {ATTRIBUTES = (Private, ); }; };
- 4C7913251799A5CC00A9633E /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C7913241799A5CB00A9633E /* MobileCoreServices.framework */; };
4C7CE5700DC7DC6600AE53FC /* SecEntitlements.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7CE56E0DC7DB0A00AE53FC /* SecEntitlements.h */; settings = {ATTRIBUTES = (Private, ); }; };
4C84DA551720698900AEE225 /* AppleAccount.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C84DA541720698900AEE225 /* AppleAccount.framework */; };
4C87F3A80D611C26000E7104 /* SecTrustPriv.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C87F3A70D611C26000E7104 /* SecTrustPriv.h */; settings = {ATTRIBUTES = (Private, ); }; };
52A23EDD161DEC3F00E271E0 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 52A23EDB161DEC3700E271E0 /* Default-568h@2x.png */; };
52D82BDF16A621F70078DFE5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7FCBE431314471B000DE34E /* Foundation.framework */; };
52D82BEE16A622370078DFE5 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D82BD316A5EADA0078DFE5 /* Security.framework */; };
+ 52DA3C7123C7E63600FEEDFF /* KCTLKRequestTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 52DA3C6F23C7E63500FEEDFF /* KCTLKRequestTest.m */; };
5328C0521738903F00708984 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C32C0AF0A4975F6002891BD /* Security.framework */; };
533B5D4F177CD63100995334 /* SpringBoardServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52222CC0167BDAE100EDD09C /* SpringBoardServices.framework */; };
5346480217331E1200FE9172 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7FCBE431314471B000DE34E /* Foundation.framework */; };
D4707A2D2114C1E8005BCFDA /* SecCmsContentInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = D4707A2B2114B31A005BCFDA /* SecCmsContentInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
D4707A2F2114C315005BCFDA /* SecCmsDigestContext.h in Headers */ = {isa = PBXBuildFile; fileRef = D4707A2E2114C30A005BCFDA /* SecCmsDigestContext.h */; settings = {ATTRIBUTES = (Private, ); }; };
D4707A302114C316005BCFDA /* SecCmsDigestContext.h in Headers */ = {isa = PBXBuildFile; fileRef = D4707A2E2114C30A005BCFDA /* SecCmsDigestContext.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ D477CB78237E482800C02355 /* si-88-sectrust-valid-data in Resources */ = {isa = PBXBuildFile; fileRef = D477CB76237E453C00C02355 /* si-88-sectrust-valid-data */; };
+ D477CB79237E484300C02355 /* si-88-sectrust-valid-data in Resources */ = {isa = PBXBuildFile; fileRef = D477CB76237E453C00C02355 /* si-88-sectrust-valid-data */; };
+ D477CB7B237E4BD700C02355 /* ExceptionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D477CB7A237E4BD700C02355 /* ExceptionTests.m */; };
+ D477CB7C237E4BD700C02355 /* ExceptionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D477CB7A237E4BD700C02355 /* ExceptionTests.m */; };
D479F6E21F980FAB00388D28 /* Trust.strings in Resources */ = {isa = PBXBuildFile; fileRef = D479F6DF1F980F8F00388D28 /* Trust.strings */; };
D479F6E31F981FD600388D28 /* OID.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4C198F1F0ACDB4BF00AAB142 /* OID.strings */; };
D479F6E41F981FD600388D28 /* Certificate.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4C198F1D0ACDB4BF00AAB142 /* Certificate.strings */; };
D4B68C66211A8186009FED69 /* trustd_spi.h in Headers */ = {isa = PBXBuildFile; fileRef = D4B68C64211A8186009FED69 /* trustd_spi.h */; };
D4B68C68211A827C009FED69 /* trustd_spi.c in Sources */ = {isa = PBXBuildFile; fileRef = D4B68C65211A8186009FED69 /* trustd_spi.c */; };
D4B6D57C2069D8450099FBEF /* si-34-cms-timestamp.m in Sources */ = {isa = PBXBuildFile; fileRef = D4B6D57B2069D8450099FBEF /* si-34-cms-timestamp.m */; };
- D4B858671D370D9A003B2D95 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4B858661D370D9A003B2D95 /* MobileCoreServices.framework */; };
D4BD5E83228A6823001650A7 /* util.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BC6F79621C9955D005ED67A /* util.m */; };
D4BD5E85228A6854001650A7 /* util.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BC6F79621C9955D005ED67A /* util.m */; };
D4BD5E86228A6855001650A7 /* util.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BC6F79621C9955D005ED67A /* util.m */; };
DC00C92420B3B82600628BEB /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D4119E72202BDF2B0048587B /* libz.tbd */; };
DC00C93420B48B4100628BEB /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E71F3E3016EA69A900FAF9B4 /* SystemConfiguration.framework */; };
DC00C93520B48BA800628BEB /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CBCE5A90BE7F69100FF81F5 /* IOKit.framework */; };
+ DC03592D235FCCD500F14883 /* KCInitialMessageData.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A47FFB5228F5E9000F781B8 /* KCInitialMessageData.m */; };
DC047081218BB21E0078BDAA /* OTCuttlefishAccountStateHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = DC04707F218BB21E0078BDAA /* OTCuttlefishAccountStateHolder.h */; };
DC047082218BB21E0078BDAA /* OTCuttlefishAccountStateHolder.m in Sources */ = {isa = PBXBuildFile; fileRef = DC047080218BB21E0078BDAA /* OTCuttlefishAccountStateHolder.m */; };
DC047087218BCEF20078BDAA /* OTOperationDependencies.h in Headers */ = {isa = PBXBuildFile; fileRef = DC047085218BCEF20078BDAA /* OTOperationDependencies.h */; };
DC0BD4F621BB0610006B9154 /* CKKSKeychainBackedKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DC0BD4F021BB05F2006B9154 /* CKKSKeychainBackedKey.m */; };
DC0C343821FA7DEB00417D04 /* SecEscrowRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = DC90A4C021F27680001300EB /* SecEscrowRequest.h */; settings = {ATTRIBUTES = (Private, ); }; };
DC0C343A21FA7DEB00417D04 /* SecEscrowRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = DC90A4C021F27680001300EB /* SecEscrowRequest.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ DC0D16012363A1D6007F0951 /* OTSetCDPBitOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC0D15FF2363A1D6007F0951 /* OTSetCDPBitOperation.h */; };
+ DC0D16022363A1D6007F0951 /* OTSetCDPBitOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DC0D16002363A1D6007F0951 /* OTSetCDPBitOperation.m */; };
+ DC0D16062363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC0D16042363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.h */; };
+ DC0D16072363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DC0D16052363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.m */; };
+ DC0DE87123750340006E2EAE /* OTPairingMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE2A2214055CF003BFDB5 /* OTPairingMessage.m */; };
DC0EF8F2208697C600AB9E95 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0EF8F1208697C600AB9E95 /* main.swift */; };
DC0FA6B02291F63F00FE01C4 /* OctagonPendingFlag.h in Headers */ = {isa = PBXBuildFile; fileRef = DC0FA6AE2291F63F00FE01C4 /* OctagonPendingFlag.h */; };
DC0FA6B12291F63F00FE01C4 /* OctagonPendingFlag.m in Sources */ = {isa = PBXBuildFile; fileRef = DC0FA6AF2291F63F00FE01C4 /* OctagonPendingFlag.m */; };
DC14478A1F5764C600236DB4 /* CKKSResultOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC1447881F5764C600236DB4 /* CKKSResultOperation.h */; };
DC14478C1F5764C600236DB4 /* CKKSResultOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DC1447891F5764C600236DB4 /* CKKSResultOperation.m */; };
DC1447961F5766D200236DB4 /* NSOperationCategories.h in Headers */ = {isa = PBXBuildFile; fileRef = DC1447941F5766D200236DB4 /* NSOperationCategories.h */; };
+ DC14C4C223AAACED007F673F /* Container_BottledPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3A9B2523A9D6120073ED06 /* Container_BottledPeers.swift */; };
DC15F7661E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC15F7641E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.h */; };
DC15F7681E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DC15F7651E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.m */; };
DC15F79C1E68EAD5003B9A40 /* CKKSTests+API.m in Sources */ = {isa = PBXBuildFile; fileRef = DC15F79B1E68EAD5003B9A40 /* CKKSTests+API.m */; };
DC311E7A2124B8EF002F5EAE /* aks_real_witness.h in Headers */ = {isa = PBXBuildFile; fileRef = DC311E782124B8EF002F5EAE /* aks_real_witness.h */; };
DC311E7B2124B8EF002F5EAE /* aks_real_witness.c in Sources */ = {isa = PBXBuildFile; fileRef = DC311E792124B8EF002F5EAE /* aks_real_witness.c */; };
DC337B1F1EA04E2100B3A1F0 /* SecBase64.h in Headers */ = {isa = PBXBuildFile; fileRef = 18351B8F14CB65870097860E /* SecBase64.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ DC33D7BD2374FD0500A68155 /* OTSponsorToApplicantRound1M2.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE28D214054F6003BFDB5 /* OTSponsorToApplicantRound1M2.m */; };
+ DC33D7BE2374FD0A00A68155 /* OTSponsorToApplicantRound2M2.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE290214054F7003BFDB5 /* OTSponsorToApplicantRound2M2.m */; };
DC340C54208E828F004D7EEC /* TrustedPeers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEF88C281EAFFC3F00357577 /* TrustedPeers.framework */; };
DC3502B81E0208BE00BC0587 /* CKKSTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DC3502B71E0208BE00BC0587 /* CKKSTests.m */; };
DC3502C51E020D5100BC0587 /* libASN1.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC8834081D8A218F00CE0ACA /* libASN1.a */; };
DC3A81D61D99D57F000C7419 /* libcoretls.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 0CFC029B1D41650700E6283B /* libcoretls.dylib */; };
DC3A81D71D99D58A000C7419 /* libcoretls_cfhelpers.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DC3A81D41D99D567000C7419 /* libcoretls_cfhelpers.dylib */; };
DC3A81EC1D99F568000C7419 /* libcoretls.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 0CFC029B1D41650700E6283B /* libcoretls.dylib */; };
+ DC3A9B2723A9D8BD0073ED06 /* Container_BottledPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3A9B2523A9D6120073ED06 /* Container_BottledPeers.swift */; };
+ DC3A9B2823A9D8C40073ED06 /* Container_BottledPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3A9B2523A9D6120073ED06 /* Container_BottledPeers.swift */; };
DC3AA2782097DF70007CA68A /* readline.c in Sources */ = {isa = PBXBuildFile; fileRef = DC65E7BE1D8CBB1500152EF0 /* readline.c */; };
DC3AA2792097DF71007CA68A /* readline.c in Sources */ = {isa = PBXBuildFile; fileRef = DC65E7BE1D8CBB1500152EF0 /* readline.c */; };
DC3AA27A2097DF84007CA68A /* not_on_this_platorm.c in Sources */ = {isa = PBXBuildFile; fileRef = DC0BCDB41D8C6A5B00070CB0 /* not_on_this_platorm.c */; };
DC4269101E82FD9F002B7110 /* server_security_helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4269061E82FBDF002B7110 /* server_security_helpers.m */; };
DC4269111E82FDA0002B7110 /* server_security_helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4269061E82FBDF002B7110 /* server_security_helpers.m */; };
DC4269121E82FDA1002B7110 /* server_security_helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4269061E82FBDF002B7110 /* server_security_helpers.m */; };
+ DC4415B423610BF40087981C /* OctagonTests+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4415B323610BF40087981C /* OctagonTests+Account.swift */; };
DC45D43D22EB619D00CEB6B7 /* OctagonStateMachineObservers.h in Headers */ = {isa = PBXBuildFile; fileRef = DC45D43B22EB619D00CEB6B7 /* OctagonStateMachineObservers.h */; };
DC45D43E22EB619D00CEB6B7 /* OctagonStateMachineObservers.m in Sources */ = {isa = PBXBuildFile; fileRef = DC45D43C22EB619D00CEB6B7 /* OctagonStateMachineObservers.m */; };
+ DC4A73C5235E69D800DB1E6E /* OTApplicantToSponsorRound2M1.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE28E214054F6003BFDB5 /* OTApplicantToSponsorRound2M1.m */; };
DC4A76A02212676D006F2D8F /* CloudServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C8A38C817B93DF10001B4C0 /* CloudServices.framework */; };
DC4A76A3221267D4006F2D8F /* EscrowRequestServerHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4A76A2221267D4006F2D8F /* EscrowRequestServerHelpers.m */; };
DC4A76A5221268A6006F2D8F /* CloudServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C8A38C817B93DF10001B4C0 /* CloudServices.framework */; };
DC4A76AC221269E4006F2D8F /* CloudServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C8A38C817B93DF10001B4C0 /* CloudServices.framework */; };
DC4A76AD22126A17006F2D8F /* CloudServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C8A38C817B93DF10001B4C0 /* CloudServices.framework */; };
DC4A76AE22126C49006F2D8F /* CloudServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C8A38C817B93DF10001B4C0 /* CloudServices.framework */; };
+ DC4CD9842372294E00EF55FC /* OctagonTests+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4CD9822372294D00EF55FC /* OctagonTests+Helpers.swift */; };
DC4DB1501E24692100CD6769 /* CKKSKey.h in Headers */ = {isa = PBXBuildFile; fileRef = DC4DB14E1E24692100CD6769 /* CKKSKey.h */; };
DC4DB1521E24692100CD6769 /* CKKSKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4DB14F1E24692100CD6769 /* CKKSKey.m */; };
DC4DB15F1E2590B100CD6769 /* CKKSAESSIVEncryptionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4DB15E1E2590B100CD6769 /* CKKSAESSIVEncryptionTests.m */; };
DC52EC381D80CFDB00B0A59C /* secToolFileIO.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D931D8085F200865A7C /* secToolFileIO.c */; };
DC52EC391D80CFDF00B0A59C /* secViewDisplay.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D9E1D8085F200865A7C /* secViewDisplay.c */; };
DC52EC3A1D80CFE400B0A59C /* keychain_log.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D9B1D8085F200865A7C /* keychain_log.m */; };
- DC52EC3B1D80CFE900B0A59C /* syncbackup.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D9D1D8085F200865A7C /* syncbackup.m */; };
DC52EC4E1D80D01F00B0A59C /* swcagent_client.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78EA01D80860C00865A7C /* swcagent_client.c */; };
DC52EC4F1D80D02400B0A59C /* SecuritydXPC.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78E9A1D8085FC00865A7C /* SecuritydXPC.c */; };
DC52EC5D1D80D06300B0A59C /* SecLogging.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78E651D8085FC00865A7C /* SecLogging.c */; };
DC52EC731D80D12E00B0A59C /* sc-20-keynames.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78CFD1D8085F200865A7C /* sc-20-keynames.m */; };
DC52EC741D80D13500B0A59C /* SOSTestDataSource.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D0C1D8085F200865A7C /* SOSTestDataSource.c */; };
DC52EC751D80D13B00B0A59C /* sc-42-circlegencount.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D021D8085F200865A7C /* sc-42-circlegencount.c */; };
- DC52EC761D80D13F00B0A59C /* sc-150-ring.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D061D8085F200865A7C /* sc-150-ring.m */; };
DC52EC771D80D14400B0A59C /* sc-130-resignationticket.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D041D8085F200865A7C /* sc-130-resignationticket.c */; };
DC52EC781D80D14800B0A59C /* SOSRegressionUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D0A1D8085F200865A7C /* SOSRegressionUtilities.m */; };
DC52EC791D80D14D00B0A59C /* sc-45-digestvector.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D031D8085F200865A7C /* sc-45-digestvector.c */; };
DC52ECE81D80D2FA00B0A59C /* pbkdf2-00-hmac-sha1.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DA41D8085FC00865A7C /* pbkdf2-00-hmac-sha1.c */; };
DC52ECE91D80D2FA00B0A59C /* spbkdf-00-hmac-sha1.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DA51D8085FC00865A7C /* spbkdf-00-hmac-sha1.c */; };
DC52ECEA1D80D30900B0A59C /* so_01_serverencryption.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78E171D8085FC00865A7C /* so_01_serverencryption.c */; };
- DC52ED9E1D80D4ED00B0A59C /* secd-95-escrow-persistence.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78C741D8085D800865A7C /* secd-95-escrow-persistence.m */; };
DC52ED9F1D80D4F200B0A59C /* SOSTransportTestTransports.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78C7C1D8085D800865A7C /* SOSTransportTestTransports.m */; };
DC52EDA01D80D4F700B0A59C /* sd-10-policytree.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78C3D1D8085D800865A7C /* sd-10-policytree.m */; };
DC52EDB51D80D5C500B0A59C /* secd-03-corrupted-items.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78C391D8085D800865A7C /* secd-03-corrupted-items.m */; };
DC52EE4A1D80D71900B0A59C /* si-24-sectrust-itms.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DC31D8085FC00865A7C /* si-24-sectrust-itms.c */; };
DC52EE4C1D80D71900B0A59C /* si-24-sectrust-passbook.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DC51D8085FC00865A7C /* si-24-sectrust-passbook.c */; };
DC52EE4D1D80D71900B0A59C /* si-26-sectrust-copyproperties.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DC61D8085FC00865A7C /* si-26-sectrust-copyproperties.c */; };
- DC52EE4E1D80D71900B0A59C /* si-27-sectrust-exceptions.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DC71D8085FC00865A7C /* si-27-sectrust-exceptions.c */; };
DC52EE4F1D80D71900B0A59C /* si-28-sectrustsettings.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DC81D8085FC00865A7C /* si-28-sectrustsettings.m */; };
DC52EE531D80D73800B0A59C /* si-44-seckey-gen.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DD31D8085FC00865A7C /* si-44-seckey-gen.m */; };
DC52EE541D80D73800B0A59C /* si-44-seckey-rsa.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DD41D8085FC00865A7C /* si-44-seckey-rsa.m */; };
DC797E1A1DD3F9A400CC9E42 /* CKKSSQLDatabaseObject.m in Sources */ = {isa = PBXBuildFile; fileRef = DC797E131DD3F88300CC9E42 /* CKKSSQLDatabaseObject.m */; };
DC7A17ED1E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC7A17EB1E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.h */; };
DC7A17EF1E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DC7A17EC1E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.m */; };
+ DC7F6A7D233D7FAC00DF5769 /* OctagonTests+ForwardCompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7F6A7C233D7FAC00DF5769 /* OctagonTests+ForwardCompatibility.swift */; };
DC7F79B622EA4ED4001FB69A /* OctagonTests+CKKS.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7F79B522EA4ED4001FB69A /* OctagonTests+CKKS.swift */; };
DC7F79BA22EA5C73001FB69A /* OTLocalCKKSResetOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC7F79B822EA5C72001FB69A /* OTLocalCKKSResetOperation.h */; };
DC7F79BB22EA5C73001FB69A /* OTLocalCKKSResetOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DC7F79B922EA5C72001FB69A /* OTLocalCKKSResetOperation.m */; };
DC8834931D8A21AB00CE0ACA /* oidsattr.c in Sources */ = {isa = PBXBuildFile; fileRef = DC88344B1D8A21AA00CE0ACA /* oidsattr.c */; };
DC8834961D8A21AB00CE0ACA /* oidsocsp.c in Sources */ = {isa = PBXBuildFile; fileRef = DC88344E1D8A21AA00CE0ACA /* oidsocsp.c */; };
DC88466B22373A5E00738068 /* TPPBDictionaryMatchingRule.proto in Sources */ = {isa = PBXBuildFile; fileRef = DC88466922373A4000738068 /* TPPBDictionaryMatchingRule.proto */; };
+ DC89608D2395C75600D339D9 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC89608C2395C75500D339D9 /* CoreServices.framework */; };
+ DC89608E2395C76300D339D9 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC89608C2395C75500D339D9 /* CoreServices.framework */; };
DC8D238D2064649400E163C8 /* CKKSAPSHandlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DC8D238C2064649400E163C8 /* CKKSAPSHandlingTests.m */; };
DC8DF6DC212F8A7C007B3FE8 /* OTSOSAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = DCB9475C2127562100ED9272 /* OTSOSAdapter.m */; };
DC8DF6DF212F8A7D007B3FE8 /* OTSOSAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = DCB9475C2127562100ED9272 /* OTSOSAdapter.m */; };
DCA9D84821FFEE9800B27421 /* EscrowRequestXPCProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = DCA9D84521FFE7CF00B27421 /* EscrowRequestXPCProtocol.m */; };
DCA9D84D21FFF04700B27421 /* EscrowRequestServer.h in Headers */ = {isa = PBXBuildFile; fileRef = DCA9D84B21FFF04600B27421 /* EscrowRequestServer.h */; };
DCA9D84E21FFF04700B27421 /* EscrowRequestServer.m in Sources */ = {isa = PBXBuildFile; fileRef = DCA9D84C21FFF04700B27421 /* EscrowRequestServer.m */; };
+ DCAA209A23AAF8F600DCB594 /* Container_RecoveryKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAA209823AAF8BD00DCB594 /* Container_RecoveryKey.swift */; };
+ DCAA209B23AAF8FD00DCB594 /* Container_RecoveryKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAA209823AAF8BD00DCB594 /* Container_RecoveryKey.swift */; };
+ DCAA209C23AAF93700DCB594 /* Container_RecoveryKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAA209823AAF8BD00DCB594 /* Container_RecoveryKey.swift */; };
DCAB14271E40039600C81511 /* libASN1.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC8834081D8A218F00CE0ACA /* libASN1.a */; };
DCAB17CE21FFF75B00E1DFCF /* MockSynchronousEscrowServer.m in Sources */ = {isa = PBXBuildFile; fileRef = DCAB17CC21FFF6C400E1DFCF /* MockSynchronousEscrowServer.m */; };
DCAB17D12200D26900E1DFCF /* SecEscrowPendingRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = DCAB17CF2200D26700E1DFCF /* SecEscrowPendingRecord.h */; };
DCC093801D80B0B700F984E4 /* SecCFAllocator.h in Headers */ = {isa = PBXBuildFile; fileRef = D47F514B1C3B812500A7CEFE /* SecCFAllocator.h */; };
DCC19518214C53FD00C9E0B6 /* AuthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4A7DD7320A26CF900F51F3F /* AuthKit.framework */; };
DCC1951C214C668A00C9E0B6 /* AppleAccount.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C84DA541720698900AEE225 /* AppleAccount.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
- DCC19F711EB9151B00B7D70F /* KeychainCKKS.plist in Copy BATS Test Discovery Plist */ = {isa = PBXBuildFile; fileRef = 6CB5F4781E402E5700DBF3F0 /* KeychainCKKS.plist */; };
DCC51C99209B7C1500A40387 /* print_cert.c in Sources */ = {isa = PBXBuildFile; fileRef = DC52EA951D80CC2A00B0A59C /* print_cert.c */; };
DCC54181225C05180095D926 /* OTUploadNewCKKSTLKsOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DCC5417F225C05170095D926 /* OTUploadNewCKKSTLKsOperation.h */; };
DCC54182225C05180095D926 /* OTUploadNewCKKSTLKsOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC54180225C05180095D926 /* OTUploadNewCKKSTLKsOperation.m */; };
DCE278DF1ED789EF0083B485 /* CKKSCurrentItemPointer.m in Sources */ = {isa = PBXBuildFile; fileRef = DCE278DC1ED789EF0083B485 /* CKKSCurrentItemPointer.m */; };
DCE278E81ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DCE278E61ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.h */; };
DCE278EA1ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DCE278E71ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.m */; };
+ DCE405C523A04A7F00C4343B /* OctagonTests+CKKSConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE405C423A04A7F00C4343B /* OctagonTests+CKKSConfiguration.swift */; };
DCE4E6921D7A37FA00AFB96E /* security_tool_commands.c in Sources */ = {isa = PBXBuildFile; fileRef = E7104A0B169E171900DB0045 /* security_tool_commands.c */; };
DCE4E6931D7A37FA00AFB96E /* NSFileHandle+Formatting.m in Sources */ = {isa = PBXBuildFile; fileRef = E78A9AD91D34959200006B5B /* NSFileHandle+Formatting.m */; };
DCE4E6961D7A37FA00AFB96E /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E43C48C1B00D07000E5ECB2 /* CoreFoundation.framework */; };
EB5E3BCC2003C67A00F1631B /* SecSignpost.h in Headers */ = {isa = PBXBuildFile; fileRef = EB5E3BC62003C66300F1631B /* SecSignpost.h */; settings = {ATTRIBUTES = (Private, ); }; };
EB5E3BCD2003C67B00F1631B /* SecSignpost.h in Headers */ = {isa = PBXBuildFile; fileRef = EB5E3BC62003C66300F1631B /* SecSignpost.h */; settings = {ATTRIBUTES = (Private, ); }; };
EB627A73233E339200F32437 /* MockAKSOptionalParameters.proto in Sources */ = {isa = PBXBuildFile; fileRef = EB627A6F233E323600F32437 /* MockAKSOptionalParameters.proto */; };
- EB627A79233E375A00F32437 /* MockAKSOptionalParameters.proto in Sources */ = {isa = PBXBuildFile; fileRef = EB627A6F233E323600F32437 /* MockAKSOptionalParameters.proto */; };
EB627A7E233E3C1300F32437 /* MockAKSOptionalParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = EB627A77233E342B00F32437 /* MockAKSOptionalParameters.m */; };
EB627A7F233E3C1600F32437 /* MockAKSRefKey.m in Sources */ = {isa = PBXBuildFile; fileRef = EB627A75233E342800F32437 /* MockAKSRefKey.m */; };
EB6667C7204CD69F000B404F /* testPlistDER.m in Sources */ = {isa = PBXBuildFile; fileRef = EB6667BE204CD65E000B404F /* testPlistDER.m */; };
EBD531772198AF19003A57E6 /* Accounts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CF4C19C171E0EA600877419 /* Accounts.framework */; };
EBDAA7E920EC4838003EA6E5 /* SecurityLocalKeychain.plist in Install BATS plist */ = {isa = PBXBuildFile; fileRef = EBDAA7E320EC46CF003EA6E5 /* SecurityLocalKeychain.plist */; };
EBDAF15D21C75FF200EAE89F /* NSXPCConnectionMock.h in Headers */ = {isa = PBXBuildFile; fileRef = EBDAF15B21C75FF200EAE89F /* NSXPCConnectionMock.h */; };
- EBDCC001233DD3E000806566 /* MockAKSRefKey.proto in Sources */ = {isa = PBXBuildFile; fileRef = EBDCBFFE233DD31700806566 /* MockAKSRefKey.proto */; };
EBDCC002233DD45700806566 /* MockAKSRefKey.proto in Sources */ = {isa = PBXBuildFile; fileRef = EBDCBFFE233DD31700806566 /* MockAKSRefKey.proto */; };
EBDE5E0E22BA3DE900A229C8 /* CKKSMockOctagonAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = EBDE5DFA22BA3D5D00A229C8 /* CKKSMockOctagonAdapter.m */; };
EBDE5E0F22BA3DEA00A229C8 /* CKKSMockOctagonAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = EBDE5DFA22BA3D5D00A229C8 /* CKKSMockOctagonAdapter.m */; };
remoteGlobalIDString = DC1789031D77980500B50D50;
remoteInfo = Security_osx;
};
+ 0CA378E823876E0900090B7E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C35DB69094F906D002917C4 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 0CA378E123876DD100090B7E;
+ remoteInfo = reset_account;
+ };
+ 0CA378EA23876E1000090B7E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 4C35DB69094F906D002917C4 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 0CA378E123876DD100090B7E;
+ remoteInfo = reset_account;
+ };
0CC593F72299EDFC006C34B5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 4C35DB69094F906D002917C4 /* Project object */;
name = "Embed OCMock";
runOnlyForDeploymentPostprocessing = 0;
};
+ 0CA378E623876DEC00090B7E /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 8;
+ dstPath = /usr/local/bin;
+ dstSubfolderSpec = 0;
+ files = (
+ 0CA378E723876DFC00090B7E /* reset_ick_account in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
0CF4064A2072E3E3003D6A7F /* Embed OCMock */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
);
runOnlyForDeploymentPostprocessing = 1;
};
- DC7162D41EB4154D000D2BB5 /* Copy BATS Test Discovery Plist */ = {
- isa = PBXCopyFilesBuildPhase;
- buildActionMask = 8;
- dstPath = /AppleInternal/CoreOS/BATS/unit_tests;
- dstSubfolderSpec = 0;
- files = (
- DCC19F711EB9151B00B7D70F /* KeychainCKKS.plist in Copy BATS Test Discovery Plist */,
- );
- name = "Copy BATS Test Discovery Plist";
- runOnlyForDeploymentPostprocessing = 1;
- };
DC7FC44F21EE9175003C39B8 /* Install Security FeatureFlags plist */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
0C664AB2175926B20092D3D9 /* secdtests-entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "secdtests-entitlements.plist"; sourceTree = "<group>"; };
0C6C2B682258211800C53C96 /* AppleAccount.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppleAccount.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.0.Internal.sdk/System/Library/PrivateFrameworks/AppleAccount.framework; sourceTree = DEVELOPER_DIR; };
0C6C2B6C2258295D00C53C96 /* UIKitCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKitCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.0.Internal.sdk/System/Library/PrivateFrameworks/UIKitCore.framework; sourceTree = DEVELOPER_DIR; };
+ 0C7382F023863AD5004F98CB /* reset_ick_account */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = reset_ick_account; sourceTree = "<group>"; };
0C75AC642141F18D0073A2F9 /* KeychainCircle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = KeychainCircle.framework; path = System/Library/PrivateFrameworks/KeychainCircle.framework; sourceTree = SDKROOT; };
0C78F1C916A5E13400654E08 /* sectask_regressions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sectask_regressions.h; sourceTree = "<group>"; };
0C78F1CA16A5E1BF00654E08 /* sectask-10-sectask.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sectask-10-sectask.c"; sourceTree = "<group>"; };
0CE760531E13155100B4381E /* SOSAccountTrustClassic+Circle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SOSAccountTrustClassic+Circle.h"; path = "SecureObjectSync/SOSAccountTrustClassic+Circle.h"; sourceTree = "<group>"; };
0CE760551E1316E900B4381E /* SOSAccountTrustClassic+Retirement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SOSAccountTrustClassic+Retirement.h"; path = "SecureObjectSync/SOSAccountTrustClassic+Retirement.h"; sourceTree = "<group>"; };
0CE98B5B1FA9360700CF1D54 /* libprequelite.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libprequelite.tbd; path = usr/lib/libprequelite.tbd; sourceTree = SDKROOT; };
- 0CE9C98921B88919006BDD80 /* OTSOSMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTSOSMessage.h; sourceTree = "<group>"; };
- 0CE9C98A21B8891A006BDD80 /* OTSOSMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTSOSMessage.m; sourceTree = "<group>"; };
0CF0E2E31F8EE3B000BD18E4 /* SFSignInAnalytics.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SFSignInAnalytics.m; sourceTree = "<group>"; };
0CF0E2E71F8EE40700BD18E4 /* SFSignInAnalytics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SFSignInAnalytics.h; sourceTree = "<group>"; };
0CF405F42072E2BF003D6A7F /* SFSignInAnalyticsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SFSignInAnalyticsTests.m; sourceTree = "<group>"; };
48C2F9321E4BCFC30093D70C /* accountCirclesViewsPrint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = accountCirclesViewsPrint.m; sourceTree = "<group>"; };
48C2F9331E4BCFC30093D70C /* accountCirclesViewsPrint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = accountCirclesViewsPrint.h; sourceTree = "<group>"; };
48CC58971DA5FF0B00EBD9DB /* secd-66-account-recovery.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-66-account-recovery.m"; sourceTree = "<group>"; };
- 48E6171A1DBEC40D0098EAAD /* SOSBackupInformation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SOSBackupInformation.m; sourceTree = "<group>"; };
- 48E6171B1DBEC40D0098EAAD /* SOSBackupInformation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SOSBackupInformation.h; sourceTree = "<group>"; };
48FE668F20E6E69B00FAEF17 /* SOSAuthKitHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SOSAuthKitHelpers.m; sourceTree = "<group>"; };
48FE669520E6E69C00FAEF17 /* SOSAuthKitHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SOSAuthKitHelpers.h; sourceTree = "<group>"; };
4AF7FFF315AFB73800B9D400 /* SecOTR.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SecOTR.h; sourceTree = "<group>"; };
52AA92881E662A4A004301A6 /* SecBackupKeybagEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SecBackupKeybagEntry.m; sourceTree = "<group>"; };
52D82BD316A5EADA0078DFE5 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
52D82BDE16A621F70078DFE5 /* CloudKeychainProxy.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CloudKeychainProxy.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
+ 52DA3C6F23C7E63500FEEDFF /* KCTLKRequestTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = KCTLKRequestTest.m; path = Tests/KCTLKRequestTest.m; sourceTree = "<group>"; };
5346480117331E1200FE9172 /* KeychainSyncAccountNotification.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KeychainSyncAccountNotification.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
5346480517331E1200FE9172 /* KeychainSyncAccountNotification-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "KeychainSyncAccountNotification-Info.plist"; sourceTree = "<group>"; };
5346480717331E1200FE9172 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
6CA837612210C5E7002770F1 /* kc-45-change-password.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = "kc-45-change-password.c"; path = "regressions/kc-45-change-password.c"; sourceTree = "<group>"; };
6CAA8D201F842FB3007B6E03 /* securityuploadd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = securityuploadd; sourceTree = BUILT_PRODUCTS_DIR; };
6CB5F4751E4025AB00DBF3F0 /* CKKSCloudKitTestsInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = CKKSCloudKitTestsInfo.plist; sourceTree = "<group>"; };
- 6CB5F4781E402E5700DBF3F0 /* KeychainCKKS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = KeychainCKKS.plist; path = testrunner/KeychainCKKS.plist; sourceTree = "<group>"; };
6CB5F4791E402E5700DBF3F0 /* KeychainEntitledTestRunner-Entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "KeychainEntitledTestRunner-Entitlements.plist"; sourceTree = "<group>"; };
6CB5F47A1E402E5700DBF3F0 /* KeychainEntitledTestRunner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KeychainEntitledTestRunner.m; sourceTree = "<group>"; };
6CB6CC022198D4BC0080AD6F /* SecDbBackupRecoverySet.proto */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.protobuf; path = SecDbBackupRecoverySet.proto; sourceTree = "<group>"; };
D4707A282113ECA0005BCFDA /* SecCmsMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SecCmsMessage.h; path = CMS/SecCmsMessage.h; sourceTree = "<group>"; };
D4707A2B2114B31A005BCFDA /* SecCmsContentInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SecCmsContentInfo.h; path = CMS/SecCmsContentInfo.h; sourceTree = "<group>"; };
D4707A2E2114C30A005BCFDA /* SecCmsDigestContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SecCmsDigestContext.h; path = CMS/SecCmsDigestContext.h; sourceTree = "<group>"; };
+ D477CB76237E453C00C02355 /* si-88-sectrust-valid-data */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "si-88-sectrust-valid-data"; path = "SecurityTests/si-88-sectrust-valid-data"; sourceTree = "<group>"; };
+ D477CB7A237E4BD700C02355 /* ExceptionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = ExceptionTests.m; path = tests/TrustTests/EvaluationTests/ExceptionTests.m; sourceTree = "<group>"; };
+ D477CB7D237F321400C02355 /* ExceptionTests_data.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ExceptionTests_data.h; path = tests/TrustTests/EvaluationTests/ExceptionTests_data.h; sourceTree = "<group>"; };
D479F6E01F980F8F00388D28 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = en.lproj/Trust.strings; sourceTree = "<group>"; };
D47AB2CA2356AD72005A3801 /* Network.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Network.framework; path = System/Library/Frameworks/Network.framework; sourceTree = SDKROOT; };
D47C56AB1DCA831C00E18518 /* lib_ios_x64.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = lib_ios_x64.xcconfig; path = xcconfig/lib_ios_x64.xcconfig; sourceTree = "<group>"; };
DC0BCDB41D8C6A5B00070CB0 /* not_on_this_platorm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = not_on_this_platorm.c; sourceTree = "<group>"; };
DC0BD4EF21BB05F2006B9154 /* CKKSKeychainBackedKey.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKKSKeychainBackedKey.h; sourceTree = "<group>"; };
DC0BD4F021BB05F2006B9154 /* CKKSKeychainBackedKey.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSKeychainBackedKey.m; sourceTree = "<group>"; };
+ DC0D15FF2363A1D6007F0951 /* OTSetCDPBitOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OTSetCDPBitOperation.h; sourceTree = "<group>"; };
+ DC0D16002363A1D6007F0951 /* OTSetCDPBitOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OTSetCDPBitOperation.m; sourceTree = "<group>"; };
+ DC0D16042363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OTDetermineCDPBitStatusOperation.h; sourceTree = "<group>"; };
+ DC0D16052363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OTDetermineCDPBitStatusOperation.m; sourceTree = "<group>"; };
DC0EF8EF208697C600AB9E95 /* tpctl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = tpctl; sourceTree = BUILT_PRODUCTS_DIR; };
DC0EF8F1208697C600AB9E95 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
DC0FA6AE2291F63F00FE01C4 /* OctagonPendingFlag.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OctagonPendingFlag.h; sourceTree = "<group>"; };
DC3A4B601D91EAC500E46D4A /* com.apple.CodeSigningHelper.sb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = com.apple.CodeSigningHelper.sb; sourceTree = "<group>"; };
DC3A4B621D91EAC500E46D4A /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; usesTabs = 1; };
DC3A81D41D99D567000C7419 /* libcoretls_cfhelpers.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcoretls_cfhelpers.dylib; path = usr/lib/libcoretls_cfhelpers.dylib; sourceTree = SDKROOT; };
+ DC3A9B2523A9D6120073ED06 /* Container_BottledPeers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container_BottledPeers.swift; sourceTree = "<group>"; };
DC3AA27C2097DF94007CA68A /* security_tool_commands_table.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = security_tool_commands_table.h; sourceTree = "<group>"; };
DC3AF52A2229E6C0006577E8 /* CKKSListenerCollection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKKSListenerCollection.h; sourceTree = "<group>"; };
DC3AF52B2229E6C0006577E8 /* CKKSListenerCollection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSListenerCollection.m; sourceTree = "<group>"; };
DC4269031E82EDAC002B7110 /* SecItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SecItem.m; sourceTree = "<group>"; };
DC4269061E82FBDF002B7110 /* server_security_helpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = server_security_helpers.m; sourceTree = "<group>"; };
DC4269071E82FBDF002B7110 /* server_security_helpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = server_security_helpers.h; sourceTree = "<group>"; };
+ DC4415B323610BF40087981C /* OctagonTests+Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OctagonTests+Account.swift"; sourceTree = "<group>"; };
DC45D43B22EB619D00CEB6B7 /* OctagonStateMachineObservers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OctagonStateMachineObservers.h; sourceTree = "<group>"; };
DC45D43C22EB619D00CEB6B7 /* OctagonStateMachineObservers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OctagonStateMachineObservers.m; sourceTree = "<group>"; };
DC4A76A2221267D4006F2D8F /* EscrowRequestServerHelpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EscrowRequestServerHelpers.m; sourceTree = "<group>"; };
DC4A76A4221267FB006F2D8F /* EscrowRequestServerHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EscrowRequestServerHelpers.h; sourceTree = "<group>"; };
DC4A76A92212698B006F2D8F /* CloudServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudServices.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.0.Internal.sdk/System/Library/PrivateFrameworks/CloudServices.framework; sourceTree = DEVELOPER_DIR; };
+ DC4CD9822372294D00EF55FC /* OctagonTests+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OctagonTests+Helpers.swift"; sourceTree = "<group>"; };
DC4D49D81F857728007AF2B8 /* CKKSSerializedKey.proto */ = {isa = PBXFileReference; lastKnownFileType = text; path = CKKSSerializedKey.proto; sourceTree = "<group>"; };
DC4DB14E1E24692100CD6769 /* CKKSKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKKSKey.h; sourceTree = "<group>"; };
DC4DB14F1E24692100CD6769 /* CKKSKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKKSKey.m; sourceTree = "<group>"; };
DC7EB920211E17E500516452 /* OTAccountMetadataClassC+KeychainSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OTAccountMetadataClassC+KeychainSupport.h"; sourceTree = "<group>"; };
DC7EB921211E17E500516452 /* OTAccountMetadataClassC+KeychainSupport.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "OTAccountMetadataClassC+KeychainSupport.m"; sourceTree = "<group>"; };
DC7EB928211E20DF00516452 /* OctagonDataPersistenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OctagonDataPersistenceTests.swift; sourceTree = "<group>"; };
+ DC7F6A7C233D7FAC00DF5769 /* OctagonTests+ForwardCompatibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OctagonTests+ForwardCompatibility.swift"; sourceTree = "<group>"; };
DC7F79B522EA4ED4001FB69A /* OctagonTests+CKKS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OctagonTests+CKKS.swift"; sourceTree = "<group>"; };
DC7F79B822EA5C72001FB69A /* OTLocalCKKSResetOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OTLocalCKKSResetOperation.h; sourceTree = "<group>"; };
DC7F79B922EA5C72001FB69A /* OTLocalCKKSResetOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OTLocalCKKSResetOperation.m; sourceTree = "<group>"; };
DC88466C2237407500738068 /* TPDictionaryMatchingRuleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TPDictionaryMatchingRuleTests.m; sourceTree = "<group>"; };
DC88467F2237431400738068 /* TPDictionaryMatchingRules.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TPDictionaryMatchingRules.h; sourceTree = "<group>"; };
DC8846802237431400738068 /* TPDictionaryMatchingRules.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TPDictionaryMatchingRules.m; sourceTree = "<group>"; };
+ DC89608C2395C75500D339D9 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
DC8D238C2064649400E163C8 /* CKKSAPSHandlingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSAPSHandlingTests.m; sourceTree = "<group>"; };
DC8E04901D7F6780006D80EB /* lib_ios.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = lib_ios.xcconfig; path = xcconfig/lib_ios.xcconfig; sourceTree = "<group>"; };
DC9061B822B02BA30071474D /* TPTypes.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TPTypes.m; sourceTree = "<group>"; };
DCA9D84521FFE7CF00B27421 /* EscrowRequestXPCProtocol.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EscrowRequestXPCProtocol.m; sourceTree = "<group>"; };
DCA9D84B21FFF04600B27421 /* EscrowRequestServer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EscrowRequestServer.h; sourceTree = "<group>"; };
DCA9D84C21FFF04700B27421 /* EscrowRequestServer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EscrowRequestServer.m; sourceTree = "<group>"; };
+ DCAA209823AAF8BD00DCB594 /* Container_RecoveryKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container_RecoveryKey.swift; sourceTree = "<group>"; };
DCAB17CB21FFF6C400E1DFCF /* MockSynchronousEscrowServer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockSynchronousEscrowServer.h; sourceTree = "<group>"; };
DCAB17CC21FFF6C400E1DFCF /* MockSynchronousEscrowServer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockSynchronousEscrowServer.m; sourceTree = "<group>"; };
DCAB17CF2200D26700E1DFCF /* SecEscrowPendingRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecEscrowPendingRecord.h; sourceTree = "<group>"; };
DCC78C701D8085D800865A7C /* secd-83-item-match-valid-on-date.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-83-item-match-valid-on-date.m"; sourceTree = "<group>"; };
DCC78C711D8085D800865A7C /* secd-83-item-match-trusted.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-83-item-match-trusted.m"; sourceTree = "<group>"; };
DCC78C721D8085D800865A7C /* secd-83-item-match.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "secd-83-item-match.h"; sourceTree = "<group>"; };
- DCC78C741D8085D800865A7C /* secd-95-escrow-persistence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-95-escrow-persistence.m"; sourceTree = "<group>"; };
DCC78C751D8085D800865A7C /* secd-100-initialsync.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-100-initialsync.m"; sourceTree = "<group>"; };
DCC78C761D8085D800865A7C /* secd-130-other-peer-views.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-130-other-peer-views.m"; sourceTree = "<group>"; };
DCC78C771D8085D800865A7C /* secd-154-engine-backoff.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-154-engine-backoff.m"; sourceTree = "<group>"; };
DCC78D021D8085F200865A7C /* sc-42-circlegencount.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sc-42-circlegencount.c"; sourceTree = "<group>"; };
DCC78D031D8085F200865A7C /* sc-45-digestvector.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sc-45-digestvector.c"; sourceTree = "<group>"; };
DCC78D041D8085F200865A7C /* sc-130-resignationticket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sc-130-resignationticket.c"; sourceTree = "<group>"; };
- DCC78D061D8085F200865A7C /* sc-150-ring.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "sc-150-ring.m"; sourceTree = "<group>"; };
DCC78D071D8085F200865A7C /* sc-150-backupkeyderivation.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sc-150-backupkeyderivation.c"; sourceTree = "<group>"; };
DCC78D081D8085F200865A7C /* sc-153-backupslicekeybag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sc-153-backupslicekeybag.c"; sourceTree = "<group>"; };
DCC78D091D8085F200865A7C /* SOSCircle_regressions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SOSCircle_regressions.h; sourceTree = "<group>"; };
DCC78D991D8085F200865A7C /* keychain_sync_test.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = keychain_sync_test.m; sourceTree = "<group>"; };
DCC78D9A1D8085F200865A7C /* keychain_log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = keychain_log.h; sourceTree = "<group>"; };
DCC78D9B1D8085F200865A7C /* keychain_log.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = keychain_log.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
- DCC78D9C1D8085F200865A7C /* syncbackup.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = syncbackup.h; sourceTree = "<group>"; };
- DCC78D9D1D8085F200865A7C /* syncbackup.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = syncbackup.m; sourceTree = "<group>"; };
DCC78D9E1D8085F200865A7C /* secViewDisplay.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = secViewDisplay.c; sourceTree = "<group>"; };
DCC78D9F1D8085F200865A7C /* secViewDisplay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = secViewDisplay.h; sourceTree = "<group>"; };
DCC78DA21D8085FC00865A7C /* Security_regressions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Security_regressions.h; path = Regressions/Security_regressions.h; sourceTree = "<group>"; };
DCC78DC31D8085FC00865A7C /* si-24-sectrust-itms.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "si-24-sectrust-itms.c"; sourceTree = "<group>"; };
DCC78DC51D8085FC00865A7C /* si-24-sectrust-passbook.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "si-24-sectrust-passbook.c"; sourceTree = "<group>"; };
DCC78DC61D8085FC00865A7C /* si-26-sectrust-copyproperties.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "si-26-sectrust-copyproperties.c"; sourceTree = "<group>"; };
- DCC78DC71D8085FC00865A7C /* si-27-sectrust-exceptions.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "si-27-sectrust-exceptions.c"; sourceTree = "<group>"; };
DCC78DC81D8085FC00865A7C /* si-28-sectrustsettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "si-28-sectrustsettings.m"; sourceTree = "<group>"; };
DCC78DC91D8085FC00865A7C /* si-28-sectrustsettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "si-28-sectrustsettings.h"; sourceTree = "<group>"; };
DCC78DCA1D8085FC00865A7C /* si-30-keychain-upgrade.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "si-30-keychain-upgrade.c"; sourceTree = "<group>"; };
DCE278DC1ED789EF0083B485 /* CKKSCurrentItemPointer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSCurrentItemPointer.m; sourceTree = "<group>"; };
DCE278E61ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKKSUpdateCurrentItemPointerOperation.h; sourceTree = "<group>"; };
DCE278E71ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSUpdateCurrentItemPointerOperation.m; sourceTree = "<group>"; };
+ DCE405C423A04A7F00C4343B /* OctagonTests+CKKSConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OctagonTests+CKKSConfiguration.swift"; sourceTree = "<group>"; };
DCE4E6A41D7A37FA00AFB96E /* security2 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = security2; sourceTree = BUILT_PRODUCTS_DIR; };
DCE4E6A71D7A38C000AFB96E /* security2.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = security2.1; sourceTree = "<group>"; };
DCE4E6D41D7A41E400AFB96E /* bc-10-knife-on-bread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "bc-10-knife-on-bread.m"; path = "OSX/Breadcrumb/bc-10-knife-on-bread.m"; sourceTree = "<group>"; };
buildActionMask = 2147483647;
files = (
0C84D83D1FCF449700B822E3 /* Security.framework in Frameworks */,
+ 0CE902352395D0A3005E3F8C /* AuthKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
0C9FB40720D872A600864612 /* CoreCDP.framework in Frameworks */,
43DB54551BB1F8920083C3F1 /* ProtectedCloudStorage.framework in Frameworks */,
4C8A38C917B93DF10001B4C0 /* CloudServices.framework in Frameworks */,
- 4C7913251799A5CC00A9633E /* MobileCoreServices.framework in Frameworks */,
4381603B1B4DCEFF00C54D58 /* AggregateDictionary.framework in Frameworks */,
4C3DD6BD179760280093F9D8 /* libMobileGestalt.dylib in Frameworks */,
533B5D4F177CD63100995334 /* SpringBoardServices.framework in Frameworks */,
7200D76F177B9999009BB396 /* ManagedConfiguration.framework in Frameworks */,
433E519E1B66D5F600482618 /* AppSupport.framework in Frameworks */,
+ DC89608D2395C75600D339D9 /* CoreServices.framework in Frameworks */,
4C84DA551720698900AEE225 /* AppleAccount.framework in Frameworks */,
4CF4C19D171E0EA600877419 /* Accounts.framework in Frameworks */,
438168C41B4ED43800C54D58 /* CoreFoundation.framework in Frameworks */,
DC00AB9A1D821D8800513D74 /* libSWCAgent.a in Frameworks */,
DCD22D981D8CCF78001C9B81 /* libutilities.a in Frameworks */,
BE442BB318B7FDB800F24DAE /* Security.framework in Frameworks */,
+ DC89608E2395C76300D339D9 /* CoreServices.framework in Frameworks */,
BE442BB418B7FDB800F24DAE /* SystemConfiguration.framework in Frameworks */,
BE442BB618B7FDB800F24DAE /* CFNetwork.framework in Frameworks */,
BE25C41618B83491003320E0 /* Foundation.framework in Frameworks */,
BE442BB718B7FDB800F24DAE /* IOKit.framework in Frameworks */,
438168C61B4ED43F00C54D58 /* CoreFoundation.framework in Frameworks */,
- D4B858671D370D9A003B2D95 /* MobileCoreServices.framework in Frameworks */,
BE442BB818B7FDB800F24DAE /* libsqlite3.dylib in Frameworks */,
BE442BB918B7FDB800F24DAE /* libbsm.dylib in Frameworks */,
);
files = (
DC730E2922401F5E0051DD48 /* ProtocolBuffer.framework in Frameworks */,
DC730E2522401E310051DD48 /* TrustedPeers.framework in Frameworks */,
- 1B916CD0223FFF25006657FD /* ProtocolBuffer.framework in Frameworks */,
BE72782C209D2C1400F0DA77 /* SecurityFoundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
isa = PBXGroup;
children = (
0C9FB40120D8729A00864612 /* CoreCDP.framework */,
- DCD067E71D8CDF7E007602F1 /* SecCodeHostLib.h */,
- DCD067E81D8CDF7E007602F1 /* SecCodeHostLib.c */,
);
name = "Recovered References";
sourceTree = "<group>";
};
+ 0C7382E52386379E004F98CB /* ResetCloudKeychainAccount */ = {
+ isa = PBXGroup;
+ children = (
+ 0C7382F023863AD5004F98CB /* reset_ick_account */,
+ );
+ path = ResetCloudKeychainAccount;
+ sourceTree = "<group>";
+ };
0C78F1C816A5E13400654E08 /* regressions */ = {
isa = PBXGroup;
children = (
0C9AE28E214054F6003BFDB5 /* OTApplicantToSponsorRound2M1.m */,
0C9AE2A1214055CE003BFDB5 /* OTPairingMessage.h */,
0C9AE2A2214055CF003BFDB5 /* OTPairingMessage.m */,
- 0CE9C98921B88919006BDD80 /* OTSOSMessage.h */,
- 0CE9C98A21B8891A006BDD80 /* OTSOSMessage.m */,
0C9AE289214054F4003BFDB5 /* OTSponsorToApplicantRound1M2.h */,
0C9AE28D214054F6003BFDB5 /* OTSponsorToApplicantRound1M2.m */,
0C9AE28A214054F5003BFDB5 /* OTSponsorToApplicantRound2M2.h */,
DC754C712228B57B00A39C8E /* TrustedPeersHelperProtocol.m */,
BE55C77B2044D0C90045863D /* Client.swift */,
BE9F8D0F206C099800B53D16 /* Container.swift */,
+ DC3A9B2523A9D6120073ED06 /* Container_BottledPeers.swift */,
+ DCAA209823AAF8BD00DCB594 /* Container_RecoveryKey.swift */,
DCAD8F8422C43EAD007C3872 /* Container_MachineIDs.swift */,
BE9F8D18206C4AD300B53D16 /* ContainerMap.swift */,
BE9F8D11206C121400B53D16 /* Decrypter.swift */,
D4AC5767214E195300A32C01 /* ECTests_data.h */,
D458C4AE214E198E0043D982 /* EvaluationBasicTests_data.h */,
D458C4AF214E198E0043D982 /* EvaluationBasicTests.m */,
+ D477CB7A237E4BD700C02355 /* ExceptionTests.m */,
+ D477CB7D237F321400C02355 /* ExceptionTests_data.h */,
D458C4B5214E19AE0043D982 /* iAPTests.m */,
D458C4AD214E198E0043D982 /* iAPTests_data.h */,
D4AC5768214E195400A32C01 /* KeySizeTests.m */,
D458C51B214E2CFF0043D982 /* si-20-sectrust-policies-data */,
D4A0F8C1211E6A2F00443CA1 /* si-82-sectrust-ct-data */,
D4056A1E22712D750026E24E /* si-87-sectrust-name-constraints */,
+ D477CB76237E453C00C02355 /* si-88-sectrust-valid-data */,
D4056A1D22712D740026E24E /* ssl-policy-certs */,
);
name = TestData;
BEF88C451EAFFFED00357577 /* TrustedPeers */,
BEAA002C202A832500E51F45 /* TrustedPeersHelper */,
BED987D42099145300607A5F /* TrustedPeersHelperUnitTests */,
+ 0C7382E52386379E004F98CB /* ResetCloudKeychainAccount */,
);
path = keychain;
sourceTree = "<group>";
DCC0A4C52152C4AB000AF654 /* Pairing */,
DC27C3C820EADD8200F7839C /* OctagonTests-BridgingHeader.h */,
DC85687C2284E7850088D3EF /* OctagonTestMocks.swift */,
+ DC4CD9822372294D00EF55FC /* OctagonTests+Helpers.swift */,
DC27C3C020EAD9C300F7839C /* OctagonTests.swift */,
+ DC4415B323610BF40087981C /* OctagonTests+Account.swift */,
DC7F79B522EA4ED4001FB69A /* OctagonTests+CKKS.swift */,
+ DCE405C423A04A7F00C4343B /* OctagonTests+CKKSConfiguration.swift */,
+ DC5BEACC2217509A001681F0 /* OctagonTests+CloudKitAccount.swift */,
DC5F2BBD2310B941001ADA5D /* OctagonTests+CoreFollowUp.swift */,
DC2819B822F8F6FE007829F5 /* OctagonTests+DeviceList.swift */,
+ DC7F6A7C233D7FAC00DF5769 /* OctagonTests+ForwardCompatibility.swift */,
0C4CDE6D22922E360050C499 /* OctagonTests+RecoveryKey.swift */,
DC07090222936BCC002711B9 /* OctagonTests+ErrorHandling.swift */,
DCDF03112284E34B008055BA /* OctagonTests+EscrowRecovery.swift */,
- DC5BEACC2217509A001681F0 /* OctagonTests+CloudKitAccount.swift */,
0C5824A322860001009E8C15 /* OctagonTests+HealthCheck.swift */,
DC72502D229600A800493D88 /* OctagonTests+Reset.swift */,
DCB947592127534C00ED9272 /* OctagonTests+SOSUpgrade.swift */,
DCA4D2121E5651950056214F /* Tests (Live CloudKit) */ = {
isa = PBXGroup;
children = (
- 6CB5F4781E402E5700DBF3F0 /* KeychainCKKS.plist */,
6CF4A0B51E45488B00ECD7B5 /* KeychainEntitledTestApp_mac */,
6CF4A0E11E4549F200ECD7B5 /* KeychainEntitledTestApp_ios */,
6CB5F4771E402D6D00DBF3F0 /* testrunner */,
0CC8A9012123AEF7005D7F6A /* OTJoinWithVoucherOperation.m */,
0C66046E2134985100BFBBB8 /* OTEstablishOperation.h */,
0C6604692134983900BFBBB8 /* OTEstablishOperation.m */,
+ DC0D15FF2363A1D6007F0951 /* OTSetCDPBitOperation.h */,
+ DC0D16002363A1D6007F0951 /* OTSetCDPBitOperation.m */,
+ DC0D16042363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.h */,
+ DC0D16052363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.m */,
DCFF82722162876400D54B02 /* OTResetOperation.h */,
DCFF82732162876400D54B02 /* OTResetOperation.m */,
0C00FC85217A972E00C8BF00 /* OTLocalCuttlefishReset.h */,
DCC78C701D8085D800865A7C /* secd-83-item-match-valid-on-date.m */,
DCC78C711D8085D800865A7C /* secd-83-item-match-trusted.m */,
DCC78C721D8085D800865A7C /* secd-83-item-match.h */,
- DCC78C741D8085D800865A7C /* secd-95-escrow-persistence.m */,
DCC78C751D8085D800865A7C /* secd-100-initialsync.m */,
DCC78C761D8085D800865A7C /* secd-130-other-peer-views.m */,
DCC78C771D8085D800865A7C /* secd-154-engine-backoff.m */,
DCC78D021D8085F200865A7C /* sc-42-circlegencount.c */,
DCC78D031D8085F200865A7C /* sc-45-digestvector.c */,
DCC78D041D8085F200865A7C /* sc-130-resignationticket.c */,
- DCC78D061D8085F200865A7C /* sc-150-ring.m */,
DCC78D071D8085F200865A7C /* sc-150-backupkeyderivation.c */,
DCC78D081D8085F200865A7C /* sc-153-backupslicekeybag.c */,
DCC78D091D8085F200865A7C /* SOSCircle_regressions.h */,
DCC78D2A1D8085F200865A7C /* SOSBackupSliceKeyBag.h */,
48776C731DA5BB4200CC09B9 /* SOSRecoveryKeyBag.m */,
48776C741DA5BB4200CC09B9 /* SOSRecoveryKeyBag.h */,
- 48E6171A1DBEC40D0098EAAD /* SOSBackupInformation.m */,
- 48E6171B1DBEC40D0098EAAD /* SOSBackupInformation.h */,
DCC78D2B1D8085F200865A7C /* SOSUserKeygen.m */,
DCC78D2C1D8085F200865A7C /* SOSUserKeygen.h */,
485B64081DC16E8300B771B9 /* SOSKeyedPubKeyIdentifier.c */,
DCC78D9B1D8085F200865A7C /* keychain_log.m */,
0C0CEC9D1DA45EA200C22FBC /* recovery_key.h */,
0C0CEC9E1DA45EA200C22FBC /* recovery_key.m */,
- DCC78D9D1D8085F200865A7C /* syncbackup.m */,
- DCC78D9C1D8085F200865A7C /* syncbackup.h */,
DCC78D9E1D8085F200865A7C /* secViewDisplay.c */,
DCC78D9F1D8085F200865A7C /* secViewDisplay.h */,
48C2F9321E4BCFC30093D70C /* accountCirclesViewsPrint.m */,
DC0B62261D90973900D43BCB /* si-25-cms-skid.h */,
DC0B62271D90973900D43BCB /* si-25-cms-skid.m */,
DCC78DC61D8085FC00865A7C /* si-26-sectrust-copyproperties.c */,
- DCC78DC71D8085FC00865A7C /* si-27-sectrust-exceptions.c */,
DCC78DC81D8085FC00865A7C /* si-28-sectrustsettings.m */,
DCC78DC91D8085FC00865A7C /* si-28-sectrustsettings.h */,
D4AA0D9922FB959600D77FA4 /* si-29-cms-chain-mode.m */,
E7D848031C6BEFAB0025BB44 /* Tests */ = {
isa = PBXGroup;
children = (
+ 52DA3C6F23C7E63500FEEDFF /* KCTLKRequestTest.m */,
E7CFF7221C8660A000E3484E /* KeychainCircle.plist */,
E7D848061C6BEFFA0025BB44 /* Info.plist */,
E7D848041C6BEFC10025BB44 /* KCSRPTests.m */,
E7FCBE401314471B000DE34E /* Frameworks */ = {
isa = PBXGroup;
children = (
+ DC89608C2395C75500D339D9 /* CoreServices.framework */,
BEC6A9142331992800080069 /* Network.framework */,
D47AB2CA2356AD72005A3801 /* Network.framework */,
0C6C2B6C2258295D00C53C96 /* UIKitCore.framework */,
DC9082C61EA027DB00D0C1C5 /* CKKSZoneChangeFetcher.h in Headers */,
DC614C5122A9BDB500E16ADA /* CKKSZoneModifier.h in Headers */,
DCA4D2151E5684220056214F /* CKKSReencryptOutgoingItemsOperation.h in Headers */,
+ DC0D16012363A1D6007F0951 /* OTSetCDPBitOperation.h in Headers */,
0C38AA96212B2D1E00C90A1D /* OTClientVoucherOperation.h in Headers */,
DC378B3C1DF0CA7200A3DAFA /* CKKSIncomingQueueEntry.h in Headers */,
DC5F65AE2225C22C0051E9FA /* CKKSProvideKeySetOperation.h in Headers */,
DC52E7E61D80BE7B00B0A59C /* SecItemDb.h in Headers */,
DCF12673218A757A000124C6 /* OTLeaveCliqueOperation.h in Headers */,
DCAD9B441F8D939C00C5E2AE /* CKKSFixups.h in Headers */,
+ DC0D16062363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.h in Headers */,
DC9C95B41F79CFD1000D19E5 /* CKKSControl.h in Headers */,
DC52E7EA1D80BE9500B0A59C /* SecItemSchema.h in Headers */,
DC7F79BA22EA5C73001FB69A /* OTLocalCKKSResetOperation.h in Headers */,
DC52E9161D80C41A00B0A59C /* SOSPeerInfoInternal.h in Headers */,
DC3C735A1D837C0000F6A832 /* SOSPeerInfoPriv.h in Headers */,
DC52E91A1D80C43500B0A59C /* SOSRing.h in Headers */,
- 48E617221DBEC6C60098EAAD /* SOSBackupInformation.h in Headers */,
CD198F971DE27B9E00F6FB83 /* SOSAccountPriv.h in Headers */,
DC52E9231D80C47100B0A59C /* SOSTransportCircleKVS.h in Headers */,
DC52E92C1D80C4AF00B0A59C /* SOSTransportKeyParameter.h in Headers */,
DC3502B11E0208BE00BC0587 /* Sources */,
DC3502B21E0208BE00BC0587 /* Frameworks */,
DC9A2C791EB40A64008FAC27 /* Embed OCMock */,
- DC7162D41EB4154D000D2BB5 /* Copy BATS Test Discovery Plist */,
DC7162D61EB4157D000D2BB5 /* ShellScript */,
);
buildRules = (
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1000;
- LastUpgradeCheck = 1000;
+ LastUpgradeCheck = 1120;
TargetAttributes = {
4381690B1B4EDCBD00C54D58 = {
CreatedOnToolsVersion = 7.0;
EB7E90F12193F90700B1FA21 /* Build C2 Metrics */,
3D58392D21890FFB000ACA44 /* SecExperimentTests */,
5A442F81233C330F00918373 /* experimentTool */,
+ 0CA378E123876DD100090B7E /* reset_account */,
);
};
/* End PBXProject section */
D458C51A214E2CC80043D982 /* si-20-sectrust-policies-data in Resources */,
D453A4BC2122236D00850A26 /* si-82-sectrust-ct-data in Resources */,
D4FD4226217D7C41002B7EE2 /* si-87-sectrust-name-constraints in Resources */,
+ D477CB79237E484300C02355 /* si-88-sectrust-valid-data in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D458C517214E2C690043D982 /* si-20-sectrust-policies-data in Resources */,
D4A0F8C2211E6A2F00443CA1 /* si-82-sectrust-ct-data in Resources */,
D4FD4227217D7C4F002B7EE2 /* si-87-sectrust-name-constraints in Resources */,
+ D477CB78237E482800C02355 /* si-88-sectrust-valid-data in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
);
runOnlyForDeploymentPostprocessing = 1;
shellPath = /bin/sh;
- shellScript = "[ \"$(whoami)\" == \"root\" ] || exit 0\nchown -f root:wheel ${DSTROOT}/AppleInternal/CoreOS/BATS/unit_tests/*.plist\n";
+ shellScript = "CKKSTESTS_DIR=${SRCROOT}/keychain/ckks/tests\npython ${CKKSTESTS_DIR}/gen_test_plist.py ${CKKSTESTS_DIR} ${DSTROOT}/AppleInternal/CoreOS/BATS/unit_tests/KeychainCKKS.plist\n\n[ \"$(whoami)\" == \"root\" ] || exit 0\nchown -f root:wheel ${DSTROOT}/AppleInternal/CoreOS/BATS/unit_tests/*.plist\n";
};
DC82FFE61D90D3F60085674B /* security_utilities DTrace */ = {
isa = PBXShellScriptBuildPhase;
0CB582D1218920090040C5F2 /* OTAuthenticatedCiphertext.m in Sources */,
0CF70BD9218BED1000EC3515 /* CuttlefishExtensionWorkaround.swift in Sources */,
DC754C742228B59000A39C8E /* TrustedPeersHelperProtocol.m in Sources */,
+ DC3A9B2823A9D8C40073ED06 /* Container_BottledPeers.swift in Sources */,
DC391FA621C04D1500772585 /* OctagonPeerKeys.swift in Sources */,
DCF6320521C074F30030CCC0 /* CuttlefishAPIHelpers.swift in Sources */,
DC391F8C21BF222B00772585 /* CKKSTLKShare.m in Sources */,
+ DCAA209C23AAF93700DCB594 /* Container_RecoveryKey.swift in Sources */,
BE9F8D19206C4AD300B53D16 /* ContainerMap.swift in Sources */,
BE9F4F8C2072D881004A52C2 /* Cuttlefish.pb.swift in Sources */,
DC391F9D21BF2F8100772585 /* CKKSConstants.m in Sources */,
BED987E120991B9B00607A5F /* Decrypter.swift in Sources */,
DC391FA721C04D6800772585 /* OctagonPeerKeys.swift in Sources */,
BE536033209BC3B30027E25A /* server_entitlement_helpers.c in Sources */,
+ DCAA209B23AAF8FD00DCB594 /* Container_RecoveryKey.swift in Sources */,
BED987E020991B1100607A5F /* TrustedPeersHelper.xcdatamodeld in Sources */,
BED987D62099145300607A5F /* TrustedPeersHelperUnitTests.swift in Sources */,
BE4C6AB820CAF4F700EAD6BE /* ContainerSync.swift in Sources */,
DCAD8F8922C43EDB007C3872 /* Container_MachineIDs.swift in Sources */,
0CF70BDA218BEFAE00EC3515 /* CuttlefishExtensionWorkaround.swift in Sources */,
DCD24DEC228C9A480052604C /* SetValueTransformer.swift in Sources */,
+ DC14C4C223AAACED007F673F /* Container_BottledPeers.swift in Sources */,
BEC0A96520B362EC00DBD772 /* Utils.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
D4FD4221217D7B2E002B7EE2 /* PathScoringTests.m in Sources */,
D40881F42175738C00180E81 /* SecPolicyLeafCallbacks.c in Sources */,
D40881F32175733F00180E81 /* SecPolicy.c in Sources */,
+ D477CB7C237E4BD700C02355 /* ExceptionTests.m in Sources */,
D40881F22175733500180E81 /* SecSignatureVerificationSupport.c in Sources */,
D40881F12175732E00180E81 /* SecCertificate.c in Sources */,
D40881F02175732600180E81 /* SecTrustStore.c in Sources */,
D458C525214E33440043D982 /* VerifyDateTests.m in Sources */,
D49A370623873BD30065719F /* TrustDaemonTestCase.m in Sources */,
D458C515214E286C0043D982 /* PolicyTests.m in Sources */,
+ D477CB7B237E4BD700C02355 /* ExceptionTests.m in Sources */,
D4EF32182156DDEB000A31A5 /* TrustSettingsInterfaceTests.m in Sources */,
D44282FF22D68564001746B3 /* TrustEvaluationTestHelpers.m in Sources */,
);
DCDCCB901DF7B8D4006E840E /* CKKSItem.m in Sources */,
DC1ED8C11DD5197E002BDCFA /* CKKSItemEncrypter.m in Sources */,
0CDD6F79226E83F6009094C2 /* OTTriggerEscrowUpdateOperation.m in Sources */,
+ DC0D16072363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.m in Sources */,
DC6D2C921DD2835A00BE372D /* CKKSOutgoingQueueEntry.m in Sources */,
DC378B3D1DF0CA7200A3DAFA /* CKKSIncomingQueueEntry.m in Sources */,
6C880FCB21C3351400D38D66 /* SecDbBackupMetadataClassKey.m in Sources */,
DCB41DFC216D5E5B00F219E0 /* OTAccountMetadataClassC+KeychainSupport.m in Sources */,
6C880FCC21C3351400D38D66 /* SecDbBackupRecoverySet.m in Sources */,
DCA4D1FF1E552DD50056214F /* CKKSCurrentKeyPointer.m in Sources */,
+ DC0D16022363A1D6007F0951 /* OTSetCDPBitOperation.m in Sources */,
EB7E91212194849900B1FA21 /* SECC2MPMetric.m in Sources */,
DA6AA1651FE88AFB004565B0 /* CKKSControlServer.m in Sources */,
DCFE1C531F1825F7007640C8 /* CKKSUpdateDeviceStateOperation.m in Sources */,
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 48E617211DBEC6BA0098EAAD /* SOSBackupInformation.m in Sources */,
DC52E8F11D80C34000B0A59C /* SOSAccount.m in Sources */,
DC52E8F31D80C34000B0A59C /* SOSAccountBackup.m in Sources */,
DCB332591F478C3C00178C30 /* SOSUserKeygen.m in Sources */,
files = (
DCD7EE841F4E46F9007D9804 /* accountCirclesViewsPrint.m in Sources */,
0C0CECA41DA45ED700C22FBC /* recovery_key.m in Sources */,
- DC52EC3B1D80CFE900B0A59C /* syncbackup.m in Sources */,
DC52EC3A1D80CFE400B0A59C /* keychain_log.m in Sources */,
DC52EC391D80CFDF00B0A59C /* secViewDisplay.c in Sources */,
DC52EC381D80CFDB00B0A59C /* secToolFileIO.c in Sources */,
DC52EC791D80D14D00B0A59C /* sc-45-digestvector.c in Sources */,
DC52EC781D80D14800B0A59C /* SOSRegressionUtilities.m in Sources */,
DC52EC771D80D14400B0A59C /* sc-130-resignationticket.c in Sources */,
- DC52EC761D80D13F00B0A59C /* sc-150-ring.m in Sources */,
DC52EC751D80D13B00B0A59C /* sc-42-circlegencount.c in Sources */,
DC52EC741D80D13500B0A59C /* SOSTestDataSource.c in Sources */,
DC52EC731D80D12E00B0A59C /* sc-20-keynames.m in Sources */,
files = (
DC52EDA01D80D4F700B0A59C /* sd-10-policytree.m in Sources */,
DC52ED9F1D80D4F200B0A59C /* SOSTransportTestTransports.m in Sources */,
- DC52ED9E1D80D4ED00B0A59C /* secd-95-escrow-persistence.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DC52EDF51D80D62E00B0A59C /* SecdTestKeychainUtilities.c in Sources */,
DC52EDF61D80D62E00B0A59C /* SOSTransportTestTransports.m in Sources */,
EB9C02481E8A15B40040D3C6 /* secd-37-pairing-initial-sync.m in Sources */,
- 0CAD1E5E1E1C5D0600537693 /* secd-95-escrow-persistence.m in Sources */,
DC52EDB51D80D5C500B0A59C /* secd-03-corrupted-items.m in Sources */,
0CAD1E5D1E1C5CF900537693 /* secd-80-views-alwayson.m in Sources */,
DC52EDB61D80D5C500B0A59C /* secd-04-corrupted-items.m in Sources */,
DC52EE4C1D80D71900B0A59C /* si-24-sectrust-passbook.c in Sources */,
DC52EE4D1D80D71900B0A59C /* si-26-sectrust-copyproperties.c in Sources */,
5E7793751E5F025A0074A2D1 /* si-44-seckey-aks.m in Sources */,
- DC52EE4E1D80D71900B0A59C /* si-27-sectrust-exceptions.c in Sources */,
DC52EE4F1D80D71900B0A59C /* si-28-sectrustsettings.m in Sources */,
DC52EE531D80D73800B0A59C /* si-44-seckey-gen.m in Sources */,
DC52EE541D80D73800B0A59C /* si-44-seckey-rsa.m in Sources */,
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ DCE405C523A04A7F00C4343B /* OctagonTests+CKKSConfiguration.swift in Sources */,
1B8D2D96226E1FA500C94238 /* SetValueTransformer.swift in Sources */,
DC5F2BBE2310B941001ADA5D /* OctagonTests+CoreFollowUp.swift in Sources */,
DCB468E520EC262C00BA7E5B /* ContainerMap.swift in Sources */,
DC27C3C920EADEE700F7839C /* MockCloudKit.m in Sources */,
0CF70BE0218CF26600EC3515 /* BottledPeer.swift in Sources */,
DC99B86B20EACA470065B73B /* spi.c in Sources */,
+ DC4CD9842372294E00EF55FC /* OctagonTests+Helpers.swift in Sources */,
+ DCAA209A23AAF8F600DCB594 /* Container_RecoveryKey.swift in Sources */,
DCDF03122284E34B008055BA /* OctagonTests+EscrowRecovery.swift in Sources */,
DCFF82712162834D00D54B02 /* OctagonTestsXPCConnections.swift in Sources */,
DC5BEACD2217509A001681F0 /* OctagonTests+CloudKitAccount.swift in Sources */,
0CBEF3432242CA0600015691 /* TestsObjcTranslation.m in Sources */,
5A04BB0222982733001848A0 /* OTFollowupTests.m in Sources */,
DCB24B45221B901700BE73FE /* CKKSMockSOSPresentAdapter.m in Sources */,
+ DC33D7BE2374FD0A00A68155 /* OTSponsorToApplicantRound2M2.m in Sources */,
0CE15E31222DF63600B7EAA4 /* RecoverKeySet.swift in Sources */,
0CADDF0721545CF100DF8B06 /* OctagonPairingTests.swift in Sources */,
DC99B86D20EACA470065B73B /* SecdWatchdog.m in Sources */,
DCB0C291222F5E130083AECB /* CuttlefishErrors.swift in Sources */,
DC99B86F20EACA470065B73B /* SecFramework.c in Sources */,
0C5258BB21BB128000B32C96 /* FakeSOSControl.m in Sources */,
+ DC3A9B2723A9D8BD0073ED06 /* Container_BottledPeers.swift in Sources */,
DC99B87020EACA470065B73B /* server_endpoint.m in Sources */,
0CD5797A21498F8200C43496 /* OctagonPairingTests+Piggybacking.swift in Sources */,
+ DC4A73C5235E69D800DB1E6E /* OTApplicantToSponsorRound2M1.m in Sources */,
DCAD8F8722C43ECA007C3872 /* Container_MachineIDs.swift in Sources */,
+ DC03592D235FCCD500F14883 /* KCInitialMessageData.m in Sources */,
DC99B87120EACA470065B73B /* server_security_helpers.m in Sources */,
0CF70BE1218CF27600EC3515 /* EscrowKeys.swift in Sources */,
DC7F79B622EA4ED4001FB69A /* OctagonTests+CKKS.swift in Sources */,
0CBA047D214C4E4D005B3A2F /* OctagonPairingTests+ProxMultiClients.swift in Sources */,
DCF6320821C09DCE0030CCC0 /* CuttlefishAPIHelpers.swift in Sources */,
0CE15E44222DF6A800B7EAA4 /* Recovery.m in Sources */,
+ DC4415B423610BF40087981C /* OctagonTests+Account.swift in Sources */,
DC725030229600C000493D88 /* OctagonTests+Reset.swift in Sources */,
0C3E316B21372FA50093C04B /* OctagonPairingTests+ProximitySetup.swift in Sources */,
DC2819B922F8F6FE007829F5 /* OctagonTests+DeviceList.swift in Sources */,
0CF70BE2218CF2AA00EC3515 /* OTAuthenticatedCiphertext.m in Sources */,
DCB41E01216D5FE500F219E0 /* OctagonDataPersistenceTests.swift in Sources */,
DC85687E2284E7860088D3EF /* OctagonTestMocks.swift in Sources */,
+ DC0DE87123750340006E2EAE /* OTPairingMessage.m in Sources */,
DC99B87320EACA470065B73B /* Decrypter.swift in Sources */,
DC99B87420EACA470065B73B /* server_entitlement_helpers.c in Sources */,
DC99B87520EACA470065B73B /* TrustedPeersHelper.xcdatamodeld in Sources */,
DC27C3C120EAD9C300F7839C /* OctagonTests.swift in Sources */,
0CE15E3F222DF6A800B7EAA4 /* OTRecovery.m in Sources */,
DCB9475A2127534C00ED9272 /* OctagonTests+SOSUpgrade.swift in Sources */,
+ DC7F6A7D233D7FAC00DF5769 /* OctagonTests+ForwardCompatibility.swift in Sources */,
DC99B87720EACA470065B73B /* ContainerSync.swift in Sources */,
+ DC33D7BD2374FD0500A68155 /* OTSponsorToApplicantRound1M2.m in Sources */,
0C61F1F62194FC79009566D4 /* OTPrivateKey+SF.m in Sources */,
DC391FA821C04DAE00772585 /* OctagonPeerKeys.swift in Sources */,
DC99B87820EACA470065B73B /* Container.swift in Sources */,
0CB72DA121E42FCF00D8BC9B /* OTSponsorToApplicantRound2M2.m in Sources */,
E7F480151C73980D00390FDB /* KCJoiningRequestSecretSession.m in Sources */,
E7F480331C73FC4C00390FDB /* KCAESGCMDuplexSession.m in Sources */,
- 0CB72D9F21E42FCF00D8BC9B /* OTSOSMessage.m in Sources */,
DC6063B221B09AB200069B82 /* KCJoiningRequestCircleSession.m in Sources */,
E794BB001C7598F900339A0F /* KCJoiningMessages.m in Sources */,
5A47FFB9228F5F2A00F781B8 /* KCInitialMessageData.m in Sources */,
E7F4809C1C74E85200390FDB /* KCDerTest.m in Sources */,
E7D848051C6BEFCD0025BB44 /* KCSRPTests.m in Sources */,
E7F4809E1C74E86D00390FDB /* KCAESGCMTest.m in Sources */,
+ 52DA3C7123C7E63600FEEDFF /* KCTLKRequestTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
EB49B2D9202DF1F7003F34A0 /* server_security_helpers.m in Sources */,
EBC73F2B2099785900AE3350 /* SFObjCType.m in Sources */,
480ADDB22155A0CE00318FC6 /* SOSAnalytics.m in Sources */,
- EB627A79233E375A00F32437 /* MockAKSOptionalParameters.proto in Sources */,
EB49B2E0202DF5D7003F34A0 /* server_entitlement_helpers.c in Sources */,
5A061196229ED6E8006AF14A /* NSDate+SFAnalytics.m in Sources */,
EBC73F2A20996AD400AE3350 /* SFSQLiteStatement.m in Sources */,
EB49B2D1202DF15F003F34A0 /* SFAnalyticsActivityTracker.m in Sources */,
EB49B2D0202DF14D003F34A0 /* SFAnalytics.m in Sources */,
EBC73F2820993FDA00AE3350 /* SFAnalyticsSampler.m in Sources */,
- EBDCC001233DD3E000806566 /* MockAKSRefKey.proto in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
target = DC1789031D77980500B50D50 /* Security_osx */;
targetProxy = 0C9AEEB920783FE000BF6237 /* PBXContainerItemProxy */;
};
+ 0CA378E923876E0900090B7E /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 0CA378E123876DD100090B7E /* reset_account */;
+ targetProxy = 0CA378E823876E0900090B7E /* PBXContainerItemProxy */;
+ };
+ 0CA378EB23876E1000090B7E /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 0CA378E123876DD100090B7E /* reset_account */;
+ targetProxy = 0CA378EA23876E1000090B7E /* PBXContainerItemProxy */;
+ };
0CC593F82299EDFC006C34B5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DC52E7731D80BC8000B0A59C /* libsecurityd_ios */;
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CODE_SIGN_IDENTITY = "";
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CODE_SIGN_IDENTITY = "";
};
name = Release;
};
+ 0CA378E423876DD100090B7E /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 0CA378E523876DD100090B7E /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
0CF4064E2072E3E3003D6A7F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
- CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_PREPROCESSOR_DEFINITIONS = (
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
- CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_PREPROCESSOR_DEFINITIONS = (
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
PRODUCT_NAME = "$(TARGET_NAME)";
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
PRODUCT_NAME = "$(TARGET_NAME)";
"$(OTHER_LDFLAGS_AKS_LIBRARY)",
"$(OTHER_LDFLAGS_UPWARD_PROTOCOLBUFFER)",
"$(OTHER_LDFLAGS_UPWARD_FOUNDATION)",
- "$(OTHER_LDFLAGS_UPWARD_SECURITY)",
"$(OTHER_LDFLAGS_UPWARD_SECURITYFOUNDATION)",
"$(OTHER_LDFLAGS_APPLEIDAUTHSUPPORT)",
);
PRODUCT_BUNDLE_IDENTIFIER = com.apple.TrustedPeers;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+ TAPI_VERIFY_MODE = Pedantic;
VERSION_INFO_PREFIX = "";
};
name = Debug;
"$(OTHER_LDFLAGS_AKS_LIBRARY)",
"$(OTHER_LDFLAGS_UPWARD_PROTOCOLBUFFER)",
"$(OTHER_LDFLAGS_UPWARD_FOUNDATION)",
- "$(OTHER_LDFLAGS_UPWARD_SECURITY)",
"$(OTHER_LDFLAGS_UPWARD_SECURITYFOUNDATION)",
"$(OTHER_LDFLAGS_APPLEIDAUTHSUPPORT)",
);
PRODUCT_BUNDLE_IDENTIFIER = com.apple.TrustedPeers;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+ TAPI_VERIFY_MODE = Pedantic;
VERSION_INFO_PREFIX = "";
};
name = Release;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
MTL_ENABLE_DEBUG_INFO = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
MTL_ENABLE_DEBUG_INFO = NO;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = NO;
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
isa = XCBuildConfiguration;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
MTL_ENABLE_DEBUG_INFO = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
isa = XCBuildConfiguration;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_NAME = "$(TARGET_NAME)";
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
HEADER_SEARCH_PATHS = (
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
HEADER_SEARCH_PATHS = (
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = NO;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = NO;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = NO;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = NO;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = NO;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
"-framework",
Security,
"$(OTHER_LDFLAGS_FOR_SECURITYD)",
+ "-framework",
+ TrustedPeers,
);
PRODUCT_BUNDLE_IDENTIFIER = com.apple.security.CKKSTests;
PRODUCT_NAME = "$(TARGET_NAME)";
"-framework",
Security,
"$(OTHER_LDFLAGS_FOR_SECURITYD)",
+ "-framework",
+ TrustedPeers,
);
PRODUCT_BUNDLE_IDENTIFIER = com.apple.security.CKKSTests;
PRODUCT_NAME = "$(TARGET_NAME)";
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = NO;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
MTL_ENABLE_DEBUG_INFO = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
MTL_ENABLE_DEBUG_INFO = NO;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
MTL_ENABLE_DEBUG_INFO = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
MTL_ENABLE_DEBUG_INFO = NO;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = NO;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = NO;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = NO;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
buildSettings = {
APPLY_RULES_IN_COPY_FILES = NO;
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_STRICT_PROTOTYPES = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
buildSettings = {
APPLY_RULES_IN_COPY_FILES = NO;
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_STRICT_PROTOTYPES = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = NO;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = NO;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = NO;
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = YES;
baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = NO;
CLANG_ENABLE_MODULES = NO;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_ENABLE_MODULES = NO;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_ENABLE_MODULES = NO;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_ENABLE_MODULES = NO;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_ENABLE_MODULES = NO;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_ENABLE_MODULES = NO;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = (
- "$(OTHER_LDFLAGS_MOBILEGESTALT)",
"$(OTHER_LDFLAGS_UPWARD_FOUNDATION)",
"$(OTHER_LDFLAGS_UPWARD_PROTOCOLBUFFER)",
);
MODULEMAP_FILE = Modules/KeychainCircle.modulemap;
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_LDFLAGS = (
- "$(OTHER_LDFLAGS_MOBILEGESTALT)",
"$(OTHER_LDFLAGS_UPWARD_FOUNDATION)",
"$(OTHER_LDFLAGS_UPWARD_PROTOCOLBUFFER)",
);
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
COPY_PHASE_STRIP = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
COPY_PHASE_STRIP = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ 0CA378E323876DD100090B7E /* Build configuration list for PBXAggregateTarget "reset_account" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 0CA378E423876DD100090B7E /* Debug */,
+ 0CA378E523876DD100090B7E /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
0CF4064D2072E3E3003D6A7F /* Build configuration list for PBXNativeTarget "SignInAnalyticsTests_ios" */ = {
isa = XCConfigurationList;
buildConfigurations = (
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1000"
+ LastUpgradeVersion = "1120"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
<Test
Identifier = "CloudKitKeychainSyncingMockXCTest">
</Test>
+ <Test
+ Identifier = "CloudKitKeychainSyncingMultiZoneTestsBase">
+ </Test>
<Test
Identifier = "CloudKitKeychainSyncingTestsBase">
</Test>
</SkippedTests>
</TestableReference>
</Testables>
- <AdditionalOptions>
- </AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
migratedStopOnEveryIssue = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
- <AdditionalOptions>
- </AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1000"
+ LastUpgradeVersion = "1120"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
</BuildableReference>
</TestableReference>
</Testables>
- <AdditionalOptions>
- </AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
- <AdditionalOptions>
- </AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1000"
+ LastUpgradeVersion = "1120"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES"
- onlyGenerateCoverageForSpecifiedTargets = "YES"
- shouldUseLaunchSchemeArgsEnv = "YES">
+ onlyGenerateCoverageForSpecifiedTargets = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "D4707A0421136E69005BCFDA"
+ BuildableName = "TrustTests.xctest"
+ BlueprintName = "TrustTests_ios"
+ ReferencedContainer = "container:Security.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
</BuildableReference>
</TestableReference>
</Testables>
- <MacroExpansion>
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "D4707A0421136E69005BCFDA"
- BuildableName = "TrustTests.xctest"
- BlueprintName = "TrustTests_ios"
- ReferencedContainer = "container:Security.xcodeproj">
- </BuildableReference>
- </MacroExpansion>
- <AdditionalOptions>
- </AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
ReferencedContainer = "container:Security.xcodeproj">
</BuildableReference>
</MacroExpansion>
- <AdditionalOptions>
- </AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1000"
+ LastUpgradeVersion = "1120"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES"
- onlyGenerateCoverageForSpecifiedTargets = "YES"
- shouldUseLaunchSchemeArgsEnv = "YES">
+ onlyGenerateCoverageForSpecifiedTargets = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "D453A4A42122236D00850A26"
+ BuildableName = "TrustTests.xctest"
+ BlueprintName = "TrustTests_macos"
+ ReferencedContainer = "container:Security.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
</BuildableReference>
</TestableReference>
</Testables>
- <MacroExpansion>
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "D453A4A42122236D00850A26"
- BuildableName = "TrustTests.xctest"
- BlueprintName = "TrustTests_macos"
- ReferencedContainer = "container:Security.xcodeproj">
- </BuildableReference>
- </MacroExpansion>
- <AdditionalOptions>
- </AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
ReferencedContainer = "container:Security.xcodeproj">
</BuildableReference>
</MacroExpansion>
- <AdditionalOptions>
- </AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1000"
+ LastUpgradeVersion = "1120"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
- codeCoverageEnabled = "YES"
- shouldUseLaunchSchemeArgsEnv = "YES">
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ codeCoverageEnabled = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "BEF88C271EAFFC3F00357577"
+ BuildableName = "TrustedPeers.framework"
+ BlueprintName = "TrustedPeers"
+ ReferencedContainer = "container:Security.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
<Testables>
<TestableReference
skipped = "NO">
</BuildableReference>
</TestableReference>
</Testables>
- <MacroExpansion>
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "BEF88C271EAFFC3F00357577"
- BuildableName = "TrustedPeers.framework"
- BlueprintName = "TrustedPeers"
- ReferencedContainer = "container:Security.xcodeproj">
- </BuildableReference>
- </MacroExpansion>
- <AdditionalOptions>
- </AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
ReferencedContainer = "container:Security.xcodeproj">
</BuildableReference>
</MacroExpansion>
- <AdditionalOptions>
- </AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1000"
+ LastUpgradeVersion = "1120"
version = "1.8">
<BuildAction
parallelizeBuildables = "NO"
argument = "si_26_sectrust_copyproperties"
isEnabled = "NO">
</CommandLineArgument>
- <CommandLineArgument
- argument = "si_27_sectrust_exceptions"
- isEnabled = "NO">
- </CommandLineArgument>
<CommandLineArgument
argument = "si_28_sectrustsettings"
isEnabled = "NO">
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1000"
+ LastUpgradeVersion = "1120"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
argument = "si_26_sectrust_copyproperties"
isEnabled = "NO">
</CommandLineArgument>
- <CommandLineArgument
- argument = "si_27_sectrust_exceptions"
- isEnabled = "NO">
- </CommandLineArgument>
<CommandLineArgument
argument = "si_28_sectrustsettings"
isEnabled = "NO">
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1000"
+ LastUpgradeVersion = "1120"
version = "2.0">
<BuildAction
parallelizeBuildables = "NO"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "0C0BDB2E175685B000BC1A7E"
+ BuildableName = "secdtests"
+ BlueprintName = "secdtests_ios"
+ ReferencedContainer = "container:Security.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
<Testables>
<TestableReference
skipped = "NO">
</BuildableReference>
</TestableReference>
</Testables>
- <MacroExpansion>
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "0C0BDB2E175685B000BC1A7E"
- BuildableName = "secdtests"
- BlueprintName = "secdtests_ios"
- ReferencedContainer = "container:Security.xcodeproj">
- </BuildableReference>
- </MacroExpansion>
- <AdditionalOptions>
- </AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
isEnabled = "NO">
</EnvironmentVariable>
</EnvironmentVariables>
- <AdditionalOptions>
- </AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Debug"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1000"
+ LastUpgradeVersion = "1120"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
argument = "si_26_sectrust_copyproperties"
isEnabled = "NO">
</CommandLineArgument>
- <CommandLineArgument
- argument = "si_27_sectrust_exceptions"
- isEnabled = "NO">
- </CommandLineArgument>
<CommandLineArgument
argument = "si_28_sectrustsettings"
isEnabled = "NO">
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1000"
+ LastUpgradeVersion = "1120"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1000"
+ LastUpgradeVersion = "1120"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
- <Testables>
- </Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
ReferencedContainer = "container:Security.xcodeproj">
</BuildableReference>
</MacroExpansion>
- <AdditionalOptions>
- </AdditionalOptions>
+ <Testables>
+ </Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
ReferencedContainer = "container:Security.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
- <AdditionalOptions>
- </AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1000"
+ LastUpgradeVersion = "1120"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
</BuildableReference>
</TestableReference>
</Testables>
- <AdditionalOptions>
- </AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
- <AdditionalOptions>
- </AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
bool gbinfo = false;
bool gbtriggered = false;
bool circleHash = false;
- bool triggerRingUpdate = false;
static struct option long_options[] =
{
/* These options set a flag. */
{"assertStashAccountKey", no_argument, NULL, 'a'},
{"trigger-backup", optional_argument, NULL, 'B'},
- {"trigger-ring-update", no_argument, NULL, 'R'},
{"trigger-sync", optional_argument, NULL, 's'},
{"circle-hash", optional_argument, NULL, 'H'},
{"ghostbustByMID", optional_argument, NULL, 'M'},
{0, 0, 0, 0}
};
- while ((ch = getopt_long(argc, argv, "as:AB:GHIMRST", long_options, &option_index)) != -1) {
+ while ((ch = getopt_long(argc, argv, "as:AB:GHMSIT", long_options, &option_index)) != -1) {
switch (ch) {
case 'a': {
assertStashAccountKey = true;
gboptions |= SOSGhostBustByMID;
break;
}
- case 'R':
- triggerRingUpdate = true;
- break;
case 'S': {
gboptions |= SOSGhostBustBySerialNumber;
break;
} else if (triggerSync) {
[[control.connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError *error) {
printControlFailureMessage(error);
- }] rpcTriggerSync:syncingPeers complete:^(bool res, NSError *error) {
+ }] triggerSync:syncingPeers complete:^(bool res, NSError *error) {
if (res) {
printf("starting to sync was successful\n");
} else {
} else if (triggerBackup) {
[[control.connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError *error) {
printControlFailureMessage(error);
- }] rpcTriggerBackup:backupPeers complete:^(NSError *error) {
+ }] triggerBackup:backupPeers complete:^(NSError *error) {
if (error == NULL) {
printf("trigger backup was successful\n");
} else {
- printf("%s", [[NSString stringWithFormat:@"Failed to start backup: %@\n", error] UTF8String]);
- }
- }];
- } else if (triggerRingUpdate) {
- [[control.connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError *error) {
- printControlFailureMessage(error);
- }] rpcTriggerRingUpdate:^(NSError *error) {
- if (error == NULL) {
- printf("trigger ring update was successful\n");
- } else {
- printf("%s", [[NSString stringWithFormat:@"Failed to start ring update: %@\n", error] UTF8String]);
+ printf("%s", [[NSString stringWithFormat:@"Failed to start sync: %@\n", error] UTF8String]);
}
}];
#include "keychain/SecureObjectSync/Tool/keychain_sync.h"
#include "keychain/SecureObjectSync/Tool/keychain_sync_test.h"
#include "keychain/SecureObjectSync/Tool/keychain_log.h"
-#include "keychain/SecureObjectSync/Tool/syncbackup.h"
#include "keychain/SecureObjectSync/Tool/recovery_key.h"
* @abstract
* Create an ARC-able `sec_experiment_t` instance wrapping an internal `SecExperiment` object.
*
- * @param experiment_name
+ * @param experiment
* Name of the experiment.
*
* @return a `sec_experiment_t` instance.
--- /dev/null
+#!/usr/bin/python
+#
+
+import sys
+from glob import glob
+import subprocess
+import re
+import os
+import argparse
+
+parser = argparse.ArgumentParser(description='Reset your iCloud Keychain account')
+parser.add_argument('icloudpassword', help='iCloud password')
+parser.add_argument('passcode', help='passcode or password of the local device')
+args = vars(parser.parse_args())
+
+
+iCloudPassword = args['icloudpassword']
+passcode = args['passcode']
+
+def set_security_mac_cmd():
+ return 'security2'
+
+def set_security_ios_cmd():
+ return 'security'
+
+def security_cmd_by_platform():
+ swVers = subprocess.check_output(["sw_vers"])
+ deviceInformation = str(swVers, 'utf-8')
+ if "Mac OS X" in deviceInformation:
+ print("using security2 command on macosx")
+ return set_security_mac_cmd()
+ elif "iPhone OS" in deviceInformation:
+ print("using security command on ios")
+ return set_security_ios_cmd()
+ else:
+ print("unsupported platform")
+ sys.exit(1)
+
+security_cmd = security_cmd_by_platform()
+
+print("resetting octagon")
+subprocess.check_output(["otctl", "resetoctagon"])
+
+print("resetting ckks")
+subprocess.check_output(["ckksctl", "reset-cloudkit"])
+
+print("resetting SOS")
+subprocess.check_output([security_cmd, "sync", "-C"])
+subprocess.check_output([security_cmd, "sync", "-P", "$iCloudPassword"])
+subprocess.check_output([security_cmd, "sync", "-O"])
+
+print("deleting all escrow records")
+subprocess.check_output(["stingrayutil", "--deleteAll", "ReallyDeleteAll"])
+
+print("creating new escrow record")
+subprocess.check_output(["sbdtool", "passcode_request_trigger"])
+subprocess.check_output(["sbdtool", "passcode_request_provide_passcode", "$passcode"])
+
ONE_TEST(sc_45_digestvector)
ONE_TEST(sc_130_resignationticket)
-ONE_TEST(sc_150_Ring)
ONE_TEST(sc_150_backupkeyderivation)
ONE_TEST(sc_153_backupslicekeybag)
+++ /dev/null
-//
-// sc-150-ring.c
-// sec
-//
-// Created by Richard Murphy on 3/3/15.
-//
-//
-
-#include <stdio.h>
-/*
- * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-
-#include <Security/SecBase.h>
-#include <Security/SecItem.h>
-#include <Security/SecKeyPriv.h>
-
-#include "keychain/SecureObjectSync/SOSRing.h"
-#include "keychain/SecureObjectSync/SOSRingTypes.h"
-#include "keychain/SecureObjectSync/SOSRingUtils.h"
-#include <Security/SecureObjectSync/SOSPeerInfo.h>
-#include "keychain/SecureObjectSync/SOSInternal.h"
-#include "keychain/SecureObjectSync/SOSUserKeygen.h"
-
-#include <utilities/SecCFWrappers.h>
-
-#include <CoreFoundation/CoreFoundation.h>
-
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "SOSCircle_regressions.h"
-#include "SOSRegressionUtilities.h"
-
-static SOSFullPeerInfoRef SOSCreateApplicantFullPeerInfoFromName(CFStringRef peerName,
- SecKeyRef user_private_key,
- SecKeyRef* outSigningKey,
- SecKeyRef* outOctagonSigningKey,
- SecKeyRef* outOctagonEncryptionKey,
- CFErrorRef *error)
-{
- SOSFullPeerInfoRef result = NULL;
- SOSFullPeerInfoRef fullPeer = SOSCreateFullPeerInfoFromName(peerName, outSigningKey, outOctagonSigningKey, outOctagonEncryptionKey, error);
-
- if (fullPeer && SOSFullPeerInfoPromoteToApplication(fullPeer, user_private_key, error))
- CFTransferRetained(result, fullPeer);
-
- CFReleaseNull(fullPeer);
- return result;
-}
-
-static int kTestTestCount = 24;
-static void tests(void)
-{
-
- //SecKeyRef publicKey = NULL;
- SecKeyRef dev_a_key = NULL;
- SecKeyRef dev_b_key = NULL;
- SecKeyRef dev_c_key = NULL;
- SecKeyRef oct_dev_as_key = NULL;
- SecKeyRef oct_dev_aw_key = NULL;
- SecKeyRef oct_dev_bs_key = NULL;
- SecKeyRef oct_dev_bw_key = NULL;
- SecKeyRef oct_dev_cs_key = NULL;
- SecKeyRef oct_dev_cw_key = NULL;
- CFErrorRef error = NULL;
- CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
-
- ok(cfpassword, "no password");
-
- CFDataRef parameters = SOSUserKeyCreateGenerateParameters(&error);
- ok(parameters, "No parameters!");
- ok(error == NULL, "Error: (%@)", error);
- CFReleaseNull(error);
-
- SecKeyRef user_privkey = SOSUserKeygen(cfpassword, parameters, &error);
- CFReleaseNull(parameters);
-
- SecKeyRef user_pubkey = SecKeyCreatePublicFromPrivate(user_privkey);
-
-
- SOSFullPeerInfoRef peer_a_full_info = SOSCreateApplicantFullPeerInfoFromName(CFSTR("Peer A"), user_privkey, &dev_a_key, &oct_dev_as_key, &oct_dev_aw_key, NULL);
- SOSFullPeerInfoRef peer_b_full_info = SOSCreateApplicantFullPeerInfoFromName(CFSTR("Peer B"), user_privkey, &dev_b_key, &oct_dev_bs_key, &oct_dev_bw_key, NULL);
- SOSFullPeerInfoRef peer_c_full_info = SOSCreateApplicantFullPeerInfoFromName(CFSTR("Peer C"), user_privkey, &dev_c_key, &oct_dev_cs_key, &oct_dev_cw_key, NULL);
- CFStringRef peerID_a = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(peer_a_full_info));
- CFStringRef peerID_b = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(peer_b_full_info));
- SOSRingRef Ring = SOSRingCreate(CFSTR("TESTRING"), peerID_a, kSOSRingBase, NULL);
-
- ok(Ring, "Ring creation");
-
-
- ok(0 == SOSRingCountPeers(Ring), "Zero peers");
-
- ok(SOSRingApply(Ring, user_pubkey, peer_a_full_info, NULL));
- ok(SOSRingApply(Ring, user_pubkey, peer_b_full_info, NULL));
-
- ok(2 == SOSRingCountPeers(Ring), "Two peers");
-
- ok(SOSRingWithdraw(Ring, user_privkey, peer_b_full_info, NULL));
-
- ok(1 == SOSRingCountPeers(Ring), "One peer");
-
- ok(kSOSRingMember == SOSRingDeviceIsInRing(Ring, peerID_a), "peer_a is in Ring");
- ok(kSOSRingNotInRing == SOSRingDeviceIsInRing(Ring, peerID_b), "peer_b is not in Ring");
- CFStringRef lastmod = SOSRingGetLastModifier(Ring);
- ok(CFEqual(lastmod, peerID_b), "peer_b_full_info did last mod");
-
- ok(SOSRingResetToEmpty(Ring, peerID_a, NULL), "Reset the circle");
- ok(kSOSRingNotInRing == SOSRingDeviceIsInRing(Ring, peerID_a), "peer_a is not in Ring");
-
- ok(SOSRingResetToOffering(Ring, NULL, peer_a_full_info, NULL), "Reset Ring to Offering for PeerA");
- ok(kSOSRingMember == SOSRingDeviceIsInRing(Ring, peerID_a), "peer_a is in Ring");
- ok(kSOSRingNotInRing == SOSRingDeviceIsInRing(Ring, peerID_b), "peer_b is not in Ring");
-
- CFDataRef ringDER = SOSRingCopyEncodedData(Ring, NULL);
- ok(ringDER, "Successful encoding to DER of Ring");
- SOSRingRef Ring2 = SOSRingCreateFromData(NULL, ringDER);
- ok(Ring2, "Successful decoding of DER to Ring");
-
- ok(CFEqualSafe(Ring, Ring2), "Compares");
-
- ok(SOSRingApply(Ring, user_pubkey, peer_c_full_info, NULL));
- ok(SOSRingApply(Ring, user_pubkey, peer_b_full_info, NULL));
-
- CFReleaseNull(ringDER);
- CFReleaseNull(Ring2);
- ringDER = SOSRingCopyEncodedData(Ring, NULL);
- Ring2 = SOSRingCreateFromData(NULL, ringDER);
- ok(CFEqualSafe(Ring, Ring2), "Compares");
-
- CFReleaseNull(ringDER);
- CFReleaseNull(Ring2);
- CFReleaseNull(dev_a_key);
- CFReleaseNull(dev_b_key);
- CFReleaseNull(dev_c_key);
- CFReleaseNull(oct_dev_as_key);
- CFReleaseNull(oct_dev_aw_key);
- CFReleaseNull(oct_dev_bs_key);
- CFReleaseNull(oct_dev_bw_key);
- CFReleaseNull(oct_dev_cs_key);
- CFReleaseNull(oct_dev_cw_key);
- CFReleaseNull(cfpassword);
-
- CFReleaseNull(user_privkey);
- CFReleaseNull(user_pubkey);
-
- CFReleaseNull(peer_a_full_info);
- CFReleaseNull(peer_b_full_info);
- CFReleaseNull(peer_c_full_info);
- CFReleaseNull(Ring);
-}
-
-int sc_150_Ring(int argc, char *const *argv)
-{
- plan_tests(kTestTestCount);
-
- tests();
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-
-// Run on a device:
-// /AppleInternal/Applications/SecurityTests.app/SecurityTests sc_kvstool -v -- --dump
-// /AppleInternal/Applications/SecurityTests.app/SecurityTests sc_kvstool -v -- --clear
-// /AppleInternal/Applications/SecurityTests.app/SecurityTests sc_kvstool -v -- --putcircle
-// /AppleInternal/Applications/SecurityTests.app/SecurityTests sc_kvstool -v -- --direct --dump
-// /AppleInternal/Applications/SecurityTests.app/SecurityTests sc_kvstool -v -- --direct --putcircle
-
-#include <Foundation/Foundation.h>
-#include <SecureObjectSync/SOSEngine.h>
-#include <SecureObjectSync/SOSPeer.h>
-#include <SecureObjectSync/SOSInternal.h>
-#include <SecureObjectSync/SOSCloudCircle.h>
-
-#include "SOSCircle_regressions.h"
-
-#include <corecrypto/ccsha2.h>
-
-#include <utilities/SecCFWrappers.h>
-#include <utilities/debugging.h>
-
-#include <stdint.h>
-
-#include <AssertMacros.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <CoreFoundation/CFDate.h>
-#include <getopt.h>
-
-#include <notify.h>
-#include <dispatch/dispatch.h>
-
-#include "SOSCircle_regressions.h"
-#include "CKDLocalKeyValueStore.h"
-#include "SOSRegressionUtilities.h"
-#include "SOSTestDataSource.h"
-#include "SOSTestTransport.h"
-#include "SOSCloudKeychainClient.h"
-
-#import "SOSDirectCloudTransport.h"
-
-// MARK: ----- Constants -----
-
-static CFStringRef circleKey = CFSTR("Circle");
-
-// MARK: ----- start of all tests -----
-
-static void putCircleInCloud(SOSCircleRef circle, dispatch_queue_t work_queue, dispatch_group_t work_group)
-{
- CFErrorRef error = NULL;
- CFDataRef newCloudCircleEncoded = SOSCircleCopyEncodedData(circle, kCFAllocatorDefault, &error);
- ok(newCloudCircleEncoded, "Encoded as: %@ [%@]", newCloudCircleEncoded, error);
-
- // Send the circle with our application request back to cloud
- testPutObjectInCloud(circleKey, newCloudCircleEncoded, &error, work_group, work_queue);
-}
-
-static void createAndPutInitialCircle(void)
-{
- dispatch_queue_t work_queue = dispatch_queue_create("capic", DISPATCH_QUEUE_CONCURRENT);
- dispatch_group_t work_group = dispatch_group_create();
-
- CFErrorRef error = NULL;
- CFStringRef cflabel = CFSTR("TEST_USERKEY");
- CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
- SOSCCRegisterUserCredentials(cflabel, cfpassword, &error);
-
- CFErrorRef localError = NULL;
-
- SOSDataSourceFactoryRef our_data_source_factory = SOSTestDataSourceFactoryCreate();
- SOSDataSourceRef our_data_source = SOSTestDataSourceCreate();
- SOSTestDataSourceFactoryAddDataSource(our_data_source_factory, circleKey, our_data_source);
-
- CFDictionaryRef gestalt = SOSCreatePeerGestaltFromName(CFSTR("Alice"));
-
- SOSAccountRef our_account = SOSAccountCreate(kCFAllocatorDefault, gestalt, our_data_source_factory,
- ^(CFArrayRef keys)
- {
- pass("SOSAccountKeyInterestBlock");
- },
- ^ bool (CFDictionaryRef keys, CFErrorRef *error)
- {
- pass("SOSAccountDataUpdateBlock");
- return false;
- },
- NULL);
- SOSAccountEnsureCircle(our_account, circleKey);
-
- SOSFullPeerInfoRef our_full_peer_info = SOSAccountGetMyFullPeerInCircleNamed(our_account, circleKey, &error);
- SOSPeerInfoRef our_peer_info = SOSFullPeerInfoGetPeerInfo(our_full_peer_info);
- CFRetain(our_peer_info);
-
- SOSCircleRef circle = SOSAccountFindCircle(our_account, circleKey);
- CFRetain(circle);
-
-// SecKeyRef user_privkey = SOSUserGetPrivKey(&localError);
- SecKeyRef user_privkey = NULL; // TODO: this will not work
- ok(SOSCircleRequestAdmission(circle, user_privkey, our_full_peer_info, &localError), "Requested admission (%@)", our_peer_info);
- ok(SOSCircleAcceptRequests(circle, user_privkey, our_full_peer_info, &localError), "Accepted self");
-
- putCircleInCloud(circle, work_queue, work_group);
- pass("Put circle in cloud: (%@)", circle);
-
- CFRelease(circle);
-}
-
-static void postCircleChangeNotification()
-{
- NSArray *keys = [NSArray arrayWithObjects:(id)circleKey, nil];
- NSDictionary* userInfo = [[NSDictionary alloc] initWithObjectsAndKeys:
- keys, NSUbiquitousKeyValueStoreChangedKeysKey,
- [NSNumber numberWithInt:NSUbiquitousKeyValueStoreServerChange], NSUbiquitousKeyValueStoreChangeReasonKey,
- nil];
- [[NSNotificationCenter defaultCenter] postNotificationName:NSUbiquitousKeyValueStoreDidChangeExternallyNotification object:NULL userInfo:userInfo];
- [userInfo release];
-}
-
-static void requestSynchronization(dispatch_queue_t processQueue, dispatch_group_t dgroup)
-{
- testSynchronize(processQueue, dgroup);
-}
-
-static void displayCircles(CFTypeRef objects)
-{
- // SOSCCCopyApplicantPeerInfo doesn't display all info, e.g. in the case where we are not in circle
- CFDictionaryForEach(objects, ^(const void *key, const void *value)
- {
- if (SOSKVSKeyGetKeyType(key) == kCircleKey)
- {
- CFErrorRef localError = NULL;
- if (isData(value))
- {
- SOSCircleRef circle = SOSCircleCreateFromData(NULL, (CFDataRef) value, &localError);
- pass("circle: %@ %@", key, circle);
- CFReleaseSafe(circle);
- }
- else
- pass("non-circle: %@ %@", key, value);
- }
- });
-}
-
-
-static void dumpCircleInfo()
-{
- CFErrorRef error = NULL;
- CFArrayRef applicantPeerInfos = NULL;
- CFArrayRef peerInfos = NULL;
- int idx;
-
- NSArray *ccmsgs = @[@"Error", @"InCircle", @"NotInCircle", @"RequestPending", @"CircleAbsent"];
-
- SOSCCStatus ccstatus = SOSCCThisDeviceIsInCircle(&error);
- pass("ccstatus: %d, error: %@", ccstatus, error);
- idx = ccstatus-kSOSCCError;
- if (0<=idx && idx<(int)[ccmsgs count])
- pass("ccstatus: %d (%@)", ccstatus, ccmsgs[idx]);
-
- // Now look at current applicants
- applicantPeerInfos = SOSCCCopyApplicantPeerInfo(&error);
- if (applicantPeerInfos)
- {
- pass("Applicants: %ld, error: %@", (long)CFArrayGetCount(applicantPeerInfos), error);
- CFArrayForEach(applicantPeerInfos, ^(const void *value) {
- SOSPeerInfoRef peer = (SOSPeerInfoRef)value;
- CFStringRef peerName = SOSPeerInfoGetPeerName(peer);
- pass("Applicant: %@", peerName);
- });
- }
- else
- pass("No applicants, error: %@", error);
-
-
- peerInfos = SOSCCCopyPeerPeerInfo(&error);
- if (peerInfos)
- {
- pass("Peers: %ld, error: %@", (long)CFArrayGetCount(applicantPeerInfos), error);
- CFArrayForEach(peerInfos, ^(const void *value) {
- SOSPeerInfoRef peer = (SOSPeerInfoRef)value;
- CFStringRef peerName = SOSPeerInfoGetPeerName(peer);
- pass("Peer: %@", peerName);
- });
- }
- else
- pass("No peers, error: %@", error);
-}
-
-// define the options table for the command line
-static const struct option options[] =
-{
- { "verbose", optional_argument, NULL, 'v' },
- { "dump", optional_argument, NULL, 'd' },
- { "clear", optional_argument, NULL, 'C' },
- { "putcircle", optional_argument, NULL, 'p' },
- { "direct", optional_argument, NULL, 'D' },
- { "notify", optional_argument, NULL, 'n' },
- { "sync", optional_argument, NULL, 's' },
- { "info", optional_argument, NULL, 'i' },
- { }
-};
-
-static int kTestCount = 10;
-
-static void usage(void)
-{
- printf("Usage:\n");
- printf(" --dump [itemName] Dump the contents of the kvs store (through proxy)\n");
- printf(" --clear Clear the contents of the kvs store (through proxy)\n");
- printf(" --putcircle Put a new circle into the kvs store (through proxy)\n");
- printf(" --direct Go directly to KVS (bypass proxy)\n");
- printf(" --notify Post a notification that the circle key has changed\n");
- printf(" --sync Post a notification that the circle key has changed\n");
- printf(" --info Dump info about circle and peer status\n");
-}
-
-enum kvscommands
-{
- kCommandClear = 1,
- kCommandDump = 2,
- kCommandPutCircle = 3,
- kCommandNotify = 4,
- kCommandSynchronize = 5,
- kCommandInfo = 6,
- kCommandHelp
-};
-
-static int command = kCommandHelp;
-static bool useDirect = false;
-
-static void tests(const char *itemName)
-{
- dispatch_queue_t generalq = dispatch_queue_create("general", DISPATCH_QUEUE_SERIAL);
- dispatch_group_t work_group = dispatch_group_create();
-
-#if 0
- if (useDirect)
- {
- SOSCloudKeychainServerInit();
- }
-#endif
- SOSCloudKeychainSetCallbackMethodXPC(); // call this first
-
- pass("Command: %d", command);
- switch (command)
- {
- case kCommandClear:
- ok(testClearAll(generalq, work_group), "test Clear All");
- break;
- case kCommandDump:
- {
- CFArrayRef keysToGet = NULL;
- if (itemName)
- {
- CFStringRef itemStr = CFStringCreateWithCString(kCFAllocatorDefault, itemName, kCFStringEncodingUTF8);
- pass("Retrieving : %@", itemStr);
- keysToGet = CFArrayCreateForCFTypes(kCFAllocatorDefault, itemStr);
- CFReleaseSafe(itemStr);
- }
- CFTypeRef objects = testGetObjectsFromCloud(keysToGet, generalq, work_group);
- CFReleaseSafe(keysToGet);
- pass(" : %@", objects);
- displayCircles(objects);
- }
- break;
- case kCommandPutCircle:
- createAndPutInitialCircle();
- break;
- case kCommandNotify:
- postCircleChangeNotification();
- break;
- case kCommandSynchronize:
- requestSynchronization(generalq, work_group);
- break;
- case kCommandInfo:
- dumpCircleInfo();
- break;
- default:
- case kCommandHelp:
- usage();
- break;
- }
-}
-
-int sc_kvstool(int argc, char *const *argv)
-{
- char *itemName = NULL;
-// extern int optind;
- extern char *optarg;
- int arg, argSlot;
-
- while (argSlot = -1, (arg = getopt_long(argc, (char * const *)argv, "ivChpdns", options, &argSlot)) != -1)
- switch (arg)
- {
- case 'd':
- itemName = (char *)(optarg);
- command = kCommandDump;
- break;
- case 'C': // should set up to call testClearAll
- command = kCommandClear;
- break;
- case 'p':
- command = kCommandPutCircle;
- break;
- case 'n':
- command = kCommandNotify;
- break;
- case 's':
- command = kCommandSynchronize;
- break;
- case 'i':
- command = kCommandInfo;
- break;
- case 'D':
- useDirect = true;
- printf("Using direct calls to KVS\n");
- break;
- default:
- secerror("arg: %s", optarg);
- break;
- }
-
- plan_tests(kTestCount);
-
- secerror("Command: %d", command);
- printf("Command: %d\n", command);
-
- tests(itemName);
-
- return 0;
-}
bool SOSAccountScanForRetired(SOSAccount* account, SOSCircleRef circle, CFErrorRef *error);
CF_RETURNS_RETAINED SOSCircleRef SOSAccountCloneCircleWithRetirement(SOSAccount* account, SOSCircleRef starting_circle, CFErrorRef *error);
-//
-// MARK: Version incompatibility Functions
-//
-CFStringRef SOSAccountCopyIncompatibilityInfo(SOSAccount* account, CFErrorRef* error);
-
//
// MARK: Backup functions
//
CF_RETURNS_RETAINED SOSBackupSliceKeyBagRef SOSAccountBackupSliceKeyBagForView(SOSAccount* account, CFStringRef viewName, CFErrorRef* error);
-bool SOSAccountIsLastBackupPeer(SOSAccount* account, CFErrorRef *error);
-
-
//
// MARK: Recovery Public Key Functions
//
@property (readwrite) CKKSPBFileStorage<SOSAccountConfiguration*>* accountConfiguration;
@property CKKSNearFutureScheduler *performBackups;
-@property CKKSNearFutureScheduler *performRingUpdates;
@end
#endif
CFReleaseNull(self->_accountKey);
CFReleaseNull(self->_accountPrivateKey);
CFReleaseNull(self->_previousAccountKey);
-#if OCTAGON
- [self.performBackups cancel];
- [self.performRingUpdates cancel];
- [self.stateMachine haltOperation];
-#endif
}
}
-(bool) ensureFactoryCircles
{
- if (self.factory == nil){
+ if (!self){
+ return false;
+ }
+
+ if (!self.factory){
return false;
}
- NSString* circle_name = CFBridgingRelease(SOSDataSourceFactoryCopyName(self.factory));
+ NSString* circle_name = (__bridge_transfer NSString*)SOSDataSourceFactoryCopyName(self.factory);
+
if (!circle_name){
return false;
}
self.settings = [[NSUserDefaults alloc] initWithSuiteName:SOSAccountUserDefaultsSuite];
- [self ensureFactoryCircles];
- SOSAccountEnsureUUID(self);
-
#if OCTAGON
[self setupStateMachine];
#endif
return self;
}
-- (void)startStateMachine
-{
-#if OCTAGON
- [self.stateMachine startOperation];
-#endif
-}
-
-(BOOL)isEqual:(id) object
{
if(![object isKindOfClass:[SOSAccount class]])
CFReleaseNull(error);
}
-- (void)rpcTriggerSync:(NSArray <NSString *> *)peers complete:(void(^)(bool success, NSError *))complete
+- (void)triggerSync:(NSArray <NSString *> *)peers complete:(void(^)(bool success, NSError *))complete
{
__block CFErrorRef localError = NULL;
__block bool res = false;
CFReleaseNull(localError);
}
-- (void)rpcTriggerBackup:(NSArray<NSString *>* _Nullable)backupPeers complete:(void (^)(NSError *error))complete
+- (void)triggerBackup:(NSArray<NSString *>* _Nullable)backupPeers complete:(void (^)(NSError *error))complete
{
__block CFErrorRef localError = NULL;
CFReleaseNull(localError);
}
-- (void)rpcTriggerRingUpdate:(void (^)(NSError *error))complete
-{
-#if OCTAGON
- [self triggerRingUpdate];
-#endif
- complete(NULL);
-}
-
- (void)getWatchdogParameters:(void (^)(NSDictionary* parameters, NSError* error))complete
{
// SecdWatchdog is only available in the secd/securityd - no other binary will contain that class
SOSDataSourceFactoryRef factory) {
SOSAccount* a = [[SOSAccount alloc] initWithGestalt:gestalt factory:factory];
- dispatch_sync(a.queue, ^{
- secnotice("circleop", "Setting account.key_interests_need_updating to true in SOSAccountCreate");
- a.key_interests_need_updating = true;
- });
+ [a ensureFactoryCircles];
+ SOSAccountEnsureUUID(a);
+ secnotice("circleop", "Setting account.key_interests_need_updating to true in SOSAccountCreate");
+ a.key_interests_need_updating = true;
return a;
}
a.key_interests_need_updating = true;
}
-bool SOSAccountIsNew(SOSAccount* account, CFErrorRef *error){
- bool result = false;
- SOSAccountTrustClassic* trust = account.trust;
- if(account.accountKeyIsTrusted != false || trust.departureCode != kSOSNeverAppliedToCircle ||
- CFSetGetCount((__bridge CFSetRef)trust.retirees) != 0)
- return result;
-
- if(trust.retirees != nil)
- return result;
- if(trust.expansion != nil)
- return result;
-
- if(account.user_private_timer != NULL || account.lock_notification_token != NOTIFY_TOKEN_INVALID)
- return result;
-
- result = true;
-
- return result;
-}
-
dispatch_queue_t SOSAccountGetQueue(SOSAccount* account) {
return account.queue;
}
return retval;
}
-bool sosAccountLeaveRing(SOSAccount* account, SOSRingRef ring, CFErrorRef* error) {
- SOSAccountTrustClassic *trust = account.trust;
- SOSFullPeerInfoRef identity = trust.fullPeerInfo;
-
- SOSFullPeerInfoRef fpi = identity;
- if(!fpi) return false;
- SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
- CFStringRef peerID = SOSPeerInfoGetPeerID(pi);
-
- CFErrorRef localError = NULL;
-
- bool retval = false;
- bool writeRing = false;
- bool writePeerInfo = false;
-
- if(SOSRingHasPeerID(ring, peerID)) {
- writePeerInfo = true;
- }
-
- if(writePeerInfo || writeRing) {
- SOSRingWithdraw(ring, NULL, fpi, error);
- }
-
- if (writeRing) {
- CFDataRef ring_data = SOSRingCopyEncodedData(ring, error);
-
- if (ring_data) {
- [account.circle_transport kvsRingPostRing:SOSRingGetName(ring) ring:ring_data err:NULL];
- }
- CFReleaseNull(ring_data);
- }
- retval = true;
- CFReleaseNull(localError);
- return retval;
-}
-
bool SOSAccountPostDebugScope(SOSAccount* account, CFTypeRef scope, CFErrorRef *error) {
bool result = false;
if (account.circle_transport) {
//
-- (bool)_onQueueEnsureInBackupRings {
+bool SOSAccountEnsureInBackupRings(SOSAccount* account) {
__block bool result = false;
__block CFErrorRef error = NULL;
secnotice("backup", "Ensuring in rings");
- dispatch_assert_queue(self.queue);
-
- if(!self.backup_key){
+ if(!account.backup_key){
return true;
}
- if(!SOSBSKBIsGoodBackupPublic((__bridge CFDataRef)self.backup_key, &error)){
+ if(!SOSBSKBIsGoodBackupPublic((__bridge CFDataRef)account.backup_key, &error)){
secnotice("backupkey", "account backup key isn't valid: %@", error);
- self.backup_key = nil;
+ account.backup_key = nil;
CFReleaseNull(error);
return false;
}
- NSData *peerBackupKey = (__bridge_transfer NSData*)SOSPeerInfoCopyBackupKey(self.peerInfo);
- if(![peerBackupKey isEqual:self.backup_key]) {
- result = SOSAccountUpdatePeerInfo(self, CFSTR("Backup public key"), &error, ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
- return SOSFullPeerInfoUpdateBackupKey(fpi, (__bridge CFDataRef)(self.backup_key), error);
+ NSData *peerBackupKey = (__bridge_transfer NSData*)SOSPeerInfoCopyBackupKey(account.peerInfo);
+ if(![peerBackupKey isEqual:account.backup_key]) {
+ result = SOSAccountUpdatePeerInfo(account, CFSTR("Backup public key"), &error, ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
+ return SOSFullPeerInfoUpdateBackupKey(fpi, (__bridge CFDataRef)(account.backup_key), error);
});
if (!result) {
secnotice("backupkey", "Failed to setup backup public key in peerInfo from account: %@", error);
CFReleaseNull(localError);
// Setup backups the new way.
- SOSAccountForEachBackupView(self, ^(const void *value) {
+ SOSAccountForEachBackupView(account, ^(const void *value) {
CFStringRef viewName = asString(value, NULL);
- bool resetRing = SOSAccountValidateBackupRingForView(self, viewName, NULL);
+ bool resetRing = SOSAccountValidateBackupRingForView(account, viewName, NULL);
if(resetRing) {
- SOSAccountUpdateBackupRing(self, viewName, NULL, ^SOSRingRef(SOSRingRef existing, CFErrorRef *error) {
- SOSRingRef newRing = SOSAccountCreateBackupRingForView(self, viewName, error);
+ SOSAccountUpdateBackupRing(account, viewName, NULL, ^SOSRingRef(SOSRingRef existing, CFErrorRef *error) {
+ SOSRingRef newRing = SOSAccountCreateBackupRingForView(account, viewName, error);
return newRing;
});
}
return success;
}
-
-CFStringRef SOSAccountCopyIncompatibilityInfo(SOSAccount* account, CFErrorRef* error) {
- return CFSTR("We're compatible, go away");
-}
-
enum DepartureReason SOSAccountGetLastDepartureReason(SOSAccount* account, CFErrorRef* error) {
SOSAccountTrustClassic *trust = account.trust;
return trust.departureCode;
SOSEngineInitializePeerCoder((SOSEngineRef)[account.kvs_message_transport SOSTransportMessageGetEngine], trust.fullPeerInfo, peer, &localError);
if (localError)
secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer, trust.fullPeerInfo, localError);
- CFReleaseNull(localError);
+ CFReleaseSafe(localError);
}
});
return (__bridge CFTypeRef)([trust.expansion objectForKey:(__bridge NSString* _Nonnull)(key)]);
}
-bool SOSAccountAddEscrowRecords(SOSAccount* account, CFStringRef dsid, CFDictionaryRef record, CFErrorRef *error){
- CFMutableDictionaryRef escrowRecords = (CFMutableDictionaryRef)SOSAccountGetValue(account, kSOSEscrowRecord, error);
- CFMutableDictionaryRef escrowCopied = NULL;
- bool success = false;
-
- if(isDictionary(escrowRecords) && escrowRecords != NULL)
- escrowCopied = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(escrowRecords), escrowRecords);
- else
- escrowCopied = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-
- CFDictionaryAddValue(escrowCopied, dsid, record);
- SOSAccountSetValue(account, kSOSEscrowRecord, escrowCopied, error);
-
- if(*error == NULL)
- success = true;
-
- CFReleaseNull(escrowCopied);
-
- return success;
-
-}
+
bool SOSAccountAddEscrowToPeerInfo(SOSAccount* account, SOSFullPeerInfoRef myPeer, CFErrorRef *error){
bool success = false;
return success;
}
-- (void)_onQueueRecordRetiredPeersInCircle {
-
- dispatch_assert_queue(self.queue);
-
- if (![self isInCircle:NULL]) {
+void SOSAccountRecordRetiredPeersInCircle(SOSAccount* account) {
+ if (![account isInCircle:NULL]) {
return;
}
__block bool updateRings = false;
- SOSAccountTrustClassic *trust = self.trust;
- [trust modifyCircle:self.circle_transport err:NULL action:^bool (SOSCircleRef circle) {
+ SOSAccountTrustClassic *trust = account.trust;
+ [trust modifyCircle:account.circle_transport err:NULL action:^bool (SOSCircleRef circle) {
__block bool updated = false;
CFSetForEach((__bridge CFMutableSetRef)trust.retirees, ^(CFTypeRef element){
SOSPeerInfoRef retiree = asSOSPeerInfo(element);
updated = true;
secnotice("retirement", "Updated retired peer %@ in %@", retiree, circle);
CFErrorRef cleanupError = NULL;
- if (![self.trust cleanupAfterPeer:self.kvs_message_transport circleTransport:self.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:retiree err:&cleanupError])
+ if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:retiree err:&cleanupError])
secerror("Error cleanup up after peer (%@): %@", retiree, cleanupError);
CFReleaseSafe(cleanupError);
updateRings = true;
return updated;
}];
if(updateRings) {
- SOSAccountProcessBackupRings(self, NULL);
+ SOSAccountProcessBackupRings(account, NULL);
}
}
}
-bool SOSAccountPopulateKVSWithBadKeys(SOSAccount* account, CFErrorRef* error) {
-
- NSMutableDictionary *testKeysAndValues = [NSMutableDictionary dictionary];
- [testKeysAndValues setObject:@"deadbeef" forKey:@"-ak|asdfjkl;asdfjk;"];
- [testKeysAndValues setObject:@"foobar" forKey:@"ak|asdfasdfasdf:qwerqwerqwer"];
- [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"poak|asdfasdfasdfasdf"];
- [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"po|asdfasdfasdfasdfasdfasdf"];
- [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"k>KeyParm"];
-
- dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
- dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
- dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-
- CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error){
- if (error){
- secerror("SOSCloudKeychainPutObjectsInCloud returned error: %@", error);
- }
- dispatch_semaphore_signal(waitSemaphore);
- };
-
- SOSCloudKeychainPutObjectsInCloud((__bridge CFDictionaryRef)(testKeysAndValues), processQueue, replyBlock);
- dispatch_semaphore_wait(waitSemaphore, finishTime);
-
- return true;
-}
-
SOSPeerInfoRef SOSAccountCopyApplication(SOSAccount* account, CFErrorRef* error) {
SOSPeerInfoRef applicant = NULL;
SOSAccountTrustClassic *trust = account.trust;
*/
OctagonFlag* SOSFlagTriggerBackup = (OctagonFlag*)@"trigger_backup";
-OctagonFlag* SOSFlagTriggerRingUpdate = (OctagonFlag*)@"trigger_ring_update";
OctagonState* SOSStateReady = (OctagonState*)@"ready";
OctagonState* SOSStateError = (OctagonState*)@"error";
OctagonState* SOSStatePerformBackup = (OctagonState*)@"perform_backup";
-OctagonState* SOSStatePerformRingUpdate = (OctagonState*)@"perform_ring_update";
static NSDictionary<OctagonState*, NSNumber*>* SOSStateMap(void) {
static NSDictionary<OctagonState*, NSNumber*>* map = nil;
SOSStateReady: @0U,
SOSStateError: @1U,
SOSStatePerformBackup: @2U,
- SOSStatePerformRingUpdate: @3U,
};
});
return map;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
set = [NSSet setWithArray:@[
- SOSFlagTriggerBackup,
- SOSFlagTriggerRingUpdate,
+ SOSFlagTriggerBackup
]];
});
return set;
[self addBackupFlag];
}];
- self.performRingUpdates = [[CKKSNearFutureScheduler alloc] initWithName:@"performRingUpdates"
- initialDelay:5*NSEC_PER_SEC
- expontialBackoff:2.0
- maximumDelay:15*60*NSEC_PER_SEC
- keepProcessAlive:YES
- dependencyDescriptionCode:CKKSResultDescriptionNone
- block:^{
- STRONGIFY(self);
- [self addRingUpdateFlag];
- }];
-
SOSAccountConfiguration *conf = self.accountConfiguration.storage;
if (conf.pendingBackupPeers.count) {
[self addBackupFlag];
}
- if (conf.ringUpdateFlag) {
- [self addRingUpdateFlag];
- }
-}
+ [self.stateMachine startOperation];
+}
-/*
- * Flag adding to state machine
- */
- (void)addBackupFlag {
OctagonPendingFlag *pendingFlag = [[OctagonPendingFlag alloc] initWithFlag:SOSFlagTriggerBackup
[self.stateMachine handlePendingFlag:pendingFlag];
}
-- (void)addRingUpdateFlag {
- OctagonPendingFlag *pendingFlag = [[OctagonPendingFlag alloc] initWithFlag:SOSFlagTriggerRingUpdate
- conditions:OctagonPendingConditionsDeviceUnlocked];
- [self.stateMachine handlePendingFlag:pendingFlag];
-}
-
-//Mark: -- Set up state for state machine
-
-
- (void)triggerBackupForPeers:(NSArray<NSString*>*)backupPeers
{
- NSMutableSet *pending = [NSMutableSet set];
- if (backupPeers) {
- [pending addObjectsFromArray:backupPeers];
- }
-
- WEAKIFY(self);
- dispatch_async(self.stateMachineQueue, ^{
- STRONGIFY(self);
-
+ dispatch_sync(self.stateMachineQueue, ^{
SOSAccountConfiguration *storage = self.accountConfiguration.storage;
+ NSMutableSet *pending = [NSMutableSet set];
if (storage.pendingBackupPeers) {
[pending addObjectsFromArray:storage.pendingBackupPeers];
}
+ if (backupPeers) {
+ [pending addObjectsFromArray:backupPeers];
+ }
storage.pendingBackupPeers = [[pending allObjects] mutableCopy];
[self.accountConfiguration setStorage:storage];
[self.performBackups trigger];
- secnotice("sos-sm", "trigger backup for peers: %@ at %@",
- backupPeers, self.performBackups.nextFireTime);
+ secnotice("sos-sm", "trigger backup for peers: %@", backupPeers);
});
}
-- (void)triggerRingUpdate
-{
-
- WEAKIFY(self);
- dispatch_async(self.stateMachineQueue, ^{
- STRONGIFY(self);
- SOSAccountConfiguration *storage = self.accountConfiguration.storage;
- storage.ringUpdateFlag = YES;
- [self.accountConfiguration setStorage:storage];
- [self.performRingUpdates trigger];
- secnotice("sos-sm", "trigger ring update at %@",
- self.performRingUpdates.nextFireTime);
- });
-}
-
-//Mark: -- State machine and opertions
-
-- (OctagonStateTransitionOperation *)performBackup {
-
- WEAKIFY(self);
- return [OctagonStateTransitionOperation named:@"perform-backup-state"
- intending:SOSStateReady
- errorState:SOSStateError
- withBlockTakingSelf:^void(OctagonStateTransitionOperation * _Nonnull op) {
- STRONGIFY(self);
- SOSAccountConfiguration *storage = self.accountConfiguration.storage;
-
- secnotice("sos-sm", "performing backup for %@", storage.pendingBackupPeers);
-
- if (storage.pendingBackupPeers.count) {
- SOSCCRequestSyncWithBackupPeerList((__bridge CFArrayRef)storage.pendingBackupPeers);
- [storage clearPendingBackupPeers];
- }
- [self.accountConfiguration setStorage:storage];
-
- op.nextState = SOSStateReady;
- }];
-}
-
-- (OctagonStateTransitionOperation *)performRingUpdate {
-
- WEAKIFY(self);
- return [OctagonStateTransitionOperation named:@"perform-ring-update"
- intending:SOSStateReady
- errorState:SOSStateError
- withBlockTakingSelf:^void(OctagonStateTransitionOperation * _Nonnull op) {
- STRONGIFY(self);
-
- SOSAccountConfiguration *storage = self.accountConfiguration.storage;
- storage.ringUpdateFlag = NO;
- [self.accountConfiguration setStorage:storage];
-
- [self performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
-
- [self _onQueueRecordRetiredPeersInCircle];
-
- SOSAccountEnsureRecoveryRing(self);
- [self _onQueueEnsureInBackupRings];
-
- CFErrorRef localError = NULL;
- if(![self.circle_transport flushChanges:&localError]){
- secerror("flush circle failed %@", localError);
- }
- CFReleaseNull(localError);
-
- if(!SecCKKSTestDisableSOS()) {
- SOSAccountNotifyEngines(self);
- }
- }];
-
- op.nextState = SOSStateReady;
- }];
-
-}
-
-
- (CKKSResultOperation<OctagonStateTransitionOperationProtocol>* _Nullable)_onqueueNextStateMachineTransition:(OctagonState*)currentState
flags:(nonnull OctagonFlags *)flags
pendingFlags:(nonnull id<OctagonStateOnqueuePendingFlagHandler>)pendingFlagHandler
{
dispatch_assert_queue(self.stateMachineQueue);
- secnotice("sos-sm", "Entering state: %@ [flags: %@]", currentState, flags);
+ WEAKIFY(self);
+
+ secnotice("sos-sm", "currentState: %@ [flags: %@]", currentState, flags);
if ([currentState isEqualToString:SOSStateReady]) {
if([flags _onqueueContains:SOSFlagTriggerBackup]) {
return [OctagonStateTransitionOperation named:@"perform-backup-flag"
entering:SOSStatePerformBackup];
}
-
- if ([flags _onqueueContains:SOSFlagTriggerRingUpdate]) {
- [flags _onqueueRemoveFlag:SOSFlagTriggerRingUpdate];
- return [OctagonStateTransitionOperation named:@"perform-ring-update-flag"
- entering:SOSStatePerformRingUpdate];
- }
+ secnotice("sos-sm", "Entering state ready");
return nil;
-
} else if ([currentState isEqualToString:SOSStateError]) {
+ secnotice("sos-sm", "Entering state error");
return nil;
- } else if ([currentState isEqualToString:SOSStatePerformRingUpdate]) {
- return [self performRingUpdate];
-
} else if ([currentState isEqualToString:SOSStatePerformBackup]) {
- return [self performBackup];
+
+ return [OctagonStateTransitionOperation named:@"perform-backup-state"
+ intending:SOSStateReady
+ errorState:SOSStateError
+ withBlockTakingSelf:^void(OctagonStateTransitionOperation * _Nonnull op) {
+ STRONGIFY(self);
+ SOSAccountConfiguration *storage = self.accountConfiguration.storage;
+
+ secnotice("sos-sm", "performing backup for %@", storage.pendingBackupPeers);
+
+ if (storage.pendingBackupPeers.count) {
+ SOSCCRequestSyncWithBackupPeerList((__bridge CFArrayRef)storage.pendingBackupPeers);
+ [storage clearPendingBackupPeers];
+ }
+ [self.accountConfiguration setStorage:storage];
+
+ op.nextState = SOSStateReady;
+ }];
}
return nil;
return bskb;
}
-
-bool SOSAccountIsLastBackupPeer(SOSAccount* account, CFErrorRef *error) {
- __block bool retval = false;
- SOSPeerInfoRef pi = account.peerInfo;
-
- if(![account isInCircle:error]) {
- return retval;
- }
-
- if(!SOSPeerInfoHasBackupKey(pi))
- return retval;
-
- SOSCircleRef circle = [account.trust getCircle:error];
-
- if(SOSCircleCountValidSyncingPeers(circle, SOSAccountGetTrustedPublicCredential(account, error)) == 1){
- retval = true;
- return retval;
- }
- // We're in a circle with more than 1 ActiveValidPeers - are they in the backups?
- SOSAccountForEachBackupView(account, ^(const void *value) {
- CFStringRef viewname = (CFStringRef) value;
- SOSBackupSliceKeyBagRef keybag = SOSAccountBackupSliceKeyBagForView(account, viewname, error);
- require_quiet(keybag, inner_errOut);
- retval |= ((SOSBSKBCountPeers(keybag) == 1) && (SOSBSKBPeerIsInKeyBag(keybag, pi)));
- inner_errOut:
- CFReleaseNull(keybag);
- });
-
- return retval;
-}
message AccountConfiguration {
repeated string pendingBackupPeers = 1;
- optional bool ringUpdateFlag = 2;
}
static NSString *SOSUserCredentialAccount = @"SOSUserCredential";
static NSString *SOSUserCredentialAccessGroup = @"com.apple.security.sos-usercredential";
-__unused static void SOSAccountDeleteStashedAccountKey(SOSAccount* account)
-{
- NSString *dsid = (__bridge NSString *)SOSAccountGetValue(account, kSOSDSIDKey, NULL);
- if (dsid == NULL)
- return;
-
- NSDictionary * attributes = @{
- (__bridge id)kSecClass : (__bridge id)kSecClassInternetPassword,
- (__bridge id)kSecAttrAccount : SOSUserCredentialAccount,
- (__bridge id)kSecAttrServer : dsid,
- (__bridge id)kSecAttrAccessGroup : SOSUserCredentialAccessGroup,
- };
- (void)SecItemDelete((__bridge CFDictionaryRef)attributes);
-}
-
void SOSAccountStashAccountKey(SOSAccount* account)
{
OSStatus status;
});
}
-CFDataRef SOSAccountCopyAccountStateFromKeychain(CFErrorRef *error){
- CFMutableDictionaryRef query = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- CFTypeRef result = NULL;
- CFDictionaryAddValue(query, kSecClass, kSecClassGenericPassword);
- CFDictionaryAddValue(query, kSecAttrAccessGroup, CFSTR("com.apple.security.sos"));
- CFDictionaryAddValue(query, kSecAttrAccessible, CFSTR("dku"));
- CFDictionaryAddValue(query, kSecAttrTombstone, kCFBooleanFalse);
- CFDictionaryAddValue(query, kSecAttrSynchronizable, kCFBooleanFalse);
- CFDictionaryAddValue(query, kSecReturnData, kCFBooleanTrue);
-
- SecItemCopyMatching(query, &result);
-
- if(!isData(result)){
- SOSErrorCreate(kSOSErrorUnexpectedType, error, NULL, CFSTR("Expected CFData, got: %@"), result);
- CFReleaseNull(result);
- return NULL;
- }
- return result;
-}
-
-bool SOSAccountDeleteAccountStateFromKeychain(CFErrorRef *error){
- CFMutableDictionaryRef query = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- bool result = false;
- CFDictionaryAddValue(query, kSecClass, kSecClassGenericPassword);
- CFDictionaryAddValue(query, kSecAttrAccessGroup, CFSTR("com.apple.security.sos"));
- CFDictionaryAddValue(query, kSecAttrAccessible, CFSTR("dku"));
- CFDictionaryAddValue(query, kSecAttrTombstone, kCFBooleanFalse);
- CFDictionaryAddValue(query, kSecAttrSynchronizable, kCFBooleanFalse);
-
- result = SecItemDelete(query);
- return result;
-}
-
-CFDataRef SOSAccountCopyEngineStateFromKeychain(CFErrorRef *error){
- CFMutableDictionaryRef query = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- CFTypeRef result = NULL;
- CFDictionaryAddValue(query, kSecClass, kSecClassGenericPassword);
- CFDictionaryAddValue(query, kSecAttrAccount, CFSTR("engine-state"));
- CFDictionaryAddValue(query, kSecAttrAccessGroup, CFSTR("com.apple.security.sos"));
- CFDictionaryAddValue(query, kSecAttrAccessible, CFSTR("dk"));
- CFDictionaryAddValue(query, kSecAttrService, CFSTR("SOSDataSource-ak"));
- CFDictionaryAddValue(query, kSecAttrTombstone, kCFBooleanFalse);
- CFDictionaryAddValue(query, kSecAttrSynchronizable, kCFBooleanFalse);
- CFDictionaryAddValue(query, kSecReturnData, kCFBooleanTrue);
-
- SecItemCopyMatching(query, &result);
-
- if(!isData(result)){
- SOSErrorCreate(kSOSErrorUnexpectedType, error, NULL, CFSTR("Expected CFData, got: %@"), result);
- CFReleaseNull(result);
- return NULL;
- }
- return result;
-}
-
-bool SOSAccountDeleteEngineStateFromKeychain(CFErrorRef *error){
- CFMutableDictionaryRef query = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- bool result = false;
- CFDictionaryAddValue(query, kSecClass, kSecClassGenericPassword);
- CFDictionaryAddValue(query, kSecAttrAccount, CFSTR("engine-state"));
- CFDictionaryAddValue(query, kSecAttrAccessGroup, CFSTR("com.apple.security.sos"));
- CFDictionaryAddValue(query, kSecAttrAccessible, CFSTR("dk"));
- CFDictionaryAddValue(query, kSecAttrService, CFSTR("SOSDataSource-ak"));
- CFDictionaryAddValue(query, kSecAttrTombstone, kCFBooleanFalse);
- CFDictionaryAddValue(query, kSecAttrSynchronizable, kCFBooleanFalse);
-
- result = SecItemDelete(query);
- return result;
-}
-
-
CFArrayRef SOSAccountCopyActivePeers(SOSAccount* account, CFErrorRef *error) {
return SOSAccountCopySortedPeerArray(account, error, ^(SOSCircleRef circle, CFMutableArrayRef appendPeersTo) {
SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
}
CFReleaseNull(oldPI);
+ SOSAccountEnsureRecoveryRing(account);
[account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
- SOSAccountEnsureRecoveryRing(account);
-
secnotice("circleop", "Setting account.key_interests_need_updating to true in SOSAccountCreateFromDER");
account.key_interests_need_updating = true;
}];
-(id) init NS_UNAVAILABLE;
-(id) initWithGestalt:(CFDictionaryRef)gestalt factory:(SOSDataSourceFactoryRef)factory;
-- (void)startStateMachine;
+//- (void)startStateMachine;
void SOSAccountAddSyncablePeerBlock(SOSAccount* a,
CFStringRef ds_name,
#if OCTAGON
- (void)triggerBackupForPeers:(NSArray<NSString*>*)backupPeer;
-- (void)triggerRingUpdate;
#endif
CF_RETURNS_RETAINED
CFDictionaryRef SOSAccountHandleRetirementMessages(SOSAccount* account, CFDictionaryRef circle_retirement_messages, CFErrorRef *error);
+void SOSAccountRecordRetiredPeersInCircle(SOSAccount* account);
+
bool SOSAccountHandleUpdateCircle(SOSAccount* account,
SOSCircleRef prospective_circle,
bool writeUpdate,
bool sosAccountLeaveCircle(SOSAccount* account, SOSCircleRef circle, CFErrorRef* error);
bool sosAccountLeaveCircleWithAnalytics(SOSAccount* account, SOSCircleRef circle, NSData* parentData, CFErrorRef* error);
-bool sosAccountLeaveRing(SOSAccount* account, SOSRingRef ring, CFErrorRef* error);
bool SOSAccountForEachRing(SOSAccount* account, SOSRingRef (^action)(CFStringRef name, SOSRingRef ring));
bool SOSAccountUpdateBackUp(SOSAccount* account, CFStringRef viewname, CFErrorRef *error);
void SOSAccountEnsureRecoveryRing(SOSAccount* account);
+bool SOSAccountEnsureInBackupRings(SOSAccount* account);
bool SOSAccountEnsurePeerRegistration(SOSAccount* account, CFErrorRef *error);
CFTypeRef SOSAccountGetValue(SOSAccount* account, CFStringRef key, CFErrorRef *error);
bool SOSAccountAddEscrowToPeerInfo(SOSAccount* account, SOSFullPeerInfoRef myPeer, CFErrorRef *error);
-bool SOSAccountAddEscrowRecords(SOSAccount* account, CFStringRef dsid, CFDictionaryRef record, CFErrorRef *error);
void SOSAccountRemoveRing(SOSAccount* a, CFStringRef ringName);
SOSRingRef SOSAccountCopyRingNamed(SOSAccount* a, CFStringRef ringName, CFErrorRef *error);
-SOSRingRef SOSAccountRingCreateForName(SOSAccount* a, CFStringRef ringName, CFErrorRef *error);
bool SOSAccountUpdateRingFromRemote(SOSAccount* account, SOSRingRef newRing, CFErrorRef *error);
bool SOSAccountUpdateRing(SOSAccount* account, SOSRingRef newRing, CFErrorRef *error);
bool SOSAccountRemoveBackupPeers(SOSAccount* account, CFArrayRef peerIDs, CFErrorRef *error);
-bool SOSAccountResetRing(SOSAccount* account, CFStringRef ringName, CFErrorRef *error);
-bool SOSAccountCheckPeerAvailability(SOSAccount* account, CFErrorRef *error);
bool SOSAccountUpdateNamedRing(SOSAccount* account, CFStringRef ringName, CFErrorRef *error,
SOSRingRef (^create)(CFStringRef ringName, CFErrorRef *error),
SOSRingRef (^copyModified)(SOSRingRef existing, CFErrorRef *error));
// Security tool test/debug functions
//
bool SOSAccountPostDebugScope(SOSAccount* account, CFTypeRef scope, CFErrorRef *error);
-CFDataRef SOSAccountCopyAccountStateFromKeychain(CFErrorRef *error);
-bool SOSAccountDeleteAccountStateFromKeychain(CFErrorRef *error);
-CFDataRef SOSAccountCopyEngineStateFromKeychain(CFErrorRef *error);
-bool SOSAccountDeleteEngineStateFromKeychain(CFErrorRef *error);
-bool SOSAccountIsNew(SOSAccount* account, CFErrorRef *error);
bool SOSAccountCheckForAlwaysOnViews(SOSAccount* account);
// UUID, no setter just getter and ensuring value.
void SOSAccountEnsureUUID(SOSAccount* account);
#endif
bool SOSAccountCleanupAllKVSKeys(SOSAccount* account, CFErrorRef* error);
-bool SOSAccountPopulateKVSWithBadKeys(SOSAccount* account, CFErrorRef* error);
@end
}
void SOSAccountEnsureRecoveryRing(SOSAccount* account) {
- dispatch_assert_queue(account.queue);
-
static SOSRecoveryKeyBagRef oldRingRKBG = NULL;
SOSRecoveryKeyBagRef acctRKBG = SOSAccountCopyRecoveryKeyBagEntry(kCFAllocatorDefault, account, NULL);
if(!CFEqualSafe(acctRKBG, oldRingRKBG)) {
const CFStringRef kSOSRingKey = CFSTR("trusted_rings");
-static CFSetRef allCurrentRings(void) {
- static dispatch_once_t dot;
- static CFMutableSetRef allRings = NULL;
- dispatch_once(&dot, ^{
- allRings = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
- CFSetAddValue(allRings, kSOSRingCircleV2);
- CFSetAddValue(allRings, kSOSRingKeychainV0);
- CFSetAddValue(allRings, kSOSRingPCSHyperion);
- CFSetAddValue(allRings, kSOSRingPCSBladerunner);
- CFSetAddValue(allRings, kSOSRingPCSLiverpool);
- CFSetAddValue(allRings, kSOSRingPCSEscrow);
- CFSetAddValue(allRings, kSOSRingPCSPianoMover);
- CFSetAddValue(allRings, kSOSRingPCSNotes);
- CFSetAddValue(allRings, kSOSRingPCSFeldspar);
- CFSetAddValue(allRings, kSOSRingAppleTV);
- CFSetAddValue(allRings, kSOSRingHomeKit);
- CFSetAddValue(allRings, kSOSRingWifi);
- CFSetAddValue(allRings, kSOSRingPasswords);
- CFSetAddValue(allRings, kSOSRingCreditCards);
- CFSetAddValue(allRings, kSOSRingiCloudIdentity);
- CFSetAddValue(allRings, kSOSRingOtherSyncable);
- });
- return allRings;
-}
-
typedef struct ringDef_t {
CFStringRef name;
SOSRingType ringType;
bool dropWhenLeaving;
} ringDef, *ringDefPtr;
-static ringDefPtr getRingDef(CFStringRef ringName) {
- static ringDef retval;
-
- // Defaults
- retval.name = ringName;
- retval.dropWhenLeaving = true;
- retval.ringType = kSOSRingEntropyKeyed;
-
-
- if(CFSetContainsValue(allCurrentRings(), ringName)) {
- retval.ringType = kSOSRingBase;
- retval.dropWhenLeaving = false;
- } else {
- retval.ringType = kSOSRingBackup;
- retval.dropWhenLeaving = false;
- }
- return &retval;
-}
-
-__unused static inline void SOSAccountRingForEachRingMatching(SOSAccount* a, void (^action)(SOSRingRef ring), bool (^condition)(SOSRingRef ring)) {
- CFSetRef allRings = allCurrentRings();
- CFSetForEach(allRings, ^(const void *value) {
- CFStringRef ringName = (CFStringRef) value;
- SOSRingRef ring = [a.trust copyRing:ringName err:NULL];
- if (condition(ring)) {
- action(ring);
- }
- CFReleaseNull(ring);
- });
-}
-
-
-
-
-
static void SOSAccountSetRings(SOSAccount* a, CFMutableDictionaryRef newrings){
SOSAccountTrustClassic *trust = a.trust;
[trust.expansion setObject:(__bridge NSMutableDictionary*)newrings forKey:(__bridge NSString* _Nonnull)(kSOSRingKey)];
return found;
}
-/* Unused? */
-SOSRingRef SOSAccountRingCreateForName(SOSAccount* a, CFStringRef ringName, CFErrorRef *error) {
- ringDefPtr rdef = getRingDef(ringName);
- if(!rdef) return NULL;
- SOSRingRef retval = SOSRingCreate(rdef->name, (__bridge CFStringRef) a.peerID, rdef->ringType, error);
- return retval;
-}
-
bool SOSAccountUpdateRingFromRemote(SOSAccount* account, SOSRingRef newRing, CFErrorRef *error) {
require_quiet(SOSAccountHasPublicKey(account, error), errOut);
}
if(self.account.circle_rings_retirements_need_attention){
- self.account.circle_rings_retirements_need_attention = false;
-#if OCTAGON
- [self.account triggerRingUpdate];
-#endif
+ SOSAccountRecordRetiredPeersInCircle(self.account);
+
+ SOSAccountEnsureRecoveryRing(self.account);
+ SOSAccountEnsureInBackupRings(self.account);
+
+ CFErrorRef localError = NULL;
+ if(![self.account.circle_transport flushChanges:&localError]){
+ secerror("flush circle failed %@", localError);
+ }
+ CFReleaseSafe(localError);
+
+ notifyEngines = true;
}
if (notifyEngines) {
SOSUpdateKeyInterest(self.account);
}
+ self.account.circle_rings_retirements_need_attention = false;
self.account.engine_peer_state_needs_repair = false;
[self.account flattenToSaveBlock];
void SOSAccountNotifyEngines(SOSAccount* account)
{
- dispatch_assert_queue(account.queue);
-
SOSAccountTrustClassic *trust = account.trust;
SOSFullPeerInfoRef identity = trust.fullPeerInfo;
SOSCircleRef circle = trust.trustedCircle;
+++ /dev/null
-/*
- * Copyright (c) 2016 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-//
-// SOSBackupInformation.h
-// Security
-//
-
-#ifndef SOSBackupInformation_h
-#define SOSBackupInformation_h
-
-#include <CoreFoundation/CoreFoundation.h>
-#import "keychain/SecureObjectSync/SOSAccountTransaction.h"
-
-enum {
- noError = 0,
- noTxnorAcct,
- noAlloc,
- noTrustedPubKey,
- noBSKBs,
-};
-
-extern const CFStringRef kSOSBkpInfoStatus;
-extern const CFStringRef kSOSBkpInfoBSKB;
-extern const CFStringRef kSOSBkpInfoRKBG;
-
-CFDictionaryRef SOSBackupInformation(SOSAccountTransaction* txn, CFErrorRef *error);
-
-#endif /* SOSBackupInformation_h */
+++ /dev/null
-/*
- * Copyright (c) 2016 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-//
-// SOSBackupInformation.c
-// Security
-//
-
-#include "SOSBackupInformation.h"
-#include "SOSAccountPriv.h"
-#include <CoreFoundation/CFNumber.h>
-#include <utilities/SecCFWrappers.h>
-
-const CFStringRef kSOSBkpInfoStatus = CFSTR("BkpInfoStatus");
-const CFStringRef kSOSBkpInfoBSKB = CFSTR("BkpInfoBSKB");
-const CFStringRef kSOSBkpInfoRKBG = CFSTR("BkpInfoRKBG");
-
-CFDictionaryRef SOSBackupInformation(SOSAccountTransaction* txn, CFErrorRef *error) {
- CFNumberRef status = NULL;
- int ibkpInfoStatus;
- __block bool havebskbcontent = false;
- SOSAccount *a = txn.account;
-
- CFMutableDictionaryRef retval = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- require_action_quiet(txn && a, errOut, ibkpInfoStatus = noTxnorAcct);
- require_action_quiet(retval, errOut, ibkpInfoStatus = noAlloc);
- require_action_quiet(txn, errOut, ibkpInfoStatus = noTxnorAcct);
-
- require_action_quiet(a.accountKey && a.accountKeyIsTrusted, errOut, ibkpInfoStatus = noTrustedPubKey);
- CFMutableDictionaryRef bskbders = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- SOSAccountForEachRing(a, ^SOSRingRef(CFStringRef name, SOSRingRef ring) {
- if(SOSRingGetType(ring) == kSOSRingBackup) {
- CFDataRef bskbder = SOSRingGetPayload(ring, NULL);
- if(bskbder) CFDictionaryAddValue(bskbders, name, bskbder);
- havebskbcontent = true;
- } else if(SOSRingGetType(ring) == kSOSRingRecovery) {
- CFDataRef rkbgder = SOSRingGetPayload(ring, NULL);
- if(rkbgder) CFDictionaryAddValue(retval, kSOSBkpInfoRKBG, rkbgder);
- }
- return NULL; // we're reporting - never changing the ring
- });
- if(havebskbcontent) {
- ibkpInfoStatus = noError;
- CFDictionaryAddValue(retval, kSOSBkpInfoBSKB, bskbders);
- } else {
- ibkpInfoStatus = noBSKBs;
- }
- CFReleaseNull(bskbders);
-
-errOut:
- status = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &ibkpInfoStatus);
- CFDictionaryAddValue(retval, kSOSBkpInfoStatus, status);
- CFReleaseNull(status);
- return retval;
-}
return SOSCircleHasDegenerateGeneration(deGenCircle) && SOSCircleIsEmpty(deGenCircle);
}
-
-__unused static inline bool SOSCircleIsResignOffering(SOSCircleRef circle, SecKeyRef pubkey) {
- return SOSCircleCountActiveValidPeers(circle, pubkey) == 1;
-}
-
static inline SOSConcordanceStatus GetSignersStatus(SOSCircleRef signers_circle, SOSCircleRef status_circle,
SecKeyRef user_pubKey, SOSPeerInfoRef exclude, CFErrorRef *error) {
CFStringRef excluded_id = exclude ? SOSPeerInfoGetPeerID(exclude) : NULL;
}
CFStringRef SOSCircleGetName(SOSCircleRef circle) {
- assert(circle);
- assert(circle->name);
+ if(!circle || !circle->name) {
+ return NULL;
+ }
return circle->name;
}
bool SOSCCWaitForInitialSync(CFErrorRef* error);
bool SOSCCWaitForInitialSyncWithAnalytics(CFDataRef parentEvent, CFErrorRef* error);
-/*!
- @function SOSCCCopyYetToSyncViewsList
- @abstract returns views not yet synced
- @param error error to fill in if we have one
- @return List of view names that we haven't synced yet.
- */
-CFArrayRef SOSCCCopyYetToSyncViewsList(CFErrorRef* error);
-
/*!
@function SOSCCCanAuthenticate
@abstract Determines whether we currently have valid credentials to authenticate a circle operation.
bool SOSCCRequestToJoinCircleAfterRestore(CFErrorRef* error);
bool SOSCCRequestToJoinCircleAfterRestoreWithAnalytics(CFDataRef parentEvent, CFErrorRef* error);
-/*!
- @function SOSCCRequestEnsureFreshParameters
- @abstract function to help debug problems with EnsureFreshParameters
- @param error What went wrong if we tried to refresh parameters
- @result true if we successfully retrieved fresh parameters. False if we failed.
-*/
-bool SOSCCRequestEnsureFreshParameters(CFErrorRef* error);
-
/*!
@function SOSCCAccountSetToNew
@abstract reset account to new
*/
bool SOSCCBailFromCircle_BestEffort(uint64_t limit_in_seconds, CFErrorRef* error);
-/*!
- @function SOSCCSignedOut
- @abstract Attempts to publish a retirement ticket for the current device.
- @param immediate If we should remove the device immediately or to leave the circle with best effort.
- @param error What went wrong trying to remove ourselves.
- @result true if we posted the ticket. False if there was an error.
- @discussion This attempts to post a retirement ticket that should
- result in other devices removing this device from the circle. It does so
- with a 5 second timeout or immediately.
- */
-bool SOSCCSignedOut(bool immediate, CFErrorRef* error);
-
/*!
@function SOSCCCopyApplicantPeerInfo
@abstract Get the list of peers wishing admittance.
bool SOSCCSetLastDepartureReason(enum DepartureReason reason, CFErrorRef *error);
-/*!
- @function SOSCCGetIncompatibilityInfo
- @abstract Returns the information (string, hopefully URL) that will lead to an explanation of why you have an incompatible circle.
- @param error What went wrong if we returned NULL.
- */
-CFStringRef SOSCCCopyIncompatibilityInfo(CFErrorRef *error);
-
-
/*
Views
*/
bool SOSCCRegisterSingleRecoverySecret(CFDataRef aks_bag, bool forV0Only, CFErrorRef *error);
-
-/*!
- @function SOSCCIsThisDeviceLastBackup
- @param error Why this query can't be accepted.
- @result true if this is the last backup device, false otherwise.
- */
-
-bool SOSCCIsThisDeviceLastBackup(CFErrorRef *error);
-
-/*!
- @function SOSCCSetEscrowRecord
- @param escrow_label Account label
- @param tries Number of attempts
- @param error What went wrong trying to set the escrow label
- @result true if we saved the escrow record, false if we had an error
- @discussion persist escrow records in the account object or the peer info
- */
-bool SOSCCSetEscrowRecord(CFStringRef escrow_label, uint64_t tries, CFErrorRef *error);
-
-/*!
- @function SOSCCCopyEscrowRecord
- @param error What went wrong trying to set the escrow label
- @result dictionary of the escrow record, false if we had an error, dictionary will be of format: [account label: <dictionary>], dictionary will contain (ex): "Burned Recovery Attempt Attestation Date" = "[2015-08-19 15:21]";
- "Burned Recovery Attempt Count" = 8;
- @discussion for debugging - retrieve the escrow record
- */
-CFDictionaryRef SOSCCCopyEscrowRecord(CFErrorRef *error);
-
/*!
@function SOSCCCopyApplication
@param error What went wrong getting the applicant peerInfo.
return result;
}
-static bool cfstring_to_error_request(enum SecXPCOperation op, CFStringRef string, CFErrorRef* error)
-{
- __block bool result = false;
-
- secdebug("sosops","enter - operation: %d", op);
- securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
- xpc_object_t xString = _CFXPCCreateXPCObjectFromCFObject(string);
- bool success = false;
- if (xString){
- xpc_dictionary_set_value(message, kSecXPCKeyString, xString);
- success = true;
- xString = nil;
- }
- return success;
- }, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
- result = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
- return result;
- });
- return result;
-}
-
-static SOSRingStatus cfstring_to_uint64_request(enum SecXPCOperation op, CFStringRef string, CFErrorRef* error)
-{
- __block bool result = false;
-
- secdebug("sosops","enter - operation: %d", op);
- securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
- xpc_object_t xString = _CFXPCCreateXPCObjectFromCFObject(string);
- bool success = false;
- if (xString){
- xpc_dictionary_set_value(message, kSecXPCKeyString, xString);
- success = true;
- xString = nil;
- }
- return success;
- }, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
- result = xpc_dictionary_get_int64(response, kSecXPCKeyResult);
- return result;
- });
- return result;
-}
-
-static CFStringRef simple_cfstring_error_request(enum SecXPCOperation op, CFErrorRef* error)
-{
- __block CFStringRef result = NULL;
-
- secdebug("sosops","enter - operation: %d", op);
- securityd_send_sync_and_do(op, error, NULL, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
- const char *c_string = xpc_dictionary_get_string(response, kSecXPCKeyResult);
-
- if (c_string) {
- result = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8*)c_string, strlen(c_string), kCFStringEncodingUTF8, false);
- }
-
- return c_string != NULL;
- });
- return result;
-}
-
static bool simple_bool_error_request(enum SecXPCOperation op, CFErrorRef* error)
{
__block bool result = false;
return result;
}
-
-static bool escrow_to_bool_error_request(enum SecXPCOperation op, CFStringRef escrow_label, uint64_t tries, CFErrorRef* error)
-{
- __block bool result = false;
-
- securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
-
- bool success = false;
- xpc_object_t xEscrowLabel = _CFXPCCreateXPCObjectFromCFObject(escrow_label);
- if (xEscrowLabel){
- xpc_dictionary_set_value(message, kSecXPCKeyEscrowLabel, xEscrowLabel);
- success = true;
- xEscrowLabel = nil;
- }
- if(tries){
- xpc_dictionary_set_int64(message, kSecXPCKeyTriesLabel, tries);
- success = true;
- }
-
- return success;
- }, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
- result = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
- return result;
- });
- return result;
-}
-
static CF_RETURNS_RETAINED CFArrayRef simple_array_error_request(enum SecXPCOperation op, CFErrorRef* error)
{
__block CFArrayRef result = NULL;
return result;
}
-static CFDictionaryRef strings_to_dictionary_error_request(enum SecXPCOperation op, CFErrorRef* error)
-{
- __block CFDictionaryRef result = NULL;
-
- secdebug("sosops","enter - operation: %d", op);
-
- if (securityd_send_sync_and_do(op, error, NULL, ^bool(xpc_object_t response, CFErrorRef *error) {
- xpc_object_t temp_result = xpc_dictionary_get_value(response, kSecXPCKeyResult);
- if(temp_result)
- result = _CFXPCCreateCFObjectFromXPCObject(temp_result);
- return result != NULL;
- })){
-
- if (!isDictionary(result)) {
- SOSErrorCreate(kSOSErrorUnexpectedType, error, NULL, CFSTR("Expected dictionary, got: %@"), result);
- CFReleaseNull(result);
- }
-
- }
- return result;
-}
-
static int simple_int_error_request(enum SecXPCOperation op, CFErrorRef* error)
{
__block int result = 0;
CFErrorRef* error)
{
__block bool result = false;
-
+
securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
xpc_dictionary_set_uint64(message, kSecXPCLimitInMinutes, number);
return true;
result = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
return result;
});
-
- return result;
-}
-
-static bool cfstring_and_cfdata_to_cfdata_cfdata_error_request(enum SecXPCOperation op, CFStringRef viewName, CFDataRef input, CFDataRef* data, CFDataRef* data2, CFErrorRef* error) {
- secdebug("sosops", "enter - operation: %d", op);
- __block bool result = false;
- securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
- xpc_object_t xviewname = _CFXPCCreateXPCObjectFromCFObject(viewName);
- xpc_object_t xinput = _CFXPCCreateXPCObjectFromCFObject(input);
- bool success = false;
- if (xviewname && xinput){
- xpc_dictionary_set_value(message, kSecXPCKeyViewName, xviewname);
- xpc_dictionary_set_value(message, kSecXPCData, xinput);
- success = true;
- xviewname = nil;
- xinput = nil;
- }
- return success;
- }, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
- result = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
-
- xpc_object_t temp_result = xpc_dictionary_get_value(response, kSecXPCData);
- if ((NULL != temp_result) && data) {
- *data = _CFXPCCreateCFObjectFromXPCObject(temp_result);
- }
- temp_result = xpc_dictionary_get_value(response, kSecXPCKeyKeybag);
- if ((NULL != temp_result) && data2) {
- *data2 = _CFXPCCreateCFObjectFromXPCObject(temp_result);
- }
-
- return result;
- });
-
- if (data &&!isData(*data)) {
- SOSErrorCreate(kSOSErrorUnexpectedType, error, NULL, CFSTR("Expected CFData, got: %@"), *data);
- }
- if (data2 &&!isData(*data2)) {
- SOSErrorCreate(kSOSErrorUnexpectedType, error, NULL, CFSTR("Expected CFData, got: %@"), *data2);
- }
return result;
}
}
-bool SOSCCAccountIsNew(CFErrorRef *error)
-{
- sec_trace_enter_api(NULL);
- sec_trace_return_bool_api(^{
- do_if_registered(soscc_AccountIsNew, error);
-
- return simple_bool_error_request(kSecXPCOpAccountIsNew, error);
- }, NULL)
-}
-
bool SOSCCWaitForInitialSyncWithAnalytics(CFDataRef parentEvent, CFErrorRef* error)
{
sec_trace_enter_api(NULL);
}, NULL)
}
-CFArrayRef SOSCCCopyYetToSyncViewsList(CFErrorRef* error)
-{
- sec_trace_enter_api(NULL);
- sec_trace_return_api(CFArrayRef, ^{
- do_if_registered(soscc_CopyYetToSyncViewsList, error);
-
- return simple_array_error_request(kSecXPCOpCopyYetToSyncViews, error);
- }, NULL)
-}
-
-bool SOSCCRequestEnsureFreshParameters(CFErrorRef* error)
-{
- sec_trace_enter_api(NULL);
- sec_trace_return_bool_api(^{
- do_if_registered(soscc_RequestEnsureFreshParameters, error);
-
- return simple_bool_error_request(kSecXPCOpRequestEnsureFreshParameters, error);
- }, NULL)
-}
-
-CFStringRef SOSCCGetAllTheRings(CFErrorRef *error){
- sec_trace_enter_api(NULL);
- sec_trace_return_api(CFStringRef, ^{
- do_if_registered(soscc_GetAllTheRings, error);
-
-
- return simple_cfstring_error_request(kSecXPCOpGetAllTheRings, error);
- }, NULL)
-}
-bool SOSCCApplyToARing(CFStringRef ringName, CFErrorRef* error)
-{
- sec_trace_enter_api(NULL);
- sec_trace_return_bool_api(^{
- do_if_registered(soscc_ApplyToARing, ringName, error);
-
- return cfstring_to_error_request(kSecXPCOpApplyToARing, ringName, error);
- }, NULL)
-}
-
-bool SOSCCWithdrawlFromARing(CFStringRef ringName, CFErrorRef* error)
-{
- sec_trace_enter_api(NULL);
- sec_trace_return_bool_api(^{
- do_if_registered(soscc_WithdrawlFromARing, ringName, error);
-
- return cfstring_to_error_request(kSecXPCOpWithdrawlFromARing, ringName, error);
- }, NULL)
-}
-
-SOSRingStatus SOSCCRingStatus(CFStringRef ringName, CFErrorRef* error)
-{
- sec_trace_enter_api(NULL);
- sec_trace_return_api(SOSRingStatus, ^{
- do_if_registered(soscc_RingStatus, ringName, error);
-
- return cfstring_to_uint64_request(kSecXPCOpRingStatus, ringName, error);
- }, CFSTR("SOSCCStatus=%d"))
-}
-
-bool SOSCCEnableRing(CFStringRef ringName, CFErrorRef* error)
-{
- sec_trace_enter_api(NULL);
- sec_trace_return_bool_api(^{
- do_if_registered(soscc_EnableRing, ringName, error);
-
- return cfstring_to_error_request(kSecXPCOpEnableRing, ringName, error);
- }, NULL)
-}
-
bool SOSCCAccountSetToNew(CFErrorRef *error)
{
secwarning("SOSCCAccountSetToNew called");
sec_trace_enter_api(NULL);
sec_trace_return_bool_api(^{
do_if_registered(soscc_BailFromCircle, limit_in_seconds, error);
-
+
return uint64_t_to_bool_error_request(kSecXPCOpBailFromCircle, limit_in_seconds, error);
}, NULL)
}
-bool SOSCCSignedOut(bool immediate, CFErrorRef* error)
-{
- uint64_t limit = strtoul(optarg, NULL, 10);
-
- if(immediate)
- return SOSCCRemoveThisDeviceFromCircle(error);
- else
- return SOSCCBailFromCircle_BestEffort(limit, error);
-
-}
-
CFArrayRef SOSCCCopyPeerPeerInfo(CFErrorRef* error)
{
sec_trace_enter_api(NULL);
}, CFSTR("return=%@"));
}
-CFDataRef SOSCCCopyAccountState(CFErrorRef* error)
-{
- sec_trace_enter_api(NULL);
- sec_trace_return_api(CFDataRef, ^{
- do_if_registered(soscc_CopyAccountState, error);
-
- return data_to_error_request(kSecXPCOpCopyAccountData, error);
- }, CFSTR("return=%@"));
-}
-
-bool SOSCCDeleteAccountState(CFErrorRef *error)
-{
- sec_trace_enter_api(NULL);
- sec_trace_return_api(bool, ^{
- do_if_registered(soscc_DeleteAccountState, error);
- return simple_bool_error_request(kSecXPCOpDeleteAccountData, error);
- }, NULL);
-}
-CFDataRef SOSCCCopyEngineData(CFErrorRef* error)
-{
- sec_trace_enter_api(NULL);
- sec_trace_return_api(CFDataRef, ^{
- do_if_registered(soscc_CopyEngineData, error);
-
- return data_to_error_request(kSecXPCOpCopyEngineData, error);
- }, CFSTR("return=%@"));
-}
-
-bool SOSCCDeleteEngineState(CFErrorRef *error)
-{
- sec_trace_enter_api(NULL);
- sec_trace_return_api(bool, ^{
- do_if_registered(soscc_DeleteEngineState, error);
- return simple_bool_error_request(kSecXPCOpDeleteEngineData, error);
- }, NULL);
-}
-
SOSPeerInfoRef SOSCCCopyMyPeerInfo(CFErrorRef *error)
{
sec_trace_enter_api(NULL);
}, NULL)
}
-CFStringRef SOSCCCopyIncompatibilityInfo(CFErrorRef* error) {
- sec_trace_enter_api(NULL);
- sec_trace_return_api(CFStringRef, ^{
- do_if_registered(soscc_CopyIncompatibilityInfo, error);
-
- return simple_cfstring_error_request(kSecXPCOpCopyIncompatibilityInfo, error);
- }, NULL)
-}
-
bool SOSCCProcessEnsurePeerRegistration(CFErrorRef* error){
secnotice("updates", "enter SOSCCProcessEnsurePeerRegistration");
sec_trace_enter_api(NULL);
return sosIsViewSetSyncing(sizeof(views)/sizeof(views[0]), views);
}
-
-bool SOSCCSetEscrowRecord(CFStringRef escrow_label, uint64_t tries, CFErrorRef *error ){
- secnotice("escrow", "enter SOSCCSetEscrowRecord");
- sec_trace_enter_api(NULL);
- sec_trace_return_bool_api(^{
- do_if_registered(soscc_SetEscrowRecords, escrow_label, tries, error);
-
- return escrow_to_bool_error_request(kSecXPCOpSetEscrowRecord, escrow_label, tries, error);
- }, NULL)
-}
-
-CFDictionaryRef SOSCCCopyEscrowRecord(CFErrorRef *error){
- secnotice("escrow", "enter SOSCCCopyEscrowRecord");
- sec_trace_enter_api(NULL);
- sec_trace_return_api(CFDictionaryRef, ^{
- do_if_registered(soscc_CopyEscrowRecords, error);
-
- return strings_to_dictionary_error_request(kSecXPCOpGetEscrowRecord, error);
- }, CFSTR("return=%@"))
-
-}
-
-CFDictionaryRef SOSCCCopyBackupInformation(CFErrorRef *error) {
- secnotice("escrow", "enter SOSCCCopyBackupInformation");
- sec_trace_enter_api(NULL);
- sec_trace_return_api(CFDictionaryRef, ^{
- do_if_registered(soscc_CopyBackupInformation, error);
- return strings_to_dictionary_error_request(kSecXPCOpCopyBackupInformation, error);
- }, CFSTR("return=%@"))
-}
-
-bool SOSWrapToBackupSliceKeyBagForView(CFStringRef viewName, CFDataRef input, CFDataRef* output, CFDataRef* bskbEncoded, CFErrorRef* error) {
- sec_trace_enter_api(NULL);
- sec_trace_return_bool_api(^{
- do_if_registered(sosbskb_WrapToBackupSliceKeyBagForView, viewName, input, output, bskbEncoded, error);
-
- return cfstring_and_cfdata_to_cfdata_cfdata_error_request(kSecXPCOpWrapToBackupSliceKeyBagForView, viewName, input, output, bskbEncoded, error);
- }, NULL)
-}
-
-
SOSPeerInfoRef SOSCCCopyApplication(CFErrorRef *error) {
secnotice("hsa2PB", "enter SOSCCCopyApplication applicant");
sec_trace_enter_api(NULL);
return false;
}
-bool SOSCCTestPopulateKVSWithBadKeys(CFErrorRef *error) {
- secnotice("cleanup-keys", "enter SOSCCPopulateKVSWithBadKeys");
- sec_trace_enter_api(NULL);
- sec_trace_return_bool_api(^{
- do_if_registered(soscc_SOSCCTestPopulateKVSWithBadKeys, error);
-
- return simple_bool_error_request(kSecXPCOpPopulateKVS, error);
- }, NULL)
-
- return false;
-}
-
CFDataRef SOSCCCopyCircleJoiningBlob(SOSPeerInfoRef applicant, CFErrorRef *error) {
secnotice("hsa2PB", "enter SOSCCCopyCircleJoiningBlob approver");
sec_trace_enter_api(NULL);
}, NULL)
}
-bool SOSCCIsThisDeviceLastBackup(CFErrorRef *error) {
- secnotice("peer", "enter SOSCCIsThisDeviceLastBackup");
- sec_trace_enter_api(NULL);
- sec_trace_return_bool_api(^{
- do_if_registered(soscc_IsThisDeviceLastBackup, error);
-
- return simple_bool_error_request(kSecXPCOpIsThisDeviceLastBackup, error);
- }, NULL)
-}
-
CFBooleanRef SOSCCPeersHaveViewsEnabled(CFArrayRef viewNames, CFErrorRef *error) {
secnotice("view-enabled", "enter SOSCCPeersHaveViewsEnabled");
sec_trace_enter_api(NULL);
CFStringRef SOSCCGetStatusDescription(SOSCCStatus status);
CFStringRef SOSCCGetViewResultDescription(SOSViewResultCode vrc);
bool SOSCCAccountHasPublicKey(CFErrorRef *error);
-bool SOSCCAccountIsNew(CFErrorRef *error);
/*!
@function SOSCCProcessSyncWithPeers
bool SOSCCProcessEnsurePeerRegistration(CFErrorRef* error);
-//Rings
-CFStringRef SOSCCGetAllTheRings(CFErrorRef *error);
-
-bool SOSCCApplyToARing(CFStringRef ringName, CFErrorRef* error);
-bool SOSCCWithdrawlFromARing(CFStringRef ringName, CFErrorRef* error);
-int SOSCCRingStatus(CFStringRef ringName, CFErrorRef* error); // TODO: this returns SOSRingStatus
-bool SOSCCEnableRing(CFStringRef ringName, CFErrorRef* error);
-
bool SOSCCCleanupKVSKeys(CFErrorRef *error);
*/
SOSPeerInfoRef SOSCCCopyMyPeerInfo(CFErrorRef *error);
-/*!
- @function SOSWrapToBackupSliceKeyBagForView
- @abstract Encrypts the given plaintext, and wraps the encryption key to the backup slice keybag for this view
- @param viewName The view to wrap to
- @param input The plaintext to encrypt
- @param output The ciphertext
- @param bskbEncoded The encoded backup slice keybag used to wrap the data
- @param error What went wrong if we returned false
- */
-bool SOSWrapToBackupSliceKeyBagForView(CFStringRef viewName, CFDataRef input, CFDataRef* output, CFDataRef* bskbEncoded, CFErrorRef* error);
-
//
// Security Tool calls
//
-CFDataRef SOSCCCopyAccountState(CFErrorRef* error);
-bool SOSCCDeleteAccountState(CFErrorRef *error);
-CFDataRef SOSCCCopyEngineData(CFErrorRef* error);
-bool SOSCCDeleteEngineState(CFErrorRef *error);
CFDataRef SOSCCCopyRecoveryPublicKey(CFErrorRef *error);
-CFDictionaryRef SOSCCCopyBackupInformation(CFErrorRef *error);
-bool SOSCCTestPopulateKVSWithBadKeys(CFErrorRef *error);
CFDataRef SOSCCCopyInitialSyncData(SOSInitialSyncFlags flags, CFErrorRef *error);
void SOSCCForEachEngineStateAsStringFromArray(CFArrayRef states, void (^block)(CFStringRef oneStateString));
[interface setClasses:errClasses forSelector:@selector(joinCircleWithBlob:version:complete:) argumentIndex:1 ofReply:YES];
[interface setClasses:errClasses forSelector:@selector(initialSyncCredentials:complete:) argumentIndex:1 ofReply:YES];
[interface setClasses:errClasses forSelector:@selector(importInitialSyncCredentials:complete:) argumentIndex:1 ofReply:YES];
+ [interface setClasses:errClasses forSelector:@selector(triggerSync:complete:) argumentIndex:1 ofReply:YES];
[interface setClasses:errClasses forSelector:@selector(getWatchdogParameters:) argumentIndex:1 ofReply:YES];
[interface setClasses:errClasses forSelector:@selector(setWatchdogParmeters:complete:) argumentIndex:0 ofReply:YES];
[interface setClasses:errClasses forSelector:@selector(ghostBust:complete:) argumentIndex:1 ofReply:YES];
- [interface setClasses:errClasses forSelector:@selector(rpcTriggerSync:complete:) argumentIndex:1 ofReply:YES];
- [interface setClasses:errClasses forSelector:@selector(rpcTriggerBackup:complete:) argumentIndex:0 ofReply:YES];
- [interface setClasses:errClasses forSelector:@selector(rpcTriggerRingUpdate:) argumentIndex:0 ofReply:YES];
+ [interface setClasses:errClasses forSelector:@selector(triggerBackup:complete:) argumentIndex:0 ofReply:YES];
}
[self.account importInitialSyncCredentials:items complete:complete];
}
-- (void)rpcTriggerSync:(NSArray <NSString *> *)peers complete:(void(^)(bool success, NSError *))complete
+- (void)triggerSync:(NSArray <NSString *> *)peers complete:(void(^)(bool success, NSError *))complete
{
- [self.account rpcTriggerSync:peers complete:complete];
+ if (![self checkEntitlement:(__bridge NSString *)kSecEntitlementKeychainCloudCircle]) {
+ complete(false, [NSError errorWithDomain:(__bridge NSString *)kSOSErrorDomain code:kSOSEntitlementMissing userInfo:NULL]);
+ return;
+ }
+
+ [self.account triggerSync:peers complete:complete];
}
- (void)getWatchdogParameters:(void (^)(NSDictionary* parameters, NSError* error))complete
[self.account ghostBustInfo:complete];
}
-- (void)rpcTriggerBackup:(NSArray<NSString *>* _Nullable)backupPeers complete:(void (^)(NSError *error))complete
+- (void)triggerBackup:(NSArray<NSString *>* _Nullable)backupPeers complete:(void (^)(NSError *error))complete
{
- [self.account rpcTriggerBackup:backupPeers complete:complete];
+ [self.account triggerBackup:backupPeers complete:complete];
}
-- (void)rpcTriggerRingUpdate:(void (^)(NSError *))complete {
- [self.account rpcTriggerRingUpdate:complete];
-}
@end
_SOSCCAccountGetPublicKey
_SOSCCAccountGetKeyCircleGeneration
_SOSCCAccountHasPublicKey
-_SOSCCAccountIsNew
_SOSCCAccountSetToNew
_SOSCCBailFromCircle_BestEffort
_SOSCCCanAuthenticate
-_SOSCCCopyAccountState
_SOSCCCopyApplicantPeerInfo
_SOSCCCopyApplication
-_SOSCCCopyBackupInformation
_SOSCCCopyCircleJoiningBlob
_SOSCCCopyConcurringPeerPeerInfo
-_SOSCCCopyEngineData
-_SOSCCCopyEscrowRecord
_SOSCCCopyGenerationPeerInfo
-_SOSCCCopyIncompatibilityInfo
_SOSCCCopyMyPeerInfo
_SOSCCCopyMyPeerWithNewDeviceRecoverySecret
_SOSCCCopyNotValidPeerPeerInfo
_SOSCCCopyRetirementPeerInfo
_SOSCCCopyValidPeerPeerInfo
_SOSCCCopyViewUnawarePeerInfo
-_SOSCCCopyYetToSyncViewsList
-_SOSCCDeleteAccountState
-_SOSCCDeleteEngineState
_SOSCCCleanupKVSKeys
-_SOSCCTestPopulateKVSWithBadKeys
_SOSCCForEachEngineStateAsString
_SOSCCForEachEngineStateAsStringFromArray
_SOSCCGetLastDepartureReason
_SOSCCRemovePeersFromCircleWithAnalytics
_SOSCCRemoveThisDeviceFromCircle
_SOSCCRemoveThisDeviceFromCircleWithAnalytics
-_SOSCCRequestEnsureFreshParameters
_SOSCCRequestToJoinCircle
_SOSCCRequestToJoinCircleWithAnalytics
_SOSCCRequestToJoinCircleAfterRestore
_SOSCCResetToEmptyWithAnalytics
_SOSCCResetToOffering
_SOSCCSendToPeerIsPending
-_SOSCCSetEscrowRecord
_SOSCCSetLastDepartureReason
_SOSCCSetUserCredentials
_SOSCCSetUserCredentialsAndDSID
_SOSCCSetUserCredentialsAndDSIDWithAnalytics
-_SOSCCSignedOut
_SOSCCThisDeviceIsInCircle
_SOSCCThisDeviceIsInCircleNonCached
_SOSCCTryUserCredentials
_SOSPeerInfoCopyDeviceID
_SOSPeerInfoCopyEnabledViews
_SOSPeerInfoCopyEncodedData
-_SOSPeerInfoCopyEscrowRecord
_SOSPeerInfoCopyOctagonSigningPublicKey
_SOSPeerInfoCopyOctagonEncryptionPublicKey
_SOSPeerInfoCopyPeerGestalt
_SOSPeerInfoCopyPubKey
_SOSPeerInfoCopyTransportType
_SOSPeerInfoCopyWithBackupKeyUpdate
-_SOSPeerInfoCopyWithEscrowRecordUpdate
_SOSPeerInfoCopyWithGestaltUpdate
_SOSPeerInfoCopyWithPing
_SOSPeerInfoCopyWithReplacedEscrowRecords
_SOSCircleAcceptPeerFromHSA2
_SOSFullPeerInfoUpdate
-_SOSCCGetAllTheRings
-_SOSCCApplyToARing
-_SOSCCWithdrawlFromARing
-_SOSCCRingStatus
-_SOSCCEnableRing
-_SOSCCIsThisDeviceLastBackup
-
_SOSCloudKeychainRemoveKeys
_SOSCloudTransportSetDefaultTransport
_der_sizeof_BackupSliceKeyBag
_bskbRkbgPrefix
-_SOSWrapToBackupSliceKeyBagForView
_SOSBSKBHasRecoveryKey
_SOSBSKBHasThisRecoveryKey
_SOSCircleWithdrawRequest
_debugDumpCircle
-_SOSFullPeerInfoAddEscrowRecord
_SOSFullPeerInfoCopyDeviceKey
_SOSFullPeerInfoCopyEncodedData
_SOSFullPeerInfoCopyFullPeerInfo
bool SOSFullPeerInfoUpdateBackupKey(SOSFullPeerInfoRef peer, CFDataRef backupKey, CFErrorRef* error);
-bool SOSFullPeerInfoAddEscrowRecord(SOSFullPeerInfoRef peer, CFStringRef dsid, CFDictionaryRef escrowRecord, CFErrorRef* error);
-
bool SOSFullPeerInfoReplaceEscrowRecords(SOSFullPeerInfoRef peer, CFDictionaryRef escrowRecords, CFErrorRef* error);
bool SOSFullPeerInfoUpdateToCurrent(SOSFullPeerInfoRef peer, CFSetRef minimumViews, CFSetRef excludedViews);
});
}
-bool SOSFullPeerInfoAddEscrowRecord(SOSFullPeerInfoRef peer, CFStringRef dsid, CFDictionaryRef escrowRecord, CFErrorRef* error)
-{
- return SOSFullPeerInfoUpdate(peer, error, ^SOSPeerInfoRef(SOSPeerInfoRef peer, SecKeyRef key, CFErrorRef *error) {
- return SOSPeerInfoCopyWithEscrowRecordUpdate(kCFAllocatorDefault, peer, dsid, escrowRecord, key, error);
- });
-}
-
bool SOSFullPeerInfoReplaceEscrowRecords(SOSFullPeerInfoRef peer, CFDictionaryRef escrowRecords, CFErrorRef* error)
{
return SOSFullPeerInfoUpdate(peer, error, ^SOSPeerInfoRef(SOSPeerInfoRef peer, SecKeyRef key, CFErrorRef *error) {
bool SOSPeerInfoVersionHasV2Data(SOSPeerInfoRef pi);
SOSPeerInfoRef SOSPeerInfoCopyWithGestaltUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error);
SOSPeerInfoRef SOSPeerInfoCopyWithBackupKeyUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDataRef backupKey, SecKeyRef signingKey, CFErrorRef* error);
-SOSPeerInfoRef SOSPeerInfoCopyWithEscrowRecordUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFStringRef dsid, CFDictionaryRef escrowRecord, SecKeyRef signingKey, CFErrorRef *error);
SOSPeerInfoRef SOSPeerInfoCopyWithReplacedEscrowRecords(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDictionaryRef escrowRecords, SecKeyRef signingKey, CFErrorRef *error);
//
bool SOSPeerInfoHasBackupKey(SOSPeerInfoRef peer);
CFDataRef SOSPeerInfoCopyBackupKey(SOSPeerInfoRef peer);
-CFMutableDictionaryRef SOSPeerInfoCopyEscrowRecord(SOSPeerInfoRef peer);
//
// DER Import Export
});
}
-static CFDictionaryRef SOSPeerInfoUpdateAndCopyRecord(SOSPeerInfoRef peer, CFStringRef dsid, CFDictionaryRef escrowRecord){
-
- CFMutableDictionaryRef existingEscrowRecords = SOSPeerInfoCopyEscrowRecord(peer);
-
- if(escrowRecord == NULL && existingEscrowRecords != NULL)
- {
- CFDictionaryRemoveValue(existingEscrowRecords, dsid);
- return existingEscrowRecords;
- }
-
- if(existingEscrowRecords == NULL)
- existingEscrowRecords = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-
- CFDictionarySetValue(existingEscrowRecords, dsid, escrowRecord);
-
- return existingEscrowRecords;
-}
-
-
-SOSPeerInfoRef SOSPeerInfoCopyWithEscrowRecordUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFStringRef dsid, CFDictionaryRef escrowRecord, SecKeyRef signingKey, CFErrorRef *error) {
- return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
- ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
-
- CFDictionaryRef updatedEscrowRecords = SOSPeerInfoUpdateAndCopyRecord(peerToModify, dsid, escrowRecord);
- SOSPeerInfoV2DictionarySetValue(peerToModify, sEscrowRecord, updatedEscrowRecords);
- CFReleaseNull(updatedEscrowRecords);
- return true;
- });
-}
-
SOSPeerInfoRef SOSPeerInfoCopyWithReplacedEscrowRecords(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDictionaryRef escrowRecords, SecKeyRef signingKey, CFErrorRef *error) {
return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
return SOSPeerInfoV2DictionaryCopyData(peer, sBackupKeyKey);
}
-CFMutableDictionaryRef SOSPeerInfoCopyEscrowRecord(SOSPeerInfoRef peer){
- return SOSPeerInfoV2DictionaryCopyDictionary(peer, sEscrowRecord);
-}
-
bool SOSPeerInfoHasBackupKey(SOSPeerInfoRef peer) {
CFDataRef bk = SOSPeerInfoCopyBackupKey(peer);
bool success = bk != NULL;
//AGGD
NSString* const SecSOSAggdMaxRenegotiation = @"com.apple.security.sos.otrrenegotiationmaxretries";
-__unused static int initialOTRTimeoutValue = 60; //best round trip time in KVS plus extra for good measure
static int maxRetryCount = 7; //max number of times to attempt restarting OTR negotiation
bool SOSPeerOTRTimerHaveReachedMaxRetryAllowance(SOSAccount* account, NSString* peerid){
SOSRingRef SOSRingCreate(CFStringRef name, CFStringRef myPeerID, SOSRingType type, CFErrorRef *error);
bool SOSRingResetToEmpty(SOSRingRef ring, CFStringRef myPeerID, CFErrorRef *error);
-bool SOSRingResetToOffering(SOSRingRef ring, __unused SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error);
-SOSRingStatus SOSRingDeviceIsInRing(SOSRingRef ring, CFStringRef peerID);
-bool SOSRingApply(SOSRingRef ring, SecKeyRef user_pubkey, SOSFullPeerInfoRef requestor, CFErrorRef *error);
-bool SOSRingWithdraw(SOSRingRef ring, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error);
bool SOSRingGenerationSign(SOSRingRef ring, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error);
bool SOSRingConcordanceSign(SOSRingRef ring, SOSFullPeerInfoRef requestor, CFErrorRef *error);
SOSConcordanceStatus SOSRingConcordanceTrust(SOSFullPeerInfoRef me, CFSetRef peers,
return ringTypes[type]->sosRingResetToEmpty(ring, myPeerID, error);
}
-bool SOSRingResetToOffering(SOSRingRef ring, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error) {
- SOSRingAssertStable(ring);
- SOSRingType type = SOSRingGetType(ring);
- if(!SOSRingValidType(type)){
- SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Not valid ring type"), NULL, error);
- return false;
- }
- if(!ringTypes[type]->sosRingResetToOffering){
- SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Not valid ring type"), NULL, error);
- return false;
- }
- return ringTypes[type]->sosRingResetToOffering(ring, user_privkey, requestor, error);
-}
-
-SOSRingStatus SOSRingDeviceIsInRing(SOSRingRef ring, CFStringRef peerID) {
- SOSRingAssertStable(ring);
- SOSRingType type = SOSRingGetType(ring);
- if(!(SOSRingValidType(type))){
- return kSOSRingError;
- }
- if(!(ringTypes[type]->sosRingDeviceIsInRing)){
- return kSOSRingError;
- }
- return ringTypes[type]->sosRingDeviceIsInRing(ring, peerID);
-}
-
-bool SOSRingApply(SOSRingRef ring, SecKeyRef user_pubkey, SOSFullPeerInfoRef fpi, CFErrorRef *error) {
- SOSRingAssertStable(ring);
- SOSRingType type = SOSRingGetType(ring);
- if(!(SOSRingValidType(type))){
- SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Not valid ring type"), NULL, error);
- return false;
- }
- if(!(ringTypes[type]->sosRingApply)){
- return true;
- }
- if(!(SOSPeerInfoApplicationVerify(SOSFullPeerInfoGetPeerInfo(fpi), user_pubkey, error))){
- SOSCreateError(kSOSErrorBadSignature, CFSTR("FullPeerInfo fails userkey signature check"), NULL, error);
- return false;
- }
-
- return ringTypes[type]->sosRingApply(ring, user_pubkey, fpi, error);
-}
-
-bool SOSRingWithdraw(SOSRingRef ring, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error) {
- SOSRingAssertStable(ring);
- SOSRingType type = SOSRingGetType(ring);
- if(!SOSRingValidType(type)){
- SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Not valid ring type"), NULL, error);
- return false;
- }
- if(!ringTypes[type]->sosRingWithdraw)
- return true;
-
- return ringTypes[type]->sosRingWithdraw(ring, user_privkey, requestor, error);
-}
-
bool SOSRingGenerationSign(SOSRingRef ring, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error) {
SOSRingAssertStable(ring);
SOSRingType type = SOSRingGetType(ring);
return (SOSRingRef) CFTypeAllocate(SOSRing, struct __OpaqueSOSRing, ALLOCATOR);
}
-__unused static bool addValueToDict(CFMutableDictionaryRef thedict, CFStringRef key, CFTypeRef value) {
- if(!value) return false;
- CFDictionaryAddValue(thedict, key, value);
- return true;
-}
-
static bool setValueInDict(CFMutableDictionaryRef thedict, CFStringRef key, CFTypeRef value) {
if(!value) return false;
CFDictionarySetValue(thedict, key, value);
}
-__unused static bool SOSRingSetPayload_V0(SOSRingRef ring, SecKeyRef user_privkey, CFDataRef payload, SOSFullPeerInfoRef requestor, CFErrorRef *error) {
- CFStringRef myPeerID = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(requestor));
- SecKeyRef priv = SOSFullPeerInfoCopyDeviceKey(requestor, error);
- bool retval = priv && myPeerID &&
- SOSRingSetLastModifier(ring, myPeerID) &&
- SOSRingSetPayload_Internal(ring, payload) &&
- SOSRingGenerationSign_Internal(ring, priv, error);
- if(user_privkey) SOSRingConcordanceSign_Internal(ring, user_privkey, error);
- CFReleaseNull(priv);
- return retval;
-}
-
-__unused static CFDataRef SOSRingGetPayload_V0(SOSRingRef ring, CFErrorRef *error) {
- return SOSRingGetPayload_Internal(ring);
-}
-
-
ringFuncStruct ringsV0 = {
"V0",
1,
- (void)initialSyncCredentials:(uint32_t)flags complete:(void (^)(NSArray *, NSError *))complete;
- (void)importInitialSyncCredentials:(NSArray *)items complete:(void (^)(bool success, NSError *))complete;
-- (void)rpcTriggerSync:(NSArray <NSString *> *)peers complete:(void(^)(bool success, NSError *))complete;
+- (void)triggerSync:(NSArray <NSString *> *)peers complete:(void(^)(bool success, NSError *))complete;
- (void)getWatchdogParameters:(void (^)(NSDictionary* parameters, NSError* error))complete;
- (void)setWatchdogParmeters:(NSDictionary*)parameters complete:(void (^)(NSError* error))complete;
-- (void)rpcTriggerBackup:(NSArray<NSString *>*)backupPeers complete:(void (^)(NSError *error))complete;
-- (void)rpcTriggerRingUpdate:(void (^)(NSError *error))complete;
-
+- (void)triggerBackup:(NSArray<NSString *>*)backupPeers complete:(void (^)(NSError *error))complete;
@end
#endif
"\n"
"Account/Circle Management\n"
" -a accept all applicants\n"
- " -l [reason] sign out of circle + set custom departure reason\n"
- " -q sign out of circle\n"
" -r reject all applicants\n"
- " -E ensure fresh parameters\n"
" -b device|all|single Register a backup bag - THIS RESETS BACKUPS!\n"
- " -A Apply to a ring\n"
- " -B Withdrawl from a ring\n"
- " -G Enable Ring\n"
- " -F Ring Status\n"
- " -I Dump Ring Information\n"
" -N (re-)set to new account (USE WITH CARE: device will not leave circle before resetting account!)\n"
" -O reset to offering\n"
" -R reset circle\n"
- " -X [limit] best effort bail from circle in limit seconds\n"
" -o list view unaware peers in circle\n"
" -0 boot view unaware peers from circle\n"
- " -1 grab account state from the keychain\n"
- " -2 delete account state from the keychain\n"
- " -3 grab engine state from the keychain\n"
- " -4 delete engine state from the keychain\n"
" -5 cleanup old KVS keys in KVS\n"
- " -6 [test]populate KVS with garbage KVS keys\n"
"\n"
"Circle Tools\n"
" --remove-peer SPID Remove a peer identified by the first 8 or more\n"
" wifi|passwords|creditcards|icloudidentity|othersyncable\n"
" -L list all known view and their status\n"
" -U purge private key material cache\n"
- " -V Report View Sync Status on all known clients.\n"
- " -Y Report yet to initial sync views\n"
- " -H Set escrow record.\n"
- " -J Get the escrow record.\n"
- " -M Check peer availability.\n",
+ " -V Report View Sync Status on all known clients.\n",
"Keychain Syncing controls." )
+
+
#include "keychain_sync.h"
#include "keychain_log.h"
-#include "syncbackup.h"
#include "secToolFileIO.h"
#include "secViewDisplay.h"
return false;
}
-
-static bool dumpYetToSync(CFErrorRef *error) {
- CFArrayRef yetToSyncViews = SOSCCCopyYetToSyncViewsList(error);
-
- bool hadError = yetToSyncViews;
-
- if (yetToSyncViews) {
- __block CFStringRef separator = CFSTR("");
-
- printmsg(CFSTR("Yet to sync views: ["), NULL);
-
- CFArrayForEach(yetToSyncViews, ^(const void *value) {
- if (isString(value)) {
- printmsg(CFSTR("%@%@"), separator, value);
-
- separator = CFSTR(", ");
- }
- });
- printmsg(CFSTR("]\n"), NULL);
- }
-
- return !hadError;
-}
-
#pragma mark -
#pragma mark --remove-peer
"
"Account/Circle Management"
" -a accept all applicants"
- " -l [reason] sign out of circle + set custom departure reason"
- " -q sign out of circle"
" -r reject all applicants"
- " -E ensure fresh parameters"
" -b device|all|single Register a backup bag - THIS RESETS BACKUPS!\n"
- " -A Apply to a ring\n"
- " -B Withdrawl from a ring\n"
- " -G Enable Ring\n"
- " -F Ring Status\n"
- " -I Dump Ring Information\n"
" -N (re-)set to new account (USE WITH CARE: device will not leave circle before resetting account!)"
" -O reset to offering"
" -R reset circle"
- " -X [limit] best effort bail from circle in limit seconds"
" -o list view unaware peers in circle"
" -0 boot view unaware peers from circle"
- " -1 grab account state from the keychain"
- " -2 delete account state from the keychain"
- " -3 grab engine state from the keychain"
- " -4 delete engine state from the keychain"
" -5 cleanup old KVS keys in KVS"
- " -6 [test]populate KVS with garbage KVS keys
"
+ "Circle Tools\n"
+ " --remove-peer SPID Remove a peer identified by the first 8 or more\n"
+ " characters of its spid. Specify multiple times to\n"
+ " remove more than one peer.\n"
"Password"
" -P [label:]password set password (optionally for a given label) for sync"
" -T [label:]password try password (optionally for a given label) for sync"
" -L list all known view and their status"
" -U purge private key material cache\n"
" -V Report View Sync Status on all known clients.\n"
- " -H Set escrow record.\n"
- " -J Get the escrow record.\n"
- " -M Check peer availability.\n"
*/
enum {
SYNC_REMOVE_PEER,
CFMutableArrayRef peers2remove = NULL;
SOSLogSetOutputTo(NULL, NULL);
- while ((ch = getopt_long(argc, argv, "ab:deg:hikl:mopq:rSv:w:x:zA:B:MNJCDEF:HG:ILOP:RT:UWX:VY0123456", longopts, NULL)) != -1)
+ while ((ch = getopt_long(argc, argv, "ab:deikmorv:NCDLOP:RT:UWV05", longopts, NULL)) != -1)
switch (ch) {
- case 'l':
- {
- fprintf(outFile, "Signing out of circle\n");
- hadError = !SOSCCSignedOut(true, &error);
- if (!hadError) {
- errno = 0;
- int reason = (int) strtoul(optarg, NULL, 10);
- if (errno != 0 ||
- reason < kSOSDepartureReasonError ||
- reason >= kSOSNumDepartureReasons) {
- fprintf(errFile, "Invalid custom departure reason %s\n", optarg);
- } else {
- fprintf(outFile, "Setting custom departure reason %d\n", reason);
- hadError = !SOSCCSetLastDepartureReason(reason, &error);
- notify_post(kSOSCCCircleChangedNotification);
- }
- }
- break;
- }
-
- case 'q':
- {
- fprintf(outFile, "Signing out of circle\n");
- bool signOutImmediately = false;
- if (strcasecmp(optarg, "true") == 0) {
- signOutImmediately = true;
- } else if (strcasecmp(optarg, "false") == 0) {
- signOutImmediately = false;
- } else {
- fprintf(outFile, "Please provide a \"true\" or \"false\" whether you'd like to leave the circle immediately\n");
- }
- hadError = !SOSCCSignedOut(signOutImmediately, &error);
- notify_post(kSOSCCCircleChangedNotification);
- break;
- }
- case 'e':
- fprintf(outFile, "Turning ON keychain syncing\n");
- hadError = requestToJoinCircle(&error);
- break;
-
- case 'd':
- fprintf(outFile, "Turning OFF keychain syncing\n");
- hadError = !SOSCCRemoveThisDeviceFromCircle(&error);
- break;
-
- case 'a':
- {
- CFArrayRef applicants = SOSCCCopyApplicantPeerInfo(NULL);
- if (applicants) {
- hadError = !SOSCCAcceptApplicants(applicants, &error);
- CFRelease(applicants);
- } else {
- fprintf(errFile, "No applicants to accept\n");
- }
- break;
- }
-
- case 'r':
- {
- CFArrayRef applicants = SOSCCCopyApplicantPeerInfo(NULL);
- if (applicants) {
- hadError = !SOSCCRejectApplicants(applicants, &error);
- CFRelease(applicants);
- } else {
- fprintf(errFile, "No applicants to reject\n");
- }
- break;
- }
-
- case 'i':
- SOSCCDumpCircleInformation();
- SOSCCDumpEngineInformation();
- break;
-
- case 'k':
- notify_post("com.apple.security.cloudkeychain.forceupdate");
- break;
-
- case 'o':
- {
- SOSCCDumpViewUnwarePeers();
- break;
- }
-
- case '0':
- {
- CFArrayRef unawares = SOSCCCopyViewUnawarePeerInfo(&error);
- if (unawares) {
- hadError = !SOSCCRemovePeersFromCircle(unawares, &error);
- } else {
- hadError = true;
+ case 'a':
+ {
+ CFArrayRef applicants = SOSCCCopyApplicantPeerInfo(NULL);
+ if (applicants) {
+ hadError = !SOSCCAcceptApplicants(applicants, &error);
+ CFRelease(applicants);
+ } else {
+ fprintf(errFile, "No applicants to accept\n");
+ }
+ break;
}
- CFReleaseNull(unawares);
- break;
- }
- case '1':
- {
- CFDataRef accountState = SOSCCCopyAccountState(&error);
- if (accountState) {
- printmsg(CFSTR(" %@\n"), CFDataCopyHexString(accountState));
- } else {
- hadError = true;
+ case 'b':
+ {
+ hadError = setBag(optarg, &error);
+ break;
}
- CFReleaseNull(accountState);
- break;
- }
- case '2':
- {
- bool status = SOSCCDeleteAccountState(&error);
- if (status) {
- printmsg(CFSTR("Deleted account from the keychain %d\n"), status);
- } else {
- hadError = true;
+ case 'd':
+ {
+ fprintf(outFile, "Turning OFF keychain syncing\n");
+ hadError = !SOSCCRemoveThisDeviceFromCircle(&error);
+ break;
}
- break;
- }
- case '3':
- {
- CFDataRef engineState = SOSCCCopyEngineData(&error);
- if (engineState) {
- printmsg(CFSTR(" %@\n"), CFDataCopyHexString(engineState));
- } else {
- hadError = true;
+ case 'e':
+ {
+ fprintf(outFile, "Turning ON keychain syncing\n");
+ hadError = requestToJoinCircle(&error);
+ break;
}
- CFReleaseNull(engineState);
- break;
- }
- case '4':
- {
- bool status = SOSCCDeleteEngineState(&error);
- if (status) {
- printmsg(CFSTR("Deleted engine-state from the keychain %d\n"), status);
- } else {
- hadError = true;
+ case 'i':
+ {
+ SOSCCDumpCircleInformation();
+ SOSCCDumpEngineInformation();
+ break;
}
- break;
- }
- case '5' :
- {
- bool result = SOSCCCleanupKVSKeys(&error);
- if(result)
+ case 'k':
{
- printmsg(CFSTR("Got all the keys from KVS %d\n"), result);
- }else {
- hadError = true;
+ notify_post("com.apple.security.cloudkeychain.forceupdate");
+ break;
}
- break;
- }
- case '6' :
- {
- bool result = SOSCCTestPopulateKVSWithBadKeys(&error);
- if(result)
- {
- printmsg(CFSTR("Populated KVS with garbage %d\n"), result);
- }else {
+ case 'm':
+ {
+ hadError = !dumpMyPeer(&error);
+ break;
+ }
+ case 'o':
+ {
+ SOSCCDumpViewUnwarePeers();
+ break;
+ }
+ case 'r':
+ {
+ CFArrayRef applicants = SOSCCCopyApplicantPeerInfo(NULL);
+ if (applicants) {
+ hadError = !SOSCCRejectApplicants(applicants, &error);
+ CFRelease(applicants);
+ } else {
+ fprintf(errFile, "No applicants to reject\n");
+ }
+ break;
+ }
+ case 'v':
+ {
+ hadError = !viewcmd(optarg, &error);
+ break;
+ }
+ case 'C':
+ {
+ hadError = clearAllKVS(&error);
+ break;
+ }
+ case 'D':
+ {
+ (void)SOSCCDumpCircleKVSInformation(optarg);
+ break;
+ }
+ case 'L':
+ {
+ hadError = !listviewcmd(&error);
+ break;
+ }
+ case 'N':
+ {
+ hadError = !SOSCCAccountSetToNew(&error);
+ if (!hadError)
+ notify_post(kSOSCCCircleChangedNotification);
+ break;
+ }
+ case 'O':
+ {
+ hadError = !SOSCCResetToOffering(&error);
+ break;
+ }
+ case 'P':
+ {
+ hadError = setPassword(optarg, &error);
+ break;
+ }
+ case 'R':
+ {
+ hadError = !SOSCCResetToEmpty(&error);
+ break;
+ }
+ case 'T':
+ {
+ hadError = tryPassword(optarg, &error);
+ break;
+ }
+ case 'U':
+ {
+ hadError = !SOSCCPurgeUserCredentials(&error);
+ break;
+ }
+ case 'V':
+ {
+ hadError = clientViewStatus(&error);
+ break;
+ }
+ case 'W':
+ {
+ hadError = syncAndWait(&error);
+ break;
+ }
+ case '0':
+ {
+ CFArrayRef unawares = SOSCCCopyViewUnawarePeerInfo(&error);
+ if (unawares) {
+ hadError = !SOSCCRemovePeersFromCircle(unawares, &error);
+ } else {
hadError = true;
}
+ CFReleaseNull(unawares);
break;
- }
- case 'E':
- {
- fprintf(outFile, "Ensuring Fresh Parameters\n");
- bool result = SOSCCRequestEnsureFreshParameters(&error);
- if (error) {
- hadError = true;
- break;
- }
- if (result) {
- fprintf(outFile, "Refreshed Parameters Ensured!\n");
- } else {
- fprintf(outFile, "Problem trying to ensure fresh parameters\n");
- }
- break;
- }
- case 'A':
- {
- fprintf(outFile, "Applying to Ring\n");
- CFStringRef ringName = CFStringCreateWithCString(kCFAllocatorDefault, (char *)optarg, kCFStringEncodingUTF8);
- hadError = SOSCCApplyToARing(ringName, &error);
- CFReleaseNull(ringName);
- break;
- }
- case 'B':
- {
- fprintf(outFile, "Withdrawing from Ring\n");
- CFStringRef ringName = CFStringCreateWithCString(kCFAllocatorDefault, (char *)optarg, kCFStringEncodingUTF8);
- hadError = SOSCCWithdrawlFromARing(ringName, &error);
- CFReleaseNull(ringName);
- break;
- }
- case 'F':
- {
- fprintf(outFile, "Status of this device in the Ring\n");
- CFStringRef ringName = CFStringCreateWithCString(kCFAllocatorDefault, (char *)optarg, kCFStringEncodingUTF8);
- hadError = SOSCCRingStatus(ringName, &error);
- CFReleaseNull(ringName);
- break;
- }
- case 'G':
- {
- fprintf(outFile, "Enabling Ring\n");
- CFStringRef ringName = CFStringCreateWithCString(kCFAllocatorDefault, (char *)optarg, kCFStringEncodingUTF8);
- hadError = SOSCCEnableRing(ringName, &error);
- CFReleaseNull(ringName);
- break;
- }
- case 'H':
- {
- fprintf(outFile, "Setting random escrow record\n");
- bool success = SOSCCSetEscrowRecord(CFSTR("label"), 8, &error);
- if(success)
- hadError = false;
- else
- hadError = true;
- break;
- }
- case 'J':
- {
- CFDictionaryRef attempts = SOSCCCopyEscrowRecord(&error);
- if(attempts){
- CFDictionaryForEach(attempts, ^(const void *key, const void *value) {
- if(isString(key)){
- char *keyString = CFStringToCString(key);
- fprintf(outFile, "%s:\n", keyString);
- free(keyString);
- }
- if(isDictionary(value)){
- CFDictionaryForEach(value, ^(const void *key, const void *value) {
- if(isString(key)){
- char *keyString = CFStringToCString(key);
- fprintf(outFile, "%s: ", keyString);
- free(keyString);
- }
- if(isString(value)){
- char *time = CFStringToCString(value);
- fprintf(outFile, "timestamp: %s\n", time);
- free(time);
- }
- else if(isNumber(value)){
- uint64_t tries;
- CFNumberGetValue(value, kCFNumberLongLongType, &tries);
- fprintf(outFile, "date: %llu\n", tries);
- }
- });
- }
-
- });
}
- CFReleaseNull(attempts);
- hadError = false;
- break;
- }
- case 'I':
- {
- fprintf(outFile, "Printing all the rings\n");
- CFStringRef ringdescription = SOSCCGetAllTheRings(&error);
- if(!ringdescription)
- hadError = true;
- else
- fprintf(outFile, "Rings: %s", CFStringToCString(ringdescription));
-
- break;
- }
-
- case 'N':
- hadError = !SOSCCAccountSetToNew(&error);
- if (!hadError)
- notify_post(kSOSCCCircleChangedNotification);
- break;
-
- case 'R':
- hadError = !SOSCCResetToEmpty(&error);
- break;
-
- case 'O':
- hadError = !SOSCCResetToOffering(&error);
- break;
-
- case 'm':
- hadError = !dumpMyPeer(&error);
- break;
-
- case 'C':
- hadError = clearAllKVS(&error);
- break;
-
- case 'P':
- hadError = setPassword(optarg, &error);
- break;
-
- case 'T':
- hadError = tryPassword(optarg, &error);
- break;
-
- case 'X':
- {
- uint64_t limit = strtoul(optarg, NULL, 10);
- hadError = !SOSCCBailFromCircle_BestEffort(limit, &error);
- break;
- }
-
- case 'U':
- hadError = !SOSCCPurgeUserCredentials(&error);
- break;
-
- case 'D':
- (void)SOSCCDumpCircleKVSInformation(optarg);
- break;
-
- case 'W':
- hadError = syncAndWait(&error);
- break;
-
- case 'v':
- hadError = !viewcmd(optarg, &error);
- break;
-
- case 'V':
- hadError = clientViewStatus(&error);
- break;
- case 'L':
- hadError = !listviewcmd(&error);
- break;
-
- case 'b':
- hadError = setBag(optarg, &error);
- break;
-
- case 'Y':
- hadError = dumpYetToSync(&error);
- break;
- case 0:
- switch (action) {
- case SYNC_REMOVE_PEER: {
- CFStringRef optstr;
- optstr = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8);
- if (peers2remove == NULL) {
- peers2remove = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+ case '5' :
+ {
+ bool result = SOSCCCleanupKVSKeys(&error);
+ if(result)
+ {
+ printmsg(CFSTR("Got all the keys from KVS %d\n"), result);
+ }else {
+ hadError = true;
}
- CFArrayAppendValue(peers2remove, optstr);
- CFRelease(optstr);
break;
}
+ case '?':
default:
return SHOW_USAGE_MESSAGE;
- }
- break;
- case '?':
- default:
- return SHOW_USAGE_MESSAGE;
- }
+ }
if (peers2remove != NULL) {
hadError = !doRemovePeers(peers2remove, &error);
CDPFollowUpContext *context = [CDPFollowUpContext contextForRecoveryKeyRepair];
context.force = true;
+ secnotice("followup", "Posting a follow up (for SOS) of type recovery key");
[cdpd postFollowUpWithContext:context error:&localError];
if(localError){
printmsg(CFSTR("Request to CoreCDP to follow up failed: %@\n"), localError);
+++ /dev/null
-/*
- * Copyright (c) 2013-2014 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-//
-// syncbackup.h
-// sec
-//
-//
-//
-
-
-#include "SecurityTool/sharedTool/security_tool_commands.h"
-
-SECURITY_COMMAND(
- "syncbackup", syncbackup,
- "[options]\n"
- " -i info (current status)\n"
- " -l list backup slice keybag membership and recovery status"
- "\n",
- "iCloud Circle Backup Information")
-
+++ /dev/null
-
-/*
- * Copyright (c) 2003-2007,2009-2010,2013-2016 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- *
- */
-
-//
-// syncbackup.c
-// sec
-//
-//
-//
-
-#include "syncbackup.h"
-
-
-#include <stdio.h>
-#include <CoreFoundation/CoreFoundation.h>
-
-#include <Security/SecureObjectSync/SOSCloudCircle.h>
-#include <Security/SecureObjectSync/SOSCloudCircleInternal.h>
-#include "keychain/SecureObjectSync/SOSBackupInformation.h"
-#include "keychain/SecureObjectSync/SOSRecoveryKeyBag.h"
-#include <Security/SecureObjectSync/SOSBackupSliceKeyBag.h>
-
-#include <utilities/SecCFWrappers.h>
-
-#include "SecurityTool/sharedTool/readline.h"
-#include "secToolFileIO.h"
-
-
-static bool dumpBackupInfo(CFErrorRef *error) {
- CFReleaseNull(*error);
- bool isLast = SOSCCIsThisDeviceLastBackup(error);
-
- printmsg(CFSTR("This %s the last backup peer.\n"), (isLast) ? "is": "isn't");
- return *error != NULL;
-}
-
-static bool longListing(CFErrorRef *error) {
- CFDataRef rkbgder = NULL;
- CFDictionaryRef bskbders = NULL;
-
- CFDictionaryRef backupInfo = SOSCCCopyBackupInformation(error);
- SOSRecoveryKeyBagRef rkbg = NULL;
- CFNumberRef status = CFDictionaryGetValue(backupInfo, kSOSBkpInfoStatus);
- int infoStatus;
- CFNumberGetValue(status, kCFNumberIntType, &infoStatus);
-
- switch(infoStatus) {
- case noError:
- rkbgder = CFDictionaryGetValue(backupInfo, kSOSBkpInfoRKBG);
- bskbders = CFDictionaryGetValue(backupInfo, kSOSBkpInfoBSKB);
- break;
- case noTxnorAcct:
- break;
- case noAlloc:
- break;
- case noTrustedPubKey:
- break;
- case noBSKBs:
- rkbgder = CFDictionaryGetValue(backupInfo, kSOSBkpInfoRKBG);
- break;
- default:
- break;
- }
-
- if(rkbgder) {
- rkbg = SOSRecoveryKeyBagCreateFromData(kCFAllocatorDefault, rkbgder, NULL);
- printmsg(CFSTR("Recovery Keybag: %@\n"), rkbg);
- }
-
- if(bskbders) {
- CFDataRef rkPub = NULL;
- if(rkbg) rkPub = SOSRecoveryKeyBagGetKeyData(rkbg, NULL);
- CFDictionaryForEach(bskbders, ^(const void *key, const void *value) {
- CFDataRef bskbder = asData(value, NULL);
- SOSBackupSliceKeyBagRef bskb = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, bskbder, NULL);
- if(bskb) {
- bool reckeyPresent = (rkPub && SOSBKSBPrefixedKeyIsInKeyBag(bskb, bskbRkbgPrefix, rkPub));
- printmsg(CFSTR("BackupSliceKeybag %@: Recovery Key %s; %@\n"), key, (reckeyPresent) ? "Present": "Absent ", bskb);
- CFReleaseNull(bskb);
- }
- });
- }
- CFReleaseNull(backupInfo);
- CFReleaseNull(rkbg);
- return *error != NULL;
-}
-
-
-
-int
-syncbackup(int argc, char * const *argv)
-{
- /*
- "Circle Backup Information"
- " -i info (current status)"
-
- */
- SOSLogSetOutputTo(NULL, NULL);
-
- int ch, result = 0;
- CFErrorRef error = NULL;
- bool hadError = false;
-
- while ((ch = getopt(argc, argv, "il")) != -1)
- switch (ch) {
-
- case 'i':
- hadError = dumpBackupInfo(&error);
- break;
-
- case 'l':
- hadError = longListing(&error);
- break;
-
- case '?':
- default:
- return SHOW_USAGE_MESSAGE;
- }
-
- if (hadError)
- printerr(CFSTR("Error: %@\n"), error);
-
- return result;
-}
@interface SOSAccountConfiguration : PBCodable <NSCopying>
{
NSMutableArray<NSString *> *_pendingBackupPeers;
- BOOL _ringUpdateFlag;
+ BOOL _sbdBackup;
struct {
- int ringUpdateFlag:1;
+ int sbdBackup:1;
} _has;
}
- (NSString *)pendingBackupPeersAtIndex:(NSUInteger)idx;
+ (Class)pendingBackupPeersType;
-@property (nonatomic) BOOL hasRingUpdateFlag;
-@property (nonatomic) BOOL ringUpdateFlag;
+@property (nonatomic) BOOL hasSbdBackup;
+@property (nonatomic) BOOL sbdBackup;
// Performs a shallow copy into other
- (void)copyTo:(SOSAccountConfiguration *)other;
{
return [NSString class];
}
-@synthesize ringUpdateFlag = _ringUpdateFlag;
-- (void)setRingUpdateFlag:(BOOL)v
-{
- _has.ringUpdateFlag = YES;
- _ringUpdateFlag = v;
-}
-- (void)setHasRingUpdateFlag:(BOOL)f
-{
- _has.ringUpdateFlag = f;
-}
-- (BOOL)hasRingUpdateFlag
-{
- return _has.ringUpdateFlag;
-}
- (NSString *)description
{
{
[dict setObject:self->_pendingBackupPeers forKey:@"pendingBackupPeers"];
}
- if (self->_has.ringUpdateFlag)
- {
- [dict setObject:[NSNumber numberWithBool:self->_ringUpdateFlag] forKey:@"ringUpdateFlag"];
- }
return dict;
}
}
}
break;
- case 2 /* ringUpdateFlag */:
- {
- self->_has.ringUpdateFlag = YES;
- self->_ringUpdateFlag = PBReaderReadBOOL(reader);
- }
- break;
default:
if (!PBReaderSkipValueWithTag(reader, tag, aType))
return NO;
PBDataWriterWriteStringField(writer, s_pendingBackupPeers, 1);
}
}
- /* ringUpdateFlag */
- {
- if (self->_has.ringUpdateFlag)
- {
- PBDataWriterWriteBOOLField(writer, self->_ringUpdateFlag, 2);
- }
- }
}
- (void)copyTo:(SOSAccountConfiguration *)other
[other addPendingBackupPeers:[self pendingBackupPeersAtIndex:i]];
}
}
- if (self->_has.ringUpdateFlag)
- {
- other->_ringUpdateFlag = _ringUpdateFlag;
- other->_has.ringUpdateFlag = YES;
- }
}
- (id)copyWithZone:(NSZone *)zone
NSString *vCopy = [v copyWithZone:zone];
[copy addPendingBackupPeers:vCopy];
}
- if (self->_has.ringUpdateFlag)
- {
- copy->_ringUpdateFlag = _ringUpdateFlag;
- copy->_has.ringUpdateFlag = YES;
- }
return copy;
}
return [other isMemberOfClass:[self class]]
&&
((!self->_pendingBackupPeers && !other->_pendingBackupPeers) || [self->_pendingBackupPeers isEqual:other->_pendingBackupPeers])
- &&
- ((self->_has.ringUpdateFlag && other->_has.ringUpdateFlag && ((self->_ringUpdateFlag && other->_ringUpdateFlag) || (!self->_ringUpdateFlag && !other->_ringUpdateFlag))) || (!self->_has.ringUpdateFlag && !other->_has.ringUpdateFlag))
;
}
return 0
^
[self->_pendingBackupPeers hash]
- ^
- (self->_has.ringUpdateFlag ? PBHashInt((NSUInteger)self->_ringUpdateFlag) : 0)
;
}
{
[self addPendingBackupPeers:iter_pendingBackupPeers];
}
- if (other->_has.ringUpdateFlag)
- {
- self->_ringUpdateFlag = other->_ringUpdateFlag;
- self->_has.ringUpdateFlag = YES;
- }
}
@end
--- /dev/null
+disabled_rules:
+ - force_cast
+ - force_try
}
addTeardownBlock {
- if (self.signedIn) {
+ if self.signedIn {
do {
let listAccounts = try device.executeFile(atPath: "/usr/local/bin/accounts_tool", withArguments: ["--no-confirmation", "deleteAccountsForUsername", self.username!])
XCTAssertEqual(listAccounts.returnCode, 0, "deleteAccountsForUsername")
device.relinquish()
}
- if (self.username != nil) {
+ if self.username != nil {
CDALog(at: .infoLevel, "Signing in to iCloud here \(self.username!)")
let listAccounts = try device.executeFile(atPath: "/usr/local/bin/accounts_tool", withArguments: ["listAccounts", "-v"])
func sosStatus(_ device: CDAIOSDevice, verbose: Bool = false) throws {
let result = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-i"])
- if (verbose) {
+ if verbose {
print("security sync -i\n\(String(data: result.standardOutput, encoding: .utf8)!)\n")
}
}
func ckksStatus(_ device: CDAIOSDevice, verbose: Bool = false) throws -> NSDictionary {
let ckks = try device.executeFile(atPath: self.ckksTool, withArguments: ["status", "--json"])
- if (verbose) {
+ if verbose {
print("ckks status\n\(String(data: ckks.standardOutput, encoding: .utf8)!)\n")
}
}
func sosApplication(_ device: CDAIOSDevice, verbose: Bool = false) throws {
- if (self.password != nil) {
+ if self.password != nil {
print("submitting application\n")
}
func sosApprove(_ device: CDAIOSDevice, verbose: Bool = false) throws {
- if (self.password != nil) {
+ if self.password != nil {
print("approving applications\n")
}
func forceResetSOS(_ device: CDAIOSDevice, resetCKKS: Bool = false) throws {
- if (self.password != nil) {
+ if self.password != nil {
_ = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-P", self.password!])
_ = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-R"])
_ = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-C"])
print("sleeping some to allow cdpd, cloudd and friends to catch up \n")
sleep(4)
- if (resetCKKS) {
+ if resetCKKS {
_ = try device.executeFile(atPath: self.ckksTool, withArguments: ["reset-cloudkit"])
print("sleeps some after ckksctl reset (should be removed)\n")
}
func test2DeviceSOS() throws {
- if (self.password == nil) {
+ if self.password == nil {
print("this test only works with password")
return
}
for i in 0..<2 {
CDALog(at: .infoLevel, "Reset \(i)")
- octagon.octagonReset("altDSID", complete: { _, error in
+ octagon.octagonReset("altDSID") { _, error in
CDTAssert(error == nil, "Octagon wasn't reset, error was \(String(describing: error))")
- })
+ }
}
}
let ciphertext = _SFAuthenticatedCiphertext.init(ciphertext: ac.ciphertext, authenticationCode: ac.authenticationCode, initializationVector: ac.initializationVector)
let clearContentsData = try op.decrypt(ciphertext, with: escrowKeys.symmetricKey)
- if clearContentsData.count == 0 {
+ if clearContentsData.isEmpty {
throw Error.OTErrorDecryptionFailure
}
let OT_ESCROW_ENCRYPTION_HKDF_SIZE = 56
let OT_ESCROW_SYMMETRIC_HKDF_SIZE = 32
-enum escrowKeyType: Int {
+enum EscrowKeyType: Int {
case kOTEscrowKeySigning = 1
case kOTEscrowKeyEncryption = 2
case kOTEscrowKeySymmetric = 3
self.secret = secret
self.bottleSalt = bottleSalt
- let encryptionKeyData = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeyEncryption, masterSecret: secret, bottleSalt: bottleSalt)
+ let encryptionKeyData = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeyEncryption, masterSecret: secret, bottleSalt: bottleSalt)
self.encryptionKey = _SFECKeyPair.init(secKey: try EscrowKeys.createSecKey(keyData: encryptionKeyData))
- let signingKeyData = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeySigning, masterSecret: secret, bottleSalt: bottleSalt)
+ let signingKeyData = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeySigning, masterSecret: secret, bottleSalt: bottleSalt)
self.signingKey = _SFECKeyPair.init(secKey: try EscrowKeys.createSecKey(keyData: signingKeyData))
- let symmetricKeyData = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeySymmetric, masterSecret: secret, bottleSalt: bottleSalt)
+ let symmetricKeyData = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeySymmetric, masterSecret: secret, bottleSalt: bottleSalt)
let specifier = _SFAESKeySpecifier.init(bitSize: TPHObjectiveC.aes256BitSize())
self.symmetricKey = try _SFAESKey.init(data: symmetricKeyData, specifier: specifier)
_ = try EscrowKeys.storeEscrowedSymmetricKey(keyData: self.symmetricKey.keyData, label: escrowSigningPubKeyHash)
}
- class func generateEscrowKey(keyType: escrowKeyType, masterSecret: Data, bottleSalt: String) throws -> (Data) {
+ class func generateEscrowKey(keyType: EscrowKeyType, masterSecret: Data, bottleSalt: String) throws -> (Data) {
var keyLength: Int
var info: Data
var derivedKey: Data
var finalKey = Data()
switch keyType {
- case escrowKeyType.kOTEscrowKeySymmetric:
+ case EscrowKeyType.kOTEscrowKeySymmetric:
keyLength = OT_ESCROW_SYMMETRIC_HKDF_SIZE
let infoString = Array("Escrow Symmetric Key".utf8)
info = Data(bytes: infoString, count: infoString.count)
- break
- case escrowKeyType.kOTEscrowKeyEncryption:
+ case EscrowKeyType.kOTEscrowKeyEncryption:
keyLength = OT_ESCROW_ENCRYPTION_HKDF_SIZE
let infoString = Array("Escrow Encryption Private Key".utf8)
info = Data(bytes: infoString, count: infoString.count)
- break
- case escrowKeyType.kOTEscrowKeySigning:
+ case EscrowKeyType.kOTEscrowKeySigning:
keyLength = OT_ESCROW_SIGNING_HKDF_SIZE
let infoString = Array("Escrow Signing Private Key".utf8)
info = Data(bytes: infoString, count: infoString.count)
- break
}
guard let cp = ccec_cp_384() else {
throw EscrowKeysError.corecryptoKeyGeneration(corecryptoError: status)
}
- if(keyType == escrowKeyType.kOTEscrowKeySymmetric) {
+ if keyType == EscrowKeyType.kOTEscrowKeySymmetric {
finalKey = Data(buffer: derivedKeyBytes.bindMemory(to: UInt8.self))
return
- } else if(keyType == escrowKeyType.kOTEscrowKeyEncryption || keyType == escrowKeyType.kOTEscrowKeySigning) {
+ } else if keyType == EscrowKeyType.kOTEscrowKeyEncryption || keyType == EscrowKeyType.kOTEscrowKeySigning {
status = ccec_generate_key_deterministic(cp,
derivedKeyBytes.count, derivedKeyBytes.bindMemory(to: UInt8.self).baseAddress!,
ccDRBGGetRngState(),
return key
}
- class func setKeyMaterialInKeychain(query: Dictionary<CFString, Any>) throws -> (Bool) {
+ class func setKeyMaterialInKeychain(query: [CFString: Any]) throws -> (Bool) {
var result = false
var results: CFTypeRef?
if status == errSecSuccess {
result = true
} else if status == errSecDuplicateItem {
- var updateQuery: Dictionary<CFString, Any> = query
+ var updateQuery: [CFString: Any] = query
updateQuery[kSecClass] = nil
status = SecItemUpdate(query as CFDictionary, updateQuery as CFDictionary)
return try EscrowKeys.setKeyMaterialInKeychain(query: query)
}
- class func retrieveEscrowKeysFromKeychain(label: String) throws -> [Dictionary <CFString, Any>]? {
- var keySet: [Dictionary<CFString, Any>]?
+ class func retrieveEscrowKeysFromKeychain(label: String) throws -> [ [CFString: Any]]? {
+ var keySet: [[CFString: Any]]?
let query: [CFString: Any] = [
kSecClass: kSecClassKey,
}
if result != nil {
- if let dictionaryArray = result as? [Dictionary<CFString, Any>] {
+ if let dictionaryArray = result as? [[CFString: Any]] {
keySet = dictionaryArray
} else {
- if let dictionary = result as? Dictionary<CFString, Any> {
+ if let dictionary = result as? [CFString: Any] {
keySet = [dictionary]
} else {
keySet = nil
func logComplete(function: String, container: ContainerName, error: Error?) {
if let error = error {
- os_log("%@ errored for %@: %@", log: tplogDebug, type: .default, function, container.description, error as CVarArg)
+ os_log("%{public}@ errored for %{public}@: %{public}@", log: tplogDebug, type: .default, function, container.description, error as CVarArg)
} else {
- os_log("%@ finished for %@", log: tplogDebug, type: .default, function, container.description)
+ os_log("%{public}@ finished for %{public}@", log: tplogDebug, type: .default, function, container.description)
}
}
func dump(withContainer container: String, context: String, reply: @escaping ([AnyHashable: Any]?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Dumping for %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("Dumping for %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.dump { result, error in
self.logComplete(function: "Dumping", container: container.name, error: error)
reply(result, CKXPCSuitableError(error))
}
} catch {
- os_log("Dumping failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ os_log("Dumping failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(nil, CKXPCSuitableError(error))
}
}
reply: @escaping (String?, TPPeerPermanentInfo?, TPPeerStableInfo?, TPPeerDynamicInfo?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Dumping peer for %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("Dumping peer for %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.dumpEgoPeer { peerID, perm, stable, dyn, error in
self.logComplete(function: "Dumping peer", container: container.name, error: error)
reply(peerID, perm, stable, dyn, CKXPCSuitableError(error))
}
} catch {
- os_log("Dumping peer failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ os_log("Dumping peer failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(nil, nil, nil, nil, CKXPCSuitableError(error))
}
}
let container = try self.containerMap.findOrCreate(name: containerName)
container.trustStatus(reply: reply)
} catch {
- os_log("Trust status failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ os_log("Trust status failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(TrustedPeersHelperEgoPeerStatus(egoPeerID: nil,
status: TPPeerStatus.unknown,
viablePeerCountsByModelID: [:],
func fetchTrustState(withContainer container: String, context: String, reply: @escaping (TrustedPeersHelperPeerState?, [TrustedPeersHelperPeer]?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Fetch Trust State for %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("Fetch Trust State for %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.fetchTrustState(reply: reply)
} catch {
- os_log("Fetch Trust State failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ os_log("Fetch Trust State failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(nil, nil, CKXPCSuitableError(error))
}
}
func reset(withContainer container: String, context: String, resetReason: CuttlefishResetReason, reply: @escaping (Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Resetting for %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("Resetting for %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.reset(resetReason: resetReason) { error in
self.logComplete(function: "Resetting", container: container.name, error: error)
reply(CKXPCSuitableError(error)) }
} catch {
- os_log("Resetting failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ os_log("Resetting failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(CKXPCSuitableError(error))
}
}
func localReset(withContainer container: String, context: String, reply: @escaping (Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Performing local reset for %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("Performing local reset for %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.localReset { error in
self.logComplete(function: "Local reset", container: container.name, error: error)
reply(CKXPCSuitableError(error))
}
} catch {
- os_log("Local reset failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ os_log("Local reset failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(CKXPCSuitableError(error))
}
}
func setAllowedMachineIDsWithContainer(_ container: String,
context: String,
allowedMachineIDs: Set<String>,
+ honorIDMSListChanges: Bool,
reply: @escaping (Bool, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Setting allowed machineIDs for %@ to %@", log: tplogDebug, type: .default, containerName.description, allowedMachineIDs)
+ os_log("Setting allowed machineIDs for %{public}@ to %{public}@", log: tplogDebug, type: .default, containerName.description, allowedMachineIDs)
let container = try self.containerMap.findOrCreate(name: containerName)
- container.setAllowedMachineIDs(allowedMachineIDs) { differences, error in
+ container.setAllowedMachineIDs(allowedMachineIDs, honorIDMSListChanges: honorIDMSListChanges) { differences, error in
self.logComplete(function: "Setting allowed machineIDs", container: container.name, error: error)
reply(differences, CKXPCSuitableError(error))
}
} catch {
- os_log("Setting allowed machineIDs failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ os_log("Setting allowed machineIDs failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(false, CKXPCSuitableError(error))
}
}
reply: @escaping (Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Adding allowed machineIDs for %@: %@", log: tplogDebug, type: .default, containerName.description, machineIDs)
+ os_log("Adding allowed machineIDs for %{public}@: %{public}@", log: tplogDebug, type: .default, containerName.description, machineIDs)
let container = try self.containerMap.findOrCreate(name: containerName)
container.addAllow(machineIDs) { error in
self.logComplete(function: "Adding allowed machineIDs", container: container.name, error: error)
reply(CKXPCSuitableError(error))
}
} catch {
- os_log("Adding allowed machineID failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ os_log("Adding allowed machineID failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(CKXPCSuitableError(error))
}
}
reply: @escaping (Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Removing allowed machineIDs for %@: %@", log: tplogDebug, type: .default, containerName.description, machineIDs)
+ os_log("Removing allowed machineIDs for %{public}@: %{public}@", log: tplogDebug, type: .default, containerName.description, machineIDs)
let container = try self.containerMap.findOrCreate(name: containerName)
container.removeAllow(machineIDs) { error in
self.logComplete(function: "Removing allowed machineIDs", container: container.name, error: error)
reply(CKXPCSuitableError(error))
}
} catch {
- os_log("Removing allowed machineID failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ os_log("Removing allowed machineID failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(CKXPCSuitableError(error))
}
}
func fetchAllowedMachineIDs(withContainer container: String, context: String, reply: @escaping (Set<String>?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Fetching allowed machineIDs for %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("Fetching allowed machineIDs for %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
- container.fetchAllowedMachineIDs() { mids, error in
+ container.fetchAllowedMachineIDs { mids, error in
self.logComplete(function: "Fetched allowed machineIDs", container: container.name, error: error)
reply(mids, CKXPCSuitableError(error))
}
} catch {
- os_log("Fetching allowed machineIDs failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ os_log("Fetching allowed machineIDs failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(nil, CKXPCSuitableError(error))
}
}
func fetchEgoEpoch(withContainer container: String, context: String, reply: @escaping (UInt64, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("retrieving epoch for %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("retrieving epoch for %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.getEgoEpoch { epoch, error in
reply(epoch, CKXPCSuitableError(error))
}
} catch {
- os_log("Epoch retrieval failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ os_log("Epoch retrieval failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(0, CKXPCSuitableError(error))
}
}
deviceName: String?,
serialNumber: String,
osVersion: String,
- policyVersion: NSNumber?,
+ policyVersion: TPPolicyVersion?,
policySecrets: [String: Data]?,
signingPrivKeyPersistentRef: Data?,
encPrivKeyPersistentRef: Data?,
- reply: @escaping (String?, Data?, Data?, Data?, Data?, Error?) -> Void) {
+ reply: @escaping (String?, Data?, Data?, Data?, Data?, Set<String>?, TPPolicy?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Preparing new identity for %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("Preparing new identity for %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.prepare(epoch: epoch,
machineID: machineID,
deviceName: deviceName,
serialNumber: serialNumber,
osVersion: osVersion,
- policyVersion: policyVersion?.uint64Value,
+ policyVersion: policyVersion,
policySecrets: policySecrets,
signingPrivateKeyPersistentRef: signingPrivKeyPersistentRef,
- encryptionPrivateKeyPersistentRef: encPrivKeyPersistentRef) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+ encryptionPrivateKeyPersistentRef: encPrivKeyPersistentRef) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, views, policy, error in
self.logComplete(function: "Prepare", container: container.name, error: error)
- reply(peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, CKXPCSuitableError(error))
+ reply(peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, views, policy, CKXPCSuitableError(error))
}
} catch {
- os_log("Prepare failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
- reply(nil, nil, nil, nil, nil, CKXPCSuitableError(error))
+ os_log("Prepare failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ reply(nil, nil, nil, nil, nil, nil, nil, CKXPCSuitableError(error))
}
}
reply: @escaping (String?, [CKRecord]?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Establishing %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("Establishing %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.establish(ckksKeys: ckksKeys,
tlkShares: tlkShares,
self.logComplete(function: "Establishing", container: container.name, error: error)
reply(peerID, keyHierarchyRecords, CKXPCSuitableError(error)) }
} catch {
- os_log("Establishing failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ os_log("Establishing failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(nil, nil, CKXPCSuitableError(error))
}
}
reply: @escaping (Data?, Data?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Vouching %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("Vouching %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.vouch(peerID: peerID,
permanentInfo: permanentInfo,
self.logComplete(function: "Vouching", container: container.name, error: error)
reply(voucher, voucherSig, CKXPCSuitableError(error)) }
} catch {
- os_log("Vouching failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ os_log("Vouching failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(nil, nil, CKXPCSuitableError(error))
}
}
func preflightVouchWithBottle(withContainer container: String,
context: String,
bottleID: String,
- reply: @escaping (String?, Error?) -> Void) {
+ reply: @escaping (String?, Set<String>?, TPPolicy?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Preflight Vouch With Bottle %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("Preflight Vouch With Bottle %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
- container.preflightVouchWithBottle(bottleID: bottleID) { peerID, error in
+ container.preflightVouchWithBottle(bottleID: bottleID) { peerID, viewSet, policy, error in
self.logComplete(function: "Preflight Vouch With Bottle", container: container.name, error: error)
- reply(peerID, CKXPCSuitableError(error)) }
+ reply(peerID, viewSet, policy, CKXPCSuitableError(error)) }
} catch {
- os_log("Preflighting Vouch With Bottle failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
- reply(nil, CKXPCSuitableError(error))
+ os_log("Preflighting Vouch With Bottle failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ reply(nil, nil, nil, CKXPCSuitableError(error))
}
}
entropy: Data,
bottleSalt: String,
tlkShares: [CKKSTLKShare],
- reply: @escaping (Data?, Data?, Error?) -> Void) {
+ reply: @escaping (Data?, Data?, Int64, Int64, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Vouching With Bottle %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("Vouching With Bottle %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
- container.vouchWithBottle(bottleID: bottleID, entropy: entropy, bottleSalt: bottleSalt, tlkShares: tlkShares) { voucher, voucherSig, error in
+ container.vouchWithBottle(bottleID: bottleID, entropy: entropy, bottleSalt: bottleSalt, tlkShares: tlkShares) { voucher, voucherSig, uniqueTLKsRecovered, totalTLKSharesRecovered, error in
self.logComplete(function: "Vouching With Bottle", container: container.name, error: error)
- reply(voucher, voucherSig, CKXPCSuitableError(error)) }
+ reply(voucher, voucherSig, uniqueTLKsRecovered, totalTLKSharesRecovered, CKXPCSuitableError(error)) }
} catch {
- os_log("Vouching with Bottle failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
- reply(nil, nil, CKXPCSuitableError(error))
+ os_log("Vouching with Bottle failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ reply(nil, nil, 0, 0, CKXPCSuitableError(error))
+ }
+ }
+
+ func preflightVouchWithRecoveryKey(withContainer container: String,
+ context: String,
+ recoveryKey: String,
+ salt: String,
+ reply: @escaping (String?, Set<String>?, TPPolicy?, Error?) -> Void) {
+ do {
+ let containerName = ContainerName(container: container, context: context)
+ os_log("Preflight Vouch With RecoveryKey %{public}@", log: tplogDebug, type: .default, containerName.description)
+ let container = try self.containerMap.findOrCreate(name: containerName)
+ container.preflightVouchWithRecoveryKey(recoveryKey: recoveryKey, salt: salt) { rkID, viewSet, policy, error in
+ self.logComplete(function: "Preflight Vouch With RecoveryKey", container: container.name, error: error)
+ reply(rkID, viewSet, policy, CKXPCSuitableError(error)) }
+ } catch {
+ os_log("Preflighting Vouch With RecoveryKey failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ reply(nil, nil, nil, CKXPCSuitableError(error))
}
}
reply: @escaping (Data?, Data?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Vouching With Recovery Key %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("Vouching With Recovery Key %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.vouchWithRecoveryKey(recoveryKey: recoveryKey, salt: salt, tlkShares: tlkShares) { voucher, voucherSig, error in
self.logComplete(function: "Vouching With Recovery Key", container: container.name, error: error)
reply(voucher, voucherSig, CKXPCSuitableError(error)) }
} catch {
- os_log("Vouching with Recovery Key failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ os_log("Vouching with Recovery Key failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(nil, nil, CKXPCSuitableError(error))
}
}
ckksKeys: [CKKSKeychainBackedKeySet],
tlkShares: [CKKSTLKShare],
preapprovedKeys: [Data],
- reply: @escaping (String?, [CKRecord]?, Error?) -> Void) {
+ reply: @escaping (String?, [CKRecord]?, Set<String>?, TPPolicy?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Joining %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("Joining %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.join(voucherData: voucherData,
voucherSig: voucherSig,
ckksKeys: ckksKeys,
tlkShares: tlkShares,
- preapprovedKeys: preapprovedKeys) { peerID, keyHierarchyRecords, error in reply(peerID, keyHierarchyRecords, CKXPCSuitableError(error)) }
+ preapprovedKeys: preapprovedKeys) { peerID, keyHierarchyRecords, views, policy, error in
+ reply(peerID, keyHierarchyRecords, views, policy, CKXPCSuitableError(error))
+
+ }
} catch {
- reply(nil, nil, CKXPCSuitableError(error))
+ os_log("Joining failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ reply(nil, nil, nil, nil, CKXPCSuitableError(error))
}
}
reply: @escaping (Bool, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Attempting to preflight a preapproved join for %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("Attempting to preflight a preapproved join for %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.preflightPreapprovedJoin { success, error in reply(success, CKXPCSuitableError(error)) }
} catch {
+ os_log("preflightPreapprovedJoin failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(false, CKXPCSuitableError(error))
}
}
ckksKeys: [CKKSKeychainBackedKeySet],
tlkShares: [CKKSTLKShare],
preapprovedKeys: [Data],
- reply: @escaping (String?, [CKRecord]?, Error?) -> Void) {
+ reply: @escaping (String?, [CKRecord]?, Set<String>?, TPPolicy?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Attempting a preapproved join for %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("Attempting a preapproved join for %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.preapprovedJoin(ckksKeys: ckksKeys,
tlkShares: tlkShares,
- preapprovedKeys: preapprovedKeys) { peerID, keyHierarchyRecords, error in reply(peerID, keyHierarchyRecords, CKXPCSuitableError(error)) }
+ preapprovedKeys: preapprovedKeys) { peerID, keyHierarchyRecords, viewSet, policy, error in
+ reply(peerID, keyHierarchyRecords, viewSet, policy, CKXPCSuitableError(error)) }
} catch {
- reply(nil, nil, CKXPCSuitableError(error))
+ os_log("attemptPreapprovedJoin failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ reply(nil, nil, nil, nil, CKXPCSuitableError(error))
}
}
reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Updating %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("Updating %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.update(deviceName: deviceName,
serialNumber: serialNumber,
policyVersion: policyVersion?.uint64Value,
policySecrets: policySecrets) { state, error in reply(state, CKXPCSuitableError(error)) }
} catch {
+ os_log("update failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(nil, CKXPCSuitableError(error))
}
}
func setPreapprovedKeysWithContainer(_ container: String,
- context: String,
- preapprovedKeys: [Data],
- reply: @escaping (Error?) -> Void) {
+ context: String,
+ preapprovedKeys: [Data],
+ reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Updating %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("setPreapprovedKeysWithContainer %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
- container.set(preapprovedKeys: preapprovedKeys) { error in reply(CKXPCSuitableError(error)) }
+ container.set(preapprovedKeys: preapprovedKeys) { state, error in reply(state, CKXPCSuitableError(error)) }
} catch {
- reply(CKXPCSuitableError(error))
+ os_log("setPreapprovedKeysWithContainer failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ reply(nil, CKXPCSuitableError(error))
}
}
reply: @escaping ([CKRecord]?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Updating TLKs for %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("Updating TLKs for %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.updateTLKs(ckksKeys: ckksKeys,
tlkShares: tlkShares,
reply: reply)
} catch {
+ os_log("updateTLKs failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(nil, CKXPCSuitableError(error))
}
}
reply: @escaping (Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Departing %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("Departing %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.departByDistrustingSelf { error in
reply(CKXPCSuitableError(error))
}
} catch {
+ os_log("departByDistrustingSelf failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(CKXPCSuitableError(error))
}
}
reply: @escaping (Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Distrusting %@ in %@", log: tplogDebug, type: .default, peerIDs, containerName.description)
+ os_log("Distrusting %{public}@ in %{public}@", log: tplogDebug, type: .default, peerIDs, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.distrust(peerIDs: peerIDs) { error in
reply(CKXPCSuitableError(error))
}
} catch {
+ os_log("distrustPeerIDs failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(CKXPCSuitableError(error))
}
}
func fetchViableBottles(withContainer container: String, context: String, reply: @escaping ([String]?, [String]?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("fetchViableBottles in %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("fetchViableBottles in %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.fetchViableBottles { sortedBottleIDs, partialBottleIDs, error in
reply(sortedBottleIDs, partialBottleIDs, CKXPCSuitableError(error))
}
} catch {
+ os_log("fetchViableBottles failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(nil, nil, CKXPCSuitableError(error))
}
}
func fetchEscrowContents(withContainer container: String, context: String, reply: @escaping (Data?, String?, Data?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("fetchEscrowContents in %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("fetchEscrowContents in %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.fetchEscrowContents { entropy, bottleID, signingPublicKey, error in
reply(entropy, bottleID, signingPublicKey, CKXPCSuitableError(error))
}
} catch {
+ os_log("fetchEscrowContents failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(nil, nil, nil, CKXPCSuitableError(error))
}
}
- func fetchPolicy(withContainer container: String,
- context: String,
- reply: @escaping (TPPolicy?, Error?) -> Void) {
+ func fetchCurrentPolicy(withContainer container: String,
+ context: String,
+ reply: @escaping (Set<String>?, TPPolicy?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Fetching policy for %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("Fetching policy+views for %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
- container.fetchPolicy { policy, error in
- reply(policy, CKXPCSuitableError(error))
+ container.fetchCurrentPolicy { viewList, policy, error in
+ reply(viewList, policy, CKXPCSuitableError(error))
}
} catch {
- reply(nil, CKXPCSuitableError(error))
+ os_log("fetchCurrentPolicy failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ reply(nil, nil, CKXPCSuitableError(error))
}
}
func fetchPolicyDocuments(withContainer container: String,
context: String,
- keys: [NSNumber: String],
- reply: @escaping ([NSNumber: [String]]?, Error?) -> Void) {
+ versions: Set<TPPolicyVersion>,
+ reply: @escaping ([TPPolicyVersion: Data]?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Fetching policy documents %@ with keys: %@", log: tplogDebug, type: .default, containerName.description, keys)
+ os_log("Fetching policy documents %{public}@ with versions: %{public}@", log: tplogDebug, type: .default, containerName.description, versions)
let container = try self.containerMap.findOrCreate(name: containerName)
- container.fetchPolicyDocuments(keys: keys) { entries, error in
+ container.fetchPolicyDocuments(versions: versions) { entries, error in
reply(entries, CKXPCSuitableError(error))
}
} catch {
+ os_log("fetchPolicyDocuments failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(nil, CKXPCSuitableError(error))
}
}
func validatePeers(withContainer container: String, context: String, reply: @escaping ([AnyHashable: Any]?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("ValidatePeers for %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("ValidatePeers for %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
let request = ValidatePeersRequest()
container.validatePeers(request: request) { result, error in
reply(result, CKXPCSuitableError(error))
}
} catch {
- os_log("ValidatePeers failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ os_log("ValidatePeers failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(nil, CKXPCSuitableError(error))
}
}
func setRecoveryKeyWithContainer(_ container: String, context: String, recoveryKey: String, salt: String, ckksKeys: [CKKSKeychainBackedKeySet], reply: @escaping (Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("SetRecoveryKey for %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("SetRecoveryKey for %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.setRecoveryKey(recoveryKey: recoveryKey, salt: salt, ckksKeys: ckksKeys) { error in
self.logComplete(function: "setRecoveryKey", container: container.name, error: error)
reply(CKXPCSuitableError(error))
}
} catch {
- os_log("SetRecoveryKey failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ os_log("SetRecoveryKey failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(CKXPCSuitableError(error))
}
}
func reportHealth(withContainer container: String, context: String, stateMachineState: String, trustState: String, reply: @escaping (Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("ReportHealth for %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("ReportHealth for %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
let request = ReportHealthRequest.with {
$0.stateMachineState = stateMachineState
reply(CKXPCSuitableError(error))
}
} catch {
- os_log("ReportHealth failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ os_log("ReportHealth failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(CKXPCSuitableError(error))
}
}
func pushHealthInquiry(withContainer container: String, context: String, reply: @escaping (Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("PushHealthInquiry for %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("PushHealthInquiry for %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.pushHealthInquiry { error in
self.logComplete(function: "pushHealthInquiry", container: container.name, error: error)
reply(CKXPCSuitableError(error))
}
} catch {
- os_log("PushHealthInquiry failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ os_log("PushHealthInquiry failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(CKXPCSuitableError(error))
}
}
- func getViewsWithContainer(_ container: String, context: String, inViews: [String], reply: @escaping ([String]?, Error?) -> Void) {
- do {
- let containerName = ContainerName(container: container, context: context)
- os_log("GetViews (%@) for %@", log: tplogDebug, type: .default, inViews, containerName.description)
- let container = try self.containerMap.findOrCreate(name: containerName)
- container.getViews(inViews: inViews) { outViews, error in
- reply(outViews, CKXPCSuitableError(error))
- }
- } catch {
- os_log("GetViews failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
- reply(nil, CKXPCSuitableError(error))
- }
- }
-
- func requestHealthCheck(withContainer container: String, context: String, requiresEscrowCheck: Bool, reply: @escaping (Bool, Bool, Bool, Error?) -> Void) {
+ func requestHealthCheck(withContainer container: String, context: String, requiresEscrowCheck: Bool, reply: @escaping (Bool, Bool, Bool, Bool, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("Health Check! requiring escrow check? %d for %@", log: tplogDebug, type: .default, requiresEscrowCheck, containerName.description)
+ os_log("Health Check! requiring escrow check? %d for %{public}@", log: tplogDebug, type: .default, requiresEscrowCheck, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
- container.requestHealthCheck(requiresEscrowCheck: requiresEscrowCheck) { postRepair, postEscrow, postReset, error in
- reply(postRepair, postEscrow, postReset, CKXPCSuitableError(error))
+ container.requestHealthCheck(requiresEscrowCheck: requiresEscrowCheck) { postRepair, postEscrow, postReset, leaveTrust, error in
+ reply(postRepair, postEscrow, postReset, leaveTrust, CKXPCSuitableError(error))
}
} catch {
- os_log("Health Check! failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
- reply(false, false, false, CKXPCSuitableError(error))
+ os_log("Health Check! failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ reply(false, false, false, false, CKXPCSuitableError(error))
}
}
func getSupportAppInfo(withContainer container: String, context: String, reply: @escaping (Data?, Error?) -> Void) {
do {
let containerName = ContainerName(container: container, context: context)
- os_log("getSupportInfo %d for %@", log: tplogDebug, type: .default, containerName.description)
+ os_log("getSupportInfo %d for %{public}@", log: tplogDebug, type: .default, containerName.description)
let container = try self.containerMap.findOrCreate(name: containerName)
container.getSupportAppInfo { info, error in
reply(info, CKXPCSuitableError(error))
}
} catch {
- os_log("getSupportInfo failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+ os_log("getSupportInfo failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
reply(nil, CKXPCSuitableError(error))
}
/*
- * Copyright (c) 2018 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2018 - 2020 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
case failedToStoreSecret(errorCode: Int)
case unknownSecurityFoundationError
case failedToSerializeData
+ case unknownInternalError
}
extension ContainerError: LocalizedError {
return "SecurityFoundation returned an unknown type"
case .failedToSerializeData:
return "Failed to encode protobuf data"
+ case .unknownInternalError:
+ return "Internal code failed, but didn't return error"
}
}
}
return 43
case .failedToSerializeData:
return 44
+ case .unknownInternalError:
+ return 45
}
}
func loadEgoKeys(peerID: String, resultHandler: @escaping (OctagonSelfPeerKeys?, Error?) -> Void) {
loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: peerID)) { signingKey, error in
guard let signingKey = signingKey else {
- os_log("Unable to load signing key: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+ os_log("Unable to load signing key: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
resultHandler(nil, error)
return
}
loadEgoKeyPair(identifier: encryptionKeyIdentifier(peerID: peerID)) { encryptionKey, error in
guard let encryptionKey = encryptionKey else {
- os_log("Unable to load encryption key: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+ os_log("Unable to load encryption key: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
resultHandler(nil, error)
return
}
}.compactMap { $0 }
}
-func extract(tlkShares: [CKKSTLKShare], peer: CKKSSelfPeer) {
- os_log("Attempting to recover %d TLK shares for peer %@", log: tplogDebug, type: .default, tlkShares.count, peer.peerID)
+@discardableResult
+func extract(tlkShares: [CKKSTLKShare], peer: OctagonSelfPeerKeys, model: TPModel) -> (Int64, Int64) {
+ os_log("Attempting to recover %d TLK shares for peer %{public}@", log: tplogDebug, type: .default, tlkShares.count, peer.peerID)
+ var tlksRecovered: Set<String> = Set()
+ var sharesRecovered: Int64 = 0
+
for share in tlkShares {
guard share.receiverPeerID == peer.peerID else {
os_log("Skipping %@ (wrong peerID)", log: tplogDebug, type: .default, share)
}
do {
- // TODO: how should we handle peer sets here?
+ var trustedPeers: [AnyHashable] = [peer]
+
+ if let egoPeer = model.peer(withID: peer.peerID) {
+ egoPeer.trustedPeerIDs.forEach { trustedPeerID in
+ if let peer = model.peer(withID: trustedPeerID) {
+ let peerObj = CKKSActualPeer(peerID: trustedPeerID,
+ encryptionPublicKey: (peer.permanentInfo.encryptionPubKey as! _SFECPublicKey),
+ signing: (peer.permanentInfo.signingPubKey as! _SFECPublicKey),
+ viewList: [])
+
+ trustedPeers.append(peerObj)
+ } else {
+ os_log("No peer for trusted ID %{public}@", log: tplogDebug, type: .default, trustedPeerID)
+ }
+ }
+ } else {
+ os_log("No ego peer in model; no trusted peers", log: tplogDebug, type: .default)
+ }
+
let key = try share.recoverTLK(peer,
- trustedPeers: [peer as! AnyHashable],
+ trustedPeers: Set(trustedPeers),
ckrecord: nil)
try key.saveMaterialToKeychain()
+ tlksRecovered.insert(key.uuid)
+ sharesRecovered += 1
os_log("Recovered %@ (from %@)", log: tplogDebug, type: .default, key, share)
} catch {
- os_log("Failed to recover share %@: %@", log: tplogDebug, type: .default, share, error as CVarArg)
+ os_log("Failed to recover share %@: %{public}@", log: tplogDebug, type: .default, share, error as CVarArg)
}
}
+ return (Int64(tlksRecovered.count), sharesRecovered)
}
struct ContainerState {
let osVersion: String?
let policyVersion: UInt64?
let policySecrets: [String: Data]?
- let recoverySigningPubKey: Data?
- var recoveryEncryptionPubKey: Data?
}
// CoreData doesn't handle creating an identical model from an identical URL. Help it out.
}
}
+extension ContainerMO {
+ func egoStableInfo() -> TPPeerStableInfo? {
+ guard let egoStableData = self.egoPeerStableInfo,
+ let egoStableSig = self.egoPeerStableInfoSig else {
+ return nil
+ }
+
+ return TPPeerStableInfo(data: egoStableData, sig: egoStableSig)
+ }
+}
+
/// This maps to a Cuttlefish service backed by a CloudKit container,
/// and a corresponding local Core Data persistent container.
///
// moc.perform() or moc.performAndWait().
internal var containerMO: ContainerMO
internal var model: TPModel
-
/**
Construct a Container.
self.containerMO = containerMO!
self.cuttlefish = cuttlefish
self.model = model!
-
super.init()
}
do {
try model.update(stableInfo, forPeerWithID: permanentInfo.peerID)
} catch {
- os_log("loadModel unable to update stable info for peer(%@): %@", log: tplogDebug, type: .default, peer, error as CVarArg)
+ os_log("loadModel unable to update stable info for peer(%{public}@): %{public}@", log: tplogDebug, type: .default, peer, error as CVarArg)
}
} else {
- os_log("loadModel: peer %@ has unparseable stable info", log: tplogDebug, type: .default, permanentInfo.peerID)
+ os_log("loadModel: peer %{public}@ has unparseable stable info", log: tplogDebug, type: .default, permanentInfo.peerID)
}
} else {
- os_log("loadModel: peer %@ has no stable info", log: tplogDebug, type: .default, permanentInfo.peerID)
+ os_log("loadModel: peer %{public}@ has no stable info", log: tplogDebug, type: .default, permanentInfo.peerID)
}
if let data = peer.dynamicInfo, let sig = peer.dynamicInfoSig {
if let dynamicInfo = TPPeerDynamicInfo(data: data as Data, sig: sig as Data) {
do {
try model.update(dynamicInfo, forPeerWithID: permanentInfo.peerID)
} catch {
- os_log("loadModel unable to update dynamic info for peer(%@): %@", log: tplogDebug, type: .default, peer, error as CVarArg)
+ os_log("loadModel unable to update dynamic info for peer(%{public}@): %{public}@", log: tplogDebug, type: .default, peer, error as CVarArg)
}
} else {
- os_log("loadModel: peer %@ has unparseable dynamic info", log: tplogDebug, type: .default, permanentInfo.peerID)
+ os_log("loadModel: peer %{public}@ has unparseable dynamic info", log: tplogDebug, type: .default, permanentInfo.peerID)
}
} else {
- os_log("loadModel: peer %@ has no dynamic info", log: tplogDebug, type: .default, permanentInfo.peerID)
+ os_log("loadModel: peer %{public}@ has no dynamic info", log: tplogDebug, type: .default, permanentInfo.peerID)
}
peer.vouchers?.forEach {
let v = $0 as! VoucherMO
}
}
+ if let recoveryKeySigningSPKI = containerMO.recoveryKeySigningSPKI,
+ let recoveryKeyEncyryptionSPKI = containerMO.recoveryKeyEncryptionSPKI {
+ model.setRecoveryKeys(TPRecoveryKeyPair(signingSPKI: recoveryKeySigningSPKI, encryptionSPKI: recoveryKeyEncyryptionSPKI))
+ } else {
+ // If the ego peer has an RK set, tell the model to use that one
+ // This is a hack to work around TPH databases which don't have the RK set on the container due to previously running old software
+ if let egoStableInfo = containerMO.egoStableInfo(),
+ egoStableInfo.recoverySigningPublicKey.count > 0,
+ egoStableInfo.recoveryEncryptionPublicKey.count > 0 {
+ os_log("loadModel: recovery key not set in model, but is set on ego peer", log: tplogDebug, type: .default)
+ model.setRecoveryKeys(TPRecoveryKeyPair(signingSPKI: egoStableInfo.recoverySigningPublicKey, encryptionSPKI: egoStableInfo.recoveryEncryptionPublicKey))
+ }
+ }
+
// Register persisted policies (cached from cuttlefish)
let policies = containerMO.policies as? Set<PolicyMO>
policies?.forEach { policyMO in
let allowedMachineIDs = Set(knownMachines.filter { $0.status == TPMachineIDStatus.allowed.rawValue }.compactMap { $0.machineID })
let disallowedMachineIDs = Set(knownMachines.filter { $0.status == TPMachineIDStatus.disallowed.rawValue }.compactMap { $0.machineID })
- os_log("loadModel: allowedMachineIDs: %@", log: tplogDebug, type: .default, allowedMachineIDs)
- os_log("loadModel: disallowedMachineIDs: %@", log: tplogDebug, type: .default, disallowedMachineIDs)
+ os_log("loadModel: allowedMachineIDs: %{public}@", log: tplogDebug, type: .default, allowedMachineIDs)
+ os_log("loadModel: disallowedMachineIDs: %{public}@", log: tplogDebug, type: .default, disallowedMachineIDs)
- if allowedMachineIDs.count == 0 {
+ if allowedMachineIDs.isEmpty {
os_log("loadModel: no allowedMachineIDs?", log: tplogDebug, type: .default)
}
guard returnError == nil else {
var isLocked = false
if let error = (loadError as NSError?) {
- os_log("trust status: Unable to load ego keys: %@", log: tplogDebug, type: .default, error as CVarArg)
+ os_log("trust status: Unable to load ego keys: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
if error.code == errSecItemNotFound && error.domain == NSOSStatusErrorDomain {
os_log("trust status: Lost the ego key pair, returning 'excluded' in hopes of fixing up the identity", log: tplogDebug, type: .debug)
isExcluded = true
let reply: (TrustedPeersHelperEgoPeerStatus, Error?) -> Void = {
// Suppress logging of successful replies here; it's not that useful
let logType: OSLogType = $1 == nil ? .debug : .info
- os_log("trustStatus complete: %@ %@",
+ os_log("trustStatus complete: %{public}@ %{public}@",
log: tplogTrace, type: logType, TPPeerStatusToString($0.egoStatus), traceError($1))
self.semaphore.signal()
self.fetchAndPersistChanges { fetchError in
guard fetchError == nil else {
if let error = fetchError {
- os_log("Unable to fetch changes, trust status is unknown: %@", log: tplogDebug, type: .default, error as CVarArg)
+ os_log("Unable to fetch changes, trust status is unknown: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
}
let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: nil,
func fetchTrustState(reply: @escaping (TrustedPeersHelperPeerState?, [TrustedPeersHelperPeer]?, Error?) -> Void) {
let reply: (TrustedPeersHelperPeerState?, [TrustedPeersHelperPeer]?, Error?) -> Void = {
- os_log("fetch trust state complete: %@ %@",
+ os_log("fetch trust state complete: %{public}@ %{public}@",
log: tplogTrace, type: .info, String(reflecting: $0), traceError($2))
reply($0, $1, $2)
}
}
let isPreapproved = self.model.hasPotentiallyTrustedPeerPreapprovingKey(permanentInfo.signingPubKey.spki())
- os_log("fetchTrustState: ego peer is %@", log: tplogDebug, type: .default, isPreapproved ? "preapproved" : "not yet preapproved")
+ os_log("fetchTrustState: ego peer is %{public}@", log: tplogDebug, type: .default, isPreapproved ? "preapproved" : "not yet preapproved")
let egoStableInfo = self.model.getStableInfoForPeer(withID: egoPeerID)
egoPeer.trustedPeerIDs.forEach { trustedPeerID in
if let peer = self.model.peer(withID: trustedPeerID) {
let peerViews = try? self.model.getViewsForPeer(peer.permanentInfo,
- stableInfo: peer.stableInfo,
- inViews: Set())
+ stableInfo: peer.stableInfo)
tphPeers.append(TrustedPeersHelperPeer(peerID: trustedPeerID,
signingSPKI: peer.permanentInfo.signingPubKey.spki(),
encryptionSPKI: peer.permanentInfo.encryptionPubKey.spki(),
viewList: peerViews ?? Set()))
} else {
- os_log("No peer for trusted ID %@", log: tplogDebug, type: .default, trustedPeerID)
+ os_log("No peer for trusted ID %{public}@", log: tplogDebug, type: .default, trustedPeerID)
}
}
} else {
os_log("No ego peer in model; no trusted peers", log: tplogDebug, type: .default)
}
- os_log("Returning trust state: %@ %@", log: tplogDebug, type: .default, egoPeerStatus, tphPeers)
+ os_log("Returning trust state: %{public}@ %@", log: tplogDebug, type: .default, egoPeerStatus, tphPeers)
reply(egoPeerStatus, tphPeers, nil)
} else {
// With no ego peer ID, there are no trusted peers
func dump(reply: @escaping ([AnyHashable: Any]?, Error?) -> Void) {
let reply: ([AnyHashable: Any]?, Error?) -> Void = {
- os_log("dump complete: %@",
+ os_log("dump complete: %{public}@",
log: tplogTrace, type: .info, traceError($1))
reply($0, $1)
}
func dumpEgoPeer(reply: @escaping (String?, TPPeerPermanentInfo?, TPPeerStableInfo?, TPPeerDynamicInfo?, Error?) -> Void) {
let reply: (String?, TPPeerPermanentInfo?, TPPeerStableInfo?, TPPeerDynamicInfo?, Error?) -> Void = {
- os_log("dumpEgoPeer complete: %@", log: tplogTrace, type: .info, traceError($4))
+ os_log("dumpEgoPeer complete: %{public}@", log: tplogTrace, type: .info, traceError($4))
reply($0, $1, $2, $3, $4)
}
self.moc.performAndWait {
func validatePeers(request: ValidatePeersRequest, reply: @escaping ([AnyHashable: Any]?, Error?) -> Void) {
self.semaphore.wait()
let reply: ([AnyHashable: Any]?, Error?) -> Void = {
- os_log("validatePeers complete %@", log: tplogTrace, type: .info, traceError($1))
+ os_log("validatePeers complete %{public}@", log: tplogTrace, type: .info, traceError($1))
self.semaphore.signal()
reply($0, $1)
}
self.cuttlefish.validatePeers(request) { response, error in
- os_log("ValidatePeers(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+ os_log("ValidatePeers(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
guard let response = response, error == nil else {
- os_log("validatePeers failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ os_log("validatePeers failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
reply(nil, error ?? ContainerError.cloudkitResponseMissing)
return
}
}
}
- func getViews(inViews: [String], reply: @escaping ([String]?, Error?) -> Void) {
- let reply: ([String]?, Error?) -> Void = {
- os_log("getViews complete %@", log: tplogTrace, type: .info, traceError($1))
- reply($0, $1)
- }
- self.moc.performAndWait {
- guard let egoPeerID = self.containerMO.egoPeerID,
- let egoPermData = self.containerMO.egoPeerPermanentInfo,
- let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
- let egoStableData = self.containerMO.egoPeerStableInfo,
- let egoStableSig = self.containerMO.egoPeerStableInfoSig
- else {
- os_log("getViews failed to find ego peer information", log: tplogDebug, type: .error)
- reply(nil, ContainerError.noPreparedIdentity)
- return
- }
- guard let stableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
- os_log("getViews failed to create TPPeerStableInfo", log: tplogDebug, type: .error)
- reply(nil, ContainerError.invalidStableInfoOrSig)
- return
- }
-
- let keyFactory = TPECPublicKeyFactory()
- guard let permanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
- os_log("getViews failed to create TPPeerPermanentInfo", log: tplogDebug, type: .error)
- reply(nil, ContainerError.invalidPermanentInfoOrSig)
- return
- }
-
- do {
- let views = try self.model.getViewsForPeer(permanentInfo, stableInfo: stableInfo, inViews: Set(inViews))
- reply(Array(views), nil)
- } catch {
- reply(nil, error)
- return
- }
- }
- }
-
func reset(resetReason: CuttlefishResetReason, reply: @escaping (Error?) -> Void) {
self.semaphore.wait()
let reply: (Error?) -> Void = {
- os_log("reset complete %@", log: tplogTrace, type: .info, traceError($0))
+ os_log("reset complete %{public}@", log: tplogTrace, type: .info, traceError($0))
self.semaphore.signal()
reply($0)
}
$0.resetReason = resetReason
}
self.cuttlefish.reset(request) { response, error in
- os_log("Reset(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+ os_log("Reset(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
guard let response = response, error == nil else {
- os_log("reset failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ os_log("reset failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
reply(error ?? ContainerError.cloudkitResponseMissing)
return
}
os_log("reset succeded", log: tplogDebug, type: .default)
reply(nil)
} catch {
- os_log("reset persist failed: %@", log: tplogDebug, type: .default, (error as CVarArg))
+ os_log("reset persist failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
reply(error)
}
}
func localReset(reply: @escaping (Error?) -> Void) {
self.semaphore.wait()
let reply: (Error?) -> Void = {
- os_log("localReset complete %@", log: tplogTrace, type: .info, traceError($0))
+ os_log("localReset complete %{public}@", log: tplogTrace, type: .info, traceError($0))
self.semaphore.signal()
reply($0)
}
}
}
- // policyVersion should only be non-nil for testing, to override prevailingPolicyVersion
+ // policyVersion should only be non-nil for testing, to override prevailingPolicyVersion.versionNumber
func prepare(epoch: UInt64,
machineID: String,
bottleSalt: String,
deviceName: String?,
serialNumber: String,
osVersion: String,
- policyVersion: UInt64?,
+ policyVersion: TPPolicyVersion?,
policySecrets: [String: Data]?,
signingPrivateKeyPersistentRef: Data?,
encryptionPrivateKeyPersistentRef: Data?,
- reply: @escaping (String?, Data?, Data?, Data?, Data?, Error?) -> Void) {
+ reply: @escaping (String?, Data?, Data?, Data?, Data?, Set<String>?, TPPolicy?, Error?) -> Void) {
self.semaphore.wait()
- let reply: (String?, Data?, Data?, Data?, Data?, Error?) -> Void = {
- os_log("prepare complete peerID: %@ %@",
- log: tplogTrace, type: .info, ($0 ?? "NULL") as CVarArg, traceError($5))
+ let reply: (String?, Data?, Data?, Data?, Data?, Set<String>?, TPPolicy?, Error?) -> Void = {
+ os_log("prepare complete peerID: %{public}@ %{public}@",
+ log: tplogTrace, type: .info, ($0 ?? "NULL") as CVarArg, traceError($7))
self.semaphore.signal()
- reply($0, $1, $2, $3, $4, $5)
+ reply($0, $1, $2, $3, $4, $5, $6, $7)
}
// Create a new peer identity with random keys, and store the keys in keychain
signingKeyPair = try self.loadOrCreateKeyPair(privateKeyPersistentRef: signingPrivateKeyPersistentRef)
encryptionKeyPair = try self.loadOrCreateKeyPair(privateKeyPersistentRef: encryptionPrivateKeyPersistentRef)
+ // <rdar://problem/56270219> Octagon: use epoch transmitted across pairing channel
permanentInfo = try TPPeerPermanentInfo(machineID: machineID,
modelID: modelID,
epoch: 1,
peerIDHashAlgo: TPHashAlgo.SHA256)
} catch {
- reply(nil, nil, nil, nil, nil, error)
+ reply(nil, nil, nil, nil, nil, nil, nil, error)
return
}
_ = try saveSecret(bottle.secret, label: peerID)
} catch {
- os_log("bottle creation failed: %@", log: tplogDebug, type: .default, error as CVarArg)
- reply(nil, nil, nil, nil, nil, error)
+ os_log("bottle creation failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ reply(nil, nil, nil, nil, nil, nil, nil, error)
return
}
saveEgoKeyPair(signingKeyPair, identifier: signingKeyIdentifier(peerID: peerID)) { success, error in
guard success else {
- os_log("Unable to save signing key: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
- reply(nil, nil, nil, nil, nil, error ?? ContainerError.failedToStoreIdentity)
+ os_log("Unable to save signing key: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+ reply(nil, nil, nil, nil, nil, nil, nil, error ?? ContainerError.failedToStoreIdentity)
return
}
saveEgoKeyPair(encryptionKeyPair, identifier: encryptionKeyIdentifier(peerID: peerID)) { success, error in
guard success else {
- os_log("Unable to save encryption key: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
- reply(nil, nil, nil, nil, nil, error ?? ContainerError.failedToStoreIdentity)
+ os_log("Unable to save encryption key: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+ reply(nil, nil, nil, nil, nil, nil, nil, error ?? ContainerError.failedToStoreIdentity)
return
}
- // Save the prepared identity as containerMO.egoPeer* and its bottle
- self.moc.performAndWait {
- do {
- let policyVersion = policyVersion ?? prevailingPolicyVersion
- let policyDoc = try self.getPolicyDoc(policyVersion)
-
- let stableInfo = TPPeerStableInfo(clock: 1,
- policyVersion: policyDoc.policyVersion,
- policyHash: policyDoc.policyHash,
- policySecrets: policySecrets,
- deviceName: deviceName,
- serialNumber: serialNumber,
- osVersion: osVersion,
- signing: signingKeyPair,
- recoverySigningPubKey: nil,
- recoveryEncryptionPubKey: nil,
- error: nil)
-
- self.containerMO.egoPeerID = permanentInfo.peerID
- self.containerMO.egoPeerPermanentInfo = permanentInfo.data
- self.containerMO.egoPeerPermanentInfoSig = permanentInfo.sig
- self.containerMO.egoPeerStableInfo = stableInfo.data
- self.containerMO.egoPeerStableInfoSig = stableInfo.sig
-
- let bottleMO = BottleMO(context: self.moc)
- bottleMO.peerID = bottle.peerID
- bottleMO.bottleID = bottle.bottleID
- bottleMO.escrowedSigningSPKI = bottle.escrowSigningSPKI
- bottleMO.signatureUsingEscrowKey = bottle.signatureUsingEscrowKey
- bottleMO.signatureUsingPeerKey = bottle.signatureUsingPeerKey
- bottleMO.contents = bottle.contents
-
- self.containerMO.addToBottles(bottleMO)
+ let policyVersion = policyVersion ?? prevailingPolicyVersion
+ self.fetchPolicyDocumentWithSemaphore(version: policyVersion) { policyDoc, policyFetchError in
+ guard let policyDoc = policyDoc, policyFetchError == nil else {
+ os_log("Unable to fetch policy: %{public}@", log: tplogDebug, type: .default, (policyFetchError as CVarArg?) ?? "error missing")
+ reply(nil, nil, nil, nil, nil, nil, nil, error ?? ContainerError.unknownInternalError)
+ return
+ }
- try self.moc.save()
+ // Save the prepared identity as containerMO.egoPeer* and its bottle
+ self.moc.performAndWait {
+ do {
- reply(permanentInfo.peerID, permanentInfo.data, permanentInfo.sig, stableInfo.data, stableInfo.sig, nil)
- } catch {
- reply(nil, nil, nil, nil, nil, error)
+ let stableInfo = TPPeerStableInfo(clock: 1,
+ frozenPolicyVersion: frozenPolicyVersion,
+ flexiblePolicyVersion: policyDoc.version,
+ policySecrets: policySecrets,
+ deviceName: deviceName,
+ serialNumber: serialNumber,
+ osVersion: osVersion,
+ signing: signingKeyPair,
+ recoverySigningPubKey: nil,
+ recoveryEncryptionPubKey: nil,
+ error: nil)
+
+ self.containerMO.egoPeerID = permanentInfo.peerID
+ self.containerMO.egoPeerPermanentInfo = permanentInfo.data
+ self.containerMO.egoPeerPermanentInfoSig = permanentInfo.sig
+ self.containerMO.egoPeerStableInfo = stableInfo.data
+ self.containerMO.egoPeerStableInfoSig = stableInfo.sig
+
+ let bottleMO = BottleMO(context: self.moc)
+ bottleMO.peerID = bottle.peerID
+ bottleMO.bottleID = bottle.bottleID
+ bottleMO.escrowedSigningSPKI = bottle.escrowSigningSPKI
+ bottleMO.signatureUsingEscrowKey = bottle.signatureUsingEscrowKey
+ bottleMO.signatureUsingPeerKey = bottle.signatureUsingPeerKey
+ bottleMO.contents = bottle.contents
+
+ self.containerMO.addToBottles(bottleMO)
+
+ let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: permanentInfo, stableInfo: stableInfo)
+
+ try self.moc.save()
+
+ reply(permanentInfo.peerID, permanentInfo.data, permanentInfo.sig, stableInfo.data, stableInfo.sig, syncingViews, policy, nil)
+ } catch {
+ os_log("Unable to save identity: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ reply(nil, nil, nil, nil, nil, nil, nil, error)
+ }
}
}
}
}
func getEgoEpoch(reply: @escaping (UInt64, Error?) -> Void) {
let reply: (UInt64, Error?) -> Void = {
- os_log("getEgoEpoch complete: %d %@", log: tplogTrace, type: .info, $0, traceError($1))
+ os_log("getEgoEpoch complete: %d %{public}@", log: tplogTrace, type: .info, $0, traceError($1))
reply($0, $1)
}
reply: @escaping (String?, [CKRecord], Error?) -> Void) {
self.semaphore.wait()
let reply: (String?, [CKRecord], Error?) -> Void = {
- os_log("establish complete peer: %@ %@",
+ os_log("establish complete peer: %{public}@ %{public}@",
log: tplogTrace, type: .default, ($0 ?? "NULL") as CVarArg, traceError($2))
self.semaphore.signal()
reply($0, $1, $2)
self.onqueueEstablish(ckksKeys: ckksKeys,
tlkShares: tlkShares,
preapprovedKeys: preapprovedKeys,
- reply: reply)
+ reply: { peerID, ckrecords, _, _, error in
+ reply(peerID, ckrecords, error)
+ })
}
}
ttr.trigger()
}
+ func fetchAfterEstablish(ckksKeys: [CKKSKeychainBackedKeySet],
+ tlkShares: [CKKSTLKShare],
+ reply: @escaping (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void) {
+ self.moc.performAndWait {
+ do {
+ try self.deleteLocalCloudKitData()
+ } catch {
+ os_log("fetchAfterEstablish failed to reset local data: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ reply(nil, [], nil, nil, error)
+ return
+ }
+ self.onqueueFetchAndPersistChanges { error in
+ guard error == nil else {
+ os_log("fetchAfterEstablish failed to fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+ reply(nil, [], nil, nil, error)
+ return
+ }
+
+ self.moc.performAndWait {
+ guard let egoPeerID = self.containerMO.egoPeerID,
+ let egoPermData = self.containerMO.egoPeerPermanentInfo,
+ let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
+ let egoStableData = self.containerMO.egoPeerStableInfo,
+ let egoStableSig = self.containerMO.egoPeerStableInfoSig
+ else {
+ os_log("fetchAfterEstablish: failed to fetch egoPeerID", log: tplogDebug, type: .default)
+ reply(nil, [], nil, nil, ContainerError.noPreparedIdentity)
+ return
+ }
+ guard self.model.hasPeer(withID: egoPeerID) else {
+ os_log("fetchAfterEstablish: did not find peer %{public}@ in model", log: tplogDebug, type: .default, egoPeerID)
+ reply(nil, [], nil, nil, ContainerError.invalidPeerID)
+ return
+ }
+ let keyFactory = TPECPublicKeyFactory()
+ guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
+ reply(nil, [], nil, nil, ContainerError.invalidPermanentInfoOrSig)
+ return
+ }
+ guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
+ os_log("cannot create TPPeerStableInfo", log: tplogDebug, type: .default)
+ reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
+ return
+ }
+ self.onqueueUpdateTLKs(ckksKeys: ckksKeys, tlkShares: tlkShares) { ckrecords, error in
+ guard error == nil else {
+ os_log("fetchAfterEstablish failed to update TLKs: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+ reply(nil, [], nil, nil, error)
+ return
+ }
+
+ do {
+ let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: selfPermanentInfo,
+ stableInfo: selfStableInfo)
+ os_log("fetchAfterEstablish succeeded", log: tplogDebug, type: .default)
+ reply(egoPeerID, ckrecords ?? [], syncingViews, policy, nil)
+ } catch {
+ os_log("fetchAfterEstablish failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
+ reply(nil, [], nil, nil, error)
+ }
+ }
+ }
+ }
+ }
+ }
+
func onqueueEstablish(ckksKeys: [CKKSKeychainBackedKeySet],
tlkShares: [CKKSTLKShare],
preapprovedKeys: [Data]?,
- reply: @escaping (String?, [CKRecord], Error?) -> Void) {
+ reply: @escaping (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void) {
// Fetch ego peer identity from local storage.
guard let egoPeerID = self.containerMO.egoPeerID,
let egoPermData = self.containerMO.egoPeerPermanentInfo,
let egoStableData = self.containerMO.egoPeerStableInfo,
let egoStableSig = self.containerMO.egoPeerStableInfoSig
else {
- reply(nil, [], ContainerError.noPreparedIdentity)
+ reply(nil, [], nil, nil, ContainerError.noPreparedIdentity)
return
}
let keyFactory = TPECPublicKeyFactory()
guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
- reply(nil, [], ContainerError.invalidPermanentInfoOrSig)
+ reply(nil, [], nil, nil, ContainerError.invalidPermanentInfoOrSig)
return
}
guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
os_log("cannot create TPPeerStableInfo", log: tplogDebug, type: .default)
- reply(nil, [], ContainerError.invalidStableInfoOrSig)
+ reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
return
}
guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
- os_log("establish: self machineID %@ not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
+ os_log("establish: self machineID %{public}@ not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
self.onqueueTTRUntrusted()
- reply(nil, [], ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
+ reply(nil, [], nil, nil, ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
return
}
loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
guard let egoPeerKeys = egoPeerKeys else {
- os_log("Don't have my own peer keys; can't establish: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
- reply(nil, [], error)
+ os_log("Don't have my own peer keys; can't establish: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+ reply(nil, [], nil, nil, error)
return
}
self.moc.performAndWait {
allTLKShares = octagonShares + sosShares
} catch {
- os_log("Unable to make TLKShares for self: %@", log: tplogDebug, type: .default, error as CVarArg)
- reply(nil, [], error)
+ os_log("Unable to make TLKShares for self: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ reply(nil, [], nil, nil, error)
return
}
signing: egoPeerKeys.signingKey,
currentMachineIDs: self.onqueueCurrentMIDList())
- os_log("dynamic info: %@", log: tplogDebug, type: .default, dynamicInfo)
+ os_log("dynamic info: %{public}@", log: tplogDebug, type: .default, dynamicInfo)
} catch {
- reply(nil, [], error)
+ reply(nil, [], nil, nil, error)
return
}
do {
bottle = try self.assembleBottle(egoPeerID: egoPeerID)
} catch {
- reply(nil, [], error)
+ reply(nil, [], nil, nil, error)
return
}
- os_log("Beginning establish for peer %@", log: tplogDebug, type: .default, egoPeerID)
- os_log("Establish permanentInfo: %@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
- os_log("Establish permanentInfoSig: %@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
- os_log("Establish stableInfo: %@", log: tplogDebug, type: .debug, egoStableData.base64EncodedString())
- os_log("Establish stableInfoSig: %@", log: tplogDebug, type: .debug, egoStableSig.base64EncodedString())
- os_log("Establish dynamicInfo: %@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
- os_log("Establish dynamicInfoSig: %@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
+ os_log("Beginning establish for peer %{public}@", log: tplogDebug, type: .default, egoPeerID)
+ os_log("Establish permanentInfo: %{public}@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
+ os_log("Establish permanentInfoSig: %{public}@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
+ os_log("Establish stableInfo: %{public}@", log: tplogDebug, type: .debug, egoStableData.base64EncodedString())
+ os_log("Establish stableInfoSig: %{public}@", log: tplogDebug, type: .debug, egoStableSig.base64EncodedString())
+ os_log("Establish dynamicInfo: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
+ os_log("Establish dynamicInfoSig: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
os_log("Establish introducing %d key sets, %d tlk shares", log: tplogDebug, type: .default, viewKeys.count, allTLKShares.count)
do {
- os_log("Establish bottle: %@", log: tplogDebug, type: .debug, try bottle.serializedData().base64EncodedString())
- os_log("Establish peer: %@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
+ os_log("Establish bottle: %{public}@", log: tplogDebug, type: .debug, try bottle.serializedData().base64EncodedString())
+ os_log("Establish peer: %{public}@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
} catch {
- os_log("Establish unable to encode bottle/peer: %@", log: tplogDebug, type: .debug, error as CVarArg)
+ os_log("Establish unable to encode bottle/peer: %{public}@", log: tplogDebug, type: .debug, error as CVarArg)
}
let request = EstablishRequest.with {
$0.tlkShares = allTLKShares
}
self.cuttlefish.establish(request) { response, error in
- os_log("Establish(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
- os_log("Establish: viewKeys: %@", String(describing: viewKeys))
+ os_log("Establish(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+ os_log("Establish: viewKeys: %{public}@", String(describing: viewKeys))
guard let response = response, error == nil else {
- os_log("establish failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
- reply(nil, [], error ?? ContainerError.cloudkitResponseMissing)
- return
+ switch error {
+ case CuttlefishErrorMatcher(code: CuttlefishErrorCode.establishFailed):
+ os_log("establish returned failed, trying fetch", log: tplogDebug, type: .default)
+ self.fetchAfterEstablish(ckksKeys: ckksKeys, tlkShares: tlkShares, reply: reply)
+ return
+ default:
+ os_log("establish failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ reply(nil, [], nil, nil, error ?? ContainerError.cloudkitResponseMissing)
+ return
+ }
}
do {
- os_log("Establish returned changes: %@", log: tplogDebug, type: .default, try response.changes.jsonString())
+ os_log("Establish returned changes: %{public}@", log: tplogDebug, type: .default, try response.changes.jsonString())
} catch {
os_log("Establish returned changes, but they can't be serialized", log: tplogDebug, type: .default)
}
let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
do {
+ let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: selfPermanentInfo,
+ stableInfo: selfStableInfo)
+
try self.persist(changes: response.changes)
guard response.changes.more == false else {
self.fetchAndPersistChanges { fetchError in
guard fetchError == nil else {
// This is an odd error condition: we might be able to fetch again and be in a good state...
- os_log("fetch-after-establish failed: %@", log: tplogDebug, type: .default, (fetchError as CVarArg?) ?? "no error")
- reply(nil, keyHierarchyRecords, fetchError)
+ os_log("fetch-after-establish failed: %{public}@", log: tplogDebug, type: .default, (fetchError as CVarArg?) ?? "no error")
+ reply(nil, keyHierarchyRecords, nil, nil, fetchError)
return
}
os_log("fetch-after-establish succeeded", log: tplogDebug, type: .default)
- reply(egoPeerID, keyHierarchyRecords, nil)
+ reply(egoPeerID, keyHierarchyRecords, nil, nil, nil)
}
return
}
os_log("establish succeeded", log: tplogDebug, type: .default)
- reply(egoPeerID, keyHierarchyRecords, nil)
+ reply(egoPeerID, keyHierarchyRecords, syncingViews, policy, nil)
} catch {
- os_log("establish handling failed: %@", log: tplogDebug, type: .default, (error as CVarArg))
- reply(nil, keyHierarchyRecords, error)
+ os_log("establish handling failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
+ reply(nil, keyHierarchyRecords, nil, nil, error)
}
}
}
func setRecoveryKey(recoveryKey: String, salt: String, ckksKeys: [CKKSKeychainBackedKeySet], reply: @escaping (Error?) -> Void) {
self.semaphore.wait()
let reply: (Error?) -> Void = {
- os_log("setRecoveryKey complete: %@", log: tplogTrace, type: .info, traceError($0))
+ os_log("setRecoveryKey complete: %{public}@", log: tplogTrace, type: .info, traceError($0))
self.semaphore.signal()
reply($0)
}
do {
recoveryKeys = try RecoveryKey(recoveryKeyString: recoveryKey, recoverySalt: salt)
} catch {
- os_log("failed to create recovery keys: %@", log: tplogDebug, type: .default, error as CVarArg)
+ os_log("failed to create recovery keys: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
reply(ContainerError.failedToCreateRecoveryKey)
return
}
let signingPublicKey: Data = recoveryKeys.peerKeys.signingVerificationKey.keyData
let encryptionPublicKey: Data = recoveryKeys.peerKeys.encryptionVerificationKey.keyData
- os_log("setRecoveryKey signingPubKey: %@", log: tplogDebug, type: .debug, signingPublicKey.base64EncodedString())
- os_log("setRecoveryKey encryptionPubKey: %@", log: tplogDebug, type: .debug, encryptionPublicKey.base64EncodedString())
+ os_log("setRecoveryKey signingPubKey: %@", log: tplogDebug, type: .default, signingPublicKey.base64EncodedString())
+ os_log("setRecoveryKey encryptionPubKey: %@", log: tplogDebug, type: .default, encryptionPublicKey.base64EncodedString())
guard let stableInfoData = self.containerMO.egoPeerStableInfo else {
os_log("stableInfo does not exist", log: tplogDebug, type: .default)
loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
guard let signingKeyPair = signingKeyPair else {
- os_log("handle: no signing key pair: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ os_log("handle: no signing key pair: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
reply(error)
return
}
toPeer: recoveryKeys.peerKeys,
epoch: Int(permanentInfo.epoch))
- let policyVersion = stableInfo.policyVersion
- let policyDoc = try self.getPolicyDoc(policyVersion)
+ let policyVersion = stableInfo.bestPolicyVersion()
+ let policyDoc = try self.getPolicyDoc(policyVersion.versionNumber)
let updatedStableInfo = TPPeerStableInfo(clock: stableInfo.clock + 1,
- policyVersion: policyDoc.policyVersion,
- policyHash: policyDoc.policyHash,
+ frozenPolicyVersion: frozenPolicyVersion,
+ flexiblePolicyVersion: policyDoc.version,
policySecrets: stableInfo.policySecrets,
deviceName: stableInfo.deviceName,
serialNumber: stableInfo.serialNumber,
}
self.cuttlefish.setRecoveryKey(request) { response, error in
- os_log("SetRecoveryKey(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+ os_log("SetRecoveryKey(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
guard let response = response, error == nil else {
- os_log("setRecoveryKey failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ os_log("setRecoveryKey failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
reply(error ?? ContainerError.cloudkitResponseMissing)
return
}
os_log("setRecoveryKey succeeded", log: tplogDebug, type: .default)
reply(nil)
} catch {
- os_log("setRecoveryKey handling failed: %@", log: tplogDebug, type: .default, (error as CVarArg))
+ os_log("setRecoveryKey handling failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
reply(error)
}
}
if shouldPerformFetch == true {
self.fetchViableBottlesWithSemaphore { _, _, error in
guard error == nil else {
- os_log("fetchViableBottles failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ os_log("fetchViableBottles failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
reply(nil, error)
return
}
guard let newBottles = self.containerMO.bottles as? Set<BottleMO> else {
- os_log("no bottles on container: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ os_log("no bottles on container: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
reply(nil, ContainerError.noBottlesPresent)
return
}
return
}
- os_log("onqueueFindBottle found bottle: %@", log: tplogDebug, type: .default, newBottles)
+ os_log("onqueueFindBottle found bottle: %{public}@", log: tplogDebug, type: .default, newBottles)
bottles = newBottles.filter {
$0.bottleID == bottleID
_ = try BottledPeer.verifyBottleSignature(data: bottledContents, signature: signatureUsingPeerKey, pubKey: signingKey)
} catch {
- os_log("Verification of bottled signature failed: %@", log: tplogDebug, type: .default, error as CVarArg)
+ os_log("Verification of bottled signature failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
throw ContainerError.failedToCreateBottledPeer
}
signatureUsingEscrow: signatureUsingEscrowKey,
signatureUsingPeerKey: signatureUsingPeerKey)
} catch {
- os_log("Creation of Bottled Peer failed: %@", log: tplogDebug, type: .default, error as CVarArg)
+ os_log("Creation of Bottled Peer failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
throw ContainerError.failedToCreateBottledPeer
}
}
}
- func preflightVouchWithBottle(bottleID: String,
- reply: @escaping (String?, Error?) -> Void) {
- self.semaphore.wait()
- let reply: (String?, Error?) -> Void = {
- os_log("preflightVouchWithBottle complete: %@",
- log: tplogTrace, type: .info, traceError($1))
- self.semaphore.signal()
- reply($0, $1)
- }
-
- self.moc.performAndWait {
- self.onqueueFindBottle(bottleID: bottleID) { bottleMO, error in
- guard let bottleMO = bottleMO else {
- os_log("preflightVouchWithBottle found no bottle: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
- reply(nil, error)
- return
- }
-
- reply(bottleMO.peerID, nil)
- }
- }
- }
-
func vouchWithBottle(bottleID: String,
entropy: Data,
bottleSalt: String,
tlkShares: [CKKSTLKShare],
- reply: @escaping (Data?, Data?, Error?) -> Void) {
+ reply: @escaping (Data?, Data?, Int64, Int64, Error?) -> Void) {
self.semaphore.wait()
- let reply: (Data?, Data?, Error?) -> Void = {
- os_log("vouchWithBottle complete: %@",
- log: tplogTrace, type: .info, traceError($2))
+ let reply: (Data?, Data?, Int64, Int64, Error?) -> Void = {
+ os_log("vouchWithBottle complete: %{public}@",
+ log: tplogTrace, type: .info, traceError($4))
self.semaphore.signal()
- reply($0, $1, $2)
+ reply($0, $1, $2, $3, $4)
}
- self.fetchAndPersistChanges { error in
+ self.fetchAndPersistChangesIfNeeded { error in
guard error == nil else {
- os_log("vouchWithBottle unable to fetch changes: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
- reply(nil, nil, error)
+ os_log("vouchWithBottle unable to fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
+ reply(nil, nil, 0, 0, error)
return
}
self.onqueueFindBottle(bottleID: bottleID) { returnedBMO, error in
self.moc.performAndWait {
guard error == nil else {
- os_log("vouchWithBottle unable to find bottle for escrow record id: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
- reply(nil, nil, error)
+ os_log("vouchWithBottle unable to find bottle for escrow record id: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
+ reply(nil, nil, 0, 0, error)
return
}
guard let bmo: BottleMO = returnedBMO else {
- os_log("vouchWithBottle bottle is nil: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
- reply(nil, nil, error)
+ os_log("vouchWithBottle bottle is nil: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
+ reply(nil, nil, 0, 0, error)
return
}
guard let bottledContents = bmo.contents else {
- reply(nil, nil, ContainerError.bottleDoesNotContainContents)
+ reply(nil, nil, 0, 0, ContainerError.bottleDoesNotContainContents)
return
}
guard let signatureUsingEscrowKey = bmo.signatureUsingEscrowKey else {
- reply(nil, nil, ContainerError.bottleDoesNotContainEscrowKeySignature)
+ reply(nil, nil, 0, 0, ContainerError.bottleDoesNotContainEscrowKeySignature)
return
}
guard let signatureUsingPeerKey = bmo.signatureUsingPeerKey else {
- reply(nil, nil, ContainerError.bottleDoesNotContainerPeerKeySignature)
+ reply(nil, nil, 0, 0, ContainerError.bottleDoesNotContainerPeerKeySignature)
return
}
guard let sponsorPeerID = bmo.peerID else {
- reply(nil, nil, ContainerError.bottleDoesNotContainPeerID)
+ reply(nil, nil, 0, 0, ContainerError.bottleDoesNotContainPeerID)
return
}
do {
guard let sponsorPeer = self.model.peer(withID: sponsorPeerID) else {
os_log("vouchWithBottle: Unable to find peer that created the bottle", log: tplogDebug, type: .default)
- reply(nil, nil, ContainerError.bottleCreatingPeerNotFound)
+ reply(nil, nil, 0, 0, ContainerError.bottleCreatingPeerNotFound)
return
}
guard let signingKey: _SFECPublicKey = sponsorPeer.permanentInfo.signingPubKey as? _SFECPublicKey else {
os_log("vouchWithBottle: Unable to create a sponsor public key", log: tplogDebug, type: .default)
- reply(nil, nil, ContainerError.signatureVerificationFailed)
+ reply(nil, nil, 0, 0, ContainerError.signatureVerificationFailed)
return
}
_ = try BottledPeer.verifyBottleSignature(data: bottledContents, signature: signatureUsingPeerKey, pubKey: signingKey)
} catch {
- os_log("vouchWithBottle: Verification of bottled signature failed: %@", log: tplogDebug, type: .default, error as CVarArg)
- reply(nil, nil, ContainerError.failedToCreateBottledPeer)
+ os_log("vouchWithBottle: Verification of bottled signature failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ reply(nil, nil, 0, 0, ContainerError.failedToCreateBottledPeer)
return
}
signatureUsingPeerKey: signatureUsingPeerKey)
} catch {
- os_log("Creation of Bottled Peer failed: %@", log: tplogDebug, type: .default, error as CVarArg)
- reply(nil, nil, ContainerError.failedToCreateBottledPeer)
+ os_log("Creation of Bottled Peer failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ reply(nil, nil, 0, 0, ContainerError.failedToCreateBottledPeer)
return
}
}
- os_log("Have a bottle for peer %@", log: tplogDebug, type: .default, bottledPeer.peerID)
+ os_log("Have a bottle for peer %{public}@", log: tplogDebug, type: .default, bottledPeer.peerID)
// Extract any TLKs we have been given
- extract(tlkShares: tlkShares, peer: bottledPeer.peerKeys)
+ let (uniqueTLKsRecovered, totalSharesRecovered) = extract(tlkShares: tlkShares, peer: bottledPeer.peerKeys, model: self.model)
self.moc.performAndWait {
// I must have an ego identity in order to vouch using bottle
guard let egoPeerID = self.containerMO.egoPeerID else {
os_log("As a nonmember, can't vouch for someone else", log: tplogDebug, type: .default)
- reply(nil, nil, ContainerError.nonMember)
+ reply(nil, nil, 0, 0, ContainerError.nonMember)
return
}
guard let permanentInfo = self.containerMO.egoPeerPermanentInfo else {
os_log("permanentInfo does not exist", log: tplogDebug, type: .default)
- reply(nil, nil, ContainerError.nonMember)
+ reply(nil, nil, 0, 0, ContainerError.nonMember)
return
}
guard let permanentInfoSig = self.containerMO.egoPeerPermanentInfoSig else {
os_log("permanentInfoSig does not exist", log: tplogDebug, type: .default)
- reply(nil, nil, ContainerError.nonMember)
+ reply(nil, nil, 0, 0, ContainerError.nonMember)
return
}
guard let stableInfo = self.containerMO.egoPeerStableInfo else {
os_log("stableInfo does not exist", log: tplogDebug, type: .default)
- reply(nil, nil, ContainerError.nonMember)
+ reply(nil, nil, 0, 0, ContainerError.nonMember)
return
}
guard let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
os_log("stableInfoSig does not exist", log: tplogDebug, type: .default)
- reply(nil, nil, ContainerError.nonMember)
+ reply(nil, nil, 0, 0, ContainerError.nonMember)
return
}
let keyFactory = TPECPublicKeyFactory()
guard let beneficiaryPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: permanentInfo, sig: permanentInfoSig, keyFactory: keyFactory) else {
os_log("Invalid permenent info or signature; can't vouch for them", log: tplogDebug, type: .default)
- reply(nil, nil, ContainerError.invalidPermanentInfoOrSig)
+ reply(nil, nil, 0, 0, ContainerError.invalidPermanentInfoOrSig)
return
}
guard let beneficiaryStableInfo = TPPeerStableInfo(data: stableInfo, sig: stableInfoSig) else {
os_log("Invalid stableinfo or signature; van't vouch for them", log: tplogDebug, type: .default)
- reply(nil, nil, ContainerError.invalidStableInfoOrSig)
+ reply(nil, nil, 0, 0, ContainerError.invalidStableInfoOrSig)
return
}
withSponsorID: sponsorPeerID,
reason: TPVoucherReason.restore,
signing: bottledPeer.peerKeys.signingKey)
- reply(voucher.data, voucher.sig, nil)
+ reply(voucher.data, voucher.sig, uniqueTLKsRecovered, totalSharesRecovered, nil)
return
} catch {
- os_log("Error creating voucher: %@", log: tplogDebug, type: .default, error as CVarArg)
- reply(nil, nil, error)
+ os_log("Error creating voucher with bottle: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ reply(nil, nil, 0, 0, error)
return
}
}
reply: @escaping (Data?, Data?, Error?) -> Void) {
self.semaphore.wait()
let reply: (Data?, Data?, Error?) -> Void = {
- os_log("vouchWithRecoveryKey complete: %@",
+ os_log("vouchWithRecoveryKey complete: %{public}@",
log: tplogTrace, type: .info, traceError($2))
self.semaphore.signal()
reply($0, $1, $2)
do {
recoveryKeys = try RecoveryKey(recoveryKeyString: recoveryKey, recoverySalt: salt)
} catch {
- os_log("failed to create recovery keys: %@", log: tplogDebug, type: .default, error as CVarArg)
+ os_log("failed to create recovery keys: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
reply(nil, nil, ContainerError.failedToCreateRecoveryKey)
return
}
- extract(tlkShares: tlkShares, peer: recoveryKeys.peerKeys)
+ extract(tlkShares: tlkShares, peer: recoveryKeys.peerKeys, model: self.model)
let signingPublicKey: Data = recoveryKeys.peerKeys.signingKey.publicKey.keyData
let encryptionPublicKey: Data = recoveryKeys.peerKeys.encryptionKey.publicKey.keyData
- os_log("vouchWithRecoveryKey signingPubKey: %@", log: tplogDebug, type: .debug, signingPublicKey.base64EncodedString())
- os_log("vouchWithRecoveryKey encryptionPubKey: %@", log: tplogDebug, type: .debug, encryptionPublicKey.base64EncodedString())
+ os_log("vouchWithRecoveryKey signingPubKey: %@", log: tplogDebug, type: .default, signingPublicKey.base64EncodedString())
+ os_log("vouchWithRecoveryKey encryptionPubKey: %@", log: tplogDebug, type: .default, encryptionPublicKey.base64EncodedString())
guard self.model.isRecoveryKeyEnrolled() else {
os_log("Recovery Key is not enrolled", log: tplogDebug, type: .default)
reply(voucher.data, voucher.sig, nil)
return
} catch {
- os_log("Error creating voucher using recovery key set: %@", log: tplogDebug, type: .default, error as CVarArg)
+ os_log("Error creating voucher using recovery key set: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
reply(nil, nil, error)
return
}
reply: @escaping (Data?, Data?, Error?) -> Void) {
self.semaphore.wait()
let reply: (Data?, Data?, Error?) -> Void = {
- os_log("vouch complete: %@", log: tplogTrace, type: .info, traceError($2))
+ os_log("vouch complete: %{public}@", log: tplogTrace, type: .info, traceError($2))
self.semaphore.signal()
reply($0, $1, $2)
}
loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
guard let egoPeerKeys = egoPeerKeys else {
- os_log("Don't have my own keys: can't vouch for %@: %@", log: tplogDebug, type: .default, beneficiaryPermanentInfo, (error as CVarArg?) ?? "no error")
+ os_log("Don't have my own keys: can't vouch for %{public}@(%{public}@): %{public}@", log: tplogDebug, type: .default, peerID, beneficiaryPermanentInfo, (error as CVarArg?) ?? "no error")
reply(nil, nil, error)
return
}
- self.moc.performAndWait {
- let voucher: TPVoucher
- do {
- voucher = try self.model.createVoucher(forCandidate: beneficiaryPermanentInfo,
- stableInfo: beneficiaryStableInfo,
- withSponsorID: egoPeerID,
- reason: TPVoucherReason.secureChannel,
- signing: egoPeerKeys.signingKey)
- } catch {
- os_log("Error creating voucher: %@", log: tplogDebug, type: .default, error as CVarArg)
- reply(nil, nil, error)
+ self.fetchPolicyDocumentsWithSemaphore(versions: Set([beneficiaryStableInfo.bestPolicyVersion()])) { _, policyFetchError in
+ guard policyFetchError == nil else {
+ os_log("Unknown policy for beneficiary: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ reply(nil, nil, policyFetchError)
return
}
- // And generate and upload any tlkShares
- // Note that this might not be the whole list: <rdar://problem/47899980> Octagon: Limited Peers
- let tlkShares: [TLKShare]
- do {
- // Note: we only want to send up TLKs for uploaded ckks zones
- let ckksTLKs = ckksKeys.filter { !$0.newUpload }.map { $0.tlk }
+ self.moc.performAndWait {
+ let voucher: TPVoucher
+ do {
+ voucher = try self.model.createVoucher(forCandidate: beneficiaryPermanentInfo,
+ stableInfo: beneficiaryStableInfo,
+ withSponsorID: egoPeerID,
+ reason: TPVoucherReason.secureChannel,
+ signing: egoPeerKeys.signingKey)
+ } catch {
+ os_log("Error creating voucher: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ reply(nil, nil, error)
+ return
+ }
- tlkShares = try makeTLKShares(ckksTLKs: ckksTLKs,
- asPeer: egoPeerKeys,
- toPeer: beneficiaryPermanentInfo,
- epoch: Int(selfPermanentInfo.epoch))
- } catch {
- os_log("Unable to make TLKShares for beneficiary %@: %@", log: tplogDebug, type: .default, beneficiaryPermanentInfo, error as CVarArg)
- reply(nil, nil, error)
- return
- }
+ // And generate and upload any tlkShares
+ let tlkShares: [TLKShare]
+ do {
+ // Note that this might not be the whole list, so filter some of them out
+ let peerViews = try? self.model.getViewsForPeer(beneficiaryPermanentInfo,
+ stableInfo: beneficiaryStableInfo)
+
+ // Note: we only want to send up TLKs for uploaded ckks zones
+ let ckksTLKs = ckksKeys
+ .filter { !$0.newUpload }
+ .filter { peerViews?.contains($0.tlk.zoneID.zoneName) ?? false }
+ .map { $0.tlk }
+
+ tlkShares = try makeTLKShares(ckksTLKs: ckksTLKs,
+ asPeer: egoPeerKeys,
+ toPeer: beneficiaryPermanentInfo,
+ epoch: Int(selfPermanentInfo.epoch))
+ } catch {
+ os_log("Unable to make TLKShares for beneficiary %{public}@(%{public}@): %{public}@", log: tplogDebug, type: .default, peerID, beneficiaryPermanentInfo, error as CVarArg)
+ reply(nil, nil, error)
+ return
+ }
- guard !tlkShares.isEmpty else {
- os_log("No TLKShares to upload for new peer, returning voucher", log: tplogDebug, type: .default)
- reply(voucher.data, voucher.sig, nil)
- return
- }
+ guard !tlkShares.isEmpty else {
+ os_log("No TLKShares to upload for new peer, returning voucher", log: tplogDebug, type: .default)
+ reply(voucher.data, voucher.sig, nil)
+ return
+ }
- self.cuttlefish.updateTrust(changeToken: self.containerMO.changeToken ?? "",
- peerID: egoPeerID,
- stableInfoAndSig: nil,
- dynamicInfoAndSig: nil,
- tlkShares: tlkShares,
- viewKeys: []) { response, error in
- guard let response = response, error == nil else {
- os_log("Unable to upload new tlkshares: %@", log: tplogDebug, type: .default, error as CVarArg? ?? "no error")
- reply(voucher.data, voucher.sig, error ?? ContainerError.cloudkitResponseMissing)
- return
- }
-
- let newKeyRecords = response.zoneKeyHierarchyRecords.map(CKRecord.init)
- os_log("Uploaded new tlkshares: %@", log: tplogDebug, type: .default, newKeyRecords)
- // We don't need to save these; CKKS will refetch them as needed
-
- reply(voucher.data, voucher.sig, nil)
+ self.cuttlefish.updateTrust(changeToken: self.containerMO.changeToken ?? "",
+ peerID: egoPeerID,
+ stableInfoAndSig: nil,
+ dynamicInfoAndSig: nil,
+ tlkShares: tlkShares,
+ viewKeys: []) { response, error in
+ guard let response = response, error == nil else {
+ os_log("Unable to upload new tlkshares: %{public}@", log: tplogDebug, type: .default, error as CVarArg? ?? "no error")
+ reply(voucher.data, voucher.sig, error ?? ContainerError.cloudkitResponseMissing)
+ return
+ }
+
+ let newKeyRecords = response.zoneKeyHierarchyRecords.map(CKRecord.init)
+ os_log("Uploaded new tlkshares: %@", log: tplogDebug, type: .default, newKeyRecords)
+ // We don't need to save these; CKKS will refetch them as needed
+
+ reply(voucher.data, voucher.sig, nil)
+ }
}
}
}
func departByDistrustingSelf(reply: @escaping (Error?) -> Void) {
self.semaphore.wait()
let reply: (Error?) -> Void = {
- os_log("departByDistrustingSelf complete: %@", log: tplogTrace, type: .info, traceError($0))
+ os_log("departByDistrustingSelf complete: %{public}@", log: tplogTrace, type: .info, traceError($0))
self.semaphore.signal()
reply($0)
}
reply: @escaping (Error?) -> Void) {
self.semaphore.wait()
let reply: (Error?) -> Void = {
- os_log("distrust complete: %@", log: tplogTrace, type: .info, traceError($0))
+ os_log("distrust complete: %{public}@", log: tplogTrace, type: .info, traceError($0))
self.semaphore.signal()
reply($0)
}
loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
guard let signingKeyPair = signingKeyPair else {
- os_log("No longer have signing key pair; can't sign distrust: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "nil")
+ os_log("No longer have signing key pair; can't sign distrust: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "nil")
reply(error)
return
}
currentMachineIDs: self.onqueueCurrentMIDList())
} catch {
- os_log("Error preparing dynamic info: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "nil")
+ os_log("Error preparing dynamic info: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "nil")
reply(error)
return
}
let signedDynamicInfo = SignedPeerDynamicInfo(dynamicInfo)
- os_log("attempting distrust for %@ with: %@", log: tplogDebug, type: .default, peerIDs, dynamicInfo)
+ os_log("attempting distrust for %{public}@ with: %{public}@", log: tplogDebug, type: .default, peerIDs, dynamicInfo)
let request = UpdateTrustRequest.with {
$0.changeToken = self.containerMO.changeToken ?? ""
$0.dynamicInfoAndSig = signedDynamicInfo
}
self.cuttlefish.updateTrust(request) { response, error in
- os_log("UpdateTrust(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+ os_log("UpdateTrust(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
guard let response = response, error == nil else {
- os_log("updateTrust failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ os_log("updateTrust failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
reply(error ?? ContainerError.cloudkitResponseMissing)
return
}
os_log("distrust succeeded", log: tplogDebug, type: .default)
reply(nil)
} catch {
- os_log("distrust handling failed: %@", log: tplogDebug, type: .default, (error as CVarArg))
+ os_log("distrust handling failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
reply(error)
}
}
func fetchEscrowContents(reply: @escaping (Data?, String?, Data?, Error?) -> Void) {
self.semaphore.wait()
let reply: (Data?, String?, Data?, Error?) -> Void = {
- os_log("fetchEscrowContents complete: %@", log: tplogTrace, type: .info, traceError($3))
+ os_log("fetchEscrowContents complete: %{public}@", log: tplogTrace, type: .info, traceError($3))
self.semaphore.signal()
reply($0, $1, $2, $3)
}
}
entropy = loaded
} catch {
- os_log("fetchEscrowContents failed to load entropy: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ os_log("fetchEscrowContents failed to load entropy: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
reply(nil, nil, nil, error)
return
}
func fetchViableBottles(reply: @escaping ([String]?, [String]?, Error?) -> Void) {
self.semaphore.wait()
let reply: ([String]?, [String]?, Error?) -> Void = {
- os_log("fetchViableBottles complete: %@", log: tplogTrace, type: .info, traceError($2))
+ os_log("fetchViableBottles complete: %{public}@", log: tplogTrace, type: .info, traceError($2))
self.semaphore.signal()
reply($0, $1, $2)
}
let cachedBottles: TPCachedViableBottles = self.model.currentCachedViableBottlesSet()
self.moc.performAndWait {
if self.onqueueCachedBottlesContainEgoPeerBottle(cachedBottles: cachedBottles)
- && (cachedBottles.viableBottles.count > 0 || cachedBottles.partialBottles.count > 0) {
+ && (!cachedBottles.viableBottles.isEmpty || !cachedBottles.partialBottles.isEmpty) {
os_log("returning from fetchViableBottles, using cached bottles", log: tplogDebug, type: .default)
reply(cachedBottles.viableBottles, cachedBottles.partialBottles, nil)
return
self.cuttlefish.fetchViableBottles { response, error in
guard error == nil else {
- os_log("fetchViableBottles failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ os_log("fetchViableBottles failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
reply(nil, nil, error)
return
}
self.moc.performAndWait {
guard let escrowPairs = response?.viableBottles else {
- os_log("fetchViableBottles returned no viable bottles: %@", log: tplogDebug, type: .default)
+ os_log("fetchViableBottles returned no viable bottles", log: tplogDebug, type: .default)
reply([], [], nil)
return
}
if let partial = response?.partialBottles {
partialPairs = partial
} else {
- os_log("fetchViableBottles returned no partially viable bottles, but that's ok: %@", log: tplogDebug, type: .default)
+ os_log("fetchViableBottles returned no partially viable bottles, but that's ok", log: tplogDebug, type: .default)
}
let viableBottleIDs = escrowPairs.compactMap { $0.bottle.bottleID }
- os_log("fetchViableBottles returned viable bottles: %@", log: tplogDebug, type: .default, viableBottleIDs)
+ os_log("fetchViableBottles returned viable bottles: %{public}@", log: tplogDebug, type: .default, viableBottleIDs)
let partialBottleIDs = partialPairs.compactMap { $0.bottle.bottleID }
- os_log("fetchViableBottles returned partial bottles: %@", log: tplogDebug, type: .default, partialBottleIDs)
+ os_log("fetchViableBottles returned partial bottles: %{public}@", log: tplogDebug, type: .default, partialBottleIDs)
escrowPairs.forEach { pair in
let bottle = pair.bottle
bmo.signatureUsingPeerKey = bottle.signatureUsingPeerKey
bmo.contents = bottle.contents
- os_log("fetchViableBottles saving new bottle: %@", log: tplogDebug, type: .default, bmo)
+ os_log("fetchViableBottles saving new bottle: %{public}@", log: tplogDebug, type: .default, bmo)
self.containerMO.addToBottles(bmo)
}
bmo.signatureUsingPeerKey = bottle.signatureUsingPeerKey
bmo.contents = bottle.contents
- os_log("fetchViableBottles saving new bottle: %@", log: tplogDebug, type: .default, bmo)
+ os_log("fetchViableBottles saving new bottle: %{public}@", log: tplogDebug, type: .default, bmo)
self.containerMO.addToBottles(bmo)
}
self.model.setViableBottles(cached)
reply(viableBottleIDs, partialBottleIDs, nil)
} catch {
- os_log("fetchViableBottles unable to save bottles: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ os_log("fetchViableBottles unable to save bottles: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
reply(nil, nil, error)
}
}
}
- func fetchPolicy(reply: @escaping (TPPolicy?, Error?) -> Void) {
+ func fetchCurrentPolicy(reply: @escaping (Set<String>?, TPPolicy?, Error?) -> Void) {
self.semaphore.wait()
- let reply: (TPPolicy?, Error?) -> Void = {
- os_log("fetchPolicy complete: %@", log: tplogTrace, type: .info, traceError($1))
+ let reply: (Set<String>?, TPPolicy?, Error?) -> Void = {
+ os_log("fetchCurrentPolicy complete: %{public}@", log: tplogTrace, type: .info, traceError($2))
self.semaphore.signal()
- reply($0, $1)
+ reply($0, $1, $2)
}
self.moc.performAndWait {
- var keys: [NSNumber: String] = [:]
-
- guard let stableInfoData = self.containerMO.egoPeerStableInfo,
- let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
- os_log("fetchPolicy failed to find ego peer stableinfodata/sig", log: tplogDebug, type: .error)
- reply(nil, ContainerError.noPreparedIdentity)
- return
- }
- guard let stableInfo = TPPeerStableInfo(data: stableInfoData, sig: stableInfoSig) else {
- os_log("fetchPolicy failed to create TPPeerStableInfo", log: tplogDebug, type: .error)
- reply(nil, ContainerError.invalidStableInfoOrSig)
- return
- }
-
- let policyVersionCounter = stableInfo.policyVersion
- let policyVersion = NSNumber(value: policyVersionCounter)
- keys[policyVersion] = stableInfo.policyHash
-
- if let policyDocument = self.model.policy(withVersion: policyVersionCounter) {
- os_log("fetchPolicy: have a local version of policy %@: %@", log: tplogDebug, type: .default, policyVersion, policyDocument)
- do {
- let policy = try policyDocument.policy(withSecrets: stableInfo.policySecrets, decrypter: Decrypter())
- reply(policy, nil)
- return
- } catch {
- os_log("TPPolicyDocument failed: %@", log: tplogDebug, type: .default, error as CVarArg)
- reply(nil, error)
+ guard let egoPeerID = self.containerMO.egoPeerID,
+ let egoPermData = self.containerMO.egoPeerPermanentInfo,
+ let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
+ let stableInfoData = self.containerMO.egoPeerStableInfo,
+ let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
+ os_log("fetchCurrentPolicy failed to find ego peer information", log: tplogDebug, type: .error)
+ reply(nil, nil, ContainerError.noPreparedIdentity)
return
- }
}
- self.fetchPolicyDocuments(keys: keys) { result, error in
- guard error == nil else {
- reply(nil, error)
- return
- }
- guard let result = result else {
- os_log("fetchPolicy: nil policies returned")
- reply(nil, ContainerError.policyDocumentDoesNotValidate)
- return
- }
- guard result.count == 1 else {
- os_log("fetchPolicy: wrong length returned")
- reply(nil, ContainerError.policyDocumentDoesNotValidate)
- return
- }
- guard let r = result[policyVersion] else {
- os_log("fetchPolicy: version not found")
- reply(nil, ContainerError.unknownPolicyVersion(policyVersion.uint64Value))
- return
- }
- guard let data = r[1].data(using: .utf8) else {
- os_log("fetchPolicy: failed to convert data")
- reply(nil, ContainerError.unknownPolicyVersion(policyVersion.uint64Value))
- return
- }
- guard let pd = TPPolicyDocument.policyDoc(withHash: r[0], data: data) else {
- os_log("fetchPolicy: pd is nil")
- reply(nil, ContainerError.policyDocumentDoesNotValidate)
- return
- }
- do {
- let policy = try pd.policy(withSecrets: stableInfo.policySecrets, decrypter: Decrypter())
- reply(policy, nil)
- } catch {
- os_log("TPPolicyDocument: %@", log: tplogDebug, type: .default, error as CVarArg)
- reply(nil, error)
- }
+ let keyFactory = TPECPublicKeyFactory()
+ guard let permanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
+ os_log("fetchCurrentPolicy failed to create TPPeerPermanentInfo", log: tplogDebug, type: .error)
+ reply(nil, nil, ContainerError.invalidPermanentInfoOrSig)
+ return
+ }
+ guard let stableInfo = TPPeerStableInfo(data: stableInfoData, sig: stableInfoSig) else {
+ os_log("fetchCurrentPolicy failed to create TPPeerStableInfo", log: tplogDebug, type: .error)
+ reply(nil, nil, ContainerError.invalidStableInfoOrSig)
+ return
+ }
+
+ do {
+ let (views, policy) = try self.policyAndViewsFor(permanentInfo: permanentInfo, stableInfo: stableInfo)
+ reply(views, policy, nil)
+ return
+ } catch {
+ os_log("TPPolicyDocument failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ reply(nil, nil, error)
+ return
}
}
}
+ func policyAndViewsFor(permanentInfo: TPPeerPermanentInfo, stableInfo: TPPeerStableInfo) throws -> (Set<String>, TPPolicy) {
+ let policyVersion = stableInfo.bestPolicyVersion()
+ guard let policyDocument = self.model.policy(withVersion: policyVersion.versionNumber) else {
+ os_log("current policy is missing?", log: tplogDebug, type: .default)
+ throw ContainerError.unknownPolicyVersion(policyVersion.versionNumber)
+ }
+
+ let policy = try policyDocument.policy(withSecrets: stableInfo.policySecrets, decrypter: Decrypter())
+ let views = try policy.views(forModel: permanentInfo.modelID)
+
+ return (views, policy)
+ }
+
// All-or-nothing: return an error in case full list cannot be returned.
// Completion handler data format: [version : [hash, data]]
- func fetchPolicyDocuments(keys: [NSNumber: String],
- reply: @escaping ([NSNumber: [String]]?, Error?) -> Void) {
+ func fetchPolicyDocuments(versions: Set<TPPolicyVersion>,
+ reply: @escaping ([TPPolicyVersion: Data]?, Error?) -> Void) {
self.semaphore.wait()
- let reply: ([NSNumber: [String]]?, Error?) -> Void = {
- os_log("fetchPolicyDocuments complete: %@", log: tplogTrace, type: .info, traceError($1))
+ let reply: ([TPPolicyVersion: Data]?, Error?) -> Void = {
+ os_log("fetchPolicyDocuments complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
self.semaphore.signal()
reply($0, $1)
}
- var keys = keys
- var docs: [NSNumber: [String]] = [:]
+ self.fetchPolicyDocumentsWithSemaphore(versions: versions) { policyDocuments, fetchError in
+ reply(policyDocuments.flatMap { $0.mapValues { policyDoc in policyDoc.protobuf } }, fetchError)
+ }
+ }
+
+ func fetchPolicyDocumentWithSemaphore(version: TPPolicyVersion,
+ reply: @escaping (TPPolicyDocument?, Error?) -> Void) {
+ self.fetchPolicyDocumentsWithSemaphore(versions: Set([version])) { versions, fetchError in
+ guard fetchError == nil else {
+ reply(nil, fetchError)
+ return
+ }
+
+ guard let doc = versions?[version] else {
+ os_log("fetchPolicyDocument: didn't return policy of version: %{public}@", log: tplogDebug, versions ?? "no versions")
+ reply(nil, ContainerError.unknownPolicyVersion(version.versionNumber))
+ return
+ }
+
+ reply(doc, nil)
+ }
+ }
+
+ func fetchPolicyDocumentsWithSemaphore(versions: Set<TPPolicyVersion>,
+ reply: @escaping ([TPPolicyVersion: TPPolicyDocument]?, Error?) -> Void) {
+ var remaining = versions
+ var docs: [TPPolicyVersion: TPPolicyDocument] = [:]
self.moc.performAndWait {
- for (version, hash) in keys {
- if let policydoc = try? self.getPolicyDoc(version.uint64Value), policydoc.policyHash == hash {
- docs[version] = [policydoc.policyHash, policydoc.protobuf.base64EncodedString()]
- keys[version] = nil
+ for version in remaining {
+ if let policydoc = try? self.getPolicyDoc(version.versionNumber), policydoc.version.policyHash == version.policyHash {
+ docs[policydoc.version] = policydoc
+ remaining.remove(version)
}
}
}
- if keys.isEmpty {
+ guard !remaining.isEmpty else {
reply(docs, nil)
return
}
let request = FetchPolicyDocumentsRequest.with {
- $0.keys = keys.map { key, value in
- PolicyDocumentKey.with { $0.version = key.uint64Value; $0.hash = value }}
+ $0.keys = remaining.map { version in
+ PolicyDocumentKey.with { $0.version = version.versionNumber; $0.hash = version.policyHash }}
}
self.cuttlefish.fetchPolicyDocuments(request) { response, error in
- os_log("FetchPolicyDocuments(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+ os_log("FetchPolicyDocuments(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
guard let response = response, error == nil else {
- os_log("FetchPolicyDocuments failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ os_log("FetchPolicyDocuments failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
reply(nil, error ?? ContainerError.cloudkitResponseMissing)
return
}
// TODO: validate the policy's signature
guard let doc = TPPolicyDocument.policyDoc(withHash: mapEntry.key.hash, data: mapEntry.value) else {
- os_log("Can't make policy document with hash %@ and data %@",
+ os_log("Can't make policy document with hash %{public}@ and data %{public}@",
log: tplogDebug, type: .default, mapEntry.key.hash, mapEntry.value.base64EncodedString())
reply(nil, ContainerError.policyDocumentDoesNotValidate)
return
}
- guard let hash = keys[NSNumber(value: doc.policyVersion)], hash == doc.policyHash else {
- os_log("Requested hash %@ does not match fetched hash %@", log: tplogDebug, type: .default,
- keys[NSNumber(value: doc.policyVersion)] ?? "<nil>", doc.policyHash)
+ guard let expectedVersion = (remaining.filter { $0.versionNumber == doc.version.versionNumber }.first) else {
+ os_log("Received a policy version we didn't request: %d", log: tplogDebug, type: .default, doc.version.versionNumber)
+ reply(nil, ContainerError.policyDocumentDoesNotValidate)
+ return
+ }
+
+ guard expectedVersion.policyHash == doc.version.policyHash else {
+ os_log("Requested hash %{public}@ does not match fetched hash %{public}@", log: tplogDebug, type: .default,
+ expectedVersion.policyHash, doc.version.policyHash)
reply(nil, ContainerError.policyDocumentDoesNotValidate)
return
}
- keys[NSNumber(value: doc.policyVersion)] = nil // Server responses should be unique, let's enforce
- docs[NSNumber(value: doc.policyVersion)] = [doc.policyHash, doc.protobuf.base64EncodedString()]
+
+ remaining.remove(expectedVersion) // Server responses should be unique, let's enforce
+
+ docs[doc.version] = doc
self.model.register(doc)
}
return
}
- if !keys.isEmpty {
- let (unknownVersion, _) = keys.first!
- reply(nil, ContainerError.unknownPolicyVersion(unknownVersion.uint64Value))
+ // Determine if there's anything left to fetch
+ guard let unfetchedVersion = remaining.first else {
+ // Nothing remaining? Success!
+ reply(docs, nil)
return
}
- reply(docs, nil)
+ reply(nil, ContainerError.unknownPolicyVersion(unfetchedVersion.versionNumber))
}
}
}
/* Returns any new CKKS keys that need uploading, as well as any TLKShares necessary for those keys */
func makeSharesForNewKeySets(ckksKeys: [CKKSKeychainBackedKeySet],
- tlkShares: [CKKSTLKShare],
- egoPeerKeys: OctagonSelfPeerKeys,
- egoPeerDynamicInfo: TPPeerDynamicInfo,
- epoch: Int) throws -> ([ViewKeys], [TLKShare]) {
+ tlkShares: [CKKSTLKShare],
+ egoPeerKeys: OctagonSelfPeerKeys,
+ egoPeerDynamicInfo: TPPeerDynamicInfo,
+ epoch: Int) throws -> ([ViewKeys], [TLKShare]) {
let newCKKSKeys = ckksKeys.filter { $0.newUpload }
let newViewKeys: [ViewKeys] = newCKKSKeys.map(ViewKeys.convert)
do {
let peerIDsWithAccess = try self.model.getPeerIDsTrustedByPeer(with: egoPeerDynamicInfo,
toAccessView: keyset.tlk.zoneID.zoneName)
- os_log("Planning to share %@ with peers %@", log: tplogDebug, type: .default, String(describing: keyset.tlk), peerIDsWithAccess)
+ os_log("Planning to share %@ with peers %{public}@", log: tplogDebug, type: .default, String(describing: keyset.tlk), peerIDsWithAccess)
let peers = peerIDsWithAccess.compactMap { self.model.peer(withID: $0) }
let viewPeerShares = try peers.map { receivingPeer in
poisoned: 0))
}
- peerShares = peerShares + viewPeerShares
+ peerShares += viewPeerShares
} catch {
- os_log("Unable to create TLKShares for keyset %@: %@", log: tplogDebug, type: .default, String(describing: keyset), error as CVarArg)
+ os_log("Unable to create TLKShares for keyset %@: %{public}@", log: tplogDebug, type: .default, String(describing: keyset), error as CVarArg)
}
}
let newStableInfo = try self.createNewStableInfoIfNeeded(stableChanges: nil,
egoPeerID: egoPeerID,
+ existingStableInfo: stableInfo,
dynamicInfo: dynamicInfo,
signingKeyPair: egoPeerKeys.signingKey)
ckksKeys: [CKKSKeychainBackedKeySet],
tlkShares: [CKKSTLKShare],
preapprovedKeys: [Data]?,
- reply: @escaping (String?, [CKRecord], Error?) -> Void) {
+ reply: @escaping (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void) {
self.semaphore.wait()
- let reply: (String?, [CKRecord], Error?) -> Void = {
- os_log("join complete: %@", log: tplogTrace, type: .info, traceError($2))
+ let reply: (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void = {
+ os_log("join complete: %{public}@", log: tplogTrace, type: .info, traceError($4))
self.semaphore.signal()
- reply($0, $1, $2)
+ reply($0, $1, $2, $3, $4)
}
self.fetchAndPersistChanges { error in
guard error == nil else {
- reply(nil, [], error)
+ reply(nil, [], nil, nil, error)
return
}
- self.moc.performAndWait {
- guard let voucher = TPVoucher(infoWith: voucherData, sig: voucherSig) else {
- reply(nil, [], ContainerError.invalidVoucherOrSig)
- return
- }
- guard let sponsor = self.model.peer(withID: voucher.sponsorID) else {
- reply(nil, [], ContainerError.sponsorNotRegistered(voucher.sponsorID))
- return
+
+ // To join, you must know all policies that exist
+ let allPolicyVersions = self.model.allPolicyVersions()
+ self.fetchPolicyDocumentsWithSemaphore(versions: allPolicyVersions) { _, policyFetchError in
+ if let error = policyFetchError {
+ os_log("join: error fetching all requested policies (continuing anyway): %{public}@", log: tplogDebug, type: .default, error as CVarArg)
}
- // Fetch ego peer identity from local storage.
- guard let egoPeerID = self.containerMO.egoPeerID,
- let egoPermData = self.containerMO.egoPeerPermanentInfo,
- let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
- let egoStableData = self.containerMO.egoPeerStableInfo,
- let egoStableSig = self.containerMO.egoPeerStableInfoSig
- else {
- reply(nil, [], ContainerError.noPreparedIdentity)
+ self.moc.performAndWait {
+ guard let voucher = TPVoucher(infoWith: voucherData, sig: voucherSig) else {
+ reply(nil, [], nil, nil, ContainerError.invalidVoucherOrSig)
return
- }
+ }
+ guard let sponsor = self.model.peer(withID: voucher.sponsorID) else {
+ reply(nil, [], nil, nil, ContainerError.sponsorNotRegistered(voucher.sponsorID))
+ return
+ }
- let keyFactory = TPECPublicKeyFactory()
- guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
- reply(nil, [], ContainerError.invalidPermanentInfoOrSig)
- return
- }
- guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
- reply(nil, [], ContainerError.invalidStableInfoOrSig)
- return
- }
- guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
- os_log("join: self machineID %@ not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
- self.onqueueTTRUntrusted()
- reply(nil, [], ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
- return
- }
+ // Fetch ego peer identity from local storage.
+ guard let egoPeerID = self.containerMO.egoPeerID,
+ let egoPermData = self.containerMO.egoPeerPermanentInfo,
+ let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
+ let egoStableData = self.containerMO.egoPeerStableInfo,
+ let egoStableSig = self.containerMO.egoPeerStableInfoSig
+ else {
+ reply(nil, [], nil, nil, ContainerError.noPreparedIdentity)
+ return
+ }
- loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
- guard let egoPeerKeys = egoPeerKeys else {
- os_log("Don't have my own peer keys; can't join: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
- reply(nil, [], error)
+ let keyFactory = TPECPublicKeyFactory()
+ guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
+ reply(nil, [], nil, nil, ContainerError.invalidPermanentInfoOrSig)
+ return
+ }
+ guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
+ reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
+ return
+ }
+ guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
+ os_log("join: self machineID %{public}@ not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
+ self.onqueueTTRUntrusted()
+ reply(nil, [], nil, nil, ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
return
}
- self.moc.performAndWait {
- let peer: Peer
- let newDynamicInfo: TPPeerDynamicInfo
- do {
- (peer, newDynamicInfo) = try self.onqueuePreparePeerForJoining(egoPeerID: egoPeerID,
- peerPermanentInfo: selfPermanentInfo,
- stableInfo: selfStableInfo,
- sponsorID: sponsor.peerID,
- preapprovedKeys: preapprovedKeys,
- vouchers: [SignedVoucher.with {
- $0.voucher = voucher.data
- $0.sig = voucher.sig
- }, ],
- egoPeerKeys: egoPeerKeys)
- } catch {
- os_log("Unable to create peer for joining: %@", log: tplogDebug, type: .default, error as CVarArg)
- reply(nil, [], error)
- return
- }
- let allTLKShares: [TLKShare]
- let viewKeys: [ViewKeys]
- do {
- (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
- tlkShares: tlkShares,
- egoPeerKeys: egoPeerKeys,
- egoPeerDynamicInfo: newDynamicInfo,
- epoch: Int(selfPermanentInfo.epoch))
- } catch {
- os_log("Unable to process keys before joining: %@", log: tplogDebug, type: .default, error as CVarArg)
- reply(nil, [], error)
+ loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
+ guard let egoPeerKeys = egoPeerKeys else {
+ os_log("Don't have my own peer keys; can't join: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+ reply(nil, [], nil, nil, error)
return
}
+ self.moc.performAndWait {
+ let peer: Peer
+ let newDynamicInfo: TPPeerDynamicInfo
+ do {
+ (peer, newDynamicInfo) = try self.onqueuePreparePeerForJoining(egoPeerID: egoPeerID,
+ peerPermanentInfo: selfPermanentInfo,
+ stableInfo: selfStableInfo,
+ sponsorID: sponsor.peerID,
+ preapprovedKeys: preapprovedKeys,
+ vouchers: [SignedVoucher.with {
+ $0.voucher = voucher.data
+ $0.sig = voucher.sig
+ }, ],
+ egoPeerKeys: egoPeerKeys)
+ } catch {
+ os_log("Unable to create peer for joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ reply(nil, [], nil, nil, error)
+ return
+ }
- do {
- try self.model.checkIntroduction(forCandidate: selfPermanentInfo,
- stableInfo: peer.stableInfoAndSig.toStableInfo(),
- withSponsorID: sponsor.peerID)
- } catch {
- os_log("Error checking introduction: %@", log: tplogDebug, type: .default, error as CVarArg)
- reply(nil, [], error)
- return
- }
+ guard let peerStableInfo = peer.stableInfoAndSig.toStableInfo() else {
+ os_log("Unable to create new peer stable new for joining", log: tplogDebug, type: .default)
+ reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
+ return
+ }
- var bottle: Bottle
- do {
- bottle = try self.assembleBottle(egoPeerID: egoPeerID)
- } catch {
- reply(nil, [], error)
- return
- }
+ let allTLKShares: [TLKShare]
+ let viewKeys: [ViewKeys]
+ do {
+ (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
+ tlkShares: tlkShares,
+ egoPeerKeys: egoPeerKeys,
+ egoPeerDynamicInfo: newDynamicInfo,
+ epoch: Int(selfPermanentInfo.epoch))
+ } catch {
+ os_log("Unable to process keys before joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ reply(nil, [], nil, nil, error)
+ return
+ }
- os_log("Beginning join for peer %@", log: tplogDebug, type: .default, egoPeerID)
- os_log("Join permanentInfo: %@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
- os_log("Join permanentInfoSig: %@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
- os_log("Join stableInfo: %@", log: tplogDebug, type: .debug, peer.stableInfoAndSig.peerStableInfo.base64EncodedString())
- os_log("Join stableInfoSig: %@", log: tplogDebug, type: .debug, peer.stableInfoAndSig.sig.base64EncodedString())
- os_log("Join dynamicInfo: %@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
- os_log("Join dynamicInfoSig: %@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
+ do {
+ try self.model.checkIntroduction(forCandidate: selfPermanentInfo,
+ stableInfo: peer.stableInfoAndSig.toStableInfo(),
+ withSponsorID: sponsor.peerID)
+ } catch {
+ os_log("Error checking introduction: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ reply(nil, [], nil, nil, error)
+ return
+ }
- os_log("Join vouchers: %@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.voucher.base64EncodedString() })
- os_log("Join voucher signatures: %@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.sig.base64EncodedString() })
+ var bottle: Bottle
+ do {
+ bottle = try self.assembleBottle(egoPeerID: egoPeerID)
+ } catch {
+ reply(nil, [], nil, nil, error)
+ return
+ }
- os_log("Uploading %d tlk shares", log: tplogDebug, type: .default, allTLKShares.count)
+ os_log("Beginning join for peer %{public}@", log: tplogDebug, type: .default, egoPeerID)
+ os_log("Join permanentInfo: %{public}@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
+ os_log("Join permanentInfoSig: %{public}@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
+ os_log("Join stableInfo: %{public}@", log: tplogDebug, type: .debug, peer.stableInfoAndSig.peerStableInfo.base64EncodedString())
+ os_log("Join stableInfoSig: %{public}@", log: tplogDebug, type: .debug, peer.stableInfoAndSig.sig.base64EncodedString())
+ os_log("Join dynamicInfo: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
+ os_log("Join dynamicInfoSig: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
- do {
- os_log("Join peer: %@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
- } catch {
- os_log("Join unable to encode peer: %@", log: tplogDebug, type: .debug, error as CVarArg)
- }
+ os_log("Join vouchers: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.voucher.base64EncodedString() })
+ os_log("Join voucher signatures: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.sig.base64EncodedString() })
- let changeToken = self.containerMO.changeToken ?? ""
- let request = JoinWithVoucherRequest.with {
- $0.changeToken = changeToken
- $0.peer = peer
- $0.bottle = bottle
- $0.tlkShares = allTLKShares
- $0.viewKeys = viewKeys
- }
- self.cuttlefish.joinWithVoucher(request) { response, error in
- os_log("JoinWithVoucher(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
- guard let response = response, error == nil else {
- os_log("joinWithVoucher failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
- reply(nil, [], error ?? ContainerError.cloudkitResponseMissing)
- return
+ os_log("Uploading %d tlk shares", log: tplogDebug, type: .default, allTLKShares.count)
+
+ do {
+ os_log("Join peer: %{public}@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
+ } catch {
+ os_log("Join unable to encode peer: %{public}@", log: tplogDebug, type: .debug, error as CVarArg)
}
- self.moc.performAndWait {
- do {
- self.containerMO.egoPeerStableInfo = peer.stableInfoAndSig.peerStableInfo
- self.containerMO.egoPeerStableInfoSig = peer.stableInfoAndSig.sig
- try self.onQueuePersist(changes: response.changes)
- os_log("JoinWithVoucher succeeded", log: tplogDebug)
+ let changeToken = self.containerMO.changeToken ?? ""
+ let request = JoinWithVoucherRequest.with {
+ $0.changeToken = changeToken
+ $0.peer = peer
+ $0.bottle = bottle
+ $0.tlkShares = allTLKShares
+ $0.viewKeys = viewKeys
+ }
+ self.cuttlefish.joinWithVoucher(request) { response, error in
+ os_log("JoinWithVoucher(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+ guard let response = response, error == nil else {
+ os_log("joinWithVoucher failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ reply(nil, [], nil, nil, error ?? ContainerError.cloudkitResponseMissing)
+ return
+ }
- let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
- reply(egoPeerID, keyHierarchyRecords, nil)
- } catch {
- os_log("JoinWithVoucher failed: %@", log: tplogDebug, String(describing: error))
- reply(nil, [], error)
+ self.moc.performAndWait {
+ do {
+ self.containerMO.egoPeerStableInfo = peer.stableInfoAndSig.peerStableInfo
+ self.containerMO.egoPeerStableInfoSig = peer.stableInfoAndSig.sig
+
+ let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: selfPermanentInfo,
+ stableInfo: peerStableInfo)
+
+ try self.onQueuePersist(changes: response.changes)
+ os_log("JoinWithVoucher succeeded", log: tplogDebug)
+
+ let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
+ reply(egoPeerID, keyHierarchyRecords, syncingViews, policy, nil)
+ } catch {
+ os_log("JoinWithVoucher failed: %{public}@", log: tplogDebug, String(describing: error))
+ reply(nil, [], nil, nil, error)
+ }
}
}
}
}
}
- func requestHealthCheck(requiresEscrowCheck: Bool, reply: @escaping (Bool, Bool, Bool, Error?) -> Void) {
+ func requestHealthCheck(requiresEscrowCheck: Bool, reply: @escaping (Bool, Bool, Bool, Bool, Error?) -> Void) {
self.semaphore.wait()
- let reply: (Bool, Bool, Bool, Error?) -> Void = {
- os_log("health check complete: %@", log: tplogTrace, type: .info, traceError($3))
+ let reply: (Bool, Bool, Bool, Bool, Error?) -> Void = {
+ os_log("health check complete: %{public}@", log: tplogTrace, type: .info, traceError($4))
self.semaphore.signal()
- reply($0, $1, $2, $3)
+ reply($0, $1, $2, $3, $4)
}
os_log("requestHealthCheck requiring escrow check: %d", log: tplogDebug, type: .default, requiresEscrowCheck)
guard let egoPeerID = self.containerMO.egoPeerID else {
// No identity, nothing to do
os_log("requestHealthCheck: No identity.", log: tplogDebug, type: .default)
- reply(false, false, false, ContainerError.noPreparedIdentity)
+ reply(false, false, false, false, ContainerError.noPreparedIdentity)
return
}
let request = GetRepairActionRequest.with {
self.cuttlefish.getRepairAction(request) { response, error in
guard error == nil else {
- reply(false, false, false, error)
+ reply(false, false, false, false, error)
return
}
guard let action = response?.repairAction else {
- os_log("repair response is empty, returning false: %@", log: tplogDebug, type: .default)
- reply(false, false, false, nil)
+ os_log("repair response is empty, returning false", log: tplogDebug, type: .default)
+ reply(false, false, false, false, nil)
return
}
var postRepairAccount: Bool = false
var postRepairEscrow: Bool = false
var resetOctagon: Bool = false
+ var leaveTrust: Bool = false
switch action {
case .noAction:
break
case .postRepairAccount:
postRepairAccount = true
- break
case .postRepairEscrow:
postRepairEscrow = true
- break
case .resetOctagon:
resetOctagon = true
- break
+ case .leaveTrust:
+ leaveTrust = true
case .UNRECOGNIZED:
break
}
- reply(postRepairAccount, postRepairEscrow, resetOctagon, nil)
+ reply(postRepairAccount, postRepairEscrow, resetOctagon, leaveTrust, nil)
}
}
}
func getSupportAppInfo(reply: @escaping (Data?, Error?) -> Void) {
self.semaphore.wait()
let reply: (Data?, Error?) -> Void = {
- os_log("getSupportAppInfo complete: %@", log: tplogTrace, type: .info, traceError($1))
+ os_log("getSupportAppInfo complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
self.semaphore.signal()
reply($0, $1)
}
self.cuttlefish.getSupportAppInfo { response, error in
- os_log("getSupportAppInfo(): %@, error: %@", log: tplogDebug,
+ os_log("getSupportAppInfo(): %{public}@, error: %{public}@", log: tplogDebug,
"(\(String(describing: response))", "\(String(describing: error))")
guard let response = response, error == nil else {
- os_log("getSupportAppInfo failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ os_log("getSupportAppInfo failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
reply(nil, error ?? ContainerError.cloudkitResponseMissing)
return
}
func preflightPreapprovedJoin(reply: @escaping (Bool, Error?) -> Void) {
self.semaphore.wait()
let reply: (Bool, Error?) -> Void = {
- os_log("preflightPreapprovedJoin complete: %@", log: tplogTrace, type: .info, traceError($1))
+ os_log("preflightPreapprovedJoin complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
self.semaphore.signal()
reply($0, $1)
}
self.fetchAndPersistChanges { error in
guard error == nil else {
- os_log("preflightPreapprovedJoin unable to fetch changes: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
+ os_log("preflightPreapprovedJoin unable to fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
reply(false, error)
return
}
- // We explicitly ignore the machine ID list here; we're only interested in the peer states: do they preapprove us?
+ // We need to try to have all policy versions that our peers claim to behave
+ let allPolicyVersions = self.model.allPolicyVersions()
+ self.fetchPolicyDocumentsWithSemaphore(versions: allPolicyVersions) { _, policyFetchError in
+ if let error = policyFetchError {
+ os_log("preflightPreapprovedJoin: error fetching all requested policies (continuing anyway): %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ }
- guard !self.model.allPeerIDs().isEmpty else {
- // If, after fetch and handle changes, there's no peers, then we can likely establish.
- reply(true, nil)
- return
- }
+ // We explicitly ignore the machine ID list here; we're only interested in the peer states: do they preapprove us?
- guard let egoPeerID = self.containerMO.egoPeerID,
- let egoPermData = self.containerMO.egoPeerPermanentInfo,
- let egoPermSig = self.containerMO.egoPeerPermanentInfoSig
- else {
- os_log("preflightPreapprovedJoin: no prepared identity", log: tplogDebug, type: .debug)
- reply(false, ContainerError.noPreparedIdentity)
+ guard !self.model.allPeerIDs().isEmpty else {
+ // If, after fetch and handle changes, there's no peers, then we can likely establish.
+ reply(true, nil)
return
- }
+ }
- let keyFactory = TPECPublicKeyFactory()
- guard let egoPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
- os_log("preflightPreapprovedJoin: invalid permanent info", log: tplogDebug, type: .debug)
- reply(false, ContainerError.invalidPermanentInfoOrSig)
- return
- }
+ guard let egoPeerID = self.containerMO.egoPeerID,
+ let egoPermData = self.containerMO.egoPeerPermanentInfo,
+ let egoPermSig = self.containerMO.egoPeerPermanentInfoSig
+ else {
+ os_log("preflightPreapprovedJoin: no prepared identity", log: tplogDebug, type: .debug)
+ reply(false, ContainerError.noPreparedIdentity)
+ return
+ }
- guard self.model.hasPotentiallyTrustedPeerPreapprovingKey(egoPermanentInfo.signingPubKey.spki()) else {
- os_log("preflightPreapprovedJoin: no peers preapprove our key", log: tplogDebug, type: .debug)
- reply(false, ContainerError.noPeersPreapprovePreparedIdentity)
- return
- }
+ let keyFactory = TPECPublicKeyFactory()
+ guard let egoPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
+ os_log("preflightPreapprovedJoin: invalid permanent info", log: tplogDebug, type: .debug)
+ reply(false, ContainerError.invalidPermanentInfoOrSig)
+ return
+ }
- reply(true, nil)
+ guard self.model.hasPotentiallyTrustedPeerPreapprovingKey(egoPermanentInfo.signingPubKey.spki()) else {
+ os_log("preflightPreapprovedJoin: no peers preapprove our key", log: tplogDebug, type: .debug)
+ reply(false, ContainerError.noPeersPreapprovePreparedIdentity)
+ return
+ }
+
+ reply(true, nil)
+ }
}
}
func preapprovedJoin(ckksKeys: [CKKSKeychainBackedKeySet],
tlkShares: [CKKSTLKShare],
preapprovedKeys: [Data]?,
- reply: @escaping (String?, [CKRecord], Error?) -> Void) {
+ reply: @escaping (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void) {
self.semaphore.wait()
- let reply: (String?, [CKRecord], Error?) -> Void = {
- os_log("preapprovedJoin complete: %@", log: tplogTrace, type: .info, traceError($2))
+ let reply: (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void = {
+ os_log("preapprovedJoin complete: %{public}@", log: tplogTrace, type: .info, traceError($4))
self.semaphore.signal()
- reply($0, $1, $2)
+ reply($0, $1, $2, $3, $4)
}
self.fetchAndPersistChangesIfNeeded { error in
guard error == nil else {
- os_log("preapprovedJoin unable to fetch changes: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
- reply(nil, [], error)
+ os_log("preapprovedJoin unable to fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
+ reply(nil, [], nil, nil, error)
return
}
self.moc.performAndWait {
let egoStableSig = self.containerMO.egoPeerStableInfoSig
else {
os_log("preapprovedJoin: no prepared identity", log: tplogDebug, type: .debug)
- reply(nil, [], ContainerError.noPreparedIdentity)
+ reply(nil, [], nil, nil, ContainerError.noPreparedIdentity)
return
}
let keyFactory = TPECPublicKeyFactory()
guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
- reply(nil, [], ContainerError.invalidPermanentInfoOrSig)
+ reply(nil, [], nil, nil, ContainerError.invalidPermanentInfoOrSig)
return
}
guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
- reply(nil, [], ContainerError.invalidStableInfoOrSig)
+ reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
return
}
guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
- os_log("preapprovedJoin: self machineID %@ (me) not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
+ os_log("preapprovedJoin: self machineID %{public}@ (me) not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
self.onqueueTTRUntrusted()
- reply(nil, [], ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
+ reply(nil, [], nil, nil, ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
return
}
loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
guard let egoPeerKeys = egoPeerKeys else {
os_log("preapprovedJoin: Don't have my own keys: can't join", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
- reply(nil, [], error)
+ reply(nil, [], nil, nil, error)
return
}
guard self.model.hasPotentiallyTrustedPeerPreapprovingKey(egoPeerKeys.signingKey.publicKey().spki()) else {
os_log("preapprovedJoin: no peers preapprove our key", log: tplogDebug, type: .debug)
- reply(nil, [], ContainerError.noPeersPreapprovePreparedIdentity)
+ reply(nil, [], nil, nil, ContainerError.noPeersPreapprovePreparedIdentity)
return
}
vouchers: [],
egoPeerKeys: egoPeerKeys)
} catch {
- os_log("Unable to create peer for joining: %@", log: tplogDebug, type: .default, error as CVarArg)
- reply(nil, [], error)
+ os_log("Unable to create peer for joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ reply(nil, [], nil, nil, error)
+ return
+ }
+
+ guard let peerStableInfo = peer.stableInfoAndSig.toStableInfo() else {
+ os_log("Unable to create new peer stable new for joining", log: tplogDebug, type: .default)
+ reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
return
}
egoPeerDynamicInfo: newDynamicInfo,
epoch: Int(selfPermanentInfo.epoch))
} catch {
- os_log("Unable to process keys before joining: %@", log: tplogDebug, type: .default, error as CVarArg)
- reply(nil, [], error)
+ os_log("Unable to process keys before joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ reply(nil, [], nil, nil, error)
return
}
do {
bottle = try self.assembleBottle(egoPeerID: egoPeerID)
} catch {
- reply(nil, [], error)
+ reply(nil, [], nil, nil, error)
return
}
- os_log("Beginning preapprovedJoin for peer %@", log: tplogDebug, type: .default, egoPeerID)
- os_log("preapprovedJoin permanentInfo: %@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
- os_log("preapprovedJoin permanentInfoSig: %@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
- os_log("preapprovedJoin stableInfo: %@", log: tplogDebug, type: .debug, egoStableData.base64EncodedString())
- os_log("preapprovedJoin stableInfoSig: %@", log: tplogDebug, type: .debug, egoStableSig.base64EncodedString())
- os_log("preapprovedJoin dynamicInfo: %@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
- os_log("preapprovedJoin dynamicInfoSig: %@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
+ os_log("Beginning preapprovedJoin for peer %{public}@", log: tplogDebug, type: .default, egoPeerID)
+ os_log("preapprovedJoin permanentInfo: %{public}@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
+ os_log("preapprovedJoin permanentInfoSig: %{public}@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
+ os_log("preapprovedJoin stableInfo: %{public}@", log: tplogDebug, type: .debug, egoStableData.base64EncodedString())
+ os_log("preapprovedJoin stableInfoSig: %{public}@", log: tplogDebug, type: .debug, egoStableSig.base64EncodedString())
+ os_log("preapprovedJoin dynamicInfo: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
+ os_log("preapprovedJoin dynamicInfoSig: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
- os_log("preapprovedJoin vouchers: %@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.voucher.base64EncodedString() })
- os_log("preapprovedJoin voucher signatures: %@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.sig.base64EncodedString() })
+ os_log("preapprovedJoin vouchers: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.voucher.base64EncodedString() })
+ os_log("preapprovedJoin voucher signatures: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.sig.base64EncodedString() })
os_log("preapprovedJoin: uploading %d tlk shares", log: tplogDebug, type: .default, allTLKShares.count)
do {
- os_log("preapprovedJoin peer: %@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
+ os_log("preapprovedJoin peer: %{public}@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
} catch {
- os_log("preapprovedJoin unable to encode peer: %@", log: tplogDebug, type: .debug, error as CVarArg)
+ os_log("preapprovedJoin unable to encode peer: %{public}@", log: tplogDebug, type: .debug, error as CVarArg)
}
let changeToken = self.containerMO.changeToken ?? ""
$0.viewKeys = viewKeys
}
self.cuttlefish.joinWithVoucher(request) { response, error in
- os_log("preapprovedJoin(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+ os_log("preapprovedJoin(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
guard let response = response, error == nil else {
- os_log("preapprovedJoin failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
- reply(nil, [], error ?? ContainerError.cloudkitResponseMissing)
+ os_log("preapprovedJoin failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ reply(nil, [], nil, nil, error ?? ContainerError.cloudkitResponseMissing)
return
}
do {
self.containerMO.egoPeerStableInfo = peer.stableInfoAndSig.peerStableInfo
self.containerMO.egoPeerStableInfoSig = peer.stableInfoAndSig.sig
+
+ let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: selfPermanentInfo,
+ stableInfo: peerStableInfo)
+
try self.onQueuePersist(changes: response.changes)
os_log("preapprovedJoin succeeded", log: tplogDebug)
let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
- reply(egoPeerID, keyHierarchyRecords, nil)
+ reply(egoPeerID, keyHierarchyRecords, syncingViews, policy, nil)
} catch {
- os_log("preapprovedJoin failed: %@", log: tplogDebug, String(describing: error))
- reply(nil, [], error)
+ os_log("preapprovedJoin failed: %{public}@", log: tplogDebug, String(describing: error))
+ reply(nil, [], nil, nil, error)
}
}
}
reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
self.semaphore.wait()
let reply: (TrustedPeersHelperPeerState?, Error?) -> Void = {
- os_log("update complete: %@", log: tplogTrace, type: .info, traceError($1))
+ os_log("update complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
self.semaphore.signal()
reply($0, $1)
}
serialNumber: serialNumber,
osVersion: osVersion,
policyVersion: policyVersion,
- policySecrets: policySecrets,
- recoverySigningPubKey: nil,
- recoveryEncryptionPubKey: nil)
+ policySecrets: policySecrets)
self.fetchChangesAndUpdateTrustIfNeeded(stableChanges: stableChanges, reply: reply)
}
func set(preapprovedKeys: [Data],
- reply: @escaping (Error?) -> Void) {
+ reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
self.semaphore.wait()
- let reply: (Error?) -> Void = {
- os_log("setPreapprovedKeys complete: %@", log: tplogTrace, type: .info, traceError($0))
+ let reply: (TrustedPeersHelperPeerState?, Error?) -> Void = {
+ os_log("setPreapprovedKeys complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
self.semaphore.signal()
- reply($0)
+ reply($0, $1)
}
self.moc.performAndWait {
guard let egoPeerID = self.containerMO.egoPeerID else {
// No identity, nothing to do
os_log("setPreapprovedKeys: No identity.", log: tplogDebug, type: .default)
- reply(ContainerError.noPreparedIdentity)
+ reply(nil, ContainerError.noPreparedIdentity)
return
}
loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
guard let signingKeyPair = signingKeyPair else {
- os_log("setPreapprovedKeys: no signing key pair: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
- reply(error ?? ContainerError.unableToCreateKeyPair)
+ os_log("setPreapprovedKeys: no signing key pair: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ reply(nil, error ?? ContainerError.unableToCreateKeyPair)
return
}
signing: signingKeyPair,
currentMachineIDs: self.onqueueCurrentMIDList())
} catch {
- os_log("setPreapprovedKeys: couldn't calculate dynamic info: %@", log: tplogDebug, type: .default, error as CVarArg)
- reply(error)
+ os_log("setPreapprovedKeys: couldn't calculate dynamic info: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ reply(nil, error)
return
}
- os_log("setPreapprovedKeys: produced a dynamicInfo: %@", log: tplogDebug, type: .default, dynamicInfo)
+ os_log("setPreapprovedKeys: produced a dynamicInfo: %{public}@", log: tplogDebug, type: .default, dynamicInfo)
if dynamicInfo == self.model.peer(withID: egoPeerID)?.dynamicInfo {
os_log("setPreapprovedKeys: no change; nothing to do.", log: tplogDebug, type: .default)
- reply(nil)
+
+ // Calling this will fill in the peer status
+ self.updateTrustIfNeeded(reply: reply)
return
}
- os_log("setPreapprovedKeys: attempting updateTrust for %@ with: %@", log: tplogDebug, type: .default, egoPeerID, dynamicInfo)
+ os_log("setPreapprovedKeys: attempting updateTrust for %{public}@ with: %{public}@", log: tplogDebug, type: .default, egoPeerID, dynamicInfo)
let request = UpdateTrustRequest.with {
$0.changeToken = self.containerMO.changeToken ?? ""
$0.peerID = egoPeerID
$0.dynamicInfoAndSig = SignedPeerDynamicInfo(dynamicInfo)
}
- self.cuttlefish.updateTrust(request) { response, error in
- os_log("setPreapprovedKeys(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
- guard let response = response, error == nil else {
- os_log("setPreapprovedKeys failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
- reply(error ?? ContainerError.cloudkitResponseMissing)
- return
- }
- os_log("setPreapprovedKeys: updateTrust suceeded", log: tplogDebug, type: .default)
-
- do {
- try self.persist(changes: response.changes)
- } catch {
- os_log("setPreapprovedKeys: could not persist changes: %@", log: tplogDebug, type: .default, error as CVarArg)
- reply(error)
+ self.perform(updateTrust: request) { state, error in
+ guard error == nil else {
+ os_log("setPreapprovedKeys: failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg? ?? "no error")
+ reply(state, error)
return
}
- reply(nil)
+ os_log("setPreapprovedKeys: updateTrust succeeded", log: tplogDebug, type: .default)
+ reply(state, nil)
}
}
}
reply: @escaping ([CKRecord]?, Error?) -> Void) {
self.semaphore.wait()
let reply: ([CKRecord]?, Error?) -> Void = {
- os_log("updateTLKs complete: %@", log: tplogTrace, type: .info, traceError($1))
+ os_log("updateTLKs complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
self.semaphore.signal()
reply($0, $1)
}
os_log("Uploading some new TLKs: %@", log: tplogDebug, type: .default, ckksKeys)
self.moc.performAndWait {
- guard let egoPeerID = self.containerMO.egoPeerID,
- let egoPermData = self.containerMO.egoPeerPermanentInfo,
- let egoPermSig = self.containerMO.egoPeerPermanentInfoSig
- else {
- os_log("Have no self identity, can't make tlk shares", log: tplogDebug, type: .default)
- reply(nil, ContainerError.noPreparedIdentity)
- return
- }
+ self.onqueueUpdateTLKs(ckksKeys: ckksKeys, tlkShares: tlkShares, reply: reply)
+ }
+ }
- guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: TPECPublicKeyFactory()) else {
- os_log("Couldn't parse self identity", log: tplogDebug, type: .default, ckksKeys)
- reply(nil, ContainerError.invalidPermanentInfoOrSig)
+ func onqueueUpdateTLKs(ckksKeys: [CKKSKeychainBackedKeySet],
+ tlkShares: [CKKSTLKShare],
+ reply: @escaping ([CKRecord]?, Error?) -> Void) {
+ guard let egoPeerID = self.containerMO.egoPeerID,
+ let egoPermData = self.containerMO.egoPeerPermanentInfo,
+ let egoPermSig = self.containerMO.egoPeerPermanentInfoSig
+ else {
+ os_log("Have no self identity, can't make tlk shares", log: tplogDebug, type: .default)
+ reply(nil, ContainerError.noPreparedIdentity)
+ return
+ }
+
+ guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: TPECPublicKeyFactory()) else {
+ os_log("Couldn't parse self identity", log: tplogDebug, type: .default, ckksKeys)
+ reply(nil, ContainerError.invalidPermanentInfoOrSig)
+ return
+ }
+
+ loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
+ guard let egoPeerKeys = egoPeerKeys else {
+ os_log("Don't have my own peer keys; can't upload new TLKs: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+ reply(nil, error)
return
}
+ self.moc.performAndWait {
+ guard let egoPeerDynamicInfo = self.model.getDynamicInfoForPeer(withID: egoPeerID) else {
+ os_log("Unable to fetch dynamic info for self", log: tplogDebug, type: .default)
+ reply(nil, ContainerError.missingDynamicInfo)
+ return
+ }
- loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
- guard let egoPeerKeys = egoPeerKeys else {
- os_log("Don't have my own peer keys; can't upload new TLKs: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+ let allTLKShares: [TLKShare]
+ let viewKeys: [ViewKeys]
+ do {
+ (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
+ tlkShares: tlkShares,
+ egoPeerKeys: egoPeerKeys,
+ egoPeerDynamicInfo: egoPeerDynamicInfo,
+ epoch: Int(selfPermanentInfo.epoch))
+ } catch {
+ os_log("Unable to process keys before uploading: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
reply(nil, error)
return
}
- self.moc.performAndWait {
- guard let egoPeerDynamicInfo = self.model.getDynamicInfoForPeer(withID: egoPeerID) else {
- os_log("Unable to fetch dynamic info for self", log: tplogDebug, type: .default)
- reply(nil, ContainerError.missingDynamicInfo)
- return
- }
- let allTLKShares: [TLKShare]
- let viewKeys: [ViewKeys]
- do {
- (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
- tlkShares: tlkShares,
- egoPeerKeys: egoPeerKeys,
- egoPeerDynamicInfo: egoPeerDynamicInfo,
- epoch: Int(selfPermanentInfo.epoch))
- } catch {
- os_log("Unable to process keys before uploading: %@", log: tplogDebug, type: .default, error as CVarArg)
+ let request = UpdateTrustRequest.with {
+ $0.changeToken = self.containerMO.changeToken ?? ""
+ $0.peerID = egoPeerID
+ $0.tlkShares = allTLKShares
+ $0.viewKeys = viewKeys
+ }
+
+ self.cuttlefish.updateTrust(request) { response, error in
+ os_log("UpdateTrust(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+
+ guard error == nil else {
reply(nil, error)
return
}
- let request = UpdateTrustRequest.with {
- $0.changeToken = self.containerMO.changeToken ?? ""
- $0.peerID = egoPeerID
- $0.tlkShares = allTLKShares
- $0.viewKeys = viewKeys
- }
-
- self.cuttlefish.updateTrust(request) { response, error in
- os_log("UpdateTrust(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
-
- guard error == nil else {
- reply(nil, error)
- return
- }
-
- let keyHierarchyRecords = response?.zoneKeyHierarchyRecords.compactMap { CKRecord($0) } ?? []
- os_log("Recevied %d CKRecords back", log: tplogDebug, type: .default, keyHierarchyRecords.count)
- reply(keyHierarchyRecords, nil)
- }
+ let keyHierarchyRecords = response?.zoneKeyHierarchyRecords.compactMap { CKRecord($0) } ?? []
+ os_log("Recevied %d CKRecords back", log: tplogDebug, type: .default, keyHierarchyRecords.count)
+ reply(keyHierarchyRecords, nil)
}
}
}
func getState(reply: @escaping (ContainerState) -> Void) {
self.semaphore.wait()
let reply: (ContainerState) -> Void = {
- os_log("getState complete: %@", log: tplogTrace, type: .info, $0.egoPeerID ?? "<NULL>")
+ os_log("getState complete: %{public}@", log: tplogTrace, type: .info, $0.egoPeerID ?? "<NULL>")
self.semaphore.signal()
reply($0)
}
}
// This will only fetch changes if no changes have ever been fetched before
- private func fetchAndPersistChangesIfNeeded(reply: @escaping (Error?) -> Void) {
+ func fetchAndPersistChangesIfNeeded(reply: @escaping (Error?) -> Void) {
self.moc.performAndWait {
if self.containerMO.changeToken == nil {
self.onqueueFetchAndPersistChanges(reply: reply)
let request = FetchChangesRequest.with {
$0.changeToken = self.containerMO.changeToken ?? ""
}
- os_log("Fetching with change token: %@", log: tplogDebug, type: .default, request.changeToken.count > 0 ? request.changeToken : "empty")
+ os_log("Fetching with change token: %{public}@", log: tplogDebug, type: .default, !request.changeToken.isEmpty ? request.changeToken : "empty")
self.cuttlefish.fetchChanges(request) { response, error in
- os_log("FetchChanges(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+ os_log("FetchChanges(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
guard let response = response, error == nil else {
switch error {
case CuttlefishErrorMatcher(code: CuttlefishErrorCode.changeTokenExpired):
do {
try self.deleteLocalCloudKitData()
} catch {
- os_log("Failed to reset local data: %@", log: tplogDebug, type: .default, error as CVarArg)
+ os_log("Failed to reset local data: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
reply(error)
return
}
return
default:
- os_log("Fetch error is an unknown error: %@", log: tplogDebug, type: .default, String(describing: error))
+ os_log("Fetch error is an unknown error: %{public}@", log: tplogDebug, type: .default, String(describing: error))
}
- os_log("Could not fetch changes: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ os_log("Could not fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
reply(error)
return
}
do {
try self.persist(changes: response.changes)
} catch {
- os_log("Could not persist changes: %@", log: tplogDebug, type: .default, error as CVarArg)
+ os_log("Could not persist changes: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
reply(error)
return
}
guard let oldDynamicInfo = oldDynamicInfo else {
return true
}
- if (newDynamicInfo.includedPeerIDs != oldDynamicInfo.includedPeerIDs) {
+ if newDynamicInfo.includedPeerIDs != oldDynamicInfo.includedPeerIDs {
return true
}
- if (newDynamicInfo.excludedPeerIDs != oldDynamicInfo.excludedPeerIDs) {
+ if newDynamicInfo.excludedPeerIDs != oldDynamicInfo.excludedPeerIDs {
return true
}
- if (newDynamicInfo.preapprovals != oldDynamicInfo.preapprovals) {
+ if newDynamicInfo.preapprovals != oldDynamicInfo.preapprovals {
return true
}
return false
// the caller's responsibility to release it after it completes
// (i.e. after reply is invoked).
internal func fetchChangesAndUpdateTrustIfNeeded(stableChanges: StableChanges? = nil,
- changesPending: Bool = false,
- reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
+ peerChanges: Bool = false,
+ reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
self.fetchAndPersistChanges { error in
if let error = error {
- os_log("fetchChangesAndUpdateTrustIfNeeded: fetching failed: %@", log: tplogDebug, type: .default, error as CVarArg)
+ os_log("fetchChangesAndUpdateTrustIfNeeded: fetching failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
reply(nil, error)
return
}
- self.updateTrustIfNeeded(stableChanges: stableChanges, changesPending: changesPending, reply: reply)
+ self.updateTrustIfNeeded(stableChanges: stableChanges, peerChanges: peerChanges, reply: reply)
}
}
// the caller's responsibility to release it after it completes
// (i.e. after reply is invoked).
private func updateTrustIfNeeded(stableChanges: StableChanges? = nil,
- changesPending: Bool = false,
+ peerChanges: Bool = false,
reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
self.moc.performAndWait {
guard let egoPeerID = self.containerMO.egoPeerID else {
// No identity, nothing to do
os_log("updateTrustIfNeeded: No identity.", log: tplogDebug, type: .default)
- reply(TrustedPeersHelperPeerState(peerID: nil, isPreapproved: false, status: .unknown, memberChanges: changesPending, unknownMachineIDs: false, osVersion: nil), nil)
+ reply(TrustedPeersHelperPeerState(peerID: nil, isPreapproved: false, status: .unknown, memberChanges: peerChanges, unknownMachineIDs: false, osVersion: nil), nil)
return
}
loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
guard let signingKeyPair = signingKeyPair else {
- os_log("updateTrustIfNeeded: no signing key pair: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
- reply(TrustedPeersHelperPeerState(peerID: nil, isPreapproved: false, status: .unknown, memberChanges: changesPending, unknownMachineIDs: false, osVersion: nil), error)
+ os_log("updateTrustIfNeeded: no signing key pair: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ reply(TrustedPeersHelperPeerState(peerID: nil, isPreapproved: false, status: .unknown, memberChanges: peerChanges, unknownMachineIDs: false, osVersion: nil), error)
return
}
- guard self.model.hasPeer(withID: egoPeerID) else {
+ guard let currentSelfInModel = self.model.peer(withID: egoPeerID) else {
// Not in circle, nothing to do
let isPreapproved = self.model.hasPotentiallyTrustedPeerPreapprovingKey(signingKeyPair.publicKey().spki())
- os_log("updateTrustIfNeeded: ego peer is not in model, is %@", log: tplogDebug, type: .default, isPreapproved ? "preapproved" : "not yet preapproved")
+ os_log("updateTrustIfNeeded: ego peer is not in model, is %{public}@", log: tplogDebug, type: .default, isPreapproved ? "preapproved" : "not yet preapproved")
reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
isPreapproved: isPreapproved,
status: .unknown,
- memberChanges: changesPending,
+ memberChanges: peerChanges,
unknownMachineIDs: false,
osVersion: nil),
nil)
return
}
- self.moc.performAndWait {
- let dynamicInfo: TPPeerDynamicInfo
- var stableInfo: TPPeerStableInfo?
- do {
- // FIXME We should be able to calculate the contents of dynamicInfo without the signingKeyPair,
- // and then only load the key if it has changed and we need to sign a new one. This would also
- // help make our detection of change immune from non-canonical serialization of dynamicInfo.
- dynamicInfo = try self.model.calculateDynamicInfoForPeer(withID: egoPeerID,
- addingPeerIDs: nil,
- removingPeerIDs: nil,
- preapprovedKeys: nil,
- signing: signingKeyPair,
- currentMachineIDs: self.onqueueCurrentMIDList())
- stableInfo = try self.createNewStableInfoIfNeeded(stableChanges: stableChanges,
- egoPeerID: egoPeerID,
- dynamicInfo: dynamicInfo,
- signingKeyPair: signingKeyPair)
- } catch {
- os_log("updateTrustIfNeeded: couldn't calculate dynamic info: %@", log: tplogDebug, type: .default, error as CVarArg)
- reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
- isPreapproved: false,
- status: self.model.statusOfPeer(withID: egoPeerID),
- memberChanges: changesPending,
- unknownMachineIDs: false,
- osVersion: nil),
- error)
- return
+ // We need to try to have all policy versions that our peers claim to behave
+ let allPolicyVersions = self.model.allPolicyVersions()
+ self.fetchPolicyDocumentsWithSemaphore(versions: allPolicyVersions) { _, policyFetchError in
+ if let error = policyFetchError {
+ os_log("updateTrustIfNeeded: error fetching all requested policies (continuing anyway): %{public}@", log: tplogDebug, type: .default, error as CVarArg)
}
- os_log("updateTrustIfNeeded: produced a stableInfo: %@", log: tplogDebug, type: .default, String(describing: stableInfo))
- os_log("updateTrustIfNeeded: produced a dynamicInfo: %@", log: tplogDebug, type: .default, dynamicInfo)
-
- let peer = self.model.peer(withID: egoPeerID)
- if (stableInfo == nil || stableInfo == peer?.stableInfo) &&
- dynamicInfo == peer?.dynamicInfo {
- os_log("updateTrustIfNeeded: complete.", log: tplogDebug, type: .default)
- // No change to the dynamicInfo: update the MID list now that we've reached a steady state
+ self.moc.performAndWait {
+ let dynamicInfo: TPPeerDynamicInfo
+ var stableInfo: TPPeerStableInfo?
do {
- self.onqueueUpdateMachineIDListFromModel(dynamicInfo: dynamicInfo)
- try self.moc.save()
+
+ // FIXME We should be able to calculate the contents of dynamicInfo without the signingKeyPair,
+ // and then only load the key if it has changed and we need to sign a new one. This would also
+ // help make our detection of change immune from non-canonical serialization of dynamicInfo.
+ dynamicInfo = try self.model.calculateDynamicInfoForPeer(withID: egoPeerID,
+ addingPeerIDs: nil,
+ removingPeerIDs: nil,
+ preapprovedKeys: nil,
+ signing: signingKeyPair,
+ currentMachineIDs: self.onqueueCurrentMIDList())
+
+ stableInfo = try self.createNewStableInfoIfNeeded(stableChanges: stableChanges,
+ egoPeerID: egoPeerID,
+ existingStableInfo: currentSelfInModel.stableInfo,
+ dynamicInfo: dynamicInfo,
+ signingKeyPair: signingKeyPair)
} catch {
- os_log("updateTrustIfNeeded: unable to remove untrusted MachineIDs: %@", log: tplogDebug, type: .default, error as CVarArg)
+ os_log("updateTrustIfNeeded: couldn't calculate dynamic info: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
+ isPreapproved: false,
+ status: self.model.statusOfPeer(withID: egoPeerID),
+ memberChanges: peerChanges,
+ unknownMachineIDs: false,
+ osVersion: nil),
+ error)
+ return
}
- reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
- isPreapproved: false,
- status: self.model.statusOfPeer(withID: egoPeerID),
- memberChanges: changesPending,
- unknownMachineIDs: self.onqueueFullIDMSListWouldBeHelpful(),
- osVersion: peer?.stableInfo?.osVersion),
- nil)
- return
- }
- // Check if we change that should trigger a notification that should trigger TLKShare updates
- let haveChanges = changesPending || self.haveTrustMemberChanges(newDynamicInfo: dynamicInfo, oldDynamicInfo: peer?.dynamicInfo)
+ os_log("updateTrustIfNeeded: produced a stableInfo: %{public}@", log: tplogDebug, type: .default, String(describing: stableInfo))
+ os_log("updateTrustIfNeeded: produced a dynamicInfo: %{public}@", log: tplogDebug, type: .default, dynamicInfo)
+
+ let peer = self.model.peer(withID: egoPeerID)
+ if (stableInfo == nil || stableInfo == peer?.stableInfo) &&
+ dynamicInfo == peer?.dynamicInfo {
+ os_log("updateTrustIfNeeded: complete.", log: tplogDebug, type: .default)
+ // No change to the dynamicInfo: update the MID list now that we've reached a steady state
+ do {
+ self.onqueueUpdateMachineIDListFromModel(dynamicInfo: dynamicInfo)
+ try self.moc.save()
+ } catch {
+ os_log("updateTrustIfNeeded: unable to remove untrusted MachineIDs: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ }
- let signedDynamicInfo = SignedPeerDynamicInfo(dynamicInfo)
- os_log("updateTrustIfNeeded: attempting updateTrust for %@ with: %@", log: tplogDebug, type: .default, egoPeerID, dynamicInfo)
- var request = UpdateTrustRequest.with {
- $0.changeToken = self.containerMO.changeToken ?? ""
- $0.peerID = egoPeerID
- $0.dynamicInfoAndSig = signedDynamicInfo
- }
- if let stableInfo = stableInfo {
- request.stableInfoAndSig = SignedPeerStableInfo(stableInfo)
- }
- self.cuttlefish.updateTrust(request) { response, error in
- os_log("UpdateTrust(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
- guard let response = response, error == nil else {
- os_log("UpdateTrust failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
- reply(nil, error ?? ContainerError.cloudkitResponseMissing)
+ reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
+ isPreapproved: false,
+ status: self.model.statusOfPeer(withID: egoPeerID),
+ memberChanges: peerChanges,
+ unknownMachineIDs: self.onqueueFullIDMSListWouldBeHelpful(),
+ osVersion: peer?.stableInfo?.osVersion),
+ nil)
return
}
+ // Check if we change that should trigger a notification that should trigger TLKShare updates
+ let havePeerChanges = peerChanges || self.haveTrustMemberChanges(newDynamicInfo: dynamicInfo, oldDynamicInfo: peer?.dynamicInfo)
- do {
- try self.persist(changes: response.changes)
- } catch {
- os_log("updateTrust failed: %@", log: tplogDebug, String(describing: error))
- reply(nil, error)
- return
+ let signedDynamicInfo = SignedPeerDynamicInfo(dynamicInfo)
+ os_log("updateTrustIfNeeded: attempting updateTrust for %{public}@ with: %{public}@", log: tplogDebug, type: .default, egoPeerID, dynamicInfo)
+ var request = UpdateTrustRequest.with {
+ $0.changeToken = self.containerMO.changeToken ?? ""
+ $0.peerID = egoPeerID
+ $0.dynamicInfoAndSig = signedDynamicInfo
}
-
- if response.changes.more {
- self.fetchChangesAndUpdateTrustIfNeeded(stableChanges: stableChanges,
- changesPending: haveChanges,
- reply: reply)
- } else {
- self.updateTrustIfNeeded(stableChanges: stableChanges,
- changesPending: haveChanges,
- reply: reply)
+ if let stableInfo = stableInfo {
+ request.stableInfoAndSig = SignedPeerStableInfo(stableInfo)
}
+
+ self.perform(updateTrust: request, stableChanges: stableChanges, peerChanges: havePeerChanges, reply: reply)
}
}
}
}
}
+ private func perform(updateTrust request: UpdateTrustRequest,
+ stableChanges: StableChanges? = nil,
+ peerChanges: Bool = false,
+ reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
+
+ self.cuttlefish.updateTrust(request) { response, error in
+ os_log("UpdateTrust(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+ guard let response = response, error == nil else {
+ os_log("UpdateTrust failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ reply(nil, error ?? ContainerError.cloudkitResponseMissing)
+ return
+ }
+
+ do {
+ try self.persist(changes: response.changes)
+ } catch {
+ os_log("UpdateTrust failed: %{public}@", log: tplogDebug, String(describing: error))
+ reply(nil, error)
+ return
+ }
+
+ if response.changes.more {
+ self.fetchChangesAndUpdateTrustIfNeeded(stableChanges: stableChanges,
+ peerChanges: peerChanges,
+ reply: reply)
+ } else {
+ self.updateTrustIfNeeded(stableChanges: stableChanges,
+ peerChanges: peerChanges,
+ reply: reply)
+ }
+ }
+ }
+
private func persist(changes: Changes) throws {
// This is some nonsense: I can't figure out how to tell swift to throw an exception across performAndWait.
// So, do it ourself
os_log("persist: Received %d peer differences, more: %d", log: tplogDebug, type: .default,
changes.differences.count,
changes.more)
- os_log("persist: New change token: %@", log: tplogDebug, type: .default, changes.changeToken)
+ os_log("persist: New change token: %{public}@", log: tplogDebug, type: .default, changes.changeToken)
do {
try self.onQueuePersist(changes: changes)
self.containerMO.changeToken = changes.changeToken
self.containerMO.moreChanges = changes.more
- if changes.differences.count > 0 {
+ if !changes.differences.isEmpty {
self.model.clearViableBottles()
}
let signingKey = changes.recoverySigningPubKey
let encryptionKey = changes.recoveryEncryptionPubKey
- if signingKey.count > 0 && encryptionKey.count > 0 {
+ if !signingKey.isEmpty && !encryptionKey.isEmpty {
self.addOrUpdate(signingKey: signingKey, encryptionKey: encryptionKey)
}
try self.moc.save()
self.model = Container.loadModel(from: self.containerMO)
try self.moc.save()
} catch {
- os_log("Local delete failed: %@", log: tplogDebug, type: .default, error as CVarArg)
+ os_log("Local delete failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
throw error
}
private func addOrUpdate(signingKey: Data, encryptionKey: Data) {
self.model.setRecoveryKeys(
TPRecoveryKeyPair(signingSPKI: signingKey, encryptionSPKI: encryptionKey))
+
+ self.containerMO.recoveryKeySigningSPKI = signingKey
+ self.containerMO.recoveryKeyEncryptionSPKI = encryptionKey
}
// Must be on moc queue to call this.
guard let policyDoc = self.model.policy(withVersion: policyVersion) else {
throw ContainerError.unknownPolicyVersion(policyVersion)
}
- assert(policyVersion == policyDoc.policyVersion)
- if policyVersion == prevailingPolicyVersion {
- assert(policyDoc.policyHash == prevailingPolicyHash)
+ assert(policyVersion == policyDoc.version.versionNumber)
+ if policyVersion == prevailingPolicyVersion.versionNumber {
+ assert(policyDoc.version.policyHash == prevailingPolicyVersion.policyHash)
}
return policyDoc
}
// Must be on moc queue to call this.
private func createNewStableInfoIfNeeded(stableChanges: StableChanges?,
egoPeerID: String,
+ existingStableInfo: TPPeerStableInfo?,
dynamicInfo: TPPeerDynamicInfo,
signingKeyPair: _SFECKeyPair) throws -> TPPeerStableInfo? {
func noChange<T: Equatable>(_ change: T?, _ existing: T?) -> Bool {
return (nil == change) || change == existing
}
- let existingStableInfo = self.model.peer(withID: egoPeerID)?.stableInfo
+
+ let policyOfPeers = try? self.model.policy(forPeerIDs: dynamicInfo.includedPeerIDs,
+ candidatePeerID: egoPeerID,
+ candidateStableInfo: existingStableInfo)
+
+ // Pick the best version of:
+ // 1. The policy version asked for by the client
+ // 2. The max of our existing policyVersion, the highest policy used by our trusted peers, and the compile-time prevailing policy version
+ let optimalPolicyVersionNumber = stableChanges?.policyVersion ??
+ max(existingStableInfo?.bestPolicyVersion().versionNumber ?? prevailingPolicyVersion.versionNumber,
+ policyOfPeers?.version.versionNumber ?? prevailingPolicyVersion.versionNumber,
+ prevailingPolicyVersion.versionNumber)
+
+ // Determine which recovery key we'd like to be using, given our current idea of who to trust
+ let optimalRecoveryKey = self.model.bestRecoveryKey(for: existingStableInfo, dynamicInfo: dynamicInfo)
+
if noChange(stableChanges?.deviceName, existingStableInfo?.deviceName) &&
noChange(stableChanges?.serialNumber, existingStableInfo?.serialNumber) &&
noChange(stableChanges?.osVersion, existingStableInfo?.osVersion) &&
- noChange(stableChanges?.policyVersion, existingStableInfo?.policyVersion) &&
+ noChange(optimalPolicyVersionNumber, existingStableInfo?.bestPolicyVersion().versionNumber) &&
noChange(stableChanges?.policySecrets, existingStableInfo?.policySecrets) &&
- noChange(stableChanges?.recoverySigningPubKey, existingStableInfo?.recoverySigningPublicKey) &&
- noChange(stableChanges?.recoveryEncryptionPubKey, existingStableInfo?.recoveryEncryptionPublicKey) &&
- self.model.doesPeerRecoveryKeyMatchPeers(egoPeerID) {
+ noChange(optimalRecoveryKey?.signingSPKI, existingStableInfo?.recoverySigningPublicKey) &&
+ noChange(optimalRecoveryKey?.encryptionSPKI, existingStableInfo?.recoveryEncryptionPublicKey) {
return nil
}
- let policyHash: String?
- if let policyVersion = stableChanges?.policyVersion {
- let policyDoc = try self.getPolicyDoc(policyVersion)
- policyHash = policyDoc.policyHash
- } else {
- policyHash = nil
- }
- // Determine which recovery key we'd like to be using, given our current idea of who to trust
- let newRecoveryKeys = self.model.bestRecoveryKey(with: dynamicInfo)
+ let optimalPolicyVersion = try self.getPolicyDoc(optimalPolicyVersionNumber).version
- return try self.model.createStableInfo(withPolicyVersion: stableChanges?.policyVersion ?? existingStableInfo?.policyVersion ?? prevailingPolicyVersion,
- policyHash: policyHash ?? existingStableInfo?.policyHash ?? prevailingPolicyHash,
+ return try self.model.createStableInfo(withFrozenPolicyVersion: frozenPolicyVersion,
+ flexiblePolicyVersion: optimalPolicyVersion,
policySecrets: stableChanges?.policySecrets ?? existingStableInfo?.policySecrets,
deviceName: stableChanges?.deviceName ?? existingStableInfo?.deviceName ?? "",
serialNumber: stableChanges?.serialNumber ?? existingStableInfo?.serialNumber ?? "",
osVersion: stableChanges?.osVersion ?? existingStableInfo?.osVersion ?? "",
signing: signingKeyPair,
- recoverySigningPubKey: newRecoveryKeys?.signingSPKI ?? existingStableInfo?.recoverySigningPublicKey,
- recoveryEncryptionPubKey: newRecoveryKeys?.encryptionSPKI ?? existingStableInfo?.recoveryEncryptionPublicKey)
+ recoverySigningPubKey: optimalRecoveryKey?.signingSPKI,
+ recoveryEncryptionPubKey: optimalRecoveryKey?.encryptionSPKI)
}
private func assembleBottle(egoPeerID: String) throws -> Bottle {
func reportHealth(request: ReportHealthRequest, reply: @escaping (Error?) -> Void) {
self.semaphore.wait()
let reply: (Error?) -> Void = {
- os_log("reportHealth complete %@", log: tplogTrace, type: .info, traceError($0))
+ os_log("reportHealth complete %{public}@", log: tplogTrace, type: .info, traceError($0))
self.semaphore.signal()
reply($0)
}
self.moc.performAndWait {
self.cuttlefish.reportHealth(updatedRequest) { response, error in
- os_log("reportHealth(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+ os_log("reportHealth(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
guard error == nil else {
reply(error)
return
func pushHealthInquiry(reply: @escaping (Error?) -> Void) {
self.semaphore.wait()
let reply: (Error?) -> Void = {
- os_log("reportHealth complete %@", log: tplogTrace, type: .info, traceError($0))
+ os_log("reportHealth complete %{public}@", log: tplogTrace, type: .info, traceError($0))
self.semaphore.signal()
reply($0)
}
self.moc.performAndWait {
self.cuttlefish.pushHealthInquiry(PushHealthInquiryRequest()) { response, error in
- os_log("pushHealthInquiry(): %@, error: %@", log: tplogDebug, "\(String(describing: response))", "\(String(describing: error))")
+ os_log("pushHealthInquiry(): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: response))", "\(String(describing: error))")
guard error == nil else {
reply(error)
return
operation.requestCompletedBlock = requestCompletion
let loggingCompletion = { (response: ResponseType?, error: Error?) -> Void in
- os_log("%@(%@): %@, error: %@",
+ os_log("%{public}@(%{public}@): %{public}@, error: %{public}@",
log: tplogDebug,
function,
"\(String(describing: request))",
let filename = name.container + "-" + name.context + ".TrustedPeersHelper.db"
return SecCopyURLForFileInKeychainDirectory(filename as CFString) as URL
}
+
+ // To be called via test only
+ func removeAllContainers() {
+ queue.sync {
+ self.containers.removeAll()
+ }
+ }
}
--- /dev/null
+import CoreData
+import Foundation
+
+extension Container {
+ func preflightVouchWithBottle(bottleID: String,
+ reply: @escaping (String?, Set<String>?, TPPolicy?, Error?) -> Void) {
+ self.semaphore.wait()
+ let reply: (String?, Set<String>?, TPPolicy?, Error?) -> Void = {
+ os_log("preflightVouchWithBottle complete: %{public}@",
+ log: tplogTrace, type: .info, traceError($3))
+ self.semaphore.signal()
+ reply($0, $1, $2, $3)
+ }
+
+ self.fetchAndPersistChangesIfNeeded { fetchError in
+ guard fetchError == nil else {
+ os_log("preflightVouchWithBottle unable to fetch current peers: %{public}@", log: tplogDebug, type: .default, (fetchError as CVarArg?) ?? "")
+ reply(nil, nil, nil, fetchError)
+ return
+ }
+
+ // Ensure we have all policy versions claimed by peers, including our sponsor
+ let allPolicyVersions = self.model.allPolicyVersions()
+ self.fetchPolicyDocumentsWithSemaphore(versions: allPolicyVersions) { _, fetchPolicyDocumentsError in
+ guard fetchPolicyDocumentsError == nil else {
+ os_log("preflightVouchWithBottle unable to fetch policy documents: %{public}@", log: tplogDebug, type: .default, (fetchPolicyDocumentsError as CVarArg?) ?? "no error")
+ reply(nil, nil, nil, fetchPolicyDocumentsError)
+ return
+ }
+
+ self.moc.performAndWait {
+ guard let egoPeerID = self.containerMO.egoPeerID,
+ let egoPermData = self.containerMO.egoPeerPermanentInfo,
+ let egoPermSig = self.containerMO.egoPeerPermanentInfoSig else {
+ os_log("fetchCurrentPolicy failed to find ego peer information", log: tplogDebug, type: .error)
+ reply(nil, nil, nil, ContainerError.noPreparedIdentity)
+ return
+ }
+
+ let keyFactory = TPECPublicKeyFactory()
+ guard let egoPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
+ os_log("fetchCurrentPolicy failed to create TPPeerPermanentInfo", log: tplogDebug, type: .error)
+ reply(nil, nil, nil, ContainerError.invalidPermanentInfoOrSig)
+ return
+ }
+
+ self.onqueueFindBottle(bottleID: bottleID) { bottleMO, error in
+ guard let bottleMO = bottleMO else {
+ os_log("preflightVouchWithBottle found no bottle: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
+ reply(nil, nil, nil, error)
+ return
+ }
+
+ guard let sponsorPeer = self.model.peer(withID: bottleMO.peerID ?? "") else {
+ os_log("preflightVouchWithBottle found no peer to match bottle", log: tplogDebug, type: .default)
+ reply(nil, nil, nil, ContainerError.sponsorNotRegistered(bottleMO.peerID ?? "no peer ID given"))
+ return
+ }
+
+ guard let sponsorPeerStableInfo = sponsorPeer.stableInfo else {
+ os_log("preflightVouchWithBottle sponsor peer has no stable info", log: tplogDebug, type: .default)
+ reply(nil, nil, nil, ContainerError.sponsorNotRegistered(bottleMO.peerID ?? "no peer ID given"))
+ return
+ }
+
+ do {
+ // We need to extract the syncing policy that the remote peer would have used (if they were the type of device that we are)
+ // So, figure out their policy version...
+ let (views, policy) = try self.policyAndViewsFor(permanentInfo: egoPermanentInfo, stableInfo: sponsorPeerStableInfo)
+
+ reply(bottleMO.peerID, views, policy, nil)
+ } catch {
+ os_log("preflightVouchWithBottle failed to fetch policy: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
+ reply(nil, nil, nil, error)
+ }
+ }
+ }
+ }
+ }
+ }
+}
// Once we run this upgrade, we will set the allowed bool to false, since it's unused.
// Therefore, if we have a single record with "allowed" set, we haven't run the upgrade.
- let runUpgrade = knownMachineMOs.filter { $0.allowed }.count > 0
+ let runUpgrade = !knownMachineMOs.filter { $0.allowed }.isEmpty
if runUpgrade {
knownMachineMOs.forEach { mo in
if mo.allowed {
}
}
- func setAllowedMachineIDs(_ allowedMachineIDs: Set<String>, reply: @escaping (Bool, Error?) -> Void) {
+ func enforceIDMSListChanges(knownMachines: Set<MachineMO>) -> Bool {
+ if self.containerMO.honorIDMSListChanges == "YES"{
+ return true
+ } else if self.containerMO.honorIDMSListChanges == "NO" {
+ return false
+ } else if self.containerMO.honorIDMSListChanges == "UNKNOWN" && knownMachines.isEmpty {
+ return false
+ } else if self.containerMO.honorIDMSListChanges == "UNKNOWN" && !knownMachines.isEmpty {
+ return true
+ } else {
+ return true
+ }
+ }
+
+ func setAllowedMachineIDs(_ allowedMachineIDs: Set<String>, honorIDMSListChanges: Bool, reply: @escaping (Bool, Error?) -> Void) {
self.semaphore.wait()
let reply: (Bool, Error?) -> Void = {
- os_log("setAllowedMachineIDs complete: %@", log: tplogTrace, type: .info, traceError($1))
+ os_log("setAllowedMachineIDs complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
self.semaphore.signal()
reply($0, $1)
}
- os_log("Setting allowed machine IDs: %@", log: tplogDebug, type: .default, allowedMachineIDs)
+ os_log("Setting allowed machine IDs: %{public}@", log: tplogDebug, type: .default, allowedMachineIDs)
// Note: we currently ignore any machineIDs that are set in the model, but never appeared on the
// Trusted Devices list. We should give them a grace period (1wk?) then kick them out.
self.moc.performAndWait {
do {
var differences = false
+ self.containerMO.honorIDMSListChanges = honorIDMSListChanges ? "YES" : "NO"
var knownMachines = containerMO.machines as? Set<MachineMO> ?? Set()
let knownMachineIDs = Set(knownMachines.compactMap { $0.machineID })
knownMachines.forEach { machine in
guard let mid = machine.machineID else {
- os_log("Machine has no ID: %@", log: tplogDebug, type: .default, machine)
+ os_log("Machine has no ID: %{public}@", log: tplogDebug, type: .default, machine)
return
}
if allowedMachineIDs.contains(mid) {
if machine.status == TPMachineIDStatus.allowed.rawValue {
- os_log("Machine ID still trusted: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+ os_log("Machine ID still trusted: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
} else {
- os_log("Machine ID newly retrusted: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+ os_log("Machine ID newly retrusted: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
differences = true
}
machine.status = Int64(TPMachineIDStatus.allowed.rawValue)
if machine.seenOnFullList {
machine.status = Int64(TPMachineIDStatus.disallowed.rawValue)
machine.modified = Date()
- os_log("Newly distrusted machine ID: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+ os_log("Newly distrusted machine ID: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
differences = true
} else {
if machine.modifiedInPast(hours: cutoffHours) {
- os_log("Allowed-but-unseen machine ID isn't on full list, last modified %@, ignoring: %@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
+ os_log("Allowed-but-unseen machine ID isn't on full list, last modified %{public}@, ignoring: %{public}@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
} else {
- os_log("Allowed-but-unseen machine ID isn't on full list, last modified %@, distrusting: %@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
+ os_log("Allowed-but-unseen machine ID isn't on full list, last modified %{public}@, distrusting: %{public}@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
machine.status = Int64(TPMachineIDStatus.disallowed.rawValue)
machine.modified = Date()
differences = true
} else if machine.status == TPMachineIDStatus.unknown.rawValue {
if machine.modifiedInPast(hours: cutoffHours) {
- os_log("Unknown machine ID last modified %@; leaving unknown: %@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
+ os_log("Unknown machine ID last modified %{public}@; leaving unknown: %{public}@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
} else {
- os_log("Unknown machine ID last modified %@; distrusting: %@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
+ os_log("Unknown machine ID last modified %{public}@; distrusting: %{public}@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
machine.status = Int64(TPMachineIDStatus.disallowed.rawValue)
machine.modified = Date()
differences = true
// Do we need to create any further objects?
allowedMachineIDs.forEach { machineID in
- if(!knownMachineIDs.contains(machineID)) {
+ if !knownMachineIDs.contains(machineID) {
// We didn't know about this machine before; it's newly trusted!
let machine = MachineMO(context: self.moc)
machine.machineID = machineID
machine.seenOnFullList = true
machine.modified = Date()
machine.status = Int64(TPMachineIDStatus.allowed.rawValue)
- os_log("Newly trusted machine ID: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+ os_log("Newly trusted machine ID: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
differences = true
self.containerMO.addToMachines(machine)
}
}
- // if this account is not a demo account...
- if knownMachines.count > 0 {
+ if self.enforceIDMSListChanges(knownMachines: knownMachines) {
// Are there any machine IDs in the model that aren't in the list? If so, add them as "unknown"
let modelMachineIDs = self.model.allMachineIDs()
modelMachineIDs.forEach { peerMachineID in
if !knownMachineIDs.contains(peerMachineID) && !allowedMachineIDs.contains(peerMachineID) {
- os_log("Peer machineID is unknown, beginning grace period: %@", log: tplogDebug, type: .default, peerMachineID)
+ os_log("Peer machineID is unknown, beginning grace period: %{public}@", log: tplogDebug, type: .default, peerMachineID)
let machine = MachineMO(context: self.moc)
machine.machineID = peerMachineID
machine.container = containerMO
}
}
} else {
- os_log("Believe we're in a demo account; not starting an unknown machine ID grace period", log: tplogDebug, type: .default)
+ os_log("Believe we're in a demo account, not enforcing IDMS list", log: tplogDebug, type: .default)
}
// We no longer use allowed machine IDs.
reply(differences, nil)
} catch {
- os_log("Error setting machine ID list: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+ os_log("Error setting machine ID list: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
reply(false, error)
}
}
func addAllow(_ machineIDs: [String], reply: @escaping (Error?) -> Void) {
self.semaphore.wait()
let reply: (Error?) -> Void = {
- os_log("addAllow complete: %@", log: tplogTrace, type: .info, traceError($0))
+ os_log("addAllow complete: %{public}@", log: tplogTrace, type: .info, traceError($0))
self.semaphore.signal()
reply($0)
}
- os_log("Adding allowed machine IDs: %@", log: tplogDebug, type: .default, machineIDs)
+ os_log("Adding allowed machine IDs: %{public}@", log: tplogDebug, type: .default, machineIDs)
self.moc.performAndWait {
do {
if machine.machineID == machineID {
machine.status = Int64(TPMachineIDStatus.allowed.rawValue)
machine.modified = Date()
- os_log("Continue to trust machine ID: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+ os_log("Continue to trust machine ID: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
}
}
machine.seenOnFullList = false
machine.modified = Date()
machine.status = Int64(TPMachineIDStatus.allowed.rawValue)
- os_log("Newly trusted machine ID: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+ os_log("Newly trusted machine ID: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
self.containerMO.addToMachines(machine)
knownMachines.insert(machine)
func removeAllow(_ machineIDs: [String], reply: @escaping (Error?) -> Void) {
self.semaphore.wait()
let reply: (Error?) -> Void = {
- os_log("removeAllow complete: %@", log: tplogTrace, type: .info, traceError($0))
+ os_log("removeAllow complete: %{public}@", log: tplogTrace, type: .info, traceError($0))
self.semaphore.signal()
reply($0)
}
- os_log("Removing allowed machine IDs: %@", log: tplogDebug, type: .default, machineIDs)
+ os_log("Removing allowed machine IDs: %{public}@", log: tplogDebug, type: .default, machineIDs)
self.moc.performAndWait {
do {
if machine.machineID == machineID {
machine.status = Int64(TPMachineIDStatus.unknown.rawValue)
machine.modified = Date.distantPast
- os_log("Now suspicious of machine ID: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+ os_log("Now suspicious of machine ID: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
}
}
machine.container = containerMO
machine.status = Int64(TPMachineIDStatus.unknown.rawValue)
machine.modified = Date.distantPast
- os_log("Suspicious of new machine ID: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+ os_log("Suspicious of new machine ID: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
self.containerMO.addToMachines(machine)
knownMachines.insert(machine)
func fetchAllowedMachineIDs(reply: @escaping (Set<String>?, Error?) -> Void) {
self.semaphore.wait()
let reply: (Set<String>?, Error?) -> Void = {
- os_log("fetchAllowedMachineIDs complete: %@", log: tplogTrace, type: .info, traceError($1))
+ os_log("fetchAllowedMachineIDs complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
self.semaphore.signal()
reply($0, $1)
}
self.moc.performAndWait {
let knownMachines = containerMO.machines as? Set<MachineMO> ?? Set()
- let allowedMachineIDs = knownMachines.filter { $0.status == Int64(TPMachineIDStatus.allowed.rawValue) }.compactMap({ $0.machineID })
+ let allowedMachineIDs = knownMachines.filter { $0.status == Int64(TPMachineIDStatus.allowed.rawValue) }.compactMap { $0.machineID }
reply(Set(allowedMachineIDs), nil)
}
}
func onqueueMachineIDAllowedByIDMS(machineID: String) -> Bool {
+
// For Demo accounts, if the list is entirely empty, then everything is allowed
- let machines = containerMO.machines as? Set<MachineMO> ?? Set()
- if machines.count == 0 {
- os_log("machineID list is empty; allowing %@", log: tplogDebug, type: .debug, machineID)
+ let knownMachines = containerMO.machines as? Set<MachineMO> ?? Set()
+
+ if !self.enforceIDMSListChanges(knownMachines: knownMachines) {
+ os_log("not enforcing idms list changes; allowing %{public}@", log: tplogDebug, type: .debug, machineID)
return true
}
// Note: this function rejects grey devices: machineIDs that are neither allowed nor disallowed
- for mo in machines {
- if mo.machineID == machineID {
- if mo.status == TPMachineIDStatus.allowed.rawValue {
- return true
- } else {
- os_log("machineID %@ not explicitly allowed: %@", log: tplogDebug, type: .debug, machineID, mo)
- return false
- }
+ for mo in knownMachines where mo.machineID == machineID {
+ if mo.status == TPMachineIDStatus.allowed.rawValue {
+ return true
+ } else {
+ os_log("machineID %{public}@ not explicitly allowed: %{public}@", log: tplogDebug, type: .debug, machineID, mo)
+ return false
}
}
// Didn't find it? reject.
- os_log("machineID %@ not found on list", log: tplogDebug, type: .debug, machineID)
+ os_log("machineID %{public}@ not found on list", log: tplogDebug, type: .debug, machineID)
return false
}
let trustedMachineIDs = Set(dynamicInfo.includedPeerIDs.compactMap { self.model.peer(withID: $0)?.permanentInfo.machineID })
// if this account is not a demo account...
- if machines.count > 0 {
+ if self.enforceIDMSListChanges(knownMachines: machines) {
for peerMachineID in trustedMachineIDs.subtracting(knownMachineIDs) {
- os_log("Peer machineID is unknown, beginning grace period: %@", log: tplogDebug, type: .default, peerMachineID)
+ os_log("Peer machineID is unknown, beginning grace period: %{public}@", log: tplogDebug, type: .default, peerMachineID)
let machine = MachineMO(context: self.moc)
machine.machineID = peerMachineID
machine.container = self.containerMO
self.containerMO.addToMachines(machine)
}
} else {
- os_log("Believe we're in a demo account; not starting an unknown machine ID timer", log: tplogDebug, type: .default)
+ os_log("Not enforcing IDMS list changes", log: tplogDebug, type: .default)
}
- for mo in (machines) {
- if mo.status == TPMachineIDStatus.disallowed.rawValue {
- os_log("Dropping knowledge of machineID %@", log: tplogDebug, type: .debug, String(describing: mo.machineID))
- self.containerMO.removeFromMachines(mo)
- }
+ for mo in (machines) where mo.status == TPMachineIDStatus.disallowed.rawValue {
+ os_log("Dropping knowledge of machineID %{public}@", log: tplogDebug, type: .debug, String(describing: mo.machineID))
+ self.containerMO.removeFromMachines(mo)
}
}
// Useful means that there's an unknown MID whose modification date is before the cutoff
// A full list fetch would either confirm it as 'untrusted' or make it trusted again
func onqueueFullIDMSListWouldBeHelpful() -> Bool {
+
+ if self.containerMO.honorIDMSListChanges == "UNKNOWN" {
+ return true
+ }
+
let unknownMOs = (containerMO.machines as? Set<MachineMO> ?? Set()).filter { $0.status == TPMachineIDStatus.unknown.rawValue }
let outdatedMOs = unknownMOs.filter { !$0.modifiedInPast(hours: cutoffHours) }
- return outdatedMOs.count > 0
+ return !outdatedMOs.isEmpty
}
}
--- /dev/null
+import CoreData
+import Foundation
+
+extension Container {
+ func preflightVouchWithRecoveryKey(recoveryKey: String,
+ salt: String,
+ reply: @escaping (String?, Set<String>?, TPPolicy?, Error?) -> Void) {
+ self.semaphore.wait()
+ let reply: (String?, Set<String>?, TPPolicy?, Error?) -> Void = {
+ os_log("preflightRecoveryKey complete: %{public}@",
+ log: tplogTrace, type: .info, traceError($3))
+ self.semaphore.signal()
+ reply($0, $1, $2, $3)
+ }
+
+ self.fetchAndPersistChangesIfNeeded { fetchError in
+ guard fetchError == nil else {
+ os_log("preflightRecoveryKey unable to fetch current peers: %{public}@", log: tplogDebug, type: .default, (fetchError as CVarArg?) ?? "")
+ reply(nil, nil, nil, fetchError)
+ return
+ }
+
+ // Ensure we have all policy versions claimed by peers, including our sponsor
+ self.fetchPolicyDocumentsWithSemaphore(versions: self.model.allPolicyVersions()) { _, fetchPolicyDocumentsError in
+ guard fetchPolicyDocumentsError == nil else {
+ os_log("preflightRecoveryKey unable to fetch policy documents: %{public}@", log: tplogDebug, type: .default, (fetchPolicyDocumentsError as CVarArg?) ?? "no error")
+ reply(nil, nil, nil, fetchPolicyDocumentsError)
+ return
+ }
+
+ self.moc.performAndWait {
+ guard let egoPeerID = self.containerMO.egoPeerID,
+ let egoPermData = self.containerMO.egoPeerPermanentInfo,
+ let egoPermSig = self.containerMO.egoPeerPermanentInfoSig else {
+ os_log("preflightRecoveryKey: no ego peer ID", log: tplogDebug, type: .default)
+ reply(nil, nil, nil, ContainerError.noPreparedIdentity)
+ return
+ }
+
+ let keyFactory = TPECPublicKeyFactory()
+ guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
+ reply(nil, nil, nil, ContainerError.invalidPermanentInfoOrSig)
+ return
+ }
+
+ var recoveryKeys: RecoveryKey
+ do {
+ recoveryKeys = try RecoveryKey(recoveryKeyString: recoveryKey, recoverySalt: salt)
+ } catch {
+ os_log("preflightRecoveryKey: failed to create recovery keys: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ reply(nil, nil, nil, ContainerError.failedToCreateRecoveryKey)
+ return
+ }
+
+ // Dear model: if i were to use this recovery key, what peers would I end up using?
+ guard self.model.isRecoveryKeyEnrolled() else {
+ os_log("preflightRecoveryKey: recovery Key is not enrolled", log: tplogDebug, type: .default)
+ reply(nil, nil, nil, ContainerError.recoveryKeysNotEnrolled)
+ return
+ }
+
+ guard let sponsorPeerID = self.model.peerIDThatTrustsRecoveryKeys(TPRecoveryKeyPair(signingSPKI: recoveryKeys.peerKeys.signingKey.publicKey.keyData,
+ encryptionSPKI: recoveryKeys.peerKeys.encryptionKey.publicKey.keyData)) else {
+ os_log("preflightRecoveryKey Untrusted recovery key set", log: tplogDebug, type: .default)
+ reply(nil, nil, nil, ContainerError.untrustedRecoveryKeys)
+ return
+ }
+
+ guard let sponsor = self.model.peer(withID: sponsorPeerID) else {
+ os_log("preflightRecoveryKey Failed to find peer with ID", log: tplogDebug, type: .default)
+ reply(nil, nil, nil, ContainerError.sponsorNotRegistered(sponsorPeerID))
+ return
+ }
+
+ do {
+ let bestPolicy = try self.model.policy(forPeerIDs: sponsor.dynamicInfo?.includedPeerIDs ?? [sponsor.peerID],
+ candidatePeerID: egoPeerID,
+ candidateStableInfo: sponsor.stableInfo)
+
+ let views = try bestPolicy.views(forModel: selfPermanentInfo.modelID)
+ reply(recoveryKeys.peerKeys.peerID, views, bestPolicy, nil)
+ } catch {
+ os_log("preflightRecoveryKey: error fetching policy: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+ reply(nil, nil, nil, error)
+ return
+ }
+ }
+ }
+ }
+ }
+}
}
// Use a 'pattern match operator' to make pretty case statements matching Cuttlefish errors
-func ~=(pattern: CuttlefishErrorMatcher, value: Error?) -> Bool {
+func ~= (pattern: CuttlefishErrorMatcher, value: Error?) -> Bool {
guard let error = value else {
return false
}
import Foundation
struct RawPolicy {
- let policyVersion: Int
- let policyHash: String
+ let version: TPPolicyVersion
let policyData: String
let plaintextPolicy: TPPolicyDocument
}
-let prevailingPolicyVersion: UInt64 = 5
-let prevailingPolicyHash: String = "SHA256:O/ECQlWhvNlLmlDNh2+nal/yekUC87bXpV3k+6kznSo="
+let prevailingPolicyVersion = TPPolicyVersion(version: 6, hash: "SHA256:L2Px1aYyR1tgChe8dIyTBSmCHCWEFJirZ3ELMFXz2PY=")
+
+// Some peers don't know how to handle new policies when pairing. If we're pairing with one of those,
+// we must prepare our identity using this policy.
+let frozenPolicyVersion = TPPolicyVersion(version: 5, hash: "SHA256:O/ECQlWhvNlLmlDNh2+nal/yekUC87bXpV3k+6kznSo=")
func builtInPolicyDocuments() -> [TPPolicyDocument] {
+ // swiftlint:disable force_try
// These bytes are generated by tppolicy
let rawPolicies = [
RawPolicy(
- policyVersion: 1,
- policyHash: "SHA256:TLXrcQmY4ue3oP5pCX1pwsi9BF8cKfohlJBilCroeBs=",
+ version: TPPolicyVersion(version: 1, hash: "SHA256:TLXrcQmY4ue3oP5pCX1pwsi9BF8cKfohlJBilCroeBs="),
policyData: "CAESDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSCwoDTWFjEgRmdWxsEgwKBGlNYWMSBGZ1bGwSDQoHQXBwbGVUVhICdHYSDgoFV2F0Y2gSBXdhdGNoGhEKCVBDU0VzY3JvdxIEZnVsbBoXCgRXaUZpEgRmdWxsEgJ0dhIFd2F0Y2gaGQoRU2FmYXJpQ3JlZGl0Q2FyZHMSBGZ1bGwiDAoEZnVsbBIEZnVsbCIUCgV3YXRjaBIEZnVsbBIFd2F0Y2giDgoCdHYSBGZ1bGwSAnR2",
plaintextPolicy: try! TPPolicyDocument(version: 1,
modelToCategory: [
),
RawPolicy(
- policyVersion: 2,
- policyHash: "SHA256:ZL1WBUCyO155rHBJQeghomCCKGmfjtS0jvsK+UEvx5o=",
+ version: TPPolicyVersion(version: 2, hash: "SHA256:ZL1WBUCyO155rHBJQeghomCCKGmfjtS0jvsK+UEvx5o="),
policyData: "CAISDgoGaUN5Y2xlEgRmdWxsEg4KBmlQaG9uZRIEZnVsbBIMCgRpUGFkEgRmdWxsEgsKA01hYxIEZnVsbBIMCgRpTWFjEgRmdWxsEg0KB0FwcGxlVFYSAnR2Eg4KBVdhdGNoEgV3YXRjaBoRCglQQ1NFc2Nyb3cSBGZ1bGwaFwoEV2lGaRIEZnVsbBICdHYSBXdhdGNoGhkKEVNhZmFyaUNyZWRpdENhcmRzEgRmdWxsIgwKBGZ1bGwSBGZ1bGwiFAoFd2F0Y2gSBGZ1bGwSBXdhdGNoIg4KAnR2EgRmdWxsEgJ0dg==",
plaintextPolicy: try! TPPolicyDocument(version: 2,
modelToCategory: [
hashAlgo: .SHA256)
),
- RawPolicy(policyVersion: 3,
- policyHash: "SHA256:JZzazSuHXrUhiOfSgElsg6vYKpnvvEPVpciR8FewRWg=",
+ RawPolicy(version: TPPolicyVersion(version: 3, hash: "SHA256:JZzazSuHXrUhiOfSgElsg6vYKpnvvEPVpciR8FewRWg="),
policyData: "CAMSDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSCwoDTWFjEgRmdWxsEgwKBGlNYWMSBGZ1bGwSDQoHQXBwbGVUVhICdHYSDgoFV2F0Y2gSBXdhdGNoEhcKDkF1ZGlvQWNjZXNzb3J5EgVhdWRpbxocCg1EZXZpY2VQYWlyaW5nEgRmdWxsEgV3YXRjaBoXCghBcHBsZVBheRIEZnVsbBIFd2F0Y2gaJAoVUHJvdGVjdGVkQ2xvdWRTdG9yYWdlEgRmdWxsEgV3YXRjaBoXCghCYWNrc3RvcBIEZnVsbBIFd2F0Y2gaGQoKQXV0b1VubG9jaxIEZnVsbBIFd2F0Y2gaHwoQU2VjdXJlT2JqZWN0U3luYxIEZnVsbBIFd2F0Y2gaIAoRU2FmYXJpQ3JlZGl0Q2FyZHMSBGZ1bGwSBXdhdGNoGhMKBEhvbWUSBGZ1bGwSBXdhdGNoGh4KD1NhZmFyaVBhc3N3b3JkcxIEZnVsbBIFd2F0Y2gaGwoMQXBwbGljYXRpb25zEgRmdWxsEgV3YXRjaBoVCgZFbmdyYW0SBGZ1bGwSBXdhdGNoGi0KE0xpbWl0ZWRQZWVyc0FsbG93ZWQSBGZ1bGwSBXdhdGNoEgJ0dhIFYXVkaW8aFgoHTWFuYXRlZRIEZnVsbBIFd2F0Y2gaHgoEV2lGaRIEZnVsbBIFd2F0Y2gSAnR2EgVhdWRpbxoVCgZIZWFsdGgSBGZ1bGwSBXdhdGNoIhMKBGZ1bGwSBGZ1bGwSBXdhdGNoIhsKBWF1ZGlvEgRmdWxsEgV3YXRjaBIFYXVkaW8iFAoFd2F0Y2gSBGZ1bGwSBXdhdGNoIhUKAnR2EgRmdWxsEgV3YXRjaBICdHYyIgoWAAQiEgIEdndodAoKXkFwcGxlUGF5JBIIQXBwbGVQYXkyJgoYAAQiFAIEdndodAoMXkF1dG9VbmxvY2skEgpBdXRvVW5sb2NrMh4KFAAEIhACBHZ3aHQKCF5FbmdyYW0kEgZFbmdyYW0yHgoUAAQiEAIEdndodAoIXkhlYWx0aCQSBkhlYWx0aDIaChIABCIOAgR2d2h0CgZeSG9tZSQSBEhvbWUyIAoVAAQiEQIEdndodAoJXk1hbmF0ZWUkEgdNYW5hdGVlMjgKIQAEIh0CBHZ3aHQKFV5MaW1pdGVkUGVlcnNBbGxvd2VkJBITTGltaXRlZFBlZXJzQWxsb3dlZDJdClAAAhIeAAQiGgIEdndodAoSXkNvbnRpbnVpdHlVbmxvY2skEhUABCIRAgR2d2h0CgleSG9tZUtpdCQSFQAEIhECBHZ3aHQKCV5BcHBsZVRWJBIJTm90U3luY2VkMisKGwAEIhcCBGFncnAKD15bMC05QS1aXXsxMH1cLhIMQXBwbGljYXRpb25zMsUBCrABAAISNAABChMABCIPAgVjbGFzcwoGXmdlbnAkChsABCIXAgRhZ3JwCg9eY29tLmFwcGxlLnNiZCQSPQABChMABCIPAgVjbGFzcwoGXmtleXMkCiQABCIgAgRhZ3JwChheY29tLmFwcGxlLnNlY3VyaXR5LnNvcyQSGQAEIhUCBHZ3aHQKDV5CYWNrdXBCYWdWMCQSHAAEIhgCBHZ3aHQKEF5pQ2xvdWRJZGVudGl0eSQSEFNlY3VyZU9iamVjdFN5bmMyYwpbAAISEgAEIg4CBHZ3aHQKBl5XaUZpJBJDAAEKEwAEIg8CBWNsYXNzCgZeZ2VucCQKEwAEIg8CBGFncnAKB15hcHBsZSQKFQAEIhECBHN2Y2UKCV5BaXJQb3J0JBIEV2lGaTLbAgrBAgACEhkABCIVAgR2d2h0Cg1eUENTQ2xvdWRLaXQkEhcABCITAgR2d2h0CgteUENTRXNjcm93JBIUAAQiEAIEdndodAoIXlBDU0ZERSQSGQAEIhUCBHZ3aHQKDV5QQ1NGZWxkc3BhciQSGQAEIhUCBHZ3aHQKDV5QQ1NNYWlsRHJvcCQSGgAEIhYCBHZ3aHQKDl5QQ1NNYXN0ZXJLZXkkEhYABCISAgR2d2h0CgpeUENTTm90ZXMkEhcABCITAgR2d2h0CgteUENTUGhvdG9zJBIYAAQiFAIEdndodAoMXlBDU1NoYXJpbmckEh0ABCIZAgR2d2h0ChFeUENTaUNsb3VkQmFja3VwJBIcAAQiGAIEdndodAoQXlBDU2lDbG91ZERyaXZlJBIZAAQiFQIEdndodAoNXlBDU2lNZXNzYWdlJBIVUHJvdGVjdGVkQ2xvdWRTdG9yYWdlMkAKKwAEIicCBGFncnAKH15jb20uYXBwbGUuc2FmYXJpLmNyZWRpdC1jYXJkcyQSEVNhZmFyaUNyZWRpdENhcmRzMjQKIQAEIh0CBGFncnAKFV5jb20uYXBwbGUuY2ZuZXR3b3JrJBIPU2FmYXJpUGFzc3dvcmRzMm0KXAACEh4ABCIaAgR2d2h0ChJeQWNjZXNzb3J5UGFpcmluZyQSGgAEIhYCBHZ3aHQKDl5OYW5vUmVnaXN0cnkkEhwABCIYAgR2d2h0ChBeV2F0Y2hNaWdyYXRpb24kEg1EZXZpY2VQYWlyaW5nMi0KIQAEIh0CBGFncnAKFV5jb20uYXBwbGUuY2ZuZXR3b3JrJBIIQmFja3N0b3A=",
plaintextPolicy: try! TPPolicyDocument(version: 3,
modelToCategory: [
],
hashAlgo: .SHA256)
),
- RawPolicy(policyVersion: 4,
- policyHash: "SHA256:Tjdu5QrWGvKWMx7k3VWFrEWSsBDPZAwCql9ybDkvFs8=",
+ RawPolicy(version: TPPolicyVersion(version: 4, hash: "SHA256:Tjdu5QrWGvKWMx7k3VWFrEWSsBDPZAwCql9ybDkvFs8="),
policyData: "CAQSDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSCwoDTWFjEgRmdWxsEgwKBGlNYWMSBGZ1bGwSDQoHQXBwbGVUVhICdHYSDgoFV2F0Y2gSBXdhdGNoEhcKDkF1ZGlvQWNjZXNzb3J5EgVhdWRpbxoTCgRIb21lEgRmdWxsEgV3YXRjaBobCgxBcHBsaWNhdGlvbnMSBGZ1bGwSBXdhdGNoGh4KBFdpRmkSBGZ1bGwSBXdhdGNoEgJ0dhIFYXVkaW8aGQoKQXV0b1VubG9jaxIEZnVsbBIFd2F0Y2gaFwoIQXBwbGVQYXkSBGZ1bGwSBXdhdGNoGhUKBkhlYWx0aBIEZnVsbBIFd2F0Y2gaFgoHTWFuYXRlZRIEZnVsbBIFd2F0Y2gaLQoTTGltaXRlZFBlZXJzQWxsb3dlZBIEZnVsbBIFd2F0Y2gSAnR2EgVhdWRpbxokChVQcm90ZWN0ZWRDbG91ZFN0b3JhZ2USBGZ1bGwSBXdhdGNoGhgKCVBhc3N3b3JkcxIEZnVsbBIFd2F0Y2gaHAoNRGV2aWNlUGFpcmluZxIEZnVsbBIFd2F0Y2gaHwoQU2VjdXJlT2JqZWN0U3luYxIEZnVsbBIFd2F0Y2gaFQoGRW5ncmFtEgRmdWxsEgV3YXRjaBoaCgtDcmVkaXRDYXJkcxIEZnVsbBIFd2F0Y2giGwoFYXVkaW8SBGZ1bGwSBXdhdGNoEgVhdWRpbyITCgRmdWxsEgRmdWxsEgV3YXRjaCIUCgV3YXRjaBIEZnVsbBIFd2F0Y2giFQoCdHYSBGZ1bGwSBXdhdGNoEgJ0djIiChYABCISAgR2d2h0CgpeQXBwbGVQYXkkEghBcHBsZVBheTImChgABCIUAgR2d2h0CgxeQXV0b1VubG9jayQSCkF1dG9VbmxvY2syHgoUAAQiEAIEdndodAoIXkVuZ3JhbSQSBkVuZ3JhbTIeChQABCIQAgR2d2h0CgheSGVhbHRoJBIGSGVhbHRoMhoKEgAEIg4CBHZ3aHQKBl5Ib21lJBIESG9tZTIgChUABCIRAgR2d2h0CgleTWFuYXRlZSQSB01hbmF0ZWUyOAohAAQiHQIEdndodAoVXkxpbWl0ZWRQZWVyc0FsbG93ZWQkEhNMaW1pdGVkUGVlcnNBbGxvd2VkMl0KUAACEh4ABCIaAgR2d2h0ChJeQ29udGludWl0eVVubG9jayQSFQAEIhECBHZ3aHQKCV5Ib21lS2l0JBIVAAQiEQIEdndodAoJXkFwcGxlVFYkEglOb3RTeW5jZWQyKwobAAQiFwIEYWdycAoPXlswLTlBLVpdezEwfVwuEgxBcHBsaWNhdGlvbnMyxQEKsAEAAhI0AAEKEwAEIg8CBWNsYXNzCgZeZ2VucCQKGwAEIhcCBGFncnAKD15jb20uYXBwbGUuc2JkJBI9AAEKEwAEIg8CBWNsYXNzCgZea2V5cyQKJAAEIiACBGFncnAKGF5jb20uYXBwbGUuc2VjdXJpdHkuc29zJBIZAAQiFQIEdndodAoNXkJhY2t1cEJhZ1YwJBIcAAQiGAIEdndodAoQXmlDbG91ZElkZW50aXR5JBIQU2VjdXJlT2JqZWN0U3luYzJjClsAAhISAAQiDgIEdndodAoGXldpRmkkEkMAAQoTAAQiDwIFY2xhc3MKBl5nZW5wJAoTAAQiDwIEYWdycAoHXmFwcGxlJAoVAAQiEQIEc3ZjZQoJXkFpclBvcnQkEgRXaUZpMucCCs0CAAISGgAEIhYCBHZ3aHQKDl5QQ1MtQ2xvdWRLaXQkEhgABCIUAgR2d2h0CgxeUENTLUVzY3JvdyQSFQAEIhECBHZ3aHQKCV5QQ1MtRkRFJBIaAAQiFgIEdndodAoOXlBDUy1GZWxkc3BhciQSGgAEIhYCBHZ3aHQKDl5QQ1MtTWFpbERyb3AkEhsABCIXAgR2d2h0Cg9eUENTLU1hc3RlcktleSQSFwAEIhMCBHZ3aHQKC15QQ1MtTm90ZXMkEhgABCIUAgR2d2h0CgxeUENTLVBob3RvcyQSGQAEIhUCBHZ3aHQKDV5QQ1MtU2hhcmluZyQSHgAEIhoCBHZ3aHQKEl5QQ1MtaUNsb3VkQmFja3VwJBIdAAQiGQIEdndodAoRXlBDUy1pQ2xvdWREcml2ZSQSGgAEIhYCBHZ3aHQKDl5QQ1MtaU1lc3NhZ2UkEhVQcm90ZWN0ZWRDbG91ZFN0b3JhZ2UyOgorAAQiJwIEYWdycAofXmNvbS5hcHBsZS5zYWZhcmkuY3JlZGl0LWNhcmRzJBILQ3JlZGl0Q2FyZHMyLgohAAQiHQIEYWdycAoVXmNvbS5hcHBsZS5jZm5ldHdvcmskEglQYXNzd29yZHMybQpcAAISHgAEIhoCBHZ3aHQKEl5BY2Nlc3NvcnlQYWlyaW5nJBIaAAQiFgIEdndodAoOXk5hbm9SZWdpc3RyeSQSHAAEIhgCBHZ3aHQKEF5XYXRjaE1pZ3JhdGlvbiQSDURldmljZVBhaXJpbmc=",
plaintextPolicy: try! TPPolicyDocument(version: 4,
modelToCategory: [
hashAlgo: .SHA256)
),
- RawPolicy(policyVersion: 5,
- policyHash: "SHA256:O/ECQlWhvNlLmlDNh2+nal/yekUC87bXpV3k+6kznSo=",
+ RawPolicy(version: TPPolicyVersion(version: 5, hash: "SHA256:O/ECQlWhvNlLmlDNh2+nal/yekUC87bXpV3k+6kznSo="),
policyData: "CAUSDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSDAoEaVBvZBIEZnVsbBILCgNNYWMSBGZ1bGwSDAoEaU1hYxIEZnVsbBINCgdBcHBsZVRWEgJ0dhIOCgVXYXRjaBIFd2F0Y2gSFwoOQXVkaW9BY2Nlc3NvcnkSBWF1ZGlvGhsKDEFwcGxpY2F0aW9ucxIEZnVsbBIFd2F0Y2gaHwoQU2VjdXJlT2JqZWN0U3luYxIEZnVsbBIFd2F0Y2gaHAoNRGV2aWNlUGFpcmluZxIEZnVsbBIFd2F0Y2gaGgoLQ3JlZGl0Q2FyZHMSBGZ1bGwSBXdhdGNoGhUKBkhlYWx0aBIEZnVsbBIFd2F0Y2gaLQoTTGltaXRlZFBlZXJzQWxsb3dlZBIEZnVsbBIFd2F0Y2gSAnR2EgVhdWRpbxokChVQcm90ZWN0ZWRDbG91ZFN0b3JhZ2USBGZ1bGwSBXdhdGNoGhcKCEFwcGxlUGF5EgRmdWxsEgV3YXRjaBoZCgpBdXRvVW5sb2NrEgRmdWxsEgV3YXRjaBoWCgdNYW5hdGVlEgRmdWxsEgV3YXRjaBoYCglQYXNzd29yZHMSBGZ1bGwSBXdhdGNoGhUKBkVuZ3JhbRIEZnVsbBIFd2F0Y2gaHgoEV2lGaRIEZnVsbBIFd2F0Y2gSAnR2EgVhdWRpbxoTCgRIb21lEgRmdWxsEgV3YXRjaCIbCgVhdWRpbxIEZnVsbBIFd2F0Y2gSBWF1ZGlvIhMKBGZ1bGwSBGZ1bGwSBXdhdGNoIhUKAnR2EgRmdWxsEgV3YXRjaBICdHYiFAoFd2F0Y2gSBGZ1bGwSBXdhdGNoMiIKFgAEIhICBHZ3aHQKCl5BcHBsZVBheSQSCEFwcGxlUGF5MiYKGAAEIhQCBHZ3aHQKDF5BdXRvVW5sb2NrJBIKQXV0b1VubG9jazIeChQABCIQAgR2d2h0CgheRW5ncmFtJBIGRW5ncmFtMh4KFAAEIhACBHZ3aHQKCF5IZWFsdGgkEgZIZWFsdGgyGgoSAAQiDgIEdndodAoGXkhvbWUkEgRIb21lMiAKFQAEIhECBHZ3aHQKCV5NYW5hdGVlJBIHTWFuYXRlZTI4CiEABCIdAgR2d2h0ChVeTGltaXRlZFBlZXJzQWxsb3dlZCQSE0xpbWl0ZWRQZWVyc0FsbG93ZWQyXQpQAAISHgAEIhoCBHZ3aHQKEl5Db250aW51aXR5VW5sb2NrJBIVAAQiEQIEdndodAoJXkhvbWVLaXQkEhUABCIRAgR2d2h0CgleQXBwbGVUViQSCU5vdFN5bmNlZDIrChsABCIXAgRhZ3JwCg9eWzAtOUEtWl17MTB9XC4SDEFwcGxpY2F0aW9uczLFAQqwAQACEjQAAQoTAAQiDwIFY2xhc3MKBl5nZW5wJAobAAQiFwIEYWdycAoPXmNvbS5hcHBsZS5zYmQkEj0AAQoTAAQiDwIFY2xhc3MKBl5rZXlzJAokAAQiIAIEYWdycAoYXmNvbS5hcHBsZS5zZWN1cml0eS5zb3MkEhkABCIVAgR2d2h0Cg1eQmFja3VwQmFnVjAkEhwABCIYAgR2d2h0ChBeaUNsb3VkSWRlbnRpdHkkEhBTZWN1cmVPYmplY3RTeW5jMmMKWwACEhIABCIOAgR2d2h0CgZeV2lGaSQSQwABChMABCIPAgVjbGFzcwoGXmdlbnAkChMABCIPAgRhZ3JwCgdeYXBwbGUkChUABCIRAgRzdmNlCgleQWlyUG9ydCQSBFdpRmkynQMKgwMAAhIYAAQiFAIEdndodAoMXlBDUy1CYWNrdXAkEhoABCIWAgR2d2h0Cg5eUENTLUNsb3VkS2l0JBIYAAQiFAIEdndodAoMXlBDUy1Fc2Nyb3ckEhUABCIRAgR2d2h0CgleUENTLUZERSQSGgAEIhYCBHZ3aHQKDl5QQ1MtRmVsZHNwYXIkEhoABCIWAgR2d2h0Cg5eUENTLU1haWxEcm9wJBIaAAQiFgIEdndodAoOXlBDUy1NYWlsZHJvcCQSGwAEIhcCBHZ3aHQKD15QQ1MtTWFzdGVyS2V5JBIXAAQiEwIEdndodAoLXlBDUy1Ob3RlcyQSGAAEIhQCBHZ3aHQKDF5QQ1MtUGhvdG9zJBIZAAQiFQIEdndodAoNXlBDUy1TaGFyaW5nJBIeAAQiGgIEdndodAoSXlBDUy1pQ2xvdWRCYWNrdXAkEh0ABCIZAgR2d2h0ChFeUENTLWlDbG91ZERyaXZlJBIaAAQiFgIEdndodAoOXlBDUy1pTWVzc2FnZSQSFVByb3RlY3RlZENsb3VkU3RvcmFnZTI6CisABCInAgRhZ3JwCh9eY29tLmFwcGxlLnNhZmFyaS5jcmVkaXQtY2FyZHMkEgtDcmVkaXRDYXJkczIuCiEABCIdAgRhZ3JwChVeY29tLmFwcGxlLmNmbmV0d29yayQSCVBhc3N3b3JkczJtClwAAhIeAAQiGgIEdndodAoSXkFjY2Vzc29yeVBhaXJpbmckEhoABCIWAgR2d2h0Cg5eTmFub1JlZ2lzdHJ5JBIcAAQiGAIEdndodAoQXldhdGNoTWlncmF0aW9uJBINRGV2aWNlUGFpcmluZzIOCgIABhIIQmFja3N0b3A=",
plaintextPolicy: try! TPPolicyDocument(version: 5,
modelToCategory: [
],
hashAlgo: .SHA256)
),
+
+ RawPolicy(version: TPPolicyVersion(version: 6, hash: "SHA256:L2Px1aYyR1tgChe8dIyTBSmCHCWEFJirZ3ELMFXz2PY="),
+ policyData: "CAYSDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSDAoEaVBvZBIEZnVsbBILCgNNYWMSBGZ1bGwSDAoEaU1hYxIEZnVsbBINCgdBcHBsZVRWEgJ0dhIOCgVXYXRjaBIFd2F0Y2gSFwoOQXVkaW9BY2Nlc3NvcnkSBWF1ZGlvGhoKC0NyZWRpdENhcmRzEgRmdWxsEgV3YXRjaBofChBTZWN1cmVPYmplY3RTeW5jEgRmdWxsEgV3YXRjaBoVCgZIZWFsdGgSBGZ1bGwSBXdhdGNoGhkKCkF1dG9VbmxvY2sSBGZ1bGwSBXdhdGNoGh4KBFdpRmkSBGZ1bGwSBXdhdGNoEgJ0dhIFYXVkaW8aHgoESG9tZRIEZnVsbBIFd2F0Y2gSAnR2EgVhdWRpbxokChVQcm90ZWN0ZWRDbG91ZFN0b3JhZ2USBGZ1bGwSBXdhdGNoGi0KE0xpbWl0ZWRQZWVyc0FsbG93ZWQSBGZ1bGwSBXdhdGNoEgJ0dhIFYXVkaW8aGAoJUGFzc3dvcmRzEgRmdWxsEgV3YXRjaBobCgxBcHBsaWNhdGlvbnMSBGZ1bGwSBXdhdGNoGhwKDURldmljZVBhaXJpbmcSBGZ1bGwSBXdhdGNoGhcKCEFwcGxlUGF5EgRmdWxsEgV3YXRjaBoWCgdNYW5hdGVlEgRmdWxsEgV3YXRjaBoVCgZFbmdyYW0SBGZ1bGwSBXdhdGNoIhsKBWF1ZGlvEgRmdWxsEgV3YXRjaBIFYXVkaW8iFQoCdHYSBGZ1bGwSBXdhdGNoEgJ0diIUCgV3YXRjaBIEZnVsbBIFd2F0Y2giEwoEZnVsbBIEZnVsbBIFd2F0Y2gyIgoWAAQiEgIEdndodAoKXkFwcGxlUGF5JBIIQXBwbGVQYXkyJgoYAAQiFAIEdndodAoMXkF1dG9VbmxvY2skEgpBdXRvVW5sb2NrMh4KFAAEIhACBHZ3aHQKCF5FbmdyYW0kEgZFbmdyYW0yHgoUAAQiEAIEdndodAoIXkhlYWx0aCQSBkhlYWx0aDIaChIABCIOAgR2d2h0CgZeSG9tZSQSBEhvbWUyIAoVAAQiEQIEdndodAoJXk1hbmF0ZWUkEgdNYW5hdGVlMjgKIQAEIh0CBHZ3aHQKFV5MaW1pdGVkUGVlcnNBbGxvd2VkJBITTGltaXRlZFBlZXJzQWxsb3dlZDJdClAAAhIeAAQiGgIEdndodAoSXkNvbnRpbnVpdHlVbmxvY2skEhUABCIRAgR2d2h0CgleSG9tZUtpdCQSFQAEIhECBHZ3aHQKCV5BcHBsZVRWJBIJTm90U3luY2VkMisKGwAEIhcCBGFncnAKD15bMC05QS1aXXsxMH1cLhIMQXBwbGljYXRpb25zMsoBCrUBAAISNgABChMABCIPAgVjbGFzcwoGXmdlbnAkCh0ABCIZAgRhZ3JwChFeY29tXC5hcHBsZVwuc2JkJBJAAAEKEwAEIg8CBWNsYXNzCgZea2V5cyQKJwAEIiMCBGFncnAKG15jb21cLmFwcGxlXC5zZWN1cml0eVwuc29zJBIZAAQiFQIEdndodAoNXkJhY2t1cEJhZ1YwJBIcAAQiGAIEdndodAoQXmlDbG91ZElkZW50aXR5JBIQU2VjdXJlT2JqZWN0U3luYzJjClsAAhISAAQiDgIEdndodAoGXldpRmkkEkMAAQoTAAQiDwIFY2xhc3MKBl5nZW5wJAoTAAQiDwIEYWdycAoHXmFwcGxlJAoVAAQiEQIEc3ZjZQoJXkFpclBvcnQkEgRXaUZpMp0DCoMDAAISGAAEIhQCBHZ3aHQKDF5QQ1MtQmFja3VwJBIaAAQiFgIEdndodAoOXlBDUy1DbG91ZEtpdCQSGAAEIhQCBHZ3aHQKDF5QQ1MtRXNjcm93JBIVAAQiEQIEdndodAoJXlBDUy1GREUkEhoABCIWAgR2d2h0Cg5eUENTLUZlbGRzcGFyJBIaAAQiFgIEdndodAoOXlBDUy1NYWlsRHJvcCQSGgAEIhYCBHZ3aHQKDl5QQ1MtTWFpbGRyb3AkEhsABCIXAgR2d2h0Cg9eUENTLU1hc3RlcktleSQSFwAEIhMCBHZ3aHQKC15QQ1MtTm90ZXMkEhgABCIUAgR2d2h0CgxeUENTLVBob3RvcyQSGQAEIhUCBHZ3aHQKDV5QQ1MtU2hhcmluZyQSHgAEIhoCBHZ3aHQKEl5QQ1MtaUNsb3VkQmFja3VwJBIdAAQiGQIEdndodAoRXlBDUy1pQ2xvdWREcml2ZSQSGgAEIhYCBHZ3aHQKDl5QQ1MtaU1lc3NhZ2UkEhVQcm90ZWN0ZWRDbG91ZFN0b3JhZ2UyPQouAAQiKgIEYWdycAoiXmNvbVwuYXBwbGVcLnNhZmFyaVwuY3JlZGl0LWNhcmRzJBILQ3JlZGl0Q2FyZHMyMAojAAQiHwIEYWdycAoXXmNvbVwuYXBwbGVcLmNmbmV0d29yayQSCVBhc3N3b3JkczJtClwAAhIeAAQiGgIEdndodAoSXkFjY2Vzc29yeVBhaXJpbmckEhoABCIWAgR2d2h0Cg5eTmFub1JlZ2lzdHJ5JBIcAAQiGAIEdndodAoQXldhdGNoTWlncmF0aW9uJBINRGV2aWNlUGFpcmluZzIOCgIABhIIQmFja3N0b3A=",
+ plaintextPolicy: try! TPPolicyDocument(version: 6,
+ modelToCategory: [
+ ["prefix": "iPhone", "category": "full"],
+ ["prefix": "iPad", "category": "full"],
+ ["prefix": "iPod", "category": "full"],
+ ["prefix": "Mac", "category": "full"],
+ ["prefix": "iMac", "category": "full"],
+ ["prefix": "AppleTV", "category": "tv"],
+ ["prefix": "Watch", "category": "watch"],
+ ["prefix": "AudioAccessory", "category": "audio"],
+ ],
+ categoriesByView: [
+ "AutoUnlock": ["full", "watch"],
+ "ApplePay": ["full", "watch"],
+ "Engram": ["full", "watch"],
+ "Health": ["full", "watch"],
+ "Home": ["full", "watch", "tv", "audio"],
+ "LimitedPeersAllowed": ["full", "watch", "tv", "audio"],
+ "Manatee": ["full", "watch"],
+ "Applications": ["full", "watch"],
+ "SecureObjectSync": ["full", "watch"],
+ "WiFi": ["full", "watch", "tv", "audio"],
+ "ProtectedCloudStorage": ["full", "watch"],
+ "CreditCards": ["full", "watch"],
+ "Passwords": ["full", "watch"],
+ "DevicePairing": ["full", "watch"],
+ ],
+ introducersByCategory: [
+ "full": ["full", "watch"],
+ "watch": ["full", "watch"],
+ "tv": ["full", "watch", "tv"],
+ "audio": ["full", "watch", "audio"],
+ ],
+ redactions: [:],
+ keyViewMapping: [
+ TPPBPolicyKeyViewMapping(view: "ApplePay", matchingRule: TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^ApplePay$")),
+ TPPBPolicyKeyViewMapping(view: "AutoUnlock", matchingRule: TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^AutoUnlock$")),
+ TPPBPolicyKeyViewMapping(view: "Engram", matchingRule: TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^Engram$")),
+ TPPBPolicyKeyViewMapping(view: "Health", matchingRule: TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^Health$")),
+ TPPBPolicyKeyViewMapping(view: "Home", matchingRule: TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^Home$")),
+ TPPBPolicyKeyViewMapping(view: "Manatee", matchingRule: TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^Manatee$")),
+ TPPBPolicyKeyViewMapping(view: "LimitedPeersAllowed", matchingRule: TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^LimitedPeersAllowed$")),
+
+ // These items will not be synced by Octagon
+ TPPBPolicyKeyViewMapping(view: "NotSynced", matchingRule:
+ TPDictionaryMatchingRule.orMatch([
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^ContinuityUnlock$"),
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^HomeKit$"),
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^AppleTV$"),
+ ])),
+
+ TPPBPolicyKeyViewMapping(view: "Applications", matchingRule:
+ TPDictionaryMatchingRule.fieldMatch("agrp", fieldRegex: "^[0-9A-Z]{10}\\.")),
+
+ TPPBPolicyKeyViewMapping(view: "SecureObjectSync", matchingRule:
+ TPDictionaryMatchingRule.orMatch([
+ TPDictionaryMatchingRule.andMatch([
+ TPDictionaryMatchingRule.fieldMatch("class", fieldRegex: "^genp$"),
+ TPDictionaryMatchingRule.fieldMatch("agrp", fieldRegex: "^com\\.apple\\.sbd$"),
+ ]),
+ TPDictionaryMatchingRule.andMatch([
+ TPDictionaryMatchingRule.fieldMatch("class", fieldRegex: "^keys$"),
+ TPDictionaryMatchingRule.fieldMatch("agrp", fieldRegex: "^com\\.apple\\.security\\.sos$"),
+ ]),
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^BackupBagV0$"),
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^iCloudIdentity$"),
+ ])),
+
+ TPPBPolicyKeyViewMapping(view: "WiFi", matchingRule:
+ TPDictionaryMatchingRule.orMatch([
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^WiFi$"),
+ TPDictionaryMatchingRule.andMatch([
+ TPDictionaryMatchingRule.fieldMatch("class", fieldRegex: "^genp$"),
+ TPDictionaryMatchingRule.fieldMatch("agrp", fieldRegex: "^apple$"),
+ TPDictionaryMatchingRule.fieldMatch("svce", fieldRegex: "^AirPort$"),
+ ]),
+ ])),
+
+ TPPBPolicyKeyViewMapping(view: "ProtectedCloudStorage", matchingRule:
+ TPDictionaryMatchingRule.orMatch([
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-Backup$"),
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-CloudKit$"),
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-Escrow$"),
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-FDE$"),
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-Feldspar$"),
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-MailDrop$"),
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-Maildrop$"),
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-MasterKey$"),
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-Notes$"),
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-Photos$"),
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-Sharing$"),
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-iCloudBackup$"),
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-iCloudDrive$"),
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-iMessage$"),
+ ])),
+
+ TPPBPolicyKeyViewMapping(view: "CreditCards",
+ matchingRule: TPDictionaryMatchingRule.fieldMatch("agrp", fieldRegex: "^com\\.apple\\.safari\\.credit-cards$")),
+
+ TPPBPolicyKeyViewMapping(view: "Passwords",
+ matchingRule: TPDictionaryMatchingRule.fieldMatch("agrp", fieldRegex: "^com\\.apple\\.cfnetwork$")),
+
+ TPPBPolicyKeyViewMapping(view: "DevicePairing", matchingRule:
+ TPDictionaryMatchingRule.orMatch([
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^AccessoryPairing$"),
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^NanoRegistry$"),
+ TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^WatchMigration$"),
+ ])),
+
+ TPPBPolicyKeyViewMapping(view: "Backstop", matchingRule:
+ TPDictionaryMatchingRule.trueMatch()),
+ ],
+ hashAlgo: .SHA256)
+ ),
]
+ // swiftlint:enable force_try
- assert(rawPolicies.filter { prevailingPolicyVersion == $0.policyVersion }.count == 1)
+ assert(rawPolicies.filter { prevailingPolicyVersion.versionNumber == $0.version.versionNumber }.count == 1)
return rawPolicies.map { raw in
let data = Data(base64Encoded: raw.policyData)!
- let doc = TPPolicyDocument.policyDoc(withHash: raw.policyHash, data: data)!
- assert(doc.policyVersion == raw.policyVersion)
- if raw.policyVersion == prevailingPolicyVersion {
- assert(prevailingPolicyHash == raw.policyHash)
+ let doc = TPPolicyDocument.policyDoc(withHash: raw.version.policyHash, data: data)!
+
+ if(!doc.isEqual(to: raw.plaintextPolicy)) {
+ let bodyData = raw.plaintextPolicy.protobuf
+ let bodyBase64 = bodyData.base64EncodedString()
+ let hash = TPHashBuilder.hash(with: .SHA256, of: bodyData)
+ os_log("raw policy doesn't match encoded bytes, new hash would be: %{public}@ new data: %{public}@", log: tplogDebug, hash, bodyBase64)
+ }
+
+ assert(doc.version.versionNumber == raw.version.versionNumber)
+ if raw.version.versionNumber == prevailingPolicyVersion.versionNumber {
+ assert(prevailingPolicyVersion.policyHash == raw.version.policyHash)
}
assert(doc.isEqual(to: raw.plaintextPolicy))
return doc
let OT_RECOVERY_SIGNING_HKDF_SIZE = 56
let OT_RECOVERY_ENCRYPTION_HKDF_SIZE = 56
-enum recoveryKeyType: Int {
+enum RecoveryKeyType: Int {
case kOTRecoveryKeySigning = 1
case kOTRecoveryKeyEncryption = 2
}
self.secret = secret
self.recoverySalt = recoverySalt
- let encryptionKeyData = try RecoveryKeySet.generateRecoveryKey(keyType: recoveryKeyType.kOTRecoveryKeyEncryption, masterSecret: secret, recoverySalt: recoverySalt)
+ let encryptionKeyData = try RecoveryKeySet.generateRecoveryKey(keyType: RecoveryKeyType.kOTRecoveryKeyEncryption, masterSecret: secret, recoverySalt: recoverySalt)
self.encryptionKey = _SFECKeyPair.init(secKey: try RecoveryKeySet.createSecKey(keyData: encryptionKeyData))
- let signingKeyData = try RecoveryKeySet.generateRecoveryKey(keyType: recoveryKeyType.kOTRecoveryKeySigning, masterSecret: secret, recoverySalt: recoverySalt)
+ let signingKeyData = try RecoveryKeySet.generateRecoveryKey(keyType: RecoveryKeyType.kOTRecoveryKeySigning, masterSecret: secret, recoverySalt: recoverySalt)
self.signingKey = _SFECKeyPair.init(secKey: try RecoveryKeySet.createSecKey(keyData: signingKeyData))
let RecoverySigningPubKeyHash = try RecoveryKeySet.hashRecoveryedSigningPublicKey(keyData: self.signingKey.publicKey().spki())
return SecRKCreateRecoveryKeyString(nil) as String
}
- class func generateRecoveryKey(keyType: recoveryKeyType, masterSecret: Data, recoverySalt: String) throws -> (Data) {
+ class func generateRecoveryKey(keyType: RecoveryKeyType, masterSecret: Data, recoverySalt: String) throws -> (Data) {
var keyLength: Int
var info: Data
var derivedKey: Data
var finalKey = Data()
switch keyType {
- case recoveryKeyType.kOTRecoveryKeyEncryption:
+ case RecoveryKeyType.kOTRecoveryKeyEncryption:
keyLength = OT_RECOVERY_ENCRYPTION_HKDF_SIZE
let infoString = Array("Recovery Encryption Private Key".utf8)
info = Data(bytes: infoString, count: infoString.count)
- break
- case recoveryKeyType.kOTRecoveryKeySigning:
+ case RecoveryKeyType.kOTRecoveryKeySigning:
keyLength = OT_RECOVERY_SIGNING_HKDF_SIZE
let infoString = Array("Recovery Signing Private Key".utf8)
info = Data(bytes: infoString, count: infoString.count)
- break
}
guard let cp = ccec_cp_384() else {
throw RecoveryKeySetError.corecryptoKeyGeneration(corecryptoError: status)
}
- if(keyType == recoveryKeyType.kOTRecoveryKeyEncryption || keyType == recoveryKeyType.kOTRecoveryKeySigning) {
+ if keyType == RecoveryKeyType.kOTRecoveryKeyEncryption || keyType == RecoveryKeyType.kOTRecoveryKeySigning {
status = ccec_generate_key_deterministic(cp,
derivedKeyBytes.count, derivedKeyBytes.bindMemory(to: UInt8.self).baseAddress!,
ccDRBGGetRngState(),
return key
}
- class func setKeyMaterialInKeychain(query: Dictionary<CFString, Any>) throws -> (Bool) {
+ class func setKeyMaterialInKeychain(query: [CFString: Any]) throws -> (Bool) {
var result = false
var results: CFTypeRef?
if status == errSecSuccess {
result = true
} else if status == errSecDuplicateItem {
- var updateQuery: Dictionary<CFString, Any> = query
+ var updateQuery: [CFString: Any] = query
updateQuery[kSecClass] = nil
status = SecItemUpdate(query as CFDictionary, updateQuery as CFDictionary)
return try RecoveryKeySet.setKeyMaterialInKeychain(query: query)
}
- class func retrieveRecoveryKeysFromKeychain(label: String) throws -> [Dictionary <CFString, Any>]? {
- var keySet: [Dictionary<CFString, Any>]?
+ class func retrieveRecoveryKeysFromKeychain(label: String) throws -> [ [CFString: Any]]? {
+ var keySet: [[CFString: Any]]?
let query: [CFString: Any] = [
kSecClass: kSecClassKey,
}
if result != nil {
- if let dictionaryArray = result as? [Dictionary<CFString, Any>] {
+ if let dictionaryArray = result as? [[CFString: Any]] {
keySet = dictionaryArray
} else {
- if let dictionary = result as? Dictionary<CFString, Any> {
+ if let dictionary = result as? [CFString: Any] {
keySet = [dictionary]
} else {
keySet = nil
override func transformedValue(_ value: Any?) -> Any? {
do {
- guard let value = value else { return nil }
+ guard let value = value else {
+ return nil
+ }
return try NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true)
} catch {
os_log("Failed to serialize a Set: %@", log: tplogDebug, type: .default, error as CVarArg)
override func reverseTransformedValue(_ value: Any?) -> Any? {
do {
- guard let dataOp = value as? Data? else { return nil }
- guard let data = dataOp else { return nil }
+ guard let dataOp = value as? Data? else {
+ return nil
+ }
+ guard let data = dataOp else {
+ return nil
+ }
let unarchiver = try NSKeyedUnarchiver(forReadingFrom: data)
return unarchiver.decodeObject(of: [NSSet.self], forKey: NSKeyedArchiveRootObjectKey)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14886.2" systemVersion="19A541" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
+<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17026" systemVersion="19E219" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="Bottle" representedClassName="BottleMO" syncable="YES" codeGenerationType="class">
<attribute name="bottleID" optional="YES" attributeType="String"/>
<attribute name="contents" optional="YES" attributeType="Binary"/>
<attribute name="egoPeerPermanentInfoSig" optional="YES" attributeType="Binary"/>
<attribute name="egoPeerStableInfo" optional="YES" attributeType="Binary"/>
<attribute name="egoPeerStableInfoSig" optional="YES" attributeType="Binary"/>
+ <attribute name="honorIDMSListChanges" optional="YES" attributeType="String" defaultValueString="UNKNOWN"/>
<attribute name="moreChanges" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="name" optional="YES" attributeType="String"/>
+ <attribute name="recoveryKeyEncryptionSPKI" optional="YES" attributeType="Binary"/>
+ <attribute name="recoveryKeySigningSPKI" optional="YES" attributeType="Binary"/>
<relationship name="bottles" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Bottle" inverseName="container" inverseEntity="Bottle"/>
<relationship name="machines" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Machine" inverseName="container" inverseEntity="Machine"/>
<relationship name="peers" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="Peer" inverseName="container" inverseEntity="Peer"/>
</entity>
<elements>
<element name="Bottle" positionX="-549" positionY="-234" width="128" height="150"/>
- <element name="Container" positionX="-758" positionY="-261" width="128" height="253"/>
+ <element name="Container" positionX="-758" positionY="-261" width="128" height="268"/>
<element name="Machine" positionX="-549" positionY="-234" width="128" height="133"/>
<element name="Peer" positionX="-414" positionY="-387" width="128" height="210"/>
<element name="Policy" positionX="-414" positionY="54" width="128" height="105"/>
<element name="RecoveryVoucher" positionX="-65" positionY="-297" width="128" height="105"/>
<element name="Voucher" positionX="-65" positionY="-171" width="128" height="90"/>
</elements>
-</model>
\ No newline at end of file
+</model>
- (void)setAllowedMachineIDsWithContainer:(NSString *)container
context:(NSString *)context
allowedMachineIDs:(NSSet<NSString*> *)allowedMachineIDs
+ honorIDMSListChanges:(BOOL)honorIDMSListChanges
reply:(void (^)(BOOL listDifferences, NSError * _Nullable error))reply;
- (void)addAllowedMachineIDsWithContainer:(NSString *)container
deviceName:(nullable NSString*)deviceName
serialNumber:(NSString *)serialNumber
osVersion:(NSString *)osVersion
- policyVersion:(nullable NSNumber *)policyVersion
+ policyVersion:(nullable TPPolicyVersion *)policyVersion
policySecrets:(nullable NSDictionary<NSString*,NSData*> *)policySecrets
signingPrivKeyPersistentRef:(nullable NSData *)spkPr
encPrivKeyPersistentRef:(nullable NSData*)epkPr
NSData * _Nullable permanentInfoSig,
NSData * _Nullable stableInfo,
NSData * _Nullable stableInfoSig,
+ NSSet<NSString*>* _Nullable syncingViewList,
+ TPPolicy* _Nullable syncingPolicy,
NSError * _Nullable error))reply;
// If there already are existing CKKSViews, please pass in their key sets anyway.
NSData * _Nullable voucherSig,
NSError * _Nullable error))reply;
-// Preflighting a vouch will return the peer ID associated with the bottle you will be recovering.
+// Preflighting a vouch will return the peer ID associated with the bottle you will be recovering, as well as
+// the syncing policy used by that peer, and,
// You can then use that peer ID to filter the tlkshares provided to vouchWithBottle.
- (void)preflightVouchWithBottleWithContainer:(NSString *)container
context:(NSString *)context
bottleID:(NSString*)bottleID
reply:(void (^)(NSString* _Nullable peerID,
+ NSSet<NSString*>* _Nullable peerSyncingViewList,
+ TPPolicy * _Nullable peerSyncingPolicy,
NSError * _Nullable error))reply;
// Returns a voucher for our own identity, created by the identity inside this bottle
tlkShares:(NSArray<CKKSTLKShare*> *)tlkShares
reply:(void (^)(NSData * _Nullable voucher,
NSData * _Nullable voucherSig,
+ int64_t uniqueTLKsRecovered,
+ int64_t totalTLKSharesRecovered,
NSError * _Nullable error))reply;
+// Preflighting a vouch will return the RK ID, view list and policy associated with the RK you will be recovering.
+// You can then use that peer ID to filter the tlkshares provided to vouchWithRecoveryKey.
+- (void)preflightVouchWithRecoveryKeyWithContainer:(NSString*)container
+ context:(NSString*)context
+ recoveryKey:(NSString*)recoveryKey
+ salt:(NSString*)salt
+ reply:(void (^)(NSString* _Nullable recoveryKeyID,
+ NSSet<NSString*>* _Nullable peerSyncingViewList,
+ TPPolicy * _Nullable peerSyncingPolicy,
+ NSError * _Nullable error))reply;
+
// Returns a voucher for our own identity, using recovery key
- (void)vouchWithRecoveryKeyWithContainer:(NSString *)container
context:(NSString *)context
preapprovedKeys:(NSArray<NSData*> *)preapprovedKeys
reply:(void (^)(NSString * _Nullable peerID,
NSArray<CKRecord*>* _Nullable keyHierarchyRecords,
+ NSSet<NSString*>* _Nullable syncingViewList,
+ TPPolicy* _Nullable syncingPolicy,
NSError * _Nullable error))reply;
// Preflighting a preapproved join suggests whether or not you expect to succeed in an immediate preapprovedJoin() call
preapprovedKeys:(NSArray<NSData*> *)preapprovedKeys
reply:(void (^)(NSString * _Nullable peerID,
NSArray<CKRecord*>* _Nullable keyHierarchyRecords,
+ NSSet<NSString*>* _Nullable syncingViewList,
+ TPPolicy* _Nullable syncingPolicy,
NSError * _Nullable error))reply;
// TODO: if the new policy causes someone to lose access to a view, how should this API work?
- (void)setPreapprovedKeysWithContainer:(NSString *)container
context:(NSString *)context
preapprovedKeys:(NSArray<NSData*> *)preapprovedKeys
- reply:(void (^)(NSError * _Nullable error))reply;
+ reply:(void (^)(TrustedPeersHelperPeerState* _Nullable peerState, NSError * _Nullable error))reply;
/* Rather thin pass-through for uploading new TLKs (for zones which may have disappeared) */
- (void)updateTLKsWithContainer:(NSString *)container
NSData* _Nullable signingPublicKey,
NSError* _Nullable error))reply;
-// The argument contains N [version:hash] keys,
-// the reply block contains 0<=N [version:[hash, data]] entries.
- (void)fetchPolicyDocumentsWithContainer:(NSString*)container
context:(NSString*)context
- keys:(NSDictionary<NSNumber*,NSString*>*)keys
- reply:(void (^)(NSDictionary<NSNumber*,NSArray<NSString*>*>* _Nullable entries,
+ versions:(NSSet<TPPolicyVersion*>*)versions
+ reply:(void (^)(NSDictionary<TPPolicyVersion*, NSData*>* _Nullable entries,
NSError * _Nullable error))reply;
-// Fetch the policy for current peer.
-- (void)fetchPolicyWithContainer:(NSString*)container
- context:(NSString*)context
- reply:(void (^)(TPPolicy * _Nullable policy,
- NSError * _Nullable error))reply;
+// Fetch the policy and view list for current peer.
+- (void)fetchCurrentPolicyWithContainer:(NSString*)container
+ context:(NSString*)context
+ reply:(void (^)(NSSet<NSString*>* _Nullable syncingViewList,
+ TPPolicy * _Nullable syncingPolicy,
+ NSError * _Nullable error))reply;
- (void)validatePeersWithContainer:(NSString *)container
context:(NSString *)context
context:(NSString *)context
reply:(void (^)(NSError* _Nullable error))reply;
-- (void)getViewsWithContainer:(NSString *)container
- context:(NSString *)context
- inViews:(NSArray<NSString*>*)inViews
- reply:(void (^)(NSArray<NSString*>* _Nullable, NSError* _Nullable))reply;
-
- (void)requestHealthCheckWithContainer:(NSString *)container
context:(NSString *)context
requiresEscrowCheck:(BOOL)requiresEscrowCheck
- reply:(void (^)(BOOL postRepairCFU, BOOL postEscrowCFU, BOOL resetOctagon, NSError* _Nullable))reply;
+ reply:(void (^)(BOOL postRepairCFU, BOOL postEscrowCFU, BOOL resetOctagon, BOOL leaveTrust, NSError* _Nullable))reply;
- (void)getSupportAppInfoWithContainer:(NSString *)container
context:(NSString *)context
tlkShares:
reply:) argumentIndex:4 ofReply:NO];
- [interface setClasses:[NSSet setWithObject:[TPPolicy class]] forSelector:@selector(fetchPolicyWithContainer:
+ [interface setClasses:[NSSet setWithObject:[TPPolicy class]] forSelector:@selector(fetchCurrentPolicyWithContainer:
context:
- reply:) argumentIndex:0 ofReply:YES];
+ reply:) argumentIndex:1 ofReply:YES];
[interface setClasses:trustedPeersHelperPeerState forSelector:@selector(updateWithContainer:
context:
func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
let tphEntitlement = "com.apple.private.trustedpeershelper.client"
- os_log("Received a new client: %@", log: tplogDebug, type: .default, newConnection)
+ os_log("Received a new client: %{public}@", log: tplogDebug, type: .default, newConnection)
switch newConnection.value(forEntitlement: tphEntitlement) {
case 1 as Int:
- os_log("client has entitlement '%@'", log: tplogDebug, type: .default, tphEntitlement)
+ os_log("client has entitlement '%{public}@'", log: tplogDebug, type: .default, tphEntitlement)
case true as Bool:
- os_log("client has entitlement '%@'", log: tplogDebug, type: .default, tphEntitlement)
+ os_log("client has entitlement '%{public}@'", log: tplogDebug, type: .default, tphEntitlement)
case let someInt as Int:
- os_log("client(%@) has wrong integer value for '%@' (%d), rejecting", log: tplogDebug, type: .default, newConnection, tphEntitlement, someInt)
+ os_log("client(%{public}@) has wrong integer value for '%{public}@' (%d), rejecting", log: tplogDebug, type: .default, newConnection, tphEntitlement, someInt)
return false
case let someBool as Bool:
- os_log("client(%@) has wrong boolean value for '%@' (%d), rejecting", log: tplogDebug, type: .default, newConnection, tphEntitlement, someBool)
+ os_log("client(%{public}@) has wrong boolean value for '%{public}@' (%d), rejecting", log: tplogDebug, type: .default, newConnection, tphEntitlement, someBool)
return false
default:
- os_log("client(%@) is missing entitlement '%@', rejecting", log: tplogDebug, type: .default, newConnection, tphEntitlement)
+ os_log("client(%{public}@) is missing entitlement '%{public}@', rejecting", log: tplogDebug, type: .default, newConnection, tphEntitlement)
return false
}
--- /dev/null
+disabled_rules:
+ - force_cast
+ - force_try
deviceName: String = "test device name",
serialNumber: String = "456",
osVersion: String = "123",
- policyVersion: UInt64? = nil,
+ policyVersion: TPPolicyVersion? = nil,
policySecrets: [String: Data]? = nil,
signingPrivateKeyPersistentRef: Data? = nil,
encryptionPrivateKeyPersistentRef: Data? = nil
- ) -> (String?, Data?, Data?, Data?, Data?, Error?) {
+ ) -> (String?, Data?, Data?, Data?, Data?, Set<String>?, TPPolicy?, Error?) {
let expectation = XCTestExpectation(description: "prepare replied")
var reta: String?, retb: Data?, retc: Data?, retd: Data?, rete: Data?, reterr: Error?
+ var retviews: Set<String>?
+ var retpolicy: TPPolicy?
self.prepare(epoch: epoch,
machineID: machineID,
bottleSalt: bottleSalt,
policySecrets: policySecrets,
signingPrivateKeyPersistentRef: signingPrivateKeyPersistentRef,
encryptionPrivateKeyPersistentRef: encryptionPrivateKeyPersistentRef
- ) { a, b, c, d, e, err in
+ ) { a, b, c, d, e, f, g, err in
reta = a
retb = b
retc = c
retd = d
rete = e
+ retviews = f
+ retpolicy = g
reterr = err
expectation.fulfill()
}
test.wait(for: [expectation], timeout: 10.0)
- return (reta, retb, retc, retd, rete, reterr)
+ return (reta, retb, retc, retd, rete, retviews, retpolicy, reterr)
}
func establishSync(test: XCTestCase,
return (reta, retb, reterr)
}
- func preflightVouchWithBottleSync(test: XCTestCase, bottleID: String) -> (String?, Error?) {
+ func preflightVouchWithBottleSync(test: XCTestCase, bottleID: String) -> (String?, Set<String>?, TPPolicy?, Error?) {
let expectation = XCTestExpectation(description: "preflightVouchWithBottle replied")
var reta: String?, reterr: Error?
- self.preflightVouchWithBottle(bottleID: bottleID) { a, err in
+ var retviews: Set<String>?, retpolicy: TPPolicy?
+ self.preflightVouchWithBottle(bottleID: bottleID) { a, views, policy, err in
reta = a
+ retviews = views
+ retpolicy = policy
reterr = err
expectation.fulfill()
}
test.wait(for: [expectation], timeout: 10.0)
- return (reta, reterr)
+ return (reta, retviews, retpolicy, reterr)
}
- func vouchWithBottleSync(test: XCTestCase, b: String, entropy: Data, bottleSalt: String, tlkShares: [CKKSTLKShare]) -> (Data?, Data?, Error?) {
+ func vouchWithBottleSync(test: XCTestCase, b: String, entropy: Data, bottleSalt: String, tlkShares: [CKKSTLKShare]) -> (Data?, Data?, Int64, Int64, Error?) {
let expectation = XCTestExpectation(description: "vouchWithBottle replied")
- var reta: Data?, retb: Data?, reterr: Error?
- self.vouchWithBottle(bottleID: b, entropy: entropy, bottleSalt: bottleSalt, tlkShares: tlkShares) { a, b, err in
+ var reta: Data?, retb: Data?, retc: Int64 = 0, retd: Int64 = 0, reterr: Error?
+ self.vouchWithBottle(bottleID: b, entropy: entropy, bottleSalt: bottleSalt, tlkShares: tlkShares) { a, b, c, d, err in
reta = a
retb = b
+ retc = c
+ retd = d
reterr = err
expectation.fulfill()
}
test.wait(for: [expectation], timeout: 10.0)
- return (reta, retb, reterr)
+ return (reta, retb, retc, retd, reterr)
}
func joinSync(test: XCTestCase,
voucherSig: Data,
ckksKeys: [CKKSKeychainBackedKeySet],
tlkShares: [CKKSTLKShare],
- preapprovedKeys: [Data]? = nil) -> (String?, [CKRecord]?, Error?) {
+ preapprovedKeys: [Data]? = nil) -> (String?, [CKRecord]?, Set<String>?, TPPolicy?, Error?) {
let expectation = XCTestExpectation(description: "join replied")
var reta: String?, retkhr: [CKRecord]?, reterr: Error?
+ var retviews: Set<String>?, retpolicy: TPPolicy?
self.join(voucherData: voucherData,
voucherSig: voucherSig,
ckksKeys: ckksKeys,
tlkShares: tlkShares,
- preapprovedKeys: preapprovedKeys) { a, khr, err in
+ preapprovedKeys: preapprovedKeys) { a, khr, views, policy, err in
reta = a
retkhr = khr
+ retviews = views
+ retpolicy = policy
reterr = err
expectation.fulfill()
}
test.wait(for: [expectation], timeout: 10.0)
- return (reta, retkhr, reterr)
+ return (reta, retkhr, retviews, retpolicy, reterr)
}
func preapprovedJoinSync(test: XCTestCase,
ckksKeys: [CKKSKeychainBackedKeySet],
tlkShares: [CKKSTLKShare],
- preapprovedKeys: [Data]? = nil) -> (String?, [CKRecord]?, Error?) {
+ preapprovedKeys: [Data]? = nil) -> (String?, [CKRecord]?, Set<String>?, TPPolicy?, Error?) {
let expectation = XCTestExpectation(description: "preapprovedjoin replied")
var reta: String?
var retkhr: [CKRecord]?
+ var retviews: Set<String>?
+ var retpolicy: TPPolicy?
var reterr: Error?
self.preapprovedJoin(ckksKeys: ckksKeys,
tlkShares: tlkShares,
- preapprovedKeys: preapprovedKeys) { a, khr, err in
+ preapprovedKeys: preapprovedKeys) { a, khr, views, policy, err in
reta = a
retkhr = khr
+ retviews = views
+ retpolicy = policy
reterr = err
expectation.fulfill()
}
test.wait(for: [expectation], timeout: 10.0)
- return (reta, retkhr, reterr)
+ return (reta, retkhr, retviews, retpolicy, reterr)
}
func updateSync(test: XCTestCase,
return (retstate, reterr)
}
- func setAllowedMachineIDsSync(test: XCTestCase, allowedMachineIDs: Set<String>, listDifference: Bool = true) -> (Error?) {
+ func setAllowedMachineIDsSync(test: XCTestCase, allowedMachineIDs: Set<String>, accountIsDemo: Bool, listDifference: Bool = true) -> (Error?) {
let expectation = XCTestExpectation(description: "setAllowedMachineIDs replied")
var reterr: Error?
- self.setAllowedMachineIDs(allowedMachineIDs) { differences, err in
+ let honorIDMSListChanges = accountIsDemo ? false : true
+ self.setAllowedMachineIDs(allowedMachineIDs, honorIDMSListChanges: honorIDMSListChanges) { differences, err in
XCTAssertEqual(differences, listDifference, "Reported list difference should match expectation")
reterr = err
expectation.fulfill()
let expectation = XCTestExpectation(description: "fetchMIDList replied")
var retlist: Set<String>?
var reterr: Error?
- self.fetchAllowedMachineIDs() { list, err in
+ self.fetchAllowedMachineIDs { list, err in
retlist = list
reterr = err
expectation.fulfill()
}
func fetchPolicyDocumentsSync(test: XCTestCase,
- keys: [NSNumber: String]) -> ([NSNumber: [String]]?, Error?) {
+ versions: Set<TPPolicyVersion>) -> ([TPPolicyVersion: Data]?, Error?) {
let expectation = XCTestExpectation(description: "fetchPolicyDocuments replied")
- var reta: [NSNumber: [String]]?, reterr: Error?
- self.fetchPolicyDocuments(keys: keys) { a, err in
+ var reta: [TPPolicyVersion: Data]?, reterr: Error?
+ self.fetchPolicyDocuments(versions: versions) { a, err in
reta = a
reterr = err
expectation.fulfill()
return (retentropy, retbottleID, retspki, reterror)
}
- func requestHealthCheckSync(requiresEscrowCheck: Bool, test: XCTestCase) -> (Bool, Bool, Bool, Error?) {
+ func requestHealthCheckSync(requiresEscrowCheck: Bool, test: XCTestCase) -> (Bool, Bool, Bool, Bool, Error?) {
let expectation = XCTestExpectation(description: "requestHealthCheck replied")
var retrepairaccount: Bool = false
var retrepairescrow: Bool = false
var retresetoctagon: Bool = false
+ var retleavetrust: Bool = false
var reterror: Error?
- self.requestHealthCheck(requiresEscrowCheck: requiresEscrowCheck) { repairAccount, repairEscrow, resetOctagon, error in
+ self.requestHealthCheck(requiresEscrowCheck: requiresEscrowCheck) { repairAccount, repairEscrow, resetOctagon, leaveTrust, error in
retrepairaccount = repairAccount
retrepairescrow = repairEscrow
retresetoctagon = resetOctagon
+ retleavetrust = leaveTrust
reterror = error
expectation.fulfill()
}
test.wait(for: [expectation], timeout: 10.0)
- return (retrepairaccount, retrepairescrow, retresetoctagon, reterror)
+ return (retrepairaccount, retrepairescrow, retresetoctagon, retleavetrust, reterror)
}
}
}
}
-@objc class FakeCuttlefishNotify: NSObject {
+@objc
+class FakeCuttlefishNotify: NSObject {
let pushes: (Data) -> Void
let containerName: String
- @objc init(_ containerName: String, pushes: @escaping (Data) -> Void) {
+ @objc
+ init(_ containerName: String, pushes: @escaping (Data) -> Void) {
self.containerName = containerName
self.pushes = pushes
}
- @objc public func notify(_ function: String) throws {
- let notification: [String: Dictionary<String, Any>] = [
+ @objc
+ public func notify(_ function: String) throws {
+ let notification: [String: [String: Any]] = [
"aps": ["content-available": 1],
"cf": [
"f": function,
record[SecCKRecordWrappedKeyKey] = self.wrappedkeyBase64
- switch(self.keyclass) {
+ switch self.keyclass {
case .tlk:
record[SecCKRecordKeyClassKey] = "tlk"
case .classA:
abort()
}
- if self.parentkeyUuid.count > 0 {
+ if !self.parentkeyUuid.isEmpty {
// TODO: no idea how to tell it about the 'verify' action
record[SecCKRecordParentKeyRefKey] = CKRecord.Reference(recordID: CKRecord.ID(__recordName: self.parentkeyUuid, zoneID: zoneID), action: .none)
}
func fakeKeyPointer(zoneID: CKRecordZone.ID) -> CKRecord {
let recordName: String
- switch(self.keyclass) {
+ switch self.keyclass {
case .tlk:
recordName = "tlk"
case .classA:
var returnRepairAccountResponse: Bool = false
var returnRepairEscrowResponse: Bool = false
var returnResetOctagonResponse: Bool = false
+ var returnLeaveTrustResponse: Bool = false
var returnRepairErrorResponse: Error?
var fetchChangesCalledCount: Int = 0
var healthListener: ((GetRepairActionRequest) -> NSError?)?
var fetchViableBottlesListener: ((FetchViableBottlesRequest) -> NSError?)?
var resetListener: ((ResetRequest) -> NSError?)?
+ var setRecoveryKeyListener: ((SetRecoveryKeyRequest) -> NSError?)?
+
+ // Any policies in here will be returned by FetchPolicy before any inbuilt policies
+ var policyOverlay: [TPPolicyDocument] = []
var fetchViableBottlesDontReturnBottleWithID: String?
return Changes.with { changes in
changes.changeToken = self.currentChangeToken
- changes.differences = self.state.peersByID.compactMap({ (key: String, value: Peer) -> PeerDifference? in
+ changes.differences = self.state.peersByID.compactMap { (key: String, value: Peer) -> PeerDifference? in
let old = snapshot.peersByID[key]
if old == nil {
return PeerDifference.with {
} else {
return nil
}
- })
+ }
snapshot.peersByID.forEach { (key: String, _: Peer) in
if nil == self.state.peersByID[key] {
changes.differences.append(PeerDifference.with {
func setRecoveryKey(_ request: SetRecoveryKeyRequest, completion: @escaping (SetRecoveryKeyResponse?, Error?) -> Void) {
print("FakeCuttlefish: setRecoveryKey called")
+
+ if let listener = self.setRecoveryKeyListener {
+ let operationError = listener(request)
+ guard operationError == nil else {
+ completion(nil, operationError)
+ return
+ }
+ }
+
guard let snapshot = self.snapshotsByChangeToken[request.changeToken] else {
completion(nil, FakeCuttlefishError.unknownChangeToken)
return
var response = FetchPolicyDocumentsResponse()
let policies = builtInPolicyDocuments()
- let dummyPolicies = Dictionary(uniqueKeysWithValues: policies.map({ ($0.policyVersion, ($0.policyHash, $0.protobuf)) }))
+ let dummyPolicies = Dictionary(uniqueKeysWithValues: policies.map { ($0.version.versionNumber, ($0.version.policyHash, $0.protobuf)) })
+ let overlayPolicies = Dictionary(uniqueKeysWithValues: self.policyOverlay.map { ($0.version.versionNumber, ($0.version.policyHash, $0.protobuf)) })
+
for key in request.keys {
+ if let (hash, data) = overlayPolicies[key.version], hash == key.hash {
+ response.entries.append(PolicyDocumentMapEntry.with { $0.key = key; $0.value = data })
+ continue
+ }
+
guard let (hash, data) = dummyPolicies[key.version] else {
continue
}
$0.repairAction = .resetOctagon
}
completion(response, nil)
+ } else if returnLeaveTrustResponse {
+ let response = GetRepairActionResponse.with {
+ $0.repairAction = .leaveTrust
+ }
+ completion(response, nil)
} else if self.returnNoActionResponse {
let response = GetRepairActionResponse.with {
$0.repairAction = .noAction
}
}
+ func getClubCertificates(_: GetClubCertificatesRequest, completion: @escaping (GetClubCertificatesResponse?, Error?) -> Void) {
+ completion(GetClubCertificatesResponse(), nil)
+ }
+
func getSupportAppInfo(_: GetSupportAppInfoRequest, completion: @escaping (GetSupportAppInfoResponse?, Error?) -> Void) {
completion(GetSupportAppInfoResponse(), nil)
}
case updateTrust((UpdateTrustRequest, @escaping (UpdateTrustResponse?, Error?) -> Void) -> Void)
case setRecoveryKey((SetRecoveryKeyRequest, @escaping (SetRecoveryKeyResponse?, Error?) -> Void) -> Void)
case fetchChanges((FetchChangesRequest, @escaping (FetchChangesResponse?, Error?) -> Void) -> Void)
- case fetchViableBottles((FetchViableBottlesRequest, @escaping (FetchViableBottlesResponse?, Error?) -> Void) ->Void)
+ case fetchViableBottles((FetchViableBottlesRequest, @escaping (FetchViableBottlesResponse?, Error?) -> Void) -> Void)
case fetchPolicyDocuments((FetchPolicyDocumentsRequest,
@escaping (FetchPolicyDocumentsResponse?, Error?) -> Void) -> Void)
}
func getSupportAppInfo(_: GetSupportAppInfoRequest, completion: @escaping (GetSupportAppInfoResponse?, Error?) -> Void) {
completion(GetSupportAppInfoResponse(), nil)
}
-
+ func getClubCertificates(_: GetClubCertificatesRequest, completion: @escaping (GetClubCertificatesResponse?, Error?) -> Void) {
+ completion(GetClubCertificatesResponse(), nil)
+ }
}
func establish(reload: Bool,
store: NSPersistentStoreDescription) throws -> (Container, String) {
- return try self.establish(reload: reload, contextID: OTDefaultContext, store: store)
+ return try self.establish(reload: reload, contextID: OTDefaultContext, accountIsDemo: false, store: store)
}
func establish(reload: Bool,
contextID: String,
allowedMachineIDs: Set<String> = Set(["aaa", "bbb", "ccc"]),
+ accountIsDemo: Bool,
store: NSPersistentStoreDescription) throws -> (Container, String) {
var container = try Container(name: ContainerName(container: "test", context: contextID), persistentStoreDescription: store, cuttlefish: cuttlefish)
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: allowedMachineIDs, listDifference: allowedMachineIDs.count > 0), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: allowedMachineIDs, accountIsDemo: accountIsDemo, listDifference: !allowedMachineIDs.isEmpty), "should be able to set allowed machine IDs")
- let (peerID, permanentInfo, permanentInfoSig, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+ let (peerID, permanentInfo, permanentInfoSig, _, _, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = container.getStateSync(test: self)
XCTAssertFalse( state.bottles.filter { $0.peerID == peerID } .isEmpty, "should have a bottle for peer")
_ = container.dumpSync(test: self)
- if (reload) {
+ if reload {
do {
container = try Container(name: ContainerName(container: "test", context: contextID), persistentStoreDescription: store, cuttlefish: cuttlefish)
} catch {
- XCTFail()
+ XCTFail("Creating container errored: \(error)")
}
}
let description = tmpStoreDescription(name: "container.db")
let container = try Container(name: ContainerName(container: "test", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
- let (peerID, permanentInfo, permanentInfoSig, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+ let (peerID, permanentInfo, permanentInfoSig, _, _, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = container.getStateSync(test: self)
XCTAssertFalse( state.bottles.filter { $0.peerID == peerID } .isEmpty, "should have a bottle for peer")
// Note that an empty machine ID list means "all are allowed", so an establish now will succeed
// Now set up a machine ID list that positively does not have our peer
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa"]), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa"], accountIsDemo: false), "should be able to set allowed machine IDs")
let (peerID3, _, error3) = container.establishSync(test: self, ckksKeys: [], tlkShares: [], preapprovedKeys: [])
XCTAssertNotNil(peerID3, "Should get a peer when you establish a now allow-listed peer")
containerID: String,
machineID: String,
machineIDs: Set<String>,
+ accountIsDemo: Bool,
store: NSPersistentStoreDescription) throws -> (Container, String) {
let c = try Container(name: ContainerName(container: containerID, context: OTDefaultContext),
persistentStoreDescription: store,
cuttlefish: cuttlefish)
- XCTAssertNil(c.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, listDifference: machineIDs.count > 0), "Should be able to set machine IDs")
+ XCTAssertNil(c.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: accountIsDemo, listDifference: !machineIDs.isEmpty), "Should be able to set machine IDs")
print("preparing \(containerID)")
- let (peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error) =
+ let (peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error) =
c.prepareSync(test: self, epoch: 1, machineID: machineID, bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
XCTAssertNil(error)
XCTAssertNotNil(peerID)
assertTLKShareFor(peerID: peerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
print("\(containerID) joins")
- let (joinedPeerID, _, joinError) = c.joinSync(test: self,
- voucherData: voucherData!,
- voucherSig: voucherSig!,
- ckksKeys: [],
- tlkShares: [])
+ let (joinedPeerID, _, _, _, joinError) = c.joinSync(test: self,
+ voucherData: voucherData!,
+ voucherSig: voucherSig!,
+ ckksKeys: [],
+ tlkShares: [])
XCTAssertNil(joinError)
XCTAssertEqual(joinedPeerID, peerID!)
}
let containerC = try Container(name: ContainerName(container: "c", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
let machineIDs = Set(["aaa", "bbb", "ccc"])
- XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
- XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
- XCTAssertNil(containerC.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+ XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+ XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+ XCTAssertNil(containerC.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
print("preparing A")
- let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+ let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, aViewList, aPolicy, error) =
containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+ XCTAssertNotNil(aViewList, "Should have a view list coming back from a successful prepare")
+ XCTAssertNotNil(aPolicy, "Should have a policy coming back from a successful prepare")
+ XCTAssertEqual(aPolicy?.version, prevailingPolicyVersion, "Policy coming back from prepare() should be prevailing policy version")
+
do {
let state = containerA.getStateSync(test: self)
XCTAssertFalse( state.bottles.filter { $0.peerID == aPeerID } .isEmpty, "should have a bottle for peer")
}
print("preparing B")
- let (bPeerID, bPermanentInfo, bPermanentInfoSig, bStableInfo, bStableInfoSig, error2) =
+ let (bPeerID, bPermanentInfo, bPermanentInfoSig, bStableInfo, bStableInfoSig, _, _, error2) =
containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerB.getStateSync(test: self)
assertTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
print("B joins")
- let (peerID, _, error) = containerB.joinSync(test: self,
- voucherData: voucherData!,
- voucherSig: voucherSig!,
- ckksKeys: [],
- tlkShares: [])
+ let (peerID, _, _, _, error) = containerB.joinSync(test: self,
+ voucherData: voucherData!,
+ voucherSig: voucherSig!,
+ ckksKeys: [],
+ tlkShares: [])
XCTAssertNil(error)
XCTAssertEqual(peerID, bPeerID!)
}
_ = containerC.dumpSync(test: self)
print("preparing C")
- let (cPeerID, cPermanentInfo, cPermanentInfoSig, cStableInfo, cStableInfoSig, error4) =
+ let (cPeerID, cPermanentInfo, cPermanentInfoSig, cStableInfo, cStableInfoSig, _, _, error4) =
containerC.prepareSync(test: self, epoch: 1, machineID: "ccc", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerC.getStateSync(test: self)
assertTLKShareFor(peerID: cPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
print("C joins")
- let (peerID, _, error2) = containerC.joinSync(test: self,
- voucherData: voucherData!,
- voucherSig: voucherSig!,
- ckksKeys: [self.manateeKeySet, provisionalEngramKeySet],
- tlkShares: [])
+ let (peerID, _, _, _, error2) = containerC.joinSync(test: self,
+ voucherData: voucherData!,
+ voucherSig: voucherSig!,
+ ckksKeys: [self.manateeKeySet, provisionalEngramKeySet],
+ tlkShares: [])
XCTAssertNil(error2)
XCTAssertEqual(peerID, cPeerID!)
let containerA = try Container(name: ContainerName(container: "a", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
- let (peerID, permanentInfo, permanentInfoSig, _, _, error) = containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+ let (peerID, permanentInfo, permanentInfoSig, _, _, _, _, error) = containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerA.getStateSync(test: self)
XCTAssertFalse( state.bottles.filter { $0.peerID == peerID } .isEmpty, "should have a bottle for peer")
XCTAssertNotNil(permanentInfo, "Should have a permanent info after preparing A")
XCTAssertNotNil(permanentInfoSig, "Should have a signature after preparing A")
- XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa"]), "should be able to set allowed machine IDs")
+ XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa"], accountIsDemo: false), "should be able to set allowed machine IDs")
let (peerID2, _, error2) = containerA.establishSync(test: self, ckksKeys: [], tlkShares: [], preapprovedKeys: [])
XCTAssertNotNil(peerID2, "Should get a peer when you establish a now allow-listed peer")
XCTAssertNil(error2, "Should not get an error when you establish a now allow-listed peer")
print("preparing B")
- let (bPeerID, bPermanentInfo, bPermanentInfoSig, bStableInfo, bStableInfoSig, errorPrepareB) =
+ let (bPeerID, bPermanentInfo, bPermanentInfoSig, bStableInfo, bStableInfoSig, _, _, errorPrepareB) =
containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerA.getStateSync(test: self)
XCTAssertNotNil(bPermanentInfo, "Should have a permanent info after preparing B")
XCTAssertNotNil(bPermanentInfoSig, "Should have a signature after preparing B")
- XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa"]), "should be able to set allowed machine IDs on container B")
+ XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa"], accountIsDemo: false), "should be able to set allowed machine IDs on container B")
do {
print("A vouches for B")
XCTAssertNotNil(voucherSig, "Should have a signature from A")
print("B joins")
- let (peerID, _, error) = containerB.joinSync(test: self,
- voucherData: voucherData!,
- voucherSig: voucherSig!,
- ckksKeys: [],
- tlkShares: [])
+ let (peerID, _, _, _, error) = containerB.joinSync(test: self,
+ voucherData: voucherData!,
+ voucherSig: voucherSig!,
+ ckksKeys: [],
+ tlkShares: [])
XCTAssertNotNil(error, "Should have an error joining with an unapproved machine ID")
XCTAssertNil(peerID, "Should not receive a peer ID joining with an unapproved machine ID")
}
let containerA = try Container(name: ContainerName(container: "a", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
let machineIDs = Set(["aaa"])
- XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+ XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
print("preparing A")
- let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) = containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+ let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) = containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerA.getStateSync(test: self)
XCTAssertFalse( state.bottles.filter { $0.peerID == aPeerID } .isEmpty, "should have a bottle for peer")
let (dict, error) = containerA.dumpSync(test: self)
XCTAssertNil(error)
XCTAssertNotNil(dict)
- let peers: Array<Any> = dict!["peers"] as! Array<Any>
+ let peers: [Any] = dict!["peers"] as! [Any]
XCTAssertEqual(0, peers.count)
}
}
let containerA = try Container(name: ContainerName(container: "a", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
let machineIDs = Set(["aaa"])
- XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+ XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
- let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) = containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+ let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) = containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerA.getStateSync(test: self)
XCTAssertFalse( state.bottles.filter { $0.peerID == aPeerID } .isEmpty, "should have a bottle for peer")
let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
let containerC = try Container(name: ContainerName(container: "c", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
- XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: Set(["aaa", "bbb", "ccc"])))
- XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: Set(["aaa", "bbb", "ccc"])))
- XCTAssertNil(containerC.setAllowedMachineIDsSync(test: self, allowedMachineIDs: Set(["aaa", "bbb", "ccc"])))
+ XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), accountIsDemo: false))
+ XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), accountIsDemo: false))
+ XCTAssertNil(containerC.setAllowedMachineIDsSync(test: self, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), accountIsDemo: false))
print("preparing")
- let (peerID, _, _, _, _, _) = containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+ let (peerID, _, _, _, _, _, _, _) = containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerA.getStateSync(test: self)
XCTAssertFalse( state.bottles.filter { $0.peerID == peerID } .isEmpty, "should have a bottle for peer")
let secret = containerA.loadSecretSync(test: self, label: peerID!)
XCTAssertNotNil(secret, "secret should not be nil")
}
- let (bPeerID, bPermanentInfo, bPermanentInfoSig, bStableInfo, bStableInfoSig, _) = containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+ let (bPeerID, bPermanentInfo, bPermanentInfoSig, bStableInfo, bStableInfoSig, _, _, _) = containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerB.getStateSync(test: self)
XCTAssertFalse( state.bottles.filter { $0.peerID == bPeerID } .isEmpty, "should have a bottle for peer")
let secret = containerB.loadSecretSync(test: self, label: bPeerID!)
XCTAssertNotNil(secret, "secret should not be nil")
}
- let (cPeerID, cPermanentInfo, cPermanentInfoSig, cStableInfo, cStableInfoSig, _) = containerC.prepareSync(test: self, epoch: 1, machineID: "ccc", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+ let (cPeerID, cPermanentInfo, cPermanentInfoSig, cStableInfo, cStableInfoSig, _, _, _) = containerC.prepareSync(test: self, epoch: 1, machineID: "ccc", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerC.getStateSync(test: self)
XCTAssertFalse( state.bottles.filter { $0.peerID == cPeerID } .isEmpty, "should have a bottle for peer")
func testFetchPolicyDocuments() throws {
// 1 is known locally via builtin, 3 is not but is known to cuttlefish
- let policies =
- [
- 1: ("SHA256:TLXrcQmY4ue3oP5pCX1pwsi9BF8cKfohlJBilCroeBs=",
- "CAESDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSCwoDTWFjEgRmdWxsEgwKBGlNYWMSBGZ1bGwSDQoHQXBwbGVUVhICdHYS" +
- "DgoFV2F0Y2gSBXdhdGNoGhEKCVBDU0VzY3JvdxIEZnVsbBoXCgRXaUZpEgRmdWxsEgJ0dhIFd2F0Y2gaGQoRU2FmYXJpQ3JlZGl0" +
- "Q2FyZHMSBGZ1bGwiDAoEZnVsbBIEZnVsbCIUCgV3YXRjaBIEZnVsbBIFd2F0Y2giDgoCdHYSBGZ1bGwSAnR2"),
- 3: ("SHA256:JZzazSuHXrUhiOfSgElsg6vYKpnvvEPVpciR8FewRWg=",
- "CAMSDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSCwoDTWFjEgRmdWxsEgwKBGlNYWMSBGZ1bGwSDQoHQXBwbGVUVhICdHYSDgoFV2F0Y2gSBXdhdGNoEhcKDkF1ZGlvQWNjZXNzb3J5EgVhdWRpbxocCg1EZXZpY2VQYWlyaW5nEgRmdWxsEgV3YXRjaBoXCghBcHBsZVBheRIEZnVsbBIFd2F0Y2gaJAoVUHJvdGVjdGVkQ2xvdWRTdG9yYWdlEgRmdWxsEgV3YXRjaBoXCghCYWNrc3RvcBIEZnVsbBIFd2F0Y2gaGQoKQXV0b1VubG9jaxIEZnVsbBIFd2F0Y2gaHwoQU2VjdXJlT2JqZWN0U3luYxIEZnVsbBIFd2F0Y2gaIAoRU2FmYXJpQ3JlZGl0Q2FyZHMSBGZ1bGwSBXdhdGNoGhMKBEhvbWUSBGZ1bGwSBXdhdGNoGh4KD1NhZmFyaVBhc3N3b3JkcxIEZnVsbBIFd2F0Y2gaGwoMQXBwbGljYXRpb25zEgRmdWxsEgV3YXRjaBoVCgZFbmdyYW0SBGZ1bGwSBXdhdGNoGi0KE0xpbWl0ZWRQZWVyc0FsbG93ZWQSBGZ1bGwSBXdhdGNoEgJ0dhIFYXVkaW8aFgoHTWFuYXRlZRIEZnVsbBIFd2F0Y2gaHgoEV2lGaRIEZnVsbBIFd2F0Y2gSAnR2EgVhdWRpbxoVCgZIZWFsdGgSBGZ1bGwSBXdhdGNoIhMKBGZ1bGwSBGZ1bGwSBXdhdGNoIhsKBWF1ZGlvEgRmdWxsEgV3YXRjaBIFYXVkaW8iFAoFd2F0Y2gSBGZ1bGwSBXdhdGNoIhUKAnR2EgRmdWxsEgV3YXRjaBICdHYyIgoWAAQiEgIEdndodAoKXkFwcGxlUGF5JBIIQXBwbGVQYXkyJgoYAAQiFAIEdndodAoMXkF1dG9VbmxvY2skEgpBdXRvVW5sb2NrMh4KFAAEIhACBHZ3aHQKCF5FbmdyYW0kEgZFbmdyYW0yHgoUAAQiEAIEdndodAoIXkhlYWx0aCQSBkhlYWx0aDIaChIABCIOAgR2d2h0CgZeSG9tZSQSBEhvbWUyIAoVAAQiEQIEdndodAoJXk1hbmF0ZWUkEgdNYW5hdGVlMjgKIQAEIh0CBHZ3aHQKFV5MaW1pdGVkUGVlcnNBbGxvd2VkJBITTGltaXRlZFBlZXJzQWxsb3dlZDJdClAAAhIeAAQiGgIEdndodAoSXkNvbnRpbnVpdHlVbmxvY2skEhUABCIRAgR2d2h0CgleSG9tZUtpdCQSFQAEIhECBHZ3aHQKCV5BcHBsZVRWJBIJTm90U3luY2VkMisKGwAEIhcCBGFncnAKD15bMC05QS1aXXsxMH1cLhIMQXBwbGljYXRpb25zMsUBCrABAAISNAABChMABCIPAgVjbGFzcwoGXmdlbnAkChsABCIXAgRhZ3JwCg9eY29tLmFwcGxlLnNiZCQSPQABChMABCIPAgVjbGFzcwoGXmtleXMkCiQABCIgAgRhZ3JwChheY29tLmFwcGxlLnNlY3VyaXR5LnNvcyQSGQAEIhUCBHZ3aHQKDV5CYWNrdXBCYWdWMCQSHAAEIhgCBHZ3aHQKEF5pQ2xvdWRJZGVudGl0eSQSEFNlY3VyZU9iamVjdFN5bmMyYwpbAAISEgAEIg4CBHZ3aHQKBl5XaUZpJBJDAAEKEwAEIg8CBWNsYXNzCgZeZ2VucCQKEwAEIg8CBGFncnAKB15hcHBsZSQKFQAEIhECBHN2Y2UKCV5BaXJQb3J0JBIEV2lGaTLbAgrBAgACEhkABCIVAgR2d2h0Cg1eUENTQ2xvdWRLaXQkEhcABCITAgR2d2h0CgteUENTRXNjcm93JBIUAAQiEAIEdndodAoIXlBDU0ZERSQSGQAEIhUCBHZ3aHQKDV5QQ1NGZWxkc3BhciQSGQAEIhUCBHZ3aHQKDV5QQ1NNYWlsRHJvcCQSGgAEIhYCBHZ3aHQKDl5QQ1NNYXN0ZXJLZXkkEhYABCISAgR2d2h0CgpeUENTTm90ZXMkEhcABCITAgR2d2h0CgteUENTUGhvdG9zJBIYAAQiFAIEdndodAoMXlBDU1NoYXJpbmckEh0ABCIZAgR2d2h0ChFeUENTaUNsb3VkQmFja3VwJBIcAAQiGAIEdndodAoQXlBDU2lDbG91ZERyaXZlJBIZAAQiFQIEdndodAoNXlBDU2lNZXNzYWdlJBIVUHJvdGVjdGVkQ2xvdWRTdG9yYWdlMkAKKwAEIicCBGFncnAKH15jb20uYXBwbGUuc2FmYXJpLmNyZWRpdC1jYXJkcyQSEVNhZmFyaUNyZWRpdENhcmRzMjQKIQAEIh0CBGFncnAKFV5jb20uYXBwbGUuY2ZuZXR3b3JrJBIPU2FmYXJpUGFzc3dvcmRzMm0KXAACEh4ABCIaAgR2d2h0ChJeQWNjZXNzb3J5UGFpcmluZyQSGgAEIhYCBHZ3aHQKDl5OYW5vUmVnaXN0cnkkEhwABCIYAgR2d2h0ChBeV2F0Y2hNaWdyYXRpb24kEg1EZXZpY2VQYWlyaW5nMi0KIQAEIh0CBGFncnAKFV5jb20uYXBwbGUuY2ZuZXR3b3JrJBIIQmFja3N0b3A="),
- ]
- let (request1, data1) = policies[1]!
- let (request3, data3) = policies[3]!
+
+ let missingTuple = TPPolicyVersion(version: 900, hash: "not a hash")
+
+ let policy1Tuple = TPPolicyVersion(version: 1, hash: "SHA256:TLXrcQmY4ue3oP5pCX1pwsi9BF8cKfohlJBilCroeBs=")
+ let policy1Data = Data(base64Encoded: "CAESDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSCwoDTWFjEgRmdWxsEgwKBGlNYWMSBGZ1bGwSDQoHQXBwbGVUVhICdHYS" +
+ "DgoFV2F0Y2gSBXdhdGNoGhEKCVBDU0VzY3JvdxIEZnVsbBoXCgRXaUZpEgRmdWxsEgJ0dhIFd2F0Y2gaGQoRU2FmYXJpQ3JlZGl0" +
+ "Q2FyZHMSBGZ1bGwiDAoEZnVsbBIEZnVsbCIUCgV3YXRjaBIEZnVsbBIFd2F0Y2giDgoCdHYSBGZ1bGwSAnR2")!
+
+ let policy3Tuple = TPPolicyVersion(version: 3, hash: "SHA256:JZzazSuHXrUhiOfSgElsg6vYKpnvvEPVpciR8FewRWg=")
+ let policy3Data = Data(base64Encoded: "CAMSDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSCwoDTWFjEgRmdWxsEgwKBGlNYWMSBGZ1bGwSDQoHQXBwbGVUVhICdHYSDgoFV2F0Y2gSBXdhdGNoEhcKDkF1ZGlvQWNjZXNzb3J5EgVhdWRpbxocCg1EZXZpY2VQYWlyaW5nEgRmdWxsEgV3YXRjaBoXCghBcHBsZVBheRIEZnVsbBIFd2F0Y2gaJAoVUHJvdGVjdGVkQ2xvdWRTdG9yYWdlEgRmdWxsEgV3YXRjaBoXCghCYWNrc3RvcBIEZnVsbBIFd2F0Y2gaGQoKQXV0b1VubG9jaxIEZnVsbBIFd2F0Y2gaHwoQU2VjdXJlT2JqZWN0U3luYxIEZnVsbBIFd2F0Y2gaIAoRU2FmYXJpQ3JlZGl0Q2FyZHMSBGZ1bGwSBXdhdGNoGhMKBEhvbWUSBGZ1bGwSBXdhdGNoGh4KD1NhZmFyaVBhc3N3b3JkcxIEZnVsbBIFd2F0Y2gaGwoMQXBwbGljYXRpb25zEgRmdWxsEgV3YXRjaBoVCgZFbmdyYW0SBGZ1bGwSBXdhdGNoGi0KE0xpbWl0ZWRQZWVyc0FsbG93ZWQSBGZ1bGwSBXdhdGNoEgJ0dhIFYXVkaW8aFgoHTWFuYXRlZRIEZnVsbBIFd2F0Y2gaHgoEV2lGaRIEZnVsbBIFd2F0Y2gSAnR2EgVhdWRpbxoVCgZIZWFsdGgSBGZ1bGwSBXdhdGNoIhMKBGZ1bGwSBGZ1bGwSBXdhdGNoIhsKBWF1ZGlvEgRmdWxsEgV3YXRjaBIFYXVkaW8iFAoFd2F0Y2gSBGZ1bGwSBXdhdGNoIhUKAnR2EgRmdWxsEgV3YXRjaBICdHYyIgoWAAQiEgIEdndodAoKXkFwcGxlUGF5JBIIQXBwbGVQYXkyJgoYAAQiFAIEdndodAoMXkF1dG9VbmxvY2skEgpBdXRvVW5sb2NrMh4KFAAEIhACBHZ3aHQKCF5FbmdyYW0kEgZFbmdyYW0yHgoUAAQiEAIEdndodAoIXkhlYWx0aCQSBkhlYWx0aDIaChIABCIOAgR2d2h0CgZeSG9tZSQSBEhvbWUyIAoVAAQiEQIEdndodAoJXk1hbmF0ZWUkEgdNYW5hdGVlMjgKIQAEIh0CBHZ3aHQKFV5MaW1pdGVkUGVlcnNBbGxvd2VkJBITTGltaXRlZFBlZXJzQWxsb3dlZDJdClAAAhIeAAQiGgIEdndodAoSXkNvbnRpbnVpdHlVbmxvY2skEhUABCIRAgR2d2h0CgleSG9tZUtpdCQSFQAEIhECBHZ3aHQKCV5BcHBsZVRWJBIJTm90U3luY2VkMisKGwAEIhcCBGFncnAKD15bMC05QS1aXXsxMH1cLhIMQXBwbGljYXRpb25zMsUBCrABAAISNAABChMABCIPAgVjbGFzcwoGXmdlbnAkChsABCIXAgRhZ3JwCg9eY29tLmFwcGxlLnNiZCQSPQABChMABCIPAgVjbGFzcwoGXmtleXMkCiQABCIgAgRhZ3JwChheY29tLmFwcGxlLnNlY3VyaXR5LnNvcyQSGQAEIhUCBHZ3aHQKDV5CYWNrdXBCYWdWMCQSHAAEIhgCBHZ3aHQKEF5pQ2xvdWRJZGVudGl0eSQSEFNlY3VyZU9iamVjdFN5bmMyYwpbAAISEgAEIg4CBHZ3aHQKBl5XaUZpJBJDAAEKEwAEIg8CBWNsYXNzCgZeZ2VucCQKEwAEIg8CBGFncnAKB15hcHBsZSQKFQAEIhECBHN2Y2UKCV5BaXJQb3J0JBIEV2lGaTLbAgrBAgACEhkABCIVAgR2d2h0Cg1eUENTQ2xvdWRLaXQkEhcABCITAgR2d2h0CgteUENTRXNjcm93JBIUAAQiEAIEdndodAoIXlBDU0ZERSQSGQAEIhUCBHZ3aHQKDV5QQ1NGZWxkc3BhciQSGQAEIhUCBHZ3aHQKDV5QQ1NNYWlsRHJvcCQSGgAEIhYCBHZ3aHQKDl5QQ1NNYXN0ZXJLZXkkEhYABCISAgR2d2h0CgpeUENTTm90ZXMkEhcABCITAgR2d2h0CgteUENTUGhvdG9zJBIYAAQiFAIEdndodAoMXlBDU1NoYXJpbmckEh0ABCIZAgR2d2h0ChFeUENTaUNsb3VkQmFja3VwJBIcAAQiGAIEdndodAoQXlBDU2lDbG91ZERyaXZlJBIZAAQiFQIEdndodAoNXlBDU2lNZXNzYWdlJBIVUHJvdGVjdGVkQ2xvdWRTdG9yYWdlMkAKKwAEIicCBGFncnAKH15jb20uYXBwbGUuc2FmYXJpLmNyZWRpdC1jYXJkcyQSEVNhZmFyaUNyZWRpdENhcmRzMjQKIQAEIh0CBGFncnAKFV5jb20uYXBwbGUuY2ZuZXR3b3JrJBIPU2FmYXJpUGFzc3dvcmRzMm0KXAACEh4ABCIaAgR2d2h0ChJeQWNjZXNzb3J5UGFpcmluZyQSGgAEIhYCBHZ3aHQKDl5OYW5vUmVnaXN0cnkkEhwABCIYAgR2d2h0ChBeV2F0Y2hNaWdyYXRpb24kEg1EZXZpY2VQYWlyaW5nMi0KIQAEIh0CBGFncnAKFV5jb20uYXBwbGUuY2ZuZXR3b3JrJBIIQmFja3N0b3A=")!
let description = tmpStoreDescription(name: "container.db")
let container = try Container(name: ContainerName(container: "a", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
// nothing
- let (response1, error1) = container.fetchPolicyDocumentsSync(test: self, keys: [:])
+ let (response1, error1) = container.fetchPolicyDocumentsSync(test: self, versions: [])
XCTAssertNil(error1, "No error querying for an empty list")
XCTAssertEqual(response1, [:], "Received empty dictionary")
// local stuff
- let (response2, error2) = container.fetchPolicyDocumentsSync(test: self, keys: [1: request1])
- XCTAssertNil(error2, "No error getting locally known policy document")
- XCTAssertEqual(response2?.count, 1, "Got one response for request for one locally known policy")
- XCTAssertEqual(response2?[1]?[0], request1, "retrieved hash matches request hash")
- XCTAssertEqual(response2?[1]?[1], data1, "retrieved data matches known data")
+ do {
+ let (response2, error2) = container.fetchPolicyDocumentsSync(test: self, versions: Set([policy1Tuple]))
+ XCTAssertNil(error2, "No error getting locally known policy document")
+ XCTAssertEqual(response2?.count, 1, "Got one response for request for one locally known policy")
+ XCTAssert(response2?.keys.contains(policy1Tuple) ?? false, "Should have retrieved request for policy1")
+ XCTAssertEqual(response2?[policy1Tuple], policy1Data, "retrieved data matches known data")
+ }
// fetch remote
- let (response3, error3) = container.fetchPolicyDocumentsSync(test: self, keys: [1: request1, 3: request3])
- XCTAssertNil(error3, "No error fetching local + remote policy")
- XCTAssertEqual(response3?.count, 2, "Got two responses for local+remote policy request")
- XCTAssertEqual(response3?[1]?[0], request1, "retrieved hash matches local request hash")
- XCTAssertEqual(response3?[1]?[1], data1, "retrieved data matches local known data")
- XCTAssertEqual(response3?[3]?[0], request3, "retrieved hash matches remote request hash")
- XCTAssertEqual(response3?[3]?[1], data3, "retrieved data matches remote known data")
+ do {
+ let (response3, error3) = container.fetchPolicyDocumentsSync(test: self, versions: [policy1Tuple, policy3Tuple])
+ XCTAssertNil(error3, "No error fetching local + remote policy")
+ XCTAssertEqual(response3?.count, 2, "Got two responses for local+remote policy request")
+
+ XCTAssert(response3?.keys.contains(policy1Tuple) ?? false, "Should have retrieved request for policy1")
+ XCTAssertEqual(response3?[policy1Tuple], policy1Data, "retrieved data matches known data")
+
+ XCTAssert(response3?.keys.contains(policy3Tuple) ?? false, "Should have retrieved request for policy3")
+ XCTAssertEqual(response3?[policy3Tuple], policy3Data, "retrieved data matches known data")
+ }
// invalid version
- let (response4, error4) = container.fetchPolicyDocumentsSync(test: self, keys: [9000: "not a hash"])
- XCTAssertNil(response4, "No response for wrong [version: hash] combination")
- XCTAssertNotNil(error4, "Expected error fetching invalid policy version")
+ do {
+ let (response4, error4) = container.fetchPolicyDocumentsSync(test: self, versions: Set([missingTuple]))
+ XCTAssertNil(response4, "No response for wrong [version: hash] combination")
+ XCTAssertNotNil(error4, "Expected error fetching invalid policy version")
+ }
// valid + invalid
- let (response5, error5) = container.fetchPolicyDocumentsSync(test: self, keys: [9000: "not a hash",
- 1: request1,
- 3: request3, ])
- XCTAssertNil(response5, "No response for valid + unknown [version: hash] combination")
- XCTAssertNotNil(error5, "Expected error fetching valid + invalid policy version")
+ do {
+ let (response5, error5) = container.fetchPolicyDocumentsSync(test: self, versions: Set([missingTuple,
+ policy1Tuple,
+ policy3Tuple, ]))
+ XCTAssertNil(response5, "No response for valid + unknown [version: hash] combination")
+ XCTAssertNotNil(error5, "Expected error fetching valid + invalid policy version")
+ }
}
func testEscrowKeys() throws {
let secret = secretString.data(using: .utf8)
do {
- let testv1 = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeySigning, masterSecret: secret!, bottleSalt: testDSID)
+ let testv1 = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeySigning, masterSecret: secret!, bottleSalt: testDSID)
XCTAssertEqual(testv1, signingKey_384, "signing keys should match")
- let testv2 = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeyEncryption, masterSecret: secret!, bottleSalt: testDSID)
+ let testv2 = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeyEncryption, masterSecret: secret!, bottleSalt: testDSID)
XCTAssertEqual(testv2, encryptionKey_384, "encryption keys should match")
- let testv3 = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeySymmetric, masterSecret: secret!, bottleSalt: testDSID)
+ let testv3 = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeySymmetric, masterSecret: secret!, bottleSalt: testDSID)
XCTAssertEqual(testv3, symmetricKey_384, "symmetric keys should match")
let newSecretString = "I'm f secretI'm a secretI'm a secretI'm a secretI'm a secretI'm a secret"
let newSecret = newSecretString.data(using: .utf8)
- let testv4 = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeySigning, masterSecret: newSecret!, bottleSalt: testDSID)
+ let testv4 = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeySigning, masterSecret: newSecret!, bottleSalt: testDSID)
XCTAssertNotEqual(testv4, signingKey_384, "signing keys should not match")
- let testv5 = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeyEncryption, masterSecret: newSecret!, bottleSalt: testDSID)
+ let testv5 = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeyEncryption, masterSecret: newSecret!, bottleSalt: testDSID)
XCTAssertNotEqual(testv5, encryptionKey_384, "encryption keys should not match")
- let testv6 = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeySymmetric, masterSecret: newSecret!, bottleSalt: testDSID)
+ let testv6 = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeySymmetric, masterSecret: newSecret!, bottleSalt: testDSID)
XCTAssertNotEqual(testv6, symmetricKey_384, "symmetric keys should not match")
} catch {
XCTFail("error testing escrow key test vectors \(error)")
let secret = secretString.data(using: .utf8)
do {
- let testv1 = try RecoveryKeySet.generateRecoveryKey(keyType: recoveryKeyType.kOTRecoveryKeySigning, masterSecret: secret!, recoverySalt: testDSID)
+ let testv1 = try RecoveryKeySet.generateRecoveryKey(keyType: RecoveryKeyType.kOTRecoveryKeySigning, masterSecret: secret!, recoverySalt: testDSID)
XCTAssertEqual(testv1, recovery_signingKey_384, "signing keys should match")
- let testv2 = try RecoveryKeySet.generateRecoveryKey(keyType: recoveryKeyType.kOTRecoveryKeyEncryption, masterSecret: secret!, recoverySalt: testDSID)
+ let testv2 = try RecoveryKeySet.generateRecoveryKey(keyType: RecoveryKeyType.kOTRecoveryKeyEncryption, masterSecret: secret!, recoverySalt: testDSID)
XCTAssertEqual(testv2, recovery_encryptionKey_384, "encryption keys should match")
let newSecretString = "I'm f secretI'm a secretI'm a secretI'm a secretI'm a secretI'm a secret"
let newSecret = newSecretString.data(using: .utf8)
- let testv4 = try RecoveryKeySet.generateRecoveryKey(keyType: recoveryKeyType.kOTRecoveryKeySigning, masterSecret: newSecret!, recoverySalt: testDSID)
+ let testv4 = try RecoveryKeySet.generateRecoveryKey(keyType: RecoveryKeyType.kOTRecoveryKeySigning, masterSecret: newSecret!, recoverySalt: testDSID)
XCTAssertNotEqual(testv4, recovery_signingKey_384, "signing keys should not match")
- let testv5 = try RecoveryKeySet.generateRecoveryKey(keyType: recoveryKeyType.kOTRecoveryKeyEncryption, masterSecret: newSecret!, recoverySalt: testDSID)
+ let testv5 = try RecoveryKeySet.generateRecoveryKey(keyType: RecoveryKeyType.kOTRecoveryKeyEncryption, masterSecret: newSecret!, recoverySalt: testDSID)
XCTAssertNotEqual(testv5, recovery_encryptionKey_384, "encryption keys should not match")
} catch {
XCTFail("error testing RecoveryKey test vectors \(error)")
let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
let machineIDs = Set(["aaa", "bbb"])
- XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
- XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+ XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+ XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
print("preparing A")
- let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+ let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
var state = containerA.getStateSync(test: self)
_ = containerB.updateSync(test: self)
print("preparing B")
- let (bPeerID, _, _, _, _, error2) =
+ let (bPeerID, _, _, _, _, _, _, error2) =
containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerB.getStateSync(test: self)
do {
print("B prepares to join via bottle")
- let (bottlePeerID, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
+ let (bottlePeerID, views, policy, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
XCTAssertNil(errorPreflight, "Should be no error preflighting a vouch with bottle")
XCTAssertEqual(bottlePeerID, aPeerID, "Bottle should be for peer A")
+ XCTAssertNotNil(views, "Should have a set of views to restore")
+ XCTAssertNotNil(policy, "Should have a policy")
- let (voucherData, voucherSig, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
+ let (voucherData, voucherSig, _, _, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
XCTAssertNil(error3)
XCTAssertNotNil(voucherData)
assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
print("B joins")
- let (peerID, _, error) = containerB.joinSync(test: self, voucherData: voucherData!, voucherSig: voucherSig!, ckksKeys: [self.manateeKeySet], tlkShares: [])
+ let (peerID, _, _, _, error) = containerB.joinSync(test: self, voucherData: voucherData!, voucherSig: voucherSig!, ckksKeys: [self.manateeKeySet], tlkShares: [])
XCTAssertNil(error)
XCTAssertEqual(peerID, bPeerID!)
let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
let machineIDs = Set(["aaa", "bbb"])
- XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
- XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+ XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+ XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
print("preparing A")
- let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+ let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
var state = containerA.getStateSync(test: self)
_ = containerB.updateSync(test: self)
print("preparing B")
- let (bPeerID, _, _, _, _, error2) =
+ let (bPeerID, _, _, _, _, _, _, error2) =
containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerB.getStateSync(test: self)
do {
print("B prepares to join via bottle")
- let (bottlePeerID, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
+ let (bottlePeerID, views, policy, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
XCTAssertNil(errorPreflight, "Should be no error preflighting a vouch with bottle")
XCTAssertEqual(bottlePeerID, aPeerID, "Bottle should be for peer A")
+ XCTAssertNotNil(views, "Should have a set of views to restore")
+ XCTAssertNotNil(policy, "Should have a policy")
- let (voucherData, voucherSig, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
+ let (voucherData, voucherSig, _, _, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
XCTAssertNil(error3)
XCTAssertNotNil(voucherData)
assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
print("B joins")
- let (peerID, _, error) = containerB.joinSync(test: self, voucherData: voucherData!, voucherSig: voucherSig!, ckksKeys: [self.manateeKeySet], tlkShares: [])
+ let (peerID, _, _, _, error) = containerB.joinSync(test: self, voucherData: voucherData!, voucherSig: voucherSig!, ckksKeys: [self.manateeKeySet], tlkShares: [])
XCTAssertNil(error)
XCTAssertEqual(peerID, bPeerID!)
let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
let machineIDs = Set(["aaa", "bbb"])
- XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
- XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+ XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+ XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
print("preparing A")
- let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+ let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerA.getStateSync(test: self)
_ = containerB.updateSync(test: self)
print("preparing B")
- let (bPeerID, _, _, _, _, error2) =
+ let (bPeerID, _, _, _, _, _, _, error2) =
containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerB.getStateSync(test: self)
do {
print("B joins via bottle")
- let (bottlePeerID, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: "wrong escrow record")
+ let (bottlePeerID, views, policy, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: "wrong escrow record")
XCTAssertNotNil(errorPreflight, "Should be an error preflighting bottle that doesn't exist")
XCTAssertNil(bottlePeerID, "peerID should be nil for no bottle")
+ XCTAssertNil(views, "Should not have a set of views to restore")
+ XCTAssertNil(policy, "Should not have a policy")
- let (voucherData, voucherSig, error3) = containerB.vouchWithBottleSync(test: self, b: "wrong escrow record", entropy: entropy, bottleSalt: "123456789", tlkShares: [])
+ let (voucherData, voucherSig, _, _, error3) = containerB.vouchWithBottleSync(test: self, b: "wrong escrow record", entropy: entropy, bottleSalt: "123456789", tlkShares: [])
XCTAssertNotNil(error3)
XCTAssertNil(voucherData)
let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
let machineIDs = Set(["aaa", "bbb"])
- XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
- XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+ XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+ XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
print("preparing A")
- let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+ let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerA.getStateSync(test: self)
}
print("preparing B")
- let (bPeerID, _, _, _, _, error2) =
+ let (bPeerID, _, _, _, _, _, _, error2) =
containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
var state = containerB.getStateSync(test: self)
do {
print("B joins via bottle")
- let (bottlePeerID, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleB.bottleID!)
- XCTAssertNil(errorPreflight, "Should be no error preflighting a vouch with bottle")
- XCTAssertEqual(bottlePeerID, bPeerID, "Bottle should be for peer B")
+ let (bottlePeerID, views, policy, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleB.bottleID!)
+ XCTAssertNotNil(errorPreflight, "Should be an error preflighting bottle that doesn't correspond to a peer")
+ XCTAssertNil(bottlePeerID, "Should have no peer for invalid bottle")
+ XCTAssertNil(views, "Should not have a set of views to restore")
+ XCTAssertNil(policy, "Should not have a policy")
- let (voucherData, voucherSig, error3) = containerB.vouchWithBottleSync(test: self, b: bottleB.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
+ let (voucherData, voucherSig, _, _, error3) = containerB.vouchWithBottleSync(test: self, b: bottleB.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
XCTAssertNotNil(error3)
XCTAssertNil(voucherData)
let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
let machineIDs = Set(["aaa", "bbb"])
- XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
- XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+ XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+ XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
print("preparing A")
- let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+ let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
var state = containerA.getStateSync(test: self)
_ = containerB.updateSync(test: self)
print("preparing B")
- let (bPeerID, _, _, _, _, error2) =
+ let (bPeerID, _, _, _, _, _, _, error2) =
containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerB.getStateSync(test: self)
do {
print("B joins via bottle")
- let (bottlePeerID, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
+ let (bottlePeerID, views, policy, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
XCTAssertNil(errorPreflight, "Should be no error preflighting a vouch with bottle")
XCTAssertEqual(bottlePeerID, aPeerID, "Bottle should be for peer A")
+ XCTAssertNotNil(views, "Should have a set of views to restore")
+ XCTAssertNotNil(policy, "Should have a policy")
- let (voucherData, voucherSig, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "987654321", tlkShares: [])
+ let (voucherData, voucherSig, _, _, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "987654321", tlkShares: [])
XCTAssertNotNil(error3)
XCTAssertNil(voucherData)
let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
let machineIDs = Set(["aaa", "bbb"])
- XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
- XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+ XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+ XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
print("preparing A")
- let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+ let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
var state = containerA.getStateSync(test: self)
_ = containerB.updateSync(test: self)
print("preparing B")
- let (bPeerID, _, _, _, _, error2) =
+ let (bPeerID, _, _, _, _, _, _, error2) =
containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerB.getStateSync(test: self)
do {
print("B joins via bottle")
- let (bottlePeerID, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
+ let (bottlePeerID, views, policy, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
XCTAssertNil(errorPreflight, "Should be no error preflighting a vouch with bottle")
XCTAssertEqual(bottlePeerID, aPeerID, "Bottle should be for peer A")
+ XCTAssertNotNil(views, "Should have a set of views to restore")
+ XCTAssertNotNil(policy, "Should have a policy")
- let (voucherData, voucherSig, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: Data(count: Int(OTMasterSecretLength)), bottleSalt: "123456789", tlkShares: [])
+ let (voucherData, voucherSig, _, _, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: Data(count: Int(OTMasterSecretLength)), bottleSalt: "123456789", tlkShares: [])
XCTAssertNotNil(error3)
XCTAssertNil(voucherData)
let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
let machineIDs = Set(["aaa", "bbb"])
- XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
- XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+ XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+ XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
print("preparing A")
- let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+ let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
var state = containerA.getStateSync(test: self)
}
print("preparing B")
- let (bPeerID, _, _, _, _, error2) =
+ let (bPeerID, _, _, _, _, _, _, error2) =
containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerB.getStateSync(test: self)
self.cuttlefish.fetchViableBottlesError.append(FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .changeTokenExpired))
- let (bottlePeerID, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
+ let (bottlePeerID, views, policy, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
XCTAssertNotNil(errorPreflight, "Should be an error preflighting a vouch with bottle with a fetch error")
XCTAssertNil(bottlePeerID, "peerID should be nil")
+ XCTAssertNil(views, "Should not have a set of views to restore")
+ XCTAssertNil(policy, "Should not have a policy")
self.cuttlefish.fetchViableBottlesError.append(FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .changeTokenExpired))
- let (voucherData, voucherSig, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
+ let (voucherData, voucherSig, _, _, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
XCTAssertNotNil(error3)
XCTAssertNil(voucherData)
let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
let machineIDs = Set(["aaa", "bbb"])
- XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
- XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+ XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+ XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
print("preparing A")
- let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+ let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, aViewList, aPolicy, error) =
containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
XCTAssertNil(error)
XCTAssertNotNil(aPeerID)
XCTAssertNotNil(aPermanentInfo)
XCTAssertNotNil(aPermanentInfoSig)
+ XCTAssertNotNil(aViewList, "Should have a view list coming back from a successful prepare")
+ XCTAssertNotNil(aPolicy, "Should have a policy coming back from a successful prepare")
+ XCTAssertEqual(aPolicy?.version, prevailingPolicyVersion, "Policy coming back from prepare() should be prevailing policy version")
+
print("preparing B")
- let (bPeerID, bPermanentInfo, bPermanentInfoSig, _, _, error2) =
+ let (bPeerID, bPermanentInfo, bPermanentInfoSig, _, _, _, _, error2) =
containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerB.getStateSync(test: self)
assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
print("B joins by preapproval, and uploads all TLKShares that it has")
- let (bJoinedPeerID, _, bJoinedError) = containerB.preapprovedJoinSync(test: self, ckksKeys: [self.manateeKeySet], tlkShares: [])
+ let (bJoinedPeerID, _, views, policy, bJoinedError) = containerB.preapprovedJoinSync(test: self, ckksKeys: [self.manateeKeySet], tlkShares: [])
XCTAssertNil(bJoinedError, "Should be no error joining by preapproval")
XCTAssertNotNil(bJoinedPeerID, "Should have a peer ID out of join")
+ XCTAssertNotNil(views, "should have a list of views to use")
+ XCTAssertNotNil(policy, "Should have a policy back from preapprovedjoin")
assertTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
}
let (c2, peerID2) = try joinByVoucher(sponsor: c,
containerID: "second",
machineID: "bbb",
- machineIDs: ["aaa", "bbb", "ccc"],
+ machineIDs: ["aaa", "bbb", "ccc"], accountIsDemo: false,
store: store)
let (c3, peerID3) = try joinByVoucher(sponsor: c,
containerID: "third",
machineID: "ccc",
- machineIDs: ["aaa", "bbb", "ccc"],
+ machineIDs: ["aaa", "bbb", "ccc"], accountIsDemo: false,
store: store)
let (_, cUpdateError) = c.updateSync(test: self)
// But all that goes away, and a new peer establishes
self.cuttlefish.state = FakeCuttlefishServer.State()
- let (_, peerID2) = try establish(reload: false, contextID: "second", store: tmpStoreDescription(name: "container-peer2.db"))
+ let (_, peerID2) = try establish(reload: false, contextID: "second", accountIsDemo: false, store: tmpStoreDescription(name: "container-peer2.db"))
// And the first container fetches again, which should succeed
self.cuttlefish.nextFetchErrors.append(FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .changeTokenExpired))
cuttlefish: self.cuttlefish)
let machineIDs = Set(["aaa", "bbb", "ccc"])
- XCTAssertNil(c.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+ XCTAssertNil(c.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
print("preparing peer A")
- let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+ let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
c.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = c.getStateSync(test: self)
cuttlefish: self.cuttlefish)
let machineIDs = Set(["aaa", "bbb", "ccc"])
- XCTAssertNil(c.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+ XCTAssertNil(c.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
print("preparing peer A")
- let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+ let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
c.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = c.getStateSync(test: self)
XCTAssertNil(error)
XCTAssertNotNil(peerID)
}
- let (repairAccount, repairEscrow, resetOctagon, healthError) = c.requestHealthCheckSync(requiresEscrowCheck: true, test: self)
+ let (repairAccount, repairEscrow, resetOctagon, leaveTrust, healthError) = c.requestHealthCheckSync(requiresEscrowCheck: true, test: self)
XCTAssertEqual(repairAccount, false, "")
XCTAssertEqual(repairEscrow, false, "")
XCTAssertEqual(resetOctagon, false, "")
+ XCTAssertEqual(leaveTrust, false, "")
XCTAssertNil(healthError)
}
let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
let machineIDs = Set(["aaa", "bbb"])
- XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
- XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+ XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+ XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
print("preparing A")
- let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+ let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
var state = containerA.getStateSync(test: self)
_ = containerB.updateSync(test: self)
print("preparing B")
- let (bPeerID, _, _, _, _, error2) =
+ let (bPeerID, _, _, _, _, _, _, error2) =
containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerB.getStateSync(test: self)
do {
print("B prepares to join via bottle")
- let (bottlePeerID, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
+ let (bottlePeerID, views, policy, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
XCTAssertNil(errorPreflight, "Should be no error preflighting a vouch with bottle")
XCTAssertEqual(bottlePeerID, aPeerID, "Bottle should be for peer A")
+ XCTAssertNotNil(views, "Should have a set of views to restore")
+ XCTAssertNotNil(policy, "Should have a policy")
- let (voucherData, voucherSig, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
+ let (voucherData, voucherSig, _, _, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
XCTAssertNil(error3)
XCTAssertNotNil(voucherData)
assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
print("B joins")
- let (peerID, _, error) = containerB.joinSync(test: self, voucherData: voucherData!, voucherSig: voucherSig!, ckksKeys: [self.manateeKeySet], tlkShares: [])
+ let (peerID, _, _, _, error) = containerB.joinSync(test: self, voucherData: voucherData!, voucherSig: voucherSig!, ckksKeys: [self.manateeKeySet], tlkShares: [])
XCTAssertNotNil(error)
XCTAssertNil(peerID)
}
let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
let machineIDs = Set(["aaa", "bbb"])
- XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
- XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+ XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+ XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
print("preparing peer A")
- let (aPeerID, aPermanentInfo, aPermanentInfoSig, aStableInfo, aStableInfoSig, error) =
+ let (aPeerID, aPermanentInfo, aPermanentInfoSig, aStableInfo, aStableInfoSig, _, _, error) =
containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerA.getStateSync(test: self)
}
print("preparing B")
- let (bPeerID, bPermanentInfo, bPermanentInfoSig, bStableInfo, bStableInfoSig, error2) =
+ let (bPeerID, bPermanentInfo, bPermanentInfoSig, bStableInfo, bStableInfoSig, _, _, error2) =
containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
do {
let state = containerB.getStateSync(test: self)
assertTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
print("B joins")
- let (peerID, _, error) = containerB.joinSync(test: self,
- voucherData: voucherData!,
- voucherSig: voucherSig!,
- ckksKeys: [],
- tlkShares: [])
+ let (peerID, _, _, _, error) = containerB.joinSync(test: self,
+ voucherData: voucherData!,
+ voucherSig: voucherSig!,
+ ckksKeys: [],
+ tlkShares: [])
XCTAssertNil(error)
XCTAssertEqual(peerID, bPeerID!)
}
XCTAssertEqual(midList.machineIDs(in: .disallowed), disallowedMachineIDs, "List of disallowed machine IDs should match")
XCTAssertEqual(midList.machineIDs(in: .unknown), unknownMachineIDs, "List of unknown machine IDs should match")
-
let (fetchedAllowList, fetchErr) = container.fetchAllowedMachineIDsSync(test: self)
XCTAssertNil(fetchErr, "Should be no error fetching the allowed list")
XCTAssertEqual(fetchedAllowList, allowedMachineIDs, "A fetched list of allowed machine IDs should match the loaded list")
let description = tmpStoreDescription(name: "container.db")
let container = try Container(name: ContainerName(container: "test", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
- let (peerID, permanentInfo, permanentInfoSig, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+ let (peerID, permanentInfo, permanentInfoSig, _, _, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
XCTAssertNil(error)
XCTAssertNotNil(peerID)
try self.assert(container: container, allowedMachineIDs: [], disallowedMachineIDs: [], persistentStore: description, cuttlefish: self.cuttlefish)
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ccc"]), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ccc"], accountIsDemo: false), "should be able to set allowed machine IDs")
try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), disallowedMachineIDs: [], persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"]), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], accountIsDemo: false), "should be able to set allowed machine IDs")
try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb"]), disallowedMachineIDs: Set(["ccc"]), persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
XCTAssertTrue(container.onqueueFullIDMSListWouldBeHelpful(), "Container should think it could use an IDMS list set: there's machine IDs pending removal")
// once they're unknown, a full list set will make them disallowed
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "zzz", "kkk"]), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "zzz", "kkk"], accountIsDemo: false), "should be able to set allowed machine IDs")
try self.assert(container: container, allowedMachineIDs: Set(["aaa", "zzz", "kkk"]), disallowedMachineIDs: Set(["bbb", "ccc", "fff"]), persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
// Resetting the list to what it is doesn't change the list
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "zzz", "kkk"], listDifference: false), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "zzz", "kkk"], accountIsDemo: false, listDifference: false), "should be able to set allowed machine IDs")
try self.assert(container: container, allowedMachineIDs: Set(["aaa", "zzz", "kkk"]), disallowedMachineIDs: Set(["bbb", "ccc", "fff"]), persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
// But changing it to something completely new does
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["xxx", "mmm"]), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["xxx", "mmm"], accountIsDemo: false), "should be able to set allowed machine IDs")
try self.assert(container: container, allowedMachineIDs: Set(["xxx", "mmm"]), disallowedMachineIDs: Set(["aaa", "zzz", "kkk", "bbb", "ccc", "fff"]), persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
// And, readding a previously disallowed machine ID works too
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["xxx", "mmm", "aaa"]), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["xxx", "mmm", "aaa"], accountIsDemo: false), "should be able to set allowed machine IDs")
try self.assert(container: container, allowedMachineIDs: Set(["xxx", "mmm", "aaa"]), disallowedMachineIDs: Set(["zzz", "kkk", "bbb", "ccc", "fff"]), persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
try self.assert(container: container, allowedMachineIDs: [], disallowedMachineIDs: [], persistentStore: description, cuttlefish: self.cuttlefish)
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ccc"], listDifference: true), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ccc"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), disallowedMachineIDs: [], persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
// But, the next time we ask IDMS, they still haven't made it to the full list, and in fact, C has disappeared.
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], listDifference: true), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ddd", "eee"]), disallowedMachineIDs: Set(["ccc"]), persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
XCTAssertTrue(container.onqueueFullIDMSListWouldBeHelpful(), "Container should think it could use an IDMS list set: there's machine IDs pending removal")
// and a list set after the remove confirms the removal
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], listDifference: true), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ddd"]), disallowedMachineIDs: Set(["ccc", "eee"]), persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
// Then a new list set includes D! Hurray IDMS. Note that this is not a "list change", because the list doesn't actually change
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ddd"], listDifference: false), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ddd"], accountIsDemo: false, listDifference: false), "should be able to set allowed machine IDs")
try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ddd"]), disallowedMachineIDs: Set(["ccc", "eee"]), persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
// And another list set no longer includes D, so it should now be disallowed
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], listDifference: true), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb"]), disallowedMachineIDs: Set(["ccc", "ddd", "eee"]), persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
// And just to check the 48 hour boundary...
XCTAssertNil(container.addAllowedMachineIDsSync(test: self, machineIDs: ["xxx"]), "should be able to receive an add push")
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], listDifference: false), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], accountIsDemo: false, listDifference: false), "should be able to set allowed machine IDs")
try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "xxx"]), disallowedMachineIDs: Set(["ccc", "ddd", "eee"]), persistentStore: description, cuttlefish: self.cuttlefish)
container.moc.performAndWait {
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
// Setting the list again should kick out X, since it was 'added' too long ago
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], listDifference: true), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb"]), disallowedMachineIDs: Set(["ccc", "ddd", "eee", "xxx"]), persistentStore: description, cuttlefish: self.cuttlefish)
}
// Now TPH boots up with a preexisting model
let container = try Container(name: containerName, persistentStoreDescription: description, cuttlefish: cuttlefish)
- let (peerID, permanentInfo, permanentInfoSig, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+ let (peerID, permanentInfo, permanentInfoSig, _, _, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
XCTAssertNil(error)
XCTAssertNotNil(peerID)
try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), disallowedMachineIDs: [], persistentStore: description, cuttlefish: self.cuttlefish)
// Setting a new list should work fine
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ddd"]), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ddd"], accountIsDemo: false), "should be able to set allowed machine IDs")
try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ddd"]), disallowedMachineIDs: ["ccc"], persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertEqual(container.containerMO.allowedMachineIDs, Set<String>() as NSSet, "Set of allowed machine IDs should now be empty")
try self.assert(container: container, allowedMachineIDs: Set(["aaa"]), disallowedMachineIDs: ["bbb"], persistentStore: description, cuttlefish: self.cuttlefish)
// Setting a new list should work fine
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "ddd"]), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "ddd"], accountIsDemo: false), "should be able to set allowed machine IDs")
try self.assert(container: container, allowedMachineIDs: Set(["aaa", "ddd"]), disallowedMachineIDs: ["bbb"], persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertEqual(container.containerMO.allowedMachineIDs, Set<String>() as NSSet, "Set of allowed machine IDs should now be empty")
}
// and set the machine ID list to something that doesn't include 'aaa'
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["bbb", "ccc"], listDifference: true), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["bbb", "ccc"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
try self.assert(container: container, allowedMachineIDs: Set(["bbb", "ccc"]), disallowedMachineIDs: [], unknownMachineIDs: Set(["aaa"]), persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
// Setting it again is fine...
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["bbb", "ccc"], listDifference: false), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["bbb", "ccc"], accountIsDemo: false, listDifference: false), "should be able to set allowed machine IDs")
try self.assert(container: container, allowedMachineIDs: Set(["bbb", "ccc"]), disallowedMachineIDs: [], unknownMachineIDs: Set(["aaa"]), persistentStore: description, cuttlefish: self.cuttlefish)
// And doesn't reset the modified date on the record
}
// And can be promoted to 'allowed'
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], listDifference: true), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb"]), disallowedMachineIDs: ["ccc"], persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
}
func testMachineIDListSetDisallowedOldUnknownMachineIDs() throws {
let description = tmpStoreDescription(name: "container.db")
- let (container, _) = try establish(reload: false, contextID: OTDefaultContext, allowedMachineIDs: Set(), store: description)
+ var (container, peerID1) = try establish(reload: false, contextID: OTDefaultContext, allowedMachineIDs: Set(["aaa"]), accountIsDemo: false, store: description)
- // and set the machine ID list to something that doesn't include 'aaa'
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["bbb", "ccc"], listDifference: true), "should be able to set allowed machine IDs")
- try self.assert(container: container, allowedMachineIDs: Set(["bbb", "ccc"]), disallowedMachineIDs: [], unknownMachineIDs: Set(["aaa"]), persistentStore: description, cuttlefish: self.cuttlefish)
+ // and set the machine ID list to something that doesn't include 'ddd'
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ccc"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
+ try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), disallowedMachineIDs: [], unknownMachineIDs: Set(), persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
+ XCTAssertEqual(container.containerMO.honorIDMSListChanges, "YES", "honorIDMSListChanges should be YES")
- // But an entry for "aaa" should exist, as a peer in the model claims it as their MID
+ let unknownMachineID = "ddd"
+ let (_, peerID3) = try self.joinByVoucher(sponsor: container,
+ containerID: "second",
+ machineID: unknownMachineID,
+ machineIDs: Set([unknownMachineID, "aaa", "bbb", "ccc"]), accountIsDemo: false,
+ store: description)
+
+ // And the first container accepts the join...
+ let (_, cUpdateError) = container.updateSync(test: self)
+ XCTAssertNil(cUpdateError, "Should be able to update first container")
+ assertTrusts(context: container, peerIDs: [peerID1, peerID3])
+
+ try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), disallowedMachineIDs: [], unknownMachineIDs: Set([unknownMachineID]), persistentStore: description, cuttlefish: self.cuttlefish)
+
+ XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
+ XCTAssertEqual(container.containerMO.honorIDMSListChanges, "YES", "honorIDMSListChanges should be YES")
+
+ // But an entry for "ddd" should exist, as a peer in the model claims it as their MID
container.moc.performAndWait {
let knownMachineMOs = container.containerMO.machines as? Set<MachineMO> ?? Set()
- let aaaMOs = knownMachineMOs.filter { $0.machineID == "aaa" }
- XCTAssertEqual(aaaMOs.count, 1, "Should have one machine MO for aaa")
+ let unknownMOs = knownMachineMOs.filter { $0.machineID == unknownMachineID }
+ XCTAssertEqual(unknownMOs.count, 1, "Should have one machine MO for ddd")
- let aaaMO = aaaMOs.first!
- XCTAssertEqual(aaaMO.status, Int64(TPMachineIDStatus.unknown.rawValue), "Status of aaa MO should be 'unknown'")
- XCTAssertFalse(aaaMO.allowed, "allowed should no longer be a used field")
+ let dddMO = unknownMOs.first!
+ XCTAssertEqual(dddMO.status, Int64(TPMachineIDStatus.unknown.rawValue), "Status of ddd MO should be 'unknown'")
+ XCTAssertFalse(dddMO.allowed, "allowed should no longer be a used field")
- // Pretend that aaa was added 49 hours ago
- aaaMO.modified = Date(timeIntervalSinceNow: -60 * 60 * 49)
+ // Pretend that ddd was added 49 hours ago
+ dddMO.modified = Date(timeIntervalSinceNow: -60 * 60 * 49)
try! container.moc.save()
}
+ //reload container
+ do {
+ container = try Container(name: ContainerName(container: "test", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
+ XCTAssertEqual(container.containerMO.honorIDMSListChanges, "YES", "honorIDMSListChanges should be YES")
+
+ } catch {
+ XCTFail("Creating container errored: \(error)")
+ }
XCTAssertTrue(container.onqueueFullIDMSListWouldBeHelpful(), "Container should think it could use an IDMS list set: there's machine IDs pending removal")
- // And, setting the list again should disallow aaa, since it is so old
- // Note that this _should_ return a list difference, since A is promoted to disallowed
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["bbb", "ccc"], listDifference: true), "should be able to set allowed machine IDs")
- try self.assert(container: container, allowedMachineIDs: Set(["bbb", "ccc"]), disallowedMachineIDs: ["aaa"], persistentStore: description, cuttlefish: self.cuttlefish)
+ // And, setting the list again should disallow ddd, since it is so old
+ // Note that this _should_ return a list difference, since D is promoted to disallowed
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ccc"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
+ try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), disallowedMachineIDs: [unknownMachineID], persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
// Setting ths list again has no change
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["bbb", "ccc"], listDifference: false), "should be able to set allowed machine IDs")
- try self.assert(container: container, allowedMachineIDs: Set(["bbb", "ccc"]), disallowedMachineIDs: ["aaa"], persistentStore: description, cuttlefish: self.cuttlefish)
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ccc"], accountIsDemo: false, listDifference: false), "should be able to set allowed machine IDs")
+ try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), disallowedMachineIDs: [unknownMachineID], persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
- // But A can appear again, no problem.
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ccc"], listDifference: true), "should be able to set allowed machine IDs")
- try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), disallowedMachineIDs: [], persistentStore: description, cuttlefish: self.cuttlefish)
+ // But D can appear again, no problem.
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ccc", "ddd"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
+ try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc", "ddd"]), disallowedMachineIDs: [], persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
+ XCTAssertEqual(container.containerMO.honorIDMSListChanges, "YES", "honorIDMSListChanges should be YES")
}
func testMachineIDListHandlingWithPeers() throws {
let (_, peerID2) = try self.joinByVoucher(sponsor: container,
containerID: "second",
machineID: unknownMachineID,
- machineIDs: Set([unknownMachineID, "aaa", "bbb", "ccc"]),
+ machineIDs: Set([unknownMachineID, "aaa", "bbb", "ccc"]), accountIsDemo: false,
store: description)
// And the first container accepts the join...
func testMachineIDListHandlingInDemoAccounts() throws {
// Demo accounts have no machine IDs in their lists
let description = tmpStoreDescription(name: "container.db")
- let (container, peerID1) = try establish(reload: false, contextID: OTDefaultContext, allowedMachineIDs: Set(), store: description)
+ var (container, peerID1) = try establish(reload: false, contextID: OTDefaultContext, allowedMachineIDs: Set(), accountIsDemo: true, store: description)
// And so we just don't write down any MIDs
try self.assert(container: container, allowedMachineIDs: Set([]), disallowedMachineIDs: [], unknownMachineIDs: Set([]), persistentStore: description, cuttlefish: self.cuttlefish)
containerID: "second",
machineID: unknownMachineID,
machineIDs: Set(),
+ accountIsDemo: true,
store: description)
try self.assert(container: c2, allowedMachineIDs: Set([]), disallowedMachineIDs: [], unknownMachineIDs: Set([]), persistentStore: description, cuttlefish: self.cuttlefish)
+ c2.containerMO.honorIDMSListChanges = "NO"
+
// And the first container accepts the join...
let (_, cUpdateError) = container.updateSync(test: self)
XCTAssertNil(cUpdateError, "Should be able to update first container")
// And still has nothing in its list...
try self.assert(container: container, allowedMachineIDs: Set([]), disallowedMachineIDs: [], unknownMachineIDs: Set([]), persistentStore: description, cuttlefish: self.cuttlefish)
+ //reload container
+ do {
+ container = try Container(name: ContainerName(container: "test", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
+ XCTAssertEqual(container.containerMO.honorIDMSListChanges, "NO", "honorIDMSListChanges should be NO")
+
+ } catch {
+ XCTFail("Creating container errored: \(error)")
+ }
+
// Even after a full list set
- XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: [], listDifference: false), "should be able to set allowed machine IDs")
+ XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: [], accountIsDemo: true, listDifference: false), "should be able to set allowed machine IDs")
try self.assert(container: container, allowedMachineIDs: Set([]), disallowedMachineIDs: [], unknownMachineIDs: Set([]), persistentStore: description, cuttlefish: self.cuttlefish)
XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
+ XCTAssertEqual(container.containerMO.honorIDMSListChanges, "NO", "honorIDMSListChanges should be NO")
}
func testContainerAndModelConsistency() throws {
let preTestContainerName = ContainerName(container: "testToCreatePrepareData", context: "context")
let description = tmpStoreDescription(name: "container.db")
let containerTest = try Container(name: preTestContainerName, persistentStoreDescription: description, cuttlefish: cuttlefish)
- let (peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error) = containerTest.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+ let (peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error) = containerTest.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
XCTAssertNil(error)
XCTAssertNotNil(peerID)
XCTAssertNotNil(permanentInfo)
do {
let peerKeys: OctagonSelfPeerKeys = try loadEgoKeysSync(peerID: containerMO.egoPeerID!)
let info3 = TPPeerStableInfo(clock: containerEgoStableInfo!.clock + 2,
- policyVersion: containerEgoStableInfo!.policyVersion,
- policyHash: containerEgoStableInfo!.policyHash,
+ frozenPolicyVersion: containerEgoStableInfo!.frozenPolicyVersion,
+ flexiblePolicyVersion: containerEgoStableInfo!.flexiblePolicyVersion!,
policySecrets: containerEgoStableInfo!.policySecrets,
deviceName: containerEgoStableInfo!.deviceName,
serialNumber: containerEgoStableInfo!.serialNumber,
let e4 = NSError(domain: CKErrorDomain, code: CKError.serverRejectedRequest.rawValue, userInfo: [NSUnderlyingErrorKey: int4])
XCTAssertTrue(RetryingInvocable.retryableError(error: e4))
}
+
+ func testEstablishWithEnforceIDMSListNotSetBehavior() throws {
+ let contextID = "OTDefaultContext"
+ let description = tmpStoreDescription(name: "container.db")
+
+ var container = try Container(name: ContainerName(container: "test", context: contextID), persistentStoreDescription: description, cuttlefish: cuttlefish)
+ XCTAssertEqual(container.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+
+ let (peerID, permanentInfo, permanentInfoSig, _, _, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+ do {
+ let state = container.getStateSync(test: self)
+ XCTAssertFalse( state.bottles.filter { $0.peerID == peerID } .isEmpty, "should have a bottle for peer")
+ let secret = container.loadSecretSync(test: self, label: peerID!)
+ XCTAssertNotNil(secret, "secret should not be nil")
+ XCTAssertNil(error, "error should be nil")
+ }
+ XCTAssertNotNil(peerID)
+ XCTAssertNotNil(permanentInfo)
+ XCTAssertNotNil(permanentInfoSig)
+ XCTAssertNil(error)
+
+ _ = container.dumpSync(test: self)
+
+ do {
+ container = try Container(name: ContainerName(container: "test", context: contextID), persistentStoreDescription: description, cuttlefish: cuttlefish)
+ } catch {
+ XCTFail("Creating container errored: \(error)")
+ }
+
+ let (peerID2, _, error2) = container.establishSync(test: self, ckksKeys: [self.manateeKeySet], tlkShares: [], preapprovedKeys: [])
+ XCTAssertNil(error2)
+ XCTAssertNotNil(peerID2)
+
+ _ = container.dumpSync(test: self)
+ XCTAssertEqual(container.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+ }
+
+ func testJoinWithEnforceIDMSListNotSetBehavior() throws {
+ let description = tmpStoreDescription(name: "container.db")
+ let containerA = try Container(name: ContainerName(container: "a", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
+ let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
+ let containerC = try Container(name: ContainerName(container: "c", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
+
+ XCTAssertEqual(containerA.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+ XCTAssertEqual(containerB.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+ XCTAssertEqual(containerC.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+
+ print("preparing A")
+ let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, aViewList, aPolicy, error) =
+ containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+ XCTAssertNotNil(aViewList, "Should have a view list coming back from a successful prepare")
+ XCTAssertNotNil(aPolicy, "Should have a policy coming back from a successful prepare")
+ XCTAssertEqual(aPolicy?.version, prevailingPolicyVersion, "Policy coming back from prepare() should be prevailing policy version")
+
+ do {
+ let state = containerA.getStateSync(test: self)
+ XCTAssertFalse( state.bottles.filter { $0.peerID == aPeerID } .isEmpty, "should have a bottle for peer")
+ let secret = containerA.loadSecretSync(test: self, label: aPeerID!)
+ XCTAssertNotNil(secret, "secret should not be nil")
+ XCTAssertNil(error, "error should be nil")
+ }
+ XCTAssertNil(error)
+ XCTAssertNotNil(aPeerID)
+ XCTAssertNotNil(aPermanentInfo)
+ XCTAssertNotNil(aPermanentInfoSig)
+
+ print("establishing A")
+ do {
+ let (peerID, _, error) = containerA.establishSync(test: self, ckksKeys: [], tlkShares: [], preapprovedKeys: [])
+ XCTAssertNil(error)
+ XCTAssertNotNil(peerID)
+ }
+
+ print("preparing B")
+ let (bPeerID, bPermanentInfo, bPermanentInfoSig, bStableInfo, bStableInfoSig, _, _, error2) =
+ containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+ do {
+ let state = containerB.getStateSync(test: self)
+ XCTAssertFalse( state.bottles.filter { $0.peerID == bPeerID } .isEmpty, "should have a bottle for peer")
+ let secret = containerB.loadSecretSync(test: self, label: bPeerID!)
+ XCTAssertNotNil(secret, "secret should not be nil")
+ XCTAssertNil(error, "error should be nil")
+ }
+ XCTAssertNil(error2)
+ XCTAssertNotNil(bPeerID)
+ XCTAssertNotNil(bPermanentInfo)
+ XCTAssertNotNil(bPermanentInfoSig)
+
+ do {
+ assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
+ print("A vouches for B, but doesn't provide any TLKShares")
+ let (_, _, errorVouchingWithoutTLKs) =
+ containerA.vouchSync(test: self,
+ peerID: bPeerID!,
+ permanentInfo: bPermanentInfo!,
+ permanentInfoSig: bPermanentInfoSig!,
+ stableInfo: bStableInfo!,
+ stableInfoSig: bStableInfoSig!,
+ ckksKeys: [])
+ XCTAssertNil(errorVouchingWithoutTLKs, "Should be no error vouching without uploading TLKShares")
+ assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
+
+ print("A vouches for B, but doesn't only has provisional TLKs at the time")
+ let provisionalManateeKeySet = try self.makeFakeKeyHierarchy(zoneID: CKRecordZone.ID(zoneName: "Manatee"))
+ provisionalManateeKeySet.newUpload = true
+
+ let (_, _, errorVouchingWithProvisionalTLKs) =
+ containerA.vouchSync(test: self,
+ peerID: bPeerID!,
+ permanentInfo: bPermanentInfo!,
+ permanentInfoSig: bPermanentInfoSig!,
+ stableInfo: bStableInfo!,
+ stableInfoSig: bStableInfoSig!,
+ ckksKeys: [provisionalManateeKeySet])
+ XCTAssertNil(errorVouchingWithProvisionalTLKs, "Should be no error vouching without uploading TLKShares for a non-existent key")
+ assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
+
+ print("A vouches for B")
+ let (voucherData, voucherSig, error3) =
+ containerA.vouchSync(test: self,
+ peerID: bPeerID!,
+ permanentInfo: bPermanentInfo!,
+ permanentInfoSig: bPermanentInfoSig!,
+ stableInfo: bStableInfo!,
+ stableInfoSig: bStableInfoSig!,
+ ckksKeys: [self.manateeKeySet])
+ XCTAssertNil(error3)
+ XCTAssertNotNil(voucherData)
+ XCTAssertNotNil(voucherSig)
+
+ // As part of the vouch, A should have uploaded a tlkshare for B
+ assertTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
+
+ print("B joins")
+ let (peerID, _, _, _, error) = containerB.joinSync(test: self,
+ voucherData: voucherData!,
+ voucherSig: voucherSig!,
+ ckksKeys: [],
+ tlkShares: [])
+ XCTAssertNil(error)
+ XCTAssertEqual(peerID, bPeerID!)
+ }
+
+ _ = containerA.dumpSync(test: self)
+ _ = containerB.dumpSync(test: self)
+ _ = containerC.dumpSync(test: self)
+
+ print("preparing C")
+ let (cPeerID, cPermanentInfo, cPermanentInfoSig, cStableInfo, cStableInfoSig, _, _, error4) =
+ containerC.prepareSync(test: self, epoch: 1, machineID: "ccc", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+ do {
+ let state = containerC.getStateSync(test: self)
+ XCTAssertFalse( state.bottles.filter { $0.peerID == cPeerID } .isEmpty, "should have a bottle for peer")
+ let secret = containerC.loadSecretSync(test: self, label: cPeerID!)
+ XCTAssertNotNil(secret, "secret should not be nil")
+ XCTAssertNil(error, "error should be nil")
+ }
+ XCTAssertNil(error4)
+ XCTAssertNotNil(cPeerID)
+ XCTAssertNotNil(cPermanentInfo)
+ XCTAssertNotNil(cPermanentInfoSig)
+
+ do {
+ // C, when it joins, will create a new CKKS zone. It should also upload TLKShares for A and B.
+ let provisionalEngramKeySet = try self.makeFakeKeyHierarchy(zoneID: CKRecordZone.ID(zoneName: "Engram"))
+ provisionalEngramKeySet.newUpload = true
+
+ assertNoTLKShareFor(peerID: cPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
+ assertNoTLKShareFor(peerID: cPeerID!, keyUUID: provisionalEngramKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Engram"))
+
+ print("B vouches for C")
+ let (voucherData, voucherSig, error) =
+ containerB.vouchSync(test: self,
+ peerID: cPeerID!,
+ permanentInfo: cPermanentInfo!,
+ permanentInfoSig: cPermanentInfoSig!,
+ stableInfo: cStableInfo!,
+ stableInfoSig: cStableInfoSig!,
+ ckksKeys: [self.manateeKeySet])
+ XCTAssertNil(error)
+ XCTAssertNotNil(voucherData)
+ XCTAssertNotNil(voucherSig)
+
+ assertTLKShareFor(peerID: cPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
+
+ print("C joins")
+ let (peerID, _, _, _, error2) = containerC.joinSync(test: self,
+ voucherData: voucherData!,
+ voucherSig: voucherSig!,
+ ckksKeys: [self.manateeKeySet, provisionalEngramKeySet],
+ tlkShares: [])
+ XCTAssertNil(error2)
+ XCTAssertEqual(peerID, cPeerID!)
+
+ assertTLKShareFor(peerID: cPeerID!, keyUUID: provisionalEngramKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Engram"))
+ assertTLKShareFor(peerID: aPeerID!, keyUUID: provisionalEngramKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Engram"))
+ assertTLKShareFor(peerID: bPeerID!, keyUUID: provisionalEngramKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Engram"))
+ }
+
+ print("A updates")
+ do {
+ let (_, error) = containerA.updateSync(test: self)
+ XCTAssertNil(error)
+ }
+
+ do {
+ let state = containerA.getStateSync(test: self)
+ let a = state.peers[aPeerID!]!
+ XCTAssertTrue(a.dynamicInfo!.includedPeerIDs.contains(cPeerID!))
+ }
+
+ _ = containerA.dumpSync(test: self)
+ _ = containerB.dumpSync(test: self)
+ _ = containerC.dumpSync(test: self)
+
+ XCTAssertEqual(containerA.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+ XCTAssertEqual(containerB.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+ XCTAssertEqual(containerC.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+ }
+
+ func testPreApprovedJoinWithEnforceIDMSListNotSetBehavior() throws {
+ let description = tmpStoreDescription(name: "container.db")
+ let containerA = try Container(name: ContainerName(container: "a", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
+ let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
+
+ XCTAssertEqual(containerA.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+ XCTAssertEqual(containerB.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+
+ print("preparing A")
+ let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, aViewList, aPolicy, error) =
+ containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+ XCTAssertNil(error)
+ XCTAssertNotNil(aPeerID)
+ XCTAssertNotNil(aPermanentInfo)
+ XCTAssertNotNil(aPermanentInfoSig)
+
+ XCTAssertNotNil(aViewList, "Should have a view list coming back from a successful prepare")
+ XCTAssertNotNil(aPolicy, "Should have a policy coming back from a successful prepare")
+ XCTAssertEqual(aPolicy?.version, prevailingPolicyVersion, "Policy coming back from prepare() should be prevailing policy version")
+
+ print("preparing B")
+ let (bPeerID, bPermanentInfo, bPermanentInfoSig, _, _, _, _, error2) =
+ containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+ do {
+ let state = containerB.getStateSync(test: self)
+ XCTAssertFalse( state.bottles.filter { $0.peerID == bPeerID } .isEmpty, "should have a bottle for peer")
+ let secret = containerB.loadSecretSync(test: self, label: bPeerID!)
+ XCTAssertNotNil(secret, "secret should not be nil")
+ XCTAssertNil(error, "error should be nil")
+ }
+ XCTAssertNil(error2)
+ XCTAssertNotNil(bPeerID)
+ XCTAssertNotNil(bPermanentInfo)
+ XCTAssertNotNil(bPermanentInfoSig)
+
+ // Now, A establishes preapproving B
+ // Note: secd is responsible for passing in TLKShares to these preapproved keys in sosTLKShares
+
+ let bPermanentInfoParsed = TPPeerPermanentInfo(peerID: bPeerID!, data: bPermanentInfo!, sig: bPermanentInfoSig!, keyFactory: TPECPublicKeyFactory())
+ XCTAssertNotNil(bPermanentInfoParsed, "Should have parsed B's permanent info")
+
+ print("establishing A")
+ do {
+ let (peerID, _, error) = containerA.establishSync(test: self, ckksKeys: [self.manateeKeySet], tlkShares: [], preapprovedKeys: [bPermanentInfoParsed!.signingPubKey.spki()])
+ XCTAssertNil(error)
+ XCTAssertNotNil(peerID)
+ }
+
+ do {
+ assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
+
+ print("B joins by preapproval, and uploads all TLKShares that it has")
+ let (bJoinedPeerID, _, views, policy, bJoinedError) = containerB.preapprovedJoinSync(test: self, ckksKeys: [self.manateeKeySet], tlkShares: [])
+ XCTAssertNil(bJoinedError, "Should be no error joining by preapproval")
+ XCTAssertNotNil(bJoinedPeerID, "Should have a peer ID out of join")
+ XCTAssertNotNil(views, "should have a list of views to use")
+ XCTAssertNotNil(policy, "Should have a policy back from preapprovedjoin")
+
+ assertTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
+ }
+
+ _ = containerA.dumpSync(test: self)
+ _ = containerB.dumpSync(test: self)
+
+ XCTAssertEqual(containerA.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+ XCTAssertEqual(containerB.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+ }
+
+ func testReloadingContainerIDMSListVariable() throws {
+ let store = tmpStoreDescription(name: "container.db")
+ let contextID = "contextID"
+ var container = try Container(name: ContainerName(container: "test", context: contextID), persistentStoreDescription: store, cuttlefish: cuttlefish)
+
+ let (peerID, permanentInfo, permanentInfoSig, _, _, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+ do {
+ let state = container.getStateSync(test: self)
+ XCTAssertFalse( state.bottles.filter { $0.peerID == peerID } .isEmpty, "should have a bottle for peer")
+ let secret = container.loadSecretSync(test: self, label: peerID!)
+ XCTAssertNotNil(secret, "secret should not be nil")
+ XCTAssertNil(error, "error should be nil")
+ }
+ XCTAssertNotNil(peerID)
+ XCTAssertNotNil(permanentInfo)
+ XCTAssertNotNil(permanentInfoSig)
+ XCTAssertNil(error)
+
+ XCTAssertEqual(container.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+
+ _ = container.dumpSync(test: self)
+
+ do {
+ container = try Container(name: ContainerName(container: "test", context: contextID), persistentStoreDescription: store, cuttlefish: cuttlefish)
+ XCTAssertEqual(container.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+
+ } catch {
+ XCTFail("Creating container errored: \(error)")
+ }
+
+ let (peerID2, _, error2) = container.establishSync(test: self, ckksKeys: [self.manateeKeySet], tlkShares: [], preapprovedKeys: [])
+ XCTAssertNil(error2)
+ XCTAssertNotNil(peerID2)
+
+ _ = container.dumpSync(test: self)
+ }
}
}
bool SecCKKSResetSyncing(void) {
- [CKKSViewManager resetManager: true setTo: nil];
+ // The function name is a bit of a lie, but it does the thing.
+ [OTManager resetManager:true to:nil];
return SecCKKSIsEnabled();
}
void SecCKKSPerformLocalResync() {
#if OCTAGON
- secnotice("ckks", "Local keychain was reset; performing local resync");
- [[CKKSViewManager manager] rpcResyncLocal:nil reply:^(NSError *result) {
- if(result) {
- secnotice("ckks", "Local keychain reset resync finished with an error: %@", result);
- } else {
- secnotice("ckks", "Local keychain reset resync finished successfully");
- }
- }];
+ if(SecCKKSIsEnabled()) {
+ secnotice("ckks", "Local keychain was reset; performing local resync");
+ [[CKKSViewManager manager] rpcResyncLocal:nil reply:^(NSError *result) {
+ if(result) {
+ secnotice("ckks", "Local keychain reset resync finished with an error: %@", result);
+ } else {
+ secnotice("ckks", "Local keychain reset resync finished successfully");
+ }
+ }];
+ }
#endif
}
extern NSString* const OctagonAnalyticsStateMachineState;
extern NSString* const OctagonAnalyticIcloudAccountState;
+extern NSString* const OctagonAnalyticCDPBitStatus;
extern NSString* const OctagonAnalyticsTrustState;
extern NSString* const OctagonAnalyticsAttemptedJoin;
extern NSString* const OctagonAnalyticsLastHealthCheck;
extern NSString* const OctagonAnalyticsCloudKitProvisioned;
extern NSString* const OctagonAnalyticsCloudKitEnabled;
+extern NSString* const OctagonAnalyticsBottledUniqueTLKsRecovered;
+extern NSString* const OctagonAnalyticsBottledTotalTLKShares;
+extern NSString* const OctagonAnalyticsBottledTotalTLKSharesRecovered;
+extern NSString* const OctagonAnalyticsBottledUniqueTLKsWithSharesCount;
+extern NSString* const OctagonAnalyticsBottledTLKUniqueViewCount;
+
@class CKKSKeychainView;
@protocol CKKSAnalyticsFailableEvent <NSObject>
extern CKKSAnalyticsFailableEvent* const OctagonEventVoucherWithBottle;
/* inner: join with recovery key */
+extern CKKSAnalyticsFailableEvent* const OctagonEventPreflightVouchWithRecoveryKey;
extern CKKSAnalyticsFailableEvent* const OctagonEventVoucherWithRecoveryKey;
extern CKKSAnalyticsFailableEvent* const OctagonEventJoinRecoveryKeyValidationFailed;
extern CKKSAnalyticsFailableEvent* const OctagonEventJoinRecoveryKeyFailed;
NSString* const OctagonAnalyticsStateMachineState = @"OASMState";
NSString* const OctagonAnalyticIcloudAccountState = @"OAiC";
+NSString* const OctagonAnalyticCDPBitStatus = @"OACDPStatus";
+
NSString* const OctagonAnalyticsTrustState = @"OATrust";
NSString* const OctagonAnalyticsAttemptedJoin = @"OAAttemptedJoin";
NSString* const OctagonAnalyticsLastHealthCheck = @"OAHealthCheck";
NSString* const OctagonAnalyticsPrerecordPending = @"OAPrerecordPending";
NSString* const OctagonAnalyticsCDPStateRun = @"OACDPStateRun";
+NSString* const OctagonAnalyticsBottledUniqueTLKsRecovered = @"OABottledUniqueTLKsRecoveredCount";
+NSString* const OctagonAnalyticsBottledTotalTLKShares = @"OABottledTotalTLKSharesCount";
+NSString* const OctagonAnalyticsBottledTotalTLKSharesRecovered = @"OABottledTotalTLKSharesRecoveredCount";
+NSString* const OctagonAnalyticsBottledUniqueTLKsWithSharesCount = @"OABottledUniqueTLKsWithSharesCount";
+NSString* const OctagonAnalyticsBottledTLKUniqueViewCount = @"OABottledTLKUniqueViewCount";
+
NSString* const OctagonAnalyticsHaveMachineID = @"OAMIDPresent";
NSString* const OctagonAnalyticsMIDOnMemoizedList = @"OAMIDOnList";
NSString* const OctagonAnalyticsPeersWithMID = @"OAPeersWithMID";
CKKSAnalyticsFailableEvent* const OctagonEventPreflightVouchWithBottle = (CKKSAnalyticsFailableEvent*)@"OctagonEventPreflightVouchWithBottle";
CKKSAnalyticsFailableEvent* const OctagonEventVoucherWithBottle = (CKKSAnalyticsFailableEvent*)@"OctagonEventVoucherWithBottle";
+CKKSAnalyticsFailableEvent* const OctagonEventPreflightVouchWithRecoveryKey = (CKKSAnalyticsFailableEvent*)@"OctagonEventPreflightVouchWithRecoveryKey";;
CKKSAnalyticsFailableEvent* const OctagonEventVoucherWithRecoveryKey = (CKKSAnalyticsFailableEvent*)@"OctagonEventVoucherWithRecoveryKey";
CKKSAnalyticsFailableEvent* const OctagonEventSetRecoveryKey = (CKKSAnalyticsFailableEvent*)@"OctagonEventSetRecoveryKey";
error: (NSError * __autoreleasing *) error {
NSMutableDictionary* whereDict = [@{@"state": CKKSNilToNSNull(state), @"ckzone":CKKSNilToNSNull(zoneID.zoneName)} mutableCopy];
if(uuid) {
- whereDict[@"UUID"] = [CKKSSQLWhereObject op:@">" stringValue:uuid];
+ whereDict[@"UUID"] = [CKKSSQLWhereValue op:CKKSSQLWhereComparatorGreaterThan value:uuid];
}
return [self fetch:n
where:whereDict
NSError* error = NULL;
NSDictionary* queryAttributes = @{(__bridge NSString*) kSecClass: (__bridge NSString*) classP->name,
(__bridge NSString*) kSecAttrUUID: iqe.uuid,
- (__bridge NSString*) kSecAttrSyncViewHint: ckks.zoneID.zoneName,
(__bridge NSString*) kSecAttrSynchronizable: @(YES)};
ckksnotice("ckksincoming", ckks, "trying to delete with query: %@", queryAttributes);
Query *q = query_create_with_limit( (__bridge CFDictionaryRef) queryAttributes, NULL, kSecMatchUnlimited, &cferror);
-
if(cferror) {
ckkserror("ckksincoming", ckks, "couldn't create query: %@", cferror);
SecTranslateError(&error, cferror);
}
+ (NSArray<CKKSKey*>*)selfWrappedKeys:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error {
- return [self allWhere: @{@"UUID": [CKKSSQLWhereObject op:@"=" string:@"parentKeyUUID"], @"state": SecCKKSProcessedStateLocal, @"ckzone":zoneID.zoneName} error:error];
+ return [self allWhere: @{@"UUID": [CKKSSQLWhereColumn op:CKKSSQLWhereComparatorEquals
+ column:CKKSSQLWhereColumnNameParentKeyUUID],
+ @"state": SecCKKSProcessedStateLocal,
+ @"ckzone":zoneID.zoneName}
+ error:error];
}
+ (instancetype _Nullable)currentKeyForClass:(CKKSKeyClass*)keyclass
/* Synchronous operations */
- (void)handleKeychainEventDbConnection:(SecDbConnectionRef)dbconn
+ source:(SecDbTransactionSource)txionSource
added:(SecDbItemRef _Nullable)added
deleted:(SecDbItemRef _Nullable)deleted
- rateLimiter:(CKKSRateLimiter*)rateLimiter
- syncCallback:(SecBoolNSErrorCallback)syncCallback;
+ rateLimiter:(CKKSRateLimiter*)rateLimiter;
- (void)setCurrentItemForAccessGroup:(NSData*)newItemPersistentRef
hash:(NSData*)newItemSHA1
- (BOOL)otherDevicesReportHavingTLKs:(CKKSCurrentKeySet*)keyset;
-- (NSSet<NSString*>*)_onqueuePriorityOutgoingQueueUUIDs;
-
/* Asynchronous kickoffs */
- (CKKSOutgoingQueueOperation*)processOutgoingQueue:(CKOperationGroup* _Nullable)ckoperationGroup;
// Please don't use these unless you're an Operation in this package
@property NSHashTable<CKKSIncomingQueueOperation*>* incomingQueueOperations;
@property NSHashTable<CKKSOutgoingQueueOperation*>* outgoingQueueOperations;
+
+@property NSHashTable<CKKSScanLocalItemsOperation*>* scanLocalItemsOperations;
@property CKKSScanLocalItemsOperation* initialScanOperation;
// Returns the current state of this view, fastStatus is the same, but as name promise, no expensive calculations
@property CKKSResultOperation* processIncomingQueueAfterNextUnlockOperation;
@property CKKSResultOperation* resultsOfNextIncomingQueueOperationOperation;
-@property NSMutableDictionary<NSString*, SecBoolNSErrorCallback>* pendingSyncCallbacks;
-
// An extra queue for semaphore-waiting-based NSOperations
@property NSOperationQueue* waitingQueue;
_incomingQueueOperations = [NSHashTable weakObjectsHashTable];
_outgoingQueueOperations = [NSHashTable weakObjectsHashTable];
+ _scanLocalItemsOperations = [NSHashTable weakObjectsHashTable];
_cloudkitDeleteZoneOperations = [NSHashTable weakObjectsHashTable];
_localResetOperations = [NSHashTable weakObjectsHashTable];
_keysetProviderOperations = [NSHashTable weakObjectsHashTable];
}];
- _pendingSyncCallbacks = [[NSMutableDictionary alloc] init];
-
_lockStateTracker = lockStateTracker;
_savedTLKNotifier = savedTLKNotifier;
#if DEBUG
// During testing, keep the developer honest: this function should always have the self identities, unless the account has lost trust
- if(self.trustStatus == CKKSAccountStatusAvailable && ![state isEqualToString:SecCKKSZoneKeyStateLoggedOut]) {
+ // But, beginTrustedOperation is currently racy: if it acquires the queue and sets the trust bit between fetching the trust states and getting on the queue,
+ // this will fire. So, release SecCKKSZoneKeyStateInitialized from this check.
+ if(self.trustStatus == CKKSAccountStatusAvailable
+ && ![state isEqualToString:SecCKKSZoneKeyStateLoggedOut]
+ && ![state isEqualToString:SecCKKSZoneKeyStateInitialized]) {
bool hasSelfIdentities = false;
NSAssert(self.currentTrustStates.count > 0, @"Should have at least one trust state");
for(CKKSPeerProviderState* state in self.currentTrustStates) {
}
- (void) handleKeychainEventDbConnection: (SecDbConnectionRef) dbconn
+ source:(SecDbTransactionSource)txionSource
added: (SecDbItemRef) added
deleted: (SecDbItemRef) deleted
rateLimiter: (CKKSRateLimiter*) rateLimiter
- syncCallback: (SecBoolNSErrorCallback) syncCallback {
+{
if(!SecCKKSIsEnabled()) {
ckksnotice("ckks", self, "Skipping handleKeychainEventDbConnection due to disabled CKKS");
return;
return;
}
+ if(txionSource == kSecDbSOSTransaction) {
+ ckksnotice("ckks", self, "Received an incoming %@ from SOS", isAdd ? @"addition" : (isModify ? @"modification" : @"deletion"));
+ }
+
// Our caller gave us a database connection. We must get on the local queue to ensure atomicity
// Note that we're at the mercy of the surrounding db transaction, so don't try to rollback here
[self dispatchSyncWithConnection: dbconn block: ^bool {
self.droppedItems = true;
ckksnotice("ckks", self, "Dropping sync item modification due to CK account state; will scan to find changes later");
+ // We're positively not logged into CloudKit, and therefore don't expect this item to be synced anytime particularly soon.
+ NSString* uuid = (__bridge NSString*)SecDbItemGetValue(added ? added : deleted, &v10itemuuid, NULL);
+
+ SecBoolNSErrorCallback syncCallback = [[CKKSViewManager manager] claimCallbackForUUID:uuid];
if(syncCallback) {
- // We're positively not logged into CloudKit, and therefore don't expect this item to be synced anytime particularly soon.
- [self callSyncCallbackWithErrorNoAccount: syncCallback];
+ [CKKSViewManager callSyncCallbackWithErrorNoAccount: syncCallback];
}
- return true;
- }
- // Always record the callback, even if we can't encrypt the item right now. Maybe we'll get to it soon!
- if(syncCallback) {
- CFErrorRef cferror = NULL;
- NSString* uuid = (__bridge_transfer NSString*) CFRetain(SecDbItemGetValue(added, &v10itemuuid, &cferror));
- if(!cferror && uuid) {
- self.pendingSyncCallbacks[uuid] = syncCallback;
- }
- CFReleaseNull(cferror);
+ return true;
}
CKKSOutgoingQueueEntry* oqe = nil;
}
}
-- (NSSet<NSString*>*)_onqueuePriorityOutgoingQueueUUIDs
-{
- return [self.pendingSyncCallbacks.allKeys copy];
-}
-
- (CKKSOutgoingQueueOperation*)processOutgoingQueue:(CKOperationGroup*)ckoperationGroup {
return [self processOutgoingQueueAfter:nil ckoperationGroup:ckoperationGroup];
}
[self.outgoingQueueOperationScheduler triggerAt:requiredDelay];
+ [op linearDependencies:self.outgoingQueueOperations];
+
[self scheduleOperation: op];
ckksnotice("ckksoutgoing", self, "Scheduled %@", op);
return op;
return [self scanLocalItems:operationName ckoperationGroup:nil after:nil];
}
-- (CKKSScanLocalItemsOperation*)scanLocalItems:(NSString*)operationName ckoperationGroup:(CKOperationGroup*)operationGroup after:(NSOperation*)after {
- CKKSScanLocalItemsOperation* scanOperation = [[CKKSScanLocalItemsOperation alloc] initWithCKKSKeychainView:self ckoperationGroup:operationGroup];
+- (CKKSScanLocalItemsOperation*)scanLocalItems:(NSString*)operationName
+ ckoperationGroup:(CKOperationGroup*)operationGroup
+ after:(NSOperation*)after
+{
+ CKKSScanLocalItemsOperation* scanOperation = (CKKSScanLocalItemsOperation*)[self findFirstPendingOperation:self.scanLocalItemsOperations];
+
+ if(scanOperation) {
+ [scanOperation addNullableDependency:after];
+
+ // check (again) for race condition; if the op has started we need to add another (for the dependency)
+ if([scanOperation isPending]) {
+ scanOperation.ckoperationGroup = operationGroup;
+
+ scanOperation.name = [NSString stringWithFormat:@"%@::%@", scanOperation.name, operationName];
+ return scanOperation;
+ }
+ }
+
+ scanOperation = [[CKKSScanLocalItemsOperation alloc] initWithCKKSKeychainView:self ckoperationGroup:operationGroup];
scanOperation.name = operationName;
[scanOperation addNullableDependency:self.lastFixupOperation];
[scanOperation addNullableDependency:self.keyStateReadyDependency];
[scanOperation addNullableDependency:after];
- [self scheduleOperation: scanOperation];
+ [scanOperation linearDependencies:self.scanLocalItemsOperations];
+
+ [self scheduleOperation:scanOperation];
return scanOperation;
}
if([state isEqualToString: SecCKKSStateDeleted]) {
// Hurray, this must be a success
- SecBoolNSErrorCallback callback = self.pendingSyncCallbacks[oqe.uuid];
- if(callback) {
- callback(true, nil);
- self.pendingSyncCallbacks[oqe.uuid] = nil;
+ SecBoolNSErrorCallback syncCallback = [[CKKSViewManager manager] claimCallbackForUUID:oqe.uuid];
+ if(syncCallback) {
+ syncCallback(true, nil);
}
[oqe deleteFromDatabase: &localerror];
- (bool)_onqueueErrorOutgoingQueueEntry: (CKKSOutgoingQueueEntry*) oqe itemError: (NSError*) itemError error: (NSError* __autoreleasing*) error {
dispatch_assert_queue(self.queue);
- SecBoolNSErrorCallback callback = self.pendingSyncCallbacks[oqe.uuid];
+ SecBoolNSErrorCallback callback = [[CKKSViewManager manager] claimCallbackForUUID:oqe.uuid];
if(callback) {
callback(false, itemError);
- self.pendingSyncCallbacks[oqe.uuid] = nil;
}
NSError* localerror = nil;
[self.loggedOut fulfill];
[self.accountStateKnown fulfill];
- // Tell all pending sync clients that we don't expect to ever sync
- for(NSString* callbackUUID in self.pendingSyncCallbacks.allKeys) {
- [self callSyncCallbackWithErrorNoAccount:self.pendingSyncCallbacks[callbackUUID]];
- self.pendingSyncCallbacks[callbackUUID] = nil;
- }
-
return true;
}];
}];
[self scheduleAccountStatusOperation: logout];
}
-- (void)callSyncCallbackWithErrorNoAccount:(SecBoolNSErrorCallback)syncCallback {
- CKKSAccountStatus accountStatus = self.accountStatus;
- dispatch_async(self.queue, ^{
- syncCallback(false, [NSError errorWithDomain:@"securityd"
- code:errSecNotLoggedIn
- userInfo:@{NSLocalizedDescriptionKey:
- [NSString stringWithFormat: @"No iCloud account available(%d); item is not expected to sync", (int)accountStatus]}]);
- });
-}
-
#pragma mark - Trust operations
- (void)beginTrustedOperation:(NSArray<id<CKKSPeerProvider>>*)peerProviders
{
[self.launch addEvent:@"changes-fetched"];
+ if(changedRecords.count == 0 && deletedRecords.count == 0 && !moreComing && !resync) {
+ // Early-exit, so we don't pick up the account keys or kick off an IncomingQueue operation for no changes
+ [self dispatchSync:^bool {
+ ckkserror("ckksfetch", self, "No record changes in this fetch");
+
+ NSError* error = nil;
+ CKKSZoneStateEntry* state = [CKKSZoneStateEntry state:self.zoneName];
+ state.lastFetchTime = [NSDate date]; // The last fetch happened right now!
+ state.changeToken = newChangeToken;
+ state.moreRecordsInCloudKit = moreComing;
+ [state saveToDatabase:&error];
+ if(error) {
+ ckkserror("ckksfetch", self, "Couldn't save new server change token: %@", error);
+ }
+ return true;
+ }];
+ return;
+ }
+
[self dispatchSyncWithAccountKeys:^bool{
for (CKRecord* record in changedRecords) {
[self _onqueueCKRecordChanged:record resync:resync];
[self.incomingQueueOperations removeAllObjects];
}
+ @synchronized(self.scanLocalItemsOperations) {
+ for(NSOperation* op in self.scanLocalItemsOperations) {
+ [op cancel];
+ }
+ [self.scanLocalItemsOperations removeAllObjects];
+ }
+
[super cancelAllOperations];
}
[self.keyStateNonTransientDependency cancel];
[self.zoneChangeFetcher cancel];
[self.notifyViewChangedScheduler cancel];
+ [self.pokeKeyStateMachineScheduler cancel];
[self cancelPendingOperations];
}
// Synchronous, on some thread. Get back on the CKKS queue for SQL thread-safety.
+ __block bool enterWaitForTLKUpload = false;
[ckks dispatchSyncWithAccountKeys: ^bool{
if(self.cancelled) {
ckksnotice("ckkstlk", ckks, "CKKSNewTLKOperation cancelled, quitting");
self.keyset = keyset;
- [ckks _onqueueAdvanceKeyStateMachineToState:SecCKKSZoneKeyStateWaitForTLKUpload withError:nil];
-
+ // Finish this transaction to cause a keychiain db commit
+ // This means that if we provide the new keys to another thread, they'll be able to immediately load them from the keychain
+ enterWaitForTLKUpload = true;
return true;
}];
+
+ if(enterWaitForTLKUpload) {
+ // And move the CKKS state machine:
+ [ckks dispatchSyncWithAccountKeys: ^bool{
+ [ckks _onqueueAdvanceKeyStateMachineToState:SecCKKSZoneKeyStateWaitForTLKUpload withError:nil];
+ return true;
+ }];
+ }
}
- (void)cancel {
#import <CloudKit/CloudKit.h>
#import <CloudKit/CloudKit_Private.h>
+#import "keychain/ckks/CKKSViewManager.h"
#import "CKKSKeychainView.h"
#import "CKKSCurrentKeyPointer.h"
#import "CKKSOutgoingQueueOperation.h"
NSError* error = nil;
- NSSet<NSString*>* priorityUUIDs = [ckks _onqueuePriorityOutgoingQueueUUIDs];
+ NSSet<NSString*>* priorityUUIDs = [[CKKSViewManager manager] pendingCallbackUUIDs];
NSMutableArray<CKKSOutgoingQueueEntry*>* priorityEntries = [NSMutableArray array];
NSMutableSet<NSString*>* priorityEntryUUIDs = [NSMutableSet set];
@end
// Helper class to use with where clauses
-// If you pass in one of these instead of a concrete value, its substring will be used directly, instead of binding the value as a named parameter
-@interface CKKSSQLWhereObject : NSObject
-@property NSString* sqlOp;
-@property NSString* contents;
-- (instancetype)initWithOperation:(NSString*)op string:(NSString*)str;
-+ (instancetype)op:(NSString*)op string:(NSString*)str;
-+ (instancetype)op:(NSString*)op stringValue:(NSString*)str; // Will add single quotes around your value.
+// If you pass in one of these in a where dictionary instead of a concrete value, columnName will be
+// used directly, instead of binding as a named parameter. Therefore, it's essential to use
+// compile-time constants for both fields.
+
+typedef NS_ENUM(uint64_t, CKKSSQLWhereComparator) {
+ CKKSSQLWhereComparatorEquals = 1,
+ CKKSSQLWhereComparatorNotEquals = 2,
+ CKKSSQLWhereComparatorGreaterThan = 3,
+ CKKSSQLWhereComparatorLessThan = 4,
+};
+
+NSString* CKKSSQLWhereComparatorAsString(CKKSSQLWhereComparator comparator);
+
+// This typedef is to ensure that CKKSSQLWhereColumn can only ever produce static strings
+typedef NS_ENUM(uint64_t, CKKSSQLWhereColumnName) {
+ CKKSSQLWhereColumnNameUUID = 1,
+ CKKSSQLWhereColumnNameParentKeyUUID = 2,
+};
+NSString* CKKSSQLWhereColumnNameAsString(CKKSSQLWhereColumnName columnName);
+
+@interface CKKSSQLWhereColumn : NSObject
+@property CKKSSQLWhereComparator sqlOp;
+@property CKKSSQLWhereColumnName columnName;
+- (instancetype)initWithOperation:(CKKSSQLWhereComparator)op columnName:(CKKSSQLWhereColumnName)column;
++ (instancetype)op:(CKKSSQLWhereComparator)op column:(CKKSSQLWhereColumnName)columnName;
@end
+// Unlike CKKSSQLWhereColumn, this will insert the value as a parameter in a prepared statement
+// but gives you the flexbility to inject a sqlOp. sqlOp must be a compile-time constant.
+@interface CKKSSQLWhereValue : NSObject
+@property CKKSSQLWhereComparator sqlOp;
+@property NSString* value;
+- (instancetype)initWithOperation:(CKKSSQLWhereComparator)op value:(NSString*)value;
++ (instancetype)op:(CKKSSQLWhereComparator)op value:(NSString*)value;
+@end
+
+
NS_ASSUME_NONNULL_END
[whereClause appendFormat: @" AND "];
}
- if([value class] == [CKKSSQLWhereObject class]) {
- // Use this string verbatim
- CKKSSQLWhereObject* whereob = (CKKSSQLWhereObject*) value;
- [whereClause appendFormat: @"%@%@%@", key, whereob.sqlOp, whereob.contents];
+ if([value class] == [CKKSSQLWhereValue class]) {
+ CKKSSQLWhereValue* obj = (CKKSSQLWhereValue*)value;
+ [whereClause appendFormat: @"%@%@(?)", key, CKKSSQLWhereComparatorAsString(obj.sqlOp)];
+
+ } else if([value class] == [CKKSSQLWhereColumn class]) {
+ CKKSSQLWhereColumn* obj = (CKKSSQLWhereColumn*)value;
+ [whereClause appendFormat: @"%@%@%@",
+ key,
+ CKKSSQLWhereComparatorAsString(obj.sqlOp),
+ CKKSSQLWhereColumnNameAsString(obj.columnName)];
+
} else {
[whereClause appendFormat: @"%@=(?)", key];
}
return orderByClause;
}
++ (void)bindWhereClause:(sqlite3_stmt*)stmt whereDict:(NSDictionary*)whereDict cferror:(CFErrorRef*)cferror
+{
+ __block int whereObjectsSkipped = 0;
+ [whereDict.allKeys enumerateObjectsUsingBlock:^(id _Nonnull key, NSUInteger i, BOOL * _Nonnull stop) {
+ if([whereDict[key] class] == [CKKSSQLWhereValue class]) {
+ CKKSSQLWhereValue* obj = (CKKSSQLWhereValue*)whereDict[key];
+ SecDbBindObject(stmt, (int)(i+1-whereObjectsSkipped), (__bridge CFStringRef)obj.value, cferror);
+ } else if([whereDict[key] class] == [CKKSSQLWhereColumn class]) {
+ // skip
+ whereObjectsSkipped += 1;
+ } else {
+ SecDbBindObject(stmt, (int)(i+1-whereObjectsSkipped), (__bridge CFStringRef) whereDict[key], cferror);
+ }
+ }];
+}
+
+ (bool) deleteFromTable: (NSString*) table where: (NSDictionary*) whereDict connection:(SecDbConnectionRef) dbconn error: (NSError * __autoreleasing *) error {
__block CFErrorRef cferror = NULL;
NSString * sql = [[NSString alloc] initWithFormat: @"DELETE FROM %@%@;", table, whereClause];
SecDbPrepare(dbconn, (__bridge CFStringRef) sql, &cferror, ^void (sqlite3_stmt *stmt) {
- __block int whereObjectsSkipped = 0;
-
- [whereDict.allKeys enumerateObjectsUsingBlock:^(id _Nonnull key, NSUInteger i, BOOL * _Nonnull stop) {
- if([whereDict[key] class] != [CKKSSQLWhereObject class]) {
- SecDbBindObject(stmt, (int)(i+1-whereObjectsSkipped), (__bridge CFStringRef) whereDict[key], &cferror);
- } else {
- whereObjectsSkipped += 1;
- }
- }];
+ [self bindWhereClause:stmt whereDict:whereDict cferror:&cferror];
SecDbStep(dbconn, stmt, &cferror, ^(bool *stop) {
});
NSString * sql = [[NSString alloc] initWithFormat: @"SELECT %@ FROM %@%@%@%@%@;", columns, table, whereClause, groupByClause, orderByClause, limitClause];
SecDbPrepare(dbconn, (__bridge CFStringRef) sql, &cferror, ^void (sqlite3_stmt *stmt) {
- __block int whereObjectsSkipped = 0;
- [whereDict.allKeys enumerateObjectsUsingBlock:^(id _Nonnull key, NSUInteger i, BOOL * _Nonnull stop) {
- if([whereDict[key] class] != [CKKSSQLWhereObject class]) {
- SecDbBindObject(stmt, (int)(i+1-whereObjectsSkipped), (__bridge CFStringRef) whereDict[key], &cferror);
- } else {
- whereObjectsSkipped += 1;
- }
- }];
+ [self bindWhereClause:stmt whereDict:whereDict cferror:&cferror];
SecDbStep(dbconn, stmt, &cferror, ^(bool *stop) {
__block NSMutableDictionary<NSString*, CKKSSQLResult*>* row = [[NSMutableDictionary alloc] init];
NSString* sql = [[NSString alloc] initWithFormat:@"SELECT %@ FROM %@%@", columns, quotedTable, whereClause];
SecDbPrepare(dbconn, (__bridge CFStringRef)sql, &cferror, ^(sqlite3_stmt* stmt) {
- [whereDict.allKeys enumerateObjectsUsingBlock:^(id _Nonnull key, NSUInteger i, BOOL* _Nonnull stop) {
- if ([whereDict[key] class] != [CKKSSQLWhereObject class]) {
- SecDbBindObject(stmt, (int)(i+1), (__bridge CFStringRef) whereDict[key], &cferror);
- }
- }];
+ [self bindWhereClause:stmt whereDict:whereDict cferror:&cferror];
SecDbStep(dbconn, stmt, &cferror, ^(bool*stop) {
__block NSMutableDictionary<NSString*, CKKSSQLResult*>* row = [[NSMutableDictionary alloc] init];
}
@end
-#pragma mark - CKKSSQLWhereObject
+NSString* CKKSSQLWhereComparatorAsString(CKKSSQLWhereComparator comparator)
+{
+ switch(comparator) {
+ case CKKSSQLWhereComparatorEquals:
+ return @"=";
+ case CKKSSQLWhereComparatorNotEquals:
+ return @"<>";
+ case CKKSSQLWhereComparatorGreaterThan:
+ return @">";
+ case CKKSSQLWhereComparatorLessThan:
+ return @"<";
+ }
+}
-@implementation CKKSSQLWhereObject
-- (instancetype)initWithOperation:(NSString*)op string: (NSString*) str {
- if(self = [super init]) {
+NSString* CKKSSQLWhereColumnNameAsString(CKKSSQLWhereColumnName columnName)
+{
+ switch(columnName) {
+ case CKKSSQLWhereColumnNameUUID:
+ return @"uuid";
+ case CKKSSQLWhereColumnNameParentKeyUUID:
+ return @"parentKeyUUID";
+ }
+}
+
+#pragma mark - CKKSSQLWhereColumn
+
+@implementation CKKSSQLWhereColumn
+- (instancetype)initWithOperation:(CKKSSQLWhereComparator)op columnName:(CKKSSQLWhereColumnName)column
+{
+ if((self = [super init])) {
_sqlOp = op;
- _contents = str;
+ _columnName = column;
}
return self;
}
-
-+ (instancetype)op:(NSString*) op string: (NSString*) str {
- return [[CKKSSQLWhereObject alloc] initWithOperation:op string: str];
++ (instancetype)op:(CKKSSQLWhereComparator)op column:(CKKSSQLWhereColumnName)columnName
+{
+ return [[CKKSSQLWhereColumn alloc] initWithOperation:op columnName:columnName];
}
+@end
-+ (instancetype)op:(NSString*) op stringValue: (NSString*) str {
- return [[CKKSSQLWhereObject alloc] initWithOperation:op string:[NSString stringWithFormat:@"'%@'", str]];
-}
+#pragma mark - CKKSSQLWhereObject
+@implementation CKKSSQLWhereValue
+- (instancetype)initWithOperation:(CKKSSQLWhereComparator)op value:(NSString*)value
+{
+ if((self = [super init])) {
+ _sqlOp = op;
+ _value = value;
+ }
+ return self;
+}
++ (instancetype)op:(CKKSSQLWhereComparator)op value:(NSString*)value
+{
+ return [[CKKSSQLWhereValue alloc] initWithOperation:op value:value];
+}
@end
@class CKKSEgoManifest;
@interface CKKSScanLocalItemsOperation : CKKSResultOperation
+@property CKOperationGroup* ckoperationGroup;
+
@property (weak) CKKSKeychainView* ckks;
@property size_t recordsFound;
*/
#if OCTAGON
+#import <TrustedPeers/TPPolicy.h>
+#import <TrustedPeers/TPPBPolicyKeyViewMapping.h>
+#import <TrustedPeers/TPDictionaryMatchingRules.h>
#import "keychain/ckks/CKKSAnalytics.h"
#import "keychain/ckks/CKKSKeychainView.h"
#import <IMCore/IMCloudKitHooks.h>
@interface CKKSScanLocalItemsOperation ()
-@property CKOperationGroup* ckoperationGroup;
@property (assign) NSUInteger processedItems;
@end
return self;
}
+// returns true if the currently loaded TPPolicy in the view manager says that only items with a VewHint
+// matching the viewName belong to this view.
+- (BOOL)policyRecommendsOnlyViewHintItems:(CKKSKeychainView*)ckks
+{
+ // Early-exit for when feature is not on:
+ if(![[CKKSViewManager manager] useCKKSViewsFromPolicy]) {
+ return YES;
+ }
+
+ TPPBPolicyKeyViewMapping* viewRule = nil;
+
+ // If there's more than one rule matching this view, then exit with NO.
+ for(TPPBPolicyKeyViewMapping* mapping in [CKKSViewManager manager].policy.keyViewMapping) {
+ if([mapping.view isEqualToString:ckks.zoneName]) {
+ if(viewRule == nil) {
+ viewRule = mapping;
+ } else {
+ // Too many rules for this view! Don't perform optimization.
+ ckksnotice("ckksscan", ckks, "Too many policy rules for view %@", ckks.zoneName);
+ return NO;
+ }
+ }
+ }
+
+ if(viewRule.hasMatchingRule &&
+ viewRule.matchingRule.andsCount == 0 &&
+ viewRule.matchingRule.orsCount == 0 &&
+ !viewRule.matchingRule.hasNot &&
+ !viewRule.matchingRule.hasExists &&
+ viewRule.matchingRule.hasMatch) {
+ if([@"vwht" isEqualToString:viewRule.matchingRule.match.fieldName] &&
+ [viewRule.matchingRule.match.regex isEqualToString:[NSString stringWithFormat:@"^%@$", ckks.zoneName]]) {
+ return YES;
+ } else {
+ ckksnotice("ckksscan", ckks, "Policy view rule is not a match against viewhint: %@", viewRule);
+ }
+ } else {
+ ckksnotice("ckksscan", ckks, "Policy view rule is of unknown type: %@", viewRule);
+ }
+
+ return NO;
+}
+
- (void) main {
// Take a strong reference.
CKKSKeychainView* ckks = self.ckks;
continue;
}
- NSDictionary* queryAttributes = @{(__bridge NSString*) kSecClass: (__bridge NSString*) (*class)->name,
- (__bridge NSString*) kSecReturnRef: @(YES),
- (__bridge NSString*) kSecAttrSynchronizable: @(YES),
- (__bridge NSString*) kSecAttrTombstone: @(NO),
- // This works ~as long as~ item views are chosen by view hint only. It's a significant perf win, though.
- // <rdar://problem/32269541> SpinTracer: CKKSScanLocalItemsOperation expensive on M8 machines
- (__bridge NSString*) kSecAttrSyncViewHint: ckks.zoneName,
- };
- ckksinfo("ckksscan", ckks, "Scanning all synchronizable items for: %@", queryAttributes);
+ // As a performance optimization, if the current policy says that this view only includes items by viewhint,
+ // add that to the query.
+ BOOL limitToViewHint = [self policyRecommendsOnlyViewHintItems:ckks];
+
+ NSMutableDictionary* queryAttributes = [
+ @{(__bridge NSString*) kSecClass: (__bridge NSString*) (*class)->name,
+ (__bridge NSString*) kSecReturnRef: @(YES),
+ (__bridge NSString*) kSecAttrSynchronizable: @(YES),
+ (__bridge NSString*) kSecAttrTombstone: @(NO),
+ } mutableCopy];
+
+ if(limitToViewHint) {
+ queryAttributes[(__bridge NSString*)kSecAttrSyncViewHint] = ckks.zoneName;
+ }
+
+ ckksnotice("ckksscan", ckks, "Scanning all synchronizable %@ items(%@) for: %@", (__bridge NSString*)(*class)->name, self.name, queryAttributes);
Query *q = query_create_with_limit( (__bridge CFDictionaryRef) queryAttributes, NULL, kSecMatchUnlimited, &cferror);
bool ok = false;
@property id<OTSOSAdapter> sosPeerAdapter;
-@property (nullable) TPPolicy* policy;
+@property (readonly, nullable) TPPolicy* policy;
-@property NSMutableDictionary<NSString*, CKKSKeychainView*>* views;
+@property (readonly) NSMutableDictionary<NSString*, CKKSKeychainView*>* views;
-- (instancetype)initWithContainerName:(NSString*)containerName
- usePCS:(bool)usePCS
- sosAdapter:(id<OTSOSAdapter> _Nullable)sosAdapter
- cloudKitClassDependencies:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies;
+- (instancetype)initWithContainer:(CKContainer*)container
+ sosAdapter:(id<OTSOSAdapter> _Nullable)sosAdapter
+ accountStateTracker:(CKKSAccountStateTracker*)accountTracker
+ lockStateTracker:(CKKSLockStateTracker*)lockStateTracker
+ cloudKitClassDependencies:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies;
- (CKKSKeychainView*)findView:(NSString*)viewName;
- (CKKSKeychainView*)findOrCreateView:(NSString*)viewName;
- (void)setupAnalytics;
-- (NSString*)viewNameForItem:(SecDbItemRef)item;
+- (NSString* _Nullable)viewNameForItem:(SecDbItemRef)item;
- (void)handleKeychainEventDbConnection:(SecDbConnectionRef)dbconn
source:(SecDbTransactionSource)txionSource
// Cancels pending operations owned by this view manager
- (void)cancelPendingOperations;
-// Use these to acquire (and set) the singleton
+ (instancetype)manager;
-+ (instancetype _Nullable)resetManager:(bool)reset setTo:(CKKSViewManager* _Nullable)obj;
// Called by XPC every 24 hours
- (void)xpc24HrNotification;
-/* White-box testing only */
-- (CKKSKeychainView*)restartZone:(NSString*)viewName;
-
-// Returns the viewList for a CKKSViewManager
+// Returns the current set of views
- (NSSet<NSString*>*)viewList;
- (NSSet<NSString*>*)defaultViewList;
-- (void)setViewList:(NSSet<NSString*>* _Nullable)newViewList;
+// Call this to set the syncing views+policy that this manager will use.
+// If beginCloudKitOperationOfAllViews has previously been called, then any new views created
+// as a result of this call will begin CK operation.
+- (void)setSyncingViews:(NSSet<NSString*>* _Nullable)viewNames sortingPolicy:(TPPolicy* _Nullable)policy;
- (void)clearAllViews;
// Create all views, but don't begin CK/network operations
+// Remove as part of <rdar://problem/57768740> CKKS: ensure we collect keychain changes made before policy is loaded from disk
- (void)createViews;
// Call this to begin CK operation of all views
+// This bit will be 'sticky', in that any new views created with also begin cloudkit operation immediately.
+// (clearAllViews will reset this bit.)
- (void)beginCloudKitOperationOfAllViews;
// Notify sbd to re-backup.
// first time after launch, only waits the the initial call
- (BOOL)waitForTrustReady;
-// For testing
-- (void)setOverrideCKKSViewsFromPolicy:(BOOL)value;
+// Helper function to make CK containers
++ (CKContainer*)makeCKContainer:(NSString*)containerName
+ usePCS:(bool)usePCS;
+
+// Checks featureflags to return whether we should use policy-based views, or use the hardcoded list
- (BOOL)useCKKSViewsFromPolicy;
+
+// Interfaces to examine sync callbacks
+- (SecBoolNSErrorCallback _Nullable)claimCallbackForUUID:(NSString*)uuid;
+- (NSSet<NSString*>*)pendingCallbackUUIDs;
++ (void)callSyncCallbackWithErrorNoAccount:(SecBoolNSErrorCallback)syncCallback;
+@end
+
+@interface CKKSViewManager (Testing)
+- (void)setOverrideCKKSViewsFromPolicy:(BOOL)value;
+- (void)resetSyncingPolicy;
+
- (void)haltAll;
+- (CKKSKeychainView*)restartZone:(NSString*)viewName;
+- (void)haltZone:(NSString*)viewName;
+// If set, any set passed to setSyncingViews will be intersected with this set
+- (void)setSyncingViewsAllowList:(NSSet<NSString*>* _Nullable)viewNames;
@end
NS_ASSUME_NONNULL_END
#import <os/feature_private.h>
+#import "keychain/ckks/CKKSAccountStateTracker.h"
#import "keychain/ckks/CKKSViewManager.h"
#import "keychain/ckks/CKKSKeychainView.h"
#import "keychain/ckks/CKKSSynchronizeOperation.h"
#import "CKKSAnalytics.h"
#endif
+#if !OCTAGON
@interface CKKSViewManager () <NSXPCListenerDelegate>
-#if OCTAGON
+#else
+@interface CKKSViewManager () <NSXPCListenerDelegate,
+ CKKSCloudKitAccountStateListener>
+
@property NSXPCListener *listener;
+@property (nullable) NSSet<NSString*>* viewAllowList;
+
// Once you set these, all CKKSKeychainViews created will use them
@property CKKSCloudKitClassDependencies* cloudKitClassDependencies;
@property (nonatomic) BOOL overrideCKKSViewsFromPolicy;
@property (nonatomic) BOOL valueCKKSViewsFromPolicy;
+@property (nonatomic) BOOL startCKOperationAtViewCreation;
+
+@property BOOL itemModificationsBeforePolicyLoaded;
+
+// Make writable
+@property (nullable) TPPolicy* policy;
#endif
@end
@implementation CKKSViewManager
#if OCTAGON
-NSSet<NSString*>* _viewList;
-
-- (instancetype)initWithContainerName: (NSString*) containerName
- usePCS: (bool)usePCS
- sosAdapter:(id<OTSOSAdapter> _Nullable)sosAdapter
- cloudKitClassDependencies:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies
+- (instancetype)initWithContainer:(CKContainer*)container
+ sosAdapter:(id<OTSOSAdapter> _Nullable)sosAdapter
+ accountStateTracker:(CKKSAccountStateTracker*)accountTracker
+ lockStateTracker:(CKKSLockStateTracker*)lockStateTracker
+ cloudKitClassDependencies:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies
{
if(self = [super init]) {
_cloudKitClassDependencies = cloudKitClassDependencies;
_sosPeerAdapter = sosAdapter;
- _viewList = nil;
- _container = [self makeCKContainer: containerName usePCS:usePCS];
- _accountTracker = [[CKKSAccountStateTracker alloc] init:self.container nsnotificationCenterClass:cloudKitClassDependencies.nsnotificationCenterClass];
- _lockStateTracker = [[CKKSLockStateTracker alloc] init];
+ _viewAllowList = nil;
+ _container = container;
+ _accountTracker = accountTracker;
+ _lockStateTracker = lockStateTracker;
[_lockStateTracker addLockStateObserver:self];
_reachabilityTracker = [[CKKSReachabilityTracker alloc] init];
+ _itemModificationsBeforePolicyLoaded = NO;
_zoneChangeFetcher = [[CKKSZoneChangeFetcher alloc] initWithContainer:_container
fetchClass:cloudKitClassDependencies.fetchRecordZoneChangesOperationClass
_views = [[NSMutableDictionary alloc] init];
_pendingSyncCallbacks = [[NSMutableDictionary alloc] init];
+ _startCKOperationAtViewCreation = NO;
+
_completedSecCKKSInitialize = [[CKKSCondition alloc] init];
WEAKIFY(self);
[self notifyNewTLKsInKeychain];
}];
+ _policy = nil;
+
_listener = [NSXPCListener anonymousListener];
_listener.delegate = self;
[_listener resume];
+
+ // Start listening for CK account status (for sync callbacks)
+ [_accountTracker registerForNotificationsOfCloudKitAccountStatusChange:self];
}
return self;
}
--(CKContainer*)makeCKContainer:(NSString*)containerName usePCS:(bool)usePCS {
++ (CKContainer*)makeCKContainer:(NSString*)containerName
+ usePCS:(bool)usePCS
+{
CKContainer* container = [CKContainer containerWithIdentifier:containerName];
if(!usePCS) {
CKContainerOptions* containerOptions = [[CKContainerOptions alloc] init];
}
}
+#pragma mark - View List handling
+
- (NSSet<NSString*>*)defaultViewList {
NSSet<NSString*>* fullList = [OTSOSActualAdapter sosCKKSViewList];
return fullList;
}
--(NSSet<NSString*>*)viewList {
- if (_viewList) {
- return _viewList;
- } else {
- return [self defaultViewList];
+- (NSSet<NSString*>*)viewList {
+ return [self.views.allKeys copy];
+}
+
+- (void)setSyncingViews:(NSSet<NSString*>*)viewNames sortingPolicy:(TPPolicy*)policy
+{
+ secnotice("ckks-policy", "New syncing policy: %@ views: %@", policy, viewNames);
+
+ if(![self useCKKSViewsFromPolicy]) {
+ // Thanks, but no thanks.
+ viewNames = [self defaultViewList];
+ secnotice("ckks-policy", "Reverting to default view list: %@", viewNames);
+ }
+
+ if(self.viewAllowList) {
+ secnotice("ckks-policy", "Intersecting view list with allow list: %@", self.viewAllowList);
+ NSMutableSet<NSString*>* set = [viewNames mutableCopy];
+ [set intersectSet:self.viewAllowList];
+
+ viewNames = set;
+ secnotice("ckks-policy", "Final list: %@", viewNames);
+ }
+
+ self.policy = policy;
+
+ @synchronized(self.views) {
+ NSArray* previousViewNames = [self.views.allKeys copy];
+
+ // First, shut down any views that are no longer in the set
+ for(NSString* viewName in previousViewNames) {
+ if(![viewNames containsObject:viewName]) {
+ secnotice("ckks-policy", "Stopping old view %@", viewName);
+ [self clearView:viewName];
+ }
+ }
+
+ for(NSString* viewName in viewNames) {
+ if([previousViewNames containsObject:viewName]) {
+ CKKSKeychainView* view = [self findView:viewName];
+ secnotice("ckks-policy", "Already have view %@", view);
+ } else {
+ CKKSKeychainView* view = [self findOrCreateView:viewName];
+ secnotice("ckks-policy", "Created new view %@", view);
+ }
+ }
+
+ if(self.itemModificationsBeforePolicyLoaded) {
+ secnotice("ckks-policy", "Issuing scan suggestions to handle missed items");
+ for(CKKSKeychainView* view in [self.views allValues]) {
+ [view scanLocalItems:@"item-added-before-policy"];
+ }
+ self.itemModificationsBeforePolicyLoaded = NO;
+ }
}
}
-- (void)setViewList:(NSSet<NSString*>* _Nullable)newViewList {
- _viewList = newViewList;
+- (void)setSyncingViewsAllowList:(NSSet<NSString*>*)viewNames
+{
+ self.viewAllowList = viewNames;
+}
+
+- (void)resetSyncingPolicy
+{
+ secnotice("ckks-policy", "Setting policy to nil");
+ self.policy = nil;
+
+ self.startCKOperationAtViewCreation = NO;
}
+#pragma mark - View Handling
+
- (void)setView: (CKKSKeychainView*) obj {
CKKSKeychainView* kcv = nil;
@synchronized(self.views) {
tempviews = [self.views.allValues copy];
[self.views removeAllObjects];
+
+ self.startCKOperationAtViewCreation = NO;
}
for(CKKSKeychainView* view in tempviews) {
zoneModifier:self.zoneModifier
savedTLKNotifier: self.savedTLKNotifier
cloudKitClassDependencies:self.cloudKitClassDependencies];
+
+ if(self.startCKOperationAtViewCreation) {
+ [self.views[viewName] beginCloudKitOperation];
+ }
return self.views[viewName];
}
}
- (void)createViews
{
- // In the future, the CKKSViewManager needs to persist its policy property through daemon restarts
- // and load it here, before creating whatever views it was told to (in a previous daemon lifetime)
- for (NSString* viewName in self.viewList) {
- CKKSKeychainView* view = [self findOrCreateView:viewName];
- (void)view;
+ if(![self useCKKSViewsFromPolicy]) {
+ // In the future, the CKKSViewManager needs to persist its policy property through daemon restarts
+ // and load it here, before creating whatever views it was told to (in a previous daemon lifetime)
+ for (NSString* viewName in [self defaultViewList]) {
+ CKKSKeychainView* view = [self findOrCreateView:viewName];
+ (void)view;
+ }
+ } else {
+ secnotice("ckks-views", "Not loading default view list due to enabled CKKS4All");
}
}
- (void)beginCloudKitOperationOfAllViews
{
- for (NSString* viewName in self.viewList) {
- CKKSKeychainView* view = [self findView:viewName];
+ self.startCKOperationAtViewCreation = YES;
+
+ for (CKKSKeychainView* view in self.views.allValues) {
[view beginCloudKitOperation];
}
}
return tlks;
}
-- (CKKSKeychainView*)restartZone:(NSString*)viewName {
+- (void)haltZone:(NSString*)viewName
+{
@synchronized(self.views) {
- [self.views[viewName] halt];
+ CKKSKeychainView* view = self.views[viewName];
+ [view halt];
+ [view cancelAllOperations];
self.views[viewName] = nil;
}
+}
+
+- (CKKSKeychainView*)restartZone:(NSString*)viewName {
+ [self haltZone:viewName];
return [self findOrCreateView: viewName];
}
if (self.overrideCKKSViewsFromPolicy) {
return self.valueCKKSViewsFromPolicy;
} else {
- return os_feature_enabled(Security, CKKSViewsFromPolicy);
+ BOOL viewsFromPolicy = os_feature_enabled(Security, CKKSViewsFromPolicy);
+
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ secnotice("ckks", "ViewsFromPolicy feature flag: %@", viewsFromPolicy ? @"on" : @"off");
+ });
+ return viewsFromPolicy;
}
}
-- (NSString*)viewNameForItem: (SecDbItemRef) item {
+- (NSString* _Nullable)viewNameForItem:(SecDbItemRef)item
+{
if ([self useCKKSViewsFromPolicy]) {
CFErrorRef cferror = NULL;
- NSMutableDictionary *dict = (__bridge_transfer NSMutableDictionary*) SecDbItemCopyPListWithMask(item, ~0, &cferror);
+ NSMutableDictionary *dict = (__bridge_transfer NSMutableDictionary*) SecDbItemCopyPListWithMask(item, kSecDbSyncFlag, &cferror);
if(cferror) {
secerror("ckks: Couldn't fetch attributes from item: %@", cferror);
CFReleaseNull(cferror);
- return [self viewNameForViewHint: nil];
- }
- TPPolicy* policy = self.policy;
- if (policy == nil) {
- return [self viewNameForViewHint: nil];
+ return nil;
}
- NSString* view = [policy mapKeyToView:dict];
+
+ NSString* view = [self.policy mapKeyToView:dict];
if (view == nil) {
- return [self viewNameForViewHint: nil];
+ secerror("ckks: No view returned from policy (%@): %@", self.policy, dict);
+ return nil;
}
+
+ // Horrible hack until <rdar://problem/57810109> Cuttlefish: remove Safari prefix from view names
+ if([view isEqualToString:@"CreditCards"]) {
+ return @"SafariCreditCards";
+ }
+ if([view isEqualToString:@"Passwords"]) {
+ return @"SafariPasswords";
+ }
+
return view;
} else {
CFErrorRef cferror = NULL;
- (void)registerSyncStatusCallback: (NSString*) uuid callback: (SecBoolNSErrorCallback) callback {
// Someone is requesting future notification of this item.
@synchronized(self.pendingSyncCallbacks) {
+ secnotice("ckkscallback", "registered callback for UUID: %@", uuid);
self.pendingSyncCallbacks[uuid] = callback;
}
}
+- (SecBoolNSErrorCallback _Nullable)claimCallbackForUUID:(NSString*)uuid
+{
+ @synchronized(self.pendingSyncCallbacks) {
+ SecBoolNSErrorCallback callback = self.pendingSyncCallbacks[uuid];
+
+ if(callback) {
+ secerror("ckkscallback : fetched UUID: %@", uuid);
+ }
+
+ self.pendingSyncCallbacks[uuid] = nil;
+ return callback;
+ }
+}
+
+- (NSSet<NSString*>*)pendingCallbackUUIDs
+{
+ @synchronized(self.pendingSyncCallbacks) {
+ return [[self.pendingSyncCallbacks allKeys] copy];
+ }
+}
+
+- (void)cloudkitAccountStateChange:(CKAccountInfo* _Nullable)oldAccountInfo to:(CKAccountInfo*)currentAccountInfo
+{
+ if(currentAccountInfo.accountStatus == CKAccountStatusAvailable && currentAccountInfo.hasValidCredentials) {
+ // Account is okay!
+ } else {
+ @synchronized(self.pendingSyncCallbacks) {
+ if(self.pendingSyncCallbacks.count > 0) {
+ secnotice("ckkscallback", "No CK account; failing all pending sync callbacks");
+
+ for(NSString* uuid in [self.pendingSyncCallbacks allKeys]) {
+ [CKKSViewManager callSyncCallbackWithErrorNoAccount:self.pendingSyncCallbacks[uuid]];
+ }
+
+ [self.pendingSyncCallbacks removeAllObjects];
+ }
+ }
+ }
+}
+
++ (void)callSyncCallbackWithErrorNoAccount:(SecBoolNSErrorCallback)syncCallback
+{
+ // I don't love using this domain, but PCS depends on it
+ syncCallback(false, [NSError errorWithDomain:@"securityd"
+ code:errSecNotLoggedIn
+ description:@"No iCloud account available; item is not expected to sync"]);
+}
+
- (void) handleKeychainEventDbConnection: (SecDbConnectionRef) dbconn source:(SecDbTransactionSource)txionSource added: (SecDbItemRef) added deleted: (SecDbItemRef) deleted {
SecDbItemRef modified = added ? added : deleted;
- NSString* viewName = [self viewNameForItem: modified];
NSString* keyViewName = [CKKSKey isItemKeyForKeychainView: modified];
if(keyViewName) {
return;
}
- // When SOS is in charge of a view, CKKS is not.
- // Since this isn't a CKKS key item, we don't care about it.
- if(txionSource == kSecDbSOSTransaction) {
- secinfo("ckks", "Ignoring new non-CKKS item in kSecDbSOSTransaction notification");
+ bool addedSync = added && SecDbItemIsSyncable(added);
+ bool deletedSync = deleted && SecDbItemIsSyncable(deleted);
+
+ if(!addedSync && !deletedSync) {
+ // Local-only change. Skip with prejudice.
+ secinfo("ckks", "skipping sync of non-sync item (%d, %d)", addedSync, deletedSync);
+ return;
}
- // Looks like a normal item. Proceed!
- CKKSKeychainView* view = [self findView:viewName];
+ NSString* viewName = nil;
- NSString* uuid = (__bridge NSString*) SecDbItemGetValue(modified, &v10itemuuid, NULL);
- SecBoolNSErrorCallback syncCallback = nil;
- if(uuid) {
- @synchronized(self.pendingSyncCallbacks) {
- syncCallback = self.pendingSyncCallbacks[uuid];
- self.pendingSyncCallbacks[uuid] = nil;
+ @synchronized(self.views) {
+ if([self useCKKSViewsFromPolicy] && !self.policy) {
+ secerror("ckks: No policy configured(%@). Skipping item: %@", self.policy, modified);
+ self.itemModificationsBeforePolicyLoaded = YES;
- if(syncCallback) {
- secinfo("ckks", "Have a pending callback for %@; passing along", uuid);
- }
+ return;
}
+
+ viewName = [self viewNameForItem:modified];
+ }
+
+ if(!viewName) {
+ secnotice("ckks", "No intended CKKS view for item; skipping: %@", modified);
+ return;
}
+ // Looks like a normal item. Proceed!
+ CKKSKeychainView* view = [self findView:viewName];
+
if(!view) {
- if(viewName) {
- secnotice("ckks", "No CKKS view for %@, skipping: %@", viewName, modified);
- } else {
- secinfo("ckks", "No CKKS view for %@, skipping: %@", viewName, modified);
- }
+ secnotice("ckks", "No CKKS view for %@, skipping: %@", viewName, modified);
+
+ NSString* uuid = (__bridge NSString*) SecDbItemGetValue(modified, &v10itemuuid, NULL);
+ SecBoolNSErrorCallback syncCallback = [self claimCallbackForUUID:uuid];
+
if(syncCallback) {
- syncCallback(false, [NSError errorWithDomain:@"securityd"
- code:kSOSCCNoSuchView
+ syncCallback(false, [NSError errorWithDomain:CKKSErrorDomain
+ code:CKKSNoSuchView
userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat: @"No syncing view for '%@'", viewName]}]);
}
return;
}
ckksnotice("ckks", view, "Routing item to zone %@: %@", viewName, modified);
- [view handleKeychainEventDbConnection: dbconn added:added deleted:deleted rateLimiter:self.globalRateLimiter syncCallback: syncCallback];
+ [view handleKeychainEventDbConnection:dbconn
+ source:txionSource
+ added:added
+ deleted:deleted
+ rateLimiter:self.globalRateLimiter];
}
-(void)setCurrentItemForAccessGroup:(NSData* _Nonnull)newItemPersistentRef
complete:complete];
}
-
-+ (instancetype) manager {
- return [self resetManager: false setTo: nil];
-}
-
-+ (instancetype) resetManager: (bool) reset setTo: (CKKSViewManager*) obj {
- static CKKSViewManager* manager = nil;
-
- if([CKDatabase class] == nil) {
- secerror("CKKS: CloudKit.framework appears to not be linked. Can't create CKKS objects.");
- return nil;
- }
-
- if(!manager || reset || obj) {
- @synchronized([self class]) {
- if(obj != nil) {
- [manager clearAllViews];
- manager = obj;
- } else {
- if(reset) {
- [manager clearAllViews];
- manager = nil;
- } else if (manager == nil && SecCKKSIsEnabled()) {
- // The CKKSViewManager doesn't do much with its adapter, so leave as nonessentialq
- manager = [[CKKSViewManager alloc] initWithContainerName:SecCKKSContainerName
- usePCS:SecCKKSContainerUsePCS
- sosAdapter:[[OTSOSActualAdapter alloc] initAsEssential:NO]
- cloudKitClassDependencies:[CKKSCloudKitClassDependencies forLiveCloudKit]];
- }
- }
- }
- }
-
- return manager;
++ (instancetype)manager
+{
+ return [OTManager manager].viewManager;
}
- (void)cancelPendingOperations {
@"lockstatetracker": stringify(self.lockStateTracker),
@"cloudkitRetryAfter": stringify(self.zoneModifier.cloudkitRetryAfter),
@"lastCKKSPush": CKKSNilToNSNull(lastCKKSPush),
+ @"policy": stringify(self.policy),
+ @"viewsFromPolicy": [self useCKKSViewsFromPolicy] ? @"yes" : @"no",
};
[a addObject: global];
}
// Bring all operations down, too
[self cancelAllOperations];
+
+ // And now, wait for all operations that are running
+ for(NSOperation* op in self.operationQueue.operations) {
+ if(op.isExecuting) {
+ [op waitUntilFinished];
+ }
+ }
}
@end
#import "keychain/ckks/CloudKitCategories.h"
#import "keychain/categories/NSError+UsefulConstructors.h"
#import "keychain/ckks/tests/MockCloudKit.h"
+#import "keychain/ot/OTManager.h"
@interface CKKSCloudKitTests : XCTestCase
nsdistributednotificationCenterClass:[NSDistributedNotificationCenter class]
notifierClass:[FakeCKKSNotifier class]];
- CKKSViewManager* manager = [[CKKSViewManager alloc] initWithContainerName:containerName
- usePCS:SecCKKSContainerUsePCS
- sosAdapter:nil
- cloudKitClassDependencies:cloudKitClassDependencies];
- [CKKSViewManager resetManager:false setTo:manager];
+ CKContainer* container = [CKKSViewManager makeCKContainer:SecCKKSContainerName usePCS:SecCKKSContainerUsePCS];
+ CKKSAccountStateTracker* accountStateTracker = [[CKKSAccountStateTracker alloc] init:container
+ nsnotificationCenterClass:cloudKitClassDependencies.nsnotificationCenterClass];
+
+ CKKSLockStateTracker* lockStateTracker = [[CKKSLockStateTracker alloc] init];
+
+ CKKSViewManager* manager = [[CKKSViewManager alloc] initWithContainer:container
+ sosAdapter:nil
+ accountStateTracker:accountStateTracker
+ lockStateTracker:lockStateTracker
+ cloudKitClassDependencies:cloudKitClassDependencies];
+ // No longer a supported mechanism:
+ //[CKKSViewManager resetManager:false setTo:manager];
+ (void)manager;
// Make a new fake keychain
NSString* smallName = [self.name componentsSeparatedByString:@" "][1];
+++ /dev/null
-/*
- * Copyright (c) 2017 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <dispatch/dispatch.h>
-#import <XCTest/XCTest.h>
-#import "keychain/ckks/CKKSNearFutureScheduler.h"
-
-@interface CKKSNearFutureSchedulerTests : XCTestCase
-
-@end
-
-@implementation CKKSNearFutureSchedulerTests
-
-- (void)setUp {
- [super setUp];
-}
-
-- (void)tearDown {
- [super tearDown];
-}
-
-- (void)testOneShot {
- XCTestExpectation *expectation = [self expectationWithDescription:@"FutureScheduler fired"];
-
- CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay:50*NSEC_PER_MSEC keepProcessAlive:true block:^{
- [expectation fulfill];
- }];
-
- [scheduler trigger];
-
- [self waitForExpectationsWithTimeout:1 handler:nil];
-}
-
-- (void)testOneShotDelay {
- XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
- toofastexpectation.inverted = YES;
-
- XCTestExpectation *expectation = [self expectationWithDescription:@"FutureScheduler fired"];
-
- CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 200*NSEC_PER_MSEC keepProcessAlive:false block:^{
- [toofastexpectation fulfill];
- [expectation fulfill];
- }];
-
- [scheduler trigger];
-
- // Make sure it waits at least 0.1 seconds
- [self waitForExpectations: @[toofastexpectation] timeout:0.1];
-
- // But finishes within 1.1s (total)
- [self waitForExpectations: @[expectation] timeout:1];
-}
-
-- (void)testOneShotManyTrigger {
- XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
- toofastexpectation.inverted = YES;
-
- XCTestExpectation *expectation = [self expectationWithDescription:@"FutureScheduler fired"];
- expectation.assertForOverFulfill = YES;
-
- CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 200*NSEC_PER_MSEC keepProcessAlive:true block:^{
- [toofastexpectation fulfill];
- [expectation fulfill];
- }];
-
- [scheduler trigger];
- [scheduler trigger];
- [scheduler trigger];
- [scheduler trigger];
- [scheduler trigger];
- [scheduler trigger];
- [scheduler trigger];
- [scheduler trigger];
-
- // Make sure it waits at least 0.1 seconds
- [self waitForExpectations: @[toofastexpectation] timeout:0.1];
-
- // But finishes within .6s (total)
- [self waitForExpectations: @[expectation] timeout:0.5];
-
- // Ensure we don't get called again in the next 0.3 s
- XCTestExpectation* waitmore = [self expectationWithDescription:@"waiting"];
- waitmore.inverted = YES;
- [self waitForExpectations: @[waitmore] timeout: 0.3];
-}
-
-
-- (void)testMultiShot {
- XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
- first.assertForOverFulfill = NO;
-
- XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
- second.expectedFulfillmentCount = 2;
- second.assertForOverFulfill = YES;
-
- CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 100*NSEC_PER_MSEC keepProcessAlive:false block:^{
- [first fulfill];
- [second fulfill];
- }];
-
- [scheduler trigger];
-
- [self waitForExpectations: @[first] timeout:0.2];
-
- [scheduler trigger];
- [scheduler trigger];
- [scheduler trigger];
-
- [self waitForExpectations: @[second] timeout:0.2];
-
- XCTestExpectation* waitmore = [self expectationWithDescription:@"waiting"];
- waitmore.inverted = YES;
- [self waitForExpectations: @[waitmore] timeout: 0.2];
-}
-
-- (void)testMultiShotDelays {
- XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
- first.assertForOverFulfill = NO;
-
- XCTestExpectation *longdelay = [self expectationWithDescription:@"FutureScheduler fired (long delay expectation)"];
- longdelay.inverted = YES;
- longdelay.expectedFulfillmentCount = 2;
-
- XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
- second.expectedFulfillmentCount = 2;
- second.assertForOverFulfill = YES;
-
- CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" initialDelay: 50*NSEC_PER_MSEC continuingDelay:300*NSEC_PER_MSEC keepProcessAlive:false block:^{
- [first fulfill];
- [longdelay fulfill];
- [second fulfill];
- }];
-
- [scheduler trigger];
-
- [self waitForExpectations: @[first] timeout:0.2];
-
- [scheduler trigger];
- [scheduler trigger];
- [scheduler trigger];
-
- // longdelay should NOT be fulfilled twice in the first 0.3 seconds
- [self waitForExpectations: @[longdelay] timeout:0.2];
-
- // But second should be fulfilled in the first 0.8 seconds
- [self waitForExpectations: @[second] timeout:0.5];
-
- XCTestExpectation* waitmore = [self expectationWithDescription:@"waiting"];
- waitmore.inverted = YES;
- [self waitForExpectations: @[waitmore] timeout: 0.2];
-}
-
-- (void)testCancel {
- XCTestExpectation *cancelexpectation = [self expectationWithDescription:@"FutureScheduler fired (after cancel)"];
- cancelexpectation.inverted = YES;
-
- CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 100*NSEC_PER_MSEC keepProcessAlive:true block:^{
- [cancelexpectation fulfill];
- }];
-
- [scheduler trigger];
- [scheduler cancel];
-
- // Make sure it does not fire in 0.5 s
- [self waitForExpectations: @[cancelexpectation] timeout:0.2];
-}
-
-- (void)testDelayedNoShot {
- XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
- toofastexpectation.inverted = YES;
-
- CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 10*NSEC_PER_MSEC keepProcessAlive:false block:^{
- [toofastexpectation fulfill];
- }];
-
- // Tell the scheduler to wait, but don't trigger it. It shouldn't fire.
- [scheduler waitUntil: 50*NSEC_PER_MSEC];
-
- [self waitForExpectations: @[toofastexpectation] timeout:0.1];
-}
-
-- (void)testDelayedOneShot {
- XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
- first.assertForOverFulfill = NO;
-
- XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
- toofastexpectation.inverted = YES;
-
- CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 10*NSEC_PER_MSEC keepProcessAlive:false block:^{
- [first fulfill];
- [toofastexpectation fulfill];
- }];
-
- [scheduler waitUntil: 150*NSEC_PER_MSEC];
- [scheduler trigger];
-
- [self waitForExpectations: @[toofastexpectation] timeout:0.1];
- [self waitForExpectations: @[first] timeout:0.2];
-}
-
-- (void)testDelayedMultiShot {
- XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
- first.assertForOverFulfill = NO;
-
- XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
- toofastexpectation.expectedFulfillmentCount = 2;
- toofastexpectation.inverted = YES;
-
- XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
- second.expectedFulfillmentCount = 2;
- second.assertForOverFulfill = YES;
-
- CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 10*NSEC_PER_MSEC keepProcessAlive:false block:^{
- [first fulfill];
- [second fulfill];
- [toofastexpectation fulfill];
- }];
-
- [scheduler trigger];
- [self waitForExpectations: @[first] timeout:0.2];
-
- [scheduler waitUntil: 150*NSEC_PER_MSEC];
- [scheduler trigger];
-
- [self waitForExpectations: @[toofastexpectation] timeout:0.1];
- [self waitForExpectations: @[second] timeout:0.3];
-}
-
-@end
@property bool excludeSelfPeerFromTrustSet;
@property SOSCCStatus circleStatus;
+@property (nullable) NSError* circleStatusError;
+
@property CKKSSOSSelfPeer* selfPeer;
@property NSMutableSet<id<CKKSSOSPeerProtocol>>* trustedPeers;
{
if(!self.sosEnabled || self.circleStatus == kSOSCCError) {
if(error && self.circleStatus == kSOSCCError) {
- *error = [NSError errorWithDomain:(__bridge NSString*)kSOSErrorDomain code:self.circleStatus userInfo:nil];
+ // I'm not at all sure that the second error here actually is any error in particular
+ *error = self.circleStatusError ?: [NSError errorWithDomain:(__bridge NSString*)kSOSErrorDomain code:self.circleStatus userInfo:nil];
}
return kSOSCCError;
}
XCTAssertEqual (zse.ckzonesubscribed, loaded.ckzonesubscribed, "ckzonesubscribed persisted through db save and load");
XCTAssertEqualObjects(zse.encodedChangeToken, loaded.encodedChangeToken, "encodedChangeToken persisted through db save and load");
- XCTAssert([[NSCalendar currentCalendar] isDate:zse.lastFetchTime equalToDate: loaded.lastFetchTime toUnitGranularity:NSCalendarUnitSecond],
+ secnotice("ckkstests", "zse.lastFetchTime: %@", zse.lastFetchTime);
+ secnotice("ckkstests", "loaded.lastFetchTime: %@", loaded.lastFetchTime);
+
+ secnotice("ckkstests", "equal?: %d", [zse.lastFetchTime isEqualToDate:loaded.lastFetchTime]);
+ secnotice("ckkstests", "equal to seconds?: %d", [[NSCalendar currentCalendar] isDate:zse.lastFetchTime equalToDate: loaded.lastFetchTime toUnitGranularity:NSCalendarUnitSecond]);
+
+ // We only compare to the minute level, as that's enough to test the save+load.
+ XCTAssert([[NSCalendar currentCalendar] isDate:zse.lastFetchTime equalToDate: loaded.lastFetchTime toUnitGranularity:NSCalendarUnitMinute],
"lastFetchTime persisted through db save and load");
}
XCTAssertTrue([wrappedKey saveToDatabase: &error], "key saved to database");
XCTAssertNil(error, "no error saving key to database");
+ NSString* secondUUID = @"8b2aeb7f-0000-0000-0000-70d5c728ebf7";
+ CKKSKey* secondtlk = [[CKKSKey alloc] initSelfWrappedWithAESKey:[[CKKSAESSIVKey alloc] initWithBase64: @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="]
+ uuid:secondUUID
+ keyclass:SecCKKSKeyClassTLK
+ state:SecCKKSProcessedStateLocal
+ zoneID:self.testZoneID
+ encodedCKRecord:testCKRecord
+ currentkey:true];
+ XCTAssertTrue([secondtlk saveToDatabase: &error], "Second TLK saved to database");
+ XCTAssertNil(error, "no error saving TLK to database");
+
NSArray<CKKSKey*>* tlks = [CKKSKey allWhere: @{@"UUID": @"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"} error: &error];
XCTAssertNotNil(tlks, "Returned some array from allWhere");
XCTAssertNil(error, "no error back from allWhere");
XCTAssertEqual([tlks count], 1ul, "Received one row (and expected one row)");
- NSArray<CKKSKey*>* selfWrapped = [CKKSKey allWhere: @{@"parentKeyUUID": [CKKSSQLWhereObject op:@"=" string:@"uuid"]} error: &error];
+ NSArray<CKKSKey*>* selfWrapped = [CKKSKey allWhere: @{@"parentKeyUUID": [CKKSSQLWhereColumn op:CKKSSQLWhereComparatorEquals column:CKKSSQLWhereColumnNameUUID]} error: &error];
XCTAssertNotNil(selfWrapped, "Returned some array from allWhere");
XCTAssertNil(error, "no error back from allWhere");
- XCTAssertEqual([selfWrapped count], 1ul, "Received one row (and expected one row)");
+ XCTAssertEqual([selfWrapped count], 2ul, "Should have recievied two rows");
- // Try using CKKSSQLWhereObject alongside normal binds
- NSArray<CKKSKey*>* selfWrapped2 = [CKKSKey allWhere: @{@"parentKeyUUID": [CKKSSQLWhereObject op:@"=" string:@"uuid"],
+ // Try using CKKSSQLWhereColumn alongside normal binds
+ NSArray<CKKSKey*>* selfWrapped2 = [CKKSKey allWhere: @{@"parentKeyUUID": [CKKSSQLWhereColumn op:CKKSSQLWhereComparatorEquals column:CKKSSQLWhereColumnNameUUID],
@"uuid": @"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"}
error: &error];
XCTAssertNotNil(selfWrapped2, "Returned some array from allWhere");
XCTAssertNil(error, "no error back from allWhere");
XCTAssertEqual([selfWrapped2 count], 1ul, "Received one row (and expected one row)");
+ XCTAssertEqualObjects([selfWrapped2[0] uuid], @"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7", "Should received first TLK UUID");
+
+ NSArray<CKKSKey*>* selfWrapped3 = [CKKSKey allWhere: @{@"parentKeyUUID": [CKKSSQLWhereColumn op:CKKSSQLWhereComparatorEquals column:CKKSSQLWhereColumnNameUUID],
+ @"uuid": [CKKSSQLWhereValue op:CKKSSQLWhereComparatorNotEquals value:@"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"]}
+ error: &error];
+ XCTAssertNotNil(selfWrapped3, "Returned some array from allWhere");
+ XCTAssertNil(error, "no error back from allWhere");
+ XCTAssertEqual([selfWrapped3 count], 1ul, "Should have received one rows");
+ XCTAssertEqualObjects([selfWrapped3[0] uuid], secondUUID, "Should received second TLK UUID");
}
- (void)testGroupBy {
while(count == 0 || uuid != nil) {
uuid = nil;
[CKKSSQLDatabaseObject queryDatabaseTable: [CKKSOutgoingQueueEntry sqlTable]
- where: lastUUID ? @{@"UUID": [CKKSSQLWhereObject op:@">" stringValue:lastUUID]} : nil
+ where: lastUUID ? @{@"UUID": [CKKSSQLWhereValue op:CKKSSQLWhereComparatorGreaterThan value:lastUUID]} : nil
columns: @[@"action", @"UUID"]
groupBy:nil
orderBy:@[@"uuid"]
(id)kSecAttrAccount : account,
(id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
(id)kSecValueData : data,
+ (id)kSecAttrSyncViewHint : self.keychainView.zoneName,
(id)kSecAttrDeriveSyncIDFromItemAttributes : (id)kCFBooleanTrue,
(id)kSecAttrPCSPlaintextServiceIdentifier : serviceIdentifier,
(id)kSecAttrPCSPlaintextPublicKey : publicKey,
(id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
(id)kSecAttrAccount : @"testaccount",
(id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
+ (id)kSecAttrSyncViewHint : self.keychainView.zoneName,
(id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
} mutableCopy];
[blockExpectation fulfill];
}), @"_SecItemAddAndNotifyOnSync succeeded");
+ OCMVerifyAllWithDelay(self.mockDatabase, 10);
+
[self waitForExpectationsWithTimeout:5.0 handler:nil];
}
(id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
(id)kSecAttrAccount : @"testaccount",
(id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
+ (id)kSecAttrSyncViewHint : self.keychainView.zoneName,
(id)kSecAttrPCSPlaintextPublicKey : [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
(id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
} mutableCopy];
(id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
(id)kSecAttrAccount : @"testaccount",
(id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
+ (id)kSecAttrSyncViewHint : self.keychainView.zoneName,
(id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
(id)kSecAttrSyncViewHint : self.keychainView.zoneName, // @ fake view hint for fake view
} mutableCopy];
(id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
(id)kSecAttrAccount : @"testaccount",
(id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
+ (id)kSecAttrSyncViewHint : self.keychainView.zoneName,
(id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
} mutableCopy];
(id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
(id)kSecAttrAccount : @"testaccount",
(id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
+ (id)kSecAttrSyncViewHint : self.keychainView.zoneName,
(id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
} mutableCopy];
[self waitForExpectationsWithTimeout:5.0 handler:nil];
}
+- (void)testAddAndNotifyOnSyncBeforePolicyLoaded {
+ [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+ [self saveTLKMaterialToKeychain:self.keychainZoneID];
+ [self expectCKKSTLKSelfShareUpload:self.keychainZoneID];
+
+ [self startCKKSSubsystem];
+
+ // Allow CKKS to spin up fully
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:20*NSEC_PER_SEC], "CKKS entered ready");
+
+ [self.keychainView waitForOperationsOfClass:[CKKSScanLocalItemsOperation class]];
+ [self.keychainView waitForOperationsOfClass:[CKKSOutgoingQueueOperation class]];
+
+ // Simulate a daemon restart
+ self.automaticallyBeginCKKSViewCloudKitOperation = false;
+ [self.injectedManager resetSyncingPolicy];
+ [self.injectedManager haltZone:self.keychainZoneID.zoneName];
+
+ // Issue the query (to simulate the query starting securityd)
+ NSMutableDictionary* query = [@{
+ (id)kSecClass : (id)kSecClassGenericPassword,
+ (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
+ (id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
+ (id)kSecAttrAccount : @"testaccount",
+ (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
+ (id)kSecAttrSyncViewHint : self.keychainView.zoneName,
+ (id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
+ } mutableCopy];
+
+ XCTestExpectation* blockExpectation = [self expectationWithDescription: @"callback occurs"];
+
+ XCTAssertEqual(errSecSuccess, _SecItemAddAndNotifyOnSync((__bridge CFDictionaryRef) query, NULL, ^(bool didSync, CFErrorRef error) {
+ XCTAssertTrue(didSync, "Item synced");
+ XCTAssertNil((__bridge NSError*)error, "Shouldn't have received an error syncing item");
+
+ [blockExpectation fulfill];
+ }), @"_SecItemAddAndNotifyOnSync succeeded");
+
+ // When the policy is loaded, the item should upload and the callback should fire
+ [self expectCKModifyItemRecords:1 currentKeyPointerRecords:1 zoneID:self.keychainZoneID];
+
+ [self.injectedManager setSyncingViews:self.managedViewList sortingPolicy:self.viewSortingPolicyForManagedViewList];
+ self.keychainView = [self.injectedManager findView:self.keychainZoneID.zoneName];
+
+ [self.injectedManager beginCloudKitOperationOfAllViews];
+ [self beginSOSTrustedViewOperation:self.keychainView];
+
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:20*NSEC_PER_SEC], @"Should have reached key state 'ready'");
+ OCMVerifyAllWithDelay(self.mockDatabase, 20);
+
+ [self waitForExpectations:@[blockExpectation] timeout:5];
+}
+
- (void)testPCSUnencryptedFieldsAdd {
[self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
// Check that the record is where we expect it in CloudKit
[self waitForCKModifications];
- CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
+ CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID];
CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
// Check that the record is where we expect it
[self waitForCKModifications];
- CKRecordID* pcsOtherItemRecordID = [[CKRecordID alloc] initWithRecordName: @"878BEAA6-1EE9-1079-1025-E6832AC8F2F3" zoneID:self.keychainZoneID];
+ CKRecordID* pcsOtherItemRecordID = [[CKRecordID alloc] initWithRecordName: @"2DEA6136-2505-6BFD-E3E8-B44A6E39C3B5" zoneID:self.keychainZoneID];
CKRecord* recordOther = self.keychainZone.currentDatabase[pcsOtherItemRecordID];
XCTAssertNotNil(recordOther, "Found other record in CloudKit at expected UUID");
// Check that the record is where we expect it in CloudKit
[self waitForCKModifications];
- CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
+ CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID];
CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
// Check that the records are where we expect them in CloudKit
[self waitForCKModifications];
- CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
+ CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID];
CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
- CKRecordID* pcsItemRecordID2 = [[CKRecordID alloc] initWithRecordName: @"3AB8E78D-75AF-CFEF-F833-FA3E3E90978A" zoneID:self.keychainZoneID];
+ CKRecordID* pcsItemRecordID2 = [[CKRecordID alloc] initWithRecordName: @"10E76B80-CE1C-A52A-B0CB-462A2EBA05AF" zoneID:self.keychainZoneID];
CKRecord* record2 = self.keychainZone.currentDatabase[pcsItemRecordID2];
XCTAssertNotNil(record2, "Found 2nd record in CloudKit at expected UUID");
// Another machine comes along and updates the pointer!
CKKSCurrentItemPointer* cip = [[CKKSCurrentItemPointer alloc] initForIdentifier:@"com.apple.security.ckks-pcsservice"
- currentItemUUID:@"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3"
+ currentItemUUID:@"50184A35-4480-E8BA-769B-567CF72F1EC0"
state:SecCKKSProcessedStateRemote
zoneID:self.keychainZoneID
encodedCKRecord:nil];
// Check that the record is where we expect it in CloudKit
[self waitForCKModifications];
- CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
+ CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID];
CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
// Another machine comes along and updates the pointer!
CKKSCurrentItemPointer* cip = [[CKKSCurrentItemPointer alloc] initForIdentifier:@"com.apple.security.ckks-pcsservice"
- currentItemUUID:@"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3"
+ currentItemUUID:@"50184A35-4480-E8BA-769B-567CF72F1EC0"
state:SecCKKSProcessedStateRemote
zoneID:self.keychainZoneID
encodedCKRecord:nil];
// Check that the record is where we expect it in CloudKit
[self waitForCKModifications];
- NSString* recordUUID = @"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3";
+ NSString* recordUUID = @"50184A35-4480-E8BA-769B-567CF72F1EC0";
CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName:recordUUID zoneID:self.keychainZoneID];
CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
// Check that the record is where we expect it in CloudKit
[self waitForCKModifications];
- CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
+ CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID];
CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
// Check that the number is on the CKKSMirrorEntry
[self.keychainView dispatchSync: ^bool {
NSError* error = nil;
- CKKSMirrorEntry* ckme = [CKKSMirrorEntry fromDatabase:@"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID error:&error];
+ CKKSMirrorEntry* ckme = [CKKSMirrorEntry fromDatabase:@"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID error:&error];
XCTAssertNil(error, "no error fetching ckme");
XCTAssertNotNil(ckme, "Received a ckme");
// Check that the record is where we expect it in CloudKit
[self waitForCKModifications];
- CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
+ CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID];
CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
// Check that the record is where we expect it in CloudKit
[self waitForCKModifications];
- CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
+ CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID];
CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
XCTAssertNil(error, "Error should be nil parsing base64 item");
item[@"v_Data"] = [@"conflictingdata" dataUsingEncoding:NSUTF8StringEncoding];
- CKRecordID* ckrid = [[CKRecordID alloc] initWithRecordName:@"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
+ item[@"vwht"] = @"keychain";
+ CKRecordID* ckrid = [[CKRecordID alloc] initWithRecordName:@"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID];
CKRecord* mismatchedRecord = [self newRecord:ckrid withNewItemData:item];
[self.keychainZone addToZone: mismatchedRecord];
@property FakeCKZone* limitedZone;
@property (readonly) ZoneKeys* limitedZoneKeys;
+@property CKRecordZoneID* passwordsZoneID;
+@property CKKSKeychainView* passwordsView;
+@property FakeCKZone* passwordsZone;
+@property (readonly) ZoneKeys* passwordsZoneKeys;
+
- (void)saveFakeKeyHierarchiesToLocalDatabase;
- (void)putFakeDeviceStatusesInCloudKit;
- (void)putFakeKeyHierachiesInCloudKit;
#import <notify.h>
#include <Security/SecItemPriv.h>
+#import <TrustedPeers/TrustedPeers.h>
+#import <TrustedPeers/TPPBPolicyKeyViewMapping.h>
+#import <TrustedPeers/TPDictionaryMatchingRules.h>
#import "keychain/ckks/tests/CloudKitMockXCTest.h"
#import "keychain/ckks/tests/CloudKitKeychainSyncingMockXCTest.h"
#import "keychain/ckks/CKKSZoneStateEntry.h"
#import "keychain/ckks/CKKSManifest.h"
+#import "keychain/ckks/tests/CKKSTests+MultiZone.h"
#import "keychain/ckks/tests/MockCloudKit.h"
#pragma clang diagnostic push
#include <Security/SecKeyPriv.h>
#pragma clang diagnostic pop
-@interface CloudKitKeychainSyncingMultiZoneTestsBase : CloudKitKeychainSyncingMockXCTest
-
-@property CKRecordZoneID* engramZoneID;
-@property CKKSKeychainView* engramView;
-@property FakeCKZone* engramZone;
-@property (readonly) ZoneKeys* engramZoneKeys;
+@interface CloudKitKeychainSyncingMultiZoneTestsBase ()
+@end
-@property CKRecordZoneID* manateeZoneID;
-@property CKKSKeychainView* manateeView;
-@property FakeCKZone* manateeZone;
-@property (readonly) ZoneKeys* manateeZoneKeys;
+@implementation CloudKitKeychainSyncingMultiZoneTestsBase
++ (void)setUp {
+ SecCKKSEnable();
+ SecCKKSResetSyncing();
+ [super setUp];
+}
-@property CKRecordZoneID* autoUnlockZoneID;
-@property CKKSKeychainView* autoUnlockView;
-@property FakeCKZone* autoUnlockZone;
-@property (readonly) ZoneKeys* autoUnlockZoneKeys;
+- (NSSet*)managedViewList {
+ NSMutableSet* parentSet = [[super managedViewList] mutableCopy];
+ [parentSet addObject:@"SafariPasswords"];
+ return parentSet;
+}
-@property CKRecordZoneID* healthZoneID;
-@property CKKSKeychainView* healthView;
-@property FakeCKZone* healthZone;
-@property (readonly) ZoneKeys* healthZoneKeys;
+// Make a policy as normal for most views, but Passwords is special
+- (TPPolicy*)viewSortingPolicyForManagedViewList
+{
+ NSMutableArray<TPPBPolicyKeyViewMapping*>* rules = [NSMutableArray array];
-@property CKRecordZoneID* applepayZoneID;
-@property CKKSKeychainView* applepayView;
-@property FakeCKZone* applepayZone;
-@property (readonly) ZoneKeys* applepayZoneKeys;
+ for(NSString* viewName in self.managedViewList) {
+ TPPBPolicyKeyViewMapping* mapping = [[TPPBPolicyKeyViewMapping alloc] init];
+ mapping.view = viewName;
-@property CKRecordZoneID* homeZoneID;
-@property CKKSKeychainView* homeView;
-@property FakeCKZone* homeZone;
-@property (readonly) ZoneKeys* homeZoneKeys;
+ // The real passwords view is on com.appple.cfnetwork, but for these tests, let's just use the sbd agrp (because of how the entitlements are specified)
+ if([viewName isEqualToString:@"SafariPasswords"]) {
+ mapping.matchingRule = [TPDictionaryMatchingRule fieldMatch:@"agrp"
+ fieldRegex:[NSString stringWithFormat:@"^com\\.apple\\.sbd$"]];
+ } else {
+ mapping.matchingRule = [TPDictionaryMatchingRule fieldMatch:@"vwht"
+ fieldRegex:[NSString stringWithFormat:@"^%@$", viewName]];
+ }
-@property CKRecordZoneID* limitedZoneID;
-@property CKKSKeychainView* limitedView;
-@property FakeCKZone* limitedZone;
-@property (readonly) ZoneKeys* limitedZoneKeys;
+ [rules addObject:mapping];
+ }
-@end
+ TPPolicy* policy = [TPPolicy policyWithModelToCategory:@[]
+ categoriesByView:@{}
+ introducersByCategory:@{}
+ keyViewMapping:rules
+ unknownRedactions:NO
+ version:[[TPPolicyVersion alloc] initWithVersion:1 hash:@"fake-policy-for-views"]];
-@implementation CloudKitKeychainSyncingMultiZoneTestsBase
-+ (void)setUp {
- SecCKKSEnable();
- SecCKKSResetSyncing();
- [super setUp];
+ return policy;
}
- (void)setUp {
XCTAssertNotNil(self.limitedView, "CKKSViewManager created the LimitedPeersAllowed view");
[self.ckksViews addObject:self.limitedView];
[self.ckksZones addObject:self.limitedZoneID];
+
+ self.passwordsZoneID = [[CKRecordZoneID alloc] initWithZoneName:@"SafariPasswords" ownerName:CKCurrentUserDefaultName];
+ self.passwordsZone = [[FakeCKZone alloc] initZone: self.passwordsZoneID];
+ self.zones[self.passwordsZoneID] = self.passwordsZone;
+ self.passwordsView = [[CKKSViewManager manager] findOrCreateView:@"SafariPasswords"];
+ XCTAssertNotNil(self.passwordsView, "should have a passwords ckks view");
+ XCTAssertNotNil(self.passwordsView, "CKKSViewManager created the Passwords view");
+ [self.ckksViews addObject:self.passwordsView];
+ [self.ckksZones addObject:self.passwordsZoneID];
+
+ // These tests, at least, will use the policy codepaths!
+ [self.injectedManager setOverrideCKKSViewsFromPolicy:YES];
+ [self.injectedManager setSyncingViews:self.managedViewList sortingPolicy:self.viewSortingPolicyForManagedViewList];
}
+ (void)tearDown {
[self.homeView waitUntilAllOperationsAreFinished];
self.homeView = nil;
+ [self.limitedView halt];
+ [self.limitedView waitUntilAllOperationsAreFinished];
+ self.limitedView = nil;
+
+ [self.passwordsView halt];
+ [self.passwordsView waitUntilAllOperationsAreFinished];
+ self.passwordsView = nil;
+
[super tearDown];
}
[self putFakeDeviceStatusInCloudKit: self.applepayZoneID];
[self putFakeDeviceStatusInCloudKit: self.homeZoneID];
[self putFakeDeviceStatusInCloudKit: self.limitedZoneID];
+ [self putFakeDeviceStatusInCloudKit: self.passwordsZoneID];
}
- (void)putFakeKeyHierachiesInCloudKit{
[self putFakeKeyHierarchyInCloudKit: self.applepayZoneID];
[self putFakeKeyHierarchyInCloudKit: self.homeZoneID];
[self putFakeKeyHierarchyInCloudKit: self.limitedZoneID];
+ [self putFakeKeyHierarchyInCloudKit: self.passwordsZoneID];
}
- (void)saveTLKsToKeychain{
[self saveTLKMaterialToKeychain:self.applepayZoneID];
[self saveTLKMaterialToKeychain:self.homeZoneID];
[self saveTLKMaterialToKeychain:self.limitedZoneID];
+ [self saveTLKMaterialToKeychain:self.passwordsZoneID];
}
- (void)deleteTLKMaterialsFromKeychain{
[self deleteTLKMaterialFromKeychain: self.applepayZoneID];
[self deleteTLKMaterialFromKeychain: self.homeZoneID];
[self deleteTLKMaterialFromKeychain:self.limitedZoneID];
+ [self deleteTLKMaterialFromKeychain:self.passwordsZoneID];
}
- (void)waitForKeyHierarchyReadinesses {
[self.applepayView waitForKeyHierarchyReadiness];
[self.homeView waitForKeyHierarchyReadiness];
[self.limitedView waitForKeyHierarchyReadiness];
+ [self.passwordsView waitForKeyHierarchyReadiness];
}
- (void)expectCKKSTLKSelfShareUploads {
[self waitForExpectations:@[pcsChanged] timeout:0.2];
}
+- (void)testMultipleZoneAdd {
+ [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
+
+ // Let the horses loose
+ [self startCKKSSubsystem];
+
+ // We expect a single record to be uploaded to the 'LimitedPeersAllowed' view
+ [self expectCKModifyItemRecords:1 currentKeyPointerRecords:1 zoneID:self.limitedZoneID];
+ [self addGenericPassword: @"data" account: @"account-delete-me-limited-peers" viewHint:(NSString*)kSecAttrViewHintLimitedPeersAllowed];
+ OCMVerifyAllWithDelay(self.mockDatabase, 20);
+
+ // We expect a single record to be uploaded to the 'atv' home
+ [self expectCKModifyItemRecords:1 currentKeyPointerRecords:1 zoneID:self.homeZoneID];
+ [self addGenericPassword: @"data" account: @"account-delete-me-home" viewHint:(NSString*)kSecAttrViewHintHome];
+
+ OCMVerifyAllWithDelay(self.mockDatabase, 20);
+ OCMVerifyAllWithDelay(self.mockCKKSViewManager, 10);
+}
+
+- (void)testMultipleZoneDelete {
+ [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
+
+ [self startCKKSSubsystem];
+
+ // We expect a single record to be uploaded.
+ [self expectCKModifyItemRecords:1 currentKeyPointerRecords:1 zoneID:self.limitedZoneID];
+ [self addGenericPassword: @"data" account: @"account-delete-me-limited-peers" viewHint:(NSString*)kSecAttrViewHintLimitedPeersAllowed];
+ OCMVerifyAllWithDelay(self.mockDatabase, 20);
+
+ [self expectCKModifyItemRecords:1 currentKeyPointerRecords:1 zoneID:self.homeZoneID];
+ [self addGenericPassword: @"data" account: @"account-delete-me-home" viewHint:(NSString*)kSecAttrViewHintHome];
+ OCMVerifyAllWithDelay(self.mockDatabase, 20);
+ [self waitForCKModifications];
+
+ // We expect a single record to be deleted from the ATV zone
+ [self expectCKDeleteItemRecords:1 zoneID:self.homeZoneID];
+ [self deleteGenericPassword:@"account-delete-me-home"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 20);
+
+ // Now we expect a single record to be deleted from the test zone
+ [self expectCKDeleteItemRecords:1 zoneID:self.limitedZoneID];
+ [self deleteGenericPassword:@"account-delete-me-limited-peers"];
+ OCMVerifyAllWithDelay(self.mockDatabase, 20);
+}
+
+- (void)testAddAndReceiveDeleteForSafariPasswordItem {
+ [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
+
+ [self startCKKSSubsystem];
+
+ XCTestExpectation* passwordChanged = [self expectChangeForView:self.passwordsView.zoneName];
+
+ // We expect a single record to be uploaded to the Passwords view.
+ [self expectCKModifyItemRecords:1 currentKeyPointerRecords:1 zoneID:self.passwordsZoneID];
+
+ [self addGenericPassword:@"data"
+ account:@"account-delete-me"
+ access:(id)kSecAttrAccessibleWhenUnlocked
+ viewHint:nil
+ accessGroup:@"com.apple.sbd"
+ expecting:errSecSuccess
+ message:@"Item for Password view"];
+
+ OCMVerifyAllWithDelay(self.mockDatabase, 20);
+ [self waitForExpectations:@[passwordChanged] timeout:1];
+ [self waitForCKModifications];
+
+ [self waitForKeyHierarchyReadinesses];
+ [self.passwordsView waitForOperationsOfClass:[CKKSIncomingQueueOperation class]];
+
+ // Now, the item is deleted. Do we properly remove it?
+ CKRecord* itemRecord = nil;
+ for(CKRecord* record in [self.passwordsZone.currentDatabase allValues]) {
+ if([record.recordType isEqualToString:SecCKRecordItemType]) {
+ itemRecord = record;
+ break;
+ }
+ }
+ XCTAssertNotNil(itemRecord, "Should have found the item in the password zone");
+
+ NSDictionary *query = @{(id)kSecClass : (id)kSecClassGenericPassword,
+ (id)kSecAttrAccessGroup : @"com.apple.sbd",
+ (id)kSecAttrAccount : @"account-delete-me",
+ (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
+ (id)kSecMatchLimit : (id)kSecMatchLimitOne,
+ (id)kSecReturnData : (id)kCFBooleanTrue,
+ };
+
+ CFTypeRef item = NULL;
+ XCTAssertEqual(errSecSuccess, SecItemCopyMatching((__bridge CFDictionaryRef) query, &item), "item should still exist");
+ XCTAssertNotNil((__bridge id)item, "No item should have been found");
+ CFReleaseNull(item);
+
+ // Now, the item is deleted. The passwords view should delete the local item, even though it has the wrong 'vwht' on disk.
+ [self.passwordsZone deleteCKRecordIDFromZone:itemRecord.recordID];
+ [self.passwordsView notifyZoneChange:nil];
+ [self.passwordsView waitForFetchAndIncomingQueueProcessing];
+
+ XCTAssertEqual(errSecItemNotFound, SecItemCopyMatching((__bridge CFDictionaryRef) query, &item), "item should no longer exist");
+ XCTAssertNil((__bridge id)item, "No item should have been found");
+}
+
- (void)testAddOtherViewHintItem {
[self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
// We expect no uploads to CKKS.
[self addGenericPassword: @"data" account: @"account-delete-me-no-viewhint"];
- [self addGenericPassword: @"data" account: @"account-delete-me-password" viewHint:(NSString*) kSOSViewAutofillPasswords];
+ [self addGenericPassword: @"data" account: @"account-delete-me-password" viewHint:(NSString*) kSOSViewBackupBagV0];
sleep(1);
OCMVerifyAllWithDelay(self.mockDatabase, 20);
}
+- (void)testUploadItemsAddedBeforeStart {
+ [self addGenericPassword:@"data"
+ account:@"account-delete-me"
+ access:(id)kSecAttrAccessibleAfterFirstUnlock
+ viewHint:nil
+ accessGroup:@"com.apple.sbd"
+ expecting:errSecSuccess
+ message:@"Item for Password view"];
+
+ [self addGenericPassword:@"data"
+ account:@"account-delete-me-2"
+ access:(id)kSecAttrAccessibleAfterFirstUnlock
+ viewHint:nil
+ accessGroup:@"com.apple.sbd"
+ expecting:errSecSuccess
+ message:@"Item for Password view"];
+
+ [self addGenericPassword:@"data" account:@"account-delete-me-limited-peers" viewHint:(NSString*)kSecAttrViewHintLimitedPeersAllowed];
+
+ NSError* error = nil;
+ NSDictionary* currentOQEs = [CKKSOutgoingQueueEntry countsByStateInZone:self.passwordsZoneID error:&error];
+ XCTAssertNil(error, "Should be no error counting OQEs");
+ XCTAssertEqual(0, currentOQEs.count, "Should be no OQEs");
+
+ [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
+
+ // Now CKKS starts up
+ // Upon sign in, these items should be uploaded
+ [self expectCKModifyItemRecords:2 currentKeyPointerRecords:1 zoneID:self.passwordsZoneID
+ checkItem:[self checkClassCBlock:self.passwordsZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+ [self expectCKModifyItemRecords:1 currentKeyPointerRecords:1 zoneID:self.limitedZoneID];
+ [self startCKKSSubsystem];
+
+ XCTAssertEqual(0, [self.passwordsView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:20*NSEC_PER_SEC], "CKKS entered ready");
+ OCMVerifyAllWithDelay(self.mockDatabase, 20);
+}
+
- (void)testReceiveItemInView {
[self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
[self startCKKSSubsystem];
[self.keychainView dispatchSync:^bool {
NSError* error = nil;
- CKRecordID* ckrid = [[CKRecordID alloc] initWithRecordName:@"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
+ CKRecordID* ckrid = [[CKRecordID alloc] initWithRecordName:@"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID];
CKKSItem* item = [self newItem:ckrid withNewItemData:[self fakeRecordDictionary:account zoneID:self.keychainZoneID] key:self.keychainZoneKeys.classC];
XCTAssertNotNil(item, "Should be able to create a new fake item");
XCTAssertNil(error, "No error loading IQEs");
XCTAssertNotNil(iqes, "Could load IQEs");
XCTAssertEqual(iqes.count, 0u, "Incoming queue is empty");
+ return false;
}];
}
(id)kSecAttrAccessible: (id)kSecAttrAccessibleWhenUnlocked,
(id)kSecAttrAccount : @"account-class-A",
(id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
+ (id)kSecAttrSyncViewHint : self.keychainView.zoneName,
(id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
}, NULL), @"Adding class A item");
OCMVerifyAllWithDelay(self.mockDatabase, 20);
[self checkGenericPassword: @"data" account: @"second"];
}
-- (void)testMultipleZoneAdd {
- // Bring up a new zone: we expect a key hierarchy upload.
- CKKSKeychainView* atvView = [self.injectedManager findOrCreateView:(id)kSecAttrViewHintAppleTV];
- [self.ckksViews addObject:atvView];
- CKRecordZoneID* appleTVZoneID = [[CKRecordZoneID alloc] initWithZoneName:(__bridge NSString*) kSecAttrViewHintAppleTV ownerName:CKCurrentUserDefaultName];
-
- // We also expect the view manager's notifyNewTLKsInKeychain call to fire once (after some delay)
- OCMExpect([self.mockCKKSViewManager notifyNewTLKsInKeychain]);
-
- // Let the horses loose
- [self startCKKSSubsystem];
- [self performOctagonTLKUpload:self.ckksViews];
-
- // We expect a single record to be uploaded to the 'keychain' view
- [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID];
- [self addGenericPassword: @"data" account: @"account-delete-me"];
- OCMVerifyAllWithDelay(self.mockDatabase, 20);
-
- // We expect a single record to be uploaded to the 'atv' view
- [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:appleTVZoneID];
- [self addGenericPassword: @"atv"
- account: @"tvaccount"
- viewHint:(__bridge NSString*) kSecAttrViewHintAppleTV
- access:(id)kSecAttrAccessibleAfterFirstUnlock
- expecting:errSecSuccess message:@"AppleTV view-hinted object"];
-
- OCMVerifyAllWithDelay(self.mockDatabase, 20);
-
- OCMVerifyAllWithDelay(self.mockCKKSViewManager, 10);
-}
-
-- (void)testMultipleZoneDelete {
- [self startCKKSSubsystem];
-
- // Bring up a new zone: we expect a key hierarchy and an item.
- CKKSKeychainView* atvView = [self.injectedManager findOrCreateView:(id)kSecAttrViewHintAppleTV];
- XCTAssertNotNil(atvView, "Should have a new ATV view");
- [self.ckksViews addObject:atvView];
- [self beginSOSTrustedViewOperation:atvView];
- CKRecordZoneID* appleTVZoneID = [[CKRecordZoneID alloc] initWithZoneName:(__bridge NSString*) kSecAttrViewHintAppleTV ownerName:CKCurrentUserDefaultName];
-
- [self performOctagonTLKUpload:self.ckksViews];
-
- // We expect a single record to be uploaded.
- [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID];
- [self addGenericPassword: @"data" account: @"account-delete-me"];
- OCMVerifyAllWithDelay(self.mockDatabase, 20);
-
- [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:appleTVZoneID];
- [self addGenericPassword: @"atv"
- account: @"tvaccount"
- viewHint:(__bridge NSString*) kSecAttrViewHintAppleTV
- access:(id)kSecAttrAccessibleAfterFirstUnlock
- expecting:errSecSuccess
- message:@"AppleTV view-hinted object"];
- OCMVerifyAllWithDelay(self.mockDatabase, 20);
-
- // We expect a single record to be deleted from the ATV zone
- [self expectCKDeleteItemRecords: 1 zoneID:appleTVZoneID];
- [self deleteGenericPassword:@"tvaccount"];
- OCMVerifyAllWithDelay(self.mockDatabase, 20);
-
- // Now we expect a single record to be deleted from the test zone
- [self expectCKDeleteItemRecords: 1 zoneID:self.keychainZoneID];
- [self deleteGenericPassword:@"account-delete-me"];
- OCMVerifyAllWithDelay(self.mockDatabase, 20);
-}
-
- (void)testRestartWithoutRefetch {
// Restarting the CKKS operation should check that it's been 15 minutes since the last fetch before it fetches again. Simulate this.
[self startCKKSSubsystem];
NSError* error = nil;
NSDictionary* currentOQEs = [CKKSOutgoingQueueEntry countsByStateInZone:self.keychainZoneID error:&error];
- XCTAssertNil(error, "Should be no error coutning OQEs");
+ XCTAssertNil(error, "Should be no error counting OQEs");
XCTAssertEqual(0, currentOQEs.count, "Should be no OQEs");
// Now, insert a restart to simulate securityd restarting (and throwing away all pending operations), then a real sign in
OCMVerifyAllWithDelay(self.mockDatabase, 20);
}
+- (void)testSyncableItemAddedOnDaemonRestartBeforePolicyLoaded {
+ [self createAndSaveFakeKeyHierarchy:self.keychainZoneID];
+ [self startCKKSSubsystem];
+
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:20*NSEC_PER_SEC], "CKKS entered ready");
+
+ [self.keychainView waitForOperationsOfClass:[CKKSScanLocalItemsOperation class]];
+ [self.keychainView waitForOperationsOfClass:[CKKSOutgoingQueueOperation class]];
+
+ // Daemon restarts
+ self.automaticallyBeginCKKSViewCloudKitOperation = false;
+ [self.injectedManager resetSyncingPolicy];
+ [self.injectedManager haltZone:self.keychainZoneID.zoneName];
+
+ // This item addition shouldn't be uploaded yet, or in any queues
+ [self addGenericPassword:@"data" account:@"account-delete-me-2"];
+
+ NSError* error = nil;
+ NSDictionary* currentOQEs = [CKKSOutgoingQueueEntry countsByStateInZone:self.keychainZoneID error:&error];
+ XCTAssertNil(error, "Should be no error counting OQEs");
+ XCTAssertEqual(0, currentOQEs.count, "Should be no OQEs");
+
+ [self.injectedManager setSyncingViews:self.managedViewList sortingPolicy:self.viewSortingPolicyForManagedViewList];
+ self.keychainView = [self.injectedManager findView:self.keychainZoneID.zoneName];
+ // end of daemon restart
+
+ [self expectCKModifyItemRecords:1 currentKeyPointerRecords:1 zoneID:self.keychainZoneID
+ checkItem:[self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+
+ [self.injectedManager beginCloudKitOperationOfAllViews];
+ [self beginSOSTrustedViewOperation:self.keychainView];
+
+ XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:20*NSEC_PER_SEC], "CKKS entered ready");
+ OCMVerifyAllWithDelay(self.mockDatabase, 20);
+}
+
// Note that this test assumes that the keychainView object was created at daemon restart.
// I don't really know how to write a test for that...
- (void)testSyncableItemAddedOnDaemonRestartBeforeCloudKitAccountKnown {
// Add some current item pointers. They don't necessarily need to point to anything...
CKKSCurrentItemPointer* cip = [[CKKSCurrentItemPointer alloc] initForIdentifier:@"com.apple.security.ckks-pcsservice"
- currentItemUUID:@"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3"
+ currentItemUUID:@"50184A35-4480-E8BA-769B-567CF72F1EC0"
state:SecCKKSProcessedStateRemote
zoneID:self.keychainZoneID
encodedCKRecord:nil];
XCTAssertNotNil(currentPointerRecord, "Found record in CloudKit at expected UUID");
CKKSCurrentItemPointer* cip2 = [[CKKSCurrentItemPointer alloc] initForIdentifier:@"com.apple.security.ckks-pcsservice2"
- currentItemUUID:@"3AB8E78D-75AF-CFEF-F833-FA3E3E90978A"
+ currentItemUUID:@"10E76B80-CE1C-A52A-B0CB-462A2EBA05AF"
state:SecCKKSProcessedStateRemote
zoneID:self.keychainZoneID
encodedCKRecord:nil];
// And add a garbage CIP
CKKSCurrentItemPointer* cip3 = [[CKKSCurrentItemPointer alloc] initForIdentifier:@"garbage"
- currentItemUUID:@"3AB8E78D-75AF-CFEF-F833-FA3E3E90978A"
+ currentItemUUID:@"10E76B80-CE1C-A52A-B0CB-462A2EBA05AF"
state:SecCKKSProcessedStateLocal
zoneID:self.keychainZoneID
encodedCKRecord:nil];
access:(NSString*)access
expecting:(OSStatus)status
message:(NSString*)message;
+
+- (BOOL)addGenericPassword:(NSString*)password
+ account:(NSString*)account
+ access:(NSString*)access
+ viewHint:(NSString* _Nullable)viewHint
+ accessGroup:(NSString* _Nullable)accessGroup
+ expecting:(OSStatus)status
+ message:(NSString*)message;
+
- (void)addGenericPassword:(NSString*)password account:(NSString*)account expecting:(OSStatus)status message:(NSString*)message;
- (void)updateGenericPassword:(NSString*)newPassword account:(NSString*)account;
self.suggestTLKUpload = OCMClassMock([CKKSNearFutureScheduler class]);
OCMStub([self.suggestTLKUpload trigger]);
- self.ckksZones = [NSMutableSet set];
- self.ckksViews = [NSMutableSet set];
- self.keys = [[NSMutableDictionary alloc] init];
+ // If a subclass wants to fill these in before calling setUp, fine.
+ self.ckksZones = self.ckksZones ?: [NSMutableSet set];
+ self.ckksViews = self.ckksViews ?: [NSMutableSet set];
+ self.keys = self.keys ?: [[NSMutableDictionary alloc] init];
[SecMockAKS reset];
- (void)verifyDatabaseMocks {
OCMVerifyAllWithDelay(self.mockDatabase, 20);
+ [self waitForCKModifications];
}
- (void)createClassCItemAndWaitForUpload:(CKRecordZoneID*)zoneID account:(NSString*)account {
return ret;
}
-- (void)addGenericPassword: (NSString*) password account: (NSString*) account viewHint: (NSString*) viewHint access: (NSString*) access expecting: (OSStatus) status message: (NSString*) message {
+- (BOOL)addGenericPassword:(NSString*)password
+ account:(NSString*)account
+ access:(NSString*)access
+ viewHint:(NSString* _Nullable)viewHint
+ accessGroup:(NSString* _Nullable)accessGroup
+ expecting:(OSStatus)status
+ message:(NSString*)message
+{
NSMutableDictionary* query = [@{
(id)kSecClass : (id)kSecClassGenericPassword,
- (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
- (id)kSecAttrAccessible: access,
+ (id)kSecAttrAccessible: (id)access,
(id)kSecAttrAccount : account,
(id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
(id)kSecValueData : (id) [password dataUsingEncoding:NSUTF8StringEncoding],
} mutableCopy];
+ query[(id)kSecAttrAccessGroup] = accessGroup ?: @"com.apple.security.ckks";
+
if(viewHint) {
query[(id)kSecAttrSyncViewHint] = viewHint;
} else {
XCTAssertEqual(status, SecItemAdd((__bridge CFDictionaryRef) query, NULL), @"%@", message);
}
+- (void)addGenericPassword: (NSString*) password account: (NSString*) account viewHint: (NSString*) viewHint access: (NSString*) access expecting: (OSStatus) status message: (NSString*) message {
+ [self addGenericPassword:password
+ account:account
+ access:access
+ viewHint:viewHint
+ accessGroup:nil
+ expecting:status
+ message:message];
+}
+
- (void)addGenericPassword: (NSString*) password account: (NSString*) account expecting: (OSStatus) status message: (NSString*) message {
[self addGenericPassword:password account:account viewHint:nil access:(id)kSecAttrAccessibleAfterFirstUnlock expecting:errSecSuccess message:message];
@property NSCalendar* utcCalendar;
-- (NSSet<NSString*>*)managedViewList;
-
-
- (ZoneKeys*)keychainZoneKeys;
@end
// Wait for the ViewManager to be brought up
XCTAssertEqual(0, [self.injectedManager.completedSecCKKSInitialize wait:20*NSEC_PER_SEC], "No timeout waiting for SecCKKSInitialize");
- self.keychainView = [[CKKSViewManager manager] findOrCreateView:@"keychain"];
+ self.keychainView = [[CKKSViewManager manager] findView:@"keychain"];
XCTAssertNotNil(self.keychainView, "CKKSViewManager created the keychain view");
[self.ckksViews addObject:self.keychainView];
}
// Check that your environment is set up correctly
XCTAssertFalse([CKKSManifest shouldSyncManifests], "Manifests syncing is disabled");
XCTAssertFalse([CKKSManifest shouldEnforceManifests], "Manifests enforcement is disabled");
-
- self.aksLockState = false; // Lie and say AKS is always unlocked
- self.mockLockStateTracker = OCMClassMock([CKKSLockStateTracker class]);
- OCMStub([self.mockLockStateTracker queryAKSLocked]).andCall(self, @selector(aksLockState));
}
#import "keychain/ckks/CKKSAccountStateTracker.h"
#import "keychain/ckks/tests/MockCloudKit.h"
+#import "keychain/ot/OTManager.h"
+
NS_ASSUME_NONNULL_BEGIN
@class CKKSKey;
@property CKKSMockSOSPresentAdapter* mockSOSAdapter;
@property (nullable) CKKSMockOctagonAdapter *mockOctagonAdapter;
--(NSSet*)managedViewList;
+- (NSSet<NSString*>*)managedViewList;
+- (TPPolicy*)viewSortingPolicyForManagedViewList;
+
@property (nullable) id mockCKKSViewManager;
@property (nullable) CKKSViewManager* injectedManager;
+// Injected into CKKSViewManager using OCMock
+@property BOOL overrideUseCKKSViewsFromPolicy;
+
+// Set this to true before calling -setup if you're going to configure the ckks view manager yourself
+// !disable format used because this will be initialized to false, and so will happen unless subclasses take positive action
+@property BOOL disableConfigureCKKSViewManagerWithViews;
+
+// Set this to true to override CKKSViewsFromPolicy to NO instead of the default YES
+// !disable format used because this will be initialized to false, and so will happen unless subclasses take positive action
+@property BOOL setCKKSViewsFromPolicyToNo;
+
+@property (nullable) OTManager* injectedOTManager;
+
// Fill this in to fail the next modifyzones operation
@property (nullable) NSError* nextModifyRecordZonesError;
- (NSError* _Nullable)shouldFailModifyRecordZonesOperation;
- (void)ensureZoneDeletionAllowed:(FakeCKZone*)zone;
+// Override this to interpose on how an OTManager is created
+// This will be called during setUp, after the CloudKit mocks are in-place
+// This needs to fill in the injectedOTManager variable on this class
+- (OTManager*)setUpOTManager:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies;
+
// Use this to assert that a fetch occurs (especially if silentFetchesAllowed = false)
- (void)expectCKFetch;
#import <CloudKit/CloudKit_Private.h>
#import <CloudKit/CKContainer_Private.h>
#import <OCMock/OCMock.h>
+#import <TrustedPeers/TrustedPeers.h>
+#import <TrustedPeers/TPPBPolicyKeyViewMapping.h>
+#import <TrustedPeers/TPDictionaryMatchingRules.h>
#include "keychain/securityd/Regressions/SecdTestKeychainUtilities.h"
#include <utilities/SecFileLocations.h>
#include "keychain/securityd/SecItemServer.h"
+#include "keychain/securityd/SecItemDataSource.h"
#if NO_SERVER
#include "keychain/securityd/spi.h"
#include "keychain/ckks/CKKSLockStateTracker.h"
#include "keychain/ckks/CKKSReachabilityTracker.h"
+#include "keychain/ot/OT.h"
+#include "keychain/ot/OTManager.h"
+
#import "tests/secdmockaks/mockaks.h"
#import "utilities/SecTapToRadar.h"
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
- self.zones = [[NSMutableDictionary alloc] init];
+ self.zones = self.zones ?: [[NSMutableDictionary alloc] init];
self.apsEnvironment = @"fake APS push string";
nsdistributednotificationCenterClass:[FakeNSDistributedNotificationCenter class]
notifierClass:[FakeCKKSNotifier class]];
- self.mockCKKSViewManager = OCMPartialMock(
- [[CKKSViewManager alloc] initWithContainerName:SecCKKSContainerName
- usePCS:SecCKKSContainerUsePCS
- sosAdapter:self.mockSOSAdapter
- cloudKitClassDependencies:cloudKitClassDependencies]);
+ self.injectedOTManager = [self setUpOTManager:cloudKitClassDependencies];
+ [OTManager resetManager:false to:self.injectedOTManager];
+
+ self.mockCKKSViewManager = OCMPartialMock(self.injectedOTManager.viewManager);
+ self.injectedManager = self.mockCKKSViewManager;
+
+ [self.mockCKKSViewManager setOverrideCKKSViewsFromPolicy:!self.setCKKSViewsFromPolicyToNo];
OCMStub([self.mockCKKSViewManager defaultViewList]).andCall(self, @selector(managedViewList));
OCMStub([self.mockCKKSViewManager syncBackupAndNotifyAboutSync]);
OCMStub([self.mockCKKSViewManager waitForTrustReady]).andReturn(YES);
- self.injectedManager = self.mockCKKSViewManager;
-
- [CKKSViewManager resetManager:false setTo:self.injectedManager];
+ if(!self.disableConfigureCKKSViewManagerWithViews) {
+ // Normally, the Octagon state machine calls this. But, since we won't be running that, help it out.
+ [self.injectedManager setSyncingViews:[self managedViewList]
+ sortingPolicy:[self viewSortingPolicyForManagedViewList]];
+ }
// Lie and say network is available
[self.reachabilityTracker setNetworkReachability:true];
kc_with_dbt(true, NULL, ^bool (SecDbConnectionRef dbt) { return false; });
}
+- (OTManager*)setUpOTManager:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies
+{
+ return [[OTManager alloc] initWithSOSAdapter:self.mockSOSAdapter
+ lockStateTracker:[[CKKSLockStateTracker alloc] init]
+ cloudKitClassDependencies:cloudKitClassDependencies];
+
+}
+
- (SOSAccountStatus*)circleStatus {
NSError* error = nil;
SOSCCStatus status = [self.mockSOSAdapter circleStatus:&error];
XCTAssertTrue(self.silentZoneDeletesAllowed, "Should be allowing zone deletes");
}
--(CKKSAccountStateTracker*)accountStateTracker {
- return self.injectedManager.accountTracker;
+- (CKKSAccountStateTracker*)accountStateTracker {
+ return self.injectedOTManager.accountStateTracker;
}
-(CKKSLockStateTracker*)lockStateTracker {
- return self.injectedManager.lockStateTracker;
+ return self.injectedOTManager.lockStateTracker;
}
-(CKKSReachabilityTracker*)reachabilityTracker {
return (NSSet*) CFBridgingRelease(SOSViewCopyViewSet(kViewSetCKKS));
}
+- (TPPolicy*)viewSortingPolicyForManagedViewList
+{
+ NSMutableArray<TPPBPolicyKeyViewMapping*>* rules = [NSMutableArray array];
+
+ for(NSString* viewName in self.managedViewList) {
+ TPPBPolicyKeyViewMapping* mapping = [[TPPBPolicyKeyViewMapping alloc] init];
+ mapping.view = viewName;
+ mapping.matchingRule = [TPDictionaryMatchingRule fieldMatch:@"vwht"
+ fieldRegex:[NSString stringWithFormat:@"^%@$", viewName]];
+
+ [rules addObject:mapping];
+ }
+
+ TPPolicy* policy = [TPPolicy policyWithModelToCategory:@[]
+ categoriesByView:@{}
+ introducersByCategory:@{}
+ keyViewMapping:rules
+ unknownRedactions:NO
+ version:[[TPPolicyVersion alloc] initWithVersion:1 hash:@"fake-policy-for-views"]];
+
+ return policy;
+}
+
-(void)expectCKFetch {
[self expectCKFetchAndRunBeforeFinished: nil];
}
[super tearDown];
[self.injectedManager cancelPendingOperations];
- [CKKSViewManager resetManager:true setTo:nil];
+ [self.injectedManager clearAllViews];
self.injectedManager = nil;
+
[self.mockCKKSViewManager stopMocking];
self.mockCKKSViewManager = nil;
+ self.injectedOTManager.viewManager = nil;
+
+ [self.injectedOTManager clearAllContexts];
+ self.injectedOTManager = nil;
+ [OTManager resetManager:true to:nil];
+
[self.mockAccountStateTracker stopMocking];
self.mockAccountStateTracker = nil;
_mockSOSAdapter = nil;
_mockOctagonAdapter = nil;
+ // Bring the database down and delete it
+
+ NSURL* keychainDir = (NSURL*)CFBridgingRelease(SecCopyHomeURL());
+
+ SecItemDataSourceFactoryReleaseAll();
+ SecKeychainDbForceClose();
+ SecKeychainDbReset(NULL);
+
+ // Only perform the desctructive step if the url matches what we expect!
+ if([keychainDir.path hasPrefix:[NSString stringWithFormat:@"/tmp/%@", testName]]) {
+ secnotice("ckkstest", "Removing test-specific keychain directory at %@", keychainDir);
+
+ NSError* removeError = nil;
+ [[NSFileManager defaultManager] removeItemAtURL:keychainDir error:&removeError];
+
+ XCTAssertNil(removeError, "Should have been able to remove temporary files");
+ } else {
+ XCTFail("Unsure what happened to the keychain directory URL: %@", keychainDir);
+ }
+
SecCKKSTestResetFlags();
}
--- /dev/null
+#!/usr/bin/python
+#
+# TODO(kirk or SEAR/QA) after radar 53867279 is fixed, please delete this script
+#
+# WARNING: if you add new tests to the swift octagon tests, it is possible that
+# this script will not find them and then your new tests will not get executed
+# in BATS!
+#
+import sys
+import Foundation
+from glob import glob
+import re
+import os
+import pprint
+
+test_dir = sys.argv[1]
+outfile = sys.argv[2]
+
+test_plist = Foundation.NSMutableDictionary.dictionary()
+test_plist['BATSConfigVersion'] = '0.1.0'
+test_plist['Project'] = 'Security'
+test_list = Foundation.NSMutableArray.array()
+
+test_files = glob(test_dir + '/*.m') + glob(test_dir + '/*.h')
+
+def get_class_names():
+ test_classes = ['CKKSLaunchSequenceTests']
+ for filename in test_files:
+ f = open(filename, 'r')
+ for line in f:
+ match = re.search('@interface ([a-zA-Z0-9_]+) ?: ?CloudKitKeychain[a-zA-Z0-9_]*TestsBase', line)
+ #match = re.search('class (([a-zA-Z0-9_]+Tests)|(OctagonTests[a-zA-Z0-9_]*(?<!Base))): ', line)
+ if match:
+ test_classes.append(match.group(1))
+
+ # And pick up everything that's a default xctest, too
+ match = re.search('@interface ([a-zA-Z0-9_]+) ?: ?XCTestCase', line)
+ if match:
+ test_classes.append(match.group(1))
+
+ # or based on CloudKitMockXCTest
+ match = re.search('@interface ([a-zA-Z0-9_]+) ?: ?CloudKitMockXCTest', line)
+ if match:
+ test_classes.append(match.group(1))
+ f.close()
+
+ # But we don't want any helper classes
+ base_classes = ['CloudKitMockXCTest', 'CloudKitKeychainSyncingMockXCTest', 'CKKSCloudKitTests']
+ for base_class in base_classes:
+ test_classes = [x for x in test_classes if base_class != x]
+ return test_classes
+
+for x in get_class_names():
+
+ test_dictionary = Foundation.NSMutableDictionary.dictionary()
+ test_dictionary['TestName'] = x
+ test_dictionary['Timeout']= 1200
+ test_dictionary['ShowSubtestResults']= True
+ test_dictionary['WorkingDirectory'] = '/AppleInternal/XCTests/com.apple.security/'
+
+ test_command = Foundation.NSMutableArray.array()
+ test_command.append('BATS_XCTEST_CMD -XCTest {} CKKSTests.xctest'.format(x))
+ test_dictionary['Command'] = test_command
+
+ test_list.append(test_dictionary)
+
+test_plist['Tests'] = test_list
+
+out_dir = os.path.dirname(outfile)
+if not os.path.exists(out_dir):
+ os.makedirs(out_dir)
+success = test_plist.writeToFile_atomically_(outfile, 1)
+if not success:
+ print "test plist failed to write, error!"
+ sys.exit(1)
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>BATSConfigVersion</key>
- <string>0.1.0</string>
- <key>Project</key>
- <string>Security</string>
- <key>Tests</key>
- <array>
- <dict>
- <key>TestName</key>
- <string>CKKSTests</string>
- <key>WorkingDirectory</key>
- <string>/AppleInternal/XCTests/com.apple.security/</string>
- <key>ShowSubtestResults</key>
- <true/>
- <key>Timeout</key>
- <integer>1200</integer>
- <key>Command</key>
- <array>
- <string>BATS_XCTEST_CMD CKKSTests.xctest</string>
- </array>
- </dict>
- <!--dict>
- <key>TestName</key>
- <string>CKKSCloudKitTests</string>
- <key>WorkingDirectory</key>
- <string>/AppleInternal/XCTests/com.apple.security/</string>
- <key>Command</key>
- <array>
- <string>/AppleInternal/CoreOS/tests/Security/KeychainEntitledTestRunner</string>
- </array>
- </dict-->
- </array>
-</dict>
-</plist>
return status;
}
-- (void)printHumanReadableStatus: (NSString*) view {
+- (void)printHumanReadableStatus:(NSString*)view shortenOutput:(BOOL)shortenOutput {
#if OCTAGON
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
NSString* lockStateTracker = pop(global,@"lockstatetracker", NSString);
NSString* retry = pop(global,@"cloudkitRetryAfter", NSString);
NSDate *lastCKKSPush = pop(global, @"lastCKKSPush", NSDate);
+ NSString *syncingPolicy = pop(global, @"policy", NSString);
+ NSString *viewsFromPolicy = pop(global, @"viewsFromPolicy", NSString);
- printf("================================================================================\n\n");
- printf("Global state:\n\n");
- printf("Reachability: %s\n", [[reachability description] UTF8String]);
- printf("Retry: %s\n", [[retry description] UTF8String]);
- printf("CK DeviceID: %s\n", [[ckdeviceID description] UTF8String]);
- printf("CK DeviceID Error: %s\n", [[ckdeviceIDError description] UTF8String]);
- printf("Lock state: %s\n", [[lockStateTracker description] UTF8String]);
- printf("Last CKKS push: %s\n", [[lastCKKSPush description] UTF8String]);
+ if(!shortenOutput) {
+ printf("================================================================================\n\n");
+ printf("Global state:\n\n");
+ }
+
+ printf("Syncing Policy: %s\n", [[syncingPolicy description] UTF8String]);
+ printf("Views from policy: %s\n", [[viewsFromPolicy description] UTF8String]);
+
+ if(!shortenOutput) {
+ printf("Reachability: %s\n", [[reachability description] UTF8String]);
+ printf("Retry: %s\n", [[retry description] UTF8String]);
+ printf("CK DeviceID: %s\n", [[ckdeviceID description] UTF8String]);
+ printf("CK DeviceID Error: %s\n", [[ckdeviceIDError description] UTF8String]);
+ printf("Lock state: %s\n", [[lockStateTracker description] UTF8String]);
+ printf("Last CKKS push: %s\n", [[lastCKKSPush description] UTF8String]);
+ }
printf("\n");
}
}
for(NSDictionary* viewStatus in remainingViews) {
+ if(shortenOutput) {
+ NSMutableDictionary* status = [viewStatus mutableCopy];
+
+ NSString* viewName = pop(status, @"view", NSString);
+ NSString* keystate = pop(status, @"keystate", NSString);
+
+ printf("%-25s: %s\n", [viewName UTF8String], [keystate UTF8String]);
+ continue;
+ }
+
NSMutableDictionary* status = [viewStatus mutableCopy];
NSString* viewName = pop(status,@"view", NSString);
static int fetch = false;
static int push = false;
static int json = false;
+static int shortOutput = false;
static int ckmetric = false;
static char* viewArg = NULL;
static struct argument options[] = {
{ .shortname='p', .longname="perfcounters", .flag=&perfCounters, .flagval=true, .description="Print CKKS performance counters"},
{ .shortname='j', .longname="json", .flag=&json, .flagval=true, .description="Output in JSON format"},
+ { .shortname='s', .longname="short", .flag=&shortOutput, .flagval=true, .description="Output a short format"},
{ .shortname='v', .longname="view", .argument=&viewArg, .description="Operate on a single view"},
{ .command="status", .flag=&status, .flagval=true, .description="Report status on CKKS views"},
}
if(!json) {
- [ctl printHumanReadableStatus:view];
+ [ctl printHumanReadableStatus:view shortenOutput:shortOutput];
}
return 0;
} else if(perfCounters) {
}
- (BOOL)hasCertCached
{
- return _has.certCached;
+ return _has.certCached != 0;
}
- (BOOL)hasSerializedPrerecord
{
}
- (BOOL)hasLastCloudServicesTriggerTime
{
- return _has.lastCloudServicesTriggerTime;
+ return _has.lastCloudServicesTriggerTime != 0;
}
@synthesize lastEscrowAttemptTime = _lastEscrowAttemptTime;
- (void)setLastEscrowAttemptTime:(uint64_t)v
}
- (BOOL)hasLastEscrowAttemptTime
{
- return _has.lastEscrowAttemptTime;
+ return _has.lastEscrowAttemptTime != 0;
}
@synthesize uploadCompleted = _uploadCompleted;
- (void)setUploadCompleted:(BOOL)v
}
- (BOOL)hasUploadCompleted
{
- return _has.uploadCompleted;
+ return _has.uploadCompleted != 0;
}
@synthesize uploadRetries = _uploadRetries;
- (void)setUploadRetries:(uint64_t)v
}
- (BOOL)hasUploadRetries
{
- return _has.uploadRetries;
+ return _has.uploadRetries != 0;
}
- (BOOL)hasAltDSID
{
}
- (BOOL)hasTriggerRequestTime
{
- return _has.triggerRequestTime;
+ return _has.triggerRequestTime != 0;
}
- (NSString *)description
@discussion Predefined item attribute constants used to get or set values
in a dictionary. The kSecAttrKeyType constant is the key
and its value is one of the constants defined here.
- @constant kSecAttrKeyTypeECSECPrimeRandom.
+ @constant kSecAttrKeyTypeECSECPrimeRandom. The used curve is P-192, P-256, P-384 or P-521.
+ The size is specified by kSecAttrKeySizeInBits attribute. Curves are defined in FIPS PUB 186-4 standard.
@constant kSecAttrKeyTypeEC This is the legacy name for kSecAttrKeyTypeECSECPrimeRandom, new applications should not use it.
@constant kSecAttrKeyTypeDSA (OSX only)
@constant kSecAttrKeyTypeAES (OSX only)
* kSecAttrKeySizeInBits
* kSecAttrTokenID
* kSecAttrApplicationLabel
- Other values returned in that dictionary are RFU.
+ The set of values is not fixed. Future versions may return more values in this dictionary.
*/
CFDictionaryRef _Nullable SecKeyCopyAttributes(SecKeyRef key)
__OSX_AVAILABLE(10.12) __IOS_AVAILABLE(10.0) __TVOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);
- (void)setAllowedMachineIDsWithContainer:(NSString *)container
context:(NSString *)context
allowedMachineIDs:(NSSet<NSString*> *)allowedMachineIDs
+ honorIDMSListChanges:(BOOL)accountIsDemo
reply:(void (^)(BOOL listDifferences, NSError * _Nullable error))reply
{
__block int i = 0;
reply(NO, error);
}
++i;
- }] setAllowedMachineIDsWithContainer:container context:context allowedMachineIDs:allowedMachineIDs reply:reply];
+ }] setAllowedMachineIDsWithContainer:container context:context allowedMachineIDs:allowedMachineIDs honorIDMSListChanges:accountIsDemo reply:reply];
} while (retry);
}
deviceName:(nullable NSString*)deviceName
serialNumber:(NSString *)serialNumber
osVersion:(NSString *)osVersion
- policyVersion:(nullable NSNumber *)policyVersion
+ policyVersion:(nullable TPPolicy *)policyVersion
policySecrets:(nullable NSDictionary<NSString*,NSData*> *)policySecrets
signingPrivKeyPersistentRef:(nullable NSData *)spkPr
encPrivKeyPersistentRef:(nullable NSData*)epkPr
NSData * _Nullable permanentInfoSig,
NSData * _Nullable stableInfo,
NSData * _Nullable stableInfoSig,
+ NSSet<NSString*>* syncingViews,
+ TPPolicy* _Nullable syncingPolicy,
NSError * _Nullable error))reply
{
__block int i = 0;
retry = true;
} else {
secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
- reply(nil, nil, nil, nil, nil, error);
+ reply(nil, nil, nil, nil, nil, nil, nil, error);
}
++i;
- }] prepareWithContainer:container context:context epoch:epoch machineID:machineID bottleSalt:bottleSalt bottleID:bottleID modelID:modelID deviceName:deviceName serialNumber:serialNumber osVersion:osVersion policyVersion:policyVersion policySecrets:policySecrets signingPrivKeyPersistentRef:spkPr encPrivKeyPersistentRef:epkPr reply:reply];
+ }] prepareWithContainer:container
+ context:context
+ epoch:epoch
+ machineID:machineID
+ bottleSalt:bottleSalt
+ bottleID:bottleID
+ modelID:modelID
+ deviceName:deviceName
+ serialNumber:serialNumber
+ osVersion:osVersion
+ policyVersion:policyVersion
+ policySecrets:policySecrets
+ signingPrivKeyPersistentRef:spkPr
+ encPrivKeyPersistentRef:epkPr
+ reply:reply];
} while (retry);
}
- (void)preflightVouchWithBottleWithContainer:(nonnull NSString *)container
context:(nonnull NSString *)context
bottleID:(nonnull NSString *)bottleID
- reply:(nonnull void (^)(NSString * _Nullable, NSError * _Nullable))reply {
+ reply:(nonnull void (^)(NSString * _Nullable,
+ NSSet<NSString*>* _Nullable peerSyncingViewList,
+ TPPolicy * _Nullable peerSyncingPolicy,
+ NSError * _Nullable))reply {
__block int i = 0;
__block bool retry;
do {
retry = true;
} else {
secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
- reply(nil, error);
+ reply(nil, nil, nil, error);
}
++i;
}] preflightVouchWithBottleWithContainer:container
tlkShares:(NSArray<CKKSTLKShare*> *)tlkShares
reply:(void (^)(NSData * _Nullable voucher,
NSData * _Nullable voucherSig,
+ int64_t uniqueTLKsRecovered,
+ int64_t totalTLKSharesRecovered,
NSError * _Nullable error))reply
{
__block int i = 0;
retry = true;
} else {
secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
- reply(nil, nil, error);
+ reply(nil, nil, 0, 0, error);
}
++i;
}] vouchWithBottleWithContainer:container context:context bottleID:bottleID entropy:entropy bottleSalt:bottleSalt tlkShares:tlkShares reply:reply];
} while (retry);
}
+- (void)preflightVouchWithRecoveryKeyWithContainer:(nonnull NSString *)container
+ context:(nonnull NSString *)context
+ recoveryKey:(NSString*)recoveryKey
+ salt:(NSString*)salt
+ reply:(nonnull void (^)(NSString * _Nullable,
+ NSSet<NSString*>* _Nullable peerSyncingViewList,
+ TPPolicy * _Nullable peerSyncingPolicy,
+ NSError * _Nullable))reply {
+ __block int i = 0;
+ __block bool retry;
+ do {
+ retry = false;
+ [[self.cuttlefishXPCConnection synchronousRemoteObjectProxyWithErrorHandler:^(NSError *_Nonnull error) {
+ if (i < NUM_RETRIES && [self.class retryable:error]) {
+ secnotice("octagon", "retrying cuttlefish XPC, (%d, %@)", i, error);
+ retry = true;
+ } else {
+ secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
+ reply(nil, nil, nil, error);
+ }
+ ++i;
+ }] preflightVouchWithRecoveryKeyWithContainer:container
+ context:context
+ recoveryKey:recoveryKey
+ salt:salt
+ reply:reply];
+ } while (retry);
+}
+
- (void)vouchWithRecoveryKeyWithContainer:(NSString *)container
context:(NSString *)context
recoveryKey:(NSString*)recoveryKey
preapprovedKeys:(NSArray<NSData*> *)preapprovedKeys
reply:(void (^)(NSString * _Nullable peerID,
NSArray<CKRecord*>* _Nullable keyHierarchyRecords,
+ NSSet<NSString*>* _Nullable viewSet,
+ TPPolicy* _Nullable syncingPolicy,
NSError * _Nullable error))reply
{
__block int i = 0;
retry = true;
} else {
secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
- reply(nil, nil, error);
+ reply(nil, nil, nil, nil, error);
}
++i;
}] joinWithContainer:container context:context voucherData:voucherData voucherSig:voucherSig ckksKeys:viewKeySets tlkShares:tlkShares preapprovedKeys:preapprovedKeys reply:reply];
preapprovedKeys:(NSArray<NSData*> *)preapprovedKeys
reply:(void (^)(NSString * _Nullable peerID,
NSArray<CKRecord*>* _Nullable keyHierarchyRecords,
+ NSSet<NSString*>* _Nullable syncingViewList,
+ TPPolicy* _Nullable syncingPolicy,
NSError * _Nullable error))reply
{
__block int i = 0;
retry = true;
} else {
secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
- reply(nil, nil, error);
+ reply(nil, nil, nil, nil, error);
}
++i;
}] attemptPreapprovedJoinWithContainer:container context:context ckksKeys:ckksKeys tlkShares:tlkShares preapprovedKeys:preapprovedKeys reply:reply];
- (void)setPreapprovedKeysWithContainer:(NSString *)container
context:(NSString *)context
preapprovedKeys:(NSArray<NSData*> *)preapprovedKeys
- reply:(void (^)(NSError * _Nullable error))reply
+ reply:(void (^)(TrustedPeersHelperPeerState* _Nullable peerState, NSError * _Nullable error))reply
{
__block int i = 0;
__block bool retry;
retry = true;
} else {
secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
- reply(error);
+ reply(nil, error);
}
++i;
}] setPreapprovedKeysWithContainer:container context:context preapprovedKeys:preapprovedKeys reply:reply];
- (void)fetchPolicyDocumentsWithContainer:(NSString*)container
context:(NSString*)context
- keys:(NSDictionary<NSNumber*,NSString*>*)keys
- reply:(void (^)(NSDictionary<NSNumber*,NSArray<NSString*>*>* _Nullable entries,
+ versions:(NSSet<TPPolicyVersion*>*)versions
+ reply:(void (^)(NSDictionary<TPPolicyVersion*, NSData*>* _Nullable entries,
NSError * _Nullable error))reply
{
__block int i = 0;
reply(nil, error);
}
++i;
- }] fetchPolicyDocumentsWithContainer:container context:context keys:keys reply:reply];
+ }] fetchPolicyDocumentsWithContainer:container context:context versions:versions reply:reply];
} while (retry);
}
-- (void)fetchPolicyWithContainer:(NSString*)container
- context:(NSString*)context
- reply:(void (^)(TPPolicy * _Nullable policy,
- NSError * _Nullable error))reply
+- (void)fetchCurrentPolicyWithContainer:(NSString*)container
+ context:(NSString*)context
+ reply:(void (^)(NSSet<NSString*>* _Nullable viewList,
+ TPPolicy * _Nullable policy,
+ NSError * _Nullable error))reply
{
__block int i = 0;
__block bool retry;
retry = true;
} else {
secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
- reply(nil, error);
+ reply(nil, nil, error);
}
++i;
- }] fetchPolicyWithContainer:container context:context reply:reply];
+ }] fetchCurrentPolicyWithContainer:container context:context reply:reply];
} while (retry);
}
} while (retry);
}
-- (void)getViewsWithContainer:(NSString *)container
- context:(NSString *)context
- inViews:(NSArray<NSString*>*)inViews
- reply:(void (^)(NSArray<NSString*>* _Nullable, NSError* _Nullable))reply
-{
- __block int i = 0;
- __block bool retry;
- do {
- retry = false;
- [[self.cuttlefishXPCConnection synchronousRemoteObjectProxyWithErrorHandler:^(NSError *_Nonnull error) {
- if (i < NUM_RETRIES && [self.class retryable:error]) {
- secnotice("octagon", "retrying cuttlefish XPC, (%d, %@)", i, error);
- retry = true;
- } else {
- secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
- reply(nil, error);
- }
- ++i;
- }] getViewsWithContainer:container context:context inViews:inViews reply:reply];
- } while (retry);
-}
-
- (void)requestHealthCheckWithContainer:(NSString *)container
context:(NSString *)context
requiresEscrowCheck:(BOOL)requiresEscrowCheck
- reply:(void (^)(BOOL postRepairCFU, BOOL postEscrowCFU, BOOL resetOctagon, NSError* _Nullable))reply
+ reply:(void (^)(BOOL postRepairCFU, BOOL postEscrowCFU, BOOL resetOctagon, BOOL leaveTrust, NSError* _Nullable))reply
{
__block int i = 0;
__block bool retry;
retry = true;
} else {
secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
- reply(NO, NO, NO, error);
+ reply(NO, NO, NO, NO, error);
}
++i;
- }] requestHealthCheckWithContainer:container context:context requiresEscrowCheck:requiresEscrowCheck reply:reply];
+ }] requestHealthCheckWithContainer:container context:context requiresEscrowCheck:requiresEscrowCheck reply:reply];
} while (retry);
}
{
OTManager* manager = [OTManager manager];
[manager initializeOctagon];
+ [manager setupAnalytics];
}
// If you want octagon to be initialized in your daemon/tests, you must set this to be true
void SecOctagon24hrNotification(void) {
#if OCTAGON
@autoreleasepool {
- [[OTManager manager] xpc24HrNotification:OTCKContainerName context:OTDefaultContext skipRateLimitingCheck:NO reply: ^(NSError * error) {
- if(error){
- secerror("error attempting to check octagon health: %@", error);
- }
- }];
+ [[OTManager manager] xpc24HrNotification];
}
#endif
}
- (NSString* _Nullable)primaryiCloudAccountAltDSID:(NSError **)error;
- (BOOL)accountIsHSA2ByAltDSID:(NSString*)altDSID;
+- (BOOL)accountIsDemoAccount:(NSError**)error NS_SWIFT_NOTHROW;
- (NSString* _Nullable)machineID:(NSError**)error;
- (void)fetchCurrentDeviceList:(void (^)(NSSet<NSString*>* _Nullable machineIDs, NSError* _Nullable error))complete;
- (BOOL)accountIsHSA2ByAltDSID:(NSString*)altDSID
{
- bool hsa2 = false;
+ BOOL hsa2 = NO;
AKAccountManager *manager = [AKAccountManager sharedInstance];
ACAccount *authKitAccount = [manager authKitAccountWithAltDSID:altDSID];
AKAppleIDSecurityLevel securityLevel = [manager securityLevelForAccount:authKitAccount];
if(securityLevel == AKAppleIDSecurityLevelHSA2) {
- hsa2 = true;
+ hsa2 = YES;
}
secnotice("security-authkit", "Security level for altDSID %@ is %lu", altDSID, (unsigned long)securityLevel);
return hsa2;
}
+- (BOOL)accountIsDemoAccount:(NSError**)error
+{
+ NSError* localError = nil;
+ NSString* altDSID = [self primaryiCloudAccountAltDSID:&localError];
+
+ if(altDSID == nil) {
+ secerror("octagon-authkit:could not retrieve altDSID");
+ }
+ if (localError) {
+ secerror("octagon-authkit: hit an error retrieving altDSID: %@", localError);
+ if(error){
+ *error = localError;
+ }
+ return NO;
+ }
+
+ AKAccountManager *manager = [AKAccountManager sharedInstance];
+ ACAccount *authKitAccount = [manager authKitAccountWithAltDSID:altDSID];
+ BOOL isDemo = [manager demoAccountForAccount:authKitAccount];
+
+ secnotice("security-authkit", "Account with altDSID %@ is a demo account: %@", altDSID, isDemo ? @"true" : @"false");
+
+ return isDemo;
+}
+
- (NSString* _Nullable)machineID:(NSError**)error
{
AKAnisetteProvisioningController* anisetteController = [[AKAnisetteProvisioningController alloc] init];
@property BOOL postRepairCFU;
@property BOOL postEscrowCFU;
@property BOOL resetOctagon;
+@property BOOL leaveTrust;
@end
_postRepairCFU = NO;
_postEscrowCFU = NO;
_resetOctagon = NO;
+ _leaveTrust = NO;
_skipRateLimitingCheck = skipRateLimitedCheck;
}
return self;
self.error = nil;
lastUpdate = [self.deps.stateHolder lastHealthCheckupDate:&accountLoadError];
- if([self.deps.viewManager.lockStateTracker isLockedError: accountLoadError]) {
+
+ CKKSViewManager* viewManager = self.deps.viewManager;
+ if([viewManager.lockStateTracker isLockedError: accountLoadError]) {
secnotice("octagon-health", "device is locked, not performing cuttlefish check");
[self runBeforeGroupFinished:self.finishOp];
return;
NSError* persistedError = nil;
BOOL persisted = [self.deps.stateHolder persistLastHealthCheck:now error:&persistedError];
- if([self.deps.viewManager.lockStateTracker isLockedError: persistedError]) {
+ if([viewManager.lockStateTracker isLockedError: persistedError]) {
secnotice("octagon-health", "device is locked, not performing cuttlefish check");
[self runBeforeGroupFinished:self.finishOp];
return;
[self.deps.cuttlefishXPCWrapper requestHealthCheckWithContainer:self.deps.containerName
context:self.deps.contextID
requiresEscrowCheck: [self checkIfPasscodeIsSetForDevice]
- reply:^(BOOL postRepairCFU, BOOL postEscrowCFU, BOOL resetOctagon, NSError *error) {
+ reply:^(BOOL postRepairCFU, BOOL postEscrowCFU, BOOL resetOctagon, BOOL leaveTrust, NSError *error) {
STRONGIFY(self);
if(error) {
secerror("octagon-health: error: %@", error);
[self runBeforeGroupFinished:self.finishOp];
return;
} else {
- secnotice("octagon-health", "cuttlefish came back with these suggestions\n: post repair? %d\n, post escrow? %d\n, reset octagon? %d", postRepairCFU, postEscrowCFU, resetOctagon);
+ secnotice("octagon-health", "cuttlefish came back with these suggestions\n: post repair? %d\n, post escrow? %d\n, reset octagon? %d\n leave trust? %d\n", postRepairCFU, postEscrowCFU, resetOctagon, leaveTrust);
[self handleRepairSuggestions:postRepairCFU
postEscrowCFU:postEscrowCFU
- resetOctagon:resetOctagon];
+ resetOctagon:resetOctagon
+ leaveTrust:leaveTrust];
}
}];
}
-- (void)handleRepairSuggestions:(BOOL)postRepairCFU postEscrowCFU:(BOOL)postEscrowCFU resetOctagon:(BOOL)resetOctagon
+- (void)handleRepairSuggestions:(BOOL)postRepairCFU postEscrowCFU:(BOOL)postEscrowCFU resetOctagon:(BOOL)resetOctagon leaveTrust:(BOOL)leaveTrust
{
self.postEscrowCFU = postEscrowCFU;
self.postRepairCFU = postRepairCFU;
self.resetOctagon = resetOctagon;
+ self.leaveTrust = leaveTrust;
if (resetOctagon) {
secnotice("octagon-health", "Resetting Octagon as per Cuttlefish request");
self.nextState = OctagonStateHealthCheckReset;
+ } else if(leaveTrust) {
+ secnotice("octagon-health", "Leaving clique as per Cuttlefish request");
+ self.nextState = OctagonStateHealthCheckLeaveClique;
} else {
self.nextState = self.intendedState;
}
#import "keychain/ot/OTConstants.h"
#import "keychain/ot/OTClientStateMachine.h"
-#import "keychain/ot/OTPrepareOperation.h"
#import "keychain/ot/OTSOSAdapter.h"
#import "keychain/ot/OTEpochOperation.h"
#import "keychain/ot/OTClientVoucherOperation.h"
#import <Security/SecureObjectSync/SOSTypes.h>
#import <Security/OTConstants.h>
+typedef NS_ENUM(NSInteger, OTCDPStatus) {
+ OTCDPStatusUnknown = 0,
+ OTCDPStatusDisabled = 1,
+ OTCDPStatusEnabled = 2,
+};
+
NS_ASSUME_NONNULL_BEGIN
NSString* OTCliqueStatusToString(CliqueStatus status);
CliqueStatus OTCliqueStatusFromString(NSString* str);
+NSString* OTCDPStatusToString(OTCDPStatus status);
@class KCPairingChannelContext;
@class KCPairingChannel;
@class OTPairingChannel;
@class OTPairingChannelContext;
@class OTControl;
+@class CKKSControl;
extern NSString* kSecEntitlementPrivateOctagonEscrow;
@interface OTConfigurationContext : NSObject
-@property (nonatomic, copy, nullable) NSString* context;
-@property (nonatomic, copy) NSString* dsid;
-@property (nonatomic, copy) NSString* altDSID;
+@property (nonatomic, copy) NSString* context;
+@property (nonatomic, copy, nullable) NSString* dsid;
+@property (nonatomic, copy, nullable) NSString* altDSID;
@property (nonatomic, strong, nullable) SFSignInAnalytics* analytics;
+@property (nonatomic, copy, nullable) NSString* authenticationAppleID;
+@property (nonatomic, copy, nullable) NSString* passwordEquivalentToken;
// Use this to inject your own OTControl object. It must be configured as synchronous.
@property (nullable, strong) OTControl* otControl;
+
+// Use this to inject your own CKKSControl object. It must be configured as synchronous.
+@property (nullable, strong) CKKSControl* ckksControl;
+
// Use this to inject your own SecureBackup object. It must conform to the OctagonEscrowRecoverer protocol.
@property (nullable, strong) id sbd;
/* *
* @abstract, initializes a clique object given a context. A clique object enables octagon trust operations for a given context and dsid.
- * @param ctx, a unique string that is used as a way to retrieve current trust state
+ * @param ctx, a collection of arguments describing the world
* @return an instance of octagon trust
*/
-- (instancetype _Nullable)initWithContextData:(OTConfigurationContext *)ctx error:(NSError * __autoreleasing * _Nonnull)error;
+- (instancetype)initWithContextData:(OTConfigurationContext *)ctx;
+
+/*
+ * Much like initWithContextData, but might fail. There are currently no failures possible.
+ */
+- (instancetype _Nullable)initWithContextData:(OTConfigurationContext *)ctx error:(NSError**)error __deprecated_msg("Use initWithContextData instead");
/* *
* @abstract Establish a new clique, reset protected data
/* *
* @abstract Create pairing channel with
*
- * @param ctx, context containing parameters to setup OTClique
- * @param pairingChannelContext, context containing parameters to setup the pairing channel as the initiator
- * @return clique, An instance of an OTClique
- * @return error, error gets filled if something goes horribly wrong
+ * @param ctx, context containing parameters to setup the pairing channel as the initiator
+ * @return KCPairingChannel, An instance of a KCPairingCHannel
*/
- (KCPairingChannel *)setupPairingChannelAsInitiator:(KCPairingChannelContext *)ctx;
/* *
* @abstract Configure this peer as the acceptor during piggybacking
*
- * @param ctx, context containing parameters to setup OTClique
- * @param pairingChannelContext, context containing parameters to setup the pairing channel as the acceptor
- * @param error, error gets filled if something goes horribly wrong
- * @return KCPairingChannel, An instance of an OTClique
+ * @param ctx, context containing parameters to setup the pairing channel as the acceptor
+ * @return KCPairingChannel, An instance of a KCPairingChannel
*/
- (KCPairingChannel *)setupPairingChannelAsAcceptor:(KCPairingChannelContext *)ctx;
- (NSDictionary<NSString*,NSString*>* _Nullable)peerDeviceNamesByPeerID:(NSError * __autoreleasing *)error;
+/*
+ * CDP bit handling
+ */
+
++ (BOOL)setCDPEnabled:(OTConfigurationContext*)arguments
+ error:(NSError* __autoreleasing*)error;
+
++ (OTCDPStatus)getCDPStatus:(OTConfigurationContext*)arguments
+ error:(NSError* __autoreleasing *)error;
/* SOS glue */
// CoreCDP will call this function when they are upgrading an account from SA to HSA2
- (BOOL)waitForOctagonUpgrade:(NSError** _Nullable)error;
+
+/*
+* @abstract CoreCDP to call this function when they need to reset protected data.
+* This routine resets all circles, creates a new octagon and sos circle, then puts this device into each circle.
+* This routine does not create a new escrow record
+* This routine will need ensure OTConfigurationContext contains appleID and passwordEquivalentToken to delete all CDP records
+* @param data The OTClique configuration data
+* @param error Reports any error along the process
+* @return a new clique
+*/
++ (OTClique* _Nullable)resetProtectedData:(OTConfigurationContext*)data error:(NSError**)error;
@end
NS_ASSUME_NONNULL_END
#import <CloudServices/SecureBackup.h>
#import <CloudServices/SecureBackupConstants.h>
#import "keychain/ot/OTControl.h"
+#import "keychain/ckks/CKKSControl.h"
#import "keychain/ot/categories/OctagonEscrowRecoverer.h"
SOFT_LINK_FRAMEWORK(PrivateFrameworks, KeychainCircle);
SOFT_LINK_CLASS(KeychainCircle, OTPairingChannel);
SOFT_LINK_CLASS(CloudServices, SecureBackup);
SOFT_LINK_CONSTANT(CloudServices, kSecureBackupErrorDomain, NSErrorDomain);
+SOFT_LINK_CONSTANT(CloudServices, kSecureBackupAuthenticationAppleID, NSString*);
+SOFT_LINK_CONSTANT(CloudServices, kSecureBackupAuthenticationPassword, NSString*);
+SOFT_LINK_CONSTANT(CloudServices, kSecureBackupiCloudDataProtectionDeleteAllRecordsKey, NSString*);
+SOFT_LINK_CONSTANT(CloudServices, kSecureBackupContainsiCDPDataKey, NSString*);
#pragma clang diagnostic pop
#endif
return CliqueStatusError;
}
+NSString* OTCDPStatusToString(OTCDPStatus status) {
+ switch(status) {
+ case OTCDPStatusUnknown:
+ return @"unknown";
+ case OTCDPStatusDisabled:
+ return @"disabled";
+ case OTCDPStatusEnabled:
+ return @"enabled";
+ }
+}
+
@implementation OTConfigurationContext
- (OTControl* _Nullable)makeOTControl:(NSError**)error
return nil;
#endif
}
+
+- (CKKSControl* _Nullable)makeCKKSControl:(NSError**)error
+{
+#if OCTAGON
+ if(self.ckksControl) {
+ return self.ckksControl;
+ }
+ return [CKKSControl CKKSControlObject:true error:error];
+#else
+ return nil;
+#endif
+}
+
+- (instancetype)init
+{
+ if((self = [super init])) {
+ _context = OTDefaultContext;
+ }
+ return self;
+}
@end
@implementation OTBottleIDs
}
- (instancetype)initWithContextData:(OTConfigurationContext *)ctx error:(NSError * __autoreleasing *)error
+{
+ return [self initWithContextData:ctx];
+}
+
+- (instancetype)initWithContextData:(OTConfigurationContext *)ctx
{
#if OCTAGON
self = [super init];
_ctx.altDSID = [ctx.altDSID copy];
_ctx.analytics = ctx.analytics;
_ctx.otControl = ctx.otControl;
+ _ctx.ckksControl = ctx.ckksControl;
self.defaults = [NSMutableDictionary dictionary];
}
return self;
#else
NSAssert(false, @"OTClique is not implemented on this platform");
- return nil;
+
+ // make the build analyzer happy
+ self = [super init];
+ return self;
#endif // OCTAGON
}
SOSPeerInfoRef me = SOSCCCopyMyPeerInfo(&error);
retPeerID = (NSString*)CFBridgingRelease(CFRetainSafe(SOSPeerInfoGetPeerID(me)));
CFReleaseNull(me);
+ CFBridgingRelease(error);
}
secnotice("clique", "cliqueMemberIdentifier complete: %@", retPeerID);
if(operationError) {
secnotice("clique-establish", "establish returned an error: %@", operationError);
}
- success = !!operationError;
+ success = operationError == nil;
localError = operationError;
}];
if(operationError) {
secnotice("clique-resetandestablish", "resetAndEstablish returned an error: %@", operationError);
}
- success = !!operationError;
+ success = operationError == nil;
localError = operationError;
}];
bool subTaskSuccess = false;
OctagonSignpost performEscrowRecoverySignpost = OctagonSignpostBegin(OctagonSignpostNameMakeNewFriends);
- OTClique* clique = [[OTClique alloc] initWithContextData:data error:error];
+ OTClique* clique = [[OTClique alloc] initWithContextData:data];
if(OctagonIsEnabled()) {
NSError* localError = nil;
if([OTClique platformSupportsSOS]) {
CFErrorRef resetError = NULL;
- NSData* analyticsData = nil;
- if(data.analytics) {
- NSError* encodingError = nil;
- analyticsData = [NSKeyedArchiver archivedDataWithRootObject:data.analytics requiringSecureCoding:YES error:&encodingError];
-
- if(encodingError) {
- secnotice("clique-newfriends", "newFriendsWithContextData: unable to serialize analytics: %@", encodingError);
- }
- }
-
result = SOSCCResetToOffering(&resetError);
if(!result || resetError){
secnotice("clique-newfriends", "newFriendsWithContextData: resetToOffering failed: %@", resetError);
if(error) {
*error = CFBridgingRelease(resetError);
+ } else {
+ CFBridgingRelease(resetError);
}
OctagonSignpostEnd(performEscrowRecoverySignpost, OctagonSignpostNameMakeNewFriends, OctagonSignpostNumber1(OctagonSignpostNameMakeNewFriends), (int)subTaskSuccess);
return nil;
OctagonSignpost performEscrowRecoverySignpost = OctagonSignpostBegin(OctagonSignpostNamePerformEscrowRecovery);
bool subTaskSuccess = false;
NSError* localError = nil;
- OTClique* clique = [[OTClique alloc] initWithContextData:data
- error:&localError];
- if(!clique || localError) {
- secnotice("clique-recovery", "unable to create otclique: %@", localError);
- if(error) {
- *error = localError;
- }
- OctagonSignpostEnd(performEscrowRecoverySignpost, OctagonSignpostNamePerformEscrowRecovery, OctagonSignpostNumber1(OctagonSignpostNamePerformEscrowRecovery), (int)subTaskSuccess);
- return nil;
- }
+ OTClique* clique = [[OTClique alloc] initWithContextData:data];
// Attempt the recovery from sbd
secnotice("clique-recovery", "attempting an escrow recovery for context:%@, altdsid:%@", data.context, data.altDSID);
} else {
secnotice("clique-recovery", "resetting SOS circle successful");
}
+ CFBridgingRelease(blowItAwayError);
} else {
secnotice("clique-recovery", "Legacy restore failed on a non-SOS platform");
}
}
BOOL setCredentialsResult = result ? YES : NO;
+ secnotice("clique-legacy", "setUserCredentialsAndDSID results: %d %@", setCredentialsResult, setCredentialsErrorRef);
if (error) {
*error = (NSError*)CFBridgingRelease(setCredentialsErrorRef);
} else {
CFBridgingRelease(setCredentialsErrorRef);
}
- secnotice("clique-legacy", "setUserCredentialsAndDSID results: %d %@", setCredentialsResult, setCredentialsErrorRef);
subTaskSuccess = result;
OctagonSignpostEnd(signPost, OctagonSignpostNameSetUserCredentialsAndDSID, OctagonSignpostNumber1(OctagonSignpostNameSetUserCredentialsAndDSID), (int)subTaskSuccess);
&tryCredentialsErrorRef);
BOOL tryCredentialsResult = result ? YES : NO;
+ secnotice("clique-legacy", "tryUserCredentialsAndDSID results: %d %@", tryCredentialsResult, tryCredentialsErrorRef);
if (error) {
*error = (NSError*)CFBridgingRelease(tryCredentialsErrorRef);
} else {
CFBridgingRelease(tryCredentialsErrorRef);
}
- secnotice("clique-legacy", "tryUserCredentialsAndDSID results: %d %@", tryCredentialsResult, tryCredentialsErrorRef);
subTaskSuccess = result;
OctagonSignpostEnd(signPost, OctagonSignpostNameTryUserCredentialsAndDSID, OctagonSignpostNumber1(OctagonSignpostNameTryUserCredentialsAndDSID), (int)subTaskSuccess);
return tryCredentialsResult;
NSArray* peerList = (result ? (NSArray*)(CFBridgingRelease(result)) : nil);
+ secnotice("clique-legacy", "copyPeerPeerInfo results: %@ (%@)", peerList, copyPeerErrorRef);
if (error) {
*error = (NSError*)CFBridgingRelease(copyPeerErrorRef);
} else {
CFBridgingRelease(copyPeerErrorRef);
}
- secnotice("clique-legacy", "copyPeerPeerInfo results: %@", peerList);
subTaskSuccess = (peerList != nil) ? true : false;
OctagonSignpostEnd(signPost, OctagonSignpostNameCopyPeerPeerInfo, OctagonSignpostNumber1(OctagonSignpostNameCopyPeerPeerInfo), (int)subTaskSuccess);
return peerList;
if(result){
viewsEnabledResult = CFBooleanGetValue(result) ? YES : NO;
}
+ secnotice("clique-legacy", "peersHaveViewsEnabled results: %@ (%@)", viewsEnabledResult ? @"YES" : @"NO",
+ viewsEnabledErrorRef);
if (error) {
*error = (NSError*)CFBridgingRelease(viewsEnabledErrorRef);
} else {
CFBridgingRelease(viewsEnabledErrorRef);
}
- secnotice("clique-legacy", "peersHaveViewsEnabled results: %@", viewsEnabledResult ? @"YES" : @"NO");
subTaskSuccess = viewsEnabledResult ? true : false;
OctagonSignpostEnd(signPost, OctagonSignpostNamePeersHaveViewsEnabled, OctagonSignpostNumber1(OctagonSignpostNamePeersHaveViewsEnabled), (int)subTaskSuccess);
return viewsEnabledResult;
- (BOOL)requestToJoinCircle:(NSError *__autoreleasing*)error
{
bool result = false;
- CFErrorRef joinErrorRef = NULL;
bool subTaskSuccess = false;
OctagonSignpost signPost = OctagonSignpostBegin(OctagonSignpostNameRequestToJoinCircle);
// If we didn't early-exit, and we aren't going to invoke SOS below, we succeeded.
if(!OctagonPlatformSupportsSOS()) {
- secnotice("clique-legacy", "requestToJoinCircle results: %d %@", result, joinErrorRef);
+ secnotice("clique-legacy", "requestToJoinCircle platform does not support SOS");
subTaskSuccess = true;
OctagonSignpostEnd(signPost, OctagonSignpostNameRequestToJoinCircle, OctagonSignpostNumber1(OctagonSignpostNameRequestToJoinCircle), (int)subTaskSuccess);
return YES;
if([OTClique platformSupportsSOS]) {
NSData* analyticsData = nil;
+ CFErrorRef joinErrorRef = NULL;
if(self.ctx.analytics){
NSError* encodingError = nil;
analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
}
secnotice("clique-legacy", "sos requestToJoinCircle complete: %d %@", result, joinErrorRef);
+ if (error) {
+ *error = (NSError*)CFBridgingRelease(joinErrorRef);
+ } else {
+ CFBridgingRelease(joinErrorRef);
+ }
}
- if (error) {
- *error = (NSError*)CFBridgingRelease(joinErrorRef);
- } else {
- CFBridgingRelease(joinErrorRef);
- }
subTaskSuccess = result;
OctagonSignpostEnd(signPost, OctagonSignpostNameRequestToJoinCircle, OctagonSignpostNumber1(OctagonSignpostNameRequestToJoinCircle), (int)subTaskSuccess);
[self performedCDPStateMachineRun:type success:YES error:nil reply:reply];
}
++ (BOOL)setCDPEnabled:(OTConfigurationContext*)arguments
+ error:(NSError* __autoreleasing*)error
+{
+#if OCTAGON
+ NSError *controlError = nil;
+ OTControl* control = [arguments makeOTControl:&controlError];
+ if (!control) {
+ secerror("octagon-setcdpenabled: failed to fetch OTControl object: %@", controlError);
+ if (error) {
+ *error = controlError;
+ }
+ return NO;
+ }
+
+ __block NSError* reterror = nil;
+
+ [control setCDPEnabled:nil
+ contextID:arguments.context
+ reply:^(NSError * _Nullable resultError) {
+ if(resultError) {
+ secnotice("octagon-setcdpenabled", "failed to set CDP bit: %@", resultError);
+ reterror = resultError;
+ } else {
+ secnotice("octagon-setcdpenabled", "successfully set CDP bit");
+ }
+ }];
+
+ if(reterror && error) {
+ *error = reterror;
+ }
+
+ return (reterror == nil);
+#else // !OCTAGON
+ if(error) {
+ *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
+ }
+ return NO;
+#endif
+}
+
++ (OTCDPStatus)getCDPStatus:(OTConfigurationContext*)arguments
+ error:(NSError* __autoreleasing *)error
+{
+#if OCTAGON
+ NSError *controlError = nil;
+ OTControl* control = [arguments makeOTControl:&controlError];
+ if (!control) {
+ secerror("octagon-cdp-status: failed to fetch OTControl object: %@", controlError);
+ if (error) {
+ *error = controlError;
+ }
+ return OTCDPStatusUnknown;
+ }
+
+ __block NSError* reterror = nil;
+ __block OTCDPStatus retcdpstatus = OTCDPStatusUnknown;
+
+ [control getCDPStatus:nil
+ contextID:arguments.context
+ reply:^(OTCDPStatus status, NSError * _Nullable resultError) {
+ if(resultError) {
+ secnotice("octagon-cdp-status", "failed to fetch CDP status: %@", resultError);
+ reterror = resultError;
+
+ } else {
+ secnotice("octagon-cdp-status", "successfully fetched CDP status as %@", OTCDPStatusToString(status));
+ retcdpstatus = status;
+ }
+ }];
+
+ if(reterror && error) {
+ *error = reterror;
+ }
+
+ return retcdpstatus;
+#else // !OCTAGON
+ if(error) {
+ *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
+ }
+ return OTCDPStatusDisabled;
+#endif
+}
+
++ (OTClique* _Nullable)resetProtectedData:(OTConfigurationContext*)data error:(NSError**)error
+{
+#if OCTAGON
+ NSError *controlError = nil;
+ OTControl *control = [data makeOTControl:&controlError];
+ if (!control) {
+ secerror("clique-reset-protected-data: unable to create otcontrol: %@", controlError);
+ if (error) {
+ *error = controlError;
+ }
+ return nil;
+ }
+
+ __block NSError* localError = nil;
+
+ //delete all records
+ id<OctagonEscrowRecovererPrococol> sb = data.sbd ?: [[getSecureBackupClass() alloc] init];
+
+ NSDictionary* deletionInformation = @{ getkSecureBackupAuthenticationAppleID() : data.authenticationAppleID,
+ getkSecureBackupAuthenticationPassword() : data.passwordEquivalentToken,
+ getkSecureBackupiCloudDataProtectionDeleteAllRecordsKey() : @YES,
+ getkSecureBackupContainsiCDPDataKey() : @YES};
+
+ NSError* sbError = [sb disableWithInfo:deletionInformation];
+ if(sbError) {
+ secerror("clique-reset-protected-data: secure backup escrow record deletion failed: %@", sbError);
+ if(error) {
+ *error = sbError;
+ }
+ return nil;
+ } else {
+ secnotice("clique-reset-protected-data", "sbd disableWithInfo succeeded");
+ }
+
+ //reset sos
+ if(OctagonPlatformSupportsSOS()) {
+ //reset SOS
+ CFErrorRef sosError = NULL;
+ bool resetSuccess = SOSCCResetToOffering(&sosError);
+
+ if(sosError || !resetSuccess) {
+ secerror("clique-reset-protected-data: sos reset failed: %@", sosError);
+ if(error) {
+ *error = (NSError*)CFBridgingRelease(sosError);
+ }
+ return nil;
+ } else {
+ secnotice("clique-reset-protected-data", "sos reset succeeded");
+ }
+ } else {
+ secnotice("clique-reset-protected-data", "platform does not support sos");
+ }
+
+ //reset octagon
+ OTClique* clique = [[OTClique alloc] initWithContextData:data];
+ if(OctagonIsEnabled()) {
+ [clique resetAndEstablish:CuttlefishResetReasonUserInitiatedReset error:&localError];
+
+ if(localError) {
+ secerror("clique-reset-protected-data: account reset failed: %@", localError);
+ if(error) {
+ *error = localError;
+ }
+ return nil;
+ } else {
+ secnotice("clique-reset-protected-data", "Octagon account reset succeeded");
+ }
+ }
+
+ //reset ckks
+ CKKSControl* ckksControl = [data makeCKKSControl:&localError];
+ [ckksControl rpcResetCloudKit:nil reason:@"clique-reset-protected-data" reply:^(NSError* resetError){
+ localError = resetError;
+ }];
+
+ if(localError) {
+ secerror("clique-reset-protected-data: ckks cloudkit reset failed: %@", localError);
+ if(error) {
+ *error = localError;
+ }
+ return nil;
+ } else {
+ secnotice("clique-reset-protected-data", "ckks cloudkit reset succeeded");
+ }
+
+ return clique;
+#else // !OCTAGON
+ if(error) {
+ *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
+ }
+ return nil;
+#endif
+
+}
+
@end
#endif /* OBJC2 */
NSError * _Nullable error))reply;
- (void)rpcPrepareIdentityAsApplicantWithConfiguration:(OTJoiningConfiguration*)config
- reply:(void (^)(NSString * _Nullable peerID,
- NSData * _Nullable permanentInfo,
- NSData * _Nullable permanentInfoSig,
- NSData * _Nullable stableInfo,
- NSData * _Nullable stableInfoSig,
- NSError * _Nullable error))reply;
+ reply:(void (^)(NSString * _Nullable peerID,
+ NSData * _Nullable permanentInfo,
+ NSData * _Nullable permanentInfoSig,
+ NSData * _Nullable stableInfo,
+ NSData * _Nullable stableInfoSig,
+ NSError * _Nullable error))reply;
- (void)rpcVoucherWithConfiguration:(OTJoiningConfiguration*)config
peerID:(NSString*)peerID
permanentInfo:(NSData *)permanentInfo
- (void)rpcJoinWithConfiguration:(OTJoiningConfiguration*)config
vouchData:(NSData*)vouchData
vouchSig:(NSData*)vouchSig
- preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
reply:(void (^)(NSError * _Nullable error))reply;
radar:(NSString *)radar
reply:(void (^)(NSError* _Nullable error))reply;
+- (void)setCDPEnabled:(NSString* _Nullable)containerName
+ contextID:(NSString*)contextID
+ reply:(void (^)(NSError* _Nullable error))reply;
+
+- (void)getCDPStatus:(NSString* _Nullable)containerName
+ contextID:(NSString*)contextID
+ reply:(void (^)(OTCDPStatus status, NSError* _Nullable error))reply;
+
+- (void)refetchCKKSPolicy:(NSString* _Nullable)containerName
+ contextID:(NSString*)contextID
+ reply:(void (^)(NSError* _Nullable error))reply;
+
@end
NS_ASSUME_NONNULL_END
- (void)rpcJoinWithConfiguration:(OTJoiningConfiguration*)config
vouchData:(NSData*)vouchData
vouchSig:(NSData*)vouchSig
- preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
reply:(void (^)(NSError * _Nullable error))reply
{
#if OCTAGON
[[self getConnection: ^(NSError* error) {
reply(error);
- }] rpcJoinWithConfiguration:config vouchData:vouchData vouchSig:vouchSig preapprovedKeys:preapprovedKeys reply:^(NSError* e) {
+ }] rpcJoinWithConfiguration:config vouchData:vouchData vouchSig:vouchSig reply:^(NSError* e) {
reply(e);
}];
#else
}] tapToRadar:action description:description radar:radar reply:reply];
}
+- (void)refetchCKKSPolicy:(NSString* _Nullable)container
+ contextID:(NSString*)contextID
+ reply:(void (^)(NSError* _Nullable error))reply
+{
+ [[self getConnection: ^(NSError* error) {
+ reply(error);
+ }] refetchCKKSPolicy:container contextID:contextID reply:reply];
+}
+
+- (void)setCDPEnabled:(NSString* _Nullable)containerName
+ contextID:(NSString*)contextID
+ reply:(void (^)(NSError* _Nullable error))reply
+{
+ [[self getConnection: ^(NSError* connectionError) {
+ reply(connectionError);
+ }] setCDPEnabled:containerName contextID:contextID reply:reply];
+}
+
+- (void)getCDPStatus:(NSString* _Nullable)containerName
+ contextID:(NSString*)contextID
+ reply:(void (^)(OTCDPStatus status, NSError* _Nullable error))reply
+{
+ [[self getConnection: ^(NSError* connectionError) {
+ reply(OTCDPStatusUnknown, connectionError);
+ }] getCDPStatus:containerName contextID:contextID reply:reply];
+}
+
+ (OTControl*)controlObject:(NSError* __autoreleasing *)error {
return [OTControl controlObject:false error:error];
}
- (void)rpcJoinWithConfiguration:(OTJoiningConfiguration*)config
vouchData:(NSData*)vouchData
vouchSig:(NSData*)vouchSig
- preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
reply:(void (^)(NSError * _Nullable error))reply;
- (void)preflightBottledPeer:(NSString*)contextID
radar:(NSString *)radar
reply:(void (^)(NSError* _Nullable error))reply;
+- (void)refetchCKKSPolicy:(NSString* _Nullable)container
+ contextID:(NSString*)contextID
+ reply:(void (^)(NSError* _Nullable error))reply;
+
+- (void)setCDPEnabled:(NSString* _Nullable)containerName
+ contextID:(NSString*)contextID
+ reply:(void (^)(NSError* _Nullable error))reply;
+
+- (void)getCDPStatus:(NSString* _Nullable)containerName
+ contextID:(NSString*)contextID
+ reply:(void (^)(OTCDPStatus status, NSError* _Nullable error))reply;
+
@end
NSXPCInterface* OTSetupControlProtocol(NSXPCInterface* interface);
argumentIndex:2
ofReply:YES];
[interface setClasses:errorClasses
- forSelector:@selector(rpcJoinWithConfiguration:vouchData:vouchSig:preapprovedKeys:reply:)
+ forSelector:@selector(rpcJoinWithConfiguration:vouchData:vouchSig:reply:)
argumentIndex:0
ofReply:YES];
#endif /* __OBJC2__ */
#import <dispatch/dispatch.h>
#import "keychain/ot/proto/generated_source/OTAccountMetadataClassC.h"
+#import "keychain/ot/categories/OTAccountMetadataClassC+KeychainSupport.h"
extern NSString* _Nonnull OTCuttlefishContextErrorDomain;
typedef NS_ENUM(uint32_t, OTCuttlefishContextErrors) {
- (BOOL)persistNewEpoch:(uint64_t)epoch error:(NSError**)error;
-- (BOOL)persistAccountChanges:(OTAccountMetadataClassC* (^)(OTAccountMetadataClassC* metadata))makeChanges
+- (BOOL)persistAccountChanges:(OTAccountMetadataClassC* _Nullable (^)(OTAccountMetadataClassC* metadata))makeChanges
error:(NSError**)error;
-- (BOOL)_onqueuePersistAccountChanges:(OTAccountMetadataClassC* (^)(OTAccountMetadataClassC* metadata))makeChanges
+- (BOOL)_onqueuePersistAccountChanges:(OTAccountMetadataClassC* _Nullable (^)(OTAccountMetadataClassC* metadata))makeChanges
error:(NSError**)error;
- (NSDate *)lastHealthCheckupDate:(NSError * _Nullable *)error;
} error:error];
}
-- (BOOL)persistAccountChanges:(OTAccountMetadataClassC* (^)(OTAccountMetadataClassC*))makeChanges
+- (BOOL)persistAccountChanges:(OTAccountMetadataClassC* _Nullable (^)(OTAccountMetadataClassC*))makeChanges
error:(NSError**)error
{
__block NSError* localError = nil;
}
newState = makeChanges([oldState copy]);
- if(![newState saveToKeychainForContainer:self.containerName contextID:self.contextID error:&localError]) {
+ if(newState && ![newState saveToKeychainForContainer:self.containerName contextID:self.contextID error:&localError]) {
newState = nil;
}
});
} error:error];
}
-- (BOOL)_onqueuePersistAccountChanges:(OTAccountMetadataClassC* (^)(OTAccountMetadataClassC* metadata))makeChanges
+- (BOOL)_onqueuePersistAccountChanges:(OTAccountMetadataClassC* _Nullable (^)(OTAccountMetadataClassC* metadata))makeChanges
error:(NSError**)error
{
__block NSError* localError = nil;
#import "keychain/ckks/CKKSCondition.h"
#import "keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h"
#import "OTDeviceInformation.h"
+#import "keychain/ot/OTConstants.h"
#import "keychain/ot/OTDefines.h"
#import "keychain/ot/OTClique.h"
#import "keychain/ot/OTFollowup.h"
@property (nonatomic, readonly) CKKSLockStateTracker *lockStateTracker;
@property (nonatomic, readonly) OTCuttlefishAccountStateHolder* accountMetadataStore;
@property (readonly) OctagonStateMachine* stateMachine;
-@property (readonly) BOOL postedRepairCFU;
-@property (readonly) BOOL postedEscrowRepairCFU;
-@property (readonly) BOOL postedRecoveryKeyCFU;
@property (nullable, nonatomic) CKKSNearFutureScheduler* apsRateLimiter;
@property (nullable, nonatomic) CKKSNearFutureScheduler* sosConsistencyRateLimiter;
@property (readonly, nullable) CKKSViewManager* viewManager;
// Dependencies (for injection)
+@property (readonly) id<OTDeviceInformationAdapter> deviceAdapter;
@property id<OTAuthKitAdapter> authKitAdapter;
@property dispatch_queue_t queue;
- (BOOL)accountNoLongerAvailable:(NSError**)error;
- (BOOL)idmsTrustLevelChanged:(NSError**)error;
+// Call these to manipulate the "CDP-ness" of the account
+// Note that there is no way to turn CDP back off again
+- (OTCDPStatus)getCDPStatus:(NSError* __autoreleasing *)error;
+- (BOOL)setCDPEnabled:(NSError* __autoreleasing *)error;
+
- (void)deviceNameUpdated;
- (void)startOctagonStateMachine;
NSError * _Nullable error))reply;
- (void)rpcJoin:(NSData*)vouchData
vouchSig:(NSData*)vouchSig
-preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
reply:(void (^)(NSError * _Nullable error))reply;
- (void)rpcResetAndEstablish:(CuttlefishResetReason)resetReason reply:(nonnull void (^)(NSError * _Nullable))reply;
NSError* _Nullable error))reply;
- (void)rpcSetRecoveryKey:(NSString*)recoveryKey reply:(void (^)(NSError * _Nullable error))reply;
+- (void)rpcRefetchCKKSPolicy:(void (^)(NSError * _Nullable error))reply;
+
- (void)requestTrustedDeviceListRefresh;
- (OTDeviceInformation*)prepareInformation;
- (void)waitForOctagonUpgrade:(void (^)(NSError* error))reply NS_SWIFT_NAME(waitForOctagonUpgrade(reply:));
-- (void)clearPendingCFUFlags;
-
- (BOOL)waitForReady:(int64_t)timeOffset;
// For testing.
-- (void)setPostedBool:(BOOL)posted;
- (OTAccountMetadataClassC_AccountState)currentMemoizedAccountState;
- (OTAccountMetadataClassC_TrustState)currentMemoizedTrustState;
- (NSDate* _Nullable) currentMemoizedLastHealthCheck;
- (void) checkTrustStatusAndPostRepairCFUIfNecessary:(void (^ _Nullable)(CliqueStatus status, BOOL posted, BOOL hasIdentity, NSError * _Nullable error))reply;
- (void) setAccountStateHolder:(OTCuttlefishAccountStateHolder*)accountMetadataStore;
+- (void)clearCKKSViewManager;
+
+@property (nullable) TPPolicyVersion* policyOverride;
+
// Octagon Health Check Helpers
- (void)checkOctagonHealth:(BOOL)skipRateLimitingCheck reply:(void (^)(NSError * _Nullable error))reply;
- (BOOL)postRepairCFU:(NSError**)error;
- (void)postConfirmPasscodeCFU:(NSError**)error;
-- (void)postRecoveryKeyCFU:(NSError**)error;
// For reporting
- (BOOL)machineIDOnMemoizedList:(NSString*)machineID error:(NSError**)error NS_SWIFT_NOTHROW;
*/
#if OCTAGON
-#include <sys/sysctl.h>
-
+#import <CoreCDP/CDPAccount.h>
+#import <notify.h>
#import <os/feature_private.h>
-
#import <Security/Security.h>
-
-#include <utilities/SecFileLocations.h>
#include <Security/SecRandomP.h>
#import <SecurityFoundation/SFKey_Private.h>
+#include <sys/sysctl.h>
+#import <TrustedPeers/TrustedPeers.h>
+
#import "keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h"
-#import <TrustedPeers/TrustedPeers.h>
+
+#import "keychain/analytics/CKKSLaunchSequence.h"
+#import "keychain/categories/NSError+UsefulConstructors.h"
#import "keychain/ckks/CKKS.h"
+#import "keychain/ckks/CKKSAccountStateTracker.h"
#import "keychain/ckks/CKKSAnalytics.h"
+#import "keychain/ckks/CKKSKeychainView.h"
#import "keychain/ckks/CKKSResultOperation.h"
-
+#import "keychain/ckks/CKKSViewManager.h"
+#import "keychain/ckks/CloudKitCategories.h"
#import "keychain/ckks/OctagonAPSReceiver.h"
-#import "keychain/analytics/CKKSLaunchSequence.h"
-#import "keychain/ot/OTDeviceInformationAdapter.h"
-
-
-#import "keychain/ot/OctagonStateMachine.h"
-#import "keychain/ot/OctagonStateMachineHelpers.h"
-#import "keychain/ot/OctagonCKKSPeerAdapter.h"
-#import "keychain/ot/OctagonCheckTrustStateOperation.h"
-#import "keychain/ot/OTStates.h"
-#import "keychain/ot/OTFollowup.h"
+#import "keychain/escrowrequest/EscrowRequestServer.h"
#import "keychain/ot/OTAuthKitAdapter.h"
-#import "keychain/ot/OTConstants.h"
-#import "keychain/ot/OTOperationDependencies.h"
+#import "keychain/ot/OTCheckHealthOperation.h"
+#import "keychain/ot/OTClientVoucherOperation.h"
#import "keychain/ot/OTClique.h"
+#import "keychain/ot/OTConstants.h"
+#import "keychain/ot/OTCuttlefishAccountStateHolder.h"
#import "keychain/ot/OTCuttlefishContext.h"
-#import "keychain/ot/OTPrepareOperation.h"
-#import "keychain/ot/OTSOSAdapter.h"
-#import "keychain/ot/OTSOSUpgradeOperation.h"
-#import "keychain/ot/OTUpdateTPHOperation.h"
+#import "keychain/ot/OTDetermineCDPBitStatusOperation.h"
+#import "keychain/ot/OTDetermineHSA2AccountStatusOperation.h"
+#import "keychain/ot/OTDeviceInformationAdapter.h"
+#import "keychain/ot/OTEnsureOctagonKeyConsistency.h"
#import "keychain/ot/OTEpochOperation.h"
-#import "keychain/ot/OTClientVoucherOperation.h"
-#import "keychain/ot/OTLeaveCliqueOperation.h"
-#import "keychain/ot/OTRemovePeersOperation.h"
-#import "keychain/ot/OTJoinWithVoucherOperation.h"
-#import "keychain/ot/OTVouchWithBottleOperation.h"
-#import "keychain/ot/OTVouchWithRecoveryKeyOperation.h"
#import "keychain/ot/OTEstablishOperation.h"
+#import "keychain/ot/OTFetchViewsOperation.h"
+#import "keychain/ot/OTFollowup.h"
+#import "keychain/ot/OTJoinWithVoucherOperation.h"
+#import "keychain/ot/OTLeaveCliqueOperation.h"
#import "keychain/ot/OTLocalCKKSResetOperation.h"
-#import "keychain/ot/OTUpdateTrustedDeviceListOperation.h"
-#import "keychain/ot/OTSOSUpdatePreapprovalsOperation.h"
-#import "keychain/ot/OTResetOperation.h"
#import "keychain/ot/OTLocalCuttlefishReset.h"
-#import "keychain/ot/OTSetRecoveryKeyOperation.h"
+#import "keychain/ot/OTOperationDependencies.h"
+#import "keychain/ot/OTPrepareOperation.h"
+#import "keychain/ot/OTRemovePeersOperation.h"
#import "keychain/ot/OTResetCKKSZonesLackingTLKsOperation.h"
+#import "keychain/ot/OTResetOperation.h"
+#import "keychain/ot/OTSOSAdapter.h"
+#import "keychain/ot/OTSOSUpdatePreapprovalsOperation.h"
+#import "keychain/ot/OTSOSUpgradeOperation.h"
+#import "keychain/ot/OTSetCDPBitOperation.h"
+#import "keychain/ot/OTSetRecoveryKeyOperation.h"
+#import "keychain/ot/OTStates.h"
+#import "keychain/ot/OTTriggerEscrowUpdateOperation.h"
+#import "keychain/ot/OTUpdateTPHOperation.h"
+#import "keychain/ot/OTUpdateTrustedDeviceListOperation.h"
#import "keychain/ot/OTUploadNewCKKSTLKsOperation.h"
-#import "keychain/ot/OTCuttlefishAccountStateHolder.h"
+#import "keychain/ot/OTVouchWithBottleOperation.h"
+#import "keychain/ot/OTVouchWithRecoveryKeyOperation.h"
#import "keychain/ot/ObjCImprovements.h"
+#import "keychain/ot/OctagonCKKSPeerAdapter.h"
+#import "keychain/ot/OctagonCheckTrustStateOperation.h"
+#import "keychain/ot/OctagonStateMachine.h"
+#import "keychain/ot/OctagonStateMachineHelpers.h"
+#import "keychain/ot/categories/OTAccountMetadataClassC+KeychainSupport.h"
#import "keychain/ot/proto/generated_source/OTAccountMetadataClassC.h"
-#import "keychain/ot/OTTriggerEscrowUpdateOperation.h"
-#import "keychain/ot/OTCheckHealthOperation.h"
-#import "keychain/ot/OTEnsureOctagonKeyConsistency.h"
-#import "keychain/ot/OTDetermineHSA2AccountStatusOperation.h"
-#import "keychain/ckks/CKKSAccountStateTracker.h"
-#import "keychain/ckks/CloudKitCategories.h"
-#import "keychain/escrowrequest/EscrowRequestServer.h"
+#import "keychain/securityd/SOSCloudCircleServer.h"
+
+#import "utilities/SecFileLocations.h"
+#import "utilities/SecTapToRadar.h"
#if TARGET_OS_WATCH
#import "keychain/otpaird/OTPairingClient.h"
#endif /* TARGET_OS_WATCH */
-#import "keychain/ckks/CKKSViewManager.h"
-#import "keychain/ckks/CKKSKeychainView.h"
-#import "utilities/SecTapToRadar.h"
-#import "keychain/categories/NSError+UsefulConstructors.h"
-#import <CoreCDP/CDPAccount.h>
-#import <notify.h>
NSString* OTCuttlefishContextErrorDomain = @"otcuttlefish";
static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
NSString* _bottleID;
NSString* _bottleSalt;
NSData* _entropy;
- NSArray<NSData*>* _preapprovedKeys;
NSString* _recoveryKey;
CuttlefishResetReason _resetReason;
BOOL _skipRateLimitingCheck;
@property CKAccountInfo* cloudKitAccountInfo;
@property CKKSCondition *cloudKitAccountStateKnown;
-@property BOOL getViewsSuccess;
-
@property CKKSNearFutureScheduler* suggestTLKUploadNotifier;
+// Make writable
+@property (nullable) CKKSViewManager* viewManager;
+
// Dependencies (for injection)
@property id<OTSOSAdapter> sosAdapter;
@property id<CKKSPeerProvider> octagonAdapter;
-@property id<OTDeviceInformationAdapter> deviceAdapter;
@property (readonly) Class<OctagonAPSConnection> apsConnectionClass;
@property (readonly) Class<SecEscrowRequestable> escrowRequestClass;
-@property (nonatomic) BOOL postedRepairCFU;
-@property (nonatomic) BOOL postedEscrowRepairCFU;
-@property (nonatomic) BOOL postedRecoveryKeyCFU;
-
@property (nonatomic) BOOL initialBecomeUntrustedPosted;
@end
_contextID = contextID;
_viewManager = viewManager;
- _postedRepairCFU = NO;
- _postedRecoveryKeyCFU = NO;
- _postedEscrowRepairCFU = NO;
_initialBecomeUntrustedPosted = NO;
return self;
}
+- (void)clearCKKSViewManager
+{
+ self.viewManager = nil;
+}
+
- (void)dealloc
{
// TODO: how to invalidate this?
metadata.icloudAccountState = OTAccountMetadataClassC_AccountState_NO_ACCOUNT;
metadata.altDSID = nil;
metadata.trustState = OTAccountMetadataClassC_TrustState_UNKNOWN;
+ metadata.cdpState = OTAccountMetadataClassC_CDPState_UNKNOWN;
return metadata;
} error:&localError];
return YES;
}
+- (OTCDPStatus)getCDPStatus:(NSError*__autoreleasing*)error
+{
+ NSError* localError = nil;
+ OTAccountMetadataClassC* accountState = [self.accountMetadataStore loadOrCreateAccountMetadata:&localError];
+
+ if(localError) {
+ secnotice("octagon-cdp-status", "error fetching account metadata: %@", localError);
+ if(error) {
+ *error = localError;
+ }
+
+ return OTCDPStatusUnknown;
+ }
+
+ OTCDPStatus status = OTCDPStatusUnknown;
+ switch(accountState.cdpState) {
+ case OTAccountMetadataClassC_CDPState_UNKNOWN:
+ status = OTCDPStatusUnknown;
+ break;
+ case OTAccountMetadataClassC_CDPState_DISABLED:
+ status = OTCDPStatusDisabled;
+ break;
+ case OTAccountMetadataClassC_CDPState_ENABLED:
+ status = OTCDPStatusEnabled;
+ break;
+ }
+
+ secnotice("octagon-cdp-status", "current cdp status is: %@", OTCDPStatusToString(status));
+ return status;
+}
+
+- (BOOL)setCDPEnabled:(NSError* __autoreleasing *)error
+{
+ NSError* localError = nil;
+ [self.accountMetadataStore persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
+ metadata.cdpState = OTAccountMetadataClassC_CDPState_ENABLED;
+ return metadata;
+ } error:&localError];
+
+ [self.stateMachine handleFlag:OctagonFlagCDPEnabled];
+
+ if(localError) {
+ secerror("octagon-cdp-status: unable to persist CDP enablement: %@", localError);
+ if(error) {
+ *error = localError;
+ }
+ return NO;
+ }
+
+ secnotice("octagon-cdp-status", "Successfully set CDP status bit to 'enabled''");
+ return YES;
+}
+
- (void)resetOctagonStateMachine
{
OctagonStateTransitionOperation* op = [OctagonStateTransitionOperation named:@"resetting-state-machine"
- (NSDictionary*)establishStatePathDictionary
{
return @{
- OctagonStateReEnactDeviceList: @{
- OctagonStateReEnactPrepare: @{
- OctagonStateReEnactReadyToEstablish: @{
- OctagonStateEscrowTriggerUpdate: @{
- OctagonStateBecomeReady: @{
- OctagonStateReady: [OctagonStateTransitionPathStep success],
+ OctagonStateEstablishEnableCDPBit: @{
+ OctagonStateReEnactDeviceList: @{
+ OctagonStateReEnactPrepare: @{
+ OctagonStateReEnactReadyToEstablish: @{
+ OctagonStateEscrowTriggerUpdate: @{
+ OctagonStateBecomeReady: @{
+ OctagonStateReady: [OctagonStateTransitionPathStep success],
+ },
},
- },
- // Error handling extra states:
- OctagonStateEstablishCKKSReset: @{
- OctagonStateEstablishAfterCKKSReset: @{
- OctagonStateEscrowTriggerUpdate: @{
- OctagonStateBecomeReady: @{
- OctagonStateReady: [OctagonStateTransitionPathStep success],
+ // Error handling extra states:
+ OctagonStateEstablishCKKSReset: @{
+ OctagonStateEstablishAfterCKKSReset: @{
+ OctagonStateEscrowTriggerUpdate: @{
+ OctagonStateBecomeReady: @{
+ OctagonStateReady: [OctagonStateTransitionPathStep success],
+ },
},
},
},
return [self cloudKitAccountNewlyAvailableOperation];
}
+ if([currentState isEqualToString:OctagonStateDetermineCDPState]) {
+ return [[OTDetermineCDPBitStatusOperation alloc] initWithDependencies:self.operationDependencies
+ intendedState:OctagonStateCheckTrustState
+ errorState:OctagonStateWaitForCDP];
+ }
+
+ if([currentState isEqualToString:OctagonStateWaitForCDP]) {
+ if([flags _onqueueContains:OctagonFlagCDPEnabled]) {
+ [flags _onqueueRemoveFlag:OctagonFlagCDPEnabled];
+ secnotice("octagon", "CDP is newly available!");
+
+ return [OctagonStateTransitionOperation named:@"cdp_enabled"
+ entering:OctagonStateDetermineiCloudAccountState];
+
+ } else if([flags _onqueueContains:OctagonFlagCuttlefishNotification]) {
+ [flags _onqueueRemoveFlag:OctagonFlagCuttlefishNotification];
+ return [OctagonStateTransitionOperation named:@"cdp_enabled_push_received"
+ entering:OctagonStateWaitForCDPUpdated];
+
+ } else {
+ return nil;
+ }
+ }
+
+ if([currentState isEqualToString:OctagonStateWaitForCDPUpdated]) {
+ return [[OTUpdateTPHOperation alloc] initWithDependencies:self.operationDependencies
+ intendedState:OctagonStateDetermineCDPState
+ peerUnknownState:OctagonStateDetermineCDPState
+ errorState:OctagonStateError
+ retryFlag:OctagonFlagCuttlefishNotification];
+ }
+
if([currentState isEqualToString:OctagonStateCheckTrustState]) {
return [[OctagonCheckTrustStateOperation alloc] initWithDependencies:self.operationDependencies
intendedState:OctagonStateBecomeUntrusted
#pragma mark --- Octagon Health Check States
if([currentState isEqualToString:OctagonStateHSA2HealthCheck]) {
return [[OTDetermineHSA2AccountStatusOperation alloc] initWithDependencies:self.operationDependencies
- stateIfHSA2:OctagonStateSecurityTrustCheck
+ stateIfHSA2:OctagonStateCDPHealthCheck
stateIfNotHSA2:OctagonStateWaitForHSA2
stateIfNoAccount:OctagonStateNoAccount
errorState:OctagonStateError];
}
+ if([currentState isEqualToString:OctagonStateCDPHealthCheck]) {
+ return [[OTDetermineCDPBitStatusOperation alloc] initWithDependencies:self.operationDependencies
+ intendedState:OctagonStateSecurityTrustCheck
+ errorState:OctagonStateWaitForCDP];
+ }
+
if([currentState isEqualToString:OctagonStateSecurityTrustCheck]) {
return [self evaluateSecdOctagonTrust];
}
if([currentState isEqualToString:OctagonStateBecomeReady]) {
return [self becomeReadyOperation];
}
+
+ if([currentState isEqualToString:OctagonStateRefetchCKKSPolicy]) {
+ return [[OTFetchViewsOperation alloc] initWithDependencies:self.operationDependencies
+ intendedState:OctagonStateBecomeReady
+ errorState:OctagonStateError];
+ }
if([currentState isEqualToString:OctagonStateNoAccount]) {
// We only want to move out of untrusted if something useful has happened!
if([flags _onqueueContains:OctagonFlagIDMSLevelChanged]) {
[flags _onqueueRemoveFlag:OctagonFlagIDMSLevelChanged];
}
+
+ // We're untrusted; no need for the CDP level flag anymore
+ if([flags _onqueueContains:OctagonFlagCDPEnabled]) {
+ secnotice("octagon", "Removing 'CDP enabled' flag");
+ [flags _onqueueRemoveFlag:OctagonFlagCDPEnabled];
+ }
}
if([currentState isEqualToString:OctagonStateUntrustedUpdated]) {
return [[OTUpdateTPHOperation alloc] initWithDependencies:self.operationDependencies
intendedState:OctagonStateUntrusted
+ peerUnknownState:OctagonStateBecomeUntrusted
errorState:OctagonStateError
retryFlag:OctagonFlagCuttlefishNotification];
}
intendedState:OctagonStateBecomeReady
ckksConflictState:OctagonStateSOSUpgradeCKKSReset
errorState:OctagonStateBecomeUntrusted
- deviceInfo:self.prepareInformation];
+ deviceInfo:self.prepareInformation
+ policyOverride:self.policyOverride];
} else if([currentState isEqualToString:OctagonStateSOSUpgradeCKKSReset]) {
return [[OTLocalCKKSResetOperation alloc] initWithDependencies:self.operationDependencies
intendedState:OctagonStateBecomeReady
ckksConflictState:OctagonStateBecomeUntrusted
errorState:OctagonStateBecomeUntrusted
- deviceInfo:self.prepareInformation];
+ deviceInfo:self.prepareInformation
+ policyOverride:self.policyOverride];
} else if([currentState isEqualToString:OctagonStateCreateIdentityForRecoveryKey]) {
- OTPrepareOperation* op = [[OTPrepareOperation alloc] initWithDependencies:self.operationDependencies
- intendedState:OctagonStateVouchWithRecoveryKey
- errorState:OctagonStateBecomeUntrusted
- deviceInfo:[self prepareInformation]
- epoch:1];
- return op;
+ return [[OTPrepareOperation alloc] initWithDependencies:self.operationDependencies
+ intendedState:OctagonStateVouchWithRecoveryKey
+ errorState:OctagonStateBecomeUntrusted
+ deviceInfo:[self prepareInformation]
+ policyOverride:self.policyOverride
+ epoch:1];
- } else if([currentState isEqualToString:OctagonStateInitiatorCreateIdentity]) {
- OTPrepareOperation* op = [[OTPrepareOperation alloc] initWithDependencies:self.operationDependencies
- intendedState:OctagonStateInitiatorVouchWithBottle
- errorState:OctagonStateBecomeUntrusted
- deviceInfo:[self prepareInformation]
- epoch:1];
- return op;
+ } else if([currentState isEqualToString:OctagonStateBottleJoinCreateIdentity]) {
+ return [[OTPrepareOperation alloc] initWithDependencies:self.operationDependencies
+ intendedState:OctagonStateBottleJoinVouchWithBottle
+ errorState:OctagonStateBecomeUntrusted
+ deviceInfo:[self prepareInformation]
+ policyOverride:self.policyOverride
+ epoch:1];
+
+ } else if([currentState isEqualToString:OctagonStateBottleJoinVouchWithBottle]) {
+ // <rdar://problem/57768490> Octagon: ensure we use appropriate CKKS policy when joining octagon with future policy
+ // When we join with a bottle, we need to be sure that we've found all the TLKShares that we can reasonably unpack via the bottle
- } else if([currentState isEqualToString:OctagonStateInitiatorVouchWithBottle]) {
OTVouchWithBottleOperation* pendingOp = [[OTVouchWithBottleOperation alloc] initWithDependencies:self.operationDependencies
- intendedState:OctagonStateInitiatorUpdateDeviceList
+ intendedState:OctagonStateInitiatorSetCDPBit
errorState:OctagonStateBecomeUntrusted
bottleID:_bottleID
entropy:_entropy
} else if([currentState isEqualToString:OctagonStateVouchWithRecoveryKey]) {
OTVouchWithRecoveryKeyOperation* pendingOp = [[OTVouchWithRecoveryKeyOperation alloc] initWithDependencies:self.operationDependencies
- intendedState:OctagonStateInitiatorUpdateDeviceList
+ intendedState:OctagonStateInitiatorSetCDPBit
errorState:OctagonStateBecomeUntrusted
recoveryKey:_recoveryKey];
return pendingOp;
+ } else if([currentState isEqualToString:OctagonStateInitiatorSetCDPBit]) {
+ return [[OTSetCDPBitOperation alloc] initWithDependencies:self.operationDependencies
+ intendedState:OctagonStateInitiatorUpdateDeviceList
+ errorState:OctagonStateDetermineCDPState];
+
} else if([currentState isEqualToString:OctagonStateInitiatorUpdateDeviceList]) {
// As part of the 'initiate' flow, we need to update the trusted device list-you're probably on it already
OTUpdateTrustedDeviceListOperation* op = [[OTUpdateTrustedDeviceListOperation alloc] initWithDependencies:self.operationDependencies
ckksConflictState:OctagonStateInitiatorJoinCKKSReset
errorState:OctagonStateBecomeUntrusted
voucherData:_vouchData
- voucherSig:_vouchSig
- preapprovedKeys:_preapprovedKeys];
+ voucherSig:_vouchSig];
return op;
} else if([currentState isEqualToString:OctagonStateInitiatorJoinCKKSReset]) {
ckksConflictState:OctagonStateBecomeUntrusted
errorState:OctagonStateBecomeUntrusted
voucherData:_vouchData
- voucherSig:_vouchSig
- preapprovedKeys:_preapprovedKeys];
+ voucherSig:_vouchSig];
} else if([currentState isEqualToString:OctagonStateResetBecomeUntrusted]) {
return [self becomeUntrustedOperation:OctagonStateResetAndEstablish];
} else if([currentState isEqualToString:OctagonStateResetAnyMissingTLKCKKSViews]) {
return [[OTResetCKKSZonesLackingTLKsOperation alloc] initWithDependencies:self.operationDependencies
- intendedState:OctagonStateReEnactDeviceList
+ intendedState:OctagonStateEstablishEnableCDPBit
errorState:OctagonStateError];
+ } else if([currentState isEqualToString:OctagonStateEstablishEnableCDPBit]) {
+ return [[OTSetCDPBitOperation alloc] initWithDependencies:self.operationDependencies
+ intendedState:OctagonStateReEnactDeviceList
+ errorState:OctagonStateError];
+
} else if([currentState isEqualToString:OctagonStateReEnactDeviceList]) {
return [[OTUpdateTrustedDeviceListOperation alloc] initWithDependencies:self.operationDependencies
intendedState:OctagonStateReEnactPrepare
listUpdatesState:OctagonStateReEnactPrepare
- errorState:OctagonStateError
+ errorState:OctagonStateBecomeUntrusted
retryFlag:nil];
} else if([currentState isEqualToString:OctagonStateReEnactPrepare]) {
+ // <rdar://problem/56270219> Octagon: use epoch transmitted across pairing channel
// Note: Resetting the account returns epoch to 0.
return [[OTPrepareOperation alloc] initWithDependencies:self.operationDependencies
intendedState:OctagonStateReEnactReadyToEstablish
errorState:OctagonStateError
deviceInfo:[self prepareInformation]
+ policyOverride:self.policyOverride
epoch:0];
} else if([currentState isEqualToString:OctagonStateReEnactReadyToEstablish]) {
errorState:OctagonStateBecomeUntrusted];
} else if ([currentState isEqualToString:OctagonStateEscrowTriggerUpdate]){
-
return [[OTTriggerEscrowUpdateOperation alloc] initWithDependencies:self.operationDependencies
intendedState:OctagonStateBecomeReady
errorState:OctagonStateError];
+ } else if ([currentState isEqualToString:OctagonStateHealthCheckLeaveClique]) {
+ return [[OTLeaveCliqueOperation alloc] initWithDependencies: self.operationDependencies
+ intendedState: OctagonStateBecomeUntrusted
+ errorState: OctagonStateBecomeUntrusted];
+
} else if([currentState isEqualToString: OctagonStateWaitForUnlock]) {
if([flags _onqueueContains:OctagonFlagUnlocked]) {
[flags _onqueueRemoveFlag:OctagonFlagUnlocked];
[flags _onqueueRemoveFlag:OctagonFlagIDMSLevelChanged];
}
+ // We're ready; no need for the CDP level flag anymore
+ if([flags _onqueueContains:OctagonFlagCDPEnabled]) {
+ secnotice("octagon", "Removing 'CDP enabled' flag");
+ [flags _onqueueRemoveFlag:OctagonFlagCDPEnabled];
+ }
+
secnotice("octagon", "Entering state ready");
[[CKKSAnalytics logger] setDateProperty:[NSDate date] forKey:OctagonAnalyticsLastKeystateReady];
[self.launchSequence launch];
} else if([currentState isEqualToString:OctagonStateReadyUpdated]) {
return [[OTUpdateTPHOperation alloc] initWithDependencies:self.operationDependencies
intendedState:OctagonStateReady
+ peerUnknownState:OctagonStateBecomeUntrusted
errorState:OctagonStateError
retryFlag:OctagonFlagCuttlefishNotification];
- } else if ([currentState isEqualToString:OctagonStateError]) {
+ }
+
+ if ([currentState isEqualToString:OctagonStateError]) {
+ return nil;
}
return nil;
} else {
secnotice("octagon-health", "trust state (%@). checking in with TPH", [account trustStateAsString:account.trustState]);
- op.nextState = [self repairAccountIfTrustedByTPHWithIntededState:OctagonStateTPHTrustCheck errorState:OctagonStatePostRepairCFU];
+ op.nextState = [self repairAccountIfTrustedByTPHWithIntendedState:OctagonStateTPHTrustCheck];
}
}];
}
CKKSResultOperation* callback = [CKKSResultOperation named:@"rpcHealthCheck"
withBlock:^{
- secnotice("octagon-health", "Returning from cuttlefish trust check call: postRepairCFU(%d), postEscrowCFU(%d), resetOctagon(%d)",
- op.postRepairCFU, op.postEscrowCFU, op.resetOctagon);
+ secnotice("octagon-health", "Returning from cuttlefish trust check call: postRepairCFU(%d), postEscrowCFU(%d), resetOctagon(%d), leaveTrust(%d)",
+ op.postRepairCFU, op.postEscrowCFU, op.resetOctagon, op.leaveTrust);
if(op.postRepairCFU) {
secnotice("octagon-health", "Posting Repair CFU");
NSError* postRepairCFUError = nil;
} else {
secnotice("octagon-health", "Not posting confirm passcode CFU, already pending a prerecord upload");
}
+
}
+ if(op.leaveTrust){
+ secnotice("octagon-health", "Leaving Octagon and SOS trust");
+ NSError* leaveError = nil;
+ if(![self leaveTrust:&leaveError]) {
+ op.error = leaveError;
+ }
+ }
}];
[callback addDependency:op];
[self.operationQueue addOperation: callback];
{
WEAKIFY(self);
return [OctagonStateTransitionOperation named:@"octagon-icloud-account-available"
- intending:OctagonStateCheckTrustState
+ intending:OctagonStateDetermineCDPState
errorState:OctagonStateError
withBlockTakingSelf:^(OctagonStateTransitionOperation * _Nonnull op) {
STRONGIFY(self);
}];
}
-- (OctagonState*) repairAccountIfTrustedByTPHWithIntededState:(OctagonState*)intendedState errorState:(OctagonState*)errorState
+- (OctagonState*) repairAccountIfTrustedByTPHWithIntendedState:(OctagonState*)intendedState
{
__block OctagonState* nextState = intendedState;
//let's check in with TPH real quick to make sure it agrees with our local assessment
- secnotice("octagon-health", "repairAccountIfTrustedByTPHWithIntededState: calling into TPH for trust status");
+ secnotice("octagon-health", "repairAccountIfTrustedByTPHWithIntendedState: calling into TPH for trust status");
OTOperationConfiguration *config = [[OTOperationConfiguration alloc]init];
BOOL isExcluded,
NSError * _Nullable error) {
BOOL hasIdentity = egoPeerID != nil;
- secnotice("octagon-health", "repairAccountIfTrustedByTPHWithIntededState status: %ld, peerID: %@, isExcluded: %d error: %@", (long)status, egoPeerID, isExcluded, error);
+ secnotice("octagon-health", "repairAccountIfTrustedByTPHWithIntendedState status: %ld, peerID: %@, isExcluded: %d error: %@", (long)status, egoPeerID, isExcluded, error);
if (error) {
secnotice("octagon-health", "got an error from tph, returning to become_ready state: %@", error);
}
if(OctagonAuthoritativeTrustIsEnabled() && hasIdentity && status == CliqueStatusIn) {
- dispatch_semaphore_t sema = dispatch_semaphore_create(0);
- [self rpcStatus:^(NSDictionary *dump, NSError *dumpError) {
- if(dumpError) {
- secerror("octagon-health: error fetching ego peer id!: %@", dumpError);
- nextState = errorState;
- } else {
- NSDictionary* egoInformation = dump[@"self"];
- NSString* peerID = egoInformation[@"peerID"];
- NSError* persistError = nil;
- BOOL persisted = [self.accountMetadataStore persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
- metadata.trustState = OTAccountMetadataClassC_TrustState_TRUSTED;
- metadata.icloudAccountState = OTAccountMetadataClassC_AccountState_ACCOUNT_AVAILABLE;
- metadata.peerID = peerID;
- return metadata;
- } error:&persistError];
- if(!persisted || persistError) {
- secerror("octagon-health: couldn't persist results: %@", persistError);
- nextState = errorState;
- } else {
- secnotice("octagon-health", "added trusted identity to account metadata");
- nextState = intendedState;
- }
- }
- dispatch_semaphore_signal(sema);
- }];
- if (0 != dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 10))) {
- secerror("octagon: Timed out checking trust status");
+ secnotice("octagon-health", "TPH believes we're trusted, accepting ego peerID as %@", egoPeerID);
+
+ NSError* persistError = nil;
+ BOOL persisted = [self.accountMetadataStore persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
+ metadata.trustState = OTAccountMetadataClassC_TrustState_TRUSTED;
+ metadata.peerID = egoPeerID;
+ return metadata;
+ } error:&persistError];
+ if(!persisted || persistError) {
+ secerror("octagon-health: couldn't persist results: %@", persistError);
+ nextState = OctagonStateError;
+ } else {
+ secnotice("octagon-health", "added trusted identity to account metadata");
+ nextState = intendedState;
}
- } else if (OctagonAuthoritativeTrustIsEnabled() && (self.postedRepairCFU == NO) && hasIdentity && status != CliqueStatusIn){
- nextState = errorState;
+
+ } else if (OctagonAuthoritativeTrustIsEnabled() && hasIdentity && status != CliqueStatusIn){
+ secnotice("octagon-health", "TPH believes we're not trusted, requesting CFU post");
+ nextState = OctagonStatePostRepairCFU;
}
}];
return nextState;
}
-- (BOOL) didDeviceAttemptToJoinOctagon:(NSError**)error
-{
- NSError* fetchAttemptError = nil;
- OTAccountMetadataClassC_AttemptedAJoinState attemptedAJoin = [self.accountMetadataStore fetchPersistedJoinAttempt:&fetchAttemptError];
- if(fetchAttemptError) {
- secerror("octagon: failed to fetch data indicating device attempted to join octagon, assuming it did: %@", fetchAttemptError);
- if(error){
- *error = fetchAttemptError;
- }
- return YES;
- }
- BOOL attempted = YES;
- switch (attemptedAJoin) {
- case OTAccountMetadataClassC_AttemptedAJoinState_NOTATTEMPTED:
- attempted = NO;
- break;
- case OTAccountMetadataClassC_AttemptedAJoinState_ATTEMPTED:
- case OTAccountMetadataClassC_AttemptedAJoinState_UNKNOWN:
- default:
- break;
- }
- return attempted;
-}
-
- (void)checkTrustStatusAndPostRepairCFUIfNecessary:(void (^ _Nullable)(CliqueStatus status, BOOL posted, BOOL hasIdentity, NSError * _Nullable error))reply
{
WEAKIFY(self);
// On platforms with SOS, we only want to post a CFU if we've attempted to join at least once.
// This prevents us from posting a CFU, then performing an SOS upgrade and succeeding.
if(self.sosAdapter.sosEnabled) {
- NSError* fetchAttemptError = nil;
- BOOL attemptedToJoin = [self didDeviceAttemptToJoinOctagon:&fetchAttemptError];
- if(fetchAttemptError){
- secerror("octagon: failed to retrieve joining attempt information: %@", fetchAttemptError);
- attemptedToJoin = YES;
- }
+ NSError* fetchError = nil;
+ OTAccountMetadataClassC* accountState = [self.accountMetadataStore loadOrCreateAccountMetadata:&fetchError];
- if(!attemptedToJoin) {
- secnotice("octagon", "SOS is enabled and we haven't attempted to join; not posting CFU");
- reply(status, NO, hasIdentity, nil);
- return;
+ if(!accountState || fetchError){
+ secerror("octagon: failed to retrieve joining attempt information: %@", fetchError);
+ // fall through to below: posting the CFU is better than a false negative
+
+ } else if(accountState.attemptedJoin == OTAccountMetadataClassC_AttemptedAJoinState_ATTEMPTED) {
+ // Normal flow, fall through to below
+ } else {
+ // Triple-check with SOS: if it's in a bad state, post the CFU anyway
+ secnotice("octagon", "SOS is enabled and we haven't attempted to join; checking with SOS");
+
+ NSError* circleError = nil;
+ SOSCCStatus sosStatus = [self.sosAdapter circleStatus:&circleError];
+
+ if(circleError && [circleError.domain isEqualToString:(__bridge NSString*)kSOSErrorDomain] && circleError.code == kSOSErrorNotReady) {
+ secnotice("octagon", "SOS is not ready, not posting CFU until it becomes so");
+ reply(status, NO, hasIdentity, nil);
+ return;
+
+ } else if(circleError) {
+ // Any other error probably indicates that there is some circle, but we're not in it
+ secnotice("octagon", "SOS is in an unknown error state, posting CFU: %@", circleError);
+
+ } else if(sosStatus == kSOSCCInCircle) {
+ secnotice("octagon", "SOS is InCircle, not posting CFU");
+ reply(status, NO, hasIdentity, nil);
+ return;
+ } else {
+ secnotice("octagon", "SOS is %@, posting CFU", (__bridge NSString*)SOSCCGetStatusDescription(sosStatus));
+ }
}
}
STRONGIFY(self);
NSError* localError = nil;
- [self.accountStateTracker triggerOctagonStatusFetch];
+ // During testing, don't kick this off until it's needed
+ if([self.contextID isEqualToString:OTDefaultContext]) {
+ [self.accountStateTracker triggerOctagonStatusFetch];
+ }
[self checkTrustStatusAndPostRepairCFUIfNecessary:^(CliqueStatus status, BOOL posted, BOOL hasIdentity, NSError * _Nullable postError) {
withBlockTakingSelf:^(OctagonStateTransitionOperation * _Nonnull op) {
STRONGIFY(self);
- // Note: we don't modify the account metadata store here; that will have been done
- // by a join or upgrade operation, possibly long ago
+ if([self.contextID isEqualToString:OTDefaultContext]) {
+ [self.accountStateTracker triggerOctagonStatusFetch];
+ }
- [self.accountStateTracker triggerOctagonStatusFetch];
+ // Note: we don't modify the account metadata trust state; that will have been done
+ // by a join or upgrade operation, possibly long ago
- NSError* localError = nil;
- NSString* peerID = [self.accountMetadataStore getEgoPeerID:&localError];
- if(!peerID || localError) {
- secerror("octagon-ckks: No peer ID to pass to CKKS. Syncing will be disabled.");
- } else {
- OctagonCKKSPeerAdapter* octagonAdapter = [[OctagonCKKSPeerAdapter alloc] initWithPeerID:peerID operationDependencies:[self operationDependencies]];
+ // but, we do set the 'attempted join' bit, just in case the device joined before we started setting this bit
- // This octagon adapter must be able to load the self peer keys, or we're in trouble.
- NSError* egoPeerKeysError = nil;
- CKKSSelves* selves = [octagonAdapter fetchSelfPeers:&egoPeerKeysError];
- if(!selves || egoPeerKeysError) {
- secerror("octagon-ckks: Unable to fetch self peers for %@: %@", octagonAdapter, egoPeerKeysError);
+ // Also, ensure that the CKKS policy is correctly present and set in the view manager
+ __block NSString* peerID = nil;
+ NSError* localError = nil;
- if([self.lockStateTracker isLockedError:egoPeerKeysError]) {
- secnotice("octagon-ckks", "Waiting for device unlock to proceed");
- op.nextState = OctagonStateWaitForUnlock;
- } else {
- secnotice("octagon-ckks", "Error is scary; becoming untrusted");
- op.nextState = OctagonStateBecomeUntrusted;
- }
- return;
- }
+ __block NSSet<NSString*>* viewList = nil;
+ __block TPPolicy* policy = nil;
- // stash a reference to the adapter so we can provided updates later
- self.octagonAdapter = octagonAdapter;
+ [self.accountMetadataStore persistAccountChanges:^OTAccountMetadataClassC * _Nullable(OTAccountMetadataClassC * _Nonnull metadata) {
+ peerID = metadata.peerID;
+ viewList = metadata.syncingViews.count > 0 ? [NSSet setWithArray:metadata.syncingViews] : nil;
+ policy = metadata.hasSyncingPolicy ? [metadata getTPPolicy] : nil;
- // Start all our CKKS views!
- for (id key in self.viewManager.views) {
- CKKSKeychainView* view = self.viewManager.views[key];
- secnotice("octagon-ckks", "Informing CKKS view '%@' of trusted operation with self peer %@", view.zoneName, peerID);
+ if(metadata.attemptedJoin == OTAccountMetadataClassC_AttemptedAJoinState_ATTEMPTED) {
+ return nil;
+ }
+ metadata.attemptedJoin = OTAccountMetadataClassC_AttemptedAJoinState_ATTEMPTED;
+ return metadata;
+ } error:&localError];
+
+ if(!peerID || localError) {
+ secerror("octagon-ckks: No peer ID to pass to CKKS. Syncing will be disabled.");
+ } else if(!viewList || !policy) {
+ secerror("octagon-ckks: No memoized CKKS policy, re-fetching");
+ op.nextState = OctagonStateRefetchCKKSPolicy;
+ return;
- NSArray<id<CKKSPeerProvider>>* peerProviders = nil;
+ } else {
+ secnotice("octagon-ckks", "Initializing CKKS views with view list %@ and policy %@", viewList, policy);
+ [self.viewManager setSyncingViews:viewList
+ sortingPolicy:policy];
- if(self.sosAdapter.sosEnabled) {
- peerProviders = @[self.octagonAdapter, self.sosAdapter];
+ OctagonCKKSPeerAdapter* octagonAdapter = [[OctagonCKKSPeerAdapter alloc] initWithPeerID:peerID operationDependencies:[self operationDependencies]];
- } else {
- peerProviders = @[self.octagonAdapter];
- }
+ // This octagon adapter must be able to load the self peer keys, or we're in trouble.
+ NSError* egoPeerKeysError = nil;
+ CKKSSelves* selves = [octagonAdapter fetchSelfPeers:&egoPeerKeysError];
+ if(!selves || egoPeerKeysError) {
+ secerror("octagon-ckks: Unable to fetch self peers for %@: %@", octagonAdapter, egoPeerKeysError);
- [view beginTrustedOperation:peerProviders
- suggestTLKUpload:self.suggestTLKUploadNotifier];
- }
- }
- [self notifyTrustChanged:OTAccountMetadataClassC_TrustState_TRUSTED];
+ if([self.lockStateTracker isLockedError:egoPeerKeysError]) {
+ secnotice("octagon-ckks", "Waiting for device unlock to proceed");
+ op.nextState = OctagonStateWaitForUnlock;
+ } else {
+ secnotice("octagon-ckks", "Error is scary; becoming untrusted");
+ op.nextState = OctagonStateBecomeUntrusted;
+ }
+ return;
+ }
- op.nextState = op.intendedState;
- }];
+ // stash a reference to the adapter so we can provide updates later
+ self.octagonAdapter = octagonAdapter;
+
+ // Start all our CKKS views!
+ for (id key in self.viewManager.views) {
+ CKKSKeychainView* view = self.viewManager.views[key];
+ secnotice("octagon-ckks", "Informing CKKS view '%@' of trusted operation with self peer %@", view.zoneName, peerID);
+
+ NSArray<id<CKKSPeerProvider>>* peerProviders = nil;
+
+ if(self.sosAdapter.sosEnabled) {
+ peerProviders = @[self.octagonAdapter, self.sosAdapter];
+
+ } else {
+ peerProviders = @[self.octagonAdapter];
+ }
+
+ [view beginTrustedOperation:peerProviders
+ suggestTLKUpload:self.suggestTLKUploadNotifier];
+ }
+ }
+ [self notifyTrustChanged:OTAccountMetadataClassC_TrustState_TRUSTED];
+
+ op.nextState = op.intendedState;
+ }];
}
#pragma mark --- Utilities to run at times
- (void)notifyContainerChangeWithUserInfo:(NSDictionary*)userInfo
{
- secerror("OTCuttlefishContext: received a cuttlefish push notification (%@): %@",
+ secnotice("octagonpush", "received a cuttlefish push notification (%@): %@",
self.containerName, userInfo);
NSDictionary *cfDictionary = userInfo[@"cf"];
}
secnotice("otrpc", "Preparing identity as applicant");
+
OTPrepareOperation* pendingOp = [[OTPrepareOperation alloc] initWithDependencies:self.operationDependencies
intendedState:OctagonStateInitiatorAwaitingVoucher
errorState:OctagonStateBecomeUntrusted
deviceInfo:[self prepareInformation]
+ policyOverride:self.policyOverride
epoch:epoch];
-
dispatch_time_t timeOut = 0;
if(config.timeout != 0) {
timeOut = config.timeout;
}
OctagonStateTransitionPath* path = [OctagonStateTransitionPath pathFromDictionary:@{
- OctagonStateInitiatorCreateIdentity: @{
- OctagonStateInitiatorVouchWithBottle: [self joinStatePathDictionary],
+ OctagonStateBottleJoinCreateIdentity: @{
+ OctagonStateBottleJoinVouchWithBottle: [self joinStatePathDictionary],
},
}];
- (NSDictionary*)joinStatePathDictionary
{
return @{
- OctagonStateInitiatorUpdateDeviceList: @{
- OctagonStateInitiatorJoin: @{
- OctagonStateBecomeReady: @{
- OctagonStateReady: [OctagonStateTransitionPathStep success],
- },
+ OctagonStateInitiatorSetCDPBit: @{
+ OctagonStateInitiatorUpdateDeviceList: @{
+ OctagonStateInitiatorJoin: @{
+ OctagonStateBecomeReady: @{
+ OctagonStateReady: [OctagonStateTransitionPathStep success],
+ },
- OctagonStateInitiatorJoinCKKSReset: @{
- OctagonStateInitiatorJoinAfterCKKSReset: @{
- OctagonStateBecomeReady: @{
- OctagonStateReady: [OctagonStateTransitionPathStep success]
+ OctagonStateInitiatorJoinCKKSReset: @{
+ OctagonStateInitiatorJoinAfterCKKSReset: @{
+ OctagonStateBecomeReady: @{
+ OctagonStateReady: [OctagonStateTransitionPathStep success]
+ },
},
},
},
- (void)rpcJoin:(NSData*)vouchData
vouchSig:(NSData*)vouchSig
-preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
reply:(void (^)(NSError * _Nullable error))reply
{
_vouchData = vouchData;
_vouchSig = vouchSig;
- _preapprovedKeys = preapprovedKeys;
if ([self checkForCKAccount:nil] != CKKSAccountStatusAvailable) {
secnotice("octagon", "No cloudkit account present");
result[@"statePendingFlags"] = [self.stateMachine dumpPendingFlags];
result[@"stateFlags"] = [self.stateMachine.flags dumpFlags];
- result[@"memoizedTrustState"] = @(self.currentMemoizedTrustState);
- result[@"memoizedAccountState"] = @(self.currentMemoizedAccountState);
+ NSError* metadataError = nil;
+ OTAccountMetadataClassC* currentAccountMetadata = [self.accountMetadataStore loadOrCreateAccountMetadata:&metadataError];
+ if(metadataError) {
+ secnotice("octagon", "Failed to load account metaada for container (%@) and context (%@): %@", self.containerName, self.contextID, metadataError);
+ }
+
+ result[@"memoizedTrustState"] = @(currentAccountMetadata.trustState);
+ result[@"memoizedAccountState"] = @(currentAccountMetadata.icloudAccountState);
+ result[@"memoizedCDPStatus"] = @(currentAccountMetadata.cdpState);
result[@"octagonLaunchSeqence"] = [self.launchSequence eventsByTime];
- result[@"memoizedlastHealthCheck"] = self.currentMemoizedLastHealthCheck ? self.currentMemoizedLastHealthCheck : @"Never checked";
+
+ NSDate* lastHealthCheck = self.currentMemoizedLastHealthCheck;
+ result[@"memoizedlastHealthCheck"] = lastHealthCheck ?: @"Never checked";
if (self.sosAdapter.sosEnabled) {
result[@"sosTrustedPeersStatus"] = [self sosTrustedPeersStatus];
result[@"sosSelvesStatus"] = [self sosSelvesStatus];
}
}];
- if(trustStatus == CliqueStatusIn && self.postedRepairCFU == YES){
- NSError* clearError = nil;
- [self.followupHandler clearFollowUp:OTFollowupContextTypeStateRepair error:&clearError];
- // TODO(caw): should we clear this flag if `clearFollowUpForContext` fails?
- self.postedRepairCFU = NO;
- }
reply(trustStatus, peerID, peerModelCounts, excluded, localError);
}
}];
}
+- (void)rpcRefetchCKKSPolicy:(void (^)(NSError * _Nullable error))reply
+{
+ [self.stateMachine doWatchedStateMachineRPC:@"octagon-refetch-ckks-policy"
+ sourceStates:[NSMutableSet setWithArray: @[OctagonStateReady]]
+ path:[OctagonStateTransitionPath pathFromDictionary:@{
+ OctagonStateRefetchCKKSPolicy: @{
+ OctagonStateBecomeReady: @{
+ OctagonStateReady: [OctagonStateTransitionPathStep success],
+ },
+ },
+ }]
+ reply:reply];
+}
#pragma mark --- Testing
- (void) setAccountStateHolder:(OTCuttlefishAccountStateHolder*)accountMetadataStore
self.accountMetadataStore = accountMetadataStore;
}
-- (void)setPostedBool:(BOOL)posted
-{
- self.postedRepairCFU = posted;
-}
-
#pragma mark --- Health Checker
- (BOOL)postRepairCFU:(NSError**)error
{
NSError* localError = nil;
BOOL postSuccess = NO;
- if (self.postedRepairCFU == NO) {
- [self.followupHandler postFollowUp:OTFollowupContextTypeStateRepair error:&localError];
- if(localError){
- secerror("octagon-health: CoreCDP repair failed: %@", localError);
- if(error){
- *error = localError;
- }
- }
- else{
- secnotice("octagon-health", "CoreCDP post repair success");
- self.postedRepairCFU = YES;
- postSuccess = YES;
+ [self.followupHandler postFollowUp:OTFollowupContextTypeStateRepair error:&localError];
+ if(localError){
+ secerror("octagon-health: CoreCDP repair failed: %@", localError);
+ if(error){
+ *error = localError;
}
- } else {
- secnotice("octagon-health", "already posted a repair CFU!");
+ }
+ else{
+ secnotice("octagon-health", "CoreCDP post repair success");
+ postSuccess = YES;
}
return postSuccess;
}
}
}
-- (void)postConfirmPasscodeCFU:(NSError**)error
+- (BOOL)leaveTrust:(NSError**)error
{
- NSError* localError = nil;
- if (self.postedEscrowRepairCFU == NO) {
- [self.followupHandler postFollowUp:OTFollowupContextTypeOfflinePasscodeChange error:&localError];
- if(localError){
- secerror("octagon-health: CoreCDP offline passcode change failed: %@", localError);
- *error = localError;
- }
- else{
- secnotice("octagon-health", "CoreCDP offline passcode change success");
- self.postedEscrowRepairCFU = YES;
+ if (OctagonPlatformSupportsSOS()) {
+ CFErrorRef cfError = NULL;
+ bool left = SOSCCRemoveThisDeviceFromCircle_Server(&cfError);
+
+ if(!left || cfError) {
+ secerror("failed to leave SOS circle: %@", cfError);
+ if(error) {
+ *error = (NSError*)CFBridgingRelease(cfError);
+ } else {
+ CFReleaseNull(cfError);
+ }
+ return NO;
}
- } else {
- secnotice("octagon-health", "already posted escrow CFU");
}
+ secnotice("octagon-health", "Successfully left SOS");
+ return YES;
}
-- (void)postRecoveryKeyCFU:(NSError**)error
+- (void)postConfirmPasscodeCFU:(NSError**)error
{
NSError* localError = nil;
- if (self.postedRecoveryKeyCFU == NO) {
- [self.followupHandler postFollowUp:OTFollowupContextTypeRecoveryKeyRepair error:&localError];
- if(localError){
- secerror("octagon-health: CoreCDP recovery key cfu failed: %@", localError);
- }
- else{
- secnotice("octagon-health", "CoreCDP recovery key cfu success");
- self.postedRecoveryKeyCFU = YES;
- }
- } else {
- secnotice("octagon-health", "already posted recovery key CFU");
+ [self.followupHandler postFollowUp:OTFollowupContextTypeOfflinePasscodeChange error:&localError];
+ if(localError){
+ secerror("octagon-health: CoreCDP offline passcode change failed: %@", localError);
+ *error = localError;
+ }
+ else{
+ secnotice("octagon-health", "CoreCDP offline passcode change success");
}
}
sourceStates:OctagonHealthSourceStates()
path:[OctagonStateTransitionPath pathFromDictionary:@{
OctagonStateHSA2HealthCheck: @{
- OctagonStateSecurityTrustCheck: @{
- OctagonStateTPHTrustCheck: @{
- OctagonStateCuttlefishTrustCheck: @{
- OctagonStateBecomeReady: @{
- OctagonStateReady: [OctagonStateTransitionPathStep success],
- OctagonStateWaitForUnlock: [OctagonStateTransitionPathStep success],
+ OctagonStateCDPHealthCheck: @{
+ OctagonStateSecurityTrustCheck: @{
+ OctagonStateTPHTrustCheck: @{
+ OctagonStateCuttlefishTrustCheck: @{
+ OctagonStateBecomeReady: @{
+ OctagonStateReady: [OctagonStateTransitionPathStep success],
+ OctagonStateWaitForUnlock: [OctagonStateTransitionPathStep success],
+ },
+ // Cuttlefish can suggest we reset the world. Consider reaching here a success,
+ // instead of tracking the whole reset.
+ OctagonStateHealthCheckReset: [OctagonStateTransitionPathStep success],
+ OctagonStateHealthCheckLeaveClique: [OctagonStateTransitionPathStep success],
},
- // Cuttlefish can suggest we reset the world. Consider reaching here a success,
- // instead of tracking the whole reset.
- OctagonStateHealthCheckReset: [OctagonStateTransitionPathStep success],
},
},
+ OctagonStateWaitForCDP: [OctagonStateTransitionPathStep success],
},
OctagonStateWaitForHSA2: [OctagonStateTransitionPathStep success],
}
intendedState:OctagonStateBecomeReady
ckksConflictState:OctagonStateBecomeUntrusted
errorState:OctagonStateBecomeUntrusted
- deviceInfo:self.prepareInformation];
+ deviceInfo:self.prepareInformation
+ policyOverride:self.policyOverride];
OctagonStateTransitionRequest<OTSOSUpgradeOperation*>* request = [[OctagonStateTransitionRequest alloc] init:@"attempt-sos-upgrade"
sourceStates:sourceStates
reply:reply];
}
-- (void)clearPendingCFUFlags
-{
- self.postedRecoveryKeyCFU = NO;
- self.postedEscrowRepairCFU = NO;
- self.postedRepairCFU = NO;
-}
-
// Metrics passthroughs
- (BOOL)machineIDOnMemoizedList:(NSString*)machineID error:(NSError**)error
OTAuthKitNoAuthenticationController = 45,
OTAuthKitMachineIDMissing = 46,
OTAuthKitPrimaryAccountHaveNoDSID = 47,
+ OTErrorFailedToLeaveClique = 48,
};
#define OTMasterSecretLength 72
typedef NS_ENUM(NSInteger, TrustedPeersHelperErrorCode) {
TrustedPeersHelperErrorNoPreparedIdentity = 1,
TrustedPeersHelperErrorNoPeersPreapprovePreparedIdentity = 14,
+ TrustedPeersHelperErrorCodeUntrustedRecoveryKeys = 32,
TrustedPeersHelperErrorCodeNotEnrolled = 34,
};
--- /dev/null
+/*
+ * Copyright (c) 2019 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#if OCTAGON
+
+#import <Foundation/Foundation.h>
+#import "keychain/ckks/CKKSGroupOperation.h"
+#import "keychain/ot/OctagonStateMachineHelpers.h"
+#import "keychain/ot/OTOperationDependencies.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface OTDetermineCDPBitStatusOperation : CKKSGroupOperation <OctagonStateTransitionOperationProtocol>
+
+- (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
+ intendedState:(OctagonState*)intendedState
+ errorState:(OctagonState*)errorState;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif // OCTAGON
--- /dev/null
+/*
+ * Copyright (c) 2019 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#if OCTAGON
+
+#import "utilities/debugging.h"
+
+#import "keychain/ot/ObjCImprovements.h"
+#import "keychain/ot/OTDetermineCDPBitStatusOperation.h"
+#import "keychain/ot/OTStates.h"
+
+@interface OTDetermineCDPBitStatusOperation ()
+@property OTOperationDependencies* deps;
+@end
+
+@implementation OTDetermineCDPBitStatusOperation
+@synthesize intendedState = _intendedState;
+@synthesize nextState = _nextState;
+
+- (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
+ intendedState:(OctagonState*)intendedState
+ errorState:(OctagonState*)errorState
+{
+ if((self = [super init])) {
+ _deps = dependencies;
+ _intendedState = intendedState;
+ _nextState = errorState;
+ }
+ return self;
+}
+
+- (void)groupStart
+{
+ secnotice("octagon-cdp-status", "Checking CDP status");
+
+ NSError* localError = nil;
+ OTAccountMetadataClassC* account = [self.deps.stateHolder loadOrCreateAccountMetadata:&localError];
+ if(localError && [self.deps.lockStateTracker isLockedError:localError]) {
+ secnotice("octagon-cdp-status", "Device is locked! restarting on unlock");
+ self.nextState = OctagonStateWaitForUnlock;
+ return;
+ }
+
+ if(localError) {
+ secnotice("octagon-cdp-status", "Failed to load account metadata: %@", localError);
+ self.error = localError;
+ return;
+ }
+
+ secnotice("octagon-cdp-status", "CDP is %@", OTAccountMetadataClassC_CDPStateAsString(account.cdpState));
+
+ if(account.cdpState == OTAccountMetadataClassC_CDPState_ENABLED) {
+ self.nextState = self.intendedState;
+ } else {
+ // If the CDP status is unknown or disabled, double-check with TPH.
+ // If there are any peers, the CDP status really should be ENABLED.
+ __block OTAccountMetadataClassC_CDPState newState = OTAccountMetadataClassC_CDPState_UNKNOWN;
+
+ WEAKIFY(self);
+ [self.deps.cuttlefishXPCWrapper trustStatusWithContainer:self.deps.containerName
+ context:self.deps.contextID
+ reply:^(TrustedPeersHelperEgoPeerStatus *egoStatus,
+ NSError *xpcError) {
+ STRONGIFY(self);
+ if(xpcError) {
+ secnotice("octagon-cdp-status", "Unable to talk with TPH; leaving CDP status as 'unknown': %@", xpcError);
+ return;
+ }
+
+ secnotice("octagon-cdp-status", "Octagon reports %d peers", (int)egoStatus.numberOfPeersInOctagon);
+ if(egoStatus.numberOfPeersInOctagon > 0) {
+ newState = OTAccountMetadataClassC_CDPState_ENABLED;
+ } else {
+ // As a last gasp, check in with SOS (if enabled). If there's a circle (in or out), CDP is on
+ if(self.deps.sosAdapter.sosEnabled) {
+ secnotice("octagon-cdp-status", "Requesting SOS status...");
+
+ NSError* circleError = nil;
+ SOSCCStatus circleStatus = [self.deps.sosAdapter circleStatus:&circleError];
+
+ if(circleError || circleStatus == kSOSCCError) {
+ secnotice("octagon-cdp-status", "Error fetching circle status. Leaving CDP status as 'unknown': %@", circleError);
+ } else if(circleStatus == kSOSCCCircleAbsent) {
+ secnotice("octagon-cdp-status", "SOS reports circle absent. Setting CDP to 'disabled'");
+ newState = OTAccountMetadataClassC_CDPState_DISABLED;
+ } else {
+ secnotice("octagon-cdp-status", "SOS reports some existing circle (%d). Setting CDP to 'enabled'", (int)circleStatus);
+ newState = OTAccountMetadataClassC_CDPState_ENABLED;
+ }
+ } else {
+ // No SOS? no CDP.
+ secnotice("octagon-cdp-status", "No SOS. CDP bit is off.");
+ newState = OTAccountMetadataClassC_CDPState_DISABLED;
+ }
+ }
+ }];
+
+ if(account.cdpState != newState) {
+ NSError* stateError = nil;
+ [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
+ if(metadata.cdpState == OTAccountMetadataClassC_CDPState_ENABLED) {
+ secnotice("octagon-cdp-status", "CDP bit is enabled on-disk, not modifying (would have been %@)", OTAccountMetadataClassC_CDPStateAsString(newState));
+
+ // Set this here to perform the right state choice later
+ newState = OTAccountMetadataClassC_CDPState_ENABLED;
+ return nil;
+ } else {
+ secnotice("octagon-cdp-status", "Writing CDP bit as %@", OTAccountMetadataClassC_CDPStateAsString(newState));
+ metadata.cdpState = newState;
+ return metadata;
+ }
+ } error:&stateError];
+
+ if(stateError) {
+ secnotice("octagon-cdp-status", "Failed to load account metadata: %@", stateError);
+ }
+ }
+
+ if(newState == OTAccountMetadataClassC_CDPState_ENABLED) {
+ self.nextState = self.intendedState;
+ } else {
+ self.nextState = OctagonStateWaitForCDP;
+ }
+ }
+}
+
+@end
+
+#endif // OCTAGON
#import "keychain/ckks/CloudKitCategories.h"
#import "keychain/ckks/CKKSCurrentKeyPointer.h"
#import "keychain/ckks/CKKSKeychainView.h"
+#import "keychain/SecureObjectSync/SOSAccount.h"
#import "keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h"
#import "keychain/ot/ObjCImprovements.h"
WEAKIFY(self);
NSArray<NSData*>* publicSigningSPKIs = nil;
-
if(self.operationDependencies.sosAdapter.sosEnabled) {
- NSError* peerError = nil;
-
- secnotice("octagon-sos", "SOS not enabled; no preapproved keys");
- NSSet<id<CKKSRemotePeerProtocol>>* peerSet = [self.operationDependencies.sosAdapter fetchTrustedPeers:&peerError];
+ NSError* sosPreapprovalError = nil;
+ publicSigningSPKIs = [OTSOSAdapterHelpers peerPublicSigningKeySPKIsForCircle:self.operationDependencies.sosAdapter error:&sosPreapprovalError];
- if(!peerSet || peerError) {
- secerror("octagon-sos: Can't fetch trusted peers during establish: %@", peerError);
+ if(publicSigningSPKIs) {
+ secnotice("octagon-sos", "SOS preapproved keys are %@", publicSigningSPKIs);
+ } else {
+ secnotice("octagon-sos", "Unable to fetch SOS preapproved keys: %@", sosPreapprovalError);
}
- publicSigningSPKIs = [OTSOSActualAdapter peerPublicSigningKeySPKIs:peerSet];
- secnotice("octagon-sos", "SOS preapproved keys are %@", publicSigningSPKIs);
} else {
secnotice("octagon-sos", "SOS not enabled; no preapproved keys");
}
self.tlkShares = tlkShares;
self.pendingTLKShares = pendingTLKShares;
- secnotice("octagon-ckks", "Fetched %d key sets, %d broken key set,s %d tlk shares, and %d pendingTLKShares",
+ secnotice("octagon-ckks", "Fetched %d key sets, %d broken key sets, %d tlk shares, and %d pendingTLKShares",
(int)self.viewKeySets.count,
(int)self.incompleteKeySets.count,
(int)self.tlkShares.count,
NS_ASSUME_NONNULL_BEGIN
-@interface OTFetchViewsOperation : CKKSGroupOperation
+@interface OTFetchViewsOperation : CKKSGroupOperation <OctagonStateTransitionOperationProtocol>
-- (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies;
-
-@property (nonatomic) NSSet<NSString*>* viewList;
-@property (nonatomic) TPPolicy* _Nullable policy;
+- (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
+ intendedState:(OctagonState*)intendedState
+ errorState:(OctagonState*)errorState;
@end
#import "keychain/ot/OTFetchViewsOperation.h"
#import "keychain/ot/ObjCImprovements.h"
#import "keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h"
+#import "keychain/ot/categories/OTAccountMetadataClassC+KeychainSupport.h"
#import "keychain/ckks/CKKSAnalytics.h"
@interface OTFetchViewsOperation ()
@property OTOperationDependencies* deps;
-@property NSOperation* finishedOp;
-@property CKKSViewManager* ckm;
@end
@implementation OTFetchViewsOperation
+@synthesize intendedState = _intendedState;
+@synthesize nextState = _nextState;
- (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
+ intendedState:(OctagonState*)intendedState
+ errorState:(OctagonState*)errorState
{
if ((self = [super init])) {
_deps = dependencies;
- _ckm = dependencies.viewManager;
+
+ _intendedState = intendedState;
+ _nextState = errorState;
}
return self;
}
{
secnotice("octagon", "fetching views");
- self.finishedOp = [[NSOperation alloc] init];
- [self dependOnBeforeGroupFinished:self.finishedOp];
-
- NSSet<NSString*>* sosViewList = [self.ckm viewList];
- self.policy = nil;
- self.viewList = sosViewList;
-
- if ([self.ckm useCKKSViewsFromPolicy]) {
- WEAKIFY(self);
-
- [self.deps.cuttlefishXPCWrapper fetchPolicyWithContainer:self.deps.containerName context:self.deps.contextID reply:^(TPPolicy* _Nullable policy, NSError* _Nullable error) {
- STRONGIFY(self);
- if (error) {
- secerror("octagon: failed to retrieve policy: %@", error);
- [[CKKSAnalytics logger] logResultForEvent:OctagonEventFetchViews hardFailure:true result:error];
- self.error = error;
- [self runBeforeGroupFinished:self.finishedOp];
- } else {
- if (policy == nil) {
- secerror("octagon: no policy returned");
- }
- self.policy = policy;
- NSArray<NSString*>* sosViews = [sosViewList allObjects];
- [self.deps.cuttlefishXPCWrapper getViewsWithContainer:self.deps.containerName context:self.deps.contextID inViews:sosViews reply:^(NSArray<NSString*>* _Nullable outViews, NSError* _Nullable error) {
- STRONGIFY(self);
- if (error) {
- secerror("octagon: failed to retrieve list of views: %@", error);
- [[CKKSAnalytics logger] logResultForEvent:OctagonEventFetchViews hardFailure:true result:error];
- self.error = error;
- [self runBeforeGroupFinished:self.finishedOp];
- } else {
- if (outViews == nil) {
- secerror("octagon: bad results from getviews");
- } else {
- self.viewList = [NSSet setWithArray:outViews];
- }
- [self complete];
- }
- }];
- }
- }];
- } else {
- [self complete];
- }
-}
+ WEAKIFY(self);
+ [self.deps.cuttlefishXPCWrapper fetchCurrentPolicyWithContainer:self.deps.containerName
+ context:self.deps.contextID
+ reply:^(NSSet<NSString*>* _Nullable viewList,
+ TPPolicy* _Nullable policy,
+ NSError* _Nullable error) {
+ STRONGIFY(self);
+ [[CKKSAnalytics logger] logResultForEvent:OctagonEventFetchViews hardFailure:true result:error];
+
+ if (error) {
+ secerror("octagon: failed to retrieve policy+views: %@", error);
+ self.error = error;
+ return;
+ }
+
+ secnotice("octagon-ckks", "Received policy %@ with view list: %@", policy, viewList);
+ // Write them down before continuing
+
+ NSError* stateError = nil;
+ [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nullable(OTAccountMetadataClassC * _Nonnull metadata) {
+ metadata.syncingViews = [viewList mutableCopy];
+ [metadata setTPPolicy:policy];
+ return metadata;
+ } error:&stateError];
+
+ if(stateError) {
+ secerror("octagon: failed to save policy+views: %@", stateError);
+ self.error = stateError;
+ return;
+ }
-- (void)complete {
- secnotice("octagon", "viewList: %@", self.viewList);
- self.ckm.policy = self.policy;
- self.ckm.viewList = self.viewList;
+ [self.deps.viewManager setSyncingViews:viewList sortingPolicy:policy];
- [self.ckm createViews];
- [self.ckm beginCloudKitOperationOfAllViews];
- [self runBeforeGroupFinished:self.finishedOp];
+ self.nextState = self.intendedState;
+ }];
}
@end
- (NSDictionary *)sysdiagnoseStatus;
- (NSDictionary<NSString*,NSNumber*> *)sfaStatus;
+@end
+@interface OTFollowup (Testing)
+// Reports on whether this individual OTFollowUp object has posted a CFU of this type.
+- (BOOL)hasPosted:(OTFollowupContextType)contextType;
+- (void)clearAllPostedFlags;
@end
NS_ASSUME_NONNULL_END
@property NSTimeInterval previousFollowupEnd;
@property NSTimeInterval followupStart;
@property NSTimeInterval followupEnd;
+
+@property NSMutableSet<NSString*>* postedCFUTypes;
@end
@implementation OTFollowup : NSObject
{
if (self = [super init]) {
self.cdpd = cdpFollowupController;
+
+ _postedCFUTypes = [NSMutableSet set];
}
return self;
}
}
NSError *followupError = nil;
+
+ secnotice("followup", "Posting a follow up (for Octagon) of type %@", OTFollowupContextTypeToString(contextType));
BOOL result = [self.cdpd postFollowUpWithContext:context error:&followupError];
- if (error) {
- *error = followupError;
+
+ if(result) {
+ [self.postedCFUTypes addObject:OTFollowupContextTypeToString(contextType)];
+ } else {
+ if (error) {
+ *error = followupError;
+ }
}
return result;
return NO;
}
- return [self.cdpd clearFollowUpWithContext:context error:error];
+ secnotice("followup", "Clearing follow ups (for Octagon) of type %@", OTFollowupContextTypeToString(contextType));
+ BOOL result = [self.cdpd clearFollowUpWithContext:context error:error];
+ if(result) {
+ [self.postedCFUTypes removeObject:OTFollowupContextTypeToString(contextType)];
+ }
+
+ return result;
}
return values;
}
+@end
+@implementation OTFollowup (Testing)
+- (BOOL)hasPosted:(OTFollowupContextType)contextType
+{
+ return [self.postedCFUTypes containsObject:OTFollowupContextTypeToString(contextType)];
+}
+
+- (void)clearAllPostedFlags
+{
+ [self.postedCFUTypes removeAllObjects];
+}
@end
#endif // OCTAGON
ckksConflictState:(OctagonState*)ckksConflictState
errorState:(OctagonState*)errorState
voucherData:(NSData*)voucherData
- voucherSig:(NSData*)voucherSig
- preapprovedKeys:(NSArray<NSData *>*)preapprovedKeys;
+ voucherSig:(NSData*)voucherSig;
@property (nonatomic) NSData* voucherData;
@property (nonatomic) NSData* voucherSig;
-@property (nonatomic) NSArray<NSData *>* preapprovedKeys;
@property (nonatomic) NSString* peerID;
errorState:(OctagonState*)errorState
voucherData:(NSData*)voucherData
voucherSig:(NSData*)voucherSig
- preapprovedKeys:(NSArray<NSData *>*)preapprovedKeys
{
if((self = [super init])) {
_deps = dependencies;
_voucherData = voucherData;
_voucherSig = voucherSig;
- _preapprovedKeys = preapprovedKeys;
}
return self;
}
{
WEAKIFY(self);
+ NSArray<NSData*>* publicSigningSPKIs = nil;
+ if(self.deps.sosAdapter.sosEnabled) {
+ NSError* sosPreapprovalError = nil;
+ publicSigningSPKIs = [OTSOSAdapterHelpers peerPublicSigningKeySPKIsForCircle:self.deps.sosAdapter error:&sosPreapprovalError];
+
+ if(publicSigningSPKIs) {
+ secnotice("octagon-sos", "SOS preapproved keys are %@", publicSigningSPKIs);
+ } else {
+ secnotice("octagon-sos", "Unable to fetch SOS preapproved keys: %@", sosPreapprovalError);
+ }
+
+ } else {
+ secnotice("octagon-sos", "SOS not enabled; no preapproved keys");
+ }
+
[self.deps.cuttlefishXPCWrapper joinWithContainer:self.deps.containerName
context:self.deps.contextID
voucherData:self.voucherData
voucherSig:self.voucherSig
ckksKeys:viewKeySets
tlkShares:pendingTLKShares
- preapprovedKeys:self.preapprovedKeys
- reply:^(NSString * _Nullable peerID, NSArray<CKRecord*>* keyHierarchyRecords, NSError * _Nullable error) {
+ preapprovedKeys:publicSigningSPKIs
+ reply:^(NSString * _Nullable peerID,
+ NSArray<CKRecord*>* keyHierarchyRecords,
+ NSSet<NSString*>* _Nullable syncingViews,
+ TPPolicy* _Nullable syncingPolicy,
+ NSError * _Nullable error) {
STRONGIFY(self);
if(error){
secerror("octagon: Error joining with voucher: %@", error);
[[CKKSAnalytics logger] logSuccessForEventNamed:OctagonEventJoinWithVoucher];
+ [self.deps.viewManager setSyncingViews:syncingViews sortingPolicy:syncingPolicy];
+
NSError* localError = nil;
BOOL persisted = [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
- metadata.trustState = OTAccountMetadataClassC_TrustState_TRUSTED;
- metadata.peerID = peerID;
- return metadata;
- } error:&localError];
+ metadata.trustState = OTAccountMetadataClassC_TrustState_TRUSTED;
+ metadata.peerID = peerID;
+ metadata.syncingViews = [syncingViews mutableCopy];
+ [metadata setTPPolicy:syncingPolicy];
+ return metadata;
+ } error:&localError];
if(!persisted || localError) {
secnotice("octagon", "Couldn't persist results: %@", localError);
self.error = localError;
// Set this to non-zero if you want to configure your timeouts
@property int64_t timeout;
-- (instancetype)initWithProtocolType:(NSString*)protocolType
- uniqueDeviceID:(NSString*)uniqueDeviceID
- uniqueClientID:(NSString*)uniqueClientID
- containerName:(NSString* _Nullable)containerName
- contextID:(NSString*)contextID
- epoch:(uint64_t)epoch
- isInitiator:(BOOL)isInitiator;
-
- (instancetype)initWithProtocolType:(NSString*)protocolType
uniqueDeviceID:(NSString*)uniqueDeviceID
uniqueClientID:(NSString*)uniqueClientID
return YES;
}
-- (instancetype)initWithProtocolType:(NSString*)protocolType
- uniqueDeviceID:(NSString*)uniqueDeviceID
- uniqueClientID:(NSString*)uniqueClientID
- containerName:(NSString* _Nullable)containerName
- contextID:(NSString*)contextID
- epoch:(uint64_t)epoch
- isInitiator:(BOOL)isInitiator
-{
- return [self initWithProtocolType:protocolType
- uniqueDeviceID:uniqueDeviceID
- uniqueClientID:uniqueClientID
- pairingUUID:[NSUUID UUID].UUIDString
- containerName:containerName
- contextID:contextID
- epoch:(uint64_t)epoch
- isInitiator:isInitiator];
-}
-
- (instancetype)initWithProtocolType:(NSString*)protocolType
uniqueDeviceID:(NSString*)uniqueDeviceID
uniqueClientID:(NSString*)uniqueClientID
#import "keychain/ot/OTCuttlefishAccountStateHolder.h"
#import "keychain/escrowrequest/Framework/SecEscrowRequest.h"
#import "keychain/ckks/CKKSAccountStateTracker.h"
+#import "keychain/ckks/CKKSViewManager.h"
#include "keychain/securityd/SecDbItem.h"
#import <CoreCDP/CDPAccount.h>
NS_ASSUME_NONNULL_BEGIN
@class OTClientStateMachine;
@class CKKSLockStateTracker;
@class CKKSAccountStateTracker;
+@class CloudKitClassDependencies;
@interface OTManager : NSObject <OTControlProtocol>
@property (nonatomic, readonly) CKKSLockStateTracker* lockStateTracker;
-@property id<CKKSCloudKitAccountStateTrackingProvider> accountStateTracker;
+@property CKKSAccountStateTracker* accountStateTracker;
-- (instancetype)init NS_UNAVAILABLE;
+@property (readonly) CKContainer* cloudKitContainer;
+@property (nullable) CKKSViewManager* viewManager;
+
+// Creates an OTManager ready for use with live external systems.
+- (instancetype)init;
- (instancetype)initWithSOSAdapter:(id<OTSOSAdapter>)sosAdapter
- authKitAdapter:(id<OTAuthKitAdapter>)authKitAdapter
+ authKitAdapter:(id<OTAuthKitAdapter>)authKitAdapter
deviceInformationAdapter:(id<OTDeviceInformationAdapter>)deviceInformationAdapter
apsConnectionClass:(Class<OctagonAPSConnection>)apsConnectionClass
escrowRequestClass:(Class<SecEscrowRequestable>)escrowRequestClass
- loggerClass:(Class<SFAnalyticsProtocol> _Nullable)loggerClass
- lockStateTracker:(CKKSLockStateTracker* _Nullable)lockStateTracker
- accountStateTracker:(id<CKKSCloudKitAccountStateTrackingProvider>)accountStateTracker
+ loggerClass:(Class<SFAnalyticsProtocol>)loggerClass
+ lockStateTracker:(CKKSLockStateTracker*)lockStateTracker
+ cloudKitClassDependencies:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies
cuttlefishXPCConnection:(id<NSXPCProxyCreating> _Nullable)cuttlefishXPCConnection
cdpd:(id<OctagonFollowUpControllerProtocol>)cdpd;
- (BOOL)waitForReady:(NSString* _Nullable)containerName context:(NSString*)context wait:(int64_t)wait;
- (void)moveToCheckTrustedStateForContainer:(NSString* _Nullable)containerName context:(NSString*)context;
+// Call this to ensure SFA is ready
+- (void)setupAnalytics;
+
+ (instancetype _Nullable)manager;
+ (instancetype _Nullable)resetManager:(bool)reset to:(OTManager* _Nullable)obj;
-- (void)xpc24HrNotification:(NSString* _Nullable)containerName context:(NSString*)context skipRateLimitingCheck:(BOOL)skipRateLimitingCheck reply:(void (^)(NSError *error))reply;
+- (void)xpc24HrNotification;
- (OTCuttlefishContext*)contextForContainerName:(NSString* _Nullable)containerName
contextID:(NSString*)contextID
containerName:(NSString* _Nullable)containerName
contextName:(NSString *)contextName
reply:(void (^)(NSError *error))reply;
+@end
-//test only
+@interface OTManager (Testing)
- (void)setSOSEnabledForPlatformFlag:(bool) value;
+
+- (void)clearAllContexts;
+
+// Note that the OTManager returned by this will not work particularly well, if you want to do Octagon things
+// This should only be used for the CKKS tests
+- (instancetype)initWithSOSAdapter:(id<OTSOSAdapter>)sosAdapter
+ lockStateTracker:(CKKSLockStateTracker*)lockStateTracker
+ cloudKitClassDependencies:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies;
@end
+
NS_ASSUME_NONNULL_END
#endif // OCTAGON
#import "keychain/ckks/CKKS.h"
#import "keychain/ckks/CKKSViewManager.h"
#import "keychain/ckks/CKKSLockStateTracker.h"
+#import "keychain/ckks/CKKSCloudKitClassDependencies.h"
#import <CloudKit/CloudKit.h>
#import <CloudKit/CloudKit_Private.h>
- (instancetype)init
{
- // Under Octagon, the sos adatper is not considered essential.
+ // Under Octagon, the sos adapter is not considered essential.
id<OTSOSAdapter> sosAdapter = (OctagonPlatformSupportsSOS() ?
[[OTSOSActualAdapter alloc] initAsEssential:NO] :
[[OTSOSMissingAdapter alloc] init]);
-
+
return [self initWithSOSAdapter:sosAdapter
authKitAdapter:[[OTAuthKitActualAdapter alloc] init]
deviceInformationAdapter:[[OTDeviceInformationActualAdapter alloc] init]
escrowRequestClass:[EscrowRequestServer class] // Use the server class here to skip the XPC layer
loggerClass:[CKKSAnalytics class]
lockStateTracker:[CKKSLockStateTracker globalTracker]
- // The use of CKKS's account tracker here is an inversion, and will be fixed with CKKS-for-all when we
- // have Octagon own the CKKS objects
- accountStateTracker:[CKKSViewManager manager].accountTracker
+ cloudKitClassDependencies:[CKKSCloudKitClassDependencies forLiveCloudKit]
cuttlefishXPCConnection:nil
cdpd:[[CDPFollowUpController alloc] init]];
}
--(instancetype)initWithSOSAdapter:(id<OTSOSAdapter>)sosAdapter
- authKitAdapter:(id<OTAuthKitAdapter>)authKitAdapter
- deviceInformationAdapter:(id<OTDeviceInformationAdapter>)deviceInformationAdapter
- apsConnectionClass:(Class<OctagonAPSConnection>)apsConnectionClass
- escrowRequestClass:(Class<SecEscrowRequestable>)escrowRequestClass
- loggerClass:(Class<SFAnalyticsProtocol>)loggerClass
- lockStateTracker:(CKKSLockStateTracker*)lockStateTracker
- accountStateTracker:(id<CKKSCloudKitAccountStateTrackingProvider>)accountStateTracker
- cuttlefishXPCConnection:(id<NSXPCProxyCreating>)cuttlefishXPCConnection
- cdpd:(id<OctagonFollowUpControllerProtocol>)cdpd
+- (instancetype)initWithSOSAdapter:(id<OTSOSAdapter>)sosAdapter
+ authKitAdapter:(id<OTAuthKitAdapter>)authKitAdapter
+ deviceInformationAdapter:(id<OTDeviceInformationAdapter>)deviceInformationAdapter
+ apsConnectionClass:(Class<OctagonAPSConnection>)apsConnectionClass
+ escrowRequestClass:(Class<SecEscrowRequestable>)escrowRequestClass
+ loggerClass:(Class<SFAnalyticsProtocol>)loggerClass
+ lockStateTracker:(CKKSLockStateTracker*)lockStateTracker
+ cloudKitClassDependencies:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies
+ cuttlefishXPCConnection:(id<NSXPCProxyCreating>)cuttlefishXPCConnection
+ cdpd:(id<OctagonFollowUpControllerProtocol>)cdpd
{
if((self = [super init])) {
_sosAdapter = sosAdapter;
_deviceInformationAdapter = deviceInformationAdapter;
_loggerClass = loggerClass;
_lockStateTracker = lockStateTracker;
- _accountStateTracker = accountStateTracker;
_sosEnabledForPlatform = OctagonPlatformSupportsSOS();
_cuttlefishXPCConnection = cuttlefishXPCConnection;
+ _cloudKitContainer = [CKKSViewManager makeCKContainer:SecCKKSContainerName usePCS:SecCKKSContainerUsePCS];
+ _accountStateTracker = [[CKKSAccountStateTracker alloc] init:_cloudKitContainer
+ nsnotificationCenterClass:cloudKitClassDependencies.nsnotificationCenterClass];
+
self.contexts = [NSMutableDictionary dictionary];
self.clients = [NSMutableDictionary dictionary];
_cdpd = cdpd;
+ _viewManager = [[CKKSViewManager alloc] initWithContainer:_cloudKitContainer
+ sosAdapter:sosAdapter
+ accountStateTracker:_accountStateTracker
+ lockStateTracker:lockStateTracker
+ cloudKitClassDependencies:cloudKitClassDependencies];
+
// The default CuttlefishContext always exists:
(void) [self contextForContainerName:OTCKContainerName contextID:OTDefaultContext];
-
- // Tell SFA to expect us
- if(OctagonIsEnabled()){
- [self setupAnalytics];
- }
secnotice("octagon", "otmanager init");
}
return self;
}
+- (instancetype)initWithSOSAdapter:(id<OTSOSAdapter>)sosAdapter
+ lockStateTracker:(CKKSLockStateTracker*)lockStateTracker
+ cloudKitClassDependencies:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies
+{
+ if((self = [super init])) {
+ _sosAdapter = sosAdapter;
+ _lockStateTracker = lockStateTracker;
+
+ _cloudKitContainer = [CKKSViewManager makeCKContainer:SecCKKSContainerName usePCS:SecCKKSContainerUsePCS];
+ _accountStateTracker = [[CKKSAccountStateTracker alloc] init:_cloudKitContainer
+ nsnotificationCenterClass:cloudKitClassDependencies.nsnotificationCenterClass];
+
+ _viewManager = [[CKKSViewManager alloc] initWithContainer:_cloudKitContainer
+ sosAdapter:sosAdapter
+ accountStateTracker:_accountStateTracker
+ lockStateTracker:lockStateTracker
+ cloudKitClassDependencies:cloudKitClassDependencies];
+ }
+ return self;
+}
+
- (void)initializeOctagon
{
secnotice("octagon", "Initializing Octagon...");
CKKSViewManager* viewManager = nil;
if([containerName isEqualToString:SecCKKSContainerName] &&
[contextID isEqualToString:OTDefaultContext]) {
- viewManager = [CKKSViewManager manager];
+ viewManager = self.viewManager;
}
if(!context) {
return context;
}
+- (void)clearAllContexts
+{
+ if(self.contexts) {
+ dispatch_sync(self.queue, ^{
+ [self.contexts removeAllObjects];
+ });
+ }
+}
+
- (void)fetchEgoPeerID:(NSString* _Nullable)container
context:(NSString*)context
reply:(void (^)(NSString* _Nullable peerID, NSError* _Nullable error))reply
OTCuttlefishContext* cfshContext = [self contextForContainerName:config.containerName contextID:config.contextID];
[cfshContext handlePairingRestart:config];
[cfshContext startOctagonStateMachine];
- [cfshContext rpcPrepareIdentityAsApplicantWithConfiguration:config epoch:config.epoch reply:^(NSString * _Nullable peerID, NSData * _Nullable permanentInfo, NSData * _Nullable permanentInfoSig, NSData * _Nullable stableInfo, NSData * _Nullable stableInfoSig, NSError * _Nullable error) {
+ [cfshContext rpcPrepareIdentityAsApplicantWithConfiguration:config
+ epoch:config.epoch
+ reply:^(NSString * _Nullable peerID,
+ NSData * _Nullable permanentInfo,
+ NSData * _Nullable permanentInfoSig,
+ NSData * _Nullable stableInfo,
+ NSData * _Nullable stableInfoSig,
+ NSError * _Nullable error) {
reply(peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error);
}];
}
- (void)rpcJoinWithConfiguration:(OTJoiningConfiguration*)config
vouchData:(NSData*)vouchData
vouchSig:(NSData*)vouchSig
- preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
reply:(void (^)(NSError * _Nullable error))reply
{
OTCuttlefishContext* cfshContext = [self contextForContainerName:config.containerName contextID:config.contextID];
[cfshContext handlePairingRestart:config];
[cfshContext startOctagonStateMachine];
- [cfshContext rpcJoin:vouchData vouchSig:vouchSig preapprovedKeys:preapprovedKeys reply:^(NSError * _Nullable error) {
+ [cfshContext rpcJoin:vouchData vouchSig:vouchSig reply:^(NSError * _Nullable error) {
reply(error);
}];
}
secnotice("octagon-analytics", "Error fetching Octagon metadata: %@", metadataError);
}
values[OctagonAnalyticIcloudAccountState] = metadata ? @(metadata.icloudAccountState) : nil;
+ values[OctagonAnalyticCDPBitStatus] = metadata? @(metadata.cdpState) : nil;
values[OctagonAnalyticsTrustState] = metadata ? @(metadata.trustState) : nil;
NSDate* healthCheck = [cuttlefishContext currentMemoizedLastHealthCheck];
[cfshContext startOctagonStateMachine];
[cfshContext joinWithRecoveryKey:recoveryKey reply:^(NSError *error) {
- if (error.code == TrustedPeersHelperErrorCodeNotEnrolled && [error.domain isEqualToString:TrustedPeersHelperErrorDomain]) {
- // If we hit this error, let's reset and establish octagon then enroll this recovery key in the new circle
+ if ((error.code == TrustedPeersHelperErrorCodeNotEnrolled || error.code == TrustedPeersHelperErrorCodeUntrustedRecoveryKeys)
+ && [error.domain isEqualToString:TrustedPeersHelperErrorDomain]){
+ // If we hit either of these errors, let's reset and establish octagon then enroll this recovery key in the new circle
secerror("octagon, recovery key is not enrolled in octagon, resetting octagon circle");
[[self.loggerClass logger] logResultForEvent:OctagonEventJoinRecoveryKeyCircleReset hardFailure:NO result:error];
}];
}
-- (void)healthCheck:(NSString *)container context:(NSString *)context skipRateLimitingCheck:(BOOL)skipRateLimitingCheck reply:(void (^)(NSError *_Nullable error))reply
+- (void)xpc24HrNotification
{
- [self xpc24HrNotification:container context:context skipRateLimitingCheck:skipRateLimitingCheck reply:reply];
-}
+ secnotice("octagon-health", "Received 24hr xpc notification");
+ [self healthCheck:OTCKContainerName context:OTDefaultContext skipRateLimitingCheck:NO reply:^(NSError * _Nullable error) {
+ if(error) {
+ secerror("octagon: error attempting to check octagon health: %@", error);
+ } else {
+ secnotice("octagon", "health check success");
+ }
+ }];
+}
-- (void)xpc24HrNotification:(NSString* _Nullable)containerName context:(NSString*)context skipRateLimitingCheck:(BOOL)skipRateLimitingCheck reply:(void (^)(NSError *error))reply
+- (void)healthCheck:(NSString *)container context:(NSString *)context skipRateLimitingCheck:(BOOL)skipRateLimitingCheck reply:(void (^)(NSError *_Nullable error))reply
{
- secnotice("octagon-health", "Received 24 xpc notification");
-
- OTCuttlefishContext* cfshContext = [self contextForContainerName:containerName contextID:context];
+ OTCuttlefishContext* cfshContext = [self contextForContainerName:container contextID:context];
secnotice("octagon", "notifying container of change");
{
for(OTCuttlefishContext* context in self.contexts.allValues) {
[context.stateMachine haltOperation];
+
+ // Also, clear the viewManager strong pointer
+ [context clearCKKSViewManager];
}
}
[[CKKSAnalytics logger] setNumberProperty:NULL forKey:countName];
}
- // Clear all CFU state variables, too
- [cuttlefishContext clearPendingCFUFlags];
-
// Always return without error
reply(nil);
}
reply(NULL);
}
+- (void)refetchCKKSPolicy:(NSString * _Nullable)containerName
+ contextID:(nonnull NSString *)contextID
+ reply:(nonnull void (^)(NSError * _Nullable))reply {
+ secnotice("octagon-ckks", "refetch-ckks-policy");
+
+ if(!containerName) {
+ containerName = OTCKContainerName;
+ }
+
+ OTCuttlefishContext* cfshContext = [self contextForContainerName:containerName contextID:contextID];
+
+ if(!cfshContext) {
+ reply([NSError errorWithDomain:OctagonErrorDomain
+ code:OTErrorNoSuchContext
+ description:[NSString stringWithFormat:@"No context for (%@,%@)", containerName, contextID]]);
+ return;
+ }
+
+ [cfshContext rpcRefetchCKKSPolicy:^(NSError* error) {
+ secnotice("octagon-ckks", "refetch-ckks-policy result: %@", error ?: @"no error");
+ reply(error);
+ }];
+}
+
+
+- (void)getCDPStatus:(NSString * _Nullable)containerName
+ contextID:(nonnull NSString *)contextID
+ reply:(nonnull void (^)(OTCDPStatus, NSError * _Nullable))reply {
+ secnotice("octagon-cdp", "get-cdp-status");
+
+ if(!containerName) {
+ containerName = OTCKContainerName;
+ }
+
+ OTCuttlefishContext* cfshContext = [self contextForContainerName:containerName contextID:contextID];
+
+ if(!cfshContext) {
+ reply(OTCDPStatusUnknown, [NSError errorWithDomain:OctagonErrorDomain
+ code:OTErrorNoSuchContext
+ description:[NSString stringWithFormat:@"No context for (%@,%@)", containerName, contextID]]);
+ return;
+ }
+
+ NSError* error = nil;
+ OTCDPStatus status = [cfshContext getCDPStatus:&error];
+
+ reply(status, error);
+}
+
+
+- (void)setCDPEnabled:(NSString * _Nullable)containerName
+ contextID:(nonnull NSString *)contextID
+ reply:(nonnull void (^)(NSError * _Nullable))reply {
+ secnotice("octagon-cdp", "set-cdp-enabled");
+ if(!containerName) {
+ containerName = OTCKContainerName;
+ }
+
+ OTCuttlefishContext* cfshContext = [self contextForContainerName:containerName contextID:contextID];
+
+ if(!cfshContext) {
+ reply([NSError errorWithDomain:OctagonErrorDomain
+ code:OTErrorNoSuchContext
+ description:[NSString stringWithFormat:@"No context for (%@,%@)", containerName, contextID]]);
+ return;
+ }
+
+ NSError* localError = nil;
+ [cfshContext setCDPEnabled:&localError];
+
+ reply(localError);
+}
+
@end
#endif
@property id<OTAuthKitAdapter> authKitAdapter;
@property id<OTDeviceInformationAdapter> deviceInformationAdapter;
@property (readonly) CuttlefishXPCWrapper* cuttlefishXPCWrapper;
-@property CKKSViewManager* viewManager;
+@property (readonly, weak) CKKSViewManager* viewManager;
@property CKKSLockStateTracker* lockStateTracker;
@property Class<SecEscrowRequestable> escrowRequestClass;
#if OCTAGON
#import <Foundation/Foundation.h>
+#import <TrustedPeers/TrustedPeers.h>
#import "keychain/ckks/CKKSGroupOperation.h"
#import "keychain/ot/OctagonStateMachineHelpers.h"
intendedState:(OctagonState*)intendedState
errorState:(OctagonState*)errorState
deviceInfo:(OTDeviceInformation*)deviceInfo
+ policyOverride:(TPPolicyVersion* _Nullable)policyOverride
epoch:(uint64_t)epoch;
@property (nonatomic) uint64_t epoch;
@property (nullable) NSData* stableInfo;
@property (nullable) NSData* stableInfoSig;
+@property (nullable) TPPolicyVersion* policyOverride;
+
@end
NS_ASSUME_NONNULL_END
#import <Security/SecKeyPriv.h>
#import "keychain/ot/OTCuttlefishContext.h"
-#import "keychain/ot/OTFetchViewsOperation.h"
#import "keychain/ot/OTOperationDependencies.h"
#import "keychain/ot/OTPrepareOperation.h"
intendedState:(OctagonState*)intendedState
errorState:(OctagonState*)errorState
deviceInfo:(OTDeviceInformation*)deviceInfo
+ policyOverride:(TPPolicyVersion* _Nullable)policyOverride
epoch:(uint64_t)epoch
{
if((self = [super init])) {
_intendedState = intendedState;
_nextState = errorState;
+
+ _policyOverride = policyOverride;
}
return self;
}
deviceName:self.deviceInfo.deviceName
serialNumber:self.deviceInfo.serialNumber
osVersion:self.deviceInfo.osVersion
- policyVersion:nil
+ policyVersion:self.policyOverride
policySecrets:nil
signingPrivKeyPersistentRef:signingKeyPersistRef
encPrivKeyPersistentRef:encryptionKeyPersistRef
- reply:^(NSString * _Nullable peerID, NSData * _Nullable permanentInfo, NSData * _Nullable permanentInfoSig, NSData * _Nullable stableInfo, NSData * _Nullable stableInfoSig, NSError * _Nullable error) {
+ reply:^(NSString * _Nullable peerID,
+ NSData * _Nullable permanentInfo,
+ NSData * _Nullable permanentInfoSig,
+ NSData * _Nullable stableInfo,
+ NSData * _Nullable stableInfoSig,
+ NSSet<NSString*>* _Nullable syncingViews,
+ TPPolicy* _Nullable syncingPolicy,
+ NSError * _Nullable error) {
STRONGIFY(self);
[[CKKSAnalytics logger] logResultForEvent:OctagonEventPrepareIdentity hardFailure:true result:error];
if(error) {
self.stableInfoSig = stableInfoSig;
NSError* localError = nil;
- BOOL persisted = [self.deps.stateHolder persistNewEgoPeerID:peerID error:&localError];
+
+ secnotice("octagon-ckks", "New syncing policy: %@ views: %@", syncingPolicy, syncingViews);
+
+ BOOL persisted = [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nullable(OTAccountMetadataClassC * _Nonnull metadata) {
+ metadata.peerID = peerID;
+ metadata.syncingViews = [syncingViews mutableCopy];
+ [metadata setTPPolicy:syncingPolicy];
+
+ return metadata;
+ } error:&localError];
+
if(!persisted || localError) {
- secnotice("octagon", "Couldn't persist peer ID: %@", localError);
+ secnotice("octagon", "Couldn't persist metadata: %@", localError);
self.error = localError;
[self runBeforeGroupFinished:self.finishedOp];
- } else {
- WEAKIFY(self);
-
- CKKSResultOperation *doneOp = [CKKSResultOperation named:@"ot-prepare"
- withBlock:^{
- STRONGIFY(self);
- self.nextState = self.intendedState;
- }];
-
- OTFetchViewsOperation *fetchViewsOp = [[OTFetchViewsOperation alloc] initWithDependencies:self.deps];
- [self runBeforeGroupFinished:fetchViewsOp];
- [doneOp addDependency:fetchViewsOp];
- [self runBeforeGroupFinished:doneOp];
- [self.finishedOp addDependency:doneOp];
- [self runBeforeGroupFinished:self.finishedOp];
+ return;
}
+
+ // Let CKKS know of the new policy, so it can spin up
+ [self.deps.viewManager setSyncingViews:syncingViews sortingPolicy:syncingPolicy];
+
+ self.nextState = self.intendedState;
+ [self runBeforeGroupFinished:self.finishedOp];
}
}];
}
for(CKKSCurrentKeySet* incompleteKeySet in incompleteKeySets) {
if(incompleteKeySet.error == nil) {
- CKKSKeychainView* viewMatchingSet = [self.deps.viewManager findView:incompleteKeySet.viewName];
+ CKKSViewManager* viewManager = self.deps.viewManager;
+ CKKSKeychainView* viewMatchingSet = [viewManager findView:incompleteKeySet.viewName];
if(!viewMatchingSet) {
secnotice("octagon-ckks", "No view matching viewset %@?", incompleteKeySet);
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initAsEssential:(BOOL)essential;
-// Helper methods
-+ (NSArray<NSData*>*)peerPublicSigningKeySPKIs:(NSSet<id<CKKSPeer>>* _Nullable)peers;
-
+// Helper methods.
+ (NSSet<NSString*>*)sosCKKSViewList;
@end
+
// This adapter is for a platform which does not have SOS (e.g., aTV, Watch, HomePod)
@interface OTSOSMissingAdapter : NSObject <OTSOSAdapter>
@end
+// Helper code
+@interface OTSOSAdapterHelpers : NSObject
++ (NSArray<NSData*>* _Nullable)peerPublicSigningKeySPKIsForCircle:(id<OTSOSAdapter>)sosAdapter error:(NSError**)error;
+@end
+
NS_ASSUME_NONNULL_END
#endif // OCTAGON
code:CKKSNoPeersAvailable
description:@"Not in SOS circle, but no error returned"];
}
- secerror("octagon-sos: Not in circle : %d %@", circleStatus, localerror);
+ secerror("octagon-sos: Not in circle : %@ %@", SOSAccountGetSOSCCStatusString(circleStatus), localerror);
if(error) {
*error = localerror;
}
return result;
}
-
-+ (NSArray<NSData*>*)peerPublicSigningKeySPKIs:(NSSet<id<CKKSPeer>>* _Nullable)peerSet
-{
- NSMutableArray<NSData*>* publicSigningSPKIs = [NSMutableArray array];
-
- for(id<CKKSPeer> peer in peerSet) {
- NSData* spki = [peer.publicSigningKey encodeSubjectPublicKeyInfo];
- if(!spki) {
- secerror("octagon-sos: Can't create SPKI for peer: %@", peer);
- } else {
- secerror("octagon-sos: Created SPKI for peer: %@", peer);
- [publicSigningSPKIs addObject:spki];
- }
- }
- return publicSigningSPKIs;
-}
@end
@implementation OTSOSMissingAdapter
trustedPeersError:unimplementedError];
}
+@end
+
+@implementation OTSOSAdapterHelpers
+
++ (NSArray<NSData*>*)peerPublicSigningKeySPKIs:(NSSet<id<CKKSPeer>>* _Nullable)peerSet
+{
+ NSMutableArray<NSData*>* publicSigningSPKIs = [NSMutableArray array];
+
+ for(id<CKKSPeer> peer in peerSet) {
+ NSData* spki = [peer.publicSigningKey encodeSubjectPublicKeyInfo];
+ if(!spki) {
+ secerror("octagon-sos: Can't create SPKI for peer: %@", peer);
+ } else {
+ secerror("octagon-sos: Created SPKI for peer: %@", peer);
+ [publicSigningSPKIs addObject:spki];
+ }
+ }
+ return publicSigningSPKIs;
+}
+
++ (NSArray<NSData*>* _Nullable)peerPublicSigningKeySPKIsForCircle:(id<OTSOSAdapter>)sosAdapter error:(NSError**)error
+{
+ NSError* peerError = nil;
+ SOSCCStatus sosStatus = [sosAdapter circleStatus:&peerError];
+
+ if(sosStatus != kSOSCCInCircle || peerError) {
+ secerror("octagon-sos: Not in circle; not preapproving keys: %@ (%@)", SOSAccountGetSOSCCStatusString(sosStatus), peerError);
+ if(error) {
+ *error = peerError;
+ }
+ return nil;
+ } else {
+ // We're in-circle: preapprove these peers
+ NSError* peerError = nil;
+ NSSet<id<CKKSRemotePeerProtocol>>* peerSet = [sosAdapter fetchTrustedPeers:&peerError];
+ if(!peerSet || peerError) {
+ secerror("octagon-sos: Can't fetch trusted peer SPKIs: %@", peerError);
+ if(error) {
+ *error = peerError;
+ }
+ return nil;
+ }
+
+ return [self peerPublicSigningKeySPKIs:peerSet];
+ }
+}
@end
#endif // OCTAGON
}];
[self dependOnBeforeGroupFinished:self.finishedOp];
- NSError* error = nil;
- NSSet<id<CKKSRemotePeerProtocol>>* peerSet = [self.deps.sosAdapter fetchTrustedPeers:&error];
+ NSError* sosPreapprovalError = nil;
+ NSArray<NSData*>* publicSigningSPKIs = [OTSOSAdapterHelpers peerPublicSigningKeySPKIsForCircle:self.deps.sosAdapter error:&sosPreapprovalError];
- if(!peerSet || error) {
- secerror("octagon-sos: Can't fetch trusted peers; stopping preapproved key update: %@", error);
- self.error = error;
+ if(!publicSigningSPKIs || sosPreapprovalError) {
+ secerror("octagon-sos: Can't fetch trusted peers; stopping preapproved key update: %@", sosPreapprovalError);
+ self.error = sosPreapprovalError;
self.nextState = self.sosNotPresentState;
[self runBeforeGroupFinished:self.finishedOp];
return;
}
- NSArray<NSData*>* publicSigningSPKIs = [OTSOSActualAdapter peerPublicSigningKeySPKIs:peerSet];
secnotice("octagon-sos", "Updating SOS preapproved keys to %@", publicSigningSPKIs);
[self.deps.cuttlefishXPCWrapper setPreapprovedKeysWithContainer:self.deps.containerName
context:self.deps.contextID
preapprovedKeys:publicSigningSPKIs
- reply:^(NSError* error) {
+ reply:^(TrustedPeersHelperPeerState* _Nullable peerState, NSError* error) {
STRONGIFY(self);
if(error) {
secerror("octagon-sos: unable to update preapproved keys: %@", error);
self.error = error;
} else {
secnotice("octagon-sos", "Updated SOS preapproved keys");
+
+ if (peerState.memberChanges) {
+ secnotice("octagon", "Member list changed");
+ [self.deps.octagonAdapter sendTrustedPeerSetChangedUpdate];
+ }
+
self.nextState = self.intendedState;
}
[self runBeforeGroupFinished:self.finishedOp];
#if OCTAGON
#import <Foundation/Foundation.h>
+#import <TrustedPeers/TrustedPeers.h>
#import "keychain/ckks/CKKSGroupOperation.h"
#import "keychain/ot/OctagonStateMachineHelpers.h"
#import "keychain/ot/OTStates.h"
@interface OTSOSUpgradeOperation : CKKSGroupOperation <OctagonStateTransitionOperationProtocol>
+@property (readonly, nullable) TPPolicyVersion* policyOverride;
+
- (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
intendedState:(OctagonState*)intendedState
ckksConflictState:(OctagonState*)ckksConflictState
errorState:(OctagonState*)errorState
- deviceInfo:(OTDeviceInformation*)deviceInfo;
+ deviceInfo:(OTDeviceInformation*)deviceInfo
+ policyOverride:(TPPolicyVersion* _Nullable)policyOverride;
@end
ckksConflictState:(OctagonState*)ckksConflictState
errorState:(OctagonState*)errorState
deviceInfo:(OTDeviceInformation*)deviceInfo
+ policyOverride:(TPPolicyVersion* _Nullable)policyOverride
{
if((self = [super init])) {
_deps = dependencies;
_ckksConflictState = ckksConflictState;
_deviceInfo = deviceInfo;
+ _policyOverride = policyOverride;
}
return self;
}
return;
}
- // Now that we have some non-error SOS status, write down that we attempted an SOS Upgrade.
+ // Now that we have some non-error SOS status, write down that we attempted an SOS Upgrade (and make sure the CDP bit is on)
NSError* persistError = nil;
- BOOL persisted = [self.deps.stateHolder persistOctagonJoinAttempt:OTAccountMetadataClassC_AttemptedAJoinState_ATTEMPTED error:&persistError];
+ BOOL persisted = [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
+ metadata.attemptedJoin = OTAccountMetadataClassC_AttemptedAJoinState_ATTEMPTED;
+ metadata.cdpState = OTAccountMetadataClassC_CDPState_ENABLED;
+ return metadata;
+ } error:&persistError];
if(!persisted || persistError) {
secerror("octagon: failed to save 'attempted join' state: %@", persistError);
}
deviceName:self.deviceInfo.deviceName
serialNumber:self.self.deviceInfo.serialNumber
osVersion:self.deviceInfo.osVersion
- policyVersion:nil
+ policyVersion:self.policyOverride
policySecrets:nil
signingPrivKeyPersistentRef:signingKeyPersistRef
encPrivKeyPersistentRef:encryptionKeyPersistRef
NSData * _Nullable permanentInfoSig,
NSData * _Nullable stableInfo,
NSData * _Nullable stableInfoSig,
+ NSSet<NSString*>* syncingViews,
+ TPPolicy* _Nullable syncingPolicy,
NSError * _Nullable error) {
STRONGIFY(self);
[self handlePrepareErrors:error nextExpectedState:OctagonStateBecomeUntrusted];
[self runBeforeGroupFinished:self.finishedOp];
- } else {
- secnotice("octagon-sos", "Prepared: %@ %@ %@", peerID, permanentInfo, permanentInfoSig);
+ return;
+ }
+
+ secnotice("octagon-sos", "Prepared: %@ %@ %@", peerID, permanentInfo, permanentInfoSig);
+
+ NSError* localError = nil;
+ BOOL persisted = [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nullable(OTAccountMetadataClassC * _Nonnull metadata) {
+ metadata.syncingViews = [syncingViews mutableCopy];
+ [metadata setTPPolicy:syncingPolicy];
+ return metadata;
+ } error:&localError];
- [self afterPrepare];
+ if(!persisted || localError) {
+ secerror("octagon-ckks: Error persisting new views and policy: %@", localError);
+ self.error = localError;
+ [self handlePrepareErrors:error nextExpectedState:OctagonStateBecomeUntrusted];
+ [self runBeforeGroupFinished:self.finishedOp];
+ return;
}
+ [self.deps.viewManager setSyncingViews:syncingViews sortingPolicy:syncingPolicy];
+
+ [self afterPrepare];
}];
}
reply:^(BOOL launchOkay, NSError * _Nullable error) {
STRONGIFY(self);
+ // Preflight preapprovedjoin should return the policy used by the peers we eventually will trust
+ // <rdar://problem/57768490> Octagon: ensure we use appropriate CKKS policy when joining octagon with future policy
+
[[CKKSAnalytics logger] logResultForEvent:OctagonEventUpgradePreflightPreapprovedJoin hardFailure:true result:error];
if(error) {
secerror("octagon-sos: preflightPreapprovedJoin failed: %@", error);
{
WEAKIFY(self);
- OTFetchViewsOperation *fetchViews = [[OTFetchViewsOperation alloc] initWithDependencies:self.deps];
- [self runBeforeGroupFinished:fetchViews];
-
OTFetchCKKSKeysOperation* fetchKeysOp = [[OTFetchCKKSKeysOperation alloc] initWithDependencies:self.deps];
- [fetchKeysOp addDependency:fetchViews];
[self runBeforeGroupFinished:fetchKeysOp];
secnotice("octagon-sos", "Fetching keys from CKKS");
secnotice("octagon-sos", "Fetching trusted peers from SOS");
- NSError* error = nil;
- NSSet<id<CKKSRemotePeerProtocol>>* peerSet = [self.deps.sosAdapter fetchTrustedPeers:&error];
+ NSArray<NSData*>* publicSigningSPKIs = nil;
+ if(self.deps.sosAdapter.sosEnabled) {
+ NSError* sosPreapprovalError = nil;
+ publicSigningSPKIs = [OTSOSAdapterHelpers peerPublicSigningKeySPKIsForCircle:self.deps.sosAdapter error:&sosPreapprovalError];
- if(!peerSet || error) {
- secerror("octagon-sos: Can't fetch trusted peers; stopping upgrade: %@", error);
- self.error = error;
- self.nextState = OctagonStateBecomeUntrusted;
- [self runBeforeGroupFinished:self.finishedOp];
- return;
- }
+ if(publicSigningSPKIs) {
+ secnotice("octagon-sos", "SOS preapproved keys are %@", publicSigningSPKIs);
+ } else {
+ secnotice("octagon-sos", "Unable to fetch SOS preapproved keys: %@", sosPreapprovalError);
+ }
- NSArray<NSData*>* publicSigningSPKIs = [OTSOSActualAdapter peerPublicSigningKeySPKIs:peerSet];
- secnotice("octagon-sos", "Creating SOS preapproved keys as %@", publicSigningSPKIs);
+ } else {
+ secnotice("octagon-sos", "SOS not enabled; no preapproved keys");
+ }
- secnotice("octagon-sos", "Beginning SOS upgrade with %d key sets and %d SOS peers", (int)viewKeySets.count, (int)peerSet.count);
+ secnotice("octagon-sos", "Beginning SOS upgrade with %d key sets and %d SOS peers", (int)viewKeySets.count, (int)publicSigningSPKIs.count);
[self.deps.cuttlefishXPCWrapper attemptPreapprovedJoinWithContainer:self.deps.containerName
context:self.deps.contextID
ckksKeys:viewKeySets
tlkShares:pendingTLKShares
preapprovedKeys:publicSigningSPKIs
- reply:^(NSString * _Nullable peerID, NSArray<CKRecord*>* keyHierarchyRecords, NSError * _Nullable error) {
- STRONGIFY(self);
+ reply:^(NSString * _Nullable peerID,
+ NSArray<CKRecord*>* keyHierarchyRecords,
+ NSSet<NSString*>* _Nullable syncingViewList,
+ TPPolicy* _Nullable syncingPolicy,
+ NSError * _Nullable error) {
+ STRONGIFY(self);
- [[CKKSAnalytics logger] logResultForEvent:OctagonEventUpgradePreapprovedJoin hardFailure:true result:error];
- if(error) {
- secerror("octagon-sos: attemptPreapprovedJoin failed: %@", error);
-
- if ([error isCuttlefishError:CuttlefishErrorKeyHierarchyAlreadyExists]) {
- secnotice("octagon-ckks", "A CKKS key hierarchy is out of date; requesting reset");
- self.nextState = self.ckksConflictState;
- } else {
- self.error = error;
- self.nextState = OctagonStateBecomeUntrusted;
- }
- [self runBeforeGroupFinished:self.finishedOp];
- return;
+ [[CKKSAnalytics logger] logResultForEvent:OctagonEventUpgradePreapprovedJoin hardFailure:true result:error];
+ if(error) {
+ secerror("octagon-sos: attemptPreapprovedJoin failed: %@", error);
+
+ if ([error isCuttlefishError:CuttlefishErrorKeyHierarchyAlreadyExists]) {
+ secnotice("octagon-ckks", "A CKKS key hierarchy is out of date; requesting reset");
+ self.nextState = self.ckksConflictState;
+ } else {
+ self.error = error;
+ self.nextState = OctagonStateBecomeUntrusted;
}
+ [self runBeforeGroupFinished:self.finishedOp];
+ return;
+ }
- [self requestSilentEscrowUpdate];
+ [self requestSilentEscrowUpdate];
- secerror("octagon-sos: attemptPreapprovedJoin succeded");
+ secerror("octagon-sos: attemptPreapprovedJoin succeded");
+ [self.deps.viewManager setSyncingViews:syncingViewList sortingPolicy:syncingPolicy];
- NSError* localError = nil;
- BOOL persisted = [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
- metadata.trustState = OTAccountMetadataClassC_TrustState_TRUSTED;
- metadata.peerID = peerID;
- return metadata;
- } error:&localError];
+ NSError* localError = nil;
+ BOOL persisted = [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
+ metadata.trustState = OTAccountMetadataClassC_TrustState_TRUSTED;
+ metadata.peerID = peerID;
+ metadata.syncingViews = [syncingViewList mutableCopy];
+ [metadata setTPPolicy:syncingPolicy];
- if(!persisted || localError) {
- secnotice("octagon-sos", "Couldn't persist results: %@", localError);
- self.error = localError;
- self.nextState = OctagonStateError;
- [self runBeforeGroupFinished:self.finishedOp];
- return;
- }
+ return metadata;
+ } error:&localError];
- self.nextState = self.intendedState;
+ if(!persisted || localError) {
+ secnotice("octagon-sos", "Couldn't persist results: %@", localError);
+ self.error = localError;
+ self.nextState = OctagonStateError;
+ [self runBeforeGroupFinished:self.finishedOp];
+ return;
+ }
- // Tell CKKS about our shiny new records!
- for (id key in self.deps.viewManager.views) {
- CKKSKeychainView* view = self.deps.viewManager.views[key];
- secnotice("octagon-ckks", "Providing ck records (from sos upgrade) to %@", view);
- [view receiveTLKUploadRecords: keyHierarchyRecords];
- }
+ self.nextState = self.intendedState;
- [self runBeforeGroupFinished:self.finishedOp];
- }];
+ // Tell CKKS about our shiny new records!
+ for (id key in self.deps.viewManager.views) {
+ CKKSKeychainView* view = self.deps.viewManager.views[key];
+ secnotice("octagon-ckks", "Providing ck records (from sos upgrade) to %@", view);
+ [view receiveTLKUploadRecords: keyHierarchyRecords];
+ }
+
+ [self runBeforeGroupFinished:self.finishedOp];
+ }];
}
@end
--- /dev/null
+/*
+ * Copyright (c) 2019 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#if OCTAGON
+
+#import <Foundation/Foundation.h>
+#import "keychain/ckks/CKKSGroupOperation.h"
+#import "keychain/ot/OctagonStateMachineHelpers.h"
+#import "keychain/ot/OTOperationDependencies.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface OTSetCDPBitOperation : CKKSGroupOperation <OctagonStateTransitionOperationProtocol>
+
+- (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
+ intendedState:(OctagonState*)intendedState
+ errorState:(OctagonState*)errorState;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif // OCTAGON
--- /dev/null
+/*
+ * Copyright (c) 2019 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#if OCTAGON
+
+#import "utilities/debugging.h"
+
+#import "keychain/ot/OTSetCDPBitOperation.h"
+#import "keychain/ot/ObjCImprovements.h"
+
+@interface OTSetCDPBitOperation ()
+@property OTOperationDependencies* deps;
+
+@property NSOperation* finishedOp;
+@end
+
+@implementation OTSetCDPBitOperation
+@synthesize intendedState = _intendedState;
+@synthesize nextState = _nextState;
+
+- (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
+ intendedState:(OctagonState*)intendedState
+ errorState:(OctagonState*)errorState
+{
+ if((self = [super init])) {
+ _deps = dependencies;
+ _intendedState = intendedState;
+ _nextState = errorState;
+ }
+ return self;
+}
+
+- (void)groupStart
+{
+ NSError* localError = nil;
+ [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
+ metadata.cdpState = OTAccountMetadataClassC_CDPState_ENABLED;
+ return metadata;
+ } error:&localError];
+
+ if(localError) {
+ secnotice("octagon-cdp-status", "Unable to set CDP bit: %@", localError);
+ } else {
+ secnotice("octagon-cdp-status", "Successfully set CDP bit");
+ self.nextState = self.intendedState;
+ }
+}
+
+@end
+
+#endif // OCTAGON
// No iCloud Account (the state machine won't help at all)
// Untrusted (user interaction is required to resolve)
// WaitForHSA2 (there's some primary icloud account, but it's not HSA2 (yet))
+// WaitForCDP (there's some HSA2 primary icloud account, but it's not CDP-enabled (yet)
extern OctagonState* const OctagonStateNoAccount;
extern OctagonState* const OctagonStateUntrusted;
extern OctagonState* const OctagonStateWaitForHSA2;
+extern OctagonState* const OctagonStateWaitForCDP;
// Entering this state will mark down that the device is untrusted, then go to OctagonStateUntrusted
extern OctagonState* const OctagonStateBecomeUntrusted;
// This state runs any final preparation to enter the Ready state
extern OctagonState* const OctagonStateBecomeReady;
+// BecomeReady might go here, if it's not actually ready
+extern OctagonState* const OctagonStateRefetchCKKSPolicy;
+
// Enter this state if you'd like the state machine to double-check everything
extern OctagonState* const OctagonStateEnsureConsistency;
extern OctagonState* const OctagonStateEnsureOctagonKeysAreConsistent;
extern OctagonState* const OctagonStateInitializing;
extern OctagonState* const OctagonStateWaitingForCloudKitAccount;
extern OctagonState* const OctagonStateCloudKitNewlyAvailable;
+extern OctagonState* const OctagonStateDetermineCDPState;
extern OctagonState* const OctagonStateCheckTrustState;
/*Piggybacking and ProximitySetup as Initiator, Octagon only*/
extern OctagonState* const OctagonStateInitiatorAwaitingVoucher;
+extern OctagonState* const OctagonStateInitiatorSetCDPBit;
extern OctagonState* const OctagonStateInitiatorUpdateDeviceList;
extern OctagonState* const OctagonStateInitiatorJoin;
extern OctagonState* const OctagonStateInitiatorJoinCKKSReset;
extern OctagonState* const OctagonStateInitiatorJoinAfterCKKSReset;
-extern OctagonState* const OctagonStateInitiatorVouchWithBottle;
+extern OctagonState* const OctagonStateBottleJoinVouchWithBottle;
extern OctagonState* const OctagonStateIdentityPrepared;
// OctagonStateIdentityPrepared leads directly to
extern OctagonState* const OctagonStateDeviceListUpdated;
/* used for join with bottle */
-extern OctagonState* const OctagonStateInitiatorCreateIdentity;
+extern OctagonState* const OctagonStateBottleJoinCreateIdentity;
/* used for join with recovery key */
extern OctagonState* const OctagonStateCreateIdentityForRecoveryKey;
extern OctagonState* const OctagonStateResetBecomeUntrusted;
extern OctagonState* const OctagonStateResetAndEstablish;
extern OctagonState* const OctagonStateResetAnyMissingTLKCKKSViews;
+extern OctagonState* const OctagonStateEstablishEnableCDPBit;
extern OctagonState* const OctagonStateReEnactDeviceList;
extern OctagonState* const OctagonStateReEnactPrepare;
extern OctagonState* const OctagonStateReEnactReadyToEstablish;
/* used for trust health checks */
extern OctagonState* const OctagonStateHSA2HealthCheck;
+extern OctagonState* const OctagonStateCDPHealthCheck;
extern OctagonState* const OctagonStateSecurityTrustCheck;
extern OctagonState* const OctagonStateTPHTrustCheck;
extern OctagonState* const OctagonStateCuttlefishTrustCheck;
// End of account reset state flow
+//Leave Clique
+extern OctagonState* const OctagonStateHealthCheckLeaveClique;
+
// Part of the signout flow
extern OctagonState* const OctagonStateNoAccountDoReset;
//
// Call out to otpaird (KCPairing via IDS), then proceed to BecomeUntrusted
extern OctagonState* const OctagonStateStartCompanionPairing;
+// Cuttlefish notification while waiting for CDP
+extern OctagonState* const OctagonStateWaitForCDPUpdated;
+
// Untrusted cuttlefish notification.
extern OctagonState* const OctagonStateUntrustedUpdated;
extern OctagonFlag* const OctagonFlagFetchAuthKitMachineIDList;
extern OctagonFlag* const OctagonFlagAccountIsAvailable;
+extern OctagonFlag* const OctagonFlagCDPEnabled;
extern OctagonFlag* const OctagonFlagAttemptSOSUpgrade;
extern OctagonFlag* const OctagonFlagUnlocked;
OctagonState* const OctagonStateNoAccount = (OctagonState*) @"no_account";
OctagonState* const OctagonStateWaitForHSA2 = (OctagonState*) @"wait_for_hsa2";
+OctagonState* const OctagonStateWaitForCDP = (OctagonState*) @"wait_for_cdp_enable";
OctagonState* const OctagonStateUntrusted = (OctagonState*) @"untrusted";
OctagonState* const OctagonStateBecomeUntrusted = (OctagonState*) @"become_untrusted";
OctagonState* const OctagonStateInitializing = (OctagonState*) @"initializing";
OctagonState* const OctagonStateWaitingForCloudKitAccount = (OctagonState*) @"waiting_for_cloudkit_account";
OctagonState* const OctagonStateCloudKitNewlyAvailable = (OctagonState*) @"account_newly_available";
+OctagonState* const OctagonStateRefetchCKKSPolicy = (OctagonState*) @"ckks_fetch_policy";
+OctagonState* const OctagonStateDetermineCDPState = (OctagonState*) @"check_cdp_state";
OctagonState* const OctagonStateCheckTrustState = (OctagonState*) @"check_trust_state";
OctagonState* const OctagonStateUpdateSOSPreapprovals = (OctagonState*) @"update_sos_preapprovals";
/*Piggybacking and ProximitySetup as Initiator Octagon only*/
+OctagonState* const OctagonStateInitiatorSetCDPBit = (OctagonState*) @"initiator_set_cdp";
OctagonState* const OctagonStateInitiatorUpdateDeviceList = (OctagonState*) @"initiator_device_list_update";
OctagonState* const OctagonStateInitiatorAwaitingVoucher = (OctagonState*)@"await_voucher";
OctagonState* const OctagonStateInitiatorJoin = (OctagonState*)@"join";
OctagonState* const OctagonStateInitiatorJoinAfterCKKSReset = (OctagonState*)@"join_after_ckks_reset";
/* used in restore (join with bottle)*/
-OctagonState* const OctagonStateInitiatorCreateIdentity = (OctagonState*)@"create_identity";
-OctagonState* const OctagonStateInitiatorVouchWithBottle = (OctagonState*)@"vouchWithBottle";
+OctagonState* const OctagonStateBottleJoinCreateIdentity = (OctagonState*)@"bottle_join_create_identity";
+OctagonState* const OctagonStateBottleJoinVouchWithBottle = (OctagonState*)@"bottle_join_vouch_with_bottle";
OctagonState* const OctagonStateCreateIdentityForRecoveryKey = (OctagonState*)@"vouchWithRecovery";
/* used in resotre (join with recovery key)*/
OctagonState* const OctagonStateStartCompanionPairing = (OctagonState*)@"start_companion_pairing";
+OctagonState* const OctagonStateWaitForCDPUpdated = (OctagonState*)@"wait_for_cdp_update";
+
// Untrusted cuttlefish notification.
OctagonState* const OctagonStateUntrustedUpdated = (OctagonState*)@"untrusted_update";
OctagonState* const OctagonStateResetBecomeUntrusted = (OctagonState*) @"reset_become_untrusted";
OctagonState* const OctagonStateResetAndEstablish = (OctagonState*) @"reset_and_establish";
OctagonState* const OctagonStateResetAnyMissingTLKCKKSViews = (OctagonState*) @"reset_ckks_missing_views";
+OctagonState* const OctagonStateEstablishEnableCDPBit = (OctagonState*) @"reenact_cdp_bit";
OctagonState* const OctagonStateReEnactDeviceList = (OctagonState*) @"reenact_device_list";
OctagonState* const OctagonStateReEnactPrepare = (OctagonState*) @"reenact_prepare";
OctagonState* const OctagonStateReEnactReadyToEstablish = (OctagonState*) @"reenact_ready_to_establish";
/* used for trust health checks */
OctagonState* const OctagonStateHSA2HealthCheck = (OctagonState*) @"health_hsa2_check";
+OctagonState* const OctagonStateCDPHealthCheck = (OctagonState*) @"health_cdp_check";
OctagonState* const OctagonStateTPHTrustCheck = (OctagonState*) @"tph_trust_check";
OctagonState* const OctagonStateCuttlefishTrustCheck = (OctagonState*) @"cuttlefish_trust_check";
OctagonState* const OctagonStatePostRepairCFU = (OctagonState*) @"post_repair_cfu";
OctagonState* const OctagonStateAssistCKKSTLKUploadCKKSReset = (OctagonState*) @"assist_ckks_tlk_upload_ckks_reset";
OctagonState* const OctagonStateAssistCKKSTLKUploadAfterCKKSReset = (OctagonState*) @"assist_ckks_tlk_upload_after_ckks_reset";
+OctagonState* const OctagonStateHealthCheckLeaveClique = (OctagonState*) @"leave_clique";
+
/* escrow */
OctagonState* const OctagonStateEscrowTriggerUpdate = (OctagonState*) @"escrow-trigger-update";
OctagonStateReEnactPrepare: @14U,
OctagonStateReEnactReadyToEstablish: @15U,
OctagonStateNoAccountDoReset: @16U,
- OctagonStateInitiatorVouchWithBottle: @17U,
- OctagonStateInitiatorCreateIdentity: @18U,
+ OctagonStateBottleJoinVouchWithBottle: @17U,
+ OctagonStateBottleJoinCreateIdentity: @18U,
OctagonStateCloudKitNewlyAvailable: @19U,
OctagonStateCheckTrustState: @20U,
OctagonStateBecomeUntrusted: @21U,
OctagonStateHealthCheckReset: @50U,
OctagonStateAssistCKKSTLKUploadCKKSReset: @51U,
OctagonStateAssistCKKSTLKUploadAfterCKKSReset: @52U,
+ OctagonStateWaitForCDP: @53U,
+ OctagonStateDetermineCDPState: @54U,
+ OctagonStateWaitForCDPUpdated: @55U,
+ OctagonStateEstablishEnableCDPBit: @56U,
+ OctagonStateInitiatorSetCDPBit: @57U,
+ OctagonStateCDPHealthCheck: @58U,
+ OctagonStateHealthCheckLeaveClique: @59U,
+ OctagonStateRefetchCKKSPolicy: @60U,
};
});
return map;
[sourceStates addObject:OctagonStateUntrusted];
[sourceStates addObject:OctagonStateWaitForHSA2];
[sourceStates addObject:OctagonStateWaitForUnlock];
+ [sourceStates addObject:OctagonStateWaitForCDP];
s = sourceStates;
});
OctagonFlag* const OctagonFlagCKKSRequestsTLKUpload = (OctagonFlag*) @"tlk_upload_needed";
OctagonFlag* const OctagonFlagCuttlefishNotification = (OctagonFlag*) @"recd_push";
OctagonFlag* const OctagonFlagAccountIsAvailable = (OctagonFlag*)@"account_available";
+OctagonFlag* const OctagonFlagCDPEnabled = (OctagonFlag*) @"cdp_enabled";
OctagonFlag* const OctagonFlagAttemptSOSUpgrade = (OctagonFlag*)@"attempt_sos_upgrade";
OctagonFlag* const OctagonFlagFetchAuthKitMachineIDList = (OctagonFlag*)@"attempt_machine_id_list";
OctagonFlag* const OctagonFlagUnlocked = (OctagonFlag*)@"unlocked";
[flags addObject:OctagonFlagCKKSRequestsTLKUpload];
[flags addObject:OctagonFlagCuttlefishNotification];
[flags addObject:OctagonFlagAccountIsAvailable];
+ [flags addObject:OctagonFlagCDPEnabled];
[flags addObject:OctagonFlagAttemptSOSUpgrade];
[flags addObject:OctagonFlagFetchAuthKitMachineIDList];
[flags addObject:OctagonFlagUnlocked];
- (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
intendedState:(OctagonState*)intendedState
+ peerUnknownState:(OctagonState*)peerUnknownState
errorState:(OctagonState*)errorState
retryFlag:(OctagonFlag* _Nullable)retryFlag;
@end
@interface OTUpdateTPHOperation ()
@property OTOperationDependencies* deps;
+@property OctagonState* peerUnknownState;
+
@property NSOperation* finishedOp;
@property (nullable) OctagonFlag* retryFlag;
- (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
intendedState:(OctagonState*)intendedState
+ peerUnknownState:(OctagonState*)peerUnknownState
errorState:(OctagonState*)errorState
retryFlag:(OctagonFlag* _Nullable)retryFlag
{
_intendedState = intendedState;
_nextState = errorState;
+ _peerUnknownState = peerUnknownState;
_retryFlag = retryFlag;
}
self.nextState = OctagonStateBecomeUntrusted;
} else if(peerState.peerStatus & TPPeerStatusUnknown) {
- secnotice("octagon", "Self peer (%@) is unknown; moving to untrusted", peerState.peerID);
- self.nextState = OctagonStateBecomeUntrusted;
+ secnotice("octagon", "Self peer (%@) is unknown; moving to '%@''", peerState.peerID, self.peerUnknownState);
+ self.nextState = self.peerUnknownState;
} else {
self.nextState = self.intendedState;
}];
[self dependOnBeforeGroupFinished:self.finishedOp];
+ NSError* localError = nil;
+ BOOL isAccountDemo = [self.deps.authKitAdapter accountIsDemoAccount:&localError];
+ if(localError) {
+ secerror("octagon-authkit: failed to fetch demo account flag: %@", localError);
+ }
+
[self.deps.authKitAdapter fetchCurrentDeviceList:^(NSSet<NSString *> * _Nullable machineIDs, NSError * _Nullable error) {
STRONGIFY(self);
if(!machineIDs || error) {
if (self.logForUpgrade) {
[[CKKSAnalytics logger] logSuccessForEventNamed:OctagonEventUpgradeFetchDeviceIDs];
}
- [self afterAuthKitFetch:machineIDs];
+ [self afterAuthKitFetch:machineIDs accountIsDemo:isAccountDemo];
}
}];
}
-- (void)afterAuthKitFetch:(NSSet<NSString *>*)allowedMachineIDs
+- (void)afterAuthKitFetch:(NSSet<NSString *>*)allowedMachineIDs accountIsDemo:(BOOL)accountIsDemo
{
WEAKIFY(self);
+ BOOL honorIDMSListChanges = accountIsDemo ? NO : YES;
+
[self.deps.cuttlefishXPCWrapper setAllowedMachineIDsWithContainer:self.deps.containerName
context:self.deps.contextID
allowedMachineIDs:allowedMachineIDs
+ honorIDMSListChanges:honorIDMSListChanges
reply:^(BOOL listDifferences, NSError * _Nullable error) {
STRONGIFY(self);
NSMutableSet<CKKSKeychainView*>* viewsToUpload = [NSMutableSet set];
// One (or more) of our sub-CKKSes believes it needs to upload new TLKs.
- for(CKKSKeychainView* view in [self.deps.viewManager currentViews]) {
+ CKKSViewManager* viewManager = self.deps.viewManager;
+ for(CKKSKeychainView* view in viewManager.currentViews) {
if([view.keyHierarchyState isEqualToString:SecCKKSZoneKeyStateWaitForTLKUpload] ||
[view.keyHierarchyState isEqualToString:SecCKKSZoneKeyStateWaitForTLKCreation]) {
secnotice("octagon-ckks", "CKKS view %@ needs TLK uploads!", view);
}
}
- WEAKIFY(self);
-
- // After a vouch, we also want to acquire all TLKs that the bottled peer might have had
- OTFetchCKKSKeysOperation* fetchKeysOp = [[OTFetchCKKSKeysOperation alloc] initWithDependencies:self.deps];
- [self runBeforeGroupFinished:fetchKeysOp];
-
- CKKSResultOperation* proceedWithKeys = [CKKSResultOperation named:@"bottle-tlks"
- withBlock:^{
- STRONGIFY(self);
- [self proceedWithKeys:fetchKeysOp.viewKeySets tlkShares:fetchKeysOp.tlkShares];
- }];
-
- [proceedWithKeys addDependency:fetchKeysOp];
- [self runBeforeGroupFinished:proceedWithKeys];
-}
-
-- (void)proceedWithKeys:(NSArray<CKKSKeychainBackedKeySet*>*)viewKeySets tlkShares:(NSArray<CKKSTLKShare*>*)tlkShares
-{
// Preflight the vouch: this will tell us the peerID of the recovering peer.
// Then, filter the tlkShares array to include only tlks sent to that peer.
WEAKIFY(self);
[self.deps.cuttlefishXPCWrapper preflightVouchWithBottleWithContainer:self.deps.containerName
context:self.deps.contextID
bottleID:self.bottleID
- reply:^(NSString * _Nullable peerID, NSError * _Nullable error) {
+ reply:^(NSString * _Nullable peerID,
+ NSSet<NSString*>* peerSyncingViews,
+ TPPolicy* peerSyncingPolicy,
+ NSError * _Nullable error) {
STRONGIFY(self);
[[CKKSAnalytics logger] logResultForEvent:OctagonEventPreflightVouchWithBottle hardFailure:true result:error];
- if(error){
+ if(error || !peerID) {
secerror("octagon: Error preflighting voucher using bottle: %@", error);
self.error = error;
[self runBeforeGroupFinished:self.finishedOp];
secnotice("octagon", "Bottle %@ is for peerID %@", self.bottleID, peerID);
+ // Tell CKKS to spin up the new views and policy
+ // But, do not persist this view set! We'll do that when we actually manager to join
+ [self.deps.viewManager setSyncingViews:peerSyncingViews sortingPolicy:peerSyncingPolicy];
+
+ [self proceedWithPeerID:peerID];
+ }];
+}
+
+- (void)proceedWithPeerID:(NSString*)peerID
+{
+ WEAKIFY(self);
+
+ // After a vouch, we also want to acquire all TLKs that the bottled peer might have had
+ OTFetchCKKSKeysOperation* fetchKeysOp = [[OTFetchCKKSKeysOperation alloc] initWithDependencies:self.deps];
+ [self runBeforeGroupFinished:fetchKeysOp];
+
+ CKKSResultOperation* proceedWithKeys = [CKKSResultOperation named:@"bottle-tlks"
+ withBlock:^{
+ STRONGIFY(self);
+
NSMutableArray<CKKSTLKShare*>* filteredTLKShares = [NSMutableArray array];
- for(CKKSTLKShare* share in tlkShares) {
+ for(CKKSTLKShare* share in fetchKeysOp.tlkShares) {
// If we didn't get a peerID, just pass every tlkshare and hope for the best
if(peerID == nil || [share.receiverPeerID isEqualToString:peerID]) {
[filteredTLKShares addObject:share];
}
}
- [self proceedWithKeys:viewKeySets filteredTLKShares:filteredTLKShares];
+ [self proceedWithKeys:fetchKeysOp.viewKeySets filteredTLKShares:filteredTLKShares];
}];
+
+ [proceedWithKeys addDependency:fetchKeysOp];
+ [self runBeforeGroupFinished:proceedWithKeys];
+}
+
+
+- (void)noteMetric:(NSString*)metric count:(int64_t)count
+{
+ NSString* metricName = [NSString stringWithFormat:@"%@%lld", metric, count];
+
+ [[CKKSAnalytics logger] logResultForEvent:metricName
+ hardFailure:NO
+ result:nil];
+
+ [[CKKSAnalytics logger] setDateProperty:[NSDate date] forKey:metricName];
+ [[CKKSAnalytics logger] setNumberProperty:[[NSNumber alloc]initWithLong:count] forKey:metric];
}
- (void)proceedWithKeys:(NSArray<CKKSKeychainBackedKeySet*>*)viewKeySets filteredTLKShares:(NSArray<CKKSTLKShare*>*)tlkShares
entropy:self.entropy
bottleSalt:self.bottleSalt
tlkShares:tlkShares
- reply:^(NSData * _Nullable voucher, NSData * _Nullable voucherSig, NSError * _Nullable error) {
+ reply:^(NSData * _Nullable voucher,
+ NSData * _Nullable voucherSig,
+ int64_t uniqueTLKsRecovered,
+ int64_t totalTLKSharesRecovered,
+ NSError * _Nullable error) {
STRONGIFY(self);
[[CKKSAnalytics logger] logResultForEvent:OctagonEventVoucherWithBottle hardFailure:true result:error];
return;
}
+ //collect TLK count metrics
+ [self noteMetric:OctagonAnalyticsBottledUniqueTLKsRecovered count:uniqueTLKsRecovered];
+ [self noteMetric:OctagonAnalyticsBottledTotalTLKSharesRecovered count:totalTLKSharesRecovered];
+ [self noteMetric:OctagonAnalyticsBottledTotalTLKShares count:tlkShares.count];
+
+ NSMutableSet<NSString*>* uniqueTLKsWithShares = [NSMutableSet set];
+ for (CKKSTLKShare* share in tlkShares) {
+ [uniqueTLKsWithShares addObject:share.tlkUUID];
+ }
+
+ [self noteMetric:OctagonAnalyticsBottledUniqueTLKsWithSharesCount count:uniqueTLKsWithShares.count];
+
+ NSMutableDictionary *views = [NSMutableDictionary dictionary];
+ for (CKKSTLKShare *share in tlkShares) {
+ views[share.zoneID] = share.zoneID;
+ }
+ [self noteMetric:OctagonAnalyticsBottledTLKUniqueViewCount count:views.count];
+
secnotice("octagon", "Received bottle voucher");
self.voucher = voucher;
recoveryKey:(NSString*)recoveryKey;
@property (weak) OTCuttlefishContext* cuttlefishContext;
-@property (nonatomic) NSString* salt;
-@property (nonatomic) NSString* recoveryKey;
@property (nonatomic) NSData* voucher;
@property (nonatomic) NSData* voucherSig;
@interface OTVouchWithRecoveryKeyOperation ()
@property OTOperationDependencies* deps;
+@property NSString* salt;
+@property NSString* recoveryKey;
+
@property NSOperation* finishOp;
@end
self.finishOp = [[NSOperation alloc] init];
[self dependOnBeforeGroupFinished:self.finishOp];
- NSString* salt = nil;
+ NSString *altDSID = [self.deps.authKitAdapter primaryiCloudAccountAltDSID:nil];
+ if(altDSID){
+ secnotice("octagon", "using auth kit adapter, altdsid is: %@", altDSID);
+ self.salt = altDSID;
+ }
+ else {
+ NSError* accountError = nil;
+ OTAccountMetadataClassC* account = [self.deps.stateHolder loadOrCreateAccountMetadata:&accountError];
- if(self.salt != nil) {
- secnotice("octagon", "using passed in altdsid, altdsid is: %@", self.salt);
- salt = self.salt;
- } else{
- NSString *altDSID = [self.deps.authKitAdapter primaryiCloudAccountAltDSID:nil];
- if(altDSID){
- secnotice("octagon", "using auth kit adapter, altdsid is: %@", altDSID);
- salt = altDSID;
+ if(account && !accountError) {
+ secnotice("octagon", "retrieved account, altdsid is: %@", account.altDSID);
+ self.salt = account.altDSID;
}
- else {
- NSError* accountError = nil;
- OTAccountMetadataClassC* account = [self.deps.stateHolder loadOrCreateAccountMetadata:&accountError];
-
- if(account && !accountError) {
- secnotice("octagon", "retrieved account, altdsid is: %@", account.altDSID);
- salt = account.altDSID;
- }
- if(accountError || !account){
- secerror("failed to rerieve account object: %@", accountError);
- }
+ if(accountError || !account){
+ secerror("failed to rerieve account object: %@", accountError);
}
}
+ // First, let's preflight the vouch (to receive a policy and view set to use for TLK fetching
+ WEAKIFY(self);
+ [self.deps.cuttlefishXPCWrapper preflightVouchWithRecoveryKeyWithContainer:self.deps.containerName
+ context:self.deps.contextID
+ recoveryKey:self.recoveryKey
+ salt:self.salt
+ reply:^(NSString * _Nullable recoveryKeyID,
+ NSSet<NSString*>* peerSyncingViews,
+ TPPolicy* peerSyncingPolicy,
+ NSError * _Nullable error) {
+ STRONGIFY(self);
+ [[CKKSAnalytics logger] logResultForEvent:OctagonEventPreflightVouchWithRecoveryKey hardFailure:true result:error];
+
+ if(error || !recoveryKeyID) {
+ secerror("octagon: Error preflighting voucher using recovery key: %@", error);
+ self.error = error;
+ [self runBeforeGroupFinished:self.finishOp];
+ return;
+ }
+
+ secnotice("octagon", "Recovery key ID %@ looks good to go", recoveryKeyID);
+
+ // Tell CKKS to spin up the new views and policy
+ // But, do not persist this view set! We'll do that when we actually manage to join
+ [self.deps.viewManager setSyncingViews:peerSyncingViews sortingPolicy:peerSyncingPolicy];
+
+ [self proceedWithRecoveryKeyID:recoveryKeyID];
+ }];
+}
+
+- (void)proceedWithRecoveryKeyID:(NSString*)recoveryKeyID
+{
WEAKIFY(self);
// After a vouch, we also want to acquire all TLKs that the bottled peer might have had
CKKSResultOperation* proceedWithKeys = [CKKSResultOperation named:@"recovery-tlks"
withBlock:^{
- STRONGIFY(self);
- [self proceedWithKeys:fetchKeysOp.viewKeySets tlkShares:fetchKeysOp.tlkShares salt:salt];
- }];
+ STRONGIFY(self);
+
+ NSMutableArray<CKKSTLKShare*>* filteredTLKShares = [NSMutableArray array];
+ for(CKKSTLKShare* share in fetchKeysOp.tlkShares) {
+ // If we didn't get a recoveryKeyID, just pass every tlkshare and hope for the best
+ if(recoveryKeyID == nil || [share.receiverPeerID isEqualToString:recoveryKeyID]) {
+ [filteredTLKShares addObject:share];
+ }
+ }
+
+ [self proceedWithKeys:fetchKeysOp.viewKeySets tlkShares:filteredTLKShares salt:self.salt];
+ }];
[proceedWithKeys addDependency:fetchKeysOp];
[self runBeforeGroupFinished:proceedWithKeys];
// Overwrite any existing pending flag!
self.pendingFlags[pendingFlag.flag] = pendingFlag;
- self.currentFlags.flagConditions[pendingFlag.flag] = [[CKKSCondition alloc]init];
-
// Do we need to recheck any conditions? Anything which is currently the state of the world needs checking
OctagonPendingConditions recheck = pendingFlag.conditions & self.currentConditions;
if(recheck != 0x0) {
+ (OTAccountMetadataClassC* _Nullable)loadFromKeychainForContainer:(NSString*)containerName contextID:(NSString*)contextID error:(NSError**)error;
@end
+@class TPPolicy;
+@interface OTAccountMetadataClassC (NSSecureCodingSupport)
+- (void)setTPPolicy:(TPPolicy* _Nullable)policy;
+- (TPPolicy* _Nullable)getTPPolicy;
+@end
+
NS_ASSUME_NONNULL_END
#endif // OCTAGON
#import "keychain/ot/OTDefines.h"
#import "keychain/ot/OTConstants.h"
+#import <TrustedPeers/TPPolicy.h>
@implementation OTAccountMetadataClassC (KeychainSupport)
return state;
}
+#pragma mark - Field Coding support
+
+- (void)setTPPolicy:(TPPolicy*)policy
+{
+ if(policy) {
+ NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:YES];
+ [policy encodeWithCoder:archiver];
+ self.syncingPolicy = archiver.encodedData;
+ } else {
+ self.syncingPolicy = nil;
+ }
+}
+
+- (TPPolicy* _Nullable)getTPPolicy
+{
+ NSKeyedUnarchiver *coder = [[NSKeyedUnarchiver alloc] initForReadingFromData:self.syncingPolicy error:nil];
+ TPPolicy* policy = [[TPPolicy alloc] initWithCoder:coder];
+ [coder finishDecoding];
+
+ return policy;
+}
+
@end
#endif // OCTAGON
@protocol OctagonEscrowRecovererPrococol <NSObject>
- (NSError*)recoverWithInfo:(NSDictionary*)info results:(NSDictionary**)results;
+- (NSError *)disableWithInfo:(NSDictionary *)info;
@end
@interface SecureBackup (OctagonProtocolConformance) <OctagonEscrowRecovererPrococol>
// If there is an SA account, we will bt in NO_ACCOUNT and have an altDSID
// If there is an HSA2 account, we will be in ACCOUNT_AVAILABLE and have an altDSID
+ // Once you're in an HSA2 account, CDP might be enabled or disabled. If it's not enabled,
+ // then Octagon shouldn't be active.
+
enum AccountState {
UNKNOWN = 0;
NO_ACCOUNT = 1;
ATTEMPTED = 2;
}
+ enum CDPState {
+ UNKNOWN = 0;
+ DISABLED = 1;
+ ENABLED = 2;
+ }
+
optional string peerID = 1;
optional AccountState icloudAccountState = 2;
optional int64 epoch = 3;
optional uint64 lastHealthCheckup = 6;
optional AttemptedAJoinState attemptedJoin = 7;
+
+ optional CDPState cdpState = 8;
+
+ // These store the current policy and view list, so that we don't need to re-ask TPH every time
+ optional bytes syncingPolicy = 9;
+ repeated string syncingView = 10;
}
package OT;
-message SOSMessage {
- optional bytes credential = 1;
- optional bytes peerInfo = 2;
- optional bytes circleBlob = 3;
- optional bytes initialSyncItems = 4;
-}
-message SponsorToApplicantRound1M2{
+message SponsorToApplicantRound1M2 {
optional uint64 epoch = 1;
}
message SponsorToApplicantRound2M2{
optional bytes voucher = 1;
optional bytes voucherSignature = 2;
- repeated bytes preapprovedKeys = 3;
+ // Claimed for a field, but never used
+ //reserved 3;
}
message PairingMessage {
- optional SponsorToApplicantRound1M2 epoch= 1;
+ optional SponsorToApplicantRound1M2 epoch = 1;
optional ApplicantToSponsorRound2M1 prepare = 2;
optional SponsorToApplicantRound2M2 voucher = 3;
- optional SOSMessage sosPairingMessage = 4;
+ // <rdar://problem/55465193> reserved is not a keyword (it should be)
+ // reserved 4;
}
* If there is no account, we will be in NO_ACCOUNT and have no altDSID
* If there is an SA account, we will bt in NO_ACCOUNT and have an altDSID
* If there is an HSA2 account, we will be in ACCOUNT_AVAILABLE and have an altDSID
+ * Once you're in an HSA2 account, CDP might be enabled or disabled. If it's not enabled,
+ * then Octagon shouldn't be active.
*/
typedef NS_ENUM(int32_t, OTAccountMetadataClassC_AccountState) {
OTAccountMetadataClassC_AccountState_UNKNOWN = 0,
return OTAccountMetadataClassC_AttemptedAJoinState_UNKNOWN;
}
#endif /* __OBJC__ */
+typedef NS_ENUM(int32_t, OTAccountMetadataClassC_CDPState) {
+ OTAccountMetadataClassC_CDPState_UNKNOWN = 0,
+ OTAccountMetadataClassC_CDPState_DISABLED = 1,
+ OTAccountMetadataClassC_CDPState_ENABLED = 2,
+};
+#ifdef __OBJC__
+NS_INLINE NSString *OTAccountMetadataClassC_CDPStateAsString(OTAccountMetadataClassC_CDPState value)
+{
+ switch (value)
+ {
+ case OTAccountMetadataClassC_CDPState_UNKNOWN: return @"UNKNOWN";
+ case OTAccountMetadataClassC_CDPState_DISABLED: return @"DISABLED";
+ case OTAccountMetadataClassC_CDPState_ENABLED: return @"ENABLED";
+ default: return [NSString stringWithFormat:@"(unknown: %i)", value];
+ }
+}
+#endif /* __OBJC__ */
+#ifdef __OBJC__
+NS_INLINE OTAccountMetadataClassC_CDPState StringAsOTAccountMetadataClassC_CDPState(NSString *value)
+{
+ if ([value isEqualToString:@"UNKNOWN"]) return OTAccountMetadataClassC_CDPState_UNKNOWN;
+ if ([value isEqualToString:@"DISABLED"]) return OTAccountMetadataClassC_CDPState_DISABLED;
+ if ([value isEqualToString:@"ENABLED"]) return OTAccountMetadataClassC_CDPState_ENABLED;
+ return OTAccountMetadataClassC_CDPState_UNKNOWN;
+}
+#endif /* __OBJC__ */
#ifdef __cplusplus
#define OTACCOUNTMETADATACLASSC_FUNCTION extern "C" __attribute__((visibility("hidden")))
uint64_t _lastHealthCheckup;
NSString *_altDSID;
OTAccountMetadataClassC_AttemptedAJoinState _attemptedJoin;
+ OTAccountMetadataClassC_CDPState _cdpState;
OTAccountMetadataClassC_AccountState _icloudAccountState;
NSString *_peerID;
+ NSData *_syncingPolicy;
+ NSMutableArray<NSString *> *_syncingViews;
OTAccountMetadataClassC_TrustState _trustState;
struct {
int epoch:1;
int lastHealthCheckup:1;
int attemptedJoin:1;
+ int cdpState:1;
int icloudAccountState:1;
int trustState:1;
} _has;
- (NSString *)attemptedJoinAsString:(OTAccountMetadataClassC_AttemptedAJoinState)value;
- (OTAccountMetadataClassC_AttemptedAJoinState)StringAsAttemptedJoin:(NSString *)str;
+@property (nonatomic) BOOL hasCdpState;
+@property (nonatomic) OTAccountMetadataClassC_CDPState cdpState;
+- (NSString *)cdpStateAsString:(OTAccountMetadataClassC_CDPState)value;
+- (OTAccountMetadataClassC_CDPState)StringAsCdpState:(NSString *)str;
+
+@property (nonatomic, readonly) BOOL hasSyncingPolicy;
+/** These store the current policy and view list, so that we don't need to re-ask TPH every time */
+@property (nonatomic, retain) NSData *syncingPolicy;
+
+@property (nonatomic, retain) NSMutableArray<NSString *> *syncingViews;
+- (void)clearSyncingViews;
+- (void)addSyncingView:(NSString *)i;
+- (NSUInteger)syncingViewsCount;
+- (NSString *)syncingViewAtIndex:(NSUInteger)idx;
++ (Class)syncingViewType;
+
// Performs a shallow copy into other
- (void)copyTo:(OTAccountMetadataClassC *)other;
}
- (BOOL)hasIcloudAccountState
{
- return _has.icloudAccountState;
+ return _has.icloudAccountState != 0;
}
- (NSString *)icloudAccountStateAsString:(OTAccountMetadataClassC_AccountState)value
{
}
- (BOOL)hasEpoch
{
- return _has.epoch;
+ return _has.epoch != 0;
}
- (BOOL)hasAltDSID
{
}
- (BOOL)hasTrustState
{
- return _has.trustState;
+ return _has.trustState != 0;
}
- (NSString *)trustStateAsString:(OTAccountMetadataClassC_TrustState)value
{
}
- (BOOL)hasLastHealthCheckup
{
- return _has.lastHealthCheckup;
+ return _has.lastHealthCheckup != 0;
}
@synthesize attemptedJoin = _attemptedJoin;
- (OTAccountMetadataClassC_AttemptedAJoinState)attemptedJoin
}
- (BOOL)hasAttemptedJoin
{
- return _has.attemptedJoin;
+ return _has.attemptedJoin != 0;
}
- (NSString *)attemptedJoinAsString:(OTAccountMetadataClassC_AttemptedAJoinState)value
{
{
return StringAsOTAccountMetadataClassC_AttemptedAJoinState(str);
}
+@synthesize cdpState = _cdpState;
+- (OTAccountMetadataClassC_CDPState)cdpState
+{
+ return _has.cdpState ? _cdpState : OTAccountMetadataClassC_CDPState_UNKNOWN;
+}
+- (void)setCdpState:(OTAccountMetadataClassC_CDPState)v
+{
+ _has.cdpState = YES;
+ _cdpState = v;
+}
+- (void)setHasCdpState:(BOOL)f
+{
+ _has.cdpState = f;
+}
+- (BOOL)hasCdpState
+{
+ return _has.cdpState != 0;
+}
+- (NSString *)cdpStateAsString:(OTAccountMetadataClassC_CDPState)value
+{
+ return OTAccountMetadataClassC_CDPStateAsString(value);
+}
+- (OTAccountMetadataClassC_CDPState)StringAsCdpState:(NSString *)str
+{
+ return StringAsOTAccountMetadataClassC_CDPState(str);
+}
+- (BOOL)hasSyncingPolicy
+{
+ return _syncingPolicy != nil;
+}
+@synthesize syncingPolicy = _syncingPolicy;
+@synthesize syncingViews = _syncingViews;
+- (void)clearSyncingViews
+{
+ [_syncingViews removeAllObjects];
+}
+- (void)addSyncingView:(NSString *)i
+{
+ if (!_syncingViews)
+ {
+ _syncingViews = [[NSMutableArray alloc] init];
+ }
+ [_syncingViews addObject:i];
+}
+- (NSUInteger)syncingViewsCount
+{
+ return [_syncingViews count];
+}
+- (NSString *)syncingViewAtIndex:(NSUInteger)idx
+{
+ return [_syncingViews objectAtIndex:idx];
+}
++ (Class)syncingViewType
+{
+ return [NSString class];
+}
- (NSString *)description
{
{
[dict setObject:OTAccountMetadataClassC_AttemptedAJoinStateAsString(self->_attemptedJoin) forKey:@"attemptedJoin"];
}
+ if (self->_has.cdpState)
+ {
+ [dict setObject:OTAccountMetadataClassC_CDPStateAsString(self->_cdpState) forKey:@"cdpState"];
+ }
+ if (self->_syncingPolicy)
+ {
+ [dict setObject:self->_syncingPolicy forKey:@"syncingPolicy"];
+ }
+ if (self->_syncingViews)
+ {
+ [dict setObject:self->_syncingViews forKey:@"syncingView"];
+ }
return dict;
}
self->_attemptedJoin = PBReaderReadInt32(reader);
}
break;
+ case 8 /* cdpState */:
+ {
+ self->_has.cdpState = YES;
+ self->_cdpState = PBReaderReadInt32(reader);
+ }
+ break;
+ case 9 /* syncingPolicy */:
+ {
+ NSData *new_syncingPolicy = PBReaderReadData(reader);
+ self->_syncingPolicy = new_syncingPolicy;
+ }
+ break;
+ case 10 /* syncingViews */:
+ {
+ NSString *new_syncingViews = PBReaderReadString(reader);
+ if (new_syncingViews)
+ {
+ [self addSyncingView:new_syncingViews];
+ }
+ }
+ break;
default:
if (!PBReaderSkipValueWithTag(reader, tag, aType))
return NO;
PBDataWriterWriteInt32Field(writer, self->_attemptedJoin, 7);
}
}
+ /* cdpState */
+ {
+ if (self->_has.cdpState)
+ {
+ PBDataWriterWriteInt32Field(writer, self->_cdpState, 8);
+ }
+ }
+ /* syncingPolicy */
+ {
+ if (self->_syncingPolicy)
+ {
+ PBDataWriterWriteDataField(writer, self->_syncingPolicy, 9);
+ }
+ }
+ /* syncingViews */
+ {
+ for (NSString *s_syncingViews in self->_syncingViews)
+ {
+ PBDataWriterWriteStringField(writer, s_syncingViews, 10);
+ }
+ }
}
- (void)copyTo:(OTAccountMetadataClassC *)other
other->_attemptedJoin = _attemptedJoin;
other->_has.attemptedJoin = YES;
}
+ if (self->_has.cdpState)
+ {
+ other->_cdpState = _cdpState;
+ other->_has.cdpState = YES;
+ }
+ if (_syncingPolicy)
+ {
+ other.syncingPolicy = _syncingPolicy;
+ }
+ if ([self syncingViewsCount])
+ {
+ [other clearSyncingViews];
+ NSUInteger syncingViewsCnt = [self syncingViewsCount];
+ for (NSUInteger i = 0; i < syncingViewsCnt; i++)
+ {
+ [other addSyncingView:[self syncingViewAtIndex:i]];
+ }
+ }
}
- (id)copyWithZone:(NSZone *)zone
copy->_attemptedJoin = _attemptedJoin;
copy->_has.attemptedJoin = YES;
}
+ if (self->_has.cdpState)
+ {
+ copy->_cdpState = _cdpState;
+ copy->_has.cdpState = YES;
+ }
+ copy->_syncingPolicy = [_syncingPolicy copyWithZone:zone];
+ for (NSString *v in _syncingViews)
+ {
+ NSString *vCopy = [v copyWithZone:zone];
+ [copy addSyncingView:vCopy];
+ }
return copy;
}
((self->_has.lastHealthCheckup && other->_has.lastHealthCheckup && self->_lastHealthCheckup == other->_lastHealthCheckup) || (!self->_has.lastHealthCheckup && !other->_has.lastHealthCheckup))
&&
((self->_has.attemptedJoin && other->_has.attemptedJoin && self->_attemptedJoin == other->_attemptedJoin) || (!self->_has.attemptedJoin && !other->_has.attemptedJoin))
+ &&
+ ((self->_has.cdpState && other->_has.cdpState && self->_cdpState == other->_cdpState) || (!self->_has.cdpState && !other->_has.cdpState))
+ &&
+ ((!self->_syncingPolicy && !other->_syncingPolicy) || [self->_syncingPolicy isEqual:other->_syncingPolicy])
+ &&
+ ((!self->_syncingViews && !other->_syncingViews) || [self->_syncingViews isEqual:other->_syncingViews])
;
}
(self->_has.lastHealthCheckup ? PBHashInt((NSUInteger)self->_lastHealthCheckup) : 0)
^
(self->_has.attemptedJoin ? PBHashInt((NSUInteger)self->_attemptedJoin) : 0)
+ ^
+ (self->_has.cdpState ? PBHashInt((NSUInteger)self->_cdpState) : 0)
+ ^
+ [self->_syncingPolicy hash]
+ ^
+ [self->_syncingViews hash]
;
}
self->_attemptedJoin = other->_attemptedJoin;
self->_has.attemptedJoin = YES;
}
+ if (other->_has.cdpState)
+ {
+ self->_cdpState = other->_cdpState;
+ self->_has.cdpState = YES;
+ }
+ if (other->_syncingPolicy)
+ {
+ [self setSyncingPolicy:other->_syncingPolicy];
+ }
+ for (NSString *iter_syncingViews in other->_syncingViews)
+ {
+ [self addSyncingView:iter_syncingViews];
+ }
}
@end
@class OTSponsorToApplicantRound1M2;
@class OTApplicantToSponsorRound2M1;
@class OTSponsorToApplicantRound2M2;
-@class OTSOSMessage;
#ifdef __cplusplus
#define OTPAIRINGMESSAGE_FUNCTION extern "C" __attribute__((visibility("hidden")))
#define OTPAIRINGMESSAGE_FUNCTION extern __attribute__((visibility("hidden")))
#endif
+/**
+ * Claimed for a field, but never used
+ * reserved 3;
+ */
__attribute__((visibility("hidden")))
@interface OTPairingMessage : PBCodable <NSCopying>
{
OTSponsorToApplicantRound1M2 *_epoch;
OTApplicantToSponsorRound2M1 *_prepare;
- OTSOSMessage *_sosPairingMessage;
OTSponsorToApplicantRound2M2 *_voucher;
}
@property (nonatomic, readonly) BOOL hasVoucher;
@property (nonatomic, retain) OTSponsorToApplicantRound2M2 *voucher;
-@property (nonatomic, readonly) BOOL hasSosPairingMessage;
-@property (nonatomic, retain) OTSOSMessage *sosPairingMessage;
-
// Performs a shallow copy into other
- (void)copyTo:(OTPairingMessage *)other;
#import <ProtocolBuffer/PBDataReader.h>
#import "OTApplicantToSponsorRound2M1.h"
-#import "OTSOSMessage.h"
#import "OTSponsorToApplicantRound1M2.h"
#import "OTSponsorToApplicantRound2M2.h"
return _voucher != nil;
}
@synthesize voucher = _voucher;
-- (BOOL)hasSosPairingMessage
-{
- return _sosPairingMessage != nil;
-}
-@synthesize sosPairingMessage = _sosPairingMessage;
- (NSString *)description
{
{
[dict setObject:[_voucher dictionaryRepresentation] forKey:@"voucher"];
}
- if (self->_sosPairingMessage)
- {
- [dict setObject:[_sosPairingMessage dictionaryRepresentation] forKey:@"sosPairingMessage"];
- }
return dict;
}
PBReaderRecallMark(reader, &mark_voucher);
}
break;
- case 4 /* sosPairingMessage */:
- {
- OTSOSMessage *new_sosPairingMessage = [[OTSOSMessage alloc] init];
- self->_sosPairingMessage = new_sosPairingMessage;
- PBDataReaderMark mark_sosPairingMessage;
- BOOL markError = !PBReaderPlaceMark(reader, &mark_sosPairingMessage);
- if (markError)
- {
- return NO;
- }
- BOOL inError = !OTSOSMessageReadFrom(new_sosPairingMessage, reader);
- if (inError)
- {
- return NO;
- }
- PBReaderRecallMark(reader, &mark_sosPairingMessage);
- }
- break;
default:
if (!PBReaderSkipValueWithTag(reader, tag, aType))
return NO;
PBDataWriterWriteSubmessage(writer, self->_voucher, 3);
}
}
- /* sosPairingMessage */
- {
- if (self->_sosPairingMessage != nil)
- {
- PBDataWriterWriteSubmessage(writer, self->_sosPairingMessage, 4);
- }
- }
}
- (void)copyTo:(OTPairingMessage *)other
{
other.voucher = _voucher;
}
- if (_sosPairingMessage)
- {
- other.sosPairingMessage = _sosPairingMessage;
- }
}
- (id)copyWithZone:(NSZone *)zone
copy->_epoch = [_epoch copyWithZone:zone];
copy->_prepare = [_prepare copyWithZone:zone];
copy->_voucher = [_voucher copyWithZone:zone];
- copy->_sosPairingMessage = [_sosPairingMessage copyWithZone:zone];
return copy;
}
((!self->_prepare && !other->_prepare) || [self->_prepare isEqual:other->_prepare])
&&
((!self->_voucher && !other->_voucher) || [self->_voucher isEqual:other->_voucher])
- &&
- ((!self->_sosPairingMessage && !other->_sosPairingMessage) || [self->_sosPairingMessage isEqual:other->_sosPairingMessage])
;
}
[self->_prepare hash]
^
[self->_voucher hash]
- ^
- [self->_sosPairingMessage hash]
;
}
{
[self setVoucher:other->_voucher];
}
- if (self->_sosPairingMessage && other->_sosPairingMessage)
- {
- [self->_sosPairingMessage mergeFrom:other->_sosPairingMessage];
- }
- else if (!self->_sosPairingMessage && other->_sosPairingMessage)
- {
- [self setSosPairingMessage:other->_sosPairingMessage];
- }
}
@end
+++ /dev/null
-// This file was automatically generated by protocompiler
-// DO NOT EDIT!
-// Compiled from OTPairingMessage.proto
-
-#import <Foundation/Foundation.h>
-#import <ProtocolBuffer/PBCodable.h>
-
-#ifdef __cplusplus
-#define OTSOSMESSAGE_FUNCTION extern "C" __attribute__((visibility("hidden")))
-#else
-#define OTSOSMESSAGE_FUNCTION extern __attribute__((visibility("hidden")))
-#endif
-
-__attribute__((visibility("hidden")))
-@interface OTSOSMessage : PBCodable <NSCopying>
-{
- NSData *_circleBlob;
- NSData *_credential;
- NSData *_initialSyncItems;
- NSData *_peerInfo;
-}
-
-
-@property (nonatomic, readonly) BOOL hasCredential;
-@property (nonatomic, retain) NSData *credential;
-
-@property (nonatomic, readonly) BOOL hasPeerInfo;
-@property (nonatomic, retain) NSData *peerInfo;
-
-@property (nonatomic, readonly) BOOL hasCircleBlob;
-@property (nonatomic, retain) NSData *circleBlob;
-
-@property (nonatomic, readonly) BOOL hasInitialSyncItems;
-@property (nonatomic, retain) NSData *initialSyncItems;
-
-// Performs a shallow copy into other
-- (void)copyTo:(OTSOSMessage *)other;
-
-// Performs a deep merge from other into self
-// If set in other, singular values in self are replaced in self
-// Singular composite values are recursively merged
-// Repeated values from other are appended to repeated values in self
-- (void)mergeFrom:(OTSOSMessage *)other;
-
-OTSOSMESSAGE_FUNCTION BOOL OTSOSMessageReadFrom(__unsafe_unretained OTSOSMessage *self, __unsafe_unretained PBDataReader *reader);
-
-@end
-
+++ /dev/null
-// This file was automatically generated by protocompiler
-// DO NOT EDIT!
-// Compiled from OTPairingMessage.proto
-
-#import "OTSOSMessage.h"
-#import <ProtocolBuffer/PBConstants.h>
-#import <ProtocolBuffer/PBHashUtil.h>
-#import <ProtocolBuffer/PBDataReader.h>
-
-#if !__has_feature(objc_arc)
-# error This generated file depends on ARC but it is not enabled; turn on ARC, or use 'objc_use_arc' option to generate non-ARC code.
-#endif
-
-@implementation OTSOSMessage
-
-- (BOOL)hasCredential
-{
- return _credential != nil;
-}
-@synthesize credential = _credential;
-- (BOOL)hasPeerInfo
-{
- return _peerInfo != nil;
-}
-@synthesize peerInfo = _peerInfo;
-- (BOOL)hasCircleBlob
-{
- return _circleBlob != nil;
-}
-@synthesize circleBlob = _circleBlob;
-- (BOOL)hasInitialSyncItems
-{
- return _initialSyncItems != nil;
-}
-@synthesize initialSyncItems = _initialSyncItems;
-
-- (NSString *)description
-{
- return [NSString stringWithFormat:@"%@ %@", [super description], [self dictionaryRepresentation]];
-}
-
-- (NSDictionary *)dictionaryRepresentation
-{
- NSMutableDictionary *dict = [NSMutableDictionary dictionary];
- if (self->_credential)
- {
- [dict setObject:self->_credential forKey:@"credential"];
- }
- if (self->_peerInfo)
- {
- [dict setObject:self->_peerInfo forKey:@"peerInfo"];
- }
- if (self->_circleBlob)
- {
- [dict setObject:self->_circleBlob forKey:@"circleBlob"];
- }
- if (self->_initialSyncItems)
- {
- [dict setObject:self->_initialSyncItems forKey:@"initialSyncItems"];
- }
- return dict;
-}
-
-BOOL OTSOSMessageReadFrom(__unsafe_unretained OTSOSMessage *self, __unsafe_unretained PBDataReader *reader) {
- while (PBReaderHasMoreData(reader)) {
- uint32_t tag = 0;
- uint8_t aType = 0;
-
- PBReaderReadTag32AndType(reader, &tag, &aType);
-
- if (PBReaderHasError(reader))
- break;
-
- if (aType == TYPE_END_GROUP) {
- break;
- }
-
- switch (tag) {
-
- case 1 /* credential */:
- {
- NSData *new_credential = PBReaderReadData(reader);
- self->_credential = new_credential;
- }
- break;
- case 2 /* peerInfo */:
- {
- NSData *new_peerInfo = PBReaderReadData(reader);
- self->_peerInfo = new_peerInfo;
- }
- break;
- case 3 /* circleBlob */:
- {
- NSData *new_circleBlob = PBReaderReadData(reader);
- self->_circleBlob = new_circleBlob;
- }
- break;
- case 4 /* initialSyncItems */:
- {
- NSData *new_initialSyncItems = PBReaderReadData(reader);
- self->_initialSyncItems = new_initialSyncItems;
- }
- break;
- default:
- if (!PBReaderSkipValueWithTag(reader, tag, aType))
- return NO;
- break;
- }
- }
- return !PBReaderHasError(reader);
-}
-
-- (BOOL)readFrom:(PBDataReader *)reader
-{
- return OTSOSMessageReadFrom(self, reader);
-}
-- (void)writeTo:(PBDataWriter *)writer
-{
- /* credential */
- {
- if (self->_credential)
- {
- PBDataWriterWriteDataField(writer, self->_credential, 1);
- }
- }
- /* peerInfo */
- {
- if (self->_peerInfo)
- {
- PBDataWriterWriteDataField(writer, self->_peerInfo, 2);
- }
- }
- /* circleBlob */
- {
- if (self->_circleBlob)
- {
- PBDataWriterWriteDataField(writer, self->_circleBlob, 3);
- }
- }
- /* initialSyncItems */
- {
- if (self->_initialSyncItems)
- {
- PBDataWriterWriteDataField(writer, self->_initialSyncItems, 4);
- }
- }
-}
-
-- (void)copyTo:(OTSOSMessage *)other
-{
- if (_credential)
- {
- other.credential = _credential;
- }
- if (_peerInfo)
- {
- other.peerInfo = _peerInfo;
- }
- if (_circleBlob)
- {
- other.circleBlob = _circleBlob;
- }
- if (_initialSyncItems)
- {
- other.initialSyncItems = _initialSyncItems;
- }
-}
-
-- (id)copyWithZone:(NSZone *)zone
-{
- OTSOSMessage *copy = [[[self class] allocWithZone:zone] init];
- copy->_credential = [_credential copyWithZone:zone];
- copy->_peerInfo = [_peerInfo copyWithZone:zone];
- copy->_circleBlob = [_circleBlob copyWithZone:zone];
- copy->_initialSyncItems = [_initialSyncItems copyWithZone:zone];
- return copy;
-}
-
-- (BOOL)isEqual:(id)object
-{
- OTSOSMessage *other = (OTSOSMessage *)object;
- return [other isMemberOfClass:[self class]]
- &&
- ((!self->_credential && !other->_credential) || [self->_credential isEqual:other->_credential])
- &&
- ((!self->_peerInfo && !other->_peerInfo) || [self->_peerInfo isEqual:other->_peerInfo])
- &&
- ((!self->_circleBlob && !other->_circleBlob) || [self->_circleBlob isEqual:other->_circleBlob])
- &&
- ((!self->_initialSyncItems && !other->_initialSyncItems) || [self->_initialSyncItems isEqual:other->_initialSyncItems])
- ;
-}
-
-- (NSUInteger)hash
-{
- return 0
- ^
- [self->_credential hash]
- ^
- [self->_peerInfo hash]
- ^
- [self->_circleBlob hash]
- ^
- [self->_initialSyncItems hash]
- ;
-}
-
-- (void)mergeFrom:(OTSOSMessage *)other
-{
- if (other->_credential)
- {
- [self setCredential:other->_credential];
- }
- if (other->_peerInfo)
- {
- [self setPeerInfo:other->_peerInfo];
- }
- if (other->_circleBlob)
- {
- [self setCircleBlob:other->_circleBlob];
- }
- if (other->_initialSyncItems)
- {
- [self setInitialSyncItems:other->_initialSyncItems];
- }
-}
-
-@end
-
__attribute__((visibility("hidden")))
@interface OTSponsorToApplicantRound2M2 : PBCodable <NSCopying>
{
- NSMutableArray<NSData *> *_preapprovedKeys;
NSData *_voucher;
NSData *_voucherSignature;
}
@property (nonatomic, readonly) BOOL hasVoucherSignature;
@property (nonatomic, retain) NSData *voucherSignature;
-@property (nonatomic, retain) NSMutableArray<NSData *> *preapprovedKeys;
-- (void)clearPreapprovedKeys;
-- (void)addPreapprovedKeys:(NSData *)i;
-- (NSUInteger)preapprovedKeysCount;
-- (NSData *)preapprovedKeysAtIndex:(NSUInteger)idx;
-+ (Class)preapprovedKeysType;
-
// Performs a shallow copy into other
- (void)copyTo:(OTSponsorToApplicantRound2M2 *)other;
return _voucherSignature != nil;
}
@synthesize voucherSignature = _voucherSignature;
-@synthesize preapprovedKeys = _preapprovedKeys;
-- (void)clearPreapprovedKeys
-{
- [_preapprovedKeys removeAllObjects];
-}
-- (void)addPreapprovedKeys:(NSData *)i
-{
- if (!_preapprovedKeys)
- {
- _preapprovedKeys = [[NSMutableArray alloc] init];
- }
- [_preapprovedKeys addObject:i];
-}
-- (NSUInteger)preapprovedKeysCount
-{
- return [_preapprovedKeys count];
-}
-- (NSData *)preapprovedKeysAtIndex:(NSUInteger)idx
-{
- return [_preapprovedKeys objectAtIndex:idx];
-}
-+ (Class)preapprovedKeysType
-{
- return [NSData class];
-}
- (NSString *)description
{
{
[dict setObject:self->_voucherSignature forKey:@"voucherSignature"];
}
- if (self->_preapprovedKeys)
- {
- [dict setObject:self->_preapprovedKeys forKey:@"preapprovedKeys"];
- }
return dict;
}
self->_voucherSignature = new_voucherSignature;
}
break;
- case 3 /* preapprovedKeys */:
- {
- NSData *new_preapprovedKeys = PBReaderReadData(reader);
- if (new_preapprovedKeys)
- {
- [self addPreapprovedKeys:new_preapprovedKeys];
- }
- }
- break;
default:
if (!PBReaderSkipValueWithTag(reader, tag, aType))
return NO;
PBDataWriterWriteDataField(writer, self->_voucherSignature, 2);
}
}
- /* preapprovedKeys */
- {
- for (NSData *s_preapprovedKeys in self->_preapprovedKeys)
- {
- PBDataWriterWriteDataField(writer, s_preapprovedKeys, 3);
- }
- }
}
- (void)copyTo:(OTSponsorToApplicantRound2M2 *)other
{
other.voucherSignature = _voucherSignature;
}
- if ([self preapprovedKeysCount])
- {
- [other clearPreapprovedKeys];
- NSUInteger preapprovedKeysCnt = [self preapprovedKeysCount];
- for (NSUInteger i = 0; i < preapprovedKeysCnt; i++)
- {
- [other addPreapprovedKeys:[self preapprovedKeysAtIndex:i]];
- }
- }
}
- (id)copyWithZone:(NSZone *)zone
OTSponsorToApplicantRound2M2 *copy = [[[self class] allocWithZone:zone] init];
copy->_voucher = [_voucher copyWithZone:zone];
copy->_voucherSignature = [_voucherSignature copyWithZone:zone];
- for (NSData *v in _preapprovedKeys)
- {
- NSData *vCopy = [v copyWithZone:zone];
- [copy addPreapprovedKeys:vCopy];
- }
return copy;
}
((!self->_voucher && !other->_voucher) || [self->_voucher isEqual:other->_voucher])
&&
((!self->_voucherSignature && !other->_voucherSignature) || [self->_voucherSignature isEqual:other->_voucherSignature])
- &&
- ((!self->_preapprovedKeys && !other->_preapprovedKeys) || [self->_preapprovedKeys isEqual:other->_preapprovedKeys])
;
}
[self->_voucher hash]
^
[self->_voucherSignature hash]
- ^
- [self->_preapprovedKeys hash]
;
}
{
[self setVoucherSignature:other->_voucherSignature];
}
- for (NSData *iter_preapprovedKeys in other->_preapprovedKeys)
- {
- [self addPreapprovedKeys:iter_preapprovedKeys];
- }
}
@end
+++ /dev/null
-// This file was automatically generated by protocompiler
-// DO NOT EDIT!
-// Compiled from OTPairingMessage.proto
-
-#import "OTSOSMessage.h"
-#import <ProtocolBuffer/PBConstants.h>
-#import <ProtocolBuffer/PBHashUtil.h>
-#import <ProtocolBuffer/PBDataReader.h>
-
-#if !__has_feature(objc_arc)
-# error This generated file depends on ARC but it is not enabled; turn on ARC, or use 'objc_use_arc' option to generate non-ARC code.
-#endif
-
-@implementation OTSOSMessage
-
-- (BOOL)hasCredential
-{
- return _credential != nil;
-}
-@synthesize credential = _credential;
-- (BOOL)hasPeerInfo
-{
- return _peerInfo != nil;
-}
-@synthesize peerInfo = _peerInfo;
-- (BOOL)hasCircleBlob
-{
- return _circleBlob != nil;
-}
-@synthesize circleBlob = _circleBlob;
-- (BOOL)hasInitialSyncItems
-{
- return _initialSyncItems != nil;
-}
-@synthesize initialSyncItems = _initialSyncItems;
-
-- (NSString *)description
-{
- return [NSString stringWithFormat:@"%@ %@", [super description], [self dictionaryRepresentation]];
-}
-
-- (NSDictionary *)dictionaryRepresentation
-{
- NSMutableDictionary *dict = [NSMutableDictionary dictionary];
- if (self->_credential)
- {
- [dict setObject:self->_credential forKey:@"credential"];
- }
- if (self->_peerInfo)
- {
- [dict setObject:self->_peerInfo forKey:@"peerInfo"];
- }
- if (self->_circleBlob)
- {
- [dict setObject:self->_circleBlob forKey:@"circleBlob"];
- }
- if (self->_initialSyncItems)
- {
- [dict setObject:self->_initialSyncItems forKey:@"initialSyncItems"];
- }
- return dict;
-}
-
-BOOL OTSOSMessageReadFrom(__unsafe_unretained OTSOSMessage *self, __unsafe_unretained PBDataReader *reader) {
- while (PBReaderHasMoreData(reader)) {
- uint32_t tag = 0;
- uint8_t aType = 0;
-
- PBReaderReadTag32AndType(reader, &tag, &aType);
-
- if (PBReaderHasError(reader))
- break;
-
- if (aType == TYPE_END_GROUP) {
- break;
- }
-
- switch (tag) {
-
- case 1 /* credential */:
- {
- NSData *new_credential = PBReaderReadData(reader);
- self->_credential = new_credential;
- }
- break;
- case 2 /* peerInfo */:
- {
- NSData *new_peerInfo = PBReaderReadData(reader);
- self->_peerInfo = new_peerInfo;
- }
- break;
- case 3 /* circleBlob */:
- {
- NSData *new_circleBlob = PBReaderReadData(reader);
- self->_circleBlob = new_circleBlob;
- }
- break;
- case 4 /* initialSyncItems */:
- {
- NSData *new_initialSyncItems = PBReaderReadData(reader);
- self->_initialSyncItems = new_initialSyncItems;
- }
- break;
- default:
- if (!PBReaderSkipValueWithTag(reader, tag, aType))
- return NO;
- break;
- }
- }
- return !PBReaderHasError(reader);
-}
-
-- (BOOL)readFrom:(PBDataReader *)reader
-{
- return OTSOSMessageReadFrom(self, reader);
-}
-- (void)writeTo:(PBDataWriter *)writer
-{
- /* credential */
- {
- if (self->_credential)
- {
- PBDataWriterWriteDataField(writer, self->_credential, 1);
- }
- }
- /* peerInfo */
- {
- if (self->_peerInfo)
- {
- PBDataWriterWriteDataField(writer, self->_peerInfo, 2);
- }
- }
- /* circleBlob */
- {
- if (self->_circleBlob)
- {
- PBDataWriterWriteDataField(writer, self->_circleBlob, 3);
- }
- }
- /* initialSyncItems */
- {
- if (self->_initialSyncItems)
- {
- PBDataWriterWriteDataField(writer, self->_initialSyncItems, 4);
- }
- }
-}
-
-- (void)copyTo:(OTSOSMessage *)other
-{
- if (_credential)
- {
- other.credential = _credential;
- }
- if (_peerInfo)
- {
- other.peerInfo = _peerInfo;
- }
- if (_circleBlob)
- {
- other.circleBlob = _circleBlob;
- }
- if (_initialSyncItems)
- {
- other.initialSyncItems = _initialSyncItems;
- }
-}
-
-- (id)copyWithZone:(NSZone *)zone
-{
- OTSOSMessage *copy = [[[self class] allocWithZone:zone] init];
- copy->_credential = [_credential copyWithZone:zone];
- copy->_peerInfo = [_peerInfo copyWithZone:zone];
- copy->_circleBlob = [_circleBlob copyWithZone:zone];
- copy->_initialSyncItems = [_initialSyncItems copyWithZone:zone];
- return copy;
-}
-
-- (BOOL)isEqual:(id)object
-{
- OTSOSMessage *other = (OTSOSMessage *)object;
- return [other isMemberOfClass:[self class]]
- &&
- ((!self->_credential && !other->_credential) || [self->_credential isEqual:other->_credential])
- &&
- ((!self->_peerInfo && !other->_peerInfo) || [self->_peerInfo isEqual:other->_peerInfo])
- &&
- ((!self->_circleBlob && !other->_circleBlob) || [self->_circleBlob isEqual:other->_circleBlob])
- &&
- ((!self->_initialSyncItems && !other->_initialSyncItems) || [self->_initialSyncItems isEqual:other->_initialSyncItems])
- ;
-}
-
-- (NSUInteger)hash
-{
- return 0
- ^
- [self->_credential hash]
- ^
- [self->_peerInfo hash]
- ^
- [self->_circleBlob hash]
- ^
- [self->_initialSyncItems hash]
- ;
-}
-
-- (void)mergeFrom:(OTSOSMessage *)other
-{
- if (other->_credential)
- {
- [self setCredential:other->_credential];
- }
- if (other->_peerInfo)
- {
- [self setPeerInfo:other->_peerInfo];
- }
- if (other->_circleBlob)
- {
- [self setCircleBlob:other->_circleBlob];
- }
- if (other->_initialSyncItems)
- {
- [self setInitialSyncItems:other->_initialSyncItems];
- }
-}
-
-@end
-
--- /dev/null
+disabled_rules:
+ - force_cast
+ - force_try
state.peerID = "asdf"
state.icloudAccountState = .ACCOUNT_AVAILABLE
state.trustState = .TRUSTED
+ state.cdpState = .ENABLED
XCTAssertNoThrow(try state.saveToKeychain(forContainer: OTCKContainerName, contextID: OTDefaultContext), "saving to the keychain should work")
XCTAssertEqual(state2.peerID, state.peerID, "peer ID persists through keychain")
XCTAssertEqual(state2.icloudAccountState, state.icloudAccountState, "account state persists through keychain")
XCTAssertEqual(state2.trustState, state.trustState, "trust state persists through keychain")
+ XCTAssertEqual(state2.cdpState, state.cdpState, "cdp state persists through keychain")
} catch {
XCTFail("error loading from keychain: \(error)")
}
}
return nil
}
+
+ func disable(withInfo info: [AnyHashable: Any]!) -> Error? {
+ return nil
+ }
}
class OTMockFollowUpController: NSObject, OctagonFollowUpControllerProtocol {
--- /dev/null
+/*
+* Copyright (c) 2019 Apple Inc. All Rights Reserved.
+*
+* @APPLE_LICENSE_HEADER_START@
+*
+* This file contains Original Code and/or Modifications of Original Code
+* as defined in and that are subject to the Apple Public Source License
+* Version 2.0 (the 'License'). You may not use this file except in
+* compliance with the License. Please obtain a copy of the License at
+* http://www.opensource.apple.com/apsl/ and read it before using this
+* file.
+*
+* The Original Code and all software distributed under the License are
+* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+* Please see the License for the specific language governing rights and
+* limitations under the License.
+*
+* @APPLE_LICENSE_HEADER_END@
+*/
+
+import Foundation
+
+#if OCTAGON
+
+class OctagonAccountTests: OctagonTestsBase {
+ func testAccountSave() throws {
+ let contextName = OTDefaultContext
+ let containerName = OTCKContainerName
+
+ self.startCKAccountStatusMock()
+
+ // Before resetAndEstablish, there shouldn't be any stored account state
+ XCTAssertThrowsError(try OTAccountMetadataClassC.loadFromKeychain(forContainer: containerName, contextID: contextName), "Before doing anything, loading a non-existent account state should fail")
+
+ let resetAndEstablishExpectation = self.expectation(description: "resetAndEstablish callback occurs")
+ self.manager.resetAndEstablish(containerName,
+ context: contextName,
+ altDSID: "new altDSID",
+ resetReason: .testGenerated) { resetError in
+ XCTAssertNil(resetError, "Should be no error calling resetAndEstablish")
+ resetAndEstablishExpectation.fulfill()
+ }
+
+ self.wait(for: [resetAndEstablishExpectation], timeout: 10)
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+
+ let selfPeerID = try self.cuttlefishContext.accountMetadataStore.loadOrCreateAccountMetadata().peerID
+
+ // After resetAndEstablish, you should be able to see the persisted account state
+ do {
+ let accountState = try OTAccountMetadataClassC.loadFromKeychain(forContainer: containerName, contextID: contextName)
+ XCTAssertEqual(selfPeerID, accountState.peerID, "Saved account state should have the same peer ID that prepare returned")
+ XCTAssertEqual(accountState.cdpState, .ENABLED, "Saved CDP status should be 'enabled' after a resetAndEstablish")
+ } catch {
+ XCTFail("error loading account state: \(error)")
+ }
+ }
+
+ func testLoadToNoAccount() throws {
+ // No CloudKit account, either
+ self.accountStatus = .noAccount
+ self.startCKAccountStatusMock()
+
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+ // With no identity and AuthKit reporting no iCloud account, Octagon should go directly into 'no account'
+ self.mockAuthKit.altDSID = nil
+
+ let asyncExpectation = self.expectation(description: "dispatch works")
+ let quiescentExpectation = self.expectation(description: "quiescence has been determined")
+ DispatchQueue.global(qos: .userInitiated).async { [weak self] in
+ asyncExpectation.fulfill()
+
+ let c = self!.cuttlefishContext.stateMachine.paused
+ XCTAssertEqual(0, c.wait(10 * NSEC_PER_SEC), "State machine should become quiescent")
+ quiescentExpectation.fulfill()
+ }
+ // Wait for the block above to fire before continuing
+ self.wait(for: [asyncExpectation], timeout: 10)
+
+ self.cuttlefishContext.startOctagonStateMachine()
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+ XCTAssertTrue(self.cuttlefishContext.stateMachine.isPaused(), "State machine should be stopped")
+ self.assertNoAccount(context: self.cuttlefishContext)
+
+ XCTAssertEqual(0, self.cuttlefishContext.stateMachine.paused.wait(10 * NSEC_PER_SEC), "State machine should be quiescent")
+
+ self.wait(for: [quiescentExpectation], timeout: 10)
+
+ // CKKS should also be logged out, since Octagon believes there's no account
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
+ }
+
+ func testNoAccountLeadsToInitialize() throws {
+ self.startCKAccountStatusMock()
+
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+ // With no identity and AuthKit reporting no iCloud account, Octagon should go directly into 'no account'
+ self.mockAuthKit.altDSID = nil
+
+ self.cuttlefishContext.startOctagonStateMachine()
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+
+ self.mockAuthKit.altDSID = "1234"
+ let signinExpectation = self.expectation(description: "sign in returns")
+ self.otControl.sign(in: "1234", container: nil, context: OTDefaultContext) { error in
+ XCTAssertNil(error, "error should be nil")
+ signinExpectation.fulfill()
+ }
+ self.wait(for: [signinExpectation], timeout: 10)
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+
+ // And now the CDP bit is set...
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+ }
+
+ func testSignIn() throws {
+ self.startCKAccountStatusMock()
+
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+ // Device is signed out
+ self.mockAuthKit.altDSID = nil
+ self.mockAuthKit.hsa2 = false
+
+ // With no account, Octagon should go directly into 'NoAccount'
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+
+ // Sign in occurs
+ let newAltDSID = UUID().uuidString
+ self.mockAuthKit.altDSID = newAltDSID
+ self.mockAuthKit.hsa2 = true
+ XCTAssertNoThrow(try self.cuttlefishContext.accountAvailable(newAltDSID), "Sign-in shouldn't error")
+
+ // Octagon should go into 'waitforcdp'
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
+ XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .disabled, "CDP status should be 'disabled'")
+ XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have not posted a repair CFU while waiting for CDP")
+
+ // And CDP is enabled:
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
+ XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .enabled, "CDP status should be 'enabled'")
+
+ #if !os(tvOS)
+ XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have posted a repair CFU")
+ #else
+ XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "posted should be false on tvOS; there aren't any devices around to repair it")
+ #endif
+
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+
+ // On sign-out, octagon should go back to 'no account'
+ self.mockAuthKit.altDSID = nil
+ XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "sign-out shouldn't error")
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+ self.assertNoAccount(context: self.cuttlefishContext)
+ XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .unknown, "CDP status should be 'unknown'")
+
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+ }
+
+ func testSignInWithDelayedHSA2Status() throws {
+ self.startCKAccountStatusMock()
+
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+ // Device is signed out
+ self.mockAuthKit.altDSID = nil
+ self.mockAuthKit.hsa2 = false
+
+ // With no account, Octagon should go directly into 'NoAccount'
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+
+ // Sign in occurs, but HSA2 status isn't here yet
+ let newAltDSID = UUID().uuidString
+ self.mockAuthKit.altDSID = newAltDSID
+ XCTAssertNoThrow(try self.cuttlefishContext.accountAvailable(newAltDSID), "Sign-in shouldn't error")
+
+ // Octagon should go into 'waitforhsa2'
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForHSA2, within: 10 * NSEC_PER_SEC)
+
+ self.mockAuthKit.hsa2 = true
+ XCTAssertNoThrow(try self.cuttlefishContext.idmsTrustLevelChanged(), "Notification of IDMS trust level shouldn't error")
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
+
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
+
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+
+ // On sign-out, octagon should go back to 'no account'
+ self.mockAuthKit.altDSID = nil
+ XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "sign-out shouldn't error")
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+ self.assertNoAccount(context: self.cuttlefishContext)
+
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+ }
+
+ func testSignInWithCDPStateBeforeDelayedHSA2Status() throws {
+ self.startCKAccountStatusMock()
+
+ // Device is signed out
+ self.mockAuthKit.altDSID = nil
+ self.mockAuthKit.hsa2 = false
+
+ // With no account, Octagon should go directly into 'NoAccount'
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+
+ // CDP state is set. Cool?
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+
+ // Sign in occurs, but HSA2 status isn't here yet
+ let newAltDSID = UUID().uuidString
+ self.mockAuthKit.altDSID = newAltDSID
+ self.mockAuthKit.hsa2 = true
+ XCTAssertNoThrow(try self.cuttlefishContext.accountAvailable(newAltDSID), "Sign-in shouldn't error")
+
+ // Octagon should go into 'untrusted', as everything is in place
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+
+ // On sign-out, octagon should go back to 'no account'
+ self.mockAuthKit.altDSID = nil
+ XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "sign-out shouldn't error")
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+ self.assertNoAccount(context: self.cuttlefishContext)
+
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+ }
+
+ func testSetCDPStateWithUnconfiguredArguments() throws {
+ self.startCKAccountStatusMock()
+
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+ // Device is signed out
+ self.mockAuthKit.altDSID = nil
+ self.mockAuthKit.hsa2 = false
+
+ // With no account, Octagon should go directly into 'NoAccount'
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+
+ // Now set the CDP state, but using the OTClique API (and not configuring the context)
+ let unconfigured = OTConfigurationContext()
+ unconfigured.otControl = self.otControl
+ XCTAssertNoThrow(try OTClique.setCDPEnabled(unconfigured))
+
+ // Sign in occurs, but HSA2 status isn't here yet
+ let newAltDSID = UUID().uuidString
+ self.mockAuthKit.altDSID = newAltDSID
+ XCTAssertNoThrow(try self.cuttlefishContext.accountAvailable(newAltDSID), "Sign-in shouldn't error")
+
+ // Octagon should go into 'waitforhsa2'
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForHSA2, within: 10 * NSEC_PER_SEC)
+
+ self.mockAuthKit.hsa2 = true
+ XCTAssertNoThrow(try self.cuttlefishContext.idmsTrustLevelChanged(), "Notification of IDMS trust level shouldn't error")
+
+ // and we should skip waiting for CDP, as it's already set
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
+
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+
+ // On sign-out, octagon should go back to 'no account'
+ self.mockAuthKit.altDSID = nil
+ XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "sign-out shouldn't error")
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+ self.assertNoAccount(context: self.cuttlefishContext)
+
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+ }
+
+ func testSignInWithExistingCuttlefishRecordsSetsCDPStatus() throws {
+ self.startCKAccountStatusMock()
+
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+ // signing in to an account with pre-existing Octagon data should turn on the CDP bit by default:
+ // no setting needed.
+
+ let remote = self.makeInitiatorContext(contextID: "remote")
+ self.assertResetAndBecomeTrusted(context: remote)
+
+ // when this context boots up, it should go straight into untrusted, and set its CDP bit
+ // since there's already CDP data in the account
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+ XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .enabled, "CDP status should be 'enabled'")
+
+ #if !os(tvOS)
+ XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have posted a repair CFU after the CDP bit was set")
+ #else
+ XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should not have posted on tvOS; there aren't any iphones around to repair it")
+ #endif
+ }
+
+ func testEnableCDPStatusIfNotificationArrives() throws {
+ self.startCKAccountStatusMock()
+
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+ // default context comes up, but CDP is not enabled
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
+ XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .disabled, "CDP status should be 'disabled'")
+ XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have not posted a repair CFU while waiting for CDP")
+
+ // If a cuttlefish push occurs without any data existing, the CDP bit should stay off
+ self.sendContainerChangeWaitForFetchForStates(context: self.cuttlefishContext, states: [OctagonStateWaitForCDPUpdated, OctagonStateDetermineCDPState])
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
+ XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .disabled, "CDP status should be 'disabled'")
+ XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have not posted a repair CFU while waiting for CDP")
+
+ // Another peer comes along and performs Octagon operations
+ let remote = self.makeInitiatorContext(contextID: "remote")
+ self.assertResetAndBecomeTrusted(context: remote)
+
+ // And some SOS operations. SOS now returns "error" when asked its state
+ self.mockSOSAdapter.circleStatusError = NSError(domain: kSOSErrorDomain as String, code: kSOSErrorPublicKeyAbsent, userInfo: nil)
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCError)
+
+ // And after the update, the context should go into 'untrusted'
+ self.sendContainerChangeWaitForFetchForStates(context: self.cuttlefishContext, states: [OctagonStateWaitForCDPUpdated, OctagonStateDetermineCDPState])
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+ XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .enabled, "CDP status should be 'enabled'")
+
+ #if !os(tvOS)
+ XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have posted a repair CFU")
+ #else
+ XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should not have posted on tvOS; there aren't any iphones around to repair it")
+ #endif
+ }
+
+ func testEnableCDPStatusIfNotificationArrivesWithoutCreatingSOSCircle() throws {
+ self.startCKAccountStatusMock()
+
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+ // default context comes up, but CDP is not enabled
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
+ XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .disabled, "CDP status should be 'disabled'")
+ XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have not posted a repair CFU while waiting for CDP")
+
+ // If a cuttlefish push occurs without any data existing, the CDP bit should stay off
+ self.sendContainerChangeWaitForFetchForStates(context: self.cuttlefishContext, states: [OctagonStateWaitForCDPUpdated, OctagonStateDetermineCDPState])
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
+ XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .disabled, "CDP status should be 'disabled'")
+ XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have not posted a repair CFU while waiting for CDP")
+
+ // Another peer comes along and performs Octagon operations
+ let remote = self.makeInitiatorContext(contextID: "remote")
+ self.assertResetAndBecomeTrusted(context: remote)
+
+ // Unlike testEnableCDPStatusIfNotificationArrives, SOS remains in 'absent', simulating a non-sos platform creating Octagon
+
+ // And after the update, the context should go into 'untrusted'
+ self.sendContainerChangeWaitForFetchForStates(context: self.cuttlefishContext, states: [OctagonStateWaitForCDPUpdated, OctagonStateDetermineCDPState])
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+ XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .enabled, "CDP status should be 'enabled'")
+
+ #if !os(tvOS)
+ XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have posted a repair CFU")
+ #else
+ XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should not have posted on tvOS; there aren't any iphones around to repair it")
+ #endif
+ }
+
+ func testCDPEnableAPIRaceWithCDPStateDetermination() throws {
+ // The API call to enable CDP might occur while Octagon is figuring out that it should be disabled
+ // Octagon should respect the API call
+
+ // SOS is absent
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+ self.startCKAccountStatusMock()
+
+ // The initial CDP-enable API call happens while Octagon is fetching the world's state
+ let fetchExpectation = self.expectation(description: "fetchChanges called")
+ self.fakeCuttlefishServer.fetchChangesListener = { [unowned self] _ in
+ do {
+ try self.cuttlefishContext.setCDPEnabled()
+ } catch {
+ XCTFail("Expected to be able to set CDP status without error: \(error)")
+ }
+ fetchExpectation.fulfill()
+ return nil
+ }
+
+ self.cuttlefishContext.startOctagonStateMachine()
+
+ // Octagon should go into 'untrusted', as the API call said that CDP was there
+ self.wait(for: [fetchExpectation], timeout: 10)
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
+
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+ }
+
+ func testSignOut() throws {
+ self.startCKAccountStatusMock()
+
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+
+ do {
+ let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, resetReason: .testGenerated)
+ XCTAssertNotNil(clique, "Clique should not be nil")
+ } catch {
+ XCTFail("Shouldn't have errored making new friends: \(error)")
+ }
+
+ // Now, we should be in 'ready'
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
+ self.verifyDatabaseMocks()
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+
+ // And 'dump' should show some information
+ let dumpExpectation = self.expectation(description: "dump callback occurs")
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, error in
+ XCTAssertNil(error, "Should be no error dumping data")
+ XCTAssertNotNil(dump, "dump should not be nil")
+ let egoSelf = dump!["self"] as? [String: AnyObject]
+ XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
+ let peerID = egoSelf!["peerID"] as? String
+ XCTAssertNotNil(peerID, "peerID should not be nil")
+
+ dumpExpectation.fulfill()
+ }
+ self.wait(for: [dumpExpectation], timeout: 10)
+
+ // Turn off the CK account too
+ self.accountStatus = .noAccount
+ self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
+
+ XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "Should be no issue signing out")
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+ self.assertNoAccount(context: self.cuttlefishContext)
+
+ // And 'dump' should show nothing
+ let signedOutDumpExpectation = self.expectation(description: "dump callback occurs")
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, error in
+ XCTAssertNil(error, "Should be no error dumping data")
+ XCTAssertNotNil(dump, "dump should not be nil")
+ let egoSelf = dump!["self"] as? [String: AnyObject]
+ XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
+ XCTAssertEqual(egoSelf!.count, 0, "egoSelf should have zero elements")
+
+ signedOutDumpExpectation.fulfill()
+ }
+ self.wait(for: [signedOutDumpExpectation], timeout: 10)
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
+
+ //check trust status
+ let checkTrustExpectation = self.expectation(description: "checkTrustExpectation callback occurs")
+ let configuration = OTOperationConfiguration()
+ self.cuttlefishContext.rpcTrustStatus(configuration) { _, _, _, _, _ in
+ checkTrustExpectation.fulfill()
+ }
+ self.wait(for: [checkTrustExpectation], timeout: 10)
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+
+ // And 'dump' should show nothing
+ let signedOutDumpExpectationAfterCheckTrustStatus = self.expectation(description: "dump callback occurs")
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, error in
+ XCTAssertNil(error, "Should be no error dumping data")
+ XCTAssertNotNil(dump, "dump should not be nil")
+ let egoSelf = dump!["self"] as? [String: AnyObject]
+ XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
+ XCTAssertEqual(egoSelf!.count, 0, "egoSelf should have zero elements")
+
+ signedOutDumpExpectationAfterCheckTrustStatus.fulfill()
+ }
+ self.wait(for: [signedOutDumpExpectationAfterCheckTrustStatus], timeout: 10)
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+ self.assertNoAccount(context: self.cuttlefishContext)
+ }
+
+ func testSignOutFromWaitForCDP() throws {
+ self.startCKAccountStatusMock()
+
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+
+ // Turn off the CK account too
+ self.accountStatus = .noAccount
+ self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
+
+ XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "Should be no issue signing out")
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+ self.assertNoAccount(context: self.cuttlefishContext)
+ XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .unknown, "CDP status should be 'unknown'")
+ }
+
+ func testNoAccountTimeoutTransitionWatcher() throws {
+ self.startCKAccountStatusMock()
+
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+ // With no identity and AuthKit reporting no iCloud account, Octagon should go directly into 'no account'
+ self.mockAuthKit.altDSID = nil
+
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.cuttlefishContext.stateMachine.setWatcherTimeout(2 * NSEC_PER_SEC)
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+ XCTAssertTrue(self.cuttlefishContext.stateMachine.isPaused(), "State machine should be stopped")
+ self.assertNoAccount(context: self.cuttlefishContext)
+ XCTAssertEqual(0, self.cuttlefishContext.stateMachine.paused.wait(10 * NSEC_PER_SEC), "State machine should be quiescent")
+
+ let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
+ self.cuttlefishContext.join(withBottle: "bottleID", entropy: Data(), bottleSalt: "peer2AltDSID") { error in
+ XCTAssertNotNil(error, "error should not be nil")
+ joinWithBottleExpectation.fulfill()
+ }
+ self.wait(for: [joinWithBottleExpectation], timeout: 3)
+ }
+
+}
+
+#endif
// Right after CKKS fetches for the first time, insert a new key hierarchy into CloudKit
self.silentFetchesAllowed = false
- self.expectCKFetchAndRun(beforeFinished: {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+ self.expectCKFetchAndRun {
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putFakeDeviceStatusesInCloudKit()
self.silentFetchesAllowed = true
- })
+ }
self.cuttlefishContext.startOctagonStateMachine()
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
// Right after CKKS fetches for the first time, insert a new key hierarchy into CloudKit
self.silentFetchesAllowed = false
- self.expectCKFetchAndRun(beforeFinished: {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.expectCKFetchAndRun {
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putFakeDeviceStatusesInCloudKit()
+ self.saveTLKMaterialToKeychain()
self.silentFetchesAllowed = true
- })
+ }
self.cuttlefishContext.startOctagonStateMachine()
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
--- /dev/null
+import Foundation
+
+class OctagonCKKSConfigurationTestsPolicyDisabled: OctagonTestsBase {
+ // Pre-configure some things, so the OctagonTests will only operate on these views
+ override func setUp() {
+ if self.mockDeviceInfo == nil {
+ let actualDeviceAdapter = OTDeviceInformationActualAdapter()
+ self.mockDeviceInfo = OTMockDeviceInfoAdapter(modelID: actualDeviceAdapter.modelID(),
+ deviceName: actualDeviceAdapter.deviceName(),
+ serialNumber: NSUUID().uuidString,
+ osVersion: actualDeviceAdapter.osVersion())
+ }
+
+ // With the policy disabled, we only want to operate on a few zones
+ if self.mockDeviceInfo.mockModelID.contains("AppleTV") {
+ self.intendedCKKSZones = Set([
+ CKRecordZone.ID(zoneName: "LimitedPeersAllowed"),
+ ])
+ } else {
+ self.intendedCKKSZones = Set([
+ CKRecordZone.ID(zoneName: "LimitedPeersAllowed"),
+ CKRecordZone.ID(zoneName: "Manatee"),
+ ])
+ }
+
+ self.setCKKSViewsFromPolicyToNo = true
+
+ super.setUp()
+
+ XCTAssertFalse(self.cuttlefishContext.viewManager!.useCKKSViewsFromPolicy(), "CKKS should not be configured to listen to policy-based views")
+ }
+
+ func testMergedViewListOff() throws {
+ XCTAssertFalse(self.cuttlefishContext.viewManager!.useCKKSViewsFromPolicy(), "CKKS should not be configured to listen to policy-based views")
+
+ self.startCKAccountStatusMock()
+ self.assertResetAndBecomeTrustedInDefaultContext()
+
+ let viewList = self.cuttlefishContext.viewManager!.viewList()
+ #if !os(tvOS)
+ let expected = Set<String>(["Manatee", "LimitedPeersAllowed"])
+ #else
+ let expected = Set<String>(["LimitedPeersAllowed"])
+ #endif
+ XCTAssertEqual(expected, viewList)
+ }
+}
+
+class OctagonCKKSConfigurationTestsPolicyEnabled: OctagonTestsBase {
+ override func setUp() {
+
+ if self.mockDeviceInfo == nil {
+ let actualDeviceAdapter = OTDeviceInformationActualAdapter()
+ self.mockDeviceInfo = OTMockDeviceInfoAdapter(modelID: actualDeviceAdapter.modelID(),
+ deviceName: actualDeviceAdapter.deviceName(),
+ serialNumber: NSUUID().uuidString,
+ osVersion: actualDeviceAdapter.osVersion())
+ }
+
+ // Most tests will use a much smaller list of views. But not us! Go wild!
+ if self.mockDeviceInfo.mockModelID.contains("AppleTV") {
+ self.intendedCKKSZones = Set([
+ CKRecordZone.ID(zoneName: "Home"),
+ self.limitedPeersAllowedZoneID!,
+ CKRecordZone.ID(zoneName: "WiFi"),
+ ])
+ } else {
+ self.intendedCKKSZones = Set([
+ CKRecordZone.ID(zoneName: "ApplePay"),
+ CKRecordZone.ID(zoneName: "Applications"),
+ CKRecordZone.ID(zoneName: "AutoUnlock"),
+ // <rdar://problem/57771098> Octagon: create final policy for CKKS4All
+ // CKRecordZone.ID(zoneName: "Backstop"),
+ // <rdar://problem/57810109> Cuttlefish: remove Safari prefix from view names
+ CKRecordZone.ID(zoneName: "SafariCreditCards"),
+ CKRecordZone.ID(zoneName: "DevicePairing"),
+ CKRecordZone.ID(zoneName: "Engram"),
+ CKRecordZone.ID(zoneName: "Health"),
+ CKRecordZone.ID(zoneName: "Home"),
+ CKRecordZone.ID(zoneName: "LimitedPeersAllowed"),
+ CKRecordZone.ID(zoneName: "Manatee"),
+ // <rdar://problem/57810109> Cuttlefish: remove Safari prefix from view names
+ CKRecordZone.ID(zoneName: "SafariPasswords"),
+ CKRecordZone.ID(zoneName: "ProtectedCloudStorage"),
+ CKRecordZone.ID(zoneName: "SecureObjectSync"),
+ CKRecordZone.ID(zoneName: "WiFi"),
+ ])
+ }
+
+ super.setUp()
+ }
+
+ func testMergedViewListOn() throws {
+ XCTAssertTrue(self.cuttlefishContext.viewManager!.useCKKSViewsFromPolicy(), "CKKS should be configured to listen to policy-based views")
+
+ self.startCKAccountStatusMock()
+ self.assertResetAndBecomeTrustedInDefaultContext()
+
+ let viewList = self.cuttlefishContext.viewManager!.viewList()
+
+ #if !os(tvOS)
+ let expected = Set<String>([
+ "ApplePay",
+ "Applications",
+ "AutoUnlock",
+ // <rdar://problem/57771098> Octagon: create final policy for CKKS4All
+ //"Backstop",
+ // <rdar://problem/57810109> Cuttlefish: remove Safari prefix from view names
+ "SafariCreditCards",
+ "DevicePairing",
+ "Engram",
+ "Health",
+ "Home",
+ "LimitedPeersAllowed",
+ "Manatee",
+ // <rdar://problem/57810109> Cuttlefish: remove Safari prefix from view names
+ "SafariPasswords",
+ "ProtectedCloudStorage",
+ "SecureObjectSync",
+ "WiFi",
+ ])
+ #else
+ let expected = Set<String>(["LimitedPeersAllowed",
+ "Home",
+ "WiFi", ])
+ #endif
+ XCTAssertEqual(expected, viewList)
+ }
+
+ func testPolicyResetRPC() throws {
+ XCTAssertTrue(self.cuttlefishContext.viewManager!.useCKKSViewsFromPolicy(), "CKKS should be configured to listen to policy-based views")
+
+ self.startCKAccountStatusMock()
+ self.assertResetAndBecomeTrustedInDefaultContext()
+
+ XCTAssertNotNil(self.injectedManager?.policy, "Should have given CKKS a TPPolicy during initialization")
+ XCTAssertEqual(self.injectedManager?.policy?.version, prevailingPolicyVersion, "Policy given to CKKS should be prevailing policy")
+
+ self.injectedManager!.resetSyncingPolicy()
+ XCTAssertNil(self.injectedManager?.policy, "CKKS policy should be reset (by the test)")
+
+ self.otControl.refetchCKKSPolicy(nil, contextID: self.cuttlefishContext.contextID) { error in
+ XCTAssertNil(error, "Should be no error refetching the CKKS policy")
+ }
+
+ XCTAssertNotNil(self.injectedManager?.policy, "Should have given CKKS a TPPolicy during refetch")
+ XCTAssertEqual(self.injectedManager?.policy?.version, prevailingPolicyVersion, "Policy given to CKKS should be prevailing policy")
+ }
+}
self.startCKAccountStatusMock()
self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
// With no account, Octagon should go directly into 'NoAccount'
self.cuttlefishContext.startOctagonStateMachine()
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
self.mockAuthKit.altDSID = newAltDSID
XCTAssertNoThrow(try self.cuttlefishContext.accountAvailable(newAltDSID), "Sign-in shouldn't error")
- // We should reach 'untrusted', as we cached the CK value
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
- self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
+ // We should reach 'waitforcdp', as we cached the CK value
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
}
func testSignInPausesForCloudKit() throws {
self.accountStatus = .noAccount
self.startCKAccountStatusMock()
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
// With no account, Octagon should go directly into 'NoAccount'
self.cuttlefishContext.startOctagonStateMachine()
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitingForCloudKitAccount, within: 10 * NSEC_PER_SEC)
assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
- // And when CK shows up, we should go into 'untrusted'
+ // And when CK shows up, we should go into 'waitforcdp'
self.accountStatus = .available
self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
+
+ // And then CDP is set to be on
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
self.accountStatus = .noAccount
self.startCKAccountStatusMock()
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
// With no account, Octagon should go directly into 'NoAccount'
self.cuttlefishContext.startOctagonStateMachine()
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
// Octagon should go into 'wait for cloudkit account'
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitingForCloudKitAccount, within: 10 * NSEC_PER_SEC)
- // And when CK shows up, we should go into 'untrusted'
+ // And when CK shows up, we should go into 'waitforcdp'
self.accountStatus = .available
self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
+
+ // and then, when CDP tells us, we should go into 'untrusted'
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
self.accountStatus = .noAccount
self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+ // With no account, Octagon should go directly into 'NoAccount'
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+
+ // Account signs in as SA.
+ let newAltDSID = UUID().uuidString
+ self.mockAuthKit.altDSID = newAltDSID
+
+ XCTAssertNoThrow(try self.cuttlefishContext.idmsTrustLevelChanged(), "Notification of IDMS trust level shouldn't error")
+ XCTAssertNoThrow(try self.cuttlefishContext.accountAvailable(newAltDSID), "Sign-in shouldn't error")
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForHSA2, within: 10 * NSEC_PER_SEC)
+ self.assertNoAccount(context: self.cuttlefishContext)
+
+ // Account promotes to HSA2
+ self.mockAuthKit.hsa2 = true
+ XCTAssertNoThrow(try self.cuttlefishContext.idmsTrustLevelChanged(), "Notification of IDMS trust level shouldn't error")
+
+ // Octagon should go into 'waitforcloudkit'
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitingForCloudKitAccount, within: 10 * NSEC_PER_SEC)
+ self.assertAccountAvailable(context: self.cuttlefishContext)
+
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
+
+ // On CK account sign-in, Octagon should race to 'waitforcdp'
+ self.accountStatus = .available
+ self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
+
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+
+ // On sign-out, octagon should go back to 'no account'
+ self.mockAuthKit.altDSID = nil
+ XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "sign-out shouldn't error")
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+ self.assertNoAccount(context: self.cuttlefishContext)
+
+ // But CKKS is listening for the CK account removal, not the accountNoLongerAvailable call
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+
+ self.accountStatus = .noAccount
+ self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
+ }
+
+ func testSAtoHSA2PromotionandCDPWithoutCloudKit() throws {
+ self.startCKAccountStatusMock()
+
+ // Device is signed out
+ self.mockAuthKit.altDSID = nil
+ self.mockAuthKit.hsa2 = false
+
+ self.accountStatus = .noAccount
+ self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
+
// With no account, Octagon should go directly into 'NoAccount'
self.cuttlefishContext.startOctagonStateMachine()
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
+ // And setting CDP bit now should be successful, but not move the state machine
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitingForCloudKitAccount, within: 10 * NSEC_PER_SEC)
+
// On CK account sign-in, Octagon should race to 'untrusted'
self.accountStatus = .available
self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
}
+
+ func testStatusRPCsWithUnknownCloudKitAccount() throws {
+ // If CloudKit isn't returning our calls, we should still return something reasonable...
+ let statusexpectation = self.expectation(description: "trust status returns")
+ let configuration = OTOperationConfiguration()
+ configuration.timeoutWaitForCKAccount = 500 * NSEC_PER_MSEC
+ self.cuttlefishContext.rpcTrustStatus(configuration) { egoStatus, _, _, _, _ in
+ XCTAssertTrue([.absent].contains(egoStatus), "Self peer should be in the 'absent' state")
+ statusexpectation.fulfill()
+ }
+ self.wait(for: [statusexpectation], timeout: 10)
+
+ // Now sign in to 'untrusted'
+ self.startCKAccountStatusMock()
+
+ self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
+
+ // And restart, without any idea of the cloudkit state
+ self.ckaccountHoldOperation = BlockOperation()
+ self.manager.accountStateTracker = CKKSAccountStateTracker(self.manager.cloudKitContainer,
+ nsnotificationCenterClass: FakeNSNotificationCenter.self as CKKSNSNotificationCenter.Type)
+ self.injectedManager!.accountTracker = self.manager.accountStateTracker
+
+ self.manager.removeContext(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
+ self.restartCKKSViews()
+ self.cuttlefishContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
+
+ // Should know it's untrusted
+ self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
+ self.startCKAccountStatusMock()
+
+ // Now become ready
+ do {
+ let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, resetReason: .testGenerated)
+ XCTAssertNotNil(clique, "Clique should not be nil")
+ } catch {
+ XCTFail("Shouldn't have errored making new friends: \(error)")
+ }
+
+ // Now, we should be in 'ready'
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
+ self.assertConsidersSelfTrustedCachedAccountStatus(context: self.cuttlefishContext)
+
+ // and all subCKKSes should enter ready...
+ assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ self.verifyDatabaseMocks()
+
+ // Restart one more time:
+
+ self.ckaccountHoldOperation = BlockOperation()
+ self.manager.accountStateTracker = CKKSAccountStateTracker(self.manager.cloudKitContainer,
+ nsnotificationCenterClass: FakeNSNotificationCenter.self as CKKSNSNotificationCenter.Type)
+ self.injectedManager!.accountTracker = self.manager.accountStateTracker
+
+ self.manager.removeContext(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
+ self.restartCKKSViews()
+ self.cuttlefishContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
+
+ self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
+ self.assertConsidersSelfTrustedCachedAccountStatus(context: self.cuttlefishContext)
+ }
}
#endif // OCTAGON
machineID: "asdf",
bottleSalt: "123456789",
bottleID: UUID().uuidString,
- modelID: "asdf",
+ modelID: "iPhone9,1",
deviceName: "asdf",
serialNumber: "1234",
osVersion: "asdf",
policyVersion: nil,
policySecrets: nil,
signingPrivKeyPersistentRef: nil,
- encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+ encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
XCTAssertNil(error, "Should be no error preparing identity")
XCTAssertNotNil(peerID, "Should be a peer ID")
XCTAssertNotNil(permanentInfo, "Should have a permenent info")
XCTAssertNoThrow(try account.saveToKeychain(forContainer: containerName, contextID: contextName), "Should be no error saving fake account metadata")
- OctagonInitialize()
+ self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
#if !os(tvOS)
- XCTAssertTrue(self.cuttlefishContext.postedRepairCFU, "should have posted an repair CFU")
+ XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "should have posted an repair CFU")
#else
// Apple TV should not post a CFU, as there's no peers to join
- XCTAssertFalse(self.cuttlefishContext.postedRepairCFU, "appleTV should not have posted a repair CFU")
+ XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "appleTV should not have posted a repair CFU")
#endif
}
machineID: "asdf",
bottleSalt: "123456789",
bottleID: UUID().uuidString,
- modelID: "asdf",
+ modelID: "iPhone9,1",
deviceName: "asdf",
serialNumber: "1234",
osVersion: "asdf",
policyVersion: nil,
policySecrets: nil,
signingPrivKeyPersistentRef: nil,
- encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+ encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
XCTAssertNil(error, "Should be no error preparing identity")
XCTAssertNotNil(peerID, "Should be a peer ID")
XCTAssertNotNil(permanentInfo, "Should have a permenent info")
XCTAssertNoThrow(try account.saveToKeychain(forContainer: containerName, contextID: contextName), "Should be no error saving fake account metadata")
- OctagonInitialize()
+ self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
// Since SOS isn't around to help, Octagon should post a CFU
#if os(tvOS)
- XCTAssertEqual(self.cuttlefishContext.postedRepairCFU, false, "Should not have posted a CFU on aTV (due to having no peers to join)")
+ XCTAssertEqual(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), false, "Should not have posted a CFU on aTV (due to having no peers to join)")
#else
- XCTAssertTrue(self.cuttlefishContext.postedRepairCFU, "should have posted an repair CFU, as SOS can't help")
+ XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "should have posted an repair CFU, as SOS can't help")
#endif
}
func testAttemptedJoinNotAttemptedStateSOSError() throws {
self.startCKAccountStatusMock()
+ // Note that some errors mean "out of circle", so use NotReady here to avoid that
self.mockSOSAdapter.sosEnabled = true
self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCError)
+ self.mockSOSAdapter.circleStatusError = NSError(domain: kSOSErrorDomain as String, code: kSOSErrorNotReady, userInfo: nil)
// Prepare an identity, then pretend like securityd thought it was in the right account
let containerName = OTCKContainerName
machineID: "asdf",
bottleSalt: "123456789",
bottleID: UUID().uuidString,
- modelID: "asdf",
+ modelID: "iPhone9,1",
deviceName: "asdf",
serialNumber: "1234",
osVersion: "asdf",
policyVersion: nil,
policySecrets: nil,
signingPrivKeyPersistentRef: nil,
- encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+ encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
XCTAssertNil(error, "Should be no error preparing identity")
XCTAssertNotNil(peerID, "Should be a peer ID")
XCTAssertNotNil(permanentInfo, "Should have a permenent info")
XCTAssertNoThrow(try account.saveToKeychain(forContainer: containerName, contextID: contextName), "Should be no error saving fake account metadata")
- OctagonInitialize()
+ self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
// Since SOS is in 'error', octagon shouldn't post until SOS can say y/n
- XCTAssertFalse(self.cuttlefishContext.postedRepairCFU, "should NOT have posted an repair CFU")
+ XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "should NOT have posted an repair CFU")
}
func testAttemptedJoinNotAttemptedStateSOSDisabled() throws {
// No need to mock not joining; Octagon won't have attempted a join if we just start it
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
#if !os(tvOS)
- XCTAssertTrue(self.cuttlefishContext.postedRepairCFU, "should have posted an repair CFU, as SOS is disabled")
+ XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "should have posted an repair CFU, as SOS is disabled")
#else
// Apple TV should not post a CFU, as there's no peers to join
- XCTAssertFalse(self.cuttlefishContext.postedRepairCFU, "appleTV should not have posted a repair CFU")
+ XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "appleTV should not have posted a repair CFU")
#endif
}
machineID: "asdf",
bottleSalt: "123456789",
bottleID: UUID().uuidString,
- modelID: "asdf",
+ modelID: "iPhone9,1",
deviceName: "asdf",
serialNumber: "1234",
osVersion: "asdf",
policyVersion: nil,
policySecrets: nil,
signingPrivKeyPersistentRef: nil,
- encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+ encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
XCTAssertNil(error, "Should be no error preparing identity")
XCTAssertNotNil(peerID, "Should be a peer ID")
XCTAssertNotNil(permanentInfo, "Should have a permenent info")
XCTAssertNoThrow(try account.saveToKeychain(forContainer: containerName, contextID: contextName), "Should be no error saving fake account metadata")
- OctagonInitialize()
+ self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
#if !os(tvOS)
- XCTAssertTrue(self.cuttlefishContext.postedRepairCFU, "should have posted an repair CFU")
+ XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "should have posted an repair CFU")
#else
// Apple TV should not post a CFU, as there's no peers to join
- XCTAssertFalse(self.cuttlefishContext.postedRepairCFU, "appleTV should not have posted a repair CFU")
+ XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "appleTV should not have posted a repair CFU")
#endif
}
self.mockSOSAdapter.sosEnabled = false
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
// Apple TV should not post a CFU, as there's no peers to join
- XCTAssertFalse(self.cuttlefishContext.postedRepairCFU, "appleTV should not have posted a repair CFU")
+ XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "appleTV should not have posted a repair CFU")
// Now, an iphone appears!
let iphone = self.manager.context(forContainerName: OTCKContainerName,
// The TV should now post a CFU, as there's an iphone that can repair it
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
- XCTAssertTrue(self.cuttlefishContext.postedRepairCFU, "appleTV should have posted a repair CFU")
+ XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "appleTV should have posted a repair CFU")
}
func testDontPostCFUWhenApprovalIncapablePeerJoins() throws {
self.mockSOSAdapter.sosEnabled = false
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
// Apple TV should not post a CFU, as there's no peers to join
- XCTAssertFalse(self.cuttlefishContext.postedRepairCFU, "appleTV should not have posted a repair CFU")
+ XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "appleTV should not have posted a repair CFU")
// Now, a mac appears! macs cannot fix apple TVs.
let mac = self.manager.context(forContainerName: OTCKContainerName,
// The TV should not post a CFU, as there's still no iPhone to repair it
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
- XCTAssertFalse(self.cuttlefishContext.postedRepairCFU, "appleTV should not have posted a repair CFU; no devices present can repair it")
+ XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "appleTV should not have posted a repair CFU; no devices present can repair it")
}
func testDontPostCFUWhenCapablePeersAreUntrusted() throws {
"iphone should distrust itself")
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
// Ensure that the aTV has fetched properly
self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
// Apple TV should not post a CFU, as the only iPhone around is untrusted
- XCTAssertFalse(self.cuttlefishContext.postedRepairCFU, "appleTV should not have posted a repair CFU")
+ XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "appleTV should not have posted a repair CFU")
// Another iPhone resets the world
let iphone2 = self.manager.context(forContainerName: OTCKContainerName,
// The aTV is notified, and now posts a CFU
self.sendContainerChangeWaitForUntrustedFetch(context: self.cuttlefishContext)
- XCTAssertTrue(self.cuttlefishContext.postedRepairCFU, "appleTV should have posted a repair CFU")
+ XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "appleTV should have posted a repair CFU")
}
#endif
self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCNotInCircle)
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
#if os(tvOS)
- XCTAssertEqual(self.cuttlefishContext.postedRepairCFU, false, "Should not have posted a CFU on aTV (due to having no peers to join)")
+ XCTAssertEqual(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), false, "Should not have posted a CFU on aTV (due to having no peers to join)")
#else
- XCTAssertTrue(self.cuttlefishContext.postedRepairCFU, "should have posted an repair CFU, as SOS can't help")
+ XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "should have posted an repair CFU, as SOS can't help")
#endif
}
}
self.startCKAccountStatusMock()
self.mockAuthKit.excludeDevices.formUnion(self.mockAuthKit.currentDeviceList())
+ self.mockAuthKit.isDemoAccount = true
XCTAssertTrue(self.mockAuthKit.currentDeviceList().isEmpty, "should have zero devices")
self.cuttlefishContext.startOctagonStateMachine()
let container = try self.tphClient.getContainer(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID)
container.moc.performAndWait {
var foundPeer2 = false
- for machinemo in container.containerMO.machines as? Set<MachineMO> ?? Set() {
- if machinemo.machineID == self.mockAuthKit2.currentMachineID {
+ for machinemo in container.containerMO.machines as? Set<MachineMO> ?? Set()
+ where machinemo.machineID == self.mockAuthKit2.currentMachineID {
foundPeer2 = true
//
machinemo.modified = Date(timeIntervalSinceNow: -60 * 60 * TimeInterval(72))
XCTAssertEqual(machinemo.status, Int64(TPMachineIDStatus.unknown.rawValue), "peer2's MID entry should be 'unknown'")
}
- }
XCTAssertTrue(foundPeer2, "Should have found an entry for peer2")
try! container.moc.save()
XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer1ID, opinion: .trusts, target: peer2ID)),
"peer 1 should trust peer 2 after update")
}
+
+ func testDemoAccountBypassIDMSTrustedDeviceList() throws {
+ // Check that we can bypass IdMS trusted device list (needed for demo accounts)
+
+ self.startCKAccountStatusMock()
+
+ self.mockAuthKit.excludeDevices.formUnion(self.mockAuthKit.currentDeviceList())
+ self.mockAuthKit.isDemoAccount = true
+ XCTAssertTrue(self.mockAuthKit.currentDeviceList().isEmpty, "should have zero devices")
+
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+
+ do {
+ let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, resetReason: .testGenerated)
+ XCTAssertNotNil(clique, "Clique should not be nil")
+ } catch {
+ XCTFail("Shouldn't have errored making new friends: \(error)")
+ }
+
+ // Now, we should be in 'ready'
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
+ self.assertConsidersSelfTrustedCachedAccountStatus(context: self.cuttlefishContext)
+
+ // and all subCKKSes should enter ready...
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ self.verifyDatabaseMocks()
+
+ self.assertSelfTLKSharesInCloudKit(context: self.cuttlefishContext)
+
+ // And we haven't helpfully added anything to the MID list
+ self.assertMIDList(context: self.cuttlefishContext, allowed: Set(), disallowed: Set(), unknown: Set())
+ }
+
+ func testDemoAccountTrustPeerWhenMissingFromDeviceList() throws {
+ self.startCKAccountStatusMock()
+
+ self.mockAuthKit.otherDevices.removeAll()
+ self.mockAuthKit.isDemoAccount = true
+ self.mockAuthKit2.isDemoAccount = true
+
+ XCTAssertEqual(self.mockAuthKit.currentDeviceList(), Set([self.mockAuthKit.currentMachineID]), "AuthKit should have exactly one device on the list")
+ XCTAssertFalse(self.mockAuthKit.currentDeviceList().contains(self.mockAuthKit2.currentMachineID), "AuthKit should not already have device 2 on the list")
+
+ let peer1ID = self.assertResetAndBecomeTrustedInDefaultContext()
+
+ let joiningContext = self.makeInitiatorContext(contextID: "joiner", authKitAdapter: self.mockAuthKit2)
+ let peer2ID = self.assertJoinViaEscrowRecovery(joiningContext: joiningContext, sponsor: self.cuttlefishContext)
+
+ // Now, tell peer1 about the change. It will trust the peer, despite it missing from the list, and upload some TLK shares
+ self.assertAllCKKSViewsUpload(tlkShares: 1)
+ self.sendContainerChangeWaitForFetch(context: self.cuttlefishContext)
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ self.verifyDatabaseMocks()
+
+ XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer1ID, opinion: .trusts, target: peer2ID)),
+ "peer 1 should trust peer 2 after update")
+ self.assertMIDList(context: self.cuttlefishContext,
+ allowed: self.mockAuthKit.currentDeviceList(),
+ disallowed: Set(),
+ unknown: Set())
+
+ // On a follow-up update, peer1 should _not_ hit IDMS, even though there's an unknown peer ID in its DB
+ let currentCount = self.mockAuthKit.fetchInvocations
+ self.sendContainerChangeWaitForFetchForStates(context: self.cuttlefishContext, states: [OctagonStateReadyUpdated, OctagonStateReady])
+ self.assertMIDList(context: self.cuttlefishContext,
+ allowed: self.mockAuthKit.currentDeviceList(),
+ disallowed: Set(),
+ unknown: Set())
+ XCTAssertEqual(currentCount, self.mockAuthKit.fetchInvocations, "Receving a push while having an unknown peer MID should not cause an AuthKit fetch")
+
+ ////////
+
+ // Then peer2 arrives on the device list. Peer 1 should update its dynamic info to no longer have a disposition for peer2.
+ let updateTrustExpectation = self.expectation(description: "updateTrust")
+ self.fakeCuttlefishServer.updateListener = { request in
+ XCTAssertTrue(request.hasDynamicInfoAndSig, "updateTrust request should have a dynamic info")
+ let newDynamicInfo = TPPeerDynamicInfo(data: request.dynamicInfoAndSig.peerDynamicInfo,
+ sig: request.dynamicInfoAndSig.sig)
+ XCTAssertNotNil(newDynamicInfo, "should be able to make a dynamic info from protobuf")
+
+ // TODO: swift refuses to see the dispositions object on newDynamicInfo; ah well
+ updateTrustExpectation.fulfill()
+ return nil
+ }
+
+ self.mockAuthKit.addAndSendNotification(machineID: try! self.mockAuthKit2.machineID())
+
+ self.wait(for: [updateTrustExpectation], timeout: 10)
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+
+ XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer1ID, opinion: .trusts, target: peer2ID)),
+ "peer 1 should trust peer 2 after update")
+ }
+
}
#endif
class OctagonErrorHandlingTests: OctagonTestsBase {
+ func testEstablishFailedError() throws {
+ self.startCKAccountStatusMock()
+
+ let establishExpectation = self.expectation(description: "establishExpectation")
+
+ self.fakeCuttlefishServer.establishListener = { [unowned self] request in
+ self.fakeCuttlefishServer.establishListener = nil
+
+ self.fakeCuttlefishServer.establish(request) {
+ response, error in
+ XCTAssertNil(error, "should be no error from establish")
+ XCTAssertNotNil(response, "should get a response from establish")
+ // drop the response on the floor
+ }
+
+ establishExpectation.fulfill()
+
+ return FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .establishFailed)
+ }
+
+ _ = self.assertResetAndBecomeTrustedInDefaultContext()
+ self.wait(for: [establishExpectation], timeout: 10)
+ }
+
func testRecoverFromImmediateTimeoutDuringEstablish() throws {
self.startCKAccountStatusMock()
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.aksLockState = true
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
do {
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
do {
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
do {
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
do {
func testPreapprovedPushWhileLocked() throws {
// Peer 1 becomes SOS+Octagon
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
- // Peer 2 attempts to join via preapprovalh
+ // Peer 2 attempts to join via preapproval
let peer2SOSMockPeer = self.createSOSPeer(peerID: "peer2ID")
let peer2contextID = "peer2"
let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: peer2SOSMockPeer, trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
self.wait(for: [updateTrustExpectation], timeout: 100)
self.fakeCuttlefishServer.updateListener = nil
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+
// Now, peer 2 should lock and receive an Octagon push
self.aksLockState = true
self.lockStateTracker.recheck()
- // Now, peer2 should receive an Octagon push, try to realize it is preapproved, and get stuck
+ // Now, peer2 should receive an Octagon push, try to realize it is preapproved, and get stuck waiting for an unlock
+ let flagCondition = peer2.stateMachine.flags.condition(forFlag: OctagonFlagCuttlefishNotification)
+
self.sendContainerChange(context: peer2)
self.assertEnters(context: peer2, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
- sleep(1)
- XCTAssertTrue(peer2.stateMachine.possiblePendingFlags().contains(OctagonFlagCuttlefishNotification), "Should have recd_push pending flag")
+ // The pending flag should become a real flag after the lock state changes
+ // But it should not fire for a bit
+ XCTAssertNotEqual(0, flagCondition.wait(1 * NSEC_PER_SEC), "Cuttlefish Notification flag should not be removed while locked")
self.aksLockState = false
self.lockStateTracker.recheck()
- XCTAssertEqual(self.cuttlefishContext.stateMachine.possiblePendingFlags(), [], "Should have 0 pending flags")
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ XCTAssertEqual(0, flagCondition.wait(10 * NSEC_PER_SEC), "Cuttlefish Notification flag should be removed")
+ XCTAssertEqual(peer2.stateMachine.possiblePendingFlags(), [], "Should have 0 pending flags")
self.assertEnters(context: peer2, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
}
func testReceiveMachineListUpdateWhileReadyAndLocked() throws {
// Peer 1 becomes SOS+Octagon
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
bNewOTCliqueContext.otControl = self.otcliqueContext.otControl
bNewOTCliqueContext.sbd = OTMockSecureBackup(bottleID: bottle.bottleID, entropy: entropy!)
- let deviceBmockAuthKit = OTMockAuthKitAdapter(altDSID: self.otcliqueContext.altDSID,
+ let deviceBmockAuthKit = OTMockAuthKitAdapter(altDSID: self.otcliqueContext.altDSID!,
machineID: "b-machine-id",
otherDevices: [self.mockAuthKit.currentMachineID])
let bRestoreContext = self.manager.context(forContainerName: OTCKContainerName,
- contextID: bNewOTCliqueContext.context!,
+ contextID: bNewOTCliqueContext.context,
sosAdapter: OTSOSMissingAdapter(),
authKitAdapter: deviceBmockAuthKit,
lockStateTracker: self.lockStateTracker,
}
func testCKKSResetRecoverFromCKKSConflict() throws {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putFakeDeviceStatusesInCloudKit()
// But do NOT add them to the keychain
// CKKS should get stuck in waitfortlk
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
do {
var tlkUUIDs: [CKRecordZone.ID: String] = [:]
self.silentFetchesAllowed = false
- self.expectCKFetchAndRun(beforeFinished: {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+ self.expectCKFetchAndRun {
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putFakeDeviceStatusesInCloudKit()
self.silentFetchesAllowed = true
- // Use the commented version below when multi-zone support is readded to the tets
- tlkUUIDs[self.manateeZoneID!] = (self.keys![self.manateeZoneID!] as? ZoneKeys)?.tlk?.uuid
- /*
for zoneID in self.ckksZones {
tlkUUIDs[zoneID as! CKRecordZone.ID] = (self.keys![zoneID] as? ZoneKeys)?.tlk?.uuid
}
- */
- })
+ }
let resetExepctation = self.expectation(description: "reset callback is called")
- self.cuttlefishContext.viewManager!.rpcResetCloudKit(nil, reason: "unit-test") {
- error in
+ self.cuttlefishContext.viewManager!.rpcResetCloudKit(nil, reason: "unit-test") { error in
XCTAssertNil(error, "should be no error resetting cloudkit")
resetExepctation.fulfill()
}
#if OCTAGON
-@objcMembers class OctagonEscrowRecoveryTests: OctagonTestsBase {
+@objcMembers
+class OctagonEscrowRecoveryTests: OctagonTestsBase {
override func setUp() {
super.setUp()
}
ckacctinfo.accountPartition = .production
bottlerContext.cloudkitAccountStateChange(nil, to: ckacctinfo)
+ XCTAssertNoThrow(try bottlerContext.setCDPEnabled())
self.assertEnters(context: bottlerContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
XCTAssertNotNil(entropy, "entropy should not be nil")
// Fake that this peer also created some TLKShares for itself
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- try self.putSelfTLKShareInCloudKit(context: bottlerContext, zoneID: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ try self.putSelfTLKSharesInCloudKit(context: bottlerContext)
let bottle = self.fakeCuttlefishServer.state.bottles[0]
self.holdCloudKitFetches()
// Note: CKKS will want to upload a TLKShare for its self
- self.expectCKModifyKeyRecords(0, currentKeyPointerRecords: 0, tlkShareRecords: 1, zoneID: self.manateeZoneID)
+ self.assertAllCKKSViewsUpload(tlkShares: 1)
// Before you call joinWithBottle, you need to call fetchViableBottles.
let fetchViableExpectation = self.expectation(description: "fetchViableBottles callback occurs")
self.wait(for: [fetchViableExpectation], timeout: 10)
let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
- self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+ self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
XCTAssertNil(error, "error should be nil")
joinWithBottleExpectation.fulfill()
}
self.wait(for: [joinWithBottleExpectation], timeout: 100)
let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
dumpCallback.fulfill()
let initiatorContextID = "initiator-context-id"
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
initiatorContext.startOctagonStateMachine()
let restoreExpectation = self.expectation(description: "restore returns")
- self.manager!.restore(OTCKContainerName, contextID: initiatorContextID, bottleSalt: self.otcliqueContext.altDSID, entropy: entropy!, bottleID: bottle.bottleID) { error in
+ self.manager!.restore(OTCKContainerName, contextID: initiatorContextID, bottleSalt: self.otcliqueContext.altDSID!, entropy: entropy!, bottleID: bottle.bottleID) { error in
XCTAssertNil(error, "error should be nil")
restoreExpectation.fulfill()
}
self.assertEnters(context: initiatorContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
ckacctinfo.accountPartition = .production
bottlerContext.cloudkitAccountStateChange(nil, to: ckacctinfo)
+ XCTAssertNoThrow(try bottlerContext.setCDPEnabled())
self.assertEnters(context: bottlerContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
// During the join, there's a CKKS key race
self.silentFetchesAllowed = false
- self.expectCKFetchAndRun(beforeFinished: {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+ self.expectCKFetchAndRun {
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putFakeDeviceStatusesInCloudKit()
self.silentFetchesAllowed = true
- })
+ }
self.cuttlefishContext.startOctagonStateMachine()
self.startCKAccountStatusMock()
self.wait(for: [fetchViableExpectation], timeout: 10)
let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
- self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+ self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
XCTAssertNil(error, "error should be nil")
joinWithBottleExpectation.fulfill()
}
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
differentDevice.startOctagonStateMachine()
differentDevice.join(withBottle: bottle.bottleID,
entropy: entropy!,
- bottleSalt: self.otcliqueContext.altDSID) { error in
+ bottleSalt: self.otcliqueContext.altDSID!) { error in
XCTAssertNil(error, "error should be nil")
differentRestoreExpectation.fulfill()
}
let restoreExpectation = self.expectation(description: "restore returns")
restoreContext.join(withBottle: bottle.bottleID,
entropy: entropy!,
- bottleSalt: self.otcliqueContext.altDSID) { error in
+ bottleSalt: self.otcliqueContext.altDSID!) { error in
XCTAssertNil(error, "error should be nil")
restoreExpectation.fulfill()
}
let initiatorContextID = "initiator-context-id"
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
initiatorContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try initiatorContext.setCDPEnabled())
self.assertEnters(context: initiatorContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let restoreExpectation = self.expectation(description: "restore returns")
- self.manager!.restore(OTCKContainerName, contextID: initiatorContextID, bottleSalt: self.otcliqueContext.altDSID, entropy: entropy!, bottleID: bottle.bottleID) { error in
+ self.manager!.restore(OTCKContainerName, contextID: initiatorContextID, bottleSalt: self.otcliqueContext.altDSID!, entropy: entropy!, bottleID: bottle.bottleID) { error in
XCTAssertNil(error, "error should be nil")
restoreExpectation.fulfill()
}
self.wait(for: [restoreExpectation], timeout: 10)
let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
let initiatorContextID = "initiator-context-id"
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
let restoreExpectation = self.expectation(description: "restore returns")
- self.manager!.restore(OTCKContainerName, contextID: initiatorContextID, bottleSalt: self.otcliqueContext.altDSID, entropy: entropy!, bottleID: "bad escrow record ID") { error in
+ self.manager!.restore(OTCKContainerName, contextID: initiatorContextID, bottleSalt: self.otcliqueContext.altDSID!, entropy: entropy!, bottleID: "bad escrow record ID") { error in
XCTAssertNotNil(error, "error should not be nil")
restoreExpectation.fulfill()
}
self.assertEnters(context: initiatorContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 1, "should be 1 peer ids")
// We will upload a new TLK for the new peer
self.assertAllCKKSViewsUpload(tlkShares: 1)
self.sendContainerChangeWaitForFetch(context: self.cuttlefishContext)
- assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ self.verifyDatabaseMocks()
self.sendContainerChangeWaitForFetch(context: initiatorContext)
XCTAssertEqual(bottleIDs.partialRecoveryBottleIDs.count, 0, "partialRecoveryBottleIDs should be empty")
let dumpExpectation = self.expectation(description: "dump callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, error in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, error in
XCTAssertNil(error, "Should be no error dumping data")
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
let peerID = egoSelf!["peerID"] as? String
XCTAssertNotNil(peerID, "peerID should not be nil")
self.wait(for: [dumpExpectation], timeout: 10)
self.otControlCLI.status(OTCKContainerName,
- context: newOTCliqueContext.context!,
+ context: newOTCliqueContext.context,
json: false)
}
let initiatorContextID = "initiator-context-id"
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
initiatorContext.startOctagonStateMachine()
let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
- initiatorContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+ initiatorContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
XCTAssertNotNil(error, "error should not be nil")
joinWithBottleExpectation.fulfill()
}
let initiatorContextID = "initiator-context-id"
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
initiatorContext.startOctagonStateMachine()
let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
- initiatorContext.join(withBottle: "sos peer id", entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+ initiatorContext.join(withBottle: "sos peer id", entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
XCTAssertNotNil(error, "error should be nil")
joinWithBottleExpectation.fulfill()
}
let initiatorContextID = "initiator-context-id"
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
- initiatorContext.join(withBottle: bottle.bottleID, entropy: Data(count: 72), bottleSalt: self.otcliqueContext.altDSID) { error in
+ initiatorContext.join(withBottle: bottle.bottleID, entropy: Data(count: 72), bottleSalt: self.otcliqueContext.altDSID!) { error in
XCTAssertNotNil(error, "error should not be nil, when entropy is missing")
joinWithBottleExpectation.fulfill()
}
let initiatorContextID = "initiator-context-id"
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
bNewOTCliqueContext.otControl = self.otcliqueContext.otControl
bNewOTCliqueContext.sbd = OTMockSecureBackup(bottleID: bottle.bottleID, entropy: entropy!)
- let deviceBmockAuthKit = OTMockAuthKitAdapter(altDSID: self.otcliqueContext.altDSID,
+ let deviceBmockAuthKit = OTMockAuthKitAdapter(altDSID: self.otcliqueContext.altDSID!,
machineID: "b-machine-id",
otherDevices: [self.mockAuthKit.currentMachineID])
let bRestoreContext = self.manager.context(forContainerName: OTCKContainerName,
- contextID: bNewOTCliqueContext.context!,
+ contextID: bNewOTCliqueContext.context,
sosAdapter: OTSOSMissingAdapter(),
authKitAdapter: deviceBmockAuthKit,
lockStateTracker: self.lockStateTracker,
// And introduce C, which will kick out A
// During the next sign in, the machine ID list has changed to just the new one
- let restoremockAuthKit = OTMockAuthKitAdapter(altDSID: self.otcliqueContext.altDSID,
+ let restoremockAuthKit = OTMockAuthKitAdapter(altDSID: self.otcliqueContext.altDSID!,
machineID: "c-machine-id",
otherDevices: [self.mockAuthKit.currentMachineID, deviceBmockAuthKit.currentMachineID])
let restoreContext = self.manager.context(forContainerName: OTCKContainerName,
self.assertConsidersSelfTrusted(context: restoreContext)
let restoreDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: newOTCliqueContext.context!) {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: newOTCliqueContext.context) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 3, "should be 3 peer id included")
ckacctinfo.accountPartition = .production
bottlerContext.cloudkitAccountStateChange(nil, to: ckacctinfo)
+ XCTAssertNoThrow(try bottlerContext.setCDPEnabled())
self.assertEnters(context: bottlerContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
XCTAssertNotNil(entropy, "entropy should not be nil")
// Fake that this peer also created some TLKShares for itself
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- try self.putSelfTLKShareInCloudKit(context: bottlerContext, zoneID: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ try self.putSelfTLKSharesInCloudKit(context: bottlerContext)
let bottle = self.fakeCuttlefishServer.state.bottles[0]
self.holdCloudKitFetches()
// Note: CKKS will want to upload a TLKShare for its self
- self.expectCKModifyKeyRecords(0, currentKeyPointerRecords: 0, tlkShareRecords: 1, zoneID: self.manateeZoneID)
+ self.assertAllCKKSViewsUpload(tlkShares: 1)
let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
- self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+ self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
XCTAssertNil(error, "error should be nil")
joinWithBottleExpectation.fulfill()
}
self.wait(for: [joinWithBottleExpectation], timeout: 100)
let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
dumpCallback.fulfill()
ckacctinfo.accountPartition = .production
bottlerContext.cloudkitAccountStateChange(nil, to: ckacctinfo)
+ XCTAssertNoThrow(try bottlerContext.setCDPEnabled())
self.assertEnters(context: bottlerContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
XCTAssertNotNil(entropy, "entropy should not be nil")
// Fake that this peer also created some TLKShares for itself
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- try self.putSelfTLKShareInCloudKit(context: bottlerContext, zoneID: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ try self.putSelfTLKSharesInCloudKit(context: bottlerContext)
let bottle = self.fakeCuttlefishServer.state.bottles[0]
self.cuttlefishContext.startOctagonStateMachine()
self.startCKAccountStatusMock()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
// Try to enforce that that CKKS doesn't know about the key hierarchy until Octagon asks it
self.holdCloudKitFetches()
// Note: CKKS will want to upload a TLKShare for its self
- self.expectCKModifyKeyRecords(0, currentKeyPointerRecords: 0, tlkShareRecords: 1, zoneID: self.manateeZoneID)
+ self.assertAllCKKSViewsUpload(tlkShares: 1)
let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
- self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+ self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
XCTAssertNil(error, "error should be nil")
joinWithBottleExpectation.fulfill()
}
self.wait(for: [joinWithBottleExpectation], timeout: 100)
let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
dumpCallback.fulfill()
ckacctinfo.accountPartition = .production
bottlerContext.cloudkitAccountStateChange(nil, to: ckacctinfo)
+ XCTAssertNoThrow(try bottlerContext.setCDPEnabled())
self.assertEnters(context: bottlerContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
XCTAssertNotNil(entropy, "entropy should not be nil")
// Fake that this peer also created some TLKShares for itself
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- try self.putSelfTLKShareInCloudKit(context: bottlerContext, zoneID: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ try self.putSelfTLKSharesInCloudKit(context: bottlerContext)
let bottle = self.fakeCuttlefishServer.state.bottles[0]
self.holdCloudKitFetches()
// Note: CKKS will want to upload a TLKShare for its self
- self.expectCKModifyKeyRecords(0, currentKeyPointerRecords: 0, tlkShareRecords: 1, zoneID: self.manateeZoneID)
+ self.assertAllCKKSViewsUpload(tlkShares: 1)
let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
- self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+ self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
XCTAssertNil(error, "error should be nil")
joinWithBottleExpectation.fulfill()
}
var egoPeerID: String?
let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
egoPeerID = egoSelf!["peerID"] as? String
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
dumpCallback.fulfill()
self.wait(for: [FetchAllViableBottles], timeout: 10)
self.wait(for: [fetchUnCachedViableBottlesExpectation], timeout: 10)
}
+
+ func testRecoverTLKSharesSendByPeers() throws {
+ // First, set up the world: two peers, one of which has sent TLKs tto itself and the other
+ let noSelfSharesContext = self.makeInitiatorContext(contextID: "noShares", authKitAdapter: self.mockAuthKit2)
+ let allSharesContext = self.makeInitiatorContext(contextID: "allShares", authKitAdapter: self.mockAuthKit3)
+
+ self.startCKAccountStatusMock()
+ let noSelfSharesPeerID = self.assertResetAndBecomeTrusted(context: noSelfSharesContext)
+ let allSharesPeerID = self.assertJoinViaEscrowRecovery(joiningContext: allSharesContext, sponsor: noSelfSharesContext)
+
+ self.sendContainerChangeWaitForFetch(context: noSelfSharesContext)
+ self.assertEnters(context: noSelfSharesContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+
+ XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: noSelfSharesPeerID, opinion: .trusts, target: allSharesPeerID)),
+ "noShares should trust allShares")
+ XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: noSelfSharesPeerID, opinion: .trusts, target: noSelfSharesPeerID)),
+ "No shares should trust itself")
+
+ self.putFakeKeyHierarchiesInCloudKit()
+ try self.putSelfTLKSharesInCloudKit(context: allSharesContext)
+ try self.putAllTLKSharesInCloudKit(to: noSelfSharesContext, from: allSharesContext)
+
+ try self.ckksZones.forEach { zone in
+ XCTAssertFalse(try self.tlkShareInCloudKit(receiverPeerID: noSelfSharesPeerID, senderPeerID: noSelfSharesPeerID, zoneID: zone as! CKRecordZone.ID), "Should not have self shares for noSelfShares")
+ XCTAssertTrue(try self.tlkShareInCloudKit(receiverPeerID: noSelfSharesPeerID, senderPeerID: allSharesPeerID, zoneID: zone as! CKRecordZone.ID), "Should have a share for noSelfShares from allShares")
+ }
+
+ // Now, recover from noSelfShares
+ self.assertAllCKKSViewsUpload(tlkShares: 1)
+ self.assertJoinViaEscrowRecovery(joiningContext: self.cuttlefishContext, sponsor: noSelfSharesContext)
+ // And CKKS should enter ready!
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ self.verifyDatabaseMocks()
+ }
}
#endif
--- /dev/null
+#if OCTAGON
+
+import Foundation
+
+class OctagonForwardCompatibilityTests: OctagonTestsBase {
+ func testApprovePeerWithNewPolicy() throws {
+ self.startCKAccountStatusMock()
+ let peer1ID = self.assertResetAndBecomeTrustedInDefaultContext()
+
+ // Now, we'll approve a new peer with a new policy! First, make that new policy.
+ let currentPolicyOptional = builtInPolicyDocuments().filter { $0.version.versionNumber == prevailingPolicyVersion.versionNumber }.first
+ XCTAssertNotNil(currentPolicyOptional, "Should have one current policy")
+ let currentPolicy = currentPolicyOptional!
+
+ let newPolicy = currentPolicy.clone(withVersionNumber: currentPolicy.version.versionNumber + 1)!
+ self.fakeCuttlefishServer.policyOverlay.append(newPolicy)
+
+ let peer2ContextID = "asdf"
+
+ // Assist the other client here: it'll likely already have the policy built-in
+ let fetchExpectation = self.expectation(description: "fetch callback occurs")
+ self.tphClient.fetchPolicyDocuments(withContainer: OTCKContainerName,
+ context: peer2ContextID,
+ versions: Set([newPolicy.version])) { _, error in
+ XCTAssertNil(error, "Should have no error")
+ fetchExpectation.fulfill()
+ }
+ self.wait(for: [fetchExpectation], timeout: 10)
+
+ var peer2ID: String = "not initialized"
+
+ let prepareExpectation = self.expectation(description: "prepare callback occurs")
+ let vouchExpectation = self.expectation(description: "vouch callback occurs")
+ let joinExpectation = self.expectation(description: "join callback occurs")
+ let serverJoinExpectation = self.expectation(description: "joinWithVoucher is called")
+
+ self.tphClient.prepare(withContainer: OTCKContainerName,
+ context: peer2ContextID,
+ epoch: 1,
+ machineID: self.mockAuthKit2.currentMachineID,
+ bottleSalt: self.mockAuthKit2.altDSID!,
+ bottleID: "why-is-this-nonnil",
+ modelID: self.mockDeviceInfo.modelID(),
+ deviceName: "new-policy-peer",
+ serialNumber: "1234",
+ osVersion: "something",
+ policyVersion: newPolicy.version,
+ policySecrets: nil,
+ signingPrivKeyPersistentRef: nil,
+ encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
+ XCTAssertNil(error, "Should be no error preparing the second peer")
+ XCTAssertNotNil(peerID, "Should have a peerID")
+ peer2ID = peerID!
+
+ XCTAssertNotNil(stableInfo, "Should have a stable info")
+ XCTAssertNotNil(stableInfoSig, "Should have a stable info signature")
+
+ let newStableInfo = TPPeerStableInfo(data: stableInfo!, sig: stableInfoSig!)
+ XCTAssertNotNil(newStableInfo, "should be able to make a stableInfo info from protobuf")
+
+ XCTAssertEqual(newStableInfo?.frozenPolicyVersion, frozenPolicyVersion, "Frozen policy version in new identity should match frozen policy version")
+ XCTAssertEqual(newStableInfo?.flexiblePolicyVersion, newPolicy.version, "Flexible policy version in new identity should match new policy version")
+
+ self.tphClient.vouch(withContainer: self.cuttlefishContext.containerName,
+ context: self.cuttlefishContext.contextID,
+ peerID: peerID!,
+ permanentInfo: permanentInfo!,
+ permanentInfoSig: permanentInfoSig!,
+ stableInfo: stableInfo!,
+ stableInfoSig: stableInfoSig!,
+ ckksKeys: []) { voucher, voucherSig, error in
+ XCTAssertNil(error, "Should be no error vouching")
+ XCTAssertNotNil(voucher, "Should have a voucher")
+ XCTAssertNotNil(voucherSig, "Should have a voucher signature")
+
+ self.fakeCuttlefishServer.joinListener = { joinRequest in
+ XCTAssertEqual(peer2ID, joinRequest.peer.peerID, "joinWithVoucher request should be for peer 2")
+ XCTAssertTrue(joinRequest.peer.hasStableInfoAndSig, "Joining peer should have a stable info")
+ let newStableInfo = joinRequest.peer.stableInfoAndSig.stableInfo()
+
+ XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Frozen policy version in new identity should match frozen policy version")
+ XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicy.version, "Flexible policy version in new identity should match new policy version (as provided by new peer)")
+
+ serverJoinExpectation.fulfill()
+ return nil
+ }
+
+ self.tphClient.join(withContainer: OTCKContainerName,
+ context: peer2ContextID,
+ voucherData: voucher!,
+ voucherSig: voucherSig!,
+ ckksKeys: [],
+ tlkShares: [],
+ preapprovedKeys: []) { peerID, _, _, _, error in
+ XCTAssertNil(error, "Should be no error joining")
+ XCTAssertNotNil(peerID, "Should have a peerID")
+ joinExpectation.fulfill()
+ }
+ vouchExpectation.fulfill()
+ }
+ prepareExpectation.fulfill()
+ }
+ self.wait(for: [prepareExpectation, vouchExpectation, joinExpectation, serverJoinExpectation], timeout: 10)
+
+ // Then, after the remote peer joins, the original peer should realize it trusts the new peer and update its own stableinfo to use the new policy
+ let updateTrustExpectation = self.expectation(description: "updateTrust")
+ self.fakeCuttlefishServer.updateListener = { request in
+ XCTAssertEqual(peer1ID, request.peerID, "updateTrust request should be for peer 1")
+ let newDynamicInfo = request.dynamicInfoAndSig.dynamicInfo()
+ XCTAssert(newDynamicInfo.includedPeerIDs.contains(peer2ID), "Peer1 should trust peer2")
+
+ let newStableInfo = request.stableInfoAndSig.stableInfo()
+
+ XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Policy version in peer should match frozen policy version")
+ XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicy.version, "Prevailing policy version in peer should match new policy version")
+
+ updateTrustExpectation.fulfill()
+ return nil
+ }
+
+ self.sendContainerChangeWaitForFetch(context: self.cuttlefishContext)
+ self.wait(for: [updateTrustExpectation], timeout: 10)
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ }
+
+ func testRejectVouchingForPeerWithUnknownNewPolicy() throws {
+ self.startCKAccountStatusMock()
+ _ = self.assertResetAndBecomeTrustedInDefaultContext()
+
+ // Now, a new peer joins with a policy we can't fetch
+ let currentPolicyOptional = builtInPolicyDocuments().filter { $0.version.versionNumber == prevailingPolicyVersion.versionNumber }.first
+ XCTAssertNotNil(currentPolicyOptional, "Should have one current policy")
+ let currentPolicy = currentPolicyOptional!
+
+ let newPolicy = currentPolicy.clone(withVersionNumber: currentPolicy.version.versionNumber + 1)!
+
+ let peer2ContextID = "asdf"
+
+ // Assist the other client here: it'll likely already have this built-in
+ self.fakeCuttlefishServer.policyOverlay.append(newPolicy)
+
+ let fetchExpectation = self.expectation(description: "fetch callback occurs")
+ self.tphClient.fetchPolicyDocuments(withContainer: OTCKContainerName,
+ context: peer2ContextID,
+ versions: Set([newPolicy.version])) { _, error in
+ XCTAssertNil(error, "Should have no error")
+ fetchExpectation.fulfill()
+ }
+ self.wait(for: [fetchExpectation], timeout: 10)
+
+ // Remove the policy, now that peer2 has it
+ self.fakeCuttlefishServer.policyOverlay.removeAll()
+
+ let prepareExpectation = self.expectation(description: "prepare callback occurs")
+ let vouchExpectation = self.expectation(description: "vouch callback occurs")
+
+ self.tphClient.prepare(withContainer: OTCKContainerName,
+ context: peer2ContextID,
+ epoch: 1,
+ machineID: self.mockAuthKit2.currentMachineID,
+ bottleSalt: self.mockAuthKit2.altDSID!,
+ bottleID: "why-is-this-nonnil",
+ modelID: self.mockDeviceInfo.modelID(),
+ deviceName: "new-policy-peer",
+ serialNumber: "1234",
+ osVersion: "something",
+ policyVersion: newPolicy.version,
+ policySecrets: nil,
+ signingPrivKeyPersistentRef: nil,
+ encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
+ XCTAssertNil(error, "Should be no error preparing the second peer")
+ XCTAssertNotNil(peerID, "Should have a peerID")
+
+ XCTAssertNotNil(stableInfo, "Should have a stable info")
+ XCTAssertNotNil(stableInfoSig, "Should have a stable info signature")
+
+ let newStableInfo = TPPeerStableInfo(data: stableInfo!, sig: stableInfoSig!)
+ XCTAssertNotNil(newStableInfo, "should be able to make a stableInfo info from protobuf")
+
+ XCTAssertEqual(newStableInfo?.frozenPolicyVersion, frozenPolicyVersion, "Frozen policy version in new identity should match frozen policy version")
+ XCTAssertEqual(newStableInfo?.flexiblePolicyVersion, newPolicy.version, "Flexible policy version in new identity should match new policy version")
+
+ self.tphClient.vouch(withContainer: self.cuttlefishContext.containerName,
+ context: self.cuttlefishContext.contextID,
+ peerID: peerID!,
+ permanentInfo: permanentInfo!,
+ permanentInfoSig: permanentInfoSig!,
+ stableInfo: stableInfo!,
+ stableInfoSig: stableInfoSig!,
+ ckksKeys: []) { voucher, voucherSig, error in
+ XCTAssertNotNil(error, "should be an error vouching for a peer with an unknown policy")
+ XCTAssertNil(voucher, "Should have no voucher")
+ XCTAssertNil(voucherSig, "Should have no voucher signature")
+
+ vouchExpectation.fulfill()
+ }
+ prepareExpectation.fulfill()
+ }
+ self.wait(for: [prepareExpectation, vouchExpectation], timeout: 10)
+ }
+
+ func testIgnoreAlreadyJoinedPeerWithUnknownNewPolicy() throws {
+ self.startCKAccountStatusMock()
+ let peer1ID = self.assertResetAndBecomeTrustedInDefaultContext()
+
+ // Now, a new peer joins with a policy we can't fetch
+ let currentPolicyOptional = builtInPolicyDocuments().filter { $0.version.versionNumber == prevailingPolicyVersion.versionNumber }.first
+ XCTAssertNotNil(currentPolicyOptional, "Should have one current policy")
+ let currentPolicy = currentPolicyOptional!
+
+ let newPolicy = currentPolicy.clone(withVersionNumber: currentPolicy.version.versionNumber + 1)!
+
+ let peer2ContextID = "asdf"
+
+ // Assist the other client here: it'll likely already have this built-in
+ self.fakeCuttlefishServer.policyOverlay.append(newPolicy)
+
+ let fetchExpectation = self.expectation(description: "fetch callback occurs")
+ self.tphClient.fetchPolicyDocuments(withContainer: OTCKContainerName,
+ context: peer2ContextID,
+ versions: Set([newPolicy.version])) { _, error in
+ XCTAssertNil(error, "Should have no error")
+ fetchExpectation.fulfill()
+ }
+ self.wait(for: [fetchExpectation], timeout: 10)
+
+ // Remove the policy, now that peer2 has it
+ self.fakeCuttlefishServer.policyOverlay.removeAll()
+
+ let joiningContext = self.makeInitiatorContext(contextID: peer2ContextID, authKitAdapter: self.mockAuthKit2)
+ joiningContext.policyOverride = newPolicy.version
+
+ let serverJoinExpectation = self.expectation(description: "peer2 joins successfully")
+ self.fakeCuttlefishServer.joinListener = { joinRequest in
+ XCTAssertTrue(joinRequest.peer.hasStableInfoAndSig, "Joining peer should have a stable info")
+ let newStableInfo = joinRequest.peer.stableInfoAndSig.stableInfo()
+
+ XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Frozen policy version in new identity should match frozen policy version")
+ XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicy.version, "Flexible policy version in new identity should match new policy version (as provided by new peer)")
+
+ serverJoinExpectation.fulfill()
+ return nil
+ }
+
+ _ = self.assertJoinViaEscrowRecovery(joiningContext: joiningContext, sponsor: self.cuttlefishContext)
+ self.wait(for: [serverJoinExpectation], timeout: 10)
+
+ // Then, after the remote peer joins, the original peer should ignore it entirely: peer1 has no idea what this new policy is about
+ // That means it won't update its trust in response to the join
+ self.fakeCuttlefishServer.updateListener = { request in
+ XCTFail("Expected no updateTrust after peer1 joins")
+ XCTAssertEqual(peer1ID, request.peerID, "updateTrust request should be for peer 1")
+ /*
+ * But, if it did update its trust, here's what we would expect:
+ let newDynamicInfo = request.dynamicInfoAndSig.dynamicInfo()
+ XCTAssertFalse(newDynamicInfo.includedPeerIDs.contains(peer2ID), "Peer1 should not trust peer2")
+ XCTAssertFalse(newDynamicInfo.excludedPeerIDs.contains(peer2ID), "Peer1 should not distrust peer2")
+
+ let newStableInfo = request.stableInfoAndSig.stableInfo()
+
+ XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Policy version in peer should match frozen policy version")
+ XCTAssertEqual(newStableInfo.flexiblePolicyVersion, prevailingPolicyVersion, "Prevailing policy version in peer should match current prevailing policy version")
+
+ updateTrustExpectation.fulfill()
+ */
+ return nil
+ }
+
+ self.sendContainerChangeWaitForFetch(context: self.cuttlefishContext)
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ }
+
+ func createOctagonAndCKKSUsingFuturePolicy() throws -> (TPPolicyDocument, CKRecordZone.ID) {
+ // We want to set up a world with a peer, in Octagon, with TLKs for zones that don't even exist in our current policy.
+ // First, make a new policy.
+ let currentPolicyOptional = builtInPolicyDocuments().filter { $0.version.versionNumber == prevailingPolicyVersion.versionNumber }.first
+ XCTAssertNotNil(currentPolicyOptional, "Should have one current policy")
+ let currentPolicyDocument = currentPolicyOptional!
+
+ let futureViewName = "FutureView"
+ let futureViewMapping = TPPBPolicyKeyViewMapping()!
+
+ let futureViewZoneID = CKRecordZone.ID(zoneName: futureViewName)
+ self.ckksZones.add(futureViewZoneID)
+ self.injectedManager!.setSyncingViewsAllowList(Set((self.intendedCKKSZones.union([futureViewZoneID])).map { $0.zoneName }))
+
+ self.zones![futureViewZoneID] = FakeCKZone(zone: futureViewZoneID)
+
+ XCTAssertFalse(currentPolicyDocument.categoriesByView.keys.contains(futureViewName), "Current policy should not include future view")
+
+ let newPolicyDocument = try TPPolicyDocument(internalVersion: currentPolicyDocument.version.versionNumber + 1,
+ modelToCategory: currentPolicyDocument.modelToCategory,
+ categoriesByView: currentPolicyDocument.categoriesByView.merging([futureViewName: Set(["watch", "full", "tv"])]) { _, new in new },
+ introducersByCategory: currentPolicyDocument.introducersByCategory,
+ redactions: [:],
+ keyViewMapping: [futureViewMapping] + currentPolicyDocument.keyViewMapping,
+ hashAlgo: .SHA256)
+
+ self.fakeCuttlefishServer.policyOverlay.append(newPolicyDocument)
+
+ return (newPolicyDocument, futureViewZoneID)
+ }
+
+ func testRestoreBottledPeerUsingFuturePolicy() throws {
+ let (newPolicyDocument, futureViewZoneID) = try self.createOctagonAndCKKSUsingFuturePolicy()
+
+ let futurePeerContext = self.makeInitiatorContext(contextID: "futurePeer")
+ futurePeerContext.policyOverride = newPolicyDocument.version
+
+ self.startCKAccountStatusMock()
+ let futurePeerID = self.assertResetAndBecomeTrusted(context: futurePeerContext)
+
+ self.putFakeKeyHierarchiesInCloudKit()
+ try self.putSelfTLKSharesInCloudKit(context: futurePeerContext)
+ XCTAssertTrue(try self.tlkShareInCloudKit(receiverPeerID: futurePeerID, senderPeerID: futurePeerID, zoneID: futureViewZoneID))
+
+ // Now, our peer (with no inbuilt knowledge of newPolicyDocument) joins via escrow recovery.
+ // It should be able to recover the FutureView TLK
+ self.assertAllCKKSViewsUpload(tlkShares: 1)
+
+ let serverJoinExpectation = self.expectation(description: "peer1 joins successfully")
+ self.fakeCuttlefishServer.joinListener = { joinRequest in
+ XCTAssertTrue(joinRequest.peer.hasStableInfoAndSig, "Joining peer should have a stable info")
+ let newStableInfo = joinRequest.peer.stableInfoAndSig.stableInfo()
+
+ XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Policy version in peer should match frozen policy version")
+ XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicyDocument.version, "Prevailing policy version in peer should match new policy version")
+
+ serverJoinExpectation.fulfill()
+ return nil
+ }
+
+ let peerID = self.assertJoinViaEscrowRecovery(joiningContext: self.cuttlefishContext, sponsor: futurePeerContext)
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ XCTAssertEqual(self.injectedManager!.policy?.version, newPolicyDocument.version, "CKKS should be configured with new policy")
+ self.verifyDatabaseMocks()
+
+ self.wait(for: [serverJoinExpectation], timeout: 10)
+
+ // And the joined peer should have recovered the TLK, and uploaded itself a share
+ XCTAssertTrue(try self.tlkShareInCloudKit(receiverPeerID: peerID, senderPeerID: peerID, zoneID: futureViewZoneID))
+ }
+
+ func testPairingJoinUsingFuturePolicy() throws {
+ let (newPolicyDocument, futureViewZoneID) = try self.createOctagonAndCKKSUsingFuturePolicy()
+
+ let futurePeerContext = self.makeInitiatorContext(contextID: "futurePeer")
+ futurePeerContext.policyOverride = newPolicyDocument.version
+
+ self.startCKAccountStatusMock()
+ let futurePeerID = self.assertResetAndBecomeTrusted(context: futurePeerContext)
+
+ self.putFakeKeyHierarchiesInCloudKit()
+ try self.putSelfTLKSharesInCloudKit(context: futurePeerContext)
+ XCTAssertTrue(try self.tlkShareInCloudKit(receiverPeerID: futurePeerID, senderPeerID: futurePeerID, zoneID: futureViewZoneID))
+
+ // Now, our peer (with no inbuilt knowledge of newPolicyDocument) joins via pairing
+ // It should be able to recover the FutureView TLK
+ self.assertAllCKKSViewsUpload(tlkShares: 1)
+
+ let serverJoinExpectation = self.expectation(description: "peer1 joins successfully")
+ self.fakeCuttlefishServer.joinListener = { joinRequest in
+ XCTAssertTrue(joinRequest.peer.hasStableInfoAndSig, "Joining peer should have a stable info")
+ let newStableInfo = joinRequest.peer.stableInfoAndSig.stableInfo()
+
+ XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Policy version in peer should match frozen policy version")
+ XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicyDocument.version, "Prevailing policy version in peer should match new policy version")
+
+ serverJoinExpectation.fulfill()
+ return nil
+ }
+
+ let peerID = self.assertJoinViaProximitySetup(joiningContext: self.cuttlefishContext, sponsor: futurePeerContext)
+
+ // And then fake like the other peer uploaded TLKShares after the join succeeded (it would normally happen during, but that's okay)
+ try self.putAllTLKSharesInCloudKit(to: self.cuttlefishContext, from: futurePeerContext)
+ self.sendAllCKKSViewsZoneChanged()
+
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ XCTAssertEqual(self.injectedManager!.policy?.version, newPolicyDocument.version, "CKKS should be configured with new policy")
+ self.verifyDatabaseMocks()
+
+ self.wait(for: [serverJoinExpectation], timeout: 10)
+
+ // And the joined peer should have recovered the TLK, and uploaded itself a share
+ XCTAssertTrue(try self.tlkShareInCloudKit(receiverPeerID: peerID, senderPeerID: peerID, zoneID: futureViewZoneID))
+ }
+
+ func testRecoveryKeyJoinUsingFuturePolicy() throws {
+ let (newPolicyDocument, futureViewZoneID) = try self.createOctagonAndCKKSUsingFuturePolicy()
+
+ let futurePeerContext = self.makeInitiatorContext(contextID: "futurePeer")
+ futurePeerContext.policyOverride = newPolicyDocument.version
+
+ self.startCKAccountStatusMock()
+ let futurePeerID = self.assertResetAndBecomeTrusted(context: futurePeerContext)
+
+ self.putFakeKeyHierarchiesInCloudKit()
+ try self.putSelfTLKSharesInCloudKit(context: futurePeerContext)
+ XCTAssertTrue(try self.tlkShareInCloudKit(receiverPeerID: futurePeerID, senderPeerID: futurePeerID, zoneID: futureViewZoneID))
+
+ // Create the recovery key
+ let recoveryKey = SecPasswordGenerate(SecPasswordType(kSecPasswordTypeiCloudRecoveryKey), nil, nil)! as String
+ XCTAssertNotNil(recoveryKey, "recoveryKey should not be nil")
+
+ self.manager.setSOSEnabledForPlatformFlag(true)
+
+ let createRecoveryExpectation = self.expectation(description: "createRecoveryExpectation returns")
+ self.manager.createRecoveryKey(OTCKContainerName, contextID: futurePeerContext.contextID, recoveryKey: recoveryKey) { error in
+ XCTAssertNil(error, "error should be nil")
+ createRecoveryExpectation.fulfill()
+ }
+ self.wait(for: [createRecoveryExpectation], timeout: 10)
+
+ // Setting the RK will make TLKShares to the RK, so help that out too
+ try self.putRecoveryKeyTLKSharesInCloudKit(recoveryKey: recoveryKey, salt: self.mockAuthKit.altDSID!)
+
+ // Now, our peer (with no inbuilt knowledge of newPolicyDocument) joins via RecoveryKey
+ let serverJoinExpectation = self.expectation(description: "peer1 joins successfully")
+ self.fakeCuttlefishServer.joinListener = { joinRequest in
+ XCTAssertTrue(joinRequest.peer.hasStableInfoAndSig, "Joining peer should have a stable info")
+ let newStableInfo = joinRequest.peer.stableInfoAndSig.stableInfo()
+
+ XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Policy version in peer should match frozen policy version")
+ XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicyDocument.version, "Prevailing policy version in peer should match new policy version")
+
+ serverJoinExpectation.fulfill()
+ return nil
+ }
+
+ // It should recover and upload the FutureView TLK
+ self.assertAllCKKSViewsUpload(tlkShares: 1)
+
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+
+ let joinWithRecoveryKeyExpectation = self.expectation(description: "joinWithRecoveryKey callback occurs")
+ self.cuttlefishContext.join(withRecoveryKey: recoveryKey) { error in
+ XCTAssertNil(error, "error should be nil")
+ joinWithRecoveryKeyExpectation.fulfill()
+ }
+ self.wait(for: [joinWithRecoveryKeyExpectation], timeout: 10)
+
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ XCTAssertEqual(self.injectedManager!.policy?.version, newPolicyDocument.version, "CKKS should be configured with new policy")
+ self.verifyDatabaseMocks()
+
+ self.wait(for: [serverJoinExpectation], timeout: 10)
+ }
+
+ func testPreapprovedJoinUsingFuturePolicy() throws {
+ let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: self.createSOSPeer(peerID: "peer2ID"), trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
+ print(peer2mockSOS.allPeers())
+ self.mockSOSAdapter.trustedPeers.add(peer2mockSOS.selfPeer)
+
+ let futurePeerContext = self.manager.context(forContainerName: OTCKContainerName,
+ contextID: "futurePeer",
+ sosAdapter: peer2mockSOS,
+ authKitAdapter: self.mockAuthKit2,
+ lockStateTracker: self.lockStateTracker,
+ accountStateTracker: self.accountStateTracker,
+ deviceInformationAdapter: OTMockDeviceInfoAdapter(modelID: "iPhone9,1", deviceName: "test-SOS-iphone", serialNumber: "456", osVersion: "iOS (fake version)"))
+
+ let (newPolicyDocument, _) = try self.createOctagonAndCKKSUsingFuturePolicy()
+ futurePeerContext.policyOverride = newPolicyDocument.version
+
+ let serverEstablishExpectation = self.expectation(description: "futurePeer establishes successfully")
+ self.fakeCuttlefishServer.establishListener = { establishRequest in
+ XCTAssertTrue(establishRequest.peer.hasStableInfoAndSig, "Establishing peer should have a stable info")
+ let newStableInfo = establishRequest.peer.stableInfoAndSig.stableInfo()
+
+ XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Policy version in peer should match frozen policy version")
+ XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicyDocument.version, "Prevailing policy version in peer should match new policy version")
+ serverEstablishExpectation.fulfill()
+ return nil
+ }
+
+ // Setup is complete. Join using a new peer
+ self.startCKAccountStatusMock()
+ futurePeerContext.startOctagonStateMachine()
+ self.assertEnters(context: futurePeerContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ self.wait(for: [serverEstablishExpectation], timeout: 10)
+
+ self.putFakeKeyHierarchiesInCloudKit()
+ try self.putSelfTLKSharesInCloudKit(context: futurePeerContext)
+
+ // Now, the default peer joins via SOS preapproval
+ let serverJoinExpectation = self.expectation(description: "peer1 joins successfully")
+ self.fakeCuttlefishServer.joinListener = { joinRequest in
+ XCTAssertTrue(joinRequest.peer.hasStableInfoAndSig, "Joining peer should have a stable info")
+ let newStableInfo = joinRequest.peer.stableInfoAndSig.stableInfo()
+
+ XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Policy version in peer should match frozen policy version")
+ XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicyDocument.version, "Prevailing policy version in peer should match new policy version")
+ serverJoinExpectation.fulfill()
+ return nil
+ }
+
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCInCircle)
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
+ self.wait(for: [serverJoinExpectation], timeout: 10)
+
+ // But, since we're not mocking the remote peer sharing the TLKs, ckks should get stuck
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLK, within: 10 * NSEC_PER_SEC)
+ XCTAssertEqual(self.injectedManager!.policy?.version, newPolicyDocument.version, "CKKS should be configured with new policy")
+ self.verifyDatabaseMocks()
+ }
+
+ func testRespondToFuturePoliciesInPeerUpdates() throws {
+ self.startCKAccountStatusMock()
+ self.assertResetAndBecomeTrustedInDefaultContext()
+
+ // Now, another peer comes along and joins via BP recovery, using a new policy
+ let (newPolicyDocument, _) = try self.createOctagonAndCKKSUsingFuturePolicy()
+
+ let futurePeerContext = self.makeInitiatorContext(contextID: "futurePeer")
+ futurePeerContext.policyOverride = newPolicyDocument.version
+
+ let serverJoinExpectation = self.expectation(description: "futurePeer joins successfully")
+ self.fakeCuttlefishServer.joinListener = { joinRequest in
+ XCTAssertTrue(joinRequest.peer.hasStableInfoAndSig, "Joining peer should have a stable info")
+ let newStableInfo = joinRequest.peer.stableInfoAndSig.stableInfo()
+
+ XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Policy version in peer should match frozen policy version")
+ XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicyDocument.version, "Prevailing policy version in peer should match new policy version")
+ serverJoinExpectation.fulfill()
+ return nil
+ }
+
+ let peer2ID = self.assertJoinViaEscrowRecovery(joiningContext: futurePeerContext, sponsor: self.cuttlefishContext)
+ self.wait(for: [serverJoinExpectation], timeout: 10)
+
+ // Now, tell our first peer about the new changes. It should trust the new peer, and update its policy
+ let updateTrustExpectation = self.expectation(description: "updateTrustExpectation successfully")
+ self.fakeCuttlefishServer.updateListener = { request in
+ let newStableInfo = request.stableInfoAndSig.stableInfo()
+
+ XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Policy version in peer should match frozen policy version")
+ XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicyDocument.version, "Prevailing policy version in peer should match new policy version")
+
+ let newDynamicInfo = request.dynamicInfoAndSig.dynamicInfo()
+ XCTAssert(newDynamicInfo.includedPeerIDs.contains(peer2ID), "Peer1 should trust peer2")
+
+ updateTrustExpectation.fulfill()
+ return nil
+ }
+
+ self.sendContainerChangeWaitForFetch(context: self.cuttlefishContext)
+ self.wait(for: [updateTrustExpectation], timeout: 10)
+ }
+}
+
+#endif
self.cuttlefishContext.startOctagonStateMachine()
self.startCKAccountStatusMock()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
self.wait(for: [healthCheckCallback], timeout: 10)
let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
- self.tphClient.dump(withContainer: containerName, context: contextName) {
- dump, _ in
+ self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
dumpCallback.fulfill()
}
self.cuttlefishContext.startOctagonStateMachine()
self.startCKAccountStatusMock()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let healthCheckCallback = self.expectation(description: "healthCheckCallback callback occurs")
self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
#if os(tvOS)
- XCTAssertEqual(self.cuttlefishContext.postedRepairCFU, false, "Should not have posted a CFU on aTV")
+ XCTAssertEqual(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), false, "Should not have posted a CFU on aTV")
#else
- XCTAssertEqual(self.cuttlefishContext.postedRepairCFU, true, "Should have posted a CFU (due to being untrusted)")
+ XCTAssertEqual(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), true, "Should have posted a CFU (due to being untrusted)")
#endif
self.verifyDatabaseMocks()
self.assertEnters(context: cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
// Reset flag for remainder of test
- self.cuttlefishContext.setPostedBool(false)
+ self.cuttlefishContext.followupHandler.clearAllPostedFlags()
// Set the "have I attempted to join" bit; TVs should still not CFU, but other devices should
try! self.cuttlefishContext.accountMetadataStore.persistOctagonJoinAttempt(.ATTEMPTED)
self.wait(for: [healthCheckCallback2], timeout: 10)
#if os(tvOS)
- XCTAssertEqual(self.cuttlefishContext.postedRepairCFU, false, "Should not have posted a CFU on aTV")
+ XCTAssertEqual(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), false, "Should not have posted a CFU on aTV")
#else
- XCTAssertEqual(self.cuttlefishContext.postedRepairCFU, true, "Should have posted a CFU")
+ XCTAssertEqual(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), true, "Should have posted a CFU")
#endif
}
self.cuttlefishContext.startOctagonStateMachine()
self.startCKAccountStatusMock()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
self.assertConsidersSelfTrusted(context: cuttlefishContext)
let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
- self.tphClient.dump(withContainer: containerName, context: contextName) {
- dump, _ in
+ self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 1, "should be 1 peer ids")
dumpCallback.fulfill()
self.cuttlefishContext.startOctagonStateMachine()
self.startCKAccountStatusMock()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
self.wait(for: [healthCheckCallback], timeout: 10)
#if !os(tvOS)
- XCTAssertEqual(cuttlefishContext.postedRepairCFU, true, "Should have posted a CFU")
+ XCTAssertTrue(cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Should have posted a CFU")
#else
- XCTAssertFalse(cuttlefishContext.postedRepairCFU, "aTV should not have posted a CFU, as there's no iphone to recover from")
+ XCTAssertFalse(cuttlefishContext.followupHandler.hasPosted(.stateRepair), "aTV should not have posted a CFU, as there's no iphone to recover from")
#endif
}
- func responseTestsSetup() throws -> (OTCuttlefishContext, String) {
+ func responseTestsSetup(expectedState: String) throws -> (OTCuttlefishContext, String) {
let containerName = OTCKContainerName
let contextName = OTDefaultContext
self.cuttlefishContext.startOctagonStateMachine()
self.startCKAccountStatusMock()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
// Reset any CFUs we've done so far
self.otFollowUpController.postedFollowUp = false
+ self.cuttlefishContext.followupHandler.clearAllPostedFlags()
let healthCheckCallback = self.expectation(description: "healthCheckCallback callback occurs")
self.manager.healthCheck(containerName, context: contextName, skipRateLimitingCheck: false) { error in
self.wait(for: [healthCheckCallback], timeout: 10)
let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
- self.tphClient.dump(withContainer: containerName, context: contextName) {
- dump, _ in
+ self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
dumpCallback.fulfill()
}
self.wait(for: [dumpCallback], timeout: 10)
self.verifyDatabaseMocks()
- self.assertEnters(context: cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ self.assertEnters(context: cuttlefishContext, state: expectedState, within: 10 * NSEC_PER_SEC)
return (cuttlefishContext, originalCliqueIdentifier!)
}
func testCuttlefishResponseNoAction() throws {
self.fakeCuttlefishServer.returnNoActionResponse = true
- let (cuttlefishContext, _) = try responseTestsSetup()
+ let (cuttlefishContext, _) = try responseTestsSetup(expectedState: OctagonStateReady)
XCTAssertFalse(self.otFollowUpController.postedFollowUp, "should not have posted a CFU")
- XCTAssertEqual(cuttlefishContext.postedRepairCFU, false, "should not have posted a CFU")
+ XCTAssertFalse(cuttlefishContext.followupHandler.hasPosted(.stateRepair), "should not have posted a Repair CFU")
}
func testCuttlefishResponseRepairAccount() throws {
self.fakeCuttlefishServer.returnRepairAccountResponse = true
- let (_, _) = try responseTestsSetup()
- XCTAssertTrue(self.otFollowUpController.postedFollowUp, "should have posted a CFU")
+ let (_, _) = try responseTestsSetup(expectedState: OctagonStateReady)
+ XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "should have posted a Repair CFU")
}
func testCuttlefishResponseRepairEscrow() throws {
self.fakeCuttlefishServer.returnRepairEscrowResponse = true
OTMockSecEscrowRequest.self.populateStatuses = false
- let (cuttlefishContext, _) = try responseTestsSetup()
+ let (cuttlefishContext, _) = try responseTestsSetup(expectedState: OctagonStateReady)
XCTAssertTrue(self.otFollowUpController.postedFollowUp, "should have posted a CFU")
- XCTAssertEqual(cuttlefishContext.postedEscrowRepairCFU, true, "should have posted an escrow CFU")
+ XCTAssertTrue(cuttlefishContext.followupHandler.hasPosted(.offlinePasscodeChange), "should have posted an escrow CFU")
}
func testCuttlefishResponseResetOctagon() throws {
let contextName = OTDefaultContext
let containerName = OTCKContainerName
self.fakeCuttlefishServer.returnResetOctagonResponse = true
- let (cuttlefishContext, cliqueIdentifier) = try responseTestsSetup()
+ let (cuttlefishContext, cliqueIdentifier) = try responseTestsSetup(expectedState: OctagonStateReady)
assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
self.verifyDatabaseMocks()
var newCliqueIdentifier: String?
let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
- self.tphClient.dump(withContainer: containerName, context: contextName) {
- dump, _ in
+ self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
newCliqueIdentifier = egoSelf!["peerID"]! as? String
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
dumpCallback.fulfill()
assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
}
+ func testCuttlefishResponseLeaveTrust() throws {
+ OctagonSetSOSFeatureEnabled(false)
+
+ self.fakeCuttlefishServer.returnLeaveTrustResponse = true
+ let (_, _) = try responseTestsSetup(expectedState: OctagonStateUntrusted)
+
+ self.verifyDatabaseMocks()
+ assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTrust, within: 10 * NSEC_PER_SEC)
+ }
+
func testCuttlefishResponseError() throws {
self.fakeCuttlefishServer.returnRepairErrorResponse = FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .changeTokenExpired)
- let (cuttlefishContext, _) = try responseTestsSetup()
- XCTAssertEqual(cuttlefishContext.postedRepairCFU, false, "should not have posted an account repair CFU")
- XCTAssertEqual(cuttlefishContext.postedEscrowRepairCFU, false, "should not have posted an escrow repair CFU")
+ let (cuttlefishContext, _) = try responseTestsSetup(expectedState: OctagonStateReady)
+ XCTAssertFalse(cuttlefishContext.followupHandler.hasPosted(.stateRepair), "should not have posted an account repair CFU")
+ XCTAssertFalse(cuttlefishContext.followupHandler.hasPosted(.offlinePasscodeChange), "should not have posted an escrow repair CFU")
}
func testHealthCheckBeforeStateMachineStarts() throws {
cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
}
let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
- self.tphClient.dump(withContainer: containerName, context: contextName) {
- dump, _ in
+ self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
dumpCallback.fulfill()
}
let containerName = OTCKContainerName
let contextName = OTDefaultContext
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.cuttlefishContext.startOctagonStateMachine()
self.startCKAccountStatusMock()
}
let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
- self.tphClient.dump(withContainer: containerName, context: contextName) {
- dump, _ in
+ self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
dumpCallback.fulfill()
}
self.cuttlefishContext.startOctagonStateMachine()
self.startCKAccountStatusMock()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let healthCheckCallback = self.expectation(description: "healthCheckCallback callback occurs")
}
let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
- self.tphClient.dump(withContainer: containerName, context: contextName) {
- dump, _ in
+ self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
dumpCallback.fulfill()
}
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
}
+ func testHealthCheckWaitingForCDP() throws {
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.startCKAccountStatusMock()
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+
+ self.cuttlefishContext.stateMachine.setWatcherTimeout(2 * NSEC_PER_SEC)
+
+ let healthCheckCallback = self.expectation(description: "healthCheckCallback callback occurs")
+ self.manager.healthCheck(OTCKContainerName, context: self.cuttlefishContext.contextID, skipRateLimitingCheck: false) { error in
+ XCTAssertNil(error, "error should be nil")
+ healthCheckCallback.fulfill()
+ }
+ self.wait(for: [healthCheckCallback], timeout: 10)
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+ }
+
+ func testHealthCheckRecoversFromWrongWaitingForCDP() throws {
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.startCKAccountStatusMock()
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+
+ // Now, another client creates the circle, but we miss the push
+ let remote = self.makeInitiatorContext(contextID: "remote")
+ self.assertResetAndBecomeTrusted(context: remote)
+
+ // Now, does the health check get us into Untrusted?
+ self.cuttlefishContext.stateMachine.setWatcherTimeout(2 * NSEC_PER_SEC)
+
+ let healthCheckCallback = self.expectation(description: "healthCheckCallback callback occurs")
+ self.manager.healthCheck(OTCKContainerName, context: self.cuttlefishContext.contextID, skipRateLimitingCheck: false) { error in
+ XCTAssertNil(error, "error should be nil")
+ healthCheckCallback.fulfill()
+ }
+ self.wait(for: [healthCheckCallback], timeout: 10)
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+
+ #if !os(tvOS)
+ XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have posted a repair CFU after the health check")
+ #else
+ XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "posted should be false on tvOS; there aren't any iphones around to repair it")
+ #endif
+ }
+
func testHealthCheckWhenLocked() throws {
let containerName = OTCKContainerName
self.cuttlefishContext.startOctagonStateMachine()
self.startCKAccountStatusMock()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
self.wait(for: [healthCheckCallback], timeout: 10)
let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
- self.tphClient.dump(withContainer: containerName, context: contextName) {
- dump, _ in
+ self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
dumpCallback.fulfill()
}
self.cuttlefishContext.startOctagonStateMachine()
self.startCKAccountStatusMock()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
self.wait(for: [healthCheckCallback], timeout: 10)
let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
- self.tphClient.dump(withContainer: containerName, context: contextName) {
- dump, _ in
+ self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
dumpCallback.fulfill()
}
//update the last health check to something way in the past
do {
let state = try OTAccountMetadataClassC.loadFromKeychain(forContainer: OTCKContainerName, contextID: OTDefaultContext)
- state.lastHealthCheckup = state.lastHealthCheckup - 172800000 /* 2 days of seconds * 1000*/
+ state.lastHealthCheckup -= 172800000 /* 2 days of seconds * 1000*/
healthCheckMinusTwoDays = state.lastHealthCheckup
XCTAssertNoThrow(try state.saveToKeychain(forContainer: OTCKContainerName, contextID: OTDefaultContext), "saving to the keychain should work")
} catch {
bottlerContext.startOctagonStateMachine()
self.startCKAccountStatusMock()
+ XCTAssertNoThrow(try bottlerContext.setCDPEnabled())
self.assertEnters(context: bottlerContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
XCTAssertNotNil(entropy, "entropy should not be nil")
// Fake that this peer also created some TLKShares for itself
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- try self.putSelfTLKShareInCloudKit(context: bottlerContext, zoneID: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ try self.putSelfTLKSharesInCloudKit(context: bottlerContext)
let bottle = self.fakeCuttlefishServer.state.bottles[0]
self.cuttlefishContext.startOctagonStateMachine()
self.startCKAccountStatusMock()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
// cheat: a bottle restore can only succeed after a fetch occurs
}
let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
- self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+ self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
XCTAssertNotNil(error, "error should not be nil")
joinWithBottleExpectation.fulfill()
}
func testCuttlefishDontPostEscrowCFUDueToPendingPrecord() throws {
self.fakeCuttlefishServer.returnRepairEscrowResponse = true
OTMockSecEscrowRequest.self.populateStatuses = true
- let (cuttlefishContext, _) = try responseTestsSetup()
- XCTAssertEqual(cuttlefishContext.postedEscrowRepairCFU, false, "should NOT have posted an escrow CFU")
+ let (cuttlefishContext, _) = try responseTestsSetup(expectedState: OctagonStateReady)
+ XCTAssertFalse(cuttlefishContext.followupHandler.hasPosted(.offlinePasscodeChange), "should NOT have posted an escrow CFU")
}
func testHealthCheckWhileLocked() throws {
self.cuttlefishContext.startOctagonStateMachine()
self.startCKAccountStatusMock()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
--- /dev/null
+#if OCTAGON
+import Foundation
+
+extension SignedPeerStableInfo {
+ func stableInfo() -> TPPeerStableInfo {
+ let newStableInfo = TPPeerStableInfo(data: self.peerStableInfo, sig: self.sig)
+ XCTAssertNotNil(newStableInfo, "should be able to make a stableInfo from protobuf")
+ return newStableInfo!
+ }
+}
+
+extension SignedPeerDynamicInfo {
+ func dynamicInfo() -> TPPeerDynamicInfo {
+ let newDynamicInfo = TPPeerDynamicInfo(data: self.peerDynamicInfo, sig: self.sig)
+ XCTAssertNotNil(newDynamicInfo, "should be able to make a dynamicInfo from protobuf")
+ return newDynamicInfo!
+ }
+}
+
+extension EstablishRequest {
+ func permanentInfo() -> TPPeerPermanentInfo {
+ XCTAssertTrue(self.hasPeer, "establish request should have a peer")
+ XCTAssertTrue(self.peer.hasPermanentInfoAndSig, "establish request should have a permanentInfo")
+ let newPermanentInfo = TPPeerPermanentInfo(peerID: self.peer.peerID,
+ data: self.peer.permanentInfoAndSig.peerPermanentInfo,
+ sig: self.peer.permanentInfoAndSig.sig,
+ keyFactory: TPECPublicKeyFactory())
+ XCTAssertNotNil(newPermanentInfo, "should be able to make a permanantInfo from protobuf")
+ return newPermanentInfo!
+ }
+}
+
+#endif
#if OCTAGON
-@objcMembers class OctagonRecoveryKeyTests: OctagonTestsBase {
+extension Container {
+ func removeRKFromContainer() {
+ self.moc.performAndWait {
+ self.containerMO.recoveryKeySigningSPKI = nil
+ self.containerMO.recoveryKeyEncryptionSPKI = nil
+
+ try! self.moc.save()
+ }
+ }
+}
+
+@objcMembers
+class OctagonRecoveryKeyTests: OctagonTestsBase {
override func setUp() {
super.setUp()
}
self.manager.setSOSEnabledForPlatformFlag(false)
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
XCTAssertFalse(self.mockAuthKit.currentDeviceList().isEmpty, "should not have zero devices")
self.manager.setSOSEnabledForPlatformFlag(true)
let createKeyExpectation = self.expectation(description: "createKeyExpectation returns")
- self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context ?? "defaultContext", recoveryKey: recoveryKey) { error in
+ self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context, recoveryKey: recoveryKey) { error in
XCTAssertNil(error, "error should be nil")
createKeyExpectation.fulfill()
}
self.manager.setSOSEnabledForPlatformFlag(false)
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
self.manager.setSOSEnabledForPlatformFlag(true)
let createKeyExpectation = self.expectation(description: "createKeyExpectation returns")
- self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context ?? "defaultContext", recoveryKey: recoveryKey) { error in
+ self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context, recoveryKey: recoveryKey) { error in
XCTAssertNil(error, "error should be nil")
createKeyExpectation.fulfill()
}
self.sendContainerChange(context: initiatorContext)
let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
- initiatorContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+ initiatorContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
XCTAssertNil(error, "error should be nil")
joinWithBottleExpectation.fulfill()
}
self.verifyDatabaseMocks()
let stableInfoCheckDumpCallback = self.expectation(description: "stableInfoCheckDumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+ let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
self.wait(for: [stableInfoCheckDumpCallback], timeout: 10)
let stableInfoAcceptorCheckDumpCallback = self.expectation(description: "stableInfoAcceptorCheckDumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: self.otcliqueContext.context ?? "defaultContext") {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: self.otcliqueContext.context) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+ let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
self.manager.setSOSEnabledForPlatformFlag(false)
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
self.manager.setSOSEnabledForPlatformFlag(true)
let createKeyExpectation = self.expectation(description: "createKeyExpectation returns")
- self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context ?? "defaultContext", recoveryKey: recoveryKey) { error in
+ self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context, recoveryKey: recoveryKey) { error in
XCTAssertNil(error, "error should be nil")
createKeyExpectation.fulfill()
}
self.assertEnters(context: initiatorContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
- initiatorContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+ initiatorContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
XCTAssertNil(error, "error should be nil")
joinWithBottleExpectation.fulfill()
}
self.verifyDatabaseMocks()
let stableInfoCheckDumpCallback = self.expectation(description: "stableInfoCheckDumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+ let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
self.wait(for: [stableInfoCheckDumpCallback], timeout: 10)
let stableInfoAcceptorCheckDumpCallback = self.expectation(description: "stableInfoAcceptorCheckDumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: self.otcliqueContext.context ?? "defaultContext") {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: self.otcliqueContext.context) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+ let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
self.sendContainerChange(context: thirdPeerContext)
let thirdPeerJoinWithBottleExpectation = self.expectation(description: "thirdPeerJoinWithBottleExpectation callback occurs")
- thirdPeerContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+ thirdPeerContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
XCTAssertNil(error, "error should be nil")
thirdPeerJoinWithBottleExpectation.fulfill()
}
self.sendContainerChangeWaitForFetch(context: thirdPeerContext)
let thirdPeerStableInfoCheckDumpCallback = self.expectation(description: "thirdPeerStableInfoCheckDumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: thirdPeerContextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: thirdPeerContextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+ let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 3, "should be 3df peer ids")
let establishContext = self.createEstablishContext(contextID: establishContextID)
establishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try establishContext.setCDPEnabled())
self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
self.assertConsidersSelfTrusted(context: establishContext)
// Fake that this peer also created some TLKShares for itself
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- try self.putSelfTLKShareInCloudKit(context: establishContext, zoneID: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ try self.putSelfTLKSharesInCloudKit(context: establishContext)
self.assertSelfTLKSharesInCloudKit(context: establishContext)
self.sendContainerChangeWaitForFetch(context: recoveryContext)
let stableInfoCheckDumpCallback = self.expectation(description: "stableInfoCheckDumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+ let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
let vouchers = dump!["vouchers"]
self.sendContainerChangeWaitForFetch(context: establishContext)
let stableInfoAcceptorCheckDumpCallback = self.expectation(description: "stableInfoAcceptorCheckDumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: establishContextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: establishContextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+ let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
let vouchers = dump!["vouchers"]
stableInfoAcceptorCheckDumpCallback.fulfill()
}
self.wait(for: [stableInfoAcceptorCheckDumpCallback], timeout: 10)
- try self.putSelfTLKShareInCloudKit(context: recoveryContext, zoneID: self.manateeZoneID)
+ try self.putSelfTLKSharesInCloudKit(context: recoveryContext)
self.assertSelfTLKSharesInCloudKit(context: recoveryContext)
}
let establishContext = self.createEstablishContext(contextID: establishContextID)
establishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try establishContext.setCDPEnabled())
self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
self.sendContainerChangeWaitForFetch(context: establishContext)
self.silentFetchesAllowed = false
- self.expectCKFetchAndRun(beforeFinished: {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+ self.expectCKFetchAndRun {
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putFakeDeviceStatusesInCloudKit()
self.silentFetchesAllowed = true
- })
+ }
let recoveryContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
recoveryContext.startOctagonStateMachine()
let establishContext = self.createEstablishContext(contextID: establishContextID)
establishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try establishContext.setCDPEnabled())
self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
let establishContext = self.createEstablishContext(contextID: establishContextID)
establishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try establishContext.setCDPEnabled())
self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
let initiatorContextID = "initiator-context-id"
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
self.sendContainerChange(context: initiatorContext)
let restoreExpectation = self.expectation(description: "restore returns")
- self.manager!.restore(OTCKContainerName, contextID: initiatorContextID, bottleSalt: self.otcliqueContext.altDSID, entropy: entropy!, bottleID: bottle.bottleID) { error in
+ self.manager!.restore(OTCKContainerName, contextID: initiatorContextID, bottleSalt: self.otcliqueContext.altDSID!, entropy: entropy!, bottleID: bottle.bottleID) { error in
XCTAssertNil(error, "error should be nil")
restoreExpectation.fulfill()
}
self.assertEnters(context: initiatorContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
var initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
//now let's ensure recovery keys are set for both the first device and second device
initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+ let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
initiatorRecoverySigningKey = stableInfo!["recovery_signing_public_key"] as? Data
initiatorRecoveryEncryptionKey = stableInfo!["recovery_encryption_public_key"] as? Data
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
let vouchers = dump!["vouchers"]
self.wait(for: [initiatorDumpCallback], timeout: 10)
let firstDeviceDumpCallback = self.expectation(description: "firstDeviceDumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+ let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
firstDeviceRecoverySigningKey = stableInfo!["recovery_signing_public_key"] as? Data
firstDeviceRecoveryEncryptionKey = stableInfo!["recovery_encryption_public_key"] as? Data
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
let vouchers = dump!["vouchers"]
let establishContext = self.createEstablishContext(contextID: establishContextID)
establishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try establishContext.setCDPEnabled())
self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
self.assertConsidersSelfTrusted(context: establishContext)
// Fake that this peer also created some TLKShares for itself
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- try self.putSelfTLKShareInCloudKit(context: establishContext, zoneID: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ try self.putSelfTLKSharesInCloudKit(context: establishContext)
self.assertSelfTLKSharesInCloudKit(context: establishContext)
self.sendContainerChangeWaitForFetch(context: newGuyContext)
let stableInfoAcceptorCheckDumpCallback = self.expectation(description: "stableInfoAcceptorCheckDumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+ let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
let vouchers = dump!["vouchers"]
self.wait(for: [stableInfoAcceptorCheckDumpCallback], timeout: 10)
self.assertEnters(context: newGuyContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfTrusted(context: newGuyContext)
- try self.putSelfTLKShareInCloudKit(context: newGuyContext, zoneID: self.manateeZoneID)
+ try self.putSelfTLKSharesInCloudKit(context: newGuyContext)
self.assertSelfTLKSharesInCloudKit(context: newGuyContext)
self.sendContainerChangeWaitForFetch(context: establishContext)
let stableInfoCheckDumpCallback = self.expectation(description: "stableInfoCheckDumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: establishContextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: establishContextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+ let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
let vouchers = dump!["vouchers"]
self.sendContainerChange(context: recoveryGuyContext)
let newGuyCheckDumpCallback = self.expectation(description: "newGuyCheckDumpCallback callback occurs")
- self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) {
- dump, _ in
+ self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+ let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 1, "should be 1 peer ids")
let vouchers = dump!["vouchers"]
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
XCTAssertFalse(self.mockAuthKit.currentDeviceList().isEmpty, "should not have zero devices")
XCTAssertNotNil(recoveryKey, "recoveryKey should not be nil")
let createKeyExpectation = self.expectation(description: "createKeyExpectation returns")
- self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context ?? "defaultContext", recoveryKey: recoveryKey) { error in
+ self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context, recoveryKey: recoveryKey) { error in
XCTAssertNotNil(error, "error should not be nil")
XCTAssertEqual((error! as NSError).code, OctagonError.OTErrorLimitedPeer.rawValue, "error code should be limited peer")
createKeyExpectation.fulfill()
let establishContext = self.createEstablishContext(contextID: establishContextID)
establishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try establishContext.setCDPEnabled())
self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
self.assertConsidersSelfTrusted(context: establishContext)
// Fake that this peer also created some TLKShares for itself
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- try self.putSelfTLKShareInCloudKit(context: establishContext, zoneID: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ try self.putSelfTLKSharesInCloudKit(context: establishContext)
self.assertSelfTLKSharesInCloudKit(context: establishContext)
let establishContext = self.createEstablishContext(contextID: establishContextID)
establishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try establishContext.setCDPEnabled())
self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
self.assertConsidersSelfTrusted(context: establishContext)
// Fake that this peer also created some TLKShares for itself
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- try self.putSelfTLKShareInCloudKit(context: establishContext, zoneID: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ try self.putSelfTLKSharesInCloudKit(context: establishContext)
self.assertSelfTLKSharesInCloudKit(context: establishContext)
XCTAssertNotNil(recoveryKey, "recoveryKey should not be nil")
OTClique.recoverOctagon(usingData: newCliqueContext, recoveryKey: recoveryKey!) { error in
- XCTAssertNotNil(error, "error should NOT be nil")
- XCTAssertEqual((error! as NSError).code, 32, "error code should be 32/untrusted recovery keys")
- XCTAssertEqual((error! as NSError).domain, "com.apple.security.trustedpeers.container", "error code domain should be com.apple.security.trustedpeers.container")
+ XCTAssertNil(error, "error should be nil")
joinWithRecoveryKeyExpectation.fulfill()
}
self.wait(for: [joinWithRecoveryKeyExpectation], timeout: 10)
let establishContext = self.createEstablishContext(contextID: establishContextID)
establishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try establishContext.setCDPEnabled())
self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
self.assertConsidersSelfTrusted(context: establishContext)
// Fake that this peer also created some TLKShares for itself
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- try self.putSelfTLKShareInCloudKit(context: establishContext, zoneID: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ try self.putSelfTLKSharesInCloudKit(context: establishContext)
self.assertSelfTLKSharesInCloudKit(context: establishContext)
let establishContext = self.createEstablishContext(contextID: establishContextID)
establishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try establishContext.setCDPEnabled())
self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
self.assertConsidersSelfTrusted(context: establishContext)
// Fake that this peer also created some TLKShares for itself
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- try self.putSelfTLKShareInCloudKit(context: establishContext, zoneID: self.manateeZoneID)
-
+ self.putFakeKeyHierarchiesInCloudKit()
+ try! self.putSelfTLKSharesInCloudKit(context: establishContext)
self.assertSelfTLKSharesInCloudKit(context: establishContext)
let recoveryKey = "malformedRecoveryKey"
self.manager.setSOSEnabledForPlatformFlag(true)
let createKeyExpectation = self.expectation(description: "createKeyExpectation returns")
- self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context ?? "defaultContext", recoveryKey: recoveryKey) { error in
+ self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context, recoveryKey: recoveryKey) { error in
XCTAssertNotNil(error, "error should NOT be nil")
XCTAssertEqual((error! as NSError).code, 41, "error code should be 41/malformed recovery key")
XCTAssertEqual((error! as NSError).domain, "com.apple.security.octagon", "error code domain should be com.apple.security.octagon")
}
self.wait(for: [joinWithRecoveryKeyExpectation], timeout: 10)
}
+
+ @discardableResult
+ func createAndSetRecoveryKey(context: OTCuttlefishContext) -> String {
+ let cliqueConfiguration = OTConfigurationContext()
+ cliqueConfiguration.context = context.contextID
+ cliqueConfiguration.altDSID = try! context.authKitAdapter.primaryiCloudAccountAltDSID()
+ cliqueConfiguration.otControl = self.otControl
+
+ let recoveryKey = SecRKCreateRecoveryKeyString(nil)
+ XCTAssertNotNil(recoveryKey, "recoveryKey should not be nil")
+
+ let setRecoveryKeyExpectation = self.expectation(description: "setRecoveryKeyExpectation callback occurs")
+ TestsObjectiveC.setNewRecoveryKeyWithData(cliqueConfiguration, recoveryKey: recoveryKey!) { _, error in
+ XCTAssertNil(error, "error should be nil")
+ setRecoveryKeyExpectation.fulfill()
+ }
+ self.wait(for: [setRecoveryKeyExpectation], timeout: 10)
+
+ return recoveryKey!
+ }
+
+ func testConcurWithTrustedPeer() throws {
+ self.startCKAccountStatusMock()
+ self.manager.setSOSEnabledForPlatformFlag(true)
+
+ self.assertResetAndBecomeTrustedInDefaultContext()
+
+ let peer2Context = self.makeInitiatorContext(contextID: "peer2")
+ let peer2ID = self.assertJoinViaEscrowRecovery(joiningContext: peer2Context, sponsor: self.cuttlefishContext)
+
+ self.assertAllCKKSViewsUpload(tlkShares: 1)
+ self.sendContainerChangeWaitForFetch(context: self.cuttlefishContext)
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ self.verifyDatabaseMocks()
+
+ // peer1 sets a recovery key
+ var rkSigningPubKey : Data? = nil
+ var rkEncryptionPubKey : Data? = nil
+
+ let setRKExpectation = self.expectation(description: "setRecoveryKey")
+ self.fakeCuttlefishServer.setRecoveryKeyListener = { request in
+ XCTAssertNotNil(request.recoverySigningPubKey, "signing public key should be present")
+ XCTAssertNotNil(request.recoveryEncryptionPubKey, "encryption public key should be present")
+
+ rkSigningPubKey = request.recoverySigningPubKey
+ rkEncryptionPubKey = request.recoveryEncryptionPubKey
+
+ setRKExpectation.fulfill()
+ return nil
+ }
+
+ self.createAndSetRecoveryKey(context: self.cuttlefishContext)
+ self.wait(for: [setRKExpectation], timeout: 10)
+
+ // And peer2 concurs with it upon receiving a push
+ let updateTrustExpectation = self.expectation(description: "updateTrust")
+ self.fakeCuttlefishServer.updateListener = { [unowned self] request in
+ XCTAssertEqual(request.peerID, peer2ID, "Update should be for peer2")
+
+ let newStableInfo = request.stableInfoAndSig.stableInfo()
+ XCTAssertEqual(newStableInfo.recoverySigningPublicKey, rkSigningPubKey, "Recovery signing key should match other peer")
+ XCTAssertEqual(newStableInfo.recoveryEncryptionPublicKey, rkEncryptionPubKey, "Recovery encryption key should match other peer")
+ self.fakeCuttlefishServer.updateListener = nil
+ updateTrustExpectation.fulfill()
+
+ return nil
+ }
+
+ self.sendContainerChangeWaitForFetch(context: peer2Context)
+ self.wait(for: [updateTrustExpectation], timeout: 10)
+
+ // Restart TPH, and ensure that more updates succeed
+ self.tphClient.containerMap.removeAllContainers()
+
+ self.sendContainerChangeWaitForFetch(context: self.cuttlefishContext)
+ self.sendContainerChangeWaitForFetch(context: peer2Context)
+ }
+
+ func testRecoveryKeyLoadingOnContainerLoad() throws {
+ self.startCKAccountStatusMock()
+ self.manager.setSOSEnabledForPlatformFlag(true)
+
+ let _ = self.assertResetAndBecomeTrustedInDefaultContext()
+ // peer1 sets a recovery key
+ self.createAndSetRecoveryKey(context: self.cuttlefishContext)
+
+ // Restart TPH
+ self.tphClient.containerMap.removeAllContainers()
+
+ self.sendContainerChangeWaitForFetch(context: self.cuttlefishContext)
+ }
+
+ func testRecoveryKeyLoadingOnContainerLoadEvenIfMissing() throws {
+ self.startCKAccountStatusMock()
+ self.manager.setSOSEnabledForPlatformFlag(true)
+
+ let _ = self.assertResetAndBecomeTrustedInDefaultContext()
+ // peer1 sets a recovery key
+ self.createAndSetRecoveryKey(context: self.cuttlefishContext)
+
+ // Before restarting TPH, emulate a world in which the RK variables were not set on the container
+
+ let containerName = ContainerName(container: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID)
+ let container = try self.tphClient.containerMap.findOrCreate(name: containerName)
+ container.removeRKFromContainer()
+
+ // Restart TPH
+ self.tphClient.containerMap.removeAllContainers()
+
+ self.sendContainerChangeWaitForFetch(context: self.cuttlefishContext)
+ }
}
#endif
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
_ = try self.cuttlefishContext.accountAvailable("13453464")
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.cuttlefishContext.rpcResetAndEstablish(.testGenerated) { resetError in
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.cuttlefishContext.rpcResetAndEstablish(.testGenerated) { resetError in
self.startCKAccountStatusMock()
// Before resetAndEstablish, there shouldn't be any stored account state
- XCTAssertThrowsError(try OTAccountMetadataClassC.loadFromKeychain(forContainer: containerName, contextID: contextName), "Before doing anything, loading a non-existent account state should fail")
+ XCTAssertThrowsError(try OTAccountMetadataClassC.loadFromKeychain(forContainer: containerName, contextID: contextName), "Before doing anything, loading a non-existent account state should fail")
let resetAndEstablishExpectation = self.expectation(description: "resetAndEstablish callback occurs")
let escrowRequestNotification = expectation(forNotification: OTMockEscrowRequestNotification,
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
do {
let waitfortrusts = self.ckksViews.compactMap { view in
(view as! CKKSKeychainView).keyHierarchyConditions[SecCKKSZoneKeyStateWaitForTrust] as? CKKSCondition
}
- XCTAssert(waitfortrusts.count > 0, "Should have at least one waitfortrust condition")
+ XCTAssert(!waitfortrusts.isEmpty, "Should have at least one waitfortrust condition")
let resetAndEstablishExpectation = self.expectation(description: "resetAndEstablish callback occurs")
let escrowRequestNotification = expectation(forNotification: OTMockEscrowRequestNotification,
}
func testOctagonResetAlsoResetsCKKSViewsMissingTLKs() {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
- let zoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
+ let zoneKeys = self.keys![self.limitedPeersAllowedZoneID!] as? ZoneKeys
XCTAssertNotNil(zoneKeys, "Should have some zone keys")
XCTAssertNotNil(zoneKeys?.tlk, "Should have a tlk in the original key set")
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTrust, within: 10 * NSEC_PER_SEC)
assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
- let laterZoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
+ let laterZoneKeys = self.keys![self.limitedPeersAllowedZoneID!] as? ZoneKeys
XCTAssertNotNil(laterZoneKeys, "Should have some zone keys")
XCTAssertNotNil(laterZoneKeys?.tlk, "Should have a tlk in the newly created keyset")
XCTAssertNotEqual(zoneKeys?.tlk?.uuid, laterZoneKeys?.tlk?.uuid, "CKKS zone should now have different keys")
func testOctagonResetIgnoresOldRemoteDevicesWithKeysAndResetsCKKS() {
// CKKS has no keys, and there's another device claiming to have them already, but it's old
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putFakeDeviceStatusesInCloudKit()
+ #if !os(tvOS)
(self.zones![self.manateeZoneID!]! as! FakeCKZone).currentDatabase.allValues.forEach { record in
let r = record as! CKRecord
- if(r.recordType == SecCKRecordDeviceStateType) {
+ if r.recordType == SecCKRecordDeviceStateType {
+ r.creationDate = NSDate.distantPast
+ r.modificationDate = NSDate.distantPast
+ }
+ }
+ #endif
+ (self.zones![self.limitedPeersAllowedZoneID!]! as! FakeCKZone).currentDatabase.allValues.forEach { record in
+ let r = record as! CKRecord
+ if r.recordType == SecCKRecordDeviceStateType {
r.creationDate = NSDate.distantPast
r.modificationDate = NSDate.distantPast
}
}
+ #if !os(tvOS)
let zoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
- XCTAssertNotNil(zoneKeys, "Should have some zone keys")
- XCTAssertNotNil(zoneKeys?.tlk, "Should have a tlk in the original key set")
+ XCTAssertNotNil(zoneKeys, "Should have some zone keys for Manatee")
+ XCTAssertNotNil(zoneKeys?.tlk, "Should have a tlk in the original key set for Manatee")
+ #endif
+
+ let lpZoneKeys = self.keys![self.limitedPeersAllowedZoneID!] as? ZoneKeys
+ XCTAssertNotNil(lpZoneKeys, "Should have some zone keys for LimitedPeers")
+ XCTAssertNotNil(lpZoneKeys?.tlk, "Should have a tlk in the original key set for LimitedPeers")
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.silentZoneDeletesAllowed = true
XCTFail("failed to make new friends: \(error)")
}
- assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ #if !os(tvOS)
let laterZoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
- XCTAssertNotNil(laterZoneKeys, "Should have some zone keys")
- XCTAssertNotNil(laterZoneKeys?.tlk, "Should have a tlk in the newly created keyset")
- XCTAssertNotEqual(zoneKeys?.tlk?.uuid, laterZoneKeys?.tlk?.uuid, "CKKS zone should now have different keys")
+ XCTAssertNotNil(laterZoneKeys, "Should have some zone keys for Manatee")
+ XCTAssertNotNil(laterZoneKeys?.tlk, "Should have a tlk in the newly created keyset for Manatee")
+ XCTAssertNotEqual(zoneKeys?.tlk?.uuid, laterZoneKeys?.tlk?.uuid, "CKKS zone should now have different keys for Manatee")
+ #else
+ let laterZoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
+ XCTAssertNil(laterZoneKeys, "Should have no Manatee zone keys for aTV")
+ #endif
+
+ let laterLpZoneKeys = self.keys![self.limitedPeersAllowedZoneID!] as? ZoneKeys
+ XCTAssertNotNil(laterLpZoneKeys, "Should have some zone keys for LimitedPeers")
+ XCTAssertNotNil(laterLpZoneKeys?.tlk, "Should have a tlk in the newly created keyset for LimitedPeers")
+ XCTAssertNotEqual(lpZoneKeys?.tlk?.uuid, laterLpZoneKeys?.tlk?.uuid, "CKKS zone should now have different keys for LimitedPeers")
}
func testOctagonResetWithRemoteDevicesWithKeysDoesNotResetCKKS() {
// CKKS has no keys, and there's another device claiming to have them already, so CKKS won't immediately reset it
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putFakeDeviceStatusesInCloudKit()
+ #if !os(tvOS)
let zoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
XCTAssertNotNil(zoneKeys, "Should have some zone keys")
XCTAssertNotNil(zoneKeys?.tlk, "Should have a tlk in the original key set")
+ #endif
+
+ let lpZoneKeys = self.keys![self.limitedPeersAllowedZoneID!] as? ZoneKeys
+ XCTAssertNotNil(lpZoneKeys, "Should have some zone keys for LimitedPeers")
+ XCTAssertNotNil(lpZoneKeys?.tlk, "Should have a tlk in the original key set for LimitedPeers")
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.silentZoneDeletesAllowed = true
assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLK, within: 10 * NSEC_PER_SEC)
+ #if !os(tvOS)
let laterZoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
XCTAssertNotNil(laterZoneKeys, "Should have some zone keys")
XCTAssertNotNil(laterZoneKeys?.tlk, "Should have a tlk in the newly created keyset")
XCTAssertEqual(zoneKeys?.tlk?.uuid, laterZoneKeys?.tlk?.uuid, "CKKS zone should now have the same keys")
+ #else
+ let laterZoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
+ XCTAssertNil(laterZoneKeys, "Should have no Manatee zone keys for aTV")
+ #endif
+
+ let lpLaterZoneKeys = self.keys![self.limitedPeersAllowedZoneID!] as? ZoneKeys
+ XCTAssertNotNil(lpLaterZoneKeys, "Should have some zone keys for LimitedPeersAllowed")
+ XCTAssertNotNil(lpLaterZoneKeys?.tlk, "Should have a tlk in the newly created keyset for LimitedPeersAllowed")
+ XCTAssertEqual(lpZoneKeys?.tlk?.uuid, lpLaterZoneKeys?.tlk?.uuid, "CKKS zone should now have the same keys for LimitedPeersAllowed")
}
func testOctagonResetWithTLKsDoesNotResetCKKS() {
// CKKS has the keys keys
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.saveTLKMaterialToKeychain()
+ #if !os(tvOS)
let zoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
XCTAssertNotNil(zoneKeys, "Should have some zone keys")
XCTAssertNotNil(zoneKeys?.tlk, "Should have a tlk in the original key set")
+ #endif
+
+ let lpZoneKeys = self.keys![self.limitedPeersAllowedZoneID!] as? ZoneKeys
+ XCTAssertNotNil(lpZoneKeys, "Should have some zone keys for LimitedPeers")
+ XCTAssertNotNil(lpZoneKeys?.tlk, "Should have a tlk in the original key set for LimitedPeers")
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
do {
assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ #if !os(tvOS)
let laterZoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
XCTAssertNotNil(laterZoneKeys, "Should have some zone keys")
XCTAssertNotNil(laterZoneKeys?.tlk, "Should have a tlk in the newly created keyset")
XCTAssertEqual(zoneKeys?.tlk?.uuid, laterZoneKeys?.tlk?.uuid, "CKKS zone should now have the same keys")
+ #else
+ let laterZoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
+ XCTAssertNil(laterZoneKeys, "Should have no Manatee zone keys for aTV")
+ #endif
+
+ let lpLaterZoneKeys = self.keys![self.limitedPeersAllowedZoneID!] as? ZoneKeys
+ XCTAssertNotNil(lpLaterZoneKeys, "Should have some zone keys for LimitedPeersAllowed")
+ XCTAssertNotNil(lpLaterZoneKeys?.tlk, "Should have a tlk in the newly created keyset for LimitedPeersAllowed")
+ XCTAssertEqual(lpZoneKeys?.tlk?.uuid, lpLaterZoneKeys?.tlk?.uuid, "CKKS zone should now have the same keys for LimitedPeersAllowed")
}
func testOctagonResetAndEstablishFail() throws {
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
_ = try self.cuttlefishContext.accountAvailable("13453464")
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
_ = try self.cuttlefishContext.accountAvailable("13453464")
}
func testResetReasonUserInitiatedReset() throws {
- // Make sure if establish fail we end up in untrusted instead of error
- self.startCKAccountStatusMock()
+ // Make sure if establish fail we end up in untrusted instead of error
+ self.startCKAccountStatusMock()
- self.cuttlefishContext.startOctagonStateMachine()
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+ self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
- _ = try self.cuttlefishContext.accountAvailable("13453464")
+ _ = try self.cuttlefishContext.accountAvailable("13453464")
let resetExpectation = self.expectation(description: "resetExpectation")
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
_ = try self.cuttlefishContext.accountAvailable("13453464")
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let initiatorContext = self.manager.context(forContainerName: OTCKContainerName,
let contextName = OTDefaultContext
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.startCKAccountStatusMock()
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.verifyDatabaseMocks()
let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
- self.tphClient.dump(withContainer: containerName, context: contextName) {
- dump, _ in
+ self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
dumpCallback.fulfill()
}
func testLegacyJoinCircleDoesNotReset() throws {
self.cuttlefishContext.startOctagonStateMachine()
self.startCKAccountStatusMock()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
_ = try self.cuttlefishContext.accountAvailable("13453464")
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
self.verifyDatabaseMocks()
}
+
+ func testCliqueResetAllSPI() throws {
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.startCKAccountStatusMock()
+ OctagonSetSOSFeatureEnabled(false)
+
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+
+ let establishAndResetExpectation = self.expectation(description: "resetExpectation")
+ let clique: OTClique
+ let otcliqueContext = OTConfigurationContext()
+ var firstCliqueIdentifier: String?
+
+ otcliqueContext.context = OTDefaultContext
+ otcliqueContext.dsid = "13453464"
+ otcliqueContext.altDSID = self.mockAuthKit.altDSID!
+ otcliqueContext.authenticationAppleID = "appleID"
+ otcliqueContext.passwordEquivalentToken = "petpetpetpetpet"
+ otcliqueContext.otControl = self.otControl
+ otcliqueContext.ckksControl = self.ckksControl
+ otcliqueContext.sbd = OTMockSecureBackup(bottleID: nil, entropy: nil)
+
+ do {
+ clique = try OTClique.newFriends(withContextData: otcliqueContext, resetReason: .testGenerated)
+ XCTAssertNotNil(clique, "Clique should not be nil")
+ XCTAssertNotNil(clique.cliqueMemberIdentifier, "Should have a member identifier after a clique newFriends call")
+ firstCliqueIdentifier = clique.cliqueMemberIdentifier
+ establishAndResetExpectation.fulfill()
+ } catch {
+ XCTFail("Shouldn't have errored making new friends everything: \(error)")
+ throw error
+ }
+ self.wait(for: [establishAndResetExpectation], timeout: 10)
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ self.verifyDatabaseMocks()
+
+ self.silentZoneDeletesAllowed = true
+
+ let newClique: OTClique
+ do {
+ newClique = try OTClique.resetProtectedData(otcliqueContext)
+ XCTAssertNotEqual(newClique.cliqueMemberIdentifier, firstCliqueIdentifier, "clique identifiers should be different")
+ } catch {
+ XCTFail("Shouldn't have errored resetting everything: \(error)")
+ throw error
+ }
+ XCTAssertNotNil(newClique, "newClique should not be nil")
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ self.verifyDatabaseMocks()
+ }
}
#endif // OCTAGON
class OctagonSOSTests: OctagonTestsBase {
func testSOSOctagonKeyConsistency() throws {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
self.startCKAccountStatusMock()
}
func testSOSOctagonKeyConsistencyLocked() throws {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
self.startCKAccountStatusMock()
}
func testSOSOctagonKeyConsistencySucceedsAfterUpdatingSOS() throws {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID!)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID!)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID!)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
self.startCKAccountStatusMock()
XCTAssertNotNil(error, "error should not be nil")
}
}
+
+ func testPreapproveSOSPeersWhenInCircle() throws {
+ self.startCKAccountStatusMock()
+
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCInCircle)
+ let peer1Preapproval = TPHashBuilder.hash(with: .SHA256, of: self.mockSOSAdapter.selfPeer.publicSigningKey.encodeSubjectPublicKeyInfo())
+
+ let peer2SOSMockPeer = self.createSOSPeer(peerID: "peer2ID")
+ self.mockSOSAdapter.trustedPeers.add(peer2SOSMockPeer)
+ let peer2Preapproval = TPHashBuilder.hash(with: .SHA256, of: peer2SOSMockPeer.publicSigningKey.encodeSubjectPublicKeyInfo())
+
+ let peer3SOSMockPeer = self.createSOSPeer(peerID: "peer3ID")
+ self.mockSOSAdapter.trustedPeers.add(peer3SOSMockPeer)
+ let peer3Preapproval = TPHashBuilder.hash(with: .SHA256, of: peer3SOSMockPeer.publicSigningKey.encodeSubjectPublicKeyInfo())
+
+ let establishTwiceExpectation = self.expectation(description: "establish should be called twice")
+ establishTwiceExpectation.expectedFulfillmentCount = 2
+
+ self.fakeCuttlefishServer.establishListener = { request in
+ XCTAssertTrue(request.hasPeer, "establish request should have a peer")
+
+ let newDynamicInfo = TPPeerDynamicInfo(data: request.peer.dynamicInfoAndSig.peerDynamicInfo, sig: request.peer.dynamicInfoAndSig.sig)
+ XCTAssertNotNil(newDynamicInfo, "should be able to make a dynamicInfo from protobuf")
+
+ XCTAssertTrue(newDynamicInfo?.preapprovals.contains(peer2Preapproval) ?? false, "Fake peer 2 should be preapproved")
+ XCTAssertTrue(newDynamicInfo?.preapprovals.contains(peer3Preapproval) ?? false, "Fake peer 3 should be preapproved")
+
+ establishTwiceExpectation.fulfill()
+ return nil
+ }
+
+ self.assertAllCKKSViewsUpload(tlkShares: 3)
+
+ // Just starting the state machine is sufficient; it should perform an SOS upgrade
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ self.verifyDatabaseMocks()
+
+ // And a reset does the right thing with preapprovals as well
+ do {
+ let arguments = OTConfigurationContext()
+ arguments.altDSID = try self.cuttlefishContext.authKitAdapter.primaryiCloudAccountAltDSID()
+ arguments.context = self.cuttlefishContext.contextID
+ arguments.otControl = self.otControl
+
+ let clique = try OTClique.newFriends(withContextData: arguments, resetReason: .testGenerated)
+ XCTAssertNotNil(clique, "Clique should not be nil")
+ } catch {
+ XCTFail("Shouldn't have errored making new friends: \(error)")
+ }
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ self.verifyDatabaseMocks()
+
+ self.wait(for: [establishTwiceExpectation], timeout: 1)
+
+ // And do we do the right thing when joining via SOS preapproval?
+ let peer2JoinExpectation = self.expectation(description: "join called")
+ self.fakeCuttlefishServer.joinListener = { request in
+ XCTAssertTrue(request.hasPeer, "establish request should have a peer")
+
+ let newDynamicInfo = TPPeerDynamicInfo(data: request.peer.dynamicInfoAndSig.peerDynamicInfo, sig: request.peer.dynamicInfoAndSig.sig)
+ XCTAssertNotNil(newDynamicInfo, "should be able to make a dynamicInfo from protobuf")
+
+ XCTAssertFalse(newDynamicInfo?.preapprovals.contains(peer1Preapproval) ?? false, "Fake peer 1 should NOT be preapproved by peer2 (as it's already in Octagon)")
+ XCTAssertTrue(newDynamicInfo?.preapprovals.contains(peer3Preapproval) ?? false, "Fake peer 3 should be preapproved by peer2")
+
+ peer2JoinExpectation.fulfill()
+
+ return nil
+ }
+
+ let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: peer2SOSMockPeer, trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
+ let peer2 = self.makeInitiatorContext(contextID: "peer2", authKitAdapter: self.mockAuthKit2, sosAdapter: peer2mockSOS)
+
+ peer2.startOctagonStateMachine()
+ self.assertEnters(context: peer2, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfTrusted(context: peer2)
+
+ self.wait(for: [peer2JoinExpectation], timeout: 1)
+ }
+
+ func testDoNotPreapproveSOSPeerWhenOutOfCircle() throws {
+ self.startCKAccountStatusMock()
+
+ // SOS returns 'trusted' peers without actually being in-circle
+ // We don't want to preapprove those peers
+
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCNotInCircle)
+ let peer1Preapproval = TPHashBuilder.hash(with: .SHA256, of: self.mockSOSAdapter.selfPeer.publicSigningKey.encodeSubjectPublicKeyInfo())
+
+ let peer2SOSMockPeer = self.createSOSPeer(peerID: "peer2ID")
+ self.mockSOSAdapter.trustedPeers.add(peer2SOSMockPeer)
+ let peer2Preapproval = TPHashBuilder.hash(with: .SHA256, of: peer2SOSMockPeer.publicSigningKey.encodeSubjectPublicKeyInfo())
+
+ let peer3SOSMockPeer = self.createSOSPeer(peerID: "peer3ID")
+ self.mockSOSAdapter.trustedPeers.add(peer3SOSMockPeer)
+ let peer3Preapproval = TPHashBuilder.hash(with: .SHA256, of: peer3SOSMockPeer.publicSigningKey.encodeSubjectPublicKeyInfo())
+
+ self.fakeCuttlefishServer.establishListener = { request in
+ XCTAssertTrue(request.hasPeer, "establish request should have a peer")
+
+ let newDynamicInfo = TPPeerDynamicInfo(data: request.peer.dynamicInfoAndSig.peerDynamicInfo, sig: request.peer.dynamicInfoAndSig.sig)
+ XCTAssertNotNil(newDynamicInfo, "should be able to make a dynamicInfo from protobuf")
+
+ XCTAssertFalse(newDynamicInfo?.preapprovals.contains(peer2Preapproval) ?? false, "Fake peer 2 should not be preapproved")
+ XCTAssertFalse(newDynamicInfo?.preapprovals.contains(peer3Preapproval) ?? false, "Fake peer 3 should not be preapproved")
+
+ return nil
+ }
+
+ self.assertResetAndBecomeTrustedInDefaultContext()
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
+
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+
+ self.verifyDatabaseMocks()
+
+ // And do we do the right thing when joining via bottle?
+ let peer2JoinExpectation = self.expectation(description: "join called")
+ self.fakeCuttlefishServer.joinListener = { request in
+ XCTAssertTrue(request.hasPeer, "establish request should have a peer")
+
+ let newDynamicInfo = TPPeerDynamicInfo(data: request.peer.dynamicInfoAndSig.peerDynamicInfo, sig: request.peer.dynamicInfoAndSig.sig)
+ XCTAssertNotNil(newDynamicInfo, "should be able to make a dynamicInfo from protobuf")
+
+ XCTAssertFalse(newDynamicInfo?.preapprovals.contains(peer1Preapproval) ?? false, "Fake peer 1 should NOT be preapproved by peer2 (as it's not in SOS)")
+ XCTAssertFalse(newDynamicInfo?.preapprovals.contains(peer3Preapproval) ?? false, "Fake peer 3 should not be preapproved by peer2 (as it's not in SOS)")
+
+ peer2JoinExpectation.fulfill()
+
+ return nil
+ }
+
+ let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: peer2SOSMockPeer, trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
+ peer2mockSOS.circleStatus = SOSCCStatus(kSOSCCNotInCircle)
+ let peer2 = self.makeInitiatorContext(contextID: "peer2", authKitAdapter: self.mockAuthKit2, sosAdapter: peer2mockSOS)
+
+ peer2.startOctagonStateMachine()
+
+ _ = self.assertJoinViaEscrowRecovery(joiningContext: peer2, sponsor: self.cuttlefishContext)
+ self.assertEnters(context: peer2, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfTrusted(context: peer2)
+
+ self.wait(for: [peer2JoinExpectation], timeout: 1)
+ }
+
+ func testRespondToNewOctagonPeerWhenUpdatingPreapprovedKeys() throws {
+ self.startCKAccountStatusMock()
+
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCInCircle)
+ let peer1Preapproval = TPHashBuilder.hash(with: .SHA256, of: self.mockSOSAdapter.selfPeer.publicSigningKey.encodeSubjectPublicKeyInfo())
+
+ let peer2SOSMockPeer = self.createSOSPeer(peerID: "peer2ID")
+ self.mockSOSAdapter.trustedPeers.add(peer2SOSMockPeer)
+ let peer2Preapproval = TPHashBuilder.hash(with: .SHA256, of: peer2SOSMockPeer.publicSigningKey.encodeSubjectPublicKeyInfo())
+
+ self.assertAllCKKSViewsUpload(tlkShares: 2)
+
+ // Just starting the state machine is sufficient; it should perform an SOS upgrade
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ let peer1ID = try self.cuttlefishContext.accountMetadataStore.getEgoPeerID()
+
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ self.verifyDatabaseMocks()
+
+ // Another peer arrives, but we miss the Octagon push
+ let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: peer2SOSMockPeer, trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
+ let joiningContext = self.makeInitiatorContext(contextID: "joiner", authKitAdapter: self.mockAuthKit2, sosAdapter: peer2mockSOS)
+ let peer2ID = self.assertJoinViaEscrowRecovery(joiningContext: joiningContext, sponsor: self.cuttlefishContext)
+
+ // Now, SOS updates its key list: we should update our preapproved keys (and then also trust the newly-joined peer)
+ let peer3SOSMockPeer = self.createSOSPeer(peerID: "peer3ID")
+ self.mockSOSAdapter.trustedPeers.add(peer3SOSMockPeer)
+ let peer3Preapproval = TPHashBuilder.hash(with: .SHA256, of: peer3SOSMockPeer.publicSigningKey.encodeSubjectPublicKeyInfo())
+
+ let updateKeysExpectation = self.expectation(description: "UpdateTrust should fire (once)")
+ self.fakeCuttlefishServer.updateListener = { [unowned self] request in
+ XCTAssertEqual(request.peerID, peer1ID, "UpdateTrust should be for peer1")
+
+ let newDynamicInfo = request.dynamicInfoAndSig.dynamicInfo()
+
+ XCTAssertFalse(newDynamicInfo.preapprovals.contains(peer1Preapproval), "Fake peer 1 should NOT be preapproved by peer1 (as it's its own keys)")
+ XCTAssertTrue(newDynamicInfo.preapprovals.contains(peer2Preapproval), "Fake peer 2 should be preapproved by original peer")
+ XCTAssertTrue(newDynamicInfo.preapprovals.contains(peer3Preapproval), "Fake peer 3 should be preapproved by original peer")
+
+ self.fakeCuttlefishServer.updateListener = nil
+ updateKeysExpectation.fulfill()
+
+ return nil
+ }
+
+ // And we'll send TLKShares to the new SOS peer and the new Octagon peer
+ self.assertAllCKKSViewsUpload(tlkShares: 2)
+
+ // to avoid CKKS race conditions (wherein it uploads each TLKShare in its own operation), send the SOS notification only to the Octagon context
+ //self.mockSOSAdapter.sendTrustedPeerSetChangedUpdate()
+ self.cuttlefishContext.trustedPeerSetChanged(self.mockSOSAdapter)
+ self.wait(for: [updateKeysExpectation], timeout: 10)
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ self.verifyDatabaseMocks()
+
+ XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer1ID, opinion: .trusts, target: peer1ID)),
+ "peer 1 should trust peer 1")
+ XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer1ID, opinion: .trusts, target: peer2ID)),
+ "peer 1 should trust peer 2")
+ }
}
#endif
class OctagonSOSUpgradeTests: OctagonTestsBase {
func testSOSUpgrade() throws {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
self.verifyDatabaseMocks()
self.assertSelfTLKSharesInCloudKit(context: self.cuttlefishContext)
- assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+
+ // Also, CKKS should be configured with the prevailing policy version
+ XCTAssertNotNil(self.injectedManager?.policy, "Should have given CKKS a TPPolicy during SOS upgrade")
+ XCTAssertEqual(self.injectedManager?.policy?.version, prevailingPolicyVersion, "Policy given to CKKS should be prevailing policy")
}
// Verify that an SOS upgrade only does one establish (and no update trust).
func testSOSUpgradeUpdateNoUpdateTrust() throws {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
}
func testSOSUpgradeAuthkitError() throws {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID!)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID!)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID!)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
// Test that we tries to perform SOS upgrade once we unlock device again
//
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
func testSOSUpgradeDuringNetworkOutage() throws {
// Test that we tries to perform SOS upgrade after a bit after a failure
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
func testSOSUpgradeStopsIfSplitGraph() throws {
// Test that we tries to perform SOS upgrade after a bit after a failure
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
}
func testSOSUpgradeWithNoTLKs() throws {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putFakeDeviceStatusesInCloudKit()
self.startCKAccountStatusMock()
func testsSOSUpgradeWithCKKSConflict() throws {
// Right after CKKS fetches for the first time, insert a new key hierarchy into CloudKit
self.silentFetchesAllowed = false
- self.expectCKFetchAndRun(beforeFinished: {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+ self.expectCKFetchAndRun {
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putFakeDeviceStatusesInCloudKit()
self.silentFetchesAllowed = true
- })
+ }
self.startCKAccountStatusMock()
}
func testSOSJoin() throws {
- if(!OctagonPerformSOSUpgrade()) {
+ if !OctagonPerformSOSUpgrade() {
return
}
self.startCKAccountStatusMock()
func testSOSJoinUponNotificationOfPreapproval() throws {
// Peer 1 becomes SOS+Octagon
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
- // Peer 2 attempts to join via preapprovalh
+ // Peer 2 attempts to join via preapproval
let peer2SOSMockPeer = self.createSOSPeer(peerID: "peer2ID")
let peer2contextID = "peer2"
let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: peer2SOSMockPeer, trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
func testSOSJoinUponNotificationOfPreapprovalRetry() throws {
// Peer 1 becomes SOS+Octagon
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID!)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID!)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID!)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
- // Peer 2 attempts to join via preapprovalh
+ // Peer 2 attempts to join via preapproval
let peer2SOSMockPeer = self.createSOSPeer(peerID: "peer2ID")
let peer2contextID = "peer2"
let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: peer2SOSMockPeer, trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
func testSOSJoinUponNotificationOfPreapprovalRetryFail() throws {
// Peer 1 becomes SOS+Octagon
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID!)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID!)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID!)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
- // Peer 2 attempts to join via preapprovalh
+ // Peer 2 attempts to join via preapproval
let peer2SOSMockPeer = self.createSOSPeer(peerID: "peer2ID")
let peer2contextID = "peer2"
let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: peer2SOSMockPeer, trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
self.cuttlefishContext.incompleteNotificationOfMachineIDListChange()
self.wait(for: [updateTrustExpectation], timeout: 10)
+ self.verifyDatabaseMocks()
assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
}
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 40 * NSEC_PER_SEC)
self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
- assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
self.verifyDatabaseMocks()
+
+ // Also, CKKS should be configured with the prevailing policy version
+ XCTAssertNotNil(self.injectedManager?.policy, "Should have given CKKS a TPPolicy during SOS upgrade")
+ XCTAssertEqual(self.injectedManager?.policy?.version, prevailingPolicyVersion, "Policy given to CKKS should be prevailing policy")
}
func testSOSDoNotAttemptUpgradeWhenPlatformDoesntSupport() throws {
let everEnteredSOSUpgrade: CKKSCondition = self.cuttlefishContext.stateMachine.stateConditions[OctagonStateAttemptSOSUpgrade] as! CKKSCondition
self.cuttlefishContext.startOctagonStateMachine()
+
+ // Cheat and even turn on CDP for the account
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
}
func testSosUpgradeAndReady() throws {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
self.startCKAccountStatusMock()
self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCInCircle)
let upgradeExpectation = self.expectation(description: "waitForOctagonUpgrade")
- self.manager.wait(forOctagonUpgrade: OTCKContainerName, context: self.otcliqueContext.context ?? "defaultContext") { error in
+ self.manager.wait(forOctagonUpgrade: OTCKContainerName, context: self.otcliqueContext.context) { error in
XCTAssertNil(error, "operation should not fail")
upgradeExpectation.fulfill()
}
}
func testSOSJoinAndBottle() throws {
- if(!OctagonPerformSOSUpgrade()) {
+ if !OctagonPerformSOSUpgrade() {
return
}
self.startCKAccountStatusMock()
}
func testSOSPeerUpdatePreapprovesNewPeer() throws {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
self.startCKAccountStatusMock()
}
func testSOSPeerUpdateOnRestartAfterMissingNotification() throws {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
self.startCKAccountStatusMock()
}
func testResetAndEstablishDoesNotReuploadSOSTLKShares() throws {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
}
func testResetAndEstablishReusesSOSKeys() throws {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
let dumpExpectation = self.expectation(description: "dump callback occurs")
var encryptionPubKey = Data()
var signingPubKey = Data()
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, error in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, error in
XCTAssertNil(error, "Should be no error dumping data")
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let permanentInfo = egoSelf!["permanentInfo"] as? Dictionary<String, AnyObject>
+ let permanentInfo = egoSelf!["permanentInfo"] as? [String: AnyObject]
XCTAssertNotNil(permanentInfo, "should have a permanent info")
let epk = permanentInfo!["encryption_pub_key"] as? Data
// And check that the pub keys are equivalent
let dumpResetExpectation = self.expectation(description: "dump callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, error in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, error in
XCTAssertNil(error, "Should be no error dumping data")
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let permanentInfo = egoSelf!["permanentInfo"] as? Dictionary<String, AnyObject>
+ let permanentInfo = egoSelf!["permanentInfo"] as? [String: AnyObject]
XCTAssertNotNil(permanentInfo, "should have a permanent info")
let epk = permanentInfo!["encryption_pub_key"] as? Data
}
func testSOSUpgradeWithFailingAuthKit() throws {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
}
func testCliqueOctagonUpgrade () throws {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
self.startCKAccountStatusMock()
self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCInCircle)
OctagonSetPlatformSupportsSOS(true)
- var clique: OTClique?
- XCTAssertNoThrow(clique = try OTClique(contextData: self.otcliqueContext))
+ let clique = OTClique(contextData: self.otcliqueContext)
XCTAssertNotNil(clique, "Clique should not be nil")
- XCTAssertNoThrow(try clique!.waitForOctagonUpgrade(), "Upgrading should pass")
+ XCTAssertNoThrow(try clique.waitForOctagonUpgrade(), "Upgrading should pass")
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
}
func testCliqueOctagonUpgradeFail () throws {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putSelfTLKSharesInCloudKit()
+ self.saveTLKMaterialToKeychain()
self.startCKAccountStatusMock()
XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
OctagonSetPlatformSupportsSOS(true)
- var clique: OTClique?
- XCTAssertNoThrow(clique = try OTClique(contextData: self.otcliqueContext))
+ let clique = OTClique(contextData: self.otcliqueContext)
XCTAssertNotNil(clique, "Clique should not be nil")
- XCTAssertThrowsError(try clique!.waitForOctagonUpgrade(), "Upgrading should fail")
+ XCTAssertThrowsError(try clique.waitForOctagonUpgrade(), "Upgrading should fail")
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer1ID, opinion: .trustsByPreapproval, target: peer2ID)),
"peer 1 should trust peer 3 by preapproval")
-
XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer2ID, opinion: .trusts, target: peer1ID)),
"peer 2 should trust peer 1")
XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer2ID, opinion: .trusts, target: peer3ID)),
// And if peer3 decides to reupgrade, but it shouldn't: there's no potentially-trusted peer that preapproves it
let upgradeExpectation = self.expectation(description: "sosUpgrade call returns")
- peer3.attemptSOSUpgrade() { error in
+ peer3.attemptSOSUpgrade { error in
XCTAssertNotNil(error, "should be an error performing an SOS upgrade (the second time)")
upgradeExpectation.fulfill()
}
// And "wait for upgrade" does something reasonable too
let upgradeWaitExpectation = self.expectation(description: "sosWaitForUpgrade call returns")
- peer3.waitForOctagonUpgrade() { error in
+ peer3.waitForOctagonUpgrade { error in
XCTAssertNotNil(error, "should be an error waiting for an SOS upgrade (the second time)")
upgradeWaitExpectation.fulfill()
}
#import "KeychainCircle/KCJoiningRequestSession+Internal.h"
#import "KeychainCircle/KCJoiningAcceptSession+Internal.h"
#import <KeychainCircle/KCJoiningMessages.h>
+#import <KeychainCircle/PairingChannel.h>
#import <TrustedPeers/TrustedPeers.h>
#import <TrustedPeers/TPHash.h>
#import "keychain/ckks/CKKSTLKShare.h"
#import "keychain/ckks/CKKSAnalytics.h"
#import "keychain/ckks/CloudKitCategories.h"
+#import "keychain/ckks/CKKSCurrentKeyPointer.h"
#import "keychain/ot/OctagonControlServer.h"
#import "keychain/ot/categories/OTAccountMetadataClassC+KeychainSupport.h"
#import "keychain/ot/categories/OctagonEscrowRecoverer.h"
+#import "KeychainCircle/generated_source/KCInitialMessageData.h"
+#import "keychain/ot/proto/generated_source/OTPairingMessage.h"
+#import "keychain/ot/proto/generated_source/OTSponsorToApplicantRound1M2.h"
+#import "keychain/ot/proto/generated_source/OTApplicantToSponsorRound2M1.h"
+#import "keychain/ot/proto/generated_source/OTSponsorToApplicantRound2M2.h"
+
#import "keychain/otctl/OTControlCLI.h"
// Also, we're going to need whatever TPH needs.
}
class OTMockAuthKitAdapter: OTAuthKitAdapter {
+
// A nil altDSID means 'no authkit account'
var altDSID: String?
var hsa2: Bool
+ var isDemoAccount: Bool
let currentMachineID: String
var otherDevices: Set<String>
self.otherDevices = otherDevices
self.excludeDevices = Set()
self.hsa2 = true
+ self.isDemoAccount = false
self.listeners = CKKSListenerCollection<OTAuthKitAdapterNotifier>(name: "test-authkit")
}
// TODO: do we need to examine altDSID here?
return self.hsa2
}
+ func accountIsDemoAccount(_ error: NSErrorPointer) -> Bool {
+ return self.isDemoAccount
+ }
func machineID() throws -> String {
// TODO: throw if !accountPresent
}
}
-class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
+class OctagonTestsBase: CloudKitKeychainSyncingMockXCTest {
var tmpPath: String!
var tmpURL: URL!
var otcliqueContext: OTConfigurationContext!
+ var intendedCKKSZones: Set<CKRecordZone.ID>!
var manateeZoneID: CKRecordZone.ID!
+ var limitedPeersAllowedZoneID: CKRecordZone.ID!
+
var fakeCuttlefishServer: FakeCuttlefishServer!
var fakeCuttlefishCreator: FakeCuttlefishInvocableCreator!
var tphClient: Client!
var otControl: OTControl!
var otXPCProxy: ProxyXPCConnection!
+
var otControlEntitlementBearer: FakeOTControlEntitlementBearer!
var otControlEntitlementChecker: OTControlProtocol!
var otControlCLI: OTControlCLI!
override static func setUp() {
UserDefaults.standard.register(defaults: ["com.apple.CoreData.ConcurrencyDebug": 1])
- OctagonSetShouldPerformInitialization(true)
- SecCKKSEnable()
super.setUp()
// Set the global bool to TRUE
OctagonSetIsEnabled(true)
+ // Set the global CKKS bool to TRUE
+ SecCKKSEnable()
+
// Until we can reasonably run SOS in xctest, this must be off. Note that this makes our tests
// not accurately reproduce what a real device would do.
OctagonSetPlatformSupportsSOS(false)
- // Tell SecDb not to initialize the manager (as we haven't made our fake one yet).
- // Each test is responsible for initialization, to allow for pre-test setup
- OctagonSetShouldPerformInitialization(false)
-
- let actualDeviceAdapter = OTDeviceInformationActualAdapter()
- self.mockDeviceInfo = OTMockDeviceInfoAdapter(modelID: actualDeviceAdapter.modelID(),
- deviceName: actualDeviceAdapter.deviceName(),
- serialNumber: NSUUID().uuidString,
- osVersion: actualDeviceAdapter.osVersion())
-
- super.setUp()
-
- // Octagon must initialize the views
- self.automaticallyBeginCKKSViewCloudKitOperation = false
+ if self.mockDeviceInfo == nil {
+ let actualDeviceAdapter = OTDeviceInformationActualAdapter()
+ self.mockDeviceInfo = OTMockDeviceInfoAdapter(modelID: actualDeviceAdapter.modelID(),
+ deviceName: actualDeviceAdapter.deviceName(),
+ serialNumber: NSUUID().uuidString,
+ osVersion: actualDeviceAdapter.osVersion())
+ }
- // The CKKStests use the "keychain" view heavily, but that causes issues in Octagon as it isn't in the Octagon policy.
- // Replace it with the Manatee view, unless you're on an appleTV: in that case, make it the LimitedPeersAllowed view
- self.injectedManager!.clearAllViews()
- #if !os(tvOS)
- self.ckksViews = NSMutableSet(array: [self.injectedManager!.findOrCreateView("Manatee")])
self.manateeZoneID = CKRecordZone.ID(zoneName: "Manatee")
- #else
- self.ckksViews = NSMutableSet(array: [self.injectedManager!.findOrCreateView("LimitedPeersAllowed")])
- self.manateeZoneID = CKRecordZone.ID(zoneName: "LimitedPeersAllowed")
- #endif
+ self.limitedPeersAllowedZoneID = CKRecordZone.ID(zoneName: "LimitedPeersAllowed")
+
+ // We'll use this set to limit the views that CKKS brings up in the tests (mostly for performance reasons)
+ if self.intendedCKKSZones == nil {
+ if self.mockDeviceInfo.mockModelID.contains("AppleTV") {
+ self.intendedCKKSZones = Set([
+ self.limitedPeersAllowedZoneID!,
+ ])
+ } else {
+ self.intendedCKKSZones = Set([
+ self.limitedPeersAllowedZoneID!,
+ self.manateeZoneID!,
+ ])
+ }
+ }
+ self.ckksZones = NSMutableSet(array: Array(self.intendedCKKSZones))
- self.zones!.removeAllObjects()
- self.zones![self.manateeZoneID!] = FakeCKZone(zone: self.manateeZoneID!)
+ // Create the zones, so we can inject them into our fake cuttlefish server
+ self.zones = [:]
+ self.keys = [:]
+ self.ckksZones.forEach { obj in
+ let zoneID = obj as! CKRecordZone.ID
+ self.zones![zoneID] = FakeCKZone(zone: zoneID)
+ }
// Asserting a type on self.zones seems to duplicate the dictionary, but not deep-copy the contents
// We'll use them as NSMutableDictionaries, I guess
self.otFollowUpController = OTMockFollowUpController()
- // Octagon requires the self peer keys to be persisted in the keychain
- saveToKeychain(keyPair: self.mockSOSAdapter.selfPeer.signingKey, label: "com.apple.securityd.sossigningkey")
- saveToKeychain(keyPair: self.mockSOSAdapter.selfPeer.encryptionKey, label: "com.apple.securityd.sosencryptionkey")
-
self.mockAuthKit = OTMockAuthKitAdapter(altDSID: UUID().uuidString, machineID: "MACHINE1", otherDevices: ["MACHINE2", "MACHINE3"])
self.mockAuthKit2 = OTMockAuthKitAdapter(altDSID: self.mockAuthKit.altDSID, machineID: "MACHINE2", otherDevices: ["MACHINE1", "MACHINE3"])
self.mockAuthKit3 = OTMockAuthKitAdapter(altDSID: self.mockAuthKit.altDSID, machineID: "MACHINE3", otherDevices: ["MACHINE1", "MACHINE2"])
- // By default, not in SOS when test starts
- // And under octagon, SOS trust is not essential
- self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCNotInCircle)
- self.mockSOSAdapter.essential = false
-
let tphInterface = TrustedPeersHelperSetupProtocol(NSXPCInterface(with: TrustedPeersHelperProtocol.self))
self.tphXPCProxy = ProxyXPCConnection(self.tphClient!, interface: tphInterface)
- self.manager = OTManager(sosAdapter: self.mockSOSAdapter,
- authKitAdapter: self.mockAuthKit,
- deviceInformationAdapter: self.mockDeviceInfo,
- apsConnectionClass: FakeAPSConnection.self,
- escrowRequestClass: OTMockSecEscrowRequest.self,
- loggerClass: OTMockLogger.self,
- lockStateTracker: self.lockStateTracker,
- accountStateTracker: self.accountStateTracker,
- cuttlefishXPCConnection: tphXPCProxy.connection(),
- cdpd: self.otFollowUpController)
+ self.disableConfigureCKKSViewManagerWithViews = true
+
+ // Now, perform further test initialization (including temporary keychain creation)
+ super.setUp()
+
+ self.injectedManager!.setSyncingViewsAllowList(Set(self.intendedCKKSZones!.map { $0.zoneName }))
+
+ // Ensure we've made the CKKSKeychainView objects we're interested in
+ self.ckksZones.forEach { obj in
+ let zoneID = obj as! CKRecordZone.ID
+ self.ckksViews.add(self.injectedManager!.findOrCreateView(zoneID.zoneName))
+ }
+
+ // Double-check that the world of zones and views looks like what we expect
+ XCTAssertEqual(self.ckksZones as? Set<CKRecordZone.ID>, self.intendedCKKSZones, "should still operate on our expected zones only")
+ XCTAssertEqual(self.ckksZones.count, self.ckksViews.count, "Should have the same number of views as expected zones")
+ XCTAssertEqual(self.ckksZones.count, self.zones!.count, "Should have the same number of fake zones as expected zones")
+
+ XCTAssertEqual(Set(self.ckksViews.map { ($0 as! CKKSKeychainView).zoneName }),
+ Set(self.ckksZones.map { ($0 as! CKRecordZone.ID).zoneName }),
+ "ckksViews should match ckksZones")
+
+ // Octagon must initialize the views
+ self.automaticallyBeginCKKSViewCloudKitOperation = false
- OTManager.resetManager(true, to: self.manager)
+ // Octagon requires the self peer keys to be persisted in the keychain
+ saveToKeychain(keyPair: self.mockSOSAdapter.selfPeer.signingKey, label: "com.apple.securityd.sossigningkey")
+ saveToKeychain(keyPair: self.mockSOSAdapter.selfPeer.encryptionKey, label: "com.apple.securityd.sosencryptionkey")
+
+ // By default, not in SOS when test starts
+ // And under octagon, SOS trust is not essential
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCNotInCircle)
+ self.mockSOSAdapter.essential = false
self.cuttlefishContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
self.otXPCProxy = ProxyXPCConnection(self.otControlEntitlementChecker!, interface: OTSetupControlProtocol(NSXPCInterface(with: OTControlProtocol.self)))
self.otControl = OTControl(connection: self.otXPCProxy.connection(), sync: true)
-
self.otControlCLI = OTControlCLI(otControl: self.otControl)
self.otcliqueContext = OTConfigurationContext()
self.otcliqueContext.otControl = self.otControl
}
+ override func setUpOTManager(_ cloudKitClassDependencies: CKKSCloudKitClassDependencies) -> OTManager {
+ self.manager = OTManager(sosAdapter: self.mockSOSAdapter,
+ authKitAdapter: self.mockAuthKit,
+ deviceInformationAdapter: self.mockDeviceInfo,
+ apsConnectionClass: FakeAPSConnection.self,
+ escrowRequestClass: OTMockSecEscrowRequest.self,
+ loggerClass: OTMockLogger.self,
+ lockStateTracker: CKKSLockStateTracker(),
+ cloudKitClassDependencies: cloudKitClassDependencies,
+ cuttlefishXPCConnection: tphXPCProxy.connection(),
+ cdpd: self.otFollowUpController)
+ return self.manager
+ }
+
override func tearDown() {
+ // Just to be sure
+ self.verifyDatabaseMocks()
+
let statusExpectation = self.expectation(description: "status callback occurs")
self.cuttlefishContext.rpcStatus { _, _ in
statusExpectation.fulfill()
XCTAssertTrue(self.manager.allContextsPause(10 * NSEC_PER_SEC), "All cuttlefish contexts should pause")
+ self.tphClient.containerMap.removeAllContainers()
+
super.tearDown()
+
+ self.cuttlefishContext = nil
+ self.manager = nil
+
+ self.otcliqueContext = nil
+
+ self.manateeZoneID = nil
+ self.limitedPeersAllowedZoneID = nil
+
+ self.fakeCuttlefishServer = nil
+ self.fakeCuttlefishCreator = nil
+ self.tphClient = nil
+ self.tphXPCProxy = nil
+
+ self.accountAltDSID = nil
+
+ self.mockAuthKit = nil
+ self.mockAuthKit2 = nil
+ self.mockAuthKit3 = nil
+
+ self.mockDeviceInfo = nil
+
+ self.otControl = nil
+ self.otXPCProxy = nil
+
+ self.otControlEntitlementBearer = nil
+ self.otControlEntitlementChecker = nil
+ self.otControlCLI = nil
+
+ self.otFollowUpController = nil
}
override func managedViewList() -> Set<String> {
- #if !os(tvOS)
- return Set(["Manatee"])
- #else
- return Set(["LimitedPeersAllowed"])
- #endif
+ if(self.overrideUseCKKSViewsFromPolicy) {
+ let viewNames = self.ckksZones.map { ($0 as! CKRecordZone.ID).zoneName }
+ return Set(viewNames)
+ } else {
+ // We only want to return the 'base' set of views here; not the full set.
+ // This should go away when CKKS4A is enabled...
+ #if !os(tvOS)
+ return Set(["LimitedPeersAllowed", "Manatee"])
+ #else
+ return Set(["LimitedPeersAllowed"])
+ #endif
+ }
}
func fetchEgoPeerID() -> String {
return ret
}
- func setAllowListToCurrentAuthKit(container: String, context: String) {
+ func setAllowListToCurrentAuthKit(container: String, context: String, accountIsDemo: Bool) {
let allowListExpectation = self.expectation(description: "set allow list callback occurs")
+ let honorIDMSListChanges = accountIsDemo ? false : true
self.tphClient.setAllowedMachineIDsWithContainer(container,
context: context,
- allowedMachineIDs: self.mockAuthKit.currentDeviceList()) { _, error in
+ allowedMachineIDs: self.mockAuthKit.currentDeviceList(), honorIDMSListChanges: honorIDMSListChanges) { _, error in
XCTAssertNil(error, "Should be no error setting allow list")
allowListExpectation.fulfill()
}
func assertEnters(context: OTCuttlefishContext, state: String, within: UInt64) {
XCTAssertEqual(0, (context.stateMachine.stateConditions[state] as! CKKSCondition).wait(within), "State machine should enter '\(state)'")
- if(state == OctagonStateReady || state == OctagonStateUntrusted) {
+ if state == OctagonStateReady || state == OctagonStateUntrusted {
XCTAssertEqual(0, context.stateMachine.paused.wait(10 * NSEC_PER_SEC), "State machine should pause soon")
}
}
func assertConsidersSelfTrusted(context: OTCuttlefishContext, isLocked: Bool = false) {
XCTAssertEqual(context.currentMemoizedTrustState(), .TRUSTED, "Trust state (for \(context)) should be trusted")
+ let accountMetadata = try! context.accountMetadataStore.loadOrCreateAccountMetadata()
+ XCTAssertEqual(accountMetadata.attemptedJoin, .ATTEMPTED, "Should have 'attempted a join'")
+
let statusexpectation = self.expectation(description: "trust status returns")
let configuration = OTOperationConfiguration()
configuration.timeoutWaitForCKAccount = 500 * NSEC_PER_MSEC
cliqueConfiguration.dsid = "1234"
cliqueConfiguration.altDSID = self.mockAuthKit.altDSID!
cliqueConfiguration.otControl = self.otControl
- let otclique = try! OTClique(contextData: cliqueConfiguration)
+ let otclique = OTClique(contextData: cliqueConfiguration)
let status = otclique.fetchStatus(nil)
XCTAssertEqual(status, .in, "OTClique API should return (trusted)")
self.wait(for: [statusexpectation], timeout: 10)
}
+ func assertConsidersSelfWaitingForCDP(context: OTCuttlefishContext) {
+ XCTAssertEqual(context.currentMemoizedTrustState(), .UNKNOWN, "Trust state (for \(context)) should be unknown")
+ let statusexpectation = self.expectation(description: "trust status returns")
+ let configuration = OTOperationConfiguration()
+ configuration.timeoutWaitForCKAccount = 500 * NSEC_PER_MSEC
+ context.rpcTrustStatus(configuration) { egoStatus, _, _, _, _ in
+ // TODO: separate 'untrusted' and 'no trusted peers for account yet'
+ XCTAssertTrue([.notIn, .absent].contains(egoStatus), "Self peer (for \(context)) should be distrusted or absent")
+ statusexpectation.fulfill()
+ }
+ self.wait(for: [statusexpectation], timeout: 10)
+
+ XCTAssertEqual(self.fetchCDPStatus(context: context), .disabled, "CDP status should be 'disabled'")
+ }
+
func assertAccountAvailable(context: OTCuttlefishContext) {
XCTAssertEqual(context.currentMemoizedAccountState(), .ACCOUNT_AVAILABLE, "Account state (for \(context)) should be 'available''")
}
XCTAssertEqual(context.currentMemoizedAccountState(), .NO_ACCOUNT, "Account state (for \(context)) should be no account")
}
+ func fetchCDPStatus(context: OTCuttlefishContext) -> OTCDPStatus {
+ let config = OTConfigurationContext()
+ config.context = context.contextID
+ config.otControl = self.otControl
+
+ var error: NSError?
+ let cdpstatus = OTClique.getCDPStatus(config, error: &error)
+ XCTAssertNil(error, "Should have no error fetching CDP status")
+
+ return cdpstatus
+ }
+
func assertTrusts(context: OTCuttlefishContext, includedPeerIDCount: Int, excludedPeerIDCount: Int) {
let dumpCallback = self.expectation(description: "dump callback occurs")
self.tphClient.dumpEgoPeer(withContainer: context.containerName, context: context.contextID) { _, _, _, dynamicInfo, error in
func restartCKKSViews() {
let viewNames = self.ckksViews.map { ($0 as! CKKSKeychainView).zoneName }
self.ckksViews.removeAllObjects()
+
+ self.injectedManager!.resetSyncingPolicy()
+
for view in viewNames {
self.ckksViews.add(self.injectedManager!.restartZone(view))
}
}
}
+ func sendAllCKKSViewsZoneChanged() {
+ for expectedView in self.ckksZones {
+ let view = self.injectedManager?.findView((expectedView as! CKRecordZone.ID).zoneName)
+ XCTAssertNotNil(view, "Should have a view '\(expectedView)'")
+ view!.notifyZoneChange(nil)
+ }
+ }
+
func assert(ckks: CKKSKeychainView, enters: String, within: UInt64) {
XCTAssertEqual(0, (ckks.keyHierarchyConditions[enters] as! CKKSCondition).wait(within), "CKKS state machine should enter '\(enters)' (currently '\(ckks.keyHierarchyState)')")
}
func assertAllCKKSViews(enter: String, within: UInt64) {
- for view in self.ckksViews {
- self.assert(ckks: view as! CKKSKeychainView, enters: enter, within: within)
+ for expectedView in self.ckksZones {
+ let view = self.injectedManager?.findView((expectedView as! CKRecordZone.ID).zoneName)
+ XCTAssertNotNil(view, "Should have a view '\(expectedView)'")
+ self.assert(ckks: view!, enters: enter, within: within)
}
}
func assertAllCKKSViewsUploadKeyHierarchy(tlkShares: UInt) {
- self.ckksViews.forEach { view in
- self.expectCKModifyKeyRecords(3, currentKeyPointerRecords: 3, tlkShareRecords: tlkShares, zoneID: (view as! CKKSKeychainView).zoneID)
+ for expectedView in self.ckksZones {
+ let view = self.injectedManager?.findView((expectedView as! CKRecordZone.ID).zoneName)
+ XCTAssertNotNil(view, "Should have a view '\(expectedView)'")
+ self.expectCKModifyKeyRecords(3, currentKeyPointerRecords: 3, tlkShareRecords: tlkShares, zoneID: view!.zoneID)
}
}
func assertAllCKKSViewsUpload(tlkShares: UInt) {
- self.ckksViews.forEach { view in
- self.expectCKModifyKeyRecords(0, currentKeyPointerRecords: 0, tlkShareRecords: tlkShares, zoneID: (view as! CKKSKeychainView).zoneID)
+ for expectedView in self.ckksZones {
+ self.expectCKModifyKeyRecords(0, currentKeyPointerRecords: 0, tlkShareRecords: tlkShares, zoneID: expectedView as! CKRecordZone.ID)
+ }
+ }
+
+ func putFakeKeyHierarchiesInCloudKit() {
+ self.ckksZones.forEach { zone in
+ self.putFakeKeyHierarchy(inCloudKit: zone as! CKRecordZone.ID)
+ }
+ }
+
+ func putSelfTLKSharesInCloudKit() {
+ self.ckksZones.forEach { zone in
+ self.putSelfTLKShares(inCloudKit: zone as! CKRecordZone.ID)
+ }
+ }
+
+ func putFakeDeviceStatusesInCloudKit() {
+ self.ckksZones.forEach { zone in
+ self.putFakeDeviceStatus(inCloudKit: zone as! CKRecordZone.ID)
+ }
+ }
+
+ func saveTLKMaterialToKeychain() {
+ self.ckksZones.forEach { zone in
+ self.saveTLKMaterial(toKeychain: zone as! CKRecordZone.ID)
}
}
self.wait(for: [resetExpectation], timeout: 30)
}
- func putSelfTLKShareInCloudKit(context: OTCuttlefishContext, zoneID: CKRecordZone.ID) throws {
- let accountMetadata = try context.accountMetadataStore.loadOrCreateAccountMetadata()
- let peerKeys: OctagonSelfPeerKeys = try loadEgoKeysSync(peerID: accountMetadata.peerID)
+ func putSelfTLKSharesInCloudKit(context: OTCuttlefishContext) throws {
+ try self.ckksZones.forEach { zone in
+ try self.putTLKShareInCloudKit(to: context, from: context, zoneID: zone as! CKRecordZone.ID)
+ }
+ }
+
+ func putAllTLKSharesInCloudKit(to: OTCuttlefishContext, from: OTCuttlefishContext) throws {
+ try self.ckksZones.forEach { zone in
+ try self.putTLKShareInCloudKit(to: to, from: from, zoneID: zone as! CKRecordZone.ID)
+ }
+ }
+
+ func putTLKShareInCloudKit(to: OTCuttlefishContext, from: OTCuttlefishContext, zoneID: CKRecordZone.ID) throws {
+ let fromAccountMetadata = try from.accountMetadataStore.loadOrCreateAccountMetadata()
+ let fromPeerKeys: OctagonSelfPeerKeys = try loadEgoKeysSync(peerID: fromAccountMetadata.peerID)
+
+ let toAccountMetadata = try to.accountMetadataStore.loadOrCreateAccountMetadata()
+ let toPeerKeys: OctagonSelfPeerKeys = try loadEgoKeysSync(peerID: toAccountMetadata.peerID)
+
+ let zoneKeys = self.keys![zoneID] as! ZoneKeys
+ self.putTLKShare(inCloudKit: zoneKeys.tlk!, from: fromPeerKeys, to: toPeerKeys, zoneID: zoneID)
+ }
+
+ func putRecoveryKeyTLKSharesInCloudKit(recoveryKey: String, salt: String) throws {
+ try self.ckksZones.forEach { zone in
+ try self.putRecoveryKeyTLKShareInCloudKit(recoveryKey: recoveryKey, salt: salt, zoneID: zone as! CKRecordZone.ID)
+ }
+ }
+
+ func putRecoveryKeyTLKShareInCloudKit(recoveryKey: String, salt: String, zoneID: CKRecordZone.ID) throws {
+ let recoveryKeys = try RecoveryKey(recoveryKeyString: recoveryKey, recoverySalt: salt)
+
let zoneKeys = self.keys![zoneID] as! ZoneKeys
- self.putTLKShare(inCloudKit: zoneKeys.tlk!, from: peerKeys, to: peerKeys, zoneID: zoneID)
+ self.putTLKShare(inCloudKit: zoneKeys.tlk!, from: recoveryKeys.peerKeys, to: recoveryKeys.peerKeys, zoneID: zoneID)
}
func assertSelfTLKSharesInCloudKit(context: OTCuttlefishContext) {
}
}
- func assertTLKShareInCloudKit(receiverPeerID: String, senderPeerID: String, zoneID: CKRecordZone.ID) throws {
- let zone = self.zones![zoneID] as! FakeCKZone
+ func tlkShareInCloudKit(receiverPeerID: String, senderPeerID: String, zoneID: CKRecordZone.ID) throws -> Bool {
+ guard let zone = self.zones![zoneID] as? FakeCKZone else {
+ return false
+ }
let tlkShares = zone.currentDatabase.allValues.filter { ($0 as? CKRecord)?.recordType == SecCKRecordTLKShareType }.map { CKKSTLKShareRecord(ckRecord: $0 as! CKRecord) }
for share in tlkShares {
if share.share.receiverPeerID == receiverPeerID && share.senderPeerID == senderPeerID {
// Found one!
- return
+ return true
}
}
+ return false
+ }
- XCTFail("Unable to find a TLKShare for peer ID \(String(describing: receiverPeerID)) sent by \(String(describing: senderPeerID))")
+ func assertTLKShareInCloudKit(receiverPeerID: String, senderPeerID: String, zoneID: CKRecordZone.ID) throws {
+ XCTAssertTrue(try self.tlkShareInCloudKit(receiverPeerID: receiverPeerID, senderPeerID: senderPeerID, zoneID: zoneID),
+ "Should have found a TLKShare for peerID \(String(describing: receiverPeerID)) sent by \(String(describing: senderPeerID)) for \(zoneID)")
}
func assertMIDList(context: OTCuttlefishContext,
}
for allowedMID in allowed {
- var err : NSError?
+ var err: NSError?
let onList = context.machineID(onMemoizedList: allowedMID, error: &err)
XCTAssertNil(err, "Should not have failed determining memoized list state")
}
for disallowedMID in disallowed {
- var err : NSError?
+ var err: NSError?
let onList = context.machineID(onMemoizedList: disallowedMID, error: &err)
XCTAssertNil(err, "Should not have failed determining memoized list state")
}
if result != nil {
- if let dictionary = result as? Dictionary<CFString, Any> {
+ if let dictionary = result as? [CFString: Any] {
secret = dictionary[kSecValueData] as? Data
} else {
throw ContainerError.failedToLoadSecretDueToType
deviceInformationAdapter: self.makeInitiatorDeviceInfoAdapter())
}
- @discardableResult func assertResetAndBecomeTrustedInDefaultContext() -> String {
- self.cuttlefishContext.startOctagonStateMachine()
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+ @discardableResult
+ func assertResetAndBecomeTrustedInDefaultContext() -> String {
+ let ret = self.assertResetAndBecomeTrusted(context: self.cuttlefishContext)
+
+ // And, the default context runs CKKS:
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ self.verifyDatabaseMocks()
+ self.assertSelfTLKSharesInCloudKit(context: self.cuttlefishContext)
+
+ return ret
+ }
+
+ @discardableResult
+ func assertResetAndBecomeTrusted(context: OTCuttlefishContext) -> String {
+ context.startOctagonStateMachine()
+ XCTAssertNoThrow(try context.setCDPEnabled())
+ self.assertEnters(context: context, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
do {
- let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, resetReason: .testGenerated)
+ let arguments = OTConfigurationContext()
+ arguments.altDSID = try context.authKitAdapter.primaryiCloudAccountAltDSID()
+ arguments.context = context.contextID
+ arguments.otControl = self.otControl
+
+ let clique = try OTClique.newFriends(withContextData: arguments, resetReason: .testGenerated)
XCTAssertNotNil(clique, "Clique should not be nil")
} catch {
XCTFail("Shouldn't have errored making new friends: \(error)")
}
- // Now, we should be in 'ready'
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
- self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
- assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
- self.verifyDatabaseMocks()
- self.assertSelfTLKSharesInCloudKit(context: self.cuttlefishContext)
- return try! self.cuttlefishContext.accountMetadataStore.getEgoPeerID()
+ self.assertEnters(context: context, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfTrusted(context: context)
+
+ return try! context.accountMetadataStore.getEgoPeerID()
}
+ @discardableResult
func assertJoinViaEscrowRecovery(joiningContext: OTCuttlefishContext, sponsor: OTCuttlefishContext) -> String {
do {
joiningContext.startOctagonStateMachine()
}
}
+ func assertJoinViaProximitySetup(joiningContext: OTCuttlefishContext, sponsor: OTCuttlefishContext) -> String {
+ do {
+ joiningContext.startOctagonStateMachine()
+ self.assertEnters(context: joiningContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+
+ let (sponsorPairingChannel, initiatorPairingChannel) = self.setupPairingChannels(initiator: joiningContext, sponsor: sponsor)
+
+ let firstInitiatorPacket = self.sendPairingExpectingReply(channel: initiatorPairingChannel, packet: nil, reason: "session initialization")
+ let sponsorEpochPacket = self.sendPairingExpectingReply(channel: sponsorPairingChannel, packet: firstInitiatorPacket, reason: "sponsor epoch")
+ let initiatorIdentityPacket = self.sendPairingExpectingReply(channel: initiatorPairingChannel, packet: sponsorEpochPacket, reason: "initiator identity")
+ let sponsorVoucherPacket = self.sendPairingExpectingCompletionAndReply(channel: sponsorPairingChannel, packet: initiatorIdentityPacket, reason: "sponsor voucher")
+ self.sendPairingExpectingCompletion(channel: initiatorPairingChannel, packet: sponsorVoucherPacket, reason: "initiator completion")
+
+ self.assertEnters(context: joiningContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ self.assertConsidersSelfTrusted(context: joiningContext)
+
+ return try joiningContext.accountMetadataStore.getEgoPeerID()
+ } catch {
+ XCTFail("Expected no error: \(error)")
+ return "failed"
+ }
+ }
+
func assertSelfOSVersion(_ osVersion: String) {
let statusExpectation = self.expectation(description: "status callback occurs")
- self.tphClient.dumpEgoPeer(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID, reply: { _, _, stableInfo, _, error in
+ self.tphClient.dumpEgoPeer(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { _, _, stableInfo, _, error in
XCTAssertNil(error, "should be no error dumping ego peer")
XCTAssertEqual(stableInfo?.osVersion, osVersion, "os version should be as required")
statusExpectation.fulfill()
- })
+ }
self.wait(for: [statusExpectation], timeout: 2)
}
machineID: "asdf",
bottleSalt: "123456789",
bottleID: UUID().uuidString,
- modelID: "asdf",
+ modelID: "iPhone9,1",
deviceName: "asdf",
serialNumber: "1234",
osVersion: "asdf",
policyVersion: nil,
policySecrets: nil,
signingPrivKeyPersistentRef: nil,
- encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+ encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
XCTAssertNil(error, "Should be no error preparing identity")
XCTAssertNotNil(peerID, "Should be a peer ID")
XCTAssertNotNil(permanentInfo, "Should have a permenent info")
machineID: "asdf",
bottleSalt: "123456789",
bottleID: UUID().uuidString,
- modelID: "asdf",
+ modelID: "iPhone9,1",
deviceName: "asdf",
serialNumber: "1234",
osVersion: "asdf",
policyVersion: nil,
policySecrets: nil,
signingPrivKeyPersistentRef: nil,
- encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+ encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
XCTAssertNil(error, "Should be no error preparing identity")
XCTAssertNotNil(peerID, "Should be a peer ID")
XCTAssertNotNil(permanentInfo, "Should have a permenent info")
machineID: "asdf",
bottleSalt: "123456789",
bottleID: UUID().uuidString,
- modelID: "asdf",
+ modelID: "iPhone9,1",
deviceName: "asdf",
serialNumber: "1234",
osVersion: "asdf",
policyVersion: nil,
policySecrets: nil,
signingPrivKeyPersistentRef: nil,
- encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+ encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
XCTAssertNil(error, "Should be no error preparing identity")
XCTAssertNotNil(peerID, "Should be a peer ID")
XCTAssertNotNil(permanentInfo, "Should have a permenent info")
self.wait(for: [tphPrepareExpectation2], timeout: 10)
}
- func testAccountSave() throws {
- let contextName = OTDefaultContext
- let containerName = OTCKContainerName
-
- self.startCKAccountStatusMock()
-
- // Before resetAndEstablish, there shouldn't be any stored account state
- XCTAssertThrowsError(try OTAccountMetadataClassC.loadFromKeychain(forContainer: containerName, contextID: contextName), "Before doing anything, loading a non-existent account state should fail")
-
- let resetAndEstablishExpectation = self.expectation(description: "resetAndEstablish callback occurs")
- self.manager.resetAndEstablish(containerName,
- context: contextName,
- altDSID: "new altDSID",
- resetReason: .testGenerated) { resetError in
- XCTAssertNil(resetError, "Should be no error calling resetAndEstablish")
- resetAndEstablishExpectation.fulfill()
- }
-
- self.wait(for: [resetAndEstablishExpectation], timeout: 10)
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
-
- let selfPeerID = try self.cuttlefishContext.accountMetadataStore.loadOrCreateAccountMetadata().peerID
-
- // After resetAndEstablish, you should be able to see the persisted account state
- do {
- let accountState = try OTAccountMetadataClassC.loadFromKeychain(forContainer: containerName, contextID: contextName)
- XCTAssertEqual(selfPeerID, accountState.peerID, "Saved account state should have the same peer ID that prepare returned")
- } catch {
- XCTFail("error loading account state: \(error)")
- }
- }
-
- func testLoadToNoAccount() throws {
- // No CloudKit account, either
- self.accountStatus = .noAccount
- self.startCKAccountStatusMock()
-
- // With no identity and AuthKit reporting no iCloud account, Octagon should go directly into 'no account'
- self.mockAuthKit.altDSID = nil
-
- let asyncExpectation = self.expectation(description: "dispatch works")
- let quiescentExpectation = self.expectation(description: "quiescence has been determined")
- DispatchQueue.global(qos: .userInitiated).async { [weak self] in
- asyncExpectation.fulfill()
-
- let c = self!.cuttlefishContext.stateMachine.paused
- XCTAssertEqual(0, c.wait(10 * NSEC_PER_SEC), "State machine should become quiescent")
- quiescentExpectation.fulfill()
- }
- // Wait for the block above to fire before continuing
- self.wait(for: [asyncExpectation], timeout: 10)
-
- // Run initialization, like the real secd will do
- OctagonInitialize()
-
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
- XCTAssertTrue(self.cuttlefishContext.stateMachine.isPaused(), "State machine should be stopped")
- self.assertNoAccount(context: self.cuttlefishContext)
-
- XCTAssertEqual(0, self.cuttlefishContext.stateMachine.paused.wait(10 * NSEC_PER_SEC), "State machine should be quiescent")
-
- self.wait(for: [quiescentExpectation], timeout: 10)
-
- // CKKS should also be logged out, since Octagon believes there's no account
- assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
- }
-
func testLoadToUntrusted() throws {
self.startCKAccountStatusMock()
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
// With no identity but AuthKit reporting an existing iCloud account, Octagon should go directly into 'untrusted'
- OctagonInitialize()
+ self.cuttlefishContext.startOctagonStateMachine()
+
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
- assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
}
func testLoadToUntrustedIfTPHHasPreparedIdentityOnly() throws {
self.startCKAccountStatusMock()
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
// Prepare an identity, then pretend like securityd thought it was in the right account
let containerName = OTCKContainerName
let contextName = OTDefaultContext
machineID: "asdf",
bottleSalt: "123456789",
bottleID: UUID().uuidString,
- modelID: "asdf",
+ modelID: "iPhone9,1",
deviceName: "asdf",
serialNumber: "1234",
osVersion: "asdf",
policyVersion: nil,
policySecrets: nil,
signingPrivKeyPersistentRef: nil,
- encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+ encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
XCTAssertNil(error, "Should be no error preparing identity")
XCTAssertNotNil(peerID, "Should be a peer ID")
XCTAssertNotNil(permanentInfo, "Should have a permenent info")
XCTAssertNoThrow(try account.saveToKeychain(forContainer: containerName, contextID: contextName), "Should be no error saving fake account metadata")
- OctagonInitialize()
-
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
- self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
-
- // CKKS should be waiting for assistance
- assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
- }
-
- func testSignIn() throws {
- self.startCKAccountStatusMock()
-
- // Device is signed out
- self.mockAuthKit.altDSID = nil
- self.mockAuthKit.hsa2 = false
-
- // With no account, Octagon should go directly into 'NoAccount'
self.cuttlefishContext.startOctagonStateMachine()
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
- // Sign in occurs
- let newAltDSID = UUID().uuidString
- self.mockAuthKit.altDSID = newAltDSID
- self.mockAuthKit.hsa2 = true
- XCTAssertNoThrow(try self.cuttlefishContext.accountAvailable(newAltDSID), "Sign-in shouldn't error")
-
- // Octagon should go into 'untrusted'
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 100 * NSEC_PER_SEC)
- self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
-
- assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
-
- // On sign-out, octagon should go back to 'no account'
- self.mockAuthKit.altDSID = nil
- XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "sign-out shouldn't error")
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
- self.assertNoAccount(context: self.cuttlefishContext)
-
- assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
- }
-
- func testSignInWithDelayedHSA2Status() throws {
- self.startCKAccountStatusMock()
-
- // Device is signed out
- self.mockAuthKit.altDSID = nil
- self.mockAuthKit.hsa2 = false
-
- // With no account, Octagon should go directly into 'NoAccount'
- self.cuttlefishContext.startOctagonStateMachine()
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
-
- // Sign in occurs, but HSA2 status isn't here yet
- let newAltDSID = UUID().uuidString
- self.mockAuthKit.altDSID = newAltDSID
- XCTAssertNoThrow(try self.cuttlefishContext.accountAvailable(newAltDSID), "Sign-in shouldn't error")
-
- // Octagon should go into 'waitforhsa2'
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForHSA2, within: 10 * NSEC_PER_SEC)
-
- self.mockAuthKit.hsa2 = true
- XCTAssertNoThrow(try self.cuttlefishContext.idmsTrustLevelChanged(), "Notification of IDMS trust level shouldn't error")
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
- assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
-
- // On sign-out, octagon should go back to 'no account'
- self.mockAuthKit.altDSID = nil
- XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "sign-out shouldn't error")
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
- self.assertNoAccount(context: self.cuttlefishContext)
-
+ // CKKS should be waiting for assistance
assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
}
func testNewFriendsForEmptyAccount() throws {
self.startCKAccountStatusMock()
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
self.cuttlefishContext.startOctagonStateMachine()
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
do {
let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, resetReason: .testGenerated)
self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
self.assertConsidersSelfTrustedCachedAccountStatus(context: self.cuttlefishContext)
+ // and the act of calling newFriends should set the CDP bit
+ XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .enabled, "CDP status should be 'enabled'")
+
// and all subCKKSes should enter ready...
- assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
self.verifyDatabaseMocks()
self.assertSelfTLKSharesInCloudKit(context: self.cuttlefishContext)
+ // Also, CKKS should be configured with the prevailing policy version
+ XCTAssertNotNil(self.injectedManager?.policy, "Should have given CKKS a TPPolicy during initialization")
+ XCTAssertEqual(self.injectedManager?.policy?.version, prevailingPolicyVersion, "Policy given to CKKS should be prevailing policy")
+
// TODO: add a CKKS item
}
self.cuttlefishContext.startOctagonStateMachine()
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
- let clique = try OTClique(contextData: self.otcliqueContext)
+ let clique = OTClique(contextData: self.otcliqueContext)
// Now, call requestToJoin. It should cause an establish to happen
try clique.requestToJoinCircle()
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
do {
func testDeviceFetchRetry() throws {
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let ckError = FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .transactionalFailure)
func testDeviceFetchRetryFail() throws {
self.startCKAccountStatusMock()
- self.cuttlefishContext.startOctagonStateMachine()
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+
+ self.assertResetAndBecomeTrustedInDefaultContext()
let ckError = FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .transactionalFailure)
self.fakeCuttlefishServer.nextFetchErrors.append(ckError)
self.fakeCuttlefishServer.nextFetchErrors.append(ckError)
- self.cuttlefishContext.notifyContainerChange(nil)
-
- do {
- let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, resetReason: .testGenerated)
- XCTAssertNotNil(clique, "Clique should not be nil")
- } catch {
- XCTFail("Shouldn't have errored making new friends: \(error)")
- }
-
- // Now, we should be in 'ready'
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 20 * NSEC_PER_SEC)
- self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
- assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
- self.verifyDatabaseMocks()
- self.assertSelfTLKSharesInCloudKit(context: self.cuttlefishContext)
- self.cuttlefishContext.notifyContainerChange(nil)
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 20 * NSEC_PER_SEC)
+ self.sendContainerChangeWaitForFetchForStates(context: self.cuttlefishContext, states: [OctagonStateReadyUpdated])
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 30 * NSEC_PER_SEC)
}
func testNewFriendsForEmptyAccountReturnsMoreChanges() throws {
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.fakeCuttlefishServer.nextEstablishReturnsMoreChanges = true
}
func testNewFriendsForEmptyAccountWithoutTLKsResetsZones() throws {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID!)
+ self.putFakeKeyHierarchiesInCloudKit()
// But do NOT add them to the keychain
// CKKS+Octagon should reset the zones and be ready
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
// CKKS should reset the zones after Octagon has entered
}
func testUploadTLKsRetry() throws {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID!)
+ self.putFakeKeyHierarchiesInCloudKit()
// But do NOT add them to the keychain
// CKKS+Octagon should reset the zones and be ready
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
// CKKS should reset the zones after Octagon has entered
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
do {
}
func testNewFriendsForEmptyAccountWithTLKs() throws {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID!)
- self.saveTLKMaterial(toKeychain: self.manateeZoneID!)
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.saveTLKMaterialToKeychain()
self.startCKAccountStatusMock()
assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTrust, within: 10 * NSEC_PER_SEC)
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
do {
assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
+ // Ensure CKKS has a policy after newFriends
+ XCTAssertNotNil(self.injectedManager?.policy, "Should have given CKKS a TPPolicy during initialization")
+ XCTAssertEqual(self.injectedManager?.policy?.version, prevailingPolicyVersion, "Policy given to CKKS should be prevailing policy")
+
let peerID = try self.cuttlefishContext.accountMetadataStore.getEgoPeerID()
XCTAssertNotNil(peerID, "Should have a peer ID after making new friends")
self.restartCKKSViews()
self.cuttlefishContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
+ XCTAssertNil(self.injectedManager?.policy, "CKKS should not have a policy after 'restart'")
+
let restartDate = Date()
self.cuttlefishContext.startOctagonStateMachine()
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
XCTAssertEqual(peerID, restartedPeerID, "Should have the same peer ID after restarting")
assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+ XCTAssertNotNil(self.injectedManager?.policy, "Should have given CKKS a TPPolicy after restart")
+ XCTAssertEqual(self.injectedManager?.policy?.version, prevailingPolicyVersion, "Policy given to CKKS after restart should be prevailing policy")
+
readyDate = CKKSAnalytics.logger().dateProperty(forKey: OctagonAnalyticsLastKeystateReady)
XCTAssertNotNil(readyDate, "Should have a ready date")
XCTAssert(readyDate! > restartDate, "ready date should be after re-startdate")
}
+ func testFillInUnknownAttemptedJoinState() throws {
+ self.startCKAccountStatusMock()
+
+ _ = self.assertResetAndBecomeTrustedInDefaultContext()
+
+ try self.cuttlefishContext.accountMetadataStore.persistAccountChanges { metadata in
+ metadata.attemptedJoin = .UNKNOWN
+ return metadata
+ }
+
+ self.manager.removeContext(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
+ self.restartCKKSViews()
+ self.cuttlefishContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
+
+ self.cuttlefishContext.startOctagonStateMachine()
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+
+ // And check that the metadata is fixed:
+ let metadata = try self.cuttlefishContext.accountMetadataStore.loadOrCreateAccountMetadata()
+ XCTAssertEqual(metadata.attemptedJoin, .ATTEMPTED, "Should have attempted a join")
+ }
+
func testLoadToUntrustedOnRestartIfKeysGone() throws {
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
do {
func testLoadToUntrustedOnRestartIfTPHLosesAllData() throws {
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
do {
func testLoadToTrustedOnRestartIfMismatchedPeerIDs() throws {
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
do {
XCTAssertEqual(peerID, newPeerID, "Should now have TPH's peer ID")
}
- func testStatusRPCsWithUnknownCloudKitAccount() throws {
- // If CloudKit isn't returning our calls, we should still return something reasonable...
- let statusexpectation = self.expectation(description: "trust status returns")
- let configuration = OTOperationConfiguration()
- configuration.timeoutWaitForCKAccount = 500 * NSEC_PER_MSEC
- self.cuttlefishContext.rpcTrustStatus(configuration) { egoStatus, _, _, _, _ in
- XCTAssertTrue([.absent].contains(egoStatus), "Self peer should be in the 'absent' state")
- statusexpectation.fulfill()
- }
- self.wait(for: [statusexpectation], timeout: 10)
-
- // Now sign in to 'untrusted'
- self.startCKAccountStatusMock()
-
- self.cuttlefishContext.startOctagonStateMachine()
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
- self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
-
- // And restart, without any idea of the cloudkit state
- self.ckaccountHoldOperation = BlockOperation()
- self.injectedManager!.accountTracker = CKKSAccountStateTracker(self.injectedManager!.container,
- nsnotificationCenterClass: FakeNSNotificationCenter.self as CKKSNSNotificationCenter.Type)
- self.manager.accountStateTracker = self.injectedManager!.accountTracker
-
- self.manager.removeContext(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
- self.restartCKKSViews()
- self.cuttlefishContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
-
- // Should know it's untrusted
- self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
- self.startCKAccountStatusMock()
-
- // Now become ready
- do {
- let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, resetReason: .testGenerated)
- XCTAssertNotNil(clique, "Clique should not be nil")
- } catch {
- XCTFail("Shouldn't have errored making new friends: \(error)")
- }
-
- // Now, we should be in 'ready'
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
- self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
- self.assertConsidersSelfTrustedCachedAccountStatus(context: self.cuttlefishContext)
-
- // and all subCKKSes should enter ready...
- assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
- self.verifyDatabaseMocks()
-
- // Restart one more time:
-
- self.ckaccountHoldOperation = BlockOperation()
- self.injectedManager!.accountTracker = CKKSAccountStateTracker(self.injectedManager!.container,
- nsnotificationCenterClass: FakeNSNotificationCenter.self as CKKSNSNotificationCenter.Type)
- self.manager.accountStateTracker = self.injectedManager!.accountTracker
-
- self.manager.removeContext(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
- self.restartCKKSViews()
- self.cuttlefishContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
-
- self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
- self.assertConsidersSelfTrustedCachedAccountStatus(context: self.cuttlefishContext)
- }
-
func testRestoreToNewClique() throws {
self.startCKAccountStatusMock()
let memberIdentifier = clique!.cliqueMemberIdentifier
let dumpExpectation = self.expectation(description: "dump callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, error in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, error in
XCTAssertNil(error, "Should be no error dumping data")
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
let peerID = egoSelf!["peerID"] as? String
XCTAssertNotNil(peerID, "peerID should not be nil")
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
// TODO: an item added here shouldn't sync
}
- func testSignOut() throws {
- self.startCKAccountStatusMock()
-
- self.cuttlefishContext.startOctagonStateMachine()
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
-
- do {
- let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, resetReason: .testGenerated)
- XCTAssertNotNil(clique, "Clique should not be nil")
- } catch {
- XCTFail("Shouldn't have errored making new friends: \(error)")
- }
-
- // Now, we should be in 'ready'
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
- self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
- self.verifyDatabaseMocks()
- assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
-
- // And 'dump' should show some information
- let dumpExpectation = self.expectation(description: "dump callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, error in
- XCTAssertNil(error, "Should be no error dumping data")
- XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
- XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let peerID = egoSelf!["peerID"] as? String
- XCTAssertNotNil(peerID, "peerID should not be nil")
-
- dumpExpectation.fulfill()
- }
- self.wait(for: [dumpExpectation], timeout: 10)
-
- // Turn off the CK account too
- self.accountStatus = .noAccount
- self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
-
- XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "Should be no issue signing out")
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
- self.assertNoAccount(context: self.cuttlefishContext)
-
- // And 'dump' should show nothing
- let signedOutDumpExpectation = self.expectation(description: "dump callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, error in
- XCTAssertNil(error, "Should be no error dumping data")
- XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
- XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- XCTAssertEqual(egoSelf!.count, 0, "egoSelf should have zero elements")
-
- signedOutDumpExpectation.fulfill()
- }
- self.wait(for: [signedOutDumpExpectation], timeout: 10)
- assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
-
- //check trust status
- let checkTrustExpectation = self.expectation(description: "checkTrustExpectation callback occurs")
- let configuration = OTOperationConfiguration()
- self.cuttlefishContext.rpcTrustStatus(configuration) { _, _, _, _, _ in
- checkTrustExpectation.fulfill()
- }
- self.wait(for: [checkTrustExpectation], timeout: 10)
-
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
-
- // And 'dump' should show nothing
- let signedOutDumpExpectationAfterCheckTrustStatus = self.expectation(description: "dump callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, error in
- XCTAssertNil(error, "Should be no error dumping data")
- XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
- XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- XCTAssertEqual(egoSelf!.count, 0, "egoSelf should have zero elements")
-
- signedOutDumpExpectationAfterCheckTrustStatus.fulfill()
- }
- self.wait(for: [signedOutDumpExpectationAfterCheckTrustStatus], timeout: 10)
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
- self.assertNoAccount(context: self.cuttlefishContext)
- }
-
func testCliqueFriendAPI() throws {
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
accountStateTracker: self.accountStateTracker,
deviceInformationAdapter: peer2DeviceAdapter)
- self.setAllowListToCurrentAuthKit(container: OTCKContainerName, context: peer2ContextID)
+ self.setAllowListToCurrentAuthKit(container: OTCKContainerName, context: peer2ContextID, accountIsDemo: false)
var peer2ID: String!
let joinExpectation = self.expectation(description: "join callback occurs")
policyVersion: nil,
policySecrets: nil,
signingPrivKeyPersistentRef: nil,
- encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+ encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
XCTAssertNil(error, "Should be no error preparing identity")
XCTAssertNotNil(permanentInfo, "Should have a permanent identity")
XCTAssertNotNil(permanentInfoSig, "Should have a permanent identity signature")
voucherSig: voucherSig!,
ckksKeys: [],
tlkShares: [],
- preapprovedKeys: []) { peerID, _, error in
+ preapprovedKeys: []) { peerID, _, _, _, error in
XCTAssertNil(error, "Should be no error joining")
XCTAssertNotNil(peerID, "Should have a peerID")
peer2ID = peerID
account.peerID = peer2ID
account.icloudAccountState = .ACCOUNT_AVAILABLE
account.trustState = .TRUSTED
+ account.attemptedJoin = . ATTEMPTED
XCTAssertNoThrow(try account.saveToKeychain(forContainer: OTCKContainerName, contextID: peer2ContextID), "Should be no error saving fake account metadata")
peer2.startOctagonStateMachine()
}
}
- func testNoAccountLeadsToInitialize() throws {
- self.startCKAccountStatusMock()
-
- // With no identity and AuthKit reporting no iCloud account, Octagon should go directly into 'no account'
- self.mockAuthKit.altDSID = nil
-
- // Run initialization, like the real secd will do
- OctagonInitialize()
-
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
-
- self.mockAuthKit.altDSID = "1234"
- let signinExpectation = self.expectation(description: "sign in returns")
- self.otControl.sign(in: "1234", container: nil, context: OTDefaultContext) { error in
- XCTAssertNil(error, "error should be nil")
- signinExpectation.fulfill()
- }
- self.wait(for: [signinExpectation], timeout: 10)
-
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
-
- }
-
func testOTCliqueOctagonAuthoritativeTrustResponse() throws {
self.startCKAccountStatusMock()
OctagonAuthoritativeTrustSetIsEnabled(true)
- do {
- let absentClique = try OTClique(contextData: self.otcliqueContext)
- let absentStatus = absentClique.fetchStatus(nil)
- XCTAssertEqual(absentStatus, CliqueStatus.absent, "clique should return Absent")
- } catch {
- XCTFail("Shouldn't have errored making new friends: \(error)")
- throw error
- }
+ let absentClique = OTClique(contextData: self.otcliqueContext)
+ let absentStatus = absentClique.fetchStatus(nil)
+ XCTAssertEqual(absentStatus, CliqueStatus.absent, "clique should return Absent")
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
XCTFail("Shouldn't have errored making new friends: \(error)")
throw error
}
- let newContext = self.manager.context(forContainerName: OTCKContainerName, contextID: newOTCliqueContext.context!)
+ let newContext = self.manager.context(forContainerName: OTCKContainerName, contextID: newOTCliqueContext.context)
self.assertEnters(context: newContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
}
json: false)
}
- func testNoAccountTimeoutTransitionWatcher() throws {
- self.startCKAccountStatusMock()
-
- // With no identity and AuthKit reporting no iCloud account, Octagon should go directly into 'no account'
- self.mockAuthKit.altDSID = nil
-
- // Run initialization, like the real secd will do
- OctagonInitialize()
- self.cuttlefishContext.stateMachine.setWatcherTimeout(2 * NSEC_PER_SEC)
-
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
- XCTAssertTrue(self.cuttlefishContext.stateMachine.isPaused(), "State machine should be stopped")
- self.assertNoAccount(context: self.cuttlefishContext)
- XCTAssertEqual(0, self.cuttlefishContext.stateMachine.paused.wait(10 * NSEC_PER_SEC), "State machine should be quiescent")
-
- let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
- self.cuttlefishContext.join(withBottle: "bottleID", entropy: Data(), bottleSalt: "peer2AltDSID") { error in
- XCTAssertNotNil(error, "error should not be nil")
- joinWithBottleExpectation.fulfill()
- }
- self.wait(for: [joinWithBottleExpectation], timeout: 3)
- }
-
func testFailingStateTransitionWatcher() throws {
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
// Set up a watcher that we expect to fail...
let path = OctagonStateTransitionPath(from: [
OctagonStateResetAndEstablish: [
- OctagonStateInitiatorVouchWithBottle: [
+ OctagonStateBottleJoinVouchWithBottle: [
OctagonStateResetAndEstablish: OctagonStateTransitionPathStep.success(),
],
],
self.cuttlefishContext.stateMachine.register(watcher)
let watcherCompleteOperationExpectation = self.expectation(description: "watcherCompleteOperationExpectation returns")
- let watcherFinishOp = CKKSResultOperation.named("should-fail-cleanup", with: {
+ let watcherFinishOp = CKKSResultOperation.named("should-fail-cleanup") {
XCTAssertNotNil(watcher.result.error, "watcher should have errored")
watcherCompleteOperationExpectation.fulfill()
- })
+ }
watcherFinishOp.addDependency(watcher.result)
self.operationQueue.addOperation(watcherFinishOp)
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let stateTransitionOp = OctagonStateTransitionOperation(name: "will-never-run",
// Set up a watcher that we expect to fail due to its initial transition op timing out...
let path = OctagonStateTransitionPath(from: [
OctagonStateResetAndEstablish: [
- OctagonStateInitiatorVouchWithBottle: [
+ OctagonStateBottleJoinVouchWithBottle: [
OctagonStateResetAndEstablish: OctagonStateTransitionPathStep.success(),
],
],
self.cuttlefishContext.stateMachine.register(watcher)
let watcherCompleteOperationExpectation = self.expectation(description: "watcherCompleteOperationExpectation returns")
- let watcherFinishOp = CKKSResultOperation.named("should-fail-cleanup", with: {
+ let watcherFinishOp = CKKSResultOperation.named("should-fail-cleanup") {
XCTAssertNotNil(watcher.result.error, "watcher should have errored")
watcherCompleteOperationExpectation.fulfill()
- })
+ }
watcherFinishOp.addDependency(watcher.result)
self.operationQueue.addOperation(watcherFinishOp)
OctagonAuthoritativeTrustSetIsEnabled(true)
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let cfuExpectation = self.expectation(description: "cfu callback occurs")
- self.cuttlefishContext.setPostedBool(false)
+ self.cuttlefishContext.followupHandler.clearAllPostedFlags()
self.cuttlefishContext.checkTrustStatusAndPostRepairCFUIfNecessary { _, posted, _, error in
#if !os(tvOS)
XCTAssertTrue(posted, "posted should be true")
}
func testDeviceLockedDuringAccountRetrieval() throws {
+ // Tell SOS that it is absent, so we don't enable CDP on bringup
+ self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
self.startCKAccountStatusMock()
self.aksLockState = true
self.aksLockState = false
self.lockStateTracker.recheck()
+ self.assertEnters(context: initiatorContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+
+ XCTAssertNoThrow(try initiatorContext.setCDPEnabled())
self.assertEnters(context: initiatorContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
}
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
do {
func testFetchViewList() throws {
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
do {
"WiFi",
"Health",
"Manatee",
- "CreditCards",
- "Passwords",
+ // <rdar://problem/57810109> Cuttlefish: remove Safari prefix from view names
+ "SafariCreditCards",
+ "SafariPasswords",
"ApplePay", ])
#else
let expectedViews = Set(["LimitedPeersAllowed",
+ "Home",
"WiFi", ])
#endif
let getViewsExpectation = self.expectation(description: "getViews callback happens")
- self.tphClient.getViewsWithContainer(OTCKContainerName, context: OTDefaultContext, inViews: []) { outViews, error in
+ self.tphClient.fetchCurrentPolicy(withContainer: OTCKContainerName, context: OTDefaultContext) { outViews, _, error in
XCTAssertNil(error, "should not have failed")
XCTAssertEqual(expectedViews, Set(outViews!))
getViewsExpectation.fulfill()
self.wait(for: [getViewsExpectation], timeout: 10)
}
- func testMergedViewListOff() throws {
- self.startCKAccountStatusMock()
- self.cuttlefishContext.viewManager!.setOverrideCKKSViewsFromPolicy(false)
- self.cuttlefishContext.startOctagonStateMachine()
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
-
- do {
- let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, resetReason: .testGenerated)
- XCTAssertNotNil(clique, "Clique should not be nil")
- } catch {
- XCTFail("Shouldn't have errored making new friends: \(error)")
- }
-
- // Now, we should be in 'ready'
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
- self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
-
- let viewList = self.cuttlefishContext.viewManager!.viewList()
- #if !os(tvOS)
- let expected = Set<String>(["Manatee"])
- #else
- let expected = Set<String>(["LimitedPeersAllowed"])
- #endif
- XCTAssertEqual(expected, viewList)
- }
-
- func testMergedViewListOn() throws {
- /*
- self.startCKAccountStatusMock()
- self.cuttlefishContext.viewManager!.setOverrideCKKSViewsFromPolicy(true)
- self.cuttlefishContext.startOctagonStateMachine()
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
-
- do {
- let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, .testGenerated)
- XCTAssertNotNil(clique, "Clique should not be nil")
- } catch {
- XCTFail("Shouldn't have errored making new friends: \(error)")
- }
-
- // Now, we should be in 'ready'
- self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
- self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
-
- let viewList = self.cuttlefishContext.viewManager!.viewList()
- let expected = Set<String>([
- "ApplePay",
- "Applications",
- "AutoUnlock",
- "Backstop",
- "DevicePairing",
- "Engram",
- "Health",
- "Home",
- "LimitedPeersAllowed",
- "Manatee",
- "ProtectedCloudStorage",
- "SafariCreditCards",
- "SafariPasswords",
- "SecureObjectSync",
- "WiFi",
- "keychain",
- ])
- XCTAssertEqual(expected, viewList)
- */
- }
-
let octagonNotificationName = "com.apple.security.octagon.trust-status-change"
func testNotifications() throws {
let untrustedNotification = XCTDarwinNotificationExpectation(notificationName: octagonNotificationName)
self.startCKAccountStatusMock()
- self.cuttlefishContext.startOctagonStateMachine()
+ self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.wait(for: [untrustedNotification], timeout: 2)
self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
let statusExpectation = self.expectation(description: "status callback occurs")
- self.tphClient.dumpEgoPeer(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID, reply: { _, _, stableInfo, _, error in
+ self.tphClient.dumpEgoPeer(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { _, _, stableInfo, _, error in
XCTAssertNil(error, "should be no error dumping ego peer")
XCTAssertEqual(stableInfo?.deviceName, newDeviceName, "device name should be updated")
statusExpectation.fulfill()
- })
+ }
self.wait(for: [statusExpectation], timeout: 2)
// Receiving a push shouldn't cause another update to be sent
let untrustedNotification = XCTDarwinNotificationExpectation(notificationName: octagonNotificationName)
self.startCKAccountStatusMock()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.cuttlefishContext.startOctagonStateMachine()
+ // Octagon will fetch once to determine its trust state
+ let trustStateFetchExpectation = self.expectation(description: "trust state fetch occurs")
+ self.fakeCuttlefishServer.fetchChangesListener = { [unowned self] _ in
+ self.fakeCuttlefishServer.fetchChangesListener = nil
+ trustStateFetchExpectation.fulfill()
+ return nil
+ }
+
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.wait(for: [untrustedNotification], timeout: 2)
+ self.wait(for: [trustStateFetchExpectation], timeout: 10)
+
+ let fetchExpectation = self.expectation(description: "fetch occurs")
+ self.fakeCuttlefishServer.fetchChangesListener = { _ in
+ fetchExpectation.fulfill()
+ return nil
+ }
+
self.cuttlefishContext.notifyContainerChange(nil)
self.cuttlefishContext.notifyContainerChange(nil)
self.cuttlefishContext.notifyContainerChange(nil)
- XCTAssertEqual(self.fakeCuttlefishServer.fetchChangesCalledCount, 1, "fetchChanges should have been called 1 times")
+ self.wait(for: [fetchExpectation], timeout: 10)
+ XCTAssertEqual(self.fakeCuttlefishServer.fetchChangesCalledCount, 2, "fetchChanges should have been called 1 times")
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
let untrustedNotification = XCTDarwinNotificationExpectation(notificationName: octagonNotificationName)
self.startCKAccountStatusMock()
+
+ // Set the CDP bit before the test begins, so we don't have to fetch to discover CDP status
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+
self.cuttlefishContext.startOctagonStateMachine()
+ // Octagon will fetch once to determine its trust state
+ let trustStateFetchExpectation = self.expectation(description: "trust state fetch occurs")
+ self.fakeCuttlefishServer.fetchChangesListener = { [unowned self] _ in
+ self.fakeCuttlefishServer.fetchChangesListener = nil
+ trustStateFetchExpectation.fulfill()
+ return nil
+ }
+
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.wait(for: [untrustedNotification], timeout: 2)
- self.cuttlefishContext.notifyContainerChange(nil)
+ self.wait(for: [trustStateFetchExpectation], timeout: 10)
- self.cuttlefishContext.notifyContainerChange(nil)
+ let fetchExpectation = self.expectation(description: "fetch occurs")
+ self.fakeCuttlefishServer.fetchChangesListener = { _ in
+ fetchExpectation.fulfill()
+ return nil
+ }
self.cuttlefishContext.notifyContainerChange(nil)
-
+ self.cuttlefishContext.notifyContainerChange(nil)
+ self.cuttlefishContext.notifyContainerChange(nil)
self.cuttlefishContext.notifyContainerChange(nil)
- XCTAssertEqual(self.fakeCuttlefishServer.fetchChangesCalledCount, 1, "fetchChanges should have been called 1 times")
+ self.wait(for: [fetchExpectation], timeout: 10)
+ XCTAssertEqual(self.fakeCuttlefishServer.fetchChangesCalledCount, 2, "fetchChanges should have been called 1 times")
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
self.startCKAccountStatusMock()
OctagonSetPlatformSupportsSOS(true)
- let initiatorPiggybackingConfig = OTJoiningConfiguration(protocolType: OTProtocolPiggybacking, uniqueDeviceID: "initiator", uniqueClientID: "acceptor", containerName: OTCKContainerName, contextID: OTDefaultContext, epoch: 1, isInitiator: true)
+ let initiatorPiggybackingConfig = OTJoiningConfiguration(protocolType: OTProtocolPiggybacking,
+ uniqueDeviceID: "initiator",
+ uniqueClientID: "acceptor",
+ pairingUUID: UUID().uuidString,
+ containerName: OTCKContainerName,
+ contextID: OTDefaultContext,
+ epoch: 1,
+ isInitiator: true)
let resetAndEstablishExpectation = self.expectation(description: "resetAndEstablish callback occurs")
self.manager.resetAndEstablish(OTCKContainerName,
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
do {
struct TestCase {
let model: String
let success: Bool
- let sendTLKs: Bool
+ let manateeTLKs: Bool
+ let limitedTLKs: Bool
+ }
+
+ func assertTLKs(expectation: TestCase, receiverPeerID: String, senderPeerID: String) throws {
+ let haveManateeTLK = try self.tlkShareInCloudKit(receiverPeerID: receiverPeerID,
+ senderPeerID: senderPeerID,
+ zoneID: self.manateeZoneID)
+ let haveLimitedPeersAllowedTLK = try self.tlkShareInCloudKit(receiverPeerID: receiverPeerID,
+ senderPeerID: senderPeerID,
+ zoneID: self.limitedPeersAllowedZoneID)
+
+ XCTAssertEqual(haveManateeTLK, expectation.manateeTLKs, "manatee should be what's expected: \(expectation)")
+ XCTAssertEqual(haveLimitedPeersAllowedTLK, expectation.limitedTLKs, "limited should be what's expected: \(expectation)")
}
func _testVouchers(expectations: [TestCase]) throws {
self.startCKAccountStatusMock()
+
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
- _ = fetchEgoPeerID()
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+
+ let ckksKeys: [CKKSKeychainBackedKeySet] = self.ckksViews.compactMap { view in
+ let viewName = (view as! CKKSKeychainView).zoneName
+ let currentKeySet = CKKSCurrentKeySet.load(forZone: CKRecordZone.ID(zoneName: viewName))
+ return try! currentKeySet.asKeychainBackedSet()
+ }
+
+ let senderPeerID = fetchEgoPeerID()
for testCase in expectations {
let model = testCase.model
accountStateTracker: self.accountStateTracker,
deviceInformationAdapter: OTMockDeviceInfoAdapter(modelID: "iPhone9,1", deviceName: "test-SOS-iphone", serialNumber: "456", osVersion: "iOS (fake version)"))
- self.setAllowListToCurrentAuthKit(container: OTCKContainerName, context: peer2ContextID)
+ self.setAllowListToCurrentAuthKit(container: OTCKContainerName, context: peer2ContextID, accountIsDemo: false)
let peer2DeviceName = "peer2-asdf"
let joinExpectation = self.expectation(description: "join callback occurs")
policyVersion: nil,
policySecrets: nil,
signingPrivKeyPersistentRef: nil,
- encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+ encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
XCTAssertNil(error, "Should be no error preparing identity")
XCTAssertNotNil(permanentInfo, "Should have a permanent identity")
XCTAssertNotNil(permanentInfoSig, "Should have a permanent identity signature")
permanentInfoSig: permanentInfoSig!,
stableInfo: stableInfo!,
stableInfoSig: stableInfoSig!,
- ckksKeys: []) { voucher, voucherSig, error in
+ ckksKeys: ckksKeys) { voucher, voucherSig, error in
XCTAssertNil(error, "Should be no error vouching")
XCTAssertNotNil(voucher, "Should have a voucher")
XCTAssertNotNil(voucherSig, "Should have a voucher signature")
+
+ try! self.assertTLKs(expectation: testCase,
+ receiverPeerID: peerID!,
+ senderPeerID: senderPeerID)
+
self.tphClient.join(withContainer: OTCKContainerName,
context: peer2ContextID,
voucherData: voucher!,
voucherSig: voucherSig!,
ckksKeys: [],
tlkShares: [],
- preapprovedKeys: []) { peerID, _, error in
+ preapprovedKeys: []) { peerID, _, _, _, error in
XCTAssertNil(error, "Should be no error joining")
XCTAssertNotNil(peerID, "Should have a peerID")
joinExpectation.fulfill()
XCTAssertNil(voucher, "voucher should be nil")
XCTAssertNil(voucherSig, "voucherSig should be nil")
XCTAssertNotNil(error, "error should be non nil")
+
+ try! self.assertTLKs(expectation: testCase,
+ receiverPeerID: peerID!,
+ senderPeerID: senderPeerID)
+
joinExpectation.fulfill()
}
}
+
}
self.wait(for: [joinExpectation], timeout: 10)
}
func _testJoin(expectations: [TestCase]) throws {
self.startCKAccountStatusMock()
self.cuttlefishContext.startOctagonStateMachine()
+ XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
let clique: OTClique
let peer2DeviceName = "peer2-device-name"
var peer2ID: String!
- self.setAllowListToCurrentAuthKit(container: OTCKContainerName, context: peer2ContextID)
+ self.setAllowListToCurrentAuthKit(container: OTCKContainerName, context: peer2ContextID, accountIsDemo: false)
let joinExpectation = self.expectation(description: "join callback occurs")
self.tphClient.prepare(withContainer: OTCKContainerName,
policyVersion: nil,
policySecrets: nil,
signingPrivKeyPersistentRef: nil,
- encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+ encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
XCTAssertNil(error, "Should be no error preparing identity")
XCTAssertNotNil(permanentInfo, "Should have a permanent identity")
XCTAssertNotNil(permanentInfoSig, "Should have a permanent identity signature")
voucherSig: voucher!.sig,
ckksKeys: [],
tlkShares: [],
- preapprovedKeys: []) { peerID, _, error in
+ preapprovedKeys: []) { peerID, _, _, _, error in
if expectedSuccess {
XCTAssertNil(error, "expected success")
XCTAssertNotNil(peerID, "peerID should be set")
return nil
}
- // Maybe send TLKs?
- if testCase.sendTLKs {
- self.assertAllCKKSViewsUpload(tlkShares: 1)
+ if testCase.manateeTLKs {
+ self.expectCKModifyKeyRecords(0, currentKeyPointerRecords: 0, tlkShareRecords: 1, zoneID: self.manateeZoneID)
+ }
+ if testCase.limitedTLKs {
+ self.expectCKModifyKeyRecords(0, currentKeyPointerRecords: 0, tlkShareRecords: 1, zoneID: self.limitedPeersAllowedZoneID)
}
self.cuttlefishContext.notifyContainerChange(nil)
self.fakeCuttlefishServer.updateListener = nil
self.sendAllCKKSTrustedPeersChanged()
- self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
self.verifyDatabaseMocks()
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
}
+
+ try self.assertTLKs(expectation: testCase,
+ receiverPeerID: peer2ID,
+ senderPeerID: peer1ID)
}
}
}
-class OctagonTestsOverrideModletV: OctagonTestsOverrideModelBase {
- // If this test is running on a TV, we will send TLKs, since we're using the LimitedPeersAllowed view
- #if !os(tvOS)
- let sendTLKsToAllPeers = false
- #else
- let sendTLKsToAllPeers = true
- #endif
-
+class OctagonTestsOverrideModelTV: OctagonTestsOverrideModelBase {
override func setUp() {
- super.setUp()
+ self.mockDeviceInfo = OTMockDeviceInfoAdapter(modelID: "AppleTV5,3",
+ deviceName: "intro-TV",
+ serialNumber: "456",
+ osVersion: "tvOS (whatever TV version)")
- self.mockDeviceInfo.mockModelID = "AppleTV5,3"
- self.mockDeviceInfo.mockDeviceName = "intro-TV"
- self.mockDeviceInfo.mockSerialNumber = "456"
- self.mockDeviceInfo.mockOsVersion = "iOS (whatever TV version)"
+ super.setUp()
}
func testVoucherFromTV() throws {
- try self._testVouchers(expectations: [TestCase(model: "AppleTV5,3", success: true, sendTLKs: sendTLKsToAllPeers),
- TestCase(model: "MacFoo", success: false, sendTLKs: sendTLKsToAllPeers),
- TestCase(model: "Watch17", success: false, sendTLKs: sendTLKsToAllPeers), ])
+ try self._testVouchers(expectations: [TestCase(model: "AppleTV5,3", success: true, manateeTLKs: false, limitedTLKs: true),
+ TestCase(model: "MacFoo", success: false, manateeTLKs: false, limitedTLKs: false),
+ TestCase(model: "Watch17", success: false, manateeTLKs: false, limitedTLKs: false), ])
}
func testJoinFromTV() throws {
- try self._testJoin(expectations: [TestCase(model: "AppleTV5,3", success: true, sendTLKs: sendTLKsToAllPeers),
- TestCase(model: "MacFoo", success: false, sendTLKs: sendTLKsToAllPeers),
- TestCase(model: "Watch17", success: false, sendTLKs: sendTLKsToAllPeers), ])
+ try self._testJoin(expectations: [TestCase(model: "AppleTV5,3", success: true, manateeTLKs: false, limitedTLKs: true),
+ TestCase(model: "MacFoo", success: false, manateeTLKs: false, limitedTLKs: false),
+ TestCase(model: "Watch17", success: false, manateeTLKs: false, limitedTLKs: false), ])
}
}
class OctagonTestsOverrideModelMac: OctagonTestsOverrideModelBase {
- #if !os(tvOS)
- let sendTLKsToAllPeers = false
- #else
- let sendTLKsToAllPeers = true
- #endif
-
override func setUp() {
+ self.mockDeviceInfo = OTMockDeviceInfoAdapter(modelID: "Mac17",
+ deviceName: "macbook",
+ serialNumber: "456",
+ osVersion: "OSX 11")
super.setUp()
-
- self.mockDeviceInfo.mockModelID = "Mac17"
- self.mockDeviceInfo.mockDeviceName = "macbook"
- self.mockDeviceInfo.mockSerialNumber = "456"
- self.mockDeviceInfo.mockOsVersion = "OSX 11"
}
func testVoucherFromMac() throws {
- try self._testVouchers(expectations: [TestCase(model: "AppleTV5,3", success: true, sendTLKs: sendTLKsToAllPeers),
- TestCase(model: "MacFoo", success: true, sendTLKs: sendTLKsToAllPeers),
- TestCase(model: "Watch17", success: true, sendTLKs: sendTLKsToAllPeers), ])
+ try self._testVouchers(expectations: [TestCase(model: "AppleTV5,3", success: true, manateeTLKs: false, limitedTLKs: true),
+ TestCase(model: "MacFoo", success: true, manateeTLKs: true, limitedTLKs: true),
+ TestCase(model: "Watch17", success: true, manateeTLKs: true, limitedTLKs: true), ])
}
func testJoinFromMac() throws {
- try self._testJoin(expectations: [TestCase(model: "AppleTV5,3", success: true, sendTLKs: sendTLKsToAllPeers),
- TestCase(model: "MacFoo", success: true, sendTLKs: true),
- TestCase(model: "Watch17", success: true, sendTLKs: true), ])
+ try self._testJoin(expectations: [TestCase(model: "AppleTV5,3", success: true, manateeTLKs: false, limitedTLKs: true),
+ TestCase(model: "MacFoo", success: true, manateeTLKs: true, limitedTLKs: true),
+ TestCase(model: "Watch17", success: true, manateeTLKs: true, limitedTLKs: true), ])
}
}
}
self.wait(for: [rpcEpochCallbackOccurs], timeout: 10)
- let initiator1Context = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
-
let clientStateMachine = self.manager.clientStateMachine(forContainerName: OTCKContainerName, contextID: self.contextForAcceptor, clientName: self.initiatorName)
- initiator1Context.startOctagonStateMachine()
+ self.cuttlefishContext.startOctagonStateMachine()
- self.assertEnters(context: initiator1Context, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+ self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
var peerID: String = ""
var permanentInfo = Data(count: 0)
self.wait(for: [firstMessageAcceptorCallback], timeout: 10)
let rpcJoinCallback = self.expectation(description: "joining callback")
- self.manager.rpcJoin(with: self.initiatorPiggybackingConfig, vouchData: voucher, vouchSig: voucherSig, preapprovedKeys: nil) { error in
+ self.manager.rpcJoin(with: self.initiatorPiggybackingConfig, vouchData: voucher, vouchSig: voucherSig) { error in
XCTAssertNil(error, "error should be nil")
rpcJoinCallback.fulfill()
}
self.wait(for: [rpcJoinCallback], timeout: 10)
- assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
-
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+
self.assertEnters(context: self.cuttlefishContextForAcceptor, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
self.assertConsidersSelfTrusted(context: self.cuttlefishContextForAcceptor)
clientStateMachine.notifyContainerChange()
let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
self.wait(for: [initiatorDumpCallback], timeout: 10)
let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
acceptorDumpCallback.fulfill()
self.wait(for: [acceptorDumpCallback], timeout: 10)
self.verifyDatabaseMocks()
+
+ self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
}
- func testVersion2ofPiggybacking() {
+ func testVersion2ofPiggybacking() throws {
KCSetJoiningOctagonPiggybackingEnabled(true)
OctagonSetIsEnabled(true)
self.startCKAccountStatusMock()
error: nil)
XCTAssertNotNil(requestCircleSession, "No request secret session")
- requestCircleSession.setJoiningConfigurationObject(self.initiatorPiggybackingConfig)
+ requestCircleSession.setContextIDOnJoiningConfiguration(self.initiatorPiggybackingConfig.contextID)
requestCircleSession.setControlObject(self.otControl)
var identityMessage: Data?
XCTAssertNil(error, "error retrieving identityMessage message")
}
+ // Double-check that there's an Octagon message in the packet
+ let initiatorIdentityMessage = try self.unpackPiggybackingInitialMessage(identityMessage: identityMessage!, session: acceptSession!.accessSession())
+ XCTAssertTrue(initiatorIdentityMessage.hasPrepare, "Pairing message should contain prepared information")
+
var voucherMessage: Data?
do {
voucherMessage = try acceptSession!.processMessage(identityMessage!)
self.verifyDatabaseMocks()
let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
self.wait(for: [initiatorDumpCallback], timeout: 10)
let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
acceptorDumpCallback.fulfill()
self.wait(for: [firstMessageWithNewJoiningConfigCallback], timeout: 10)
}
- func testVersion2ofPiggybackingWithSOS() {
+ func testVersion2ofPiggybackingWithSOS() throws {
KCSetJoiningOctagonPiggybackingEnabled(true)
OctagonSetPlatformSupportsSOS(true)
self.startCKAccountStatusMock()
error: nil)
XCTAssertNotNil(requestCircleSession, "No request secret session")
- requestCircleSession.setJoiningConfigurationObject(self.initiatorPiggybackingConfig)
+ requestCircleSession.setContextIDOnJoiningConfiguration(self.initiatorPiggybackingConfig.contextID)
requestCircleSession.setControlObject(self.otControl)
var identityMessage: Data?
XCTAssertNil(error, "error retrieving identityMessage message")
}
+ // Double-check that there's an Octagon message in the packet
+ let initiatorIdentityMessage = try self.unpackPiggybackingInitialMessage(identityMessage: identityMessage!, session: acceptSession!.accessSession())
+ XCTAssertTrue(initiatorIdentityMessage.hasPrepare, "Pairing message should contain prepared information")
+
var voucherMessage: Data?
do {
voucherMessage = try acceptSession!.processMessage(identityMessage!)
XCTAssertNotNil(voucherMessage, "No voucherMessage message")
} catch {
XCTAssertNil(error, "error retrieving voucherMessage message")
-
}
var nothing: Data?
self.assertTLKSharesInCloudKit(receiver: self.cuttlefishContextForAcceptor, sender: self.cuttlefishContext)
let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
self.wait(for: [initiatorDumpCallback], timeout: 10)
let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
acceptorDumpCallback.fulfill()
error: nil)
XCTAssertNotNil(requestCircleSession, "No request secret session")
- requestCircleSession.setJoiningConfigurationObject(self.initiatorPiggybackingConfig)
+ requestCircleSession.setContextIDOnJoiningConfiguration(self.initiatorPiggybackingConfig.contextID)
requestCircleSession.setControlObject(self.otControl)
var identityMessage: Data?
clientStateMachine.notifyContainerChange()
let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
self.wait(for: [initiatorDumpCallback], timeout: 10)
let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
acceptorDumpCallback.fulfill()
clientStateMachine2.notifyContainerChange()
let pair2InitiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
self.wait(for: [pair2InitiatorDumpCallback], timeout: 10)
let pair2AcceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 3, "should be 3 peer ids")
pair2AcceptorDumpCallback.fulfill()
clientStateMachine.notifyContainerChange()
let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
self.wait(for: [initiatorDumpCallback], timeout: 10)
let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
acceptorDumpCallback.fulfill()
clientStateMachine2.notifyContainerChange()
let pair2InitiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
self.wait(for: [pair2InitiatorDumpCallback], timeout: 10)
let pair2AcceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 3, "should be 3 peer ids")
pair2AcceptorDumpCallback.fulfill()
clientStateMachine.notifyContainerChange()
let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
self.wait(for: [initiatorDumpCallback], timeout: 10)
let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
acceptorDumpCallback.fulfill()
clientStateMachine.notifyContainerChange()
let pair2InitiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
self.wait(for: [pair2InitiatorDumpCallback], timeout: 10)
let pair2AcceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 3, "should be 3 peer ids")
pair2AcceptorDumpCallback.fulfill()
clientStateMachine.notifyContainerChange()
let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
self.wait(for: [initiatorDumpCallback], timeout: 10)
let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
acceptorDumpCallback.fulfill()
clientStateMachine.notifyContainerChange()
let pair2InitiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
self.wait(for: [pair2InitiatorDumpCallback], timeout: 10)
let pair2AcceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 3, "should be 3 peer ids")
pair2AcceptorDumpCallback.fulfill()
// XCTAssert(SOSCircleHasPeer(self.circle, self.fcAcceptor.peerInfo(), nil), "HasPeer 2") <rdar://problem/54040068>
}
+ func tlkInPairingChannel(packet: Data) throws -> Bool {
+ let plist = try self.pairingPacketToPlist(packet: packet)
+
+ guard let arrayOfItems = (plist["d"] as? [[String: Any]]) else {
+ return false
+ }
+
+ var foundTLK = false
+ arrayOfItems.forEach { item in
+ guard let agrp = (item["agrp"] as? String) else {
+ return
+ }
+ guard let cls = (item["class"] as? String) else {
+ return
+ }
+ if cls == "inet" && agrp == "com.apple.security.ckks" {
+ foundTLK = true
+ }
+ }
+ return foundTLK
+ }
+
func testJoin() {
self.startCKAccountStatusMock()
/* calling Join */
let rpcJoinCallbackOccurs = self.expectation(description: "rpcJoin callback occurs")
- self.cuttlefishContext.rpcJoin(v, vouchSig: vS, preapprovedKeys: nil) { error in
+ self.cuttlefishContext.rpcJoin(v, vouchSig: vS) { error in
XCTAssertNil(error, "error should be nil")
rpcJoinCallbackOccurs.fulfill()
}
let rpcJoinCallbackOccurs = self.expectation(description: "rpcJoin callback occurs")
- self.cuttlefishContext.rpcJoin(v, vouchSig: vS, preapprovedKeys: nil) { error in
+ self.cuttlefishContext.rpcJoin(v, vouchSig: vS) { error in
XCTAssertNil(error, "error should be nil")
rpcJoinCallbackOccurs.fulfill()
}
let rpcJoinCallbackOccurs = self.expectation(description: "rpcJoin callback occurs")
- self.cuttlefishContext.rpcJoin(v, vouchSig: vS, preapprovedKeys: nil) { error in
+ self.cuttlefishContext.rpcJoin(v, vouchSig: vS) { error in
XCTAssertNotNil(error, "error should be set")
rpcJoinCallbackOccurs.fulfill()
}
let clientStateMachine = self.manager.clientStateMachine(forContainerName: OTCKContainerName, contextID: self.contextForAcceptor, clientName: self.initiatorName)
self.silentFetchesAllowed = false
- self.expectCKFetchAndRun(beforeFinished: {
- self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
- self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+ self.expectCKFetchAndRun {
+ self.putFakeKeyHierarchiesInCloudKit()
+ self.putFakeDeviceStatusesInCloudKit()
self.silentFetchesAllowed = true
- })
+ }
clientStateMachine.startOctagonStateMachine()
self.cuttlefishContext.startOctagonStateMachine()
/* calling Join */
let rpcJoinCallbackOccurs = self.expectation(description: "rpcJoin callback occurs")
- self.cuttlefishContext.rpcJoin(v, vouchSig: vS, preapprovedKeys: nil) { error in
+ self.cuttlefishContext.rpcJoin(v, vouchSig: vS) { error in
XCTAssertNil(error, "error should be nil")
rpcJoinCallbackOccurs.fulfill()
}
self.wait(for: [voucherCallback], timeout: 10)
let rpcJoinCallback = self.expectation(description: "joining octagon callback")
- self.manager.rpcJoin(with: self.initiatorPairingConfig, vouchData: voucher, vouchSig: voucherSig, preapprovedKeys: nil) { error in
+ self.manager.rpcJoin(with: self.initiatorPairingConfig, vouchData: voucher, vouchSig: voucherSig) { error in
XCTAssertNil(error, "error should be nil")
rpcJoinCallback.fulfill()
}
self.wait(for: [signInCallback], timeout: 10)
/* INITIATOR FIRST RTT JOINING MESSAGE*/
- var initiatorFirstPacket = Data()
- let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
- initiator.exchangePacket(nil) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorFirstPacket = packet!
- firstInitiatorCallback.fulfill()
- }
-
- self.wait(for: [firstInitiatorCallback], timeout: 10)
+ let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
/* ACCEPTOR FIRST RTT EPOCH*/
- var acceptorFirstPacket = Data()
- let firstAcceptorCallback = self.expectation(description: "firstAcceptorCallback callback occurs")
-
- acceptor.exchangePacket(initiatorFirstPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- acceptorFirstPacket = packet!
- firstAcceptorCallback.fulfill()
- }
- self.wait(for: [firstAcceptorCallback], timeout: 10)
+ let acceptorEpochPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorFirstPacket, reason: "epoch return")
/* INITIATOR SECOND RTT PREPARE*/
- var initiatorSecondPacket = Data()
- let secondInitiatorCallback = self.expectation(description: "secondInitiatorCallback callback occurs")
-
- initiator.exchangePacket(acceptorFirstPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorSecondPacket = packet!
- secondInitiatorCallback.fulfill()
- }
-
- self.wait(for: [secondInitiatorCallback], timeout: 10)
+ let initiatorPreparedIdentityPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorEpochPacket, reason: "prepared identity")
/* ACCEPTOR SECOND RTT */
- var acceptorSecondPacket = Data()
- let SecondAcceptorCallback = self.expectation(description: "SecondAcceptorCallback callback occurs")
-
- acceptor.exchangePacket(initiatorSecondPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- acceptorSecondPacket = packet!
- SecondAcceptorCallback.fulfill()
- }
- self.wait(for: [SecondAcceptorCallback], timeout: 10)
- XCTAssertNotNil(acceptorSecondPacket, "acceptor second packet should not be nil")
+ let acceptorVoucherPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorPreparedIdentityPacket, reason: "epoch return")
/* INITIATOR THIRD STEP*/
- var initiatorThirdPacket: Data?
- let thirdInitiatorCallback = self.expectation(description: "thirdInitiatorCallback callback occurs")
+ let initiatorThirdPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorVoucherPacket, reason: "intitiator third packet")
- initiator.exchangePacket(acceptorSecondPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorThirdPacket = packet!
- thirdInitiatorCallback.fulfill()
- }
- self.wait(for: [thirdInitiatorCallback], timeout: 10)
- XCTAssertNotNil(initiatorThirdPacket, "acceptor second packet should not be nil")
+ /* ACCEPTOR THIRD STEP */
+ let acceptorThirdPacket = self.sendPairingExpectingCompletionAndReply(channel: acceptor, packet: initiatorThirdPacket, reason: "acceptor third packet")
+ XCTAssertFalse(try self.tlkInPairingChannel(packet: acceptorThirdPacket), "pairing channel should NOT transport TLKs for SOS+Octagon")
+
+ /* INITIATOR FOURTH STEP*/
+ self.sendPairingExpectingCompletion(channel: initiator, packet: acceptorThirdPacket, reason: "final packet receipt")
+
+ // pairing completes here
assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
self.verifyDatabaseMocks()
let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
self.wait(for: [initiatorDumpCallback], timeout: 10)
let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
acceptorDumpCallback.fulfill()
self.assertSOSSuccess()
}
- func testProximitySetupUsingCliqueOctagonOnly() {
+ func testProximitySetupUsingCliqueOctagonOnly() throws {
OctagonSetPlatformSupportsSOS(false)
OctagonSetIsEnabled(true)
self.startCKAccountStatusMock()
self.wait(for: [signInCallback], timeout: 10)
/* INITIATOR FIRST RTT JOINING MESSAGE*/
- var initiatorFirstPacket = Data()
- let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
- initiator.exchangePacket(nil) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorFirstPacket = packet!
- firstInitiatorCallback.fulfill()
- }
-
- self.wait(for: [firstInitiatorCallback], timeout: 10)
+ let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
/* ACCEPTOR FIRST RTT EPOCH*/
- var acceptorFirstPacket = Data()
- let firstAcceptorCallback = self.expectation(description: "firstAcceptorCallback callback occurs")
-
- acceptor.exchangePacket(initiatorFirstPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- acceptorFirstPacket = packet!
- firstAcceptorCallback.fulfill()
- }
- self.wait(for: [firstAcceptorCallback], timeout: 10)
+ let acceptorEpochPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorFirstPacket, reason: "epoch return")
/* INITIATOR SECOND RTT PREPARE*/
- var initiatorSecondPacket = Data()
- let secondInitiatorCallback = self.expectation(description: "secondInitiatorCallback callback occurs")
-
- initiator.exchangePacket(acceptorFirstPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorSecondPacket = packet!
- secondInitiatorCallback.fulfill()
- }
-
- self.wait(for: [secondInitiatorCallback], timeout: 10)
+ let initiatorPreparedIdentityPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorEpochPacket, reason: "prepared identity")
/* ACCEPTOR SECOND RTT */
- var acceptorSecondPacket = Data()
- let SecondAcceptorCallback = self.expectation(description: "SecondAcceptorCallback callback occurs")
+ let acceptorVoucherPacket = self.sendPairingExpectingCompletionAndReply(channel: acceptor, packet: initiatorPreparedIdentityPacket, reason: "acceptor third packet")
- acceptor.exchangePacket(initiatorSecondPacket) { complete, packet, error in
- XCTAssertTrue(complete, "should be true")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- acceptorSecondPacket = packet!
- SecondAcceptorCallback.fulfill()
- }
- self.wait(for: [SecondAcceptorCallback], timeout: 10)
- XCTAssertNotNil(acceptorSecondPacket, "acceptor second packet should not be nil")
+ // the tlks are in the 3rd roundtrip, but lets check here too
+ XCTAssertFalse(try self.tlkInPairingChannel(packet: acceptorVoucherPacket), "pairing channel should not transport TLKs for octagon")
/* INITIATOR THIRD STEP*/
- var initiatorThirdPacket: Data?
- let thirdInitiatorCallback = self.expectation(description: "thirdInitiatorCallback callback occurs")
-
- initiator.exchangePacket(acceptorSecondPacket) { complete, packet, error in
- XCTAssertTrue(complete, "should be true")
- XCTAssertNil(packet, "packet should be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorThirdPacket = (packet)
- thirdInitiatorCallback.fulfill()
- }
- self.wait(for: [thirdInitiatorCallback], timeout: 10)
- XCTAssertNil(initiatorThirdPacket, "acceptor second packet should be nil")
+ self.sendPairingExpectingCompletion(channel: initiator, packet: acceptorVoucherPacket, reason: "final packet receipt")
assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
self.verifyDatabaseMocks()
let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
self.wait(for: [initiatorDumpCallback], timeout: 10)
let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
acceptorDumpCallback.fulfill()
self.assertEnters(context: initiator1Context, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
- let (acceptor, initiator) = self.setupPairingEndpoints(withPairNumber: "1", initiatorContextID: OTDefaultContext, acceptorContextID: self.contextForAcceptor, initiatorUniqueID: self.initiatorName, acceptorUniqueID: "acceptor-2")
+ let (acceptor, initiator) = self.setupPairingEndpoints(withPairNumber: "1",
+ initiatorContextID: OTDefaultContext,
+ acceptorContextID: self.contextForAcceptor,
+ initiatorUniqueID: self.initiatorName,
+ acceptorUniqueID: "acceptor-2")
XCTAssertNotNil(acceptor, "acceptor should not be nil")
XCTAssertNotNil(initiator, "initiator should not be nil")
self.wait(for: [signInCallback], timeout: 10)
/* INITIATOR FIRST RTT JOINING MESSAGE*/
- var initiatorFirstPacket = Data()
- let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
- initiator.exchangePacket(nil) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorFirstPacket = packet!
- firstInitiatorCallback.fulfill()
- }
-
- self.wait(for: [firstInitiatorCallback], timeout: 10)
+ let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
/* ACCEPTOR FIRST RTT EPOCH*/
- var acceptorFirstPacket = Data()
- let firstAcceptorCallback = self.expectation(description: "firstAcceptorCallback callback occurs")
-
- acceptor.exchangePacket(initiatorFirstPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- acceptorFirstPacket = packet!
- firstAcceptorCallback.fulfill()
- }
- self.wait(for: [firstAcceptorCallback], timeout: 10)
+ let acceptorEpochPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorFirstPacket, reason: "epoch return")
/* INITIATOR SECOND RTT PREPARE*/
- var initiatorSecondPacket = Data()
- let secondInitiatorCallback = self.expectation(description: "secondInitiatorCallback callback occurs")
-
- initiator.exchangePacket(acceptorFirstPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorSecondPacket = packet!
- secondInitiatorCallback.fulfill()
- }
-
- self.wait(for: [secondInitiatorCallback], timeout: 10)
+ let initiatorPreparedIdentityPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorEpochPacket, reason: "prepared identity")
/* ACCEPTOR SECOND RTT */
- var acceptorSecondPacket = Data()
- let SecondAcceptorCallback = self.expectation(description: "SecondAcceptorCallback callback occurs")
+ let acceptorVoucherPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorPreparedIdentityPacket, reason: "epoch return")
- acceptor.exchangePacket(initiatorSecondPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- acceptorSecondPacket = packet!
- SecondAcceptorCallback.fulfill()
- }
- self.wait(for: [SecondAcceptorCallback], timeout: 10)
- XCTAssertNotNil(acceptorSecondPacket, "acceptor second packet should not be nil")
+ // the tlks are in the 3rd roundtrip, but lets check here too
+ XCTAssertFalse(try self.tlkInPairingChannel(packet: acceptorVoucherPacket), "pairing channel should transport TLKs for SOS not 2nd step though")
/* INITIATOR THIRD STEP*/
- var initiatorThirdPacket: Data?
- let thirdInitiatorCallback = self.expectation(description: "thirdInitiatorCallback callback occurs")
+ let initiatorThirdPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorVoucherPacket, reason: "intitiator third packet")
- initiator.exchangePacket(acceptorSecondPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorThirdPacket = packet!
- thirdInitiatorCallback.fulfill()
- }
- self.wait(for: [thirdInitiatorCallback], timeout: 10)
- XCTAssertNotNil(initiatorThirdPacket, "acceptor second packet should not be nil")
+ /* ACCEPTOR THIRD STEP */
+ let acceptorThirdPacket = self.sendPairingExpectingCompletionAndReply(channel: acceptor, packet: initiatorThirdPacket, reason: "acceptor third packet")
+
+ XCTAssertTrue(try self.tlkInPairingChannel(packet: acceptorThirdPacket), "pairing channel should transport TLKs for SOS")
+
+ /* INITIATOR FORTH STEP*/
+ self.sendPairingExpectingCompletion(channel: initiator, packet: acceptorThirdPacket, reason: "final packet receipt")
self.assertSOSSuccess()
}
self.wait(for: [signInCallback], timeout: 10)
/* INITIATOR FIRST RTT JOINING MESSAGE*/
- var initiatorFirstPacket = Data()
- let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
- initiator.exchangePacket(nil) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorFirstPacket = packet!
- firstInitiatorCallback.fulfill()
- }
-
- self.wait(for: [firstInitiatorCallback], timeout: 10)
+ let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
/* ACCEPTOR FIRST RTT EPOCH*/
- var acceptorFirstPacket = Data()
- let firstAcceptorCallback = self.expectation(description: "firstAcceptorCallback callback occurs")
-
- acceptor.exchangePacket(initiatorFirstPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- acceptorFirstPacket = packet!
- firstAcceptorCallback.fulfill()
- }
- self.wait(for: [firstAcceptorCallback], timeout: 10)
+ let acceptorEpochPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorFirstPacket, reason: "epoch return")
/* INITIATOR SECOND RTT PREPARE*/
- var initiatorSecondPacket = Data()
- let secondInitiatorCallback = self.expectation(description: "secondInitiatorCallback callback occurs")
-
- initiator.exchangePacket(acceptorFirstPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorSecondPacket = packet!
- secondInitiatorCallback.fulfill()
- }
-
- self.wait(for: [secondInitiatorCallback], timeout: 10)
+ let initiatorPreparedIdentityPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorEpochPacket, reason: "prepared identity")
/* ACCEPTOR SECOND RTT */
- var acceptorSecondPacket = Data()
- let SecondAcceptorCallback = self.expectation(description: "SecondAcceptorCallback callback occurs")
-
- acceptor.exchangePacket(initiatorSecondPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- acceptorSecondPacket = packet!
- SecondAcceptorCallback.fulfill()
- }
- self.wait(for: [SecondAcceptorCallback], timeout: 10)
- XCTAssertNotNil(acceptorSecondPacket, "acceptor second packet should not be nil")
+ let acceptorVoucherPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorPreparedIdentityPacket, reason: "epoch return")
/* INITIATOR THIRD STEP*/
- var initiatorThirdPacket: Data?
- let thirdInitiatorCallback = self.expectation(description: "thirdInitiatorCallback callback occurs")
-
- initiator.exchangePacket(acceptorSecondPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorThirdPacket = packet!
- thirdInitiatorCallback.fulfill()
- }
- self.wait(for: [thirdInitiatorCallback], timeout: 10)
- XCTAssertNotNil(initiatorThirdPacket, "acceptor second packet should not be nil")
+ _ = self.sendPairingExpectingReply(channel: initiator, packet: acceptorVoucherPacket, reason: "intitiator third packet")
assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
self.verifyDatabaseMocks()
let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
self.wait(for: [initiatorDumpCallback], timeout: 10)
let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
- self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
- dump, _ in
+ self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
XCTAssertNotNil(dump, "dump should not be nil")
- let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+ let egoSelf = dump!["self"] as? [String: AnyObject]
XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
- let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+ let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
- let included = dynamicInfo!["included"] as? Array<String>
+ let included = dynamicInfo!["included"] as? [String]
XCTAssertNotNil(included, "included should not be nil")
XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
acceptorDumpCallback.fulfill()
self.wait(for: [signInCallback], timeout: 10)
/* INITIATOR FIRST RTT JOINING MESSAGE*/
- let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
- initiator.exchangePacket(nil) { complete, packet, error in
- XCTAssertNil(error, "should be no error")
- XCTAssertTrue(complete, "should be true")
- XCTAssertNil(packet, "packet should be nil")
- firstInitiatorCallback.fulfill()
- }
-
- self.wait(for: [firstInitiatorCallback], timeout: 10)
+ self.sendPairingExpectingCompletion(channel: initiator, packet: nil, reason: "error on first message")
}
func testProximitySetupOctagonAndSOSWithOctagonAcceptorMessage1Failure() {
self.wait(for: [signInCallback], timeout: 10)
/* INITIATOR FIRST RTT JOINING MESSAGE*/
- var initiatorFirstPacket = Data()
- let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
- initiator.exchangePacket(nil) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorFirstPacket = packet!
- firstInitiatorCallback.fulfill()
- }
-
- self.wait(for: [firstInitiatorCallback], timeout: 10)
+ let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
acceptor.setOctagonMessageFailForTesting(true)
}
self.wait(for: [firstAcceptorCallback], timeout: 10)
}
- func testProximitySetupOctagonAndSOSWithOctagonInitiatorMessage2Failure() {
+ func testProximitySetupOctagonAndSOSWithOctagonInitiatorMessage2Failure() {
OctagonSetPlatformSupportsSOS(true)
OctagonSetIsEnabled(true)
self.startCKAccountStatusMock()
self.wait(for: [signInCallback], timeout: 10)
/* INITIATOR FIRST RTT JOINING MESSAGE*/
- var initiatorFirstPacket = Data()
- let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
- initiator.exchangePacket(nil) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorFirstPacket = packet!
- firstInitiatorCallback.fulfill()
- }
-
- self.wait(for: [firstInitiatorCallback], timeout: 10)
+ let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
/* ACCEPTOR FIRST RTT EPOCH*/
- var acceptorFirstPacket = Data()
- let firstAcceptorCallback = self.expectation(description: "firstAcceptorCallback callback occurs")
-
- acceptor.exchangePacket(initiatorFirstPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- acceptorFirstPacket = packet!
- firstAcceptorCallback.fulfill()
- }
- self.wait(for: [firstAcceptorCallback], timeout: 10)
+ let acceptorEpochPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorFirstPacket, reason: "epoch return")
/* INITIATOR SECOND RTT PREPARE*/
let secondInitiatorCallback = self.expectation(description: "secondInitiatorCallback callback occurs")
//set up initiator's message 2 to fail
initiator.setOctagonMessageFailForTesting(true)
- initiator.exchangePacket(acceptorFirstPacket) { complete, packet, error in
+ initiator.exchangePacket(acceptorEpochPacket) { complete, packet, error in
XCTAssertNil(error, "should be no error")
XCTAssertTrue(complete, "should be true")
XCTAssertNil(packet, "packet should not be nil")
self.wait(for: [signInCallback], timeout: 10)
/* INITIATOR FIRST RTT JOINING MESSAGE*/
- var initiatorFirstPacket = Data()
- let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
- initiator.exchangePacket(nil) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorFirstPacket = packet!
- firstInitiatorCallback.fulfill()
- }
-
- self.wait(for: [firstInitiatorCallback], timeout: 10)
+ let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
/* ACCEPTOR FIRST RTT EPOCH*/
- var acceptorFirstPacket = Data()
- let firstAcceptorCallback = self.expectation(description: "firstAcceptorCallback callback occurs")
-
- acceptor.exchangePacket(initiatorFirstPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- acceptorFirstPacket = packet!
- firstAcceptorCallback.fulfill()
- }
- self.wait(for: [firstAcceptorCallback], timeout: 10)
+ let acceptorEpochPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorFirstPacket, reason: "epoch return")
/* INITIATOR SECOND RTT PREPARE*/
- var initiatorSecondPacket = Data()
- let secondInitiatorCallback = self.expectation(description: "secondInitiatorCallback callback occurs")
-
- initiator.exchangePacket(acceptorFirstPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorSecondPacket = packet!
- secondInitiatorCallback.fulfill()
- }
-
- self.wait(for: [secondInitiatorCallback], timeout: 10)
+ let initiatorPreparedIdentityPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorEpochPacket, reason: "prepared identity")
/* ACCEPTOR SECOND RTT */
let SecondAcceptorCallback = self.expectation(description: "SecondAcceptorCallback callback occurs")
acceptor.setOctagonMessageFailForTesting(true)
- acceptor.exchangePacket(initiatorSecondPacket) { complete, packet, error in
+ acceptor.exchangePacket(initiatorPreparedIdentityPacket) { complete, packet, error in
XCTAssertNil(error, "should be no error")
XCTAssertTrue(complete, "should be true")
XCTAssertNil(packet, "packet should be nil")
self.wait(for: [signInCallback], timeout: 10)
/* INITIATOR FIRST RTT JOINING MESSAGE*/
- var initiatorFirstPacket = Data()
- let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
- initiator.exchangePacket(nil) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorFirstPacket = packet!
- firstInitiatorCallback.fulfill()
- }
-
- self.wait(for: [firstInitiatorCallback], timeout: 10)
+ let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
/* ACCEPTOR FIRST RTT EPOCH*/
- var acceptorFirstPacket = Data()
- let firstAcceptorCallback = self.expectation(description: "firstAcceptorCallback callback occurs")
-
- acceptor.exchangePacket(initiatorFirstPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- acceptorFirstPacket = packet!
- firstAcceptorCallback.fulfill()
- }
- self.wait(for: [firstAcceptorCallback], timeout: 10)
+ let acceptorEpochPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorFirstPacket, reason: "epoch return")
/* INITIATOR SECOND RTT PREPARE*/
- var initiatorSecondPacket = Data()
- let secondInitiatorCallback = self.expectation(description: "secondInitiatorCallback callback occurs")
-
- initiator.exchangePacket(acceptorFirstPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorSecondPacket = packet!
- secondInitiatorCallback.fulfill()
- }
-
- self.wait(for: [secondInitiatorCallback], timeout: 10)
+ let initiatorPreparedIdentityPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorEpochPacket, reason: "prepared identity")
/* ACCEPTOR SECOND RTT */
- var acceptorSecondPacket = Data()
- let SecondAcceptorCallback = self.expectation(description: "SecondAcceptorCallback callback occurs")
-
- acceptor.exchangePacket(initiatorSecondPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- acceptorSecondPacket = packet!
- SecondAcceptorCallback.fulfill()
- }
- self.wait(for: [SecondAcceptorCallback], timeout: 10)
- XCTAssertNotNil(acceptorSecondPacket, "acceptor second packet should not be nil")
+ let acceptorVoucherPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorPreparedIdentityPacket, reason: "epoch return")
/* INITIATOR THIRD STEP*/
let thirdInitiatorCallback = self.expectation(description: "thirdInitiatorCallback callback occurs")
initiator.setOctagonMessageFailForTesting(true)
- initiator.exchangePacket(acceptorSecondPacket) { complete, packet, error in
+ initiator.exchangePacket(acceptorVoucherPacket) { complete, packet, error in
XCTAssertNil(error, "should be no error")
XCTAssertTrue(complete, "should be true")
XCTAssertNil(packet, "packet should be nil")
self.wait(for: [signInCallback], timeout: 10)
/* INITIATOR FIRST RTT JOINING MESSAGE*/
- var initiatorFirstPacket = Data()
- let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
- initiator.exchangePacket(nil) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorFirstPacket = packet!
- firstInitiatorCallback.fulfill()
- }
-
- self.wait(for: [firstInitiatorCallback], timeout: 10)
+ let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
/* ACCEPTOR FIRST RTT EPOCH*/
- var acceptorFirstPacket = Data()
- let firstAcceptorCallback = self.expectation(description: "firstAcceptorCallback callback occurs")
-
- acceptor.exchangePacket(initiatorFirstPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- acceptorFirstPacket = packet!
- firstAcceptorCallback.fulfill()
- }
- self.wait(for: [firstAcceptorCallback], timeout: 10)
-
- initiator.setSessionSupportsOctagonForTesting(false)
+ let acceptorEpochPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorFirstPacket, reason: "epoch return")
/* INITIATOR SECOND RTT PREPARE*/
- var initiatorSecondPacket = Data()
- let secondInitiatorCallback = self.expectation(description: "secondInitiatorCallback callback occurs")
-
- initiator.exchangePacket(acceptorFirstPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorSecondPacket = packet!
- secondInitiatorCallback.fulfill()
- }
-
- self.wait(for: [secondInitiatorCallback], timeout: 10)
+ let initiatorPreparedIdentityPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorEpochPacket, reason: "prepared identity")
/* ACCEPTOR SECOND RTT */
- var acceptorSecondPacket = Data()
- let SecondAcceptorCallback = self.expectation(description: "SecondAcceptorCallback callback occurs")
-
- acceptor.exchangePacket(initiatorSecondPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- acceptorSecondPacket = packet!
- SecondAcceptorCallback.fulfill()
- }
- self.wait(for: [SecondAcceptorCallback], timeout: 10)
- XCTAssertNotNil(acceptorSecondPacket, "acceptor second packet should not be nil")
+ let acceptorVoucherPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorPreparedIdentityPacket, reason: "epoch return")
/* INITIATOR THIRD STEP*/
- var initiatorThirdPacket: Data?
- let thirdInitiatorCallback = self.expectation(description: "thirdInitiatorCallback callback occurs")
-
- initiator.exchangePacket(acceptorSecondPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorThirdPacket = packet!
- thirdInitiatorCallback.fulfill()
- }
- self.wait(for: [thirdInitiatorCallback], timeout: 10)
- XCTAssertNotNil(initiatorThirdPacket, "acceptor second packet should not be nil")
+ _ = self.sendPairingExpectingReply(channel: initiator, packet: acceptorVoucherPacket, reason: "intitiator third packet")
/*
need to fix attempting sos upgrade in the tests when pairing/piggybacking and then kicking off an upgrade
let initiatorContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
self.wait(for: [signInCallback], timeout: 10)
/* INITIATOR FIRST RTT JOINING MESSAGE*/
- var initiatorFirstPacket = Data()
- let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
- initiator.exchangePacket(nil) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorFirstPacket = packet!
- firstInitiatorCallback.fulfill()
- }
-
- self.wait(for: [firstInitiatorCallback], timeout: 10)
+ let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
/* ACCEPTOR FIRST RTT EPOCH*/
- var acceptorFirstPacket = Data()
- let firstAcceptorCallback = self.expectation(description: "firstAcceptorCallback callback occurs")
-
- acceptor.exchangePacket(initiatorFirstPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- acceptorFirstPacket = packet!
- firstAcceptorCallback.fulfill()
- }
- self.wait(for: [firstAcceptorCallback], timeout: 10)
+ let acceptorEpochPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorFirstPacket, reason: "epoch return")
/* INITIATOR SECOND RTT PREPARE*/
- var initiatorSecondPacket = Data()
- let secondInitiatorCallback = self.expectation(description: "secondInitiatorCallback callback occurs")
-
- initiator.exchangePacket(acceptorFirstPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorSecondPacket = packet!
- secondInitiatorCallback.fulfill()
- }
-
- self.wait(for: [secondInitiatorCallback], timeout: 10)
+ let initiatorPreparedIdentityPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorEpochPacket, reason: "prepared identity")
/* ACCEPTOR SECOND RTT */
- var acceptorSecondPacket = Data()
- let SecondAcceptorCallback = self.expectation(description: "SecondAcceptorCallback callback occurs")
-
- acceptor.exchangePacket(initiatorSecondPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- acceptorSecondPacket = packet!
- SecondAcceptorCallback.fulfill()
- }
- self.wait(for: [SecondAcceptorCallback], timeout: 10)
- XCTAssertNotNil(acceptorSecondPacket, "acceptor second packet should not be nil")
+ let acceptorVoucherPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorPreparedIdentityPacket, reason: "epoch return")
/* INITIATOR THIRD STEP*/
- var initiatorThirdPacket: Data?
- let thirdInitiatorCallback = self.expectation(description: "thirdInitiatorCallback callback occurs")
-
- initiator.exchangePacket(acceptorSecondPacket) { complete, packet, error in
- XCTAssertFalse(complete, "should be false")
- XCTAssertNotNil(packet, "packet should not be nil")
- XCTAssertNil(error, "error should be nil")
- initiatorThirdPacket = packet!
- thirdInitiatorCallback.fulfill()
- }
- self.wait(for: [thirdInitiatorCallback], timeout: 10)
- XCTAssertNotNil(initiatorThirdPacket, "acceptor second packet should not be nil")
+ _ = self.sendPairingExpectingReply(channel: initiator, packet: acceptorVoucherPacket, reason: "intitiator third packet")
/*
need to fix attempting sos upgrade in the tests when pairing/piggybacking and then kicking off an upgrade
let keyPair = _SFECKeyPair.init(randomKeyPairWith: _SFECKeySpecifier.init(curve: SFEllipticCurve.nistp384))!
- var keyAttributes: Dictionary<String, String> = [:]
+ var keyAttributes: [String: String] = [:]
keyAttributes[kSecAttrKeyClass as String] = kSecAttrKeyClassPrivate as String
keyAttributes[kSecAttrKeyType as String] = kSecAttrKeyTypeEC as String
XCTAssertNotNil(octagonSigningKey, "signing key should not be nil")
XCTAssertNotNil(octagonEncryptionKey, "encryption key should not be nil")
- var gestalt: Dictionary<String, String> = [:]
+ var gestalt: [String: String] = [:]
gestalt[kPIUserDefinedDeviceNameKey as String] = "Fakey"
let newPeerInfo = SOSPeerInfoCreate(nil, gestalt as CFDictionary, nil, signingKey, octagonSigningKey, octagonEncryptionKey, nil)
}
func nextSecret() -> String {
- if (self.incorrectTries > 0) {
+ if self.incorrectTries > 0 {
self.incorrectTries -= 1
return self.incorrectSecret
}
class KCJoiningAcceptTestDelegate: NSObject, KCJoiningAcceptSecretDelegate, KCJoiningAcceptCircleDelegate {
- var secrets: Array<String> = []
+ var secrets: [String] = []
var currentSecret: Int = 0
var retriesLeft: Int = 0
var retriesPerSecret: Int = 0
var circleJoinData = Data()
var peerInfo: SOSPeerInfoRef?
- class func acceptDelegateWithSecrets(secrets: Array<String>, retries: Int, code: String) -> KCJoiningAcceptTestDelegate {
+ class func acceptDelegateWithSecrets(secrets: [String], retries: Int, code: String) -> KCJoiningAcceptTestDelegate {
return KCJoiningAcceptTestDelegate(withSecrets: secrets, retries: retries, code: code)
}
}
class func initWithSecret(secret: String, code: String) -> KCJoiningAcceptTestDelegate {
- var secretArray: Array<String> = Array()
+ var secretArray: [String] = Array()
secretArray.append(secret)
return KCJoiningAcceptTestDelegate(withSecrets: secretArray, retries: 3, code: code)
}
- init(withSecrets secrets: Array<String>, retries: Int, code: String) {
+ init(withSecrets secrets: [String], retries: Int, code: String) {
self.secrets = secrets
self.currentSecret = 0
}
func advanceSecret() -> KCRetryOrNot {
- if (self.retriesLeft == 0) {
+ if self.retriesLeft == 0 {
self.currentSecret += 1
- if (self.currentSecret >= self.secrets.count) {
+ if self.currentSecret >= self.secrets.count {
self.currentSecret = self.secrets.count - 1
}
self.retriesLeft = self.retriesPerSecret
}
}
-@objcMembers class OctagonPairingTests: OctagonTestsBase {
+// Similar to the helpers below, but more accessible
+extension OctagonTestsBase {
+ func joiningConfigurations(initiator: OTCuttlefishContext, initiatorDeviceID: String, sponsor: OTCuttlefishContext, sponsorDeviceID: String) -> (OTJoiningConfiguration, OTJoiningConfiguration) {
+ let acceptorConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing,
+ uniqueDeviceID: sponsorDeviceID,
+ uniqueClientID: initiatorDeviceID,
+ pairingUUID: UUID().uuidString,
+ containerName: OTCKContainerName,
+ contextID: sponsor.contextID,
+ epoch: 1,
+ isInitiator: false)
+
+ let initiatorConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing,
+ uniqueDeviceID: initiatorDeviceID,
+ uniqueClientID: initiatorDeviceID,
+ pairingUUID: UUID().uuidString,
+ containerName: OTCKContainerName,
+ contextID: initiator.contextID,
+ epoch: 1,
+ isInitiator: true)
+
+ return (acceptorConfig, initiatorConfig)
+ }
+
+ func setupPairingChannels(initiator: OTCuttlefishContext, sponsor: OTCuttlefishContext) -> (KCPairingChannel, KCPairingChannel) {
+ let sponsorChannelContext = KCPairingChannelContext()
+ sponsorChannelContext.model = sponsor.deviceAdapter.modelID()
+ sponsorChannelContext.osVersion = sponsor.deviceAdapter.osVersion()
+ sponsorChannelContext.modelClass = "AcceptorModelClass"
+
+ sponsorChannelContext.uniqueDeviceID = UUID().uuidString
+ sponsorChannelContext.uniqueClientID = UUID().uuidString
+
+ let initiatorChannelContext = KCPairingChannelContext()
+ initiatorChannelContext.model = initiator.deviceAdapter.modelID()
+ initiatorChannelContext.osVersion = initiator.deviceAdapter.osVersion()
+ initiatorChannelContext.modelClass = "InitiatorModelClass"
+
+ // The initiator's client ID is equivalent to the sponsor's client ID
+ initiatorChannelContext.uniqueDeviceID = sponsorChannelContext.uniqueClientID
+
+ let sponsorPairingChannel = KCPairingChannel(acceptor: sponsorChannelContext)
+ let initiatorPairingChannel = KCPairingChannel(initiator: initiatorChannelContext)
+
+ XCTAssertNotNil(sponsorPairingChannel, "Should have a sponsor pairing channel")
+ XCTAssertNotNil(initiatorPairingChannel, "Should have a initiator pairing channel")
+
+ sponsorPairingChannel?.setControlObject(self.otControl)
+ initiatorPairingChannel?.setControlObject(self.otControl)
+
+ let (acceptorPairingConfig, initiatorPairingConfig) = self.joiningConfigurations(initiator: initiator,
+ initiatorDeviceID: initiatorChannelContext.uniqueDeviceID,
+ sponsor: sponsor,
+ sponsorDeviceID: sponsorChannelContext.uniqueDeviceID)
+
+ sponsorPairingChannel?.setConfiguration(acceptorPairingConfig)
+ initiatorPairingChannel?.setConfiguration(initiatorPairingConfig)
+
+ let fakeCircle = SOSCircleCreate(kCFAllocatorDefault, "TEST DOMAIN" as CFString, nil) as SOSCircleRef
+
+ sponsorPairingChannel?.setXPCConnectionObject(FakeNSXPCConnectionSOS(withSOSControl: FCPairingFakeSOSControl(randomAccountKey: true, circle: fakeCircle)))
+ initiatorPairingChannel?.setXPCConnectionObject(FakeNSXPCConnectionSOS(withSOSControl: FCPairingFakeSOSControl(randomAccountKey: true, circle: fakeCircle)))
+
+ return (sponsorPairingChannel!, initiatorPairingChannel!)
+ }
+
+ func setupPiggybackingSessions(initiator: OTCuttlefishContext,
+ sponsor: OTCuttlefishContext) throws -> (KCJoiningRequestTestDelegate, KCJoiningAcceptTestDelegate, KCJoiningAcceptSession, KCJoiningRequestSecretSession) {
+ let (acceptorJoiningConfig, _) = self.joiningConfigurations(initiator: initiator,
+ initiatorDeviceID: UUID().uuidString,
+ sponsor: sponsor,
+ sponsorDeviceID: UUID().uuidString)
+
+ return try self.setupKCJoiningSessionObjects(dsid: 0x123456,
+ sponsorConfiguration: acceptorJoiningConfig)
+ }
+
+ func setupKCJoiningSessionObjects(dsid: UInt64,
+ sponsorConfiguration: OTJoiningConfiguration) throws -> (KCJoiningRequestTestDelegate, KCJoiningAcceptTestDelegate, KCJoiningAcceptSession, KCJoiningRequestSecretSession) {
+ let secret = "123456"
+ let code = "987654"
+
+ let requestDelegate = KCJoiningRequestTestDelegate.requestDelegate(withSecret: secret)
+ let acceptDelegate = KCJoiningAcceptTestDelegate.acceptDelegateWithSecret(secret: secret, code: code)
+
+ let requestSession = try KCJoiningRequestSecretSession(secretDelegate: requestDelegate as KCJoiningRequestSecretDelegate, dsid: dsid, rng: ccDRBGGetRngState())
+
+ let acceptSession = try KCJoiningAcceptSession(secretDelegate: acceptDelegate as KCJoiningAcceptSecretDelegate,
+ circleDelegate: acceptDelegate as KCJoiningAcceptCircleDelegate,
+ dsid: dsid,
+ rng: ccDRBGGetRngState())
+ requestSession.setControlObject(self.otControl)
+ acceptSession.setControlObject(self.otControl)
+
+ // requestSessions don't need control objects
+ acceptSession.setConfiguration(sponsorConfiguration)
+
+ return (requestDelegate, acceptDelegate, acceptSession, requestSession)
+ }
+
+ func octagonPiggypackingMessage(in message: KCJoiningMessage) throws -> OTPairingMessage {
+ guard let octagonData = message.secondData else {
+ throw NSError(domain: "missing octagon data" as String, code: -1, userInfo: nil)
+ }
+
+ return OTPairingMessage(data: octagonData)
+ }
+
+ func unpackPiggybackingInitialMessage(identityMessage: Data, session: KCAESGCMDuplexSession) throws -> OTPairingMessage {
+ let encryptedJoiningIdentityMessage = try KCJoiningMessage(der: identityMessage)
+ let joiningIdentityMessage = try session.decryptAndVerify(encryptedJoiningIdentityMessage.firstData)
+
+ let initialMessageProtobuf = KCInitialMessageData(data: joiningIdentityMessage)
+ XCTAssertNotNil(initialMessageProtobuf, "should have an initial message container")
+ let initiatorIdentityMessageOpt = OTPairingMessage(data: initialMessageProtobuf!.prepareMessage)
+ XCTAssertNotNil(initiatorIdentityMessageOpt, "should have an Octagon message container")
+ return initiatorIdentityMessageOpt!
+ }
+
+ func makePiggybackingPacket(combining currentMessage: KCJoiningMessage, octagonMessage: OTPairingMessage) throws -> Data {
+ let newMessage = try KCJoiningMessage(type: currentMessage.type,
+ data: currentMessage.firstData,
+ payload: octagonMessage.data)
+ return newMessage.der
+ }
+
+ func pairingPacketToPlist(packet: Data) throws -> [String: Any] {
+ let binaryPlist = KCPairingChannel.pairingChannelDecompressData(packet)!
+ let plist = (try PropertyListSerialization.propertyList(from: binaryPlist, options: [], format: nil)) as! [String: Any]
+ return plist
+ }
+
+ func octagonPairingMessage(in packet: Data) throws -> OTPairingMessage {
+ let plist = try self.pairingPacketToPlist(packet: packet)
+
+ guard let octagonData = plist["o"] as? Data else {
+ throw NSError(domain: "missing octagon data" as String, code: -1, userInfo: nil)
+ }
+
+ return OTPairingMessage(data: octagonData)
+ }
+
+ func makePairingPacket(combining currentPacket: Data, octagonMessage: OTPairingMessage) throws -> Data {
+ var plist = try self.pairingPacketToPlist(packet: currentPacket)
+ plist["o"] = octagonMessage.data
+
+ return KCPairingChannel.pairingChannelCompressData(try PropertyListSerialization.data(fromPropertyList: plist, format: .binary, options: 0))
+ }
+
+ func sendPairingExpectingReply(channel: KCPairingChannel, packet: Data?, reason: String) -> Data {
+ var packetResponse = Data()
+ let callback = self.expectation(description: "callback occurs (\(reason))")
+
+ channel.exchangePacket(packet) { complete, response, error in
+ XCTAssertNil(error, "error should be nil (\(reason))")
+
+ XCTAssertFalse(complete, "Expected no completion (\(reason))")
+ XCTAssertNotNil(response, "packet should not be nil (\(reason))")
+ packetResponse = response!
+ callback.fulfill()
+ }
+ self.wait(for: [callback], timeout: 10)
+ return packetResponse
+ }
+
+ func sendPairingExpectingCompletionAndReply(channel: KCPairingChannel, packet: Data?, reason: String) -> Data {
+ var packetResponse = Data()
+ let callback = self.expectation(description: "callback occurs (\(reason))")
+
+ channel.exchangePacket(packet) { complete, response, error in
+ XCTAssertNil(error, "error should be nil (\(reason))")
+
+ XCTAssertTrue(complete, "Expected channel completion (\(reason))")
+ XCTAssertNotNil(response, "response should be present (\(reason))")
+ packetResponse = response!
+ callback.fulfill()
+ }
+ self.wait(for: [callback], timeout: 10)
+ return packetResponse
+ }
+
+ func sendPairingExpectingCompletion(channel: KCPairingChannel, packet: Data?, reason: String) {
+ let callback = self.expectation(description: "callback occurs (\(reason))")
+
+ channel.exchangePacket(packet) { complete, response, error in
+ XCTAssertNil(error, "error should be nil (\(reason))")
+
+ XCTAssertTrue(complete, "Expected channel completion (\(reason))")
+ XCTAssertNil(response, "response should be nil (\(reason))")
+ callback.fulfill()
+ }
+ self.wait(for: [callback], timeout: 10)
+ }
+}
+
+@objcMembers
+class OctagonPairingTests: OctagonTestsBase {
var sosAdapterForAcceptor: CKKSMockSOSPresentAdapter!
var cuttlefishContextForAcceptor: OTCuttlefishContext!
accountStateTracker: self.accountStateTracker,
deviceInformationAdapter: OTMockDeviceInfoAdapter(modelID: "iPhone9,1", deviceName: "test-SOS-iphone", serialNumber: "456", osVersion: "iOS (fake version)"))
- self.acceptorPiggybackingConfig = OTJoiningConfiguration(protocolType: OTProtocolPiggybacking, uniqueDeviceID: "acceptor", uniqueClientID: self.initiatorName, containerName: OTCKContainerName, contextID: self.contextForAcceptor, epoch: 1, isInitiator: false)
- self.initiatorPiggybackingConfig = OTJoiningConfiguration(protocolType: OTProtocolPiggybacking, uniqueDeviceID: "initiator", uniqueClientID: "acceptor", containerName: OTCKContainerName, contextID: OTDefaultContext, epoch: 1, isInitiator: true)
-
- self.acceptorPairingConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing, uniqueDeviceID: "acceptor", uniqueClientID: self.initiatorName, containerName: OTCKContainerName, contextID: self.contextForAcceptor, epoch: 1, isInitiator: false)
- self.initiatorPairingConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing, uniqueDeviceID: "initiator", uniqueClientID: "acceptor", containerName: OTCKContainerName, contextID: OTDefaultContext, epoch: 1, isInitiator: true)
+ self.acceptorPiggybackingConfig = OTJoiningConfiguration(protocolType: OTProtocolPiggybacking,
+ uniqueDeviceID: "acceptor",
+ uniqueClientID: self.initiatorName,
+ pairingUUID: UUID().uuidString,
+ containerName: OTCKContainerName,
+ contextID: self.contextForAcceptor,
+ epoch: 1,
+ isInitiator: false)
+ self.initiatorPiggybackingConfig = OTJoiningConfiguration(protocolType: OTProtocolPiggybacking,
+ uniqueDeviceID: "initiator",
+ uniqueClientID: "acceptor",
+ pairingUUID: UUID().uuidString,
+ containerName: OTCKContainerName,
+ contextID: OTDefaultContext,
+ epoch: 1,
+ isInitiator: true)
+
+ self.acceptorPairingConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing,
+ uniqueDeviceID: "acceptor",
+ uniqueClientID: self.initiatorName,
+ pairingUUID: UUID().uuidString,
+ containerName: OTCKContainerName,
+ contextID: self.contextForAcceptor,
+ epoch: 1,
+ isInitiator: false)
+ self.initiatorPairingConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing,
+ uniqueDeviceID: "initiator",
+ uniqueClientID: "acceptor",
+ pairingUUID: UUID().uuidString,
+ containerName: OTCKContainerName,
+ contextID: OTDefaultContext,
+ epoch: 1,
+ isInitiator: true)
}
func getAcceptorInCircle() {
initiatorContext.osVersion = "InitiatorOsVersion"
initiatorContext.modelClass = "InitiatorModelClass"
initiatorContext.uniqueDeviceID = initiatorUniqueID
- initiatorContext.uniqueDeviceID = initiatorUniqueID
let acceptor = acceptorClique!.setupPairingChannel(asAcceptor: acceptorContext)
let initiator = initiatorClique!.setupPairingChannel(asInitiator: initiatorContext)
acceptor.setControlObject(self.otControl)
initiator.setControlObject(self.otControl)
- let acceptorPairingConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing, uniqueDeviceID: acceptorUniqueID, uniqueClientID: initiatorUniqueID, containerName: OTCKContainerName, contextID: acceptorContextID, epoch: 1, isInitiator: false)
- let initiatorPairingConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing, uniqueDeviceID: initiatorUniqueID, uniqueClientID: initiatorUniqueID, containerName: OTCKContainerName, contextID: initiatorContextID, epoch: 1, isInitiator: true)
+ let acceptorPairingConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing,
+ uniqueDeviceID: acceptorUniqueID,
+ uniqueClientID: initiatorUniqueID,
+ pairingUUID: UUID().uuidString,
+ containerName: OTCKContainerName,
+ contextID: acceptorContextID,
+ epoch: 1,
+ isInitiator: false)
+ let initiatorPairingConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing,
+ uniqueDeviceID: initiatorUniqueID,
+ uniqueClientID: initiatorUniqueID,
+ pairingUUID: UUID().uuidString,
+ containerName: OTCKContainerName,
+ contextID: initiatorContextID,
+ epoch: 1,
+ isInitiator: true)
acceptor.setConfiguration(acceptorPairingConfig)
initiator.setConfiguration(initiatorPairingConfig)
XCTAssertNotNil(acceptorAnalytics, "acceptorAnalytics should not be nil")
secondAcceptorData.analytics = acceptorAnalytics
- do {
- let acceptor = try OTClique(contextData: secondAcceptorData)
- XCTAssertNotNil(acceptor, "Clique should not be nil")
- acceptor.setPairingDefault(true)
+ let acceptor = OTClique(contextData: secondAcceptorData)
+ XCTAssertNotNil(acceptor, "Clique should not be nil")
+ acceptor.setPairingDefault(true)
- let secondInitiatorData = OTConfigurationContext()
- secondInitiatorData.context = "secondInitiator"
- secondInitiatorData.dsid = "i-"+count
- secondInitiatorData.altDSID = "alt-i-"+count
+ let secondInitiatorData = OTConfigurationContext()
+ secondInitiatorData.context = "secondInitiator"
+ secondInitiatorData.dsid = "i-"+count
+ secondInitiatorData.altDSID = "alt-i-"+count
- let initiatorAnalytics = SFSignInAnalytics(signInUUID: "uuid", category: "com.apple.cdp", eventName: "signed in")
- XCTAssertNotNil(initiatorAnalytics, "initiatorAnalytics should not be nil")
- secondInitiatorData.analytics = initiatorAnalytics
- let initiator = try OTClique(contextData: secondInitiatorData)
- XCTAssertNotNil(initiator, "Clique should not be nil")
- initiator.setPairingDefault(true)
+ let initiatorAnalytics = SFSignInAnalytics(signInUUID: "uuid", category: "com.apple.cdp", eventName: "signed in")
+ XCTAssertNotNil(initiatorAnalytics, "initiatorAnalytics should not be nil")
+ secondInitiatorData.analytics = initiatorAnalytics
+ let initiator = OTClique(contextData: secondInitiatorData)
+ XCTAssertNotNil(initiator, "Clique should not be nil")
+ initiator.setPairingDefault(true)
- return (acceptor, initiator)
-
- } catch {
- XCTFail("error creating test clique: \(error)")
- }
- return(nil, nil)
+ return (acceptor, initiator)
}
func setupKCJoiningSessionObjects() -> (KCJoiningRequestTestDelegate?, KCJoiningAcceptTestDelegate?, KCJoiningAcceptSession?, KCJoiningRequestSecretSession?) {
-
- let secret = "123456"
- let code = "987654"
let dsid: UInt64 = 0x1234567887654321
- let requestDelegate = KCJoiningRequestTestDelegate.requestDelegate(withSecret: secret)
- let acceptDelegate = KCJoiningAcceptTestDelegate.acceptDelegateWithSecret(secret: secret, code: code)
-
do {
- let requestSession = try KCJoiningRequestSecretSession(secretDelegate: requestDelegate as KCJoiningRequestSecretDelegate, dsid: dsid, rng: ccDRBGGetRngState())
-
- let acceptSession = try KCJoiningAcceptSession(secretDelegate: acceptDelegate as KCJoiningAcceptSecretDelegate,
- circleDelegate: acceptDelegate as KCJoiningAcceptCircleDelegate,
- dsid: dsid,
- rng: ccDRBGGetRngState())
- requestSession.setControlObject(self.otControl)
- acceptSession.setControlObject(self.otControl)
- requestSession.setConfiguration(self.initiatorPiggybackingConfig)
- acceptSession.setConfiguration(self.acceptorPiggybackingConfig)
-
- return (requestDelegate, acceptDelegate, acceptSession, requestSession)
+ return try self.setupKCJoiningSessionObjects(dsid: dsid, sponsorConfiguration: self.acceptorPiggybackingConfig)
} catch {
XCTFail("error creating test clique: \(error)")
return (nil, nil, nil, nil)
- (long)resetOctagon:(NSString*)container context:(NSString*)contextID altDSID:(NSString*)altDSID;
+- (long)resetProtectedData:(NSString*)container context:(NSString*)contextID altDSID:(NSString*)altDSID appleID:(NSString*)appleID dsid:(NSString*)dsid;
+
- (long)status:(NSString* _Nullable)container context:(NSString*)contextID json:(bool)json;
- (long)recoverUsingBottleID:(NSString*)bottleID
control:(OTControl*)control;
- (long)healthCheck:(NSString* _Nullable)container context:(NSString*)contextID skipRateLimitingCheck:(BOOL)skipRateLimitingCheck;
+- (long)refetchCKKSPolicy:(NSString*)container context:(NSString*)contextID;
- (long)tapToRadar:(NSString *)action description:(NSString *)description radar:(NSString *)radar;
#include "utilities/SecInternalReleasePriv.h"
#import "utilities/debugging.h"
+#import "keychain/ot/OTClique.h"
#import "keychain/ot/OT.h"
#import "keychain/ot/OTConstants.h"
#import "keychain/ot/OTControl.h"
#import "keychain/otctl/OTControlCLI.h"
+
+#import <AuthKit/AKAppleIDAuthenticationController.h>
+#import <AuthKit/AKAppleIDAuthenticationContext.h>
+#import <AuthKit/AKAppleIDAuthenticationContext_Private.h>
+
+static NSString* fetch_pet(NSString* appleID, NSString* dsid)
+{
+ if(!appleID && !dsid) {
+ NSLog(@"Must provide either an AppleID or a DSID to fetch a PET");
+ exit(1);
+ }
+
+ AKAppleIDAuthenticationContext* authContext = [[AKAppleIDAuthenticationContext alloc] init];
+ authContext.username = appleID;
+
+ authContext.authenticationType = AKAppleIDAuthenticationTypeSilent;
+ authContext.isUsernameEditable = NO;
+
+ __block NSString* pet = nil;
+
+ dispatch_semaphore_t s = dispatch_semaphore_create(0);
+
+ AKAppleIDAuthenticationController *authenticationController = [[AKAppleIDAuthenticationController alloc] init];
+ [authenticationController authenticateWithContext:authContext
+ completion:^(AKAuthenticationResults authenticationResults, NSError *error) {
+ if(error) {
+ NSLog(@"error fetching PET: %@", error);
+ exit(1);
+ }
+
+ pet = authenticationResults[AKAuthenticationPasswordKey];
+ dispatch_semaphore_signal(s);
+ }];
+ dispatch_semaphore_wait(s, DISPATCH_TIME_FOREVER);
+
+ return pet;
+}
+
// Mutual recursion to set up an object for jsonification
static NSDictionary* cleanDictionaryForJSON(NSDictionary* dict);
#endif
}
+
+- (long)resetProtectedData:(NSString*)container context:(NSString*)contextID altDSID:(NSString*)altDSID appleID:(NSString*)appleID dsid:(NSString*)dsid
+{
+#if OCTAGON
+ __block long ret = -1;
+
+ NSError* error = nil;
+ OTConfigurationContext *data = [[OTConfigurationContext alloc] init];
+ data.passwordEquivalentToken = fetch_pet(appleID, dsid);
+ data.authenticationAppleID = appleID;
+ data.altDSID = altDSID;
+ data.context = contextID;
+
+ OTClique* clique = [OTClique resetProtectedData:data error:&error];
+ if(clique != nil && error == nil) {
+ ret = 0;
+ }
+ return ret;
+#else
+ printf("Unimplemented.\n");
+ return -1;
+#endif
+}
+
- (void)printPeer:(NSDictionary*)peerInformation prefix:(NSString* _Nullable)prefix {
NSString* peerID = peerInformation[@"peerID"];
NSString* model = peerInformation[@"permanentInfo"][@"model_id"];
#endif
}
+- (long)refetchCKKSPolicy:(NSString*)container context:(NSString*)contextID
+{
+ #if OCTAGON
+ __block long ret = 1;
+
+ [self.control refetchCKKSPolicy:container
+ contextID:contextID
+ reply:^(NSError * _Nullable error) {
+ if(error) {
+ printf("Error refetching CKKS policy: %s\n", [[error description] UTF8String]);
+ } else {
+ printf("CKKS refetch completed.\n");
+ ret = 0;
+ }
+ }];
+ return ret;
+ #else
+ printf("Unimplemented.\n");
+ return 1;
+ #endif
+}
+
- (long)tapToRadar:(NSString *)action description:(NSString *)description radar:(NSString *)radar
{
#if OCTAGON
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
+ <key>com.apple.securebackupd.access</key>
+ <true/>
+ <key>keychain-cloud-circle</key>
+ <true/>
+ <key>com.apple.private.ckks</key>
+ <true/>
+ <key>com.apple.authkit.client.private</key>
+ <true/>
<key>com.apple.private.octagon</key>
<true/>
<key>com.apple.private.escrow-update</key>
static int signIn = false;
static int signOut = false;
static int resetoctagon = false;
+static int resetProtectedData = false;
static int fetchAllBottles = false;
static int recover = false;
static int er_status = false;
static int er_reset = false;
static int er_store = false;
+static int ckks_policy_flag = false;
static int ttr_flag = false;
static char* altDSIDArg = NULL;
static char* containerStr = NULL;
static char* radarNumber = NULL;
+static char* appleIDArg = NULL;
+static char* dsidArg = NULL;
static void internalOnly(void)
{
{.longname = "altDSID", .argument = &altDSIDArg, .description = "altDSID (for sign-in/out)"},
{.longname = "entropy", .argument = &secretArg, .description = "escrowed entropy in JSON"},
+ {.longname = "appleID", .argument = &appleIDArg, .description = "AppleID"},
+ {.longname = "dsid", .argument = &dsidArg, .description = "DSID"},
+
{.longname = "container", .argument = &containerStr, .description = "CloudKit container name"},
{.longname = "radar", .argument = &radarNumber, .description = "Radar number"},
{.command = "sign-in", .flag = &signIn, .flagval = true, .description = "Inform Cuttlefish container of sign in"},
{.command = "sign-out", .flag = &signOut, .flagval = true, .description = "Inform Cuttlefish container of sign out"},
{.command = "status", .flag = &status, .flagval = true, .description = "Report Octagon status"},
+
{.command = "resetoctagon", .flag = &resetoctagon, .flagval = true, .description = "Reset and establish new Octagon trust"},
+ {.command = "resetProtectedData", .flag = &resetProtectedData, .flagval = true, .description = "Reset ProtectedData"},
+
{.command = "allBottles", .flag = &fetchAllBottles, .flagval = true, .description = "Fetch all viable bottles"},
{.command = "recover", .flag = &recover, .flagval = true, .description = "Recover using this bottle"},
{.command = "depart", .flag = &depart, .flagval = true, .description = "Depart from Octagon Trust"},
{.command = "er-store", .flag = &er_store, .flagval = true, .description = "Store any pending Escrow Request prerecords"},
{.command = "health", .flag = &health, .flagval = true, .description = "Check Octagon Health status"},
+ {.command = "ckks-policy", .flag = &ckks_policy_flag, .flagval = true, .description = "Trigger a refetch of the CKKS policy"},
{.command = "taptoradar", .flag = &ttr_flag, .flagval = true, .description = "Trigger a TapToRadar"},
+
#if TARGET_OS_WATCH
{.command = "pairme", .flag = &pairme, .flagval = true, .description = "Perform pairing (watchOS only)"},
#endif /* TARGET_OS_WATCH */
NSString* context = contextNameArg ? [NSString stringWithCString:contextNameArg encoding:NSUTF8StringEncoding] : OTDefaultContext;
NSString* container = containerStr ? [NSString stringWithCString:containerStr encoding:NSUTF8StringEncoding] : nil;
NSString* altDSID = altDSIDArg ? [NSString stringWithCString:altDSIDArg encoding:NSUTF8StringEncoding] : nil;
+ NSString* dsid = dsidArg ? [NSString stringWithCString:dsidArg encoding:NSUTF8StringEncoding] : nil;
+ NSString* appleID = appleIDArg ? [NSString stringWithCString:appleIDArg encoding:NSUTF8StringEncoding] : nil;
+
NSString* skipRateLimitingCheck = skipRateLimitingCheckArg ? [NSString stringWithCString:skipRateLimitingCheckArg encoding:NSUTF8StringEncoding] : @"NO";
OTControlCLI* ctl = [[OTControlCLI alloc] initWithOTControl:rpc];
long ret = [ctl resetOctagon:container context:context altDSID:altDSID];
return (int)ret;
}
+ if(resetProtectedData) {
+ internalOnly();
+ long ret = [ctl resetProtectedData:container context:context altDSID:altDSID appleID:appleID dsid:dsid];
+ return (int)ret;
+ }
if(fetchAllBottles) {
return (int)[ctl fetchAllBottles:altDSID containerName:container context:context control:rpc];
}
}
return (int)[ctl healthCheck:container context:context skipRateLimitingCheck:skip];
}
+ if(ckks_policy_flag) {
+ return (int)[ctl refetchCKKSPolicy:container context:context];
+ }
if (ttr_flag) {
if (radarNumber == NULL) {
radarNumber = "1";
}
return (int)[ctl tapToRadar:@"action" description:@"description" radar:[NSString stringWithUTF8String:radarNumber]];
}
+
if(er_trigger) {
internalOnly();
return (int)[escrowctl trigger];
return result;
}
+static inline bool SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(SOSAccount *account, CFStringRef viewname) {
+ __block bool result = false;
+ [account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
+ result = SOSAccountIsMyPeerInBackupAndCurrentInView(account, viewname);
+ }];
+ return result;
+}
+
+static inline bool SOSAccountIsPeerInBackupAndCurrentInView_wTxn(SOSAccount *account, SOSPeerInfoRef peerInfo, CFStringRef viewname) {
+ __block bool result = false;
+ [account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
+ result = SOSAccountIsPeerInBackupAndCurrentInView(account, peerInfo, viewname);
+ }];
+ return result;
+}
+
+static inline bool SOSAccountRecoveryKeyIsInBackupAndCurrentInView_wTxn(SOSAccount *account, CFStringRef viewname) {
+ __block bool result = false;
+ [account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
+ result = SOSAccountRecoveryKeyIsInBackupAndCurrentInView(account, viewname);
+ }];
+ return result;
+}
+
+static inline SOSBackupSliceKeyBagRef SOSAccountBackupSliceKeyBagForView_wTxn(SOSAccount *account, CFStringRef viewname, CFErrorRef *error) {
+ __block SOSBackupSliceKeyBagRef result = NULL;
+ [account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
+ result = SOSAccountBackupSliceKeyBagForView(account, viewname, error);
+ }];
+ return result;
+}
+
+
+
+
//
// Account comparison
//
a.key_transport = nil;
a.kvs_message_transport = nil;
- SOSAccountEnsureFactoryCirclesTest(a, accountName);
+ [a performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
+ SOSAccountEnsureFactoryCirclesTest(a, accountName);
+ }];
}
SOSAccount* reinflatedAccount = NULL;
NSError* error = nil;
- require(retval, errOut);
+ if(!retval) {
+ error = nil;
+ return retval;
+ }
// Re-inflate to "inflated"
reinflatedAccount = [SOSAccount accountFromData:accountDER
ok(CFEqualSafe((__bridge CFTypeRef)reinflatedAccount, (__bridge CFTypeRef)account), "Compares");
// Repeat through SOSAccountCopyEncodedData() interface - this is the normally called combined interface
- accountDER = [account encodedData:&error];
+ [account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
+ NSError* error = nil;
+ accountDER = [account encodedData:&error];
+ }];
+
error = nil;
- reinflatedAccount = [SOSAccount accountFromData:accountDER factory:test_factory error:&error];
- ok(reinflatedAccount, "inflated2: %@", error);
- ok(CFEqual((__bridge CFTypeRef)account, (__bridge CFTypeRef)reinflatedAccount), "Compares");
+ SOSAccount* reinflatedAccount2 = NULL;
+
+ reinflatedAccount2 = [SOSAccount accountFromData:accountDER factory:test_factory error:&error];
+ ok(reinflatedAccount2, "inflated2: %@", error);
+ ok(CFEqual((__bridge CFTypeRef)account, (__bridge CFTypeRef)reinflatedAccount2), "Compares");
retval = true;
-errOut:
error = nil;
-
return retval;
}
ok(SOSAccountIsMyPeerInBackupAndCurrentInView(alice_account, kTestView1), "Is alice in backup after sync?");
ok(SOSAccountIsMyPeerInBackupAndCurrentInView(bob_account, kTestView1), "IS bob in the backup after sync");
-
- ok(!SOSAccountIsLastBackupPeer(alice_account, &error), "Alice is not last backup peer");
- CFReleaseNull(error);
//
//Bob leaves the circle
is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
ok(SOSAccountIsMyPeerInBackupAndCurrentInView(alice_account, kTestView1), "Bob left the circle, Alice is not in the backup");
-
- ok(SOSAccountIsLastBackupPeer(alice_account, &error), "Alice is last backup peer");
- CFReleaseNull(error);
- ok(!SOSAccountIsLastBackupPeer(bob_account, &error), "Bob is not last backup peer");
- CFReleaseNull(error);
ok(testAccountPersistence(alice_account), "Test Account->DER->Account Equivalence");
ok(!SOSAccountIsMyPeerInBackupAndCurrentInView(bob_account, kTestView1), "Bob isn't in the backup yet");
- ok(!SOSAccountIsLastBackupPeer(alice_account, &error), "Alice is the not the last backup peer - Bob still registers as one");
- CFReleaseNull(error);
-
ok(SOSAccountSetBackupPublicKey_wTxn(bob_account, bob_backup_key, &error), "Set backup public key, bob (%@)", error);
is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 3, "updates");
- ok(!SOSAccountIsLastBackupPeer(alice_account, &error), "Alice is not last backup peer");
- CFReleaseNull(error);
-
//
//removing backup key for bob account
//
return 2;
}, ^{
- NSError *ns_error = nil;
- frozen_alice = (CFDataRef) CFBridgingRetain([alice_account encodedData:&ns_error]);
- ok(frozen_alice, "Copy encoded %@", ns_error);
- ns_error = nil;
+ [alice_account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
+ NSError *ns_error = nil;
+ frozen_alice = (CFDataRef) CFBridgingRetain([alice_account encodedData:&ns_error]);
+ ok(frozen_alice, "Copy encoded %@", ns_error);
+ ns_error = nil;
+ }];
SOSAccountPurgePrivateCredential(alice_account);
#include <unistd.h>
#include "secd_regressions.h"
-#include "SOSTestDataSource.h"
+#include "keychain/SecureObjectSync/Regressions/SOSTestDataSource.h"
#include "SOSRegressionUtilities.h"
#include <Security/SecRecoveryKey.h>
ok([bob_account.trust checkForRings:&error], "Bob_account is good");
CFReleaseNull(error);
- ok(SOSAccountIsMyPeerInBackupAndCurrentInView(alice_account, kTestView1), "Is alice is in backup before sync?");
+ ok(SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(alice_account, kTestView1), "Is alice is in backup before sync?");
if(!recKeyFirst) {
- SOSBackupSliceKeyBagRef bskb = SOSAccountBackupSliceKeyBagForView(alice_account, kTestView1, &error);
+ SOSBackupSliceKeyBagRef bskb = SOSAccountBackupSliceKeyBagForView_wTxn(alice_account, kTestView1, &error);
CFReleaseNull(error);
ok(!SOSBSKBHasRecoveryKey(bskb), "BSKB should not have recovery key");
CFReleaseNull(bskb);
ok([alice_account.trust checkForRings:&error], "Alice_account is good");
CFReleaseNull(error);
- ok(SOSAccountIsMyPeerInBackupAndCurrentInView(bob_account, kTestView1), "Is bob in the backup after sync? - 1");
+ ok(SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(bob_account, kTestView1), "Is bob in the backup after sync? - 1");
ok([bob_account.trust checkForRings:&error], "Alice_account is good");
CFReleaseNull(error);
ok([alice_account.trust checkForRings:&error], "Alice_account is good");
CFReleaseNull(error);
- ok(SOSAccountIsMyPeerInBackupAndCurrentInView(alice_account, kTestView1), "Is alice is in backup after sync?");
+ ok(SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(alice_account, kTestView1), "Is alice is in backup after sync?");
- ok(SOSAccountIsMyPeerInBackupAndCurrentInView(bob_account, kTestView1), "IS bob in the backup after sync");
-
- ok(!SOSAccountIsLastBackupPeer(alice_account, &error), "Alice is not last backup peer");
- CFReleaseNull(error);
+ ok(SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(bob_account, kTestView1), "IS bob in the backup after sync");
//
//Bob leaves the circle
//Alice should kick Bob out of the backup!
is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
- ok(SOSAccountIsMyPeerInBackupAndCurrentInView(alice_account, kTestView1), "Bob left the circle, Alice is in the backup");
-
- ok(SOSAccountIsLastBackupPeer(alice_account, &error), "Alice is last backup peer");
- CFReleaseNull(error);
- ok(!SOSAccountIsLastBackupPeer(bob_account, &error), "Bob is not last backup peer");
- CFReleaseNull(error);
-
+ ok(SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(alice_account, kTestView1), "Bob left the circle, Alice is in the backup");
+
//ok(testAccountPersistence(alice_account), "Test Account->DER->Account Equivalence");
SOSAccountTrustClassic* bobTrust = bob_account.trust;
- ok(!SOSAccountIsPeerInBackupAndCurrentInView(alice_account, bobTrust.peerInfo, kTestView1), "Bob is still in the backup!");
+ ok(!SOSAccountIsPeerInBackupAndCurrentInView_wTxn(alice_account, bobTrust.peerInfo, kTestView1), "Bob is still in the backup!");
//Bob gets back into the circle
ok(SOSTestJoinWithApproval(cfpassword, cfaccount, changes, alice_account, bob_account, KEEP_USERKEY, 2, false), "Bob Re-Joins");
is([bob_account.trust updateView:bob_account name:kTestView1 code:kSOSCCViewEnable err:&error], kSOSCCViewMember, "Enable view (%@)", error);
CFReleaseNull(error);
- ok(!SOSAccountIsMyPeerInBackupAndCurrentInView(bob_account, kTestView1), "Bob isn't in the backup yet");
-
- ok(!SOSAccountIsLastBackupPeer(alice_account, &error), "Alice is the not the last backup peer - Bob still registers as one");
- CFReleaseNull(error);
-
+ ok(!SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(bob_account, kTestView1), "Bob isn't in the backup yet");
+
ok(SOSAccountSetBackupPublicKey_wTxn(bob_account, bob_backup_key, &error), "Set backup public key, alice (%@)", error);
is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 3, "updates");
- ok(!SOSAccountIsLastBackupPeer(alice_account, &error), "Alice is not last backup peer");
- CFReleaseNull(error);
-
//
//removing backup key for bob account
//
int nchanges = (recKeyFirst) ? 2: 2;
is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), nchanges, "updates");
- ok(!SOSAccountIsMyPeerInBackupAndCurrentInView(bob_account, kTestView1), "Bob's backup key is in the backup - should not be so!");
- ok(!SOSAccountIsPeerInBackupAndCurrentInView(alice_account, bobTrust.peerInfo, kTestView1), "Bob is up to date in the backup!");
+ ok(!SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(bob_account, kTestView1), "Bob's backup key is in the backup - should not be so!");
+ ok(!SOSAccountIsPeerInBackupAndCurrentInView_wTxn(alice_account, bobTrust.peerInfo, kTestView1), "Bob is up to date in the backup!");
//
// Setting new backup public key for Bob
ok(SOSAccountNewBKSBForView(bob_account, kTestView1, &error), "Setting new backup public key for bob account failed: (%@)", error);
//bob is in his own backup
- ok(SOSAccountIsMyPeerInBackupAndCurrentInView(bob_account, kTestView1), "Bob's backup key is not in the backup");
+ ok(SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(bob_account, kTestView1), "Bob's backup key is not in the backup");
//alice does not have bob in her backup
- ok(!SOSAccountIsPeerInBackupAndCurrentInView(alice_account, bobTrust.peerInfo, kTestView1), "Bob is up to date in the backup - should not be so!");
+ ok(!SOSAccountIsPeerInBackupAndCurrentInView_wTxn(alice_account, bobTrust.peerInfo, kTestView1), "Bob is up to date in the backup - should not be so!");
is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 5, "updates");
- ok(SOSAccountIsMyPeerInBackupAndCurrentInView(bob_account, kTestView1), "Bob's backup key should be in the backup");
- ok(SOSAccountIsMyPeerInBackupAndCurrentInView(alice_account, kTestView1), "Alice is in the backup");
+ ok(SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(bob_account, kTestView1), "Bob's backup key should be in the backup");
+ ok(SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(alice_account, kTestView1), "Alice is in the backup");
if(!recKeyFirst) registerRecoveryKeyNow(changes, alice_account, bob_account, pubKeyBytes, recKeyFirst);
- ok(SOSAccountRecoveryKeyIsInBackupAndCurrentInView(alice_account, kTestView1), "Recovery Key is also in the backup");
- ok(SOSAccountRecoveryKeyIsInBackupAndCurrentInView(bob_account, kTestView1), "Recovery Key is also in the backup");
+ ok(SOSAccountRecoveryKeyIsInBackupAndCurrentInView_wTxn(alice_account, kTestView1), "Recovery Key is also in the backup");
+ ok(SOSAccountRecoveryKeyIsInBackupAndCurrentInView_wTxn(bob_account, kTestView1), "Recovery Key is also in the backup");
- SOSBackupSliceKeyBagRef bskb = SOSAccountBackupSliceKeyBagForView(alice_account, kTestView1, &error);
+ SOSBackupSliceKeyBagRef bskb = SOSAccountBackupSliceKeyBagForView_wTxn(alice_account, kTestView1, &error);
CFReleaseNull(error);
ok(SOSBSKBHasRecoveryKey(bskb), "BSKB should have recovery key");
registerRecoveryKeyNow(changes, alice_account, bob_account, NULL, recKeyFirst);
- ok(!SOSAccountRecoveryKeyIsInBackupAndCurrentInView(alice_account, kTestView1), "Recovery Key is not in the backup");
- ok(!SOSAccountRecoveryKeyIsInBackupAndCurrentInView(bob_account, kTestView1), "Recovery Key is not in the backup");
+ ok(!SOSAccountRecoveryKeyIsInBackupAndCurrentInView_wTxn(alice_account, kTestView1), "Recovery Key is not in the backup");
+ ok(!SOSAccountRecoveryKeyIsInBackupAndCurrentInView_wTxn(bob_account, kTestView1), "Recovery Key is not in the backup");
- bskb = SOSAccountBackupSliceKeyBagForView(alice_account, kTestView1, &error);
+ bskb = SOSAccountBackupSliceKeyBagForView_wTxn(alice_account, kTestView1, &error);
CFReleaseNull(error);
ok(!SOSBSKBHasRecoveryKey(bskb), "BSKB should not have recovery key");
+++ /dev/null
-//
-// secd-76-idstransport.c
-// sec
-//
-//
-
-/*
- * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-#include <stdio.h>
-#include <Security/SecBase.h>
-#include <Security/SecItem.h>
-
-#include <Security/SecureObjectSync/SOSAccount.h>
-#include <Security/SecureObjectSync/SOSCloudCircle.h>
-#include <Security/SecureObjectSync/SOSInternal.h>
-#include <Security/SecureObjectSync/SOSFullPeerInfo.h>
-#include <Security/SecureObjectSync/SOSUserKeygen.h>
-#include <Security/SecureObjectSync/SOSAccountTrustClassic+Circle.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "secd_regressions.h"
-#include "SOSTestDataSource.h"
-
-#include "SOSRegressionUtilities.h"
-#include <utilities/SecCFWrappers.h>
-
-#include <securityd/SOSCloudCircleServer.h>
-#include "SecdTestKeychainUtilities.h"
-#import "SOSAccountTesting.h"
-#import "SOSTransportTestTransports.h"
-#include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
-#include <SOSCircle/CKBridge/SOSCloudKeychainConstants.h>
-#include "SOSTestDevice.h"
-
-
-
-static int kTestTestCount = 73;
-
-static void tests()
-{
- CFErrorRef error = NULL;
-
- CFMutableDictionaryRef changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
- CFStringRef cfaccount = CFSTR("test@test.org");
-
- SOSAccount* alice_account = CreateAccountForLocalChanges(CFSTR("Alice"), CFSTR("ak"));
- SOSAccount* bob_account = CreateAccountForLocalChanges(CFSTR("Bob"), CFSTR("ak"));
- SOSAccountTrustClassic *aliceTrust = alice_account.trust;
- SOSAccountTrustClassic *bobTrust = bob_account.trust;
-
- ok(SOSAccountAssertUserCredentialsAndUpdate(bob_account, cfaccount, cfpassword, &error), "Credential setting (%@)", error);
-
- // Bob wins writing at this point, feed the changes back to alice.
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 1, "updates");
-
- ok(SOSAccountAssertUserCredentialsAndUpdate(alice_account, cfaccount, cfpassword, &error), "Credential setting (%@)", error);
- CFReleaseNull(cfpassword);
- CFReleaseNull(error);
-
- ok(NULL != alice_account, "Alice Created");
- ok(NULL != bob_account, "Bob Created");
-
- ok(SOSAccountResetToOffering_wTxn(alice_account, &error), "Reset to offering (%@)", error);
- CFReleaseNull(error);
-
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-
- ok(SOSAccountJoinCircles_wTxn(bob_account, &error), "Bob Applies (%@)", error);
- CFReleaseNull(error);
-
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-
- {
- CFArrayRef applicants = SOSAccountCopyApplicants(alice_account, &error);
-
- ok(applicants && CFArrayGetCount(applicants) == 1, "See one applicant %@ (%@)", applicants, error);
- ok(SOSAccountAcceptApplicants(alice_account, applicants, &error), "Alice accepts (%@)", error);
- CFReleaseNull(error);
- CFReleaseNull(applicants);
- }
-
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 3, "updates");
-
- accounts_agree("bob&alice pair", bob_account, alice_account);
-
- CFArrayRef peers = SOSAccountCopyPeers(alice_account, &error);
- ok(peers && CFArrayGetCount(peers) == 2, "See two peers %@ (%@)", peers, error);
- CFReleaseNull(peers);
-
- //creating test devices
- CFIndex version = 0;
-
- // Optionally prefix each peer with name to make them more unique.
- CFArrayRef deviceIDs = CFArrayCreateForCFTypes(kCFAllocatorDefault,alice_account.peerID, bob_account.peerID, NULL);
- CFSetRef views = SOSViewsCopyTestV2Default();
- CFMutableArrayRef peerMetas = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
- CFStringRef deviceID;
- CFArrayForEachC(deviceIDs, deviceID) {
- SOSPeerMetaRef peerMeta = SOSPeerMetaCreateWithComponents(deviceID, views, NULL);
- CFArrayAppendValue(peerMetas, peerMeta);
- CFReleaseNull(peerMeta);
- }
-
- CFReleaseNull(views);
- CFArrayForEachC(deviceIDs, deviceID) {
- SOSTestDeviceRef device = SOSTestDeviceCreateWithDbNamed(kCFAllocatorDefault, deviceID, deviceID);
- SOSTestDeviceSetPeerIDs(device, peerMetas, version, NULL);
-
- if(CFEqualSafe(deviceID, (__bridge CFTypeRef)(alice_account.peerID))){
- alice_account.factory = device->dsf;
- SOSTestDeviceAddGenericItem(device, CFSTR("Alice"), CFSTR("Alice-add"));
- }
- else{
- bob_account.factory = device->dsf;
- SOSTestDeviceAddGenericItem(device, CFSTR("Bob"), CFSTR("Bob-add"));
- }
- CFReleaseNull(device);
- }
- CFReleaseNull(deviceIDs);
- CFReleaseNull(peerMetas);
-
- SOSUnregisterAllTransportMessages();
- CFArrayRemoveAllValues(message_transports);
-
- alice_account.ids_message_transport = (SOSMessageIDS*)[[SOSMessageIDSTest alloc] initWithAccount:alice_account andAccountName:CFSTR("Alice") andCircleName:SOSCircleGetName(aliceTrust.trustedCircle) err:&error];
-
-
- bob_account.ids_message_transport = (SOSMessageIDS*)[[SOSMessageIDSTest alloc] initWithAccount:bob_account andAccountName:CFSTR("Bob") andCircleName:SOSCircleGetName(bobTrust.trustedCircle) err:&error];
- ok(alice_account.ids_message_transport != NULL, "Alice Account, Created IDS Test Transport");
- ok(bob_account.ids_message_transport != NULL, "Bob Account, Created IDS Test Transport");
-
- bool result = [alice_account.trust modifyCircle:alice_account.circle_transport err:&error action:^(SOSCircleRef circle) {
- CFErrorRef localError = NULL;
-
- SOSFullPeerInfoUpdateTransportType(aliceTrust.fullPeerInfo, SOSTransportMessageTypeIDSV2, &localError);
- SOSFullPeerInfoUpdateTransportPreference(aliceTrust.fullPeerInfo, kCFBooleanFalse, &localError);
- SOSFullPeerInfoUpdateTransportFragmentationPreference(aliceTrust.fullPeerInfo, kCFBooleanTrue, &localError);
- SOSFullPeerInfoUpdateTransportAckModelPreference(aliceTrust.fullPeerInfo, kCFBooleanTrue, &localError);
-
- return SOSCircleHasPeer(circle, aliceTrust.peerInfo, NULL);
- }];
-
- ok(result, "Alice account update circle with transport type");
-
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-
- result &= [bob_account.trust modifyCircle:bob_account.circle_transport err:&error action:^(SOSCircleRef circle) {
- CFErrorRef localError = NULL;
-
- SOSFullPeerInfoUpdateTransportType(bobTrust.fullPeerInfo, SOSTransportMessageTypeIDSV2, &localError);
- SOSFullPeerInfoUpdateTransportPreference(bobTrust.fullPeerInfo, kCFBooleanFalse, &localError);
- SOSFullPeerInfoUpdateTransportFragmentationPreference(bobTrust.fullPeerInfo, kCFBooleanTrue, &localError);
- SOSFullPeerInfoUpdateTransportAckModelPreference(bobTrust.fullPeerInfo, kCFBooleanTrue, &localError);
-
- return SOSCircleHasPeer(circle, bobTrust.peerInfo, NULL);
- }];
-
- ok(result, "Bob account update circle with transport type");
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-
- CFStringRef alice_transportType =SOSPeerInfoCopyTransportType(alice_account.peerInfo);
- CFStringRef bob_accountTransportType = SOSPeerInfoCopyTransportType(bob_account.peerInfo);
- ok(CFEqualSafe(alice_transportType, CFSTR("IDS2.0")), "Alice transport type not IDS");
- ok(CFEqualSafe(bob_accountTransportType, CFSTR("IDS2.0")), "Bob transport type not IDS");
-
- CFReleaseNull(alice_transportType);
- CFReleaseNull(bob_accountTransportType);
-
- SOSTransportMessageIDSTestSetName((SOSMessageIDSTest*)alice_account.ids_message_transport, CFSTR("Alice Account"));
- ok(SOSTransportMessageIDSTestGetName((SOSMessageIDSTest*)alice_account.ids_message_transport) != NULL, "retrieved getting account name");
- ok(SOSAccountRetrieveDeviceIDFromKeychainSyncingOverIDSProxy(alice_account, &error) != false, "device ID from KeychainSyncingOverIDSProxy");
-
- SOSTransportMessageIDSTestSetName((SOSMessageIDSTest*)bob_account.ids_message_transport, CFSTR("Bob Account"));
- ok(SOSTransportMessageIDSTestGetName((SOSMessageIDSTest*)bob_account.ids_message_transport) != NULL, "retrieved getting account name");
- ok(SOSAccountRetrieveDeviceIDFromKeychainSyncingOverIDSProxy(bob_account, &error) != false, "device ID from KeychainSyncingOverIDSProxy");
-
- ok(SOSAccountSetMyDSID_wTxn(alice_account, CFSTR("Alice"),&error), "Setting IDS device ID");
- CFStringRef alice_dsid = SOSAccountCopyDeviceID(alice_account, &error);
- ok(CFEqualSafe(alice_dsid, CFSTR("Alice")), "Getting IDS device ID");
-
- ok(SOSAccountSetMyDSID_wTxn(bob_account, CFSTR("Bob"),&error), "Setting IDS device ID");
- CFStringRef bob_dsid = SOSAccountCopyDeviceID(bob_account, &error);
- ok(CFEqualSafe(bob_dsid, CFSTR("Bob")), "Getting IDS device ID");
-
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 3, "updates");
-
- SOSTransportMessageIDSTestSetName((SOSMessageIDSTest*)alice_account.ids_message_transport, CFSTR("Alice Account"));
- ok(SOSTransportMessageIDSTestGetName((SOSMessageIDSTest*)alice_account.ids_message_transport) != NULL, "retrieved getting account name");
- ok(SOSAccountRetrieveDeviceIDFromKeychainSyncingOverIDSProxy(alice_account, &error) != false, "device ID from KeychainSyncingOverIDSProxy");
-
- ok(SOSAccountSetMyDSID_wTxn(alice_account, CFSTR("DSID"),&error), "Setting IDS device ID");
- CFStringRef dsid = SOSAccountCopyDeviceID(alice_account, &error);
- ok(CFEqualSafe(dsid, CFSTR("DSID")), "Getting IDS device ID");
- CFReleaseNull(dsid);
-
- ok(SOSAccountStartPingTest(alice_account, CFSTR("hai there!"), &error), "Ping test");
- ok(CFDictionaryGetCount(SOSTransportMessageIDSTestGetChanges((SOSMessageIDSTest*)alice_account.ids_message_transport)) != 0, "ping message made it to transport");
- SOSTransportMessageIDSTestClearChanges((SOSMessageIDSTest*)alice_account.ids_message_transport);
-
- ok(SOSAccountSendIDSTestMessage(alice_account, CFSTR("hai again!"), &error), "Send Test Message");
- ok(CFDictionaryGetCount(SOSTransportMessageIDSTestGetChanges((SOSMessageIDSTest*)alice_account.ids_message_transport)) != 0, "ping message made it to transport");
-
- CFStringRef dataKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeyIDSDataMessage, kCFStringEncodingASCII);
- CFStringRef deviceIDKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeyDeviceID, kCFStringEncodingASCII);
- CFStringRef sendersPeerIDKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeySendersPeerID, kCFStringEncodingASCII);
-
- //test IDS message handling
- CFMutableDictionaryRef messageDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-
- ok([alice_account.ids_message_transport SOSTransportMessageIDSHandleMessage:alice_account m:messageDict err:&error]
- == kHandleIDSMessageDontHandle, "sending empty message dictionary");
-
- CFDictionaryAddValue(messageDict, deviceIDKey, CFSTR("Alice Account"));
- ok([alice_account.ids_message_transport SOSTransportMessageIDSHandleMessage:alice_account m:messageDict err:&error] == kHandleIDSMessageDontHandle, "sending device ID only");
-
- CFReleaseNull(messageDict);
- messageDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- CFDictionaryAddValue(messageDict, sendersPeerIDKey, CFSTR("Alice Account"));
- ok([alice_account.ids_message_transport SOSTransportMessageIDSHandleMessage:alice_account m:messageDict err:&error] == kHandleIDSMessageDontHandle, "sending peer ID only");
-
- CFReleaseNull(messageDict);
- messageDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- CFDataRef data = CFDataCreate(kCFAllocatorDefault, 0, 0);
- CFDictionaryAddValue(messageDict, dataKey, data);
- ok( [alice_account.ids_message_transport SOSTransportMessageIDSHandleMessage:alice_account m:messageDict err:&error] == kHandleIDSMessageDontHandle, "sending data only");
-
- CFReleaseNull(messageDict);
- CFReleaseNull(data);
- messageDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- data = CFDataCreate(kCFAllocatorDefault, 0, 0);
- CFDictionaryAddValue(messageDict, dataKey, data);
- CFDictionaryAddValue(messageDict, sendersPeerIDKey, CFSTR("Alice Account"));
- ok([(SOSMessageIDS*)alice_account.ids_message_transport SOSTransportMessageIDSHandleMessage:alice_account m:messageDict err:&error]== kHandleIDSMessageDontHandle, "sending data and peerid only");
-
- CFReleaseNull(messageDict);
- CFReleaseNull(data);
- messageDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- data = CFDataCreate(kCFAllocatorDefault, 0, 0);
- CFDictionaryAddValue(messageDict, dataKey, data);
- CFDictionaryAddValue(messageDict, deviceIDKey, CFSTR("Alice Account"));
- ok([(SOSMessageIDS*)alice_account.ids_message_transport SOSTransportMessageIDSHandleMessage:alice_account m:messageDict err:&error] == kHandleIDSMessageDontHandle, "sending data and deviceid only");
-
- CFReleaseNull(messageDict);
- CFReleaseNull(data);
- messageDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- CFDictionaryAddValue(messageDict, deviceIDKey, CFSTR("Alice Account"));
- CFDictionaryAddValue(messageDict, sendersPeerIDKey, CFSTR("Alice Account"));
- ok([(SOSMessageIDS*)alice_account.ids_message_transport SOSTransportMessageIDSHandleMessage:alice_account m:messageDict err:&error] == kHandleIDSMessageDontHandle, "sending peerid and deviceid only");
-
- CFReleaseNull(messageDict);
- CFReleaseNull(data);
- messageDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- data = CFDataCreate(kCFAllocatorDefault, 0, 0);
- CFDictionaryAddValue(messageDict, dataKey, data);
- CFDictionaryAddValue(messageDict, deviceIDKey, CFSTR("Alice Account"));
- CFDictionaryAddValue(messageDict, sendersPeerIDKey, SOSPeerInfoGetPeerID(bob_account.peerInfo));
- ok([(SOSMessageIDS*)alice_account.ids_message_transport SOSTransportMessageIDSHandleMessage:alice_account m:messageDict err:&error]== kHandleIDSMessageDontHandle, "sending peerid and deviceid and data");
-
- CFReleaseNull(messageDict);
- CFReleaseNull(data);
-
- messageDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- data = CFDataCreate(kCFAllocatorDefault, 0, 0);
- CFDictionaryAddValue(messageDict, dataKey, data);
- CFStringRef BobDeviceID = SOSPeerInfoCopyDeviceID(bob_account.peerInfo);
- CFDictionaryAddValue(messageDict, deviceIDKey, BobDeviceID);
- CFReleaseNull(BobDeviceID);
- CFDictionaryAddValue(messageDict, sendersPeerIDKey, CFSTR("Alice Account"));
- ok([(SOSMessageIDS*)alice_account.ids_message_transport SOSTransportMessageIDSHandleMessage:alice_account m:messageDict err:&error]== kHandleIDSMessageDontHandle, "sending peerid and deviceid and data");
-
- CFReleaseNull(data);
- CFReleaseNull(dataKey);
- CFReleaseNull(deviceIDKey);
- CFReleaseNull(sendersPeerIDKey);
-
- CFReleaseNull(alice_dsid);
- CFReleaseNull(bob_dsid);
- CFReleaseNull(changes);
-
- SOSTestCleanup();
-}
-int secd_76_idstransport(int argc, char *const *argv)
-{
- plan_tests(kTestTestCount);
-
- secd_test_setup_temp_keychain(__FUNCTION__, NULL);
-
- tests();
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (c) 2014 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-#include <Security/SecBase.h>
-#include <Security/SecItem.h>
-
-#include <CoreFoundation/CFDictionary.h>
-
-#include "keychain/SecureObjectSync/SOSAccount.h"
-#include <Security/SecureObjectSync/SOSCloudCircle.h>
-#include "keychain/SecureObjectSync/SOSInternal.h"
-#include "keychain/SecureObjectSync/SOSUserKeygen.h"
-#include "keychain/SecureObjectSync/SOSTransport.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "secd_regressions.h"
-#include "SOSTestDataSource.h"
-
-#include "SOSRegressionUtilities.h"
-#include <utilities/SecCFWrappers.h>
-#include <Security/SecKeyPriv.h>
-
-#include "keychain/securityd/SOSCloudCircleServer.h"
-
-#include "SOSAccountTesting.h"
-
-#include "SecdTestKeychainUtilities.h"
-
-static void tests(void)
-{
- CFErrorRef error = NULL;
-
- CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
- CFStringRef cfaccount = CFSTR("test@test.org");
-
- CFMutableDictionaryRef changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-
- SOSAccount* alice_account = CreateAccountForLocalChanges(CFSTR("Alice"), CFSTR("TestSource"));
- SOSAccount* bob_account = CreateAccountForLocalChanges(CFSTR("Bob"), CFSTR("TestSource"));
-
- ok(SOSAccountAssertUserCredentialsAndUpdate(bob_account, cfaccount, cfpassword, &error), "Credential setting (%@)", error);
-
- // Bob wins writing at this point, feed the changes back to alice.
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 1, "updates");
-
- ok(SOSAccountAssertUserCredentialsAndUpdate(alice_account, cfaccount, cfpassword, &error), "Credential setting (%@)", error);
- CFReleaseNull(error);
-
- CFReleaseNull(cfpassword);
- CFReleaseNull(error);
-
- ok(SOSAccountResetToOffering_wTxn(alice_account, &error), "Reset to offering (%@)", error);
- CFReleaseNull(error);
-
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-
- ok(SOSAccountJoinCircles_wTxn(bob_account, &error), "Bob Applies (%@)", error);
- CFReleaseNull(error);
-
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
- {
- CFArrayRef applicants = SOSAccountCopyApplicants(alice_account, &error);
-
- ok(applicants && CFArrayGetCount(applicants) == 1, "See one applicants %@ (%@)", applicants, error);
- CFReleaseNull(error);
- CFReleaseSafe(applicants);
- }
-
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 1, "updates");
- CFMutableStringRef timeDescription = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("["));
- CFAbsoluteTime currentTimeAndDate = CFAbsoluteTimeGetCurrent();
-
- withStringOfAbsoluteTime(currentTimeAndDate, ^(CFStringRef decription) {
- CFStringAppend(timeDescription, decription);
- });
- CFStringAppend(timeDescription, CFSTR("]"));
-
- int tries = 5;
-
- CFNumberRef attempts = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &tries);
-
- CFMutableArrayRef escrowTimeAndTries = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
- CFArrayAppendValue(escrowTimeAndTries, timeDescription);
- CFArrayAppendValue(escrowTimeAndTries, attempts);
- CFDictionaryRef escrowRecord = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, CFSTR("account label"), escrowTimeAndTries, NULL);
-
- CFMutableDictionaryRef record = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- CFDictionaryAddValue(record, CFSTR("12345"), escrowRecord);
-
- SOSFullPeerInfoRef alice_fpi = alice_account.fullPeerInfo;
- ok(SOSFullPeerInfoAddEscrowRecord(alice_fpi, CFSTR("12345"), escrowRecord, &error), "Adding Escrow records to Alice FPI(%@)", error);
- CFDictionaryRef fpi_escrow = SOSPeerInfoCopyEscrowRecord(SOSFullPeerInfoGetPeerInfo(alice_fpi));
- ok(CFEqualSafe(CFDictionaryGetValue(fpi_escrow, CFSTR("12345")), escrowRecord), "Alice's FPI has escrow (%@)", error);
-
- ok(SOSAccountAddEscrowRecords(bob_account, CFSTR("12345"), escrowRecord, &error), "Adding escrow to Bob's account (%@)", error);
- CFReleaseNull(fpi_escrow);
-
- fpi_escrow = (CFDictionaryRef)SOSAccountGetValue(bob_account, kSOSEscrowRecord, NULL);
- ok(CFEqualSafe(CFDictionaryGetValue(fpi_escrow, CFSTR("12345")), escrowRecord), "Bob has escrow records in account (%@)", error);
- ok(SOSAccountHasPublicKey(alice_account, &error), "Has Public Key" );
-
- ok([alice_account.trust resetAccountToEmpty:alice_account transport:alice_account.circle_transport err:&error], "Reset to offering (%@)", error);
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
- ok(SOSAccountHasPublicKey(bob_account, &error), "Has Public Key" );
-
- ok([bob_account.trust resetAccountToEmpty:bob_account transport:bob_account.circle_transport err:&error], "Reset to offering (%@)", error);
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-
- ok(SOSAccountAddEscrowRecords(bob_account, CFSTR("12345"), escrowRecord, &error), "Adding escrow to Bob's account (%@)", error);
-
- ok(SOSAccountResetToOffering_wTxn(alice_account, &error), "Reset to offering (%@)", error);
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-
- SOSAccountTrustClassic *bobTrust = bob_account.trust;
- CFDictionaryRef bob_fpi_escrow = SOSPeerInfoCopyEscrowRecord(SOSFullPeerInfoGetPeerInfo(bobTrust.fullPeerInfo));
- ok(bob_fpi_escrow == NULL, "Bob's FPI escrow should be null");
- CFReleaseNull(bob_fpi_escrow);
-
- ok(SOSAccountJoinCircles_wTxn(bob_account, &error), "Bob Applies (%@)", error);
- bob_fpi_escrow = SOSPeerInfoCopyEscrowRecord(SOSFullPeerInfoGetPeerInfo(bobTrust.fullPeerInfo));
- ok(bob_fpi_escrow && CFEqualSafe(CFDictionaryGetValue(bob_fpi_escrow, CFSTR("12345")), escrowRecord), "Bob has escrow records in account (%@)", error);
- CFReleaseNull(bob_fpi_escrow);
-
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-
- {
- CFArrayRef applicants = SOSAccountCopyApplicants(alice_account, &error);
- ok(applicants && CFArrayGetCount(applicants) == 1, "See one applicants %@ (%@)", applicants, error);
- ok(SOSAccountAcceptApplicants(alice_account, applicants, &error), "Accept bob into the fold");
- CFReleaseNull(error);
- CFReleaseNull(applicants);
- }
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 3, "updates");
-
- fpi_escrow = (CFDictionaryRef)SOSAccountGetValue(bob_account, kSOSEscrowRecord, NULL);
- ok(isNull(fpi_escrow), "Bob's escrow records in the account object should be gone");
-
- CFReleaseNull(record);
- CFReleaseNull(escrowRecord);
- CFReleaseNull(timeDescription);
- CFReleaseNull(attempts);
- SOSTestCleanup();
-
-}
-
-int secd_95_escrow_persistence(int argc, char *const *argv)
-{
- plan_tests(41);
-
- secd_test_setup_temp_keychain(__FUNCTION__, NULL);
-
- tests();
-
- return 0;
-}
+++ /dev/null
-//
-// secd_77_ids_messaging.c
-// sec
-//
-
-/*
- * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-#include <stdio.h>
-#include <Security/SecBase.h>
-#include <Security/SecItem.h>
-
-#include <Security/SecureObjectSync/SOSAccount.h>
-#include <Security/SecureObjectSync/SOSCloudCircle.h>
-#include <Security/SecureObjectSync/SOSInternal.h>
-#include <Security/SecureObjectSync/SOSFullPeerInfo.h>
-#include <Security/SecureObjectSync/SOSUserKeygen.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "secd_regressions.h"
-#include "SOSTestDataSource.h"
-
-#include "SOSRegressionUtilities.h"
-#include <utilities/SecCFWrappers.h>
-
-#include <securityd/SOSCloudCircleServer.h>
-#include "SecdTestKeychainUtilities.h"
-#include "SOSAccountTesting.h"
-#import "SOSTransportTestTransports.h"
-#include "SOSTestDevice.h"
-#include "SOSTestDataSource.h"
-#include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
-#include <Security/SecureObjectSync/SOSAccountTrustClassic+Circle.h>
-
-static bool SOSAccountIsThisPeerIDMe(SOSAccount* account, CFStringRef peerID) {
- SOSAccountTrustClassic* trust = account.trust;
- SOSPeerInfoRef mypi = trust.peerInfo;
- CFStringRef myPeerID = SOSPeerInfoGetPeerID(mypi);
-
- return myPeerID && CFEqualSafe(myPeerID, peerID);
-}
-
-__unused static void ids_test_sync(SOSAccount* alice_account, SOSAccount* bob_account){
-
- CFMutableDictionaryRef changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- __block bool SyncingCompletedOverIDS = false;
- __block CFErrorRef localError = NULL;
- __block bool done = false;
- SOSAccountTrustClassic* aliceTrust = alice_account.trust;
- SOSAccountTrustClassic* bobTrust = bob_account.trust;
-
- do{
- SOSCircleForEachValidPeer(aliceTrust.trustedCircle, alice_account.accountKey, ^(SOSPeerInfoRef peer) {
- if (!SOSAccountIsThisPeerIDMe(alice_account, SOSPeerInfoGetPeerID(peer))) {
- if(SOSPeerInfoShouldUseIDSTransport(aliceTrust.peerInfo, peer) &&
- SOSPeerInfoShouldUseIDSMessageFragmentation(aliceTrust.peerInfo, peer)){
- secnotice("IDS Transport","Syncing with IDS capable peers using IDS!");
-
- CFMutableSetRef ids = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
- CFSetAddValue(ids, SOSPeerInfoGetPeerID(peer));
-
- SyncingCompletedOverIDS = [alice_account.ids_message_transport SOSTransportMessageSyncWithPeers:alice_account.ids_message_transport p:ids err:&localError];
- CFReleaseNull(ids);
- }
- }
- });
-
- ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL);
-
- SOSCircleForEachValidPeer(bobTrust.trustedCircle, bob_account.accountKey, ^(SOSPeerInfoRef peer) {
- if (!SOSAccountIsThisPeerIDMe(bob_account, SOSPeerInfoGetPeerID(peer))) {
- if(SOSPeerInfoShouldUseIDSTransport(bobTrust.peerInfo, peer) &&
- SOSPeerInfoShouldUseIDSMessageFragmentation(bobTrust.peerInfo, peer)){
- secnotice("IDS Transport","Syncing with IDS capable peers using IDS!");
-
- CFMutableSetRef ids = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
- CFSetAddValue(ids, SOSPeerInfoGetPeerID(peer));
-
- SyncingCompletedOverIDS = [(SOSMessageIDSTest*)bob_account.ids_message_transport SOSTransportMessageSyncWithPeers:(SOSMessageIDSTest*)bob_account.ids_message_transport p:ids err:&localError];
- CFReleaseNull(ids);
- }
- }
- });
-
- ok(SyncingCompletedOverIDS, "synced items over IDS");
- if(CFDictionaryGetCount(SOSTransportMessageIDSTestGetChanges((SOSMessageIDSTest*)alice_account.ids_message_transport)) == 0 && CFDictionaryGetCount(SOSTransportMessageIDSTestGetChanges((SOSMessageIDSTest*)bob_account.ids_message_transport)) == 0){
- done = true;
- break;
- }
-
- ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL);
-
- }while(done == false);
- CFReleaseNull(changes);
-}
-
-static void tests()
-{
- CFErrorRef error = NULL;
-
- CFMutableDictionaryRef changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
- CFStringRef cfaccount = CFSTR("test@test.org");
- CFStringRef dsName = CFSTR("Test");
-
- SOSAccount* alice_account = CreateAccountForLocalChanges(CFSTR("Alice"), dsName);
- SOSAccount* bob_account = CreateAccountForLocalChanges(CFSTR("Bob"), dsName);
-
- ok(SOSAccountAssertUserCredentialsAndUpdate(bob_account, cfaccount, cfpassword, &error), "Credential setting (%@)", error);
-
- // Bob wins writing at this point, feed the changes back to alice.
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 1, "updates");
-
- ok(SOSAccountAssertUserCredentialsAndUpdate(alice_account, cfaccount, cfpassword, &error), "Credential setting (%@)", error);
- CFReleaseNull(cfpassword);
- CFReleaseNull(error);
-
- ok(NULL != alice_account, "Alice Created");
- ok(NULL != bob_account, "Bob Created");
-
- ok(SOSAccountResetToOffering_wTxn(alice_account, &error), "Reset to offering (%@)", error);
- CFReleaseNull(error);
-
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-
- ok(SOSAccountJoinCircles_wTxn(bob_account, &error), "Bob Applies (%@)", error);
- CFReleaseNull(error);
-
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-
- {
- CFArrayRef applicants = SOSAccountCopyApplicants(alice_account, &error);
-
- ok(applicants && CFArrayGetCount(applicants) == 1, "See one applicant %@ (%@)", applicants, error);
- ok(SOSAccountAcceptApplicants(alice_account, applicants, &error), "Alice accepts (%@)", error);
- CFReleaseNull(error);
- CFReleaseNull(applicants);
- }
-
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 3, "updates");
-
- accounts_agree("bob&alice pair", bob_account, alice_account);
-
- CFArrayRef peers = SOSAccountCopyPeers(alice_account, &error);
- ok(peers && CFArrayGetCount(peers) == 2, "See two peers %@ (%@)", peers, error);
- CFReleaseNull(peers);
-
- //creating test devices
- CFIndex version = 0;
-
- // Optionally prefix each peer with name to make them more unique.
- CFArrayRef deviceIDs = CFArrayCreateForCFTypes(kCFAllocatorDefault,alice_account.peerID, bob_account.peerID, NULL);
- CFSetRef views = SOSViewsCopyTestV2Default();
- CFMutableArrayRef peerMetas = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
- CFStringRef deviceID;
- CFArrayForEachC(deviceIDs, deviceID) {
- SOSPeerMetaRef peerMeta = SOSPeerMetaCreateWithComponents(deviceID, views, NULL);
- CFArrayAppendValue(peerMetas, peerMeta);
- CFReleaseNull(peerMeta);
- }
-
- CFReleaseNull(views);
- CFArrayForEachC(deviceIDs, deviceID) {
- SOSTestDeviceRef device = SOSTestDeviceCreateWithDbNamed(kCFAllocatorDefault, deviceID, deviceID);
- SOSTestDeviceSetPeerIDs(device, peerMetas, version, NULL);
-
- if([alice_account.peerID isEqual: (__bridge id)(deviceID)]){
- alice_account.factory = device->dsf;
- SOSTestDeviceAddGenericItem(device, CFSTR("Alice"), CFSTR("Alice-add"));
- }
- else{
- bob_account.factory = device->dsf;
- SOSTestDeviceAddGenericItem(device, CFSTR("Bob"), CFSTR("Bob-add"));
- }
-
- CFReleaseNull(device);
- }
- CFReleaseNull(deviceIDs);
- CFReleaseNull(peerMetas);
-
- SOSUnregisterAllTransportMessages();
- CFArrayRemoveAllValues(message_transports);
-
- SOSAccountTrustClassic* aliceTrust = alice_account.trust;
- SOSAccountTrustClassic* bobTrust = bob_account.trust;
-
- alice_account.ids_message_transport = (SOSMessageIDS*)[[SOSMessageIDSTest alloc] initWithAccount:alice_account andAccountName:CFSTR("Alice") andCircleName:SOSCircleGetName(aliceTrust.trustedCircle) err:&error ];
-
- bob_account.ids_message_transport = (SOSMessageIDS*)[[SOSMessageIDSTest alloc] initWithAccount:bob_account andAccountName:CFSTR("Bob") andCircleName:SOSCircleGetName(bobTrust.trustedCircle) err:&error];
-
- ok(alice_account.ids_message_transport != NULL, "Alice Account, Created IDS Test Transport");
- ok(bob_account.ids_message_transport != NULL, "Bob Account, Created IDS Test Transport");
-
- bool result = [alice_account.trust modifyCircle:alice_account.circle_transport err:&error action:^bool(SOSCircleRef circle) {
- CFErrorRef localError = NULL;
-
- SOSFullPeerInfoUpdateTransportType(aliceTrust.fullPeerInfo, SOSTransportMessageTypeIDSV2, &localError);
- SOSFullPeerInfoUpdateTransportPreference(aliceTrust.fullPeerInfo, kCFBooleanFalse, &localError);
- SOSFullPeerInfoUpdateTransportFragmentationPreference(aliceTrust.fullPeerInfo, kCFBooleanTrue, &localError);
- SOSFullPeerInfoUpdateTransportAckModelPreference(aliceTrust.fullPeerInfo, kCFBooleanTrue, &localError);
-
- return SOSCircleHasPeer(circle, aliceTrust.peerInfo, NULL);
- }];
-
- ok(result, "Alice account update circle with transport type");
-
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-
- result = [bob_account.trust modifyCircle:bob_account.circle_transport err:&error action:^bool(SOSCircleRef circle) {
- CFErrorRef localError = NULL;
-
- SOSFullPeerInfoUpdateTransportType(bobTrust.fullPeerInfo, SOSTransportMessageTypeIDSV2, &localError);
- SOSFullPeerInfoUpdateTransportPreference(bobTrust.fullPeerInfo, kCFBooleanFalse, &localError);
- SOSFullPeerInfoUpdateTransportFragmentationPreference(bobTrust.fullPeerInfo, kCFBooleanTrue, &localError);
- SOSFullPeerInfoUpdateTransportAckModelPreference(bobTrust.fullPeerInfo, kCFBooleanTrue, &localError);
-
- return SOSCircleHasPeer(circle, bobTrust.peerInfo, NULL);
- }];
-
- ok(result, "Bob account update circle with transport type");
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-
- CFStringRef alice_transportType =SOSPeerInfoCopyTransportType(alice_account.peerInfo);
- CFStringRef bob_accountTransportType = SOSPeerInfoCopyTransportType(bob_account.peerInfo);
- ok(CFEqualSafe(alice_transportType, CFSTR("IDS2.0")), "Alice transport type not IDS");
- ok(CFEqualSafe(bob_accountTransportType, CFSTR("IDS2.0")), "Bob transport type not IDS");
-
- CFReleaseNull(alice_transportType);
- CFReleaseNull(bob_accountTransportType);
-
- SOSTransportMessageIDSTestSetName((SOSMessageIDSTest*)alice_account.ids_message_transport, CFSTR("Alice Account"));
- ok(SOSTransportMessageIDSTestGetName((SOSMessageIDSTest*)alice_account.ids_message_transport) != NULL, "retrieved getting account name");
- ok(SOSAccountRetrieveDeviceIDFromKeychainSyncingOverIDSProxy(alice_account, &error) != false, "device ID from KeychainSyncingOverIDSProxy");
-
- SOSTransportMessageIDSTestSetName((SOSMessageIDSTest*)bob_account.ids_message_transport, CFSTR("Bob Account"));
- ok(SOSTransportMessageIDSTestGetName((SOSMessageIDSTest*)bob_account.ids_message_transport) != NULL, "retrieved getting account name");
- ok(SOSAccountRetrieveDeviceIDFromKeychainSyncingOverIDSProxy(bob_account, &error) != false, "device ID from KeychainSyncingOverIDSProxy");
-
-
- ok(SOSAccountSetMyDSID_wTxn(alice_account, CFSTR("Alice"),&error), "Setting IDS device ID");
- CFStringRef alice_dsid = SOSAccountCopyDeviceID(alice_account, &error);
- ok(CFEqualSafe(alice_dsid, CFSTR("Alice")), "Getting IDS device ID");
-
- ok(SOSAccountSetMyDSID_wTxn(bob_account, CFSTR("Bob"),&error), "Setting IDS device ID");
- CFStringRef bob_dsid = SOSAccountCopyDeviceID(bob_account, &error);
- ok(CFEqualSafe(bob_dsid, CFSTR("Bob")), "Getting IDS device ID");
-
- is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 3, "updates");
-
-
- ok(SOSAccountEnsurePeerRegistration(alice_account, NULL), "ensure peer registration - alice");
-
- ok(SOSAccountEnsurePeerRegistration(bob_account, NULL), "ensure peer registration - bob");
-
-
- //ids_test_sync(alice_account, bob_account);
-
- CFReleaseNull(bob_dsid);
- CFReleaseNull(alice_dsid);
- CFReleaseNull(changes);
-
- SOSTestCleanup();
-}
-
-int secd_77_ids_messaging(int argc, char *const *argv)
-{
- plan_tests(100);
-
- secd_test_setup_temp_keychain(__FUNCTION__, NULL);
-
- tests();
-
- return 0;
-}
ONE_TEST(secd_83_item_match_policy)
ONE_TEST(secd_83_item_match_valid_on_date)
ONE_TEST(secd_83_item_match_trusted)
-ONE_TEST(secd_95_escrow_persistence)
ONE_TEST(secd_154_engine_backoff)
ONE_TEST(secd_100_initialsync)
ONE_TEST(secd_130_other_peer_views)
bool SOSCCRemovePeersFromCircleWithAnalytics_Server(CFArrayRef peers, CFDataRef parentEvent, CFErrorRef* error);
bool SOSCCLoggedOutOfAccount_Server(CFErrorRef *error);
bool SOSCCBailFromCircle_Server(uint64_t limit_in_seconds, CFErrorRef* error);
-bool SOSCCRequestEnsureFreshParameters_Server(CFErrorRef* error);
-
-
-bool SOSCCApplyToARing_Server(CFStringRef ringName, CFErrorRef *error);
-bool SOSCCWithdrawlFromARing_Server(CFStringRef ringName, CFErrorRef *error);
-SOSRingStatus SOSCCRingStatus_Server(CFStringRef ringName, CFErrorRef *error);
-CF_RETURNS_RETAINED CFStringRef SOSCCGetAllTheRings_Server(CFErrorRef *error);
-bool SOSCCEnableRing_Server(CFStringRef ringName, CFErrorRef *error);
-
CFArrayRef SOSCCCopyGenerationPeerInfo_Server(CFErrorRef* error);
CFArrayRef SOSCCCopyApplicantPeerInfo_Server(CFErrorRef* error);
CFArrayRef SOSCCCopyPeerPeerInfo_Server(CFErrorRef* error);
CFArrayRef SOSCCCopyConcurringPeerPeerInfo_Server(CFErrorRef* error);
-bool SOSCCkSecXPCOpIsThisDeviceLastBackup_Server(CFErrorRef *error);
-bool SOSCCkSecXPCOpIsThisDeviceLastBackup_Server(CFErrorRef *error);
bool SOSCCAccountSetToNew_Server(CFErrorRef *error);
bool SOSCCResetToOffering_Server(CFErrorRef* error);
bool SOSCCResetToEmpty_Server(CFErrorRef* error);
bool SOSCCViewSetWithAnalytics_Server(CFSetRef enabledViews, CFSetRef disabledViews, CFDataRef parentEvent);
bool SOSCCViewSet_Server(CFSetRef enabledViews, CFSetRef disabledViews);
-CFStringRef SOSCCCopyIncompatibilityInfo_Server(CFErrorRef* error);
enum DepartureReason SOSCCGetLastDepartureReason_Server(CFErrorRef* error);
bool SOSCCSetLastDepartureReason_Server(enum DepartureReason reason, CFErrorRef *error);
bool SOSCCWaitForInitialSync_Server(CFErrorRef*);
bool SOSCCWaitForInitialSyncWithAnalytics_Server(CFDataRef parentEvent, CFErrorRef* error);
-CFArrayRef SOSCCCopyYetToSyncViewsList_Server(CFErrorRef*);
-
-bool SOSWrapToBackupSliceKeyBagForView_Server(CFStringRef viewName, CFDataRef input, CFDataRef* output, CFDataRef* bskbEncoded, CFErrorRef* error);
-
-SOSBackupSliceKeyBagRef SOSBackupSliceKeyBagForView(CFStringRef viewName, CFErrorRef* error);
-CF_RETURNS_RETAINED CFDataRef SOSWrapToBackupSliceKeyBag(SOSBackupSliceKeyBagRef bskb, CFDataRef input, CFErrorRef* error);
//
// MARK: Internal kicks.
void SOSCCSetGestalt_Server(CFStringRef name, CFStringRef version, CFStringRef model, CFStringRef serial);
CFStringRef SOSCCCopyOSVersion(void);
-CFDataRef SOSCCCopyAccountState_Server(CFErrorRef* error);
-CFDataRef SOSCCCopyEngineData_Server(CFErrorRef* error);
-bool SOSCCDeleteEngineState_Server(CFErrorRef* error);
-bool SOSCCDeleteAccountState_Server(CFErrorRef* error);
-
//
// MARK: Testing operations, dangerous to call in normal operation.
CFDataRef SOSItemCopy(CFStringRef label, CFErrorRef* error);
bool SOSItemUpdateOrAdd(CFStringRef label, CFStringRef accessibility, CFDataRef data, CFErrorRef *error);
-bool SOSCCSetEscrowRecord_Server(CFStringRef escrow_label, uint64_t tries, CFErrorRef *error);
-CFDictionaryRef SOSCCCopyEscrowRecord_Server(CFErrorRef *error);
bool SOSCCRegisterRecoveryPublicKey_Server(CFDataRef recovery_key, CFErrorRef *error);
CFDataRef SOSCCCopyRecoveryPublicKey_Server(CFErrorRef *error);
-CFDictionaryRef SOSCCCopyBackupInformation_Server(CFErrorRef *error);
-
SOSPeerInfoRef SOSCCCopyApplication_Server(CFErrorRef *error);
CFDataRef SOSCCCopyCircleJoiningBlob_Server(SOSPeerInfoRef applicant, CFErrorRef *error);
bool SOSCCJoinWithCircleJoiningBlob_Server(CFDataRef joiningBlob, PiggyBackProtocolVersion version, CFErrorRef *error);
bool SOSCCCleanupKVSKeys_Server(CFErrorRef *error);
bool SOSCCAccountHasPublicKey_Server(CFErrorRef *error);
-bool SOSCCAccountIsNew_Server(CFErrorRef *error);
-bool SOSCCTestPopulateKVSWithBadKeys_Server(CFErrorRef *error);
void sync_the_last_data_to_kvs(CFTypeRef account, bool waitForeverForSynchronization);
#include "keychain/SecureObjectSync/SOSInternal.h"
#include "keychain/SecureObjectSync/SOSUserKeygen.h"
#include "keychain/SecureObjectSync/SOSMessage.h"
-#include "keychain/SecureObjectSync/SOSBackupInformation.h"
#include "keychain/SecureObjectSync/SOSDataSource.h"
#include "keychain/SecureObjectSync/SOSKVSKeys.h"
#import "keychain/SecureObjectSync/SOSAccountTrustClassic.h"
secerror("Got NULL creating account");
}
- [account startStateMachine];
+ //[account startStateMachine];
done:
CFReleaseNull(savedAccount);
return hasPublicKey;
}
-bool SOSCCAccountIsNew_Server(CFErrorRef *error)
-{
- __block bool result = true;
- __block CFErrorRef localError = NULL;
-
- (void) do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
- result = SOSAccountIsNew(txn.account, &localError);
- return result;
- });
-
- if(error != NULL && localError != NULL)
- *error = localError;
-
- return result;
-}
bool SOSCCRequestToJoinCircleAfterRestore_Server(CFErrorRef* error)
{
__block bool result = true;
}
-bool SOSCCRequestEnsureFreshParameters_Server(CFErrorRef* error)
-{
- bool returned = false;
- returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
- return SyncKVSAndWait(block_error);
- });
- if (returned) {
- returned = Flush(error);
- }
- return returned;
-}
-
-bool SOSCCApplyToARing_Server(CFStringRef ringName, CFErrorRef *error){
- __block bool result = true;
- bool returned = false;
- returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
- SOSFullPeerInfoRef fpi = txn.account.fullPeerInfo;
- SOSRingRef ring = [txn.account.trust copyRing:ringName err:error];
-
- if(fpi && ring) {
- result = SOSRingApply(ring, txn.account.accountKey, fpi , error);
- }
- CFReleaseNull(ring);
- return result;
- });
- return returned;
-}
-
-bool SOSCCWithdrawlFromARing_Server(CFStringRef ringName, CFErrorRef *error){
- __block bool result = true;
- bool returned = false;
- returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
- SOSFullPeerInfoRef fpi = txn.account.fullPeerInfo;
- SOSRingRef ring = [txn.account.trust copyRing:ringName err:error];
- if(fpi && ring) {
- result = SOSRingWithdraw(ring, txn.account.accountKey, fpi , error);
- }
- CFReleaseNull(ring);
- return result;
- });
- return returned;
-}
-
-bool SOSCCEnableRing_Server(CFStringRef ringName, CFErrorRef *error){
- __block bool result = true;
- bool returned = false;
- returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
- SOSFullPeerInfoRef fpi = txn.account.fullPeerInfo;
- SOSRingRef ring = [txn.account.trust copyRing:ringName err:error];
- if(fpi && ring) {
- result = SOSRingResetToOffering(ring, NULL, fpi, error);
- }
- CFReleaseNull(ring);
- return result;
- });
- return returned;
-}
-
-CFStringRef SOSCCGetAllTheRings_Server(CFErrorRef *error){
- __block CFMutableDictionaryRef result = NULL;
- __block CFMutableStringRef description = CFStringCreateMutable(kCFAllocatorDefault, 0);
-
- (void) do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
- SOSAccountForEachRing(txn.account, ^SOSRingRef(CFStringRef name, SOSRingRef ring) {
- CFStringAppendFormat(description, NULL, CFSTR("%@\n"), ring);
- return NULL;
- });
- if(result)
- return true;
- return false;
- });
-
- return description;
-}
-
-SOSRingStatus SOSCCRingStatus_Server(CFStringRef ringName, CFErrorRef *error){
- __block bool result = true;
- SOSRingStatus returned;
- returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
- SOSFullPeerInfoRef fpi = txn.account.fullPeerInfo;
- SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(fpi);
-
- SOSRingRef ring = [txn.account.trust copyRing:ringName err:error];
- if(myPeer && ring) {
- result = SOSRingDeviceIsInRing(ring, SOSPeerInfoGetPeerID(myPeer));
- }
- CFReleaseNull(ring);
-
- return result;
- });
- return returned;
-}
-
bool SOSCCAccountSetToNew_Server(CFErrorRef *error)
{
return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
{
return do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
bool waitForeverForSynchronization = false;
-
+
bool result = SOSAccountBail(txn.account, limit_in_seconds, block_error);
[txn restart]; // Make sure this gets finished before we set to new.
return result;
}
-
-static CFArrayRef SOSAccountCopyYetToSyncViews(SOSAccount* account, CFErrorRef *error) {
- __block CFArrayRef result = NULL;
-
- CFTypeRef valueFetched = SOSAccountGetValue(account, kSOSUnsyncedViewsKey, error);
- if (valueFetched == kCFBooleanTrue) {
- SOSPeerInfoRef myPI = account.peerInfo;
- if (myPI) {
- SOSPeerInfoWithEnabledViewSet(myPI, ^(CFSetRef enabled) {
- result = CFSetCopyValues(enabled);
- });
- }
- } else if (isSet(valueFetched)) {
- result = CFSetCopyValues((CFSetRef)valueFetched);
- }
-
- if (result == NULL) {
- result = CFArrayCreateForCFTypes(kCFAllocatorDefault, NULL);
- }
-
- return result;
-}
-
-CFArrayRef SOSCCCopyYetToSyncViewsList_Server(CFErrorRef* error) {
-
- __block CFArrayRef views = NULL;
-
- (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
- views = SOSAccountCopyYetToSyncViews(txn.account, error);
-
- return true;
- });
-
- return views;
-}
-
-bool SOSWrapToBackupSliceKeyBagForView_Server(CFStringRef viewName, CFDataRef input, CFDataRef* output, CFDataRef* bskbEncoded, CFErrorRef* error) {
- CFErrorRef localerror = NULL;
- SOSBackupSliceKeyBagRef bskb = SOSBackupSliceKeyBagForView(viewName, &localerror);
-
- if(bskbEncoded && bskb) {
- *bskbEncoded = SOSBSKBCopyEncoded(bskb, &localerror);
- }
-
- if(output) {
- *output = SOSWrapToBackupSliceKeyBag(bskb, input, &localerror);
- }
-
- if(error) {
- *error = localerror;
- }
- return localerror == NULL;
-}
-
-SOSBackupSliceKeyBagRef SOSBackupSliceKeyBagForView(CFStringRef viewName, CFErrorRef* error){
- __block SOSBackupSliceKeyBagRef bskb = NULL;
- (void) do_with_account(^ (SOSAccountTransaction* txn) {
- bskb = SOSAccountBackupSliceKeyBagForView(txn.account, viewName, error);
- });
- return bskb;
-}
-
-CFDataRef SOSWrapToBackupSliceKeyBag(SOSBackupSliceKeyBagRef bskb, CFDataRef input, CFErrorRef* error) {
- CFDataRef encrypted = NULL;
- bskb_keybag_handle_t bskb_handle = 0;
-
- require_quiet(bskb, exit);
-
- bskb_handle = SOSBSKBLoadLocked(bskb, error);
- require_quiet(bskb_handle, exit);
-
- SecAccessControlRef access = NULL;
- require_quiet(access = SecAccessControlCreate(kCFAllocatorDefault, error), exit);
- require_quiet(SecAccessControlSetProtection(access, kSecAttrAccessibleWhenUnlocked, error), exit);
-
- // ks_encrypt_data takes a dictionary as its plaintext.
- CFMutableDictionaryRef plaintext = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- CFDictionarySetValue(plaintext, CFSTR("data"), input);
-
- require_quiet(ks_encrypt_data_legacy(bskb_handle, access, NULL, plaintext, NULL, &encrypted, false, error), exit);
-
-exit:
- CFReleaseNull(bskb);
- if(bskb_handle != 0) {
- ks_close_keybag(bskb_handle, error);
- }
- if(error && *error) {
- secnotice("backup", "Failed to wrap to a BKSB: %@", *error);
- }
- return encrypted;
-
-}
-
-CFDictionaryRef SOSCCCopyEscrowRecord_Server(CFErrorRef *error){
-
- __block CFDictionaryRef result = NULL;
-
- (void) do_with_account_if_after_first_unlock(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
- CFErrorRef localError = NULL;
- SOSCCStatus status = [txn.account getCircleStatus:&localError];
-
- CFStringRef dsid = SOSAccountGetValue(txn.account, kSOSDSIDKey, error);
- CFDictionaryRef escrowRecords = NULL;
- CFDictionaryRef record = NULL;
- switch(status) {
- case kSOSCCInCircle:
- //get the escrow record in the peer info!
- escrowRecords = SOSPeerInfoCopyEscrowRecord(txn.account.peerInfo);
- if(escrowRecords){
- record = CFDictionaryGetValue(escrowRecords, dsid);
- if(record)
- result = CFRetainSafe(record);
- }
- CFReleaseNull(escrowRecords);
- break;
- case kSOSCCRequestPending:
- //set the escrow record in the peer info/application?
- break;
- case kSOSCCNotInCircle:
- case kSOSCCCircleAbsent:
- //set the escrow record in the account expansion!
- escrowRecords = SOSAccountGetValue(txn.account, kSOSEscrowRecord, error);
- if(escrowRecords){
- record = CFDictionaryGetValue(escrowRecords, dsid);
- if(record)
- result = CFRetainSafe(record);
- }
- break;
- default:
- secdebug("account", "no circle status!");
- break;
- }
- return true;
- });
-
- return result;
-}
-
-CFDictionaryRef SOSCCCopyBackupInformation_Server(CFErrorRef *error) {
- __block CFDictionaryRef result = NULL;
-
- (void) do_with_account_if_after_first_unlock(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
- result = SOSBackupInformation(txn, error);
- return true;
- });
- return result;
-}
-
-bool SOSCCSetEscrowRecord_Server(CFStringRef escrow_label, uint64_t tries, CFErrorRef *error){
-
- if (escrow_label == NULL) {
- return false;
- }
-
- __block bool result = true;
- __block CFErrorRef block_error = NULL;
-
- (void) do_with_account_if_after_first_unlock(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
- SOSCCStatus status = [txn.account getCircleStatus:&block_error];
-
- CFStringRef dsid = SOSAccountGetValue(txn.account, kSOSDSIDKey, error);
-
- CFMutableStringRef timeDescription = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("["));
- CFAbsoluteTime currentTimeAndDate = CFAbsoluteTimeGetCurrent();
-
- withStringOfAbsoluteTime(currentTimeAndDate, ^(CFStringRef decription) {
- CFStringAppend(timeDescription, decription);
- });
- CFStringAppend(timeDescription, CFSTR("]"));
-
- CFNumberRef attempts = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, (const void*)&tries);
-
- CFMutableDictionaryRef escrowTimeAndTries = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- CFDictionaryAddValue(escrowTimeAndTries, kSOSBurnedRecoveryAttemptCount, attempts);
- CFDictionaryAddValue(escrowTimeAndTries, kSOSBurnedRecoveryAttemptAttestationDate, timeDescription);
-
- CFMutableDictionaryRef escrowRecord = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
- CFDictionaryAddValue(escrowRecord, escrow_label, escrowTimeAndTries);
-
- switch(status) {
- case kSOSCCInCircle:
- //set the escrow record in the peer info!
- if(!SOSFullPeerInfoAddEscrowRecord(txn.account.fullPeerInfo, dsid, escrowRecord, error)){
- secdebug("accout", "Could not set escrow record in the full peer info");
- result = false;
- }
- break;
- case kSOSCCRequestPending:
- //set the escrow record in the peer info/application?
- break;
- case kSOSCCNotInCircle:
- case kSOSCCCircleAbsent:
- //set the escrow record in the account expansion!
-
- if(!SOSAccountAddEscrowRecords(txn.account, dsid, escrowRecord, error)) {
- secdebug("account", "Could not set escrow record in expansion data");
- result = false;
- }
- break;
- default:
- secdebug("account", "no circle status!");
- break;
- }
- CFReleaseNull(attempts);
- CFReleaseNull(timeDescription);
- CFReleaseNull(escrowTimeAndTries);
- CFReleaseNull(escrowRecord);
-
- return true;
- });
-
- return result;
-}
-
bool SOSCCAcceptApplicants_Server(CFArrayRef applicants, CFErrorRef* error)
{
OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCAcceptApplicants);
return result;
}
-CFDataRef SOSCCCopyAccountState_Server(CFErrorRef* error)
-{
- __block CFDataRef accountState = NULL;
-
- (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
- // Copy account state from the keychain
- accountState = SOSAccountCopyAccountStateFromKeychain(block_error);
- return accountState != NULL;
- });
-
- return accountState;
-}
-
-bool SOSCCDeleteAccountState_Server(CFErrorRef* error)
-{
- __block bool result = NULL;
-
- (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
- // Delete account state from the keychain
- result = SOSAccountDeleteAccountStateFromKeychain(block_error);
- return result;
- });
-
- return result;
-}
-
-CFDataRef SOSCCCopyEngineData_Server(CFErrorRef* error)
-{
- __block CFDataRef engineState = NULL;
-
- (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
- // Copy engine state from the keychain
- engineState = SOSAccountCopyEngineStateFromKeychain(block_error);
- return engineState != NULL;
- });
-
- return engineState;
-}
-
-bool SOSCCDeleteEngineState_Server(CFErrorRef* error)
-{
- __block bool result = NULL;
-
- (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
- // Delete engine state from the keychain
- result = SOSAccountDeleteEngineStateFromKeychain(block_error);
- return result;
- });
-
- return result;
-}
-
-
-
SOSPeerInfoRef SOSCCSetNewPublicBackupKey_Server(CFDataRef newPublicBackup, CFErrorRef *error){
__block SOSPeerInfoRef result = NULL;
OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCSetNewPublicBackupKey);
return registerResult;
}
-CFStringRef SOSCCCopyIncompatibilityInfo_Server(CFErrorRef* error)
-{
- __block CFStringRef result = NULL;
-
- (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
- result = SOSAccountCopyIncompatibilityInfo(txn.account, block_error);
- return result != NULL;
- });
-
- return result;
-}
-
-bool SOSCCkSecXPCOpIsThisDeviceLastBackup_Server(CFErrorRef *error) {
- bool result = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
- return SOSAccountIsLastBackupPeer(txn.account, block_error);
- });
- return result;
-}
-
enum DepartureReason SOSCCGetLastDepartureReason_Server(CFErrorRef* error)
{
__block enum DepartureReason result = kSOSDepartureReasonError;
return result;
}
-bool SOSCCTestPopulateKVSWithBadKeys_Server(CFErrorRef *error)
-{
- __block bool result = false;
- do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
- return SOSAccountPopulateKVSWithBadKeys(txn.account, error);
- });
- return result;
-}
CFDataRef SOSCCCopyCircleJoiningBlob_Server(SOSPeerInfoRef applicant, CFErrorRef *error) {
__block CFDataRef pbblob = NULL;
OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCCopyCircleJoiningBlob);
}
- (BOOL)hasKeyClass
{
- return _has.keyClass;
+ return _has.keyClass != 0;
}
- (BOOL)hasPublicKey
{
}
- (BOOL)hasKeyClass
{
- return _has.keyClass;
+ return _has.keyClass != 0;
}
- (BOOL)hasBackupWrappedMetadataKey
{
}
- (BOOL)hasRecoveryType
{
- return _has.recoveryType;
+ return _has.recoveryType != 0;
}
- (BOOL)hasBagIdentity
{
static CFTypeRef kc_copy_protection_from(const uint8_t *der, const uint8_t *der_end);
static CF_RETURNS_RETAINED CFMutableDictionaryRef s3dl_item_v2_decode(CFDataRef plain, CFErrorRef *error);
static CF_RETURNS_RETAINED CFMutableDictionaryRef s3dl_item_v3_decode(CFDataRef plain, CFErrorRef *error);
-#if USE_KEYSTORE
+#if USE_KEYSTORE && !TARGET_OS_SIMULATOR
static bool kc_attribs_key_encrypted_data_from_blob(keybag_handle_t keybag, const SecDbClass *class, const void *blob_data, size_t blob_data_len, SecAccessControlRef access_control, uint32_t version,
CFMutableDictionaryRef *authenticated_attributes, aks_ref_key_t *ref_key, CFDataRef *encrypted_data, CFErrorRef *error);
static CFDataRef kc_create_auth_data(SecAccessControlRef access_control, CFDictionaryRef auth_attributes);
CFRelease(attributes_dict);
}
} else {
-#if USE_KEYSTORE
+#if USE_KEYSTORE && !TARGET_OS_SIMULATOR
if (attributes) {
plainText = CFPropertyListCreateDERData(kCFAllocatorDefault, attributes, error);
}
if (!keyclass)
goto out;
-#if USE_KEYSTORE
+#if USE_KEYSTORE && !TARGET_OS_SIMULATOR
if (version >= 4) {
auth_data = kc_create_auth_data(access_control, authenticated_attributes);
require_quiet(ok = ks_encrypt_acl(keybag, keyclass, bulkKeySize, bulkKey, bulkKeyWrapped, auth_data, acm_context, access_control, error), out);
}
}
-#if USE_KEYSTORE
+#if USE_KEYSTORE && !TARGET_OS_SIMULATOR
if (hasProtectionData) {
if (caller_access_groups) {
caller_access_groups_data = kc_copy_access_groups_data(caller_access_groups, error);
}
}
-#if USE_KEYSTORE
+// Simulator fakes security, this code is unused on it
+#if USE_KEYSTORE && !TARGET_OS_SIMULATOR
static bool kc_attribs_key_encrypted_data_from_blob(keybag_handle_t keybag, const SecDbClass *class, const void *blob_data, size_t blob_data_len, SecAccessControlRef access_control, uint32_t version,
CFMutableDictionaryRef *authenticated_attributes, aks_ref_key_t *ref_key, CFDataRef *encrypted_data, CFErrorRef *error)
{
return &dsf->factory;
}
+
+static dispatch_once_t sDSFQueueOnce;
+static dispatch_queue_t sDSFQueue;
+static CFMutableDictionaryRef sDSTable = NULL;
+
+void SecItemDataSourceFactoryReleaseAll() {
+ // Ensure that the queue is set up
+ (void) SecItemDataSourceFactoryGetShared(nil);
+
+ dispatch_sync(sDSFQueue, ^{
+ if(sDSTable) {
+ CFDictionaryRemoveAllValues(sDSTable);
+ }
+ });
+}
+
SOSDataSourceFactoryRef SecItemDataSourceFactoryGetShared(SecDbRef db) {
- static dispatch_once_t sDSFQueueOnce;
- static dispatch_queue_t sDSFQueue;
- static CFMutableDictionaryRef sDSTable = NULL;
dispatch_once(&sDSFQueueOnce, ^{
sDSFQueue = dispatch_queue_create("dataSourceFactory queue", DISPATCH_QUEUE_SERIAL);
// Hack to log objects from inside SOS code
void SecItemServerAppendItemDescription(CFMutableStringRef desc, CFDictionaryRef object);
+// Are you a test? Call this to drop all data sources.
+void SecItemDataSourceFactoryReleaseAll(void);
__END_DECLS
SecDbRef SecKeychainDbInitialize(SecDbRef db) {
#if OCTAGON
- if(SecCKKSIsEnabled()) {
- // This needs to be async, otherwise we get hangs between securityd, cloudd, and apsd
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- SecCKKSInitialize(db);
-
- });
- }
-
- if(OctagonIsEnabled() && OctagonShouldPerformInitialization()) {
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ // This needs to be async, otherwise we get hangs between securityd, cloudd, and apsd
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ if(OctagonIsEnabled() && OctagonShouldPerformInitialization()) {
OctagonInitialize();
- });
- }
+ }
+
+ if(SecCKKSIsEnabled()) {
+ SecCKKSInitialize(db);
+ }
+ });
if(EscrowRequestServerIsEnabled()) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
return _kc_dbhandle;
}
+/* whitebox testing only, and I really hope you call DbReset soon */
+void SecKeychainDbForceClose(void)
+{
+ dispatch_sync(get_kc_dbhandle_dispatch(), ^{
+ if(_kc_dbhandle) {
+ SecDbForceClose(_kc_dbhandle);
+ }
+ });
+}
+
/* For whitebox testing only */
void SecKeychainDbReset(dispatch_block_t inbetween)
{
bool kc_with_custom_db(bool writeAndRead, bool usesItemTables, SecDbRef db, CFErrorRef *error, bool (^perform)(SecDbConnectionRef dbt));
+
/* For whitebox testing only */
+void SecKeychainDbForceClose(void);
void SecKeychainDbReset(dispatch_block_t inbetween);
.soscc_RequestToJoinCircle = SOSCCRequestToJoinCircle_Server,
.soscc_RequestToJoinCircleAfterRestore = SOSCCRequestToJoinCircleAfterRestore_Server,
.soscc_RequestToJoinCircleAfterRestoreWithAnalytics = SOSCCRequestToJoinCircleAfterRestoreWithAnalytics_Server,
- .soscc_RequestEnsureFreshParameters = SOSCCRequestEnsureFreshParameters_Server,
- .soscc_GetAllTheRings = SOSCCGetAllTheRings_Server,
- .soscc_ApplyToARing = SOSCCApplyToARing_Server,
- .soscc_WithdrawlFromARing = SOSCCWithdrawlFromARing_Server,
- .soscc_EnableRing = SOSCCEnableRing_Server,
- .soscc_RingStatus = SOSCCRingStatus_Server,
.soscc_SetToNew = SOSCCAccountSetToNew_Server,
.soscc_ResetToOffering = SOSCCResetToOffering_Server,
.soscc_ResetToEmpty = SOSCCResetToEmpty_Server,
.soscc_RegisterSingleRecoverySecret = SOSCCRegisterSingleRecoverySecret_Server,
.soscc_WaitForInitialSync = SOSCCWaitForInitialSync_Server,
.soscc_WaitForInitialSyncWithAnalytics = SOSCCWaitForInitialSyncWithAnalytics_Server,
- .soscc_CopyYetToSyncViewsList = SOSCCCopyYetToSyncViewsList_Server,
- .soscc_SetEscrowRecords = SOSCCSetEscrowRecord_Server,
- .soscc_CopyEscrowRecords = SOSCCCopyEscrowRecord_Server,
- .sosbskb_WrapToBackupSliceKeyBagForView = SOSWrapToBackupSliceKeyBagForView_Server,
- .soscc_CopyAccountState = SOSCCCopyAccountState_Server,
- .soscc_DeleteAccountState = SOSCCDeleteAccountState_Server,
- .soscc_CopyEngineData = SOSCCCopyEngineData_Server,
- .soscc_DeleteEngineState = SOSCCDeleteEngineState_Server,
.soscc_AccountHasPublicKey = SOSCCAccountHasPublicKey_Server,
- .soscc_AccountIsNew = SOSCCAccountIsNew_Server,
- .soscc_IsThisDeviceLastBackup = SOSCCkSecXPCOpIsThisDeviceLastBackup_Server,
.soscc_SOSCCPeersHaveViewsEnabled = SOSCCPeersHaveViewsEnabled_Server,
.soscc_RegisterRecoveryPublicKey = SOSCCRegisterRecoveryPublicKey_Server,
.soscc_CopyRecoveryPublicKey = SOSCCCopyRecoveryPublicKey_Server,
- .soscc_CopyBackupInformation = SOSCCCopyBackupInformation_Server,
.soscc_SOSCCMessageFromPeerIsPending = SOSCCMessageFromPeerIsPending_Server,
.soscc_SOSCCSendToPeerIsPending = SOSCCSendToPeerIsPending_Server,
#endif /* SECUREOBJECTSYNC */
var deviceName: String?
var serialNumber: String?
var osVersion: String?
-var policyVersion: NSNumber?
var policySecrets: [String: Data]?
enum Command {
osVersion = newOsVersion
case "--policy-version":
- guard let newPolicyVersion = UInt64(argIterator.next() ?? "") else {
+ guard let _ = UInt64(argIterator.next() ?? "") else {
print("Error: --policy-version takes an integer argument")
exitUsage(1)
}
- policyVersion = NSNumber(value: newPolicyVersion)
+ // Option ignored for now
case "--policy-secret":
guard let name = argIterator.next(), let dataBase64 = argIterator.next() else {
var machineIDs = Set<String>()
var performIDMS = false
while let arg = argIterator.next() {
- if(arg == "--idms") {
+ if arg == "--idms" {
performIDMS = true
} else {
machineIDs.insert(arg)
}
}
-if commands.count == 0 {
+if commands.isEmpty {
exitUsage(0)
}
voucherSig: voucherSig,
ckksKeys: [],
tlkShares: [],
- preapprovedKeys: preapprovedKeys ?? []) { peerID, _, error in
+ preapprovedKeys: preapprovedKeys ?? []) { peerID, _, _, _, error in
guard error == nil else {
print("Error joining:", error!)
return
deviceName: deviceName ?? deviceInfo.deviceName(),
serialNumber: serialNumber ?? deviceInfo.serialNumber(),
osVersion: osVersion ?? deviceInfo.osVersion(),
- policyVersion: policyVersion,
+ policyVersion: nil,
policySecrets: policySecrets,
signingPrivKeyPersistentRef: nil,
- encPrivKeyPersistentRef: nil) {
- peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+ encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, views, _, error in
guard error == nil else {
print("Error preparing:", error!)
return
"stableInfo": stableInfo!.base64EncodedString(),
"stableInfoSig": stableInfoSig!.base64EncodedString(),
"machineID": machineID!,
- ]
+ "views": Array(views ?? Set()),
+ ] as [String: Any]
do {
print(try TPCTLObjectiveC.jsonSerialize(cleanDictionaryForJSON(result)))
} catch {
deviceName: deviceName,
serialNumber: serialNumber,
osVersion: osVersion,
- policyVersion: policyVersion,
+ policyVersion: nil,
policySecrets: policySecrets) { _, error in
guard error == nil else {
print("Error updating:", error!)
bottleID: bottleID,
entropy: entropy,
bottleSalt: salt,
- tlkShares: []) { voucher, voucherSig, error in
+ tlkShares: []) { voucher, voucherSig, _, _, error in
guard error == nil else {
print("Error during vouchWithBottle", error!)
return
os_log("allow-listing (%@, %@)", log: tplogDebug, type: .default, container, context)
var idmsDeviceIDs: Set<String> = Set()
+ var accountIsDemo: Bool = false
- if(performIDMS) {
+ if performIDMS {
let store = ACAccountStore()
guard let account = store.aa_primaryAppleAccount() else {
print("Unable to fetch primary Apple account!")
requestArguments.altDSID = account.aa_altDSID
requestArguments.services = [AKServiceNameiCloud]
+ let akManager = AKAccountManager.sharedInstance
+ let authKitAccount = akManager.authKitAccount(withAltDSID: account.aa_altDSID)
+ if let account = authKitAccount {
+ accountIsDemo = akManager.demoAccount(for: account)
+ }
+
guard let controller = AKAppleIDAuthenticationController() else {
print("Unable to create AKAppleIDAuthenticationController!")
abort()
}
semaphore.wait()
}
-
let allMachineIDs = machineIDs.union(idmsDeviceIDs)
print("Setting allowed machineIDs to \(allMachineIDs)")
- tpHelper.setAllowedMachineIDsWithContainer(container, context: context, allowedMachineIDs: allMachineIDs) { listChanged, error in
+ tpHelper.setAllowedMachineIDsWithContainer(container, context: context, allowedMachineIDs: allMachineIDs, honorIDMSListChanges: accountIsDemo) { listChanged, error in
guard error == nil else {
print("Error during allow:", error!)
return
#include <security_asn1/secerr.h>
#include <security_asn1/secport.h>
-#if USE_CDSA_CRYPTO
-#include <Security/cssmapi.h>
-#else
#include <CommonCrypto/CommonDigest.h>
-#endif
#include <Security/SecCmsDigestContext.h>
PLArenaPool * poolp;
Boolean saw_contents;
int digcnt;
-#if USE_CDSA_CRYPTO
- CSSM_CC_HANDLE * digobjs;
-#else
void ** digobjs;
-#endif
SECAlgorithmID ** digestalgs;
};
{
PLArenaPool *poolp;
SecCmsDigestContextRef cmsdigcx;
-#if USE_CDSA_CRYPTO
- CSSM_CC_HANDLE digobj;
-#else
void * digobj;
-#endif
int digcnt;
int i;
poolp = PORT_NewArena(1024);
- if (poolp == NULL)
- goto loser;
+ if (poolp == NULL) {
+ goto loser;
+ }
digcnt = (digestalgs == NULL) ? 0 : SecCmsArrayCount((void **)digestalgs);
cmsdigcx->poolp = poolp;
if (digcnt > 0) {
-#if USE_CDSA_CRYPTO
- /* Security check to prevent under-allocation */
- if (digcnt >= (int)((INT_MAX/(MAX(sizeof(CSSM_CC_HANDLE),sizeof(SECAlgorithmID *))))-1)) {
- goto loser;
- }
- cmsdigcx->digobjs = (CSSM_CC_HANDLE *)PORT_ArenaAlloc(poolp, digcnt * sizeof(CSSM_CC_HANDLE));
- if (cmsdigcx->digobjs == NULL)
- goto loser;
-#else
- /* Security check to prevent under-allocation */
- if (digcnt >= (int)((INT_MAX/(MAX(sizeof(void *),sizeof(SECAlgorithmID *))))-1)) {
- goto loser;
- }
- cmsdigcx->digobjs = (void**)PORT_ArenaAlloc(poolp, digcnt * sizeof(void *));
- if (cmsdigcx->digobjs == NULL)
- goto loser;
-#endif
- cmsdigcx->digestalgs = (SECAlgorithmID **)PORT_ArenaZAlloc(poolp,
- (digcnt + 1) * sizeof(SECAlgorithmID *));
- if (cmsdigcx->digestalgs == NULL)
- goto loser;
+ /* Security check to prevent under-allocation */
+ if (digcnt >= (int)((INT_MAX/(MAX(sizeof(void *),sizeof(SECAlgorithmID *))))-1)) {
+ goto loser;
+ }
+ cmsdigcx->digobjs = (void**)PORT_ArenaAlloc(poolp, digcnt * sizeof(void *));
+ if (cmsdigcx->digobjs == NULL) {
+ goto loser;
+ }
+ cmsdigcx->digestalgs = (SECAlgorithmID **)PORT_ArenaZAlloc(poolp, (digcnt + 1) * sizeof(SECAlgorithmID *));
+ if (cmsdigcx->digestalgs == NULL) {
+ goto loser;
+ }
}
cmsdigcx->digcnt = 0;
* Create a digest object context for each algorithm.
*/
for (i = 0; i < digcnt; i++) {
- digobj = SecCmsUtilGetHashObjByAlgID(digestalgs[i]);
- /*
- * Skip any algorithm we do not even recognize; obviously,
- * this could be a problem, but if it is critical then the
- * result will just be that the signature does not verify.
- * We do not necessarily want to error out here, because
- * the particular algorithm may not actually be important,
- * but we cannot know that until later.
- */
-#if USE_CDSA_CRYPTO
- if (digobj)
- if (CSSM_DigestDataInit(digobj))
- goto loser;
-#endif
-
- cmsdigcx->digobjs[cmsdigcx->digcnt] = digobj;
- cmsdigcx->digestalgs[cmsdigcx->digcnt] = PORT_ArenaAlloc(poolp, sizeof(SECAlgorithmID));
- if (SECITEM_CopyItem(poolp,
- &(cmsdigcx->digestalgs[cmsdigcx->digcnt]->algorithm),
- &(digestalgs[i]->algorithm))
- || SECITEM_CopyItem(poolp,
- &(cmsdigcx->digestalgs[cmsdigcx->digcnt]->parameters),
- &(digestalgs[i]->parameters)))
- goto loser;
- cmsdigcx->digcnt++;
+ digobj = SecCmsUtilGetHashObjByAlgID(digestalgs[i]);
+ /*
+ * Skip any algorithm we do not even recognize; obviously,
+ * this could be a problem, but if it is critical then the
+ * result will just be that the signature does not verify.
+ * We do not necessarily want to error out here, because
+ * the particular algorithm may not actually be important,
+ * but we cannot know that until later.
+ */
+
+ cmsdigcx->digobjs[cmsdigcx->digcnt] = digobj;
+ cmsdigcx->digestalgs[cmsdigcx->digcnt] = PORT_ArenaAlloc(poolp, sizeof(SECAlgorithmID));
+ if (SECITEM_CopyItem(poolp,
+ &(cmsdigcx->digestalgs[cmsdigcx->digcnt]->algorithm),
+ &(digestalgs[i]->algorithm))
+ || SECITEM_CopyItem(poolp,
+ &(cmsdigcx->digestalgs[cmsdigcx->digcnt]->parameters),
+ &(digestalgs[i]->parameters))) {
+ goto loser;
+ }
+ cmsdigcx->digcnt++;
}
cmsdigcx->saw_contents = PR_FALSE;
return cmsdigcx;
loser:
- if (poolp)
- PORT_FreeArena(poolp, PR_FALSE);
+ if (poolp) {
+ PORT_FreeArena(poolp, PR_FALSE);
+ }
return NULL;
}
dataBuf.Data = (uint8_t *)data;
cmsdigcx->saw_contents = PR_TRUE;
for (i = 0; i < cmsdigcx->digcnt; i++) {
- if (cmsdigcx->digobjs[i]) {
-#if USE_CDSA_CRYPTO
- CSSM_DigestDataUpdate(cmsdigcx->digobjs[i], &dataBuf, 1);
-#else
+ if (cmsdigcx->digobjs[i]) {
/* 64 bits cast: worst case is we truncate the length and we dont hash all the data.
- This may cause an invalid CMS blob larger than 4GB to be validated. Unlikely, but
- possible security issue. There is no way to return an error here, but a check at
- the upper level may happen. */
- /*
- rdar://problem/20642513
- Let's just die a horrible death rather than have the security issue.
- CMS blob over 4GB? Oh well.
- */
- if (len > UINT32_MAX) {
- /* Ugh. */
- abort();
- }
+ This may cause an invalid CMS blob larger than 4GB to be validated. Unlikely, but
+ possible security issue. There is no way to return an error here, but a check at
+ the upper level may happen. */
+ /*
+ rdar://problem/20642513
+ Let's just die a horrible death rather than have the security issue.
+ CMS blob over 4GB? Oh well.
+ */
+ if (len > UINT32_MAX) {
+ /* Ugh. */
+ abort();
+ }
assert(len<=UINT32_MAX); /* Debug check. Correct as long as CC_LONG is uint32_t */
switch (SECOID_GetAlgorithmTag(cmsdigcx->digestalgs[i])) {
- case SEC_OID_SHA1: CC_SHA1_Update((CC_SHA1_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
- case SEC_OID_MD5: CC_MD5_Update((CC_MD5_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
- case SEC_OID_SHA224: CC_SHA224_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
- case SEC_OID_SHA256: CC_SHA256_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
- case SEC_OID_SHA384: CC_SHA384_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
- case SEC_OID_SHA512: CC_SHA512_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
- default:
- break;
+ case SEC_OID_SHA1: CC_SHA1_Update((CC_SHA1_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+ case SEC_OID_MD5: CC_MD5_Update((CC_MD5_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+ case SEC_OID_SHA224: CC_SHA224_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+ case SEC_OID_SHA256: CC_SHA256_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+ case SEC_OID_SHA384: CC_SHA384_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+ case SEC_OID_SHA512: CC_SHA512_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+ default:
+ break;
}
-#endif
}
}
}
{
int i;
- for (i = 0; i < cmsdigcx->digcnt; i++)
- if (cmsdigcx->digobjs[i])
-#if USE_CDSA_CRYPTO
- CSSM_DeleteContext(cmsdigcx->digobjs[i]);
-#else
+ for (i = 0; i < cmsdigcx->digcnt; i++) {
+ if (cmsdigcx->digobjs && cmsdigcx->digobjs[i]) {
free(cmsdigcx->digobjs[i]);
-#endif
+ cmsdigcx->digobjs[i] = NULL;
+ }
+ }
PORT_FreeArena(cmsdigcx->poolp, PR_TRUE);
}
/*
* SecCmsDigestContextFinishMultiple - finish the digests
+ * Note that on iOS, this call only frees the digest objects and requires a call to SecCmsDisgestContextDestroy
+ * or SecCmsDisgestContextCancel (because the digests are allocated out of the context's pool).
+ * The macOS version cancels and frees the digest context (because the digests are allocated from an input arena pool).
*/
OSStatus
SecCmsDigestContextFinishMultiple(SecCmsDigestContextRef cmsdigcx,
- SECAlgorithmID ***digestalgsp,
- SecAsn1Item * **digestsp)
+ SECAlgorithmID ***digestalgsp,
+ SecAsn1Item * **digestsp)
{
-#if USE_CDSA_CRYPTO
- CSSM_CC_HANDLE digboj;
-#else
void * digobj;
-#endif
SecAsn1Item **digests, *digest;
SECAlgorithmID **digestalgs;
int i;
#if 0
/* no contents? do not update digests */
if (digestsp == NULL || !cmsdigcx->saw_contents) {
- for (i = 0; i < cmsdigcx->digcnt; i++)
- if (cmsdigcx->digobjs[i])
-#if USE_CDSA_CRYPTO
- CSSM_DeleteContext(cmsdigcx->digobjs[i]);
-#else
+ for (i = 0; i < cmsdigcx->digcnt; i++) {
+ if (cmsdigcx->digobjs[i]) {
free(cmsdigcx->digobjs[i]);
-#endif
- rv = SECSuccess;
- if (digestsp)
- *digestsp = NULL;
- goto cleanup;
+ }
+ }
+ rv = SECSuccess;
+ if (digestsp) {
+ *digestsp = NULL;
+ }
+ goto cleanup;
}
#endif
digests = (SecAsn1Item * *)PORT_ArenaZAlloc(cmsdigcx->poolp, (cmsdigcx->digcnt+1) * sizeof(SecAsn1Item *));
digest = (SecAsn1Item *)PORT_ArenaZAlloc(cmsdigcx->poolp, cmsdigcx->digcnt * sizeof(SecAsn1Item));
if (digestalgs == NULL || digests == NULL || digest == NULL) {
- goto loser;
+ goto loser;
}
for (i = 0; i < cmsdigcx->digcnt; i++, digest++) {
-
SECOidTag hash_alg = SECOID_GetAlgorithmTag(cmsdigcx->digestalgs[i]);
- int diglength = 0;
-
+ int diglength = 0;
+
switch (hash_alg) {
case SEC_OID_SHA1: diglength = CC_SHA1_DIGEST_LENGTH; break;
case SEC_OID_MD5: diglength = CC_MD5_DIGEST_LENGTH; break;
default: goto loser;
}
- digobj = cmsdigcx->digobjs[i];
- if (digobj)
- {
- digest->Data = (unsigned char*)PORT_ArenaAlloc(cmsdigcx->poolp, diglength);
- if (digest->Data == NULL)
- goto loser;
- digest->Length = diglength;
-#if USE_CDSA_CRYPTO
- CSSM_DigestDataFinal(digobj, digest);
- CSSM_DeleteContext(digobj);
-#else
+ digobj = cmsdigcx->digobjs[i];
+ if (digobj) {
+ digest->Data = (unsigned char*)PORT_ArenaAlloc(cmsdigcx->poolp, diglength);
+ if (digest->Data == NULL)
+ goto loser;
+ digest->Length = diglength;
switch (hash_alg) {
case SEC_OID_SHA1: CC_SHA1_Final(digest->Data, digobj); break;
case SEC_OID_MD5: CC_MD5_Final(digest->Data, digobj); break;
}
free(digobj);
-#endif
- digestalgs[i] = cmsdigcx->digestalgs[i];
- digests[i] = digest;
- }
- else
- {
- digest->Data = NULL;
- digest->Length = 0;
- }
+ digestalgs[i] = cmsdigcx->digestalgs[i];
+ digests[i] = digest;
+ } else {
+ digest->Data = NULL;
+ digest->Length = 0;
+ }
}
digestalgs[i] = NULL;
digests[i] = NULL;
rv = SECSuccess;
loser:
- if (rv == SECSuccess)
- PORT_ArenaUnmark(cmsdigcx->poolp, mark);
- else
- PORT_ArenaRelease(cmsdigcx->poolp, mark);
+ if (rv == SECSuccess) {
+ PORT_ArenaUnmark(cmsdigcx->poolp, mark);
+ } else {
+ PORT_ArenaRelease(cmsdigcx->poolp, mark);
+ }
-/*cleanup:*/
- /* Set things up so SecCmsDigestContextDestroy won't call CSSM_DeleteContext again. */
+ /*cleanup:*/
+ /* Set things up so SecCmsDigestContextDestroy won't call CSSM_DeleteContext again. */
cmsdigcx->digcnt = 0;
return rv;
*/
OSStatus
SecCmsDigestContextFinishSingle(SecCmsDigestContextRef cmsdigcx,
- SecAsn1Item * digest)
+ SecAsn1Item * digest)
{
OSStatus rv = SECFailure;
SecAsn1Item * *dp;
SECAlgorithmID **ap;
/* get the digests into arena, then copy the first digest into poolp */
- if (SecCmsDigestContextFinishMultiple(cmsdigcx, &ap, &dp) != SECSuccess)
- goto loser;
+ if (SecCmsDigestContextFinishMultiple(cmsdigcx, &ap, &dp) != SECSuccess) {
+ goto loser;
+ }
/* Return the first element in the digest array. */
if (digest) {
extern int
SecCmsAlgArrayGetIndexByAlgTag(SECAlgorithmID **algorithmArray, SECOidTag algtag);
-#if USE_CDSA_CRYPTO
-extern CSSM_CC_HANDLE
-#else
+
extern void *
-#endif
SecCmsUtilGetHashObjByAlgID(SECAlgorithmID *algid);
/*
return i;
}
-#if USE_CDSA_CRYPTO
-CSSM_CC_HANDLE
-#else
void *
-#endif
SecCmsUtilGetHashObjByAlgID(SECAlgorithmID *algid)
{
SECOidData *oidData = SECOID_FindOID(&(algid->algorithm));
if (oidData)
{
-#if USE_CDSA_CRYPTO
- CSSM_ALGORITHMS alg = oidData->cssmAlgorithm;
- if (alg)
- {
- CSSM_CC_HANDLE digobj;
- CSSM_CSP_HANDLE cspHandle = SecCspHandleForAlgorithm(alg);
-
- if (!CSSM_CSP_CreateDigestContext(cspHandle, alg, &digobj))
- return digobj;
- }
-#else
void *digobj = NULL;
switch (oidData->offset) {
- case SEC_OID_SHA1:
- digobj = calloc(1, sizeof(CC_SHA1_CTX));
- CC_SHA1_Init(digobj);
- break;
- case SEC_OID_MD5:
- digobj = calloc(1, sizeof(CC_MD5_CTX));
- CC_MD5_Init(digobj);
- break;
- case SEC_OID_SHA224:
- digobj = calloc(1, sizeof(CC_SHA256_CTX));
- CC_SHA224_Init(digobj);
- break;
- case SEC_OID_SHA256:
- digobj = calloc(1, sizeof(CC_SHA256_CTX));
- CC_SHA256_Init(digobj);
- break;
- case SEC_OID_SHA384:
- digobj = calloc(1, sizeof(CC_SHA512_CTX));
- CC_SHA384_Init(digobj);
- break;
- case SEC_OID_SHA512:
- digobj = calloc(1, sizeof(CC_SHA512_CTX));
- CC_SHA512_Init(digobj);
- break;
- default:
- break;
+ case SEC_OID_SHA1:
+ digobj = calloc(1, sizeof(CC_SHA1_CTX));
+ CC_SHA1_Init(digobj);
+ break;
+ case SEC_OID_MD5:
+ digobj = calloc(1, sizeof(CC_MD5_CTX));
+ CC_MD5_Init(digobj);
+ break;
+ case SEC_OID_SHA224:
+ digobj = calloc(1, sizeof(CC_SHA256_CTX));
+ CC_SHA224_Init(digobj);
+ break;
+ case SEC_OID_SHA256:
+ digobj = calloc(1, sizeof(CC_SHA256_CTX));
+ CC_SHA256_Init(digobj);
+ break;
+ case SEC_OID_SHA384:
+ digobj = calloc(1, sizeof(CC_SHA512_CTX));
+ CC_SHA384_Init(digobj);
+ break;
+ case SEC_OID_SHA512:
+ digobj = calloc(1, sizeof(CC_SHA512_CTX));
+ CC_SHA512_Init(digobj);
+ break;
+ default:
+ break;
}
return digobj;
-#endif
}
return 0;
*
* Note: this SPI is meant to be called by libnetcore. It should not be called in any other circumstances.
*
- * @param options
+ * @param metadata
* A `sec_protocol_metadata_t` instance.
*
* @return The identifier for a secure connection experiment, or NULL if none was specified.
(global-name "com.apple.ocspd")
(global-name "com.apple.PowerManagement.control")
(global-name "com.apple.security.syspolicy")
- (global-name "com.apple.security.agent"))
+ (global-name "com.apple.security.agent")
+ (global-name "com.apple.security.agent.login"))
(allow ipc-posix-shm
(ipc-posix-name "com.apple.AppleDatabaseChanged")
189D462D166AC95C001D8533 /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 1000;
+ LastUpgradeCheck = 1120;
ORGANIZATIONNAME = Apple;
};
buildConfigurationList = 189D4630166AC95C001D8533 /* Build configuration list for PBXProject "securityd_service" */;
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
+ English,
en,
);
mainGroup = 189D462C166AC95C001D8533;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPRESSION = lossless;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPRESSION = "respect-asset-catalog";
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
: LocalKey(db, n2h(blob->header.attributes()))
{
// perform basic validation on the incoming blob
- assert(blob);
+ if (blob == NULL) {
+ CssmError::throwMe(CSSMERR_APPLEDL_INVALID_KEY_BLOB);
+ }
blob->validate(CSSMERR_APPLEDL_INVALID_KEY_BLOB);
if (blob->startCryptoBlob > blob->totalLength) {
CssmError::throwMe(CSSMERR_APPLEDL_INVALID_KEY_BLOB);
<array>
<string>kTCCServiceSystemPolicyAllFiles</string>
</array>
+ <key>com.apple.private.security.storage.SystemKeychain</key>
+ <true/>
</dict>
</plist>
#import <XCTest/XCTest.h>
#import <Security/SFAnalytics.h>
+#import "SFAnalytics+Internal.h"
#import "SFAnalyticsDefines.h"
#import "SFAnalyticsSQLiteStore.h"
#import "NSDate+SFAnalytics.h"
static NSInteger _testnum;
static NSString* build = NULL;
static NSString* product = NULL;
+static NSString* modelID = nil;
// MARK: Test helper methods
XCTAssertTrue([rowdata[SFAnalyticsEventClassKey] isKindOfClass:[NSNumber class]] && [rowdata[SFAnalyticsEventClassKey] intValue] == class, @"eventClass is %ld", (long)class);
XCTAssertTrue([rowdata[@"build"] isEqualToString:build], @"event row includes build");
XCTAssertTrue([rowdata[@"product"] isEqualToString:product], @"event row includes product");
+ XCTAssertTrue([rowdata[@"modelid"] isEqualToString:modelID], @"event row includes modelid");
XCTAssertTrue(rowdata[@"internal"], @"event row includes internal");
}
NSLog(@"could not get build version/product, tests should fail");
}
+ modelID = [SFAnalytics hwModelID];
+
[TestResourceUsage monitorTestResourceUsage];
}
{
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
__block NSDictionary* data;
- [_supd getLoggingJSON:YES topic:topic reply:^(NSData *json, NSError *error) {
+ [_supd createLoggingJSON:YES topic:topic reply:^(NSData *json, NSError *error) {
XCTAssertNil(error);
XCTAssertNotNil(json);
if (!error) {
[_sosAnalytics logHardFailureForEventNamed:@"unittestevent" withAttributes:utAttrs];
[_sosAnalytics logSoftFailureForEventNamed:@"unittestevent" withAttributes:utAttrs];
- NSDictionary* data = [self getJSONDataFromSupd];
+ NSDictionary *data = [self getJSONDataFromSupd];
+
[self inspectDataBlobStructure:data];
// TODO: inspect health summaries
XCTAssertEqual(foundErrorEvents, 1);
}
+- (void)testUploadSizeLimits
+{
+ SFAnalyticsTopic *trustTopic = [self TrustTopic];
+ XCTAssertEqual(1000000, trustTopic.uploadSizeLimit);
+
+ SFAnalyticsTopic *keySyncTopic = [self keySyncTopic];
+ XCTAssertEqual(1000000, keySyncTopic.uploadSizeLimit);
+}
+
+- (NSArray<NSDictionary *> *)createRandomEventList:(size_t)count
+{
+ NSMutableArray<NSDictionary *> *eventSet = [[NSMutableArray<NSDictionary *> alloc] init];
+
+ const size_t dataSize = 100;
+ uint8_t backingBuffer[dataSize] = {};
+ for (size_t i = 0; i < count; i++) {
+ NSData *data = [[NSData alloc] initWithBytes:backingBuffer length:dataSize];
+ NSDictionary *entry = @{@"key" : [data base64EncodedStringWithOptions:0]};
+ [eventSet addObject:entry];
+ }
+
+ return eventSet;
+}
+
+- (void)testCreateLoggingJSON
+{
+ NSArray<NSDictionary *> *summaries = [self createRandomEventList:5];
+ NSArray<NSDictionary *> *failures = [self createRandomEventList:100];
+ NSMutableArray<NSDictionary *> *visitedEvents = [[NSMutableArray<NSDictionary *> alloc] init];
+
+ SFAnalyticsTopic *topic = [self TrustTopic];
+ const size_t sizeLimit = 10000; // total size of the encoded data
+ topic.uploadSizeLimit = sizeLimit;
+
+ NSError *error = nil;
+ NSArray<NSDictionary *> *eventSet = [topic createChunkedLoggingJSON:summaries failures:failures error:&error];
+ XCTAssertNil(error);
+
+ for (NSDictionary *event in eventSet) {
+ XCTAssertNotNil([event objectForKey:@"events"]);
+ XCTAssertNotNil([event objectForKey:SFAnalyticsPostTime]);
+ NSArray *events = [event objectForKey:@"events"];
+ for (NSDictionary *summary in summaries) {
+ BOOL foundSummary = NO;
+ for (NSDictionary *innerEvent in events) {
+ if ([summary isEqualToDictionary:innerEvent]) {
+ foundSummary = YES;
+ break;
+ }
+ }
+ XCTAssertTrue(foundSummary);
+ }
+
+ // Record the events we've seen so far
+ for (NSDictionary *innerEvent in events) {
+ [visitedEvents addObject:innerEvent];
+ }
+ }
+
+ // Check that each summary and failure is in the visitedEvents
+ for (NSDictionary *summary in summaries) {
+ BOOL foundSummary = NO;
+ for (NSDictionary *innerEvent in visitedEvents) {
+ if ([summary isEqualToDictionary:innerEvent]) {
+ foundSummary = YES;
+ break;
+ }
+ }
+ XCTAssertTrue(foundSummary);
+ }
+ for (NSDictionary *failure in failures) {
+ BOOL foundFailure = NO;
+ for (NSDictionary *innerEvent in visitedEvents) {
+ if ([failure isEqualToDictionary:innerEvent]) {
+ foundFailure = YES;
+ break;
+ }
+ }
+ XCTAssertTrue(foundFailure);
+ }
+}
+
+- (void)testEventSetChunking
+{
+ NSArray<NSDictionary *> *eventSet = [self createRandomEventList:100];
+ SFAnalyticsTopic *topic = [self TrustTopic];
+
+ const size_t sizeLimit = 10000; // total size of the encoded data
+ size_t encodedEventSize = [topic serializedEventSize:eventSet[0] error:nil];
+ topic.uploadSizeLimit = sizeLimit; // fix the upload limit
+
+ // Chunk up the set, assuming that each chunk already has one event in it.
+ // In practice, this is the health summary.
+ NSError *error = nil;
+ NSArray<NSArray *> *chunkedEvents = [topic chunkFailureSet:(sizeLimit - encodedEventSize) events:eventSet error:nil];
+ XCTAssertNil(error);
+
+ // There should be two resulting chunks, since the set of chunks overflows.
+ XCTAssertEqual(2, [chunkedEvents count]);
+}
// TODO
- (void)testGetSysdiagnoseDump
@property NSString* splunkTopicName;
@property NSURL* splunkBagURL;
@property NSString *internalTopicName;
+@property NSUInteger uploadSizeLimit;
@property NSArray<SFAnalyticsClient*>* topicClients;
// Things below are for unit testing
- (instancetype)initWithDictionary:(NSDictionary *)dictionary name:(NSString *)topicName samplingRates:(NSDictionary *)rates;
- (BOOL)haveEligibleClients;
+- (NSArray<NSDictionary *> *)createChunkedLoggingJSON:(NSArray<NSDictionary *> *)healthSummaries failures:(NSArray<NSDictionary *> *)failures error:(NSError **)error;
+- (NSArray<NSArray *> *)chunkFailureSet:(size_t)sizeCapacity events:(NSArray<NSDictionary *> *)events error:(NSError **)error;
+- (size_t)serializedEventSize:(NSObject *)event error:(NSError**)error;
+ (NSString*)databasePathForCKKS;
+ (NSString*)databasePathForSOS;
+ (NSString*)databasePathForPCS;
NSString* const SFAnalyticsSplunkTopic = @"topic";
-NSString* const SFAnalyticsSplunkPostTime = @"postTime";
NSString* const SFAnalyticsClientId = @"clientId";
NSString* const SFAnalyticsInternal = @"internal";
NSString* const SFAnalyticsDeviceID = @"ckdeviceID";
NSString* const SFAnalyticsAltDSID = @"altDSID";
+NSString* const SFAnalyticsEventCorrelationID = @"eventLinkID";
+
NSString* const SFAnalyticsSecondsCustomerKey = @"SecondsBetweenUploadsCustomer";
NSString* const SFAnalyticsSecondsInternalKey = @"SecondsBetweenUploadsInternal";
NSString* const SFAnalyticsSecondsSeedKey = @"SecondsBetweenUploadsSeed";
__splunkUploadURL = [NSURL URLWithString:dictionary[@"splunk_uploadURL"]];
_splunkBagURL = [NSURL URLWithString:dictionary[@"splunk_bagURL"]];
_allowInsecureSplunkCert = [[dictionary valueForKey:@"splunk_allowInsecureCertificate"] boolValue];
+ _uploadSizeLimit = [[dictionary valueForKey:@"uploadSizeLimit"] unsignedIntegerValue];
+
NSString* splunkEndpoint = dictionary[@"splunk_endpointDomain"];
if (dictionary[@"disableClientId"]) {
_disableClientId = YES;
_splunkBagURL = userDefaultsSplunkBagURL;
}
+ NSInteger userDefaultsUploadSizeLimit = [defaults integerForKey:@"uploadSizeLimit"];
+ if (userDefaultsUploadSizeLimit > 0) {
+ _uploadSizeLimit = userDefaultsUploadSizeLimit;
+ }
+
BOOL userDefaultsAllowInsecureSplunkCert = [defaults boolForKey:@"splunk_allowInsecureCertificate"];
_allowInsecureSplunkCert |= userDefaultsAllowInsecureSplunkCert;
}];
}
-- (BOOL)prepareEventForUpload:(NSMutableDictionary*)event {
+- (BOOL)prepareEventForUpload:(NSMutableDictionary*)event
+ linkedUUID:(NSUUID *)linkedUUID {
if ([self eventIsBlacklisted:event]) {
return NO;
}
event[SFAnalyticsClientId] = @(0);
}
event[SFAnalyticsSplunkTopic] = self->_splunkTopicName ?: [NSNull null];
+ if (linkedUUID) {
+ event[SFAnalyticsEventCorrelationID] = [linkedUUID UUIDString];
+ }
return YES;
}
-- (void)addFailures:(NSMutableArray<NSArray*>*)failures toUploadRecords:(NSMutableArray*)records threshold:(NSUInteger)threshold
+- (void)addFailures:(NSMutableArray<NSArray*>*)failures toUploadRecords:(NSMutableArray*)records threshold:(NSUInteger)threshold linkedUUID:(NSUUID *)linkedUUID
{
// The first 0 through 'threshold' items are getting uploaded in any case (which might be 0 for lower priority data)
*stop = YES;
return;
}
- if ([self prepareEventForUpload:event]) {
+ if ([self prepareEventForUpload:event linkedUUID:linkedUUID]) {
if ([NSJSONSerialization isValidJSONObject:event]) {
[records addObject:event];
} else {
NSRange range = NSMakeRange(threshold, (client.count - threshold) * scale);
NSArray* sub = [client subarrayWithRange:range];
[sub enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
- if ([self prepareEventForUpload:obj]) {
+ if ([self prepareEventForUpload:obj linkedUUID:linkedUUID]) {
[records addObject:obj];
}
}];
return statistics;
}
-- (NSMutableDictionary*)healthSummaryWithName:(NSString*)name store:(SFAnalyticsSQLiteStore*)store
+- (NSMutableDictionary*)healthSummaryWithName:(NSString*)name store:(SFAnalyticsSQLiteStore*)store uuid:(NSUUID *)uuid
{
__block NSMutableDictionary* summary = [NSMutableDictionary new];
}];
// Should always return yes because we already checked for event blacklisting specifically (unless summary itself is blacklisted)
- if (![self prepareEventForUpload:summary]) {
+ if (![self prepareEventForUpload:summary linkedUUID:uuid]) {
secwarning("supd: health summary for %@ blacklisted", name);
return nil;
}
}
}
-- (NSData*)getLoggingJSON:(bool)pretty
- forUpload:(BOOL)upload
- participatingClients:(NSMutableArray<SFAnalyticsClient*>**)clients
- force:(BOOL)force // supdctl uploads ignore privacy settings and recency
- error:(NSError**)error
+- (size_t)serializedEventSize:(NSObject *)event
+ error:(NSError**)error
+{
+ if (![NSJSONSerialization isValidJSONObject:event]) {
+ secnotice("serializedEventSize", "invalid JSON object");
+ return 0;
+ }
+
+ NSData *json = [NSJSONSerialization dataWithJSONObject:event
+ options:0
+ error:error];
+ if (json) {
+ return [json length];
+ } else {
+ secnotice("serializedEventSize", "failed to serialize event");
+ return 0;
+ }
+}
+
+- (NSArray<NSArray *> *)chunkFailureSet:(size_t)sizeCapacity
+ events:(NSArray<NSDictionary *> *)events
+ error:(NSError **)error
+{
+ const size_t postBodyLimit = 1000; // 1000 events in a single upload
+ size_t currentSize = 0;
+ size_t currentEventCount = 0;
+
+ NSMutableArray<NSArray<NSDictionary *> *> *eventChunks = [[NSMutableArray<NSArray<NSDictionary *> *> alloc] init];
+ NSMutableArray<NSDictionary *> *currentEventChunk = [[NSMutableArray<NSDictionary *> alloc] init];
+ for (NSDictionary *event in events) {
+ NSError *localError = nil;
+ size_t eventSize = [self serializedEventSize:event error:&localError];
+ if (localError != nil) {
+ if (error) {
+ *error = localError;
+ }
+ secemergency("Unable to serialize event JSON: %@", [localError localizedDescription]);
+ return nil;
+ }
+
+ BOOL countLessThanLimit = currentEventCount < postBodyLimit;
+ BOOL sizeLessThanCapacity = (currentSize + eventSize) <= sizeCapacity;
+ if (!countLessThanLimit || !sizeLessThanCapacity) {
+ [eventChunks addObject:currentEventChunk];
+ currentEventChunk = [[NSMutableArray<NSDictionary *> alloc] init];
+ currentEventCount = 0;
+ currentSize = 0;
+ }
+
+ [currentEventChunk addObject:event];
+ currentEventCount++;
+ currentSize += eventSize;
+ }
+
+ if ([currentEventChunk count] > 0) {
+ [eventChunks addObject:currentEventChunk];
+ }
+
+ return eventChunks;
+}
+
+- (NSDictionary *)createEventDictionary:(NSArray *)healthSummaries
+ failures:(NSArray<NSDictionary *> *)failures
+ error:(NSError **)error
+{
+ NSMutableArray *events = [[NSMutableArray alloc] init];
+ [events addObjectsFromArray:healthSummaries];
+ if (failures) {
+ [events addObjectsFromArray:failures];
+ }
+
+ NSDictionary *eventDictionary = @{
+ SFAnalyticsPostTime : @([[NSDate date] timeIntervalSince1970] * 1000),
+ @"events" : events,
+ };
+
+ if (![NSJSONSerialization isValidJSONObject:eventDictionary]) {
+ secemergency("json: final dictionary invalid JSON.");
+ if (error) {
+ *error = [NSError errorWithDomain:SupdErrorDomain code:SupdInvalidJSONError
+ userInfo:@{NSLocalizedDescriptionKey : [NSString localizedStringWithFormat:@"Final dictionary for upload is invalid JSON: %@", eventDictionary]}];
+ }
+ return nil;
+ }
+
+ return eventDictionary;
+}
+
+- (NSArray<NSDictionary *> *)createChunkedLoggingJSON:(NSArray<NSDictionary *> *)healthSummaries
+ failures:(NSArray<NSDictionary *> *)failures
+ error:(NSError **)error
{
- NSMutableArray<SFAnalyticsClient*>* localClients = [NSMutableArray new];
- __block NSMutableArray* uploadRecords = [NSMutableArray arrayWithCapacity:_maxEventsToReport];
- __block NSError *localError;
- __block NSMutableArray<NSArray*>* hardFailures = [NSMutableArray new];
- __block NSMutableArray<NSArray*>* softFailures = [NSMutableArray new];
- NSString* ckdeviceID = nil;
- NSString* accountID = nil;
+ NSError *localError = nil;
+ size_t baseSize = [self serializedEventSize:healthSummaries error:&localError];
+ if (localError != nil) {
+ secemergency("Unable to serialize health summary JSON");
+ if (error) {
+ *error = localError;
+ }
+ return nil;
+ }
+
+ NSArray<NSArray *> *chunkedEvents = [self chunkFailureSet:(self.uploadSizeLimit - baseSize) events:failures error:&localError];
+
+ NSMutableArray<NSDictionary *> *jsonResults = [[NSMutableArray<NSDictionary *> alloc] init];
+ for (NSArray<NSDictionary *> *failureSet in chunkedEvents) {
+ NSDictionary *eventDictionary = [self createEventDictionary:healthSummaries failures:failureSet error:error];
+ if (eventDictionary) {
+ [jsonResults addObject:eventDictionary];
+ } else {
+ return nil;
+ }
+ }
+
+ if ([jsonResults count] == 0) {
+ NSDictionary *eventDictionary = [self createEventDictionary:healthSummaries failures:nil error:error];
+ if (eventDictionary) {
+ [jsonResults addObject:eventDictionary];
+ } else {
+ return nil;
+ }
+ }
+
+ return jsonResults;
+}
+
+- (BOOL)copyEvents:(NSMutableArray<NSDictionary *> **)healthSummaries
+ failures:(NSMutableArray<NSDictionary *> **)failures
+ forUpload:(BOOL)upload
+participatingClients:(NSMutableArray<SFAnalyticsClient*>**)clients
+ force:(BOOL)force
+ linkedUUID:(NSUUID *)linkedUUID
+ error:(NSError**)error
+{
+ NSMutableArray<SFAnalyticsClient*> *localClients = [[NSMutableArray alloc] init];
+ NSMutableArray<NSDictionary *> *localHealthSummaries = [[NSMutableArray<NSDictionary *> alloc] init];
+ NSMutableArray<NSDictionary *> *localFailures = [[NSMutableArray<NSDictionary *> alloc] init];
+ NSMutableArray<NSArray*> *hardFailures = [[NSMutableArray alloc] init];
+ NSMutableArray<NSArray*> *softFailures = [[NSMutableArray alloc] init];
+ NSString *ckdeviceID = nil;
+ NSString *accountID = nil;
if (os_variant_has_internal_diagnostics("com.apple.security") && [_internalTopicName isEqualToString:SFAnalyticsTopicKeySync]) {
ckdeviceID = [self askSecurityForCKDeviceID];
}
for (SFAnalyticsClient* client in self->_topicClients) {
if (!force && [client requireDeviceAnalytics] && !_isDeviceAnalyticsEnabled()) {
- // Client required device analytics, yet the user did not opt in.
+ // Client required device analytics, yet the user did not opt in.
secnotice("getLoggingJSON", "Client '%@' requires device analytics yet user did not opt in.", [client name]);
continue;
- }
+ }
if (!force && [client requireiCloudAnalytics] && !_isiCloudAnalyticsEnabled()) {
- // Client required iCloud analytics, yet the user did not opt in.
+ // Client required iCloud analytics, yet the user did not opt in.
secnotice("getLoggingJSON", "Client '%@' requires iCloud analytics yet user did not opt in.", [client name]);
continue;
}
[localClients addObject:client];
}
- NSMutableDictionary* healthSummary = [self healthSummaryWithName:client.name store:store];
+ NSMutableDictionary* healthSummary = [self healthSummaryWithName:client.name store:store uuid:linkedUUID];
if (healthSummary) {
if (ckdeviceID) {
healthSummary[SFAnalyticsDeviceID] = ckdeviceID;
if (accountID) {
healthSummary[SFAnalyticsAltDSID] = accountID;
}
- [uploadRecords addObject:healthSummary];
+ [localHealthSummaries addObject:healthSummary];
}
[hardFailures addObject:store.hardFailures];
code:-10
userInfo:@{NSLocalizedDescriptionKey : description}];
}
- return nil;
+ return NO;
}
if (clients) {
*clients = localClients;
}
- [self addFailures:hardFailures toUploadRecords:uploadRecords threshold:_maxEventsToReport/10];
- [self addFailures:softFailures toUploadRecords:uploadRecords threshold:0];
+ if (failures) {
+ [self addFailures:hardFailures toUploadRecords:localFailures threshold:_maxEventsToReport/10 linkedUUID:linkedUUID];
+ [self addFailures:softFailures toUploadRecords:localFailures threshold:0 linkedUUID:linkedUUID];
+ [*failures addObjectsFromArray:localFailures];
+ }
+
+ if (healthSummaries) {
+ [*healthSummaries addObjectsFromArray:localHealthSummaries];
+ }
- NSDictionary* jsonDict = @{
- SFAnalyticsSplunkPostTime : @([[NSDate date] timeIntervalSince1970] * 1000),
- @"events" : uploadRecords
- };
+ return YES;
+}
- // This check is "belt and suspenders" because we already checked each event separately
- if (![NSJSONSerialization isValidJSONObject:jsonDict]) {
- secemergency("json: final dictionary invalid JSON. This is terrible!");
+- (NSArray<NSDictionary *> *)createChunkedLoggingJSON:(bool)pretty
+ forUpload:(BOOL)upload
+ participatingClients:(NSMutableArray<SFAnalyticsClient*>**)clients
+ force:(BOOL)force // supdctl uploads ignore privacy settings and recency
+ error:(NSError**)error
+{
+ NSUUID *linkedUUID = [NSUUID UUID];
+ NSError *localError = nil;
+ NSMutableArray *failures = [[NSMutableArray alloc] init];
+ NSMutableArray *healthSummaries = [[NSMutableArray alloc] init];
+ BOOL copied = [self copyEvents:&healthSummaries
+ failures:&failures
+ forUpload:upload
+ participatingClients:clients
+ force:force
+ linkedUUID:linkedUUID
+ error:&localError];
+ if (!copied || localError) {
if (error) {
- *error = [NSError errorWithDomain:SupdErrorDomain code:SupdInvalidJSONError
- userInfo:@{NSLocalizedDescriptionKey : [NSString localizedStringWithFormat:@"Final dictionary for upload is invalid JSON: %@", jsonDict]}];
+ *error = localError;
}
return nil;
}
- NSData *json = [NSJSONSerialization dataWithJSONObject:jsonDict
- options:(pretty ? NSJSONWritingPrettyPrinted : 0)
- error:&localError];
-
- if (error) {
- *error = localError;
+ // Trim failures to the max count, based on health summary count
+ if ([failures count] > (_maxEventsToReport - [healthSummaries count])) {
+ NSRange range;
+ range.location = 0;
+ range.length = _maxEventsToReport - [healthSummaries count];
+ failures = [[failures subarrayWithRange:range] mutableCopy];
}
- return json;
+ return [self createChunkedLoggingJSON:healthSummaries failures:failures error:error];
+}
+
+- (NSDictionary *)createLoggingJSON:(bool)pretty
+ forUpload:(BOOL)upload
+ participatingClients:(NSMutableArray<SFAnalyticsClient*>**)clients
+ force:(BOOL)force // supdctl uploads ignore privacy settings and recency
+ error:(NSError**)error
+{
+ NSError *localError = nil;
+ NSMutableArray *failures = [[NSMutableArray alloc] init];
+ NSMutableArray *healthSummaries = [[NSMutableArray alloc] init];
+ BOOL copied = [self copyEvents:&healthSummaries
+ failures:&failures
+ forUpload:upload
+ participatingClients:clients
+ force:force
+ linkedUUID:nil
+ error:&localError];
+ if (!copied || localError) {
+ if (error) {
+ *error = localError;
+ }
+ return nil;
+ }
+
+ // Trim failures to the max count, based on health summary count
+ if ([failures count] > (_maxEventsToReport - [healthSummaries count])) {
+ NSRange range;
+ range.location = 0;
+ range.length = _maxEventsToReport - [healthSummaries count];
+ failures = [[failures subarrayWithRange:range] mutableCopy];
+ }
+
+ return [self createEventDictionary:healthSummaries failures:failures error:error];
}
// Is at least one client eligible for data collection based on user consent? Otherwise callers should NOT reach off-device.
}
}
+- (NSArray<NSData *> *)serializeLoggingEvents:(NSArray<NSDictionary *> *)events
+ error:(NSError **)error
+{
+ if (!events) {
+ return nil;
+ }
+
+ NSMutableArray<NSData *> *serializedEvents = [[NSMutableArray<NSData *> alloc] init];
+ for (NSDictionary *event in events) {
+ NSError *serializationError = nil;
+ NSData* serializedEvent = [NSJSONSerialization dataWithJSONObject:event
+ options:0
+ error:&serializationError];
+ if (serializedEvent && !serializationError) {
+ [serializedEvents addObject:serializedEvent];
+ } else if (error) {
+ *error = serializationError;
+ return nil;
+ }
+ }
+
+ return serializedEvents;
+}
+
- (BOOL)uploadAnalyticsWithError:(NSError**)error force:(BOOL)force {
[self sendNotificationForOncePerReportSamplers];
}
NSMutableArray<SFAnalyticsClient*>* clients = [NSMutableArray new];
- NSData* json = [topic getLoggingJSON:false forUpload:YES participatingClients:&clients force:force error:&localError];
- if (json) {
- if ([topic isSampledUpload]) {
+ NSArray<NSDictionary *> *jsonEvents = [topic createChunkedLoggingJSON:false forUpload:YES participatingClients:&clients force:force error:&localError];
+ if (!jsonEvents || localError) {
+ if ([[localError domain] isEqualToString:SupdErrorDomain] && [localError code] == SupdInvalidJSONError) {
+ // Pretend this was a success because at least we'll get rid of bad data.
+ // If someone keeps logging bad data and we only catch it here then
+ // this causes sustained data loss for the entire topic.
+ [topic updateUploadDateForClients:clients date:[NSDate date] clearData:YES];
+ }
+ secerror("upload: failed to create chunked log events for logging topic %@: %@", [topic internalTopicName], localError);
+ continue;
+ }
+
+ NSArray<NSData *> *serializedEvents = [self serializeLoggingEvents:jsonEvents error:&localError];
+ if (!serializedEvents || localError) {
+ if ([[localError domain] isEqualToString:SupdErrorDomain] && [localError code] == SupdInvalidJSONError) {
+ // Pretend this was a success because at least we'll get rid of bad data.
+ // If someone keeps logging bad data and we only catch it here then
+ // this causes sustained data loss for the entire topic.
+ [topic updateUploadDateForClients:clients date:[NSDate date] clearData:YES];
+ }
+ secerror("upload: failed to serialized chunked log events for logging topic %@: %@", [topic internalTopicName], localError);
+ continue;
+ }
+
+ if ([topic isSampledUpload]) {
+ for (NSData *json in serializedEvents) {
if (![self->_reporter saveReport:json fileName:[topic internalTopicName]]) {
secerror("upload: failed to write analytics data to log");
}
} else {
secerror("upload: Failed to post JSON for %@: %@", [topic internalTopicName], localError);
}
- } else {
- /* If we didn't sample this report, update date to prevent trying to upload again sooner
- * than we should. Clear data so that per-day calculations remain consistent. */
- secnotice("upload", "skipping unsampled upload for %@ and clearing data", [topic internalTopicName]);
- [topic updateUploadDateForClients:clients date:[NSDate date] clearData:YES];
}
} else {
- if ([[localError domain] isEqualToString:SupdErrorDomain] && [localError code] == SupdInvalidJSONError) {
- // Pretend this was a success because at least we'll get rid of bad data.
- // If someone keeps logging bad data and we only catch it here then
- // this causes sustained data loss for the entire topic.
- [topic updateUploadDateForClients:clients date:[NSDate date] clearData:YES];
- }
- secerror("upload: failed to get logging JSON for topic %@: %@", [topic internalTopicName], localError);
+ /* If we didn't sample this report, update date to prevent trying to upload again sooner
+ * than we should. Clear data so that per-day calculations remain consistent. */
+ secnotice("upload", "skipping unsampled upload for %@ and clearing data", [topic internalTopicName]);
+ [topic updateUploadDateForClients:clients date:[NSDate date] clearData:YES];
}
}
if (error && localError) {
reply(info, nil);
}
-
-
- (NSString*)stringForEventClass:(SFAnalyticsEventClass)eventClass
{
if (eventClass == SFAnalyticsEventClassNote) {
reply([self getSysdiagnoseDump]);
}
-- (void)getLoggingJSON:(bool)pretty topic:(NSString *)topicName reply:(void (^)(NSData*, NSError*))reply {
- secnotice("rpcGetLoggingJSON", "Building a JSON blob resembling the one we would have uploaded");
+- (void)createLoggingJSON:(bool)pretty topic:(NSString *)topicName reply:(void (^)(NSData *, NSError*))reply {
+ secnotice("rpcCreateLoggingJSON", "Building a JSON blob resembling the one we would have uploaded");
NSError* error = nil;
[self sendNotificationForOncePerReportSamplers];
- NSData* json = nil;
+ NSDictionary *eventDictionary = nil;
for (SFAnalyticsTopic* topic in self->_analyticsTopics) {
if ([topic.internalTopicName isEqualToString:topicName]) {
- json = [topic getLoggingJSON:pretty forUpload:NO participatingClients:nil force:!runningTests error:&error];
+ eventDictionary = [topic createLoggingJSON:pretty forUpload:NO participatingClients:nil force:!runningTests error:&error];
}
}
- if (!json) {
+
+ NSData *data = nil;
+ if (!eventDictionary) {
secerror("Unable to obtain JSON: %@", error);
+ } else {
+ data = [NSJSONSerialization dataWithJSONObject:eventDictionary
+ options:(pretty ? NSJSONWritingPrettyPrinted : 0)
+ error:&error];
+ }
+
+ reply(data, error);
+}
+
+- (void)createChunkedLoggingJSON:(bool)pretty topic:(NSString *)topicName reply:(void (^)(NSData *, NSError*))reply
+{
+ secnotice("rpcCreateChunkedLoggingJSON", "Building an array of JSON blobs resembling the one we would have uploaded");
+ NSError* error = nil;
+ [self sendNotificationForOncePerReportSamplers];
+ NSArray<NSDictionary *> *events = nil;
+ for (SFAnalyticsTopic* topic in self->_analyticsTopics) {
+ if ([topic.internalTopicName isEqualToString:topicName]) {
+ events = [topic createChunkedLoggingJSON:pretty forUpload:NO participatingClients:nil force:!runningTests error:&error];
+ }
+ }
+
+ NSData *data = nil;
+ if (!events) {
+ secerror("Unable to obtain JSON: %@", error);
+ } else {
+ data = [NSJSONSerialization dataWithJSONObject:events
+ options:(pretty ? NSJSONWritingPrettyPrinted : 0)
+ error:&error];
}
- reply(json, error);
+
+ reply(data, error);
}
- (void)forceUploadWithReply:(void (^)(BOOL, NSError*))reply {
@protocol supdProtocol
- (void)getSysdiagnoseDumpWithReply:(void (^)(NSString*))reply;
-- (void)getLoggingJSON:(bool)pretty topic:(NSString *)topicName reply:(void (^)(NSData*, NSError*))reply;
+- (void)createLoggingJSON:(bool)pretty topic:(NSString *)topicName reply:(void (^)(NSData *, NSError*))reply;
+- (void)createChunkedLoggingJSON:(bool)pretty topic:(NSString *)topicName reply:(void (^)(NSData *, NSError*))reply;
- (void)forceUploadWithReply:(void (^)(BOOL, NSError*))reply;
- (void)setUploadDateWith:(NSDate *)date reply:(void (^)(BOOL, NSError*))reply;
- (void)clientStatus:(void (^)(NSDictionary<NSString *, id> *, NSError *))reply;
[connection invalidate];
}
-static void getLoggingJSON(char *topicName)
+static void createLoggingJSON(char *topicName)
{
NSString *topic = topicName ? [NSString stringWithUTF8String:topicName] : SFAnalyticsTopicKeySync;
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[[connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
nsprintf(@"Could not communicate with supd: %@", error);
dispatch_semaphore_signal(sema);
- }] getLoggingJSON:YES topic:topic reply:^(NSData* data, NSError* error) {
+ }] createLoggingJSON:YES topic:topic reply:^(NSData* data, NSError* error) {
+ if (data) {
+ // Success! Only print the JSON blob to make output easier to parse
+ nsprintf(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
+ } else {
+ nsprintf(@"supd gave us an error: %@", error);
+ }
+ dispatch_semaphore_signal(sema);
+ }];
+
+ if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 20)) != 0) {
+ printf("\n\nError: timed out waiting for response from supd\n");
+ }
+ [connection invalidate];
+}
+
+static void createChunkedLoggingJSON(char *topicName)
+{
+ NSString *topic = topicName ? [NSString stringWithUTF8String:topicName] : SFAnalyticsTopicKeySync;
+ dispatch_semaphore_t sema = dispatch_semaphore_create(0);
+ NSXPCConnection* connection = getConnection();
+ [[connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
+ nsprintf(@"Could not communicate with supd: %@", error);
+ dispatch_semaphore_signal(sema);
+ }] createChunkedLoggingJSON:YES topic:topic reply:^(NSData* data, NSError* error) {
if (data) {
// Success! Only print the JSON blob to make output easier to parse
nsprintf(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
static int forceUpload = false;
static int getJSON = false;
+static int getChunkedJSON = false;
static int getSysdiagnose = false;
static int getInfo = false;
static int setOldUploadDate = false;
{
static struct argument options[] = {
{ .shortname='t', .longname="topicName", .argument=&topicName, .description="Operate on a non-default topic"},
-
{ .command="sysdiagnose", .flag=&getSysdiagnose, .flagval=true, .description="Retrieve the current sysdiagnose dump for security analytics"},
{ .command="get", .flag=&getJSON, .flagval=true, .description="Get the JSON blob we would upload to the server if an upload were due"},
+ { .command="getChunked", .flag=&getChunkedJSON, .flagval=true, .description="Chunk the JSON blob"},
{ .command="upload", .flag=&forceUpload, .flagval=true, .description="Force an upload of analytics data to server (ignoring privacy settings)"},
{ .command="info", .flag=&getInfo, .flagval=true, .description="Request info about clients"},
{ .command="set-old-upload-date", .flag=&setOldUploadDate, .flagval=true, .description="Clear last upload date"},
if (forceUpload) {
forceUploadAnalytics();
} else if (getJSON) {
- getLoggingJSON(topicName);
+ createLoggingJSON(topicName);
+ } else if (getChunkedJSON) {
+ createChunkedLoggingJSON(topicName);
} else if (getSysdiagnose) {
getSysdiagnoseDump();
} else if (getInfo) {
--- /dev/null
+/*
+* Copyright (c) 2006-2010,2012-2019 Apple Inc. All Rights Reserved.
+*/
+
+#include <AssertMacros.h>
+#import <XCTest/XCTest.h>
+#import <Security/SecCertificatePriv.h>
+#include <Security/SecTrustPriv.h>
+#include <Security/SecPolicyPriv.h>
+#include "OSX/utilities/array_size.h"
+#include "OSX/utilities/SecCFWrappers.h"
+
+#import "../TestMacroConversions.h"
+#import "TrustEvaluationTestCase.h"
+
+#import "ExceptionTests_data.h"
+
+@interface TrustExceptionTests : TrustEvaluationTestCase
+@end
+
+@implementation TrustExceptionTests
+
+static NSArray *certs = nil;
+static NSDate *date = nil;
+
++ (void)setUp
+{
+ [super setUp];
+ SecCertificateRef cert0 = SecCertificateCreateWithBytes(NULL, _exception_cert0, sizeof(_exception_cert0));
+ SecCertificateRef cert1 = SecCertificateCreateWithBytes(NULL, _exception_cert1, sizeof(_exception_cert1));
+ certs = @[ (__bridge id)cert0, (__bridge id)cert1 ];
+ date = [NSDate dateWithTimeIntervalSinceReferenceDate:545000000.0]; /* April 9, 2018 at 1:53:20 PM PDT */
+
+ CFReleaseNull(cert0);
+ CFReleaseNull(cert1);
+}
+
+#if !TARGET_OS_BRIDGE
+// bridgeOS doesn't have a system root store
+- (void)testPassingTrust
+{
+ SecTrustRef trust = NULL;
+ SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("store.apple.com"));
+ ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+ ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+
+ SecTrustResultType trustResult;
+ ok(SecTrustEvaluateWithError(trust, NULL), "evaluate trust");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult));
+ is_status(trustResult, kSecTrustResultUnspecified,
+ "trust is kSecTrustResultUnspecified");
+ CFDataRef exceptions;
+ ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+ ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+ is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+ CFReleaseNull(trust);
+ CFReleaseNull(policy);
+ CFReleaseNull(exceptions);
+}
+#endif
+
+- (void)testFailingTrust
+{
+ SecTrustRef trust = NULL;
+ SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("badstore.apple.com"));
+ ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+ ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+
+ SecTrustResultType trustResult;
+ XCTAssertFalse(SecTrustEvaluateWithError(trust, NULL), "evaluate trust");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult));
+ is_status(trustResult, kSecTrustResultRecoverableTrustFailure);
+ CFDataRef exceptions;
+ ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+ ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+ is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+ CFReleaseNull(trust);
+ CFReleaseNull(policy);
+ CFReleaseNull(exceptions);
+}
+
+- (void)testNewTrustObjectSameFailure
+{
+ SecTrustRef trust = NULL;
+ SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("badstore.apple.com"));
+ ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+ ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+
+ SecTrustResultType trustResult;
+ XCTAssertFalse(SecTrustEvaluateWithError(trust, NULL), "evaluate trust");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult));
+ is_status(trustResult, kSecTrustResultRecoverableTrustFailure);
+ CFDataRef exceptions;
+ ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+ ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+ is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+ /* new trust with the same failing policy and certs should pass */
+ CFReleaseNull(trust);
+ ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+ ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+ ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+ is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+ CFReleaseNull(trust);
+ CFReleaseNull(policy);
+ CFReleaseNull(exceptions);
+}
+
+#if !TARGET_OS_BRIDGE
+// bridgeOS always has an AnchorTrusted error due to lack of a system root store
+- (void)testIntroduceNewAnchorTrustedFailure
+{
+ SecTrustRef trust = NULL;
+ SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("badstore.apple.com"));
+ ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+ ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+
+ SecTrustResultType trustResult;
+ XCTAssertFalse(SecTrustEvaluateWithError(trust, NULL), "evaluate trust");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult));
+ is_status(trustResult, kSecTrustResultRecoverableTrustFailure);
+ CFDataRef exceptions;
+ ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+ ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+ is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+ // new AnchorTrusted failure
+ ok_status(SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)@[]), "set empty anchor list");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+ is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure");
+
+ // fix AnchorTrusted failure
+ ok_status(SecTrustSetAnchorCertificatesOnly(trust, false), "trust passed in anchors and system anchors");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+ is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+ CFReleaseNull(trust);
+ CFReleaseNull(policy);
+ CFReleaseNull(exceptions);
+}
+#endif
+
+- (void)testIntroduceNewExpiredFailure
+{
+ SecTrustRef trust = NULL;
+ SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("badstore.apple.com"));
+ ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+ ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+
+ SecTrustResultType trustResult;
+ XCTAssertFalse(SecTrustEvaluateWithError(trust, NULL), "evaluate trust");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult));
+ is_status(trustResult, kSecTrustResultRecoverableTrustFailure);
+ CFDataRef exceptions;
+ ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+ ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+ is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+ // new expiry failure
+ ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)[NSDate dateWithTimeIntervalSinceReferenceDate:667680000.0]),
+ "set date to far future so certs are expired");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+ is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure");
+
+ CFReleaseNull(trust);
+ CFReleaseNull(policy);
+ CFReleaseNull(exceptions);
+}
+
+- (void)testNewTrustObjectNewHostnameFailure
+{
+ SecTrustRef trust = NULL;
+ SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("store.apple.com"));
+ ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+ ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+ CFDataRef exceptions;
+ ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+
+ CFReleaseNull(trust);
+ CFReleaseNull(policy);
+ policy = SecPolicyCreateSSL(true, CFSTR("badstore.apple.com"));
+ ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust with hostname mismatch");
+ ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+
+ /* exceptions from the old trust evaluation should fail */
+ SecTrustResultType trustResult;
+ ok(SecTrustSetExceptions(trust, exceptions), "set old exceptions");
+ XCTAssertFalse(SecTrustEvaluateWithError(trust, NULL), "evaluate trust");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult));
+ is_status(trustResult, kSecTrustResultRecoverableTrustFailure);
+
+ /* we should be able to get new exceptions and pass */
+ CFReleaseNull(exceptions);
+ ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+ ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+ is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+ CFReleaseNull(trust);
+ CFReleaseNull(policy);
+ CFReleaseNull(exceptions);
+}
+
+- (void)testClearExceptions
+{
+ SecTrustRef trust = NULL;
+ SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("badstore.apple.com"));
+ ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+ ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+
+ SecTrustResultType trustResult;
+ XCTAssertFalse(SecTrustEvaluateWithError(trust, NULL), "evaluate trust");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult));
+ is_status(trustResult, kSecTrustResultRecoverableTrustFailure);
+ CFDataRef exceptions;
+ ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+ ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+ is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+ XCTAssertFalse(SecTrustSetExceptions(trust, NULL));
+ ok_status(SecTrustGetTrustResult(trust, &trustResult));
+ is_status(trustResult, kSecTrustResultRecoverableTrustFailure);
+
+ CFReleaseNull(trust);
+ CFReleaseNull(policy);
+ CFReleaseNull(exceptions);
+}
+
+- (void)testWrongCertForExceptions
+{
+ SecTrustRef trust = NULL;
+ SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("badstore.apple.com"));
+ ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+ ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+
+ SecTrustResultType trustResult;
+ XCTAssertFalse(SecTrustEvaluateWithError(trust, NULL), "evaluate trust");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult));
+ is_status(trustResult, kSecTrustResultRecoverableTrustFailure);
+ CFDataRef exceptions;
+ ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+ ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+ is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+ /* new trust with the same failing policy and certs should pass */
+ CFReleaseNull(trust);
+ CFReleaseNull(policy);
+ SecCertificateRef sscert0 = SecCertificateCreateWithBytes(NULL, _exception_self_signed, sizeof(_exception_self_signed));
+ policy = SecPolicyCreateSSL(false, CFSTR("self-signed.ssltest.apple.com"));
+ ok_status(SecTrustCreateWithCertificates(sscert0, policy, &trust), "create trust");
+ ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+ XCTAssertFalse(SecTrustSetExceptions(trust, exceptions), "set exceptions fails for other cert");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+ is_status(trustResult, kSecTrustResultRecoverableTrustFailure);
+
+ CFReleaseNull(trust);
+ CFReleaseNull(policy);
+ CFReleaseNull(exceptions);
+}
+
+#if TARGET_OS_IPHONE
+- (void)testExtensionsEpoch
+{
+ SecTrustRef trust = NULL;
+ SecTrustResultType trustResult;
+ CFDataRef exceptions = NULL;
+
+ SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("badstore.apple.com"));
+ ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+ ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+ ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+
+ /* Test the uninitialized extensions epoch. */
+ CFErrorRef exceptionResetCountError = NULL;
+ uint64_t exceptionResetCount = SecTrustGetExceptionResetCount(&exceptionResetCountError);
+ ok(exceptionResetCount == 0, "exception reset count is uninitialized");
+ is(SecTrustGetExceptionResetCount(&exceptionResetCountError), exceptionResetCount, "SecTrustGetExceptionResetCount is idempotent");
+ ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+ is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+ /* Test increasing the extensions epoch. */
+ exceptionResetCountError = NULL;
+ ok_status(SecTrustIncrementExceptionResetCount(&exceptionResetCountError), "increase exception reset count");
+ exceptionResetCountError = NULL;
+ is(SecTrustGetExceptionResetCount(&exceptionResetCountError), 1 + exceptionResetCount, "exception reset count is 1 + previous count");
+
+ /* Test trust evaluation under a future extensions epoch. */
+ ok(!SecTrustSetExceptions(trust, exceptions), "set exceptions");
+ ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+ is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure");
+
+ CFReleaseNull(trust);
+ CFReleaseNull(policy);
+ CFReleaseNull(exceptions);
+}
+#endif
+
+#if !TARGET_OS_BRIDGE
+// bridgeOS doesn't support Valid
+- (void)testFatalResultsNonOverride
+{
+ id root = [self SecCertificateCreateFromPEMResource:@"ca-ki" subdirectory:@"si-88-sectrust-valid-data"];
+ id revokedLeaf = [self SecCertificateCreateFromPEMResource:@"leaf-ki-revoked1" subdirectory:@"si-88-sectrust-valid-data"];
+ TestTrustEvaluation *eval = [[TestTrustEvaluation alloc] initWithCertificates:@[revokedLeaf, root] policies:nil];
+ [eval setVerifyDate:[NSDate dateWithTimeIntervalSinceReferenceDate:542400000.0]]; // March 10, 2018 at 10:40:00 AM PST
+ [eval setAnchors:@[root]];
+ XCTAssertFalse([eval evaluate:nil]);
+ XCTAssertEqual(eval.trustResult, kSecTrustResultFatalTrustFailure);
+
+ /* try to set exceptions on the trust and ensure it still fails */
+ NSData *exceptions = CFBridgingRelease(SecTrustCopyExceptions(eval.trust));
+ XCTAssertNotNil(exceptions);
+ XCTAssert(SecTrustSetExceptions(eval.trust, (__bridge CFDataRef)exceptions));
+ XCTAssertFalse([eval evaluate:nil]);
+ XCTAssertEqual(eval.trustResult, kSecTrustResultFatalTrustFailure);
+}
+#endif
+
+@end
--- /dev/null
+/*
+* Copyright (c) 2019 Apple Inc. All Rights Reserved.
+*/
+
+#ifndef _TRUSTTESTS_EXCEPTION_TESTS_H_
+#define _TRUSTTESTS_EXCEPTION_TESTS_H_
+
+/*
+ Serial Number:
+ 01:f1:eb:db:ee:d3:1d:7a:b5:72:54:7d:34:43:4b:87
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 Extended Validation Server CA
+ Validity
+ Not Before: Mar 22 00:00:00 2018 GMT
+ Not After : Mar 23 12:00:00 2019 GMT
+ Subject: businessCategory=Private Organization/jurisdictionC=US/jurisdictionST=California/serialNumber=C0806592, C=US, ST=California, L=Cupertino, O=Apple Inc., OU=Internet Services for Akamai, CN=store.apple.com
+ */
+static unsigned char _exception_cert0[]={
+ 0x30,0x82,0x06,0xF7,0x30,0x82,0x05,0xDF,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x01,
+ 0xF1,0xEB,0xDB,0xEE,0xD3,0x1D,0x7A,0xB5,0x72,0x54,0x7D,0x34,0x43,0x4B,0x87,0x30,
+ 0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,0x75,
+ 0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,
+ 0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,
+ 0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77,
+ 0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31,
+ 0x34,0x30,0x32,0x06,0x03,0x55,0x04,0x03,0x13,0x2B,0x44,0x69,0x67,0x69,0x43,0x65,
+ 0x72,0x74,0x20,0x53,0x48,0x41,0x32,0x20,0x45,0x78,0x74,0x65,0x6E,0x64,0x65,0x64,
+ 0x20,0x56,0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x53,0x65,0x72,0x76,
+ 0x65,0x72,0x20,0x43,0x41,0x30,0x1E,0x17,0x0D,0x31,0x38,0x30,0x33,0x32,0x32,0x30,
+ 0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x31,0x39,0x30,0x33,0x32,0x33,0x31,0x32,
+ 0x30,0x30,0x30,0x30,0x5A,0x30,0x81,0xF0,0x31,0x1D,0x30,0x1B,0x06,0x03,0x55,0x04,
+ 0x0F,0x0C,0x14,0x50,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x4F,0x72,0x67,0x61,0x6E,
+ 0x69,0x7A,0x61,0x74,0x69,0x6F,0x6E,0x31,0x13,0x30,0x11,0x06,0x0B,0x2B,0x06,0x01,
+ 0x04,0x01,0x82,0x37,0x3C,0x02,0x01,0x03,0x13,0x02,0x55,0x53,0x31,0x1B,0x30,0x19,
+ 0x06,0x0B,0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x3C,0x02,0x01,0x02,0x13,0x0A,0x43,
+ 0x61,0x6C,0x69,0x66,0x6F,0x72,0x6E,0x69,0x61,0x31,0x11,0x30,0x0F,0x06,0x03,0x55,
+ 0x04,0x05,0x13,0x08,0x43,0x30,0x38,0x30,0x36,0x35,0x39,0x32,0x31,0x0B,0x30,0x09,
+ 0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x13,0x30,0x11,0x06,0x03,0x55,
+ 0x04,0x08,0x13,0x0A,0x43,0x61,0x6C,0x69,0x66,0x6F,0x72,0x6E,0x69,0x61,0x31,0x12,
+ 0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x13,0x09,0x43,0x75,0x70,0x65,0x72,0x74,0x69,
+ 0x6E,0x6F,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x13,0x0A,0x41,0x70,0x70,
+ 0x6C,0x65,0x20,0x49,0x6E,0x63,0x2E,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04,0x0B,
+ 0x13,0x1C,0x49,0x6E,0x74,0x65,0x72,0x6E,0x65,0x74,0x20,0x53,0x65,0x72,0x76,0x69,
+ 0x63,0x65,0x73,0x20,0x66,0x6F,0x72,0x20,0x41,0x6B,0x61,0x6D,0x61,0x69,0x31,0x18,
+ 0x30,0x16,0x06,0x03,0x55,0x04,0x03,0x13,0x0F,0x73,0x74,0x6F,0x72,0x65,0x2E,0x61,
+ 0x70,0x70,0x6C,0x65,0x2E,0x63,0x6F,0x6D,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,
+ 0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,
+ 0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xB5,0xDF,0x48,0x6A,0xE5,0x5F,0x0C,
+ 0x42,0xBF,0x71,0x07,0xAB,0xE6,0x48,0xD2,0x2C,0x13,0x7C,0x29,0xF7,0x90,0xC2,0x45,
+ 0x39,0x43,0x48,0x2E,0x16,0x22,0xBF,0xAB,0x48,0x20,0x14,0x27,0xE7,0x87,0x52,0x97,
+ 0xF6,0x64,0x0C,0x62,0xF5,0x39,0x54,0x64,0x09,0xE8,0x29,0x56,0xF4,0x03,0x40,0x5A,
+ 0xFA,0x9A,0x11,0x2B,0xD0,0x01,0x51,0x1E,0xED,0xB8,0x51,0xCB,0xAB,0x6B,0x7A,0x99,
+ 0xF3,0xB0,0x6E,0xA8,0xC4,0xB6,0xAF,0x17,0xEC,0xA6,0xD2,0x4D,0xCA,0x63,0x3D,0x59,
+ 0x30,0x25,0x30,0x54,0x86,0xDB,0x70,0x7A,0xEC,0x07,0x83,0x19,0xA0,0x44,0xE7,0x3C,
+ 0x68,0xE0,0xFD,0xB9,0xEE,0x3F,0xAC,0xF3,0x32,0x5A,0x5F,0x42,0x31,0x94,0x70,0x2C,
+ 0xC5,0x73,0xD2,0x79,0x23,0x5B,0x96,0x45,0xB1,0xB3,0x2A,0xA1,0x5A,0x69,0xFE,0xBE,
+ 0x52,0x0D,0x5D,0x79,0x18,0xCA,0xF1,0x44,0x92,0xA0,0x27,0x1F,0xAA,0x6E,0x9D,0x6F,
+ 0x1B,0x83,0x5B,0x73,0x28,0x1D,0x87,0xB5,0x70,0x0E,0x3D,0xED,0xE2,0xC2,0x34,0x8A,
+ 0x81,0xB2,0x22,0x40,0x98,0x77,0x2F,0x34,0x1B,0x70,0xEC,0x96,0x3F,0x91,0xB9,0xFF,
+ 0xC9,0xE5,0x7E,0xE7,0x25,0xEF,0xDB,0x9A,0x58,0x4E,0xB2,0x92,0x19,0xA5,0x8D,0xEB,
+ 0x76,0xF8,0xA8,0x48,0x9F,0x3D,0x10,0x0C,0xE4,0x69,0x7B,0xE7,0xB7,0xCA,0xF6,0x14,
+ 0x5E,0x93,0x1E,0x20,0x37,0x1B,0xB8,0xB1,0x2C,0xD1,0x46,0x5C,0xD7,0x85,0x4A,0x2E,
+ 0x19,0xB2,0x3C,0x31,0xDC,0x3D,0xB5,0x3C,0xEC,0x49,0x8D,0x9C,0xCF,0x75,0x0B,0xFC,
+ 0x03,0x31,0x37,0xE7,0xA5,0xD6,0x9D,0x19,0x0D,0x02,0x03,0x01,0x00,0x01,0xA3,0x82,
+ 0x03,0x05,0x30,0x82,0x03,0x01,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,
+ 0x16,0x80,0x14,0x3D,0xD3,0x50,0xA5,0xD6,0xA0,0xAD,0xEE,0xF3,0x4A,0x60,0x0A,0x65,
+ 0xD3,0x21,0xD4,0xF8,0xF8,0xD6,0x0F,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,
+ 0x04,0x14,0x6F,0x89,0xAE,0x9B,0xE4,0x8A,0x21,0x3A,0x3B,0xEA,0xAA,0xBE,0x99,0x90,
+ 0xC8,0xDB,0x34,0x80,0x21,0xB9,0x30,0x2F,0x06,0x03,0x55,0x1D,0x11,0x04,0x28,0x30,
+ 0x26,0x82,0x0F,0x73,0x74,0x6F,0x72,0x65,0x2E,0x61,0x70,0x70,0x6C,0x65,0x2E,0x63,
+ 0x6F,0x6D,0x82,0x13,0x77,0x77,0x77,0x2E,0x73,0x74,0x6F,0x72,0x65,0x2E,0x61,0x70,
+ 0x70,0x6C,0x65,0x2E,0x63,0x6F,0x6D,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,
+ 0xFF,0x04,0x04,0x03,0x02,0x05,0xA0,0x30,0x1D,0x06,0x03,0x55,0x1D,0x25,0x04,0x16,
+ 0x30,0x14,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x01,0x06,0x08,0x2B,0x06,
+ 0x01,0x05,0x05,0x07,0x03,0x02,0x30,0x75,0x06,0x03,0x55,0x1D,0x1F,0x04,0x6E,0x30,
+ 0x6C,0x30,0x34,0xA0,0x32,0xA0,0x30,0x86,0x2E,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,
+ 0x63,0x72,0x6C,0x33,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,
+ 0x6D,0x2F,0x73,0x68,0x61,0x32,0x2D,0x65,0x76,0x2D,0x73,0x65,0x72,0x76,0x65,0x72,
+ 0x2D,0x67,0x32,0x2E,0x63,0x72,0x6C,0x30,0x34,0xA0,0x32,0xA0,0x30,0x86,0x2E,0x68,
+ 0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63,0x72,0x6C,0x34,0x2E,0x64,0x69,0x67,0x69,0x63,
+ 0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x73,0x68,0x61,0x32,0x2D,0x65,0x76,0x2D,
+ 0x73,0x65,0x72,0x76,0x65,0x72,0x2D,0x67,0x32,0x2E,0x63,0x72,0x6C,0x30,0x4B,0x06,
+ 0x03,0x55,0x1D,0x20,0x04,0x44,0x30,0x42,0x30,0x37,0x06,0x09,0x60,0x86,0x48,0x01,
+ 0x86,0xFD,0x6C,0x02,0x01,0x30,0x2A,0x30,0x28,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,
+ 0x07,0x02,0x01,0x16,0x1C,0x68,0x74,0x74,0x70,0x73,0x3A,0x2F,0x2F,0x77,0x77,0x77,
+ 0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x43,0x50,
+ 0x53,0x30,0x07,0x06,0x05,0x67,0x81,0x0C,0x01,0x01,0x30,0x81,0x88,0x06,0x08,0x2B,
+ 0x06,0x01,0x05,0x05,0x07,0x01,0x01,0x04,0x7C,0x30,0x7A,0x30,0x24,0x06,0x08,0x2B,
+ 0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x86,0x18,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,
+ 0x6F,0x63,0x73,0x70,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,
+ 0x6D,0x30,0x52,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x02,0x86,0x46,0x68,
+ 0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63,0x61,0x63,0x65,0x72,0x74,0x73,0x2E,0x64,0x69,
+ 0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x44,0x69,0x67,0x69,0x43,
+ 0x65,0x72,0x74,0x53,0x48,0x41,0x32,0x45,0x78,0x74,0x65,0x6E,0x64,0x65,0x64,0x56,
+ 0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x53,0x65,0x72,0x76,0x65,0x72,0x43,
+ 0x41,0x2E,0x63,0x72,0x74,0x30,0x09,0x06,0x03,0x55,0x1D,0x13,0x04,0x02,0x30,0x00,
+ 0x30,0x82,0x01,0x03,0x06,0x0A,0x2B,0x06,0x01,0x04,0x01,0xD6,0x79,0x02,0x04,0x02,
+ 0x04,0x81,0xF4,0x04,0x81,0xF1,0x00,0xEF,0x00,0x75,0x00,0xA4,0xB9,0x09,0x90,0xB4,
+ 0x18,0x58,0x14,0x87,0xBB,0x13,0xA2,0xCC,0x67,0x70,0x0A,0x3C,0x35,0x98,0x04,0xF9,
+ 0x1B,0xDF,0xB8,0xE3,0x77,0xCD,0x0E,0xC8,0x0D,0xDC,0x10,0x00,0x00,0x01,0x62,0x4E,
+ 0xBC,0xC7,0x43,0x00,0x00,0x04,0x03,0x00,0x46,0x30,0x44,0x02,0x20,0x3E,0xD3,0xFE,
+ 0xB4,0x45,0x97,0xCE,0xA3,0x05,0xC9,0x29,0x70,0x55,0x3B,0x77,0x9E,0xCC,0x4B,0x06,
+ 0xFD,0x76,0x21,0xD0,0x79,0x69,0xD6,0x60,0x01,0xBB,0xC7,0x43,0x5E,0x02,0x20,0x3D,
+ 0xB7,0x73,0x91,0x51,0xB7,0xAC,0x40,0xFB,0xA7,0x36,0xCF,0x10,0xE8,0x63,0x79,0xE6,
+ 0x06,0xEC,0xFA,0x60,0xFE,0x44,0x90,0x9A,0x53,0x26,0x04,0x27,0x1A,0x4B,0xD4,0x00,
+ 0x76,0x00,0x56,0x14,0x06,0x9A,0x2F,0xD7,0xC2,0xEC,0xD3,0xF5,0xE1,0xBD,0x44,0xB2,
+ 0x3E,0xC7,0x46,0x76,0xB9,0xBC,0x99,0x11,0x5C,0xC0,0xEF,0x94,0x98,0x55,0xD6,0x89,
+ 0xD0,0xDD,0x00,0x00,0x01,0x62,0x4E,0xBC,0xC8,0x6A,0x00,0x00,0x04,0x03,0x00,0x47,
+ 0x30,0x45,0x02,0x20,0x18,0xF4,0x40,0x99,0x83,0xA7,0x53,0x6D,0x11,0xBF,0x7C,0xA5,
+ 0x64,0x21,0x52,0xD0,0x7C,0xF5,0x96,0xCD,0xB0,0x56,0xAE,0x1E,0x13,0x9C,0xFC,0x08,
+ 0x3B,0x56,0x66,0x0F,0x02,0x21,0x00,0x98,0xBE,0xC7,0x01,0x8E,0x88,0xB7,0xB2,0xF9,
+ 0xC7,0xE6,0x08,0x46,0x10,0xEA,0x8D,0x01,0x12,0x0D,0x7D,0xA6,0xBA,0xA6,0xD3,0x1F,
+ 0xEC,0xA3,0xD6,0x7A,0xE4,0x85,0x89,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,
+ 0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x6A,0x52,0xE8,0xB3,0x70,
+ 0x25,0xD9,0x95,0x87,0xC4,0xF3,0x14,0xB1,0x8C,0x29,0x6F,0x11,0xA0,0x6D,0xD7,0xE9,
+ 0x96,0xFA,0x06,0x57,0x4D,0x86,0xE8,0xD6,0x95,0xC0,0x3A,0x33,0xEC,0x67,0x6C,0x98,
+ 0xA2,0x99,0x09,0x28,0x16,0x18,0xEC,0x21,0xED,0x93,0xEF,0xAB,0x69,0x5F,0x98,0x9C,
+ 0x62,0x21,0x10,0xEB,0x1C,0xC4,0x20,0x5E,0x3E,0x6F,0x80,0x03,0xC9,0xC5,0xD5,0x2F,
+ 0xDA,0x8C,0x86,0x40,0x59,0x71,0x34,0xEE,0xCF,0x54,0x83,0xE1,0x3E,0x11,0x1D,0x12,
+ 0x66,0x3D,0x16,0x7F,0xD8,0x1B,0x42,0x35,0x50,0xA6,0xD6,0xE6,0x6B,0x32,0xDE,0xE2,
+ 0xD9,0x01,0xD7,0xB1,0xF5,0xE8,0x72,0x44,0x03,0xB5,0xFA,0x19,0x0D,0xF2,0x8D,0x0B,
+ 0x75,0xBA,0xD9,0x39,0xFF,0x73,0xF2,0xA0,0xD6,0x49,0xEF,0x9E,0xE9,0x23,0x5D,0xED,
+ 0xC5,0x08,0x69,0x10,0xF7,0xF0,0x0B,0x8D,0x80,0xB6,0x43,0xE4,0x2A,0xEC,0x92,0xCF,
+ 0x22,0xCA,0xAF,0x1A,0x8A,0x16,0xC7,0xC5,0x88,0xB7,0xCA,0xC6,0x19,0x1A,0xD1,0xFF,
+ 0x13,0x93,0x56,0x29,0x1A,0x48,0xA8,0x92,0x21,0xE5,0x7F,0x3E,0x4C,0xB4,0x89,0xD6,
+ 0x08,0xF0,0xB0,0x4B,0x22,0x7C,0x1F,0xEC,0xE7,0x09,0x5D,0x23,0xE2,0xD1,0xB8,0xA3,
+ 0xDF,0xEC,0x2D,0x87,0x1F,0xCD,0x58,0x1C,0xDD,0x09,0xE8,0xB5,0xD4,0x30,0x1E,0x2A,
+ 0x4D,0xA3,0xA1,0x84,0x43,0xD7,0x4A,0x7B,0x15,0x8E,0xEB,0xE2,0x53,0x2B,0x12,0xCB,
+ 0xFB,0xF2,0x61,0x7E,0x92,0x0B,0x87,0x07,0x1C,0x8D,0x0A,0xED,0x62,0x10,0x27,0xE3,
+ 0xDB,0xC4,0x65,0xDE,0x57,0xFB,0x6D,0x49,0xC8,0x7A,0xF8,
+};
+
+/* subject:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert SHA2 Extended Validation Server CA */
+/* issuer :/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA */
+static unsigned char _exception_cert1[]={
+ 0x30,0x82,0x04,0xB6,0x30,0x82,0x03,0x9E,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x0C,
+ 0x79,0xA9,0x44,0xB0,0x8C,0x11,0x95,0x20,0x92,0x61,0x5F,0xE2,0x6B,0x1D,0x83,0x30,
+ 0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,0x6C,
+ 0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,
+ 0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,
+ 0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77,
+ 0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31,
+ 0x2B,0x30,0x29,0x06,0x03,0x55,0x04,0x03,0x13,0x22,0x44,0x69,0x67,0x69,0x43,0x65,
+ 0x72,0x74,0x20,0x48,0x69,0x67,0x68,0x20,0x41,0x73,0x73,0x75,0x72,0x61,0x6E,0x63,
+ 0x65,0x20,0x45,0x56,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x30,0x1E,0x17,0x0D,
+ 0x31,0x33,0x31,0x30,0x32,0x32,0x31,0x32,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32,
+ 0x38,0x31,0x30,0x32,0x32,0x31,0x32,0x30,0x30,0x30,0x30,0x5A,0x30,0x75,0x31,0x0B,
+ 0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,0x13,0x06,
+ 0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x49,
+ 0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77,0x77,0x77,
+ 0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31,0x34,0x30,
+ 0x32,0x06,0x03,0x55,0x04,0x03,0x13,0x2B,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,
+ 0x20,0x53,0x48,0x41,0x32,0x20,0x45,0x78,0x74,0x65,0x6E,0x64,0x65,0x64,0x20,0x56,
+ 0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x53,0x65,0x72,0x76,0x65,0x72,
+ 0x20,0x43,0x41,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,
+ 0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,
+ 0x82,0x01,0x01,0x00,0xD7,0x53,0xA4,0x04,0x51,0xF8,0x99,0xA6,0x16,0x48,0x4B,0x67,
+ 0x27,0xAA,0x93,0x49,0xD0,0x39,0xED,0x0C,0xB0,0xB0,0x00,0x87,0xF1,0x67,0x28,0x86,
+ 0x85,0x8C,0x8E,0x63,0xDA,0xBC,0xB1,0x40,0x38,0xE2,0xD3,0xF5,0xEC,0xA5,0x05,0x18,
+ 0xB8,0x3D,0x3E,0xC5,0x99,0x17,0x32,0xEC,0x18,0x8C,0xFA,0xF1,0x0C,0xA6,0x64,0x21,
+ 0x85,0xCB,0x07,0x10,0x34,0xB0,0x52,0x88,0x2B,0x1F,0x68,0x9B,0xD2,0xB1,0x8F,0x12,
+ 0xB0,0xB3,0xD2,0xE7,0x88,0x1F,0x1F,0xEF,0x38,0x77,0x54,0x53,0x5F,0x80,0x79,0x3F,
+ 0x2E,0x1A,0xAA,0xA8,0x1E,0x4B,0x2B,0x0D,0xAB,0xB7,0x63,0xB9,0x35,0xB7,0x7D,0x14,
+ 0xBC,0x59,0x4B,0xDF,0x51,0x4A,0xD2,0xA1,0xE2,0x0C,0xE2,0x90,0x82,0x87,0x6A,0xAE,
+ 0xEA,0xD7,0x64,0xD6,0x98,0x55,0xE8,0xFD,0xAF,0x1A,0x50,0x6C,0x54,0xBC,0x11,0xF2,
+ 0xFD,0x4A,0xF2,0x9D,0xBB,0x7F,0x0E,0xF4,0xD5,0xBE,0x8E,0x16,0x89,0x12,0x55,0xD8,
+ 0xC0,0x71,0x34,0xEE,0xF6,0xDC,0x2D,0xEC,0xC4,0x87,0x25,0x86,0x8D,0xD8,0x21,0xE4,
+ 0xB0,0x4D,0x0C,0x89,0xDC,0x39,0x26,0x17,0xDD,0xF6,0xD7,0x94,0x85,0xD8,0x04,0x21,
+ 0x70,0x9D,0x6F,0x6F,0xFF,0x5C,0xBA,0x19,0xE1,0x45,0xCB,0x56,0x57,0x28,0x7E,0x1C,
+ 0x0D,0x41,0x57,0xAA,0xB7,0xB8,0x27,0xBB,0xB1,0xE4,0xFA,0x2A,0xEF,0x21,0x23,0x75,
+ 0x1A,0xAD,0x2D,0x9B,0x86,0x35,0x8C,0x9C,0x77,0xB5,0x73,0xAD,0xD8,0x94,0x2D,0xE4,
+ 0xF3,0x0C,0x9D,0xEE,0xC1,0x4E,0x62,0x7E,0x17,0xC0,0x71,0x9E,0x2C,0xDE,0xF1,0xF9,
+ 0x10,0x28,0x19,0x33,0x02,0x03,0x01,0x00,0x01,0xA3,0x82,0x01,0x49,0x30,0x82,0x01,
+ 0x45,0x30,0x12,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x08,0x30,0x06,0x01,
+ 0x01,0xFF,0x02,0x01,0x00,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,
+ 0x04,0x03,0x02,0x01,0x86,0x30,0x1D,0x06,0x03,0x55,0x1D,0x25,0x04,0x16,0x30,0x14,
+ 0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x01,0x06,0x08,0x2B,0x06,0x01,0x05,
+ 0x05,0x07,0x03,0x02,0x30,0x34,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x01,
+ 0x04,0x28,0x30,0x26,0x30,0x24,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,
+ 0x86,0x18,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x6F,0x63,0x73,0x70,0x2E,0x64,0x69,
+ 0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x30,0x4B,0x06,0x03,0x55,0x1D,
+ 0x1F,0x04,0x44,0x30,0x42,0x30,0x40,0xA0,0x3E,0xA0,0x3C,0x86,0x3A,0x68,0x74,0x74,
+ 0x70,0x3A,0x2F,0x2F,0x63,0x72,0x6C,0x34,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,
+ 0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x48,0x69,
+ 0x67,0x68,0x41,0x73,0x73,0x75,0x72,0x61,0x6E,0x63,0x65,0x45,0x56,0x52,0x6F,0x6F,
+ 0x74,0x43,0x41,0x2E,0x63,0x72,0x6C,0x30,0x3D,0x06,0x03,0x55,0x1D,0x20,0x04,0x36,
+ 0x30,0x34,0x30,0x32,0x06,0x04,0x55,0x1D,0x20,0x00,0x30,0x2A,0x30,0x28,0x06,0x08,
+ 0x2B,0x06,0x01,0x05,0x05,0x07,0x02,0x01,0x16,0x1C,0x68,0x74,0x74,0x70,0x73,0x3A,
+ 0x2F,0x2F,0x77,0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,
+ 0x6F,0x6D,0x2F,0x43,0x50,0x53,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,
+ 0x14,0x3D,0xD3,0x50,0xA5,0xD6,0xA0,0xAD,0xEE,0xF3,0x4A,0x60,0x0A,0x65,0xD3,0x21,
+ 0xD4,0xF8,0xF8,0xD6,0x0F,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,
+ 0x80,0x14,0xB1,0x3E,0xC3,0x69,0x03,0xF8,0xBF,0x47,0x01,0xD4,0x98,0x26,0x1A,0x08,
+ 0x02,0xEF,0x63,0x64,0x2B,0xC3,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,
+ 0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x9D,0xB6,0xD0,0x90,0x86,0xE1,
+ 0x86,0x02,0xED,0xC5,0xA0,0xF0,0x34,0x1C,0x74,0xC1,0x8D,0x76,0xCC,0x86,0x0A,0xA8,
+ 0xF0,0x4A,0x8A,0x42,0xD6,0x3F,0xC8,0xA9,0x4D,0xAD,0x7C,0x08,0xAD,0xE6,0xB6,0x50,
+ 0xB8,0xA2,0x1A,0x4D,0x88,0x07,0xB1,0x29,0x21,0xDC,0xE7,0xDA,0xC6,0x3C,0x21,0xE0,
+ 0xE3,0x11,0x49,0x70,0xAC,0x7A,0x1D,0x01,0xA4,0xCA,0x11,0x3A,0x57,0xAB,0x7D,0x57,
+ 0x2A,0x40,0x74,0xFD,0xD3,0x1D,0x85,0x18,0x50,0xDF,0x57,0x47,0x75,0xA1,0x7D,0x55,
+ 0x20,0x2E,0x47,0x37,0x50,0x72,0x8C,0x7F,0x82,0x1B,0xD2,0x62,0x8F,0x2D,0x03,0x5A,
+ 0xDA,0xC3,0xC8,0xA1,0xCE,0x2C,0x52,0xA2,0x00,0x63,0xEB,0x73,0xBA,0x71,0xC8,0x49,
+ 0x27,0x23,0x97,0x64,0x85,0x9E,0x38,0x0E,0xAD,0x63,0x68,0x3C,0xBA,0x52,0x81,0x58,
+ 0x79,0xA3,0x2C,0x0C,0xDF,0xDE,0x6D,0xEB,0x31,0xF2,0xBA,0xA0,0x7C,0x6C,0xF1,0x2C,
+ 0xD4,0xE1,0xBD,0x77,0x84,0x37,0x03,0xCE,0x32,0xB5,0xC8,0x9A,0x81,0x1A,0x4A,0x92,
+ 0x4E,0x3B,0x46,0x9A,0x85,0xFE,0x83,0xA2,0xF9,0x9E,0x8C,0xA3,0xCC,0x0D,0x5E,0xB3,
+ 0x3D,0xCF,0x04,0x78,0x8F,0x14,0x14,0x7B,0x32,0x9C,0xC7,0x00,0xA6,0x5C,0xC4,0xB5,
+ 0xA1,0x55,0x8D,0x5A,0x56,0x68,0xA4,0x22,0x70,0xAA,0x3C,0x81,0x71,0xD9,0x9D,0xA8,
+ 0x45,0x3B,0xF4,0xE5,0xF6,0xA2,0x51,0xDD,0xC7,0x7B,0x62,0xE8,0x6F,0x0C,0x74,0xEB,
+ 0xB8,0xDA,0xF8,0xBF,0x87,0x0D,0x79,0x50,0x91,0x90,0x9B,0x18,0x3B,0x91,0x59,0x27,
+ 0xF1,0x35,0x28,0x13,0xAB,0x26,0x7E,0xD5,0xF7,0x7A,
+};
+
+/* subject:/CN=self-signed.ssltest.apple.com/C=US */
+/* issuer :/CN=self-signed.ssltest.apple.com/C=US */
+static unsigned char _exception_self_signed[]={
+ 0x30,0x82,0x03,0x0F,0x30,0x82,0x01,0xF7,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01,
+ 0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,
+ 0x35,0x31,0x26,0x30,0x24,0x06,0x03,0x55,0x04,0x03,0x0C,0x1D,0x73,0x65,0x6C,0x66,
+ 0x2D,0x73,0x69,0x67,0x6E,0x65,0x64,0x2E,0x73,0x73,0x6C,0x74,0x65,0x73,0x74,0x2E,
+ 0x61,0x70,0x70,0x6C,0x65,0x2E,0x63,0x6F,0x6D,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,
+ 0x04,0x06,0x13,0x02,0x55,0x53,0x30,0x1E,0x17,0x0D,0x31,0x36,0x30,0x36,0x30,0x37,
+ 0x32,0x31,0x35,0x33,0x30,0x38,0x5A,0x17,0x0D,0x31,0x37,0x30,0x36,0x30,0x37,0x32,
+ 0x31,0x35,0x33,0x30,0x38,0x5A,0x30,0x35,0x31,0x26,0x30,0x24,0x06,0x03,0x55,0x04,
+ 0x03,0x0C,0x1D,0x73,0x65,0x6C,0x66,0x2D,0x73,0x69,0x67,0x6E,0x65,0x64,0x2E,0x73,
+ 0x73,0x6C,0x74,0x65,0x73,0x74,0x2E,0x61,0x70,0x70,0x6C,0x65,0x2E,0x63,0x6F,0x6D,
+ 0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x30,0x82,0x01,
+ 0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,
+ 0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xCC,0x72,
+ 0x7D,0x09,0x36,0x5A,0x6A,0xED,0xC1,0x7A,0x2C,0xF4,0x7C,0x58,0x63,0x05,0x3E,0x91,
+ 0x68,0x55,0xB1,0x2A,0x5D,0x57,0xF3,0xA4,0xA7,0x80,0x05,0x41,0x74,0xB2,0xAD,0x5A,
+ 0x7F,0x38,0xF6,0xF7,0xFD,0xF9,0x64,0x4D,0xDE,0xF9,0x7A,0xD3,0x8C,0x78,0xE9,0x71,
+ 0xCF,0x1D,0x3E,0xF0,0xDB,0x12,0x48,0x74,0x22,0xA8,0x1F,0x3F,0xB9,0xDD,0xB0,0xAD,
+ 0x8C,0x10,0x64,0x05,0x0E,0xE2,0x59,0x9A,0xEB,0x3F,0xBF,0xA9,0x48,0x07,0xD9,0x2C,
+ 0x07,0x44,0x70,0x14,0x16,0x56,0x9C,0x73,0x01,0x2E,0x0B,0xF1,0x2A,0x9F,0x1C,0xC6,
+ 0x78,0x56,0xB7,0x0B,0xDA,0xA6,0xE6,0x99,0x87,0x2D,0x49,0xFB,0xF0,0x47,0x22,0xA6,
+ 0x8B,0xF0,0x02,0x37,0x31,0xD0,0x34,0x9F,0x43,0xD1,0x24,0x49,0x94,0x7F,0xFD,0x48,
+ 0x9C,0xBA,0x5D,0x6B,0xD4,0xF9,0x9E,0xB5,0x18,0xE4,0xB2,0x06,0x46,0xC3,0xD9,0xE7,
+ 0x80,0xD8,0x61,0xA9,0x09,0x5E,0xBA,0x2E,0x58,0x56,0xAE,0x37,0x31,0x6E,0x87,0x98,
+ 0xD5,0xC9,0x2B,0x31,0x5C,0x40,0x01,0xDF,0xD5,0x63,0x9E,0x05,0x18,0x21,0x53,0x70,
+ 0x62,0x36,0x44,0xCD,0x02,0xC0,0xCC,0x6A,0x58,0xC6,0xF6,0xA4,0xDC,0x89,0x94,0xBD,
+ 0x4E,0xC4,0xEE,0xEE,0x40,0x31,0x59,0xC3,0x43,0xAD,0x34,0x30,0xDE,0xA9,0xA7,0x0D,
+ 0x85,0xF7,0x96,0x8C,0x45,0xC1,0x6E,0x85,0x39,0x97,0xA6,0x4F,0xEA,0xE8,0x2F,0x01,
+ 0x3D,0xC0,0x3B,0x34,0x9F,0x8F,0xCB,0xD6,0x22,0x79,0x2C,0x8C,0x8C,0xE6,0xBB,0x1F,
+ 0x89,0x87,0x93,0x3B,0x39,0x4E,0x64,0x7D,0xDA,0x4D,0x52,0x4C,0x97,0xE5,0x02,0x03,
+ 0x01,0x00,0x01,0xA3,0x2A,0x30,0x28,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,
+ 0xFF,0x04,0x04,0x03,0x02,0x07,0x80,0x30,0x16,0x06,0x03,0x55,0x1D,0x25,0x01,0x01,
+ 0xFF,0x04,0x0C,0x30,0x0A,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x01,0x30,
+ 0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82,
+ 0x01,0x01,0x00,0x36,0x06,0xC9,0xE6,0x98,0xC2,0x84,0x1D,0x13,0x1E,0x54,0x35,0x6D,
+ 0xE5,0xCB,0xC5,0xFD,0xD9,0x54,0x45,0x83,0x53,0xB3,0x3B,0xE7,0x30,0x6F,0xAE,0xEA,
+ 0x63,0x3F,0xA8,0xFA,0xD9,0x6D,0x0F,0x7D,0xD4,0xB6,0x28,0x66,0xF9,0x57,0x87,0x3E,
+ 0x57,0x27,0xB6,0x9A,0x56,0xAE,0xD7,0xE0,0x11,0x20,0x71,0xC1,0xEA,0xF6,0xED,0x74,
+ 0x1A,0x5A,0xB1,0x74,0x6C,0xBE,0xAC,0x0E,0x3C,0xD9,0x3E,0xEC,0x17,0x6E,0xF0,0x69,
+ 0xC9,0x4D,0xD2,0x7E,0xAE,0x8B,0x01,0xCC,0x1A,0x23,0x7C,0x58,0x07,0x30,0xE4,0x2A,
+ 0x12,0xE8,0xA0,0x25,0x65,0x66,0xB5,0xC7,0x5D,0xD8,0x47,0xDF,0xD7,0x51,0xBC,0xA2,
+ 0xAA,0xF0,0x2F,0xB5,0x9E,0x20,0x6D,0x1F,0x84,0x00,0xF0,0xD0,0xB8,0x42,0x6A,0x9A,
+ 0xE7,0xCA,0x7B,0xE5,0x39,0x09,0x91,0xBF,0xCB,0x4D,0x7A,0x32,0x1E,0x00,0x6E,0xE5,
+ 0xF7,0x44,0x80,0x82,0x38,0x53,0x64,0xB7,0x26,0x81,0xCB,0xCE,0xA1,0xAF,0x0C,0x67,
+ 0x32,0xC6,0xE4,0x5D,0x09,0x7B,0x37,0xD7,0xC8,0x43,0x44,0xEF,0xC6,0xF8,0x72,0xFF,
+ 0x65,0xD4,0x39,0x3D,0xEC,0x72,0xA5,0x28,0xFF,0x70,0x47,0x38,0xA3,0xC7,0xCC,0x5E,
+ 0x0F,0xFF,0x43,0x83,0x78,0x49,0x68,0x90,0x48,0x89,0xAD,0xE1,0x2E,0xFA,0x8F,0x59,
+ 0xB6,0x08,0x2A,0x72,0x2F,0x52,0x3F,0x73,0x84,0xCA,0xD8,0x18,0x6C,0xDA,0xA3,0x2E,
+ 0xF2,0xD7,0x4C,0x21,0xD9,0xF8,0xB1,0x86,0xE9,0x35,0x78,0xE4,0x4F,0xD0,0x93,0x11,
+ 0x8F,0xF4,0xB1,0x17,0x4F,0xDE,0xAC,0xBD,0xA9,0xBC,0x94,0xFC,0x2E,0x7D,0xF9,0x05,
+ 0x26,0x90,0xF1,
+};
+
+#endif /* _TRUSTTESTS_EXCEPTION_TESTS_H_ */
CFReleaseNull(error);
}
+- (void) test_revocation_checked_via_cache {
+ if (!ping_host("ocsp.digicert.com")) {
+ XCTAssert(false, "Unable to contact required network resource");
+ return;
+ }
+
+ SecCertificateRef leaf = NULL, subCA = NULL, root = NULL;
+ SecPolicyRef sslPolicy = NULL, ocspPolicy = NULL;
+ SecTrustRef trust = NULL;
+ CFArrayRef certs = NULL, anchors = NULL, policies = NULL;
+ CFDateRef verifyDate = NULL;
+ CFErrorRef error = NULL;
+
+ leaf = SecCertificateCreateWithBytes(NULL, _ocsp_c0, sizeof(_ocsp_c0));
+ subCA = SecCertificateCreateWithBytes(NULL, _ocsp_c1, sizeof(_ocsp_c1));
+ root = SecCertificateCreateWithBytes(NULL, _ocsp_c2, sizeof(_ocsp_c2));
+
+ sslPolicy = SecPolicyCreateSSL(true, CFSTR("www.apple.com"));
+ ocspPolicy = SecPolicyCreateRevocation(kSecRevocationOCSPMethod);
+
+ const void *v_certs[] = { leaf, subCA };
+ const void *v_anchors[] = { root };
+ const void *v_policies[] = { sslPolicy, ocspPolicy };
+
+ certs = CFArrayCreate(NULL, v_certs, 2, &kCFTypeArrayCallBacks);
+ policies = CFArrayCreate(NULL, v_policies, 2, &kCFTypeArrayCallBacks);
+ require_noerr_action(SecTrustCreateWithCertificates(certs, policies, &trust), errOut, fail("failed to create trust object"));
+
+ anchors = CFArrayCreate(NULL, v_anchors, 1, &kCFTypeArrayCallBacks);
+ require_noerr_action(SecTrustSetAnchorCertificates(trust, anchors), errOut, fail("failed to set anchors"));
+
+ verifyDate = CFDateCreate(NULL, 577000000.0); // April 14, 2019 at 10:46:40 PM PDT
+ require_noerr_action(SecTrustSetVerifyDate(trust, verifyDate), errOut, fail("failed to set verify date"));
+
+ is(SecTrustEvaluateWithError(trust, &error), true, "valid cert failed");
+
+ /* Set no fetch allowed, so we're relying on the cached response from above */
+ require_noerr_action(SecTrustSetNetworkFetchAllowed(trust, false), errOut, fail("failed to set network fetch disallowed"));
+
+ /* Evaluate trust. Cached response should tell us that it's revoked. */
+ is(SecTrustEvaluateWithError(trust, &error), true, "valid cert failed");
+
+ /* Verify that the results dictionary contains the kSecTrustRevocationChecked key for a valid cert where revocation checked */
+ CFDictionaryRef result = SecTrustCopyResult(trust);
+ isnt(result, NULL, "failed to copy result dictionary");
+ if (result) {
+ is(CFDictionaryGetValue(result, kSecTrustRevocationChecked), kCFBooleanTrue, "expected revocation checked flag");
+ }
+ CFReleaseNull(result);
+
+errOut:
+ CFReleaseNull(leaf);
+ CFReleaseNull(subCA);
+ CFReleaseNull(root);
+ CFReleaseNull(ocspPolicy);
+ CFReleaseNull(sslPolicy);
+ CFReleaseNull(trust);
+ CFReleaseNull(certs);
+ CFReleaseNull(anchors);
+ CFReleaseNull(policies);
+ CFReleaseNull(verifyDate);
+ CFReleaseNull(error);
+}
+
#else /* TARGET_OS_WATCH || TARGET_OS_BRIDGE */
- (void)testNoNetworking
{
CFReleaseNull(verifyDate);
CFReleaseNull(error);
}
-#endif
+#endif /* !TARGET_OS_WATCH && !TARGET_OS_BRIDGE */
+
+#if !TARGET_OS_BRIDGE
+/* bridgeOS doesn't use Valid */
+- (NSNumber *)runRevocationCheckNoNetwork:(SecCertificateRef)leaf
+ subCA:(SecCertificateRef)subCA
+{
+ CFArrayRef anchors = NULL;
+ SecPolicyRef smimePolicy = NULL, revocationPolicy = NULL;
+ CFArrayRef certs = NULL;
+ SecTrustRef trust = NULL;
+ CFDateRef date = NULL;
+ CFErrorRef error = NULL;
+ NSArray *policies = nil;
+ NSDictionary *result = nil;
+ NSNumber *revocationChecked = nil;
+
+ const void *v_certs[] = { leaf };
+ require_action(certs = CFArrayCreate(NULL, v_certs, array_size(v_certs), &kCFTypeArrayCallBacks), errOut,
+ fail("unable to create certificates array"));
+ require_action(anchors = CFArrayCreate(NULL, (const void **)&subCA, 1, &kCFTypeArrayCallBacks), errOut,
+ fail("unable to create anchors array"));
+
+ require_action(smimePolicy = SecPolicyCreateSMIME(kSecSignSMIMEUsage, NULL), errOut, fail("unable to create policy"));
+ revocationPolicy = SecPolicyCreateRevocation(kSecRevocationUseAnyAvailableMethod | kSecRevocationCheckIfTrusted);
+ policies = @[(__bridge id)smimePolicy, (__bridge id)revocationPolicy];
+ ok_status(SecTrustCreateWithCertificates(certs, (__bridge CFArrayRef)policies, &trust), "failed to create trust");
+ ok_status(SecTrustSetNetworkFetchAllowed(trust, false), "SecTrustSetNetworkFetchAllowed failed");
+
+ require_noerr_action(SecTrustSetAnchorCertificates(trust, anchors), errOut,
+ fail("unable to set anchors"));
+ ok(SecTrustEvaluateWithError(trust, &error), "Not trusted");
+ result = CFBridgingRelease(SecTrustCopyResult(trust));
+ revocationChecked = result[(__bridge NSString *)kSecTrustRevocationChecked];
+
+errOut:
+ CFReleaseNull(anchors);
+ CFReleaseNull(smimePolicy);
+ CFReleaseNull(revocationPolicy);
+ CFReleaseNull(certs);
+ CFReleaseNull(trust);
+ CFReleaseNull(date);
+ CFReleaseNull(error);
+
+ return revocationChecked;
+}
+
+- (void) test_revocation_checked_via_valid {
+ SecCertificateRef leaf = NULL, subCA = NULL;
+ NSNumber *revocationChecked = NULL;
+
+ require_action(leaf = SecCertificateCreateWithBytes(NULL, _leaf_sha256_valid_cav2_complete_ok1, sizeof(_leaf_sha256_valid_cav2_complete_ok1)), errOut,
+ fail("unable to create cert"));
+ require_action(subCA = SecCertificateCreateWithBytes(NULL, _ca_sha256_valid_cav2_complete, sizeof(_ca_sha256_valid_cav2_complete)), errOut, fail("unable to create cert"));
+
+ revocationChecked = [self runRevocationCheckNoNetwork:leaf
+ subCA:subCA];
+ XCTAssert(revocationChecked != NULL, "kSecTrustRevocationChecked is not in the result dictionary");
+
+errOut:
+ CFReleaseNull(leaf);
+ CFReleaseNull(subCA);
+}
+
+- (void) test_revocation_not_checked_no_network {
+ /* The intermediate does not have the noCAv2 flag and is "probably not revoked,", so
+ kSecTrustRevocationChecked should not be in the results dictionary */
+ SecCertificateRef leaf = NULL, subCA = NULL;
+ NSNumber *revocationChecked = NULL;
+
+ require_action(leaf = SecCertificateCreateWithBytes(NULL, _leaf_serial_invalid_incomplete_ok1, sizeof(_leaf_serial_invalid_incomplete_ok1)), errOut,
+ fail("unable to create cert"));
+ require_action(subCA = SecCertificateCreateWithBytes(NULL, _ca_serial_invalid_incomplete, sizeof(_ca_serial_invalid_incomplete)), errOut, fail("unable to create cert"));
+
+ revocationChecked = [self runRevocationCheckNoNetwork:leaf
+ subCA:subCA];
+ XCTAssert(revocationChecked == NULL, "kSecTrustRevocationChecked is in the result dictionary");
+
+errOut:
+ CFReleaseNull(leaf);
+ CFReleaseNull(subCA);
+}
+#endif /* !TARGET_OS_BRIDGE */
/* bridgeOS and watchOS do not support networked OCSP but do support stapling */
- (void) test_stapled_revoked_response {
0x44,0xc9,0x27,0x73,0x07,0xee,0x82,0xe5,0x4e,0xf5,0x70
};
+uint8_t _leaf_sha256_valid_cav2_complete_ok1[] = {
+ 0x30,0x82,0x04,0x7b,0x30,0x82,0x03,0x63,0xa0,0x03,0x02,0x01,0x02,0x02,0x05,0x00,
+ 0xa4,0x37,0x10,0xc8,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,
+ 0x0b,0x05,0x00,0x30,0x81,0x8f,0x31,0x26,0x30,0x24,0x06,0x03,0x55,0x04,0x03,0x0c,
+ 0x1d,0x43,0x41,0x20,0x73,0x68,0x61,0x32,0x35,0x36,0x20,0x76,0x61,0x6c,0x69,0x64,
+ 0x20,0x63,0x61,0x76,0x32,0x20,0x63,0x6f,0x6d,0x70,0x6c,0x65,0x74,0x65,0x31,0x22,
+ 0x30,0x20,0x06,0x03,0x55,0x04,0x0b,0x0c,0x19,0x56,0x61,0x6c,0x69,0x64,0x20,0x50,
+ 0x72,0x6f,0x6a,0x65,0x63,0x74,0x20,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x73,0x20,
+ 0x56,0x32,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0a,0x0c,0x0a,0x41,0x70,0x70,
+ 0x6c,0x65,0x20,0x49,0x6e,0x63,0x2e,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x07,
+ 0x0c,0x09,0x43,0x75,0x70,0x65,0x72,0x74,0x69,0x6e,0x6f,0x31,0x0b,0x30,0x09,0x06,
+ 0x03,0x55,0x04,0x08,0x0c,0x02,0x43,0x41,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,
+ 0x06,0x13,0x02,0x55,0x53,0x30,0x20,0x17,0x0d,0x31,0x38,0x30,0x36,0x31,0x32,0x32,
+ 0x33,0x32,0x38,0x32,0x32,0x5a,0x18,0x0f,0x32,0x31,0x31,0x38,0x30,0x35,0x31,0x39,
+ 0x32,0x33,0x32,0x38,0x32,0x32,0x5a,0x30,0x81,0x95,0x31,0x2c,0x30,0x2a,0x06,0x03,
+ 0x55,0x04,0x03,0x0c,0x23,0x4c,0x65,0x61,0x66,0x20,0x73,0x68,0x61,0x32,0x35,0x36,
+ 0x20,0x76,0x61,0x6c,0x69,0x64,0x20,0x63,0x61,0x76,0x32,0x20,0x63,0x6f,0x6d,0x70,
+ 0x6c,0x65,0x74,0x65,0x20,0x6f,0x6b,0x31,0x31,0x22,0x30,0x20,0x06,0x03,0x55,0x04,
+ 0x0b,0x0c,0x19,0x56,0x61,0x6c,0x69,0x64,0x20,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74,
+ 0x20,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x73,0x20,0x56,0x32,0x31,0x13,0x30,0x11,
+ 0x06,0x03,0x55,0x04,0x0a,0x0c,0x0a,0x41,0x70,0x70,0x6c,0x65,0x20,0x49,0x6e,0x63,
+ 0x2e,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x0c,0x09,0x43,0x75,0x70,0x65,
+ 0x72,0x74,0x69,0x6e,0x6f,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x08,0x0c,0x02,
+ 0x43,0x41,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x30,
+ 0x82,0x01,0x22,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01,
+ 0x05,0x00,0x03,0x82,0x01,0x0f,0x00,0x30,0x82,0x01,0x0a,0x02,0x82,0x01,0x01,0x00,
+ 0xc3,0x8b,0xfa,0x6c,0xa7,0x18,0xd3,0xbc,0x41,0x58,0x47,0x9c,0xa0,0x5a,0x21,0xb0,
+ 0xaa,0x28,0xd3,0x2b,0xb3,0x57,0xdf,0x5b,0x0f,0x18,0xa8,0xa9,0x5a,0x01,0x9a,0x15,
+ 0x0b,0x62,0x7f,0x51,0x7d,0x85,0x98,0x48,0x74,0xdf,0xa4,0xb7,0xf2,0x4c,0x88,0x10,
+ 0xe0,0x41,0x31,0x77,0x64,0x2f,0xe2,0x47,0x3a,0x9b,0xf9,0xf4,0xf3,0xf9,0x3f,0x09,
+ 0xbc,0x33,0xbb,0xbc,0xf9,0x75,0x7c,0xc8,0x4c,0x95,0xd2,0xa2,0xc3,0xc0,0x0a,0x5f,
+ 0xf4,0x3b,0x50,0x95,0x53,0xf8,0xf2,0x77,0xce,0x02,0x06,0xbf,0x9e,0x74,0x2e,0x58,
+ 0x6e,0xff,0x29,0xb6,0x80,0x6a,0xe4,0x07,0xaf,0x41,0x5b,0x91,0x49,0x30,0x3f,0x5b,
+ 0x1b,0x2d,0xab,0x6a,0xf5,0x48,0x44,0x87,0xf0,0xaf,0xa4,0xb3,0x41,0x28,0x9f,0xf6,
+ 0xae,0x35,0x0c,0xf6,0x09,0x17,0x34,0x55,0x35,0x50,0xf1,0x57,0xe7,0x6d,0x18,0x24,
+ 0x35,0x40,0xc3,0xf5,0x41,0xe7,0x9d,0x76,0x33,0xcc,0xaa,0x85,0x3f,0x80,0x6e,0x7f,
+ 0x55,0x62,0xf3,0x6d,0x61,0x26,0x90,0x4f,0x7d,0x73,0xc1,0x0b,0xae,0xf5,0xf8,0x04,
+ 0x1a,0xbb,0x88,0x50,0x79,0xfd,0x59,0xe3,0xfc,0x58,0x4b,0xb6,0x20,0x5f,0x26,0xc6,
+ 0x50,0x31,0x06,0x84,0x8a,0xa9,0xd4,0x1d,0x9b,0x7a,0x55,0x2e,0x52,0xfe,0x5b,0x95,
+ 0xdf,0x0b,0x03,0xf3,0xcc,0xca,0x9b,0xb5,0x75,0x66,0x16,0x67,0x11,0x63,0x39,0xf9,
+ 0x77,0x95,0x30,0x0a,0x74,0x9c,0xd4,0xc8,0x81,0x75,0xd3,0x3f,0x59,0x8a,0xea,0x17,
+ 0x06,0xb3,0xc1,0x20,0x81,0xef,0xbe,0xde,0x91,0x44,0x7c,0x6a,0x49,0xae,0xf2,0x25,
+ 0x02,0x03,0x01,0x00,0x01,0xa3,0x81,0xd3,0x30,0x81,0xd0,0x30,0x0e,0x06,0x03,0x55,
+ 0x1d,0x0f,0x01,0x01,0xff,0x04,0x04,0x03,0x02,0x07,0x80,0x30,0x0c,0x06,0x03,0x55,
+ 0x1d,0x13,0x01,0x01,0xff,0x04,0x02,0x30,0x00,0x30,0x1f,0x06,0x03,0x55,0x1d,0x23,
+ 0x04,0x18,0x30,0x16,0x80,0x14,0x39,0xbb,0x91,0xb5,0x14,0x54,0x76,0x0c,0x1c,0x0c,
+ 0xf4,0x4b,0xbc,0xd9,0xf6,0x51,0xc8,0xe2,0x03,0xfc,0x30,0x54,0x06,0x03,0x55,0x1d,
+ 0x1f,0x04,0x4d,0x30,0x4b,0x30,0x49,0xa0,0x47,0xa0,0x45,0x86,0x43,0x68,0x74,0x74,
+ 0x70,0x3a,0x2f,0x2f,0x76,0x61,0x6c,0x69,0x64,0x74,0x65,0x73,0x74,0x2e,0x61,0x70,
+ 0x70,0x6c,0x65,0x2e,0x67,0x65,0x6f,0x66,0x66,0x6b,0x2e,0x6e,0x65,0x74,0x2f,0x76,
+ 0x32,0x2d,0x73,0x68,0x61,0x32,0x35,0x36,0x2d,0x76,0x61,0x6c,0x69,0x64,0x2d,0x63,
+ 0x61,0x76,0x32,0x2d,0x63,0x6f,0x6d,0x70,0x6c,0x65,0x74,0x65,0x2e,0x63,0x72,0x6c,
+ 0x30,0x39,0x06,0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x01,0x01,0x04,0x2d,0x30,0x2b,
+ 0x30,0x29,0x06,0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x86,0x1d,0x68,0x74,
+ 0x74,0x70,0x3a,0x2f,0x2f,0x6b,0x6e,0x6f,0x77,0x6e,0x2d,0x61,0x6e,0x73,0x77,0x65,
+ 0x72,0x2d,0x6f,0x63,0x73,0x70,0x2f,0x6f,0x63,0x73,0x70,0x30,0x0d,0x06,0x09,0x2a,
+ 0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x02,
+ 0xc7,0x2c,0xec,0x13,0xcc,0x7c,0x54,0xc6,0xe5,0x00,0x83,0x9b,0xc4,0xe5,0x4e,0xc3,
+ 0xb1,0x3c,0x1d,0x95,0x2f,0x45,0xe7,0x75,0xaf,0x04,0x89,0xdb,0x05,0x21,0x13,0x12,
+ 0x75,0x1a,0x52,0xc1,0x31,0x74,0x89,0x87,0xd4,0xfa,0xea,0x6b,0x6f,0x61,0xb1,0xb1,
+ 0x21,0x49,0xd7,0x25,0xfd,0x31,0xc5,0xf7,0x31,0x1a,0xa5,0x9f,0x08,0xc3,0x16,0x84,
+ 0x3e,0x93,0x7a,0xdd,0xc4,0x28,0xe2,0xec,0x90,0x70,0x6b,0x58,0x8f,0xdf,0x1f,0x1d,
+ 0x46,0xf2,0x23,0xa2,0x2f,0x29,0xd4,0xe2,0xd1,0xa2,0x6e,0x20,0x94,0x91,0xa2,0x03,
+ 0x81,0x04,0xf9,0x4e,0x36,0x4b,0x75,0x2e,0x3a,0x13,0x0a,0x94,0xf8,0x29,0x79,0x0d,
+ 0xac,0x4a,0xff,0xb6,0x60,0x59,0xa4,0x10,0xea,0x39,0xdd,0x39,0x06,0x7c,0xbc,0x91,
+ 0x5f,0xbd,0xa7,0x8c,0x9b,0xf5,0xbd,0x17,0x80,0xde,0x8f,0x71,0x22,0x6d,0xef,0xfe,
+ 0x01,0x04,0xf2,0x7c,0xe1,0x35,0xd1,0x21,0x64,0x4d,0xd7,0xf0,0x43,0x26,0x0b,0x18,
+ 0x78,0xc0,0xe8,0xad,0x0e,0xc5,0x38,0xb5,0xcc,0xaf,0x04,0x7a,0x78,0xbc,0x1b,0x89,
+ 0x15,0x1f,0x1e,0x61,0x41,0xb6,0x46,0x8e,0x8b,0xd9,0xae,0x69,0x5c,0x9d,0x2b,0x83,
+ 0x71,0x25,0xd4,0x48,0x8d,0xfc,0x48,0x51,0x60,0xb3,0xa9,0xf7,0x53,0xf2,0x9b,0xa7,
+ 0x32,0x56,0x5f,0xea,0xd6,0x99,0xd0,0xef,0x2c,0x41,0x9d,0xe0,0x68,0x9f,0x8e,0xad,
+ 0x86,0x90,0xaa,0x24,0x5e,0xef,0xb0,0xc7,0x15,0xf2,0xce,0x2d,0x54,0x0c,0x8e,0x5e,
+ 0x3f,0x63,0xdc,0x74,0x35,0x0e,0x35,0x0d,0xfc,0x0b,0x45,0x56,0x5b,0xb1,0xe2
+};
+
+uint8_t _ca_sha256_valid_cav2_complete[] = {
+ 0x30,0x82,0x04,0x41,0x30,0x82,0x03,0x29,0xa0,0x03,0x02,0x01,0x02,0x02,0x05,0x00,
+ 0xd3,0x18,0x1d,0xb8,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,
+ 0x0b,0x05,0x00,0x30,0x81,0x87,0x31,0x1e,0x30,0x1c,0x06,0x03,0x55,0x04,0x03,0x0c,
+ 0x15,0x56,0x61,0x6c,0x69,0x64,0x20,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x20,0x52,
+ 0x6f,0x6f,0x74,0x20,0x56,0x32,0x31,0x22,0x30,0x20,0x06,0x03,0x55,0x04,0x0b,0x0c,
+ 0x19,0x56,0x61,0x6c,0x69,0x64,0x20,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74,0x20,0x54,
+ 0x65,0x73,0x74,0x20,0x43,0x41,0x73,0x20,0x56,0x32,0x31,0x13,0x30,0x11,0x06,0x03,
+ 0x55,0x04,0x0a,0x0c,0x0a,0x41,0x70,0x70,0x6c,0x65,0x20,0x49,0x6e,0x63,0x2e,0x31,
+ 0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x0c,0x09,0x43,0x75,0x70,0x65,0x72,0x74,
+ 0x69,0x6e,0x6f,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x08,0x0c,0x02,0x43,0x41,
+ 0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x30,0x20,0x17,
+ 0x0d,0x31,0x38,0x30,0x36,0x31,0x32,0x32,0x33,0x32,0x38,0x32,0x31,0x5a,0x18,0x0f,
+ 0x32,0x31,0x31,0x38,0x30,0x35,0x31,0x39,0x32,0x33,0x32,0x38,0x32,0x31,0x5a,0x30,
+ 0x81,0x8f,0x31,0x26,0x30,0x24,0x06,0x03,0x55,0x04,0x03,0x0c,0x1d,0x43,0x41,0x20,
+ 0x73,0x68,0x61,0x32,0x35,0x36,0x20,0x76,0x61,0x6c,0x69,0x64,0x20,0x63,0x61,0x76,
+ 0x32,0x20,0x63,0x6f,0x6d,0x70,0x6c,0x65,0x74,0x65,0x31,0x22,0x30,0x20,0x06,0x03,
+ 0x55,0x04,0x0b,0x0c,0x19,0x56,0x61,0x6c,0x69,0x64,0x20,0x50,0x72,0x6f,0x6a,0x65,
+ 0x63,0x74,0x20,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x73,0x20,0x56,0x32,0x31,0x13,
+ 0x30,0x11,0x06,0x03,0x55,0x04,0x0a,0x0c,0x0a,0x41,0x70,0x70,0x6c,0x65,0x20,0x49,
+ 0x6e,0x63,0x2e,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x0c,0x09,0x43,0x75,
+ 0x70,0x65,0x72,0x74,0x69,0x6e,0x6f,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x08,
+ 0x0c,0x02,0x43,0x41,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,
+ 0x53,0x30,0x82,0x01,0x22,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,
+ 0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0f,0x00,0x30,0x82,0x01,0x0a,0x02,0x82,0x01,
+ 0x01,0x00,0xb6,0xfb,0x13,0xd4,0x51,0x5a,0xe0,0x4e,0x35,0xb6,0xe0,0xc9,0xc3,0x78,
+ 0x5f,0xec,0x24,0x23,0xf2,0x40,0x0e,0xf8,0x6c,0x7f,0x9a,0xc8,0x71,0xd8,0x0a,0x3e,
+ 0xdb,0xf4,0x85,0xa5,0x11,0x0e,0x5e,0x89,0xd8,0xd9,0x86,0x5f,0xec,0x85,0xc1,0x36,
+ 0x4c,0xa5,0x40,0x8b,0x2a,0x66,0xe2,0x41,0x07,0x08,0x74,0xb2,0xe0,0x75,0x5e,0x7b,
+ 0xe0,0x10,0xf0,0x7f,0x69,0x44,0xa5,0xe5,0xfc,0xd4,0x00,0xc7,0x7e,0xe7,0xc5,0x50,
+ 0xcf,0xc0,0xe1,0xac,0x84,0xe9,0x08,0x2a,0xba,0x36,0xc4,0x60,0x37,0x07,0x91,0xe0,
+ 0xcd,0x55,0xef,0x3a,0xcf,0x73,0xb6,0xd6,0x60,0x46,0xa4,0x8a,0x65,0x6d,0xfc,0x77,
+ 0xa1,0xa0,0x12,0xef,0x20,0xc3,0xc3,0xf5,0x82,0x88,0x36,0x42,0x61,0xec,0x3d,0x3a,
+ 0xaf,0x73,0x55,0xda,0x4c,0xca,0x86,0x3d,0xbf,0x47,0x1d,0xdf,0x1f,0x41,0xbf,0x2a,
+ 0x97,0x84,0x85,0x13,0x1e,0x63,0xc5,0x7e,0x2a,0xb4,0xd4,0x56,0x2b,0xa1,0x93,0xc3,
+ 0xb8,0xb7,0x15,0x71,0xee,0xdc,0xe6,0x8b,0x08,0xe5,0xc9,0x04,0x9f,0xf6,0x87,0xd1,
+ 0xbf,0x78,0xf8,0x91,0x38,0x43,0x57,0x4b,0x8a,0x12,0x66,0xd6,0xae,0xf2,0xf1,0xa8,
+ 0x0b,0x1a,0x81,0xf6,0xfd,0x20,0x28,0x5f,0xaf,0xa8,0xe7,0xbc,0x90,0x09,0x85,0xd4,
+ 0xae,0x21,0x02,0x52,0x41,0xa8,0x03,0x61,0x89,0x11,0x86,0xf1,0xe7,0x49,0x3d,0x14,
+ 0x63,0x8a,0x69,0x5c,0x53,0x55,0xfe,0x25,0xbd,0xd0,0x2a,0xe8,0xe2,0x19,0xea,0x6f,
+ 0x20,0x6e,0xb3,0x85,0xa7,0x49,0xe7,0xcc,0x96,0xd8,0x3f,0x4d,0xad,0xe9,0x22,0x03,
+ 0x65,0x25,0x02,0x03,0x01,0x00,0x01,0xa3,0x81,0xa7,0x30,0x81,0xa4,0x30,0x0e,0x06,
+ 0x03,0x55,0x1d,0x0f,0x01,0x01,0xff,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x12,0x06,
+ 0x03,0x55,0x1d,0x13,0x01,0x01,0xff,0x04,0x08,0x30,0x06,0x01,0x01,0xff,0x02,0x01,
+ 0x00,0x30,0x1d,0x06,0x03,0x55,0x1d,0x0e,0x04,0x16,0x04,0x14,0x39,0xbb,0x91,0xb5,
+ 0x14,0x54,0x76,0x0c,0x1c,0x0c,0xf4,0x4b,0xbc,0xd9,0xf6,0x51,0xc8,0xe2,0x03,0xfc,
+ 0x30,0x1f,0x06,0x03,0x55,0x1d,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0xba,0x56,0xd8,
+ 0x57,0x66,0xbd,0xfc,0x5b,0xe2,0x10,0xf2,0x39,0xb3,0xaf,0xb2,0x72,0xed,0x55,0x0f,
+ 0x1c,0x30,0x3e,0x06,0x03,0x55,0x1d,0x1f,0x04,0x37,0x30,0x35,0x30,0x33,0xa0,0x31,
+ 0xa0,0x2f,0x86,0x2d,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x76,0x61,0x6c,0x69,0x64,
+ 0x74,0x65,0x73,0x74,0x2e,0x61,0x70,0x70,0x6c,0x65,0x2e,0x67,0x65,0x6f,0x66,0x66,
+ 0x6b,0x2e,0x6e,0x65,0x74,0x2f,0x76,0x32,0x2d,0x72,0x6f,0x6f,0x74,0x2e,0x63,0x72,
+ 0x6c,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,
+ 0x03,0x82,0x01,0x01,0x00,0x58,0xc7,0x67,0x96,0xb5,0x7a,0x36,0x26,0x63,0xe8,0xc1,
+ 0x7d,0xd9,0x0b,0x26,0xa4,0x2c,0x09,0xec,0x1e,0x67,0xfd,0x26,0xe4,0x5b,0x73,0x11,
+ 0x1d,0xfc,0xaf,0xa5,0x70,0x05,0x19,0xdb,0xb7,0x42,0x1b,0xd0,0x36,0x6f,0x8e,0x01,
+ 0xd3,0x19,0x72,0xff,0x36,0xa5,0x06,0x62,0x8b,0xd6,0x9b,0x40,0x24,0x54,0x48,0x4c,
+ 0x3f,0xeb,0x8d,0x36,0x9b,0x56,0x2e,0x27,0xf6,0x19,0xbf,0xae,0x07,0x20,0x7c,0x64,
+ 0x79,0xe3,0x4e,0x1d,0x44,0x26,0x79,0x4f,0x21,0x12,0x6e,0xe6,0x33,0xe3,0xd3,0x23,
+ 0x55,0xfb,0x49,0x8b,0x7b,0x5d,0xce,0xf5,0x58,0x3b,0x3f,0xb5,0xc6,0x33,0xc2,0x6f,
+ 0xf5,0xc5,0x0f,0x25,0xf9,0x65,0x79,0x67,0x5d,0xab,0xe8,0xe5,0x59,0x55,0xd3,0x62,
+ 0xf4,0xfa,0xee,0x65,0xfa,0x55,0x4e,0xc7,0x0e,0x5c,0xa9,0x48,0xc1,0x10,0x30,0x73,
+ 0xbd,0x5b,0xe3,0x75,0x2b,0xc3,0x20,0x10,0xc7,0x7d,0x3a,0x1d,0x8d,0x17,0x39,0xb5,
+ 0x58,0xae,0x91,0x32,0xe1,0xd2,0x98,0xfe,0x9a,0xdf,0x4b,0x0e,0x79,0x7b,0x8a,0x7a,
+ 0x32,0xc7,0x69,0xec,0xe4,0x8e,0xc5,0x4f,0x45,0x00,0x89,0xe6,0x53,0xa0,0x3d,0xee,
+ 0x1c,0x5a,0xee,0x4d,0x49,0xf2,0x54,0x3b,0xc0,0x4d,0xb4,0xa1,0x47,0x65,0x65,0x7a,
+ 0xbc,0x3d,0xda,0x54,0xee,0x56,0x74,0x22,0xa9,0x68,0x8e,0x70,0x48,0x2c,0x83,0x1e,
+ 0x35,0x08,0xb0,0xf2,0x9a,0xb1,0x43,0x88,0x07,0x5e,0xd6,0x94,0x0b,0x4a,0xa5,0x5b,
+ 0x17,0x47,0x8e,0xcc,0xfa,0xa0,0x24,0xd9,0x1a,0xad,0xad,0x23,0x4f,0xa3,0x59,0xf1,
+ 0x58,0x45,0x12,0x48,0xdc
+};
+
+uint8_t _leaf_serial_invalid_incomplete_ok1[] = {
+ 0x30,0x82,0x04,0x78,0x30,0x82,0x03,0x60,0xa0,0x03,0x02,0x01,0x02,0x02,0x05,0x00,
+ 0xcd,0xfb,0x60,0x2b,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,
+ 0x0b,0x05,0x00,0x30,0x81,0x8e,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04,0x03,0x0c,
+ 0x1c,0x43,0x41,0x20,0x73,0x65,0x72,0x69,0x61,0x6c,0x20,0x69,0x6e,0x76,0x61,0x6c,
+ 0x69,0x64,0x20,0x69,0x6e,0x63,0x6f,0x6d,0x70,0x6c,0x65,0x74,0x65,0x31,0x22,0x30,
+ 0x20,0x06,0x03,0x55,0x04,0x0b,0x0c,0x19,0x56,0x61,0x6c,0x69,0x64,0x20,0x50,0x72,
+ 0x6f,0x6a,0x65,0x63,0x74,0x20,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x73,0x20,0x56,
+ 0x32,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0a,0x0c,0x0a,0x41,0x70,0x70,0x6c,
+ 0x65,0x20,0x49,0x6e,0x63,0x2e,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x0c,
+ 0x09,0x43,0x75,0x70,0x65,0x72,0x74,0x69,0x6e,0x6f,0x31,0x0b,0x30,0x09,0x06,0x03,
+ 0x55,0x04,0x08,0x0c,0x02,0x43,0x41,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,
+ 0x13,0x02,0x55,0x53,0x30,0x20,0x17,0x0d,0x31,0x37,0x30,0x35,0x30,0x31,0x32,0x32,
+ 0x34,0x30,0x30,0x37,0x5a,0x18,0x0f,0x32,0x31,0x31,0x37,0x30,0x34,0x30,0x37,0x32,
+ 0x32,0x34,0x30,0x30,0x37,0x5a,0x30,0x81,0x94,0x31,0x2b,0x30,0x29,0x06,0x03,0x55,
+ 0x04,0x03,0x0c,0x22,0x4c,0x65,0x61,0x66,0x20,0x73,0x65,0x72,0x69,0x61,0x6c,0x20,
+ 0x69,0x6e,0x76,0x61,0x6c,0x69,0x64,0x20,0x69,0x6e,0x63,0x6f,0x6d,0x70,0x6c,0x65,
+ 0x74,0x65,0x20,0x6f,0x6b,0x31,0x31,0x22,0x30,0x20,0x06,0x03,0x55,0x04,0x0b,0x0c,
+ 0x19,0x56,0x61,0x6c,0x69,0x64,0x20,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74,0x20,0x54,
+ 0x65,0x73,0x74,0x20,0x43,0x41,0x73,0x20,0x56,0x32,0x31,0x13,0x30,0x11,0x06,0x03,
+ 0x55,0x04,0x0a,0x0c,0x0a,0x41,0x70,0x70,0x6c,0x65,0x20,0x49,0x6e,0x63,0x2e,0x31,
+ 0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x0c,0x09,0x43,0x75,0x70,0x65,0x72,0x74,
+ 0x69,0x6e,0x6f,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x08,0x0c,0x02,0x43,0x41,
+ 0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x30,0x82,0x01,
+ 0x22,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01,0x05,0x00,
+ 0x03,0x82,0x01,0x0f,0x00,0x30,0x82,0x01,0x0a,0x02,0x82,0x01,0x01,0x00,0xc0,0x12,
+ 0xf8,0x4e,0x54,0xd5,0x0a,0xf6,0x43,0x16,0x4d,0xe2,0xe9,0x4c,0xb8,0x1c,0x05,0x6d,
+ 0x1b,0x0c,0xd5,0x7c,0x29,0x1e,0x82,0x5e,0x6a,0xb6,0x85,0x03,0x72,0x13,0x60,0x94,
+ 0xee,0x8d,0x56,0x03,0xd1,0x41,0x84,0x7c,0xf0,0xc1,0xd2,0x60,0xda,0x2e,0x56,0x8a,
+ 0xca,0x01,0xf6,0xbe,0x89,0x7a,0x27,0x44,0x6d,0x28,0x2c,0xfa,0x02,0x36,0xf7,0xa7,
+ 0x0d,0x4b,0x27,0x83,0x90,0x3c,0x14,0x1e,0x73,0xa5,0x61,0xc0,0xf4,0x05,0x43,0xed,
+ 0x36,0x34,0x14,0x7a,0xe0,0x9b,0x4e,0xbb,0x0f,0x12,0x97,0xcb,0x1b,0x99,0x46,0x41,
+ 0x3e,0xcb,0xa1,0xc3,0xab,0x43,0x27,0x45,0x37,0x61,0x94,0xe3,0xe8,0x60,0xd3,0x94,
+ 0x21,0x6f,0xd8,0x61,0x28,0x81,0xb5,0xe3,0x0f,0xfd,0x2f,0x61,0xe4,0xa4,0x5c,0x25,
+ 0x91,0x58,0xa0,0x55,0x5e,0xae,0x44,0x61,0x92,0x2b,0x99,0x13,0x1a,0xba,0x7c,0x1e,
+ 0xa1,0x2d,0xd2,0x02,0x78,0x9d,0xf2,0xfb,0x1a,0xd8,0xc2,0x01,0x19,0x76,0x30,0xf1,
+ 0x6a,0x65,0xd3,0x76,0xc7,0x67,0xb0,0xe8,0xa0,0x15,0xce,0x8d,0x90,0x45,0xe8,0x71,
+ 0x68,0x9f,0x56,0x91,0x22,0xe4,0xf4,0x16,0x69,0x2d,0x87,0x23,0x52,0x51,0x27,0x13,
+ 0x6e,0x7e,0xdf,0xf1,0x7e,0x67,0x5d,0x56,0xb8,0x8b,0x5d,0x75,0x09,0xb2,0x4a,0x3b,
+ 0x5e,0xac,0x8e,0x42,0xf4,0xb6,0xc2,0xf6,0x5c,0xde,0x9d,0xeb,0xf5,0x82,0xed,0x71,
+ 0xfa,0x23,0x94,0xd6,0x1c,0x19,0x65,0xa5,0xae,0x38,0xbd,0x57,0x27,0xfd,0x7c,0x2b,
+ 0x30,0x71,0xb1,0xa0,0x4a,0xac,0xbe,0xcb,0xe7,0x17,0x81,0x20,0xe9,0x8f,0x02,0x03,
+ 0x01,0x00,0x01,0xa3,0x81,0xd2,0x30,0x81,0xcf,0x30,0x0e,0x06,0x03,0x55,0x1d,0x0f,
+ 0x01,0x01,0xff,0x04,0x04,0x03,0x02,0x07,0x80,0x30,0x0c,0x06,0x03,0x55,0x1d,0x13,
+ 0x01,0x01,0xff,0x04,0x02,0x30,0x00,0x30,0x1f,0x06,0x03,0x55,0x1d,0x23,0x04,0x18,
+ 0x30,0x16,0x80,0x14,0xa2,0xee,0xae,0x1f,0x41,0x28,0x69,0x40,0x55,0xdf,0xc5,0x33,
+ 0x56,0xab,0xfc,0x23,0x68,0x17,0xcb,0x39,0x30,0x53,0x06,0x03,0x55,0x1d,0x1f,0x04,
+ 0x4c,0x30,0x4a,0x30,0x48,0xa0,0x46,0xa0,0x44,0x86,0x42,0x68,0x74,0x74,0x70,0x3a,
+ 0x2f,0x2f,0x76,0x61,0x6c,0x69,0x64,0x74,0x65,0x73,0x74,0x2e,0x61,0x70,0x70,0x6c,
+ 0x65,0x2e,0x67,0x65,0x6f,0x66,0x66,0x6b,0x2e,0x6e,0x65,0x74,0x2f,0x76,0x32,0x2d,
+ 0x73,0x65,0x72,0x69,0x61,0x6c,0x2d,0x69,0x6e,0x76,0x61,0x6c,0x69,0x64,0x2d,0x69,
+ 0x6e,0x63,0x6f,0x6d,0x70,0x6c,0x65,0x74,0x65,0x2e,0x63,0x72,0x6c,0x30,0x39,0x06,
+ 0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x01,0x01,0x04,0x2d,0x30,0x2b,0x30,0x29,0x06,
+ 0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x86,0x1d,0x68,0x74,0x74,0x70,0x3a,
+ 0x2f,0x2f,0x6b,0x6e,0x6f,0x77,0x6e,0x2d,0x61,0x6e,0x73,0x77,0x65,0x72,0x2d,0x6f,
+ 0x63,0x73,0x70,0x2f,0x6f,0x63,0x73,0x70,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,
+ 0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x6c,0x93,0x89,0x44,
+ 0x96,0x48,0x63,0xa2,0x3c,0x68,0x26,0x3c,0xb5,0x36,0x4a,0x4c,0x3e,0xc4,0xd2,0x7c,
+ 0x2a,0x62,0xed,0x48,0x61,0x60,0x7f,0xe0,0x90,0xe9,0x31,0xf0,0x12,0xa3,0xdd,0x51,
+ 0x20,0xf2,0x4b,0xec,0x1c,0x0e,0x1c,0xd5,0xdc,0xbf,0x79,0x1a,0x4b,0xc4,0xd8,0x50,
+ 0x9d,0x9d,0x17,0xcb,0x0b,0x13,0x12,0x5f,0x54,0x20,0x05,0x5a,0x36,0x27,0x6b,0xef,
+ 0x89,0x34,0x96,0x73,0xae,0x9d,0xd2,0x1c,0x0a,0x98,0x39,0xec,0x83,0x2d,0xcd,0x47,
+ 0x50,0x41,0x5e,0xac,0x03,0x02,0x43,0x05,0xdd,0x34,0xbe,0x64,0x97,0xe3,0x2f,0xa0,
+ 0x70,0xc8,0xbd,0xc9,0x3e,0x6b,0x58,0x84,0x87,0xa5,0xea,0xbe,0xcc,0x7a,0xf8,0xe6,
+ 0xe4,0xa7,0x57,0x32,0xc0,0x3e,0x5a,0x38,0xdb,0xf9,0x95,0xdb,0xb2,0x18,0x09,0xab,
+ 0xbc,0x5c,0x22,0xe4,0x08,0xd7,0xd1,0xdf,0xd0,0xf6,0xef,0x02,0x0d,0xbc,0x9b,0xce,
+ 0x0e,0xcd,0x13,0x60,0x48,0xcc,0x5f,0x0e,0xef,0x38,0x13,0x3b,0x88,0x51,0xca,0xea,
+ 0x7e,0xb7,0xf9,0x53,0xa5,0xe2,0xe3,0x79,0xc5,0xe3,0x3f,0x3d,0x7b,0xfb,0x16,0xd8,
+ 0xcf,0xed,0x8e,0x02,0x4a,0x60,0xa2,0x2e,0x00,0x16,0xd6,0x1b,0x8a,0xbe,0x0b,0xb3,
+ 0x6b,0x91,0x10,0x97,0xf4,0xf0,0x39,0xa8,0x35,0xb8,0xc8,0x51,0x09,0x34,0x9e,0x0f,
+ 0xc8,0x52,0xad,0x72,0xb1,0xe8,0x39,0x64,0xa4,0xa2,0x96,0xa2,0x85,0xff,0xc9,0x63,
+ 0x45,0xe0,0xad,0xf0,0x15,0x91,0x66,0xd0,0x05,0x7b,0x39,0xbd,0x39,0x33,0xf8,0xd1,
+ 0x46,0x21,0x02,0xe7,0xc3,0x73,0xda,0x65,0xe8,0xa7,0x64,0xbd
+};
+
+uint8_t _ca_serial_invalid_incomplete[] = {
+ 0x30,0x82,0x04,0x40,0x30,0x82,0x03,0x28,0xa0,0x03,0x02,0x01,0x02,0x02,0x05,0x00,
+ 0x80,0x00,0x46,0x45,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,
+ 0x0b,0x05,0x00,0x30,0x81,0x87,0x31,0x1e,0x30,0x1c,0x06,0x03,0x55,0x04,0x03,0x0c,
+ 0x15,0x56,0x61,0x6c,0x69,0x64,0x20,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x20,0x52,
+ 0x6f,0x6f,0x74,0x20,0x56,0x32,0x31,0x22,0x30,0x20,0x06,0x03,0x55,0x04,0x0b,0x0c,
+ 0x19,0x56,0x61,0x6c,0x69,0x64,0x20,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74,0x20,0x54,
+ 0x65,0x73,0x74,0x20,0x43,0x41,0x73,0x20,0x56,0x32,0x31,0x13,0x30,0x11,0x06,0x03,
+ 0x55,0x04,0x0a,0x0c,0x0a,0x41,0x70,0x70,0x6c,0x65,0x20,0x49,0x6e,0x63,0x2e,0x31,
+ 0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x0c,0x09,0x43,0x75,0x70,0x65,0x72,0x74,
+ 0x69,0x6e,0x6f,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x08,0x0c,0x02,0x43,0x41,
+ 0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x30,0x20,0x17,
+ 0x0d,0x31,0x37,0x30,0x35,0x30,0x31,0x32,0x32,0x34,0x30,0x30,0x36,0x5a,0x18,0x0f,
+ 0x32,0x31,0x31,0x37,0x30,0x34,0x30,0x37,0x32,0x32,0x34,0x30,0x30,0x36,0x5a,0x30,
+ 0x81,0x8e,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04,0x03,0x0c,0x1c,0x43,0x41,0x20,
+ 0x73,0x65,0x72,0x69,0x61,0x6c,0x20,0x69,0x6e,0x76,0x61,0x6c,0x69,0x64,0x20,0x69,
+ 0x6e,0x63,0x6f,0x6d,0x70,0x6c,0x65,0x74,0x65,0x31,0x22,0x30,0x20,0x06,0x03,0x55,
+ 0x04,0x0b,0x0c,0x19,0x56,0x61,0x6c,0x69,0x64,0x20,0x50,0x72,0x6f,0x6a,0x65,0x63,
+ 0x74,0x20,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x73,0x20,0x56,0x32,0x31,0x13,0x30,
+ 0x11,0x06,0x03,0x55,0x04,0x0a,0x0c,0x0a,0x41,0x70,0x70,0x6c,0x65,0x20,0x49,0x6e,
+ 0x63,0x2e,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x0c,0x09,0x43,0x75,0x70,
+ 0x65,0x72,0x74,0x69,0x6e,0x6f,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x08,0x0c,
+ 0x02,0x43,0x41,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,
+ 0x30,0x82,0x01,0x22,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,
+ 0x01,0x05,0x00,0x03,0x82,0x01,0x0f,0x00,0x30,0x82,0x01,0x0a,0x02,0x82,0x01,0x01,
+ 0x00,0xcd,0x06,0x79,0xc3,0xeb,0x86,0x31,0x75,0xe7,0xb5,0x9d,0xe3,0x20,0xd3,0x13,
+ 0x7c,0x90,0xfb,0xfe,0x51,0xd0,0x17,0x04,0x61,0x64,0x92,0xd2,0x63,0xe2,0x91,0x17,
+ 0x90,0x2e,0xb0,0xa6,0x03,0x8f,0xb2,0xc2,0x8c,0x32,0xf2,0xbe,0x51,0xad,0xee,0x72,
+ 0xf5,0x1f,0x15,0x94,0x72,0x8c,0x17,0xc1,0x47,0x3c,0x74,0xa2,0xb0,0x89,0x7d,0x96,
+ 0xa3,0x01,0xf7,0x14,0x21,0x80,0x0d,0x6e,0x5d,0x56,0x2a,0xcc,0xdc,0x7b,0xfc,0x83,
+ 0xe9,0xb4,0xbc,0x46,0x41,0x14,0xfb,0xb6,0xcc,0x4b,0x5a,0xa0,0x11,0x14,0x4c,0x92,
+ 0x2f,0x47,0xd5,0x3c,0xc9,0xe9,0xfa,0xb5,0x66,0x6b,0x3a,0xdf,0x1b,0x62,0x1a,0x44,
+ 0xa3,0x63,0xf3,0x1a,0x2f,0x46,0xdb,0xd0,0x9e,0x0d,0x9a,0xed,0xcd,0xf3,0xa0,0xb0,
+ 0xd9,0xf0,0xb0,0x58,0xe8,0xfe,0xb7,0x34,0x6c,0x87,0xfa,0xf6,0x96,0xe3,0x2d,0xc1,
+ 0xd5,0x54,0x56,0x88,0x61,0xa6,0xd6,0x58,0xbd,0x58,0x0d,0xc7,0x6f,0xe6,0x84,0xf8,
+ 0x2a,0x24,0xf2,0x97,0xca,0x8c,0xae,0xf9,0xa5,0xe2,0x5e,0x4a,0x9f,0xbf,0xd2,0x4b,
+ 0xe6,0x53,0x72,0xfc,0x05,0x93,0x72,0x40,0x0f,0xc5,0x26,0xda,0x00,0x35,0xe6,0x57,
+ 0xef,0xc2,0x28,0xcf,0x62,0xe9,0x27,0x05,0x1c,0x47,0x33,0x92,0x14,0x87,0xcb,0x8a,
+ 0x34,0xb3,0xb2,0xf8,0x88,0xe5,0x8c,0x44,0xdf,0x1b,0x62,0xbc,0xaa,0x9a,0x49,0x5b,
+ 0xff,0x4c,0x37,0x33,0xad,0x6f,0x98,0x61,0xce,0xc5,0xb5,0x02,0xe0,0x12,0x24,0xc1,
+ 0x59,0x28,0xd8,0x2b,0xea,0x94,0x82,0x2d,0x28,0x37,0xcc,0xf3,0x60,0x9f,0x08,0xe1,
+ 0x3f,0x02,0x03,0x01,0x00,0x01,0xa3,0x81,0xa7,0x30,0x81,0xa4,0x30,0x0e,0x06,0x03,
+ 0x55,0x1d,0x0f,0x01,0x01,0xff,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x12,0x06,0x03,
+ 0x55,0x1d,0x13,0x01,0x01,0xff,0x04,0x08,0x30,0x06,0x01,0x01,0xff,0x02,0x01,0x00,
+ 0x30,0x1d,0x06,0x03,0x55,0x1d,0x0e,0x04,0x16,0x04,0x14,0xa2,0xee,0xae,0x1f,0x41,
+ 0x28,0x69,0x40,0x55,0xdf,0xc5,0x33,0x56,0xab,0xfc,0x23,0x68,0x17,0xcb,0x39,0x30,
+ 0x1f,0x06,0x03,0x55,0x1d,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0xba,0x56,0xd8,0x57,
+ 0x66,0xbd,0xfc,0x5b,0xe2,0x10,0xf2,0x39,0xb3,0xaf,0xb2,0x72,0xed,0x55,0x0f,0x1c,
+ 0x30,0x3e,0x06,0x03,0x55,0x1d,0x1f,0x04,0x37,0x30,0x35,0x30,0x33,0xa0,0x31,0xa0,
+ 0x2f,0x86,0x2d,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x76,0x61,0x6c,0x69,0x64,0x74,
+ 0x65,0x73,0x74,0x2e,0x61,0x70,0x70,0x6c,0x65,0x2e,0x67,0x65,0x6f,0x66,0x66,0x6b,
+ 0x2e,0x6e,0x65,0x74,0x2f,0x76,0x32,0x2d,0x72,0x6f,0x6f,0x74,0x2e,0x63,0x72,0x6c,
+ 0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x03,
+ 0x82,0x01,0x01,0x00,0x43,0xa4,0x08,0x47,0x8c,0x5c,0x79,0x0b,0x5f,0xe2,0xe7,0xec,
+ 0x0e,0x70,0x69,0xee,0xd1,0xbb,0xda,0x02,0x06,0xcf,0x75,0xbe,0xe9,0xbf,0xfd,0xf7,
+ 0xa0,0x3b,0x8e,0xf0,0x01,0x6c,0x1b,0x82,0x7d,0xeb,0x6f,0xaa,0x7a,0x3c,0xef,0x7b,
+ 0xd9,0x9c,0x74,0x48,0x2a,0x2a,0xd1,0xa3,0x0d,0xee,0xe8,0xa3,0x55,0x83,0xd6,0xa4,
+ 0x3a,0xb1,0x2b,0xf1,0x44,0x8e,0x3d,0xec,0xc7,0x74,0x73,0x1b,0xaf,0xf1,0xee,0xea,
+ 0x69,0x8a,0xa2,0x88,0xf6,0x82,0x6a,0x2d,0x21,0xfd,0x0c,0xf2,0x50,0x6a,0x34,0x81,
+ 0x43,0x8e,0x81,0x0a,0xfa,0x26,0xc4,0x4a,0x10,0x60,0x85,0xb7,0xf3,0xdc,0xd2,0xc7,
+ 0xe2,0xe7,0x17,0x94,0x82,0x68,0x33,0xa3,0xd4,0x19,0xcb,0x10,0x16,0x65,0x4c,0x6b,
+ 0xe7,0x3f,0x3a,0xbe,0x1b,0x3b,0x08,0x0c,0x16,0x0e,0xe0,0x7f,0x75,0x44,0xa9,0xfa,
+ 0x6e,0x2f,0xd1,0x27,0x9b,0xc5,0x4e,0xec,0xac,0xb5,0xaf,0x2b,0xdf,0x10,0xbc,0x38,
+ 0x85,0x4c,0x00,0x18,0x86,0x30,0x40,0x2d,0xab,0xf4,0xc3,0x1e,0xfb,0x3b,0xdf,0xa6,
+ 0x72,0xb9,0x5e,0x24,0x81,0x39,0x92,0x02,0xf9,0xc9,0xc1,0xfc,0x35,0x1a,0x0a,0x0c,
+ 0x2c,0xd4,0x4e,0xae,0x03,0x79,0x22,0xd5,0x97,0x29,0xf6,0x13,0x2f,0xce,0xb6,0x69,
+ 0x5e,0x4f,0xc6,0x0c,0x06,0x00,0x28,0x7c,0x14,0x3e,0xab,0x32,0xaf,0xd6,0x02,0x04,
+ 0x18,0xc8,0xdc,0x9c,0xaf,0x23,0x08,0xc3,0xca,0x3e,0xbc,0xf1,0x44,0xc5,0x43,0x91,
+ 0x00,0x39,0x46,0x15,0x88,0xce,0xe9,0xe3,0xf5,0xae,0xdc,0x5b,0x63,0x77,0xd5,0xc8,
+ 0xf9,0x35,0x0b,0xe1
+};
+
#endif /* _TRUSTTESTS_REVOCATION_TESTS_H_ */
- (void)runCertificateTestForDirectory:(SecPolicyRef)policy subDirectory:(NSString *)resourceSubDirectory verifyDate:(NSDate*)date;
- (id _Nullable) CF_RETURNS_RETAINED SecCertificateCreateFromResource:(NSString * )name subdirectory:(NSString *)dir;
+- (id _Nullable) CF_RETURNS_RETAINED SecCertificateCreateFromPEMResource:(NSString *)name subdirectory:(NSString *)dir;
@end
/* Use this interface to get a SecCertificateRef that has the same CFTypeID
NSURL *url = [[NSBundle bundleForClass:[self class]] URLForResource:name withExtension:@".cer"
subdirectory:dir];
NSData *certData = [NSData dataWithContentsOfURL:url];
- SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)certData);
+ if (!certData) {
+ return nil;
+ }
+ SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certData);
+ return (__bridge id)cert;
+}
+
+- (id _Nullable) CF_RETURNS_RETAINED SecCertificateCreateFromPEMResource:(NSString *)name
+ subdirectory:(NSString *)dir
+{
+ NSURL *url = [[NSBundle bundleForClass:[self class]] URLForResource:name withExtension:@".pem"
+ subdirectory:dir];
+ NSData *certData = [NSData dataWithContentsOfURL:url];
+ if (!certData) {
+ return nil;
+ }
+
+ SecCertificateRef cert = SecCertificateCreateWithPEM(kCFAllocatorDefault, (__bridge CFDataRef)certData);
return (__bridge id)cert;
}
- (instancetype _Nullable) initWithTrustDictionary:(NSDictionary *)testDict;
- (void)addAnchor:(SecCertificateRef)certificate;
+- (void)setNeedsEvaluation;
- (bool)evaluate:(out NSError * _Nullable __autoreleasing * _Nullable)outError;
- (bool)evaluateForExpectedResults:(out NSError * _Nullable __autoreleasing *)outError;
}
}
+- (void)setNeedsEvaluation {
+ SecTrustSetNeedsEvaluation(_trust);
+}
+
- (bool)evaluate:(out NSError * _Nullable __autoreleasing *)outError {
CFErrorRef localError = nil;
_trustResult = kSecTrustResultInvalid;
#include "OSX/sec/Security/SecItemShim.h"
#import "server_security_helpers.h"
#import "spi.h"
+#import "utilities/SecAKSWrappers.h"
#import <utilities/SecCFWrappers.h>
#import <utilities/SecFileLocations.h>
#import "utilities/der_plist.h"
- (void)testCreateSampleDatabase
{
-#if USE_KEYSTORE
+ // The keychain code only does the right thing with generation count if TARGET_HAS_KEYSTORE
+#if TARGET_HAS_KEYSTORE
id mock = OCMClassMock([SecMockAKS class]);
OCMStub([mock useGenerationCount]).andReturn(true);
#endif
*/
[self findManyItems:50];
+
+#if TARGET_HAS_KEYSTORE
+ [mock stopMocking];
+#endif
}
- (void)testTestAKSGenerationCount
{
-#if USE_KEYSTORE
+#if TARGET_HAS_KEYSTORE
id mock = OCMClassMock([SecMockAKS class]);
OCMStub([mock useGenerationCount]).andReturn(true);
[self createManyItems];
[self findManyItems:50];
+
+ [mock stopMocking];
#endif
}
API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
extern const CFStringRef kSecPolicyAppleLegacySSL
API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
+extern const CFStringRef kSecPolicyAppleAlisha
+ API_AVAILABLE(macos(10.15.4), ios(13.4), watchos(6.2), tvos(13.4));
+extern const CFStringRef kSecPolicyAppleMeasuredBootPolicySigning
+ API_AVAILABLE(macos(10.15.4), ios(13.4), watchos(6.2), tvos(13.4));
+
/*!
@enum Policy Name Constants (Private)
@constant kSecPolicyNameAppleAMPService
@constant kSecPolicyNameAppleSiriService
@constant kSecPolicyNameAppleHomeAppClipUploadService
+ @constant kSecPolicyNameAppleUpdatesService
*/
extern const CFStringRef kSecPolicyNameAppleAST2Service
__OSX_AVAILABLE(10.13) __IOS_AVAILABLE(11.0) __TVOS_AVAILABLE(11.0) __WATCHOS_AVAILABLE(4.0);
API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
extern const CFStringRef kSecPolicyNameAppleHomeAppClipUploadService
API_AVAILABLE(macos(10.15.1), ios(13.2), watchos(6.1), tvos(13.1));
+extern const CFStringRef kSecPolicyNameAppleUpdatesService
+ API_AVAILABLE(macos(10.15.4), ios(13.4), watchos(6.2), tvos(13.4));
/*!
@enum Policy Value Constants
pinning options:
* The chain is anchored to any of the production Apple Root CAs.
* There are exactly 3 certs in the chain.
- * The intermediate has a marker extension with OID TBD.
+ * The intermediate has a marker extension with OID 1.2.840.113635.100.6.2.3".
* The leaf has a marker extension with OID 1.2.840.113635.100.6.69.1 and value
matching the applicationId.
* Revocation is checked via any available method.
SecPolicyRef SecPolicyCreateLegacySSL(Boolean server, CFStringRef __nullable hostname)
SPI_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
+/*!
+ @function SecPolicyCreateAlisha
+ @abstract Returns a policy object for verifying Alisha certificates.
+ @discussion The resulting policy uses the Basic X.509 policy with no validity check and
+ pinning options:
+ * EC key sizes are P-256 or larger.
+ @result A policy object. The caller is responsible for calling CFRelease on this when
+ it is no longer needed.
+ */
+__nullable CF_RETURNS_RETAINED
+SecPolicyRef SecPolicyCreateAlisha(void)
+ API_AVAILABLE(macos(10.15.4), ios(13.4), watchos(6.2), tvos(13.4));
+
+/*!
+ @function SecPolicyCreateMeasuredBootPolicySigning
+ @abstract Returns a policy object for verifying Measured Boot Policy Signing certificates.
+ @discussion The resulting policy uses the Basic X.509 policy with no validity check and
+ pinning options:
+ * There are exactly 3 certs in the chain.
+ * The intermediate has a marker extension with OID 1.2.840.113635.100.6.24.17.
+ * The leaf has a marker extension with OID 1.2.840.113635.100.6.26.6.1
+ * RSA key sizes are 2048-bit or larger. EC key sizes are P-256 or larger.
+ Because this policy does not pin the anchors, the caller must use SecTrustSetAnchorCertificates with
+ the expected roots.
+ @result A policy object. The caller is responsible for calling CFRelease on this when
+ it is no longer needed.
+ */
+__nullable CF_RETURNS_RETAINED
+SecPolicyRef SecPolicyCreateMeasuredBootPolicySigning(void)
+ API_AVAILABLE(macos(10.15.4), ios(13.4), watchos(6.2), tvos(13.4));
+
/*
* Legacy functions (OS X only)
*/
return enu;
}
+bool SecCertificatePathVCRevocationCheckedAllCerts(SecCertificatePathVCRef path) {
+ CFIndex certIX, certCount = path->count;
+ if (certCount <= 1 || !path->rvcs) {
+ /* If there is only one certificate, it's the root, so revocation checking is irrelevant. */
+ return false;
+ }
+
+ for (certIX = 0; certIX < path->rvcCount - 1; ++certIX) {
+ SecRVCRef rvc = &((SecRVCRef)path->rvcs)[certIX];
+ if (!SecRVCRevocationChecked(rvc)) {
+ secdebug("rvc", "revocation has not been checked for all certs (not checked for cert %ld)", certIX);
+ return false;
+ }
+ }
+
+ secdebug("rvc", "revocation has been checked for all certs");
+ return true;
+}
+
void SecCertificatePathVCSetRevocationReasonForCertificateAtIndex(SecCertificatePathVCRef certificatePath,
CFIndex ix, CFNumberRef revocationReason) {
if (ix > certificatePath->count - 1) { return; }
void SecCertificatePathVCAllocateRVCs(SecCertificatePathVCRef certificatePath, CFIndex certCount);
CFAbsoluteTime SecCertificatePathVCGetEarliestNextUpdate(SecCertificatePathVCRef path);
void *SecCertificatePathVCGetRVCAtIndex(SecCertificatePathVCRef certificatePath, CFIndex ix); // Returns a SecRVCRef
+bool SecCertificatePathVCRevocationCheckedAllCerts(SecCertificatePathVCRef path);
bool SecCertificatePathVCIsRevocationRequiredForCertificateAtIndex(SecCertificatePathVCRef certificatePath,
CFIndex ix);
void SecCertificatePathVCSetRevocationRequiredForCertificateAtIndex(SecCertificatePathVCRef certificatePath,
CFRelease(dbPath);
}
}
- cacheJob(kSecOCSPCache);
+ if (kSecOCSPCache) {
+ cacheJob(kSecOCSPCache);
+ }
os_unfair_lock_unlock(&cacheLock);
}
policy->_options is a caller provided dictionary, only its cf type has
been checked.
*/
-bool SecPVCSetResultForced(SecPVCRef pvc,
- CFStringRef key, CFIndex ix, CFTypeRef result, bool force) {
+bool SecPVCSetResultForcedWithTrustResult(SecPVCRef pvc, CFStringRef key, CFIndex ix, CFTypeRef result, bool force,
+ SecTrustResultType overrideDefaultTR) {
/* If this is not something the current policy cares about ignore
this error and return true so our caller continues evaluation. */
}
}
- /* Check to see if the SecTrustSettings for the certificate in question
- tell us to ignore this error. */
- if (SecPVCIsAllowedError(pvc, ix, key)) {
- secinfo("policy", "cert[%d]: skipped allowed error %@", (int) ix, key);
- return true;
- }
+ /* Get the default trust result for this key and override it if the caller needs to
+ * set a different trust result than the default. */
+ SecTrustResultType trustResult = trust_result_for_key(key);
+ if (overrideDefaultTR != kSecTrustResultInvalid) {
+ trustResult = overrideDefaultTR;
+ }
- /* Check to see if exceptions tells us to ignore this error. */
- if (SecPVCIsExceptedError(pvc, ix, key, result)) {
- secinfo("policy", "cert[%d]: skipped exception error %@", (int) ix, key);
- return true;
+ /* only recoverable errors can be allowed/excepted */
+ if (trustResult == kSecTrustResultRecoverableTrustFailure) {
+ /* Check to see if the SecTrustSettings for the certificate in question
+ tell us to ignore this error. */
+ if (SecPVCIsAllowedError(pvc, ix, key)) {
+ secinfo("policy", "cert[%d]: skipped allowed error %@", (int) ix, key);
+ return true;
+ }
+
+ /* Check to see if exceptions tells us to ignore this error. */
+ if (SecPVCIsExceptedError(pvc, ix, key, result)) {
+ secinfo("policy", "cert[%d]: skipped exception error %@", (int) ix, key);
+ return true;
+ }
}
secnotice("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix, key,
(force ? "force" : ""), result);
/* Avoid resetting deny or fatal to recoverable */
- SecTrustResultType trustResult = trust_result_for_key(key);
if (SecPVCIsOkResult(pvc) || trustResult == kSecTrustResultFatalTrustFailure) {
pvc->result = trustResult;
} else if (trustResult == kSecTrustResultDeny &&
return true;
}
+bool SecPVCSetResultForced(SecPVCRef pvc, CFStringRef key, CFIndex ix, CFTypeRef result, bool force) {
+ return SecPVCSetResultForcedWithTrustResult(pvc, key, ix, result, force, kSecTrustResultInvalid);
+}
+
bool SecPVCSetResult(SecPVCRef pvc,
CFStringRef key, CFIndex ix, CFTypeRef result) {
return SecPVCSetResultForced(pvc, key, ix, result, false);
if (!SecOTAPKIKillSwitchEnabled(otaref, kOTAPKIKillSwitchCT) &&
SecOTAPKIAssetStalenessLessThanSeconds(otaref, kSecOTAPKIAssetStalenessDisable)) {
/* CT was required. Error is always set on leaf certificate. */
- SecPVCSetResultForced(pvc, kSecPolicyCheckCTRequired,
- 0, kCFBooleanFalse, true);
if (ctp != kSecPathCTRequiredOverridable) {
- /* Normally kSecPolicyCheckCTRequired is recoverable,
- so need to manually change trust result here. */
- pvc->result = kSecTrustResultFatalTrustFailure;
+ /* Normally kSecPolicyCheckCTRequired is recoverable */
+ SecPVCSetResultForcedWithTrustResult(pvc, kSecPolicyCheckCTRequired, 0, kCFBooleanFalse, true,
+ kSecTrustResultFatalTrustFailure);
+ } else {
+ SecPVCSetResultForced(pvc, kSecPolicyCheckCTRequired, 0, kCFBooleanFalse, true);
}
}
CFReleaseNull(otaref);
/* Set the string result as the reason for the sub policy check key
failing. The policy check function should continue processing if
this function returns true. */
-bool SecPVCSetResult(SecPVCRef pv, CFStringRef key, CFIndex ix,
- CFTypeRef result);
-bool SecPVCSetResultForced(SecPVCRef pvc,
- CFStringRef key, CFIndex ix, CFTypeRef result, bool force);
-bool SecPVCIsOkResult(SecPVCRef pvc);
+bool SecPVCSetResult(SecPVCRef pv, CFStringRef key, CFIndex ix, CFTypeRef result);
+bool SecPVCSetResultForced(SecPVCRef pvc, CFStringRef key, CFIndex ix, CFTypeRef result, bool force);
+bool SecPVCSetResultForcedWithTrustResult(SecPVCRef pvc, CFStringRef key, CFIndex ix, CFTypeRef result, bool force,
+ SecTrustResultType overrideDefaultTR);
/* Is the current result considered successful. */
bool SecPVCIsOkResult(SecPVCRef pvc);
// but we haven't checked dates yet.
bool sr_valid = SecOCSPSingleResponseCalculateValidity(sr, kSecDefaultOCSPResponseTTL, verifyTime);
+ if (sr_valid) {
+ rvc->rvc->revocation_checked = true;
+ }
if (sr->certStatus == CS_Good) {
// Side effect of SecOCSPResponseCalculateValidity sets ocspResponse->expireTime
require_quiet(sr_valid && SecOCSPResponseCalculateValidity(ocspResponse, maxAge, kSecDefaultOCSPResponseTTL, verifyTime), errOut);
SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, policyIX);
if (!SecRVCPolicyConstraintsPermitPolicy(constraints, count, policy)) {
policyDeniedByConstraints = true;
- SecPVCSetResultForced(pvc, kSecPolicyCheckIssuerPolicyConstraints, rvc->certIX,
- kCFBooleanFalse, true);
- pvc->result = kSecTrustResultRecoverableTrustFailure;
- if (!rvc->valid_info->overridable) {
- /* error for this check should be non-recoverable */
- pvc->result = kSecTrustResultFatalTrustFailure;
+ if (rvc->valid_info->overridable) {
+ SecPVCSetResultForcedWithTrustResult(pvc, kSecPolicyCheckIssuerPolicyConstraints, rvc->certIX,
+ kCFBooleanFalse, true, kSecTrustResultRecoverableTrustFailure);
+ } else {
+ SecPVCSetResultForced(pvc, kSecPolicyCheckIssuerPolicyConstraints, rvc->certIX, kCFBooleanFalse, true);
}
}
}
CFReleaseNull(cfreason);
}
+bool SecRVCRevocationChecked(SecRVCRef rvc) {
+ return rvc->revocation_checked;
+}
+
static void SecRVCProcessValidInfoResults(SecRVCRef rvc) {
if (!rvc || !rvc->valid_info || !rvc->builder) {
return;
} else if (allowed) {
/* definitely not revoked (allowlisted) */
SecCertificatePathVCSetIsAllowlisted(path, true);
+ rvc->revocation_checked = true;
}
/* no-ca is definitive; no need to check further. */
secdebug("validupdate", "rvc: definitely %s cert %" PRIdCFIndex,
SecValidInfoRef valid_info;
bool done;
+
+ bool revocation_checked;
};
typedef struct OpaqueSecRVC *SecRVCRef;
bool SecRVCHasDefinitiveValidInfo(SecRVCRef rvc);
bool SecRVCHasRevokedValidInfo(SecRVCRef rvc);
void SecRVCSetValidDeterminedErrorResult(SecRVCRef rvc);
+bool SecRVCRevocationChecked(SecRVCRef rvc);
/* OCSP verification callbacks */
void SecORVCConsumeOCSPResponse(SecORVCRef rvc, SecOCSPResponseRef ocspResponse /*CF_CONSUMED*/,
kCFBooleanFalse); /* iOS key */
CFDictionarySetValue(builder->info, kSecTrustRevocationChecked,
kCFBooleanFalse); /* unified API key */
+ } else if (SecCertificatePathVCRevocationCheckedAllCerts(builder->bestPath)) {
+ CFDictionarySetValue(builder->info, kSecTrustInfoRevocationKey,
+ kCFBooleanTrue); /* iOS key */
+ CFDictionarySetValue(builder->info, kSecTrustRevocationChecked,
+ kCFBooleanTrue); /* unified API key */
}
}