diff options
author | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2017-05-26 15:20:38 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2017-07-17 17:08:01 +0200 |
commit | 650dad4e18f458f60a2cdb43be32356753ed6518 (patch) | |
tree | 308661634af506842f12c709f3ab669d6be1cd7c | |
parent | f7bc8c6eea0f0fc9d02cfd22fe9fea364061ee37 (diff) | |
download | gnutls-650dad4e18f458f60a2cdb43be32356753ed6518.tar.gz |
Added support for EdDSA (Ed25519) curve keys
This adds support for draft-ietf-curdle-pkix-04.
Resolves #25
Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
-rw-r--r-- | lib/abstract_int.h | 13 | ||||
-rw-r--r-- | lib/algorithms.h | 28 | ||||
-rw-r--r-- | lib/algorithms/ecc.c | 21 | ||||
-rw-r--r-- | lib/algorithms/publickey.c | 97 | ||||
-rw-r--r-- | lib/algorithms/sign.c | 3 | ||||
-rw-r--r-- | lib/auth/ecdhe.c | 5 | ||||
-rw-r--r-- | lib/crypto-backend.h | 2 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 12 | ||||
-rw-r--r-- | lib/nettle/pk.c | 157 | ||||
-rw-r--r-- | lib/pk.c | 30 | ||||
-rw-r--r-- | lib/pkcs11_privkey.c | 6 | ||||
-rw-r--r-- | lib/privkey.c | 86 | ||||
-rw-r--r-- | lib/privkey_raw.c | 3 | ||||
-rw-r--r-- | lib/pubkey.c | 72 | ||||
-rw-r--r-- | lib/x509/common.c | 35 | ||||
-rw-r--r-- | lib/x509/common.h | 3 | ||||
-rw-r--r-- | lib/x509/crq.c | 4 | ||||
-rw-r--r-- | lib/x509/key_decode.c | 16 | ||||
-rw-r--r-- | lib/x509/key_encode.c | 111 | ||||
-rw-r--r-- | lib/x509/mpi.c | 9 | ||||
-rw-r--r-- | lib/x509/output.c | 33 | ||||
-rw-r--r-- | lib/x509/pkcs7.c | 2 | ||||
-rw-r--r-- | lib/x509/privkey.c | 41 | ||||
-rw-r--r-- | lib/x509/privkey_pkcs8.c | 78 | ||||
-rw-r--r-- | lib/x509/sign.c | 6 | ||||
-rw-r--r-- | lib/x509/x509.c | 2 | ||||
-rw-r--r-- | lib/x509/x509_int.h | 8 | ||||
-rw-r--r-- | src/certtool-args.def | 6 | ||||
-rw-r--r-- | src/certtool-common.c | 31 | ||||
-rw-r--r-- | src/certtool-common.h | 13 | ||||
-rw-r--r-- | src/certtool.c | 26 |
31 files changed, 757 insertions, 202 deletions
diff --git a/lib/abstract_int.h b/lib/abstract_int.h index b3a0131eae..f82e4f922a 100644 --- a/lib/abstract_int.h +++ b/lib/abstract_int.h @@ -80,10 +80,15 @@ int _gnutls_privkey_update_sign_params(gnutls_privkey_t key, void _gnutls_privkey_cleanup(gnutls_privkey_t key); -int privkey_sign_data(gnutls_privkey_t signer, - const gnutls_datum_t * data, - gnutls_datum_t * signature, - gnutls_x509_spki_st *params); +int privkey_sign_and_hash_data(gnutls_privkey_t signer, + const gnutls_datum_t * data, + gnutls_datum_t * signature, + gnutls_x509_spki_st *params); +int +privkey_sign_raw_data(gnutls_privkey_t key, + const gnutls_datum_t * data, + gnutls_datum_t * signature, + gnutls_x509_spki_st * params); unsigned pubkey_to_bits(gnutls_pk_algorithm_t pk, gnutls_pk_params_st * params); int _gnutls_pubkey_compatible_with_sig(gnutls_session_t, diff --git a/lib/algorithms.h b/lib/algorithms.h index 623758fb6e..f449f8bf0b 100644 --- a/lib/algorithms.h +++ b/lib/algorithms.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2000-2012 Free Software Foundation, Inc. + * Copyright (C) 2017 Red Hat, Inc. * * Author: Nikos Mavrogiannopoulos * @@ -31,7 +32,7 @@ #define GNUTLS_FALLBACK_SCSV_MAJOR 0x56 #define GNUTLS_FALLBACK_SCSV_MINOR 0x00 -#define IS_EC(x) (((x)==GNUTLS_PK_ECDSA)||((x)==GNUTLS_PK_ECDHX)) +#define IS_EC(x) (((x)==GNUTLS_PK_ECDSA)||((x)==GNUTLS_PK_ECDHX)||((x)==GNUTLS_PK_EDDSA_ED25519)) #define TLS_SIGN_AID_UNKNOWN {{255, 255}} #define HAVE_UNKNOWN_SIGAID(aid) ((aid)->id[0] == 255 && (aid)->id[1] == 255) @@ -321,6 +322,8 @@ const sign_algorithm_st *_gnutls_sign_to_tls_aid(gnutls_sign_algorithm_t unsigned int _gnutls_pk_bits_to_subgroup_bits(unsigned int pk_bits); +bool _gnutls_pk_is_not_prehashed(gnutls_pk_algorithm_t algorithm); + /* ECC */ struct gnutls_ecc_curve_entry_st { const char *name; @@ -328,7 +331,8 @@ struct gnutls_ecc_curve_entry_st { gnutls_ecc_curve_t id; gnutls_pk_algorithm_t pk; int tls_id; /* The RFC4492 namedCurve ID */ - int size; /* the size in bytes */ + unsigned size; /* the size in bytes */ + unsigned sig_size; /* the size of curve signatures in bytes (EdDSA) */ }; typedef struct gnutls_ecc_curve_entry_st gnutls_ecc_curve_entry_st; @@ -337,9 +341,27 @@ const gnutls_ecc_curve_entry_st gnutls_ecc_curve_t gnutls_ecc_curve_get_id(const char *name); int _gnutls_tls_id_to_ecc_curve(int num); int _gnutls_ecc_curve_get_tls_id(gnutls_ecc_curve_t supported_ecc); -gnutls_ecc_curve_t _gnutls_ecc_bits_to_curve(int bits); +gnutls_ecc_curve_t _gnutls_ecc_bits_to_curve(gnutls_pk_algorithm_t pk, int bits); #define MAX_ECC_CURVE_SIZE 66 +gnutls_pk_algorithm_t _gnutls_oid_to_pk_and_curve(const char *oid, gnutls_ecc_curve_t *curve); + +inline static int _curve_is_eddsa(const gnutls_ecc_curve_entry_st * e) +{ + size_t ret = 0; + if (unlikely(e == NULL)) + return ret; + if (e->pk == GNUTLS_PK_EDDSA_ED25519) + return 1; + return 0; +} + +inline static int curve_is_eddsa(gnutls_ecc_curve_t id) +{ + const gnutls_ecc_curve_entry_st *e = _gnutls_ecc_curve_get_params(id); + return _curve_is_eddsa(e); +} + static inline int _gnutls_kx_is_ecc(gnutls_kx_algorithm_t kx) { if (kx == GNUTLS_KX_ECDHE_RSA || kx == GNUTLS_KX_ECDHE_ECDSA || diff --git a/lib/algorithms/ecc.c b/lib/algorithms/ecc.c index 5274a815cb..c59099747e 100644 --- a/lib/algorithms/ecc.c +++ b/lib/algorithms/ecc.c @@ -77,6 +77,14 @@ static const gnutls_ecc_curve_entry_st ecc_curves[] = { .pk = GNUTLS_PK_ECDHX, .size = 32, }, + { + .name = "Ed25519", + .oid = SIG_EDDSA_SHA512_OID, + .id = GNUTLS_ECC_CURVE_ED25519, + .pk = GNUTLS_PK_EDDSA_ED25519, + .size = 32, + .sig_size = 64 + }, {0, 0, 0} }; @@ -159,7 +167,7 @@ gnutls_ecc_curve_t gnutls_oid_to_ecc_curve(const char *oid) gnutls_ecc_curve_t ret = GNUTLS_ECC_CURVE_INVALID; GNUTLS_ECC_CURVE_LOOP( - if (p->oid && strcasecmp(p->oid, oid) == 0 && _gnutls_pk_curve_exists(p->id)) { + if (p->oid != NULL && strcasecmp(p->oid, oid) == 0 && _gnutls_pk_curve_exists(p->id)) { ret = p->id; break; } @@ -200,12 +208,17 @@ gnutls_ecc_curve_t gnutls_ecc_curve_get_id(const char *name) * Returns: return a #gnutls_ecc_curve_t value corresponding to * the specified bit length, or %GNUTLS_ECC_CURVE_INVALID on error. -*/ -gnutls_ecc_curve_t _gnutls_ecc_bits_to_curve(int bits) +gnutls_ecc_curve_t _gnutls_ecc_bits_to_curve(gnutls_pk_algorithm_t pk, int bits) { - gnutls_ecc_curve_t ret = GNUTLS_ECC_CURVE_SECP256R1; + gnutls_ecc_curve_t ret; + + if (pk == GNUTLS_PK_ECDSA) + ret = GNUTLS_ECC_CURVE_SECP256R1; + else + ret = GNUTLS_ECC_CURVE_ED25519; GNUTLS_ECC_CURVE_LOOP( - if (8 * p->size >= bits && _gnutls_pk_curve_exists(p->id)) { + if (pk == p->pk && 8 * p->size >= (unsigned)bits && _gnutls_pk_curve_exists(p->id)) { ret = p->id; break; } diff --git a/lib/algorithms/publickey.c b/lib/algorithms/publickey.c index cbdd0d64fc..a75feddf0d 100644 --- a/lib/algorithms/publickey.c +++ b/lib/algorithms/publickey.c @@ -103,26 +103,43 @@ struct gnutls_pk_entry { const char *name; const char *oid; gnutls_pk_algorithm_t id; + gnutls_ecc_curve_t curve; /* to map PK to specific OID, we need to know the curve for EdDSA */ + bool no_prehashed; /* non-zero if the algorithm cannot sign pre-hashed data */ }; typedef struct gnutls_pk_entry gnutls_pk_entry; static const gnutls_pk_entry pk_algorithms[] = { /* having duplicate entries is ok, as long as the one * we want to return OID from is first */ - {"UNKNOWN", NULL, GNUTLS_PK_UNKNOWN}, - {"RSA", PK_PKIX1_RSA_OID, GNUTLS_PK_RSA}, - {"RSA-PSS", PK_PKIX1_RSA_PSS_OID, GNUTLS_PK_RSA_PSS}, - {"RSA (X.509)", PK_X509_RSA_OID, GNUTLS_PK_RSA}, /* some certificates use this OID for RSA */ - {"RSA-MD5", SIG_RSA_MD5_OID, GNUTLS_PK_RSA}, /* some other broken certificates set RSA with MD5 as an indicator of RSA */ - {"RSA-SHA1", SIG_RSA_SHA1_OID, GNUTLS_PK_RSA}, /* some other broken certificates set RSA with SHA1 as an indicator of RSA */ - {"RSA-SHA1", ISO_SIG_RSA_SHA1_OID, GNUTLS_PK_RSA}, /* some other broken certificates set RSA with SHA1 as an indicator of RSA */ - {"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/ECDSA", "1.2.840.10045.2.1", GNUTLS_PK_ECDSA}, - {"DH", NULL, GNUTLS_PK_DH}, - {"ECDHX", NULL, GNUTLS_PK_ECDHX}, - {0, 0, 0} + { .name = "RSA", .oid = PK_PKIX1_RSA_OID, .id = GNUTLS_PK_RSA, + .curve = GNUTLS_ECC_CURVE_INVALID }, + { .name = "RSA-PSS", .oid = PK_PKIX1_RSA_PSS_OID, .id = GNUTLS_PK_RSA_PSS, + .curve = GNUTLS_ECC_CURVE_INVALID }, + { .name = "RSA (X.509)", .oid = PK_X509_RSA_OID, .id = GNUTLS_PK_RSA, + .curve = GNUTLS_ECC_CURVE_INVALID }, /* some certificates use this OID for RSA */ + { .name = "RSA-MD5", .oid = SIG_RSA_MD5_OID, .id = GNUTLS_PK_RSA, + .curve = GNUTLS_ECC_CURVE_INVALID }, /* some other broken certificates set RSA with MD5 as an indicator of RSA */ + { .name = "RSA-SHA1", .oid = SIG_RSA_SHA1_OID, .id = GNUTLS_PK_RSA, + .curve = GNUTLS_ECC_CURVE_INVALID }, /* some other broken certificates set RSA with SHA1 as an indicator of RSA */ + { .name = "RSA-SHA1", .oid = ISO_SIG_RSA_SHA1_OID, .id = GNUTLS_PK_RSA, + .curve = GNUTLS_ECC_CURVE_INVALID }, /* some other broken certificates set RSA with SHA1 as an indicator of RSA */ + { .name = "DSA", .oid = PK_DSA_OID, .id = GNUTLS_PK_DSA, + .curve = GNUTLS_ECC_CURVE_INVALID }, + { .name = "GOST R 34.10-2001", .oid = PK_GOST_R3410_2001_OID, .id = GNUTLS_PK_UNKNOWN, + .curve = GNUTLS_ECC_CURVE_INVALID }, + { .name = "GOST R 34.10-94", .oid = PK_GOST_R3410_94_OID, .id = GNUTLS_PK_UNKNOWN, + .curve = GNUTLS_ECC_CURVE_INVALID }, + { .name = "EC/ECDSA", .oid = "1.2.840.10045.2.1", .id = GNUTLS_PK_ECDSA, + .curve = GNUTLS_ECC_CURVE_INVALID }, + { .name = "EdDSA (Ed25519)", .oid = SIG_EDDSA_SHA512_OID, .id = GNUTLS_PK_EDDSA_ED25519, + .curve = GNUTLS_ECC_CURVE_ED25519, .no_prehashed = 1 }, + { .name = "DH", .oid = NULL, .id = GNUTLS_PK_DH, + .curve = GNUTLS_ECC_CURVE_INVALID }, + { .name = "ECDH (X25519)", .oid = "1.3.101.110", .id = GNUTLS_PK_ECDHX, + .curve = GNUTLS_ECC_CURVE_X25519 }, + { .name = "UNKNOWN", .oid = NULL, .id = GNUTLS_PK_UNKNOWN, + .curve = GNUTLS_ECC_CURVE_INVALID }, + {0, 0, 0, 0} }; #define GNUTLS_PK_LOOP(b) \ @@ -234,6 +251,27 @@ const char *gnutls_pk_get_name(gnutls_pk_algorithm_t algorithm) return ret; } +/*- + * _gnutls_pk_is_not_prehashed: + * @algorithm: is a public key algorithm + * + * Returns non-zero when the public key algorithm does not support pre-hashed + * data. + * + * Since: 3.6.0 + **/ +bool _gnutls_pk_is_not_prehashed(gnutls_pk_algorithm_t algorithm) +{ + const gnutls_pk_entry *p; + + for (p = pk_algorithms; p->name != NULL; p++) + if (algorithm == p->id) { + return p->no_prehashed; + } + + return 0; +} + /** * gnutls_oid_to_pk: * @oid: is an object identifier @@ -284,6 +322,37 @@ const char *gnutls_pk_get_oid(gnutls_pk_algorithm_t algorithm) return ret; } +/*- + * _gnutls_oid_to_pk_and_curve: + * @oid: is an object identifier + * + * Convert an OID to a #gnutls_pk_algorithm_t and curve values. If no curve + * is applicable, curve will be set GNUTLS_ECC_CURVE_INVALID. + * + * Returns: a #gnutls_pk_algorithm_t id of the specified digest + * algorithm, or %GNUTLS_PK_UNKNOWN on failure. + * + * Since: 3.6.0 + -*/ +gnutls_pk_algorithm_t _gnutls_oid_to_pk_and_curve(const char *oid, gnutls_ecc_curve_t *curve) +{ + gnutls_pk_algorithm_t ret = GNUTLS_PK_UNKNOWN; + const gnutls_pk_entry *p; + + for (p = pk_algorithms; p->name != NULL; p++) + if (p->oid && strcmp(p->oid, oid) == 0) { + ret = p->id; + if (curve) + *curve = p->curve; + break; + } + + if (ret == GNUTLS_PK_UNKNOWN && curve) + *curve = GNUTLS_PK_UNKNOWN; + + return ret; +} + /* Returns the encipher type for the given key exchange algorithm. * That one of CIPHER_ENCRYPT, CIPHER_SIGN, CIPHER_IGN. * diff --git a/lib/algorithms/sign.c b/lib/algorithms/sign.c index 5f953f2b40..be4b19ec18 100644 --- a/lib/algorithms/sign.c +++ b/lib/algorithms/sign.c @@ -127,6 +127,9 @@ static const gnutls_sign_entry_st sign_algorithms[] = { {"RSA-PSS-SHA512", PK_PKIX1_RSA_PSS_OID, GNUTLS_SIGN_RSA_PSS_SHA512, GNUTLS_PK_RSA, GNUTLS_DIG_SHA512, {{8, 6}}}, + {"EdDSA-Ed25519", SIG_EDDSA_SHA512_OID, GNUTLS_SIGN_EDDSA_ED25519, + GNUTLS_PK_EDDSA_ED25519, GNUTLS_DIG_SHA512, {{8, 7}}}, + {0, 0, 0, 0, 0, TLS_SIGN_AID_UNKNOWN} }; diff --git a/lib/auth/ecdhe.c b/lib/auth/ecdhe.c index c79e43347e..d9b8b42eae 100644 --- a/lib/auth/ecdhe.c +++ b/lib/auth/ecdhe.c @@ -141,7 +141,7 @@ int _gnutls_proc_ecdh_common_client_kx(gnutls_session_t session, { ssize_t data_size = _data_size; int ret, i = 0; - int point_size; + unsigned point_size; const gnutls_ecc_curve_entry_st *ecurve = _gnutls_ecc_curve_get_params(curve); if (curve == GNUTLS_ECC_CURVE_INVALID || ecurve == NULL) @@ -314,7 +314,8 @@ int _gnutls_proc_ecdh_common_server_kx(gnutls_session_t session, uint8_t * data, size_t _data_size) { - int i, ret, point_size; + int i, ret; + unsigned point_size; gnutls_ecc_curve_t curve; ssize_t data_size = _data_size; const gnutls_ecc_curve_entry_st *ecurve; diff --git a/lib/crypto-backend.h b/lib/crypto-backend.h index 92e3092d8d..07b23cb0f1 100644 --- a/lib/crypto-backend.h +++ b/lib/crypto-backend.h @@ -182,7 +182,7 @@ typedef struct gnutls_x509_spki_st { typedef struct { bigint_t params[GNUTLS_MAX_PK_PARAMS]; unsigned int params_nr; /* the number of parameters */ - unsigned int flags; + unsigned int flags; /* curve */ gnutls_datum_t raw_pub; /* used by x25519 */ gnutls_datum_t raw_priv; diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 8056a9fcba..fa805f2f5e 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -706,6 +706,7 @@ typedef enum gnutls_certificate_print_formats { * @GNUTLS_PK_DH: Diffie-Hellman 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. + * @GNUTLS_PK_EDDSA_ED25519: Edwards curve Digital signature algorithm. * * Enumeration of different public-key algorithms. */ @@ -717,7 +718,8 @@ typedef enum { GNUTLS_PK_ECDSA = 4, GNUTLS_PK_ECDHX = 5, GNUTLS_PK_RSA_PSS = 6, - GNUTLS_PK_MAX = GNUTLS_PK_RSA_PSS + GNUTLS_PK_EDDSA_ED25519 = 7, + GNUTLS_PK_MAX = GNUTLS_PK_EDDSA_ED25519 } gnutls_pk_algorithm_t; @@ -761,6 +763,7 @@ const char *gnutls_pk_algorithm_get_name(gnutls_pk_algorithm_t algorithm); * @GNUTLS_SIGN_RSA_PSS_SHA256: Digital signature algorithm RSA with SHA-256, with PSS padding. * @GNUTLS_SIGN_RSA_PSS_SHA384: Digital signature algorithm RSA with SHA-384, with PSS padding. * @GNUTLS_SIGN_RSA_PSS_SHA512: Digital signature algorithm RSA with SHA-512, with PSS padding. + * @GNUTLS_SIGN_EDDSA_ED25519: Digital signature algorithm EdDSA with Ed25519 curve. * * Enumeration of different digital signature algorithms. */ @@ -803,7 +806,8 @@ typedef enum { GNUTLS_SIGN_RSA_PSS_SHA256 = 32, GNUTLS_SIGN_RSA_PSS_SHA384 = 33, GNUTLS_SIGN_RSA_PSS_SHA512 = 34, - GNUTLS_SIGN_MAX = GNUTLS_SIGN_RSA_PSS_SHA512 + GNUTLS_SIGN_EDDSA_ED25519 = 35, + GNUTLS_SIGN_MAX = GNUTLS_SIGN_EDDSA_ED25519 } gnutls_sign_algorithm_t; /** @@ -815,6 +819,7 @@ typedef enum { * @GNUTLS_ECC_CURVE_SECP384R1: the SECP384R1 curve * @GNUTLS_ECC_CURVE_SECP521R1: the SECP521R1 curve * @GNUTLS_ECC_CURVE_X25519: the X25519 curve (ECDH only) + * @GNUTLS_ECC_CURVE_ED25519: the Ed25519 curve * * Enumeration of ECC curves. */ @@ -826,7 +831,8 @@ typedef enum { GNUTLS_ECC_CURVE_SECP521R1, GNUTLS_ECC_CURVE_SECP192R1, GNUTLS_ECC_CURVE_X25519, - GNUTLS_ECC_CURVE_MAX = GNUTLS_ECC_CURVE_X25519 + GNUTLS_ECC_CURVE_ED25519, + GNUTLS_ECC_CURVE_MAX = GNUTLS_ECC_CURVE_ED25519 } gnutls_ecc_curve_t; /* macros to allow specifying a specific curve in gnutls_privkey_generate() diff --git a/lib/nettle/pk.c b/lib/nettle/pk.c index b635c645ff..890a92b3d3 100644 --- a/lib/nettle/pk.c +++ b/lib/nettle/pk.c @@ -48,6 +48,7 @@ #include <nettle/ecdsa.h> #include <nettle/ecc-curve.h> #include <nettle/curve25519.h> +#include <nettle/eddsa.h> #include <gnettle.h> #include <fips.h> #ifndef HAVE_NETTLE_RSA_PSS @@ -566,8 +567,42 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, unsigned int hash_len; const mac_entry_st *me; + if (IS_EC(algo)) { + /* check if the curve relates to the algorithm used */ + if (gnutls_ecc_curve_get_pk(pk_params->flags) != algo) + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + } + switch (algo) { - case GNUTLS_PK_EC: /* we do ECDSA */ + case GNUTLS_PK_EDDSA_ED25519: /* we do EdDSA */ + { + const gnutls_ecc_curve_entry_st *e; + + if (pk_params->flags != GNUTLS_ECC_CURVE_ED25519) + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + + e = _gnutls_ecc_curve_get_params(pk_params->flags); + if (e == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + signature->data = gnutls_malloc(e->sig_size); + if (signature->data == NULL) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto cleanup; + } + + signature->size = e->sig_size; + + if (pk_params->raw_pub.size != e->size || pk_params->raw_priv.size != e->size) + return gnutls_assert_val(GNUTLS_E_PK_SIGN_FAILED); + + ed25519_sha512_sign(pk_params->raw_pub.data, + pk_params->raw_priv.data, + vdata->size, vdata->data, signature->data); + + break; + } + case GNUTLS_PK_ECDSA: /* we do ECDSA */ { struct ecc_scalar priv; struct dsa_signature sig; @@ -809,8 +844,40 @@ _wrap_nettle_pk_verify(gnutls_pk_algorithm_t algo, unsigned int hash_len; bigint_t tmp[2] = { NULL, NULL }; + if (IS_EC(algo)) { + /* check if the curve relates to the algorithm used */ + if (gnutls_ecc_curve_get_pk(pk_params->flags) != algo) + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + } + switch (algo) { - case GNUTLS_PK_EC: /* ECDSA */ + case GNUTLS_PK_EDDSA_ED25519: /* we do EdDSA */ + { + const gnutls_ecc_curve_entry_st *e; + + if (pk_params->flags != GNUTLS_ECC_CURVE_ED25519) + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + + e = _gnutls_ecc_curve_get_params(pk_params->flags); + if (e == NULL) + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + + if (signature->size != e->sig_size) + return gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED); + + if (pk_params->raw_pub.size != e->size) + return gnutls_assert_val(GNUTLS_E_PK_SIGN_FAILED); + + ret = ed25519_sha512_verify(pk_params->raw_pub.data, vdata->size, vdata->data, signature->data); + if (ret == 0) { + gnutls_assert(); + ret = GNUTLS_E_PK_SIG_VERIFY_FAILED; + } else { + ret = 0; + } + break; + } + case GNUTLS_PK_ECDSA: /* ECDSA */ { struct ecc_point pub; struct dsa_signature sig; @@ -998,6 +1065,7 @@ static inline const struct ecc_curve *get_supported_nist_curve(int curve) static int _wrap_nettle_pk_curve_exists(gnutls_ecc_curve_t curve) { switch (curve) { + case GNUTLS_ECC_CURVE_ED25519: case GNUTLS_ECC_CURVE_X25519: return 1; default: @@ -1008,7 +1076,7 @@ static int _wrap_nettle_pk_curve_exists(gnutls_ecc_curve_t curve) /* Generates algorithm's parameters. That is: * For DSA: p, q, and g are generated. * For RSA: nothing - * For ECDSA: just checks the curve is ok + * For ECDSA/EDDSA: nothing */ static int wrap_nettle_pk_generate_params(gnutls_pk_algorithm_t algo, @@ -1121,7 +1189,8 @@ wrap_nettle_pk_generate_params(gnutls_pk_algorithm_t algo, } case GNUTLS_PK_RSA_PSS: case GNUTLS_PK_RSA: - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: + case GNUTLS_PK_EDDSA_ED25519: break; default: gnutls_assert(); @@ -1265,13 +1334,13 @@ int _gnutls_ecdh_generate_key(gnutls_ecc_curve_t curve, gnutls_pk_params_init(¶ms); params.flags = curve; - params.algo = GNUTLS_PK_EC; + params.algo = GNUTLS_PK_ECDSA; x->data = NULL; y->data = NULL; k->data = NULL; - ret = _gnutls_pk_generate_keys(GNUTLS_PK_EC, curve, ¶ms, 0); + ret = _gnutls_pk_generate_keys(GNUTLS_PK_ECDSA, curve, ¶ms, 0); if (ret < 0) { return gnutls_assert_val(ret); } @@ -1320,7 +1389,7 @@ int _gnutls_ecdh_compute_key(gnutls_ecc_curve_t curve, gnutls_pk_params_init(&pub); gnutls_pk_params_init(&priv); - pub.algo = GNUTLS_PK_EC; + pub.algo = GNUTLS_PK_ECDSA; pub.flags = curve; if (_gnutls_mpi_init_scan_nz @@ -1367,12 +1436,12 @@ int _gnutls_ecdh_compute_key(gnutls_ecc_curve_t curve, priv.params_nr = 3; - priv.algo = GNUTLS_PK_EC; + priv.algo = GNUTLS_PK_ECDSA; priv.flags = curve; Z->data = NULL; - ret = _gnutls_pk_derive(GNUTLS_PK_EC, Z, &priv, &pub); + ret = _gnutls_pk_derive(GNUTLS_PK_ECDSA, Z, &priv, &pub); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -1462,6 +1531,7 @@ char* gen_data = NULL; * signing and encryption. */ case GNUTLS_PK_EC: /* we only do keys for ECDSA */ + case GNUTLS_PK_EDDSA_ED25519: case GNUTLS_PK_DSA: case GNUTLS_PK_RSA_PSS: ret = _gnutls_pk_sign(algo, &sig, &ddata, params, ¶ms->sign); @@ -1512,6 +1582,12 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, unsigned rnd_level; nettle_random_func *rnd_func; + if (IS_EC(algo)) { + /* check if the curve relates to the algorithm used */ + if (gnutls_ecc_curve_get_pk(level) != algo) + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + } + if (ephemeral) { rnd_level = GNUTLS_RND_RANDOM; rnd_func = rnd_tmpkey_func; @@ -1718,7 +1794,44 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, break; } - case GNUTLS_PK_EC: + case GNUTLS_PK_EDDSA_ED25519: + { + unsigned size = gnutls_ecc_curve_get_size(level); + + if (params->flags & GNUTLS_PK_FLAG_PROVABLE) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + if (level != GNUTLS_ECC_CURVE_ED25519) + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + + 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(rnd_level, 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; + + ed25519_sha512_public_key(params->raw_pub.data, params->raw_priv.data); + + break; + } + case GNUTLS_PK_ECDSA: if (params->flags & GNUTLS_PK_FLAG_PROVABLE) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); @@ -1810,6 +1923,8 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, } #endif + params->algo = algo; + FAIL_IF_LIB_ERROR; return 0; @@ -1955,7 +2070,7 @@ wrap_nettle_pk_verify_priv_params(gnutls_pk_algorithm_t algo, } break; - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: { struct ecc_point r, pub; struct ecc_scalar priv; @@ -2028,6 +2143,9 @@ wrap_nettle_pk_verify_priv_params(gnutls_pk_algorithm_t algo, mpz_clear(y2); } break; + case GNUTLS_PK_EDDSA_ED25519: + ret = 0; + break; default: ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); } @@ -2045,8 +2163,9 @@ wrap_nettle_pk_verify_pub_params(gnutls_pk_algorithm_t algo, case GNUTLS_PK_RSA: case GNUTLS_PK_RSA_PSS: case GNUTLS_PK_DSA: + case GNUTLS_PK_EDDSA_ED25519: return 0; - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: { /* just verify that x and y lie on the curve */ struct ecc_point r, pub; @@ -2186,8 +2305,22 @@ wrap_nettle_pk_fixup(gnutls_pk_algorithm_t algo, if (ret == 0) { return gnutls_assert_val(GNUTLS_E_PK_INVALID_PRIVKEY); } + } else if (algo == GNUTLS_PK_EDDSA_ED25519) { + if (params->flags != GNUTLS_ECC_CURVE_ED25519) + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + + if (params->raw_pub.data == NULL) { + params->raw_pub.data = gnutls_malloc(params->raw_priv.size); + } + + if (params->raw_pub.data == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + ed25519_sha512_public_key(params->raw_pub.data, params->raw_priv.data); + params->raw_pub.size = params->raw_priv.size; } + return 0; } @@ -299,7 +299,7 @@ int _gnutls_pk_params_copy(gnutls_pk_params_st * dst, unsigned int i, j; dst->params_nr = 0; - if (src == NULL || src->params_nr == 0) { + if (src == NULL || (src->params_nr == 0 && src->raw_pub.size == 0)) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } @@ -832,6 +832,31 @@ int _gnutls_params_get_ecc_raw(const gnutls_pk_params_st* params, if (curve) *curve = params->flags; + if (curve_is_eddsa(params->flags)) { + if (x) { + ret = _gnutls_set_datum(x, params->raw_pub.data, params->raw_pub.size); + if (ret < 0) { + return gnutls_assert_val(ret); + } + } + + if (y) { + y->data = NULL; + y->size = 0; + } + + if (k) { + ret = _gnutls_set_datum(k, params->raw_priv.data, params->raw_priv.size); + if (ret < 0) { + _gnutls_free_datum(x); + return gnutls_assert_val(ret); + } + } + + return 0; + } + + /* X */ if (x) { ret = dprint(params->params[ECC_X], x); @@ -926,7 +951,8 @@ pk_prepare_hash(gnutls_pk_algorithm_t pk, break; case GNUTLS_PK_RSA_PSS: case GNUTLS_PK_DSA: - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: + case GNUTLS_PK_EDDSA_ED25519: break; default: gnutls_assert(); diff --git a/lib/pkcs11_privkey.c b/lib/pkcs11_privkey.c index 40def6cff5..86bdff4ef8 100644 --- a/lib/pkcs11_privkey.c +++ b/lib/pkcs11_privkey.c @@ -354,7 +354,7 @@ _gnutls_pkcs11_privkey_sign_hash(gnutls_pkcs11_privkey_t key, } - if (key->pk_algorithm == GNUTLS_PK_EC + if (key->pk_algorithm == GNUTLS_PK_ECDSA || key->pk_algorithm == GNUTLS_PK_DSA) { unsigned int hlen = siglen / 2; gnutls_datum_t r, s; @@ -961,7 +961,7 @@ gnutls_pkcs11_privkey_generate3(const char *url, gnutls_pk_algorithm_t pk, } break; - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: p[p_val].type = CKA_SIGN; if (key_usage & GNUTLS_KEY_DIGITAL_SIGNATURE) { p[p_val].value = (void *) &tval; @@ -980,7 +980,7 @@ gnutls_pkcs11_privkey_generate3(const char *url, gnutls_pk_algorithm_t pk, if (GNUTLS_BITS_ARE_CURVE(bits)) { bits = GNUTLS_BITS_TO_CURVE(bits); } else { - bits = _gnutls_ecc_bits_to_curve(bits); + bits = _gnutls_ecc_bits_to_curve(pk, bits); } ret = _gnutls_x509_write_ecc_params(bits, &der); diff --git a/lib/privkey.c b/lib/privkey.c index 7d4cbb546b..6dc33e9e22 100644 --- a/lib/privkey.c +++ b/lib/privkey.c @@ -38,17 +38,11 @@ #include <abstract_int.h> static int -privkey_sign_hash(gnutls_privkey_t signer, +privkey_sign_prehashed(gnutls_privkey_t signer, const gnutls_datum_t * hash_data, gnutls_datum_t * signature, gnutls_x509_spki_st * params, unsigned flags); -static int -_gnutls_privkey_sign_raw_data(gnutls_privkey_t key, - const gnutls_datum_t * data, - gnutls_datum_t * signature, - gnutls_x509_spki_st * params); - /** * gnutls_privkey_get_type: * @key: should contain a #gnutls_privkey_t type @@ -134,10 +128,10 @@ int gnutls_privkey_get_pk_algorithm(gnutls_privkey_t key, unsigned int *bits) bits); #endif case GNUTLS_PRIVKEY_X509: - if (bits) - *bits = - _gnutls_mpi_get_nbits(key->key.x509-> - params.params[0]); + if (bits) { + *bits = pubkey_to_bits(key->key.x509->pk_algorithm, &key->key.x509->params); + } + return gnutls_x509_privkey_get_pk_algorithm(key->key.x509); case GNUTLS_PRIVKEY_EXT: if (bits) @@ -191,7 +185,7 @@ privkey_to_pubkey(gnutls_pk_algorithm_t pk, } break; - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: pub->params[ECC_X] = _gnutls_mpi_copy(priv->params[ECC_X]); pub->params[ECC_Y] = _gnutls_mpi_copy(priv->params[ECC_Y]); @@ -204,6 +198,12 @@ privkey_to_pubkey(gnutls_pk_algorithm_t pk, } break; + case GNUTLS_PK_EDDSA_ED25519: + ret = _gnutls_set_datum(&pub->raw_pub, priv->raw_pub.data, priv->raw_pub.size); + if (ret < 0) + return gnutls_assert_val(ret); + + break; default: gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; @@ -1007,7 +1007,11 @@ gnutls_privkey_sign_data(gnutls_privkey_t signer, return ret; } - return privkey_sign_data(signer, data, signature, ¶ms); + if (_gnutls_pk_is_not_prehashed(signer->pk_algorithm)) { + return privkey_sign_raw_data(signer, data, signature, ¶ms); + } else { + return privkey_sign_and_hash_data(signer, data, signature, ¶ms); + } } /** @@ -1059,7 +1063,7 @@ gnutls_privkey_sign_data2(gnutls_privkey_t signer, return ret; } - return privkey_sign_data(signer, data, signature, ¶ms); + return privkey_sign_and_hash_data(signer, data, signature, ¶ms); } /** @@ -1082,7 +1086,7 @@ gnutls_privkey_sign_data2(gnutls_privkey_t signer, * In the former case this function will ignore @hash_algo and perform a raw PKCS1 signature, * and in the latter an RSA-PSS signature will be generated. * - * Note that, not all algorithm support signing already signed data. When + * Note that, not all algorithm support signing already hashed data. When * signing with Ed25519, gnutls_privkey_sign_data() should be used. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a @@ -1118,14 +1122,14 @@ gnutls_privkey_sign_hash2(gnutls_privkey_t signer, return ret; } - return privkey_sign_hash(signer, hash_data, signature, ¶ms, flags); + return privkey_sign_prehashed(signer, hash_data, signature, ¶ms, flags); } int -privkey_sign_data(gnutls_privkey_t signer, - const gnutls_datum_t * data, - gnutls_datum_t * signature, - gnutls_x509_spki_st * params) +privkey_sign_and_hash_data(gnutls_privkey_t signer, + const gnutls_datum_t * data, + gnutls_datum_t * signature, + gnutls_x509_spki_st * params) { int ret; gnutls_datum_t digest; @@ -1146,7 +1150,7 @@ privkey_sign_data(gnutls_privkey_t signer, goto cleanup; } - ret = _gnutls_privkey_sign_raw_data(signer, &digest, signature, params); + ret = privkey_sign_raw_data(signer, &digest, signature, params); _gnutls_free_datum(&digest); if (ret < 0) { @@ -1178,8 +1182,12 @@ privkey_sign_data(gnutls_privkey_t signer, * You may use gnutls_pubkey_get_preferred_hash_algorithm() to determine * the hash algorithm. * - * Note that if %GNUTLS_PRIVKEY_SIGN_FLAG_TLS1_RSA flag is specified this function - * will ignore @hash_algo and perform a raw PKCS1 signature. + * The flags may be %GNUTLS_PRIVKEY_SIGN_FLAG_TLS1_RSA or %GNUTLS_PRIVKEY_SIGN_FLAG_RSA_PSS. + * In the former case this function will ignore @hash_algo and perform a raw PKCS1 signature, + * and in the latter an RSA-PSS signature will be generated. + * + * Note that, not all algorithm support signing already hashed data. When + * signing with Ed25519, gnutls_privkey_sign_data() should be used. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. @@ -1209,11 +1217,11 @@ gnutls_privkey_sign_hash(gnutls_privkey_t signer, return ret; } - return privkey_sign_hash(signer, hash_data, signature, ¶ms, flags); + return privkey_sign_prehashed(signer, hash_data, signature, ¶ms, flags); } static int -privkey_sign_hash(gnutls_privkey_t signer, +privkey_sign_prehashed(gnutls_privkey_t signer, const gnutls_datum_t * hash_data, gnutls_datum_t * signature, gnutls_x509_spki_st * params, @@ -1223,9 +1231,13 @@ privkey_sign_hash(gnutls_privkey_t signer, gnutls_datum_t digest; if (flags & GNUTLS_PRIVKEY_SIGN_FLAG_TLS1_RSA) - return _gnutls_privkey_sign_raw_data(signer, - hash_data, signature, - params); + return privkey_sign_raw_data(signer, + hash_data, signature, + params); + + if (_gnutls_pk_is_not_prehashed(signer->pk_algorithm)) { + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } digest.data = gnutls_malloc(hash_data->size); if (digest.data == NULL) { @@ -1241,9 +1253,9 @@ privkey_sign_hash(gnutls_privkey_t signer, goto cleanup; } - ret = _gnutls_privkey_sign_raw_data(signer, - &digest, signature, - params); + ret = privkey_sign_raw_data(signer, + &digest, signature, + params); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -1257,7 +1269,7 @@ privkey_sign_hash(gnutls_privkey_t signer, } /*- - * gnutls_privkey_sign_raw_data: + * privkey_sign_raw_data: * @key: Holds the key * @data: holds the data to be signed * @signature: will contain the signature allocated with gnutls_malloc() @@ -1277,11 +1289,11 @@ privkey_sign_hash(gnutls_privkey_t signer, * * Since: 3.1.10 -*/ -static int -_gnutls_privkey_sign_raw_data(gnutls_privkey_t key, - const gnutls_datum_t * data, - gnutls_datum_t * signature, - gnutls_x509_spki_st * params) +int +privkey_sign_raw_data(gnutls_privkey_t key, + const gnutls_datum_t * data, + gnutls_datum_t * signature, + gnutls_x509_spki_st * params) { switch (key->type) { #ifdef ENABLE_PKCS11 diff --git a/lib/privkey_raw.c b/lib/privkey_raw.c index 76e9c43f39..8e231b1808 100644 --- a/lib/privkey_raw.c +++ b/lib/privkey_raw.c @@ -46,7 +46,8 @@ * * This function will export the RSA private key's parameters found * in the given structure. The new parameters will be allocated using - * gnutls_malloc() and will be stored in the appropriate datum. + * gnutls_malloc() and will be stored in the appropriate datum. For + * EdDSA keys, the @y value should be %NULL. * * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. * diff --git a/lib/pubkey.c b/lib/pubkey.c index fa7bccc679..7a77c30741 100644 --- a/lib/pubkey.c +++ b/lib/pubkey.c @@ -46,7 +46,8 @@ unsigned pubkey_to_bits(gnutls_pk_algorithm_t pk, gnutls_pk_params_st * params) return _gnutls_mpi_get_nbits(params->params[RSA_MODULUS]); case GNUTLS_PK_DSA: return _gnutls_mpi_get_nbits(params->params[DSA_P]); - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: + case GNUTLS_PK_EDDSA_ED25519: return gnutls_ecc_curve_get_size(params->flags) * 8; default: return 0; @@ -285,7 +286,7 @@ gnutls_pubkey_get_preferred_hash_algorithm(gnutls_pubkey_t key, if (mand) *mand = 1; /* fallthrough */ - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: me = _gnutls_dsa_q_to_hash(key->pk_algorithm, &key->params, NULL); if (hash) @@ -293,6 +294,12 @@ gnutls_pubkey_get_preferred_hash_algorithm(gnutls_pubkey_t key, ret = 0; break; + case GNUTLS_PK_EDDSA_ED25519: + if (hash) + *hash = GNUTLS_DIG_SHA512; + + ret = 0; + break; case GNUTLS_PK_RSA: case GNUTLS_PK_RSA_PSS: if (hash) @@ -784,6 +791,7 @@ gnutls_pubkey_export_dsa_raw2(gnutls_pubkey_t key, * This function will export the ECC public key's parameters found in * the given key. The new parameters will be allocated using * gnutls_malloc() and will be stored in the appropriate datum. + * For EdDSA public keys, @y will be set to %NULL. * * This function allows for %NULL parameters since 3.4.1. * @@ -834,7 +842,7 @@ gnutls_pubkey_export_ecc_raw2(gnutls_pubkey_t key, return GNUTLS_E_INVALID_REQUEST; } - if (key->pk_algorithm != GNUTLS_PK_EC) { + if (!IS_EC(key->pk_algorithm)) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } @@ -842,6 +850,21 @@ gnutls_pubkey_export_ecc_raw2(gnutls_pubkey_t key, if (curve) *curve = key->params.flags; + if (key->pk_algorithm == GNUTLS_PK_EDDSA_ED25519) { + if (x) { + ret = _gnutls_set_datum(x, key->params.raw_pub.data, key->params.raw_pub.size); + if (ret < 0) + return gnutls_assert_val(ret); + } + if (y) { + y->data = NULL; + y->size = 0; + } + return 0; + } + + /* ECDSA */ + /* X */ if (x) { ret = dprint(key->params.params[ECC_X], x); @@ -937,6 +960,7 @@ gnutls_pubkey_import(gnutls_pubkey_t key, int result = 0, need_free = 0; gnutls_datum_t _data; ASN1_TYPE spk; + gnutls_ecc_curve_t curve; if (key == NULL) { gnutls_assert(); @@ -986,7 +1010,9 @@ gnutls_pubkey_import(gnutls_pubkey_t key, /* this has already been called by get_asn_mpis() thus it cannot * fail. */ - key->pk_algorithm = _gnutls_x509_get_pk_algorithm(spk, "", NULL); + key->pk_algorithm = _gnutls_x509_get_pk_algorithm(spk, "", &curve, NULL); + + key->params.flags = curve; key->bits = pubkey_to_bits(key->pk_algorithm, &key->params); result = 0; @@ -1256,7 +1282,8 @@ gnutls_pubkey_import_rsa_raw(gnutls_pubkey_t key, * @y: holds the y * * This function will convert the given elliptic curve parameters to a - * #gnutls_pubkey_t. The output will be stored in @key. + * #gnutls_pubkey_t. The output will be stored in @key. For EdDSA + * keys the @y parameter should be %NULL. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. @@ -1279,6 +1306,20 @@ gnutls_pubkey_import_ecc_raw(gnutls_pubkey_t key, gnutls_pk_params_release(&key->params); gnutls_pk_params_init(&key->params); + if (curve_is_eddsa(curve)) { + ret = _gnutls_set_datum(&key->params.raw_pub, x->data, x->size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + key->pk_algorithm = GNUTLS_PK_EDDSA_ED25519; + key->params.flags = curve; + + return 0; + } + + /* ECDSA */ key->params.flags = curve; if (_gnutls_mpi_init_scan_nz @@ -1296,7 +1337,7 @@ gnutls_pubkey_import_ecc_raw(gnutls_pubkey_t key, goto cleanup; } key->params.params_nr++; - key->pk_algorithm = GNUTLS_PK_EC; + key->pk_algorithm = GNUTLS_PK_ECDSA; return 0; @@ -1565,6 +1606,10 @@ gnutls_pubkey_verify_hash2(gnutls_pubkey_t key, return GNUTLS_E_INVALID_REQUEST; } + if (_gnutls_pk_is_not_prehashed(key->pk_algorithm)) { + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } + memcpy(¶ms, &key->params.sign, sizeof(gnutls_x509_spki_st)); if (flags & OLD_PUBKEY_VERIFY_FLAG_TLS1_RSA || flags & GNUTLS_VERIFY_USE_TLS1_RSA) { @@ -1866,7 +1911,7 @@ pubkey_verify_hashed_data(gnutls_pk_algorithm_t pk, return 1; break; - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: case GNUTLS_PK_DSA: if (dsa_verify_hashed_data (pk, hash_algo, hash, signature, params, sign_params) != 0) { @@ -1878,7 +1923,7 @@ pubkey_verify_hashed_data(gnutls_pk_algorithm_t pk, break; default: gnutls_assert(); - return GNUTLS_E_INTERNAL_ERROR; + return GNUTLS_E_INVALID_REQUEST; } } @@ -1906,6 +1951,15 @@ pubkey_verify_data(gnutls_pk_algorithm_t pk, return 1; break; + case GNUTLS_PK_EDDSA_ED25519: + if (_gnutls_pk_verify(pk, data, signature, params, sign_params) != 0) { + gnutls_assert(); + return GNUTLS_E_PK_SIG_VERIFY_FAILED; + } + + return 1; + break; + case GNUTLS_PK_EC: case GNUTLS_PK_DSA: if (dsa_verify_data @@ -1918,7 +1972,7 @@ pubkey_verify_data(gnutls_pk_algorithm_t pk, break; default: gnutls_assert(); - return GNUTLS_E_INTERNAL_ERROR; + return GNUTLS_E_INVALID_REQUEST; } } diff --git a/lib/x509/common.c b/lib/x509/common.c index a07b0ec5ed..b1be062c54 100644 --- a/lib/x509/common.c +++ b/lib/x509/common.c @@ -1036,13 +1036,13 @@ _gnutls_x509_encode_and_copy_PKI_params(ASN1_TYPE dst, pk_algorithm, gnutls_pk_params_st * params) { - const char *pk; + const char *oid; gnutls_datum_t der = { NULL, 0 }; int result; char name[128]; - pk = gnutls_pk_get_oid(pk_algorithm); - if (pk == NULL) { + oid = gnutls_pk_get_oid(pk_algorithm); + if (oid == NULL) { gnutls_assert(); return GNUTLS_E_UNKNOWN_PK_ALGORITHM; } @@ -1052,7 +1052,7 @@ _gnutls_x509_encode_and_copy_PKI_params(ASN1_TYPE dst, _asnstr_append_name(name, sizeof(name), dst_name, ".algorithm.algorithm"); - result = asn1_write_value(dst, name, pk, 1); + result = asn1_write_value(dst, name, oid, 1); if (result != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); @@ -1141,17 +1141,16 @@ _gnutls_x509_encode_PKI_params(gnutls_datum_t * der, */ int _gnutls_x509_get_pk_algorithm(ASN1_TYPE src, const char *src_name, + gnutls_ecc_curve_t *curve, unsigned int *bits) { int result; int algo; char oid[64]; int len; - gnutls_pk_params_st params; + gnutls_ecc_curve_t lcurve = GNUTLS_ECC_CURVE_INVALID; char name[128]; - gnutls_pk_params_init(¶ms); - _asnstr_append_name(name, sizeof(name), src_name, ".algorithm.algorithm"); len = sizeof(oid); @@ -1162,26 +1161,36 @@ _gnutls_x509_get_pk_algorithm(ASN1_TYPE src, const char *src_name, return _gnutls_asn2err(result); } - algo = gnutls_oid_to_pk(oid); + algo = _gnutls_oid_to_pk_and_curve(oid, &lcurve); if (algo == GNUTLS_PK_UNKNOWN) { _gnutls_debug_log ("%s: unknown public key algorithm: %s\n", __func__, oid); } + if (curve) + *curve = lcurve; + if (bits == NULL) { return algo; } /* Now read the parameters' bits */ - result = _gnutls_get_asn_mpis(src, src_name, ¶ms); - if (result < 0) - return gnutls_assert_val(result); + if (lcurve != GNUTLS_ECC_CURVE_INVALID) { /* curve present */ + bits[0] = gnutls_ecc_curve_get_size(lcurve)*8; + } else { + gnutls_pk_params_st params; + gnutls_pk_params_init(¶ms); - bits[0] = pubkey_to_bits(algo, ¶ms); + result = _gnutls_get_asn_mpis(src, src_name, ¶ms); + if (result < 0) + return gnutls_assert_val(result); + + bits[0] = pubkey_to_bits(algo, ¶ms); + gnutls_pk_params_release(¶ms); + } - gnutls_pk_params_release(¶ms); return algo; } diff --git a/lib/x509/common.h b/lib/x509/common.h index 6d80b819b8..34e6d5b5de 100644 --- a/lib/x509/common.h +++ b/lib/x509/common.h @@ -88,6 +88,8 @@ #define SIG_RSA_SHA3_384_OID "2.16.840.1.101.3.4.3.15" #define SIG_RSA_SHA3_512_OID "2.16.840.1.101.3.4.3.16" +#define SIG_EDDSA_SHA512_OID "1.3.101.112" + #define XMPP_OID "1.3.6.1.5.5.7.8.5" #define KRB5_PRINCIPAL_OID "1.3.6.1.5.2.2" #define PKIX1_RSA_PSS_MGF1_OID "1.2.840.113549.1.1.8" @@ -165,6 +167,7 @@ int _gnutls_x509_decode_and_read_attribute(ASN1_TYPE asn1_struct, int multi, int octet); int _gnutls_x509_get_pk_algorithm(ASN1_TYPE src, const char *src_name, + gnutls_ecc_curve_t *curve, unsigned int *bits); int diff --git a/lib/x509/crq.c b/lib/x509/crq.c index 2e53bfadb3..0f91278b4e 100644 --- a/lib/x509/crq.c +++ b/lib/x509/crq.c @@ -1314,7 +1314,7 @@ gnutls_x509_crq_get_pk_algorithm2(gnutls_x509_crq_t crq, } result = _gnutls_x509_get_pk_algorithm - (crq->crq, "certificationRequestInfo.subjectPKInfo", bits); + (crq->crq, "certificationRequestInfo.subjectPKInfo", NULL, bits); if (result < 0) { gnutls_assert(); return result; @@ -2862,7 +2862,7 @@ gnutls_x509_crq_privkey_sign(gnutls_x509_crq_t crq, gnutls_privkey_t key, return result; } - result = privkey_sign_data(key, &tbs, &signature, ¶ms); + result = privkey_sign_and_hash_data(key, &tbs, &signature, ¶ms); gnutls_free(tbs.data); if (result < 0) { diff --git a/lib/x509/key_decode.c b/lib/x509/key_decode.c index 5db6d9ebf0..a9b59d125b 100644 --- a/lib/x509/key_decode.c +++ b/lib/x509/key_decode.c @@ -37,6 +37,8 @@ static int _gnutls_x509_read_dsa_pubkey(uint8_t * der, int dersize, gnutls_pk_params_st * params); static int _gnutls_x509_read_ecc_pubkey(uint8_t * der, int dersize, gnutls_pk_params_st * params); +static int _gnutls_x509_read_eddsa_pubkey(uint8_t * der, int dersize, + gnutls_pk_params_st * params); static int _gnutls_x509_read_dsa_params(uint8_t * der, int dersize, @@ -109,6 +111,11 @@ _gnutls_x509_read_ecc_pubkey(uint8_t * der, int dersize, ¶ms->params[ECC_Y]); } +int _gnutls_x509_read_eddsa_pubkey(uint8_t * der, int dersize, + gnutls_pk_params_st * params) +{ + return _gnutls_set_datum(¶ms->raw_pub, der, dersize); +} /* reads p,q and g * from the certificate (subjectPublicKey BIT STRING). @@ -398,13 +405,16 @@ int _gnutls_x509_read_pubkey(gnutls_pk_algorithm_t algo, uint8_t * der, params->params_nr = DSA_PUBLIC_PARAMS; } break; - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: ret = _gnutls_x509_read_ecc_pubkey(der, dersize, params); if (ret >= 0) { params->algo = GNUTLS_PK_ECDSA; params->params_nr = ECC_PUBLIC_PARAMS; } break; + case GNUTLS_PK_EDDSA_ED25519: + ret = _gnutls_x509_read_eddsa_pubkey(der, dersize, params); + break; default: ret = gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); break; @@ -420,6 +430,7 @@ int _gnutls_x509_read_pubkey_params(gnutls_pk_algorithm_t algo, { switch (algo) { case GNUTLS_PK_RSA: + case GNUTLS_PK_EDDSA_ED25519: return 0; case GNUTLS_PK_RSA_PSS: return _gnutls_x509_read_rsa_pss_params(der, dersize, ¶ms->sign); @@ -453,7 +464,8 @@ int _gnutls_x509_check_pubkey_params(gnutls_pk_algorithm_t algo, } case GNUTLS_PK_RSA: case GNUTLS_PK_DSA: - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: + case GNUTLS_PK_EDDSA_ED25519: return 0; default: return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); diff --git a/lib/x509/key_encode.c b/lib/x509/key_encode.c index af5e5be5a7..58b1362c58 100644 --- a/lib/x509/key_encode.c +++ b/lib/x509/key_encode.c @@ -128,6 +128,34 @@ _gnutls_x509_write_ecc_pubkey(gnutls_pk_params_st * params, return 0; } +/* + * some x509 certificate functions that relate to MPI parameter + * setting. This writes a raw public key. + * + * Allocates the space used to store the data. + */ +int +_gnutls_x509_write_eddsa_pubkey(gnutls_pk_params_st * params, + gnutls_datum_t * raw) +{ + int ret; + + raw->data = NULL; + raw->size = 0; + + if (params->raw_pub.size == 0) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + if (params->flags != GNUTLS_ECC_CURVE_ED25519) + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + + ret = _gnutls_set_datum(raw, params->raw_pub.data, params->raw_pub.size); + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + int _gnutls_x509_write_pubkey_params(gnutls_pk_algorithm_t algo, gnutls_pk_params_st * params, @@ -146,8 +174,13 @@ _gnutls_x509_write_pubkey_params(gnutls_pk_algorithm_t algo, return 0; case GNUTLS_PK_RSA_PSS: return _gnutls_x509_write_rsa_pss_params(¶ms->sign, der); - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: return _gnutls_x509_write_ecc_params(params->flags, der); + case GNUTLS_PK_EDDSA_ED25519: + der->data = NULL; + der->size = 0; + + return 0; default: return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); } @@ -164,8 +197,10 @@ _gnutls_x509_write_pubkey(gnutls_pk_algorithm_t algo, case GNUTLS_PK_RSA: case GNUTLS_PK_RSA_PSS: return _gnutls_x509_write_rsa_pubkey(params, der); - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: return _gnutls_x509_write_ecc_pubkey(params, der); + case GNUTLS_PK_EDDSA_ED25519: + return _gnutls_x509_write_eddsa_pubkey(params, der); default: return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); } @@ -615,20 +650,9 @@ _gnutls_asn1_encode_ecc(ASN1_TYPE * c2, gnutls_pk_params_st * params) const char *oid; oid = gnutls_ecc_curve_get_oid(params->flags); - - if (params->params_nr != ECC_PRIVATE_PARAMS || oid == NULL) + if (oid == NULL) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); - ret = - _gnutls_ecc_ansi_x962_export(params->flags, - params->params[ECC_X], - params->params[ECC_Y], &pubkey); - if (ret < 0) - return gnutls_assert_val(ret); - - /* Ok. Now we have the data. Create the asn1 structures - */ - /* first make sure that no previously allocated data are leaked */ if (*c2 != ASN1_TYPE_EMPTY) { asn1_delete_structure(c2); @@ -650,20 +674,50 @@ _gnutls_asn1_encode_ecc(ASN1_TYPE * c2, gnutls_pk_params_st * params) goto cleanup; } - ret = - _gnutls_x509_write_key_int(*c2, "privateKey", - params->params[ECC_K], 1); - if (ret < 0) { - gnutls_assert(); - goto cleanup; - } + if (curve_is_eddsa(params->flags)) { + if (params->raw_pub.size == 0 || params->raw_priv.size == 0) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + ret = + asn1_write_value(*c2, "privateKey", params->raw_priv.data, params->raw_priv.size); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } - if ((ret = - asn1_write_value(*c2, "publicKey", pubkey.data, - pubkey.size * 8)) != ASN1_SUCCESS) { - gnutls_assert(); - ret = _gnutls_asn2err(ret); - goto cleanup; + ret = + asn1_write_value(*c2, "publicKey", params->raw_pub.data, params->raw_pub.size*8); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + } else { + if (params->params_nr != ECC_PRIVATE_PARAMS) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + ret = + _gnutls_ecc_ansi_x962_export(params->flags, + params->params[ECC_X], + params->params[ECC_Y], &pubkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + _gnutls_x509_write_key_int(*c2, "privateKey", + params->params[ECC_K], 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if ((ret = + asn1_write_value(*c2, "publicKey", pubkey.data, + pubkey.size * 8)) != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } } /* write our choice */ @@ -799,7 +853,8 @@ int _gnutls_asn1_encode_privkey(gnutls_pk_algorithm_t pk, ASN1_TYPE * c2, return _gnutls_asn1_encode_rsa(c2, params, compat); case GNUTLS_PK_DSA: return _gnutls_asn1_encode_dsa(c2, params, compat); - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: + case GNUTLS_PK_EDDSA_ED25519: return _gnutls_asn1_encode_ecc(c2, params); default: return GNUTLS_E_UNIMPLEMENTED_FEATURE; diff --git a/lib/x509/mpi.c b/lib/x509/mpi.c index bd47d3244c..384e6e9fda 100644 --- a/lib/x509/mpi.c +++ b/lib/x509/mpi.c @@ -114,16 +114,18 @@ _gnutls_get_asn_mpis(ASN1_TYPE asn, const char *root, char name[256]; gnutls_datum_t tmp = { NULL, 0 }; gnutls_pk_algorithm_t pk_algorithm; + gnutls_ecc_curve_t curve; gnutls_pk_params_init(params); - result = _gnutls_x509_get_pk_algorithm(asn, root, NULL); + result = _gnutls_x509_get_pk_algorithm(asn, root, &curve, NULL); if (result < 0) { gnutls_assert(); return result; } pk_algorithm = result; + params->flags = curve; /* Read the algorithm's parameters */ @@ -132,9 +134,10 @@ _gnutls_get_asn_mpis(ASN1_TYPE asn, const char *root, /* FIXME: If the parameters are not included in the certificate * then the issuer's parameters should be used. This is not - * done yet. + * needed in practice though. */ - if (pk_algorithm != GNUTLS_PK_RSA) { /* RSA doesn't use parameters */ + if (pk_algorithm != GNUTLS_PK_RSA && pk_algorithm != GNUTLS_PK_EDDSA_ED25519 && pk_algorithm != GNUTLS_PK_ECDHX) { + /* RSA and EdDSA do not use parameters */ result = _gnutls_x509_read_value(asn, name, &tmp); if (result < 0) { gnutls_assert(); diff --git a/lib/x509/output.c b/lib/x509/output.c index 93eed2e57e..b12c4890e4 100644 --- a/lib/x509/output.c +++ b/lib/x509/output.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2007-2016 Free Software Foundation, Inc. - * Copyright (C) 2015-2016 Red Hat, Inc. + * Copyright (C) 2015-2017 Red Hat, Inc. * * Author: Simon Josefsson, Nikos Mavrogiannopoulos * @@ -1279,7 +1279,8 @@ print_pubkey(gnutls_buffer_st * str, const char *key_name, } break; - case GNUTLS_PK_EC: + case GNUTLS_PK_EDDSA_ED25519: + case GNUTLS_PK_ECDSA: { gnutls_datum_t x, y; gnutls_ecc_curve_t curve; @@ -1287,10 +1288,10 @@ print_pubkey(gnutls_buffer_st * str, const char *key_name, err = gnutls_pubkey_get_pk_ecc_raw(pubkey, &curve, &x, &y); - if (err < 0) + if (err < 0) { addf(str, "error: get_pk_ecc_raw: %s\n", gnutls_strerror(err)); - else { + } else { addf(str, _("\t\tCurve:\t%s\n"), gnutls_ecc_curve_get_name(curve)); if (format == @@ -1300,20 +1301,24 @@ print_pubkey(gnutls_buffer_st * str, const char *key_name, x.data, x.size); adds(str, "\n"); - adds(str, _("\t\tY: ")); - _gnutls_buffer_hexprint(str, - y.data, - y.size); - adds(str, "\n"); + if (y.size > 0) { + adds(str, _("\t\tY: ")); + _gnutls_buffer_hexprint(str, + y.data, + y.size); + adds(str, "\n"); + } } else { adds(str, _("\t\tX:\n")); _gnutls_buffer_hexdump(str, x.data, x.size, "\t\t\t"); - adds(str, _("\t\tY:\n")); - _gnutls_buffer_hexdump(str, y.data, - y.size, - "\t\t\t"); + if (y.size > 0) { + adds(str, _("\t\tY:\n")); + _gnutls_buffer_hexdump(str, y.data, + y.size, + "\t\t\t"); + } } gnutls_free(x.data); @@ -1753,7 +1758,7 @@ static void print_keyid(gnutls_buffer_st * str, gnutls_x509_crt_t cert) print_obj_id(str, "\t", cert, (get_id_func*)gnutls_x509_crt_get_key_id); - if (err == GNUTLS_PK_EC) { + if (IS_EC(err)) { gnutls_ecc_curve_t curve; err = gnutls_x509_crt_get_pk_ecc_raw(cert, &curve, NULL, NULL); diff --git a/lib/x509/pkcs7.c b/lib/x509/pkcs7.c index 2812b7f89d..b8670c8134 100644 --- a/lib/x509/pkcs7.c +++ b/lib/x509/pkcs7.c @@ -2536,7 +2536,7 @@ int gnutls_pkcs7_sign(gnutls_pkcs7_t pkcs7, goto cleanup; } - ret = privkey_sign_data(signer_key, &sigdata, &signature, ¶ms); + ret = privkey_sign_and_hash_data(signer_key, &sigdata, &signature, ¶ms); if (ret < 0) { gnutls_assert(); goto cleanup; diff --git a/lib/x509/privkey.c b/lib/x509/privkey.c index b82d6d5236..7ecdab0129 100644 --- a/lib/x509/privkey.c +++ b/lib/x509/privkey.c @@ -267,6 +267,10 @@ _gnutls_privkey_decode_ecc_key(ASN1_TYPE* pkey_asn, const gnutls_datum_t * raw_k int oid_size; gnutls_datum_t out; + if (curve_is_eddsa(curve)) { + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } + gnutls_pk_params_init(&pkey->params); if ((ret = @@ -1102,7 +1106,7 @@ gnutls_x509_privkey_import_dsa_raw(gnutls_x509_privkey_t key, * * This function will convert the given elliptic curve parameters to the * native #gnutls_x509_privkey_t format. The output will be stored - * in @key. + * in @key. For EdDSA keys, the @x and @k values will be read. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. @@ -1125,6 +1129,26 @@ gnutls_x509_privkey_import_ecc_raw(gnutls_x509_privkey_t key, key->params.flags = curve; + if (curve_is_eddsa(curve)) { + key->params.algo = GNUTLS_PK_EDDSA_ED25519; + + ret = _gnutls_set_datum(&key->params.raw_pub, x->data, x->size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_set_datum(&key->params.raw_priv, k->data, k->size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + key->pk_algorithm = key->params.algo; + + return 0; + } + if (_gnutls_mpi_init_scan_nz (&key->params.params[ECC_X], x->data, x->size)) { gnutls_assert(); @@ -1149,6 +1173,8 @@ gnutls_x509_privkey_import_ecc_raw(gnutls_x509_privkey_t key, } key->params.params_nr++; + key->params.algo = key->pk_algorithm = GNUTLS_PK_EC; + ret = _gnutls_pk_fixup(GNUTLS_PK_EC, GNUTLS_IMPORT, &key->params); if (ret < 0) { @@ -1156,9 +1182,6 @@ gnutls_x509_privkey_import_ecc_raw(gnutls_x509_privkey_t key, goto cleanup; } - key->pk_algorithm = GNUTLS_PK_EC; - key->params.algo = key->pk_algorithm; - return 0; cleanup: @@ -1388,6 +1411,7 @@ gnutls_sec_param_t gnutls_x509_privkey_sec_param(gnutls_x509_privkey_t key) * This function will export the ECC private key's parameters found * in the given structure. The new parameters will be allocated using * gnutls_malloc() and will be stored in the appropriate datum. + * For EdDSA keys, the @y value should be %NULL. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. @@ -1574,11 +1598,16 @@ gnutls_x509_privkey_generate2(gnutls_x509_privkey_t key, } } - if (algo == GNUTLS_PK_EC) { + if (IS_EC(algo)) { if (GNUTLS_BITS_ARE_CURVE(bits)) bits = GNUTLS_BITS_TO_CURVE(bits); else - bits = _gnutls_ecc_bits_to_curve(bits); + bits = _gnutls_ecc_bits_to_curve(algo, bits); + + if (gnutls_ecc_curve_get_pk(bits) != algo) { + _gnutls_debug_log("curve is incompatible with public key algorithm\n"); + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } } if (flags & GNUTLS_PRIVKEY_FLAG_PROVABLE) { diff --git a/lib/x509/privkey_pkcs8.c b/lib/x509/privkey_pkcs8.c index 827794bec2..620357110f 100644 --- a/lib/x509/privkey_pkcs8.c +++ b/lib/x509/privkey_pkcs8.c @@ -66,9 +66,19 @@ _encode_privkey(gnutls_x509_privkey_t pkey, gnutls_datum_t * raw) ASN1_TYPE spk = ASN1_TYPE_EMPTY; switch (pkey->pk_algorithm) { + case GNUTLS_PK_EDDSA_ED25519: + /* we encode as octet string (which is going to be stored inside + * another octet string). No comments. */ + ret = _gnutls_x509_encode_string(ASN1_ETYPE_OCTET_STRING, + pkey->params.raw_priv.data, pkey->params.raw_priv.size, + raw); + if (ret < 0) + gnutls_assert(); + return ret; + case GNUTLS_PK_RSA: case GNUTLS_PK_RSA_PSS: - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: ret = gnutls_x509_privkey_export2(pkey, GNUTLS_X509_FMT_DER, raw); @@ -999,6 +1009,7 @@ _decode_pkcs8_ecc_key(ASN1_TYPE pkcs8_asn, gnutls_x509_privkey_t pkey) if (result == ASN1_SUCCESS) { ret = _gnutls_x509_read_ecc_params(oid, len, &curve); if (ret < 0) { + _gnutls_debug_log("PKCS#8: unknown curve OID %s\n", oid); curve = GNUTLS_ECC_CURVE_INVALID; } } @@ -1023,6 +1034,41 @@ _decode_pkcs8_ecc_key(ASN1_TYPE pkcs8_asn, gnutls_x509_privkey_t pkey) return ret; } +static int +_decode_pkcs8_eddsa_key(ASN1_TYPE pkcs8_asn, gnutls_x509_privkey_t pkey, const char *oid) +{ + int ret; + gnutls_datum_t tmp; + gnutls_ecc_curve_t curve = GNUTLS_ECC_CURVE_INVALID; + const gnutls_ecc_curve_entry_st *ce; + + curve = gnutls_oid_to_ecc_curve(oid); + if (curve == GNUTLS_ECC_CURVE_INVALID) { + _gnutls_debug_log("PKCS#8: unknown curve OID %s\n", oid); + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + } + + ce = _gnutls_ecc_curve_get_params(curve); + if (_curve_is_eddsa(ce)) { + ret = _gnutls_x509_read_string(pkcs8_asn, "privateKey", &tmp, ASN1_ETYPE_OCTET_STRING, 1); + if (ret < 0) { + gnutls_assert(); + return gnutls_assert_val(ret); + } + + gnutls_free(pkey->params.raw_priv.data); + pkey->params.algo = GNUTLS_PK_EDDSA_ED25519; + pkey->params.raw_priv.data = tmp.data; + pkey->params.raw_priv.size = tmp.size; + pkey->params.flags = curve; + + tmp.data = NULL; + return 0; + } else { + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + } +} + /* Decodes an DSA privateKey and params from a PKCS8 structure. */ static int @@ -1157,17 +1203,25 @@ decode_private_key_info(const gnutls_datum_t * der, /* Get the DER encoding of the actual private key. */ - if (pkey->pk_algorithm == GNUTLS_PK_RSA) - result = _decode_pkcs8_rsa_key(pkcs8_asn, pkey); - else if (pkey->pk_algorithm == GNUTLS_PK_RSA_PSS) - result = _decode_pkcs8_rsa_pss_key(pkcs8_asn, pkey); - else if (pkey->pk_algorithm == GNUTLS_PK_DSA) - result = _decode_pkcs8_dsa_key(pkcs8_asn, pkey); - else if (pkey->pk_algorithm == GNUTLS_PK_EC) - result = _decode_pkcs8_ecc_key(pkcs8_asn, pkey); - else { - result = gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); - goto error; + switch(pkey->pk_algorithm) { + case GNUTLS_PK_RSA: + result = _decode_pkcs8_rsa_key(pkcs8_asn, pkey); + break; + case GNUTLS_PK_RSA_PSS: + result = _decode_pkcs8_rsa_pss_key(pkcs8_asn, pkey); + break; + case GNUTLS_PK_DSA: + result = _decode_pkcs8_dsa_key(pkcs8_asn, pkey); + break; + case GNUTLS_PK_ECDSA: + result = _decode_pkcs8_ecc_key(pkcs8_asn, pkey); + break; + case GNUTLS_PK_EDDSA_ED25519: + result = _decode_pkcs8_eddsa_key(pkcs8_asn, pkey, oid); + break; + default: + result = gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); + goto error; } if (result < 0) { diff --git a/lib/x509/sign.c b/lib/x509/sign.c index 20387d8826..dd688ea9ab 100644 --- a/lib/x509/sign.c +++ b/lib/x509/sign.c @@ -168,7 +168,11 @@ _gnutls_x509_pkix_sign(ASN1_TYPE src, const char *src_name, return result; } - result = privkey_sign_data(issuer_key, &tbs, &signature, ¶ms); + if (_gnutls_pk_is_not_prehashed(issuer_key->pk_algorithm)) { + result = privkey_sign_raw_data(issuer_key, &tbs, &signature, ¶ms); + } else { + result = privkey_sign_and_hash_data(issuer_key, &tbs, &signature, ¶ms); + } gnutls_free(tbs.data); if (result < 0) { diff --git a/lib/x509/x509.c b/lib/x509/x509.c index f346c93b3b..f779cd97b4 100644 --- a/lib/x509/x509.c +++ b/lib/x509/x509.c @@ -1592,6 +1592,7 @@ gnutls_x509_crt_get_pk_algorithm2(gnutls_x509_crt_t cert, result = _gnutls_x509_get_pk_algorithm(cert->cert, "tbsCertificate.subjectPublicKeyInfo", + NULL, bits); if (result < 0) { @@ -3019,6 +3020,7 @@ gnutls_x509_crt_get_key_id(gnutls_x509_crt_t crt, unsigned int flags, return pk; } + /* initializes params */ ret = _gnutls_x509_crt_get_mpis(crt, ¶ms); if (ret < 0) { gnutls_assert(); diff --git a/lib/x509/x509_int.h b/lib/x509/x509_int.h index 13cbc96e4b..6160a50e0c 100644 --- a/lib/x509/x509_int.h +++ b/lib/x509/x509_int.h @@ -244,6 +244,11 @@ int _gnutls_privkey_decode_ecc_key(ASN1_TYPE* pkey_asn, const gnutls_datum_t * gnutls_x509_privkey_t pkey, gnutls_ecc_curve_t curve); +int _gnutls_privkey_decode_eddsa_key(ASN1_TYPE* pkey_asn, + const gnutls_datum_t *raw_key, + gnutls_x509_privkey_t pkey, + gnutls_ecc_curve_t curve); + int _gnutls_x509_read_ecc_params(uint8_t * der, int dersize, unsigned int *curve); @@ -342,6 +347,9 @@ int _gnutls_x509_write_ecc_params(gnutls_ecc_curve_t curve, int _gnutls_x509_write_ecc_pubkey(gnutls_pk_params_st * params, gnutls_datum_t * der); +int _gnutls_x509_write_eddsa_pubkey(gnutls_pk_params_st * params, + gnutls_datum_t * der); + int _gnutls_x509_write_pubkey_params(gnutls_pk_algorithm_t algo, gnutls_pk_params_st * params, diff --git a/src/certtool-args.def b/src/certtool-args.def index e2d29090af..edc5aa3ff4 100644 --- a/src/certtool-args.def +++ b/src/certtool-args.def @@ -437,6 +437,12 @@ flag = { }; flag = { + name = eddsa; + descrip = "Generate EdDSA key"; + doc = "When combined with --generate-privkey generates an elliptic curve private key to be used with EdDSA."; +}; + +flag = { name = rsa-pss; descrip = "Generate RSA-PSS key or certificate"; doc = "This option can be combined with --generate-privkey or --generate-certificate, to generate private key or certificate restricted to the use with the RSA-PSS padding scheme."; diff --git a/src/certtool-common.c b/src/certtool-common.c index 2453024567..6ab2238429 100644 --- a/src/certtool-common.c +++ b/src/certtool-common.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2003-2016 Free Software Foundation, Inc. - * Copyright (C) 2015-2016 Red Hat, Inc. + * Copyright (C) 2015-2017 Red Hat, Inc. * * This file is part of GnuTLS. * @@ -891,14 +891,20 @@ print_ecc_pkey(FILE * outfile, gnutls_ecc_curve_t curve, fprintf(outfile, "curve:\t%s\n", gnutls_ecc_curve_get_name(curve)); - if (k) { + if (k && k->data) { print_head(outfile, "private key", k->size, cprint); print_hex_datum(outfile, k, cprint); } - print_head(outfile, "x", x->size, cprint); - print_hex_datum(outfile, x, cprint); - print_head(outfile, "y", y->size, cprint); - print_hex_datum(outfile, y, cprint); + + if (x && x->data) { + print_head(outfile, "x", x->size, cprint); + print_hex_datum(outfile, x, cprint); + } + + if (y && y->data) { + print_head(outfile, "y", y->size, cprint); + print_hex_datum(outfile, y, cprint); + } } @@ -1197,7 +1203,7 @@ static void privkey_info_int(FILE *outfile, common_info_st * cinfo, gnutls_free(q.data); gnutls_free(g.data); } - } else if (key_type == GNUTLS_PK_EC) { + } else if (key_type == GNUTLS_PK_ECDSA || key_type == GNUTLS_PK_EDDSA_ED25519) { gnutls_datum_t y, x, k; gnutls_ecc_curve_t curve; @@ -1264,11 +1270,14 @@ print_private_key(FILE *outfile, common_info_st * cinfo, gnutls_x509_privkey_t k if (!key) return; + /* Only print private key parameters when an unencrypted + * format is used */ + if (cinfo->outcert_format == GNUTLS_X509_FMT_PEM) + privkey_info_int(outfile, cinfo, key); + + switch_to_pkcs8_when_needed(cinfo, gnutls_x509_privkey_get_pk_algorithm(key)); + if (!cinfo->pkcs8) { - /* Only print private key parameters when an unencrypted - * format is used */ - if (cinfo->outcert_format == GNUTLS_X509_FMT_PEM) - privkey_info_int(outfile, cinfo, key); size = lbuffer_size; ret = gnutls_x509_privkey_export(key, cinfo->outcert_format, diff --git a/src/certtool-common.h b/src/certtool-common.h index 16c3c53dfa..a4a8b5e085 100644 --- a/src/certtool-common.h +++ b/src/certtool-common.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2003-2012 Free Software Foundation, Inc. + * Copyright (C) 2017 Red Hat, Inc. * * This file is part of GnuTLS. * @@ -79,6 +80,18 @@ typedef struct common_info { unsigned sort_chain; } common_info_st; +static inline +void switch_to_pkcs8_when_needed(common_info_st *cinfo, unsigned key_type) +{ + if ((key_type == GNUTLS_PK_RSA_PSS || key_type == GNUTLS_PK_EDDSA_ED25519) && !cinfo->pkcs8) { + fprintf(stderr, "Assuming --pkcs8 is given; %s private keys can only be exported in PKCS#8 format\n", + gnutls_pk_algorithm_get_name(key_type)); + cinfo->pkcs8 = 1; + if (cinfo->password == NULL) + cinfo->password = ""; + } +} + /* this must be provided by the app */ const char *get_pass(void); const char *get_confirmed_pass(bool empty_ok); diff --git a/src/certtool.c b/src/certtool.c index abaac83d62..4ba3fae775 100644 --- a/src/certtool.c +++ b/src/certtool.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2003-2016 Free Software Foundation, Inc. - * Copyright (C) 2015-2016 Red Hat, Inc. + * Copyright (C) 2015-2017 Red Hat, Inc. * * This file is part of GnuTLS. * @@ -128,11 +128,6 @@ int main(int argc, char **argv) return 0; } -#define ENABLE_PKCS8(cinfo) \ - cinfo->pkcs8 = 1; \ - if (!HAVE_OPT(PASSWORD) && cinfo->password == NULL) \ - cinfo->password = "" - static gnutls_x509_privkey_t generate_private_key_int(common_info_st * cinfo) { @@ -151,22 +146,23 @@ generate_private_key_int(common_info_st * cinfo) bits = get_bits(key_type, cinfo->bits, cinfo->sec_param, 1); - if (key_type == GNUTLS_PK_RSA_PSS && !cinfo->pkcs8) { - fprintf(stderr, "Assuming --pkcs8 is given; RSA-PSS private keys can only be exported in PKCS#8 format\n"); - ENABLE_PKCS8(cinfo); - } + switch_to_pkcs8_when_needed(cinfo, key_type); - if (key_type == GNUTLS_PK_EC) { + if (key_type == GNUTLS_PK_ECDSA || key_type == GNUTLS_PK_EDDSA_ED25519) { + char name[64]; int ecc_bits; if (GNUTLS_BITS_ARE_CURVE(bits)) { gnutls_ecc_curve_t curve = GNUTLS_BITS_TO_CURVE(bits); ecc_bits = gnutls_ecc_curve_get_size(curve) * 8; + snprintf(name, sizeof(name), "(%s)", gnutls_ecc_curve_get_name(curve)); } else { ecc_bits = bits; + name[0] = 0; } - fprintf(stdlog, "Generating a %d bit %s private key...\n", - ecc_bits, gnutls_pk_algorithm_get_name(key_type)); + + fprintf(stdlog, "Generating a %d bit %s private key %s...\n", + ecc_bits, gnutls_pk_algorithm_get_name(key_type), name); if (ecc_bits < 256) fprintf(stderr, @@ -1182,7 +1178,9 @@ static void cmd_parser(int argc, char **argv) if (HAVE_OPT(DSA)) req_key_type = GNUTLS_PK_DSA; else if (HAVE_OPT(ECC)) - req_key_type = GNUTLS_PK_ECC; + req_key_type = GNUTLS_PK_ECDSA; + else if (HAVE_OPT(EDDSA)) + req_key_type = GNUTLS_PK_EDDSA_ED25519; else if (HAVE_OPT(RSA_PSS)) req_key_type = GNUTLS_PK_RSA_PSS; else |