Support Ed25519 in TLS.

This only works at TLS 1.2 and above as, before TLS 1.2, there is no way
to advertise support for Ed25519 or negotiate the correct signature
algorithm. Add tests for this accordingly.

For now, this is disabled by default on the verifying side but may be
enabled per SSL_CTX. Notably, projects like Chromium which use an
external verifier may need changes elsewhere before they can enable it.
(On the signing side, we can assume that if the caller gave us an
Ed25519 certificate, they mean for us to use it.)

BUG=187

Change-Id: Id25b0a677dcbe205ddd26d8dbba11c04bb520756
Reviewed-on: https://boringssl-review.googlesource.com/14450
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
David Benjamin 2017-03-28 15:38:29 -05:00 committed by Adam Langley
parent 0aef1686de
commit 69522117a6
18 changed files with 378 additions and 149 deletions

View File

@ -947,6 +947,7 @@ OPENSSL_EXPORT int SSL_set_ocsp_response(SSL *ssl,
#define SSL_SIGN_RSA_PSS_SHA256 0x0804
#define SSL_SIGN_RSA_PSS_SHA384 0x0805
#define SSL_SIGN_RSA_PSS_SHA512 0x0806
#define SSL_SIGN_ED25519 0x0807
/* SSL_SIGN_RSA_PKCS1_MD5_SHA1 is an internal signature algorithm used to
* specify raw RSASSA-PKCS1-v1_5 with an MD5/SHA-1 concatenation, as used in TLS
@ -2405,6 +2406,10 @@ OPENSSL_EXPORT int SSL_set0_verify_cert_store(SSL *ssl, X509_STORE *store);
* reference to |store| will be taken. */
OPENSSL_EXPORT int SSL_set1_verify_cert_store(SSL *ssl, X509_STORE *store);
/* SSL_CTX_set_ed25519_enabled configures whether |ctx| advertises support for
* the Ed25519 signature algorithm. */
OPENSSL_EXPORT void SSL_CTX_set_ed25519_enabled(SSL_CTX *ctx, int enabled);
/* Client certificate CA list.
*
@ -4292,6 +4297,9 @@ struct ssl_ctx_st {
/* allow_unknown_alpn_protos is one if the client allows unsolicited ALPN
* protocols from the peer. */
unsigned allow_unknown_alpn_protos:1;
/* ed25519_enabled is one if Ed25519 is advertised in the handshake. */
unsigned ed25519_enabled:1;
};

View File

