162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2021 IBM Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <crypto/internal/akcipher.h>
862306a36Sopenharmony_ci#include <crypto/internal/ecc.h>
962306a36Sopenharmony_ci#include <crypto/akcipher.h>
1062306a36Sopenharmony_ci#include <crypto/ecdh.h>
1162306a36Sopenharmony_ci#include <linux/asn1_decoder.h>
1262306a36Sopenharmony_ci#include <linux/scatterlist.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "ecdsasignature.asn1.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistruct ecc_ctx {
1762306a36Sopenharmony_ci	unsigned int curve_id;
1862306a36Sopenharmony_ci	const struct ecc_curve *curve;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	bool pub_key_set;
2162306a36Sopenharmony_ci	u64 x[ECC_MAX_DIGITS]; /* pub key x and y coordinates */
2262306a36Sopenharmony_ci	u64 y[ECC_MAX_DIGITS];
2362306a36Sopenharmony_ci	struct ecc_point pub_key;
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistruct ecdsa_signature_ctx {
2762306a36Sopenharmony_ci	const struct ecc_curve *curve;
2862306a36Sopenharmony_ci	u64 r[ECC_MAX_DIGITS];
2962306a36Sopenharmony_ci	u64 s[ECC_MAX_DIGITS];
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci * Get the r and s components of a signature from the X509 certificate.
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_cistatic int ecdsa_get_signature_rs(u64 *dest, size_t hdrlen, unsigned char tag,
3662306a36Sopenharmony_ci				  const void *value, size_t vlen, unsigned int ndigits)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	size_t keylen = ndigits * sizeof(u64);
3962306a36Sopenharmony_ci	ssize_t diff = vlen - keylen;
4062306a36Sopenharmony_ci	const char *d = value;
4162306a36Sopenharmony_ci	u8 rs[ECC_MAX_BYTES];
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	if (!value || !vlen)
4462306a36Sopenharmony_ci		return -EINVAL;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	/* diff = 0: 'value' has exacly the right size
4762306a36Sopenharmony_ci	 * diff > 0: 'value' has too many bytes; one leading zero is allowed that
4862306a36Sopenharmony_ci	 *           makes the value a positive integer; error on more
4962306a36Sopenharmony_ci	 * diff < 0: 'value' is missing leading zeros, which we add
5062306a36Sopenharmony_ci	 */
5162306a36Sopenharmony_ci	if (diff > 0) {
5262306a36Sopenharmony_ci		/* skip over leading zeros that make 'value' a positive int */
5362306a36Sopenharmony_ci		if (*d == 0) {
5462306a36Sopenharmony_ci			vlen -= 1;
5562306a36Sopenharmony_ci			diff--;
5662306a36Sopenharmony_ci			d++;
5762306a36Sopenharmony_ci		}
5862306a36Sopenharmony_ci		if (diff)
5962306a36Sopenharmony_ci			return -EINVAL;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci	if (-diff >= keylen)
6262306a36Sopenharmony_ci		return -EINVAL;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if (diff) {
6562306a36Sopenharmony_ci		/* leading zeros not given in 'value' */
6662306a36Sopenharmony_ci		memset(rs, 0, -diff);
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	memcpy(&rs[-diff], d, vlen);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	ecc_swap_digits((u64 *)rs, dest, ndigits);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return 0;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ciint ecdsa_get_signature_r(void *context, size_t hdrlen, unsigned char tag,
7762306a36Sopenharmony_ci			  const void *value, size_t vlen)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct ecdsa_signature_ctx *sig = context;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	return ecdsa_get_signature_rs(sig->r, hdrlen, tag, value, vlen,
8262306a36Sopenharmony_ci				      sig->curve->g.ndigits);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ciint ecdsa_get_signature_s(void *context, size_t hdrlen, unsigned char tag,
8662306a36Sopenharmony_ci			  const void *value, size_t vlen)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct ecdsa_signature_ctx *sig = context;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return ecdsa_get_signature_rs(sig->s, hdrlen, tag, value, vlen,
9162306a36Sopenharmony_ci				      sig->curve->g.ndigits);
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic int _ecdsa_verify(struct ecc_ctx *ctx, const u64 *hash, const u64 *r, const u64 *s)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	const struct ecc_curve *curve = ctx->curve;
9762306a36Sopenharmony_ci	unsigned int ndigits = curve->g.ndigits;
9862306a36Sopenharmony_ci	u64 s1[ECC_MAX_DIGITS];
9962306a36Sopenharmony_ci	u64 u1[ECC_MAX_DIGITS];
10062306a36Sopenharmony_ci	u64 u2[ECC_MAX_DIGITS];
10162306a36Sopenharmony_ci	u64 x1[ECC_MAX_DIGITS];
10262306a36Sopenharmony_ci	u64 y1[ECC_MAX_DIGITS];
10362306a36Sopenharmony_ci	struct ecc_point res = ECC_POINT_INIT(x1, y1, ndigits);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	/* 0 < r < n  and 0 < s < n */
10662306a36Sopenharmony_ci	if (vli_is_zero(r, ndigits) || vli_cmp(r, curve->n, ndigits) >= 0 ||
10762306a36Sopenharmony_ci	    vli_is_zero(s, ndigits) || vli_cmp(s, curve->n, ndigits) >= 0)
10862306a36Sopenharmony_ci		return -EBADMSG;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/* hash is given */
11162306a36Sopenharmony_ci	pr_devel("hash : %016llx %016llx ... %016llx\n",
11262306a36Sopenharmony_ci		 hash[ndigits - 1], hash[ndigits - 2], hash[0]);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* s1 = (s^-1) mod n */
11562306a36Sopenharmony_ci	vli_mod_inv(s1, s, curve->n, ndigits);
11662306a36Sopenharmony_ci	/* u1 = (hash * s1) mod n */
11762306a36Sopenharmony_ci	vli_mod_mult_slow(u1, hash, s1, curve->n, ndigits);
11862306a36Sopenharmony_ci	/* u2 = (r * s1) mod n */
11962306a36Sopenharmony_ci	vli_mod_mult_slow(u2, r, s1, curve->n, ndigits);
12062306a36Sopenharmony_ci	/* res = u1*G + u2 * pub_key */
12162306a36Sopenharmony_ci	ecc_point_mult_shamir(&res, u1, &curve->g, u2, &ctx->pub_key, curve);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/* res.x = res.x mod n (if res.x > order) */
12462306a36Sopenharmony_ci	if (unlikely(vli_cmp(res.x, curve->n, ndigits) == 1))
12562306a36Sopenharmony_ci		/* faster alternative for NIST p384, p256 & p192 */
12662306a36Sopenharmony_ci		vli_sub(res.x, res.x, curve->n, ndigits);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (!vli_cmp(res.x, r, ndigits))
12962306a36Sopenharmony_ci		return 0;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	return -EKEYREJECTED;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/*
13562306a36Sopenharmony_ci * Verify an ECDSA signature.
13662306a36Sopenharmony_ci */
13762306a36Sopenharmony_cistatic int ecdsa_verify(struct akcipher_request *req)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
14062306a36Sopenharmony_ci	struct ecc_ctx *ctx = akcipher_tfm_ctx(tfm);
14162306a36Sopenharmony_ci	size_t keylen = ctx->curve->g.ndigits * sizeof(u64);
14262306a36Sopenharmony_ci	struct ecdsa_signature_ctx sig_ctx = {
14362306a36Sopenharmony_ci		.curve = ctx->curve,
14462306a36Sopenharmony_ci	};
14562306a36Sopenharmony_ci	u8 rawhash[ECC_MAX_BYTES];
14662306a36Sopenharmony_ci	u64 hash[ECC_MAX_DIGITS];
14762306a36Sopenharmony_ci	unsigned char *buffer;
14862306a36Sopenharmony_ci	ssize_t diff;
14962306a36Sopenharmony_ci	int ret;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (unlikely(!ctx->pub_key_set))
15262306a36Sopenharmony_ci		return -EINVAL;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	buffer = kmalloc(req->src_len + req->dst_len, GFP_KERNEL);
15562306a36Sopenharmony_ci	if (!buffer)
15662306a36Sopenharmony_ci		return -ENOMEM;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	sg_pcopy_to_buffer(req->src,
15962306a36Sopenharmony_ci		sg_nents_for_len(req->src, req->src_len + req->dst_len),
16062306a36Sopenharmony_ci		buffer, req->src_len + req->dst_len, 0);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	ret = asn1_ber_decoder(&ecdsasignature_decoder, &sig_ctx,
16362306a36Sopenharmony_ci			       buffer, req->src_len);
16462306a36Sopenharmony_ci	if (ret < 0)
16562306a36Sopenharmony_ci		goto error;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* if the hash is shorter then we will add leading zeros to fit to ndigits */
16862306a36Sopenharmony_ci	diff = keylen - req->dst_len;
16962306a36Sopenharmony_ci	if (diff >= 0) {
17062306a36Sopenharmony_ci		if (diff)
17162306a36Sopenharmony_ci			memset(rawhash, 0, diff);
17262306a36Sopenharmony_ci		memcpy(&rawhash[diff], buffer + req->src_len, req->dst_len);
17362306a36Sopenharmony_ci	} else if (diff < 0) {
17462306a36Sopenharmony_ci		/* given hash is longer, we take the left-most bytes */
17562306a36Sopenharmony_ci		memcpy(&rawhash, buffer + req->src_len, keylen);
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	ecc_swap_digits((u64 *)rawhash, hash, ctx->curve->g.ndigits);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	ret = _ecdsa_verify(ctx, hash, sig_ctx.r, sig_ctx.s);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cierror:
18362306a36Sopenharmony_ci	kfree(buffer);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	return ret;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic int ecdsa_ecc_ctx_init(struct ecc_ctx *ctx, unsigned int curve_id)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	ctx->curve_id = curve_id;
19162306a36Sopenharmony_ci	ctx->curve = ecc_get_curve(curve_id);
19262306a36Sopenharmony_ci	if (!ctx->curve)
19362306a36Sopenharmony_ci		return -EINVAL;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return 0;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic void ecdsa_ecc_ctx_deinit(struct ecc_ctx *ctx)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	ctx->pub_key_set = false;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic int ecdsa_ecc_ctx_reset(struct ecc_ctx *ctx)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	unsigned int curve_id = ctx->curve_id;
20762306a36Sopenharmony_ci	int ret;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	ecdsa_ecc_ctx_deinit(ctx);
21062306a36Sopenharmony_ci	ret = ecdsa_ecc_ctx_init(ctx, curve_id);
21162306a36Sopenharmony_ci	if (ret == 0)
21262306a36Sopenharmony_ci		ctx->pub_key = ECC_POINT_INIT(ctx->x, ctx->y,
21362306a36Sopenharmony_ci					      ctx->curve->g.ndigits);
21462306a36Sopenharmony_ci	return ret;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci/*
21862306a36Sopenharmony_ci * Set the public key given the raw uncompressed key data from an X509
21962306a36Sopenharmony_ci * certificate. The key data contain the concatenated X and Y coordinates of
22062306a36Sopenharmony_ci * the public key.
22162306a36Sopenharmony_ci */
22262306a36Sopenharmony_cistatic int ecdsa_set_pub_key(struct crypto_akcipher *tfm, const void *key, unsigned int keylen)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct ecc_ctx *ctx = akcipher_tfm_ctx(tfm);
22562306a36Sopenharmony_ci	const unsigned char *d = key;
22662306a36Sopenharmony_ci	const u64 *digits = (const u64 *)&d[1];
22762306a36Sopenharmony_ci	unsigned int ndigits;
22862306a36Sopenharmony_ci	int ret;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	ret = ecdsa_ecc_ctx_reset(ctx);
23162306a36Sopenharmony_ci	if (ret < 0)
23262306a36Sopenharmony_ci		return ret;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (keylen < 1 || (((keylen - 1) >> 1) % sizeof(u64)) != 0)
23562306a36Sopenharmony_ci		return -EINVAL;
23662306a36Sopenharmony_ci	/* we only accept uncompressed format indicated by '4' */
23762306a36Sopenharmony_ci	if (d[0] != 4)
23862306a36Sopenharmony_ci		return -EINVAL;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	keylen--;
24162306a36Sopenharmony_ci	ndigits = (keylen >> 1) / sizeof(u64);
24262306a36Sopenharmony_ci	if (ndigits != ctx->curve->g.ndigits)
24362306a36Sopenharmony_ci		return -EINVAL;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	ecc_swap_digits(digits, ctx->pub_key.x, ndigits);
24662306a36Sopenharmony_ci	ecc_swap_digits(&digits[ndigits], ctx->pub_key.y, ndigits);
24762306a36Sopenharmony_ci	ret = ecc_is_pubkey_valid_full(ctx->curve, &ctx->pub_key);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	ctx->pub_key_set = ret == 0;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return ret;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic void ecdsa_exit_tfm(struct crypto_akcipher *tfm)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	struct ecc_ctx *ctx = akcipher_tfm_ctx(tfm);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	ecdsa_ecc_ctx_deinit(ctx);
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic unsigned int ecdsa_max_size(struct crypto_akcipher *tfm)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct ecc_ctx *ctx = akcipher_tfm_ctx(tfm);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	return ctx->pub_key.ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic int ecdsa_nist_p384_init_tfm(struct crypto_akcipher *tfm)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	struct ecc_ctx *ctx = akcipher_tfm_ctx(tfm);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	return ecdsa_ecc_ctx_init(ctx, ECC_CURVE_NIST_P384);
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic struct akcipher_alg ecdsa_nist_p384 = {
27662306a36Sopenharmony_ci	.verify = ecdsa_verify,
27762306a36Sopenharmony_ci	.set_pub_key = ecdsa_set_pub_key,
27862306a36Sopenharmony_ci	.max_size = ecdsa_max_size,
27962306a36Sopenharmony_ci	.init = ecdsa_nist_p384_init_tfm,
28062306a36Sopenharmony_ci	.exit = ecdsa_exit_tfm,
28162306a36Sopenharmony_ci	.base = {
28262306a36Sopenharmony_ci		.cra_name = "ecdsa-nist-p384",
28362306a36Sopenharmony_ci		.cra_driver_name = "ecdsa-nist-p384-generic",
28462306a36Sopenharmony_ci		.cra_priority = 100,
28562306a36Sopenharmony_ci		.cra_module = THIS_MODULE,
28662306a36Sopenharmony_ci		.cra_ctxsize = sizeof(struct ecc_ctx),
28762306a36Sopenharmony_ci	},
28862306a36Sopenharmony_ci};
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic int ecdsa_nist_p256_init_tfm(struct crypto_akcipher *tfm)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct ecc_ctx *ctx = akcipher_tfm_ctx(tfm);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	return ecdsa_ecc_ctx_init(ctx, ECC_CURVE_NIST_P256);
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic struct akcipher_alg ecdsa_nist_p256 = {
29862306a36Sopenharmony_ci	.verify = ecdsa_verify,
29962306a36Sopenharmony_ci	.set_pub_key = ecdsa_set_pub_key,
30062306a36Sopenharmony_ci	.max_size = ecdsa_max_size,
30162306a36Sopenharmony_ci	.init = ecdsa_nist_p256_init_tfm,
30262306a36Sopenharmony_ci	.exit = ecdsa_exit_tfm,
30362306a36Sopenharmony_ci	.base = {
30462306a36Sopenharmony_ci		.cra_name = "ecdsa-nist-p256",
30562306a36Sopenharmony_ci		.cra_driver_name = "ecdsa-nist-p256-generic",
30662306a36Sopenharmony_ci		.cra_priority = 100,
30762306a36Sopenharmony_ci		.cra_module = THIS_MODULE,
30862306a36Sopenharmony_ci		.cra_ctxsize = sizeof(struct ecc_ctx),
30962306a36Sopenharmony_ci	},
31062306a36Sopenharmony_ci};
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic int ecdsa_nist_p192_init_tfm(struct crypto_akcipher *tfm)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	struct ecc_ctx *ctx = akcipher_tfm_ctx(tfm);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	return ecdsa_ecc_ctx_init(ctx, ECC_CURVE_NIST_P192);
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic struct akcipher_alg ecdsa_nist_p192 = {
32062306a36Sopenharmony_ci	.verify = ecdsa_verify,
32162306a36Sopenharmony_ci	.set_pub_key = ecdsa_set_pub_key,
32262306a36Sopenharmony_ci	.max_size = ecdsa_max_size,
32362306a36Sopenharmony_ci	.init = ecdsa_nist_p192_init_tfm,
32462306a36Sopenharmony_ci	.exit = ecdsa_exit_tfm,
32562306a36Sopenharmony_ci	.base = {
32662306a36Sopenharmony_ci		.cra_name = "ecdsa-nist-p192",
32762306a36Sopenharmony_ci		.cra_driver_name = "ecdsa-nist-p192-generic",
32862306a36Sopenharmony_ci		.cra_priority = 100,
32962306a36Sopenharmony_ci		.cra_module = THIS_MODULE,
33062306a36Sopenharmony_ci		.cra_ctxsize = sizeof(struct ecc_ctx),
33162306a36Sopenharmony_ci	},
33262306a36Sopenharmony_ci};
33362306a36Sopenharmony_cistatic bool ecdsa_nist_p192_registered;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic int __init ecdsa_init(void)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	int ret;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	/* NIST p192 may not be available in FIPS mode */
34062306a36Sopenharmony_ci	ret = crypto_register_akcipher(&ecdsa_nist_p192);
34162306a36Sopenharmony_ci	ecdsa_nist_p192_registered = ret == 0;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	ret = crypto_register_akcipher(&ecdsa_nist_p256);
34462306a36Sopenharmony_ci	if (ret)
34562306a36Sopenharmony_ci		goto nist_p256_error;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	ret = crypto_register_akcipher(&ecdsa_nist_p384);
34862306a36Sopenharmony_ci	if (ret)
34962306a36Sopenharmony_ci		goto nist_p384_error;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return 0;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cinist_p384_error:
35462306a36Sopenharmony_ci	crypto_unregister_akcipher(&ecdsa_nist_p256);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cinist_p256_error:
35762306a36Sopenharmony_ci	if (ecdsa_nist_p192_registered)
35862306a36Sopenharmony_ci		crypto_unregister_akcipher(&ecdsa_nist_p192);
35962306a36Sopenharmony_ci	return ret;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic void __exit ecdsa_exit(void)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	if (ecdsa_nist_p192_registered)
36562306a36Sopenharmony_ci		crypto_unregister_akcipher(&ecdsa_nist_p192);
36662306a36Sopenharmony_ci	crypto_unregister_akcipher(&ecdsa_nist_p256);
36762306a36Sopenharmony_ci	crypto_unregister_akcipher(&ecdsa_nist_p384);
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cisubsys_initcall(ecdsa_init);
37162306a36Sopenharmony_cimodule_exit(ecdsa_exit);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
37462306a36Sopenharmony_ciMODULE_AUTHOR("Stefan Berger <stefanb@linux.ibm.com>");
37562306a36Sopenharmony_ciMODULE_DESCRIPTION("ECDSA generic algorithm");
37662306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("ecdsa-generic");
377