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