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 );