1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-11 14:04:03 +00:00

decrypt attachment content using sdk

This commit is contained in:
gbubemismith
2025-04-18 19:05:02 -04:00
parent 97e1c4ad94
commit d48aa21bd6
11 changed files with 360 additions and 30 deletions

View File

@@ -1,21 +1,25 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core";
import { firstValueFrom } from "rxjs";
import { firstValueFrom, map } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer";
import { UserId } from "@bitwarden/common/types/guid";
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
import { OrgKey } from "@bitwarden/common/types/key";
import { CipherEncryptionService } from "@bitwarden/common/vault/abstractions/cipher-encryption.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
@@ -56,6 +60,8 @@ export class AttachmentsComponent implements OnInit {
protected billingAccountProfileStateService: BillingAccountProfileStateService,
protected accountService: AccountService,
protected toastService: ToastService,
protected configService: ConfigService,
protected cipherEncryptionService: CipherEncryptionService,
) {}
async ngOnInit() {
@@ -193,12 +199,9 @@ export class AttachmentsComponent implements OnInit {
}
try {
const encBuf = await EncArrayBuffer.fromResponse(response);
const key =
attachment.key != null
? attachment.key
: await this.keyService.getOrgKey(this.cipher.organizationId);
const decBuf = await this.encryptService.decryptToBytes(encBuf, key);
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const decBuf = await this.getDecryptedBuffer(response, attachment, activeUserId);
this.fileDownloadService.download({
fileName: attachment.fileName,
blobData: decBuf,
@@ -270,15 +273,11 @@ export class AttachmentsComponent implements OnInit {
try {
// 2. Resave
const encBuf = await EncArrayBuffer.fromResponse(response);
const key =
attachment.key != null
? attachment.key
: await this.keyService.getOrgKey(this.cipher.organizationId);
const decBuf = await this.encryptService.decryptToBytes(encBuf, key);
const activeUserId = await firstValueFrom(
this.accountService.activeAccount$.pipe(getUserId),
);
const decBuf = await this.getDecryptedBuffer(response, attachment, activeUserId);
this.cipherDomain = await this.cipherService.saveAttachmentRawWithServer(
this.cipherDomain,
attachment.fileName,
@@ -341,4 +340,39 @@ export class AttachmentsComponent implements OnInit {
protected async reupload(attachment: AttachmentView) {
// TODO: This should be removed but is needed since we re-use the same template
}
private async getDecryptedBuffer(
response: Response,
attachment: AttachmentView,
userId: UserId,
): Promise<Uint8Array> {
const useSdkDecryption = await this.configService.getFeatureFlag(
FeatureFlag.PM19941MigrateCipherDomainToSdk,
);
if (useSdkDecryption) {
const attachmentDomain = this.cipherDomain.attachments?.find((a) => a.id === attachment.id);
const encArrayBuf = new Uint8Array(await response.arrayBuffer());
return await this.cipherEncryptionService.decryptAttachmentContent(
this.cipherDomain,
attachmentDomain,
encArrayBuf,
userId,
);
}
const encBuf = await EncArrayBuffer.fromResponse(response);
const key =
attachment.key != null
? attachment.key
: await firstValueFrom(
this.keyService
.orgKeys$(userId)
.pipe(
map((orgKeys) => orgKeys[this.cipher.organizationId as OrganizationId] as OrgKey),
),
);
return await this.encryptService.decryptToBytes(encBuf, key);
}
}

View File

@@ -31,22 +31,27 @@ import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { EventType } from "@bitwarden/common/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer";
import { CollectionId, UserId } from "@bitwarden/common/types/guid";
import { CipherId, CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid";
import { OrgKey } from "@bitwarden/common/types/key";
import { CipherEncryptionService } from "@bitwarden/common/vault/abstractions/cipher-encryption.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
import { CipherType, FieldType } from "@bitwarden/common/vault/enums";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
import { Launchable } from "@bitwarden/common/vault/interfaces/launchable";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import { AttachmentView } from "@bitwarden/common/vault/models/view/attachment.view";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
@@ -137,6 +142,8 @@ export class ViewComponent implements OnDestroy, OnInit {
private billingAccountProfileStateService: BillingAccountProfileStateService,
protected toastService: ToastService,
private cipherAuthorizationService: CipherAuthorizationService,
private configService: ConfigService,
private cipherEncryptionService: CipherEncryptionService,
) {}
ngOnInit() {
@@ -458,12 +465,8 @@ export class ViewComponent implements OnDestroy, OnInit {
}
try {
const encBuf = await EncArrayBuffer.fromResponse(response);
const key =
attachment.key != null
? attachment.key
: await this.keyService.getOrgKey(this.cipher.organizationId);
const decBuf = await this.encryptService.decryptToBytes(encBuf, key);
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const decBuf = await this.getDecryptedBuffer(response, attachment, activeUserId);
this.fileDownloadService.download({
fileName: attachment.fileName,
blobData: decBuf,
@@ -564,4 +567,41 @@ export class ViewComponent implements OnDestroy, OnInit {
}
this.previousCipherId = this.cipherId;
}
private async getDecryptedBuffer(
response: Response,
attachment: AttachmentView,
userId: UserId,
): Promise<Uint8Array> {
const useSdkDecryption = await this.configService.getFeatureFlag(
FeatureFlag.PM19941MigrateCipherDomainToSdk,
);
if (useSdkDecryption) {
const ciphersData = await firstValueFrom(this.cipherService.ciphers$(userId));
const cipherDomain = new Cipher(ciphersData[this.cipher.id as CipherId]);
const attachmentDomain = cipherDomain.attachments?.find((a) => a.id === attachment.id);
const encArrayBuf = new Uint8Array(await response.arrayBuffer());
return await this.cipherEncryptionService.decryptAttachmentContent(
cipherDomain,
attachmentDomain,
encArrayBuf,
userId,
);
}
const encBuf = await EncArrayBuffer.fromResponse(response);
const key =
attachment.key != null
? attachment.key
: await firstValueFrom(
this.keyService
.orgKeys$(userId)
.pipe(
map((orgKeys) => orgKeys[this.cipher.organizationId as OrganizationId] as OrgKey),
),
);
return await this.encryptService.decryptToBytes(encBuf, key);
}
}