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(
23 SymmetricKeyAlgorithm::XChaCha20Poly1305,
24 ))
25 }
26
27 pub fn from_context<Ids: KeySlotIds>(
30 key_to_seal: Ids::Symmetric,
31 ctx: &mut KeyStoreContext<Ids>,
32 ) -> Result<(SymmetricKeyEnvelope, SessionKey), SymmetricKeyEnvelopeError> {
33 let session_key = SessionKey::make();
34 let session_key_id = ctx.add_local_symmetric_key(session_key.0.clone());
35 let envelope = SymmetricKeyEnvelope::seal(
36 key_to_seal,
37 session_key_id,
38 SymmetricKeyEnvelopeNamespace::SessionKey,
39 ctx,
40 )?;
41 Ok((envelope, session_key))
42 }
43
44 pub fn unwrap_to_context<Ids: KeySlotIds>(
47 &self,
48 envelope: &SymmetricKeyEnvelope,
49 ctx: &mut KeyStoreContext<Ids>,
50 ) -> Result<Ids::Symmetric, SymmetricKeyEnvelopeError> {
51 let session_key_id = ctx.add_local_symmetric_key(self.0.clone());
52 envelope.unseal(
53 session_key_id,
54 SymmetricKeyEnvelopeNamespace::SessionKey,
55 ctx,
56 )
57 }
58}
59
60impl FromStr for SessionKey {
61 type Err = CryptoError;
62
63 fn from_str(s: &str) -> Result<Self, Self::Err> {
64 Ok(SessionKey(s.parse()?))
65 }
66}
67
68impl Display for SessionKey {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 self.0.to_base64().fmt(f)
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn from_str_roundtrip_recovers_session_key() {
80 let original = SessionKey::make();
81 let encoded = original.0.to_base64().to_string();
82
83 let parsed: SessionKey = encoded.parse().unwrap();
84 assert!(parsed == original);
85 }
86
87 #[test]
88 fn from_str_rejects_invalid_base64() {
89 let result: Result<SessionKey, _> = "not-a-valid-key".parse();
90 assert!(matches!(result, Err(CryptoError::InvalidKey)));
91 }
92}