]> git.saurik.com Git - apple/security.git/blob - keychain/SecureObjectSync/SOSAccountTrustClassic+Identity.m
Security-59306.11.20.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 #if __OBJC2__
14 #import "Analytics/Clients/SOSAnalytics.h"
15 #endif // __OBJC2__
16
17 #import "keychain/SecureObjectSync/SOSViews.h"
18
19 @implementation SOSAccountTrustClassic (Identity)
20
21 -(bool)isLockedError:(NSError *)error {
22 return error &&
23 ([error.domain isEqualToString:(__bridge NSString*)kSecErrorDomain])
24 && error.code == errSecInteractionNotAllowed;
25 }
26
27 -(bool) updateFullPeerInfo:(SOSAccount*)account minimum:(CFSetRef)minimumViews excluded:(CFSetRef)excludedViews
28 {
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);
34 }];
35 }
36 }
37
38 return true;
39 }
40
41 -(SOSFullPeerInfoRef) getMyFullPeerInfo
42 {
43 return self.trustedCircle ? self.fullPeerInfo : NULL;
44 }
45
46 -(bool) fullPeerInfoVerify:(SecKeyRef) privKey err:(CFErrorRef *)error
47 {
48 if(!self.fullPeerInfo) return false;
49 SecKeyRef pubKey = SecKeyCreatePublicFromPrivate(privKey);
50 bool retval = SOSPeerInfoApplicationVerify(self.peerInfo, pubKey, error);
51 CFReleaseNull(pubKey);
52 return retval;
53 }
54
55 -(bool) hasFullPeerInfo:(CFErrorRef*) error
56 {
57 bool hasPeer = false;
58 if(![self hasCircle:error]){
59 return hasPeer;
60 }
61 hasPeer = self.fullPeerInfo != NULL;
62
63 if (!hasPeer)
64 SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("No peer for circle"));
65
66 return hasPeer;
67 }
68
69 -(SOSFullPeerInfoRef) CopyAccountIdentityPeerInfo
70 {
71 return SOSFullPeerInfoCopyFullPeerInfo(self.fullPeerInfo);
72 }
73
74 - (SecKeyRef)randomPermanentFullECKey:(int)keysize name:(NSString *)name error:(CFErrorRef*)cferror CF_RETURNS_RETAINED
75 {
76 return GeneratePermanentFullECKey(keysize, (__bridge CFStringRef)name, cferror);
77 }
78
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
81 // all is swell.
82 #if OCTAGON
83 - (bool)haveConfirmedOctagonKeys
84 {
85 bool haveSigningKey = false;
86 bool haveEncryptionKey = false;
87
88 SecKeyRef signingKey = SOSFullPeerInfoCopyOctagonPublicSigningKey(self.fullPeerInfo, NULL);
89 if (self.cachedOctagonSigningKey && CFEqualSafe(signingKey, self.cachedOctagonSigningKey)) {
90 haveSigningKey = true;
91 } else {
92 secerror("circleChange: No extant octagon signing key");
93 }
94
95 SecKeyRef encrytionKey = SOSFullPeerInfoCopyOctagonPublicEncryptionKey(self.fullPeerInfo, NULL);
96 if (self.cachedOctagonEncryptionKey && CFEqualSafe(encrytionKey, self.cachedOctagonEncryptionKey)) {
97 haveEncryptionKey = true;
98 } else {
99 secerror("circleChange: No extant octagon encryption key");
100 }
101
102 CFReleaseNull(signingKey);
103 CFReleaseNull(encrytionKey);
104
105 return haveSigningKey && haveEncryptionKey;
106 }
107 #endif
108
109 - (void)ensureOctagonPeerKeys:(SOSKVSCircleStorageTransport*)circleTransport
110 {
111 #if OCTAGON
112 NSString* octagonKeyName;
113 SecKeyRef octagonSigningFullKey = NULL;
114 SecKeyRef octagonEncryptionFullKey = NULL;
115
116 // check if we already confirmed the keys
117 if ([self haveConfirmedOctagonKeys]) {
118 return;
119 }
120
121 bool changedSelf = false;
122
123 CFErrorRef copyError = NULL;
124 octagonSigningFullKey = SOSFullPeerInfoCopyOctagonSigningKey(self.fullPeerInfo, &copyError);
125 if(copyError && ![self isLockedError:(__bridge NSError *)copyError]) {
126 secerror("circleChange: Error fetching Octagon signing key: %@", copyError);
127 }
128
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.
133
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)))
142 {
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);
148 } else {
149 SOSFullPeerInfoUpdateOctagonSigningKey(self.fullPeerInfo, octagonSigningFullKey, &cferror);
150 if(cferror) {
151 secerror("circleChange: Error upgrading Octagon signing key: %@", cferror);
152 } else {
153 secnotice("circleChange", "Successfully created new Octagon signing key");
154 }
155 changedSelf = true;
156 }
157
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);
161 #if __OBJC2__
162 [[SOSAnalytics logger] logResultForEvent:@"SOSCheckOctagonSigningKey" hardFailure:true result:(__bridge NSError*)copyError];
163 #endif // __OBJC2__
164 }
165
166 CFReleaseNull(copyError);
167 CFReleaseNull(octagonSigningFullKey);
168
169 // Now do the same thing for encryption key
170
171 CFReleaseNull(copyError);
172 octagonEncryptionFullKey = SOSFullPeerInfoCopyOctagonEncryptionKey(self.fullPeerInfo, &copyError);
173 if(copyError && ![self isLockedError:(__bridge NSError *)copyError]) {
174 secerror("circleChange: Error fetching Octagon encryption key: %@", copyError);
175 }
176
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)))
185 {
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);
191 } else {
192 SOSFullPeerInfoUpdateOctagonEncryptionKey(self.fullPeerInfo, octagonEncryptionFullKey, &cferror);
193 if(cferror) {
194 secerror("circleChange: Error upgrading Octagon encryption key: %@", cferror);
195 } else {
196 secnotice("circleChange", "Successfully created new Octagon encryption key");
197 }
198 changedSelf = true;
199 }
200
201 CFReleaseNull(cferror);
202
203 } else if((octagonEncryptionFullKey == NULL || copyError) && ![self isLockedError:(__bridge NSError *)copyError]) {
204 secerror("error is too scary, not creating new Octagon encryption key: %@", copyError);
205 #if __OBJC2__
206 [[SOSAnalytics logger] logResultForEvent:@"SOSCheckOctagonEncryptionKey" hardFailure:true result:(__bridge NSError*)copyError];
207 #endif
208 }
209 CFReleaseNull(copyError);
210 CFReleaseNull(octagonEncryptionFullKey);
211
212 if(changedSelf) {
213 [self modifyCircle:circleTransport err:NULL action:^bool (SOSCircleRef circle_to_change) {
214 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(self.fullPeerInfo));
215 }];
216 }
217 #endif /* OCTAGON */
218 }
219
220 -(bool) ensureFullPeerAvailable:(CFDictionaryRef)gestalt deviceID:(CFStringRef)deviceID backupKey:(CFDataRef)backup err:(CFErrorRef *) error
221 {
222 require_action_quiet(self.trustedCircle, fail, SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("Don't have circle")));
223
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];
229
230 octagonKeyName = [@"Octagon Peer Signing " stringByAppendingString:(__bridge NSString*)keyName];
231 SecKeyRef octagonSigningFullKey = [self randomPermanentFullECKey:384 name:octagonKeyName error:NULL];
232
233 octagonKeyName = [@"Octagon Peer Encryption " stringByAppendingString:(__bridge NSString*)keyName];
234 SecKeyRef octagonEncryptionFullKey = [self randomPermanentFullECKey:384 name:octagonKeyName error:NULL];
235
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);
241
242 CFSetUnion(initialSyncDoneViews, defaultViews);
243
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);
248 }
249 CFSetUnion(initialViews, backupViews);
250
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;
255 CFReleaseNull(fpi);
256
257 CFDictionaryRef v2dictionaryTestUpdates = [self getValueFromExpansion:kSOSTestV2Settings err:NULL];
258 if(v2dictionaryTestUpdates) SOSFullPeerInfoUpdateV2Dictionary(self.fullPeerInfo, v2dictionaryTestUpdates, NULL);
259
260 if(!skipInitialSync) {
261 [self pendEnableViewSet:initialSyncDoneViews];
262 [self setValueInExpansion:kSOSUnsyncedViewsKey value:kCFBooleanTrue err:NULL];
263 }
264
265 CFReleaseNull(initialViews);
266 CFReleaseNull(backupViews);
267 CFReleaseNull(initialSyncDoneViews);
268 CFReleaseNull(defaultViews);
269 }
270 else {
271 secerror("No full_key: %@:", error ? *error : NULL);
272
273 }
274
275 CFReleaseNull(full_key);
276 CFReleaseNull(octagonSigningFullKey);
277 CFReleaseNull(octagonEncryptionFullKey);
278 CFReleaseNull(keyName);
279 }
280
281 fail:
282 return self.fullPeerInfo != NULL;
283 }
284 -(bool) isMyPeerActive:(CFErrorRef*) error
285 {
286 return (self.peerInfo ? SOSCircleHasActivePeer(self.trustedCircle, self.peerInfo, error) : false);
287 }
288
289 -(void) purgeIdentity
290 {
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);
296 }
297 CFReleaseNull(purgeError);
298
299 self.fullPeerInfo=nil;
300 }
301 }
302 @end