]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
pubkey: handle X25519 and X448 in gnutls_pubkey_import_pkcs11
authorDaniel Kahn Gillmor <dkg@fifthhorseman.net>
Sat, 8 May 2021 00:12:15 +0000 (20:12 -0400)
committerDaniel Kahn Gillmor <dkg@fifthhorseman.net>
Fri, 17 Sep 2021 20:33:07 +0000 (16:33 -0400)
I am not confident in the strings I chose to match on in
ASN1_ETYPE_PRINTABLE_STRING, in that I do not know what registry
I should look this up in.

The *parse_ecc_ecdh_params and *import_ecc_ecdh functions are tweaked
analogs to the eddsa versions of those functions.

Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
lib/pubkey.c

index a1735cf7667c966515b2d25e5105aa2a4132077f..6d00e87876fb6c312130e7bd3f92ae5037c0188e 100644 (file)
@@ -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;