]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/CKKSCheckKeyHierarchyOperation.m
Security-59754.41.1.tar.gz
[apple/security.git] / keychain / ckks / CKKSCheckKeyHierarchyOperation.m
1
2 #if OCTAGON
3
4 #import "keychain/categories/NSError+UsefulConstructors.h"
5 #import "keychain/ckks/CKKSCheckKeyHierarchyOperation.h"
6 #import "keychain/ckks/CKKSMirrorEntry.h"
7 #import "keychain/ot/OTDefines.h"
8 #import "utilities/SecTrace.h"
9
10 @implementation CKKSCheckKeyHierarchyOperation
11 @synthesize nextState = _nextState;
12 @synthesize intendedState = _intendedState;
13
14 - (instancetype)initWithDependencies:(CKKSOperationDependencies*)dependencies
15 intendedState:(OctagonState*)intendedState
16 errorState:(OctagonState*)errorState
17 {
18 if(self = [super init]) {
19 _deps = dependencies;
20
21 _intendedState = intendedState;
22 _nextState = errorState;
23 }
24 return self;
25 }
26
27 - (void)main {
28 NSArray<CKKSPeerProviderState*>* currentTrustStates = [self.deps currentTrustStates];
29
30 __block CKKSCurrentKeySet* set = nil;
31
32 [self.deps.databaseProvider dispatchSyncWithReadOnlySQLTransaction:^{
33 set = [CKKSCurrentKeySet loadForZone:self.deps.zoneID];
34 }];
35
36 // Drop off the sql queue: we can do the rest of this function with what we've already loaded
37
38 if(set.error && !([set.error.domain isEqual: @"securityd"] && set.error.code == errSecItemNotFound)) {
39 ckkserror("ckkskey", self.deps.zoneID, "Error examining existing key hierarchy: %@", set.error);
40 }
41
42 if(!set.currentTLKPointer && !set.currentClassAPointer && !set.currentClassCPointer) {
43 ckkserror("ckkskey", self.deps.zoneID, "Error examining existing key hierarchy (missing all CKPs, likely no hierarchy exists): %@", set);
44 self.nextState = SecCKKSZoneKeyStateWaitForTLKCreation;
45 return;
46 }
47
48 // Check keyset
49 if(!set.tlk || !set.classA || !set.classC) {
50 ckkserror("ckkskey", self.deps.zoneID, "Error examining existing key hierarchy (missing at least one key): %@", set);
51 self.error = set.error;
52 self.nextState = SecCKKSZoneKeyStateUnhealthy;
53 return;
54 }
55
56 NSError* localerror = nil;
57 bool probablyOkIfUnlocked = false;
58
59 // keychain being locked is not a fatal error here
60 [set.tlk loadKeyMaterialFromKeychain:&localerror];
61 if(localerror && ![self.deps.lockStateTracker isLockedError:localerror]) {
62 ckkserror("ckkskey", self.deps.zoneID, "Error loading TLK(%@): %@", set.tlk, localerror);
63 self.error = localerror;
64 self.nextState = SecCKKSZoneKeyStateUnhealthy;
65 return;
66 } else if(localerror) {
67 ckkserror("ckkskey", self.deps.zoneID, "Soft error loading TLK(%@), maybe locked: %@", set.tlk, localerror);
68 probablyOkIfUnlocked = true;
69 }
70 localerror = nil;
71
72 // keychain being locked is not a fatal error here
73 [set.classA loadKeyMaterialFromKeychain:&localerror];
74 if(localerror && ![self.deps.lockStateTracker isLockedError:localerror]) {
75 ckkserror("ckkskey", self.deps.zoneID, "Error loading classA key(%@): %@", set.classA, localerror);
76 self.error = localerror;
77 self.nextState = SecCKKSZoneKeyStateUnhealthy;
78 return;
79 } else if(localerror) {
80 ckkserror("ckkskey", self.deps.zoneID, "Soft error loading classA key(%@), maybe locked: %@", set.classA, localerror);
81 probablyOkIfUnlocked = true;
82 }
83 localerror = nil;
84
85 // keychain being locked is a fatal error here, since this is class C
86 [set.classC loadKeyMaterialFromKeychain:&localerror];
87 if(localerror) {
88 ckkserror("ckkskey", self.deps.zoneID, "Error loading classC(%@): %@", set.classC, localerror);
89 self.error = localerror;
90 self.nextState = SecCKKSZoneKeyStateUnhealthy;
91 return;
92 }
93
94 // Check that the classA and classC keys point to the current TLK
95 if(![set.classA.parentKeyUUID isEqualToString: set.tlk.uuid]) {
96 localerror = [NSError errorWithDomain:CKKSServerExtensionErrorDomain
97 code:CKKSServerUnexpectedSyncKeyInChain
98 userInfo:@{
99 NSLocalizedDescriptionKey: @"Current class A key does not wrap to current TLK",
100 }];
101 ckkserror("ckkskey", self.deps.zoneID, "Key hierarchy unhealthy: %@", localerror);
102 self.error = localerror;
103 self.nextState = SecCKKSZoneKeyStateUnhealthy;
104 return;
105 }
106 if(![set.classC.parentKeyUUID isEqualToString: set.tlk.uuid]) {
107 localerror = [NSError errorWithDomain:CKKSServerExtensionErrorDomain
108 code:CKKSServerUnexpectedSyncKeyInChain
109 userInfo:@{
110 NSLocalizedDescriptionKey: @"Current class C key does not wrap to current TLK",
111 }];
112 ckkserror("ckkskey", self.deps.zoneID, "Key hierarchy unhealthy: %@", localerror);
113 self.error = localerror;
114 self.nextState = SecCKKSZoneKeyStateUnhealthy;
115 return;
116 }
117
118 // Now that we're pretty sure we have the keys, are they shared appropriately?
119 // We need trust in order to proceed here
120 if(currentTrustStates.count == 0u) {
121 ckkserror("ckkskey", self.deps.zoneID, "Can't check TLKShares due to missing trust states");
122 [self.deps provideKeySet:set];
123 self.nextState = SecCKKSZoneKeyStateLoseTrust;
124 return;
125 }
126
127 // If we've reached this point, we have a workable keyset. Let's provide it to all waiters.
128 [self.deps provideKeySet:set];
129
130 if(probablyOkIfUnlocked) {
131 ckkserror("ckkskey", self.deps.zoneID, "Can't check TLKShares due to lock state");
132 [self.deps provideKeySet:set];
133 self.nextState = SecCKKSZoneKeyStateReadyPendingUnlock;
134 return;
135 }
136
137 // Check that every trusted peer has at least one TLK share
138 // If any trust state check works, don't error out
139 bool anyTrustStateSucceeded = false;
140 for(CKKSPeerProviderState* trustState in currentTrustStates) {
141 NSSet<id<CKKSPeer>>* missingShares = [trustState findPeersMissingTLKSharesFor:set
142 error:&localerror];
143
144 if(localerror && [self.deps.lockStateTracker isLockedError:localerror]) {
145 ckkserror("ckkskey", self.deps.zoneID, "Couldn't find missing TLK shares due to lock state: %@", localerror);
146 continue;
147
148 } else if(([localerror.domain isEqualToString:TrustedPeersHelperErrorDomain] && localerror.code == TrustedPeersHelperErrorNoPreparedIdentity) ||
149 ([localerror.domain isEqualToString:CKKSErrorDomain] && localerror.code == CKKSLackingTrust) ||
150 ([localerror.domain isEqualToString:CKKSErrorDomain] && localerror.code == CKKSNoPeersAvailable)) {
151 ckkserror("ckkskey", self.deps.zoneID, "Couldn't find missing TLK shares due some trust issue: %@", localerror);
152
153 if(trustState.essential) {
154 ckkserror("ckkskey", self.deps.zoneID, "Trust state is considered essential; entering waitfortrust: %@", trustState);
155
156 // Octagon can reinform us when it thinks we should start again
157 self.nextState = SecCKKSZoneKeyStateLoseTrust;
158
159 return;
160 } else {
161 ckkserror("ckkskey", self.deps.zoneID, "Peer provider is considered nonessential; ignoring error: %@", trustState);
162 continue;
163 }
164
165 } else if(localerror) {
166 ckkserror("ckkskey", self.deps.zoneID, "Error finding missing TLK shares: %@", localerror);
167 continue;
168 }
169
170 if(!missingShares || missingShares.count != 0u) {
171 ckksnotice("ckksshare", self.deps.zoneID, "TLK (%@) is not shared correctly for trust state %@, but we believe AKS is locked", set.tlk, trustState.peerProviderID);
172
173 self.error = [NSError errorWithDomain:CKKSErrorDomain
174 code:CKKSMissingTLKShare
175 description:[NSString stringWithFormat:@"Missing shares for %lu peers", (unsigned long)missingShares.count]];
176 self.nextState = SecCKKSZoneKeyStateHealTLKShares;
177 return;
178 } else {
179 ckksnotice("ckksshare", self.deps.zoneID, "TLK (%@) is shared correctly for trust state %@", set.tlk, trustState.peerProviderID);
180 }
181
182 anyTrustStateSucceeded |= true;
183 }
184
185 if(!anyTrustStateSucceeded) {
186 self.error = localerror;
187 self.nextState = SecCKKSZoneKeyStateError;
188
189 return;
190 }
191
192 // Got to the bottom? Cool! All keys are present and accounted for.
193 self.nextState = SecCKKSZoneKeyStateReady;
194 }
195
196 @end
197
198 #endif // OCTAGON