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