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