mirror of
https://github.com/bitwarden/web
synced 2025-12-16 08:13:22 +00:00
move pbkdf2 to web crypto with shim fallback
This commit is contained in:
@@ -149,12 +149,12 @@ angular
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = cryptoService.makeKey(_masterPassword, _email);
|
return cryptoService.makeKeyAndHash(_email, _masterPassword).then(function (result) {
|
||||||
var hash = cryptoService.hashPassword(_masterPassword, key);
|
return apiService.twoFactor.sendEmailLogin({
|
||||||
apiService.twoFactor.sendEmailLogin({
|
email: _email,
|
||||||
email: _email,
|
masterPasswordHash: result.hash
|
||||||
masterPasswordHash: hash
|
}).$promise;
|
||||||
}, function () {
|
}).then(function () {
|
||||||
if (doToast) {
|
if (doToast) {
|
||||||
toastr.success('Verification email sent to ' + $scope.twoFactorEmail + '.');
|
toastr.success('Verification email sent to ' + $scope.twoFactorEmail + '.');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,17 +6,16 @@ angular
|
|||||||
|
|
||||||
$scope.submit = function (model) {
|
$scope.submit = function (model) {
|
||||||
var email = model.email.toLowerCase();
|
var email = model.email.toLowerCase();
|
||||||
var key = cryptoService.makeKey(model.masterPassword, email);
|
|
||||||
|
|
||||||
var request = {
|
$scope.submitPromise = cryptoService.makeKeyAndHash(model.email, model.masterPassword).then(function (result) {
|
||||||
email: email,
|
return apiService.twoFactor.recover({
|
||||||
masterPasswordHash: cryptoService.hashPassword(model.masterPassword, key),
|
email: email,
|
||||||
recoveryCode: model.code.replace(/\s/g, '').toLowerCase()
|
masterPasswordHash: result.hash,
|
||||||
};
|
recoveryCode: model.code.replace(/\s/g, '').toLowerCase()
|
||||||
|
}).$promise;
|
||||||
$scope.submitPromise = apiService.twoFactor.recover(request, function () {
|
}).then(function () {
|
||||||
$analytics.eventTrack('Recovered 2FA');
|
$analytics.eventTrack('Recovered 2FA');
|
||||||
$scope.success = true;
|
$scope.success = true;
|
||||||
}).$promise;
|
});
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -51,14 +51,17 @@ angular
|
|||||||
}
|
}
|
||||||
|
|
||||||
var email = $scope.model.email.toLowerCase();
|
var email = $scope.model.email.toLowerCase();
|
||||||
var key = cryptoService.makeKey($scope.model.masterPassword, email);
|
var makeResult, encKey;
|
||||||
var encKey = cryptoService.makeEncKey(key);
|
|
||||||
|
|
||||||
$scope.registerPromise = cryptoService.makeKeyPair(encKey.encKey).then(function (result) {
|
$scope.registerPromise = cryptoService.makeKeyAndHash(email, $scope.model.masterPassword).then(function (result) {
|
||||||
|
makeResult = result;
|
||||||
|
encKey = cryptoService.makeEncKey(result.key);
|
||||||
|
return cryptoService.makeKeyPair(encKey.encKey);
|
||||||
|
}).then(function (result) {
|
||||||
var request = {
|
var request = {
|
||||||
name: $scope.model.name,
|
name: $scope.model.name,
|
||||||
email: email,
|
email: email,
|
||||||
masterPasswordHash: cryptoService.hashPassword($scope.model.masterPassword, key),
|
masterPasswordHash: makeResult.hash,
|
||||||
masterPasswordHint: $scope.model.masterPasswordHint,
|
masterPasswordHint: $scope.model.masterPasswordHint,
|
||||||
key: encKey.encKeyEnc,
|
key: encKey.encKeyEnc,
|
||||||
keys: {
|
keys: {
|
||||||
|
|||||||
@@ -55,11 +55,14 @@ angular
|
|||||||
|
|
||||||
$httpProvider.defaults.headers.post['Content-Type'] = 'text/plain; charset=utf-8';
|
$httpProvider.defaults.headers.post['Content-Type'] = 'text/plain; charset=utf-8';
|
||||||
|
|
||||||
if (!$httpProvider.defaults.headers.get) {
|
// stop IE from caching get requests
|
||||||
$httpProvider.defaults.headers.get = {};
|
if (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > 0) {
|
||||||
|
if (!$httpProvider.defaults.headers.get) {
|
||||||
|
$httpProvider.defaults.headers.get = {};
|
||||||
|
}
|
||||||
|
$httpProvider.defaults.headers.get['Cache-Control'] = 'no-cache';
|
||||||
|
$httpProvider.defaults.headers.get['Pragma'] = 'no-cache';
|
||||||
}
|
}
|
||||||
$httpProvider.defaults.headers.get['Cache-Control'] = 'no-cache';
|
|
||||||
$httpProvider.defaults.headers.get['Pragma'] = 'no-cache';
|
|
||||||
|
|
||||||
$httpProvider.interceptors.push('apiInterceptor');
|
$httpProvider.interceptors.push('apiInterceptor');
|
||||||
$httpProvider.interceptors.push('jwtInterceptor');
|
$httpProvider.interceptors.push('jwtInterceptor');
|
||||||
|
|||||||
@@ -13,10 +13,11 @@ angular
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = cryptoService.makeKey(value, profile.email);
|
return cryptoService.makeKey(value, profile.email).then(function (result) {
|
||||||
var valid = key.keyB64 === cryptoService.getKey().keyB64;
|
var valid = result.keyB64 === cryptoService.getKey().keyB64;
|
||||||
ngModel.$setValidity('masterPassword', valid);
|
ngModel.$setValidity('masterPassword', valid);
|
||||||
return valid ? value : undefined;
|
return valid ? value : undefined;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// For model -> DOM validation
|
// For model -> DOM validation
|
||||||
@@ -25,11 +26,11 @@ angular
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = cryptoService.makeKey(value, profile.email);
|
return cryptoService.makeKey(value, profile.email).then(function (result) {
|
||||||
var valid = key.keyB64 === cryptoService.getKey().keyB64;
|
var valid = result.keyB64 === cryptoService.getKey().keyB64;
|
||||||
|
ngModel.$setValidity('masterPassword', valid);
|
||||||
ngModel.$setValidity('masterPassword', valid);
|
return value;
|
||||||
return value;
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,19 +5,18 @@
|
|||||||
authService, toastr, $analytics) {
|
authService, toastr, $analytics) {
|
||||||
$analytics.eventTrack('organizationDeleteController', { category: 'Modal' });
|
$analytics.eventTrack('organizationDeleteController', { category: 'Modal' });
|
||||||
$scope.submit = function () {
|
$scope.submit = function () {
|
||||||
var request = {
|
$scope.submitPromise = cryptoService.hashPassword($scope.masterPassword).then(function (hash) {
|
||||||
masterPasswordHash: cryptoService.hashPassword($scope.masterPassword)
|
return apiService.organizations.del({ id: $state.params.orgId }, {
|
||||||
};
|
masterPasswordHash: hash
|
||||||
|
}).$promise;
|
||||||
$scope.submitPromise = apiService.organizations.del({ id: $state.params.orgId }, request, function () {
|
}).then(function () {
|
||||||
$uibModalInstance.dismiss('cancel');
|
$uibModalInstance.dismiss('cancel');
|
||||||
authService.removeProfileOrganization($state.params.orgId);
|
authService.removeProfileOrganization($state.params.orgId);
|
||||||
$analytics.eventTrack('Deleted Organization');
|
$analytics.eventTrack('Deleted Organization');
|
||||||
$state.go('backend.user.vault').then(function () {
|
return $state.go('backend.user.vault');
|
||||||
toastr.success('This organization and all associated data has been deleted.',
|
}).then(function () {
|
||||||
'Organization Deleted');
|
toastr.success('This organization and all associated data has been deleted.', 'Organization Deleted');
|
||||||
});
|
});
|
||||||
}).$promise;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.close = function () {
|
$scope.close = function () {
|
||||||
|
|||||||
@@ -7,48 +7,52 @@ angular
|
|||||||
|
|
||||||
_service.logIn = function (email, masterPassword, token, provider, remember) {
|
_service.logIn = function (email, masterPassword, token, provider, remember) {
|
||||||
email = email.toLowerCase();
|
email = email.toLowerCase();
|
||||||
var key = cryptoService.makeKey(masterPassword, email);
|
|
||||||
|
|
||||||
var request = {
|
|
||||||
username: email,
|
|
||||||
password: cryptoService.hashPassword(masterPassword, key),
|
|
||||||
grant_type: 'password',
|
|
||||||
scope: 'api offline_access',
|
|
||||||
client_id: 'web'
|
|
||||||
};
|
|
||||||
|
|
||||||
if (token && typeof (provider) !== 'undefined' && provider !== null) {
|
|
||||||
remember = remember || remember !== false;
|
|
||||||
|
|
||||||
request.twoFactorToken = token;
|
|
||||||
request.twoFactorProvider = provider;
|
|
||||||
request.twoFactorRemember = remember ? '1' : '0';
|
|
||||||
}
|
|
||||||
else if (tokenService.getTwoFactorToken(email)) {
|
|
||||||
request.twoFactorToken = tokenService.getTwoFactorToken(email);
|
|
||||||
request.twoFactorProvider = constants.twoFactorProvider.remember;
|
|
||||||
request.twoFactorRemember = '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: device information one day?
|
|
||||||
|
|
||||||
var deferred = $q.defer();
|
var deferred = $q.defer();
|
||||||
|
|
||||||
apiService.identity.token(request).$promise.then(function (response) {
|
var makeResult;
|
||||||
|
cryptoService.makeKeyAndHash(email, masterPassword).then(function (result) {
|
||||||
|
makeResult = result;
|
||||||
|
|
||||||
|
var request = {
|
||||||
|
username: email,
|
||||||
|
password: result.hash,
|
||||||
|
grant_type: 'password',
|
||||||
|
scope: 'api offline_access',
|
||||||
|
client_id: 'web'
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: device information one day?
|
||||||
|
|
||||||
|
if (token && typeof (provider) !== 'undefined' && provider !== null) {
|
||||||
|
remember = remember || remember !== false;
|
||||||
|
|
||||||
|
request.twoFactorToken = token;
|
||||||
|
request.twoFactorProvider = provider;
|
||||||
|
request.twoFactorRemember = remember ? '1' : '0';
|
||||||
|
}
|
||||||
|
else if (tokenService.getTwoFactorToken(email)) {
|
||||||
|
request.twoFactorToken = tokenService.getTwoFactorToken(email);
|
||||||
|
request.twoFactorProvider = constants.twoFactorProvider.remember;
|
||||||
|
request.twoFactorRemember = '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiService.identity.token(request).$promise;
|
||||||
|
}).then(function (response) {
|
||||||
if (!response || !response.access_token) {
|
if (!response || !response.access_token) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenService.setToken(response.access_token);
|
tokenService.setToken(response.access_token);
|
||||||
tokenService.setRefreshToken(response.refresh_token);
|
tokenService.setRefreshToken(response.refresh_token);
|
||||||
cryptoService.setKey(key);
|
cryptoService.setKey(makeResult.key);
|
||||||
|
|
||||||
if (response.TwoFactorToken) {
|
if (response.TwoFactorToken) {
|
||||||
tokenService.setTwoFactorToken(response.TwoFactorToken, email);
|
tokenService.setTwoFactorToken(response.TwoFactorToken, email);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.Key) {
|
if (response.Key) {
|
||||||
cryptoService.setEncKey(response.Key, key);
|
cryptoService.setEncKey(response.Key, makeResult.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.PrivateKey) {
|
if (response.PrivateKey) {
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
angular
|
angular
|
||||||
.module('bit.services')
|
.module('bit.services')
|
||||||
|
|
||||||
.factory('cryptoService', function ($sessionStorage, constants, $q) {
|
.factory('cryptoService', function ($sessionStorage, constants, $q, $window) {
|
||||||
var _service = {},
|
var _service = {},
|
||||||
_key,
|
_key,
|
||||||
_encKey,
|
_encKey,
|
||||||
_legacyEtmKey,
|
_legacyEtmKey,
|
||||||
_orgKeys,
|
_orgKeys,
|
||||||
_privateKey,
|
_privateKey,
|
||||||
_publicKey;
|
_publicKey,
|
||||||
|
_crypto = $window.crypto,
|
||||||
|
_subtle = $window.crypto.subtle;
|
||||||
|
|
||||||
_service.setKey = function (key) {
|
_service.setKey = function (key) {
|
||||||
_key = key;
|
_key = key;
|
||||||
@@ -233,9 +235,18 @@ angular
|
|||||||
};
|
};
|
||||||
|
|
||||||
_service.makeKey = function (password, salt) {
|
_service.makeKey = function (password, salt) {
|
||||||
var keyBytes = forge.pbkdf2(forge.util.encodeUtf8(password), forge.util.encodeUtf8(salt),
|
if (!$window.cryptoShimmed) {
|
||||||
5000, 256 / 8, 'sha256');
|
return pbkdf2WC(password, salt, 5000, 256).then(function (keyBuf) {
|
||||||
return new SymmetricCryptoKey(keyBytes);
|
return new SymmetricCryptoKey(bufToB64(keyBuf), true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var deferred = $q.defer();
|
||||||
|
var keyBytes = forge.pbkdf2(forge.util.encodeUtf8(password), forge.util.encodeUtf8(salt),
|
||||||
|
5000, 256 / 8, 'sha256');
|
||||||
|
deferred.resolve(new SymmetricCryptoKey(keyBytes));
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_service.makeEncKey = function (key) {
|
_service.makeEncKey = function (key) {
|
||||||
@@ -290,8 +301,46 @@ angular
|
|||||||
throw 'Invalid parameters.';
|
throw 'Invalid parameters.';
|
||||||
}
|
}
|
||||||
|
|
||||||
var hashBits = forge.pbkdf2(key.key, forge.util.encodeUtf8(password), 1, 256 / 8, 'sha256');
|
if (!$window.cryptoShimmed) {
|
||||||
return forge.util.encode64(hashBits);
|
var keyBuf = key.getBuffers();
|
||||||
|
return pbkdf2WC(new Uint8Array(keyBuf.key), password, 1, 256).then(function (hashBuf) {
|
||||||
|
return bufToB64(hashBuf);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var deferred = $q.defer();
|
||||||
|
var hashBits = forge.pbkdf2(key.key, forge.util.encodeUtf8(password), 1, 256 / 8, 'sha256');
|
||||||
|
deferred.resolve(forge.util.encode64(hashBits));
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function pbkdf2WC(password, salt, iterations, size) {
|
||||||
|
password = typeof (password) === 'string' ? utf8ToArray(password) : password;
|
||||||
|
salt = typeof (salt) === 'string' ? utf8ToArray(salt) : salt;
|
||||||
|
|
||||||
|
return _subtle.importKey('raw', password.buffer, { name: 'PBKDF2' }, false, ['deriveKey', 'deriveBits'])
|
||||||
|
.then(function (importedKey) {
|
||||||
|
return _subtle.deriveKey(
|
||||||
|
{ name: 'PBKDF2', salt: salt.buffer, iterations: iterations, hash: { name: 'SHA-256' } },
|
||||||
|
importedKey, { name: 'AES-CBC', length: size }, true, ['encrypt', 'decrypt']);
|
||||||
|
}).then(function (derivedKey) {
|
||||||
|
return _subtle.exportKey('raw', derivedKey);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_service.makeKeyAndHash = function (email, password) {
|
||||||
|
email = email.toLowerCase();
|
||||||
|
var key;
|
||||||
|
return _service.makeKey(password, email).then(function (theKey) {
|
||||||
|
key = theKey;
|
||||||
|
return _service.hashPassword(password, theKey);
|
||||||
|
}).then(function (theHash) {
|
||||||
|
return {
|
||||||
|
key: key,
|
||||||
|
hash: theHash
|
||||||
|
};
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
_service.encrypt = function (plainValue, key, plainValueEncoding) {
|
_service.encrypt = function (plainValue, key, plainValueEncoding) {
|
||||||
@@ -375,11 +424,11 @@ angular
|
|||||||
};
|
};
|
||||||
|
|
||||||
var keyBuf = key.getBuffers();
|
var keyBuf = key.getBuffers();
|
||||||
window.crypto.getRandomValues(obj.iv);
|
_crypto.getRandomValues(obj.iv);
|
||||||
|
|
||||||
return window.crypto.subtle.importKey('raw', keyBuf.encKey, { name: 'AES-CBC' }, false, ['encrypt'])
|
return _subtle.importKey('raw', keyBuf.encKey, { name: 'AES-CBC' }, false, ['encrypt'])
|
||||||
.then(function (encKey) {
|
.then(function (encKey) {
|
||||||
return window.crypto.subtle.encrypt({ name: 'AES-CBC', iv: obj.iv }, encKey, plainValue);
|
return _subtle.encrypt({ name: 'AES-CBC', iv: obj.iv }, encKey, plainValue);
|
||||||
}).then(function (encValue) {
|
}).then(function (encValue) {
|
||||||
obj.ct = new Uint8Array(encValue);
|
obj.ct = new Uint8Array(encValue);
|
||||||
if (!keyBuf.macKey) {
|
if (!keyBuf.macKey) {
|
||||||
@@ -554,7 +603,7 @@ angular
|
|||||||
var keyBuf = key.getBuffers(),
|
var keyBuf = key.getBuffers(),
|
||||||
encKey = null;
|
encKey = null;
|
||||||
|
|
||||||
return window.crypto.subtle.importKey('raw', keyBuf.encKey, { name: 'AES-CBC' }, false, ['decrypt'])
|
return _subtle.importKey('raw', keyBuf.encKey, { name: 'AES-CBC' }, false, ['decrypt'])
|
||||||
.then(function (theEncKey) {
|
.then(function (theEncKey) {
|
||||||
encKey = theEncKey;
|
encKey = theEncKey;
|
||||||
|
|
||||||
@@ -576,7 +625,7 @@ angular
|
|||||||
console.error('MAC failed.');
|
console.error('MAC failed.');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return window.crypto.subtle.decrypt({ name: 'AES-CBC', iv: ivBuf }, encKey, ctBuf);
|
return _subtle.decrypt({ name: 'AES-CBC', iv: ivBuf }, encKey, ctBuf);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,9 +712,9 @@ angular
|
|||||||
}
|
}
|
||||||
|
|
||||||
function computeMacWC(dataBuf, macKeyBuf) {
|
function computeMacWC(dataBuf, macKeyBuf) {
|
||||||
return window.crypto.subtle.importKey('raw', macKeyBuf, { name: 'HMAC', hash: { name: 'SHA-256' } }, false, ['sign'])
|
return _subtle.importKey('raw', macKeyBuf, { name: 'HMAC', hash: { name: 'SHA-256' } }, false, ['sign'])
|
||||||
.then(function (key) {
|
.then(function (key) {
|
||||||
return window.crypto.subtle.sign({ name: 'HMAC', hash: { name: 'SHA-256' } }, key, dataBuf);
|
return _subtle.sign({ name: 'HMAC', hash: { name: 'SHA-256' } }, key, dataBuf);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -787,7 +836,7 @@ angular
|
|||||||
};
|
};
|
||||||
|
|
||||||
function b64ToArray(b64Str) {
|
function b64ToArray(b64Str) {
|
||||||
var binaryString = window.atob(b64Str);
|
var binaryString = $window.atob(b64Str);
|
||||||
var arr = new Uint8Array(binaryString.length);
|
var arr = new Uint8Array(binaryString.length);
|
||||||
for (var i = 0; i < binaryString.length; i++) {
|
for (var i = 0; i < binaryString.length; i++) {
|
||||||
arr[i] = binaryString.charCodeAt(i);
|
arr[i] = binaryString.charCodeAt(i);
|
||||||
@@ -795,6 +844,24 @@ angular
|
|||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function bufToB64(buf) {
|
||||||
|
var binary = '';
|
||||||
|
var bytes = new Uint8Array(buf);
|
||||||
|
for (var i = 0; i < bytes.byteLength; i++) {
|
||||||
|
binary += String.fromCharCode(bytes[i]);
|
||||||
|
}
|
||||||
|
return $window.btoa(binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
function utf8ToArray(str) {
|
||||||
|
var utf8Str = unescape(encodeURIComponent(str));
|
||||||
|
var arr = new Uint8Array(utf8Str.length);
|
||||||
|
for (var i = 0; i < utf8Str.length; i++) {
|
||||||
|
arr[i] = utf8Str.charCodeAt(i);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
function slice(arr, begin, end) {
|
function slice(arr, begin, end) {
|
||||||
if (arr.slice) {
|
if (arr.slice) {
|
||||||
return arr.slice(begin, end);
|
return arr.slice(begin, end);
|
||||||
|
|||||||
@@ -11,22 +11,25 @@
|
|||||||
|
|
||||||
$scope.token = function (model) {
|
$scope.token = function (model) {
|
||||||
_masterPassword = model.masterPassword;
|
_masterPassword = model.masterPassword;
|
||||||
_masterPasswordHash = cryptoService.hashPassword(_masterPassword);
|
|
||||||
_newEmail = model.newEmail.toLowerCase();
|
_newEmail = model.newEmail.toLowerCase();
|
||||||
|
|
||||||
var encKey = cryptoService.getEncKey();
|
cryptoService.hashPassword(_masterPassword).then(function (hash) {
|
||||||
if (encKey) {
|
_masterPasswordHash = hash;
|
||||||
$scope.tokenPromise = requestToken(model);
|
|
||||||
}
|
var encKey = cryptoService.getEncKey();
|
||||||
else {
|
if (encKey) {
|
||||||
// User is not using an enc key, let's make them one
|
$scope.tokenPromise = requestToken();
|
||||||
$scope.tokenPromise = cipherService.updateKey(_masterPasswordHash, function () {
|
}
|
||||||
return requestToken(model);
|
else {
|
||||||
}, processError);
|
// User is not using an enc key, let's make them one
|
||||||
}
|
$scope.tokenPromise = cipherService.updateKey(_masterPasswordHash, function () {
|
||||||
|
return requestToken();
|
||||||
|
}, processError);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function requestToken(model) {
|
function requestToken() {
|
||||||
var request = {
|
var request = {
|
||||||
newEmail: _newEmail,
|
newEmail: _newEmail,
|
||||||
masterPasswordHash: _masterPasswordHash
|
masterPasswordHash: _masterPasswordHash
|
||||||
@@ -40,33 +43,31 @@
|
|||||||
$scope.confirm = function (model) {
|
$scope.confirm = function (model) {
|
||||||
$scope.processing = true;
|
$scope.processing = true;
|
||||||
|
|
||||||
var newKey = cryptoService.makeKey(_masterPassword, _newEmail);
|
$scope.confirmPromise = cryptoService.makeKeyAndHash(_newEmail, _masterPassword).then(function (result) {
|
||||||
var encKey = cryptoService.getEncKey();
|
var encKey = cryptoService.getEncKey();
|
||||||
var newEncKey = cryptoService.encrypt(encKey.key, newKey, 'raw');
|
var newEncKey = cryptoService.encrypt(encKey.key, result.key, 'raw');
|
||||||
|
var request = {
|
||||||
|
token: model.token,
|
||||||
|
newEmail: _newEmail,
|
||||||
|
masterPasswordHash: _masterPasswordHash,
|
||||||
|
newMasterPasswordHash: result.hash,
|
||||||
|
key: newEncKey
|
||||||
|
};
|
||||||
|
|
||||||
var request = {
|
return apiService.accounts.email(request).$promise;
|
||||||
token: model.token,
|
}).then(function () {
|
||||||
newEmail: _newEmail,
|
|
||||||
masterPasswordHash: _masterPasswordHash,
|
|
||||||
newMasterPasswordHash: cryptoService.hashPassword(_masterPassword, newKey),
|
|
||||||
key: newEncKey
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.confirmPromise = apiService.accounts.email(request).$promise.then(function () {
|
|
||||||
$uibModalInstance.dismiss('cancel');
|
$uibModalInstance.dismiss('cancel');
|
||||||
authService.logOut();
|
authService.logOut();
|
||||||
$analytics.eventTrack('Changed Email');
|
$analytics.eventTrack('Changed Email');
|
||||||
return $state.go('frontend.login.info');
|
return $state.go('frontend.login.info');
|
||||||
}, processError).then(function () {
|
}).then(function () {
|
||||||
toastr.success('Please log back in.', 'Email Changed');
|
toastr.success('Please log back in.', 'Email Changed');
|
||||||
}, processError);
|
}, function () {
|
||||||
|
$uibModalInstance.dismiss('cancel');
|
||||||
|
toastr.error('Something went wrong. Try again.', 'Oh No!');
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function processError() {
|
|
||||||
$uibModalInstance.dismiss('cancel');
|
|
||||||
toastr.error('Something went wrong. Try again.', 'Oh No!');
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.close = function () {
|
$scope.close = function () {
|
||||||
$uibModalInstance.dismiss('cancel');
|
$uibModalInstance.dismiss('cancel');
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -31,39 +31,43 @@
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// User is not using an enc key, let's make them one
|
// User is not using an enc key, let's make them one
|
||||||
var mpHash = cryptoService.hashPassword(model.masterPassword);
|
$scope.savePromise = cryptoService.hashPassword(model.masterPassword).then(function (hash) {
|
||||||
$scope.savePromise = cipherService.updateKey(mpHash, function () {
|
return cipherService.updateKey(hash);
|
||||||
|
}, processError).then(function () {
|
||||||
return changePassword(model);
|
return changePassword(model);
|
||||||
}, processError);
|
}, processError);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function changePassword(model) {
|
function changePassword(model) {
|
||||||
|
var makeResult;
|
||||||
return authService.getUserProfile().then(function (profile) {
|
return authService.getUserProfile().then(function (profile) {
|
||||||
var newKey = cryptoService.makeKey(model.newMasterPassword, profile.email.toLowerCase());
|
return cryptoService.makeKeyAndHash(profile.email, model.newMasterPassword);
|
||||||
|
}).then(function (result) {
|
||||||
|
makeResult = result;
|
||||||
|
return cryptoService.hashPassword(model.masterPassword);
|
||||||
|
}).then(function (hash) {
|
||||||
var encKey = cryptoService.getEncKey();
|
var encKey = cryptoService.getEncKey();
|
||||||
var newEncKey = cryptoService.encrypt(encKey.key, newKey, 'raw');
|
var newEncKey = cryptoService.encrypt(encKey.key, makeResult.key, 'raw');
|
||||||
|
|
||||||
var request = {
|
var request = {
|
||||||
masterPasswordHash: cryptoService.hashPassword(model.masterPassword),
|
masterPasswordHash: hash,
|
||||||
newMasterPasswordHash: cryptoService.hashPassword(model.newMasterPassword, newKey),
|
newMasterPasswordHash: makeResult.hash,
|
||||||
key: newEncKey
|
key: newEncKey
|
||||||
};
|
};
|
||||||
|
|
||||||
return apiService.accounts.putPassword(request).$promise;
|
return apiService.accounts.putPassword(request).$promise;
|
||||||
}, processError).then(function () {
|
}).then(function () {
|
||||||
$uibModalInstance.dismiss('cancel');
|
$uibModalInstance.dismiss('cancel');
|
||||||
authService.logOut();
|
authService.logOut();
|
||||||
$analytics.eventTrack('Changed Password');
|
$analytics.eventTrack('Changed Password');
|
||||||
return $state.go('frontend.login.info');
|
return $state.go('frontend.login.info');
|
||||||
}, processError).then(function () {
|
}).then(function () {
|
||||||
toastr.success('Please log back in.', 'Master Password Changed');
|
toastr.success('Please log back in.', 'Master Password Changed');
|
||||||
}, processError);
|
}, function () {
|
||||||
}
|
$uibModalInstance.dismiss('cancel');
|
||||||
|
toastr.error('Something went wrong.', 'Oh No!');
|
||||||
function processError() {
|
});
|
||||||
$uibModalInstance.dismiss('cancel');
|
|
||||||
toastr.error('Something went wrong.', 'Oh No!');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.close = function () {
|
$scope.close = function () {
|
||||||
|
|||||||
@@ -5,18 +5,18 @@
|
|||||||
authService, toastr, $analytics) {
|
authService, toastr, $analytics) {
|
||||||
$analytics.eventTrack('settingsDeleteController', { category: 'Modal' });
|
$analytics.eventTrack('settingsDeleteController', { category: 'Modal' });
|
||||||
$scope.submit = function (model) {
|
$scope.submit = function (model) {
|
||||||
var request = {
|
$scope.submitPromise = cryptoService.hashPassword(model.masterPassword).then(function (hash) {
|
||||||
masterPasswordHash: cryptoService.hashPassword(model.masterPassword)
|
return apiService.accounts.postDelete({
|
||||||
};
|
masterPasswordHash: hash
|
||||||
|
}).$promise;
|
||||||
$scope.submitPromise = apiService.accounts.postDelete(request, function () {
|
}).then(function () {
|
||||||
$uibModalInstance.dismiss('cancel');
|
$uibModalInstance.dismiss('cancel');
|
||||||
authService.logOut();
|
authService.logOut();
|
||||||
$analytics.eventTrack('Deleted Account');
|
$analytics.eventTrack('Deleted Account');
|
||||||
$state.go('frontend.login.info').then(function () {
|
return $state.go('frontend.login.info');
|
||||||
toastr.success('Your account has been closed and all associated data has been deleted.', 'Account Deleted');
|
}).then(function () {
|
||||||
});
|
toastr.success('Your account has been closed and all associated data has been deleted.', 'Account Deleted');
|
||||||
}).$promise;
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.close = function () {
|
$scope.close = function () {
|
||||||
|
|||||||
@@ -5,22 +5,25 @@
|
|||||||
authService, tokenService, toastr, $analytics) {
|
authService, tokenService, toastr, $analytics) {
|
||||||
$analytics.eventTrack('settingsSessionsController', { category: 'Modal' });
|
$analytics.eventTrack('settingsSessionsController', { category: 'Modal' });
|
||||||
$scope.submit = function (model) {
|
$scope.submit = function (model) {
|
||||||
var request = {
|
var hash, profile;
|
||||||
masterPasswordHash: cryptoService.hashPassword(model.masterPassword)
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.submitPromise =
|
$scope.submitPromise = cryptoService.hashPassword(model.masterPassword).then(function (theHash) {
|
||||||
authService.getUserProfile().then(function (profile) {
|
hash = theHash;
|
||||||
return apiService.accounts.putSecurityStamp(request, function () {
|
return authService.getUserProfile();
|
||||||
$uibModalInstance.dismiss('cancel');
|
}).then(function (theProfile) {
|
||||||
authService.logOut();
|
profile = theProfile;
|
||||||
tokenService.clearTwoFactorToken(profile.email);
|
return apiService.accounts.putSecurityStamp({
|
||||||
$analytics.eventTrack('Deauthorized Sessions');
|
masterPasswordHash: hash
|
||||||
$state.go('frontend.login.info').then(function () {
|
}).$promise;
|
||||||
toastr.success('Please log back in.', 'All Sessions Deauthorized');
|
}).then(function () {
|
||||||
});
|
$uibModalInstance.dismiss('cancel');
|
||||||
}).$promise;
|
authService.logOut();
|
||||||
});
|
tokenService.clearTwoFactorToken(profile.email);
|
||||||
|
$analytics.eventTrack('Deauthorized Sessions');
|
||||||
|
return $state.go('frontend.login.info');
|
||||||
|
}).then(function () {
|
||||||
|
toastr.success('Please log back in.', 'All Sessions Deauthorized');
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.close = function () {
|
$scope.close = function () {
|
||||||
|
|||||||
@@ -10,12 +10,13 @@
|
|||||||
_key = null;
|
_key = null;
|
||||||
|
|
||||||
$scope.auth = function (model) {
|
$scope.auth = function (model) {
|
||||||
_masterPasswordHash = cryptoService.hashPassword(model.masterPassword);
|
|
||||||
|
|
||||||
var response = null;
|
var response = null;
|
||||||
$scope.authPromise = apiService.twoFactor.getAuthenticator({}, {
|
$scope.authPromise = cryptoService.hashPassword(model.masterPassword).then(function (hash) {
|
||||||
masterPasswordHash: _masterPasswordHash
|
_masterPasswordHash = hash;
|
||||||
}).$promise.then(function (apiResponse) {
|
return apiService.twoFactor.getAuthenticator({}, {
|
||||||
|
masterPasswordHash: _masterPasswordHash
|
||||||
|
}).$promise;
|
||||||
|
}).then(function (apiResponse) {
|
||||||
response = apiResponse;
|
response = apiResponse;
|
||||||
return authService.getUserProfile();
|
return authService.getUserProfile();
|
||||||
}).then(function (profile) {
|
}).then(function (profile) {
|
||||||
|
|||||||
@@ -14,10 +14,12 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
$scope.auth = function (model) {
|
$scope.auth = function (model) {
|
||||||
_masterPasswordHash = cryptoService.hashPassword(model.masterPassword);
|
$scope.authPromise = cryptoService.hashPassword(model.masterPassword).then(function (hash) {
|
||||||
$scope.authPromise = apiService.twoFactor.getDuo({}, {
|
_masterPasswordHash = hash;
|
||||||
masterPasswordHash: _masterPasswordHash
|
return apiService.twoFactor.getDuo({}, {
|
||||||
}).$promise.then(function (apiResponse) {
|
masterPasswordHash: _masterPasswordHash
|
||||||
|
}).$promise;
|
||||||
|
}).then(function (apiResponse) {
|
||||||
processResult(apiResponse);
|
processResult(apiResponse);
|
||||||
$scope.authed = true;
|
$scope.authed = true;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,12 +13,13 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
$scope.auth = function (model) {
|
$scope.auth = function (model) {
|
||||||
_masterPasswordHash = cryptoService.hashPassword(model.masterPassword);
|
|
||||||
|
|
||||||
var response = null;
|
var response = null;
|
||||||
$scope.authPromise = apiService.twoFactor.getEmail({}, {
|
$scope.authPromise = cryptoService.hashPassword(model.masterPassword).then(function (hash) {
|
||||||
masterPasswordHash: _masterPasswordHash
|
_masterPasswordHash = hash;
|
||||||
}).$promise.then(function (apiResponse) {
|
return apiService.twoFactor.getEmail({}, {
|
||||||
|
masterPasswordHash: _masterPasswordHash
|
||||||
|
}).$promise;
|
||||||
|
}).then(function (apiResponse) {
|
||||||
response = apiResponse;
|
response = apiResponse;
|
||||||
return authService.getUserProfile();
|
return authService.getUserProfile();
|
||||||
}).then(function (profile) {
|
}).then(function (profile) {
|
||||||
|
|||||||
@@ -7,11 +7,11 @@
|
|||||||
$scope.code = null;
|
$scope.code = null;
|
||||||
|
|
||||||
$scope.auth = function (model) {
|
$scope.auth = function (model) {
|
||||||
var masterPasswordHash = cryptoService.hashPassword(model.masterPassword);
|
$scope.authPromise = cryptoService.hashPassword(model.masterPassword).then(function (hash) {
|
||||||
|
return apiService.twoFactor.getRecover({}, {
|
||||||
$scope.authPromise = apiService.twoFactor.getRecover({}, {
|
masterPasswordHash: hash
|
||||||
masterPasswordHash: masterPasswordHash
|
}).$promise;
|
||||||
}).$promise.then(function (apiResponse) {
|
}).then(function (apiResponse) {
|
||||||
$scope.code = formatString(apiResponse.Code);
|
$scope.code = formatString(apiResponse.Code);
|
||||||
$scope.authed = true;
|
$scope.authed = true;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,11 +12,12 @@
|
|||||||
$scope.deviceError = false;
|
$scope.deviceError = false;
|
||||||
|
|
||||||
$scope.auth = function (model) {
|
$scope.auth = function (model) {
|
||||||
_masterPasswordHash = cryptoService.hashPassword(model.masterPassword);
|
$scope.authPromise = cryptoService.hashPassword(model.masterPassword).then(function (hash) {
|
||||||
|
_masterPasswordHash = hash;
|
||||||
$scope.authPromise = apiService.twoFactor.getU2f({}, {
|
return apiService.twoFactor.getU2f({}, {
|
||||||
masterPasswordHash: _masterPasswordHash
|
masterPasswordHash: _masterPasswordHash
|
||||||
}).$promise.then(function (response) {
|
}).$promise;
|
||||||
|
}).then(function (response) {
|
||||||
$scope.enabled = response.Enabled;
|
$scope.enabled = response.Enabled;
|
||||||
$scope.challenge = response.Challenge;
|
$scope.challenge = response.Challenge;
|
||||||
$scope.authed = true;
|
$scope.authed = true;
|
||||||
|
|||||||
@@ -8,12 +8,13 @@
|
|||||||
_masterPasswordHash;
|
_masterPasswordHash;
|
||||||
|
|
||||||
$scope.auth = function (model) {
|
$scope.auth = function (model) {
|
||||||
_masterPasswordHash = cryptoService.hashPassword(model.masterPassword);
|
|
||||||
|
|
||||||
var response = null;
|
var response = null;
|
||||||
$scope.authPromise = apiService.twoFactor.getYubi({}, {
|
$scope.authPromise = cryptoService.hashPassword(model.masterPassword).then(function (hash) {
|
||||||
masterPasswordHash: _masterPasswordHash
|
_masterPasswordHash = hash;
|
||||||
}).$promise.then(function (apiResponse) {
|
return apiService.twoFactor.getYubi({}, {
|
||||||
|
masterPasswordHash: _masterPasswordHash
|
||||||
|
}).$promise;
|
||||||
|
}).then(function (apiResponse) {
|
||||||
response = apiResponse;
|
response = apiResponse;
|
||||||
return authService.getUserProfile();
|
return authService.getUserProfile();
|
||||||
}).then(function (profile) {
|
}).then(function (profile) {
|
||||||
|
|||||||
@@ -14,8 +14,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$scope.processing = true;
|
$scope.processing = true;
|
||||||
var mpHash = cryptoService.hashPassword($scope.masterPassword);
|
$scope.savePromise = cryptoService.hashPassword($scope.masterPassword).then(function (hash) {
|
||||||
$scope.savePromise = cipherService.updateKey(mpHash, function () {
|
return cipherService.updateKey(hash);
|
||||||
|
}).then(function () {
|
||||||
$uibModalInstance.dismiss('cancel');
|
$uibModalInstance.dismiss('cancel');
|
||||||
authService.logOut();
|
authService.logOut();
|
||||||
$analytics.eventTrack('Key Updated');
|
$analytics.eventTrack('Key Updated');
|
||||||
@@ -23,14 +24,12 @@
|
|||||||
}).then(function () {
|
}).then(function () {
|
||||||
toastr.success('Please log back in. If you are using other bitwarden applications, ' +
|
toastr.success('Please log back in. If you are using other bitwarden applications, ' +
|
||||||
'log out and back in to those as well.', 'Key Updated', { timeOut: 10000 });
|
'log out and back in to those as well.', 'Key Updated', { timeOut: 10000 });
|
||||||
}, processError);
|
}, function () {
|
||||||
|
$uibModalInstance.dismiss('cancel');
|
||||||
|
toastr.error('Something went wrong.', 'Oh No!');
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function processError() {
|
|
||||||
$uibModalInstance.dismiss('cancel');
|
|
||||||
toastr.error('Something went wrong.', 'Oh No!');
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.close = function () {
|
$scope.close = function () {
|
||||||
$uibModalInstance.dismiss('cancel');
|
$uibModalInstance.dismiss('cancel');
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,8 +18,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group" show-errors>
|
<div class="form-group" show-errors>
|
||||||
<label for="masterPassword">Master Password</label>
|
<label for="masterPassword">Master Password</label>
|
||||||
<input type="password" id="masterPassword" name="MasterPasswordHash" ng-model="model.masterPassword" class="form-control"
|
<input type="password" id="masterPassword" name="MasterPasswordHash" ng-model="model.masterPassword"
|
||||||
master-password required api-field ng-model-options="{ 'updateOn': 'blur'}" />
|
class="form-control" master-password required api-field ng-model-options="{ 'updateOn': 'blur'}" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
|||||||
@@ -25,6 +25,9 @@
|
|||||||
isWebkit = !_crypto.subtle && !!_crypto.webkitSubtle;
|
isWebkit = !_crypto.subtle && !!_crypto.webkitSubtle;
|
||||||
if (!isIE && !isWebkit) return;
|
if (!isIE && !isWebkit) return;
|
||||||
|
|
||||||
|
// Added
|
||||||
|
global.cryptoShimmed = true;
|
||||||
|
|
||||||
function s2a(s) {
|
function s2a(s) {
|
||||||
return btoa(s).replace(/\=+$/, '').replace(/\+/g, '-').replace(/\//g, '_');
|
return btoa(s).replace(/\=+$/, '').replace(/\+/g, '-').replace(/\//g, '_');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user