1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-20 18:23:31 +00:00

[PM-17666] Move Encstring to KM ownership (#15457)

* Move Encstring to KM ownership

* Fix wrong import

* Fix build

* Fix remaining imports

* Fix tests
This commit is contained in:
Bernd Schoolmann
2025-07-16 17:15:24 +02:00
committed by GitHub
parent ee598288d4
commit 60855c734f
184 changed files with 233 additions and 192 deletions

View File

@@ -3,8 +3,8 @@ import { Decryptable } from "../../../platform/interfaces/decryptable.interface"
import { Encrypted } from "../../../platform/interfaces/encrypted";
import { InitializerMetadata } from "../../../platform/interfaces/initializer-metadata.interface";
import { EncArrayBuffer } from "../../../platform/models/domain/enc-array-buffer";
import { EncString } from "../../../platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
import { EncString } from "../models/enc-string";
export abstract class EncryptService {
/**

View File

@@ -0,0 +1,384 @@
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 { makeEncString, 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";
describe("EncString", () => {
afterEach(() => {
(window as any).bitwardenContainerService = undefined;
});
describe("Rsa2048_OaepSha256_B64", () => {
it("constructor", () => {
const encString = new EncString(EncryptionType.Rsa2048_OaepSha256_B64, "data");
expect(encString).toEqual({
data: "data",
encryptedString: "3.data",
encryptionType: 3,
});
});
describe("isSerializedEncString", () => {
it("is true if valid", () => {
expect(EncString.isSerializedEncString("3.data")).toBe(true);
});
it("is false if invalid", () => {
expect(EncString.isSerializedEncString("3.data|test")).toBe(false);
});
});
describe("parse existing", () => {
it("valid", () => {
const encString = new EncString("3.data");
expect(encString).toEqual({
data: "data",
encryptedString: "3.data",
encryptionType: 3,
});
});
it("invalid", () => {
const encString = new EncString("3.data|test");
expect(encString).toEqual({
encryptedString: "3.data|test",
encryptionType: 3,
});
});
const cases = [
"aXY=|Y3Q=", // AesCbc256_B64 w/out header
"0.QmFzZTY0UGFydA==|QmFzZTY0UGFydA==", // AesCbc256_B64 with header
"2.QmFzZTY0UGFydA==|QmFzZTY0UGFydA==|QmFzZTY0UGFydA==", // AesCbc256_HmacSha256_B64
"3.QmFzZTY0UGFydA==", // Rsa2048_OaepSha256_B64
"4.QmFzZTY0UGFydA==", // Rsa2048_OaepSha1_B64
"5.QmFzZTY0UGFydA==|QmFzZTY0UGFydA==", // Rsa2048_OaepSha256_HmacSha256_B64
"6.QmFzZTY0UGFydA==|QmFzZTY0UGFydA==", // Rsa2048_OaepSha1_HmacSha256_B64
];
it.each(cases)("can retrieve data bytes for %s", (encryptedString) => {
const encString = new EncString(encryptedString);
const dataBytes = encString.dataBytes;
expect(dataBytes).not.toBeNull();
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.getUserKeyWithLegacySupport.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("decryptWithKey", () => {
const encString = new EncString(EncryptionType.Rsa2048_OaepSha256_B64, "data");
const keyService = mock<KeyService>();
const encryptService = mock<EncryptService>();
encryptService.decryptString
.calledWith(encString, expect.anything())
.mockResolvedValue("decrypted");
function setupEncryption() {
encryptService.encryptString.mockImplementation(async (data, key) => {
return makeEncString(data);
});
encryptService.decryptString.mockImplementation(async (encString, key) => {
return encString.data;
});
}
beforeEach(() => {
(window as any).bitwardenContainerService = new ContainerService(keyService, encryptService);
});
it("decrypts using the provided key and encryptService", async () => {
setupEncryption();
const key = new SymmetricCryptoKey(makeStaticByteArray(32));
await encString.decryptWithKey(key, encryptService);
expect(encryptService.decryptString).toHaveBeenCalledWith(encString, key);
});
it("fails to decrypt when key is null", async () => {
const decrypted = await encString.decryptWithKey(null, encryptService);
expect(decrypted).toBe("[error: cannot decrypt]");
expect(encString.decryptedValue).toBe("[error: cannot decrypt]");
});
it("fails to decrypt when encryptService is null", async () => {
const decrypted = await encString.decryptWithKey(
new SymmetricCryptoKey(makeStaticByteArray(32)),
null,
);
expect(decrypted).toBe("[error: cannot decrypt]");
expect(encString.decryptedValue).toBe("[error: cannot decrypt]");
});
it("fails to decrypt when encryptService throws", async () => {
encryptService.decryptString.mockRejectedValue("error");
const decrypted = await encString.decryptWithKey(
new SymmetricCryptoKey(makeStaticByteArray(32)),
encryptService,
);
expect(decrypted).toBe("[error: cannot decrypt]");
expect(encString.decryptedValue).toBe("[error: cannot decrypt]");
});
});
describe("AesCbc256_B64", () => {
it("constructor", () => {
const encString = new EncString(EncryptionType.AesCbc256_B64, "data", "iv");
expect(encString).toEqual({
data: "data",
encryptedString: "0.iv|data",
encryptionType: 0,
iv: "iv",
});
});
describe("isSerializedEncString", () => {
it("is true if valid", () => {
expect(EncString.isSerializedEncString("0.iv|data")).toBe(true);
});
it("is false if invalid", () => {
expect(EncString.isSerializedEncString("0.iv|data|mac")).toBe(false);
});
});
describe("parse existing", () => {
it("valid", () => {
const encString = new EncString("0.iv|data");
expect(encString).toEqual({
data: "data",
encryptedString: "0.iv|data",
encryptionType: 0,
iv: "iv",
});
});
it("invalid", () => {
const encString = new EncString("0.iv|data|mac");
expect(encString).toEqual({
encryptedString: "0.iv|data|mac",
encryptionType: 0,
});
});
});
});
describe("AesCbc256_HmacSha256_B64", () => {
it("constructor", () => {
const encString = new EncString(EncryptionType.AesCbc256_HmacSha256_B64, "data", "iv", "mac");
expect(encString).toEqual({
data: "data",
encryptedString: "2.iv|data|mac",
encryptionType: 2,
iv: "iv",
mac: "mac",
});
});
describe("isSerializedEncString", () => {
it("is true if valid", () => {
expect(EncString.isSerializedEncString("2.iv|data|mac")).toBe(true);
});
it("is false if invalid", () => {
expect(EncString.isSerializedEncString("2.iv|data")).toBe(false);
});
it("should return false if a null string is passed in", () => {
// Act
const result = EncString.isSerializedEncString(null);
// Assert
expect(result).toBe(false);
});
it("should return false if an error is thrown while parsing the string", () => {
// Arrange
const value = "invalid.value";
// Act
const result = EncString.isSerializedEncString(value);
// Assert
expect(result).toBe(false);
});
describe("Access Token Parsing Tests", () => {
const encryptedAccessToken =
"2.rFNYSTJoljn8h6GOSNVYdQ==|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";
const standardAccessToken =
"eyJhbGciOiJSUzI1NiIsImtpZCI6IkY5NjBFQzY4RThEMTBDMUEzNEE0OUYwODkwQkExQkExMDk4QUIzMjFSUzI1NiIsIng1dCI6Ii1XRHNhT2pSREJvMHBKOElrTG9ib1FtS3N5RSIsInR5cCI6ImF0K2p3dCJ9.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0IiwibmJmIjoxNzE0NjAwNzI3LCJpYXQiOjE3MTQ2MDA3MjcsImV4cCI6MTcxNDYwNDMyNywic2NvcGUiOlsiYXBpIiwib2ZmbGluZV9hY2Nlc3MiXSwiYW1yIjpbIkFwcGxpY2F0aW9uIl0sImNsaWVudF9pZCI6ImRlc2t0b3AiLCJzdWIiOiJlY2U3MGExMy03MjE2LTQzYzQtOTk3Ny1iMTAzMDE0NmUxZTciLCJhdXRoX3RpbWUiOjE3MTQ2MDA3MjcsImlkcCI6ImJpdHdhcmRlbiIsInByZW1pdW0iOmZhbHNlLCJlbWFpbCI6ImpzbmlkZXJcdTAwMkJsb2NhbEBiaXR3YXJkZW4uY29tIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJzc3RhbXAiOiJHWTdKQU82NENLS1RLQkI2WkVBVVlMMldPUVU3QVNUMiIsIm5hbWUiOiJKYXJlZCBTbmlkZXIgMSIsIm9yZ293bmVyIjpbIjkyYjQ5OTA4LWI1MTQtNDVhOC1iYWRiLWIxMDMwMTQ4ZmU1MyIsIjM4ZWRlMzIyLWI0YjQtNGJkOC05ZTA5LWIxMDcwMTEyZGMxMSIsImIyZDA3MDI4LWE1ODMtNGMzZS04ZDYwLWIxMDcwMTE5OGMyOSIsImJmOTM0YmEyLTBmZDQtNDlmMi1hOTVlLWIxMDcwMTFmYzllNiIsImMwYjdmNzVkLTAxNWYtNDJjOS1iM2E2LWIxMDgwMTc2MDdjYSJdLCJkZXZpY2UiOiIwYmQzZWFmZC0yYjE3LTRiNGItYmUzNS1kMjIxNTE5MTA1ZmUiLCJqdGkiOiI0MEVGNjlEQ0QyNkI4MERDMkJFQ0UwODZDOTIxNDg5OSJ9.pRaZphZ8pygx3gHMdsKnCHWSBFAvm6gJ5aCLKbXIfx6Zc-LtQ_CkjO17rQaXlE4MwDt_n_wMzA38SDG2WzwjJrF3rWziPJrOMEdMGXLvVHyqxh0gcIiAQXZMYq0PdCYPBSDmsRZUZqg5BXFb9ylZjC0-m-EqDgl-i6OfxaHTPBCosX4_fr4bcyZtAaoaSeY4ZWf_1T8HrEzTlEyYKepHFzWdG3e4pJKHfs4sNGfs0uiW1awMqtRIPYI1n1F--oF5Hkm6jUJOdtrCKU0mKntyF4v7YRxgXdxUDqKw08nkk11vdPFVG87kWhR6ARYBWDp4AASy66YewqGhX7BNaekSTqK7DKxzQ9Adiv4XvmNEz3JO8tQrEFfE_mz9-WZiS6PlUipCxW-UtFp093_gHZh9_xgbuTdaO1u5_8Y42v0V_69v9WgzCGQGEWZ3PPaJsARGDO7FVKdPxP2S2lWIu22gydNHhfDZrOpBGHD1FpByfd5DbhKk0JdhHEPObs8RwNEweK-jlKmQpc_8bnhXFRUeMFrDL2Q2pNrYcDOpF1crIePPcWBk2_YdcWTqYVnGewT0toJ8sGlaAuAe6uOUZkBG3sxkOttkLYQtkxJYqt54gjazJ0N0GxAc0UBUDt0JnuLqk-cuxXiQO2_vHomTf7dilPq8fvqffrtrISxDVZenceg";
it("should return false if a non-encrypted string is passed in", () => {
// Act
const result = EncString.isSerializedEncString(standardAccessToken);
// Assert
expect(result).toBe(false);
});
it("should return true if an encrypted string is passed in", () => {
// Act
const result = EncString.isSerializedEncString(encryptedAccessToken);
// Assert
expect(result).toBe(true);
});
});
});
it("valid", () => {
const encString = new EncString("2.iv|data|mac");
expect(encString).toEqual({
data: "data",
encryptedString: "2.iv|data|mac",
encryptionType: 2,
iv: "iv",
mac: "mac",
});
});
it("invalid", () => {
const encString = new EncString("2.iv|data");
expect(encString).toEqual({
encryptedString: "2.iv|data",
encryptionType: 2,
});
});
});
it("Exit early if null", () => {
const encString = new EncString(null);
expect(encString).toEqual({
encryptedString: null,
});
});
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.getUserKeyWithLegacySupport).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.getUserKeyWithLegacySupport.mockResolvedValue(userKey);
await encString.decrypt(null, null);
expect(keyService.getUserKeyWithLegacySupport).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");
expect(encString.toJSON()).toBe(encString.encryptedString);
});
it("returns null if object is null", () => {
expect(EncString.fromJSON(null)).toBeNull();
});
});
});

View File

@@ -0,0 +1,210 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Jsonify, Opaque } from "type-fest";
import { EncryptionType, EXPECTED_NUM_PARTS_BY_ENCRYPTION_TYPE } from "../../../platform/enums";
import { Encrypted } from "../../../platform/interfaces/encrypted";
import { Utils } from "../../../platform/misc/utils";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
import { EncryptService } from "../abstractions/encrypt.service";
export const DECRYPT_ERROR = "[error: cannot decrypt]";
export class EncString implements Encrypted {
encryptedString?: EncryptedString;
encryptionType?: EncryptionType;
decryptedValue?: string;
data?: string;
iv?: string;
mac?: string;
constructor(
encryptedStringOrType: string | EncryptionType,
data?: string,
iv?: string,
mac?: string,
) {
if (data != null) {
this.initFromData(encryptedStringOrType as EncryptionType, data, iv, mac);
} else {
this.initFromEncryptedString(encryptedStringOrType as string);
}
}
get ivBytes(): Uint8Array {
return this.iv == null ? null : Utils.fromB64ToArray(this.iv);
}
get macBytes(): Uint8Array {
return this.mac == null ? null : Utils.fromB64ToArray(this.mac);
}
get dataBytes(): Uint8Array {
return this.data == null ? null : Utils.fromB64ToArray(this.data);
}
toJSON() {
return this.encryptedString as string;
}
static fromJSON(obj: Jsonify<EncString>): EncString {
if (obj == null) {
return null;
}
return new EncString(obj);
}
private initFromData(encType: EncryptionType, data: string, iv: string, mac: string) {
if (iv != null) {
this.encryptedString = (encType + "." + iv + "|" + data) as EncryptedString;
} else {
this.encryptedString = (encType + "." + data) as EncryptedString;
}
// mac
if (mac != null) {
this.encryptedString = (this.encryptedString + "|" + mac) as EncryptedString;
}
this.encryptionType = encType;
this.data = data;
this.iv = iv;
this.mac = mac;
}
private initFromEncryptedString(encryptedString: string) {
this.encryptedString = encryptedString as EncryptedString;
if (!this.encryptedString) {
return;
}
const { encType, encPieces } = EncString.parseEncryptedString(this.encryptedString);
this.encryptionType = encType;
if (encPieces.length !== EXPECTED_NUM_PARTS_BY_ENCRYPTION_TYPE[encType]) {
return;
}
switch (encType) {
case EncryptionType.AesCbc256_HmacSha256_B64:
this.iv = encPieces[0];
this.data = encPieces[1];
this.mac = encPieces[2];
break;
case EncryptionType.AesCbc256_B64:
this.iv = encPieces[0];
this.data = encPieces[1];
break;
case EncryptionType.Rsa2048_OaepSha256_B64:
case EncryptionType.Rsa2048_OaepSha1_B64:
this.data = encPieces[0];
break;
case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64:
case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64:
this.data = encPieces[0];
this.mac = encPieces[1];
break;
default:
return;
}
}
private static parseEncryptedString(encryptedString: string): {
encType: EncryptionType;
encPieces: string[];
} {
const headerPieces = encryptedString.split(".");
let encType: EncryptionType;
let encPieces: string[] = null;
if (headerPieces.length === 2) {
try {
encType = parseInt(headerPieces[0], null);
encPieces = headerPieces[1].split("|");
// FIXME: Remove when updating file. Eslint update
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
return { encType: NaN, encPieces: [] };
}
} else {
encPieces = encryptedString.split("|");
encType = EncryptionType.AesCbc256_B64;
}
return {
encType,
encPieces,
};
}
static isSerializedEncString(s: string): boolean {
if (s == null) {
return false;
}
const { encType, encPieces } = this.parseEncryptedString(s);
if (isNaN(encType) || encPieces.length === 0) {
return false;
}
return EXPECTED_NUM_PARTS_BY_ENCRYPTION_TYPE[encType] === encPieces.length;
}
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);
// FIXME: Remove when updating file. Eslint update
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
this.decryptedValue = DECRYPT_ERROR;
}
return this.decryptedValue;
}
async decryptWithKey(
key: SymmetricCryptoKey,
encryptService: EncryptService,
decryptTrace: string = "domain-withkey",
): Promise<string> {
try {
if (key == null) {
throw new Error("No key to decrypt EncString");
}
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) {
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.getUserKeyWithLegacySupport();
}
}
export type EncryptedString = Opaque<string, "EncString">;

View File

@@ -1,6 +1,7 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import {
EncryptionType,
@@ -11,7 +12,6 @@ import { Encrypted } from "@bitwarden/common/platform/interfaces/encrypted";
import { InitializerMetadata } from "@bitwarden/common/platform/interfaces/initializer-metadata.interface";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { EncryptedObject } from "@bitwarden/common/platform/models/domain/encrypted-object";
import {
Aes256CbcHmacKey,

View File

@@ -1,11 +1,11 @@
import { mockReset, mock } from "jest-mock-extended";
import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { EncryptionType } from "@bitwarden/common/platform/enums";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import {
Aes256CbcHmacKey,
SymmetricCryptoKey,

View File

@@ -5,9 +5,9 @@ import { Observable } from "rxjs";
import { OtherDeviceKeysUpdateRequest } from "@bitwarden/common/auth/models/request/update-devices-trust.request";
import { DeviceResponse } from "../../../auth/abstractions/devices/responses/device.response";
import { EncString } from "../../../platform/models/domain/enc-string";
import { UserId } from "../../../types/guid";
import { DeviceKey, UserKey } from "../../../types/key";
import { EncString } from "../../crypto/models/enc-string";
export abstract class DeviceTrustServiceAbstraction {
/**

View File

@@ -25,7 +25,6 @@ import { LogService } from "../../../platform/abstractions/log.service";
import { PlatformUtilsService } from "../../../platform/abstractions/platform-utils.service";
import { AbstractStorageService } from "../../../platform/abstractions/storage.service";
import { StorageLocation } from "../../../platform/enums";
import { EncString } from "../../../platform/models/domain/enc-string";
import { StorageOptions } from "../../../platform/models/domain/storage-options";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
import { DEVICE_TRUST_DISK_LOCAL, StateProvider, UserKeyDefinition } from "../../../platform/state";
@@ -33,6 +32,7 @@ import { UserId } from "../../../types/guid";
import { UserKey, DeviceKey } from "../../../types/key";
import { CryptoFunctionService } from "../../crypto/abstractions/crypto-function.service";
import { EncryptService } from "../../crypto/abstractions/encrypt.service";
import { EncString } from "../../crypto/models/enc-string";
import { DeviceTrustServiceAbstraction } from "../abstractions/device-trust.service.abstraction";
/** Uses disk storage so that the device key can persist after log out and tab removal. */

View File

@@ -32,7 +32,6 @@ import { AbstractStorageService } from "../../../platform/abstractions/storage.s
import { StorageLocation } from "../../../platform/enums";
import { EncryptionType } from "../../../platform/enums/encryption-type.enum";
import { Utils } from "../../../platform/misc/utils";
import { EncString } from "../../../platform/models/domain/enc-string";
import { StorageOptions } from "../../../platform/models/domain/storage-options";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
import { CsprngArray } from "../../../types/csprng";
@@ -40,6 +39,7 @@ import { UserId } from "../../../types/guid";
import { DeviceKey, UserKey } from "../../../types/key";
import { CryptoFunctionService } from "../../crypto/abstractions/crypto-function.service";
import { EncryptService } from "../../crypto/abstractions/encrypt.service";
import { EncString } from "../../crypto/models/enc-string";
import {
SHOULD_TRUST_DEVICE,

View File

@@ -17,11 +17,11 @@ import { KeyConnectorUserKeyResponse } from "../../../auth/models/response/key-c
import { TokenService } from "../../../auth/services/token.service";
import { LogService } from "../../../platform/abstractions/log.service";
import { Utils } from "../../../platform/misc/utils";
import { EncString } from "../../../platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
import { KeyGenerationService } from "../../../platform/services/key-generation.service";
import { OrganizationId, UserId } from "../../../types/guid";
import { MasterKey, UserKey } from "../../../types/key";
import { EncString } from "../../crypto/models/enc-string";
import { FakeMasterPasswordService } from "../../master-password/services/fake-master-password.service";
import { KeyConnectorUserKeyRequest } from "../models/key-connector-user-key.request";

View File

@@ -1,9 +1,9 @@
import { Observable } from "rxjs";
import { ForceSetPasswordReason } from "../../../auth/models/domain/force-set-password-reason";
import { EncString } from "../../../platform/models/domain/enc-string";
import { UserId } from "../../../types/guid";
import { MasterKey, UserKey } from "../../../types/key";
import { EncString } from "../../crypto/models/enc-string";
export abstract class MasterPasswordServiceAbstraction {
/**

View File

@@ -4,9 +4,9 @@ import { mock } from "jest-mock-extended";
import { ReplaySubject, Observable } from "rxjs";
import { ForceSetPasswordReason } from "../../../auth/models/domain/force-set-password-reason";
import { EncString } from "../../../platform/models/domain/enc-string";
import { UserId } from "../../../types/guid";
import { MasterKey, UserKey } from "../../../types/key";
import { EncString } from "../../crypto/models/enc-string";
import { InternalMasterPasswordServiceAbstraction } from "../abstractions/master-password.service.abstraction";
export class FakeMasterPasswordService implements InternalMasterPasswordServiceAbstraction {

View File

@@ -7,12 +7,12 @@ import { ForceSetPasswordReason } from "../../../auth/models/domain/force-set-pa
import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service";
import { LogService } from "../../../platform/abstractions/log.service";
import { StateService } from "../../../platform/abstractions/state.service";
import { EncString } from "../../../platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
import { StateProvider } from "../../../platform/state";
import { UserId } from "../../../types/guid";
import { MasterKey } from "../../../types/key";
import { EncryptService } from "../../crypto/abstractions/encrypt.service";
import { EncString } from "../../crypto/models/enc-string";
import { MasterPasswordService } from "./master-password.service";

View File

@@ -7,7 +7,6 @@ import { KeyGenerationService } from "../../../platform/abstractions/key-generat
import { LogService } from "../../../platform/abstractions/log.service";
import { StateService } from "../../../platform/abstractions/state.service";
import { EncryptionType } from "../../../platform/enums";
import { EncryptedString, EncString } from "../../../platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
import {
MASTER_PASSWORD_DISK,
@@ -18,6 +17,7 @@ import {
import { UserId } from "../../../types/guid";
import { MasterKey, UserKey } from "../../../types/key";
import { EncryptService } from "../../crypto/abstractions/encrypt.service";
import { EncryptedString, EncString } from "../../crypto/models/enc-string";
import { InternalMasterPasswordServiceAbstraction } from "../abstractions/master-password.service.abstraction";
/** Memory since master key shouldn't be available on lock */