Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34085858
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
91 KB
Subscribers
None
View Options
diff --git a/src/ecdh.c b/src/ecdh.c
index fec5643..4bc1627 100644
--- a/src/ecdh.c
+++ b/src/ecdh.c
@@ -1,471 +1,490 @@
/* ecdh.c - EC Diffie-Hellman key exchange
* Copyright (C) 2014, 2017 g10 Code GmbH
*
* This file is part of NTBTLS
*
* NTBTLS is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* NTBTLS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ksba.h>
#include "ntbtls-int.h"
/* While running the validation function we need to keep track of the
* certificates and the validation outcome of each. We use this type
* for it. */
struct ecdh_context_s
{
const char *curve_name; /* Only for display purposes. */
+ unsigned int curve_id;
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);
}
gpg_error_t
_ntbtls_ecdh_peer_ec_point (ecdh_context_t ecdh,
const unsigned char *ecpoint, size_t ecpointlen)
{
gcry_mpi_t mpi;
gpg_error_t err;
mpi = gcry_mpi_set_opaque_copy (NULL, ecpoint, 8*ecpointlen);
if (!mpi)
return gpg_error_from_syserror ();
ecdh->Qpeer = gcry_mpi_point_new (0);
err = gcry_mpi_ec_decode_point (ecdh->Qpeer, mpi, ecdh->ecctx);
if (err)
{
gcry_mpi_point_release (ecdh->Qpeer);
ecdh->Qpeer = NULL;
}
gcry_mpi_release (mpi);
return err;
}
+unsigned int
+_ntbtls_ecdh_curve_id (ecdh_context_t ecdh)
+{
+ return ecdh->curve_id;
+}
+
gpg_error_t
_ntbtls_ecdh_curvename (ecdh_context_t ecdh, unsigned int curve_id)
{
+ if (curve_id == 0) /* Use default when not set. */
+ {
+ if (ecdh->curve_name)
+ /* Already initialized, do nothing. */
+ return 0;
+
+ curve_id = 29;
+ }
+ else
+ {
+ ecdh->curve_name = NULL;
+ gcry_ctx_release (ecdh->ecctx); ecdh->ecctx = NULL;
+ gcry_mpi_point_release (ecdh->Qpeer); ecdh->Qpeer = NULL;
+ }
+
switch (curve_id)
{
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;
#ifdef SUPPORT_X25519
case 29: ecdh->curve_name = "X25519"; break;
#endif
#ifdef SUPPORT_X448
case 30: ecdh->curve_name = "X448"; break;
#endif
default:
return gpg_error (GPG_ERR_UNKNOWN_CURVE);
}
+ ecdh->curve_id = curve_id;
return gcry_mpi_ec_new (&ecdh->ecctx, NULL, ecdh->curve_name);
}
/* 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;
unsigned int curve_id;
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--;
curve_id = buf16_to_uint (der);
der += 2;
derlen -= 2;
err = _ntbtls_ecdh_curvename (ecdh, curve_id);
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);
err = _ntbtls_ecdh_peer_ec_point (ecdh, der, n);
if (err)
return err;
der += n;
derlen -= n;
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)
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 0645384..925ebc3 100644
--- a/src/ntbtls-int.h
+++ b/src/ntbtls-int.h
@@ -1,439 +1,440 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef NTBTLS_NTBTLS_INT_H
#define NTBTLS_NTBTLS_INT_H
#ifdef _NTBTLS_H
#error ntbtls.h already included
#endif
#include <gcrypt.h>
#include "ntbtls.h"
#include "util.h"
/*
* Macros to help building with different crypto library versions.
*/
#undef SUPPORT_X25519
#undef SUPPORT_X448
#if GCRYPT_VERSION_NUMBER >= 0x010900 /* >= 1.9 */
#define SUPPORT_X25519 1
#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 TLS_MINOR_VERSION_4 4 /* TLS v1.3 */
/* 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_4
#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_SUPPORTED_VERSIONS 43
#define TLS_EXT_KEY_SHARE 51
#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);
gpg_error_t _ntbtls_ecdh_peer_ec_point (ecdh_context_t ecdh,
const unsigned char *ecpoint,
size_t ecpointlen);
gpg_error_t _ntbtls_ecdh_curvename (ecdh_context_t ecdh, unsigned int curve_id);
+unsigned int _ntbtls_ecdh_curve_id (ecdh_context_t ecdh);
#endif /*NTBTLS_NTBTLS_INT_H*/
diff --git a/src/protocol-cli.c b/src/protocol-cli.c
index 358a088..8b55d91 100644
--- a/src/protocol-cli.c
+++ b/src/protocol-cli.c
@@ -1,2253 +1,2346 @@
/* protocol-cli.c - TLS 1.2 client side protocol
* Copyright (C) 2006-2014, Brainspark B.V.
* Copyright (C) 2014 g10 code GmbH
*
* This file is part of NTBTLS
*
* NTBTLS is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* NTBTLS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* This file was part of PolarSSL (http://www.polarssl.org). Former
* Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>.
* Please do not file bug reports to them but to the address given in
* the file AUTHORS in the top directory of NTBTLS.
*/
#include <config.h>
#include <stdlib.h>
#include <time.h>
#include "ntbtls-int.h"
#include "ciphersuites.h"
static void
write_hostname_ext (ntbtls_t tls, unsigned char *buf, size_t * olen)
{
unsigned char *p = buf;
size_t len;
*olen = 0;
if (!tls->hostname)
return;
debug_msg (3, "client_hello, adding server name extension: '%s'",
tls->hostname);
len = strlen (tls->hostname);
/*
* struct {
* NameType name_type;
* select (name_type) {
* case host_name: HostName;
* } name;
* } ServerName;
*
* enum {
* host_name(0), (255)
* } NameType;
*
* opaque HostName<1..2^16-1>;
*
* struct {
* ServerName server_name_list<1..2^16-1>
* } ServerNameList;
*/
*p++ = (unsigned char) ((TLS_EXT_SERVERNAME >> 8) & 0xFF);
*p++ = (unsigned char) ((TLS_EXT_SERVERNAME) & 0xFF);
*p++ = (unsigned char) (((len + 5) >> 8) & 0xFF);
*p++ = (unsigned char) (((len + 5)) & 0xFF);
*p++ = (unsigned char) (((len + 3) >> 8) & 0xFF);
*p++ = (unsigned char) (((len + 3)) & 0xFF);
*p++ = (unsigned char) ((TLS_EXT_SERVERNAME) & 0xFF);
*p++ = (unsigned char) ((len >> 8) & 0xFF);
*p++ = (unsigned char) ((len) & 0xFF);
memcpy (p, tls->hostname, len);
*olen = len + 9;
}
static void
write_cli_renegotiation_ext (ntbtls_t ssl,
unsigned char *buf, size_t * olen)
{
unsigned char *p = buf;
*olen = 0;
if (ssl->renegotiation != TLS_RENEGOTIATION)
return;
debug_msg (3, "client_hello, adding renegotiation extension");
/*
* Secure renegotiation
*/
*p++ = (unsigned char) ((TLS_EXT_RENEGOTIATION_INFO >> 8) & 0xFF);
*p++ = (unsigned char) ((TLS_EXT_RENEGOTIATION_INFO) & 0xFF);
*p++ = 0x00;
*p++ = (ssl->verify_data_len + 1) & 0xFF;
*p++ = ssl->verify_data_len & 0xFF;
memcpy (p, ssl->own_verify_data, ssl->verify_data_len);
*olen = 5 + ssl->verify_data_len;
}
static void
write_signature_algorithms_ext (ntbtls_t ssl,
unsigned char *buf, size_t * olen)
{
unsigned char *p = buf;
size_t sig_alg_len = 0;
unsigned char *sig_alg_list = buf + 6;
*olen = 0;
if (ssl->max_minor_ver != TLS_MINOR_VERSION_4)
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_key_share_ext (ntbtls_t tls, unsigned char *buf, size_t * olen)
{
- /* FIMXE: For now, it's hard-coded for X25519 */
- unsigned char public[33];
+ unsigned char public[256];
size_t len;
unsigned char *p = buf;
- size_t key_share_len = 2 + 2 + 32;
+ size_t key_share_len;
+ ecdh_context_t ecdh;
- _ntbtls_ecdh_make_public (tls->handshake->ecdh_ctx, public, 33, &len);
+ ecdh = tls->handshake->ecdh_ctx;
+ _ntbtls_ecdh_curvename (ecdh, 0);
+ _ntbtls_ecdh_make_public (ecdh, public, sizeof public, &len);
+ key_share_len = 2 + 2 + len - 1;
debug_msg (3, "client_hello, adding key share extension");
*p++ = (unsigned char) ((TLS_EXT_KEY_SHARE >> 8) & 0xFF);
*p++ = (unsigned char) ((TLS_EXT_KEY_SHARE) & 0xFF);
*p++ = (unsigned char) (((key_share_len + 2) >> 8) & 0xFF);
*p++ = (unsigned char) (((key_share_len + 2)) & 0xFF);
*p++ = (unsigned char) ((key_share_len >> 8) & 0xFF);
*p++ = (unsigned char) (key_share_len & 0xFF);
*p++ = 0;
- *p++ = 29; /* X25519 */
+ *p++ = _ntbtls_ecdh_curve_id (ecdh);
*p++ = 0;
- *p++ = 32; /* length for X25519 */
+ *p++ = len - 1;
- memcpy (p, public+1, 32);
- p += 32;
+ memcpy (p, public+1, len - 1);
+ p += len - 1;
*olen = p - buf;
}
static void
write_supported_versions_ext (ntbtls_t tls, unsigned char *buf, size_t * olen)
{
unsigned char *p = buf;
size_t version_len;
(void)tls;
version_len = 2 * 2;
debug_msg (3, "client hello, adding supported_versions extension");
*p++ = (unsigned char) ((TLS_EXT_SUPPORTED_VERSIONS >> 8) & 0xFF);
*p++ = (unsigned char) ((TLS_EXT_SUPPORTED_VERSIONS) & 0xFF);
*p++ = (unsigned char) (((version_len + 1) >> 8) & 0xFF);
*p++ = (unsigned char) (((version_len + 1)) & 0xFF);
*p++ = (unsigned char) version_len;
*p++ = (unsigned char) TLS_MAJOR_VERSION_3;
*p++ = (unsigned char) TLS_MINOR_VERSION_3;
*p++ = (unsigned char) TLS_MAJOR_VERSION_3;
*p++ = (unsigned char) TLS_MINOR_VERSION_4;
*olen = (p - buf);
}
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;
#ifdef SUPPORT_X25519
elliptic_curve_list[elliptic_curve_len++] = 0;
elliptic_curve_list[elliptic_curve_len++] = 29;
#endif
#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)
+ (1 /* Always include the session ID */
+ ||(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", 1);
debug_msg (3, "client_hello, compress alg.: %d", TLS_COMPRESS_NULL);
*p++ = 1;
*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_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_signature_algorithms_ext (tls, p + 2 + ext_len, &olen);
ext_len += olen;
write_key_share_ext (tls, p + 2 + ext_len, &olen);
ext_len += olen;
write_supported_versions_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
+parse_key_share_ext (ntbtls_t tls, const unsigned char *buf, size_t len)
+{
+ gpg_error_t err = 0;
+ size_t key_share_len = len;
+
+ if (len < 2)
+ return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO);
+
+ while (key_share_len >= 2)
+ {
+ unsigned int group_id;
+ unsigned int key_ext_length;
+ ecdh_context_t ecdh;
+
+ group_id = buf16_to_uint (buf);
+ buf += 2;
+ key_share_len -= 2;
+
+ /* FIXME: Only support ECDH now, so, group_id is curve_id here. */
+
+ ecdh = tls->handshake->ecdh_ctx;
+
+ if (key_share_len == 0)
+ {
+ /* It's Hello Retry Request to change key share param. */
+ err = _ntbtls_ecdh_curvename (ecdh, group_id);
+ break;
+ }
+
+ key_ext_length = buf16_to_uint (buf);
+ buf += 2;
+ key_share_len -= 2;
+
+ if (!err)
+ {
+ err = _ntbtls_ecdh_peer_ec_point (ecdh, buf, key_ext_length);
+ if (!err)
+ {
+ debug_msg (3, "parsing key share extension, calc secret");
+ err = _ntbtls_ecdh_calc_secret (ecdh,
+ tls->handshake->premaster,
+ TLS_PREMASTER_SIZE,
+ &tls->handshake->pmslen);
+ if (err)
+ {
+ debug_ret (1, "ecdh_calc_secret", err);
+ return err;
+ }
+ }
+ }
+
+ key_share_len -= key_ext_length;
+ if (!err)
+ break;
+ }
+
+ return err;
+}
+
+
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;
+ const unsigned char helloretryrequest[32] = {
+ 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11,
+ 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
+ 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E,
+ 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C
+ };
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_CHANGE_CIPHER_SPEC)
+ /* Ignore. */
+ return 0;
+
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;
}
}
+ if (memcmp (helloretryrequest, tls->handshake->randbytes + 32, 32) == 0)
+ {
+ /* It's Hello Retry Request, we need to go back to client hello */
+ tls->state = TLS_CLIENT_HELLO;
+ }
+
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)
{
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;
+ case TLS_EXT_SUPPORTED_VERSIONS:
+ debug_msg (2, "found supported versions extension");
+ /* FIXME */
+ break;
+
+ case TLS_EXT_KEY_SHARE:
+ debug_msg (2, "found key share extension");
+ err = parse_key_share_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/1.3 */
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;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Wed, Dec 3, 3:42 AM (9 h, 32 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
78/02/d497e56ce6d3a7a52e0060189a5a
Attached To
rT Not Too Bad TLS
Event Timeline
Log In to Comment