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