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.

157 lines
4.6 KiB

  1. import { randomBytes } from 'crypto'
  2. import * as BigInteger from 'bigi'
  3. import { getCurveByName, Point } from 'ecurve'
  4. import { keccak256 } from '@ethersproject/keccak256'
  5. const ecparams = getCurveByName('secp256k1')
  6. const G = ecparams.G
  7. const n = ecparams.n as BigInteger
  8. export { ecparams }
  9. export { BigInteger }
  10. export { Point }
  11. export type UserSecretData = { a: BigInteger, b: BigInteger, f: Point }
  12. export type UnblindedSignature = { s: BigInteger, f: Point }
  13. /**
  14. * Imports a Point from hex string where X and Y coordinates were encoded as 32
  15. * & 32 bytes in LittleEndian.
  16. */
  17. export function pointFromHex(pointHex: string) {
  18. const xBuff = Buffer.from(pointHex.substr(0, 64), 'hex').reverse().toString('hex')
  19. const yBuff = Buffer.from(pointHex.substr(64), 'hex').reverse().toString('hex')
  20. const x = BigInteger.fromHex(xBuff)
  21. const y = BigInteger.fromHex(yBuff)
  22. const p = Point.fromAffine(ecparams, x, y)
  23. return p
  24. }
  25. export function pointToHex(point: Point): string {
  26. const buffX = point.affineX.toBuffer(32).reverse()
  27. const buffY = point.affineY.toBuffer(32).reverse()
  28. return buffX.toString("hex") + buffY.toString("hex")
  29. }
  30. export function signatureFromHex(hexSignature: string): UnblindedSignature {
  31. if (!hexSignature || hexSignature.length != 192) throw new Error("Invalid hex signature (96 bytes expected)")
  32. const s = BigInteger.fromBuffer(Buffer.from(hexSignature.substr(0, 64), "hex").reverse())
  33. const f = pointFromHex(hexSignature.substr(64))
  34. return { s, f }
  35. }
  36. export function signatureToHex(signature: UnblindedSignature): string {
  37. if (!signature || !signature.f || !signature.s) throw new Error("The signature is empty")
  38. const { f, s } = signature
  39. // hex(swapEndiannes(s) ) + hex(f)
  40. const flippedHexS = s.toBuffer(32).reverse().toString("hex")
  41. return flippedHexS + pointToHex(f)
  42. }
  43. export function messageToBigNumber(message: string) {
  44. const msg = Buffer.from(message, 'utf8')
  45. return BigInteger.fromBuffer(msg)
  46. }
  47. export function newBigFromString(s: string) {
  48. let a = new BigInteger(null, null, null)
  49. a.fromString(s, null)
  50. return a
  51. }
  52. export function newKeyPair() {
  53. const sk = random(32)
  54. return { sk: sk, pk: G.multiply(sk) }
  55. }
  56. export function newRequestParameters() {
  57. const k = random(32)
  58. return { k: k, signerR: G.multiply(k) }
  59. }
  60. /**
  61. * Blinds the message for the signer R.
  62. * @param {BigInteger} m
  63. * @param {Point} signerR
  64. * @returns {struct} {mBlinded: BigInteger, userSecretData: {a: BigInteger, b: BigInteger, f: Point}}
  65. */
  66. export function blind(m: BigInteger, signerR: Point): { mBlinded: BigInteger, userSecretData: UserSecretData } {
  67. const u: UserSecretData = { a: BigInteger.ZERO as BigInteger, b: BigInteger.ZERO as BigInteger, f: G }
  68. u.a = random(32)
  69. u.b = random(32)
  70. const aR = signerR.multiply(u.a)
  71. const bG = G.multiply(u.b)
  72. u.f = aR.add(bG)
  73. const rx = u.f.affineX.mod(n)
  74. const ainv = u.a.modInverse(n as unknown as number)
  75. const ainvrx = ainv.multiply(rx)
  76. const mHex = m.toString(16)
  77. const hHex = keccak256('0x' + evenHex(mHex)).substr(2)
  78. const h = BigInteger.fromHex(hHex)
  79. const mBlinded = ainvrx.multiply(h)
  80. return { mBlinded: mBlinded.mod(n), userSecretData: u }
  81. }
  82. export function blindSign(sk: BigInteger, mBlinded: BigInteger, k: BigInteger): BigInteger {
  83. let sBlind = sk.multiply(mBlinded)
  84. sBlind = sBlind.add(k)
  85. return sBlind.mod(n)
  86. }
  87. /**
  88. * Unblinds the blinded signature.
  89. * @param blinded signature
  90. * @param userSecretData
  91. * @returns unblinded signature
  92. */
  93. export function unblind(sBlind: BigInteger, userSecretData: UserSecretData): UnblindedSignature {
  94. const s = userSecretData.a.multiply(sBlind).add(userSecretData.b)
  95. return { s: s.mod(n), f: userSecretData.f }
  96. }
  97. export function verify(m: BigInteger, s: UnblindedSignature, q: Point) {
  98. const sG = G.multiply(s.s)
  99. const mHex = m.toString(16)
  100. const hHex = keccak256('0x' + evenHex(mHex)).substr(2)
  101. const h = BigInteger.fromHex(hHex)
  102. const rx = s.f.affineX.mod(n)
  103. const right = s.f.add(
  104. q.multiply(
  105. rx.multiply(h)
  106. )
  107. )
  108. if ((sG.affineX.toString() == right.affineX.toString())
  109. && (sG.affineY.toString() == right.affineY.toString())) {
  110. return true
  111. }
  112. return false
  113. }
  114. // HELPERS
  115. function random(bytes: number) {
  116. let k: BigInteger
  117. do {
  118. k = BigInteger.fromByteArrayUnsigned(randomBytes(bytes)) as unknown as BigInteger
  119. } while (k.toString() == '0' && k.gcd(n).toString() != '1')
  120. return k
  121. }
  122. export function evenHex(hexString: string) {
  123. if ((hexString.length % 2) != 0) {
  124. hexString = "0" + hexString
  125. }
  126. return hexString
  127. }