// Our Header
+
#include <Security/SecureObjectSync/SOSBackupSliceKeyBag.h>
#include <limits.h>
+#include "SecRecoveryKey.h"
+#include "SOSKeyedPubKeyIdentifier.h"
#include "SOSInternal.h"
+#include "SecADWrapper.h"
+
+CFStringRef bskbRkbgPrefix = CFSTR("RK");
//
// MARK: Type creation
CFReleaseNull(vb->wrapped_keys);
}
+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;
+}
+
static CFStringRef SOSBackupSliceKeyBagCopyFormatDescription(CFTypeRef aObj, CFDictionaryRef formatOptions) {
SOSBackupSliceKeyBagRef vb = (SOSBackupSliceKeyBagRef) aObj;
- CFMutableStringRef description = CFStringCreateMutable(kCFAllocatorDefault, 0);
-
- CFStringAppendFormat(description, NULL, CFSTR("<SOSBackupSliceKeyBag@%p %ld"), vb, vb->peers ? CFSetGetCount(vb->peers) : 0);
- CFStringAppend(description, CFSTR(">"));
+ 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(">"));
- return description;
+ return retval;
}
size_t der_sizeof_BackupSliceKeyBag(SOSBackupSliceKeyBagRef BackupSliceKeyBag, CFErrorRef *error) {
size_t result = 0;
+ 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.
+
size_t bag_size = der_sizeof_data(BackupSliceKeyBag->aks_bag, error);
require_quiet(bag_size, fail);
if (der_end == NULL) return der_end;
require_quiet(SecRequirementError(set != NULL, error, CFSTR("Null set passed to encode")), fail);
+ 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.
der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
der_encode_data(set->aks_bag, error, der,
}
+
SOSBackupSliceKeyBagRef SOSBackupSliceKeyBagCreateFromData(CFAllocatorRef allocator, CFDataRef data, CFErrorRef *error) {
SOSBackupSliceKeyBagRef result = NULL;
SOSBackupSliceKeyBagRef decodedBag = NULL;
}
-static CFDictionaryRef SOSBackupSliceKeyBagCopyWrappedKeys(SOSBackupSliceKeyBagRef vb, CFDataRef secret, CFErrorRef *error) {
+static CFDictionaryRef SOSBackupSliceKeyBagCopyWrappedKeys(SOSBackupSliceKeyBagRef vb, CFDataRef secret, CFDictionaryRef additionalKeys, CFErrorRef *error) {
CFDictionaryRef result = NULL;
CFMutableDictionaryRef wrappedKeys = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
CFDataRef backupKey = SOSPeerInfoCopyBackupKey(pi);
if (backupKey) {
- CFDataRef wrappedKey = SOSCopyECWrapped(backupKey, secret, error);
+ CFErrorRef wrapError = NULL;
+ CFDataRef wrappedKey = SOSCopyECWrapped(backupKey, secret, &wrapError);
if (wrappedKey) {
CFDictionaryAddValue(wrappedKeys, id, wrappedKey);
+ CFDataPerformWithHexString(backupKey, ^(CFStringRef backupKeyString) {
+ CFDataPerformWithHexString(wrappedKey, ^(CFStringRef wrappedKeyString) {
+ secnotice("bskb", "Add for id: %@, bk: %@, wrapped: %@", id, backupKeyString, wrappedKeyString);
+ });
+ });
} else {
+ CFDataPerformWithHexString(backupKey, ^(CFStringRef backupKeyString) {
+ secnotice("bskb", "Failed at id: %@, bk: %@ error: %@", id, backupKeyString, wrapError);
+ });
+ CFErrorPropagate(wrapError, error);
success = false;
}
CFReleaseNull(wrappedKey);
CFReleaseNull(backupKey);
+ } else {
+ secnotice("bskb", "Skipping id %@, no backup key.", id);
}
}
});
+ 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);
+ }
+ });
+
if (success)
CFTransferRetained(result, wrappedKeys);
return result;
}
-static bool SOSBackupSliceKeyBagCreateBackupBag(SOSBackupSliceKeyBagRef vb, CFErrorRef* error) {
+static bool SOSBackupSliceKeyBagCreateBackupBag(SOSBackupSliceKeyBagRef vb, CFDictionaryRef/*CFDataRef*/ additionalKeys, CFErrorRef* error) {
CFReleaseNull(vb->aks_bag);
// Choose a random key.
PerformWithBufferAndClear(kAKSBagSecretLength, ^(size_t size, uint8_t *buffer) {
- SecRandomCopyBytes(kSecRandomDefault, size, buffer);
- CFDataRef secret = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buffer, size, kCFAllocatorNull);
+ CFDataRef secret = NULL;
+
+ require_quiet(SecError(SecRandomCopyBytes(kSecRandomDefault, size, buffer), error, CFSTR("SecRandom falied!")), fail);
- CFAssignRetained(vb->wrapped_keys, SOSBackupSliceKeyBagCopyWrappedKeys(vb, secret, error));
+ secret = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buffer, size, kCFAllocatorNull);
+
+ CFAssignRetained(vb->wrapped_keys, SOSBackupSliceKeyBagCopyWrappedKeys(vb, secret, additionalKeys, error));
CFAssignRetained(vb->aks_bag, SecAKSCopyBackupBagWithSecret(size, buffer, error));
+ fail:
CFReleaseSafe(secret);
});
}
SOSBackupSliceKeyBagRef SOSBackupSliceKeyBagCreate(CFAllocatorRef allocator, CFSetRef peers, CFErrorRef* error) {
+ 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) {
SOSBackupSliceKeyBagRef result = NULL;
SOSBackupSliceKeyBagRef vb = CFTypeAllocate(SOSBackupSliceKeyBag, struct __OpaqueSOSBackupSliceKeyBag, allocator);
require_quiet(SecAllocationError(vb, error, CFSTR("View bag allocation failed")), fail);
vb->peers = SOSBackupSliceKeyBagCreatePeerSet(allocator, peers);
vb->wrapped_keys = CFDictionaryCreateMutableForCFTypes(allocator);
- require_quiet(SOSBackupSliceKeyBagCreateBackupBag(vb, error), fail);
+ require_quiet(SOSBackupSliceKeyBagCreateBackupBag(vb, additionalKeys, error), fail);
CFTransferRetained(result, vb);
return result;
}
+
SOSBackupSliceKeyBagRef SOSBackupSliceKeyBagCreateDirect(CFAllocatorRef allocator, CFDataRef aks_bag, CFErrorRef *error)
{
SOSBackupSliceKeyBagRef result = NULL;
return backupSliceKeyBag->peers;
}
+int SOSBSKBCountPeers(SOSBackupSliceKeyBagRef backupSliceKeyBag) {
+ return (int) CFSetGetCount(backupSliceKeyBag->peers);
+}
+
+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;
+}
+
+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
+
+}
+
static keybag_handle_t SOSBSKBLoadAndUnlockBagWithSecret(SOSBackupSliceKeyBagRef backupSliceKeyBag,
size_t secretSize, const uint8_t *secret,
CFErrorRef *error)
exit:
return result;
}
+
+#include "SecRecoveryKey.h"
+
+static bool SOSPerformWithRecoveryKeyFullKey(CFDataRef wrappingSecret, CFErrorRef *error, void (^operation)(ccec_full_ctx_t fullKey, CFStringRef keyID)) {
+ bool result = false;
+
+ CFStringRef keyID = NULL;
+ SecRecoveryKey *sRecKey = NULL;
+ CFDataRef fullKeyBytes = NULL;
+ CFDataRef pubKeyBytes = NULL;
+ CFStringRef restoreKeySecret = CFStringCreateWithBytes(kCFAllocatorDefault, CFDataGetBytePtr(wrappingSecret), CFDataGetLength(wrappingSecret), kCFStringEncodingUTF8, false);
+ require_action_quiet(restoreKeySecret, errOut, SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("Unable to create key string from data.")));
+ sRecKey = SecRKCreateRecoveryKey(restoreKeySecret);
+ require_action_quiet(sRecKey, errOut, SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("Unable to create recovery key from string.")));
+ fullKeyBytes = SecRKCopyBackupFullKey(sRecKey);
+ pubKeyBytes = SecRKCopyBackupPublicKey(sRecKey);
+ require_action_quiet(fullKeyBytes && pubKeyBytes, errOut, SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("Unable to create recovery key public and private keys.")));
+ keyID = SOSCopyIDOfDataBuffer(pubKeyBytes, error);
+ require_quiet(keyID, errOut);
+ {
+ size_t keysize = ccec_compact_import_priv_size(CFDataGetLength(fullKeyBytes));
+ 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);
+ int res = ccec_compact_import_priv(cp, CFDataGetLength(fullKeyBytes), CFDataGetBytePtr(fullKeyBytes), fullKey);
+ 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(sRecKey);
+ CFReleaseNull(fullKeyBytes);
+ CFReleaseNull(pubKeyBytes);
+ 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;
+}
+
+static CFDictionaryRef SOSBSKBCopyAdditionalKeysWithPrefix(CFAllocatorRef allocator, SOSBackupSliceKeyBagRef bskb, CFStringRef prefix) {
+ 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);
+ bool haveKeys = CFDictionaryGetCount(keyDict) > 0;
+ CFReleaseNull(keyDict);
+ return haveKeys;
+}
+
+CFDataRef SOSBSKBCopyRecoveryKey(SOSBackupSliceKeyBagRef bskb) {
+ CFDictionaryRef keyDict = SOSBSKBCopyAdditionalKeysWithPrefix(kCFAllocatorDefault, bskb, bskbRkbgPrefix);
+ if(CFDictionaryGetCount(keyDict) == 1) {
+ __block CFDataRef keyData = NULL;
+ CFDictionaryForEach(keyDict, ^(const void *key, const void *value) {
+ keyData = asData(value, NULL);
+ });
+ return CFDataCreateCopy(kCFAllocatorDefault, keyData);
+ }
+ CFReleaseNull(keyDict);
+ return NULL;
+}
+
+bool SOSBSKBHasRecoveryKey(SOSBackupSliceKeyBagRef bskb) {
+ if(SOSBSKBHasPrefixedKey(bskb, bskbRkbgPrefix)) return true;
+ // old way for RecoveryKeys
+ int keyCount = (int) CFDictionaryGetCount(bskb->wrapped_keys);
+ int peerCount = SOSBSKBCountPeers(bskb);
+ return !SOSBSKBIsDirect(bskb) && ((keyCount - peerCount) > 0);
+}
+