mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 16:23:44 +00:00
support for new attachment keys
This commit is contained in:
@@ -143,7 +143,8 @@ export class AttachmentsComponent implements OnInit {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const buf = await response.arrayBuffer();
|
const buf = await response.arrayBuffer();
|
||||||
const key = await this.cryptoService.getOrgKey(this.cipher.organizationId);
|
const key = attachment.key != null ? attachment.key :
|
||||||
|
await this.cryptoService.getOrgKey(this.cipher.organizationId);
|
||||||
const decBuf = await this.cryptoService.decryptFromBytes(buf, key);
|
const decBuf = await this.cryptoService.decryptFromBytes(buf, key);
|
||||||
this.platformUtilsService.saveFile(this.win, decBuf, null, attachment.fileName);
|
this.platformUtilsService.saveFile(this.win, decBuf, null, attachment.fileName);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -171,7 +171,8 @@ export class ViewComponent implements OnDestroy, OnInit {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const buf = await response.arrayBuffer();
|
const buf = await response.arrayBuffer();
|
||||||
const key = await this.cryptoService.getOrgKey(this.cipher.organizationId);
|
const key = attachment.key != null ? attachment.key :
|
||||||
|
await this.cryptoService.getOrgKey(this.cipher.organizationId);
|
||||||
const decBuf = await this.cryptoService.decryptFromBytes(buf, key);
|
const decBuf = await this.cryptoService.decryptFromBytes(buf, key);
|
||||||
this.platformUtilsService.saveFile(this.win, decBuf, null, attachment.fileName);
|
this.platformUtilsService.saveFile(this.win, decBuf, null, attachment.fileName);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export class AttachmentData {
|
|||||||
id: string;
|
id: string;
|
||||||
url: string;
|
url: string;
|
||||||
fileName: string;
|
fileName: string;
|
||||||
|
key: string;
|
||||||
size: number;
|
size: number;
|
||||||
sizeName: string;
|
sizeName: string;
|
||||||
|
|
||||||
@@ -14,6 +15,7 @@ export class AttachmentData {
|
|||||||
this.id = response.id;
|
this.id = response.id;
|
||||||
this.url = response.url;
|
this.url = response.url;
|
||||||
this.fileName = response.fileName;
|
this.fileName = response.fileName;
|
||||||
|
this.key = response.key;
|
||||||
this.size = response.size;
|
this.size = response.size;
|
||||||
this.sizeName = response.sizeName;
|
this.sizeName = response.sizeName;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
import { AttachmentData } from '../data/attachmentData';
|
import { AttachmentData } from '../data/attachmentData';
|
||||||
|
|
||||||
|
import { AttachmentView } from '../view/attachmentView';
|
||||||
|
|
||||||
import { CipherString } from './cipherString';
|
import { CipherString } from './cipherString';
|
||||||
import Domain from './domainBase';
|
import Domain from './domainBase';
|
||||||
|
import { SymmetricCryptoKey } from './symmetricCryptoKey';
|
||||||
|
|
||||||
import { AttachmentView } from '../view/attachmentView';
|
import { CryptoService } from '../../abstractions/crypto.service';
|
||||||
|
|
||||||
|
import { Utils } from '../../misc/utils';
|
||||||
|
|
||||||
export class Attachment extends Domain {
|
export class Attachment extends Domain {
|
||||||
id: string;
|
id: string;
|
||||||
url: string;
|
url: string;
|
||||||
size: number;
|
size: number;
|
||||||
sizeName: string;
|
sizeName: string;
|
||||||
|
key: CipherString;
|
||||||
fileName: CipherString;
|
fileName: CipherString;
|
||||||
|
|
||||||
constructor(obj?: AttachmentData, alreadyEncrypted: boolean = false) {
|
constructor(obj?: AttachmentData, alreadyEncrypted: boolean = false) {
|
||||||
@@ -24,13 +30,34 @@ export class Attachment extends Domain {
|
|||||||
url: null,
|
url: null,
|
||||||
sizeName: null,
|
sizeName: null,
|
||||||
fileName: null,
|
fileName: null,
|
||||||
|
key: null,
|
||||||
}, alreadyEncrypted, ['id', 'url', 'sizeName']);
|
}, alreadyEncrypted, ['id', 'url', 'sizeName']);
|
||||||
}
|
}
|
||||||
|
|
||||||
decrypt(orgId: string): Promise<AttachmentView> {
|
async decrypt(orgId: string): Promise<AttachmentView> {
|
||||||
return this.decryptObj(new AttachmentView(this), {
|
const view = await this.decryptObj(new AttachmentView(this), {
|
||||||
fileName: null,
|
fileName: null,
|
||||||
}, orgId);
|
}, orgId);
|
||||||
|
|
||||||
|
if (this.key != null) {
|
||||||
|
let cryptoService: CryptoService;
|
||||||
|
const containerService = (Utils.global as any).bitwardenContainerService;
|
||||||
|
if (containerService) {
|
||||||
|
cryptoService = containerService.getCryptoService();
|
||||||
|
} else {
|
||||||
|
throw new Error('global bitwardenContainerService not initialized.');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const orgKey = await cryptoService.getOrgKey(orgId);
|
||||||
|
const decValue = await cryptoService.decryptToBytes(this.key, orgKey);
|
||||||
|
view.key = new SymmetricCryptoKey(decValue);
|
||||||
|
} catch (e) {
|
||||||
|
// TODO: error?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
toAttachmentData(): AttachmentData {
|
toAttachmentData(): AttachmentData {
|
||||||
@@ -40,6 +67,7 @@ export class Attachment extends Domain {
|
|||||||
url: null,
|
url: null,
|
||||||
sizeName: null,
|
sizeName: null,
|
||||||
fileName: null,
|
fileName: null,
|
||||||
|
key: null,
|
||||||
}, ['id', 'url', 'sizeName']);
|
}, ['id', 'url', 'sizeName']);
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|||||||
4
src/models/request/attachmentRequest.ts
Normal file
4
src/models/request/attachmentRequest.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export class AttachmentRequest {
|
||||||
|
fileName: string;
|
||||||
|
key: string;
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import { IdentityApi } from '../api/identityApi';
|
|||||||
import { LoginApi } from '../api/loginApi';
|
import { LoginApi } from '../api/loginApi';
|
||||||
import { SecureNoteApi } from '../api/secureNoteApi';
|
import { SecureNoteApi } from '../api/secureNoteApi';
|
||||||
|
|
||||||
|
import { AttachmentRequest } from './attachmentRequest';
|
||||||
import { PasswordHistoryRequest } from './passwordHistoryRequest';
|
import { PasswordHistoryRequest } from './passwordHistoryRequest';
|
||||||
|
|
||||||
export class CipherRequest {
|
export class CipherRequest {
|
||||||
@@ -23,7 +24,9 @@ export class CipherRequest {
|
|||||||
identity: IdentityApi;
|
identity: IdentityApi;
|
||||||
fields: FieldApi[];
|
fields: FieldApi[];
|
||||||
passwordHistory: PasswordHistoryRequest[];
|
passwordHistory: PasswordHistoryRequest[];
|
||||||
|
// Deprecated, remove at some point and rename attachments2 to attachments
|
||||||
attachments: { [id: string]: string; };
|
attachments: { [id: string]: string; };
|
||||||
|
attachments2: { [id: string]: AttachmentRequest; };
|
||||||
|
|
||||||
constructor(cipher: Cipher) {
|
constructor(cipher: Cipher) {
|
||||||
this.type = cipher.type;
|
this.type = cipher.type;
|
||||||
@@ -119,8 +122,16 @@ export class CipherRequest {
|
|||||||
|
|
||||||
if (cipher.attachments) {
|
if (cipher.attachments) {
|
||||||
this.attachments = {};
|
this.attachments = {};
|
||||||
|
this.attachments2 = {};
|
||||||
cipher.attachments.forEach((attachment) => {
|
cipher.attachments.forEach((attachment) => {
|
||||||
this.attachments[attachment.id] = attachment.fileName ? attachment.fileName.encryptedString : null;
|
const fileName = attachment.fileName ? attachment.fileName.encryptedString : null;
|
||||||
|
this.attachments[attachment.id] = fileName;
|
||||||
|
const attachmentRequest = new AttachmentRequest();
|
||||||
|
attachmentRequest.fileName = fileName;
|
||||||
|
if (attachment.key != null) {
|
||||||
|
attachmentRequest.key = attachment.key.encryptedString;
|
||||||
|
}
|
||||||
|
this.attachments2[attachment.id] = attachmentRequest;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ export class AttachmentResponse {
|
|||||||
id: string;
|
id: string;
|
||||||
url: string;
|
url: string;
|
||||||
fileName: string;
|
fileName: string;
|
||||||
|
key: string;
|
||||||
size: number;
|
size: number;
|
||||||
sizeName: string;
|
sizeName: string;
|
||||||
|
|
||||||
@@ -9,6 +10,7 @@ export class AttachmentResponse {
|
|||||||
this.id = response.Id;
|
this.id = response.Id;
|
||||||
this.url = response.Url;
|
this.url = response.Url;
|
||||||
this.fileName = response.FileName;
|
this.fileName = response.FileName;
|
||||||
|
this.key = response.Key;
|
||||||
this.size = response.Size;
|
this.size = response.Size;
|
||||||
this.sizeName = response.SizeName;
|
this.sizeName = response.SizeName;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { View } from './view';
|
import { View } from './view';
|
||||||
|
|
||||||
import { Attachment } from '../domain/attachment';
|
import { Attachment } from '../domain/attachment';
|
||||||
|
import { SymmetricCryptoKey } from '../domain/symmetricCryptoKey';
|
||||||
|
|
||||||
export class AttachmentView implements View {
|
export class AttachmentView implements View {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -8,6 +9,7 @@ export class AttachmentView implements View {
|
|||||||
size: number;
|
size: number;
|
||||||
sizeName: string;
|
sizeName: string;
|
||||||
fileName: string;
|
fileName: string;
|
||||||
|
key: SymmetricCryptoKey;
|
||||||
|
|
||||||
constructor(a?: Attachment) {
|
constructor(a?: Attachment) {
|
||||||
if (!a) {
|
if (!a) {
|
||||||
|
|||||||
@@ -173,7 +173,10 @@ export class CipherService implements CipherServiceAbstraction {
|
|||||||
attachment.url = model.url;
|
attachment.url = model.url;
|
||||||
const promise = this.encryptObjProperty(model, attachment, {
|
const promise = this.encryptObjProperty(model, attachment, {
|
||||||
fileName: null,
|
fileName: null,
|
||||||
}, key).then(() => {
|
}, key).then(async () => {
|
||||||
|
if (model.key != null) {
|
||||||
|
attachment.key = await this.cryptoService.encrypt(model.key.key, key);
|
||||||
|
}
|
||||||
encAttachments.push(attachment);
|
encAttachments.push(attachment);
|
||||||
});
|
});
|
||||||
promises.push(promise);
|
promises.push(promise);
|
||||||
@@ -519,14 +522,18 @@ export class CipherService implements CipherServiceAbstraction {
|
|||||||
data: ArrayBuffer, admin = false): Promise<Cipher> {
|
data: ArrayBuffer, admin = false): Promise<Cipher> {
|
||||||
const key = await this.cryptoService.getOrgKey(cipher.organizationId);
|
const key = await this.cryptoService.getOrgKey(cipher.organizationId);
|
||||||
const encFileName = await this.cryptoService.encrypt(filename, key);
|
const encFileName = await this.cryptoService.encrypt(filename, key);
|
||||||
const encData = await this.cryptoService.encryptToBytes(data, key);
|
|
||||||
|
const dataEncKey = await this.cryptoService.makeEncKey(key);
|
||||||
|
const encData = await this.cryptoService.encryptToBytes(data, dataEncKey[0]);
|
||||||
|
|
||||||
const fd = new FormData();
|
const fd = new FormData();
|
||||||
try {
|
try {
|
||||||
|
fd.append('key', dataEncKey[1].encryptedString);
|
||||||
const blob = new Blob([encData], { type: 'application/octet-stream' });
|
const blob = new Blob([encData], { type: 'application/octet-stream' });
|
||||||
fd.append('data', blob, encFileName.encryptedString);
|
fd.append('data', blob, encFileName.encryptedString);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (Utils.isNode && !Utils.isBrowser) {
|
if (Utils.isNode && !Utils.isBrowser) {
|
||||||
|
fd.append('key', dataEncKey[1].encryptedString);
|
||||||
fd.append('data', Buffer.from(encData) as any, {
|
fd.append('data', Buffer.from(encData) as any, {
|
||||||
filepath: encFileName.encryptedString,
|
filepath: encFileName.encryptedString,
|
||||||
contentType: 'application/octet-stream',
|
contentType: 'application/octet-stream',
|
||||||
@@ -753,15 +760,19 @@ export class CipherService implements CipherServiceAbstraction {
|
|||||||
const buf = await attachmentResponse.arrayBuffer();
|
const buf = await attachmentResponse.arrayBuffer();
|
||||||
const decBuf = await this.cryptoService.decryptFromBytes(buf, null);
|
const decBuf = await this.cryptoService.decryptFromBytes(buf, null);
|
||||||
const key = await this.cryptoService.getOrgKey(organizationId);
|
const key = await this.cryptoService.getOrgKey(organizationId);
|
||||||
const encData = await this.cryptoService.encryptToBytes(decBuf, key);
|
|
||||||
const encFileName = await this.cryptoService.encrypt(attachmentView.fileName, key);
|
const encFileName = await this.cryptoService.encrypt(attachmentView.fileName, key);
|
||||||
|
|
||||||
|
const dataEncKey = await this.cryptoService.makeEncKey(key);
|
||||||
|
const encData = await this.cryptoService.encryptToBytes(decBuf, dataEncKey[0]);
|
||||||
|
|
||||||
const fd = new FormData();
|
const fd = new FormData();
|
||||||
try {
|
try {
|
||||||
|
fd.append('key', dataEncKey[1].encryptedString);
|
||||||
const blob = new Blob([encData], { type: 'application/octet-stream' });
|
const blob = new Blob([encData], { type: 'application/octet-stream' });
|
||||||
fd.append('data', blob, encFileName.encryptedString);
|
fd.append('data', blob, encFileName.encryptedString);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (Utils.isNode && !Utils.isBrowser) {
|
if (Utils.isNode && !Utils.isBrowser) {
|
||||||
|
fd.append('key', dataEncKey[1].encryptedString);
|
||||||
fd.append('data', Buffer.from(encData) as any, {
|
fd.append('data', Buffer.from(encData) as any, {
|
||||||
filepath: encFileName.encryptedString,
|
filepath: encFileName.encryptedString,
|
||||||
contentType: 'application/octet-stream',
|
contentType: 'application/octet-stream',
|
||||||
|
|||||||
@@ -333,8 +333,9 @@ export class CryptoService implements CryptoServiceAbstraction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async makeEncKey(key: SymmetricCryptoKey): Promise<[SymmetricCryptoKey, CipherString]> {
|
async makeEncKey(key: SymmetricCryptoKey): Promise<[SymmetricCryptoKey, CipherString]> {
|
||||||
|
const theKey = await this.getKeyForEncryption(key);
|
||||||
const encKey = await this.cryptoFunctionService.randomBytes(64);
|
const encKey = await this.cryptoFunctionService.randomBytes(64);
|
||||||
return this.buildEncKey(key, encKey);
|
return this.buildEncKey(theKey, encKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
async remakeEncKey(key: SymmetricCryptoKey): Promise<[SymmetricCryptoKey, CipherString]> {
|
async remakeEncKey(key: SymmetricCryptoKey): Promise<[SymmetricCryptoKey, CipherString]> {
|
||||||
|
|||||||
Reference in New Issue
Block a user