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 }