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