| /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| /* |
| * SSL3 Protocol |
| * |
| * 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/. */ |
| |
| /* ECC code moved here from ssl3con.c */ |
| |
| #include "cert.h" |
| #include "ssl.h" |
| #include "cryptohi.h" /* for DSAU_ stuff */ |
| #include "keyhi.h" |
| #include "secder.h" |
| #include "secitem.h" |
| |
| #include "sslimpl.h" |
| #include "sslproto.h" |
| #include "sslerr.h" |
| #include "ssl3ext.h" |
| #include "prtime.h" |
| #include "prinrval.h" |
| #include "prerror.h" |
| #include "pratom.h" |
| #include "prthread.h" |
| #include "prinit.h" |
| |
| #include "pk11func.h" |
| #include "secmod.h" |
| |
| #include <stdio.h> |
| |
| SECStatus |
| ssl_NamedGroup2ECParams(PLArenaPool *arena, const sslNamedGroupDef *ecGroup, |
| SECKEYECParams *params) |
| { |
| SECOidData *oidData = NULL; |
| |
| if (!params) { |
| PORT_Assert(0); |
| PORT_SetError(SEC_ERROR_INVALID_ARGS); |
| return SECFailure; |
| } |
| |
| if (!ecGroup || ecGroup->keaType != ssl_kea_ecdh || |
| (oidData = SECOID_FindOIDByTag(ecGroup->oidTag)) == NULL) { |
| PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE); |
| return SECFailure; |
| } |
| |
| if (SECITEM_AllocItem(arena, params, (2 + oidData->oid.len)) == NULL) { |
| PORT_SetError(SEC_ERROR_NO_MEMORY); |
| return SECFailure; |
| } |
| |
| /* |
| * params->data needs to contain the ASN encoding of an object ID (OID) |
| * representing the named curve. The actual OID is in |
| * oidData->oid.data so we simply prepend 0x06 and OID length |
| */ |
| params->data[0] = SEC_ASN1_OBJECT_ID; |
| params->data[1] = oidData->oid.len; |
| memcpy(params->data + 2, oidData->oid.data, oidData->oid.len); |
| |
| return SECSuccess; |
| } |
| |
| const sslNamedGroupDef * |
| ssl_ECPubKey2NamedGroup(const SECKEYPublicKey *pubKey) |
| { |
| SECItem oid = { siBuffer, NULL, 0 }; |
| SECOidData *oidData = NULL; |
| PRUint32 policyFlags = 0; |
| unsigned int i; |
| const SECKEYECParams *params; |
| |
| if (pubKey->keyType != ecKey) { |
| PORT_Assert(0); |
| return NULL; |
| } |
| |
| params = &pubKey->u.ec.DEREncodedParams; |
| |
| /* |
| * params->data needs to contain the ASN encoding of an object ID (OID) |
| * representing a named curve. Here, we strip away everything |
| * before the actual OID and use the OID to look up a named curve. |
| */ |
| if (params->data[0] != SEC_ASN1_OBJECT_ID) |
| return NULL; |
| oid.len = params->len - 2; |
| oid.data = params->data + 2; |
| if ((oidData = SECOID_FindOID(&oid)) == NULL) |
| return NULL; |
| if ((NSS_GetAlgorithmPolicy(oidData->offset, &policyFlags) == |
| SECSuccess) && |
| !(policyFlags & NSS_USE_ALG_IN_SSL_KX)) { |
| return NULL; |
| } |
| for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) { |
| if (ssl_named_groups[i].oidTag == oidData->offset) { |
| return &ssl_named_groups[i]; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| /* Caller must set hiLevel error code. */ |
| static SECStatus |
| ssl3_ComputeECDHKeyHash(SSLHashType hashAlg, |
| SECItem ec_params, SECItem server_ecpoint, |
| PRUint8 *client_rand, PRUint8 *server_rand, |
| SSL3Hashes *hashes) |
| { |
| PRUint8 *hashBuf; |
| PRUint8 *pBuf; |
| SECStatus rv = SECSuccess; |
| unsigned int bufLen; |
| /* |
| * We only support named curves (the appropriate checks are made before this |
| * method is called) so ec_params takes up only two bytes. ECPoint needs to |
| * fit in 256 bytes because the spec says the length must fit in one byte. |
| */ |
| PRUint8 buf[2 * SSL3_RANDOM_LENGTH + 2 + 1 + 256]; |
| |
| bufLen = 2 * SSL3_RANDOM_LENGTH + ec_params.len + 1 + server_ecpoint.len; |
| if (bufLen <= sizeof buf) { |
| hashBuf = buf; |
| } else { |
| hashBuf = PORT_Alloc(bufLen); |
| if (!hashBuf) { |
| return SECFailure; |
| } |
| } |
| |
| memcpy(hashBuf, client_rand, SSL3_RANDOM_LENGTH); |
| pBuf = hashBuf + SSL3_RANDOM_LENGTH; |
| memcpy(pBuf, server_rand, SSL3_RANDOM_LENGTH); |
| pBuf += SSL3_RANDOM_LENGTH; |
| memcpy(pBuf, ec_params.data, ec_params.len); |
| pBuf += ec_params.len; |
| pBuf[0] = (PRUint8)(server_ecpoint.len); |
| pBuf += 1; |
| memcpy(pBuf, server_ecpoint.data, server_ecpoint.len); |
| pBuf += server_ecpoint.len; |
| PORT_Assert((unsigned int)(pBuf - hashBuf) == bufLen); |
| |
| rv = ssl3_ComputeCommonKeyHash(hashAlg, hashBuf, bufLen, hashes); |
| |
| PRINT_BUF(95, (NULL, "ECDHkey hash: ", hashBuf, bufLen)); |
| PRINT_BUF(95, (NULL, "ECDHkey hash: MD5 result", |
| hashes->u.s.md5, MD5_LENGTH)); |
| PRINT_BUF(95, (NULL, "ECDHkey hash: SHA1 result", |
| hashes->u.s.sha, SHA1_LENGTH)); |
| |
| if (hashBuf != buf) |
| PORT_Free(hashBuf); |
| return rv; |
| } |
| |
| /* Called from ssl3_SendClientKeyExchange(). */ |
| SECStatus |
| ssl3_SendECDHClientKeyExchange(sslSocket *ss, SECKEYPublicKey *svrPubKey) |
| { |
| PK11SymKey *pms = NULL; |
| SECStatus rv = SECFailure; |
| PRBool isTLS, isTLS12; |
| CK_MECHANISM_TYPE target; |
| const sslNamedGroupDef *groupDef; |
| sslEphemeralKeyPair *keyPair = NULL; |
| SECKEYPublicKey *pubKey; |
| |
| PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); |
| PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); |
| |
| isTLS = (PRBool)(ss->version > SSL_LIBRARY_VERSION_3_0); |
| isTLS12 = (PRBool)(ss->version >= SSL_LIBRARY_VERSION_TLS_1_2); |
| |
| /* Generate ephemeral EC keypair */ |
| if (svrPubKey->keyType != ecKey) { |
| PORT_SetError(SEC_ERROR_BAD_KEY); |
| goto loser; |
| } |
| groupDef = ssl_ECPubKey2NamedGroup(svrPubKey); |
| if (!groupDef) { |
| PORT_SetError(SEC_ERROR_BAD_KEY); |
| goto loser; |
| } |
| ss->sec.keaGroup = groupDef; |
| rv = ssl_CreateECDHEphemeralKeyPair(ss, groupDef, &keyPair); |
| if (rv != SECSuccess) { |
| ssl_MapLowLevelError(SEC_ERROR_KEYGEN_FAIL); |
| goto loser; |
| } |
| |
| pubKey = keyPair->keys->pubKey; |
| PRINT_BUF(50, (ss, "ECDH public value:", |
| pubKey->u.ec.publicValue.data, |
| pubKey->u.ec.publicValue.len)); |
| |
| if (isTLS12) { |
| target = CKM_TLS12_MASTER_KEY_DERIVE_DH; |
| } else if (isTLS) { |
| target = CKM_TLS_MASTER_KEY_DERIVE_DH; |
| } else { |
| target = CKM_SSL3_MASTER_KEY_DERIVE_DH; |
| } |
| |
| /* Determine the PMS */ |
| pms = PK11_PubDeriveWithKDF(keyPair->keys->privKey, svrPubKey, |
| PR_FALSE, NULL, NULL, CKM_ECDH1_DERIVE, target, |
| CKA_DERIVE, 0, CKD_NULL, NULL, NULL); |
| |
| if (pms == NULL) { |
| (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter); |
| ssl_MapLowLevelError(SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE); |
| goto loser; |
| } |
| |
| rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_client_key_exchange, |
| pubKey->u.ec.publicValue.len + 1); |
| if (rv != SECSuccess) { |
| goto loser; /* err set by ssl3_AppendHandshake* */ |
| } |
| |
| rv = ssl3_AppendHandshakeVariable(ss, pubKey->u.ec.publicValue.data, |
| pubKey->u.ec.publicValue.len, 1); |
| |
| if (rv != SECSuccess) { |
| goto loser; /* err set by ssl3_AppendHandshake* */ |
| } |
| |
| rv = ssl3_InitPendingCipherSpecs(ss, pms, PR_TRUE); |
| if (rv != SECSuccess) { |
| ssl_MapLowLevelError(SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE); |
| goto loser; |
| } |
| |
| PK11_FreeSymKey(pms); |
| ssl_FreeEphemeralKeyPair(keyPair); |
| return SECSuccess; |
| |
| loser: |
| if (pms) |
| PK11_FreeSymKey(pms); |
| if (keyPair) |
| ssl_FreeEphemeralKeyPair(keyPair); |
| return SECFailure; |
| } |
| |
| /* |
| ** Called from ssl3_HandleClientKeyExchange() |
| */ |
| SECStatus |
| ssl3_HandleECDHClientKeyExchange(sslSocket *ss, PRUint8 *b, |
| PRUint32 length, |
| sslKeyPair *serverKeyPair) |
| { |
| PK11SymKey *pms; |
| SECStatus rv; |
| SECKEYPublicKey clntPubKey; |
| CK_MECHANISM_TYPE target; |
| PRBool isTLS, isTLS12; |
| int errCode = SSL_ERROR_RX_MALFORMED_CLIENT_KEY_EXCH; |
| |
| PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); |
| PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); |
| |
| clntPubKey.keyType = ecKey; |
| clntPubKey.u.ec.DEREncodedParams.len = |
| serverKeyPair->pubKey->u.ec.DEREncodedParams.len; |
| clntPubKey.u.ec.DEREncodedParams.data = |
| serverKeyPair->pubKey->u.ec.DEREncodedParams.data; |
| clntPubKey.u.ec.encoding = ECPoint_Undefined; |
| |
| rv = ssl3_ConsumeHandshakeVariable(ss, &clntPubKey.u.ec.publicValue, |
| 1, &b, &length); |
| if (rv != SECSuccess) { |
| PORT_SetError(errCode); |
| return SECFailure; |
| } |
| |
| /* we have to catch the case when the client's public key has length 0. */ |
| if (!clntPubKey.u.ec.publicValue.len) { |
| (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter); |
| PORT_SetError(errCode); |
| return SECFailure; |
| } |
| |
| isTLS = (PRBool)(ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0); |
| isTLS12 = (PRBool)(ss->ssl3.prSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2); |
| |
| if (isTLS12) { |
| target = CKM_TLS12_MASTER_KEY_DERIVE_DH; |
| } else if (isTLS) { |
| target = CKM_TLS_MASTER_KEY_DERIVE_DH; |
| } else { |
| target = CKM_SSL3_MASTER_KEY_DERIVE_DH; |
| } |
| |
| /* Determine the PMS */ |
| pms = PK11_PubDeriveWithKDF(serverKeyPair->privKey, &clntPubKey, |
| PR_FALSE, NULL, NULL, |
| CKM_ECDH1_DERIVE, target, CKA_DERIVE, 0, |
| CKD_NULL, NULL, NULL); |
| |
| if (pms == NULL) { |
| /* last gasp. */ |
| errCode = ssl_MapLowLevelError(SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE); |
| PORT_SetError(errCode); |
| return SECFailure; |
| } |
| |
| rv = ssl3_InitPendingCipherSpecs(ss, pms, PR_TRUE); |
| PK11_FreeSymKey(pms); |
| if (rv != SECSuccess) { |
| /* error code set by ssl3_InitPendingCipherSpec */ |
| return SECFailure; |
| } |
| ss->sec.keaGroup = ssl_ECPubKey2NamedGroup(&clntPubKey); |
| return SECSuccess; |
| } |
| |
| /* |
| ** Take an encoded key share and make a public key out of it. |
| */ |
| SECStatus |
| ssl_ImportECDHKeyShare(SECKEYPublicKey *peerKey, |
| PRUint8 *b, PRUint32 length, |
| const sslNamedGroupDef *ecGroup) |
| { |
| SECStatus rv; |
| SECItem ecPoint = { siBuffer, NULL, 0 }; |
| |
| if (!length) { |
| PORT_SetError(SSL_ERROR_RX_MALFORMED_ECDHE_KEY_SHARE); |
| return SECFailure; |
| } |
| |
| /* Fail if the ec point uses compressed representation */ |
| if (b[0] != EC_POINT_FORM_UNCOMPRESSED && |
| ecGroup->name != ssl_grp_ec_curve25519) { |
| PORT_SetError(SEC_ERROR_UNSUPPORTED_EC_POINT_FORM); |
| return SECFailure; |
| } |
| |
| peerKey->keyType = ecKey; |
| /* Set up the encoded params */ |
| rv = ssl_NamedGroup2ECParams(peerKey->arena, ecGroup, |
| &peerKey->u.ec.DEREncodedParams); |
| if (rv != SECSuccess) { |
| ssl_MapLowLevelError(SSL_ERROR_RX_MALFORMED_ECDHE_KEY_SHARE); |
| return SECFailure; |
| } |
| peerKey->u.ec.encoding = ECPoint_Undefined; |
| |
| /* copy publicValue in peerKey */ |
| ecPoint.data = b; |
| ecPoint.len = length; |
| |
| rv = SECITEM_CopyItem(peerKey->arena, &peerKey->u.ec.publicValue, &ecPoint); |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| |
| return SECSuccess; |
| } |
| |
| const sslNamedGroupDef * |
| ssl_GetECGroupWithStrength(sslSocket *ss, unsigned int requiredECCbits) |
| { |
| int i; |
| |
| PORT_Assert(ss); |
| |
| for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) { |
| const sslNamedGroupDef *group = ss->namedGroupPreferences[i]; |
| if (group && group->keaType == ssl_kea_ecdh && |
| group->bits >= requiredECCbits) { |
| return group; |
| } |
| } |
| |
| PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP); |
| return NULL; |
| } |
| |
| /* Find the "weakest link". Get the strength of the signature and symmetric |
| * keys and choose a curve based on the weakest of those two. */ |
| const sslNamedGroupDef * |
| ssl_GetECGroupForServerSocket(sslSocket *ss) |
| { |
| const sslServerCert *cert = ss->sec.serverCert; |
| unsigned int certKeySize; |
| const ssl3BulkCipherDef *bulkCipher; |
| unsigned int requiredECCbits; |
| |
| PORT_Assert(cert); |
| if (!cert || !cert->serverKeyPair || !cert->serverKeyPair->pubKey) { |
| PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP); |
| return NULL; |
| } |
| |
| if (SSL_CERT_IS(cert, ssl_auth_rsa_sign) || |
| SSL_CERT_IS(cert, ssl_auth_rsa_pss)) { |
| certKeySize = SECKEY_PublicKeyStrengthInBits(cert->serverKeyPair->pubKey); |
| certKeySize = SSL_RSASTRENGTH_TO_ECSTRENGTH(certKeySize); |
| } else if (SSL_CERT_IS_EC(cert)) { |
| /* We won't select a certificate unless the named curve has been |
| * negotiated (or supported_curves was absent), double check that. */ |
| PORT_Assert(cert->namedCurve->keaType == ssl_kea_ecdh); |
| PORT_Assert(ssl_NamedGroupEnabled(ss, cert->namedCurve)); |
| if (!ssl_NamedGroupEnabled(ss, cert->namedCurve)) { |
| return NULL; |
| } |
| certKeySize = cert->namedCurve->bits; |
| } else { |
| PORT_Assert(0); |
| return NULL; |
| } |
| bulkCipher = ssl_GetBulkCipherDef(ss->ssl3.hs.suite_def); |
| requiredECCbits = bulkCipher->key_size * BPB * 2; |
| PORT_Assert(requiredECCbits || |
| ss->ssl3.hs.suite_def->bulk_cipher_alg == cipher_null); |
| if (requiredECCbits > certKeySize) { |
| requiredECCbits = certKeySize; |
| } |
| |
| return ssl_GetECGroupWithStrength(ss, requiredECCbits); |
| } |
| |
| /* Create an ECDHE key pair for a given curve */ |
| SECStatus |
| ssl_CreateECDHEphemeralKeyPair(const sslSocket *ss, |
| const sslNamedGroupDef *ecGroup, |
| sslEphemeralKeyPair **keyPair) |
| { |
| SECKEYPrivateKey *privKey = NULL; |
| SECKEYPublicKey *pubKey = NULL; |
| SECKEYECParams ecParams = { siBuffer, NULL, 0 }; |
| sslEphemeralKeyPair *pair; |
| |
| if (ssl_NamedGroup2ECParams(NULL, ecGroup, &ecParams) != SECSuccess) { |
| return SECFailure; |
| } |
| privKey = SECKEY_CreateECPrivateKey(&ecParams, &pubKey, ss->pkcs11PinArg); |
| SECITEM_FreeItem(&ecParams, PR_FALSE); |
| |
| if (!privKey || !pubKey || |
| !(pair = ssl_NewEphemeralKeyPair(ecGroup, privKey, pubKey))) { |
| if (privKey) { |
| SECKEY_DestroyPrivateKey(privKey); |
| } |
| if (pubKey) { |
| SECKEY_DestroyPublicKey(pubKey); |
| } |
| ssl_MapLowLevelError(SEC_ERROR_KEYGEN_FAIL); |
| return SECFailure; |
| } |
| |
| *keyPair = pair; |
| SSL_TRC(50, ("%d: SSL[%d]: Create ECDH ephemeral key %d", |
| SSL_GETPID(), ss ? ss->fd : NULL, ecGroup->name)); |
| PRINT_BUF(50, (ss, "Public Key", pubKey->u.ec.publicValue.data, |
| pubKey->u.ec.publicValue.len)); |
| #ifdef TRACE |
| if (ssl_trace >= 50) { |
| SECItem d = { siBuffer, NULL, 0 }; |
| SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, privKey, |
| CKA_VALUE, &d); |
| if (rv == SECSuccess) { |
| PRINT_BUF(50, (ss, "Private Key", d.data, d.len)); |
| SECITEM_FreeItem(&d, PR_FALSE); |
| } else { |
| SSL_TRC(50, ("Error extracting private key")); |
| } |
| } |
| #endif |
| return SECSuccess; |
| } |
| |
| SECStatus |
| ssl3_HandleECDHServerKeyExchange(sslSocket *ss, PRUint8 *b, PRUint32 length) |
| { |
| PLArenaPool *arena = NULL; |
| SECKEYPublicKey *peerKey = NULL; |
| PRBool isTLS; |
| SECStatus rv; |
| int errCode = SSL_ERROR_RX_MALFORMED_SERVER_KEY_EXCH; |
| SSL3AlertDescription desc = illegal_parameter; |
| SSL3Hashes hashes; |
| SECItem signature = { siBuffer, NULL, 0 }; |
| SSLHashType hashAlg; |
| SSLSignatureScheme sigScheme; |
| |
| SECItem ec_params = { siBuffer, NULL, 0 }; |
| SECItem ec_point = { siBuffer, NULL, 0 }; |
| unsigned char paramBuf[3]; |
| const sslNamedGroupDef *ecGroup; |
| |
| isTLS = (PRBool)(ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0); |
| |
| ec_params.len = sizeof paramBuf; |
| ec_params.data = paramBuf; |
| rv = ssl3_ConsumeHandshake(ss, ec_params.data, ec_params.len, &b, &length); |
| if (rv != SECSuccess) { |
| goto loser; /* malformed. */ |
| } |
| |
| /* Fail if the curve is not a named curve */ |
| if (ec_params.data[0] != ec_type_named) { |
| errCode = SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE; |
| desc = handshake_failure; |
| goto alert_loser; |
| } |
| ecGroup = ssl_LookupNamedGroup(ec_params.data[1] << 8 | ec_params.data[2]); |
| if (!ecGroup || ecGroup->keaType != ssl_kea_ecdh) { |
| errCode = SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE; |
| desc = handshake_failure; |
| goto alert_loser; |
| } |
| |
| rv = ssl3_ConsumeHandshakeVariable(ss, &ec_point, 1, &b, &length); |
| if (rv != SECSuccess) { |
| goto loser; /* malformed. */ |
| } |
| |
| /* Fail if the provided point has length 0. */ |
| if (!ec_point.len) { |
| /* desc and errCode are initialized already */ |
| goto alert_loser; |
| } |
| |
| /* Fail if the ec point is not uncompressed for any curve that's not 25519. */ |
| if (ecGroup->name != ssl_grp_ec_curve25519 && |
| ec_point.data[0] != EC_POINT_FORM_UNCOMPRESSED) { |
| errCode = SEC_ERROR_UNSUPPORTED_EC_POINT_FORM; |
| desc = handshake_failure; |
| goto alert_loser; |
| } |
| |
| PORT_Assert(ss->ssl3.prSpec->version <= SSL_LIBRARY_VERSION_TLS_1_2); |
| if (ss->ssl3.prSpec->version == SSL_LIBRARY_VERSION_TLS_1_2) { |
| rv = ssl_ConsumeSignatureScheme(ss, &b, &length, &sigScheme); |
| if (rv != SECSuccess) { |
| errCode = PORT_GetError(); |
| goto alert_loser; /* malformed or unsupported. */ |
| } |
| rv = ssl_CheckSignatureSchemeConsistency(ss, sigScheme, |
| ss->sec.peerCert); |
| if (rv != SECSuccess) { |
| errCode = PORT_GetError(); |
| goto alert_loser; |
| } |
| hashAlg = ssl_SignatureSchemeToHashType(sigScheme); |
| } else { |
| /* Use ssl_hash_none to represent the MD5+SHA1 combo. */ |
| hashAlg = ssl_hash_none; |
| sigScheme = ssl_sig_none; |
| } |
| |
| rv = ssl3_ConsumeHandshakeVariable(ss, &signature, 2, &b, &length); |
| if (rv != SECSuccess) { |
| goto loser; /* malformed. */ |
| } |
| |
| if (length != 0) { |
| if (isTLS) |
| desc = decode_error; |
| goto alert_loser; /* malformed. */ |
| } |
| |
| PRINT_BUF(60, (NULL, "Server EC params", ec_params.data, ec_params.len)); |
| PRINT_BUF(60, (NULL, "Server EC point", ec_point.data, ec_point.len)); |
| |
| /* failures after this point are not malformed handshakes. */ |
| /* TLS: send decrypt_error if signature failed. */ |
| desc = isTLS ? decrypt_error : handshake_failure; |
| |
| /* |
| * check to make sure the hash is signed by right guy |
| */ |
| rv = ssl3_ComputeECDHKeyHash(hashAlg, ec_params, ec_point, |
| ss->ssl3.hs.client_random, |
| ss->ssl3.hs.server_random, |
| &hashes); |
| |
| if (rv != SECSuccess) { |
| errCode = |
| ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE); |
| goto alert_loser; |
| } |
| rv = ssl3_VerifySignedHashes(ss, sigScheme, &hashes, &signature); |
| if (rv != SECSuccess) { |
| errCode = |
| ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE); |
| goto alert_loser; |
| } |
| |
| arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
| if (arena == NULL) { |
| errCode = SEC_ERROR_NO_MEMORY; |
| goto loser; |
| } |
| |
| peerKey = PORT_ArenaZNew(arena, SECKEYPublicKey); |
| if (peerKey == NULL) { |
| errCode = SEC_ERROR_NO_MEMORY; |
| goto loser; |
| } |
| peerKey->arena = arena; |
| |
| /* create public key from point data */ |
| rv = ssl_ImportECDHKeyShare(peerKey, ec_point.data, ec_point.len, |
| ecGroup); |
| if (rv != SECSuccess) { |
| /* error code is set */ |
| desc = handshake_failure; |
| errCode = PORT_GetError(); |
| goto alert_loser; |
| } |
| peerKey->pkcs11Slot = NULL; |
| peerKey->pkcs11ID = CK_INVALID_HANDLE; |
| |
| ss->sec.peerKey = peerKey; |
| return SECSuccess; |
| |
| alert_loser: |
| (void)SSL3_SendAlert(ss, alert_fatal, desc); |
| loser: |
| if (arena) { |
| PORT_FreeArena(arena, PR_FALSE); |
| } |
| PORT_SetError(errCode); |
| return SECFailure; |
| } |
| |
| SECStatus |
| ssl3_SendECDHServerKeyExchange(sslSocket *ss) |
| { |
| SECStatus rv = SECFailure; |
| int length; |
| PRBool isTLS12; |
| SECItem signed_hash = { siBuffer, NULL, 0 }; |
| SSLHashType hashAlg; |
| SSL3Hashes hashes; |
| |
| SECItem ec_params = { siBuffer, NULL, 0 }; |
| unsigned char paramBuf[3]; |
| const sslNamedGroupDef *ecGroup; |
| sslEphemeralKeyPair *keyPair; |
| SECKEYPublicKey *pubKey; |
| |
| /* Generate ephemeral ECDH key pair and send the public key */ |
| ecGroup = ssl_GetECGroupForServerSocket(ss); |
| if (!ecGroup) { |
| goto loser; |
| } |
| |
| PORT_Assert(PR_CLIST_IS_EMPTY(&ss->ephemeralKeyPairs)); |
| if (ss->opt.reuseServerECDHEKey) { |
| rv = ssl_CreateStaticECDHEKey(ss, ecGroup); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| keyPair = (sslEphemeralKeyPair *)PR_NEXT_LINK(&ss->ephemeralKeyPairs); |
| } else { |
| rv = ssl_CreateECDHEphemeralKeyPair(ss, ecGroup, &keyPair); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| PR_APPEND_LINK(&keyPair->link, &ss->ephemeralKeyPairs); |
| } |
| |
| PORT_Assert(keyPair); |
| if (!keyPair) { |
| PORT_SetError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE); |
| return SECFailure; |
| } |
| |
| ec_params.len = sizeof(paramBuf); |
| ec_params.data = paramBuf; |
| PORT_Assert(keyPair->group); |
| PORT_Assert(keyPair->group->keaType == ssl_kea_ecdh); |
| ec_params.data[0] = ec_type_named; |
| ec_params.data[1] = keyPair->group->name >> 8; |
| ec_params.data[2] = keyPair->group->name & 0xff; |
| |
| pubKey = keyPair->keys->pubKey; |
| if (ss->version == SSL_LIBRARY_VERSION_TLS_1_2) { |
| hashAlg = ssl_SignatureSchemeToHashType(ss->ssl3.hs.signatureScheme); |
| } else { |
| /* Use ssl_hash_none to represent the MD5+SHA1 combo. */ |
| hashAlg = ssl_hash_none; |
| } |
| rv = ssl3_ComputeECDHKeyHash(hashAlg, ec_params, |
| pubKey->u.ec.publicValue, |
| ss->ssl3.hs.client_random, |
| ss->ssl3.hs.server_random, |
| &hashes); |
| if (rv != SECSuccess) { |
| ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE); |
| goto loser; |
| } |
| |
| isTLS12 = (PRBool)(ss->version >= SSL_LIBRARY_VERSION_TLS_1_2); |
| |
| rv = ssl3_SignHashes(ss, &hashes, |
| ss->sec.serverCert->serverKeyPair->privKey, &signed_hash); |
| if (rv != SECSuccess) { |
| goto loser; /* ssl3_SignHashes has set err. */ |
| } |
| |
| length = ec_params.len + |
| 1 + pubKey->u.ec.publicValue.len + |
| (isTLS12 ? 2 : 0) + 2 + signed_hash.len; |
| |
| rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_server_key_exchange, length); |
| if (rv != SECSuccess) { |
| goto loser; /* err set by AppendHandshake. */ |
| } |
| |
| rv = ssl3_AppendHandshake(ss, ec_params.data, ec_params.len); |
| if (rv != SECSuccess) { |
| goto loser; /* err set by AppendHandshake. */ |
| } |
| |
| rv = ssl3_AppendHandshakeVariable(ss, pubKey->u.ec.publicValue.data, |
| pubKey->u.ec.publicValue.len, 1); |
| if (rv != SECSuccess) { |
| goto loser; /* err set by AppendHandshake. */ |
| } |
| |
| if (isTLS12) { |
| rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.hs.signatureScheme, 2); |
| if (rv != SECSuccess) { |
| goto loser; /* err set by AppendHandshake. */ |
| } |
| } |
| |
| rv = ssl3_AppendHandshakeVariable(ss, signed_hash.data, |
| signed_hash.len, 2); |
| if (rv != SECSuccess) { |
| goto loser; /* err set by AppendHandshake. */ |
| } |
| |
| PORT_Free(signed_hash.data); |
| return SECSuccess; |
| |
| loser: |
| if (signed_hash.data != NULL) |
| PORT_Free(signed_hash.data); |
| return SECFailure; |
| } |
| |
| /* List of all ECC cipher suites */ |
| static const ssl3CipherSuite ssl_all_ec_suites[] = { |
| TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, |
| TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, |
| TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, |
| TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, |
| TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, |
| TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, |
| TLS_ECDHE_ECDSA_WITH_NULL_SHA, |
| TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, |
| TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, |
| TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, |
| TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, |
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, |
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, |
| TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, |
| TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, |
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, |
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, |
| TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, |
| TLS_ECDHE_RSA_WITH_NULL_SHA, |
| TLS_ECDHE_RSA_WITH_RC4_128_SHA, |
| TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, |
| TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, |
| TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, |
| TLS_ECDH_ECDSA_WITH_NULL_SHA, |
| TLS_ECDH_ECDSA_WITH_RC4_128_SHA, |
| TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, |
| TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, |
| TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, |
| TLS_ECDH_RSA_WITH_NULL_SHA, |
| TLS_ECDH_RSA_WITH_RC4_128_SHA, |
| 0 /* end of list marker */ |
| }; |
| |
| static const ssl3CipherSuite ssl_dhe_suites[] = { |
| TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, |
| TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, |
| TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, |
| TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, |
| TLS_DHE_RSA_WITH_AES_128_CBC_SHA, |
| TLS_DHE_DSS_WITH_AES_128_CBC_SHA, |
| TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, |
| TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, |
| TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, |
| TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA, |
| TLS_DHE_RSA_WITH_AES_256_CBC_SHA, |
| TLS_DHE_DSS_WITH_AES_256_CBC_SHA, |
| TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, |
| TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, |
| TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, |
| TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA, |
| TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, |
| TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, |
| TLS_DHE_DSS_WITH_RC4_128_SHA, |
| TLS_DHE_RSA_WITH_DES_CBC_SHA, |
| TLS_DHE_DSS_WITH_DES_CBC_SHA, |
| 0 |
| }; |
| |
| /* Order(N^2). Yuk. */ |
| static PRBool |
| ssl_IsSuiteEnabled(const sslSocket *ss, const ssl3CipherSuite *list) |
| { |
| const ssl3CipherSuite *suite; |
| |
| for (suite = list; *suite; ++suite) { |
| PRBool enabled = PR_FALSE; |
| SECStatus rv = ssl3_CipherPrefGet(ss, *suite, &enabled); |
| |
| PORT_Assert(rv == SECSuccess); /* else is coding error */ |
| if (rv == SECSuccess && enabled) |
| return PR_TRUE; |
| } |
| return PR_FALSE; |
| } |
| |
| /* Ask: is ANY ECC cipher suite enabled on this socket? */ |
| PRBool |
| ssl_IsECCEnabled(const sslSocket *ss) |
| { |
| PK11SlotInfo *slot; |
| |
| /* make sure we can do ECC */ |
| slot = PK11_GetBestSlot(CKM_ECDH1_DERIVE, ss->pkcs11PinArg); |
| if (!slot) { |
| return PR_FALSE; |
| } |
| PK11_FreeSlot(slot); |
| |
| /* make sure an ECC cipher is enabled */ |
| return ssl_IsSuiteEnabled(ss, ssl_all_ec_suites); |
| } |
| |
| PRBool |
| ssl_IsDHEEnabled(const sslSocket *ss) |
| { |
| return ssl_IsSuiteEnabled(ss, ssl_dhe_suites); |
| } |
| |
| /* Send our Supported Groups extension. */ |
| SECStatus |
| ssl_SendSupportedGroupsXtn(const sslSocket *ss, TLSExtensionData *xtnData, |
| sslBuffer *buf, PRBool *added) |
| { |
| unsigned int i; |
| PRBool ec; |
| PRBool ff = PR_FALSE; |
| PRBool found = PR_FALSE; |
| SECStatus rv; |
| unsigned int lengthOffset; |
| |
| /* We only send FF supported groups if we require DH named groups |
| * or if TLS 1.3 is a possibility. */ |
| if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) { |
| ec = ssl_IsECCEnabled(ss); |
| if (ss->opt.requireDHENamedGroups) { |
| ff = ssl_IsDHEEnabled(ss); |
| } |
| if (!ec && !ff) { |
| return SECSuccess; |
| } |
| } else { |
| ec = ff = PR_TRUE; |
| } |
| |
| /* Mark the location of the length. */ |
| rv = sslBuffer_Skip(buf, 2, &lengthOffset); |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| |
| for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) { |
| const sslNamedGroupDef *group = ss->namedGroupPreferences[i]; |
| if (!group) { |
| continue; |
| } |
| if (group->keaType == ssl_kea_ecdh && !ec) { |
| continue; |
| } |
| if (group->keaType == ssl_kea_dh && !ff) { |
| continue; |
| } |
| |
| found = PR_TRUE; |
| rv = sslBuffer_AppendNumber(buf, group->name, 2); |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| } |
| |
| if (!found) { |
| /* We added nothing, don't send the extension. */ |
| return SECSuccess; |
| } |
| |
| rv = sslBuffer_InsertLength(buf, lengthOffset, 2); |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| |
| *added = PR_TRUE; |
| return SECSuccess; |
| } |
| |
| /* Send our "canned" (precompiled) Supported Point Formats extension, |
| * which says that we only support uncompressed points. |
| */ |
| SECStatus |
| ssl3_SendSupportedPointFormatsXtn(const sslSocket *ss, TLSExtensionData *xtnData, |
| sslBuffer *buf, PRBool *added) |
| { |
| SECStatus rv; |
| |
| /* No point in doing this unless we have a socket that supports ECC. |
| * Similarly, no point if we are going to do TLS 1.3 only or we have already |
| * picked TLS 1.3 (server) given that it doesn't use point formats. */ |
| if (!ss || !ssl_IsECCEnabled(ss) || |
| ss->vrange.min >= SSL_LIBRARY_VERSION_TLS_1_3 || |
| (ss->sec.isServer && ss->version >= SSL_LIBRARY_VERSION_TLS_1_3)) { |
| return SECSuccess; |
| } |
| rv = sslBuffer_AppendNumber(buf, 1, 1); /* length */ |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| rv = sslBuffer_AppendNumber(buf, 0, 1); /* uncompressed type only */ |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| |
| *added = PR_TRUE; |
| return SECSuccess; |
| } |