AxolotlService.java

  1package eu.siacs.conversations.crypto.axolotl;
  2
  3import android.util.Base64;
  4import android.util.Log;
  5
  6import org.whispersystems.libaxolotl.AxolotlAddress;
  7import org.whispersystems.libaxolotl.DuplicateMessageException;
  8import org.whispersystems.libaxolotl.IdentityKey;
  9import org.whispersystems.libaxolotl.IdentityKeyPair;
 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.SessionBuilder;
 17import org.whispersystems.libaxolotl.SessionCipher;
 18import org.whispersystems.libaxolotl.UntrustedIdentityException;
 19import org.whispersystems.libaxolotl.ecc.Curve;
 20import org.whispersystems.libaxolotl.ecc.ECKeyPair;
 21import org.whispersystems.libaxolotl.ecc.ECPublicKey;
 22import org.whispersystems.libaxolotl.protocol.CiphertextMessage;
 23import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage;
 24import org.whispersystems.libaxolotl.protocol.WhisperMessage;
 25import org.whispersystems.libaxolotl.state.AxolotlStore;
 26import org.whispersystems.libaxolotl.state.PreKeyBundle;
 27import org.whispersystems.libaxolotl.state.PreKeyRecord;
 28import org.whispersystems.libaxolotl.state.SessionRecord;
 29import org.whispersystems.libaxolotl.state.SignedPreKeyRecord;
 30import org.whispersystems.libaxolotl.util.KeyHelper;
 31
 32import java.util.ArrayList;
 33import java.util.HashMap;
 34import java.util.HashSet;
 35import java.util.List;
 36import java.util.Map;
 37import java.util.Random;
 38import java.util.Set;
 39
 40import eu.siacs.conversations.Config;
 41import eu.siacs.conversations.entities.Account;
 42import eu.siacs.conversations.entities.Contact;
 43import eu.siacs.conversations.entities.Conversation;
 44import eu.siacs.conversations.services.XmppConnectionService;
 45import eu.siacs.conversations.xmpp.jid.InvalidJidException;
 46import eu.siacs.conversations.xmpp.jid.Jid;
 47
 48public class AxolotlService {
 49
 50    private final Account account;
 51    private final XmppConnectionService mXmppConnectionService;
 52    private final SQLiteAxolotlStore axolotlStore;
 53    private final SessionMap sessions;
 54    private int ownDeviceId;
 55
 56    public static class SQLiteAxolotlStore implements AxolotlStore {
 57
 58        public static final String PREKEY_TABLENAME = "prekeys";
 59        public static final String SIGNED_PREKEY_TABLENAME = "signed_prekeys";
 60        public static final String SESSION_TABLENAME = "sessions";
 61        public static final String ACCOUNT = "account";
 62        public static final String DEVICE_ID = "device_id";
 63        public static final String ID = "id";
 64        public static final String KEY = "key";
 65        public static final String NAME = "name";
 66        public static final String TRUSTED = "trusted";
 67
 68        public static final String JSONKEY_IDENTITY_KEY_PAIR = "axolotl_key";
 69        public static final String JSONKEY_REGISTRATION_ID = "axolotl_reg_id";
 70        public static final String JSONKEY_CURRENT_PREKEY_ID = "axolotl_cur_prekey_id";
 71
 72        private final Account account;
 73        private final XmppConnectionService mXmppConnectionService;
 74
 75        private final IdentityKeyPair identityKeyPair;
 76        private final int localRegistrationId;
 77        private int currentPreKeyId = 0;
 78
 79
 80        private static IdentityKeyPair generateIdentityKeyPair() {
 81            Log.d(Config.LOGTAG, "Generating axolotl IdentityKeyPair...");
 82            ECKeyPair identityKeyPairKeys = Curve.generateKeyPair();
 83            IdentityKeyPair ownKey = new IdentityKeyPair(new IdentityKey(identityKeyPairKeys.getPublicKey()),
 84                    identityKeyPairKeys.getPrivateKey());
 85            return ownKey;
 86        }
 87
 88        private static int generateRegistrationId() {
 89            Log.d(Config.LOGTAG, "Generating axolotl registration ID...");
 90            int reg_id = KeyHelper.generateRegistrationId(false);
 91            return reg_id;
 92        }
 93
 94        public SQLiteAxolotlStore(Account account, XmppConnectionService service) {
 95            this.account = account;
 96            this.mXmppConnectionService = service;
 97            this.identityKeyPair = loadIdentityKeyPair();
 98            this.localRegistrationId = loadRegistrationId();
 99            this.currentPreKeyId = loadCurrentPreKeyId();
100            for( SignedPreKeyRecord record:loadSignedPreKeys()) {
101                Log.d(Config.LOGTAG, "Got Axolotl signed prekey record:" + record.getId());
102            }
103        }
104
105        public int getCurrentPreKeyId() {
106            return currentPreKeyId;
107        }
108
109        // --------------------------------------
110        // IdentityKeyStore
111        // --------------------------------------
112
113        private IdentityKeyPair loadIdentityKeyPair() {
114            String serializedKey = this.account.getKey(JSONKEY_IDENTITY_KEY_PAIR);
115            IdentityKeyPair ownKey;
116            if( serializedKey != null ) {
117                try {
118                    ownKey = new IdentityKeyPair(Base64.decode(serializedKey,Base64.DEFAULT));
119                    return ownKey;
120                } catch (InvalidKeyException e) {
121                    Log.d(Config.LOGTAG, "Invalid key stored for account " + account.getJid() + ": " + e.getMessage());
122//                    return null;
123                }
124            } //else {
125                Log.d(Config.LOGTAG, "Could not retrieve axolotl key for account " + account.getJid());
126                ownKey = generateIdentityKeyPair();
127                boolean success = this.account.setKey(JSONKEY_IDENTITY_KEY_PAIR, Base64.encodeToString(ownKey.serialize(), Base64.DEFAULT));
128                if(success) {
129                    mXmppConnectionService.databaseBackend.updateAccount(account);
130                } else {
131                    Log.e(Config.LOGTAG, "Failed to write new key to the database!");
132                }
133            //}
134            return ownKey;
135        }
136
137        private int loadRegistrationId() {
138            String regIdString = this.account.getKey(JSONKEY_REGISTRATION_ID);
139            int reg_id;
140            if (regIdString != null) {
141                reg_id = Integer.valueOf(regIdString);
142            } else {
143                Log.d(Config.LOGTAG, "Could not retrieve axolotl registration id for account " + account.getJid());
144                reg_id = generateRegistrationId();
145                boolean success = this.account.setKey(JSONKEY_REGISTRATION_ID,""+reg_id);
146                if(success) {
147                    mXmppConnectionService.databaseBackend.updateAccount(account);
148                } else {
149                    Log.e(Config.LOGTAG, "Failed to write new key to the database!");
150                }
151            }
152            return reg_id;
153        }
154
155        private int loadCurrentPreKeyId() {
156            String regIdString = this.account.getKey(JSONKEY_CURRENT_PREKEY_ID);
157            int reg_id;
158            if (regIdString != null) {
159                reg_id = Integer.valueOf(regIdString);
160            } else {
161                Log.d(Config.LOGTAG, "Could not retrieve current prekey id for account " + account.getJid());
162                reg_id = 0;
163            }
164            return reg_id;
165        }
166
167
168        /**
169         * Get the local client's identity key pair.
170         *
171         * @return The local client's persistent identity key pair.
172         */
173        @Override
174        public IdentityKeyPair getIdentityKeyPair() {
175            return identityKeyPair;
176        }
177
178        /**
179         * Return the local client's registration ID.
180         * <p/>
181         * Clients should maintain a registration ID, a random number
182         * between 1 and 16380 that's generated once at install time.
183         *
184         * @return the local client's registration ID.
185         */
186        @Override
187        public int getLocalRegistrationId() {
188            return localRegistrationId;
189        }
190
191        /**
192         * Save a remote client's identity key
193         * <p/>
194         * Store a remote client's identity key as trusted.
195         *
196         * @param name        The name of the remote client.
197         * @param identityKey The remote client's identity key.
198         */
199        @Override
200        public void saveIdentity(String name, IdentityKey identityKey) {
201            try {
202                Jid contactJid = Jid.fromString(name);
203                Conversation conversation = this.mXmppConnectionService.find(this.account, contactJid);
204                if (conversation != null) {
205                    conversation.getContact().addAxolotlIdentityKey(identityKey);
206                    mXmppConnectionService.updateConversationUi();
207                    mXmppConnectionService.syncRosterToDisk(conversation.getAccount());
208                }
209            } catch (final InvalidJidException e) {
210                Log.e(Config.LOGTAG, "Failed to save identityKey for contact name " + name + ": " + e.toString());
211            }
212        }
213
214        /**
215         * Verify a remote client's identity key.
216         * <p/>
217         * Determine whether a remote client's identity is trusted.  Convention is
218         * that the TextSecure protocol is 'trust on first use.'  This means that
219         * an identity key is considered 'trusted' if there is no entry for the recipient
220         * in the local store, or if it matches the saved key for a recipient in the local
221         * store.  Only if it mismatches an entry in the local store is it considered
222         * 'untrusted.'
223         *
224         * @param name        The name of the remote client.
225         * @param identityKey The identity key to verify.
226         * @return true if trusted, false if untrusted.
227         */
228        @Override
229        public boolean isTrustedIdentity(String name, IdentityKey identityKey) {
230            try {
231                Jid contactJid = Jid.fromString(name);
232                Conversation conversation = this.mXmppConnectionService.find(this.account, contactJid);
233                if (conversation != null) {
234                    List<IdentityKey> trustedKeys = conversation.getContact().getAxolotlIdentityKeys();
235                    return trustedKeys.isEmpty() || trustedKeys.contains(identityKey);
236                } else {
237                    return false;
238                }
239            } catch (final InvalidJidException e) {
240                Log.e(Config.LOGTAG, "Failed to save identityKey for contact name" + name + ": " + e.toString());
241                return false;
242            }
243        }
244
245        // --------------------------------------
246        // SessionStore
247        // --------------------------------------
248
249        /**
250         * Returns a copy of the {@link SessionRecord} corresponding to the recipientId + deviceId tuple,
251         * or a new SessionRecord if one does not currently exist.
252         * <p/>
253         * It is important that implementations return a copy of the current durable information.  The
254         * returned SessionRecord may be modified, but those changes should not have an effect on the
255         * durable session state (what is returned by subsequent calls to this method) without the
256         * store method being called here first.
257         *
258         * @param address The name and device ID of the remote client.
259         * @return a copy of the SessionRecord corresponding to the recipientId + deviceId tuple, or
260         * a new SessionRecord if one does not currently exist.
261         */
262        @Override
263        public SessionRecord loadSession(AxolotlAddress address) {
264            SessionRecord session = mXmppConnectionService.databaseBackend.loadSession(this.account, address);
265            return (session!=null)?session:new SessionRecord();
266        }
267
268        /**
269         * Returns all known devices with active sessions for a recipient
270         *
271         * @param name the name of the client.
272         * @return all known sub-devices with active sessions.
273         */
274        @Override
275        public List<Integer> getSubDeviceSessions(String name) {
276            return mXmppConnectionService.databaseBackend.getSubDeviceSessions(account,
277                    new AxolotlAddress(name,0));
278        }
279
280        /**
281         * Commit to storage the {@link SessionRecord} for a given recipientId + deviceId tuple.
282         *
283         * @param address the address of the remote client.
284         * @param record  the current SessionRecord for the remote client.
285         */
286        @Override
287        public void storeSession(AxolotlAddress address, SessionRecord record) {
288            mXmppConnectionService.databaseBackend.storeSession(account, address, record);
289        }
290
291        /**
292         * Determine whether there is a committed {@link SessionRecord} for a recipientId + deviceId tuple.
293         *
294         * @param address the address of the remote client.
295         * @return true if a {@link SessionRecord} exists, false otherwise.
296         */
297        @Override
298        public boolean containsSession(AxolotlAddress address) {
299            return mXmppConnectionService.databaseBackend.containsSession(account, address);
300        }
301
302        /**
303         * Remove a {@link SessionRecord} for a recipientId + deviceId tuple.
304         *
305         * @param address the address of the remote client.
306         */
307        @Override
308        public void deleteSession(AxolotlAddress address) {
309            mXmppConnectionService.databaseBackend.deleteSession(account, address);
310        }
311
312        /**
313         * Remove the {@link SessionRecord}s corresponding to all devices of a recipientId.
314         *
315         * @param name the name of the remote client.
316         */
317        @Override
318        public void deleteAllSessions(String name) {
319            mXmppConnectionService.databaseBackend.deleteAllSessions(account,
320                    new AxolotlAddress(name, 0));
321        }
322
323        public boolean isTrustedSession(AxolotlAddress address) {
324            return mXmppConnectionService.databaseBackend.isTrustedSession(this.account, address);
325        }
326
327        public void setTrustedSession(AxolotlAddress address, boolean trusted) {
328            mXmppConnectionService.databaseBackend.setTrustedSession(this.account, address,trusted);
329        }
330
331        // --------------------------------------
332        // PreKeyStore
333        // --------------------------------------
334
335        /**
336         * Load a local PreKeyRecord.
337         *
338         * @param preKeyId the ID of the local PreKeyRecord.
339         * @return the corresponding PreKeyRecord.
340         * @throws InvalidKeyIdException when there is no corresponding PreKeyRecord.
341         */
342        @Override
343        public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException {
344            PreKeyRecord record = mXmppConnectionService.databaseBackend.loadPreKey(account, preKeyId);
345            if(record == null) {
346                throw new InvalidKeyIdException("No such PreKeyRecord: " + preKeyId);
347            }
348            return record;
349        }
350
351        /**
352         * Store a local PreKeyRecord.
353         *
354         * @param preKeyId the ID of the PreKeyRecord to store.
355         * @param record   the PreKeyRecord.
356         */
357        @Override
358        public void storePreKey(int preKeyId, PreKeyRecord record) {
359            mXmppConnectionService.databaseBackend.storePreKey(account, record);
360            currentPreKeyId = preKeyId;
361            boolean success = this.account.setKey(JSONKEY_CURRENT_PREKEY_ID,Integer.toString(preKeyId));
362            if(success) {
363                mXmppConnectionService.databaseBackend.updateAccount(account);
364            } else {
365                Log.e(Config.LOGTAG, "Failed to write new prekey id to the database!");
366            }
367        }
368
369        /**
370         * @param preKeyId A PreKeyRecord ID.
371         * @return true if the store has a record for the preKeyId, otherwise false.
372         */
373        @Override
374        public boolean containsPreKey(int preKeyId) {
375            return mXmppConnectionService.databaseBackend.containsPreKey(account, preKeyId);
376        }
377
378        /**
379         * Delete a PreKeyRecord from local storage.
380         *
381         * @param preKeyId The ID of the PreKeyRecord to remove.
382         */
383        @Override
384        public void removePreKey(int preKeyId) {
385            mXmppConnectionService.databaseBackend.deletePreKey(account, preKeyId);
386        }
387
388        // --------------------------------------
389        // SignedPreKeyStore
390        // --------------------------------------
391
392        /**
393         * Load a local SignedPreKeyRecord.
394         *
395         * @param signedPreKeyId the ID of the local SignedPreKeyRecord.
396         * @return the corresponding SignedPreKeyRecord.
397         * @throws InvalidKeyIdException when there is no corresponding SignedPreKeyRecord.
398         */
399        @Override
400        public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException {
401            SignedPreKeyRecord record = mXmppConnectionService.databaseBackend.loadSignedPreKey(account, signedPreKeyId);
402            if(record == null) {
403                throw new InvalidKeyIdException("No such SignedPreKeyRecord: " + signedPreKeyId);
404            }
405            return record;
406        }
407
408        /**
409         * Load all local SignedPreKeyRecords.
410         *
411         * @return All stored SignedPreKeyRecords.
412         */
413        @Override
414        public List<SignedPreKeyRecord> loadSignedPreKeys() {
415            return mXmppConnectionService.databaseBackend.loadSignedPreKeys(account);
416        }
417
418        /**
419         * Store a local SignedPreKeyRecord.
420         *
421         * @param signedPreKeyId the ID of the SignedPreKeyRecord to store.
422         * @param record         the SignedPreKeyRecord.
423         */
424        @Override
425        public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) {
426            mXmppConnectionService.databaseBackend.storeSignedPreKey(account, record);
427        }
428
429        /**
430         * @param signedPreKeyId A SignedPreKeyRecord ID.
431         * @return true if the store has a record for the signedPreKeyId, otherwise false.
432         */
433        @Override
434        public boolean containsSignedPreKey(int signedPreKeyId) {
435            return mXmppConnectionService.databaseBackend.containsSignedPreKey(account, signedPreKeyId);
436        }
437
438        /**
439         * Delete a SignedPreKeyRecord from local storage.
440         *
441         * @param signedPreKeyId The ID of the SignedPreKeyRecord to remove.
442         */
443        @Override
444        public void removeSignedPreKey(int signedPreKeyId) {
445            mXmppConnectionService.databaseBackend.deleteSignedPreKey(account, signedPreKeyId);
446        }
447    }
448
449    public static class XmppAxolotlSession {
450        private SessionCipher cipher;
451        private boolean isTrusted = false;
452        private SQLiteAxolotlStore sqLiteAxolotlStore;
453        private AxolotlAddress remoteAddress;
454
455        public XmppAxolotlSession(SQLiteAxolotlStore store, AxolotlAddress remoteAddress) {
456            this.cipher = new SessionCipher(store, remoteAddress);
457            this.remoteAddress = remoteAddress;
458            this.sqLiteAxolotlStore = store;
459            this.isTrusted = sqLiteAxolotlStore.isTrustedSession(remoteAddress);
460        }
461
462        public void trust() {
463            sqLiteAxolotlStore.setTrustedSession(remoteAddress, true);
464            this.isTrusted = true;
465        }
466
467        public boolean isTrusted() {
468            return this.isTrusted;
469        }
470
471        public byte[] processReceiving(XmppAxolotlMessage.XmppAxolotlMessageHeader incomingHeader) {
472            byte[] plaintext = null;
473            try {
474                try {
475                    PreKeyWhisperMessage message = new PreKeyWhisperMessage(incomingHeader.getContents());
476                    Log.d(Config.LOGTAG,"PreKeyWhisperMessage ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId());
477                    plaintext = cipher.decrypt(message);
478                } catch (InvalidMessageException|InvalidVersionException e) {
479                    WhisperMessage message = new WhisperMessage(incomingHeader.getContents());
480                    plaintext = cipher.decrypt(message);
481                } catch (InvalidKeyException|InvalidKeyIdException| UntrustedIdentityException e) {
482                    Log.d(Config.LOGTAG, "Error decrypting axolotl header: " + e.getMessage());
483                }
484            } catch (LegacyMessageException|InvalidMessageException e) {
485                Log.d(Config.LOGTAG, "Error decrypting axolotl header: " + e.getMessage());
486            } catch (DuplicateMessageException|NoSessionException e) {
487                Log.d(Config.LOGTAG, "Error decrypting axolotl header: " + e.getMessage());
488            }
489            return plaintext;
490        }
491
492        public XmppAxolotlMessage.XmppAxolotlMessageHeader processSending(byte[] outgoingMessage) {
493            CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage);
494            XmppAxolotlMessage.XmppAxolotlMessageHeader header =
495                    new XmppAxolotlMessage.XmppAxolotlMessageHeader(remoteAddress.getDeviceId(),
496                            ciphertextMessage.serialize());
497            return header;
498        }
499    }
500
501    private static class AxolotlAddressMap<T> {
502        protected Map<String, Map<Integer,T>> map;
503        protected final Object MAP_LOCK = new Object();
504
505        public AxolotlAddressMap() {
506            this.map = new HashMap<>();
507        }
508
509        public void put(AxolotlAddress address, T value) {
510            synchronized (MAP_LOCK) {
511                Map<Integer, T> devices = map.get(address.getName());
512                if (devices == null) {
513                    devices = new HashMap<>();
514                    map.put(address.getName(), devices);
515                }
516                devices.put(address.getDeviceId(), value);
517            }
518        }
519
520        public T get(AxolotlAddress address) {
521            synchronized (MAP_LOCK) {
522                Map<Integer, T> devices = map.get(address.getName());
523                if(devices == null) {
524                    return null;
525                }
526                return devices.get(address.getDeviceId());
527            }
528        }
529
530        public Map<Integer, T> getAll(AxolotlAddress address) {
531            synchronized (MAP_LOCK) {
532                Map<Integer, T> devices = map.get(address.getName());
533                if(devices == null) {
534                    return new HashMap<>();
535                }
536                return devices;
537            }
538        }
539
540        public boolean hasAny(AxolotlAddress address) {
541            synchronized (MAP_LOCK) {
542                Map<Integer, T> devices = map.get(address.getName());
543                return devices != null && !devices.isEmpty();
544            }
545        }
546
547
548    }
549
550    private static class SessionMap extends AxolotlAddressMap<XmppAxolotlSession> {
551
552        public SessionMap(SQLiteAxolotlStore store, Account account) {
553            super();
554            this.fillMap(store, account);
555        }
556
557        private void fillMap(SQLiteAxolotlStore store, Account account) {
558            for(Contact contact:account.getRoster().getContacts()){
559                Jid bareJid = contact.getJid().toBareJid();
560                if(bareJid == null) {
561                    continue; // FIXME: handle this?
562                }
563                String address = bareJid.toString();
564                List<Integer> deviceIDs = store.getSubDeviceSessions(address);
565                for(Integer deviceId:deviceIDs) {
566                    AxolotlAddress axolotlAddress = new AxolotlAddress(address, deviceId);
567                    this.put(axolotlAddress, new XmppAxolotlSession(store, axolotlAddress));
568                }
569            }
570        }
571
572    }
573
574    }
575
576    public AxolotlService(Account account, XmppConnectionService connectionService) {
577        this.mXmppConnectionService = connectionService;
578        this.account = account;
579        this.axolotlStore = new SQLiteAxolotlStore(this.account, this.mXmppConnectionService);
580        this.sessions = new SessionMap(axolotlStore, account);
581        this.ownDeviceId = axolotlStore.getLocalRegistrationId();
582    }
583
584    public void trustSession(AxolotlAddress counterpart) {
585        XmppAxolotlSession session = sessions.get(counterpart);
586        if(session != null) {
587            session.trust();
588        }
589    }
590
591    public boolean isTrustedSession(AxolotlAddress counterpart) {
592        XmppAxolotlSession session = sessions.get(counterpart);
593        return session != null && session.isTrusted();
594    }
595
596    private AxolotlAddress getAddressForJid(Jid jid) {
597        return new AxolotlAddress(jid.toString(), 0);
598    }
599
600    private Set<XmppAxolotlSession> findOwnSessions() {
601        AxolotlAddress ownAddress = getAddressForJid(account.getJid());
602        Set<XmppAxolotlSession> ownDeviceSessions = new HashSet<>(this.sessions.getAll(ownAddress).values());
603        return ownDeviceSessions;
604    }
605
606    private Set<XmppAxolotlSession> findSessionsforContact(Contact contact) {
607        AxolotlAddress contactAddress = getAddressForJid(contact.getJid());
608        Set<XmppAxolotlSession> sessions = new HashSet<>(this.sessions.getAll(contactAddress).values());
609        return sessions;
610    }
611
612    private boolean hasAny(Contact contact) {
613        AxolotlAddress contactAddress = getAddressForJid(contact.getJid());
614        return sessions.hasAny(contactAddress);
615    }
616
617    public int getOwnDeviceId() {
618        return ownDeviceId;
619    }
620
621    private void createSessionsIfNeeded(Contact contact) throws NoSessionsCreatedException {
622    }
623
624    public XmppAxolotlMessage processSending(Contact contact, String outgoingMessage) throws NoSessionsCreatedException {
625        XmppAxolotlMessage message = new XmppAxolotlMessage(contact, ownDeviceId, outgoingMessage);
626        createSessionsIfNeeded(contact);
627        Log.d(Config.LOGTAG, "Building axolotl foreign headers...");
628
629        for(XmppAxolotlSession session : findSessionsforContact(contact)) {
630//            if(!session.isTrusted()) {
631                // TODO: handle this properly
632  //              continue;
633    //        }
634            message.addHeader(session.processSending(message.getInnerKey()));
635        }
636        Log.d(Config.LOGTAG, "Building axolotl own headers...");
637        for(XmppAxolotlSession session : findOwnSessions()) {
638    //        if(!session.isTrusted()) {
639                // TODO: handle this properly
640      //          continue;
641        //    }
642            message.addHeader(session.processSending(message.getInnerKey()));
643        }
644
645        return message;
646    }
647
648    public XmppAxolotlMessage.XmppAxolotlPlaintextMessage processReceiving(XmppAxolotlMessage message) {
649        XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = null;
650        AxolotlAddress senderAddress = new AxolotlAddress(message.getContact().getJid().toBareJid().toString(),
651                message.getSenderDeviceId());
652
653        XmppAxolotlSession session = sessions.get(senderAddress);
654        if (session == null) {
655            Log.d(Config.LOGTAG, "No axolotl session found while parsing received message " + message);
656            // TODO: handle this properly
657            session = new XmppAxolotlSession(axolotlStore, senderAddress);
658
659        }
660
661        for(XmppAxolotlMessage.XmppAxolotlMessageHeader header : message.getHeaders()) {
662            if (header.getRecipientDeviceId() == ownDeviceId) {
663                Log.d(Config.LOGTAG, "Found axolotl header matching own device ID, processing...");
664                byte[] payloadKey = session.processReceiving(header);
665                if (payloadKey != null) {
666                    Log.d(Config.LOGTAG, "Got payload key from axolotl header. Decrypting message...");
667                    plaintextMessage = message.decrypt(session, payloadKey);
668                }
669            }
670        }
671
672        return plaintextMessage;
673    }
674}