mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-26 12:25:20 +01:00
refactor for enc type header and CryptoKey
This commit is contained in:
parent
1edda8b9c0
commit
863cfbad4a
@ -86,7 +86,7 @@ chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
|
||||
setIcon();
|
||||
function setIcon() {
|
||||
userService.isAuthenticated(function (isAuthenticated) {
|
||||
cryptoService.getKey(false, function (key) {
|
||||
cryptoService.getKey(function (key) {
|
||||
var suffix = '';
|
||||
if (!isAuthenticated) {
|
||||
suffix = '_gray';
|
||||
@ -737,7 +737,7 @@ function checkLock() {
|
||||
return;
|
||||
}
|
||||
|
||||
cryptoService.getKey(false, function (key) {
|
||||
cryptoService.getKey(function (key) {
|
||||
if (!key) {
|
||||
// no key so no need to lock
|
||||
return;
|
||||
|
@ -1,13 +1,87 @@
|
||||
var CipherString = function (encryptedString) {
|
||||
this.encryptedString = encryptedString;
|
||||
var CipherString = function () {
|
||||
this.encryptedString = null;
|
||||
this.encryptionType = null;
|
||||
this.decryptedValue = null;
|
||||
this.cipherText = null;
|
||||
this.initializationVector = null;
|
||||
this.mac = null;
|
||||
|
||||
if (encryptedString) {
|
||||
var encPieces = this.encryptedString.split('|');
|
||||
if (arguments.length >= 2) {
|
||||
this.encryptedString = arguments[0] + '.' + arguments[1];
|
||||
|
||||
if (arguments.length > 2 && arguments[2]) {
|
||||
this.encryptedString += ('|' + arguments[2]);
|
||||
}
|
||||
|
||||
if (arguments.length > 3 && arguments[3]) {
|
||||
this.encryptedString += ('|' + arguments[3]);
|
||||
}
|
||||
|
||||
this.encryptionType = arguments[0];
|
||||
this.cipherText = arguments[1];
|
||||
this.initializationVector = arguments[2] || null;
|
||||
this.mac = arguments[3] || null;
|
||||
|
||||
return;
|
||||
}
|
||||
else if (arguments.length !== 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.encryptedString = arguments[0];
|
||||
if (!this.encryptedString) {
|
||||
return;
|
||||
}
|
||||
|
||||
var constants = chrome.extension.getBackgroundPage().constantsService;
|
||||
|
||||
var headerPieces = this.encryptedString.split('.'),
|
||||
encPieces;
|
||||
|
||||
if (headerPieces.length === 2) {
|
||||
try {
|
||||
this.encryptionType = parseInt(headerPieces[0]);
|
||||
encPieces = headerPieces[1].split('|');
|
||||
}
|
||||
catch (e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
encPieces = this.encryptedString.split('|');
|
||||
this.encryptionType = encPieces.length === 3 ? constants.encType.AesCbc128_HmacSha256_B64 :
|
||||
constants.encType.AesCbc256_B64;
|
||||
}
|
||||
|
||||
switch (this.encryptionType) {
|
||||
case constants.encType.AesCbc128_HmacSha256_B64:
|
||||
case constants.encType.AesCbc256_HmacSha256_B64:
|
||||
if (encPieces.length !== 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.initializationVector = encPieces[0];
|
||||
this.cipherText = encPieces[1];
|
||||
this.mac = encPieces.length > 2 ? encPieces[2] : null;
|
||||
this.mac = encPieces[2];
|
||||
break;
|
||||
case constants.encType.AesCbc256_B64:
|
||||
if (encPieces.length !== 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.initializationVector = encPieces[0];
|
||||
this.cipherText = encPieces[1];
|
||||
break;
|
||||
case constants.encType.Rsa2048_OaepSha256_B64:
|
||||
case constants.encType.Rsa2048_OaepSha1_B64:
|
||||
if (encPieces.length !== 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.cipherText = encPieces[0];
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
@ -52,6 +126,9 @@ var Folder = function (obj, alreadyEncrypted) {
|
||||
cryptoService.decrypt(this).then(function (decValue) {
|
||||
this.decryptedValue = decValue;
|
||||
deferred.resolve(this.decryptedValue);
|
||||
}, function () {
|
||||
this.decryptedValue = '[error: cannot decrypt]';
|
||||
deferred.resolve(this.decryptedValue);
|
||||
});
|
||||
}
|
||||
else {
|
||||
|
@ -14,7 +14,7 @@
|
||||
var userService = $injector.get('userService');
|
||||
var cryptoService = $injector.get('cryptoService');
|
||||
|
||||
cryptoService.getKey(false, function (key) {
|
||||
cryptoService.getKey(function (key) {
|
||||
userService.isAuthenticated(function (isAuthenticated) {
|
||||
if (isAuthenticated) {
|
||||
if (!key) {
|
||||
|
@ -24,7 +24,7 @@
|
||||
userService.getEmail(function (email) {
|
||||
var key = cryptoService.makeKey($scope.masterPassword, email);
|
||||
cryptoService.hashPassword($scope.masterPassword, key, function (keyHash) {
|
||||
cryptoService.getKeyHash(true, function (storedKeyHash) {
|
||||
cryptoService.getKeyHash(function (storedKeyHash) {
|
||||
if (storedKeyHash && keyHash && storedKeyHash === keyHash) {
|
||||
cryptoService.setKey(key, function () {
|
||||
chrome.runtime.sendMessage({ command: 'unlocked' });
|
||||
|
@ -26,7 +26,7 @@
|
||||
}
|
||||
|
||||
chrome.storage.local.set(obj, function () {
|
||||
cryptoService.getKeyHash(false, function (keyHash) {
|
||||
cryptoService.getKeyHash(function (keyHash) {
|
||||
if (keyHash) {
|
||||
cryptoService.toggleKey(function () { });
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
userService.getEmail(function (email) {
|
||||
var key = cryptoService.makeKey($scope.masterPassword, email);
|
||||
cryptoService.hashPassword($scope.masterPassword, key, function (keyHash) {
|
||||
cryptoService.getKeyHash(true, function (storedKeyHash) {
|
||||
cryptoService.getKeyHash(function (storedKeyHash) {
|
||||
if (storedKeyHash && keyHash && storedKeyHash === keyHash) {
|
||||
deferred.resolve();
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
function ApiService(tokenService, appIdService, utilsService, logoutCallback) {
|
||||
//this.baseUrl = 'http://localhost:4000';
|
||||
this.baseUrl = 'https://api.bitwarden.com';
|
||||
this.baseUrl = 'http://localhost:4000';
|
||||
//this.baseUrl = 'https://api.bitwarden.com';
|
||||
this.tokenService = tokenService;
|
||||
this.logoutCallback = logoutCallback;
|
||||
this.appIdService = appIdService;
|
||||
|
@ -4,6 +4,13 @@ function ConstantsService() {
|
||||
disableAddLoginNotificationKey: 'disableAddLoginNotification',
|
||||
disableContextMenuItemKey: 'disableContextMenuItem',
|
||||
lockOptionKey: 'lockOption',
|
||||
lastActiveKey: 'lastActive'
|
||||
lastActiveKey: 'lastActive',
|
||||
encType: {
|
||||
AesCbc256_B64: 0,
|
||||
AesCbc128_HmacSha256_B64: 1,
|
||||
AesCbc256_HmacSha256_B64: 2,
|
||||
Rsa2048_OaepSha256_B64: 3,
|
||||
Rsa2048_OaepSha1_B64: 4
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -1,15 +1,13 @@
|
||||
function CryptoService(constantsService) {
|
||||
this.constantsService = constantsService;
|
||||
initCryptoService();
|
||||
initCryptoService(constantsService);
|
||||
};
|
||||
|
||||
function initCryptoService() {
|
||||
function initCryptoService(constantsService) {
|
||||
var _key,
|
||||
_b64Key,
|
||||
_legacyEtmKey,
|
||||
_keyHash,
|
||||
_b64KeyHash,
|
||||
_encKey,
|
||||
_macKey;
|
||||
_b64KeyHash;
|
||||
|
||||
CryptoService.prototype.setKey = function (key, callback) {
|
||||
if (!callback || typeof callback !== 'function') {
|
||||
@ -27,7 +25,7 @@ function initCryptoService() {
|
||||
}
|
||||
|
||||
chrome.storage.local.set({
|
||||
'key': forge.util.encode64(key)
|
||||
'key': key.keyB64
|
||||
}, function () {
|
||||
callback();
|
||||
});
|
||||
@ -39,33 +37,24 @@ function initCryptoService() {
|
||||
throw 'callback function required';
|
||||
}
|
||||
|
||||
_keyHash = forge.util.encode64(keyHash);
|
||||
_keyHash = keyHash;
|
||||
|
||||
chrome.storage.local.set({
|
||||
'keyHash': keyHash
|
||||
'keyHash': _keyHash
|
||||
}, function () {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
CryptoService.prototype.getKey = function (b64, callback) {
|
||||
CryptoService.prototype.getKey = function (callback) {
|
||||
if (!callback || typeof callback !== 'function') {
|
||||
throw 'callback function required';
|
||||
}
|
||||
|
||||
if (b64 && b64 === true && _b64Key) {
|
||||
callback(_b64Key);
|
||||
return;
|
||||
}
|
||||
else if (!b64 && _key) {
|
||||
if (_key) {
|
||||
callback(_key);
|
||||
return;
|
||||
}
|
||||
else if (b64 && b64 === true && _key && !_b64Key) {
|
||||
_b64Key = forge.util.encode64(_key);
|
||||
callback(_b64Key);
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
chrome.storage.local.get(self.constantsService.lockOptionKey, function (obj) {
|
||||
@ -77,13 +66,7 @@ function initCryptoService() {
|
||||
|
||||
chrome.storage.local.get('key', function (obj) {
|
||||
if (obj && obj.key) {
|
||||
_key = forge.util.decode64(obj.key);
|
||||
|
||||
if (b64 && b64 === true) {
|
||||
_b64Key = obj.key;
|
||||
callback(_b64Key);
|
||||
return;
|
||||
}
|
||||
_key = new CryptoKey(obj.key, true);
|
||||
}
|
||||
|
||||
callback(_key);
|
||||
@ -91,67 +74,19 @@ function initCryptoService() {
|
||||
});
|
||||
};
|
||||
|
||||
CryptoService.prototype.getEncKey = function (callback) {
|
||||
CryptoService.prototype.getKeyHash = function (callback) {
|
||||
if (!callback || typeof callback !== 'function') {
|
||||
throw 'callback function required';
|
||||
}
|
||||
|
||||
if (_encKey) {
|
||||
callback(_encKey);
|
||||
}
|
||||
|
||||
this.getKey(false, function (key) {
|
||||
var buffer = forge.util.createBuffer(key);
|
||||
_encKey = buffer.getBytes(16);
|
||||
callback(_encKey);
|
||||
});
|
||||
};
|
||||
|
||||
CryptoService.prototype.getMacKey = function (callback) {
|
||||
if (!callback || typeof callback !== 'function') {
|
||||
throw 'callback function required';
|
||||
}
|
||||
|
||||
if (_macKey) {
|
||||
callback(_macKey);
|
||||
}
|
||||
|
||||
this.getKey(false, function (key) {
|
||||
var buffer = forge.util.createBuffer(key);
|
||||
buffer.getBytes(16);
|
||||
_macKey = buffer.getBytes(16);
|
||||
callback(_macKey);
|
||||
});
|
||||
};
|
||||
|
||||
CryptoService.prototype.getKeyHash = function (b64, callback) {
|
||||
if (!callback || typeof callback !== 'function') {
|
||||
throw 'callback function required';
|
||||
}
|
||||
|
||||
if (b64 && b64 === true && _b64KeyHash) {
|
||||
callback(_b64KeyHash);
|
||||
return;
|
||||
}
|
||||
else if (!b64 && _keyHash) {
|
||||
if (_keyHash) {
|
||||
callback(_keyHash);
|
||||
return;
|
||||
}
|
||||
else if (b64 && b64 === true && _keyHash && !_b64KeyHash) {
|
||||
_b64KeyHash = forge.util.encode64(_keyHash);
|
||||
callback(_b64KeyHash);
|
||||
return;
|
||||
}
|
||||
|
||||
chrome.storage.local.get('keyHash', function (obj) {
|
||||
if (obj && obj.keyHash) {
|
||||
_keyHash = forge.util.decode64(obj.keyHash);
|
||||
|
||||
if (b64 && b64 === true) {
|
||||
_b64KeyHash = obj.keyHash;
|
||||
callback(_b64KeyHash);
|
||||
return;
|
||||
}
|
||||
_keyHash = obj.keyHash;
|
||||
}
|
||||
|
||||
callback(_keyHash);
|
||||
@ -163,7 +98,7 @@ function initCryptoService() {
|
||||
throw 'callback function required';
|
||||
}
|
||||
|
||||
_key = _b64Key = _macKey = _encKey = null;
|
||||
_key = _legacyEtmKey = null;
|
||||
chrome.storage.local.remove('key', function () {
|
||||
callback();
|
||||
});
|
||||
@ -174,7 +109,7 @@ function initCryptoService() {
|
||||
throw 'callback function required';
|
||||
}
|
||||
|
||||
_keyHash = _b64KeyHash = null;
|
||||
_keyHash = null;
|
||||
chrome.storage.local.remove('keyHash', function () {
|
||||
callback();
|
||||
});
|
||||
@ -186,7 +121,7 @@ function initCryptoService() {
|
||||
}
|
||||
|
||||
var self = this;
|
||||
self.getKey(false, function (key) {
|
||||
self.getKey(function (key) {
|
||||
chrome.storage.local.get(self.constantsService.lockOptionKey, function (obj) {
|
||||
if (obj && (obj[self.constantsService.lockOptionKey] || obj[self.constantsService.lockOptionKey] === 0)) {
|
||||
// if we have a lock option set, clear the key
|
||||
@ -207,53 +142,44 @@ function initCryptoService() {
|
||||
});
|
||||
};
|
||||
|
||||
CryptoService.prototype.makeKey = function (password, salt, b64) {
|
||||
var key = forge.pbkdf2(forge.util.encodeUtf8(password), forge.util.encodeUtf8(salt),
|
||||
CryptoService.prototype.makeKey = function (password, salt) {
|
||||
var keyBytes = forge.pbkdf2(forge.util.encodeUtf8(password), forge.util.encodeUtf8(salt),
|
||||
5000, 256 / 8, 'sha256');
|
||||
|
||||
if (b64 && b64 === true) {
|
||||
return forge.util.encode64(key);
|
||||
}
|
||||
|
||||
return key;
|
||||
return new CryptoKey(keyBytes);
|
||||
};
|
||||
|
||||
CryptoService.prototype.hashPassword = function (password, key, callback) {
|
||||
this.getKey(false, function (storedKey) {
|
||||
if (!key) {
|
||||
key = storedKey;
|
||||
}
|
||||
this.getKey(function (storedKey) {
|
||||
key = key || storedKey;
|
||||
|
||||
if (!password || !key) {
|
||||
throw 'Invalid parameters.';
|
||||
}
|
||||
|
||||
var hashBits = forge.pbkdf2(key, forge.util.encodeUtf8(password), 1, 256 / 8, 'sha256');
|
||||
var hashBits = forge.pbkdf2(key.key, forge.util.encodeUtf8(password), 1, 256 / 8, 'sha256');
|
||||
callback(forge.util.encode64(hashBits));
|
||||
});
|
||||
};
|
||||
|
||||
CryptoService.prototype.encrypt = function (plaintextValue) {
|
||||
CryptoService.prototype.encrypt = function (plainValue, key, plainValueEncoding) {
|
||||
var self = this;
|
||||
var deferred = Q.defer();
|
||||
|
||||
if (plaintextValue === null || plaintextValue === undefined) {
|
||||
if (plainValue === null || plainValue === undefined) {
|
||||
deferred.resolve(null);
|
||||
}
|
||||
else {
|
||||
self.getKey(false, function (key) {
|
||||
self.getEncKey(function (theEncKey) {
|
||||
self.getMacKey(function (macKey) {
|
||||
if (!key || !theEncKey || !macKey) {
|
||||
self.getKey(function (localKey) {
|
||||
key = key || localKey;
|
||||
if (!key) {
|
||||
throw 'Encryption key unavailable.';
|
||||
}
|
||||
|
||||
// TODO: Turn on whenever ready to support encrypt-then-mac
|
||||
var encKey = false ? theEncKey : key;
|
||||
|
||||
var buffer = forge.util.createBuffer(plaintextValue, 'utf8');
|
||||
plainValueEncoding = plainValueEncoding || 'utf8';
|
||||
var buffer = forge.util.createBuffer(plainValue, plainValueEncoding);
|
||||
var ivBytes = forge.random.getBytesSync(16);
|
||||
var cipher = forge.cipher.createCipher('AES-CBC', encKey);
|
||||
var cipher = forge.cipher.createCipher('AES-CBC', key.encKey);
|
||||
cipher.start({ iv: ivBytes });
|
||||
cipher.update(buffer);
|
||||
cipher.finish();
|
||||
@ -261,25 +187,17 @@ function initCryptoService() {
|
||||
var iv = forge.util.encode64(ivBytes);
|
||||
var ctBytes = cipher.output.getBytes();
|
||||
var ct = forge.util.encode64(ctBytes);
|
||||
var cipherString = iv + '|' + ct;
|
||||
var mac = !key.macKey ? null : computeMac(ctBytes, ivBytes, key.macKey);
|
||||
|
||||
// TODO: Turn on whenever ready to support encrypt-then-mac
|
||||
if (false) {
|
||||
var mac = computeMac(ctBytes, ivBytes, macKey);
|
||||
cipherString = cipherString + '|' + mac;
|
||||
}
|
||||
|
||||
var cs = new CipherString(cipherString);
|
||||
var cs = new CipherString(key.encType, iv, ct, mac);
|
||||
deferred.resolve(cs);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
CryptoService.prototype.decrypt = function (cipherString) {
|
||||
CryptoService.prototype.decrypt = function (cipherString, key, outputEncoding) {
|
||||
var deferred = Q.defer();
|
||||
var self = this;
|
||||
|
||||
@ -287,35 +205,56 @@ function initCryptoService() {
|
||||
throw 'cannot decrypt nothing';
|
||||
}
|
||||
|
||||
self.getKey(false, function (key) {
|
||||
self.getEncKey(function (theEncKey) {
|
||||
self.getMacKey(function (macKey) {
|
||||
if (!macKey) {
|
||||
throw 'MAC key unavailable.';
|
||||
self.getKey(function (localKey) {
|
||||
key = key || localKey;
|
||||
if (!key) {
|
||||
throw 'Encryption key unavailable.';
|
||||
}
|
||||
|
||||
outputEncoding = outputEncoding || 'utf8';
|
||||
|
||||
if (cipherString.encryptionType === constantsService.encType.AesCbc128_HmacSha256_B64 &&
|
||||
key.encType === constantsService.encType.AesCbc256_B64) {
|
||||
// Old encrypt-then-mac scheme, swap out the key
|
||||
_legacyEtmKey = _legacyEtmKey ||
|
||||
new CryptoKey(key.key, false, constantsService.encType.AesCbc128_HmacSha256_B64);
|
||||
key = _legacyEtmKey;
|
||||
}
|
||||
|
||||
if (cipherString.encryptionType !== key.encType) {
|
||||
throw 'encType unavailable.';
|
||||
}
|
||||
|
||||
var ivBytes = forge.util.decode64(cipherString.initializationVector);
|
||||
var ctBytes = forge.util.decode64(cipherString.cipherText);
|
||||
|
||||
var computedMac = null;
|
||||
if (cipherString.mac) {
|
||||
computedMac = computeMac(ctBytes, ivBytes, macKey);
|
||||
if (key.macKey && cipherString.mac) {
|
||||
var computedMac = computeMac(ctBytes, ivBytes, key.macKey);
|
||||
if (computedMac !== cipherString.mac) {
|
||||
console.error('MAC failed.');
|
||||
deferred.reject('MAC failed.');
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
var ctBuffer = forge.util.createBuffer(ctBytes);
|
||||
var decipher = forge.cipher.createDecipher('AES-CBC', computedMac ? theEncKey : key);
|
||||
var decipher = forge.cipher.createDecipher('AES-CBC', key.encKey);
|
||||
decipher.start({ iv: ivBytes });
|
||||
decipher.update(ctBuffer);
|
||||
decipher.finish();
|
||||
|
||||
var decValue = decipher.output.toString('utf8');
|
||||
var decValue;
|
||||
if (outputEncoding === 'utf8') {
|
||||
decValue = decipher.output.toString('utf8');
|
||||
}
|
||||
else {
|
||||
decValue = decipher.output.getBytes();
|
||||
}
|
||||
deferred.resolve(decValue);
|
||||
});
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
deferred.reject('Decryption failed.');
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
@ -328,4 +267,52 @@ function initCryptoService() {
|
||||
var mac = hmac.digest();
|
||||
return forge.util.encode64(mac.getBytes());
|
||||
}
|
||||
|
||||
function CryptoKey(keyBytes, b64KeyBytes, encType) {
|
||||
if (b64KeyBytes) {
|
||||
keyBytes = forge.util.decode64(keyBytes);
|
||||
}
|
||||
|
||||
if (!keyBytes) {
|
||||
throw 'Must provide keyBytes';
|
||||
}
|
||||
|
||||
var buffer = forge.util.createBuffer(keyBytes);
|
||||
if (!buffer || buffer.length() === 0) {
|
||||
throw 'Couldn\'t make buffer';
|
||||
}
|
||||
var bufferLength = buffer.length();
|
||||
|
||||
if (encType === null || encType === undefined) {
|
||||
if (bufferLength === 32) {
|
||||
encType = constantsService.encType.AesCbc256_B64;
|
||||
}
|
||||
else if (bufferLength === 64) {
|
||||
encType = constantsService.encType.AesCbc256_HmacSha256_B64;
|
||||
}
|
||||
else {
|
||||
throw 'Unable to determine encType.';
|
||||
}
|
||||
}
|
||||
|
||||
this.key = keyBytes;
|
||||
this.keyB64 = forge.util.encode64(keyBytes);
|
||||
this.encType = encType;
|
||||
|
||||
if (encType === constantsService.encType.AesCbc256_B64 && bufferLength === 32) {
|
||||
this.encKey = keyBytes;
|
||||
this.macKey = null;
|
||||
}
|
||||
else if (encType === constantsService.encType.AesCbc128_HmacSha256_B64 && bufferLength === 32) {
|
||||
this.encKey = buffer.getBytes(16); // first half
|
||||
this.macKey = buffer.getBytes(16); // second half
|
||||
}
|
||||
else if (encType === constantsService.encType.AesCbc256_HmacSha256_B64 && bufferLength === 64) {
|
||||
this.encKey = buffer.getBytes(32); // first half
|
||||
this.macKey = buffer.getBytes(32); // second half
|
||||
}
|
||||
else {
|
||||
throw 'Unsupported encType/key length.';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -68,7 +68,7 @@ function initFolderService() {
|
||||
var deferred = Q.defer();
|
||||
var self = this;
|
||||
|
||||
cryptoService.getKey(false, function (key) {
|
||||
cryptoService.getKey(function (key) {
|
||||
if (!key) {
|
||||
deferred.reject();
|
||||
return;
|
||||
|
@ -86,7 +86,7 @@ function initLoginService() {
|
||||
var deferred = Q.defer();
|
||||
var self = this;
|
||||
|
||||
cryptoService.getKey(false, function (key) {
|
||||
cryptoService.getKey(function (key) {
|
||||
if (!key) {
|
||||
deferred.reject();
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user