bitwarden_core/key_management/
security_state.rs1use std::{fmt::Debug, str::FromStr};
24
25use bitwarden_crypto::{
26 CoseSerializable, CoseSign1Bytes, CryptoError, EncodingError, KeySlotIds, KeyStoreContext,
27 SignedObject, SigningNamespace, VerifyingKey,
28};
29use bitwarden_encoding::{B64, FromStrVisitor};
30use serde::{Deserialize, Serialize};
31
32pub const MINIMUM_ENFORCE_ICON_URI_HASH_VERSION: u64 = 2;
34
35pub const BLOB_SECURITY_VERSION: u64 = 2;
37
38#[cfg(feature = "wasm")]
39#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
40const TS_CUSTOM_TYPES: &'static str = r#"
41export type SignedSecurityState = string;
42"#;
43
44#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
50#[derive(Debug, Clone, Serialize, Deserialize)]
51#[serde(rename_all = "camelCase")]
52pub struct SecurityState {
53 version: u64,
57}
58
59impl Default for SecurityState {
60 fn default() -> Self {
61 Self::new()
62 }
63}
64
65impl SecurityState {
66 pub fn new() -> Self {
69 SecurityState { version: 2 }
70 }
71
72 pub fn version(&self) -> u64 {
74 self.version
75 }
76
77 pub fn sign<Ids: KeySlotIds>(
79 &self,
80 signing_key_id: Ids::Signing,
81 ctx: &mut KeyStoreContext<Ids>,
82 ) -> Result<SignedSecurityState, CryptoError> {
83 Ok(SignedSecurityState(ctx.sign(
84 signing_key_id,
85 &self,
86 &SigningNamespace::SecurityState,
87 )?))
88 }
89}
90
91#[derive(Clone, PartialEq)]
93pub struct SignedSecurityState(pub(crate) SignedObject);
94
95impl Debug for SignedSecurityState {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 let mut debug_struct = f.debug_struct("SignedSecurityState");
98
99 if let Ok(signed_by) = self.0.signed_by_id() {
100 debug_struct.field("signed_by", &signed_by);
101 }
102 if let Some(state) = self
103 .0
104 .dangerous_unverified_decode_do_not_use_except_for_debug_logs::<SecurityState>()
105 {
106 debug_struct.field("version", &state.version);
107 }
108
109 debug_struct.finish()
110 }
111}
112
113impl SignedSecurityState {
114 pub fn verify_and_unwrap(
116 self,
117 verifying_key: &VerifyingKey,
118 ) -> Result<SecurityState, CryptoError> {
119 self.0
120 .verify_and_unwrap(verifying_key, &SigningNamespace::SecurityState)
121 }
122}
123
124impl From<SignedSecurityState> for CoseSign1Bytes {
125 fn from(val: SignedSecurityState) -> Self {
126 val.0.to_cose()
127 }
128}
129
130impl TryFrom<&CoseSign1Bytes> for SignedSecurityState {
131 type Error = EncodingError;
132 fn try_from(bytes: &CoseSign1Bytes) -> Result<Self, EncodingError> {
133 Ok(SignedSecurityState(SignedObject::from_cose(bytes)?))
134 }
135}
136
137impl From<&SignedSecurityState> for String {
138 fn from(val: &SignedSecurityState) -> Self {
139 val.to_owned().into()
140 }
141}
142
143impl From<SignedSecurityState> for String {
144 fn from(val: SignedSecurityState) -> Self {
145 let bytes: CoseSign1Bytes = val.into();
146 B64::from(bytes.as_ref()).to_string()
147 }
148}
149
150impl FromStr for SignedSecurityState {
151 type Err = EncodingError;
152
153 fn from_str(s: &str) -> Result<Self, Self::Err> {
154 let bytes = B64::try_from(s).map_err(|_| EncodingError::InvalidBase64Encoding)?;
155 Self::try_from(&CoseSign1Bytes::from(&bytes))
156 }
157}
158
159impl<'de> Deserialize<'de> for SignedSecurityState {
160 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
161 where
162 D: serde::Deserializer<'de>,
163 {
164 deserializer.deserialize_str(FromStrVisitor::new())
165 }
166}
167
168impl serde::Serialize for SignedSecurityState {
169 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
170 where
171 S: serde::Serializer,
172 {
173 let b64_serialized_signed_public_key: String = self.clone().into();
174 serializer.serialize_str(&b64_serialized_signed_public_key)
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use bitwarden_crypto::{CoseKeyBytes, KeyStore, SignatureAlgorithm, SigningKey};
181
182 use super::*;
183 use crate::key_management::KeySlotIds;
184
185 const TEST_SIGNED_SECURITY_STATE: &str = "hFgepAEnAxg8BFBHo5ojcqDqbynNymOZGgJzOgABOH8CoFgkomhlbnRpdHlJZFBHmj2OTpBFO7aDLgeNnbZPZ3ZlcnNpb24CWEA4mQbYRRoPpc77tVHH4LlwY52Vz6tutThv8b/BV3ntQmjuKUxbzIGRxSyOhzCn3ouFJGEVnfsl6SqSm6K9XcME";
186 const TEST_VERIFYING_KEY: &str =
187 "pgEBAlBHo5ojcqDqbynNymOZGgJzAycEgQIgBiFYIK9hIvbLIdnzKhykPt8jT/ktXAlzPUfx4Nyx4EYTpIp7";
188
189 #[test]
190 #[ignore = "Manual test for debug logs"]
191 fn test_security_state_debug_logs() {
192 let store: KeyStore<KeySlotIds> = KeyStore::default();
193 let mut ctx = store.context_mut();
194
195 let security_state = SecurityState::new();
196 let signing_key = SigningKey::make(SignatureAlgorithm::Ed25519);
197 let key = ctx.add_local_signing_key(signing_key.clone());
198 let signed_security_state = security_state.sign(key, &mut ctx).unwrap();
199
200 println!("{:?}", signed_security_state);
201 }
202
203 #[test]
204 #[ignore = "Make test vectors"]
205 fn test_make_test_vector() {
206 let store: KeyStore<KeySlotIds> = KeyStore::default();
207 let mut ctx = store.context_mut();
208
209 let security_state = SecurityState::new();
210 let signing_key = SigningKey::make(SignatureAlgorithm::Ed25519);
211 let key = ctx.add_local_signing_key(signing_key.clone());
212 let signed_security_state = security_state.sign(key, &mut ctx).unwrap();
213 let verifying_key = signing_key.to_verifying_key();
214
215 println!(
216 "const TEST_SIGNED_SECURITY_STATE: &str = \"{}\";",
217 String::from(&signed_security_state)
218 );
219 println!(
220 "const TEST_VERIFYING_KEY: &str = \"{}\";",
221 B64::from(verifying_key.to_cose())
222 );
223 }
224
225 #[test]
226 fn test_security_state_signing() {
227 let store: KeyStore<KeySlotIds> = KeyStore::default();
228 let mut ctx = store.context_mut();
229
230 let security_state = SecurityState::new();
231 let signing_key = SigningKey::make(SignatureAlgorithm::Ed25519);
232 let key = ctx.add_local_signing_key(signing_key.clone());
233 let signed_security_state = security_state.sign(key, &mut ctx).unwrap();
234
235 let verifying_key = signing_key.to_verifying_key();
236 let verified_security_state = signed_security_state
237 .verify_and_unwrap(&verifying_key)
238 .unwrap();
239
240 assert_eq!(verified_security_state.version(), 2);
241 }
242
243 #[test]
244 fn test_stable_testvector() {
245 let b64 = B64::try_from(TEST_VERIFYING_KEY).unwrap();
246 let verifying_key = VerifyingKey::from_cose(&CoseKeyBytes::from(&b64)).unwrap();
247 let signed_security_state =
248 SignedSecurityState::from_str(TEST_SIGNED_SECURITY_STATE).unwrap();
249 let verified_security_state = signed_security_state
250 .verify_and_unwrap(&verifying_key)
251 .unwrap();
252
253 assert_eq!(verified_security_state.version(), 2);
254 }
255}