/* -*- 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; }