CKKSPeerProvider.m [plain text]
#if OCTAGON
#import "keychain/ckks/CKKS.h"
#import "keychain/ckks/CKKSKey.h"
#import "keychain/ckks/CKKSPeerProvider.h"
#import "keychain/ckks/CKKSTLKShareRecord.h"
#import "keychain/categories/NSError+UsefulConstructors.h"
@implementation CKKSPeerProviderState
- (instancetype)initWithPeerProviderID:(NSString*)providerID
essential:(BOOL)essential
selfPeers:(CKKSSelves* _Nullable)selfPeers
selfPeersError:(NSError* _Nullable)selfPeersError
trustedPeers:(NSSet<id<CKKSRemotePeerProtocol>>* _Nullable)currentTrustedPeers
trustedPeersError:(NSError* _Nullable)trustedPeersError
{
if((self = [super init])) {
_peerProviderID = providerID;
_essential = essential;
_currentSelfPeers = selfPeers;
_currentSelfPeersError = selfPeersError;
_currentTrustedPeers = currentTrustedPeers;
_currentTrustedPeersError = trustedPeersError;
if(_currentTrustedPeers) {
NSMutableSet<NSString*>* trustedPeerIDs = [NSMutableSet set];
for(id<CKKSPeer> peer in _currentTrustedPeers) {
[trustedPeerIDs addObject:peer.peerID];
}
_currentTrustedPeerIDs = trustedPeerIDs;
}
}
return self;
}
- (NSString*)description
{
return [NSString stringWithFormat:@"<CKKSPeerProviderState( self.peerProviderID,
self.currentSelfPeers,
self.currentSelfPeersError ?: @"",
self.currentTrustedPeers,
self.currentTrustedPeersError ?: @""];
}
+ (CKKSPeerProviderState*)noPeersState:(id<CKKSPeerProvider>)provider
{
return [[CKKSPeerProviderState alloc] initWithPeerProviderID:provider.providerID
essential:provider.essential
selfPeers:nil
selfPeersError:[NSError errorWithDomain:CKKSErrorDomain
code:CKKSNoPeersAvailable
description:@"No current self peer available"]
trustedPeers:nil
trustedPeersError:[NSError errorWithDomain:CKKSErrorDomain
code:CKKSNoPeersAvailable
description:@"No current trusted peers available"]];
}
+ (CKKSPeerProviderState*)createFromProvider:(id<CKKSPeerProvider>)provider
{
NSError* selfPeersError = nil;
CKKSSelves* currentSelfPeers = [provider fetchSelfPeers:&selfPeersError];
NSError* trustedPeersError = nil;
NSSet<id<CKKSRemotePeerProtocol>>* currentTrustedPeers = [provider fetchTrustedPeers:&trustedPeersError];
return [[CKKSPeerProviderState alloc] initWithPeerProviderID:provider.providerID
essential:provider.essential
selfPeers:currentSelfPeers
selfPeersError:selfPeersError
trustedPeers:currentTrustedPeers
trustedPeersError:trustedPeersError];
}
// For this key, who doesn't yet have a valid CKKSTLKShare for it?
// Note that we really want a record sharing the TLK to ourselves, so this function might return
// a non-empty set even if all peers have the TLK: it wants us to make a record for ourself.
- (NSSet<id<CKKSPeer>>* _Nullable)findPeersMissingTLKSharesFor:(CKKSCurrentKeySet*)keyset
error:(NSError**)error
{
if(self.currentTrustedPeersError) {
ckkserror("ckksshare", keyset.tlk, "Couldn't find missing shares because trusted peers aren't available: if(error) {
*error = self.currentTrustedPeersError;
}
return [NSSet set];
}
if(self.currentSelfPeersError) {
ckkserror("ckksshare", keyset.tlk, "Couldn't find missing shares because self peers aren't available: if(error) {
*error = self.currentSelfPeersError;
}
return [NSSet set];
}
NSArray<CKKSTLKShareRecord*>* tlkShares = [keyset.tlkShares arrayByAddingObjectsFromArray:keyset.pendingTLKShares ?: @[]];
NSMutableSet<id<CKKSPeer>>* peersMissingShares = [NSMutableSet set];
// Ensure that the 'self peer' is one of the current trusted peers. Otherwise, any TLKShare we create
// won't be considered trusted the next time through...
if(![self.currentTrustedPeerIDs containsObject:self.currentSelfPeers.currentSelf.peerID]) {
ckkserror("ckksshare", keyset.tlk, "current self peer ( self.currentSelfPeers.currentSelf.peerID,
self.currentTrustedPeerIDs);
if(error) {
*error = [NSError errorWithDomain:CKKSErrorDomain
code:CKKSLackingTrust
description:[NSString stringWithFormat:@"current self peer ( self.currentSelfPeers.currentSelf.peerID]];
}
return nil;
}
for(id<CKKSRemotePeerProtocol> peer in self.currentTrustedPeers) {
if(![peer shouldHaveView:keyset.tlk.zoneName]) {
ckkserror("ckksshare", keyset.tlk.keycore.zoneID, "Peer ( continue;
}
// Determine if we think this peer has enough things shared to them
bool alreadyShared = false;
for(CKKSTLKShareRecord* existingShare in tlkShares) {
// Ensure this share is to this peer...
if(![existingShare.share.receiverPeerID isEqualToString:peer.peerID]) {
continue;
}
// If an SOS Peer sent this share, is its signature still valid? Or did the signing key change?
if([existingShare.senderPeerID hasPrefix:CKKSSOSPeerPrefix]) {
NSError* signatureError = nil;
if(![existingShare signatureVerifiesWithPeerSet:self.currentTrustedPeers error:&signatureError]) {
ckksnotice("ckksshare", keyset.tlk, "Existing TLKShare's signature doesn't verify with current peer set: continue;
}
}
if([existingShare.tlkUUID isEqualToString:keyset.tlk.uuid] && [self.currentTrustedPeerIDs containsObject:existingShare.senderPeerID]) {
// Was this shared to us?
if([peer.peerID isEqualToString:self.currentSelfPeers.currentSelf.peerID]) {
// We only count this as 'found' if we did the sharing and it's to our current keys
NSData* currentKey = self.currentSelfPeers.currentSelf.publicEncryptionKey.keyData;
if([existingShare.senderPeerID isEqualToString:self.currentSelfPeers.currentSelf.peerID] &&
[existingShare.share.receiverPublicEncryptionKeySPKI isEqual:currentKey]) {
ckksnotice("ckksshare", keyset.tlk, "Local peer alreadyShared = true;
break;
} else {
ckksnotice("ckksshare", keyset.tlk, "Local peer }
} else {
// Was this shared to the remote peer's current keys?
NSData* currentKeySPKI = peer.publicEncryptionKey.keyData;
if([existingShare.share.receiverPublicEncryptionKeySPKI isEqual:currentKeySPKI]) {
// Some other peer has a trusted share. Cool!
ckksnotice("ckksshare", keyset.tlk, "Peer alreadyShared = true;
break;
} else {
ckksnotice("ckksshare", keyset.tlk, "Peer }
}
}
}
if(!alreadyShared) {
// Add this peer to our set, if it has an encryption key to receive the share
if(peer.publicEncryptionKey) {
[peersMissingShares addObject:peer];
}
}
}
if(peersMissingShares.count > 0u) {
// Log each and every one of the things
ckksnotice("ckksshare", keyset.tlk, "Missing TLK shares for ckksnotice("ckksshare", keyset.tlk, "Self peers are ( ckksnotice("ckksshare", keyset.tlk, "Trusted peers are ( }
return peersMissingShares;
}
- (BOOL)unwrapKey:(CKKSKey*)proposedTLK
fromShares:(NSArray<CKKSTLKShareRecord*>*)tlkShares
error:(NSError**)error
{
if(!self.currentSelfPeers.currentSelf || self.currentSelfPeersError) {
ckkserror("ckksshare", proposedTLK, "Don't have self peers for if(error) {
*error = [NSError errorWithDomain:CKKSErrorDomain
code:CKKSNoEncryptionKey
description:@"Key unwrap failed"
underlying:self.currentSelfPeersError];;
}
return NO;
}
if(!self.currentTrustedPeers || self.currentTrustedPeersError) {
ckkserror("ckksshare", proposedTLK, "Don't have trusted peers: if(error) {
*error = [NSError errorWithDomain:CKKSErrorDomain
code:CKKSNoPeersAvailable
description:@"No trusted peers"
underlying:self.currentTrustedPeersError];
}
return NO;
}
NSError* lastShareError = nil;
for(id<CKKSSelfPeer> selfPeer in self.currentSelfPeers.allSelves) {
NSMutableArray<CKKSTLKShareRecord*>* possibleShares = [NSMutableArray array];
for(CKKSTLKShareRecord* share in tlkShares) {
if([share.share.receiverPeerID isEqualToString:selfPeer.peerID]) {
[possibleShares addObject:share];
}
}
if(possibleShares.count == 0) {
ckksnotice("ckksshare", proposedTLK, "No CKKSTLKShares to continue;
}
for(CKKSTLKShareRecord* possibleShare in possibleShares) {
NSError* possibleShareError = nil;
ckksnotice("ckksshare", proposedTLK, "Checking possible TLK share
CKKSKey* possibleKey = [possibleShare recoverTLK:selfPeer
trustedPeers:self.currentTrustedPeers
error:&possibleShareError];
if(!possibleKey || possibleShareError) {
ckkserror("ckksshare", proposedTLK, "Unable to unwrap TLKShare( possibleShare, selfPeer, possibleShareError);
ckkserror("ckksshare", proposedTLK, "Current trust set: lastShareError = possibleShareError;
continue;
}
bool result = [proposedTLK trySelfWrappedKeyCandidate:possibleKey.aessivkey error:&possibleShareError];
if(!result || possibleShareError) {
ckkserror("ckksshare", proposedTLK, "Unwrapped TLKShare( possibleShare, proposedTLK, self.currentSelfPeers.currentSelf, possibleShareError);
lastShareError = possibleShareError;
continue;
}
ckksnotice("ckksshare", proposedTLK, "TLKShare( possibleShare, proposedTLK, selfPeer);
return YES;
}
}
if(error) {
*error = [NSError errorWithDomain:CKKSErrorDomain
code:CKKSNoTrustedTLKShares
description:[NSString stringWithFormat:@"No trusted TLKShares for underlying:lastShareError];
}
return NO;
}
@end
#endif