1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-28 12:45:45 +01:00

move pbkdf2 to web crypto with shim fallback

This commit is contained in:
Kyle Spearrin 2017-07-08 23:41:02 -04:00
parent b62950fa2b
commit bc8892a237
21 changed files with 284 additions and 192 deletions

View File

@ -149,12 +149,12 @@ angular
return;
}
var key = cryptoService.makeKey(_masterPassword, _email);
var hash = cryptoService.hashPassword(_masterPassword, key);
apiService.twoFactor.sendEmailLogin({
email: _email,
masterPasswordHash: hash
}, function () {
return cryptoService.makeKeyAndHash(_email, _masterPassword).then(function (result) {
return apiService.twoFactor.sendEmailLogin({
email: _email,
masterPasswordHash: result.hash
}).$promise;
}).then(function () {
if (doToast) {
toastr.success('Verification email sent to ' + $scope.twoFactorEmail + '.');
}

View File

@ -6,17 +6,16 @@ angular
$scope.submit = function (model) {
var email = model.email.toLowerCase();
var key = cryptoService.makeKey(model.masterPassword, email);
var request = {
email: email,
masterPasswordHash: cryptoService.hashPassword(model.masterPassword, key),
recoveryCode: model.code.replace(/\s/g, '').toLowerCase()
};
$scope.submitPromise = apiService.twoFactor.recover(request, function () {
$scope.submitPromise = cryptoService.makeKeyAndHash(model.email, model.masterPassword).then(function (result) {
return apiService.twoFactor.recover({
email: email,
masterPasswordHash: result.hash,
recoveryCode: model.code.replace(/\s/g, '').toLowerCase()
}).$promise;
}).then(function () {
$analytics.eventTrack('Recovered 2FA');
$scope.success = true;
}).$promise;
});
};
});

View File

@ -51,14 +51,17 @@ angular
}
var email = $scope.model.email.toLowerCase();
var key = cryptoService.makeKey($scope.model.masterPassword, email);
var encKey = cryptoService.makeEncKey(key);
var makeResult, encKey;
$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 = {
name: $scope.model.name,
email: email,
masterPasswordHash: cryptoService.hashPassword($scope.model.masterPassword, key),
masterPasswordHash: makeResult.hash,
masterPasswordHint: $scope.model.masterPasswordHint,
key: encKey.encKeyEnc,
keys: {

View File

@ -55,11 +55,14 @@ angular
$httpProvider.defaults.headers.post['Content-Type'] = 'text/plain; charset=utf-8';
if (!$httpProvider.defaults.headers.get) {
$httpProvider.defaults.headers.get = {};
// stop IE from caching get requests
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('jwtInterceptor');

View File

@ -13,10 +13,11 @@ angular
return undefined;
}
var key = cryptoService.makeKey(value, profile.email);
var valid = key.keyB64 === cryptoService.getKey().keyB64;
ngModel.$setValidity('masterPassword', valid);
return valid ? value : undefined;
return cryptoService.makeKey(value, profile.email).then(function (result) {
var valid = result.keyB64 === cryptoService.getKey().keyB64;
ngModel.$setValidity('masterPassword', valid);
return valid ? value : undefined;
});
});
// For model -> DOM validation
@ -25,11 +26,11 @@ angular
return undefined;
}
var key = cryptoService.makeKey(value, profile.email);
var valid = key.keyB64 === cryptoService.getKey().keyB64;
ngModel.$setValidity('masterPassword', valid);
return value;
return cryptoService.makeKey(value, profile.email).then(function (result) {
var valid = result.keyB64 === cryptoService.getKey().keyB64;
ngModel.$setValidity('masterPassword', valid);
return value;
});
});
});
}

View File

