From 926af7582af6229f380e4b37ed6f0c34756ebb66 Mon Sep 17 00:00:00 2001
From: Paul Bakker
Date: Fri, 23 Nov 2012 13:38:07 +0100
Subject: [PATCH] Fixed client certificate handling with TLS 1.2
---
include/polarssl/ssl.h | 7 ++
library/ssl_cli.c | 141 ++++++++++++++++++++++++++++++++---------
library/ssl_srv.c | 100 ++++++++++++++++++++++++-----
3 files changed, 201 insertions(+), 47 deletions(-)
diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h
index bcc84287c..e5d9eb73c 100644
--- a/include/polarssl/ssl.h
+++ b/include/polarssl/ssl.h
@@ -192,6 +192,11 @@
#define SSL_SIG_RSA 1
+/*
+ * Client Certificate Types
+ */
+#define SSL_CERT_TYPE_RSA_SIGN 1
+
/*
* Message, alert and handshake types
*/
@@ -351,6 +356,8 @@ struct _ssl_handshake_params
* Handshake specific crypto variables
*/
int sig_alg; /*!< Signature algorithm */
+ int cert_type; /*!< Requested cert type */
+ int verify_sig_alg; /*!< Signature algorithm for verify */
#if defined(POLARSSL_DHM_C)
dhm_context dhm_ctx; /*!< DHM key exchange */
#endif
diff --git a/library/ssl_cli.c b/library/ssl_cli.c
index 11a7a61fe..16b32004d 100644
--- a/library/ssl_cli.c
+++ b/library/ssl_cli.c
@@ -891,15 +891,19 @@ static int ssl_parse_server_key_exchange( ssl_context *ssl )
static int ssl_parse_certificate_request( ssl_context *ssl )
{
int ret;
+ unsigned char *buf, *p;
+ size_t n = 0;
+ size_t cert_type_len = 0, sig_alg_len = 0, dn_len = 0;
SSL_DEBUG_MSG( 2, ( "=> parse certificate request" ) );
/*
* 0 . 0 handshake type
* 1 . 3 handshake length
- * 4 . 5 SSL version
- * 6 . 6 cert type count
- * 7 .. n-1 cert types
+ * 4 . 4 cert type count
+ * 5 .. m-1 cert types
+ * m .. m+1 sig alg length (TLS 1.2 only)
+ * m+1 .. n-1 SignatureAndHashAlgorithms (TLS 1.2 only)
* n .. n+1 length of all DNs
* n+2 .. n+3 length of DN 1
* n+4 .. ... Distinguished Name #1
@@ -926,6 +930,70 @@ static int ssl_parse_certificate_request( ssl_context *ssl )
SSL_DEBUG_MSG( 3, ( "got %s certificate request",
ssl->client_auth ? "a" : "no" ) );
+ if( ssl->client_auth == 0 )
+ goto exit;
+
+ // TODO: handshake_failure alert for an anonymous server to request
+ // client authentication
+
+ buf = ssl->in_msg;
+
+ // Retrieve cert types
+ //
+ cert_type_len = buf[4];
+ n = cert_type_len;
+
+ if( ssl->in_hslen < 6 + n )
+ {
+ SSL_DEBUG_MSG( 1, ( "bad certificate request message" ) );
+ return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST );
+ }
+
+ p = buf + 4;
+ while( cert_type_len > 0 )
+ {
+ if( *p == SSL_CERT_TYPE_RSA_SIGN )
+ {
+ ssl->handshake->cert_type = SSL_CERT_TYPE_RSA_SIGN;
+ break;
+ }
+
+ cert_type_len--;
+ p++;
+ }
+
+ if( ssl->handshake->cert_type == 0 )
+ {
+ SSL_DEBUG_MSG( 1, ( "no known cert_type provided" ) );
+ return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST );
+ }
+
+ if( ssl->minor_ver == SSL_MINOR_VERSION_3 )
+ {
+ sig_alg_len = ( ( buf[5 + n] << 8 )
+ | ( buf[6 + n] ) );
+
+ p = buf + 7 + n;
+ n += sig_alg_len;
+
+ if( ssl->in_hslen < 6 + n )
+ {
+ SSL_DEBUG_MSG( 1, ( "bad certificate request message" ) );
+ return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST );
+ }
+ }
+
+ dn_len = ( ( buf[7 + n] << 8 )
+ | ( buf[8 + n] ) );
+
+ n += dn_len;
+ if( ssl->in_hslen != 9 + n )
+ {
+ SSL_DEBUG_MSG( 1, ( "bad certificate request message" ) );
+ return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST );
+ }
+
+exit:
SSL_DEBUG_MSG( 2, ( "<= parse certificate request" ) );
return( 0 );
@@ -1102,25 +1170,6 @@ static int ssl_write_certificate_verify( ssl_context *ssl )
return( 0 );
}
- if( ssl->minor_ver == SSL_MINOR_VERSION_3 )
- {
- // TODO TLS1.2 Should be based on allowed signature algorithm received in
- // Certificate Request according to RFC 5246. But OpenSSL only allows
- // SHA256 and SHA384. Find out why OpenSSL does this.
- //
- if( ssl->session_negotiate->ciphersuite == TLS_RSA_WITH_AES_256_GCM_SHA384 ||
- ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 )
- {
- hash_id = SIG_RSA_SHA384;
- hashlen = 48;
- }
- else
- {
- hash_id = SIG_RSA_SHA256;
- hashlen = 32;
- }
- }
-
if( ssl->rsa_key == NULL )
{
SSL_DEBUG_MSG( 1, ( "got no private key" ) );
@@ -1132,23 +1181,52 @@ static int ssl_write_certificate_verify( ssl_context *ssl )
*/
ssl->handshake->calc_verify( ssl, hash );
- if ( ssl->rsa_key )
- n = ssl->rsa_key_len ( ssl->rsa_key );
-
- if( ssl->minor_ver == SSL_MINOR_VERSION_3 )
+ if( ssl->minor_ver != SSL_MINOR_VERSION_3 )
{
- // TODO TLS1.2 Should be based on allowed signature algorithm received in
- // Certificate Request according to RFC 5246. But OpenSSL only allows
- // SHA256 and SHA384. Find out why OpenSSL does this.
- //
+ /*
+ * digitally-signed struct {
+ * opaque md5_hash[16];
+ * opaque sha_hash[20];
+ * };
+ *
+ * md5_hash
+ * MD5(handshake_messages);
+ *
+ * sha_hash
+ * SHA(handshake_messages);
+ */
+ hashlen = 36;
+ hash_id = SIG_RSA_RAW;
+ }
+ else
+ {
+ /*
+ * digitally-signed struct {
+ * opaque handshake_messages[handshake_messages_length];
+ * };
+ *
+ * Taking shortcut here. We assume that the server always allows the
+ * PRF Hash function and has sent it in the allowed signature
+ * algorithms list received in the Certificate Request message.
+ *
+ * Until we encounter a server that does not, we will take this
+ * shortcut.
+ *
+ * Reason: Otherwise we should have running hashes for SHA512 and SHA224
+ * in order to satisfy 'weird' needs from the server side.
+ */
if( ssl->session_negotiate->ciphersuite == TLS_RSA_WITH_AES_256_GCM_SHA384 ||
ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 )
{
+ hash_id = SIG_RSA_SHA384;
+ hashlen = 48;
ssl->out_msg[4] = SSL_HASH_SHA384;
ssl->out_msg[5] = SSL_SIG_RSA;
}
else
{
+ hash_id = SIG_RSA_SHA256;
+ hashlen = 32;
ssl->out_msg[4] = SSL_HASH_SHA256;
ssl->out_msg[5] = SSL_SIG_RSA;
}
@@ -1156,6 +1234,9 @@ static int ssl_write_certificate_verify( ssl_context *ssl )
offset = 2;
}
+ if ( ssl->rsa_key )
+ n = ssl->rsa_key_len ( ssl->rsa_key );
+
ssl->out_msg[4 + offset] = (unsigned char)( n >> 8 );
ssl->out_msg[5 + offset] = (unsigned char)( n );
diff --git a/library/ssl_srv.c b/library/ssl_srv.c
index 0e2767735..79c3a380f 100644
--- a/library/ssl_srv.c
+++ b/library/ssl_srv.c
@@ -689,7 +689,7 @@ static int ssl_write_server_hello( ssl_context *ssl )
static int ssl_write_certificate_request( ssl_context *ssl )
{
int ret;
- size_t n;
+ size_t n = 0, dn_size, total_dn_size;
unsigned char *buf, *p;
const x509_cert *crt;
@@ -707,7 +707,9 @@ static int ssl_write_certificate_request( ssl_context *ssl )
* 0 . 0 handshake type
* 1 . 3 handshake length
* 4 . 4 cert type count
- * 5 .. n-1 cert types
+ * 5 .. m-1 cert types
+ * m .. m+1 sig alg length (TLS 1.2 only)
+ * m+1 .. n-1 SignatureAndHashAlgorithms (TLS 1.2 only)
* n .. n+1 length of all DNs
* n+2 .. n+3 length of DN 1
* n+4 .. ... Distinguished Name #1
@@ -720,30 +722,60 @@ static int ssl_write_certificate_request( ssl_context *ssl )
* At the moment, only RSA certificates are supported
*/
*p++ = 1;
- *p++ = 1;
+ *p++ = SSL_CERT_TYPE_RSA_SIGN;
+
+ /*
+ * Add signature_algorithms for verify (TLS 1.2)
+ * Only add current running algorithm that is already required for
+ * requested ciphersuite.
+ *
+ * Length is always 2
+ */
+ if( ssl->max_minor_ver == SSL_MINOR_VERSION_3 )
+ {
+ ssl->handshake->verify_sig_alg = SSL_HASH_SHA256;
+
+ *p++ = 0;
+ *p++ = 2;
+
+ if( ssl->session_negotiate->ciphersuite == TLS_RSA_WITH_AES_256_GCM_SHA384 ||
+ ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 )
+ {
+ ssl->handshake->verify_sig_alg = SSL_HASH_SHA384;
+ }
+
+ *p++ = ssl->handshake->verify_sig_alg;
+ *p++ = SSL_SIG_RSA;
+
+ n += 4;
+ }
p += 2;
crt = ssl->ca_chain;
+ total_dn_size = 2;
while( crt != NULL )
{
if( p - buf > 4096 )
break;
- n = crt->subject_raw.len;
- *p++ = (unsigned char)( n >> 8 );
- *p++ = (unsigned char)( n );
- memcpy( p, crt->subject_raw.p, n );
+ dn_size = crt->subject_raw.len;
+ *p++ = (unsigned char)( dn_size >> 8 );
+ *p++ = (unsigned char)( dn_size );
+ memcpy( p, crt->subject_raw.p, dn_size );
+ p += dn_size;
- SSL_DEBUG_BUF( 3, "requested DN", p, n );
- p += n; crt = crt->next;
+ SSL_DEBUG_BUF( 3, "requested DN", p, dn_size );
+
+ total_dn_size += dn_size;
+ crt = crt->next;
}
- ssl->out_msglen = n = p - buf;
+ ssl->out_msglen = p - buf;
ssl->out_msgtype = SSL_MSG_HANDSHAKE;
ssl->out_msg[0] = SSL_HS_CERTIFICATE_REQUEST;
- ssl->out_msg[6] = (unsigned char)( ( n - 8 ) >> 8 );
- ssl->out_msg[7] = (unsigned char)( ( n - 8 ) );
+ ssl->out_msg[6 + n] = (unsigned char)( total_dn_size >> 8 );
+ ssl->out_msg[7 + n] = (unsigned char)( total_dn_size );
ret = ssl_write_record( ssl );
@@ -1170,8 +1202,10 @@ static int ssl_parse_client_key_exchange( ssl_context *ssl )
static int ssl_parse_certificate_verify( ssl_context *ssl )
{
int ret;
- size_t n1, n2;
+ size_t n = 0, n1, n2;
unsigned char hash[48];
+ int hash_id;
+ unsigned int hashlen;
SSL_DEBUG_MSG( 2, ( "=> parse certificate verify" ) );
@@ -1204,17 +1238,49 @@ static int ssl_parse_certificate_verify( ssl_context *ssl )
return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY );
}
- n1 = ssl->session_negotiate->peer_cert->rsa.len;
- n2 = ( ssl->in_msg[4] << 8 ) | ssl->in_msg[5];
+ if( ssl->minor_ver == SSL_MINOR_VERSION_3 )
+ {
+ /*
+ * As server we know we either have SSL_HASH_SHA384 or
+ * SSL_HASH_SHA256
+ */
+ if( ssl->in_msg[4] != ssl->handshake->verify_sig_alg ||
+ ssl->in_msg[5] != SSL_SIG_RSA )
+ {
+ SSL_DEBUG_MSG( 1, ( "peer not adhering to requested sig_alg for verify message" ) );
+ return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY );
+ }
- if( n1 + 6 != ssl->in_hslen || n1 != n2 )
+ if( ssl->handshake->verify_sig_alg == SSL_HASH_SHA384 )
+ {
+ hashlen = 48;
+ hash_id = SIG_RSA_SHA384;
+ }
+ else
+ {
+ hashlen = 32;
+ hash_id = SIG_RSA_SHA256;
+ }
+
+ n += 2;
+ }
+ else
+ {
+ hashlen = 36;
+ hash_id = SIG_RSA_RAW;
+ }
+
+ n1 = ssl->session_negotiate->peer_cert->rsa.len;
+ n2 = ( ssl->in_msg[4 + n] << 8 ) | ssl->in_msg[5 + 2];
+
+ if( n + n1 + 6 != ssl->in_hslen || n1 != n2 )
{
SSL_DEBUG_MSG( 1, ( "bad certificate verify message" ) );
return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY );
}
ret = rsa_pkcs1_verify( &ssl->session_negotiate->peer_cert->rsa, RSA_PUBLIC,
- SIG_RSA_RAW, 36, hash, ssl->in_msg + 6 );
+ hash_id, hashlen, hash, ssl->in_msg + 6 + n );
if( ret != 0 )
{
SSL_DEBUG_RET( 1, "rsa_pkcs1_verify", ret );