Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(776)

Side by Side Diff: net/third_party/nss/ssl/sslplatf.c

Issue 2828002: Support for using OS-native certificates for SSL client auth.... (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: Add a short-circuit when the CSP reports the container is not removable Created 10 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « net/third_party/nss/ssl/sslnonce.c ('k') | net/third_party/nss/ssl/sslsnce.c » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Platform specific crypto wrappers
3 *
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is the Netscape security libraries.
18 *
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1994-2000
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 * Ryan Sleevi <ryan.sleevi@gmail.com>
26 *
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
38 *
39 * ***** END LICENSE BLOCK ***** */
40 /* $Id$ */
41 #include "ssl.h"
42 #include "certt.h"
43 #include "keythi.h"
44 #include "sslimpl.h"
45 #include "cryptohi.h"
46 #include "secitem.h"
47
48 #ifdef NSS_PLATFORM_CLIENT_AUTH
49 CERTCertificateList*
50 hack_NewCertificateListFromCertList(CERTCertList* list)
51 {
52 CERTCertificateList * chain = NULL;
53 PRArenaPool * arena = NULL;
54 CERTCertListNode * node;
55 int len;
56
57 if (CERT_LIST_EMPTY(list))
58 goto loser;
59
60 arena = PORT_NewArena(4096);
61 if (arena == NULL)
62 goto loser;
63
64 for (len = 0, node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list);
65 len++, node = CERT_LIST_NEXT(node)) {
66 }
67
68 chain = PORT_ArenaNew(arena, CERTCertificateList);
69 if (chain == NULL)
70 goto loser;
71
72 chain->certs = PORT_ArenaNewArray(arena, SECItem, len);
73 if (!chain->certs)
74 goto loser;
75 chain->len = len;
76
77 for (len = 0, node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list);
78 len++, node = CERT_LIST_NEXT(node)) {
79 // Check to see if the last cert to be sent is a self-signed cert,
80 // and if so, omit it from the list of certificates. However, if
81 // there is only one cert (len == 0), include the cert, as it means
82 // the EE cert is self-signed.
83 if (len > 0 && (len == chain->len - 1) && node->cert->isRoot) {
84 chain->len = len;
85 break;
86 }
87 SECITEM_CopyItem(arena, &chain->certs[len], &node->cert->derCert);
88 }
89
90 chain->arena = arena;
91 return chain;
92
93 loser:
94 if (arena) {
95 PORT_FreeArena(arena, PR_FALSE);
96 }
97 return NULL;
98 }
99
100 #if defined(XP_WIN32)
101 void
102 ssl_FreePlatformKey(PlatformKey key)
103 {
104 CryptReleaseContext(key, 0);
105 }
106
107 void
108 ssl_FreePlatformAuthInfo(PlatformAuthInfo* info)
109 {
110 if (info->provider != NULL) {
111 PORT_Free(info->provider);
112 info->provider = NULL;
113 }
114 if (info->container != NULL) {
115 PORT_Free(info->container);
116 info->container = NULL;
117 }
118 info->provType = 0;
119 info->removable = PR_FALSE;
120 }
121
122 void
123 ssl_InitPlatformAuthInfo(PlatformAuthInfo* info)
124 {
125 info->provider = NULL;
126 info->container = NULL;
127 info->provType = 0;
128 info->removable = PR_FALSE;
129 }
130
131 PRBool
132 ssl_PlatformAuthTokenPresent(PlatformAuthInfo *info)
133 {
134 HCRYPTPROV prov = 0;
135
136 if (!info || !info->provider || !info->container)
137 return PR_FALSE;
138
139 if (info->removable == PR_FALSE)
140 return PR_TRUE;
141
142 if (!CryptAcquireContextA(&prov, info->container, info->provider,
143 info->provType, CRYPT_SILENT))
144 return PR_FALSE;
145
146 CryptReleaseContext(prov, 0);
147 return PR_TRUE;
148 }
149
150 void
151 ssl_GetPlatformAuthInfoForKey(PlatformKey key,
152 PlatformAuthInfo *info)
153 {
154 DWORD bytesNeeded = 0;
155 DWORD implementationType = 0;
156 ssl_InitPlatformAuthInfo(info);
157 bytesNeeded = sizeof(info->provType);
158 if (!CryptGetProvParam(key, PP_PROVTYPE, (BYTE*)&info->provType,
159 &bytesNeeded, 0))
160 goto error;
161
162 bytesNeeded = 0;
163 if (!CryptGetProvParam(key, PP_CONTAINER, NULL, &bytesNeeded, 0))
164 goto error;
165 info->container = (char*)PORT_Alloc(bytesNeeded);
166 if (info->container == NULL)
167 goto error;
168 if (!CryptGetProvParam(key, PP_CONTAINER, (BYTE*)info->container,
169 &bytesNeeded, 0))
170 goto error;
171
172 bytesNeeded = 0;
173 if (!CryptGetProvParam(key, PP_NAME, NULL, &bytesNeeded, 0))
174 goto error;
175 info->provider = (char*)PORT_Alloc(bytesNeeded);
176 if (info->provider == NULL)
177 goto error;
178 if (!CryptGetProvParam(key, PP_NAME, (BYTE*)info->provider,
179 &bytesNeeded, 0))
180 goto error;
181
182 bytesNeeded = sizeof(implementationType);
183 if (!CryptGetProvParam(key, PP_IMPTYPE, (BYTE*)&implementationType,
184 &bytesNeeded, 0))
185 goto error;
186 if (implementationType & (CRYPT_IMPL_REMOVABLE | CRYPT_IMPL_UNKNOWN))
187 info->removable = PR_TRUE;
188
189 goto done;
190 error:
191 ssl_FreePlatformAuthInfo(info);
192
193 done:
194 return;
195 }
196
197 SECStatus
198 ssl3_PlatformSignHashes(SSL3Hashes *hash, PlatformKey key, SECItem *buf,
199 PRBool isTLS)
200 {
201 SECStatus rv = SECFailure;
202 PRBool doDerEncode = PR_FALSE;
203 SECItem hashItem;
204 /* TODO(rsleevi): Should AT_SIGNATURE also be checked if doing client
205 * auth?
206 */
207 DWORD keySpec = AT_KEYEXCHANGE;
208 HCRYPTKEY hKey = 0;
209 DWORD argLen = 0;
210 ALG_ID keyAlg = 0;
211 DWORD signatureLen = 0;
212 ALG_ID hashAlg = 0;
213 HCRYPTHASH hHash = 0;
214 DWORD hashLen = 0;
215 unsigned int i = 0;
216
217 buf->data = NULL;
218 if (!CryptGetUserKey(key, keySpec, &hKey)) {
219 PORT_SetError(SEC_ERROR_INVALID_KEY);
220 goto done;
221 }
222
223 argLen = sizeof(keyAlg);
224 if (!CryptGetKeyParam(hKey, KP_ALGID, (BYTE*)&keyAlg, &argLen, 0)) {
225 PORT_SetError(SEC_ERROR_INVALID_KEY);
226 goto done;
227 }
228
229 switch (keyAlg) {
230 case CALG_RSA_KEYX:
231 case CALG_RSA_SIGN:
232 hashAlg = CALG_SSL3_SHAMD5;
233 hashItem.data = hash->md5;
234 hashItem.len = sizeof(SSL3Hashes);
235 break;
236 case CALG_DSS_SIGN:
237 /* TODO: Support CALG_ECDSA once tested */
238 case CALG_ECDSA:
239 if (keyAlg == CALG_ECDSA) {
240 doDerEncode = PR_TRUE;
241 } else {
242 doDerEncode = isTLS;
243 }
244 hashAlg = CALG_SHA1;
245 hashItem.data = hash->sha;
246 hashItem.len = sizeof(hash->sha);
247 break;
248 default:
249 PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG);
250 goto done;
251 }
252 PRINT_BUF(60, (NULL, "hash(es) to be signed", hashItem.data, hashItem.len));
253
254 if (!CryptCreateHash(key, hashAlg, 0, 0, &hHash)) {
255 ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE);
256 goto done;
257 }
258 argLen = sizeof(hashLen);
259 if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)&hashLen, &argLen, 0)) {
260 ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE);
261 goto done;
262 }
263 if (hashLen != hashItem.len) {
264 ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE);
265 goto done;
266 }
267 if (!CryptSetHashParam(hHash, HP_HASHVAL, (BYTE*)hashItem.data, 0)) {
268 ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE);
269 goto done;
270 }
271 if (!CryptSignHash(hHash, keySpec, NULL, CRYPT_NOHASHOID,
272 NULL, &signatureLen) || signatureLen == 0) {
273 ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE);
274 goto done;
275 }
276 buf->len = signatureLen;
277 buf->data = (unsigned char *)PORT_Alloc(signatureLen);
278 if (!buf->data)
279 goto done; /* error code was set. */
280
281 if (!CryptSignHash(hHash, keySpec, NULL, CRYPT_NOHASHOID,
282 (BYTE*)buf->data, &signatureLen)) {
283 ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE);
284 goto done;
285 }
286
287 /* CryptoAPI signs in little-endian, so reverse */
288 for (i = 0; i < buf->len / 2; ++i) {
289 unsigned char tmp = buf->data[i];
290 buf->data[i] = buf->data[buf->len - 1 - i];
291 buf->data[buf->len - 1 - i] = tmp;
292 }
293 if (doDerEncode) {
294 SECItem derSig = {siBuffer, NULL, 0};
295
296 /* This also works for an ECDSA signature */
297 rv = DSAU_EncodeDerSigWithLen(&derSig, buf, buf->len);
298 if (rv == SECSuccess) {
299 PORT_Free(buf->data); /* discard unencoded signature. */
300 *buf = derSig; /* give caller encoded signature. */
301 } else if (derSig.data) {
302 PORT_Free(derSig.data);
303 }
304 }
305
306 PRINT_BUF(60, (NULL, "signed hashes", buf->data, buf->len));
307 rv = SECSuccess;
308 done:
309 if (hHash)
310 CryptDestroyHash(hHash);
311 if (hKey)
312 CryptDestroyKey(hKey);
313 if (rv != SECSuccess && buf->data) {
314 PORT_Free(buf->data);
315 buf->data = NULL;
316 }
317 return rv;
318 }
319
320 #elif defined(XP_MACOSX)
321 #include <Security/cssm.h>
322
323 /*
324 * In Mac OS X 10.5, these two functions are private but implemented, and
325 * in Mac OS X 10.6, these are exposed publicly. To compile with the 10.5
326 * SDK, we declare them here.
327 */
328 OSStatus SecKeychainItemCreatePersistentReference(SecKeychainItemRef itemRef, CF DataRef *persistentItemRef);
329 OSStatus SecKeychainItemCopyFromPersistentReference(CFDataRef persistentItemRef, SecKeychainItemRef *itemRef);
330
331 void
332 ssl_FreePlatformKey(PlatformKey key)
333 {
334 CFRelease(key);
335 }
336
337 void
338 ssl_FreePlatformAuthInfo(PlatformAuthInfo* info)
339 {
340 if (info->keychain != NULL) {
341 CFRelease(info->keychain);
342 info->keychain = NULL;
343 }
344 if (info->persistentKey != NULL) {
345 CFRelease(info->persistentKey);
346 info->persistentKey = NULL;
347 }
348 }
349
350 void
351 ssl_InitPlatformAuthInfo(PlatformAuthInfo* info)
352 {
353 info->keychain = NULL;
354 info->persistentKey = NULL;
355 }
356
357 PRBool
358 ssl_PlatformAuthTokenPresent(PlatformAuthInfo* info)
359 {
360 if (!info || !info->keychain || !info->persistentKey)
361 return PR_FALSE;
362
363 // Not actually interested in the status, but it can be used to make sure
364 // that the keychain still exists (as smart card ejection will remove
365 // the keychain)
366 SecKeychainStatus keychainStatus;
367 OSStatus rv = SecKeychainGetStatus(info->keychain, &keychainStatus);
368 if (rv != noErr)
369 return PR_FALSE;
370
371 // Make sure the individual key still exists within the keychain, if
372 // the keychain is present
373 SecKeychainItemRef keychainItem;
374 rv = SecKeychainItemCopyFromPersistentReference(info->persistentKey,
375 &keychainItem);
376 if (rv != noErr)
377 return PR_FALSE;
378
379 CFRelease(keychainItem);
380 return PR_TRUE;
381 }
382
383 void
384 ssl_GetPlatformAuthInfoForKey(PlatformKey key,
385 PlatformAuthInfo *info)
386 {
387 SecKeychainItemRef keychainItem = (SecKeychainItemRef)key;
388 OSStatus rv = SecKeychainItemCopyKeychain(keychainItem, &info->keychain);
389 if (rv == noErr) {
390 rv = SecKeychainItemCreatePersistentReference(keychainItem,
391 &info->persistentKey);
392 }
393 if (rv != noErr) {
394 ssl_FreePlatformAuthInfo(info);
395 }
396 return;
397 }
398
399 SECStatus
400 ssl3_PlatformSignHashes(SSL3Hashes *hash, PlatformKey key, SECItem *buf,
401 PRBool isTLS)
402 {
403 SECStatus rv = SECFailure;
404 PRBool doDerEncode = PR_FALSE;
405 unsigned int signatureLen;
406 OSStatus status = noErr;
407 CSSM_CSP_HANDLE cspHandle = 0;
408 const CSSM_KEY *cssmKey = NULL;
409 CSSM_ALGORITHMS sigAlg;
410 const CSSM_ACCESS_CREDENTIALS * cssmCreds = NULL;
411 CSSM_RETURN cssmRv;
412 CSSM_DATA hashData;
413 CSSM_DATA signatureData;
414 CSSM_CC_HANDLE cssmSignature = 0;
415
416 buf->data = NULL;
417
418 status = SecKeyGetCSPHandle(key, &cspHandle);
419 if (status != noErr) {
420 PORT_SetError(SEC_ERROR_INVALID_KEY);
421 goto done;
422 }
423
424 status = SecKeyGetCSSMKey(key, &cssmKey);
425 if (status != noErr || !cssmKey) {
426 PORT_SetError(SEC_ERROR_INVALID_KEY);
427 goto done;
428 }
429
430 /* SecKeyGetBlockSize wasn't addeded until OS X 10.6 - but the
431 * needed information is readily available on the key itself.
432 */
433 signatureLen = (cssmKey->KeyHeader.LogicalKeySizeInBits + 7) / 8;
434
435 if (signatureLen == 0) {
436 PORT_SetError(SEC_ERROR_INVALID_KEY);
437 goto done;
438 }
439
440 buf->len = signatureLen;
441 buf->data = (unsigned char *)PORT_Alloc(signatureLen);
442 if (!buf->data)
443 goto done; /* error code was set. */
444
445 sigAlg = cssmKey->KeyHeader.AlgorithmId;
446 switch (sigAlg) {
447 case CSSM_ALGID_RSA:
448 hashData.Data = hash->md5;
449 hashData.Length = sizeof(SSL3Hashes);
450 break;
451 case CSSM_ALGID_ECDSA:
452 case CSSM_ALGID_DSA:
453 if (sigAlg == CSSM_ALGID_ECDSA) {
454 doDerEncode = PR_TRUE;
455 } else {
456 doDerEncode = isTLS;
457 }
458 hashData.Data = hash->sha;
459 hashData.Length = sizeof(hash->sha);
460 break;
461 default:
462 PORT_SetError(SEC_ERROR_INVALID_KEY);
463 goto done;
464 }
465 PRINT_BUF(60, (NULL, "hash(es) to be signed", hashData.Data, hashData.Length ));
466
467 /* TODO(rsleevi): Should it be kSecCredentialTypeNoUI? In Win32, at least,
468 * you can prevent the UI by setting the provider handle on the
469 * certificate to be opened with CRYPT_SILENT, but is there an equivalent?
470 */
471 status = SecKeyGetCredentials(key, CSSM_ACL_AUTHORIZATION_SIGN,
472 kSecCredentialTypeDefault, &cssmCreds);
473 if (status != noErr) {
474 ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE);
475 goto done;
476 }
477
478 signatureData.Length = buf->len;
479 signatureData.Data = (uint8*)buf->data;
480
481 cssmRv = CSSM_CSP_CreateSignatureContext(cspHandle, sigAlg, cssmCreds,
482 cssmKey, &cssmSignature);
483 if (cssmRv) {
484 ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE);
485 goto done;
486 }
487
488 /* See "Apple Cryptographic Service Provider Functional Specification" */
489 if (cssmKey->KeyHeader.AlgorithmId == CSSM_ALGID_RSA) {
490 /* To set RSA blinding for RSA keys */
491 CSSM_CONTEXT_ATTRIBUTE blindingAttr;
492 blindingAttr.AttributeType = CSSM_ATTRIBUTE_RSA_BLINDING;
493 blindingAttr.AttributeLength = sizeof(uint32);
494 blindingAttr.Attribute.Uint32 = 1;
495 cssmRv = CSSM_UpdateContextAttributes(cssmSignature, 1, &blindingAttr);
496 if (cssmRv) {
497 ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE);
498 goto done;
499 }
500 }
501
502 cssmRv = CSSM_SignData(cssmSignature, &hashData, 1, CSSM_ALGID_NONE,
503 &signatureData);
504 if (cssmRv) {
505 ssl_MapLowLevelError(SSL_ERROR_SIGN_HASHES_FAILURE);
506 goto done;
507 }
508
509 if (doDerEncode) {
510 SECItem derSig = {siBuffer, NULL, 0};
511
512 /* This also works for an ECDSA signature */
513 rv = DSAU_EncodeDerSigWithLen(&derSig, buf, buf->len);
514 if (rv == SECSuccess) {
515 PORT_Free(buf->data); /* discard unencoded signature. */
516 *buf = derSig; /* give caller encoded signature. */
517 } else if (derSig.data) {
518 PORT_Free(derSig.data);
519 }
520 }
521
522 PRINT_BUF(60, (NULL, "signed hashes", buf->data, buf->len));
523 rv = SECSuccess;
524 done:
525 if (cssmSignature)
526 CSSM_DeleteContext(cssmSignature);
527
528 if (rv != SECSuccess && buf->data) {
529 PORT_Free(buf->data);
530 buf->data = NULL;
531 }
532 return rv;
533 }
534 #else
535 void
536 ssl_FreePlatformKey(PlatformKey key)
537 {
538 }
539
540 void
541 ssl_FreePlatformAuthInfo(PlatformAuthInfo *info)
542 {
543 }
544
545 void
546 ssl_InitPlatformAuthInfo(PlatformAuthInfo *info)
547 {
548 }
549
550 PRBool
551 ssl_PlatformAuthTokenPresent(PlatformAuthInfo *info)
552 {
553 return PR_FALSE;
554 }
555
556 void
557 ssl_GetPlatformAuthInfoForKey(PlatformKey key, PlatformAuthInfo *info)
558 {
559 }
560
561 SECStatus
562 ssl3_PlatformSignHashes(SSL3Hashes *hash, PlatformKey key, SECItem *buf,
563 PRBool isTLS)
564 {
565 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
566 return SECFailure;
567 }
568 #endif
569
570 #endif /* NSS_PLATFORM_CLIENT_AUTH */
OLDNEW
« no previous file with comments | « net/third_party/nss/ssl/sslnonce.c ('k') | net/third_party/nss/ssl/sslsnce.c » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698