summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2020-03-20 12:37:16 +0000
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2020-03-20 12:37:16 +0000
commit2bc4d444a309278a3aaea6eaeb05ce48ac99d3c1 (patch)
tree39e02ae8c8e3f27768798735c0f087998021800c
parent90466c8542849843821e42bc6fe443b9dea3bf79 (diff)
parent36fd86a5043cdd5bfaddd8da24c19226e5b9bef1 (diff)
downloadgnutls-2bc4d444a309278a3aaea6eaeb05ce48ac99d3c1.tar.gz
Merge branch 'eddsa-pkcs11' into 'master'
Add support for loading Ed25519 keys from PKCS#11 and using them Closes #946 See merge request gnutls/gnutls!1200
-rw-r--r--lib/gnutls.asn8
-rw-r--r--lib/gnutls_asn1_tab.c3
-rw-r--r--lib/pkcs11.c29
-rw-r--r--lib/pkcs11_write.c15
-rw-r--r--lib/pubkey.c146
-rw-r--r--tests/pkcs11/pkcs11-ec-privkey-test.c42
-rw-r--r--tests/pkcs11/pkcs11-eddsa-privkey-test.c43
7 files changed, 282 insertions, 4 deletions
diff --git a/lib/gnutls.asn b/lib/gnutls.asn
index b3adae054d..aca39fd296 100644
--- a/lib/gnutls.asn
+++ b/lib/gnutls.asn
@@ -90,6 +90,14 @@ DHParameter ::= SEQUENCE {
privateValueLength INTEGER OPTIONAL
}
+-- From PKCS #11 3.0
+pkcs-11-ec-Parameters ::= CHOICE {
+ --ecParameters ECParameters,
+ oId OBJECT IDENTIFIER,
+ --implicitlyCA NULL,
+ curveName PrintableString
+}
+
-- ECC from RFC5480
ECParameters ::= CHOICE {
namedCurve OBJECT IDENTIFIER
diff --git a/lib/gnutls_asn1_tab.c b/lib/gnutls_asn1_tab.c
index f5c88e1abf..0f56619559 100644
--- a/lib/gnutls_asn1_tab.c
+++ b/lib/gnutls_asn1_tab.c
@@ -58,6 +58,9 @@ const asn1_static_node gnutls_asn1_tab[] = {
{ "prime", 1073741827, NULL },
{ "base", 1073741827, NULL },
{ "privateValueLength", 16387, NULL },
+ { "pkcs-11-ec-Parameters", 1610612754, NULL },
+ { "oId", 1073741836, NULL },
+ { "curveName", 31, NULL },
{ "ECParameters", 1610612754, NULL },
{ "namedCurve", 12, NULL },
{ "ECPrivateKey", 1610612741, NULL },
diff --git a/lib/pkcs11.c b/lib/pkcs11.c
index 2ef0e3e025..8b65212a50 100644
--- a/lib/pkcs11.c
+++ b/lib/pkcs11.c
@@ -1897,6 +1897,35 @@ int pkcs11_read_pubkey(struct ck_function_list *module,
}
break;
+#ifdef HAVE_CKM_EDDSA
+ case CKK_EC_EDWARDS:
+ a[0].type = CKA_EC_PARAMS;
+ a[0].value = tmp1;
+ a[0].value_len = tmp1_size;
+
+ a[1].type = CKA_EC_POINT;
+ a[1].value = tmp2;
+ a[1].value_len = tmp2_size;
+
+ if ((rv = pkcs11_get_attribute_value(module, pks, ctx, a, 2)) ==
+ CKR_OK) {
+
+ pobj->pubkey[0].data = a[0].value;
+ pobj->pubkey[0].size = a[0].value_len;
+
+ pobj->pubkey[1].data = a[1].value;
+ pobj->pubkey[1].size = a[1].value_len;
+
+ pobj->pubkey_size = 2;
+ } else {
+ gnutls_assert();
+
+ ret = pkcs11_rv_to_err(rv);
+ goto cleanup;
+ }
+
+ break;
+#endif
default:
_gnutls_debug_log("requested reading public key of unsupported type %u\n", (unsigned)key_type);
ret = gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
diff --git a/lib/pkcs11_write.c b/lib/pkcs11_write.c
index ab740dcd62..3ce794b076 100644
--- a/lib/pkcs11_write.c
+++ b/lib/pkcs11_write.c
@@ -358,7 +358,7 @@ static int add_pubkey(gnutls_pubkey_t pubkey, struct ck_attribute *a, unsigned *
break;
}
case GNUTLS_PK_EDDSA_ED25519: {
- gnutls_datum_t params;
+ gnutls_datum_t params, ecpoint;
ret =
_gnutls_x509_write_ecc_params(pubkey->params.curve,
@@ -373,9 +373,18 @@ static int add_pubkey(gnutls_pubkey_t pubkey, struct ck_attribute *a, unsigned *
a[*a_val].value_len = params.size;
(*a_val)++;
+ ret = _gnutls_x509_encode_string(ASN1_ETYPE_OCTET_STRING,
+ pubkey->params.raw_pub.data,
+ pubkey->params.raw_pub.size,
+ &ecpoint);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
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[*a_val].value = ecpoint.data;
+ a[*a_val].value_len = ecpoint.size;
(*a_val)++;
break;
}
diff --git a/lib/pubkey.c b/lib/pubkey.c
index eb7fdbaa82..de95a04c37 100644
--- a/lib/pubkey.c
+++ b/lib/pubkey.c
@@ -362,6 +362,141 @@ gnutls_pubkey_get_preferred_hash_algorithm(gnutls_pubkey_t key,
#ifdef ENABLE_PKCS11
+/* The EC_PARAMS attribute can contain either printable string with curve name
+ * or OID defined in RFC 8410 */
+static int
+gnutls_pubkey_parse_ecc_eddsa_params(const gnutls_datum_t *parameters,
+ gnutls_ecc_curve_t *outcurve)
+{
+ gnutls_ecc_curve_t curve = GNUTLS_ECC_CURVE_INVALID;
+ ASN1_TYPE asn1 = ASN1_TYPE_EMPTY;
+ unsigned int etype = ASN1_ETYPE_INVALID;
+ char str[MAX_OID_SIZE];
+ int str_size;
+ int ret;
+
+ ret = asn1_create_element(_gnutls_get_gnutls_asn(),
+ "GNUTLS.pkcs-11-ec-Parameters", &asn1);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ ret = asn1_der_decoding(&asn1, parameters->data, parameters->size,
+ NULL);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ /* Read the type of choice.
+ */
+ str_size = sizeof(str) - 1;
+ ret = asn1_read_value(asn1, "", str, &str_size);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+ str[str_size] = 0;
+
+ /* Convert the choice to enum type */
+ if (strcmp(str, "oId") == 0) {
+ etype = ASN1_ETYPE_OBJECT_ID;
+ } else if (strcmp(str, "curveName") == 0) {
+ etype = ASN1_ETYPE_PRINTABLE_STRING;
+ }
+
+ str_size = sizeof(str) - 1;
+ switch (etype) {
+ case ASN1_ETYPE_OBJECT_ID:
+ ret = asn1_read_value(asn1, "oId", str, &str_size);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ break;
+ }
+
+ curve = gnutls_oid_to_ecc_curve(str);
+ if (curve != GNUTLS_ECC_CURVE_ED25519 &&
+ curve != GNUTLS_ECC_CURVE_ED448) {
+ _gnutls_debug_log("Curve %s is not supported for EdDSA\n", str);
+ gnutls_assert();
+ curve = GNUTLS_ECC_CURVE_INVALID;
+ ret = GNUTLS_E_ECC_UNSUPPORTED_CURVE;
+ break;
+ }
+
+ ret = GNUTLS_E_SUCCESS;
+ break;
+
+ case ASN1_ETYPE_PRINTABLE_STRING:
+ ret = asn1_read_value(asn1, "curveName", str, &str_size);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ break;
+ }
+
+ if (str_size == strlen("edwards25519") &&
+ strncmp(str, "edwards25519", str_size) == 0) {
+ curve = GNUTLS_ECC_CURVE_ED25519;
+ ret = GNUTLS_E_SUCCESS;
+ break;
+ } else if (str_size == strlen("edwards448") &&
+ strncmp(str, "edwards448", str_size) == 0) {
+ curve = GNUTLS_ECC_CURVE_ED448;
+ ret = GNUTLS_E_SUCCESS;
+ break;
+ }
+ /* FALLTHROUGH */
+
+ default:
+ /* Neither of CHOICEs found. Fail */
+ gnutls_assert();
+ ret = GNUTLS_E_ECC_UNSUPPORTED_CURVE;
+ curve = GNUTLS_ECC_CURVE_INVALID;
+ break;
+ }
+
+
+ cleanup:
+ asn1_delete_structure(&asn1);
+ *outcurve = curve;
+ return ret;
+}
+
+
+static int
+gnutls_pubkey_import_ecc_eddsa(gnutls_pubkey_t key,
+ const gnutls_datum_t * parameters,
+ const gnutls_datum_t * ecpoint)
+{
+ int ret;
+
+ gnutls_ecc_curve_t curve = GNUTLS_ECC_CURVE_INVALID;
+ gnutls_datum_t raw_point = {NULL, 0};
+
+ ret = gnutls_pubkey_parse_ecc_eddsa_params(parameters, &curve);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ ret = _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING,
+ ecpoint->data, ecpoint->size,
+ &raw_point, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_free(raw_point.data);
+ return ret;
+ }
+ ret = gnutls_pubkey_import_ecc_raw(key, curve, &raw_point, NULL);
+
+ gnutls_free(raw_point.data);
+ return ret;
+}
+
/**
* gnutls_pubkey_import_pkcs11:
* @key: The public key
@@ -438,6 +573,10 @@ gnutls_pubkey_import_pkcs11(gnutls_pubkey_t key,
ret = gnutls_pubkey_import_ecc_x962(key, &obj->pubkey[0],
&obj->pubkey[1]);
break;
+ case GNUTLS_PK_EDDSA_ED25519:
+ ret = gnutls_pubkey_import_ecc_eddsa(key, &obj->pubkey[0],
+ &obj->pubkey[1]);
+ break;
default:
gnutls_assert();
return GNUTLS_E_UNIMPLEMENTED_FEATURE;
@@ -1416,7 +1555,7 @@ gnutls_pubkey_import_ecc_raw(gnutls_pubkey_t key,
{
int ret;
- if (key == NULL) {
+ if (key == NULL || x == NULL) {
gnutls_assert();
return GNUTLS_E_INVALID_REQUEST;
}
@@ -1454,6 +1593,11 @@ gnutls_pubkey_import_ecc_raw(gnutls_pubkey_t key,
}
/* ECDSA */
+ if (y == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
key->params.curve = curve;
if (_gnutls_mpi_init_scan_nz
diff --git a/tests/pkcs11/pkcs11-ec-privkey-test.c b/tests/pkcs11/pkcs11-ec-privkey-test.c
index c256e6b0d9..1b24c8150f 100644
--- a/tests/pkcs11/pkcs11-ec-privkey-test.c
+++ b/tests/pkcs11/pkcs11-ec-privkey-test.c
@@ -79,6 +79,8 @@ void doit(void)
gnutls_privkey_t pkey;
gnutls_pubkey_t pubkey;
gnutls_pubkey_t pubkey2;
+ gnutls_pubkey_t pubkey3;
+ gnutls_pubkey_t pubkey4;
unsigned i;
bin = softhsm_bin();
@@ -180,8 +182,21 @@ void doit(void)
exit(1);
}
+ /* Write pubkey to the card too */
+ assert(gnutls_pubkey_init(&pubkey) == 0);
+ assert(gnutls_pubkey_import_x509(pubkey, crt, 0) == 0);
+
+ ret = gnutls_pkcs11_copy_pubkey(SOFTHSM_URL, pubkey, "cert", NULL,
+ GNUTLS_KEY_DIGITAL_SIGNATURE |
+ GNUTLS_KEY_KEY_ENCIPHERMENT, 0);
+ if (ret < 0) {
+ fail("gnutls_pkcs11_copy_pubkey: %s\n",
+ gnutls_strerror(ret));
+ }
+
gnutls_x509_crt_deinit(crt);
gnutls_x509_privkey_deinit(key);
+ gnutls_pubkey_deinit(pubkey);
gnutls_pkcs11_set_pin_function(NULL, NULL);
assert(gnutls_privkey_init(&pkey) == 0);
@@ -192,6 +207,31 @@ void doit(void)
exit(1);
}
+ /* Try to read the public key with public key URI */
+ assert(gnutls_pubkey_init(&pubkey3) == 0);
+
+
+ ret =
+ gnutls_pubkey_import_pkcs11_url(pubkey3,
+ SOFTHSM_URL
+ ";object=cert;object-type=public;pin-value="
+ PIN, 0);
+ if (ret < 0) {
+ fail("error in gnutls_pubkey_import_pkcs11_url: %s\n", gnutls_strerror(ret));
+ }
+
+ /* Try to read the public key with certificate URI */
+ assert(gnutls_pubkey_init(&pubkey4) == 0);
+
+ ret =
+ gnutls_pubkey_import_pkcs11_url(pubkey4,
+ SOFTHSM_URL
+ ";object=cert;object-type=cert;pin-value="
+ PIN, 0);
+ if (ret < 0) {
+ fail("error in gnutls_pubkey_import_pkcs11_url: %s\n", gnutls_strerror(ret));
+ }
+
assert(gnutls_pubkey_init(&pubkey) == 0);
assert(gnutls_pubkey_import_privkey(pubkey, pkey, 0, 0) == 0);
@@ -228,6 +268,8 @@ void doit(void)
gnutls_free(s.data);
}
+ gnutls_pubkey_deinit(pubkey4);
+ gnutls_pubkey_deinit(pubkey3);
gnutls_pubkey_deinit(pubkey2);
gnutls_pubkey_deinit(pubkey);
gnutls_privkey_deinit(pkey);
diff --git a/tests/pkcs11/pkcs11-eddsa-privkey-test.c b/tests/pkcs11/pkcs11-eddsa-privkey-test.c
index 5bc653e029..44515da3f4 100644
--- a/tests/pkcs11/pkcs11-eddsa-privkey-test.c
+++ b/tests/pkcs11/pkcs11-eddsa-privkey-test.c
@@ -94,6 +94,8 @@ void doit(void)
gnutls_privkey_t pkey;
gnutls_pubkey_t pubkey;
gnutls_pubkey_t pubkey2;
+ gnutls_pubkey_t pubkey3;
+ gnutls_pubkey_t pubkey4;
unsigned i, sigalgo;
bin = softhsm_bin();
@@ -188,8 +190,21 @@ void doit(void)
gnutls_strerror(ret));
}
+ /* Write pubkey to the card too */
+ assert(gnutls_pubkey_init(&pubkey) == 0);
+ assert(gnutls_pubkey_import_x509(pubkey, crt, 0) == 0);
+
+ ret = gnutls_pkcs11_copy_pubkey(SOFTHSM_URL, pubkey, "cert", NULL,
+ GNUTLS_KEY_DIGITAL_SIGNATURE |
+ GNUTLS_KEY_KEY_ENCIPHERMENT, 0);
+ if (ret < 0) {
+ fail("gnutls_pkcs11_copy_pubkey: %s\n",
+ gnutls_strerror(ret));
+ }
+
gnutls_x509_crt_deinit(crt);
gnutls_x509_privkey_deinit(key);
+ gnutls_pubkey_deinit(pubkey);
gnutls_pkcs11_set_pin_function(NULL, NULL);
assert(gnutls_privkey_init(&pkey) == 0);
@@ -203,6 +218,31 @@ void doit(void)
fail("error in gnutls_privkey_import_pkcs11_url: %s\n", gnutls_strerror(ret));
}
+ /* Try to read the public key with public key URI */
+ assert(gnutls_pubkey_init(&pubkey3) == 0);
+
+
+ ret =
+ gnutls_pubkey_import_pkcs11_url(pubkey3,
+ SOFTHSM_URL
+ ";object=cert;object-type=public;pin-value="
+ PIN, 0);
+ if (ret < 0) {
+ fail("error in gnutls_pubkey_import_pkcs11_url: %s\n", gnutls_strerror(ret));
+ }
+
+ /* Try to read the public key with certificate URI */
+ assert(gnutls_pubkey_init(&pubkey4) == 0);
+
+ ret =
+ gnutls_pubkey_import_pkcs11_url(pubkey4,
+ SOFTHSM_URL
+ ";object=cert;object-type=cert;pin-value="
+ PIN, 0);
+ if (ret < 0) {
+ fail("error in gnutls_pubkey_import_pkcs11_url: %s\n", gnutls_strerror(ret));
+ }
+
assert(gnutls_pubkey_init(&pubkey) == 0);
assert(gnutls_pubkey_import_privkey(pubkey, pkey, 0, 0) == 0);
@@ -241,6 +281,9 @@ void doit(void)
gnutls_free(sig.data);
}
+ /* TODO is there any sensible way to check the pubkeys are the same? */
+ gnutls_pubkey_deinit(pubkey4);
+ gnutls_pubkey_deinit(pubkey3);
gnutls_pubkey_deinit(pubkey2);
gnutls_pubkey_deinit(pubkey);
gnutls_privkey_deinit(pkey);