From 620c97b671a9c7bc31ca36a24b2242aa1aa80022 Mon Sep 17 00:00:00 2001 From: Kurt Roeckx Date: Sun, 9 Feb 2020 19:28:15 +0100 Subject: [PATCH] Check that ed25519 and ed448 are allowed by the security level Signature algorithms not using an MD weren't checked that they're allowed by the security level. Reviewed-by: Matt Caswell GH: #10785 --- ssl/t1_lib.c | 59 +++++++++++----- test/ssl-tests/28-seclevel.conf | 106 +++++++++++++++++++++-------- test/ssl-tests/28-seclevel.conf.in | 31 ++++++++- 3 files changed, 146 insertions(+), 50 deletions(-) diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 103a8f18bb..aedb521015 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -1075,6 +1075,31 @@ int tls_check_sigalg_curve(const SSL *s, int curve) } #endif +/* + * Return the number of security bits for the signature algorithm, or 0 on + * error. + */ +static int sigalg_security_bits(SSL_CTX *ctx, const SIGALG_LOOKUP *lu) +{ + const EVP_MD *md = NULL; + int secbits = 0; + + if (!tls1_lookup_md(ctx, lu, &md)) + return 0; + if (md != NULL) + { + /* Security bits: half digest bits */ + secbits = EVP_MD_size(md) * 4; + } else { + /* Values from https://tools.ietf.org/html/rfc8032#section-8.5 */ + if (lu->sigalg == TLSEXT_SIGALG_ed25519) + secbits = 128; + else if (lu->sigalg == TLSEXT_SIGALG_ed448) + secbits = 224; + } + return secbits; +} + /* * Check signature algorithm is consistent with sent supported signature * algorithms and if so set relevant digest and signature scheme in @@ -1088,6 +1113,7 @@ int tls12_check_peer_sigalg(SSL *s, uint16_t sig, EVP_PKEY *pkey) size_t sent_sigslen, i, cidx; int pkeyid = EVP_PKEY_id(pkey); const SIGALG_LOOKUP *lu; + int secbits = 0; /* Should never happen */ if (pkeyid == -1) @@ -1189,20 +1215,20 @@ int tls12_check_peer_sigalg(SSL *s, uint16_t sig, EVP_PKEY *pkey) SSL_R_UNKNOWN_DIGEST); return 0; } - if (md != NULL) { - /* - * Make sure security callback allows algorithm. For historical - * reasons we have to pass the sigalg as a two byte char array. - */ - sigalgstr[0] = (sig >> 8) & 0xff; - sigalgstr[1] = sig & 0xff; - if (!ssl_security(s, SSL_SECOP_SIGALG_CHECK, - EVP_MD_size(md) * 4, EVP_MD_type(md), - (void *)sigalgstr)) { - SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, SSL_F_TLS12_CHECK_PEER_SIGALG, - SSL_R_WRONG_SIGNATURE_TYPE); - return 0; - } + /* + * Make sure security callback allows algorithm. For historical + * reasons we have to pass the sigalg as a two byte char array. + */ + sigalgstr[0] = (sig >> 8) & 0xff; + sigalgstr[1] = sig & 0xff; + secbits = sigalg_security_bits(s->ctx, lu); + if (secbits == 0 || + !ssl_security(s, SSL_SECOP_SIGALG_CHECK, secbits, + md != NULL ? EVP_MD_type(md) : NID_undef, + (void *)sigalgstr)) { + SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, SSL_F_TLS12_CHECK_PEER_SIGALG, + SSL_R_WRONG_SIGNATURE_TYPE); + return 0; } /* Store the sigalg the peer uses */ s->s3.tmp.peer_sigalg = lu; @@ -1726,11 +1752,8 @@ static int tls12_sigalg_allowed(const SSL *s, int op, const SIGALG_LOOKUP *lu) } } - if (lu->hash == NID_undef) - return 1; - /* Security bits: half digest bits */ - secbits = EVP_MD_size(ssl_md(s->ctx, lu->hash_idx)) * 4; /* Finally see if security callback allows it */ + secbits = sigalg_security_bits(s->ctx, lu); sigalgstr[0] = (lu->sigalg >> 8) & 0xff; sigalgstr[1] = lu->sigalg & 0xff; return ssl_security(s, op, secbits, lu->hash, (void *)sigalgstr); diff --git a/test/ssl-tests/28-seclevel.conf b/test/ssl-tests/28-seclevel.conf index 04a0c4fbd5..99fa8109c3 100644 --- a/test/ssl-tests/28-seclevel.conf +++ b/test/ssl-tests/28-seclevel.conf @@ -1,11 +1,13 @@ # Generated with generate_ssl_tests.pl -num_tests = 4 +num_tests = 6 test-0 = 0-SECLEVEL 3 with default key -test-1 = 1-SECLEVEL 3 with ED448 key -test-2 = 2-SECLEVEL 3 with P-384 key, X25519 ECDHE -test-3 = 3-SECLEVEL 3 with ED448 key, TLSv1.2 +test-1 = 1-SECLEVEL 4 with ED448 key +test-2 = 2-SECLEVEL 5 server with ED448 key +test-3 = 3-SECLEVEL 5 client with ED448 key +test-4 = 4-SECLEVEL 3 with P-384 key, X25519 ECDHE +test-5 = 5-SECLEVEL 3 with ED448 key, TLSv1.2 # =========================================================== [0-SECLEVEL 3 with default key] @@ -31,20 +33,20 @@ ExpectedResult = ServerFail # =========================================================== -[1-SECLEVEL 3 with ED448 key] -ssl_conf = 1-SECLEVEL 3 with ED448 key-ssl +[1-SECLEVEL 4 with ED448 key] +ssl_conf = 1-SECLEVEL 4 with ED448 key-ssl -[1-SECLEVEL 3 with ED448 key-ssl] -server = 1-SECLEVEL 3 with ED448 key-server -client = 1-SECLEVEL 3 with ED448 key-client +[1-SECLEVEL 4 with ED448 key-ssl] +server = 1-SECLEVEL 4 with ED448 key-server +client = 1-SECLEVEL 4 with ED448 key-client -[1-SECLEVEL 3 with ED448 key-server] +[1-SECLEVEL 4 with ED448 key-server] Certificate = ${ENV::TEST_CERTS_DIR}/server-ed448-cert.pem -CipherString = DEFAULT:@SECLEVEL=3 +CipherString = DEFAULT:@SECLEVEL=4 PrivateKey = ${ENV::TEST_CERTS_DIR}/server-ed448-key.pem -[1-SECLEVEL 3 with ED448 key-client] -CipherString = DEFAULT +[1-SECLEVEL 4 with ED448 key-client] +CipherString = DEFAULT:@SECLEVEL=4 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/root-ed448-cert.pem VerifyMode = Peer @@ -54,49 +56,95 @@ ExpectedResult = Success # =========================================================== -[2-SECLEVEL 3 with P-384 key, X25519 ECDHE] -ssl_conf = 2-SECLEVEL 3 with P-384 key, X25519 ECDHE-ssl +[2-SECLEVEL 5 server with ED448 key] +ssl_conf = 2-SECLEVEL 5 server with ED448 key-ssl + +[2-SECLEVEL 5 server with ED448 key-ssl] +server = 2-SECLEVEL 5 server with ED448 key-server +client = 2-SECLEVEL 5 server with ED448 key-client + +[2-SECLEVEL 5 server with ED448 key-server] +Certificate = ${ENV::TEST_CERTS_DIR}/server-ed448-cert.pem +CipherString = DEFAULT:@SECLEVEL=5 +PrivateKey = ${ENV::TEST_CERTS_DIR}/server-ed448-key.pem + +[2-SECLEVEL 5 server with ED448 key-client] +CipherString = DEFAULT:@SECLEVEL=4 +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/root-ed448-cert.pem +VerifyMode = Peer + +[test-2] +ExpectedResult = ServerFail + + +# =========================================================== + +[3-SECLEVEL 5 client with ED448 key] +ssl_conf = 3-SECLEVEL 5 client with ED448 key-ssl -[2-SECLEVEL 3 with P-384 key, X25519 ECDHE-ssl] -server = 2-SECLEVEL 3 with P-384 key, X25519 ECDHE-server -client = 2-SECLEVEL 3 with P-384 key, X25519 ECDHE-client +[3-SECLEVEL 5 client with ED448 key-ssl] +server = 3-SECLEVEL 5 client with ED448 key-server +client = 3-SECLEVEL 5 client with ED448 key-client -[2-SECLEVEL 3 with P-384 key, X25519 ECDHE-server] +[3-SECLEVEL 5 client with ED448 key-server] +Certificate = ${ENV::TEST_CERTS_DIR}/server-ed448-cert.pem +CipherString = DEFAULT:@SECLEVEL=4 +PrivateKey = ${ENV::TEST_CERTS_DIR}/server-ed448-key.pem + +[3-SECLEVEL 5 client with ED448 key-client] +CipherString = DEFAULT:@SECLEVEL=5 +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/root-ed448-cert.pem +VerifyMode = Peer + +[test-3] +ExpectedResult = ServerFail + + +# =========================================================== + +[4-SECLEVEL 3 with P-384 key, X25519 ECDHE] +ssl_conf = 4-SECLEVEL 3 with P-384 key, X25519 ECDHE-ssl + +[4-SECLEVEL 3 with P-384 key, X25519 ECDHE-ssl] +server = 4-SECLEVEL 3 with P-384 key, X25519 ECDHE-server +client = 4-SECLEVEL 3 with P-384 key, X25519 ECDHE-client + +[4-SECLEVEL 3 with P-384 key, X25519 ECDHE-server] Certificate = ${ENV::TEST_CERTS_DIR}/p384-server-cert.pem CipherString = DEFAULT:@SECLEVEL=3 Groups = X25519 PrivateKey = ${ENV::TEST_CERTS_DIR}/p384-server-key.pem -[2-SECLEVEL 3 with P-384 key, X25519 ECDHE-client] +[4-SECLEVEL 3 with P-384 key, X25519 ECDHE-client] CipherString = ECDHE:@SECLEVEL=3 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/p384-root.pem VerifyMode = Peer -[test-2] +[test-4] ExpectedResult = Success # =========================================================== -[3-SECLEVEL 3 with ED448 key, TLSv1.2] -ssl_conf = 3-SECLEVEL 3 with ED448 key, TLSv1.2-ssl +[5-SECLEVEL 3 with ED448 key, TLSv1.2] +ssl_conf = 5-SECLEVEL 3 with ED448 key, TLSv1.2-ssl -[3-SECLEVEL 3 with ED448 key, TLSv1.2-ssl] -server = 3-SECLEVEL 3 with ED448 key, TLSv1.2-server -client = 3-SECLEVEL 3 with ED448 key, TLSv1.2-client +[5-SECLEVEL 3 with ED448 key, TLSv1.2-ssl] +server = 5-SECLEVEL 3 with ED448 key, TLSv1.2-server +client = 5-SECLEVEL 3 with ED448 key, TLSv1.2-client -[3-SECLEVEL 3 with ED448 key, TLSv1.2-server] +[5-SECLEVEL 3 with ED448 key, TLSv1.2-server] Certificate = ${ENV::TEST_CERTS_DIR}/server-ed448-cert.pem CipherString = DEFAULT:@SECLEVEL=3 MaxProtocol = TLSv1.2 PrivateKey = ${ENV::TEST_CERTS_DIR}/server-ed448-key.pem -[3-SECLEVEL 3 with ED448 key, TLSv1.2-client] +[5-SECLEVEL 3 with ED448 key, TLSv1.2-client] CipherString = DEFAULT VerifyCAFile = ${ENV::TEST_CERTS_DIR}/root-ed448-cert.pem VerifyMode = Peer -[test-3] +[test-5] ExpectedResult = Success diff --git a/test/ssl-tests/28-seclevel.conf.in b/test/ssl-tests/28-seclevel.conf.in index f2cdc477ba..aba5007947 100644 --- a/test/ssl-tests/28-seclevel.conf.in +++ b/test/ssl-tests/28-seclevel.conf.in @@ -23,13 +23,38 @@ our @tests = ( our @tests_ec = ( { - name => "SECLEVEL 3 with ED448 key", - server => { "CipherString" => "DEFAULT:\@SECLEVEL=3", + name => "SECLEVEL 4 with ED448 key", + server => { "CipherString" => "DEFAULT:\@SECLEVEL=4", "Certificate" => test_pem("server-ed448-cert.pem"), "PrivateKey" => test_pem("server-ed448-key.pem") }, - client => { "VerifyCAFile" => test_pem("root-ed448-cert.pem") }, + client => { "CipherString" => "DEFAULT:\@SECLEVEL=4", + "VerifyCAFile" => test_pem("root-ed448-cert.pem") }, test => { "ExpectedResult" => "Success" }, }, + { + # The Ed488 signature algorithm will not be enabled. + # Because of the config order, the certificate is first loaded, and + # then the security level is chaged. If you try this with s_server + # the order will be reversed and it will instead fail to load the key. + name => "SECLEVEL 5 server with ED448 key", + server => { "CipherString" => "DEFAULT:\@SECLEVEL=5", + "Certificate" => test_pem("server-ed448-cert.pem"), + "PrivateKey" => test_pem("server-ed448-key.pem") }, + client => { "CipherString" => "DEFAULT:\@SECLEVEL=4", + "VerifyCAFile" => test_pem("root-ed448-cert.pem") }, + test => { "ExpectedResult" => "ServerFail" }, + }, + { + # The client will not sent the Ed488 signature algorithm, so the server + # doesn't have a useable signature algorithm for the certificate. + name => "SECLEVEL 5 client with ED448 key", + server => { "CipherString" => "DEFAULT:\@SECLEVEL=4", + "Certificate" => test_pem("server-ed448-cert.pem"), + "PrivateKey" => test_pem("server-ed448-key.pem") }, + client => { "CipherString" => "DEFAULT:\@SECLEVEL=5", + "VerifyCAFile" => test_pem("root-ed448-cert.pem") }, + test => { "ExpectedResult" => "ServerFail" }, + }, { name => "SECLEVEL 3 with P-384 key, X25519 ECDHE", server => { "CipherString" => "DEFAULT:\@SECLEVEL=3", -- 2.25.1