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.IdentityKey;
 10import org.whispersystems.libaxolotl.InvalidKeyException;
 11import org.whispersystems.libaxolotl.InvalidKeyIdException;
 12import org.whispersystems.libaxolotl.InvalidMessageException;
 13import org.whispersystems.libaxolotl.InvalidVersionException;
 14import org.whispersystems.libaxolotl.LegacyMessageException;
 15import org.whispersystems.libaxolotl.NoSessionException;
 16import org.whispersystems.libaxolotl.SessionCipher;
 17import org.whispersystems.libaxolotl.UntrustedIdentityException;
 18import org.whispersystems.libaxolotl.protocol.CiphertextMessage;
 19import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage;
 20import org.whispersystems.libaxolotl.protocol.WhisperMessage;
 21
 22import eu.siacs.conversations.Config;
 23import eu.siacs.conversations.entities.Account;
 24
 25public class XmppAxolotlSession implements Comparable<XmppAxolotlSession> {
 26	private final SessionCipher cipher;
 27	private final SQLiteAxolotlStore sqLiteAxolotlStore;
 28	private final AxolotlAddress remoteAddress;
 29	private final Account account;
 30	private IdentityKey identityKey;
 31	private Integer preKeyId = null;
 32	private boolean fresh = true;
 33
 34	public XmppAxolotlSession(Account account, SQLiteAxolotlStore store, AxolotlAddress remoteAddress, IdentityKey identityKey) {
 35		this(account, store, remoteAddress);
 36		this.identityKey = identityKey;
 37	}
 38
 39	public XmppAxolotlSession(Account account, SQLiteAxolotlStore store, AxolotlAddress remoteAddress) {
 40		this.cipher = new SessionCipher(store, remoteAddress);
 41		this.remoteAddress = remoteAddress;
 42		this.sqLiteAxolotlStore = store;
 43		this.account = account;
 44	}
 45
 46	public Integer getPreKeyId() {
 47		return preKeyId;
 48	}
 49
 50	public void resetPreKeyId() {
 51
 52		preKeyId = null;
 53	}
 54
 55	public String getFingerprint() {
 56		return identityKey == null ? null : identityKey.getFingerprint().replaceAll("\\s", "");
 57	}
 58
 59	public IdentityKey getIdentityKey() {
 60		return identityKey;
 61	}
 62
 63	public AxolotlAddress getRemoteAddress() {
 64		return remoteAddress;
 65	}
 66
 67	public boolean isFresh() {
 68		return fresh;
 69	}
 70
 71	public void setNotFresh() {
 72		this.fresh = false;
 73	}
 74
 75	protected void setTrust(FingerprintStatus status) {
 76		sqLiteAxolotlStore.setFingerprintStatus(getFingerprint(), status);
 77	}
 78
 79	public FingerprintStatus getTrust() {
 80		FingerprintStatus status = sqLiteAxolotlStore.getFingerprintStatus(getFingerprint());
 81		return (status == null) ? FingerprintStatus.createActiveUndecided() : status;
 82	}
 83
 84	@Nullable
 85	public byte[] processReceiving(AxolotlKey encryptedKey) {
 86		byte[] plaintext = null;
 87		FingerprintStatus status = getTrust();
 88		if (!status.isCompromised()) {
 89			try {
 90				try {
 91					PreKeyWhisperMessage message = new PreKeyWhisperMessage(encryptedKey.key);
 92					if (!message.getPreKeyId().isPresent()) {
 93						Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage did not contain a PreKeyId");
 94						return null;
 95					}
 96					Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage received, new session ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId());
 97					IdentityKey msgIdentityKey = message.getIdentityKey();
 98					if (this.identityKey != null && !this.identityKey.equals(msgIdentityKey)) {
 99						Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Had session with fingerprint " + this.getFingerprint() + ", received message with fingerprint " + msgIdentityKey.getFingerprint());
100					} else {
101						this.identityKey = msgIdentityKey;
102						plaintext = cipher.decrypt(message);
103						preKeyId = message.getPreKeyId().get();
104					}
105				} catch (InvalidMessageException | InvalidVersionException e) {
106					Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "WhisperMessage received");
107					WhisperMessage message = new WhisperMessage(encryptedKey.key);
108					plaintext = cipher.decrypt(message);
109				} catch (InvalidKeyException | InvalidKeyIdException | UntrustedIdentityException e) {
110					Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
111				}
112			} catch (LegacyMessageException | InvalidMessageException | DuplicateMessageException | NoSessionException e) {
113				Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
114			}
115
116			if (plaintext != null) {
117				if (!status.isActive()) {
118					setTrust(status.toActive());
119				}
120			}
121		} else {
122			Log.d(Config.LOGTAG,account.getJid().toBareJid()+" not encrypting omemo message from fingerprint "+getFingerprint()+" because it was marked as compromised");
123		}
124		return plaintext;
125	}
126
127	@Nullable
128	public AxolotlKey processSending(@NonNull byte[] outgoingMessage) {
129		FingerprintStatus status = getTrust();
130		if (status.isTrustedAndActive()) {
131			CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage);
132			return new AxolotlKey(ciphertextMessage.serialize(),ciphertextMessage.getType() == CiphertextMessage.PREKEY_TYPE);
133		} else {
134			return null;
135		}
136	}
137
138	public Account getAccount() {
139		return account;
140	}
141
142	@Override
143	public int compareTo(XmppAxolotlSession o) {
144		return getTrust().compareTo(o.getTrust());
145	}
146
147	public static class AxolotlKey {
148
149
150		public final byte[] key;
151		public final boolean prekey;
152
153		public AxolotlKey(byte[] key, boolean prekey) {
154			this.key = key;
155			this.prekey = prekey;
156		}
157	}
158}