SignalOmemoStoreConnector.java

  1. /**
  2.  *
  3.  * Copyright 2017 Paul Schaub
  4.  *
  5.  * This file is part of smack-omemo-signal.
  6.  *
  7.  * smack-omemo-signal is free software; you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License as published by
  9.  * the Free Software Foundation; either version 3 of the License, or
  10.  * (at your option) any later version.
  11.  *
  12.  * This program is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.  * GNU General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU General Public License
  18.  * along with this program; if not, write to the Free Software Foundation,
  19.  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
  20.  */
  21. package org.jivesoftware.smackx.omemo.signal;

  22. import java.io.IOException;
  23. import java.util.ArrayList;
  24. import java.util.List;
  25. import java.util.TreeMap;
  26. import java.util.logging.Level;
  27. import java.util.logging.Logger;

  28. import org.jivesoftware.smackx.omemo.OmemoManager;
  29. import org.jivesoftware.smackx.omemo.OmemoStore;
  30. import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
  31. import org.jivesoftware.smackx.omemo.internal.OmemoDevice;

  32. import org.jxmpp.jid.BareJid;
  33. import org.jxmpp.jid.impl.JidCreate;
  34. import org.jxmpp.stringprep.XmppStringprepException;
  35. import org.whispersystems.libsignal.IdentityKey;
  36. import org.whispersystems.libsignal.IdentityKeyPair;
  37. import org.whispersystems.libsignal.InvalidKeyIdException;
  38. import org.whispersystems.libsignal.SessionCipher;
  39. import org.whispersystems.libsignal.SignalProtocolAddress;
  40. import org.whispersystems.libsignal.ecc.ECPublicKey;
  41. import org.whispersystems.libsignal.state.IdentityKeyStore;
  42. import org.whispersystems.libsignal.state.PreKeyBundle;
  43. import org.whispersystems.libsignal.state.PreKeyRecord;
  44. import org.whispersystems.libsignal.state.PreKeyStore;
  45. import org.whispersystems.libsignal.state.SessionRecord;
  46. import org.whispersystems.libsignal.state.SessionStore;
  47. import org.whispersystems.libsignal.state.SignedPreKeyRecord;
  48. import org.whispersystems.libsignal.state.SignedPreKeyStore;

  49. /**
  50.  * Class that adapts libsignal-protocol-java's Store classes to the OmemoStore class.
  51.  *
  52.  * @author Paul Schaub
  53.  */
  54. public class SignalOmemoStoreConnector
  55.         implements IdentityKeyStore, SessionStore, PreKeyStore, SignedPreKeyStore {

  56.     private static final Logger LOGGER = Logger.getLogger(SignalOmemoStoreConnector.class.getName());

  57.     private final OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord,
  58.             SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher>
  59.             omemoStore;
  60.     private final OmemoManager omemoManager;

  61.     public SignalOmemoStoreConnector(OmemoManager omemoManager, OmemoStore<IdentityKeyPair,
  62.             IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey,
  63.             PreKeyBundle, SessionCipher> store) {
  64.         this.omemoManager = omemoManager;
  65.         this.omemoStore = store;
  66.     }

  67.     OmemoDevice getOurDevice() {
  68.         return omemoManager.getOwnDevice();
  69.     }

  70.     @Override
  71.     public IdentityKeyPair getIdentityKeyPair() {
  72.         try {
  73.             return omemoStore.loadOmemoIdentityKeyPair(getOurDevice());
  74.         } catch (CorruptedOmemoKeyException | IOException e) {
  75.             LOGGER.log(Level.SEVERE, "IdentityKeyPair seems to be invalid.", e);
  76.             return null;
  77.         }
  78.     }

  79.     /**
  80.      * The OMEMO protocol does not make use of a local registration ID, so we can simply return 0 here.
  81.      *
  82.      * @return local registration id.
  83.      */
  84.     @Override
  85.     public int getLocalRegistrationId() {
  86.         return 0;
  87.     }

  88.     @Override
  89.     public boolean saveIdentity(SignalProtocolAddress signalProtocolAddress, IdentityKey identityKey) {
  90.         OmemoDevice device;
  91.         try {
  92.             device = asOmemoDevice(signalProtocolAddress);
  93.         } catch (XmppStringprepException e) {
  94.             throw new AssertionError(e);
  95.         }

  96.         try {
  97.             omemoStore.storeOmemoIdentityKey(getOurDevice(), device, identityKey);
  98.         } catch (IOException e) {
  99.             throw new IllegalStateException(e);
  100.         }
  101.         return true;
  102.     }

  103.     @Override
  104.     public boolean isTrustedIdentity(SignalProtocolAddress signalProtocolAddress,
  105.                                      IdentityKey identityKey,
  106.                                      Direction direction) {
  107.         // Disable internal trust management. Instead we use OmemoStore.isTrustedOmemoIdentity() before encrypting
  108.         // for a recipient.
  109.         return true;
  110.     }

  111.     @Override
  112.     public IdentityKey getIdentity(SignalProtocolAddress address) {
  113.         OmemoDevice device;
  114.         try {
  115.             device = asOmemoDevice(address);
  116.         } catch (XmppStringprepException e) {
  117.             throw new AssertionError(e);
  118.         }

  119.         try {
  120.             return omemoStore.loadOmemoIdentityKey(getOurDevice(), device);
  121.         } catch (IOException | CorruptedOmemoKeyException e) {
  122.             throw new IllegalStateException(e);
  123.         }
  124.     }

  125.     @Override
  126.     public PreKeyRecord loadPreKey(int i) throws InvalidKeyIdException {
  127.         PreKeyRecord preKey;
  128.         try {
  129.             preKey = omemoStore.loadOmemoPreKey(getOurDevice(), i);
  130.         } catch (IOException e) {
  131.             throw new IllegalStateException(e);
  132.         }

  133.         if (preKey == null) {
  134.             throw new InvalidKeyIdException("No PreKey with Id " + i + " found.");
  135.         }

  136.         return preKey;
  137.     }

  138.     @Override
  139.     public void storePreKey(int i, PreKeyRecord preKeyRecord) {
  140.         try {
  141.             omemoStore.storeOmemoPreKey(getOurDevice(), i, preKeyRecord);
  142.         } catch (IOException e) {
  143.             throw new IllegalStateException(e);
  144.         }
  145.     }

  146.     @Override
  147.     public boolean containsPreKey(int i) {
  148.         try {
  149.             return loadPreKey(i) != null;
  150.         } catch (InvalidKeyIdException e) {
  151.             return false;
  152.         }
  153.     }

  154.     @Override
  155.     public void removePreKey(int i) {
  156.         omemoStore.removeOmemoPreKey(getOurDevice(), i);
  157.     }

  158.     @Override
  159.     public SessionRecord loadSession(SignalProtocolAddress signalProtocolAddress) {
  160.         OmemoDevice device;
  161.         try {
  162.             device = asOmemoDevice(signalProtocolAddress);
  163.         } catch (XmppStringprepException e) {
  164.             throw new AssertionError(e);
  165.         }

  166.         SessionRecord record;
  167.         try {
  168.             record = omemoStore.loadRawSession(getOurDevice(), device);
  169.         } catch (IOException e) {
  170.             throw new IllegalStateException(e);
  171.         }

  172.         if (record != null) {
  173.             return record;
  174.         } else {
  175.             return new SessionRecord();
  176.         }
  177.     }

  178.     @Override
  179.     public List<Integer> getSubDeviceSessions(String s) {
  180.         BareJid jid;
  181.         try {
  182.             jid = JidCreate.bareFrom(s);
  183.         } catch (XmppStringprepException e) {
  184.             throw new AssertionError(e);
  185.         }

  186.         try {
  187.             return new ArrayList<>(omemoStore.loadAllRawSessionsOf(getOurDevice(), jid).keySet());
  188.         } catch (IOException e) {
  189.             throw new IllegalStateException(e);
  190.         }
  191.     }

  192.     @Override
  193.     public void storeSession(SignalProtocolAddress signalProtocolAddress, SessionRecord sessionRecord) {
  194.         OmemoDevice device;
  195.         try {
  196.             device = asOmemoDevice(signalProtocolAddress);
  197.         } catch (XmppStringprepException e) {
  198.             throw new AssertionError(e);
  199.         }

  200.         try {
  201.             omemoStore.storeRawSession(getOurDevice(), device, sessionRecord);
  202.         } catch (IOException e) {
  203.             throw new IllegalStateException(e);
  204.         }
  205.     }

  206.     @Override
  207.     public boolean containsSession(SignalProtocolAddress signalProtocolAddress) {
  208.         OmemoDevice device;
  209.         try {
  210.             device = asOmemoDevice(signalProtocolAddress);
  211.         } catch (XmppStringprepException e) {
  212.             throw new AssertionError(e);
  213.         }

  214.         return omemoStore.containsRawSession(getOurDevice(), device);
  215.     }

  216.     @Override
  217.     public void deleteSession(SignalProtocolAddress signalProtocolAddress) {
  218.         OmemoDevice device;
  219.         try {
  220.             device = asOmemoDevice(signalProtocolAddress);
  221.         } catch (XmppStringprepException e) {
  222.             throw new AssertionError(e);
  223.         }

  224.         omemoStore.removeRawSession(getOurDevice(), device);
  225.     }

  226.     @Override
  227.     public void deleteAllSessions(String s) {
  228.         BareJid jid;
  229.         try {
  230.             jid = JidCreate.bareFrom(s);
  231.         } catch (XmppStringprepException e) {
  232.             throw new AssertionError(e);
  233.         }

  234.         omemoStore.removeAllRawSessionsOf(getOurDevice(), jid);
  235.     }

  236.     @Override
  237.     public SignedPreKeyRecord loadSignedPreKey(int i) throws InvalidKeyIdException {
  238.         SignedPreKeyRecord signedPreKeyRecord;
  239.         try {
  240.             signedPreKeyRecord = omemoStore.loadOmemoSignedPreKey(getOurDevice(), i);
  241.         } catch (IOException e) {
  242.             throw new IllegalStateException(e);
  243.         }
  244.         if (signedPreKeyRecord == null) {
  245.             throw new InvalidKeyIdException("No signed preKey with id " + i + " found.");
  246.         }
  247.         return signedPreKeyRecord;
  248.     }

  249.     @Override
  250.     public List<SignedPreKeyRecord> loadSignedPreKeys() {

  251.         TreeMap<Integer, SignedPreKeyRecord> signedPreKeyRecordHashMap;
  252.         try {
  253.             signedPreKeyRecordHashMap = omemoStore.loadOmemoSignedPreKeys(getOurDevice());
  254.         } catch (IOException e) {
  255.             throw new IllegalStateException(e);
  256.         }
  257.         return new ArrayList<>(signedPreKeyRecordHashMap.values());
  258.     }

  259.     @Override
  260.     public void storeSignedPreKey(int i, SignedPreKeyRecord signedPreKeyRecord) {
  261.         try {
  262.             omemoStore.storeOmemoSignedPreKey(getOurDevice(), i, signedPreKeyRecord);
  263.         } catch (IOException e) {
  264.             throw new IllegalStateException(e);
  265.         }
  266.     }

  267.     @Override
  268.     public boolean containsSignedPreKey(int i) {
  269.         try {
  270.             return loadSignedPreKey(i) != null;
  271.         } catch (InvalidKeyIdException e) {
  272.             LOGGER.log(Level.WARNING, "containsSignedPreKey has failed: " + e.getMessage());
  273.             return false;
  274.         }
  275.     }

  276.     @Override
  277.     public void removeSignedPreKey(int i) {
  278.         omemoStore.removeOmemoSignedPreKey(getOurDevice(), i);
  279.     }

  280.     private static OmemoDevice asOmemoDevice(SignalProtocolAddress address) throws XmppStringprepException {
  281.         return new OmemoDevice(JidCreate.bareFrom(address.getName()), address.getDeviceId());
  282.     }

  283.     public static SignalProtocolAddress asAddress(OmemoDevice device) {
  284.         return new SignalProtocolAddress(device.getJid().toString(), device.getDeviceId());
  285.     }
  286. }