Bingen Eguzkitza пре 4 дана
комит
ca178511c7
7 измењених фајлова са 2211 додато и 0 уклоњено
  1. +2
    -0
      .gitignore
  2. +134
    -0
      HotSigner/worker.js
  3. +26
    -0
      README.org
  4. +72
    -0
      SeedSigner/worker.js
  5. +43
    -0
      main.js
  6. +1917
    -0
      package-lock.json
  7. +17
    -0
      package.json

+ 2
- 0
.gitignore Прегледај датотеку

@@ -0,0 +1,2 @@
.env
node_modules

+ 134
- 0
HotSigner/worker.js Прегледај датотеку

@@ -0,0 +1,134 @@
const crypto = require('crypto')
const { signTypedData } = require('@metamask/eth-sig-util')
const { TransactionFactory } = require('@ethereumjs/tx')
const { Common } = require('@ethereumjs/common')
const {
hashPersonalMessage,
toBuffer,
ecsign,
addHexPrefix,
pubToAddress,
ecrecover
} = require('@ethereumjs/util')

function chainConfig(chain, hardfork) {
const chainId = BigInt(chain)

return Common.isSupportedChainId(chainId)
? new Common({ chain: chainId, hardfork })
: Common.custom({ chainId: chainId }, { baseChain: 'mainnet', hardfork })
}

class HotSignerWorker {
constructor() {
this.token = crypto.randomBytes(32).toString('hex')
//process.send({ type: 'token', token: this.token })
}

handleMessage({ id, method, params, token }) {
// Define (pseudo) callback
const pseudoCallback = (error, result) => {
// Add correlation id to response
const response = { id, error, result, type: 'rpc' }
// Send response to parent process
process.send(response)
}
// Verify token
if (!crypto.timingSafeEqual(Buffer.from(token), Buffer.from(this.token)))
return pseudoCallback('Invalid token')
// If method exists -> execute
if (this[method]) return this[method](params, pseudoCallback)
// Else return error
pseudoCallback(`Invalid method: '${method}'`)
}

signMessage(key, message, pseudoCallback) {
// Hash message
const hash = hashPersonalMessage(toBuffer(message))

// Sign message
const signed = ecsign(hash, key)

// Return serialized signed message
const hex = Buffer.concat([signed.r, signed.s, Buffer.from([Number(signed.v)])]).toString('hex')

pseudoCallback(null, addHexPrefix(hex))
}

signTypedData(key, typedMessage, pseudoCallback) {
try {
const { data, version } = typedMessage
const signature = signTypedData({ privateKey: key, data, version })
pseudoCallback(null, signature)
} catch (e) {
pseudoCallback(e.message)
}
}

signTransaction(key, rawTx, pseudoCallback) {
if (!rawTx.chainId) {
console.error(`invalid chain id ${rawTx.chainId} for transaction`)
return pseudoCallback('could not determine chain id for transaction')
}

const chainId = parseInt(rawTx.chainId, 16)
const hardfork = parseInt(rawTx.type) === 2 ? 'london' : 'berlin'
const common = chainConfig(chainId, hardfork)

const tx = TransactionFactory.fromTxData(rawTx, { common })
const signedTx = tx.sign(key)
const serialized = signedTx.serialize().toString('hex')

pseudoCallback(null, addHexPrefix(serialized))
}

verifyAddress({ index, address }, pseudoCallback) {
const message = '0x' + crypto.randomBytes(32).toString('hex')
this.signMessage({ index, message }, (err, signedMessage) => {
// Handle signing errors
if (err) return pseudoCallback(err)
// Signature -> buffer
const signature = Buffer.from(signedMessage.replace('0x', ''), 'hex')
// Ensure correct length
if (signature.length !== 65)
return pseudoCallback(new Error('Frame verifyAddress signature has incorrect length'))
// Verify address
let v = signature[64]
v = BigInt(v === 0 || v === 1 ? v + 27 : v)
const r = toBuffer(signature.slice(0, 32))
const s = toBuffer(signature.slice(32, 64))
const hash = hashPersonalMessage(toBuffer(message))
const verifiedAddress = '0x' + pubToAddress(ecrecover(hash, v, r, s)).toString('hex')
// Return result
pseudoCallback(null, verifiedAddress.toLowerCase() === address.toLowerCase())
})
}

_encrypt(string, password) {
const salt = crypto.randomBytes(16)
const iv = crypto.randomBytes(16)
const cipher = crypto.createCipheriv('aes-256-cbc', this._hashPassword(password, salt), iv)
const encrypted = Buffer.concat([cipher.update(string), cipher.final()])
return salt.toString('hex') + ':' + iv.toString('hex') + ':' + encrypted.toString('hex')
}

_decrypt(string, password) {
const parts = string.split(':')
const salt = Buffer.from(parts.shift(), 'hex')
const iv = Buffer.from(parts.shift(), 'hex')
const decipher = crypto.createDecipheriv('aes-256-cbc', this._hashPassword(password, salt), iv)
const encryptedString = Buffer.from(parts.join(':'), 'hex')
const decrypted = Buffer.concat([decipher.update(encryptedString), decipher.final()])
return decrypted.toString()
}

_hashPassword(password, salt) {
try {
return crypto.scryptSync(password, salt, 32, { N: 32768, r: 8, p: 1, maxmem: 36000000 })
} catch (e) {
console.error('Error during hashPassword', e) // TODO: Handle Error
}
}
}

module.exports = HotSignerWorker

+ 26
- 0
README.org Прегледај датотеку

