fix(security): treat a new identity key as a message request, never as a known contact

A stranger could pick the display name of an existing contact ("Mom")
and appear as that contact. Messages from a key that is not in the
contact list now open a message request labelled "(unknown)": no
replies, read receipts, typing indicators, calls or name in
notifications until the user accepts it and names the contact
themselves. Names that look like an existing contact (case, diacritics,
Cyrillic/Greek homoglyphs, invisible characters) also show a warning in
the real contact's chat.

When the relay account of a known contact shows up with a different
key, the chat shows "safety number changed", verification is cleared
and the session that now belongs to the other key is dropped before
sending, so a reply never goes to the new key.
This commit is contained in:
Julius Joska
2026-10-23 10:50:42 +02:00
parent 2f6b542f24
commit 717aeed4ff
23 changed files with 999 additions and 67 deletions
+15 -2
View File
@@ -35,9 +35,14 @@ final avatarProvider =
final me = ref.watch(identityProvider).valueOrNull;
final contacts = ref.watch(contactsProvider).valueOrNull ?? const [];
if (!service.isStarted) return null;
final contact = contacts.where((c) => c.id == fingerprint).firstOrNull;
// Fotka neznámého odesílatele může být fotka mámy z Facebooku — do
// přijetí jen iniciály na tónu z klíče, ne jeho obrázek.
final avatar = fingerprint == me?.id
? service.myAvatar
: contacts.where((c) => c.id == fingerprint).firstOrNull?.avatar;
: contact != null && contact.accepted
? contact.avatar
: null;
return avatar == null ? null : service.loadAvatar(avatar);
});
@@ -83,7 +88,15 @@ final messagingStartProvider = FutureProvider<void>((ref) async {
final conv = await ref.read(storageProvider).loadConversation(convId);
final mentionedMe = message.mentions.contains(identity.id);
if (conv?.isMuted == true && !mentionedMe) return;
PushNotifier.showMessageNotification(contact.displayName);
// Jméno, které si neznámý zvolil sám, nepatří do notifikace —
// „Nová zpráva od Máma“ by byl phishing ještě před otevřením appky.
if (contact.accepted) {
PushNotifier.showMessageNotification(contact.displayName);
} else if (convId != contact.id) {
PushNotifier.showWakeNotification();
} else {
PushNotifier.showMessageRequestNotification();
}
};
service.onCallSignal = (contact, signal) =>