Skip to content

Commit 181ee64

Browse files
committed
crypto: Expose a way to pin a user's identity
1 parent a12a46b commit 181ee64

File tree

6 files changed

+108
-3
lines changed

6 files changed

+108
-3
lines changed

bindings/matrix-sdk-ffi/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,5 @@ Breaking changes:
3131

3232
Additions:
3333

34+
- Add `Encryption::get_user_identity`
3435
- Add `ClientBuilder::room_key_recipient_strategy`

bindings/matrix-sdk-ffi/src/encryption.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,50 @@ impl Encryption {
410410
pub async fn wait_for_e2ee_initialization_tasks(&self) {
411411
self.inner.wait_for_e2ee_initialization_tasks().await;
412412
}
413+
414+
/// Get the E2EE identity of a user.
415+
///
416+
/// Returns an error if this user does not exist, if there is an error
417+
/// contacting the crypto store, or if our client is not logged in.
418+
pub async fn get_user_identity(
419+
&self,
420+
user_id: String,
421+
) -> Result<Arc<UserIdentity>, ClientError> {
422+
Ok(Arc::new(UserIdentity {
423+
inner: self
424+
.inner
425+
.get_user_identity(user_id.as_str().try_into()?)
426+
.await?
427+
.ok_or(ClientError::new("User not found"))?,
428+
}))
429+
}
430+
}
431+
432+
/// The E2EE identity of a user.
433+
#[derive(uniffi::Object)]
434+
pub struct UserIdentity {
435+
inner: matrix_sdk::encryption::identities::UserIdentity,
436+
}
437+
438+
#[uniffi::export]
439+
impl UserIdentity {
440+
/// Remember this identity, ensuring it does not result in a pin violation.
441+
///
442+
/// When we first see a user, we assume their cryptographic identity has not
443+
/// been tampered with by the homeserver or another entity with
444+
/// man-in-the-middle capabilities. We remember this identity and call this
445+
/// action "pinning".
446+
///
447+
/// If the identity presented for the user changes later on, the newly
448+
/// presented identity is considered to be in "pin violation". This
449+
/// method explicitly accepts the new identity, allowing it to replace
450+
/// the previously pinned one and bringing it out of pin violation.
451+
///
452+
/// UIs should display a warning to the user when encountering an identity
453+
/// which is not verified and is in pin violation.
454+
pub(crate) async fn pin(&self) -> Result<(), ClientError> {
455+
Ok(self.inner.pin().await?)
456+
}
413457
}
414458

415459
#[derive(uniffi::Object)]

crates/matrix-sdk-crypto/src/identities/user.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,32 @@ impl UserIdentity {
125125
}
126126
}
127127

128+
/// Remember this identity, ensuring it does not result in a pin violation.
129+
///
130+
/// When we first see a user, we assume their cryptographic identity has not
131+
/// been tampered with by the homeserver or another entity with
132+
/// man-in-the-middle capabilities. We remember this identity and call this
133+
/// action "pinning".
134+
///
135+
/// If the identity presented for the user changes later on, the newly
136+
/// presented identity is considered to be in "pin violation". This
137+
/// method explicitly accepts the new identity, allowing it to replace
138+
/// the previously pinned one and bringing it out of pin violation.
139+
///
140+
/// UIs should display a warning to the user when encountering an identity
141+
/// which is not verified and is in pin violation. See
142+
/// [`OtherUserIdentity::identity_needs_user_approval`].
143+
pub async fn pin(&self) -> Result<(), CryptoStoreError> {
144+
match self {
145+
UserIdentity::Own(_) => {
146+
// Nothing to be done for our own identity: we already
147+
// consider it trusted in this sense.
148+
Ok(())
149+
}
150+
UserIdentity::Other(u) => u.pin_current_master_key().await,
151+
}
152+
}
153+
128154
/// Was this identity previously verified, and is no longer?
129155
pub fn has_verification_violation(&self) -> bool {
130156
match self {
@@ -737,7 +763,21 @@ impl OtherUserIdentityData {
737763
&self.self_signing_key
738764
}
739765

740-
/// Pin the current identity
766+
/// Remember this identity, ensuring it does not result in a pin violation.
767+
///
768+
/// When we first see a user, we assume their cryptographic identity has not
769+
/// been tampered with by the homeserver or another entity with
770+
/// man-in-the-middle capabilities. We remember this identity and call this
771+
/// action "pinning".
772+
///
773+
/// If the identity presented for the user changes later on, the newly
774+
/// presented identity is considered to be in "pin violation". This
775+
/// method explicitly accepts the new identity, allowing it to replace
776+
/// the previously pinned one and bringing it out of pin violation.
777+
///
778+
/// UIs should display a warning to the user when encountering an identity
779+
/// which is not verified and is in pin violation. See
780+
/// [`OtherUserIdentity::identity_needs_user_approval`].
741781
pub(crate) fn pin(&self) {
742782
let mut m = self.pinned_master_key.write().unwrap();
743783
*m = self.master_key.as_ref().clone()

crates/matrix-sdk/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Breaking changes:
2828

2929
Additions:
3030

31+
- new `UserIdentity::pin` method.
3132
- new `ClientBuilder::with_decryption_trust_requirement` method.
3233
- new `ClientBuilder::with_room_key_recipient_strategy` method
3334
- new `Room.set_account_data` and `Room.set_account_data_raw` RoomAccountData setters, analogous to the GlobalAccountData

crates/matrix-sdk/src/encryption/identities/users.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,24 @@ impl UserIdentity {
422422
self.inner.withdraw_verification().await
423423
}
424424

425+
/// Remember this identity, ensuring it does not result in a pin violation.
426+
///
427+
/// When we first see a user, we assume their cryptographic identity has not
428+
/// been tampered with by the homeserver or another entity with
429+
/// man-in-the-middle capabilities. We remember this identity and call this
430+
/// action "pinning".
431+
///
432+
/// If the identity presented for the user changes later on, the newly
433+
/// presented identity is considered to be in "pin violation". This
434+
/// method explicitly accepts the new identity, allowing it to replace
435+
/// the previously pinned one and bringing it out of pin violation.
436+
///
437+
/// UIs should display a warning to the user when encountering an identity
438+
/// which is not verified and is in pin violation.
439+
pub async fn pin(&self) -> Result<(), CryptoStoreError> {
440+
self.inner.pin().await
441+
}
442+
425443
/// Get the public part of the Master key of this user identity.
426444
///
427445
/// The public part of the Master key is usually used to uniquely identify

crates/matrix-sdk/src/room/identity_status_changes.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,9 +430,10 @@ mod tests {
430430
);
431431

432432
// Pin it
433-
self.crypto_other_identity()
433+
self.user_identity()
434434
.await
435-
.pin_current_master_key()
435+
.expect("User should exist")
436+
.pin()
436437
.await
437438
.expect("Should not fail to pin");
438439
} else {

0 commit comments

Comments
 (0)