@@ -0,0 +1,26 @@
* Frame decryption tool
Tool to recover seeds stored in [Frame](https://frame.sh/)

* Instructions
Get the encrypted seed phrase (or key) from ~\~/.config/frame/signers/*.json~ file. Get the value ~encryptedSeed~ (or ~encryptedKeys~):

#+BEGIN_SRC
jq '.encryptedSeed ~/.config/frame/signers/1234.json
#+END_SRC

Create an ~.env~ file with:

#+BEGIN_SRC
export PASSWORD='password-used-in-frame'
export ENCRYPTED='the-value-obtained-above'
export NUM_KEYS=num-of-keys-and-addresses-you-want-to-derive
#+END_SRC

Run:

#+BEGIN_SRC
npm install
npm start
#+END_SRC

Note that you will get the hex string seed value obtained from ~bip39.mnemonicToSeed~, which is irreversible. (I.e., it is impossible to recover the original mnemonic phrase). But you can recover private keys and private addresses.

+ 72
- 0
SeedSigner/worker.js Прегледај датотеку

@@ -0,0 +1,72 @@
const { computeAddress } = require('ethers').utils;
const hdKey = require('hdkey');
const HotSignerWorker = require('../HotSigner/worker');

class SeedSignerWorker extends HotSignerWorker {
constructor() {
super()
this.seed = null
//process.on('message', (message) => this.handleMessage(message))
}

unlock({ encryptedSeed, password }, pseudoCallback) {
try {
this.seed = this._decrypt(encryptedSeed, password)
pseudoCallback(null)
} catch (e) {
pseudoCallback('Invalid password')
}
}

lock(_, pseudoCallback) {
this.seed = null
pseudoCallback(null)
}

encryptSeed({ seed, password }, pseudoCallback) {
pseudoCallback(null, this._encrypt(seed.toString('hex'), password))
}

signMessage({ index, message }, pseudoCallback) {
// Make sure signer is unlocked
if (!this.seed) return pseudoCallback('Signer locked')
// Derive private key
const key = this._derivePrivateKey(index)
// Sign message
super.signMessage(key, message, pseudoCallback)
}

signTypedData({ index, typedMessage }, pseudoCallback) {
// Make sure signer is unlocked
if (!this.seed) return pseudoCallback('Signer locked')
// Derive private key
const key = this._derivePrivateKey(index)
// Sign message
super.signTypedData(key, typedMessage, pseudoCallback)
}

signTransaction({ index, rawTx }, pseudoCallback) {
// Make sure signer is unlocked
if (!this.seed) return pseudoCallback('Signer locked')
// Derive private key
const key = this._derivePrivateKey(index)
// Sign transaction
super.signTransaction(key, rawTx, pseudoCallback)
}

_derivePrivateKey(index) {
let key = hdKey.fromMasterSeed(Buffer.from(this.seed, 'hex'))
key = key.derive("m/44'/60'/0'/0/" + index)
return key.privateKey
}

_deriveAddress(index) {
const key = hdKey.fromMasterSeed(Buffer.from(this.seed, 'hex'));
const publicKey = key.derive("m/44'/60'/0'/0/" + index).publicKey;
const address = computeAddress(publicKey);
return address;
}
}

//const seedSignerWorker = new SeedSignerWorker() // eslint-disable-line
module.exports = SeedSignerWorker

+ 43
- 0
main.js Прегледај датотеку

@@ -0,0 +1,43 @@
const SeedSignerWorker = require('./SeedSigner/worker.js');

// Create an instance of the worker
const worker = new SeedSignerWorker();

// Example usage of _encrypt and _decrypt
const password = process.env.PASSWORD ? process.env.PASSWORD : "1234";
// console.log('password: ', password)
const originalEncrypted = process.env.ENCRYPTED;
const NUM_KEYS = process.env.NUM_KEYS ? process.env.NUM_KEYS : 1;

if (!originalEncrypted) {
const originalMessage = 'attract rapid earn couch also first limb beyond defense truth yard final';

console.log('Original message:', originalMessage);

// Encrypt the message
let encrypted = worker._encrypt(originalMessage, password);

console.log('Encrypted:', encrypted);

// Decrypt the message
let decrypted = worker._decrypt(encrypted, password);
console.log('Decrypted:', decrypted);

// Verify
console.log('Match:', originalMessage === decrypted);
} else {
console.log('Encrypted:', originalEncrypted);

const seed = worker._decrypt(originalEncrypted, password);
console.log('Decrypted seed:', seed);
worker.seed = seed;

for (let i=0; i < NUM_KEYS; i++) {
const privateKey = worker._derivePrivateKey(i);
console.log(`Private Key ${i}: ${privateKey.toString('hex')}`);
const address = worker._deriveAddress(i);
console.log(`Address ${i}: ${address.toString('hex')}`);
}
}



+ 1917
- 0
package-lock.json
Разлика између датотеке није приказан због своје велике величине
Прегледај датотеку


+ 17
- 0
package.json Прегледај датотеку

@@ -0,0 +1,17 @@
{
"name": "frame-decryption",
"version": "1.0.0",
"description": "Frame decryption tool",
"main": "main.js",
"scripts": {
"start": "node main.js"
},
"dependencies": {
"@ethereumjs/common": "^4.1.0",
"@ethereumjs/tx": "^5.1.0",
"@ethereumjs/util": "^9.0.1",
"@metamask/eth-sig-util": "^7.0.1",
"ethers": "5.7.2",
"hdkey": "2.1.0"
}
}

Loading…
Откажи
Сачувај