Skip to main content

bitwarden_user_crypto_management/key_rotation/
rotation_context.rs

1use bitwarden_core::key_management::{KeySlotIds, SymmetricKeySlotId};
2use bitwarden_crypto::{KeyStore, KeyStoreContext, PublicKey, SymmetricKeyAlgorithm};
3use tracing::{debug, info, warn};
4
5use super::{
6    RotateUserKeysError,
7    rotate_user_keys::UpgradeTokenAction,
8    sync::SyncedAccountData,
9    unlock::{V1EmergencyAccessMembership, V1OrganizationMembership},
10};
11
12#[derive(Debug)]
13struct UntrustedKeyError;
14
15/// Whether the rotation creates a V2 upgrade token.
16///
17/// A token only makes sense when the user moves from a V1 to a V2 user key. Every other rotation
18/// logs the user out of their other sessions. The SDK always rotates into a V2 user key, so the
19/// current user key decides the answer.
20fn creates_v2_upgrade_token(
21    current_user_key_id: SymmetricKeySlotId,
22    upgrade_token_action: UpgradeTokenAction,
23    ctx: &KeyStoreContext<KeySlotIds>,
24) -> bool {
25    if matches!(upgrade_token_action, UpgradeTokenAction::Skip) {
26        debug!("UpgradeTokenAction::Skip, no upgrade token is created for this rotation");
27        return false;
28    }
29
30    ctx.is_v1_symmetric_key(current_user_key_id)
31        .unwrap_or(false)
32}
33
34/// The organization memberships the user has to confirm as trusted before the rotation.
35///
36/// When the rotation creates a V2 upgrade token, returns no memberships. Organization admins update
37/// account recovery from that token, so the user confirms nothing. Every other rotation returns the
38/// memberships it was given.
39pub(super) fn organization_memberships_needing_trust(
40    organization_memberships: Vec<V1OrganizationMembership>,
41    upgrade_token_action: UpgradeTokenAction,
42    key_store: &KeyStore<KeySlotIds>,
43) -> Vec<V1OrganizationMembership> {
44    let creates_v2_upgrade_token = {
45        let ctx = key_store.context();
46        creates_v2_upgrade_token(SymmetricKeySlotId::User, upgrade_token_action, &ctx)
47    };
48
49    if creates_v2_upgrade_token {
50        info!(
51            "Rotation creates a V2 upgrade token, none of the {} organization public key(s) need confirmation",
52            organization_memberships.len()
53        );
54        return vec![];
55    }
56
57    organization_memberships
58}
59
60fn filter_trusted_organization(
61    org: &[V1OrganizationMembership],
62    trusted_orgs: &[PublicKey],
63) -> Result<Vec<V1OrganizationMembership>, UntrustedKeyError> {
64    org.iter()
65        .map(|o| {
66            let is_trusted = trusted_orgs.iter().any(|tk| tk == &o.public_key);
67            if !is_trusted {
68                warn!(
69                    "Aborting because untrusted organization detected with id={}",
70                    o.organization_id
71                );
72                Err(UntrustedKeyError)
73            } else {
74                Ok(o.clone())
75            }
76        })
77        .collect::<Result<Vec<V1OrganizationMembership>, UntrustedKeyError>>()
78}
79
80fn filter_trusted_emergency_access(
81    ea: &[V1EmergencyAccessMembership],
82    trusted_emergency_access_user_public_keys: &[PublicKey],
83) -> Result<Vec<V1EmergencyAccessMembership>, UntrustedKeyError> {
84    ea.iter()
85        .map(|e| {
86            let is_trusted = trusted_emergency_access_user_public_keys
87                .iter()
88                .any(|tk| tk == &e.public_key);
89            if !is_trusted {
90                warn!(
91                    "Aborting because untrusted emergency access membership detected with id={}",
92                    e.id
93                );
94                Err(UntrustedKeyError)
95            } else {
96                Ok(e.to_owned())
97            }
98        })
99        .collect::<Result<Vec<V1EmergencyAccessMembership>, UntrustedKeyError>>()
100}
101
102pub(super) struct RotationContext {
103    pub(super) v1_organization_memberships: Vec<V1OrganizationMembership>,
104    pub(super) v1_emergency_access_memberships: Vec<V1EmergencyAccessMembership>,
105    pub(super) current_user_key_id: SymmetricKeySlotId,
106    pub(super) new_user_key_id: SymmetricKeySlotId,
107    pub(super) creates_v2_upgrade_token: bool,
108}
109
110/// Generates the new user key and collects the memberships that get a copy of it.
111///
112/// Returns [`RotateUserKeysError::UntrustedKey`] if the rotation needs a public key the user has
113/// not confirmed. A public key does not show who owns it, so the user has to confirm it first.
114pub(super) fn make_rotation_context(
115    sync: &SyncedAccountData,
116    trusted_organization_public_keys: &[PublicKey],
117    trusted_emergency_access_public_keys: &[PublicKey],
118    upgrade_token_action: UpgradeTokenAction,
119    ctx: &mut KeyStoreContext<KeySlotIds>,
120) -> Result<RotationContext, RotateUserKeysError> {
121    info!(
122        "Existing user cryptographic version {:?}",
123        sync.wrapped_account_cryptographic_state
124    );
125    let current_user_key_id = SymmetricKeySlotId::User;
126
127    debug!("Generating new XAES-256-GCM user key for key rotation");
128    let new_user_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
129
130    let creates_v2_upgrade_token =
131        creates_v2_upgrade_token(current_user_key_id, upgrade_token_action, ctx);
132
133    let v1_organization_memberships = if creates_v2_upgrade_token {
134        // Organization admins update account recovery from the token. The user is never asked to
135        // trust these keys.
136        info!(
137            "Skipping the trust check for {} organization public key(s), the rotation does not use them",
138            sync.organization_memberships.len()
139        );
140        sync.organization_memberships.clone()
141    } else {
142        filter_trusted_organization(
143            sync.organization_memberships.as_slice(),
144            trusted_organization_public_keys,
145        )
146        .map_err(|_| RotateUserKeysError::UntrustedKey)?
147    };
148
149    let v1_emergency_access_memberships = filter_trusted_emergency_access(
150        sync.emergency_access_memberships.as_slice(),
151        trusted_emergency_access_public_keys,
152    )
153    .map_err(|_| RotateUserKeysError::UntrustedKey)?;
154
155    Ok(RotationContext {
156        v1_organization_memberships,
157        v1_emergency_access_memberships,
158        current_user_key_id,
159        new_user_key_id,
160        creates_v2_upgrade_token,
161    })
162}
163
164#[cfg(test)]
165mod tests {
166    use bitwarden_core::key_management::{
167        KeySlotIds, PrivateKeySlotId, SymmetricKeySlotId,
168        account_cryptographic_state::WrappedAccountCryptographicState,
169    };
170    use bitwarden_crypto::{
171        KeyStore, KeyStoreContext, PublicKeyEncryptionAlgorithm, SymmetricKeyAlgorithm,
172    };
173    use uuid::Uuid;
174
175    use super::{
176        super::{
177            sync::SyncedAccountData,
178            unlock::{V1EmergencyAccessMembership, V1OrganizationMembership},
179        },
180        RotateUserKeysError, UpgradeTokenAction, creates_v2_upgrade_token,
181        filter_trusted_emergency_access, filter_trusted_organization, make_rotation_context,
182        organization_memberships_needing_trust,
183    };
184
185    #[test]
186    fn test_creates_v2_upgrade_token_v1_user_key_with_create_if_needed() {
187        let store: KeyStore<KeySlotIds> = KeyStore::default();
188        let mut ctx = store.context_mut();
189
190        let current_user_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
191
192        assert!(creates_v2_upgrade_token(
193            current_user_key_id,
194            UpgradeTokenAction::CreateIfNeeded,
195            &ctx,
196        ));
197    }
198
199    #[test]
200    fn test_creates_v2_upgrade_token_v1_user_key_with_skip() {
201        let store: KeyStore<KeySlotIds> = KeyStore::default();
202        let mut ctx = store.context_mut();
203
204        let current_user_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
205
206        assert!(!creates_v2_upgrade_token(
207            current_user_key_id,
208            UpgradeTokenAction::Skip,
209            &ctx,
210        ));
211    }
212
213    #[test]
214    fn test_creates_v2_upgrade_token_v2_user_key_with_create_if_needed() {
215        let store: KeyStore<KeySlotIds> = KeyStore::default();
216        let mut ctx = store.context_mut();
217
218        let current_user_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
219
220        assert!(!creates_v2_upgrade_token(
221            current_user_key_id,
222            UpgradeTokenAction::CreateIfNeeded,
223            &ctx,
224        ));
225    }
226
227    #[test]
228    fn test_creates_v2_upgrade_token_missing_current_user_key() {
229        let store: KeyStore<KeySlotIds> = KeyStore::default();
230        let ctx = store.context();
231
232        assert!(
233            !creates_v2_upgrade_token(
234                SymmetricKeySlotId::User,
235                UpgradeTokenAction::CreateIfNeeded,
236                &ctx,
237            ),
238            "a user key that cannot be read must not be treated as V1"
239        );
240    }
241
242    fn make_org_membership(
243        ctx: &mut KeyStoreContext<KeySlotIds>,
244    ) -> (V1OrganizationMembership, PrivateKeySlotId) {
245        let org_private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
246        (
247            V1OrganizationMembership {
248                organization_id: Uuid::new_v4(),
249                name: "Test Org".to_string(),
250                public_key: ctx.get_public_key(org_private_key).expect("key exists"),
251            },
252            org_private_key,
253        )
254    }
255
256    fn make_ea_membership(
257        ctx: &mut KeyStoreContext<KeySlotIds>,
258    ) -> (V1EmergencyAccessMembership, PrivateKeySlotId) {
259        let private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
260        (
261            V1EmergencyAccessMembership {
262                id: Uuid::new_v4(),
263                name: "Test User".to_string(),
264                public_key: ctx.get_public_key(private_key).expect("key exists"),
265                grantee_id: Uuid::new_v4(),
266            },
267            private_key,
268        )
269    }
270
271    fn assert_org_membership_eq(
272        actual: &V1OrganizationMembership,
273        expected: &V1OrganizationMembership,
274    ) {
275        assert_eq!(actual.organization_id, expected.organization_id);
276        assert_eq!(actual.name, expected.name);
277        assert_eq!(actual.public_key, expected.public_key);
278    }
279
280    fn assert_ea_membership_eq(
281        actual: &V1EmergencyAccessMembership,
282        expected: &V1EmergencyAccessMembership,
283    ) {
284        assert_eq!(actual.id, expected.id);
285        assert_eq!(actual.name, expected.name);
286        assert_eq!(actual.grantee_id, expected.grantee_id);
287        assert_eq!(actual.public_key, expected.public_key);
288    }
289
290    fn make_test_sync(
291        org_memberships: Vec<V1OrganizationMembership>,
292        ea_memberships: Vec<V1EmergencyAccessMembership>,
293        ctx: &mut KeyStoreContext<KeySlotIds>,
294    ) -> SyncedAccountData {
295        let user_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
296        let private_key = ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
297        let wrapped_private_key = ctx.wrap_private_key(user_key, private_key).unwrap();
298        SyncedAccountData {
299            wrapped_account_cryptographic_state: WrappedAccountCryptographicState::V1 {
300                private_key: wrapped_private_key,
301            },
302            folders: vec![],
303            ciphers: vec![],
304            sends: vec![],
305            emergency_access_memberships: ea_memberships,
306            organization_memberships: org_memberships,
307            trusted_devices: vec![],
308            passkeys: vec![],
309            kdf_and_salt: None,
310        }
311    }
312
313    #[test]
314    fn test_filter_trusted_org_empty_list() {
315        let store: KeyStore<KeySlotIds> = KeyStore::default();
316        let mut ctx = store.context_mut();
317        let (org, _) = make_org_membership(&mut ctx);
318        let trusted = [org.public_key.clone()];
319
320        // Note this is important to allow for the case where a user has no org memberships, but has
321        // provided a non-empty list of trusted org public keys. For example their
322        // organization membership was removed in the middle of the key rotation process.
323        let result = filter_trusted_organization(&[], &trusted);
324
325        assert!(matches!(result, Ok(ref v) if v.is_empty()));
326    }
327
328    #[test]
329    fn test_filter_trusted_org_all_trusted() {
330        let store: KeyStore<KeySlotIds> = KeyStore::default();
331        let mut ctx = store.context_mut();
332        let (org1, _) = make_org_membership(&mut ctx);
333        let (org2, _) = make_org_membership(&mut ctx);
334        let trusted = [org1.public_key.clone(), org2.public_key.clone()];
335        let expected_org1 = org1.clone();
336        let expected_org2 = org2.clone();
337
338        let result = filter_trusted_organization(&[org1, org2], &trusted);
339
340        let memberships = result.unwrap();
341        assert_eq!(memberships.len(), 2);
342        assert_org_membership_eq(&memberships[0], &expected_org1);
343        assert_org_membership_eq(&memberships[1], &expected_org2);
344    }
345
346    #[test]
347    fn test_filter_trusted_org_one_untrusted() {
348        let store: KeyStore<KeySlotIds> = KeyStore::default();
349        let mut ctx = store.context_mut();
350        let (org1, _) = make_org_membership(&mut ctx);
351        let (org2, _) = make_org_membership(&mut ctx);
352        let trusted = [org1.public_key.clone()];
353
354        let result = filter_trusted_organization(&[org1, org2], &trusted);
355
356        assert!(result.is_err());
357    }
358
359    #[test]
360    fn test_filter_trusted_org_empty_trusted_with_orgs() {
361        let store: KeyStore<KeySlotIds> = KeyStore::default();
362        let mut ctx = store.context_mut();
363        let (org, _) = make_org_membership(&mut ctx);
364
365        let result = filter_trusted_organization(&[org], &[]);
366
367        assert!(result.is_err());
368    }
369
370    #[test]
371    fn test_filter_trusted_ea_empty_list() {
372        let store: KeyStore<KeySlotIds> = KeyStore::default();
373        let mut ctx = store.context_mut();
374        let (ea, _) = make_ea_membership(&mut ctx);
375        let trusted = [ea.public_key.clone()];
376
377        let result = filter_trusted_emergency_access(&[], &trusted);
378
379        assert!(matches!(result, Ok(ref v) if v.is_empty()));
380    }
381
382    #[test]
383    fn test_filter_trusted_ea_all_trusted() {
384        let store: KeyStore<KeySlotIds> = KeyStore::default();
385        let mut ctx = store.context_mut();
386        let (ea1, _) = make_ea_membership(&mut ctx);
387        let (ea2, _) = make_ea_membership(&mut ctx);
388        let trusted = [ea1.public_key.clone(), ea2.public_key.clone()];
389        let expected_ea1 = ea1.clone();
390        let expected_ea2 = ea2.clone();
391
392        let result = filter_trusted_emergency_access(&[ea1, ea2], &trusted);
393
394        let memberships = result.expect("should succeed");
395        assert_eq!(memberships.len(), 2);
396        assert_ea_membership_eq(&memberships[0], &expected_ea1);
397        assert_ea_membership_eq(&memberships[1], &expected_ea2);
398    }
399
400    #[test]
401    fn test_filter_trusted_ea_one_untrusted() {
402        let store: KeyStore<KeySlotIds> = KeyStore::default();
403        let mut ctx = store.context_mut();
404        let (ea1, _) = make_ea_membership(&mut ctx);
405        let (ea2, _) = make_ea_membership(&mut ctx);
406        // only ea1 is trusted
407        let trusted = [ea1.public_key.clone()];
408
409        let result = filter_trusted_emergency_access(&[ea1, ea2], &trusted);
410
411        assert!(result.is_err());
412    }
413
414    #[test]
415    fn test_filter_trusted_ea_empty_trusted_with_memberships() {
416        let store: KeyStore<KeySlotIds> = KeyStore::default();
417        let mut ctx = store.context_mut();
418        let (ea, _) = make_ea_membership(&mut ctx);
419
420        let result = filter_trusted_emergency_access(&[ea], &[]);
421
422        assert!(result.is_err());
423    }
424
425    #[test]
426    fn test_make_rotation_context_empty_data() {
427        let store: KeyStore<KeySlotIds> = KeyStore::default();
428        let mut ctx = store.context_mut();
429        let sync = make_test_sync(vec![], vec![], &mut ctx);
430
431        let result = make_rotation_context(&sync, &[], &[], UpgradeTokenAction::Skip, &mut ctx);
432
433        let rotation_ctx = result.expect("should succeed");
434        assert!(rotation_ctx.v1_organization_memberships.is_empty());
435        assert!(rotation_ctx.v1_emergency_access_memberships.is_empty());
436        assert_eq!(rotation_ctx.current_user_key_id, SymmetricKeySlotId::User);
437        assert_ne!(
438            rotation_ctx.new_user_key_id,
439            rotation_ctx.current_user_key_id
440        );
441        assert!(!rotation_ctx.creates_v2_upgrade_token);
442    }
443
444    #[test]
445    fn test_make_rotation_context_untrusted_org_with_upgrade_token_is_accepted() {
446        let store: KeyStore<KeySlotIds> = KeyStore::default();
447        let mut ctx = store.context_mut();
448        let user_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
449        ctx.persist_symmetric_key(user_key, SymmetricKeySlotId::User)
450            .expect("persisting the user key should work");
451        let (org, _) = make_org_membership(&mut ctx);
452        let expected_org = org.clone();
453        let sync = make_test_sync(vec![org], vec![], &mut ctx);
454
455        let result = make_rotation_context(
456            &sync,
457            &[],
458            &[],
459            UpgradeTokenAction::CreateIfNeeded,
460            &mut ctx,
461        );
462
463        let rotation_ctx = result.expect("should succeed");
464        assert!(rotation_ctx.creates_v2_upgrade_token);
465        assert_eq!(rotation_ctx.v1_organization_memberships.len(), 1);
466        assert_org_membership_eq(&rotation_ctx.v1_organization_memberships[0], &expected_org);
467    }
468
469    #[test]
470    fn test_make_rotation_context_untrusted_org_v2_user_returns_error() {
471        let store: KeyStore<KeySlotIds> = KeyStore::default();
472        let mut ctx = store.context_mut();
473        let user_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XAes256Gcm);
474        ctx.persist_symmetric_key(user_key, SymmetricKeySlotId::User)
475            .expect("persisting the user key should work");
476        let (org, _) = make_org_membership(&mut ctx);
477        let sync = make_test_sync(vec![org], vec![], &mut ctx);
478
479        let result = make_rotation_context(
480            &sync,
481            &[],
482            &[],
483            UpgradeTokenAction::CreateIfNeeded,
484            &mut ctx,
485        );
486
487        assert!(matches!(result, Err(RotateUserKeysError::UntrustedKey)));
488    }
489
490    #[test]
491    fn test_make_rotation_context_untrusted_emergency_access_with_upgrade_token_returns_error() {
492        let store: KeyStore<KeySlotIds> = KeyStore::default();
493        let mut ctx = store.context_mut();
494        let user_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
495        ctx.persist_symmetric_key(user_key, SymmetricKeySlotId::User)
496            .expect("persisting the user key should work");
497        let (ea, _) = make_ea_membership(&mut ctx);
498        let sync = make_test_sync(vec![], vec![ea], &mut ctx);
499
500        let result = make_rotation_context(
501            &sync,
502            &[],
503            &[],
504            UpgradeTokenAction::CreateIfNeeded,
505            &mut ctx,
506        );
507
508        assert!(matches!(result, Err(RotateUserKeysError::UntrustedKey)));
509    }
510
511    #[test]
512    fn test_make_rotation_context_trusted_org_and_ea() {
513        let store: KeyStore<KeySlotIds> = KeyStore::default();
514        let mut ctx = store.context_mut();
515        let (org, _) = make_org_membership(&mut ctx);
516        let (ea, _) = make_ea_membership(&mut ctx);
517        let trusted_orgs = [org.public_key.clone()];
518        let trusted_eas = [ea.public_key.clone()];
519        let expected_org = org.clone();
520        let expected_ea = ea.clone();
521        let sync = make_test_sync(vec![org], vec![ea], &mut ctx);
522
523        let result = make_rotation_context(
524            &sync,
525            &trusted_orgs,
526            &trusted_eas,
527            UpgradeTokenAction::Skip,
528            &mut ctx,
529        );
530
531        let rotation_ctx = result.expect("should succeed");
532        assert_eq!(rotation_ctx.v1_organization_memberships.len(), 1);
533        assert_org_membership_eq(&rotation_ctx.v1_organization_memberships[0], &expected_org);
534        assert_eq!(rotation_ctx.v1_emergency_access_memberships.len(), 1);
535        assert_ea_membership_eq(
536            &rotation_ctx.v1_emergency_access_memberships[0],
537            &expected_ea,
538        );
539        assert_eq!(rotation_ctx.current_user_key_id, SymmetricKeySlotId::User);
540        assert_ne!(
541            rotation_ctx.new_user_key_id,
542            rotation_ctx.current_user_key_id
543        );
544    }
545
546    #[test]
547    fn test_make_rotation_context_untrusted_org_returns_error() {
548        let store: KeyStore<KeySlotIds> = KeyStore::default();
549        let mut ctx = store.context_mut();
550        let (org, _) = make_org_membership(&mut ctx);
551        let sync = make_test_sync(vec![org], vec![], &mut ctx);
552
553        let result = make_rotation_context(&sync, &[], &[], UpgradeTokenAction::Skip, &mut ctx);
554
555        assert!(matches!(result, Err(RotateUserKeysError::UntrustedKey)));
556    }
557
558    #[test]
559    fn test_make_rotation_context_untrusted_ea_returns_error() {
560        let store: KeyStore<KeySlotIds> = KeyStore::default();
561        let mut ctx = store.context_mut();
562        let (ea, _) = make_ea_membership(&mut ctx);
563        let sync = make_test_sync(vec![], vec![ea], &mut ctx);
564
565        let result = make_rotation_context(&sync, &[], &[], UpgradeTokenAction::Skip, &mut ctx);
566
567        assert!(matches!(result, Err(RotateUserKeysError::UntrustedKey)));
568    }
569
570    /// Persists a user key of the given algorithm and returns one organization membership.
571    fn make_store_with_user_key(
572        algorithm: SymmetricKeyAlgorithm,
573    ) -> (KeyStore<KeySlotIds>, V1OrganizationMembership) {
574        let store: KeyStore<KeySlotIds> = KeyStore::default();
575        let org = {
576            let mut ctx = store.context_mut();
577            let user_key = ctx.make_symmetric_key(algorithm);
578            ctx.persist_symmetric_key(user_key, SymmetricKeySlotId::User)
579                .expect("persisting the user key should work");
580            make_org_membership(&mut ctx).0
581        };
582
583        (store, org)
584    }
585
586    #[test]
587    fn test_organization_memberships_needing_trust_v1_user_key_with_create_if_needed_is_empty() {
588        let (store, org) = make_store_with_user_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
589
590        let memberships = organization_memberships_needing_trust(
591            vec![org],
592            UpgradeTokenAction::CreateIfNeeded,
593            &store,
594        );
595
596        assert!(memberships.is_empty());
597    }
598
599    #[test]
600    fn test_organization_memberships_needing_trust_v1_user_key_with_skip() {
601        let (store, org) = make_store_with_user_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
602        let expected_org = org.clone();
603
604        let memberships =
605            organization_memberships_needing_trust(vec![org], UpgradeTokenAction::Skip, &store);
606
607        assert_eq!(memberships.len(), 1);
608        assert_org_membership_eq(&memberships[0], &expected_org);
609    }
610
611    #[test]
612    fn test_organization_memberships_needing_trust_v2_user_key_with_create_if_needed() {
613        let (store, org) = make_store_with_user_key(SymmetricKeyAlgorithm::XAes256Gcm);
614        let expected_org = org.clone();
615
616        let memberships = organization_memberships_needing_trust(
617            vec![org],
618            UpgradeTokenAction::CreateIfNeeded,
619            &store,
620        );
621
622        assert_eq!(memberships.len(), 1);
623        assert_org_membership_eq(&memberships[0], &expected_org);
624    }
625}