XmppAxolotlSession.java

  1package eu.siacs.conversations.crypto.axolotl;
  2
  3import android.support.annotation.NonNull;
  4import android.support.annotation.Nullable;
  5import android.util.Log;
  6
  7import org.whispersystems.libaxolotl.AxolotlAddress;
  8import org.whispersystems.libaxolotl.DuplicateMessageException;
  9import org.whispersystems.libaxolotl.InvalidKeyException;
 10import org.whispersystems.libaxolotl.InvalidKeyIdException;
 11import org.whispersystems.libaxolotl.InvalidMessageException;
 12import org.whispersystems.libaxolotl.InvalidVersionException;
 13import org.whispersystems.libaxolotl.LegacyMessageException;
 14import org.whispersystems.libaxolotl.NoSessionException;
 15import org.whispersystems.libaxolotl.SessionCipher;
 16import org.whispersystems.libaxolotl.UntrustedIdentityException;
 17import org.whispersystems.libaxolotl.protocol.CiphertextMessage;
 18import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage;
 19import org.whispersystems.libaxolotl.protocol.WhisperMessage;
 20
 21import eu.siacs.conversations.Config;
 22import eu.siacs.conversations.entities.Account;
 23
 24public class XmppAxolotlSession {
 25	private final SessionCipher cipher;
 26	private Integer preKeyId = null;
 27	private final SQLiteAxolotlStore sqLiteAxolotlStore;
 28
 29	public AxolotlAddress getRemoteAddress() {
 30		return remoteAddress;
 31	}
 32
 33	private final AxolotlAddress remoteAddress;
 34	private final Account account;
 35	private String fingerprint = null;
 36
 37	public XmppAxolotlSession(Account account, SQLiteAxolotlStore store, AxolotlAddress remoteAddress, String fingerprint) {
 38		this(account, store, remoteAddress);
 39		this.fingerprint = fingerprint;
 40	}
 41
 42	public XmppAxolotlSession(Account account, SQLiteAxolotlStore store, AxolotlAddress remoteAddress) {
 43		this.cipher = new SessionCipher(store, remoteAddress);
 44		this.remoteAddress = remoteAddress;
 45		this.sqLiteAxolotlStore = store;
 46		this.account = account;
 47	}
 48
 49	public Integer getPreKeyId() {
 50		return preKeyId;
 51	}
 52
 53	public void resetPreKeyId() {
 54
 55		preKeyId = null;
 56	}
 57
 58	public String getFingerprint() {
 59		return fingerprint;
 60	}
 61
 62	protected void setTrust(SQLiteAxolotlStore.Trust trust) {
 63		sqLiteAxolotlStore.setFingerprintTrust(fingerprint, trust);
 64	}
 65
 66	protected SQLiteAxolotlStore.Trust getTrust() {
 67		return sqLiteAxolotlStore.getFingerprintTrust(fingerprint);
 68	}
 69
 70	@Nullable
 71	public byte[] processReceiving(XmppAxolotlMessage.XmppAxolotlMessageHeader incomingHeader) {
 72		byte[] plaintext = null;
 73		SQLiteAxolotlStore.Trust trust = getTrust();
 74		switch (trust) {
 75			case INACTIVE:
 76			case UNDECIDED:
 77			case UNTRUSTED:
 78			case TRUSTED:
 79				try {
 80					try {
 81						PreKeyWhisperMessage message = new PreKeyWhisperMessage(incomingHeader.getContents());
 82						Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage received, new session ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId());
 83						String fingerprint = message.getIdentityKey().getFingerprint().replaceAll("\\s", "");
 84						if (this.fingerprint != null && !this.fingerprint.equals(fingerprint)) {
 85							Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Had session with fingerprint " + this.fingerprint + ", received message with fingerprint " + fingerprint);
 86						} else {
 87							this.fingerprint = fingerprint;
 88							plaintext = cipher.decrypt(message);
 89							if (message.getPreKeyId().isPresent()) {
 90								preKeyId = message.getPreKeyId().get();
 91							}
 92						}
 93					} catch (InvalidMessageException | InvalidVersionException e) {
 94						Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "WhisperMessage received");
 95						WhisperMessage message = new WhisperMessage(incomingHeader.getContents());
 96						plaintext = cipher.decrypt(message);
 97					} catch (InvalidKeyException | InvalidKeyIdException | UntrustedIdentityException e) {
 98						Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
 99					}
100				} catch (LegacyMessageException | InvalidMessageException | DuplicateMessageException | NoSessionException e) {
101					Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
102				}
103
104				if (plaintext != null && trust == SQLiteAxolotlStore.Trust.INACTIVE) {
105					setTrust(SQLiteAxolotlStore.Trust.TRUSTED);
106				}
107
108				break;
109
110			case COMPROMISED:
111			default:
112				// ignore
113				break;
114		}
115		return plaintext;
116	}
117
118	@Nullable
119	public XmppAxolotlMessage.XmppAxolotlMessageHeader processSending(@NonNull byte[] outgoingMessage) {
120		SQLiteAxolotlStore.Trust trust = getTrust();
121		if (trust == SQLiteAxolotlStore.Trust.TRUSTED) {
122			CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage);
123			XmppAxolotlMessage.XmppAxolotlMessageHeader header =
124					new XmppAxolotlMessage.XmppAxolotlMessageHeader(remoteAddress.getDeviceId(),
125							ciphertextMessage.serialize());
126			return header;
127		} else {
128			return null;
129		}
130	}
131}