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.TxProposal = require('./js/models/TxProposal');
|
||||||
module.exports.TxProposals = require('./js/models/TxProposals');
|
module.exports.TxProposals = require('./js/models/TxProposals');
|
||||||
module.exports.PrivateKey = require('./js/models/PrivateKey');
|
module.exports.PrivateKey = require('./js/models/PrivateKey');
|
||||||
module.exports.Passphrase = require('./js/models/Passphrase');
|
|
||||||
module.exports.HDPath = require('./js/models/HDPath');
|
module.exports.HDPath = require('./js/models/HDPath');
|
||||||
module.exports.HDParams = require('./js/models/HDParams');
|
module.exports.HDParams = require('./js/models/HDParams');
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('copayApp.controllers').controller('ImportController',
|
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';
|
$rootScope.title = 'Import a backup';
|
||||||
$scope.importStatus = 'Importing wallet - Reading backup...';
|
$scope.importStatus = 'Importing wallet - Reading backup...';
|
||||||
|
|
|
@ -1,94 +1,173 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
var Identity = require('Identity'),
|
|
||||||
Passphrase = require('Passphrase'),
|
|
||||||
Wallet = require('Wallet'),
|
|
||||||
// walletFactory = new WalletFactory(),
|
|
||||||
passphrase = new Passphrase();
|
|
||||||
|
|
||||||
function Compatibility(){
|
var Identity = require('./Identity');
|
||||||
// - preDotEightListWallets()
|
var Wallet = require('./Wallet');
|
||||||
// - preDotEightImportWalletToStorage(walletId, passphrase, profile) (edited)
|
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) {
|
if (!walletId
|
||||||
passphrase.getBase64Async(password, function(passphrase) {
|
|| walletId === 'nameFor'
|
||||||
// updateStatus('Importing wallet - Setting things up...');
|
|| walletId === 'lock'
|
||||||
var w, errMsg;
|
|| walletId === 'wallet') {
|
||||||
|
continue;
|
||||||
var skipFields = [];
|
}
|
||||||
if (skipPublicKeyRing)
|
|
||||||
skipFields.push('publicKeyRing');
|
if (typeof uniq[walletId] === 'undefined') {
|
||||||
|
walletIds.push(walletId);
|
||||||
if (skipTxProposals)
|
uniq[walletId] = 1;
|
||||||
skipFields.push('txProposals');
|
}
|
||||||
|
}
|
||||||
// try to import encrypted wallet with passphrase
|
}
|
||||||
try {
|
return cb(walletIds);
|
||||||
w = walletFactory.import(encryptedObj, passphrase, skipFields);
|
};
|
||||||
} catch (e) {
|
|
||||||
errMsg = e.message;
|
/**
|
||||||
}
|
* Decrypts using the CryptoJS library (unknown encryption schema)
|
||||||
|
*
|
||||||
if (!w) {
|
* Don't use CryptoJS to encrypt. This still exists for compatibility reasons only.
|
||||||
// $scope.loading = false;
|
*/
|
||||||
// notification.error('Error', errMsg || 'Wrong password');
|
Compatibility._decrypt = function(base64, passphrase) {
|
||||||
$rootScope.$digest();
|
var decryptedStr = null;
|
||||||
return;
|
try {
|
||||||
}
|
var decrypted = CryptoJS.AES.decrypt(base64, passphrase);
|
||||||
|
if (decrypted)
|
||||||
// if wallet was never used, we're done
|
decryptedStr = decrypted.toString(CryptoJS.enc.Utf8);
|
||||||
if (!w.isReady()) {
|
} catch (e) {
|
||||||
$rootScope.wallet = w;
|
// Error while decrypting
|
||||||
// controllerUtils.startNetwork($rootScope.wallet, $scope);
|
return null;
|
||||||
return;
|
}
|
||||||
}
|
return decryptedStr;
|
||||||
|
};
|
||||||
// if it was used, we need to scan for indices
|
|
||||||
w.updateIndexes(function(err) {
|
/**
|
||||||
// updateStatus('Importing wallet - We are almost there...');
|
* Reads an item from localstorage, decrypts it with passphrase
|
||||||
if (err) {
|
*/
|
||||||
// $scope.loading = false;
|
Compatibility._read = function(k, passphrase, cb) {
|
||||||
// notification.error('Error', 'Error updating indexes: ' + err);
|
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([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
_.each(ids, function(id) {
|
||||||
|
var name = localStorage.getItem('nameFor::' + id);
|
||||||
|
if (name) {
|
||||||
|
wallets.push({
|
||||||
|
id: id,
|
||||||
|
name: name,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
$rootScope.wallet = w;
|
|
||||||
// controllerUtils.startNetwork($rootScope.wallet, $scope);
|
|
||||||
});
|
});
|
||||||
|
return cb(wallets);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Compatibility.prototype.fromEncryptedObj = function(base64, passphrase, skipFields) {
|
Compatibility.getWallets2 = function(cb) {
|
||||||
this.storage.setPassphrase(passphrase);
|
var self = this;
|
||||||
var walletObj = this.storage.import(base64);
|
var re = /wallet::([^_]+)(_?(.*))/;
|
||||||
if (!walletObj) return false;
|
|
||||||
return this.fromObj(walletObj, skipFields);
|
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);
|
* Lists all wallets in localstorage
|
||||||
preconditions.checkState(networkName);
|
*/
|
||||||
preconditions.checkArgument(inObj);
|
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
|
for (key in localStorage) {
|
||||||
obj.opts = obj.opts || {};
|
if (key.indexOf('wallet::' + walletId) !== -1) {
|
||||||
obj.opts.reconnectDelay = this.walletDefaults.reconnectDelay;
|
var ret = self._read(localStorage.getItem(key), passphrase);
|
||||||
|
if (err) return cb(err);
|
||||||
|
|
||||||
skipFields = skipFields || [];
|
_.each(Wallet.PERSISTED_PROPERTIES, function(p) {
|
||||||
skipFields.forEach(function(k) {
|
obj[p] = ret[p];
|
||||||
if (obj[k]) {
|
});
|
||||||
delete obj[k];
|
|
||||||
} else
|
|
||||||
throw new Error('unknown field:' + k);
|
|
||||||
});
|
|
||||||
|
|
||||||
var w = Wallet.fromObj(obj, this.storage, this.networks[networkName], this.blockchains[networkName]);
|
if (!_.any(_.values(obj)))
|
||||||
if (!w) return false;
|
return cb(new Error('Wallet not found'));
|
||||||
this._checkVersion(w.version);
|
|
||||||
return w;
|
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;
|
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;
|
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) {
|
Wallet.prototype.getTransactionHistory = function(cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
|
@ -8,7 +8,7 @@ function EncryptedInsightStorage(config) {
|
||||||
inherits(EncryptedInsightStorage, InsightStorage);
|
inherits(EncryptedInsightStorage, InsightStorage);
|
||||||
|
|
||||||
EncryptedInsightStorage.prototype.getItem = function(name, callback) {
|
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,
|
InsightStorage.prototype.getItem.apply(this, [name,
|
||||||
function(err, body) {
|
function(err, body) {
|
||||||
var decryptedJson = cryptoUtil.decrypt(key, body);
|
var decryptedJson = cryptoUtil.decrypt(key, body);
|
||||||
|
@ -21,13 +21,13 @@ EncryptedInsightStorage.prototype.getItem = function(name, callback) {
|
||||||
};
|
};
|
||||||
|
|
||||||
EncryptedInsightStorage.prototype.setItem = function(name, value, 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);
|
var record = cryptoUtil.encrypt(key, value);
|
||||||
InsightStorage.prototype.setItem.apply(this, [name, record, callback]);
|
InsightStorage.prototype.setItem.apply(this, [name, record, callback]);
|
||||||
};
|
};
|
||||||
|
|
||||||
EncryptedInsightStorage.prototype.removeItem = function(name, 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]);
|
InsightStorage.prototype.removeItem.apply(this, [name, callback]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ function EncryptedLocalStorage(config) {
|
||||||
inherits(EncryptedLocalStorage, LocalStorage);
|
inherits(EncryptedLocalStorage, LocalStorage);
|
||||||
|
|
||||||
EncryptedLocalStorage.prototype.getItem = function(name, callback) {
|
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) {
|
LocalStorage.prototype.getItem.apply(this, [name, function(err, body) {
|
||||||
var decryptedJson = cryptoUtil.decrypt(key, body);
|
var decryptedJson = cryptoUtil.decrypt(key, body);
|
||||||
if (!decryptedJson) {
|
if (!decryptedJson) {
|
||||||
|
@ -19,7 +19,7 @@ EncryptedLocalStorage.prototype.getItem = function(name, callback) {
|
||||||
};
|
};
|
||||||
|
|
||||||
EncryptedLocalStorage.prototype.setItem = function(name, value, 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)) {
|
if (!_.isString(value)) {
|
||||||
value = JSON.stringify(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 defaultSalt = 'mjuBtGybi/4=';
|
||||||
var defaultIterations = 100;
|
var defaultIterations = 100;
|
||||||
|
|
||||||
sjcl.defaults = {
|
|
||||||
v: 1,
|
|
||||||
iter: 100,
|
|
||||||
ks: 128,
|
|
||||||
ts: 64,
|
|
||||||
mode: "ccm",
|
|
||||||
adata: "",
|
|
||||||
cipher: "aes"
|
|
||||||
},
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
kdf: function(value1, value2, salt, iterations) {
|
/**
|
||||||
iterations = iterations || defaultIterations;
|
* @param {string} password
|
||||||
salt = salt || defaultSalt;
|
* @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 hash = sjcl.hash.sha256.hash(sjcl.hash.sha256.hash(password));
|
||||||
var salt = sjcl.codec.base64.toBits(salt);
|
|
||||||
var prff = function(key) {
|
var prff = function(key) {
|
||||||
return new sjcl.misc.hmac(hash, sjcl.hash.sha1);
|
return new sjcl.misc.hmac(hash, sjcl.hash.sha1);
|
||||||
};
|
};
|
||||||
|
|
||||||
var bits = sjcl.misc.pbkdf2(hash, salt, iterations, 64 * 8, prff);
|
return sjcl.misc.pbkdf2(hash, salt, iterations, length, prff);
|
||||||
var base64 = sjcl.codec.base64.fromBits(bits);
|
|
||||||
return base64;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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', {
|
b.require('./js/models/PublicKeyRing', {
|
||||||
expose: '../js/models/PublicKeyRing'
|
expose: '../js/models/PublicKeyRing'
|
||||||
});
|
});
|
||||||
b.require('./js/models/Passphrase', {
|
|
||||||
expose: '../js/models/Passphrase'
|
|
||||||
});
|
|
||||||
b.require('./js/models/HDPath', {
|
b.require('./js/models/HDPath', {
|
||||||
expose: '../js/models/HDPath'
|
expose: '../js/models/HDPath'
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
cd ./lib/sjcl && \
|
cd ./lib/sjcl && \
|
||||||
./configure &&\
|
./configure --with-sha1 &&\
|
||||||
make && cp -v sjcl.js .. && echo "Done!" || echo " ## Please run $0 on copay root directory"
|
make && cp -v sjcl.js .. && echo "Done!" || echo " ## Please run $0 on copay root directory"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue