From 2ea803546046e61783da398306b4a19070c51448 Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Wed, 15 Aug 2012 15:15:05 +0000 Subject: [PATCH] Add three Suite B modes to TLS code, supporting RFC6460. --- CHANGES | 6 ++ apps/s_server.c | 4 +- ssl/s23_clnt.c | 6 ++ ssl/s23_srvr.c | 7 ++ ssl/s3_clnt.c | 11 +- ssl/s3_lib.c | 57 ++++------ ssl/s3_srvr.c | 4 +- ssl/ssl.h | 15 ++- ssl/ssl_cert.c | 4 + ssl/ssl_ciph.c | 45 +++++++- ssl/ssl_err.c | 2 + ssl/ssl_lib.c | 8 +- ssl/ssl_locl.h | 9 +- ssl/t1_lib.c | 272 +++++++++++++++++++++++++++++++++++++++++++----- ssl/tls1.h | 5 + 15 files changed, 375 insertions(+), 80 deletions(-) diff --git a/CHANGES b/CHANGES index f37e3d7ec6..0352bceb4b 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,12 @@ Changes between 1.0.1 and 1.1.0 [xx XXX xxxx] + *) New Suite B modes for TLS code. These use and enforce the requirements + of RFC6460: restrict ciphersuites, only permit Suite B algorithms and + only use Suite B curves. The Suite B modes can be set by using the + strings "SUITEB128", "SUITEB192" or "SUITEB128ONLY" for the cipherstring. + [Steve Henson] + *) New chain verification flags for Suite B levels of security. Check algorithms are acceptable when flags are set in X509_verify_cert. [Steve Henson] diff --git a/apps/s_server.c b/apps/s_server.c index bbe24929fb..3924171924 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -2623,8 +2623,8 @@ static int init_ssl_connection(SSL *con) BIO_printf(bio_err,"verify error:%s\n", X509_verify_cert_error_string(verify_error)); } - else - ERR_print_errors(bio_err); + /* Always print any error messages */ + ERR_print_errors(bio_err); return(0); } diff --git a/ssl/s23_clnt.c b/ssl/s23_clnt.c index 807dd0ba26..20a8b3ba5c 100644 --- a/ssl/s23_clnt.c +++ b/ssl/s23_clnt.c @@ -367,6 +367,12 @@ static int ssl23_client_hello(SSL *s) version_major = TLS1_2_VERSION_MAJOR; version_minor = TLS1_2_VERSION_MINOR; } + else if (tls1_suiteb(s)) + { + SSLerr(SSL_F_SSL23_CLIENT_HELLO, + SSL_R_ONLY_TLS_1_2_ALLOWED_IN_SUITEB_MODE); + return -1; + } else if (version == TLS1_1_VERSION) { version_major = TLS1_1_VERSION_MAJOR; diff --git a/ssl/s23_srvr.c b/ssl/s23_srvr.c index 4877849013..9d47c22cb8 100644 --- a/ssl/s23_srvr.c +++ b/ssl/s23_srvr.c @@ -425,6 +425,13 @@ int ssl23_get_client_hello(SSL *s) } } + if (s->version < TLS1_2_VERSION && tls1_suiteb(s)) + { + SSLerr(SSL_F_SSL23_GET_CLIENT_HELLO, + SSL_R_ONLY_TLS_1_2_ALLOWED_IN_SUITEB_MODE); + goto err; + } + #ifdef OPENSSL_FIPS if (FIPS_mode() && (s->version < TLS1_VERSION)) { diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c index 5adbb50485..721c3ba3cb 100644 --- a/ssl/s3_clnt.c +++ b/ssl/s3_clnt.c @@ -3154,8 +3154,9 @@ err: } /* Check a certificate can be used for client authentication. Currently - * check cert exists, if we have a suitable digest for TLS 1.2 and if - * static DH client certificates can be used. + * check cert exists, if we have a suitable digest for TLS 1.2 if + * static DH client certificates can be used and optionally checks + * suitability for Suite B. */ static int ssl3_check_client_certificate(SSL *s) { @@ -3165,6 +3166,12 @@ static int ssl3_check_client_certificate(SSL *s) /* If no suitable signature algorithm can't use certificate */ if (TLS1_get_version(s) >= TLS1_2_VERSION && !s->cert->key->digest) return 0; + /* If strict mode check suitability of chain before using it. + * This also adjusts suite B digest if necessary. + */ + if (s->cert->cert_flags & SSL_CERT_FLAGS_CHECK_TLS_STRICT && + !tls1_check_chain(s, NULL, NULL, NULL, -2)) + return 0; alg_k=s->s3->tmp.new_cipher->algorithm_mkey; /* See if we can use client certificate for fixed DH */ if (alg_k & (SSL_kDHr|SSL_kDHd)) diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index 3bc5ce952a..0147e413ff 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -3966,7 +3966,7 @@ SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *clnt, } #endif - if (s->options & SSL_OP_CIPHER_SERVER_PREFERENCE) + if (s->options & SSL_OP_CIPHER_SERVER_PREFERENCE || tls1_suiteb(s)) { prio = srvr; allow = clnt; @@ -4040,7 +4040,7 @@ SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *clnt, /* if we are considering an ECC cipher suite that uses * an ephemeral EC key check it */ if (alg_k & SSL_kEECDH) - ok = ok && tls1_check_ec_tmp_key(s); + ok = ok && tls1_check_ec_tmp_key(s, c->id); #endif /* OPENSSL_NO_EC */ #endif /* OPENSSL_NO_TLSEXT */ @@ -4059,7 +4059,7 @@ int ssl3_get_req_cert_type(SSL *s, unsigned char *p) { int ret=0; const unsigned char *sig; - size_t siglen; + size_t i, siglen; int have_rsa_sign = 0, have_dsa_sign = 0, have_ecdsa_sign = 0; int nostrict = 1; unsigned long alg_k; @@ -4070,48 +4070,27 @@ int ssl3_get_req_cert_type(SSL *s, unsigned char *p) memcpy(p, s->cert->ctypes, s->cert->ctype_num); return (int)s->cert->ctype_num; } - /* Else see if we have any signature algorithms configured */ - if (s->cert->client_sigalgs) + /* get configured sigalgs */ + siglen = tls12_get_psigalgs(s, &sig); + if (s->cert->cert_flags & SSL_CERT_FLAGS_CHECK_TLS_STRICT) + nostrict = 0; + for (i = 0; i < siglen; i+=2, sig+=2) { - sig = s->cert->client_sigalgs; - siglen = s->cert->client_sigalgslen; - } - else - { - sig = s->cert->conf_sigalgs; - siglen = s->cert->conf_sigalgslen; - } - /* If we have sigalgs work out if we can sign with RSA, DSA, ECDSA */ - if (sig) - { - size_t i; - if (s->cert->cert_flags & SSL_CERT_FLAG_TLS_STRICT) - nostrict = 0; - for (i = 0; i < siglen; i+=2, sig+=2) + switch(sig[1]) { - switch(sig[1]) - { - case TLSEXT_signature_rsa: - have_rsa_sign = 1; - break; + case TLSEXT_signature_rsa: + have_rsa_sign = 1; + break; - case TLSEXT_signature_dsa: - have_dsa_sign = 1; - break; + case TLSEXT_signature_dsa: + have_dsa_sign = 1; + break; - case TLSEXT_signature_ecdsa: - have_ecdsa_sign = 1; - break; - } + case TLSEXT_signature_ecdsa: + have_ecdsa_sign = 1; + break; } } - /* Otherwise allow anything */ - else - { - have_rsa_sign = 1; - have_dsa_sign = 1; - have_ecdsa_sign = 1; - } alg_k = s->s3->tmp.new_cipher->algorithm_mkey; diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c index 2f23f2121b..76f49f7978 100644 --- a/ssl/s3_srvr.c +++ b/ssl/s3_srvr.c @@ -1704,8 +1704,8 @@ int ssl3_send_server_key_exchange(SSL *s) ecdhp=cert->ecdh_tmp; if (s->cert->ecdh_tmp_auto) { - /* Get NID of first shared curve */ - int nid = tls1_shared_curve(s, 0); + /* Get NID of appropriate shared curve */ + int nid = tls1_shared_curve(s, -2); if (nid != NID_undef) ecdhp = EC_KEY_new_by_curve_name(nid); } diff --git a/ssl/ssl.h b/ssl/ssl.h index 78ead35fc7..d1104fadee 100644 --- a/ssl/ssl.h +++ b/ssl/ssl.h @@ -654,7 +654,16 @@ struct ssl_session_st /* Many implementations ignore some aspects of the TLS standards such as * enforcing certifcate chain algorithms. When this is set we enforce them. */ -#define SSL_CERT_FLAG_TLS_STRICT 0x00000001L +#define SSL_CERT_FLAG_TLS_STRICT 0x00000001L + +/* Suite B modes, takes same values as certificate verify flags */ +#define SSL_CERT_FLAG_SUITEB_128_LOS_ONLY 0x10000 +/* Suite B 192 bit only mode */ +#define SSL_CERT_FLAG_SUITEB_192_LOS 0x20000 +/* Suite B 128 bit mode allowing 192 bit algorithms */ +#define SSL_CERT_FLAG_SUITEB_128_LOS 0x30000 + + /* Flags for building certificate chains */ /* Treat any existing certificates as untrusted CAs */ @@ -681,6 +690,8 @@ struct ssl_session_st #define CERT_PKEY_ISSUER_NAME 0x200 /* Cert type matches client types (always set for server cert) */ #define CERT_PKEY_CERT_TYPE 0x400 +/* Cert chain suitable to Suite B */ +#define CERT_PKEY_SUITEB 0x800 /* Note: SSL[_CTX]_set_{options,mode} use |= op on the previous value, * they cannot be used to clear bits. */ @@ -2579,6 +2590,7 @@ void ERR_load_SSL_strings(void); #define SSL_R_HTTPS_PROXY_REQUEST 155 #define SSL_R_HTTP_REQUEST 156 #define SSL_R_ILLEGAL_PADDING 283 +#define SSL_R_ILLEGAL_SUITEB_DIGEST 380 #define SSL_R_INCONSISTENT_COMPRESSION 340 #define SSL_R_INVALID_AUDIT_PROOF 371 #define SSL_R_INVALID_AUTHZ_DATA 374 @@ -2653,6 +2665,7 @@ void ERR_load_SSL_strings(void); #define SSL_R_NULL_SSL_METHOD_PASSED 196 #define SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED 197 #define SSL_R_OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED 344 +#define SSL_R_ONLY_TLS_1_2_ALLOWED_IN_SUITEB_MODE 379 #define SSL_R_ONLY_TLS_ALLOWED_IN_FIPS_MODE 297 #define SSL_R_OPAQUE_PRF_INPUT_TOO_LONG 327 #define SSL_R_PACKET_LENGTH_TOO_LONG 198 diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c index 470ac17098..6dfde2f8da 100644 --- a/ssl/ssl_cert.c +++ b/ssl/ssl_cert.c @@ -692,6 +692,8 @@ int ssl_verify_cert_chain(SSL *s,STACK_OF(X509) *sk) SSLerr(SSL_F_SSL_VERIFY_CERT_CHAIN,ERR_R_X509_LIB); return(0); } + /* Set suite B flags if needed */ + X509_STORE_CTX_set_flags(&ctx, tls1_suiteb(s)); #if 0 if (SSL_get_verify_depth(s) >= 0) X509_STORE_CTX_set_depth(&ctx, SSL_get_verify_depth(s)); @@ -1151,6 +1153,8 @@ int ssl_build_cert_chain(CERT *c, X509_STORE *chain_store, int flags) SSLerr(SSL_F_SSL_BUILD_CERT_CHAIN, ERR_R_X509_LIB); return 0; } + /* Set suite B flags if needed */ + X509_STORE_CTX_set_flags(&xs_ctx, c->cert_flags & SSL_CERT_FLAG_SUITEB_128_LOS); i = X509_verify_cert(&xs_ctx); if (i > 0) diff --git a/ssl/ssl_ciph.c b/ssl/ssl_ciph.c index 6db2aa945d..e00c452b8b 100644 --- a/ssl/ssl_ciph.c +++ b/ssl/ssl_ciph.c @@ -1347,10 +1347,49 @@ static int ssl_cipher_process_rulestr(const char *rule_str, return(retval); } +static int check_suiteb_cipher_list(const SSL_METHOD *meth, CERT *c, + const char **prule_str) + { + unsigned int suiteb_flags = 0; + if (!strcmp(*prule_str, "SUITEB128")) + suiteb_flags = SSL_CERT_FLAG_SUITEB_128_LOS; + else if (!strcmp(*prule_str, "SUITEB128ONLY")) + suiteb_flags = SSL_CERT_FLAG_SUITEB_128_LOS_ONLY; + else if (!strcmp(*prule_str, "SUITEB192")) + suiteb_flags = SSL_CERT_FLAG_SUITEB_192_LOS; + + if (suiteb_flags) + { + c->cert_flags &= ~SSL_CERT_FLAG_SUITEB_128_LOS; + c->cert_flags |= suiteb_flags; + } + else + suiteb_flags = c->cert_flags & SSL_CERT_FLAG_SUITEB_128_LOS; + + if (!suiteb_flags) + return 1; + /* Check version */ + + switch(suiteb_flags) + { + case SSL_CERT_FLAG_SUITEB_128_LOS: + *prule_str = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384"; + break; + case SSL_CERT_FLAG_SUITEB_128_LOS_ONLY: + *prule_str = "ECDHE-ECDSA-AES128-GCM-SHA256"; + break; + case SSL_CERT_FLAG_SUITEB_192_LOS: + *prule_str = "ECDHE-ECDSA-AES256-GCM-SHA384"; + break; + } + return 1; + } + + STACK_OF(SSL_CIPHER) *ssl_create_cipher_list(const SSL_METHOD *ssl_method, STACK_OF(SSL_CIPHER) **cipher_list, STACK_OF(SSL_CIPHER) **cipher_list_by_id, - const char *rule_str) + const char *rule_str, CERT *c) { int ok, num_of_ciphers, num_of_alias_max, num_of_group_aliases; unsigned long disabled_mkey, disabled_auth, disabled_enc, disabled_mac, disabled_ssl; @@ -1365,6 +1404,10 @@ STACK_OF(SSL_CIPHER) *ssl_create_cipher_list(const SSL_METHOD *ssl_method, if (rule_str == NULL || cipher_list == NULL || cipher_list_by_id == NULL) return NULL; + if (!check_suiteb_cipher_list(ssl_method, c, &rule_str)) + return NULL; + + /* * To reduce the work to do we only want to process the compiled * in algorithms, so we first get the mask of disabled ciphers. diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 14e8488411..4a7d7f2194 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -398,6 +398,7 @@ static ERR_STRING_DATA SSL_str_reasons[]= {ERR_REASON(SSL_R_HTTPS_PROXY_REQUEST) ,"https proxy request"}, {ERR_REASON(SSL_R_HTTP_REQUEST) ,"http request"}, {ERR_REASON(SSL_R_ILLEGAL_PADDING) ,"illegal padding"}, +{ERR_REASON(SSL_R_ILLEGAL_SUITEB_DIGEST) ,"illegal Suite B digest"}, {ERR_REASON(SSL_R_INCONSISTENT_COMPRESSION),"inconsistent compression"}, {ERR_REASON(SSL_R_INVALID_AUDIT_PROOF) ,"invalid audit proof"}, {ERR_REASON(SSL_R_INVALID_AUTHZ_DATA) ,"invalid authz data"}, @@ -472,6 +473,7 @@ static ERR_STRING_DATA SSL_str_reasons[]= {ERR_REASON(SSL_R_NULL_SSL_METHOD_PASSED),"null ssl method passed"}, {ERR_REASON(SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED),"old session cipher not returned"}, {ERR_REASON(SSL_R_OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED),"old session compression algorithm not returned"}, +{ERR_REASON(SSL_R_ONLY_TLS_1_2_ALLOWED_IN_SUITEB_MODE),"only TLS 1.2 allowed in Suite B mode"}, {ERR_REASON(SSL_R_ONLY_TLS_ALLOWED_IN_FIPS_MODE),"only tls allowed in fips mode"}, {ERR_REASON(SSL_R_OPAQUE_PRF_INPUT_TOO_LONG),"opaque PRF input too long"}, {ERR_REASON(SSL_R_PACKET_LENGTH_TOO_LONG),"packet length too long"}, diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index bec87d8141..548ba1c5e9 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -263,7 +263,7 @@ int SSL_CTX_set_ssl_version(SSL_CTX *ctx,const SSL_METHOD *meth) sk=ssl_create_cipher_list(ctx->method,&(ctx->cipher_list), &(ctx->cipher_list_by_id), - meth->version == SSL2_VERSION ? "SSLv2" : SSL_DEFAULT_CIPHER_LIST); + meth->version == SSL2_VERSION ? "SSLv2" : SSL_DEFAULT_CIPHER_LIST, ctx->cert); if ((sk == NULL) || (sk_SSL_CIPHER_num(sk) <= 0)) { SSLerr(SSL_F_SSL_CTX_SET_SSL_VERSION,SSL_R_SSL_LIBRARY_HAS_NO_CIPHERS); @@ -1333,7 +1333,7 @@ int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str) STACK_OF(SSL_CIPHER) *sk; sk=ssl_create_cipher_list(ctx->method,&ctx->cipher_list, - &ctx->cipher_list_by_id,str); + &ctx->cipher_list_by_id,str, ctx->cert); /* ssl_create_cipher_list may return an empty stack if it * was unable to find a cipher matching the given rule string * (for example if the rule string specifies a cipher which @@ -1357,7 +1357,7 @@ int SSL_set_cipher_list(SSL *s,const char *str) STACK_OF(SSL_CIPHER) *sk; sk=ssl_create_cipher_list(s->ctx->method,&s->cipher_list, - &s->cipher_list_by_id,str); + &s->cipher_list_by_id,str, s->cert); /* see comment in SSL_CTX_set_cipher_list */ if (sk == NULL) return 0; @@ -1787,7 +1787,7 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth) ssl_create_cipher_list(ret->method, &ret->cipher_list,&ret->cipher_list_by_id, - meth->version == SSL2_VERSION ? "SSLv2" : SSL_DEFAULT_CIPHER_LIST); + meth->version == SSL2_VERSION ? "SSLv2" : SSL_DEFAULT_CIPHER_LIST, ret->cert); if (ret->cipher_list == NULL || sk_SSL_CIPHER_num(ret->cipher_list) <= 0) { diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 15233033c2..39ee16b434 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -489,6 +489,11 @@ typedef struct cert_pkey_st */ int valid_flags; } CERT_PKEY; +/* Retrieve Suite B flags */ +#define tls1_suiteb(s) (s->cert->cert_flags & SSL_CERT_FLAG_SUITEB_128_LOS) +/* Uses to check strict mode: suite B modes are always strict */ +#define SSL_CERT_FLAGS_CHECK_TLS_STRICT \ + (SSL_CERT_FLAG_SUITEB_128_LOS|SSL_CERT_FLAG_TLS_STRICT) typedef struct cert_st { @@ -910,7 +915,7 @@ int ssl_cipher_list_to_bytes(SSL *s,STACK_OF(SSL_CIPHER) *sk,unsigned char *p, STACK_OF(SSL_CIPHER) *ssl_create_cipher_list(const SSL_METHOD *meth, STACK_OF(SSL_CIPHER) **pref, STACK_OF(SSL_CIPHER) **sorted, - const char *rule_str); + const char *rule_str, CERT *c); void ssl_update_cache(SSL *s, int mode); int ssl_cipher_get_evp(const SSL_SESSION *s,const EVP_CIPHER **enc, const EVP_MD **md,int *mac_pkey_type,int *mac_secret_size, SSL_COMP **comp); @@ -1184,7 +1189,7 @@ int tls1_set_curves(unsigned char **pext, size_t *pextlen, int *curves, size_t ncurves); int tls1_set_curves_list(unsigned char **pext, size_t *pextlen, const char *str); -int tls1_check_ec_tmp_key(SSL *s); +int tls1_check_ec_tmp_key(SSL *s, unsigned long id); #endif /* OPENSSL_NO_EC */ #ifndef OPENSSL_NO_TLSEXT diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 68bd709b2d..ecac97b3aa 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -241,6 +241,12 @@ static const unsigned char eccurves_default[] = 0,17, /* secp160r2 (17) */ }; +static const unsigned char suiteb_curves[] = + { + 0, TLSEXT_curve_P_256, + 0, TLSEXT_curve_P_384 + }; + int tls1_ec_curve_id2nid(int curve_id) { /* ECC curves from draft-ietf-tls-ecc-12.txt (Oct. 17, 2005) */ @@ -320,13 +326,29 @@ static void tls1_get_curvelist(SSL *s, int sess, { *pcurves = s->session->tlsext_ellipticcurvelist; *pcurveslen = s->session->tlsext_ellipticcurvelist_length; + return; } - else + /* For Suite B mode only include P-256, P-384 */ + switch (tls1_suiteb(s)) { + case SSL_CERT_FLAG_SUITEB_128_LOS: + *pcurves = suiteb_curves; + *pcurveslen = sizeof(suiteb_curves); + break; + + case SSL_CERT_FLAG_SUITEB_128_LOS_ONLY: + *pcurves = suiteb_curves; + *pcurveslen = 2; + break; + + case SSL_CERT_FLAG_SUITEB_192_LOS: + *pcurves = suiteb_curves + 2; + *pcurveslen = 2; + break; + default: *pcurves = s->tlsext_ellipticcurvelist; *pcurveslen = s->tlsext_ellipticcurvelist_length; } - /* If not set use default: for now static structure */ if (!*pcurves) { *pcurves = eccurves_default; @@ -338,8 +360,28 @@ int tls1_check_curve(SSL *s, const unsigned char *p, size_t len) { const unsigned char *curves; size_t curveslen, i; + unsigned int suiteb_flags = tls1_suiteb(s); if (len != 3 || p[0] != NAMED_CURVE_TYPE) return 0; + /* Check curve matches Suite B preferences */ + if (suiteb_flags) + { + unsigned long cid = s->s3->tmp.new_cipher->id; + if (p[1]) + return 0; + if (cid == TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) + { + if (p[2] != TLSEXT_curve_P_256) + return 0; + } + else if (cid == TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) + { + if (p[2] != TLSEXT_curve_P_384) + return 0; + } + else /* Should never happen */ + return 0; + } tls1_get_curvelist(s, 0, &curves, &curveslen); for (i = 0; i < curveslen; i += 2, curves += 2) { @@ -350,7 +392,8 @@ int tls1_check_curve(SSL *s, const unsigned char *p, size_t len) } /* Return nth shared curve. If nmatch == -1 return number of - * matches. + * matches. For nmatch == -2 return the NID of the curve to use for + * an EC tmp key. */ int tls1_shared_curve(SSL *s, int nmatch) @@ -361,6 +404,25 @@ int tls1_shared_curve(SSL *s, int nmatch) /* Can't do anything on client side */ if (s->server == 0) return -1; + if (nmatch == -2) + { + if (tls1_suiteb(s)) + { + /* For Suite B ciphersuite determines curve: we + * already know these are acceptable due to previous + * checks. + */ + unsigned long cid = s->s3->tmp.new_cipher->id; + if (cid == TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) + return NID_X9_62_prime256v1; /* P-256 */ + if (cid == TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) + return NID_secp384r1; /* P-384 */ + /* Should never happen */ + return NID_undef; + } + /* If not Suite B just return first preference shared curve */ + nmatch = 0; + } tls1_get_curvelist(s, !!(s->options & SSL_OP_CIPHER_SERVER_PREFERENCE), &supp, &supplen); tls1_get_curvelist(s, !(s->options & SSL_OP_CIPHER_SERVER_PREFERENCE), @@ -540,6 +602,8 @@ static int tls1_check_ec_key(SSL *s, if (i == plen) return 0; } + if (!curve_id) + return 1; /* Check curve is consistent with client and server preferences */ for (j = 0; j <= 1; j++) { @@ -551,6 +615,9 @@ static int tls1_check_ec_key(SSL *s, } if (i == plen) return 0; + /* For clients can only check sent curve list */ + if (!s->server) + return 1; } return 1; } @@ -558,7 +625,7 @@ static int tls1_check_ec_key(SSL *s, /* Check cert parameters compatible with extensions: currently just checks * EC certificates have compatible curves and compression. */ -static int tls1_check_cert_param(SSL *s, X509 *x) +static int tls1_check_cert_param(SSL *s, X509 *x, int set_ee_md) { unsigned char comp_id, curve_id[2]; EVP_PKEY *pkey; @@ -576,13 +643,82 @@ static int tls1_check_cert_param(SSL *s, X509 *x) EVP_PKEY_free(pkey); if (!rv) return 0; - return tls1_check_ec_key(s, curve_id, &comp_id); + /* Can't check curve_id for client certs as we don't have a + * supported curves extension. + */ + rv = tls1_check_ec_key(s, s->server ? curve_id : NULL, &comp_id); + if (!rv) + return 0; + /* Special case for suite B. We *MUST* sign using SHA256+P-256 or + * SHA384+P-384, adjust digest if necessary. + */ + if (set_ee_md && tls1_suiteb(s)) + { + int check_md; + size_t i; + CERT *c = s->cert; + if (curve_id[0]) + return 0; + /* Check to see we have necessary signing algorithm */ + if (curve_id[1] == TLSEXT_curve_P_256) + check_md = NID_ecdsa_with_SHA256; + else if (curve_id[1] == TLSEXT_curve_P_384) + check_md = NID_ecdsa_with_SHA384; + else + return 0; /* Should never happen */ + for (i = 0; i < c->shared_sigalgslen; i++) + if (check_md == c->shared_sigalgs[i].signandhash_nid) + break; + if (i == c->shared_sigalgslen) + return 0; + if (set_ee_md == 2) + { + if (check_md == NID_ecdsa_with_SHA256) + c->pkeys[SSL_PKEY_ECC].digest = EVP_sha256(); + else + c->pkeys[SSL_PKEY_ECC].digest = EVP_sha384(); + } + } + return rv; } /* Check EC temporary key is compatible with client extensions */ -int tls1_check_ec_tmp_key(SSL *s) +int tls1_check_ec_tmp_key(SSL *s, unsigned long cid) { unsigned char curve_id[2]; EC_KEY *ec = s->cert->ecdh_tmp; + /* If Suite B, AES128 MUST use P-256 and AES256 MUST use P-384, + * no other curves permitted. + */ + if (tls1_suiteb(s)) + { + /* Curve to check determined by ciphersuite */ + if (cid == TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) + curve_id[1] = TLSEXT_curve_P_256; + else if (cid == TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) + curve_id[1] = TLSEXT_curve_P_384; + else + return 0; + curve_id[0] = 0; + /* Check this curve is acceptable */ + if (!tls1_check_ec_key(s, curve_id, NULL)) + return 0; + /* If auto or setting curve from callback assume OK */ + if (s->cert->ecdh_tmp_auto || s->cert->ecdh_tmp_cb) + return 1; + /* Otherwise check curve is acceptable */ + else + { + unsigned char curve_tmp[2]; + if (!ec) + return 0; + if (!tls1_set_ec_id(curve_tmp, NULL, ec)) + return 0; + if (!curve_tmp[0] || curve_tmp[1] == curve_id[1]) + return 1; + return 0; + } + + } if (s->cert->ecdh_tmp_auto) { /* Need a shared curve */ @@ -655,8 +791,31 @@ static unsigned char tls12_sigalgs[] = { #endif }; +static unsigned char suiteb_sigalgs[] = { + tlsext_sigalg_ecdsa(TLSEXT_hash_sha256) + tlsext_sigalg_ecdsa(TLSEXT_hash_sha384) +}; + size_t tls12_get_psigalgs(SSL *s, const unsigned char **psigs) { + /* If Suite B mode use Suite B sigalgs only, ignore any other + * preferences. + */ + switch (tls1_suiteb(s)) + { + case SSL_CERT_FLAG_SUITEB_128_LOS: + *psigs = suiteb_sigalgs; + return sizeof(suiteb_sigalgs); + + case SSL_CERT_FLAG_SUITEB_128_LOS_ONLY: + *psigs = suiteb_sigalgs; + return 2; + + case SSL_CERT_FLAG_SUITEB_192_LOS: + *psigs = suiteb_sigalgs + 2; + return 2; + } + /* If server use client authentication sigalgs if not NULL */ if (s->server && s->cert->client_sigalgs) { @@ -698,6 +857,44 @@ int tls12_check_peer_sigalg(const EVP_MD **pmd, SSL *s, SSLerr(SSL_F_TLS12_CHECK_PEER_SIGALG,SSL_R_WRONG_SIGNATURE_TYPE); return 0; } + if (pkey->type == EVP_PKEY_EC) + { + unsigned char curve_id[2], comp_id; + /* Check compression and curve matches extensions */ + if (!tls1_set_ec_id(curve_id, &comp_id, pkey->pkey.ec)) + return 0; + if (!s->server && !tls1_check_ec_key(s, curve_id, &comp_id)) + return 0; + /* If Suite B only P-384+SHA384 or P-256+SHA-256 allowed */ + if (tls1_suiteb(s)) + { + if (curve_id[0]) + return 0; + if (curve_id[1] == TLSEXT_curve_P_256) + { + if (sig[0] != TLSEXT_hash_sha256) + { + SSLerr(SSL_F_TLS12_CHECK_PEER_SIGALG, + SSL_R_ILLEGAL_SUITEB_DIGEST); + return 0; + } + } + else if (curve_id[1] == TLSEXT_curve_P_384) + { + if (sig[0] != TLSEXT_hash_sha384) + { + SSLerr(SSL_F_TLS12_CHECK_PEER_SIGALG, + SSL_R_ILLEGAL_SUITEB_DIGEST); + return 0; + } + } + else + return 0; + } + } + else if (tls1_suiteb(s)) + return 0; + /* Check signature matches a type we sent */ sent_sigslen = tls12_get_psigalgs(s, &sent_sigs); for (i = 0; i < sent_sigslen; i+=2, sent_sigs+=2) @@ -706,7 +903,7 @@ int tls12_check_peer_sigalg(const EVP_MD **pmd, SSL *s, break; } /* Allow fallback to SHA1 if not strict mode */ - if (i == sent_sigslen && (sig[0] != TLSEXT_hash_sha1 || s->cert->cert_flags & SSL_CERT_FLAG_TLS_STRICT)) + if (i == sent_sigslen && (sig[0] != TLSEXT_hash_sha1 || s->cert->cert_flags & SSL_CERT_FLAGS_CHECK_TLS_STRICT)) { SSLerr(SSL_F_TLS12_CHECK_PEER_SIGALG,SSL_R_WRONG_SIGNATURE_TYPE); return 0; @@ -3123,27 +3320,21 @@ static int tls1_set_shared_sigalgs(SSL *s) size_t nmatch; TLS_SIGALGS *salgs = NULL; CERT *c = s->cert; + unsigned int is_suiteb = tls1_suiteb(s); /* If client use client signature algorithms if not NULL */ - if (!s->server && c->client_sigalgs) + if (!s->server && c->client_sigalgs && !is_suiteb) { conf = c->client_sigalgs; conflen = c->client_sigalgslen; } - else if (c->conf_sigalgs) + else if (c->conf_sigalgs && !is_suiteb) { conf = c->conf_sigalgs; conflen = c->conf_sigalgslen; } else - { - conf = tls12_sigalgs; - conflen = sizeof(tls12_sigalgs); -#ifdef OPENSSL_FIPS - if (FIPS_mode()) - conflen -= 2; -#endif - } - if(s->options & SSL_OP_CIPHER_SERVER_PREFERENCE) + conflen = tls12_get_psigalgs(s, &conf); + if(s->options & SSL_OP_CIPHER_SERVER_PREFERENCE || is_suiteb) { pref = conf; preflen = conflen; @@ -3214,7 +3405,7 @@ int tls1_process_sigalgs(SSL *s, const unsigned char *data, int dsize) /* In strict mode leave unset digests as NULL to indicate we can't * use the certificate for signing. */ - if (!(s->cert->cert_flags & SSL_CERT_FLAG_TLS_STRICT)) + if (!(s->cert->cert_flags & SSL_CERT_FLAGS_CHECK_TLS_STRICT)) { /* Set any remaining keys to default values. NOTE: if alg is * not supported it stays as NULL. @@ -3588,14 +3779,22 @@ int tls1_check_chain(SSL *s, X509 *x, EVP_PKEY *pk, STACK_OF(X509) *chain, int check_flags = 0, strict_mode; CERT_PKEY *cpk = NULL; CERT *c = s->cert; - /* idx != -1 means checking server chains */ + unsigned int suiteb_flags = tls1_suiteb(s); + /* idx == -1 means checking server chains */ if (idx != -1) { - cpk = c->pkeys + idx; + /* idx == -2 means checking client certificate chains */ + if (idx == -2) + { + cpk = c->key; + idx = cpk - c->pkeys; + } + else + cpk = c->pkeys + idx; x = cpk->x509; pk = cpk->privatekey; chain = cpk->chain; - strict_mode = c->cert_flags & SSL_CERT_FLAG_TLS_STRICT; + strict_mode = c->cert_flags & SSL_CERT_FLAGS_CHECK_TLS_STRICT; /* If no cert or key, forget it */ if (!x || !pk) goto end; @@ -3608,13 +3807,27 @@ int tls1_check_chain(SSL *s, X509 *x, EVP_PKEY *pk, STACK_OF(X509) *chain, if (idx == -1) goto end; cpk = c->pkeys + idx; - if (c->cert_flags & SSL_CERT_FLAG_TLS_STRICT) + if (c->cert_flags & SSL_CERT_FLAGS_CHECK_TLS_STRICT) check_flags = CERT_PKEY_STRICT_FLAGS; else check_flags = CERT_PKEY_VALID_FLAGS; strict_mode = 1; } + if (suiteb_flags) + { + int ok; + if (check_flags) + check_flags |= CERT_PKEY_SUITEB; + ok = X509_chain_check_suiteb(NULL, x, chain, suiteb_flags); + if (ok != X509_V_OK) + { + if (check_flags) + rv |= CERT_PKEY_SUITEB; + else + goto end; + } + } /* Check all signature algorithms are consistent with * signature algorithms extension if TLS 1.2 or later @@ -3702,8 +3915,8 @@ int tls1_check_chain(SSL *s, X509 *x, EVP_PKEY *pk, STACK_OF(X509) *chain, else if(check_flags) rv |= CERT_PKEY_EE_SIGNATURE|CERT_PKEY_CA_SIGNATURE; skip_sigs: - /* Check cert parameters are consistent: server certs only */ - if (!s->server || tls1_check_cert_param(s, x)) + /* Check cert parameters are consistent */ + if (tls1_check_cert_param(s, x, check_flags ? 1 : 2)) rv |= CERT_PKEY_EE_PARAM; else if (!check_flags) goto end; @@ -3715,7 +3928,8 @@ int tls1_check_chain(SSL *s, X509 *x, EVP_PKEY *pk, STACK_OF(X509) *chain, rv |= CERT_PKEY_CA_PARAM; for (i = 0; i < sk_X509_num(chain); i++) { - if (!tls1_check_cert_param(s, sk_X509_value(chain, i))) + X509 *ca = sk_X509_value(chain, i); + if (!tls1_check_cert_param(s, ca, 0)) { if (check_flags) { @@ -3832,7 +4046,11 @@ int tls1_check_chain(SSL *s, X509 *x, EVP_PKEY *pk, STACK_OF(X509) *chain, if (rv & CERT_PKEY_VALID) cpk->valid_flags = rv; else - cpk->valid_flags = 0; + { + /* Preserve explicit sign flag, clear rest */ + cpk->valid_flags &= CERT_PKEY_EXPLICIT_SIGN; + return 0; + } } return rv; } diff --git a/ssl/tls1.h b/ssl/tls1.h index 4d087e0a2b..c59a02fee3 100644 --- a/ssl/tls1.h +++ b/ssl/tls1.h @@ -285,6 +285,11 @@ extern "C" { /* Flag set for unrecognised algorithms */ #define TLSEXT_nid_unknown 0x1000000 +/* ECC curves */ + +#define TLSEXT_curve_P_256 23 +#define TLSEXT_curve_P_384 24 + #ifndef OPENSSL_NO_TLSEXT #define TLSEXT_MAXLEN_host_name 255 -- 2.34.1