mirror of
https://github.com/bitwarden/browser
synced 2026-02-05 03:03:26 +00:00
Remove decrypt from encstring
This commit is contained in:
@@ -186,7 +186,7 @@ export class EditCommand {
|
||||
return Response.notFound();
|
||||
}
|
||||
|
||||
let folderView = await folder.decrypt();
|
||||
let folderView = await folder.decrypt(activeUserId);
|
||||
folderView = FolderExport.toView(req, folderView);
|
||||
|
||||
const userKey = await this.keyService.getUserKey(activeUserId);
|
||||
@@ -194,7 +194,7 @@ export class EditCommand {
|
||||
try {
|
||||
const folder = await this.folderApiService.save(encFolder, activeUserId);
|
||||
const updatedFolder = new Folder(folder);
|
||||
const decFolder = await updatedFolder.decrypt();
|
||||
const decFolder = await updatedFolder.decrypt(activeUserId);
|
||||
const res = new FolderResponse(decFolder);
|
||||
return Response.success(res);
|
||||
} catch (e) {
|
||||
|
||||
@@ -420,7 +420,7 @@ export class GetCommand extends DownloadCommand {
|
||||
if (Utils.isGuid(id)) {
|
||||
const folder = await this.folderService.getFromState(id, activeUserId);
|
||||
if (folder != null) {
|
||||
decFolder = await folder.decrypt();
|
||||
decFolder = await folder.decrypt(activeUserId);
|
||||
}
|
||||
} else if (id.trim() !== "") {
|
||||
let folders = await this.folderService.getAllDecryptedFromState(activeUserId);
|
||||
|
||||
@@ -5,6 +5,8 @@ import * as inquirer from "inquirer";
|
||||
import { firstValueFrom } 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 { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
|
||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||
@@ -37,6 +39,7 @@ export class SendReceiveCommand extends DownloadCommand {
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private environmentService: EnvironmentService,
|
||||
private sendApiService: SendApiService,
|
||||
private accountService: AccountService,
|
||||
apiService: ApiService,
|
||||
) {
|
||||
super(encryptService, apiService);
|
||||
@@ -152,6 +155,8 @@ export class SendReceiveCommand extends DownloadCommand {
|
||||
key: Uint8Array,
|
||||
): Promise<Response | SendAccessView> {
|
||||
try {
|
||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||
|
||||
const sendResponse = await this.sendApiService.postSendAccess(
|
||||
id,
|
||||
this.sendAccessRequest,
|
||||
@@ -160,7 +165,7 @@ export class SendReceiveCommand extends DownloadCommand {
|
||||
|
||||
const sendAccess = new SendAccess(sendResponse);
|
||||
this.decKey = await this.keyService.makeSendKey(key);
|
||||
return await sendAccess.decrypt(this.decKey);
|
||||
return await sendAccess.decrypt(activeUserId, this.decKey);
|
||||
} catch (e) {
|
||||
if (e instanceof ErrorResponse) {
|
||||
if (e.statusCode === 401) {
|
||||
|
||||
@@ -123,6 +123,7 @@ export class SendProgram extends BaseProgram {
|
||||
this.serviceContainer.platformUtilsService,
|
||||
this.serviceContainer.environmentService,
|
||||
this.serviceContainer.sendApiService,
|
||||
this.serviceContainer.accountService,
|
||||
this.serviceContainer.apiService,
|
||||
);
|
||||
const response = await cmd.run(url, options);
|
||||
|
||||
@@ -181,12 +181,12 @@ export class CreateCommand {
|
||||
|
||||
private async createFolder(req: FolderExport) {
|
||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||
const userKey = await this.keyService.getUserKey(activeUserId);
|
||||
const userKey = await firstValueFrom(this.keyService.userKey$(activeUserId));
|
||||
const folder = await this.folderService.encrypt(FolderExport.toView(req), userKey);
|
||||
try {
|
||||
const folderData = await this.folderApiService.save(folder, activeUserId);
|
||||
const newFolder = new Folder(folderData);
|
||||
const decFolder = await newFolder.decrypt();
|
||||
const decFolder = await newFolder.decrypt(activeUserId);
|
||||
const res = new FolderResponse(decFolder);
|
||||
return Response.success(res);
|
||||
} catch (e) {
|
||||
|
||||
@@ -264,7 +264,9 @@ export class EmergencyAccessService
|
||||
|
||||
let ciphers: CipherView[] = [];
|
||||
const ciphersEncrypted = response.ciphers.map((c) => new Cipher(c));
|
||||
ciphers = await Promise.all(ciphersEncrypted.map(async (c) => c.decrypt(grantorUserKey)));
|
||||
ciphers = await Promise.all(
|
||||
ciphersEncrypted.map(async (c) => c.decrypt(grantorUserKey, activeUserId)),
|
||||
);
|
||||
return ciphers.sort(this.cipherService.getLocaleSortingFunction());
|
||||
}
|
||||
|
||||
|
||||
@@ -239,6 +239,7 @@ export class CipherReportComponent implements OnDestroy {
|
||||
// convert cipher to cipher view model
|
||||
const updatedCipherView = await updatedCipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher, activeUserId),
|
||||
activeUserId,
|
||||
);
|
||||
|
||||
// request downstream report status if the cipher was updated
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { FormBuilder } from "@angular/forms";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||
import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
|
||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
@@ -64,6 +67,7 @@ export class AccessComponent implements OnInit {
|
||||
private i18nService: I18nService,
|
||||
private layoutWrapperDataService: AnonLayoutWrapperDataService,
|
||||
protected formBuilder: FormBuilder,
|
||||
private accountService: AccountService,
|
||||
) {}
|
||||
|
||||
protected get expirationDate() {
|
||||
@@ -118,7 +122,8 @@ export class AccessComponent implements OnInit {
|
||||
this.passwordRequired = false;
|
||||
const sendAccess = new SendAccess(sendResponse);
|
||||
this.decKey = await this.keyService.makeSendKey(keyArray);
|
||||
this.send = await sendAccess.decrypt(this.decKey);
|
||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||
this.send = await sendAccess.decrypt(activeUserId, this.decKey);
|
||||
} catch (e) {
|
||||
if (e instanceof ErrorResponse) {
|
||||
if (e.statusCode === 401) {
|
||||
|
||||
@@ -498,6 +498,7 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy {
|
||||
|
||||
updatedCipherView = await cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
||||
activeUserId,
|
||||
);
|
||||
} else {
|
||||
const updatedCipher = await this.cipherService.get(
|
||||
|
||||
@@ -27,10 +27,9 @@ export function BuildTestObject<T, K extends keyof T = keyof T>(
|
||||
return Object.assign(constructor === null ? {} : new constructor(), def) as T;
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
export function mockEnc(s: string): MockProxy<EncString> {
|
||||
const mocked = mock<EncString>();
|
||||
mocked.decrypt.mockResolvedValue(s);
|
||||
|
||||
return mocked;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,4 @@
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
|
||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
|
||||
import { makeStaticByteArray } from "../../../../spec";
|
||||
import { EncryptionType } from "../../../platform/enums";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
import { ContainerService } from "../../../platform/services/container.service";
|
||||
import { UserKey, OrgKey } from "../../../types/key";
|
||||
import { EncryptService } from "../abstractions/encrypt.service";
|
||||
|
||||
import { EncString } from "./enc-string";
|
||||
|
||||
@@ -77,41 +66,6 @@ describe("EncString", () => {
|
||||
expect(dataBytes.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("decrypt", () => {
|
||||
const encString = new EncString(EncryptionType.Rsa2048_OaepSha256_B64, "data");
|
||||
|
||||
const keyService = mock<KeyService>();
|
||||
keyService.hasUserKey.mockResolvedValue(true);
|
||||
keyService.getUserKey.mockResolvedValue(
|
||||
new SymmetricCryptoKey(makeStaticByteArray(32)) as UserKey,
|
||||
);
|
||||
|
||||
const encryptService = mock<EncryptService>();
|
||||
encryptService.decryptString
|
||||
.calledWith(encString, expect.anything())
|
||||
.mockResolvedValue("decrypted");
|
||||
|
||||
beforeEach(() => {
|
||||
(window as any).bitwardenContainerService = new ContainerService(
|
||||
keyService,
|
||||
encryptService,
|
||||
);
|
||||
});
|
||||
|
||||
it("decrypts correctly", async () => {
|
||||
const decrypted = await encString.decrypt(null);
|
||||
|
||||
expect(decrypted).toBe("decrypted");
|
||||
});
|
||||
|
||||
it("result should be cached", async () => {
|
||||
const decrypted = await encString.decrypt(null);
|
||||
expect(encryptService.decryptString).toBeCalledTimes(1);
|
||||
|
||||
expect(decrypted).toBe("decrypted");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("AesCbc256_B64", () => {
|
||||
@@ -249,66 +203,6 @@ describe("EncString", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("decrypt", () => {
|
||||
let keyService: MockProxy<KeyService>;
|
||||
let encryptService: MockProxy<EncryptService>;
|
||||
let encString: EncString;
|
||||
|
||||
beforeEach(() => {
|
||||
keyService = mock<KeyService>();
|
||||
encryptService = mock<EncryptService>();
|
||||
encString = new EncString(null);
|
||||
|
||||
(window as any).bitwardenContainerService = new ContainerService(keyService, encryptService);
|
||||
});
|
||||
|
||||
it("handles value it can't decrypt", async () => {
|
||||
encryptService.decryptString.mockRejectedValue("error");
|
||||
|
||||
(window as any).bitwardenContainerService = new ContainerService(keyService, encryptService);
|
||||
|
||||
const decrypted = await encString.decrypt(null);
|
||||
|
||||
expect(decrypted).toBe("[error: cannot decrypt]");
|
||||
|
||||
expect(encString).toEqual({
|
||||
decryptedValue: "[error: cannot decrypt]",
|
||||
encryptedString: null,
|
||||
});
|
||||
});
|
||||
|
||||
it("uses provided key without depending on KeyService", async () => {
|
||||
const key = mock<SymmetricCryptoKey>();
|
||||
|
||||
await encString.decrypt(null, key);
|
||||
|
||||
expect(keyService.getUserKey).not.toHaveBeenCalled();
|
||||
expect(encryptService.decryptString).toHaveBeenCalledWith(encString, key);
|
||||
});
|
||||
|
||||
it("gets an organization key if required", async () => {
|
||||
const orgKey = mock<OrgKey>();
|
||||
|
||||
keyService.getOrgKey.calledWith("orgId").mockResolvedValue(orgKey);
|
||||
|
||||
await encString.decrypt("orgId", null);
|
||||
|
||||
expect(keyService.getOrgKey).toHaveBeenCalledWith("orgId");
|
||||
expect(encryptService.decryptString).toHaveBeenCalledWith(encString, orgKey);
|
||||
});
|
||||
|
||||
it("gets the user's decryption key if required", async () => {
|
||||
const userKey = mock<UserKey>();
|
||||
|
||||
keyService.getUserKey.mockResolvedValue(userKey);
|
||||
|
||||
await encString.decrypt(null, null);
|
||||
|
||||
expect(keyService.getUserKey).toHaveBeenCalledWith();
|
||||
expect(encryptService.decryptString).toHaveBeenCalledWith(encString, userKey);
|
||||
});
|
||||
});
|
||||
|
||||
describe("toJSON", () => {
|
||||
it("Should be represented by the encrypted string", () => {
|
||||
const encString = new EncString(EncryptionType.AesCbc256_B64, "data", "iv");
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { EncString as SdkEncString } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { EncryptionType, EXPECTED_NUM_PARTS_BY_ENCRYPTION_TYPE } from "../../../platform/enums";
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
|
||||
export const DECRYPT_ERROR = "[error: cannot decrypt]";
|
||||
|
||||
@@ -156,46 +155,6 @@ export class EncString {
|
||||
|
||||
return EXPECTED_NUM_PARTS_BY_ENCRYPTION_TYPE[encType] === encPieces.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated - This function is deprecated. Use EncryptService.decryptString instead.
|
||||
* @returns - The decrypted string, or `[error: cannot decrypt]` if decryption fails.
|
||||
*/
|
||||
async decrypt(
|
||||
orgId: string | null,
|
||||
key: SymmetricCryptoKey | null = null,
|
||||
context?: string,
|
||||
): Promise<string> {
|
||||
if (this.decryptedValue != null) {
|
||||
return this.decryptedValue;
|
||||
}
|
||||
|
||||
try {
|
||||
if (key == null) {
|
||||
key = await this.getKeyForDecryption(orgId);
|
||||
}
|
||||
if (key == null) {
|
||||
throw new Error("No key to decrypt EncString with orgId " + orgId);
|
||||
}
|
||||
|
||||
const encryptService = Utils.getContainerService().getEncryptService();
|
||||
this.decryptedValue = await encryptService.decryptString(this, key);
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
"[EncString Generic Decrypt] failed to decrypt encstring. Context: " +
|
||||
(context ?? "No context"),
|
||||
e,
|
||||
);
|
||||
this.decryptedValue = DECRYPT_ERROR;
|
||||
}
|
||||
return this.decryptedValue;
|
||||
}
|
||||
|
||||
private async getKeyForDecryption(orgId: string) {
|
||||
const keyService = Utils.getContainerService().getKeyService();
|
||||
return orgId != null ? await keyService.getOrgKey(orgId) : await keyService.getUserKey();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
|
||||
|
||||
import { InitializerMetadata } from "./initializer-metadata.interface";
|
||||
@@ -8,5 +10,5 @@ import { InitializerMetadata } from "./initializer-metadata.interface";
|
||||
* @example Cipher implements Decryptable<CipherView>
|
||||
*/
|
||||
export interface Decryptable<TDecrypted extends InitializerMetadata> extends InitializerMetadata {
|
||||
decrypt: (key: SymmetricCryptoKey) => Promise<TDecrypted>;
|
||||
decrypt: (key: SymmetricCryptoKey, userId: UserId) => Promise<TDecrypted>;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
import { ConditionalExcept, ConditionalKeys } from "type-fest";
|
||||
|
||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { EncString } from "../../../key-management/crypto/models/enc-string";
|
||||
import { View } from "../../../models/view/view";
|
||||
import { Utils } from "../../misc/utils";
|
||||
|
||||
import { SymmetricCryptoKey } from "./symmetric-crypto-key";
|
||||
|
||||
@@ -69,21 +74,34 @@ export default class Domain {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
protected async decryptObj<D extends Domain, V extends View>(
|
||||
domain: DomainEncryptableKeys<D>,
|
||||
viewModel: ViewEncryptableKeys<V>,
|
||||
props: EncryptableKeys<D, V>[],
|
||||
userId: UserId,
|
||||
orgId: string | null,
|
||||
key: SymmetricCryptoKey | null = null,
|
||||
objectContext: string = "No Domain Context",
|
||||
_objectContext: string = "No Domain Context",
|
||||
): Promise<V> {
|
||||
const keyService = Utils.getContainerService().getKeyService();
|
||||
if (key == null) {
|
||||
if (orgId != null) {
|
||||
key = await firstValueFrom(
|
||||
keyService
|
||||
.orgKeys$(userId)
|
||||
.pipe(map((orgKeys) => orgKeys![orgId as OrganizationId] ?? null)),
|
||||
);
|
||||
} else {
|
||||
key = await firstValueFrom(keyService.userKey$(userId));
|
||||
}
|
||||
}
|
||||
|
||||
const encService = Utils.getContainerService().getEncryptService();
|
||||
for (const prop of props) {
|
||||
viewModel[prop] =
|
||||
(await domain[prop]?.decrypt(
|
||||
orgId,
|
||||
key,
|
||||
`Property: ${prop as string}; ObjectContext: ${objectContext}`,
|
||||
)) ?? null;
|
||||
if (domain[prop] != null) {
|
||||
viewModel[prop] = await encService.decryptString(domain[prop], key!);
|
||||
}
|
||||
}
|
||||
|
||||
return viewModel as V;
|
||||
|
||||
@@ -67,7 +67,7 @@ describe("SendAccess", () => {
|
||||
sendAccess.expirationDate = new Date("2022-01-31T12:00:00.000Z");
|
||||
sendAccess.creatorIdentifier = "creatorIdentifier";
|
||||
|
||||
const view = await sendAccess.decrypt(null);
|
||||
const view = await sendAccess.decrypt(null, null);
|
||||
|
||||
expect(text.decrypt).toHaveBeenCalledTimes(1);
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { EncString } from "../../../../key-management/crypto/models/enc-string";
|
||||
import Domain from "../../../../platform/models/domain/domain-base";
|
||||
import { SymmetricCryptoKey } from "../../../../platform/models/domain/symmetric-crypto-key";
|
||||
@@ -51,17 +53,17 @@ export class SendAccess extends Domain {
|
||||
}
|
||||
}
|
||||
|
||||
async decrypt(key: SymmetricCryptoKey): Promise<SendAccessView> {
|
||||
async decrypt(userId: UserId, key: SymmetricCryptoKey): Promise<SendAccessView> {
|
||||
const model = new SendAccessView(this);
|
||||
|
||||
await this.decryptObj<SendAccess, SendAccessView>(this, model, ["name"], null, key);
|
||||
await this.decryptObj<SendAccess, SendAccessView>(this, model, ["name"], userId, null, key);
|
||||
|
||||
switch (this.type) {
|
||||
case SendType.File:
|
||||
model.file = await this.file.decrypt(key);
|
||||
model.file = await this.file.decrypt(userId, key);
|
||||
break;
|
||||
case SendType.Text:
|
||||
model.text = await this.text.decrypt(key);
|
||||
model.text = await this.text.decrypt(userId, key);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -45,7 +45,7 @@ describe("SendFile", () => {
|
||||
sendFile.sizeName = "1.1 KB";
|
||||
sendFile.fileName = mockEnc("fileName");
|
||||
|
||||
const view = await sendFile.decrypt(null);
|
||||
const view = await sendFile.decrypt(null, null);
|
||||
|
||||
expect(view).toEqual({
|
||||
fileName: "fileName",
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { EncString } from "../../../../key-management/crypto/models/enc-string";
|
||||
import Domain from "../../../../platform/models/domain/domain-base";
|
||||
import { SymmetricCryptoKey } from "../../../../platform/models/domain/symmetric-crypto-key";
|
||||
@@ -33,11 +35,12 @@ export class SendFile extends Domain {
|
||||
);
|
||||
}
|
||||
|
||||
async decrypt(key: SymmetricCryptoKey): Promise<SendFileView> {
|
||||
async decrypt(userId: UserId, key: SymmetricCryptoKey): Promise<SendFileView> {
|
||||
return await this.decryptObj<SendFile, SendFileView>(
|
||||
this,
|
||||
new SendFileView(this),
|
||||
["fileName"],
|
||||
userId,
|
||||
null,
|
||||
key,
|
||||
);
|
||||
|
||||
@@ -37,7 +37,7 @@ describe("SendText", () => {
|
||||
secureNote.text = mockEnc("text");
|
||||
secureNote.hidden = true;
|
||||
|
||||
const view = await secureNote.decrypt(null);
|
||||
const view = await secureNote.decrypt(null, null);
|
||||
|
||||
expect(view).toEqual({
|
||||
text: "text",
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { EncString } from "../../../../key-management/crypto/models/enc-string";
|
||||
import Domain from "../../../../platform/models/domain/domain-base";
|
||||
import { SymmetricCryptoKey } from "../../../../platform/models/domain/symmetric-crypto-key";
|
||||
@@ -29,11 +31,12 @@ export class SendText extends Domain {
|
||||
);
|
||||
}
|
||||
|
||||
decrypt(key: SymmetricCryptoKey): Promise<SendTextView> {
|
||||
decrypt(userId: UserId, key: SymmetricCryptoKey): Promise<SendTextView> {
|
||||
return this.decryptObj<SendText, SendTextView>(
|
||||
this,
|
||||
new SendTextView(this),
|
||||
["text"],
|
||||
userId,
|
||||
null,
|
||||
key,
|
||||
);
|
||||
|
||||
@@ -130,12 +130,6 @@ describe("Send", () => {
|
||||
const view = await send.decrypt(userId);
|
||||
|
||||
expect(text.decrypt).toHaveBeenNthCalledWith(1, "cryptoKey");
|
||||
expect(send.name.decrypt).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
null,
|
||||
"cryptoKey",
|
||||
"Property: name; ObjectContext: No Domain Context",
|
||||
);
|
||||
|
||||
expect(view).toMatchObject({
|
||||
id: "id",
|
||||
|
||||
@@ -89,14 +89,21 @@ export class Send extends Domain {
|
||||
model.key = await encryptService.decryptBytes(this.key, sendKeyEncryptionKey);
|
||||
model.cryptoKey = await keyService.makeSendKey(model.key);
|
||||
|
||||
await this.decryptObj<Send, SendView>(this, model, ["name", "notes"], null, model.cryptoKey);
|
||||
await this.decryptObj<Send, SendView>(
|
||||
this,
|
||||
model,
|
||||
["name", "notes"],
|
||||
userId,
|
||||
null,
|
||||
model.cryptoKey,
|
||||
);
|
||||
|
||||
switch (this.type) {
|
||||
case SendType.File:
|
||||
model.file = await this.file.decrypt(model.cryptoKey);
|
||||
model.file = await this.file.decrypt(userId, model.cryptoKey);
|
||||
break;
|
||||
case SendType.Text:
|
||||
model.text = await this.text.decrypt(model.cryptoKey);
|
||||
model.text = await this.text.decrypt(userId, model.cryptoKey);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -86,7 +86,7 @@ describe("Attachment", () => {
|
||||
new SymmetricCryptoKey(makeStaticByteArray(64)),
|
||||
);
|
||||
|
||||
const view = await attachment.decrypt(null);
|
||||
const view = await attachment.decrypt(null, null);
|
||||
|
||||
expect(view).toEqual({
|
||||
id: "id",
|
||||
@@ -110,7 +110,7 @@ describe("Attachment", () => {
|
||||
it("uses the provided key without depending on KeyService", async () => {
|
||||
const providedKey = mock<SymmetricCryptoKey>();
|
||||
|
||||
await attachment.decrypt(null, "", providedKey);
|
||||
await attachment.decrypt(null, null, "", providedKey);
|
||||
|
||||
expect(keyService.getUserKey).not.toHaveBeenCalled();
|
||||
expect(encryptService.unwrapSymmetricKey).toHaveBeenCalledWith(attachment.key, providedKey);
|
||||
@@ -120,7 +120,7 @@ describe("Attachment", () => {
|
||||
const orgKey = mock<OrgKey>();
|
||||
keyService.getOrgKey.calledWith("orgId").mockResolvedValue(orgKey);
|
||||
|
||||
await attachment.decrypt("orgId", "", null);
|
||||
await attachment.decrypt(null, "orgId", "", null);
|
||||
|
||||
expect(keyService.getOrgKey).toHaveBeenCalledWith("orgId");
|
||||
expect(encryptService.unwrapSymmetricKey).toHaveBeenCalledWith(attachment.key, orgKey);
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Jsonify } from "type-fest";
|
||||
|
||||
import { OrgKey, UserKey } from "@bitwarden/common/types/key";
|
||||
import { Attachment as SdkAttachment } from "@bitwarden/sdk-internal";
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { EncString } from "../../../key-management/crypto/models/enc-string";
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
@@ -34,6 +35,7 @@ export class Attachment extends Domain {
|
||||
}
|
||||
|
||||
async decrypt(
|
||||
userId: UserId,
|
||||
orgId: string | undefined,
|
||||
context = "No Cipher Context",
|
||||
encKey?: SymmetricCryptoKey,
|
||||
@@ -42,6 +44,7 @@ export class Attachment extends Domain {
|
||||
this,
|
||||
new AttachmentView(this),
|
||||
["fileName"],
|
||||
userId,
|
||||
orgId ?? null,
|
||||
encKey,
|
||||
"DomainType: Attachment; " + context,
|
||||
|
||||
@@ -58,7 +58,7 @@ describe("Card", () => {
|
||||
card.expYear = mockEnc("expYear");
|
||||
card.code = mockEnc("code");
|
||||
|
||||
const view = await card.decrypt(null);
|
||||
const view = await card.decrypt(null, null);
|
||||
|
||||
expect(view).toEqual({
|
||||
_brand: "brand",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Card as SdkCard } from "@bitwarden/sdk-internal";
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { EncString } from "../../../key-management/crypto/models/enc-string";
|
||||
import Domain from "../../../platform/models/domain/domain-base";
|
||||
@@ -32,6 +33,7 @@ export class Card extends Domain {
|
||||
}
|
||||
|
||||
async decrypt(
|
||||
userId: UserId,
|
||||
orgId: string | undefined,
|
||||
context = "No Cipher Context",
|
||||
encKey?: SymmetricCryptoKey,
|
||||
@@ -40,6 +42,7 @@ export class Card extends Domain {
|
||||
this,
|
||||
new CardView(),
|
||||
["cardholderName", "brand", "number", "expMonth", "expYear", "code"],
|
||||
userId,
|
||||
orgId ?? null,
|
||||
encKey,
|
||||
"DomainType: Card; " + context,
|
||||
|
||||
@@ -105,6 +105,7 @@ describe("Cipher DTO", () => {
|
||||
|
||||
const cipherView = await cipher.decrypt(
|
||||
await cipherService.getKeyForCipherKeyDecryption(cipher, mockUserId),
|
||||
null,
|
||||
);
|
||||
|
||||
expect(cipherView).toMatchObject({
|
||||
@@ -329,6 +330,7 @@ describe("Cipher DTO", () => {
|
||||
|
||||
const cipherView = await cipher.decrypt(
|
||||
await cipherService.getKeyForCipherKeyDecryption(cipher, mockUserId),
|
||||
null,
|
||||
);
|
||||
|
||||
expect(cipherView).toMatchObject({
|
||||
@@ -457,6 +459,7 @@ describe("Cipher DTO", () => {
|
||||
|
||||
const cipherView = await cipher.decrypt(
|
||||
await cipherService.getKeyForCipherKeyDecryption(cipher, mockUserId),
|
||||
null,
|
||||
);
|
||||
|
||||
expect(cipherView).toMatchObject({
|
||||
@@ -603,6 +606,7 @@ describe("Cipher DTO", () => {
|
||||
|
||||
const cipherView = await cipher.decrypt(
|
||||
await cipherService.getKeyForCipherKeyDecryption(cipher, mockUserId),
|
||||
null,
|
||||
);
|
||||
|
||||
expect(cipherView).toMatchObject({
|
||||
@@ -773,6 +777,7 @@ describe("Cipher DTO", () => {
|
||||
|
||||
const cipherView = await cipher.decrypt(
|
||||
await cipherService.getKeyForCipherKeyDecryption(cipher, mockUserId),
|
||||
null,
|
||||
);
|
||||
|
||||
expect(cipherView).toMatchObject({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Cipher as SdkCipher } from "@bitwarden/sdk-internal";
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { EncString } from "../../../key-management/crypto/models/enc-string";
|
||||
import { asUuid, uuidAsString } from "../../../platform/abstractions/sdk/sdk.service";
|
||||
@@ -123,7 +124,7 @@ export class Cipher extends Domain implements Decryptable<CipherView> {
|
||||
// We are passing the organizationId into the EncString.decrypt() method here, but because the encKey will always be
|
||||
// present and so the organizationId will not be used.
|
||||
// We will refactor the EncString.decrypt() in https://bitwarden.atlassian.net/browse/PM-3762 to remove the dependency on the organizationId.
|
||||
async decrypt(encKey: SymmetricCryptoKey): Promise<CipherView> {
|
||||
async decrypt(encKey: SymmetricCryptoKey, userId: UserId): Promise<CipherView> {
|
||||
const model = new CipherView(this);
|
||||
let bypassValidation = true;
|
||||
|
||||
@@ -145,14 +146,17 @@ export class Cipher extends Domain implements Decryptable<CipherView> {
|
||||
this,
|
||||
model,
|
||||
["name", "notes"],
|
||||
userId,
|
||||
this.organizationId ?? null,
|
||||
encKey,
|
||||
"cipher",
|
||||
);
|
||||
|
||||
switch (this.type) {
|
||||
case CipherType.Login:
|
||||
if (this.login != null) {
|
||||
model.login = await this.login.decrypt(
|
||||
userId,
|
||||
this.organizationId,
|
||||
bypassValidation,
|
||||
`Cipher Id: ${this.id}`,
|
||||
@@ -168,6 +172,7 @@ export class Cipher extends Domain implements Decryptable<CipherView> {
|
||||
case CipherType.Card:
|
||||
if (this.card != null) {
|
||||
model.card = await this.card.decrypt(
|
||||
userId,
|
||||
this.organizationId,
|
||||
`Cipher Id: ${this.id}`,
|
||||
encKey,
|
||||
@@ -177,6 +182,7 @@ export class Cipher extends Domain implements Decryptable<CipherView> {
|
||||
case CipherType.Identity:
|
||||
if (this.identity != null) {
|
||||
model.identity = await this.identity.decrypt(
|
||||
userId,
|
||||
this.organizationId,
|
||||
`Cipher Id: ${this.id}`,
|
||||
encKey,
|
||||
@@ -186,6 +192,7 @@ export class Cipher extends Domain implements Decryptable<CipherView> {
|
||||
case CipherType.SshKey:
|
||||
if (this.sshKey != null) {
|
||||
model.sshKey = await this.sshKey.decrypt(
|
||||
userId,
|
||||
this.organizationId,
|
||||
`Cipher Id: ${this.id}`,
|
||||
encKey,
|
||||
@@ -200,6 +207,7 @@ export class Cipher extends Domain implements Decryptable<CipherView> {
|
||||
const attachments: AttachmentView[] = [];
|
||||
for (const attachment of this.attachments) {
|
||||
const decryptedAttachment = await attachment.decrypt(
|
||||
userId,
|
||||
this.organizationId,
|
||||
`Cipher Id: ${this.id}`,
|
||||
encKey,
|
||||
@@ -212,7 +220,7 @@ export class Cipher extends Domain implements Decryptable<CipherView> {
|
||||
if (this.fields != null && this.fields.length > 0) {
|
||||
const fields: FieldView[] = [];
|
||||
for (const field of this.fields) {
|
||||
const decryptedField = await field.decrypt(this.organizationId, encKey);
|
||||
const decryptedField = await field.decrypt(userId, this.organizationId, encKey);
|
||||
fields.push(decryptedField);
|
||||
}
|
||||
model.fields = fields;
|
||||
@@ -221,7 +229,7 @@ export class Cipher extends Domain implements Decryptable<CipherView> {
|
||||
if (this.passwordHistory != null && this.passwordHistory.length > 0) {
|
||||
const passwordHistory: PasswordHistoryView[] = [];
|
||||
for (const ph of this.passwordHistory) {
|
||||
const decryptedPh = await ph.decrypt(this.organizationId, encKey);
|
||||
const decryptedPh = await ph.decrypt(userId, this.organizationId, encKey);
|
||||
passwordHistory.push(decryptedPh);
|
||||
}
|
||||
model.passwordHistory = passwordHistory;
|
||||
|
||||
@@ -103,7 +103,7 @@ describe("Fido2Credential", () => {
|
||||
credential.discoverable = mockEnc("true");
|
||||
credential.creationDate = mockDate;
|
||||
|
||||
const credentialView = await credential.decrypt(null);
|
||||
const credentialView = await credential.decrypt(null, null);
|
||||
|
||||
expect(credentialView).toEqual({
|
||||
credentialId: "credentialId",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Fido2Credential as SdkFido2Credential } from "@bitwarden/sdk-internal";
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { EncString } from "../../../key-management/crypto/models/enc-string";
|
||||
import Domain from "../../../platform/models/domain/domain-base";
|
||||
@@ -47,6 +48,7 @@ export class Fido2Credential extends Domain {
|
||||
}
|
||||
|
||||
async decrypt(
|
||||
userId: UserId,
|
||||
orgId: string | undefined,
|
||||
encKey?: SymmetricCryptoKey,
|
||||
): Promise<Fido2CredentialView> {
|
||||
@@ -65,6 +67,7 @@ export class Fido2Credential extends Domain {
|
||||
"rpName",
|
||||
"userDisplayName",
|
||||
],
|
||||
userId,
|
||||
orgId ?? null,
|
||||
encKey,
|
||||
);
|
||||
@@ -74,7 +77,7 @@ export class Fido2Credential extends Domain {
|
||||
{
|
||||
counter: string;
|
||||
}
|
||||
>(this, { counter: "" }, ["counter"], orgId ?? null, encKey);
|
||||
>(this, { counter: "" }, ["counter"], userId, orgId ?? null, encKey);
|
||||
// Counter will end up as NaN if this fails
|
||||
view.counter = parseInt(counter);
|
||||
|
||||
@@ -82,6 +85,7 @@ export class Fido2Credential extends Domain {
|
||||
this,
|
||||
{ discoverable: "" },
|
||||
["discoverable"],
|
||||
userId,
|
||||
orgId ?? null,
|
||||
encKey,
|
||||
);
|
||||
|
||||
@@ -58,7 +58,7 @@ describe("Field", () => {
|
||||
field.name = mockEnc("encName");
|
||||
field.value = mockEnc("encValue");
|
||||
|
||||
const view = await field.decrypt(null);
|
||||
const view = await field.decrypt(null, null);
|
||||
|
||||
expect(view).toEqual({
|
||||
type: 0,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Field as SdkField, LinkedIdType as SdkLinkedIdType } from "@bitwarden/sdk-internal";
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { EncString } from "../../../key-management/crypto/models/enc-string";
|
||||
import Domain from "../../../platform/models/domain/domain-base";
|
||||
@@ -28,11 +29,16 @@ export class Field extends Domain {
|
||||
this.value = conditionalEncString(obj.value);
|
||||
}
|
||||
|
||||
decrypt(orgId: string | undefined, encKey?: SymmetricCryptoKey): Promise<FieldView> {
|
||||
decrypt(
|
||||
userId: UserId,
|
||||
orgId: string | undefined,
|
||||
encKey?: SymmetricCryptoKey,
|
||||
): Promise<FieldView> {
|
||||
return this.decryptObj<Field, FieldView>(
|
||||
this,
|
||||
new FieldView(this),
|
||||
["name", "value"],
|
||||
userId,
|
||||
orgId ?? null,
|
||||
encKey,
|
||||
);
|
||||
|
||||
@@ -33,7 +33,7 @@ describe("Folder", () => {
|
||||
folder.name = mockEnc("encName");
|
||||
folder.revisionDate = new Date("2022-01-31T12:00:00.000Z");
|
||||
|
||||
const view = await folder.decrypt();
|
||||
const view = await folder.decrypt(null);
|
||||
|
||||
expect(view).toEqual({
|
||||
id: "id",
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service";
|
||||
import { EncString } from "../../../key-management/crypto/models/enc-string";
|
||||
import Domain from "../../../platform/models/domain/domain-base";
|
||||
@@ -39,8 +41,8 @@ export class Folder extends Domain {
|
||||
this.revisionDate = obj.revisionDate != null ? new Date(obj.revisionDate) : null;
|
||||
}
|
||||
|
||||
decrypt(): Promise<FolderView> {
|
||||
return this.decryptObj<Folder, FolderView>(this, new FolderView(this), ["name"], null);
|
||||
decrypt(userId: UserId): Promise<FolderView> {
|
||||
return this.decryptObj<Folder, FolderView>(this, new FolderView(this), ["name"], userId, null);
|
||||
}
|
||||
|
||||
async decryptWithKey(
|
||||
|
||||
@@ -107,7 +107,7 @@ describe("Identity", () => {
|
||||
identity.passportNumber = mockEnc("mockPassportNumber");
|
||||
identity.licenseNumber = mockEnc("mockLicenseNumber");
|
||||
|
||||
const view = await identity.decrypt(null);
|
||||
const view = await identity.decrypt(null, null);
|
||||
|
||||
expect(view).toEqual({
|
||||
_firstName: "mockFirstName",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Identity as SdkIdentity } from "@bitwarden/sdk-internal";
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { EncString } from "../../../key-management/crypto/models/enc-string";
|
||||
import Domain from "../../../platform/models/domain/domain-base";
|
||||
@@ -56,6 +57,7 @@ export class Identity extends Domain {
|
||||
}
|
||||
|
||||
decrypt(
|
||||
userId: UserId,
|
||||
orgId: string | undefined,
|
||||
context: string = "No Cipher Context",
|
||||
encKey?: SymmetricCryptoKey,
|
||||
@@ -83,6 +85,7 @@ export class Identity extends Domain {
|
||||
"passportNumber",
|
||||
"licenseNumber",
|
||||
],
|
||||
userId,
|
||||
orgId ?? null,
|
||||
encKey,
|
||||
"DomainType: Identity; " + context,
|
||||
|
||||
@@ -53,7 +53,7 @@ describe("LoginUri", () => {
|
||||
loginUri.match = UriMatchStrategy.Exact;
|
||||
loginUri.uri = mockEnc("uri");
|
||||
|
||||
const view = await loginUri.decrypt(null);
|
||||
const view = await loginUri.decrypt(null, null);
|
||||
|
||||
expect(view).toEqual({
|
||||
_uri: "uri",
|
||||
@@ -77,7 +77,7 @@ describe("LoginUri", () => {
|
||||
loginUri.uriChecksum = mockEnc("checksum");
|
||||
encryptService.hash.mockResolvedValue("checksum");
|
||||
|
||||
const actual = await loginUri.validateChecksum("uri", undefined, undefined);
|
||||
const actual = await loginUri.validateChecksum("uri", undefined, undefined, undefined);
|
||||
|
||||
expect(actual).toBe(true);
|
||||
expect(encryptService.hash).toHaveBeenCalledWith("uri", "sha256");
|
||||
@@ -88,7 +88,7 @@ describe("LoginUri", () => {
|
||||
loginUri.uriChecksum = mockEnc("checksum");
|
||||
encryptService.hash.mockResolvedValue("incorrect checksum");
|
||||
|
||||
const actual = await loginUri.validateChecksum("uri", undefined, undefined);
|
||||
const actual = await loginUri.validateChecksum("uri", undefined, undefined, undefined);
|
||||
|
||||
expect(actual).toBe(false);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||
import { LoginUri as SdkLoginUri } from "@bitwarden/sdk-internal";
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { EncString } from "../../../key-management/crypto/models/enc-string";
|
||||
import { UriMatchStrategySetting } from "../../../models/domain/domain-service";
|
||||
@@ -28,6 +33,7 @@ export class LoginUri extends Domain {
|
||||
}
|
||||
|
||||
decrypt(
|
||||
userId: UserId,
|
||||
orgId: string | undefined,
|
||||
context: string = "No Cipher Context",
|
||||
encKey?: SymmetricCryptoKey,
|
||||
@@ -36,21 +42,41 @@ export class LoginUri extends Domain {
|
||||
this,
|
||||
new LoginUriView(this),
|
||||
["uri"],
|
||||
userId,
|
||||
orgId ?? null,
|
||||
encKey,
|
||||
context,
|
||||
);
|
||||
}
|
||||
|
||||
async validateChecksum(clearTextUri: string, orgId?: string, encKey?: SymmetricCryptoKey) {
|
||||
async validateChecksum(
|
||||
clearTextUri: string,
|
||||
userId: UserId,
|
||||
orgId?: string,
|
||||
key?: SymmetricCryptoKey,
|
||||
) {
|
||||
if (this.uriChecksum == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const keyService = Utils.getContainerService().getEncryptService();
|
||||
const localChecksum = await keyService.hash(clearTextUri, "sha256");
|
||||
const keyService = Utils.getContainerService().getKeyService();
|
||||
const encService = Utils.getContainerService().getEncryptService();
|
||||
const localChecksum = await encService.hash(clearTextUri, "sha256");
|
||||
|
||||
const remoteChecksum = await this.uriChecksum.decrypt(orgId ?? null, encKey);
|
||||
if (key == null) {
|
||||
if (orgId != null) {
|
||||
key = await firstValueFrom(
|
||||
keyService
|
||||
.orgKeys$(userId)
|
||||
.pipe(map((orgKeys) => orgKeys[orgId as OrganizationId] ?? null)),
|
||||
);
|
||||
} else {
|
||||
key = await firstValueFrom(keyService.userKey$(userId));
|
||||
}
|
||||
}
|
||||
const remoteChecksum = await encService.decryptString(this.uriChecksum, key);
|
||||
|
||||
/// WARNING: This is not constant time. This should be moved to the SDK
|
||||
return remoteChecksum === localChecksum;
|
||||
}
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ describe("Login DTO", () => {
|
||||
loginUri.validateChecksum.mockResolvedValue(true);
|
||||
login.uris = [loginUri];
|
||||
|
||||
const loginView = await login.decrypt(null, true);
|
||||
const loginView = await login.decrypt(null, null, true);
|
||||
expect(loginView).toEqual(expectedView);
|
||||
});
|
||||
|
||||
@@ -111,7 +111,7 @@ describe("Login DTO", () => {
|
||||
.mockResolvedValueOnce(true);
|
||||
login.uris = [loginUri, loginUri, loginUri];
|
||||
|
||||
const loginView = await login.decrypt(null, false);
|
||||
const loginView = await login.decrypt(null, null, false);
|
||||
expect(loginView).toEqual(expectedView);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Login as SdkLogin } from "@bitwarden/sdk-internal";
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { EncString } from "../../../key-management/crypto/models/enc-string";
|
||||
import Domain from "../../../platform/models/domain/domain-base";
|
||||
@@ -44,6 +45,7 @@ export class Login extends Domain {
|
||||
}
|
||||
|
||||
async decrypt(
|
||||
userId: UserId,
|
||||
orgId: string | undefined,
|
||||
bypassValidation: boolean,
|
||||
context: string = "No Cipher Context",
|
||||
@@ -53,6 +55,7 @@ export class Login extends Domain {
|
||||
this,
|
||||
new LoginView(this),
|
||||
["username", "password", "totp"],
|
||||
userId,
|
||||
orgId ?? null,
|
||||
encKey,
|
||||
`DomainType: Login; ${context}`,
|
||||
@@ -66,7 +69,7 @@ export class Login extends Domain {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uri = await this.uris[i].decrypt(orgId, context, encKey);
|
||||
const uri = await this.uris[i].decrypt(userId, orgId, context, encKey);
|
||||
const uriString = uri.uri;
|
||||
|
||||
if (uriString == null) {
|
||||
@@ -79,7 +82,8 @@ export class Login extends Domain {
|
||||
// So we bypass the validation if there's no cipher.key or proceed with the validation and
|
||||
// Skip the value if it's been tampered with.
|
||||
const isValidUri =
|
||||
bypassValidation || (await this.uris[i].validateChecksum(uriString, orgId, encKey));
|
||||
bypassValidation ||
|
||||
(await this.uris[i].validateChecksum(uriString, userId, orgId, encKey));
|
||||
|
||||
if (isValidUri) {
|
||||
view.uris.push(uri);
|
||||
@@ -89,7 +93,7 @@ export class Login extends Domain {
|
||||
|
||||
if (this.fido2Credentials != null) {
|
||||
view.fido2Credentials = await Promise.all(
|
||||
this.fido2Credentials.map((key) => key.decrypt(orgId, encKey)),
|
||||
this.fido2Credentials.map((key) => key.decrypt(userId, orgId, encKey)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ describe("Password", () => {
|
||||
password.password = mockEnc("password");
|
||||
password.lastUsedDate = new Date("2022-01-31T12:00:00.000Z");
|
||||
|
||||
const view = await password.decrypt(null);
|
||||
const view = await password.decrypt(null, null);
|
||||
|
||||
expect(view).toEqual({
|
||||
password: "password",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { PasswordHistory } from "@bitwarden/sdk-internal";
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { EncString } from "../../../key-management/crypto/models/enc-string";
|
||||
import Domain from "../../../platform/models/domain/domain-base";
|
||||
@@ -22,11 +23,16 @@ export class Password extends Domain {
|
||||
this.lastUsedDate = new Date(obj.lastUsedDate);
|
||||
}
|
||||
|
||||
decrypt(orgId: string | undefined, encKey?: SymmetricCryptoKey): Promise<PasswordHistoryView> {
|
||||
decrypt(
|
||||
userId: UserId,
|
||||
orgId: string | undefined,
|
||||
encKey?: SymmetricCryptoKey,
|
||||
): Promise<PasswordHistoryView> {
|
||||
return this.decryptObj<Password, PasswordHistoryView>(
|
||||
this,
|
||||
new PasswordHistoryView(this),
|
||||
["password"],
|
||||
userId,
|
||||
orgId ?? null,
|
||||
encKey,
|
||||
"DomainType: PasswordHistory",
|
||||
|
||||
@@ -56,7 +56,7 @@ describe("Sshkey", () => {
|
||||
keyFingerprint: "keyFingerprint",
|
||||
};
|
||||
|
||||
const loginView = await sshKey.decrypt(null);
|
||||
const loginView = await sshKey.decrypt(null, null);
|
||||
expect(loginView).toEqual(expectedView);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { SshKey as SdkSshKey } from "@bitwarden/sdk-internal";
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { EncString } from "../../../key-management/crypto/models/enc-string";
|
||||
import Domain from "../../../platform/models/domain/domain-base";
|
||||
@@ -25,6 +26,7 @@ export class SshKey extends Domain {
|
||||
}
|
||||
|
||||
decrypt(
|
||||
userId: UserId,
|
||||
orgId: string | undefined,
|
||||
context = "No Cipher Context",
|
||||
encKey?: SymmetricCryptoKey,
|
||||
@@ -33,6 +35,7 @@ export class SshKey extends Domain {
|
||||
this,
|
||||
new SshKeyView(),
|
||||
["privateKey", "publicKey", "keyFingerprint"],
|
||||
userId,
|
||||
orgId ?? null,
|
||||
encKey,
|
||||
"DomainType: SshKey; " + context,
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from "rxjs";
|
||||
import { SemVer } from "semver";
|
||||
|
||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessageSender } from "@bitwarden/common/platform/messaging";
|
||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||
@@ -520,7 +521,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
const key = keys.orgKeys[orgId as OrganizationId] ?? keys.userKey;
|
||||
return await Promise.all(
|
||||
groupedCiphers.map(async (cipher) => {
|
||||
return await cipher.decrypt(key);
|
||||
return await cipher.decrypt(key, userId);
|
||||
}),
|
||||
);
|
||||
}),
|
||||
@@ -558,7 +559,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
return await this.cipherEncryptionService.decrypt(cipher, userId);
|
||||
} else {
|
||||
const encKey = await this.getKeyForCipherKeyDecryption(cipher, userId);
|
||||
return await cipher.decrypt(encKey);
|
||||
return await cipher.decrypt(encKey, userId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -718,6 +719,8 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
response: ListResponse<CipherResponse>,
|
||||
organizationId: string,
|
||||
): Promise<CipherView[]> {
|
||||
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||
|
||||
if (response?.data == null || response.data.length < 1) {
|
||||
return [];
|
||||
}
|
||||
@@ -726,7 +729,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
const key = await this.keyService.getOrgKey(organizationId);
|
||||
const decCiphers: CipherView[] = await Promise.all(
|
||||
ciphers.map(async (cipher) => {
|
||||
return await cipher.decrypt(key);
|
||||
return await cipher.decrypt(key, userId);
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { concatMap, firstValueFrom, map } from "rxjs";
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { Collection, CollectionView } from "@bitwarden/admin-console/common";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
||||
import {
|
||||
@@ -15,7 +16,7 @@ import {
|
||||
} from "@bitwarden/common/models/export";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
@@ -167,6 +168,8 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
|
||||
private async parseFolders(
|
||||
data: BitwardenUnEncryptedIndividualJsonExport | BitwardenEncryptedIndividualJsonExport,
|
||||
): Promise<Map<string, number>> | null {
|
||||
const userId: UserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||
|
||||
if (data.folders == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -178,7 +181,7 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
|
||||
if (data.encrypted) {
|
||||
const folder = FolderWithIdExport.toDomain(f);
|
||||
if (folder != null) {
|
||||
folderView = await folder.decrypt();
|
||||
folderView = await folder.decrypt(userId);
|
||||
}
|
||||
} else {
|
||||
folderView = FolderWithIdExport.toView(f);
|
||||
|
||||
@@ -175,7 +175,7 @@ export class DefaultUserAsymmetricKeysRegenerationService
|
||||
}
|
||||
|
||||
try {
|
||||
const cipherView = await cipher.decrypt(userKey);
|
||||
const cipherView = await cipher.decrypt(userKey, userId);
|
||||
|
||||
if (cipherView.decryptionFailure) {
|
||||
this.logService.error(
|
||||
|
||||
Reference in New Issue
Block a user