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 messageToBigNumber(message: string) {
19 const msg = Buffer.from(message, 'utf8')
20 return new BigNumber(msg)
21}
22
23export function hashBigNumber(m: BigNumber) {
24 const mHex = m.toString(16)
25
26 if (mHex.length % 2 == 0)
27 return keccak256('0x' + mHex).slice(2) // Trim 0x
28 else
29 return keccak256('0x0' + mHex).slice(2) // Trim 0x
30}
31
32export function stringToBigNumber(s: string) {
33 return new BigNumber(s)
34}
35
36export function decodePoint(hexPoint: string): Point {
37 return secp256k1.keyFromPublic(Buffer.from(hexPoint, "hex")).getPublic()
38}
39
40function random(bytes: number) {
41 let k: BigNumber
42 do {
43 k = new BigNumber(randomBytes(bytes))
44 } while (k.toString() == "0" && k.gcd(n).toString() != "1")
45 return k
46}
47
48export function newKeyPair() {
49 const sk = random(32)
50 return { sk: sk, pk: G.mul(sk) }
51}
52
53export function newRequestParameters() {
54 const k = random(32)
55 return { k: k, signerR: G.mul(k) }
56}
57
58/**
59 * Blinds the message for the signer R.
60 * @param m The message to blind
61 * @param signerR
62 * @returns The blinded signature and the user secret data
63 */
64export function blind(m: BigNumber, signerR: Point): { mBlinded: BigNumber, userSecretData: UserSecretData } {
65 const u: UserSecretData = { a: new BigNumber(0), b: new BigNumber(0), f: G }
66 u.a = random(32)
67 u.b = random(32)
68
69 const aR = signerR.mul(u.a)
70 const bG = G.mul(u.b)
71 u.f = aR.add(bG)
72
73 const rx = u.f.getX().mod(n)
74
75 const ainv = u.a.invm(n)
76 const ainvrx = ainv.mul(rx)
77
78 const hHex = hashBigNumber(m)
79
80 const h = new BigNumber(Buffer.from(hHex, "hex"))
81 const mBlinded = ainvrx.mul(h)
82
83 return { mBlinded: mBlinded.mod(n), userSecretData: u }
84}
85
86/** Performs a signature on a blinded message */
87export function blindSign(sk: BigNumber, mBlinded: BigNumber, k: BigNumber): BigNumber {
88 let sBlind = sk.mul(mBlinded)
89 sBlind = sBlind.add(k)
90 return sBlind.mod(n)
91}
92
93/**
94 * Unblinds the blinded signature.
95 * @param blinded signature
96 * @param userSecretData
97 * @returns unblinded signature
98 */
99export function unblind(sBlind: BigNumber, userSecretData: UserSecretData): UnblindedSignature {
100 const s = userSecretData.a.mul(sBlind).add(userSecretData.b)
101 return { s: s.mod(n), f: userSecretData.f }
102}
103
104export function verify(m: BigNumber, s: UnblindedSignature, q: Point) {
105 const sG = G.mul(s.s)
106
107 const hHex = hashBigNumber(m)
108
109 const h = new BigNumber(Buffer.from(hHex, "hex"))
110
111 const rx = s.f.getX().mod(n)
112 const right = s.f.add(
113 q.mul(
114 rx.mul(h)
115 )
116 )
117
118 if ((sG.getX().toString() == right.getX().toString())
119 && (sG.getY().toString() == right.getY().toString())) {
120 return true
121 }
122 return false
123}