diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2020-03-20 12:37:16 +0000 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2020-03-20 12:37:16 +0000 |
commit | 2bc4d444a309278a3aaea6eaeb05ce48ac99d3c1 (patch) | |
tree | 39e02ae8c8e3f27768798735c0f087998021800c | |
parent | 90466c8542849843821e42bc6fe443b9dea3bf79 (diff) | |
parent | 36fd86a5043cdd5bfaddd8da24c19226e5b9bef1 (diff) | |
download | gnutls-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.asn | 8 | ||||
-rw-r--r-- | lib/gnutls_asn1_tab.c | 3 | ||||
-rw-r--r-- | lib/pkcs11.c | 29 | ||||
-rw-r--r-- | lib/pkcs11_write.c | 15 | ||||
-rw-r--r-- | lib/pubkey.c | 146 | ||||
-rw-r--r-- | tests/pkcs11/pkcs11-ec-privkey-test.c | 42 | ||||
-rw-r--r-- | tests/pkcs11/pkcs11-eddsa-privkey-test.c | 43 |
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); |