bitwarden_crypto/
content_format.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{
4    traits::PrimitiveEncryptableWithContentType, CryptoError, EncString, KeyEncryptable,
5    KeyEncryptableWithContentType, KeyIds, KeyStoreContext, PrimitiveEncryptable,
6    SymmetricCryptoKey,
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    /// Bitwarden Legacy Key
28    /// There are three permissible byte values here:
29    /// - `[u8; 32]` - AES-CBC (no hmac) key. This is to be removed and banned.
30    /// - `[u8; 64]` - AES-CBC with HMAC key. This is the v1 userkey key type
31    /// - `[u8; >64]` - COSE key. Padded to be larger than 64 bytes.
32    BitwardenLegacyKey,
33    /// Stream of bytes
34    OctetStream,
35}
36
37mod private {
38    /// This trait is used to seal the `ConstContentFormat` trait, preventing external
39    /// implementations.
40    pub trait Sealed {}
41}
42
43/// This trait is used to instantiate different typed byte vectors with a specific content format,
44/// using `SerializedBytes<C>`. This allows for compile-time guarantees about the content format
45/// of the serialized bytes. The exception here is the escape hatch using e.g. `from(Vec<u8>)`,
46/// which can still be mis-used, but has to be misused explicitly.
47pub trait ConstContentFormat: private::Sealed {
48    /// Returns the content format as a `ContentFormat` enum.
49    #[allow(private_interfaces)]
50    fn content_format() -> ContentFormat;
51}
52
53/// A serialized byte array with a specific content format. This is used to represent data that has
54/// a specific format, such as UTF-8 encoded text, raw bytes, or COSE keys. The content
55/// format is used to determine how the bytes should be interpreted when encrypting or decrypting
56/// the data.
57#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
58pub struct Bytes<C: ConstContentFormat> {
59    inner: Vec<u8>,
60    _marker: std::marker::PhantomData<C>,
61}
62
63impl<C: ConstContentFormat> From<Vec<u8>> for Bytes<C> {
64    fn from(inner: Vec<u8>) -> Self {
65        Self {
66            inner,
67            _marker: std::marker::PhantomData,
68        }
69    }
70}
71
72impl<C: ConstContentFormat> From<&[u8]> for Bytes<C> {
73    fn from(inner: &[u8]) -> Self {
74        Self::from(inner.to_vec())
75    }
76}
77
78impl<C: ConstContentFormat> AsRef<[u8]> for Bytes<C> {
79    fn as_ref(&self) -> &[u8] {
80        &self.inner
81    }
82}
83
84impl<C: ConstContentFormat> Bytes<C> {
85    /// Returns the serialized bytes as a `Vec<u8>`.
86    pub fn to_vec(&self) -> Vec<u8> {
87        self.inner.clone()
88    }
89}
90
91/// Content format for UTF-8 encoded text. Used for most text messages.
92#[derive(PartialEq, Eq, Clone, Debug)]
93pub(crate) struct Utf8ContentFormat;
94impl private::Sealed for Utf8ContentFormat {}
95impl ConstContentFormat for Utf8ContentFormat {
96    fn content_format() -> ContentFormat {
97        ContentFormat::Utf8
98    }
99}
100/// Utf8Bytes is a type alias for Bytes with `Utf8ContentFormat`, which is used for any textual
101/// data.
102pub(crate) type Utf8Bytes = Bytes<Utf8ContentFormat>;
103
104/// Content format for raw bytes. Used for attachments and send seed keys.
105#[derive(PartialEq, Eq, Clone, Debug)]
106pub struct OctetStreamContentFormat;
107impl private::Sealed for OctetStreamContentFormat {}
108impl ConstContentFormat for OctetStreamContentFormat {
109    #[allow(private_interfaces)]
110    fn content_format() -> ContentFormat {
111        ContentFormat::OctetStream
112    }
113}
114/// OctetStreamBytes is a type alias for Bytes with `OctetStreamContentFormat`. This should be used
115/// for e.g. attachments and other data without an explicit content format.
116pub type OctetStreamBytes = Bytes<OctetStreamContentFormat>;
117
118/// Content format for PKCS8 private keys in DER format.
119#[derive(PartialEq, Eq, Clone, Debug)]
120pub struct Pkcs8PrivateKeyDerContentFormat;
121impl private::Sealed for Pkcs8PrivateKeyDerContentFormat {}
122impl ConstContentFormat for Pkcs8PrivateKeyDerContentFormat {
123    #[allow(private_interfaces)]
124    fn content_format() -> ContentFormat {
125        ContentFormat::Pkcs8PrivateKey
126    }
127}
128/// Pkcs8PrivateKeyBytes is a type alias for Bytes with `Pkcs8PrivateKeyDerContentFormat`. This is
129/// used for PKCS8 private keys in DER format.
130pub type Pkcs8PrivateKeyBytes = Bytes<Pkcs8PrivateKeyDerContentFormat>;
131
132/// Content format for SPKI public keys in DER format.
133#[derive(PartialEq, Eq, Clone, Debug)]
134pub struct SpkiPublicKeyDerContentFormat;
135impl private::Sealed for SpkiPublicKeyDerContentFormat {}
136impl ConstContentFormat for SpkiPublicKeyDerContentFormat {
137    #[allow(private_interfaces)]
138    fn content_format() -> ContentFormat {
139        ContentFormat::SPKIPublicKeyDer
140    }
141}
142/// SpkiPublicKeyBytes is a type alias for Bytes with `SpkiPublicKeyDerContentFormat`. This is used
143/// for SPKI public keys in DER format.
144pub type SpkiPublicKeyBytes = Bytes<SpkiPublicKeyDerContentFormat>;
145
146/// A marker trait for COSE content formats.
147pub trait CoseContentFormat {}
148
149/// Content format for COSE keys.
150#[derive(PartialEq, Eq, Clone, Debug)]
151pub struct CoseKeyContentFormat;
152impl private::Sealed for CoseKeyContentFormat {}
153impl ConstContentFormat for CoseKeyContentFormat {
154    #[allow(private_interfaces)]
155    fn content_format() -> ContentFormat {
156        ContentFormat::CoseKey
157    }
158}
159impl CoseContentFormat for CoseKeyContentFormat {}
160/// CoseKeyBytes is a type alias for Bytes with `CoseKeyContentFormat`. This is used for serialized
161/// CoseKey objects.
162pub type CoseKeyBytes = Bytes<CoseKeyContentFormat>;
163
164/// A legacy content format for Bitwarden keys. See `ContentFormat::BitwardenLegacyKey`
165#[derive(PartialEq, Eq, Clone, Debug)]
166pub struct BitwardenLegacyKeyContentFormat;
167impl private::Sealed for BitwardenLegacyKeyContentFormat {}
168impl ConstContentFormat for BitwardenLegacyKeyContentFormat {
169    #[allow(private_interfaces)]
170    fn content_format() -> ContentFormat {
171        ContentFormat::BitwardenLegacyKey
172    }
173}
174/// BitwardenLegacyKeyBytes is a type alias for Bytes with `BitwardenLegacyKeyContentFormat`. This
175/// is used for the legacy format for symmetric keys. A description of the format is available in
176/// the `ContentFormat::BitwardenLegacyKey` documentation.
177pub type BitwardenLegacyKeyBytes = Bytes<BitwardenLegacyKeyContentFormat>;
178
179/// Content format for COSE Sign1 messages.
180#[derive(PartialEq, Eq, Clone, Debug)]
181pub struct CoseSign1ContentFormat;
182impl private::Sealed for CoseSign1ContentFormat {}
183impl ConstContentFormat for CoseSign1ContentFormat {
184    #[allow(private_interfaces)]
185    fn content_format() -> ContentFormat {
186        ContentFormat::CoseSign1
187    }
188}
189impl CoseContentFormat for CoseSign1ContentFormat {}
190/// CoseSign1Bytes is a type alias for Bytes with `CoseSign1ContentFormat`. This is used for
191/// serialized COSE Sign1 messages.
192pub type CoseSign1Bytes = Bytes<CoseSign1ContentFormat>;
193
194impl<Ids: KeyIds, T: ConstContentFormat> PrimitiveEncryptable<Ids, Ids::Symmetric, EncString>
195    for Bytes<T>
196{
197    fn encrypt(
198        &self,
199        ctx: &mut KeyStoreContext<Ids>,
200        key: Ids::Symmetric,
201    ) -> Result<EncString, CryptoError> {
202        self.inner.encrypt(ctx, key, T::content_format())
203    }
204}
205
206impl<T: ConstContentFormat> KeyEncryptable<SymmetricCryptoKey, EncString> for &Bytes<T> {
207    fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result<EncString, CryptoError> {
208        self.as_ref().encrypt_with_key(key, T::content_format())
209    }
210}
211
212impl From<String> for Bytes<Utf8ContentFormat> {
213    fn from(val: String) -> Self {
214        Bytes::from(val.into_bytes())
215    }
216}
217
218impl From<&str> for Bytes<Utf8ContentFormat> {
219    fn from(val: &str) -> Self {
220        Bytes::from(val.as_bytes().to_vec())
221    }
222}