xdg_secret_portal_store 0.1.1
xdg_secret_portal_store: ^0.1.1 copied to clipboard
A helper library for storing application secrets in an encrypted file using the master secret provided by the XDG Desktop Portal Secret API.
A helper library for storing application secrets in an encrypted file using
the master secret provided by the XDG Desktop Portal Secret API. Designed to
complement package:xdg_desktop_portal.
To add the dependencies:
dart pub add xdg_secret_portal_store xdg_desktop_portal
Usage #
import 'package:xdg_desktop_portal/xdg_desktop_portal.dart';
import 'package:xdg_secret_portal_store/xdg_secret_portal_store.dart';
final portalClient = XdgDesktopPortalClient();
final store = XdgSecretPortalStore(
secretRetriever: portalClient.secret.retrieveSecret,
persistence: SecretStorePersistenceFile(
// Read the "File Path" section for details.
File('/path/to/application/data/secrets.json'),
),
);
await store.loadMasterSecret();
final Map<String, String> secrets = await store.read();
secrets['password'] = '123';
await store.write(secrets);
// Closes the client when no longer needed.
await portalClient.close();
Tip
This package is intended to be a helper library rather than a portal client.
The portal org.freedesktop.portal.Secret
provides a master secret for a sandboxed application.
package:xdg_desktop_portal already implements the secret portal
(XdgSecretPortal).
Unlike org.freedesktop.secrets, the Secret Portal is not a secure storage API
itself. This package provides a convenient encrypted secret store built on top
of the portal-provided master secret.
Cryptography #
The Secret Portal specification states:
The master secret can be used for encrypting confidential data, but its format is opaque to the application. In particular, the length of the secret might not be sufficient for use with certain encryption algorithms. In that case, the application is supposed to expand it using a KDF algorithm.
The default implementation of this package:
- derives a 32-byte encryption key using HKDF-SHA-256 with
xdg_secret_portal_storeas the HKDFinfovalue. - uses XChaCha20-Poly1305 for authenticated encryption of the secret store.
The cryptographic operations are implemented using
package:cryptography.
Additionally, SecretStoreCrypto can be implemented to provide a custom crypto
implementation (which can be independent of package:cryptography):
class SecretStoreCryptoCustom implements SecretStoreCrypto {
// ...
}
To override it:
final store = XdgSecretPortalStore(
crypto: SecretStoreCryptoCustom(),
);
Storage format #
Secrets are stored as a UTF-8 encoded JSON file with the following structure:
{
"version": 1,
"kdf": "HKDF-SHA256",
"cipher": "XChaCha20-Poly1305",
"nonce": "...",
"ciphertext": "...",
"mac": "..."
}
Where:
versionis the serialized storage format version.kdfidentifies the key derivation function used to derive the encryption key.cipheris the authenticated encryption (AEAD) algorithm.nonceis the Base64-encoded nonce.ciphertextis the Base64-encoded encrypted UTF-8 JSON representation of the secret map (Map<String, String>).macis the Base64-encoded message authentication code.
File Path #
This package does not define a default file path. Applications should store the encrypted secret store in their own application data directory.
A typical XDG-compatible layout is:
$XDG_DATA_HOME/$APPLICATION_ID/xdg_secret_portal_store/secrets.json.
For example, path_provider can be used:
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;
final filePath = p.join(
await getApplicationSupportPath(),
'xdg_secret_portal_store',
'secrets.json',
);
final store = XdgSecretPortalStore(
persistence: SecretStorePersistenceFile(File(filePath)),
);
Tip
To keep a package independent of Flutter, use xdg_directories, which is used by path_provider_linux.
To get the application ID on Linux, the package linux_application_id can be used.
See also: get_application_id_real.dart of path_provider_linux
Additionally, SecretStorePersistence can be implemented to provide a custom
persistence implementation (which can be independent of dart:io's File):
class SecretStorePersistenceDatabase implements SecretStorePersistence {
// ...
}
final store = XdgSecretPortalStore(
persistence: SecretStorePersistenceDatabase(),
);
Not interoperable with GNOME libsecret #
This package cannot retrieve secrets stored by GNOME libsecret.
GNOME libsecret supports multiple storage backends. The two main ones are:
If available, secrets are stored in the freedesktop secret service. Otherwise, secrets are stored in a file that is encrypted using a master secret that was provided by the secret portal.
If GNOME libsecret is using the Secret Portal backend (see secret-backend.c#L156), it stores encrypted secrets in its own encrypted file format. According to this source, the data is stored in:
$XDG_DATA_HOME/keyrings/<default-collection>.keyring
If GNOME libsecret is using the Secret Service backend, this package is also not interoperable. However,
package:freedesktop_secret is interoperable with GNOME libsecret when using that backend.
Disclaimer #
Support for this library is given as best effort.
This library has not been reviewed or vetted by security professionals.
See also #
package:freedesktop_secret: a client for the Secret Service API (org.freedesktop.secrets).
Tip
Using direct access to the Secret Service API may result in Flathub app submission not being approved. New sandboxed applications should consider the XDG Desktop Portal Secret API when available.