Files
GmSSL/js/sm2.js
2021-08-21 14:47:21 +08:00

654 lines
13 KiB
JavaScript

/*
* Copyright (c) 2014 - 2020 The GmSSL Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the GmSSL Project.
* (http://gmssl.org/)"
*
* 4. The name "GmSSL Project" must not be used to endorse or promote
* products derived from this software without prior written
* permission. For written permission, please contact
* guanzhi1980@gmail.com.
*
* 5. Products derived from this software may not be called "GmSSL"
* nor may "GmSSL" appear in their names without prior written
* permission of the GmSSL Project.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the GmSSL Project
* (http://gmssl.org/)"
*
* THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE GmSSL PROJECT OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
const SM2_P = [
0xffff, 0xffff, 0xffff, 0xffff,
0x0000, 0x0000, 0xffff, 0xffff,
0xffff, 0xffff, 0xffff, 0xffff,
0xffff, 0xffff, 0xfffe, 0xffff,
];
const SM2_B = [
0x0e93, 0x4d94, 0xbd41, 0xddbc,
0x8f92, 0x15ab, 0x89f5, 0xf397,
0x09a7, 0xcf65, 0x9e4b, 0x4d5a,
0x5e34, 0x9d9f, 0xfa9e, 0x28e9,
];
const SM2_G = {
X : [
0x74c7, 0x334c, 0x4589, 0x715a,
0x0be1, 0xf266, 0x0bbf, 0x8fe3,
0xc994, 0x6a39, 0x0446, 0x5f99,
0x8119, 0x1f19, 0xae2c, 0x32c4],
Y : [
0xf0a0, 0x2139, 0x32e5, 0x02df,
0x4740, 0xc62a, 0x877c, 0xd0a9,
0x2153, 0x6b69, 0xcee3, 0x59bd,
0x779c, 0xf4f6, 0x36a2, 0xbc37],
Z: [
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
};
const SM2_N = [
0x4123, 0x39d5, 0xf409, 0x53bb,
0x052b, 0x21c6, 0xdf6b, 0x7203,
0xffff, 0xffff, 0xffff, 0xffff,
0xffff, 0xffff, 0xfffe, 0xffff,
];
function bn_new() {
return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
}
function bn_free(a) {
bn_set_zero(a);
delete a;
}
function bn_is_zero(a) {
for (var i = 0; i < 16; i++) {
if (a[i] != 0)
return 0;
}
return 1;
}
function bn_is_one(a) {
if (a[0] != 1)
return 0;
for (var i = 1; i < 16; i++) {
if (a[i] != 0)
return 0;
}
return 1;
}
function bn_to_bytes(a, buf, offset) {
for (var i = 15; i >= 0; i--) {
buf[offset++] = Math.floor(a[i]/256);
buf[offset++] = a[i] % 256;
}
}
function bn_set_bytes(a, buf, offset) {
for (var i = 15; i >= 0; i--) {
a[i] = buf[offset++] * 256;
a[i] += buf[offset++];
}
}
function bn_cmp(a, b) {
for (var i = 15; i >= 0; i--) {
if (a[i] > b[i])
return 1;
if (a[i] < b[i])
return -1;
}
return 0;
}
function bn_set_word(a, word) {
a[0] = word;
for (var i = 1; i < 16; i++) {
a[i] = 0;
}
}
function bn_set_zero(a) {
bn_set_word(a, 0);
}
function bn_set_one(a) {
bn_set_word(a, 1);
}
function bn_copy(r, a) {
for (var i = 0; i < 16; i++) {
r[i] = a[i];
}
}
function bn_to_bits(a) {
var bits = new Array(256);
for (var i = 0; i < 16; i++) {
var w = a[i];
for (var j = 0; j < 16; j++) {
bits[i*16 + j] = w % 2;
w = Math.floor(w/2);
}
}
return bits.reverse();
}
function bn_add(r, a, b) {
r[0] = a[0] + b[0];
for (var i = 1; i < 16; i++) {
r[i] = a[i] + b[i] + Math.floor(r[i-1] / 65536);
}
for (var i = 0; i < 15; i++) {
r[i] %= 65536;
}
}
function bn_sub(r, a, b) {
var borrow = 0;
for (var i = 0; i < 16; i++) {
r[i] = a[i] - b[i] - borrow;
borrow = 0;
if (r[i] < 0) {
r[i] += 65536;
borrow = 1;
}
}
}
function fp_add(r, a, b) {
bn_add(r, a, b);
if (bn_cmp(r, SM2_P) >= 0) {
return bn_sub(r, r, SM2_P);
}
}
function fp_sub(r, a, b) {
if (bn_cmp(a, b) >= 0) {
bn_sub(r, a, b);
} else {
// FIXME: remove t,
// it need there is no fp_sub(b, a, b)
// which is used in point_double()
var t = bn_new();
bn_add(t, a, SM2_P);
bn_sub(r, t, b);
}
}
function fp_dbl(r, a) {
return fp_add(r, a, a);
}
function fp_tri(r, a) {
var t = bn_new();
fp_dbl(t, a);
fp_add(r, t, a);
}
function fp_div2(r, a) {
bn_copy(r, a);
if (r[0] % 2) {
bn_add(r, r, SM2_P);
}
var i;
for (i = 0; i < 15; i++) {
r[i] = Math.floor(r[i] / 2);
r[i] += (r[i + 1] % 2) * 32768;
}
r[i] = Math.floor(r[i] / 2);
}
function fp_neg(r, a) {
if (bn_is_zero(a)) {
bn_set_zero(r);
} else {
bn_sub(r, SM2_P, a);
}
}
function fp_mul(r, a, b) {
var ab = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]
for (var i = 0; i < 16; i++) {
for (var j = 0; j < 16; j++) {
ab[i + j] += a[i] * b[j];
}
}
for (var i = 0; i < 31; i++) {
ab[i + 1] += Math.floor(ab[i] / 65536);
ab[i] = ab[i] % 65536;
}
s = ab.slice(16, 32);
r[ 0] = ab[ 0] + s[0] + s[ 2] + s[ 4] + s[ 6] + s[ 8] + 2 * (s[10] + s[12] + s[14]);
r[ 1] = ab[ 1] + s[1] + s[ 3] + s[ 5] + s[ 7] + s[ 9] + 2 * (s[11] + s[13] + s[15]);
r[ 2] = ab[ 2] + s[2] + s[ 4] + s[ 6] + s[ 8] + s[10] + 2 * (s[12] + s[14]);
r[ 3] = ab[ 3] + s[3] + s[ 5] + s[ 7] + s[ 9] + s[11] + 2 * (s[13] + s[15]);
r[ 4] = ab[ 4];
r[ 5] = ab[ 5];
r[ 6] = ab[ 6] + s[0] + s[ 6] + s[ 8] + s[12] + s[14] + 2 * s[10];
r[ 7] = ab[ 7] + s[1] + s[ 7] + s[ 9] + s[13] + s[15] + 2 * s[11];
r[ 8] = ab[ 8] + s[2] + s[ 8] + s[10] + s[14] + 2 * s[12];
r[ 9] = ab[ 9] + s[3] + s[ 9] + s[11] + s[15] + 2 * s[13];
r[10] = ab[10] + s[4] + s[10] + s[12] + 2 * s[14];
r[11] = ab[11] + s[5] + s[11] + s[13] + 2 * s[15];
r[12] = ab[12] + s[6] + s[12] + s[14];
r[13] = ab[13] + s[7] + s[13] + s[15];
r[14] = ab[14] + s[0] + s[ 2] + s[ 4] + s[ 6] + s[14] + 2 * (s[8] + s[10] + s[12] + s[14]);
r[15] = ab[15] + s[1] + s[ 3] + s[ 5] + s[ 7] + s[15] + 2 * (s[9] + s[11] + s[13] + s[15]);
for (var i = 0; i < 15; i++) {
r[i + 1] += Math.floor(r[i] / 65536);
r[i] %= 65536
}
var d = [0,0,0,0,s[0]+s[2]+s[10]+s[12],s[1]+s[3]+s[11]+s[13],0,0,0,0,0,0,0,0,0,0];
for (var i = 0; i < 15; i++) {
d[i + 1] += Math.floor(d[i] / 65536);
d[i] %= 65536
}
bn_sub(r, r, d);
while (bn_cmp(r, SM2_P) >= 0) {
bn_sub(r, r, SM2_P);
}
}
function fp_sqr(r, a) {
fp_mul(r, a, a);
}
function fp_inv(r, a) {
var a1 = bn_new();
var a2 = bn_new();
var a3 = bn_new();
var a4 = bn_new();
var a5 = bn_new();
fp_sqr(a1, a);
fp_mul(a2, a1, a);
fp_sqr(a3, a2);
fp_sqr(a3, a3);
fp_mul(a3, a3, a2);
fp_sqr(a4, a3);
fp_sqr(a4, a4);
fp_sqr(a4, a4);
fp_sqr(a4, a4);
fp_mul(a4, a4, a3);
fp_sqr(a5, a4);
for (i = 1; i < 8; i++)
fp_sqr(a5, a5);
fp_mul(a5, a5, a4);
for (i = 0; i < 8; i++)
fp_sqr(a5, a5);
fp_mul(a5, a5, a4);
for (i = 0; i < 4; i++)
fp_sqr(a5, a5);
fp_mul(a5, a5, a3);
fp_sqr(a5, a5);
fp_sqr(a5, a5);
fp_mul(a5, a5, a2);
fp_sqr(a5, a5);
fp_mul(a5, a5, a);
fp_sqr(a4, a5);
fp_mul(a3, a4, a1);
fp_sqr(a5, a4);
for (i = 1; i< 31; i++)
fp_sqr(a5, a5);
fp_mul(a4, a5, a4);
fp_sqr(a4, a4);
fp_mul(a4, a4, a);
fp_mul(a3, a4, a2);
for (i = 0; i < 33; i++)
fp_sqr(a5, a5);
fp_mul(a2, a5, a3);
fp_mul(a3, a2, a3);
for (i = 0; i < 32; i++)
fp_sqr(a5, a5);
fp_mul(a2, a5, a3);
fp_mul(a3, a2, a3);
fp_mul(a4, a2, a4);
for (i = 0; i < 32; i++)
fp_sqr(a5, a5);
fp_mul(a2, a5, a3);
fp_mul(a3, a2, a3);
fp_mul(a4, a2, a4);
for (i = 0; i < 32; i++)
fp_sqr(a5, a5);
fp_mul(a2, a5, a3);
fp_mul(a3, a2, a3);
fp_mul(a4, a2, a4);
for (i = 0; i < 32; i++)
fp_sqr(a5, a5);
fp_mul(a2, a5, a3);
fp_mul(a3, a2, a3);
fp_mul(a4, a2, a4);
for (i = 0; i < 32; i++)
fp_sqr(a5, a5)
fp_mul(r, a4, a5);
bn_free(a1);
bn_free(a2);
bn_free(a3);
bn_free(a4);
bn_free(a5);
}
function sm2_point_new() {
var point = {
X : [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
Y : [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
Z : [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
}
return point;
}
function sm2_point_free(P) {
bn_set_zero(P.X);
bn_set_zero(P.Y);
bn_set_zero(P.Z);
delete P;
}
function sm2_point_copy(R, P) {
bn_copy(R.X, P.X);
bn_copy(R.Y, P.Y);
bn_copy(R.Z, P.Z);
}
function sm2_point_is_at_infinity(P) {
return bn_is_zero(P.Z);
}
function sm2_point_set_infinity(R) {
bn_set_one(R.X);
bn_set_one(R.Y);
bn_set_zero(R.Z);
}
function sm2_point_get_affine(R, P) {
sm2_point_copy(R, P);
if (bn_is_zero(R.Z) || bn_is_one(R.Z))
return;
fp_inv(R.Z, R.Z);
fp_mul(R.Y, R.Y, R.Z);
fp_sqr(R.Z, R.Z);
fp_mul(R.X, R.X, R.Z);
fp_mul(R.Y, R.Y, R.Z);
bn_set_one(R.Z);
}
function sm2_point_set_bytes(P, buf, offset) {
bn_set_bytes(P.X, buf, offset);
bn_set_bytes(P.Y, buf, offset + 32);
bn_set_one(P.Z);
}
function sm2_point_to_bytes(P, buf, offset) {
var A = sm2_point_new();
sm2_point_get_affine(A, P);
bn_to_bytes(A.X, buf, offset);
bn_to_bytes(A.Y, buf, offset + 32);
}
function sm2_point_is_on_curve(P) {
var t0 = bn_new();
var t1 = bn_new();
var t2 = bn_new();
if (bn_is_one(P.Z)) {
fp_sqr(t0, P.Y);
fp_add(t0, t0, P.X);
fp_add(t0, t0, P.X);
fp_add(t0, t0, P.X);
fp_sqr(t1, P.X);
fp_mul(t1, t1, P.X);
fp_add(t1, t1, SM2_B);
} else {
fp_sqr(t0, P.Y);
fp_sqr(t1, P.Z);
fp_sqr(t2, t1);
fp_mul(t1, t1, t2);
fp_mul(t1, t1, SM2_B);
fp_mul(t2, t2, P.X);
fp_add(t0, t0, t2);
fp_add(t0, t0, t2);
fp_add(t0, t0, t2);
fp_sqr(t2, P.X);
fp_mul(t2, t2, P.X);
fp_add(t1, t1, t2);
}
return (bn_cmp(t0, t1) == 0);
}
function sm2_point_neg(R, P) {
bn_copy(R.X, P.X);
fp_neg(R.Y, P.Y);
bn_copy(R.Z, P.Z);
}
function sm2_point_double(R, P) {
if (sm2_point_is_at_infinity(P)) {
return sm2_point_copy(R, P);
}
var X1 = P.X;
var Y1 = P.Y;
var Z1 = P.Z;
var T1 = bn_new();
var T2 = bn_new();
var T3 = bn_new();
var X3 = bn_new();
var Y3 = bn_new();
var Z3 = bn_new();
fp_sqr(T1, Z1);
fp_sub(T2, X1, T1);
fp_add(T1, X1, T1);
fp_mul(T2, T2, T1);
fp_tri(T2, T2);
fp_dbl(Y3, Y1);
fp_mul(Z3, Y3, Z1);
fp_sqr(Y3, Y3);
fp_mul(T3, Y3, X1);
fp_sqr(Y3, Y3);
fp_div2(Y3, Y3);
fp_sqr(X3, T2);
fp_dbl(T1, T3);
fp_sub(X3, X3, T1);
fp_sub(T1, T3, X3);
fp_mul(T1, T1, T2);
fp_sub(Y3, T1, Y3);
bn_copy(R.X, X3);
bn_copy(R.Y, Y3);
bn_copy(R.Z, Z3);
}
function sm2_point_add(R, P, Q) {
if (sm2_point_is_at_infinity(Q)) {
sm2_point_copy(R, P);
return;
}
if (sm2_point_is_at_infinity(P)) {
sm2_point_copy(R, Q);
return;
}
var X1 = P.X;
var Y1 = P.Y;
var Z1 = P.Z;
var x2 = Q.X;
var y2 = Q.Y;
var T1 = bn_new();
var T2 = bn_new();
var T3 = bn_new();
var T4 = bn_new();
var X3 = bn_new();
var Y3 = bn_new();
var Z3 = bn_new();
fp_sqr(T1, Z1);
fp_mul(T2, T1, Z1);
fp_mul(T1, T1, x2);
fp_mul(T2, T2, y2);
fp_sub(T1, T1, X1);
fp_sub(T2, T2, Y1);
if (bn_is_zero(T1)) {
if (bn_is_zero(T2)) {
return sm2_point_double(R, Q);
} else {
return sm2_point_set_infinity(R);
}
}
fp_mul(Z3, Z1, T1);
fp_sqr(T3, T1);
fp_mul(T4, T3, T1);
fp_mul(T3, T3, X1);
fp_dbl(T1, T3);
fp_sqr(X3, T2);
fp_sub(X3, X3, T1);
fp_sub(X3, X3, T4);
fp_sub(T3, T3, X3);
fp_mul(T3, T3, T2);
fp_mul(T4, T4, Y1);
fp_sub(Y3, T3, T4);
bn_copy(R.X, X3);
bn_copy(R.Y, Y3);
bn_copy(R.Z, Z3);
}
function sm2_point_mul(R, k, P) {
var bits = bn_to_bits(k);
var Q = sm2_point_new();
for (var i = 0; i < bits.length; i++) {
sm2_point_double(Q, Q);
if (bits[i] == 1) {
sm2_point_add(Q, Q, P);
}
}
sm2_point_copy(R, Q);
}
function sm2_point_mul_G(R, k) {
return sm2_point_mul(R, k, SM2_G);
}
function sm2_sig_new() {
var sig = {
r: bn_new(),
s: bn_new(),
};
return sig;
}
function sm2_do_verify(dgst, sig, P) {
if (dgst.length < 32
|| bn_is_zero(sig.r)
|| bn_cmp(sig.r, SM2_N) >= 0
|| bn_is_zero(sig.s)
|| bn_cmp(sig.s, SM2_N) >= 0) {
return false;
}
var e = bn_new();
bn_set_bytes(e, dgst, 0);
if (bn_cmp(e, SM2_N) >= 0) {
bn_sub(e, e, SM2_N);
}
var t = bn_new();
bn_add(t, sig.r, sig.s);
if (bn_cmp(t, SM2_N) == 0) {
return false;
}
var Q1 = sm2_point_new();
var Q2 = sm2_point_new();
sm2_point_mul_G(Q1, s1);
sm2_point_mul(Q2, t, P);
sm2_point_add(Q1, Q1, Q2);
sm2_point_get_affine(Q1, Q1);
bn_add(e, e, Q1.X);
if (bn_cmp(e, SM2_N)) {
bn_sub(e, e, SM2_N);
}
if (bn_cmp(e, SM2_N)) {
bn_sub(e, e, SM2_N);
}
return bn_cmp(e, sig.r) == 0;
}
function sm2_point_test() {
var P = sm2_point_new();
/*
sm2_point_double(P, SM2_G);
sm2_point_double(P, P);
sm2_point_add(P, P, SM2_G);
sm2_point_get_affine(P, P);
console.log(P);
*/
var k = bn_new();
bn_set_word(k, 11);
console.log("mul");
sm2_point_mul(P, SM2_B, SM2_G);
sm2_point_get_affine(P, P);
console.log(P.X);
console.log(P.Y);
console.log(P.Z);
}