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