2016-02-23 21:19:11 +01:00
/ *
2020-07-06 07:59:58 -04:00
* Copyright ( c ) 2015 -2017 Apple Inc . All Rights Reserved .
2016-02-23 21:19:11 +01:00
*
* @ 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 @
* /
// Our Header
2017-06-03 12:31:08 -07:00
2016-02-23 21:19:11 +01:00
# include < Security / SecureObjectSync / SOSBackupSliceKeyBag . h >
// Needed headers for implementation
# include "AssertMacros.h"
# include < utilities / SecCFWrappers . h >
# include < utilities / SecAKSWrappers . h >
# include < utilities / SecBuffer . h >
# include < utilities / SecCFError . h >
# include < utilities / der_set . h >
# include < utilities / der_plist _internal . h >
# include < Security / SecRandom . h >
# include < Security / SecureObjectSync / SOSPeerInfo . h >
2020-07-06 07:59:58 -04:00
# include "keychain/SecureObjectSync/SOSPeerInfoCollections.h"
# include "keychain/SecureObjectSync/SOSInternal.h"
2016-02-23 21:19:11 +01:00
# include < corecrypto / ccec . h >
# include < corecrypto / ccsha2 . h >
# include < corecrypto / ccrng . h >
# include < limits . h >
2020-07-06 07:59:58 -04:00
# include < Security / SecRecoveryKey . h >
2017-06-03 12:31:08 -07:00
# include "SOSKeyedPubKeyIdentifier.h"
2020-07-06 07:59:58 -04:00
# include "keychain/SecureObjectSync/SOSInternal.h"
# include "SecCFAllocator.h"
2017-06-03 12:31:08 -07:00
CFStringRef bskbRkbgPrefix = CFSTR ( "RK" ) ;
2016-02-23 21:19:11 +01:00
2020-07-06 07:59:58 -04:00
CFDataRef SOSRKNullKey ( void ) {
static CFDataRef localNullKey = NULL ;
static dispatch_once _t onceToken ;
dispatch_once ( & onceToken , ^ {
localNullKey = CFDataCreate ( kCFAllocatorDefault , ( const UInt8 * ) "nullkey" , 8 ) ;
} ) ;
return localNullKey ;
}
2016-02-23 21:19:11 +01:00
//
// MARK : Type creation
//
CFGiblisFor ( SOSBackupSliceKeyBag ) ;
const int kAKSBagSecretLength = 32 ;
struct __OpaqueSOSBackupSliceKeyBag {
CFRuntimeBase _base ;
CFDataRef aks_bag ;
CFSetRef peers ;
CFDictionaryRef wrapped_keys ;
} ;
static void SOSBackupSliceKeyBagDestroy ( CFTypeRef aObj ) {
SOSBackupSliceKeyBagRef vb = ( SOSBackupSliceKeyBagRef ) aObj ;
CFReleaseNull ( vb -> aks_bag ) ;
CFReleaseNull ( vb -> peers ) ;
CFReleaseNull ( vb -> wrapped_keys ) ;
}
2017-06-03 12:31:08 -07:00
static CFSetRef SOSBackupSliceKeyBagCopyPeerNames ( SOSBackupSliceKeyBagRef bksb ) {
CFMutableSetRef retval = CFSetCreateMutableForCFTypes ( kCFAllocatorDefault ) ;
if ( ! retval ) return NULL ;
CFSetForEach ( bksb -> peers , ^ ( const void * value ) {
SOSPeerInfoRef pi = ( SOSPeerInfoRef ) value ;
CFSetAddValue ( retval , SOSPeerInfoGetPeerName ( pi ) ) ;
} ) ;
return retval ;
}
2016-02-23 21:19:11 +01:00
static CFStringRef SOSBackupSliceKeyBagCopyFormatDescription ( CFTypeRef aObj , CFDictionaryRef formatOptions ) {
SOSBackupSliceKeyBagRef vb = ( SOSBackupSliceKeyBagRef ) aObj ;
2017-06-03 12:31:08 -07:00
CFMutableStringRef retval = CFStringCreateMutable ( kCFAllocatorDefault , 0 ) ;
CFSetRef peerIDs = SOSBackupSliceKeyBagCopyPeerNames ( vb ) ;
CFStringSetPerformWithDescription ( peerIDs , ^ ( CFStringRef description ) {
CFStringAppendFormat ( retval , NULL , CFSTR ( "<SOSBackupSliceKeyBag@%p %ld %@" ) , vb , vb -> peers ? CFSetGetCount ( vb -> peers ) : 0 , description ) ;
} ) ;
CFReleaseNull ( peerIDs ) ;
CFStringAppend ( retval , CFSTR ( ">" ) ) ;
2016-02-23 21:19:11 +01:00
2017-06-03 12:31:08 -07:00
return retval ;
2016-02-23 21:19:11 +01:00
}
//
// MARK : Encode / Decode
//
const uint8_t * der_decode _BackupSliceKeyBag ( CFAllocatorRef allocator ,
SOSBackupSliceKeyBagRef * BackupSliceKeyBag , CFErrorRef * error ,
const uint8_t * der , const uint8_t * der_end ) {
if ( der = = NULL ) return der ;
const uint8_t * result = NULL ;
SOSBackupSliceKeyBagRef vb = CFTypeAllocate ( SOSBackupSliceKeyBag , struct __OpaqueSOSBackupSliceKeyBag , allocator ) ;
require_quiet ( SecAllocationError ( vb , error , CFSTR ( "View bag allocation failed" ) ) , fail ) ;
const uint8_t * sequence_end = NULL ;
der = ccder_decode _sequence _tl ( & sequence_end , der , der_end ) ;
require_quiet ( sequence_end = = der_end , fail ) ;
2022-12-12 19:51:23 -08:00
der = der_decode _data ( kCFAllocatorDefault , & vb -> aks_bag , error , der , sequence_end ) ;
2016-02-23 21:19:11 +01:00
vb -> peers = SOSPeerInfoSetCreateFromArrayDER ( kCFAllocatorDefault , & kSOSPeerSetCallbacks , error ,
& der , der_end ) ;
2022-12-12 19:51:23 -08:00
der = der_decode _dictionary ( kCFAllocatorDefault , & vb -> wrapped_keys , error , der , sequence_end ) ;
2016-02-23 21:19:11 +01:00
require_quiet ( SecRequirementError ( der = = der_end , error , CFSTR ( "Extra space in sequence" ) ) , fail ) ;
if ( BackupSliceKeyBag )
CFTransferRetained ( * BackupSliceKeyBag , vb ) ;
result = der ;
fail :
CFReleaseNull ( vb ) ;
return result ;
}
size_t der_sizeof _BackupSliceKeyBag ( SOSBackupSliceKeyBagRef BackupSliceKeyBag , CFErrorRef * error ) {
size_t result = 0 ;
2017-06-03 12:31:08 -07:00
require_quiet ( SecRequirementError ( BackupSliceKeyBag ! = NULL , error , CFSTR ( "Null BackupSliceKeyBag" ) ) , fail ) ;
require_quiet ( BackupSliceKeyBag ! = NULL , fail ) ; // this is redundant with what happens in SecRequirementError , but the analyzer can ' t understand that .
require_quiet ( SecRequirementError ( BackupSliceKeyBag -> aks_bag ! = NULL , error , CFSTR ( "null aks_bag in BackupSliceKeyBag" ) ) , fail ) ;
require_quiet ( BackupSliceKeyBag -> aks_bag ! = NULL , fail ) ; // this is redundant with what happens in SecRequirementError , but the analyzer can ' t understand that .
2016-02-23 21:19:11 +01:00
size_t bag_size = der_sizeof _data ( BackupSliceKeyBag -> aks_bag , error ) ;
require_quiet ( bag_size , fail ) ;
size_t peers_size = SOSPeerInfoSetGetDEREncodedArraySize ( BackupSliceKeyBag -> peers , error ) ;
require_quiet ( peers_size , fail ) ;
size_t wrapped_keys _size = der_sizeof _dictionary ( BackupSliceKeyBag -> wrapped_keys , error ) ;
require_quiet ( wrapped_keys _size , fail ) ;
result = ccder_sizeof ( CCDER_CONSTRUCTED _SEQUENCE , bag_size + peers_size + wrapped_keys _size ) ;
fail :
return result ;
}
uint8_t * der_encode _BackupSliceKeyBag ( SOSBackupSliceKeyBagRef set , CFErrorRef * error ,
const uint8_t * der , uint8_t * der_end ) {
uint8_t * result = NULL ;
if ( der_end = = NULL ) return der_end ;
require_quiet ( SecRequirementError ( set ! = NULL , error , CFSTR ( "Null set passed to encode" ) ) , fail ) ;
2017-06-03 12:31:08 -07:00
require_quiet ( set , fail ) ; // Silence the NULL warning .
require_quiet ( SecRequirementError ( set -> aks_bag ! = NULL , error , CFSTR ( "Null set passed to encode" ) ) , fail ) ;
require_quiet ( set -> aks_bag , fail ) ; // Silence the warning .
2016-02-23 21:19:11 +01:00
der_end = ccder_encode _constructed _tl ( CCDER_CONSTRUCTED _SEQUENCE , der_end , der ,
der_encode _data ( set -> aks_bag , error , der ,
SOSPeerInfoSetEncodeToArrayDER ( set -> peers , error , der ,
der_encode _dictionary ( set -> wrapped_keys , error , der , der_end ) ) ) ) ;
require_quiet ( der_end = = der , fail ) ;
result = der_end ;
fail :
return result ;
}
2017-06-03 12:31:08 -07:00
2016-02-23 21:19:11 +01:00
SOSBackupSliceKeyBagRef SOSBackupSliceKeyBagCreateFromData ( CFAllocatorRef allocator , CFDataRef data , CFErrorRef * error ) {
SOSBackupSliceKeyBagRef result = NULL ;
SOSBackupSliceKeyBagRef decodedBag = NULL ;
const uint8_t * der = CFDataGetBytePtr ( data ) ;
const uint8_t * der_end = der + CFDataGetLength ( data ) ;
der = der_decode _BackupSliceKeyBag ( allocator , & decodedBag , error , der , der_end ) ;
require_quiet ( SecRequirementError ( der = = der_end , error , CFSTR ( "Didn't consume all data supplied" ) ) , fail ) ;
CFTransferRetained ( result , decodedBag ) ;
fail :
CFReleaseNull ( decodedBag ) ;
return result ;
}
//
// MARK : Construction
//
bool SOSBSKBIsGoodBackupPublic ( CFDataRef publicKey , CFErrorRef * error ) {
bool result = false ;
ccec_pub _ctx _decl _cp ( SOSGetBackupKeyCurveParameters ( ) , pub_key ) ;
int cc_result = ccec_compact _import _pub ( SOSGetBackupKeyCurveParameters ( ) , CFDataGetLength ( publicKey ) , CFDataGetBytePtr ( publicKey ) , pub_key ) ;
require_action _quiet ( cc_result = = 0 , exit , SOSErrorCreate ( kSOSErrorDecodeFailure , error , NULL , CFSTR ( "Unable to decode public key: %@" ) , publicKey ) ) ;
result = true ;
exit :
return result ;
}
static CFDataRef SOSCopyECWrapped ( CFDataRef publicKey , CFDataRef secret , CFErrorRef * error ) {
CFDataRef result = NULL ;
ccec_pub _ctx _decl _cp ( SOSGetBackupKeyCurveParameters ( ) , pub_key ) ;
int cc_result = ccec_compact _import _pub ( SOSGetBackupKeyCurveParameters ( ) , CFDataGetLength ( publicKey ) , CFDataGetBytePtr ( publicKey ) , pub_key ) ;
require_action _quiet ( cc_result = = 0 , exit , SOSErrorCreate ( kSOSErrorDecodeFailure , error , NULL , CFSTR ( "Unable to decode public key: %@" ) , publicKey ) ) ;
result = SOSCopyECWrappedData ( pub_key , secret , error ) ;
exit :
return result ;
}
2017-06-03 12:31:08 -07:00
static CFDictionaryRef SOSBackupSliceKeyBagCopyWrappedKeys ( SOSBackupSliceKeyBagRef vb , CFDataRef secret , CFDictionaryRef additionalKeys , CFErrorRef * error ) {
2016-02-23 21:19:11 +01:00
CFDictionaryRef result = NULL ;
CFMutableDictionaryRef wrappedKeys = CFDictionaryCreateMutableForCFTypes ( kCFAllocatorDefault ) ;
__block bool success = true ;
CFSetForEach ( vb -> peers , ^ ( const void * value ) {
SOSPeerInfoRef pi = ( SOSPeerInfoRef ) value ;
if ( isSOSPeerInfo ( pi ) ) {
CFStringRef id = SOSPeerInfoGetPeerID ( pi ) ;
CFDataRef backupKey = SOSPeerInfoCopyBackupKey ( pi ) ;
if ( backupKey ) {
2017-06-03 12:31:08 -07:00
CFErrorRef wrapError = NULL ;
CFDataRef wrappedKey = SOSCopyECWrapped ( backupKey , secret , & wrapError ) ;
2016-02-23 21:19:11 +01:00
if ( wrappedKey ) {
CFDictionaryAddValue ( wrappedKeys , id , wrappedKey ) ;
2017-06-03 12:31:08 -07:00
CFDataPerformWithHexString ( backupKey , ^ ( CFStringRef backupKeyString ) {
CFDataPerformWithHexString ( wrappedKey , ^ ( CFStringRef wrappedKeyString ) {
secnotice ( "bskb" , "Add for id: %@, bk: %@, wrapped: %@" , id , backupKeyString , wrappedKeyString ) ;
} ) ;
} ) ;
2016-02-23 21:19:11 +01:00
} else {
2017-06-03 12:31:08 -07:00
CFDataPerformWithHexString ( backupKey , ^ ( CFStringRef backupKeyString ) {
secnotice ( "bskb" , "Failed at id: %@, bk: %@ error: %@" , id , backupKeyString , wrapError ) ;
} ) ;
CFErrorPropagate ( wrapError , error ) ;
2016-02-23 21:19:11 +01:00
success = false ;
}
CFReleaseNull ( wrappedKey ) ;
CFReleaseNull ( backupKey ) ;
2017-06-03 12:31:08 -07:00
} else {
secnotice ( "bskb" , "Skipping id %@, no backup key." , id ) ;
2016-02-23 21:19:11 +01:00
}
}
} ) ;
2017-06-03 12:31:08 -07:00
CFDictionaryForEach ( additionalKeys , ^ ( const void * key , const void * value ) {
CFStringRef prefix = asString ( key , NULL ) ;
CFDataRef backupKey = asData ( value , NULL ) ;
if ( backupKey ) {
CFDataRef wrappedKey = NULL ;
CFErrorRef localError = NULL ;
CFStringRef id = SOSKeyedPubKeyIdentifierCreateWithData ( prefix , backupKey ) ;
require_quiet ( id , done ) ;
wrappedKey = SOSCopyECWrapped ( backupKey , secret , & localError ) ;
require_quiet ( wrappedKey , done ) ;
CFDictionaryAddValue ( wrappedKeys , id , wrappedKey ) ;
done :
if ( ! localError ) {
CFDataPerformWithHexString ( backupKey , ^ ( CFStringRef backupKeyString ) {
CFDataPerformWithHexString ( wrappedKey , ^ ( CFStringRef wrappedKeyString ) {
secnotice ( "bskb" , "Add for bk: %@, wrapped: %@" , backupKeyString , wrappedKeyString ) ;
} ) ;
} ) ;
} else {
CFDataPerformWithHexString ( backupKey , ^ ( CFStringRef backupKeyString ) {
secnotice ( "bskb" , "Failed at bk: %@ error: %@" , backupKeyString , localError ) ;
} ) ;
CFErrorPropagate ( localError , error ) ;
success = false ;
}
CFReleaseNull ( wrappedKey ) ;
CFReleaseNull ( id ) ;
} else {
secnotice ( "bskb" , "Skipping %@, not data." , value ) ;
}
} ) ;
2016-02-23 21:19:11 +01:00
if ( success )
CFTransferRetained ( result , wrappedKeys ) ;
CFReleaseNull ( wrappedKeys ) ;
return result ;
}
2017-06-03 12:31:08 -07:00
static bool SOSBackupSliceKeyBagCreateBackupBag ( SOSBackupSliceKeyBagRef vb , CFDictionaryRef / * CFDataRef * / additionalKeys , CFErrorRef * error ) {
2016-02-23 21:19:11 +01:00
CFReleaseNull ( vb -> aks_bag ) ;
// Choose a random key .
PerformWithBufferAndClear ( kAKSBagSecretLength , ^ ( size_t size , uint8_t * buffer ) {
CFDataRef secret = NULL ;
require_quiet ( SecError ( SecRandomCopyBytes ( kSecRandomDefault , size , buffer ) , error , CFSTR ( "SecRandom falied!" ) ) , fail ) ;
secret = CFDataCreateWithBytesNoCopy ( kCFAllocatorDefault , buffer , size , kCFAllocatorNull ) ;
2017-06-03 12:31:08 -07:00
CFAssignRetained ( vb -> wrapped_keys , SOSBackupSliceKeyBagCopyWrappedKeys ( vb , secret , additionalKeys , error ) ) ;
2016-02-23 21:19:11 +01:00
CFAssignRetained ( vb -> aks_bag , SecAKSCopyBackupBagWithSecret ( size , buffer , error ) ) ;
fail :
CFReleaseSafe ( secret ) ;
} ) ;
return vb -> aks_bag ! = NULL ;
}
CFDataRef SOSBSKBCopyEncoded ( SOSBackupSliceKeyBagRef BackupSliceKeyBag , CFErrorRef * error ) {
CFDataRef result = NULL ;
CFMutableDataRef encoded = NULL ;
size_t encodedSize = der_sizeof _BackupSliceKeyBag ( BackupSliceKeyBag , error ) ;
require_quiet ( encodedSize , fail ) ;
encoded = CFDataCreateMutableWithScratch ( kCFAllocatorDefault , encodedSize ) ;
require_quiet ( SecAllocationError ( encoded , error , CFSTR ( "Faild to create scratch" ) ) , fail ) ;
uint8_t * encode_to = CFDataGetMutableBytePtr ( encoded ) ;
uint8_t * encode_to _end = encode_to + CFDataGetLength ( encoded ) ;
require_quiet ( encode_to = = der_encode _BackupSliceKeyBag ( BackupSliceKeyBag , error , encode_to , encode_to _end ) , fail ) ;
CFTransferRetained ( result , encoded ) ;
fail :
CFReleaseSafe ( encoded ) ;
return result ;
}
static CFSetRef SOSBackupSliceKeyBagCreatePeerSet ( CFAllocatorRef allocator , CFSetRef peers ) {
CFMutableSetRef result = CFSetCreateMutableForSOSPeerInfosByID ( allocator ) ;
CFSetForEach ( peers , ^ ( const void * value ) {
CFSetAddValue ( result , value ) ;
} ) ;
return result ;
}
SOSBackupSliceKeyBagRef SOSBackupSliceKeyBagCreate ( CFAllocatorRef allocator , CFSetRef peers , CFErrorRef * error ) {
2017-06-03 12:31:08 -07:00
CFMutableDictionaryRef additionalKeys = CFDictionaryCreateMutableForCFTypes ( kCFAllocatorDefault ) ;
SOSBackupSliceKeyBagRef result = SOSBackupSliceKeyBagCreateWithAdditionalKeys ( allocator , peers , additionalKeys , NULL ) ;
CFReleaseNull ( additionalKeys ) ;
return result ;
}
SOSBackupSliceKeyBagRef SOSBackupSliceKeyBagCreateWithAdditionalKeys ( CFAllocatorRef allocator ,
CFSetRef / * SOSPeerInfoRef * / peers ,
CFDictionaryRef / * CFStringRef ( prefix ) CFDataRef ( keydata ) * / additionalKeys ,
CFErrorRef * error ) {
2016-02-23 21:19:11 +01:00
SOSBackupSliceKeyBagRef result = NULL ;
SOSBackupSliceKeyBagRef vb = CFTypeAllocate ( SOSBackupSliceKeyBag , struct __OpaqueSOSBackupSliceKeyBag , allocator ) ;
require_quiet ( SecAllocationError ( vb , error , CFSTR ( "View bag allocation failed" ) ) , fail ) ;
require_quiet ( SecRequirementError ( CFSetGetCount ( peers ) > 0 , error , CFSTR ( "Need peers" ) ) , fail ) ;
vb -> peers = SOSBackupSliceKeyBagCreatePeerSet ( allocator , peers ) ;
vb -> wrapped_keys = CFDictionaryCreateMutableForCFTypes ( allocator ) ;
2017-06-03 12:31:08 -07:00
require_quiet ( SOSBackupSliceKeyBagCreateBackupBag ( vb , additionalKeys , error ) , fail ) ;
2016-02-23 21:19:11 +01:00
CFTransferRetained ( result , vb ) ;
fail :
CFReleaseNull ( vb ) ;
return result ;
}
2017-06-03 12:31:08 -07:00
2016-02-23 21:19:11 +01:00
SOSBackupSliceKeyBagRef SOSBackupSliceKeyBagCreateDirect ( CFAllocatorRef allocator , CFDataRef aks_bag , CFErrorRef * error )
{
SOSBackupSliceKeyBagRef result = NULL ;
SOSBackupSliceKeyBagRef vb = CFTypeAllocate ( SOSBackupSliceKeyBag , struct __OpaqueSOSBackupSliceKeyBag , allocator ) ;
require_quiet ( SecAllocationError ( vb , error , CFSTR ( "View bag allocation failed" ) ) , fail ) ;
require_quiet ( SecRequirementError ( aks_bag , error , CFSTR ( "Need aks bag" ) ) , fail ) ;
vb -> aks_bag = CFRetainSafe ( aks_bag ) ;
vb -> peers = CFSetCreateMutableForSOSPeerInfosByID ( allocator ) ;
vb -> wrapped_keys = CFDictionaryCreateMutableForCFTypes ( allocator ) ;
CFTransferRetained ( result , vb ) ;
fail :
CFReleaseNull ( vb ) ;
return result ;
}
//
// MARK : Use
//
bool SOSBSKBIsDirect ( SOSBackupSliceKeyBagRef backupSliceKeyBag )
{
return 0 = = CFSetGetCount ( backupSliceKeyBag -> peers ) ;
}
CFDataRef SOSBSKBCopyAKSBag ( SOSBackupSliceKeyBagRef backupSliceKeyBag , CFErrorRef * error )
{
return CFRetainSafe ( backupSliceKeyBag -> aks_bag ) ;
}
CFSetRef SOSBSKBGetPeers ( SOSBackupSliceKeyBagRef backupSliceKeyBag ) {
return backupSliceKeyBag -> peers ;
}
2017-06-03 12:31:08 -07:00
int SOSBSKBCountPeers ( SOSBackupSliceKeyBagRef backupSliceKeyBag ) {
2020-07-06 07:59:58 -04:00
return ( backupSliceKeyBag -> peers ) ? ( int ) CFSetGetCount ( backupSliceKeyBag -> peers ) : 0 ;
2017-06-03 12:31:08 -07:00
}
bool SOSBSKBPeerIsInKeyBag ( SOSBackupSliceKeyBagRef backupSliceKeyBag , SOSPeerInfoRef pi ) {
return CFSetGetValue ( backupSliceKeyBag -> peers , pi ) ! = NULL ;
}
bool SOSBKSBKeyIsInKeyBag ( SOSBackupSliceKeyBagRef backupSliceKeyBag , CFDataRef publicKey ) {
bool result = false ;
CFStringRef keyID = SOSCopyIDOfDataBuffer ( publicKey , NULL ) ;
require_quiet ( keyID , done ) ;
result = CFDictionaryContainsKey ( backupSliceKeyBag -> wrapped_keys , keyID ) ;
done :
CFReleaseSafe ( keyID ) ;
return result ;
}
2020-07-06 07:59:58 -04:00
bool SOSBKSBPeerBackupKeyIsInKeyBag ( SOSBackupSliceKeyBagRef backupSliceKeyBag , SOSPeerInfoRef pi ) {
bool result = false ;
CFDataRef bskbBackupKey = NULL ;
CFDataRef backupKey = SOSPeerInfoCopyBackupKey ( pi ) ;
SOSPeerInfoRef backupPI = asSOSPeerInfo ( CFSetGetValue ( backupSliceKeyBag -> peers , pi ) ) ;
if ( backupPI ) {
bskbBackupKey = SOSPeerInfoCopyBackupKey ( backupPI ) ;
}
result = CFEqualSafe ( backupKey , bskbBackupKey ) ;
CFReleaseNull ( bskbBackupKey ) ;
CFReleaseNull ( backupKey ) ;
return result ;
}
bool SOSBSKBAllPeersBackupKeysAreInKeyBag ( SOSBackupSliceKeyBagRef backupSliceKeyBag , CFSetRef peers ) {
2022-12-12 19:51:23 -08:00
// earlier we had to accept BSKBs listing peers without backup keys for compatibility . For that reason
// this routine will iterate over the peers given and only disqualify the BSKB if it doesn ' t include the backup key of a peer with one .
__block bool result = true ;
2020-07-06 07:59:58 -04:00
if ( backupSliceKeyBag && peers ) {
2022-12-12 19:51:23 -08:00
CFSetForEach ( peers , ^ ( const void * value ) {
SOSPeerInfoRef pi = asSOSPeerInfo ( value ) ;
if ( pi && SOSPeerInfoHasBackupKey ( pi ) ) {
result & = SOSBKSBPeerBackupKeyIsInKeyBag ( backupSliceKeyBag , pi ) ;
}
} ) ;
2020-07-06 07:59:58 -04:00
}
return result ;
}
2017-06-03 12:31:08 -07:00
bool SOSBKSBPrefixedKeyIsInKeyBag ( SOSBackupSliceKeyBagRef backupSliceKeyBag , CFStringRef prefix , CFDataRef publicKey ) {
bool result = false ;
CFStringRef kpkid = SOSKeyedPubKeyIdentifierCreateWithData ( prefix , publicKey ) ;
require_quiet ( kpkid , done ) ;
result = CFDictionaryContainsKey ( backupSliceKeyBag -> wrapped_keys , kpkid ) ;
done :
CFReleaseSafe ( kpkid ) ;
return result ;
}
bskb_keybag _handle _t SOSBSKBLoadLocked ( SOSBackupSliceKeyBagRef backupSliceKeyBag ,
CFErrorRef * error )
{
# if ! TARGET_HAS _KEYSTORE
return bad_keybag _handle ;
# else
keybag_handle _t result = bad_keybag _handle ;
keybag_handle _t bag_handle = bad_keybag _handle ;
require_quiet ( SecRequirementError ( backupSliceKeyBag -> aks_bag , error ,
CFSTR ( "No aks bag to load" ) ) , exit ) ;
require_quiet ( SecRequirementError ( CFDataGetLength ( backupSliceKeyBag -> aks_bag ) < INT_MAX , error ,
CFSTR ( "No aks bag to load" ) ) , exit ) ;
kern_return _t aks_result ;
aks_result = aks_load _bag ( CFDataGetBytePtr ( backupSliceKeyBag -> aks_bag ) ,
( int ) CFDataGetLength ( backupSliceKeyBag -> aks_bag ) ,
& bag_handle ) ;
require_quiet ( SecKernError ( aks_result , error ,
CFSTR ( "aks_load_bag failed: %d" ) , aks_result ) , exit ) ;
result = bag_handle ;
bag_handle = bad_keybag _handle ;
exit :
if ( bag_handle ! = bad_keybag _handle ) {
( void ) aks_unload _bag ( bag_handle ) ;
}
return result ;
# endif
}
2016-02-23 21:19:11 +01:00
static keybag_handle _t SOSBSKBLoadAndUnlockBagWithSecret ( SOSBackupSliceKeyBagRef backupSliceKeyBag ,
size_t secretSize , const uint8_t * secret ,
CFErrorRef * error )
{
# if ! TARGET_HAS _KEYSTORE
return bad_keybag _handle ;
# else
keybag_handle _t result = bad_keybag _handle ;
keybag_handle _t bag_handle = bad_keybag _handle ;
require_quiet ( SecRequirementError ( backupSliceKeyBag -> aks_bag , error ,
CFSTR ( "No aks bag to load" ) ) , exit ) ;
require_quiet ( SecRequirementError ( CFDataGetLength ( backupSliceKeyBag -> aks_bag ) < INT_MAX , error ,
CFSTR ( "No aks bag to load" ) ) , exit ) ;
require_quiet ( SecRequirementError ( secretSize <= INT_MAX , error ,
CFSTR ( "secret too big" ) ) , exit ) ;
kern_return _t aks_result ;
aks_result = aks_load _bag ( CFDataGetBytePtr ( backupSliceKeyBag -> aks_bag ) ,
( int ) CFDataGetLength ( backupSliceKeyBag -> aks_bag ) ,
& bag_handle ) ;
require_quiet ( SecKernError ( aks_result , error ,
CFSTR ( "aks_load_bag failed: %d" ) , aks_result ) , exit ) ;
aks_result = aks_unlock _bag ( bag_handle , secret , ( int ) secretSize ) ;
require_quiet ( SecKernError ( aks_result , error ,
CFSTR ( "failed to unlock bag: %d" ) , aks_result ) , exit ) ;
result = bag_handle ;
bag_handle = bad_keybag _handle ;
exit :
if ( bag_handle ! = bad_keybag _handle ) {
( void ) aks_unload _bag ( bag_handle ) ;
}
return result ;
# endif
}
keybag_handle _t SOSBSKBLoadAndUnlockWithPeerIDAndSecret ( SOSBackupSliceKeyBagRef backupSliceKeyBag ,
CFStringRef peerID , CFDataRef peerSecret ,
CFErrorRef * error )
{
__block keybag_handle _t result = bad_keybag _handle ;
CFDataRef lookedUpData = CFDictionaryGetValue ( backupSliceKeyBag -> wrapped_keys , peerID ) ;
require_quiet ( SecRequirementError ( lookedUpData ! = NULL , error , CFSTR ( "%@ has no wrapped key in %@" ) , peerID , backupSliceKeyBag ) , exit ) ;
require_quiet ( SOSPerformWithDeviceBackupFullKey ( SOSGetBackupKeyCurveParameters ( ) , peerSecret , error , ^ ( ccec_full _ctx _t fullKey ) {
SOSPerformWithUnwrappedData ( fullKey , lookedUpData , error , ^ ( size_t size , uint8_t * buffer ) {
result = SOSBSKBLoadAndUnlockBagWithSecret ( backupSliceKeyBag , size , buffer , error ) ;
} ) ;
} ) , exit ) ;
exit :
return result ;
}
keybag_handle _t SOSBSKBLoadAndUnlockWithPeerSecret ( SOSBackupSliceKeyBagRef backupSliceKeyBag ,
SOSPeerInfoRef peer , CFDataRef peerSecret ,
CFErrorRef * error )
{
return SOSBSKBLoadAndUnlockWithPeerIDAndSecret ( backupSliceKeyBag , SOSPeerInfoGetPeerID ( peer ) , peerSecret , error ) ;
}
keybag_handle _t SOSBSKBLoadAndUnlockWithDirectSecret ( SOSBackupSliceKeyBagRef backupSliceKeyBag ,
CFDataRef secret ,
CFErrorRef * error )
{
keybag_handle _t result = bad_keybag _handle ;
require_quiet ( SecRequirementError ( SOSBSKBIsDirect ( backupSliceKeyBag ) , error , CFSTR ( "Not direct bag" ) ) , exit ) ;
result = SOSBSKBLoadAndUnlockBagWithSecret ( backupSliceKeyBag ,
CFDataGetLength ( secret ) ,
CFDataGetBytePtr ( secret ) ,
error ) ;
exit :
return result ;
}
2017-06-03 12:31:08 -07:00
2020-07-06 07:59:58 -04:00
# include < Security / SecRecoveryKey . h >
2017-06-03 12:31:08 -07:00
static bool SOSPerformWithRecoveryKeyFullKey ( CFDataRef wrappingSecret , CFErrorRef * error , void ( ^ operation ) ( ccec_full _ctx _t fullKey , CFStringRef keyID ) ) {
bool result = false ;
CFStringRef keyID = NULL ;
2020-07-06 07:59:58 -04:00
NSError * nserror = NULL ;
2017-06-03 12:31:08 -07:00
SecRecoveryKey * sRecKey = NULL ;
2020-07-06 07:59:58 -04:00
NSData * fullKeyBytes = NULL ;
NSData * pubKeyBytes = NULL ;
CFStringRef restoreKeySecret = CFStringCreateWithBytes ( SecCFAllocatorZeroize ( ) , CFDataGetBytePtr ( wrappingSecret ) , CFDataGetLength ( wrappingSecret ) , kCFStringEncodingUTF8 , false ) ;
2017-06-03 12:31:08 -07:00
require_action _quiet ( restoreKeySecret , errOut , SOSErrorCreate ( kSOSErrorDecodeFailure , error , NULL , CFSTR ( "Unable to create key string from data." ) ) ) ;
2020-07-06 07:59:58 -04:00
sRecKey = SecRKCreateRecoveryKeyWithError ( ( __bridge NSString * ) ( restoreKeySecret ) , & nserror ) ;
if ( ! sRecKey ) {
if ( error )
* error = ( CFErrorRef ) CFBridgingRetain ( nserror ) ;
goto errOut ;
}
2017-06-03 12:31:08 -07:00
require_action _quiet ( sRecKey , errOut , SOSErrorCreate ( kSOSErrorDecodeFailure , error , NULL , CFSTR ( "Unable to create recovery key from string." ) ) ) ;
2020-07-06 07:59:58 -04:00
2017-06-03 12:31:08 -07:00
fullKeyBytes = SecRKCopyBackupFullKey ( sRecKey ) ;
pubKeyBytes = SecRKCopyBackupPublicKey ( sRecKey ) ;
2020-07-06 07:59:58 -04:00
require_action _quiet ( fullKeyBytes && pubKeyBytes , errOut , SOSErrorCreate ( kSOSErrorDecodeFailure , error , NULL , CFSTR ( "Unable to get recovery key public and private keys." ) ) ) ;
keyID = SOSCopyIDOfDataBuffer ( ( ( __bridge CFDataRef ) pubKeyBytes ) , error ) ;
2017-06-03 12:31:08 -07:00
require_quiet ( keyID , errOut ) ;
{
2020-07-06 07:59:58 -04:00
size_t keysize = ccec_compact _import _priv _size ( CFDataGetLength ( ( __bridge CFDataRef ) fullKeyBytes ) ) ;
2017-06-03 12:31:08 -07:00
ccec_const _cp _t cp = ccec_curve _for _length _lookup ( keysize , ccec_cp _256 ( ) , ccec_cp _384 ( ) , ccec_cp _521 ( ) ) ;
ccec_full _ctx _decl _cp ( cp , fullKey ) ;
2020-07-06 07:59:58 -04:00
int res = ccec_compact _import _priv ( cp , CFDataGetLength ( ( __bridge CFDataRef ) fullKeyBytes ) , CFDataGetBytePtr ( ( __bridge CFDataRef ) fullKeyBytes ) , fullKey ) ;
2017-06-03 12:31:08 -07:00
if ( res = = 0 ) {
operation ( fullKey , keyID ) ;
result = true ;
ccec_full _ctx _clear _cp ( cp , fullKey ) ;
}
}
if ( ! result ) SOSErrorCreate ( kSOSErrorProcessingFailure , error , NULL , CFSTR ( "Unable to perform crypto operation from fullKeyBytes." ) ) ;
errOut :
CFReleaseNull ( keyID ) ;
CFReleaseNull ( restoreKeySecret ) ;
return result ;
}
bskb_keybag _handle _t SOSBSKBLoadAndUnlockWithWrappingSecret ( SOSBackupSliceKeyBagRef backupSliceKeyBag ,
CFDataRef wrappingSecret ,
CFErrorRef * error ) {
__block keybag_handle _t result = bad_keybag _handle ;
CFDataRef lookedUpData = SOSBSKBCopyRecoveryKey ( backupSliceKeyBag ) ;
require_quiet ( SecRequirementError ( lookedUpData ! = NULL , error , CFSTR ( "no recovery key found in %@" ) , backupSliceKeyBag ) , errOut ) ;
SOSPerformWithRecoveryKeyFullKey ( wrappingSecret , error , ^ ( ccec_full _ctx _t fullKey , CFStringRef keyID ) {
SOSPerformWithUnwrappedData ( fullKey , lookedUpData , error , ^ ( size_t size , uint8_t * buffer ) {
result = SOSBSKBLoadAndUnlockBagWithSecret ( backupSliceKeyBag , size , buffer , error ) ;
} ) ;
} ) ;
errOut :
CFReleaseSafe ( lookedUpData ) ;
return result ;
}
2020-07-06 07:59:58 -04:00
// fixed for < rdar : // problem / 30918831 > CrashTracer : secdtests at secdtests : SOSBSKBCopyAdditionalKeysWithPrefix
2017-06-03 12:31:08 -07:00
static CFDictionaryRef SOSBSKBCopyAdditionalKeysWithPrefix ( CFAllocatorRef allocator , SOSBackupSliceKeyBagRef bskb , CFStringRef prefix ) {
2020-07-06 07:59:58 -04:00
if ( ! bskb || ! bskb -> wrapped_keys ) return NULL ;
2017-06-03 12:31:08 -07:00
CFMutableDictionaryRef retval = CFDictionaryCreateMutableForCFTypes ( allocator ) ;
if ( ! retval ) return NULL ;
CFDictionaryForEach ( bskb -> wrapped_keys , ^ ( const void * key , const void * value ) {
CFStringRef kpkid = asString ( key , NULL ) ;
CFDataRef keyData = asData ( value , NULL ) ;
if ( kpkid && keyData && SOSKeyedPubKeyIdentifierIsPrefixed ( kpkid ) ) {
CFStringRef idPrefix = SOSKeyedPubKeyIdentifierCopyPrefix ( kpkid ) ;
if ( CFEqualSafe ( idPrefix , prefix ) ) {
CFDictionaryAddValue ( retval , kpkid , keyData ) ;
}
CFReleaseNull ( idPrefix ) ;
}
} ) ;
return retval ;
}
static bool SOSBSKBHasPrefixedKey ( SOSBackupSliceKeyBagRef bskb , CFStringRef prefix ) {
CFDictionaryRef keyDict = SOSBSKBCopyAdditionalKeysWithPrefix ( kCFAllocatorDefault , bskb , prefix ) ;
2020-07-06 07:59:58 -04:00
bool haveKeys = keyDict && CFDictionaryGetCount ( keyDict ) > 0 ;
2017-06-03 12:31:08 -07:00
CFReleaseNull ( keyDict ) ;
return haveKeys ;
}
CFDataRef SOSBSKBCopyRecoveryKey ( SOSBackupSliceKeyBagRef bskb ) {
2020-07-06 07:59:58 -04:00
if ( ! bskb ) return NULL ;
2017-06-03 12:31:08 -07:00
CFDictionaryRef keyDict = SOSBSKBCopyAdditionalKeysWithPrefix ( kCFAllocatorDefault , bskb , bskbRkbgPrefix ) ;
2020-07-06 07:59:58 -04:00
if ( ! keyDict ) return NULL ;
CFDataRef result = NULL ;
2017-06-03 12:31:08 -07:00
if ( CFDictionaryGetCount ( keyDict ) = = 1 ) {
__block CFDataRef keyData = NULL ;
CFDictionaryForEach ( keyDict , ^ ( const void * key , const void * value ) {
keyData = asData ( value , NULL ) ;
} ) ;
2020-07-06 07:59:58 -04:00
result = CFDataCreateCopy ( kCFAllocatorDefault , keyData ) ;
2017-06-03 12:31:08 -07:00
}
CFReleaseNull ( keyDict ) ;
2020-07-06 07:59:58 -04:00
return result ;
2017-06-03 12:31:08 -07:00
}
bool SOSBSKBHasRecoveryKey ( SOSBackupSliceKeyBagRef bskb ) {
2020-07-06 07:59:58 -04:00
if ( ! bskb ) return false ;
if ( SOSBSKBHasPrefixedKey ( bskb , bskbRkbgPrefix ) ) {
return true ;
}
2017-06-03 12:31:08 -07:00
// old way for RecoveryKeys
2020-07-06 07:59:58 -04:00
int keyCount = ( bskb -> wrapped_keys ! = NULL ) ? ( int ) CFDictionaryGetCount ( bskb -> wrapped_keys ) : 0 ;
2017-06-03 12:31:08 -07:00
int peerCount = SOSBSKBCountPeers ( bskb ) ;
return ! SOSBSKBIsDirect ( bskb ) && ( ( keyCount - peerCount ) > 0 ) ;
}
2020-07-06 07:59:58 -04:00
bool SOSBSKBHasThisRecoveryKey ( SOSBackupSliceKeyBagRef bskb , CFDataRef backupKey ) {
if ( backupKey ) {
CFStringRef id = SOSKeyedPubKeyIdentifierCreateWithData ( bskbRkbgPrefix , backupKey ) ;
bool result = ( bskb && id && CFDictionaryContainsKey ( bskb -> wrapped_keys , id ) ) ;
CFReleaseNull ( id ) ;
return result ;
} else {
2022-12-12 19:51:23 -08:00
// let ' s not judge ripping out other recovery keys
return true ;
2020-07-06 07:59:58 -04:00
}
}