You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123 lines
3.2 KiB

  1. import { randomBytes } from 'crypto'
  2. import * as BigNumber from 'bn.js'
  3. import { ec, curve } from 'elliptic'
  4. import { keccak256 } from "@ethersproject/keccak256"
  5. export type Point = curve.base.BasePoint
  6. export { BigNumber }
  7. const secp256k1 = new ec("secp256k1")
  8. const G: Point = secp256k1.g
  9. const n = secp256k1.n // as BigNumber
  10. export const ecParams = { G, n }
  11. export type UserSecretData = { a: BigNumber, b: BigNumber, f: Point }
  12. export type UnblindedSignature = { s: BigNumber, f: Point }
  13. export function messageToBigNumber(message: string) {
  14. const msg = Buffer.from(message, 'utf8')
  15. return new BigNumber(msg)
  16. }
  17. export function hashBigNumber(m: BigNumber) {
  18. const mHex = m.toString(16)
  19. if (mHex.length % 2 == 0)
  20. return keccak256('0x' + mHex).slice(2) // Trim 0x
  21. else
  22. return keccak256('0x0' + mHex).slice(2) // Trim 0x
  23. }
  24. export function stringToBigNumber(s: string) {
  25. return new BigNumber(s)
  26. }
  27. export function decodePoint(hexPoint: string): Point {
  28. return secp256k1.keyFromPublic(Buffer.from(hexPoint, "hex")).getPublic()
  29. }
  30. function random(bytes: number) {
  31. let k: BigNumber
  32. do {
  33. k = new BigNumber(randomBytes(bytes))
  34. } while (k.toString() == "0" && k.gcd(n).toString() != "1")
  35. return k
  36. }
  37. export function newKeyPair() {
  38. const sk = random(32)
  39. return { sk: sk, pk: G.mul(sk) }
  40. }
  41. export function newRequestParameters() {
  42. const k = random(32)
  43. return { k: k, signerR: G.mul(k) }
  44. }
  45. /**
  46. * Blinds the message for the signer R.
  47. * @param m The message to blind
  48. * @param signerR
  49. * @returns The blinded signature and the user secret data
  50. */
  51. export function blind(m: BigNumber, signerR: Point): { mBlinded: BigNumber, userSecretData: UserSecretData } {
  52. const u: UserSecretData = { a: new BigNumber(0), b: new BigNumber(0), f: G }
  53. u.a = random(32)
  54. u.b = random(32)
  55. const aR = signerR.mul(u.a)
  56. const bG = G.mul(u.b)
  57. u.f = aR.add(bG)
  58. const rx = u.f.getX().mod(n)
  59. const ainv = u.a.invm(n)
  60. const ainvrx = ainv.mul(rx)
  61. const hHex = hashBigNumber(m)
  62. const h = new BigNumber(Buffer.from(hHex, "hex"))
  63. const mBlinded = ainvrx.mul(h)
  64. return { mBlinded: mBlinded.mod(n), userSecretData: u }
  65. }
  66. /** Performs a signature on a blinded message */
  67. export function blindSign(sk: BigNumber, mBlinded: BigNumber, k: BigNumber): BigNumber {
  68. let sBlind = sk.mul(mBlinded)
  69. sBlind = sBlind.add(k)
  70. return sBlind.mod(n)
  71. }
  72. /**
  73. * Unblinds the blinded signature.
  74. * @param blinded signature
  75. * @param userSecretData
  76. * @returns unblinded signature
  77. */
  78. export function unblind(sBlind: BigNumber, userSecretData: UserSecretData): UnblindedSignature {
  79. const s = userSecretData.a.mul(sBlind).add(userSecretData.b)
  80. return { s: s.mod(n), f: userSecretData.f }
  81. }
  82. export function verify(m: BigNumber, s: UnblindedSignature, q: Point) {
  83. const sG = G.mul(s.s)
  84. const hHex = hashBigNumber(m)
  85. const h = new BigNumber(Buffer.from(hHex, "hex"))
  86. const rx = s.f.getX().mod(n)
  87. const right = s.f.add(
  88. q.mul(
  89. rx.mul(h)
  90. )
  91. )
  92. if ((sG.getX().toString() == right.getX().toString())
  93. && (sG.getY().toString() == right.getY().toString())) {
  94. return true
  95. }
  96. return false
  97. }