2022-06-14 17:10:53 +02:00
|
|
|
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
|
|
|
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
2022-06-27 19:38:12 +02:00
|
|
|
import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service";
|
2022-06-14 17:10:53 +02:00
|
|
|
import { Utils } from "@bitwarden/common/misc/utils";
|
2022-10-14 18:25:50 +02:00
|
|
|
import { EncArrayBuffer } from "@bitwarden/common/models/domain/enc-array-buffer";
|
|
|
|
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key";
|
2018-05-16 03:11:58 +02:00
|
|
|
|
2022-06-27 19:38:12 +02:00
|
|
|
export class NodeEnvSecureStorageService implements AbstractStorageService {
|
2020-12-14 18:29:17 +01:00
|
|
|
constructor(
|
2022-06-27 19:38:12 +02:00
|
|
|
private storageService: AbstractStorageService,
|
2020-12-14 18:29:17 +01:00
|
|
|
private logService: LogService,
|
|
|
|
private cryptoService: () => CryptoService
|
|
|
|
) {}
|
2018-05-16 03:11:58 +02:00
|
|
|
|
|
|
|
async get<T>(key: string): Promise<T> {
|
|
|
|
const value = await this.storageService.get<string>(this.makeProtectedStorageKey(key));
|
|
|
|
if (value == null) {
|
2021-06-22 19:37:30 +02:00
|
|
|
return null;
|
2021-06-15 20:41:17 +02:00
|
|
|
}
|
2018-05-16 03:11:58 +02:00
|
|
|
const obj = await this.decrypt(value);
|
|
|
|
return obj as any;
|
2021-12-20 18:04:00 +01:00
|
|
|
}
|
2021-06-15 20:41:17 +02:00
|
|
|
|
2018-05-16 03:11:58 +02:00
|
|
|
async has(key: string): Promise<boolean> {
|
|
|
|
return (await this.get(key)) != null;
|
|
|
|
}
|
|
|
|
|
|
|
|
async save(key: string, obj: any): Promise<any> {
|
2022-01-13 18:03:19 +01:00
|
|
|
if (obj == null) {
|
|
|
|
return this.remove(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (obj !== null && typeof obj !== "string") {
|
2018-05-16 03:11:58 +02:00
|
|
|
throw new Error("Only string storage is allowed.");
|
|
|
|
}
|
|
|
|
const protectedObj = await this.encrypt(obj);
|
|
|
|
await this.storageService.save(this.makeProtectedStorageKey(key), protectedObj);
|
2021-12-20 18:04:00 +01:00
|
|
|
}
|
2018-05-16 03:11:58 +02:00
|
|
|
|
|
|
|
remove(key: string): Promise<any> {
|
|
|
|
return this.storageService.remove(this.makeProtectedStorageKey(key));
|
|
|
|
}
|
|
|
|
|
2021-04-14 18:44:57 +02:00
|
|
|
private async encrypt(plainValue: string): Promise<string> {
|
|
|
|
const sessionKey = this.getSessionKey();
|
2018-05-16 03:11:58 +02:00
|
|
|
if (sessionKey == null) {
|
2021-04-14 18:44:57 +02:00
|
|
|
throw new Error("No session key available.");
|
2021-12-20 18:04:00 +01:00
|
|
|
}
|
2021-04-14 18:44:57 +02:00
|
|
|
const encValue = await this.cryptoService().encryptToBytes(
|
|
|
|
Utils.fromB64ToArray(plainValue).buffer,
|
2018-05-16 03:11:58 +02:00
|
|
|
sessionKey
|
2021-12-20 18:04:00 +01:00
|
|
|
);
|
2021-04-14 18:44:57 +02:00
|
|
|
if (encValue == null) {
|
2018-05-16 03:11:58 +02:00
|
|
|
throw new Error("Value didn't encrypt.");
|
|
|
|
}
|
|
|
|
|
|
|
|
return Utils.fromBufferToB64(encValue.buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
private async decrypt(encValue: string): Promise<string> {
|
|
|
|
try {
|
|
|
|
const sessionKey = this.getSessionKey();
|
|
|
|
if (sessionKey == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2022-07-26 03:40:32 +02:00
|
|
|
const encBuf = EncArrayBuffer.fromB64(encValue);
|
|
|
|
const decValue = await this.cryptoService().decryptFromBytes(encBuf, sessionKey);
|
2018-05-16 03:11:58 +02:00
|
|
|
if (decValue == null) {
|
2020-12-14 18:29:17 +01:00
|
|
|
this.logService.info("Failed to decrypt.");
|
2018-05-16 03:11:58 +02:00
|
|
|
return null;
|
2021-12-20 18:04:00 +01:00
|
|
|
}
|
|
|
|
|
2018-05-16 03:11:58 +02:00
|
|
|
return Utils.fromBufferToB64(decValue);
|
|
|
|
} catch (e) {
|
2020-12-14 18:29:17 +01:00
|
|
|
this.logService.info("Decrypt error.");
|
2018-05-16 03:11:58 +02:00
|
|
|
return null;
|
|
|
|
}
|
2021-12-20 18:04:00 +01:00
|
|
|
}
|
2018-05-16 03:11:58 +02:00
|
|
|
|
|
|
|
private getSessionKey() {
|
|
|
|
try {
|
|
|
|
if (process.env.BW_SESSION != null) {
|
|
|
|
const sessionBuffer = Utils.fromB64ToArray(process.env.BW_SESSION).buffer;
|
|
|
|
if (sessionBuffer != null) {
|
|
|
|
const sessionKey = new SymmetricCryptoKey(sessionBuffer);
|
|
|
|
if (sessionBuffer != null) {
|
|
|
|
return sessionKey;
|
|
|
|
}
|
|
|
|
}
|
2021-12-20 18:04:00 +01:00
|
|
|
}
|
2018-05-16 03:11:58 +02:00
|
|
|
} catch (e) {
|
|
|
|
this.logService.info("Session key is invalid.");
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
2021-12-20 18:04:00 +01:00
|
|
|
}
|
|
|
|
|
2018-05-16 03:11:58 +02:00
|
|
|
private makeProtectedStorageKey(key: string) {
|
|
|
|
return "__PROTECTED__" + key;
|
|
|
|
}
|
|
|
|
}
|