diff --git a/.gitignore b/.gitignore index f3d14e6d..89837ac2 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ raqia.json scratch/ seeds/ +mnemonics/ certs/ lamassu.json diff --git a/bin/bip39 b/bin/bip39 new file mode 100755 index 00000000..9751e047 --- /dev/null +++ b/bin/bip39 @@ -0,0 +1,9 @@ +#!/usr/bin/env node + +'use strict' + +const mnemonicHelpers = require('../lib/mnemonic-helpers') + +const seed = process.argv[2] + +console.log(mnemonicHelpers.fromSeed(seed)) diff --git a/bin/cert-gen.sh b/bin/cert-gen.sh index 73aeee27..a0b30829 100755 --- a/bin/cert-gen.sh +++ b/bin/cert-gen.sh @@ -16,12 +16,13 @@ OFAC_DATA_DIR=$CONFIG_DIR/ofac mkdir -p $CERT_DIR mkdir -p $CONFIG_DIR >> $LOG_FILE 2>&1 -echo "Generating seed..." -SEEDS_DIR=seeds -SEED_FILE=$SEEDS_DIR/seed.txt -mkdir -p $SEEDS_DIR >> $LOG_FILE 2>&1 -SEED=$(openssl rand -hex 32) -echo $SEED > $SEED_FILE +echo "Generating mnemonic..." +MNEMONIC_DIR=$CONFIG_DIR/mnemonics +MNEMONIC_FILE=$MNEMONIC_DIR/mnemonic.txt +mkdir -p $MNEMONIC_DIR >> $LOG_FILE 2>&1 +SEED=$(openssl-1.0 rand -hex 32) +MNEMONIC=$($PWD/bin/bip39 $SEED) +echo "$MNEMONIC" > $MNEMONIC_FILE echo "Generating SSL certificates..." @@ -71,7 +72,7 @@ touch $OFAC_DATA_DIR/etags.json cat < $CONFIG_DIR/lamassu.json { "postgresql": "psql://postgres:$POSTGRES_PASS@localhost/lamassu", - "seedPath": "$SEED_FILE", + "mnemonicPath": "$MNEMONIC_FILE", "caPath": "$CA_PATH", "certPath": "$SERVER_CERT_PATH", "keyPath": "$SERVER_KEY_PATH", diff --git a/bin/lamassu-mnemonic b/bin/lamassu-mnemonic index 122975b8..500d07bb 100755 --- a/bin/lamassu-mnemonic +++ b/bin/lamassu-mnemonic @@ -1,14 +1,8 @@ #!/usr/bin/env node const fs = require('fs') -const bip39 = require('bip39') const options = require('../lib/options') -const seed = fs.readFileSync(options.seedPath, 'utf8').trim() - -const words = bip39.entropyToMnemonic(seed).split(' ') - -for (let i = 0; i < words.length; i += 6) { - console.log(words.slice(i, i + 6).join(' ')) -} +const mnemonic = fs.readFileSync(options.mnemonicPath, 'utf8').trim() +console.log(mnemonic) diff --git a/bin/lamassu-update b/bin/lamassu-update index d6f00bde..601c2e15 100755 --- a/bin/lamassu-update +++ b/bin/lamassu-update @@ -4,6 +4,8 @@ set -e export LOG_FILE=/tmp/update.$(date +"%Y%m%d").log export NPM_BIN=$(npm -g bin) +SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" + rm -f ${LOG_FILE} decho () { @@ -55,6 +57,9 @@ decho "running migration" lamassu-migrate >> ${LOG_FILE} 2>&1 lamassu-migrate-config >> ${LOG_FILE} 2>&1 +decho "update to mnemonic" +$SCRIPTPATH/bin/lamassu-update-to-mnemonic --prod + decho "updating supervisor conf" perl -i -pe 's/command=.*/command=$ENV{NPM_BIN}\/lamassu-server/g' /etc/supervisor/conf.d/lamassu-server.conf >> ${LOG_FILE} 2>&1 perl -i -pe 's/command=.*/command=$ENV{NPM_BIN}\/lamassu-admin-server/g' /etc/supervisor/conf.d/lamassu-admin-server.conf >> ${LOG_FILE} 2>&1 diff --git a/bin/lamassu-update-to-mnemonic b/bin/lamassu-update-to-mnemonic new file mode 100755 index 00000000..10446f0a --- /dev/null +++ b/bin/lamassu-update-to-mnemonic @@ -0,0 +1,30 @@ +#!/usr/bin/env node + +'use strict' + +const fs = require('fs') +const path = require('path') +const os = require('os') +const mnemonicHelpers = require('../lib/mnemonic-helpers') +const options = require('../lib/options-loader')() + +if (!options.opts.mnemonicPath && options.opts.seedPath) { + const seed = fs.readFileSync(options.opts.seedPath, 'utf8').trim() + const mnemonic = mnemonicHelpers.fromSeed(seed) + + if (process.argv[2] === '--prod') { + options.opts.mnemonicPath = path.resolve('etc', 'lamassu', 'mnemonics', 'mnemonic.txt') + } else { + options.opts.mnemonicPath = path.resolve(os.homedir(), '.lamassu', 'mnemonics', 'mnemonic.txt') + } + + if (!fs.existsSync(path.dirname(options.opts.mnemonicPath))) { + fs.mkdirSync(path.dirname(options.opts.mnemonicPath)) + } + + if (!fs.existsSync(options.opts.mnemonicPath)) { + fs.writeFileSync(options.opts.mnemonicPath, mnemonic, 'utf8') + } + + fs.writeFileSync(options.path, JSON.stringify(options.opts, null, '\t'), 'utf8') +} diff --git a/lamassu-default.json b/lamassu-default.json index f8400b72..2397d8d8 100644 --- a/lamassu-default.json +++ b/lamassu-default.json @@ -1,5 +1,6 @@ { "seedPath": "/etc/lamassu/seeds/seed.txt", + "mnemonicPath": "/etc/lamassu/mnemonics/mnemonic.txt", "caPath": "/etc/ssl/certs/Lamassu_OP_Root_CA.pem", "certPath": "/etc/ssl/certs/Lamassu_OP.pem", "keyPath": "/etc/ssl/private/Lamassu_OP.key", diff --git a/lib/coinatmradar/coinatmradar.js b/lib/coinatmradar/coinatmradar.js index 2186f6e9..4330e86d 100644 --- a/lib/coinatmradar/coinatmradar.js +++ b/lib/coinatmradar/coinatmradar.js @@ -6,6 +6,7 @@ const pify = require('pify') const fs = pify(require('fs')) const db = require('../db') +const mnemonicHelpers = require('../mnemonic-helpers') const configManager = require('../config-manager') const options = require('../options') const logger = require('../logger') @@ -129,17 +130,19 @@ function sendRadar (data) { function mapRecord (info) { const timestamp = new Date().toISOString() - return Promise.all([getMachines(info), fs.readFile(options.seedPath, 'utf8')]) - .then(([machines, hex]) => ({ - operatorId: computeOperatorId(Buffer.from(hex.trim(), 'hex')), - operator: { - name: null, - phone: null, - email: null - }, - timestamp, - machines - })) + return Promise.all([getMachines(info), fs.readFile(options.mnemonicPath, 'utf8')]) + .then(([machines, mnemonic]) => { + return { + operatorId: computeOperatorId(mnemonicHelpers.toEntropyBuffer(mnemonic)), + operator: { + name: null, + phone: null, + email: null + }, + timestamp, + machines + } + }) } function update (info) { diff --git a/lib/mnemonic-helpers.js b/lib/mnemonic-helpers.js new file mode 100644 index 00000000..ad4e3675 --- /dev/null +++ b/lib/mnemonic-helpers.js @@ -0,0 +1,19 @@ +const bip39 = require('bip39') +const os = require('os') + +function fromSeed (seed) { + const words = bip39.entropyToMnemonic(seed).split(' ') + + let mnemonic = '' + for (let i = 0; i < words.length; i += 6) { + mnemonic += words.slice(i, i + 6).join(' ') + os.EOL + } + return mnemonic +} + +function toEntropyBuffer (mnemonic) { + const hex = bip39.mnemonicToEntropy(mnemonic.split('\n').join(' ').trim()) + return Buffer.from(hex.trim(), 'hex') +} + +module.exports = { toEntropyBuffer, fromSeed } diff --git a/lib/wallet.js b/lib/wallet.js index 879373c2..24982462 100644 --- a/lib/wallet.js +++ b/lib/wallet.js @@ -6,6 +6,7 @@ const configManager = require('./config-manager') const pify = require('pify') const fs = pify(require('fs')) +const mnemonicHelpers = require('./mnemonic-helpers') const options = require('./options') const ph = require('./plugin-helper') const layer2 = require('./layer2') @@ -24,20 +25,20 @@ function httpError (msg, code) { } function computeSeed (masterSeed) { - return hkdf(masterSeed, 32, {salt: 'lamassu-server-salt', info: 'wallet-seed'}).toString('hex') + return hkdf(masterSeed, 32, { salt: 'lamassu-server-salt', info: 'wallet-seed' }).toString('hex') } function fetchWallet (settings, cryptoCode) { - return fs.readFile(options.seedPath, 'utf8') - .then(hex => { - const masterSeed = Buffer.from(hex.trim(), 'hex') + return fs.readFile(options.mnemonicPath, 'utf8') + .then(mnemonic => { + const masterSeed = mnemonicHelpers.toEntropyBuffer(mnemonic) const plugin = configManager.cryptoScoped(cryptoCode, settings.config).wallet const wallet = ph.load(ph.WALLET, plugin) const rawAccount = settings.accounts[plugin] const account = _.set('seed', computeSeed(masterSeed), rawAccount) if (_.isFunction(wallet.run)) wallet.run(account) - return {wallet, account} + return { wallet, account } }) } @@ -46,7 +47,7 @@ const lastBalance = {} function _balance (settings, cryptoCode) { return fetchWallet(settings, cryptoCode) .then(r => r.wallet.balance(r.account, cryptoCode)) - .then(balance => ({balance, timestamp: Date.now()})) + .then(balance => ({ balance, timestamp: Date.now() })) .then(r => { lastBalance[cryptoCode] = r return r @@ -103,7 +104,7 @@ function mergeStatus (a, b) { if (!a) return b if (!b) return a - return {status: mergeStatusMode(a.status, b.status)} + return { status: mergeStatusMode(a.status, b.status) } } function mergeStatusMode (a, b) { @@ -169,7 +170,7 @@ function getStatus (settings, tx, machineId) { const status = isAuthorized ? 'authorized' : unauthorizedStatus - return {status} + return { status } }) }