diff --git a/src/ecdh.c b/src/ecdh.c index 85b58f0..165c15c 100644 --- a/src/ecdh.c +++ b/src/ecdh.c @@ -1,324 +1,444 @@ /* ecdh.c - EC Diffie-Hellman key exchange * Copyright (C) 2014, 2017 g10 Code GmbH * * This file is part of NTBTLS * * NTBTLS is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * NTBTLS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include "ntbtls-int.h" /* While running the validation function we need to keep track of the * certificates and the validation outcome of each. We use this type * for it. */ struct ecdh_context_s { const char *curve_name; /* Only for display purposes. */ gcry_ctx_t ecctx; /* The initialized context for the curve. * This also holds the secre D and our * public key Q. */ gcry_mpi_point_t Qpeer; /* The peer's public value */ }; /* Create a new ECDH context. */ gpg_error_t _ntbtls_ecdh_new (ecdh_context_t *r_ecdh) { ecdh_context_t ecdh; *r_ecdh = NULL; ecdh = calloc (1, sizeof *ecdh); if (!ecdh) return gpg_error_from_syserror (); *r_ecdh = ecdh; return 0; } /* Release an ECDH context. */ void _ntbtls_ecdh_release (ecdh_context_t ecdh) { if (!ecdh) return; gcry_ctx_release (ecdh->ecctx); gcry_mpi_point_release (ecdh->Qpeer); free (ecdh); } /* Parse the TLS ECDHE parameters and store them in ECDH. DER is the * buffer with the params of length DERLEN. The number of actual * parsed bytes is stored at R_NPARSED. */ gpg_error_t _ntbtls_ecdh_read_params (ecdh_context_t ecdh, const void *_der, size_t derlen, size_t *r_nparsed) { gpg_error_t err; const unsigned char *derstart = _der; const unsigned char *der = _der; size_t n; gcry_mpi_t tmpmpi; if (r_nparsed) *r_nparsed = 0; if (!ecdh || !der) return gpg_error (GPG_ERR_INV_ARG); ecdh->curve_name = NULL; gcry_ctx_release (ecdh->ecctx); ecdh->ecctx = NULL; gcry_mpi_point_release (ecdh->Qpeer); ecdh->Qpeer = NULL; /* struct { * ECParameters curve_params; * ECPoint public; * } ServerECDHParams; */ /* Parse ECParameters. */ if (derlen < 3) return gpg_error (GPG_ERR_TOO_SHORT); /* We only support named curves (3). */ if (*der != 3) return gpg_error (GPG_ERR_UNKNOWN_CURVE); der++; derlen--; switch (buf16_to_uint (der)) { case 23: ecdh->curve_name = "secp256r1"; break; case 24: ecdh->curve_name = "secp384r1"; break; case 25: ecdh->curve_name = "secp521r1"; break; case 26: ecdh->curve_name = "brainpoolP256r1"; break; case 27: ecdh->curve_name = "brainpoolP384r1"; break; case 28: ecdh->curve_name = "brainpoolP512r1"; break; + case 29: ecdh->curve_name = "X25519"; break; + case 30: ecdh->curve_name = "X448"; break; default: return gpg_error (GPG_ERR_UNKNOWN_CURVE); } der += 2; derlen -= 2; err = gcry_mpi_ec_new (&ecdh->ecctx, NULL, ecdh->curve_name); if (err) return err; /* Parse ECPoint. */ if (derlen < 2) return gpg_error (GPG_ERR_TOO_SHORT); n = *der++; derlen--; if (!n) return gpg_error (GPG_ERR_INV_OBJ); if (n > derlen) return gpg_error (GPG_ERR_TOO_LARGE); tmpmpi = gcry_mpi_set_opaque_copy (NULL, der, 8*n); if (!tmpmpi) return gpg_error_from_syserror (); der += n; derlen -= n; ecdh->Qpeer = gcry_mpi_point_new (0); err = gcry_mpi_ec_decode_point (ecdh->Qpeer, tmpmpi, ecdh->ecctx); gcry_mpi_release (tmpmpi); if (err) { gcry_mpi_point_release (ecdh->Qpeer); ecdh->Qpeer = NULL; return err; } if (r_nparsed) *r_nparsed = (der - derstart); debug_msg (3, "ECDH curve: %s", ecdh->curve_name); - debug_pnt (3, "ECDH Qpeer", ecdh->Qpeer, ecdh->ecctx); + if (ecdh->curve_name[0] != 'X') + debug_pnt (3, "ECDH Qpeer", ecdh->Qpeer, ecdh->ecctx); + /* FIXME: debug print the point for Montgomery curve. */ return 0; } /* Generate the secret D with 0 < D < N. */ static gcry_mpi_t gen_d (ecdh_context_t ecdh) { unsigned int nbits; gcry_mpi_t n, d; + if (ecdh->curve_name[0] == 'X') + { + gcry_mpi_t p; + unsigned int pbits; + void *rnd; + int len; + + p = gcry_mpi_ec_get_mpi ("p", ecdh->ecctx, 0); + if (!p) + return NULL; + pbits = gcry_mpi_get_nbits (p); + len = (pbits+7)/8; + gcry_mpi_release (p); + + rnd = gcry_random_bytes_secure (len, GCRY_STRONG_RANDOM); + d = gcry_mpi_set_opaque (NULL, rnd, pbits); + return d; + } + n = gcry_mpi_ec_get_mpi ("n", ecdh->ecctx, 0); if (!n) return NULL; nbits = gcry_mpi_get_nbits (n); d = gcry_mpi_snew (nbits); for (;;) { /* FIXME: For the second and further iterations we use too much * random. It would be better to get just a few bits and use * set/clear_bit to insert that into the D. Or implement a * suitable gen_d function in libgcrypt. */ gcry_mpi_randomize (d, nbits, GCRY_STRONG_RANDOM); /* Make sure we have the requested number of bits. The code * looks a bit weird but it is easy to understand if you * consider that mpi_set_highbit clears all higher bits. */ if (mpi_test_bit (d, nbits-1)) mpi_set_highbit (d, nbits-1); else mpi_clear_highbit (d, nbits-1); if (mpi_cmp (d, n) < 0 /* check: D < N */ && mpi_cmp_ui (d, 0) > 0) /* check: D > 0 */ break; /* okay */ } gcry_mpi_release (n); return d; } /* Create our own private value D and a public key. Store the public key in OUTBUF. OUTBUFSIZE is the available length of OUTBUF. On success the actual length of OUTBUF is stored at R_OUTBUFLEN. */ gpg_error_t _ntbtls_ecdh_make_public (ecdh_context_t ecdh, unsigned char *outbuf, size_t outbufsize, size_t *r_outbuflen) { gpg_error_t err; size_t n; if (!ecdh || !outbuf || !r_outbuflen || outbufsize < 2) return gpg_error (GPG_ERR_INV_ARG); *r_outbuflen = 0; if (!ecdh->curve_name || !ecdh->ecctx || !ecdh->Qpeer) return gpg_error (GPG_ERR_NOT_INITIALIZED); /* Create a secret and store it in the context. */ { gcry_mpi_t d; d = gen_d (ecdh); if (!d) return gpg_error (GPG_ERR_INV_OBJ); gcry_mpi_ec_set_mpi ("d", d, ecdh->ecctx); debug_mpi (3, "ECDH d ", d); gcry_mpi_release (d); } - { - gcry_mpi_t Q; - - /* Note that "q" is computed by the get function and returned in - * uncompressed form. */ - Q = gcry_mpi_ec_get_mpi ("q", ecdh->ecctx, 0); - if (!Q) - { + if (ecdh->curve_name[0] == 'X') + { + gcry_mpi_t p; + unsigned int pbits; + gcry_mpi_point_t Q; + gcry_mpi_t x; + int i; + int len; + + p = gcry_mpi_ec_get_mpi ("p", ecdh->ecctx, 0); + if (!p) return gpg_error (GPG_ERR_INTERNAL); - } - debug_mpi (3, "ECDH Qour ", Q); - - /* Write as an ECPoint, that is prefix it with a one octet length. */ - err = gcry_mpi_print (GCRYMPI_FMT_USG, outbuf+1, outbufsize-1, &n, Q); - gcry_mpi_release (Q); - if (err) - return err; - if (n > 255) - return gpg_error (GPG_ERR_INV_DATA); - outbuf[0] = n; - n++; - } + pbits = gcry_mpi_get_nbits (p); + len = (pbits+7)/8; + gcry_mpi_release (p); + if (len > 255) + return gpg_error (GPG_ERR_INV_DATA); + + x = gcry_mpi_new (0); + Q = gcry_mpi_ec_get_point ("q", ecdh->ecctx, 0); + if (!Q) + { + gcry_mpi_release (x); + return gpg_error (GPG_ERR_INTERNAL); + } + if (gcry_mpi_ec_get_affine (x, NULL, Q, ecdh->ecctx)) + { + gcry_mpi_point_release (Q); + return gpg_error (GPG_ERR_INV_DATA); + } + + gcry_mpi_point_release (Q); + debug_mpi (3, "ECDH Qour (in big-endian)", x); + + err = gcry_mpi_print (GCRYMPI_FMT_USG, outbuf+1, outbufsize-1, &n, x); + gcry_mpi_release (x); + if (err) + return err; + /* Fill zero, if shorter. */ + if (n < len) + { + memmove (outbuf+1+len-n, outbuf+1, n); + memset (outbuf+1, 0, len - n); + } + /* Reverse the buffer */ + for (i = 0; i < len/2; i++) + { + unsigned int tmp; + + tmp = outbuf[i+1]; + outbuf[i+1] = outbuf[len-i]; + outbuf[len-i] = tmp; + } + outbuf[0] = len; + n = len + 1; + } + else + { + gcry_mpi_t Q; + + /* Note that "q" is computed by the get function and returned in + * uncompressed form. */ + Q = gcry_mpi_ec_get_mpi ("q", ecdh->ecctx, 0); + if (!Q) + { + return gpg_error (GPG_ERR_INTERNAL); + } + debug_mpi (3, "ECDH Qour ", Q); + + /* Write as an ECPoint, that is prefix it with a one octet length. */ + err = gcry_mpi_print (GCRYMPI_FMT_USG, outbuf+1, outbufsize-1, &n, Q); + gcry_mpi_release (Q); + if (err) + return err; + if (n > 255) + return gpg_error (GPG_ERR_INV_DATA); + outbuf[0] = n; + n++; + } *r_outbuflen = n; return 0; } /* Derive the shared secret Z and store it in OUTBUF. OUTBUFSIZE is * the available length of OUTBUF. On success the actual length of * OUTBUF is stored at R_OUTBUFLEN. */ gpg_error_t _ntbtls_ecdh_calc_secret (ecdh_context_t ecdh, unsigned char *outbuf, size_t outbufsize, size_t *r_outbuflen) { gpg_error_t err; gcry_mpi_point_t P = NULL; gcry_mpi_t d = NULL; gcry_mpi_t x = NULL; size_t n; if (!ecdh || !outbuf || !r_outbuflen) return gpg_error (GPG_ERR_INV_ARG); *r_outbuflen = 0; if (!ecdh->curve_name || !ecdh->ecctx || !ecdh->Qpeer) return gpg_error (GPG_ERR_NOT_INITIALIZED); /* 1. Check that Q_peer is on the curve * 2. Compute: P = d * Q_peer * 2. Check that P is not the point at infinity. * 3. Copy the x-coordinate of P to the output. */ if (!gcry_mpi_ec_curve_point (ecdh->Qpeer, ecdh->ecctx)) { err = gpg_error (GPG_ERR_INV_DATA); goto leave; } d = gcry_mpi_ec_get_mpi ("d", ecdh->ecctx, 0); if (!d) return gpg_error (GPG_ERR_NOT_INITIALIZED); P = gcry_mpi_point_new (0); gcry_mpi_ec_mul (P, d, ecdh->Qpeer, ecdh->ecctx); x = gcry_mpi_new (0); if (gcry_mpi_ec_get_affine (x, NULL, P, ecdh->ecctx)) { err = gpg_error (GPG_ERR_INV_DATA); goto leave; } err = gcry_mpi_print (GCRYMPI_FMT_USG, outbuf, outbufsize, &n, x); if (err) goto leave; + if (ecdh->curve_name[0] == 'X') + { + gcry_mpi_t p; + unsigned int pbits; + int i; + int len; + + p = gcry_mpi_ec_get_mpi ("p", ecdh->ecctx, 0); + if (!p) + { + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + pbits = gcry_mpi_get_nbits (p); + len = (pbits+7)/8; + gcry_mpi_release (p); + if (len > 255) + { + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + + /* Fill zero, if shorter. */ + if (n < len) + { + memmove (outbuf+len-n, outbuf, n); + memset (outbuf, 0, len - n); + } + /* Reverse the buffer */ + for (i = 0; i < len/2; i++) + { + unsigned int tmp; + + tmp = outbuf[i]; + outbuf[i] = outbuf[len-i-1]; + outbuf[len-i-1] = tmp; + } + n = len; + } + *r_outbuflen = n; leave: gcry_mpi_release (d); gcry_mpi_release (x); gcry_mpi_point_release (P); return err; } diff --git a/src/protocol-cli.c b/src/protocol-cli.c index 74c61b8..e22f6e5 100644 --- a/src/protocol-cli.c +++ b/src/protocol-cli.c @@ -1,2176 +1,2180 @@ /* protocol-cli.c - TLS 1.2 client side protocol * Copyright (C) 2006-2014, Brainspark B.V. * Copyright (C) 2014 g10 code GmbH * * This file is part of NTBTLS * * NTBTLS is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * NTBTLS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * This file was part of PolarSSL (http://www.polarssl.org). Former * Lead Maintainer: Paul Bakker . * Please do not file bug reports to them but to the address given in * the file AUTHORS in the top directory of NTBTLS. */ #include #include #include #include "ntbtls-int.h" #include "ciphersuites.h" static void write_hostname_ext (ntbtls_t tls, unsigned char *buf, size_t * olen) { unsigned char *p = buf; size_t len; *olen = 0; if (!tls->hostname) return; debug_msg (3, "client_hello, adding server name extension: '%s'", tls->hostname); len = strlen (tls->hostname); /* * struct { * NameType name_type; * select (name_type) { * case host_name: HostName; * } name; * } ServerName; * * enum { * host_name(0), (255) * } NameType; * * opaque HostName<1..2^16-1>; * * struct { * ServerName server_name_list<1..2^16-1> * } ServerNameList; */ *p++ = (unsigned char) ((TLS_EXT_SERVERNAME >> 8) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_SERVERNAME) & 0xFF); *p++ = (unsigned char) (((len + 5) >> 8) & 0xFF); *p++ = (unsigned char) (((len + 5)) & 0xFF); *p++ = (unsigned char) (((len + 3) >> 8) & 0xFF); *p++ = (unsigned char) (((len + 3)) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_SERVERNAME) & 0xFF); *p++ = (unsigned char) ((len >> 8) & 0xFF); *p++ = (unsigned char) ((len) & 0xFF); memcpy (p, tls->hostname, len); *olen = len + 9; } static void write_cli_renegotiation_ext (ntbtls_t ssl, unsigned char *buf, size_t * olen) { unsigned char *p = buf; *olen = 0; if (ssl->renegotiation != TLS_RENEGOTIATION) return; debug_msg (3, "client_hello, adding renegotiation extension"); /* * Secure renegotiation */ *p++ = (unsigned char) ((TLS_EXT_RENEGOTIATION_INFO >> 8) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_RENEGOTIATION_INFO) & 0xFF); *p++ = 0x00; *p++ = (ssl->verify_data_len + 1) & 0xFF; *p++ = ssl->verify_data_len & 0xFF; memcpy (p, ssl->own_verify_data, ssl->verify_data_len); *olen = 5 + ssl->verify_data_len; } static void write_signature_algorithms_ext (ntbtls_t ssl, unsigned char *buf, size_t * olen) { unsigned char *p = buf; size_t sig_alg_len = 0; unsigned char *sig_alg_list = buf + 6; *olen = 0; if (ssl->max_minor_ver != TLS_MINOR_VERSION_3) return; debug_msg (3, "client_hello, adding signature_algorithms extension"); /* * Prepare signature_algorithms extension (TLS 1.2) */ sig_alg_list[sig_alg_len++] = TLS_HASH_SHA512; sig_alg_list[sig_alg_len++] = TLS_SIG_RSA; sig_alg_list[sig_alg_len++] = TLS_HASH_SHA384; sig_alg_list[sig_alg_len++] = TLS_SIG_RSA; sig_alg_list[sig_alg_len++] = TLS_HASH_SHA256; sig_alg_list[sig_alg_len++] = TLS_SIG_RSA; sig_alg_list[sig_alg_len++] = TLS_HASH_SHA224; sig_alg_list[sig_alg_len++] = TLS_SIG_RSA; sig_alg_list[sig_alg_len++] = TLS_HASH_SHA1; sig_alg_list[sig_alg_len++] = TLS_SIG_RSA; sig_alg_list[sig_alg_len++] = TLS_HASH_SHA512; sig_alg_list[sig_alg_len++] = TLS_SIG_ECDSA; sig_alg_list[sig_alg_len++] = TLS_HASH_SHA384; sig_alg_list[sig_alg_len++] = TLS_SIG_ECDSA; sig_alg_list[sig_alg_len++] = TLS_HASH_SHA256; sig_alg_list[sig_alg_len++] = TLS_SIG_ECDSA; sig_alg_list[sig_alg_len++] = TLS_HASH_SHA224; sig_alg_list[sig_alg_len++] = TLS_SIG_ECDSA; sig_alg_list[sig_alg_len++] = TLS_HASH_SHA1; sig_alg_list[sig_alg_len++] = TLS_SIG_ECDSA; /* * enum { * none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5), * sha512(6), (255) * } HashAlgorithm; * * enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) } * SignatureAlgorithm; * * struct { * HashAlgorithm hash; * SignatureAlgorithm signature; * } SignatureAndHashAlgorithm; * * SignatureAndHashAlgorithm * supported_signature_algorithms<2..2^16-2>; */ *p++ = (unsigned char) ((TLS_EXT_SIG_ALG >> 8) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_SIG_ALG) & 0xFF); *p++ = (unsigned char) (((sig_alg_len + 2) >> 8) & 0xFF); *p++ = (unsigned char) (((sig_alg_len + 2)) & 0xFF); *p++ = (unsigned char) ((sig_alg_len >> 8) & 0xFF); *p++ = (unsigned char) ((sig_alg_len) & 0xFF); *olen = 6 + sig_alg_len; } static void write_supported_elliptic_curves_ext (ntbtls_t tls, unsigned char *buf, size_t * olen) { unsigned char *p = buf; unsigned char *elliptic_curve_list = p + 6; size_t elliptic_curve_len = 0; (void)tls; debug_msg (3, "client hello, adding supported_elliptic_curves extension"); - /* The 6 curves we support; see _ntbtls_ecdh_read_params. */ + /* The 8 curves we support; see _ntbtls_ecdh_read_params. */ elliptic_curve_list[elliptic_curve_len++] = 0; elliptic_curve_list[elliptic_curve_len++] = 23; elliptic_curve_list[elliptic_curve_len++] = 0; elliptic_curve_list[elliptic_curve_len++] = 24; elliptic_curve_list[elliptic_curve_len++] = 0; elliptic_curve_list[elliptic_curve_len++] = 25; elliptic_curve_list[elliptic_curve_len++] = 0; elliptic_curve_list[elliptic_curve_len++] = 26; elliptic_curve_list[elliptic_curve_len++] = 0; elliptic_curve_list[elliptic_curve_len++] = 27; elliptic_curve_list[elliptic_curve_len++] = 0; elliptic_curve_list[elliptic_curve_len++] = 28; + elliptic_curve_list[elliptic_curve_len++] = 0; + elliptic_curve_list[elliptic_curve_len++] = 29; + elliptic_curve_list[elliptic_curve_len++] = 0; + elliptic_curve_list[elliptic_curve_len++] = 30; *p++ = (unsigned char) ((TLS_EXT_SUPPORTED_ELLIPTIC_CURVES >> 8) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_SUPPORTED_ELLIPTIC_CURVES) & 0xFF); *p++ = (unsigned char) (((elliptic_curve_len + 2) >> 8) & 0xFF); *p++ = (unsigned char) (((elliptic_curve_len + 2)) & 0xFF); *p++ = (unsigned char) (((elliptic_curve_len) >> 8) & 0xFF); *p++ = (unsigned char) (((elliptic_curve_len)) & 0xFF); *olen = 6 + elliptic_curve_len; } static void write_cli_supported_point_formats_ext (ntbtls_t tls, unsigned char *buf, size_t *olen) { unsigned char *p = buf; (void)tls; debug_msg (3, "client hello, adding supported_point_formats extension"); *p++ = (unsigned char) ((TLS_EXT_SUPPORTED_POINT_FORMATS >> 8) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_SUPPORTED_POINT_FORMATS) & 0xFF); *p++ = 0; *p++ = 2; *p++ = 1; /* One item. */ *p++ = 0; /* Uncompressed. */ *olen = 6; } static void write_cli_max_fragment_length_ext (ntbtls_t tls, unsigned char *buf, size_t *olen) { unsigned char *p = buf; if (tls->mfl_code == TLS_MAX_FRAG_LEN_NONE) { *olen = 0; return; } debug_msg (3, "client_hello, adding max_fragment_length extension"); *p++ = (unsigned char) ((TLS_EXT_MAX_FRAGMENT_LENGTH >> 8) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_MAX_FRAGMENT_LENGTH) & 0xFF); *p++ = 0x00; *p++ = 1; *p++ = tls->mfl_code; *olen = 5; } static void write_cli_truncated_hmac_ext (ntbtls_t tls, unsigned char *buf, size_t * olen) { unsigned char *p = buf; if (!tls->use_trunc_hmac) { *olen = 0; return; } debug_msg (3, "client_hello, adding truncated_hmac extension"); *p++ = (unsigned char) ((TLS_EXT_TRUNCATED_HMAC >> 8) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_TRUNCATED_HMAC) & 0xFF); *p++ = 0x00; *p++ = 0x00; *olen = 4; } static void write_cli_session_ticket_ext (ntbtls_t ssl, unsigned char *buf, size_t * olen) { unsigned char *p = buf; size_t tlen = ssl->session_negotiate->ticket_len; if (!ssl->use_session_tickets) { *olen = 0; return; } debug_msg (3, "client_hello, adding session ticket extension"); *p++ = (unsigned char) ((TLS_EXT_SESSION_TICKET >> 8) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_SESSION_TICKET) & 0xFF); *p++ = (unsigned char) ((tlen >> 8) & 0xFF); *p++ = (unsigned char) ((tlen) & 0xFF); *olen = 4; if (ssl->session_negotiate->ticket == NULL || ssl->session_negotiate->ticket_len == 0) { return; } debug_msg (3, "sending session_ticket of length %zu", tlen); memcpy (p, ssl->session_negotiate->ticket, tlen); *olen += tlen; } static void write_cli_alpn_ext (ntbtls_t ssl, unsigned char *buf, size_t * olen) { unsigned char *p = buf; const char **cur; if (ssl->alpn_list == NULL) { *olen = 0; return; } debug_msg (3, "client hello, adding alpn extension"); *p++ = (unsigned char) ((TLS_EXT_ALPN >> 8) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_ALPN) & 0xFF); /* * opaque ProtocolName<1..2^8-1>; * * struct { * ProtocolName protocol_name_list<2..2^16-1> * } ProtocolNameList; */ /* Skip writing extension and list length for now */ p += 4; for (cur = ssl->alpn_list; *cur != NULL; cur++) { *p = (unsigned char) (strlen (*cur) & 0xFF); memcpy (p + 1, *cur, *p); p += 1 + *p; } *olen = p - buf; /* List length = olen - 2 (ext_type) - 2 (ext_len) - 2 (list_len) */ buf[4] = (unsigned char) (((*olen - 6) >> 8) & 0xFF); buf[5] = (unsigned char) (((*olen - 6)) & 0xFF); /* Extension length = olen - 2 (ext_type) - 2 (ext_len) */ buf[2] = (unsigned char) (((*olen - 4) >> 8) & 0xFF); buf[3] = (unsigned char) (((*olen - 4)) & 0xFF); } static gpg_error_t write_client_hello (ntbtls_t tls) { gpg_error_t err; size_t i, n, olen; size_t ext_len = 0; unsigned char *buf; unsigned char *p, *q; time_t t; const int *ciphersuites; ciphersuite_t suite; debug_msg (2, "write client_hello"); if (tls->renegotiation == TLS_INITIAL_HANDSHAKE) { tls->major_ver = tls->min_major_ver; tls->minor_ver = tls->min_minor_ver; } if (tls->max_major_ver == 0 && tls->max_minor_ver == 0) { tls->max_major_ver = TLS_MAX_MAJOR_VERSION; tls->max_minor_ver = TLS_MAX_MINOR_VERSION; } /* * 0 . 0 handshake type * 1 . 3 handshake length * 4 . 5 highest version supported * 6 . 9 current UNIX time * 10 . 37 random bytes */ buf = tls->out_msg; p = buf + 4; *p++ = (unsigned char) tls->max_major_ver; *p++ = (unsigned char) tls->max_minor_ver; debug_msg (3, "client_hello, max version: [%d:%d]", buf[4], buf[5]); t = time (NULL); *p++ = (unsigned char) (t >> 24); *p++ = (unsigned char) (t >> 16); *p++ = (unsigned char) (t >> 8); *p++ = (unsigned char) (t); debug_msg (3, "client_hello, current time: %lu", t); //FIXME: Check RNG requirements. gcry_create_nonce (p, 28); p += 28; memcpy (tls->handshake->randbytes, buf + 6, 32); debug_buf (3, "client_hello, random bytes", buf + 6, 32); /* * 38 . 38 session id length * 39 . 39+n session id * 40+n . 41+n ciphersuitelist length * 42+n . .. ciphersuitelist * .. . .. compression methods length * .. . .. compression methods * .. . .. extensions length * .. . .. extensions */ n = tls->session_negotiate->length; if (tls->renegotiation != TLS_INITIAL_HANDSHAKE || n < 16 || n > 32 || tls->handshake->resume == 0) { n = 0; } /* * RFC 5077 section 3.4: "When presenting a ticket, the client MAY * generate and include a Session ID in the TLS ClientHello." */ if (tls->renegotiation == TLS_INITIAL_HANDSHAKE && tls->session_negotiate->ticket != NULL && tls->session_negotiate->ticket_len != 0) { gcry_create_nonce (tls->session_negotiate->id, 32); tls->session_negotiate->length = n = 32; } *p++ = (unsigned char) n; for (i = 0; i < n; i++) *p++ = tls->session_negotiate->id[i]; debug_msg (3, "client_hello, session id len.: %zu", n); debug_buf (3, "client_hello, session id", buf + 39, n); // Fixme: We do not have a way to set the ciphersuites. Thus // consider to replace this with simpler code. ciphersuites = tls->ciphersuite_list[tls->minor_ver]; n = 0; q = p; /* Skip writing ciphersuite length for now. */ p += 2; /* * Add TLS_EMPTY_RENEGOTIATION_INFO_SCSV */ if (tls->renegotiation == TLS_INITIAL_HANDSHAKE) { *p++ = (unsigned char) (TLS_EMPTY_RENEGOTIATION_INFO >> 8); *p++ = (unsigned char) (TLS_EMPTY_RENEGOTIATION_INFO); n++; } /*FIXME: We should add an explicit limit and not rely on the known length of the ciphersuites. */ for (i = 0; ciphersuites && ciphersuites[i]; i++) { suite = _ntbtls_ciphersuite_from_id (ciphersuites[i]); if (!suite) continue; if (!_ntbtls_ciphersuite_version_ok (suite, tls->min_minor_ver, tls->max_minor_ver)) continue; debug_msg (5, "client_hello, add ciphersuite: %5d %s", ciphersuites[i], _ntbtls_ciphersuite_get_name (ciphersuites[i])); n++; *p++ = (unsigned char) (ciphersuites[i] >> 8); *p++ = (unsigned char) (ciphersuites[i]); } /* Fixup the ciphersuite length. */ *q++ = (unsigned char) (n >> 7); *q++ = (unsigned char) (n << 1); debug_msg (3, "client_hello, got %zu ciphersuites", n); debug_msg (3, "client_hello, compress len.: %d", 2); debug_msg (3, "client_hello, compress alg.: %d %d", TLS_COMPRESS_DEFLATE, TLS_COMPRESS_NULL); *p++ = 2; *p++ = TLS_COMPRESS_DEFLATE; *p++ = TLS_COMPRESS_NULL; /* First write extensions, then the total length. */ write_hostname_ext (tls, p + 2 + ext_len, &olen); ext_len += olen; write_cli_renegotiation_ext (tls, p + 2 + ext_len, &olen); ext_len += olen; write_signature_algorithms_ext (tls, p + 2 + ext_len, &olen); ext_len += olen; write_supported_elliptic_curves_ext (tls, p + 2 + ext_len, &olen); ext_len += olen; write_cli_supported_point_formats_ext (tls, p + 2 + ext_len, &olen); ext_len += olen; write_cli_max_fragment_length_ext (tls, p + 2 + ext_len, &olen); ext_len += olen; write_cli_truncated_hmac_ext (tls, p + 2 + ext_len, &olen); ext_len += olen; write_cli_session_ticket_ext (tls, p + 2 + ext_len, &olen); ext_len += olen; write_cli_alpn_ext (tls, p + 2 + ext_len, &olen); ext_len += olen; debug_msg (3, "client_hello, total extension length: %zu", ext_len); if (ext_len > 0) { *p++ = (unsigned char) ((ext_len >> 8) & 0xFF); *p++ = (unsigned char) ((ext_len) & 0xFF); p += ext_len; } tls->out_msglen = p - buf; tls->out_msgtype = TLS_MSG_HANDSHAKE; tls->out_msg[0] = TLS_HS_CLIENT_HELLO; tls->state++; err = _ntbtls_write_record (tls); if (err) { debug_ret (1, "write_record", err); return err; } return 0; } static gpg_error_t parse_renegotiation_info (ntbtls_t tls, const unsigned char *buf, size_t len) { gpg_error_t err; if (tls->renegotiation == TLS_INITIAL_HANDSHAKE) { if (len != 1 || buf[0] != 0x0) { debug_msg (1, "non-zero length renegotiated connection field"); err = _ntbtls_send_fatal_handshake_failure (tls); if (!err) err = gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); return err; } tls->secure_renegotiation = TLS_SECURE_RENEGOTIATION; } else { /* Check verify-data in constant-time. The length OTOH is no secret */ if (len != 1 + tls->verify_data_len * 2 || buf[0] != tls->verify_data_len * 2 || memcmpct (buf + 1, tls->own_verify_data, tls->verify_data_len) || memcmpct (buf + 1 + tls->verify_data_len, tls->peer_verify_data, tls->verify_data_len)) { debug_msg (1, "non-matching renegotiated connection field"); err = _ntbtls_send_fatal_handshake_failure (tls); if (!err) err = gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); return err; } } return 0; } static gpg_error_t parse_max_fragment_length_ext (ntbtls_t tls, const unsigned char *buf, size_t len) { /* * server should use the extension only if we did, * and if so the server's value should match ours (and len is always 1) */ if (tls->mfl_code == TLS_MAX_FRAG_LEN_NONE || len != 1 || buf[0] != tls->mfl_code) { return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } return 0; } static gpg_error_t parse_truncated_hmac_ext (ntbtls_t tls, const unsigned char *buf, size_t len) { (void)buf; if (!tls->use_trunc_hmac || len) { return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } tls->session_negotiate->use_trunc_hmac = 1; return 0; } static gpg_error_t parse_session_ticket_ext (ntbtls_t tls, const unsigned char *buf, size_t len) { (void)buf; if (!tls->use_session_tickets || len) { return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } tls->handshake->new_session_ticket = 1; return 0; } static gpg_error_t parse_supported_point_formats_ext (ntbtls_t ssl, const unsigned char *buf, size_t len) { size_t list_size; const unsigned char *p; list_size = buf[0]; if (list_size + 1 != len) { debug_msg (1, "bad server hello message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } p = buf + 1; while (list_size > 0) { if (p[0] == 0) { /* Fixme: Store the format - right now not required because * we support only one format. */ /* ssl->handshake->ecdh_ctx.point_format = p[0]; */ (void)ssl; debug_msg (4, "point format selected: %d", p[0]); return 0; } list_size--; p++; } debug_msg (1, "no point format in common"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } static gpg_error_t parse_alpn_ext (ntbtls_t tls, const unsigned char *buf, size_t len) { size_t list_len, name_len; const char **p; /* If we didn't send it, the server shouldn't send it */ if (!tls->alpn_list) return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); /* * opaque ProtocolName<1..2^8-1>; * * struct { * ProtocolName protocol_name_list<2..2^16-1> * } ProtocolNameList; * * the "ProtocolNameList" MUST contain exactly one "ProtocolName" */ /* Min length is 2 (list_len) + 1 (name_len) + 1 (name) */ if (len < 4) return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); list_len = buf16_to_size_t (buf); if (list_len != len - 2) return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); name_len = buf[2]; if (name_len != list_len - 1) return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); /* Check that the server chosen protocol was in our list and save it */ for (p = tls->alpn_list; *p; p++) { if (name_len == strlen (*p) && !memcmp (buf + 3, *p, name_len)) { tls->alpn_chosen = *p; return 0; } } return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } static gpg_error_t read_server_hello (ntbtls_t tls) { gpg_error_t err; int i, suite_id, comp; size_t n; size_t ext_len = 0; unsigned char *buf, *ext; int renegotiation_info_seen = 0; int handshake_failure = 0; const int *ciphersuites; uint32_t t; debug_msg (2, "read server_hello"); /* * 0 . 0 handshake type * 1 . 3 handshake length * 4 . 5 protocol version * 6 . 9 UNIX time() * 10 . 37 random bytes */ buf = tls->in_msg; err = _ntbtls_read_record (tls); if (err) { debug_ret (1, "read_record", err); return err; } if (tls->in_msgtype != TLS_MSG_HANDSHAKE) { debug_msg (1, "bad server_hello message"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } debug_msg (1, "server_hello, chosen version: [%d:%d]", buf[4], buf[5]); if (tls->in_hslen < 42 || buf[0] != TLS_HS_SERVER_HELLO || buf[4] != TLS_MAJOR_VERSION_3) { debug_msg (1, "bad server_hello message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } if (buf[5] > tls->max_minor_ver) { debug_msg (1, "bad server_hello message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } tls->minor_ver = buf[5]; if (tls->minor_ver < tls->min_minor_ver) { debug_msg (1, "server only supports TLS smaller than minimum" " [%d:%d] < [%d:%d]", tls->major_ver, tls->minor_ver, buf[4], buf[5]); _ntbtls_send_alert_message (tls, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_MSG_PROTOCOL_VERSION); return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL); } t = buf32_to_u32 (buf+6); debug_msg (3, "server_hello, current time: %lu", (unsigned long)t); memcpy (tls->handshake->randbytes + 32, buf + 6, 32); n = buf[38]; debug_buf (3, "server_hello, random bytes", buf + 6, 32); if (n > 32) { debug_msg (1, "bad server_hello message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } /* * 38 . 38 session id length * 39 . 38+n session id * 39+n . 40+n chosen ciphersuite * 41+n . 41+n chosen compression alg. * 42+n . 43+n extensions length * 44+n . 44+n+m extensions */ if (tls->in_hslen > 42 + n) { ext_len = buf16_to_size_t (buf + 42 + n); if ((ext_len > 0 && ext_len < 4) || tls->in_hslen != 44 + n + ext_len) { debug_msg (1, "bad server_hello message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } } suite_id = buf16_to_uint (buf + 39 + n); comp = buf[41 + n]; /* * Initialize update checksum functions */ tls->transform_negotiate->ciphersuite = _ntbtls_ciphersuite_from_id (suite_id); if (!tls->transform_negotiate->ciphersuite) { debug_msg (1, "ciphersuite info for %04x not found", suite_id); return gpg_error (GPG_ERR_INV_ARG); } _ntbtls_optimize_checksum (tls, tls->transform_negotiate->ciphersuite); debug_msg (3, "server_hello, session id len.: %zu", n); debug_buf (3, "server_hello, session id", buf + 39, n); /* * Check if the session can be resumed */ if (tls->renegotiation != TLS_INITIAL_HANDSHAKE || !tls->handshake->resume || !n || tls->session_negotiate->ciphersuite != suite_id || tls->session_negotiate->compression != comp || tls->session_negotiate->length != n || memcmp (tls->session_negotiate->id, buf + 39, n)) { tls->state++; tls->handshake->resume = 0; tls->session_negotiate->start = time (NULL); tls->session_negotiate->ciphersuite = suite_id; tls->session_negotiate->compression = comp; tls->session_negotiate->length = n; memcpy (tls->session_negotiate->id, buf + 39, n); } else { tls->state = TLS_SERVER_CHANGE_CIPHER_SPEC; err = _ntbtls_derive_keys (tls); if (err) { debug_ret (1, "derive_keys", err); return err; } } debug_msg (3, "%s session has been resumed", tls->handshake->resume ? "a" : "no"); debug_msg (1, "server_hello, chosen ciphersuite: %d (%s)", suite_id, _ntbtls_ciphersuite_get_name (suite_id)); debug_msg (3, "server_hello, compress alg.: %d", buf[41 + n]); /* Check that we support the cipher suite. */ ciphersuites = tls->ciphersuite_list[tls->minor_ver]; if (ciphersuites) { for (i=0; ciphersuites[i]; i++) if (ciphersuites[i] == tls->session_negotiate->ciphersuite) break; } if (!ciphersuites || !ciphersuites[i]) { debug_msg (1, "bad server_hello message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } if (comp != TLS_COMPRESS_NULL && comp != TLS_COMPRESS_DEFLATE) { debug_msg (1, "bad server_hello message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } tls->session_negotiate->compression = comp; ext = buf + 44 + n; debug_msg (2, "server_hello, total extension length: %zu", ext_len); while (ext_len) { unsigned int ext_id = buf16_to_uint (ext); unsigned int ext_size = buf16_to_uint (ext+2); if (ext_size + 4 > ext_len) { debug_msg (1, "bad server_hello message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } switch (ext_id) { case TLS_EXT_RENEGOTIATION_INFO: debug_msg (2, "found renegotiation extension"); renegotiation_info_seen = 1; err = parse_renegotiation_info (tls, ext + 4, ext_size); if (err) return err; break; case TLS_EXT_MAX_FRAGMENT_LENGTH: debug_msg (2, "found max_fragment_length extension"); err = parse_max_fragment_length_ext (tls, ext + 4, ext_size); if (err) return err; break; case TLS_EXT_TRUNCATED_HMAC: debug_msg (2, "found truncated_hmac extension"); err = parse_truncated_hmac_ext (tls, ext + 4, ext_size); if (err) return err; break; case TLS_EXT_SESSION_TICKET: debug_msg (2, "found session_ticket extension"); err = parse_session_ticket_ext (tls, ext + 4, ext_size); if (err) return err; break; case TLS_EXT_SUPPORTED_POINT_FORMATS: debug_msg (2, "found supported_point_formats extension"); err = parse_supported_point_formats_ext (tls, ext + 4, ext_size); if (err) return err; break; case TLS_EXT_ALPN: debug_msg (2, "found alpn extension"); err = parse_alpn_ext (tls, ext + 4, ext_size); if (err) return err; break; default: debug_msg (2, "unknown extension found: %d (ignoring)", ext_id); break; } ext_len -= 4 + ext_size; ext += 4 + ext_size; if (ext_len > 0 && ext_len < 4) { debug_msg (1, "bad server_hello message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } } /* * Renegotiation security checks */ if (tls->secure_renegotiation == TLS_LEGACY_RENEGOTIATION && tls->allow_legacy_renegotiation == TLS_LEGACY_BREAK_HANDSHAKE) { debug_msg (1, "legacy renegotiation, breaking off handshake"); handshake_failure = 1; } else if (tls->renegotiation == TLS_RENEGOTIATION && tls->secure_renegotiation == TLS_SECURE_RENEGOTIATION && !renegotiation_info_seen) { debug_msg (1, "renegotiation_info extension missing (secure)"); handshake_failure = 1; } else if (tls->renegotiation == TLS_RENEGOTIATION && tls->secure_renegotiation == TLS_LEGACY_RENEGOTIATION && tls->allow_legacy_renegotiation == TLS_LEGACY_NO_RENEGOTIATION) { debug_msg (1, "legacy renegotiation not allowed"); handshake_failure = 1; } else if (tls->renegotiation == TLS_RENEGOTIATION && tls->secure_renegotiation == TLS_LEGACY_RENEGOTIATION && renegotiation_info_seen) { debug_msg (1, "renegotiation_info extension present (legacy)"); handshake_failure = 1; } if (handshake_failure) { err = _ntbtls_send_fatal_handshake_failure (tls); if (!err) err = gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); return err; } return 0; } static gpg_error_t parse_server_dh_params (ntbtls_t tls, unsigned char **p, unsigned char *end) { gpg_error_t err; unsigned int nbits; size_t n; /* * Ephemeral DH parameters: * * struct { * opaque dh_p<1..2^16-1>; * opaque dh_g<1..2^16-1>; * opaque dh_Ys<1..2^16-1>; * } ServerDHParams; */ err = _ntbtls_dhm_read_params (tls->handshake->dhm_ctx, *p, end - *p, &n); if (err) { debug_ret (2, "dhm_read_params", err); return err; } *p += n; nbits = _ntbtls_dhm_get_nbits (tls->handshake->dhm_ctx); if (nbits < 1024 || nbits > 4096) { debug_msg (1, "bad server key exchange message (DHM length: %u)", nbits); return gpg_error (GPG_ERR_BAD_HS_SERVER_KEX); } return 0; } static int parse_server_ecdh_params (ntbtls_t tls, unsigned char **p, unsigned char *end) { gpg_error_t err; size_t n; if ((err = _ntbtls_ecdh_read_params (tls->handshake->ecdh_ctx, *p, end - *p, &n))) { debug_ret (1, "ecdh_read_params", err); return err; } *p += n; return 0; } static gpg_error_t parse_server_psk_hint (ntbtls_t tls, unsigned char **p, unsigned char *end) { size_t len; (void)tls; /* * PSK parameters: * * opaque psk_identity_hint<0..2^16-1>; */ if (*p + 1 < end) { debug_msg (1, "bad server key exchange message" " (psk_identity_hint too short)"); return gpg_error (GPG_ERR_BAD_HS_SERVER_KEX); } len = buf16_to_size_t (*p); *p += 2; if ((*p) + len > end) { debug_msg (1, "bad server key exchange message" " (psk_identity_hint too long)"); return gpg_error (GPG_ERR_BAD_HS_SERVER_KEX); } // TODO: Retrieve PSK identity hint and callback to app // *p += len; return 0; } /* * Generate a pre-master secret and encrypt it with the server's RSA key */ static gpg_error_t write_encrypted_pms (ntbtls_t tls, size_t offset, size_t *olen, size_t pms_offset) { gpg_error_t err; size_t len_bytes = tls->minor_ver == TLS_MINOR_VERSION_0 ? 0 : 2; unsigned char *p = tls->handshake->premaster + pms_offset; /* * Generate (part of) the pre-master as * struct { * ProtocolVersion client_version; * opaque random[46]; * } PreMasterSecret; */ p[0] = (unsigned char) tls->max_major_ver; p[1] = (unsigned char) tls->max_minor_ver; gcry_randomize (p + 2, 46, GCRY_STRONG_RANDOM); tls->handshake->pmslen = 48; /* * Now write it out, encrypted */ //FIXME: Need a cert related can_do function. /* if (!_ntbtls_x509_foo_can_do (tls->session_negotiate->peer_chain, GCRY_PK_RSA)) */ /* { */ /* debug_msg (1, "certificate key type mismatch"); */ /* return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); */ /* } */ err = _ntbtls_pk_encrypt (tls->session_negotiate->peer_chain, p, tls->handshake->pmslen, tls->out_msg + offset + len_bytes, olen, TLS_MAX_CONTENT_LEN - offset - len_bytes); if (err) { debug_ret (1, "rsa_pkcs1_encrypt", err); return err; } if (len_bytes == 2) { tls->out_msg[offset + 0] = (unsigned char) (*olen >> 8); tls->out_msg[offset + 1] = (unsigned char) (*olen); *olen += 2; } return 0; } static gpg_error_t parse_signature_algorithm (ntbtls_t tls, unsigned char **p, unsigned char *end, md_algo_t *md_alg, pk_algo_t *pk_alg) { *md_alg = 0; *pk_alg = 0; /* Only in TLS 1.2 */ if (tls->minor_ver != TLS_MINOR_VERSION_3) { return 0; } if ((*p) + 2 > end) return gpg_error (GPG_ERR_BAD_HS_SERVER_KEX); /* * Get hash algorithm */ *md_alg = _ntbtls_md_alg_from_hash ((*p)[0]); if (!*md_alg) { debug_msg (2, "Server used unsupported HashAlgorithm %d", *(p)[0]); return gpg_error (GPG_ERR_BAD_HS_SERVER_KEX); } /* * Get signature algorithm */ *pk_alg = _ntbtls_pk_alg_from_sig ((*p)[1]); if (!*pk_alg) { debug_msg (2, "server used unsupported SignatureAlgorithm %d", (*p)[1]); return gpg_error (GPG_ERR_BAD_HS_SERVER_KEX); } debug_msg (2, "Server used HashAlgo %s", gcry_md_algo_name (*md_alg)); debug_msg (2, "Server used SignAlgo %s", gcry_pk_algo_name (*pk_alg)); *p += 2; return 0; } static gpg_error_t get_ecdh_params_from_cert (ntbtls_t tls) { (void)tls; //FIXME: /* int ret; */ /* const ecp_keypair *peer_key; */ /* if (!pk_can_do (&ssl->session_negotiate->peer_chain->pk, POLARSSL_PK_ECKEY)) */ /* { */ /* debug_msg (1, "server key not ECDH capable"); */ /* return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); */ /* } */ /* peer_key = pk_ec (ssl->session_negotiate->peer_chain->pk); */ /* if ((ret = ecdh_get_params (&ssl->handshake->ecdh_ctx, peer_key, */ /* POLARSSL_ECDH_THEIRS)) != 0) */ /* { */ /* debug_ret (1, ("ecdh_get_params"), ret); */ /* return (ret); */ /* } */ /* if (ssl_check_server_ecdh_params (ssl) != 0) */ /* { */ /* debug_msg (1, "bad server certificate (ECDH curve)"); */ /* return gpg_error (GPG_ERR_BAD_HS_CERT); */ /* } */ return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } static gpg_error_t read_server_key_exchange (ntbtls_t tls) { gpg_error_t err; const ciphersuite_t suite = tls->transform_negotiate->ciphersuite; key_exchange_type_t kex = _ntbtls_ciphersuite_get_kex (suite); unsigned char *p, *end; size_t sig_len, params_len; unsigned char hash[64]; md_algo_t md_alg = 0; size_t hashlen; pk_algo_t pk_alg = 0; if (kex == KEY_EXCHANGE_RSA) { debug_msg (2, "skipping read server_key_exchange"); tls->state++; return 0; } if (kex == KEY_EXCHANGE_ECDH_RSA || kex == KEY_EXCHANGE_ECDH_ECDSA) { err = get_ecdh_params_from_cert (tls); if (err) { debug_ret (1, "get_ecdh_params_from_cert", err); return err; } debug_msg (2, "skipping read server_key_exchange"); tls->state++; return 0; } debug_msg (2, "read server_key_exchange"); err = _ntbtls_read_record (tls); if (err) { debug_ret (1, "read_record", err); return err; } if (tls->in_msgtype != TLS_MSG_HANDSHAKE) { debug_msg (1, "bad server_key_exchange message (%d)", __LINE__); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } /* * ServerKeyExchange may be skipped with PSK and RSA-PSK when the server * doesn't use a psk_identity_hint. */ if (tls->in_msg[0] != TLS_HS_SERVER_KEY_EXCHANGE) { if (kex == KEY_EXCHANGE_PSK || kex == KEY_EXCHANGE_RSA_PSK) { tls->record_read = 1; goto leave; } debug_msg (1, "bad server_key_exchange message (%d)", __LINE__); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } p = tls->in_msg + 4; end = tls->in_msg + tls->in_hslen; debug_buf (3, "server_key_exchange", p, tls->in_hslen - 4); if (kex == KEY_EXCHANGE_PSK || kex == KEY_EXCHANGE_RSA_PSK || kex == KEY_EXCHANGE_DHE_PSK || kex == KEY_EXCHANGE_ECDHE_PSK) { err = parse_server_psk_hint (tls, &p, end); if (err) { debug_msg (1, "bad server_key_exchange message (%d)", __LINE__); return err; } } if (kex == KEY_EXCHANGE_PSK || kex == KEY_EXCHANGE_RSA_PSK) ; /* Nothing more to do. */ else if (kex == KEY_EXCHANGE_DHE_RSA || kex == KEY_EXCHANGE_DHE_PSK) { err = parse_server_dh_params (tls, &p, end); if (err) { debug_msg (1, "bad server_key_exchange message (%d)", __LINE__); return err; } } else if (kex == KEY_EXCHANGE_ECDHE_RSA || kex == KEY_EXCHANGE_ECDHE_PSK || kex == KEY_EXCHANGE_ECDHE_ECDSA) { err = parse_server_ecdh_params (tls, &p, end); if (err) { debug_msg (1, "bad server_key_exchange message (%d)", __LINE__); return err; } } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } if (kex == KEY_EXCHANGE_DHE_RSA || kex == KEY_EXCHANGE_ECDHE_RSA || kex == KEY_EXCHANGE_ECDHE_ECDSA) { params_len = p - (tls->in_msg + 4); /* * Handle the digitally-signed structure */ if (tls->minor_ver == TLS_MINOR_VERSION_3) { err = parse_signature_algorithm (tls, &p, end, &md_alg, &pk_alg); if (err) { debug_msg (1, "bad server_key_exchange message (%d): %s", __LINE__, gpg_strerror (err)); return err; } if (pk_alg != _ntbtls_ciphersuite_get_sig_pk_alg (suite)) { debug_msg (1, "bad server_key_exchange message (%d): %s", __LINE__, gpg_strerror (err)); return gpg_error (GPG_ERR_BAD_HS_SERVER_KEX); } //FIXME: Check that the ECC subtype matches. */ } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } /* * Read signature */ sig_len = buf16_to_size_t (p); p += 2; if (end != p + sig_len) { debug_msg (1, "bad server_key_exchange message (%d)", __LINE__); return gpg_error (GPG_ERR_BAD_HS_SERVER_KEX); } debug_buf (3, "signature", p, sig_len); /* * Compute the hash that has been signed */ if (md_alg) { gcry_buffer_t iov[2]; memset (iov, 0, sizeof iov); /* * digitally-signed struct { * opaque client_random[32]; * opaque server_random[32]; * ServerDHParams params; * }; */ iov[0].data = tls->handshake->randbytes; iov[0].len = 64; iov[1].data = tls->in_msg + 4; iov[1].len = params_len; hashlen = gcry_md_get_algo_dlen (md_alg); if (hashlen > sizeof hash) err = gpg_error (GPG_ERR_BUG); else err = gcry_md_hash_buffers (md_alg, 0, hash, iov, 2); if (err) return err; } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } debug_buf (3, "parameters hash", hash, hashlen); /* * Verify signature */ err = _ntbtls_pk_verify (tls->session_negotiate->peer_chain, pk_alg, md_alg, hash, hashlen, p, sig_len); debug_ret (1, "pk_verify", err); if (err) return err; } leave: tls->state++; return 0; } static gpg_error_t read_certificate_request (ntbtls_t tls) { gpg_error_t err; unsigned char *buf, *p; size_t n = 0, m = 0; size_t cert_type_len = 0; size_t dn_len = 0; const ciphersuite_t suite = tls->transform_negotiate->ciphersuite; key_exchange_type_t kex = _ntbtls_ciphersuite_get_kex (suite); if (kex == KEY_EXCHANGE_PSK || kex == KEY_EXCHANGE_RSA_PSK || kex == KEY_EXCHANGE_DHE_PSK || kex == KEY_EXCHANGE_ECDHE_PSK) { debug_msg (2, "skipping read certificate_request"); tls->state++; return 0; } debug_msg (2, "read certificate_request"); /* * 0 . 0 handshake type * 1 . 3 handshake length * 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 * ... .. ... length of DN 2, etc. */ if (!tls->record_read) { err = _ntbtls_read_record (tls); if (err) { debug_ret (1, "read_record", err); return err; } if (tls->in_msgtype != TLS_MSG_HANDSHAKE) { debug_msg (1, "bad certificate_request message"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } tls->record_read = 1; } tls->client_auth = 0; tls->state++; if (tls->in_msg[0] == TLS_HS_CERTIFICATE_REQUEST) tls->client_auth++; debug_msg (3, "got %s certificate_request", tls->client_auth ? "a" : "no"); if (!tls->client_auth) goto leave; tls->record_read = 0; // TODO: handshake_failure alert for an anonymous server to request // client authentication buf = tls->in_msg; // Retrieve cert types // cert_type_len = buf[4]; n = cert_type_len; if (tls->in_hslen < 6 + n) { debug_msg (1, "bad certificate_request message"); return gpg_error (GPG_ERR_BAD_HS_CERT_REQ); } p = buf + 5; while (cert_type_len > 0) { if (*p == TLS_CERT_TYPE_RSA_SIGN && _ntbtls_x509_can_do (tls_own_key (tls), GCRY_PK_RSA)) { tls->handshake->cert_type = TLS_CERT_TYPE_RSA_SIGN; break; } else if (*p == TLS_CERT_TYPE_ECDSA_SIGN && _ntbtls_x509_can_do (tls_own_key (tls), GCRY_PK_ECDSA)) { tls->handshake->cert_type = TLS_CERT_TYPE_ECDSA_SIGN; break; } else { /* Unsupported cert type, ignore */ } cert_type_len--; p++; } if (tls->minor_ver == TLS_MINOR_VERSION_3) { /* Ignored, see comments about hash in write_certificate_verify */ // TODO: should check the signature part against our pk_key though size_t sig_alg_len = buf16_to_size_t (buf + 5 + n); p = buf + 7 + n; m += 2; n += sig_alg_len; if (tls->in_hslen < 6 + n) { debug_msg (1, "bad certificate_request message"); return gpg_error (GPG_ERR_BAD_HS_CERT_REQ); } } /* Ignore certificate_authorities, we only have one cert anyway */ // TODO: should not send cert if no CA matches dn_len = buf16_to_size_t (buf + 5 + m + n); n += dn_len; if (tls->in_hslen != 7 + m + n) { debug_msg (1, "bad certificate_request message"); return gpg_error (GPG_ERR_BAD_HS_CERT_REQ); } leave: return 0; } static gpg_error_t read_server_hello_done (ntbtls_t tls) { gpg_error_t err; debug_msg (2, "read server_hello_done"); if (!tls->record_read) { err = _ntbtls_read_record (tls); if (err) { debug_ret (1, "read_record", err); return err; } if (tls->in_msgtype != TLS_MSG_HANDSHAKE) { debug_msg (1, "bad server_hello_done message"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } } tls->record_read = 0; if (tls->in_hslen != 4 || tls->in_msg[0] != TLS_HS_SERVER_HELLO_DONE) { debug_msg (1, "bad server_hello_done message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO_DONE); } tls->state++; return 0; } static gpg_error_t write_client_key_exchange (ntbtls_t tls) { gpg_error_t err; size_t i, n; const ciphersuite_t suite = tls->transform_negotiate->ciphersuite; key_exchange_type_t kex = _ntbtls_ciphersuite_get_kex (suite); debug_msg (2, "write client_key_exchange"); if (kex == KEY_EXCHANGE_DHE_RSA) { /* * DHM key exchange -- send G^X mod P * * We don't have the remaining size of the buffer available, * thus we use a value which will always fit into our buffer. */ i = 4; err = _ntbtls_dhm_make_public (tls->handshake->dhm_ctx, tls->out_msg + i, 514, &n); if (err) { debug_ret (1, "dhm_make_public", err); return err; } err = _ntbtls_dhm_calc_secret (tls->handshake->dhm_ctx, tls->handshake->premaster, TLS_PREMASTER_SIZE, &tls->handshake->pmslen); if (err) { debug_ret (1, "dhm_calc_secret", err); return err; } } else if (kex == KEY_EXCHANGE_ECDHE_RSA || kex == KEY_EXCHANGE_ECDHE_ECDSA || kex == KEY_EXCHANGE_ECDH_RSA || kex == KEY_EXCHANGE_ECDH_ECDSA) { /* * ECDH key exchange -- send client public value */ i = 4; err = _ntbtls_ecdh_make_public (tls->handshake->ecdh_ctx, tls->out_msg + i, 1000, &n); if (err) { debug_ret (1, "ecdh_make_public", err); return err; } err = _ntbtls_ecdh_calc_secret (tls->handshake->ecdh_ctx, tls->handshake->premaster, TLS_PREMASTER_SIZE, &tls->handshake->pmslen); if (err) { debug_ret (1, "ecdh_calc_secret", err); return err; } } else if (kex == KEY_EXCHANGE_PSK || kex == KEY_EXCHANGE_RSA_PSK || kex == KEY_EXCHANGE_DHE_PSK || kex == KEY_EXCHANGE_ECDHE_PSK) { /* * opaque psk_identity<0..2^16-1>; */ if (!tls->psk || !tls->psk_identity) return gpg_error (GPG_ERR_NO_SECKEY); i = 4; n = tls->psk_identity_len; tls->out_msg[i++] = (unsigned char) (n >> 8); tls->out_msg[i++] = (unsigned char) (n); memcpy (tls->out_msg + i, tls->psk_identity, tls->psk_identity_len); i += tls->psk_identity_len; if (kex == KEY_EXCHANGE_PSK) { n = 0; } else if (kex == KEY_EXCHANGE_RSA_PSK) { err = write_encrypted_pms (tls, i, &n, 2); if (err) return err; } else if (kex == KEY_EXCHANGE_DHE_PSK) { /* * ClientDiffieHellmanPublic public (DHM send G^X mod P) */ n = 0; //FIXME: tls->handshake->dhm_ctx.len; tls->out_msg[i++] = (unsigned char) (n >> 8); tls->out_msg[i++] = (unsigned char) (n); /* err = dhm_make_public (&tls->handshake->dhm_ctx, */ /* (int) mpi_size (&tls->handshake->dhm_ctx.P), */ /* &tls->out_msg[i], n); */ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); if (err) { debug_ret (1, "dhm_make_public", err); return err; } } else if (kex == KEY_EXCHANGE_ECDHE_PSK) { /* * ClientECDiffieHellmanPublic public; */ /* err = ecdh_make_public (&tls->handshake->ecdh_ctx, &n, */ /* &tls->out_msg[i], TLS_MAX_CONTENT_LEN - i); */ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); if (err) { debug_ret (1, "ecdh_make_public", err); return err; } /* SSL_DEBUG_ECP (3, "ECDH: Q", &tls->handshake->ecdh_ctx.Q); */ } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } err = _ntbtls_psk_derive_premaster (tls, kex); if (err) { debug_ret (1, "psk_derive_premaster", err); return err; } } else if (kex == KEY_EXCHANGE_RSA) { i = 4; err = write_encrypted_pms (tls, i, &n, 0); if (err) return err; } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } err = _ntbtls_derive_keys (tls); if (err) { debug_ret (1, "derive_keys", err); return err; } tls->out_msglen = i + n; tls->out_msgtype = TLS_MSG_HANDSHAKE; tls->out_msg[0] = TLS_HS_CLIENT_KEY_EXCHANGE; tls->state++; err = _ntbtls_write_record (tls); if (err) { debug_ret (1, "write_record", err); return err; } return 0; } static gpg_error_t write_certificate_verify (ntbtls_t tls) { gpg_error_t err; const ciphersuite_t suite = tls->transform_negotiate->ciphersuite; key_exchange_type_t kex = _ntbtls_ciphersuite_get_kex (suite); size_t n = 0; size_t offset = 0; unsigned char hash[48]; unsigned char *hash_start = hash; md_algo_t md_alg = 0; unsigned int hashlen; if (kex == KEY_EXCHANGE_PSK || kex == KEY_EXCHANGE_RSA_PSK || kex == KEY_EXCHANGE_ECDHE_PSK || kex == KEY_EXCHANGE_DHE_PSK || !tls->client_auth || !tls_own_cert (tls)) { debug_msg (2, "skipping write certificate_verify"); tls->state++; return 0; } debug_msg (2, "write certificate_verify"); if (!tls_own_key (tls)) { debug_msg (1, "got no private key"); return gpg_error (GPG_ERR_NO_SECKEY); } /* * Make an RSA signature of the handshake digests */ tls->handshake->calc_verify (tls, hash); if (tls->minor_ver == TLS_MINOR_VERSION_3) { /* * 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 (_ntbtls_ciphersuite_get_mac (tls->transform_negotiate->ciphersuite) == GCRY_MAC_HMAC_SHA384) { md_alg = GCRY_MD_SHA384; tls->out_msg[4] = TLS_HASH_SHA384; hashlen = 48; } else { md_alg = GCRY_MD_SHA256; tls->out_msg[4] = TLS_HASH_SHA256; hashlen = 32; } tls->out_msg[5] = 0; //FIXME: ssl_sig_from_pk (ssl_own_key (tls)); offset = 2; } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } /* err = pk_sign (tls_own_key (tls), md_alg, hash_start, hashlen, */ /* tls->out_msg + 6 + offset, &n); */ (void)md_alg; (void)hash_start; (void)hashlen; err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); if (err) { debug_ret (1, "pk_sign", err); return err; } tls->out_msg[4 + offset] = (unsigned char) (n >> 8); tls->out_msg[5 + offset] = (unsigned char) (n); tls->out_msglen = 6 + n + offset; tls->out_msgtype = TLS_MSG_HANDSHAKE; tls->out_msg[0] = TLS_HS_CERTIFICATE_VERIFY; tls->state++; err = _ntbtls_write_record (tls); if (err) { debug_ret (1, "write_record", err); return err; } return 0; } static gpg_error_t parse_new_session_ticket (ntbtls_t tls) { gpg_error_t err; uint32_t lifetime; size_t ticket_len; unsigned char *ticket; debug_msg (2, "read new_session_ticket"); err = _ntbtls_read_record (tls); if (err) { debug_ret (1, "read_record", err); return err; } if (tls->in_msgtype != TLS_MSG_HANDSHAKE) { debug_msg (1, "bad new_session_ticket message"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } /* * struct { * uint32 ticket_lifetime_hint; * opaque ticket<0..2^16-1>; * } NewSessionTicket; * * 0 . 0 handshake message type * 1 . 3 handshake message length * 4 . 7 ticket_lifetime_hint * 8 . 9 ticket_len (n) * 10 . 9+n ticket content */ if (tls->in_msg[0] != TLS_HS_NEW_SESSION_TICKET || tls->in_hslen < 10) { debug_msg (1, "bad new_session_ticket message"); return gpg_error (GPG_ERR_BAD_TICKET); } lifetime = buf32_to_u32 (tls->in_msg + 4); ticket_len = buf16_to_size_t (tls->in_msg + 8); if (ticket_len + 10 != tls->in_hslen) { debug_msg (1, "bad new_session_ticket message"); return gpg_error (GPG_ERR_BAD_TICKET); } debug_msg (3, "ticket length: %zu", ticket_len); /* We're not waiting for a NewSessionTicket message any more */ tls->handshake->new_session_ticket = 0; /* * Zero-length ticket means the server changed his mind and doesn't want * to send a ticket after all, so just forget it */ if (!ticket_len) return 0; wipememory (tls->session_negotiate->ticket, tls->session_negotiate->ticket_len); free (tls->session_negotiate->ticket); tls->session_negotiate->ticket = NULL; tls->session_negotiate->ticket_len = 0; ticket = malloc (ticket_len); if (!ticket) { err = gpg_error_from_syserror (); debug_msg (1, "ticket malloc failed"); return err; } memcpy (ticket, tls->in_msg + 10, ticket_len); tls->session_negotiate->ticket = ticket; tls->session_negotiate->ticket_len = ticket_len; tls->session_negotiate->ticket_lifetime = lifetime; /* * RFC 5077 section 3.4: * "If the client receives a session ticket from the server, then it * discards any Session ID that was sent in the ServerHello." */ debug_msg (3, "ticket in use, discarding session id"); tls->session_negotiate->length = 0; return 0; } /* * SSL handshake -- client side -- single step */ gpg_error_t _ntbtls_handshake_client_step (ntbtls_t tls) { gpg_error_t err; if (tls->state == TLS_HANDSHAKE_OVER) return gpg_error (GPG_ERR_INV_STATE); debug_msg (2, "client state: %d (%s)", tls->state, _ntbtls_state2str (tls->state)); err = _ntbtls_flush_output (tls); if (err) return err; switch (tls->state) { case TLS_HELLO_REQUEST: tls->state = TLS_CLIENT_HELLO; break; /* * ==> ClientHello */ case TLS_CLIENT_HELLO: err = write_client_hello (tls); break; /* * <== ServerHello * Certificate * ( ServerKeyExchange ) * ( CertificateRequest ) * ServerHelloDone */ case TLS_SERVER_HELLO: err = read_server_hello (tls); break; case TLS_SERVER_CERTIFICATE: err = _ntbtls_read_certificate (tls); break; case TLS_SERVER_KEY_EXCHANGE: err = read_server_key_exchange (tls); break; case TLS_CERTIFICATE_REQUEST: err = read_certificate_request (tls); break; case TLS_SERVER_HELLO_DONE: err = read_server_hello_done (tls); break; /* * ==> ( Certificate/Alert ) * ClientKeyExchange * ( CertificateVerify ) * ChangeCipherSpec * Finished */ case TLS_CLIENT_CERTIFICATE: err = _ntbtls_write_certificate (tls); break; case TLS_CLIENT_KEY_EXCHANGE: err = write_client_key_exchange (tls); break; case TLS_CERTIFICATE_VERIFY: err = write_certificate_verify (tls); break; case TLS_CLIENT_CHANGE_CIPHER_SPEC: err = _ntbtls_write_change_cipher_spec (tls); break; case TLS_CLIENT_FINISHED: err = _ntbtls_write_finished (tls); break; /* * <== ( NewSessionTicket ) * ChangeCipherSpec * Finished */ case TLS_SERVER_CHANGE_CIPHER_SPEC: if (tls->handshake->new_session_ticket) err = parse_new_session_ticket (tls); else err = _ntbtls_read_change_cipher_spec (tls); break; case TLS_SERVER_FINISHED: err = _ntbtls_read_finished (tls); break; case TLS_FLUSH_BUFFERS: debug_msg (2, "handshake: done"); tls->state = TLS_HANDSHAKE_WRAPUP; break; case TLS_HANDSHAKE_WRAPUP: _ntbtls_handshake_wrapup (tls); break; default: debug_msg (1, "invalid state %d", tls->state); err = gpg_error (GPG_ERR_INV_STATE); break; } return err; }