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
 15/**
 16 * Imports a Point from hex string where X and Y coordinates were encoded as 32
 17 * & 32 bytes in LittleEndian.
 18 */
 19export function importPointFromHex(pointHex: string) {
 20    const xBuff = Buffer.from(pointHex.substr(0, 64), 'hex').reverse().toString('hex')
 21    const yBuff = Buffer.from(pointHex.substr(64), 'hex').reverse().toString('hex')
 22    const x = BigInteger.fromHex(xBuff)
 23    const y = BigInteger.fromHex(yBuff)
 24    const p = Point.fromAffine(ecparams, x, y)
 25    return p
 26}
 27
 28export function newBigFromString(s: string) {
 29    let a = new BigInteger(null, null, null)
 30    a.fromString(s, null)
 31    return a
 32}
 33
 34function random(bytes: number) {
 35    let k: BigInteger
 36    do {
 37        k = BigInteger.fromByteArrayUnsigned(randomBytes(bytes)) as unknown as BigInteger
 38    } while (k.toString() == '0' && k.gcd(n).toString() != '1')
 39    return k
 40}
 41
 42export function newKeyPair() {
 43    const sk = random(32)
 44    return { sk: sk, pk: G.multiply(sk) }
 45}
 46
 47export function newRequestParameters() {
 48    const k = random(32)
 49    return { k: k, signerR: G.multiply(k) }
 50}
 51
 52/**
 53 * Blinds the message for the signer R.
 54 * @param {BigInteger} m
 55 * @param {Point} signerR
 56 * @returns {struct} {mBlinded: BigInteger, userSecretData: {a: BigInteger, b: BigInteger, f: Point}}
 57 */
 58export function blind(m: BigInteger, signerR: Point): { mBlinded: BigInteger, userSecretData: UserSecretData } {
 59    const u: UserSecretData = { a: BigInteger.ZERO as BigInteger, b: BigInteger.ZERO as BigInteger, f: G }
 60    u.a = random(32)
 61    u.b = random(32)
 62
 63    const aR = signerR.multiply(u.a)
 64    const bG = G.multiply(u.b)
 65    u.f = aR.add(bG)
 66
 67    const rx = u.f.affineX.mod(n)
 68
 69    const ainv = u.a.modInverse(n as unknown as number)
 70    const ainvrx = ainv.multiply(rx)
 71
 72    const mHex = m.toString(16)
 73    const hHex = keccak256('0x' + mHex)
 74    const h = BigInteger.fromHex(hHex.slice(2))
 75    const mBlinded = ainvrx.multiply(h)
 76
 77    return { mBlinded: mBlinded.mod(n), userSecretData: u }
 78}
 79
 80export function blindSign(sk: BigInteger, mBlinded: BigInteger, k: BigInteger): BigInteger {
 81    let sBlind = sk.multiply(mBlinded)
 82    sBlind = sBlind.add(k)
 83    return sBlind.mod(n)
 84}
 85
 86/**
 87 * Unblinds the blinded signature.
 88 * @param blinded signature
 89 * @param userSecretData
 90 * @returns unblinded signature
 91 */
 92export function unblind(sBlind: BigInteger, userSecretData: UserSecretData): UnblindedSignature {
 93    const s = userSecretData.a.multiply(sBlind).add(userSecretData.b)
 94    return { s: s.mod(n), f: userSecretData.f }
 95}
 96
 97export function verify(m: BigInteger, s: UnblindedSignature, q: Point) {
 98    const sG = G.multiply(s.s)
 99
100    const mHex = m.toString(16)
101    const hHex = keccak256('0x' + mHex)
102    const h = BigInteger.fromHex(hHex.slice(2))
103
104    const rx = s.f.affineX.mod(n)
105    const right = s.f.add(
106        q.multiply(
107            rx.multiply(h)
108        )
109    )
110
111    if ((sG.affineX.toString() == right.affineX.toString())
112        && (sG.affineY.toString() == right.affineY.toString())) {
113        return true
114    }
115    return false
116}