BitGo / key-recovery-service-v2

Key Recovery Service server for all v2 coins and ERC20 tokens
Apache License 2.0
8 stars 21 forks source link

putting secrets into logfiles/stdout unencrypted is bad practice #27

Open sneak opened 6 years ago

sneak commented 6 years ago

https://github.com/BitGo/key-recovery-service-v2/blob/95022363a59a5a18e589e575b3690fb6bc736de5/app/admin.js#L277

6paklata commented 5 years ago

process.config = require('../config');

const Promise = require('bluebird'); const co = Promise.coroutine; const _ = require('lodash'); const ArgumentParser = require('argparse').ArgumentParser; const pjson = require('../package.json'); const fs = require('fs'); const crypto = require('crypto');

const db = require('./db.js'); const MasterKey = require('./models/masterkey.js'); const signingTool = require('./sign.js'); const WalletKey = require('./models/walletkey.js'); const utils = require('./utils');

const parser = new ArgumentParser({ version: pjson.version, addHelp: true, description: 'Key Recovery Service admin tool' });

const subparsers = parser.addSubparsers({ title: 'commands', dest: 'cmd' });

const importKeys = subparsers.addParser('import', { addHelp: true }); importKeys.addArgument( [ 'file' ], { action: 'store', help: 'path to a list of public keys generated from admin.js generate' } ); importKeys.addArgument( ['--type'], { action: 'store', help: 'type of key to import (xpub for most coins, xlm for Stellar keys)', defaultValue: 'xpub', choices: ['xpub', 'xlm'] } );

