xdg_secret_portal_store 0.2.1
xdg_secret_portal_store: ^0.2.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_secret_portal_store_default xdg_desktop_portal
Usage #
import 'package:xdg_desktop_portal/xdg_desktop_portal.dart';
import 'package:xdg_secret_portal_store/xdg_secret_portal_store.dart';
import 'package:xdg_secret_portal_store_default/xdg_secret_portal_store_default.dart' show SecretStoreCryptoDefault;
final portalClient = XdgDesktopPortalClient();
final store = XdgSecretPortalStore(
masterSecretRetriever: portalClient.secret.retrieveSecret,
persistence: SecretStorePersistenceFile(
// Read the "File Path" section for details.
File('/path/to/application/data/secrets.json'),
),
// Read the "Cryptography" section for details.
crypto: SecretStoreCryptoDefault(),
);
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 #
This package does not provide a default cryptographic implementation. A SecretStoreCrypto implementation must be supplied, either by using package:xdg_secret_portal_store_default or by providing a custom implementation.
The cryptographic implementation and its security considerations are documented in the README of package:xdg_secret_portal_store_default.
Note
When a custom implementation is used, the consumer
is responsible for compatibility and migrations. xdg_secret_portal_store and
its companion package cannot assume the implementation details.
Custom implementation #
In this case, package:xdg_secret_portal_store_default is not required.
To use a different crypto library or a different algorithm:
import 'package:xdg_secret_portal_store/xdg_secret_portal_store.dart';
class SecretStoreCryptoCustom implements SecretStoreCrypto {
// ...
}
To override it:
final store = XdgSecretPortalStore(
crypto: SecretStoreCryptoCustom(),
);
Tip
Consumers are strongly encouraged to read the Secret Portal specification.
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. Also known as the authentication tag.
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.
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.