diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/pubkey.c | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/lib/pubkey.c b/lib/pubkey.c index a1735cf766..6d00e87876 100644 --- a/lib/pubkey.c +++ b/lib/pubkey.c @@ -497,6 +497,139 @@ gnutls_pubkey_import_ecc_eddsa(gnutls_pubkey_t key, return ret; } +/* Same as above, but for Edwards key agreement */ +static int +gnutls_pubkey_parse_ecc_ecdh_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_X25519 && + curve != GNUTLS_ECC_CURVE_X448) { + _gnutls_debug_log("Curve %s is not supported for Edwards-based key agreement\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("x25519") && + strncmp(str, "x25519", str_size) == 0) { + curve = GNUTLS_ECC_CURVE_X25519; + ret = GNUTLS_E_SUCCESS; + break; + } else if (str_size == strlen("x448") && + strncmp(str, "x448", str_size) == 0) { + curve = GNUTLS_ECC_CURVE_X448; + 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_ecdh(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_ecdh_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 @@ -577,6 +710,10 @@ gnutls_pubkey_import_pkcs11(gnutls_pubkey_t key, ret = gnutls_pubkey_import_ecc_eddsa(key, &obj->pubkey[0], &obj->pubkey[1]); break; + case GNUTLS_PK_ECDH_X25519: + ret = gnutls_pubkey_import_ecc_ecdh(key, &obj->pubkey[0], + &obj->pubkey[1]); + break; default: gnutls_assert(); return GNUTLS_E_UNIMPLEMENTED_FEATURE; |