1use 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
61#[derive(Debug)]
62pub(crate) enum MigrationFailed {
63 EnvelopeMalformed,
65 V2KeyRotationUnsupported,
68 V2EnvelopeWithV1UserKey,
70 MissingV2UpgradeToken,
72 MissingEncryptedPin,
74 PinDecryption,
77 Reenrollment,
79}
80
81pub struct PinLockSystem<'a> {
85 client: &'a Client,
86}
87
88impl PinLockSystem<'_> {
89 fn key_store(&self) -> &KeyStore<KeySlotIds> {
90 self.client.internal.get_key_store()
91 }
92
93 pub fn with_client(client: &Client) -> PinLockSystem<'_> {
95 PinLockSystem { client }
96 }
97
98 async fn get_active_pin_envelope(&self) -> Option<PasswordProtectedKeyEnvelope> {
102 let mut pin_protected_key_envelope = self
103 .client
104 .km_state_bridge()
105 .get_ephemeral_pin_envelope()
106 .await;
107 if pin_protected_key_envelope.is_none() {
108 pin_protected_key_envelope = self
109 .client
110 .km_state_bridge()
111 .get_persistent_pin_envelope()
112 .await;
113 }
114 pin_protected_key_envelope
115 }
116
117 pub(crate) async fn unlock(&self, pin: &str) -> Result<(), UnlockError> {
123 let pin_envelope = Self::get_active_pin_envelope(self)
124 .await
125 .ok_or(UnlockError::NoPinSet)?;
126
127 let mut ctx = self.key_store().context_mut();
129 let key_slot = pin_envelope
130 .unseal(
131 pin,
132 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
133 &mut ctx,
134 )
135 .map_err(|e| match e {
136 bitwarden_crypto::safe::PasswordProtectedKeyEnvelopeError::WrongPassword => {
137 UnlockError::PinWrong
138 }
139 _ => UnlockError::InternalError,
140 })?;
141
142 ctx.persist_symmetric_key(key_slot, SymmetricKeySlotId::User)
145 .map_err(|_| UnlockError::InternalError)
146 }
147
148 async fn migrate_pin_envelope_if_needed(&self) -> Result<(), MigrationFailed> {
152 let Some(envelope) = self
153 .client
154 .km_state_bridge()
155 .get_persistent_pin_envelope()
156 .await
157 else {
158 return Ok(());
159 };
160
161 let envelope_key_id = envelope
162 .contained_key_id()
163 .map_err(|_| MigrationFailed::EnvelopeMalformed)?;
164 let current_user_key_id = self
165 .key_store()
166 .context()
167 .get_symmetric_key_id(SymmetricKeySlotId::User);
168 match (envelope_key_id, current_user_key_id) {
171 (Some(envelope_key_id), Some(current_user_key_id))
173 if envelope_key_id == current_user_key_id =>
174 {
175 return Ok(());
176 }
177 (None, Some(_)) => {}
179 (Some(_), Some(_)) => return Err(MigrationFailed::V2KeyRotationUnsupported),
181 (Some(_), None) => return Err(MigrationFailed::V2EnvelopeWithV1UserKey),
183 (None, None) => return Ok(()),
185 }
186
187 let token = self
188 .client
189 .km_state_bridge()
190 .get_v2_upgrade_token()
191 .await
192 .ok_or(MigrationFailed::MissingV2UpgradeToken)?;
193 let encrypted_pin = self
194 .client
195 .km_state_bridge()
196 .get_encrypted_pin()
197 .await
198 .ok_or(MigrationFailed::MissingEncryptedPin)?;
199
200 let pin = (|| -> Result<String, ()> {
202 let mut ctx = self.key_store().context_mut();
203 let v1_slot = token
204 .unwrap_v1(SymmetricKeySlotId::User, &mut ctx)
205 .map_err(|_| ())?;
206 encrypted_pin.decrypt(&mut ctx, v1_slot).map_err(|_| ())
207 })()
208 .map_err(|_| MigrationFailed::PinDecryption)?;
209
210 self.set_pin(pin, PinLockType::BeforeFirstUnlock)
212 .await
213 .map_err(|_| MigrationFailed::Reenrollment)?;
214
215 Ok(())
216 }
217
218 pub(crate) async fn on_unlock(&self) {
222 if !self.client.km_state_bridge().is_bridge_registered() {
224 return;
225 }
226
227 if let Err(e) = self.migrate_pin_envelope_if_needed().await {
228 warn!("PIN migration failed: {e:?}, unenrolling PIN");
229 self.unset_pin().await;
230 return;
231 }
232
233 let encrypted_pin = self.client.km_state_bridge().get_encrypted_pin().await;
234
235 let Some(encrypted_pin) = encrypted_pin else {
237 return;
238 };
239
240 let Ok(pin_envelope) = (|| -> Result<PasswordProtectedKeyEnvelope, ()> {
242 let mut ctx = self.key_store().context_mut();
243 let pin: String = encrypted_pin
244 .decrypt(&mut ctx, SymmetricKeySlotId::User)
245 .map_err(|_| ())?;
246 PasswordProtectedKeyEnvelope::seal(
247 SymmetricKeySlotId::User,
248 pin.as_str(),
249 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
250 &ctx,
251 )
252 .map_err(|_| ())
253 })() else {
254 warn!("Failed to create PIN envelope");
255 return;
256 };
257
258 self.client
260 .km_state_bridge()
261 .set_ephemeral_pin_envelope(&pin_envelope)
262 .await;
263 }
264
265 pub async fn set_pin(&self, pin: String, lock_type: PinLockType) -> Result<(), ()> {
267 self.client
269 .km_state_bridge()
270 .clear_persistent_pin_envelope()
271 .await;
272 self.client
273 .km_state_bridge()
274 .clear_ephemeral_pin_envelope()
275 .await;
276 self.client.km_state_bridge().clear_encrypted_pin().await;
277
278 let pin_envelope: PasswordProtectedKeyEnvelope = PasswordProtectedKeyEnvelope::seal(
279 SymmetricKeySlotId::User,
280 pin.as_str(),
281 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
282 &self.key_store().context_mut(),
283 )
284 .map_err(|_| ())?;
285 let encrypted_pin = pin
286 .encrypt(
287 &mut self.key_store().context_mut(),
288 SymmetricKeySlotId::User,
289 )
290 .map_err(|_| ())?;
291
292 self.client
293 .km_state_bridge()
294 .set_encrypted_pin(&encrypted_pin)
295 .await;
296 self.client
297 .km_state_bridge()
298 .set_ephemeral_pin_envelope(&pin_envelope)
299 .await;
300
301 if lock_type == PinLockType::BeforeFirstUnlock {
302 self.client
303 .km_state_bridge()
304 .set_persistent_pin_envelope(&pin_envelope)
305 .await;
306 }
307
308 Ok(())
309 }
310
311 pub async fn unset_pin(&self) {
313 self.client
314 .km_state_bridge()
315 .clear_persistent_pin_envelope()
316 .await;
317 self.client
318 .km_state_bridge()
319 .clear_ephemeral_pin_envelope()
320 .await;
321 self.client.km_state_bridge().clear_encrypted_pin().await;
322 }
323
324 pub async fn get_pin_lock_type(&self) -> Option<PinLockType> {
326 if self
327 .client
328 .km_state_bridge()
329 .get_persistent_pin_envelope()
330 .await
331 .is_some()
332 {
333 return Some(PinLockType::BeforeFirstUnlock);
334 }
335
336 if self
340 .client
341 .km_state_bridge()
342 .get_encrypted_pin()
343 .await
344 .is_some()
345 {
346 return Some(PinLockType::AfterFirstUnlock);
347 }
348
349 None
350 }
351
352 pub async fn get_pin_status(&self) -> PinUnlockStatus {
357 match Self::get_pin_lock_type(self).await {
358 Some(PinLockType::BeforeFirstUnlock) => {
359 if self.get_active_pin_envelope().await.is_some() {
360 PinUnlockStatus::Available
361 } else {
362 PinUnlockStatus::NeedsUnlock
363 }
364 }
365 Some(PinLockType::AfterFirstUnlock) => {
366 if self
367 .client
368 .km_state_bridge()
369 .get_ephemeral_pin_envelope()
370 .await
371 .is_some()
372 {
373 PinUnlockStatus::Available
374 } else {
375 PinUnlockStatus::NeedsUnlock
378 }
379 }
380 None => PinUnlockStatus::NotSet,
381 }
382 }
383
384 pub async fn get_pin(&self) -> Option<String> {
386 let encrypted_pin = self.client.km_state_bridge().get_encrypted_pin().await?;
387 encrypted_pin
388 .decrypt(
389 &mut self.client.internal.get_key_store().context_mut(),
390 SymmetricKeySlotId::User,
391 )
392 .ok()
393 }
394
395 pub async fn validate_pin(&self, pin: String) -> bool {
397 let pin_envelope = self.get_active_pin_envelope().await;
398 let Some(pin_envelope) = pin_envelope else {
399 return false;
400 };
401
402 pin_envelope
403 .unseal(
404 pin.as_str(),
405 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
406 &mut self.key_store().context_mut(),
407 )
408 .is_ok()
409 }
410}
411
412#[cfg(test)]
413mod tests {
414 use bitwarden_crypto::{EncString, KeyId, SymmetricKeyAlgorithm};
415
416 use super::*;
417 use crate::key_management::{V2UpgradeToken, state_bridge::test_support::InMemoryStateBridge};
418
419 fn decrypt_encrypted_pin(client: &Client, encrypted_pin: &EncString) -> String {
420 encrypted_pin
421 .decrypt(
422 &mut client.internal.get_key_store().context_mut(),
423 SymmetricKeySlotId::User,
424 )
425 .expect("encrypted pin should decrypt successfully")
426 }
427
428 fn user_key_id(client: &Client) -> KeyId {
430 client
431 .internal
432 .get_key_store()
433 .context()
434 .get_symmetric_key_id(SymmetricKeySlotId::User)
435 .expect("user key present")
436 }
437
438 fn assert_envelope_wraps_user_key(
440 client: &Client,
441 envelope: &PasswordProtectedKeyEnvelope,
442 pin: &str,
443 expected_key_id: &KeyId,
444 ) {
445 assert_eq!(
446 envelope
447 .contained_key_id()
448 .expect("contained key id readable"),
449 Some(expected_key_id.clone()),
450 "envelope wraps a key other than the current user key",
451 );
452 let _ = envelope
453 .unseal(
454 pin,
455 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
456 &mut client.internal.get_key_store().context_mut(),
457 )
458 .expect("envelope unseals with the configured pin");
459 }
460
461 fn client_with_user_key() -> Client {
462 let client = Client::new(None);
463 client
464 .km_state_bridge()
465 .register_bridge(Box::new(InMemoryStateBridge::default()));
466 {
467 let key_store = client.internal.get_key_store();
468 let mut ctx = key_store.context_mut();
469 let user_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
470 ctx.persist_symmetric_key(user_key, SymmetricKeySlotId::User)
471 .expect("persisting user key should succeed");
472 }
473 client
474 }
475
476 fn seal_envelope(client: &Client, pin: &str) -> PasswordProtectedKeyEnvelope {
477 PasswordProtectedKeyEnvelope::seal(
478 SymmetricKeySlotId::User,
479 pin,
480 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
481 &client.internal.get_key_store().context_mut(),
482 )
483 .expect("seal succeeds")
484 }
485
486 #[tokio::test]
487 async fn set_pin_bfu_persists_both_envelopes() {
488 let client = client_with_user_key();
489 let user_key_id = user_key_id(&client);
490 let system = PinLockSystem::with_client(&client);
491
492 system
493 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
494 .await
495 .expect("set_pin succeeds");
496
497 let bridge = client.km_state_bridge();
498 let persistent = bridge
499 .get_persistent_pin_envelope()
500 .await
501 .expect("persistent envelope present");
502 let ephemeral = bridge
503 .get_ephemeral_pin_envelope()
504 .await
505 .expect("ephemeral envelope present");
506 let encrypted_pin = bridge
507 .get_encrypted_pin()
508 .await
509 .expect("encrypted pin present");
510
511 assert_envelope_wraps_user_key(&client, &persistent, "1234", &user_key_id);
512 assert_envelope_wraps_user_key(&client, &ephemeral, "1234", &user_key_id);
513 assert_eq!(decrypt_encrypted_pin(&client, &encrypted_pin), "1234");
514
515 assert_eq!(
516 system.get_pin_lock_type().await,
517 Some(PinLockType::BeforeFirstUnlock)
518 );
519 assert_eq!(system.get_pin_status().await, PinUnlockStatus::Available);
520 }
521
522 #[tokio::test]
523 async fn set_pin_afu_persists_only_ephemeral() {
524 let client = client_with_user_key();
525 let user_key_id = user_key_id(&client);
526 let system = PinLockSystem::with_client(&client);
527
528 system
529 .set_pin("1234".into(), PinLockType::AfterFirstUnlock)
530 .await
531 .expect("set_pin succeeds");
532
533 let bridge = client.km_state_bridge();
534 assert!(bridge.get_persistent_pin_envelope().await.is_none());
535 let ephemeral = bridge
536 .get_ephemeral_pin_envelope()
537 .await
538 .expect("ephemeral envelope present");
539 let encrypted_pin = bridge
540 .get_encrypted_pin()
541 .await
542 .expect("encrypted pin present");
543
544 assert_envelope_wraps_user_key(&client, &ephemeral, "1234", &user_key_id);
545 assert_eq!(decrypt_encrypted_pin(&client, &encrypted_pin), "1234");
546
547 assert_eq!(
548 system.get_pin_lock_type().await,
549 Some(PinLockType::AfterFirstUnlock)
550 );
551 assert_eq!(system.get_pin_status().await, PinUnlockStatus::Available);
552 }
553
554 #[tokio::test]
555 async fn set_pin_overwrites_existing_state() {
556 let client = client_with_user_key();
557 let system = PinLockSystem::with_client(&client);
558
559 system
560 .set_pin("first".into(), PinLockType::BeforeFirstUnlock)
561 .await
562 .expect("first set_pin");
563 system
564 .set_pin("second".into(), PinLockType::AfterFirstUnlock)
565 .await
566 .expect("second set_pin");
567
568 let bridge = client.km_state_bridge();
569 assert!(
570 bridge.get_persistent_pin_envelope().await.is_none(),
571 "switching to AFU must clear the persistent envelope"
572 );
573 assert_eq!(
574 system.get_pin_lock_type().await,
575 Some(PinLockType::AfterFirstUnlock)
576 );
577 assert!(system.validate_pin("second".into()).await);
578 assert!(!system.validate_pin("first".into()).await);
579 }
580
581 #[tokio::test]
582 async fn unset_pin_clears_all_state() {
583 let client = client_with_user_key();
584 let system = PinLockSystem::with_client(&client);
585
586 system
587 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
588 .await
589 .expect("set_pin succeeds");
590 system.unset_pin().await;
591
592 let bridge = client.km_state_bridge();
593 assert!(bridge.get_persistent_pin_envelope().await.is_none());
594 assert!(bridge.get_ephemeral_pin_envelope().await.is_none());
595 assert!(bridge.get_encrypted_pin().await.is_none());
596 assert_eq!(system.get_pin_lock_type().await, None);
597 assert_eq!(system.get_pin_status().await, PinUnlockStatus::NotSet);
598 }
599
600 #[tokio::test]
601 async fn unlock_with_correct_pin_persists_user_key() {
602 let client = client_with_user_key();
603 let system = PinLockSystem::with_client(&client);
604
605 let pre_unlock_user_key_id = user_key_id(&client);
606 system
608 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
609 .await
610 .expect("set_pin succeeds");
611 client.internal.get_key_store().clear();
612
613 assert!(system.unlock("1234").await.is_ok());
614 let post_unlock_user_key_id = user_key_id(&client);
615 assert_eq!(post_unlock_user_key_id, pre_unlock_user_key_id);
616 }
617
618 #[tokio::test]
619 async fn unlock_with_wrong_pin_returns_pin_wrong() {
620 let client = client_with_user_key();
621 let system = PinLockSystem::with_client(&client);
622 system
623 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
624 .await
625 .expect("set_pin succeeds");
626
627 assert!(matches!(
628 system.unlock("wrong").await,
629 Err(UnlockError::PinWrong)
630 ));
631 }
632
633 #[tokio::test]
634 async fn unlock_with_no_pin_set_returns_no_pin_set() {
635 let client = client_with_user_key();
636 let system = PinLockSystem::with_client(&client);
637
638 assert!(matches!(
639 system.unlock("anything").await,
640 Err(UnlockError::NoPinSet)
641 ));
642 }
643
644 #[tokio::test]
645 async fn unlock_prefers_ephemeral_envelope_over_persistent() {
646 let client = client_with_user_key();
647 let system = PinLockSystem::with_client(&client);
648 system
649 .set_pin("persistent".into(), PinLockType::BeforeFirstUnlock)
650 .await
651 .expect("set_pin succeeds");
652
653 let ephemeral = seal_envelope(&client, "ephemeral");
656 client
657 .km_state_bridge()
658 .set_ephemeral_pin_envelope(&ephemeral)
659 .await;
660
661 assert!(system.unlock("ephemeral").await.is_ok());
662 assert!(matches!(
663 system.unlock("persistent").await,
664 Err(UnlockError::PinWrong)
665 ));
666 }
667
668 #[tokio::test]
669 async fn get_pin_status_available_bfu() {
670 let client = client_with_user_key();
671 let system = PinLockSystem::with_client(&client);
672 system
673 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
674 .await
675 .expect("set_pin succeeds");
676
677 client
679 .km_state_bridge()
680 .clear_ephemeral_pin_envelope()
681 .await;
682
683 assert_eq!(system.get_pin_status().await, PinUnlockStatus::Available);
684 assert_eq!(
685 system.get_pin_lock_type().await,
686 Some(PinLockType::BeforeFirstUnlock)
687 );
688 }
689
690 #[tokio::test]
691 async fn on_unlock_rebuilds_ephemeral_envelope() {
692 let client = client_with_user_key();
693 let user_key_id = user_key_id(&client);
694 let system = PinLockSystem::with_client(&client);
695 system
696 .set_pin("1234".into(), PinLockType::AfterFirstUnlock)
697 .await
698 .expect("set_pin succeeds");
699 client
700 .km_state_bridge()
701 .clear_ephemeral_pin_envelope()
702 .await;
703 assert_eq!(system.get_pin_status().await, PinUnlockStatus::NeedsUnlock);
704
705 system.on_unlock().await;
706
707 let rebuilt = client
708 .km_state_bridge()
709 .get_ephemeral_pin_envelope()
710 .await
711 .expect("on_unlock should restore the ephemeral envelope");
712 assert_envelope_wraps_user_key(&client, &rebuilt, "1234", &user_key_id);
713 assert_eq!(system.get_pin_status().await, PinUnlockStatus::Available);
714 assert!(system.unlock("1234").await.is_ok());
715 }
716
717 #[tokio::test]
718 async fn on_unlock_is_noop_when_no_encrypted_pin() {
719 let client = client_with_user_key();
720 let system = PinLockSystem::with_client(&client);
721
722 system.on_unlock().await;
723
724 assert_eq!(system.get_pin_status().await, PinUnlockStatus::NotSet);
725 }
726
727 #[tokio::test]
728 async fn on_unlock_is_noop_when_bridge_not_registered() {
729 let client = Client::new(None);
730 let system = PinLockSystem::with_client(&client);
731
732 system.on_unlock().await;
734 }
735
736 #[tokio::test]
737 async fn get_pin_returns_set_pin() {
738 let client = client_with_user_key();
739 let system = PinLockSystem::with_client(&client);
740
741 assert_eq!(system.get_pin().await, None);
742
743 system
744 .set_pin("1234".into(), PinLockType::AfterFirstUnlock)
745 .await
746 .expect("set_pin succeeds");
747 assert_eq!(system.get_pin().await, Some("1234".to_owned()));
748
749 system.unset_pin().await;
750 assert_eq!(system.get_pin().await, None);
751 }
752
753 #[tokio::test]
754 async fn validate_pin_matches_only_correct_pin() {
755 let client = client_with_user_key();
756 let system = PinLockSystem::with_client(&client);
757
758 assert!(!system.validate_pin("anything".into()).await);
759
760 system
761 .set_pin("1234".into(), PinLockType::AfterFirstUnlock)
762 .await
763 .expect("set_pin succeeds");
764 assert!(system.validate_pin("1234".into()).await);
765 assert!(!system.validate_pin("wrong".into()).await);
766 }
767
768 struct V1State {
771 envelope: PasswordProtectedKeyEnvelope,
772 encrypted_pin: EncString,
773 token: V2UpgradeToken,
774 }
775
776 fn fresh_v1_state_with_v2_user_key(pin: &str) -> (Client, V1State) {
780 let client = Client::new(None);
781 client
782 .km_state_bridge()
783 .register_bridge(Box::new(InMemoryStateBridge::default()));
784
785 let state = {
786 let key_store = client.internal.get_key_store();
787 let mut ctx = key_store.context_mut();
788
789 let v1_local = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
790 let v2_local = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
791
792 let envelope = PasswordProtectedKeyEnvelope::seal(
793 v1_local,
794 pin,
795 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
796 &ctx,
797 )
798 .expect("v1 envelope seals");
799 let encrypted_pin = pin
800 .encrypt(&mut ctx, v1_local)
801 .expect("pin encrypts under v1 key");
802 let token =
803 V2UpgradeToken::create(v1_local, v2_local, &ctx).expect("upgrade token created");
804
805 ctx.persist_symmetric_key(v2_local, SymmetricKeySlotId::User)
806 .expect("persisting v2 user key succeeds");
807
808 V1State {
809 envelope,
810 encrypted_pin,
811 token,
812 }
813 };
814
815 (client, state)
816 }
817
818 fn client_with_v1_user_key() -> Client {
821 let client = Client::new(None);
822 client
823 .km_state_bridge()
824 .register_bridge(Box::new(InMemoryStateBridge::default()));
825 {
826 let key_store = client.internal.get_key_store();
827 let mut ctx = key_store.context_mut();
828 let user_key = ctx.generate_symmetric_key();
829 ctx.persist_symmetric_key(user_key, SymmetricKeySlotId::User)
830 .expect("persisting v1 user key should succeed");
831 }
832 client
833 }
834
835 fn assert_pin_envelopes_equal(
836 envelope_1: &PasswordProtectedKeyEnvelope,
837 envelope_2: &PasswordProtectedKeyEnvelope,
838 ) {
839 assert_eq!(
840 serde_json::to_string(envelope_1).expect("envelope serializes"),
841 serde_json::to_string(envelope_2).expect("envelope serializes"),
842 "envelopes should be identical",
843 );
844 }
845
846 async fn assert_pin_fully_unenrolled(client: &Client) {
847 let bridge = client.km_state_bridge();
848 assert!(bridge.get_persistent_pin_envelope().await.is_none());
849 assert!(bridge.get_ephemeral_pin_envelope().await.is_none());
850 assert!(bridge.get_encrypted_pin().await.is_none());
851 assert_eq!(
852 PinLockSystem::with_client(client).get_pin_status().await,
853 PinUnlockStatus::NotSet,
854 );
855 }
856
857 #[tokio::test]
858 async fn migrate_v1_to_v2_reseals_envelope_with_user_key() {
859 let pin = "1234";
860 let (client, state) = fresh_v1_state_with_v2_user_key(pin);
861 let bridge = client.km_state_bridge();
862 bridge.set_persistent_pin_envelope(&state.envelope).await;
863 bridge.set_encrypted_pin(&state.encrypted_pin).await;
864 bridge.set_v2_upgrade_token(&state.token).await;
865
866 assert_eq!(
867 state.envelope.contained_key_id().expect("readable"),
868 None,
869 "starting envelope is V1 (no key_id)",
870 );
871
872 let user_key_id = user_key_id(&client);
873 let system = PinLockSystem::with_client(&client);
874
875 system
876 .migrate_pin_envelope_if_needed()
877 .await
878 .expect("migration succeeds");
879
880 let persistent = bridge
881 .get_persistent_pin_envelope()
882 .await
883 .expect("persistent envelope present after migration");
884 let ephemeral = bridge
885 .get_ephemeral_pin_envelope()
886 .await
887 .expect("ephemeral envelope present after migration");
888 let encrypted_pin = bridge
889 .get_encrypted_pin()
890 .await
891 .expect("encrypted pin present after migration");
892
893 assert_envelope_wraps_user_key(&client, &persistent, pin, &user_key_id);
894 assert_envelope_wraps_user_key(&client, &ephemeral, pin, &user_key_id);
895 assert_eq!(decrypt_encrypted_pin(&client, &encrypted_pin), pin);
896 assert_eq!(
897 system.get_pin_lock_type().await,
898 Some(PinLockType::BeforeFirstUnlock),
899 );
900 assert_eq!(system.get_pin_status().await, PinUnlockStatus::Available);
901 assert!(system.unlock(pin).await.is_ok());
902 }
903
904 #[tokio::test]
905 async fn on_unlock_triggers_v1_to_v2_migration() {
906 let pin = "1234";
907 let (client, state) = fresh_v1_state_with_v2_user_key(pin);
908 let bridge = client.km_state_bridge();
909 bridge.set_persistent_pin_envelope(&state.envelope).await;
910 bridge.set_encrypted_pin(&state.encrypted_pin).await;
911 bridge.set_v2_upgrade_token(&state.token).await;
912
913 let user_key_id = user_key_id(&client);
914 let system = PinLockSystem::with_client(&client);
915
916 system.on_unlock().await;
917
918 let persistent = bridge
919 .get_persistent_pin_envelope()
920 .await
921 .expect("persistent envelope present after on_unlock");
922 assert_envelope_wraps_user_key(&client, &persistent, pin, &user_key_id);
923 assert!(system.unlock(pin).await.is_ok());
924 }
925
926 #[tokio::test]
927 async fn migrate_no_persistent_envelope_is_noop() {
928 let client = client_with_user_key();
929 let system = PinLockSystem::with_client(&client);
930
931 system
932 .migrate_pin_envelope_if_needed()
933 .await
934 .expect("migration succeeds");
935
936 assert_pin_fully_unenrolled(&client).await;
937 }
938
939 #[tokio::test]
940 async fn migrate_v2_envelope_matching_user_key_is_noop() {
941 let client = client_with_user_key();
942 let system = PinLockSystem::with_client(&client);
943 system
944 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
945 .await
946 .expect("set_pin succeeds");
947
948 let bridge = client.km_state_bridge();
949 let persistent_before = &bridge
950 .get_persistent_pin_envelope()
951 .await
952 .expect("persistent envelope present");
953 let ephemeral_before = &bridge
954 .get_ephemeral_pin_envelope()
955 .await
956 .expect("ephemeral envelope present");
957 let encrypted_pin_before = bridge
958 .get_encrypted_pin()
959 .await
960 .expect("encrypted pin present")
961 .to_string();
962
963 system
964 .migrate_pin_envelope_if_needed()
965 .await
966 .expect("migration succeeds");
967
968 let persistent_after = &bridge
969 .get_persistent_pin_envelope()
970 .await
971 .expect("persistent envelope still present");
972 let ephemeral_after = &bridge
973 .get_ephemeral_pin_envelope()
974 .await
975 .expect("ephemeral envelope still present");
976 let encrypted_pin_after = bridge
977 .get_encrypted_pin()
978 .await
979 .expect("encrypted pin still present")
980 .to_string();
981
982 assert_pin_envelopes_equal(persistent_before, persistent_after);
983 assert_pin_envelopes_equal(ephemeral_before, ephemeral_after);
984 assert_eq!(encrypted_pin_before, encrypted_pin_after);
985 }
986
987 #[tokio::test]
988 async fn migrate_v1_envelope_with_v1_user_key_is_noop() {
989 let pin = "1234";
990 let client = client_with_v1_user_key();
991 let envelope = seal_envelope(&client, pin);
992 let encrypted_pin = pin
993 .encrypt(
994 &mut client.internal.get_key_store().context_mut(),
995 SymmetricKeySlotId::User,
996 )
997 .expect("encrypt under v1 user key");
998
999 let bridge = client.km_state_bridge();
1000 bridge.set_persistent_pin_envelope(&envelope).await;
1001 bridge.set_encrypted_pin(&encrypted_pin).await;
1002
1003 assert_eq!(envelope.contained_key_id().expect("readable"), None);
1004
1005 let persistent_before = &envelope;
1006 let encrypted_pin_before = encrypted_pin.to_string();
1007
1008 let system = PinLockSystem::with_client(&client);
1009 system
1010 .migrate_pin_envelope_if_needed()
1011 .await
1012 .expect("migration succeeds");
1013
1014 let persistent_after = &bridge
1015 .get_persistent_pin_envelope()
1016 .await
1017 .expect("persistent envelope still present");
1018 let encrypted_pin_after = bridge
1019 .get_encrypted_pin()
1020 .await
1021 .expect("encrypted pin still present")
1022 .to_string();
1023 assert_pin_envelopes_equal(persistent_before, persistent_after);
1024 assert_eq!(encrypted_pin_before, encrypted_pin_after);
1025 assert!(bridge.get_ephemeral_pin_envelope().await.is_none());
1026 }
1027
1028 #[tokio::test]
1029 async fn migrate_v2_envelope_not_matching_user_key_returns_v2_key_rotation_unsupported() {
1030 let client = client_with_user_key();
1031 let system = PinLockSystem::with_client(&client);
1032 system
1033 .set_pin("1234".into(), PinLockType::BeforeFirstUnlock)
1034 .await
1035 .expect("set_pin succeeds");
1036
1037 let mismatched_envelope = {
1039 let mut ctx = client.internal.get_key_store().context_mut();
1040 let other_v2 = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
1041 PasswordProtectedKeyEnvelope::seal(
1042 other_v2,
1043 "1234",
1044 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
1045 &ctx,
1046 )
1047 .expect("seal under other v2 key")
1048 };
1049 client
1050 .km_state_bridge()
1051 .set_persistent_pin_envelope(&mismatched_envelope)
1052 .await;
1053
1054 assert!(matches!(
1055 system.migrate_pin_envelope_if_needed().await,
1056 Err(MigrationFailed::V2KeyRotationUnsupported),
1057 ));
1058 }
1059
1060 #[tokio::test]
1061 async fn migrate_v2_envelope_with_v1_user_key_returns_v2_envelope_with_v1_user_key() {
1062 let client = client_with_v1_user_key();
1063
1064 let v2_envelope = {
1066 let key_store = client.internal.get_key_store();
1067 let mut ctx = key_store.context_mut();
1068 let v2_local = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
1069 PasswordProtectedKeyEnvelope::seal(
1070 v2_local,
1071 "1234",
1072 PasswordProtectedKeyEnvelopeNamespace::PinUnlock,
1073 &ctx,
1074 )
1075 .expect("seal under v2 key")
1076 };
1077 assert!(
1078 v2_envelope.contained_key_id().expect("readable").is_some(),
1079 "envelope should be V2",
1080 );
1081
1082 let bridge = client.km_state_bridge();
1083 bridge.set_persistent_pin_envelope(&v2_envelope).await;
1084
1085 let system = PinLockSystem::with_client(&client);
1086 assert!(matches!(
1087 system.migrate_pin_envelope_if_needed().await,
1088 Err(MigrationFailed::V2EnvelopeWithV1UserKey),
1089 ));
1090 }
1091
1092 #[tokio::test]
1093 async fn migrate_v1_to_v2_without_upgrade_token_returns_missing_v2_upgrade_token() {
1094 let pin = "1234";
1095 let (client, state) = fresh_v1_state_with_v2_user_key(pin);
1096 let bridge = client.km_state_bridge();
1097 bridge.set_persistent_pin_envelope(&state.envelope).await;
1098 bridge.set_encrypted_pin(&state.encrypted_pin).await;
1099 let system = PinLockSystem::with_client(&client);
1102 assert!(matches!(
1103 system.migrate_pin_envelope_if_needed().await,
1104 Err(MigrationFailed::MissingV2UpgradeToken),
1105 ));
1106 }
1107
1108 #[tokio::test]
1109 async fn migrate_v1_to_v2_without_encrypted_pin_returns_missing_encrypted_pin() {
1110 let pin = "1234";
1111 let (client, state) = fresh_v1_state_with_v2_user_key(pin);
1112 let bridge = client.km_state_bridge();
1113 bridge.set_persistent_pin_envelope(&state.envelope).await;
1114 bridge.set_v2_upgrade_token(&state.token).await;
1115 let system = PinLockSystem::with_client(&client);
1118 assert!(matches!(
1119 system.migrate_pin_envelope_if_needed().await,
1120 Err(MigrationFailed::MissingEncryptedPin),
1121 ));
1122 }
1123
1124 #[tokio::test]
1125 async fn migrate_v1_to_v2_with_mismatched_upgrade_token_returns_pin_decryption_failure() {
1126 let pin = "1234";
1127 let (client, state) = fresh_v1_state_with_v2_user_key(pin);
1128
1129 let unrelated_token = {
1133 let key_store = bitwarden_crypto::KeyStore::<KeySlotIds>::default();
1134 let mut ctx = key_store.context_mut();
1135 let v1 = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
1136 let v2 = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
1137 V2UpgradeToken::create(v1, v2, &ctx).expect("unrelated token created")
1138 };
1139
1140 let bridge = client.km_state_bridge();
1141 bridge.set_persistent_pin_envelope(&state.envelope).await;
1142 bridge.set_encrypted_pin(&state.encrypted_pin).await;
1143 bridge.set_v2_upgrade_token(&unrelated_token).await;
1144
1145 let system = PinLockSystem::with_client(&client);
1146 assert!(matches!(
1147 system.migrate_pin_envelope_if_needed().await,
1148 Err(MigrationFailed::PinDecryption),
1149 ));
1150 }
1151
1152 #[tokio::test]
1153 async fn on_unlock_unenrolls_when_migration_fails() {
1154 let pin = "1234";
1156 let (client, state) = fresh_v1_state_with_v2_user_key(pin);
1157 let bridge = client.km_state_bridge();
1158 bridge.set_persistent_pin_envelope(&state.envelope).await;
1159 bridge.set_encrypted_pin(&state.encrypted_pin).await;
1160 let system = PinLockSystem::with_client(&client);
1163 system.on_unlock().await;
1164
1165 assert_pin_fully_unenrolled(&client).await;
1166 }
1167}