diff --git a/js/sm9.js b/js/sm9.js new file mode 100644 index 00000000..05f27d96 --- /dev/null +++ b/js/sm9.js @@ -0,0 +1,1618 @@ +/* + * 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 SM9_P = 0xb640000002a3a6f1d603ab4ff58ec74521f2934b1a7aeedbe56f9b27e351457dn; + +const SM9_N = 0xb640000002a3a6f1d603ab4ff58ec74449f2934b18ea8beee56ee19cd69ecf25n; + +const SM9_P1 = { + X : 0x93DE051D62BF718FF5ED0704487D01D6E1E4086909DC3280E8C4E4817C66DDDDn, + Y : 0x21FE8DDA4F21E607631065125C395BBC1C1C00CBFA6024350C464CD70A3EA616n, + Z : 1n, +}; + +const SM9_P2 = { + X : [0x3722755292130b08d2aab97fd34ec120ee265948d19c17abf9b7213baf82d65bn, + 0x85aef3d078640c98597b6027b441a01ff1dd2c190f5e93c454806c11d8806141n], + Y : [0xa7cf28d519be3da65f3170153d278ff247efba98a71a08116215bba5c999a7c7n, + 0x17509b092e845c1266ba0d262cbee6ed0736a96fa347c8bd856dc76b84ebeb96n], + Z : [1n, 0n], +} + +const SM9_Ppubs = { + X : [0x29DBA116152D1F786CE843ED24A3B573414D2177386A92DD8F14D65696EA5E32n, + 0x9F64080B3084F733E48AFF4B41B565011CE0711C5E392CFB0AB1B6791B94C408n], + Y : [0x41E00A53DDA532DA1A7CE027B7A46F741006E85F5CDFF0730E75C05FB4E3216Dn, + 0x69850938ABEA0112B57329F447E3A0CBAD3E2FDB1A77F335E89E1408D0EF1C25n], + Z : [1n, 0n], +} + +function fp_new() { + return 0n; +} + +function fp_zero() { + return 0n; +} + +function fp_one() { + return 1n; +} + +function fp_from_hex(s) { + return BigInt("0x" + s); +} + +function fp_is_zero(a) { + return a == 0n; +} + +function fp_is_one(a) { + return a == 1n; +} + +function fp_equ(a, b) { + return a == b; +} + +function fp_add(a, b) { + return (a + b) % SM9_P; +} + +function fp_dbl(a) { + return (a + a) % SM9_P; +} + +function fp_tri(a) { + return (a + a + a) % SM9_P; +} + +function fp_div2(a) { + if (a % 2n == 1n) + a += SM9_P; + return a/2n; +} + +function fp_sub(a, b) { + if (a >= b) { + return a - b; + } else { + return SM9_P + a - b; + } +} + +function fp_neg(a) { + if (a == 0n) + return 0n; + return SM9_P - a; +} + +function fp_mul(a, b) { + return (a * b) % SM9_P; +} + +function fp_sqr(a) { + return (a * a) % SM9_P; +} + +function fp_inv(a) { + let u = a; + let v = SM9_P; + let x1 = 1n; + let x2 = 0n; + let q = 0n; + let r = 0n; + let x = 0n; + + while (u != 1) { + q = v / u; + r = v - q * u; + x = x2 - q * x1; + v = u; + u = r; + x2 = x1; + x1 = x; + } + + if (x1 < 0) + return x1 + SM9_P; + else + return x1 % SM9_P; +} + +function fp2_new() { + return [0n, 0n]; +} + +function fp2_set_hex(a, s) { + a[0] = fp_from_hex(s[0]); + a[1] = fp_from_hex(s[1]); +} + +function fp2_from_hex(s) { + let r = fp2_new(); + fp2_set_hex(r, s); + return r; +} + +function fp2_is_zero(a) { + return a[0] == 0n + && a[1] == 0n; +} + +function fp2_is_one(a) { + return a[0] == 1n + && a[1] == 0n; +} + +function fp2_set_zero(a) { + a[0] = 0n; + a[1] = 0n; +} + +function fp2_set_one(a) { + a[0] = 1n; + a[1] = 0n; +} + +function fp2_copy(r, a) { + r[0] = a[0]; + r[1] = a[1]; +} + +function fp2_set(a, a0, a1) { + a[0] = a0; + a[1] = a1; +} + +function fp2_set_u(a) { + a[0] = 0n; + a[1] = 1n; +} + +function fp2_set_5u(a) { + a[0] = 0n; + a[1] = 5n; +} + +function fp2_set_bn(a, a0) { + a[0] = a0 % SM9_P; + a[1] = 0n; +} + +function fp2_equ(a, b) { + return a[0] == b[0] && a[1] == b[1]; +} + +function fp2_add(r, a, b) { + r[0] = fp_add(a[0], b[0]); + r[1] = fp_add(a[1], b[1]); +} + +function fp2_dbl(r, a) { + r[0] = fp_dbl(a[0]); + r[1] = fp_dbl(a[1]); +} + +function fp2_tri(r, a) { + let t = fp2_new(); + fp2_dbl(t, a); + fp2_add(r, t, a); +} + +function fp2_div2(r, a) { + r[0] = fp_div2(a[0]); + r[1] = fp_div2(a[1]); +} + +function fp2_sub(r, a, b) { + r[0] = fp_sub(a[0], b[0]); + r[1] = fp_sub(a[1], b[1]); +} + +function fp2_neg(r, a) { + r[0] = fp_neg(a[0]); + r[1] = fp_neg(a[1]); +} + +function fp2_mul(r, a, b) { + let r0 = fp_sub(fp_mul(a[0], b[0]), fp_dbl(fp_mul(a[1], b[1]))); + let r1 = fp_add(fp_mul(a[0], b[1]), fp_mul(a[1], b[0])); + r[0] = r0; + r[1] = r1; +} + +function fp2_mul_u(r, a, b) { + let r0 = fp_neg(fp_dbl(fp_add(fp_mul(a[0], b[1]), fp_mul(a[1], b[0])))); + let r1 = fp_sub(fp_mul(a[0], b[0]), fp_dbl(fp_mul(a[1], b[1]))); + r[0] = r0; + r[1] = r1; +} + +function fp2_mul_fp(r, a, k) { + r[0] = fp_mul(a[0], k); + r[1] = fp_mul(a[1], k); +} + +function fp2_sqr(r, a) { + let r0 = fp_sub(fp_sqr(a[0]), fp_dbl(fp_sqr(a[1]))); + let r1 = fp_dbl(fp_mul(a[0], a[1])); + r[0] = r0; + r[1] = r1; +} + +function fp2_sqr_u(r, a) { + let r0 = fp_neg(fp_dbl(fp_dbl(fp_mul(a[0], a[1])))); + let r1 = fp_sub(fp_sqr(a[0]), fp_dbl(fp_sqr(a[1]))); + r[0] = r0; + r[1] = r1; +} + +function fp2_inv(r, a) { + if (fp_is_zero(a[0])) { + r[0] = fp_zero(); + r[1] = fp_neg(fp_inv(fp_dbl(a[1]))); + + } else if (fp_is_zero(a[1])) { + r[0] = fp_inv(a[0]); + r[1] = fp_zero(); + + } else { + let k = fp_inv(fp_add(fp_sqr(a[0]), fp_dbl(fp_sqr(a[1])))); + r[0] = fp_mul(a[0], k); + r[1] = fp_neg(fp_mul(a[1], k)); + } +} + +function fp2_div(r, a, b) { + let t = fp2_new(); + fp2_inv(t, b); + fp2_mul(r, a, t); +} + +function fp4_new() { + return [fp2_new(), fp2_new()]; +} + +function fp4_set_hex(r, s) { + fp2_set_hex(r[0], s.slice(0, 2)); + fp2_set_hex(r[1], s.slice(2, 4)); +} + +function fp4_from_hex(s) { + let r = fp4_new(); + fp4_set_hex(r, s); + return r; +} + +function fp4_is_zero(a) { + return fp2_is_zero(a[0]) + && fp2_is_zero(a[1]); +} + +function fp4_is_one(a) { + return fp2_is_one(a[0]) + && fp2_is_zero(a[1]); +} + +function fp4_set_zero(a) { + fp2_set_zero(a[0]); + fp2_set_zero(a[1]); +} + +function fp4_set_one(a) { + fp2_set_one(a[0]); + fp2_set_zero(a[1]); +} + +function fp4_set_bn(r, a) { + fp2_set_bn(r[0], a); + fp2_set_zero(r[1]); +} + +function fp4_set_fp2(r, a) { + fp2_copy(r[0], a); + fp2_set_zero(r[1]); +} + +function fp4_set(r, a0, a1) { + fp2_copy(r[0], a0); + fp2_copy(r[1], a1); +} + +function fp4_copy(r, a) { + fp2_copy(r[0], a[0]); + fp2_copy(r[1], a[1]); +} + +function fp4_set_u(r) { + fp2_set_u(r[0]); + fp2_set_zero(r[1]); +} + +function fp4_set_v(r) { + fp2_set_zero(r[0]); + fp2_set_one(r[1]); +} + +function fp4_equ(a, b) { + return fp2_equ(a[0], b[0]) + && fp2_equ(a[1], b[1]); +} + +function fp4_add(r, a, b) { + fp2_add(r[0], a[0], b[0]); + fp2_add(r[1], a[1], b[1]); +} + +function fp4_dbl(r, a) { + fp2_dbl(r[0], a[0]); + fp2_dbl(r[1], a[1]); +} + +function fp4_sub(r, a, b) { + fp2_sub(r[0], a[0], b[0]); + fp2_sub(r[1], a[1], b[1]); +} + +function fp4_neg(r, a) { + fp2_neg(r[0], a[0]); + fp2_neg(r[1], a[1]); +} + +function fp4_mul(r, a, b) { + let r0 = fp2_new(); + let r1 = fp2_new(); + let t = fp2_new(); + + fp2_mul(r0, a[0], b[0]); + fp2_mul_u(t, a[1], b[1]); + fp2_add(r0, r0, t); + + fp2_mul(r1, a[0], b[1]); + fp2_mul(t, a[1], b[0]); + fp2_add(r1, r1, t); + + fp2_copy(r[0], r0); + fp2_copy(r[1], r1); +} + +function fp4_mul_fp(r, a, k) { + fp2_mul_fp(r[0], a[0], k); + fp2_mul_fp(r[1], a[1], k); +} + +function fp4_mul_fp2(r, a, b) { + fp2_mul(r[0], a[0], b); + fp2_mul(r[1], a[1], b); +} + +function fp4_mul_v(r, a, b) { + let r0 = fp2_new(); + let r1 = fp2_new(); + let t = fp2_new(); + + fp2_mul_u(r0, a[0], b[1]); + fp2_mul_u(t, a[1], b[0]); + fp2_add(r0, r0, t); + + fp2_mul(r1, a[0], b[0]); + fp2_mul_u(t, a[1], b[1]); + fp2_add(r1, r1, t); + + fp2_copy(r[0], r0); + fp2_copy(r[1], r1); +} + +function fp4_sqr(r, a) { + let r0 = fp2_new(); + let r1 = fp2_new(); + let t = fp2_new(); + + fp2_sqr(r0, a[0]); + fp2_sqr_u(t, a[1]); + fp2_add(r0, r0, t); + + fp2_mul(r1, a[0], a[1]); + fp2_dbl(r1, r1); + fp2_copy(r[0], r0); + fp2_copy(r[1], r1); +} + +function fp4_sqr_v(r, a) { + let r0 = fp2_new(); + let r1 = fp2_new(); + let t = fp2_new(); + + fp2_mul_u(t, a[0], a[1]); + fp2_dbl(r0, t); + + fp2_sqr(r1, a[0]); + fp2_sqr_u(t, a[1]); + fp2_add(r1, r1, t); + + fp2_copy(r[0], r0); + fp2_copy(r[1], r1); +} + +function fp4_inv(r, a) { + let r0 = fp2_new(); + let r1 = fp2_new(); + let k = fp2_new(); + + fp2_sqr_u(k, a[1]); + fp2_sqr(r0, a[0]); + fp2_sub(k, k, r0); + fp2_inv(k, k); + + fp2_mul(r0, a[0], k); + fp2_neg(r0, r0); + + fp2_mul(r1, a[1], k); + + fp2_copy(r[0], r0); + fp2_copy(r[1], r1); +} + +function fp12_new() { + return [fp4_new(), + fp4_new(), + fp4_new()]; +} + +function fp12_set_hex(r, s) { + fp4_set_hex(r[0], s.slice(0, 4)); + fp4_set_hex(r[1], s.slice(4, 8)); + fp4_set_hex(r[2], s.slice(8, 12)); +} + +function fp12_from_hex(s) { + let r = fp12_new(); + fp12_set_hex(r, s); + return r; +} + +function fp12_is_zero(a) { + return fp4_is_zero(a[0]) + && fp4_is_zero(a[1]) + && fp4_is_zero(a[2]); +} + +function fp12_is_one(a) { + return fp4_is_one(a[0]) + && fp4_is_zero(a[1]) + && fp4_is_zero(a[2]); +} + +function fp12_set_zero(a) { + fp4_set_zero(a[0]); + fp4_set_zero(a[1]); + fp4_set_zero(a[2]); +} + +function fp12_set_one(a) { + fp4_set_one(a[0]); + fp4_set_zero(a[1]); + fp4_set_zero(a[2]); +} + +function fp12_copy(r, a) { + fp4_copy(r[0], a[0]); + fp4_copy(r[1], a[1]); + fp4_copy(r[2], a[2]); +} + +function fp12_set(r, a0, a1, a2) { + fp4_copy(r[0], a0); + fp4_copy(r[1], a1); + fp4_copy(r[2], a2); +} + +function fp12_set_fp4(r, a) { + fp4_copy(r[0], a); + fp4_set_zero(r[1]); + fp4_set_zero(r[2]); +} + +function fp12_set_fp2(r, a) { + fp4_set_fp2(r[0], a); + fp4_set_zero(r[1]); + fp4_set_zero(r[2]); +} + +function fp12_set_bn(r, a) { + fp4_set_bn(r[0], a); + fp4_set_zero(r[1]); + fp4_set_zero(r[2]); +} + +function fp12_set_u(r) { + fp4_set_u(r[0]); + fp4_set_zero(r[1]); + fp4_set_zero(r[2]); +} + +function fp12_set_v(r) { + fp4_set_v(r[0]); + fp4_set_zero(r[1]); + fp4_set_zero(r[2]); +} + +function fp12_set_w(r) { + fp4_set_zero(r[0]); + fp4_set_one(r[1]); + fp4_set_zero(r[2]); +} + +function fp12_set_w_sqr(r) { + fp4_set_zero(r[0]); + fp4_set_zero(r[1]); + fp4_set_one(r[2]); +} + +function fp12_equ(a, b) { + return fp4_equ(a[0], b[0]) + && fp4_equ(a[1], b[1]) + && fp4_equ(a[2], b[2]); +} + +function fp12_add(r, a, b) { + fp4_add(r[0], a[0], b[0]); + fp4_add(r[1], a[1], b[1]); + fp4_add(r[2], a[2], b[2]); +} + +function fp12_dbl(r, a) { + fp4_dbl(r[0], a[0]); + fp4_dbl(r[1], a[1]); + fp4_dbl(r[2], a[2]); +} + +function fp12_tri(r, a) { + let t = fp12_new(); + fp12_dbl(t, a); + fp12_add(r, t, a); +} + +function fp12_sub(r, a, b) { + fp4_sub(r[0], a[0], b[0]); + fp4_sub(r[1], a[1], b[1]); + fp4_sub(r[2], a[2], b[2]); +} + +function fp12_neg(r, a) { + fp4_neg(r[0], a[0]); + fp4_neg(r[1], a[1]); + fp4_neg(r[2], a[2]); +} + +function fp12_mul(r, a, b) { + let r0 = fp12_new(); + let r1 = fp12_new(); + let r2 = fp12_new(); + let t = fp12_new(); + + fp4_mul(r0, a[0], b[0]); + fp4_mul_v(t, a[1], b[2]); + fp4_add(r0, r0, t); + fp4_mul_v(t, a[2], b[1]); + fp4_add(r0, r0, t); + + fp4_mul(r1, a[0], b[1]); + fp4_mul(t, a[1], b[0]); + fp4_add(r1, r1, t); + fp4_mul_v(t, a[2], b[2]); + fp4_add(r1, r1, t); + + fp4_mul(r2, a[0], b[2]); + fp4_mul(t, a[1], b[1]); + fp4_add(r2, r2, t); + fp4_mul(t, a[2], b[0]); + fp4_add(r2, r2, t); + + fp4_copy(r[0], r0); + fp4_copy(r[1], r1); + fp4_copy(r[2], r2); +} + +function fp12_sqr(r, a, b) { + let r0 = fp12_new(); + let r1 = fp12_new(); + let r2 = fp12_new(); + let t = fp12_new(); + + fp4_sqr(r0, a[0]); + fp4_mul_v(t, a[1], a[2]); + fp4_dbl(t, t); + fp4_add(r0, r0, t); + + fp4_mul(r1, a[0], a[1]); + fp4_dbl(r1, r1); + fp4_sqr_v(t, a[2]); + fp4_add(r1, r1, t); + + fp4_mul(r2, a[0], a[2]); + fp4_dbl(r2, r2); + fp4_sqr(t, a[1]); + fp4_add(r2, r2, t); + + fp4_copy(r[0], r0); + fp4_copy(r[1], r1); + fp4_copy(r[2], r2); +} + +function fp12_inv(r, a) { + + if (fp4_is_zero(a[2])) { + let k = fp4_new(); + let t = fp4_new(); + + fp4_sqr(k, a[0]); + fp4_mul(k, k, a[0]); + fp4_sqr_v(t, a[1]); + fp4_mul(t, t, a[1]); + fp4_add(k, k, t); + fp4_inv(k, k); + + fp4_sqr(r[2], a[1]); + fp4_mul(r[2], r[2], k); + + fp4_mul(r[1], a[0], a[1]); + fp4_mul(r[1], r[1], k); + fp4_neg(r[1], r[1]); + + fp4_sqr(r[0], a[0]); + fp4_mul(r[0], r[0], k); + + } else { + let t0 = fp4_new(); + let t1 = fp4_new(); + let t2 = fp4_new(); + let t3 = fp4_new(); + + fp4_sqr(t0, a[1]); + fp4_mul(t1, a[0], a[2]); + fp4_sub(t0, t0, t1); + + fp4_mul(t1, a[0], a[1]); + fp4_sqr_v(t2, a[2]); + fp4_sub(t1, t1, t2); + + fp4_sqr(t2, a[0]); + fp4_mul_v(t3, a[1], a[2]); + fp4_sub(t2, t2, t3); + + fp4_sqr(t3, t1); + fp4_mul(r[0], t0, t2); + fp4_sub(t3, t3, r[0]); + fp4_inv(t3, t3); + fp4_mul(t3, a[2], t3); + + fp4_mul(r[0], t2, t3); + + fp4_mul(r[1], t1, t3); + fp4_neg(r[1], r[1]); + + fp4_mul(r[2], t0, t3); + } +} + + +function fp12_pow(r, a, k) { + k %= SM9_P; + if (k == 0n) { + fp12_set_one(r); + return; + } + + let t = fp12_new(); + let kbits = k.toString(2); + + fp12_copy(t, a); + for (let i = 1; i < kbits.length; i++) { + fp12_sqr(t, t); + if (kbits[i] == 1) { + fp12_mul(t, t, a); + } + } + fp12_copy(r, t); +} + +function fp2_conjugate(r, a) { + r[0] = a[0]; + r[1] = fp_neg(a[1]); +} + +function fp2_frobenius(r, a) { + return fp2_conjugate(r, a); +} + +function fp4_frobenius(r, a) { + const beta = [ + 0x6c648de5dc0a3f2cf55acc93ee0baf159f9d411806dc5177f5b21fd3da24d011n, + 0n]; + fp2_conjugate(r[0], a[0]); + fp2_conjugate(r[1], a[1]); + fp2_mul(r[1], r[1], beta); +} + +function fp4_conjugate(r, a) { + fp2_copy(r[0], a[0]); + fp2_neg(r[1], a[1]); +} + +function fp4_frobenius2(r, a) { + return fp4_conjugate(r, a); +} + +function fp4_frobenius3(r, a) { + const beta = [ + 0x6c648de5dc0a3f2cf55acc93ee0baf159f9d411806dc5177f5b21fd3da24d011n, + 0n]; + fp2_conjugate(r[0], a[0]); + fp2_conjugate(r[1], a[1]); + fp2_mul(r[1], r[1], beta); + fp2_neg(r[1], r[1]); +} + +function fp12_frobenius(r, x) { + const alpha1 = 0x3f23ea58e5720bdb843c6cfa9c08674947c5c86e0ddd04eda91d8354377b698bn; + const alpha2 = 0xf300000002a3a6f2780272354f8b78f4d5fc11967be65334n; + const alpha3 = 0x6c648de5dc0a3f2cf55acc93ee0baf159f9d411806dc5177f5b21fd3da24d011n; + const alpha4 = 0xf300000002a3a6f2780272354f8b78f4d5fc11967be65333n; + const alpha5 = 0x2d40a38cf6983351711e5f99520347cc57d778a9f8ff4c8a4c949c7fa2a96686n; + + let xa = x[0]; + let xb = x[1]; + let xc = x[2]; + let ra = fp4_new(); + let rb = fp4_new(); + let rc = fp4_new(); + + fp2_conjugate(ra[0], xa[0]); + fp2_conjugate(ra[1], xa[1]); + fp2_mul_fp(ra[1], ra[1], alpha3); + + fp2_conjugate(rb[0], xb[0]); + fp2_mul_fp(rb[0], rb[0], alpha1); + fp2_conjugate(rb[1], xb[1]); + fp2_mul_fp(rb[1], rb[1], alpha4); + + fp2_conjugate(rc[0], xc[0]); + fp2_mul_fp(rc[0], rc[0], alpha2); + fp2_conjugate(rc[1], xc[1]); + fp2_mul_fp(rc[1], rc[1], alpha5); + + fp12_set(r, ra, rb, rc); +} + +function fp12_frobenius2(r, x) { + const alpha2 = 0xf300000002a3a6f2780272354f8b78f4d5fc11967be65334n; + const alpha4 = 0xf300000002a3a6f2780272354f8b78f4d5fc11967be65333n; + + let a = fp4_new(); + let b = fp4_new(); + let c = fp4_new(); + + fp4_conjugate(a, x[0]); + fp4_conjugate(b, x[1]); + fp4_mul_fp(b, b, alpha2); + fp4_conjugate(c, x[2]); + fp4_mul_fp(c, c, alpha4); + + fp4_copy(r[0], a); + fp4_copy(r[1], b); + fp4_copy(r[2], c); +} + +function fp12_frobenius3(r, x) { + const beta = [ + 0x6c648de5dc0a3f2cf55acc93ee0baf159f9d411806dc5177f5b21fd3da24d011n, + 0n]; + + let ra = fp4_new(); + let rb = fp4_new(); + let rc = fp4_new(); + + let xa = x[0]; + let xb = x[1]; + let xc = x[2]; + + fp2_conjugate(ra[0], xa[0]); + fp2_conjugate(ra[1], xa[1]); + fp2_mul(ra[1], ra[1], beta); + fp2_neg(ra[1], ra[1]); + + fp2_conjugate(rb[0], xb[0]); + fp2_mul(rb[0], rb[0], beta); + fp2_conjugate(rb[1], xb[1]); + + fp2_conjugate(rc[0], xc[0]); + fp2_neg(rc[0], rc[0]); + fp2_conjugate(rc[1], xc[1]); + fp2_mul(rc[1], rc[1], beta); + + fp4_copy(r[0], ra); + fp4_copy(r[1], rb); + fp4_copy(r[2], rc); +} + +function fp12_frobenius6(r, x) { + let a = fp4_new(); + let b = fp4_new(); + let c = fp4_new(); + + fp4_copy(a, x[0]); + fp4_copy(b, x[1]); + fp4_copy(c, x[2]); + + fp4_conjugate(a, a); + fp4_conjugate(b, b); + fp4_neg(b, b); + fp4_conjugate(c, c); + + fp4_copy(r[0], a); + fp4_copy(r[1], b); + fp4_copy(r[2], c); +} + +function point_new() { + let R = { + X : 1n, + Y : 1n, + Z : 0n, + } + return R; +} + +function point_set_hex(R, s) { + R.X = fp_from_hex(s[0]); + R.Y = fp_from_hex(s[1]); + R.Z = fp_one(); +} + +function point_from_hex(s) { + let R = point_new(); + point_set_hex(R, s); + return R; +} + +function point_copy(R, P) { + R.X = P.X; + R.Y = P.Y; + R.Z = P.Z; +} + +function point_is_at_infinity(P) { + return P.Z == 0n; +} + +function point_set_infinity(R) { + R.X = 1n; + R.Y = 1n; + R.Z = 0n; +} + +function point_get_affine(R, P) { + point_copy(R, P); + if (R.Z == 0 || R.Z == 1) { + return; + } + R.Z = fp_inv(R.Z); + R.Y = fp_mul(R.Y, R.Z); + R.Z = fp_sqr(R.Z); + R.X = fp_mul(R.X, R.Z); + R.Y = fp_mul(R.Y, R.Z); + R.Z = 1n; +} + +function point_equ(P, Q) { + let t1 = fp_sqr(P.Z); + let t2 = fp_sqr(Q.Z); + let t3 = fp_mul(P.X, t2); + let t4 = fp_mul(Q.X, t1); + if (t3 != t4) { + return false; + } + t1 = fp_mul(t1, P.Z); + t2 = fp_mul(t2, Q.Z); + t3 = fp_mul(P.Y, t2); + t4 = fp_mul(Q.Y, t1); + return t3 == t4; +} + +function point_is_on_curve(P) { + let t0 = fp_new(); + let t1 = fp_new(); + let t2 = fp_new(); + + if (fp_is_one(P.Z)) { + t0 = fp_sqr(P.Y); + t1 = fp_sqr(P.X); + t1 = fp_mul(t1, P.X); + t1 = fp_add(t1, 5n); + } else { + t0 = fp_sqr(P.X); + t0 = fp_mul(t0, P.X); + t1 = fp_sqr(P.Z); + t2 = fp_sqr(t1); + t1 = fp_mul(t1, t2); + t1 = fp_mul(t1, 5n); + t1 = fp_add(t0, t1); + t0 = fp_sqr(P.Y); + } + + return fp_equ(t0, t1); +} + +function point_double(R, P) { + if (point_is_at_infinity(P)) { + return point_copy(R, P); + } + let X1 = P.X; + let Y1 = P.Y; + let Z1 = P.Z; + let T1 = fp_new(); + let T2 = fp_new(); + let T3 = fp_new(); + let X3 = fp_new(); + let Y3 = fp_new(); + let Z3 = fp_new(); + + T2 = fp_sqr(X1); + T2 = fp_tri(T2); + Y3 = fp_dbl(Y1); + Z3 = fp_mul(Y3, Z1); + Y3 = fp_sqr(Y3); + T3 = fp_mul(Y3, X1); + Y3 = fp_sqr(Y3); + Y3 = fp_div2(Y3); + X3 = fp_sqr(T2); + T1 = fp_dbl(T3); + X3 = fp_sub(X3, T1); + T1 = fp_sub(T3, X3); + T1 = fp_mul(T1, T2); + Y3 = fp_sub(T1, Y3); + + R.X = X3; + R.Y = Y3; + R.Z = Z3; +} + +function point_add(R, P, Q) { + if (point_is_at_infinity(Q)) { + point_copy(R, P); + return; + } + if (point_is_at_infinity(P)) { + point_copy(R, Q); + return; + } + + let X1 = P.X; + let Y1 = P.Y; + let Z1 = P.Z; + let x2 = Q.X; + let y2 = Q.Y; + let T1 = 0n; + let T2 = 0n; + let T3 = 0n; + let T4 = 0n; + let X3 = 0n; + let Y3 = 0n; + let Z3 = 0n; + + T1 = fp_sqr(Z1); + T2 = fp_mul(T1, Z1); + T1 = fp_mul(T1, x2); + T2 = fp_mul(T2, y2); + T1 = fp_sub(T1, X1); + T2 = fp_sub(T2, Y1); + if (T1 == 0n) { + if (T2 == 0n) { + return point_double(R, Q); + } else { + return point_set_infinity(R); + } + } + Z3 = fp_mul(Z1, T1); + T3 = fp_sqr(T1); + T4 = fp_mul(T3, T1); + T3 = fp_mul(T3, X1); + T1 = fp_dbl(T3); + X3 = fp_sqr(T2); + X3 = fp_sub(X3, T1); + X3 = fp_sub(X3, T4); + T3 = fp_sub(T3, X3); + T3 = fp_mul(T3, T2); + T4 = fp_mul(T4, Y1); + Y3 = fp_sub(T3, T4); + + R.X = X3; + R.Y = Y3; + R.Z = Z3; +} + +function point_sub(R, P, Q) { + let T = point_new(); + point_neg(T, Q); + point_add(R, P, T); +} + +function point_neg(R, P) { + R.X = P.X; + R.Y = fp_neg(P.Y); + R.Z = P.Z; +} + +function point_mul(R, k, P) { + let Q = point_new(); + let kbits = k.toString(2); + for (let i = 0; i < kbits.length; i++) { + point_double(Q, Q); + if (kbits[i] == 1) { + point_add(Q, Q, P); + } + } + point_copy(R, Q); +} + +function point_mul_G(R, k) { + point_mul(R, k, SM9_P1) +} + +function twist_point_new() { + let R = { + X : fp2_new(), + Y : fp2_new(), + Z : fp2_new(), + }; + fp2_set_one(R.X); + fp2_set_one(R.Y); + fp2_set_zero(R.Z); + return R; +} + +function twist_point_set_hex(R, s) { + fp2_set_hex(R.X, s.slice(0, 2)); + fp2_set_hex(R.Y, s.slice(2, 4)); + fp2_set_one(R.Z); +} + +function twist_point_from_hex(s) { + let R = twist_point_new(); + twist_point_set_hex(R, s); + return R; +} + +function twist_point_copy(R, P) { + fp2_copy(R.X, P.X); + fp2_copy(R.Y, P.Y); + fp2_copy(R.Z, P.Z); +} + +function twist_point_is_at_infinity(P) { + return fp2_is_zero(P.Z); +} + +function twist_point_set_infinity(R) { + fp2_set_one(R.X); + fp2_set_one(R.Y); + fp2_set_zero(R.Z); +} + +function twist_point_get_affine(R, P) { + twist_point_copy(R, P); + if (fp2_is_zero(R.Z) || fp2_is_one(R.Z)) { + return; + } + fp2_inv(R.Z, R.Z); + fp2_mul(R.Y, R.Y, R.Z); + fp2_sqr(R.Z, R.Z); + fp2_mul(R.X, R.X, R.Z); + fp2_mul(R.Y, R.Y, R.Z); + fp2_set_one(R.Z); +} + +function twist_point_equ(P, Q) { + let t1 = fp2_new(); + let t2 = fp2_new(); + let t3 = fp2_new(); + let t4 = fp2_new(); + + fp2_sqr(t1, P.Z); + fp2_sqr(t2, Q.Z); + fp2_mul(t3, P.X, t2); + fp2_mul(t4, Q.X, t1); + if (!fp2_equ(t3, t4)) { + return false; + } + fp2_mul(t1, t1, P.Z); + fp2_mul(t2, t2, Q.Z); + fp2_mul(t3, P.Y, t2); + fp2_mul(t4, Q.Y, t1); + return fp2_equ(t3, t4); +} + +function twist_point_is_on_curve(P) { + let t0 = fp2_new(); + let t1 = fp2_new(); + let t2 = fp2_new(); + + if (fp2_is_one(P.Z)) { + fp2_sqr(t0, P.Y); + fp2_sqr(t1, P.X); + fp2_mul(t1, t1, P.X); + fp2_add(t1, t1, [0n, 5n]); + } else { + fp2_sqr(t0, P.X); + fp2_mul(t0, t0, P.X); + fp2_sqr(t1, P.Z); + fp2_sqr(t2, t1); + fp2_mul(t1, t1, t2); + fp2_mul(t1, t1, [0n, 5n]); + fp2_add(t1, t0, t1); + fp2_sqr(t0, P.Y); + } + + return fp2_equ(t0, t1); +} + +function twist_point_sub(R, P, Q) { + let T = twist_point_new(); + twist_point_neg(T, Q); + twist_point_add(R, P, T); +} + +function twist_point_neg(R, P) { + fp2_copy(R.X, P.X); + fp2_neg(R.Y, P.Y); + fp2_copy(R.Z, P.Z); +} + +function twist_point_double(R, P) { + if (twist_point_is_at_infinity(P)) { + return twist_point_copy(R, P); + } + let X1 = P.X; + let Y1 = P.Y; + let Z1 = P.Z; + let T1 = fp2_new(); + let T2 = fp2_new(); + let T3 = fp2_new(); + let X3 = fp2_new(); + let Y3 = fp2_new(); + let Z3 = fp2_new(); + + fp2_sqr(T2, X1); + fp2_tri(T2, T2); + fp2_dbl(Y3, Y1); + fp2_mul(Z3, Y3, Z1); + fp2_sqr(Y3, Y3); + fp2_mul(T3, Y3, X1); + fp2_sqr(Y3, Y3); + fp2_div2(Y3, Y3); + fp2_sqr(X3, T2); + fp2_dbl(T1, T3); + fp2_sub(X3, X3, T1); + fp2_sub(T1, T3, X3); + fp2_mul(T1, T1, T2); + fp2_sub(Y3, T1, Y3); + + fp2_copy(R.X, X3); + fp2_copy(R.Y, Y3); + fp2_copy(R.Z, Z3); +} + +function twist_point_add(R, P, Q) { + if (twist_point_is_at_infinity(Q)) { + twist_point_copy(R, P); + return; + } + if (twist_point_is_at_infinity(P)) { + twist_point_copy(R, Q); + return; + } + + let X1 = P.X; + let Y1 = P.Y; + let Z1 = P.Z; + let x2 = Q.X; + let y2 = Q.Y; + let T1 = fp2_new(); + let T2 = fp2_new(); + let T3 = fp2_new(); + let T4 = fp2_new(); + let X3 = fp2_new(); + let Y3 = fp2_new(); + let Z3 = fp2_new(); + + fp2_sqr(T1, Z1); + fp2_mul(T2, T1, Z1); + fp2_mul(T1, T1, x2); + fp2_mul(T2, T2, y2); + fp2_sub(T1, T1, X1); + fp2_sub(T2, T2, Y1); + if (fp2_is_zero(T1)) { + if (fp2_is_zero(T2)) { + return twist_point_double(R, Q); + } else { + return twist_point_set_infinity(R); + } + } + fp2_mul(Z3, Z1, T1); + fp2_sqr(T3, T1); + fp2_mul(T4, T3, T1); + fp2_mul(T3, T3, X1); + fp2_dbl(T1, T3); + fp2_sqr(X3, T2); + fp2_sub(X3, X3, T1); + fp2_sub(X3, X3, T4); + fp2_sub(T3, T3, X3); + fp2_mul(T3, T3, T2); + fp2_mul(T4, T4, Y1); + fp2_sub(Y3, T3, T4); + + fp2_copy(R.X, X3); + fp2_copy(R.Y, Y3); + fp2_copy(R.Z, Z3); +} + +function twist_point_add_full(R, P, Q) { + if (twist_point_is_at_infinity(Q)) { + twist_point_copy(R, P); + return; + } + if (twist_point_is_at_infinity(P)) { + twist_point_copy(R, Q); + return; + } + + let X1 = P.X; + let Y1 = P.Y; + let Z1 = P.Z; + let X2 = Q.X; + let Y2 = Q.Y; + let Z2 = Q.Z; + let T1 = fp2_new(); + let T2 = fp2_new(); + let T3 = fp2_new(); + let T4 = fp2_new(); + let T5 = fp2_new(); + let T6 = fp2_new(); + let T7 = fp2_new(); + let T8 = fp2_new(); + + fp2_sqr(T1, Z1); + fp2_sqr(T2, Z2); + fp2_mul(T3, X2, T1); + fp2_mul(T4, X1, T2); + fp2_add(T5, T3, T4); + fp2_sub(T3, T3, T4); + fp2_mul(T1, T1, Z1); + fp2_mul(T1, T1, Y2); + fp2_mul(T2, T2, Z2); + fp2_mul(T2, T2, Y1); + fp2_add(T6, T1, T2); + fp2_sub(T1, T1, T2); + + if (fp2_is_zero(T1) && fp2_is_zero(T3)) { + return twist_point_double(R, P); + } + if (fp2_is_zero(T1) && fp2_is_zero(T6)) { + return twist_point_set_infinity(R); + } + + fp2_sqr(T6, T1); + fp2_mul(T7, T3, Z1); + fp2_mul(T7, T7, Z2); + fp2_sqr(T8, T3); + fp2_mul(T5, T5, T8); + fp2_mul(T3, T3, T8); + fp2_mul(T4, T4, T8); + fp2_sub(T6, T6, T5); + fp2_sub(T4, T4, T6); + fp2_mul(T1, T1, T4); + fp2_mul(T2, T2, T3); + fp2_sub(T1, T1, T2); + + fp2_copy(R.X, T6); + fp2_copy(R.Y, T1); + fp2_copy(R.Z, T7); +} + +function twist_point_mul(R, k, P) { + let Q = twist_point_new(); + let kbits = k.toString(2); + for (let i = 0; i < kbits.length; i++) { + twist_point_double(Q, Q); + if (kbits[i] == 1) { + twist_point_add(Q, Q, P); + } + } + twist_point_copy(R, Q); +} + +function twist_point_mul_G(R, k) { + twist_point_mul(R, k, SM9_P2); +} + +function eval_g_tangent(num, den, P, Q) { + let XP = P.X; + let YP = P.Y; + let ZP = P.Z; + let xQ = Q.X; + let yQ = Q.Y; + let a0 = num[0][0]; + let a1 = num[0][1]; + let a4 = num[2][0]; + let b1 = den[0][1]; + let t0 = fp2_new(); + let t1 = fp2_new(); + let t2 = fp2_new(); + + fp12_set_zero(num); + fp12_set_zero(den); + + fp2_sqr(t0, ZP); + fp2_mul(t1, t0, ZP); + fp2_mul(b1, t1, YP); + + fp2_mul_fp(t2, b1, yQ); + fp2_neg(a1, t2); + + fp2_sqr(t1, XP); + fp2_mul(t0, t0, t1); + fp2_mul_fp(t0, t0, xQ); + fp2_tri(t0, t0); + fp2_div2(a4, t0); + + fp2_mul(t1, t1, XP); + fp2_tri(t1, t1); + fp2_div2(t1, t1); + fp2_sqr(t0, YP); + fp2_sub(a0, t0, t1); +} + +function eval_g_line(num, den, T, P, Q) { + let XT = T.X; + let YT = T.Y; + let ZT = T.Z; + let XP = P.X; + let YP = P.Y; + let ZP = P.Z; + let xQ = Q.X; + let yQ = Q.Y; + let a0 = num[0][0]; + let a1 = num[0][1]; + let a4 = num[2][0]; + let b1 = den[0][1]; + let T0 = fp2_new(); + let T1 = fp2_new(); + let T2 = fp2_new(); + let T3 = fp2_new(); + let T4 = fp2_new(); + + fp12_set_zero(num); + fp12_set_zero(den); + + fp2_sqr(T0, ZP); + fp2_mul(T1, T0, XT); + fp2_mul(T0, T0, ZP); + fp2_sqr(T2, ZT); + fp2_mul(T3, T2, XP); + fp2_mul(T2, T2, ZT); + fp2_mul(T2, T2, YP); + fp2_sub(T1, T1, T3); + fp2_mul(T1, T1, ZT); + fp2_mul(T1, T1, ZP); + fp2_mul(T4, T1, T0); + fp2_copy(b1, T4); + fp2_mul(T1, T1, YP); + fp2_mul(T3, T0, YT); + fp2_sub(T3, T3, T2); + fp2_mul(T0, T0, T3); + fp2_mul_fp(T0, T0, xQ); + fp2_copy(a4, T0); + fp2_mul(T3, T3, XP); + fp2_mul(T3, T3, ZP); + fp2_sub(T1, T1, T3); + fp2_copy(a0, T1); + fp2_mul_fp(T2, T4, yQ); + fp2_neg(T2, T2); + fp2_copy(a1, T2); +} + +function twist_point_pi1(R, P) { + const c = 0x3f23ea58e5720bdb843c6cfa9c08674947c5c86e0ddd04eda91d8354377b698bn; + fp2_conjugate(R.X, P.X); + fp2_conjugate(R.Y, P.Y); + fp2_conjugate(R.Z, P.Z); + fp2_mul_fp(R.Z, R.Z, c); + +} + +function twist_point_pi2(R, P) { + const c = 0xf300000002a3a6f2780272354f8b78f4d5fc11967be65334n; + fp2_copy(R.X, P.X); + fp2_copy(R.Y, P.Y); + fp2_mul_fp(R.Z, P.Z, c); +} + +function twist_point_neg_pi2(R, P) { + const c = 0xf300000002a3a6f2780272354f8b78f4d5fc11967be65334n; + fp2_copy(R.X, P.X); + fp2_neg(R.Y, P.Y); + fp2_mul_fp(R.Z, P.Z, c); +} + +function final_exponent_hard_part(r, f) { + const a2 = 0xd8000000019062ed0000b98b0cb27659n; + const a3 = 0x2400000000215d941n; + + let t0 = fp12_new(); + let t1 = fp12_new(); + let t2 = fp12_new(); + let t3 = fp12_new(); + + fp12_pow(t0, f, a3); + fp12_inv(t0, t0); + fp12_frobenius(t1, t0); + fp12_mul(t1, t0, t1); + + fp12_mul(t0, t0, t1); + fp12_frobenius(t2, f); + fp12_mul(t3, t2, f); + fp12_pow(t3, t3, 9n); + + fp12_mul(t0, t0, t3); + fp12_pow(t3, f, 4n); + fp12_mul(t0, t0, t3); + fp12_sqr(t2, t2); + fp12_mul(t2, t2, t1); + fp12_frobenius2(t1, f); + fp12_mul(t1, t1, t2); + + fp12_pow(t2, t1, a2); + fp12_mul(t0, t2, t0); + fp12_frobenius3(t1, f); + fp12_mul(t1, t1, t0); + + fp12_copy(r, t1); +} + +function final_exponent(r, f) { + let t0 = fp12_new(); + let t1 = fp12_new(); + + fp12_frobenius6(t0, f); + fp12_inv(t1, f); + fp12_mul(t0, t0, t1); + fp12_frobenius2(t1, t0); + fp12_mul(t0, t0, t1); + final_exponent_hard_part(t0, t0); + + fp12_copy(r, t0); +} + +function sm9_pairing(r, Q, P) { + const abits = '00100000000000000000000000000000000000010000101011101100100111110'; + + let T = twist_point_new(); + let Q1 = twist_point_new(); + let Q2 = twist_point_new(); + + let f_num = fp12_new(); + let f_den = fp12_new(); + let g_num = fp12_new(); + let g_den = fp12_new(); + + twist_point_copy(T, Q); + + fp12_set_one(f_num); + fp12_set_one(f_den); + + for (let i = 0; i < abits.length; i++) { + + fp12_sqr(f_num, f_num); + fp12_sqr(f_den, f_den); + eval_g_tangent(g_num, g_den, T, P); + fp12_mul(f_num, f_num, g_num); + fp12_mul(f_den, f_den, g_den); + + twist_point_double(T, T); + + if (abits[i] == 1) { + eval_g_line(g_num, g_den, T, Q, P); + fp12_mul(f_num, f_num, g_num); + fp12_mul(f_den, f_den, g_den); + twist_point_add(T, T, Q); + } + } + + twist_point_pi1(Q1, Q); + twist_point_neg_pi2(Q2, Q); + + eval_g_line(g_num, g_den, T, Q1, P); + fp12_mul(f_num, f_num, g_num); + fp12_mul(f_den, f_den, g_den); + twist_point_add_full(T, T, Q1); + + eval_g_line(g_num, g_den, T, Q2, P); + fp12_mul(f_num, f_num, g_num); + fp12_mul(f_den, f_den, g_den); + twist_point_add_full(T, T, Q2); + + fp12_inv(f_den, f_den); + fp12_mul(r, f_num, f_den); + + final_exponent(r, r); +} + +function pairing_test() { + + let r = fp12_new(); + const g = [ + "aab9f06a4eeba4323a7833db202e4e35639d93fa3305af73f0f071d7d284fcfb", + "84b87422330d7936eaba1109fa5a7a7181ee16f2438b0aeb2f38fd5f7554e57a", + "4c744e69c4a2e1c8ed72f796d151a17ce2325b943260fc460b9f73cb57c9014b", + "b3129a75d31d17194675a1bc56947920898fbf390a5bf5d931ce6cbb3340f66d", + "93634f44fa13af76169f3cc8fbea880adaff8475d5fd28a75deb83c44362b439", + "1604a3fcfa9783e667ce9fcb1062c2a5c6685c316dda62de0548baa6ba30038b", + "5a1ae172102efd95df7338dbc577c66d8d6c15e0a0158c7507228efb078f42a6", + "67e0e0c2eed7a6993dce28fe9aa2ef56834307860839677f96685f2b44d0911f", + "a01f2c8bee81769609462c69c96aa923fd863e209d3ce26dd889b55e2e3873db", + "38bffe40a22d529a0c66124b2c308dac9229912656f62b4facfced408e02380f", + "28b3404a61908f5d6198815c99af1990c8af38655930058c28c21bb539ce0000", + "4e378fb5561cd0668f906b731ac58fee25738edf09cadc7a29c0abc0177aea6d"]; + + sm9_pairing(r, SM9_Ppubs, SM9_P1); + console.log("test pairing: ", fp12_equ(r, fp12_from_hex(g))); +} +pairing_test(); +