michael@0: /* michael@0: * SSL3 Protocol michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* ECC code moved here from ssl3con.c */ michael@0: michael@0: #include "nss.h" michael@0: #include "cert.h" michael@0: #include "ssl.h" michael@0: #include "cryptohi.h" /* for DSAU_ stuff */ michael@0: #include "keyhi.h" michael@0: #include "secder.h" michael@0: #include "secitem.h" michael@0: michael@0: #include "sslimpl.h" michael@0: #include "sslproto.h" michael@0: #include "sslerr.h" michael@0: #include "prtime.h" michael@0: #include "prinrval.h" michael@0: #include "prerror.h" michael@0: #include "pratom.h" michael@0: #include "prthread.h" michael@0: #include "prinit.h" michael@0: michael@0: #include "pk11func.h" michael@0: #include "secmod.h" michael@0: michael@0: #include michael@0: michael@0: #ifndef NSS_DISABLE_ECC michael@0: michael@0: #ifndef PK11_SETATTRS michael@0: #define PK11_SETATTRS(x,id,v,l) (x)->type = (id); \ michael@0: (x)->pValue=(v); (x)->ulValueLen = (l); michael@0: #endif michael@0: michael@0: #define SSL_GET_SERVER_PUBLIC_KEY(sock, type) \ michael@0: (ss->serverCerts[type].serverKeyPair ? \ michael@0: ss->serverCerts[type].serverKeyPair->pubKey : NULL) michael@0: michael@0: #define SSL_IS_CURVE_NEGOTIATED(curvemsk, curveName) \ michael@0: ((curveName > ec_noName) && \ michael@0: (curveName < ec_pastLastName) && \ michael@0: ((1UL << curveName) & curvemsk) != 0) michael@0: michael@0: michael@0: michael@0: static SECStatus ssl3_CreateECDHEphemeralKeys(sslSocket *ss, ECName ec_curve); michael@0: michael@0: #define supportedCurve(x) (((x) > ec_noName) && ((x) < ec_pastLastName)) michael@0: michael@0: /* Table containing OID tags for elliptic curves named in the michael@0: * ECC-TLS IETF draft. michael@0: */ michael@0: static const SECOidTag ecName2OIDTag[] = { michael@0: 0, michael@0: SEC_OID_SECG_EC_SECT163K1, /* 1 */ michael@0: SEC_OID_SECG_EC_SECT163R1, /* 2 */ michael@0: SEC_OID_SECG_EC_SECT163R2, /* 3 */ michael@0: SEC_OID_SECG_EC_SECT193R1, /* 4 */ michael@0: SEC_OID_SECG_EC_SECT193R2, /* 5 */ michael@0: SEC_OID_SECG_EC_SECT233K1, /* 6 */ michael@0: SEC_OID_SECG_EC_SECT233R1, /* 7 */ michael@0: SEC_OID_SECG_EC_SECT239K1, /* 8 */ michael@0: SEC_OID_SECG_EC_SECT283K1, /* 9 */ michael@0: SEC_OID_SECG_EC_SECT283R1, /* 10 */ michael@0: SEC_OID_SECG_EC_SECT409K1, /* 11 */ michael@0: SEC_OID_SECG_EC_SECT409R1, /* 12 */ michael@0: SEC_OID_SECG_EC_SECT571K1, /* 13 */ michael@0: SEC_OID_SECG_EC_SECT571R1, /* 14 */ michael@0: SEC_OID_SECG_EC_SECP160K1, /* 15 */ michael@0: SEC_OID_SECG_EC_SECP160R1, /* 16 */ michael@0: SEC_OID_SECG_EC_SECP160R2, /* 17 */ michael@0: SEC_OID_SECG_EC_SECP192K1, /* 18 */ michael@0: SEC_OID_SECG_EC_SECP192R1, /* 19 */ michael@0: SEC_OID_SECG_EC_SECP224K1, /* 20 */ michael@0: SEC_OID_SECG_EC_SECP224R1, /* 21 */ michael@0: SEC_OID_SECG_EC_SECP256K1, /* 22 */ michael@0: SEC_OID_SECG_EC_SECP256R1, /* 23 */ michael@0: SEC_OID_SECG_EC_SECP384R1, /* 24 */ michael@0: SEC_OID_SECG_EC_SECP521R1, /* 25 */ michael@0: }; michael@0: michael@0: static const PRUint16 curve2bits[] = { michael@0: 0, /* ec_noName = 0, */ michael@0: 163, /* ec_sect163k1 = 1, */ michael@0: 163, /* ec_sect163r1 = 2, */ michael@0: 163, /* ec_sect163r2 = 3, */ michael@0: 193, /* ec_sect193r1 = 4, */ michael@0: 193, /* ec_sect193r2 = 5, */ michael@0: 233, /* ec_sect233k1 = 6, */ michael@0: 233, /* ec_sect233r1 = 7, */ michael@0: 239, /* ec_sect239k1 = 8, */ michael@0: 283, /* ec_sect283k1 = 9, */ michael@0: 283, /* ec_sect283r1 = 10, */ michael@0: 409, /* ec_sect409k1 = 11, */ michael@0: 409, /* ec_sect409r1 = 12, */ michael@0: 571, /* ec_sect571k1 = 13, */ michael@0: 571, /* ec_sect571r1 = 14, */ michael@0: 160, /* ec_secp160k1 = 15, */ michael@0: 160, /* ec_secp160r1 = 16, */ michael@0: 160, /* ec_secp160r2 = 17, */ michael@0: 192, /* ec_secp192k1 = 18, */ michael@0: 192, /* ec_secp192r1 = 19, */ michael@0: 224, /* ec_secp224k1 = 20, */ michael@0: 224, /* ec_secp224r1 = 21, */ michael@0: 256, /* ec_secp256k1 = 22, */ michael@0: 256, /* ec_secp256r1 = 23, */ michael@0: 384, /* ec_secp384r1 = 24, */ michael@0: 521, /* ec_secp521r1 = 25, */ michael@0: 65535 /* ec_pastLastName */ michael@0: }; michael@0: michael@0: typedef struct Bits2CurveStr { michael@0: PRUint16 bits; michael@0: ECName curve; michael@0: } Bits2Curve; michael@0: michael@0: static const Bits2Curve bits2curve [] = { michael@0: { 192, ec_secp192r1 /* = 19, fast */ }, michael@0: { 160, ec_secp160r2 /* = 17, fast */ }, michael@0: { 160, ec_secp160k1 /* = 15, */ }, michael@0: { 160, ec_secp160r1 /* = 16, */ }, michael@0: { 163, ec_sect163k1 /* = 1, */ }, michael@0: { 163, ec_sect163r1 /* = 2, */ }, michael@0: { 163, ec_sect163r2 /* = 3, */ }, michael@0: { 192, ec_secp192k1 /* = 18, */ }, michael@0: { 193, ec_sect193r1 /* = 4, */ }, michael@0: { 193, ec_sect193r2 /* = 5, */ }, michael@0: { 224, ec_secp224r1 /* = 21, fast */ }, michael@0: { 224, ec_secp224k1 /* = 20, */ }, michael@0: { 233, ec_sect233k1 /* = 6, */ }, michael@0: { 233, ec_sect233r1 /* = 7, */ }, michael@0: { 239, ec_sect239k1 /* = 8, */ }, michael@0: { 256, ec_secp256r1 /* = 23, fast */ }, michael@0: { 256, ec_secp256k1 /* = 22, */ }, michael@0: { 283, ec_sect283k1 /* = 9, */ }, michael@0: { 283, ec_sect283r1 /* = 10, */ }, michael@0: { 384, ec_secp384r1 /* = 24, fast */ }, michael@0: { 409, ec_sect409k1 /* = 11, */ }, michael@0: { 409, ec_sect409r1 /* = 12, */ }, michael@0: { 521, ec_secp521r1 /* = 25, fast */ }, michael@0: { 571, ec_sect571k1 /* = 13, */ }, michael@0: { 571, ec_sect571r1 /* = 14, */ }, michael@0: { 65535, ec_noName } michael@0: }; michael@0: michael@0: typedef struct ECDHEKeyPairStr { michael@0: ssl3KeyPair * pair; michael@0: int error; /* error code of the call-once function */ michael@0: PRCallOnceType once; michael@0: } ECDHEKeyPair; michael@0: michael@0: /* arrays of ECDHE KeyPairs */ michael@0: static ECDHEKeyPair gECDHEKeyPairs[ec_pastLastName]; michael@0: michael@0: SECStatus michael@0: ssl3_ECName2Params(PLArenaPool * arena, ECName curve, SECKEYECParams * params) michael@0: { michael@0: SECOidData *oidData = NULL; michael@0: michael@0: if ((curve <= ec_noName) || (curve >= ec_pastLastName) || michael@0: ((oidData = SECOID_FindOIDByTag(ecName2OIDTag[curve])) == NULL)) { michael@0: PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE); michael@0: return SECFailure; michael@0: } michael@0: michael@0: SECITEM_AllocItem(arena, params, (2 + oidData->oid.len)); michael@0: /* michael@0: * params->data needs to contain the ASN encoding of an object ID (OID) michael@0: * representing the named curve. The actual OID is in michael@0: * oidData->oid.data so we simply prepend 0x06 and OID length michael@0: */ michael@0: params->data[0] = SEC_ASN1_OBJECT_ID; michael@0: params->data[1] = oidData->oid.len; michael@0: memcpy(params->data + 2, oidData->oid.data, oidData->oid.len); michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: static ECName michael@0: params2ecName(SECKEYECParams * params) michael@0: { michael@0: SECItem oid = { siBuffer, NULL, 0}; michael@0: SECOidData *oidData = NULL; michael@0: ECName i; michael@0: michael@0: /* michael@0: * params->data needs to contain the ASN encoding of an object ID (OID) michael@0: * representing a named curve. Here, we strip away everything michael@0: * before the actual OID and use the OID to look up a named curve. michael@0: */ michael@0: if (params->data[0] != SEC_ASN1_OBJECT_ID) return ec_noName; michael@0: oid.len = params->len - 2; michael@0: oid.data = params->data + 2; michael@0: if ((oidData = SECOID_FindOID(&oid)) == NULL) return ec_noName; michael@0: for (i = ec_noName + 1; i < ec_pastLastName; i++) { michael@0: if (ecName2OIDTag[i] == oidData->offset) michael@0: return i; michael@0: } michael@0: michael@0: return ec_noName; michael@0: } michael@0: michael@0: /* Caller must set hiLevel error code. */ michael@0: static SECStatus michael@0: ssl3_ComputeECDHKeyHash(SECOidTag hashAlg, michael@0: SECItem ec_params, SECItem server_ecpoint, michael@0: SSL3Random *client_rand, SSL3Random *server_rand, michael@0: SSL3Hashes *hashes, PRBool bypassPKCS11) michael@0: { michael@0: PRUint8 * hashBuf; michael@0: PRUint8 * pBuf; michael@0: SECStatus rv = SECSuccess; michael@0: unsigned int bufLen; michael@0: /* michael@0: * XXX For now, we only support named curves (the appropriate michael@0: * checks are made before this method is called) so ec_params michael@0: * takes up only two bytes. ECPoint needs to fit in 256 bytes michael@0: * (because the spec says the length must fit in one byte) michael@0: */ michael@0: PRUint8 buf[2*SSL3_RANDOM_LENGTH + 2 + 1 + 256]; michael@0: michael@0: bufLen = 2*SSL3_RANDOM_LENGTH + ec_params.len + 1 + server_ecpoint.len; michael@0: if (bufLen <= sizeof buf) { michael@0: hashBuf = buf; michael@0: } else { michael@0: hashBuf = PORT_Alloc(bufLen); michael@0: if (!hashBuf) { michael@0: return SECFailure; michael@0: } michael@0: } michael@0: michael@0: memcpy(hashBuf, client_rand, SSL3_RANDOM_LENGTH); michael@0: pBuf = hashBuf + SSL3_RANDOM_LENGTH; michael@0: memcpy(pBuf, server_rand, SSL3_RANDOM_LENGTH); michael@0: pBuf += SSL3_RANDOM_LENGTH; michael@0: memcpy(pBuf, ec_params.data, ec_params.len); michael@0: pBuf += ec_params.len; michael@0: pBuf[0] = (PRUint8)(server_ecpoint.len); michael@0: pBuf += 1; michael@0: memcpy(pBuf, server_ecpoint.data, server_ecpoint.len); michael@0: pBuf += server_ecpoint.len; michael@0: PORT_Assert((unsigned int)(pBuf - hashBuf) == bufLen); michael@0: michael@0: rv = ssl3_ComputeCommonKeyHash(hashAlg, hashBuf, bufLen, hashes, michael@0: bypassPKCS11); michael@0: michael@0: PRINT_BUF(95, (NULL, "ECDHkey hash: ", hashBuf, bufLen)); michael@0: PRINT_BUF(95, (NULL, "ECDHkey hash: MD5 result", michael@0: hashes->u.s.md5, MD5_LENGTH)); michael@0: PRINT_BUF(95, (NULL, "ECDHkey hash: SHA1 result", michael@0: hashes->u.s.sha, SHA1_LENGTH)); michael@0: michael@0: if (hashBuf != buf) michael@0: PORT_Free(hashBuf); michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: /* Called from ssl3_SendClientKeyExchange(). */ michael@0: SECStatus michael@0: ssl3_SendECDHClientKeyExchange(sslSocket * ss, SECKEYPublicKey * svrPubKey) michael@0: { michael@0: PK11SymKey * pms = NULL; michael@0: SECStatus rv = SECFailure; michael@0: PRBool isTLS, isTLS12; michael@0: CK_MECHANISM_TYPE target; michael@0: SECKEYPublicKey *pubKey = NULL; /* Ephemeral ECDH key */ michael@0: SECKEYPrivateKey *privKey = NULL; /* Ephemeral ECDH key */ michael@0: michael@0: PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) ); michael@0: PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); michael@0: michael@0: isTLS = (PRBool)(ss->ssl3.pwSpec->version > SSL_LIBRARY_VERSION_3_0); michael@0: isTLS12 = (PRBool)(ss->ssl3.pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2); michael@0: michael@0: /* Generate ephemeral EC keypair */ michael@0: if (svrPubKey->keyType != ecKey) { michael@0: PORT_SetError(SEC_ERROR_BAD_KEY); michael@0: goto loser; michael@0: } michael@0: /* XXX SHOULD CALL ssl3_CreateECDHEphemeralKeys here, instead! */ michael@0: privKey = SECKEY_CreateECPrivateKey(&svrPubKey->u.ec.DEREncodedParams, michael@0: &pubKey, ss->pkcs11PinArg); michael@0: if (!privKey || !pubKey) { michael@0: ssl_MapLowLevelError(SEC_ERROR_KEYGEN_FAIL); michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: PRINT_BUF(50, (ss, "ECDH public value:", michael@0: pubKey->u.ec.publicValue.data, michael@0: pubKey->u.ec.publicValue.len)); michael@0: michael@0: if (isTLS12) { michael@0: target = CKM_NSS_TLS_MASTER_KEY_DERIVE_DH_SHA256; michael@0: } else if (isTLS) { michael@0: target = CKM_TLS_MASTER_KEY_DERIVE_DH; michael@0: } else { michael@0: target = CKM_SSL3_MASTER_KEY_DERIVE_DH; michael@0: } michael@0: michael@0: /* Determine the PMS */ michael@0: pms = PK11_PubDeriveWithKDF(privKey, svrPubKey, PR_FALSE, NULL, NULL, michael@0: CKM_ECDH1_DERIVE, target, CKA_DERIVE, 0, michael@0: CKD_NULL, NULL, NULL); michael@0: michael@0: if (pms == NULL) { michael@0: SSL3AlertDescription desc = illegal_parameter; michael@0: (void)SSL3_SendAlert(ss, alert_fatal, desc); michael@0: ssl_MapLowLevelError(SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE); michael@0: goto loser; michael@0: } michael@0: michael@0: SECKEY_DestroyPrivateKey(privKey); michael@0: privKey = NULL; michael@0: michael@0: rv = ssl3_InitPendingCipherSpec(ss, pms); michael@0: PK11_FreeSymKey(pms); pms = NULL; michael@0: michael@0: if (rv != SECSuccess) { michael@0: ssl_MapLowLevelError(SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE); michael@0: goto loser; michael@0: } michael@0: michael@0: rv = ssl3_AppendHandshakeHeader(ss, client_key_exchange, michael@0: pubKey->u.ec.publicValue.len + 1); michael@0: if (rv != SECSuccess) { michael@0: goto loser; /* err set by ssl3_AppendHandshake* */ michael@0: } michael@0: michael@0: rv = ssl3_AppendHandshakeVariable(ss, michael@0: pubKey->u.ec.publicValue.data, michael@0: pubKey->u.ec.publicValue.len, 1); michael@0: SECKEY_DestroyPublicKey(pubKey); michael@0: pubKey = NULL; michael@0: michael@0: if (rv != SECSuccess) { michael@0: goto loser; /* err set by ssl3_AppendHandshake* */ michael@0: } michael@0: michael@0: rv = SECSuccess; michael@0: michael@0: loser: michael@0: if(pms) PK11_FreeSymKey(pms); michael@0: if(privKey) SECKEY_DestroyPrivateKey(privKey); michael@0: if(pubKey) SECKEY_DestroyPublicKey(pubKey); michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: /* michael@0: ** Called from ssl3_HandleClientKeyExchange() michael@0: */ michael@0: SECStatus michael@0: ssl3_HandleECDHClientKeyExchange(sslSocket *ss, SSL3Opaque *b, michael@0: PRUint32 length, michael@0: SECKEYPublicKey *srvrPubKey, michael@0: SECKEYPrivateKey *srvrPrivKey) michael@0: { michael@0: PK11SymKey * pms; michael@0: SECStatus rv; michael@0: SECKEYPublicKey clntPubKey; michael@0: CK_MECHANISM_TYPE target; michael@0: PRBool isTLS, isTLS12; michael@0: michael@0: PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) ); michael@0: PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) ); michael@0: michael@0: clntPubKey.keyType = ecKey; michael@0: clntPubKey.u.ec.DEREncodedParams.len = michael@0: srvrPubKey->u.ec.DEREncodedParams.len; michael@0: clntPubKey.u.ec.DEREncodedParams.data = michael@0: srvrPubKey->u.ec.DEREncodedParams.data; michael@0: michael@0: rv = ssl3_ConsumeHandshakeVariable(ss, &clntPubKey.u.ec.publicValue, michael@0: 1, &b, &length); michael@0: if (rv != SECSuccess) { michael@0: SEND_ALERT michael@0: return SECFailure; /* XXX Who sets the error code?? */ michael@0: } michael@0: michael@0: isTLS = (PRBool)(ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0); michael@0: isTLS12 = (PRBool)(ss->ssl3.prSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2); michael@0: michael@0: if (isTLS12) { michael@0: target = CKM_NSS_TLS_MASTER_KEY_DERIVE_DH_SHA256; michael@0: } else if (isTLS) { michael@0: target = CKM_TLS_MASTER_KEY_DERIVE_DH; michael@0: } else { michael@0: target = CKM_SSL3_MASTER_KEY_DERIVE_DH; michael@0: } michael@0: michael@0: /* Determine the PMS */ michael@0: pms = PK11_PubDeriveWithKDF(srvrPrivKey, &clntPubKey, PR_FALSE, NULL, NULL, michael@0: CKM_ECDH1_DERIVE, target, CKA_DERIVE, 0, michael@0: CKD_NULL, NULL, NULL); michael@0: michael@0: if (pms == NULL) { michael@0: /* last gasp. */ michael@0: ssl_MapLowLevelError(SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE); michael@0: return SECFailure; michael@0: } michael@0: michael@0: rv = ssl3_InitPendingCipherSpec(ss, pms); michael@0: PK11_FreeSymKey(pms); michael@0: if (rv != SECSuccess) { michael@0: SEND_ALERT michael@0: return SECFailure; /* error code set by ssl3_InitPendingCipherSpec */ michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: ECName michael@0: ssl3_GetCurveWithECKeyStrength(PRUint32 curvemsk, int requiredECCbits) michael@0: { michael@0: int i; michael@0: michael@0: for ( i = 0; bits2curve[i].curve != ec_noName; i++) { michael@0: if (bits2curve[i].bits < requiredECCbits) michael@0: continue; michael@0: if (SSL_IS_CURVE_NEGOTIATED(curvemsk, bits2curve[i].curve)) { michael@0: return bits2curve[i].curve; michael@0: } michael@0: } michael@0: PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP); michael@0: return ec_noName; michael@0: } michael@0: michael@0: /* find the "weakest link". Get strength of signature key and of sym key. michael@0: * choose curve for the weakest of those two. michael@0: */ michael@0: ECName michael@0: ssl3_GetCurveNameForServerSocket(sslSocket *ss) michael@0: { michael@0: SECKEYPublicKey * svrPublicKey = NULL; michael@0: ECName ec_curve = ec_noName; michael@0: int signatureKeyStrength = 521; michael@0: int requiredECCbits = ss->sec.secretKeyBits * 2; michael@0: michael@0: if (ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa) { michael@0: svrPublicKey = SSL_GET_SERVER_PUBLIC_KEY(ss, kt_ecdh); michael@0: if (svrPublicKey) michael@0: ec_curve = params2ecName(&svrPublicKey->u.ec.DEREncodedParams); michael@0: if (!SSL_IS_CURVE_NEGOTIATED(ss->ssl3.hs.negotiatedECCurves, ec_curve)) { michael@0: PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP); michael@0: return ec_noName; michael@0: } michael@0: signatureKeyStrength = curve2bits[ ec_curve ]; michael@0: } else { michael@0: /* RSA is our signing cert */ michael@0: int serverKeyStrengthInBits; michael@0: michael@0: svrPublicKey = SSL_GET_SERVER_PUBLIC_KEY(ss, kt_rsa); michael@0: if (!svrPublicKey) { michael@0: PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP); michael@0: return ec_noName; michael@0: } michael@0: michael@0: /* currently strength in bytes */ michael@0: serverKeyStrengthInBits = svrPublicKey->u.rsa.modulus.len; michael@0: if (svrPublicKey->u.rsa.modulus.data[0] == 0) { michael@0: serverKeyStrengthInBits--; michael@0: } michael@0: /* convert to strength in bits */ michael@0: serverKeyStrengthInBits *= BPB; michael@0: michael@0: signatureKeyStrength = michael@0: SSL_RSASTRENGTH_TO_ECSTRENGTH(serverKeyStrengthInBits); michael@0: } michael@0: if ( requiredECCbits > signatureKeyStrength ) michael@0: requiredECCbits = signatureKeyStrength; michael@0: michael@0: return ssl3_GetCurveWithECKeyStrength(ss->ssl3.hs.negotiatedECCurves, michael@0: requiredECCbits); michael@0: } michael@0: michael@0: /* function to clear out the lists */ michael@0: static SECStatus michael@0: ssl3_ShutdownECDHECurves(void *appData, void *nssData) michael@0: { michael@0: int i; michael@0: ECDHEKeyPair *keyPair = &gECDHEKeyPairs[0]; michael@0: michael@0: for (i=0; i < ec_pastLastName; i++, keyPair++) { michael@0: if (keyPair->pair) { michael@0: ssl3_FreeKeyPair(keyPair->pair); michael@0: } michael@0: } michael@0: memset(gECDHEKeyPairs, 0, sizeof gECDHEKeyPairs); michael@0: return SECSuccess; michael@0: } michael@0: michael@0: static PRStatus michael@0: ssl3_ECRegister(void) michael@0: { michael@0: SECStatus rv; michael@0: rv = NSS_RegisterShutdown(ssl3_ShutdownECDHECurves, gECDHEKeyPairs); michael@0: if (rv != SECSuccess) { michael@0: gECDHEKeyPairs[ec_noName].error = PORT_GetError(); michael@0: } michael@0: return (PRStatus)rv; michael@0: } michael@0: michael@0: /* CallOnce function, called once for each named curve. */ michael@0: static PRStatus michael@0: ssl3_CreateECDHEphemeralKeyPair(void * arg) michael@0: { michael@0: SECKEYPrivateKey * privKey = NULL; michael@0: SECKEYPublicKey * pubKey = NULL; michael@0: ssl3KeyPair * keyPair = NULL; michael@0: ECName ec_curve = (ECName)arg; michael@0: SECKEYECParams ecParams = { siBuffer, NULL, 0 }; michael@0: michael@0: PORT_Assert(gECDHEKeyPairs[ec_curve].pair == NULL); michael@0: michael@0: /* ok, no one has generated a global key for this curve yet, do so */ michael@0: if (ssl3_ECName2Params(NULL, ec_curve, &ecParams) != SECSuccess) { michael@0: gECDHEKeyPairs[ec_curve].error = PORT_GetError(); michael@0: return PR_FAILURE; michael@0: } michael@0: michael@0: privKey = SECKEY_CreateECPrivateKey(&ecParams, &pubKey, NULL); michael@0: SECITEM_FreeItem(&ecParams, PR_FALSE); michael@0: michael@0: if (!privKey || !pubKey || !(keyPair = ssl3_NewKeyPair(privKey, pubKey))) { michael@0: if (privKey) { michael@0: SECKEY_DestroyPrivateKey(privKey); michael@0: } michael@0: if (pubKey) { michael@0: SECKEY_DestroyPublicKey(pubKey); michael@0: } michael@0: ssl_MapLowLevelError(SEC_ERROR_KEYGEN_FAIL); michael@0: gECDHEKeyPairs[ec_curve].error = PORT_GetError(); michael@0: return PR_FAILURE; michael@0: } michael@0: michael@0: gECDHEKeyPairs[ec_curve].pair = keyPair; michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: /* michael@0: * Creates the ephemeral public and private ECDH keys used by michael@0: * server in ECDHE_RSA and ECDHE_ECDSA handshakes. michael@0: * For now, the elliptic curve is chosen to be the same michael@0: * strength as the signing certificate (ECC or RSA). michael@0: * We need an API to specify the curve. This won't be a real michael@0: * issue until we further develop server-side support for ECC michael@0: * cipher suites. michael@0: */ michael@0: static SECStatus michael@0: ssl3_CreateECDHEphemeralKeys(sslSocket *ss, ECName ec_curve) michael@0: { michael@0: ssl3KeyPair * keyPair = NULL; michael@0: michael@0: /* if there's no global key for this curve, make one. */ michael@0: if (gECDHEKeyPairs[ec_curve].pair == NULL) { michael@0: PRStatus status; michael@0: michael@0: status = PR_CallOnce(&gECDHEKeyPairs[ec_noName].once, ssl3_ECRegister); michael@0: if (status != PR_SUCCESS) { michael@0: PORT_SetError(gECDHEKeyPairs[ec_noName].error); michael@0: return SECFailure; michael@0: } michael@0: status = PR_CallOnceWithArg(&gECDHEKeyPairs[ec_curve].once, michael@0: ssl3_CreateECDHEphemeralKeyPair, michael@0: (void *)ec_curve); michael@0: if (status != PR_SUCCESS) { michael@0: PORT_SetError(gECDHEKeyPairs[ec_curve].error); michael@0: return SECFailure; michael@0: } michael@0: } michael@0: michael@0: keyPair = gECDHEKeyPairs[ec_curve].pair; michael@0: PORT_Assert(keyPair != NULL); michael@0: if (!keyPair) michael@0: return SECFailure; michael@0: ss->ephemeralECDHKeyPair = ssl3_GetKeyPairRef(keyPair); michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: SECStatus michael@0: ssl3_HandleECDHServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length) michael@0: { michael@0: PLArenaPool * arena = NULL; michael@0: SECKEYPublicKey *peerKey = NULL; michael@0: PRBool isTLS, isTLS12; michael@0: SECStatus rv; michael@0: int errCode = SSL_ERROR_RX_MALFORMED_SERVER_KEY_EXCH; michael@0: SSL3AlertDescription desc = illegal_parameter; michael@0: SSL3Hashes hashes; michael@0: SECItem signature = {siBuffer, NULL, 0}; michael@0: michael@0: SECItem ec_params = {siBuffer, NULL, 0}; michael@0: SECItem ec_point = {siBuffer, NULL, 0}; michael@0: unsigned char paramBuf[3]; /* only for curve_type == named_curve */ michael@0: SSL3SignatureAndHashAlgorithm sigAndHash; michael@0: michael@0: sigAndHash.hashAlg = SEC_OID_UNKNOWN; michael@0: michael@0: isTLS = (PRBool)(ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0); michael@0: isTLS12 = (PRBool)(ss->ssl3.prSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2); michael@0: michael@0: /* XXX This works only for named curves, revisit this when michael@0: * we support generic curves. michael@0: */ michael@0: ec_params.len = sizeof paramBuf; michael@0: ec_params.data = paramBuf; michael@0: rv = ssl3_ConsumeHandshake(ss, ec_params.data, ec_params.len, &b, &length); michael@0: if (rv != SECSuccess) { michael@0: goto loser; /* malformed. */ michael@0: } michael@0: michael@0: /* Fail if the curve is not a named curve */ michael@0: if ((ec_params.data[0] != ec_type_named) || michael@0: (ec_params.data[1] != 0) || michael@0: !supportedCurve(ec_params.data[2])) { michael@0: errCode = SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE; michael@0: desc = handshake_failure; michael@0: goto alert_loser; michael@0: } michael@0: michael@0: rv = ssl3_ConsumeHandshakeVariable(ss, &ec_point, 1, &b, &length); michael@0: if (rv != SECSuccess) { michael@0: goto loser; /* malformed. */ michael@0: } michael@0: /* Fail if the ec point uses compressed representation */ michael@0: if (ec_point.data[0] != EC_POINT_FORM_UNCOMPRESSED) { michael@0: errCode = SEC_ERROR_UNSUPPORTED_EC_POINT_FORM; michael@0: desc = handshake_failure; michael@0: goto alert_loser; michael@0: } michael@0: michael@0: if (isTLS12) { michael@0: rv = ssl3_ConsumeSignatureAndHashAlgorithm(ss, &b, &length, michael@0: &sigAndHash); michael@0: if (rv != SECSuccess) { michael@0: goto loser; /* malformed or unsupported. */ michael@0: } michael@0: rv = ssl3_CheckSignatureAndHashAlgorithmConsistency( michael@0: &sigAndHash, ss->sec.peerCert); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: rv = ssl3_ConsumeHandshakeVariable(ss, &signature, 2, &b, &length); michael@0: if (rv != SECSuccess) { michael@0: goto loser; /* malformed. */ michael@0: } michael@0: michael@0: if (length != 0) { michael@0: if (isTLS) michael@0: desc = decode_error; michael@0: goto alert_loser; /* malformed. */ michael@0: } michael@0: michael@0: PRINT_BUF(60, (NULL, "Server EC params", ec_params.data, michael@0: ec_params.len)); michael@0: PRINT_BUF(60, (NULL, "Server EC point", ec_point.data, ec_point.len)); michael@0: michael@0: /* failures after this point are not malformed handshakes. */ michael@0: /* TLS: send decrypt_error if signature failed. */ michael@0: desc = isTLS ? decrypt_error : handshake_failure; michael@0: michael@0: /* michael@0: * check to make sure the hash is signed by right guy michael@0: */ michael@0: rv = ssl3_ComputeECDHKeyHash(sigAndHash.hashAlg, ec_params, ec_point, michael@0: &ss->ssl3.hs.client_random, michael@0: &ss->ssl3.hs.server_random, michael@0: &hashes, ss->opt.bypassPKCS11); michael@0: michael@0: if (rv != SECSuccess) { michael@0: errCode = michael@0: ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE); michael@0: goto alert_loser; michael@0: } michael@0: rv = ssl3_VerifySignedHashes(&hashes, ss->sec.peerCert, &signature, michael@0: isTLS, ss->pkcs11PinArg); michael@0: if (rv != SECSuccess) { michael@0: errCode = michael@0: ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE); michael@0: goto alert_loser; michael@0: } michael@0: michael@0: arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); michael@0: if (arena == NULL) { michael@0: goto no_memory; michael@0: } michael@0: michael@0: ss->sec.peerKey = peerKey = PORT_ArenaZNew(arena, SECKEYPublicKey); michael@0: if (peerKey == NULL) { michael@0: goto no_memory; michael@0: } michael@0: michael@0: peerKey->arena = arena; michael@0: peerKey->keyType = ecKey; michael@0: michael@0: /* set up EC parameters in peerKey */ michael@0: if (ssl3_ECName2Params(arena, ec_params.data[2], michael@0: &peerKey->u.ec.DEREncodedParams) != SECSuccess) { michael@0: /* we should never get here since we already michael@0: * checked that we are dealing with a supported curve michael@0: */ michael@0: errCode = SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE; michael@0: goto alert_loser; michael@0: } michael@0: michael@0: /* copy publicValue in peerKey */ michael@0: if (SECITEM_CopyItem(arena, &peerKey->u.ec.publicValue, &ec_point)) michael@0: { michael@0: PORT_FreeArena(arena, PR_FALSE); michael@0: goto no_memory; michael@0: } michael@0: peerKey->pkcs11Slot = NULL; michael@0: peerKey->pkcs11ID = CK_INVALID_HANDLE; michael@0: michael@0: ss->sec.peerKey = peerKey; michael@0: ss->ssl3.hs.ws = wait_cert_request; michael@0: michael@0: return SECSuccess; michael@0: michael@0: alert_loser: michael@0: (void)SSL3_SendAlert(ss, alert_fatal, desc); michael@0: loser: michael@0: PORT_SetError( errCode ); michael@0: return SECFailure; michael@0: michael@0: no_memory: /* no-memory error has already been set. */ michael@0: ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE); michael@0: return SECFailure; michael@0: } michael@0: michael@0: SECStatus michael@0: ssl3_SendECDHServerKeyExchange( michael@0: sslSocket *ss, michael@0: const SSL3SignatureAndHashAlgorithm *sigAndHash) michael@0: { michael@0: const ssl3KEADef * kea_def = ss->ssl3.hs.kea_def; michael@0: SECStatus rv = SECFailure; michael@0: int length; michael@0: PRBool isTLS, isTLS12; michael@0: SECItem signed_hash = {siBuffer, NULL, 0}; michael@0: SSL3Hashes hashes; michael@0: michael@0: SECKEYPublicKey * ecdhePub; michael@0: SECItem ec_params = {siBuffer, NULL, 0}; michael@0: unsigned char paramBuf[3]; michael@0: ECName curve; michael@0: SSL3KEAType certIndex; michael@0: michael@0: /* Generate ephemeral ECDH key pair and send the public key */ michael@0: curve = ssl3_GetCurveNameForServerSocket(ss); michael@0: if (curve == ec_noName) { michael@0: goto loser; michael@0: } michael@0: rv = ssl3_CreateECDHEphemeralKeys(ss, curve); michael@0: if (rv != SECSuccess) { michael@0: goto loser; /* err set by AppendHandshake. */ michael@0: } michael@0: ecdhePub = ss->ephemeralECDHKeyPair->pubKey; michael@0: PORT_Assert(ecdhePub != NULL); michael@0: if (!ecdhePub) { michael@0: PORT_SetError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE); michael@0: return SECFailure; michael@0: } michael@0: michael@0: ec_params.len = sizeof paramBuf; michael@0: ec_params.data = paramBuf; michael@0: curve = params2ecName(&ecdhePub->u.ec.DEREncodedParams); michael@0: if (curve != ec_noName) { michael@0: ec_params.data[0] = ec_type_named; michael@0: ec_params.data[1] = 0x00; michael@0: ec_params.data[2] = curve; michael@0: } else { michael@0: PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE); michael@0: goto loser; michael@0: } michael@0: michael@0: rv = ssl3_ComputeECDHKeyHash(sigAndHash->hashAlg, michael@0: ec_params, michael@0: ecdhePub->u.ec.publicValue, michael@0: &ss->ssl3.hs.client_random, michael@0: &ss->ssl3.hs.server_random, michael@0: &hashes, ss->opt.bypassPKCS11); michael@0: if (rv != SECSuccess) { michael@0: ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE); michael@0: goto loser; michael@0: } michael@0: michael@0: isTLS = (PRBool)(ss->ssl3.pwSpec->version > SSL_LIBRARY_VERSION_3_0); michael@0: isTLS12 = (PRBool)(ss->ssl3.pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2); michael@0: michael@0: /* XXX SSLKEAType isn't really a good choice for michael@0: * indexing certificates but that's all we have michael@0: * for now. michael@0: */ michael@0: if (kea_def->kea == kea_ecdhe_rsa) michael@0: certIndex = kt_rsa; michael@0: else /* kea_def->kea == kea_ecdhe_ecdsa */ michael@0: certIndex = kt_ecdh; michael@0: michael@0: rv = ssl3_SignHashes(&hashes, ss->serverCerts[certIndex].SERVERKEY, michael@0: &signed_hash, isTLS); michael@0: if (rv != SECSuccess) { michael@0: goto loser; /* ssl3_SignHashes has set err. */ michael@0: } michael@0: if (signed_hash.data == NULL) { michael@0: /* how can this happen and rv == SECSuccess ?? */ michael@0: PORT_SetError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE); michael@0: goto loser; michael@0: } michael@0: michael@0: length = ec_params.len + michael@0: 1 + ecdhePub->u.ec.publicValue.len + michael@0: (isTLS12 ? 2 : 0) + 2 + signed_hash.len; michael@0: michael@0: rv = ssl3_AppendHandshakeHeader(ss, server_key_exchange, length); michael@0: if (rv != SECSuccess) { michael@0: goto loser; /* err set by AppendHandshake. */ michael@0: } michael@0: michael@0: rv = ssl3_AppendHandshake(ss, ec_params.data, ec_params.len); michael@0: if (rv != SECSuccess) { michael@0: goto loser; /* err set by AppendHandshake. */ michael@0: } michael@0: michael@0: rv = ssl3_AppendHandshakeVariable(ss, ecdhePub->u.ec.publicValue.data, michael@0: ecdhePub->u.ec.publicValue.len, 1); michael@0: if (rv != SECSuccess) { michael@0: goto loser; /* err set by AppendHandshake. */ michael@0: } michael@0: michael@0: if (isTLS12) { michael@0: rv = ssl3_AppendSignatureAndHashAlgorithm(ss, sigAndHash); michael@0: if (rv != SECSuccess) { michael@0: goto loser; /* err set by AppendHandshake. */ michael@0: } michael@0: } michael@0: michael@0: rv = ssl3_AppendHandshakeVariable(ss, signed_hash.data, michael@0: signed_hash.len, 2); michael@0: if (rv != SECSuccess) { michael@0: goto loser; /* err set by AppendHandshake. */ michael@0: } michael@0: michael@0: PORT_Free(signed_hash.data); michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: if (signed_hash.data != NULL) michael@0: PORT_Free(signed_hash.data); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* Lists of ECC cipher suites for searching and disabling. */ michael@0: michael@0: static const ssl3CipherSuite ecdh_suites[] = { michael@0: TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, michael@0: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, michael@0: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, michael@0: TLS_ECDH_ECDSA_WITH_NULL_SHA, michael@0: TLS_ECDH_ECDSA_WITH_RC4_128_SHA, michael@0: TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, michael@0: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, michael@0: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, michael@0: TLS_ECDH_RSA_WITH_NULL_SHA, michael@0: TLS_ECDH_RSA_WITH_RC4_128_SHA, michael@0: 0 /* end of list marker */ michael@0: }; michael@0: michael@0: static const ssl3CipherSuite ecdh_ecdsa_suites[] = { michael@0: TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, michael@0: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, michael@0: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, michael@0: TLS_ECDH_ECDSA_WITH_NULL_SHA, michael@0: TLS_ECDH_ECDSA_WITH_RC4_128_SHA, michael@0: 0 /* end of list marker */ michael@0: }; michael@0: michael@0: static const ssl3CipherSuite ecdh_rsa_suites[] = { michael@0: TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, michael@0: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, michael@0: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, michael@0: TLS_ECDH_RSA_WITH_NULL_SHA, michael@0: TLS_ECDH_RSA_WITH_RC4_128_SHA, michael@0: 0 /* end of list marker */ michael@0: }; michael@0: michael@0: static const ssl3CipherSuite ecdhe_ecdsa_suites[] = { michael@0: TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, michael@0: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, michael@0: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, michael@0: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, michael@0: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, michael@0: TLS_ECDHE_ECDSA_WITH_NULL_SHA, michael@0: TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, michael@0: 0 /* end of list marker */ michael@0: }; michael@0: michael@0: static const ssl3CipherSuite ecdhe_rsa_suites[] = { michael@0: TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, michael@0: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, michael@0: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, michael@0: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, michael@0: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, michael@0: TLS_ECDHE_RSA_WITH_NULL_SHA, michael@0: TLS_ECDHE_RSA_WITH_RC4_128_SHA, michael@0: 0 /* end of list marker */ michael@0: }; michael@0: michael@0: /* List of all ECC cipher suites */ michael@0: static const ssl3CipherSuite ecSuites[] = { michael@0: TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, michael@0: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, michael@0: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, michael@0: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, michael@0: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, michael@0: TLS_ECDHE_ECDSA_WITH_NULL_SHA, michael@0: TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, michael@0: TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, michael@0: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, michael@0: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, michael@0: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, michael@0: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, michael@0: TLS_ECDHE_RSA_WITH_NULL_SHA, michael@0: TLS_ECDHE_RSA_WITH_RC4_128_SHA, michael@0: TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, michael@0: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, michael@0: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, michael@0: TLS_ECDH_ECDSA_WITH_NULL_SHA, michael@0: TLS_ECDH_ECDSA_WITH_RC4_128_SHA, michael@0: TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, michael@0: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, michael@0: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, michael@0: TLS_ECDH_RSA_WITH_NULL_SHA, michael@0: TLS_ECDH_RSA_WITH_RC4_128_SHA, michael@0: 0 /* end of list marker */ michael@0: }; michael@0: michael@0: /* On this socket, Disable the ECC cipher suites in the argument's list */ michael@0: SECStatus michael@0: ssl3_DisableECCSuites(sslSocket * ss, const ssl3CipherSuite * suite) michael@0: { michael@0: if (!suite) michael@0: suite = ecSuites; michael@0: for (; *suite; ++suite) { michael@0: SECStatus rv = ssl3_CipherPrefSet(ss, *suite, PR_FALSE); michael@0: michael@0: PORT_Assert(rv == SECSuccess); /* else is coding error */ michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* Look at the server certs configured on this socket, and disable any michael@0: * ECC cipher suites that are not supported by those certs. michael@0: */ michael@0: void michael@0: ssl3_FilterECCipherSuitesByServerCerts(sslSocket * ss) michael@0: { michael@0: CERTCertificate * svrCert; michael@0: michael@0: svrCert = ss->serverCerts[kt_rsa].serverCert; michael@0: if (!svrCert) { michael@0: ssl3_DisableECCSuites(ss, ecdhe_rsa_suites); michael@0: } michael@0: michael@0: svrCert = ss->serverCerts[kt_ecdh].serverCert; michael@0: if (!svrCert) { michael@0: ssl3_DisableECCSuites(ss, ecdh_suites); michael@0: ssl3_DisableECCSuites(ss, ecdhe_ecdsa_suites); michael@0: } else { michael@0: SECOidTag sigTag = SECOID_GetAlgorithmTag(&svrCert->signature); michael@0: michael@0: switch (sigTag) { michael@0: case SEC_OID_PKCS1_RSA_ENCRYPTION: michael@0: case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION: michael@0: case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION: michael@0: case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: michael@0: case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION: michael@0: case SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION: michael@0: case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION: michael@0: case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION: michael@0: case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION: michael@0: ssl3_DisableECCSuites(ss, ecdh_ecdsa_suites); michael@0: break; michael@0: case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE: michael@0: case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE: michael@0: case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE: michael@0: case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE: michael@0: case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE: michael@0: case SEC_OID_ANSIX962_ECDSA_SIGNATURE_RECOMMENDED_DIGEST: michael@0: case SEC_OID_ANSIX962_ECDSA_SIGNATURE_SPECIFIED_DIGEST: michael@0: ssl3_DisableECCSuites(ss, ecdh_rsa_suites); michael@0: break; michael@0: default: michael@0: ssl3_DisableECCSuites(ss, ecdh_suites); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* Ask: is ANY ECC cipher suite enabled on this socket? */ michael@0: /* Order(N^2). Yuk. Also, this ignores export policy. */ michael@0: PRBool michael@0: ssl3_IsECCEnabled(sslSocket * ss) michael@0: { michael@0: const ssl3CipherSuite * suite; michael@0: PK11SlotInfo *slot; michael@0: michael@0: /* make sure we can do ECC */ michael@0: slot = PK11_GetBestSlot(CKM_ECDH1_DERIVE, ss->pkcs11PinArg); michael@0: if (!slot) { michael@0: return PR_FALSE; michael@0: } michael@0: PK11_FreeSlot(slot); michael@0: michael@0: /* make sure an ECC cipher is enabled */ michael@0: for (suite = ecSuites; *suite; ++suite) { michael@0: PRBool enabled = PR_FALSE; michael@0: SECStatus rv = ssl3_CipherPrefGet(ss, *suite, &enabled); michael@0: michael@0: PORT_Assert(rv == SECSuccess); /* else is coding error */ michael@0: if (rv == SECSuccess && enabled) michael@0: return PR_TRUE; michael@0: } michael@0: return PR_FALSE; michael@0: } michael@0: michael@0: #define BE(n) 0, n michael@0: michael@0: /* Prefabricated TLS client hello extension, Elliptic Curves List, michael@0: * offers only 3 curves, the Suite B curves, 23-25 michael@0: */ michael@0: static const PRUint8 suiteBECList[12] = { michael@0: BE(10), /* Extension type */ michael@0: BE( 8), /* octets that follow ( 3 pairs + 1 length pair) */ michael@0: BE( 6), /* octets that follow ( 3 pairs) */ michael@0: BE(23), BE(24), BE(25) michael@0: }; michael@0: michael@0: /* Prefabricated TLS client hello extension, Elliptic Curves List, michael@0: * offers curves 1-25. michael@0: */ michael@0: static const PRUint8 tlsECList[56] = { michael@0: BE(10), /* Extension type */ michael@0: BE(52), /* octets that follow (25 pairs + 1 length pair) */ michael@0: BE(50), /* octets that follow (25 pairs) */ michael@0: BE( 1), BE( 2), BE( 3), BE( 4), BE( 5), BE( 6), BE( 7), michael@0: BE( 8), BE( 9), BE(10), BE(11), BE(12), BE(13), BE(14), BE(15), michael@0: BE(16), BE(17), BE(18), BE(19), BE(20), BE(21), BE(22), BE(23), michael@0: BE(24), BE(25) michael@0: }; michael@0: michael@0: static const PRUint8 ecPtFmt[6] = { michael@0: BE(11), /* Extension type */ michael@0: BE( 2), /* octets that follow */ michael@0: 1, /* octets that follow */ michael@0: 0 /* uncompressed type only */ michael@0: }; michael@0: michael@0: /* This function already presumes we can do ECC, ssl3_IsECCEnabled must be michael@0: * called before this function. It looks to see if we have a token which michael@0: * is capable of doing smaller than SuiteB curves. If the token can, we michael@0: * presume the token can do the whole SSL suite of curves. If it can't we michael@0: * presume the token that allowed ECC to be enabled can only do suite B michael@0: * curves. */ michael@0: static PRBool michael@0: ssl3_SuiteBOnly(sslSocket *ss) michael@0: { michael@0: /* See if we can support small curves (like 163). If not, assume we can michael@0: * only support Suite-B curves (P-256, P-384, P-521). */ michael@0: PK11SlotInfo *slot = michael@0: PK11_GetBestSlotWithAttributes(CKM_ECDH1_DERIVE, 0, 163, michael@0: ss ? ss->pkcs11PinArg : NULL); michael@0: michael@0: if (!slot) { michael@0: /* nope, presume we can only do suite B */ michael@0: return PR_TRUE; michael@0: } michael@0: /* we can, presume we can do all curves */ michael@0: PK11_FreeSlot(slot); michael@0: return PR_FALSE; michael@0: } michael@0: michael@0: /* Send our "canned" (precompiled) Supported Elliptic Curves extension, michael@0: * which says that we support all TLS-defined named curves. michael@0: */ michael@0: PRInt32 michael@0: ssl3_SendSupportedCurvesXtn( michael@0: sslSocket * ss, michael@0: PRBool append, michael@0: PRUint32 maxBytes) michael@0: { michael@0: PRInt32 ecListSize = 0; michael@0: const PRUint8 *ecList = NULL; michael@0: michael@0: if (!ss || !ssl3_IsECCEnabled(ss)) michael@0: return 0; michael@0: michael@0: if (ssl3_SuiteBOnly(ss)) { michael@0: ecListSize = sizeof suiteBECList; michael@0: ecList = suiteBECList; michael@0: } else { michael@0: ecListSize = sizeof tlsECList; michael@0: ecList = tlsECList; michael@0: } michael@0: michael@0: if (append && maxBytes >= ecListSize) { michael@0: SECStatus rv = ssl3_AppendHandshake(ss, ecList, ecListSize); michael@0: if (rv != SECSuccess) michael@0: return -1; michael@0: if (!ss->sec.isServer) { michael@0: TLSExtensionData *xtnData = &ss->xtnData; michael@0: xtnData->advertised[xtnData->numAdvertised++] = michael@0: ssl_elliptic_curves_xtn; michael@0: } michael@0: } michael@0: return ecListSize; michael@0: } michael@0: michael@0: PRUint32 michael@0: ssl3_GetSupportedECCurveMask(sslSocket *ss) michael@0: { michael@0: if (ssl3_SuiteBOnly(ss)) { michael@0: return SSL3_SUITE_B_SUPPORTED_CURVES_MASK; michael@0: } michael@0: return SSL3_ALL_SUPPORTED_CURVES_MASK; michael@0: } michael@0: michael@0: /* Send our "canned" (precompiled) Supported Point Formats extension, michael@0: * which says that we only support uncompressed points. michael@0: */ michael@0: PRInt32 michael@0: ssl3_SendSupportedPointFormatsXtn( michael@0: sslSocket * ss, michael@0: PRBool append, michael@0: PRUint32 maxBytes) michael@0: { michael@0: if (!ss || !ssl3_IsECCEnabled(ss)) michael@0: return 0; michael@0: if (append && maxBytes >= (sizeof ecPtFmt)) { michael@0: SECStatus rv = ssl3_AppendHandshake(ss, ecPtFmt, (sizeof ecPtFmt)); michael@0: if (rv != SECSuccess) michael@0: return -1; michael@0: if (!ss->sec.isServer) { michael@0: TLSExtensionData *xtnData = &ss->xtnData; michael@0: xtnData->advertised[xtnData->numAdvertised++] = michael@0: ssl_ec_point_formats_xtn; michael@0: } michael@0: } michael@0: return (sizeof ecPtFmt); michael@0: } michael@0: michael@0: /* Just make sure that the remote client supports uncompressed points, michael@0: * Since that is all we support. Disable ECC cipher suites if it doesn't. michael@0: */ michael@0: SECStatus michael@0: ssl3_HandleSupportedPointFormatsXtn(sslSocket *ss, PRUint16 ex_type, michael@0: SECItem *data) michael@0: { michael@0: int i; michael@0: michael@0: if (data->len < 2 || data->len > 255 || !data->data || michael@0: data->len != (unsigned int)data->data[0] + 1) { michael@0: /* malformed */ michael@0: goto loser; michael@0: } michael@0: for (i = data->len; --i > 0; ) { michael@0: if (data->data[i] == 0) { michael@0: /* indicate that we should send a reply */ michael@0: SECStatus rv; michael@0: rv = ssl3_RegisterServerHelloExtensionSender(ss, ex_type, michael@0: &ssl3_SendSupportedPointFormatsXtn); michael@0: return rv; michael@0: } michael@0: } michael@0: loser: michael@0: /* evil client doesn't support uncompressed */ michael@0: ssl3_DisableECCSuites(ss, ecSuites); michael@0: return SECFailure; michael@0: } michael@0: michael@0: michael@0: #define SSL3_GET_SERVER_PUBLICKEY(sock, type) \ michael@0: (ss->serverCerts[type].serverKeyPair ? \ michael@0: ss->serverCerts[type].serverKeyPair->pubKey : NULL) michael@0: michael@0: /* Extract the TLS curve name for the public key in our EC server cert. */ michael@0: ECName ssl3_GetSvrCertCurveName(sslSocket *ss) michael@0: { michael@0: SECKEYPublicKey *srvPublicKey; michael@0: ECName ec_curve = ec_noName; michael@0: michael@0: srvPublicKey = SSL3_GET_SERVER_PUBLICKEY(ss, kt_ecdh); michael@0: if (srvPublicKey) { michael@0: ec_curve = params2ecName(&srvPublicKey->u.ec.DEREncodedParams); michael@0: } michael@0: return ec_curve; michael@0: } michael@0: michael@0: /* Ensure that the curve in our server cert is one of the ones suppored michael@0: * by the remote client, and disable all ECC cipher suites if not. michael@0: */ michael@0: SECStatus michael@0: ssl3_HandleSupportedCurvesXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) michael@0: { michael@0: PRInt32 list_len; michael@0: PRUint32 peerCurves = 0; michael@0: PRUint32 mutualCurves = 0; michael@0: PRUint16 svrCertCurveName; michael@0: michael@0: if (!data->data || data->len < 4 || data->len > 65535) michael@0: goto loser; michael@0: /* get the length of elliptic_curve_list */ michael@0: list_len = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); michael@0: if (list_len < 0 || data->len != list_len || (data->len % 2) != 0) { michael@0: /* malformed */ michael@0: goto loser; michael@0: } michael@0: /* build bit vector of peer's supported curve names */ michael@0: while (data->len) { michael@0: PRInt32 curve_name = michael@0: ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); michael@0: if (curve_name > ec_noName && curve_name < ec_pastLastName) { michael@0: peerCurves |= (1U << curve_name); michael@0: } michael@0: } michael@0: /* What curves do we support in common? */ michael@0: mutualCurves = ss->ssl3.hs.negotiatedECCurves &= peerCurves; michael@0: if (!mutualCurves) { /* no mutually supported EC Curves */ michael@0: goto loser; michael@0: } michael@0: michael@0: /* if our ECC cert doesn't use one of these supported curves, michael@0: * disable ECC cipher suites that require an ECC cert. michael@0: */ michael@0: svrCertCurveName = ssl3_GetSvrCertCurveName(ss); michael@0: if (svrCertCurveName != ec_noName && michael@0: (mutualCurves & (1U << svrCertCurveName)) != 0) { michael@0: return SECSuccess; michael@0: } michael@0: /* Our EC cert doesn't contain a mutually supported curve. michael@0: * Disable all ECC cipher suites that require an EC cert michael@0: */ michael@0: ssl3_DisableECCSuites(ss, ecdh_ecdsa_suites); michael@0: ssl3_DisableECCSuites(ss, ecdhe_ecdsa_suites); michael@0: return SECFailure; michael@0: michael@0: loser: michael@0: /* no common curve supported */ michael@0: ssl3_DisableECCSuites(ss, ecSuites); michael@0: return SECFailure; michael@0: } michael@0: michael@0: #endif /* NSS_DISABLE_ECC */