@@ -44,6 +44,11 @@ export class ElectronSafeStorageDecryptError extends Schema.TaggedError<Electron
4444 }
4545}
4646
47+ // Chromium's v10 Linux prefix marks ciphertext under the fixed "peanuts" key. The async
48+ // API falls back to it when the keyring provider fails or is cancelled, while still
49+ // reporting the keyring backend, so it would store credentials effectively in plaintext.
50+ const LINUX_FALLBACK_CIPHERTEXT_PREFIX = Buffer . from ( "v10" ) ;
51+
4752export const ElectronSafeStorageError = Schema . Union ( [
4853 ElectronSafeStorageAvailabilityError ,
4954 ElectronSafeStorageEncryptError ,
@@ -96,10 +101,21 @@ export const make = Effect.gen(function* () {
96101 } ) ,
97102 encryptString : ( value ) =>
98103 Effect . tryPromise ( {
99- try : async ( ) =>
100- ( await isAsyncEncryptionAvailable ( ) )
101- ? Electron . safeStorage . encryptStringAsync ( value )
102- : Electron . safeStorage . encryptString ( value ) ,
104+ try : async ( ) => {
105+ if ( ! ( await isAsyncEncryptionAvailable ( ) ) ) {
106+ return Electron . safeStorage . encryptString ( value ) ;
107+ }
108+ const encrypted = await Electron . safeStorage . encryptStringAsync ( value ) ;
109+ if (
110+ platform === "linux" &&
111+ encrypted
112+ . subarray ( 0 , LINUX_FALLBACK_CIPHERTEXT_PREFIX . length )
113+ . equals ( LINUX_FALLBACK_CIPHERTEXT_PREFIX )
114+ ) {
115+ throw new Error ( "The keyring fell back to an insecure fixed encryption key." ) ;
116+ }
117+ return encrypted ;
118+ } ,
103119 catch : ( cause ) => new ElectronSafeStorageEncryptError ( { cause } ) ,
104120 } ) ,
105121 decryptString : ( value ) =>
0 commit comments