2 // SOSAccountTrustClassicIdentity.m
7 #import <Foundation/Foundation.h>
8 #include <AssertMacros.h>
9 #import "keychain/SecureObjectSync/SOSAccountTrustClassic.h"
10 #import "keychain/SecureObjectSync/SOSAccountTrustClassic+Expansion.h"
11 #import "keychain/SecureObjectSync/SOSAccountTrustClassic+Identity.h"
12 #import "keychain/SecureObjectSync/SOSAccountTrustClassic+Circle.h"
14 #import "Analytics/Clients/SOSAnalytics.h"
17 #import "keychain/SecureObjectSync/SOSViews.h"
19 @implementation SOSAccountTrustClassic (Identity)
21 -(bool)isLockedError:(NSError *)error {
23 ([error.domain isEqualToString:(__bridge NSString*)kSecErrorDomain])
24 && error.code == errSecInteractionNotAllowed;
27 -(bool) updateFullPeerInfo:(SOSAccount*)account minimum:(CFSetRef)minimumViews excluded:(CFSetRef)excludedViews
29 if (self.trustedCircle && self.fullPeerInfo) {
30 if(SOSFullPeerInfoUpdateToCurrent(self.fullPeerInfo, minimumViews, excludedViews)) {
31 [self modifyCircle:account.circle_transport err:NULL action:^(SOSCircleRef circle_to_change) {
32 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
33 return SOSCircleUpdatePeerInfo(circle_to_change, self.peerInfo);
41 -(SOSFullPeerInfoRef) getMyFullPeerInfo
43 return self.trustedCircle ? self.fullPeerInfo : NULL;
46 -(bool) fullPeerInfoVerify:(SecKeyRef) privKey err:(CFErrorRef *)error
48 if(!self.fullPeerInfo) return false;
49 SecKeyRef pubKey = SecKeyCreatePublicFromPrivate(privKey);
50 bool retval = SOSPeerInfoApplicationVerify(self.peerInfo, pubKey, error);
51 CFReleaseNull(pubKey);
55 -(bool) hasFullPeerInfo:(CFErrorRef*) error
58 if(![self hasCircle:error]){
61 hasPeer = self.fullPeerInfo != NULL;
64 SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("No peer for circle"));
69 -(SOSFullPeerInfoRef) CopyAccountIdentityPeerInfo
71 return SOSFullPeerInfoCopyFullPeerInfo(self.fullPeerInfo);
74 - (SecKeyRef)randomPermanentFullECKey:(int)keysize name:(NSString *)name error:(CFErrorRef*)cferror CF_RETURNS_RETAINED
76 return GeneratePermanentFullECKey(keysize, (__bridge CFStringRef)name, cferror);
79 // Check that cached values of what is in keychain with what we have in the peer info,
80 // if they ware the same, we could read the items while this process was alive, assume
83 - (bool)haveConfirmedOctagonKeys
85 bool haveSigningKey = false;
86 bool haveEncryptionKey = false;
88 SecKeyRef signingKey = SOSFullPeerInfoCopyOctagonPublicSigningKey(self.fullPeerInfo, NULL);
89 if (self.cachedOctagonSigningKey && CFEqualSafe(signingKey, self.cachedOctagonSigningKey)) {
90 haveSigningKey = true;
92 secerror("circleChange: No extant octagon signing key");
95 SecKeyRef encrytionKey = SOSFullPeerInfoCopyOctagonPublicEncryptionKey(self.fullPeerInfo, NULL);
96 if (self.cachedOctagonEncryptionKey && CFEqualSafe(encrytionKey, self.cachedOctagonEncryptionKey)) {
97 haveEncryptionKey = true;
99 secerror("circleChange: No extant octagon encryption key");
102 CFReleaseNull(signingKey);
103 CFReleaseNull(encrytionKey);
105 return haveSigningKey && haveEncryptionKey;
109 - (void)ensureOctagonPeerKeys:(SOSKVSCircleStorageTransport*)circleTransport
112 NSString* octagonKeyName;
113 SecKeyRef octagonSigningFullKey = NULL;
114 SecKeyRef octagonEncryptionFullKey = NULL;
116 // check if we already confirmed the keys
117 if ([self haveConfirmedOctagonKeys]) {
121 bool changedSelf = false;
123 CFErrorRef copyError = NULL;
124 octagonSigningFullKey = SOSFullPeerInfoCopyOctagonSigningKey(self.fullPeerInfo, ©Error);
125 if(copyError && ![self isLockedError:(__bridge NSError *)copyError]) {
126 secerror("circleChange: Error fetching Octagon signing key: %@", copyError);
129 // Cache that public key we found, to so that we don't need to make the roundtrip though
130 // keychain to get them item, if we don't find a key, try to create a new key if the error
131 // is specifically "couldn't find key", "couldn't read key", or "something went very very wrong".
132 // Otherwise, log a fatal error.
134 if (octagonSigningFullKey) {
135 secnotice("circleChange", "Already have Octagon signing key");
136 CFReleaseNull(self->_cachedOctagonSigningKey);
137 _cachedOctagonSigningKey = SecKeyCopyPublicKey(octagonSigningFullKey);
138 } else if (octagonSigningFullKey == NULL && copyError &&
139 ((CFEqualSafe(CFErrorGetDomain(copyError), kCFErrorDomainOSStatus) && CFErrorGetCode(copyError) == errSecItemNotFound) ||
140 (CFEqualSafe(CFErrorGetDomain(copyError), kCFErrorDomainOSStatus) && CFErrorGetCode(copyError) == errSecDecode) ||
141 (CFEqualSafe(CFErrorGetDomain(copyError), kCFErrorDomainOSStatus) && CFErrorGetCode(copyError) == errSecParam)))
143 octagonKeyName = [NSString stringWithFormat:@"Octagon Peer Signing ID for %@", SOSCircleGetName(self.trustedCircle)];
144 CFErrorRef cferror = NULL;
145 octagonSigningFullKey = [self randomPermanentFullECKey:384 name:octagonKeyName error:&cferror];
146 if(cferror || !octagonSigningFullKey) {
147 secerror("circleChange: Error creating Octagon signing key: %@", cferror);
149 SOSFullPeerInfoUpdateOctagonSigningKey(self.fullPeerInfo, octagonSigningFullKey, &cferror);
151 secerror("circleChange: Error upgrading Octagon signing key: %@", cferror);
153 secnotice("circleChange", "Successfully created new Octagon signing key");
158 CFReleaseNull(cferror);
159 } else if((octagonSigningFullKey == NULL || copyError) && ![self isLockedError:(__bridge NSError *)copyError]) {
160 secerror("error is too scary, not creating new Octagon signing key: %@", copyError);
162 [[SOSAnalytics logger] logResultForEvent:@"SOSCheckOctagonSigningKey" hardFailure:true result:(__bridge NSError*)copyError];
166 CFReleaseNull(copyError);
167 CFReleaseNull(octagonSigningFullKey);
169 // Now do the same thing for encryption key
171 CFReleaseNull(copyError);
172 octagonEncryptionFullKey = SOSFullPeerInfoCopyOctagonEncryptionKey(self.fullPeerInfo, ©Error);
173 if(copyError && ![self isLockedError:(__bridge NSError *)copyError]) {
174 secerror("circleChange: Error fetching Octagon encryption key: %@", copyError);
177 if (octagonEncryptionFullKey) {
178 secnotice("circleChange", "Already have Octagon encryption key");
179 CFReleaseNull(self->_cachedOctagonEncryptionKey);
180 _cachedOctagonEncryptionKey = SecKeyCopyPublicKey(octagonEncryptionFullKey);
181 } else if (octagonEncryptionFullKey == NULL && copyError &&
182 ((CFEqualSafe(CFErrorGetDomain(copyError), kCFErrorDomainOSStatus) && CFErrorGetCode(copyError) == errSecItemNotFound) ||
183 (CFEqualSafe(CFErrorGetDomain(copyError), kCFErrorDomainOSStatus) && CFErrorGetCode(copyError) == errSecDecode) ||
184 (CFEqualSafe(CFErrorGetDomain(copyError), kCFErrorDomainOSStatus) && CFErrorGetCode(copyError) == errSecParam)))
186 octagonKeyName = [NSString stringWithFormat:@"Octagon Peer Encryption ID for %@", SOSCircleGetName(self.trustedCircle)];
187 CFErrorRef cferror = NULL;
188 octagonEncryptionFullKey = [self randomPermanentFullECKey:384 name:octagonKeyName error:&cferror];
189 if(cferror || !octagonEncryptionFullKey) {
190 secerror("circleChange: Error creating Octagon encryption key: %@", cferror);
192 SOSFullPeerInfoUpdateOctagonEncryptionKey(self.fullPeerInfo, octagonEncryptionFullKey, &cferror);
194 secerror("circleChange: Error upgrading Octagon encryption key: %@", cferror);
196 secnotice("circleChange", "Successfully created new Octagon encryption key");
201 CFReleaseNull(cferror);
203 } else if((octagonEncryptionFullKey == NULL || copyError) && ![self isLockedError:(__bridge NSError *)copyError]) {
204 secerror("error is too scary, not creating new Octagon encryption key: %@", copyError);
206 [[SOSAnalytics logger] logResultForEvent:@"SOSCheckOctagonEncryptionKey" hardFailure:true result:(__bridge NSError*)copyError];
209 CFReleaseNull(copyError);
210 CFReleaseNull(octagonEncryptionFullKey);
213 [self modifyCircle:circleTransport err:NULL action:^bool (SOSCircleRef circle_to_change) {
214 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(self.fullPeerInfo));
220 -(bool) ensureFullPeerAvailable:(CFDictionaryRef)gestalt deviceID:(CFStringRef)deviceID backupKey:(CFDataRef)backup err:(CFErrorRef *) error
222 require_action_quiet(self.trustedCircle, fail, SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("Don't have circle")));
224 if (self.fullPeerInfo == NULL) {
225 bool skipInitialSync = false; // This will be set if the set of initial sync views is empty
226 NSString* octagonKeyName;
227 CFStringRef keyName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("ID for %@-%@"), SOSPeerGestaltGetName(gestalt), SOSCircleGetName(self.trustedCircle));
228 SecKeyRef full_key = [self randomPermanentFullECKey:256 name:(__bridge NSString *)keyName error:NULL];
230 octagonKeyName = [@"Octagon Peer Signing " stringByAppendingString:(__bridge NSString*)keyName];
231 SecKeyRef octagonSigningFullKey = [self randomPermanentFullECKey:384 name:octagonKeyName error:NULL];
233 octagonKeyName = [@"Octagon Peer Encryption " stringByAppendingString:(__bridge NSString*)keyName];
234 SecKeyRef octagonEncryptionFullKey = [self randomPermanentFullECKey:384 name:octagonKeyName error:NULL];
236 if (full_key && octagonSigningFullKey && octagonEncryptionFullKey) {
237 CFMutableSetRef initialViews = SOSViewCopyViewSet(kViewSetInitial);
238 CFMutableSetRef initialSyncDoneViews = SOSViewCopyViewSet(kViewSetAlwaysOn);
239 CFSetRef defaultViews = SOSViewCopyViewSet(kViewSetDefault);
240 CFSetRef backupViews = SOSViewCopyViewSet(kViewSetRequiredForBackup);
242 CFSetUnion(initialSyncDoneViews, defaultViews);
244 // If there are no "initialViews" then we're basically through initial sync - so setup alwaysOn and default views
245 if(CFSetGetCount(initialViews) == 0) {
246 skipInitialSync = true;
247 CFSetUnion(initialViews, initialSyncDoneViews);
249 CFSetUnion(initialViews, backupViews);
251 // setting fullPeerInfo takes an extra ref, so...
252 self.fullPeerInfo = nil;
253 SOSFullPeerInfoRef fpi = SOSFullPeerInfoCreateWithViews(kCFAllocatorDefault, gestalt, backup, initialViews, full_key, octagonSigningFullKey, octagonEncryptionFullKey, error);
254 self.fullPeerInfo = fpi;
257 CFDictionaryRef v2dictionaryTestUpdates = [self getValueFromExpansion:kSOSTestV2Settings err:NULL];
258 if(v2dictionaryTestUpdates) SOSFullPeerInfoUpdateV2Dictionary(self.fullPeerInfo, v2dictionaryTestUpdates, NULL);
260 if(!skipInitialSync) {
261 [self pendEnableViewSet:initialSyncDoneViews];
262 [self setValueInExpansion:kSOSUnsyncedViewsKey value:kCFBooleanTrue err:NULL];
265 CFReleaseNull(initialViews);
266 CFReleaseNull(backupViews);
267 CFReleaseNull(initialSyncDoneViews);
268 CFReleaseNull(defaultViews);
271 secerror("No full_key: %@:", error ? *error : NULL);
275 CFReleaseNull(full_key);
276 CFReleaseNull(octagonSigningFullKey);
277 CFReleaseNull(octagonEncryptionFullKey);
278 CFReleaseNull(keyName);
282 return self.fullPeerInfo != NULL;
284 -(bool) isMyPeerActive:(CFErrorRef*) error
286 return (self.peerInfo ? SOSCircleHasActivePeer(self.trustedCircle, self.peerInfo, error) : false);
289 -(void) purgeIdentity
291 if (self.fullPeerInfo) {
292 // Purge private key but don't return error if we can't.
293 CFErrorRef purgeError = NULL;
294 if (!SOSFullPeerInfoPurgePersistentKey(self.fullPeerInfo, &purgeError)) {
295 secwarning("Couldn't purge persistent key for %@ [%@]", self.fullPeerInfo, purgeError);
297 CFReleaseNull(purgeError);
299 self.fullPeerInfo=nil;