From cfeb67d71da01bc95ed713d6591fa6e79fc08dd6 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 16 Oct 2015 23:48:42 +0200 Subject: introduced code to verify omemo device keys with x509 certificates. cleaned up TrustKeysActivity to automatically close if there is nothing to do --- .../crypto/axolotl/AxolotlService.java | 86 +++++++++++++++++----- 1 file changed, 67 insertions(+), 19 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java') 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 58e5a095..ca75ec99 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java @@ -1,10 +1,10 @@ package eu.siacs.conversations.crypto.axolotl; import android.security.KeyChain; -import android.security.KeyChainException; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; +import android.util.Pair; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.whispersystems.libaxolotl.AxolotlAddress; @@ -20,11 +20,9 @@ import org.whispersystems.libaxolotl.state.PreKeyRecord; import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; import org.whispersystems.libaxolotl.util.KeyHelper; -import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Security; import java.security.Signature; -import java.security.SignatureException; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.HashMap; @@ -43,12 +41,13 @@ import eu.siacs.conversations.parser.IqParser; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.SerialSingleThreadExecutor; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded; import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.stanzas.IqPacket; -public class AxolotlService { +public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { public static final String PEP_PREFIX = "eu.siacs.conversations.axolotl"; public static final String PEP_DEVICE_LIST = PEP_PREFIX + ".devicelist"; @@ -71,6 +70,15 @@ public class AxolotlService { private int numPublishTriesOnEmptyPep = 0; private boolean pepBroken = false; + @Override + public void onAdvancedStreamFeaturesAvailable(Account account) { + if (account.getXmppConnection().getFeatures().pep()) { + publishBundlesIfNeeded(true, false); + } else { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": skipping OMEMO initialization"); + } + } + private static class AxolotlAddressMap { protected Map> map; protected final Object MAP_LOCK = new Object(); @@ -402,7 +410,6 @@ public class AxolotlService { byte[] signature = verifier.sign(); IqPacket packet = mXmppConnectionService.getIqGenerator().publishVerification(signature, chain, getOwnDeviceId()); Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ": publish verification for device "+getOwnDeviceId()); - Log.d(Config.LOGTAG,"verification : "+packet.toString()); mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { @@ -565,6 +572,50 @@ public class AxolotlService { axolotlStore.setFingerprintTrust(fingerprint, trust); } + private void verifySessionWithPEP(final XmppAxolotlSession session, final IdentityKey identityKey) { + final AxolotlAddress address = session.getRemoteAddress(); + try { + IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveVerificationForDevice(Jid.fromString(address.getName()), address.getDeviceId()); + mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + Pair verification = mXmppConnectionService.getIqParser().verification(packet); + if (verification != null) { + try { + Signature verifier = Signature.getInstance("sha256WithRSA"); + verifier.initVerify(verification.first[0]); + verifier.update(identityKey.serialize()); + if (verifier.verify(verification.second)) { + try { + mXmppConnectionService.getMemorizingTrustManager().getNonInteractive().checkClientTrusted(verification.first, "RSA"); + Log.d(Config.LOGTAG, "verified session with x.509 signature"); + setFingerprintTrust(session.getFingerprint(), XmppAxolotlSession.Trust.TRUSTED); + } catch (Exception e) { + Log.d(Config.LOGTAG,"could not verify certificate"); + } + } + } catch (Exception e) { + Log.d(Config.LOGTAG, "error during verification " + e.getMessage()); + } + } else { + Log.d(Config.LOGTAG, " unable to parse verification"); + } + finishBuildingSessionsFromPEP(address); + } + }); + } catch (InvalidJidException e) { + finishBuildingSessionsFromPEP(address); + } + } + + private void finishBuildingSessionsFromPEP(final AxolotlAddress address) { + AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0); + if (!fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING) + && !fetchStatusMap.getAll(address).containsValue(FetchStatus.PENDING)) { + mXmppConnectionService.keyStatusUpdated(); + } + } + private void buildSessionFromPEP(final AxolotlAddress address) { Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building new sesstion for " + address.toString()); if (address.getDeviceId() == getOwnDeviceId()) { @@ -576,13 +627,6 @@ public class AxolotlService { Jid.fromString(address.getName()), address.getDeviceId()); Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Retrieving bundle: " + bundlesPacket); mXmppConnectionService.sendIqPacket(account, bundlesPacket, new OnIqPacketReceived() { - private void finish() { - AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0); - if (!fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING) - && !fetchStatusMap.getAll(address).containsValue(FetchStatus.PENDING)) { - mXmppConnectionService.keyStatusUpdated(); - } - } @Override public void onIqPacketReceived(Account account, IqPacket packet) { @@ -594,7 +638,7 @@ public class AxolotlService { if (preKeyBundleList.isEmpty() || bundle == null) { Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "preKey IQ packet invalid: " + packet); fetchStatusMap.put(address, FetchStatus.ERROR); - finish(); + finishBuildingSessionsFromPEP(address); return; } Random random = new Random(); @@ -602,7 +646,7 @@ public class AxolotlService { if (preKey == null) { //should never happen fetchStatusMap.put(address, FetchStatus.ERROR); - finish(); + finishBuildingSessionsFromPEP(address); return; } @@ -617,17 +661,21 @@ public class AxolotlService { XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, bundle.getIdentityKey().getFingerprint().replaceAll("\\s", "")); sessions.put(address, session); fetchStatusMap.put(address, FetchStatus.SUCCESS); + if (Config.X509_VERIFICATION) { + verifySessionWithPEP(session, bundle.getIdentityKey()); + } else { + finishBuildingSessionsFromPEP(address); + } } catch (UntrustedIdentityException | InvalidKeyException e) { Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error building session for " + address + ": " + e.getClass().getName() + ", " + e.getMessage()); fetchStatusMap.put(address, FetchStatus.ERROR); + finishBuildingSessionsFromPEP(address); } - - finish(); } else { fetchStatusMap.put(address, FetchStatus.ERROR); Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while building session:" + packet.findChild("error")); - finish(); + finishBuildingSessionsFromPEP(address); } } }); @@ -699,9 +747,9 @@ public class AxolotlService { return newSessions; } - public boolean hasPendingKeyFetches(Conversation conversation) { + public boolean hasPendingKeyFetches(Account account, Contact contact) { AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0); - AxolotlAddress foreignAddress = new AxolotlAddress(conversation.getJid().toBareJid().toString(), 0); + AxolotlAddress foreignAddress = new AxolotlAddress(contact.getJid().toBareJid().toString(), 0); return fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING) || fetchStatusMap.getAll(foreignAddress).containsValue(FetchStatus.PENDING); -- cgit v1.2.3