From d95d4a4f3abbc54c7d03872d7bf0987be61c1051 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Wed, 27 Apr 2016 12:12:06 +0200 Subject: Added support for generating and reading EdDSA/Ed25519 keys This adds support for draft-ietf-curdle-pkix-00 --- doc/credentials/x509/Makefile.am | 3 +- doc/credentials/x509/cert-eddsa.pem | 9 ++ lib/algorithms.h | 23 ++++- lib/algorithms/ecc.c | 21 +++- lib/algorithms/publickey.c | 1 + lib/algorithms/sign.c | 3 + lib/auth/ecdhe.c | 5 +- lib/gnutls.asn | 2 + lib/gnutls_asn1_tab.c | 5 +- lib/includes/gnutls/abstract.h | 8 ++ lib/includes/gnutls/gnutls.h.in | 12 ++- lib/nettle/pk.c | 159 +++++++++++++++++++++++++++--- lib/pk.c | 31 +++++- lib/pkcs11_privkey.c | 6 +- lib/privkey.c | 17 +++- lib/privkey_raw.c | 3 +- lib/pubkey.c | 76 ++++++++++++-- lib/x509/common.h | 2 + lib/x509/key_decode.c | 65 +++++++++++- lib/x509/key_encode.c | 156 +++++++++++++++++++++++------ lib/x509/output.c | 2 +- lib/x509/privkey.c | 127 ++++++++++++++++-------- lib/x509/privkey_pkcs8.c | 3 +- lib/x509/x509_int.h | 13 +++ src/certtool-args.def | 6 ++ src/certtool-common.c | 18 ++-- src/certtool.c | 10 +- tests/cert-tests/Makefile.am | 5 +- tests/cert-tests/ed25519 | 82 +++++++++++++++ tests/cert-tests/pkcs7-detached.eddsa.sig | 14 +++ 30 files changed, 760 insertions(+), 127 deletions(-) create mode 100644 doc/credentials/x509/cert-eddsa.pem create mode 100755 tests/cert-tests/ed25519 create mode 100644 tests/cert-tests/pkcs7-detached.eddsa.sig diff --git a/doc/credentials/x509/Makefile.am b/doc/credentials/x509/Makefile.am index b40a52ed52..80529f5588 100644 --- a/doc/credentials/x509/Makefile.am +++ b/doc/credentials/x509/Makefile.am @@ -1,2 +1,3 @@ EXTRA_DIST = ca-key.pem ca.pem cert-rsa.pem key-rsa.pem clikey.pem clicert.pem \ - clicert-dsa.pem clikey-dsa.pem cert-dsa.pem key-dsa.pem cert-ecc.pem key-ecc.pem + clicert-dsa.pem clikey-dsa.pem cert-dsa.pem key-dsa.pem cert-ecc.pem key-ecc.pem \ + key-eddsa.pem cert-eddsa.pem diff --git a/doc/credentials/x509/cert-eddsa.pem b/doc/credentials/x509/cert-eddsa.pem new file mode 100644 index 0000000000..eb87d12491 --- /dev/null +++ b/doc/credentials/x509/cert-eddsa.pem @@ -0,0 +1,9 @@ +-----BEGIN CERTIFICATE----- +MIIBOTCB7KADAgECAghXIMh0GpYGCDAFBgMrZWQwFTETMBEGA1UEAxMKVGVzdCBF +ZERTQTAgFw0xNjA0MjcxNDExMDFaGA85OTk5MTIzMTIzNTk1OVowFTETMBEGA1UE +AxMKVGVzdCBFZERTQTAtMAgGAytlZAoBAgMhAKTkfrObKJtnttNIzxQBgFiFKb/7 +82TkhQ1x3zPKDlumo1UwUzAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUF +BwMBMA8GA1UdDwEB/wQFAwMHgAAwHQYDVR0OBBYEFOBkJtX8yjpbIXnFik3dAC10 +o5a/MAUGAytlZANBANDF032xvnU+5xjOEruQe0MBLdIv9Sy34oQLpDC7D8Pez6bN +f0/CMwlyEeE22wCWYvDRNw7sE35hFe+j27StTw8= +-----END CERTIFICATE----- diff --git a/lib/algorithms.h b/lib/algorithms.h index 5ab350b098..aa9f52c35f 100644 --- a/lib/algorithms.h +++ b/lib/algorithms.h @@ -34,7 +34,7 @@ /* would allow for 256 ciphersuites */ #define MAX_CIPHERSUITE_SIZE 512 -#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)) /* Functions for version handling. */ const version_entry_st *version_to_entry(gnutls_protocol_t c); @@ -323,7 +323,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 of curve 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; @@ -332,9 +333,25 @@ 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 +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) + 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 9d0c584b0a..bac5466c99 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 = "Ed25519ph", + .oid = "1.3.101.112", + .id = GNUTLS_ECC_CURVE_ED25519PH, + .pk = GNUTLS_PK_EDDSA, + .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 (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_ED25519PH; 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 c70187736f..2f0bc6b501 100644 --- a/lib/algorithms/publickey.c +++ b/lib/algorithms/publickey.c @@ -97,6 +97,7 @@ static const gnutls_pk_entry pk_algorithms[] = { {"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}, + {"EdDSA", "1.3.101.100", GNUTLS_PK_EDDSA}, {"DH", NULL, GNUTLS_PK_DH}, {"ECDHX", NULL, GNUTLS_PK_ECDHX}, {0, 0, 0} diff --git a/lib/algorithms/sign.c b/lib/algorithms/sign.c index 6107c44435..5d62ab643b 100644 --- a/lib/algorithms/sign.c +++ b/lib/algorithms/sign.c @@ -106,6 +106,9 @@ static const gnutls_sign_entry sign_algorithms[] = { {"ECDSA-SHA3-512", SIG_ECDSA_SHA3_512_OID, GNUTLS_SIGN_ECDSA_SHA3_512, GNUTLS_PK_EC, GNUTLS_DIG_SHA3_512, TLS_SIGN_AID_UNKNOWN}, + {"EdDSA-SHA2-512", SIG_EDDSA_SHA512_OID, GNUTLS_SIGN_EDDSA_SHA512, + GNUTLS_PK_EDDSA, GNUTLS_DIG_SHA512, TLS_SIGN_AID_UNKNOWN}, + {"RSA-SHA3-224", SIG_RSA_SHA3_224_OID, GNUTLS_SIGN_RSA_SHA3_224, GNUTLS_PK_RSA, GNUTLS_DIG_SHA3_224, TLS_SIGN_AID_UNKNOWN}, {"RSA-SHA3-256", SIG_RSA_SHA3_256_OID, GNUTLS_SIGN_RSA_SHA3_256, diff --git a/lib/auth/ecdhe.c b/lib/auth/ecdhe.c index e445c2f0fe..b86d259bd1 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) @@ -315,7 +315,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/gnutls.asn b/lib/gnutls.asn index 76bad6fbb6..1205c61eb0 100644 --- a/lib/gnutls.asn +++ b/lib/gnutls.asn @@ -122,4 +122,6 @@ KRB5PrincipalName ::= SEQUENCE { principalName [1] PrincipalName } +EdDSAParameters ::= ENUMERATED { ed25519 (1), ed25519p (2) } + END diff --git a/lib/gnutls_asn1_tab.c b/lib/gnutls_asn1_tab.c index bc0e85ef5d..bcc0b67d2a 100644 --- a/lib/gnutls_asn1_tab.c +++ b/lib/gnutls_asn1_tab.c @@ -81,10 +81,13 @@ const asn1_static_node gnutls_asn1_tab[] = { { "name-string", 536879115, NULL }, { NULL, 1073743880, "1"}, { NULL, 27, NULL }, - { "KRB5PrincipalName", 536870917, NULL }, + { "KRB5PrincipalName", 1610612741, NULL }, { "realm", 1610620955, NULL }, { NULL, 2056, "0"}, { "principalName", 536879106, "PrincipalName"}, { NULL, 2056, "1"}, + { "EdDSAParameters", 537133077, NULL }, + { "ed25519", 1073741825, "1"}, + { "ed25519p", 1, "2"}, { NULL, 0, NULL } }; diff --git a/lib/includes/gnutls/abstract.h b/lib/includes/gnutls/abstract.h index 772bd36255..71cba0c59a 100644 --- a/lib/includes/gnutls/abstract.h +++ b/lib/includes/gnutls/abstract.h @@ -134,6 +134,9 @@ int gnutls_pubkey_get_preferred_hash_algorithm(gnutls_pubkey_t key, gnutls_digest_algorithm_t * hash, unsigned int *mand); +int gnutls_pubkey_export_eddsa_raw(gnutls_pubkey_t key, gnutls_ecc_curve_t * curve, + gnutls_datum_t * pub); + #define gnutls_pubkey_get_pk_rsa_raw gnutls_pubkey_export_rsa_raw int gnutls_pubkey_export_rsa_raw(gnutls_pubkey_t key, gnutls_datum_t * m, gnutls_datum_t * e); @@ -415,6 +418,11 @@ int gnutls_privkey_decrypt_data(gnutls_privkey_t key, const gnutls_datum_t * ciphertext, gnutls_datum_t * plaintext); +int gnutls_privkey_export_eddsa_raw(gnutls_privkey_t key, + gnutls_ecc_curve_t * curve, + gnutls_datum_t *pub, + gnutls_datum_t *priv); + int gnutls_privkey_export_rsa_raw(gnutls_privkey_t key, gnutls_datum_t * m, gnutls_datum_t * e, diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index aecf7ebc7c..cc6e146e12 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -676,6 +676,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: Edwards curve Digital signature algorithm. * * Enumeration of different public-key algorithms. */ @@ -685,7 +686,8 @@ typedef enum { GNUTLS_PK_DSA = 2, GNUTLS_PK_DH = 3, GNUTLS_PK_ECDSA = 4, - GNUTLS_PK_ECDHX = 5 + GNUTLS_PK_ECDHX = 5, + GNUTLS_PK_EDDSA = 6 } gnutls_pk_algorithm_t; @@ -726,6 +728,7 @@ const char *gnutls_pk_algorithm_get_name(gnutls_pk_algorithm_t algorithm); * @GNUTLS_SIGN_RSA_SHA3_256: Digital signature algorithm RSA with SHA3-256. * @GNUTLS_SIGN_RSA_SHA3_384: Digital signature algorithm RSA with SHA3-384. * @GNUTLS_SIGN_RSA_SHA3_512: Digital signature algorithm RSA with SHA3-512. + * @GNUTLS_SIGN_EDDSA_SHA512: Digital signature algorithm EdDSA with SHA-512. * * Enumeration of different digital signature algorithms. */ @@ -763,7 +766,8 @@ typedef enum { GNUTLS_SIGN_RSA_SHA3_224 = 28, GNUTLS_SIGN_RSA_SHA3_256 = 29, GNUTLS_SIGN_RSA_SHA3_384 = 30, - GNUTLS_SIGN_RSA_SHA3_512 = 31 + GNUTLS_SIGN_RSA_SHA3_512 = 31, + GNUTLS_SIGN_EDDSA_SHA512 = 32 } gnutls_sign_algorithm_t; /** @@ -775,6 +779,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_ED25519PH: the Ed25519 curve (in prehash mode) * * Enumeration of ECC curves. */ @@ -785,7 +790,8 @@ typedef enum { GNUTLS_ECC_CURVE_SECP384R1, GNUTLS_ECC_CURVE_SECP521R1, GNUTLS_ECC_CURVE_SECP192R1, - GNUTLS_ECC_CURVE_X25519 + GNUTLS_ECC_CURVE_X25519, + GNUTLS_ECC_CURVE_ED25519PH } 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 55ed70666c..0e76add3dd 100644 --- a/lib/nettle/pk.c +++ b/lib/nettle/pk.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -238,7 +239,7 @@ dh_cleanup: break; } - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: { struct ecc_scalar ecc_priv; struct ecc_point ecc_pub; @@ -467,8 +468,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: /* we do EdDSA */ + { + const gnutls_ecc_curve_entry_st *e; + + if (pk_params->flags != GNUTLS_ECC_CURVE_ED25519PH) + 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; @@ -621,8 +656,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: /* we do EdDSA */ + { + const gnutls_ecc_curve_entry_st *e; + + if (pk_params->flags != GNUTLS_ECC_CURVE_ED25519PH) + 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; @@ -770,6 +837,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_ED25519PH: case GNUTLS_ECC_CURVE_X25519: return 1; default: @@ -780,7 +848,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, @@ -896,7 +964,8 @@ wrap_nettle_pk_generate_params(gnutls_pk_algorithm_t algo, break; } case GNUTLS_PK_RSA: - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: + case GNUTLS_PK_EDDSA: ret = 0; break; default: @@ -1041,13 +1110,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); + ret = _gnutls_pk_generate_keys(GNUTLS_PK_ECDSA, curve, ¶ms); if (ret < 0) { return gnutls_assert_val(ret); } @@ -1096,7 +1165,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 @@ -1143,12 +1212,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; @@ -1174,6 +1243,12 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, int ret; unsigned int i; + 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); + } + switch (algo) { case GNUTLS_PK_DSA: #ifdef ENABLE_FIPS140 @@ -1365,7 +1440,7 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, break; } - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: if (params->flags & GNUTLS_PK_FLAG_PROVABLE) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); @@ -1408,6 +1483,44 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, if (ret < 0) goto fail; + break; + } + case GNUTLS_PK_EDDSA: + { + 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_ED25519PH) + 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(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; + + ed25519_sha512_public_key(params->raw_pub.data, params->raw_priv.data); + params->params_nr = 2; + break; } case GNUTLS_PK_ECDHX: @@ -1445,6 +1558,8 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, return GNUTLS_E_INVALID_REQUEST; } + params->algo = algo; + FAIL_IF_LIB_ERROR; return 0; @@ -1588,7 +1703,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; @@ -1661,6 +1776,9 @@ wrap_nettle_pk_verify_priv_params(gnutls_pk_algorithm_t algo, mpz_clear(y2); } break; + case GNUTLS_PK_EDDSA: + ret = 0; + break; default: ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); } @@ -1678,7 +1796,7 @@ wrap_nettle_pk_verify_pub_params(gnutls_pk_algorithm_t algo, case GNUTLS_PK_RSA: case GNUTLS_PK_DSA: 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; @@ -1803,6 +1921,21 @@ wrap_nettle_pk_fixup(gnutls_pk_algorithm_t algo, params->params_nr = RSA_PRIVATE_PARAMS; } + if (direction == GNUTLS_IMPORT && algo == GNUTLS_PK_EDDSA) { + if (params->flags != GNUTLS_ECC_CURVE_ED25519PH) + 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; } diff --git a/lib/pk.c b/lib/pk.c index 182cdcb15f..0996b71492 100644 --- a/lib/pk.c +++ b/lib/pk.c @@ -170,7 +170,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; } @@ -668,6 +668,32 @@ 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; + } + + /* ECDSA */ + /* X */ if (x) { ret = _gnutls_mpi_dprint_lz(params->params[ECC_X], x); @@ -761,7 +787,8 @@ pk_prepare_hash(gnutls_pk_algorithm_t pk, _gnutls_free_datum(&old_digest); break; case GNUTLS_PK_DSA: - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: + case GNUTLS_PK_EDDSA: break; default: gnutls_assert(); diff --git a/lib/pkcs11_privkey.c b/lib/pkcs11_privkey.c index 531f646f5a..5f0f1a2372 100644 --- a/lib/pkcs11_privkey.c +++ b/lib/pkcs11_privkey.c @@ -302,7 +302,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; @@ -891,7 +891,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; @@ -910,7 +910,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 0f63649ec2..9274a036f3 100644 --- a/lib/privkey.c +++ b/lib/privkey.c @@ -134,10 +134,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) @@ -189,7 +189,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]); @@ -202,6 +202,13 @@ privkey_to_pubkey(gnutls_pk_algorithm_t pk, } break; + case GNUTLS_PK_EDDSA: + 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; diff --git a/lib/privkey_raw.c b/lib/privkey_raw.c index 47754831ee..2e17556b2e 100644 --- a/lib/privkey_raw.c +++ b/lib/privkey_raw.c @@ -138,7 +138,8 @@ int ret; * * 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. + * 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 8ff248e2cd..4d14c73547 100644 --- a/lib/pubkey.c +++ b/lib/pubkey.c @@ -50,7 +50,8 @@ int 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: return gnutls_ecc_curve_get_size(params->flags) * 8; default: return 0; @@ -289,12 +290,18 @@ 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) *hash = (gnutls_digest_algorithm_t)me->id; + ret = 0; + break; + case GNUTLS_PK_EDDSA: + if (hash) + *hash = GNUTLS_DIG_SHA512; + ret = 0; break; case GNUTLS_PK_RSA: @@ -954,6 +961,7 @@ gnutls_pubkey_export_dsa_raw(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. * @@ -973,7 +981,7 @@ gnutls_pubkey_export_ecc_raw(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; } @@ -981,6 +989,17 @@ gnutls_pubkey_export_ecc_raw(gnutls_pubkey_t key, if (curve) *curve = key->params.flags; + if (key->pk_algorithm == GNUTLS_PK_EDDSA) { + 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); + } + return 0; + } + + /* ECDSA */ + /* X */ if (x) { ret = _gnutls_mpi_dprint_lz(key->params.params[ECC_X], x); @@ -1395,7 +1414,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. @@ -1420,6 +1440,19 @@ gnutls_pubkey_import_ecc_raw(gnutls_pubkey_t key, key->params.flags = curve; + if (curve_is_eddsa(curve)) { + key->pk_algorithm = GNUTLS_PK_EDDSA; + + ret = _gnutls_set_datum(&key->params.raw_pub, x->data, x->size); + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; + } + + /* ECDSA */ + key->pk_algorithm = GNUTLS_PK_ECDSA; + if (_gnutls_mpi_init_scan_nz (&key->params.params[ECC_X], x->data, x->size)) { gnutls_assert(); @@ -1435,7 +1468,6 @@ gnutls_pubkey_import_ecc_raw(gnutls_pubkey_t key, goto cleanup; } key->params.params_nr++; - key->pk_algorithm = GNUTLS_PK_EC; return 0; @@ -1869,6 +1901,30 @@ dsa_verify_hashed_data(gnutls_pk_algorithm_t pk, return _gnutls_pk_verify(pk, &digest, signature, params); } +static int +eddsa_verify_data(gnutls_pk_algorithm_t pk, + const mac_entry_st * algo, + const gnutls_datum_t * data, + const gnutls_datum_t * signature, + gnutls_pk_params_st * params) +{ + int ret; + uint8_t _digest[MAX_HASH_SIZE]; + gnutls_datum_t digest; + + if (algo == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + ret = _gnutls_hash_fast(algo->id, data->data, data->size, _digest); + if (ret < 0) + return gnutls_assert_val(ret); + + digest.data = _digest; + digest.size = _gnutls_hash_get_algo_len(algo); + + return _gnutls_pk_verify(pk, &digest, signature, params); +} + static int dsa_verify_data(gnutls_pk_algorithm_t pk, const mac_entry_st * algo, @@ -1957,7 +2013,15 @@ pubkey_verify_data(gnutls_pk_algorithm_t pk, return 1; break; - case GNUTLS_PK_EC: + case GNUTLS_PK_EDDSA: + if (eddsa_verify_data(pk, me, data, signature, issuer_params) != 0) { + gnutls_assert(); + return GNUTLS_E_PK_SIG_VERIFY_FAILED; + } + + return 1; + break; + case GNUTLS_PK_ECDSA: case GNUTLS_PK_DSA: if (dsa_verify_data(pk, me, data, signature, issuer_params) != 0) { diff --git a/lib/x509/common.h b/lib/x509/common.h index dc6dccec83..9946bf6b6b 100644 --- a/lib/x509/common.h +++ b/lib/x509/common.h @@ -84,6 +84,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.100" + #define XMPP_OID "1.3.6.1.5.5.7.8.5" #define KRB5_PRINCIPAL_OID "1.3.6.1.5.2.2" diff --git a/lib/x509/key_decode.c b/lib/x509/key_decode.c index 049e93a951..b3ba2cacae 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,60 @@ _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); +} + +/* params as defined in draft-ietf-curdle-pkix-00 + */ +int +_gnutls_x509_read_eddsa_params(uint8_t * der, int dersize, + unsigned int * curve) +{ + int ret; + ASN1_TYPE spk = ASN1_TYPE_EMPTY; + unsigned int cid; + + if ((ret = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.EdDSAParameters", + &spk)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + ret = asn1_der_decoding(&spk, der, dersize, NULL); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + /* read the curve */ + ret = _gnutls_x509_read_uint(spk, "", &cid); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (cid != 2) { + _gnutls_debug_log("EdDSA curve %u is not supported\n", cid); + gnutls_assert(); + ret = GNUTLS_E_ECC_UNSUPPORTED_CURVE; + goto cleanup; + } + + *curve = GNUTLS_ECC_CURVE_ED25519PH; + + ret = 0; + + cleanup: + + asn1_delete_structure(&spk); + + return ret; +} /* reads p,q and g * from the certificate (subjectPublicKey BIT STRING). @@ -247,11 +303,14 @@ int _gnutls_x509_read_pubkey(gnutls_pk_algorithm_t algo, uint8_t * der, if (ret >= 0) 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->params_nr = ECC_PUBLIC_PARAMS; break; + case GNUTLS_PK_EDDSA: + ret = _gnutls_x509_read_eddsa_pubkey(der, dersize, params); + break; default: ret = gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); break; @@ -268,8 +327,10 @@ int _gnutls_x509_read_pubkey_params(gnutls_pk_algorithm_t algo, return 0; case GNUTLS_PK_DSA: return _gnutls_x509_read_dsa_params(der, dersize, params); - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: return _gnutls_x509_read_ecc_params(der, dersize, ¶ms->flags); + case GNUTLS_PK_EDDSA: + return _gnutls_x509_read_eddsa_params(der, dersize, ¶ms->flags); default: return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); } diff --git a/lib/x509/key_encode.c b/lib/x509/key_encode.c index 8ebc9fabc7..acc13492bc 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_ED25519PH) + 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, @@ -144,8 +172,10 @@ _gnutls_x509_write_pubkey_params(gnutls_pk_algorithm_t algo, memcpy(der->data, ASN1_NULL, ASN1_NULL_SIZE); der->size = ASN1_NULL_SIZE; return 0; - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: return _gnutls_x509_write_ecc_params(params->flags, der); + case GNUTLS_PK_EDDSA: + return _gnutls_x509_write_eddsa_params(params->flags, der); default: return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); } @@ -161,13 +191,62 @@ _gnutls_x509_write_pubkey(gnutls_pk_algorithm_t algo, return _gnutls_x509_write_dsa_pubkey(params, der); case GNUTLS_PK_RSA: 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: + return _gnutls_x509_write_eddsa_pubkey(params, der); default: return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); } } +/* + * This function writes the parameters for EdDSA keys. + * That is the EdDSAParameters struct. + * + * Allocates the space used to store the DER data. + */ +int +_gnutls_x509_write_eddsa_params(gnutls_ecc_curve_t curve, + gnutls_datum_t * der) +{ + int result; + ASN1_TYPE spk = ASN1_TYPE_EMPTY; + const uint8_t two = '\x02'; + + der->data = NULL; + der->size = 0; + + if (curve != GNUTLS_ECC_CURVE_ED25519PH) + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + + if ((result = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.EdDSAParameters", &spk)) + != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if ((result = + asn1_write_value(spk, "", &two, 1)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = _gnutls_x509_der_encode(spk, "", der, 0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + asn1_delete_structure(&spk); + return result; +} + /* * This function writes the parameters for DSS keys. * Needs 3 parameters (p,q,g). @@ -249,7 +328,6 @@ _gnutls_x509_write_ecc_params(gnutls_ecc_curve_t curve, if (oid == NULL) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); - if ((result = asn1_create_element (_gnutls_get_gnutls_asn(), "GNUTLS.ECParameters", &spk)) != ASN1_SUCCESS) { @@ -502,19 +580,9 @@ _gnutls_asn1_encode_ecc(ASN1_TYPE * c2, gnutls_pk_params_st * params) 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_x963_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); @@ -536,20 +604,51 @@ _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); - 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, "privateKey", params->raw_priv.data, params->raw_priv.size); + if (ret != 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_x963_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 */ @@ -684,7 +783,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: return _gnutls_asn1_encode_ecc(c2, params); default: return GNUTLS_E_UNIMPLEMENTED_FEATURE; diff --git a/lib/x509/output.c b/lib/x509/output.c index 34f2c70b68..2bfb41d957 100644 --- a/lib/x509/output.c +++ b/lib/x509/output.c @@ -1669,7 +1669,7 @@ static void print_keyid(gnutls_buffer_st * str, gnutls_x509_crt_t cert) if (err < 0) return; - 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/privkey.c b/lib/x509/privkey.c index 4d6a3a32eb..1783b18d32 100644 --- a/lib/x509/privkey.c +++ b/lib/x509/privkey.c @@ -279,7 +279,6 @@ _gnutls_privkey_decode_ecc_key(ASN1_TYPE* pkey_asn, const gnutls_datum_t * raw_k gnutls_pk_params_init(&pkey->params); - pkey->params.algo = GNUTLS_PK_EC; if ((ret = asn1_create_element(_gnutls_get_gnutls_asn(), "GNUTLS.ECPrivateKey", @@ -325,6 +324,7 @@ _gnutls_privkey_decode_ecc_key(ASN1_TYPE* pkey_asn, const gnutls_datum_t * raw_k } pkey->params.flags = gnutls_oid_to_ecc_curve(oid); + curve = pkey->params.flags; if (pkey->params.flags == GNUTLS_ECC_CURVE_INVALID) { _gnutls_debug_log("Curve %s is not supported\n", oid); @@ -336,35 +336,57 @@ _gnutls_privkey_decode_ecc_key(ASN1_TYPE* pkey_asn, const gnutls_datum_t * raw_k pkey->params.flags = curve; } + if (curve_is_eddsa(curve)) { + pkey->pk_algorithm = GNUTLS_PK_EDDSA; + pkey->params.algo = GNUTLS_PK_EDDSA; - /* read the public key */ - ret = _gnutls_x509_read_value(*pkey_asn, "publicKey", &out); - if (ret < 0) { - gnutls_assert(); - goto error; - } + ret = + _gnutls_x509_read_value(*pkey_asn, "privateKey", + &pkey->params.raw_priv); + if (ret < 0) { + gnutls_assert(); + goto error; + } - ret = - _gnutls_ecc_ansi_x963_import(out.data, out.size, - &pkey->params.params[ECC_X], - &pkey->params.params[ECC_Y]); + /* make the public key */ + ret = _gnutls_pk_fixup(GNUTLS_PK_EDDSA, GNUTLS_IMPORT, &pkey->params); + if (ret < 0) { + gnutls_assert(); + goto error; + } + } else { /* Normal EC curves */ + pkey->pk_algorithm = GNUTLS_PK_ECDSA; + pkey->params.algo = GNUTLS_PK_ECDSA; - _gnutls_free_datum(&out); - if (ret < 0) { - gnutls_assert(); - goto error; - } - pkey->params.params_nr += 2; + /* read the public key */ + ret = _gnutls_x509_read_value(*pkey_asn, "publicKey", &out); + if (ret < 0) { + gnutls_assert(); + goto error; + } - /* read the private key */ - ret = - _gnutls_x509_read_key_int(*pkey_asn, "privateKey", - &pkey->params.params[ECC_K]); - if (ret < 0) { - gnutls_assert(); - goto error; + ret = + _gnutls_ecc_ansi_x963_import(out.data, out.size, + &pkey->params.params[ECC_X], + &pkey->params.params[ECC_Y]); + + _gnutls_free_datum(&out); + if (ret < 0) { + gnutls_assert(); + goto error; + } + pkey->params.params_nr += 2; + + /* read the private key */ + ret = + _gnutls_x509_read_key_int(*pkey_asn, "privateKey", + &pkey->params.params[ECC_K]); + if (ret < 0) { + gnutls_assert(); + goto error; + } + pkey->params.params_nr++; } - pkey->params.params_nr++; return 0; @@ -373,10 +395,8 @@ _gnutls_privkey_decode_ecc_key(ASN1_TYPE* pkey_asn, const gnutls_datum_t * raw_k gnutls_pk_params_clear(&pkey->params); gnutls_pk_params_release(&pkey->params); return ret; - } - static ASN1_TYPE decode_dsa_key(const gnutls_datum_t * raw_key, gnutls_x509_privkey_t pkey) { @@ -477,6 +497,7 @@ decode_dsa_key(const gnutls_datum_t * raw_key, gnutls_x509_privkey_t pkey) #define PEM_KEY_DSA_PROVABLE "FIPS186-4 DSA PRIVATE KEY" #define PEM_KEY_RSA_PROVABLE "FIPS186-4 RSA PRIVATE KEY" #define PEM_KEY_ECC "EC PRIVATE KEY" +#define PEM_KEY_EDDSA "EdDSA PRIVATE KEY" #define PEM_KEY_PKCS8 "PRIVATE KEY" #define MAX_PEM_HEADER_SIZE 25 @@ -554,6 +575,7 @@ gnutls_x509_privkey_import(gnutls_x509_privkey_t key, IF_CHECK_FOR(PEM_KEY_RSA, GNUTLS_PK_RSA, ptr, begin_ptr, left, key) else IF_CHECK_FOR(PEM_KEY_ECC, GNUTLS_PK_EC, ptr, begin_ptr, left, key) + else IF_CHECK_FOR(PEM_KEY_EDDSA, GNUTLS_PK_EDDSA, ptr, begin_ptr, left, key) else IF_CHECK_FOR(PEM_KEY_DSA, GNUTLS_PK_DSA, ptr, begin_ptr, left, key) else IF_CHECK_FOR(PEM_KEY_RSA_PROVABLE, GNUTLS_PK_RSA, ptr, begin_ptr, left, key) else IF_CHECK_FOR(PEM_KEY_DSA_PROVABLE, GNUTLS_PK_DSA, ptr, begin_ptr, left, key) @@ -604,7 +626,7 @@ gnutls_x509_privkey_import(gnutls_x509_privkey_t key, key->key = decode_dsa_key(&_data, key); if (key->key == NULL) gnutls_assert(); - } else if (key->pk_algorithm == GNUTLS_PK_EC) { + } else if (key->pk_algorithm == GNUTLS_PK_EC || key->pk_algorithm == GNUTLS_PK_EDDSA) { result = _gnutls_privkey_decode_ecc_key(&key->key, &_data, key, 0); if (result < 0) { gnutls_assert(); @@ -622,7 +644,6 @@ gnutls_x509_privkey_import(gnutls_x509_privkey_t key, key->pk_algorithm = GNUTLS_PK_DSA; key->key = decode_dsa_key(&_data, key); if (key->key == NULL) { - key->pk_algorithm = GNUTLS_PK_EC; result = _gnutls_privkey_decode_ecc_key(&key->key, &_data, key, 0); if (result < 0) { @@ -635,7 +656,6 @@ gnutls_x509_privkey_import(gnutls_x509_privkey_t key, key->key = NULL; } } - } } } @@ -739,9 +759,9 @@ gnutls_x509_privkey_import2(gnutls_x509_privkey_t key, if (ptr != NULL) { left = data->size - ((ptrdiff_t)ptr - (ptrdiff_t)data->data); - if (data->size - left > 15) { - ptr -= 15; - left += 15; + if (data->size - left > MAX_PEM_HEADER_SIZE) { + ptr -= MAX_PEM_HEADER_SIZE; + left += MAX_PEM_HEADER_SIZE; } else { ptr = (char*)data->data; left = data->size; @@ -756,6 +776,7 @@ gnutls_x509_privkey_import2(gnutls_x509_privkey_t key, if (ptr != NULL && left > sizeof(PEM_KEY_RSA)) { if (memcmp(ptr, PEM_KEY_RSA, sizeof(PEM_KEY_RSA)-1) == 0 || memcmp(ptr, PEM_KEY_ECC, sizeof(PEM_KEY_ECC)-1) == 0 || + memcmp(ptr, PEM_KEY_EDDSA, sizeof(PEM_KEY_EDDSA)-1) == 0 || memcmp(ptr, PEM_KEY_DSA, sizeof(PEM_KEY_DSA)-1) == 0) { head_enc = 0; } @@ -1111,6 +1132,25 @@ 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; + + 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; + } + + return 0; + } + + key->params.algo = GNUTLS_PK_ECDSA; if (_gnutls_mpi_init_scan_nz (&key->params.params[ECC_X], x->data, x->size)) { @@ -1136,8 +1176,6 @@ gnutls_x509_privkey_import_ecc_raw(gnutls_x509_privkey_t key, } key->params.params_nr++; - key->pk_algorithm = GNUTLS_PK_EC; - return 0; cleanup: @@ -1212,9 +1250,11 @@ static const char *set_msg(gnutls_x509_privkey_t key) return PEM_KEY_DSA_PROVABLE; else return PEM_KEY_DSA; - } else if (key->pk_algorithm == GNUTLS_PK_EC) + } else if (key->pk_algorithm == GNUTLS_PK_EC) { return PEM_KEY_ECC; - else + } else if (key->pk_algorithm == GNUTLS_PK_EDDSA) { + return PEM_KEY_EDDSA; + } else return "UNKNOWN"; } @@ -1343,6 +1383,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. @@ -1502,7 +1543,8 @@ char* gen_data = NULL; /* Here we don't know the purpose of the key. Check both * signing and encryption. */ - case GNUTLS_PK_EC: /* we only do keys for ECDSA */ + case GNUTLS_PK_EDDSA: + case GNUTLS_PK_ECDSA: case GNUTLS_PK_DSA: ret = _gnutls_pk_sign(algo, &sig, &ddata, params); if (ret < 0) { @@ -1618,11 +1660,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 ca438656b4..4372064fab 100644 --- a/lib/x509/privkey_pkcs8.c +++ b/lib/x509/privkey_pkcs8.c @@ -112,7 +112,8 @@ _encode_privkey(gnutls_x509_privkey_t pkey, gnutls_datum_t * raw) switch (pkey->pk_algorithm) { case GNUTLS_PK_RSA: - case GNUTLS_PK_EC: + case GNUTLS_PK_ECDSA: + case GNUTLS_PK_EDDSA: ret = gnutls_x509_privkey_export2(pkey, GNUTLS_X509_FMT_DER, raw); diff --git a/lib/x509/x509_int.h b/lib/x509/x509_int.h index 782365d8c4..7f82aa5cc7 100644 --- a/lib/x509/x509_int.h +++ b/lib/x509/x509_int.h @@ -227,10 +227,17 @@ 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); +int _gnutls_x509_read_eddsa_params(uint8_t * der, int dersize, unsigned int *curve); + int _gnutls_asn1_encode_privkey(gnutls_pk_algorithm_t pk, ASN1_TYPE * c2, gnutls_pk_params_st * params, unsigned compat); @@ -306,6 +313,12 @@ 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_params(gnutls_ecc_curve_t curve, + 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 0eee218cea..7cba434a1d 100644 --- a/src/certtool-args.def +++ b/src/certtool-args.def @@ -447,6 +447,12 @@ flag = { aliases = ecc; }; +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 = hash; arg-type = string; diff --git a/src/certtool-common.c b/src/certtool-common.c index dab6c59f99..e30882c632 100644 --- a/src/certtool-common.c +++ b/src/certtool-common.c @@ -866,14 +866,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); + } } @@ -1143,7 +1149,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_EC || key_type == GNUTLS_PK_EDDSA) { gnutls_datum_t y, x, k; gnutls_ecc_curve_t curve; diff --git a/src/certtool.c b/src/certtool.c index db8e626164..d0ce96cb5b 100644 --- a/src/certtool.c +++ b/src/certtool.c @@ -132,8 +132,10 @@ generate_private_key_int(common_info_st * cinfo) bits = get_bits(key_type, cinfo->bits, cinfo->sec_param, 1); - fprintf(stdlog, "Generating a %d bit %s private key...\n", - bits, gnutls_pk_algorithm_get_name(key_type)); + if (GNUTLS_BITS_ARE_CURVE(bits)) + fprintf(stdlog, "Generating an %s private key...\n", gnutls_pk_algorithm_get_name(key_type)); + else + fprintf(stdlog, "Generating a %d bit %s private key...\n", bits, gnutls_pk_algorithm_get_name(key_type)); if (bits < 256 && key_type == GNUTLS_PK_EC) fprintf(stderr, @@ -1084,7 +1086,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; else req_key_type = GNUTLS_PK_RSA; diff --git a/tests/cert-tests/Makefile.am b/tests/cert-tests/Makefile.am index 50301d5159..515181519f 100644 --- a/tests/cert-tests/Makefile.am +++ b/tests/cert-tests/Makefile.am @@ -39,7 +39,7 @@ EXTRA_DIST = ca-no-pathlen.pem no-ca-or-pathlen.pem aki-cert.pem \ template-othername-xmpp.tmpl template-othername-xmpp.pem template-krb5name.tmpl \ template-krb5name.pem template-krb5name-full.pem template-test-ecc.key \ template-rsa-sha3-256.pem template-rsa-sha3-512.pem template-rsa-sha3-224.pem template-rsa-sha3-384.pem \ - name-constraints-ip2.pem chain-md5.pem gost-cert.pem + name-constraints-ip2.pem chain-md5.pem gost-cert.pem pkcs7-detached.eddsa.sig dist_check_SCRIPTS = pathlen aki certtool invalid-sig email \ pkcs7 pkcs7-broken-sigs privkey-import name-constraints certtool-long-cn crl provable-privkey @@ -53,7 +53,8 @@ dist_check_SCRIPTS += crq endif if !WINDOWS -dist_check_SCRIPTS += template-test pem-decoding othername-test krb5-test sha3-test md5-test +dist_check_SCRIPTS += template-test pem-decoding othername-test krb5-test sha3-test md5-test \ + ed25519 endif if ENABLE_DANE diff --git a/tests/cert-tests/ed25519 b/tests/cert-tests/ed25519 new file mode 100755 index 0000000000..72b5148672 --- /dev/null +++ b/tests/cert-tests/ed25519 @@ -0,0 +1,82 @@ +#!/bin/sh + +# Copyright (C) 2016 Red Hat, Inc. +# Copyright (C) 2015-2016 Nikos Mavrogiannopoulos +# +# This file is part of GnuTLS. +# +# GnuTLS is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 3 of the License, or (at +# your option) any later version. +# +# GnuTLS is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GnuTLS; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#set -e + +srcdir="${srcdir:-.}" +CERTTOOL="${CERTTOOL:-../../src/certtool${EXEEXT}}" +DIFF="${DIFF:-diff}" +if ! test -z "${VALGRIND}"; then + VALGRIND="${LIBTOOL:-libtool} --mode=execute ${VALGRIND} --error-exitcode=15" +fi +OUTFILE=out-ed25519.tmp +OUTFILE2=out2-ed25519.tmp + +# Verifying and existing sig +FILE="shipped-sig-verify" +${VALGRIND} "${CERTTOOL}" --p7-verify --load-certificate "${srcdir}/../../doc/credentials/x509/cert-eddsa.pem" <"pkcs7-detached.eddsa.sig" +rc=$? +if test "${rc}" != "0"; then + echo "${FILE}: PKCS7-Ed25519 struct signing failed verification" + exit ${rc} +fi + +# Test signing +FILE="signing" +${VALGRIND} "${CERTTOOL}" --p7-sign --load-privkey "${srcdir}/../../doc/credentials/x509/key-eddsa.pem" --load-certificate "${srcdir}/../../doc/credentials/x509/cert-eddsa.pem" --infile "${srcdir}/pkcs7-detached.txt" >"${OUTFILE}" +rc=$? + +if test "${rc}" != "0"; then + echo "${FILE}: PKCS7-Ed25519 struct signing failed" + exit ${rc} +fi + +FILE="signing-verify" +${VALGRIND} "${CERTTOOL}" --p7-verify --load-certificate "${srcdir}/../../doc/credentials/x509/cert-eddsa.pem" <"${OUTFILE}" +rc=$? + +if test "${rc}" != "0"; then + echo "${FILE}: PKCS7-Ed25519 struct signing failed verification" + exit ${rc} +fi + +FILE="signing-detached" +${VALGRIND} "${CERTTOOL}" --p7-detached-sign --load-privkey "${srcdir}/../../doc/credentials/x509/key-eddsa.pem" --load-certificate "${srcdir}/../../doc/credentials/x509/cert-eddsa.pem" --infile "${srcdir}/pkcs7-detached.txt" >"${OUTFILE}" +rc=$? + +if test "${rc}" != "0"; then + echo "${FILE}: PKCS7-Ed25519 struct signing-detached failed" + exit ${rc} +fi + +FILE="signing-detached-verify" +${VALGRIND} "${CERTTOOL}" --p7-verify --load-certificate "${srcdir}/../../doc/credentials/x509/cert-eddsa.pem" --load-data "${srcdir}/pkcs7-detached.txt" <"${OUTFILE}" +rc=$? + +if test "${rc}" != "0"; then + echo "${FILE}: PKCS7-Ed25519 struct signing-detached failed verification" + exit ${rc} +fi + +rm -f "${OUTFILE}" +rm -f "${OUTFILE2}" + +exit 0 diff --git a/tests/cert-tests/pkcs7-detached.eddsa.sig b/tests/cert-tests/pkcs7-detached.eddsa.sig new file mode 100644 index 0000000000..5f48db2822 --- /dev/null +++ b/tests/cert-tests/pkcs7-detached.eddsa.sig @@ -0,0 +1,14 @@ +-----BEGIN PKCS7----- +MIICEQYJKoZIhvcNAQcCoIICAjCCAf4CAQExDTALBglghkgBZQMEAgMwKQYJKoZI +hvcNAQcBoBwEGkhlbGxvIHRoZXJlLiBIb3cgYXJlIHlvdT8KoIIBPTCCATkwgeyg +AwIBAgIIVyDIdBqWBggwBQYDK2VkMBUxEzARBgNVBAMTClRlc3QgRWREU0EwIBcN +MTYwNDI3MTQxMTAxWhgPOTk5OTEyMzEyMzU5NTlaMBUxEzARBgNVBAMTClRlc3Qg +RWREU0EwLTAIBgMrZWQKAQIDIQCk5H6zmyibZ7bTSM8UAYBYhSm/+/Nk5IUNcd8z +yg5bpqNVMFMwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNV +HQ8BAf8EBQMDB4AAMB0GA1UdDgQWBBTgZCbV/Mo6WyF5xYpN3QAtdKOWvzAFBgMr +ZWQDQQDQxdN9sb51PucYzhK7kHtDAS3SL/Ust+KEC6Qwuw/D3s+mzX9PwjMJchHh +NtsAlmLw0TcO7BN+YRXvo9u0rU8PMX4wfAIBATAhMBUxEzARBgNVBAMTClRlc3Qg +RWREU0ECCFcgyHQalgYIMAsGCWCGSAFlAwQCAzAFBgMrZWQEQDgOzyf+REobJlwS +VAHqRBjGY9TfjZQlAaHYWesNM248McIjStGf/jbv/19cKt9EZKekr+gHBORe82DZ +jaDddQU= +-----END PKCS7----- -- cgit v1.2.1