Skip to content

Commit

Permalink
warn users of outdated macos on arm64
Browse files Browse the repository at this point in the history
  • Loading branch information
tutao committed Nov 24, 2023
1 parent 6dfab05 commit 522dd08
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 6 deletions.
8 changes: 3 additions & 5 deletions src/desktop/DesktopMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { DesktopTray } from "./tray/DesktopTray"
import { log } from "./DesktopLog"
import { UpdaterWrapper } from "./UpdaterWrapper"
import { ElectronNotificationFactory } from "./NotificatonFactory"
import { KeytarSecretStorage, SafeStorageSecretStorage } from "./sse/SecretStorage"
import { buildSecretStorage } from "./sse/SecretStorage"
import fs from "node:fs"
import { DesktopIntegrator, getDesktopIntegratorForPlatform } from "./integration/DesktopIntegrator"
import net from "node:net"
Expand Down Expand Up @@ -130,10 +130,8 @@ if (opts.registerAsMailHandler && opts.unregisterAsMailHandler) {
async function createComponents(): Promise<Components> {
const en = (await import("../translations/en.js")).default
lang.init(en)
const { default: keytar } = await import("keytar")
const secretStorage = new KeytarSecretStorage(keytar)
const safeStorageSecretStorage = new SafeStorageSecretStorage(electron, fs, path, secretStorage)
const keyStoreFacade = new KeyStoreFacadeImpl(safeStorageSecretStorage, desktopCrypto)
const secretStorage = await buildSecretStorage(electron, fs, path)
const keyStoreFacade = new KeyStoreFacadeImpl(secretStorage, desktopCrypto)
const configMigrator = new DesktopConfigMigrator(desktopCrypto, keyStoreFacade, electron)
const conf = new DesktopConfig(configMigrator, keyStoreFacade, desktopCrypto)
// Fire config loading, dont wait for it
Expand Down
67 changes: 66 additions & 1 deletion src/desktop/sse/SecretStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,55 @@ import * as PathModule from "node:path"
import * as FsModule from "node:fs"
import { DeviceStorageUnavailableError } from "../../api/common/error/DeviceStorageUnavailableError.js"
import type { default as Keytar } from "keytar"
import os from "node:os"

export async function buildSecretStorage(electron: typeof Electron.CrossProcessExports, fs: typeof FsModule, path: typeof PathModule): Promise<SecretStorage> {
const mode = determineMode(electron)
switch (mode) {
case "dummy":
return new SafeStorageSecretStorage(electron, fs, path, new DummySecretStorage())
case "quit":
electron.app.quit()
return new SafeStorageSecretStorage(electron, fs, path, new DummySecretStorage())
case "keytar": {
const { default: keytar } = await import("keytar")
const secretStorage = new KeytarSecretStorage(keytar)
return new SafeStorageSecretStorage(electron, fs, path, secretStorage)
}
}
}

function determineMode(electron: typeof Electron.CrossProcessExports): "dummy" | "keytar" | "quit" {
const release = Number(os.release().split(".")[0])
// on macos, the last working keytar build was for 3.118.13, which
// only supported x64. we cannot get a working keytar for old macos
// on arm64 devices, but all of them can upgrade and use the new arm64 binary.
const isBroken =
process.platform === "darwin" &&
// only arm64 must use a keytar.node post-breakage
process.arch === "arm64" &&
// basically big sur (do M1s even come with catalina?)
release < 21

if (!isBroken) return "keytar"

switch (
electron.dialog.showMessageBoxSync({
buttons: ["Don't decrypt", "Decrypt anyway", "Quit Tuta"],
cancelId: 2,
defaultId: 0,
message: "Your MacOS version is outdated and decrypting stored credentials may crash Tuta.",
title: "SecretStorage",
})
) {
case 0:
return "dummy"
case 1:
return "keytar"
default:
return "quit"
}
}

export interface SecretStorage {
getPassword(service: string, account: string): Promise<string | null>
Expand Down Expand Up @@ -66,7 +115,7 @@ export class SafeStorageSecretStorage implements SecretStorage {
private readonly electron: typeof Electron.CrossProcessExports,
private readonly fs: typeof FsModule,
private readonly path: typeof PathModule,
private readonly keytarSecretStorage: KeytarSecretStorage,
private readonly keytarSecretStorage: SecretStorage,
) {}

async getPassword(service: string, account: string): Promise<string | null> {
Expand Down Expand Up @@ -144,3 +193,19 @@ export class SafeStorageSecretStorage implements SecretStorage {
return keytarPw
}
}

class DummySecretStorage implements SecretStorage {
private readonly map: Map<string, string> = new Map()

async getPassword(service: string, account: string): Promise<string | null> {
return this.map.get(DummySecretStorage.getName(service, account)) ?? null
}

async setPassword(service: string, account: string, password: string): Promise<void> {
this.map.set(DummySecretStorage.getName(service, account), password)
}

private static getName(service: string, account: string): string {
return service.concat("-", account)
}
}

0 comments on commit 522dd08

Please sign in to comment.