Tianjia Zhang | ea7ecb6 | 2020-09-21 00:20:57 +0800 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| 2 | /* |
| 3 | * SM2 asymmetric public-key algorithm |
| 4 | * as specified by OSCCA GM/T 0003.1-2012 -- 0003.5-2012 SM2 and |
| 5 | * described at https://tools.ietf.org/html/draft-shen-sm2-ecdsa-02 |
| 6 | * |
| 7 | * Copyright (c) 2020, Alibaba Group. |
| 8 | * Authors: Tianjia Zhang <tianjia.zhang@linux.alibaba.com> |
| 9 | */ |
| 10 | |
| 11 | #include <linux/module.h> |
| 12 | #include <linux/mpi.h> |
| 13 | #include <crypto/internal/akcipher.h> |
| 14 | #include <crypto/akcipher.h> |
| 15 | #include <crypto/hash.h> |
| 16 | #include <crypto/sm3_base.h> |
| 17 | #include <crypto/rng.h> |
| 18 | #include <crypto/sm2.h> |
| 19 | #include "sm2signature.asn1.h" |
| 20 | |
| 21 | #define MPI_NBYTES(m) ((mpi_get_nbits(m) + 7) / 8) |
| 22 | |
| 23 | struct ecc_domain_parms { |
| 24 | const char *desc; /* Description of the curve. */ |
| 25 | unsigned int nbits; /* Number of bits. */ |
| 26 | unsigned int fips:1; /* True if this is a FIPS140-2 approved curve */ |
| 27 | |
| 28 | /* The model describing this curve. This is mainly used to select |
| 29 | * the group equation. |
| 30 | */ |
| 31 | enum gcry_mpi_ec_models model; |
| 32 | |
| 33 | /* The actual ECC dialect used. This is used for curve specific |
| 34 | * optimizations and to select encodings etc. |
| 35 | */ |
| 36 | enum ecc_dialects dialect; |
| 37 | |
| 38 | const char *p; /* The prime defining the field. */ |
| 39 | const char *a, *b; /* The coefficients. For Twisted Edwards |
| 40 | * Curves b is used for d. For Montgomery |
| 41 | * Curves (a,b) has ((A-2)/4,B^-1). |
| 42 | */ |
| 43 | const char *n; /* The order of the base point. */ |
| 44 | const char *g_x, *g_y; /* Base point. */ |
| 45 | unsigned int h; /* Cofactor. */ |
| 46 | }; |
| 47 | |
| 48 | static const struct ecc_domain_parms sm2_ecp = { |
| 49 | .desc = "sm2p256v1", |
| 50 | .nbits = 256, |
| 51 | .fips = 0, |
| 52 | .model = MPI_EC_WEIERSTRASS, |
| 53 | .dialect = ECC_DIALECT_STANDARD, |
| 54 | .p = "0xfffffffeffffffffffffffffffffffffffffffff00000000ffffffffffffffff", |
| 55 | .a = "0xfffffffeffffffffffffffffffffffffffffffff00000000fffffffffffffffc", |
| 56 | .b = "0x28e9fa9e9d9f5e344d5a9e4bcf6509a7f39789f515ab8f92ddbcbd414d940e93", |
| 57 | .n = "0xfffffffeffffffffffffffffffffffff7203df6b21c6052b53bbf40939d54123", |
| 58 | .g_x = "0x32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7", |
| 59 | .g_y = "0xbc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0", |
| 60 | .h = 1 |
| 61 | }; |
| 62 | |
| 63 | static int sm2_ec_ctx_init(struct mpi_ec_ctx *ec) |
| 64 | { |
| 65 | const struct ecc_domain_parms *ecp = &sm2_ecp; |
| 66 | MPI p, a, b; |
| 67 | MPI x, y; |
| 68 | int rc = -EINVAL; |
| 69 | |
| 70 | p = mpi_scanval(ecp->p); |
| 71 | a = mpi_scanval(ecp->a); |
| 72 | b = mpi_scanval(ecp->b); |
| 73 | if (!p || !a || !b) |
| 74 | goto free_p; |
| 75 | |
| 76 | x = mpi_scanval(ecp->g_x); |
| 77 | y = mpi_scanval(ecp->g_y); |
| 78 | if (!x || !y) |
| 79 | goto free; |
| 80 | |
| 81 | rc = -ENOMEM; |
| 82 | /* mpi_ec_setup_elliptic_curve */ |
| 83 | ec->G = mpi_point_new(0); |
| 84 | if (!ec->G) |
| 85 | goto free; |
| 86 | |
| 87 | mpi_set(ec->G->x, x); |
| 88 | mpi_set(ec->G->y, y); |
| 89 | mpi_set_ui(ec->G->z, 1); |
| 90 | |
| 91 | rc = -EINVAL; |
| 92 | ec->n = mpi_scanval(ecp->n); |
| 93 | if (!ec->n) { |
| 94 | mpi_point_release(ec->G); |
| 95 | goto free; |
| 96 | } |
| 97 | |
| 98 | ec->h = ecp->h; |
| 99 | ec->name = ecp->desc; |
| 100 | mpi_ec_init(ec, ecp->model, ecp->dialect, 0, p, a, b); |
| 101 | |
| 102 | rc = 0; |
| 103 | |
| 104 | free: |
| 105 | mpi_free(x); |
| 106 | mpi_free(y); |
| 107 | free_p: |
| 108 | mpi_free(p); |
| 109 | mpi_free(a); |
| 110 | mpi_free(b); |
| 111 | |
| 112 | return rc; |
| 113 | } |
| 114 | |
| 115 | static void sm2_ec_ctx_deinit(struct mpi_ec_ctx *ec) |
| 116 | { |
| 117 | mpi_ec_deinit(ec); |
| 118 | |
| 119 | memset(ec, 0, sizeof(*ec)); |
| 120 | } |
| 121 | |
| 122 | static int sm2_ec_ctx_reset(struct mpi_ec_ctx *ec) |
| 123 | { |
| 124 | sm2_ec_ctx_deinit(ec); |
| 125 | return sm2_ec_ctx_init(ec); |
| 126 | } |
| 127 | |
| 128 | /* RESULT must have been initialized and is set on success to the |
| 129 | * point given by VALUE. |
| 130 | */ |
| 131 | static int sm2_ecc_os2ec(MPI_POINT result, MPI value) |
| 132 | { |
| 133 | int rc; |
| 134 | size_t n; |
| 135 | const unsigned char *buf; |
| 136 | unsigned char *buf_memory; |
| 137 | MPI x, y; |
| 138 | |
| 139 | n = (mpi_get_nbits(value)+7)/8; |
| 140 | buf_memory = kmalloc(n, GFP_KERNEL); |
| 141 | rc = mpi_print(GCRYMPI_FMT_USG, buf_memory, n, &n, value); |
| 142 | if (rc) { |
| 143 | kfree(buf_memory); |
| 144 | return rc; |
| 145 | } |
| 146 | buf = buf_memory; |
| 147 | |
| 148 | if (n < 1) { |
| 149 | kfree(buf_memory); |
| 150 | return -EINVAL; |
| 151 | } |
| 152 | if (*buf != 4) { |
| 153 | kfree(buf_memory); |
| 154 | return -EINVAL; /* No support for point compression. */ |
| 155 | } |
| 156 | if (((n-1)%2)) { |
| 157 | kfree(buf_memory); |
| 158 | return -EINVAL; |
| 159 | } |
| 160 | n = (n-1)/2; |
| 161 | x = mpi_read_raw_data(buf + 1, n); |
| 162 | if (!x) { |
| 163 | kfree(buf_memory); |
| 164 | return -ENOMEM; |
| 165 | } |
| 166 | y = mpi_read_raw_data(buf + 1 + n, n); |
| 167 | kfree(buf_memory); |
| 168 | if (!y) { |
| 169 | mpi_free(x); |
| 170 | return -ENOMEM; |
| 171 | } |
| 172 | |
| 173 | mpi_normalize(x); |
| 174 | mpi_normalize(y); |
| 175 | |
| 176 | mpi_set(result->x, x); |
| 177 | mpi_set(result->y, y); |
| 178 | mpi_set_ui(result->z, 1); |
| 179 | |
| 180 | mpi_free(x); |
| 181 | mpi_free(y); |
| 182 | |
| 183 | return 0; |
| 184 | } |
| 185 | |
| 186 | struct sm2_signature_ctx { |
| 187 | MPI sig_r; |
| 188 | MPI sig_s; |
| 189 | }; |
| 190 | |
| 191 | int sm2_get_signature_r(void *context, size_t hdrlen, unsigned char tag, |
| 192 | const void *value, size_t vlen) |
| 193 | { |
| 194 | struct sm2_signature_ctx *sig = context; |
| 195 | |
| 196 | if (!value || !vlen) |
| 197 | return -EINVAL; |
| 198 | |
| 199 | sig->sig_r = mpi_read_raw_data(value, vlen); |
| 200 | if (!sig->sig_r) |
| 201 | return -ENOMEM; |
| 202 | |
| 203 | return 0; |
| 204 | } |
| 205 | |
| 206 | int sm2_get_signature_s(void *context, size_t hdrlen, unsigned char tag, |
| 207 | const void *value, size_t vlen) |
| 208 | { |
| 209 | struct sm2_signature_ctx *sig = context; |
| 210 | |
| 211 | if (!value || !vlen) |
| 212 | return -EINVAL; |
| 213 | |
| 214 | sig->sig_s = mpi_read_raw_data(value, vlen); |
| 215 | if (!sig->sig_s) |
| 216 | return -ENOMEM; |
| 217 | |
| 218 | return 0; |
| 219 | } |
| 220 | |
| 221 | static int sm2_z_digest_update(struct shash_desc *desc, |
| 222 | MPI m, unsigned int pbytes) |
| 223 | { |
| 224 | static const unsigned char zero[32]; |
| 225 | unsigned char *in; |
| 226 | unsigned int inlen; |
| 227 | |
| 228 | in = mpi_get_buffer(m, &inlen, NULL); |
| 229 | if (!in) |
| 230 | return -EINVAL; |
| 231 | |
| 232 | if (inlen < pbytes) { |
| 233 | /* padding with zero */ |
| 234 | crypto_sm3_update(desc, zero, pbytes - inlen); |
| 235 | crypto_sm3_update(desc, in, inlen); |
| 236 | } else if (inlen > pbytes) { |
| 237 | /* skip the starting zero */ |
| 238 | crypto_sm3_update(desc, in + inlen - pbytes, pbytes); |
| 239 | } else { |
| 240 | crypto_sm3_update(desc, in, inlen); |
| 241 | } |
| 242 | |
| 243 | kfree(in); |
| 244 | return 0; |
| 245 | } |
| 246 | |
| 247 | static int sm2_z_digest_update_point(struct shash_desc *desc, |
| 248 | MPI_POINT point, struct mpi_ec_ctx *ec, unsigned int pbytes) |
| 249 | { |
| 250 | MPI x, y; |
| 251 | int ret = -EINVAL; |
| 252 | |
| 253 | x = mpi_new(0); |
| 254 | y = mpi_new(0); |
| 255 | |
| 256 | if (!mpi_ec_get_affine(x, y, point, ec) && |
| 257 | !sm2_z_digest_update(desc, x, pbytes) && |
| 258 | !sm2_z_digest_update(desc, y, pbytes)) |
| 259 | ret = 0; |
| 260 | |
| 261 | mpi_free(x); |
| 262 | mpi_free(y); |
| 263 | return ret; |
| 264 | } |
| 265 | |
| 266 | int sm2_compute_z_digest(struct crypto_akcipher *tfm, |
| 267 | const unsigned char *id, size_t id_len, |
| 268 | unsigned char dgst[SM3_DIGEST_SIZE]) |
| 269 | { |
| 270 | struct mpi_ec_ctx *ec = akcipher_tfm_ctx(tfm); |
| 271 | uint16_t bits_len; |
| 272 | unsigned char entl[2]; |
| 273 | SHASH_DESC_ON_STACK(desc, NULL); |
| 274 | unsigned int pbytes; |
| 275 | |
| 276 | if (id_len > (USHRT_MAX / 8) || !ec->Q) |
| 277 | return -EINVAL; |
| 278 | |
| 279 | bits_len = (uint16_t)(id_len * 8); |
| 280 | entl[0] = bits_len >> 8; |
| 281 | entl[1] = bits_len & 0xff; |
| 282 | |
| 283 | pbytes = MPI_NBYTES(ec->p); |
| 284 | |
| 285 | /* ZA = H256(ENTLA | IDA | a | b | xG | yG | xA | yA) */ |
| 286 | sm3_base_init(desc); |
| 287 | crypto_sm3_update(desc, entl, 2); |
| 288 | crypto_sm3_update(desc, id, id_len); |
| 289 | |
| 290 | if (sm2_z_digest_update(desc, ec->a, pbytes) || |
| 291 | sm2_z_digest_update(desc, ec->b, pbytes) || |
| 292 | sm2_z_digest_update_point(desc, ec->G, ec, pbytes) || |
| 293 | sm2_z_digest_update_point(desc, ec->Q, ec, pbytes)) |
| 294 | return -EINVAL; |
| 295 | |
| 296 | crypto_sm3_final(desc, dgst); |
| 297 | return 0; |
| 298 | } |
| 299 | EXPORT_SYMBOL(sm2_compute_z_digest); |
| 300 | |
| 301 | static int _sm2_verify(struct mpi_ec_ctx *ec, MPI hash, MPI sig_r, MPI sig_s) |
| 302 | { |
| 303 | int rc = -EINVAL; |
| 304 | struct gcry_mpi_point sG, tP; |
| 305 | MPI t = NULL; |
| 306 | MPI x1 = NULL, y1 = NULL; |
| 307 | |
| 308 | mpi_point_init(&sG); |
| 309 | mpi_point_init(&tP); |
| 310 | x1 = mpi_new(0); |
| 311 | y1 = mpi_new(0); |
| 312 | t = mpi_new(0); |
| 313 | |
| 314 | /* r, s in [1, n-1] */ |
| 315 | if (mpi_cmp_ui(sig_r, 1) < 0 || mpi_cmp(sig_r, ec->n) > 0 || |
| 316 | mpi_cmp_ui(sig_s, 1) < 0 || mpi_cmp(sig_s, ec->n) > 0) { |
| 317 | goto leave; |
| 318 | } |
| 319 | |
| 320 | /* t = (r + s) % n, t == 0 */ |
| 321 | mpi_addm(t, sig_r, sig_s, ec->n); |
| 322 | if (mpi_cmp_ui(t, 0) == 0) |
| 323 | goto leave; |
| 324 | |
| 325 | /* sG + tP = (x1, y1) */ |
| 326 | rc = -EBADMSG; |
| 327 | mpi_ec_mul_point(&sG, sig_s, ec->G, ec); |
| 328 | mpi_ec_mul_point(&tP, t, ec->Q, ec); |
| 329 | mpi_ec_add_points(&sG, &sG, &tP, ec); |
| 330 | if (mpi_ec_get_affine(x1, y1, &sG, ec)) |
| 331 | goto leave; |
| 332 | |
| 333 | /* R = (e + x1) % n */ |
| 334 | mpi_addm(t, hash, x1, ec->n); |
| 335 | |
| 336 | /* check R == r */ |
| 337 | rc = -EKEYREJECTED; |
| 338 | if (mpi_cmp(t, sig_r)) |
| 339 | goto leave; |
| 340 | |
| 341 | rc = 0; |
| 342 | |
| 343 | leave: |
| 344 | mpi_point_free_parts(&sG); |
| 345 | mpi_point_free_parts(&tP); |
| 346 | mpi_free(x1); |
| 347 | mpi_free(y1); |
| 348 | mpi_free(t); |
| 349 | |
| 350 | return rc; |
| 351 | } |
| 352 | |
| 353 | static int sm2_verify(struct akcipher_request *req) |
| 354 | { |
| 355 | struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req); |
| 356 | struct mpi_ec_ctx *ec = akcipher_tfm_ctx(tfm); |
| 357 | unsigned char *buffer; |
| 358 | struct sm2_signature_ctx sig; |
| 359 | MPI hash; |
| 360 | int ret; |
| 361 | |
| 362 | if (unlikely(!ec->Q)) |
| 363 | return -EINVAL; |
| 364 | |
| 365 | buffer = kmalloc(req->src_len + req->dst_len, GFP_KERNEL); |
| 366 | if (!buffer) |
| 367 | return -ENOMEM; |
| 368 | |
| 369 | sg_pcopy_to_buffer(req->src, |
| 370 | sg_nents_for_len(req->src, req->src_len + req->dst_len), |
| 371 | buffer, req->src_len + req->dst_len, 0); |
| 372 | |
| 373 | sig.sig_r = NULL; |
| 374 | sig.sig_s = NULL; |
| 375 | ret = asn1_ber_decoder(&sm2signature_decoder, &sig, |
| 376 | buffer, req->src_len); |
| 377 | if (ret) |
| 378 | goto error; |
| 379 | |
| 380 | ret = -ENOMEM; |
| 381 | hash = mpi_read_raw_data(buffer + req->src_len, req->dst_len); |
| 382 | if (!hash) |
| 383 | goto error; |
| 384 | |
| 385 | ret = _sm2_verify(ec, hash, sig.sig_r, sig.sig_s); |
| 386 | |
| 387 | mpi_free(hash); |
| 388 | error: |
| 389 | mpi_free(sig.sig_r); |
| 390 | mpi_free(sig.sig_s); |
| 391 | kfree(buffer); |
| 392 | return ret; |
| 393 | } |
| 394 | |
| 395 | static int sm2_set_pub_key(struct crypto_akcipher *tfm, |
| 396 | const void *key, unsigned int keylen) |
| 397 | { |
| 398 | struct mpi_ec_ctx *ec = akcipher_tfm_ctx(tfm); |
| 399 | MPI a; |
| 400 | int rc; |
| 401 | |
| 402 | rc = sm2_ec_ctx_reset(ec); |
| 403 | if (rc) |
| 404 | return rc; |
| 405 | |
| 406 | ec->Q = mpi_point_new(0); |
| 407 | if (!ec->Q) |
| 408 | return -ENOMEM; |
| 409 | |
| 410 | /* include the uncompressed flag '0x04' */ |
| 411 | rc = -ENOMEM; |
| 412 | a = mpi_read_raw_data(key, keylen); |
| 413 | if (!a) |
| 414 | goto error; |
| 415 | |
| 416 | mpi_normalize(a); |
| 417 | rc = sm2_ecc_os2ec(ec->Q, a); |
| 418 | mpi_free(a); |
| 419 | if (rc) |
| 420 | goto error; |
| 421 | |
| 422 | return 0; |
| 423 | |
| 424 | error: |
| 425 | mpi_point_release(ec->Q); |
| 426 | ec->Q = NULL; |
| 427 | return rc; |
| 428 | } |
| 429 | |
| 430 | static unsigned int sm2_max_size(struct crypto_akcipher *tfm) |
| 431 | { |
| 432 | /* Unlimited max size */ |
| 433 | return PAGE_SIZE; |
| 434 | } |
| 435 | |
| 436 | static int sm2_init_tfm(struct crypto_akcipher *tfm) |
| 437 | { |
| 438 | struct mpi_ec_ctx *ec = akcipher_tfm_ctx(tfm); |
| 439 | |
| 440 | return sm2_ec_ctx_init(ec); |
| 441 | } |
| 442 | |
| 443 | static void sm2_exit_tfm(struct crypto_akcipher *tfm) |
| 444 | { |
| 445 | struct mpi_ec_ctx *ec = akcipher_tfm_ctx(tfm); |
| 446 | |
| 447 | sm2_ec_ctx_deinit(ec); |
| 448 | } |
| 449 | |
| 450 | static struct akcipher_alg sm2 = { |
| 451 | .verify = sm2_verify, |
| 452 | .set_pub_key = sm2_set_pub_key, |
| 453 | .max_size = sm2_max_size, |
| 454 | .init = sm2_init_tfm, |
| 455 | .exit = sm2_exit_tfm, |
| 456 | .base = { |
| 457 | .cra_name = "sm2", |
| 458 | .cra_driver_name = "sm2-generic", |
| 459 | .cra_priority = 100, |
| 460 | .cra_module = THIS_MODULE, |
| 461 | .cra_ctxsize = sizeof(struct mpi_ec_ctx), |
| 462 | }, |
| 463 | }; |
| 464 | |
| 465 | static int sm2_init(void) |
| 466 | { |
| 467 | return crypto_register_akcipher(&sm2); |
| 468 | } |
| 469 | |
| 470 | static void sm2_exit(void) |
| 471 | { |
| 472 | crypto_unregister_akcipher(&sm2); |
| 473 | } |
| 474 | |
| 475 | subsys_initcall(sm2_init); |
| 476 | module_exit(sm2_exit); |
| 477 | |
| 478 | MODULE_LICENSE("GPL"); |
| 479 | MODULE_AUTHOR("Tianjia Zhang <tianjia.zhang@linux.alibaba.com>"); |
| 480 | MODULE_DESCRIPTION("SM2 generic algorithm"); |
| 481 | MODULE_ALIAS_CRYPTO("sm2-generic"); |