5 #import "SecRecoveryKey.h"
6 #import <dispatch/dispatch.h>
9 #import <corecrypto/cchkdf.h>
10 #import <corecrypto/ccsha2.h>
11 #import <corecrypto/ccec.h>
13 #import <utilities/SecCFWrappers.h>
14 #import <CommonCrypto/CommonRandomSPI.h>
15 #import <AssertMacros.h>
18 #import <Security/SecureObjectSync/SOSCloudCircle.h>
19 #import <Security/SecureObjectSync/SOSInternal.h>
23 #include <AppleIDAuthSupport/AppleIDAuthSupport.h>
24 #define PATH_FOR_APPLEIDAUTHSUPPORTFRAMEWORK "/System/Library/PrivateFrameworks/AppleIDAuthSupport.framework/AppleIDAuthSupport"
27 #import "SecCFAllocator.h"
28 #import "SecPasswordGenerate.h"
31 typedef struct _CFSecRecoveryKey *CFSecRecoveryKeyRef;
34 static uint8_t backupPublicKey[] = { 'B', 'a', 'c', 'k', 'u', ' ', 'P', 'u', 'b', 'l', 'i', 'c', 'k', 'e', 'y' };
35 static uint8_t passwordInfoKey[] = { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd', ' ', 's', 'e', 'c', 'r', 'e', 't' };
36 #if !(defined(__i386__) || TARGET_IPHONE_SIMULATOR || TARGET_OS_BRIDGE)
37 static uint8_t masterkeyIDSalt[] = { 'M', 'a', 's', 't', 'e', 'r', ' ', 'K', 'e', 'y', ' ', 'I', 'd', 'e', 't' };
40 #define RK_BACKUP_HKDF_SIZE 128
41 #define RK_PASSWORD_HKDF_SIZE 32
43 CFGiblisFor(CFSecRecoveryKey);
45 struct _CFSecRecoveryKey {
51 CFSecRecoveryKeyDestroy(CFTypeRef cf)
53 CFSecRecoveryKeyRef rk = (CFSecRecoveryKeyRef)cf;
54 CFReleaseNull(rk->basecode);
59 CFSecRecoveryKeyCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions)
61 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SecRecoveryKey: %p>"), cf);
66 ValidateRecoveryKey(CFStringRef masterkey, NSError **error)
68 CFErrorRef cferror = NULL;
69 bool res = SecPasswordValidatePasswordFormat(kSecPasswordTypeiCloudRecoveryKey, masterkey, &cferror);
72 *error = CFBridgingRelease(cferror);
74 CFReleaseNull(cferror);
82 SecRKCreateRecoveryKeyString(NSError **error)
84 CFErrorRef cferror = NULL;
86 CFStringRef recoveryKey = SecPasswordGenerate(kSecPasswordTypeiCloudRecoveryKey, &cferror, NULL);
87 if (recoveryKey == NULL) {
89 *error = CFBridgingRelease(cferror);
91 CFReleaseNull(cferror);
95 if (!ValidateRecoveryKey(recoveryKey, error)) {
96 CFRelease(recoveryKey);
100 return (__bridge NSString *)recoveryKey;
104 SecRKCreateRecoveryKey(NSString *masterKey)
106 return SecRKCreateRecoveryKeyWithError(masterKey, NULL);
110 SecRKCreateRecoveryKeyWithError(NSString *masterKey, NSError **error)
112 if (!ValidateRecoveryKey((__bridge CFStringRef)masterKey, error)) {
116 CFSecRecoveryKeyRef rk = CFTypeAllocate(CFSecRecoveryKey, struct _CFSecRecoveryKey, NULL);
120 rk->basecode = CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(),
121 (__bridge CFStringRef)masterKey,
122 kCFStringEncodingUTF8, 0);
123 if (rk->basecode == NULL) {
128 return (__bridge SecRecoveryKey *)rk;
132 SecRKCreateDerivedSecret(CFSecRecoveryKeyRef rk, CFIndex outputLength,
133 const uint8_t *variant, size_t variantLength)
135 CFMutableDataRef derived;
138 derived = CFDataCreateMutableWithScratch(SecCFAllocatorZeroize(), outputLength);
142 status = cchkdf(ccsha256_di(),
143 CFDataGetLength(rk->basecode), CFDataGetBytePtr(rk->basecode),
145 variantLength, variant,
146 CFDataGetLength(derived), CFDataGetMutableBytePtr(derived));
148 CFReleaseNull(derived);
155 SecRKCopyAccountRecoveryPassword(SecRecoveryKey *rk)
157 CFStringRef base64Data = NULL;
158 CFDataRef derived = NULL;
159 void *b64string = NULL;
160 size_t base64Len = 0;
162 derived = SecRKCreateDerivedSecret((__bridge CFSecRecoveryKeyRef)rk,
163 RK_PASSWORD_HKDF_SIZE,
164 passwordInfoKey, sizeof(passwordInfoKey));
165 require(derived, fail);
167 base64Len = SecBase64Encode(CFDataGetBytePtr(derived), CFDataGetLength(derived), NULL, 0);
168 assert(base64Len < 1024);
170 b64string = malloc(base64Len);
171 require(b64string, fail);
173 SecBase64Encode(CFDataGetBytePtr(derived), CFDataGetLength(derived), b64string, base64Len);
175 base64Data = CFStringCreateWithBytes(SecCFAllocatorZeroize(),
176 (const UInt8 *)b64string, base64Len,
177 kCFStringEncodingUTF8, false);
178 require(base64Data, fail);
182 cc_clear(base64Len, b64string);
185 CFReleaseNull(derived);
187 return (__bridge NSString *)base64Data;
190 // We should gen salt/iteration - use S2K for kdf for the time being
191 // Pass back a dictionary of the parms
193 // Need companion call to respond with MRK on the "iforgot" sequence.
195 NSString *const kSecRVSalt = @"s";
196 NSString *const kSecRVIterations = @"i";
197 NSString *const kSecRVProtocol = @"p";
198 NSString *const kSecRVVerifier = @"v";
199 NSString *const kSecRVMasterID = @"mkid";
201 #if !TARGET_OS_BRIDGE
203 CFStringRef localProtocolSRPGROUP;
204 CFDataRef (*localAppleIDauthSupportCreateVerifierPtr) (CFStringRef proto,
205 CFStringRef username,
208 CFStringRef password,
211 #if !(defined(__i386__) || TARGET_IPHONE_SIMULATOR)
212 static CFStringRef getdlsymforString(void *framework, const char *symbol) {
213 CFStringRef retval = NULL;
214 void *tmpptr = dlsym(framework, symbol);
216 retval = *(CFStringRef*) tmpptr;
221 static bool connectAppleIDFrameworkSymbols(void) {
222 static dispatch_once_t onceToken;
223 static void* framework = NULL;
224 dispatch_once(&onceToken, ^{
225 localAppleIDauthSupportCreateVerifierPtr = NULL;
226 localProtocolSRPGROUP = NULL;
227 framework = dlopen(PATH_FOR_APPLEIDAUTHSUPPORTFRAMEWORK, RTLD_NOW);
229 localProtocolSRPGROUP = getdlsymforString(framework,
230 "kAppleIDAuthSupportProtocolSRPGROUP2048SHA256PBKDF");
231 localAppleIDauthSupportCreateVerifierPtr =
232 dlsym(framework, "AppleIDAuthSupportCreateVerifier");
235 return (framework != NULL && localProtocolSRPGROUP != NULL &&
236 localAppleIDauthSupportCreateVerifierPtr != NULL);
242 SecRKCopyAccountRecoveryVerifier(NSString *recoveryKey,
245 #if defined(__i386__) || TARGET_IPHONE_SIMULATOR || TARGET_OS_BRIDGE
249 CFErrorRef localError = NULL;
250 CFStringRef username = CFSTR("foo");
251 NSDictionary *retval = nil;
252 if(!connectAppleIDFrameworkSymbols()) {
253 SOSCreateError(kSOSErrorUnsupported, CFSTR("Recovery Key Creation Not Supported on this platform"), NULL, &localError);
254 if(error) *error = (__bridge_transfer NSError *) localError;
258 NSData *salt = (__bridge_transfer NSData*) CFDataCreateWithRandomBytes(32);
259 NSNumber *iterations = @40000;
260 NSString *protocol = (__bridge NSString*) localProtocolSRPGROUP;
261 NSData *verifier = (__bridge_transfer NSData*) localAppleIDauthSupportCreateVerifierPtr(
262 localProtocolSRPGROUP,
264 (__bridge CFDataRef) salt,
265 (__bridge CFNumberRef) iterations,
266 (__bridge CFStringRef) (recoveryKey),
268 SecRecoveryKey *srk = SecRKCreateRecoveryKey(recoveryKey);
269 NSData *masterKeyID = (__bridge_transfer NSData*) SecRKCreateDerivedSecret(
270 (__bridge CFSecRecoveryKeyRef) srk,
271 RK_PASSWORD_HKDF_SIZE,
273 sizeof(masterkeyIDSalt));
274 if(verifier && masterKeyID) {
275 retval = @{ kSecRVSalt: salt,
276 kSecRVIterations: iterations,
277 kSecRVProtocol: protocol,
278 kSecRVVerifier: verifier,
279 kSecRVMasterID: masterKeyID };
282 if(error && localError) *error = (__bridge NSError *) localError;
289 // This recreates the key pair using the recovery key string.
291 RKBackupCreateECKey(SecRecoveryKey *rk, bool returnFullkey)
293 CFMutableDataRef keyData = NULL;
294 CFDataRef derivedSecret = NULL;
295 ccec_const_cp_t cp = ccec_cp_256();
296 CFDataRef result = NULL;
299 ccec_full_ctx_decl_cp(cp, fullKey);
301 derivedSecret = SecRKCreateDerivedSecret((__bridge CFSecRecoveryKeyRef)rk, RK_BACKUP_HKDF_SIZE,
302 backupPublicKey, sizeof(backupPublicKey));
303 require(derivedSecret, fail);
305 status = ccec_generate_key_deterministic(cp,
306 CFDataGetLength(derivedSecret), CFDataGetBytePtr(derivedSecret),
308 CCEC_GENKEY_DETERMINISTIC_COMPACT,
310 require_noerr(status, fail);
312 size_t space = ccec_compact_export_size(returnFullkey, ccec_ctx_pub(fullKey));
313 keyData = CFDataCreateMutableWithScratch(SecCFAllocatorZeroize(), space);
314 require_quiet(keyData, fail);
316 ccec_compact_export(returnFullkey, CFDataGetMutableBytePtr(keyData), fullKey);
318 CFTransferRetained(result, keyData);
320 CFReleaseNull(derivedSecret);
321 CFReleaseNull(keyData);
323 return (__bridge NSData *)result;
327 SecRKCopyBackupFullKey(SecRecoveryKey *rk)
329 return RKBackupCreateECKey(rk, true);
334 SecRKCopyBackupPublicKey(SecRecoveryKey *rk)
336 return RKBackupCreateECKey(rk, false);
340 SecRKRegisterBackupPublicKey(SecRecoveryKey *rk, CFErrorRef *error)
342 CFDataRef backupKey = (__bridge CFDataRef)SecRKCopyBackupPublicKey(rk);
345 require_action_quiet(backupKey, fail, SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to create key from rk"), NULL, error));
347 res = SOSCCRegisterRecoveryPublicKey(backupKey, error);
350 CFReleaseNull(backupKey);