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 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}