| /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| /* |
| * This file contains prototypes for the public SSL functions. |
| * |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #include "nss.h" |
| #include "pk11func.h" |
| #include "ssl.h" |
| #include "sslimpl.h" |
| |
| struct { |
| sslEphemeralKeyPair *keyPair; |
| PRCallOnceType once; |
| } gECDHEKeyPairs[SSL_NAMED_GROUP_COUNT]; |
| |
| typedef struct sslSocketAndGroupArgStr { |
| const sslNamedGroupDef *group; |
| const sslSocket *ss; |
| } sslSocketAndGroupArg; |
| |
| /* Function to clear out the ECDHE keys. */ |
| static SECStatus |
| ssl_CleanupECDHEKeys(void *appData, void *nssData) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < SSL_NAMED_GROUP_COUNT; i++) { |
| if (gECDHEKeyPairs[i].keyPair) { |
| ssl_FreeEphemeralKeyPair(gECDHEKeyPairs[i].keyPair); |
| } |
| } |
| memset(gECDHEKeyPairs, 0, sizeof(gECDHEKeyPairs)); |
| return SECSuccess; |
| } |
| |
| /* Only run the cleanup once. */ |
| static PRCallOnceType cleanupECDHEKeysOnce; |
| static PRStatus |
| ssl_SetupCleanupECDHEKeysOnce(void) |
| { |
| SECStatus rv = NSS_RegisterShutdown(ssl_CleanupECDHEKeys, NULL); |
| return (rv != SECSuccess) ? PR_FAILURE : PR_SUCCESS; |
| } |
| |
| /* This creates a key pair for each of the supported EC groups. If that works, |
| * we assume that the token supports that group. Since this is relatively |
| * expensive, this is only done for the first socket that is used. That means |
| * that if tokens are added or removed, then this will not pick up any changes. |
| */ |
| static PRStatus |
| ssl_CreateStaticECDHEKeyPair(void *arg) |
| { |
| const sslSocketAndGroupArg *typed_arg = (sslSocketAndGroupArg *)arg; |
| const sslNamedGroupDef *group = typed_arg->group; |
| const sslSocket *ss = typed_arg->ss; |
| unsigned int i = group - ssl_named_groups; |
| SECStatus rv; |
| |
| PORT_Assert(group->keaType == ssl_kea_ecdh); |
| PORT_Assert(i < SSL_NAMED_GROUP_COUNT); |
| rv = ssl_CreateECDHEphemeralKeyPair(ss, group, |
| &gECDHEKeyPairs[i].keyPair); |
| if (rv != SECSuccess) { |
| gECDHEKeyPairs[i].keyPair = NULL; |
| SSL_TRC(5, ("%d: SSL[-]: disabling group %d", |
| SSL_GETPID(), group->name)); |
| } |
| |
| return PR_SUCCESS; |
| } |
| |
| void |
| ssl_FilterSupportedGroups(sslSocket *ss) |
| { |
| unsigned int i; |
| PRStatus prv; |
| sslSocketAndGroupArg arg = { NULL, ss }; |
| |
| prv = PR_CallOnce(&cleanupECDHEKeysOnce, ssl_SetupCleanupECDHEKeysOnce); |
| PORT_Assert(prv == PR_SUCCESS); |
| if (prv != PR_SUCCESS) { |
| return; |
| } |
| |
| for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) { |
| PRUint32 policy; |
| SECStatus srv; |
| unsigned int index; |
| const sslNamedGroupDef *group = ss->namedGroupPreferences[i]; |
| if (!group) { |
| continue; |
| } |
| |
| srv = NSS_GetAlgorithmPolicy(group->oidTag, &policy); |
| if (srv == SECSuccess && !(policy & NSS_USE_ALG_IN_SSL_KX)) { |
| ss->namedGroupPreferences[i] = NULL; |
| continue; |
| } |
| |
| if (group->assumeSupported) { |
| continue; |
| } |
| |
| /* For EC groups, we have to test that a key pair can be created. This |
| * is gross, and expensive, so only do it once. */ |
| index = group - ssl_named_groups; |
| PORT_Assert(index < SSL_NAMED_GROUP_COUNT); |
| |
| arg.group = group; |
| prv = PR_CallOnceWithArg(&gECDHEKeyPairs[index].once, |
| ssl_CreateStaticECDHEKeyPair, |
| (void *)&arg); |
| PORT_Assert(prv == PR_SUCCESS); |
| if (prv != PR_SUCCESS) { |
| continue; |
| } |
| |
| if (!gECDHEKeyPairs[index].keyPair) { |
| ss->namedGroupPreferences[i] = NULL; |
| } |
| } |
| } |
| |
| /* |
| * Creates the static "ephemeral" public and private ECDH keys used by server in |
| * ECDHE_RSA and ECDHE_ECDSA handshakes when we reuse the same key. |
| */ |
| SECStatus |
| ssl_CreateStaticECDHEKey(sslSocket *ss, const sslNamedGroupDef *ecGroup) |
| { |
| sslEphemeralKeyPair *keyPair; |
| /* We index gECDHEKeyPairs by the named group. Pointer arithmetic! */ |
| unsigned int index = ecGroup - ssl_named_groups; |
| PRStatus prv; |
| sslSocketAndGroupArg arg = { ecGroup, ss }; |
| |
| prv = PR_CallOnceWithArg(&gECDHEKeyPairs[index].once, |
| ssl_CreateStaticECDHEKeyPair, |
| (void *)&arg); |
| PORT_Assert(prv == PR_SUCCESS); |
| if (prv != PR_SUCCESS) { |
| PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
| return SECFailure; |
| } |
| |
| keyPair = gECDHEKeyPairs[index].keyPair; |
| if (!keyPair) { |
| /* Attempting to use a key pair for an unsupported group. */ |
| PORT_Assert(keyPair); |
| PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
| return SECFailure; |
| } |
| |
| keyPair = ssl_CopyEphemeralKeyPair(keyPair); |
| if (!keyPair) |
| return SECFailure; |
| |
| PORT_Assert(PR_CLIST_IS_EMPTY(&ss->ephemeralKeyPairs)); |
| PR_APPEND_LINK(&keyPair->link, &ss->ephemeralKeyPairs); |
| return SECSuccess; |
| } |