/ OSX / libsecurity_keychain / lib / TokenLogin.cpp
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  }