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