]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/TokenLogin.cpp
03255cd2fbecd81661a7bfd8e3b587458c97814c
[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 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 TokenLoginUpdateUnlockData(CFDictionaryRef context)
312 {
313 if (!context) {
314 return errSecParam;
315 }
316
317 CFRef<SecKeychainRef> loginKeychain;
318 OSStatus result = SecKeychainCopyLogin(loginKeychain.take());
319 if (result != errSecSuccess) {
320 secinfo("TokenLogin", "Failed to get user keychain: %d", (int) result);
321 return result;
322 }
323
324 return SecKeychainStoreUnlockKeyWithPubKeyHash(getPubKeyHash(context), getTokenId(context), getPubKeyHashWrap(context), loginKeychain, NULL);
325 }
326
327 OSStatus TokenLoginCreateLoginData(CFStringRef tokenId, CFDataRef pubKeyHash, CFDataRef pubKeyHashWrap, CFDataRef unlockKey, CFDataRef scBlob)
328 {
329 if (!tokenId || !pubKeyHash || !pubKeyHashWrap || !unlockKey || !scBlob)
330 return errSecParam;
331
332 CFRef<CFDictionaryRef> ctx = makeCFDictionary(3,
333 kSecAttrTokenID, tokenId,
334 kSecAttrPublicKeyHash, pubKeyHash,
335 kSecAttrAccount, pubKeyHashWrap
336 );
337 CFRef<SecKeyRef> privKey;
338 OSStatus result = privKeyForPubKeyHash(ctx, privKey.take(), NULL);
339 if (result != errSecSuccess) {
340 secinfo("TokenLogin", "Failed to get private key for public key hash: %d", (int) result);
341 return result;
342 }
343
344 CFRef<SecKeyRef> pubKey = SecKeyCopyPublicKey(privKey);
345 if (!pubKey) {
346 secinfo("TokenLogin", "Failed to get public key from private key");
347 return errSecParam;
348 }
349
350 SecKeyAlgorithm algorithms[] = {
351 kSecKeyAlgorithmECIESEncryptionStandardX963SHA512AESGCM,
352 kSecKeyAlgorithmECIESEncryptionStandardX963SHA384AESGCM,
353 kSecKeyAlgorithmECIESEncryptionStandardX963SHA256AESGCM,
354 kSecKeyAlgorithmECIESEncryptionStandardX963SHA224AESGCM,
355 kSecKeyAlgorithmECIESEncryptionStandardX963SHA1AESGCM,
356 kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM,
357 kSecKeyAlgorithmRSAEncryptionOAEPSHA384AESGCM,
358 kSecKeyAlgorithmRSAEncryptionOAEPSHA256AESGCM,
359 kSecKeyAlgorithmRSAEncryptionOAEPSHA224AESGCM,
360 kSecKeyAlgorithmRSAEncryptionOAEPSHA1AESGCM
361 };
362
363 SecKeyAlgorithm algorithm = NULL;
364 for (size_t i = 0; i < sizeof(algorithms) / sizeof(*algorithms); i++) {
365 if (SecKeyIsAlgorithmSupported(pubKey, kSecKeyOperationTypeEncrypt, algorithms[i])
366 && SecKeyIsAlgorithmSupported(privKey, kSecKeyOperationTypeDecrypt, algorithms[i])) {
367 algorithm = algorithms[i];
368 break;
369 }
370 }
371 if (algorithm == NULL) {
372 secinfo("SecKeychain", "Failed to find supported wrap algorithm");
373 return errSecParam;
374 }
375
376 CFRef<CFErrorRef> error;
377 CFRef<CFDataRef> wrappedUnlockKey = SecKeyCreateEncryptedData(pubKey, algorithm, unlockKey, error.take());
378 if (!wrappedUnlockKey) {
379 secinfo("TokenLogin", "Failed to wrap unlock key: %@", error.get());
380 return errSecParam;
381 }
382
383 CFRef<CFDictionaryRef> loginData = makeCFDictionary(4,
384 kSecAttrService, algorithm,
385 kSecAttrPublicKeyHash, pubKeyHashWrap,
386 kSecValueData, wrappedUnlockKey.get(),
387 kSecClassKey, scBlob
388 );
389 return TokenLoginStoreUnlockData(ctx, loginData);
390 }
391
392 OSStatus TokenLoginStoreUnlockData(CFDictionaryRef context, CFDictionaryRef loginData)
393 {
394
395 CFRef<CFErrorRef> error;
396 CFRef<CFDataRef> data = CFPropertyListCreateData(kCFAllocatorDefault,
397 loginData,
398 kCFPropertyListBinaryFormat_v1_0,
399 0,
400 error.take());
401 if (!data) {
402 secdebug("TokenLogin", "Failed to create unlock data: %@", error.get());
403 return errSecInternal;
404 }
405 CFRef<CFStringRef> pubKeyHashHex = cfDataToHex(getPubKeyHash(context));
406 CFPreferencesSetValue(pubKeyHashHex, data, kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
407 CFPreferencesSynchronize(kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
408 CFRef<CFDataRef> storedData = (CFDataRef)CFPreferencesCopyValue(pubKeyHashHex, kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
409
410 if (!storedData || !CFEqual(storedData, data)) {
411 secinfo("TokenLogin", "Failed to write token login plist");
412 return errSecIO;
413 }
414
415 return errSecSuccess;
416 }
417
418 OSStatus TokenLoginDeleteUnlockData(CFDataRef pubKeyHash)
419 {
420 CFRef<CFStringRef> pubKeyHashHex = cfDataToHex(pubKeyHash);
421 CFPreferencesSetValue(pubKeyHashHex, NULL, kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
422 CFPreferencesSynchronize(kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
423 CFRef<CFDataRef> storedData = (CFDataRef)CFPreferencesCopyValue(pubKeyHashHex, kSecTokenLoginDomain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
424
425 if (storedData) {
426 secinfo("TokenLogin", "Failed to remove unlock data");
427 return errSecIO;
428 }
429
430 return errSecSuccess;
431 }
432
433 OSStatus TokenLoginGetScBlob(CFDataRef pubKeyHashWrap, CFStringRef tokenId, CFStringRef password, CFDataRef *scBlob)
434 {
435 if (scBlob == NULL || password == NULL || pubKeyHashWrap == NULL || tokenId == NULL) {
436 secinfo("TokenLogin", "TokenLoginGetScBlob wrong params");
437 return errSecParam;
438 }
439
440 CFRef<CFDictionaryRef> ctx = makeCFDictionary(2,
441 kSecAttrTokenID, tokenId,
442 kSecAttrAccount, pubKeyHashWrap
443 );
444
445 CFRef<SecKeyRef> privKey;
446 OSStatus retval = privKeyForPubKeyHash(ctx, privKey.take(), NULL);
447 if (retval != errSecSuccess) {
448 secinfo("TokenLogin", "TokenLoginGetScBlob failed to get private key for public key hash: %d", (int) retval);
449 return retval;
450 }
451
452 CFRef<SecKeyRef> pubKey = SecKeyCopyPublicKey(privKey);
453 if (!pubKey) {
454 secinfo("TokenLogin", "TokenLoginGetScBlob no pubkey");
455 return errSecInternal;
456 }
457
458 CFRef<CFDictionaryRef> attributes = SecKeyCopyAttributes(pubKey);
459 if (!attributes) {
460 secinfo("TokenLogin", "TokenLoginGetScBlob no attributes");
461 return errSecInternal;
462 }
463
464 aks_smartcard_mode_t mode;
465 CFRef<CFStringRef> type = (CFStringRef)CFDictionaryGetValue(attributes, kSecAttrKeyType);
466 if (CFEqual(type, kSecAttrKeyTypeRSA))
467 mode = AKS_SMARTCARD_MODE_RSA;
468 else if (CFEqual(type, kSecAttrKeyTypeEC))
469 mode = AKS_SMARTCARD_MODE_ECDH;
470 else {
471 secinfo("TokenLogin", "TokenLoginGetScBlob bad type");
472 return errSecNotAvailable;
473 }
474
475 CFRef<CFDataRef> publicBytes = SecKeyCopyExternalRepresentation(pubKey, NULL);
476 if (!publicBytes) {
477 secinfo("TokenLogin", "TokenLoginGetScBlob cannot get public bytes");
478 return retval;
479 }
480
481 CFIndex maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(password), kCFStringEncodingUTF8) + 1;
482 char* buf = (char*)malloc(maxLength);
483 if (buf == NULL) {
484 secinfo("TokenLogin", "TokenLoginGetScBlob no mem for buffer");
485 return retval;
486 }
487
488 if (CFStringGetCString(password, buf, maxLength, kCFStringEncodingUTF8) == FALSE) {
489 secinfo("TokenLogin", "TokenLoginGetScBlob no pwd cstr");
490 free(buf);
491 return retval;
492 }
493
494 void *sc_blob = NULL;
495 size_t sc_len = 0;
496 aks_smartcard_unregister(session_keybag_handle); // just to be sure no previous registration exist
497 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);
498 free(buf);
499 secinfo("TokenLogin", "TokenLoginGetScBlob register result %d", aks_retval);
500
501 if (sc_blob) {
502 *scBlob = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)sc_blob, (CFIndex)sc_len);
503 free(sc_blob);
504 }
505 return aks_retval;
506 }
507
508 OSStatus TokenLoginUnlockKeybag(CFDictionaryRef context, CFDictionaryRef loginData)
509 {
510 if (!loginData || !context) {
511 return errSecParam;
512 }
513
514 CFDataRef scBlob = (CFDataRef)CFDictionaryGetValue(loginData, kSecClassKey);
515 if (scBlob == NULL) {
516 secinfo("TokenLogin", "Failed to get scblob");
517 return errSecInternal;
518 }
519
520 CFRef<CFErrorRef> error;
521 CFRef<SecKeyRef> privKey;
522 CFRef<CFTypeRef> LAContext;
523 OSStatus retval = privKeyForPubKeyHash(context, privKey.take(), LAContext.take());
524 if (retval != errSecSuccess) {
525 secinfo("TokenLogin", "Failed to get private key for public key hash: %d", (int) retval);
526 return retval;
527 }
528
529 CFRef<SecKeyRef> pubKey = SecKeyCopyPublicKey(privKey);
530 if (!pubKey) {
531 secinfo("TokenLogin", "Failed to get pubkey");
532 return retval;
533 }
534
535 CFRef<CFDictionaryRef> attributes = SecKeyCopyAttributes(pubKey);
536 if (!attributes) {
537 secinfo("TokenLogin", "TokenLoginUnlockKeybag no attributes");
538 return errSecInternal;
539 }
540
541 aks_smartcard_mode_t mode;
542 CFStringRef type = (CFStringRef)CFDictionaryGetValue(attributes, kSecAttrKeyType);
543 if (CFEqual(type, kSecAttrKeyTypeRSA))
544 mode = AKS_SMARTCARD_MODE_RSA;
545 else if (CFEqual(type, kSecAttrKeyTypeEC))
546 mode = AKS_SMARTCARD_MODE_ECDH;
547 else {
548 secinfo("TokenLogin", "TokenLoginUnlockKeybag bad type");
549 return errSecNotAvailable;
550 }
551
552 void *scChallenge = NULL;
553 size_t scChallengeLen = 0;
554 int res = aks_smartcard_request_unlock(session_keybag_handle, (uint8_t *)CFDataGetBytePtr(scBlob), (size_t)CFDataGetLength(scBlob), &scChallenge, &scChallengeLen);
555 if (res != 0) {
556 secinfo("TokenLogin", "TokenLoginUnlockKeybag cannot request unlock: %x", res);
557 return errSecInternal;
558 }
559 const void *scUsk = NULL;
560 size_t scUskLen = 0;
561 res = aks_smartcard_get_sc_usk(scChallenge, scChallengeLen, &scUsk, &scUskLen);
562
563 if (res != 0 || scUsk == NULL) {
564 free(scChallenge);
565 secinfo("TokenLogin", "TokenLoginUnlockKeybag cannot get usk: %x", res);
566 return errSecInternal;
567 }
568
569 CFRef<CFTypeRef> wrappedUsk;
570 if (mode == AKS_SMARTCARD_MODE_ECDH) {
571 const void *ecPub = NULL;
572 size_t ecPubLen = 0;
573 res = aks_smartcard_get_ec_pub(scChallenge, scChallengeLen, &ecPub, &ecPubLen);
574 if (res != 0 || ecPub == NULL) {
575 free(scChallenge);
576 secinfo("TokenLogin", "TokenLoginUnlockKeybag cannot get ecpub: %x", res);
577 return errSecInternal;
578 }
579 wrappedUsk = CFDataCreateMutable(kCFAllocatorDefault, ecPubLen + scUskLen);
580 if (!wrappedUsk) {
581 free(scChallenge);
582 secinfo("TokenLogin", "TokenLoginUnlockKeybag no mem for ecpubusk");
583 return errSecInternal;
584 }
585 CFDataAppendBytes((CFMutableDataRef)wrappedUsk.get(), (const UInt8 *)ecPub, (CFIndex)ecPubLen);
586 CFDataAppendBytes((CFMutableDataRef)wrappedUsk.get(), (const UInt8 *)scUsk, (CFIndex)scUskLen);
587 } else {
588 wrappedUsk = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)scUsk, (CFIndex)scUskLen);
589 }
590 free(scChallenge);
591 // decrypt Usk with SC
592 CFRef<CFDataRef> unwrappedUsk = SecKeyCreateDecryptedData(privKey,
593 mode == AKS_SMARTCARD_MODE_RSA ? kSecKeyAlgorithmRSAEncryptionOAEPSHA256 : kSecKeyAlgorithmECIESEncryptionAKSSmartCard,
594 (CFDataRef)wrappedUsk.get(),
595 error.take());
596 if (!unwrappedUsk) {
597 secinfo("TokenLogin", "TokenLoginUnlockKeybag failed to unwrap blob: %@", error.get());
598 return errSecInternal;
599 }
600
601 void *scNewBlob = NULL;
602 size_t scNewLen = 0;
603 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);
604 if (scNewBlob) {
605 CFRef<CFDataRef> newBlobData = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)scNewBlob, (CFIndex)scNewLen);
606 free(scNewBlob);
607 CFRef<CFMutableDictionaryRef> newDict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 4, loginData);
608 if (newDict) {
609 CFDictionarySetValue(newDict, kSecClassKey, newBlobData.get());
610 TokenLoginStoreUnlockData(context, newDict);
611 }
612 }
613 return res;
614 }