]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
handshake: added support for ECDH with curve X25519
authorNikos Mavrogiannopoulos <nmav@redhat.com>
Fri, 22 Apr 2016 10:25:59 +0000 (12:25 +0200)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Sun, 24 Apr 2016 12:07:09 +0000 (14:07 +0200)
This follows draft-ietf-tls-rfc4492bis-07 and rfc7748

16 files changed:
lib/algorithms.h
lib/algorithms/ecc.c
lib/algorithms/publickey.c
lib/algorithms/secparams.c
lib/auth/ecdhe.c
lib/crypto-backend.h
lib/ecc.c
lib/ecc.h
lib/gnutls_int.h
lib/includes/gnutls/gnutls.h.in
lib/libgnutls.map
lib/mem.c
lib/mem.h
lib/nettle/pk.c
lib/pk.c
lib/state.c

index 3135756954b80f42585818042a6d101ff1fd6787..5ab350b0986e5b65e0947b58aedeaf977b76cc66 100644 (file)
@@ -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 */
 };
index b42d95aa4dc01341299d95402902a2e9a013e74c..9d0c584b0ae75f379454abebc5811a93b2169faf 100644 (file)
@@ -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;
+}
+
index 183f436899bfd952192de48b6497e05064a2281a..c70187736facbaa33b8ffd733cd6811f5a5ebce3 100644 (file)
@@ -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}
 };
 
index 26338e70300cb22b9c1c57a5a9273820687b7cab..081a6bf4cff5f6eaf591911918b4cf2f4cf378c9 100644 (file)
@@ -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;
index 0cfcfd1467199d7c4227209e641a0673dc9ef5b5..35eaa9cb585e516f7732389fa0b555ea1fbf868e 100644 (file)
@@ -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;
 }
index 3aba11a842fa9f1a182f1158bf7efd7dbb8593c7..3d979d84ecb13dc82429db513bbfd2e5bd17354b 100644 (file)
@@ -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];
index 14ad242fac9bc5259043dd030dadec47bfc34a4d..e559cc39f3e7ba7a8886c8c5b8dfdd60ed00a832 100644 (file)
--- 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;
 }
+
index a0bd94c19dbf60ffec011892b9d5fcb410c4b27f..623a1a55bb896ed6037fe2cb84c5482c7f3d8c92 100644 (file)
--- 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
index 1cf43e12522e540678a4d6ee81219c5e4a4d33c2..6bdfe25980b43e7e79333be7cf95b9151de6f62f 100644 (file)
@@ -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;
index 98014aa11bedf972824eacdb407300565f1b56bd..aecf7ebc7c8cf5c33336d8de0b3bb1084f71f055 100644 (file)
@@ -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__;
index b1bef2805c4f0b43390d248f2ee31447063ccf97..84c9faf591a8dbe9bdb43e4c6c4c53f1695db82f 100644 (file)
@@ -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:
        *;
 };
index 2f4fc93f344bc48d45a020bf78d02d5ee84fc4c4..65f06588a371508bb00074d84154ddb5ba32a929 100644 (file)
--- 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);
+}
index a235b1cc4f276a9959aa6c65cb0b0f3f3a904322..3ae5fd13e67c5918f53182cdd0ef13772c016320 100644 (file)
--- 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)
index dd1d8bb104b413d8176c66a8ba10067839368557..55ed70666ca0a017bc6eb2f1f1e5e4f033e27e65 100644 (file)
 #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
index 6864c857e771d92545aed753098adf1d48f6d098..182cdcb15feab8f55b28957edc360e17acebccc9 100644 (file)
--- 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
index 6cb578319d4e56fb9e3531a54513c7644a4a8fde..0c6ebf92a1a7df02b4672514e6a2c105c752657f 100644 (file)
@@ -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