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;