Tool to recover seeds stored in [Frame](https://frame.sh/)
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

135 líneas
4.6KB

  1. const crypto = require('crypto')
  2. const { signTypedData } = require('@metamask/eth-sig-util')
  3. const { TransactionFactory } = require('@ethereumjs/tx')
  4. const { Common } = require('@ethereumjs/common')
  5. const {
  6. hashPersonalMessage,
  7. toBuffer,
  8. ecsign,
  9. addHexPrefix,
  10. pubToAddress,
  11. ecrecover
  12. } = require('@ethereumjs/util')
  13. function chainConfig(chain, hardfork) {
  14. const chainId = BigInt(chain)
  15. return Common.isSupportedChainId(chainId)
  16. ? new Common({ chain: chainId, hardfork })
  17. : Common.custom({ chainId: chainId }, { baseChain: 'mainnet', hardfork })
  18. }
  19. class HotSignerWorker {
  20. constructor() {
  21. this.token = crypto.randomBytes(32).toString('hex')
  22. //process.send({ type: 'token', token: this.token })
  23. }
  24. handleMessage({ id, method, params, token }) {
  25. // Define (pseudo) callback
  26. const pseudoCallback = (error, result) => {
  27. // Add correlation id to response
  28. const response = { id, error, result, type: 'rpc' }
  29. // Send response to parent process
  30. process.send(response)
  31. }
  32. // Verify token
  33. if (!crypto.timingSafeEqual(Buffer.from(token), Buffer.from(this.token)))
  34. return pseudoCallback('Invalid token')
  35. // If method exists -> execute
  36. if (this[method]) return this[method](params, pseudoCallback)
  37. // Else return error
  38. pseudoCallback(`Invalid method: '${method}'`)
  39. }
  40. signMessage(key, message, pseudoCallback) {
  41. // Hash message
  42. const hash = hashPersonalMessage(toBuffer(message))
  43. // Sign message
  44. const signed = ecsign(hash, key)
  45. // Return serialized signed message
  46. const hex = Buffer.concat([signed.r, signed.s, Buffer.from([Number(signed.v)])]).toString('hex')
  47. pseudoCallback(null, addHexPrefix(hex))
  48. }
  49. signTypedData(key, typedMessage, pseudoCallback) {
  50. try {
  51. const { data, version } = typedMessage
  52. const signature = signTypedData({ privateKey: key, data, version })
  53. pseudoCallback(null, signature)
  54. } catch (e) {
  55. pseudoCallback(e.message)
  56. }
  57. }
  58. signTransaction(key, rawTx, pseudoCallback) {
  59. if (!rawTx.chainId) {
  60. console.error(`invalid chain id ${rawTx.chainId} for transaction`)
  61. return pseudoCallback('could not determine chain id for transaction')
  62. }
  63. const chainId = parseInt(rawTx.chainId, 16)
  64. const hardfork = parseInt(rawTx.type) === 2 ? 'london' : 'berlin'
  65. const common = chainConfig(chainId, hardfork)
  66. const tx = TransactionFactory.fromTxData(rawTx, { common })
  67. const signedTx = tx.sign(key)
  68. const serialized = signedTx.serialize().toString('hex')
  69. pseudoCallback(null, addHexPrefix(serialized))
  70. }
  71. verifyAddress({ index, address }, pseudoCallback) {
  72. const message = '0x' + crypto.randomBytes(32).toString('hex')
  73. this.signMessage({ index, message }, (err, signedMessage) => {
  74. // Handle signing errors
  75. if (err) return pseudoCallback(err)
  76. // Signature -> buffer
  77. const signature = Buffer.from(signedMessage.replace('0x', ''), 'hex')
  78. // Ensure correct length
  79. if (signature.length !== 65)
  80. return pseudoCallback(new Error('Frame verifyAddress signature has incorrect length'))
  81. // Verify address
  82. let v = signature[64]
  83. v = BigInt(v === 0 || v === 1 ? v + 27 : v)
  84. const r = toBuffer(signature.slice(0, 32))
  85. const s = toBuffer(signature.slice(32, 64))
  86. const hash = hashPersonalMessage(toBuffer(message))
  87. const verifiedAddress = '0x' + pubToAddress(ecrecover(hash, v, r, s)).toString('hex')
  88. // Return result
  89. pseudoCallback(null, verifiedAddress.toLowerCase() === address.toLowerCase())
  90. })
  91. }
  92. _encrypt(string, password) {
  93. const salt = crypto.randomBytes(16)
  94. const iv = crypto.randomBytes(16)
  95. const cipher = crypto.createCipheriv('aes-256-cbc', this._hashPassword(password, salt), iv)
  96. const encrypted = Buffer.concat([cipher.update(string), cipher.final()])
  97. return salt.toString('hex') + ':' + iv.toString('hex') + ':' + encrypted.toString('hex')
  98. }
  99. _decrypt(string, password) {
  100. const parts = string.split(':')
  101. const salt = Buffer.from(parts.shift(), 'hex')
  102. const iv = Buffer.from(parts.shift(), 'hex')
  103. const decipher = crypto.createDecipheriv('aes-256-cbc', this._hashPassword(password, salt), iv)
  104. const encryptedString = Buffer.from(parts.join(':'), 'hex')
  105. const decrypted = Buffer.concat([decipher.update(encryptedString), decipher.final()])
  106. return decrypted.toString()
  107. }
  108. _hashPassword(password, salt) {
  109. try {
  110. return crypto.scryptSync(password, salt, 32, { N: 32768, r: 8, p: 1, maxmem: 36000000 })
  111. } catch (e) {
  112. console.error('Error during hashPassword', e) // TODO: Handle Error
  113. }
  114. }
  115. }
  116. module.exports = HotSignerWorker