@ -695,12 +695,9 @@ static void ssl_get_compatible_server_ciphers(SSL_HANDSHAKE *hs,
uint32_t mask_a = 0;
if (ssl_has_certificate(ssl)) {
int type = EVP_PKEY_id(hs->local_pubkey);
if (type == EVP_PKEY_RSA) {
mask_a |= ssl_cipher_auth_mask_for_key(hs->local_pubkey);
if (EVP_PKEY_id(hs->local_pubkey) == EVP_PKEY_RSA) {
mask_k |= SSL_kRSA;
mask_a |= SSL_aRSA;
} else if (type == EVP_PKEY_EC) {
mask_a |= SSL_aECDSA;
}
}
@ -1321,17 +1318,10 @@ static int ssl3_send_certificate_request(SSL_HANDSHAKE *hs) {
}
if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
const uint16_t *sigalgs;
size_t num_sigalgs = tls12_get_verify_sigalgs(ssl, &sigalgs);
if (!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb)) {
if (!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb) ||
!tls12_add_verify_sigalgs(ssl, &sigalgs_cbb)) {
goto err;
}
for (size_t i = 0; i < num_sigalgs; i++) {
if (!CBB_add_u16(&sigalgs_cbb, sigalgs[i])) {
goto err;
}
}
}
if (!ssl_add_client_CA_list(ssl, &body) ||

View File

@ -238,9 +238,9 @@ int ssl_create_cipher_list(
/* ssl_cipher_get_value returns the cipher suite id of |cipher|. */
uint16_t ssl_cipher_get_value(const SSL_CIPHER *cipher);
/* ssl_cipher_get_key_type returns the |EVP_PKEY_*| value corresponding to the
* server key used in |cipher| or |EVP_PKEY_NONE| if there is none. */
int ssl_cipher_get_key_type(const SSL_CIPHER *cipher);
/* ssl_cipher_auth_mask_for_key returns the mask of cipher |algorithm_auth|
* values suitable for use with |key| in TLS 1.2 and below. */
uint32_t ssl_cipher_auth_mask_for_key(const EVP_PKEY *key);
/* ssl_cipher_uses_certificate_auth returns one if |cipher| authenticates the
* server and, optionally, the client with a certificate. Otherwise it returns
@ -1287,9 +1287,9 @@ int tls1_parse_peer_sigalgs(SSL_HANDSHAKE *hs, const CBS *sigalgs);
* supported. It returns one on success and zero on error. */
int tls1_choose_signature_algorithm(SSL_HANDSHAKE *hs, uint16_t *out);
/* tls12_get_verify_sigalgs sets |*out| to the signature algorithms acceptable
* for the peer signature and returns the length of the list. */
size_t tls12_get_verify_sigalgs(const SSL *ssl, const uint16_t **out);
/* tls12_add_verify_sigalgs adds the signature algorithms acceptable for the
* peer signature to |out|. It returns one on success and zero on error. */
int tls12_add_verify_sigalgs(const SSL *ssl, CBB *out);
/* tls12_check_peer_sigalg checks if |sigalg| is acceptable for the peer
* signature. It returns one on success and zero on error, setting |*out_alert|

View File

@ -825,29 +825,28 @@ int ssl_check_leaf_certificate(SSL_HANDSHAKE *hs, EVP_PKEY *pkey,
assert(ssl3_protocol_version(ssl) < TLS1_3_VERSION);
/* Check the certificate's type matches the cipher. */
int expected_type = ssl_cipher_get_key_type(hs->new_cipher);
assert(expected_type != EVP_PKEY_NONE);
if (pkey->type != expected_type) {
if (!(hs->new_cipher->algorithm_auth & ssl_cipher_auth_mask_for_key(pkey))) {
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CERTIFICATE_TYPE);
return 0;
}
if (hs->new_cipher->algorithm_auth & SSL_aECDSA) {
/* Check key usages for all key types but RSA. This is needed to distinguish
* ECDH certificates, which we do not support, from ECDSA certificates. In
* principle, we should check RSA key usages based on cipher, but this breaks
* buggy antivirus deployments. Other key types are always used for signing.
*
* TODO(davidben): Get more recent data on RSA key usages. */
if (EVP_PKEY_id(pkey) != EVP_PKEY_RSA) {
CBS leaf_cbs;
CBS_init(&leaf_cbs, CRYPTO_BUFFER_data(leaf), CRYPTO_BUFFER_len(leaf));
/* ECDSA and ECDH certificates use the same public key format. Instead,
* they are distinguished by the key usage extension in the certificate. */
if (!ssl_cert_check_digital_signature_key_usage(&leaf_cbs)) {
return 0;
}
}
EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey);
if (ec_key == NULL) {
OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECC_CERT);
return 0;
}
if (EVP_PKEY_id(pkey) == EVP_PKEY_EC) {
/* Check the key's group and point format are acceptable. */
EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey);
uint16_t group_id;
if (!ssl_nid_to_group_id(
&group_id, EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key))) ||

View File

