bitwarden_core/key_management/
security_state.rs1use std::str::FromStr;
24
25use base64::{engine::general_purpose::STANDARD, Engine};
26use bitwarden_crypto::{
27 CoseSerializable, CoseSign1Bytes, CryptoError, EncodingError, FromStrVisitor, KeyIds,
28 KeyStoreContext, SignedObject, SigningNamespace, VerifyingKey,
29};
30use serde::{Deserialize, Serialize};
31use uuid::Uuid;
32
33#[cfg(feature = "wasm")]
34#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
35const TS_CUSTOM_TYPES: &'static str = r#"
36export type SignedSecurityState = string;
37"#;
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
45#[serde(rename_all = "camelCase")]
46pub struct SecurityState {
47 entity_id: Uuid,
50 version: u64,
54}
55
56impl SecurityState {
57 pub fn initialize_for_user(user_id: uuid::Uuid) -> Self {
60 SecurityState {
61 entity_id: user_id,
62 version: 2,
63 }
64 }
65
66 pub fn version(&self) -> u64 {
68 self.version
69 }
70
71 pub fn sign<Ids: KeyIds>(
73 &self,
74 signing_key_id: Ids::Signing,
75 ctx: &mut KeyStoreContext<Ids>,
76 ) -> Result<SignedSecurityState, CryptoError> {
77 Ok(SignedSecurityState(ctx.sign(
78 signing_key_id,
79 &self,
80 &SigningNamespace::SecurityState,
81 )?))
82 }
83}
84
85#[derive(Clone, Debug)]
87pub struct SignedSecurityState(pub(crate) SignedObject);
88
89impl SignedSecurityState {
90 pub fn verify_and_unwrap(
92 self,
93 verifying_key: &VerifyingKey,
94 ) -> Result<SecurityState, CryptoError> {
95 self.0
96 .verify_and_unwrap(verifying_key, &SigningNamespace::SecurityState)
97 }
98}
99
100impl From<SignedSecurityState> for CoseSign1Bytes {
101 fn from(val: SignedSecurityState) -> Self {
102 val.0.to_cose()
103 }
104}
105
106impl TryFrom<&CoseSign1Bytes> for SignedSecurityState {
107 type Error = EncodingError;
108 fn try_from(bytes: &CoseSign1Bytes) -> Result<Self, EncodingError> {
109 Ok(SignedSecurityState(SignedObject::from_cose(bytes)?))
110 }
111}
112
113impl From<SignedSecurityState> for String {
114 fn from(val: SignedSecurityState) -> Self {
115 let bytes: CoseSign1Bytes = val.into();
116 STANDARD.encode(&bytes)
117 }
118}
119
120impl FromStr for SignedSecurityState {
121 type Err = EncodingError;
122
123 fn from_str(s: &str) -> Result<Self, Self::Err> {
124 let bytes = STANDARD
125 .decode(s)
126 .map_err(|_| EncodingError::InvalidBase64Encoding)?;
127 Self::try_from(&CoseSign1Bytes::from(bytes))
128 }
129}
130
131impl<'de> Deserialize<'de> for SignedSecurityState {
132 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
133 where
134 D: serde::Deserializer<'de>,
135 {
136 deserializer.deserialize_str(FromStrVisitor::new())
137 }
138}
139
140impl serde::Serialize for SignedSecurityState {
141 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
142 where
143 S: serde::Serializer,
144 {
145 let b64_serialized_signed_public_key: String = self.clone().into();
146 serializer.serialize_str(&b64_serialized_signed_public_key)
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use bitwarden_crypto::{KeyStore, SignatureAlgorithm, SigningKey};
153
154 use super::*;
155 use crate::key_management::{KeyIds, SigningKeyId};
156
157 #[test]
158 fn test_security_state_signing() {
159 let store: KeyStore<KeyIds> = KeyStore::default();
160 let mut ctx = store.context_mut();
161
162 let user_id = uuid::Uuid::new_v4();
163 let security_state = SecurityState::initialize_for_user(user_id);
164 let signing_key = SigningKey::make(SignatureAlgorithm::Ed25519);
165 #[allow(deprecated)]
166 ctx.set_signing_key(SigningKeyId::Local(""), signing_key.clone())
167 .unwrap();
168 let signed_security_state = security_state
169 .sign(SigningKeyId::Local(""), &mut ctx)
170 .unwrap();
171
172 let verifying_key = signing_key.to_verifying_key();
173 let verified_security_state = signed_security_state
174 .verify_and_unwrap(&verifying_key)
175 .unwrap();
176
177 assert_eq!(verified_security_state.entity_id, user_id);
178 assert_eq!(verified_security_state.version(), 2);
179 }
180}