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