bitwarden-estensione-browser/apps/desktop/src/main/desktop-credential-storage-...

133 lines
4.4 KiB
TypeScript

import { ipcMain } from "electron";
import { BiometricKey } from "@bitwarden/common/auth/types/biometric-key";
import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service";
import { passwords } from "@bitwarden/desktop-native";
import { BiometricMessage, BiometricStorageAction } from "../types/biometric-message";
import { BiometricsServiceAbstraction } from "./biometric/index";
const AuthRequiredSuffix = "_biometric";
export class DesktopCredentialStorageListener {
constructor(
private serviceName: string,
private biometricService: BiometricsServiceAbstraction,
private logService: ConsoleLogService
) {}
init() {
ipcMain.handle("keytar", async (event: any, message: any) => {
try {
let serviceName = this.serviceName;
message.keySuffix = "_" + (message.keySuffix ?? "");
if (message.keySuffix !== "_") {
serviceName += message.keySuffix;
}
let val: string | boolean = null;
if (message.action && message.key) {
if (message.action === "getPassword") {
val = await this.getPassword(serviceName, message.key, message.keySuffix);
} else if (message.action === "hasPassword") {
const result = await passwords.getPassword(serviceName, message.key);
val = result != null;
} else if (message.action === "setPassword" && message.value) {
await this.setPassword(serviceName, message.key, message.value, message.keySuffix);
} else if (message.action === "deletePassword") {
await this.deletePassword(serviceName, message.key, message.keySuffix);
}
}
return val;
} catch (e) {
if (
e.message === "Password not found." ||
e.message === "The specified item could not be found in the keychain."
) {
return null;
}
this.logService.info(e);
}
});
ipcMain.handle("biometric", async (event: any, message: BiometricMessage) => {
try {
let serviceName = this.serviceName;
message.keySuffix = "_" + (message.keySuffix ?? "");
if (message.keySuffix !== "_") {
serviceName += message.keySuffix;
}
let val: string | boolean = null;
if (!message.action) {
return val;
}
switch (message.action) {
case BiometricStorageAction.EnabledForUser:
if (!message.key || !message.userId) {
break;
}
val = await this.biometricService.canAuthBiometric({
service: serviceName,
key: message.key,
userId: message.userId,
});
break;
case BiometricStorageAction.OsSupported:
val = await this.biometricService.osSupportsBiometric();
break;
default:
}
return val;
} catch (e) {
this.logService.info(e);
}
});
}
// Gracefully handle old keytar values, and if detected updated the entry to the proper format
private async getPassword(serviceName: string, key: string, keySuffix: string) {
let val: string;
if (keySuffix === AuthRequiredSuffix) {
val = (await this.biometricService.getBiometricKey(serviceName, key)) ?? null;
} else {
val = await passwords.getPassword(serviceName, key);
}
try {
JSON.parse(val);
} catch (e) {
throw new Error("Password in bad format" + e + val);
}
return val;
}
private async setPassword(serviceName: string, key: string, value: string, keySuffix: string) {
if (keySuffix === AuthRequiredSuffix) {
const valueObj = JSON.parse(value) as BiometricKey;
await this.biometricService.setEncryptionKeyHalf({
service: serviceName,
key,
value: valueObj?.clientEncKeyHalf,
});
// Value is usually a JSON string, but we need to pass the key half as well, so we re-stringify key here.
await this.biometricService.setBiometricKey(serviceName, key, JSON.stringify(valueObj?.key));
} else {
await passwords.setPassword(serviceName, key, value);
}
}
private async deletePassword(serviceName: string, key: string, keySuffix: string) {
if (keySuffix === AuthRequiredSuffix) {
await this.biometricService.deleteBiometricKey(serviceName, key);
} else {
await passwords.deletePassword(serviceName, key);
}
}
}