]> git.saurik.com Git - apple/security.git/blob - keychain/SecureObjectSync/SOSAccountTrustClassic+Identity.m
Security-59754.80.3.tar.gz
[apple/security.git] / keychain / SecureObjectSync / SOSAccountTrustClassic+Identity.m
1 //
2 // SOSAccountTrustClassicIdentity.m
3 // Security
4 //
5
6
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"
13 #import <SecurityFoundation/SFSigningOperation.h>
14 #import <SecurityFoundation/SFKey.h>
15 #import <SecurityFoundation/SFKey_Private.h>
16 #import <SecurityFoundation/SFDigestOperation.h>
17 #if __OBJC2__
18 #import "Analytics/Clients/SOSAnalytics.h"
19 #endif // __OBJC2__
20
21 #import "keychain/SecureObjectSync/SOSViews.h"
22
23 @implementation SOSAccountTrustClassic (Identity)
24
25 -(bool)isLockedError:(NSError *)error {
26 return error &&
27 ([error.domain isEqualToString:(__bridge NSString*)kSecErrorDomain])
28 && error.code == errSecInteractionNotAllowed;
29 }
30
31 -(bool) updateFullPeerInfo:(SOSAccount*)account minimum:(CFSetRef)minimumViews excluded:(CFSetRef)excludedViews
32 {
33 if (self.trustedCircle && self.fullPeerInfo) {
34 if(SOSFullPeerInfoUpdateToCurrent(self.fullPeerInfo, minimumViews, excludedViews)) {
35 [self modifyCircle:account.circle_transport err:NULL action:^(SOSCircleRef circle_to_change) {
36 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
37 return SOSCircleUpdatePeerInfo(circle_to_change, self.peerInfo);
38 }];
39 }
40 }
41
42 return true;
43 }
44
45 -(SOSFullPeerInfoRef) getMyFullPeerInfo
46 {
47 return self.trustedCircle ? self.fullPeerInfo : NULL;
48 }
49
50 -(bool) fullPeerInfoVerify:(SecKeyRef) privKey err:(CFErrorRef *)error
51 {
52 if(!self.fullPeerInfo) return false;
53 SecKeyRef pubKey = SecKeyCreatePublicFromPrivate(privKey);
54 bool retval = SOSPeerInfoApplicationVerify(self.peerInfo, pubKey, error);
55 CFReleaseNull(pubKey);
56 return retval;
57 }
58
59 -(bool) hasFullPeerInfo:(CFErrorRef*) error
60 {
61 bool hasPeer = false;
62 if(![self hasCircle:error]){
63 return hasPeer;
64 }
65 hasPeer = self.fullPeerInfo != NULL;
66
67 if (!hasPeer)
68 SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("No peer for circle"));
69
70 return hasPeer;
71 }
72
73 -(SOSFullPeerInfoRef) CopyAccountIdentityPeerInfo
74 {
75 return SOSFullPeerInfoCopyFullPeerInfo(self.fullPeerInfo);
76 }
77
78 - (SecKeyRef)randomPermanentFullECKey:(int)keysize name:(NSString *)name error:(CFErrorRef*)cferror CF_RETURNS_RETAINED
79 {
80 return GeneratePermanentFullECKey(keysize, (__bridge CFStringRef)name, cferror);
81 }
82
83 // Check that cached values of what is in keychain with what we have in the peer info,
84 // if they ware the same, we could read the items while this process was alive, assume
85 // all is swell.
86 #if OCTAGON
87 - (bool)haveConfirmedOctagonKeys
88 {
89 bool haveSigningKey = false;
90 bool haveEncryptionKey = false;
91
92 SecKeyRef signingKey = SOSFullPeerInfoCopyOctagonPublicSigningKey(self.fullPeerInfo, NULL);
93 if (self.cachedOctagonSigningKey && CFEqualSafe(signingKey, self.cachedOctagonSigningKey)) {
94 haveSigningKey = true;
95 } else {
96 secerror("circleChange: No extant octagon signing key");
97 }
98
99 SecKeyRef encrytionKey = SOSFullPeerInfoCopyOctagonPublicEncryptionKey(self.fullPeerInfo, NULL);
100 if (self.cachedOctagonEncryptionKey && CFEqualSafe(encrytionKey, self.cachedOctagonEncryptionKey)) {
101 haveEncryptionKey = true;
102 } else {
103 secerror("circleChange: No extant octagon encryption key");
104 }
105
106 CFReleaseNull(signingKey);
107 CFReleaseNull(encrytionKey);
108
109 return haveSigningKey && haveEncryptionKey;
110 }
111 #endif
112
113 - (void)ensureOctagonPeerKeys:(SOSKVSCircleStorageTransport*)circleTransport
114 {
115 #if OCTAGON
116 NSString* octagonKeyName;
117 SecKeyRef octagonSigningFullKey = NULL;
118 SecKeyRef octagonEncryptionFullKey = NULL;
119
120 // check if we already confirmed the keys
121 if ([self haveConfirmedOctagonKeys]) {
122 return;
123 }
124
125 bool changedSelf = false;
126
127 CFErrorRef copyError = NULL;
128 octagonSigningFullKey = SOSFullPeerInfoCopyOctagonSigningKey(self.fullPeerInfo, &copyError);
129 if(copyError && ![self isLockedError:(__bridge NSError *)copyError]) {
130 secerror("circleChange: Error fetching Octagon signing key: %@", copyError);
131 }
132
133 // Cache that public key we found, to so that we don't need to make the roundtrip though
134 // keychain to get them item, if we don't find a key, try to create a new key if the error
135 // is specifically "couldn't find key", "couldn't read key", or "something went very very wrong".
136 // Otherwise, log a fatal error.
137
138 if (octagonSigningFullKey) {
139 secnotice("circleChange", "Already have Octagon signing key");
140 CFReleaseNull(self->_cachedOctagonSigningKey);
141 _cachedOctagonSigningKey = SecKeyCopyPublicKey(octagonSigningFullKey);
142
143 // Ensure that the agrp is correct.
144 SOSCCEnsureAccessGroupOfKey(_cachedOctagonSigningKey, @"sync", (__bridge NSString*)kSOSInternalAccessGroup);
145
146 } else if (octagonSigningFullKey == NULL && copyError &&
147 ((CFEqualSafe(CFErrorGetDomain(copyError), kCFErrorDomainOSStatus) && CFErrorGetCode(copyError) == errSecItemNotFound) ||
148 (CFEqualSafe(CFErrorGetDomain(copyError), kCFErrorDomainOSStatus) && CFErrorGetCode(copyError) == errSecDecode) ||
149 (CFEqualSafe(CFErrorGetDomain(copyError), kCFErrorDomainOSStatus) && CFErrorGetCode(copyError) == errSecParam)))
150 {
151 octagonKeyName = [NSString stringWithFormat:@"Octagon Peer Signing ID for %@", SOSCircleGetName(self.trustedCircle)];
152 CFErrorRef cferror = NULL;
153 octagonSigningFullKey = [self randomPermanentFullECKey:384 name:octagonKeyName error:&cferror];
154 if(cferror || !octagonSigningFullKey) {
155 secerror("circleChange: Error creating Octagon signing key: %@", cferror);
156 } else {
157 SOSFullPeerInfoUpdateOctagonSigningKey(self.fullPeerInfo, octagonSigningFullKey, &cferror);
158 if(cferror) {
159 secerror("circleChange: Error upgrading Octagon signing key: %@", cferror);
160 } else {
161 secnotice("circleChange", "Successfully created new Octagon signing key");
162 }
163 changedSelf = true;
164 }
165
166 CFReleaseNull(cferror);
167 } else if((octagonSigningFullKey == NULL || copyError) && ![self isLockedError:(__bridge NSError *)copyError]) {
168 secerror("error is too scary, not creating new Octagon signing key: %@", copyError);
169 #if __OBJC2__
170 [[SOSAnalytics logger] logResultForEvent:@"SOSCheckOctagonSigningKey" hardFailure:true result:(__bridge NSError*)copyError];
171 #endif // __OBJC2__
172 }
173
174 CFReleaseNull(copyError);
175 CFReleaseNull(octagonSigningFullKey);
176
177 // Now do the same thing for encryption key
178
179 CFReleaseNull(copyError);
180 octagonEncryptionFullKey = SOSFullPeerInfoCopyOctagonEncryptionKey(self.fullPeerInfo, &copyError);
181 if(copyError && ![self isLockedError:(__bridge NSError *)copyError]) {
182 secerror("circleChange: Error fetching Octagon encryption key: %@", copyError);
183 }
184
185 if (octagonEncryptionFullKey) {
186 secnotice("circleChange", "Already have Octagon encryption key");
187 CFReleaseNull(self->_cachedOctagonEncryptionKey);
188 _cachedOctagonEncryptionKey = SecKeyCopyPublicKey(octagonEncryptionFullKey);
189
190 SOSCCEnsureAccessGroupOfKey(_cachedOctagonEncryptionKey, @"sync", (__bridge NSString*)kSOSInternalAccessGroup);
191 } else if (octagonEncryptionFullKey == NULL && copyError &&
192 ((CFEqualSafe(CFErrorGetDomain(copyError), kCFErrorDomainOSStatus) && CFErrorGetCode(copyError) == errSecItemNotFound) ||
193 (CFEqualSafe(CFErrorGetDomain(copyError), kCFErrorDomainOSStatus) && CFErrorGetCode(copyError) == errSecDecode) ||
194 (CFEqualSafe(CFErrorGetDomain(copyError), kCFErrorDomainOSStatus) && CFErrorGetCode(copyError) == errSecParam)))
195 {
196 octagonKeyName = [NSString stringWithFormat:@"Octagon Peer Encryption ID for %@", SOSCircleGetName(self.trustedCircle)];
197 CFErrorRef cferror = NULL;
198 octagonEncryptionFullKey = [self randomPermanentFullECKey:384 name:octagonKeyName error:&cferror];
199 if(cferror || !octagonEncryptionFullKey) {
200 secerror("circleChange: Error creating Octagon encryption key: %@", cferror);
201 } else {
202 SOSFullPeerInfoUpdateOctagonEncryptionKey(self.fullPeerInfo, octagonEncryptionFullKey, &cferror);
203 if(cferror) {
204 secerror("circleChange: Error upgrading Octagon encryption key: %@", cferror);
205 } else {
206 secnotice("circleChange", "Successfully created new Octagon encryption key");
207 }
208 changedSelf = true;
209 }
210
211 CFReleaseNull(cferror);
212
213 } else if((octagonEncryptionFullKey == NULL || copyError) && ![self isLockedError:(__bridge NSError *)copyError]) {
214 secerror("error is too scary, not creating new Octagon encryption key: %@", copyError);
215 #if __OBJC2__
216 [[SOSAnalytics logger] logResultForEvent:@"SOSCheckOctagonEncryptionKey" hardFailure:true result:(__bridge NSError*)copyError];
217 #endif
218 }
219 CFReleaseNull(copyError);
220 CFReleaseNull(octagonEncryptionFullKey);
221
222 if(changedSelf) {
223 [self modifyCircle:circleTransport err:NULL action:^bool (SOSCircleRef circle_to_change) {
224 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(self.fullPeerInfo));
225 }];
226 }
227 #endif /* OCTAGON */
228 }
229
230 -(bool) ensureFullPeerAvailable:(SOSAccount*) account err:(CFErrorRef *) error
231 {
232 require_action_quiet(self.trustedCircle, fail, SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("Don't have circle")));
233
234 if (self.fullPeerInfo == NULL || !SOSFullPeerInfoPrivKeyExists(self.fullPeerInfo)) {
235 if(self.fullPeerInfo) { // fullPeerInfo where privkey is gone
236 secnotice("circleOps", "FullPeerInfo has no matching private key - resetting FPI and attendant keys");
237 CFReleaseNull(self->fullPeerInfo);
238 if(self->peerInfo) CFReleaseNull(self->peerInfo);
239 if(self->_cachedOctagonSigningKey) CFReleaseNull(self->_cachedOctagonSigningKey);
240 if(self->_cachedOctagonEncryptionKey) CFReleaseNull(self->_cachedOctagonEncryptionKey);
241 }
242
243 SecKeyRef fullKey = NULL;
244 bool skipInitialSync = false; // This will be set if the set of initial sync views is empty
245 NSString* octagonKeyName;
246 CFStringRef keyName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("ID for %@-%@"), SOSPeerGestaltGetName((__bridge CFDictionaryRef)(account.gestalt)), SOSCircleGetName(self.trustedCircle));
247 fullKey = [self randomPermanentFullECKey:256 name:(__bridge NSString *)keyName error:NULL];
248
249 octagonKeyName = [@"Octagon Peer Signing " stringByAppendingString:(__bridge NSString*)keyName];
250
251 SecKeyRef octagonSigningFullKey = NULL;
252 if (account.octagonSigningFullKeyRef != NULL) {
253 octagonSigningFullKey = CFRetainSafe(account.octagonSigningFullKeyRef);
254 } else {
255 octagonSigningFullKey = [self randomPermanentFullECKey:384 name:octagonKeyName error:NULL];
256 }
257
258 octagonKeyName = [@"Octagon Peer Encryption " stringByAppendingString:(__bridge NSString*)keyName];
259
260 SecKeyRef octagonEncryptionFullKey = NULL;
261 if (account.octagonEncryptionFullKeyRef != NULL){
262 octagonEncryptionFullKey = CFRetainSafe(account.octagonEncryptionFullKeyRef);
263 } else {
264 octagonEncryptionFullKey = [self randomPermanentFullECKey:384 name:octagonKeyName error:NULL];
265 }
266
267 if (fullKey && octagonSigningFullKey && octagonEncryptionFullKey) {
268 CFMutableSetRef initialViews = SOSViewCopyViewSet(kViewSetInitial);
269 CFMutableSetRef initialSyncDoneViews = SOSViewCopyViewSet(kViewSetAlwaysOn);
270 CFSetRef defaultViews = SOSViewCopyViewSet(kViewSetDefault);
271 CFSetRef backupViews = SOSViewCopyViewSet(kViewSetRequiredForBackup);
272
273 CFSetUnion(initialSyncDoneViews, defaultViews);
274
275 // If there are no "initialViews" then we're basically through initial sync - so setup alwaysOn and default views
276 if(CFSetGetCount(initialViews) == 0) {
277 skipInitialSync = true;
278 CFSetUnion(initialViews, initialSyncDoneViews);
279 }
280 CFSetUnion(initialViews, backupViews);
281
282 // setting fullPeerInfo takes an extra ref, so...
283 self.fullPeerInfo = nil;
284 SOSFullPeerInfoRef fpi = SOSFullPeerInfoCreateWithViews(kCFAllocatorDefault, (__bridge CFDictionaryRef)(account.gestalt), (__bridge CFDataRef)(account.backup_key), initialViews, fullKey, octagonSigningFullKey, octagonEncryptionFullKey, error);
285 self.fullPeerInfo = fpi;
286 SecKeyRef pubKey = SOSFullPeerInfoCopyPubKey(fpi, NULL);
287 account.peerPublicKey = pubKey;
288 CFReleaseNull(pubKey);
289 if(!account.peerPublicKey) {
290 secnotice("circleOp", "Failed to copy peer public key for account object");
291 }
292 CFReleaseNull(fpi);
293
294 CFDictionaryRef v2dictionaryTestUpdates = [self getValueFromExpansion:kSOSTestV2Settings err:NULL];
295 if(v2dictionaryTestUpdates) SOSFullPeerInfoUpdateV2Dictionary(self.fullPeerInfo, v2dictionaryTestUpdates, NULL);
296
297 if(!skipInitialSync) {
298 [self pendEnableViewSet:initialSyncDoneViews];
299 [self setValueInExpansion:kSOSUnsyncedViewsKey value:kCFBooleanTrue err:NULL];
300 }
301
302 CFReleaseNull(initialViews);
303 CFReleaseNull(backupViews);
304 CFReleaseNull(initialSyncDoneViews);
305 CFReleaseNull(defaultViews);
306 }
307 else {
308 secerror("No full_key: %@:", error ? *error : NULL);
309
310 }
311
312 CFReleaseNull(fullKey);
313 CFReleaseNull(octagonSigningFullKey);
314 CFReleaseNull(octagonEncryptionFullKey);
315 CFReleaseNull(keyName);
316 }
317
318 fail:
319 return self.fullPeerInfo != NULL;
320 }
321 -(bool) isMyPeerActive:(CFErrorRef*) error
322 {
323 return (self.peerInfo ? SOSCircleHasActivePeer(self.trustedCircle, self.peerInfo, error) : false);
324 }
325
326 -(void) purgeIdentity
327 {
328 if (self.fullPeerInfo) {
329 // Purge private key but don't return error if we can't.
330 CFErrorRef purgeError = NULL;
331 if (!SOSFullPeerInfoPurgePersistentKey(self.fullPeerInfo, &purgeError)) {
332 secwarning("Couldn't purge persistent keys for %@ [%@]", self.fullPeerInfo, purgeError);
333 }
334 CFReleaseNull(purgeError);
335
336 self.fullPeerInfo=nil;
337 }
338 }
339 @end