@ -1830,16 +1830,17 @@ const char *SSL_COMP_get_name(const COMP_METHOD *comp) { return NULL; }
void SSL_COMP_free_compression_methods(void) {}
int ssl_cipher_get_key_type(const SSL_CIPHER *cipher) {
uint32_t alg_a = cipher->algorithm_auth;
if (alg_a & SSL_aECDSA) {
return EVP_PKEY_EC;
} else if (alg_a & SSL_aRSA) {
return EVP_PKEY_RSA;
uint32_t ssl_cipher_auth_mask_for_key(const EVP_PKEY *key) {
switch (EVP_PKEY_id(key)) {
case EVP_PKEY_RSA:
return SSL_aRSA;
case EVP_PKEY_EC:
case EVP_PKEY_ED25519:
/* Ed25519 keys in TLS 1.2 repurpose the ECDSA ciphers. */
return SSL_aECDSA;
default:
return 0;
}
return EVP_PKEY_NONE;
}
int ssl_cipher_uses_certificate_auth(const SSL_CIPHER *cipher) {

View File

@ -70,7 +70,8 @@
int ssl_is_key_type_supported(int key_type) {
return key_type == EVP_PKEY_RSA || key_type == EVP_PKEY_EC;
return key_type == EVP_PKEY_RSA || key_type == EVP_PKEY_EC ||
key_type == EVP_PKEY_ED25519;
}
static int ssl_set_pkey(CERT *cert, EVP_PKEY *pkey) {
@ -321,6 +322,8 @@ static const SSL_SIGNATURE_ALGORITHM kSignatureAlgorithms[] = {
0},
{SSL_SIGN_ECDSA_SECP521R1_SHA512, EVP_PKEY_EC, NID_secp521r1, &EVP_sha512,
0},
{SSL_SIGN_ED25519, EVP_PKEY_ED25519, NID_undef, NULL, 0},
};
static const SSL_SIGNATURE_ALGORITHM *get_signature_algorithm(uint16_t sigalg) {
@ -384,6 +387,11 @@ static int setup_ctx(SSL *ssl, EVP_PKEY_CTX *ctx, uint16_t sigalg) {
return 1;
}
static int legacy_sign_digest_supported(const SSL_SIGNATURE_ALGORITHM *alg) {
return (alg->pkey_type == EVP_PKEY_EC || alg->pkey_type == EVP_PKEY_RSA) &&
!alg->is_rsa_pss;
}
enum ssl_private_key_result_t ssl_private_key_sign(
SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
uint16_t sigalg, const uint8_t *in, size_t in_len) {
@ -397,8 +405,7 @@ enum ssl_private_key_result_t ssl_private_key_sign(
* |SSL_PRIVATE_KEY_METHOD|s. */
const SSL_SIGNATURE_ALGORITHM *alg = get_signature_algorithm(sigalg);
if (alg == NULL ||
(alg->pkey_type != EVP_PKEY_EC && alg->pkey_type != EVP_PKEY_RSA) ||
alg->is_rsa_pss) {
!legacy_sign_digest_supported(alg)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY);
return ssl_private_key_failure;
}
@ -473,24 +480,25 @@ int ssl_private_key_supports_signature_algorithm(SSL_HANDSHAKE *hs,
return 0;
}
/* Ensure the RSA key is large enough for the hash. RSASSA-PSS requires that
* emLen be at least hLen + sLen + 2. Both hLen and sLen are the size of the
* hash in TLS. Reasonable RSA key sizes are large enough for the largest
* defined RSASSA-PSS algorithm, but 1024-bit RSA is slightly too small for
* SHA-512. 1024-bit RSA is sometimes used for test credentials, so check the
* size so that we can fall back to another algorithm in that case. */
const SSL_SIGNATURE_ALGORITHM *alg = get_signature_algorithm(sigalg);
if (alg->is_rsa_pss) {
/* Ensure the RSA key is large enough for the hash. RSASSA-PSS requires that
* emLen be at least hLen + sLen + 2. Both hLen and sLen are the size of the
* hash in TLS. Reasonable RSA key sizes are large enough for the largest
* defined RSASSA-PSS algorithm, but 1024-bit RSA is slightly too small for
* SHA-512. 1024-bit RSA is sometimes used for test credentials, so check
* the size so that we can fall back to another algorithm in that case. */
if ((size_t)EVP_PKEY_size(hs->local_pubkey) <
2 * EVP_MD_size(alg->digest_func()) + 2) {
return 0;
}
if (alg->is_rsa_pss &&
(size_t)EVP_PKEY_size(hs->local_pubkey) <
2 * EVP_MD_size(alg->digest_func()) + 2) {
return 0;
}
/* RSA-PSS is only supported by message-based private keys.
* TODO(davidben): Remove this check when sign_digest is gone. */
if (ssl->cert->key_method != NULL && ssl->cert->key_method->sign == NULL) {
return 0;
}
/* Newer algorithms require message-based private keys.
* TODO(davidben): Remove this check when sign_digest is gone. */
if (ssl->cert->key_method != NULL &&
ssl->cert->key_method->sign == NULL &&
!legacy_sign_digest_supported(alg)) {
return 0;
}
return 1;

View File

@ -446,7 +446,8 @@ int tls1_check_group_id(SSL *ssl, uint16_t group_id) {
* BoringSSL. Once the change in Chrome has stuck and the values are finalized,
* restore them. */
static const uint16_t kVerifySignatureAlgorithms[] = {
/* Prefer SHA-256 algorithms. */
/* List our preferred algorithms first. */
SSL_SIGN_ED25519,
SSL_SIGN_ECDSA_SECP256R1_SHA256,
#if !defined(BORINGSSL_ANDROID_SYSTEM)
SSL_SIGN_RSA_PSS_SHA256,
@ -481,7 +482,8 @@ static const uint16_t kVerifySignatureAlgorithms[] = {
* BoringSSL. Once the change in Chrome has stuck and the values are finalized,
* restore them. */
static const uint16_t kSignSignatureAlgorithms[] = {
/* Prefer SHA-256 algorithms. */
/* List our preferred algorithms first. */
SSL_SIGN_ED25519,
SSL_SIGN_ECDSA_SECP256R1_SHA256,
#if !defined(BORINGSSL_ANDROID_SYSTEM)
SSL_SIGN_RSA_PSS_SHA256,
@ -508,16 +510,31 @@ static const uint16_t kSignSignatureAlgorithms[] = {
SSL_SIGN_RSA_PKCS1_SHA1,
};
size_t tls12_get_verify_sigalgs(const SSL *ssl, const uint16_t **out) {
*out = kVerifySignatureAlgorithms;
return OPENSSL_ARRAY_SIZE(kVerifySignatureAlgorithms);
void SSL_CTX_set_ed25519_enabled(SSL_CTX *ctx, int enabled) {
ctx->ed25519_enabled = !!enabled;
}
int tls12_add_verify_sigalgs(const SSL *ssl, CBB *out) {
for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kVerifySignatureAlgorithms); i++) {
if (kVerifySignatureAlgorithms[i] == SSL_SIGN_ED25519 &&
!ssl->ctx->ed25519_enabled) {
continue;
}
if (!CBB_add_u16(out, kVerifySignatureAlgorithms[i])) {
return 0;
}
}
return 1;
}
int tls12_check_peer_sigalg(SSL *ssl, int *out_alert, uint16_t sigalg) {
const uint16_t *verify_sigalgs;
size_t num_verify_sigalgs = tls12_get_verify_sigalgs(ssl, &verify_sigalgs);
for (size_t i = 0; i < num_verify_sigalgs; i++) {
if (sigalg == verify_sigalgs[i]) {
for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kVerifySignatureAlgorithms); i++) {
if (kVerifySignatureAlgorithms[i] == SSL_SIGN_ED25519 &&
!ssl->ctx->ed25519_enabled) {
continue;
}
if (sigalg == kVerifySignatureAlgorithms[i]) {
return 1;
}
}
@ -1031,23 +1048,12 @@ static int ext_sigalgs_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
return 1;
}
const uint16_t *sigalgs;
const size_t num_sigalgs = tls12_get_verify_sigalgs(ssl, &sigalgs);
CBB contents, sigalgs_cbb;
if (!CBB_add_u16(out, TLSEXT_TYPE_signature_algorithms) ||
!CBB_add_u16_length_prefixed(out, &contents) ||
!CBB_add_u16_length_prefixed(&contents, &sigalgs_cbb)) {
return 0;
}
for (size_t i = 0; i < num_sigalgs; i++) {
if (!CBB_add_u16(&sigalgs_cbb, sigalgs[i])) {
return 0;
}
}
if (!CBB_flush(out)) {
!CBB_add_u16_length_prefixed(&contents, &sigalgs_cbb) ||
!tls12_add_verify_sigalgs(ssl, &sigalgs_cbb) ||
!CBB_flush(out)) {
return 0;
}

View File

@ -295,6 +295,13 @@ static ssl_private_key_result_t AsyncPrivateKeySign(
abort();
}
bssl::UniquePtr<EVP_PKEY_CTX> ctx(
EVP_PKEY_CTX_new(test_state->private_key.get(), nullptr));
if (!ctx ||
!EVP_PKEY_sign_init(ctx.get())) {
return ssl_private_key_failure;
}
// Determine the hash.
const EVP_MD *md;
switch (signature_algorithm) {
@ -320,16 +327,17 @@ static ssl_private_key_result_t AsyncPrivateKeySign(
case SSL_SIGN_RSA_PKCS1_MD5_SHA1:
md = EVP_md5_sha1();
break;
case SSL_SIGN_ED25519:
md = nullptr;
break;
default:
fprintf(stderr, "Unknown signature algorithm %04x.\n",
signature_algorithm);
return ssl_private_key_failure;
}
bssl::ScopedEVP_MD_CTX ctx;
EVP_PKEY_CTX *pctx;
if (!EVP_DigestSignInit(ctx.get(), &pctx, md, nullptr,
test_state->private_key.get())) {
if (md != nullptr &&
!EVP_PKEY_CTX_set_signature_md(ctx.get(), md)) {
return ssl_private_key_failure;
}
@ -338,8 +346,8 @@ static ssl_private_key_result_t AsyncPrivateKeySign(
case SSL_SIGN_RSA_PSS_SHA256:
case SSL_SIGN_RSA_PSS_SHA384:
case SSL_SIGN_RSA_PSS_SHA512:
if (!EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) ||
!EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx,
if (!EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_PSS_PADDING) ||
!EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx.get(),
-1 /* salt len = hash len */)) {
return ssl_private_key_failure;
}
@ -347,13 +355,12 @@ static ssl_private_key_result_t AsyncPrivateKeySign(
// Write the signature into |test_state|.
size_t len = 0;
if (!EVP_DigestSignUpdate(ctx.get(), in, in_len) ||
!EVP_DigestSignFinal(ctx.get(), nullptr, &len)) {
if (!EVP_PKEY_sign_message(ctx.get(), nullptr, &len, in, in_len)) {
return ssl_private_key_failure;
}
test_state->private_key_result.resize(len);
if (!EVP_DigestSignFinal(ctx.get(), test_state->private_key_result.data(),
&len)) {
if (!EVP_PKEY_sign_message(ctx.get(), test_state->private_key_result.data(),
&len, in, in_len)) {
return ssl_private_key_failure;
}
test_state->private_key_result.resize(len);
@ -1174,6 +1181,10 @@ static bssl::UniquePtr<SSL_CTX> SetupCtx(const TestConfig *config) {
SSL_CTX_set_allow_unknown_alpn_protos(ssl_ctx.get(), 1);
}
if (config->enable_ed25519) {
SSL_CTX_set_ed25519_enabled(ssl_ctx.get(), 1);
}
return ssl_ctx;
}

View File

@ -1326,6 +1326,11 @@ type ProtocolBugs struct {
// RenegotiationCertificate, if not nil, is the certificate to use on
// renegotiation handshakes.
RenegotiationCertificate *Certificate
// UseLegacySigningAlgorithm, if non-zero, is the signature algorithm
// to use when signing in TLS 1.1 and earlier where algorithms are not
// negotiated.
UseLegacySigningAlgorithm signatureAlgorithm
}
func (c *Config) serverInit() {

View File

@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBkTCCAUOgAwIBAgIJAJwooam0UCDmMAUGAytlcDBFMQswCQYDVQQGEwJBVTET
MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
dHkgTHRkMB4XDTE0MDQyMzIzMjE1N1oXDTE0MDUyMzIzMjE1N1owRTELMAkGA1UE
BhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdp
ZGdpdHMgUHR5IEx0ZDAqMAUGAytlcAMhANdamAGCsQq31Uv+08lkBzoO4XLz2qYj
Ja8CGmj3B1Eao1AwTjAdBgNVHQ4EFgQUoux7eV+fJK2v3ah6QPU/lj1/+7UwHwYD
VR0jBBgwFoAUoux7eV+fJK2v3ah6QPU/lj1/+7UwDAYDVR0TBAUwAwEB/zAFBgMr
ZXADQQBuCzqji8VP9xU8mHEMjXGChX7YP5J664UyVKHKH9Z1u4wEbB8dJ3ScaWSL
r+VHVKUhsrvcdCelnXRrrSD7xWAL
-----END CERTIFICATE-----

View File

@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIJ1hsZ3v/VpguoRK9JLsLMREScVpezJpGXA7rAMcrn9g
-----END PRIVATE KEY-----

View File

@ -104,6 +104,7 @@ const (
testCertECDSAP256
testCertECDSAP384
testCertECDSAP521
testCertEd25519
)
const (
@ -114,6 +115,7 @@ const (
ecdsaP256CertificateFile = "ecdsa_p256_cert.pem"
ecdsaP384CertificateFile = "ecdsa_p384_cert.pem"
ecdsaP521CertificateFile = "ecdsa_p521_cert.pem"
ed25519CertificateFile = "ed25519_cert.pem"
)
const (
@ -124,6 +126,7 @@ const (
ecdsaP256KeyFile = "ecdsa_p256_key.pem"
ecdsaP384KeyFile = "ecdsa_p384_key.pem"
ecdsaP521KeyFile = "ecdsa_p521_key.pem"
ed25519KeyFile = "ed25519_key.pem"
channelIDKeyFile = "channel_id_key.pem"
)
@ -135,6 +138,7 @@ var (
ecdsaP256Certificate Certificate
ecdsaP384Certificate Certificate
ecdsaP521Certificate Certificate
ed25519Certificate Certificate
)
var testCerts = []struct {
@ -184,6 +188,12 @@ var testCerts = []struct {
keyFile: ecdsaP521KeyFile,
cert: &ecdsaP521Certificate,
},
{
id: testCertEd25519,
certFile: ed25519CertificateFile,
keyFile: ed25519KeyFile,
cert: &ed25519Certificate,
},
}
var channelIDKey *ecdsa.PrivateKey
@ -1466,32 +1476,6 @@ func addBasicTests() {
shouldFail: true,
expectedLocalError: "dtls: exceeded maximum packet length",
},
{
name: "CertMismatchRSA",
config: Config{
MaxVersion: VersionTLS12,
CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
Certificates: []Certificate{ecdsaP256Certificate},
Bugs: ProtocolBugs{
SendCipherSuite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
},
},
shouldFail: true,
expectedError: ":WRONG_CERTIFICATE_TYPE:",
},
{
name: "CertMismatchECDSA",
config: Config{
MaxVersion: VersionTLS12,
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
Certificates: []Certificate{rsaCertificate},
Bugs: ProtocolBugs{
SendCipherSuite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
},
},
shouldFail: true,
expectedError: ":WRONG_CERTIFICATE_TYPE:",
},
{
name: "EmptyCertificateList",
config: Config{
@ -2867,6 +2851,93 @@ func addCipherSuiteTests() {
},
flags: []string{"-psk", "secret"},
})
// Test that clients enforce that the server-sent certificate and cipher
// suite match in TLS 1.2.
testCases = append(testCases, testCase{
name: "CertificateCipherMismatch-RSA",
config: Config{
MaxVersion: VersionTLS12,
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
Certificates: []Certificate{rsaCertificate},
Bugs: ProtocolBugs{
SendCipherSuite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
},
},
shouldFail: true,
expectedError: ":WRONG_CERTIFICATE_TYPE:",
})
testCases = append(testCases, testCase{
name: "CertificateCipherMismatch-ECDSA",
config: Config{
MaxVersion: VersionTLS12,
CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
Certificates: []Certificate{ecdsaP256Certificate},
Bugs: ProtocolBugs{
SendCipherSuite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
},
},
shouldFail: true,
expectedError: ":WRONG_CERTIFICATE_TYPE:",
})
testCases = append(testCases, testCase{
name: "CertificateCipherMismatch-Ed25519",
config: Config{
MaxVersion: VersionTLS12,
CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
Certificates: []Certificate{ed25519Certificate},
Bugs: ProtocolBugs{
SendCipherSuite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
},
},
shouldFail: true,
expectedError: ":WRONG_CERTIFICATE_TYPE:",
})
// Test that servers decline to select a cipher suite which is
// inconsistent with their configured certificate.
testCases = append(testCases, testCase{
testType: serverTest,
name: "ServerCipherFilter-RSA",
config: Config{
MaxVersion: VersionTLS12,
CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
},
flags: []string{
"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
"-key-file", path.Join(*resourceDir, rsaKeyFile),
},
shouldFail: true,
expectedError: ":NO_SHARED_CIPHER:",
})
testCases = append(testCases, testCase{
testType: serverTest,
name: "ServerCipherFilter-ECDSA",
config: Config{
MaxVersion: VersionTLS12,
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
},
flags: []string{
"-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
"-key-file", path.Join(*resourceDir, ecdsaP256KeyFile),
},
shouldFail: true,
expectedError: ":NO_SHARED_CIPHER:",
})
testCases = append(testCases, testCase{
testType: serverTest,
name: "ServerCipherFilter-Ed25519",
config: Config{
MaxVersion: VersionTLS12,
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
},
flags: []string{
"-cert-file", path.Join(*resourceDir, ed25519CertificateFile),
"-key-file", path.Join(*resourceDir, ed25519KeyFile),
},
shouldFail: true,
expectedError: ":NO_SHARED_CIPHER:",
})
}
func addBadECDSASignatureTests() {
@ -3800,6 +3871,19 @@ func addStateMachineCoverageTests(config stateMachineTestConfig) {
"-key-file", path.Join(*resourceDir, ecdsaP256KeyFile),
},
})
tests = append(tests, testCase{
testType: serverTest,
name: "Basic-Server-Ed25519",
config: Config{
MaxVersion: VersionTLS12,
CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
},
flags: []string{
"-cert-file", path.Join(*resourceDir, ed25519CertificateFile),
"-key-file", path.Join(*resourceDir, ed25519KeyFile),
"-enable-ed25519",
},
})
// No session ticket support; server doesn't send NewSessionTicket.
tests = append(tests, testCase{
@ -6686,6 +6770,7 @@ var testSignatureAlgorithms = []struct {
{"RSA-PSS-SHA256", signatureRSAPSSWithSHA256, testCertRSA},
{"RSA-PSS-SHA384", signatureRSAPSSWithSHA384, testCertRSA},
{"RSA-PSS-SHA512", signatureRSAPSSWithSHA512, testCertRSA},
{"Ed25519", signatureEd25519, testCertEd25519},
// Tests for key types prior to TLS 1.2.
{"RSA", 0, testCertRSA},
{"ECDSA", 0, testCertECDSAP256},
@ -6777,6 +6862,7 @@ func addSignatureAlgorithmTests() {
"-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)),
"-key-file", path.Join(*resourceDir, getShimKey(alg.cert)),
"-enable-all-curves",
"-enable-ed25519",
},
shouldFail: shouldSignFail,
expectedError: signError,
@ -6803,6 +6889,7 @@ func addSignatureAlgorithmTests() {
"-require-any-client-certificate",
"-expect-peer-signature-algorithm", strconv.Itoa(int(alg.id)),
"-enable-all-curves",
"-enable-ed25519",
},
// Resume the session to assert the peer signature
// algorithm is reported on both handshakes.
@ -6829,6 +6916,7 @@ func addSignatureAlgorithmTests() {
"-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)),
"-key-file", path.Join(*resourceDir, getShimKey(alg.cert)),
"-enable-all-curves",
"-enable-ed25519",
},
shouldFail: shouldSignFail,
expectedError: signError,
@ -6855,6 +6943,7 @@ func addSignatureAlgorithmTests() {
flags: []string{
"-expect-peer-signature-algorithm", strconv.Itoa(int(alg.id)),
"-enable-all-curves",
"-enable-ed25519",
},
// Resume the session to assert the peer signature
// algorithm is reported on both handshakes.
@ -6880,6 +6969,7 @@ func addSignatureAlgorithmTests() {
flags: []string{
"-require-any-client-certificate",
"-enable-all-curves",
"-enable-ed25519",
},
shouldFail: true,
expectedError: ":BAD_SIGNATURE:",
@ -6898,7 +6988,10 @@ func addSignatureAlgorithmTests() {
InvalidSignature: true,
},
},
flags: []string{"-enable-all-curves"},
flags: []string{
"-enable-all-curves",
"-enable-ed25519",
},
shouldFail: true,
expectedError: ":BAD_SIGNATURE:",
})
@ -6916,6 +7009,7 @@ func addSignatureAlgorithmTests() {
"-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)),
"-key-file", path.Join(*resourceDir, getShimKey(alg.cert)),
"-enable-all-curves",
"-enable-ed25519",
"-signing-prefs", strconv.Itoa(int(alg.id)),
},
expectedPeerSignatureAlgorithm: alg.id,
@ -6933,6 +7027,7 @@ func addSignatureAlgorithmTests() {
"-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)),
"-key-file", path.Join(*resourceDir, getShimKey(alg.cert)),
"-enable-all-curves",
"-enable-ed25519",
"-signing-prefs", strconv.Itoa(int(alg.id)),
},
expectedPeerSignatureAlgorithm: alg.id,
@ -7510,6 +7605,96 @@ func addSignatureAlgorithmTests() {
},
flags: []string{"-max-version", strconv.Itoa(VersionTLS12)},
})
// TLS 1.1 and below has no way to advertise support for or negotiate
// Ed25519's signature algorithm.
testCases = append(testCases, testCase{
testType: clientTest,
name: "NoEd25519-TLS11-ServerAuth-Verify",
config: Config{
MaxVersion: VersionTLS11,
Certificates: []Certificate{ed25519Certificate},
Bugs: ProtocolBugs{
// Sign with Ed25519 even though it is TLS 1.1.
UseLegacySigningAlgorithm: signatureEd25519,
},
},
flags: []string{"-enable-ed25519"},
shouldFail: true,
expectedError: ":PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE:",
})
testCases = append(testCases, testCase{
testType: serverTest,
name: "NoEd25519-TLS11-ServerAuth-Sign",
config: Config{
MaxVersion: VersionTLS11,
},
flags: []string{
"-cert-file", path.Join(*resourceDir, ed25519CertificateFile),
"-key-file", path.Join(*resourceDir, ed25519KeyFile),
},
shouldFail: true,
expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:",
})
testCases = append(testCases, testCase{
testType: serverTest,
name: "NoEd25519-TLS11-ClientAuth-Verify",
config: Config{
MaxVersion: VersionTLS11,
Certificates: []Certificate{ed25519Certificate},
Bugs: ProtocolBugs{
// Sign with Ed25519 even though it is TLS 1.1.
UseLegacySigningAlgorithm: signatureEd25519,
},
},
flags: []string{
"-enable-ed25519",
"-require-any-client-certificate",
},
shouldFail: true,
expectedError: ":PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE:",
})
testCases = append(testCases, testCase{
testType: clientTest,
name: "NoEd25519-TLS11-ClientAuth-Sign",
config: Config{
MaxVersion: VersionTLS11,
ClientAuth: RequireAnyClientCert,
},
flags: []string{
"-cert-file", path.Join(*resourceDir, ed25519CertificateFile),
"-key-file", path.Join(*resourceDir, ed25519KeyFile),
},
shouldFail: true,
expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:",
})
// Test Ed25519 is not advertised by default.
testCases = append(testCases, testCase{
testType: clientTest,
name: "Ed25519DefaultDisable-NoAdvertise",
config: Config{
Certificates: []Certificate{ed25519Certificate},
},
shouldFail: true,
expectedLocalError: "tls: no common signature algorithms",
})
// Test Ed25519, when disabled, is not accepted if the peer ignores our
// preferences.
testCases = append(testCases, testCase{
testType: clientTest,
name: "Ed25519DefaultDisable-NoAccept",
config: Config{
Certificates: []Certificate{ed25519Certificate},
Bugs: ProtocolBugs{
IgnorePeerSignatureAlgorithmPreferences: true,
},
},
shouldFail: true,
expectedLocalError: "remote error: illegal parameter",
expectedError: ":WRONG_SIGNATURE_TYPE:",
})
}
// timeouts is the retransmit schedule for BoringSSL. It doubles and

View File

@ -40,7 +40,7 @@ func selectSignatureAlgorithm(version uint16, key crypto.PrivateKey, config *Con
continue
}
signer, err := getSigner(version, key, config, sigAlg)
signer, err := getSigner(version, key, config, sigAlg, false)
if err != nil {
continue
}
@ -60,7 +60,7 @@ func signMessage(version uint16, key crypto.PrivateKey, config *Config, sigAlg s
msg = newMsg
}
signer, err := getSigner(version, key, config, sigAlg)
signer, err := getSigner(version, key, config, sigAlg, false)
if err != nil {
return nil, err
}
@ -73,7 +73,7 @@ func verifyMessage(version uint16, key crypto.PublicKey, config *Config, sigAlg
return errors.New("tls: unsupported signature algorithm")
}
signer, err := getSigner(version, key, config, sigAlg)
signer, err := getSigner(version, key, config, sigAlg, true)
if err != nil {
return err
}
@ -273,20 +273,24 @@ func (e *ed25519Signer) verifyMessage(key crypto.PublicKey, msg, sig []byte) err
return nil
}
func getSigner(version uint16, key interface{}, config *Config, sigAlg signatureAlgorithm) (signer, error) {
func getSigner(version uint16, key interface{}, config *Config, sigAlg signatureAlgorithm, isVerify bool) (signer, error) {
// TLS 1.1 and below use legacy signature algorithms.
if version < VersionTLS12 {
switch key.(type) {
case *rsa.PrivateKey, *rsa.PublicKey:
return &rsaPKCS1Signer{crypto.MD5SHA1}, nil
case *ecdsa.PrivateKey, *ecdsa.PublicKey:
return &ecdsaSigner{version, config, nil, crypto.SHA1}, nil
default:
return nil, errors.New("unknown key type")
if config.Bugs.UseLegacySigningAlgorithm == 0 || isVerify {
switch key.(type) {
case *rsa.PrivateKey, *rsa.PublicKey:
return &rsaPKCS1Signer{crypto.MD5SHA1}, nil
case *ecdsa.PrivateKey, *ecdsa.PublicKey:
return &ecdsaSigner{version, config, nil, crypto.SHA1}, nil
default:
return nil, errors.New("unknown key type")
}
}
// Fall through, forcing a particular algorithm.
sigAlg = config.Bugs.UseLegacySigningAlgorithm
}
// TODO(davidben): Forbid RSASSA-PKCS1-v1_5 in TLS 1.3.
switch sigAlg {
case signatureRSAPKCS1WithMD5:
if version < VersionTLS13 || config.Bugs.IgnoreSignatureVersionChecks {

View File

@ -131,6 +131,7 @@ const Flag<bool> kBoolFlags[] = {
{ "-no-op-extra-handshake", &TestConfig::no_op_extra_handshake },
{ "-handshake-twice", &TestConfig::handshake_twice },
{ "-allow-unknown-alpn-protos", &TestConfig::allow_unknown_alpn_protos },
{ "-enable-ed25519", &TestConfig::enable_ed25519 },
};
const Flag<std::string> kStringFlags[] = {

View File

@ -144,6 +144,7 @@ struct TestConfig {
bool no_op_extra_handshake = false;
bool handshake_twice = false;
bool allow_unknown_alpn_protos = false;
bool enable_ed25519 = false;
};
bool ParseConfig(int argc, char **argv, TestConfig *out_config);

View File

@ -555,23 +555,10 @@ static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) {
CBB sigalgs_cbb;
if (!ssl->method->init_message(ssl, &cbb, &body,
SSL3_MT_CERTIFICATE_REQUEST) ||
!CBB_add_u8(&body, 0 /* no certificate_request_context. */)) {
goto err;
}
const uint16_t *sigalgs;
size_t num_sigalgs = tls12_get_verify_sigalgs(ssl, &sigalgs);
if (!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb)) {
goto err;
}
for (size_t i = 0; i < num_sigalgs; i++) {
if (!CBB_add_u16(&sigalgs_cbb, sigalgs[i])) {
goto err;
}
}
if (!ssl_add_client_CA_list(ssl, &body) ||
!CBB_add_u8(&body, 0 /* no certificate_request_context. */) ||
!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb) ||
!tls12_add_verify_sigalgs(ssl, &sigalgs_cbb) ||
!ssl_add_client_CA_list(ssl, &body) ||
!CBB_add_u16(&body, 0 /* empty certificate_extensions. */) ||
!ssl_add_message_cbb(ssl, &cbb)) {
goto err;

View File

@ -113,6 +113,9 @@ static const struct argument kArguments[] = {
{
"-early-data", kBooleanArgument, "Allow early data",
},
{
"-ed25519", kBooleanArgument, "Advertise Ed25519 support",
},
{
"", kOptionalArgument, "",
},
@ -414,6 +417,10 @@ bool Client(const std::vector<std::string> &args) {
SSL_CTX_set_early_data_enabled(ctx.get(), 1);
}
if (args_map.count("-ed25519") != 0) {
SSL_CTX_set_ed25519_enabled(ctx.get(), 1);
}
if (args_map.count("-test-resumption") != 0) {
if (args_map.count("-session-in") != 0) {
fprintf(stderr,

View File

@ -238,6 +238,8 @@ static const char *SignatureAlgorithmToString(uint16_t version, uint16_t sigalg)
return "rsa_pss_sha384";
case SSL_SIGN_RSA_PSS_SHA512:
return "rsa_pss_sha512";
case SSL_SIGN_ED25519:
return "ed25519";
default:
return "(unknown)";
}