Support different hash/sig algorithms in handshake signing, including ECDSA
This commit is contained in:
+146
-26
@@ -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))
|
||||
chunk_t sig, hash;
|
||||
|
||||
switch (key->get_type(key))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, &sig))
|
||||
{
|
||||
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))
|
||||
switch (key->get_type(key))
|
||||
{
|
||||
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;
|
||||
}
|
||||
if (!key->verify(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, sig))
|
||||
{
|
||||
free(hash.ptr);
|
||||
return FALSE;
|
||||
}
|
||||
free(hash.ptr);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
+56
-7
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+64
-7
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user