+
+#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);
+}
+