TokenLogin.cpp
1 /* 2 * Copyright (c) 2016 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 #include "TokenLogin.h" 25 26 #include <Security/SecItem.h> 27 #include <Security/SecItemPriv.h> 28 #include <Security/SecKeyPriv.h> 29 #include "SecBase64P.h" 30 #include <Security/SecIdentity.h> 31 #include <Security/SecCertificatePriv.h> 32 #include <Security/SecKeychainPriv.h> 33 #include <security_utilities/cfutilities.h> 34 #include <libaks.h> 35 #include <libaks_smartcard.h> 36 37 extern "C" { 38 #include <ctkclient/ctkclient.h> 39 #include <coreauthd_spi.h> 40 } 41 42 static os_log_t TOKEN_LOG_DEFAULT() { 43 static dispatch_once_t once; 44 static os_log_t log; 45 dispatch_once(&once, ^{ log = os_log_create("com.apple.security", "tokenlogin"); }); 46 return log; 47 }; 48 #define TL_LOG TOKEN_LOG_DEFAULT() 49 50 #define kSecTokenLoginDomain CFSTR("com.apple.security.tokenlogin") 51 52 static CFStringRef CF_RETURNS_RETAINED cfDataToHex(CFDataRef bin) 53 { 54 size_t len = CFDataGetLength(bin) * 2; 55 CFMutableStringRef str = CFStringCreateMutable(NULL, len); 56 57 static const char* digits[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"}; 58 59 const uint8_t* data = CFDataGetBytePtr(bin); 60 for (size_t i = 0; i < CFDataGetLength(bin); i++) { 61 CFStringAppendCString(str, digits[data[i] >> 4], 1); 62 CFStringAppendCString(str, digits[data[i] & 0xf], 1); 63 } 64 return str; 65 } 66 67 static CFStringRef getPin(CFDictionaryRef context) 68 { 69 if (!context) { 70 return NULL; 71 } 72 73 CFStringRef pin = (CFStringRef)CFDictionaryGetValue(context, kSecAttrService); 74 if (!pin || CFGetTypeID(pin) != CFStringGetTypeID()) { 75 return NULL; 76 } 77 return pin; 78 } 79 80 static CFStringRef getTokenId(CFDictionaryRef context) 81 { 82 if (!context) { 83 return NULL; 84 } 85 86 CFStringRef tokenId = (CFStringRef)CFDictionaryGetValue(context, kSecAttrTokenID); 87 if (!tokenId || CFGetTypeID(tokenId) != CFStringGetTypeID()) { 88 os_log_debug(TL_LOG, "Invalid tokenId"); 89 return NULL; 90 } 91 return tokenId; 92 } 93 94 static CFDataRef getPubKeyHash(CFDictionaryRef context) 95 { 96 if (!context) { 97 return NULL; 98 } 99 100 CFDataRef pubKeyHash = (CFDataRef)CFDictionaryGetValue(context, kSecAttrPublicKeyHash); 101 if (!pubKeyHash || CFGetTypeID(pubKeyHash) != CFDataGetTypeID()) { 102 os_log_debug(TL_LOG, "Invalid pubkeyhash"); 103 return NULL; 104 } 105 return pubKeyHash; 106 } 107 108 static CFDataRef getPubKeyHashWrap(CFDictionaryRef context) 109 { 110 if (!context) { 111 return NULL; 112 } 113 114 CFDataRef pubKeyHashWrap = (CFDataRef)CFDictionaryGetValue(context, kSecAttrAccount); 115 if (!pubKeyHashWrap || CFGetTypeID(pubKeyHashWrap) != CFDataGetTypeID()) { 116 os_log_debug(TL_LOG, "Invalid pubkeyhashwrap"); 117 return NULL; 118 } 119 return pubKeyHashWrap; 120 } 121 122 static OSStatus privKeyForPubKeyHashWrap(CFDictionaryRef context, SecKeyRef *privKey, CFTypeRef *laCtx) 123 { 124 if (!context) { 125 os_log_error(TL_LOG, "private key for pubkeyhash wrong params"); 126 return errSecParam; 127 } 128 129 CFRef<CFMutableDictionaryRef> tokenAttributes = makeCFMutableDictionary(1, kSecAttrTokenID, getTokenId(context)); 130 CFRef<CFErrorRef> error; 131 132 CFStringRef pin = getPin(context); 133 if (pin) { 134 CFRef<CFTypeRef> LAContext = LACreateNewContextWithACMContext(NULL, error.take()); 135 if (!LAContext) { 136 os_log_error(TL_LOG, "Failed to LA Context: %{public}@", error.get()); 137 return errSecParam; 138 } 139 if (laCtx) 140 *laCtx = (CFTypeRef)CFRetain(LAContext); 141 CFRef<CFDataRef> externalizedContext = LACopyACMContext(LAContext, error.take()); 142 if (!externalizedContext) { 143 os_log_error(TL_LOG, "Failed to get externalized context: %{public}@", error.get()); 144 return errSecParam; 145 } 146 CFDictionarySetValue(tokenAttributes, kSecUseCredentialReference, externalizedContext.get()); 147 CFDictionarySetValue(tokenAttributes, CFSTR("PIN"), pin); 148 } 149 150 CFRef<TKTokenRef> token = TKTokenCreate(tokenAttributes, error.take()); 151 if (!token) { 152 os_log_error(TL_LOG, "Failed to create token: %{public}@", error.get()); 153 return errSecParam; 154 } 155 156 CFRef<CFArrayRef> identities = TKTokenCopyIdentities(token, TKTokenKeyUsageAny, error.take()); 157 if (!identities || !CFArrayGetCount(identities)) { 158 os_log_error(TL_LOG, "No identities found for token: %{public}@", error.get()); 159 return errSecParam; 160 } 161 162 CFDataRef desiredHash = getPubKeyHashWrap(context); 163 if (!desiredHash) { 164 os_log_error(TL_LOG, "No wrap key in context"); 165 return errSecParam; 166 } 167 168 CFIndex idx, count = CFArrayGetCount(identities); 169 for (idx = 0; idx < count; ++idx) { 170 SecIdentityRef identity = (SecIdentityRef)CFArrayGetValueAtIndex(identities, idx); 171 CFRef<SecCertificateRef> certificate; 172 OSStatus result = SecIdentityCopyCertificate(identity, certificate.take()); 173 if (result != errSecSuccess) { 174 os_log_error(TL_LOG, "Failed to get certificate for identity: %d", (int) result); 175 continue; 176 } 177 178 CFRef<CFDataRef> identityHash = SecCertificateCopyPublicKeySHA1Digest(certificate); 179 if (identityHash && CFEqual(desiredHash, identityHash)) { 180 result = SecIdentityCopyPrivateKey(identity, privKey); 181 if (result != errSecSuccess) { 182 os_log_error(TL_LOG, "Failed to get identity private key: %d", (int) result); 183 } 184 return result; 185 } 186 } 187 188 return errSecParam; 189 } 190 191 OSStatus TokenLoginGetContext(const void *base64TokenLoginData, UInt32 base64TokenLoginDataLength, CFDictionaryRef *context) 192 { 193 if (!base64TokenLoginData || !context) { 194 os_log_error(TL_LOG, "Get login context - wrong params"); 195 return errSecParam; 196 } 197 198 // Token data are base64 encoded in password. 199 size_t dataLen = SecBase64Decode((const char *)base64TokenLoginData, base64TokenLoginDataLength, NULL, 0); 200 if (!dataLen) { 201 os_log_debug(TL_LOG, "Invalid base64 encoded token data"); 202 return errSecParam; 203 } 204 205 CFRef<CFMutableDataRef> data = CFDataCreateMutable(kCFAllocatorDefault, dataLen); 206 dataLen = SecBase64Decode((const char *)base64TokenLoginData, base64TokenLoginDataLength, CFDataGetMutableBytePtr(data), dataLen); 207 if (!dataLen) { 208 os_log_error(TL_LOG, "Invalid base64 encoded token data"); 209 return errSecParam; 210 } 211 CFDataSetLength(data, dataLen); 212 213 // Content of the password consists of a serialized dictionary containing token ID, PIN, wrap key hash etc. 214 CFRef<CFErrorRef> error; 215 *context = (CFDictionaryRef)CFPropertyListCreateWithData(kCFAllocatorDefault, 216 data, 217 kCFPropertyListImmutable, 218 NULL, 219 error.take()); 220 if (!*context || CFGetTypeID(*context) != CFDictionaryGetTypeID()) { 221 os_log_error(TL_LOG, "Invalid token login data property list, %{public}@", error.get()); 222 return errSecParam; 223 } 224 225 if (!getPin(*context) || !getTokenId(*context) || !getPubKeyHash(*context) || !getPubKeyHashWrap(*context)) { 226 os_log_error(TL_LOG, "Invalid token login data context, %{public}@", error.get()); 227 return errSecParam; 228 } 229 230 return errSecSuccess; 231 } 232 233 OSStatus TokenLoginGetUnlockKey(CFDictionaryRef context, CFDataRef *unlockKey) 234 { 235 if (!context || !unlockKey) { 236 os_log_error(TL_LOG, "Get unlock key - wrong params"); 237 return errSecParam; 238 } 239 240 CFRef<CFDictionaryRef> loginData; 241 OSStatus result = TokenLoginGetLoginData(context, loginData.take()); 242 if (result != errSecSuccess) { 243 os_log_error(TL_LOG, "Failed to get login data: %d", (int)result); 244 return result; 245 } 246 247 CFDataRef wrappedUnlockKey = (CFDataRef)CFDictionaryGetValue(loginData, kSecValueData); 248 if (!wrappedUnlockKey) { 249 os_log_error(TL_LOG, "Wrapped unlock key not found in unlock key data"); 250 return errSecParam; 251 } 252 SecKeyAlgorithm algorithm = (SecKeyAlgorithm)CFDictionaryGetValue(loginData, kSecAttrService); 253 if (!algorithm) { 254 os_log_error(TL_LOG, "Algorithm not found in unlock key data"); 255 return errSecParam; 256 } 257 CFDataRef pubKeyHashWrapFromPlist = (CFDataRef)CFDictionaryGetValue(loginData, kSecAttrPublicKeyHash); 258 if (pubKeyHashWrapFromPlist == NULL) { 259 os_log_error(TL_LOG, "Failed to get wrapkey for unlock key data"); 260 return errSecInternal; 261 } 262 263 CFRef<CFDictionaryRef> ctx = makeCFDictionary(3, 264 kSecAttrTokenID, getTokenId(context), 265 kSecAttrService, getPin(context), 266 kSecAttrAccount, pubKeyHashWrapFromPlist 267 ); 268 269 CFRef<SecKeyRef> privKey; 270 CFRef<CFTypeRef> LAContext; 271 result = privKeyForPubKeyHashWrap(ctx, privKey.take(), LAContext.take()); 272 if (result != errSecSuccess) { 273 os_log_error(TL_LOG, "Failed to get private key for public key hash %{public}@: %d", pubKeyHashWrapFromPlist, (int)result); 274 return result; 275 } 276 277 CFRef<SecKeyRef> pubKey = SecKeyCopyPublicKey(privKey); 278 if (!pubKey) { 279 os_log_error(TL_LOG, "Failed to get public key from private key"); 280 return errSecParam; 281 } 282 CFRef<CFErrorRef> error; 283 *unlockKey = SecKeyCreateDecryptedData(privKey, 284 algorithm, 285 wrappedUnlockKey, 286 error.take()); 287 if (!*unlockKey) { 288 os_log_error(TL_LOG, "Failed to unwrap unlock key: %{public}@", error.get()); 289 return errSecDecode; 290 } 291 292 // we need to re-wrap already unwrapped data to avoid capturing and reusing communication with the smartcard 293 CFRef<CFDataRef> reWrappedUnlockKey = SecKeyCreateEncryptedData(pubKey, algorithm, *unlockKey, error.take()); 294 if (!reWrappedUnlockKey) { 295 os_log_error(TL_LOG, "Failed to rewrap unlock key: %{public}@", error.get()); 296 TokenLoginDeleteUnlockData(getPubKeyHash(context)); 297 return errSecParam; 298 } 299 300 CFRef<CFMutableDictionaryRef> newDict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 4, loginData); 301 if (newDict) { 302 CFDictionarySetValue(newDict, kSecValueData, reWrappedUnlockKey); 303 TokenLoginStoreUnlockData(context, newDict); 304 } 305 306 return errSecSuccess; 307 } 308 309 OSStatus TokenLoginGetLoginData(CFDictionaryRef context, CFDictionaryRef *loginData) 310 { 311 if (!loginData || !context) { 312 os_log_error(TL_LOG, "Get login data - wrong params"); 313 return errSecParam; 314 } 315 316 CFRef<CFStringRef> pubKeyHashHex = cfDataToHex(getPubKeyHash(context)); 317 318 CFPreferencesSynchronize(kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 319 CFRef<CFDataRef> storedData = (CFDataRef)CFPreferencesCopyValue(pubKeyHashHex, kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 320 if (!storedData) { 321 // this is not an error, might be a normal situation if the value does not exist 322 os_log_debug(TL_LOG, "Failed to read token login plist"); 323 return errSecIO; 324 } 325 326 CFRef<CFErrorRef> error; 327 *loginData = (CFDictionaryRef)CFPropertyListCreateWithData(kCFAllocatorDefault, 328 storedData, 329 kCFPropertyListImmutable, 330 NULL, 331 error.take()); 332 if (!*loginData || CFGetTypeID(*loginData) != CFDictionaryGetTypeID()) { 333 os_log_error(TL_LOG, "Failed to deserialize unlock key data: %{public}@", error.get()); 334 return errSecParam; 335 } 336 337 return errSecSuccess; 338 } 339 340 OSStatus TokenLoginGetPin(CFDictionaryRef context, CFStringRef *pin) 341 { 342 if (!pin || !context) { 343 return errSecParam; 344 } 345 *pin = getPin(context); 346 347 return errSecSuccess; 348 } 349 350 OSStatus TokenLoginUpdateUnlockData(CFDictionaryRef context, CFStringRef password) 351 { 352 if (!context) { 353 os_log_error(TL_LOG, "Updating unlock data - wrong params"); 354 return errSecParam; 355 } 356 357 CFRef<SecKeychainRef> loginKeychain; 358 OSStatus result = SecKeychainCopyLogin(loginKeychain.take()); 359 if (result != errSecSuccess) { 360 os_log_error(TL_LOG, "Failed to get user keychain: %d", (int) result); 361 return result; 362 } 363 364 return SecKeychainStoreUnlockKeyWithPubKeyHash(getPubKeyHash(context), getTokenId(context), getPubKeyHashWrap(context), loginKeychain, password); 365 } 366 367 OSStatus TokenLoginCreateLoginData(CFStringRef tokenId, CFDataRef pubKeyHash, CFDataRef pubKeyHashWrap, CFDataRef unlockKey, CFDataRef scBlob) 368 { 369 if (!tokenId || !pubKeyHash || !pubKeyHashWrap || !unlockKey || !scBlob) { 370 os_log_error(TL_LOG, "Create login data - wrong params"); 371 return errSecParam; 372 } 373 374 CFRef<CFDictionaryRef> ctx = makeCFDictionary(3, 375 kSecAttrTokenID, tokenId, 376 kSecAttrPublicKeyHash, pubKeyHash, 377 kSecAttrAccount, pubKeyHashWrap 378 ); 379 CFRef<SecKeyRef> privKey; 380 OSStatus result = privKeyForPubKeyHashWrap(ctx, privKey.take(), NULL); 381 if (result != errSecSuccess) { 382 os_log_error(TL_LOG, "Failed to get private key for public key hash %{public}@: %d", pubKeyHashWrap, (int)result); 383 return result; 384 } 385 386 CFRef<SecKeyRef> pubKey = SecKeyCopyPublicKey(privKey); 387 if (!pubKey) { 388 os_log_error(TL_LOG, "Failed to get public key from private key"); 389 return errSecParam; 390 } 391 392 SecKeyAlgorithm algorithms[] = { 393 kSecKeyAlgorithmECIESEncryptionStandardX963SHA512AESGCM, 394 kSecKeyAlgorithmECIESEncryptionStandardX963SHA384AESGCM, 395 kSecKeyAlgorithmECIESEncryptionStandardX963SHA256AESGCM, 396 kSecKeyAlgorithmECIESEncryptionStandardX963SHA224AESGCM, 397 kSecKeyAlgorithmECIESEncryptionStandardX963SHA1AESGCM, 398 kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM, 399 kSecKeyAlgorithmRSAEncryptionOAEPSHA384AESGCM, 400 kSecKeyAlgorithmRSAEncryptionOAEPSHA256AESGCM, 401 kSecKeyAlgorithmRSAEncryptionOAEPSHA224AESGCM, 402 kSecKeyAlgorithmRSAEncryptionOAEPSHA1AESGCM 403 }; 404 405 SecKeyAlgorithm algorithm = NULL; 406 for (size_t i = 0; i < sizeof(algorithms) / sizeof(*algorithms); i++) { 407 if (SecKeyIsAlgorithmSupported(pubKey, kSecKeyOperationTypeEncrypt, algorithms[i]) 408 && SecKeyIsAlgorithmSupported(privKey, kSecKeyOperationTypeDecrypt, algorithms[i])) { 409 algorithm = algorithms[i]; 410 break; 411 } 412 } 413 if (algorithm == NULL) { 414 os_log_error(TL_LOG, "Failed to find supported wrap algorithm"); 415 return errSecParam; 416 } 417 418 CFRef<CFErrorRef> error; 419 CFRef<CFDataRef> wrappedUnlockKey = SecKeyCreateEncryptedData(pubKey, algorithm, unlockKey, error.take()); 420 if (!wrappedUnlockKey) { 421 os_log_error(TL_LOG, "Failed to wrap unlock key: %{public}@", error.get()); 422 return errSecParam; 423 } 424 425 CFRef<CFDictionaryRef> loginData = makeCFDictionary(4, 426 kSecAttrService, algorithm, 427 kSecAttrPublicKeyHash, pubKeyHashWrap, 428 kSecValueData, wrappedUnlockKey.get(), 429 kSecClassKey, scBlob 430 ); 431 return TokenLoginStoreUnlockData(ctx, loginData); 432 } 433 434 OSStatus TokenLoginStoreUnlockData(CFDictionaryRef context, CFDictionaryRef loginData) 435 { 436 os_log_debug(TL_LOG, "Storing unlock data"); 437 438 CFRef<CFErrorRef> error; 439 CFRef<CFDataRef> data = CFPropertyListCreateData(kCFAllocatorDefault, 440 loginData, 441 kCFPropertyListBinaryFormat_v1_0, 442 0, 443 error.take()); 444 if (!data) { 445 os_log_error(TL_LOG, "Failed to create unlock data: %{public}@", error.get()); 446 return errSecInternal; 447 } 448 CFRef<CFStringRef> pubKeyHashHex = cfDataToHex(getPubKeyHash(context)); 449 os_log_debug(TL_LOG, "Pubkeyhash %@", pubKeyHashHex.get()); 450 451 CFPreferencesSetValue(pubKeyHashHex, data, kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 452 os_log_debug(TL_LOG, "Pubkeyhash %@", pubKeyHashHex.get()); 453 454 CFPreferencesSynchronize(kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 455 CFRef<CFDataRef> storedData = (CFDataRef)CFPreferencesCopyValue(pubKeyHashHex, kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 456 os_log_debug(TL_LOG, "Stored data %@", storedData.get()); 457 458 if (!storedData || !CFEqual(storedData, data)) { 459 os_log_error(TL_LOG, "Failed to write token login plist"); 460 return errSecIO; 461 } 462 os_log_debug(TL_LOG, "Original data %@. Everything is OK", data.get()); 463 464 return errSecSuccess; 465 } 466 467 OSStatus TokenLoginDeleteUnlockData(CFDataRef pubKeyHash) 468 { 469 CFRef<CFStringRef> pubKeyHashHex = cfDataToHex(pubKeyHash); 470 CFPreferencesSetValue(pubKeyHashHex, NULL, kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 471 CFPreferencesSynchronize(kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 472 CFRef<CFDataRef> storedData = (CFDataRef)CFPreferencesCopyValue(pubKeyHashHex, kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 473 474 if (storedData) { 475 os_log_error(TL_LOG, "Failed to remove unlock data"); 476 return errSecIO; 477 } 478 479 return errSecSuccess; 480 } 481 482 OSStatus TokenLoginGetScBlob(CFDataRef pubKeyHashWrap, CFStringRef tokenId, CFStringRef password, CFDataRef *scBlob) 483 { 484 if (scBlob == NULL || password == NULL || pubKeyHashWrap == NULL || tokenId == NULL) { 485 os_log_error(TL_LOG, "TokenLoginGetScBlob wrong params"); 486 return errSecParam; 487 } 488 489 CFRef<CFDictionaryRef> ctx = makeCFDictionary(2, 490 kSecAttrTokenID, tokenId, 491 kSecAttrAccount, pubKeyHashWrap 492 ); 493 494 CFRef<SecKeyRef> privKey; 495 OSStatus retval = privKeyForPubKeyHashWrap(ctx, privKey.take(), NULL); 496 if (retval != errSecSuccess) { 497 os_log_error(TL_LOG, "TokenLoginGetScBlob failed to get private key for public key hash %{public}@: %d", pubKeyHashWrap, (int)retval); 498 return retval; 499 } 500 501 CFRef<SecKeyRef> pubKey = SecKeyCopyPublicKey(privKey); 502 if (!pubKey) { 503 os_log_error(TL_LOG, "TokenLoginGetScBlob no pubkey"); 504 return errSecInternal; 505 } 506 507 CFRef<CFDictionaryRef> attributes = SecKeyCopyAttributes(pubKey); 508 if (!attributes) { 509 os_log_error(TL_LOG, "TokenLoginGetScBlob no attributes"); 510 return errSecInternal; 511 } 512 513 aks_smartcard_mode_t mode; 514 CFRef<CFStringRef> type = (CFStringRef)CFDictionaryGetValue(attributes, kSecAttrKeyType); 515 if (CFEqual(type, kSecAttrKeyTypeRSA)) 516 mode = AKS_SMARTCARD_MODE_RSA; 517 else if (CFEqual(type, kSecAttrKeyTypeEC)) 518 mode = AKS_SMARTCARD_MODE_ECDH; 519 else { 520 os_log_error(TL_LOG, "TokenLoginGetScBlob bad type"); 521 return errSecNotAvailable; 522 } 523 524 CFRef<CFDataRef> publicBytes = SecKeyCopyExternalRepresentation(pubKey, NULL); 525 if (!publicBytes) { 526 os_log_error(TL_LOG, "TokenLoginGetScBlob cannot get public bytes"); 527 return retval; 528 } 529 530 CFIndex maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(password), kCFStringEncodingUTF8) + 1; 531 char* buf = (char*)malloc(maxLength); 532 if (buf == NULL) { 533 os_log_error(TL_LOG, "TokenLoginGetScBlob no mem for buffer"); 534 return retval; 535 } 536 537 if (CFStringGetCString(password, buf, maxLength, kCFStringEncodingUTF8) == FALSE) { 538 os_log_error(TL_LOG, "TokenLoginGetScBlob no pwd cstr"); 539 free(buf); 540 return retval; 541 } 542 543 void *sc_blob = NULL; 544 size_t sc_len = 0; 545 aks_smartcard_unregister(session_keybag_handle); // just to be sure no previous registration exist 546 kern_return_t aks_retval = aks_smartcard_register(session_keybag_handle, (uint8_t *)buf, strlen(buf), mode, (uint8_t *)CFDataGetBytePtr(publicBytes), (size_t)CFDataGetLength(publicBytes), &sc_blob, &sc_len); 547 free(buf); 548 os_log_debug(TL_LOG, "TokenLoginGetScBlob register result %d", aks_retval); 549 550 if (sc_blob) { 551 *scBlob = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)sc_blob, (CFIndex)sc_len); 552 free(sc_blob); 553 } 554 return aks_retval; 555 } 556 557 // context = data wrapped in password variable, loginData = dictionary from stored plist 558 OSStatus TokenLoginUnlockKeybag(CFDictionaryRef context, CFDictionaryRef loginData) 559 { 560 if (!loginData || !context) { 561 return errSecParam; 562 } 563 564 CFDataRef scBlob = (CFDataRef)CFDictionaryGetValue(loginData, kSecClassKey); 565 if (scBlob == NULL) { 566 os_log_error(TL_LOG, "Failed to get scblob"); 567 return errSecInternal; 568 } 569 570 CFDataRef pubKeyHashWrapFromPlist = (CFDataRef)CFDictionaryGetValue(loginData, kSecAttrPublicKeyHash); 571 if (pubKeyHashWrapFromPlist == NULL) { 572 os_log_error(TL_LOG, "Failed to get wrapkey"); 573 return errSecInternal; 574 } 575 576 CFRef<CFDictionaryRef> ctx = makeCFDictionary(3, 577 kSecAttrTokenID, getTokenId(context), 578 kSecAttrService, getPin(context), 579 kSecAttrAccount, pubKeyHashWrapFromPlist 580 ); 581 582 CFRef<CFErrorRef> error; 583 CFRef<SecKeyRef> privKey; 584 CFRef<CFTypeRef> LAContext; 585 OSStatus retval = privKeyForPubKeyHashWrap(ctx, privKey.take(), LAContext.take()); 586 if (retval != errSecSuccess) { 587 os_log_error(TL_LOG, "Failed to get private key for public key hash %{public}@: %d", pubKeyHashWrapFromPlist, (int)retval); 588 return retval; 589 } 590 591 CFRef<SecKeyRef> pubKey = SecKeyCopyPublicKey(privKey); 592 if (!pubKey) { 593 os_log_error(TL_LOG, "Failed to get pubkey"); 594 return retval; 595 } 596 597 CFRef<CFDictionaryRef> attributes = SecKeyCopyAttributes(pubKey); 598 if (!attributes) { 599 os_log_error(TL_LOG, "TokenLoginUnlockKeybag no attributes"); 600 return errSecInternal; 601 } 602 603 aks_smartcard_mode_t mode; 604 CFStringRef type = (CFStringRef)CFDictionaryGetValue(attributes, kSecAttrKeyType); 605 if (CFEqual(type, kSecAttrKeyTypeRSA)) 606 mode = AKS_SMARTCARD_MODE_RSA; 607 else if (CFEqual(type, kSecAttrKeyTypeEC)) 608 mode = AKS_SMARTCARD_MODE_ECDH; 609 else { 610 os_log_error(TL_LOG, "TokenLoginUnlockKeybag bad type"); 611 return errSecNotAvailable; 612 } 613 614 void *scChallenge = NULL; 615 size_t scChallengeLen = 0; 616 int res = aks_smartcard_request_unlock(session_keybag_handle, (uint8_t *)CFDataGetBytePtr(scBlob), (size_t)CFDataGetLength(scBlob), &scChallenge, &scChallengeLen); 617 if (res != 0) { 618 os_log_error(TL_LOG, "TokenLoginUnlockKeybag cannot request unlock: %x", res); 619 return errSecInternal; 620 } 621 const void *scUsk = NULL; 622 size_t scUskLen = 0; 623 res = aks_smartcard_get_sc_usk(scChallenge, scChallengeLen, &scUsk, &scUskLen); 624 625 if (res != 0 || scUsk == NULL) { 626 free(scChallenge); 627 os_log_error(TL_LOG, "TokenLoginUnlockKeybag cannot get usk: %x", res); 628 return errSecInternal; 629 } 630 631 CFRef<CFTypeRef> wrappedUsk; 632 if (mode == AKS_SMARTCARD_MODE_ECDH) { 633 const void *ecPub = NULL; 634 size_t ecPubLen = 0; 635 res = aks_smartcard_get_ec_pub(scChallenge, scChallengeLen, &ecPub, &ecPubLen); 636 if (res != 0 || ecPub == NULL) { 637 free(scChallenge); 638 os_log_error(TL_LOG, "TokenLoginUnlockKeybag cannot get ecpub: %x", res); 639 return errSecInternal; 640 } 641 wrappedUsk = CFDataCreateMutable(kCFAllocatorDefault, ecPubLen + scUskLen); 642 if (!wrappedUsk) { 643 free(scChallenge); 644 os_log_error(TL_LOG, "TokenLoginUnlockKeybag no mem for ecpubusk"); 645 return errSecInternal; 646 } 647 CFDataAppendBytes((CFMutableDataRef)wrappedUsk.get(), (const UInt8 *)ecPub, (CFIndex)ecPubLen); 648 CFDataAppendBytes((CFMutableDataRef)wrappedUsk.get(), (const UInt8 *)scUsk, (CFIndex)scUskLen); 649 } else { 650 wrappedUsk = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)scUsk, (CFIndex)scUskLen); 651 } 652 free(scChallenge); 653 // decrypt Usk with SC 654 CFRef<CFDataRef> unwrappedUsk = SecKeyCreateDecryptedData(privKey, 655 mode == AKS_SMARTCARD_MODE_RSA ? kSecKeyAlgorithmRSAEncryptionOAEPSHA256 : kSecKeyAlgorithmECIESEncryptionAKSSmartCard, 656 (CFDataRef)wrappedUsk.get(), 657 error.take()); 658 if (!unwrappedUsk) { 659 os_log_error(TL_LOG, "TokenLoginUnlockKeybag failed to unwrap blob: %{public}@", error.get()); 660 return errSecInternal; 661 } 662 663 void *scNewBlob = NULL; 664 size_t scNewLen = 0; 665 res = aks_smartcard_unlock(session_keybag_handle, (uint8_t *)CFDataGetBytePtr(scBlob), (size_t)CFDataGetLength(scBlob), (uint8_t *)CFDataGetBytePtr(unwrappedUsk), (size_t)CFDataGetLength(unwrappedUsk), &scNewBlob, &scNewLen); 666 if (scNewBlob) { 667 CFRef<CFDataRef> newBlobData = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)scNewBlob, (CFIndex)scNewLen); 668 free(scNewBlob); 669 CFRef<CFMutableDictionaryRef> newDict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 4, loginData); 670 if (newDict) { 671 CFDictionarySetValue(newDict, kSecClassKey, newBlobData.get()); 672 TokenLoginStoreUnlockData(context, newDict); 673 } 674 } else { 675 os_log_error(TL_LOG, "TokenLoginUnlockKeybag no new scblob received: %d", res); 676 } 677 return res; 678 }