blindsecp256k1-js/src/index.ts
src/index.tsRaw
1import { randomBytes } from 'crypto'
2import * as BigInteger from 'bigi'
3import { getCurveByName, Point } from 'ecurve'
4import { keccak256 } from "@ethersproject/keccak256"
5
6const ecparams = getCurveByName('secp256k1')
7const G = ecparams.G
8const n = ecparams.n as BigInteger
9
10export { ecparams }
11
12export type UserSecretData = { a: BigInteger, b: BigInteger, f: Point }
13export type UnblindedSignature = { s: BigInteger, f: Point }
14
15export function newBigFromString(s: string) {
16 let a = new BigInteger(null, null, null)
17 a.fromString(s, null)
18 return a
19}
20
21function random(bytes: number) {
22 let k: BigInteger
23 do {
24 k = BigInteger.fromByteArrayUnsigned(randomBytes(bytes)) as unknown as BigInteger
25 } while (k.toString() == "0" && k.gcd(n).toString() != "1")
26 return k
27}
28
29export function newKeyPair() {
30 const sk = random(32)
31 return { sk: sk, pk: G.multiply(sk) }
32}
33
34export function newRequestParameters() {
35 const k = random(32)
36 return { k: k, signerR: G.multiply(k) }
37}
38
39/**
40 * Blinds the message for the signer R.
41 * @param {BigInteger} m
42 * @param {Point} signerR
43 * @returns {struct} {mBlinded: BigInteger, userSecretData: {a: BigInteger, b: BigInteger, f: Point}}
44 */
45export function blind(m: BigInteger, signerR: Point): { mBlinded: BigInteger, userSecretData: UserSecretData } {
46 const u: UserSecretData = { a: BigInteger.ZERO as BigInteger, b: BigInteger.ZERO as BigInteger, f: G }
47 u.a = random(32)
48 u.b = random(32)
49
50 const aR = signerR.multiply(u.a)
51 const bG = G.multiply(u.b)
52 u.f = aR.add(bG)
53
54 const rx = u.f.affineX.mod(n)
55
56 const ainv = u.a.modInverse(n as unknown as number)
57 const ainvrx = ainv.multiply(rx)
58
59 const mHex = m.toString(16)
60 const hHex = keccak256('0x' + mHex)
61 const h = BigInteger.fromHex(hHex.slice(2))
62 const mBlinded = ainvrx.multiply(h)
63
64 return { mBlinded: mBlinded.mod(n), userSecretData: u }
65}
66
67export function blindSign(sk: BigInteger, mBlinded: BigInteger, k: BigInteger): BigInteger {
68 let sBlind = sk.multiply(mBlinded)
69 sBlind = sBlind.add(k)
70 return sBlind.mod(n)
71}
72
73/**
74 * Unblinds the blinded signature.
75 * @param blinded signature
76 * @param userSecretData
77 * @returns unblinded signature
78 */
79export function unblind(sBlind: BigInteger, userSecretData: UserSecretData): UnblindedSignature {
80 const s = userSecretData.a.multiply(sBlind).add(userSecretData.b)
81 return { s: s.mod(n), f: userSecretData.f }
82}
83
84export function verify(m: BigInteger, s: UnblindedSignature, q: Point) {
85 const sG = G.multiply(s.s)
86
87 const mHex = m.toString(16)
88 const hHex = keccak256('0x' + mHex)
89 const h = BigInteger.fromHex(hHex.slice(2))
90
91 const rx = s.f.affineX.mod(n)
92 const right = s.f.add(
93 q.multiply(
94 rx.multiply(h)
95 )
96 )
97
98 if ((sG.affineX.toString() == right.affineX.toString())
99 && (sG.affineY.toString() == right.affineY.toString())) {
100 return true
101 }
102 return false
103}