mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
[PM-21001] Move platform code to new encrypt service interface (#14544)
* Move platform code to new encrypt service interface * Fix tests * Fix tests * Fix cli build
This commit is contained in:
@@ -46,22 +46,18 @@ describe("LocalBackedSessionStorage", () => {
|
||||
it("returns a decrypted value when one is stored in local storage", async () => {
|
||||
const encrypted = makeEncString("encrypted");
|
||||
localStorage.internalStore["session_test"] = encrypted.encryptedString;
|
||||
encryptService.decryptToUtf8.mockResolvedValue(JSON.stringify("decrypted"));
|
||||
encryptService.decryptString.mockResolvedValue(JSON.stringify("decrypted"));
|
||||
const result = await sut.get("test");
|
||||
// FIXME: Remove when updating file. Eslint update
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
expect(encryptService.decryptToUtf8).toHaveBeenCalledWith(
|
||||
encrypted,
|
||||
sessionKey,
|
||||
"browser-session-key",
|
||||
),
|
||||
expect(encryptService.decryptString).toHaveBeenCalledWith(encrypted, sessionKey),
|
||||
expect(result).toEqual("decrypted");
|
||||
});
|
||||
|
||||
it("caches the decrypted value when one is stored in local storage", async () => {
|
||||
const encrypted = makeEncString("encrypted");
|
||||
localStorage.internalStore["session_test"] = encrypted.encryptedString;
|
||||
encryptService.decryptToUtf8.mockResolvedValue(JSON.stringify("decrypted"));
|
||||
encryptService.decryptString.mockResolvedValue(JSON.stringify("decrypted"));
|
||||
await sut.get("test");
|
||||
expect(sut["cache"]["test"]).toEqual("decrypted");
|
||||
});
|
||||
@@ -69,22 +65,18 @@ describe("LocalBackedSessionStorage", () => {
|
||||
it("returns a decrypted value when one is stored in local storage", async () => {
|
||||
const encrypted = makeEncString("encrypted");
|
||||
localStorage.internalStore["session_test"] = encrypted.encryptedString;
|
||||
encryptService.decryptToUtf8.mockResolvedValue(JSON.stringify("decrypted"));
|
||||
encryptService.decryptString.mockResolvedValue(JSON.stringify("decrypted"));
|
||||
const result = await sut.get("test");
|
||||
// FIXME: Remove when updating file. Eslint update
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
expect(encryptService.decryptToUtf8).toHaveBeenCalledWith(
|
||||
encrypted,
|
||||
sessionKey,
|
||||
"browser-session-key",
|
||||
),
|
||||
expect(encryptService.decryptString).toHaveBeenCalledWith(encrypted, sessionKey),
|
||||
expect(result).toEqual("decrypted");
|
||||
});
|
||||
|
||||
it("caches the decrypted value when one is stored in local storage", async () => {
|
||||
const encrypted = makeEncString("encrypted");
|
||||
localStorage.internalStore["session_test"] = encrypted.encryptedString;
|
||||
encryptService.decryptToUtf8.mockResolvedValue(JSON.stringify("decrypted"));
|
||||
encryptService.decryptString.mockResolvedValue(JSON.stringify("decrypted"));
|
||||
await sut.get("test");
|
||||
expect(sut["cache"]["test"]).toEqual("decrypted");
|
||||
});
|
||||
@@ -104,7 +96,7 @@ describe("LocalBackedSessionStorage", () => {
|
||||
|
||||
it("returns true when the key is in local storage", async () => {
|
||||
localStorage.internalStore["session_test"] = makeEncString("encrypted").encryptedString;
|
||||
encryptService.decryptToUtf8.mockResolvedValue(JSON.stringify("decrypted"));
|
||||
encryptService.decryptString.mockResolvedValue(JSON.stringify("decrypted"));
|
||||
const result = await sut.has("test");
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
@@ -119,7 +111,7 @@ describe("LocalBackedSessionStorage", () => {
|
||||
async (nullish) => {
|
||||
localStorage.internalStore["session_test"] = nullish;
|
||||
await expect(sut.has("test")).resolves.toBe(false);
|
||||
expect(encryptService.decryptToUtf8).not.toHaveBeenCalled();
|
||||
expect(encryptService.decryptString).not.toHaveBeenCalled();
|
||||
},
|
||||
);
|
||||
});
|
||||
@@ -127,7 +119,7 @@ describe("LocalBackedSessionStorage", () => {
|
||||
describe("save", () => {
|
||||
const encString = makeEncString("encrypted");
|
||||
beforeEach(() => {
|
||||
encryptService.encrypt.mockResolvedValue(encString);
|
||||
encryptService.encryptString.mockResolvedValue(encString);
|
||||
});
|
||||
|
||||
it("logs a warning when saving the same value twice and in a dev environment", async () => {
|
||||
@@ -157,7 +149,10 @@ describe("LocalBackedSessionStorage", () => {
|
||||
|
||||
it("encrypts and saves the value to local storage", async () => {
|
||||
await sut.save("test", "value");
|
||||
expect(encryptService.encrypt).toHaveBeenCalledWith(JSON.stringify("value"), sessionKey);
|
||||
expect(encryptService.encryptString).toHaveBeenCalledWith(
|
||||
JSON.stringify("value"),
|
||||
sessionKey,
|
||||
);
|
||||
expect(localStorage.internalStore["session_test"]).toEqual(encString.encryptedString);
|
||||
});
|
||||
|
||||
|
||||
@@ -118,11 +118,7 @@ export class LocalBackedSessionStorageService
|
||||
return null;
|
||||
}
|
||||
|
||||
const valueJson = await this.encryptService.decryptToUtf8(
|
||||
new EncString(local),
|
||||
encKey,
|
||||
"browser-session-key",
|
||||
);
|
||||
const valueJson = await this.encryptService.decryptString(new EncString(local), encKey);
|
||||
if (valueJson == null) {
|
||||
// error with decryption, value is lost, delete state and start over
|
||||
await this.localStorage.remove(this.sessionStorageKey(key));
|
||||
@@ -139,7 +135,10 @@ export class LocalBackedSessionStorageService
|
||||
}
|
||||
|
||||
const valueJson = JSON.stringify(value);
|
||||
const encValue = await this.encryptService.encrypt(valueJson, await this.sessionKey.get());
|
||||
const encValue = await this.encryptService.encryptString(
|
||||
valueJson,
|
||||
await this.sessionKey.get(),
|
||||
);
|
||||
await this.localStorage.save(this.sessionStorageKey(key), encValue.encryptedString);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ export abstract class DownloadCommand {
|
||||
|
||||
try {
|
||||
const encBuf = await EncArrayBuffer.fromResponse(response);
|
||||
const decBuf = await this.encryptService.decryptToBytes(encBuf, key);
|
||||
const decBuf = await this.encryptService.decryptFileData(encBuf, key);
|
||||
if (process.env.BW_SERVE === "true") {
|
||||
const res = new FileResponse(Buffer.from(decBuf), fileName);
|
||||
return Response.success(res);
|
||||
|
||||
@@ -195,7 +195,7 @@ export class EditCommand {
|
||||
(u) => new SelectionReadOnlyRequest(u.id, u.readOnly, u.hidePasswords, u.manage),
|
||||
);
|
||||
const request = new CollectionRequest();
|
||||
request.name = (await this.encryptService.encrypt(req.name, orgKey)).encryptedString;
|
||||
request.name = (await this.encryptService.encryptString(req.name, orgKey)).encryptedString;
|
||||
request.externalId = req.externalId;
|
||||
request.groups = groups;
|
||||
request.users = users;
|
||||
|
||||
@@ -453,10 +453,9 @@ export class GetCommand extends DownloadCommand {
|
||||
|
||||
const response = await this.apiService.getCollectionAccessDetails(options.organizationId, id);
|
||||
const decCollection = new CollectionView(response);
|
||||
decCollection.name = await this.encryptService.decryptToUtf8(
|
||||
decCollection.name = await this.encryptService.decryptString(
|
||||
new EncString(response.name),
|
||||
orgKey,
|
||||
`orgkey-${options.organizationId}`,
|
||||
);
|
||||
const groups =
|
||||
response.groups == null
|
||||
|
||||
@@ -61,7 +61,7 @@ export class NodeEnvSecureStorageService implements AbstractStorageService {
|
||||
if (sessionKey == null) {
|
||||
throw new Error("No session key available.");
|
||||
}
|
||||
const encValue = await this.encryptService.encryptToBytes(
|
||||
const encValue = await this.encryptService.encryptFileData(
|
||||
Utils.fromB64ToArray(plainValue),
|
||||
sessionKey,
|
||||
);
|
||||
@@ -80,7 +80,7 @@ export class NodeEnvSecureStorageService implements AbstractStorageService {
|
||||
}
|
||||
|
||||
const encBuf = EncArrayBuffer.fromB64(encValue);
|
||||
const decValue = await this.encryptService.decryptToBytes(encBuf, sessionKey);
|
||||
const decValue = await this.encryptService.decryptFileData(encBuf, sessionKey);
|
||||
if (decValue == null) {
|
||||
this.logService.info("Failed to decrypt.");
|
||||
return null;
|
||||
|
||||
@@ -110,7 +110,7 @@ export class ElectronKeyService extends DefaultKeyService {
|
||||
// Set a key half if it doesn't exist
|
||||
const keyBytes = await this.cryptoFunctionService.randomBytes(32);
|
||||
clientKeyHalf = Utils.fromBufferToUtf8(keyBytes) as CsprngString;
|
||||
const encKey = await this.encryptService.encrypt(clientKeyHalf, userKey);
|
||||
const encKey = await this.encryptService.encryptString(clientKeyHalf, userKey);
|
||||
await this.biometricStateService.setEncryptedClientKeyHalf(encKey, userId);
|
||||
}
|
||||
|
||||
|
||||
@@ -123,9 +123,6 @@ const mockCryptoService = () => {
|
||||
encryptService.decryptString
|
||||
.calledWith(expect.any(EncString), expect.anything())
|
||||
.mockResolvedValue("DECRYPTED_STRING");
|
||||
encryptService.decryptToUtf8
|
||||
.calledWith(expect.any(EncString), expect.anything(), expect.anything())
|
||||
.mockResolvedValue("DECRYPTED_STRING");
|
||||
|
||||
(window as any).bitwardenContainerService = new ContainerService(keyService, encryptService);
|
||||
|
||||
|
||||
@@ -51,11 +51,6 @@ describe("DefaultvNextCollectionService", () => {
|
||||
.mockImplementation((encString, key) =>
|
||||
Promise.resolve(encString.data.replace("ENC_", "DEC_")),
|
||||
);
|
||||
encryptService.decryptToUtf8
|
||||
.calledWith(expect.any(EncString), expect.any(SymmetricCryptoKey), expect.any(String))
|
||||
.mockImplementation((encString, key) =>
|
||||
Promise.resolve(encString.data.replace("ENC_", "DEC_")),
|
||||
);
|
||||
|
||||
(window as any).bitwardenContainerService = new ContainerService(keyService, encryptService);
|
||||
|
||||
@@ -109,15 +104,13 @@ describe("DefaultvNextCollectionService", () => {
|
||||
|
||||
// Assert that the correct org keys were used for each encrypted string
|
||||
// This should be replaced with decryptString when the platform PR (https://github.com/bitwarden/clients/pull/14544) is merged
|
||||
expect(encryptService.decryptToUtf8).toHaveBeenCalledWith(
|
||||
expect(encryptService.decryptString).toHaveBeenCalledWith(
|
||||
expect.objectContaining(new EncString(collection1.name)),
|
||||
orgKey1,
|
||||
expect.any(String),
|
||||
);
|
||||
expect(encryptService.decryptToUtf8).toHaveBeenCalledWith(
|
||||
expect(encryptService.decryptString).toHaveBeenCalledWith(
|
||||
expect.objectContaining(new EncString(collection2.name)),
|
||||
orgKey2,
|
||||
expect.any(String),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import { mock, MockProxy } from "jest-mock-extended";
|
||||
|
||||
import { makeEncString, makeSymmetricCryptoKey } from "../../../../spec";
|
||||
import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service";
|
||||
import { Utils } from "../../misc/utils";
|
||||
|
||||
import Domain from "./domain-base";
|
||||
import { EncString } from "./enc-string";
|
||||
@@ -22,24 +21,13 @@ describe("DomainBase", () => {
|
||||
});
|
||||
|
||||
function setUpCryptography() {
|
||||
encryptService.encrypt.mockImplementation((value) => {
|
||||
let data: string;
|
||||
if (typeof value === "string") {
|
||||
data = value;
|
||||
} else {
|
||||
data = Utils.fromBufferToUtf8(value);
|
||||
}
|
||||
encryptService.encryptString.mockImplementation((value) =>
|
||||
Promise.resolve(makeEncString(value)),
|
||||
);
|
||||
|
||||
return Promise.resolve(makeEncString(data));
|
||||
});
|
||||
|
||||
encryptService.decryptToUtf8.mockImplementation((value) => {
|
||||
encryptService.decryptString.mockImplementation((value) => {
|
||||
return Promise.resolve(value.data);
|
||||
});
|
||||
|
||||
encryptService.decryptToBytes.mockImplementation((value) => {
|
||||
return Promise.resolve(value.dataBytes);
|
||||
});
|
||||
}
|
||||
|
||||
describe("decryptWithKey", () => {
|
||||
@@ -82,7 +70,7 @@ describe("DomainBase", () => {
|
||||
|
||||
const domain = new TestDomain();
|
||||
|
||||
domain.encToString = await encryptService.encrypt("string", key);
|
||||
domain.encToString = await encryptService.encryptString("string", key);
|
||||
|
||||
const decrypted = await domain["decryptObjWithKey"](["encToString"], key, encryptService);
|
||||
|
||||
@@ -96,8 +84,8 @@ describe("DomainBase", () => {
|
||||
|
||||
const domain = new TestDomain();
|
||||
|
||||
domain.encToString = await encryptService.encrypt("string", key);
|
||||
domain.encString2 = await encryptService.encrypt("string2", key);
|
||||
domain.encToString = await encryptService.encryptString("string", key);
|
||||
domain.encString2 = await encryptService.encryptString("string2", key);
|
||||
|
||||
const decrypted = await domain["decryptObjWithKey"](
|
||||
["encToString", "encString2"],
|
||||
|
||||
@@ -7,7 +7,6 @@ import { EncryptService } from "../../../key-management/crypto/abstractions/encr
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
import { UserKey, OrgKey } from "../../../types/key";
|
||||
import { EncryptionType } from "../../enums";
|
||||
import { Utils } from "../../misc/utils";
|
||||
import { ContainerService } from "../../services/container.service";
|
||||
|
||||
import { EncString } from "./enc-string";
|
||||
@@ -87,7 +86,7 @@ describe("EncString", () => {
|
||||
);
|
||||
|
||||
const encryptService = mock<EncryptService>();
|
||||
encryptService.decryptToUtf8
|
||||
encryptService.decryptString
|
||||
.calledWith(encString, expect.anything())
|
||||
.mockResolvedValue("decrypted");
|
||||
|
||||
@@ -106,7 +105,7 @@ describe("EncString", () => {
|
||||
|
||||
it("result should be cached", async () => {
|
||||
const decrypted = await encString.decrypt(null);
|
||||
expect(encryptService.decryptToUtf8).toBeCalledTimes(1);
|
||||
expect(encryptService.decryptString).toBeCalledTimes(1);
|
||||
|
||||
expect(decrypted).toBe("decrypted");
|
||||
});
|
||||
@@ -118,24 +117,17 @@ describe("EncString", () => {
|
||||
|
||||
const keyService = mock<KeyService>();
|
||||
const encryptService = mock<EncryptService>();
|
||||
encryptService.decryptToUtf8
|
||||
encryptService.decryptString
|
||||
.calledWith(encString, expect.anything())
|
||||
.mockResolvedValue("decrypted");
|
||||
|
||||
function setupEncryption() {
|
||||
encryptService.encrypt.mockImplementation(async (data, key) => {
|
||||
if (typeof data === "string") {
|
||||
return makeEncString(data);
|
||||
} else {
|
||||
return makeEncString(Utils.fromBufferToUtf8(data));
|
||||
}
|
||||
encryptService.encryptString.mockImplementation(async (data, key) => {
|
||||
return makeEncString(data);
|
||||
});
|
||||
encryptService.decryptToUtf8.mockImplementation(async (encString, key) => {
|
||||
encryptService.decryptString.mockImplementation(async (encString, key) => {
|
||||
return encString.data;
|
||||
});
|
||||
encryptService.decryptToBytes.mockImplementation(async (encString, key) => {
|
||||
return encString.dataBytes;
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -148,7 +140,7 @@ describe("EncString", () => {
|
||||
const key = new SymmetricCryptoKey(makeStaticByteArray(32));
|
||||
await encString.decryptWithKey(key, encryptService);
|
||||
|
||||
expect(encryptService.decryptToUtf8).toHaveBeenCalledWith(encString, key, "domain-withkey");
|
||||
expect(encryptService.decryptString).toHaveBeenCalledWith(encString, key);
|
||||
});
|
||||
|
||||
it("fails to decrypt when key is null", async () => {
|
||||
@@ -169,7 +161,7 @@ describe("EncString", () => {
|
||||
});
|
||||
|
||||
it("fails to decrypt when encryptService throws", async () => {
|
||||
encryptService.decryptToUtf8.mockRejectedValue("error");
|
||||
encryptService.decryptString.mockRejectedValue("error");
|
||||
|
||||
const decrypted = await encString.decryptWithKey(
|
||||
new SymmetricCryptoKey(makeStaticByteArray(32)),
|
||||
@@ -330,7 +322,7 @@ describe("EncString", () => {
|
||||
});
|
||||
|
||||
it("handles value it can't decrypt", async () => {
|
||||
encryptService.decryptToUtf8.mockRejectedValue("error");
|
||||
encryptService.decryptString.mockRejectedValue("error");
|
||||
|
||||
(window as any).bitwardenContainerService = new ContainerService(keyService, encryptService);
|
||||
|
||||
@@ -350,7 +342,7 @@ describe("EncString", () => {
|
||||
await encString.decrypt(null, key);
|
||||
|
||||
expect(keyService.getUserKeyWithLegacySupport).not.toHaveBeenCalled();
|
||||
expect(encryptService.decryptToUtf8).toHaveBeenCalledWith(encString, key, "provided-key");
|
||||
expect(encryptService.decryptString).toHaveBeenCalledWith(encString, key);
|
||||
});
|
||||
|
||||
it("gets an organization key if required", async () => {
|
||||
@@ -361,11 +353,7 @@ describe("EncString", () => {
|
||||
await encString.decrypt("orgId", null);
|
||||
|
||||
expect(keyService.getOrgKey).toHaveBeenCalledWith("orgId");
|
||||
expect(encryptService.decryptToUtf8).toHaveBeenCalledWith(
|
||||
encString,
|
||||
orgKey,
|
||||
"domain-orgkey-orgId",
|
||||
);
|
||||
expect(encryptService.decryptString).toHaveBeenCalledWith(encString, orgKey);
|
||||
});
|
||||
|
||||
it("gets the user's decryption key if required", async () => {
|
||||
@@ -376,11 +364,7 @@ describe("EncString", () => {
|
||||
await encString.decrypt(null, null);
|
||||
|
||||
expect(keyService.getUserKeyWithLegacySupport).toHaveBeenCalledWith();
|
||||
expect(encryptService.decryptToUtf8).toHaveBeenCalledWith(
|
||||
encString,
|
||||
userKey,
|
||||
"domain-withlegacysupport-masterkey",
|
||||
);
|
||||
expect(encryptService.decryptString).toHaveBeenCalledWith(encString, userKey);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -163,31 +163,16 @@ export class EncString implements Encrypted {
|
||||
return this.decryptedValue;
|
||||
}
|
||||
|
||||
let decryptTrace = "provided-key";
|
||||
try {
|
||||
if (key == null) {
|
||||
key = await this.getKeyForDecryption(orgId);
|
||||
decryptTrace = orgId == null ? `domain-orgkey-${orgId}` : "domain-userkey|masterkey";
|
||||
if (orgId != null) {
|
||||
decryptTrace = `domain-orgkey-${orgId}`;
|
||||
} else {
|
||||
const cryptoService = Utils.getContainerService().getKeyService();
|
||||
decryptTrace =
|
||||
(await cryptoService.getUserKey()) == null
|
||||
? "domain-withlegacysupport-masterkey"
|
||||
: "domain-withlegacysupport-userkey";
|
||||
}
|
||||
}
|
||||
if (key == null) {
|
||||
throw new Error("No key to decrypt EncString with orgId " + orgId);
|
||||
}
|
||||
|
||||
const encryptService = Utils.getContainerService().getEncryptService();
|
||||
this.decryptedValue = await encryptService.decryptToUtf8(
|
||||
this,
|
||||
key,
|
||||
decryptTrace == null ? context : `${decryptTrace}${context || ""}`,
|
||||
);
|
||||
this.decryptedValue = await encryptService.decryptString(this, key);
|
||||
// FIXME: Remove when updating file. Eslint update
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (e) {
|
||||
@@ -206,7 +191,7 @@ export class EncString implements Encrypted {
|
||||
throw new Error("No key to decrypt EncString");
|
||||
}
|
||||
|
||||
this.decryptedValue = await encryptService.decryptToUtf8(this, key, decryptTrace);
|
||||
this.decryptedValue = await encryptService.decryptString(this, key);
|
||||
// FIXME: Remove when updating file. Eslint update
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user