1
0
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:
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; 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 + '.');
} }

View File

@@ -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; });
}; };
}); });

View File

@@ -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: {

View File

@@ -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');

View File

@@ -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; });
}); });
}); });
} }

View File

@@ -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 () {

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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');
}; };

View File

@@ -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 () {

View File

@@ -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 () {

View File

@@ -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 () {

View File

@@ -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) {

View File

@@ -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;
}); });

View File

@@ -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) {

View File

@@ -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;
}); });

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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');
}; };

View File

@@ -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">

View File

@@ -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, '_');
} }