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