From d417c2e908d53e80d55949ca0a16c363b12e875b Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Fri, 22 Apr 2016 12:25:59 +0200 Subject: handshake: added support for ECDH with curve X25519 This follows draft-ietf-tls-rfc4492bis-07 and rfc7748 --- lib/algorithms.h | 3 + lib/algorithms/ecc.c | 39 +++++++- lib/algorithms/publickey.c | 4 +- lib/algorithms/secparams.c | 4 +- lib/auth/ecdhe.c | 217 ++++++++++++++++++++++++++++------------ lib/crypto-backend.h | 2 + lib/ecc.c | 2 + lib/ecc.h | 1 + lib/gnutls_int.h | 4 +- lib/includes/gnutls/gnutls.h.in | 16 ++- lib/libgnutls.map | 1 + lib/mem.c | 15 +++ lib/mem.h | 2 + lib/nettle/pk.c | 88 ++++++++++++++-- lib/pk.c | 19 ++++ lib/state.c | 2 + 16 files changed, 333 insertions(+), 86 deletions(-) diff --git a/lib/algorithms.h b/lib/algorithms.h index 3135756954..5ab350b098 100644 --- a/lib/algorithms.h +++ b/lib/algorithms.h @@ -34,6 +34,8 @@ /* would allow for 256 ciphersuites */ #define MAX_CIPHERSUITE_SIZE 512 +#define IS_EC(x) (((x)==GNUTLS_PK_ECDSA)||((x)==GNUTLS_PK_ECDHX)) + /* Functions for version handling. */ const version_entry_st *version_to_entry(gnutls_protocol_t c); const version_entry_st *_gnutls_version_lowest(gnutls_session_t session); @@ -319,6 +321,7 @@ struct gnutls_ecc_curve_entry_st { const char *name; const char *oid; gnutls_ecc_curve_t id; + gnutls_pk_algorithm_t pk; int tls_id; /* The RFC4492 namedCurve ID */ int size; /* the size in bytes */ }; diff --git a/lib/algorithms/ecc.c b/lib/algorithms/ecc.c index b42d95aa4d..9d0c584b0a 100644 --- a/lib/algorithms/ecc.c +++ b/lib/algorithms/ecc.c @@ -35,6 +35,7 @@ static const gnutls_ecc_curve_entry_st ecc_curves[] = { .oid = "1.2.840.10045.3.1.1", .id = GNUTLS_ECC_CURVE_SECP192R1, .tls_id = 19, + .pk = GNUTLS_PK_ECDSA, .size = 24, }, { @@ -42,6 +43,7 @@ static const gnutls_ecc_curve_entry_st ecc_curves[] = { .oid = "1.3.132.0.33", .id = GNUTLS_ECC_CURVE_SECP224R1, .tls_id = 21, + .pk = GNUTLS_PK_ECDSA, .size = 28, }, { @@ -49,6 +51,7 @@ static const gnutls_ecc_curve_entry_st ecc_curves[] = { .oid = "1.2.840.10045.3.1.7", .id = GNUTLS_ECC_CURVE_SECP256R1, .tls_id = 23, + .pk = GNUTLS_PK_ECDSA, .size = 32, }, { @@ -56,6 +59,7 @@ static const gnutls_ecc_curve_entry_st ecc_curves[] = { .oid = "1.3.132.0.34", .id = GNUTLS_ECC_CURVE_SECP384R1, .tls_id = 24, + .pk = GNUTLS_PK_ECDSA, .size = 48, }, { @@ -63,8 +67,16 @@ static const gnutls_ecc_curve_entry_st ecc_curves[] = { .oid = "1.3.132.0.35", .id = GNUTLS_ECC_CURVE_SECP521R1, .tls_id = 25, + .pk = GNUTLS_PK_ECDSA, .size = 66, }, + { + .name = "X25519", + .id = GNUTLS_ECC_CURVE_X25519, + .tls_id = 29, + .pk = GNUTLS_PK_ECDHX, + .size = 32, + }, {0, 0, 0} }; @@ -279,9 +291,7 @@ const gnutls_ecc_curve_entry_st * gnutls_ecc_curve_get_size: * @curve: is an ECC curve * - * Returns the size in bytes of the curve. - * - * Returns: a the size or (0). + * Returns: the size in bytes of the curve or 0 on failure. * * Since: 3.0 **/ @@ -298,3 +308,26 @@ int gnutls_ecc_curve_get_size(gnutls_ecc_curve_t curve) return ret; } + +/** + * gnutls_ecc_curve_get_pk: + * @curve: is an ECC curve + * + * Returns: the public key algorithm associated with the named curve or %GNUTLS_PK_UNKNOWN. + * + * Since: 3.5.0 + **/ +gnutls_pk_algorithm_t gnutls_ecc_curve_get_pk(gnutls_ecc_curve_t curve) +{ + int ret = GNUTLS_PK_UNKNOWN; + + GNUTLS_ECC_CURVE_LOOP( + if (p->id == curve) { + ret = p->pk; + break; + } + ); + + return ret; +} + diff --git a/lib/algorithms/publickey.c b/lib/algorithms/publickey.c index 183f436899..c70187736f 100644 --- a/lib/algorithms/publickey.c +++ b/lib/algorithms/publickey.c @@ -96,7 +96,9 @@ static const gnutls_pk_entry pk_algorithms[] = { {"DSA", PK_DSA_OID, GNUTLS_PK_DSA}, {"GOST R 34.10-2001", PK_GOST_R3410_2001_OID, GNUTLS_PK_UNKNOWN}, {"GOST R 34.10-94", PK_GOST_R3410_94_OID, GNUTLS_PK_UNKNOWN}, - {"EC", "1.2.840.10045.2.1", GNUTLS_PK_EC}, + {"EC/ECDSA", "1.2.840.10045.2.1", GNUTLS_PK_ECDSA}, + {"DH", NULL, GNUTLS_PK_DH}, + {"ECDHX", NULL, GNUTLS_PK_ECDHX}, {0, 0, 0} }; diff --git a/lib/algorithms/secparams.c b/lib/algorithms/secparams.c index 26338e7030..081a6bf4cf 100644 --- a/lib/algorithms/secparams.c +++ b/lib/algorithms/secparams.c @@ -88,7 +88,7 @@ gnutls_sec_param_to_pk_bits(gnutls_pk_algorithm_t algo, if (p->sec_param == param) { if (algo == GNUTLS_PK_DSA) ret = p->dsa_bits; - else if (algo == GNUTLS_PK_EC) + else if (IS_EC(algo)) ret = p->ecc_bits; else ret = p->pk_bits; break; @@ -184,7 +184,7 @@ gnutls_pk_bits_to_sec_param(gnutls_pk_algorithm_t algo, unsigned int bits) if (bits == 0) return GNUTLS_SEC_PARAM_UNKNOWN; - if (algo == GNUTLS_PK_EC) { + if (IS_EC(algo)) { GNUTLS_SEC_PARAM_LOOP( if (p->ecc_bits > bits) { break; diff --git a/lib/auth/ecdhe.c b/lib/auth/ecdhe.c index 0cfcfd1467..35eaa9cb58 100644 --- a/lib/auth/ecdhe.c +++ b/lib/auth/ecdhe.c @@ -87,40 +87,40 @@ const mod_auth_st ecdhe_rsa_auth_struct = { static int calc_ecdh_key(gnutls_session_t session, gnutls_datum_t * psk_key, - gnutls_ecc_curve_t curve) + const gnutls_ecc_curve_entry_st *ecurve) { gnutls_pk_params_st pub; int ret; + gnutls_datum_t tmp_dh_key; gnutls_pk_params_init(&pub); pub.params[ECC_X] = session->key.ecdh_x; pub.params[ECC_Y] = session->key.ecdh_y; - pub.flags = curve; + pub.raw_pub.data = session->key.ecdhx.data; + pub.raw_pub.size = session->key.ecdhx.size; + pub.flags = ecurve->id; + + ret = + _gnutls_pk_derive(ecurve->pk, &tmp_dh_key, + &session->key.ecdh_params, &pub); + if (ret < 0) { + ret = gnutls_assert_val(ret); + goto cleanup; + } if (psk_key == NULL) { - ret = - _gnutls_pk_derive(GNUTLS_PK_EC, &session->key.key, - &session->key.ecdh_params, &pub); + memcpy(&session->key.key, &tmp_dh_key, sizeof(gnutls_datum_t)); + tmp_dh_key.data = NULL; /* no longer needed */ } else { - gnutls_datum_t tmp_dh_key; - - ret = - _gnutls_pk_derive(GNUTLS_PK_EC, &tmp_dh_key, - &session->key.ecdh_params, &pub); - if (ret < 0) { - ret = gnutls_assert_val(ret); - goto cleanup; - } - ret = _gnutls_set_psk_session_key(session, psk_key, &tmp_dh_key); _gnutls_free_temp_key_datum(&tmp_dh_key); - } - if (ret < 0) { - ret = gnutls_assert_val(ret); - goto cleanup; + if (ret < 0) { + ret = gnutls_assert_val(ret); + goto cleanup; + } } ret = 0; @@ -129,6 +129,7 @@ static int calc_ecdh_key(gnutls_session_t session, /* no longer needed */ _gnutls_mpi_release(&session->key.ecdh_x); _gnutls_mpi_release(&session->key.ecdh_y); + _gnutls_free_datum(&session->key.ecdhx); gnutls_pk_params_release(&session->key.ecdh_params); return ret; } @@ -141,8 +142,9 @@ int _gnutls_proc_ecdh_common_client_kx(gnutls_session_t session, ssize_t data_size = _data_size; int ret, i = 0; int point_size; + const gnutls_ecc_curve_entry_st *ecurve = _gnutls_ecc_curve_get_params(curve); - if (curve == GNUTLS_ECC_CURVE_INVALID) + if (curve == GNUTLS_ECC_CURVE_INVALID || ecurve == NULL) return gnutls_assert_val(GNUTLS_E_ECC_NO_SUPPORTED_CURVES); DECR_LEN(data_size, 1); @@ -150,20 +152,43 @@ int _gnutls_proc_ecdh_common_client_kx(gnutls_session_t session, i += 1; DECR_LEN(data_size, point_size); - ret = - _gnutls_ecc_ansi_x963_import(&data[i], point_size, + + if (ecurve->pk == GNUTLS_PK_EC) { + ret = + _gnutls_ecc_ansi_x963_import(&data[i], point_size, &session->key.ecdh_x, &session->key.ecdh_y); - if (ret < 0) { - gnutls_assert(); - goto cleanup; + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } else if (ecurve->pk == GNUTLS_PK_ECDHX) { + if (ecurve->size != point_size) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + if (_gnutls_mem_is_zero(&data[i], point_size)) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + ret = _gnutls_set_datum(&session->key.ecdhx, + &data[i], point_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + /* RFC7748 requires to mask the MSB in the final byte */ + if (ecurve->id == GNUTLS_ECC_CURVE_X25519) { + session->key.ecdhx.data[point_size-1] &= 0x7f; + } + } else { + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); } if (data_size != 0) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); /* generate pre-shared key */ - ret = calc_ecdh_key(session, psk_key, curve); + ret = calc_ecdh_key(session, psk_key, ecurve); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -208,37 +233,57 @@ _gnutls_gen_ecdh_common_client_kx_int(gnutls_session_t session, int ret; gnutls_datum_t out; int curve = _gnutls_session_ecc_curve_get(session); + const gnutls_ecc_curve_entry_st *ecurve = _gnutls_ecc_curve_get_params(curve); + int pk; + + if (ecurve == NULL) + return gnutls_assert_val(GNUTLS_E_ECC_NO_SUPPORTED_CURVES); + + pk = ecurve->pk; /* generate temporal key */ ret = - _gnutls_pk_generate_keys(GNUTLS_PK_EC, curve, + _gnutls_pk_generate_keys(pk, curve, &session->key.ecdh_params); if (ret < 0) return gnutls_assert_val(ret); - ret = - _gnutls_ecc_ansi_x963_export(curve, - session->key.ecdh_params. - params[ECC_X] /* x */ , - session->key.ecdh_params. - params[ECC_Y] /* y */ , &out); - if (ret < 0) { - gnutls_assert(); - goto cleanup; - } + ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + if (pk == GNUTLS_PK_EC) { + ret = + _gnutls_ecc_ansi_x963_export(curve, + session->key.ecdh_params. + params[ECC_X] /* x */ , + session->key.ecdh_params. + params[ECC_Y] /* y */ , &out); - ret = - _gnutls_buffer_append_data_prefix(data, 8, out.data, out.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } - _gnutls_free_datum(&out); + ret = + _gnutls_buffer_append_data_prefix(data, 8, out.data, out.size); - if (ret < 0) { - gnutls_assert(); - goto cleanup; + _gnutls_free_datum(&out); + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } else if (pk == GNUTLS_PK_ECDHX) { + ret = + _gnutls_buffer_append_data_prefix(data, 8, + session->key.ecdh_params.raw_pub.data, + session->key.ecdh_params.raw_pub.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } } /* generate pre-shared key */ - ret = calc_ecdh_key(session, psk_key, curve); + ret = calc_ecdh_key(session, psk_key, ecurve); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -276,6 +321,7 @@ _gnutls_proc_ecdh_common_server_kx(gnutls_session_t session, int i, ret, point_size; gnutls_ecc_curve_t curve; ssize_t data_size = _data_size; + const gnutls_ecc_curve_entry_st *ecurve; /* just in case we are resuming a session */ gnutls_pk_params_release(&session->key.ecdh_params); @@ -302,6 +348,12 @@ _gnutls_proc_ecdh_common_server_kx(gnutls_session_t session, if (ret < 0) return gnutls_assert_val(ret); + ecurve = _gnutls_ecc_curve_get_params(curve); + if (ecurve == NULL) { + gnutls_assert(); + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + } + _gnutls_session_ecc_curve_set(session, curve); DECR_LEN(data_size, 1); @@ -309,12 +361,34 @@ _gnutls_proc_ecdh_common_server_kx(gnutls_session_t session, i++; DECR_LEN(data_size, point_size); - ret = - _gnutls_ecc_ansi_x963_import(&data[i], point_size, - &session->key.ecdh_x, - &session->key.ecdh_y); - if (ret < 0) - return gnutls_assert_val(ret); + + if (ecurve->pk == GNUTLS_PK_EC) { + ret = + _gnutls_ecc_ansi_x963_import(&data[i], point_size, + &session->key.ecdh_x, + &session->key.ecdh_y); + if (ret < 0) + return gnutls_assert_val(ret); + + } else if (ecurve->pk == GNUTLS_PK_ECDHX) { + if (ecurve->size != point_size) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + if (_gnutls_mem_is_zero(&data[i], point_size)) + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + + ret = _gnutls_set_datum(&session->key.ecdhx, + &data[i], point_size); + if (ret < 0) + return gnutls_assert_val(ret); + + /* RFC7748 requires to mask the MSB in the final byte */ + if (ecurve->id == GNUTLS_ECC_CURVE_X25519) { + session->key.ecdhx.data[point_size-1] &= 0x7f; + } + } else { + return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + } i += point_size; @@ -328,7 +402,7 @@ int _gnutls_ecdh_common_print_server_kx(gnutls_session_t session, gnutls_ecc_curve_t curve) { uint8_t p; - int ret; + int ret, pk; gnutls_datum_t out; if (curve == GNUTLS_ECC_CURVE_INVALID) @@ -353,29 +427,42 @@ int _gnutls_ecdh_common_print_server_kx(gnutls_session_t session, if (ret < 0) return gnutls_assert_val(ret); + pk = gnutls_ecc_curve_get_pk(curve); + /* generate temporal key */ ret = - _gnutls_pk_generate_keys(GNUTLS_PK_EC, curve, + _gnutls_pk_generate_keys(pk, curve, &session->key.ecdh_params); if (ret < 0) return gnutls_assert_val(ret); - ret = - _gnutls_ecc_ansi_x963_export(curve, - session->key.ecdh_params. - params[ECC_X] /* x */ , - session->key.ecdh_params. - params[ECC_Y] /* y */ , &out); - if (ret < 0) - return gnutls_assert_val(ret); + ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + if (pk == GNUTLS_PK_EC) { + ret = + _gnutls_ecc_ansi_x963_export(curve, + session->key.ecdh_params. + params[ECC_X] /* x */ , + session->key.ecdh_params. + params[ECC_Y] /* y */ , &out); + if (ret < 0) + return gnutls_assert_val(ret); - ret = - _gnutls_buffer_append_data_prefix(data, 8, out.data, out.size); + ret = + _gnutls_buffer_append_data_prefix(data, 8, out.data, out.size); - _gnutls_free_datum(&out); + _gnutls_free_datum(&out); - if (ret < 0) - return gnutls_assert_val(ret); + if (ret < 0) + return gnutls_assert_val(ret); + + } else if (pk == GNUTLS_PK_ECDHX) { + ret = + _gnutls_buffer_append_data_prefix(data, 8, + session->key.ecdh_params.raw_pub.data, + session->key.ecdh_params.raw_pub.size); + if (ret < 0) + return gnutls_assert_val(ret); + } return data->length; } diff --git a/lib/crypto-backend.h b/lib/crypto-backend.h index 3aba11a842..3d979d84ec 100644 --- a/lib/crypto-backend.h +++ b/lib/crypto-backend.h @@ -173,6 +173,8 @@ typedef struct { bigint_t params[GNUTLS_MAX_PK_PARAMS]; unsigned int params_nr; /* the number of parameters */ unsigned int flags; + gnutls_datum_t raw_pub; /* used by x25519 */ + gnutls_datum_t raw_priv; unsigned int seed_size; uint8_t seed[MAX_PVP_SEED_SIZE]; diff --git a/lib/ecc.c b/lib/ecc.c index 14ad242fac..e559cc39f3 100644 --- a/lib/ecc.c +++ b/lib/ecc.c @@ -91,6 +91,7 @@ _gnutls_ecc_ansi_x963_export(gnutls_ecc_curve_t curve, bigint_t x, } + int _gnutls_ecc_ansi_x963_import(const uint8_t * in, unsigned long inlen, bigint_t * x, @@ -123,3 +124,4 @@ _gnutls_ecc_ansi_x963_import(const uint8_t * in, return 0; } + diff --git a/lib/ecc.h b/lib/ecc.h index a0bd94c19d..623a1a55bb 100644 --- a/lib/ecc.h +++ b/lib/ecc.h @@ -27,4 +27,5 @@ int _gnutls_ecc_ansi_x963_import(const uint8_t * in, unsigned long inlen, bigint_t * x, bigint_t * y); int _gnutls_ecc_ansi_x963_export(gnutls_ecc_curve_t curve, bigint_t x, bigint_t y, gnutls_datum_t * out); + #endif diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 1cf43e1252..6bdfe25980 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -376,9 +376,11 @@ typedef struct auth_cred_st { struct gnutls_key_st { /* For ECDH KX */ - gnutls_pk_params_st ecdh_params; + gnutls_pk_params_st ecdh_params; /* private part */ + /* public part */ bigint_t ecdh_x; bigint_t ecdh_y; + gnutls_datum_t ecdhx; /* public key used in ECDHX (point) */ /* For DH KX */ gnutls_datum_t key; diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 98014aa11b..aecf7ebc7c 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -665,14 +665,17 @@ typedef enum gnutls_certificate_print_formats { GNUTLS_CRT_PRINT_FULL_NUMBERS = 4 } gnutls_certificate_print_formats_t; -#define GNUTLS_PK_ECC GNUTLS_PK_EC +#define GNUTLS_PK_ECC GNUTLS_PK_ECDSA +#define GNUTLS_PK_EC GNUTLS_PK_ECDSA + /** * gnutls_pk_algorithm_t: * @GNUTLS_PK_UNKNOWN: Unknown public-key algorithm. * @GNUTLS_PK_RSA: RSA public-key algorithm. * @GNUTLS_PK_DSA: DSA public-key algorithm. * @GNUTLS_PK_DH: Diffie-Hellman algorithm. Used to generate parameters. - * @GNUTLS_PK_EC: Elliptic curve algorithm. Used to generate parameters. + * @GNUTLS_PK_ECDSA: Elliptic curve algorithm. These parameters are compatible with the ECDSA and ECDH algorithm. + * @GNUTLS_PK_ECDHX: Elliptic curve algorithm, restricted to ECDH as per rfc7748. * * Enumeration of different public-key algorithms. */ @@ -681,9 +684,11 @@ typedef enum { GNUTLS_PK_RSA = 1, GNUTLS_PK_DSA = 2, GNUTLS_PK_DH = 3, - GNUTLS_PK_EC = 4 + GNUTLS_PK_ECDSA = 4, + GNUTLS_PK_ECDHX = 5 } gnutls_pk_algorithm_t; + const char *gnutls_pk_algorithm_get_name(gnutls_pk_algorithm_t algorithm); /** @@ -769,6 +774,7 @@ typedef enum { * @GNUTLS_ECC_CURVE_SECP256R1: the SECP256R1 curve * @GNUTLS_ECC_CURVE_SECP384R1: the SECP384R1 curve * @GNUTLS_ECC_CURVE_SECP521R1: the SECP521R1 curve + * @GNUTLS_ECC_CURVE_X25519: the X25519 curve (ECDH only) * * Enumeration of ECC curves. */ @@ -778,7 +784,8 @@ typedef enum { GNUTLS_ECC_CURVE_SECP256R1, GNUTLS_ECC_CURVE_SECP384R1, GNUTLS_ECC_CURVE_SECP521R1, - GNUTLS_ECC_CURVE_SECP192R1 + GNUTLS_ECC_CURVE_SECP192R1, + GNUTLS_ECC_CURVE_X25519 } gnutls_ecc_curve_t; /* macros to allow specifying a specific curve in gnutls_privkey_generate() @@ -990,6 +997,7 @@ gnutls_pk_algorithm_t gnutls_sign_algorithm_t gnutls_sign_get_id(const char *name) __GNUTLS_CONST__; gnutls_ecc_curve_t gnutls_ecc_curve_get_id(const char *name) __GNUTLS_CONST__; +gnutls_pk_algorithm_t gnutls_ecc_curve_get_pk(gnutls_ecc_curve_t curve) __GNUTLS_CONST__; gnutls_digest_algorithm_t gnutls_oid_to_digest(const char *oid) __GNUTLS_CONST__; diff --git a/lib/libgnutls.map b/lib/libgnutls.map index b1bef2805c..84c9faf591 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1083,6 +1083,7 @@ GNUTLS_3_4 gnutls_x509_crq_get_signature_oid; gnutls_x509_crq_get_pk_oid; gnutls_x509_crl_get_signature_oid; + gnutls_ecc_curve_get_pk; local: *; }; diff --git a/lib/mem.c b/lib/mem.c index 2f4fc93f34..65f06588a3 100644 --- a/lib/mem.c +++ b/lib/mem.c @@ -113,3 +113,18 @@ void gnutls_free(void *ptr) } #endif + +/* Returns 1 if the provided buffer is all zero. + * It leaks no information via timing. + */ +unsigned _gnutls_mem_is_zero(const uint8_t *ptr, unsigned size) +{ + unsigned i; + uint8_t res = 0; + + for (i=0;i #include #include +#include #include #include -static inline const struct ecc_curve *get_supported_curve(int curve); +static inline const struct ecc_curve *get_supported_nist_curve(int curve); static void rnd_func(void *_ctx, size_t length, uint8_t * data) { @@ -245,7 +246,7 @@ dh_cleanup: out->data = NULL; - curve = get_supported_curve(priv->flags); + curve = get_supported_nist_curve(priv->flags); if (curve == NULL) return gnutls_assert_val @@ -282,6 +283,34 @@ dh_cleanup: goto cleanup; break; } + case GNUTLS_PK_ECDHX: + { + unsigned size = gnutls_ecc_curve_get_size(priv->flags); + + /* The point is in pub, while the private part (scalar) in priv. */ + + if (size == 0) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + out->data = gnutls_malloc(size); + if (out->data == NULL) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto cleanup; + } + + out->size = size; + + curve25519_mul(out->data, priv->raw_priv.data, pub->raw_pub.data); + + if (_gnutls_mem_is_zero(out->data, out->size)) { + gnutls_free(out->data); + out->data = NULL; + gnutls_assert(); + ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + goto cleanup; + } + break; + } default: gnutls_assert(); ret = GNUTLS_E_INTERNAL_ERROR; @@ -446,7 +475,7 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, int curve_id = pk_params->flags; const struct ecc_curve *curve; - curve = get_supported_curve(curve_id); + curve = get_supported_nist_curve(curve_id); if (curve == NULL) return gnutls_assert_val @@ -600,7 +629,7 @@ _wrap_nettle_pk_verify(gnutls_pk_algorithm_t algo, int curve_id = pk_params->flags; const struct ecc_curve *curve; - curve = get_supported_curve(curve_id); + curve = get_supported_nist_curve(curve_id); if (curve == NULL) return gnutls_assert_val @@ -718,7 +747,7 @@ _wrap_nettle_pk_verify(gnutls_pk_algorithm_t algo, return ret; } -static inline const struct ecc_curve *get_supported_curve(int curve) +static inline const struct ecc_curve *get_supported_nist_curve(int curve) { switch (curve) { #ifdef ENABLE_NON_SUITEB_CURVES @@ -740,7 +769,12 @@ static inline const struct ecc_curve *get_supported_curve(int curve) static int _wrap_nettle_pk_curve_exists(gnutls_ecc_curve_t curve) { - return ((get_supported_curve(curve)!=NULL)?1:0); + switch (curve) { + case GNUTLS_ECC_CURVE_X25519: + return 1; + default: + return ((get_supported_nist_curve(curve)!=NULL)?1:0); + } } /* Generates algorithm's parameters. That is: @@ -1134,7 +1168,7 @@ int _gnutls_ecdh_compute_key(gnutls_ecc_curve_t curve, */ static int wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, - unsigned int level /*bits */ , + unsigned int level /*bits or curve */ , gnutls_pk_params_st * params) { int ret; @@ -1340,7 +1374,7 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, struct ecc_point pub; const struct ecc_curve *curve; - curve = get_supported_curve(level); + curve = get_supported_nist_curve(level); if (curve == NULL) return gnutls_assert_val @@ -1376,6 +1410,36 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, break; } + case GNUTLS_PK_ECDHX: + { + unsigned size = gnutls_ecc_curve_get_size(level); + + if (size == 0) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + params->flags = level; + + params->raw_priv.data = gnutls_malloc(size); + if (params->raw_priv.data == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + params->raw_pub.data = gnutls_malloc(size); + if (params->raw_pub.data == NULL) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto fail; + } + + ret = _gnutls_rnd(GNUTLS_RND_RANDOM, params->raw_priv.data, size); + if (ret < 0) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto fail; + } + params->raw_pub.size = size; + params->raw_priv.size = size; + + curve25519_mul_g(params->raw_pub.data, params->raw_priv.data); + break; + } default: gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; @@ -1390,6 +1454,10 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, _gnutls_mpi_release(¶ms->params[i]); } params->params_nr = 0; + gnutls_free(params->raw_priv.data); + gnutls_free(params->raw_pub.data); + params->raw_priv.data = NULL; + params->raw_pub.data = NULL; FAIL_IF_LIB_ERROR; return ret; @@ -1532,7 +1600,7 @@ wrap_nettle_pk_verify_priv_params(gnutls_pk_algorithm_t algo, gnutls_assert_val (GNUTLS_E_INVALID_REQUEST); - curve = get_supported_curve(params->flags); + curve = get_supported_nist_curve(params->flags); if (curve == NULL) return gnutls_assert_val @@ -1621,7 +1689,7 @@ wrap_nettle_pk_verify_pub_params(gnutls_pk_algorithm_t algo, gnutls_assert_val (GNUTLS_E_INVALID_REQUEST); - curve = get_supported_curve(params->flags); + curve = get_supported_nist_curve(params->flags); if (curve == NULL) return gnutls_assert_val diff --git a/lib/pk.c b/lib/pk.c index 6864c857e7..182cdcb15f 100644 --- a/lib/pk.c +++ b/lib/pk.c @@ -187,6 +187,16 @@ int _gnutls_pk_params_copy(gnutls_pk_params_st * dst, dst->params_nr++; } + if (_gnutls_set_datum(&dst->raw_priv, src->raw_priv.data, src->raw_priv.size) < 0) { + gnutls_assert(); + goto fail; + } + + if (_gnutls_set_datum(&dst->raw_pub, src->raw_pub.data, src->raw_pub.size) < 0) { + gnutls_assert(); + goto fail; + } + if (src->seed_size) { dst->seed_size = src->seed_size; memcpy(dst->seed, src->seed, src->seed_size); @@ -211,6 +221,11 @@ void gnutls_pk_params_release(gnutls_pk_params_st * p) for (i = 0; i < p->params_nr; i++) { _gnutls_mpi_release(&p->params[i]); } + gnutls_free(p->raw_priv.data); + gnutls_free(p->raw_pub.data); + p->raw_priv.data = NULL; + p->raw_pub.data = NULL; + p->params_nr = 0; } @@ -223,6 +238,10 @@ void gnutls_pk_params_clear(gnutls_pk_params_st * p) } gnutls_memset(p->seed, 0, p->seed_size); p->seed_size = 0; + if (p->raw_priv.data != NULL) { + gnutls_memset(p->raw_priv.data, 0, p->raw_priv.size); + p->raw_priv.size = 0; + } } /* Writes the digest information and the digest in a DER encoded diff --git a/lib/state.c b/lib/state.c index 6cb578319d..0c6ebf92a1 100644 --- a/lib/state.c +++ b/lib/state.c @@ -231,6 +231,7 @@ static void deinit_keys(gnutls_session_t session) gnutls_pk_params_release(&session->key.dh_params); zrelease_temp_mpi_key(&session->key.ecdh_x); zrelease_temp_mpi_key(&session->key.ecdh_y); + _gnutls_free_temp_key_datum(&session->key.ecdhx); zrelease_temp_mpi_key(&session->key.client_Y); @@ -247,6 +248,7 @@ static void deinit_keys(gnutls_session_t session) zrelease_temp_mpi_key(&session->key.b); _gnutls_free_temp_key_datum(&session->key.key); + _gnutls_free_temp_key_datum(&session->key.key); } /* this function deinitializes all the internal parameters stored -- cgit v1.2.1