2 // SOSAccountTrustClassicIdentity.m
7 #import <Foundation/Foundation.h>
8 #include <AssertMacros.h>
9 #import "Security/SecureObjectSync/SOSAccountTrustClassic.h"
10 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Expansion.h"
11 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Identity.h"
12 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Circle.h"
14 #import "Analytics/Clients/SOSAnalytics.h"
17 #import "Security/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 NSString* octagonKeyName;
226 CFStringRef keyName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("ID for %@-%@"), SOSPeerGestaltGetName(gestalt), SOSCircleGetName(self.trustedCircle));
227 SecKeyRef full_key = [self randomPermanentFullECKey:256 name:(__bridge NSString *)keyName error:NULL];
229 octagonKeyName = [@"Octagon Peer Signing " stringByAppendingString:(__bridge NSString*)keyName];
230 SecKeyRef octagonSigningFullKey = [self randomPermanentFullECKey:384 name:octagonKeyName error:NULL];
232 octagonKeyName = [@"Octagon Peer Encryption " stringByAppendingString:(__bridge NSString*)keyName];
233 SecKeyRef octagonEncryptionFullKey = [self randomPermanentFullECKey:384 name:octagonKeyName error:NULL];
235 if (full_key && octagonSigningFullKey && octagonEncryptionFullKey) {
236 CFSetRef initialViews = SOSViewCopyViewSet(kViewSetInitial);
238 self.fullPeerInfo = nil;
240 // setting fullPeerInfo takes an extra ref, so...
241 SOSFullPeerInfoRef fpi = SOSFullPeerInfoCreateWithViews(kCFAllocatorDefault, gestalt, backup, initialViews, full_key, octagonSigningFullKey, octagonEncryptionFullKey, error);
242 self.fullPeerInfo = fpi;
245 CFDictionaryRef v2dictionaryTestUpdates = [self getValueFromExpansion:kSOSTestV2Settings err:NULL];
246 if(v2dictionaryTestUpdates) SOSFullPeerInfoUpdateV2Dictionary(self.fullPeerInfo, v2dictionaryTestUpdates, NULL);
247 CFReleaseNull(initialViews);
249 CFSetRef pendingDefaultViews = SOSViewCopyViewSet(kViewSetDefault);
250 [self pendEnableViewSet:pendingDefaultViews];
251 CFReleaseNull(pendingDefaultViews);
253 [self setValueInExpansion:kSOSUnsyncedViewsKey value:kCFBooleanTrue err:NULL];
256 secerror("No full_key: %@:", error ? *error : NULL);
260 CFReleaseNull(full_key);
261 CFReleaseNull(octagonSigningFullKey);
262 CFReleaseNull(octagonEncryptionFullKey);
263 CFReleaseNull(keyName);
267 return self.fullPeerInfo != NULL;
269 -(bool) isMyPeerActive:(CFErrorRef*) error
271 return (self.peerInfo ? SOSCircleHasActivePeer(self.trustedCircle, self.peerInfo, error) : false);
274 -(void) purgeIdentity
276 if (self.fullPeerInfo) {
277 // Purge private key but don't return error if we can't.
278 CFErrorRef purgeError = NULL;
279 if (!SOSFullPeerInfoPurgePersistentKey(self.fullPeerInfo, &purgeError)) {
280 secwarning("Couldn't purge persistent key for %@ [%@]", self.fullPeerInfo, purgeError);
282 CFReleaseNull(purgeError);
284 self.fullPeerInfo=nil;