Create a Compatibility namespace
This commit is contained in:
parent
202a047edc
commit
1b0f6836dc
14 changed files with 257 additions and 275 deletions
|
|
@ -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;
|
||||
|
||||
var skipFields = [];
|
||||
if (skipPublicKeyRing)
|
||||
skipFields.push('publicKeyRing');
|
||||
|
||||
if (skipTxProposals)
|
||||
skipFields.push('txProposals');
|
||||
|
||||
// try to import encrypted wallet with passphrase
|
||||
try {
|
||||
w = walletFactory.import(encryptedObj, passphrase, skipFields);
|
||||
} catch (e) {
|
||||
errMsg = e.message;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!walletId
|
||||
|| walletId === 'nameFor'
|
||||
|| walletId === 'lock'
|
||||
|| walletId === 'wallet') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof uniq[walletId] === 'undefined') {
|
||||
walletIds.push(walletId);
|
||||
uniq[walletId] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cb(walletIds);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
var decrypted = CryptoJS.AES.decrypt(base64, passphrase);
|
||||
if (decrypted)
|
||||
decryptedStr = decrypted.toString(CryptoJS.enc.Utf8);
|
||||
} catch (e) {
|
||||
// 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([]);
|
||||
}
|
||||
|
||||
_.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) {
|
||||
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);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue