static SECStatus Null_Cipher(void *ctx, unsigned char *output, int *outputLen,
int maxOutputLen, const unsigned char *input,
int inputLen);
+#ifndef NO_PKCS11_BYPASS
+static SECStatus ssl3_AESGCMBypass(ssl3KeyMaterial *keys, PRBool doDecrypt,
+ unsigned char *out, int *outlen, int maxout,
+ const unsigned char *in, int inlen,
+ const unsigned char *additionalData,
+ int additionalDataLen);
+#endif
#define MAX_SEND_BUF_LENGTH 32000 /* watch for 16-bit integer overflow */
#define MIN_SEND_BUF_LENGTH 4000
* precedence (desirability). It only includes cipher suites we implement.
* This table is modified by SSL3_SetPolicy(). The ordering of cipher suites
* in this table must match the ordering in SSL_ImplementedCiphers (sslenum.c)
+ *
+ * Important: See bug 946147 before enabling, reordering, or adding any cipher
+ * suites to this list.
*/
static ssl3CipherSuiteCfg cipherSuites[ssl_V3_SUITES_IMPLEMENTED] = {
- /* cipher_suite policy enabled is_present*/
-#ifdef NSS_ENABLE_ECC
- { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
-#endif /* NSS_ENABLE_ECC */
- { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_DHE_RSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
- { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
- { TLS_DHE_DSS_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
-#ifdef NSS_ENABLE_ECC
- { TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
-#endif /* NSS_ENABLE_ECC */
- { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_RSA_WITH_AES_256_CBC_SHA, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
- { TLS_RSA_WITH_AES_256_CBC_SHA256, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
+ /* cipher_suite policy enabled isPresent */
#ifdef NSS_ENABLE_ECC
- { TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDHE_RSA_WITH_RC4_128_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
-#endif /* NSS_ENABLE_ECC */
- { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_DHE_DSS_WITH_RC4_128_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_DHE_RSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
- { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
- { TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
-#ifdef NSS_ENABLE_ECC
- { TLS_ECDH_RSA_WITH_RC4_128_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDH_ECDSA_WITH_RC4_128_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
+ { TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ /* TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA is out of order to work around
+ * bug 946147.
+ */
+ { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_RSA_WITH_RC4_128_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
#endif /* NSS_ENABLE_ECC */
- { TLS_RSA_WITH_SEED_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { SSL_RSA_WITH_RC4_128_SHA, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
- { SSL_RSA_WITH_RC4_128_MD5, SSL_NOT_ALLOWED, PR_TRUE, PR_FALSE},
- { TLS_RSA_WITH_AES_128_CBC_SHA, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
- { TLS_RSA_WITH_AES_128_CBC_SHA256, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
+
+ { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_DHE_RSA_WITH_AES_128_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_DHE_RSA_WITH_AES_256_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_DHE_DSS_WITH_AES_256_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_DHE_DSS_WITH_RC4_128_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
#ifdef NSS_ENABLE_ECC
- { TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
+ { TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDH_ECDSA_WITH_RC4_128_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDH_RSA_WITH_RC4_128_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
#endif /* NSS_ENABLE_ECC */
- { SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
- { SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, PR_TRUE,PR_FALSE},
+
+ /* RSA */
+ { TLS_RSA_WITH_AES_128_GCM_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_RSA_WITH_AES_128_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_RSA_WITH_AES_128_CBC_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_RSA_WITH_AES_256_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_RSA_WITH_AES_256_CBC_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_RSA_WITH_SEED_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { SSL_RSA_WITH_RC4_128_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+ { SSL_RSA_WITH_RC4_128_MD5, SSL_ALLOWED, PR_TRUE, PR_FALSE},
+
+ /* 56-bit DES "domestic" cipher suites */
+ { SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { SSL_RSA_FIPS_WITH_DES_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { SSL_RSA_WITH_DES_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+
+ /* export ciphersuites with 1024-bit public key exchange keys */
+ { TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+
+ /* export ciphersuites with 512-bit public key exchange keys */
+ { SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+
+ /* ciphersuites with no encryption */
#ifdef NSS_ENABLE_ECC
- { TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
+ { TLS_ECDHE_ECDSA_WITH_NULL_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDHE_RSA_WITH_NULL_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDH_RSA_WITH_NULL_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_ECDH_ECDSA_WITH_NULL_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
#endif /* NSS_ENABLE_ECC */
- { SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
- { SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED, PR_TRUE, PR_FALSE},
-
-
- { SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { SSL_RSA_FIPS_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
- { SSL_RSA_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
- { TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
- { TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
+ { SSL_RSA_WITH_NULL_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { TLS_RSA_WITH_NULL_SHA256, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+ { SSL_RSA_WITH_NULL_MD5, SSL_ALLOWED, PR_FALSE, PR_FALSE},
+};
- { SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
- { SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
+/* Verify that SSL_ImplementedCiphers and cipherSuites are in consistent order.
+ */
+#ifdef DEBUG
+void ssl3_CheckCipherSuiteOrderConsistency()
+{
+ unsigned int i;
-#ifdef NSS_ENABLE_ECC
- { TLS_ECDHE_ECDSA_WITH_NULL_SHA, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
- { TLS_ECDHE_RSA_WITH_NULL_SHA, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
- { TLS_ECDH_RSA_WITH_NULL_SHA, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
- { TLS_ECDH_ECDSA_WITH_NULL_SHA, SSL_NOT_ALLOWED, PR_FALSE, PR_FALSE},
-#endif /* NSS_ENABLE_ECC */
- { SSL_RSA_WITH_NULL_SHA, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { TLS_RSA_WITH_NULL_SHA256, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
- { SSL_RSA_WITH_NULL_MD5, SSL_NOT_ALLOWED, PR_FALSE,PR_FALSE},
+ /* Note that SSL_ImplementedCiphers has more elements than cipherSuites
+ * because it SSL_ImplementedCiphers includes SSL 2.0 cipher suites.
+ */
+ PORT_Assert(SSL_NumImplementedCiphers >= PR_ARRAY_SIZE(cipherSuites));
-};
+ for (i = 0; i < PR_ARRAY_SIZE(cipherSuites); ++i) {
+ PORT_Assert(SSL_ImplementedCiphers[i] == cipherSuites[i].cipher_suite);
+ }
+}
+#endif
/* This list of SSL3 compression methods is sorted in descending order of
* precedence (desirability). It only includes compression methods we
/* indexed by SSL3BulkCipher */
static const ssl3BulkCipherDef bulk_cipher_defs[] = {
- /* cipher calg keySz secretSz type ivSz BlkSz keygen */
- {cipher_null, calg_null, 0, 0, type_stream, 0, 0, kg_null},
- {cipher_rc4, calg_rc4, 16, 16, type_stream, 0, 0, kg_strong},
- {cipher_rc4_40, calg_rc4, 16, 5, type_stream, 0, 0, kg_export},
- {cipher_rc4_56, calg_rc4, 16, 7, type_stream, 0, 0, kg_export},
- {cipher_rc2, calg_rc2, 16, 16, type_block, 8, 8, kg_strong},
- {cipher_rc2_40, calg_rc2, 16, 5, type_block, 8, 8, kg_export},
- {cipher_des, calg_des, 8, 8, type_block, 8, 8, kg_strong},
- {cipher_3des, calg_3des, 24, 24, type_block, 8, 8, kg_strong},
- {cipher_des40, calg_des, 8, 5, type_block, 8, 8, kg_export},
- {cipher_idea, calg_idea, 16, 16, type_block, 8, 8, kg_strong},
- {cipher_aes_128, calg_aes, 16, 16, type_block, 16,16, kg_strong},
- {cipher_aes_256, calg_aes, 32, 32, type_block, 16,16, kg_strong},
- {cipher_camellia_128, calg_camellia,16, 16, type_block, 16,16, kg_strong},
- {cipher_camellia_256, calg_camellia,32, 32, type_block, 16,16, kg_strong},
- {cipher_seed, calg_seed, 16, 16, type_block, 16,16, kg_strong},
- {cipher_missing, calg_null, 0, 0, type_stream, 0, 0, kg_null},
+ /* |--------- Lengths --------| */
+ /* cipher calg k s type i b t n */
+ /* e e v l a o */
+ /* y c | o g n */
+ /* | r | c | c */
+ /* | e | k | e */
+ /* | t | | | | */
+ {cipher_null, calg_null, 0, 0, type_stream, 0, 0, 0, 0},
+ {cipher_rc4, calg_rc4, 16,16, type_stream, 0, 0, 0, 0},
+ {cipher_rc4_40, calg_rc4, 16, 5, type_stream, 0, 0, 0, 0},
+ {cipher_rc4_56, calg_rc4, 16, 7, type_stream, 0, 0, 0, 0},
+ {cipher_rc2, calg_rc2, 16,16, type_block, 8, 8, 0, 0},
+ {cipher_rc2_40, calg_rc2, 16, 5, type_block, 8, 8, 0, 0},
+ {cipher_des, calg_des, 8, 8, type_block, 8, 8, 0, 0},
+ {cipher_3des, calg_3des, 24,24, type_block, 8, 8, 0, 0},
+ {cipher_des40, calg_des, 8, 5, type_block, 8, 8, 0, 0},
+ {cipher_idea, calg_idea, 16,16, type_block, 8, 8, 0, 0},
+ {cipher_aes_128, calg_aes, 16,16, type_block, 16,16, 0, 0},
+ {cipher_aes_256, calg_aes, 32,32, type_block, 16,16, 0, 0},
+ {cipher_camellia_128, calg_camellia, 16,16, type_block, 16,16, 0, 0},
+ {cipher_camellia_256, calg_camellia, 32,32, type_block, 16,16, 0, 0},
+ {cipher_seed, calg_seed, 16,16, type_block, 16,16, 0, 0},
+ {cipher_aes_128_gcm, calg_aes_gcm, 16,16, type_aead, 4, 0,16, 8},
+ {cipher_missing, calg_null, 0, 0, type_stream, 0, 0, 0, 0},
};
static const ssl3KEADef kea_defs[] =
{SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, cipher_3des, mac_sha, kea_rsa_fips},
{SSL_RSA_FIPS_WITH_DES_CBC_SHA, cipher_des, mac_sha, kea_rsa_fips},
+ {TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, mac_aead, kea_dhe_rsa},
+ {TLS_RSA_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, mac_aead, kea_rsa},
+ {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, mac_aead, kea_ecdhe_rsa},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, cipher_aes_128_gcm, mac_aead, kea_ecdhe_ecdsa},
+
#ifdef NSS_ENABLE_ECC
{TLS_ECDH_ECDSA_WITH_NULL_SHA, cipher_null, mac_sha, kea_ecdh_ecdsa},
{TLS_ECDH_ECDSA_WITH_RC4_128_SHA, cipher_rc4, mac_sha, kea_ecdh_ecdsa},
{ calg_aes , CKM_AES_CBC },
{ calg_camellia , CKM_CAMELLIA_CBC },
{ calg_seed , CKM_SEED_CBC },
+ { calg_aes_gcm , CKM_AES_GCM },
/* { calg_init , (CK_MECHANISM_TYPE)0x7fffffffL } */
};
-#define mmech_null (CK_MECHANISM_TYPE)0x80000000L
+#define mmech_invalid (CK_MECHANISM_TYPE)0x80000000L
#define mmech_md5 CKM_SSL3_MD5_MAC
#define mmech_sha CKM_SSL3_SHA1_MAC
#define mmech_md5_hmac CKM_MD5_HMAC
static const ssl3MACDef mac_defs[] = { /* indexed by SSL3MACAlgorithm */
/* pad_size is only used for SSL 3.0 MAC. See RFC 6101 Sec. 5.2.3.1. */
/* mac mmech pad_size mac_size */
- { mac_null, mmech_null, 0, 0 },
+ { mac_null, mmech_invalid, 0, 0 },
{ mac_md5, mmech_md5, 48, MD5_LENGTH },
{ mac_sha, mmech_sha, 40, SHA1_LENGTH},
{hmac_md5, mmech_md5_hmac, 0, MD5_LENGTH },
{hmac_sha, mmech_sha_hmac, 0, SHA1_LENGTH},
{hmac_sha256, mmech_sha256_hmac, 0, SHA256_LENGTH},
+ { mac_aead, mmech_invalid, 0, 0 },
};
/* indexed by SSL3BulkCipher */
"Camellia-128",
"Camellia-256",
"SEED-CBC",
+ "AES-128-GCM",
"missing"
};
}
static PRBool
-ssl3_CipherSuiteAllowedForVersion(ssl3CipherSuite cipherSuite,
- SSL3ProtocolVersion version)
+ssl3_CipherSuiteAllowedForVersionRange(
+ ssl3CipherSuite cipherSuite,
+ const SSLVersionRange *vrange)
{
switch (cipherSuite) {
/* See RFC 4346 A.5. Export cipher suites must not be used in TLS 1.1 or
* SSL_DH_ANON_EXPORT_WITH_RC4_40_MD5: never implemented
* SSL_DH_ANON_EXPORT_WITH_DES40_CBC_SHA: never implemented
*/
- return version <= SSL_LIBRARY_VERSION_TLS_1_0;
+ return vrange->min <= SSL_LIBRARY_VERSION_TLS_1_0;
case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
case TLS_RSA_WITH_AES_256_CBC_SHA256:
case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
case TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_RSA_WITH_AES_128_GCM_SHA256:
case TLS_RSA_WITH_NULL_SHA256:
- return version >= SSL_LIBRARY_VERSION_TLS_1_2;
+ return vrange->max >= SSL_LIBRARY_VERSION_TLS_1_2;
default:
return PR_TRUE;
}
}
-/* return PR_TRUE if suite matches policy and enabled state */
+/* return PR_TRUE if suite matches policy, enabled state and is applicable to
+ * the given version range. */
/* It would be a REALLY BAD THING (tm) if we ever permitted the use
** of a cipher that was NOT_ALLOWED. So, if this is ever called with
** policy == SSL_NOT_ALLOWED, report no match.
/* adjust suite enabled to the availability of a token that can do the
* cipher suite. */
static PRBool
-config_match(ssl3CipherSuiteCfg *suite, int policy, PRBool enabled)
+config_match(ssl3CipherSuiteCfg *suite, int policy, PRBool enabled,
+ const SSLVersionRange *vrange)
{
PORT_Assert(policy != SSL_NOT_ALLOWED && enabled != PR_FALSE);
if (policy == SSL_NOT_ALLOWED || !enabled)
return (PRBool)(suite->enabled &&
suite->isPresent &&
suite->policy != SSL_NOT_ALLOWED &&
- suite->policy <= policy);
+ suite->policy <= policy &&
+ ssl3_CipherSuiteAllowedForVersionRange(
+ suite->cipher_suite, vrange));
}
-/* return number of cipher suites that match policy and enabled state */
+/* return number of cipher suites that match policy, enabled state and are
+ * applicable for the configured protocol version range. */
/* called from ssl3_SendClientHello and ssl3_ConstructV2CipherSpecsHack */
static int
count_cipher_suites(sslSocket *ss, int policy, PRBool enabled)
int i, count = 0;
if (SSL3_ALL_VERSIONS_DISABLED(&ss->vrange)) {
- return 0;
+ return 0;
}
for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
- if (config_match(&ss->cipherSuites[i], policy, enabled))
+ if (config_match(&ss->cipherSuites[i], policy, enabled, &ss->vrange))
count++;
}
if (count <= 0) {
Null_Cipher(void *ctx, unsigned char *output, int *outputLen, int maxOutputLen,
const unsigned char *input, int inputLen)
{
+ if (inputLen > maxOutputLen) {
+ *outputLen = 0; /* Match PK11_CipherOp in setting outputLen */
+ PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+ return SECFailure;
+ }
*outputLen = inputLen;
if (input != output)
PORT_Memcpy(output, input, inputLen);
static SECStatus
ssl3_GetNewRandom(SSL3Random *random)
{
- PRUint32 gmt = ssl_Time();
SECStatus rv;
- random->rand[0] = (unsigned char)(gmt >> 24);
- random->rand[1] = (unsigned char)(gmt >> 16);
- random->rand[2] = (unsigned char)(gmt >> 8);
- random->rand[3] = (unsigned char)(gmt);
-
/* first 4 bytes are reserverd for time */
- rv = PK11_GenerateRandom(&random->rand[4], SSL3_RANDOM_LENGTH - 4);
+ rv = PK11_GenerateRandom(random->rand, SSL3_RANDOM_LENGTH);
if (rv != SECSuccess) {
ssl_MapLowLevelError(SSL_ERROR_GENERATE_RANDOM_FAILURE);
}
}
/* Allow DER encoded DSA signatures in SSL 3.0 */
if (isTLS || buf->len != SECKEY_SignatureLen(key)) {
- signature = DSAU_DecodeDerSig(buf);
+ signature = DSAU_DecodeDerSigToLen(buf, SECKEY_SignatureLen(key));
if (!signature) {
PORT_SetError(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
return SECFailure;
cipher = suite_def->bulk_cipher_alg;
kea = suite_def->key_exchange_alg;
mac = suite_def->mac_alg;
- if (mac <= ssl_mac_sha && isTLS)
+ if (mac <= ssl_mac_sha && mac != ssl_mac_null && isTLS)
mac += 2;
ss->ssl3.hs.suite_def = suite_def;
unsigned int optArg2 = 0;
PRBool server_encrypts = ss->sec.isServer;
SSLCipherAlgorithm calg;
- SSLCompressionMethod compression_method;
SECStatus rv;
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
cipher_def = pwSpec->cipher_def;
calg = cipher_def->calg;
- compression_method = pwSpec->compression_method;
+
+ if (calg == ssl_calg_aes_gcm) {
+ pwSpec->encode = NULL;
+ pwSpec->decode = NULL;
+ pwSpec->destroy = NULL;
+ pwSpec->encodeContext = NULL;
+ pwSpec->decodeContext = NULL;
+ pwSpec->aead = ssl3_AESGCMBypass;
+ ssl3_InitCompressionContext(pwSpec);
+ return SECSuccess;
+ }
serverContext = pwSpec->server.cipher_context;
clientContext = pwSpec->client.cipher_context;
case ssl_calg_rc2:
case ssl_calg_idea:
case ssl_calg_fortezza:
+ case ssl_calg_aes_gcm:
break;
}
return param;
}
+/* ssl3_BuildRecordPseudoHeader writes the SSL/TLS pseudo-header (the data
+ * which is included in the MAC or AEAD additional data) to |out| and returns
+ * its length. See https://tools.ietf.org/html/rfc5246#section-6.2.3.3 for the
+ * definition of the AEAD additional data.
+ *
+ * TLS pseudo-header includes the record's version field, SSL's doesn't. Which
+ * pseudo-header defintiion to use should be decided based on the version of
+ * the protocol that was negotiated when the cipher spec became current, NOT
+ * based on the version value in the record itself, and the decision is passed
+ * to this function as the |includesVersion| argument. But, the |version|
+ * argument should be the record's version value.
+ */
+static unsigned int
+ssl3_BuildRecordPseudoHeader(unsigned char *out,
+ SSL3SequenceNumber seq_num,
+ SSL3ContentType type,
+ PRBool includesVersion,
+ SSL3ProtocolVersion version,
+ PRBool isDTLS,
+ int length)
+{
+ out[0] = (unsigned char)(seq_num.high >> 24);
+ out[1] = (unsigned char)(seq_num.high >> 16);
+ out[2] = (unsigned char)(seq_num.high >> 8);
+ out[3] = (unsigned char)(seq_num.high >> 0);
+ out[4] = (unsigned char)(seq_num.low >> 24);
+ out[5] = (unsigned char)(seq_num.low >> 16);
+ out[6] = (unsigned char)(seq_num.low >> 8);
+ out[7] = (unsigned char)(seq_num.low >> 0);
+ out[8] = type;
+
+ /* SSL3 MAC doesn't include the record's version field. */
+ if (!includesVersion) {
+ out[9] = MSB(length);
+ out[10] = LSB(length);
+ return 11;
+ }
+
+ /* TLS MAC and AEAD additional data include version. */
+ if (isDTLS) {
+ SSL3ProtocolVersion dtls_version;
+
+ dtls_version = dtls_TLSVersionToDTLSVersion(version);
+ out[9] = MSB(dtls_version);
+ out[10] = LSB(dtls_version);
+ } else {
+ out[9] = MSB(version);
+ out[10] = LSB(version);
+ }
+ out[11] = MSB(length);
+ out[12] = LSB(length);
+ return 13;
+}
+
+static SECStatus
+ssl3_AESGCM(ssl3KeyMaterial *keys,
+ PRBool doDecrypt,
+ unsigned char *out,
+ int *outlen,
+ int maxout,
+ const unsigned char *in,
+ int inlen,
+ const unsigned char *additionalData,
+ int additionalDataLen)
+{
+ SECItem param;
+ SECStatus rv = SECFailure;
+ unsigned char nonce[12];
+ unsigned int uOutLen;
+ CK_GCM_PARAMS gcmParams;
+
+ static const int tagSize = 16;
+ static const int explicitNonceLen = 8;
+
+ /* See https://tools.ietf.org/html/rfc5288#section-3 for details of how the
+ * nonce is formed. */
+ memcpy(nonce, keys->write_iv, 4);
+ if (doDecrypt) {
+ memcpy(nonce + 4, in, explicitNonceLen);
+ in += explicitNonceLen;
+ inlen -= explicitNonceLen;
+ *outlen = 0;
+ } else {
+ if (maxout < explicitNonceLen) {
+ PORT_SetError(SEC_ERROR_INPUT_LEN);
+ return SECFailure;
+ }
+ /* Use the 64-bit sequence number as the explicit nonce. */
+ memcpy(nonce + 4, additionalData, explicitNonceLen);
+ memcpy(out, additionalData, explicitNonceLen);
+ out += explicitNonceLen;
+ maxout -= explicitNonceLen;
+ *outlen = explicitNonceLen;
+ }
+
+ param.type = siBuffer;
+ param.data = (unsigned char *) &gcmParams;
+ param.len = sizeof(gcmParams);
+ gcmParams.pIv = nonce;
+ gcmParams.ulIvLen = sizeof(nonce);
+ gcmParams.pAAD = (unsigned char *)additionalData; /* const cast */
+ gcmParams.ulAADLen = additionalDataLen;
+ gcmParams.ulTagBits = tagSize * 8;
+
+ if (doDecrypt) {
+ rv = PK11_Decrypt(keys->write_key, CKM_AES_GCM, ¶m, out, &uOutLen,
+ maxout, in, inlen);
+ } else {
+ rv = PK11_Encrypt(keys->write_key, CKM_AES_GCM, ¶m, out, &uOutLen,
+ maxout, in, inlen);
+ }
+ *outlen += (int) uOutLen;
+
+ return rv;
+}
+
+#ifndef NO_PKCS11_BYPASS
+static SECStatus
+ssl3_AESGCMBypass(ssl3KeyMaterial *keys,
+ PRBool doDecrypt,
+ unsigned char *out,
+ int *outlen,
+ int maxout,
+ const unsigned char *in,
+ int inlen,
+ const unsigned char *additionalData,
+ int additionalDataLen)
+{
+ SECStatus rv = SECFailure;
+ unsigned char nonce[12];
+ unsigned int uOutLen;
+ AESContext *cx;
+ CK_GCM_PARAMS gcmParams;
+
+ static const int tagSize = 16;
+ static const int explicitNonceLen = 8;
+
+ /* See https://tools.ietf.org/html/rfc5288#section-3 for details of how the
+ * nonce is formed. */
+ PORT_Assert(keys->write_iv_item.len == 4);
+ if (keys->write_iv_item.len != 4) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ memcpy(nonce, keys->write_iv_item.data, 4);
+ if (doDecrypt) {
+ memcpy(nonce + 4, in, explicitNonceLen);
+ in += explicitNonceLen;
+ inlen -= explicitNonceLen;
+ *outlen = 0;
+ } else {
+ if (maxout < explicitNonceLen) {
+ PORT_SetError(SEC_ERROR_INPUT_LEN);
+ return SECFailure;
+ }
+ /* Use the 64-bit sequence number as the explicit nonce. */
+ memcpy(nonce + 4, additionalData, explicitNonceLen);
+ memcpy(out, additionalData, explicitNonceLen);
+ out += explicitNonceLen;
+ maxout -= explicitNonceLen;
+ *outlen = explicitNonceLen;
+ }
+
+ gcmParams.pIv = nonce;
+ gcmParams.ulIvLen = sizeof(nonce);
+ gcmParams.pAAD = (unsigned char *)additionalData; /* const cast */
+ gcmParams.ulAADLen = additionalDataLen;
+ gcmParams.ulTagBits = tagSize * 8;
+
+ cx = (AESContext *)keys->cipher_context;
+ rv = AES_InitContext(cx, keys->write_key_item.data,
+ keys->write_key_item.len,
+ (unsigned char *)&gcmParams, NSS_AES_GCM, !doDecrypt,
+ AES_BLOCK_SIZE);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ if (doDecrypt) {
+ rv = AES_Decrypt(cx, out, &uOutLen, maxout, in, inlen);
+ } else {
+ rv = AES_Encrypt(cx, out, &uOutLen, maxout, in, inlen);
+ }
+ AES_DestroyContext(cx, PR_FALSE);
+ *outlen += (int) uOutLen;
+
+ return rv;
+}
+#endif
+
/* Initialize encryption and MAC contexts for pending spec.
* Master Secret already is derived.
* Caller holds Spec write lock.
pwSpec = ss->ssl3.pwSpec;
cipher_def = pwSpec->cipher_def;
macLength = pwSpec->mac_size;
+ calg = cipher_def->calg;
+ PORT_Assert(alg2Mech[calg].calg == calg);
+
+ pwSpec->client.write_mac_context = NULL;
+ pwSpec->server.write_mac_context = NULL;
+
+ if (calg == calg_aes_gcm) {
+ pwSpec->encode = NULL;
+ pwSpec->decode = NULL;
+ pwSpec->destroy = NULL;
+ pwSpec->encodeContext = NULL;
+ pwSpec->decodeContext = NULL;
+ pwSpec->aead = ssl3_AESGCM;
+ return SECSuccess;
+ }
/*
** Now setup the MAC contexts,
** crypto contexts are setup below.
*/
- pwSpec->client.write_mac_context = NULL;
- pwSpec->server.write_mac_context = NULL;
mac_mech = pwSpec->mac_def->mmech;
mac_param.data = (unsigned char *)&macLength;
mac_param.len = sizeof(macLength);
** Now setup the crypto contexts.
*/
- calg = cipher_def->calg;
- PORT_Assert(alg2Mech[calg].calg == calg);
-
if (calg == calg_null) {
pwSpec->encode = Null_Cipher;
pwSpec->decode = Null_Cipher;
ssl3_ComputeRecordMAC(
ssl3CipherSpec * spec,
PRBool useServerMacKey,
- PRBool isDTLS,
- SSL3ContentType type,
- SSL3ProtocolVersion version,
- SSL3SequenceNumber seq_num,
+ const unsigned char *header,
+ unsigned int headerLen,
const SSL3Opaque * input,
int inputLength,
unsigned char * outbuf,
{
const ssl3MACDef * mac_def;
SECStatus rv;
-#ifndef NO_PKCS11_BYPASS
- PRBool isTLS;
-#endif
- unsigned int tempLen;
- unsigned char temp[MAX_MAC_LENGTH];
-
- temp[0] = (unsigned char)(seq_num.high >> 24);
- temp[1] = (unsigned char)(seq_num.high >> 16);
- temp[2] = (unsigned char)(seq_num.high >> 8);
- temp[3] = (unsigned char)(seq_num.high >> 0);
- temp[4] = (unsigned char)(seq_num.low >> 24);
- temp[5] = (unsigned char)(seq_num.low >> 16);
- temp[6] = (unsigned char)(seq_num.low >> 8);
- temp[7] = (unsigned char)(seq_num.low >> 0);
- temp[8] = type;
-
- /* TLS MAC includes the record's version field, SSL's doesn't.
- ** We decide which MAC defintiion to use based on the version of
- ** the protocol that was negotiated when the spec became current,
- ** NOT based on the version value in the record itself.
- ** But, we use the record'v version value in the computation.
- */
- if (spec->version <= SSL_LIBRARY_VERSION_3_0) {
- temp[9] = MSB(inputLength);
- temp[10] = LSB(inputLength);
- tempLen = 11;
-#ifndef NO_PKCS11_BYPASS
- isTLS = PR_FALSE;
-#endif
- } else {
- /* New TLS hash includes version. */
- if (isDTLS) {
- SSL3ProtocolVersion dtls_version;
-
- dtls_version = dtls_TLSVersionToDTLSVersion(version);
- temp[9] = MSB(dtls_version);
- temp[10] = LSB(dtls_version);
- } else {
- temp[9] = MSB(version);
- temp[10] = LSB(version);
- }
- temp[11] = MSB(inputLength);
- temp[12] = LSB(inputLength);
- tempLen = 13;
-#ifndef NO_PKCS11_BYPASS
- isTLS = PR_TRUE;
-#endif
- }
- PRINT_BUF(95, (NULL, "frag hash1: temp", temp, tempLen));
+ PRINT_BUF(95, (NULL, "frag hash1: header", header, headerLen));
PRINT_BUF(95, (NULL, "frag hash1: input", input, inputLength));
mac_def = spec->mac_def;
return SECFailure;
}
- if (!isTLS) {
+ if (spec->version <= SSL_LIBRARY_VERSION_3_0) {
+ unsigned int tempLen;
+ unsigned char temp[MAX_MAC_LENGTH];
+
/* compute "inner" part of SSL3 MAC */
hashObj->begin(write_mac_context);
if (useServerMacKey)
spec->client.write_mac_key_item.data,
spec->client.write_mac_key_item.len);
hashObj->update(write_mac_context, mac_pad_1, pad_bytes);
- hashObj->update(write_mac_context, temp, tempLen);
+ hashObj->update(write_mac_context, header, headerLen);
hashObj->update(write_mac_context, input, inputLength);
hashObj->end(write_mac_context, temp, &tempLen, sizeof temp);
}
if (rv == SECSuccess) {
HMAC_Begin(cx);
- HMAC_Update(cx, temp, tempLen);
+ HMAC_Update(cx, header, headerLen);
HMAC_Update(cx, input, inputLength);
rv = HMAC_Finish(cx, outbuf, outLength, spec->mac_size);
HMAC_Destroy(cx, PR_FALSE);
(useServerMacKey ? spec->server.write_mac_context
: spec->client.write_mac_context);
rv = PK11_DigestBegin(mac_context);
- rv |= PK11_DigestOp(mac_context, temp, tempLen);
+ rv |= PK11_DigestOp(mac_context, header, headerLen);
rv |= PK11_DigestOp(mac_context, input, inputLength);
rv |= PK11_DigestFinal(mac_context, outbuf, outLength, spec->mac_size);
}
ssl3_ComputeRecordMACConstantTime(
ssl3CipherSpec * spec,
PRBool useServerMacKey,
- PRBool isDTLS,
- SSL3ContentType type,
- SSL3ProtocolVersion version,
- SSL3SequenceNumber seq_num,
+ const unsigned char *header,
+ unsigned int headerLen,
const SSL3Opaque * input,
int inputLen,
int originalLen,
CK_NSS_MAC_CONSTANT_TIME_PARAMS params;
SECItem param, inputItem, outputItem;
SECStatus rv;
- unsigned char header[13];
PK11SymKey * key;
- int recordLength;
PORT_Assert(inputLen >= spec->mac_size);
PORT_Assert(originalLen >= inputLen);
return SECSuccess;
}
- header[0] = (unsigned char)(seq_num.high >> 24);
- header[1] = (unsigned char)(seq_num.high >> 16);
- header[2] = (unsigned char)(seq_num.high >> 8);
- header[3] = (unsigned char)(seq_num.high >> 0);
- header[4] = (unsigned char)(seq_num.low >> 24);
- header[5] = (unsigned char)(seq_num.low >> 16);
- header[6] = (unsigned char)(seq_num.low >> 8);
- header[7] = (unsigned char)(seq_num.low >> 0);
- header[8] = type;
-
macType = CKM_NSS_HMAC_CONSTANT_TIME;
- recordLength = inputLen - spec->mac_size;
if (spec->version <= SSL_LIBRARY_VERSION_3_0) {
macType = CKM_NSS_SSL3_MAC_CONSTANT_TIME;
- header[9] = recordLength >> 8;
- header[10] = recordLength;
- params.ulHeaderLen = 11;
- } else {
- if (isDTLS) {
- SSL3ProtocolVersion dtls_version;
-
- dtls_version = dtls_TLSVersionToDTLSVersion(version);
- header[9] = dtls_version >> 8;
- header[10] = dtls_version;
- } else {
- header[9] = version >> 8;
- header[10] = version;
- }
- header[11] = recordLength >> 8;
- header[12] = recordLength;
- params.ulHeaderLen = 13;
}
params.macAlg = spec->mac_def->mmech;
params.ulBodyTotalLen = originalLen;
- params.pHeader = header;
+ params.pHeader = (unsigned char *) header; /* const cast */
+ params.ulHeaderLen = headerLen;
param.data = (unsigned char*) ¶ms;
param.len = sizeof(params);
/* ssl3_ComputeRecordMAC expects the MAC to have been removed from the
* length already. */
inputLen -= spec->mac_size;
- return ssl3_ComputeRecordMAC(spec, useServerMacKey, isDTLS, type,
- version, seq_num, input, inputLen,
- outbuf, outLen);
+ return ssl3_ComputeRecordMAC(spec, useServerMacKey, header, headerLen,
+ input, inputLen, outbuf, outLen);
}
static PRBool
PRUint16 headerLen;
int ivLen = 0;
int cipherBytes = 0;
+ unsigned char pseudoHeader[13];
+ unsigned int pseudoHeaderLen;
cipher_def = cwSpec->cipher_def;
headerLen = isDTLS ? DTLS_RECORD_HEADER_LENGTH : SSL3_RECORD_HEADER_LENGTH;
contentLen = outlen;
}
- /*
- * Add the MAC
- */
- rv = ssl3_ComputeRecordMAC( cwSpec, isServer, isDTLS,
- type, cwSpec->version, cwSpec->write_seq_num, pIn, contentLen,
- wrBuf->buf + headerLen + ivLen + contentLen, &macLen);
- if (rv != SECSuccess) {
- ssl_MapLowLevelError(SSL_ERROR_MAC_COMPUTATION_FAILURE);
- return SECFailure;
- }
- p1Len = contentLen;
- p2Len = macLen;
- fragLen = contentLen + macLen; /* needs to be encrypted */
- PORT_Assert(fragLen <= MAX_FRAGMENT_LENGTH + 1024);
+ pseudoHeaderLen = ssl3_BuildRecordPseudoHeader(
+ pseudoHeader, cwSpec->write_seq_num, type,
+ cwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_0, cwSpec->version,
+ isDTLS, contentLen);
+ PORT_Assert(pseudoHeaderLen <= sizeof(pseudoHeader));
+ if (cipher_def->type == type_aead) {
+ const int nonceLen = cipher_def->explicit_nonce_size;
+ const int tagLen = cipher_def->tag_size;
- /*
- * Pad the text (if we're doing a block cipher)
- * then Encrypt it
- */
- if (cipher_def->type == type_block) {
- unsigned char * pBuf;
- int padding_length;
- int i;
-
- oddLen = contentLen % cipher_def->block_size;
- /* Assume blockSize is a power of two */
- padding_length = cipher_def->block_size - 1 -
- ((fragLen) & (cipher_def->block_size - 1));
- fragLen += padding_length + 1;
- PORT_Assert((fragLen % cipher_def->block_size) == 0);
-
- /* Pad according to TLS rules (also acceptable to SSL3). */
- pBuf = &wrBuf->buf[headerLen + ivLen + fragLen - 1];
- for (i = padding_length + 1; i > 0; --i) {
- *pBuf-- = padding_length;
- }
- /* now, if contentLen is not a multiple of block size, fix it */
- p2Len = fragLen - p1Len;
- }
- if (p1Len < 256) {
- oddLen = p1Len;
- p1Len = 0;
- } else {
- p1Len -= oddLen;
- }
- if (oddLen) {
- p2Len += oddLen;
- PORT_Assert( (cipher_def->block_size < 2) || \
- (p2Len % cipher_def->block_size) == 0);
- memmove(wrBuf->buf + headerLen + ivLen + p1Len, pIn + p1Len, oddLen);
- }
- if (p1Len > 0) {
- int cipherBytesPart1 = -1;
- rv = cwSpec->encode( cwSpec->encodeContext,
- wrBuf->buf + headerLen + ivLen, /* output */
- &cipherBytesPart1, /* actual outlen */
- p1Len, /* max outlen */
- pIn, p1Len); /* input, and inputlen */
- PORT_Assert(rv == SECSuccess && cipherBytesPart1 == (int) p1Len);
- if (rv != SECSuccess || cipherBytesPart1 != (int) p1Len) {
- PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE);
+ if (headerLen + nonceLen + contentLen + tagLen > wrBuf->space) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
- cipherBytes += cipherBytesPart1;
- }
- if (p2Len > 0) {
- int cipherBytesPart2 = -1;
- rv = cwSpec->encode( cwSpec->encodeContext,
- wrBuf->buf + headerLen + ivLen + p1Len,
- &cipherBytesPart2, /* output and actual outLen */
- p2Len, /* max outlen */
- wrBuf->buf + headerLen + ivLen + p1Len,
- p2Len); /* input and inputLen*/
- PORT_Assert(rv == SECSuccess && cipherBytesPart2 == (int) p2Len);
- if (rv != SECSuccess || cipherBytesPart2 != (int) p2Len) {
+
+ cipherBytes = contentLen;
+ rv = cwSpec->aead(
+ isServer ? &cwSpec->server : &cwSpec->client,
+ PR_FALSE, /* do encrypt */
+ wrBuf->buf + headerLen, /* output */
+ &cipherBytes, /* out len */
+ wrBuf->space - headerLen, /* max out */
+ pIn, contentLen, /* input */
+ pseudoHeader, pseudoHeaderLen);
+ if (rv != SECSuccess) {
PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE);
return SECFailure;
}
- cipherBytes += cipherBytesPart2;
- }
+ } else {
+ /*
+ * Add the MAC
+ */
+ rv = ssl3_ComputeRecordMAC(cwSpec, isServer,
+ pseudoHeader, pseudoHeaderLen, pIn, contentLen,
+ wrBuf->buf + headerLen + ivLen + contentLen, &macLen);
+ if (rv != SECSuccess) {
+ ssl_MapLowLevelError(SSL_ERROR_MAC_COMPUTATION_FAILURE);
+ return SECFailure;
+ }
+ p1Len = contentLen;
+ p2Len = macLen;
+ fragLen = contentLen + macLen; /* needs to be encrypted */
+ PORT_Assert(fragLen <= MAX_FRAGMENT_LENGTH + 1024);
+
+ /*
+ * Pad the text (if we're doing a block cipher)
+ * then Encrypt it
+ */
+ if (cipher_def->type == type_block) {
+ unsigned char * pBuf;
+ int padding_length;
+ int i;
+
+ oddLen = contentLen % cipher_def->block_size;
+ /* Assume blockSize is a power of two */
+ padding_length = cipher_def->block_size - 1 -
+ ((fragLen) & (cipher_def->block_size - 1));
+ fragLen += padding_length + 1;
+ PORT_Assert((fragLen % cipher_def->block_size) == 0);
+
+ /* Pad according to TLS rules (also acceptable to SSL3). */
+ pBuf = &wrBuf->buf[headerLen + ivLen + fragLen - 1];
+ for (i = padding_length + 1; i > 0; --i) {
+ *pBuf-- = padding_length;
+ }
+ /* now, if contentLen is not a multiple of block size, fix it */
+ p2Len = fragLen - p1Len;
+ }
+ if (p1Len < 256) {
+ oddLen = p1Len;
+ p1Len = 0;
+ } else {
+ p1Len -= oddLen;
+ }
+ if (oddLen) {
+ p2Len += oddLen;
+ PORT_Assert( (cipher_def->block_size < 2) || \
+ (p2Len % cipher_def->block_size) == 0);
+ memmove(wrBuf->buf + headerLen + ivLen + p1Len, pIn + p1Len,
+ oddLen);
+ }
+ if (p1Len > 0) {
+ int cipherBytesPart1 = -1;
+ rv = cwSpec->encode( cwSpec->encodeContext,
+ wrBuf->buf + headerLen + ivLen, /* output */
+ &cipherBytesPart1, /* actual outlen */
+ p1Len, /* max outlen */
+ pIn, p1Len); /* input, and inputlen */
+ PORT_Assert(rv == SECSuccess && cipherBytesPart1 == (int) p1Len);
+ if (rv != SECSuccess || cipherBytesPart1 != (int) p1Len) {
+ PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE);
+ return SECFailure;
+ }
+ cipherBytes += cipherBytesPart1;
+ }
+ if (p2Len > 0) {
+ int cipherBytesPart2 = -1;
+ rv = cwSpec->encode( cwSpec->encodeContext,
+ wrBuf->buf + headerLen + ivLen + p1Len,
+ &cipherBytesPart2, /* output and actual outLen */
+ p2Len, /* max outlen */
+ wrBuf->buf + headerLen + ivLen + p1Len,
+ p2Len); /* input and inputLen*/
+ PORT_Assert(rv == SECSuccess && cipherBytesPart2 == (int) p2Len);
+ if (rv != SECSuccess || cipherBytesPart2 != (int) p2Len) {
+ PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE);
+ return SECFailure;
+ }
+ cipherBytes += cipherBytesPart2;
+ }
+ }
+
PORT_Assert(cipherBytes <= MAX_FRAGMENT_LENGTH + 1024);
wrBuf->len = cipherBytes + headerLen;
* Forces the use of the provided epoch
* ssl_SEND_FLAG_CAP_RECORD_VERSION
* Caps the record layer version number of TLS ClientHello to { 3, 1 }
- * (TLS 1.0). Some TLS 1.0 servers (which seem to use F5 BIG-IP) ignore
+ * (TLS 1.0). Some TLS 1.0 servers (which seem to use F5 BIG-IP) ignore
* ClientHello.client_version and use the record layer version number
* (TLSPlaintext.version) instead when negotiating protocol versions. In
* addition, if the record layer version number of ClientHello is { 3, 2 }
- * (TLS 1.1) or higher, these servers reset the TCP connections. Set this
+ * (TLS 1.1) or higher, these servers reset the TCP connections. Lastly,
+ * some F5 BIG-IP servers hang if a record containing a ClientHello has a
+ * version greater than { 3, 1 } and a length greater than 255. Set this
* flag to work around such servers.
*/
PRInt32
SSL_TRC(3, ("%d: SSL3[%d] SendRecord type: %s nIn=%d",
SSL_GETPID(), ss->fd, ssl3_DecodeContentType(type),
nIn));
- PRINT_BUF(3, (ss, "Send record (plain text)", pIn, nIn));
+ PRINT_BUF(50, (ss, "Send record (plain text)", pIn, nIn));
PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss) );
static SECStatus
ssl3_IllegalParameter(sslSocket *ss)
{
- PRBool isTLS;
-
- isTLS = (PRBool)(ss->ssl3.pwSpec->version > SSL_LIBRARY_VERSION_3_0);
(void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
PORT_SetError(ss->sec.isServer ? SSL_ERROR_BAD_CLIENT
: SSL_ERROR_BAD_SERVER );
}
key_material_params.bIsExport = (CK_BBOOL)(kea_def->is_limited);
- /* was: (CK_BBOOL)(cipher_def->keygen_mode != kg_strong); */
key_material_params.RandomInfo.pClientRandom = cr;
key_material_params.RandomInfo.ulClientRandomLen = SSL3_RANDOM_LENGTH;
ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE);
return SECFailure;
}
+
+ /* Create a backup SHA-1 hash for a potential client auth
+ * signature.
+ *
+ * In TLS 1.2, ssl3_ComputeHandshakeHashes always uses the
+ * handshake hash function (SHA-256). If the server or the client
+ * does not support SHA-256 as a signature hash, we can either
+ * maintain a backup SHA-1 handshake hash or buffer all handshake
+ * messages.
+ */
+ if (!ss->sec.isServer) {
+ ss->ssl3.hs.backupHash = PK11_CreateDigestContext(SEC_OID_SHA1);
+ if (ss->ssl3.hs.backupHash == NULL) {
+ ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
+ return SECFailure;
+ }
+
+ if (PK11_DigestBegin(ss->ssl3.hs.backupHash) != SECSuccess) {
+ ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
+ return SECFailure;
+ }
+ }
} else {
/* Both ss->ssl3.hs.md5 and ss->ssl3.hs.sha should be NULL or
* created successfully. */
ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE);
return rv;
}
+ if (ss->ssl3.hs.backupHash) {
+ rv = PK11_DigestOp(ss->ssl3.hs.backupHash, b, l);
+ if (rv != SECSuccess) {
+ ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
+ return rv;
+ }
+ }
} else {
rv = PK11_DigestOp(ss->ssl3.hs.md5, b, l);
if (rv != SECSuccess) {
return rv;
}
+static SECStatus
+ssl3_ComputeBackupHandshakeHashes(sslSocket * ss,
+ SSL3Hashes * hashes) /* output goes here. */
+{
+ SECStatus rv = SECSuccess;
+
+ PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
+ PORT_Assert( !ss->sec.isServer );
+ PORT_Assert( ss->ssl3.hs.hashType == handshake_hash_single );
+
+ rv = PK11_DigestFinal(ss->ssl3.hs.backupHash, hashes->u.raw, &hashes->len,
+ sizeof(hashes->u.raw));
+ if (rv != SECSuccess) {
+ ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE);
+ rv = SECFailure;
+ goto loser;
+ }
+ hashes->hashAlg = SEC_OID_SHA1;
+
+loser:
+ PK11_DestroyContext(ss->ssl3.hs.backupHash, PR_TRUE);
+ ss->ssl3.hs.backupHash = NULL;
+ return rv;
+}
+
/*
* SSL 2 based implementations pass in the initial outbound buffer
* so that the handshake hash can contain the included information.
int num_suites;
int actual_count = 0;
PRBool isTLS = PR_FALSE;
- PRBool requestingResume = PR_FALSE;
PRInt32 total_exten_len = 0;
unsigned numCompressionMethods;
PRInt32 flags;
ss->ssl3.hs.sendingSCSV = PR_FALSE; /* Must be reset every handshake */
PORT_Assert(IS_DTLS(ss) || !resending);
+ SECITEM_FreeItem(&ss->ssl3.hs.newSessionTicket.ticket, PR_FALSE);
+ ss->ssl3.hs.receivedNewSessionTicket = PR_FALSE;
+
/* We might be starting a session renegotiation in which case we should
* clear previous state.
*/
}
if (sid) {
- requestingResume = PR_TRUE;
SSL_AtomicIncrementLong(& ssl3stats.sch_sid_cache_hits );
- /* Are we attempting a stateless session resume? */
- if (sid->version > SSL_LIBRARY_VERSION_3_0 &&
- sid->u.ssl3.sessionTicket.ticket.data)
- SSL_AtomicIncrementLong(& ssl3stats.sch_sid_stateless_resumes );
-
PRINT_BUF(4, (ss, "client, found session-id:", sid->u.ssl3.sessionID,
sid->u.ssl3.sessionIDLength));
return SECFailure; /* ssl3_config_match_init has set error code. */
/* HACK for SCSV in SSL 3.0. On initial handshake, prepend SCSV,
- * only if we're willing to complete an SSL 3.0 handshake.
+ * only if TLS is disabled.
*/
- if (!ss->firstHsDone && ss->vrange.min == SSL_LIBRARY_VERSION_3_0) {
+ if (!ss->firstHsDone && !isTLS) {
/* Must set this before calling Hello Extension Senders,
* to suppress sending of empty RI extension.
*/
ss->ssl3.hs.sendingSCSV = PR_TRUE;
}
+ /* When we attempt session resumption (only), we must lock the sid to
+ * prevent races with other resumption connections that receive a
+ * NewSessionTicket that will cause the ticket in the sid to be replaced.
+ * Once we've copied the session ticket into our ClientHello message, it
+ * is OK for the ticket to change, so we just need to make sure we hold
+ * the lock across the calls to ssl3_CallHelloExtensionSenders.
+ */
+ if (sid->u.ssl3.lock) {
+ PR_RWLock_Rlock(sid->u.ssl3.lock);
+ }
+
if (isTLS || (ss->firstHsDone && ss->peerRequestedProtection)) {
PRUint32 maxBytes = 65535; /* 2^16 - 1 */
PRInt32 extLen;
extLen = ssl3_CallHelloExtensionSenders(ss, PR_FALSE, maxBytes, NULL);
if (extLen < 0) {
+ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
return SECFailure;
}
maxBytes -= extLen;
/* how many suites are permitted by policy and user preference? */
num_suites = count_cipher_suites(ss, ss->ssl3.policy, PR_TRUE);
- if (!num_suites)
+ if (!num_suites) {
+ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
return SECFailure; /* count_cipher_suites has set error code. */
+ }
if (ss->ssl3.hs.sendingSCSV) {
++num_suites; /* make room for SCSV */
}
rv = ssl3_AppendHandshakeHeader(ss, client_hello, length);
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
rv = ssl3_AppendHandshakeNumber(ss, ss->clientHelloVersion, 2);
}
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
if (!resending) { /* Don't re-generate if we are in DTLS re-sending mode */
rv = ssl3_GetNewRandom(&ss->ssl3.hs.client_random);
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
return rv; /* err set by GetNewRandom. */
}
}
rv = ssl3_AppendHandshake(ss, &ss->ssl3.hs.client_random,
SSL3_RANDOM_LENGTH);
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
else
rv = ssl3_AppendHandshakeVariable(ss, NULL, 0, 1);
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
rv = ssl3_AppendHandshakeVariable(
ss, ss->ssl3.hs.cookie, ss->ssl3.hs.cookieLen, 1);
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
}
rv = ssl3_AppendHandshakeNumber(ss, num_suites*sizeof(ssl3CipherSuite), 2);
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
rv = ssl3_AppendHandshakeNumber(ss, TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
sizeof(ssl3CipherSuite));
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
actual_count++;
}
for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
ssl3CipherSuiteCfg *suite = &ss->cipherSuites[i];
- if (config_match(suite, ss->ssl3.policy, PR_TRUE)) {
+ if (config_match(suite, ss->ssl3.policy, PR_TRUE, &ss->vrange)) {
actual_count++;
if (actual_count > num_suites) {
+ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
/* set error card removal/insertion error */
PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
return SECFailure;
rv = ssl3_AppendHandshakeNumber(ss, suite->cipher_suite,
sizeof(ssl3CipherSuite));
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
}
* the server.. */
if (actual_count != num_suites) {
/* Card removal/insertion error */
+ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
return SECFailure;
}
rv = ssl3_AppendHandshakeNumber(ss, numCompressionMethods, 1);
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
for (i = 0; i < compressionMethodsCount; i++) {
continue;
rv = ssl3_AppendHandshakeNumber(ss, compressions[i], 1);
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
return rv; /* err set by ssl3_AppendHandshake* */
}
}
rv = ssl3_AppendHandshakeNumber(ss, maxBytes, 2);
if (rv != SECSuccess) {
+ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
return rv; /* err set by AppendHandshake. */
}
extLen = ssl3_CallHelloExtensionSenders(ss, PR_TRUE, maxBytes, NULL);
if (extLen < 0) {
+ if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
return SECFailure;
}
maxBytes -= extLen;
PORT_Assert(!maxBytes);
}
+
+ if (sid->u.ssl3.lock) {
+ PR_RWLock_Unlock(sid->u.ssl3.lock);
+ }
+
+ if (ss->xtnData.sentSessionTicketInClientHello) {
+ SSL_AtomicIncrementLong(&ssl3stats.sch_sid_stateless_resumes);
+ }
+
if (ss->ssl3.hs.sendingSCSV) {
/* Since we sent the SCSV, pretend we sent empty RI extension. */
TLSExtensionData *xtnData = &ss->xtnData;
}
flags = 0;
- if (!ss->firstHsDone && !requestingResume && !IS_DTLS(ss)) {
+ if (!ss->firstHsDone && !IS_DTLS(ss)) {
flags |= ssl_SEND_FLAG_CAP_RECORD_VERSION;
}
rv = ssl3_FlushHandshake(ss, flags);
SSL_GETPID(), ss->fd));
ssl_GetSpecReadLock(ss);
- rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.pwSpec, &hashes, 0);
+ if (ss->ssl3.hs.hashType == handshake_hash_single &&
+ ss->ssl3.hs.backupHash) {
+ rv = ssl3_ComputeBackupHandshakeHashes(ss, &hashes);
+ PORT_Assert(!ss->ssl3.hs.backupHash);
+ } else {
+ rv = ssl3_ComputeHandshakeHashes(ss, ss->ssl3.pwSpec, &hashes, 0);
+ }
ssl_ReleaseSpecReadLock(ss);
if (rv != SECSuccess) {
goto done; /* err code was set by ssl3_ComputeHandshakeHashes */
if (rv != SECSuccess) {
goto done;
}
- /* We always sign using the handshake hash function. It's possible that
- * a server could support SHA-256 as the handshake hash but not as a
- * signature hash. In that case we wouldn't be able to do client
- * certificates with it. The alternative is to buffer all handshake
- * messages. */
sigAndHash.hashAlg = hashes.hashAlg;
rv = ssl3_AppendSignatureAndHashAlgorithm(ss, &sigAndHash);
for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
ssl3CipherSuiteCfg *suite = &ss->cipherSuites[i];
if (temp == suite->cipher_suite) {
- if (!config_match(suite, ss->ssl3.policy, PR_TRUE)) {
+ SSLVersionRange vrange = {ss->version, ss->version};
+ if (!config_match(suite, ss->ssl3.policy, PR_TRUE, &vrange)) {
+ /* config_match already checks whether the cipher suite is
+ * acceptable for the version, but the check is repeated here
+ * in order to give a more precise error code. */
+ if (!ssl3_CipherSuiteAllowedForVersionRange(temp, &vrange)) {
+ desc = handshake_failure;
+ errCode = SSL_ERROR_CIPHER_DISALLOWED_FOR_VERSION;
+ goto alert_loser;
+ }
+
break; /* failure */
}
- if (!ssl3_CipherSuiteAllowedForVersion(suite->cipher_suite,
- ss->version)) {
- desc = handshake_failure;
- errCode = SSL_ERROR_CIPHER_DISALLOWED_FOR_VERSION;
- goto alert_loser;
- }
suite_found = PR_TRUE;
break; /* success */
SSL_AtomicIncrementLong(& ssl3stats.hsh_sid_cache_hits );
/* If we sent a session ticket, then this is a stateless resume. */
- if (sid->version > SSL_LIBRARY_VERSION_3_0 &&
- sid->u.ssl3.sessionTicket.ticket.data != NULL)
+ if (ss->xtnData.sentSessionTicketInClientHello)
SSL_AtomicIncrementLong(& ssl3stats.hsh_sid_stateless_resumes );
if (ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn))
}
+/*
+ * Returns the TLS signature algorithm for the client authentication key and
+ * whether it is an RSA or DSA key that may be able to sign only SHA-1 hashes.
+ */
+static SECStatus
+ssl3_ExtractClientKeyInfo(sslSocket *ss,
+ TLSSignatureAlgorithm *sigAlg,
+ PRBool *preferSha1)
+{
+ SECStatus rv = SECSuccess;
+ SECKEYPublicKey *pubk;
+
+ pubk = CERT_ExtractPublicKey(ss->ssl3.clientCertificate);
+ if (pubk == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ rv = ssl3_TLSSignatureAlgorithmForKeyType(pubk->keyType, sigAlg);
+ if (rv != SECSuccess) {
+ goto done;
+ }
+
+ /* If the key is a 1024-bit RSA or DSA key, assume conservatively that
+ * it may be unable to sign SHA-256 hashes. This is the case for older
+ * Estonian ID cards that have 1024-bit RSA keys. In FIPS 186-2 and
+ * older, DSA key size is at most 1024 bits and the hash function must
+ * be SHA-1.
+ */
+ if (pubk->keyType == rsaKey || pubk->keyType == dsaKey) {
+ *preferSha1 = SECKEY_PublicKeyStrength(pubk) <= 128;
+ } else {
+ *preferSha1 = PR_FALSE;
+ }
+
+done:
+ if (pubk)
+ SECKEY_DestroyPublicKey(pubk);
+ return rv;
+}
+
+/* Destroys the backup handshake hash context if we don't need it. Note that
+ * this function selects the hash algorithm for client authentication
+ * signatures; ssl3_SendCertificateVerify uses the presence of the backup hash
+ * to determine whether to use SHA-1 or SHA-256. */
+static void
+ssl3_DestroyBackupHandshakeHashIfNotNeeded(sslSocket *ss,
+ const SECItem *algorithms)
+{
+ SECStatus rv;
+ TLSSignatureAlgorithm sigAlg;
+ PRBool preferSha1;
+ PRBool supportsSha1 = PR_FALSE;
+ PRBool supportsSha256 = PR_FALSE;
+ PRBool needBackupHash = PR_FALSE;
+ unsigned int i;
+
+#ifndef NO_PKCS11_BYPASS
+ /* Backup handshake hash is not supported in PKCS #11 bypass mode. */
+ if (ss->opt.bypassPKCS11) {
+ PORT_Assert(!ss->ssl3.hs.backupHash);
+ return;
+ }
+#endif
+ PORT_Assert(ss->ssl3.hs.backupHash);
+
+ /* Determine the key's signature algorithm and whether it prefers SHA-1. */
+ rv = ssl3_ExtractClientKeyInfo(ss, &sigAlg, &preferSha1);
+ if (rv != SECSuccess) {
+ goto done;
+ }
+
+ /* Determine the server's hash support for that signature algorithm. */
+ for (i = 0; i < algorithms->len; i += 2) {
+ if (algorithms->data[i+1] == sigAlg) {
+ if (algorithms->data[i] == tls_hash_sha1) {
+ supportsSha1 = PR_TRUE;
+ } else if (algorithms->data[i] == tls_hash_sha256) {
+ supportsSha256 = PR_TRUE;
+ }
+ }
+ }
+
+ /* If either the server does not support SHA-256 or the client key prefers
+ * SHA-1, leave the backup hash. */
+ if (supportsSha1 && (preferSha1 || !supportsSha256)) {
+ needBackupHash = PR_TRUE;
+ }
+
+done:
+ if (!needBackupHash) {
+ PK11_DestroyContext(ss->ssl3.hs.backupHash, PR_TRUE);
+ ss->ssl3.hs.backupHash = NULL;
+ }
+}
+
typedef struct dnameNode {
struct dnameNode *next;
SECItem name;
ss->ssl3.clientCertificate,
certUsageSSLClient, PR_FALSE);
if (ss->ssl3.clientCertChain == NULL) {
- if (ss->ssl3.clientCertificate != NULL) {
- CERT_DestroyCertificate(ss->ssl3.clientCertificate);
- ss->ssl3.clientCertificate = NULL;
- }
- if (ss->ssl3.clientPrivateKey != NULL) {
- SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey);
- ss->ssl3.clientPrivateKey = NULL;
- }
+ CERT_DestroyCertificate(ss->ssl3.clientCertificate);
+ ss->ssl3.clientCertificate = NULL;
+ SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey);
+ ss->ssl3.clientPrivateKey = NULL;
goto send_no_certificate;
}
+ if (ss->ssl3.hs.hashType == handshake_hash_single) {
+ ssl3_DestroyBackupHandshakeHashIfNotNeeded(ss, &algorithms);
+ }
break; /* not an error */
case SECFailure:
return rv;
}
+static SECStatus
+ssl3_CheckFalseStart(sslSocket *ss)
+{
+ PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
+ PORT_Assert( !ss->ssl3.hs.authCertificatePending );
+ PORT_Assert( !ss->ssl3.hs.canFalseStart );
+
+ if (!ss->canFalseStartCallback) {
+ SSL_TRC(3, ("%d: SSL[%d]: no false start callback so no false start",
+ SSL_GETPID(), ss->fd));
+ } else {
+ PRBool maybeFalseStart;
+ SECStatus rv;
+
+ /* An attacker can control the selected ciphersuite so we only wish to
+ * do False Start in the case that the selected ciphersuite is
+ * sufficiently strong that the attack can gain no advantage.
+ * Therefore we always require an 80-bit cipher. */
+ ssl_GetSpecReadLock(ss);
+ maybeFalseStart = ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10;
+ ssl_ReleaseSpecReadLock(ss);
+
+ if (!maybeFalseStart) {
+ SSL_TRC(3, ("%d: SSL[%d]: no false start due to weak cipher",
+ SSL_GETPID(), ss->fd));
+ } else {
+ rv = (ss->canFalseStartCallback)(ss->fd,
+ ss->canFalseStartCallbackData,
+ &ss->ssl3.hs.canFalseStart);
+ if (rv == SECSuccess) {
+ SSL_TRC(3, ("%d: SSL[%d]: false start callback returned %s",
+ SSL_GETPID(), ss->fd,
+ ss->ssl3.hs.canFalseStart ? "TRUE" : "FALSE"));
+ } else {
+ SSL_TRC(3, ("%d: SSL[%d]: false start callback failed (%s)",
+ SSL_GETPID(), ss->fd,
+ PR_ErrorToName(PR_GetError())));
+ }
+ return rv;
+ }
+ }
+
+ ss->ssl3.hs.canFalseStart = PR_FALSE;
+ return SECSuccess;
+}
+
PRBool
-ssl3_CanFalseStart(sslSocket *ss) {
- PRBool rv;
+ssl3_WaitingForStartOfServerSecondRound(sslSocket *ss)
+{
+ PRBool result;
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
- /* XXX: does not take into account whether we are waiting for
- * SSL_AuthCertificateComplete or SSL_RestartHandshakeAfterCertReq. If/when
- * that is done, this function could return different results each time it
- * would be called.
- */
+ switch (ss->ssl3.hs.ws) {
+ case wait_new_session_ticket:
+ result = PR_TRUE;
+ break;
+ case wait_change_cipher:
+ result = !ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn);
+ break;
+ default:
+ result = PR_FALSE;
+ break;
+ }
- ssl_GetSpecReadLock(ss);
- rv = ss->opt.enableFalseStart &&
- !ss->sec.isServer &&
- !ss->ssl3.hs.isResuming &&
- ss->ssl3.cwSpec &&
-
- /* An attacker can control the selected ciphersuite so we only wish to
- * do False Start in the case that the selected ciphersuite is
- * sufficiently strong that the attack can gain no advantage.
- * Therefore we require an 80-bit cipher and a forward-secret key
- * exchange. */
- ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10 &&
- (ss->ssl3.hs.kea_def->kea == kea_dhe_dss ||
- ss->ssl3.hs.kea_def->kea == kea_dhe_rsa ||
- ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa ||
- ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa);
- ssl_ReleaseSpecReadLock(ss);
- return rv;
+ return result;
}
static SECStatus ssl3_SendClientSecondRound(sslSocket *ss);
ss->ssl3.clientCertChain != NULL &&
ss->ssl3.clientPrivateKey != NULL;
+ if (!sendClientCert &&
+ ss->ssl3.hs.hashType == handshake_hash_single &&
+ ss->ssl3.hs.backupHash) {
+ /* Don't need the backup handshake hash. */
+ PK11_DestroyContext(ss->ssl3.hs.backupHash, PR_TRUE);
+ ss->ssl3.hs.backupHash = NULL;
+ }
+
/* We must wait for the server's certificate to be authenticated before
* sending the client certificate in order to disclosing the client
* certificate to an attacker that does not have a valid cert for the
}
if (ss->ssl3.hs.authCertificatePending &&
(sendClientCert || ss->ssl3.sendEmptyCert || ss->firstHsDone)) {
+ SSL_TRC(3, ("%d: SSL3[%p]: deferring ssl3_SendClientSecondRound because"
+ " certificate authentication is still pending.",
+ SSL_GETPID(), ss->fd));
ss->ssl3.hs.restartTarget = ssl3_SendClientSecondRound;
return SECWouldBlock;
}
goto loser; /* err code was set. */
}
- /* XXX: If the server's certificate hasn't been authenticated by this
- * point, then we may be leaking this NPN message to an attacker.
+ /* This must be done after we've set ss->ssl3.cwSpec in
+ * ssl3_SendChangeCipherSpecs because SSL_GetChannelInfo uses information
+ * from cwSpec. This must be done before we call ssl3_CheckFalseStart
+ * because the false start callback (if any) may need the information from
+ * the functions that depend on this being set.
*/
+ ss->enoughFirstHsDone = PR_TRUE;
+
if (!ss->firstHsDone) {
+ /* XXX: If the server's certificate hasn't been authenticated by this
+ * point, then we may be leaking this NPN message to an attacker.
+ */
rv = ssl3_SendNextProto(ss);
if (rv != SECSuccess) {
goto loser; /* err code was set. */
}
+
+ if (ss->opt.enableFalseStart) {
+ if (!ss->ssl3.hs.authCertificatePending) {
+ /* When we fix bug 589047, we will need to know whether we are
+ * false starting before we try to flush the client second
+ * round to the network. With that in mind, we purposefully
+ * call ssl3_CheckFalseStart before calling ssl3_SendFinished,
+ * which includes a call to ssl3_FlushHandshake, so that
+ * no application develops a reliance on such flushing being
+ * done before its false start callback is called.
+ */
+ ssl_ReleaseXmitBufLock(ss);
+ rv = ssl3_CheckFalseStart(ss);
+ ssl_GetXmitBufLock(ss);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ } else {
+ /* The certificate authentication and the server's Finished
+ * message are racing each other. If the certificate
+ * authentication wins, then we will try to false start in
+ * ssl3_AuthCertificateComplete.
+ */
+ SSL_TRC(3, ("%d: SSL3[%p]: deferring false start check because"
+ " certificate authentication is still pending.",
+ SSL_GETPID(), ss->fd));
+ }
+ }
}
rv = ssl3_SendFinished(ss, 0);
else
ss->ssl3.hs.ws = wait_change_cipher;
- /* Do the handshake callback for sslv3 here, if we can false start. */
- if (ss->handshakeCallback != NULL && ssl3_CanFalseStart(ss)) {
- (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
- }
+ PORT_Assert(ssl3_WaitingForStartOfServerSecondRound(ss));
return SECSuccess;
*/
if (sid) do {
ssl3CipherSuiteCfg *suite;
+#ifdef PARANOID
+ SSLVersionRange vrange = {ss->version, ss->version};
+#endif
/* Check that the cached compression method is still enabled. */
if (!compressionEnabled(ss, sid->u.ssl3.compression))
* The product policy won't change during the process lifetime.
* Implemented ("isPresent") shouldn't change for servers.
*/
- if (!config_match(suite, ss->ssl3.policy, PR_TRUE))
+ if (!config_match(suite, ss->ssl3.policy, PR_TRUE, &vrange))
break;
#else
if (!suite->enabled)
*/
for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) {
ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
- if (!config_match(suite, ss->ssl3.policy, PR_TRUE) ||
- !ssl3_CipherSuiteAllowedForVersion(suite->cipher_suite,
- ss->version)) {
+ SSLVersionRange vrange = {ss->version, ss->version};
+ if (!config_match(suite, ss->ssl3.policy, PR_TRUE, &vrange)) {
continue;
}
for (i = 0; i + 1 < suites.len; i += 2) {
goto alert_loser;
suite_found:
- /* Look for a matching compression algorithm. */
+ /* Select a compression algorithm. */
for (i = 0; i < comps.len; i++) {
if (!compressionEnabled(ss, comps.data[i]))
continue;
*/
for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) {
ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
- if (!config_match(suite, ss->ssl3.policy, PR_TRUE) ||
- !ssl3_CipherSuiteAllowedForVersion(suite->cipher_suite,
- ss->version)) {
+ SSLVersionRange vrange = {ss->version, ss->version};
+ if (!config_match(suite, ss->ssl3.policy, PR_TRUE, &vrange)) {
continue;
}
for (i = 0; i+2 < suite_length; i += 3) {
SECStatus
ssl3_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
{
- SECStatus rv;
- NewSessionTicket session_ticket;
+ SECStatus rv;
+ SECItem ticketData;
SSL_TRC(3, ("%d: SSL3[%d]: handle session_ticket handshake",
SSL_GETPID(), ss->fd));
PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
+ PORT_Assert(!ss->ssl3.hs.newSessionTicket.ticket.data);
+ PORT_Assert(!ss->ssl3.hs.receivedNewSessionTicket);
+
if (ss->ssl3.hs.ws != wait_new_session_ticket) {
SSL3_SendAlert(ss, alert_fatal, unexpected_message);
PORT_SetError(SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET);
return SECFailure;
}
- session_ticket.received_timestamp = ssl_Time();
+ /* RFC5077 Section 3.3: "The client MUST NOT treat the ticket as valid
+ * until it has verified the server's Finished message." See the comment in
+ * ssl3_FinishHandshake for more details.
+ */
+ ss->ssl3.hs.newSessionTicket.received_timestamp = ssl_Time();
if (length < 4) {
(void)SSL3_SendAlert(ss, alert_fatal, decode_error);
PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
return SECFailure;
}
- session_ticket.ticket_lifetime_hint =
+ ss->ssl3.hs.newSessionTicket.ticket_lifetime_hint =
(PRUint32)ssl3_ConsumeHandshakeNumber(ss, 4, &b, &length);
- rv = ssl3_ConsumeHandshakeVariable(ss, &session_ticket.ticket, 2,
- &b, &length);
+ rv = ssl3_ConsumeHandshakeVariable(ss, &ticketData, 2, &b, &length);
if (length != 0 || rv != SECSuccess) {
(void)SSL3_SendAlert(ss, alert_fatal, decode_error);
PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
return SECFailure; /* malformed */
}
-
- rv = ssl3_SetSIDSessionTicket(ss->sec.ci.sid, &session_ticket);
+ rv = SECITEM_CopyItem(NULL, &ss->ssl3.hs.newSessionTicket.ticket,
+ &ticketData);
if (rv != SECSuccess) {
- (void)SSL3_SendAlert(ss, alert_fatal, handshake_failure);
- PORT_SetError(SSL_ERROR_INTERNAL_ERROR_ALERT);
- return SECFailure;
+ return rv;
}
+ ss->ssl3.hs.receivedNewSessionTicket = PR_TRUE;
+
ss->ssl3.hs.ws = wait_change_cipher;
return SECSuccess;
}
ss->ssl3.hs.authCertificatePending = PR_TRUE;
rv = SECSuccess;
-
- /* XXX: Async cert validation and False Start don't work together
- * safely yet; if we leave False Start enabled, we may end up false
- * starting (sending application data) before we
- * SSL_AuthCertificateComplete has been called.
- */
- ss->opt.enableFalseStart = PR_FALSE;
}
if (rv != SECSuccess) {
} else if (ss->ssl3.hs.restartTarget != NULL) {
sslRestartTarget target = ss->ssl3.hs.restartTarget;
ss->ssl3.hs.restartTarget = NULL;
+
+ if (target == ssl3_FinishHandshake) {
+ SSL_TRC(3,("%d: SSL3[%p]: certificate authentication lost the race"
+ " with peer's finished message", SSL_GETPID(), ss->fd));
+ }
+
rv = target(ss);
/* Even if we blocked here, we have accomplished enough to claim
* success. Any remaining work will be taken care of by subsequent
rv = SECSuccess;
}
} else {
- rv = SECSuccess;
+ SSL_TRC(3, ("%d: SSL3[%p]: certificate authentication won the race with"
+ " peer's finished message", SSL_GETPID(), ss->fd));
+
+ PORT_Assert(!ss->ssl3.hs.isResuming);
+ PORT_Assert(ss->ssl3.hs.ws != idle_handshake);
+
+ if (ss->opt.enableFalseStart &&
+ !ss->firstHsDone &&
+ !ss->ssl3.hs.isResuming &&
+ ssl3_WaitingForStartOfServerSecondRound(ss)) {
+ /* ssl3_SendClientSecondRound deferred the false start check because
+ * certificate authentication was pending, so we do it now if we still
+ * haven't received any of the server's second round yet.
+ */
+ rv = ssl3_CheckFalseStart(ss);
+ } else {
+ rv = SECSuccess;
+ }
}
done:
return rv;
}
-/* called from ssl3_HandleServerHelloDone
+/* called from ssl3_SendClientSecondRound
+ * ssl3_HandleFinished
*/
static SECStatus
ssl3_SendNextProto(sslSocket *ss)
static void
ssl3_RecordKeyLog(sslSocket *ss)
{
- sslSessionID *sid;
SECStatus rv;
SECItem *keyData;
char buf[14 /* "CLIENT_RANDOM " */ +
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
- sid = ss->sec.ci.sid;
-
if (!ssl_keylog_iob)
return;
return;
}
-/* called from ssl3_HandleServerHelloDone
+/* called from ssl3_SendClientSecondRound
* ssl3_HandleClientHello
* ssl3_HandleFinished
*/
*/
if (isServer && !ss->ssl3.hs.isResuming &&
ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn)) {
+ /* RFC 5077 Section 3.3: "In the case of a full handshake, the
+ * server MUST verify the client's Finished message before sending
+ * the ticket." Presumably, this also means that the client's
+ * certificate, if any, must be verified beforehand too.
+ */
rv = ssl3_SendNewSessionTicket(ss);
if (rv != SECSuccess) {
goto xmit_loser;
return rv;
}
- ss->gs.writeOffset = 0;
- ss->gs.readOffset = 0;
-
if (ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa) {
effectiveExchKeyType = kt_rsa;
} else {
return rv;
}
+/* The return type is SECStatus instead of void because this function needs
+ * to have type sslRestartTarget.
+ */
SECStatus
ssl3_FinishHandshake(sslSocket * ss)
{
/* The first handshake is now completed. */
ss->handshake = NULL;
- ss->firstHsDone = PR_TRUE;
+
+ /* RFC 5077 Section 3.3: "The client MUST NOT treat the ticket as valid
+ * until it has verified the server's Finished message." When the server
+ * sends a NewSessionTicket in a resumption handshake, we must wait until
+ * the handshake is finished (we have verified the server's Finished
+ * AND the server's certificate) before we update the ticket in the sid.
+ *
+ * This must be done before we call (*ss->sec.cache)(ss->sec.ci.sid)
+ * because CacheSID requires the session ticket to already be set, and also
+ * because of the lazy lock creation scheme used by CacheSID and
+ * ssl3_SetSIDSessionTicket.
+ */
+ if (ss->ssl3.hs.receivedNewSessionTicket) {
+ PORT_Assert(!ss->sec.isServer);
+ ssl3_SetSIDSessionTicket(ss->sec.ci.sid, &ss->ssl3.hs.newSessionTicket);
+ /* The sid took over the ticket data */
+ PORT_Assert(!ss->ssl3.hs.newSessionTicket.ticket.data);
+ ss->ssl3.hs.receivedNewSessionTicket = PR_FALSE;
+ }
if (ss->ssl3.hs.cacheSID) {
+ PORT_Assert(ss->sec.ci.sid->cached == never_cached);
(*ss->sec.cache)(ss->sec.ci.sid);
ss->ssl3.hs.cacheSID = PR_FALSE;
}
+ ss->ssl3.hs.canFalseStart = PR_FALSE; /* False Start phase is complete */
ss->ssl3.hs.ws = idle_handshake;
- /* Do the handshake callback for sslv3 here, if we cannot false start. */
- if (ss->handshakeCallback != NULL && !ssl3_CanFalseStart(ss)) {
- (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
- }
+ ssl_FinishHandshake(ss);
return SECSuccess;
}
/* SSLv3 padding bytes are random and cannot be checked. */
t = plaintext->len;
t -= paddingLength+overhead;
- /* If len >= padding_length+overhead then the MSB of t is zero. */
+ /* If len >= paddingLength+overhead then the MSB of t is zero. */
good = DUPLICATE_MSB_TO_ALL(~t);
/* SSLv3 requires that the padding is minimal. */
t = blockSize - (paddingLength+1);
unsigned int originalLen = 0;
unsigned int good;
unsigned int minLength;
+ unsigned char header[13];
+ unsigned int headerLen;
PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
}
}
- good = (unsigned)-1;
+ good = ~0U;
minLength = crSpec->mac_size;
if (cipher_def->type == type_block) {
/* CBC records have a padding length byte at the end. */
/* With >= TLS 1.1, CBC records have an explicit IV. */
minLength += cipher_def->iv_size;
}
+ } else if (cipher_def->type == type_aead) {
+ minLength = cipher_def->explicit_nonce_size + cipher_def->tag_size;
}
/* We can perform this test in variable time because the record's total
* length and the ciphersuite are both public knowledge. */
if (cText->buf->len < minLength) {
- SSL_DBG(("%d: SSL3[%d]: HandleRecord, record too small.",
- SSL_GETPID(), ss->fd));
- /* must not hold spec lock when calling SSL3_SendAlert. */
- ssl_ReleaseSpecReadLock(ss);
- SSL3_SendAlert(ss, alert_fatal, bad_record_mac);
- /* always log mac error, in case attacker can read server logs. */
- PORT_SetError(SSL_ERROR_BAD_MAC_READ);
- return SECFailure;
+ goto decrypt_loser;
}
if (cipher_def->type == type_block &&
return SECFailure;
}
- /* decrypt from cText buf to plaintext. */
- rv = crSpec->decode(
- crSpec->decodeContext, plaintext->buf, (int *)&plaintext->len,
- plaintext->space, cText->buf->buf + ivLen, cText->buf->len - ivLen);
- good &= SECStatusToMask(rv);
+ rType = cText->type;
+ if (cipher_def->type == type_aead) {
+ /* XXX For many AEAD ciphers, the plaintext is shorter than the
+ * ciphertext by a fixed byte count, but it is not true in general.
+ * Each AEAD cipher should provide a function that returns the
+ * plaintext length for a given ciphertext. */
+ unsigned int decryptedLen =
+ cText->buf->len - cipher_def->explicit_nonce_size -
+ cipher_def->tag_size;
+ headerLen = ssl3_BuildRecordPseudoHeader(
+ header, IS_DTLS(ss) ? cText->seq_num : crSpec->read_seq_num,
+ rType, isTLS, cText->version, IS_DTLS(ss), decryptedLen);
+ PORT_Assert(headerLen <= sizeof(header));
+ rv = crSpec->aead(
+ ss->sec.isServer ? &crSpec->client : &crSpec->server,
+ PR_TRUE, /* do decrypt */
+ plaintext->buf, /* out */
+ (int*) &plaintext->len, /* outlen */
+ plaintext->space, /* maxout */
+ cText->buf->buf, /* in */
+ cText->buf->len, /* inlen */
+ header, headerLen);
+ if (rv != SECSuccess) {
+ good = 0;
+ }
+ } else {
+ if (cipher_def->type == type_block &&
+ ((cText->buf->len - ivLen) % cipher_def->block_size) != 0) {
+ goto decrypt_loser;
+ }
- PRINT_BUF(80, (ss, "cleartext:", plaintext->buf, plaintext->len));
+ /* decrypt from cText buf to plaintext. */
+ rv = crSpec->decode(
+ crSpec->decodeContext, plaintext->buf, (int *)&plaintext->len,
+ plaintext->space, cText->buf->buf + ivLen, cText->buf->len - ivLen);
+ if (rv != SECSuccess) {
+ goto decrypt_loser;
+ }
- originalLen = plaintext->len;
+ PRINT_BUF(80, (ss, "cleartext:", plaintext->buf, plaintext->len));
- /* If it's a block cipher, check and strip the padding. */
- if (cipher_def->type == type_block) {
- const unsigned int blockSize = cipher_def->iv_size;
- const unsigned int macSize = crSpec->mac_size;
+ originalLen = plaintext->len;
- if (crSpec->version <= SSL_LIBRARY_VERSION_3_0) {
- good &= SECStatusToMask(ssl_RemoveSSLv3CBCPadding(
- plaintext, blockSize, macSize));
- } else {
- good &= SECStatusToMask(ssl_RemoveTLSCBCPadding(
- plaintext, macSize));
+ /* If it's a block cipher, check and strip the padding. */
+ if (cipher_def->type == type_block) {
+ const unsigned int blockSize = cipher_def->block_size;
+ const unsigned int macSize = crSpec->mac_size;
+
+ if (!isTLS) {
+ good &= SECStatusToMask(ssl_RemoveSSLv3CBCPadding(
+ plaintext, blockSize, macSize));
+ } else {
+ good &= SECStatusToMask(ssl_RemoveTLSCBCPadding(
+ plaintext, macSize));
+ }
}
- }
- /* compute the MAC */
- rType = cText->type;
- if (cipher_def->type == type_block) {
- rv = ssl3_ComputeRecordMACConstantTime(
- crSpec, (PRBool)(!ss->sec.isServer),
- IS_DTLS(ss), rType, cText->version,
- IS_DTLS(ss) ? cText->seq_num : crSpec->read_seq_num,
- plaintext->buf, plaintext->len, originalLen,
- hash, &hashBytes);
-
- ssl_CBCExtractMAC(plaintext, originalLen, givenHashBuf,
- crSpec->mac_size);
- givenHash = givenHashBuf;
-
- /* plaintext->len will always have enough space to remove the MAC
- * because in ssl_Remove{SSLv3|TLS}CBCPadding we only adjust
- * plaintext->len if the result has enough space for the MAC and we
- * tested the unadjusted size against minLength, above. */
- plaintext->len -= crSpec->mac_size;
- } else {
- /* This is safe because we checked the minLength above. */
- plaintext->len -= crSpec->mac_size;
+ /* compute the MAC */
+ headerLen = ssl3_BuildRecordPseudoHeader(
+ header, IS_DTLS(ss) ? cText->seq_num : crSpec->read_seq_num,
+ rType, isTLS, cText->version, IS_DTLS(ss),
+ plaintext->len - crSpec->mac_size);
+ PORT_Assert(headerLen <= sizeof(header));
+ if (cipher_def->type == type_block) {
+ rv = ssl3_ComputeRecordMACConstantTime(
+ crSpec, (PRBool)(!ss->sec.isServer), header, headerLen,
+ plaintext->buf, plaintext->len, originalLen,
+ hash, &hashBytes);
+
+ ssl_CBCExtractMAC(plaintext, originalLen, givenHashBuf,
+ crSpec->mac_size);
+ givenHash = givenHashBuf;
+
+ /* plaintext->len will always have enough space to remove the MAC
+ * because in ssl_Remove{SSLv3|TLS}CBCPadding we only adjust
+ * plaintext->len if the result has enough space for the MAC and we
+ * tested the unadjusted size against minLength, above. */
+ plaintext->len -= crSpec->mac_size;
+ } else {
+ /* This is safe because we checked the minLength above. */
+ plaintext->len -= crSpec->mac_size;
- rv = ssl3_ComputeRecordMAC(
- crSpec, (PRBool)(!ss->sec.isServer),
- IS_DTLS(ss), rType, cText->version,
- IS_DTLS(ss) ? cText->seq_num : crSpec->read_seq_num,
- plaintext->buf, plaintext->len,
- hash, &hashBytes);
+ rv = ssl3_ComputeRecordMAC(
+ crSpec, (PRBool)(!ss->sec.isServer), header, headerLen,
+ plaintext->buf, plaintext->len, hash, &hashBytes);
- /* We can read the MAC directly from the record because its location is
- * public when a stream cipher is used. */
- givenHash = plaintext->buf + plaintext->len;
- }
+ /* We can read the MAC directly from the record because its location
+ * is public when a stream cipher is used. */
+ givenHash = plaintext->buf + plaintext->len;
+ }
- good &= SECStatusToMask(rv);
+ good &= SECStatusToMask(rv);
- if (hashBytes != (unsigned)crSpec->mac_size ||
- NSS_SecureMemcmp(givenHash, hash, crSpec->mac_size) != 0) {
- /* We're allowed to leak whether or not the MAC check was correct */
- good = 0;
+ if (hashBytes != (unsigned)crSpec->mac_size ||
+ NSS_SecureMemcmp(givenHash, hash, crSpec->mac_size) != 0) {
+ /* We're allowed to leak whether or not the MAC check was correct */
+ good = 0;
+ }
}
if (good == 0) {
+decrypt_loser:
/* must not hold spec lock when calling SSL3_SendAlert. */
ssl_ReleaseSpecReadLock(ss);
- SSL_DBG(("%d: SSL3[%d]: mac check failed", SSL_GETPID(), ss->fd));
+ SSL_DBG(("%d: SSL3[%d]: decryption failed", SSL_GETPID(), ss->fd));
if (!IS_DTLS(ss)) {
SSL3_SendAlert(ss, alert_fatal, bad_record_mac);
ssl_ReleaseSSL3HandshakeLock(ss);
return rv;
-
}
/*
ss->ssl3.hs.messages.buf = NULL;
ss->ssl3.hs.messages.space = 0;
+ ss->ssl3.hs.receivedNewSessionTicket = PR_FALSE;
+ PORT_Memset(&ss->ssl3.hs.newSessionTicket, 0,
+ sizeof(ss->ssl3.hs.newSessionTicket));
+
ss->ssl3.initialized = PR_TRUE;
return SECSuccess;
}
/* ssl3_config_match_init was called by the caller of this function. */
for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
ssl3CipherSuiteCfg *suite = &ss->cipherSuites[i];
- if (config_match(suite, SSL_ALLOWED, PR_TRUE)) {
+ if (config_match(suite, SSL_ALLOWED, PR_TRUE, &ss->vrange)) {
if (cs != NULL) {
*cs++ = 0x00;
*cs++ = (suite->cipher_suite >> 8) & 0xFF;
/* free the SSL3Buffer (msg_body) */
PORT_Free(ss->ssl3.hs.msg_body.buf);
+ SECITEM_FreeItem(&ss->ssl3.hs.newSessionTicket.ticket, PR_FALSE);
+
/* free up the CipherSpecs */
ssl3_DestroyCipherSpec(&ss->ssl3.specs[0], PR_TRUE/*freeSrvName*/);
ssl3_DestroyCipherSpec(&ss->ssl3.specs[1], PR_TRUE/*freeSrvName*/);