bitwarden_crypto/
content_format.rs

1use bitwarden_encoding::B64;
2use serde::{Deserialize, Serialize};
3
4use crate::{
5    CryptoError, EncString, KeyEncryptable, KeyEncryptableWithContentType, KeyIds, KeyStoreContext,
6    PrimitiveEncryptable, SymmetricCryptoKey, traits::PrimitiveEncryptableWithContentType,
7};
8
9/// The content format describes the format of the contained bytes. Message encryption always
10/// happens on the byte level, and this allows determining what format the contained data has. For
11/// instance, an `EncString` in most cases contains UTF-8 encoded text. In some cases it may contain
12/// a Pkcs8 private key, or a COSE key. Specifically, for COSE keys, this allows distinguishing
13/// between the old symmetric key format, represented as `ContentFormat::OctetStream`, and the new
14/// COSE key format, represented as `ContentFormat::CoseKey`.
15#[derive(Clone, Copy, Debug, PartialEq)]
16pub(crate) enum ContentFormat {
17    /// UTF-8 encoded text
18    Utf8,
19    /// Pkcs8 private key DER
20    Pkcs8PrivateKey,
21    /// SPKI public key DER
22    SPKIPublicKeyDer,
23    /// COSE serialized CoseKey
24    CoseKey,
25    /// CoseSign1 message
26    CoseSign1,
27    /// CoseEncrypt0 message
28    CoseEncrypt0,
29    /// Bitwarden Legacy Key
30    /// There are three permissible byte values here:
31    /// - `[u8; 32]` - AES-CBC (no hmac) key. This is to be removed and banned.
32    /// - `[u8; 64]` - AES-CBC with HMAC key. This is the v1 userkey key type
33    /// - `[u8; >64]` - COSE key. Padded to be larger than 64 bytes.
34    BitwardenLegacyKey,
35    /// Stream of bytes
36    OctetStream,
37    /// Cbor serialized data
38    Cbor,
39}
40
41mod private {
42    /// This trait is used to seal the `ConstContentFormat` trait, preventing external
43    /// implementations.
44    pub trait Sealed {}
45}
46
47/// This trait is used to instantiate different typed byte vectors with a specific content format,
48/// using `SerializedBytes<C>`. This allows for compile-time guarantees about the content format
49/// of the serialized bytes. The exception here is the escape hatch using e.g. `from(Vec<u8>)`,
50/// which can still be mis-used, but has to be misused explicitly.
51pub trait ConstContentFormat: private::Sealed {
52    /// Returns the content format as a `ContentFormat` enum.
53    #[allow(private_interfaces)]
54    fn content_format() -> ContentFormat;
55}
56
57/// A serialized byte array with a specific content format. This is used to represent data that has
58/// a specific format, such as UTF-8 encoded text, raw bytes, or COSE keys. The content
59/// format is used to determine how the bytes should be interpreted when encrypting or decrypting
60/// the data.
61#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
62pub struct Bytes<C: ConstContentFormat> {
63    inner: Vec<u8>,
64    _marker: std::marker::PhantomData<C>,
65}
66
67impl<C: ConstContentFormat> From<Vec<u8>> for Bytes<C> {
68    fn from(inner: Vec<u8>) -> Self {
69        Self {
70            inner,
71            _marker: std::marker::PhantomData,
72        }
73    }
74}
75
76impl<C: ConstContentFormat> From<&[u8]> for Bytes<C> {
77    fn from(inner: &[u8]) -> Self {
78        Self::from(inner.to_vec())
79    }
80}
81
82impl<C: ConstContentFormat + FromB64ContentFormat> From<&B64> for Bytes<C> {
83    fn from(val: &B64) -> Self {
84        Self::from(val.as_bytes())
85    }
86}
87
88impl<C: ConstContentFormat + FromB64ContentFormat> From<Bytes<C>> for B64 {
89    fn from(val: Bytes<C>) -> Self {
90        B64::from(val.as_ref())
91    }
92}
93
94impl<C: ConstContentFormat> AsRef<[u8]> for Bytes<C> {
95    fn as_ref(&self) -> &[u8] {
96        &self.inner
97    }
98}
99
100impl<C: ConstContentFormat> Bytes<C> {
101    /// Returns the serialized bytes as a `Vec<u8>`.
102    pub fn to_vec(&self) -> Vec<u8> {
103        self.inner.clone()
104    }
105}
106
107/// Content format for UTF-8 encoded text. Used for most text messages.
108#[derive(PartialEq, Eq, Clone, Debug)]
109pub(crate) struct Utf8ContentFormat;
110impl private::Sealed for Utf8ContentFormat {}
111impl ConstContentFormat for Utf8ContentFormat {
112    fn content_format() -> ContentFormat {
113        ContentFormat::Utf8
114    }
115}
116/// Utf8Bytes is a type alias for Bytes with `Utf8ContentFormat`, which is used for any textual
117/// data.
118pub(crate) type Utf8Bytes = Bytes<Utf8ContentFormat>;
119
120/// Content format for raw bytes. Used for attachments and send seed keys.
121#[derive(PartialEq, Eq, Clone, Debug)]
122pub struct OctetStreamContentFormat;
123impl private::Sealed for OctetStreamContentFormat {}
124impl ConstContentFormat for OctetStreamContentFormat {
125    #[allow(private_interfaces)]
126    fn content_format() -> ContentFormat {
127        ContentFormat::OctetStream
128    }
129}
130/// OctetStreamBytes is a type alias for Bytes with `OctetStreamContentFormat`. This should be used
131/// for e.g. attachments and other data without an explicit content format.
132pub type OctetStreamBytes = Bytes<OctetStreamContentFormat>;
133
134/// Content format for PKCS8 private keys in DER format.
135#[derive(PartialEq, Eq, Clone, Debug)]
136pub struct Pkcs8PrivateKeyDerContentFormat;
137impl private::Sealed for Pkcs8PrivateKeyDerContentFormat {}
138impl ConstContentFormat for Pkcs8PrivateKeyDerContentFormat {
139    #[allow(private_interfaces)]
140    fn content_format() -> ContentFormat {
141        ContentFormat::Pkcs8PrivateKey
142    }
143}
144/// Pkcs8PrivateKeyBytes is a type alias for Bytes with `Pkcs8PrivateKeyDerContentFormat`. This is
145/// used for PKCS8 private keys in DER format.
146pub type Pkcs8PrivateKeyBytes = Bytes<Pkcs8PrivateKeyDerContentFormat>;
147
148/// Content format for SPKI public keys in DER format.
149#[derive(PartialEq, Eq, Clone, Debug)]
150pub struct SpkiPublicKeyDerContentFormat;
151impl private::Sealed for SpkiPublicKeyDerContentFormat {}
152impl ConstContentFormat for SpkiPublicKeyDerContentFormat {
153    #[allow(private_interfaces)]
154    fn content_format() -> ContentFormat {
155        ContentFormat::SPKIPublicKeyDer
156    }
157}
158impl FromB64ContentFormat for SpkiPublicKeyDerContentFormat {}
159/// SpkiPublicKeyBytes is a type alias for Bytes with `SpkiPublicKeyDerContentFormat`. This is used
160/// for SPKI public keys in DER format.
161pub type SpkiPublicKeyBytes = Bytes<SpkiPublicKeyDerContentFormat>;
162
163/// A marker trait for COSE content formats.
164pub trait CoseContentFormat {}
165
166/// A marker trait for content formats that can be converted from B64.
167pub trait FromB64ContentFormat {}
168
169/// Content format for COSE keys.
170#[derive(PartialEq, Eq, Clone, Debug)]
171pub struct CoseKeyContentFormat;
172impl private::Sealed for CoseKeyContentFormat {}
173impl ConstContentFormat for CoseKeyContentFormat {
174    #[allow(private_interfaces)]
175    fn content_format() -> ContentFormat {
176        ContentFormat::CoseKey
177    }
178}
179impl CoseContentFormat for CoseKeyContentFormat {}
180impl FromB64ContentFormat for CoseKeyContentFormat {}
181/// CoseKeyBytes is a type alias for Bytes with `CoseKeyContentFormat`. This is used for serialized
182/// CoseKey objects.
183pub type CoseKeyBytes = Bytes<CoseKeyContentFormat>;
184
185/// A legacy content format for Bitwarden keys. See `ContentFormat::BitwardenLegacyKey`
186#[derive(PartialEq, Eq, Clone, Debug)]
187pub struct BitwardenLegacyKeyContentFormat;
188impl private::Sealed for BitwardenLegacyKeyContentFormat {}
189impl ConstContentFormat for BitwardenLegacyKeyContentFormat {
190    #[allow(private_interfaces)]
191    fn content_format() -> ContentFormat {
192        ContentFormat::BitwardenLegacyKey
193    }
194}
195impl FromB64ContentFormat for BitwardenLegacyKeyContentFormat {}
196/// BitwardenLegacyKeyBytes is a type alias for Bytes with `BitwardenLegacyKeyContentFormat`. This
197/// is used for the legacy format for symmetric keys. A description of the format is available in
198/// the `ContentFormat::BitwardenLegacyKey` documentation.
199pub type BitwardenLegacyKeyBytes = Bytes<BitwardenLegacyKeyContentFormat>;
200
201/// Content format for COSE Sign1 messages.
202#[derive(PartialEq, Eq, Clone, Debug)]
203pub struct CoseSign1ContentFormat;
204impl private::Sealed for CoseSign1ContentFormat {}
205impl ConstContentFormat for CoseSign1ContentFormat {
206    #[allow(private_interfaces)]
207    fn content_format() -> ContentFormat {
208        ContentFormat::CoseSign1
209    }
210}
211impl CoseContentFormat for CoseSign1ContentFormat {}
212impl FromB64ContentFormat for CoseSign1ContentFormat {}
213/// CoseSign1Bytes is a type alias for Bytes with `CoseSign1ContentFormat`. This is used for
214/// serialized COSE Sign1 messages.
215pub type CoseSign1Bytes = Bytes<CoseSign1ContentFormat>;
216
217/// CBOR serialized data
218#[derive(PartialEq, Eq, Clone, Debug)]
219pub struct CborContentFormat;
220impl private::Sealed for CborContentFormat {}
221impl ConstContentFormat for CborContentFormat {
222    #[allow(private_interfaces)]
223    fn content_format() -> ContentFormat {
224        ContentFormat::Cbor
225    }
226}
227/// CborBytes is a type alias for Bytes with `CborContentFormat`. This is used for CBOR serialized
228/// data.
229pub type CborBytes = Bytes<CborContentFormat>;
230
231/// Content format for COSE Encrypt0 messages.
232#[derive(PartialEq, Eq, Clone, Debug)]
233pub struct CoseEncrypt0ContentFormat;
234impl private::Sealed for CoseEncrypt0ContentFormat {}
235impl ConstContentFormat for CoseEncrypt0ContentFormat {
236    #[allow(private_interfaces)]
237    fn content_format() -> ContentFormat {
238        ContentFormat::CoseEncrypt0
239    }
240}
241/// CoseEncrypt0Bytes is a type alias for Bytes with `CoseEncrypt0ContentFormat`. This is used for
242/// serialized COSE Encrypt0 messages.
243pub type CoseEncrypt0Bytes = Bytes<CoseEncrypt0ContentFormat>;
244
245impl<Ids: KeyIds, T: ConstContentFormat> PrimitiveEncryptable<Ids, Ids::Symmetric, EncString>
246    for Bytes<T>
247{
248    fn encrypt(
249        &self,
250        ctx: &mut KeyStoreContext<Ids>,
251        key: Ids::Symmetric,
252    ) -> Result<EncString, CryptoError> {
253        self.inner.encrypt(ctx, key, T::content_format())
254    }
255}
256
257impl<T: ConstContentFormat> KeyEncryptable<SymmetricCryptoKey, EncString> for &Bytes<T> {
258    fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result<EncString, CryptoError> {
259        self.as_ref().encrypt_with_key(key, T::content_format())
260    }
261}
262
263impl From<String> for Bytes<Utf8ContentFormat> {
264    fn from(val: String) -> Self {
265        Bytes::from(val.into_bytes())
266    }
267}
268
269impl From<&str> for Bytes<Utf8ContentFormat> {
270    fn from(val: &str) -> Self {
271        Bytes::from(val.as_bytes().to_vec())
272    }
273}