001/**
002 *
003 * Copyright 2017 Paul Schaub
004 *
005 * This file is part of smack-omemo-signal.
006 *
007 * smack-omemo-signal is free software; you can redistribute it and/or modify
008 * it under the terms of the GNU General Public License as published by
009 * the Free Software Foundation; either version 3 of the License, or
010 * (at your option) any later version.
011 *
012 * This program is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
015 * GNU General Public License for more details.
016 *
017 * You should have received a copy of the GNU General Public License
018 * along with this program; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
020 */
021package org.jivesoftware.smackx.omemo.signal;
022
023import java.util.ArrayList;
024import java.util.HashMap;
025import java.util.List;
026import java.util.logging.Level;
027import java.util.logging.Logger;
028
029import org.jivesoftware.smackx.omemo.OmemoManager;
030import org.jivesoftware.smackx.omemo.OmemoStore;
031import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
032
033import org.jxmpp.jid.impl.JidCreate;
034import org.jxmpp.stringprep.XmppStringprepException;
035import org.whispersystems.libsignal.IdentityKey;
036import org.whispersystems.libsignal.IdentityKeyPair;
037import org.whispersystems.libsignal.InvalidKeyIdException;
038import org.whispersystems.libsignal.SessionCipher;
039import org.whispersystems.libsignal.SignalProtocolAddress;
040import org.whispersystems.libsignal.ecc.ECPublicKey;
041import org.whispersystems.libsignal.state.IdentityKeyStore;
042import org.whispersystems.libsignal.state.PreKeyBundle;
043import org.whispersystems.libsignal.state.PreKeyRecord;
044import org.whispersystems.libsignal.state.PreKeyStore;
045import org.whispersystems.libsignal.state.SessionRecord;
046import org.whispersystems.libsignal.state.SessionStore;
047import org.whispersystems.libsignal.state.SignedPreKeyRecord;
048import org.whispersystems.libsignal.state.SignedPreKeyStore;
049
050/**
051 * Class that adapts libsignal-protocol-java's Store classes to the OmemoStore class.
052 *
053 * @author Paul Schaub
054 */
055public class SignalOmemoStoreConnector
056        implements IdentityKeyStore, SessionStore, PreKeyStore, SignedPreKeyStore {
057
058    private static final Logger LOGGER = Logger.getLogger(SignalOmemoStoreConnector.class.getName());
059
060    private final OmemoManager omemoManager;
061    private final OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher>
062            omemoStore;
063
064    public SignalOmemoStoreConnector(OmemoManager omemoManager, OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> store) {
065        this.omemoManager = omemoManager;
066        this.omemoStore = store;
067    }
068
069    @Override
070    public IdentityKeyPair getIdentityKeyPair() {
071        try {
072            return omemoStore.loadOmemoIdentityKeyPair(omemoManager);
073        } catch (CorruptedOmemoKeyException e) {
074            LOGGER.log(Level.SEVERE, "getIdentityKeyPair has failed: " + e, e);
075            return null;
076        }
077    }
078
079    /**
080     * We don't use this.
081     * @return dummy
082     */
083    @Override
084    public int getLocalRegistrationId() {
085        return 0;
086    }
087
088    @Override
089    public void saveIdentity(SignalProtocolAddress signalProtocolAddress, IdentityKey identityKey) {
090        try {
091            omemoStore.storeOmemoIdentityKey(omemoManager, omemoStore.keyUtil().addressAsOmemoDevice(signalProtocolAddress), identityKey);
092        } catch (XmppStringprepException e) {
093            throw new AssertionError(e);
094        }
095    }
096
097    @Override
098    public boolean isTrustedIdentity(SignalProtocolAddress signalProtocolAddress, IdentityKey identityKey) {
099        // Disable internal trust management. Instead we use OmemoStore.isTrustedOmemoIdentity() before encrypting for a
100        // recipient.
101        return true;
102    }
103
104    @Override
105    public PreKeyRecord loadPreKey(int i) throws InvalidKeyIdException {
106        PreKeyRecord pr = omemoStore.loadOmemoPreKey(omemoManager, i);
107        if (pr == null) {
108            throw new InvalidKeyIdException("No PreKey with Id " + i + " found!");
109        }
110        return pr;
111    }
112
113    @Override
114    public void storePreKey(int i, PreKeyRecord preKeyRecord) {
115        omemoStore.storeOmemoPreKey(omemoManager, i, preKeyRecord);
116    }
117
118    @Override
119    public boolean containsPreKey(int i) {
120        try {
121            return (loadPreKey(i) != null);
122        } catch (InvalidKeyIdException e) {
123            LOGGER.log(Level.WARNING, "containsPreKey has failed: " + e.getMessage());
124            return false;
125        }
126    }
127
128    @Override
129    public void removePreKey(int i) {
130        omemoStore.removeOmemoPreKey(omemoManager, i);
131    }
132
133    @Override
134    public SessionRecord loadSession(SignalProtocolAddress signalProtocolAddress) {
135        try {
136            SessionRecord s = omemoStore.loadRawSession(omemoManager, omemoStore.keyUtil().addressAsOmemoDevice(signalProtocolAddress));
137            return (s != null ? s : new SessionRecord());
138        } catch (XmppStringprepException e) {
139            throw new AssertionError(e);
140        }
141    }
142
143    @Override
144    public List<Integer> getSubDeviceSessions(String s) {
145        HashMap<Integer, SessionRecord> contactsSessions;
146        try {
147            contactsSessions = omemoStore.loadAllRawSessionsOf(omemoManager, JidCreate.bareFrom(s));
148        } catch (XmppStringprepException e) {
149            throw new AssertionError(e);
150        }
151        if (contactsSessions != null) {
152            return new ArrayList<>(contactsSessions.keySet());
153        }
154        return new ArrayList<>();
155    }
156
157    @Override
158    public void storeSession(SignalProtocolAddress signalProtocolAddress, SessionRecord sessionRecord) {
159        try {
160            omemoStore.storeRawSession(omemoManager, omemoStore.keyUtil().addressAsOmemoDevice(signalProtocolAddress), sessionRecord);
161        } catch (XmppStringprepException e) {
162            throw new AssertionError(e);
163        }
164    }
165
166    @Override
167    public boolean containsSession(SignalProtocolAddress signalProtocolAddress) {
168        try {
169            return omemoStore.containsRawSession(omemoManager, omemoStore.keyUtil().addressAsOmemoDevice(signalProtocolAddress));
170        } catch (XmppStringprepException e) {
171            throw new AssertionError(e);
172        }
173    }
174
175    @Override
176    public void deleteSession(SignalProtocolAddress signalProtocolAddress) {
177        try {
178            omemoStore.removeRawSession(omemoManager, omemoStore.keyUtil().addressAsOmemoDevice(signalProtocolAddress));
179        } catch (XmppStringprepException e) {
180            throw new AssertionError(e);
181        }
182    }
183
184    @Override
185    public void deleteAllSessions(String s) {
186        try {
187            omemoStore.removeAllRawSessionsOf(omemoManager, JidCreate.bareFrom(s));
188        } catch (XmppStringprepException e) {
189            throw new AssertionError(e);
190        }
191    }
192
193    @Override
194    public SignedPreKeyRecord loadSignedPreKey(int i) throws InvalidKeyIdException {
195        SignedPreKeyRecord spkr = omemoStore.loadOmemoSignedPreKey(omemoManager, i);
196        if (spkr == null) {
197            throw new InvalidKeyIdException("No SignedPreKey with Id " + i + " found!");
198        }
199        return spkr;
200    }
201
202    @Override
203    public List<SignedPreKeyRecord> loadSignedPreKeys() {
204        HashMap<Integer, SignedPreKeyRecord> signedPreKeyRecordHashMap = omemoStore.loadOmemoSignedPreKeys(omemoManager);
205        List<SignedPreKeyRecord> signedPreKeyRecordList = new ArrayList<>();
206        signedPreKeyRecordList.addAll(signedPreKeyRecordHashMap.values());
207        return signedPreKeyRecordList;
208    }
209
210    @Override
211    public void storeSignedPreKey(int i, SignedPreKeyRecord signedPreKeyRecord) {
212        omemoStore.storeOmemoSignedPreKey(omemoManager, i, signedPreKeyRecord);
213    }
214
215    @Override
216    public boolean containsSignedPreKey(int i) {
217        try {
218            return loadSignedPreKey(i) != null;
219        } catch (InvalidKeyIdException e) {
220            LOGGER.log(Level.WARNING, "containsSignedPreKey has failed: " + e.getMessage());
221            return false;
222        }
223    }
224
225    @Override
226    public void removeSignedPreKey(int i) {
227        omemoStore.removeOmemoSignedPreKey(omemoManager, i);
228    }
229}