X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/ce3c8656732c924baf7e88df75eab50891bdc471..fa7225c82381bac4432a6edf16f53b5370238d85:/OSX/libsecurity_keychain/lib/TokenLogin.cpp diff --git a/OSX/libsecurity_keychain/lib/TokenLogin.cpp b/OSX/libsecurity_keychain/lib/TokenLogin.cpp new file mode 100644 index 00000000..03255cd2 --- /dev/null +++ b/OSX/libsecurity_keychain/lib/TokenLogin.cpp @@ -0,0 +1,614 @@ +/* + * Copyright (c) 2016 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include "TokenLogin.h" + +#include +#include +#include +#include "SecBase64P.h" +#include +#include +#include +#include +#include +#include + +extern "C" { +#include +#include +} + +#define kSecTokenLoginDomain CFSTR("com.apple.security.tokenlogin") + +static CFStringRef cfDataToHex(CFDataRef bin) +{ + size_t len = CFDataGetLength(bin) * 2; + CFMutableStringRef str = CFStringCreateMutable(NULL, len); + + static const char* digits[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"}; + + const uint8_t* data = CFDataGetBytePtr(bin); + for (size_t i = 0; i < CFDataGetLength(bin); i++) { + CFStringAppendCString(str, digits[data[i] >> 4], 1); + CFStringAppendCString(str, digits[data[i] & 0xf], 1); + } + return str; +} + +static CFStringRef getPin(CFDictionaryRef context) +{ + if (!context) { + return NULL; + } + + CFStringRef pin = (CFStringRef)CFDictionaryGetValue(context, kSecAttrService); + if (!pin || CFGetTypeID(pin) != CFStringGetTypeID()) { + return NULL; + } + return pin; +} + +static CFStringRef getTokenId(CFDictionaryRef context) +{ + if (!context) { + return NULL; + } + + CFStringRef tokenId = (CFStringRef)CFDictionaryGetValue(context, kSecAttrTokenID); + if (!tokenId || CFGetTypeID(tokenId) != CFStringGetTypeID()) { + secinfo("TokenLogin", "Invalid tokenId"); + return NULL; + } + return tokenId; +} + +static CFDataRef getPubKeyHash(CFDictionaryRef context) +{ + if (!context) { + return NULL; + } + + CFDataRef pubKeyHash = (CFDataRef)CFDictionaryGetValue(context, kSecAttrPublicKeyHash); + if (!pubKeyHash || CFGetTypeID(pubKeyHash) != CFDataGetTypeID()) { + secinfo("TokenLogin", "Invalid pubkeyhash"); + return NULL; + } + return pubKeyHash; +} + +static CFDataRef getPubKeyHashWrap(CFDictionaryRef context) +{ + if (!context) { + return NULL; + } + + CFDataRef pubKeyHashWrap = (CFDataRef)CFDictionaryGetValue(context, kSecAttrAccount); + if (!pubKeyHashWrap || CFGetTypeID(pubKeyHashWrap) != CFDataGetTypeID()) { + secinfo("TokenLogin", "Invalid pubkeyhashwrap"); + return NULL; + } + return pubKeyHashWrap; +} + +static OSStatus privKeyForPubKeyHash(CFDictionaryRef context, SecKeyRef *privKey, CFTypeRef *laCtx) +{ + if (!context) { + return errSecParam; + } + + CFRef tokenAttributes = makeCFMutableDictionary(1, kSecAttrTokenID, getTokenId(context)); + CFRef error; + + CFStringRef pin = getPin(context); + if (pin) { + CFRef LAParams = makeCFDictionary(1, CFSTR("useDaemon"), kCFBooleanFalse); + CFRef LAContext = LACreateNewContextWithACMContext(LAParams.as(), error.take()); + if (!LAContext) { + secinfo("TokenLogin", "Failed to LA Context: %@", error.get()); + return errSecParam; + } + if (laCtx) + *laCtx = (CFTypeRef)CFRetain(LAContext); + CFRef externalizedContext = LACopyACMContext(LAContext, error.take()); + if (!externalizedContext) { + secinfo("TokenLogin", "Failed to get externalized context: %@", error.get()); + return errSecParam; + } + CFDictionarySetValue(tokenAttributes, kSecUseCredentialReference, externalizedContext.get()); + CFDictionarySetValue(tokenAttributes, CFSTR("PIN"), pin); + } + + CFRef token = TKTokenCreate(tokenAttributes, error.take()); + if (!token) { + secinfo("TokenLogin", "Failed to create token: %@", error.get()); + return errSecParam; + } + + CFRef identities = TKTokenCopyIdentities(token, TKTokenKeyUsageAny, error.take()); + if (!identities || !CFArrayGetCount(identities)) { + secinfo("TokenLogin", "No identities found for token: %@", error.get()); + return errSecParam; + } + + CFDataRef desiredHash = getPubKeyHashWrap(context); + CFIndex idx, count = CFArrayGetCount(identities); + for (idx = 0; idx < count; ++idx) { + SecIdentityRef identity = (SecIdentityRef)CFArrayGetValueAtIndex(identities, idx); + CFRef certificate; + OSStatus result = SecIdentityCopyCertificate(identity, certificate.take()); + if (result != errSecSuccess) { + secinfo("TokenLogin", "Failed to get certificate for identity: %d", (int) result); + continue; + } + + CFRef identityHash = SecCertificateCopyPublicKeySHA1Digest(certificate); + if (identityHash && CFEqual(desiredHash, identityHash)) { + result = SecIdentityCopyPrivateKey(identity, privKey); + if (result != errSecSuccess) { + secinfo("TokenLogin", "Failed to get identity private key: %d", (int) result); + } + return result; + } + } + + return errSecParam; +} + +OSStatus TokenLoginGetContext(const void *base64TokenLoginData, UInt32 base64TokenLoginDataLength, CFDictionaryRef *context) +{ + if (!base64TokenLoginData || !context) { + return errSecParam; + } + + // Token data are base64 encoded in password. + size_t dataLen = SecBase64Decode((const char *)base64TokenLoginData, base64TokenLoginDataLength, NULL, 0); + if (!dataLen) { + secinfo("TokenLogin", "Invalid base64 encoded token data"); + return errSecParam; + } + + CFRef data = CFDataCreateMutable(kCFAllocatorDefault, dataLen); + dataLen = SecBase64Decode((const char *)base64TokenLoginData, base64TokenLoginDataLength, CFDataGetMutableBytePtr(data), dataLen); + if (!dataLen) { + secinfo("TokenLogin", "Invalid base64 encoded token data"); + return errSecParam; + } + CFDataSetLength(data, dataLen); + + // Content of the password consists of a serialized dictionary containing token ID, PIN, wrap key hash etc. + CFRef error; + *context = (CFDictionaryRef)CFPropertyListCreateWithData(kCFAllocatorDefault, + data, + kCFPropertyListImmutable, + NULL, + error.take()); + if (!*context || CFGetTypeID(*context) != CFDictionaryGetTypeID()) { + secinfo("TokenLogin", "Invalid token login data property list, %@", error.get()); + return errSecParam; + } + + if (!getPin(*context) || !getTokenId(*context) || !getPubKeyHash(*context) || !getPubKeyHashWrap(*context)) { + secinfo("TokenLogin", "Invalid token login data context, %@", error.get()); + return errSecParam; + } + + return errSecSuccess; +} + +OSStatus TokenLoginGetUnlockKey(CFDictionaryRef context, CFDataRef *unlockKey) +{ + if (!context || !unlockKey) { + return errSecParam; + } + + CFRef loginData; + OSStatus result = TokenLoginGetLoginData(context, loginData.take()); + if (result != errSecSuccess) { + secinfo("TokenLogin", "Failed to get login data: %d", (int)result); + return result; + } + + CFDataRef wrappedUnlockKey = (CFDataRef)CFDictionaryGetValue(loginData, kSecValueData); + if (!wrappedUnlockKey) { + secinfo("TokenLogin", "Wrapped unlock key not found in unlock key data"); + return errSecParam; + } + SecKeyAlgorithm algorithm = (SecKeyAlgorithm)CFDictionaryGetValue(loginData, kSecAttrService); + if (!algorithm) { + secinfo("TokenLogin", "Algorithm not found in unlock key data"); + return errSecParam; + } + + CFRef privKey; + CFRef LAContext; + result = privKeyForPubKeyHash(context, privKey.take(), LAContext.take()); + if (result != errSecSuccess) { + secinfo("TokenLogin", "Failed to get private key for public key hash: %d", (int)result); + return result; + } + + CFRef pubKey = SecKeyCopyPublicKey(privKey); + if (!pubKey) { + secinfo("TokenLogin", "Failed to get public key from private key"); + return errSecParam; + } + CFRef error; + *unlockKey = SecKeyCreateDecryptedData(privKey, + algorithm, + wrappedUnlockKey, + error.take()); + if (!*unlockKey) { + secinfo("TokenLogin", "Failed to unwrap unlock key: %@", error.get()); + return errSecDecode; + } + + // we need to re-wrap already unwrapped data to avoid capturing and reusing communication with the smartcard + CFRef reWrappedUnlockKey = SecKeyCreateEncryptedData(pubKey, algorithm, *unlockKey, error.take()); + if (!reWrappedUnlockKey) { + secinfo("TokenLogin", "Failed to rewrap unlock key: %@", error.get()); + TokenLoginDeleteUnlockData(getPubKeyHash(context)); + return errSecParam; + } + + CFRef newDict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 4, loginData); + if (newDict) { + CFDictionarySetValue(newDict, kSecValueData, reWrappedUnlockKey); + TokenLoginStoreUnlockData(context, newDict); + } + + return errSecSuccess; +} + +OSStatus TokenLoginGetLoginData(CFDictionaryRef context, CFDictionaryRef *loginData) +{ + if (!loginData || !context) { + return errSecParam; + } + + CFRef pubKeyHashHex = cfDataToHex(getPubKeyHash(context)); + CFPreferencesSynchronize(kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + CFRef storedData = (CFDataRef)CFPreferencesCopyValue(pubKeyHashHex, kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + if (!storedData) { + secinfo("TokenLogin", "Failed to read token login plist"); + return errSecIO; + } + + CFRef error; + *loginData = (CFDictionaryRef)CFPropertyListCreateWithData(kCFAllocatorDefault, + storedData, + kCFPropertyListImmutable, + NULL, + error.take()); + if (!*loginData || CFGetTypeID(*loginData) != CFDictionaryGetTypeID()) { + secinfo("TokenLogin", "Failed to deserialize unlock key data: %@", error.get()); + return errSecParam; + } + + return errSecSuccess; +} + +OSStatus TokenLoginUpdateUnlockData(CFDictionaryRef context) +{ + if (!context) { + return errSecParam; + } + + CFRef loginKeychain; + OSStatus result = SecKeychainCopyLogin(loginKeychain.take()); + if (result != errSecSuccess) { + secinfo("TokenLogin", "Failed to get user keychain: %d", (int) result); + return result; + } + + return SecKeychainStoreUnlockKeyWithPubKeyHash(getPubKeyHash(context), getTokenId(context), getPubKeyHashWrap(context), loginKeychain, NULL); +} + +OSStatus TokenLoginCreateLoginData(CFStringRef tokenId, CFDataRef pubKeyHash, CFDataRef pubKeyHashWrap, CFDataRef unlockKey, CFDataRef scBlob) +{ + if (!tokenId || !pubKeyHash || !pubKeyHashWrap || !unlockKey || !scBlob) + return errSecParam; + + CFRef ctx = makeCFDictionary(3, + kSecAttrTokenID, tokenId, + kSecAttrPublicKeyHash, pubKeyHash, + kSecAttrAccount, pubKeyHashWrap + ); + CFRef privKey; + OSStatus result = privKeyForPubKeyHash(ctx, privKey.take(), NULL); + if (result != errSecSuccess) { + secinfo("TokenLogin", "Failed to get private key for public key hash: %d", (int) result); + return result; + } + + CFRef pubKey = SecKeyCopyPublicKey(privKey); + if (!pubKey) { + secinfo("TokenLogin", "Failed to get public key from private key"); + return errSecParam; + } + + SecKeyAlgorithm algorithms[] = { + kSecKeyAlgorithmECIESEncryptionStandardX963SHA512AESGCM, + kSecKeyAlgorithmECIESEncryptionStandardX963SHA384AESGCM, + kSecKeyAlgorithmECIESEncryptionStandardX963SHA256AESGCM, + kSecKeyAlgorithmECIESEncryptionStandardX963SHA224AESGCM, + kSecKeyAlgorithmECIESEncryptionStandardX963SHA1AESGCM, + kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM, + kSecKeyAlgorithmRSAEncryptionOAEPSHA384AESGCM, + kSecKeyAlgorithmRSAEncryptionOAEPSHA256AESGCM, + kSecKeyAlgorithmRSAEncryptionOAEPSHA224AESGCM, + kSecKeyAlgorithmRSAEncryptionOAEPSHA1AESGCM + }; + + SecKeyAlgorithm algorithm = NULL; + for (size_t i = 0; i < sizeof(algorithms) / sizeof(*algorithms); i++) { + if (SecKeyIsAlgorithmSupported(pubKey, kSecKeyOperationTypeEncrypt, algorithms[i]) + && SecKeyIsAlgorithmSupported(privKey, kSecKeyOperationTypeDecrypt, algorithms[i])) { + algorithm = algorithms[i]; + break; + } + } + if (algorithm == NULL) { + secinfo("SecKeychain", "Failed to find supported wrap algorithm"); + return errSecParam; + } + + CFRef error; + CFRef wrappedUnlockKey = SecKeyCreateEncryptedData(pubKey, algorithm, unlockKey, error.take()); + if (!wrappedUnlockKey) { + secinfo("TokenLogin", "Failed to wrap unlock key: %@", error.get()); + return errSecParam; + } + + CFRef loginData = makeCFDictionary(4, + kSecAttrService, algorithm, + kSecAttrPublicKeyHash, pubKeyHashWrap, + kSecValueData, wrappedUnlockKey.get(), + kSecClassKey, scBlob + ); + return TokenLoginStoreUnlockData(ctx, loginData); +} + +OSStatus TokenLoginStoreUnlockData(CFDictionaryRef context, CFDictionaryRef loginData) +{ + + CFRef error; + CFRef data = CFPropertyListCreateData(kCFAllocatorDefault, + loginData, + kCFPropertyListBinaryFormat_v1_0, + 0, + error.take()); + if (!data) { + secdebug("TokenLogin", "Failed to create unlock data: %@", error.get()); + return errSecInternal; + } + CFRef pubKeyHashHex = cfDataToHex(getPubKeyHash(context)); + CFPreferencesSetValue(pubKeyHashHex, data, kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + CFPreferencesSynchronize(kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + CFRef storedData = (CFDataRef)CFPreferencesCopyValue(pubKeyHashHex, kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + + if (!storedData || !CFEqual(storedData, data)) { + secinfo("TokenLogin", "Failed to write token login plist"); + return errSecIO; + } + + return errSecSuccess; +} + +OSStatus TokenLoginDeleteUnlockData(CFDataRef pubKeyHash) +{ + CFRef pubKeyHashHex = cfDataToHex(pubKeyHash); + CFPreferencesSetValue(pubKeyHashHex, NULL, kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + CFPreferencesSynchronize(kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + CFRef storedData = (CFDataRef)CFPreferencesCopyValue(pubKeyHashHex, kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + + if (storedData) { + secinfo("TokenLogin", "Failed to remove unlock data"); + return errSecIO; + } + + return errSecSuccess; +} + +OSStatus TokenLoginGetScBlob(CFDataRef pubKeyHashWrap, CFStringRef tokenId, CFStringRef password, CFDataRef *scBlob) +{ + if (scBlob == NULL || password == NULL || pubKeyHashWrap == NULL || tokenId == NULL) { + secinfo("TokenLogin", "TokenLoginGetScBlob wrong params"); + return errSecParam; + } + + CFRef ctx = makeCFDictionary(2, + kSecAttrTokenID, tokenId, + kSecAttrAccount, pubKeyHashWrap + ); + + CFRef privKey; + OSStatus retval = privKeyForPubKeyHash(ctx, privKey.take(), NULL); + if (retval != errSecSuccess) { + secinfo("TokenLogin", "TokenLoginGetScBlob failed to get private key for public key hash: %d", (int) retval); + return retval; + } + + CFRef pubKey = SecKeyCopyPublicKey(privKey); + if (!pubKey) { + secinfo("TokenLogin", "TokenLoginGetScBlob no pubkey"); + return errSecInternal; + } + + CFRef attributes = SecKeyCopyAttributes(pubKey); + if (!attributes) { + secinfo("TokenLogin", "TokenLoginGetScBlob no attributes"); + return errSecInternal; + } + + aks_smartcard_mode_t mode; + CFRef type = (CFStringRef)CFDictionaryGetValue(attributes, kSecAttrKeyType); + if (CFEqual(type, kSecAttrKeyTypeRSA)) + mode = AKS_SMARTCARD_MODE_RSA; + else if (CFEqual(type, kSecAttrKeyTypeEC)) + mode = AKS_SMARTCARD_MODE_ECDH; + else { + secinfo("TokenLogin", "TokenLoginGetScBlob bad type"); + return errSecNotAvailable; + } + + CFRef publicBytes = SecKeyCopyExternalRepresentation(pubKey, NULL); + if (!publicBytes) { + secinfo("TokenLogin", "TokenLoginGetScBlob cannot get public bytes"); + return retval; + } + + CFIndex maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(password), kCFStringEncodingUTF8) + 1; + char* buf = (char*)malloc(maxLength); + if (buf == NULL) { + secinfo("TokenLogin", "TokenLoginGetScBlob no mem for buffer"); + return retval; + } + + if (CFStringGetCString(password, buf, maxLength, kCFStringEncodingUTF8) == FALSE) { + secinfo("TokenLogin", "TokenLoginGetScBlob no pwd cstr"); + free(buf); + return retval; + } + + void *sc_blob = NULL; + size_t sc_len = 0; + aks_smartcard_unregister(session_keybag_handle); // just to be sure no previous registration exist + 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); + free(buf); + secinfo("TokenLogin", "TokenLoginGetScBlob register result %d", aks_retval); + + if (sc_blob) { + *scBlob = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)sc_blob, (CFIndex)sc_len); + free(sc_blob); + } + return aks_retval; +} + +OSStatus TokenLoginUnlockKeybag(CFDictionaryRef context, CFDictionaryRef loginData) +{ + if (!loginData || !context) { + return errSecParam; + } + + CFDataRef scBlob = (CFDataRef)CFDictionaryGetValue(loginData, kSecClassKey); + if (scBlob == NULL) { + secinfo("TokenLogin", "Failed to get scblob"); + return errSecInternal; + } + + CFRef error; + CFRef privKey; + CFRef LAContext; + OSStatus retval = privKeyForPubKeyHash(context, privKey.take(), LAContext.take()); + if (retval != errSecSuccess) { + secinfo("TokenLogin", "Failed to get private key for public key hash: %d", (int) retval); + return retval; + } + + CFRef pubKey = SecKeyCopyPublicKey(privKey); + if (!pubKey) { + secinfo("TokenLogin", "Failed to get pubkey"); + return retval; + } + + CFRef attributes = SecKeyCopyAttributes(pubKey); + if (!attributes) { + secinfo("TokenLogin", "TokenLoginUnlockKeybag no attributes"); + return errSecInternal; + } + + aks_smartcard_mode_t mode; + CFStringRef type = (CFStringRef)CFDictionaryGetValue(attributes, kSecAttrKeyType); + if (CFEqual(type, kSecAttrKeyTypeRSA)) + mode = AKS_SMARTCARD_MODE_RSA; + else if (CFEqual(type, kSecAttrKeyTypeEC)) + mode = AKS_SMARTCARD_MODE_ECDH; + else { + secinfo("TokenLogin", "TokenLoginUnlockKeybag bad type"); + return errSecNotAvailable; + } + + void *scChallenge = NULL; + size_t scChallengeLen = 0; + int res = aks_smartcard_request_unlock(session_keybag_handle, (uint8_t *)CFDataGetBytePtr(scBlob), (size_t)CFDataGetLength(scBlob), &scChallenge, &scChallengeLen); + if (res != 0) { + secinfo("TokenLogin", "TokenLoginUnlockKeybag cannot request unlock: %x", res); + return errSecInternal; + } + const void *scUsk = NULL; + size_t scUskLen = 0; + res = aks_smartcard_get_sc_usk(scChallenge, scChallengeLen, &scUsk, &scUskLen); + + if (res != 0 || scUsk == NULL) { + free(scChallenge); + secinfo("TokenLogin", "TokenLoginUnlockKeybag cannot get usk: %x", res); + return errSecInternal; + } + + CFRef wrappedUsk; + if (mode == AKS_SMARTCARD_MODE_ECDH) { + const void *ecPub = NULL; + size_t ecPubLen = 0; + res = aks_smartcard_get_ec_pub(scChallenge, scChallengeLen, &ecPub, &ecPubLen); + if (res != 0 || ecPub == NULL) { + free(scChallenge); + secinfo("TokenLogin", "TokenLoginUnlockKeybag cannot get ecpub: %x", res); + return errSecInternal; + } + wrappedUsk = CFDataCreateMutable(kCFAllocatorDefault, ecPubLen + scUskLen); + if (!wrappedUsk) { + free(scChallenge); + secinfo("TokenLogin", "TokenLoginUnlockKeybag no mem for ecpubusk"); + return errSecInternal; + } + CFDataAppendBytes((CFMutableDataRef)wrappedUsk.get(), (const UInt8 *)ecPub, (CFIndex)ecPubLen); + CFDataAppendBytes((CFMutableDataRef)wrappedUsk.get(), (const UInt8 *)scUsk, (CFIndex)scUskLen); + } else { + wrappedUsk = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)scUsk, (CFIndex)scUskLen); + } + free(scChallenge); + // decrypt Usk with SC + CFRef unwrappedUsk = SecKeyCreateDecryptedData(privKey, + mode == AKS_SMARTCARD_MODE_RSA ? kSecKeyAlgorithmRSAEncryptionOAEPSHA256 : kSecKeyAlgorithmECIESEncryptionAKSSmartCard, + (CFDataRef)wrappedUsk.get(), + error.take()); + if (!unwrappedUsk) { + secinfo("TokenLogin", "TokenLoginUnlockKeybag failed to unwrap blob: %@", error.get()); + return errSecInternal; + } + + void *scNewBlob = NULL; + size_t scNewLen = 0; + 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); + if (scNewBlob) { + CFRef newBlobData = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)scNewBlob, (CFIndex)scNewLen); + free(scNewBlob); + CFRef newDict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 4, loginData); + if (newDict) { + CFDictionarySetValue(newDict, kSecClassKey, newBlobData.get()); + TokenLoginStoreUnlockData(context, newDict); + } + } + return res; +}