summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2016-04-22 12:25:59 +0200
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2016-04-24 14:07:09 +0200
commitd417c2e908d53e80d55949ca0a16c363b12e875b (patch)
tree20d6080a47be4f8455e925d2da2e3f8c31400df3
parent40810a957eefb61c732bbe68a4a2ccdc7faecf04 (diff)
downloadgnutls-d417c2e908d53e80d55949ca0a16c363b12e875b.tar.gz
handshake: added support for ECDH with curve X25519
This follows draft-ietf-tls-rfc4492bis-07 and rfc7748
-rw-r--r--lib/algorithms.h3
-rw-r--r--lib/algorithms/ecc.c39
-rw-r--r--lib/algorithms/publickey.c4
-rw-r--r--lib/algorithms/secparams.c4
-rw-r--r--lib/auth/ecdhe.c217
-rw-r--r--lib/crypto-backend.h2
-rw-r--r--lib/ecc.c2
-rw-r--r--lib/ecc.h1
-rw-r--r--lib/gnutls_int.h4
-rw-r--r--lib/includes/gnutls/gnutls.h.in16
-rw-r--r--lib/libgnutls.map1
-rw-r--r--lib/mem.c15
-rw-r--r--lib/mem.h2
-rw-r--r--lib/nettle/pk.c88
-rw-r--r--lib/pk.c19
-rw-r--r--lib/state.c2
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<size;i++) {
+ res |= ptr[i];
+ }
+
+ return ((res==0)?1:0);
+}
diff --git a/lib/mem.h b/lib/mem.h
index a235b1cc4f..3ae5fd13e6 100644
--- a/lib/mem.h
+++ b/lib/mem.h
@@ -31,6 +31,8 @@ void *gnutls_realloc_fast(void *ptr, size_t size);
void *_gnutls_calloc(size_t nmemb, size_t size);
char *_gnutls_strdup(const char *);
+unsigned _gnutls_mem_is_zero(const uint8_t *ptr, unsigned size);
+
/* To avoid undefined behavior when s1 or s2 are null and n = 0 */
inline static
int safe_memcmp(const void *s1, const void *s2, size_t n)
diff --git a/lib/nettle/pk.c b/lib/nettle/pk.c
index dd1d8bb104..55ed70666c 100644
--- a/lib/nettle/pk.c
+++ b/lib/nettle/pk.c
@@ -46,10 +46,11 @@
#include <nettle/ecc.h>
#include <nettle/ecdsa.h>
#include <nettle/ecc-curve.h>
+#include <nettle/curve25519.h>
#include <gnettle.h>
#include <fips.h>
-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(&params->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