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