mirror of
https://github.com/bitwarden/browser
synced 2026-02-26 09:33:22 +00:00
Merge master into branch
This commit is contained in:
@@ -8,7 +8,7 @@ module.exports = {
|
||||
preset: "ts-jest",
|
||||
testEnvironment: "jsdom",
|
||||
testMatch: ["**/+(*.)+(spec).+(ts)"],
|
||||
setupFilesAfterEnv: ["<rootDir>/spec/test.ts"],
|
||||
setupFilesAfterEnv: ["<rootDir>/spec/test.setup.ts"],
|
||||
collectCoverage: true,
|
||||
coverageReporters: ["html", "lcov"],
|
||||
coverageDirectory: "coverage",
|
||||
|
||||
82
libs/common/spec/services/state.service.spec.ts
Normal file
82
libs/common/spec/services/state.service.spec.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute";
|
||||
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service";
|
||||
import { StateFactory } from "@bitwarden/common/factories/stateFactory";
|
||||
import { Account } from "@bitwarden/common/models/domain/account";
|
||||
import { GlobalState } from "@bitwarden/common/models/domain/globalState";
|
||||
import { State } from "@bitwarden/common/models/domain/state";
|
||||
import { StorageOptions } from "@bitwarden/common/models/domain/storageOptions";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCryptoKey";
|
||||
import { StateService } from "@bitwarden/common/services/state.service";
|
||||
import { StateMigrationService } from "@bitwarden/common/services/stateMigration.service";
|
||||
|
||||
describe("Browser State Service backed by chrome.storage api", () => {
|
||||
let secureStorageService: SubstituteOf<AbstractStorageService>;
|
||||
let diskStorageService: SubstituteOf<AbstractStorageService>;
|
||||
let memoryStorageService: SubstituteOf<AbstractStorageService>;
|
||||
let logService: SubstituteOf<LogService>;
|
||||
let stateMigrationService: SubstituteOf<StateMigrationService>;
|
||||
let stateFactory: SubstituteOf<StateFactory<GlobalState, Account>>;
|
||||
let useAccountCache: boolean;
|
||||
|
||||
let state: State<GlobalState, Account>;
|
||||
const userId = "userId";
|
||||
|
||||
let sut: StateService;
|
||||
|
||||
beforeEach(() => {
|
||||
secureStorageService = Substitute.for();
|
||||
diskStorageService = Substitute.for();
|
||||
memoryStorageService = Substitute.for();
|
||||
logService = Substitute.for();
|
||||
stateMigrationService = Substitute.for();
|
||||
stateFactory = Substitute.for();
|
||||
useAccountCache = true;
|
||||
|
||||
state = new State(new GlobalState());
|
||||
const stateGetter = (key: string) => Promise.resolve(JSON.parse(JSON.stringify(state)));
|
||||
memoryStorageService.get("state").mimicks(stateGetter);
|
||||
memoryStorageService
|
||||
.save("state", Arg.any(), Arg.any())
|
||||
.mimicks((key: string, obj: any, options: StorageOptions) => {
|
||||
return new Promise(() => {
|
||||
state = obj;
|
||||
});
|
||||
});
|
||||
|
||||
sut = new StateService(
|
||||
diskStorageService,
|
||||
secureStorageService,
|
||||
memoryStorageService,
|
||||
logService,
|
||||
stateMigrationService,
|
||||
stateFactory,
|
||||
useAccountCache
|
||||
);
|
||||
});
|
||||
|
||||
describe("account state getters", () => {
|
||||
beforeEach(() => {
|
||||
state.accounts[userId] = createAccount(userId);
|
||||
state.activeUserId = userId;
|
||||
});
|
||||
|
||||
describe("getCryptoMasterKey", () => {
|
||||
it("should return the stored SymmetricCryptoKey", async () => {
|
||||
const key = new SymmetricCryptoKey(new Uint8Array(32).buffer);
|
||||
state.accounts[userId].keys.cryptoMasterKey = key;
|
||||
|
||||
const actual = await sut.getCryptoMasterKey();
|
||||
expect(actual).toBeInstanceOf(SymmetricCryptoKey);
|
||||
expect(actual).toMatchObject(key);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function createAccount(userId: string): Account {
|
||||
return new Account({
|
||||
profile: { userId: userId },
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute";
|
||||
|
||||
import { StorageService } from "@bitwarden/common/abstractions/storage.service";
|
||||
import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service";
|
||||
import { StateVersion } from "@bitwarden/common/enums/stateVersion";
|
||||
import { StateFactory } from "@bitwarden/common/factories/stateFactory";
|
||||
import { Account } from "@bitwarden/common/models/domain/account";
|
||||
@@ -10,15 +10,15 @@ import { StateMigrationService } from "@bitwarden/common/services/stateMigration
|
||||
const userId = "USER_ID";
|
||||
|
||||
describe("State Migration Service", () => {
|
||||
let storageService: SubstituteOf<StorageService>;
|
||||
let secureStorageService: SubstituteOf<StorageService>;
|
||||
let storageService: SubstituteOf<AbstractStorageService>;
|
||||
let secureStorageService: SubstituteOf<AbstractStorageService>;
|
||||
let stateFactory: SubstituteOf<StateFactory>;
|
||||
|
||||
let stateMigrationService: StateMigrationService;
|
||||
|
||||
beforeEach(() => {
|
||||
storageService = Substitute.for<StorageService>();
|
||||
secureStorageService = Substitute.for<StorageService>();
|
||||
storageService = Substitute.for<AbstractStorageService>();
|
||||
secureStorageService = Substitute.for<AbstractStorageService>();
|
||||
stateFactory = Substitute.for<StateFactory>();
|
||||
|
||||
stateMigrationService = new StateMigrationService(
|
||||
@@ -28,7 +28,7 @@ describe("State Migration Service", () => {
|
||||
);
|
||||
});
|
||||
|
||||
describe("StateVersion 3 to 4 migration", async () => {
|
||||
describe("StateVersion 3 to 4 migration", () => {
|
||||
beforeEach(() => {
|
||||
const globalVersion3: Partial<GlobalState> = {
|
||||
stateVersion: StateVersion.Three,
|
||||
7
libs/common/src/abstractions/abstractEncrypt.service.ts
Normal file
7
libs/common/src/abstractions/abstractEncrypt.service.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { EncString } from "@bitwarden/common/models/domain/encString";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCryptoKey";
|
||||
|
||||
export abstract class AbstractEncryptService {
|
||||
abstract encrypt(plainValue: string | ArrayBuffer, key: SymmetricCryptoKey): Promise<EncString>;
|
||||
abstract decryptToUtf8(encString: EncString, key: SymmetricCryptoKey): Promise<string>;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { FileDownloadRequest } from "./fileDownloadRequest";
|
||||
|
||||
export abstract class FileDownloadService {
|
||||
download: (request: FileDownloadRequest) => void;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { FileDownloadRequest } from "./fileDownloadRequest";
|
||||
|
||||
export class FileDownloadBuilder {
|
||||
get blobOptions(): any {
|
||||
const options = this._request.blobOptions ?? {};
|
||||
if (options.type == null) {
|
||||
options.type = this.fileType;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
get blob(): Blob {
|
||||
if (this.blobOptions != null) {
|
||||
return new Blob([this._request.blobData], this.blobOptions);
|
||||
} else {
|
||||
return new Blob([this._request.blobData]);
|
||||
}
|
||||
}
|
||||
|
||||
get downloadMethod(): "save" | "open" {
|
||||
if (this._request.downloadMethod != null) {
|
||||
return this._request.downloadMethod;
|
||||
}
|
||||
return this.fileType != "application/pdf" ? "save" : "open";
|
||||
}
|
||||
|
||||
private get fileType() {
|
||||
const fileNameLower = this._request.fileName.toLowerCase();
|
||||
if (fileNameLower.endsWith(".pdf")) {
|
||||
return "application/pdf";
|
||||
} else if (fileNameLower.endsWith(".xlsx")) {
|
||||
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
||||
} else if (fileNameLower.endsWith(".docx")) {
|
||||
return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
||||
} else if (fileNameLower.endsWith(".pptx")) {
|
||||
return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
|
||||
} else if (fileNameLower.endsWith(".csv")) {
|
||||
return "text/csv";
|
||||
} else if (fileNameLower.endsWith(".png")) {
|
||||
return "image/png";
|
||||
} else if (fileNameLower.endsWith(".jpg") || fileNameLower.endsWith(".jpeg")) {
|
||||
return "image/jpeg";
|
||||
} else if (fileNameLower.endsWith(".gif")) {
|
||||
return "image/gif";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
constructor(private readonly _request: FileDownloadRequest) {}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export type FileDownloadRequest = {
|
||||
fileName: string;
|
||||
blobData: BlobPart;
|
||||
blobOptions?: BlobPropertyBag;
|
||||
downloadMethod?: "save" | "open";
|
||||
};
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ClientType } from "../enums/clientType";
|
||||
import { DeviceType } from "../enums/deviceType";
|
||||
import { ThemeType } from "../enums/themeType";
|
||||
|
||||
interface ToastOptions {
|
||||
timeout?: number;
|
||||
@@ -19,7 +18,6 @@ export abstract class PlatformUtilsService {
|
||||
isMacAppStore: () => boolean;
|
||||
isViewOpen: () => Promise<boolean>;
|
||||
launchUri: (uri: string, options?: any) => void;
|
||||
saveFile: (win: Window, blobData: any, blobOptions: any, fileName: string) => void;
|
||||
getApplicationVersion: () => Promise<string>;
|
||||
supportsWebAuthn: (win: Window) => boolean;
|
||||
supportsDuo: () => boolean;
|
||||
@@ -43,10 +41,5 @@ export abstract class PlatformUtilsService {
|
||||
readFromClipboard: (options?: any) => Promise<string>;
|
||||
supportsBiometric: () => Promise<boolean>;
|
||||
authenticateBiometric: () => Promise<boolean>;
|
||||
getDefaultSystemTheme: () => Promise<ThemeType.Light | ThemeType.Dark>;
|
||||
onDefaultSystemThemeChange: (
|
||||
callback: (theme: ThemeType.Light | ThemeType.Dark) => unknown
|
||||
) => unknown;
|
||||
getEffectiveTheme: () => Promise<ThemeType>;
|
||||
supportsSecureStorage: () => boolean;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { StorageOptions } from "../models/domain/storageOptions";
|
||||
|
||||
export abstract class StorageService {
|
||||
get: <T>(key: string, options?: StorageOptions) => Promise<T>;
|
||||
has: (key: string, options?: StorageOptions) => Promise<boolean>;
|
||||
save: (key: string, obj: any, options?: StorageOptions) => Promise<any>;
|
||||
remove: (key: string, options?: StorageOptions) => Promise<any>;
|
||||
export abstract class AbstractStorageService {
|
||||
abstract get<T>(key: string, options?: StorageOptions): Promise<T>;
|
||||
abstract has(key: string, options?: StorageOptions): Promise<boolean>;
|
||||
abstract save<T>(key: string, obj: T, options?: StorageOptions): Promise<void>;
|
||||
abstract remove(key: string, options?: StorageOptions): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
export abstract class TotpService {
|
||||
getCode: (key: string) => Promise<string>;
|
||||
getTimeInterval: (key: string) => number;
|
||||
isAutoCopyEnabled: () => Promise<boolean>;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import { SymmetricCryptoKey } from "./symmetricCryptoKey";
|
||||
export class EncryptionPair<TEncrypted, TDecrypted> {
|
||||
encrypted?: TEncrypted;
|
||||
decrypted?: TDecrypted;
|
||||
decryptedSerialized?: string;
|
||||
}
|
||||
|
||||
export class DataEncryptionPair<TEncrypted, TDecrypted> {
|
||||
@@ -76,6 +77,7 @@ export class AccountKeys {
|
||||
privateKey?: EncryptionPair<string, ArrayBuffer> = new EncryptionPair<string, ArrayBuffer>();
|
||||
legacyEtmKey?: SymmetricCryptoKey;
|
||||
publicKey?: ArrayBuffer;
|
||||
publicKeySerialized?: string;
|
||||
apiKeyClientSecret?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,4 +54,22 @@ export class SymmetricCryptoKey {
|
||||
this.macKeyB64 = Utils.fromBufferToB64(this.macKey);
|
||||
}
|
||||
}
|
||||
|
||||
static initFromJson(jsonResult: SymmetricCryptoKey): SymmetricCryptoKey {
|
||||
if (jsonResult == null) {
|
||||
return jsonResult;
|
||||
}
|
||||
|
||||
if (jsonResult.keyB64 != null) {
|
||||
jsonResult.key = Utils.fromB64ToArray(jsonResult.keyB64).buffer;
|
||||
}
|
||||
if (jsonResult.encKeyB64 != null) {
|
||||
jsonResult.encKey = Utils.fromB64ToArray(jsonResult.encKeyB64).buffer;
|
||||
}
|
||||
if (jsonResult.macKeyB64 != null) {
|
||||
jsonResult.macKey = Utils.fromB64ToArray(jsonResult.macKeyB64).buffer;
|
||||
}
|
||||
|
||||
return jsonResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { AppIdService as AppIdServiceAbstraction } from "../abstractions/appId.service";
|
||||
import { StorageService } from "../abstractions/storage.service";
|
||||
import { AbstractStorageService } from "../abstractions/storage.service";
|
||||
import { HtmlStorageLocation } from "../enums/htmlStorageLocation";
|
||||
import { Utils } from "../misc/utils";
|
||||
|
||||
export class AppIdService implements AppIdServiceAbstraction {
|
||||
constructor(private storageService: StorageService) {}
|
||||
constructor(private storageService: AbstractStorageService) {}
|
||||
|
||||
getAppId(): Promise<string> {
|
||||
return this.makeAndGetAppId("appId");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as bigInt from "big-integer";
|
||||
|
||||
import { AbstractEncryptService } from "../abstractions/abstractEncrypt.service";
|
||||
import { CryptoService as CryptoServiceAbstraction } from "../abstractions/crypto.service";
|
||||
import { CryptoFunctionService } from "../abstractions/cryptoFunction.service";
|
||||
import { LogService } from "../abstractions/log.service";
|
||||
@@ -23,6 +24,7 @@ import { ProfileProviderResponse } from "../models/response/profileProviderRespo
|
||||
export class CryptoService implements CryptoServiceAbstraction {
|
||||
constructor(
|
||||
private cryptoFunctionService: CryptoFunctionService,
|
||||
private encryptService: AbstractEncryptService,
|
||||
protected platformUtilService: PlatformUtilsService,
|
||||
protected logService: LogService,
|
||||
protected stateService: StateService
|
||||
@@ -503,23 +505,15 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return this.buildEncKey(key, encKey.key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated June 22 2022: This method has been moved to encryptService.
|
||||
* All callers should use this service to grab the relevant key and use encryptService for encryption instead.
|
||||
* This method will be removed once all existing code has been refactored to use encryptService.
|
||||
*/
|
||||
async encrypt(plainValue: string | ArrayBuffer, key?: SymmetricCryptoKey): Promise<EncString> {
|
||||
if (plainValue == null) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
key = await this.getKeyForEncryption(key);
|
||||
|
||||
let plainBuf: ArrayBuffer;
|
||||
if (typeof plainValue === "string") {
|
||||
plainBuf = Utils.fromUtf8ToArray(plainValue).buffer;
|
||||
} else {
|
||||
plainBuf = plainValue;
|
||||
}
|
||||
|
||||
const encObj = await this.aesEncrypt(plainBuf, key);
|
||||
const iv = Utils.fromBufferToB64(encObj.iv);
|
||||
const data = Utils.fromBufferToB64(encObj.data);
|
||||
const mac = encObj.mac != null ? Utils.fromBufferToB64(encObj.mac) : null;
|
||||
return new EncString(encObj.key.encType, data, iv, mac);
|
||||
return await this.encryptService.encrypt(plainValue, key);
|
||||
}
|
||||
|
||||
async encryptToBytes(plainValue: ArrayBuffer, key?: SymmetricCryptoKey): Promise<EncArrayBuffer> {
|
||||
@@ -618,13 +612,9 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
}
|
||||
|
||||
async decryptToUtf8(encString: EncString, key?: SymmetricCryptoKey): Promise<string> {
|
||||
return await this.aesDecryptToUtf8(
|
||||
encString.encryptionType,
|
||||
encString.data,
|
||||
encString.iv,
|
||||
encString.mac,
|
||||
key
|
||||
);
|
||||
key = await this.getKeyForEncryption(key);
|
||||
key = await this.resolveLegacyKey(encString.encryptionType, key);
|
||||
return await this.encryptService.decryptToUtf8(encString, key);
|
||||
}
|
||||
|
||||
async decryptFromBytes(encBuf: ArrayBuffer, key: SymmetricCryptoKey): Promise<ArrayBuffer> {
|
||||
@@ -754,6 +744,10 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
: await this.stateService.getCryptoMasterKeyBiometric({ userId: userId });
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated June 22 2022: This method has been moved to encryptService.
|
||||
* All callers should use encryptService instead. This method will be removed once all existing code has been refactored to use encryptService.
|
||||
*/
|
||||
private async aesEncrypt(data: ArrayBuffer, key: SymmetricCryptoKey): Promise<EncryptedObject> {
|
||||
const obj = new EncryptedObject();
|
||||
obj.key = await this.getKeyForEncryption(key);
|
||||
@@ -770,43 +764,6 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return obj;
|
||||
}
|
||||
|
||||
private async aesDecryptToUtf8(
|
||||
encType: EncryptionType,
|
||||
data: string,
|
||||
iv: string,
|
||||
mac: string,
|
||||
key: SymmetricCryptoKey
|
||||
): Promise<string> {
|
||||
const keyForEnc = await this.getKeyForEncryption(key);
|
||||
const theKey = await this.resolveLegacyKey(encType, keyForEnc);
|
||||
|
||||
if (theKey.macKey != null && mac == null) {
|
||||
this.logService.error("mac required.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (theKey.encType !== encType) {
|
||||
this.logService.error("encType unavailable.");
|
||||
return null;
|
||||
}
|
||||
|
||||
const fastParams = this.cryptoFunctionService.aesDecryptFastParameters(data, iv, mac, theKey);
|
||||
if (fastParams.macKey != null && fastParams.mac != null) {
|
||||
const computedMac = await this.cryptoFunctionService.hmacFast(
|
||||
fastParams.macData,
|
||||
fastParams.macKey,
|
||||
"sha256"
|
||||
);
|
||||
const macsEqual = await this.cryptoFunctionService.compareFast(fastParams.mac, computedMac);
|
||||
if (!macsEqual) {
|
||||
this.logService.error("mac failed.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return this.cryptoFunctionService.aesDecryptFast(fastParams);
|
||||
}
|
||||
|
||||
private async aesDecryptToBytes(
|
||||
encType: EncryptionType,
|
||||
data: ArrayBuffer,
|
||||
|
||||
94
libs/common/src/services/encrypt.service.ts
Normal file
94
libs/common/src/services/encrypt.service.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { Utils } from "@bitwarden/common/misc/utils";
|
||||
import { EncString } from "@bitwarden/common/models/domain/encString";
|
||||
import { EncryptedObject } from "@bitwarden/common/models/domain/encryptedObject";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCryptoKey";
|
||||
|
||||
import { AbstractEncryptService } from "../abstractions/abstractEncrypt.service";
|
||||
|
||||
export class EncryptService implements AbstractEncryptService {
|
||||
constructor(
|
||||
private cryptoFunctionService: CryptoFunctionService,
|
||||
private logService: LogService,
|
||||
private logMacFailures: boolean
|
||||
) {}
|
||||
|
||||
async encrypt(plainValue: string | ArrayBuffer, key: SymmetricCryptoKey): Promise<EncString> {
|
||||
if (key == null) {
|
||||
throw new Error("no encryption key provided.");
|
||||
}
|
||||
|
||||
if (plainValue == null) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
let plainBuf: ArrayBuffer;
|
||||
if (typeof plainValue === "string") {
|
||||
plainBuf = Utils.fromUtf8ToArray(plainValue).buffer;
|
||||
} else {
|
||||
plainBuf = plainValue;
|
||||
}
|
||||
|
||||
const encObj = await this.aesEncrypt(plainBuf, key);
|
||||
const iv = Utils.fromBufferToB64(encObj.iv);
|
||||
const data = Utils.fromBufferToB64(encObj.data);
|
||||
const mac = encObj.mac != null ? Utils.fromBufferToB64(encObj.mac) : null;
|
||||
return new EncString(encObj.key.encType, data, iv, mac);
|
||||
}
|
||||
|
||||
async decryptToUtf8(encString: EncString, key: SymmetricCryptoKey): Promise<string> {
|
||||
if (key?.macKey != null && encString?.mac == null) {
|
||||
this.logService.error("mac required.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (key.encType !== encString.encryptionType) {
|
||||
this.logService.error("encType unavailable.");
|
||||
return null;
|
||||
}
|
||||
|
||||
const fastParams = this.cryptoFunctionService.aesDecryptFastParameters(
|
||||
encString.data,
|
||||
encString.iv,
|
||||
encString.mac,
|
||||
key
|
||||
);
|
||||
if (fastParams.macKey != null && fastParams.mac != null) {
|
||||
const computedMac = await this.cryptoFunctionService.hmacFast(
|
||||
fastParams.macData,
|
||||
fastParams.macKey,
|
||||
"sha256"
|
||||
);
|
||||
const macsEqual = await this.cryptoFunctionService.compareFast(fastParams.mac, computedMac);
|
||||
if (!macsEqual) {
|
||||
this.logMacFailed("mac failed.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return this.cryptoFunctionService.aesDecryptFast(fastParams);
|
||||
}
|
||||
|
||||
private async aesEncrypt(data: ArrayBuffer, key: SymmetricCryptoKey): Promise<EncryptedObject> {
|
||||
const obj = new EncryptedObject();
|
||||
obj.key = key;
|
||||
obj.iv = await this.cryptoFunctionService.randomBytes(16);
|
||||
obj.data = await this.cryptoFunctionService.aesEncrypt(data, obj.iv, obj.key.encKey);
|
||||
|
||||
if (obj.key.macKey != null) {
|
||||
const macData = new Uint8Array(obj.iv.byteLength + obj.data.byteLength);
|
||||
macData.set(new Uint8Array(obj.iv), 0);
|
||||
macData.set(new Uint8Array(obj.data), obj.iv.byteLength);
|
||||
obj.mac = await this.cryptoFunctionService.hmac(macData.buffer, obj.key.macKey, "sha256");
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
private logMacFailed(msg: string) {
|
||||
if (this.logMacFailures) {
|
||||
this.logService.error(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
libs/common/src/services/memoryStorage.service.ts
Normal file
30
libs/common/src/services/memoryStorage.service.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service";
|
||||
|
||||
export class MemoryStorageService implements AbstractStorageService {
|
||||
private store = new Map<string, any>();
|
||||
|
||||
get<T>(key: string): Promise<T> {
|
||||
if (this.store.has(key)) {
|
||||
const obj = this.store.get(key);
|
||||
return Promise.resolve(obj as T);
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
async has(key: string): Promise<boolean> {
|
||||
return this.get(key) != null;
|
||||
}
|
||||
|
||||
save(key: string, obj: any): Promise<any> {
|
||||
if (obj == null) {
|
||||
return this.remove(key);
|
||||
}
|
||||
this.store.set(key, obj);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
remove(key: string): Promise<any> {
|
||||
this.store.delete(key);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
@@ -180,7 +180,11 @@ export class SearchService implements SearchServiceAbstraction {
|
||||
if (c.subTitle != null && c.subTitle.toLowerCase().indexOf(query) > -1) {
|
||||
return true;
|
||||
}
|
||||
if (c.login && c.login.uri != null && c.login.uri.toLowerCase().indexOf(query) > -1) {
|
||||
if (
|
||||
c.login &&
|
||||
c.login.hasUris &&
|
||||
c.login.uris.some((loginUri) => loginUri.uri.toLowerCase().indexOf(query) > -1)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
import { StorageService } from "../abstractions/storage.service";
|
||||
import { AbstractStorageService } from "../abstractions/storage.service";
|
||||
import { HtmlStorageLocation } from "../enums/htmlStorageLocation";
|
||||
import { KdfType } from "../enums/kdfType";
|
||||
import { StateVersion } from "../enums/stateVersion";
|
||||
@@ -132,8 +132,8 @@ export class StateMigrationService<
|
||||
TAccount extends Account = Account
|
||||
> {
|
||||
constructor(
|
||||
protected storageService: StorageService,
|
||||
protected secureStorageService: StorageService,
|
||||
protected storageService: AbstractStorageService,
|
||||
protected secureStorageService: AbstractStorageService,
|
||||
protected stateFactory: StateFactory<TGlobalState, TAccount>
|
||||
) {}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { CryptoFunctionService } from "../abstractions/cryptoFunction.service";
|
||||
import { LogService } from "../abstractions/log.service";
|
||||
import { StateService } from "../abstractions/state.service";
|
||||
import { TotpService as TotpServiceAbstraction } from "../abstractions/totp.service";
|
||||
import { Utils } from "../misc/utils";
|
||||
|
||||
@@ -10,8 +9,7 @@ const SteamChars = "23456789BCDFGHJKMNPQRTVWXY";
|
||||
export class TotpService implements TotpServiceAbstraction {
|
||||
constructor(
|
||||
private cryptoFunctionService: CryptoFunctionService,
|
||||
private logService: LogService,
|
||||
private stateService: StateService
|
||||
private logService: LogService
|
||||
) {}
|
||||
|
||||
async getCode(key: string): Promise<string> {
|
||||
@@ -113,10 +111,6 @@ export class TotpService implements TotpServiceAbstraction {
|
||||
return period;
|
||||
}
|
||||
|
||||
async isAutoCopyEnabled(): Promise<boolean> {
|
||||
return !(await this.stateService.getDisableAutoTotpCopy());
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
private leftPad(s: string, l: number, p: string): string {
|
||||
|
||||
Reference in New Issue
Block a user