electron: [Bug]: safeStorage.decryptString - Error while decrypting the ciphertext

Preflight Checklist

Electron Version

16.0.7

What operating system are you using?

macOS

Operating System Version

macOS Monterey 12.1

What arch are you using?

x64

Last Known Working Electron version

No response

Expected Behavior

safeStorage.decryptString(buffer) should decrypt a previously encrypted string (using safeStorage.encryptString(plainText))

Given the following module for storing settings for an application.

Settings are stored to a file and retrieved on next app start.

Sensitive information, e.g. personalAccessToken should only be stored encrypted.

const { join } = require('path')
const { existsSync, promises: { readFile, writeFile } } = require('fs')
const { app, safeStorage } = require('electron')
const { Buffer } = require('buffer')

const userData = app.getPath('userData')
const settingsPath = join(userData, 'settings.json')

const defaultSettings = {
  baseUrl: '',
  personalAccessToken: '',
  fetchInterval: 5
}

const encrypt = plainText => {
  const buffer = safeStorage.encryptString(plainText)
  const encrypted = buffer.toString('base64')
  return encrypted
}

const decrypt = encrypted => {
  const buffer = Buffer.from(encrypted, 'base64')
  const plainText = safeStorage.decryptString(buffer)
  return plainText
}

module.exports = {
  async save ({ baseUrl, personalAccessToken, fetchInterval }) {
    const json = JSON.stringify({
      baseUrl,
      personalAccessToken: encrypt(personalAccessToken),
      fetchInterval
    })
    await writeFile(settingsPath, json, 'utf8')
  },

  async load () {
    if (!existsSync(settingsPath)) { 
      return defaultSettings
    }
    const json = await readFile(settingsPath, 'utf8')
    const { baseUrl, personalAccessToken, fetchInterval } = JSON.parse(json)
    return {
      ...defaultSettings,
      baseUrl,
      personalAccessToken: decrypt(personalAccessToken),
      fetchInterval
    }
  }
}

Actual Behavior

safeStorage.encryptString(plainText) works as expected but whenever a safeStorage.decryptString(buffer) is called it throws an error:

Error while decrypting the ciphertext provided to safeStorage.decryptString.

Testcase Gist URL

No response

Additional Information

No response

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 3
  • Comments: 23 (5 by maintainers)

Most upvoted comments

Does this potentially have to do with saving the encrypted string to a file, e.g. using electron-store? In my case I can run this part correctly:

var testEncrypted = safeStorage.encryptString('test')
var testDecrypted = safeStorage.decryptString(testEncrypted) // returns "test"

But once I round-trip through electron-store, it no longer is able to decrypt the string.

Does this potentially have to do with saving the encrypted string to a file, e.g. using electron-store? In my case I can run this part correctly:

var testEncrypted = safeStorage.encryptString('test')
var testDecrypted = safeStorage.decryptString(testEncrypted) // returns "test"

But once I round-trip through electron-store, it no longer is able to decrypt the string.

I resolved this issue by saving encrypted string with base64 encoding (toString has utf8 encoding by default, and with it not working for me, too)

   configStore.set('secret', safeStorage.encryptString("test").toString('base64'));

    const secret : string = configStore.get('secret') as string;
    console.log('decrypted data: ' + safeStorage.decryptString(Buffer.from(secret, 'base64')));
   //print decrypted data: test

What password manager is electron looking for exactly?

* `basic_text` - When the desktop environment is not recognised or if the following
command line flag is provided `--password-store="basic"`.
* `gnome_libsecret` - When the desktop environment is `X-Cinnamon`, `Deepin`, `GNOME`, `Pantheon`, `XFCE`, `UKUI`, `unity` or if the following command line flag is provided `--password-store="gnome-libsecret"`.
* `kwallet` - When the desktop session is `kde4` or if the following command line flag
is provided `--password-store="kwallet"`.
* `kwallet5` - When the desktop session is `kde5` or if the following command line flag
is provided `--password-store="kwallet5"`.
* `kwallet6` - When the desktop session is `kde6`.
* `unknown` - When the function is called before app has emitted the `ready` event.

Try to use safeStorage after any browserWindow created. It helped me to figure out with the same error on MacOs.