bitwarden_unlock/
session_key.rs1use std::{fmt::Display, str::FromStr};
2
3use bitwarden_crypto::{
4 CryptoError, KeySlotIds, KeyStoreContext, SymmetricCryptoKey, SymmetricKeyAlgorithm,
5 safe::{SymmetricKeyEnvelope, SymmetricKeyEnvelopeError, SymmetricKeyEnvelopeNamespace},
6};
7
8#[derive(PartialEq, Clone)] pub struct SessionKey(pub(crate) SymmetricCryptoKey);
18
19impl SessionKey {
20 pub fn make() -> Self {
22 Self(SymmetricCryptoKey::make(SymmetricKeyAlgorithm::XAes256Gcm))
23 }
24
25 pub fn from_context<Ids: KeySlotIds>(
28 key_to_seal: Ids::Symmetric,
29 ctx: &mut KeyStoreContext<Ids>,
30 ) -> Result<(SymmetricKeyEnvelope, SessionKey), SymmetricKeyEnvelopeError> {
31 let session_key = SessionKey::make();
32 let session_key_id = ctx.add_local_symmetric_key(session_key.0.clone());
33 let envelope = SymmetricKeyEnvelope::seal(
34 key_to_seal,
35 session_key_id,
36 SymmetricKeyEnvelopeNamespace::SessionKey,
37 ctx,
38 )?;
39 Ok((envelope, session_key))
40 }
41
42 pub fn unwrap_to_context<Ids: KeySlotIds>(
45 &self,
46 envelope: &SymmetricKeyEnvelope,
47 ctx: &mut KeyStoreContext<Ids>,
48 ) -> Result<Ids::Symmetric, SymmetricKeyEnvelopeError> {
49 let session_key_id = ctx.add_local_symmetric_key(self.0.clone());
50 envelope.unseal(
51 session_key_id,
52 SymmetricKeyEnvelopeNamespace::SessionKey,
53 ctx,
54 )
55 }
56}
57
58impl FromStr for SessionKey {
59 type Err = CryptoError;
60
61 fn from_str(s: &str) -> Result<Self, Self::Err> {
62 Ok(SessionKey(s.parse()?))
63 }
64}
65
66impl Display for SessionKey {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 self.0.to_base64().fmt(f)
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn make_uses_xaes256_gcm() {
78 assert!(matches!(
79 SessionKey::make().0,
80 SymmetricCryptoKey::XAes256GcmKey(_)
81 ));
82 }
83
84 #[test]
85 fn from_str_roundtrip_recovers_session_key() {
86 let original = SessionKey::make();
87 let encoded = original.0.to_base64().to_string();
88
89 let parsed: SessionKey = encoded.parse().unwrap();
90 assert!(parsed == original);
91 }
92
93 #[test]
94 fn from_str_rejects_invalid_base64() {
95 let result: Result<SessionKey, _> = "not-a-valid-key".parse();
96 assert!(matches!(result, Err(CryptoError::InvalidKey)));
97 }
98}