Skip to content

Commit 1bba4ad

Browse files
Merge pull request #151 from bilelmoussaoui/bilelmoussaoui/secret
client: Add a generic Secret type
2 parents 768f653 + 3fbbf46 commit 1bba4ad

30 files changed

+274
-228
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cargo-credential/src/main.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ impl SecretServiceCredential {
5050
let token = cargo_credential::read_token(options, registry)?.expose();
5151

5252
if let Some(item) = items.first() {
53-
item.set_secret(token, "text/utf8")
53+
item.set_secret(token)
5454
.await
5555
.map_err(|err| Error::Other(Box::new(err)))?;
5656
} else {
@@ -60,7 +60,6 @@ impl SecretServiceCredential {
6060
&attributes,
6161
token,
6262
true,
63-
"text/utf8",
6463
None,
6564
)
6665
.await

cli/src/main.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ impl Commands {
184184
};
185185

186186
collection
187-
.create_item(&label, &attributes, &secret, true, "text/plain", None)
187+
.create_item(&label, &attributes, secret, true, None)
188188
.await?;
189189
}
190190
Commands::Lock => collection.lock(None).await?,
@@ -245,22 +245,22 @@ async fn print_item(
245245
as_hex: bool,
246246
) -> Result<(), Error> {
247247
use std::fmt::Write;
248+
let secret = item.secret().await?;
249+
let bytes = secret.as_bytes();
248250
if secret_only {
249-
let bytes = item.secret().await?;
250251
let mut stdout = std::io::stdout().lock();
251252
if as_hex {
252-
let hex = hex::encode(&bytes);
253+
let hex = hex::encode(bytes);
253254
stdout.write_all(hex.as_bytes())?;
254255
} else {
255-
stdout.write_all(&bytes)?;
256+
stdout.write_all(bytes)?;
256257
}
257258
// Add a new line if we are writing to a tty
258259
if stdout.is_terminal() {
259260
stdout.write_all(b"\n")?;
260261
}
261262
} else {
262263
let label = item.label().await?;
263-
let bytes = item.secret().await?;
264264
let mut attributes = item.attributes().await?;
265265
let created = item.created().await?;
266266
let modified = item.modified().await?;
@@ -277,15 +277,15 @@ async fn print_item(
277277

278278
// we still fallback to hex if it is not a string
279279
if as_hex {
280-
let hex = hex::encode(&bytes);
280+
let hex = hex::encode(bytes);
281281
writeln!(&mut result, "secret = {hex}").unwrap();
282282
} else {
283-
match std::str::from_utf8(&bytes) {
283+
match std::str::from_utf8(bytes) {
284284
Ok(secret) => {
285285
writeln!(&mut result, "secret = {secret}").unwrap();
286286
}
287287
Err(_) => {
288-
let hex = hex::encode(&bytes);
288+
let hex = hex::encode(bytes);
289289
writeln!(&mut result, "secret = {hex}").unwrap();
290290
}
291291
}

client/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ digest = { version = "0.10", optional = true }
2828
endi.workspace = true
2929
futures-lite = { workspace = true, optional = true }
3030
futures-util.workspace = true
31+
getrandom = "0.2"
3132
hkdf = { version = "0.12", optional = true }
3233
hmac = { version = "0.12", optional = true }
3334
md-5 = { version = "0.10", optional = true }

client/examples/basic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ async fn main() -> oo7::Result<()> {
77
let keyring = Keyring::new().await?;
88
let attributes = HashMap::from([("attr", "value")]);
99
keyring
10-
.create_item("Some Label", &attributes, b"secret", true)
10+
.create_item("Some Label", &attributes, "secret", true)
1111
.await?;
1212

1313
let items = keyring.search_items(&attributes).await?;

client/examples/basic_2.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ async fn main() -> oo7::Result<()> {
1414
KEYRING
1515
.get()
1616
.unwrap()
17-
.create_item("Some Label", &attributes, b"secret", true)
17+
.create_item("Some Label", &attributes, "secret", true)
1818
.await?;
1919

2020
let items = KEYRING.get().unwrap().search_items(&attributes).await?;

client/src/dbus/api/collection.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use futures_util::{Stream, StreamExt};
55
use serde::Serialize;
66
use zbus::zvariant::{ObjectPath, OwnedObjectPath, Type};
77

8-
use super::{Item, Prompt, Properties, Secret, Unlockable, DESTINATION};
8+
use super::{DBusSecret, Item, Prompt, Properties, Unlockable, DESTINATION};
99
use crate::{
1010
dbus::{Error, ServiceError},
1111
AsAttributes,
@@ -165,7 +165,7 @@ impl<'a> Collection<'a> {
165165
&self,
166166
label: &str,
167167
attributes: &impl AsAttributes,
168-
secret: &Secret<'_>,
168+
secret: &DBusSecret<'_>,
169169
replace: bool,
170170
window_id: Option<WindowIdentifier>,
171171
) -> Result<Item<'a>, Error> {

client/src/dbus/api/item.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use ashpd::WindowIdentifier;
44
use serde::Serialize;
55
use zbus::zvariant::{ObjectPath, OwnedObjectPath, Type};
66

7-
use super::{secret::SecretInner, Prompt, Secret, Session, Unlockable, DESTINATION};
7+
use super::{DBusSecret, Prompt, Session, Unlockable, DESTINATION};
88
use crate::{
99
dbus::{Error, ServiceError},
1010
AsAttributes,
@@ -121,19 +121,19 @@ impl<'a> Item<'a> {
121121
}
122122

123123
#[doc(alias = "GetSecret")]
124-
pub async fn secret(&self, session: &Session<'_>) -> Result<Secret<'_>, Error> {
124+
pub async fn secret(&self, session: &Session<'_>) -> Result<DBusSecret<'_>, Error> {
125125
let inner = self
126126
.inner()
127127
.call_method("GetSecret", &(session))
128128
.await
129129
.map_err::<ServiceError, _>(From::from)?
130130
.body()
131-
.deserialize::<SecretInner>()?;
132-
Secret::from_inner(self.inner().connection(), inner).await
131+
.deserialize::<super::secret::DBusSecretInner>()?;
132+
DBusSecret::from_inner(self.inner().connection(), inner).await
133133
}
134134

135135
#[doc(alias = "SetSecret")]
136-
pub async fn set_secret(&self, secret: &Secret<'_>) -> Result<(), Error> {
136+
pub async fn set_secret(&self, secret: &DBusSecret<'_>) -> Result<(), Error> {
137137
self.inner()
138138
.call_method("SetSecret", &(secret,))
139139
.await

client/src/dbus/api/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ pub(crate) use properties::Properties;
2929
#[cfg(feature = "unstable")]
3030
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
3131
pub use properties::Properties;
32-
pub use secret::Secret;
32+
pub use secret::DBusSecret;
3333
#[cfg(feature = "unstable")]
3434
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
35-
pub use secret::SecretInner;
35+
pub use secret::DBusSecretInner;
3636
pub use service::Service;
3737
pub use session::Session;

client/src/dbus/api/secret.rs

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,21 @@ use zbus::zvariant::{OwnedObjectPath, Type};
55
use zeroize::{Zeroize, ZeroizeOnDrop};
66

77
use super::Session;
8-
use crate::{crypto, dbus::Error, Key};
8+
use crate::{
9+
crypto,
10+
dbus::Error,
11+
secret::{BLOB_CONTENT_TYPE, TEXT_CONTENT_TYPE},
12+
Key, Secret,
13+
};
914

1015
#[derive(Debug, Serialize, Deserialize, Type)]
1116
#[zvariant(signature = "(oayays)")]
12-
pub struct SecretInner(pub OwnedObjectPath, pub Vec<u8>, pub Vec<u8>, pub String);
17+
/// Same as [`DBusSecret`] without tying the session path to a [`Session`] type.
18+
pub struct DBusSecretInner(pub OwnedObjectPath, pub Vec<u8>, pub Vec<u8>, pub String);
1319

1420
#[derive(Debug, Type, Zeroize, ZeroizeOnDrop)]
1521
#[zvariant(signature = "(oayays)")]
16-
pub struct Secret<'a> {
22+
pub struct DBusSecret<'a> {
1723
#[zeroize(skip)]
1824
pub(crate) session: Arc<Session<'a>>,
1925
pub(crate) parameters: Vec<u8>,
@@ -22,47 +28,59 @@ pub struct Secret<'a> {
2228
pub(crate) content_type: String,
2329
}
2430

25-
impl<'a> Secret<'a> {
26-
pub(crate) fn new(
27-
session: Arc<Session<'a>>,
28-
secret: impl AsRef<[u8]>,
29-
content_type: &str,
30-
) -> Self {
31+
impl<'a> DBusSecret<'a> {
32+
pub(crate) fn new(session: Arc<Session<'a>>, secret: impl Into<Secret>) -> Self {
33+
let secret = secret.into();
3134
Self {
3235
session,
3336
parameters: vec![],
34-
value: secret.as_ref().to_vec(),
35-
content_type: content_type.to_owned(),
37+
value: secret.as_bytes().to_vec(),
38+
content_type: secret.content_type().to_owned(),
3639
}
3740
}
3841

3942
pub(crate) fn new_encrypted(
4043
session: Arc<Session<'a>>,
41-
secret: impl AsRef<[u8]>,
42-
content_type: &str,
44+
secret: impl Into<Secret>,
4345
aes_key: &Key,
4446
) -> Self {
4547
let iv = crypto::generate_iv();
46-
let secret = crypto::encrypt(secret.as_ref(), aes_key, &iv);
48+
let secret = secret.into();
4749
Self {
4850
session,
51+
value: crypto::encrypt(secret.as_bytes(), aes_key, &iv),
4952
parameters: iv,
50-
value: secret,
51-
content_type: content_type.to_owned(),
53+
content_type: secret.content_type().to_owned(),
5254
}
5355
}
5456

5557
pub(crate) async fn from_inner(
5658
cnx: &zbus::Connection,
57-
inner: SecretInner,
58-
) -> Result<Secret<'_>, Error> {
59-
let secret = Secret {
59+
inner: DBusSecretInner,
60+
) -> Result<Self, Error> {
61+
Ok(Self {
6062
session: Arc::new(Session::new(cnx, inner.0).await?),
6163
parameters: inner.1,
6264
value: inner.2,
6365
content_type: inner.3,
66+
})
67+
}
68+
69+
pub(crate) fn decrypt(&self, key: Option<&Arc<Key>>) -> Result<Secret, Error> {
70+
let value = match key {
71+
Some(key) => &crypto::decrypt(&self.value, key, &self.parameters),
72+
None => &self.value,
6473
};
65-
Ok(secret)
74+
75+
match self.content_type.as_str() {
76+
TEXT_CONTENT_TYPE => Ok(Secret::Text(String::from_utf8(value.to_vec())?)),
77+
BLOB_CONTENT_TYPE => Ok(Secret::blob(value)),
78+
e => {
79+
#[cfg(feature = "tracing")]
80+
tracing::warn!("Unsupported content-type {e}, falling back to blob");
81+
Ok(Secret::blob(value))
82+
}
83+
}
6684
}
6785

6886
/// Session used to encode the secret
@@ -86,7 +104,7 @@ impl<'a> Secret<'a> {
86104
}
87105
}
88106

89-
impl Serialize for Secret<'_> {
107+
impl Serialize for DBusSecret<'_> {
90108
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
91109
where
92110
S: serde::Serializer,
@@ -106,6 +124,6 @@ mod tests {
106124

107125
#[test]
108126
fn signature() {
109-
assert_eq!(Secret::SIGNATURE, "(oayays)");
127+
assert_eq!(DBusSecret::SIGNATURE, "(oayays)");
110128
}
111129
}

client/src/dbus/api/service.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ use futures_util::{Stream, StreamExt};
55
use zbus::zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Type, Value};
66

77
use super::{
8-
secret::SecretInner, Collection, Item, Prompt, Properties, Secret, Session, Unlockable,
9-
DESTINATION, PATH,
8+
Collection, DBusSecret, Item, Prompt, Properties, Session, Unlockable, DESTINATION, PATH,
109
};
1110
use crate::{
1211
dbus::{Algorithm, Error, ServiceError},
@@ -211,14 +210,14 @@ impl<'a> Service<'a> {
211210
&self,
212211
items: &[Item<'_>],
213212
session: &Session<'_>,
214-
) -> Result<HashMap<Item<'_>, Secret<'_>>, Error> {
213+
) -> Result<HashMap<Item<'_>, DBusSecret<'_>>, Error> {
215214
let secrets = self
216215
.inner()
217216
.call_method("GetSecrets", &(items, session))
218217
.await
219218
.map_err::<ServiceError, _>(From::from)?
220219
.body()
221-
.deserialize::<HashMap<OwnedObjectPath, SecretInner>>()?;
220+
.deserialize::<HashMap<OwnedObjectPath, super::secret::DBusSecretInner>>()?;
222221

223222
let cnx = self.inner().connection();
224223
// Item's Hash implementation doesn't make use of any mutable internals
@@ -227,7 +226,7 @@ impl<'a> Service<'a> {
227226
for (path, secret_inner) in secrets {
228227
output.insert(
229228
Item::new(cnx, path).await?,
230-
Secret::from_inner(cnx, secret_inner).await?,
229+
DBusSecret::from_inner(cnx, secret_inner).await?,
231230
);
232231
}
233232

client/src/dbus/collection.rs

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use tokio::sync::RwLock;
99
use zbus::zvariant::{ObjectPath, OwnedObjectPath};
1010

1111
use super::{api, Algorithm, Error, Item};
12-
use crate::{AsAttributes, Key};
12+
use crate::{AsAttributes, Key, Secret};
1313

1414
/// A collection allows to store and retrieve items.
1515
///
@@ -151,28 +151,22 @@ impl<'a> Collection<'a> {
151151
/// * `secret` - The secret to store.
152152
/// * `replace` - Whether to replace the value if the `attributes` matches
153153
/// an existing `secret`.
154-
/// * `content_type` - The content type of the secret, usually something
155-
/// like `text/plain`.
156154
pub async fn create_item(
157155
&self,
158156
label: &str,
159157
attributes: &impl AsAttributes,
160-
secret: impl AsRef<[u8]>,
158+
secret: impl Into<Secret>,
161159
replace: bool,
162-
content_type: &str,
163160
window_id: Option<WindowIdentifier>,
164161
) -> Result<Item<'a>, Error> {
165162
if !self.is_available().await {
166163
Err(Error::Deleted)
167164
} else {
168165
let secret = match self.algorithm {
169-
Algorithm::Plain => {
170-
api::Secret::new(Arc::clone(&self.session), secret, content_type)
171-
}
172-
Algorithm::Encrypted => api::Secret::new_encrypted(
166+
Algorithm::Plain => api::DBusSecret::new(Arc::clone(&self.session), secret),
167+
Algorithm::Encrypted => api::DBusSecret::new_encrypted(
173168
Arc::clone(&self.session),
174169
secret,
175-
content_type,
176170
self.aes_key.as_ref().unwrap(),
177171
),
178172
};
@@ -275,18 +269,18 @@ mod tests {
275269
"plain-type-test"
276270
};
277271
attributes.insert("type", value);
278-
let secret = "a password".as_bytes();
272+
let secret = crate::Secret::text("a password");
279273

280274
let collection = service.default_collection().await.unwrap();
281275
let n_items = collection.items().await.unwrap().len();
282276
let n_search_items = collection.search_items(&attributes).await.unwrap().len();
283277

284278
let item = collection
285-
.create_item("A secret", &attributes, secret, true, "text/plain", None)
279+
.create_item("A secret", &attributes, secret.clone(), true, None)
286280
.await
287281
.unwrap();
288282

289-
assert_eq!(*item.secret().await.unwrap(), secret);
283+
assert_eq!(item.secret().await.unwrap(), secret);
290284
assert_eq!(item.attributes().await.unwrap()["type"], value);
291285

292286
assert_eq!(collection.items().await.unwrap().len(), n_items + 1);

0 commit comments

Comments
 (0)