OmemoStore.java

  1. /**
  2.  *
  3.  * Copyright 2017 Paul Schaub
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smackx.omemo;

  18. import static org.jivesoftware.smackx.omemo.util.OmemoConstants.PRE_KEY_COUNT_PER_BUNDLE;

  19. import java.io.IOException;
  20. import java.util.Date;
  21. import java.util.HashMap;
  22. import java.util.Map;
  23. import java.util.SortedSet;
  24. import java.util.TreeMap;
  25. import java.util.logging.Level;
  26. import java.util.logging.Logger;

  27. import org.jivesoftware.smack.SmackException;

  28. import org.jivesoftware.smackx.omemo.element.OmemoBundleElement_VAxolotl;
  29. import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement;
  30. import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
  31. import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
  32. import org.jivesoftware.smackx.omemo.exceptions.NoIdentityKeyException;
  33. import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
  34. import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
  35. import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
  36. import org.jivesoftware.smackx.omemo.util.OmemoKeyUtil;

  37. import org.jxmpp.jid.BareJid;

  38. /**
  39.  * Class that presents some methods that are used to load/generate/store keys and session data needed for OMEMO.
  40.  *
  41.  * @param <T_IdKeyPair> IdentityKeyPair class
  42.  * @param <T_IdKey>     IdentityKey class
  43.  * @param <T_PreKey>    PreKey class
  44.  * @param <T_SigPreKey> SignedPreKey class
  45.  * @param <T_Sess>      Session class
  46.  * @param <T_Addr>      Address class
  47.  * @param <T_ECPub>     Elliptic Curve PublicKey class
  48.  * @param <T_Bundle>    Bundle class
  49.  * @param <T_Ciph>      Cipher class
  50.  *
  51.  * @author Paul Schaub
  52.  */
  53. public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> {
  54.     private static final Logger LOGGER = Logger.getLogger(OmemoStore.class.getName());

  55.     /**
  56.      * Create a new OmemoStore.
  57.      */
  58.     public OmemoStore() {

  59.     }

  60.     /**
  61.      * Returns a sorted set of all the deviceIds, the localUser has had data stored under in the store.
  62.      * Basically this returns the deviceIds of all "accounts" of localUser, which are known to the store.
  63.      *
  64.      * @param localUser BareJid of the user.
  65.      * @return set of deviceIds with available data.
  66.      */
  67.     public abstract SortedSet<Integer> localDeviceIdsOf(BareJid localUser);

  68.     /**
  69.      * Check, if our freshly generated deviceId is available (unique) in our deviceList.
  70.      *
  71.      * @param userDevice    our current device.
  72.      * @param id            deviceId to check for.
  73.      * @return true if list did not contain our id, else false
  74.      *
  75.      * @throws IOException if an I/O error occurred.
  76.      */
  77.     boolean isAvailableDeviceId(OmemoDevice userDevice, int id) throws IOException {
  78.         LOGGER.log(Level.INFO, "Check if id " + id + " is available...");

  79.         // Lookup local cached device list
  80.         BareJid ownJid = userDevice.getJid();
  81.         OmemoCachedDeviceList cachedDeviceList;

  82.         cachedDeviceList = loadCachedDeviceList(userDevice, ownJid);

  83.         if (cachedDeviceList == null) {
  84.             cachedDeviceList = new OmemoCachedDeviceList();
  85.         }
  86.         // Does the list already contain that id?
  87.         return !cachedDeviceList.contains(id);
  88.     }

  89.     /**
  90.      * Merge the received OmemoDeviceListElement with the one we already have. If we had none, the received one is saved.
  91.      *
  92.      * @param userDevice our OmemoDevice.
  93.      * @param contact Contact we received the list from.
  94.      * @param list    List we received.
  95.      *
  96.      * @throws IOException if an I/O error occurred.
  97.      */
  98.     OmemoCachedDeviceList mergeCachedDeviceList(OmemoDevice userDevice, BareJid contact, OmemoDeviceListElement list) throws IOException {
  99.         OmemoCachedDeviceList cached = loadCachedDeviceList(userDevice, contact);

  100.         if (cached == null) {
  101.             cached = new OmemoCachedDeviceList();
  102.         }

  103.         if (list == null) {
  104.             return cached;
  105.         }

  106.         for (int devId : list.getDeviceIds()) {
  107.             if (!cached.contains(devId)) {
  108.                 setDateOfLastDeviceIdPublication(userDevice, new OmemoDevice(contact, devId), new Date());
  109.             }
  110.         }

  111.         cached.merge(list.getDeviceIds());
  112.         storeCachedDeviceList(userDevice, contact, cached);

  113.         return cached;
  114.     }

  115.     /**
  116.      * Renew our singed preKey. This should be done once every 7-14 days.
  117.      * The old signed PreKey should be kept for around a month or so (look it up in the XEP).
  118.      *
  119.      * @param userDevice our OmemoDevice.
  120.      *
  121.      * @throws CorruptedOmemoKeyException when our identityKey is invalid.
  122.      * @throws IOException if an I/O error occurred.
  123.      * @throws IllegalStateException when our IdentityKeyPair is null.
  124.      */
  125.     void changeSignedPreKey(OmemoDevice userDevice)
  126.             throws CorruptedOmemoKeyException, IOException {

  127.         T_IdKeyPair idKeyPair = loadOmemoIdentityKeyPair(userDevice);
  128.         if (idKeyPair == null) {
  129.             throw new IllegalStateException("Our IdentityKeyPair is null.");
  130.         }

  131.         TreeMap<Integer, T_SigPreKey> signedPreKeys = loadOmemoSignedPreKeys(userDevice);
  132.         if (signedPreKeys.size() == 0) {
  133.             T_SigPreKey newKey = generateOmemoSignedPreKey(idKeyPair, 1);
  134.             storeOmemoSignedPreKey(userDevice, 1, newKey);
  135.         } else {
  136.             int lastId = signedPreKeys.lastKey();
  137.             T_SigPreKey newKey = generateOmemoSignedPreKey(idKeyPair, lastId + 1);
  138.             storeOmemoSignedPreKey(userDevice, lastId + 1, newKey);
  139.         }

  140.         setDateOfLastSignedPreKeyRenewal(userDevice, new Date());
  141.         removeOldSignedPreKeys(userDevice);
  142.     }

  143.     /**
  144.      * Remove the oldest signedPreKey until there are only MAX_NUMBER_OF_STORED_SIGNED_PREKEYS left.
  145.      *
  146.      * @param userDevice our OmemoDevice.
  147.      *
  148.      * @throws IOException if an I/O error occurred.
  149.      */
  150.     private void removeOldSignedPreKeys(OmemoDevice userDevice) throws IOException {
  151.         if (OmemoConfiguration.getMaxNumberOfStoredSignedPreKeys() <= 0) {
  152.             return;
  153.         }

  154.         TreeMap<Integer, T_SigPreKey> signedPreKeys = loadOmemoSignedPreKeys(userDevice);

  155.         for (int i = 0; i < signedPreKeys.keySet().size() - OmemoConfiguration.getMaxNumberOfStoredSignedPreKeys(); i++) {
  156.             int keyId = signedPreKeys.firstKey();
  157.             LOGGER.log(Level.INFO, "Remove signedPreKey " + keyId + ".");
  158.             removeOmemoSignedPreKey(userDevice, i);
  159.             signedPreKeys = loadOmemoSignedPreKeys(userDevice);
  160.         }
  161.     }

  162.     /**
  163.      * Pack a OmemoBundleElement containing our key material.
  164.      *
  165.      * @param userDevice our OmemoDevice.
  166.      * @return OMEMO bundle element
  167.      *
  168.      * @throws CorruptedOmemoKeyException when a key could not be loaded
  169.      * @throws IOException if an I/O error occurred.
  170.      */
  171.     OmemoBundleElement_VAxolotl packOmemoBundle(OmemoDevice userDevice)
  172.             throws CorruptedOmemoKeyException, IOException {

  173.         int currentSignedPreKeyId = loadCurrentOmemoSignedPreKeyId(userDevice);
  174.         T_SigPreKey currentSignedPreKey = loadOmemoSignedPreKeys(userDevice).get(currentSignedPreKeyId);

  175.         return new OmemoBundleElement_VAxolotl(
  176.                 currentSignedPreKeyId,
  177.                 keyUtil().signedPreKeyPublicForBundle(currentSignedPreKey),
  178.                 keyUtil().signedPreKeySignatureFromKey(currentSignedPreKey),
  179.                 keyUtil().identityKeyForBundle(keyUtil().identityKeyFromPair(loadOmemoIdentityKeyPair(userDevice))),
  180.                 keyUtil().preKeyPublicKeysForBundle(loadOmemoPreKeys(userDevice))
  181.         );
  182.     }

  183.     /**
  184.      * Replenish our supply of keys. If we are missing any type of keys, generate them fresh.
  185.      *
  186.      * @param userDevice our own OMEMO device
  187.      *
  188.      * @throws CorruptedOmemoKeyException if the OMEMO key is corrupted.
  189.      * @throws IOException if an I/O error occurred.
  190.      */
  191.     public void replenishKeys(OmemoDevice userDevice)
  192.             throws CorruptedOmemoKeyException, IOException {

  193.         T_IdKeyPair identityKeyPair = loadOmemoIdentityKeyPair(userDevice);
  194.         if (identityKeyPair == null) {
  195.             identityKeyPair = generateOmemoIdentityKeyPair();
  196.             storeOmemoIdentityKeyPair(userDevice, identityKeyPair);
  197.         }

  198.         TreeMap<Integer, T_SigPreKey> signedPreKeys = loadOmemoSignedPreKeys(userDevice);
  199.         if (signedPreKeys.size() == 0) {
  200.             changeSignedPreKey(userDevice);
  201.         }

  202.         TreeMap<Integer, T_PreKey> preKeys = loadOmemoPreKeys(userDevice);
  203.         int newKeysCount = PRE_KEY_COUNT_PER_BUNDLE - preKeys.size();
  204.         int startId = preKeys.size() == 0 ? 0 : preKeys.lastKey();

  205.         if (newKeysCount > 0) {
  206.             TreeMap<Integer, T_PreKey> newKeys = generateOmemoPreKeys(startId + 1, newKeysCount);
  207.             storeOmemoPreKeys(userDevice, newKeys);
  208.         }
  209.     }

  210.     // *sigh*

  211.     /**
  212.      * Generate a new IdentityKeyPair. We should always have only one pair and usually keep this for a long time.
  213.      *
  214.      * @return a fresh identityKeyPair
  215.      */
  216.     public T_IdKeyPair generateOmemoIdentityKeyPair() {
  217.         return keyUtil().generateOmemoIdentityKeyPair();
  218.     }

  219.     /**
  220.      * Load our identityKeyPair from storage.
  221.      * Return null, if we have no identityKeyPair.
  222.      *
  223.      * @param userDevice our OmemoDevice.
  224.      * @return loaded identityKeyPair
  225.      *
  226.      * @throws CorruptedOmemoKeyException Thrown, if the stored key is damaged (*hands up* not my fault!)
  227.      * @throws IOException if an I/O error occurred.
  228.      */
  229.     public abstract T_IdKeyPair loadOmemoIdentityKeyPair(OmemoDevice userDevice)
  230.             throws CorruptedOmemoKeyException, IOException;

  231.     /**
  232.      * Store our identityKeyPair in storage. It would be a cool feature, if the key could be stored in a encrypted
  233.      * database or something similar.
  234.      *
  235.      * @param userDevice our OmemoDevice.
  236.      * @param identityKeyPair identityKeyPair
  237.      *
  238.      * @throws IOException if an I/O error occurred.
  239.      */
  240.     public abstract void storeOmemoIdentityKeyPair(OmemoDevice userDevice, T_IdKeyPair identityKeyPair) throws IOException;

  241.     /**
  242.      * Remove the identityKeyPair of a user.
  243.      *
  244.      * @param userDevice our device.
  245.      */
  246.     public abstract void removeOmemoIdentityKeyPair(OmemoDevice userDevice);

  247.     /**
  248.      * Load the public identityKey of a device.
  249.      *
  250.      * @param userDevice our OmemoDevice.
  251.      * @param contactsDevice the device of which we want to load the identityKey.
  252.      * @return loaded identityKey
  253.      *
  254.      * @throws CorruptedOmemoKeyException when the key in question is corrupted and cant be deserialized.
  255.      * @throws IOException if an I/O error occurred.
  256.      */
  257.     public abstract T_IdKey loadOmemoIdentityKey(OmemoDevice userDevice, OmemoDevice contactsDevice)
  258.             throws CorruptedOmemoKeyException, IOException;

  259.     /**
  260.      * Store the public identityKey of the device.
  261.      *
  262.      * @param userDevice our OmemoDevice.
  263.      * @param contactsDevice device.
  264.      * @param contactsKey    identityKey belonging to the contactsDevice.
  265.      *
  266.      * @throws IOException if an I/O error occurred.
  267.      */
  268.     public abstract void storeOmemoIdentityKey(OmemoDevice userDevice, OmemoDevice contactsDevice, T_IdKey contactsKey) throws IOException;

  269.     /**
  270.      * Removes the identityKey of a device.
  271.      *
  272.      * @param userDevice our omemoDevice.
  273.      * @param contactsDevice device of which we want to delete the identityKey.
  274.      */
  275.     public abstract void removeOmemoIdentityKey(OmemoDevice userDevice, OmemoDevice contactsDevice);

  276.     /**
  277.      * Store the number of messages we sent to a device since we last received a message back.
  278.      * This counter gets reset to 0 whenever we receive a message from the contacts device.
  279.      *
  280.      * @param userDevice our omemoDevice.
  281.      * @param contactsDevice device of which we want to set the message counter.
  282.      * @param counter counter value.
  283.      *
  284.      * @throws IOException if an I/O error occurred.
  285.      */
  286.     public abstract void storeOmemoMessageCounter(OmemoDevice userDevice, OmemoDevice contactsDevice, int counter) throws IOException;

  287.     /**
  288.      * Return the current value of the message counter.
  289.      * This counter represents the number of message we sent to the contactsDevice without getting a reply back.
  290.      * The default value for this counter is 0.
  291.      *
  292.      * @param userDevice our omemoDevice
  293.      * @param contactsDevice device of which we want to get the message counter.
  294.      * @return counter value.
  295.      *
  296.      * @throws IOException if an I/O error occurred.
  297.      */
  298.     public abstract int loadOmemoMessageCounter(OmemoDevice userDevice, OmemoDevice contactsDevice) throws IOException;

  299.     /**
  300.      * Set the date of the last message that was received from a device.
  301.      *
  302.      * @param userDevice omemoManager of our device.
  303.      * @param contactsDevice device in question
  304.      * @param date date of the last received message
  305.      *
  306.      * @throws IOException if an I/O error occurred.
  307.      */
  308.     public abstract void setDateOfLastReceivedMessage(OmemoDevice userDevice, OmemoDevice contactsDevice, Date date) throws IOException;

  309.     /**
  310.      * Return the date of the last message that was received from device 'from'.
  311.      *
  312.      * @param userDevice our OmemoDevice.
  313.      * @param contactsDevice device in question
  314.      * @return date if existent, null
  315.      *
  316.      * @throws IOException if an I/O error occurred.
  317.      */
  318.     public abstract Date getDateOfLastReceivedMessage(OmemoDevice userDevice, OmemoDevice contactsDevice) throws IOException;

  319.     /**
  320.      * Set the date of the last time the deviceId was published. This method only gets called, when the deviceId
  321.      * was inactive/non-existent before it was published.
  322.      *
  323.      * @param userDevice our OmemoDevice
  324.      * @param contactsDevice OmemoDevice in question
  325.      * @param date date of the last publication after not being published
  326.      *
  327.      * @throws IOException if an I/O error occurred.
  328.      */
  329.     public abstract void setDateOfLastDeviceIdPublication(OmemoDevice userDevice, OmemoDevice contactsDevice, Date date) throws IOException;

  330.     /**
  331.      * Return the date of the last time the deviceId was published after previously being not published.
  332.      * (Point in time, where the status of the deviceId changed from inactive/non-existent to active).
  333.      *
  334.      * @param userDevice our OmemoDevice
  335.      * @param contactsDevice OmemoDevice in question
  336.      * @return date of the last publication after not being published
  337.      *
  338.      * @throws IOException if an I/O error occurred.
  339.      */
  340.     public abstract Date getDateOfLastDeviceIdPublication(OmemoDevice userDevice, OmemoDevice contactsDevice) throws IOException;

  341.     /**
  342.      * Set the date of the last time the signed preKey was renewed.
  343.      *
  344.      * @param userDevice our OmemoDevice.
  345.      * @param date date
  346.      *
  347.      * @throws IOException if an I/O error occurred.
  348.      */
  349.     public abstract void setDateOfLastSignedPreKeyRenewal(OmemoDevice userDevice, Date date) throws IOException;

  350.     /**
  351.      * Get the date of the last time the signed preKey was renewed.
  352.      *
  353.      * @param userDevice our OmemoDevice.
  354.      * @return date if existent, otherwise null
  355.      *
  356.      * @throws IOException if an I/O error occurred.
  357.      */
  358.     public abstract Date getDateOfLastSignedPreKeyRenewal(OmemoDevice userDevice) throws IOException;

  359.     /**
  360.      * Generate 'count' new PreKeys beginning with id 'startId'.
  361.      * These preKeys are published and can be used by contacts to establish sessions with us.
  362.      *
  363.      * @param startId start id
  364.      * @param count   how many keys do we want to generate
  365.      * @return Map of new preKeys
  366.      */
  367.     public TreeMap<Integer, T_PreKey> generateOmemoPreKeys(int startId, int count) {
  368.         return keyUtil().generateOmemoPreKeys(startId, count);
  369.     }

  370.     /**
  371.      * Load the preKey with id 'preKeyId' from storage.
  372.      *
  373.      * @param userDevice our OmemoDevice.
  374.      * @param preKeyId id of the key to be loaded
  375.      * @return loaded preKey
  376.      *
  377.      * @throws IOException if an I/O error occurred.
  378.      */
  379.     public abstract T_PreKey loadOmemoPreKey(OmemoDevice userDevice, int preKeyId) throws IOException;

  380.     /**
  381.      * Store a PreKey in storage.
  382.      *
  383.      * @param userDevice our OmemoDevice.
  384.      * @param preKeyId id of the key
  385.      * @param preKey   key
  386.      *
  387.      * @throws IOException if an I/O error occurred.
  388.      */
  389.     public abstract void storeOmemoPreKey(OmemoDevice userDevice, int preKeyId, T_PreKey preKey) throws IOException;

  390.     /**
  391.      * Store a whole bunch of preKeys.
  392.      *
  393.      * @param userDevice our OmemoDevice.
  394.      * @param preKeyHashMap HashMap of preKeys
  395.      *
  396.      * @throws IOException if an I/O error occurred.
  397.      */
  398.     public void storeOmemoPreKeys(OmemoDevice userDevice, TreeMap<Integer, T_PreKey> preKeyHashMap) throws IOException {
  399.         for (Map.Entry<Integer, T_PreKey> entry : preKeyHashMap.entrySet()) {
  400.             storeOmemoPreKey(userDevice, entry.getKey(), entry.getValue());
  401.         }
  402.     }

  403.     /**
  404.      * Remove a preKey from storage. This is called, when a contact used one of our preKeys to establish a session
  405.      * with us.
  406.      *
  407.      * @param userDevice our OmemoDevice.
  408.      * @param preKeyId id of the used key that will be deleted
  409.      */
  410.     public abstract void removeOmemoPreKey(OmemoDevice userDevice, int preKeyId);

  411.     /**
  412.      * Return all our current OmemoPreKeys.
  413.      *
  414.      * @param userDevice our OmemoDevice.
  415.      * @return Map containing our preKeys
  416.      *
  417.      * @throws IOException if an I/O error occurred.
  418.      */
  419.     public abstract TreeMap<Integer, T_PreKey> loadOmemoPreKeys(OmemoDevice userDevice) throws IOException;

  420.     /**
  421.      * Return the signedPreKey with the id 'singedPreKeyId'.
  422.      *
  423.      * @param userDevice our OmemoDevice.
  424.      * @param signedPreKeyId id of the key
  425.      * @return loaded signed preKey
  426.      *
  427.      * @throws IOException if an I/O error occurred.
  428.      */
  429.     public abstract T_SigPreKey loadOmemoSignedPreKey(OmemoDevice userDevice, int signedPreKeyId) throws IOException;

  430.     public int loadCurrentOmemoSignedPreKeyId(OmemoDevice userDevice) throws IOException {
  431.         return loadOmemoSignedPreKeys(userDevice).lastKey();
  432.     }

  433.     /**
  434.      * Load all our signed PreKeys.
  435.      *
  436.      * @param userDevice our OmemoDevice.
  437.      * @return HashMap of our singedPreKeys
  438.      *
  439.      * @throws IOException if an I/O error occurred.
  440.      */
  441.     public abstract TreeMap<Integer, T_SigPreKey> loadOmemoSignedPreKeys(OmemoDevice userDevice) throws IOException;

  442.     /**
  443.      * Generate a new signed preKey.
  444.      *
  445.      * @param identityKeyPair identityKeyPair used to sign the preKey
  446.      * @param signedPreKeyId  id that the preKey will have
  447.      * @return a fresh signedPreKey
  448.      *
  449.      * @throws CorruptedOmemoKeyException when something goes wrong
  450.      */
  451.     public T_SigPreKey generateOmemoSignedPreKey(T_IdKeyPair identityKeyPair, int signedPreKeyId)
  452.             throws CorruptedOmemoKeyException {
  453.         return keyUtil().generateOmemoSignedPreKey(identityKeyPair, signedPreKeyId);
  454.     }

  455.     /**
  456.      * Store a signedPreKey in storage.
  457.      *
  458.      * @param userDevice our OmemoDevice.
  459.      * @param signedPreKeyId id of the signedPreKey
  460.      * @param signedPreKey   the key itself
  461.      *
  462.      * @throws IOException if an I/O error occurred.
  463.      */
  464.     public abstract void storeOmemoSignedPreKey(OmemoDevice userDevice, int signedPreKeyId, T_SigPreKey signedPreKey) throws IOException;

  465.     /**
  466.      * Remove a signedPreKey from storage.
  467.      *
  468.      * @param userDevice our OmemoDevice.
  469.      * @param signedPreKeyId id of the key that will be removed
  470.      */
  471.     public abstract void removeOmemoSignedPreKey(OmemoDevice userDevice, int signedPreKeyId);

  472.     /**
  473.      * Load the crypto-lib specific session object of the device from storage.
  474.      *
  475.      * @param userDevice our OmemoDevice.
  476.      * @param contactsDevice device whose session we want to load
  477.      * @return crypto related session
  478.      *
  479.      * @throws IOException if an I/O error occurred.
  480.      */
  481.     public abstract T_Sess loadRawSession(OmemoDevice userDevice, OmemoDevice contactsDevice) throws IOException;

  482.     /**
  483.      * Load all crypto-lib specific session objects of contact 'contact'.
  484.      *
  485.      * @param userDevice our OmemoDevice.
  486.      * @param contact BareJid of the contact we want to get all sessions from
  487.      * @return TreeMap of deviceId and sessions of the contact
  488.      *
  489.      * @throws IOException if an I/O error occurred.
  490.      */
  491.     public abstract HashMap<Integer, T_Sess> loadAllRawSessionsOf(OmemoDevice userDevice, BareJid contact) throws IOException;

  492.     /**
  493.      * Store a crypto-lib specific session to storage.
  494.      *
  495.      * @param userDevice our OmemoDevice.
  496.      * @param contactsDevice  OmemoDevice whose session we want to store
  497.      * @param session session
  498.      *
  499.      * @throws IOException if an I/O error occurred.
  500.      */
  501.     public abstract void storeRawSession(OmemoDevice userDevice, OmemoDevice contactsDevice, T_Sess session) throws IOException;

  502.     /**
  503.      * Remove a crypto-lib specific session from storage.
  504.      *
  505.      * @param userDevice our OmemoDevice.
  506.      * @param contactsDevice device whose session we want to delete
  507.      */
  508.     public abstract void removeRawSession(OmemoDevice userDevice, OmemoDevice contactsDevice);

  509.     /**
  510.      * Remove all crypto-lib specific session of a contact.
  511.      *
  512.      * @param userDevice our OmemoDevice.
  513.      * @param contact BareJid of the contact
  514.      */
  515.     public abstract void removeAllRawSessionsOf(OmemoDevice userDevice, BareJid contact);

  516.     /**
  517.      * Return true, if we have a session with the device, otherwise false.
  518.      * Hint for Signal: Do not try 'return getSession() != null' since this will create a new session.
  519.      *
  520.      * @param userDevice our OmemoDevice.
  521.      * @param contactsDevice device
  522.      * @return true if we have session, otherwise false
  523.      */
  524.     public abstract boolean containsRawSession(OmemoDevice userDevice, OmemoDevice contactsDevice);

  525.     /**
  526.      * Load a list of deviceIds from contact 'contact' from the local cache.
  527.      *
  528.      * @param userDevice our OmemoDevice.
  529.      * @param contact contact we want to get the deviceList of
  530.      * @return CachedDeviceList of the contact
  531.      *
  532.      * @throws IOException if an I/O error occurred.
  533.      */
  534.     public abstract OmemoCachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact) throws IOException;

  535.     /**
  536.      * Load a list of deviceIds from our own devices.
  537.      *
  538.      * @param userDevice our own OMEMO device
  539.      * @return the cached OMEMO device list.
  540.      *
  541.      * @throws IOException if an I/O error occurred.
  542.      */
  543.     public OmemoCachedDeviceList loadCachedDeviceList(OmemoDevice userDevice) throws IOException {
  544.         return loadCachedDeviceList(userDevice, userDevice.getJid());
  545.     }

  546.     /**
  547.      * Store the DeviceList of the contact in local storage.
  548.      * See this as a cache.
  549.      *
  550.      * @param userDevice our OmemoDevice.
  551.      * @param contact    Contact
  552.      * @param contactsDeviceList list of the contacts devices' ids.
  553.      *
  554.      * @throws IOException if an I/O error occurred.
  555.      */
  556.     public abstract void storeCachedDeviceList(OmemoDevice userDevice,
  557.                                                BareJid contact,
  558.                                                OmemoCachedDeviceList contactsDeviceList) throws IOException;

  559.     /**
  560.      * Delete this device's IdentityKey, PreKeys, SignedPreKeys and Sessions.
  561.      *
  562.      * @param userDevice our OmemoDevice.
  563.      */
  564.     public abstract void purgeOwnDeviceKeys(OmemoDevice userDevice);

  565.     /**
  566.      * Return a concrete KeyUtil object that we can use as a utility to create keys etc.
  567.      *
  568.      * @return KeyUtil object
  569.      */
  570.     public abstract OmemoKeyUtil<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_ECPub, T_Bundle> keyUtil();

  571.     /**
  572.      * Return our identityKeys fingerprint.
  573.      *
  574.      * @param userDevice our OmemoDevice.
  575.      * @return fingerprint of our identityKeyPair
  576.      *
  577.      * @throws CorruptedOmemoKeyException if the identityKey of userDevice is corrupted.
  578.      * @throws IOException if an I/O error occurred.
  579.      */
  580.     public OmemoFingerprint getFingerprint(OmemoDevice userDevice)
  581.             throws CorruptedOmemoKeyException, IOException {

  582.         T_IdKeyPair keyPair = loadOmemoIdentityKeyPair(userDevice);
  583.         if (keyPair == null) {
  584.             return null;
  585.         }

  586.         return keyUtil().getFingerprintOfIdentityKey(keyUtil().identityKeyFromPair(keyPair));
  587.     }

  588.     /**
  589.      * Return the fingerprint of the identityKey belonging to contactsDevice.
  590.      *
  591.      * @param userDevice our OmemoDevice.
  592.      * @param contactsDevice OmemoDevice we want to have the fingerprint for.
  593.      * @return fingerprint of the userDevices IdentityKey.
  594.      *
  595.      * @throws CorruptedOmemoKeyException if the IdentityKey is corrupted.
  596.      * @throws NoIdentityKeyException if no IdentityKey for contactsDevice has been found locally.
  597.      * @throws IOException if an I/O error occurred.
  598.      */
  599.     public OmemoFingerprint getFingerprint(OmemoDevice userDevice, OmemoDevice contactsDevice)
  600.             throws CorruptedOmemoKeyException, NoIdentityKeyException, IOException {

  601.         T_IdKey identityKey = loadOmemoIdentityKey(userDevice, contactsDevice);
  602.         if (identityKey == null) {
  603.             throw new NoIdentityKeyException(contactsDevice);
  604.         }
  605.         return keyUtil().getFingerprintOfIdentityKey(identityKey);
  606.     }

  607.     /**
  608.      * Return the fingerprint of the given devices announced identityKey.
  609.      * If we have no local copy of the identityKey of the contact, build a fresh session in order to get the key.
  610.      *
  611.      * @param managerGuard authenticated OmemoManager
  612.      * @param contactsDevice OmemoDevice we want to get the fingerprint from
  613.      * @return fingerprint of the contacts OMEMO device
  614.      *
  615.      * @throws CannotEstablishOmemoSessionException If we have no local copy of the identityKey of the contact
  616.      *                                              and are unable to build a fresh session
  617.      * @throws CorruptedOmemoKeyException           If the identityKey we have of the contact is corrupted
  618.      * @throws SmackException.NotConnectedException if the XMPP connection is not connected.
  619.      * @throws InterruptedException if the calling thread was interrupted.
  620.      * @throws SmackException.NoResponseException if there was no response from the remote entity.
  621.      * @throws IOException if an I/O error occurred.
  622.      */
  623.     public OmemoFingerprint getFingerprintAndMaybeBuildSession(OmemoManager.LoggedInOmemoManager managerGuard, OmemoDevice contactsDevice)
  624.             throws CannotEstablishOmemoSessionException, CorruptedOmemoKeyException,
  625.             SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, IOException {
  626.         OmemoManager omemoManager = managerGuard.get();

  627.         // Load identityKey
  628.         T_IdKey identityKey = loadOmemoIdentityKey(omemoManager.getOwnDevice(), contactsDevice);
  629.         if (identityKey == null) {
  630.             // Key cannot be loaded. Maybe it doesn't exist. Fetch a bundle to get it...
  631.             OmemoService.getInstance().buildFreshSessionWithDevice(omemoManager.getConnection(),
  632.                     omemoManager.getOwnDevice(), contactsDevice);
  633.         }

  634.         // Load identityKey again
  635.         identityKey = loadOmemoIdentityKey(omemoManager.getOwnDevice(), contactsDevice);
  636.         if (identityKey == null) {
  637.             return null;
  638.         }

  639.         return keyUtil().getFingerprintOfIdentityKey(identityKey);
  640.     }
  641. }