diff --git a/src/ecdh.c b/src/ecdh.c
index 165c15c..0cd435d 100644
--- a/src/ecdh.c
+++ b/src/ecdh.c
@@ -1,444 +1,446 @@
/* 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;
+#ifdef SUPPORT_X448
case 30: ecdh->curve_name = "X448"; break;
+#endif
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);
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);
}
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);
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/ntbtls-int.h b/src/ntbtls-int.h
index c431152..ab50ea5 100644
--- a/src/ntbtls-int.h
+++ b/src/ntbtls-int.h
@@ -1,422 +1,430 @@
/* ntbtls-int.h - Internal version of ntbtls.h
* 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 .
*/
#ifndef NTBTLS_NTBTLS_INT_H
#define NTBTLS_NTBTLS_INT_H
#ifdef _NTBTLS_H
#error ntbtls.h already included
#endif
#include
#include "ntbtls.h"
#include "util.h"
+/*
+ * Macros to help building with different crypto library versions.
+ */
+#undef SUPPORT_X448
+#if GCRYPT_VERSION_NUMBER >= 0x010900 /* >= 1.9 */
+#define SUPPORT_X448 1
+#endif
+
/*
* Various constants
*/
#define TLS_MAJOR_VERSION_3 3
#define TLS_MINOR_VERSION_0 0 /* SSL v3.0 */
#define TLS_MINOR_VERSION_1 1 /* TLS v1.0 */
#define TLS_MINOR_VERSION_2 2 /* TLS v1.1 */
#define TLS_MINOR_VERSION_3 3 /* TLS v1.2 */
/* Define minimum and maximum supported versions. This is currently
TLS v1.2 only but we may support newer versions of TLS as soon as
they are standardized. */
#define TLS_MIN_MAJOR_VERSION TLS_MAJOR_VERSION_3
#define TLS_MIN_MINOR_VERSION TLS_MINOR_VERSION_3
#define TLS_MAX_MAJOR_VERSION TLS_MAJOR_VERSION_3
#define TLS_MAX_MINOR_VERSION TLS_MINOR_VERSION_3
#define TLS_RENEGO_MAX_RECORDS_DEFAULT 16
#define TLS_RENEGOTIATION_DISABLED 0
#define TLS_RENEGOTIATION_ENABLED 1
#define TLS_RENEGOTIATION_NOT_ENFORCED -1
#define TLS_COMPRESS_NULL 0
#define TLS_COMPRESS_DEFLATE 1
#define TLS_VERIFY_NONE 0
#define TLS_VERIFY_OPTIONAL 1
#define TLS_VERIFY_REQUIRED 2
#define TLS_LEGACY_RENEGOTIATION 0
#define TLS_SECURE_RENEGOTIATION 1
#define TLS_LEGACY_NO_RENEGOTIATION 0
#define TLS_LEGACY_ALLOW_RENEGOTIATION 1
#define TLS_LEGACY_BREAK_HANDSHAKE 2
#define TLS_TRUNCATED_HMAC_LEN 10 /* 80 bits, rfc 6066 section 7 */
/* Lifetime of session tickets in seconds. */
#define TLS_DEFAULT_TICKET_LIFETIME 86400
/* Maximum size of a MAC. */
#define TLS_MAX_MAC_SIZE 48
/*
* Size of the input/output buffer. Note: the RFC defines the default
* size of TLS messages. If you change the value here, other
* clients/servers may not be able to communicate with you anymore.
* Only change this value if you control both sides of the connection
* and have it reduced at both sides, or if you're using the Max
* Fragment Length extension and you know all your peers are using it
* too!
*/
#define TLS_MAX_CONTENT_LEN 16384
/*
* Allow extra bytes for record, authentication and encryption overhead:
* counter (8) + header (5) + IV(16) + MAC (16-48) + padding (0-256)
* and allow for a maximum of 1024 of compression expansion if
* enabled.
*/
#define TLS_COMPRESSION_ADD 1024
#define TLS_MAC_ADD 48 /* SHA-384 used for HMAC */
#define TLS_PADDING_ADD 256
#define TLS_BUFFER_LEN (TLS_MAX_CONTENT_LEN \
+ TLS_COMPRESSION_ADD \
+ 29 /* counter + header + IV */ \
+ TLS_MAC_ADD \
+ TLS_PADDING_ADD \
)
/*
* The size of the premaster secret.
*/
#define TLS_MPI_MAX_SIZE 512 /* 4096 bits */
#define TLS_ECP_MAX_BITS 521
#define TLS_ECP_MAX_BYTES ((TLS_ECP_MAX_BITS + 7)/8)
#define TLS_PSK_MAX_LEN 32 /* 256 bits */
/* Dummy type used only for its size */
union premaster_secret_u
{
unsigned char _pms_rsa[48]; /* RFC 5246 8.1.1 */
unsigned char _pms_dhm[TLS_MPI_MAX_SIZE]; /* RFC 5246 8.1.2 */
unsigned char _pms_ecdh[TLS_ECP_MAX_BYTES]; /* RFC 4492 5.10 */
unsigned char _pms_psk[4 + 2 * TLS_PSK_MAX_LEN]; /* RFC 4279 2 */
unsigned char _pms_dhe_psk[4 + TLS_MPI_MAX_SIZE
+ TLS_PSK_MAX_LEN]; /* RFC 4279 3 */
unsigned char _pms_rsa_psk[52 + TLS_PSK_MAX_LEN]; /* RFC 4279 4 */
unsigned char _pms_ecdhe_psk[4 + TLS_ECP_MAX_BYTES
+ TLS_PSK_MAX_LEN]; /* RFC 5489 2 */
};
#define TLS_PREMASTER_SIZE sizeof( union premaster_secret_u )
/* RFC 6066 section 4, see also mfl_code_to_length in protocol.c.
* NONE must be zero so that memset()ing structure to zero works. */
#define TLS_MAX_FRAG_LEN_NONE 0 /*!< don't use this extension */
#define TLS_MAX_FRAG_LEN_512 1 /*!< MaxFragmentLength 2^9 */
#define TLS_MAX_FRAG_LEN_1024 2 /*!< MaxFragmentLength 2^10 */
#define TLS_MAX_FRAG_LEN_2048 3 /*!< MaxFragmentLength 2^11 */
#define TLS_MAX_FRAG_LEN_4096 4 /*!< MaxFragmentLength 2^12 */
/*
* Supported hash and signature algorithms (for TLS 1.2).
* RFC 5246 section 7.4.1.4.1
*/
#define TLS_HASH_NONE 0
#define TLS_HASH_SHA1 2
#define TLS_HASH_SHA224 3
#define TLS_HASH_SHA256 4
#define TLS_HASH_SHA384 5
#define TLS_HASH_SHA512 6
#define TLS_SIG_ANON 0
#define TLS_SIG_RSA 1
#define TLS_SIG_ECDSA 3
/*
* Client Certificate Types
* RFC 5246 section 7.4.4 plus RFC 4492 section 5.5
*/
#define TLS_CERT_TYPE_RSA_SIGN 1
#define TLS_CERT_TYPE_ECDSA_SIGN 64
/*
* Message, alert and handshake types
*/
#define TLS_MSG_CHANGE_CIPHER_SPEC 20
#define TLS_MSG_ALERT 21
#define TLS_MSG_HANDSHAKE 22
#define TLS_MSG_APPLICATION_DATA 23
#define TLS_ALERT_LEVEL_WARNING 1
#define TLS_ALERT_LEVEL_FATAL 2
#define TLS_ALERT_MSG_CLOSE_NOTIFY 0 /* 0x00 */
#define TLS_ALERT_MSG_UNEXPECTED_MESSAGE 10 /* 0x0A */
#define TLS_ALERT_MSG_BAD_RECORD_MAC 20 /* 0x14 */
#define TLS_ALERT_MSG_DECRYPTION_FAILED 21 /* 0x15 */
#define TLS_ALERT_MSG_RECORD_OVERFLOW 22 /* 0x16 */
#define TLS_ALERT_MSG_DECOMPRESSION_FAILURE 30 /* 0x1E */
#define TLS_ALERT_MSG_HANDSHAKE_FAILURE 40 /* 0x28 */
#define TLS_ALERT_MSG_NO_CERT 41 /* 0x29 */
#define TLS_ALERT_MSG_BAD_CERT 42 /* 0x2A */
#define TLS_ALERT_MSG_UNSUPPORTED_CERT 43 /* 0x2B */
#define TLS_ALERT_MSG_CERT_REVOKED 44 /* 0x2C */
#define TLS_ALERT_MSG_CERT_EXPIRED 45 /* 0x2D */
#define TLS_ALERT_MSG_CERT_UNKNOWN 46 /* 0x2E */
#define TLS_ALERT_MSG_ILLEGAL_PARAMETER 47 /* 0x2F */
#define TLS_ALERT_MSG_UNKNOWN_CA 48 /* 0x30 */
#define TLS_ALERT_MSG_ACCESS_DENIED 49 /* 0x31 */
#define TLS_ALERT_MSG_DECODE_ERROR 50 /* 0x32 */
#define TLS_ALERT_MSG_DECRYPT_ERROR 51 /* 0x33 */
#define TLS_ALERT_MSG_EXPORT_RESTRICTION 60 /* 0x3C */
#define TLS_ALERT_MSG_PROTOCOL_VERSION 70 /* 0x46 */
#define TLS_ALERT_MSG_INSUFFICIENT_SECURITY 71 /* 0x47 */
#define TLS_ALERT_MSG_INTERNAL_ERROR 80 /* 0x50 */
#define TLS_ALERT_MSG_USER_CANCELED 90 /* 0x5A */
#define TLS_ALERT_MSG_NO_RENEGOTIATION 100 /* 0x64 */
#define TLS_ALERT_MSG_UNSUPPORTED_EXT 110 /* 0x6E */
#define TLS_ALERT_MSG_UNRECOGNIZED_NAME 112 /* 0x70 */
#define TLS_ALERT_MSG_UNKNOWN_PSK_IDENTITY 115 /* 0x73 */
#define TLS_ALERT_MSG_NO_APPLICATION_PROTOCOL 120 /* 0x78 */
#define TLS_HS_HELLO_REQUEST 0
#define TLS_HS_CLIENT_HELLO 1
#define TLS_HS_SERVER_HELLO 2
#define TLS_HS_NEW_SESSION_TICKET 4
#define TLS_HS_CERTIFICATE 11
#define TLS_HS_SERVER_KEY_EXCHANGE 12
#define TLS_HS_CERTIFICATE_REQUEST 13
#define TLS_HS_SERVER_HELLO_DONE 14
#define TLS_HS_CERTIFICATE_VERIFY 15
#define TLS_HS_CLIENT_KEY_EXCHANGE 16
#define TLS_HS_FINISHED 20
/*
* TLS extensions
*/
#define TLS_EXT_SERVERNAME 0
#define TLS_EXT_MAX_FRAGMENT_LENGTH 1
#define TLS_EXT_TRUNCATED_HMAC 4
#define TLS_EXT_SUPPORTED_ELLIPTIC_CURVES 10
#define TLS_EXT_SUPPORTED_POINT_FORMATS 11
#define TLS_EXT_SIG_ALG 13
#define TLS_EXT_ALPN 16
#define TLS_EXT_SESSION_TICKET 35
#define TLS_EXT_RENEGOTIATION_INFO 0xFF01
/* TLS extension flags (for extensions with outgoing ServerHello
* content that need it (e.g. for RENEGOTIATION_INFO the server
* already knows because of state of the renegotiation flag, so no
* indicator is required). */
#define TLS_EXT_SUPPORTED_POINT_FORMATS_PRESENT (1 << 0)
/*
* Signaling ciphersuite values (SCSV)
*/
#define TLS_EMPTY_RENEGOTIATION_INFO 0xFF
/*
* The structure definitions are in a separate file.
*/
#include "context.h"
/*
* Inline functions etc.
*/
/* Return the private key object from the context object or NULL if
there is none. */
static inline x509_privkey_t
tls_own_key (ntbtls_t tls)
{
return tls->handshake->key_cert? tls->handshake->key_cert->key : NULL;
}
/* Return the certifciate key object from the context object or NULL
if there is none. */
static inline x509_cert_t
tls_own_cert (ntbtls_t tls)
{
return tls->handshake->key_cert? tls->handshake->key_cert->cert : NULL;
}
/*
* Prototypes
*/
/*-- util.c --*/
const char *_ntbtls_check_version (const char *req_version);
char *_ntbtls_trim_trailing_spaces (char *string);
int _ntbtls_ascii_strcasecmp (const char *a, const char *b);
/*-- protocol.c --*/
const char *_ntbtls_state2str (tls_state_t state);
gpg_error_t _ntbtls_fetch_input (ntbtls_t tls, size_t nb_want);
gpg_error_t _ntbtls_flush_output (ntbtls_t tls);
gpg_error_t _ntbtls_write_record (ntbtls_t tls);
gpg_error_t _ntbtls_read_record (ntbtls_t tls);
gpg_error_t _ntbtls_send_fatal_handshake_failure (ntbtls_t tls);
gpg_error_t _ntbtls_send_alert_message (ntbtls_t tls, unsigned char level,
unsigned char message);
pk_algo_t _ntbtls_pk_alg_from_sig (unsigned char sig);
md_algo_t _ntbtls_md_alg_from_hash (unsigned char hash);
gpg_error_t _ntbtls_derive_keys (ntbtls_t tls);
void _ntbtls_optimize_checksum (ntbtls_t tls,
const ciphersuite_t ciphersuite_info);
gpg_error_t _ntbtls_psk_derive_premaster (ntbtls_t tls,
key_exchange_type_t kex);
gpg_error_t _ntbtls_write_certificate (ntbtls_t tls);
gpg_error_t _ntbtls_read_certificate (ntbtls_t tls);
gpg_error_t _ntbtls_write_change_cipher_spec (ntbtls_t tls);
gpg_error_t _ntbtls_read_change_cipher_spec (ntbtls_t tls);
gpg_error_t _ntbtls_write_finished (ntbtls_t tls);
gpg_error_t _ntbtls_read_finished (ntbtls_t tls);
void _ntbtls_handshake_wrapup (ntbtls_t tls);
/* Functions directly used by the public API. */
gpg_error_t _ntbtls_new (ntbtls_t *r_tls, unsigned int flags);
void _ntbtls_release (ntbtls_t tls);
const char *_ntbtls_get_last_alert (ntbtls_t tls, unsigned int *r_level,
unsigned int *r_type);
gpg_error_t _ntbtls_set_transport (ntbtls_t tls,
gpgrt_stream_t inbound,
gpgrt_stream_t outbound);
gpg_error_t _ntbtls_get_stream (ntbtls_t tls,
gpgrt_stream_t *r_readfp,
gpgrt_stream_t *r_writefp);
gpg_error_t _ntbtls_set_verify_cb (ntbtls_t tls,
ntbtls_verify_cb_t cb, void *cb_value);
gpg_error_t _ntbtls_set_hostname (ntbtls_t tls, const char *hostname);
const char *_ntbtls_get_hostname (ntbtls_t tls);
gpg_error_t _ntbtls_handshake (ntbtls_t tls);
/*-- protocol-srv.c --*/
gpg_error_t _ntbtls_handshake_server_step (ntbtls_t tls);
/*-- protocol-cli.c --*/
gpg_error_t _ntbtls_handshake_client_step (ntbtls_t tls);
/*-- pkglue.c --*/
gpg_error_t _ntbtls_pk_verify (x509_cert_t chain,
pk_algo_t pk_alg, md_algo_t md_alg,
const unsigned char *hash, size_t hashlen,
const unsigned char *sig, size_t siglen);
gpg_error_t _ntbtls_pk_encrypt (x509_cert_t chain, const unsigned char *input,
size_t ilen, unsigned char *output,
size_t *olen, size_t osize);
/*-- x509.c --*/
/*
*X509 Verify codes - FIXME: Replace them by ksba stuff.
*/
#define BADCERT_EXPIRED 0x01 /* The certificate validity has expired. */
#define BADCERT_REVOKED 0x02 /* The certificate has been revoked (is on a CRL). */
#define BADCERT_CN_MISMATCH 0x04 /* The certificate Common Name (CN) does not match with the expected CN. */
#define BADCERT_NOT_TRUSTED 0x08 /* The certificate is not correctly signed by the trusted CA. */
#define BADCRL_NOT_TRUSTED 0x10 /* CRL is not correctly signed by the trusted CA. */
#define BADCRL_EXPIRED 0x20 /* CRL is expired. */
#define BADCERT_MISSING 0x40 /* Certificate was missing. */
#define BADCERT_SKIP_VERIFY 0x80 /* Certificate verification was skipped. */
#define BADCERT_OTHER 0x0100 /* Other reason (can be used by verify callback) */
#define BADCERT_FUTURE 0x0200 /* The certificate validity starts in the future. */
#define BADCRL_FUTURE 0x0400 /* The CRL is from the future */
gpg_error_t _ntbtls_x509_cert_new (x509_cert_t *r_cert);
void _ntbtls_x509_cert_release (x509_cert_t crt);
gpg_error_t _ntbtls_x509_append_cert (x509_cert_t cert,
const void *der, size_t derlen);
void _ntbtls_x509_log_cert (const char *text, x509_cert_t chain, int full);
const unsigned char *_ntbtls_x509_get_cert (x509_cert_t cert, int idx,
size_t *r_derlen);
ksba_cert_t _ntbtls_x509_get_peer_cert (ntbtls_t tls, int idx);
gpg_error_t _ntbtls_x509_get_pk (x509_cert_t cert, int idx, gcry_sexp_t *r_pk);
int _ntbtls_x509_can_do (x509_privkey_t privkey, pk_algo_t pkalgo);
gpg_error_t _ntbtls_x509_check_hostname (x509_cert_t cert,
const char *hostname);
/*-- dhm.c --*/
gpg_error_t _ntbtls_dhm_new (dhm_context_t *r_dhm);
void _ntbtls_dhm_release (dhm_context_t dhm);
gpg_error_t _ntbtls_dhm_read_params (dhm_context_t dhm,
const void *der, size_t derlen,
size_t *r_nparsed);
unsigned int _ntbtls_dhm_get_nbits (dhm_context_t dhm);
gpg_error_t _ntbtls_dhm_make_public (dhm_context_t dhm,
unsigned char *outbuf, size_t outbufsize,
size_t *r_outbuflen);
gpg_error_t _ntbtls_dhm_calc_secret (dhm_context_t dhm,
unsigned char *outbuf, size_t outbufsize,
size_t *r_outbuflen);
/*-- ecdh.c --*/
gpg_error_t _ntbtls_ecdh_new (ecdh_context_t *r_ecdh);
void _ntbtls_ecdh_release (ecdh_context_t ecdh);
gpg_error_t _ntbtls_ecdh_read_params (ecdh_context_t ecdh,
const void *der, size_t derlen,
size_t *r_nparsed);
gpg_error_t _ntbtls_ecdh_make_public (ecdh_context_t ecdh,
unsigned char *outbuf, size_t outbufsize,
size_t *r_outbuflen);
gpg_error_t _ntbtls_ecdh_calc_secret (ecdh_context_t ecdh,
unsigned char *outbuf, size_t outbufsize,
size_t *r_outbuflen);
#endif /*NTBTLS_NTBTLS_INT_H*/
diff --git a/src/protocol-cli.c b/src/protocol-cli.c
index e22f6e5..cf36548 100644
--- a/src/protocol-cli.c
+++ b/src/protocol-cli.c
@@ -1,2180 +1,2182 @@
/* 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 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;
+#ifdef SUPPORT_X448
elliptic_curve_list[elliptic_curve_len++] = 0;
elliptic_curve_list[elliptic_curve_len++] = 30;
+#endif
*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;
}