]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecRecoveryKey.m
Security-59754.41.1.tar.gz
[apple/security.git] / OSX / sec / Security / SecRecoveryKey.m
1 //
2 // SecRecoveryKey.c
3 //
4
5 #import "SecRecoveryKey.h"
6 #import <dispatch/dispatch.h>
7
8
9 #import <corecrypto/cchkdf.h>
10 #import <corecrypto/ccsha2.h>
11 #import <corecrypto/ccec.h>
12 #import <corecrypto/ccrng.h>
13
14 #import <utilities/SecCFWrappers.h>
15 #import <AssertMacros.h>
16
17
18 #import <Security/SecureObjectSync/SOSCloudCircle.h>
19 #import "keychain/SecureObjectSync/SOSInternal.h"
20
21 #if !TARGET_OS_BRIDGE
22 #include <dlfcn.h>
23 #include <AppleIDAuthSupport/AppleIDAuthSupport.h>
24 #define PATH_FOR_APPLEIDAUTHSUPPORTFRAMEWORK "/System/Library/PrivateFrameworks/AppleIDAuthSupport.framework/AppleIDAuthSupport"
25 #endif
26
27 #import "SecCFAllocator.h"
28 #import "SecPasswordGenerate.h"
29 #import "SecBase64.h"
30
31 typedef struct _CFSecRecoveryKey *CFSecRecoveryKeyRef;
32
33
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_OS_SIMULATOR || TARGET_OS_BRIDGE)
37 static uint8_t masterkeyIDSalt[] = { 'M', 'a', 's', 't', 'e', 'r', ' ', 'K', 'e', 'y', ' ', 'I', 'd', 'e', 't' };
38 #endif
39
40 #define RK_BACKUP_HKDF_SIZE 128
41 #define RK_PASSWORD_HKDF_SIZE 32
42
43 CFGiblisFor(CFSecRecoveryKey);
44
45 struct _CFSecRecoveryKey {
46 CFRuntimeBase _base;
47 CFDataRef basecode;
48 };
49
50 static void
51 CFSecRecoveryKeyDestroy(CFTypeRef cf)
52 {
53 CFSecRecoveryKeyRef rk = (CFSecRecoveryKeyRef)cf;
54 CFReleaseNull(rk->basecode);
55 }
56
57
58 static CFStringRef
59 CFSecRecoveryKeyCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions)
60 {
61 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SecRecoveryKey: %p>"), cf);
62 }
63
64
65 static bool
66 ValidateRecoveryKey(CFStringRef masterkey, NSError **error)
67 {
68 CFErrorRef cferror = NULL;
69 bool res = SecPasswordValidatePasswordFormat(kSecPasswordTypeiCloudRecoveryKey, masterkey, &cferror);
70 if (!res) {
71 if (error) {
72 *error = CFBridgingRelease(cferror);
73 } else {
74 CFReleaseNull(cferror);
75 }
76 }
77 return res;
78 }
79
80 NSString *
81 SecRKCreateRecoveryKeyString(NSError **error)
82 {
83 CFErrorRef cferror = NULL;
84
85 CFStringRef recoveryKey = SecPasswordGenerate(kSecPasswordTypeiCloudRecoveryKey, &cferror, NULL);
86 if (recoveryKey == NULL) {
87 if (error) {
88 *error = CFBridgingRelease(cferror);
89 } else {
90 CFReleaseNull(cferror);
91 }
92 return NULL;
93 }
94 if (!ValidateRecoveryKey(recoveryKey, error)) {
95 CFRelease(recoveryKey);
96 return NULL;
97 }
98 return (__bridge NSString *)recoveryKey;
99 }
100
101 SecRecoveryKey *
102 SecRKCreateRecoveryKey(NSString *masterKey)
103 {
104 return SecRKCreateRecoveryKeyWithError(masterKey, NULL);
105 }
106
107 SecRecoveryKey *
108 SecRKCreateRecoveryKeyWithError(NSString *masterKey, NSError **error)
109 {
110 if (!ValidateRecoveryKey((__bridge CFStringRef)masterKey, error)) {
111 return NULL;
112 }
113
114 CFSecRecoveryKeyRef rk = CFTypeAllocate(CFSecRecoveryKey, struct _CFSecRecoveryKey, NULL);
115 if (rk == NULL)
116 return NULL;
117
118 rk->basecode = CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(),
119 (__bridge CFStringRef)masterKey,
120 kCFStringEncodingUTF8, 0);
121 if (rk->basecode == NULL) {
122 CFRelease(rk);
123 return NULL;
124 }
125 return (SecRecoveryKey *) CFBridgingRelease(rk);
126 }
127
128 static CFDataRef
129 SecRKCreateDerivedSecret(CFSecRecoveryKeyRef rk, CFIndex outputLength,
130 const uint8_t *variant, size_t variantLength)
131 {
132 CFMutableDataRef derived;
133 int status;
134
135 derived = CFDataCreateMutableWithScratch(SecCFAllocatorZeroize(), outputLength);
136 if (derived == NULL)
137 return NULL;
138
139 status = cchkdf(ccsha256_di(),
140 CFDataGetLength(rk->basecode), CFDataGetBytePtr(rk->basecode),
141 4, "salt",
142 variantLength, variant,
143 CFDataGetLength(derived), CFDataGetMutableBytePtr(derived));
144 if (status) {
145 CFReleaseNull(derived);
146 }
147 return derived;
148 }
149
150
151 NSString *
152 SecRKCopyAccountRecoveryPassword(SecRecoveryKey *rk)
153 {
154 CFStringRef base64Data = NULL;
155 CFDataRef derived = NULL;
156 void *b64string = NULL;
157 size_t base64Len = 0;
158
159 derived = SecRKCreateDerivedSecret((__bridge CFSecRecoveryKeyRef)rk,
160 RK_PASSWORD_HKDF_SIZE,
161 passwordInfoKey, sizeof(passwordInfoKey));
162 require(derived, fail);
163
164 base64Len = SecBase64Encode(CFDataGetBytePtr(derived), CFDataGetLength(derived), NULL, 0);
165 assert(base64Len < 1024);
166
167 b64string = malloc(base64Len);
168 require(b64string, fail);
169
170 SecBase64Encode(CFDataGetBytePtr(derived), CFDataGetLength(derived), b64string, base64Len);
171
172 base64Data = CFStringCreateWithBytes(SecCFAllocatorZeroize(),
173 (const UInt8 *)b64string, base64Len,
174 kCFStringEncodingUTF8, false);
175 require(base64Data, fail);
176
177 fail:
178 if (b64string) {
179 cc_clear(base64Len, b64string);
180 free(b64string);
181 }
182 CFReleaseNull(derived);
183
184 return (__bridge NSString *)base64Data;
185 }
186
187 // We should gen salt/iteration - use S2K for kdf for the time being
188 // Pass back a dictionary of the parms
189 //
190 // Need companion call to respond with MRK on the "iforgot" sequence.
191
192 NSString *const kSecRVSalt = @"s";
193 NSString *const kSecRVIterations = @"i";
194 NSString *const kSecRVProtocol = @"p";
195 NSString *const kSecRVVerifier = @"v";
196 NSString *const kSecRVMasterID = @"mkid";
197
198 #if !TARGET_OS_BRIDGE
199
200 CFStringRef localProtocolSRPGROUP;
201 CFDataRef (*localAppleIDauthSupportCreateVerifierPtr) (CFStringRef proto,
202 CFStringRef username,
203 CFDataRef salt,
204 CFNumberRef iter,
205 CFStringRef password,
206 CFErrorRef *error);
207
208 #if !(defined(__i386__) || TARGET_OS_SIMULATOR)
209 static CFStringRef getdlsymforString(void *framework, const char *symbol) {
210 CFStringRef retval = NULL;
211 void *tmpptr = dlsym(framework, symbol);
212 if(tmpptr) {
213 retval = *(CFStringRef*) tmpptr;
214 }
215 return retval;
216 }
217
218 static bool connectAppleIDFrameworkSymbols(void) {
219 static dispatch_once_t onceToken;
220 static void* framework = NULL;
221 dispatch_once(&onceToken, ^{
222 localAppleIDauthSupportCreateVerifierPtr = NULL;
223 localProtocolSRPGROUP = NULL;
224 framework = dlopen(PATH_FOR_APPLEIDAUTHSUPPORTFRAMEWORK, RTLD_NOW);
225 if(framework) {
226 localProtocolSRPGROUP = getdlsymforString(framework,
227 "kAppleIDAuthSupportProtocolSRPGROUP2048SHA256PBKDF");
228 localAppleIDauthSupportCreateVerifierPtr =
229 dlsym(framework, "AppleIDAuthSupportCreateVerifier");
230 }
231 });
232 return (framework != NULL && localProtocolSRPGROUP != NULL &&
233 localAppleIDauthSupportCreateVerifierPtr != NULL);
234 }
235 #endif
236 #endif
237
238 NSDictionary *
239 SecRKCopyAccountRecoveryVerifier(NSString *recoveryKey,
240 NSError **error) {
241
242 #if defined(__i386__) || TARGET_OS_SIMULATOR || TARGET_OS_BRIDGE
243 abort();
244 return NULL;
245 #else
246 CFErrorRef localError = NULL;
247 CFStringRef username = CFSTR("foo");
248 NSDictionary *retval = nil;
249 if(!connectAppleIDFrameworkSymbols()) {
250 SOSCreateError(kSOSErrorUnsupported, CFSTR("Recovery Key Creation Not Supported on this platform"), NULL, &localError);
251 if(error) *error = (__bridge_transfer NSError *) localError;
252 return NULL;
253 }
254
255 NSData *salt = (__bridge_transfer NSData*) CFDataCreateWithRandomBytes(32);
256 NSNumber *iterations = @40000;
257 NSString *protocol = (__bridge NSString*) localProtocolSRPGROUP;
258 NSData *verifier = (__bridge_transfer NSData*) localAppleIDauthSupportCreateVerifierPtr(
259 localProtocolSRPGROUP,
260 username,
261 (__bridge CFDataRef) salt,
262 (__bridge CFNumberRef) iterations,
263 (__bridge CFStringRef) (recoveryKey),
264 &localError);
265 SecRecoveryKey *srk = SecRKCreateRecoveryKey(recoveryKey);
266 NSData *masterKeyID = (__bridge_transfer NSData*) SecRKCreateDerivedSecret(
267 (__bridge CFSecRecoveryKeyRef) srk,
268 RK_PASSWORD_HKDF_SIZE,
269 masterkeyIDSalt,
270 sizeof(masterkeyIDSalt));
271 if(verifier && masterKeyID) {
272 retval = @{ kSecRVSalt: salt,
273 kSecRVIterations: iterations,
274 kSecRVProtocol: protocol,
275 kSecRVVerifier: verifier,
276 kSecRVMasterID: masterKeyID };
277
278 } else {
279 if(error && localError) *error = (__bridge NSError *) localError;
280 }
281 return retval;
282 #endif
283
284 }
285
286 // This recreates the key pair using the recovery key string.
287 static NSData *
288 RKBackupCreateECKey(SecRecoveryKey *rk, bool returnFullkey)
289 {
290 CFMutableDataRef keyData = NULL;
291 CFDataRef derivedSecret = NULL;
292 ccec_const_cp_t cp = ccec_cp_256();
293 CFDataRef result = NULL;
294 int status;
295
296 ccec_full_ctx_decl_cp(cp, fullKey);
297
298 derivedSecret = SecRKCreateDerivedSecret((__bridge CFSecRecoveryKeyRef)rk, RK_BACKUP_HKDF_SIZE,
299 backupPublicKey, sizeof(backupPublicKey));
300 require(derivedSecret, fail);
301
302 status = ccec_generate_key_deterministic(cp,
303 CFDataGetLength(derivedSecret), CFDataGetBytePtr(derivedSecret),
304 ccrng(NULL),
305 CCEC_GENKEY_DETERMINISTIC_COMPACT,
306 fullKey);
307 require_noerr(status, fail);
308
309 size_t space = ccec_compact_export_size(returnFullkey, ccec_ctx_pub(fullKey));
310 keyData = CFDataCreateMutableWithScratch(SecCFAllocatorZeroize(), space);
311 require_quiet(keyData, fail);
312
313 ccec_compact_export(returnFullkey, CFDataGetMutableBytePtr(keyData), fullKey);
314
315 CFTransferRetained(result, keyData);
316 fail:
317 CFReleaseNull(derivedSecret);
318 CFReleaseNull(keyData);
319
320 return (__bridge NSData *)result;
321 }
322
323 NSData *
324 SecRKCopyBackupFullKey(SecRecoveryKey *rk)
325 {
326 return RKBackupCreateECKey(rk, true);
327 }
328
329
330 NSData *
331 SecRKCopyBackupPublicKey(SecRecoveryKey *rk)
332 {
333 return RKBackupCreateECKey(rk, false);
334 }
335
336 bool
337 SecRKRegisterBackupPublicKey(SecRecoveryKey *rk, CFErrorRef *error)
338 {
339 CFDataRef backupKey = (__bridge CFDataRef)SecRKCopyBackupPublicKey(rk);
340 bool res = false;
341
342 require_action_quiet(backupKey, fail, SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to create key from rk"), NULL, error));
343
344 res = SOSCCRegisterRecoveryPublicKey(backupKey, error);
345
346 fail:
347 CFReleaseNull(backupKey);
348
349 return res;
350 }