Sailfish Secrets
API DocumentationContents
- Sailfish OS Secrets Architecture
- Using the Sailfish OS Secrets Library
- Client API
- Usage Examples
- Creating a block-encrypted collection of secrets
- Storing a secret into a collection
- Retrieving a secret from a collection
- Deleting a secret or a collection
- Extending the Sailfish OS Secrets and Crypto Framework with Secrets Plugins
Sailfish OS Secrets Library
The Sailfish OS Secrets Library provides applications with API to access the secure storage functionality offered by the system's secure storage and cryptography service daemon as part of the Sailfish OS Secrets and Crypto Framework.
Note: The Sailfish OS Secrets and Crypto Framework is still in active development and is subject to change. Normal 3rd party API backward compatibility promises don't hold. Follow Sailfish OS Forum release notes to get notified of API changes.
Sailfish OS Secrets Architecture
Applications running on highly-connected devices require the capability to securely store sensitive data (including certificates and cryptographic keys, user credentials, and application license data). In order to provide this capability, Sailfish OS includes a system service (extensible via vendor-specific plugins) which offers secure storage of data on behalf of client applications. Client applications access this service via the Sailfish OS Secrets Library, which provides an asynchronous API for performing a variety of operations (and hides the IPC used as an internal implementation detail).
Data storage is provided by vendor-specific plugins, and may include value-encrypted databases, block-encrypted databases, or hardware-backed secure storage. Access to secret data requires user authentication which is provided either by the system device lock, a system-mediated authentication flow, an application-mediated authentication flow, or a custom authentication flow implemented by a framework extension plugin.
Using the Sailfish OS Secrets Library
Client applications can utilise the Sailfish OS Secrets Library in order to make use of the secure storage services provided by the Sailfish OS Secrets and Crypto Framework.
This library provides client applications written in C++ (with Qt) with API to make use of the secure storage services provided by the Sailfish OS Secrets and Crypto Framework.
To make use of this library, applications should depend on the "sailfishsecrets.pc" pkgconfig file.
e.g. in a qmake-based project:
CONFIG += link_pkgconfig PKGCONFIG += sailfishsecrets INCLUDEPATH += /usr/include/Sailfish
Client API
The client API consists of a variety of C++ classes which represent the inputs to secure storage operations (including secrets of various types), the result of a secure storage operation (that is, whether it succeeded or failed, along with some information about the reason for the failure), and one class which provides an interface to the remote service.
- Sailfish::Secrets::Secret represents a (possibly partial or reference) secret
- Sailfish::Secrets::Secret::Identifier consists of a secret name and optionally a collection name
- Sailfish::Secrets::Result represents the result (but not the output) of a secure storage operation
- Sailfish::Secrets::SecretManager provides an interface to the system secure storage service
In order to perform operations with secrets, clients must request that the system service perform the operation on their behalf, and either return the result to them, or store it securely. There are a variety of request-specific classes which provide this functionality:
- Sailfish::Secrets::PluginInfoRequest to request information about available secrets plugins
- Sailfish::Secrets::CollectionNamesRequest to request the names of collections of secrets stored by the secrets service
- Sailfish::Secrets::CreateCollectionRequest to create a collection in which to store secrets
- Sailfish::Secrets::DeleteCollectionRequest to delete a collection of secrets
- Sailfish::Secrets::StoreSecretRequest to store a secret either in a collection or standalone
- Sailfish::Secrets::StoredSecretRequest to retrieve a secret
- Sailfish::Secrets::FindSecretsRequest to search a collection for secrets matching a filter
- Sailfish::Secrets::DeleteSecretRequest to delete a secret
- Sailfish::Secrets::InteractionRequest to request the system mediate a user-interaction flow on behalf of the application
In many cases, you will wish to store a secret for the user, without requiring the user to provide your application with the secret data first. This may be the case if you are storing a password or PIN code for the user, for example. In this case, you should pass appropriate user interaction parameters in a store secret request, so that the secrets service will request the secret data directly from the user prior to storing the secret.
- Sailfish::Secrets::InteractionParameters represents parameters for user input flows
There also exist some API for dealing with authentication flows which require user interaction, where that user interaction is not mediated by the system (e.g., device lock). These APIs are listed here only for the sake of completeness as most applications should never use them:
- Sailfish::Secrets::InteractionResponse represents a response to an interaction request
- Sailfish::Secrets::InteractionRequestWatcher represents a watcher on an interaction request
- Sailfish::Secrets::InteractionView provides a view for user interaction
As described previously, these APIs are only useful for applications which implement in-process authentication flows rather than using the system-mediated authentication flows, and thus only apply to device vendor- or partner-supplied applications, and even then they need not be directly used if the appropriate QML plugin is used to provide this functionality within the application.
Usage Examples
The examples directory in the source repository contains a variety of examples of usage of the Sailfish OS Crypto Library as well as the Sailfish OS Secrets Library. Please see those for complete, working examples.
Some snippets showing commonly-required functionality are included below.
Creating a block-encrypted collection of secrets
This snippet shows how to create a block-encrypted collection of secrets which will be automatically locked when the device is locked, and unlocked when the device is unlocked. The client specifies the type of storage to create the collection in, along with the required locking and access control semantics, as parameters to the call to the createCollection()
method, which results in an IPC call to the Sailfish OS Secrets and Crypto Framework system service.
The Sailfish OS Secrets and Crypto Framework system service will then delegate the operation to the specified storage plugin, which in turn will generate a block-level encrypted database file for the collection.
Note that these operations are all asynchronous, however in the snippet we force the operation to be synchronous by calling the waitForFinished()
method on the request object. In practice, the client application should instead listen to the signals which notify when the operation is complete.
Sailfish::Secrets::SecretManager sm; Sailfish::Secrets::CreateCollectionRequest ccr; ccr.setManager(&sm); ccr.setCollectionName(QLatin1String("ExampleCollection")); ccr.setAccessControlMode(Sailfish::Secrets::SecretManager::OwnerOnlyMode); ccr.setCollectionLockType(Sailfish::Secrets::CreateCollectionRequest::DeviceLock); ccr.setDeviceLockUnlockSemantic(Sailfish::Secrets::SecretManager::DeviceLockKeepUnlocked); ccr.setStoragePluginName(Sailfish::Secrets::SecretManager::DefaultEncryptedStoragePluginName); ccr.setEncryptionPluginName(Sailfish::Secrets::SecretManager::DefaultEncryptedStoragePluginName); ccr.startRequest(); ccr.waitForFinished(); if (ccr.result().code() == Sailfish::Secrets::Result::Failed) { qWarning() << "Failed to create collection:" << ccr.result().errorMessage(); }
Storing a secret into a collection
After creating a collection, an application may wish to store a secret into that collection. To do so, the client specifies the name of the collection in the identifier of the secret, along with the secret's own name, and the name of the plugin in which the collection is stored.
Once the identifier is set, the client can set the secret data into the secret, and specify some metadata and filter tags, before performing the call to request that the secret be stored in the collection by the system service.
// Define the secret. Sailfish::Secrets::Secret exampleSecret( Sailfish::Secrets::Secret::Identifier( QLatin1String("ExampleSecret"), QLatin1String("ExampleCollection"), Sailfish::Secrets::SecretManager::DefaultEncryptedStoragePluginName)); exampleSecret.setData("Some secret data"); exampleSecret.setType(Sailfish::Secrets::Secret::TypeBlob); exampleSecret.setFilterData(QLatin1String("domain"), QLatin1String("sailfishos.org")); exampleSecret.setFilterData(QLatin1String("example"), QLatin1String("true")); // Request that the secret be securely stored. Sailfish::Secrets::StoreSecretRequest ssr; ssr.setManager(&sm); ssr.setSecretStorageType(Sailfish::Secrets::StoreSecretRequest::CollectionSecret); ssr.setUserInteractionMode(Sailfish::Secrets::SecretManager::SystemInteraction); ssr.setSecret(exampleSecret); ssr.startRequest(); ssr.waitForFinished(); if (ssr.result().code() == Sailfish::Secrets::Result::Failed) { qWarning() << "Failed to store secret:" << ssr.result().errorMessage(); }
Retrieving a secret from a collection
A secret may be retrieved from a collection either by specifying the secret's identifier, or by specifying filter tags which are used to find matching secrets. The APIs are very similar, here we will show the case where the identifier is known (e.g. this would be the case if this application originally stored the secret).
Sailfish::Secrets::StoredSecretRequest gsr; gsr.setManager(&sm); gsr.setIdentifier(exampleSecret.identifier()); gsr.setUserInteractionMode(Sailfish::Secrets::SecretManager::SystemInteraction); gsr.startRequest(); gsr.waitForFinished(); if (gsr.result().code() == Sailfish::Secrets::Result::Failed) { qWarning() << "Failed to retrieve secret:" << gsr.result().errorMessage(); } else { qDebug() << "The secret data is:" << gsr.secret().data(); }
Deleting a secret or a collection
An application may want to delete a secret (or a collection of secrets) at some point, after the secret data is no longer valid. The API to delete a collection is very similar to that for deleting a secret, so only the latter will be shown below.
Sailfish::Secrets::DeleteCollectionRequest dcr; dcr.setManager(&sm); dcr.setCollectionName(QLatin1String("ExampleCollection")); dcr.setUserInteractionMode(Sailfish::Secrets::SecretManager::SystemInteraction); dcr.startRequest(); dcr.waitForFinished(); if (dcr.result().code() == Sailfish::Secrets::Result::Failed) { qWarning() << "Failed to delete secrets collection:" << dcr.result().errorMessage(); }
Extending the Sailfish OS Secrets and Crypto Framework with Secrets Plugins
The Sailfish OS Secrets Library also provides a plugin base-class which may be extended by device vendors or trusted partners to allow them to build plugins to extend the Sailfish OS Secrets and Crypto Framework with additional secure storage functionality (for example, supporting different algorithms or databases, or performing the operations via a Trusted Execution Environment application rather than in-process in the rich application process).
The following classes may be extended in order to achieve this, and the resulting plugins should be installed into the /usr/lib/Sailfish/Secrets/
directory.
- Sailfish::Secrets::PluginBase is an abstract base class which all other plugin types inherit from
- Sailfish::Secrets::EncryptionPlugin to value-encrypt secrets
- Sailfish::Secrets::StoragePlugin to store value-encrypted secrets
- Sailfish::Secrets::EncryptedStoragePlugin to store block-encrypted collections of secrets
- Sailfish::Secrets::AuthenticationPlugin to implement alternative user authentication flows
A variety of plugins are shipped by default with the framework, and these are documented at the page about Default Secrets Plugins for the Sailfish OS Secrets and Crypto Framework.