mirror of
https://github.com/chatmail/core.git
synced 2026-04-29 11:26:29 +03:00
replace chat.is_verified() by chat.is_protected()
This commit is contained in:
@@ -1329,7 +1329,7 @@ int dc_is_contact_in_chat (dc_context_t* context, uint32_t ch
|
|||||||
* If the group is already _promoted_ (any message was sent to the group),
|
* If the group is already _promoted_ (any message was sent to the group),
|
||||||
* all group members are informed by a special status message that is sent automatically by this function.
|
* all group members are informed by a special status message that is sent automatically by this function.
|
||||||
*
|
*
|
||||||
* If the group is a verified group, only verified contacts can be added to the group.
|
* If the group has group protection enabled, only verified contacts can be added to the group.
|
||||||
*
|
*
|
||||||
* Sends out #DC_EVENT_CHAT_MODIFIED and #DC_EVENT_MSGS_CHANGED if a status message was sent.
|
* Sends out #DC_EVENT_CHAT_MODIFIED and #DC_EVENT_MSGS_CHANGED if a status message was sent.
|
||||||
*
|
*
|
||||||
@@ -1973,7 +1973,7 @@ dc_lot_t* dc_check_qr (dc_context_t* context, const char*
|
|||||||
* @param context The context object.
|
* @param context The context object.
|
||||||
* @param chat_id If set to a group-chat-id,
|
* @param chat_id If set to a group-chat-id,
|
||||||
* the Verified-Group-Invite protocol is offered in the QR code;
|
* the Verified-Group-Invite protocol is offered in the QR code;
|
||||||
* works for verified groups as well as for normal groups.
|
* works for protected groups as well as for normal groups.
|
||||||
* If set to 0, the Setup-Contact protocol is offered in the QR code.
|
* If set to 0, the Setup-Contact protocol is offered in the QR code.
|
||||||
* See https://countermitm.readthedocs.io/en/latest/new.html
|
* See https://countermitm.readthedocs.io/en/latest/new.html
|
||||||
* for details about both protocols.
|
* for details about both protocols.
|
||||||
@@ -2003,7 +2003,7 @@ char* dc_get_securejoin_qr (dc_context_t* context, uint32_t ch
|
|||||||
* When the protocol has finished, an info-message is added to that chat.
|
* When the protocol has finished, an info-message is added to that chat.
|
||||||
* - If the given QR code starts the Verified-Group-Invite protocol,
|
* - If the given QR code starts the Verified-Group-Invite protocol,
|
||||||
* the function waits until the protocol has finished.
|
* the function waits until the protocol has finished.
|
||||||
* This is because the verified group is not opportunistic
|
* This is because the protected group is not opportunistic
|
||||||
* and can be created only when the contacts have verified each other.
|
* and can be created only when the contacts have verified each other.
|
||||||
*
|
*
|
||||||
* See https://countermitm.readthedocs.io/en/latest/new.html
|
* See https://countermitm.readthedocs.io/en/latest/new.html
|
||||||
@@ -2015,8 +2015,8 @@ char* dc_get_securejoin_qr (dc_context_t* context, uint32_t ch
|
|||||||
* to dc_check_qr().
|
* to dc_check_qr().
|
||||||
* @return Chat-id of the joined chat, the UI may redirect to the this chat.
|
* @return Chat-id of the joined chat, the UI may redirect to the this chat.
|
||||||
* If the out-of-band verification failed or was aborted, 0 is returned.
|
* If the out-of-band verification failed or was aborted, 0 is returned.
|
||||||
* A returned chat-id does not guarantee that the chat or the belonging contact is verified.
|
* A returned chat-id does not guarantee that the chat is protected or the belonging contact is verified.
|
||||||
* If needed, this be checked with dc_chat_is_verified() and dc_contact_is_verified(),
|
* If needed, this be checked with dc_chat_is_protected() and dc_contact_is_verified(),
|
||||||
* however, in practise, the UI will just listen to #DC_EVENT_CONTACTS_CHANGED unconditionally.
|
* however, in practise, the UI will just listen to #DC_EVENT_CONTACTS_CHANGED unconditionally.
|
||||||
*/
|
*/
|
||||||
uint32_t dc_join_securejoin (dc_context_t* context, const char* qr);
|
uint32_t dc_join_securejoin (dc_context_t* context, const char* qr);
|
||||||
@@ -2942,15 +2942,17 @@ int dc_chat_can_send (const dc_chat_t* chat);
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a chat is verified. Verified chats contain only verified members
|
* Check if a chat is protected.
|
||||||
* and encryption is alwasy enabled. Verified chats are created using
|
* Protected chats contain only verified members and encryption is always enabled.
|
||||||
* dc_create_group_chat() by setting the 'verified' parameter to true.
|
* Protected chats are created using dc_create_group_chat() by setting the 'protect' parameter to 1.
|
||||||
|
* The status can be changed using dc_set_chat_protection().
|
||||||
*
|
*
|
||||||
* @memberof dc_chat_t
|
* @memberof dc_chat_t
|
||||||
* @param chat The chat object.
|
* @param chat The chat object.
|
||||||
* @return 1=chat verified, 0=chat is not verified
|
* @return 1=chat protected, 0=chat is not protected
|
||||||
*/
|
*/
|
||||||
int dc_chat_is_verified (const dc_chat_t* chat);
|
int dc_chat_is_protected (const dc_chat_t* chat);
|
||||||
|
#define dc_chat_is_verified dc_chat_is_protected // allow using old function name for a while
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2389,13 +2389,13 @@ pub unsafe extern "C" fn dc_chat_can_send(chat: *mut dc_chat_t) -> libc::c_int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn dc_chat_is_verified(chat: *mut dc_chat_t) -> libc::c_int {
|
pub unsafe extern "C" fn dc_chat_is_protected(chat: *mut dc_chat_t) -> libc::c_int {
|
||||||
if chat.is_null() {
|
if chat.is_null() {
|
||||||
eprintln!("ignoring careless call to dc_chat_is_verified()");
|
eprintln!("ignoring careless call to dc_chat_is_protected()");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
let ffi_chat = &*chat;
|
let ffi_chat = &*chat;
|
||||||
ffi_chat.chat.is_verified() as libc::c_int
|
ffi_chat.chat.is_protected() as libc::c_int
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
|||||||
@@ -523,7 +523,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
|||||||
for i in (0..cnt).rev() {
|
for i in (0..cnt).rev() {
|
||||||
let chat = Chat::load_from_db(&context, chatlist.get_chat_id(i)).await?;
|
let chat = Chat::load_from_db(&context, chatlist.get_chat_id(i)).await?;
|
||||||
println!(
|
println!(
|
||||||
"{}#{}: {} [{} fresh] {}",
|
"{}#{}: {} [{} fresh] {}{}",
|
||||||
chat_prefix(&chat),
|
chat_prefix(&chat),
|
||||||
chat.get_id(),
|
chat.get_id(),
|
||||||
chat.get_name(),
|
chat.get_name(),
|
||||||
@@ -533,6 +533,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
|||||||
ChatVisibility::Archived => "📦",
|
ChatVisibility::Archived => "📦",
|
||||||
ChatVisibility::Pinned => "📌",
|
ChatVisibility::Pinned => "📌",
|
||||||
},
|
},
|
||||||
|
if chat.is_protected() { "🛡️" } else { "" },
|
||||||
);
|
);
|
||||||
let lot = chatlist.get_summary(&context, i, Some(&chat)).await;
|
let lot = chatlist.get_summary(&context, i, Some(&chat)).await;
|
||||||
let statestr = if chat.visibility == ChatVisibility::Archived {
|
let statestr = if chat.visibility == ChatVisibility::Archived {
|
||||||
@@ -607,7 +608,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
|||||||
format!("{} member(s)", members.len())
|
format!("{} member(s)", members.len())
|
||||||
};
|
};
|
||||||
println!(
|
println!(
|
||||||
"{}#{}: {} [{}]{}{}",
|
"{}#{}: {} [{}]{}{} {}",
|
||||||
chat_prefix(sel_chat),
|
chat_prefix(sel_chat),
|
||||||
sel_chat.get_id(),
|
sel_chat.get_id(),
|
||||||
sel_chat.get_name(),
|
sel_chat.get_name(),
|
||||||
@@ -624,6 +625,11 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
|||||||
},
|
},
|
||||||
_ => "".to_string(),
|
_ => "".to_string(),
|
||||||
},
|
},
|
||||||
|
if sel_chat.is_protected() {
|
||||||
|
"🛡️"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
);
|
);
|
||||||
log_msglist(&context, &msglist).await?;
|
log_msglist(&context, &msglist).await?;
|
||||||
if let Some(draft) = sel_chat.get_id().get_draft(&context).await? {
|
if let Some(draft) = sel_chat.get_id().get_draft(&context).await? {
|
||||||
|
|||||||
@@ -85,12 +85,12 @@ class Chat(object):
|
|||||||
"""
|
"""
|
||||||
return not lib.dc_chat_is_unpromoted(self._dc_chat)
|
return not lib.dc_chat_is_unpromoted(self._dc_chat)
|
||||||
|
|
||||||
def is_verified(self):
|
def is_protected(self):
|
||||||
""" return True if this chat is a verified group.
|
""" return True if this chat is a verified group.
|
||||||
|
|
||||||
:returns: True if chat is verified, False otherwise.
|
:returns: True if chat is verified, False otherwise.
|
||||||
"""
|
"""
|
||||||
return lib.dc_chat_is_verified(self._dc_chat)
|
return lib.dc_chat_is_protected(self._dc_chat)
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
""" return name of this chat.
|
""" return name of this chat.
|
||||||
|
|||||||
@@ -1343,7 +1343,7 @@ class TestOnlineAccount:
|
|||||||
ac1, ac2 = acfactory.get_two_online_accounts()
|
ac1, ac2 = acfactory.get_two_online_accounts()
|
||||||
lp.sec("ac1: create verified-group QR, ac2 scans and joins")
|
lp.sec("ac1: create verified-group QR, ac2 scans and joins")
|
||||||
chat1 = ac1.create_group_chat("hello", verified=True)
|
chat1 = ac1.create_group_chat("hello", verified=True)
|
||||||
assert chat1.is_verified()
|
assert chat1.is_protected()
|
||||||
qr = chat1.get_join_qr()
|
qr = chat1.get_join_qr()
|
||||||
lp.sec("ac2: start QR-code based join-group protocol")
|
lp.sec("ac2: start QR-code based join-group protocol")
|
||||||
chat2 = ac2.qr_join_chat(qr)
|
chat2 = ac2.qr_join_chat(qr)
|
||||||
@@ -1362,7 +1362,7 @@ class TestOnlineAccount:
|
|||||||
lp.sec("ac2: read message and check it's verified chat")
|
lp.sec("ac2: read message and check it's verified chat")
|
||||||
msg = ac2._evtracker.wait_next_incoming_message()
|
msg = ac2._evtracker.wait_next_incoming_message()
|
||||||
assert msg.text == "hello"
|
assert msg.text == "hello"
|
||||||
assert msg.chat.is_verified()
|
assert msg.chat.is_protected()
|
||||||
assert msg.is_encrypted()
|
assert msg.is_encrypted()
|
||||||
|
|
||||||
lp.sec("ac2: send message and let ac1 read it")
|
lp.sec("ac2: send message and let ac1 read it")
|
||||||
|
|||||||
22
src/chat.rs
22
src/chat.rs
@@ -566,6 +566,7 @@ pub struct Chat {
|
|||||||
pub param: Params,
|
pub param: Params,
|
||||||
is_sending_locations: bool,
|
is_sending_locations: bool,
|
||||||
pub mute_duration: MuteDuration,
|
pub mute_duration: MuteDuration,
|
||||||
|
protected: ProtectionStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chat {
|
impl Chat {
|
||||||
@@ -575,7 +576,7 @@ impl Chat {
|
|||||||
.sql
|
.sql
|
||||||
.query_row(
|
.query_row(
|
||||||
"SELECT c.type, c.name, c.grpid, c.param, c.archived,
|
"SELECT c.type, c.name, c.grpid, c.param, c.archived,
|
||||||
c.blocked, c.locations_send_until, c.muted_until
|
c.blocked, c.locations_send_until, c.muted_until, c.protected
|
||||||
FROM chats c
|
FROM chats c
|
||||||
WHERE c.id=?;",
|
WHERE c.id=?;",
|
||||||
paramsv![chat_id],
|
paramsv![chat_id],
|
||||||
@@ -590,6 +591,7 @@ impl Chat {
|
|||||||
blocked: row.get::<_, Option<_>>(5)?.unwrap_or_default(),
|
blocked: row.get::<_, Option<_>>(5)?.unwrap_or_default(),
|
||||||
is_sending_locations: row.get(6)?,
|
is_sending_locations: row.get(6)?,
|
||||||
mute_duration: row.get(7)?,
|
mute_duration: row.get(7)?,
|
||||||
|
protected: row.get(8)?,
|
||||||
};
|
};
|
||||||
Ok(c)
|
Ok(c)
|
||||||
},
|
},
|
||||||
@@ -755,9 +757,9 @@ impl Chat {
|
|||||||
!self.is_unpromoted()
|
!self.is_unpromoted()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if chat is a verified group chat.
|
/// Returns true if chat protection is enabled.
|
||||||
pub fn is_verified(&self) -> bool {
|
pub fn is_protected(&self) -> bool {
|
||||||
self.typ == Chattype::VerifiedGroup
|
self.protected == ProtectionStatus::Protected
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if location streaming is enabled in the chat.
|
/// Returns true if location streaming is enabled in the chat.
|
||||||
@@ -2062,7 +2064,7 @@ pub(crate) async fn add_contact_to_chat_ex(
|
|||||||
{
|
{
|
||||||
error!(
|
error!(
|
||||||
context,
|
context,
|
||||||
"Only bidirectional verified contacts can be added to verified groups."
|
"Only bidirectional verified contacts can be added to protected chats."
|
||||||
);
|
);
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
@@ -2626,7 +2628,7 @@ pub(crate) async fn get_chat_cnt(context: &Context) -> usize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a tuple of `(chatid, is_verified, blocked)`.
|
/// Returns a tuple of `(chatid, is_protected, blocked)`.
|
||||||
pub(crate) async fn get_chat_id_by_grpid(
|
pub(crate) async fn get_chat_id_by_grpid(
|
||||||
context: &Context,
|
context: &Context,
|
||||||
grpid: impl AsRef<str>,
|
grpid: impl AsRef<str>,
|
||||||
@@ -2634,14 +2636,16 @@ pub(crate) async fn get_chat_id_by_grpid(
|
|||||||
context
|
context
|
||||||
.sql
|
.sql
|
||||||
.query_row(
|
.query_row(
|
||||||
"SELECT id, blocked, type FROM chats WHERE grpid=?;",
|
"SELECT id, blocked, protected FROM chats WHERE grpid=?;",
|
||||||
paramsv![grpid.as_ref()],
|
paramsv![grpid.as_ref()],
|
||||||
|row| {
|
|row| {
|
||||||
let chat_id = row.get::<_, ChatId>(0)?;
|
let chat_id = row.get::<_, ChatId>(0)?;
|
||||||
|
|
||||||
let b = row.get::<_, Option<Blocked>>(1)?.unwrap_or_default();
|
let b = row.get::<_, Option<Blocked>>(1)?.unwrap_or_default();
|
||||||
let v = row.get::<_, Option<Chattype>>(2)?.unwrap_or_default();
|
let p = row
|
||||||
Ok((chat_id, v == Chattype::VerifiedGroup, b))
|
.get::<_, Option<ProtectionStatus>>(2)?
|
||||||
|
.unwrap_or_default();
|
||||||
|
Ok((chat_id, p == ProtectionStatus::Protected, b))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -1731,7 +1731,7 @@ async fn check_verified_properties(
|
|||||||
}
|
}
|
||||||
if !is_verified {
|
if !is_verified {
|
||||||
bail!(
|
bail!(
|
||||||
"{} is not a member of this verified group",
|
"{} is not a member of this protected chat",
|
||||||
to_addr.to_string()
|
to_addr.to_string()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -348,7 +348,7 @@ async fn securejoin(context: &Context, qr: &str) -> Result<ChatId, JoinError> {
|
|||||||
let bob = context.bob.read().await;
|
let bob = context.bob.read().await;
|
||||||
let grpid = bob.qr_scan.as_ref().unwrap().text2.as_ref().unwrap();
|
let grpid = bob.qr_scan.as_ref().unwrap().text2.as_ref().unwrap();
|
||||||
match chat::get_chat_id_by_grpid(context, grpid).await {
|
match chat::get_chat_id_by_grpid(context, grpid).await {
|
||||||
Ok((chatid, _is_verified, _blocked)) => break chatid,
|
Ok((chatid, _is_protected, _blocked)) => break chatid,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if start.elapsed() > Duration::from_secs(7) {
|
if start.elapsed() > Duration::from_secs(7) {
|
||||||
return Err(JoinError::MissingChat(err));
|
return Err(JoinError::MissingChat(err));
|
||||||
@@ -791,19 +791,19 @@ pub(crate) async fn handle_securejoin_handshake(
|
|||||||
|
|
||||||
let vg_expect_encrypted = if join_vg {
|
let vg_expect_encrypted = if join_vg {
|
||||||
let group_id = get_qr_attr!(context, text2).to_string();
|
let group_id = get_qr_attr!(context, text2).to_string();
|
||||||
// This is buggy, is_verified_group will always be
|
// This is buggy, is_protected_group will always be
|
||||||
// false since the group is created by receive_imf by
|
// false since the group is created by receive_imf by
|
||||||
// the very handshake message we're handling now. But
|
// the very handshake message we're handling now. But
|
||||||
// only after we have returned. It does not impact
|
// only after we have returned. It does not impact
|
||||||
// the security invariants of secure-join however.
|
// the security invariants of secure-join however.
|
||||||
let (_, is_verified_group, _) = chat::get_chat_id_by_grpid(context, &group_id)
|
let (_, is_protected_group, _) = chat::get_chat_id_by_grpid(context, &group_id)
|
||||||
.await
|
.await
|
||||||
.unwrap_or((ChatId::new(0), false, Blocked::Not));
|
.unwrap_or((ChatId::new(0), false, Blocked::Not));
|
||||||
// when joining a non-verified group
|
// when joining a non-verified group
|
||||||
// the vg-member-added message may be unencrypted
|
// the vg-member-added message may be unencrypted
|
||||||
// when not all group members have keys or prefer encryption.
|
// when not all group members have keys or prefer encryption.
|
||||||
// So only expect encryption if this is a verified group
|
// So only expect encryption if this is a verified group
|
||||||
is_verified_group
|
is_protected_group
|
||||||
} else {
|
} else {
|
||||||
// setup contact is always encrypted
|
// setup contact is always encrypted
|
||||||
true
|
true
|
||||||
@@ -1428,6 +1428,6 @@ mod tests {
|
|||||||
|
|
||||||
let bob_chatid = joiner.await;
|
let bob_chatid = joiner.await;
|
||||||
let bob_chat = Chat::load_from_db(&bob.ctx, bob_chatid).await.unwrap();
|
let bob_chat = Chat::load_from_db(&bob.ctx, bob_chatid).await.unwrap();
|
||||||
assert!(bob_chat.is_verified());
|
assert!(bob_chat.is_protected());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user