162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Elliptic Curve (Russian) Digital Signature Algorithm for Cryptographic API 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2019 Vitaly Chikunov <vt@altlinux.org> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * References: 862306a36Sopenharmony_ci * GOST 34.10-2018, GOST R 34.10-2012, RFC 7091, ISO/IEC 14888-3:2018. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Historical references: 1162306a36Sopenharmony_ci * GOST R 34.10-2001, RFC 4357, ISO/IEC 14888-3:2006/Amd 1:2010. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 1462306a36Sopenharmony_ci * under the terms of the GNU General Public License as published by the Free 1562306a36Sopenharmony_ci * Software Foundation; either version 2 of the License, or (at your option) 1662306a36Sopenharmony_ci * any later version. 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/crypto.h> 2162306a36Sopenharmony_ci#include <crypto/streebog.h> 2262306a36Sopenharmony_ci#include <crypto/internal/akcipher.h> 2362306a36Sopenharmony_ci#include <crypto/internal/ecc.h> 2462306a36Sopenharmony_ci#include <crypto/akcipher.h> 2562306a36Sopenharmony_ci#include <linux/oid_registry.h> 2662306a36Sopenharmony_ci#include <linux/scatterlist.h> 2762306a36Sopenharmony_ci#include "ecrdsa_params.asn1.h" 2862306a36Sopenharmony_ci#include "ecrdsa_pub_key.asn1.h" 2962306a36Sopenharmony_ci#include "ecrdsa_defs.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define ECRDSA_MAX_SIG_SIZE (2 * 512 / 8) 3262306a36Sopenharmony_ci#define ECRDSA_MAX_DIGITS (512 / 64) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct ecrdsa_ctx { 3562306a36Sopenharmony_ci enum OID algo_oid; /* overall public key oid */ 3662306a36Sopenharmony_ci enum OID curve_oid; /* parameter */ 3762306a36Sopenharmony_ci enum OID digest_oid; /* parameter */ 3862306a36Sopenharmony_ci const struct ecc_curve *curve; /* curve from oid */ 3962306a36Sopenharmony_ci unsigned int digest_len; /* parameter (bytes) */ 4062306a36Sopenharmony_ci const char *digest; /* digest name from oid */ 4162306a36Sopenharmony_ci unsigned int key_len; /* @key length (bytes) */ 4262306a36Sopenharmony_ci const char *key; /* raw public key */ 4362306a36Sopenharmony_ci struct ecc_point pub_key; 4462306a36Sopenharmony_ci u64 _pubp[2][ECRDSA_MAX_DIGITS]; /* point storage for @pub_key */ 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic const struct ecc_curve *get_curve_by_oid(enum OID oid) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci switch (oid) { 5062306a36Sopenharmony_ci case OID_gostCPSignA: 5162306a36Sopenharmony_ci case OID_gostTC26Sign256B: 5262306a36Sopenharmony_ci return &gost_cp256a; 5362306a36Sopenharmony_ci case OID_gostCPSignB: 5462306a36Sopenharmony_ci case OID_gostTC26Sign256C: 5562306a36Sopenharmony_ci return &gost_cp256b; 5662306a36Sopenharmony_ci case OID_gostCPSignC: 5762306a36Sopenharmony_ci case OID_gostTC26Sign256D: 5862306a36Sopenharmony_ci return &gost_cp256c; 5962306a36Sopenharmony_ci case OID_gostTC26Sign512A: 6062306a36Sopenharmony_ci return &gost_tc512a; 6162306a36Sopenharmony_ci case OID_gostTC26Sign512B: 6262306a36Sopenharmony_ci return &gost_tc512b; 6362306a36Sopenharmony_ci /* The following two aren't implemented: */ 6462306a36Sopenharmony_ci case OID_gostTC26Sign256A: 6562306a36Sopenharmony_ci case OID_gostTC26Sign512C: 6662306a36Sopenharmony_ci default: 6762306a36Sopenharmony_ci return NULL; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int ecrdsa_verify(struct akcipher_request *req) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req); 7462306a36Sopenharmony_ci struct ecrdsa_ctx *ctx = akcipher_tfm_ctx(tfm); 7562306a36Sopenharmony_ci unsigned char sig[ECRDSA_MAX_SIG_SIZE]; 7662306a36Sopenharmony_ci unsigned char digest[STREEBOG512_DIGEST_SIZE]; 7762306a36Sopenharmony_ci unsigned int ndigits = req->dst_len / sizeof(u64); 7862306a36Sopenharmony_ci u64 r[ECRDSA_MAX_DIGITS]; /* witness (r) */ 7962306a36Sopenharmony_ci u64 _r[ECRDSA_MAX_DIGITS]; /* -r */ 8062306a36Sopenharmony_ci u64 s[ECRDSA_MAX_DIGITS]; /* second part of sig (s) */ 8162306a36Sopenharmony_ci u64 e[ECRDSA_MAX_DIGITS]; /* h \mod q */ 8262306a36Sopenharmony_ci u64 *v = e; /* e^{-1} \mod q */ 8362306a36Sopenharmony_ci u64 z1[ECRDSA_MAX_DIGITS]; 8462306a36Sopenharmony_ci u64 *z2 = _r; 8562306a36Sopenharmony_ci struct ecc_point cc = ECC_POINT_INIT(s, e, ndigits); /* reuse s, e */ 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* 8862306a36Sopenharmony_ci * Digest value, digest algorithm, and curve (modulus) should have the 8962306a36Sopenharmony_ci * same length (256 or 512 bits), public key and signature should be 9062306a36Sopenharmony_ci * twice bigger. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci if (!ctx->curve || 9362306a36Sopenharmony_ci !ctx->digest || 9462306a36Sopenharmony_ci !req->src || 9562306a36Sopenharmony_ci !ctx->pub_key.x || 9662306a36Sopenharmony_ci req->dst_len != ctx->digest_len || 9762306a36Sopenharmony_ci req->dst_len != ctx->curve->g.ndigits * sizeof(u64) || 9862306a36Sopenharmony_ci ctx->pub_key.ndigits != ctx->curve->g.ndigits || 9962306a36Sopenharmony_ci req->dst_len * 2 != req->src_len || 10062306a36Sopenharmony_ci WARN_ON(req->src_len > sizeof(sig)) || 10162306a36Sopenharmony_ci WARN_ON(req->dst_len > sizeof(digest))) 10262306a36Sopenharmony_ci return -EBADMSG; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci sg_copy_to_buffer(req->src, sg_nents_for_len(req->src, req->src_len), 10562306a36Sopenharmony_ci sig, req->src_len); 10662306a36Sopenharmony_ci sg_pcopy_to_buffer(req->src, 10762306a36Sopenharmony_ci sg_nents_for_len(req->src, 10862306a36Sopenharmony_ci req->src_len + req->dst_len), 10962306a36Sopenharmony_ci digest, req->dst_len, req->src_len); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci vli_from_be64(s, sig, ndigits); 11262306a36Sopenharmony_ci vli_from_be64(r, sig + ndigits * sizeof(u64), ndigits); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* Step 1: verify that 0 < r < q, 0 < s < q */ 11562306a36Sopenharmony_ci if (vli_is_zero(r, ndigits) || 11662306a36Sopenharmony_ci vli_cmp(r, ctx->curve->n, ndigits) >= 0 || 11762306a36Sopenharmony_ci vli_is_zero(s, ndigits) || 11862306a36Sopenharmony_ci vli_cmp(s, ctx->curve->n, ndigits) >= 0) 11962306a36Sopenharmony_ci return -EKEYREJECTED; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* Step 2: calculate hash (h) of the message (passed as input) */ 12262306a36Sopenharmony_ci /* Step 3: calculate e = h \mod q */ 12362306a36Sopenharmony_ci vli_from_le64(e, digest, ndigits); 12462306a36Sopenharmony_ci if (vli_cmp(e, ctx->curve->n, ndigits) >= 0) 12562306a36Sopenharmony_ci vli_sub(e, e, ctx->curve->n, ndigits); 12662306a36Sopenharmony_ci if (vli_is_zero(e, ndigits)) 12762306a36Sopenharmony_ci e[0] = 1; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* Step 4: calculate v = e^{-1} \mod q */ 13062306a36Sopenharmony_ci vli_mod_inv(v, e, ctx->curve->n, ndigits); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* Step 5: calculate z_1 = sv \mod q, z_2 = -rv \mod q */ 13362306a36Sopenharmony_ci vli_mod_mult_slow(z1, s, v, ctx->curve->n, ndigits); 13462306a36Sopenharmony_ci vli_sub(_r, ctx->curve->n, r, ndigits); 13562306a36Sopenharmony_ci vli_mod_mult_slow(z2, _r, v, ctx->curve->n, ndigits); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* Step 6: calculate point C = z_1P + z_2Q, and R = x_c \mod q */ 13862306a36Sopenharmony_ci ecc_point_mult_shamir(&cc, z1, &ctx->curve->g, z2, &ctx->pub_key, 13962306a36Sopenharmony_ci ctx->curve); 14062306a36Sopenharmony_ci if (vli_cmp(cc.x, ctx->curve->n, ndigits) >= 0) 14162306a36Sopenharmony_ci vli_sub(cc.x, cc.x, ctx->curve->n, ndigits); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* Step 7: if R == r signature is valid */ 14462306a36Sopenharmony_ci if (!vli_cmp(cc.x, r, ndigits)) 14562306a36Sopenharmony_ci return 0; 14662306a36Sopenharmony_ci else 14762306a36Sopenharmony_ci return -EKEYREJECTED; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ciint ecrdsa_param_curve(void *context, size_t hdrlen, unsigned char tag, 15162306a36Sopenharmony_ci const void *value, size_t vlen) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct ecrdsa_ctx *ctx = context; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci ctx->curve_oid = look_up_OID(value, vlen); 15662306a36Sopenharmony_ci if (!ctx->curve_oid) 15762306a36Sopenharmony_ci return -EINVAL; 15862306a36Sopenharmony_ci ctx->curve = get_curve_by_oid(ctx->curve_oid); 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/* Optional. If present should match expected digest algo OID. */ 16362306a36Sopenharmony_ciint ecrdsa_param_digest(void *context, size_t hdrlen, unsigned char tag, 16462306a36Sopenharmony_ci const void *value, size_t vlen) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct ecrdsa_ctx *ctx = context; 16762306a36Sopenharmony_ci int digest_oid = look_up_OID(value, vlen); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (digest_oid != ctx->digest_oid) 17062306a36Sopenharmony_ci return -EINVAL; 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ciint ecrdsa_parse_pub_key(void *context, size_t hdrlen, unsigned char tag, 17562306a36Sopenharmony_ci const void *value, size_t vlen) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct ecrdsa_ctx *ctx = context; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci ctx->key = value; 18062306a36Sopenharmony_ci ctx->key_len = vlen; 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic u8 *ecrdsa_unpack_u32(u32 *dst, void *src) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci memcpy(dst, src, sizeof(u32)); 18762306a36Sopenharmony_ci return src + sizeof(u32); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* Parse BER encoded subjectPublicKey. */ 19162306a36Sopenharmony_cistatic int ecrdsa_set_pub_key(struct crypto_akcipher *tfm, const void *key, 19262306a36Sopenharmony_ci unsigned int keylen) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct ecrdsa_ctx *ctx = akcipher_tfm_ctx(tfm); 19562306a36Sopenharmony_ci unsigned int ndigits; 19662306a36Sopenharmony_ci u32 algo, paramlen; 19762306a36Sopenharmony_ci u8 *params; 19862306a36Sopenharmony_ci int err; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci err = asn1_ber_decoder(&ecrdsa_pub_key_decoder, ctx, key, keylen); 20162306a36Sopenharmony_ci if (err < 0) 20262306a36Sopenharmony_ci return err; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Key parameters is in the key after keylen. */ 20562306a36Sopenharmony_ci params = ecrdsa_unpack_u32(¶mlen, 20662306a36Sopenharmony_ci ecrdsa_unpack_u32(&algo, (u8 *)key + keylen)); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (algo == OID_gost2012PKey256) { 20962306a36Sopenharmony_ci ctx->digest = "streebog256"; 21062306a36Sopenharmony_ci ctx->digest_oid = OID_gost2012Digest256; 21162306a36Sopenharmony_ci ctx->digest_len = 256 / 8; 21262306a36Sopenharmony_ci } else if (algo == OID_gost2012PKey512) { 21362306a36Sopenharmony_ci ctx->digest = "streebog512"; 21462306a36Sopenharmony_ci ctx->digest_oid = OID_gost2012Digest512; 21562306a36Sopenharmony_ci ctx->digest_len = 512 / 8; 21662306a36Sopenharmony_ci } else 21762306a36Sopenharmony_ci return -ENOPKG; 21862306a36Sopenharmony_ci ctx->algo_oid = algo; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* Parse SubjectPublicKeyInfo.AlgorithmIdentifier.parameters. */ 22162306a36Sopenharmony_ci err = asn1_ber_decoder(&ecrdsa_params_decoder, ctx, params, paramlen); 22262306a36Sopenharmony_ci if (err < 0) 22362306a36Sopenharmony_ci return err; 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * Sizes of algo (set in digest_len) and curve should match 22662306a36Sopenharmony_ci * each other. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci if (!ctx->curve || 22962306a36Sopenharmony_ci ctx->curve->g.ndigits * sizeof(u64) != ctx->digest_len) 23062306a36Sopenharmony_ci return -ENOPKG; 23162306a36Sopenharmony_ci /* 23262306a36Sopenharmony_ci * Key is two 256- or 512-bit coordinates which should match 23362306a36Sopenharmony_ci * curve size. 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_ci if ((ctx->key_len != (2 * 256 / 8) && 23662306a36Sopenharmony_ci ctx->key_len != (2 * 512 / 8)) || 23762306a36Sopenharmony_ci ctx->key_len != ctx->curve->g.ndigits * sizeof(u64) * 2) 23862306a36Sopenharmony_ci return -ENOPKG; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci ndigits = ctx->key_len / sizeof(u64) / 2; 24162306a36Sopenharmony_ci ctx->pub_key = ECC_POINT_INIT(ctx->_pubp[0], ctx->_pubp[1], ndigits); 24262306a36Sopenharmony_ci vli_from_le64(ctx->pub_key.x, ctx->key, ndigits); 24362306a36Sopenharmony_ci vli_from_le64(ctx->pub_key.y, ctx->key + ndigits * sizeof(u64), 24462306a36Sopenharmony_ci ndigits); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (ecc_is_pubkey_valid_partial(ctx->curve, &ctx->pub_key)) 24762306a36Sopenharmony_ci return -EKEYREJECTED; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic unsigned int ecrdsa_max_size(struct crypto_akcipher *tfm) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct ecrdsa_ctx *ctx = akcipher_tfm_ctx(tfm); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* 25762306a36Sopenharmony_ci * Verify doesn't need any output, so it's just informational 25862306a36Sopenharmony_ci * for keyctl to determine the key bit size. 25962306a36Sopenharmony_ci */ 26062306a36Sopenharmony_ci return ctx->pub_key.ndigits * sizeof(u64); 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic void ecrdsa_exit_tfm(struct crypto_akcipher *tfm) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic struct akcipher_alg ecrdsa_alg = { 26862306a36Sopenharmony_ci .verify = ecrdsa_verify, 26962306a36Sopenharmony_ci .set_pub_key = ecrdsa_set_pub_key, 27062306a36Sopenharmony_ci .max_size = ecrdsa_max_size, 27162306a36Sopenharmony_ci .exit = ecrdsa_exit_tfm, 27262306a36Sopenharmony_ci .base = { 27362306a36Sopenharmony_ci .cra_name = "ecrdsa", 27462306a36Sopenharmony_ci .cra_driver_name = "ecrdsa-generic", 27562306a36Sopenharmony_ci .cra_priority = 100, 27662306a36Sopenharmony_ci .cra_module = THIS_MODULE, 27762306a36Sopenharmony_ci .cra_ctxsize = sizeof(struct ecrdsa_ctx), 27862306a36Sopenharmony_ci }, 27962306a36Sopenharmony_ci}; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic int __init ecrdsa_mod_init(void) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci return crypto_register_akcipher(&ecrdsa_alg); 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic void __exit ecrdsa_mod_fini(void) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci crypto_unregister_akcipher(&ecrdsa_alg); 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cimodule_init(ecrdsa_mod_init); 29262306a36Sopenharmony_cimodule_exit(ecrdsa_mod_fini); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 29562306a36Sopenharmony_ciMODULE_AUTHOR("Vitaly Chikunov <vt@altlinux.org>"); 29662306a36Sopenharmony_ciMODULE_DESCRIPTION("EC-RDSA generic algorithm"); 29762306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("ecrdsa-generic"); 298