From f05f97251c9a3129db315009cd24759fb2ecd7c0 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 19 Dec 2015 12:43:09 +0100 Subject: prefer server name over address book name when x509 verification is being used --- src/main/java/eu/siacs/conversations/entities/Contact.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java index f924c05a1..ecba70a77 100644 --- a/src/main/java/eu/siacs/conversations/entities/Contact.java +++ b/src/main/java/eu/siacs/conversations/entities/Contact.java @@ -11,6 +11,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; +import eu.siacs.conversations.Config; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.jid.InvalidJidException; @@ -104,10 +105,12 @@ public class Contact implements ListItem, Blockable { } public String getDisplayName() { - if (this.systemName != null) { + if (this.systemName != null && !Config.X509_VERIFICATION) { return this.systemName; } else if (this.serverName != null) { return this.serverName; + } else if (this.systemName != null) { + return this.systemName; } else if (this.presenceName != null) { return this.presenceName; } else if (jid.hasLocalpart()) { -- cgit v1.2.3 From e8bf5cada4dcbd65bbd7f51ebd7233dbfaad1881 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 19 Dec 2015 12:44:12 +0100 Subject: only offer plain and omemo encryption when x509 verification is enabled --- .../java/eu/siacs/conversations/entities/Conversation.java | 11 ++++++++++- .../java/eu/siacs/conversations/ui/ConversationActivity.java | 5 +++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index a085f0c03..962ad13b3 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -20,6 +20,7 @@ import java.util.Iterator; import java.util.List; import eu.siacs.conversations.Config; +import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.xmpp.chatstate.ChatState; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; @@ -607,6 +608,14 @@ public class Conversation extends AbstractEntity implements Blockable { } public int getNextEncryption() { + final AxolotlService axolotlService = getAccount().getAxolotlService(); + if (Config.X509_VERIFICATION && mode == MODE_SINGLE) { + if (axolotlService != null && axolotlService.isContactAxolotlCapable(getContact())) { + return Message.ENCRYPTION_AXOLOTL; + } else { + return Message.ENCRYPTION_NONE; + } + } int next = this.getIntAttribute(ATTRIBUTE_NEXT_ENCRYPTION, -1); if (next == -1) { int outgoing = this.getMostRecentlyUsedOutgoingEncryption(); @@ -617,7 +626,7 @@ public class Conversation extends AbstractEntity implements Blockable { } } if (Config.FORCE_ENCRYPTION && mode == MODE_SINGLE && next <= 0) { - if (getAccount().getAxolotlService().isContactAxolotlCapable(getContact())) { + if (axolotlService != null && axolotlService.isContactAxolotlCapable(getContact())) { return Message.ENCRYPTION_AXOLOTL; } else { return Message.ENCRYPTION_OTR; diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java index 688ee95cc..e6c2aad1a 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java @@ -414,7 +414,7 @@ public class ConversationActivity extends XmppActivity menuContactDetails.setVisible(false); menuAttach.setVisible(getSelectedConversation().getAccount().httpUploadAvailable() && getSelectedConversation().getMucOptions().participating()); menuInviteContact.setVisible(getSelectedConversation().getMucOptions().canInvite()); - menuSecure.setVisible(!Config.HIDE_PGP_IN_UI); //if pgp is hidden conferences have no choice of encryption + menuSecure.setVisible(!Config.HIDE_PGP_IN_UI && !Config.X509_VERIFICATION); //if pgp is hidden conferences have no choice of encryption } else { menuMucDetails.setVisible(false); } @@ -868,8 +868,9 @@ public class ConversationActivity extends XmppActivity MenuItem none = popup.getMenu().findItem(R.id.encryption_choice_none); MenuItem pgp = popup.getMenu().findItem(R.id.encryption_choice_pgp); MenuItem axolotl = popup.getMenu().findItem(R.id.encryption_choice_axolotl); - pgp.setVisible(!Config.HIDE_PGP_IN_UI); + pgp.setVisible(!Config.HIDE_PGP_IN_UI && !Config.X509_VERIFICATION); none.setVisible(!Config.FORCE_ENCRYPTION); + otr.setVisible(!Config.X509_VERIFICATION); if (conversation.getMode() == Conversation.MODE_MULTI) { otr.setVisible(false); axolotl.setVisible(false); -- cgit v1.2.3 From 15c8cb8ac6de9b16d7ef4e405b4129b2377a42b8 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 19 Dec 2015 12:44:55 +0100 Subject: add more debugging to certificate checks after new omemo session was established --- .../java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java index e22b05c00..9f964d038 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java @@ -580,6 +580,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { } private void verifySessionWithPEP(final XmppAxolotlSession session, final IdentityKey identityKey) { + Log.d(Config.LOGTAG,"trying to verify fresh session ("+session.getRemoteAddress().getName()+") with pep"); final AxolotlAddress address = session.getRemoteAddress(); try { IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveVerificationForDevice(Jid.fromString(address.getName()), address.getDeviceId()); @@ -607,6 +608,8 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { } catch (Exception e) { Log.d(Config.LOGTAG, "error during verification " + e.getMessage()); } + } else { + Log.d(Config.LOGTAG,"no verification found"); } fetchStatusMap.put(address, FetchStatus.SUCCESS); finishBuildingSessionsFromPEP(address); @@ -944,6 +947,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { } private void putFreshSession(XmppAxolotlSession session) { + Log.d(Config.LOGTAG,"put fresh session"); sessions.put(session); if (Config.X509_VERIFICATION) { IdentityKey identityKey = axolotlStore.loadSession(session.getRemoteAddress()).getSessionState().getRemoteIdentityKey(); -- cgit v1.2.3 From ade89beb9647f753ebe7382b1b07bcfb3b688798 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 19 Dec 2015 13:07:38 +0100 Subject: use presence name not server name when verification is being used --- src/main/java/eu/siacs/conversations/entities/Contact.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java index ecba70a77..c2d8b278b 100644 --- a/src/main/java/eu/siacs/conversations/entities/Contact.java +++ b/src/main/java/eu/siacs/conversations/entities/Contact.java @@ -105,12 +105,12 @@ public class Contact implements ListItem, Blockable { } public String getDisplayName() { - if (this.systemName != null && !Config.X509_VERIFICATION) { + if (this.presenceName != null && Config.X509_VERIFICATION) { + return this.presenceName; + } else if (this.systemName != null) { return this.systemName; } else if (this.serverName != null) { return this.serverName; - } else if (this.systemName != null) { - return this.systemName; } else if (this.presenceName != null) { return this.presenceName; } else if (jid.hasLocalpart()) { -- cgit v1.2.3 From 534013fd0c53e29b674130e536d50a3548dce68a Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 19 Dec 2015 15:44:11 +0100 Subject: store identity key in XmppAxolotlSession instead of the fingerprint --- .../crypto/axolotl/AxolotlService.java | 25 +++++++++++----------- .../crypto/axolotl/XmppAxolotlSession.java | 25 +++++++++++++--------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java index 9f964d038..88ce99aa0 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java @@ -146,8 +146,8 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { for (Integer deviceId : deviceIds) { AxolotlAddress axolotlAddress = new AxolotlAddress(bareJid, deviceId); Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building session for remote address: " + axolotlAddress.toString()); - String fingerprint = store.loadSession(axolotlAddress).getSessionState().getRemoteIdentityKey().getFingerprint().replaceAll("\\s", ""); - this.put(axolotlAddress, new XmppAxolotlSession(account, store, axolotlAddress, fingerprint)); + IdentityKey identityKey = store.loadSession(axolotlAddress).getSessionState().getRemoteIdentityKey(); + this.put(axolotlAddress, new XmppAxolotlSession(account, store, axolotlAddress, identityKey)); } } @@ -579,9 +579,10 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { axolotlStore.setFingerprintTrust(fingerprint, trust); } - private void verifySessionWithPEP(final XmppAxolotlSession session, final IdentityKey identityKey) { - Log.d(Config.LOGTAG,"trying to verify fresh session ("+session.getRemoteAddress().getName()+") with pep"); + private void verifySessionWithPEP(final XmppAxolotlSession session) { + Log.d(Config.LOGTAG, "trying to verify fresh session (" + session.getRemoteAddress().getName() + ") with pep"); final AxolotlAddress address = session.getRemoteAddress(); + final IdentityKey identityKey = session.getIdentityKey(); try { IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveVerificationForDevice(Jid.fromString(address.getName()), address.getDeviceId()); mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { @@ -681,10 +682,10 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { try { SessionBuilder builder = new SessionBuilder(axolotlStore, address); builder.process(preKeyBundle); - XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, bundle.getIdentityKey().getFingerprint().replaceAll("\\s", "")); + XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, bundle.getIdentityKey()); sessions.put(address, session); if (Config.X509_VERIFICATION) { - verifySessionWithPEP(session, bundle.getIdentityKey()); + verifySessionWithPEP(session); } else { fetchStatusMap.put(address, FetchStatus.SUCCESS); finishBuildingSessionsFromPEP(address); @@ -721,7 +722,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey(); if (identityKey != null) { Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache..."); - XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey.getFingerprint().replaceAll("\\s", "")); + XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey); sessions.put(address, session); } else { Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Found device " + account.getJid().toBareJid() + ":" + foreignId); @@ -743,7 +744,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey(); if (identityKey != null) { Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache..."); - XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey.getFingerprint().replaceAll("\\s", "")); + XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey); sessions.put(address, session); } else { Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Found device " + account.getJid().toBareJid() + ":" + ownId); @@ -892,8 +893,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { private XmppAxolotlSession recreateUncachedSession(AxolotlAddress address) { IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey(); return (identityKey != null) - ? new XmppAxolotlSession(account, axolotlStore, address, - identityKey.getFingerprint().replaceAll("\\s", "")) + ? new XmppAxolotlSession(account, axolotlStore, address, identityKey) : null; } @@ -950,9 +950,8 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { Log.d(Config.LOGTAG,"put fresh session"); sessions.put(session); if (Config.X509_VERIFICATION) { - IdentityKey identityKey = axolotlStore.loadSession(session.getRemoteAddress()).getSessionState().getRemoteIdentityKey(); - if (identityKey != null) { - verifySessionWithPEP(session, identityKey); + if (session.getIdentityKey() != null) { + verifySessionWithPEP(session); } else { Log.e(Config.LOGTAG,account.getJid().toBareJid()+": identity key was empty after reloading for x509 verification"); } diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java index c452acfd4..b713eb5fe 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java @@ -6,6 +6,7 @@ import android.util.Log; import org.whispersystems.libaxolotl.AxolotlAddress; import org.whispersystems.libaxolotl.DuplicateMessageException; +import org.whispersystems.libaxolotl.IdentityKey; import org.whispersystems.libaxolotl.InvalidKeyException; import org.whispersystems.libaxolotl.InvalidKeyIdException; import org.whispersystems.libaxolotl.InvalidMessageException; @@ -29,7 +30,7 @@ public class XmppAxolotlSession { private final SQLiteAxolotlStore sqLiteAxolotlStore; private final AxolotlAddress remoteAddress; private final Account account; - private String fingerprint = null; + private IdentityKey identityKey; private Integer preKeyId = null; private boolean fresh = true; @@ -103,9 +104,9 @@ public class XmppAxolotlSession { } } - public XmppAxolotlSession(Account account, SQLiteAxolotlStore store, AxolotlAddress remoteAddress, String fingerprint) { + public XmppAxolotlSession(Account account, SQLiteAxolotlStore store, AxolotlAddress remoteAddress, IdentityKey identityKey) { this(account, store, remoteAddress); - this.fingerprint = fingerprint.replaceAll("\\s",""); + this.identityKey = identityKey; } public XmppAxolotlSession(Account account, SQLiteAxolotlStore store, AxolotlAddress remoteAddress) { @@ -125,7 +126,11 @@ public class XmppAxolotlSession { } public String getFingerprint() { - return fingerprint; + return identityKey == null ? null : identityKey.getFingerprint().replaceAll("\\s", ""); + } + + public IdentityKey getIdentityKey() { + return identityKey; } public AxolotlAddress getRemoteAddress() { @@ -141,11 +146,11 @@ public class XmppAxolotlSession { } protected void setTrust(Trust trust) { - sqLiteAxolotlStore.setFingerprintTrust(fingerprint, trust); + sqLiteAxolotlStore.setFingerprintTrust(getFingerprint(), trust); } protected Trust getTrust() { - Trust trust = sqLiteAxolotlStore.getFingerprintTrust(fingerprint); + Trust trust = sqLiteAxolotlStore.getFingerprintTrust(getFingerprint()); return (trust == null) ? Trust.UNDECIDED : trust; } @@ -164,11 +169,11 @@ public class XmppAxolotlSession { try { PreKeyWhisperMessage message = new PreKeyWhisperMessage(encryptedKey); Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage received, new session ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId()); - String fingerprint = message.getIdentityKey().getFingerprint().replaceAll("\\s", ""); - if (this.fingerprint != null && !this.fingerprint.equals(fingerprint)) { - Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Had session with fingerprint " + this.fingerprint + ", received message with fingerprint " + fingerprint); + IdentityKey msgIdentityKey = message.getIdentityKey(); + if (this.identityKey != null && !this.identityKey.equals(msgIdentityKey)) { + Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Had session with fingerprint " + this.getFingerprint() + ", received message with fingerprint " + msgIdentityKey.getFingerprint()); } else { - this.fingerprint = fingerprint; + this.identityKey = msgIdentityKey; plaintext = cipher.decrypt(message); if (message.getPreKeyId().isPresent()) { preKeyId = message.getPreKeyId().get(); -- cgit v1.2.3 From ae491764f2619809e5b6037e9123acf9594c2f4e Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 20 Dec 2015 19:37:27 +0100 Subject: pulled translations from transifex --- src/main/res/values-de/strings.xml | 10 ++++++++-- src/main/res/values-fr/strings.xml | 40 ++++++++++++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index 0ed90f750..ce1071b12 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -89,13 +89,14 @@ Conversations benutzt eine Drittanwendung namens OpenKeychain, um Nachrichten zu ver- und entschlüsseln und um deine Schlüssel zu verwalten.\n\nOpenKeychain ist GPLv3-lizenziert und kann über F-Droid oder Google Play bezogen werden.\n\n(Bitte starte Conversations danach neu.) Neu starten Installieren + Bitte OpenKeychain installieren angeboten… warten… Kein OpenPGP-Schlüssel gefunden Conversations ist nicht in der Lage, deine Nachrichten zu verschlüsseln, weil dein Kontakt seinen oder ihren Schlüssel nicht preisgibt.\n\nBitte sag deinem Kontakt, er oder sie möge OpenPGP einrichten. Keine OpenPGP-Schlüssel gefunden Conversations ist nicht in der Lage, deine Nachrichten zu verschlüsseln, weil deine Kontakte ihre Schlüssel nicht preisgeben.\n\nBitte sage deinen Kontakten, sie mögen OpenPGP einrichten. - Verschlüsselte Nachricht erhalten. Drücke hier, um sie zu entschlüsseln. + Verschlüsselte Nachricht erhalten. Antippen zum Entschlüsseln. Allgemeines XMPP-Ressource Der Name, mit dem sich der Client selbst identifiziert @@ -216,7 +217,7 @@ Eigener OMEMO-Fingerabdruck Andere Geräte OMEMO-Fingerabdruck vertrauen - Schlüssel empfangen… + Schlüssel werden abgerufen … Erledigt Überprüfen Entschlüsseln @@ -521,4 +522,9 @@ %d Nachricht %d Nachrichten + Datei mit %s geteilt + Bild mit %s geteilt + Conversations benötigt Zugriff auf externen Speicher + Mit Kontakten synchronisieren + Conversations möchte deine XMPP-Kontaktliste mit deinen Kontakten abgleichen, um deren vollständige Namen und Avatare anzuzeigen.\n\nConversations wird deine Kontakte nur lokal lesen und abgleichen und überträgt diese nicht auf den Server.\n\nDu wirst nun gefragt, ob du den Zugriff auf deine Kontakte erlauben möchtest. diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index a5a2d5230..a00885804 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -28,6 +28,8 @@ Il y a %d minutes Conversations non lues Envoi… + Déchiffrement du message. Veuillez patienter... + Message chiffré avec OpenPGP Cet identifiant est déjà utilisé. Administrateur Propriétaire @@ -78,6 +80,7 @@ Envoyer un message non chiffré Envoyer un message chiffré avec OTR Envoyé un message chiffré avec OMEMO + Envoyé un message chiffré avec \\OMEMO Envoyer un message chiffré avec OpenPGP Votre identifiant a été changé Envoyer en clair @@ -86,12 +89,14 @@ Conversations requiert une application tierce nommée OpenKeychain pour chiffrer et déchiffrer les messages.\n\nOpenKeychain est sous licence GPLv3 et est disponible sur F-Droid et Google Play.\n\n(Veuillez redémarrer Conversations apres l\'installation de l\'app) Redémarrer Installer + Veuillez installer OpenKeychain Proposition… Patientez… Aucune clef OpenPGP trouvée. Conversations ne peut pas chiffrer vos messages car votre correspondant n\'a pas communiqué sa clef publique.\n\nDemandez-lui de configurer OpenPGP. Aucune clef OpenPGP n\'a été trouvée. Conversations ne peut pas chiffrer votre message car vos contacts ne communiquent pas leur clef publique.\n\nDemandez-leur de configurer OpenPGP. + Message chiffré reçu. Appuyez pour déchiffrer. Général Ressource XMPP Nom utilisé par ce client pour s\'identifier @@ -170,6 +175,7 @@ Les deux mots de passe ne correspondent pas. Cet identifiant n\'est pas valide. Plus de mémoire disponible. L\'image est trop volumineuse. + Voulez-vous ajouter %s aux contacts de l\'appareil ? En ligne Disponible Absent @@ -189,7 +195,7 @@ XEP-0363 : Envoi de fichiers via HTTP supporté non supporté - Aucune annonce de clef publique + Annonce de clef publique manquante en ligne à l\'instant en ligne il y a 1 minute en ligne il y a %d minutes @@ -205,10 +211,13 @@ Votre empreinte Empreinte OTR Empreinte OMEMO + v\\Empreinte OMEMO Empreinte OMEMO du message + v\\Empreinte OMEMO du message Votre empreinte OMEMO Autres appareils Faire confiance aux empreintes OMEMO + Récupération des clefs... Terminé Vérifier Déchiffrer @@ -227,13 +236,13 @@ salle@conference.exemple.com Enregistrer comme favori Supprimer le favori - Ce favori existe déjà + Le favori existe déjà Vous Modifier le sujet de la conférence Impossible de trouver la conférence Partir Votre correspondant vous a ajouté dans sa liste de contacts - Ajouter également + Ré-ajouter %s a tout lu jusqu\'à ici Publier Toucher l\'avatar pour choisir une image depuis la galerie. @@ -333,7 +342,7 @@ Évite que le système ne ferme votre connexion. Exporter les historiques Sauvegarder les historiques sur la carte SD - Sauvegarde des historiques sur la carte SD en cours... + Sauvegarde des historiques sur la carte SD... Choix du fichier Réception %1$s (%2$d%% complété) Télécharger %s @@ -438,6 +447,7 @@ En train de proposer un(e) %s Se cacher hors-ligne Désactiver le compte + %s est en train d\'écrire %s a arrêté d\'écrire Notifications d\'écriture Informer votre contact lorsque vous êtes en train d\'écrire un message. @@ -480,6 +490,7 @@ Échec du téléchargement : impossible de se connecter à l\'hôte Utiliser un fond blanc Afficher les messages reçus en texte noir sur fond blanc. + Réseau Tor indisponible Détraqué Options de présence Absent quand l\'écran est éteint @@ -495,4 +506,25 @@ La chaîne de certificats n\'est pas digne de confiance L\'identifiant ne correspond pas au certificat Renouveler le certificat + Erreur lors de la récupération de la clef OMEMO ! + Clef OMEMO vérifiée avec un certificat ! + Votre appareil ne supporte pas la sélection de certificats client ! + Options de connexion + Connection via Tor + Faire transiter toutes les connexions sur le réseau Tor. Requiert Orbot + Nom d\'hôte + Port + Adresse du serveur ou .onion + Ce numéro de port n\'est pas valide + Ce nom d\'hôte n\'est pas valide + %1$d compte(s) sur %2$d connecté(s) + + %d message + %d messages + + Fichier partagé avec %s + Image partagée avec %s + Conversations a besoin d\'accéder au stockage externe + Synchroniser avec contacts + Conversations souhaite associer vos contacts XMPP avec les contacts de votre appareil, pour utiliser leur nom complet et leur avatar.\n\nConversations va uniquement lire vos contacts et les associer localement, sans les uploader sur le serveur XMPP.\n\nVotre appareil va maintenant vous demander la permission d\'accéder à vos contacts. -- cgit v1.2.3 From 186757136866a8e3dabec0ab45f3e2995b788991 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 20 Dec 2015 19:37:33 +0100 Subject: version bump to 1.8.2 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 3a6d19796..47e2856f3 100644 --- a/build.gradle +++ b/build.gradle @@ -53,8 +53,8 @@ android { defaultConfig { minSdkVersion 14 targetSdkVersion 23 - versionCode 112 - versionName "1.8.1" + versionCode 113 + versionName "1.8.2" project.ext.set(archivesBaseName, archivesBaseName + "-" + versionName); } -- cgit v1.2.3 From d0bad09f13886c7d8ee20c0205293cb0250d9c2f Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 23 Dec 2015 17:41:26 +0100 Subject: save certificate when verifying with x509 --- .../crypto/axolotl/AxolotlService.java | 6 +++-- .../crypto/axolotl/SQLiteAxolotlStore.java | 6 +++++ .../conversations/persistance/DatabaseBackend.java | 31 ++++++++++++++++++++-- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java index 88ce99aa0..ad9b8be30 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java @@ -597,8 +597,10 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { if (verifier.verify(verification.second)) { try { mXmppConnectionService.getMemorizingTrustManager().getNonInteractive().checkClientTrusted(verification.first, "RSA"); - Log.d(Config.LOGTAG, "verified session with x.509 signature. fingerprint was: "+session.getFingerprint()); - setFingerprintTrust(session.getFingerprint(), XmppAxolotlSession.Trust.TRUSTED_X509); + String fingerprint = session.getFingerprint(); + Log.d(Config.LOGTAG, "verified session with x.509 signature. fingerprint was: "+fingerprint); + setFingerprintTrust(fingerprint, XmppAxolotlSession.Trust.TRUSTED_X509); + axolotlStore.setFingerprintCertificate(fingerprint, verification.first[0]); fetchStatusMap.put(address, FetchStatus.SUCCESS_VERIFIED); finishBuildingSessionsFromPEP(address); return; diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java index a78317183..788a391db 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java @@ -15,6 +15,7 @@ import org.whispersystems.libaxolotl.state.SessionRecord; import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; import org.whispersystems.libaxolotl.util.KeyHelper; +import java.security.cert.X509Certificate; import java.util.List; import java.util.Set; @@ -36,6 +37,7 @@ public class SQLiteAxolotlStore implements AxolotlStore { public static final String NAME = "name"; public static final String TRUSTED = "trusted"; public static final String OWN = "ownkey"; + public static final String CERTIFICATE = "certificate"; public static final String JSONKEY_REGISTRATION_ID = "axolotl_reg_id"; public static final String JSONKEY_CURRENT_PREKEY_ID = "axolotl_cur_prekey_id"; @@ -213,6 +215,10 @@ public class SQLiteAxolotlStore implements AxolotlStore { trustCache.remove(fingerprint); } + public void setFingerprintCertificate(String fingerprint, X509Certificate x509Certificate) { + mXmppConnectionService.databaseBackend.setIdentityKeyCertificate(account, fingerprint, x509Certificate); + } + public Set getContactKeysWithTrust(String bareJid, XmppAxolotlSession.Trust trust) { return mXmppConnectionService.databaseBackend.loadIdentityKeys(account, bareJid, trust); } diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index e482f0f85..4882d72b3 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -15,11 +15,14 @@ import org.whispersystems.libaxolotl.AxolotlAddress; import org.whispersystems.libaxolotl.IdentityKey; import org.whispersystems.libaxolotl.IdentityKeyPair; import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.state.AxolotlStore; import org.whispersystems.libaxolotl.state.PreKeyRecord; import org.whispersystems.libaxolotl.state.SessionRecord; import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; import java.io.IOException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; @@ -44,7 +47,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { private static DatabaseBackend instance = null; private static final String DATABASE_NAME = "history"; - private static final int DATABASE_VERSION = 21; + private static final int DATABASE_VERSION = 22; private static String CREATE_CONTATCS_STATEMENT = "create table " + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, " @@ -102,6 +105,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { + SQLiteAxolotlStore.NAME + " TEXT, " + SQLiteAxolotlStore.OWN + " INTEGER, " + SQLiteAxolotlStore.FINGERPRINT + " TEXT, " + + SQLiteAxolotlStore.CERTIFICATE + " BLOB, " + SQLiteAxolotlStore.TRUSTED + " INTEGER, " + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY(" + SQLiteAxolotlStore.ACCOUNT @@ -345,6 +349,10 @@ public class DatabaseBackend extends SQLiteOpenHelper { + "=?", new String[]{account.getUuid()}); } } + + if (oldVersion < 22 && newVersion >= 22) { + db.execSQL("ALTER TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + " ADD COLUMN " + SQLiteAxolotlStore.CERTIFICATE); + } } public static synchronized DatabaseBackend getInstance(Context context) { @@ -596,7 +604,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { SQLiteDatabase db = this.getReadableDatabase(); String sql = "select messages.timeSent,messages.serverMsgId from accounts join conversations on accounts.uuid=conversations.accountUuid join messages on conversations.uuid=messages.conversationUuid where accounts.uuid=? and (messages.status=0 or messages.carbon=1 or messages.serverMsgId not null) order by messages.timesent desc limit 1"; String[] args = {account.getUuid()}; - Cursor cursor = db.rawQuery(sql,args); + Cursor cursor = db.rawQuery(sql, args); if (cursor.getCount() ==0) { return null; } else { @@ -1050,6 +1058,25 @@ public class DatabaseBackend extends SQLiteOpenHelper { return rows == 1; } + public boolean setIdentityKeyCertificate(Account account, String fingerprint, X509Certificate x509Certificate) { + SQLiteDatabase db = this.getWritableDatabase(); + String[] selectionArgs = { + account.getUuid(), + fingerprint + }; + try { + ContentValues values = new ContentValues(); + values.put(SQLiteAxolotlStore.CERTIFICATE, x509Certificate.getEncoded()); + return db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, values, + SQLiteAxolotlStore.ACCOUNT + " = ? AND " + + SQLiteAxolotlStore.FINGERPRINT + " = ? ", + selectionArgs) == 1; + } catch (CertificateEncodingException e) { + Log.d(Config.LOGTAG,"could not encode certificate"); + return false; + } + } + public void storeIdentityKey(Account account, String name, IdentityKey identityKey) { storeIdentityKey(account, name, false, identityKey.getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKey.serialize(), Base64.DEFAULT)); } -- cgit v1.2.3 From f46cbb38a92ff5281a974ecc0932ba5459c7334e Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 23 Dec 2015 19:18:53 +0100 Subject: show certificate information --- .../crypto/axolotl/AxolotlService.java | 4 + .../crypto/axolotl/SQLiteAxolotlStore.java | 4 + .../conversations/persistance/DatabaseBackend.java | 39 ++++++++-- .../conversations/ui/ContactDetailsActivity.java | 44 ++++++++++- .../conversations/ui/EditAccountActivity.java | 2 +- .../siacs/conversations/ui/TrustKeysActivity.java | 2 + .../eu/siacs/conversations/ui/XmppActivity.java | 10 ++- .../eu/siacs/conversations/utils/CryptoHelper.java | 50 ++++++++++++ src/main/res/layout/certificate_information.xml | 88 ++++++++++++++++++++++ src/main/res/values/strings.xml | 7 ++ 10 files changed, 240 insertions(+), 10 deletions(-) create mode 100644 src/main/res/layout/certificate_information.xml diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java index ad9b8be30..31b23e3cb 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java @@ -575,6 +575,10 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { return axolotlStore.getFingerprintTrust(fingerprint); } + public X509Certificate getFingerprintCertificate(String fingerprint) { + return axolotlStore.getFingerprintCertificate(fingerprint); + } + public void setFingerprintTrust(String fingerprint, XmppAxolotlSession.Trust trust) { axolotlStore.setFingerprintTrust(fingerprint, trust); } diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java index 788a391db..3c8cb3c10 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java @@ -219,6 +219,10 @@ public class SQLiteAxolotlStore implements AxolotlStore { mXmppConnectionService.databaseBackend.setIdentityKeyCertificate(account, fingerprint, x509Certificate); } + public X509Certificate getFingerprintCertificate(String fingerprint) { + return mXmppConnectionService.databaseBackend.getIdentityKeyCertifcate(account, fingerprint); + } + public Set getContactKeysWithTrust(String bareJid, XmppAxolotlSession.Trust trust) { return mXmppConnectionService.databaseBackend.loadIdentityKeys(account, bareJid, trust); } diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index 4882d72b3..3077c4889 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -15,13 +15,15 @@ import org.whispersystems.libaxolotl.AxolotlAddress; import org.whispersystems.libaxolotl.IdentityKey; import org.whispersystems.libaxolotl.IdentityKeyPair; import org.whispersystems.libaxolotl.InvalidKeyException; -import org.whispersystems.libaxolotl.state.AxolotlStore; import org.whispersystems.libaxolotl.state.PreKeyRecord; import org.whispersystems.libaxolotl.state.SessionRecord; import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.HashSet; @@ -600,16 +602,16 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args); } - public Pair getLastMessageReceived(Account account) { + public Pair getLastMessageReceived(Account account) { SQLiteDatabase db = this.getReadableDatabase(); String sql = "select messages.timeSent,messages.serverMsgId from accounts join conversations on accounts.uuid=conversations.accountUuid join messages on conversations.uuid=messages.conversationUuid where accounts.uuid=? and (messages.status=0 or messages.carbon=1 or messages.serverMsgId not null) order by messages.timesent desc limit 1"; String[] args = {account.getUuid()}; Cursor cursor = db.rawQuery(sql, args); - if (cursor.getCount() ==0) { + if (cursor.getCount() == 0) { return null; } else { cursor.moveToFirst(); - return new Pair<>(cursor.getLong(0),cursor.getString(1)); + return new Pair<>(cursor.getLong(0), cursor.getString(1)); } } @@ -1072,11 +1074,38 @@ public class DatabaseBackend extends SQLiteOpenHelper { + SQLiteAxolotlStore.FINGERPRINT + " = ? ", selectionArgs) == 1; } catch (CertificateEncodingException e) { - Log.d(Config.LOGTAG,"could not encode certificate"); + Log.d(Config.LOGTAG, "could not encode certificate"); return false; } } + public X509Certificate getIdentityKeyCertifcate(Account account, String fingerprint) { + SQLiteDatabase db = this.getReadableDatabase(); + String[] selectionArgs = { + account.getUuid(), + fingerprint + }; + String[] colums = {SQLiteAxolotlStore.CERTIFICATE}; + String selection = SQLiteAxolotlStore.ACCOUNT + " = ? AND " + SQLiteAxolotlStore.FINGERPRINT + " = ? "; + Cursor cursor = db.query(SQLiteAxolotlStore.IDENTITIES_TABLENAME, colums, selection, selectionArgs, null, null, null); + if (cursor.getCount() < 1) { + return null; + } else { + cursor.moveToFirst(); + byte[] certificate = cursor.getBlob(cursor.getColumnIndex(SQLiteAxolotlStore.CERTIFICATE)); + if (certificate == null || certificate.length == 0) { + return null; + } + try { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + return (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(certificate)); + } catch (CertificateException e) { + Log.d(Config.LOGTAG,"certificate exception "+e.getMessage()); + return null; + } + } + } + public void storeIdentityKey(Account account, String name, IdentityKey identityKey) { storeIdentityKey(account, name, false, identityKey.getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKey.serialize(), Base64.DEFAULT)); } diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java index 10bdaab16..5143190fd 100644 --- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -14,6 +14,7 @@ import android.provider.ContactsContract; import android.provider.ContactsContract.CommonDataKinds; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Intents; +import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -30,12 +31,14 @@ import android.widget.TextView; import org.openintents.openpgp.util.OpenPgpUtils; +import java.security.cert.X509Certificate; import java.util.List; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.crypto.axolotl.AxolotlService; +import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.ListItem; @@ -394,7 +397,12 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd } for (final String fingerprint : contact.getAccount().getAxolotlService().getFingerprintsForContact(contact)) { boolean highlight = fingerprint.equals(messageFingerprint); - hasKeys |= addFingerprintRow(keys, contact.getAccount(), fingerprint, highlight); + hasKeys |= addFingerprintRow(keys, contact.getAccount(), fingerprint, highlight, new OnClickListener() { + @Override + public void onClick(View v) { + onOmemoKeyClicked(contact.getAccount(), fingerprint); + } + }); } if (contact.getPgpKeyId() != 0) { hasKeys = true; @@ -446,6 +454,40 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd } } + private void onOmemoKeyClicked(Account account, String fingerprint) { + Log.d(Config.LOGTAG,"on omemo key clicked"); + final XmppAxolotlSession.Trust trust = account.getAxolotlService().getFingerprintTrust(fingerprint); + if (trust != null) { + X509Certificate x509Certificate = account.getAxolotlService().getFingerprintCertificate(fingerprint); + if (x509Certificate != null) { + Log.d(Config.LOGTAG, "certificate for fingerprint " + fingerprint + " was not null"); + showCertificateInformationDialog(CryptoHelper.extractCertificateInformation(x509Certificate)); + } + } + } + + private void showCertificateInformationDialog(Bundle bundle) { + View view = getLayoutInflater().inflate(R.layout.certificate_information, null); + final String not_available = getString(R.string.certicate_info_not_available); + TextView subject_cn = (TextView) view.findViewById(R.id.subject_cn); + TextView subject_o = (TextView) view.findViewById(R.id.subject_o); + TextView issuer_cn = (TextView) view.findViewById(R.id.issuer_cn); + TextView issuer_o = (TextView) view.findViewById(R.id.issuer_o); + TextView sha1 = (TextView) view.findViewById(R.id.sha1); + + subject_cn.setText(bundle.getString("subject_cn", not_available)); + subject_o.setText(bundle.getString("subject_o", not_available)); + issuer_cn.setText(bundle.getString("issuer_cn", not_available)); + issuer_o.setText(bundle.getString("issuer_o", not_available)); + sha1.setText(bundle.getString("sha1", not_available)); + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.certificate_information); + builder.setView(view); + builder.setPositiveButton(R.string.ok, null); + builder.create().show(); + } + protected void confirmToDeleteFingerprint(final String fingerprint) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.delete_fingerprint); diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java index a6c459968..31d26b23c 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java @@ -710,7 +710,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate continue; } boolean highlight = fingerprint.equals(messageFingerprint); - hasKeys |= addFingerprintRow(keys, mAccount, fingerprint, highlight); + hasKeys |= addFingerprintRow(keys, mAccount, fingerprint, highlight, null); } if (hasKeys) { keysCard.setVisibility(View.VISIBLE); diff --git a/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java b/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java index 4bd0d42da..2e6d32466 100644 --- a/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java @@ -126,6 +126,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate // own fingerprints have no impact on locked status. } }, + null, null ); } @@ -140,6 +141,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate lockOrUnlockAsNeeded(); } }, + null, null ); } diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java index 4cb93fdef..b372692d3 100644 --- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java @@ -648,7 +648,7 @@ public abstract class XmppActivity extends Activity { builder.create().show(); } - protected boolean addFingerprintRow(LinearLayout keys, final Account account, final String fingerprint, boolean highlight) { + protected boolean addFingerprintRow(LinearLayout keys, final Account account, final String fingerprint, boolean highlight, View.OnClickListener onKeyClickedListener) { final XmppAxolotlSession.Trust trust = account.getAxolotlService() .getFingerprintTrust(fingerprint); if (trust == null) { @@ -670,7 +670,8 @@ public abstract class XmppActivity extends Activity { XmppAxolotlSession.Trust.UNTRUSTED); v.setEnabled(true); } - } + }, + onKeyClickedListener ); } @@ -682,13 +683,16 @@ public abstract class XmppActivity extends Activity { boolean showTag, CompoundButton.OnCheckedChangeListener onCheckedChangeListener, - View.OnClickListener onClickListener) { + View.OnClickListener onClickListener, + View.OnClickListener onKeyClickedListener) { if (trust == XmppAxolotlSession.Trust.COMPROMISED) { return false; } View view = getLayoutInflater().inflate(R.layout.contact_key, keys, false); TextView key = (TextView) view.findViewById(R.id.key); + key.setOnClickListener(onKeyClickedListener); TextView keyType = (TextView) view.findViewById(R.id.key_type); + keyType.setOnClickListener(onKeyClickedListener); Switch trustToggle = (Switch) view.findViewById(R.id.tgl_trust); trustToggle.setVisibility(View.VISIBLE); trustToggle.setOnCheckedChangeListener(onCheckedChangeListener); diff --git a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java index ab407249c..d1b90ab64 100644 --- a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java @@ -1,5 +1,6 @@ package eu.siacs.conversations.utils; +import android.os.Bundle; import android.util.Log; import android.util.Pair; @@ -9,6 +10,7 @@ import org.bouncycastle.asn1.x500.style.IETFUtils; import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; import org.bouncycastle.jce.PrincipalUtil; +import java.security.MessageDigest; import java.security.SecureRandom; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateParsingException; @@ -121,6 +123,14 @@ public final class CryptoHelper { return builder.toString(); } + public static String prettifyFingerprintCert(String fingerprint) { + StringBuilder builder = new StringBuilder(fingerprint); + for(int i=2;i < builder.length(); i+=3) { + builder.insert(i,':'); + } + return builder.toString(); + } + public static String[] getOrderedCipherSuites(final String[] platformSupportedCipherSuites) { final Collection cipherSuites = new LinkedHashSet<>(Arrays.asList(Config.ENABLED_CIPHERS)); final List platformCiphers = Arrays.asList(platformSupportedCipherSuites); @@ -167,6 +177,46 @@ public final class CryptoHelper { } } + public static Bundle extractCertificateInformation(X509Certificate certificate) { + Bundle information = new Bundle(); + try { + JcaX509CertificateHolder holder = new JcaX509CertificateHolder(certificate); + X500Name subject = holder.getSubject(); + try { + information.putString("subject_cn", subject.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString()); + } catch (Exception e) { + //ignored + } + try { + information.putString("subject_o",subject.getRDNs(BCStyle.O)[0].getFirst().getValue().toString()); + } catch (Exception e) { + //ignored + } + + X500Name issuer = holder.getIssuer(); + try { + information.putString("issuer_cn", issuer.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString()); + } catch (Exception e) { + //ignored + } + try { + information.putString("issuer_o", issuer.getRDNs(BCStyle.O)[0].getFirst().getValue().toString()); + } catch (Exception e) { + //ignored + } + try { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + byte[] fingerprint = md.digest(certificate.getEncoded()); + information.putString("sha1", prettifyFingerprintCert(bytesToHex(fingerprint))); + } catch (Exception e) { + + } + return information; + } catch (CertificateEncodingException e) { + return information; + } + } + public static int encryptionTypeToText(int encryption) { switch (encryption) { case Message.ENCRYPTION_OTR: diff --git a/src/main/res/layout/certificate_information.xml b/src/main/res/layout/certificate_information.xml new file mode 100644 index 000000000..4c085459b --- /dev/null +++ b/src/main/res/layout/certificate_information.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index d3cda8668..2666e98d4 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -560,4 +560,11 @@ Conversations need access to external storage Synchronize with contacts Conversations wants to match your XMPP roster with your contacts to show their full names and avatars.\n\nConversations will only read your contacts and match them locally without uploading them to your server.\n\nYou will now be asked to grant permission to access your contacts. + Certificate Information + Subject + Issuer + Common Name + Organization + SHA1 + (Not available) -- cgit v1.2.3 From c40372fc0d1bc81650321b0166c06a0be05ac0a2 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 23 Dec 2015 22:30:14 +0100 Subject: code cleanup --- .../conversations/ui/ContactDetailsActivity.java | 7 ++++--- .../eu/siacs/conversations/utils/CryptoHelper.java | 22 ---------------------- src/main/res/values/strings.xml | 1 + 3 files changed, 5 insertions(+), 25 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java index 5143190fd..04885ad10 100644 --- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -28,6 +28,7 @@ import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.QuickContactBadge; import android.widget.TextView; +import android.widget.Toast; import org.openintents.openpgp.util.OpenPgpUtils; @@ -455,13 +456,13 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd } private void onOmemoKeyClicked(Account account, String fingerprint) { - Log.d(Config.LOGTAG,"on omemo key clicked"); final XmppAxolotlSession.Trust trust = account.getAxolotlService().getFingerprintTrust(fingerprint); - if (trust != null) { + if (trust != null && trust == XmppAxolotlSession.Trust.TRUSTED_X509) { X509Certificate x509Certificate = account.getAxolotlService().getFingerprintCertificate(fingerprint); if (x509Certificate != null) { - Log.d(Config.LOGTAG, "certificate for fingerprint " + fingerprint + " was not null"); showCertificateInformationDialog(CryptoHelper.extractCertificateInformation(x509Certificate)); + } else { + Toast.makeText(this,R.string.certificate_not_found, Toast.LENGTH_SHORT).show(); } } } diff --git a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java index d1b90ab64..5c504e040 100644 --- a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java @@ -1,21 +1,17 @@ package eu.siacs.conversations.utils; import android.os.Bundle; -import android.util.Log; import android.util.Pair; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.style.BCStyle; import org.bouncycastle.asn1.x500.style.IETFUtils; import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; -import org.bouncycastle.jce.PrincipalUtil; import java.security.MessageDigest; -import java.security.SecureRandom; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; -import java.security.cert.X509Extension; import java.text.Normalizer; import java.util.ArrayList; import java.util.Arrays; @@ -33,8 +29,6 @@ import eu.siacs.conversations.xmpp.jid.Jid; public final class CryptoHelper { public static final String FILETRANSFER = "?FILETRANSFERv1:"; private final static char[] hexArray = "0123456789abcdef".toCharArray(); - private final static char[] vowels = "aeiou".toCharArray(); - private final static char[] consonants = "bcdfghjklmnpqrstvwxyz".toCharArray(); final public static byte[] ONE = new byte[] { 0, 0, 0, 1 }; public static String bytesToHex(byte[] bytes) { @@ -68,22 +62,6 @@ public final class CryptoHelper { return result; } - public static String randomMucName(SecureRandom random) { - return randomWord(3, random) + "." + randomWord(7, random); - } - - private static String randomWord(int lenght, SecureRandom random) { - StringBuilder builder = new StringBuilder(lenght); - for (int i = 0; i < lenght; ++i) { - if (i % 2 == 0) { - builder.append(consonants[random.nextInt(consonants.length)]); - } else { - builder.append(vowels[random.nextInt(vowels.length)]); - } - } - return builder.toString(); - } - /** * Escapes usernames or passwords for SASL. */ diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 2666e98d4..1035ff833 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -567,4 +567,5 @@ Organization SHA1 (Not available) + No certificate found -- cgit v1.2.3 From be91c0741f5e6d28124981380fbff9d687f0edf2 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 26 Dec 2015 19:18:37 +0100 Subject: made text selectable again unless text contains more than 1 link fixes #1615 --- .../eu/siacs/conversations/ui/adapter/MessageAdapter.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index fd2e5751b..add0bc084 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -18,6 +18,7 @@ import android.text.style.ForegroundColorSpan; import android.text.style.RelativeSizeSpan; import android.text.style.StyleSpan; import android.util.DisplayMetrics; +import android.util.Patterns; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; @@ -32,6 +33,7 @@ import android.widget.Toast; import java.lang.ref.WeakReference; import java.util.List; import java.util.concurrent.RejectedExecutionException; +import java.util.regex.Matcher; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession; @@ -244,6 +246,7 @@ public class MessageAdapter extends ArrayAdapter { viewHolder.messageBody.setText(text); viewHolder.messageBody.setTextColor(getMessageTextColor(darkBackground, false)); viewHolder.messageBody.setTypeface(null, Typeface.ITALIC); + viewHolder.messageBody.setTextIsSelectable(false); } private void displayDecryptionFailed(ViewHolder viewHolder, boolean darkBackground) { @@ -256,6 +259,7 @@ public class MessageAdapter extends ArrayAdapter { R.string.decryption_failed)); viewHolder.messageBody.setTextColor(getMessageTextColor(darkBackground, false)); viewHolder.messageBody.setTypeface(null, Typeface.NORMAL); + viewHolder.messageBody.setTextIsSelectable(false); } private void displayHeartMessage(final ViewHolder viewHolder, final String body) { @@ -330,8 +334,15 @@ public class MessageAdapter extends ArrayAdapter { } viewHolder.messageBody.setText(span); } + int urlCount = 0; + Matcher matcher = Patterns.WEB_URL.matcher(body); + while (matcher.find()) { + urlCount++; + } + viewHolder.messageBody.setTextIsSelectable(urlCount <= 1); } else { viewHolder.messageBody.setText(""); + viewHolder.messageBody.setTextIsSelectable(false); } viewHolder.messageBody.setTextColor(this.getMessageTextColor(darkBackground, true)); viewHolder.messageBody.setLinkTextColor(this.getMessageTextColor(darkBackground, true)); -- cgit v1.2.3 From f49158a44b2d28b7704ffd49ae7f688aa6310eda Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 27 Dec 2015 17:28:42 +0100 Subject: register context menu long click listener on message text. fixes #1614 --- src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index add0bc084..a9234e1a8 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -38,7 +38,6 @@ import java.util.regex.Matcher; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession; import eu.siacs.conversations.entities.Account; -import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.Message; @@ -348,6 +347,7 @@ public class MessageAdapter extends ArrayAdapter { viewHolder.messageBody.setLinkTextColor(this.getMessageTextColor(darkBackground, true)); viewHolder.messageBody.setHighlightColor(activity.getResources().getColor(darkBackground ? R.color.grey800 : R.color.grey500)); viewHolder.messageBody.setTypeface(null, Typeface.NORMAL); + viewHolder.messageBody.setOnLongClickListener(openContextMenu); } private void displayDownloadableMessage(ViewHolder viewHolder, -- cgit v1.2.3 From bcf99db3df27c196caba823b08fb55cf196dc9c8 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 27 Dec 2015 17:29:19 +0100 Subject: fixed stuck at omemo encryption when x509 verification is being used --- .../java/eu/siacs/conversations/entities/Conversation.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index 962ad13b3..7793cc23b 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -609,15 +609,15 @@ public class Conversation extends AbstractEntity implements Blockable { public int getNextEncryption() { final AxolotlService axolotlService = getAccount().getAxolotlService(); - if (Config.X509_VERIFICATION && mode == MODE_SINGLE) { - if (axolotlService != null && axolotlService.isContactAxolotlCapable(getContact())) { - return Message.ENCRYPTION_AXOLOTL; - } else { - return Message.ENCRYPTION_NONE; - } - } int next = this.getIntAttribute(ATTRIBUTE_NEXT_ENCRYPTION, -1); if (next == -1) { + if (Config.X509_VERIFICATION && mode == MODE_SINGLE) { + if (axolotlService != null && axolotlService.isContactAxolotlCapable(getContact())) { + return Message.ENCRYPTION_AXOLOTL; + } else { + return Message.ENCRYPTION_NONE; + } + } int outgoing = this.getMostRecentlyUsedOutgoingEncryption(); if (outgoing == Message.ENCRYPTION_NONE) { next = this.getMostRecentlyUsedIncomingEncryption(); -- cgit v1.2.3 From c3bdec1ce9a28cadc2cacbe619801e94829118e8 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 27 Dec 2015 17:29:32 +0100 Subject: dedublicate bookmarks --- .../eu/siacs/conversations/services/XmppConnectionService.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 649ddacd8..28d6e3550 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -50,6 +50,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.List; @@ -992,13 +993,16 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void onIqPacketReceived(final Account account, final IqPacket packet) { if (packet.getType() == IqPacket.TYPE.RESULT) { final Element query = packet.query(); - final List bookmarks = new CopyOnWriteArrayList<>(); + final HashMap bookmarks = new HashMap<>(); final Element storage = query.findChild("storage", "storage:bookmarks"); if (storage != null) { for (final Element item : storage.getChildren()) { if (item.getName().equals("conference")) { final Bookmark bookmark = Bookmark.parse(item, account); - bookmarks.add(bookmark); + Bookmark old = bookmarks.put(bookmark.getJid(), bookmark); + if (old != null && old.getBookmarkName() != null && bookmark.getBookmarkName() == null) { + bookmark.setBookmarkName(old.getBookmarkName()); + } Conversation conversation = find(bookmark); if (conversation != null) { conversation.setBookmark(bookmark); @@ -1011,7 +1015,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } } - account.setBookmarks(bookmarks); + account.setBookmarks(new ArrayList<>(bookmarks.values())); } else { Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not fetch bookmarks"); } -- cgit v1.2.3 From 703d95fcf84d720567cefe1483695d76f32ebb34 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 27 Dec 2015 18:37:12 +0100 Subject: lower case all fingerprints. fixes #1521 --- src/main/java/eu/siacs/conversations/utils/CryptoHelper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java index 5c504e040..1ef5fb3f6 100644 --- a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java @@ -19,6 +19,7 @@ import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; +import java.util.Locale; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; @@ -94,7 +95,7 @@ public final class CryptoHelper { } else if (fingerprint.length() < 40) { return fingerprint; } - StringBuilder builder = new StringBuilder(fingerprint.replaceAll("\\s","")); + StringBuilder builder = new StringBuilder(fingerprint.toLowerCase(Locale.US).replaceAll("\\s", "")); for(int i=8;i Date: Sun, 27 Dec 2015 18:37:31 +0100 Subject: strip leading 0x05 off omemo fingerprints --- src/main/java/eu/siacs/conversations/ui/XmppActivity.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java index b372692d3..f52066cb9 100644 --- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java @@ -697,13 +697,16 @@ public abstract class XmppActivity extends Activity { trustToggle.setVisibility(View.VISIBLE); trustToggle.setOnCheckedChangeListener(onCheckedChangeListener); trustToggle.setOnClickListener(onClickListener); - view.setOnLongClickListener(new View.OnLongClickListener() { + final View.OnLongClickListener purge = new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { showPurgeKeyDialog(account, fingerprint); return true; } - }); + }; + view.setOnLongClickListener(purge); + key.setOnLongClickListener(purge); + keyType.setOnLongClickListener(purge); boolean x509 = trust == XmppAxolotlSession.Trust.TRUSTED_X509 || trust == XmppAxolotlSession.Trust.INACTIVE_TRUSTED_X509; switch (trust) { case UNTRUSTED: @@ -753,7 +756,7 @@ public abstract class XmppActivity extends Activity { keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint)); } - key.setText(CryptoHelper.prettifyFingerprint(fingerprint)); + key.setText(CryptoHelper.prettifyFingerprint(fingerprint.substring(2))); keys.addView(view); return true; } @@ -763,7 +766,7 @@ public abstract class XmppActivity extends Activity { builder.setTitle(getString(R.string.purge_key)); builder.setIconAttribute(android.R.attr.alertDialogIcon); builder.setMessage(getString(R.string.purge_key_desc_part1) - + "\n\n" + CryptoHelper.prettifyFingerprint(fingerprint) + + "\n\n" + CryptoHelper.prettifyFingerprint(fingerprint.substring(2)) + "\n\n" + getString(R.string.purge_key_desc_part2)); builder.setNegativeButton(getString(R.string.cancel), null); builder.setPositiveButton(getString(R.string.accept), -- cgit v1.2.3