arnaucube
arnaucube/blindsecp256k1-jsPublic Self-hosted repository
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}