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}