diff --git a/CHANGELOG.md b/CHANGELOG.md
index 787d93d2f..8edd9f065 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
### API-Changes
- jsonrpc: add python API for webxdc updates #3872
+- Add ffi functions to retrieve `verified by` information #3786
### Fixes
- Do not add an error if the message is encrypted but not signed #3860
@@ -26,8 +27,6 @@
- Only send the message about ephemeral timer change if the chat is promoted #3847
- Use relative paths in `accounts.toml` #3838
-### API-Changes
-
### Fixes
- Set read/write timeouts for IMAP over SOCKS5 #3833
- Treat attached PGP keys as peer keys with mutual encryption preference #3832
diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h
index 95e4cf45f..4270d0003 100644
--- a/deltachat-ffi/deltachat.h
+++ b/deltachat-ffi/deltachat.h
@@ -4735,6 +4735,37 @@ int dc_contact_is_blocked (const dc_contact_t* contact);
int dc_contact_is_verified (dc_contact_t* contact);
+
+/**
+ * Return the address that verified a contact
+ *
+ * The UI may use this in addition to a checkmark showing the verification status
+ *
+ * @memberof dc_contact_t
+ * @param contact The contact object.
+ * @return
+ * A string containing the verifiers address. If it is the same address as the contact itself,
+ * we verified the contact ourself. If it is an empty string, we don't have verifier
+ * information or the contact is not verified.
+ */
+char* dc_contact_get_verifier_addr (dc_contact_t* contact);
+
+
+/**
+ * Return the `ContactId` that verified a contact
+ *
+ * The UI may use this in addition to a checkmark showing the verification status
+ *
+ * @memberof dc_contact_t
+ * @param contact The contact object.
+ * @return
+ * The `ContactId` of the verifiers address. If it is the same address as the contact itself,
+ * we verified the contact ourself. If it is 0, we don't have verifier information or
+ * the contact is not verified.
+ */
+int dc_contact_get_verifier_id (dc_contact_t* contact);
+
+
/**
* @class dc_provider_t
*
diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs
index a3b772c28..51a5d3353 100644
--- a/deltachat-ffi/src/lib.rs
+++ b/deltachat-ffi/src/lib.rs
@@ -3963,6 +3963,40 @@ pub unsafe extern "C" fn dc_contact_is_verified(contact: *mut dc_contact_t) -> l
.unwrap_or_default() as libc::c_int
}
+#[no_mangle]
+pub unsafe extern "C" fn dc_contact_get_verifier_addr(
+ contact: *mut dc_contact_t,
+) -> *mut libc::c_char {
+ if contact.is_null() {
+ eprintln!("ignoring careless call to dc_contact_get_verifier_addr()");
+ return "".strdup();
+ }
+ let ffi_contact = &*contact;
+ let ctx = &*ffi_contact.context;
+ block_on(Contact::get_verifier_addr(
+ ctx,
+ &ffi_contact.contact.get_id(),
+ ))
+ .log_err(ctx, "failed to get verifier for contact")
+ .unwrap_or_default()
+ .strdup()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn dc_contact_get_verifier_id(contact: *mut dc_contact_t) -> libc::c_int {
+ if contact.is_null() {
+ eprintln!("ignoring careless call to dc_contact_get_verifier_id()");
+ return 0 as libc::c_int;
+ }
+ let ffi_contact = &*contact;
+ let ctx = &*ffi_contact.context;
+ let contact_id = block_on(Contact::get_verifier_id(ctx, &ffi_contact.contact.get_id()))
+ .log_err(ctx, "failed to get verifier")
+ .unwrap_or_default()
+ .unwrap_or_default();
+
+ contact_id.to_u32() as libc::c_int
+}
// dc_lot_t
pub type dc_lot_t = lot::Lot;
diff --git a/python/src/deltachat/contact.py b/python/src/deltachat/contact.py
index 69fa809b6..75c44b8e5 100644
--- a/python/src/deltachat/contact.py
+++ b/python/src/deltachat/contact.py
@@ -75,6 +75,10 @@ class Contact(object):
"""Return True if the contact is verified."""
return lib.dc_contact_is_verified(self._dc_contact)
+ def get_verifier(self, contact):
+ """Return the address of the contact that verified the contact"""
+ return from_dc_charpointer(lib.dc_contact_get_verifier_addr(contact._dc_contact))
+
def get_profile_image(self) -> Optional[str]:
"""Get contact profile image.
diff --git a/python/tests/test_0_complex_or_slow.py b/python/tests/test_0_complex_or_slow.py
index 335d07590..034839ee5 100644
--- a/python/tests/test_0_complex_or_slow.py
+++ b/python/tests/test_0_complex_or_slow.py
@@ -123,6 +123,7 @@ class TestGroupStressTests:
def test_qr_verified_group_and_chatting(acfactory, lp):
ac1, ac2, ac3 = acfactory.get_online_accounts(3)
+ ac1_addr = ac1.get_self_contact().addr
lp.sec("ac1: create verified-group QR, ac2 scans and joins")
chat1 = ac1.create_group_chat("hello", verified=True)
assert chat1.is_protected()
@@ -141,12 +142,17 @@ def test_qr_verified_group_and_chatting(acfactory, lp):
msg_out = chat1.send_text("hello")
assert msg_out.is_encrypted()
- lp.sec("ac2: read message and check it's verified chat")
+ lp.sec("ac2: read message and check that it's a verified chat")
msg = ac2._evtracker.wait_next_incoming_message()
assert msg.text == "hello"
assert msg.chat.is_protected()
assert msg.is_encrypted()
+ lp.sec("ac2: Check that ac2 verified ac1")
+ # If we verified the contact ourselves then verifier addr == contact addr
+ ac2_ac1_contact = ac2.get_contacts()[0]
+ assert ac2.get_self_contact().get_verifier(ac2_ac1_contact) == ac1_addr
+
lp.sec("ac2: send message and let ac1 read it")
chat2.send_text("world")
msg = ac1._evtracker.wait_next_incoming_message()
@@ -168,6 +174,12 @@ def test_qr_verified_group_and_chatting(acfactory, lp):
assert msg.is_system_message()
assert not msg.error
+ lp.sec("ac2: Check that ac1 verified ac3 for ac2")
+ ac2_ac1_contact = ac2.get_contacts()[0]
+ assert ac2.get_self_contact().get_verifier(ac2_ac1_contact) == ac1_addr
+ ac2_ac3_contact = ac2.get_contacts()[1]
+ assert ac2.get_self_contact().get_verifier(ac2_ac3_contact) == ac1_addr
+
lp.sec("ac2: send message and let ac3 read it")
chat2.send_text("hi")
# Skip system message about added member
diff --git a/src/chat.rs b/src/chat.rs
index 22818dda0..8be73e1ef 100644
--- a/src/chat.rs
+++ b/src/chat.rs
@@ -3562,6 +3562,7 @@ mod tests {
use crate::constants::{DC_GCL_ARCHIVED_ONLY, DC_GCL_NO_SPECIALS};
use crate::contact::Contact;
use crate::receive_imf::receive_imf;
+
use crate::test_utils::TestContext;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
diff --git a/src/contact.rs b/src/contact.rs
index 17b74987d..baf74b4ac 100644
--- a/src/contact.rs
+++ b/src/contact.rs
@@ -1138,6 +1138,31 @@ impl Contact {
Ok(VerifiedStatus::Unverified)
}
+ /// Return the address that verified the given contact
+ pub async fn get_verifier_addr(
+ context: &Context,
+ contact_id: &ContactId,
+ ) -> Result