]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/CKKSPeerProvider.m
Security-59754.41.1.tar.gz
[apple/security.git] / keychain / ckks / CKKSPeerProvider.m
1 #if OCTAGON
2 #import "keychain/ckks/CKKS.h"
3 #import "keychain/ckks/CKKSKey.h"
4 #import "keychain/ckks/CKKSPeerProvider.h"
5 #import "keychain/ckks/CKKSTLKShareRecord.h"
6 #import "keychain/categories/NSError+UsefulConstructors.h"
7
8 @implementation CKKSPeerProviderState
9 - (instancetype)initWithPeerProviderID:(NSString*)providerID
10 essential:(BOOL)essential
11 selfPeers:(CKKSSelves* _Nullable)selfPeers
12 selfPeersError:(NSError* _Nullable)selfPeersError
13 trustedPeers:(NSSet<id<CKKSRemotePeerProtocol>>* _Nullable)currentTrustedPeers
14 trustedPeersError:(NSError* _Nullable)trustedPeersError
15 {
16 if((self = [super init])) {
17 _peerProviderID = providerID;
18 _essential = essential;
19 _currentSelfPeers = selfPeers;
20 _currentSelfPeersError = selfPeersError;
21 _currentTrustedPeers = currentTrustedPeers;
22 _currentTrustedPeersError = trustedPeersError;
23
24 if(_currentTrustedPeers) {
25 NSMutableSet<NSString*>* trustedPeerIDs = [NSMutableSet set];
26 for(id<CKKSPeer> peer in _currentTrustedPeers) {
27 [trustedPeerIDs addObject:peer.peerID];
28 }
29 _currentTrustedPeerIDs = trustedPeerIDs;
30 }
31 }
32 return self;
33 }
34
35 - (NSString*)description
36 {
37 return [NSString stringWithFormat:@"<CKKSPeerProviderState(%@): %@%@ %@%@>",
38 self.peerProviderID,
39 self.currentSelfPeers,
40 self.currentSelfPeersError ?: @"",
41 self.currentTrustedPeers,
42 self.currentTrustedPeersError ?: @""];
43 }
44
45 + (CKKSPeerProviderState*)noPeersState:(id<CKKSPeerProvider>)provider
46 {
47 return [[CKKSPeerProviderState alloc] initWithPeerProviderID:provider.providerID
48 essential:provider.essential
49 selfPeers:nil
50 selfPeersError:[NSError errorWithDomain:CKKSErrorDomain
51 code:CKKSNoPeersAvailable
52 description:@"No current self peer available"]
53 trustedPeers:nil
54 trustedPeersError:[NSError errorWithDomain:CKKSErrorDomain
55 code:CKKSNoPeersAvailable
56 description:@"No current trusted peers available"]];
57 }
58
59
60 + (CKKSPeerProviderState*)createFromProvider:(id<CKKSPeerProvider>)provider
61 {
62 NSError* selfPeersError = nil;
63 CKKSSelves* currentSelfPeers = [provider fetchSelfPeers:&selfPeersError];
64
65 NSError* trustedPeersError = nil;
66 NSSet<id<CKKSRemotePeerProtocol>>* currentTrustedPeers = [provider fetchTrustedPeers:&trustedPeersError];
67
68 return [[CKKSPeerProviderState alloc] initWithPeerProviderID:provider.providerID
69 essential:provider.essential
70 selfPeers:currentSelfPeers
71 selfPeersError:selfPeersError
72 trustedPeers:currentTrustedPeers
73 trustedPeersError:trustedPeersError];
74 }
75
76 // For this key, who doesn't yet have a valid CKKSTLKShare for it?
77 // Note that we really want a record sharing the TLK to ourselves, so this function might return
78 // a non-empty set even if all peers have the TLK: it wants us to make a record for ourself.
79 - (NSSet<id<CKKSPeer>>* _Nullable)findPeersMissingTLKSharesFor:(CKKSCurrentKeySet*)keyset
80 error:(NSError**)error
81 {
82 if(self.currentTrustedPeersError) {
83 ckkserror("ckksshare", keyset.tlk, "Couldn't find missing shares because trusted peers aren't available: %@", self.currentTrustedPeersError);
84 if(error) {
85 *error = self.currentTrustedPeersError;
86 }
87 return [NSSet set];
88 }
89 if(self.currentSelfPeersError) {
90 ckkserror("ckksshare", keyset.tlk, "Couldn't find missing shares because self peers aren't available: %@", self.currentSelfPeersError);
91 if(error) {
92 *error = self.currentSelfPeersError;
93 }
94 return [NSSet set];
95 }
96
97 NSArray<CKKSTLKShareRecord*>* tlkShares = [keyset.tlkShares arrayByAddingObjectsFromArray:keyset.pendingTLKShares ?: @[]];
98
99 NSMutableSet<id<CKKSPeer>>* peersMissingShares = [NSMutableSet set];
100
101 // Ensure that the 'self peer' is one of the current trusted peers. Otherwise, any TLKShare we create
102 // won't be considered trusted the next time through...
103 if(![self.currentTrustedPeerIDs containsObject:self.currentSelfPeers.currentSelf.peerID]) {
104 ckkserror("ckksshare", keyset.tlk, "current self peer (%@) is not in the set of trusted peers: %@",
105 self.currentSelfPeers.currentSelf.peerID,
106 self.currentTrustedPeerIDs);
107
108 if(error) {
109 *error = [NSError errorWithDomain:CKKSErrorDomain
110 code:CKKSLackingTrust
111 description:[NSString stringWithFormat:@"current self peer (%@) is not in the set of trusted peers",
112 self.currentSelfPeers.currentSelf.peerID]];
113 }
114
115 return nil;
116 }
117
118 for(id<CKKSRemotePeerProtocol> peer in self.currentTrustedPeers) {
119 if(![peer shouldHaveView:keyset.tlk.zoneName]) {
120 ckkserror("ckksshare", keyset.tlk.keycore.zoneID, "Peer (%@) is not supposed to have view, skipping", peer);
121 continue;
122 }
123
124 // Determine if we think this peer has enough things shared to them
125 bool alreadyShared = false;
126 for(CKKSTLKShareRecord* existingShare in tlkShares) {
127 // Ensure this share is to this peer...
128 if(![existingShare.share.receiverPeerID isEqualToString:peer.peerID]) {
129 continue;
130 }
131
132 // If an SOS Peer sent this share, is its signature still valid? Or did the signing key change?
133 if([existingShare.senderPeerID hasPrefix:CKKSSOSPeerPrefix]) {
134 NSError* signatureError = nil;
135 if(![existingShare signatureVerifiesWithPeerSet:self.currentTrustedPeers error:&signatureError]) {
136 ckksnotice("ckksshare", keyset.tlk, "Existing TLKShare's signature doesn't verify with current peer set: %@ %@", signatureError, existingShare);
137 continue;
138 }
139 }
140
141 if([existingShare.tlkUUID isEqualToString:keyset.tlk.uuid] && [self.currentTrustedPeerIDs containsObject:existingShare.senderPeerID]) {
142 // Was this shared to us?
143 if([peer.peerID isEqualToString:self.currentSelfPeers.currentSelf.peerID]) {
144 // We only count this as 'found' if we did the sharing and it's to our current keys
145 NSData* currentKey = self.currentSelfPeers.currentSelf.publicEncryptionKey.keyData;
146
147 if([existingShare.senderPeerID isEqualToString:self.currentSelfPeers.currentSelf.peerID] &&
148 [existingShare.share.receiverPublicEncryptionKeySPKI isEqual:currentKey]) {
149 ckksnotice("ckksshare", keyset.tlk, "Local peer %@ is shared %@ via self: %@", peer, keyset.tlk, existingShare);
150 alreadyShared = true;
151 break;
152 } else {
153 ckksnotice("ckksshare", keyset.tlk, "Local peer %@ is shared %@ via trusted %@, but that's not good enough", peer, keyset.tlk, existingShare);
154 }
155
156 } else {
157 // Was this shared to the remote peer's current keys?
158 NSData* currentKeySPKI = peer.publicEncryptionKey.keyData;
159
160 if([existingShare.share.receiverPublicEncryptionKeySPKI isEqual:currentKeySPKI]) {
161 // Some other peer has a trusted share. Cool!
162 ckksnotice("ckksshare", keyset.tlk, "Peer %@ is shared %@ via trusted %@", peer, keyset.tlk, existingShare);
163 alreadyShared = true;
164 break;
165 } else {
166 ckksnotice("ckksshare", keyset.tlk, "Peer %@ has a share for %@, but to old keys: %@", peer, keyset.tlk, existingShare);
167 }
168 }
169 }
170 }
171
172 if(!alreadyShared) {
173 // Add this peer to our set, if it has an encryption key to receive the share
174 if(peer.publicEncryptionKey) {
175 [peersMissingShares addObject:peer];
176 }
177 }
178 }
179
180 if(peersMissingShares.count > 0u) {
181 // Log each and every one of the things
182 ckksnotice("ckksshare", keyset.tlk, "Missing TLK shares for %lu peers: %@", (unsigned long)peersMissingShares.count, peersMissingShares);
183 ckksnotice("ckksshare", keyset.tlk, "Self peers are (%@) %@", self.currentSelfPeersError ?: @"no error", self.currentSelfPeers);
184 ckksnotice("ckksshare", keyset.tlk, "Trusted peers are (%@) %@", self.currentTrustedPeersError ?: @"no error", self.currentTrustedPeers);
185 }
186
187 return peersMissingShares;
188 }
189
190 - (BOOL)unwrapKey:(CKKSKey*)proposedTLK
191 fromShares:(NSArray<CKKSTLKShareRecord*>*)tlkShares
192 error:(NSError**)error
193 {
194 if(!self.currentSelfPeers.currentSelf || self.currentSelfPeersError) {
195 ckkserror("ckksshare", proposedTLK, "Don't have self peers for %@: %@", self.peerProviderID, self.currentSelfPeersError);
196 if(error) {
197 *error = [NSError errorWithDomain:CKKSErrorDomain
198 code:CKKSNoEncryptionKey
199 description:@"Key unwrap failed"
200 underlying:self.currentSelfPeersError];;
201 }
202 return NO;
203 }
204
205 if(!self.currentTrustedPeers || self.currentTrustedPeersError) {
206 ckkserror("ckksshare", proposedTLK, "Don't have trusted peers: %@", self.currentTrustedPeersError);
207 if(error) {
208 *error = [NSError errorWithDomain:CKKSErrorDomain
209 code:CKKSNoPeersAvailable
210 description:@"No trusted peers"
211 underlying:self.currentTrustedPeersError];
212 }
213 return NO;
214 }
215
216 NSError* lastShareError = nil;
217
218 for(id<CKKSSelfPeer> selfPeer in self.currentSelfPeers.allSelves) {
219 NSMutableArray<CKKSTLKShareRecord*>* possibleShares = [NSMutableArray array];
220
221 for(CKKSTLKShareRecord* share in tlkShares) {
222 if([share.share.receiverPeerID isEqualToString:selfPeer.peerID]) {
223 [possibleShares addObject:share];
224 }
225 }
226
227 if(possibleShares.count == 0) {
228 ckksnotice("ckksshare", proposedTLK, "No CKKSTLKShares to %@ for %@", selfPeer, proposedTLK);
229 continue;
230 }
231
232 for(CKKSTLKShareRecord* possibleShare in possibleShares) {
233 NSError* possibleShareError = nil;
234 ckksnotice("ckksshare", proposedTLK, "Checking possible TLK share %@ as %@", possibleShare, selfPeer);
235
236 CKKSKey* possibleKey = [possibleShare recoverTLK:selfPeer
237 trustedPeers:self.currentTrustedPeers
238 error:&possibleShareError];
239
240 if(!possibleKey || possibleShareError) {
241 ckkserror("ckksshare", proposedTLK, "Unable to unwrap TLKShare(%@) as %@: %@",
242 possibleShare, selfPeer, possibleShareError);
243 ckkserror("ckksshare", proposedTLK, "Current trust set: %@", self.currentTrustedPeers);
244 lastShareError = possibleShareError;
245 continue;
246 }
247
248 bool result = [proposedTLK trySelfWrappedKeyCandidate:possibleKey.aessivkey error:&possibleShareError];
249 if(!result || possibleShareError) {
250 ckkserror("ckksshare", proposedTLK, "Unwrapped TLKShare(%@) does not unwrap proposed TLK(%@) as %@: %@",
251 possibleShare, proposedTLK, self.currentSelfPeers.currentSelf, possibleShareError);
252 lastShareError = possibleShareError;
253 continue;
254 }
255
256 ckksnotice("ckksshare", proposedTLK, "TLKShare(%@) unlocked TLK(%@) as %@",
257 possibleShare, proposedTLK, selfPeer);
258
259 return YES;
260 }
261 }
262
263 if(error) {
264 *error = [NSError errorWithDomain:CKKSErrorDomain
265 code:CKKSNoTrustedTLKShares
266 description:[NSString stringWithFormat:@"No trusted TLKShares for %@", proposedTLK]
267 underlying:lastShareError];
268 }
269
270 return NO;
271 }
272
273 @end
274
275 #endif
276