From: Martin Willi Date: Thu, 2 Sep 2010 08:29:32 +0000 (+0200) Subject: Support different hash/sig algorithms in handshake signing, including ECDSA X-Git-Tag: 4.5.0~346 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=dbb7c0306cbb5e6276745f0cdfd9a46edd086fb1;p=thirdparty%2Fstrongswan.git Support different hash/sig algorithms in handshake signing, including ECDSA --- diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index 7e5ca197d5..1f61795657 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -692,36 +692,130 @@ static bool hash_handshake(private_tls_crypto_t *this, chunk_t *hash) return TRUE; } -METHOD(tls_crypto_t, sign_handshake, bool, - private_tls_crypto_t *this, private_key_t *key, tls_writer_t *writer) +/** + * Get the signature scheme from a TLS 1.2 hash/sig algorithm pair + */ +static signature_scheme_t hashsig_to_scheme(key_type_t type, + tls_hash_algorithm_t hash, tls_signature_algorithm_t sig) { - chunk_t sig, hash; + switch (sig) + { + case TLS_SIG_RSA: + if (type != KEY_RSA) + { + return SIGN_UNKNOWN; + } + switch (hash) + { + case TLS_HASH_MD5: + return SIGN_RSA_EMSA_PKCS1_MD5; + case TLS_HASH_SHA1: + return SIGN_RSA_EMSA_PKCS1_SHA1; + case TLS_HASH_SHA224: + return SIGN_RSA_EMSA_PKCS1_SHA224; + case TLS_HASH_SHA256: + return SIGN_RSA_EMSA_PKCS1_SHA256; + case TLS_HASH_SHA384: + return SIGN_RSA_EMSA_PKCS1_SHA384; + case TLS_HASH_SHA512: + return SIGN_RSA_EMSA_PKCS1_SHA512; + default: + return SIGN_UNKNOWN; + } + case TLS_SIG_ECDSA: + if (type != KEY_ECDSA) + { + return SIGN_UNKNOWN; + } + switch (hash) + { + case TLS_HASH_SHA224: + return SIGN_ECDSA_WITH_SHA1_DER; + case TLS_HASH_SHA256: + return SIGN_ECDSA_WITH_SHA256_DER; + case TLS_HASH_SHA384: + return SIGN_ECDSA_WITH_SHA384_DER; + case TLS_HASH_SHA512: + return SIGN_ECDSA_WITH_SHA512_DER; + default: + return SIGN_UNKNOWN; + } + default: + return SIGN_UNKNOWN; + } +} +METHOD(tls_crypto_t, sign_handshake, bool, + private_tls_crypto_t *this, private_key_t *key, tls_writer_t *writer, + chunk_t hashsig) +{ if (this->tls->get_version(this->tls) >= TLS_1_2) { - /* TODO: use supported algorithms instead of fixed SHA1/RSA */ - if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_SHA1, this->handshake, &sig)) + signature_scheme_t scheme; + tls_reader_t *reader; + u_int8_t hash, alg; + chunk_t sig; + bool done = FALSE; + + reader = tls_reader_create(hashsig); + while (reader->remaining(reader) >= 2) { + if (reader->read_uint8(reader, &hash) && + reader->read_uint8(reader, &alg)) + { + scheme = hashsig_to_scheme(key->get_type(key), hash, alg); + if (scheme != SIGN_UNKNOWN && + key->sign(key, scheme, this->handshake, &sig)) + { + done = TRUE; + break; + } + } + } + reader->destroy(reader); + if (!done) + { + DBG1(DBG_TLS, "none of the proposed hash/sig algorithms supported"); return FALSE; } - writer->write_uint8(writer, 2); - writer->write_uint8(writer, 1); + DBG2(DBG_TLS, "signed handshake data with %N/%N", + tls_hash_algorithm_names, hash, tls_signature_algorithm_names, alg); + writer->write_uint8(writer, hash); + writer->write_uint8(writer, alg); writer->write_data16(writer, sig); free(sig.ptr); } else { - if (!hash_handshake(this, &hash)) - { - return FALSE; - } - if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, &sig)) + chunk_t sig, hash; + + switch (key->get_type(key)) { - free(hash.ptr); - return FALSE; + case KEY_RSA: + if (!hash_handshake(this, &hash)) + { + return FALSE; + } + if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, &sig)) + { + free(hash.ptr); + return FALSE; + } + DBG2(DBG_TLS, "signed handshake data with SHA1+MD5/RSA"); + free(hash.ptr); + break; + case KEY_ECDSA: + if (!key->sign(key, SIGN_ECDSA_WITH_SHA1_DER, + this->handshake, &sig)) + { + return FALSE; + } + DBG2(DBG_TLS, "signed handshake data with SHA1/ECDSA"); + break; + default: + return FALSE; } writer->write_data16(writer, sig); - free(hash.ptr); free(sig.ptr); } return TRUE; @@ -732,6 +826,7 @@ METHOD(tls_crypto_t, verify_handshake, bool, { if (this->tls->get_version(this->tls) >= TLS_1_2) { + signature_scheme_t scheme = SIGN_UNKNOWN; u_int8_t hash, alg; chunk_t sig; @@ -742,12 +837,20 @@ METHOD(tls_crypto_t, verify_handshake, bool, DBG1(DBG_TLS, "received invalid Certificate Verify"); return FALSE; } - /* TODO: map received hash/sig alg to signature scheme */ - if (hash != 2 || alg != 1 || - !key->verify(key, SIGN_RSA_EMSA_PKCS1_SHA1, this->handshake, sig)) + scheme = hashsig_to_scheme(key->get_type(key), hash, alg); + if (scheme == SIGN_UNKNOWN) { + DBG1(DBG_TLS, "Certificate Verify algorithms %N/%N not supported", + tls_hash_algorithm_names, hash, + tls_signature_algorithm_names, alg); return FALSE; } + if (!key->verify(key, scheme, this->handshake, sig)) + { + return FALSE; + } + DBG2(DBG_TLS, "verified handshake data with %N/%N", + tls_hash_algorithm_names, hash, tls_signature_algorithm_names, alg); } else { @@ -758,16 +861,33 @@ METHOD(tls_crypto_t, verify_handshake, bool, DBG1(DBG_TLS, "received invalid Certificate Verify"); return FALSE; } - if (!hash_handshake(this, &hash)) - { - return FALSE; - } - if (!key->verify(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, sig)) + switch (key->get_type(key)) { - free(hash.ptr); - return FALSE; + case KEY_RSA: + if (!hash_handshake(this, &hash)) + { + return FALSE; + } + if (!key->verify(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, sig)) + { + free(hash.ptr); + return FALSE; + } + DBG2(DBG_TLS, "verified handshake data with SHA1+MD5/RSA"); + free(hash.ptr); + break; + case KEY_ECDSA: + if (!key->verify(key, SIGN_ECDSA_WITH_SHA1_DER, + this->handshake, sig)) + { + free(hash.ptr); + return FALSE; + } + DBG2(DBG_TLS, "verified handshake data with SHA1/ECDSA"); + break; + default: + return FALSE; } - free(hash.ptr); } return TRUE; } diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index 4ae456f38c..be4005dad0 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -349,10 +349,11 @@ struct tls_crypto_t { * * @param key private key to use for signature * @param writer TLS writer to write signature to + * @param hashsig list of TLS1.2 hash/sig algorithms to select from * @return TRUE if signature create successfully */ bool (*sign_handshake)(tls_crypto_t *this, private_key_t *key, - tls_writer_t *writer); + tls_writer_t *writer, chunk_t hashsig); /** * Verify the signature over handshake data using a given public key. diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index a7b23f76d1..3ee77d5010 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -102,6 +102,16 @@ struct private_tls_peer_t { * Peer private key */ private_key_t *private; + + /** + * List of server-supported hashsig algorithms + */ + chunk_t hashsig; + + /** + * List of server-supported client certificate types + */ + chunk_t cert_types; }; /** @@ -283,6 +293,7 @@ static status_t process_certreq(private_tls_peer_t *this, tls_reader_t *reader) this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); return NEED_MORE; } + this->cert_types = chunk_clone(types); if (this->tls->get_version(this->tls) >= TLS_1_2) { if (!reader->read_data16(reader, &hashsig)) @@ -291,7 +302,7 @@ static status_t process_certreq(private_tls_peer_t *this, tls_reader_t *reader) this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); return NEED_MORE; } - /* TODO: store supported hashsig algorithms */ + this->hashsig = chunk_clone(hashsig); } if (!reader->read_data16(reader, &data)) { @@ -475,6 +486,45 @@ static status_t send_client_hello(private_tls_peer_t *this, return NEED_MORE; } +/** + * Find a private key suitable to sign Certificate Verify + */ +static private_key_t *find_private_key(private_tls_peer_t *this) +{ + private_key_t *key = NULL; + tls_reader_t *reader; + key_type_t type; + u_int8_t cert; + + if (!this->peer) + { + return NULL; + } + reader = tls_reader_create(this->cert_types); + while (reader->remaining(reader) && reader->read_uint8(reader, &cert)) + { + switch (cert) + { + case TLS_RSA_SIGN: + type = KEY_RSA; + break; + case TLS_ECDSA_SIGN: + type = KEY_ECDSA; + break; + default: + continue; + } + key = lib->credmgr->get_private(lib->credmgr, type, + this->peer, this->peer_auth); + if (key) + { + break; + } + } + reader->destroy(reader); + return key; +} + /** * Send Certificate */ @@ -487,11 +537,7 @@ static status_t send_certificate(private_tls_peer_t *this, tls_writer_t *certs; chunk_t data; - if (this->peer) - { - this->private = lib->credmgr->get_private(lib->credmgr, - KEY_ANY, this->peer, this->peer_auth); - } + this->private = find_private_key(this); if (!this->private) { DBG1(DBG_TLS, "no TLS peer certificate found for '%Y'", this->peer); @@ -611,7 +657,8 @@ static status_t send_certificate_verify(private_tls_peer_t *this, tls_handshake_type_t *type, tls_writer_t *writer) { if (!this->private || - !this->crypto->sign_handshake(this->crypto, this->private, writer)) + !this->crypto->sign_handshake(this->crypto, this->private, + writer, this->hashsig)) { DBG1(DBG_TLS, "creating TLS Certificate Verify signature failed"); this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); @@ -715,6 +762,8 @@ METHOD(tls_handshake_t, destroy, void, DESTROY_IF(this->private); this->peer_auth->destroy(this->peer_auth); this->server_auth->destroy(this->server_auth); + free(this->hashsig.ptr); + free(this->cert_types.ptr); free(this); } diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 47e12a854f..e8b96d9f00 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -499,6 +499,69 @@ static status_t send_certificate(private_tls_server_t *this, return NEED_MORE; } +/** + * Create a list of supported certificate types and hash/sig algorithms + */ +static void get_supported_algorithms(private_tls_server_t *this, + tls_writer_t *writer) +{ + tls_writer_t *supported; + + supported = tls_writer_create(4); + /* we propose both RSA and ECDSA */ + supported->write_uint8(supported, TLS_RSA_SIGN); + supported->write_uint8(supported, TLS_ECDSA_SIGN); + writer->write_data8(writer, supported->get_buf(supported)); + supported->destroy(supported); + + if (this->tls->get_version(this->tls) >= TLS_1_2) + { + enumerator_t *enumerator; + hash_algorithm_t alg; + tls_hash_algorithm_t hash; + + supported = tls_writer_create(32); + enumerator = lib->crypto->create_hasher_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &alg)) + { + switch (alg) + { + case HASH_MD5: + hash = TLS_HASH_MD5; + break; + case HASH_SHA1: + hash = TLS_HASH_SHA1; + break; + case HASH_SHA224: + hash = TLS_HASH_SHA224; + break; + case HASH_SHA256: + hash = TLS_HASH_SHA256; + break; + case HASH_SHA384: + hash = TLS_HASH_SHA384; + break; + case HASH_SHA512: + hash = TLS_HASH_SHA512; + break; + default: + continue; + } + supported->write_uint8(supported, hash); + supported->write_uint8(supported, TLS_SIG_RSA); + if (alg != HASH_MD5 && alg != HASH_SHA224) + { + supported->write_uint8(supported, hash); + supported->write_uint8(supported, TLS_SIG_ECDSA); + } + } + enumerator->destroy(enumerator); + + writer->write_data16(writer, supported->get_buf(supported)); + supported->destroy(supported); + } +} + /** * Send Certificate Request */ @@ -511,13 +574,7 @@ static status_t send_certificate_request(private_tls_server_t *this, x509_t *x509; identification_t *id; - /* currently only RSA signatures are supported */ - writer->write_data8(writer, chunk_from_chars(1)); - if (this->tls->get_version(this->tls) >= TLS_1_2) - { - /* enforce RSA with SHA1 signatures */ - writer->write_data16(writer, chunk_from_chars(2, 1)); - } + get_supported_algorithms(this, writer); authorities = tls_writer_create(64); enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,