]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
TLS: Add TLS v1.2 style CertificateVerify functionality
authorJouni Malinen <j@w1.fi>
Sun, 27 Nov 2011 19:56:26 +0000 (21:56 +0200)
committerJouni Malinen <j@w1.fi>
Sun, 27 Nov 2011 20:08:29 +0000 (22:08 +0200)
Add support for generating and verifying RFC 3447 RSASSA-PKCS1-v1_5
style DigestInfo for TLS v1.2 CertificateVerify. For now, this is
hardcoded to only support SHA256-based digest.

Signed-hostap: Jouni Malinen <j@w1.fi>

src/tls/tlsv1_client_write.c
src/tls/tlsv1_common.h
src/tls/tlsv1_server_read.c

index 86b5f2d977c07a0fcf60c99e8c2d990417938f6e..badfc604124faec70706dc4559a09fa399290eb7 100644 (file)
@@ -437,7 +437,7 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
 {
        u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start;
        size_t rlen, hlen, clen;
-       u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos;
+       u8 hash[100], *hpos;
        enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
 
        pos = *msgpos;
@@ -477,6 +477,40 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
 
        hpos = hash;
 
+#ifdef CONFIG_TLSV12
+       if (conn->rl.tls_version == TLS_VERSION_1_2) {
+               hlen = SHA256_MAC_LEN;
+               if (conn->verify.sha256_cert == NULL ||
+                   crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) <
+                   0) {
+                       conn->verify.sha256_cert = NULL;
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_INTERNAL_ERROR);
+                       return -1;
+               }
+               conn->verify.sha256_cert = NULL;
+
+               /*
+                * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
+                *
+                * DigestInfo ::= SEQUENCE {
+                *   digestAlgorithm DigestAlgorithm,
+                *   digest OCTET STRING
+                * }
+                *
+                * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
+                *
+                * DER encoded DigestInfo for SHA256 per RFC 3447:
+                * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||
+                * H
+                */
+               os_memmove(hash + 19, hash, hlen);
+               hlen += 19;
+               os_memcpy(hash, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65"
+                         "\x03\x04\x02\x01\x05\x00\x04\x20", 19);
+       } else {
+#endif /* CONFIG_TLSV12 */
+
        if (alg == SIGN_ALG_RSA) {
                hlen = MD5_MAC_LEN;
                if (conn->verify.md5_cert == NULL ||
@@ -507,8 +541,29 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
        if (alg == SIGN_ALG_RSA)
                hlen += MD5_MAC_LEN;
 
+#ifdef CONFIG_TLSV12
+       }
+#endif /* CONFIG_TLSV12 */
+
        wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
 
+#ifdef CONFIG_TLSV12
+       if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+               /*
+                * RFC 5246, 4.7:
+                * TLS v1.2 adds explicit indication of the used signature and
+                * hash algorithms.
+                *
+                * struct {
+                *   HashAlgorithm hash;
+                *   SignatureAlgorithm signature;
+                * } SignatureAndHashAlgorithm;
+                */
+               *pos++ = TLS_HASH_ALG_SHA256;
+               *pos++ = TLS_SIGN_ALG_RSA;
+       }
+#endif /* CONFIG_TLSV12 */
+
        /*
         * RFC 2246, 4.7:
         * In digital signing, one-way hash functions are used as input for a
index 944264954432e538d4715f2e620cf058d40d4a17..624ef6fe3b9f16d62192499b7a0f40ed87a3aa29 100644 (file)
@@ -97,6 +97,25 @@ enum {
 /* CompressionMethod */
 #define TLS_COMPRESSION_NULL 0
 
+/* HashAlgorithm */
+enum {
+       TLS_HASH_ALG_NONE = 0,
+       TLS_HASH_ALG_MD5 = 1,
+       TLS_HASH_ALG_SHA1 = 2,
+       TLS_HASH_ALG_SHA224 = 3,
+       TLS_HASH_ALG_SHA256 = 4,
+       TLS_HASH_ALG_SHA384 = 5,
+       TLS_HASH_ALG_SHA512 = 6
+};
+
+/* SignatureAlgorithm */
+enum {
+       TLS_SIGN_ALG_ANONYMOUS = 0,
+       TLS_SIGN_ALG_RSA = 1,
+       TLS_SIGN_ALG_DSA = 2,
+       TLS_SIGN_ALG_ECDSA = 3,
+};
+
 /* AlertLevel */
 #define TLS_ALERT_LEVEL_WARNING 1
 #define TLS_ALERT_LEVEL_FATAL 2
index e727806e4cc043b2bfa20a9f92a56fce5e46d60a..11116f10c5687f1ac25c565382a3abadf25ea3b2 100644 (file)
@@ -842,6 +842,47 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
 
        hpos = hash;
 
+#ifdef CONFIG_TLSV12
+       if (conn->rl.tls_version == TLS_VERSION_1_2) {
+               /*
+                * RFC 5246, 4.7:
+                * TLS v1.2 adds explicit indication of the used signature and
+                * hash algorithms.
+                *
+                * struct {
+                *   HashAlgorithm hash;
+                *   SignatureAlgorithm signature;
+                * } SignatureAndHashAlgorithm;
+                */
+               if (end - pos < 2) {
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_DECODE_ERROR);
+                       return -1;
+               }
+               if (pos[0] != TLS_HASH_ALG_SHA256 ||
+                   pos[1] != TLS_SIGN_ALG_RSA) {
+                       wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/"
+                                  "signature(%u) algorithm",
+                                  pos[0], pos[1]);
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_INTERNAL_ERROR);
+                       return -1;
+               }
+               pos += 2;
+
+               hlen = SHA256_MAC_LEN;
+               if (conn->verify.sha256_cert == NULL ||
+                   crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) <
+                   0) {
+                       conn->verify.sha256_cert = NULL;
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_INTERNAL_ERROR);
+                       return -1;
+               }
+               conn->verify.sha256_cert = NULL;
+       } else {
+#endif /* CONFIG_TLSV12 */
+
        if (alg == SIGN_ALG_RSA) {
                hlen = MD5_MAC_LEN;
                if (conn->verify.md5_cert == NULL ||
@@ -872,6 +913,10 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
        if (alg == SIGN_ALG_RSA)
                hlen += MD5_MAC_LEN;
 
+#ifdef CONFIG_TLSV12
+       }
+#endif /* CONFIG_TLSV12 */
+
        wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
 
        if (end - pos < 2) {
@@ -911,6 +956,41 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
        wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature",
                        buf, buflen);
 
+#ifdef CONFIG_TLSV12
+       if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+               /*
+                * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
+                *
+                * DigestInfo ::= SEQUENCE {
+                *   digestAlgorithm DigestAlgorithm,
+                *   digest OCTET STRING
+                * }
+                *
+                * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
+                *
+                * DER encoded DigestInfo for SHA256 per RFC 3447:
+                * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||
+                * H
+                */
+               if (buflen >= 19 + 32 &&
+                   os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01"
+                             "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0)
+               {
+                       wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = "
+                                  "SHA-256");
+                       os_memmove(buf, buf + 19, buflen - 19);
+                       buflen -= 19;
+               } else {
+                       wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized "
+                                  "DigestInfo");
+                       os_free(buf);
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_DECRYPT_ERROR);
+                       return -1;
+               }
+       }
+#endif /* CONFIG_TLSV12 */
+
        if (buflen != hlen || os_memcmp(buf, hash, buflen) != 0) {
                wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in "
                           "CertificateVerify - did not match with calculated "