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