arnaucube
arnaucube/blindsecp256k1-jsPublic Self-hosted repository
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 mHex = m.toString(16)
 79    const hHex = hashBigNumber(m)
 80
 81    const h = new BigNumber(Buffer.from(hHex, "hex"))
 82    const mBlinded = ainvrx.mul(h)
 83
 84    return { mBlinded: mBlinded.mod(n), userSecretData: u }
 85}
 86
 87/** Performs a signature on a blinded message */
 88export function blindSign(sk: BigNumber, mBlinded: BigNumber, k: BigNumber): BigNumber {
 89    let sBlind = sk.mul(mBlinded)
 90    sBlind = sBlind.add(k)
 91    return sBlind.mod(n)
 92}
 93
 94/**
 95 * Unblinds the blinded signature.
 96 * @param blinded signature
 97 * @param userSecretData
 98 * @returns unblinded signature
 99 */
100export function unblind(sBlind: BigNumber, userSecretData: UserSecretData): UnblindedSignature {
101    const s = userSecretData.a.mul(sBlind).add(userSecretData.b)
102    return { s: s.mod(n), f: userSecretData.f }
103}
104
105export function verify(m: BigNumber, s: UnblindedSignature, q: Point) {
106    const sG = G.mul(s.s)
107
108    const hHex = hashBigNumber(m)
109
110    const h = new BigNumber(Buffer.from(hHex, "hex"))
111
112    const rx = s.f.getX().mod(n)
113    const right = s.f.add(
114        q.mul(
115            rx.mul(h)
116        )
117    )
118
119    if ((sG.getX().toString() == right.getX().toString())
120        && (sG.getY().toString() == right.getY().toString())) {
121        return true
122    }
123    return false
124}