mirror of https://github.com/BTCPrivate/copay.git
Create a Compatibility namespace
This commit is contained in:
parent
202a047edc
commit
1b0f6836dc
1
copay.js
1
copay.js
|
@ -3,7 +3,6 @@ module.exports.PublicKeyRing = require('./js/models/PublicKeyRing');
|
|||
module.exports.TxProposal = require('./js/models/TxProposal');
|
||||
module.exports.TxProposals = require('./js/models/TxProposals');
|
||||
module.exports.PrivateKey = require('./js/models/PrivateKey');
|
||||
module.exports.Passphrase = require('./js/models/Passphrase');
|
||||
module.exports.HDPath = require('./js/models/HDPath');
|
||||
module.exports.HDParams = require('./js/models/HDParams');
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('ImportController',
|
||||
function($scope, $rootScope, $location, controllerUtils, Passphrase, notification, isMobile, Compatibility) {
|
||||
function($scope, $rootScope, $location, controllerUtils, notification, isMobile, Compatibility) {
|
||||
|
||||
$rootScope.title = 'Import a backup';
|
||||
$scope.importStatus = 'Importing wallet - Reading backup...';
|
||||
|
|
|
@ -1,94 +1,173 @@
|
|||
'use strict';
|
||||
var Identity = require('Identity'),
|
||||
Passphrase = require('Passphrase'),
|
||||
Wallet = require('Wallet'),
|
||||
// walletFactory = new WalletFactory(),
|
||||
passphrase = new Passphrase();
|
||||
|
||||
function Compatibility(){
|
||||
// - preDotEightListWallets()
|
||||
// - preDotEightImportWalletToStorage(walletId, passphrase, profile) (edited)
|
||||
}
|
||||
var Identity = require('./Identity');
|
||||
var Wallet = require('./Wallet');
|
||||
var cryptoUtils = require('../util/crypto');
|
||||
var CryptoJS = require('node-cryptojs-aes').CryptoJS;
|
||||
|
||||
Compatibility.prototype.preDotEightListWallets = function () {};
|
||||
var Compatibility = {};
|
||||
|
||||
/**
|
||||
* Reads from localstorage wallets saved previously to 0.8
|
||||
*/
|
||||
Compatibility._getWalletIds = function(cb) {
|
||||
preconditions.checkArgument(cb);
|
||||
var walletIds = [];
|
||||
var uniq = {};
|
||||
for (key in localStorage) {
|
||||
var split = key.split('::');
|
||||
if (split.length == 2) {
|
||||
var walletId = split[0];
|
||||
|
||||
Compatibility.prototype.preDotEightImportWalletToStorage = function(encryptedObj, password, skipPublicKeyRing, skipTxProposals) {
|
||||
passphrase.getBase64Async(password, function(passphrase) {
|
||||
// updateStatus('Importing wallet - Setting things up...');
|
||||
var w, errMsg;
|
||||
if (!walletId
|
||||
|| walletId === 'nameFor'
|
||||
|| walletId === 'lock'
|
||||
|| walletId === 'wallet') {
|
||||
continue;
|
||||
}
|
||||
|
||||
var skipFields = [];
|
||||
if (skipPublicKeyRing)
|
||||
skipFields.push('publicKeyRing');
|
||||
if (typeof uniq[walletId] === 'undefined') {
|
||||
walletIds.push(walletId);
|
||||
uniq[walletId] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cb(walletIds);
|
||||
};
|
||||
|
||||
if (skipTxProposals)
|
||||
skipFields.push('txProposals');
|
||||
|
||||
// try to import encrypted wallet with passphrase
|
||||
/**
|
||||
* Decrypts using the CryptoJS library (unknown encryption schema)
|
||||
*
|
||||
* Don't use CryptoJS to encrypt. This still exists for compatibility reasons only.
|
||||
*/
|
||||
Compatibility._decrypt = function(base64, passphrase) {
|
||||
var decryptedStr = null;
|
||||
try {
|
||||
w = walletFactory.import(encryptedObj, passphrase, skipFields);
|
||||
var decrypted = CryptoJS.AES.decrypt(base64, passphrase);
|
||||
if (decrypted)
|
||||
decryptedStr = decrypted.toString(CryptoJS.enc.Utf8);
|
||||
} catch (e) {
|
||||
errMsg = e.message;
|
||||
// Error while decrypting
|
||||
return null;
|
||||
}
|
||||
return decryptedStr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads an item from localstorage, decrypts it with passphrase
|
||||
*/
|
||||
Compatibility._read = function(k, passphrase, cb) {
|
||||
preconditions.checkArgument(cb);
|
||||
|
||||
var ret = localStorage.getItem(k);
|
||||
if (!ret) return cb(null);
|
||||
var ret = self._decrypt(ret, passphrase);
|
||||
if (!ret) return cb(null);
|
||||
|
||||
ret = ret.toString(CryptoJS.enc.Utf8);
|
||||
ret = JSON.parse(ret);
|
||||
return ret;
|
||||
};
|
||||
|
||||
Compatibility.getWallets_Old = function(cb) {
|
||||
preconditions.checkArgument(cb);
|
||||
|
||||
var wallets = [];
|
||||
var self = this;
|
||||
|
||||
this._getWalletIds(function(ids) {
|
||||
if (!ids.length) {
|
||||
return cb([]);
|
||||
}
|
||||
|
||||
if (!w) {
|
||||
// $scope.loading = false;
|
||||
// notification.error('Error', errMsg || 'Wrong password');
|
||||
$rootScope.$digest();
|
||||
return;
|
||||
}
|
||||
|
||||
// if wallet was never used, we're done
|
||||
if (!w.isReady()) {
|
||||
$rootScope.wallet = w;
|
||||
// controllerUtils.startNetwork($rootScope.wallet, $scope);
|
||||
return;
|
||||
}
|
||||
|
||||
// if it was used, we need to scan for indices
|
||||
w.updateIndexes(function(err) {
|
||||
// updateStatus('Importing wallet - We are almost there...');
|
||||
if (err) {
|
||||
// $scope.loading = false;
|
||||
// notification.error('Error', 'Error updating indexes: ' + err);
|
||||
}
|
||||
$rootScope.wallet = w;
|
||||
// controllerUtils.startNetwork($rootScope.wallet, $scope);
|
||||
_.each(ids, function(id) {
|
||||
var name = localStorage.getItem('nameFor::' + id);
|
||||
if (name) {
|
||||
wallets.push({
|
||||
id: id,
|
||||
name: name,
|
||||
});
|
||||
}
|
||||
});
|
||||
return cb(wallets);
|
||||
});
|
||||
};
|
||||
|
||||
Compatibility.prototype.fromEncryptedObj = function(base64, passphrase, skipFields) {
|
||||
this.storage.setPassphrase(passphrase);
|
||||
var walletObj = this.storage.import(base64);
|
||||
if (!walletObj) return false;
|
||||
return this.fromObj(walletObj, skipFields);
|
||||
Compatibility.getWallets2 = function(cb) {
|
||||
var self = this;
|
||||
var re = /wallet::([^_]+)(_?(.*))/;
|
||||
|
||||
var keys = [];
|
||||
for (key in localStorage) {
|
||||
keys.push(key);
|
||||
}
|
||||
var wallets = _.compact(_.map(keys, function(key) {
|
||||
if (key.indexOf('wallet::') !== 0)
|
||||
return null;
|
||||
var match = key.match(re);
|
||||
if (match.length != 4)
|
||||
return null;
|
||||
return {
|
||||
id: match[1],
|
||||
name: match[3] ? match[3] : undefined,
|
||||
};
|
||||
}));
|
||||
|
||||
return cb(wallets);
|
||||
};
|
||||
|
||||
Compatibility.prototype.fromObj = function(inObj, skipFields) {
|
||||
var networkName = this.obtainNetworkName(inObj);
|
||||
preconditions.checkState(networkName);
|
||||
preconditions.checkArgument(inObj);
|
||||
/**
|
||||
* Lists all wallets in localstorage
|
||||
*/
|
||||
Compatibility.listWalletsPre8 = function (cb) {
|
||||
var self = this;
|
||||
self.getWallets2(function(wallets) {
|
||||
self.getWallets_Old(function(wallets2) {
|
||||
var ids = _.pluck(wallets, 'id');
|
||||
_.each(wallets2, function(w) {
|
||||
if (!_.contains(ids, w.id))
|
||||
wallets.push(w);
|
||||
});
|
||||
return cb(wallets);
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
var obj = JSON.parse(JSON.stringify(inObj));
|
||||
/**
|
||||
* Retrieves a wallet that predates the 0.8 release
|
||||
*/
|
||||
Compatibility.readWalletPre8 = function(walletId, password, cb) {
|
||||
var self = this;
|
||||
var passphrase = cryptoUtils.kdf(password);
|
||||
var obj = {};
|
||||
|
||||
// not stored options
|
||||
obj.opts = obj.opts || {};
|
||||
obj.opts.reconnectDelay = this.walletDefaults.reconnectDelay;
|
||||
for (key in localStorage) {
|
||||
if (key.indexOf('wallet::' + walletId) !== -1) {
|
||||
var ret = self._read(localStorage.getItem(key), passphrase);
|
||||
if (err) return cb(err);
|
||||
|
||||
skipFields = skipFields || [];
|
||||
skipFields.forEach(function(k) {
|
||||
if (obj[k]) {
|
||||
delete obj[k];
|
||||
} else
|
||||
throw new Error('unknown field:' + k);
|
||||
_.each(Wallet.PERSISTED_PROPERTIES, function(p) {
|
||||
obj[p] = ret[p];
|
||||
});
|
||||
|
||||
var w = Wallet.fromObj(obj, this.storage, this.networks[networkName], this.blockchains[networkName]);
|
||||
if (!w) return false;
|
||||
this._checkVersion(w.version);
|
||||
return w;
|
||||
if (!_.any(_.values(obj)))
|
||||
return cb(new Error('Wallet not found'));
|
||||
|
||||
var w, err;
|
||||
obj.id = walletId;
|
||||
try {
|
||||
w = self.fromObj(obj);
|
||||
} catch (e) {
|
||||
if (e && e.message && e.message.indexOf('MISSOPTS')) {
|
||||
err = new Error('Could not read: ' + walletId);
|
||||
} else {
|
||||
err = e;
|
||||
}
|
||||
w = null;
|
||||
}
|
||||
return cb(err, w);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Compatibility;
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
// 65.7% typed (by google's closure-compiler account)
|
||||
|
||||
|
||||
// this line throws a warning on Chrome Desktop
|
||||
var sjcl = require('../../lib/sjcl');
|
||||
|
||||
var preconditions = require('preconditions').instance();
|
||||
var _ = require('lodash');
|
||||
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Class for a Passphrase object, uses PBKDF2 to expand a password
|
||||
*
|
||||
* @constructor
|
||||
* @param {object} config
|
||||
* @param {string=} config.salt - 'mjuBtGybi/4=' by default
|
||||
* @param {number=} config.iterations - 1000 by default
|
||||
*/
|
||||
function Passphrase(config) {
|
||||
preconditions.checkArgument(!config || !config.salt || _.isString(config.salt));
|
||||
preconditions.checkArgument(!config || !config.iterations || _.isNumber(config.iterations));
|
||||
config = config || {};
|
||||
this.salt = config.salt || 'mjuBtGybi/4=';
|
||||
this.iterations = config.iterations;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Generate a WordArray expanding a password
|
||||
*
|
||||
* @param {string} password - the password to expand
|
||||
* @returns WordArray 512 bits with the expanded key generated from password
|
||||
*/
|
||||
Passphrase.prototype.get = function(password) {
|
||||
var hash = sjcl.hash.sha256.hash(sjcl.hash.sha256.hash(password));
|
||||
var salt = sjcl.codec.base64.toBits(this.salt);
|
||||
|
||||
var crypto2 = function(key, salt, iterations, length, alg) {
|
||||
return sjcl.codec.hex.fromBits(sjcl.misc.pbkdf2(key, salt, iterations, length * 8,
|
||||
alg == 'sha1' ? function(key) {
|
||||
return new sjcl.misc.hmac(key, sjcl.hash.sha1)
|
||||
} : null
|
||||
))
|
||||
};
|
||||
|
||||
var key512 = crypto2(hash, salt, this.iterations, 64, 'sha1');
|
||||
|
||||
return key512;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @desc Generate a base64 encoded key
|
||||
*
|
||||
* @param {string} password - the password to expand
|
||||
* @returns {string} 512 bits of a base64 encoded passphrase based on password
|
||||
*/
|
||||
Passphrase.prototype.getBase64 = function(password) {
|
||||
var key512 = this.get(password);
|
||||
|
||||
var sbase64 = sjcl.codec.base64.fromBits(sjcl.codec.hex.toBits(key512));
|
||||
return sbase64;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @desc Generate a base64 encoded key, without blocking
|
||||
*
|
||||
* @param {string} password - the password to expand
|
||||
* @param {passphraseCallback} cb
|
||||
*/
|
||||
Passphrase.prototype.getBase64Async = function(password, cb) {
|
||||
var self = this;
|
||||
setTimeout(function() {
|
||||
var ret = self.getBase64(password);
|
||||
return cb(ret);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
module.exports = Passphrase;
|
|
@ -2752,66 +2752,6 @@ Wallet.request = function(options, callback) {
|
|||
return ret;
|
||||
};
|
||||
|
||||
/*
|
||||
* Old fns, only for compat
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
Wallet.prototype.migrateWallet = function(walletId, passphrase, cb) {
|
||||
var self = this;
|
||||
|
||||
self.storage.setPassphrase(passphrase);
|
||||
self.read_Old(walletId, null, function(err, wallet) {
|
||||
if (err) return cb(err);
|
||||
|
||||
// TODO
|
||||
TODO(fix_this);
|
||||
wallet.store(function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
self.storage.deleteWallet_Old(walletId, function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
self.storage.removeGlobal('nameFor::' + walletId, function() {
|
||||
return cb();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
Wallet.prototype.read_Old = function(walletId, skipFields, cb) {
|
||||
var self = this,
|
||||
err;
|
||||
var obj = {};
|
||||
|
||||
this.storage.readWallet_Old(walletId, function(err, ret) {
|
||||
if (err) return cb(err);
|
||||
|
||||
_.each(Wallet.PERSISTED_PROPERTIES, function(p) {
|
||||
obj[p] = ret[p];
|
||||
});
|
||||
|
||||
if (!_.any(_.values(obj)))
|
||||
return cb(new Error('Wallet not found'));
|
||||
|
||||
var w, err;
|
||||
obj.id = walletId;
|
||||
try {
|
||||
w = self.fromObj(obj, skipFields);
|
||||
} catch (e) {
|
||||
if (e && e.message && e.message.indexOf('MISSOPTS')) {
|
||||
err = new Error('Could not read: ' + walletId);
|
||||
} else {
|
||||
err = e;
|
||||
}
|
||||
w = null;
|
||||
}
|
||||
return cb(err, w);
|
||||
});
|
||||
};
|
||||
|
||||
Wallet.prototype.getTransactionHistory = function(cb) {
|
||||
var self = this;
|
||||
|
|
|
@ -8,7 +8,7 @@ function EncryptedInsightStorage(config) {
|
|||
inherits(EncryptedInsightStorage, InsightStorage);
|
||||
|
||||
EncryptedInsightStorage.prototype.getItem = function(name, callback) {
|
||||
var key = cryptoUtil.kdf(this.password, this.email);
|
||||
var key = cryptoUtil.kdf(this.password + this.email);
|
||||
InsightStorage.prototype.getItem.apply(this, [name,
|
||||
function(err, body) {
|
||||
var decryptedJson = cryptoUtil.decrypt(key, body);
|
||||
|
@ -21,13 +21,13 @@ EncryptedInsightStorage.prototype.getItem = function(name, callback) {
|
|||
};
|
||||
|
||||
EncryptedInsightStorage.prototype.setItem = function(name, value, callback) {
|
||||
var key = cryptoUtil.kdf(this.password, this.email);
|
||||
var key = cryptoUtil.kdf(this.password + this.email);
|
||||
var record = cryptoUtil.encrypt(key, value);
|
||||
InsightStorage.prototype.setItem.apply(this, [name, record, callback]);
|
||||
};
|
||||
|
||||
EncryptedInsightStorage.prototype.removeItem = function(name, callback) {
|
||||
var key = cryptoUtil.kdf(this.password, this.email);
|
||||
var key = cryptoUtil.kdf(this.password + this.email);
|
||||
InsightStorage.prototype.removeItem.apply(this, [name, callback]);
|
||||
};
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ function EncryptedLocalStorage(config) {
|
|||
inherits(EncryptedLocalStorage, LocalStorage);
|
||||
|
||||
EncryptedLocalStorage.prototype.getItem = function(name, callback) {
|
||||
var key = cryptoUtil.kdf(this.password, this.email);
|
||||
var key = cryptoUtil.kdf(this.password + this.email);
|
||||
LocalStorage.prototype.getItem.apply(this, [name, function(err, body) {
|
||||
var decryptedJson = cryptoUtil.decrypt(key, body);
|
||||
if (!decryptedJson) {
|
||||
|
@ -19,7 +19,7 @@ EncryptedLocalStorage.prototype.getItem = function(name, callback) {
|
|||
};
|
||||
|
||||
EncryptedLocalStorage.prototype.setItem = function(name, value, callback) {
|
||||
var key = cryptoUtil.kdf(this.password, this.email);
|
||||
var key = cryptoUtil.kdf(this.password + this.email);
|
||||
if (!_.isString(value)) {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.services')
|
||||
.value('Passphrase', new copay.Passphrase(config.passphraseConfig));
|
|
@ -8,32 +8,39 @@ var _ = require('lodash');
|
|||
var defaultSalt = 'mjuBtGybi/4=';
|
||||
var defaultIterations = 100;
|
||||
|
||||
sjcl.defaults = {
|
||||
v: 1,
|
||||
iter: 100,
|
||||
ks: 128,
|
||||
ts: 64,
|
||||
mode: "ccm",
|
||||
adata: "",
|
||||
cipher: "aes"
|
||||
},
|
||||
|
||||
module.exports = {
|
||||
|
||||
kdf: function(value1, value2, salt, iterations) {
|
||||
iterations = iterations || defaultIterations;
|
||||
salt = salt || defaultSalt;
|
||||
/**
|
||||
* @param {string} password
|
||||
* @param {string} salt - base64 encoded, defaults to 'mjuBtGybi/4='
|
||||
* @param {number} iterations - defaults to 100
|
||||
* @param {number} length - bits, defaults to 512 bits
|
||||
* @returns {string} base64 encoded pbkdf2 derivation using sha1 for hmac
|
||||
*/
|
||||
kdf: function(password, salt, iterations, length) {
|
||||
return sjcl.codec.base64.fromBits(
|
||||
this.kdfbinary(password, salt, iterations, length)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} password
|
||||
* @param {string} salt - base64 encoded, defaults to 'mjuBtGybi/4='
|
||||
* @param {number} iterations - defaults to 100
|
||||
* @param {number} length - bits, defaults to 512 bits
|
||||
* @returns {string} base64 encoded pbkdf2 derivation using sha1 for hmac
|
||||
*/
|
||||
kdfbinary: function(password, salt, iterations, length) {
|
||||
iterations = iterations || defaultIterations;
|
||||
length = length || 512;
|
||||
salt = sjcl.codec.base64.toBits(salt || defaultSalt);
|
||||
|
||||
var password = value1 + (value2 || '');
|
||||
var hash = sjcl.hash.sha256.hash(sjcl.hash.sha256.hash(password));
|
||||
var salt = sjcl.codec.base64.toBits(salt);
|
||||
var prff = function(key) {
|
||||
return new sjcl.misc.hmac(hash, sjcl.hash.sha1);
|
||||
};
|
||||
|
||||
var bits = sjcl.misc.pbkdf2(hash, salt, iterations, 64 * 8, prff);
|
||||
var base64 = sjcl.codec.base64.fromBits(bits);
|
||||
return base64;
|
||||
return sjcl.misc.pbkdf2(hash, salt, iterations, length, prff);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
|
||||
var Compatibility = require('../js/models/Compatibility');
|
||||
|
||||
describe('Compatibility', function() {
|
||||
var compat = new Compatibility();
|
||||
|
||||
describe('#import', function() {
|
||||
it('should not be able to decrypt with wrong password', function() {
|
||||
var wo = compat.importLegacy(encryptedLegacy1, 'badpassword');
|
||||
should.not.exist(wo);
|
||||
});
|
||||
|
||||
it('should be able to decrypt an old backup', function() {
|
||||
var wo = compat.importLegacy(encryptedLegacy1, legacyPassword1);
|
||||
should.exist(wo);
|
||||
wo.opts.id.should.equal('48ba2f1ffdfe9708');
|
||||
wo.opts.spendUnconfirmed.should.equal(true);
|
||||
wo.opts.requiredCopayers.should.equal(1);
|
||||
wo.opts.totalCopayers.should.equal(1);
|
||||
wo.opts.name.should.equal('pepe wallet');
|
||||
wo.opts.version.should.equal('0.4.7');
|
||||
wo.publicKeyRing.walletId.should.equal('48ba2f1ffdfe9708');
|
||||
wo.publicKeyRing.networkName.should.equal('testnet');
|
||||
wo.publicKeyRing.requiredCopayers.should.equal(1);
|
||||
wo.publicKeyRing.totalCopayers.should.equal(1);
|
||||
wo.publicKeyRing.indexes.length.should.equal(2);
|
||||
JSON.stringify(wo.publicKeyRing.indexes[0]).should.equal('{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":1}');
|
||||
JSON.stringify(wo.publicKeyRing.indexes[1]).should.equal('{"copayerIndex":0,"changeIndex":0,"receiveIndex":1}');
|
||||
wo.publicKeyRing.copayersBackup.length.should.equal(1);
|
||||
wo.publicKeyRing.copayersBackup[0].should.equal('0298f65b2694c55f9048bc05f10368242727c7f9d2065cbd788c3ecde1ec57f33f');
|
||||
wo.publicKeyRing.copayersExtPubKeys.length.should.equal(1);
|
||||
wo.publicKeyRing.copayersExtPubKeys[0].should.equal('tpubD9SGoP7CXsqSKTiQxCZSCpicDcophqnE4yuqjfw5M9tAR3fSjT9GDGwPEUFCN7SSmRKGDLZgKQePYFaLWyK32akeSan45TNTd8sgef9Ymh6');
|
||||
wo.privateKey.extendedPrivateKeyString.should.equal('tprv8ZgxMBicQKsPfQCscb7CtJKzixxcVSyrCVcfr3WCFbtT8kYTzNubhjQ5R7AuYJgPCcSH4R8T34YVxeohKGhAB9wbB4eFBbQFjUpjGCqptHm');
|
||||
wo.privateKey.networkName.should.equal('testnet');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var legacyPassword1 = '1DUpLRbuVpgLkcEY8gY8iod/SmA7+OheGZJ9PtvmTlvNE0FkEWpCKW9STdzXYJqbn0wiAapE4ojHNYj2hjYYAQ==';
|
||||
var encryptedLegacy1 = 'U2FsdGVkX19yGM1uBAIzQa8Po/dvUicmxt1YyRk/S97PcZ6I6rHMp9dMagIrehg4Qd6JHn/ustmFHS7vmBYj0EBpf6rdXiQezaWnVAJS9/xYjAO36EFUbl+NmUanuwujAxgYdSP/sNssRLeInvExmZYW993EEclxkwL6YUyX66kKsxGQo2oWng0NreBJNhFmrbOEWeFje2PiWP57oUjKsurFzwpluAAarUTYSLud+nXeabC7opzOP5yqniWBMJz0Ou8gpNCWCMhG/P9F9ccVPY7juyd0Hf41FVse8nd2++axKB57+paozLdO+HRfV6zkMqC3h8gWY7LkS75j3bvqcTw9LhXmzE0Sz21n9yDnRpA4chiAvtwQvvBGgj1pFMKhNQU6Obac9ZwKYzUTgdDn3Uzg1UlDzgyOh9S89rbRTV84WB+hXwhuVluWzbNNYV3vXe5PFrocVktIrtS3xQh+k/7my4A6/gRRrzNYpKrUASJqDS/9u9WBkG35xD63J/qXjtG2M0YPwbI57BK1IK4K510b8V72lz5U2XQrIC4ldBwni1rpSavwCJV9xF6hUdOmNV8fZsVHP0NeN1PYlLkSb2QgfuoWnkcsJerwuFR7GZC/i6efrswtpO0wMEQr/J0CLbeXlHAru6xxjCBhWoJvZpMGw72zgnDLoyMNsEVglNhx/VlV9ZMYkkdaEYAxPOEIyZdQ5MS+2jEAlXf818n/xzJSVrniCn9be8EPePvkw35pivprvy09vbW4cKsWBKvgIyoT6A3OhUOCCS8E9cg0WAjjav2EymrbKmGWRHaiD+EoJqaDg6s20zhHn1YEa/YwvGGSB5+Hg8baLHD8ZASvxz4cFFAAVZrBUedRFgHzqwaMUlFXLgueivWUj7RXlIw6GuNhLoo1QkhZMacf23hrFxxQYvGBRw1hekBuDmcsGWljA28udBxBd5f9i+3gErttMLJ6IPaud590uvrxRIclu0Sz9R2EQX64YJxqDtLpMY0PjddSMu8vaDRpK9/ZSrnz/xrXsyabaafz4rE/ItFXjwFUFkvtmuauHTz6nmuKjVfxvNLNAiKb/gI7vQyUhnTbKIApe7XyJsjedNDtZqsPoJRIzdDmrZYxGStbAZ7HThqFJlSJ9NPNhH+E2jm3TwL5mwt0fFZ5h+p497lHMtIcKffESo7KNa2juSVNMDREk0NcyxGXGiVB2FWl4sLdvyhcsVq0I7tmW6OGZKRf8W49GCJXq6Ie69DJ9LB1DO67NV1jsYbsLx9uhE2yEmpWZ3jkoCV/Eas4grxt0CGN6EavzQ==';
|
|
@ -1,30 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var Passphrase = copay.Passphrase;
|
||||
|
||||
describe('Passphrase model', function() {
|
||||
|
||||
it('should create an instance', function() {
|
||||
var p = new Passphrase();
|
||||
should.exist(p);
|
||||
});
|
||||
|
||||
it('should generate key from password', function(done) {
|
||||
var p = new Passphrase({
|
||||
salt: 'mjuBtGybi/4=',
|
||||
iterations: 10,
|
||||
});
|
||||
var pass = '123456';
|
||||
var k = p.get(pass);
|
||||
var k64 = p.getBase64(pass);
|
||||
|
||||
// Note: hashes were generated using CryptoJS
|
||||
k.toString().should.equal('2283fe11b9a189b82f1c09200806920cbdd8ef752f53dea910f90ab526f441acdbd5128555647a7e390a1a9fea042226963ccd0f7851030b3d6e282ccebaa17e');
|
||||
k64.toString().should.equal('IoP+EbmhibgvHAkgCAaSDL3Y73UvU96pEPkKtSb0Qazb1RKFVWR6fjkKGp/qBCImljzND3hRAws9bigszrqhfg==');
|
||||
|
||||
p.getBase64Async(pass, function(ret) {
|
||||
ret.toString().should.equal('IoP+EbmhibgvHAkgCAaSDL3Y73UvU96pEPkKtSb0Qazb1RKFVWR6fjkKGp/qBCImljzND3hRAws9bigszrqhfg==');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
'use strict';
|
||||
|
||||
var cryptoUtils = require('../js/util/crypto');
|
||||
var assert = require('assert');
|
||||
|
||||
describe('crypto utils', function() {
|
||||
|
||||
it('should use pbkdf2 to generate a passphrase from password', function() {
|
||||
var salt = 'mjuBtGybi/4=';
|
||||
var iterations = 10;
|
||||
var pass = '123456';
|
||||
var passphrase = cryptoUtils.kdf(pass, salt, iterations);
|
||||
|
||||
passphrase.toString().should.equal('IoP+EbmhibgvHAkgCAaSDL3Y73UvU96pEPkKtSb0Qazb1RKFVWR6fjkKGp/qBCImljzND3hRAws9bigszrqhfg==');
|
||||
});
|
||||
|
||||
it('should decrypt what it encrypts', function() {
|
||||
|
||||
var key = 'My secret key';
|
||||
var message = 'My secret message';
|
||||
var encrypted = cryptoUtils.encrypt(key, message);
|
||||
var decrypted = cryptoUtils.decrypt(key, encrypted);
|
||||
|
||||
decrypted.should.equal(message);
|
||||
});
|
||||
|
||||
it('should return null if the provided key cant decrypt', function() {
|
||||
var key = 'My secret key';
|
||||
var message = 'My secret message';
|
||||
var encrypted = cryptoUtils.encrypt(key, message);
|
||||
var decrypted = cryptoUtils.decrypt('Invalid key', encrypted);
|
||||
|
||||
assert(decrypted === null);
|
||||
});
|
||||
|
||||
});
|
|
@ -78,9 +78,6 @@ var createBundle = function(opts) {
|
|||
b.require('./js/models/PublicKeyRing', {
|
||||
expose: '../js/models/PublicKeyRing'
|
||||
});
|
||||
b.require('./js/models/Passphrase', {
|
||||
expose: '../js/models/Passphrase'
|
||||
});
|
||||
b.require('./js/models/HDPath', {
|
||||
expose: '../js/models/HDPath'
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
cd ./lib/sjcl && \
|
||||
./configure &&\
|
||||
./configure --with-sha1 &&\
|
||||
make && cp -v sjcl.js .. && echo "Done!" || echo " ## Please run $0 on copay root directory"
|
||||
|
||||
|
|
Loading…
Reference in New Issue