]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecRecoveryKey.m
Security-58286.31.2.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
13 #import <utilities/SecCFWrappers.h>
14 #import <CommonCrypto/CommonRandomSPI.h>
15 #import <AssertMacros.h>
16
17
18 #import <Security/SecureObjectSync/SOSCloudCircle.h>
19 #import <Security/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_IPHONE_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
81 NSString *
82 SecRKCreateRecoveryKeyString(NSError **error)
83 {
84 CFErrorRef cferror = NULL;
85
86 CFStringRef recoveryKey = SecPasswordGenerate(kSecPasswordTypeiCloudRecoveryKey, &cferror, NULL);
87 if (recoveryKey == NULL) {
88 if (error) {
89 *error = CFBridgingRelease(cferror);
90 } else {
91 CFReleaseNull(cferror);
92 }
93 return NULL;
94 }
95 if (!ValidateRecoveryKey(recoveryKey, error)) {
96 CFRelease(recoveryKey);
97 return NULL;
98 }
99
100 return (__bridge NSString *)recoveryKey;
101 }
102
103 SecRecoveryKey *
104 SecRKCreateRecoveryKey(NSString *masterKey)
105 {
106 return SecRKCreateRecoveryKeyWithError(masterKey, NULL);
107 }
108
109 SecRecoveryKey *
110 SecRKCreateRecoveryKeyWithError(NSString *masterKey, NSError **error)
111 {
112 if (!ValidateRecoveryKey((__bridge CFStringRef)masterKey, error)) {
113 return NULL;
114 }
115
116 CFSecRecoveryKeyRef rk = CFTypeAllocate(CFSecRecoveryKey, struct _CFSecRecoveryKey, NULL);
117 if (rk == NULL)
118 return NULL;
119
120 rk->basecode = CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(),
121 (__bridge CFStringRef)masterKey,
122 kCFStringEncodingUTF8, 0);
123 if (rk->basecode == NULL) {
124 CFRelease(rk);
125 return NULL;
126 }
127
128 return (__bridge SecRecoveryKey *)rk;
129 }
130
131 static CFDataRef
132 SecRKCreateDerivedSecret(CFSecRecoveryKeyRef rk, CFIndex outputLength,
133 const uint8_t *variant, size_t variantLength)
134 {
135 CFMutableDataRef derived;
136 int status;
137
138 derived = CFDataCreateMutableWithScratch(SecCFAllocatorZeroize(), outputLength);
139 if (derived == NULL)
140 return NULL;
141
142 status = cchkdf(ccsha256_di(),
143 CFDataGetLength(rk->basecode), CFDataGetBytePtr(rk->basecode),
144 4, "salt",
145 variantLength, variant,
146 CFDataGetLength(derived), CFDataGetMutableBytePtr(derived));
147 if (status) {
148 CFReleaseNull(derived);
149 }
150 return derived;
151 }
152
153
154 NSString *
155 SecRKCopyAccountRecoveryPassword(SecRecoveryKey *rk)
156 {
157 CFStringRef base64Data = NULL;
158 CFDataRef derived = NULL;
159 void *b64string = NULL;
160 size_t base64Len = 0;
161
162 derived = SecRKCreateDerivedSecret((__bridge CFSecRecoveryKeyRef)rk,
163 RK_PASSWORD_HKDF_SIZE,
164 passwordInfoKey, sizeof(passwordInfoKey));
165 require(derived, fail);
166
167 base64Len = SecBase64Encode(CFDataGetBytePtr(derived), CFDataGetLength(derived), NULL, 0);
168 assert(base64Len < 1024);
169
170 b64string = malloc(base64Len);
171 require(b64string, fail);
172
173 SecBase64Encode(CFDataGetBytePtr(derived), CFDataGetLength(derived), b64string, base64Len);
174
175 base64Data = CFStringCreateWithBytes(SecCFAllocatorZeroize(),
176 (const UInt8 *)b64string, base64Len,
177 kCFStringEncodingUTF8, false);
178 require(base64Data, fail);
179
180 fail:
181 if (b64string) {
182 cc_clear(base64Len, b64string);
183 free(b64string);
184 }
185 CFReleaseNull(derived);
186
187 return (__bridge NSString *)base64Data;
188 }
189
190 // We should gen salt/iteration - use S2K for kdf for the time being
191 // Pass back a dictionary of the parms
192 //
193 // Need companion call to respond with MRK on the "iforgot" sequence.
194
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";
200
201 #if !TARGET_OS_BRIDGE
202
203 CFStringRef localProtocolSRPGROUP;
204 CFDataRef (*localAppleIDauthSupportCreateVerifierPtr) (CFStringRef proto,
205 CFStringRef username,
206 CFDataRef salt,
207 CFNumberRef iter,
208 CFStringRef password,
209 CFErrorRef *error);
210
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);
215 if(tmpptr) {
216 retval = *(CFStringRef*) tmpptr;
217 }
218 return retval;
219 }
220
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);
228 if(framework) {
229 localProtocolSRPGROUP = getdlsymforString(framework,
230 "kAppleIDAuthSupportProtocolSRPGROUP2048SHA256PBKDF");
231 localAppleIDauthSupportCreateVerifierPtr =
232 dlsym(framework, "AppleIDAuthSupportCreateVerifier");
233 }
234 });
235 return (framework != NULL && localProtocolSRPGROUP != NULL &&
236 localAppleIDauthSupportCreateVerifierPtr != NULL);
237 }
238 #endif
239 #endif
240
241 NSDictionary *
242 SecRKCopyAccountRecoveryVerifier(NSString *recoveryKey,
243 NSError **error) {
244
245 #if defined(__i386__) || TARGET_IPHONE_SIMULATOR || TARGET_OS_BRIDGE
246 abort();
247 return NULL;
248 #else
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;
255 return NULL;
256 }
257
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,
263 username,
264 (__bridge CFDataRef) salt,
265 (__bridge CFNumberRef) iterations,
266 (__bridge CFStringRef) (recoveryKey),
267 &localError);
268 SecRecoveryKey *srk = SecRKCreateRecoveryKey(recoveryKey);
269 NSData *masterKeyID = (__bridge_transfer NSData*) SecRKCreateDerivedSecret(
270 (__bridge CFSecRecoveryKeyRef) srk,
271 RK_PASSWORD_HKDF_SIZE,
272 masterkeyIDSalt,
273 sizeof(masterkeyIDSalt));
274 if(verifier && masterKeyID) {
275 retval = @{ kSecRVSalt: salt,
276 kSecRVIterations: iterations,
277 kSecRVProtocol: protocol,
278 kSecRVVerifier: verifier,
279 kSecRVMasterID: masterKeyID };
280
281 } else {
282 if(error && localError) *error = (__bridge NSError *) localError;
283 }
284 return retval;
285 #endif
286
287 }
288
289 // This recreates the key pair using the recovery key string.
290 static NSData *
291 RKBackupCreateECKey(SecRecoveryKey *rk, bool returnFullkey)
292 {
293 CFMutableDataRef keyData = NULL;
294 CFDataRef derivedSecret = NULL;
295 ccec_const_cp_t cp = ccec_cp_256();
296 CFDataRef result = NULL;
297 int status;
298
299 ccec_full_ctx_decl_cp(cp, fullKey);
300
301 derivedSecret = SecRKCreateDerivedSecret((__bridge CFSecRecoveryKeyRef)rk, RK_BACKUP_HKDF_SIZE,
302 backupPublicKey, sizeof(backupPublicKey));
303 require(derivedSecret, fail);
304
305 status = ccec_generate_key_deterministic(cp,
306 CFDataGetLength(derivedSecret), CFDataGetBytePtr(derivedSecret),
307 ccDRBGGetRngState(),
308 CCEC_GENKEY_DETERMINISTIC_COMPACT,
309 fullKey);
310 require_noerr(status, fail);
311
312 size_t space = ccec_compact_export_size(returnFullkey, ccec_ctx_pub(fullKey));
313 keyData = CFDataCreateMutableWithScratch(SecCFAllocatorZeroize(), space);
314 require_quiet(keyData, fail);
315
316 ccec_compact_export(returnFullkey, CFDataGetMutableBytePtr(keyData), fullKey);
317
318 CFTransferRetained(result, keyData);
319 fail:
320 CFReleaseNull(derivedSecret);
321 CFReleaseNull(keyData);
322
323 return (__bridge NSData *)result;
324 }
325
326 NSData *
327 SecRKCopyBackupFullKey(SecRecoveryKey *rk)
328 {
329 return RKBackupCreateECKey(rk, true);
330 }
331
332
333 NSData *
334 SecRKCopyBackupPublicKey(SecRecoveryKey *rk)
335 {
336 return RKBackupCreateECKey(rk, false);
337 }
338
339 bool
340 SecRKRegisterBackupPublicKey(SecRecoveryKey *rk, CFErrorRef *error)
341 {
342 CFDataRef backupKey = (__bridge CFDataRef)SecRKCopyBackupPublicKey(rk);
343 bool res = false;
344
345 require_action_quiet(backupKey, fail, SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to create key from rk"), NULL, error));
346
347 res = SOSCCRegisterRecoveryPublicKey(backupKey, error);
348
349 fail:
350 CFReleaseNull(backupKey);
351
352 return res;
353 }