1 import { systemPreferences } from 'electron';
3 import { utf8ArrayToString } from '@proton/crypto/lib/utils';
4 import { uint8ArrayToString } from '@proton/shared/lib/helpers/encoding';
6 import { biometric as macBiometrics } from '../../../native';
7 import type { BiometricsFactory, BiometricsPlatformHandler } from './types';
9 const factory: BiometricsFactory = () => {
10 /** reason is prefixed by 'Proton Pass wants to' */
11 const checkPresence = async (reason = 'unlock') => {
13 await systemPreferences.promptTouchID(reason);
20 const biometrics: BiometricsPlatformHandler = {
21 canCheckPresence: () => Promise.resolve(true),
22 checkPresence: (_, reason) => checkPresence(reason),
23 getDecryptionKey: () => Promise.resolve(null),
24 getSecret: async (_, key, version) => {
25 if (!(await checkPresence())) throw new Error('Biometric authentication failed');
27 const secretBytes = await macBiometrics.getSecret(key).catch(() => null);
28 if (!secretBytes) return null;
30 /** Version 1 (Legacy): Secrets were stored as UTF-8 encoded strings.
31 * Rust would store the bytes of this string using `as_bytes()` and
32 * retrieve using `String::from_utf8()`. As such treat the uint8 array
33 * as an utf8 array for proper string conversion */
34 if (version === 1) return utf8ArrayToString(secretBytes);
36 /* Version 2+: Secrets are stored as raw byte arrays without
37 * any conversions to and from strings */
38 return uint8ArrayToString(secretBytes);
40 setSecret: (_, key, secret) => macBiometrics.setSecret(key, secret),
41 deleteSecret: (_, key) => macBiometrics.deleteSecret(key),
46 export default factory;