bitwarden_core/key_management/
pin_lock_system.rs1use bitwarden_crypto::{
11 Decryptable, KeyStore, PrimitiveEncryptable,
12 safe::{PasswordProtectedKeyEnvelope, PasswordProtectedKeyEnvelopeNamespace},
13};
14use serde::{Deserialize, Serialize};
15use tracing::warn;
16#[cfg(feature = "wasm")]
17use tsify::Tsify;
18#[cfg(feature = "wasm")]
19use wasm_bindgen::prelude::*;
20
21use crate::{
22 Client,
23 key_management::{KeySlotIds, SymmetricKeySlotId},
24};
25
26#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
31#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
32#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
33pub enum PinLockType {
34 BeforeFirstUnlock,
36 AfterFirstUnlock,
39}
40
41#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
42#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
43#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
44pub enum PinUnlockStatus {
46 Available,
49 NeedsUnlock,
51 NotSet,
53}
54
55pub(crate) enum UnlockError {
56 NoPinSet,
57 PinWrong,
58 InternalError,
59}
60
61pub struct PinLockSystem<'a> {
65 client: &'a Client,
66}
67
68impl PinLockSystem<'_> {
69 fn key_store(&self) -> &KeyStore<KeySlotIds> {
70 self.client.internal.get_key_store()
71 }
72
73 pub fn with_client(client: &Client) -> PinLockSystem<'_> {
75 PinLockSystem { client }
76 }
77
78 async fn get_active_pin_envelope(&self) -> Option<PasswordProtectedKeyEnvelope> {
82 let mut pin_protected_key_envelope = self
83 .client
84 .km_state_bridge()
85 .get_ephemeral_pin_envelope()
86 .await;
87 if pin_protected_key_envelope.is_none() {
88 pin_protected_key_envelope = self
89 .client
90 .km_state_bridge()
91 .get_persistent_pin_envelope()
92 .await;
93 }
94 pin_protected_key_envelope
95 }
96
97 pub(crate) async fn unlock(&self, pin: &str) -> Result<(), UnlockError> {
103 let pin_envelope = Self::get_active_pin_envelope(self)
104 .await
105 .ok_or(UnlockError::NoPinSet)?;
106
107 let mut ctx = self.key_store().context_mut();
109 let key_slot = pin_envelope
110 .unseal(
111 pin,
112 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
113 &mut ctx,
114 )
115 .map_err(|e| match e {
116 bitwarden_crypto::safe::PasswordProtectedKeyEnvelopeError::WrongPassword => {
117 UnlockError::PinWrong
118 }
119 _ => UnlockError::InternalError,
120 })?;
121
122 ctx.persist_symmetric_key(key_slot, SymmetricKeySlotId::User)
125 .map_err(|_| UnlockError::InternalError)
126 }
127
128 pub(crate) async fn on_unlock(&self) {
132 if !self.client.km_state_bridge().is_bridge_registered() {
134 return;
135 }
136
137 let encrypted_pin = self.client.km_state_bridge().get_encrypted_pin().await;
138
139 let Some(encrypted_pin) = encrypted_pin else {
141 return;
142 };
143
144 let Ok(pin_envelope) = (|| -> Result<PasswordProtectedKeyEnvelope, ()> {
146 let mut ctx = self.key_store().context_mut();
147 let pin: String = encrypted_pin
148 .decrypt(&mut ctx, SymmetricKeySlotId::User)
149 .map_err(|_| ())?;
150 PasswordProtectedKeyEnvelope::seal(
151 SymmetricKeySlotId::User,
152 pin.as_str(),
153 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
154 &ctx,
155 )
156 .map_err(|_| ())
157 })() else {
158 warn!("Failed to create PIN envelope");
159 return;
160 };
161
162 self.client
164 .km_state_bridge()
165 .set_ephemeral_pin_envelope(&pin_envelope)
166 .await;
167 }
168
169 pub async fn set_pin(&self, pin: String, lock_type: PinLockType) -> Result<(), ()> {
171 self.client
173 .km_state_bridge()
174 .clear_persistent_pin_envelope()
175 .await;
176 self.client
177 .km_state_bridge()
178 .clear_ephemeral_pin_envelope()
179 .await;
180 self.client.km_state_bridge().clear_encrypted_pin().await;
181
182 let pin_envelope: PasswordProtectedKeyEnvelope = PasswordProtectedKeyEnvelope::seal(
183 SymmetricKeySlotId::User,
184 pin.as_str(),
185 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
186 &self.key_store().context_mut(),
187 )
188 .map_err(|_| ())?;
189 let encrypted_pin = pin
190 .encrypt(
191 &mut self.key_store().context_mut(),
192 SymmetricKeySlotId::User,
193 )
194 .map_err(|_| ())?;
195
196 self.client
197 .km_state_bridge()
198 .set_encrypted_pin(&encrypted_pin)
199 .await;
200 self.client
201 .km_state_bridge()
202 .set_ephemeral_pin_envelope(&pin_envelope)
203 .await;
204
205 if lock_type == PinLockType::BeforeFirstUnlock {
206 self.client
207 .km_state_bridge()
208 .set_persistent_pin_envelope(&pin_envelope)
209 .await;
210 }
211
212 Ok(())
213 }
214
215 pub async fn unset_pin(&self) {
217 self.client
218 .km_state_bridge()
219 .clear_persistent_pin_envelope()
220 .await;
221 self.client
222 .km_state_bridge()
223 .clear_ephemeral_pin_envelope()
224 .await;
225 self.client.km_state_bridge().clear_encrypted_pin().await;
226 }
227
228 pub async fn get_pin_lock_type(&self) -> Option<PinLockType> {
230 if self
231 .client
232 .km_state_bridge()
233 .get_persistent_pin_envelope()
234 .await
235 .is_some()
236 {
237 return Some(PinLockType::BeforeFirstUnlock);
238 }
239
240 if self
244 .client
245 .km_state_bridge()
246 .get_encrypted_pin()
247 .await
248 .is_some()
249 {
250 return Some(PinLockType::AfterFirstUnlock);
251 }
252
253 None
254 }
255
256 pub async fn get_pin_status(&self) -> PinUnlockStatus {
261 match Self::get_pin_lock_type(self).await {
262 Some(PinLockType::BeforeFirstUnlock) => {
263 if self.get_active_pin_envelope().await.is_some() {
264 PinUnlockStatus::Available
265 } else {
266 PinUnlockStatus::NeedsUnlock
267 }
268 }
269 Some(PinLockType::AfterFirstUnlock) => {
270 if self
271 .client
272 .km_state_bridge()
273 .get_ephemeral_pin_envelope()
274 .await
275 .is_some()
276 {
277 PinUnlockStatus::Available
278 } else {
279 PinUnlockStatus::NeedsUnlock
282 }
283 }
284 None => PinUnlockStatus::NotSet,
285 }
286 }
287
288 pub async fn get_pin(&self) -> Option<String> {
290 let encrypted_pin = self.client.km_state_bridge().get_encrypted_pin().await?;
291 encrypted_pin
292 .decrypt(
293 &mut self.client.internal.get_key_store().context_mut(),
294 SymmetricKeySlotId::User,
295 )
296 .ok()
297 }
298
299 pub async fn validate_pin(&self, pin: String) -> bool {
301 let pin_envelope = self.get_active_pin_envelope().await;
302 let Some(pin_envelope) = pin_envelope else {
303 return false;
304 };
305
306 pin_envelope
307 .unseal(
308 pin.as_str(),
309 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
310 &mut self.key_store().context_mut(),
311 )
312 .is_ok()
313 }
314}
315
316#[cfg(test)]
317mod tests {
318 use bitwarden_crypto::{EncString, KeyId, SymmetricKeyAlgorithm};
319
320 use super::*;
321 use crate::key_management::state_bridge::test_support::InMemoryStateBridge;
322
323 fn decrypt_encrypted_pin(client: &Client, encrypted_pin: &EncString) -> String {
324 encrypted_pin
325 .decrypt(
326 &mut client.internal.get_key_store().context_mut(),
327 SymmetricKeySlotId::User,
328 )
329 .expect("encrypted pin should decrypt successfully")
330 }
331
332 fn user_key_id(client: &Client) -> KeyId {
334 client
335 .internal
336 .get_key_store()
337 .context()
338 .get_symmetric_key_id(SymmetricKeySlotId::User)
339 .expect("user key present")
340 }
341
342 fn assert_envelope_wraps_user_key(
344 client: &Client,
345 envelope: &PasswordProtectedKeyEnvelope,
346 pin: &str,
347 expected_key_id: &KeyId,
348 ) {
349 assert_eq!(
350 envelope
351 .contained_key_id()
352 .expect("contained key id readable"),
353 Some(expected_key_id.clone()),
354 "envelope wraps a key other than the current user key",
355 );
356 let _ = envelope
357 .unseal(
358 pin,
359 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
360 &mut client.internal.get_key_store().context_mut(),
361 )
362 .expect("envelope unseals with the configured pin");
363 }
364
365 fn client_with_user_key() -> Client {
366 let client = Client::new(None);
367 client
368 .km_state_bridge()
369 .register_bridge(Box::new(InMemoryStateBridge::default()));
370 {
371 let key_store = client.internal.get_key_store();
372 let mut ctx = key_store.context_mut();
373 let user_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
374 ctx.persist_symmetric_key(user_key, SymmetricKeySlotId::User)
375 .expect("persisting user key should succeed");
376 }
377 client
378 }
379
380 fn seal_envelope(client: &Client, pin: &str) -> PasswordProtectedKeyEnvelope {
381 PasswordProtectedKeyEnvelope::seal(
382 SymmetricKeySlotId::User,
383 pin,
384 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
385 &client.internal.get_key_store().context_mut(),
386 )
387 .expect("seal succeeds")
388 }
389
390 #[tokio::test]
391 async fn set_pin_bfu_persists_both_envelopes() {
392 let client = client_with_user_key();
393 let user_key_id = user_key_id(&client);
394 let system = PinLockSystem::with_client(&client);
395
396 system
397 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
398 .await
399 .expect("set_pin succeeds");
400
401 let bridge = client.km_state_bridge();
402 let persistent = bridge
403 .get_persistent_pin_envelope()
404 .await
405 .expect("persistent envelope present");
406 let ephemeral = bridge
407 .get_ephemeral_pin_envelope()
408 .await
409 .expect("ephemeral envelope present");
410 let encrypted_pin = bridge
411 .get_encrypted_pin()
412 .await
413 .expect("encrypted pin present");
414
415 assert_envelope_wraps_user_key(&client, &persistent, "1234", &user_key_id);
416 assert_envelope_wraps_user_key(&client, &ephemeral, "1234", &user_key_id);
417 assert_eq!(decrypt_encrypted_pin(&client, &encrypted_pin), "1234");
418
419 assert_eq!(
420 system.get_pin_lock_type().await,
421 Some(PinLockType::BeforeFirstUnlock)
422 );
423 assert_eq!(system.get_pin_status().await, PinUnlockStatus::Available);
424 }
425
426 #[tokio::test]
427 async fn set_pin_afu_persists_only_ephemeral() {
428 let client = client_with_user_key();
429 let user_key_id = user_key_id(&client);
430 let system = PinLockSystem::with_client(&client);
431
432 system
433 .set_pin("1234".into(), PinLockType::AfterFirstUnlock)
434 .await
435 .expect("set_pin succeeds");
436
437 let bridge = client.km_state_bridge();
438 assert!(bridge.get_persistent_pin_envelope().await.is_none());
439 let ephemeral = bridge
440 .get_ephemeral_pin_envelope()
441 .await
442 .expect("ephemeral envelope present");
443 let encrypted_pin = bridge
444 .get_encrypted_pin()
445 .await
446 .expect("encrypted pin present");
447
448 assert_envelope_wraps_user_key(&client, &ephemeral, "1234", &user_key_id);
449 assert_eq!(decrypt_encrypted_pin(&client, &encrypted_pin), "1234");
450
451 assert_eq!(
452 system.get_pin_lock_type().await,
453 Some(PinLockType::AfterFirstUnlock)
454 );
455 assert_eq!(system.get_pin_status().await, PinUnlockStatus::Available);
456 }
457
458 #[tokio::test]
459 async fn set_pin_overwrites_existing_state() {
460 let client = client_with_user_key();
461 let system = PinLockSystem::with_client(&client);
462
463 system
464 .set_pin("first".into(), PinLockType::BeforeFirstUnlock)
465 .await
466 .expect("first set_pin");
467 system
468 .set_pin("second".into(), PinLockType::AfterFirstUnlock)
469 .await
470 .expect("second set_pin");
471
472 let bridge = client.km_state_bridge();
473 assert!(
474 bridge.get_persistent_pin_envelope().await.is_none(),
475 "switching to AFU must clear the persistent envelope"
476 );
477 assert_eq!(
478 system.get_pin_lock_type().await,
479 Some(PinLockType::AfterFirstUnlock)
480 );
481 assert!(system.validate_pin("second".into()).await);
482 assert!(!system.validate_pin("first".into()).await);
483 }
484
485 #[tokio::test]
486 async fn unset_pin_clears_all_state() {
487 let client = client_with_user_key();
488 let system = PinLockSystem::with_client(&client);
489
490 system
491 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
492 .await
493 .expect("set_pin succeeds");
494 system.unset_pin().await;
495
496 let bridge = client.km_state_bridge();
497 assert!(bridge.get_persistent_pin_envelope().await.is_none());
498 assert!(bridge.get_ephemeral_pin_envelope().await.is_none());
499 assert!(bridge.get_encrypted_pin().await.is_none());
500 assert_eq!(system.get_pin_lock_type().await, None);
501 assert_eq!(system.get_pin_status().await, PinUnlockStatus::NotSet);
502 }
503
504 #[tokio::test]
505 async fn unlock_with_correct_pin_persists_user_key() {
506 let client = client_with_user_key();
507 let system = PinLockSystem::with_client(&client);
508
509 let pre_unlock_user_key_id = user_key_id(&client);
510 system
512 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
513 .await
514 .expect("set_pin succeeds");
515 client.internal.get_key_store().clear();
516
517 assert!(system.unlock("1234").await.is_ok());
518 let post_unlock_user_key_id = user_key_id(&client);
519 assert_eq!(post_unlock_user_key_id, pre_unlock_user_key_id);
520 }
521
522 #[tokio::test]
523 async fn unlock_with_wrong_pin_returns_pin_wrong() {
524 let client = client_with_user_key();
525 let system = PinLockSystem::with_client(&client);
526 system
527 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
528 .await
529 .expect("set_pin succeeds");
530
531 assert!(matches!(
532 system.unlock("wrong").await,
533 Err(UnlockError::PinWrong)
534 ));
535 }
536
537 #[tokio::test]
538 async fn unlock_with_no_pin_set_returns_no_pin_set() {
539 let client = client_with_user_key();
540 let system = PinLockSystem::with_client(&client);
541
542 assert!(matches!(
543 system.unlock("anything").await,
544 Err(UnlockError::NoPinSet)
545 ));
546 }
547
548 #[tokio::test]
549 async fn unlock_prefers_ephemeral_envelope_over_persistent() {
550 let client = client_with_user_key();
551 let system = PinLockSystem::with_client(&client);
552 system
553 .set_pin("persistent".into(), PinLockType::BeforeFirstUnlock)
554 .await
555 .expect("set_pin succeeds");
556
557 let ephemeral = seal_envelope(&client, "ephemeral");
560 client
561 .km_state_bridge()
562 .set_ephemeral_pin_envelope(&ephemeral)
563 .await;
564
565 assert!(system.unlock("ephemeral").await.is_ok());
566 assert!(matches!(
567 system.unlock("persistent").await,
568 Err(UnlockError::PinWrong)
569 ));
570 }
571
572 #[tokio::test]
573 async fn get_pin_status_available_bfu() {
574 let client = client_with_user_key();
575 let system = PinLockSystem::with_client(&client);
576 system
577 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
578 .await
579 .expect("set_pin succeeds");
580
581 client
583 .km_state_bridge()
584 .clear_ephemeral_pin_envelope()
585 .await;
586
587 assert_eq!(system.get_pin_status().await, PinUnlockStatus::Available);
588 assert_eq!(
589 system.get_pin_lock_type().await,
590 Some(PinLockType::BeforeFirstUnlock)
591 );
592 }
593
594 #[tokio::test]
595 async fn on_unlock_rebuilds_ephemeral_envelope() {
596 let client = client_with_user_key();
597 let user_key_id = user_key_id(&client);
598 let system = PinLockSystem::with_client(&client);
599 system
600 .set_pin("1234".into(), PinLockType::AfterFirstUnlock)
601 .await
602 .expect("set_pin succeeds");
603 client
604 .km_state_bridge()
605 .clear_ephemeral_pin_envelope()
606 .await;
607 assert_eq!(system.get_pin_status().await, PinUnlockStatus::NeedsUnlock);
608
609 system.on_unlock().await;
610
611 let rebuilt = client
612 .km_state_bridge()
613 .get_ephemeral_pin_envelope()
614 .await
615 .expect("on_unlock should restore the ephemeral envelope");
616 assert_envelope_wraps_user_key(&client, &rebuilt, "1234", &user_key_id);
617 assert_eq!(system.get_pin_status().await, PinUnlockStatus::Available);
618 assert!(system.unlock("1234").await.is_ok());
619 }
620
621 #[tokio::test]
622 async fn on_unlock_is_noop_when_no_encrypted_pin() {
623 let client = client_with_user_key();
624 let system = PinLockSystem::with_client(&client);
625
626 system.on_unlock().await;
627
628 assert_eq!(system.get_pin_status().await, PinUnlockStatus::NotSet);
629 }
630
631 #[tokio::test]
632 async fn on_unlock_is_noop_when_bridge_not_registered() {
633 let client = Client::new(None);
634 let system = PinLockSystem::with_client(&client);
635
636 system.on_unlock().await;
638 }
639
640 #[tokio::test]
641 async fn get_pin_returns_set_pin() {
642 let client = client_with_user_key();
643 let system = PinLockSystem::with_client(&client);
644
645 assert_eq!(system.get_pin().await, None);
646
647 system
648 .set_pin("1234".into(), PinLockType::AfterFirstUnlock)
649 .await
650 .expect("set_pin succeeds");
651 assert_eq!(system.get_pin().await, Some("1234".to_owned()));
652
653 system.unset_pin().await;
654 assert_eq!(system.get_pin().await, None);
655 }
656
657 #[tokio::test]
658 async fn validate_pin_matches_only_correct_pin() {
659 let client = client_with_user_key();
660 let system = PinLockSystem::with_client(&client);
661
662 assert!(!system.validate_pin("anything".into()).await);
663
664 system
665 .set_pin("1234".into(), PinLockType::AfterFirstUnlock)
666 .await
667 .expect("set_pin succeeds");
668 assert!(system.validate_pin("1234".into()).await);
669 assert!(!system.validate_pin("wrong".into()).await);
670 }
671}