Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34311277
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
65 KB
Subscribers
None
View Options
diff --git a/src/ecdh.c b/src/ecdh.c
index da7e5a2..85b58f0 100644
--- a/src/ecdh.c
+++ b/src/ecdh.c
@@ -1,316 +1,324 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ksba.h>
#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--;
- /* And only the secp256r1 curve (23). */
- if (buf16_to_uint (der) != 23)
- return gpg_error (GPG_ERR_UNKNOWN_CURVE);
+
+ 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;
+ default:
+ return gpg_error (GPG_ERR_UNKNOWN_CURVE);
+ }
der += 2;
derlen -= 2;
- ecdh->curve_name = "secp256r1";
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);
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;
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)
{
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;
*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 54e8244..f34a7a0 100644
--- a/src/protocol-cli.c
+++ b/src/protocol-cli.c
@@ -1,2163 +1,2173 @@
/* 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 <http://www.gnu.org/licenses/>.
*
* This file was part of PolarSSL (http://www.polarssl.org). Former
* Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>.
* Please do not file bug reports to them but to the address given in
* the file AUTHORS in the top directory of NTBTLS.
*/
#include <config.h>
#include <stdlib.h>
#include <time.h>
#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");
- /* We only support curve secp256r1 (23). */
+ /* The 6 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;
*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 %d", 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.: %d", 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 %d 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: %d", 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", 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.: %d", 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: %d", 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); */
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: %d", 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;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Dec 29, 6:58 AM (7 h, 11 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
e8/24/60dd38675822f0c9503c8d9c8063
Attached To
rT Not Too Bad TLS
Event Timeline
Log In to Comment