1use bitwarden_crypto::{
11 Decryptable, KeyId, KeyStore, PrimitiveEncryptable, SymmetricKeyAlgorithm,
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
61#[derive(Debug, PartialEq, Eq)]
62pub(crate) enum MigrationFailed {
63 Locked,
65 EnvelopeMalformed,
67 V2KeyRotationUnsupported,
70 EnvelopeWithKeyIdWithV1UserKey,
72 MissingV2UpgradeToken,
74 MissingEncryptedPin,
76 PinDecryption,
78 Reenrollment,
80}
81
82#[derive(Debug, PartialEq, Eq)]
85enum PinEnvelopeAction {
86 UpToDate,
88 BackfillKeyId,
92 MigrateV1ToV2,
95 Failed(MigrationFailed),
97}
98
99fn classify_pin_envelope(
106 envelope_key_id: Option<&KeyId>,
107 current_user_key_id: &KeyId,
108 user_key_is_v1: bool,
109) -> PinEnvelopeAction {
110 match envelope_key_id {
111 Some(envelope_key_id) if envelope_key_id == current_user_key_id => {
112 PinEnvelopeAction::UpToDate
113 }
114 None if user_key_is_v1 => PinEnvelopeAction::BackfillKeyId,
115 None => PinEnvelopeAction::MigrateV1ToV2,
116 Some(_) if user_key_is_v1 => {
117 PinEnvelopeAction::Failed(MigrationFailed::EnvelopeWithKeyIdWithV1UserKey)
118 }
119 Some(_) => PinEnvelopeAction::MigrateV1ToV2,
120 }
121}
122
123pub struct PinLockSystem<'a> {
127 client: &'a Client,
128}
129
130impl PinLockSystem<'_> {
131 fn key_store(&self) -> &KeyStore<KeySlotIds> {
132 self.client.internal.get_key_store()
133 }
134
135 pub fn with_client(client: &Client) -> PinLockSystem<'_> {
137 PinLockSystem { client }
138 }
139
140 async fn get_active_pin_envelope(&self) -> Option<PasswordProtectedKeyEnvelope> {
144 let mut pin_protected_key_envelope = self
145 .client
146 .km_state_bridge()
147 .get_ephemeral_pin_envelope()
148 .await;
149 if pin_protected_key_envelope.is_none() {
150 pin_protected_key_envelope = self
151 .client
152 .km_state_bridge()
153 .get_persistent_pin_envelope()
154 .await;
155 }
156 pin_protected_key_envelope
157 }
158
159 pub(crate) async fn unlock(&self, pin: &str) -> Result<(), UnlockError> {
165 let pin_envelope = Self::get_active_pin_envelope(self)
166 .await
167 .ok_or(UnlockError::NoPinSet)?;
168
169 let mut ctx = self.key_store().context_mut();
171 let key_slot = pin_envelope
172 .unseal(
173 pin,
174 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
175 &mut ctx,
176 )
177 .map_err(|e| match e {
178 bitwarden_crypto::safe::PasswordProtectedKeyEnvelopeError::WrongPassword => {
179 UnlockError::PinWrong
180 }
181 _ => UnlockError::InternalError,
182 })?;
183
184 ctx.persist_symmetric_key(key_slot, SymmetricKeySlotId::User)
187 .map_err(|_| UnlockError::InternalError)
188 }
189
190 async fn migrate_pin_envelope_if_needed(&self) -> Result<(), MigrationFailed> {
202 let Some(envelope) = self
203 .client
204 .km_state_bridge()
205 .get_persistent_pin_envelope()
206 .await
207 else {
208 return Ok(());
209 };
210
211 let envelope_key_id = envelope
212 .contained_key_id()
213 .map_err(|_| MigrationFailed::EnvelopeMalformed)?;
214 let (current_user_key_id, user_key_is_v1) = {
216 let ctx = self.key_store().context();
217 (
218 ctx.get_symmetric_key_id(SymmetricKeySlotId::User)
219 .ok_or(MigrationFailed::Locked)?,
220 matches!(
221 ctx.get_symmetric_key_algorithm(SymmetricKeySlotId::User),
222 Ok(SymmetricKeyAlgorithm::Aes256CbcHmac)
223 ),
224 )
225 };
226
227 let pin: String = match classify_pin_envelope(
229 envelope_key_id.as_ref(),
230 ¤t_user_key_id,
231 user_key_is_v1,
232 ) {
233 PinEnvelopeAction::UpToDate => return Ok(()),
234 PinEnvelopeAction::Failed(error) => return Err(error),
235
236 PinEnvelopeAction::BackfillKeyId => {
238 let encrypted_pin = self
239 .client
240 .km_state_bridge()
241 .get_encrypted_pin()
242 .await
243 .ok_or(MigrationFailed::MissingEncryptedPin)?;
244 encrypted_pin
245 .decrypt(
246 &mut self.key_store().context_mut(),
247 SymmetricKeySlotId::User,
248 )
249 .map_err(|_| MigrationFailed::PinDecryption)?
250 }
251
252 PinEnvelopeAction::MigrateV1ToV2 => {
255 let token = self
256 .client
257 .km_state_bridge()
258 .get_v2_upgrade_token()
259 .await
260 .ok_or(MigrationFailed::MissingV2UpgradeToken)?;
261 let encrypted_pin = self
262 .client
263 .km_state_bridge()
264 .get_encrypted_pin()
265 .await
266 .ok_or(MigrationFailed::MissingEncryptedPin)?;
267
268 let mut ctx = self.key_store().context_mut();
271 let v1_slot = token
272 .unwrap_v1(SymmetricKeySlotId::User, &mut ctx)
273 .map_err(|_| MigrationFailed::PinDecryption)?;
274
275 if envelope_key_id.is_some() && envelope_key_id != ctx.get_symmetric_key_id(v1_slot)
279 {
280 return Err(MigrationFailed::V2KeyRotationUnsupported);
281 }
282
283 encrypted_pin
284 .decrypt(&mut ctx, v1_slot)
285 .map_err(|_| MigrationFailed::PinDecryption)?
286 }
287 };
288
289 self.set_pin(pin, PinLockType::BeforeFirstUnlock)
291 .await
292 .map_err(|_| MigrationFailed::Reenrollment)?;
293
294 Ok(())
295 }
296
297 pub(crate) async fn on_unlock(&self) {
301 if !self.client.km_state_bridge().is_bridge_registered() {
303 return;
304 }
305
306 if let Err(e) = self.migrate_pin_envelope_if_needed().await {
307 warn!("PIN migration failed: {e:?}, unenrolling PIN");
308 self.unset_pin().await;
309 return;
310 }
311
312 let encrypted_pin = self.client.km_state_bridge().get_encrypted_pin().await;
313
314 let Some(encrypted_pin) = encrypted_pin else {
316 return;
317 };
318
319 let Ok(pin_envelope) = (|| -> Result<PasswordProtectedKeyEnvelope, ()> {
321 let mut ctx = self.key_store().context_mut();
322 let pin: String = encrypted_pin
323 .decrypt(&mut ctx, SymmetricKeySlotId::User)
324 .map_err(|_| ())?;
325 PasswordProtectedKeyEnvelope::seal(
326 SymmetricKeySlotId::User,
327 pin.as_str(),
328 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
329 &ctx,
330 )
331 .map_err(|_| ())
332 })() else {
333 warn!("Failed to create PIN envelope");
334 return;
335 };
336
337 self.client
339 .km_state_bridge()
340 .set_ephemeral_pin_envelope(&pin_envelope)
341 .await;
342 }
343
344 pub async fn set_pin(&self, pin: String, lock_type: PinLockType) -> Result<(), ()> {
346 self.client
348 .km_state_bridge()
349 .clear_persistent_pin_envelope()
350 .await;
351 self.client
352 .km_state_bridge()
353 .clear_ephemeral_pin_envelope()
354 .await;
355 self.client.km_state_bridge().clear_encrypted_pin().await;
356
357 let pin_envelope: PasswordProtectedKeyEnvelope = PasswordProtectedKeyEnvelope::seal(
358 SymmetricKeySlotId::User,
359 pin.as_str(),
360 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
361 &self.key_store().context_mut(),
362 )
363 .map_err(|_| ())?;
364 let encrypted_pin = pin
365 .encrypt(
366 &mut self.key_store().context_mut(),
367 SymmetricKeySlotId::User,
368 )
369 .map_err(|_| ())?;
370
371 self.client
372 .km_state_bridge()
373 .set_encrypted_pin(&encrypted_pin)
374 .await;
375 self.client
376 .km_state_bridge()
377 .set_ephemeral_pin_envelope(&pin_envelope)
378 .await;
379
380 if lock_type == PinLockType::BeforeFirstUnlock {
381 self.client
382 .km_state_bridge()
383 .set_persistent_pin_envelope(&pin_envelope)
384 .await;
385 }
386
387 Ok(())
388 }
389
390 pub async fn unset_pin(&self) {
392 self.client
393 .km_state_bridge()
394 .clear_persistent_pin_envelope()
395 .await;
396 self.client
397 .km_state_bridge()
398 .clear_ephemeral_pin_envelope()
399 .await;
400 self.client.km_state_bridge().clear_encrypted_pin().await;
401 }
402
403 pub async fn get_pin_lock_type(&self) -> Option<PinLockType> {
405 if self
406 .client
407 .km_state_bridge()
408 .get_persistent_pin_envelope()
409 .await
410 .is_some()
411 {
412 return Some(PinLockType::BeforeFirstUnlock);
413 }
414
415 if self
419 .client
420 .km_state_bridge()
421 .get_encrypted_pin()
422 .await
423 .is_some()
424 {
425 return Some(PinLockType::AfterFirstUnlock);
426 }
427
428 None
429 }
430
431 pub async fn get_pin_status(&self) -> PinUnlockStatus {
436 match Self::get_pin_lock_type(self).await {
437 Some(PinLockType::BeforeFirstUnlock) => {
438 if self.get_active_pin_envelope().await.is_some() {
439 PinUnlockStatus::Available
440 } else {
441 PinUnlockStatus::NeedsUnlock
442 }
443 }
444 Some(PinLockType::AfterFirstUnlock) => {
445 if self
446 .client
447 .km_state_bridge()
448 .get_ephemeral_pin_envelope()
449 .await
450 .is_some()
451 {
452 PinUnlockStatus::Available
453 } else {
454 PinUnlockStatus::NeedsUnlock
457 }
458 }
459 None => PinUnlockStatus::NotSet,
460 }
461 }
462
463 pub async fn get_pin(&self) -> Option<String> {
465 let encrypted_pin = self.client.km_state_bridge().get_encrypted_pin().await?;
466 encrypted_pin
467 .decrypt(
468 &mut self.client.internal.get_key_store().context_mut(),
469 SymmetricKeySlotId::User,
470 )
471 .ok()
472 }
473
474 pub async fn validate_pin(&self, pin: String) -> bool {
476 let pin_envelope = self.get_active_pin_envelope().await;
477 let Some(pin_envelope) = pin_envelope else {
478 return false;
479 };
480
481 pin_envelope
482 .unseal(
483 pin.as_str(),
484 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
485 &mut self.key_store().context_mut(),
486 )
487 .is_ok()
488 }
489}
490
491#[cfg(test)]
492mod tests {
493 use bitwarden_crypto::{EncString, KeyId, SymmetricKeyAlgorithm};
494
495 use super::*;
496 use crate::key_management::{V2UpgradeToken, state_bridge::test_support::InMemoryStateBridge};
497
498 fn decrypt_encrypted_pin(client: &Client, encrypted_pin: &EncString) -> String {
499 encrypted_pin
500 .decrypt(
501 &mut client.internal.get_key_store().context_mut(),
502 SymmetricKeySlotId::User,
503 )
504 .expect("encrypted pin should decrypt successfully")
505 }
506
507 const TESTVECTOR_LEGACY_ENVELOPE_PIN: &str = "1234";
509 const TESTVECTOR_LEGACY_ENVELOPE: &[u8] = &[
522 132, 88, 52, 164, 1, 3, 3, 120, 34, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47,
523 120, 46, 98, 105, 116, 119, 97, 114, 100, 101, 110, 46, 108, 101, 103, 97, 99, 121, 45,
524 107, 101, 121, 58, 0, 1, 56, 129, 1, 58, 0, 1, 56, 128, 1, 161, 5, 76, 148, 49, 223, 205,
525 195, 252, 91, 216, 65, 200, 121, 104, 88, 80, 89, 120, 16, 161, 14, 97, 191, 97, 138, 42,
526 102, 234, 49, 186, 2, 255, 31, 4, 232, 178, 100, 53, 37, 181, 172, 129, 193, 51, 109, 8,
527 160, 29, 254, 181, 242, 102, 73, 229, 89, 150, 227, 252, 120, 156, 71, 202, 200, 241, 74,
528 241, 206, 16, 155, 83, 49, 242, 13, 209, 10, 217, 251, 164, 244, 69, 41, 52, 9, 192, 140,
529 248, 251, 244, 84, 154, 15, 100, 222, 102, 117, 185, 129, 131, 71, 161, 1, 58, 0, 1, 21,
530 87, 165, 1, 58, 0, 1, 21, 87, 58, 0, 1, 21, 89, 3, 58, 0, 1, 21, 90, 26, 0, 1, 0, 0, 58, 0,
531 1, 21, 91, 4, 58, 0, 1, 21, 88, 80, 64, 184, 87, 20, 40, 186, 214, 56, 87, 53, 118, 100, 5,
532 21, 13, 3, 246,
533 ];
534
535 fn legacy_envelope() -> PasswordProtectedKeyEnvelope {
537 let envelope = PasswordProtectedKeyEnvelope::try_from(&TESTVECTOR_LEGACY_ENVELOPE.to_vec())
538 .expect("legacy envelope test vector parses");
539 assert_eq!(
540 envelope.contained_key_id().expect("readable"),
541 None,
542 "legacy envelope test vector must have no contained key id",
543 );
544 envelope
545 }
546
547 fn user_key_id(client: &Client) -> KeyId {
549 client
550 .internal
551 .get_key_store()
552 .context()
553 .get_symmetric_key_id(SymmetricKeySlotId::User)
554 .expect("user key present")
555 }
556
557 fn assert_envelope_wraps_user_key(
559 client: &Client,
560 envelope: &PasswordProtectedKeyEnvelope,
561 pin: &str,
562 expected_key_id: &KeyId,
563 ) {
564 assert_eq!(
565 envelope
566 .contained_key_id()
567 .expect("contained key id readable"),
568 Some(expected_key_id.clone()),
569 "envelope wraps a key other than the current user key",
570 );
571 let _ = envelope
572 .unseal(
573 pin,
574 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
575 &mut client.internal.get_key_store().context_mut(),
576 )
577 .expect("envelope unseals with the configured pin");
578 }
579
580 fn client_with_user_key() -> Client {
581 let client = Client::new(None);
582 client
583 .km_state_bridge()
584 .register_bridge(Box::new(InMemoryStateBridge::default()));
585 {
586 let key_store = client.internal.get_key_store();
587 let mut ctx = key_store.context_mut();
588 let user_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
589 ctx.persist_symmetric_key(user_key, SymmetricKeySlotId::User)
590 .expect("persisting user key should succeed");
591 }
592 client
593 }
594
595 fn seal_envelope(client: &Client, pin: &str) -> PasswordProtectedKeyEnvelope {
596 PasswordProtectedKeyEnvelope::seal(
597 SymmetricKeySlotId::User,
598 pin,
599 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
600 &client.internal.get_key_store().context_mut(),
601 )
602 .expect("seal succeeds")
603 }
604
605 #[tokio::test]
606 async fn set_pin_bfu_persists_both_envelopes() {
607 let client = client_with_user_key();
608 let user_key_id = user_key_id(&client);
609 let system = PinLockSystem::with_client(&client);
610
611 system
612 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
613 .await
614 .expect("set_pin succeeds");
615
616 let bridge = client.km_state_bridge();
617 let persistent = bridge
618 .get_persistent_pin_envelope()
619 .await
620 .expect("persistent envelope present");
621 let ephemeral = bridge
622 .get_ephemeral_pin_envelope()
623 .await
624 .expect("ephemeral envelope present");
625 let encrypted_pin = bridge
626 .get_encrypted_pin()
627 .await
628 .expect("encrypted pin present");
629
630 assert_envelope_wraps_user_key(&client, &persistent, "1234", &user_key_id);
631 assert_envelope_wraps_user_key(&client, &ephemeral, "1234", &user_key_id);
632 assert_eq!(decrypt_encrypted_pin(&client, &encrypted_pin), "1234");
633
634 assert_eq!(
635 system.get_pin_lock_type().await,
636 Some(PinLockType::BeforeFirstUnlock)
637 );
638 assert_eq!(system.get_pin_status().await, PinUnlockStatus::Available);
639 }
640
641 #[tokio::test]
642 async fn set_pin_afu_persists_only_ephemeral() {
643 let client = client_with_user_key();
644 let user_key_id = user_key_id(&client);
645 let system = PinLockSystem::with_client(&client);
646
647 system
648 .set_pin("1234".into(), PinLockType::AfterFirstUnlock)
649 .await
650 .expect("set_pin succeeds");
651
652 let bridge = client.km_state_bridge();
653 assert!(bridge.get_persistent_pin_envelope().await.is_none());
654 let ephemeral = bridge
655 .get_ephemeral_pin_envelope()
656 .await
657 .expect("ephemeral envelope present");
658 let encrypted_pin = bridge
659 .get_encrypted_pin()
660 .await
661 .expect("encrypted pin present");
662
663 assert_envelope_wraps_user_key(&client, &ephemeral, "1234", &user_key_id);
664 assert_eq!(decrypt_encrypted_pin(&client, &encrypted_pin), "1234");
665
666 assert_eq!(
667 system.get_pin_lock_type().await,
668 Some(PinLockType::AfterFirstUnlock)
669 );
670 assert_eq!(system.get_pin_status().await, PinUnlockStatus::Available);
671 }
672
673 #[tokio::test]
674 async fn set_pin_overwrites_existing_state() {
675 let client = client_with_user_key();
676 let system = PinLockSystem::with_client(&client);
677
678 system
679 .set_pin("first".into(), PinLockType::BeforeFirstUnlock)
680 .await
681 .expect("first set_pin");
682 system
683 .set_pin("second".into(), PinLockType::AfterFirstUnlock)
684 .await
685 .expect("second set_pin");
686
687 let bridge = client.km_state_bridge();
688 assert!(
689 bridge.get_persistent_pin_envelope().await.is_none(),
690 "switching to AFU must clear the persistent envelope"
691 );
692 assert_eq!(
693 system.get_pin_lock_type().await,
694 Some(PinLockType::AfterFirstUnlock)
695 );
696 assert!(system.validate_pin("second".into()).await);
697 assert!(!system.validate_pin("first".into()).await);
698 }
699
700 #[tokio::test]
701 async fn unset_pin_clears_all_state() {
702 let client = client_with_user_key();
703 let system = PinLockSystem::with_client(&client);
704
705 system
706 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
707 .await
708 .expect("set_pin succeeds");
709 system.unset_pin().await;
710
711 let bridge = client.km_state_bridge();
712 assert!(bridge.get_persistent_pin_envelope().await.is_none());
713 assert!(bridge.get_ephemeral_pin_envelope().await.is_none());
714 assert!(bridge.get_encrypted_pin().await.is_none());
715 assert_eq!(system.get_pin_lock_type().await, None);
716 assert_eq!(system.get_pin_status().await, PinUnlockStatus::NotSet);
717 }
718
719 #[tokio::test]
720 async fn unlock_with_correct_pin_persists_user_key() {
721 let client = client_with_user_key();
722 let system = PinLockSystem::with_client(&client);
723
724 let pre_unlock_user_key_id = user_key_id(&client);
725 system
727 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
728 .await
729 .expect("set_pin succeeds");
730 client.internal.get_key_store().clear();
731
732 assert!(system.unlock("1234").await.is_ok());
733 let post_unlock_user_key_id = user_key_id(&client);
734 assert_eq!(post_unlock_user_key_id, pre_unlock_user_key_id);
735 }
736
737 #[tokio::test]
738 async fn unlock_with_wrong_pin_returns_pin_wrong() {
739 let client = client_with_user_key();
740 let system = PinLockSystem::with_client(&client);
741 system
742 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
743 .await
744 .expect("set_pin succeeds");
745
746 assert!(matches!(
747 system.unlock("wrong").await,
748 Err(UnlockError::PinWrong)
749 ));
750 }
751
752 #[tokio::test]
753 async fn unlock_with_no_pin_set_returns_no_pin_set() {
754 let client = client_with_user_key();
755 let system = PinLockSystem::with_client(&client);
756
757 assert!(matches!(
758 system.unlock("anything").await,
759 Err(UnlockError::NoPinSet)
760 ));
761 }
762
763 #[tokio::test]
764 async fn unlock_prefers_ephemeral_envelope_over_persistent() {
765 let client = client_with_user_key();
766 let system = PinLockSystem::with_client(&client);
767 system
768 .set_pin("persistent".into(), PinLockType::BeforeFirstUnlock)
769 .await
770 .expect("set_pin succeeds");
771
772 let ephemeral = seal_envelope(&client, "ephemeral");
775 client
776 .km_state_bridge()
777 .set_ephemeral_pin_envelope(&ephemeral)
778 .await;
779
780 assert!(system.unlock("ephemeral").await.is_ok());
781 assert!(matches!(
782 system.unlock("persistent").await,
783 Err(UnlockError::PinWrong)
784 ));
785 }
786
787 #[tokio::test]
788 async fn get_pin_status_available_bfu() {
789 let client = client_with_user_key();
790 let system = PinLockSystem::with_client(&client);
791 system
792 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
793 .await
794 .expect("set_pin succeeds");
795
796 client
798 .km_state_bridge()
799 .clear_ephemeral_pin_envelope()
800 .await;
801
802 assert_eq!(system.get_pin_status().await, PinUnlockStatus::Available);
803 assert_eq!(
804 system.get_pin_lock_type().await,
805 Some(PinLockType::BeforeFirstUnlock)
806 );
807 }
808
809 #[tokio::test]
810 async fn on_unlock_rebuilds_ephemeral_envelope() {
811 let client = client_with_user_key();
812 let user_key_id = user_key_id(&client);
813 let system = PinLockSystem::with_client(&client);
814 system
815 .set_pin("1234".into(), PinLockType::AfterFirstUnlock)
816 .await
817 .expect("set_pin succeeds");
818 client
819 .km_state_bridge()
820 .clear_ephemeral_pin_envelope()
821 .await;
822 assert_eq!(system.get_pin_status().await, PinUnlockStatus::NeedsUnlock);
823
824 system.on_unlock().await;
825
826 let rebuilt = client
827 .km_state_bridge()
828 .get_ephemeral_pin_envelope()
829 .await
830 .expect("on_unlock should restore the ephemeral envelope");
831 assert_envelope_wraps_user_key(&client, &rebuilt, "1234", &user_key_id);
832 assert_eq!(system.get_pin_status().await, PinUnlockStatus::Available);
833 assert!(system.unlock("1234").await.is_ok());
834 }
835
836 #[tokio::test]
837 async fn on_unlock_is_noop_when_no_encrypted_pin() {
838 let client = client_with_user_key();
839 let system = PinLockSystem::with_client(&client);
840
841 system.on_unlock().await;
842
843 assert_eq!(system.get_pin_status().await, PinUnlockStatus::NotSet);
844 }
845
846 #[tokio::test]
847 async fn on_unlock_is_noop_when_bridge_not_registered() {
848 let client = Client::new(None);
849 let system = PinLockSystem::with_client(&client);
850
851 system.on_unlock().await;
853 }
854
855 #[tokio::test]
856 async fn get_pin_returns_set_pin() {
857 let client = client_with_user_key();
858 let system = PinLockSystem::with_client(&client);
859
860 assert_eq!(system.get_pin().await, None);
861
862 system
863 .set_pin("1234".into(), PinLockType::AfterFirstUnlock)
864 .await
865 .expect("set_pin succeeds");
866 assert_eq!(system.get_pin().await, Some("1234".to_owned()));
867
868 system.unset_pin().await;
869 assert_eq!(system.get_pin().await, None);
870 }
871
872 #[tokio::test]
873 async fn validate_pin_matches_only_correct_pin() {
874 let client = client_with_user_key();
875 let system = PinLockSystem::with_client(&client);
876
877 assert!(!system.validate_pin("anything".into()).await);
878
879 system
880 .set_pin("1234".into(), PinLockType::AfterFirstUnlock)
881 .await
882 .expect("set_pin succeeds");
883 assert!(system.validate_pin("1234".into()).await);
884 assert!(!system.validate_pin("wrong".into()).await);
885 }
886
887 struct V1State {
890 envelope: PasswordProtectedKeyEnvelope,
891 encrypted_pin: EncString,
892 token: V2UpgradeToken,
893 }
894
895 fn fresh_v1_state_with_v2_user_key(pin: &str) -> (Client, V1State) {
899 let client = Client::new(None);
900 client
901 .km_state_bridge()
902 .register_bridge(Box::new(InMemoryStateBridge::default()));
903
904 let state = {
905 let key_store = client.internal.get_key_store();
906 let mut ctx = key_store.context_mut();
907
908 let v1_local = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
909 let v2_local = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
910
911 let envelope = PasswordProtectedKeyEnvelope::seal(
912 v1_local,
913 pin,
914 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
915 &ctx,
916 )
917 .expect("v1 envelope seals");
918 let encrypted_pin = pin
919 .encrypt(&mut ctx, v1_local)
920 .expect("pin encrypts under v1 key");
921 let token =
922 V2UpgradeToken::create(v1_local, v2_local, &ctx).expect("upgrade token created");
923
924 ctx.persist_symmetric_key(v2_local, SymmetricKeySlotId::User)
925 .expect("persisting v2 user key succeeds");
926
927 V1State {
928 envelope,
929 encrypted_pin,
930 token,
931 }
932 };
933
934 (client, state)
935 }
936
937 fn client_with_v1_user_key() -> Client {
940 let client = Client::new(None);
941 client
942 .km_state_bridge()
943 .register_bridge(Box::new(InMemoryStateBridge::default()));
944 {
945 let key_store = client.internal.get_key_store();
946 let mut ctx = key_store.context_mut();
947 let user_key = ctx.generate_symmetric_key();
948 ctx.persist_symmetric_key(user_key, SymmetricKeySlotId::User)
949 .expect("persisting v1 user key should succeed");
950 }
951 client
952 }
953
954 fn assert_pin_envelopes_equal(
955 envelope_1: &PasswordProtectedKeyEnvelope,
956 envelope_2: &PasswordProtectedKeyEnvelope,
957 ) {
958 assert_eq!(
959 serde_json::to_string(envelope_1).expect("envelope serializes"),
960 serde_json::to_string(envelope_2).expect("envelope serializes"),
961 "envelopes should be identical",
962 );
963 }
964
965 async fn assert_pin_fully_unenrolled(client: &Client) {
966 let bridge = client.km_state_bridge();
967 assert!(bridge.get_persistent_pin_envelope().await.is_none());
968 assert!(bridge.get_ephemeral_pin_envelope().await.is_none());
969 assert!(bridge.get_encrypted_pin().await.is_none());
970 assert_eq!(
971 PinLockSystem::with_client(client).get_pin_status().await,
972 PinUnlockStatus::NotSet,
973 );
974 }
975
976 fn test_key_id(byte: u8) -> KeyId {
984 KeyId::from([byte; 16])
985 }
986
987 #[test]
988 fn classify_matching_key_id_is_up_to_date() {
989 for user_key_is_v1 in [true, false] {
990 assert_eq!(
991 classify_pin_envelope(Some(&test_key_id(1)), &test_key_id(1), user_key_is_v1),
992 PinEnvelopeAction::UpToDate,
993 "user_key_is_v1 = {user_key_is_v1}",
994 );
995 }
996 }
997
998 #[test]
999 fn classify_missing_envelope_key_id_with_v1_user_key_backfills() {
1000 assert_eq!(
1001 classify_pin_envelope(None, &test_key_id(1), true),
1002 PinEnvelopeAction::BackfillKeyId,
1003 );
1004 }
1005
1006 #[test]
1007 fn classify_missing_envelope_key_id_with_v2_user_key_migrates() {
1008 assert_eq!(
1009 classify_pin_envelope(None, &test_key_id(1), false),
1010 PinEnvelopeAction::MigrateV1ToV2,
1011 );
1012 }
1013
1014 #[test]
1015 fn classify_differing_key_id_with_v1_user_key_fails() {
1016 assert_eq!(
1017 classify_pin_envelope(Some(&test_key_id(1)), &test_key_id(2), true),
1018 PinEnvelopeAction::Failed(MigrationFailed::EnvelopeWithKeyIdWithV1UserKey),
1019 );
1020 }
1021
1022 #[test]
1023 fn classify_differing_key_id_with_v2_user_key_migrates() {
1024 assert_eq!(
1025 classify_pin_envelope(Some(&test_key_id(1)), &test_key_id(2), false),
1026 PinEnvelopeAction::MigrateV1ToV2,
1027 );
1028 }
1029
1030 #[tokio::test]
1033 async fn migrate_without_user_key_fails_locked() {
1034 let client = Client::new(None);
1035 client
1036 .km_state_bridge()
1037 .register_bridge(Box::new(InMemoryStateBridge::default()));
1038 client
1039 .km_state_bridge()
1040 .set_persistent_pin_envelope(&legacy_envelope())
1041 .await;
1042
1043 let system = PinLockSystem::with_client(&client);
1044 assert_eq!(
1045 system.migrate_pin_envelope_if_needed().await,
1046 Err(MigrationFailed::Locked),
1047 );
1048 }
1049
1050 #[tokio::test]
1051 async fn migrate_without_persistent_envelope_is_noop() {
1052 let client = client_with_user_key();
1053 let system = PinLockSystem::with_client(&client);
1054
1055 system
1056 .migrate_pin_envelope_if_needed()
1057 .await
1058 .expect("migration succeeds");
1059
1060 assert_pin_fully_unenrolled(&client).await;
1061 }
1062
1063 #[tokio::test]
1065 async fn migrate_up_to_date_v2_envelope_is_noop() {
1066 let client = client_with_user_key();
1067 let system = PinLockSystem::with_client(&client);
1068 system
1069 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
1070 .await
1071 .expect("set_pin succeeds");
1072
1073 let bridge = client.km_state_bridge();
1074 let persistent_before = &bridge
1075 .get_persistent_pin_envelope()
1076 .await
1077 .expect("persistent envelope present");
1078 let ephemeral_before = &bridge
1079 .get_ephemeral_pin_envelope()
1080 .await
1081 .expect("ephemeral envelope present");
1082 let encrypted_pin_before = bridge
1083 .get_encrypted_pin()
1084 .await
1085 .expect("encrypted pin present")
1086 .to_string();
1087
1088 system
1089 .migrate_pin_envelope_if_needed()
1090 .await
1091 .expect("migration succeeds");
1092
1093 let persistent_after = &bridge
1094 .get_persistent_pin_envelope()
1095 .await
1096 .expect("persistent envelope still present");
1097 let ephemeral_after = &bridge
1098 .get_ephemeral_pin_envelope()
1099 .await
1100 .expect("ephemeral envelope still present");
1101 let encrypted_pin_after = bridge
1102 .get_encrypted_pin()
1103 .await
1104 .expect("encrypted pin still present")
1105 .to_string();
1106
1107 assert_pin_envelopes_equal(persistent_before, persistent_after);
1108 assert_pin_envelopes_equal(ephemeral_before, ephemeral_after);
1109 assert_eq!(encrypted_pin_before, encrypted_pin_after);
1110 }
1111
1112 #[tokio::test]
1115 async fn migrate_up_to_date_v1_envelope_is_noop() {
1116 let pin = "1234";
1117 let client = client_with_v1_user_key();
1118 let envelope = seal_envelope(&client, pin);
1119 let encrypted_pin = pin
1120 .encrypt(
1121 &mut client.internal.get_key_store().context_mut(),
1122 SymmetricKeySlotId::User,
1123 )
1124 .expect("encrypt under v1 user key");
1125
1126 let bridge = client.km_state_bridge();
1127 bridge.set_persistent_pin_envelope(&envelope).await;
1128 bridge.set_encrypted_pin(&encrypted_pin).await;
1129
1130 assert_eq!(
1131 envelope.contained_key_id().expect("readable"),
1132 Some(user_key_id(&client)),
1133 "a freshly sealed V1 envelope carries the V1 key's derived key id",
1134 );
1135
1136 let persistent_before = &envelope;
1137 let encrypted_pin_before = encrypted_pin.to_string();
1138
1139 let system = PinLockSystem::with_client(&client);
1140 system
1141 .migrate_pin_envelope_if_needed()
1142 .await
1143 .expect("migration succeeds");
1144
1145 let persistent_after = &bridge
1146 .get_persistent_pin_envelope()
1147 .await
1148 .expect("persistent envelope still present");
1149 let encrypted_pin_after = bridge
1150 .get_encrypted_pin()
1151 .await
1152 .expect("encrypted pin still present")
1153 .to_string();
1154 assert_pin_envelopes_equal(persistent_before, persistent_after);
1155 assert_eq!(encrypted_pin_before, encrypted_pin_after);
1156 assert!(bridge.get_ephemeral_pin_envelope().await.is_none());
1157 }
1158
1159 #[tokio::test]
1163 async fn migrate_legacy_envelope_with_v1_user_key_backfills_key_id() {
1164 let pin = TESTVECTOR_LEGACY_ENVELOPE_PIN;
1165 let client = client_with_v1_user_key();
1166 let encrypted_pin = pin
1167 .encrypt(
1168 &mut client.internal.get_key_store().context_mut(),
1169 SymmetricKeySlotId::User,
1170 )
1171 .expect("encrypt under v1 user key");
1172
1173 let bridge = client.km_state_bridge();
1174 bridge.set_persistent_pin_envelope(&legacy_envelope()).await;
1175 bridge.set_encrypted_pin(&encrypted_pin).await;
1176
1177 let user_key_id = user_key_id(&client);
1178 let system = PinLockSystem::with_client(&client);
1179 system
1180 .migrate_pin_envelope_if_needed()
1181 .await
1182 .expect("migration succeeds");
1183
1184 let persistent = bridge
1185 .get_persistent_pin_envelope()
1186 .await
1187 .expect("persistent envelope present after backfill");
1188 assert_envelope_wraps_user_key(&client, &persistent, pin, &user_key_id);
1189 assert!(system.unlock(pin).await.is_ok());
1190 }
1191
1192 #[tokio::test]
1194 async fn migrate_legacy_envelope_with_v1_user_key_without_encrypted_pin_fails() {
1195 let client = client_with_v1_user_key();
1196 let bridge = client.km_state_bridge();
1197 bridge.set_persistent_pin_envelope(&legacy_envelope()).await;
1198 let system = PinLockSystem::with_client(&client);
1201 assert_eq!(
1202 system.migrate_pin_envelope_if_needed().await,
1203 Err(MigrationFailed::MissingEncryptedPin),
1204 );
1205 }
1206
1207 #[tokio::test]
1210 async fn migrate_envelope_with_v1_user_key_and_other_key_id_fails() {
1211 let client = client_with_v1_user_key();
1212
1213 let v2_envelope = {
1215 let key_store = client.internal.get_key_store();
1216 let mut ctx = key_store.context_mut();
1217 let v2_local = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
1218 PasswordProtectedKeyEnvelope::seal(
1219 v2_local,
1220 "1234",
1221 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
1222 &ctx,
1223 )
1224 .expect("seal under v2 key")
1225 };
1226 assert!(
1227 v2_envelope.contained_key_id().expect("readable").is_some(),
1228 "envelope should be V2",
1229 );
1230
1231 let bridge = client.km_state_bridge();
1232 bridge.set_persistent_pin_envelope(&v2_envelope).await;
1233
1234 let system = PinLockSystem::with_client(&client);
1235 assert_eq!(
1236 system.migrate_pin_envelope_if_needed().await,
1237 Err(MigrationFailed::EnvelopeWithKeyIdWithV1UserKey),
1238 );
1239 }
1240
1241 #[tokio::test]
1243 async fn migrate_v1_envelope_with_v2_user_key_reseals_with_user_key() {
1244 let pin = "1234";
1245 let (client, state) = fresh_v1_state_with_v2_user_key(pin);
1246 let bridge = client.km_state_bridge();
1247 bridge.set_persistent_pin_envelope(&state.envelope).await;
1248 bridge.set_encrypted_pin(&state.encrypted_pin).await;
1249 bridge.set_v2_upgrade_token(&state.token).await;
1250
1251 let user_key_id = user_key_id(&client);
1252 assert_ne!(
1253 state.envelope.contained_key_id().expect("readable"),
1254 Some(user_key_id.clone()),
1255 "starting envelope holds the V1 key, not the current V2 user key",
1256 );
1257
1258 let system = PinLockSystem::with_client(&client);
1259 system
1260 .migrate_pin_envelope_if_needed()
1261 .await
1262 .expect("migration succeeds");
1263
1264 let persistent = bridge
1265 .get_persistent_pin_envelope()
1266 .await
1267 .expect("persistent envelope present after migration");
1268 let ephemeral = bridge
1269 .get_ephemeral_pin_envelope()
1270 .await
1271 .expect("ephemeral envelope present after migration");
1272 let encrypted_pin = bridge
1273 .get_encrypted_pin()
1274 .await
1275 .expect("encrypted pin present after migration");
1276
1277 assert_envelope_wraps_user_key(&client, &persistent, pin, &user_key_id);
1278 assert_envelope_wraps_user_key(&client, &ephemeral, pin, &user_key_id);
1279 assert_eq!(decrypt_encrypted_pin(&client, &encrypted_pin), pin);
1280 assert_eq!(
1281 system.get_pin_lock_type().await,
1282 Some(PinLockType::BeforeFirstUnlock),
1283 );
1284 assert_eq!(system.get_pin_status().await, PinUnlockStatus::Available);
1285 assert!(system.unlock(pin).await.is_ok());
1286 }
1287
1288 #[tokio::test]
1289 async fn migrate_v1_envelope_with_v2_user_key_without_token_fails() {
1290 let pin = "1234";
1291 let (client, state) = fresh_v1_state_with_v2_user_key(pin);
1292 let bridge = client.km_state_bridge();
1293 bridge.set_persistent_pin_envelope(&state.envelope).await;
1294 bridge.set_encrypted_pin(&state.encrypted_pin).await;
1295 let system = PinLockSystem::with_client(&client);
1298 assert_eq!(
1299 system.migrate_pin_envelope_if_needed().await,
1300 Err(MigrationFailed::MissingV2UpgradeToken),
1301 );
1302 }
1303
1304 #[tokio::test]
1305 async fn migrate_v1_envelope_with_v2_user_key_without_encrypted_pin_fails() {
1306 let pin = "1234";
1307 let (client, state) = fresh_v1_state_with_v2_user_key(pin);
1308 let bridge = client.km_state_bridge();
1309 bridge.set_persistent_pin_envelope(&state.envelope).await;
1310 bridge.set_v2_upgrade_token(&state.token).await;
1311 let system = PinLockSystem::with_client(&client);
1314 assert_eq!(
1315 system.migrate_pin_envelope_if_needed().await,
1316 Err(MigrationFailed::MissingEncryptedPin),
1317 );
1318 }
1319
1320 #[tokio::test]
1321 async fn migrate_v1_envelope_with_v2_user_key_with_mismatched_token_fails() {
1322 let pin = "1234";
1323 let (client, state) = fresh_v1_state_with_v2_user_key(pin);
1324
1325 let unrelated_token = {
1329 let key_store = bitwarden_crypto::KeyStore::<KeySlotIds>::default();
1330 let mut ctx = key_store.context_mut();
1331 let v1 = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
1332 let v2 = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
1333 V2UpgradeToken::create(v1, v2, &ctx).expect("unrelated token created")
1334 };
1335
1336 let bridge = client.km_state_bridge();
1337 bridge.set_persistent_pin_envelope(&state.envelope).await;
1338 bridge.set_encrypted_pin(&state.encrypted_pin).await;
1339 bridge.set_v2_upgrade_token(&unrelated_token).await;
1340
1341 let system = PinLockSystem::with_client(&client);
1342 assert_eq!(
1343 system.migrate_pin_envelope_if_needed().await,
1344 Err(MigrationFailed::PinDecryption),
1345 );
1346 }
1347
1348 #[tokio::test]
1351 async fn migrate_v2_envelope_with_rotated_user_key_fails() {
1352 let client = client_with_user_key();
1353 let system = PinLockSystem::with_client(&client);
1354 system
1355 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
1356 .await
1357 .expect("set_pin succeeds");
1358
1359 let (mismatched_envelope, token) = {
1362 let mut ctx = client.internal.get_key_store().context_mut();
1363 let other_v2 = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
1364 let envelope = PasswordProtectedKeyEnvelope::seal(
1365 other_v2,
1366 "1234",
1367 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
1368 &ctx,
1369 )
1370 .expect("seal under other v2 key");
1371
1372 let v1 = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
1373 let token = V2UpgradeToken::create(v1, SymmetricKeySlotId::User, &ctx)
1374 .expect("upgrade token created");
1375 (envelope, token)
1376 };
1377
1378 let bridge = client.km_state_bridge();
1379 bridge
1380 .set_persistent_pin_envelope(&mismatched_envelope)
1381 .await;
1382 bridge.set_v2_upgrade_token(&token).await;
1383
1384 assert_eq!(
1385 system.migrate_pin_envelope_if_needed().await,
1386 Err(MigrationFailed::V2KeyRotationUnsupported),
1387 );
1388 }
1389
1390 #[tokio::test]
1393 async fn migrate_v2_envelope_with_rotated_user_key_without_token_fails() {
1394 let client = client_with_user_key();
1395 let system = PinLockSystem::with_client(&client);
1396 system
1397 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
1398 .await
1399 .expect("set_pin succeeds");
1400
1401 let mismatched_envelope = {
1402 let mut ctx = client.internal.get_key_store().context_mut();
1403 let other_v2 = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
1404 PasswordProtectedKeyEnvelope::seal(
1405 other_v2,
1406 "1234",
1407 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
1408 &ctx,
1409 )
1410 .expect("seal under other v2 key")
1411 };
1412 client
1413 .km_state_bridge()
1414 .set_persistent_pin_envelope(&mismatched_envelope)
1415 .await;
1416
1417 assert_eq!(
1418 system.migrate_pin_envelope_if_needed().await,
1419 Err(MigrationFailed::MissingV2UpgradeToken),
1420 );
1421 }
1422
1423 #[tokio::test]
1424 async fn on_unlock_triggers_migration() {
1425 let pin = "1234";
1426 let (client, state) = fresh_v1_state_with_v2_user_key(pin);
1427 let bridge = client.km_state_bridge();
1428 bridge.set_persistent_pin_envelope(&state.envelope).await;
1429 bridge.set_encrypted_pin(&state.encrypted_pin).await;
1430 bridge.set_v2_upgrade_token(&state.token).await;
1431
1432 let user_key_id = user_key_id(&client);
1433 let system = PinLockSystem::with_client(&client);
1434
1435 system.on_unlock().await;
1436
1437 let persistent = bridge
1438 .get_persistent_pin_envelope()
1439 .await
1440 .expect("persistent envelope present after on_unlock");
1441 assert_envelope_wraps_user_key(&client, &persistent, pin, &user_key_id);
1442 assert!(system.unlock(pin).await.is_ok());
1443 }
1444
1445 #[tokio::test]
1446 async fn on_unlock_unenrolls_when_migration_fails() {
1447 let pin = "1234";
1449 let (client, state) = fresh_v1_state_with_v2_user_key(pin);
1450 let bridge = client.km_state_bridge();
1451 bridge.set_persistent_pin_envelope(&state.envelope).await;
1452 bridge.set_encrypted_pin(&state.encrypted_pin).await;
1453 let system = PinLockSystem::with_client(&client);
1456 system.on_unlock().await;
1457
1458 assert_pin_fully_unenrolled(&client).await;
1459 }
1460}