'use strict'; var _ = require('lodash'); var $ = require('preconditions').singleton(); var sjcl = require('sjcl'); var Bitcore = require('bitcore'); var Address = Bitcore.Address; var PrivateKey = Bitcore.PrivateKey; var PublicKey = Bitcore.PublicKey; var crypto = Bitcore.crypto; var encoding = Bitcore.encoding; var Utils = require('./utils'); function WalletUtils() {}; /* TODO: It would be nice to be compatible with bitcoind signmessage. How * the hash is calculated there? */ WalletUtils.hashMessage = function(text) { $.checkArgument(text); var buf = new Buffer(text); var ret = crypto.Hash.sha256sha256(buf); ret = new Bitcore.encoding.BufferReader(ret).readReverse(); return ret; }; WalletUtils.signMessage = function(text, privKey) { $.checkArgument(text); var priv = new PrivateKey(privKey); var hash = WalletUtils.hashMessage(text); return crypto.ECDSA.sign(hash, priv, 'little').toString(); }; WalletUtils.accessFromData = function(data) { if (data.xPrivKey) return 'full'; if (data.rwPrivKey) return 'readwrite'; return 'readonly'; }; WalletUtils.verifyMessage = function(text, signature, pubKey) { $.checkArgument(text); $.checkArgument(pubKey); if (!signature) return false; var pub = new PublicKey(pubKey); var hash = WalletUtils.hashMessage(text); try { var sig = new crypto.Signature.fromString(signature); return crypto.ECDSA.verify(hash, sig, pub, 'little'); } catch (e) { return false; } }; WalletUtils.deriveAddress = function(publicKeyRing, path, m, network) { var publicKeys = _.map(publicKeyRing, function(xPubKey) { var xpub = new Bitcore.HDPublicKey(xPubKey); return xpub.derive(path).publicKey; }); var bitcoreAddress = Address.createMultisig(publicKeys, m, network); return { address: bitcoreAddress.toString(), path: path, publicKeys: _.invoke(publicKeys, 'toString'), }; }; WalletUtils.getProposalHash = function(toAddress, amount, message) { return toAddress + '|' + amount + '|' + (message || ''); }; WalletUtils.xPubToCopayerId = function(xpub) { var hash = sjcl.hash.sha256.hash(xpub); return sjcl.codec.hex.fromBits(hash); }; WalletUtils.toSecret = function(walletId, walletPrivKey, network) { var widHex = new Buffer(walletId.replace(/-/g, ''), 'hex'); var widBase58 = new encoding.Base58(widHex).toString(); return _.padRight(widBase58, 22, '0') + walletPrivKey.toWIF() + (network == 'testnet' ? 'T' : 'L'); }; WalletUtils.fromSecret = function(secret) { $.checkArgument(secret); function split(str, indexes) { var parts = []; indexes.push(str.length); var i = 0; while (i < indexes.length) { parts.push(str.substring(i == 0 ? 0 : indexes[i - 1], indexes[i])); i++; }; return parts; }; try { var secretSplit = split(secret, [22, 74]); var widBase58 = secretSplit[0].replace(/0/g, ''); var widHex = encoding.Base58.decode(widBase58).toString('hex'); var walletId = split(widHex, [8, 12, 16, 20]).join('-'); var walletPrivKey = Bitcore.PrivateKey.fromString(secretSplit[1]); var networkChar = secretSplit[2]; return { walletId: walletId, walletPrivKey: walletPrivKey, network: networkChar == 'T' ? 'testnet' : 'livenet', }; } catch (ex) { throw new Error('Invalid secret'); } }; WalletUtils.encryptMessage = function(message, encryptingKey) { var key = sjcl.codec.base64.toBits(encryptingKey); return sjcl.encrypt(key, message, { ks: 128, iter: 1 }); }; WalletUtils.decryptMessage = function(cyphertextJson, encryptingKey) { var key = sjcl.codec.base64.toBits(encryptingKey); return sjcl.decrypt(key, cyphertextJson); }; WalletUtils.privateKeyToAESKey = function(privKey) { var pk = Bitcore.PrivateKey.fromString(privKey); return Bitcore.crypto.Hash.sha256(pk.toBuffer()).slice(0, 16).toString('base64'); }; module.exports = WalletUtils;