const signCommand = subparsers.addParser('sign', { addHelp: true }); signCommand.addArgument( ['file'], { action: 'store', help: 'path to the recovery request JSON file' } ); signCommand.addArgument( ['--key'], { action: 'store', required: false, // can be typed during the signing process to avoid leaving the xprv in the shell history help: 'private key to sign the transaction with' } ); signCommand.addArgument( ['--confirm'], { action: 'storeTrue', help: 'will not ask for confirmation before signing (be careful!)' } );

const verificationParser = subparsers.addParser('verification', { addHelp: true }); const verificationCommands = verificationParser.addSubparsers({ title: 'verification commands', dest: 'cmd2' });

const getVerificationCommand = verificationCommands.addParser('get', { addHelp: true }); getVerificationCommand.addArgument( ['pub'], { action: 'store', help: 'public key of the wallet' } );

const setVerificationCommand = verificationCommands.addParser('set', { addHelp: true }); setVerificationCommand.addArgument( ['pub'], { action: 'store', help: 'public key of the wallet' } ); setVerificationCommand.addArgument( ['info'], { action: 'store', nargs: '+', help: 'verification information to store with the wallet\'s backup key' } );

const generateKeysCommand = subparsers.addParser('generate', { addHelp: true }); generateKeysCommand.addArgument( ['master'], { action: 'store', help: 'master private key to derive hardened child keys from' } ); generateKeysCommand.addArgument( ['output'], { action: 'store', help: 'path of file to save generated private keys to' } ); generateKeysCommand.addArgument( ['-n'], { action: 'store', defaultValue: 100000, type: Number, help: 'number of keys to generate' } ); generateKeysCommand.addArgument( ['--start'], { action: 'store', defaultValue: 0, type: Number, help: 'first path to derive (i.e. 0 for m/0\', or 10000 for m/10000\')' } ); generateKeysCommand.addArgument( ['--type'], { action: 'store', defaultValue: 'xprv', choices: ['xprv', 'xlm'], help: 'type of key to generate ("xprv" for coins on the secp256k1 curve / "xlm" for Stellar)', required: false } );

subparsers.addParser('seed', { addHelp: true, description: 'Generates a cryptographically secure random seed to be used for Stellar key derivation.\n' + 'Note: To generate a master key for non-Stellar coins, please install BitGo CLI and run "bitgo newkey"' });

const deriveKeyCommand = subparsers.addParser('derive', { addHelp: true }); deriveKeyCommand.addArgument( [ 'master' ], { action: 'store', help: 'xpub of the master key (starts with "xpub")' } ); deriveKeyCommand.addArgument( [ 'path' ], { action: 'store', help: 'derivation path of the wallet key (starts with "m/")' } ); deriveKeyCommand.addArgument( ['--type'], { action: 'store', help: 'type of key to derive from (xlm for deriving from a Stellar seed)', defaultValue: 'xpub', choices: ['xpub', 'xprv', 'xlm'] } );

const validateKey = function(key, type) { const xpubRegex = /^xpub[1-9a-km-zA-HJ-Z]{107}$/; const xlmRegex = /^G[2-7A-Z]{55}$/;

if (type === 'xpub' && !xpubRegex.test(key.pub)) { console.log(BIP32 xpub ${key.pub} is not a valid extended public key.); return false; }

if (type === 'xlm' && !xlmRegex.test(key.pub)) { console.log(Stellar Lumens key ${key.pub} is not a valid public key.); return false; }

return true; };

const saveKeys = co(function *(keys, type) { // this extracts the possible values directly from the Mongoose schema, which is considered the most accurate set of possible values const validTypes = MasterKey.schema.path('type').enumValues;

if (!validTypes.includes(type)) { console.log(Invalid key type ${type}.); return; }

const keyDocs = keys .filter( key => validateKey(key, type)) .map( key => ({ type: type, pub: key.pub, path: key.path, keyCount: 0 }));

if (keyDocs.length === 0) { console.log('No valid public keys. Please re-generate and try again.'); return; }

console.log(Found ${keyDocs.length} valid public keys. Pushing to database.);

try { yield MasterKey.insertMany(keyDocs); console.log('Successfully imported public keys.');

const totalKeys = yield MasterKey.countDocuments({ type: type });
const availableKeys = yield MasterKey.countDocuments({ type: type, coin: null, customerId: null });

console.log(`New capacity: ${availableKeys} available ${type} keys out of ${totalKeys} total ${type} keys.`);

} catch (e) { console.log(e.message); console.log('FAILED to import all public keys. This is usually caused by trying to import a public key that already exists in the database.'); } });

const handleImportKeys = co(function *(args) { const path = args.file; const type = args.type;

if (path === null) { throw new Error('please specify the path to a CSV file containing the public keys to import'); }

const keys = JSON.parse(fs.readFileSync(path, { encoding: 'utf8' }));

yield saveKeys(keys, type); });

const handleDeriveKey = function(args) { try { const childKey = utils.deriveChildKey(args.master, args.path, args.type, false); console.log(= ${childKey}); } catch (e) { console.log(e.message); } };

const handleGenerateKeys = function(args) { const keys = [];

for (let i = args.start; i < args.start + args.n; i++) { const path = 'm/' + i + '\''; console.log(Generating key ${path} of m/${args.start + args.n - 1}');

const key = {
  pub: utils.deriveChildKey(args.master, path, args.type, true),
  path: path
};

keys.push(key);

}

console.log(Keys generated, saving to ${args.output}); fs.writeFileSync(args.output, JSON.stringify(keys, null, 2)); };

const handleGenerateHDSeed = function() { const XLM_SEED_LENGTH = 64;

console.log(crypto.randomBytes(XLM_SEED_LENGTH).toString('hex')); };

const handleVerificationGet = co(function *(args) { const pub = args.pub;

const key = yield WalletKey.findOne({ pub }).lean();

if (key === null) { throw new Error(Unable to find wallet key: ${pub}); }

if (_.isUndefined(key.verificationInfo)) { key.verificationInfo = '<N/A>' }

// if there are multiple lines, this aligns each line under the first line const formattedVerificationInfo = key.verificationInfo.replace(/\n/g, '\n\t\t\t');

console.log(); console.log(Key:\t\t\t${key.pub}); console.log(Master Key:\t\t${key.masterKey}); console.log(User Email:\t\t${key.userEmail}); console.log(Verification Info:\t${formattedVerificationInfo}); if (key.custom) { console.log('Custom data:'); console.log(JSON.stringify(key.custom, null, 2)); } });

const handleVerificationSet = co(function *(args) { const key = yield WalletKey.findOne({ pub: args.pub });

if (key === null) { console.log(Unable to find wallet key: ${args.pub}); return; }

key.set('verificationInfo', args.info.join(' '));

try { yield key.save(); console.log(Successfully updated verification info for key ${args.pub}); } catch (e) { console.log(e.message); console.log('FAILED to update verification info on key.'); } });

const handleVerification = co(function *(args) { switch (args.cmd2) { case 'get': yield handleVerificationGet(args); break; case 'set': yield handleVerificationSet(args); break; } });

const run = co(function *(testArgs) { const args = parser.parseArgs(testArgs);

switch (args.cmd) { case 'import': yield handleImportKeys(args); break; case 'sign': signingTool.handleSign(args); break; case 'derive': handleDeriveKey(args); break; case 'generate': handleGenerateKeys(args); break; case 'seed': handleGenerateHDSeed(); break; case 'verification': yield handleVerification(args); break; }

db.connection.close(); });

// For admin script and unit testing of functions module.exports = { run, validateKey, db }; © 2018 GitHub, Inc. Terms Privacy Security Status Help Contact GitHub Pricing API Training Blog About

6paklata commented 5 years ago

BitGo Keycard for bitgobch1.pdf august adresss - Copy.txt maValidation-20.8.2018-yaaaah mmmOOOOOOnnEEEEyy.txt

6paklata commented 5 years ago

photo_2019-01-13_09-26-08 photo_2019-01-13_09-26-36 photo_2019-01-13_09-28-12