@ -1,2 +1,3 @@ |
|||||
node_modules |
node_modules |
||||
blindsecp256k1-browser.js |
blindsecp256k1-browser.js |
||||
|
dist |
@ -1,109 +0,0 @@ |
|||||
var crypto = require('crypto'); |
|
||||
var BigInteger = require('bigi') |
|
||||
var ecurve = require('ecurve') |
|
||||
const {keccak256} = require("@ethersproject/keccak256"); |
|
||||
|
|
||||
const ecparams = ecurve.getCurveByName('secp256k1'); |
|
||||
const G = ecparams.G; |
|
||||
const n = ecparams.n; |
|
||||
|
|
||||
function newBigFromString(s) { |
|
||||
var a = new BigInteger() |
|
||||
a.fromString(s) |
|
||||
return a; |
|
||||
} |
|
||||
|
|
||||
function random(bytes){ |
|
||||
do { |
|
||||
var k = BigInteger.fromByteArrayUnsigned(crypto.randomBytes(bytes)); |
|
||||
} while (k.toString() == "0" && k.gcd(n).toString() != "1") |
|
||||
return k; |
|
||||
} |
|
||||
|
|
||||
function newKeyPair() { |
|
||||
const sk = random(32); |
|
||||
return {sk: sk, pk: G.multiply(sk)}; |
|
||||
} |
|
||||
|
|
||||
function newRequestParameters() { |
|
||||
const k = random(32); |
|
||||
return {k: k, signerR: G.multiply(k)}; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Blinds the message for the signer R. |
|
||||
* @param {BigInteger} m |
|
||||
* @param {Point} signerR |
|
||||
* @returns {struct} {mBlinded: BigInteger, userSecretData: {a: BigInteger, b: BigInteger, f: Point}} |
|
||||
*/ |
|
||||
function blind(m, signerR) { |
|
||||
let u = {a: BigInteger.ZERO, b: BigInteger.ZERO, f: G}; |
|
||||
u.a = random(32); |
|
||||
u.b = random(32); |
|
||||
|
|
||||
const aR = signerR.multiply(u.a); |
|
||||
const bG = G.multiply(u.b); |
|
||||
u.f = aR.add(bG); |
|
||||
|
|
||||
const rx = u.f.affineX.mod(n); |
|
||||
|
|
||||
const ainv = u.a.modInverse(n); |
|
||||
const ainvrx = ainv.multiply(rx); |
|
||||
|
|
||||
const mHex = m.toString(16); |
|
||||
const hHex = keccak256('0x' + mHex); |
|
||||
const h = BigInteger.fromHex(hHex.slice(2)); |
|
||||
const mBlinded = ainvrx.multiply(h); |
|
||||
|
|
||||
return {mBlinded: mBlinded.mod(n), userSecretData: u}; |
|
||||
} |
|
||||
|
|
||||
function blindSign(sk, mBlinded, k) { |
|
||||
let sBlind = sk.multiply(mBlinded); |
|
||||
sBlind = sBlind.add(k); |
|
||||
return sBlind.mod(n); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Unblinds the blinded signature. |
|
||||
* @param {BigInteger} sBlind - blinded signature |
|
||||
* @param {a: BigInteger, b: BigInteger, f: Point} - userSecretData |
|
||||
* @returns {s: BigInteger, f: Point} - unblinded signature |
|
||||
*/ |
|
||||
function unblind(sBlind, userSecretData) { |
|
||||
const s = userSecretData.a.multiply(sBlind).add(userSecretData.b); |
|
||||
return {s: s.mod(n), f: userSecretData.f}; |
|
||||
} |
|
||||
|
|
||||
function verify(m, s, q) { |
|
||||
const sG = G.multiply(s.s); |
|
||||
|
|
||||
const mHex = m.toString(16); |
|
||||
const hHex = keccak256('0x' + mHex); |
|
||||
const h = BigInteger.fromHex(hHex.slice(2)); |
|
||||
|
|
||||
const rx = s.f.affineX.mod(n); |
|
||||
const right = s.f.add( |
|
||||
q.multiply( |
|
||||
rx.multiply(h) |
|
||||
) |
|
||||
); |
|
||||
|
|
||||
if ((sG.affineX.toString() == right.affineX.toString()) |
|
||||
&& (sG.affineY.toString() == right.affineY.toString())) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
module.exports = { |
|
||||
newBigFromString: newBigFromString, |
|
||||
ecparams: ecparams, |
|
||||
newKeyPair: newKeyPair, |
|
||||
newRequestParameters: newRequestParameters, |
|
||||
blind: blind, |
|
||||
blindSign: blindSign, |
|
||||
unblind: unblind, |
|
||||
verify: verify |
|
||||
} |
|
@ -0,0 +1,124 @@ |
|||||
|
import { randomBytes } from 'crypto' |
||||
|
import * as BigNumber from 'bn.js' |
||||
|
import { ec, curve } from 'elliptic' |
||||
|
import { keccak256 } from "@ethersproject/keccak256" |
||||
|
|
||||
|
export type Point = curve.base.BasePoint |
||||
|
export { BigNumber } |
||||
|
|
||||
|
const secp256k1 = new ec("secp256k1") |
||||
|
const G: Point = secp256k1.g |
||||
|
const n = secp256k1.n // as BigNumber
|
||||
|
|
||||
|
export const ecParams = { G, n } |
||||
|
|
||||
|
export type UserSecretData = { a: BigNumber, b: BigNumber, f: Point } |
||||
|
export type UnblindedSignature = { s: BigNumber, f: Point } |
||||
|
|
||||
|
export function messageToBigNumber(message: string) { |
||||
|
const msg = Buffer.from(message, 'utf8') |
||||
|
return new BigNumber(msg) |
||||
|
} |
||||
|
|
||||
|
export function hashBigNumber(m: BigNumber) { |
||||
|
const mHex = m.toString(16) |
||||
|
|
||||
|
if (mHex.length % 2 == 0) |
||||
|
return keccak256('0x' + mHex).slice(2) // Trim 0x
|
||||
|
else |
||||
|
return keccak256('0x0' + mHex).slice(2) // Trim 0x
|
||||
|
} |
||||
|
|
||||
|
export function stringToBigNumber(s: string) { |
||||
|
return new BigNumber(s) |
||||
|
} |
||||
|
|
||||
|
export function decodePoint(hexPoint: string): Point { |
||||
|
return secp256k1.keyFromPublic(Buffer.from(hexPoint, "hex")).getPublic() |
||||
|
} |
||||
|
|
||||
|
function random(bytes: number) { |
||||
|
let k: BigNumber |
||||
|
do { |
||||
|
k = new BigNumber(randomBytes(bytes)) |
||||
|
} while (k.toString() == "0" && k.gcd(n).toString() != "1") |
||||
|
return k |
||||
|
} |
||||
|
|
||||
|
export function newKeyPair() { |
||||
|
const sk = random(32) |
||||
|
return { sk: sk, pk: G.mul(sk) } |
||||
|
} |
||||
|
|
||||
|
export function newRequestParameters() { |
||||
|
const k = random(32) |
||||
|
return { k: k, signerR: G.mul(k) } |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Blinds the message for the signer R. |
||||
|
* @param m The message to blind |
||||
|
* @param signerR |
||||
|
* @returns The blinded signature and the user secret data |
||||
|
*/ |
||||
|
export function blind(m: BigNumber, signerR: Point): { mBlinded: BigNumber, userSecretData: UserSecretData } { |
||||
|
const u: UserSecretData = { a: new BigNumber(0), b: new BigNumber(0), f: G } |
||||
|
u.a = random(32) |
||||
|
u.b = random(32) |
||||
|
|
||||
|
const aR = signerR.mul(u.a) |
||||
|
const bG = G.mul(u.b) |
||||
|
u.f = aR.add(bG) |
||||
|
|
||||
|
const rx = u.f.getX().mod(n) |
||||
|
|
||||
|
const ainv = u.a.invm(n) |
||||
|
const ainvrx = ainv.mul(rx) |
||||
|
|
||||
|
// const mHex = m.toString(16)
|
||||
|
const hHex = hashBigNumber(m) |
||||
|
|
||||
|
const h = new BigNumber(Buffer.from(hHex, "hex")) |
||||
|
const mBlinded = ainvrx.mul(h) |
||||
|
|
||||
|
return { mBlinded: mBlinded.mod(n), userSecretData: u } |
||||
|
} |
||||
|
|
||||
|
/** Performs a signature on a blinded message */ |
||||
|
export function blindSign(sk: BigNumber, mBlinded: BigNumber, k: BigNumber): BigNumber { |
||||
|
let sBlind = sk.mul(mBlinded) |
||||
|
sBlind = sBlind.add(k) |
||||
|
return sBlind.mod(n) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Unblinds the blinded signature. |
||||
|
* @param blinded signature |
||||
|
* @param userSecretData |
||||
|
* @returns unblinded signature |
||||
|
*/ |
||||
|
export function unblind(sBlind: BigNumber, userSecretData: UserSecretData): UnblindedSignature { |
||||
|
const s = userSecretData.a.mul(sBlind).add(userSecretData.b) |
||||
|
return { s: s.mod(n), f: userSecretData.f } |
||||
|
} |
||||
|
|
||||
|
export function verify(m: BigNumber, s: UnblindedSignature, q: Point) { |
||||
|
const sG = G.mul(s.s) |
||||
|
|
||||
|
const hHex = hashBigNumber(m) |
||||
|
|
||||
|
const h = new BigNumber(Buffer.from(hHex, "hex")) |
||||
|
|
||||
|
const rx = s.f.getX().mod(n) |
||||
|
const right = s.f.add( |
||||
|
q.mul( |
||||
|
rx.mul(h) |
||||
|
) |
||||
|
) |
||||
|
|
||||
|
if ((sG.getX().toString() == right.getX().toString()) |
||||
|
&& (sG.getY().toString() == right.getY().toString())) { |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
} |
@ -1,39 +0,0 @@ |
|||||
const assert = require('assert'); |
|
||||
const BigInteger = require('bigi'); |
|
||||
var {Point} = require('ecurve') |
|
||||
const {keccak256} = require("@ethersproject/keccak256"); |
|
||||
|
|
||||
const {newBigFromString, ecparams, newKeyPair, newRequestParameters, blind, blindSign, unblind, verify} = require("../src/index.js"); |
|
||||
|
|
||||
describe("keccak256", function () { |
|
||||
it("keccak256", async () => { |
|
||||
const m = BigInteger.fromBuffer(Buffer.from("test", 'utf8')); |
|
||||
const mHex = m.toString(16); |
|
||||
const hHex = keccak256('0x' + mHex); |
|
||||
assert.equal('0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658', hHex); |
|
||||
const h = BigInteger.fromHex(hHex.slice(2)); |
|
||||
assert.equal('70622639689279718371527342103894932928233838121221666359043189029713682937432', h.toString()); |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
describe("test blind", function () { |
|
||||
it("blind", async () => { |
|
||||
const {sk, pk} = newKeyPair(); |
|
||||
|
|
||||
const {k, signerR} = newRequestParameters(); |
|
||||
|
|
||||
const msg = BigInteger.fromBuffer( |
|
||||
Buffer.from("test", 'utf8') |
|
||||
); |
|
||||
assert.equal('1952805748', msg.toString()); |
|
||||
|
|
||||
const {mBlinded, userSecretData} = blind(msg, signerR); |
|
||||
|
|
||||
const sBlind = blindSign(sk, mBlinded, k); |
|
||||
|
|
||||
const sig = unblind(sBlind, userSecretData); |
|
||||
|
|
||||
const verified = verify(msg, sig, pk); |
|
||||
assert.equal(true, verified); |
|
||||
}); |
|
||||
}); |
|
@ -0,0 +1,55 @@ |
|||||
|
import * as assert from 'assert' |
||||
|
|
||||
|
import { |
||||
|
stringToBigNumber, |
||||
|
messageToBigNumber, |
||||
|
decodePoint, |
||||
|
ecParams, |
||||
|
newKeyPair, |
||||
|
newRequestParameters, |
||||
|
blind, |
||||
|
blindSign, |
||||
|
unblind, |
||||
|
verify, |
||||
|
hashBigNumber, |
||||
|
Point, |
||||
|
BigNumber |
||||
|
} from "../src/index" |
||||
|
|
||||
|
describe("keccak256", function () { |
||||
|
it("should hash strings and big numbers", async () => { |
||||
|
const m = messageToBigNumber("test") |
||||
|
assert.strictEqual('1952805748', m.toString()) |
||||
|
|
||||
|
const hHex = hashBigNumber(m) |
||||
|
assert.strictEqual(hHex, '9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658') |
||||
|
const h = new BigNumber(Buffer.from(hHex, "hex")) |
||||
|
assert.strictEqual(h.toString(), '70622639689279718371527342103894932928233838121221666359043189029713682937432') |
||||
|
}) |
||||
|
|
||||
|
it("should decode points in the secp256k1 curve", () => { |
||||
|
const tokenR = "7cfe4af054e13b4e7231d876d23205fb5f939ac8185271ca6b64c635a365faae259fb8cabdb06dde39d1ebeada3cb75cb9739621a79c61a8cf1e9a38abaf782a" |
||||
|
const point = decodePoint("04" + tokenR) |
||||
|
assert.strictEqual(point.getX().toString(16), tokenR.substr(0, 64)) |
||||
|
assert.strictEqual(point.getY().toString(16), tokenR.substr(64)) |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
|
describe("blind signatures", function () { |
||||
|
it("should blind, unblind and verify", async () => { |
||||
|
const { sk, pk } = newKeyPair() |
||||
|
|
||||
|
const { k, signerR } = newRequestParameters() |
||||
|
|
||||
|
const msg = messageToBigNumber("test") |
||||
|
|
||||
|
const { mBlinded, userSecretData } = blind(msg, signerR) |
||||
|
|
||||
|
const sBlind = blindSign(sk, mBlinded, k) |
||||
|
|
||||
|
const sig = unblind(sBlind, userSecretData) |
||||
|
|
||||
|
const verified = verify(msg, sig, pk) |
||||
|
assert(verified) |
||||
|
}) |
||||
|
}) |
@ -0,0 +1,19 @@ |
|||||
|
{ |
||||
|
"compilerOptions": { |
||||
|
"module": "commonjs", |
||||
|
"moduleResolution": "node", |
||||
|
"resolveJsonModule": true, |
||||
|
"pretty": true, |
||||
|
"declaration": true, |
||||
|
"sourceMap": true, |
||||
|
"target": "es2017", |
||||
|
"outDir": "dist", |
||||
|
"baseUrl": "src" |
||||
|
}, |
||||
|
"include": [ |
||||
|
"src/**/*.ts" |
||||
|
], |
||||
|
"exclude": [ |
||||
|
"node_modules" |
||||
|
] |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
{ |
||||
|
"defaultSeverity": "error", |
||||
|
"extends": [ |
||||
|
"tslint:recommended" |
||||
|
], |
||||
|
"jsRules": {}, |
||||
|
"rules": { |
||||
|
"indent": [ |
||||
|
true, |
||||
|
"spaces", |
||||
|
4 |
||||
|
], |
||||
|
"semicolon": [ |
||||
|
false, |
||||
|
"always" |
||||
|
] |
||||
|
}, |
||||
|
"rulesDirectory": [], |
||||
|
"linterOptions": { |
||||
|
"exclude": [ |
||||
|
"node_modules/**" |
||||
|
] |
||||
|
} |
||||
|
} |