mirror of
https://github.com/bitwarden/browser
synced 2025-12-25 12:43:36 +00:00
encrypt, upload, and view attachments
This commit is contained in:
@@ -40,7 +40,13 @@
|
||||
del: { url: _apiUri + '/ciphers/:id/delete', method: 'POST', params: { id: '@id' } },
|
||||
delAdmin: { url: _apiUri + '/ciphers/:id/delete-admin', method: 'POST', params: { id: '@id' } },
|
||||
delMany: { url: _apiUri + '/ciphers/delete', method: 'POST' },
|
||||
moveMany: { url: _apiUri + '/ciphers/move', method: 'POST' }
|
||||
moveMany: { url: _apiUri + '/ciphers/move', method: 'POST' },
|
||||
postAttachment: {
|
||||
url: _apiUri + '/ciphers/:id/attachment',
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': undefined },
|
||||
params: { id: '@id' }
|
||||
}
|
||||
});
|
||||
|
||||
_service.organizations = $resource(_apiUri + '/organizations/:id', {}, {
|
||||
|
||||
@@ -35,9 +35,19 @@ angular
|
||||
uri: encryptedLogin.Uri && encryptedLogin.Uri !== '' ? cryptoService.decrypt(encryptedLogin.Uri, key) : null,
|
||||
username: encryptedLogin.Username && encryptedLogin.Username !== '' ? cryptoService.decrypt(encryptedLogin.Username, key) : null,
|
||||
password: encryptedLogin.Password && encryptedLogin.Password !== '' ? cryptoService.decrypt(encryptedLogin.Password, key) : null,
|
||||
notes: encryptedLogin.Notes && encryptedLogin.Notes !== '' ? cryptoService.decrypt(encryptedLogin.Notes, key) : null
|
||||
notes: encryptedLogin.Notes && encryptedLogin.Notes !== '' ? cryptoService.decrypt(encryptedLogin.Notes, key) : null,
|
||||
attachments: null
|
||||
};
|
||||
|
||||
if (!encryptedLogin.Attachments) {
|
||||
return login;
|
||||
}
|
||||
|
||||
login.attachments = [];
|
||||
for (var i = 0; i < encryptedLogin.Attachments.length; i++) {
|
||||
login.attachments.push(_service.decryptAttachment(key, encryptedLogin.Attachments[i]));
|
||||
}
|
||||
|
||||
return login;
|
||||
};
|
||||
|
||||
@@ -58,12 +68,23 @@ angular
|
||||
edit: encryptedCipher.Edit,
|
||||
name: _service.decryptProperty(encryptedCipher.Data.Name, key, false),
|
||||
username: _service.decryptProperty(encryptedCipher.Data.Username, key, true),
|
||||
password: _service.decryptProperty(encryptedCipher.Data.Password, key, true)
|
||||
password: _service.decryptProperty(encryptedCipher.Data.Password, key, true),
|
||||
hasAttachments: !!encryptedCipher.Attachments
|
||||
};
|
||||
|
||||
return login;
|
||||
};
|
||||
|
||||
_service.decryptAttachment = function (key, encryptedAttachment) {
|
||||
if (!encryptedAttachment) throw "encryptedAttachment is undefined or null";
|
||||
|
||||
return {
|
||||
id: encryptedAttachment.Id,
|
||||
fileName: cryptoService.decrypt(encryptedAttachment.FileName, key),
|
||||
size: encryptedAttachment.SizeName
|
||||
};
|
||||
};
|
||||
|
||||
_service.decryptFolders = function (encryptedFolders) {
|
||||
if (!encryptedFolders) throw "encryptedFolders is undefined or null";
|
||||
|
||||
|
||||
@@ -295,6 +295,41 @@ angular
|
||||
};
|
||||
|
||||
_service.encrypt = function (plainValue, key, plainValueEncoding) {
|
||||
var encValue = aesEncrypt(plainValue, key, plainValueEncoding);
|
||||
|
||||
var iv = forge.util.encode64(encValue.iv);
|
||||
var ct = forge.util.encode64(encValue.ct);
|
||||
var cipherString = iv + '|' + ct;
|
||||
|
||||
if (encValue.mac) {
|
||||
var mac = forge.util.encode64(encValue.mac)
|
||||
cipherString = cipherString + '|' + mac;
|
||||
}
|
||||
|
||||
return encValue.key.encType + '.' + cipherString;
|
||||
};
|
||||
|
||||
_service.encryptToBytes = function (plainValue, key) {
|
||||
return aesEncryptWC(plainValue, key).then(function (encValue) {
|
||||
var macLen = 0;
|
||||
if (encValue.mac) {
|
||||
macLen = encValue.mac.length
|
||||
}
|
||||
|
||||
var encBytes = new Uint8Array(1 + encValue.iv.length + macLen + encValue.ct.length);
|
||||
|
||||
encBytes.set([encValue.key.encType]);
|
||||
encBytes.set(encValue.iv, 1);
|
||||
if (encValue.mac) {
|
||||
encBytes.set(encValue.mac, 1 + encValue.iv.length);
|
||||
}
|
||||
encBytes.set(encValue.ct, 1 + encValue.iv.length + macLen);
|
||||
|
||||
return encBytes.buffer;
|
||||
});
|
||||
};
|
||||
|
||||
function aesEncrypt(plainValue, key, plainValueEncoding) {
|
||||
key = key || _service.getEncKey() || _service.getKey();
|
||||
|
||||
if (!key) {
|
||||
@@ -309,18 +344,55 @@ angular
|
||||
cipher.update(buffer);
|
||||
cipher.finish();
|
||||
|
||||
var iv = forge.util.encode64(ivBytes);
|
||||
var ctBytes = cipher.output.getBytes();
|
||||
var ct = forge.util.encode64(ctBytes);
|
||||
var cipherString = iv + '|' + ct;
|
||||
|
||||
var macBytes = null;
|
||||
if (key.macKey) {
|
||||
var mac = computeMac(ivBytes + ctBytes, key.macKey, true);
|
||||
cipherString = cipherString + '|' + mac;
|
||||
macBytes = computeMac(ivBytes + ctBytes, key.macKey, false);
|
||||
}
|
||||
|
||||
return key.encType + '.' + cipherString;
|
||||
};
|
||||
return {
|
||||
iv: ivBytes,
|
||||
ct: ctBytes,
|
||||
mac: macBytes,
|
||||
key: key,
|
||||
plainValueEncoding: plainValueEncoding
|
||||
};
|
||||
}
|
||||
|
||||
function aesEncryptWC(plainValue, key) {
|
||||
key = key || _service.getEncKey() || _service.getKey();
|
||||
|
||||
if (!key) {
|
||||
throw 'Encryption key unavailable.';
|
||||
}
|
||||
|
||||
var obj = {
|
||||
iv: new Uint8Array(16),
|
||||
ct: null,
|
||||
mac: null,
|
||||
key: key
|
||||
};
|
||||
|
||||
var keyBuf = key.getBuffers();
|
||||
window.crypto.getRandomValues(obj.iv);
|
||||
|
||||
return window.crypto.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);
|
||||
}).then(function (encValue) {
|
||||
obj.ct = new Uint8Array(encValue);
|
||||
if (!keyBuf.macKey) {
|
||||
return null;
|
||||
}
|
||||
return computeMacWC(encValue, keyBuf.macKey);
|
||||
}).then(function (mac) {
|
||||
if (mac) {
|
||||
obj.mac = new Uint8Array(mac);
|
||||
}
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
|
||||
_service.rsaEncrypt = function (plainValue, publicKey, key) {
|
||||
publicKey = publicKey || _service.getPublicKey();
|
||||
@@ -513,6 +585,13 @@ angular
|
||||
return b64Output ? forge.util.encode64(mac.getBytes()) : mac.getBytes();
|
||||
}
|
||||
|
||||
function computeMacWC(data, macKey) {
|
||||
return window.crypto.subtle.importKey('raw', macKey, { name: 'HMAC', hash: { name: 'SHA-256' } }, false, ['sign'])
|
||||
.then(function (key) {
|
||||
return window.crypto.subtle.sign({ name: 'HMAC' }, key, data);
|
||||
});
|
||||
}
|
||||
|
||||
// Safely compare two MACs in a way that protects against timing attacks (Double HMAC Verification).
|
||||
// ref: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/february/double-hmac-verification/
|
||||
function macsEqual(macKey, mac1, mac2) {
|
||||
@@ -577,5 +656,33 @@ angular
|
||||
}
|
||||
}
|
||||
|
||||
SymmetricCryptoKey.prototype.getBuffers = function () {
|
||||
var key = b64ToArray(this.keyB64);
|
||||
|
||||
var keys = {
|
||||
key: key.buffer
|
||||
};
|
||||
|
||||
if (this.macKey) {
|
||||
keys.encKey = key.slice(0, key.length / 2).buffer;
|
||||
keys.macKey = key.slice(key.length / 2).buffer;
|
||||
}
|
||||
else {
|
||||
keys.encKey = key.buffer;
|
||||
keys.macKey = null;
|
||||
}
|
||||
|
||||
return keys;
|
||||
};
|
||||
|
||||
function b64ToArray(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);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
return _service;
|
||||
});
|
||||
Reference in New Issue
Block a user