@ -5,19 +5,18 @@
authService, toastr, $analytics) {
$analytics.eventTrack('organizationDeleteController', { category: 'Modal' });
$scope.submit = function () {
var request = {
masterPasswordHash: cryptoService.hashPassword($scope.masterPassword)
};
$scope.submitPromise = apiService.organizations.del({ id: $state.params.orgId }, request, function () {
$scope.submitPromise = cryptoService.hashPassword($scope.masterPassword).then(function (hash) {
return apiService.organizations.del({ id: $state.params.orgId }, {
masterPasswordHash: hash
}).$promise;
}).then(function () {
$uibModalInstance.dismiss('cancel');
authService.removeProfileOrganization($state.params.orgId);
$analytics.eventTrack('Deleted Organization');
$state.go('backend.user.vault').then(function () {
toastr.success('This organization and all associated data has been deleted.',
'Organization Deleted');
});
}).$promise;
return $state.go('backend.user.vault');
}).then(function () {
toastr.success('This organization and all associated data has been deleted.', 'Organization Deleted');
});
};
$scope.close = function () {

View File

@ -7,48 +7,52 @@ angular
_service.logIn = function (email, masterPassword, token, provider, remember) {
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();
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) {
return;
}
tokenService.setToken(response.access_token);
tokenService.setRefreshToken(response.refresh_token);
cryptoService.setKey(key);
cryptoService.setKey(makeResult.key);
if (response.TwoFactorToken) {
tokenService.setTwoFactorToken(response.TwoFactorToken, email);
}
if (response.Key) {
cryptoService.setEncKey(response.Key, key);
cryptoService.setEncKey(response.Key, makeResult.key);
}
if (response.PrivateKey) {

View File

@ -1,14 +1,16 @@
angular
.module('bit.services')
.factory('cryptoService', function ($sessionStorage, constants, $q) {
.factory('cryptoService', function ($sessionStorage, constants, $q, $window) {
var _service = {},
_key,
_encKey,
_legacyEtmKey,
_orgKeys,
_privateKey,
_publicKey;
_publicKey,
_crypto = $window.crypto,
_subtle = $window.crypto.subtle;
_service.setKey = function (key) {
_key = key;
@ -233,9 +235,18 @@ angular
};
_service.makeKey = function (password, salt) {
var keyBytes = forge.pbkdf2(forge.util.encodeUtf8(password), forge.util.encodeUtf8(salt),
5000, 256 / 8, 'sha256');
return new SymmetricCryptoKey(keyBytes);
if (!$window.cryptoShimmed) {
return pbkdf2WC(password, salt, 5000, 256).then(function (keyBuf) {
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) {
@ -290,8 +301,46 @@ angular
throw 'Invalid parameters.';
}
var hashBits = forge.pbkdf2(key.key, forge.util.encodeUtf8(password), 1, 256 / 8, 'sha256');
return forge.util.encode64(hashBits);
if (!$window.cryptoShimmed) {
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) {
@ -375,11 +424,11 @@ angular
};
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) {
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) {
obj.ct = new Uint8Array(encValue);
if (!keyBuf.macKey) {
@ -554,7 +603,7 @@ angular
var keyBuf = key.getBuffers(),
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) {
encKey = theEncKey;
@ -576,7 +625,7 @@ angular
console.error('MAC failed.');
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) {
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) {
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) {
var binaryString = window.atob(b64Str);
var binaryString = $window.atob(b64Str);
var arr = new Uint8Array(binaryString.length);
for (var i = 0; i < binaryString.length; i++) {
arr[i] = binaryString.charCodeAt(i);
@ -795,6 +844,24 @@ angular
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) {
if (arr.slice) {
return arr.slice(begin, end);

View File

@ -11,22 +11,25 @@
$scope.token = function (model) {
_masterPassword = model.masterPassword;
_masterPasswordHash = cryptoService.hashPassword(_masterPassword);
_newEmail = model.newEmail.toLowerCase();
var encKey = cryptoService.getEncKey();
if (encKey) {
$scope.tokenPromise = requestToken(model);
}
else {
// User is not using an enc key, let's make them one
$scope.tokenPromise = cipherService.updateKey(_masterPasswordHash, function () {
return requestToken(model);
}, processError);
}
cryptoService.hashPassword(_masterPassword).then(function (hash) {
_masterPasswordHash = hash;
var encKey = cryptoService.getEncKey();
if (encKey) {
$scope.tokenPromise = requestToken();
}
else {
// 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 = {
newEmail: _newEmail,
masterPasswordHash: _masterPasswordHash
@ -40,33 +43,31 @@
$scope.confirm = function (model) {
$scope.processing = true;
var newKey = cryptoService.makeKey(_masterPassword, _newEmail);
var encKey = cryptoService.getEncKey();
var newEncKey = cryptoService.encrypt(encKey.key, newKey, 'raw');
$scope.confirmPromise = cryptoService.makeKeyAndHash(_newEmail, _masterPassword).then(function (result) {
var encKey = cryptoService.getEncKey();
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 = {
token: model.token,
newEmail: _newEmail,
masterPasswordHash: _masterPasswordHash,
newMasterPasswordHash: cryptoService.hashPassword(_masterPassword, newKey),
key: newEncKey
};
$scope.confirmPromise = apiService.accounts.email(request).$promise.then(function () {
return apiService.accounts.email(request).$promise;
}).then(function () {
$uibModalInstance.dismiss('cancel');
authService.logOut();
$analytics.eventTrack('Changed Email');
return $state.go('frontend.login.info');
}, processError).then(function () {
}).then(function () {
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 () {
$uibModalInstance.dismiss('cancel');
};

View File

@ -31,39 +31,43 @@
}
else {
// User is not using an enc key, let's make them one
var mpHash = cryptoService.hashPassword(model.masterPassword);
$scope.savePromise = cipherService.updateKey(mpHash, function () {
$scope.savePromise = cryptoService.hashPassword(model.masterPassword).then(function (hash) {
return cipherService.updateKey(hash);
}, processError).then(function () {
return changePassword(model);
}, processError);
}
};
function changePassword(model) {
var makeResult;
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 newEncKey = cryptoService.encrypt(encKey.key, newKey, 'raw');
var newEncKey = cryptoService.encrypt(encKey.key, makeResult.key, 'raw');
var request = {
masterPasswordHash: cryptoService.hashPassword(model.masterPassword),
newMasterPasswordHash: cryptoService.hashPassword(model.newMasterPassword, newKey),
masterPasswordHash: hash,
newMasterPasswordHash: makeResult.hash,
key: newEncKey
};
return apiService.accounts.putPassword(request).$promise;
}, processError).then(function () {
}).then(function () {
$uibModalInstance.dismiss('cancel');
authService.logOut();
$analytics.eventTrack('Changed Password');
return $state.go('frontend.login.info');
}, processError).then(function () {
}).then(function () {
toastr.success('Please log back in.', 'Master Password Changed');
}, processError);
}
function processError() {
$uibModalInstance.dismiss('cancel');
toastr.error('Something went wrong.', 'Oh No!');
}, function () {
$uibModalInstance.dismiss('cancel');
toastr.error('Something went wrong.', 'Oh No!');
});
}
$scope.close = function () {

View File

@ -5,18 +5,18 @@
authService, toastr, $analytics) {
$analytics.eventTrack('settingsDeleteController', { category: 'Modal' });
$scope.submit = function (model) {
var request = {
masterPasswordHash: cryptoService.hashPassword(model.masterPassword)
};
$scope.submitPromise = apiService.accounts.postDelete(request, function () {
$scope.submitPromise = cryptoService.hashPassword(model.masterPassword).then(function (hash) {
return apiService.accounts.postDelete({
masterPasswordHash: hash
}).$promise;
}).then(function () {
$uibModalInstance.dismiss('cancel');
authService.logOut();
$analytics.eventTrack('Deleted Account');
$state.go('frontend.login.info').then(function () {
toastr.success('Your account has been closed and all associated data has been deleted.', 'Account Deleted');
});
}).$promise;
return $state.go('frontend.login.info');
}).then(function () {
toastr.success('Your account has been closed and all associated data has been deleted.', 'Account Deleted');
});
};
$scope.close = function () {

View File

@ -5,22 +5,25 @@
authService, tokenService, toastr, $analytics) {
$analytics.eventTrack('settingsSessionsController', { category: 'Modal' });
$scope.submit = function (model) {
var request = {
masterPasswordHash: cryptoService.hashPassword(model.masterPassword)
};
var hash, profile;
$scope.submitPromise =
authService.getUserProfile().then(function (profile) {
return apiService.accounts.putSecurityStamp(request, function () {
$uibModalInstance.dismiss('cancel');
authService.logOut();
tokenService.clearTwoFactorToken(profile.email);
$analytics.eventTrack('Deauthorized Sessions');
$state.go('frontend.login.info').then(function () {
toastr.success('Please log back in.', 'All Sessions Deauthorized');
});
}).$promise;
});
$scope.submitPromise = cryptoService.hashPassword(model.masterPassword).then(function (theHash) {
hash = theHash;
return authService.getUserProfile();
}).then(function (theProfile) {
profile = theProfile;
return apiService.accounts.putSecurityStamp({
masterPasswordHash: hash
}).$promise;
}).then(function () {
$uibModalInstance.dismiss('cancel');
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 () {

View File

@ -10,12 +10,13 @@
_key = null;
$scope.auth = function (model) {
_masterPasswordHash = cryptoService.hashPassword(model.masterPassword);
var response = null;
$scope.authPromise = apiService.twoFactor.getAuthenticator({}, {
masterPasswordHash: _masterPasswordHash
}).$promise.then(function (apiResponse) {
$scope.authPromise = cryptoService.hashPassword(model.masterPassword).then(function (hash) {
_masterPasswordHash = hash;
return apiService.twoFactor.getAuthenticator({}, {
masterPasswordHash: _masterPasswordHash
}).$promise;
}).then(function (apiResponse) {
response = apiResponse;
return authService.getUserProfile();
}).then(function (profile) {

View File

@ -14,10 +14,12 @@
};
$scope.auth = function (model) {
_masterPasswordHash = cryptoService.hashPassword(model.masterPassword);
$scope.authPromise = apiService.twoFactor.getDuo({}, {
masterPasswordHash: _masterPasswordHash
}).$promise.then(function (apiResponse) {
$scope.authPromise = cryptoService.hashPassword(model.masterPassword).then(function (hash) {
_masterPasswordHash = hash;
return apiService.twoFactor.getDuo({}, {
masterPasswordHash: _masterPasswordHash
}).$promise;
}).then(function (apiResponse) {
processResult(apiResponse);
$scope.authed = true;
});

View File

@ -13,12 +13,13 @@
};
$scope.auth = function (model) {
_masterPasswordHash = cryptoService.hashPassword(model.masterPassword);
var response = null;
$scope.authPromise = apiService.twoFactor.getEmail({}, {
masterPasswordHash: _masterPasswordHash
}).$promise.then(function (apiResponse) {
$scope.authPromise = cryptoService.hashPassword(model.masterPassword).then(function (hash) {
_masterPasswordHash = hash;
return apiService.twoFactor.getEmail({}, {
masterPasswordHash: _masterPasswordHash
}).$promise;
}).then(function (apiResponse) {
response = apiResponse;
return authService.getUserProfile();
}).then(function (profile) {

View File

@ -7,11 +7,11 @@
$scope.code = null;
$scope.auth = function (model) {
var masterPasswordHash = cryptoService.hashPassword(model.masterPassword);
$scope.authPromise = apiService.twoFactor.getRecover({}, {
masterPasswordHash: masterPasswordHash
}).$promise.then(function (apiResponse) {
$scope.authPromise = cryptoService.hashPassword(model.masterPassword).then(function (hash) {
return apiService.twoFactor.getRecover({}, {
masterPasswordHash: hash
}).$promise;
}).then(function (apiResponse) {
$scope.code = formatString(apiResponse.Code);
$scope.authed = true;
});

View File

@ -12,11 +12,12 @@
$scope.deviceError = false;
$scope.auth = function (model) {
_masterPasswordHash = cryptoService.hashPassword(model.masterPassword);
$scope.authPromise = apiService.twoFactor.getU2f({}, {
masterPasswordHash: _masterPasswordHash
}).$promise.then(function (response) {
$scope.authPromise = cryptoService.hashPassword(model.masterPassword).then(function (hash) {
_masterPasswordHash = hash;
return apiService.twoFactor.getU2f({}, {
masterPasswordHash: _masterPasswordHash
}).$promise;
}).then(function (response) {
$scope.enabled = response.Enabled;
$scope.challenge = response.Challenge;
$scope.authed = true;

View File

@ -8,12 +8,13 @@
_masterPasswordHash;
$scope.auth = function (model) {
_masterPasswordHash = cryptoService.hashPassword(model.masterPassword);
var response = null;
$scope.authPromise = apiService.twoFactor.getYubi({}, {
masterPasswordHash: _masterPasswordHash
}).$promise.then(function (apiResponse) {
$scope.authPromise = cryptoService.hashPassword(model.masterPassword).then(function (hash) {
_masterPasswordHash = hash;
return apiService.twoFactor.getYubi({}, {
masterPasswordHash: _masterPasswordHash
}).$promise;
}).then(function (apiResponse) {
response = apiResponse;
return authService.getUserProfile();
}).then(function (profile) {

View File

@ -14,8 +14,9 @@
}
$scope.processing = true;
var mpHash = cryptoService.hashPassword($scope.masterPassword);
$scope.savePromise = cipherService.updateKey(mpHash, function () {
$scope.savePromise = cryptoService.hashPassword($scope.masterPassword).then(function (hash) {
return cipherService.updateKey(hash);
}).then(function () {
$uibModalInstance.dismiss('cancel');
authService.logOut();
$analytics.eventTrack('Key Updated');
@ -23,14 +24,12 @@
}).then(function () {
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 });
}, 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 () {
$uibModalInstance.dismiss('cancel');
};

View File

@ -18,8 +18,8 @@
</div>
<div class="form-group" show-errors>
<label for="masterPassword">Master Password</label>
<input type="password" id="masterPassword" name="MasterPasswordHash" ng-model="model.masterPassword" class="form-control"
master-password required api-field ng-model-options="{ 'updateOn': 'blur'}" />
<input type="password" id="masterPassword" name="MasterPasswordHash" ng-model="model.masterPassword"
class="form-control" master-password required api-field ng-model-options="{ 'updateOn': 'blur'}" />
</div>
</div>
<div class="modal-footer">

View File

@ -25,6 +25,9 @@
isWebkit = !_crypto.subtle && !!_crypto.webkitSubtle;
if (!isIE && !isWebkit) return;
// Added
global.cryptoShimmed = true;
function s2a(s) {
return btoa(s).replace(/\=+$/, '').replace(/\+/g, '-').replace(/\//g, '_');
}