keyring_core/
api.rs

1/*!
2
3# Platform-independent secure storage model
4
5This module defines a plug and play model for credential stores.
6The model comprises two traits: [CredentialStoreApi] for
7store-level operations
8and [CredentialApi] for
9entry-level operations.  These traits must be implemented
10in a thread-safe way, a requirement captured in the [CredentialStore] and
11[Credential] types that wrap them.
12 */
13use std::any::Any;
14use std::collections::HashMap;
15use std::sync::Arc;
16
17use super::{Entry, Error, Result};
18
19/// The API that [credentials](Credential) implement.
20pub trait CredentialApi {
21    /// Set the entry's protected data to be the given string.
22    ///
23    /// This method has a default implementation in terms of
24    /// [set_secret](CredentialApi::set_secret), which see.
25    fn set_password(&self, password: &str) -> Result<()> {
26        self.set_secret(password.as_bytes())
27    }
28
29    /// Set the underlying credential's protected data to be the given byte array.
30    ///
31    /// - If the password cannot be stored in a credential,
32    ///   return an [Invalid](Error::Invalid) error.
33    /// - If the entry is a specifier, and there is no matching credential,
34    ///   create a matching credential and save the data in it.
35    /// - If the entry is a specifier, and there is more than one matching credential,
36    ///   return an [Ambiguous](Error::Ambiguous) error.
37    /// - If the entry is a wrapper, and the wrapped credential has been deleted,
38    ///   either recreate the wrapped credential and set its value or
39    ///   return a [NoEntry](Error::NoEntry) error.
40    /// - Otherwise, set the value of the single, matching credential.
41    ///
42    /// Note: If an entry is both a specifier and a wrapper, it's up to the store
43    /// whether to recreate a deleted credential or to fail with a NoEntry error.
44    fn set_secret(&self, secret: &[u8]) -> Result<()>;
45
46    /// Retrieve the protected data as a UTF-8 string from the underlying credential.
47    ///
48    /// This method has a default implementation in terms of
49    /// [get_secret](CredentialApi::get_secret), which see.
50    /// If the data in the credential is not valid UTF-8, the default implementation
51    /// returns a [BadEncoding](Error::BadEncoding) error containing the data.
52    fn get_password(&self) -> Result<String> {
53        let secret = self.get_secret()?;
54        super::error::decode_password(secret)
55    }
56
57    /// Retrieve the protected data as a byte array from the underlying credential.
58    ///
59    /// - If the entry is a specifier, and there is no matching credential,
60    ///   return a [NoEntry](Error::NoEntry) error.
61    /// - If the entry is a specifier, and there is more than one matching credential,
62    ///   return an [Ambiguous](Error::Ambiguous) error.
63    /// - If the entry is a wrapper, and the wrapped credential has been deleted,
64    ///   return a [NoEntry](Error::NoEntry) error.
65    /// - Otherwise, return the value of the single, matching credential.
66    fn get_secret(&self) -> Result<Vec<u8>>;
67
68    /// Return any store-specific decorations on this entry's credential.
69    ///
70    /// The expected error and success cases are the same as with
71    /// [get_secret](CredentialApi::get_secret), which see.
72    ///
73    /// For convenience, a default implementation of this method is
74    /// provided which doesn't return any attributes. Credential
75    /// store implementations which support attributes should
76    /// override this method.
77    fn get_attributes(&self) -> Result<HashMap<String, String>> {
78        // this should err in the same cases as get_secret, so first call that for effect
79        self.get_secret()?;
80        // if we got this far, return success with no attributes
81        Ok(HashMap::new())
82    }
83
84    /// Update the secure store attributes on this entry's credential.
85    ///
86    /// If the user supplies any attributes that cannot be updated,
87    /// return an appropriate [Invalid](Error::Invalid) error.
88    ///
89    /// Other expected error and success cases are the same as with
90    /// [get_secret](CredentialApi::get_secret), which see.
91    ///
92    /// For convenience, a default implementation of this method is
93    /// provided which returns a [NotSupportedByStore](Error::NotSupportedByStore) error.
94    fn update_attributes(&self, _: &HashMap<&str, &str>) -> Result<()> {
95        Err(Error::NotSupportedByStore(String::from(
96            "No attributes can be updated",
97        )))
98    }
99
100    /// Delete the underlying credential.
101    ///
102    /// If the underlying credential doesn't exist, return
103    /// a [NoEntry](Error::NoEntry) error.
104    ///
105    /// If there is more than one matching credential,
106    /// return an [Ambiguous](Error::Ambiguous) error.
107    fn delete_credential(&self) -> Result<()>;
108
109    /// Return a wrapper for the underlying credential.
110    ///
111    /// If `self` is already a wrapper, return None.
112    ///
113    /// If the underlying credential doesn't exist, return
114    /// a [NoEntry](Error::NoEntry) error.
115    ///
116    /// If there is more than one matching credential,
117    /// return an [Ambiguous](Error::Ambiguous) error.
118    fn get_credential(&self) -> Result<Option<Arc<Credential>>>;
119
120    /// Return the `<service, user>` pair for this credential, if any.
121    fn get_specifiers(&self) -> Option<(String, String)>;
122
123    /// Return the inner credential object cast to [Any].
124    ///
125    /// This call is used to expose the Debug trait for credentials.
126    fn as_any(&self) -> &dyn Any;
127
128    /// The Debug trait call for the object.
129    ///
130    /// This is used to implement the Debug trait on this type; it
131    /// allows generic code to provide debug printing as provided by
132    /// the underlying concrete object.
133    ///
134    /// We provide a (no-op) default implementation of this method.
135    fn debug_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136        std::fmt::Debug::fmt(self.as_any(), f)
137    }
138}
139
140/// A thread-safe implementation of the [Credential API](CredentialApi).
141pub type Credential = dyn CredentialApi + Send + Sync;
142
143impl std::fmt::Debug for Credential {
144    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145        self.debug_fmt(f)
146    }
147}
148
149/// A descriptor for the lifetime of stored credentials, returned from
150/// a credential store's [persistence](CredentialStoreApi::persistence) call.
151///
152/// This enum may change even in minor and patch versions of the library, so it's
153/// marked as non-exhaustive.
154#[non_exhaustive]
155pub enum CredentialPersistence {
156    /// Credential storage is in the entry, so storage vanishes when the entry is dropped.
157    EntryOnly,
158    /// Credential storage is in process memory,
159    /// so storage vanishes when the process terminates
160    ProcessOnly,
161    /// Credential storage is in user-space memory, so storage vanishes when the user logs out
162    UntilLogout,
163    /// Credentials stored in kernel-space memory, so storage vanishes when the machine reboots
164    UntilReboot,
165    /// Credentials stored on disk, so storage vanishes when the credential is deleted
166    UntilDelete,
167    /// Placeholder for cases not (yet) handled here
168    Unspecified,
169}
170
171/// The API that [credential stores](CredentialStore) implement.
172pub trait CredentialStoreApi {
173    /// The name of the "vendor" that provides this store.
174    ///
175    /// This allows clients to conditionalize their code for specific vendors.
176    /// This string should not vary with versions of the store. It's recommended
177    /// that it include the crate URL for the module provider.
178    fn vendor(&self) -> String;
179
180    /// The ID of this credential store instance.
181    ///
182    /// IDs need not be unique across vendors or processes, but they
183    /// serve as instance IDs within a process.  If two credential store
184    /// instances in a process have the same vendor and id,
185    /// then they are the same instance.
186    ///
187    /// It's recommended that this include the version of the provider.
188    fn id(&self) -> String;
189
190    /// Create an entry specified by the given service and user,
191    /// perhaps with additional creation-time modifiers.
192    ///
193    /// The credential returned from this call must be a specifier,
194    /// meaning that it can be used to create a credential later
195    /// even if a matching credential existed in the store .
196    ///
197    /// This typically has no effect on the content of the underlying store.
198    /// A credential need not be persisted until its password is set.
199    fn build(
200        &self,
201        service: &str,
202        user: &str,
203        modifiers: Option<&HashMap<&str, &str>>,
204    ) -> Result<Entry>;
205
206    /// Search for credentials that match the given spec.
207    ///
208    /// Returns a list of the matching credentials.
209    ///
210    /// Should return an [Invalid](Error::Invalid) error if the spec is bad.
211    ///
212    /// The default implementation returns a
213    /// [NotSupportedByStore](Error::NotSupportedByStore) error; that is,
214    /// credential stores need not provide support for search.
215    fn search(&self, _spec: &HashMap<&str, &str>) -> Result<Vec<Entry>> {
216        let vendor = self.vendor();
217        Err(Error::NotSupportedByStore(vendor))
218    }
219
220    /// Return the inner store object cast to [Any].
221    ///
222    /// This call is used to expose the Debug trait for stores.
223    fn as_any(&self) -> &dyn Any;
224
225    /// The lifetime of credentials produced by this builder.
226    ///
227    /// A default implementation is provided for backward compatibility,
228    /// since this API was added in a minor release.  The default assumes
229    /// that keystores use disk-based credential storage.
230    fn persistence(&self) -> CredentialPersistence {
231        CredentialPersistence::UntilDelete
232    }
233
234    /// The Debug trait call for the object.
235    ///
236    /// This is used to implement the Debug trait on this type; it
237    /// allows generic code to provide debug printing as provided by
238    /// the underlying concrete object.
239    ///
240    /// We provide a (no-op) default implementation of this method.
241    fn debug_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
242        std::fmt::Debug::fmt(self.as_any(), f)
243    }
244}
245
246impl std::fmt::Debug for CredentialStore {
247    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
248        self.debug_fmt(f)
249    }
250}
251
252/// A thread-safe implementation of the [CredentialBuilder API](CredentialStoreApi).
253pub type CredentialStore = dyn CredentialStoreApi + Send + Sync;