Skip to content

Commit 92c6dd4

Browse files
committed
api: add API to reset contact encryption
1 parent c627d2f commit 92c6dd4

File tree

4 files changed

+106
-0
lines changed

4 files changed

+106
-0
lines changed

deltachat-jsonrpc/src/api.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,6 +1419,15 @@ impl CommandApi {
14191419
Ok(())
14201420
}
14211421

1422+
/// Resets contact encryption.
1423+
async fn reset_contact_encryption(&self, account_id: u32, contact_id: u32) -> Result<()> {
1424+
let ctx = self.get_context(account_id).await?;
1425+
let contact_id = ContactId::new(contact_id);
1426+
1427+
contact_id.reset_encryption(&ctx).await?;
1428+
Ok(())
1429+
}
1430+
14221431
async fn change_contact_name(
14231432
&self,
14241433
account_id: u32,

deltachat-rpc-client/src/deltachat_rpc_client/contact.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ def delete(self) -> None:
3636
"""Delete contact."""
3737
self._rpc.delete_contact(self.account.id, self.id)
3838

39+
def reset_encryption(self) -> None:
40+
"""Reset contact encryption."""
41+
self._rpc.reset_contact_encryption(self.account.id, self.id)
42+
3943
def set_name(self, name: str) -> None:
4044
"""Change the name of this contact."""
4145
self._rpc.change_contact_name(self.account.id, self.id, name)

deltachat-rpc-client/tests/test_something.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ def test_contact(acfactory) -> None:
246246
assert repr(alice_contact_bob)
247247
alice_contact_bob.block()
248248
alice_contact_bob.unblock()
249+
alice_contact_bob.reset_encryption()
249250
alice_contact_bob.set_name("new name")
250251
alice_contact_bob.get_encryption_info()
251252
snapshot = alice_contact_bob.get_snapshot()

src/contact.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,43 @@ impl ContactId {
143143
.await?;
144144
Ok(())
145145
}
146+
147+
/// Returns contact adress.
148+
pub async fn addr(&self, context: &Context) -> Result<String> {
149+
let addr = context
150+
.sql
151+
.query_row("SELECT addr FROM contacts WHERE id=?", (self,), |row| {
152+
let addr: String = row.get(0)?;
153+
Ok(addr)
154+
})
155+
.await?;
156+
Ok(addr)
157+
}
158+
159+
/// Resets encryption with the contact.
160+
///
161+
/// Effect is similar to receiving a message without Autocrypt header
162+
/// from the contact, but this action is triggered manually by the user.
163+
///
164+
/// For example, this will result in sending the next message
165+
/// to 1:1 chat unencrypted, but will not remove existing verified keys.
166+
pub async fn reset_encryption(self, context: &Context) -> Result<()> {
167+
let now = time();
168+
169+
let addr = self.addr(context).await?;
170+
if let Some(mut peerstate) = Peerstate::from_addr(context, &addr).await? {
171+
peerstate.degrade_encryption(now);
172+
peerstate.save_to_db(&context.sql).await?;
173+
}
174+
175+
// Reset 1:1 chat protection.
176+
if let Some(chat_id) = ChatId::lookup_by_contact(context, self).await? {
177+
chat_id
178+
.set_protection(context, ProtectionStatus::Unprotected, now, Some(self))
179+
.await?;
180+
}
181+
Ok(())
182+
}
146183
}
147184

148185
impl fmt::Display for ContactId {
@@ -3152,4 +3189,59 @@ Until the false-positive is fixed:
31523189

31533190
Ok(())
31543191
}
3192+
3193+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
3194+
async fn test_reset_encryption() -> Result<()> {
3195+
let mut tcm = TestContextManager::new();
3196+
let alice = &tcm.alice().await;
3197+
let bob = &tcm.bob().await;
3198+
3199+
let msg = tcm.send_recv_accept(alice, bob, "Hello!").await;
3200+
assert_eq!(msg.get_showpadlock(), false);
3201+
3202+
let msg = tcm.send_recv(bob, alice, "Hi!").await;
3203+
assert_eq!(msg.get_showpadlock(), true);
3204+
let alice_bob_contact_id = msg.from_id;
3205+
3206+
alice_bob_contact_id.reset_encryption(alice).await?;
3207+
3208+
let msg = tcm.send_recv(alice, bob, "Unencrypted").await;
3209+
assert_eq!(msg.get_showpadlock(), false);
3210+
3211+
Ok(())
3212+
}
3213+
3214+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
3215+
async fn test_reset_verified_encryption() -> Result<()> {
3216+
let mut tcm = TestContextManager::new();
3217+
let alice = &tcm.alice().await;
3218+
let bob = &tcm.bob().await;
3219+
3220+
tcm.execute_securejoin(bob, alice).await;
3221+
3222+
let msg = tcm.send_recv(bob, alice, "Encrypted").await;
3223+
assert_eq!(msg.get_showpadlock(), true);
3224+
3225+
let alice_bob_chat_id = msg.chat_id;
3226+
let alice_bob_contact_id = msg.from_id;
3227+
alice_bob_contact_id.reset_encryption(alice).await?;
3228+
3229+
// Check that the contact is still verified after resetting encryption.
3230+
let alice_bob_contact = Contact::get_by_id(alice, alice_bob_contact_id).await?;
3231+
assert_eq!(alice_bob_contact.is_verified(alice).await?, true);
3232+
3233+
// 1:1 chat and profile is no longer verified.
3234+
assert_eq!(alice_bob_contact.is_profile_verified(alice).await?, false);
3235+
3236+
let info_msg = alice.get_last_msg_in(alice_bob_chat_id).await;
3237+
assert_eq!(
3238+
info_msg.text,
3239+
"[email protected] sent a message from another device."
3240+
);
3241+
3242+
let msg = tcm.send_recv(alice, bob, "Unencrypted").await;
3243+
assert_eq!(msg.get_showpadlock(), false);
3244+
3245+
Ok(())
3246+
}
31553247
}

0 commit comments

Comments
 (0)