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 SQLiteAxolotlStore.Trust trust = sqLiteAxolotlStore.getFingerprintTrust(fingerprint);
68 return (trust == null) ? SQLiteAxolotlStore.Trust.UNDECIDED : trust;
69 }
70
71 @Nullable
72 public byte[] processReceiving(byte[] encryptedKey) {
73 byte[] plaintext = null;
74 SQLiteAxolotlStore.Trust trust = getTrust();
75 switch (trust) {
76 case INACTIVE:
77 case UNDECIDED:
78 case UNTRUSTED:
79 case TRUSTED:
80 try {
81 try {
82 PreKeyWhisperMessage message = new PreKeyWhisperMessage(encryptedKey);
83 Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage received, new session ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId());
84 String fingerprint = message.getIdentityKey().getFingerprint().replaceAll("\\s", "");
85 if (this.fingerprint != null && !this.fingerprint.equals(fingerprint)) {
86 Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Had session with fingerprint " + this.fingerprint + ", received message with fingerprint " + fingerprint);
87 } else {
88 this.fingerprint = fingerprint;
89 plaintext = cipher.decrypt(message);
90 if (message.getPreKeyId().isPresent()) {
91 preKeyId = message.getPreKeyId().get();
92 }
93 }
94 } catch (InvalidMessageException | InvalidVersionException e) {
95 Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "WhisperMessage received");
96 WhisperMessage message = new WhisperMessage(encryptedKey);
97 plaintext = cipher.decrypt(message);
98 } catch (InvalidKeyException | InvalidKeyIdException | UntrustedIdentityException e) {
99 Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
100 }
101 } catch (LegacyMessageException | InvalidMessageException | DuplicateMessageException | NoSessionException e) {
102 Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
103 }
104
105 if (plaintext != null && trust == SQLiteAxolotlStore.Trust.INACTIVE) {
106 setTrust(SQLiteAxolotlStore.Trust.TRUSTED);
107 }
108
109 break;
110
111 case COMPROMISED:
112 default:
113 // ignore
114 break;
115 }
116 return plaintext;
117 }
118
119 @Nullable
120 public byte[] processSending(@NonNull byte[] outgoingMessage) {
121 SQLiteAxolotlStore.Trust trust = getTrust();
122 if (trust == SQLiteAxolotlStore.Trust.TRUSTED) {
123 CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage);
124 return ciphertextMessage.serialize();
125 } else {
126 return null;
127 }
128 }
129}