/*
* Copyright (c) 2017 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#if OCTAGON
#import "OTIdentity.h"
#import <SecurityFoundation/SFKey.h>
#import <SecurityFoundation/SFKey_Private.h>
#import "keychain/ot/OTDefines.h"
#import <Security/SecureObjectSync/SOSAccountTransaction.h>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#import <Security/SecureObjectSync/SOSAccount.h>
#pragma clang diagnostic pop
@interface OTIdentity ()
@property (nonatomic, strong) NSString* peerID;
@property (nonatomic, strong) NSString* spID;
@property (nonatomic, strong) SFECKeyPair* peerSigningKey;
@property (nonatomic, strong) SFECKeyPair* peerEncryptionKey;
@end
@implementation OTIdentity
- (instancetype) initWithPeerID:(nullable NSString*)peerID
spID:(nullable NSString*)spID
peerSigningKey:(SFECKeyPair*)peerSigningKey
peerEncryptionkey:(SFECKeyPair*)peerEncryptionKey
error:(NSError**)error
{
self = [super init];
if (self) {
_peerID = peerID;
_spID = spID;
_peerSigningKey = peerSigningKey;
_peerEncryptionKey = peerEncryptionKey;
}
return self;
}
+ (nullable instancetype) currentIdentityFromSOS:(NSError**)error
{
CFErrorRef circleCheckError = NULL;
SOSCCStatus circleStatus = SOSCCThisDeviceIsInCircle(&circleCheckError);
if(circleStatus != kSOSCCInCircle){
if(circleCheckError){
secerror("octagon: cannot retrieve octagon keys from SOS, not in circle, error: if(error){
*error = (__bridge NSError*)circleCheckError;
}
}
secerror("octagon: current circle status: return nil;
}
__block NSString* sosPeerID = nil;
__block NSError* sosPeerIDError = nil;
SOSCCPerformWithPeerID(^(CFStringRef peerID, CFErrorRef error) {
sosPeerID = (__bridge NSString *)(peerID);
if(error){
secerror("octagon: retrieving sos peer id error: sosPeerIDError = CFBridgingRelease(error);
}
});
if(sosPeerID == nil || sosPeerIDError != nil){
secerror("octagon: cannot retrieve peer id from SOS, error: if(error){
*error = sosPeerIDError;
}
return nil;
}
__block SFECKeyPair *peerEncryptionKey;
__block SFECKeyPair *peerSigningKey;
__block NSError* localError = nil;
SOSCCPerformWithAllOctagonKeys(^(SecKeyRef octagonEncryptionKey, SecKeyRef octagonSigningKey, CFErrorRef cferror) {
if(cferror) {
localError = (__bridge NSError*)cferror;
return;
}
if (!cferror && octagonEncryptionKey && octagonSigningKey) {
peerSigningKey = [[SFECKeyPair alloc] initWithSecKey:octagonSigningKey];
peerEncryptionKey = [[SFECKeyPair alloc] initWithSecKey:octagonEncryptionKey];
}
});
if(!peerEncryptionKey || !peerSigningKey || localError != nil){
secerror("octagon: failed to retrieve octagon keys from sos: if(error){
*error = localError;
}
return nil;
}
return [[OTIdentity alloc] initWithPeerID:nil
spID:sosPeerID
peerSigningKey:peerSigningKey
peerEncryptionkey:peerEncryptionKey
error:error];
}
-(BOOL)isEqual:(OTIdentity*)identity
{
return [self.peerID isEqualToString:identity.peerID] &&
[self.spID isEqualToString:identity.spID] &&
[self.peerSigningKey isEqual:identity.peerSigningKey] &&
[self.peerEncryptionKey isEqual:identity.peerEncryptionKey];
}
+ (BOOL) setKeyMaterialInKeychain:(NSDictionary*)query error:(NSError* __autoreleasing *)error
{
BOOL result = NO;
CFTypeRef results = NULL;
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, &results);
NSError* localerror = nil;
if(status == errSecDuplicateItem || status == errSecSuccess) {
result = YES;
} else {
localerror = [NSError errorWithDomain:@"securityd"
code:status
userInfo:nil];
}
if(status != errSecSuccess) {
CFReleaseNull(results);
if(error) {
*error = localerror;
}
}
return result;
}
+ (BOOL)storeOtagonKey:(NSData*)keyData
octagonKeyType:(OctagonKeyType)octagonKeyType
restoredPeerID:(NSString*)restoredPeerID
escrowSigningPubKeyHash:(NSString*)escrowSigningPubKeyHash
error:(NSError**)error
{
NSNumber *keyType = [[NSNumber alloc]initWithInt:octagonKeyType];
NSDictionary* query = @{
(id)kSecClass : (id)kSecClassInternetPassword,
(id)kSecAttrAccessible: (id)kSecAttrAccessibleWhenUnlocked,
(id)kSecAttrNoLegacy : @YES,
(id)kSecAttrLabel : escrowSigningPubKeyHash,
(id)kSecAttrAccount : restoredPeerID,
(id)kSecAttrType : keyType,
(id)kSecAttrServer : (octagonKeyType == 1) ? @"Octagon Signing Key" : @"Octagon Encryption Key",
(id)kSecAttrAccessGroup: @"com.apple.security.ckks",
(id)kSecAttrSynchronizable : (id)kCFBooleanFalse,
(id)kSecValueData : keyData,
};
return [OTIdentity setKeyMaterialInKeychain:query error:error];
}
+(BOOL) storeOctagonIdentityIntoKeychain:(_SFECKeyPair *)restoredSigningKey
restoredEncryptionKey:(_SFECKeyPair *)restoredEncryptionKey
escrowSigningPubKeyHash:(NSString *)escrowSigningPubKeyHash
restoredPeerID:(NSString *)peerID
error:(NSError**)error
{
NSError* localError = nil;
BOOL result = [OTIdentity storeOtagonKey:[restoredSigningKey keyData] octagonKeyType:OctagonSigningKey restoredPeerID:peerID escrowSigningPubKeyHash:escrowSigningPubKeyHash error:&localError];
if(!result || localError){
secerror("octagon: could not store octagon signing key in keychain: if(error){
*error = localError;
}
return NO;
}
result = [OTIdentity storeOtagonKey:[restoredEncryptionKey keyData] octagonKeyType:OctagonEncryptionKey restoredPeerID:peerID escrowSigningPubKeyHash:escrowSigningPubKeyHash error:&localError];
if(!result || localError){
secerror("octagon: could not store octagon encryption key in keychain: if(error){
*error = localError;
}
return NO;
}
return result;
}
@end
#endif