2015-07-28 20:00:54 +00:00
package eu.siacs.conversations.crypto.axolotl ;
import android.support.annotation.NonNull ;
import android.support.annotation.Nullable ;
import android.util.Log ;
import org.whispersystems.libaxolotl.AxolotlAddress ;
import org.whispersystems.libaxolotl.DuplicateMessageException ;
import org.whispersystems.libaxolotl.InvalidKeyException ;
import org.whispersystems.libaxolotl.InvalidKeyIdException ;
import org.whispersystems.libaxolotl.InvalidMessageException ;
import org.whispersystems.libaxolotl.InvalidVersionException ;
import org.whispersystems.libaxolotl.LegacyMessageException ;
import org.whispersystems.libaxolotl.NoSessionException ;
import org.whispersystems.libaxolotl.SessionCipher ;
import org.whispersystems.libaxolotl.UntrustedIdentityException ;
import org.whispersystems.libaxolotl.protocol.CiphertextMessage ;
import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage ;
import org.whispersystems.libaxolotl.protocol.WhisperMessage ;
2015-08-01 16:27:52 +00:00
import java.util.HashMap ;
import java.util.Map ;
2015-07-28 20:00:54 +00:00
import eu.siacs.conversations.Config ;
import eu.siacs.conversations.entities.Account ;
public class XmppAxolotlSession {
private final SessionCipher cipher ;
private final SQLiteAxolotlStore sqLiteAxolotlStore ;
private final AxolotlAddress remoteAddress ;
private final Account account ;
private String fingerprint = null ;
2015-07-31 21:28:09 +00:00
private Integer preKeyId = null ;
private boolean fresh = true ;
2015-07-28 20:00:54 +00:00
2015-08-01 16:27:52 +00:00
public enum Trust {
UNDECIDED ( 0 ) ,
TRUSTED ( 1 ) ,
UNTRUSTED ( 2 ) ,
COMPROMISED ( 3 ) ,
INACTIVE_TRUSTED ( 4 ) ,
INACTIVE_UNDECIDED ( 5 ) ,
2015-10-31 09:57:57 +00:00
INACTIVE_UNTRUSTED ( 6 ) ,
TRUSTED_X509 ( 7 ) ,
INACTIVE_TRUSTED_X509 ( 8 ) ;
2015-08-01 16:27:52 +00:00
private static final Map < Integer , Trust > trustsByValue = new HashMap < > ( ) ;
static {
for ( Trust trust : Trust . values ( ) ) {
trustsByValue . put ( trust . getCode ( ) , trust ) ;
}
}
private final int code ;
Trust ( int code ) {
this . code = code ;
}
public int getCode ( ) {
return this . code ;
}
public String toString ( ) {
switch ( this ) {
case UNDECIDED :
return " Trust undecided " + getCode ( ) ;
case TRUSTED :
return " Trusted " + getCode ( ) ;
case COMPROMISED :
return " Compromised " + getCode ( ) ;
case INACTIVE_TRUSTED :
return " Inactive (Trusted) " + getCode ( ) ;
case INACTIVE_UNDECIDED :
return " Inactive (Undecided) " + getCode ( ) ;
case INACTIVE_UNTRUSTED :
return " Inactive (Untrusted) " + getCode ( ) ;
2015-10-31 09:57:57 +00:00
case TRUSTED_X509 :
return " Trusted (X509) " + getCode ( ) ;
case INACTIVE_TRUSTED_X509 :
return " Inactive (Trusted (X509)) " + getCode ( ) ;
2015-08-01 16:27:52 +00:00
case UNTRUSTED :
default :
return " Untrusted " + getCode ( ) ;
}
}
public static Trust fromBoolean ( Boolean trusted ) {
return trusted ? TRUSTED : UNTRUSTED ;
}
public static Trust fromCode ( int code ) {
return trustsByValue . get ( code ) ;
}
2015-10-31 09:57:57 +00:00
public boolean trusted ( ) {
return this = = TRUSTED_X509 | | this = = TRUSTED ;
}
public boolean trustedInactive ( ) {
return this = = INACTIVE_TRUSTED_X509 | | this = = INACTIVE_TRUSTED ;
}
2015-08-01 16:27:52 +00:00
}
2015-07-28 20:00:54 +00:00
public XmppAxolotlSession ( Account account , SQLiteAxolotlStore store , AxolotlAddress remoteAddress , String fingerprint ) {
this ( account , store , remoteAddress ) ;
2015-09-06 13:08:42 +00:00
this . fingerprint = fingerprint . replaceAll ( " \\ s " , " " ) ;
2015-07-28 20:00:54 +00:00
}
public XmppAxolotlSession ( Account account , SQLiteAxolotlStore store , AxolotlAddress remoteAddress ) {
this . cipher = new SessionCipher ( store , remoteAddress ) ;
this . remoteAddress = remoteAddress ;
this . sqLiteAxolotlStore = store ;
this . account = account ;
}
public Integer getPreKeyId ( ) {
return preKeyId ;
}
public void resetPreKeyId ( ) {
preKeyId = null ;
}
public String getFingerprint ( ) {
return fingerprint ;
}
2015-07-31 21:28:09 +00:00
public AxolotlAddress getRemoteAddress ( ) {
return remoteAddress ;
}
public boolean isFresh ( ) {
return fresh ;
}
public void setNotFresh ( ) {
this . fresh = false ;
}
2015-08-01 16:27:52 +00:00
protected void setTrust ( Trust trust ) {
2015-07-28 20:00:54 +00:00
sqLiteAxolotlStore . setFingerprintTrust ( fingerprint , trust ) ;
}
2015-08-01 16:27:52 +00:00
protected Trust getTrust ( ) {
Trust trust = sqLiteAxolotlStore . getFingerprintTrust ( fingerprint ) ;
return ( trust = = null ) ? Trust . UNDECIDED : trust ;
2015-07-28 20:00:54 +00:00
}
@Nullable
2015-07-31 19:12:34 +00:00
public byte [ ] processReceiving ( byte [ ] encryptedKey ) {
2015-07-28 20:00:54 +00:00
byte [ ] plaintext = null ;
2015-08-01 16:27:52 +00:00
Trust trust = getTrust ( ) ;
2015-07-28 20:00:54 +00:00
switch ( trust ) {
2015-08-01 16:27:52 +00:00
case INACTIVE_TRUSTED :
2015-07-28 20:00:54 +00:00
case UNDECIDED :
case UNTRUSTED :
case TRUSTED :
2015-10-31 09:57:57 +00:00
case INACTIVE_TRUSTED_X509 :
case TRUSTED_X509 :
2015-07-28 20:00:54 +00:00
try {
try {
2015-07-31 19:12:34 +00:00
PreKeyWhisperMessage message = new PreKeyWhisperMessage ( encryptedKey ) ;
2015-07-28 20:00:54 +00:00
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 ) ;
} else {
this . fingerprint = fingerprint ;
plaintext = cipher . decrypt ( message ) ;
if ( message . getPreKeyId ( ) . isPresent ( ) ) {
preKeyId = message . getPreKeyId ( ) . get ( ) ;
}
}
} catch ( InvalidMessageException | InvalidVersionException e ) {
Log . i ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " WhisperMessage received " ) ;
2015-07-31 19:12:34 +00:00
WhisperMessage message = new WhisperMessage ( encryptedKey ) ;
2015-07-28 20:00:54 +00:00
plaintext = cipher . decrypt ( message ) ;
} catch ( InvalidKeyException | InvalidKeyIdException | UntrustedIdentityException e ) {
Log . w ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Error decrypting axolotl header, " + e . getClass ( ) . getName ( ) + " : " + e . getMessage ( ) ) ;
}
} catch ( LegacyMessageException | InvalidMessageException | DuplicateMessageException | NoSessionException e ) {
Log . w ( Config . LOGTAG , AxolotlService . getLogprefix ( account ) + " Error decrypting axolotl header, " + e . getClass ( ) . getName ( ) + " : " + e . getMessage ( ) ) ;
}
2015-10-31 09:57:57 +00:00
if ( plaintext ! = null ) {
if ( trust = = Trust . INACTIVE_TRUSTED ) {
setTrust ( Trust . TRUSTED ) ;
} else if ( trust = = Trust . INACTIVE_TRUSTED_X509 ) {
setTrust ( Trust . TRUSTED_X509 ) ;
}
2015-07-28 20:00:54 +00:00
}
break ;
case COMPROMISED :
default :
// ignore
break ;
}
return plaintext ;
}
@Nullable
2015-07-31 19:12:34 +00:00
public byte [ ] processSending ( @NonNull byte [ ] outgoingMessage ) {
2015-08-01 16:27:52 +00:00
Trust trust = getTrust ( ) ;
2015-10-31 09:57:57 +00:00
if ( trust . trusted ( ) ) {
2015-07-28 20:00:54 +00:00
CiphertextMessage ciphertextMessage = cipher . encrypt ( outgoingMessage ) ;
2015-07-31 19:12:34 +00:00
return ciphertextMessage . serialize ( ) ;
2015-07-28 20:00:54 +00:00
} else {
return null ;
}
}
}