bitwarden_crypto/signing/
namespace.rs

1use crate::{CryptoError, error::SignatureError};
2
3/// Signing is domain-separated within bitwarden, to prevent cross protocol attacks.
4///
5/// A new signed entity or protocol shall use a new signing namespace. Generally, this means
6/// that a signing namespace has exactly one associated valid message struct.
7///
8/// If there is a new version of a message added, it should (generally) use a new namespace, since
9/// this prevents downgrades to the old type of message, and makes optional fields unnecessary.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum SigningNamespace {
12    /// The namespace for
13    /// [`SignedPublicKey`](crate::keys::SignedPublicKey).
14    SignedPublicKey = 1,
15    /// The namespace for SignedSecurityState
16    SecurityState = 2,
17    /// This namespace is only used in tests
18    #[cfg(test)]
19    ExampleNamespace = -1,
20    /// This namespace is only used in tests
21    #[cfg(test)]
22    ExampleNamespace2 = -2,
23}
24
25impl SigningNamespace {
26    /// Returns the numeric value of the namespace.
27    pub fn as_i64(&self) -> i64 {
28        *self as i64
29    }
30}
31
32impl TryFrom<i64> for SigningNamespace {
33    type Error = CryptoError;
34
35    fn try_from(value: i64) -> Result<Self, Self::Error> {
36        match value {
37            1 => Ok(SigningNamespace::SignedPublicKey),
38            2 => Ok(SigningNamespace::SecurityState),
39            #[cfg(test)]
40            -1 => Ok(SigningNamespace::ExampleNamespace),
41            #[cfg(test)]
42            -2 => Ok(SigningNamespace::ExampleNamespace2),
43            _ => Err(SignatureError::InvalidNamespace.into()),
44        }
45    }
46}
47
48impl TryFrom<i128> for SigningNamespace {
49    type Error = CryptoError;
50
51    fn try_from(value: i128) -> Result<Self, Self::Error> {
52        let Ok(value) = i64::try_from(value) else {
53            return Err(SignatureError::InvalidNamespace.into());
54        };
55        Self::try_from(value)
56    }
57}