diff --git a/include/gmssl/sm2.h b/include/gmssl/sm2.h index 3c1baf77..11a161e3 100644 --- a/include/gmssl/sm2.h +++ b/include/gmssl/sm2.h @@ -368,6 +368,10 @@ int sm2_decrypt(const SM2_KEY *key, const uint8_t *in, size_t inlen, uint8_t *ou int sm2_ecdh(const SM2_KEY *key, const SM2_POINT *peer_public, SM2_POINT *out); +int sm2_signature_to_public_key_points(const SM2_SIGNATURE *sig, const uint8_t dgst[32], + SM2_POINT points[4], size_t *points_cnt); + + #ifdef __cplusplus } #endif diff --git a/include/gmssl/sm2_elgamal.h b/include/gmssl/sm2_elgamal.h new file mode 100644 index 00000000..892eaceb --- /dev/null +++ b/include/gmssl/sm2_elgamal.h @@ -0,0 +1,45 @@ +/* + * Copyright 2014-2022 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +#ifndef GMSSL_SM2_ELGAMAL_H +#define GMSSL_SM2_ELGAMAL_H + + +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef uint32_t sm2_elgamal_plaintext_t; + +typedef struct { + SM2_POINT C1; + SM2_POINT C2; +} SM2_ELGAMAL_CIPHERTEXT; + + +int sm2_elgamal_encrypt(const SM2_KEY *pub_key, sm2_elgamal_plaintext_t in, SM2_ELGAMAL_CIPHERTEXT *out); +int sm2_elgamal_decrypt(const SM2_KEY *key, const SM2_ELGAMAL_CIPHERTEXT *in, sm2_elgamal_plaintext_t *out); +int sm2_elgamal_ciphertext_add(SM2_ELGAMAL_CIPHERTEXT *r, const SM2_ELGAMAL_CIPHERTEXT *a, const SM2_ELGAMAL_CIPHERTEXT *b, const SM2_KEY *pub_key); +int sm2_elgamal_cipehrtext_sub(SM2_ELGAMAL_CIPHERTEXT *r, const SM2_ELGAMAL_CIPHERTEXT *a, const SM2_ELGAMAL_CIPHERTEXT *b, const SM2_KEY *pub_key); +int sm2_elgamal_cipehrtext_neg(SM2_ELGAMAL_CIPHERTEXT *r, const SM2_ELGAMAL_CIPHERTEXT *a, const SM2_KEY *pub_key); +int sm2_elgamal_ciphertext_scalar_mul(SM2_ELGAMAL_CIPHERTEXT *r, uint32_t scalar, const SM2_ELGAMAL_CIPHERTEXT *a, const SM2_KEY *pub_key); +int sm2_elgamal_ciphertext_to_der(const SM2_ELGAMAL_CIPHERTEXT *c, uint8_t **out, size_t *outlen); +int sm2_elgamal_ciphertext_from_der(SM2_ELGAMAL_CIPHERTEXT *c, const uint8_t **in, size_t *inlen); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/sm2_elgamal.c b/src/sm2_elgamal.c new file mode 100644 index 00000000..037e7ed4 --- /dev/null +++ b/src/sm2_elgamal.c @@ -0,0 +1,253 @@ +/* + * Copyright 2014-2022 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sm2_elgamal.h" + + +int sm2_elgamal_encrypt(const SM2_KEY *pub_key, sm2_elgamal_plaintext_t in, SM2_ELGAMAL_CIPHERTEXT *out) +{ + SM2_Fn k; + uint8_t k_bytes[32]; + SM2_Fn m; + uint8_t m_bytes[32]; + + do { + sm2_fn_rand(k); // FIXME: sm2_fn_rand() return value! + } while (sm2_bn_is_zero(k)); + sm2_bn_to_bytes(k, k_bytes); + + + if (sm2_point_mul_generator(&out->C1, k_bytes) != 1) { + error_print(); + return -1; + } + + sm2_bn_set_word(m, in); + sm2_bn_to_bytes(m, m_bytes); + + if (sm2_point_mul_sum(&out->C2, k_bytes, &pub_key->public_key, m_bytes) != 1) { + error_print(); + return -1; + } + return 1; +} + + +// M = m * G = -[x]C1 + C2 +int sm2_elgamal_decrypt(const SM2_KEY *key, const SM2_ELGAMAL_CIPHERTEXT *in, sm2_elgamal_plaintext_t *out) +{ + SM2_Fn x; + SM2_JACOBIAN_POINT M; + SM2_JACOBIAN_POINT M_; + SM2_JACOBIAN_POINT C1; + SM2_JACOBIAN_POINT C2; + uint8_t bytes[64]; + uint32_t m; + + sm2_bn_from_bytes(x, key->private_key); + sm2_jacobian_point_from_bytes(&C1, (const uint8_t *)&in->C1); + sm2_jacobian_point_from_bytes(&C2, (const uint8_t *)&in->C2); + + sm2_jacobian_point_mul(&C1, x, &C1); + sm2_jacobian_point_neg(&C1, &C1); + sm2_jacobian_point_add(&M, &C1, &C2); + + sm2_jacobian_point_to_bytes(&M, bytes); + + sm2_jacobian_point_init(&M_); + + // TODO: a real algor required + for (m = 1; m < INT_MAX; m++) { + uint8_t point[64]; + + sm2_jacobian_point_add(&M_, &M_, SM2_G); + sm2_jacobian_point_to_bytes(&M_, point); + if (memcmp(point, bytes, 64) == 0) { + *out = m; + return 1; + } + } + + error_print(); + return -1; +} + +int test_sm2_elgamal_encrypt(void) +{ + SM2_KEY key; + SM2_ELGAMAL_CIPHERTEXT C1; + SM2_ELGAMAL_CIPHERTEXT C2; + sm2_elgamal_plaintext_t m = 1555; + + + sm2_key_generate(&key); + sm2_elgamal_encrypt(&key, m, &C1); + + m = 0; + sm2_elgamal_decrypt(&key, &C1, &m); + + printf("m = %u\n", m); + + return 1; + +} + + +// ([k1]G, [m1]G + [k1]P) + ([k2]G, [m2]G + [k2]P) => +// ([k1]G + [k2]G + [r]G, [m1]G + [m2]G + [k1]P + [k2]P + [r]P) + +int sm2_elgamal_ciphertext_add(SM2_ELGAMAL_CIPHERTEXT *r, + const SM2_ELGAMAL_CIPHERTEXT *a, + const SM2_ELGAMAL_CIPHERTEXT *b, + const SM2_KEY *pub_key) +{ + SM2_Fn k; + SM2_JACOBIAN_POINT A1; + SM2_JACOBIAN_POINT A2; + SM2_JACOBIAN_POINT B1; + SM2_JACOBIAN_POINT B2; + SM2_JACOBIAN_POINT R1; + SM2_JACOBIAN_POINT R2; + + sm2_jacobian_point_from_bytes(&A1, (uint8_t *)&a->C1); + sm2_jacobian_point_from_bytes(&A2, (uint8_t *)&a->C2); + sm2_jacobian_point_from_bytes(&B1, (uint8_t *)&b->C1); + sm2_jacobian_point_from_bytes(&B2, (uint8_t *)&b->C2); + + sm2_jacobian_point_add(&A1, &A1, &B1); + sm2_jacobian_point_add(&A2, &A2, &B2); + + do { + sm2_fn_rand(k); + } while (sm2_bn_is_zero(k)); + + sm2_jacobian_point_mul_generator(&R1, k); + sm2_jacobian_point_add(&A1, &A1, &R1); + + + sm2_jacobian_point_from_bytes(&R2, (const uint8_t *)&pub_key->public_key); + sm2_jacobian_point_mul(&R2, k, &R2); + sm2_jacobian_point_add(&A2, &A2, &R2); + + + sm2_jacobian_point_to_bytes(&A1, (uint8_t *)&r->C1); + sm2_jacobian_point_to_bytes(&A2, (uint8_t *)&r->C2); + + return 1; +} + +int sm2_elgamal_cipehrtext_sub(SM2_ELGAMAL_CIPHERTEXT *r, + const SM2_ELGAMAL_CIPHERTEXT *a, const SM2_ELGAMAL_CIPHERTEXT *b, + const SM2_KEY *pub_key) +{ + error_print(); + return -1; +} + +int sm2_elgamal_cipehrtext_neg(SM2_ELGAMAL_CIPHERTEXT *r, const SM2_ELGAMAL_CIPHERTEXT *a, const SM2_KEY *pub_key) +{ + error_print(); + return -1; +} + +// s * (C1, C2) := ([s]C1 + [r]G, [s]C2 + [r]P) +int sm2_elgamal_ciphertext_scalar_mul(SM2_ELGAMAL_CIPHERTEXT *r, const uint8_t scalar[32], const SM2_ELGAMAL_CIPHERTEXT *a, const SM2_KEY *pub_key) +{ + SM2_BN r; + uint8_t r_bytes[32]; + + SM2_BN s; + SM2_JACOBIAN_POINT C; + SM2_JACOBIAN_POINT P; + + do { + sm2_fn_rand(r); // FIXME: return value + } while (sm2_bn_is_zero(r)); + sm2_bn_to_bytes(r, r_bytes); + + sm2_point_mul_sum(&r->C1, scalar, &a->C1, r_bytes); + + sm2_bn_from_bytes(s, scalar); + sm2_jacobian_point_from_bytes(&C, &a->C2); + sm2_jacobian_point_from_bytes(&P, &pub_key->public_key); + sm2_jacobian_point_mul(&C, s, &C); + sm2_jacobian_point_mul(&P, r, &P); + sm2_jacobian_point_add(&C, &C, &P); + sm2_jacobian_point_to_bytes(&C, (uint8_t *)&r->C2); + + return 1; +} + +int sm2_elgamal_ciphertext_to_der(const SM2_ELGAMAL_CIPHERTEXT *c, uint8_t **out, size_t *outlen) +{ + uint8_t c1[65]; + uint8_t c2[65]; + size_t len; + + sm2_point_to_uncompressed_octets(&c->C1, c1); + sm2_point_to_uncompressed_octets(&c->C2, c2); + + if (asn1_octet_string_to_der(c1, sizeof(c1), NULL, &len) != 1 + || asn1_octet_string_to_der(c2, sizeof(c2), NULL, &len) != 1 + || asn1_sequence_header_to_der(len, out, outlen) != 1 + || asn1_octet_string_to_der(c1, sizeof(c1), out, outlen) != 1 + || asn1_octet_string_to_der(c2, sizeof(c2), out, outlen) != 1) { + error_print(); + return -1; + } + + return 1; +} + +int sm2_elgamal_ciphertext_from_der(SM2_ELGAMAL_CIPHERTEXT *c, const uint8_t **in, size_t *inlen) +{ + int ret; + const uint8_t *d; + size_t dlen; + const uint8_t *c1; + size_t c1len; + const uint8_t *c2; + size_t c2len; + + if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) { + error_print(); + return -1; + } + if (asn1_octet_string_from_der(&c1, &c1len, &d, &dlen) != 1 + || asn1_octet_string_from_der(&c2, &c2len, &d, &dlen) != 1 + || asn1_length_is_zero(dlen) != 1) { + error_print(); + return -1; + } + if (sm2_point_from_octets(&c->C1, c1, c1len) != 1 + || sm2_point_from_octets(&c->C2, c2, c2len) != 1) { + error_print(); + return -1; + } + return 1; +} + +#if 0 +int main(void) +{ + + test_sm2_elgamal_encrypt(); + + return 0; +} +#endif + diff --git a/src/sm2_recover.c b/src/sm2_recover.c new file mode 100644 index 00000000..3a1ea278 --- /dev/null +++ b/src/sm2_recover.c @@ -0,0 +1,200 @@ +/* + * Copyright 2014-2022 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include "sm2_recover.h" + + +static int sm2_bn_rshift(SM2_BN ret, const SM2_BN a, unsigned int nbits) +{ + SM2_BN r; + int i; + assert(nbits < 32); + for (i = 0; i < 7; i++) { + r[i] = a[i] >> nbits; + r[i] |= (a[i+1] << (32 - nbits)) & 0xffffffff; + } + r[i] = a[i] >> nbits; + sm2_bn_copy(ret, r); + return 1; +} + +static int test_sm2_bn_rshift(void) +{ + SM2_BN a; + int i; + + sm2_bn_from_hex(a, "ad23f3bff55b45c7192e25efcefa5fcecab1f072cd04a88e6cf64bfd3f531966"); + sm2_bn_print(stderr, 0, 0, "a", a); + + for (i = 0; i < 64; i++) { + sm2_bn_rshift(a, a, 31); + sm2_bn_print(stderr, 0, 0, "a", a); + } + + return 0; +} + +static int sm2_fp_sqrt(SM2_Fp r, const SM2_Fp a) +{ + SM2_BN u; + SM2_BN y; // temp result, prevent call sm2_fp_sqrt(a, a) + + // r = a^((p - 1)/4) when p = 3 (mod 4) + sm2_bn_add(u, SM2_P, SM2_ONE); + sm2_bn_rshift(u, u, 2); + sm2_fp_exp(y, a, u); + + // check r^2 == a + sm2_fp_sqr(u, y); + if (sm2_bn_cmp(u, a) != 0) { + error_print(); + return -1; + } + + sm2_bn_copy(r, y); + return 1; +} + +static int test_sm2_fp_sqrt(void) +{ + // a = 0x998eb0e4b8399fb359268966270049a6a4a317f9417c572c910d80c09969dc3 + // a^2 = 0x1b20ef7d2082f66c7561cdd4cdb0a8d58fa753e3e0d2e0560c80c849568f3fdb + return 1; +} + +// r = H(Z||M) + x1 (mod n) +// x1 = r - H(Z||M) (mod n) or (r - H(Z||M) (mod n)) + n +// y1 = sqrt(x1^3 + a*x1 + b) +// R = (x1, y1) or (x1, -y1) +// P = (r + s)^-1 * R - (r + s)^-1 * s * G +int sm2_signature_to_public_key_points(const SM2_SIGNATURE *sig, const uint8_t dgst[32], + SM2_POINT points[4], size_t *points_cnt) +{ + SM2_BN SM2_P_SUB_N; + SM2_JACOBIAN_POINT P; + SM2_JACOBIAN_POINT R; + + SM2_Fp r; + SM2_Fp s; + SM2_Fp e; + SM2_Fn u; + SM2_Fn v; + SM2_Fp x1; + SM2_Fp y1; + + sm2_bn_from_bytes(r, sig->r); + sm2_bn_from_bytes(s, sig->s); + + // u = (r + s)^-1, v = -(r + s)^-1 * s + sm2_fn_add(u, r, s); + sm2_fn_inv(u, u); + sm2_fn_mul(v, u, s); + sm2_fn_neg(v, v); + + // e = H(Z||M) (mod n) + sm2_bn_from_bytes(e, dgst); + if (sm2_bn_cmp(e, SM2_N) >= 0) { + sm2_bn_sub(e, e, SM2_N); + } + + // x1 = r - e (mod n) + sm2_fn_sub(x1, r, e); + + // y1 = sqrt(x1^3 + a*x + b) = sqrt((x1^2 + a)*x1 + b) + sm2_fp_sqr(y1, x1); + sm2_fp_sub(y1, y1, SM2_THREE); + sm2_fp_mul(y1, y1, x1); + sm2_fp_add(y1, y1, SM2_B); + + if (sm2_fp_sqrt(y1, y1) != 1) { + error_print(); + return -1; + } + sm2_jacobian_point_set_xy(&R, x1, y1); + + // P = u * R + v * G + sm2_jacobian_point_mul_sum(&P, u, &R, v); + sm2_jacobian_point_to_bytes(&P, (uint8_t *)&points[0]); + + // P' = u * (-R) + v * G + sm2_jacobian_point_neg(&R, &R); + sm2_jacobian_point_mul_sum(&P, u, &R, v); + sm2_jacobian_point_to_bytes(&P, (uint8_t *)&points[1]); + *points_cnt = 2; + + // if x1 in [n, p-1], x1 (mod n) in [0, p-n-1] + // ==> if x1 (mod n) in [0, p-n-1], x1 == (x1 (mod n) + n) (mod p) + sm2_bn_sub(SM2_P_SUB_N, SM2_P, SM2_N); + + if (sm2_bn_cmp(x1, SM2_P_SUB_N) < 0) { + + // x1' = x1 (mod n) + n + sm2_bn_add(x1, x1, SM2_N); + + // y1' = sqrt(x1'^3 + a*x' + b) + sm2_fp_sqr(y1, x1); + sm2_fp_sub(y1, y1, SM2_THREE); + sm2_fp_mul(y1, y1, x1); + sm2_fp_add(y1, y1, SM2_B); + if (sm2_fp_sqrt(y1, y1) != 1) { + error_print(); + return -1; + } + sm2_jacobian_point_set_xy(&R, x1, y1); + + // P = u * R + v * G + sm2_jacobian_point_mul_sum(&P, u, &R, v); + sm2_jacobian_point_to_bytes(&P, (uint8_t *)&points[2]); + // P' = u * (-R) + v * G + sm2_jacobian_point_neg(&R, &R); + sm2_jacobian_point_mul_sum(&P, u, &R, v); + sm2_jacobian_point_to_bytes(&P, (uint8_t *)&points[3]); + *points_cnt = 4; + } + + return 1; +} + +static int test_sm2_signature_to_public_key_points(void) +{ + SM2_KEY key; + uint8_t dgst[32] = {1,2,3,4}; + SM2_SIGNATURE sig; + SM2_POINT points[4]; + size_t points_cnt, i; + + sm2_key_generate(&key); + sm2_do_sign(&key, dgst, &sig); + sm2_signature_to_public_key_points(&sig, dgst, points, &points_cnt); + + for (i = 0; i < points_cnt; i++) { + int vr; + sm2_point_print(stderr, 0, 0, "point", &points[i]); + vr = sm2_do_verify((SM2_KEY *)&points[1], dgst, &sig); + printf("verify = %d\n", vr); + } + return 1; +} + +#if 0 +int main(void) +{ + //test_sm2_bn_rshift(); + //test_sm2_fp_sqrt(); + + test_sm2_signature_to_public_key_points(); + return 0; +} +#endif