matrix_sdk_crypto/store/
traits.rs

1// Copyright 2023 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{collections::HashMap, fmt, sync::Arc};
16
17use async_trait::async_trait;
18use matrix_sdk_common::AsyncTraitDeps;
19use ruma::{
20    events::secret::request::SecretName, DeviceId, OwnedDeviceId, RoomId, TransactionId, UserId,
21};
22use vodozemac::Curve25519PublicKey;
23
24use super::{
25    types::{
26        BackupKeys, Changes, DehydratedDeviceKey, PendingChanges, RoomKeyCounts, RoomSettings,
27        StoredRoomKeyBundleData, TrackedUser,
28    },
29    CryptoStoreError, Result,
30};
31#[cfg(doc)]
32use crate::olm::SenderData;
33use crate::{
34    olm::{
35        InboundGroupSession, OlmMessageHash, OutboundGroupSession, PrivateCrossSigningIdentity,
36        SenderDataType, Session,
37    },
38    types::events::room_key_withheld::RoomKeyWithheldEvent,
39    Account, DeviceData, GossipRequest, GossippedSecret, SecretInfo, UserIdentityData,
40};
41
42/// Represents a store that the `OlmMachine` uses to store E2EE data (such as
43/// cryptographic keys).
44#[cfg_attr(target_family = "wasm", async_trait(?Send))]
45#[cfg_attr(not(target_family = "wasm"), async_trait)]
46pub trait CryptoStore: AsyncTraitDeps {
47    /// The error type used by this crypto store.
48    type Error: fmt::Debug + Into<CryptoStoreError>;
49
50    /// Load an account that was previously stored.
51    async fn load_account(&self) -> Result<Option<Account>, Self::Error>;
52
53    /// Try to load a private cross signing identity, if one is stored.
54    async fn load_identity(&self) -> Result<Option<PrivateCrossSigningIdentity>, Self::Error>;
55
56    /// Save the set of changes to the store.
57    ///
58    /// # Arguments
59    ///
60    /// * `changes` - The set of changes that should be stored.
61    async fn save_changes(&self, changes: Changes) -> Result<(), Self::Error>;
62
63    /// Save the set of changes to the store.
64    ///
65    /// This is an updated version of `save_changes` that will replace it as
66    /// #2624 makes progress.
67    ///
68    /// # Arguments
69    ///
70    /// * `changes` - The set of changes that should be stored.
71    async fn save_pending_changes(&self, changes: PendingChanges) -> Result<(), Self::Error>;
72
73    /// Save a list of inbound group sessions to the store.
74    ///
75    /// # Arguments
76    ///
77    /// * `sessions` - The sessions to be saved.
78    /// * `backed_up_to_version` - If the keys should be marked as having been
79    ///   backed up, the version of the backup.
80    ///
81    /// Note: some implementations ignore `backup_version` and assume the
82    /// current backup version, which is normally the same.
83    async fn save_inbound_group_sessions(
84        &self,
85        sessions: Vec<InboundGroupSession>,
86        backed_up_to_version: Option<&str>,
87    ) -> Result<(), Self::Error>;
88
89    /// Get all the sessions that belong to the given sender key.
90    ///
91    /// # Arguments
92    ///
93    /// * `sender_key` - The sender key that was used to establish the sessions.
94    async fn get_sessions(&self, sender_key: &str) -> Result<Option<Vec<Session>>, Self::Error>;
95
96    /// Get the inbound group session from our store.
97    ///
98    /// # Arguments
99    /// * `room_id` - The room id of the room that the session belongs to.
100    ///
101    /// * `sender_key` - The sender key that sent us the session.
102    ///
103    /// * `session_id` - The unique id of the session.
104    async fn get_inbound_group_session(
105        &self,
106        room_id: &RoomId,
107        session_id: &str,
108    ) -> Result<Option<InboundGroupSession>, Self::Error>;
109
110    /// Get withheld info for this key.
111    /// Allows to know if the session was not sent on purpose.
112    /// This only returns withheld info sent by the owner of the group session,
113    /// not the one you can get from a response to a key request from
114    /// another of your device.
115    async fn get_withheld_info(
116        &self,
117        room_id: &RoomId,
118        session_id: &str,
119    ) -> Result<Option<RoomKeyWithheldEvent>, Self::Error>;
120
121    /// Get all the inbound group sessions we have stored.
122    async fn get_inbound_group_sessions(&self) -> Result<Vec<InboundGroupSession>, Self::Error>;
123
124    /// Get the number inbound group sessions we have and how many of them are
125    /// backed up.
126    async fn inbound_group_session_counts(
127        &self,
128        backup_version: Option<&str>,
129    ) -> Result<RoomKeyCounts, Self::Error>;
130
131    /// Get all the inbound group sessions for a given room.
132    ///
133    /// # Arguments
134    /// * `room_id` - The ID of the room to return sessions for.
135    async fn get_inbound_group_sessions_by_room_id(
136        &self,
137        room_id: &RoomId,
138    ) -> Result<Vec<InboundGroupSession>, Self::Error>;
139
140    /// Get a batch of inbound group sessions for the device with the supplied
141    /// curve key, whose sender data is of the supplied type.
142    ///
143    /// Sessions are not necessarily returned in any specific order, but the
144    /// returned batches are consistent: if this function is called repeatedly
145    /// with `after_session_id` set to the session ID from the last result
146    /// from the previous call, until an empty result is returned, then
147    /// eventually all matching sessions are returned. (New sessions that are
148    /// added in the course of iteration may or may not be returned.)
149    ///
150    /// This function is used when the device information is updated via a
151    /// `/keys/query` response and we want to update the sender data based
152    /// on the new information.
153    ///
154    /// # Arguments
155    ///
156    /// * `curve_key` - only return sessions created by the device with this
157    ///   curve key.
158    ///
159    /// * `sender_data_type` - only return sessions whose [`SenderData`] record
160    ///   is in this state.
161    ///
162    /// * `after_session_id` - return the sessions after this id, or start at
163    ///   the earliest if this is None.
164    ///
165    /// * `limit` - return a maximum of this many sessions.
166    async fn get_inbound_group_sessions_for_device_batch(
167        &self,
168        curve_key: Curve25519PublicKey,
169        sender_data_type: SenderDataType,
170        after_session_id: Option<String>,
171        limit: usize,
172    ) -> Result<Vec<InboundGroupSession>, Self::Error>;
173
174    /// Return a batch of ['InboundGroupSession'] ("room keys") that have not
175    /// yet been backed up in the supplied backup version.
176    ///
177    /// The size of the returned `Vec` is <= `limit`.
178    ///
179    /// Note: some implementations ignore `backup_version` and assume the
180    /// current backup version, which is normally the same.
181    async fn inbound_group_sessions_for_backup(
182        &self,
183        backup_version: &str,
184        limit: usize,
185    ) -> Result<Vec<InboundGroupSession>, Self::Error>;
186
187    /// Store the fact that the supplied sessions were backed up into the backup
188    /// with version `backup_version`.
189    ///
190    /// Note: some implementations ignore `backup_version` and assume the
191    /// current backup version, which is normally the same.
192    async fn mark_inbound_group_sessions_as_backed_up(
193        &self,
194        backup_version: &str,
195        room_and_session_ids: &[(&RoomId, &str)],
196    ) -> Result<(), Self::Error>;
197
198    /// Reset the backup state of all the stored inbound group sessions.
199    ///
200    /// Note: this is mostly implemented by stores that ignore the
201    /// `backup_version` argument on `inbound_group_sessions_for_backup` and
202    /// `mark_inbound_group_sessions_as_backed_up`. Implementations that
203    /// pay attention to the supplied backup version probably don't need to
204    /// update their storage when the current backup version changes, so have
205    /// empty implementations of this method.
206    async fn reset_backup_state(&self) -> Result<(), Self::Error>;
207
208    /// Get the backup keys we have stored.
209    async fn load_backup_keys(&self) -> Result<BackupKeys, Self::Error>;
210
211    /// Get the dehydrated device pickle key we have stored.
212    async fn load_dehydrated_device_pickle_key(
213        &self,
214    ) -> Result<Option<DehydratedDeviceKey>, Self::Error>;
215
216    /// Deletes the previously stored dehydrated device pickle key.
217    async fn delete_dehydrated_device_pickle_key(&self) -> Result<(), Self::Error>;
218
219    /// Get the outbound group session we have stored that is used for the
220    /// given room.
221    async fn get_outbound_group_session(
222        &self,
223        room_id: &RoomId,
224    ) -> Result<Option<OutboundGroupSession>, Self::Error>;
225
226    /// Provide the list of users whose devices we are keeping track of, and
227    /// whether they are considered dirty/outdated.
228    async fn load_tracked_users(&self) -> Result<Vec<TrackedUser>, Self::Error>;
229
230    /// Update the list of users whose devices we are keeping track of, and
231    /// whether they are considered dirty/outdated.
232    ///
233    /// Replaces any existing entry with a matching user ID.
234    async fn save_tracked_users(&self, users: &[(&UserId, bool)]) -> Result<(), Self::Error>;
235
236    /// Get the device for the given user with the given device ID.
237    ///
238    /// # Arguments
239    ///
240    /// * `user_id` - The user that the device belongs to.
241    ///
242    /// * `device_id` - The unique id of the device.
243    async fn get_device(
244        &self,
245        user_id: &UserId,
246        device_id: &DeviceId,
247    ) -> Result<Option<DeviceData>, Self::Error>;
248
249    /// Get all the devices of the given user.
250    ///
251    /// # Arguments
252    ///
253    /// * `user_id` - The user for which we should get all the devices.
254    async fn get_user_devices(
255        &self,
256        user_id: &UserId,
257    ) -> Result<HashMap<OwnedDeviceId, DeviceData>, Self::Error>;
258
259    /// Get the device for the current client.
260    ///
261    /// Since our own device is set when the store is created, this will always
262    /// return a device (unless there is an error).
263    async fn get_own_device(&self) -> Result<DeviceData, Self::Error>;
264
265    /// Get the user identity that is attached to the given user id.
266    ///
267    /// # Arguments
268    ///
269    /// * `user_id` - The user for which we should get the identity.
270    async fn get_user_identity(
271        &self,
272        user_id: &UserId,
273    ) -> Result<Option<UserIdentityData>, Self::Error>;
274
275    /// Check if a hash for an Olm message stored in the database.
276    async fn is_message_known(&self, message_hash: &OlmMessageHash) -> Result<bool, Self::Error>;
277
278    /// Get an outgoing secret request that we created that matches the given
279    /// request id.
280    ///
281    /// # Arguments
282    ///
283    /// * `request_id` - The unique request id that identifies this outgoing
284    /// secret request.
285    async fn get_outgoing_secret_requests(
286        &self,
287        request_id: &TransactionId,
288    ) -> Result<Option<GossipRequest>, Self::Error>;
289
290    /// Get an outgoing key request that we created that matches the given
291    /// requested key info.
292    ///
293    /// # Arguments
294    ///
295    /// * `key_info` - The key info of an outgoing secret request.
296    async fn get_secret_request_by_info(
297        &self,
298        secret_info: &SecretInfo,
299    ) -> Result<Option<GossipRequest>, Self::Error>;
300
301    /// Get all outgoing secret requests that we have in the store.
302    async fn get_unsent_secret_requests(&self) -> Result<Vec<GossipRequest>, Self::Error>;
303
304    /// Delete an outgoing key request that we created that matches the given
305    /// request id.
306    ///
307    /// # Arguments
308    ///
309    /// * `request_id` - The unique request id that identifies this outgoing key
310    /// request.
311    async fn delete_outgoing_secret_requests(
312        &self,
313        request_id: &TransactionId,
314    ) -> Result<(), Self::Error>;
315
316    /// Get all the secrets with the given [`SecretName`] we have currently
317    /// stored.
318    async fn get_secrets_from_inbox(
319        &self,
320        secret_name: &SecretName,
321    ) -> Result<Vec<GossippedSecret>, Self::Error>;
322
323    /// Delete all the secrets with the given [`SecretName`] we have currently
324    /// stored.
325    async fn delete_secrets_from_inbox(&self, secret_name: &SecretName) -> Result<(), Self::Error>;
326
327    /// Get the room settings, such as the encryption algorithm or whether to
328    /// encrypt only for trusted devices.
329    ///
330    /// # Arguments
331    ///
332    /// * `room_id` - The room id of the room
333    async fn get_room_settings(
334        &self,
335        room_id: &RoomId,
336    ) -> Result<Option<RoomSettings>, Self::Error>;
337
338    /// Get the details about the room key bundle data received from the given
339    /// user for the given room.
340    async fn get_received_room_key_bundle_data(
341        &self,
342        room_id: &RoomId,
343        user_id: &UserId,
344    ) -> Result<Option<StoredRoomKeyBundleData>, Self::Error>;
345
346    /// Get arbitrary data from the store
347    ///
348    /// # Arguments
349    ///
350    /// * `key` - The key to fetch data for
351    async fn get_custom_value(&self, key: &str) -> Result<Option<Vec<u8>>, Self::Error>;
352
353    /// Put arbitrary data into the store
354    ///
355    /// # Arguments
356    ///
357    /// * `key` - The key to insert data into
358    ///
359    /// * `value` - The value to insert
360    async fn set_custom_value(&self, key: &str, value: Vec<u8>) -> Result<(), Self::Error>;
361
362    /// Remove arbitrary data into the store
363    ///
364    /// # Arguments
365    ///
366    /// * `key` - The key to insert data into
367    async fn remove_custom_value(&self, key: &str) -> Result<(), Self::Error>;
368
369    /// Try to take a leased lock.
370    ///
371    /// This attempts to take a lock for the given lease duration.
372    ///
373    /// - If we already had the lease, this will extend the lease.
374    /// - If we didn't, but the previous lease has expired, we will acquire the
375    ///   lock.
376    /// - If there was no previous lease, we will acquire the lock.
377    /// - Otherwise, we don't get the lock.
378    ///
379    /// Returns whether taking the lock succeeded.
380    async fn try_take_leased_lock(
381        &self,
382        lease_duration_ms: u32,
383        key: &str,
384        holder: &str,
385    ) -> Result<bool, Self::Error>;
386
387    /// Load the next-batch token for a to-device query, if any.
388    async fn next_batch_token(&self) -> Result<Option<String>, Self::Error>;
389}
390
391#[repr(transparent)]
392struct EraseCryptoStoreError<T>(T);
393
394#[cfg(not(tarpaulin_include))]
395impl<T: fmt::Debug> fmt::Debug for EraseCryptoStoreError<T> {
396    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397        self.0.fmt(f)
398    }
399}
400
401#[cfg_attr(target_family = "wasm", async_trait(?Send))]
402#[cfg_attr(not(target_family = "wasm"), async_trait)]
403impl<T: CryptoStore> CryptoStore for EraseCryptoStoreError<T> {
404    type Error = CryptoStoreError;
405
406    async fn load_account(&self) -> Result<Option<Account>> {
407        self.0.load_account().await.map_err(Into::into)
408    }
409
410    async fn load_identity(&self) -> Result<Option<PrivateCrossSigningIdentity>> {
411        self.0.load_identity().await.map_err(Into::into)
412    }
413
414    async fn save_changes(&self, changes: Changes) -> Result<()> {
415        self.0.save_changes(changes).await.map_err(Into::into)
416    }
417
418    async fn save_pending_changes(&self, changes: PendingChanges) -> Result<()> {
419        self.0.save_pending_changes(changes).await.map_err(Into::into)
420    }
421
422    async fn save_inbound_group_sessions(
423        &self,
424        sessions: Vec<InboundGroupSession>,
425        backed_up_to_version: Option<&str>,
426    ) -> Result<()> {
427        self.0.save_inbound_group_sessions(sessions, backed_up_to_version).await.map_err(Into::into)
428    }
429
430    async fn get_sessions(&self, sender_key: &str) -> Result<Option<Vec<Session>>> {
431        self.0.get_sessions(sender_key).await.map_err(Into::into)
432    }
433
434    async fn get_inbound_group_session(
435        &self,
436        room_id: &RoomId,
437        session_id: &str,
438    ) -> Result<Option<InboundGroupSession>> {
439        self.0.get_inbound_group_session(room_id, session_id).await.map_err(Into::into)
440    }
441
442    async fn get_inbound_group_sessions(&self) -> Result<Vec<InboundGroupSession>> {
443        self.0.get_inbound_group_sessions().await.map_err(Into::into)
444    }
445
446    async fn get_inbound_group_sessions_by_room_id(
447        &self,
448        room_id: &RoomId,
449    ) -> Result<Vec<InboundGroupSession>> {
450        self.0.get_inbound_group_sessions_by_room_id(room_id).await.map_err(Into::into)
451    }
452
453    async fn get_inbound_group_sessions_for_device_batch(
454        &self,
455        curve_key: Curve25519PublicKey,
456        sender_data_type: SenderDataType,
457        after_session_id: Option<String>,
458        limit: usize,
459    ) -> Result<Vec<InboundGroupSession>> {
460        self.0
461            .get_inbound_group_sessions_for_device_batch(
462                curve_key,
463                sender_data_type,
464                after_session_id,
465                limit,
466            )
467            .await
468            .map_err(Into::into)
469    }
470
471    async fn inbound_group_session_counts(
472        &self,
473        backup_version: Option<&str>,
474    ) -> Result<RoomKeyCounts> {
475        self.0.inbound_group_session_counts(backup_version).await.map_err(Into::into)
476    }
477    async fn inbound_group_sessions_for_backup(
478        &self,
479        backup_version: &str,
480        limit: usize,
481    ) -> Result<Vec<InboundGroupSession>> {
482        self.0.inbound_group_sessions_for_backup(backup_version, limit).await.map_err(Into::into)
483    }
484
485    async fn mark_inbound_group_sessions_as_backed_up(
486        &self,
487        backup_version: &str,
488        room_and_session_ids: &[(&RoomId, &str)],
489    ) -> Result<()> {
490        self.0
491            .mark_inbound_group_sessions_as_backed_up(backup_version, room_and_session_ids)
492            .await
493            .map_err(Into::into)
494    }
495
496    async fn reset_backup_state(&self) -> Result<()> {
497        self.0.reset_backup_state().await.map_err(Into::into)
498    }
499
500    async fn load_backup_keys(&self) -> Result<BackupKeys> {
501        self.0.load_backup_keys().await.map_err(Into::into)
502    }
503
504    async fn load_dehydrated_device_pickle_key(&self) -> Result<Option<DehydratedDeviceKey>> {
505        self.0.load_dehydrated_device_pickle_key().await.map_err(Into::into)
506    }
507
508    async fn delete_dehydrated_device_pickle_key(&self) -> Result<(), Self::Error> {
509        self.0.delete_dehydrated_device_pickle_key().await.map_err(Into::into)
510    }
511
512    async fn get_outbound_group_session(
513        &self,
514        room_id: &RoomId,
515    ) -> Result<Option<OutboundGroupSession>> {
516        self.0.get_outbound_group_session(room_id).await.map_err(Into::into)
517    }
518
519    async fn load_tracked_users(&self) -> Result<Vec<TrackedUser>> {
520        self.0.load_tracked_users().await.map_err(Into::into)
521    }
522
523    async fn save_tracked_users(&self, users: &[(&UserId, bool)]) -> Result<()> {
524        self.0.save_tracked_users(users).await.map_err(Into::into)
525    }
526
527    async fn get_device(
528        &self,
529        user_id: &UserId,
530        device_id: &DeviceId,
531    ) -> Result<Option<DeviceData>> {
532        self.0.get_device(user_id, device_id).await.map_err(Into::into)
533    }
534
535    async fn get_user_devices(
536        &self,
537        user_id: &UserId,
538    ) -> Result<HashMap<OwnedDeviceId, DeviceData>> {
539        self.0.get_user_devices(user_id).await.map_err(Into::into)
540    }
541
542    async fn get_own_device(&self) -> Result<DeviceData> {
543        self.0.get_own_device().await.map_err(Into::into)
544    }
545
546    async fn get_user_identity(&self, user_id: &UserId) -> Result<Option<UserIdentityData>> {
547        self.0.get_user_identity(user_id).await.map_err(Into::into)
548    }
549
550    async fn is_message_known(&self, message_hash: &OlmMessageHash) -> Result<bool> {
551        self.0.is_message_known(message_hash).await.map_err(Into::into)
552    }
553
554    async fn get_outgoing_secret_requests(
555        &self,
556        request_id: &TransactionId,
557    ) -> Result<Option<GossipRequest>> {
558        self.0.get_outgoing_secret_requests(request_id).await.map_err(Into::into)
559    }
560
561    async fn get_secret_request_by_info(
562        &self,
563        secret_info: &SecretInfo,
564    ) -> Result<Option<GossipRequest>> {
565        self.0.get_secret_request_by_info(secret_info).await.map_err(Into::into)
566    }
567
568    async fn get_unsent_secret_requests(&self) -> Result<Vec<GossipRequest>> {
569        self.0.get_unsent_secret_requests().await.map_err(Into::into)
570    }
571
572    async fn delete_outgoing_secret_requests(&self, request_id: &TransactionId) -> Result<()> {
573        self.0.delete_outgoing_secret_requests(request_id).await.map_err(Into::into)
574    }
575
576    async fn get_secrets_from_inbox(
577        &self,
578        secret_name: &SecretName,
579    ) -> Result<Vec<GossippedSecret>> {
580        self.0.get_secrets_from_inbox(secret_name).await.map_err(Into::into)
581    }
582
583    async fn delete_secrets_from_inbox(&self, secret_name: &SecretName) -> Result<()> {
584        self.0.delete_secrets_from_inbox(secret_name).await.map_err(Into::into)
585    }
586
587    async fn get_withheld_info(
588        &self,
589        room_id: &RoomId,
590        session_id: &str,
591    ) -> Result<Option<RoomKeyWithheldEvent>, Self::Error> {
592        self.0.get_withheld_info(room_id, session_id).await.map_err(Into::into)
593    }
594
595    async fn get_room_settings(&self, room_id: &RoomId) -> Result<Option<RoomSettings>> {
596        self.0.get_room_settings(room_id).await.map_err(Into::into)
597    }
598
599    async fn get_received_room_key_bundle_data(
600        &self,
601        room_id: &RoomId,
602        user_id: &UserId,
603    ) -> Result<Option<StoredRoomKeyBundleData>> {
604        self.0.get_received_room_key_bundle_data(room_id, user_id).await.map_err(Into::into)
605    }
606
607    async fn get_custom_value(&self, key: &str) -> Result<Option<Vec<u8>>, Self::Error> {
608        self.0.get_custom_value(key).await.map_err(Into::into)
609    }
610
611    async fn set_custom_value(&self, key: &str, value: Vec<u8>) -> Result<(), Self::Error> {
612        self.0.set_custom_value(key, value).await.map_err(Into::into)
613    }
614
615    async fn remove_custom_value(&self, key: &str) -> Result<(), Self::Error> {
616        self.0.remove_custom_value(key).await.map_err(Into::into)
617    }
618
619    async fn try_take_leased_lock(
620        &self,
621        lease_duration_ms: u32,
622        key: &str,
623        holder: &str,
624    ) -> Result<bool, Self::Error> {
625        self.0.try_take_leased_lock(lease_duration_ms, key, holder).await.map_err(Into::into)
626    }
627
628    async fn next_batch_token(&self) -> Result<Option<String>, Self::Error> {
629        self.0.next_batch_token().await.map_err(Into::into)
630    }
631}
632
633/// A type-erased [`CryptoStore`].
634pub type DynCryptoStore = dyn CryptoStore<Error = CryptoStoreError>;
635
636/// A type that can be type-erased into `Arc<DynCryptoStore>`.
637///
638/// This trait is not meant to be implemented directly outside
639/// `matrix-sdk-crypto`, but it is automatically implemented for everything that
640/// implements `CryptoStore`.
641pub trait IntoCryptoStore {
642    #[doc(hidden)]
643    fn into_crypto_store(self) -> Arc<DynCryptoStore>;
644}
645
646impl<T> IntoCryptoStore for T
647where
648    T: CryptoStore + 'static,
649{
650    fn into_crypto_store(self) -> Arc<DynCryptoStore> {
651        Arc::new(EraseCryptoStoreError(self))
652    }
653}
654
655// Turns a given `Arc<T>` into `Arc<DynCryptoStore>` by attaching the
656// CryptoStore impl vtable of `EraseCryptoStoreError<T>`.
657impl<T> IntoCryptoStore for Arc<T>
658where
659    T: CryptoStore + 'static,
660{
661    fn into_crypto_store(self) -> Arc<DynCryptoStore> {
662        let ptr: *const T = Arc::into_raw(self);
663        let ptr_erased = ptr as *const EraseCryptoStoreError<T>;
664        // SAFETY: EraseCryptoStoreError is repr(transparent) so T and
665        //         EraseCryptoStoreError<T> have the same layout and ABI
666        unsafe { Arc::from_raw(ptr_erased) }
667    }
668}
669
670impl IntoCryptoStore for Arc<DynCryptoStore> {
671    fn into_crypto_store(self) -> Arc<DynCryptoStore> {
672        self
673    }
674}