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