]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecRecoveryKey.m
Security-57740.51.3.tar.gz
[apple/security.git] / OSX / sec / Security / SecRecoveryKey.m
1 //
2 // SecRecoveryKey.c
3 //
4
5 #import "SecRecoveryKey.h"
6
7 #import <corecrypto/cchkdf.h>
8 #import <corecrypto/ccsha2.h>
9 #import <corecrypto/ccec.h>
10
11 #import <utilities/SecCFWrappers.h>
12 #import <CommonCrypto/CommonRandomSPI.h>
13 #import <AssertMacros.h>
14
15 #import <Security/SecureObjectSync/SOSCloudCircle.h>
16
17 #import "SecCFAllocator.h"
18 #import "SecPasswordGenerate.h"
19 #import "SecBase64.h"
20
21 typedef struct _CFSecRecoveryKey *CFSecRecoveryKeyRef;
22
23
24 static uint8_t backupPublicKey[] = { 'B', 'a', 'c', 'k', 'u', ' ', 'P', 'u', 'b', 'l', 'i', 'c', 'k', 'e', 'y' };
25 static uint8_t passwordInfoKey[] = { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd', ' ', 's', 'e', 'c', 'r', 'e', 't' };
26
27 #define RK_BACKUP_HKDF_SIZE 128
28 #define RK_PASSWORD_HKDF_SIZE 32
29
30 CFGiblisFor(CFSecRecoveryKey);
31
32 struct _CFSecRecoveryKey {
33 CFRuntimeBase _base;
34 CFDataRef basecode;
35 };
36
37 static void
38 CFSecRecoveryKeyDestroy(CFTypeRef cf)
39 {
40 CFSecRecoveryKeyRef rk = (CFSecRecoveryKeyRef)cf;
41 CFReleaseNull(rk->basecode);
42 }
43
44
45 static CFStringRef
46 CFSecRecoveryKeyCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions)
47 {
48 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SecRecoveryKey: %p>"), cf);
49 }
50
51
52 static bool
53 ValidateRecoveryKey(CFStringRef recoveryKey)
54 {
55
56 return SecPasswordValidatePasswordFormat(kSecPasswordTypeiCloudRecoveryKey, recoveryKey, NULL);
57 }
58
59
60 NSString *
61 SecRKCreateRecoveryKeyString(NSError **error)
62 {
63 CFErrorRef cferror = NULL;
64
65 CFStringRef recoveryKey = SecPasswordGenerate(kSecPasswordTypeiCloudRecoveryKey, &cferror, NULL);
66 if (recoveryKey == NULL) {
67 if (error) {
68 *error = CFBridgingRelease(cferror);
69 } else {
70 CFReleaseNull(cferror);
71 }
72 return NULL;
73 }
74 if (!ValidateRecoveryKey(recoveryKey)) {
75 CFRelease(recoveryKey);
76 return NULL;
77 }
78
79 return (__bridge NSString *)recoveryKey;
80 }
81
82
83 SecRecoveryKey *
84 SecRKCreateRecoveryKey(NSString *masterKey)
85 {
86 if (!ValidateRecoveryKey((__bridge CFStringRef)masterKey))
87 return NULL;
88
89 CFSecRecoveryKeyRef rk = CFTypeAllocate(CFSecRecoveryKey, struct _CFSecRecoveryKey, NULL);
90 if (rk == NULL)
91 return NULL;
92
93 rk->basecode = CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(),
94 (__bridge CFStringRef)masterKey,
95 kCFStringEncodingUTF8, 0);
96 if (rk->basecode == NULL) {
97 CFRelease(rk);
98 return NULL;
99 }
100
101 return (__bridge SecRecoveryKey *)rk;
102 }
103
104 static CFDataRef
105 SecRKCreateDerivedSecret(CFSecRecoveryKeyRef rk, CFIndex outputLength,
106 const uint8_t *variant, size_t variantLength)
107 {
108 CFMutableDataRef derived;
109 int status;
110
111 derived = CFDataCreateMutableWithScratch(SecCFAllocatorZeroize(), outputLength);
112 if (derived == NULL)
113 return NULL;
114
115 status = cchkdf(ccsha256_di(),
116 CFDataGetLength(rk->basecode), CFDataGetBytePtr(rk->basecode),
117 4, "salt",
118 variantLength, variant,
119 CFDataGetLength(derived), CFDataGetMutableBytePtr(derived));
120 if (status) {
121 CFReleaseNull(derived);
122 }
123 return derived;
124 }
125
126
127 NSString *
128 SecRKCopyAccountRecoveryPassword(SecRecoveryKey *rk)
129 {
130 CFStringRef base64Data = NULL;
131 CFDataRef derived = NULL;
132 void *b64string = NULL;
133 size_t base64Len = 0;
134
135 derived = SecRKCreateDerivedSecret((__bridge CFSecRecoveryKeyRef)rk,
136 RK_PASSWORD_HKDF_SIZE,
137 passwordInfoKey, sizeof(passwordInfoKey));
138 require(derived, fail);
139
140 base64Len = SecBase64Encode(CFDataGetBytePtr(derived), CFDataGetLength(derived), NULL, 0);
141 assert(base64Len < 1024);
142
143 b64string = malloc(base64Len);
144 require(b64string, fail);
145
146 SecBase64Encode(CFDataGetBytePtr(derived), CFDataGetLength(derived), b64string, base64Len);
147
148 base64Data = CFStringCreateWithBytes(SecCFAllocatorZeroize(),
149 (const UInt8 *)b64string, base64Len,
150 kCFStringEncodingUTF8, false);
151 require(base64Data, fail);
152
153 fail:
154 if (b64string) {
155 cc_clear(base64Len, b64string);
156 free(b64string);
157 }
158 CFReleaseNull(derived);
159
160 return (__bridge NSString *)base64Data;
161 }
162
163 #if 0
164 NSString *
165 SecRKCopyAccountRecoveryVerifier(SecRecoveryKey *rk,
166 NSString *type,
167 NSData *salt,
168 NSNumber *iterations,
169 NSError **error)
170 {
171 /* use verifier create function from AppleIDAuthSupport with dlopen/dlsym
172
173 CFDataRef
174 AppleIDAuthSupportCreateVerifier(CFStringRef proto,
175 CFStringRef username,
176 CFDataRef salt,
177 CFNumberRef iter,
178 CFStringRef password,
179 CFErrorRef *error);
180 */
181
182 return NULL;
183 }
184 #endif
185
186 static NSData *
187 RKBackupCreateECKey(SecRecoveryKey *rk, bool fullkey)
188 {
189 CFMutableDataRef publicKeyData = NULL;
190 CFDataRef derivedSecret = NULL;
191 ccec_const_cp_t cp = ccec_cp_256();
192 CFDataRef result = NULL;
193 int status;
194
195 ccec_full_ctx_decl_cp(cp, fullKey);
196
197 derivedSecret = SecRKCreateDerivedSecret((__bridge CFSecRecoveryKeyRef)rk, RK_BACKUP_HKDF_SIZE,
198 backupPublicKey, sizeof(backupPublicKey));
199 require(derivedSecret, fail);
200
201 status = ccec_generate_key_deterministic(cp,
202 CFDataGetLength(derivedSecret), CFDataGetBytePtr(derivedSecret),
203 ccDRBGGetRngState(),
204 CCEC_GENKEY_DETERMINISTIC_COMPACT,
205 fullKey);
206 require_noerr(status, fail);
207
208 size_t space = ccec_compact_export_size(fullkey, fullKey);
209 publicKeyData = CFDataCreateMutableWithScratch(SecCFAllocatorZeroize(), space);
210 require_quiet(publicKeyData, fail);
211
212 ccec_compact_export(fullkey, CFDataGetMutableBytePtr(publicKeyData), fullKey);
213
214 CFTransferRetained(result, publicKeyData);
215 fail:
216 CFReleaseNull(derivedSecret);
217 CFReleaseNull(publicKeyData);
218
219 return (__bridge NSData *)result;
220 }
221
222 NSData *
223 SecRKCopyBackupFullKey(SecRecoveryKey *rk)
224 {
225 return RKBackupCreateECKey(rk, true);
226 }
227
228
229 NSData *
230 SecRKCopyBackupPublicKey(SecRecoveryKey *rk)
231 {
232 return RKBackupCreateECKey(rk, false);
233 }
234
235 bool
236 SecRKRegisterBackupPublicKey(SecRecoveryKey *rk, CFErrorRef *error)
237 {
238 CFDataRef backupKey = (__bridge CFDataRef)SecRKCopyBackupPublicKey(rk);
239 bool res = false;
240
241 require(backupKey, fail);
242
243 res = SOSCCRegisterRecoveryPublicKey(backupKey, error);
244 fail:
245 CFReleaseNull(backupKey);
246
247 return res;
248 }