1
0
mirror of https://github.com/bitwarden/web synced 2025-12-06 00:03:28 +00:00
Files
web/src/app/services/cipherService.js
2017-10-23 15:35:46 -04:00

553 lines
25 KiB
JavaScript

angular
.module('bit.services')
.factory('cipherService', function (cryptoService, apiService, $q, $window, constants, appSettings) {
var _service = {};
_service.decryptCiphers = function (encryptedCiphers) {
if (!encryptedCiphers) throw "encryptedCiphers is undefined or null";
var unencryptedCiphers = [];
for (var i = 0; i < encryptedCiphers.length; i++) {
unencryptedCiphers.push(_service.decryptCipher(encryptedCiphers[i]));
}
return unencryptedCiphers;
};
_service.decryptCipher = function (encryptedCipher) {
if (!encryptedCipher) throw "encryptedCipher is undefined or null";
var key = null;
if (encryptedCipher.OrganizationId) {
key = cryptoService.getOrgKey(encryptedCipher.OrganizationId);
}
var cipher = {
id: encryptedCipher.Id,
organizationId: encryptedCipher.OrganizationId,
collectionIds: encryptedCipher.CollectionIds || [],
'type': encryptedCipher.Type,
folderId: encryptedCipher.FolderId,
favorite: encryptedCipher.Favorite,
edit: encryptedCipher.Edit,
organizationUseTotp: encryptedCipher.OrganizationUseTotp,
attachments: null,
icon: null
};
var cipherData = encryptedCipher.Data;
if (cipherData) {
cipher.name = cryptoService.decrypt(cipherData.Name, key);
cipher.notes = _service.decryptProperty(cipherData.Notes, key, true, false);
cipher.fields = _service.decryptFields(key, cipherData.Fields);
var dataObj = {};
switch (cipher.type) {
case constants.cipherType.login:
dataObj.uri = _service.decryptProperty(cipherData.Uri, key, true, false);
dataObj.username = _service.decryptProperty(cipherData.Username, key, true, false);
dataObj.password = _service.decryptProperty(cipherData.Password, key, true, false);
dataObj.totp = _service.decryptProperty(cipherData.Totp, key, true, false);
cipher.login = dataObj;
cipher.icon = 'fa-globe';
break;
case constants.cipherType.secureNote:
dataObj.type = cipherData.Type;
cipher.secureNote = dataObj;
cipher.icon = 'fa-sticky-note-o';
break;
case constants.cipherType.card:
dataObj.cardholderName = _service.decryptProperty(cipherData.CardholderName, key, true, false);
dataObj.number = _service.decryptProperty(cipherData.Number, key, true, false);
dataObj.brand = _service.decryptProperty(cipherData.Brand, key, true, false);
dataObj.expMonth = _service.decryptProperty(cipherData.ExpMonth, key, true, false);
dataObj.expYear = _service.decryptProperty(cipherData.ExpYear, key, true, false);
dataObj.code = _service.decryptProperty(cipherData.Code, key, true, false);
cipher.card = dataObj;
cipher.icon = 'fa-credit-card';
break;
case constants.cipherType.identity:
dataObj.title = _service.decryptProperty(cipherData.Title, key, true, false);
dataObj.firstName = _service.decryptProperty(cipherData.FirstName, key, true, false);
dataObj.middleName = _service.decryptProperty(cipherData.MiddleName, key, true, false);
dataObj.lastName = _service.decryptProperty(cipherData.LastName, key, true, false);
dataObj.address1 = _service.decryptProperty(cipherData.Address1, key, true, false);
dataObj.address2 = _service.decryptProperty(cipherData.Address2, key, true, false);
dataObj.address3 = _service.decryptProperty(cipherData.Address3, key, true, false);
dataObj.city = _service.decryptProperty(cipherData.City, key, true, false);
dataObj.state = _service.decryptProperty(cipherData.State, key, true, false);
dataObj.postalCode = _service.decryptProperty(cipherData.PostalCode, key, true, false);
dataObj.country = _service.decryptProperty(cipherData.Country, key, true, false);
dataObj.company = _service.decryptProperty(cipherData.Company, key, true, false);
dataObj.email = _service.decryptProperty(cipherData.Email, key, true, false);
dataObj.phone = _service.decryptProperty(cipherData.Phone, key, true, false);
dataObj.ssn = _service.decryptProperty(cipherData.SSN, key, true, false);
dataObj.username = _service.decryptProperty(cipherData.Username, key, true, false);
dataObj.passportNumber = _service.decryptProperty(cipherData.PassportNumber, key, true, false);
dataObj.licenseNumber = _service.decryptProperty(cipherData.LicenseNumber, key, true, false);
cipher.identity = dataObj;
cipher.icon = 'fa-id-card-o';
break;
default:
break;
}
}
if (!encryptedCipher.Attachments) {
return cipher;
}
cipher.attachments = [];
for (var i = 0; i < encryptedCipher.Attachments.length; i++) {
cipher.attachments.push(_service.decryptAttachment(key, encryptedCipher.Attachments[i]));
}
return cipher;
};
_service.decryptCipherPreview = function (encryptedCipher) {
if (!encryptedCipher) throw "encryptedCipher is undefined or null";
var key = null;
if (encryptedCipher.OrganizationId) {
key = cryptoService.getOrgKey(encryptedCipher.OrganizationId);
}
var cipher = {
id: encryptedCipher.Id,
organizationId: encryptedCipher.OrganizationId,
collectionIds: encryptedCipher.CollectionIds || [],
'type': encryptedCipher.Type,
folderId: encryptedCipher.FolderId,
favorite: encryptedCipher.Favorite,
edit: encryptedCipher.Edit,
organizationUseTotp: encryptedCipher.OrganizationUseTotp,
hasAttachments: !!encryptedCipher.Attachments && encryptedCipher.Attachments.length > 0,
meta: {},
icon: null
};
var cipherData = encryptedCipher.Data;
if (cipherData) {
cipher.name = _service.decryptProperty(cipherData.Name, key, false, true);
var dataObj = {};
switch (cipher.type) {
case constants.cipherType.login:
cipher.subTitle = _service.decryptProperty(cipherData.Username, key, true, true);
cipher.meta.password = _service.decryptProperty(cipherData.Password, key, true, true);
cipher.meta.uri = _service.decryptProperty(cipherData.Uri, key, true, true);
setLoginIcon(cipher, cipher.meta.uri, true);
break;
case constants.cipherType.secureNote:
cipher.subTitle = null;
cipher.icon = 'fa-sticky-note-o';
break;
case constants.cipherType.card:
cipher.subTitle = '';
cipher.meta.number = _service.decryptProperty(cipherData.Number, key, true, true);
var brand = _service.decryptProperty(cipherData.Brand, key, true, true);
if (brand) {
cipher.subTitle = brand;
}
if (cipher.meta.number && cipher.meta.number.length >= 4) {
if (cipher.subTitle !== '') {
cipher.subTitle += ', ';
}
cipher.subTitle += ('*' + cipher.meta.number.substr(cipher.meta.number.length - 4));
}
cipher.icon = 'fa-credit-card';
break;
case constants.cipherType.identity:
var firstName = _service.decryptProperty(cipherData.FirstName, key, true, true);
var lastName = _service.decryptProperty(cipherData.LastName, key, true, true);
cipher.subTitle = '';
if (firstName) {
cipher.subTitle = firstName;
}
if (lastName) {
if (cipher.subTitle !== '') {
cipher.subTitle += ' ';
}
cipher.subTitle += lastName;
}
cipher.icon = 'fa-id-card-o';
break;
default:
break;
}
if (cipher.subTitle === '') {
cipher.subTitle = null;
}
}
return cipher;
};
function setLoginIcon(cipher, uri, setImage) {
if (!appSettings.selfHosted && uri) {
var hostnameUri = uri,
isWebsite = false;
if (hostnameUri.indexOf('androidapp://') === 0) {
cipher.icon = 'fa-android';
}
else if (hostnameUri.indexOf('iosapp://') === 0) {
cipher.icon = 'fa-apple';
}
else if (hostnameUri.indexOf('://') === -1 && hostnameUri.indexOf('.') > -1) {
hostnameUri = "http://" + hostnameUri;
isWebsite = true;
}
else {
isWebsite = hostnameUri.indexOf('http') === 0 && hostnameUri.indexOf('.') > -1;
}
if (setImage && isWebsite) {
try {
var url = new URL(hostnameUri);
cipher.meta.image = 'https://icons.bitwarden.com/' + url.hostname + '/icon.png';
}
catch (e) { }
}
}
if (!cipher.icon) {
cipher.icon = 'fa-globe';
}
}
_service.decryptAttachment = function (key, encryptedAttachment) {
if (!encryptedAttachment) throw "encryptedAttachment is undefined or null";
return {
id: encryptedAttachment.Id,
url: encryptedAttachment.Url,
fileName: cryptoService.decrypt(encryptedAttachment.FileName, key),
size: encryptedAttachment.SizeName
};
};
_service.downloadAndDecryptAttachment = function (key, decryptedAttachment, openDownload) {
var deferred = $q.defer();
var req = new XMLHttpRequest();
req.open('GET', decryptedAttachment.url, true);
req.responseType = 'arraybuffer';
req.onload = function (evt) {
if (!req.response) {
deferred.reject('No response');
// error
return;
}
cryptoService.decryptFromBytes(req.response, key).then(function (decBuf) {
if (openDownload) {
var blob = new Blob([decBuf]);
// IE hack. ref http://msdn.microsoft.com/en-us/library/ie/hh779016.aspx
if ($window.navigator.msSaveOrOpenBlob) {
$window.navigator.msSaveBlob(blob, decryptedAttachment.fileName);
}
else {
var a = $window.document.createElement('a');
a.href = $window.URL.createObjectURL(blob);
a.download = decryptedAttachment.fileName;
$window.document.body.appendChild(a);
a.click();
$window.document.body.removeChild(a);
}
}
deferred.resolve(new Uint8Array(decBuf));
});
};
req.send(null);
return deferred.promise;
};
_service.decryptFields = function (key, encryptedFields) {
var unencryptedFields = [];
if (encryptedFields) {
for (var i = 0; i < encryptedFields.length; i++) {
unencryptedFields.push(_service.decryptField(key, encryptedFields[i]));
}
}
return unencryptedFields;
};
_service.decryptField = function (key, encryptedField) {
if (!encryptedField) throw "encryptedField is undefined or null";
return {
type: encryptedField.Type.toString(),
name: encryptedField.Name && encryptedField.Name !== '' ? cryptoService.decrypt(encryptedField.Name, key) : null,
value: encryptedField.Value && encryptedField.Value !== '' ? cryptoService.decrypt(encryptedField.Value, key) : null
};
};
_service.decryptFolders = function (encryptedFolders) {
if (!encryptedFolders) throw "encryptedFolders is undefined or null";
var unencryptedFolders = [];
for (var i = 0; i < encryptedFolders.length; i++) {
unencryptedFolders.push(_service.decryptFolder(encryptedFolders[i]));
}
return unencryptedFolders;
};
_service.decryptFolder = function (encryptedFolder) {
if (!encryptedFolder) throw "encryptedFolder is undefined or null";
return {
id: encryptedFolder.Id,
name: cryptoService.decrypt(encryptedFolder.Name)
};
};
_service.decryptFolderPreview = function (encryptedFolder) {
if (!encryptedFolder) throw "encryptedFolder is undefined or null";
return {
id: encryptedFolder.Id,
name: _service.decryptProperty(encryptedFolder.Name, null, false, true)
};
};
_service.decryptCollections = function (encryptedCollections, orgId, catchError) {
if (!encryptedCollections) throw "encryptedCollections is undefined or null";
var unencryptedCollections = [];
for (var i = 0; i < encryptedCollections.length; i++) {
unencryptedCollections.push(_service.decryptCollection(encryptedCollections[i], orgId, catchError));
}
return unencryptedCollections;
};
_service.decryptCollection = function (encryptedCollection, orgId, catchError) {
if (!encryptedCollection) throw "encryptedCollection is undefined or null";
catchError = catchError === true ? true : false;
orgId = orgId || encryptedCollection.OrganizationId;
var key = cryptoService.getOrgKey(orgId);
return {
id: encryptedCollection.Id,
name: catchError ? _service.decryptProperty(encryptedCollection.Name, key, false, true) :
cryptoService.decrypt(encryptedCollection.Name, key)
};
};
_service.decryptProperty = function (property, key, checkEmpty, showError) {
if (checkEmpty && (!property || property === '')) {
return null;
}
try {
property = cryptoService.decrypt(property, key);
}
catch (err) {
property = null;
}
return property || (showError ? '[error: cannot decrypt]' : null);
};
_service.encryptCiphers = function (unencryptedCiphers, key) {
if (!unencryptedCiphers) throw "unencryptedCiphers is undefined or null";
var encryptedCiphers = [];
for (var i = 0; i < unencryptedCiphers.length; i++) {
encryptedCiphers.push(_service.encryptCipher(unencryptedCiphers[i], null, key));
}
return encryptedCiphers;
};
_service.encryptCipher = function (unencryptedCipher, type, key, attachments) {
if (!unencryptedCipher) throw "unencryptedCipher is undefined or null";
if (unencryptedCipher.organizationId) {
key = key || cryptoService.getOrgKey(unencryptedCipher.organizationId);
}
var cipher = {
id: unencryptedCipher.id,
'type': type || unencryptedCipher.type,
organizationId: unencryptedCipher.organizationId || null,
folderId: unencryptedCipher.folderId === '' ? null : unencryptedCipher.folderId,
favorite: unencryptedCipher.favorite !== null ? unencryptedCipher.favorite : false,
name: cryptoService.encrypt(unencryptedCipher.name, key),
notes: encryptProperty(unencryptedCipher.notes, key),
fields: _service.encryptFields(unencryptedCipher.fields, key)
};
switch (cipher.type) {
case constants.cipherType.login:
var loginData = unencryptedCipher.login;
cipher.login = {
uri: encryptProperty(loginData.uri, key),
username: encryptProperty(loginData.username, key),
password: encryptProperty(loginData.password, key),
totp: encryptProperty(loginData.totp, key)
};
break;
case constants.cipherType.secureNote:
cipher.secureNote = {
type: unencryptedCipher.secureNote.type
};
break;
case constants.cipherType.card:
var cardData = unencryptedCipher.card;
cipher.card = {
cardholderName: encryptProperty(cardData.cardholderName, key),
brand: encryptProperty(cardData.brand, key),
number: encryptProperty(cardData.number, key),
expMonth: encryptProperty(cardData.expMonth, key),
expYear: encryptProperty(cardData.expYear, key),
code: encryptProperty(cardData.code, key)
};
break;
case constants.cipherType.identity:
var identityData = unencryptedCipher.identity;
cipher.identity = {
title: encryptProperty(identityData.title, key),
firstName: encryptProperty(identityData.firstName, key),
middleName: encryptProperty(identityData.middleName, key),
lastName: encryptProperty(identityData.lastName, key),
address1: encryptProperty(identityData.address1, key),
address2: encryptProperty(identityData.address2, key),
address3: encryptProperty(identityData.address3, key),
city: encryptProperty(identityData.city, key),
state: encryptProperty(identityData.state, key),
postalCode: encryptProperty(identityData.postalCode, key),
country: encryptProperty(identityData.country, key),
company: encryptProperty(identityData.company, key),
email: encryptProperty(identityData.email, key),
phone: encryptProperty(identityData.phone, key),
ssn: encryptProperty(identityData.ssn, key),
username: encryptProperty(identityData.username, key),
passportNumber: encryptProperty(identityData.passportNumber, key),
licenseNumber: encryptProperty(identityData.licenseNumber, key)
};
break;
default:
break;
}
if (unencryptedCipher.attachments && attachments) {
cipher.attachments = {};
for (var i = 0; i < unencryptedCipher.attachments.length; i++) {
cipher.attachments[unencryptedCipher.attachments[i].id] =
cryptoService.encrypt(unencryptedCipher.attachments[i].fileName, key);
}
}
return cipher;
};
_service.encryptAttachmentFile = function (key, unencryptedFile) {
var deferred = $q.defer();
if (unencryptedFile.size > 104857600) { // 100 MB
deferred.reject('Maximum file size is 100 MB.');
return;
}
var reader = new FileReader();
reader.readAsArrayBuffer(unencryptedFile);
reader.onload = function (evt) {
cryptoService.encryptToBytes(evt.target.result, key).then(function (encData) {
deferred.resolve({
fileName: cryptoService.encrypt(unencryptedFile.name, key),
data: new Uint8Array(encData),
size: unencryptedFile.size
});
});
};
reader.onerror = function (evt) {
deferred.reject('Error reading file.');
};
return deferred.promise;
};
_service.encryptFields = function (unencryptedFields, key) {
if (!unencryptedFields || !unencryptedFields.length) {
return null;
}
var encFields = [];
for (var i = 0; i < unencryptedFields.length; i++) {
if (!unencryptedFields[i]) {
continue;
}
encFields.push(_service.encryptField(unencryptedFields[i], key));
}
return encFields;
};
_service.encryptField = function (unencryptedField, key) {
if (!unencryptedField) throw "unencryptedField is undefined or null";
return {
type: parseInt(unencryptedField.type),
name: unencryptedField.name ? cryptoService.encrypt(unencryptedField.name, key) : null,
value: unencryptedField.value ? cryptoService.encrypt(unencryptedField.value.toString(), key) : null
};
};
_service.encryptFolders = function (unencryptedFolders, key) {
if (!unencryptedFolders) throw "unencryptedFolders is undefined or null";
var encryptedFolders = [];
for (var i = 0; i < unencryptedFolders.length; i++) {
encryptedFolders.push(_service.encryptFolder(unencryptedFolders[i], key));
}
return encryptedFolders;
};
_service.encryptFolder = function (unencryptedFolder, key) {
if (!unencryptedFolder) throw "unencryptedFolder is undefined or null";
return {
id: unencryptedFolder.id,
name: cryptoService.encrypt(unencryptedFolder.name, key)
};
};
_service.encryptCollections = function (unencryptedCollections, orgId) {
if (!unencryptedCollections) throw "unencryptedCollections is undefined or null";
var encryptedCollections = [];
for (var i = 0; i < unencryptedCollections.length; i++) {
encryptedCollections.push(_service.encryptCollection(unencryptedCollections[i], orgId));
}
return encryptedCollections;
};
_service.encryptCollection = function (unencryptedCollection, orgId) {
if (!unencryptedCollection) throw "unencryptedCollection is undefined or null";
return {
id: unencryptedCollection.id,
name: cryptoService.encrypt(unencryptedCollection.name, cryptoService.getOrgKey(orgId))
};
};
function encryptProperty(property, key) {
return !property || property === '' ? null : cryptoService.encrypt(property, key);
}
return _service;
});