From 88377775a3eff679a9ec60ab9bfc6b3c683a0407 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Thu, 25 Oct 2018 10:03:01 -0400 Subject: Add support for EDDSA/Ed25519 object support via PKCS#11 Tested with softHSM 2.5.0 Resolves #417 Signed-off-by: Simo Sorce Signed-off-by: Nikos Mavrogiannopoulos --- lib/pkcs11_int.h | 9 ++ lib/pkcs11_privkey.c | 23 +++ lib/pkcs11_write.c | 48 ++++++ src/p11tool-args.def | 2 +- tests/Makefile.am | 1 + tests/pkcs11/pkcs11-eddsa-privkey-test.c | 251 +++++++++++++++++++++++++++++++ tests/pkcs11/tls-neg-pkcs11-key.c | 28 +++- tests/testpkcs11.sh | 36 +++++ 8 files changed, 394 insertions(+), 4 deletions(-) create mode 100644 tests/pkcs11/pkcs11-eddsa-privkey-test.c diff --git a/lib/pkcs11_int.h b/lib/pkcs11_int.h index 76c09b460a..9c81f4e19d 100644 --- a/lib/pkcs11_int.h +++ b/lib/pkcs11_int.h @@ -226,6 +226,8 @@ static inline int pk_to_mech(gnutls_pk_algorithm_t pk) return CKM_RSA_PKCS; else if (pk == GNUTLS_PK_RSA_PSS) return CKM_RSA_PKCS_PSS; + else if (pk == GNUTLS_PK_EDDSA_ED25519) + return CKM_EDDSA; else return -1; } @@ -238,6 +240,8 @@ static inline int pk_to_key_type(gnutls_pk_algorithm_t pk) return CKK_ECDSA; else if (pk == GNUTLS_PK_RSA_PSS || pk == GNUTLS_PK_RSA) return CKK_RSA; + else if (pk == GNUTLS_PK_EDDSA_ED25519) + return CKK_EC_EDWARDS; else return -1; } @@ -250,6 +254,8 @@ static inline gnutls_pk_algorithm_t key_type_to_pk(ck_key_type_t m) return GNUTLS_PK_DSA; else if (m == CKK_ECDSA) return GNUTLS_PK_EC; + else if (m == CKK_EC_EDWARDS) + return GNUTLS_PK_EDDSA_ED25519; else return GNUTLS_PK_UNKNOWN; } @@ -265,6 +271,9 @@ static inline int pk_to_genmech(gnutls_pk_algorithm_t pk, ck_key_type_t *type) } else if (pk == GNUTLS_PK_RSA_PSS || pk == GNUTLS_PK_RSA) { *type = CKK_RSA; return CKM_RSA_PKCS_KEY_PAIR_GEN; + } else if (pk == GNUTLS_PK_EDDSA_ED25519) { + *type = CKK_EC_EDWARDS; + return CKM_EDDSA; } else { *type = -1; return -1; diff --git a/lib/pkcs11_privkey.c b/lib/pkcs11_privkey.c index b721ed1252..f643a69a66 100644 --- a/lib/pkcs11_privkey.c +++ b/lib/pkcs11_privkey.c @@ -1071,6 +1071,29 @@ gnutls_pkcs11_privkey_generate3(const char *url, gnutls_pk_algorithm_t pk, goto cleanup; } + a[a_val].type = CKA_EC_PARAMS; + a[a_val].value = der.data; + a[a_val].value_len = der.size; + a_val++; + + break; + case GNUTLS_PK_EDDSA_ED25519: + p[p_val].type = CKA_SIGN; + p[p_val].value = (void *) &tval; + p[p_val].value_len = sizeof(tval); + p_val++; + + a[a_val].type = CKA_VERIFY; + a[a_val].value = (void *) &tval; + a[a_val].value_len = sizeof(tval); + a_val++; + + ret = _gnutls_x509_write_ecc_params(GNUTLS_ECC_CURVE_ED25519, &der); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + a[a_val].type = CKA_EC_PARAMS; a[a_val].value = der.data; a[a_val].value_len = der.size; diff --git a/lib/pkcs11_write.c b/lib/pkcs11_write.c index cb5b65d508..07dd98e9c6 100644 --- a/lib/pkcs11_write.c +++ b/lib/pkcs11_write.c @@ -357,6 +357,29 @@ static int add_pubkey(gnutls_pubkey_t pubkey, struct ck_attribute *a, unsigned * (*a_val)++; break; } + case GNUTLS_PK_EDDSA_ED25519: { + gnutls_datum_t params; + + ret = + _gnutls_x509_write_ecc_params(pubkey->params.curve, + ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + a[*a_val].type = CKA_EC_PARAMS; + a[*a_val].value = params.data; + a[*a_val].value_len = params.size; + (*a_val)++; + + a[*a_val].type = CKA_EC_POINT; + a[*a_val].value = pubkey->params.raw_pub.data; + a[*a_val].value_len = pubkey->params.raw_pub.size; + (*a_val)++; + break; + } + default: _gnutls_debug_log("requested writing public key of unsupported type %u\n", (unsigned)pk); return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); @@ -918,6 +941,30 @@ gnutls_pkcs11_copy_x509_privkey2(const char *token_url, a[a_val].value_len = x.size; a_val++; + break; + } + case GNUTLS_PK_EDDSA_ED25519: + { + ret = + _gnutls_x509_write_ecc_params(key->params.curve, + &p); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + type = CKK_EC_EDWARDS; + + a[a_val].type = CKA_EC_PARAMS; + a[a_val].value = p.data; + a[a_val].value_len = p.size; + a_val++; + + a[a_val].type = CKA_VALUE; + a[a_val].value = key->params.raw_priv.data; + a[a_val].value_len = key->params.raw_priv.size; + a_val++; + break; } default: @@ -966,6 +1013,7 @@ gnutls_pkcs11_copy_x509_privkey2(const char *token_url, break; } case GNUTLS_PK_EC: + case GNUTLS_PK_EDDSA_ED25519: { gnutls_free(p.data); gnutls_free(x.data); diff --git a/src/p11tool-args.def b/src/p11tool-args.def index 8477a4ddac..bd7789e017 100644 --- a/src/p11tool-args.def +++ b/src/p11tool-args.def @@ -191,7 +191,7 @@ flag = { arg-type = string; descrip = "Generate private-public key pair of given type"; doc = "Generates a private-public key pair in the specified token. -Acceptable types are RSA, ECDSA, and DSA. Should be combined with --sec-param or --bits."; +Acceptable types are RSA, ECDSA, Ed25519, and DSA. Should be combined with --sec-param or --bits."; }; flag = { diff --git a/tests/Makefile.am b/tests/Makefile.am index b8a7b66729..45192203e1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -368,6 +368,7 @@ ctests += pkcs11-cert-import-url-exts pkcs11-get-exts pkcs11-get-raw-issuer-exts pkcs11-privkey-fork-reinit pkcs11-mechanisms pkcs11-privkey-safenet-always-auth \ pkcs11/pkcs11-rsa-pss-privkey-test pkcs11/tls-neg-pkcs11-key pkcs11/pkcs11-privkey-generate \ pkcs11/gnutls_x509_crt_list_import_url pkcs11/gnutls_pcert_list_import_x509_file \ + pkcs11/pkcs11-eddsa-privkey-test \ pkcs11-token-raw pkcs11-obj-raw if P11KIT_0_23_11_API diff --git a/tests/pkcs11/pkcs11-eddsa-privkey-test.c b/tests/pkcs11/pkcs11-eddsa-privkey-test.c new file mode 100644 index 0000000000..4be8847dd9 --- /dev/null +++ b/tests/pkcs11/pkcs11-eddsa-privkey-test.c @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2018 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos, Simo Sorce + * + * 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 Lesser General Public License + * along with this program. If not, see + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../utils.h" +#include "softhsm.h" + +#define CONFIG_NAME "softhsm-privkey-eddsa-test" +#define CONFIG CONFIG_NAME".config" + +/* Tests whether signing with PKCS#11 and EDDSA would + * generate valid signatures */ + +#include "../cert-common.h" + +#define PIN "1234" + +static const gnutls_datum_t testdata = { (void *)"test test", 9 }; + +static void tls_log_func(int level, const char *str) +{ + fprintf(stderr, "|<%d>| %s", level, str); +} + +static +int pin_func(void *userdata, int attempt, const char *url, const char *label, + unsigned flags, char *pin, size_t pin_max) +{ + if (attempt == 0) { + strcpy(pin, PIN); + return 0; + } + return -1; +} + +#define myfail(fmt, ...) \ + fail("%s (iter %d): "fmt, gnutls_sign_get_name(sigalgo), i, ##__VA_ARGS__) + +static unsigned verify_eddsa_presence(void) +{ + unsigned i; + unsigned long mechanism; + int ret; + + i = 0; + do { + ret = gnutls_pkcs11_token_get_mechanism("pkcs11:", i++, &mechanism); + if (ret >= 0 && mechanism == 0x1057 /* CKM_EDDSA */) + return 1; + } while(ret>=0); + + return 0; +} + +void doit(void) +{ + char buf[128]; + int ret; + const char *lib, *bin; + gnutls_x509_crt_t crt; + gnutls_x509_privkey_t key; + gnutls_datum_t tmp, sig; + gnutls_privkey_t pkey; + gnutls_pubkey_t pubkey; + gnutls_pubkey_t pubkey2; + unsigned i, sigalgo; + + bin = softhsm_bin(); + + lib = softhsm_lib(); + + ret = global_init(); + if (ret != 0) { + fail("%d: %s\n", ret, gnutls_strerror(ret)); + } + + gnutls_pkcs11_set_pin_function(pin_func, NULL); + gnutls_global_set_log_function(tls_log_func); + if (debug) + gnutls_global_set_log_level(4711); + + set_softhsm_conf(CONFIG); + snprintf(buf, sizeof(buf), + "%s --init-token --slot 0 --label test --so-pin " PIN " --pin " + PIN, bin); + system(buf); + + ret = gnutls_pkcs11_add_provider(lib, NULL); + if (ret < 0) { + fail("gnutls_x509_crt_init: %s\n", gnutls_strerror(ret)); + } + + if (verify_eddsa_presence() == 0) { + fprintf(stderr, "Skipping test as no EDDSA mech is supported\n"); + exit(77); + } + + ret = gnutls_x509_crt_init(&crt); + if (ret < 0) + fail("gnutls_x509_crt_init: %s\n", gnutls_strerror(ret)); + + ret = + gnutls_x509_crt_import(crt, &server_ca3_eddsa_cert, + GNUTLS_X509_FMT_PEM); + if (ret < 0) + fail("gnutls_x509_crt_import: %s\n", gnutls_strerror(ret)); + + if (debug) { + gnutls_x509_crt_print(crt, GNUTLS_CRT_PRINT_ONELINE, &tmp); + + printf("\tCertificate: %.*s\n", tmp.size, tmp.data); + gnutls_free(tmp.data); + } + + ret = gnutls_x509_privkey_init(&key); + if (ret < 0) { + fail("gnutls_x509_privkey_init: %s\n", gnutls_strerror(ret)); + } + + ret = + gnutls_x509_privkey_import(key, &server_ca3_eddsa_key, + GNUTLS_X509_FMT_PEM); + if (ret < 0) { + fail("gnutls_x509_privkey_import: %s\n", gnutls_strerror(ret)); + } + + /* initialize softhsm token */ + ret = gnutls_pkcs11_token_init(SOFTHSM_URL, PIN, "test"); + if (ret < 0) { + fail("gnutls_pkcs11_token_init: %s\n", gnutls_strerror(ret)); + } + + ret = + gnutls_pkcs11_token_set_pin(SOFTHSM_URL, NULL, PIN, + GNUTLS_PIN_USER); + if (ret < 0) { + fail("gnutls_pkcs11_token_set_pin: %s\n", gnutls_strerror(ret)); + } + + ret = gnutls_pkcs11_copy_x509_crt(SOFTHSM_URL, crt, "cert", + GNUTLS_PKCS11_OBJ_FLAG_MARK_PRIVATE | + GNUTLS_PKCS11_OBJ_FLAG_LOGIN); + if (ret < 0) { + fail("gnutls_pkcs11_copy_x509_crt: %s\n", gnutls_strerror(ret)); + } + + ret = + gnutls_pkcs11_copy_x509_privkey(SOFTHSM_URL, key, "cert", + GNUTLS_KEY_DIGITAL_SIGNATURE | + GNUTLS_KEY_KEY_ENCIPHERMENT, + GNUTLS_PKCS11_OBJ_FLAG_MARK_PRIVATE + | + GNUTLS_PKCS11_OBJ_FLAG_MARK_SENSITIVE + | GNUTLS_PKCS11_OBJ_FLAG_LOGIN); + if (ret < 0) { + fail("gnutls_pkcs11_copy_x509_privkey: %s\n", + gnutls_strerror(ret)); + } + + gnutls_x509_crt_deinit(crt); + gnutls_x509_privkey_deinit(key); + gnutls_pkcs11_set_pin_function(NULL, NULL); + + assert(gnutls_privkey_init(&pkey) == 0); + + ret = + gnutls_privkey_import_pkcs11_url(pkey, + SOFTHSM_URL + ";object=cert;object-type=private;pin-value=" + PIN); + if (ret < 0) { + fail("error in gnutls_privkey_import_pkcs11_url: %s\n", gnutls_strerror(ret)); + } + + assert(gnutls_pubkey_init(&pubkey) == 0); + assert(gnutls_pubkey_import_privkey(pubkey, pkey, 0, 0) == 0); + + assert(gnutls_pubkey_init(&pubkey2) == 0); + assert(gnutls_pubkey_import_x509_raw + (pubkey2, &server_ca3_eddsa_cert, GNUTLS_X509_FMT_PEM, 0) == 0); + + /* this is the algorithm supported by the certificate */ + sigalgo = GNUTLS_SIGN_EDDSA_ED25519; + + for (i = 0; i < 20; i++) { + /* check whether privkey and pubkey are operational + * by signing and verifying */ + ret = + gnutls_privkey_sign_data2(pkey, sigalgo, 0, + &testdata, &sig); + if (ret < 0) + myfail("Error signing data %s\n", gnutls_strerror(ret)); + + /* verify against the pubkey in PKCS #11 */ + ret = + gnutls_pubkey_verify_data2(pubkey, sigalgo, 0, + &testdata, &sig); + if (ret < 0) + myfail("Error verifying data1: %s\n", + gnutls_strerror(ret)); + + /* verify against the raw pubkey */ + ret = + gnutls_pubkey_verify_data2(pubkey2, sigalgo, 0, + &testdata, &sig); + if (ret < 0) + myfail("Error verifying data2: %s\n", + gnutls_strerror(ret)); + + gnutls_free(sig.data); + } + + gnutls_pubkey_deinit(pubkey2); + gnutls_pubkey_deinit(pubkey); + gnutls_privkey_deinit(pkey); + + gnutls_global_deinit(); + + remove(CONFIG); +} diff --git a/tests/pkcs11/tls-neg-pkcs11-key.c b/tests/pkcs11/tls-neg-pkcs11-key.c index c32dee27a6..78bfc6a763 100644 --- a/tests/pkcs11/tls-neg-pkcs11-key.c +++ b/tests/pkcs11/tls-neg-pkcs11-key.c @@ -56,6 +56,22 @@ static void tls_log_func(int level, const char *str) #define testfail(fmt, ...) \ fail("%s: "fmt, name, ##__VA_ARGS__) +static unsigned verify_eddsa_presence(void) +{ + unsigned i; + unsigned long mechanism; + int ret; + + i = 0; + do { + ret = gnutls_pkcs11_token_get_mechanism("pkcs11:", i++, &mechanism); + if (ret >= 0 && mechanism == 0x1057 /* CKM_EDDSA */) + return 1; + } while(ret>=0); + + return 0; +} + static gnutls_privkey_t load_virt_privkey(const char *name, const gnutls_datum_t *txtkey, int exp_key_err) { gnutls_privkey_t privkey; @@ -243,6 +259,7 @@ typedef struct test_st { gnutls_kx_algorithm_t exp_kx; int exp_key_err; int exp_serv_err; + int needs_eddsa; unsigned requires_pkcs11_pss; } test_st; @@ -292,13 +309,13 @@ static const test_st tests[] = { .exp_kx = GNUTLS_KX_ECDHE_RSA, .exp_serv_err = GNUTLS_E_NO_CIPHER_SUITES }, - {.name = "tls1.2: ed25519 cert, ed25519 key", /* we cannot import that key */ + {.name = "tls1.2: ed25519 cert, ed25519 key", .pk = GNUTLS_PK_EDDSA_ED25519, + .needs_eddsa = 1, .prio = "NORMAL:+ECDHE-RSA:+ECDHE-ECDSA", .cert = &server_ca3_eddsa_cert, .key = &server_ca3_eddsa_key, .exp_kx = GNUTLS_KX_ECDHE_RSA, - .exp_key_err = GNUTLS_E_INVALID_REQUEST } }; @@ -322,7 +339,7 @@ void doit(void) gnutls_privkey_t privkey; const char *bin, *lib; char buf[512]; - unsigned int i; + unsigned int i, have_eddsa; int ret; #ifdef _WIN32 @@ -351,7 +368,12 @@ void doit(void) gnutls_strerror(ret)); } + have_eddsa = verify_eddsa_presence(); + for (i=0;i>"${LOGFILE}" 2>&1 + if test $? = 0; then + echo ok + else + echo failed + exit 1 + fi +} + # $1: token # $2: PIN delete_temp_privkey () { @@ -452,6 +469,10 @@ import_temp_ecc_privkey () { import_privkey ECC temp-ecc --ecc $@ } +import_temp_ed25519_privkey () { + import_privkey ed25519 temp-ed25519 --key-type ed25519 $@ +} + import_temp_dsa_privkey () { import_privkey DSA temp-dsa --dsa $@ } @@ -1024,6 +1045,11 @@ if test "x${TOKEN}" = x; then exit_error fi +${P11TOOL} ${ADDITIONAL_PARAM} --list-machanisms ${TOKEN}|grep 25519 >/dev/null +if test $? = 0;then + have_ed25519=1 +fi + reset_pins "${TOKEN}" "${GNUTLS_PIN}" "${GNUTLS_SO_PIN}" #write a given privkey @@ -1038,6 +1064,11 @@ delete_temp_privkey "${TOKEN}" "${GNUTLS_PIN}" ecc-no-256 generate_temp_ecc_privkey "${TOKEN}" "${GNUTLS_PIN}" 384 delete_temp_privkey "${TOKEN}" "${GNUTLS_PIN}" ecc-384 +if test $have_ed25519 != 0;then + generate_temp_ed25519_privkey "${TOKEN}" "${GNUTLS_PIN}" ed25519 + delete_temp_privkey "${TOKEN}" "${GNUTLS_PIN}" ed25519 +fi + generate_temp_rsa_privkey "${TOKEN}" "${GNUTLS_PIN}" 2048 delete_temp_privkey "${TOKEN}" "${GNUTLS_PIN}" rsa-2048 @@ -1051,6 +1082,11 @@ delete_temp_privkey "${TOKEN}" "${GNUTLS_PIN}" ecc-256 import_temp_dsa_privkey "${TOKEN}" "${GNUTLS_PIN}" 2048 delete_temp_privkey "${TOKEN}" "${GNUTLS_PIN}" dsa-2048 +if test $have_ed25519 != 0;then + import_temp_ed25519_privkey "${TOKEN}" "${GNUTLS_PIN}" ed25519 + delete_temp_privkey "${TOKEN}" "${GNUTLS_PIN}" ed25519 +fi + generate_rsa_privkey "${TOKEN}" "${GNUTLS_PIN}" 1024 change_id_of_privkey "${TOKEN}" "${GNUTLS_PIN}" export_pubkey_of_privkey "${TOKEN}" "${GNUTLS_PIN}" -- cgit v1.2.1