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 final SQLiteAxolotlStore sqLiteAxolotlStore;
27 private final AxolotlAddress remoteAddress;
28 private final Account account;
29 private String fingerprint = null;
30 private Integer preKeyId = null;
31 private boolean fresh = true;
32
33 public XmppAxolotlSession(Account account, SQLiteAxolotlStore store, AxolotlAddress remoteAddress, String fingerprint) {
34 this(account, store, remoteAddress);
35 this.fingerprint = fingerprint;
36 }
37
38 public XmppAxolotlSession(Account account, SQLiteAxolotlStore store, AxolotlAddress remoteAddress) {
39 this.cipher = new SessionCipher(store, remoteAddress);
40 this.remoteAddress = remoteAddress;
41 this.sqLiteAxolotlStore = store;
42 this.account = account;
43 }
44
45 public Integer getPreKeyId() {
46 return preKeyId;
47 }
48
49 public void resetPreKeyId() {
50
51 preKeyId = null;
52 }
53
54 public String getFingerprint() {
55 return fingerprint;
56 }
57
58 public AxolotlAddress getRemoteAddress() {
59 return remoteAddress;
60 }
61
62 public boolean isFresh() {
63 return fresh;
64 }
65
66 public void setNotFresh() {
67 this.fresh = false;
68 }
69
70 protected void setTrust(SQLiteAxolotlStore.Trust trust) {
71 sqLiteAxolotlStore.setFingerprintTrust(fingerprint, trust);
72 }
73
74 protected SQLiteAxolotlStore.Trust getTrust() {
75 SQLiteAxolotlStore.Trust trust = sqLiteAxolotlStore.getFingerprintTrust(fingerprint);
76 return (trust == null) ? SQLiteAxolotlStore.Trust.UNDECIDED : trust;
77 }
78
79 @Nullable
80 public byte[] processReceiving(byte[] encryptedKey) {
81 byte[] plaintext = null;
82 SQLiteAxolotlStore.Trust trust = getTrust();
83 switch (trust) {
84 case INACTIVE:
85 case UNDECIDED:
86 case UNTRUSTED:
87 case TRUSTED:
88 try {
89 try {
90 PreKeyWhisperMessage message = new PreKeyWhisperMessage(encryptedKey);
91 Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage received, new session ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId());
92 String fingerprint = message.getIdentityKey().getFingerprint().replaceAll("\\s", "");
93 if (this.fingerprint != null && !this.fingerprint.equals(fingerprint)) {
94 Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Had session with fingerprint " + this.fingerprint + ", received message with fingerprint " + fingerprint);
95 } else {
96 this.fingerprint = fingerprint;
97 plaintext = cipher.decrypt(message);
98 if (message.getPreKeyId().isPresent()) {
99 preKeyId = message.getPreKeyId().get();
100 }
101 }
102 } catch (InvalidMessageException | InvalidVersionException e) {
103 Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "WhisperMessage received");
104 WhisperMessage message = new WhisperMessage(encryptedKey);
105 plaintext = cipher.decrypt(message);
106 } catch (InvalidKeyException | InvalidKeyIdException | UntrustedIdentityException e) {
107 Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
108 }
109 } catch (LegacyMessageException | InvalidMessageException | DuplicateMessageException | NoSessionException e) {
110 Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
111 }
112
113 if (plaintext != null && trust == SQLiteAxolotlStore.Trust.INACTIVE) {
114 setTrust(SQLiteAxolotlStore.Trust.TRUSTED);
115 }
116
117 break;
118
119 case COMPROMISED:
120 default:
121 // ignore
122 break;
123 }
124 return plaintext;
125 }
126
127 @Nullable
128 public byte[] processSending(@NonNull byte[] outgoingMessage) {
129 SQLiteAxolotlStore.Trust trust = getTrust();
130 if (trust == SQLiteAxolotlStore.Trust.TRUSTED) {
131 CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage);
132 return ciphertextMessage.serialize();
133 } else {
134 return null;
135 }
136 }
137}