2015-05-29 09:17:26 +00:00
package eu.siacs.conversations.crypto.axolotl ;
2016-02-02 12:43:20 +00:00
import android.os.Bundle ;
2015-10-11 13:48:58 +00:00
import android.security.KeyChain ;
2015-07-05 20:53:34 +00:00
import android.support.annotation.NonNull ;
2015-06-29 12:22:26 +00:00
import android.support.annotation.Nullable ;
2015-05-29 09:17:26 +00:00
import android.util.Log ;
2015-10-16 21:48:42 +00:00
import android.util.Pair ;
2015-05-29 09:17:26 +00:00
2015-07-20 23:15:32 +00:00
import org.bouncycastle.jce.provider.BouncyCastleProvider ;
2017-06-18 14:35:30 +00:00
import org.whispersystems.libsignal.SignalProtocolAddress ;
import org.whispersystems.libsignal.IdentityKey ;
import org.whispersystems.libsignal.IdentityKeyPair ;
import org.whispersystems.libsignal.InvalidKeyException ;
import org.whispersystems.libsignal.InvalidKeyIdException ;
import org.whispersystems.libsignal.SessionBuilder ;
import org.whispersystems.libsignal.UntrustedIdentityException ;
import org.whispersystems.libsignal.ecc.ECPublicKey ;
import org.whispersystems.libsignal.state.PreKeyBundle ;
import org.whispersystems.libsignal.state.PreKeyRecord ;
import org.whispersystems.libsignal.state.SignedPreKeyRecord ;
import org.whispersystems.libsignal.util.KeyHelper ;
2015-05-29 09:17:26 +00:00
2015-10-11 13:48:58 +00:00
import java.security.PrivateKey ;
2015-07-20 23:15:32 +00:00
import java.security.Security ;
2015-10-11 13:48:58 +00:00
import java.security.Signature ;
import java.security.cert.X509Certificate ;
2016-11-19 12:34:54 +00:00
import java.util.ArrayList ;
2015-06-29 12:18:11 +00:00
import java.util.Arrays ;
2016-11-19 12:34:54 +00:00
import java.util.Collection ;
import java.util.Collections ;
2015-05-29 09:17:26 +00:00
import java.util.HashMap ;
2015-06-25 14:56:34 +00:00
import java.util.HashSet ;
2017-06-29 12:17:45 +00:00
import java.util.Iterator ;
2015-05-29 09:17:26 +00:00
import java.util.List ;
import java.util.Map ;
2015-06-25 14:56:34 +00:00
import java.util.Random ;
import java.util.Set ;
2016-11-19 20:39:16 +00:00
import java.util.concurrent.atomic.AtomicBoolean ;
2015-05-29 09:17:26 +00:00
import eu.siacs.conversations.Config ;
import eu.siacs.conversations.entities.Account ;
2015-06-25 14:56:34 +00:00
import eu.siacs.conversations.entities.Contact ;
2015-05-29 09:17:26 +00:00
import eu.siacs.conversations.entities.Conversation ;
2015-06-29 12:22:26 +00:00
import eu.siacs.conversations.entities.Message ;
2015-06-25 14:58:24 +00:00
import eu.siacs.conversations.parser.IqParser ;
2015-05-29 09:17:26 +00:00
import eu.siacs.conversations.services.XmppConnectionService ;
2016-02-02 12:43:20 +00:00
import eu.siacs.conversations.utils.CryptoHelper ;
2015-06-29 12:22:26 +00:00
import eu.siacs.conversations.utils.SerialSingleThreadExecutor ;
2015-06-25 14:58:24 +00:00
import eu.siacs.conversations.xml.Element ;
2017-07-12 15:49:49 +00:00
import eu.siacs.conversations.xml.Namespace ;
2015-10-16 21:48:42 +00:00
import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded ;
2015-06-25 14:58:24 +00:00
import eu.siacs.conversations.xmpp.OnIqPacketReceived ;
2015-05-29 09:17:26 +00:00
import eu.siacs.conversations.xmpp.jid.InvalidJidException ;
import eu.siacs.conversations.xmpp.jid.Jid ;
2017-06-28 08:21:06 +00:00
import eu.siacs.conversations.xmpp.pep.PublishOptions ;
2015-06-25 14:58:24 +00:00
import eu.siacs.conversations.xmpp.stanzas.IqPacket ;
2015-05-29 09:17:26 +00:00
2015-10-16 21:48:42 +00:00
public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
2015-05-29 09:17:26 +00:00
2015-06-26 13:41:02 +00:00
public static final String PEP_PREFIX = " eu.siacs.conversations.axolotl " ;
public static final String PEP_DEVICE_LIST = PEP_PREFIX + " .devicelist " ;
2016-05-12 12:20:11 +00:00
public static final String PEP_DEVICE_LIST_NOTIFY = PEP_DEVICE_LIST + " +notify " ;
2015-06-29 12:18:11 +00:00
public static final String PEP_BUNDLES = PEP_PREFIX + " .bundles " ;
2015-10-11 13:48:58 +00:00
public static final String PEP_VERIFICATION = PEP_PREFIX + " .verification " ;
2015-06-29 12:18:11 +00:00
2015-07-08 15:44:24 +00:00
public static final String LOGPREFIX = " AxolotlService " ;
2015-08-07 10:28:45 +00:00
public static final int NUM_KEYS_TO_PUBLISH = 100 ;
2015-08-25 16:41:30 +00:00
public static final int publishTriesThreshold = 3 ;
2015-06-26 13:41:02 +00:00
private final Account account ;
private final XmppConnectionService mXmppConnectionService ;
private final SQLiteAxolotlStore axolotlStore ;
private final SessionMap sessions ;
2015-06-29 12:18:11 +00:00
private final Map < Jid , Set < Integer > > deviceIds ;
2015-07-20 21:13:28 +00:00
private final Map < String , XmppAxolotlMessage > messageCache ;
2015-06-29 12:22:26 +00:00
private final FetchStatusMap fetchStatusMap ;
2017-06-29 12:17:45 +00:00
private final HashMap < Jid , List < OnDeviceIdsFetched > > fetchDeviceIdsMap = new HashMap < > ( ) ;
2015-06-29 12:22:26 +00:00
private final SerialSingleThreadExecutor executor ;
2015-08-25 16:41:30 +00:00
private int numPublishTriesOnEmptyPep = 0 ;
private boolean pepBroken = false ;
2015-06-26 13:41:02 +00:00
2016-11-19 20:39:16 +00:00
private AtomicBoolean ownPushPending = new AtomicBoolean ( false ) ;
2017-07-12 20:16:31 +00:00
private AtomicBoolean changeAccessMode = new AtomicBoolean ( false ) ;
2016-11-19 20:39:16 +00:00
2015-10-16 21:48:42 +00:00
@Override
public void onAdvancedStreamFeaturesAvailable ( Account account ) {
2016-05-25 19:55:01 +00:00
if ( Config . supportOmemo ( )
& & account . getXmppConnection ( ) ! = null
& & account . getXmppConnection ( ) . getFeatures ( ) . pep ( ) ) {
2015-10-16 21:48:42 +00:00
publishBundlesIfNeeded ( true , false ) ;
} else {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : skipping OMEMO initialization " ) ;
}
}
2016-02-29 12:18:07 +00:00
public boolean fetchMapHasErrors ( List < Jid > jids ) {
for ( Jid jid : jids ) {
if ( deviceIds . get ( jid ) ! = null ) {
for ( Integer foreignId : this . deviceIds . get ( jid ) ) {
2017-06-18 14:35:30 +00:00
SignalProtocolAddress address = new SignalProtocolAddress ( jid . toPreppedString ( ) , foreignId ) ;
if ( fetchStatusMap . getAll ( address . getName ( ) ) . containsValue ( FetchStatus . ERROR ) ) {
2016-02-29 12:18:07 +00:00
return true ;
}
2016-01-23 10:39:02 +00:00
}
}
}
return false ;
}
2016-11-17 19:09:42 +00:00
public void preVerifyFingerprint ( Contact contact , String fingerprint ) {
axolotlStore . preVerifyFingerprint ( contact . getAccount ( ) , contact . getJid ( ) . toBareJid ( ) . toPreppedString ( ) , fingerprint ) ;
}
2016-11-17 21:28:45 +00:00
public void preVerifyFingerprint ( Account account , String fingerprint ) {
axolotlStore . preVerifyFingerprint ( account , account . getJid ( ) . toBareJid ( ) . toPreppedString ( ) , fingerprint ) ;
}
2016-11-23 09:42:27 +00:00
public boolean hasVerifiedKeys ( String name ) {
2017-06-18 14:35:30 +00:00
for ( XmppAxolotlSession session : this . sessions . getAll ( name ) . values ( ) ) {
2016-11-23 09:42:27 +00:00
if ( session . getTrust ( ) . isVerified ( ) ) {
return true ;
}
}
return false ;
}
2015-06-26 13:41:02 +00:00
private static class AxolotlAddressMap < T > {
2015-06-29 11:55:45 +00:00
protected Map < String , Map < Integer , T > > map ;
2015-06-26 13:41:02 +00:00
protected final Object MAP_LOCK = new Object ( ) ;
public AxolotlAddressMap ( ) {
this . map = new HashMap < > ( ) ;
}
2017-06-18 14:35:30 +00:00
public void put ( SignalProtocolAddress address , T value ) {
2015-06-26 13:41:02 +00:00
synchronized ( MAP_LOCK ) {
Map < Integer , T > devices = map . get ( address . getName ( ) ) ;
if ( devices = = null ) {
devices = new HashMap < > ( ) ;
map . put ( address . getName ( ) , devices ) ;
}
devices . put ( address . getDeviceId ( ) , value ) ;
}
}
2017-06-18 14:35:30 +00:00
public T get ( SignalProtocolAddress address ) {
2015-06-26 13:41:02 +00:00
synchronized ( MAP_LOCK ) {
Map < Integer , T > devices = map . get ( address . getName ( ) ) ;
2015-06-29 11:55:45 +00:00
if ( devices = = null ) {
2015-06-26 13:41:02 +00:00
return null ;
}
return devices . get ( address . getDeviceId ( ) ) ;
}
}
2017-06-18 14:35:30 +00:00
public Map < Integer , T > getAll ( String name ) {
2015-06-26 13:41:02 +00:00
synchronized ( MAP_LOCK ) {
2017-06-18 14:35:30 +00:00
Map < Integer , T > devices = map . get ( name ) ;
2015-06-29 11:55:45 +00:00
if ( devices = = null ) {
2015-06-26 13:41:02 +00:00
return new HashMap < > ( ) ;
}
return devices ;
}
}
2017-06-18 14:35:30 +00:00
public boolean hasAny ( SignalProtocolAddress address ) {
2015-06-26 13:41:02 +00:00
synchronized ( MAP_LOCK ) {
Map < Integer , T > devices = map . get ( address . getName ( ) ) ;
return devices ! = null & & ! devices . isEmpty ( ) ;
}
}
2015-07-10 00:18:01 +00:00
public void clear ( ) {
map . clear ( ) ;
}
2015-06-26 13:41:02 +00:00
}
private static class SessionMap extends AxolotlAddressMap < XmppAxolotlSession > {
2015-07-10 00:36:29 +00:00
private final XmppConnectionService xmppConnectionService ;
private final Account account ;
2015-06-26 13:41:02 +00:00
2015-07-10 00:36:29 +00:00
public SessionMap ( XmppConnectionService service , SQLiteAxolotlStore store , Account account ) {
2015-06-26 13:41:02 +00:00
super ( ) ;
2015-07-10 00:36:29 +00:00
this . xmppConnectionService = service ;
this . account = account ;
this . fillMap ( store ) ;
2015-06-26 13:41:02 +00:00
}
2015-07-21 11:51:15 +00:00
private void putDevicesForJid ( String bareJid , List < Integer > deviceIds , SQLiteAxolotlStore store ) {
for ( Integer deviceId : deviceIds ) {
2017-06-18 14:35:30 +00:00
SignalProtocolAddress axolotlAddress = new SignalProtocolAddress ( bareJid , deviceId ) ;
2015-12-19 14:44:11 +00:00
IdentityKey identityKey = store . loadSession ( axolotlAddress ) . getSessionState ( ) . getRemoteIdentityKey ( ) ;
2016-02-02 12:43:20 +00:00
if ( Config . X509_VERIFICATION ) {
2017-06-25 16:18:13 +00:00
X509Certificate certificate = store . getFingerprintCertificate ( CryptoHelper . bytesToHex ( identityKey . getPublicKey ( ) . serialize ( ) ) ) ;
2016-02-02 12:43:20 +00:00
if ( certificate ! = null ) {
Bundle information = CryptoHelper . extractCertificateInformation ( certificate ) ;
try {
final String cn = information . getString ( " subject_cn " ) ;
final Jid jid = Jid . fromString ( bareJid ) ;
Log . d ( Config . LOGTAG , " setting common name for " + jid + " to " + cn ) ;
account . getRoster ( ) . getContact ( jid ) . setCommonName ( cn ) ;
} catch ( final InvalidJidException ignored ) {
//ignored
}
}
}
2015-12-19 14:44:11 +00:00
this . put ( axolotlAddress , new XmppAxolotlSession ( account , store , axolotlAddress , identityKey ) ) ;
2015-07-21 11:51:15 +00:00
}
}
2015-07-10 00:36:29 +00:00
private void fillMap ( SQLiteAxolotlStore store ) {
2016-10-20 15:31:46 +00:00
List < Integer > deviceIds = store . getSubDeviceSessions ( account . getJid ( ) . toBareJid ( ) . toPreppedString ( ) ) ;
putDevicesForJid ( account . getJid ( ) . toBareJid ( ) . toPreppedString ( ) , deviceIds , store ) ;
2017-06-29 12:17:45 +00:00
for ( String address : store . getKnownAddresses ( ) ) {
2015-07-21 11:51:15 +00:00
deviceIds = store . getSubDeviceSessions ( address ) ;
putDevicesForJid ( address , deviceIds , store ) ;
2015-06-26 13:41:02 +00:00
}
}
2015-07-10 00:36:29 +00:00
@Override
2017-06-18 14:35:30 +00:00
public void put ( SignalProtocolAddress address , XmppAxolotlSession value ) {
2015-07-10 00:36:29 +00:00
super . put ( address , value ) ;
2015-07-31 21:28:09 +00:00
value . setNotFresh ( ) ;
2015-07-10 00:36:29 +00:00
}
2015-07-31 21:28:09 +00:00
public void put ( XmppAxolotlSession session ) {
this . put ( session . getRemoteAddress ( ) , session ) ;
}
2015-06-26 13:41:02 +00:00
}
2015-10-17 12:09:26 +00:00
public enum FetchStatus {
2015-06-29 12:22:26 +00:00
PENDING ,
SUCCESS ,
2015-10-17 12:09:26 +00:00
SUCCESS_VERIFIED ,
2015-10-17 13:51:21 +00:00
TIMEOUT ,
2016-11-23 09:42:27 +00:00
SUCCESS_TRUSTED ,
2015-06-29 12:22:26 +00:00
ERROR
}
private static class FetchStatusMap extends AxolotlAddressMap < FetchStatus > {
2015-06-26 13:41:02 +00:00
2016-05-19 08:47:27 +00:00
public void clearErrorFor ( Jid jid ) {
synchronized ( MAP_LOCK ) {
2016-10-20 15:31:46 +00:00
Map < Integer , FetchStatus > devices = this . map . get ( jid . toBareJid ( ) . toPreppedString ( ) ) ;
2016-05-19 08:47:27 +00:00
if ( devices = = null ) {
return ;
}
for ( Map . Entry < Integer , FetchStatus > entry : devices . entrySet ( ) ) {
if ( entry . getValue ( ) = = FetchStatus . ERROR ) {
Log . d ( Config . LOGTAG , " resetting error for " + jid . toBareJid ( ) + " ( " + entry . getKey ( ) + " ) " ) ;
entry . setValue ( FetchStatus . TIMEOUT ) ;
}
}
}
}
2015-06-26 13:41:02 +00:00
}
2015-07-31 16:05:32 +00:00
2015-07-08 15:44:24 +00:00
public static String getLogprefix ( Account account ) {
2015-07-31 16:05:32 +00:00
return LOGPREFIX + " ( " + account . getJid ( ) . toBareJid ( ) . toString ( ) + " ): " ;
2015-07-08 15:44:24 +00:00
}
2015-06-26 13:41:02 +00:00
public AxolotlService ( Account account , XmppConnectionService connectionService ) {
2015-07-20 23:15:32 +00:00
if ( Security . getProvider ( " BC " ) = = null ) {
Security . addProvider ( new BouncyCastleProvider ( ) ) ;
}
2015-06-26 13:41:02 +00:00
this . mXmppConnectionService = connectionService ;
this . account = account ;
this . axolotlStore = new SQLiteAxolotlStore ( this . account , this . mXmppConnectionService ) ;
2015-06-29 12:18:11 +00:00
this . deviceIds = new HashMap < > ( ) ;
2015-07-03 11:31:14 +00:00
this . messageCache = new HashMap < > ( ) ;
2015-07-10 00:36:29 +00:00
this . sessions = new SessionMap ( mXmppConnectionService , axolotlStore , account ) ;
2015-06-29 12:22:26 +00:00
this . fetchStatusMap = new FetchStatusMap ( ) ;
this . executor = new SerialSingleThreadExecutor ( ) ;
2015-06-26 13:41:02 +00:00
}
2015-09-06 13:08:42 +00:00
public String getOwnFingerprint ( ) {
2017-06-18 14:35:30 +00:00
return CryptoHelper . bytesToHex ( axolotlStore . getIdentityKeyPair ( ) . getPublicKey ( ) . serialize ( ) ) ;
2015-07-07 17:36:22 +00:00
}
2016-11-14 21:27:41 +00:00
public Set < IdentityKey > getKeysWithTrust ( FingerprintStatus status ) {
return axolotlStore . getContactKeysWithTrust ( account . getJid ( ) . toBareJid ( ) . toPreppedString ( ) , status ) ;
2015-07-19 16:36:28 +00:00
}
2016-11-14 21:27:41 +00:00
public Set < IdentityKey > getKeysWithTrust ( FingerprintStatus status , Jid jid ) {
return axolotlStore . getContactKeysWithTrust ( jid . toBareJid ( ) . toPreppedString ( ) , status ) ;
2016-02-29 12:18:07 +00:00
}
2016-11-14 21:27:41 +00:00
public Set < IdentityKey > getKeysWithTrust ( FingerprintStatus status , List < Jid > jids ) {
2016-02-29 12:18:07 +00:00
Set < IdentityKey > keys = new HashSet < > ( ) ;
for ( Jid jid : jids ) {
2016-11-14 21:27:41 +00:00
keys . addAll ( axolotlStore . getContactKeysWithTrust ( jid . toPreppedString ( ) , status ) ) ;
2016-02-29 12:18:07 +00:00
}
return keys ;
2015-07-19 16:36:28 +00:00
}
2016-02-29 12:18:07 +00:00
public long getNumTrustedKeys ( Jid jid ) {
2016-10-20 15:31:46 +00:00
return axolotlStore . getContactNumTrustedKeys ( jid . toBareJid ( ) . toPreppedString ( ) ) ;
2016-02-29 12:18:07 +00:00
}
public boolean anyTargetHasNoTrustedKeys ( List < Jid > jids ) {
for ( Jid jid : jids ) {
2016-10-20 15:31:46 +00:00
if ( axolotlStore . getContactNumTrustedKeys ( jid . toBareJid ( ) . toPreppedString ( ) ) = = 0 ) {
2016-02-29 12:18:07 +00:00
return true ;
}
}
return false ;
2015-07-20 12:56:41 +00:00
}
2017-06-18 14:35:30 +00:00
private SignalProtocolAddress getAddressForJid ( Jid jid ) {
return new SignalProtocolAddress ( jid . toPreppedString ( ) , 0 ) ;
2015-06-26 13:41:02 +00:00
}
2016-11-19 12:34:54 +00:00
public Collection < XmppAxolotlSession > findOwnSessions ( ) {
2017-06-18 14:35:30 +00:00
SignalProtocolAddress ownAddress = getAddressForJid ( account . getJid ( ) . toBareJid ( ) ) ;
ArrayList < XmppAxolotlSession > s = new ArrayList < > ( this . sessions . getAll ( ownAddress . getName ( ) ) . values ( ) ) ;
2016-11-19 12:34:54 +00:00
Collections . sort ( s ) ;
return s ;
2015-06-26 13:41:02 +00:00
}
2016-11-17 21:28:45 +00:00
2016-11-19 12:34:54 +00:00
public Collection < XmppAxolotlSession > findSessionsForContact ( Contact contact ) {
2017-06-18 14:35:30 +00:00
SignalProtocolAddress contactAddress = getAddressForJid ( contact . getJid ( ) ) ;
ArrayList < XmppAxolotlSession > s = new ArrayList < > ( this . sessions . getAll ( contactAddress . getName ( ) ) . values ( ) ) ;
2016-11-19 12:34:54 +00:00
Collections . sort ( s ) ;
return s ;
2015-06-26 13:41:02 +00:00
}
2016-02-29 12:18:07 +00:00
private Set < XmppAxolotlSession > findSessionsForConversation ( Conversation conversation ) {
HashSet < XmppAxolotlSession > sessions = new HashSet < > ( ) ;
2016-03-01 10:26:59 +00:00
for ( Jid jid : conversation . getAcceptedCryptoTargets ( ) ) {
2017-06-18 14:35:30 +00:00
sessions . addAll ( this . sessions . getAll ( getAddressForJid ( jid ) . getName ( ) ) . values ( ) ) ;
2016-02-29 12:18:07 +00:00
}
return sessions ;
}
private boolean hasAny ( Jid jid ) {
return sessions . hasAny ( getAddressForJid ( jid ) ) ;
2015-06-26 13:41:02 +00:00
}
2015-09-06 17:40:28 +00:00
public boolean isPepBroken ( ) {
return this . pepBroken ;
}
2016-04-05 11:31:03 +00:00
public void resetBrokenness ( ) {
this . pepBroken = false ;
numPublishTriesOnEmptyPep = 0 ;
}
2016-05-19 08:47:27 +00:00
public void clearErrorsInFetchStatusMap ( Jid jid ) {
fetchStatusMap . clearErrorFor ( jid ) ;
}
2015-10-11 13:48:58 +00:00
public void regenerateKeys ( boolean wipeOther ) {
2015-07-07 17:36:22 +00:00
axolotlStore . regenerate ( ) ;
2015-07-10 00:18:01 +00:00
sessions . clear ( ) ;
fetchStatusMap . clear ( ) ;
2017-06-29 12:17:45 +00:00
fetchDeviceIdsMap . clear ( ) ;
2015-10-11 13:48:58 +00:00
publishBundlesIfNeeded ( true , wipeOther ) ;
2015-07-07 17:36:22 +00:00
}
2015-06-26 13:41:02 +00:00
public int getOwnDeviceId ( ) {
2015-07-28 20:00:54 +00:00
return axolotlStore . getLocalRegistrationId ( ) ;
2015-06-26 13:41:02 +00:00
}
2017-06-18 14:35:30 +00:00
public SignalProtocolAddress getOwnAxolotlAddress ( ) {
return new SignalProtocolAddress ( account . getJid ( ) . toBareJid ( ) . toPreppedString ( ) , getOwnDeviceId ( ) ) ;
2016-12-28 21:15:24 +00:00
}
2015-07-07 17:36:22 +00:00
public Set < Integer > getOwnDeviceIds ( ) {
return this . deviceIds . get ( account . getJid ( ) . toBareJid ( ) ) ;
}
2015-07-05 20:53:34 +00:00
public void registerDevices ( final Jid jid , @NonNull final Set < Integer > deviceIds ) {
2016-11-19 20:39:16 +00:00
boolean me = jid . toBareJid ( ) . equals ( account . getJid ( ) . toBareJid ( ) ) ;
if ( me & & ownPushPending . getAndSet ( false ) ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : ignoring own device update because of pending push " ) ;
return ;
}
boolean needsPublishing = me & & ! deviceIds . contains ( getOwnDeviceId ( ) ) ;
if ( me ) {
deviceIds . remove ( getOwnDeviceId ( ) ) ;
2015-07-07 17:32:52 +00:00
}
2016-10-20 15:31:46 +00:00
Set < Integer > expiredDevices = new HashSet < > ( axolotlStore . getSubDeviceSessions ( jid . toBareJid ( ) . toPreppedString ( ) ) ) ;
2015-07-21 12:18:16 +00:00
expiredDevices . removeAll ( deviceIds ) ;
2016-11-14 21:27:41 +00:00
for ( Integer deviceId : expiredDevices ) {
2017-06-18 14:35:30 +00:00
SignalProtocolAddress address = new SignalProtocolAddress ( jid . toBareJid ( ) . toPreppedString ( ) , deviceId ) ;
2016-11-14 21:27:41 +00:00
XmppAxolotlSession session = sessions . get ( address ) ;
if ( session ! = null & & session . getFingerprint ( ) ! = null ) {
if ( session . getTrust ( ) . isActive ( ) ) {
session . setTrust ( session . getTrust ( ) . toInactive ( ) ) ;
}
}
}
2015-07-21 12:18:16 +00:00
Set < Integer > newDevices = new HashSet < > ( deviceIds ) ;
2016-11-14 21:27:41 +00:00
for ( Integer deviceId : newDevices ) {
2017-06-18 14:35:30 +00:00
SignalProtocolAddress address = new SignalProtocolAddress ( jid . toBareJid ( ) . toPreppedString ( ) , deviceId ) ;
2016-11-14 21:27:41 +00:00
XmppAxolotlSession session = sessions . get ( address ) ;
if ( session ! = null & & session . getFingerprint ( ) ! = null ) {
if ( ! session . getTrust ( ) . isActive ( ) ) {
2016-12-16 16:12:26 +00:00
Log . d ( Config . LOGTAG , " reactivating device with fingerprint " + session . getFingerprint ( ) ) ;
2016-11-14 21:27:41 +00:00
session . setTrust ( session . getTrust ( ) . toActive ( ) ) ;
}
}
}
2016-11-19 20:39:16 +00:00
if ( me ) {
if ( Config . OMEMO_AUTO_EXPIRY ! = 0 ) {
needsPublishing | = deviceIds . removeAll ( getExpiredDevices ( ) ) ;
}
2017-07-12 20:16:31 +00:00
needsPublishing | = this . changeAccessMode . get ( ) ;
2016-11-19 20:39:16 +00:00
for ( Integer deviceId : deviceIds ) {
2017-06-18 14:35:30 +00:00
SignalProtocolAddress ownDeviceAddress = new SignalProtocolAddress ( jid . toBareJid ( ) . toPreppedString ( ) , deviceId ) ;
2016-11-19 20:39:16 +00:00
if ( sessions . get ( ownDeviceAddress ) = = null ) {
2016-12-16 16:12:26 +00:00
FetchStatus status = fetchStatusMap . get ( ownDeviceAddress ) ;
if ( status = = null | | status = = FetchStatus . TIMEOUT ) {
fetchStatusMap . put ( ownDeviceAddress , FetchStatus . PENDING ) ;
this . buildSessionFromPEP ( ownDeviceAddress ) ;
}
2016-11-19 20:39:16 +00:00
}
}
if ( needsPublishing ) {
publishOwnDeviceId ( deviceIds ) ;
}
}
2015-06-29 12:18:11 +00:00
this . deviceIds . put ( jid , deviceIds ) ;
2016-11-24 10:28:04 +00:00
mXmppConnectionService . updateConversationUi ( ) ; //update the lock icon
2015-10-17 12:09:26 +00:00
mXmppConnectionService . keyStatusUpdated ( null ) ;
2015-06-26 13:41:02 +00:00
}
2015-07-07 17:36:22 +00:00
public void wipeOtherPepDevices ( ) {
2015-08-25 16:41:30 +00:00
if ( pepBroken ) {
Log . d ( Config . LOGTAG , getLogprefix ( account ) + " wipeOtherPepDevices called, but PEP is broken. Ignoring... " ) ;
return ;
}
2015-07-07 17:36:22 +00:00
Set < Integer > deviceIds = new HashSet < > ( ) ;
deviceIds . add ( getOwnDeviceId ( ) ) ;
2017-07-12 15:49:49 +00:00
publishDeviceIdsAndRefineAccessModel ( deviceIds ) ;
2015-07-07 17:36:22 +00:00
}
2017-01-12 14:59:13 +00:00
public void distrustFingerprint ( final String fingerprint ) {
final String fp = fingerprint . replaceAll ( " \\ s " , " " ) ;
final FingerprintStatus fingerprintStatus = axolotlStore . getFingerprintStatus ( fp ) ;
axolotlStore . setFingerprintStatus ( fp , fingerprintStatus . toUntrusted ( ) ) ;
2015-07-20 20:18:24 +00:00
}
2015-06-26 13:41:02 +00:00
public void publishOwnDeviceIdIfNeeded ( ) {
2015-08-25 16:41:30 +00:00
if ( pepBroken ) {
Log . d ( Config . LOGTAG , getLogprefix ( account ) + " publishOwnDeviceIdIfNeeded called, but PEP is broken. Ignoring... " ) ;
return ;
}
2015-06-26 13:41:02 +00:00
IqPacket packet = mXmppConnectionService . getIqGenerator ( ) . retrieveDeviceIds ( account . getJid ( ) . toBareJid ( ) ) ;
mXmppConnectionService . sendIqPacket ( account , packet , new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
2015-08-26 18:51:18 +00:00
if ( packet . getType ( ) = = IqPacket . TYPE . TIMEOUT ) {
Log . d ( Config . LOGTAG , getLogprefix ( account ) + " Timeout received while retrieving own Device Ids. " ) ;
} else {
2015-08-23 11:23:10 +00:00
Element item = mXmppConnectionService . getIqParser ( ) . getItem ( packet ) ;
Set < Integer > deviceIds = mXmppConnectionService . getIqParser ( ) . deviceIds ( item ) ;
2016-12-28 21:15:24 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : retrieved own device list: " + deviceIds ) ;
2016-11-19 20:39:16 +00:00
registerDevices ( account . getJid ( ) . toBareJid ( ) , deviceIds ) ;
2015-06-26 13:41:02 +00:00
}
}
} ) ;
}
2016-11-19 20:39:16 +00:00
private Set < Integer > getExpiredDevices ( ) {
Set < Integer > devices = new HashSet < > ( ) ;
for ( XmppAxolotlSession session : findOwnSessions ( ) ) {
if ( session . getTrust ( ) . isActive ( ) ) {
long diff = System . currentTimeMillis ( ) - session . getTrust ( ) . getLastActivation ( ) ;
if ( diff > Config . OMEMO_AUTO_EXPIRY ) {
long lastMessageDiff = System . currentTimeMillis ( ) - mXmppConnectionService . databaseBackend . getLastTimeFingerprintUsed ( account , session . getFingerprint ( ) ) ;
2016-12-16 16:12:26 +00:00
long hours = Math . round ( lastMessageDiff / ( 1000 * 60 . 0 * 60 . 0 ) ) ;
2016-11-19 20:39:16 +00:00
if ( lastMessageDiff > Config . OMEMO_AUTO_EXPIRY ) {
devices . add ( session . getRemoteAddress ( ) . getDeviceId ( ) ) ;
session . setTrust ( session . getTrust ( ) . toInactive ( ) ) ;
2016-12-16 16:12:26 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : added own device " + session . getFingerprint ( ) + " to list of expired devices. Last message received " + hours + " hours ago " ) ;
} else {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : own device " + session . getFingerprint ( ) + " was active " + hours + " hours ago " ) ;
2016-11-19 20:39:16 +00:00
}
}
}
}
return devices ;
}
2015-08-25 10:17:09 +00:00
public void publishOwnDeviceId ( Set < Integer > deviceIds ) {
2015-09-05 15:25:46 +00:00
Set < Integer > deviceIdsCopy = new HashSet < > ( deviceIds ) ;
2016-12-16 16:12:26 +00:00
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " publishing own device ids " ) ;
if ( deviceIdsCopy . isEmpty ( ) ) {
if ( numPublishTriesOnEmptyPep > = publishTriesThreshold ) {
Log . w ( Config . LOGTAG , getLogprefix ( account ) + " Own device publish attempt threshold exceeded, aborting... " ) ;
pepBroken = true ;
return ;
2015-08-25 16:41:30 +00:00
} else {
2016-12-16 16:12:26 +00:00
numPublishTriesOnEmptyPep + + ;
Log . w ( Config . LOGTAG , getLogprefix ( account ) + " Own device list empty, attempting to publish (try " + numPublishTriesOnEmptyPep + " ) " ) ;
2015-08-25 10:17:09 +00:00
}
2016-12-16 16:12:26 +00:00
} else {
numPublishTriesOnEmptyPep = 0 ;
2015-08-25 16:41:30 +00:00
}
2016-12-16 16:12:26 +00:00
deviceIdsCopy . add ( getOwnDeviceId ( ) ) ;
2017-07-12 15:49:49 +00:00
publishDeviceIdsAndRefineAccessModel ( deviceIdsCopy ) ;
}
private void publishDeviceIdsAndRefineAccessModel ( Set < Integer > ids ) {
publishDeviceIdsAndRefineAccessModel ( ids , true ) ;
}
private void publishDeviceIdsAndRefineAccessModel ( final Set < Integer > ids , final boolean firstAttempt ) {
final Bundle publishOptions = account . getXmppConnection ( ) . getFeatures ( ) . pepPublishOptions ( ) ? PublishOptions . openAccess ( ) : null ;
IqPacket publish = mXmppConnectionService . getIqGenerator ( ) . publishDeviceIds ( ids , publishOptions ) ;
2016-12-16 16:12:26 +00:00
ownPushPending . set ( true ) ;
mXmppConnectionService . sendIqPacket ( account , publish , new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
2017-07-12 15:49:49 +00:00
Element error = packet . getType ( ) = = IqPacket . TYPE . ERROR ? packet . findChild ( " error " ) : null ;
if ( firstAttempt & & error ! = null & & error . hasChild ( " precondition-not-met " , Namespace . PUBSUB_ERROR ) ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : precondition wasn't met for device list. pushing node configuration " ) ;
mXmppConnectionService . pushNodeConfiguration ( account , AxolotlService . PEP_DEVICE_LIST , publishOptions , new XmppConnectionService . OnConfigurationPushed ( ) {
@Override
public void onPushSucceeded ( ) {
publishDeviceIdsAndRefineAccessModel ( ids , false ) ;
}
@Override
public void onPushFailed ( ) {
publishDeviceIdsAndRefineAccessModel ( ids , false ) ;
}
} ) ;
} else {
2017-07-12 20:16:31 +00:00
if ( AxolotlService . this . changeAccessMode . compareAndSet ( true , false ) ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : done changing access mode " ) ;
account . setOption ( Account . OPTION_REQURIES_ACCESS_MODE_CHANGE , false ) ;
mXmppConnectionService . databaseBackend . updateAccount ( account ) ;
}
2017-07-12 15:49:49 +00:00
ownPushPending . set ( false ) ;
if ( packet . getType ( ) = = IqPacket . TYPE . ERROR ) {
pepBroken = true ;
Log . d ( Config . LOGTAG , getLogprefix ( account ) + " Error received while publishing own device id " + packet . findChild ( " error " ) ) ;
}
2016-12-16 16:12:26 +00:00
}
}
} ) ;
2015-08-25 10:17:09 +00:00
}
2015-10-11 13:48:58 +00:00
public void publishDeviceVerificationAndBundle ( final SignedPreKeyRecord signedPreKeyRecord ,
final Set < PreKeyRecord > preKeyRecords ,
final boolean announceAfter ,
final boolean wipe ) {
try {
IdentityKey axolotlPublicKey = axolotlStore . getIdentityKeyPair ( ) . getPublicKey ( ) ;
PrivateKey x509PrivateKey = KeyChain . getPrivateKey ( mXmppConnectionService , account . getPrivateKeyAlias ( ) ) ;
X509Certificate [ ] chain = KeyChain . getCertificateChain ( mXmppConnectionService , account . getPrivateKeyAlias ( ) ) ;
Signature verifier = Signature . getInstance ( " sha256WithRSA " ) ;
verifier . initSign ( x509PrivateKey , mXmppConnectionService . getRNG ( ) ) ;
verifier . update ( axolotlPublicKey . serialize ( ) ) ;
byte [ ] signature = verifier . sign ( ) ;
IqPacket packet = mXmppConnectionService . getIqGenerator ( ) . publishVerification ( signature , chain , getOwnDeviceId ( ) ) ;
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " : publish verification for device " + getOwnDeviceId ( ) ) ;
mXmppConnectionService . sendIqPacket ( account , packet , new OnIqPacketReceived ( ) {
@Override
2017-02-24 18:58:46 +00:00
public void onIqPacketReceived ( final Account account , IqPacket packet ) {
String node = AxolotlService . PEP_VERIFICATION + " : " + getOwnDeviceId ( ) ;
2017-06-28 08:21:06 +00:00
mXmppConnectionService . pushNodeConfiguration ( account , node , PublishOptions . openAccess ( ) , new XmppConnectionService . OnConfigurationPushed ( ) {
2017-02-24 18:58:46 +00:00
@Override
public void onPushSucceeded ( ) {
Log . d ( Config . LOGTAG , getLogprefix ( account ) + " configured verification node to be world readable " ) ;
publishDeviceBundle ( signedPreKeyRecord , preKeyRecords , announceAfter , wipe ) ;
}
@Override
public void onPushFailed ( ) {
Log . d ( Config . LOGTAG , getLogprefix ( account ) + " unable to set access model on verification node " ) ;
publishDeviceBundle ( signedPreKeyRecord , preKeyRecords , announceAfter , wipe ) ;
}
} ) ;
2015-10-11 13:48:58 +00:00
}
} ) ;
2015-10-12 10:36:54 +00:00
} catch ( Exception e ) {
2015-10-11 13:48:58 +00:00
e . printStackTrace ( ) ;
}
}
public void publishBundlesIfNeeded ( final boolean announce , final boolean wipe ) {
2015-08-25 22:27:39 +00:00
if ( pepBroken ) {
2015-08-25 16:41:30 +00:00
Log . d ( Config . LOGTAG , getLogprefix ( account ) + " publishBundlesIfNeeded called, but PEP is broken. Ignoring... " ) ;
return ;
}
2017-07-12 20:16:31 +00:00
this . changeAccessMode . set ( account . isOptionSet ( Account . OPTION_REQURIES_ACCESS_MODE_CHANGE ) & & account . getXmppConnection ( ) . getFeatures ( ) . pepPublishOptions ( ) ) ;
if ( this . changeAccessMode . get ( ) ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : server gained publish-options capabilities. changing access model " ) ;
}
2015-07-10 00:18:01 +00:00
IqPacket packet = mXmppConnectionService . getIqGenerator ( ) . retrieveBundlesForDevice ( account . getJid ( ) . toBareJid ( ) , getOwnDeviceId ( ) ) ;
2015-06-26 13:41:02 +00:00
mXmppConnectionService . sendIqPacket ( account , packet , new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
2015-09-17 12:18:06 +00:00
if ( packet . getType ( ) = = IqPacket . TYPE . TIMEOUT ) {
return ; //ignore timeout. do nothing
}
if ( packet . getType ( ) = = IqPacket . TYPE . ERROR ) {
Element error = packet . findChild ( " error " ) ;
if ( error = = null | | ! error . hasChild ( " item-not-found " ) ) {
pepBroken = true ;
Log . w ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " request for device bundles came back with something other than item-not-found " + packet ) ;
return ;
}
}
2015-08-25 22:27:39 +00:00
PreKeyBundle bundle = mXmppConnectionService . getIqParser ( ) . bundle ( packet ) ;
Map < Integer , ECPublicKey > keys = mXmppConnectionService . getIqParser ( ) . preKeyPublics ( packet ) ;
boolean flush = false ;
if ( bundle = = null ) {
Log . w ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Received invalid bundle: " + packet ) ;
bundle = new PreKeyBundle ( - 1 , - 1 , - 1 , null , - 1 , null , null , null ) ;
flush = true ;
}
if ( keys = = null ) {
Log . w ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Received invalid prekeys: " + packet ) ;
}
try {
boolean changed = false ;
// Validate IdentityKey
IdentityKeyPair identityKeyPair = axolotlStore . getIdentityKeyPair ( ) ;
if ( flush | | ! identityKeyPair . getPublicKey ( ) . equals ( bundle . getIdentityKey ( ) ) ) {
Log . i ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Adding own IdentityKey " + identityKeyPair . getPublicKey ( ) + " to PEP. " ) ;
changed = true ;
2015-07-03 11:20:27 +00:00
}
2015-08-23 11:23:10 +00:00
2015-08-25 22:27:39 +00:00
// Validate signedPreKeyRecord + ID
SignedPreKeyRecord signedPreKeyRecord ;
2017-05-04 09:03:58 +00:00
int numSignedPreKeys = axolotlStore . getSignedPreKeysCount ( ) ;
2015-08-25 22:27:39 +00:00
try {
signedPreKeyRecord = axolotlStore . loadSignedPreKey ( bundle . getSignedPreKeyId ( ) ) ;
if ( flush
| | ! bundle . getSignedPreKey ( ) . equals ( signedPreKeyRecord . getKeyPair ( ) . getPublicKey ( ) )
| | ! Arrays . equals ( bundle . getSignedPreKeySignature ( ) , signedPreKeyRecord . getSignature ( ) ) ) {
2015-07-31 16:05:32 +00:00
Log . i ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Adding new signedPreKey with ID " + ( numSignedPreKeys + 1 ) + " to PEP. " ) ;
2015-07-03 11:20:27 +00:00
signedPreKeyRecord = KeyHelper . generateSignedPreKey ( identityKeyPair , numSignedPreKeys + 1 ) ;
axolotlStore . storeSignedPreKey ( signedPreKeyRecord . getId ( ) , signedPreKeyRecord ) ;
changed = true ;
}
2015-08-25 22:27:39 +00:00
} catch ( InvalidKeyIdException e ) {
Log . i ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Adding new signedPreKey with ID " + ( numSignedPreKeys + 1 ) + " to PEP. " ) ;
signedPreKeyRecord = KeyHelper . generateSignedPreKey ( identityKeyPair , numSignedPreKeys + 1 ) ;
axolotlStore . storeSignedPreKey ( signedPreKeyRecord . getId ( ) , signedPreKeyRecord ) ;
changed = true ;
}
2015-06-29 12:18:11 +00:00
2015-08-25 22:27:39 +00:00
// Validate PreKeys
Set < PreKeyRecord > preKeyRecords = new HashSet < > ( ) ;
if ( keys ! = null ) {
for ( Integer id : keys . keySet ( ) ) {
try {
PreKeyRecord preKeyRecord = axolotlStore . loadPreKey ( id ) ;
if ( preKeyRecord . getKeyPair ( ) . getPublicKey ( ) . equals ( keys . get ( id ) ) ) {
preKeyRecords . add ( preKeyRecord ) ;
2015-07-03 11:20:27 +00:00
}
2015-08-25 22:27:39 +00:00
} catch ( InvalidKeyIdException ignored ) {
2015-07-03 11:20:27 +00:00
}
}
2015-08-25 22:27:39 +00:00
}
int newKeys = NUM_KEYS_TO_PUBLISH - preKeyRecords . size ( ) ;
if ( newKeys > 0 ) {
List < PreKeyRecord > newRecords = KeyHelper . generatePreKeys (
axolotlStore . getCurrentPreKeyId ( ) + 1 , newKeys ) ;
preKeyRecords . addAll ( newRecords ) ;
for ( PreKeyRecord record : newRecords ) {
axolotlStore . storePreKey ( record . getId ( ) , record ) ;
2015-06-29 12:18:11 +00:00
}
2015-08-25 22:27:39 +00:00
changed = true ;
Log . i ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Adding " + newKeys + " new preKeys to PEP. " ) ;
}
2015-06-29 12:18:11 +00:00
2015-07-03 11:20:27 +00:00
2017-07-12 20:16:31 +00:00
if ( changed | | changeAccessMode . get ( ) ) {
2015-10-12 11:18:20 +00:00
if ( account . getPrivateKeyAlias ( ) ! = null & & Config . X509_VERIFICATION ) {
2015-10-29 12:41:08 +00:00
mXmppConnectionService . publishDisplayName ( account ) ;
2015-10-11 13:48:58 +00:00
publishDeviceVerificationAndBundle ( signedPreKeyRecord , preKeyRecords , announce , wipe ) ;
2015-10-12 11:18:20 +00:00
} else {
publishDeviceBundle ( signedPreKeyRecord , preKeyRecords , announce , wipe ) ;
2015-10-11 13:48:58 +00:00
}
2015-08-26 13:32:49 +00:00
} else {
Log . d ( Config . LOGTAG , getLogprefix ( account ) + " Bundle " + getOwnDeviceId ( ) + " in PEP was current " ) ;
2015-10-11 13:48:58 +00:00
if ( wipe ) {
wipeOtherPepDevices ( ) ;
} else if ( announce ) {
2015-08-26 13:32:49 +00:00
Log . d ( Config . LOGTAG , getLogprefix ( account ) + " Announcing device " + getOwnDeviceId ( ) ) ;
publishOwnDeviceIdIfNeeded ( ) ;
}
2015-07-03 11:20:27 +00:00
}
2015-08-25 22:27:39 +00:00
} catch ( InvalidKeyException e ) {
Log . e ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Failed to publish bundle " + getOwnDeviceId ( ) + " , reason: " + e . getMessage ( ) ) ;
2015-06-26 13:41:02 +00:00
}
}
} ) ;
}
2015-10-11 13:48:58 +00:00
private void publishDeviceBundle ( SignedPreKeyRecord signedPreKeyRecord ,
Set < PreKeyRecord > preKeyRecords ,
final boolean announceAfter ,
final boolean wipe ) {
2017-07-12 15:49:49 +00:00
publishDeviceBundle ( signedPreKeyRecord , preKeyRecords , announceAfter , wipe , true ) ;
}
private void publishDeviceBundle ( final SignedPreKeyRecord signedPreKeyRecord ,
final Set < PreKeyRecord > preKeyRecords ,
final boolean announceAfter ,
final boolean wipe ,
final boolean firstAttempt ) {
final Bundle publishOptions = account . getXmppConnection ( ) . getFeatures ( ) . pepPublishOptions ( ) ? PublishOptions . openAccess ( ) : null ;
2015-10-11 13:48:58 +00:00
IqPacket publish = mXmppConnectionService . getIqGenerator ( ) . publishBundles (
signedPreKeyRecord , axolotlStore . getIdentityKeyPair ( ) . getPublicKey ( ) ,
2017-07-12 15:49:49 +00:00
preKeyRecords , getOwnDeviceId ( ) , publishOptions ) ;
2017-02-24 18:58:46 +00:00
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " : Bundle " + getOwnDeviceId ( ) + " in PEP not current. Publishing... " ) ;
2015-10-11 13:48:58 +00:00
mXmppConnectionService . sendIqPacket ( account , publish , new OnIqPacketReceived ( ) {
@Override
2017-06-28 08:21:06 +00:00
public void onIqPacketReceived ( final Account account , IqPacket packet ) {
2017-07-12 15:49:49 +00:00
Element error = packet . getType ( ) = = IqPacket . TYPE . ERROR ? packet . findChild ( " error " ) : null ;
if ( firstAttempt & & error ! = null & & error . hasChild ( " precondition-not-met " , Namespace . PUBSUB_ERROR ) ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : precondition wasn't met for bundle. pushing node configuration " ) ;
2017-06-28 08:21:06 +00:00
final String node = AxolotlService . PEP_BUNDLES + " : " + getOwnDeviceId ( ) ;
2017-07-12 15:49:49 +00:00
mXmppConnectionService . pushNodeConfiguration ( account , node , publishOptions , new XmppConnectionService . OnConfigurationPushed ( ) {
2017-06-28 08:21:06 +00:00
@Override
public void onPushSucceeded ( ) {
2017-07-12 15:49:49 +00:00
publishDeviceBundle ( signedPreKeyRecord , preKeyRecords , announceAfter , wipe , false ) ;
2017-06-28 08:21:06 +00:00
}
@Override
public void onPushFailed ( ) {
2017-07-12 15:49:49 +00:00
publishDeviceBundle ( signedPreKeyRecord , preKeyRecords , announceAfter , wipe , false ) ;
2017-06-28 08:21:06 +00:00
}
} ) ;
2017-07-12 20:16:31 +00:00
} else if ( packet . getType ( ) = = IqPacket . TYPE . RESULT ) {
2017-07-12 15:49:49 +00:00
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Successfully published bundle. " ) ;
if ( wipe ) {
wipeOtherPepDevices ( ) ;
} else if ( announceAfter ) {
Log . d ( Config . LOGTAG , getLogprefix ( account ) + " Announcing device " + getOwnDeviceId ( ) ) ;
publishOwnDeviceIdIfNeeded ( ) ;
}
2016-04-05 11:31:03 +00:00
} else if ( packet . getType ( ) = = IqPacket . TYPE . ERROR ) {
pepBroken = true ;
2015-10-11 13:48:58 +00:00
Log . d ( Config . LOGTAG , getLogprefix ( account ) + " Error received while publishing bundle: " + packet . findChild ( " error " ) ) ;
}
}
} ) ;
}
2016-09-08 09:01:27 +00:00
public enum AxolotlCapability {
FULL ,
MISSING_PRESENCE ,
MISSING_KEYS ,
WRONG_CONFIGURATION ,
NO_MEMBERS
}
2016-02-29 12:18:07 +00:00
public boolean isConversationAxolotlCapable ( Conversation conversation ) {
2017-07-14 07:08:39 +00:00
return conversation . getMode ( ) = = Conversation . MODE_SINGLE | | ( conversation . getMucOptions ( ) . nonanonymous ( ) & & conversation . getMucOptions ( ) . membersOnly ( ) ) ;
2016-09-08 09:01:27 +00:00
}
public Pair < AxolotlCapability , Jid > isConversationAxolotlCapableDetailed ( Conversation conversation ) {
2016-10-03 19:04:10 +00:00
if ( conversation . getMode ( ) = = Conversation . MODE_SINGLE
| | ( conversation . getMucOptions ( ) . membersOnly ( ) & & conversation . getMucOptions ( ) . nonanonymous ( ) ) ) {
2016-10-03 08:42:43 +00:00
final List < Jid > jids = getCryptoTargets ( conversation ) ;
for ( Jid jid : jids ) {
if ( ! hasAny ( jid ) & & ( ! deviceIds . containsKey ( jid ) | | deviceIds . get ( jid ) . isEmpty ( ) ) ) {
2016-10-07 08:05:08 +00:00
if ( conversation . getAccount ( ) . getRoster ( ) . getContact ( jid ) . mutualPresenceSubscription ( ) ) {
2016-10-03 08:42:43 +00:00
return new Pair < > ( AxolotlCapability . MISSING_KEYS , jid ) ;
} else {
return new Pair < > ( AxolotlCapability . MISSING_PRESENCE , jid ) ;
}
2016-09-08 09:01:27 +00:00
}
}
2016-10-03 08:42:43 +00:00
if ( jids . size ( ) > 0 ) {
return new Pair < > ( AxolotlCapability . FULL , null ) ;
2016-09-08 09:01:27 +00:00
} else {
2016-10-03 08:42:43 +00:00
return new Pair < > ( AxolotlCapability . NO_MEMBERS , null ) ;
2016-02-29 12:18:07 +00:00
}
2016-10-03 08:42:43 +00:00
} else {
return new Pair < > ( AxolotlCapability . WRONG_CONFIGURATION , null ) ;
2016-02-29 12:18:07 +00:00
}
}
public List < Jid > getCryptoTargets ( Conversation conversation ) {
final List < Jid > jids ;
if ( conversation . getMode ( ) = = Conversation . MODE_SINGLE ) {
2017-06-29 12:17:45 +00:00
jids = new ArrayList < > ( ) ;
jids . add ( conversation . getJid ( ) . toBareJid ( ) ) ;
2016-02-29 12:18:07 +00:00
} else {
jids = conversation . getMucOptions ( ) . getMembers ( ) ;
}
return jids ;
2015-06-26 13:41:02 +00:00
}
2015-07-31 16:05:32 +00:00
2016-11-14 21:27:41 +00:00
public FingerprintStatus getFingerprintTrust ( String fingerprint ) {
return axolotlStore . getFingerprintStatus ( fingerprint ) ;
2015-07-09 12:23:17 +00:00
}
2015-12-23 18:18:53 +00:00
public X509Certificate getFingerprintCertificate ( String fingerprint ) {
return axolotlStore . getFingerprintCertificate ( fingerprint ) ;
}
2016-11-14 21:27:41 +00:00
public void setFingerprintTrust ( String fingerprint , FingerprintStatus status ) {
2016-11-17 19:09:42 +00:00
axolotlStore . setFingerprintStatus ( fingerprint , status ) ;
2015-07-09 12:23:17 +00:00
}
2015-06-26 13:41:02 +00:00
2015-12-19 14:44:11 +00:00
private void verifySessionWithPEP ( final XmppAxolotlSession session ) {
Log . d ( Config . LOGTAG , " trying to verify fresh session ( " + session . getRemoteAddress ( ) . getName ( ) + " ) with pep " ) ;
2017-06-18 14:35:30 +00:00
final SignalProtocolAddress address = session . getRemoteAddress ( ) ;
2015-12-19 14:44:11 +00:00
final IdentityKey identityKey = session . getIdentityKey ( ) ;
2015-10-16 21:48:42 +00:00
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 < X509Certificate [ ] , byte [ ] > 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 " ) ;
2015-12-23 16:41:26 +00:00
String fingerprint = session . getFingerprint ( ) ;
Log . d ( Config . LOGTAG , " verified session with x.509 signature. fingerprint was: " + fingerprint ) ;
2016-11-14 21:27:41 +00:00
setFingerprintTrust ( fingerprint , FingerprintStatus . createActiveVerified ( true ) ) ;
2015-12-23 16:41:26 +00:00
axolotlStore . setFingerprintCertificate ( fingerprint , verification . first [ 0 ] ) ;
2015-10-17 12:09:26 +00:00
fetchStatusMap . put ( address , FetchStatus . SUCCESS_VERIFIED ) ;
2016-02-02 12:43:20 +00:00
Bundle information = CryptoHelper . extractCertificateInformation ( verification . first [ 0 ] ) ;
try {
final String cn = information . getString ( " subject_cn " ) ;
final Jid jid = Jid . fromString ( address . getName ( ) ) ;
Log . d ( Config . LOGTAG , " setting common name for " + jid + " to " + cn ) ;
account . getRoster ( ) . getContact ( jid ) . setCommonName ( cn ) ;
} catch ( final InvalidJidException ignored ) {
//ignored
}
2015-10-17 12:09:26 +00:00
finishBuildingSessionsFromPEP ( address ) ;
return ;
2015-10-16 21:48:42 +00:00
} catch ( Exception e ) {
Log . d ( Config . LOGTAG , " could not verify certificate " ) ;
}
}
} catch ( Exception e ) {
Log . d ( Config . LOGTAG , " error during verification " + e . getMessage ( ) ) ;
}
2015-12-19 11:44:55 +00:00
} else {
Log . d ( Config . LOGTAG , " no verification found " ) ;
2015-10-16 21:48:42 +00:00
}
2015-10-17 12:09:26 +00:00
fetchStatusMap . put ( address , FetchStatus . SUCCESS ) ;
2015-10-16 21:48:42 +00:00
finishBuildingSessionsFromPEP ( address ) ;
}
} ) ;
} catch ( InvalidJidException e ) {
2015-10-17 12:09:26 +00:00
fetchStatusMap . put ( address , FetchStatus . SUCCESS ) ;
2015-10-16 21:48:42 +00:00
finishBuildingSessionsFromPEP ( address ) ;
}
}
2016-12-18 10:47:42 +00:00
private final Set < Integer > PREVIOUSLY_REMOVED_FROM_ANNOUNCEMENT = new HashSet < > ( ) ;
2017-06-18 14:35:30 +00:00
private void finishBuildingSessionsFromPEP ( final SignalProtocolAddress address ) {
SignalProtocolAddress ownAddress = new SignalProtocolAddress ( account . getJid ( ) . toBareJid ( ) . toPreppedString ( ) , 0 ) ;
Map < Integer , FetchStatus > own = fetchStatusMap . getAll ( ownAddress . getName ( ) ) ;
Map < Integer , FetchStatus > remote = fetchStatusMap . getAll ( address . getName ( ) ) ;
2016-11-17 19:09:42 +00:00
if ( ! own . containsValue ( FetchStatus . PENDING ) & & ! remote . containsValue ( FetchStatus . PENDING ) ) {
2015-10-17 12:09:26 +00:00
FetchStatus report = null ;
2016-11-17 19:09:42 +00:00
if ( own . containsValue ( FetchStatus . SUCCESS ) | | remote . containsValue ( FetchStatus . SUCCESS ) ) {
report = FetchStatus . SUCCESS ;
} else if ( own . containsValue ( FetchStatus . SUCCESS_VERIFIED ) | | remote . containsValue ( FetchStatus . SUCCESS_VERIFIED ) ) {
2015-10-17 12:09:26 +00:00
report = FetchStatus . SUCCESS_VERIFIED ;
2016-11-23 09:42:27 +00:00
} else if ( own . containsValue ( FetchStatus . SUCCESS_TRUSTED ) | | remote . containsValue ( FetchStatus . SUCCESS_TRUSTED ) ) {
report = FetchStatus . SUCCESS_TRUSTED ;
2016-11-17 19:09:42 +00:00
} else if ( own . containsValue ( FetchStatus . ERROR ) | | remote . containsValue ( FetchStatus . ERROR ) ) {
2015-10-17 12:09:26 +00:00
report = FetchStatus . ERROR ;
}
mXmppConnectionService . keyStatusUpdated ( report ) ;
2015-10-16 21:48:42 +00:00
}
2016-12-18 10:49:27 +00:00
if ( Config . REMOVE_BROKEN_DEVICES ) {
Set < Integer > ownDeviceIds = new HashSet < > ( getOwnDeviceIds ( ) ) ;
boolean publish = false ;
for ( Map . Entry < Integer , FetchStatus > entry : own . entrySet ( ) ) {
int id = entry . getKey ( ) ;
if ( entry . getValue ( ) = = FetchStatus . ERROR & & PREVIOUSLY_REMOVED_FROM_ANNOUNCEMENT . add ( id ) & & ownDeviceIds . remove ( id ) ) {
publish = true ;
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : error fetching own device with id " + id + " . removing from announcement " ) ;
}
}
if ( publish ) {
publishOwnDeviceId ( ownDeviceIds ) ;
2016-12-16 16:12:26 +00:00
}
}
2015-10-16 21:48:42 +00:00
}
2017-06-28 07:49:24 +00:00
public boolean hasEmptyDeviceList ( Jid jid ) {
return ! hasAny ( jid ) & & ( ! deviceIds . containsKey ( jid ) | | deviceIds . get ( jid ) . isEmpty ( ) ) ;
}
2017-06-28 09:34:53 +00:00
public interface OnDeviceIdsFetched {
2017-06-29 12:17:45 +00:00
void fetched ( Jid jid , Set < Integer > deviceIds ) ;
}
public interface OnMultipleDeviceIdFetched {
void fetched ( ) ;
2017-06-28 09:34:53 +00:00
}
2017-06-28 07:49:24 +00:00
public void fetchDeviceIds ( final Jid jid ) {
2017-06-28 09:34:53 +00:00
fetchDeviceIds ( jid , null ) ;
}
2017-06-29 12:17:45 +00:00
public void fetchDeviceIds ( final Jid jid , OnDeviceIdsFetched callback ) {
synchronized ( this . fetchDeviceIdsMap ) {
List < OnDeviceIdsFetched > callbacks = this . fetchDeviceIdsMap . get ( jid ) ;
if ( callbacks ! = null ) {
if ( callback ! = null ) {
callbacks . add ( callback ) ;
2017-06-28 07:49:24 +00:00
}
2017-06-29 12:17:45 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : fetching device ids for " + jid + " already running. adding callback " ) ;
} else {
callbacks = new ArrayList < > ( ) ;
if ( callback ! = null ) {
callbacks . add ( callback ) ;
}
this . fetchDeviceIdsMap . put ( jid , callbacks ) ;
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : fetching device ids for " + jid ) ;
IqPacket packet = mXmppConnectionService . getIqGenerator ( ) . retrieveDeviceIds ( jid ) ;
mXmppConnectionService . sendIqPacket ( account , packet , new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
synchronized ( fetchDeviceIdsMap ) {
List < OnDeviceIdsFetched > callbacks = fetchDeviceIdsMap . remove ( jid ) ;
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT ) {
Element item = mXmppConnectionService . getIqParser ( ) . getItem ( packet ) ;
Set < Integer > deviceIds = mXmppConnectionService . getIqParser ( ) . deviceIds ( item ) ;
registerDevices ( jid , deviceIds ) ;
if ( callbacks ! = null ) {
for ( OnDeviceIdsFetched callback : callbacks ) {
callback . fetched ( jid , deviceIds ) ;
}
}
} else {
Log . d ( Config . LOGTAG , packet . toString ( ) ) ;
if ( callbacks ! = null ) {
for ( OnDeviceIdsFetched callback : callbacks ) {
callback . fetched ( jid , null ) ;
}
}
}
}
}
} ) ;
}
}
}
2017-06-28 09:34:53 +00:00
2017-06-29 12:17:45 +00:00
private void fetchDeviceIds ( List < Jid > jids , final OnMultipleDeviceIdFetched callback ) {
final ArrayList < Jid > unfinishedJids = new ArrayList < > ( jids ) ;
synchronized ( unfinishedJids ) {
for ( Jid jid : unfinishedJids ) {
fetchDeviceIds ( jid , new OnDeviceIdsFetched ( ) {
@Override
public void fetched ( Jid jid , Set < Integer > deviceIds ) {
synchronized ( unfinishedJids ) {
unfinishedJids . remove ( jid ) ;
if ( unfinishedJids . size ( ) = = 0 & & callback ! = null ) {
callback . fetched ( ) ;
}
}
}
} ) ;
2017-06-28 07:49:24 +00:00
}
2017-06-29 12:17:45 +00:00
}
2017-06-28 07:49:24 +00:00
}
2017-06-18 14:35:30 +00:00
private void buildSessionFromPEP ( final SignalProtocolAddress address ) {
2016-12-16 16:12:26 +00:00
Log . i ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Building new session for " + address . toString ( ) ) ;
2016-12-28 21:15:24 +00:00
if ( address . equals ( getOwnAxolotlAddress ( ) ) ) {
2015-09-05 15:25:46 +00:00
throw new AssertionError ( " We should NEVER build a session with ourselves. What happened here?! " ) ;
}
2015-06-29 12:18:11 +00:00
try {
IqPacket bundlesPacket = mXmppConnectionService . getIqGenerator ( ) . retrieveBundlesForDevice (
Jid . fromString ( address . getName ( ) ) , address . getDeviceId ( ) ) ;
2015-07-31 16:05:32 +00:00
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Retrieving bundle: " + bundlesPacket ) ;
2015-06-29 12:18:11 +00:00
mXmppConnectionService . sendIqPacket ( account , bundlesPacket , new OnIqPacketReceived ( ) {
2015-07-19 16:36:28 +00:00
2015-06-29 12:18:11 +00:00
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
2015-10-17 13:51:21 +00:00
if ( packet . getType ( ) = = IqPacket . TYPE . TIMEOUT ) {
fetchStatusMap . put ( address , FetchStatus . TIMEOUT ) ;
} else if ( packet . getType ( ) = = IqPacket . TYPE . RESULT ) {
2015-08-23 11:23:10 +00:00
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Received preKey IQ packet, processing... " ) ;
final IqParser parser = mXmppConnectionService . getIqParser ( ) ;
final List < PreKeyBundle > preKeyBundleList = parser . preKeys ( packet ) ;
final PreKeyBundle bundle = parser . bundle ( packet ) ;
if ( preKeyBundleList . isEmpty ( ) | | bundle = = null ) {
Log . e ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " preKey IQ packet invalid: " + packet ) ;
fetchStatusMap . put ( address , FetchStatus . ERROR ) ;
2015-10-16 21:48:42 +00:00
finishBuildingSessionsFromPEP ( address ) ;
2015-08-23 11:23:10 +00:00
return ;
}
Random random = new Random ( ) ;
final PreKeyBundle preKey = preKeyBundleList . get ( random . nextInt ( preKeyBundleList . size ( ) ) ) ;
if ( preKey = = null ) {
//should never happen
fetchStatusMap . put ( address , FetchStatus . ERROR ) ;
2015-10-16 21:48:42 +00:00
finishBuildingSessionsFromPEP ( address ) ;
2015-08-23 11:23:10 +00:00
return ;
}
final PreKeyBundle preKeyBundle = new PreKeyBundle ( 0 , address . getDeviceId ( ) ,
preKey . getPreKeyId ( ) , preKey . getPreKey ( ) ,
bundle . getSignedPreKeyId ( ) , bundle . getSignedPreKey ( ) ,
bundle . getSignedPreKeySignature ( ) , bundle . getIdentityKey ( ) ) ;
try {
SessionBuilder builder = new SessionBuilder ( axolotlStore , address ) ;
builder . process ( preKeyBundle ) ;
2015-12-19 14:44:11 +00:00
XmppAxolotlSession session = new XmppAxolotlSession ( account , axolotlStore , address , bundle . getIdentityKey ( ) ) ;
2015-08-23 11:23:10 +00:00
sessions . put ( address , session ) ;
2015-10-16 21:48:42 +00:00
if ( Config . X509_VERIFICATION ) {
2015-12-19 14:44:11 +00:00
verifySessionWithPEP ( session ) ;
2015-10-16 21:48:42 +00:00
} else {
2017-06-25 16:18:13 +00:00
FingerprintStatus status = getFingerprintTrust ( CryptoHelper . bytesToHex ( bundle . getIdentityKey ( ) . getPublicKey ( ) . serialize ( ) ) ) ;
2016-11-23 09:42:27 +00:00
FetchStatus fetchStatus ;
if ( status ! = null & & status . isVerified ( ) ) {
fetchStatus = FetchStatus . SUCCESS_VERIFIED ;
} else if ( status ! = null & & status . isTrusted ( ) ) {
fetchStatus = FetchStatus . SUCCESS_TRUSTED ;
} else {
fetchStatus = FetchStatus . SUCCESS ;
}
fetchStatusMap . put ( address , fetchStatus ) ;
2015-10-16 21:48:42 +00:00
finishBuildingSessionsFromPEP ( address ) ;
}
2015-08-23 11:23:10 +00:00
} 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 ) ;
2015-10-16 21:48:42 +00:00
finishBuildingSessionsFromPEP ( address ) ;
2015-08-23 11:23:10 +00:00
}
} else {
2015-06-29 12:18:11 +00:00
fetchStatusMap . put ( address , FetchStatus . ERROR ) ;
2015-08-23 11:23:10 +00:00
Log . d ( Config . LOGTAG , getLogprefix ( account ) + " Error received while building session: " + packet . findChild ( " error " ) ) ;
2015-10-16 21:48:42 +00:00
finishBuildingSessionsFromPEP ( address ) ;
2015-06-29 12:18:11 +00:00
}
}
} ) ;
} catch ( InvalidJidException e ) {
2015-07-31 16:05:32 +00:00
Log . e ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Got address with invalid jid: " + address . getName ( ) ) ;
2015-06-29 12:18:11 +00:00
}
2015-06-26 13:41:02 +00:00
}
2017-06-18 14:35:30 +00:00
public Set < SignalProtocolAddress > findDevicesWithoutSession ( final Conversation conversation ) {
Set < SignalProtocolAddress > addresses = new HashSet < > ( ) ;
2016-02-29 12:18:07 +00:00
for ( Jid jid : getCryptoTargets ( conversation ) ) {
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Finding devices without session for " + jid ) ;
if ( deviceIds . get ( jid ) ! = null ) {
for ( Integer foreignId : this . deviceIds . get ( jid ) ) {
2017-06-18 14:35:30 +00:00
SignalProtocolAddress address = new SignalProtocolAddress ( jid . toPreppedString ( ) , foreignId ) ;
2016-02-29 12:18:07 +00:00
if ( sessions . get ( address ) = = null ) {
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 ) ;
sessions . put ( address , session ) ;
2015-10-17 13:51:21 +00:00
} else {
2016-02-29 12:18:07 +00:00
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Found device " + jid + " : " + foreignId ) ;
if ( fetchStatusMap . get ( address ) ! = FetchStatus . ERROR ) {
addresses . add ( address ) ;
} else {
Log . d ( Config . LOGTAG , getLogprefix ( account ) + " skipping over " + address + " because it's broken " ) ;
}
2015-10-17 13:51:21 +00:00
}
2015-07-19 16:36:28 +00:00
}
}
2016-02-29 12:18:07 +00:00
} else {
2017-07-28 16:37:07 +00:00
mXmppConnectionService . keyStatusUpdated ( FetchStatus . ERROR ) ;
2016-02-29 12:18:07 +00:00
Log . w ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Have no target devices in PEP! " ) ;
2015-06-29 12:22:26 +00:00
}
}
2015-07-31 16:05:32 +00:00
if ( deviceIds . get ( account . getJid ( ) . toBareJid ( ) ) ! = null ) {
for ( Integer ownId : this . deviceIds . get ( account . getJid ( ) . toBareJid ( ) ) ) {
2017-06-18 14:35:30 +00:00
SignalProtocolAddress address = new SignalProtocolAddress ( account . getJid ( ) . toBareJid ( ) . toPreppedString ( ) , ownId ) ;
2015-07-31 16:05:32 +00:00
if ( sessions . get ( address ) = = null ) {
2015-07-19 16:36:28 +00:00
IdentityKey identityKey = axolotlStore . loadSession ( address ) . getSessionState ( ) . getRemoteIdentityKey ( ) ;
2015-07-31 16:05:32 +00:00
if ( identityKey ! = null ) {
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Already have session for " + address . toString ( ) + " , adding to cache... " ) ;
2015-12-19 14:44:11 +00:00
XmppAxolotlSession session = new XmppAxolotlSession ( account , axolotlStore , address , identityKey ) ;
2015-07-19 16:36:28 +00:00
sessions . put ( address , session ) ;
} else {
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Found device " + account . getJid ( ) . toBareJid ( ) + " : " + ownId ) ;
2015-10-17 13:51:21 +00:00
if ( fetchStatusMap . get ( address ) ! = FetchStatus . ERROR ) {
addresses . add ( address ) ;
} else {
Log . d ( Config . LOGTAG , getLogprefix ( account ) + " skipping over " + address + " because it's broken " ) ;
}
2015-07-19 16:36:28 +00:00
}
}
2015-06-26 13:41:02 +00:00
}
}
2015-07-19 16:36:28 +00:00
return addresses ;
}
2015-07-31 19:12:34 +00:00
public boolean createSessionsIfNeeded ( final Conversation conversation ) {
2017-06-29 12:17:45 +00:00
final List < Jid > jidsWithEmptyDeviceList = getCryptoTargets ( conversation ) ;
for ( Iterator < Jid > iterator = jidsWithEmptyDeviceList . iterator ( ) ; iterator . hasNext ( ) ; ) {
final Jid jid = iterator . next ( ) ;
if ( ! hasEmptyDeviceList ( jid ) ) {
iterator . remove ( ) ;
2017-06-28 09:34:53 +00:00
}
2017-06-29 12:17:45 +00:00
}
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : createSessionsIfNeeded() - jids with empty device list: " + jidsWithEmptyDeviceList ) ;
if ( jidsWithEmptyDeviceList . size ( ) > 0 ) {
fetchDeviceIds ( jidsWithEmptyDeviceList , new OnMultipleDeviceIdFetched ( ) {
2017-06-28 09:34:53 +00:00
@Override
2017-06-29 12:17:45 +00:00
public void fetched ( ) {
2017-06-28 09:34:53 +00:00
createSessionsIfNeededActual ( conversation ) ;
}
} ) ;
return true ;
} else {
return createSessionsIfNeededActual ( conversation ) ;
}
}
private boolean createSessionsIfNeededActual ( final Conversation conversation ) {
2015-07-19 16:36:28 +00:00
Log . i ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Creating axolotl sessions if needed... " ) ;
boolean newSessions = false ;
2017-06-18 14:35:30 +00:00
Set < SignalProtocolAddress > addresses = findDevicesWithoutSession ( conversation ) ;
for ( SignalProtocolAddress address : addresses ) {
2015-07-19 16:36:28 +00:00
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Processing device: " + address . toString ( ) ) ;
2015-06-29 12:22:26 +00:00
FetchStatus status = fetchStatusMap . get ( address ) ;
2015-10-17 13:51:21 +00:00
if ( status = = null | | status = = FetchStatus . TIMEOUT ) {
2015-07-31 16:05:32 +00:00
fetchStatusMap . put ( address , FetchStatus . PENDING ) ;
2015-07-31 19:12:34 +00:00
this . buildSessionFromPEP ( address ) ;
newSessions = true ;
} else if ( status = = FetchStatus . PENDING ) {
2015-07-31 16:05:32 +00:00
newSessions = true ;
2015-06-29 12:22:26 +00:00
} else {
2015-07-31 16:05:32 +00:00
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Already fetching bundle for " + address . toString ( ) ) ;
2015-06-29 12:22:26 +00:00
}
2015-06-26 13:41:02 +00:00
}
2015-07-19 16:36:28 +00:00
2015-06-29 12:22:26 +00:00
return newSessions ;
2015-06-26 13:41:02 +00:00
}
2015-10-31 21:55:04 +00:00
public boolean trustedSessionVerified ( final Conversation conversation ) {
2016-02-29 12:18:07 +00:00
Set < XmppAxolotlSession > sessions = findSessionsForConversation ( conversation ) ;
2015-10-31 21:55:04 +00:00
sessions . addAll ( findOwnSessions ( ) ) ;
boolean verified = false ;
for ( XmppAxolotlSession session : sessions ) {
2016-11-14 21:27:41 +00:00
if ( session . getTrust ( ) . isTrustedAndActive ( ) ) {
if ( session . getTrust ( ) . getTrust ( ) = = FingerprintStatus . Trust . VERIFIED_X509 ) {
2015-10-31 21:55:04 +00:00
verified = true ;
} else {
return false ;
}
}
}
return verified ;
}
2016-02-29 12:18:07 +00:00
public boolean hasPendingKeyFetches ( Account account , List < Jid > jids ) {
2017-06-18 14:35:30 +00:00
SignalProtocolAddress ownAddress = new SignalProtocolAddress ( account . getJid ( ) . toBareJid ( ) . toPreppedString ( ) , 0 ) ;
if ( fetchStatusMap . getAll ( ownAddress . getName ( ) ) . containsValue ( FetchStatus . PENDING ) ) {
2016-02-29 12:18:07 +00:00
return true ;
}
2017-06-29 18:04:38 +00:00
synchronized ( this . fetchDeviceIdsMap ) {
for ( Jid jid : jids ) {
SignalProtocolAddress foreignAddress = new SignalProtocolAddress ( jid . toBareJid ( ) . toPreppedString ( ) , 0 ) ;
if ( fetchStatusMap . getAll ( foreignAddress . getName ( ) ) . containsValue ( FetchStatus . PENDING ) | | this . fetchDeviceIdsMap . containsKey ( jid ) ) {
return true ;
}
2016-02-29 12:18:07 +00:00
}
}
return false ;
2015-07-19 16:36:28 +00:00
}
2015-06-29 12:22:26 +00:00
@Nullable
2017-01-09 20:47:07 +00:00
private boolean buildHeader ( XmppAxolotlMessage axolotlMessage , Conversation conversation ) {
2016-02-29 12:18:07 +00:00
Set < XmppAxolotlSession > remoteSessions = findSessionsForConversation ( conversation ) ;
2016-11-19 12:34:54 +00:00
Collection < XmppAxolotlSession > ownSessions = findOwnSessions ( ) ;
2016-02-29 12:18:07 +00:00
if ( remoteSessions . isEmpty ( ) ) {
2017-01-09 20:47:07 +00:00
return false ;
2015-06-29 12:22:26 +00:00
}
2016-02-29 12:18:07 +00:00
for ( XmppAxolotlSession session : remoteSessions ) {
2015-07-31 19:12:34 +00:00
axolotlMessage . addDevice ( session ) ;
2015-06-26 13:41:02 +00:00
}
2015-07-31 19:12:34 +00:00
for ( XmppAxolotlSession session : ownSessions ) {
axolotlMessage . addDevice ( session ) ;
}
2017-01-09 20:47:07 +00:00
return true ;
2015-07-31 19:12:34 +00:00
}
@Nullable
public XmppAxolotlMessage encrypt ( Message message ) {
2017-01-09 20:47:07 +00:00
final XmppAxolotlMessage axolotlMessage = new XmppAxolotlMessage ( account . getJid ( ) . toBareJid ( ) , getOwnDeviceId ( ) ) ;
final String content ;
if ( message . hasFileOnRemoteHost ( ) ) {
content = message . getFileParams ( ) . url . toString ( ) ;
} else {
content = message . getBody ( ) ;
}
try {
axolotlMessage . encrypt ( content ) ;
} catch ( CryptoFailedException e ) {
Log . w ( Config . LOGTAG , getLogprefix ( account ) + " Failed to encrypt message: " + e . getMessage ( ) ) ;
return null ;
}
if ( ! buildHeader ( axolotlMessage , message . getConversation ( ) ) ) {
return null ;
2015-06-26 13:41:02 +00:00
}
2015-06-29 12:22:26 +00:00
return axolotlMessage ;
}
2015-07-31 19:12:34 +00:00
public void preparePayloadMessage ( final Message message , final boolean delay ) {
2015-06-29 12:22:26 +00:00
executor . execute ( new Runnable ( ) {
@Override
public void run ( ) {
2015-07-20 21:13:28 +00:00
XmppAxolotlMessage axolotlMessage = encrypt ( message ) ;
if ( axolotlMessage = = null ) {
2015-06-29 12:22:26 +00:00
mXmppConnectionService . markMessage ( message , Message . STATUS_SEND_FAILED ) ;
2015-07-03 11:31:14 +00:00
//mXmppConnectionService.updateConversationUi();
2015-06-29 12:22:26 +00:00
} else {
2015-07-31 16:05:32 +00:00
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Generated message, caching: " + message . getUuid ( ) ) ;
2015-07-20 21:13:28 +00:00
messageCache . put ( message . getUuid ( ) , axolotlMessage ) ;
2015-07-31 16:05:32 +00:00
mXmppConnectionService . resendMessage ( message , delay ) ;
2015-06-29 12:22:26 +00:00
}
}
} ) ;
}
2016-02-29 12:18:07 +00:00
public void prepareKeyTransportMessage ( final Conversation conversation , final OnMessageCreatedCallback onMessageCreatedCallback ) {
2015-07-31 19:12:34 +00:00
executor . execute ( new Runnable ( ) {
@Override
public void run ( ) {
2017-01-09 20:47:07 +00:00
final XmppAxolotlMessage axolotlMessage = new XmppAxolotlMessage ( account . getJid ( ) . toBareJid ( ) , getOwnDeviceId ( ) ) ;
if ( buildHeader ( axolotlMessage , conversation ) ) {
onMessageCreatedCallback . run ( axolotlMessage ) ;
} else {
onMessageCreatedCallback . run ( null ) ;
}
2015-07-03 11:31:14 +00:00
}
2015-07-31 19:12:34 +00:00
} ) ;
2015-07-03 11:31:14 +00:00
}
2015-07-20 21:13:28 +00:00
public XmppAxolotlMessage fetchAxolotlMessageFromCache ( Message message ) {
XmppAxolotlMessage axolotlMessage = messageCache . get ( message . getUuid ( ) ) ;
if ( axolotlMessage ! = null ) {
2015-07-31 16:05:32 +00:00
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Cache hit: " + message . getUuid ( ) ) ;
2015-07-03 11:31:14 +00:00
messageCache . remove ( message . getUuid ( ) ) ;
} else {
2015-07-31 16:05:32 +00:00
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Cache miss: " + message . getUuid ( ) ) ;
2015-06-29 12:22:26 +00:00
}
2015-07-20 21:13:28 +00:00
return axolotlMessage ;
2015-06-26 13:41:02 +00:00
}
2017-06-18 14:35:30 +00:00
private XmppAxolotlSession recreateUncachedSession ( SignalProtocolAddress address ) {
2015-07-31 21:28:09 +00:00
IdentityKey identityKey = axolotlStore . loadSession ( address ) . getSessionState ( ) . getRemoteIdentityKey ( ) ;
return ( identityKey ! = null )
2015-12-19 14:44:11 +00:00
? new XmppAxolotlSession ( account , axolotlStore , address , identityKey )
2015-07-31 21:28:09 +00:00
: null ;
}
private XmppAxolotlSession getReceivingSession ( XmppAxolotlMessage message ) {
2017-06-18 14:35:30 +00:00
SignalProtocolAddress senderAddress = new SignalProtocolAddress ( message . getFrom ( ) . toPreppedString ( ) ,
2015-06-26 13:41:02 +00:00
message . getSenderDeviceId ( ) ) ;
XmppAxolotlSession session = sessions . get ( senderAddress ) ;
if ( session = = null ) {
2015-07-31 16:05:32 +00:00
Log . d ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Account: " + account . getJid ( ) + " No axolotl session found while parsing received message " + message ) ;
2015-07-31 21:28:09 +00:00
session = recreateUncachedSession ( senderAddress ) ;
if ( session = = null ) {
2015-07-10 00:36:29 +00:00
session = new XmppAxolotlSession ( account , axolotlStore , senderAddress ) ;
}
2015-06-26 13:41:02 +00:00
}
2015-07-31 21:28:09 +00:00
return session ;
}
2015-06-26 13:41:02 +00:00
2015-07-31 21:28:09 +00:00
public XmppAxolotlMessage . XmppAxolotlPlaintextMessage processReceivingPayloadMessage ( XmppAxolotlMessage message ) {
XmppAxolotlMessage . XmppAxolotlPlaintextMessage plaintextMessage = null ;
XmppAxolotlSession session = getReceivingSession ( message ) ;
2015-07-31 19:12:34 +00:00
try {
plaintextMessage = message . decrypt ( session , getOwnDeviceId ( ) ) ;
Integer preKeyId = session . getPreKeyId ( ) ;
if ( preKeyId ! = null ) {
2015-10-11 13:48:58 +00:00
publishBundlesIfNeeded ( false , false ) ;
2015-07-31 19:12:34 +00:00
session . resetPreKeyId ( ) ;
2015-06-26 13:41:02 +00:00
}
2015-07-31 19:12:34 +00:00
} catch ( CryptoFailedException e ) {
2017-01-15 17:54:47 +00:00
Log . w ( Config . LOGTAG , getLogprefix ( account ) + " Failed to decrypt message from " + message . getFrom ( ) + " : " + e . getMessage ( ) ) ;
2015-06-26 13:41:02 +00:00
}
2015-07-31 21:28:09 +00:00
if ( session . isFresh ( ) & & plaintextMessage ! = null ) {
2015-12-08 16:15:08 +00:00
putFreshSession ( session ) ;
2015-07-05 20:54:28 +00:00
}
2015-06-26 13:41:02 +00:00
return plaintextMessage ;
}
2015-07-31 21:28:09 +00:00
public XmppAxolotlMessage . XmppAxolotlKeyTransportMessage processReceivingKeyTransportMessage ( XmppAxolotlMessage message ) {
2015-10-11 14:04:31 +00:00
XmppAxolotlMessage . XmppAxolotlKeyTransportMessage keyTransportMessage ;
2015-07-31 21:28:09 +00:00
XmppAxolotlSession session = getReceivingSession ( message ) ;
2017-01-14 17:10:04 +00:00
try {
keyTransportMessage = message . getParameters ( session , getOwnDeviceId ( ) ) ;
} catch ( CryptoFailedException e ) {
Log . d ( Config . LOGTAG , " could not decrypt keyTransport message " + e . getMessage ( ) ) ;
keyTransportMessage = null ;
}
2015-07-31 21:28:09 +00:00
if ( session . isFresh ( ) & & keyTransportMessage ! = null ) {
2015-12-08 16:15:08 +00:00
putFreshSession ( session ) ;
2015-07-31 21:28:09 +00:00
}
return keyTransportMessage ;
}
2015-12-08 16:15:08 +00:00
private void putFreshSession ( XmppAxolotlSession session ) {
2015-12-19 11:44:55 +00:00
Log . d ( Config . LOGTAG , " put fresh session " ) ;
2015-12-08 16:15:08 +00:00
sessions . put ( session ) ;
if ( Config . X509_VERIFICATION ) {
2015-12-19 14:44:11 +00:00
if ( session . getIdentityKey ( ) ! = null ) {
verifySessionWithPEP ( session ) ;
2015-12-08 16:15:08 +00:00
} else {
Log . e ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : identity key was empty after reloading for x509 verification " ) ;
}
}
}
2015-05-29 09:17:26 +00:00
}