/* * 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();