2017-06-02 12:26:37 +02:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* Copyright 2017 Paul Schaub
|
|
|
|
*
|
|
|
|
* This file is part of smack-omemo-signal.
|
|
|
|
*
|
|
|
|
* smack-omemo-signal is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
package org.jivesoftware.smackx.omemo.signal;
|
|
|
|
|
2019-08-05 08:28:42 +02:00
|
|
|
import java.io.IOException;
|
2017-06-14 17:12:43 +02:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
2018-06-13 12:29:16 +02:00
|
|
|
import java.util.TreeMap;
|
2017-06-14 17:12:43 +02:00
|
|
|
import java.util.logging.Level;
|
|
|
|
import java.util.logging.Logger;
|
|
|
|
|
2017-06-02 12:26:37 +02:00
|
|
|
import org.jivesoftware.smackx.omemo.OmemoManager;
|
|
|
|
import org.jivesoftware.smackx.omemo.OmemoStore;
|
|
|
|
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
2018-06-13 12:29:16 +02:00
|
|
|
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
2017-06-14 17:12:43 +02:00
|
|
|
|
2018-06-13 12:29:16 +02:00
|
|
|
import org.jxmpp.jid.BareJid;
|
2017-06-02 12:26:37 +02:00
|
|
|
import org.jxmpp.jid.impl.JidCreate;
|
|
|
|
import org.jxmpp.stringprep.XmppStringprepException;
|
|
|
|
import org.whispersystems.libsignal.IdentityKey;
|
|
|
|
import org.whispersystems.libsignal.IdentityKeyPair;
|
|
|
|
import org.whispersystems.libsignal.InvalidKeyIdException;
|
|
|
|
import org.whispersystems.libsignal.SessionCipher;
|
|
|
|
import org.whispersystems.libsignal.SignalProtocolAddress;
|
|
|
|
import org.whispersystems.libsignal.ecc.ECPublicKey;
|
|
|
|
import org.whispersystems.libsignal.state.IdentityKeyStore;
|
|
|
|
import org.whispersystems.libsignal.state.PreKeyBundle;
|
|
|
|
import org.whispersystems.libsignal.state.PreKeyRecord;
|
|
|
|
import org.whispersystems.libsignal.state.PreKeyStore;
|
|
|
|
import org.whispersystems.libsignal.state.SessionRecord;
|
|
|
|
import org.whispersystems.libsignal.state.SessionStore;
|
|
|
|
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
|
|
|
import org.whispersystems.libsignal.state.SignedPreKeyStore;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Class that adapts libsignal-protocol-java's Store classes to the OmemoStore class.
|
|
|
|
*
|
|
|
|
* @author Paul Schaub
|
|
|
|
*/
|
|
|
|
public class SignalOmemoStoreConnector
|
|
|
|
implements IdentityKeyStore, SessionStore, PreKeyStore, SignedPreKeyStore {
|
|
|
|
|
|
|
|
private static final Logger LOGGER = Logger.getLogger(SignalOmemoStoreConnector.class.getName());
|
|
|
|
|
2018-06-13 12:29:16 +02:00
|
|
|
private final OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord,
|
|
|
|
SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher>
|
2017-06-02 12:26:37 +02:00
|
|
|
omemoStore;
|
2018-06-13 12:29:16 +02:00
|
|
|
private final OmemoManager omemoManager;
|
2017-06-02 12:26:37 +02:00
|
|
|
|
2018-06-13 12:29:16 +02:00
|
|
|
public SignalOmemoStoreConnector(OmemoManager omemoManager, OmemoStore<IdentityKeyPair,
|
|
|
|
IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey,
|
|
|
|
PreKeyBundle, SessionCipher> store) {
|
2017-06-02 12:26:37 +02:00
|
|
|
this.omemoManager = omemoManager;
|
|
|
|
this.omemoStore = store;
|
|
|
|
}
|
|
|
|
|
2018-06-13 12:29:16 +02:00
|
|
|
OmemoDevice getOurDevice() {
|
|
|
|
return omemoManager.getOwnDevice();
|
|
|
|
}
|
|
|
|
|
2017-06-02 12:26:37 +02:00
|
|
|
@Override
|
|
|
|
public IdentityKeyPair getIdentityKeyPair() {
|
|
|
|
try {
|
2018-06-13 12:29:16 +02:00
|
|
|
return omemoStore.loadOmemoIdentityKeyPair(getOurDevice());
|
2019-08-05 08:28:42 +02:00
|
|
|
} catch (CorruptedOmemoKeyException | IOException e) {
|
2018-06-13 12:29:16 +02:00
|
|
|
LOGGER.log(Level.SEVERE, "IdentityKeyPair seems to be invalid.", e);
|
2017-06-02 12:26:37 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-12-13 18:25:46 +01:00
|
|
|
* The OMEMO protocol does not make use of a local registration ID, so we can simply return 0 here.
|
|
|
|
*
|
|
|
|
* @return local registration id.
|
2017-06-02 12:26:37 +02:00
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public int getLocalRegistrationId() {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2018-06-13 12:29:16 +02:00
|
|
|
public boolean saveIdentity(SignalProtocolAddress signalProtocolAddress, IdentityKey identityKey) {
|
|
|
|
OmemoDevice device;
|
2017-06-02 12:26:37 +02:00
|
|
|
try {
|
2018-06-13 12:29:16 +02:00
|
|
|
device = asOmemoDevice(signalProtocolAddress);
|
2017-06-02 12:26:37 +02:00
|
|
|
} catch (XmppStringprepException e) {
|
|
|
|
throw new AssertionError(e);
|
|
|
|
}
|
2018-06-13 12:29:16 +02:00
|
|
|
|
2019-08-05 08:28:42 +02:00
|
|
|
try {
|
|
|
|
omemoStore.storeOmemoIdentityKey(getOurDevice(), device, identityKey);
|
|
|
|
} catch (IOException e) {
|
|
|
|
throw new IllegalStateException(e);
|
|
|
|
}
|
2018-06-13 12:29:16 +02:00
|
|
|
return true;
|
2017-06-02 12:26:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2018-06-13 12:29:16 +02:00
|
|
|
public boolean isTrustedIdentity(SignalProtocolAddress signalProtocolAddress,
|
|
|
|
IdentityKey identityKey,
|
|
|
|
Direction direction) {
|
|
|
|
// Disable internal trust management. Instead we use OmemoStore.isTrustedOmemoIdentity() before encrypting
|
|
|
|
// for a recipient.
|
2017-06-02 12:26:37 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public PreKeyRecord loadPreKey(int i) throws InvalidKeyIdException {
|
2019-08-05 08:28:42 +02:00
|
|
|
PreKeyRecord preKey;
|
|
|
|
try {
|
|
|
|
preKey = omemoStore.loadOmemoPreKey(getOurDevice(), i);
|
|
|
|
} catch (IOException e) {
|
|
|
|
throw new IllegalStateException(e);
|
|
|
|
}
|
2018-06-13 12:29:16 +02:00
|
|
|
|
|
|
|
if (preKey == null) {
|
|
|
|
throw new InvalidKeyIdException("No PreKey with Id " + i + " found.");
|
2017-06-02 12:26:37 +02:00
|
|
|
}
|
2018-06-13 12:29:16 +02:00
|
|
|
|
|
|
|
return preKey;
|
2017-06-02 12:26:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void storePreKey(int i, PreKeyRecord preKeyRecord) {
|
2019-08-05 08:28:42 +02:00
|
|
|
try {
|
|
|
|
omemoStore.storeOmemoPreKey(getOurDevice(), i, preKeyRecord);
|
|
|
|
} catch (IOException e) {
|
|
|
|
throw new IllegalStateException(e);
|
|
|
|
}
|
2017-06-02 12:26:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean containsPreKey(int i) {
|
|
|
|
try {
|
2019-07-24 09:18:39 +02:00
|
|
|
return loadPreKey(i) != null;
|
2017-06-02 12:26:37 +02:00
|
|
|
} catch (InvalidKeyIdException e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void removePreKey(int i) {
|
2018-06-13 12:29:16 +02:00
|
|
|
omemoStore.removeOmemoPreKey(getOurDevice(), i);
|
2017-06-02 12:26:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public SessionRecord loadSession(SignalProtocolAddress signalProtocolAddress) {
|
2018-06-13 12:29:16 +02:00
|
|
|
OmemoDevice device;
|
2017-06-02 12:26:37 +02:00
|
|
|
try {
|
2018-06-13 12:29:16 +02:00
|
|
|
device = asOmemoDevice(signalProtocolAddress);
|
2017-06-02 12:26:37 +02:00
|
|
|
} catch (XmppStringprepException e) {
|
|
|
|
throw new AssertionError(e);
|
|
|
|
}
|
2018-06-13 12:29:16 +02:00
|
|
|
|
2019-08-05 08:28:42 +02:00
|
|
|
SessionRecord record;
|
|
|
|
try {
|
|
|
|
record = omemoStore.loadRawSession(getOurDevice(), device);
|
|
|
|
} catch (IOException e) {
|
|
|
|
throw new IllegalStateException(e);
|
|
|
|
}
|
2018-06-13 12:29:16 +02:00
|
|
|
|
|
|
|
if (record != null) {
|
|
|
|
return record;
|
|
|
|
} else {
|
|
|
|
return new SessionRecord();
|
|
|
|
}
|
2017-06-02 12:26:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public List<Integer> getSubDeviceSessions(String s) {
|
2018-06-13 12:29:16 +02:00
|
|
|
BareJid jid;
|
2017-06-02 12:26:37 +02:00
|
|
|
try {
|
2018-06-13 12:29:16 +02:00
|
|
|
jid = JidCreate.bareFrom(s);
|
2017-06-02 12:26:37 +02:00
|
|
|
} catch (XmppStringprepException e) {
|
|
|
|
throw new AssertionError(e);
|
|
|
|
}
|
2018-06-13 12:29:16 +02:00
|
|
|
|
2019-08-05 08:28:42 +02:00
|
|
|
try {
|
|
|
|
return new ArrayList<>(omemoStore.loadAllRawSessionsOf(getOurDevice(), jid).keySet());
|
|
|
|
} catch (IOException e) {
|
|
|
|
throw new IllegalStateException(e);
|
|
|
|
}
|
2017-06-02 12:26:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void storeSession(SignalProtocolAddress signalProtocolAddress, SessionRecord sessionRecord) {
|
2018-06-13 12:29:16 +02:00
|
|
|
OmemoDevice device;
|
2017-06-02 12:26:37 +02:00
|
|
|
try {
|
2018-06-13 12:29:16 +02:00
|
|
|
device = asOmemoDevice(signalProtocolAddress);
|
2017-06-02 12:26:37 +02:00
|
|
|
} catch (XmppStringprepException e) {
|
|
|
|
throw new AssertionError(e);
|
|
|
|
}
|
2018-06-13 12:29:16 +02:00
|
|
|
|
2019-08-05 08:28:42 +02:00
|
|
|
try {
|
|
|
|
omemoStore.storeRawSession(getOurDevice(), device, sessionRecord);
|
|
|
|
} catch (IOException e) {
|
|
|
|
throw new IllegalStateException(e);
|
|
|
|
}
|
2017-06-02 12:26:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean containsSession(SignalProtocolAddress signalProtocolAddress) {
|
2018-06-13 12:29:16 +02:00
|
|
|
OmemoDevice device;
|
2017-06-02 12:26:37 +02:00
|
|
|
try {
|
2018-06-13 12:29:16 +02:00
|
|
|
device = asOmemoDevice(signalProtocolAddress);
|
2017-06-02 12:26:37 +02:00
|
|
|
} catch (XmppStringprepException e) {
|
|
|
|
throw new AssertionError(e);
|
|
|
|
}
|
2018-06-13 12:29:16 +02:00
|
|
|
|
|
|
|
return omemoStore.containsRawSession(getOurDevice(), device);
|
2017-06-02 12:26:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void deleteSession(SignalProtocolAddress signalProtocolAddress) {
|
2018-06-13 12:29:16 +02:00
|
|
|
OmemoDevice device;
|
2017-06-02 12:26:37 +02:00
|
|
|
try {
|
2018-06-13 12:29:16 +02:00
|
|
|
device = asOmemoDevice(signalProtocolAddress);
|
2017-06-02 12:26:37 +02:00
|
|
|
} catch (XmppStringprepException e) {
|
|
|
|
throw new AssertionError(e);
|
|
|
|
}
|
2018-06-13 12:29:16 +02:00
|
|
|
|
|
|
|
omemoStore.removeRawSession(getOurDevice(), device);
|
2017-06-02 12:26:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void deleteAllSessions(String s) {
|
2018-06-13 12:29:16 +02:00
|
|
|
BareJid jid;
|
2017-06-02 12:26:37 +02:00
|
|
|
try {
|
2018-06-13 12:29:16 +02:00
|
|
|
jid = JidCreate.bareFrom(s);
|
2017-06-02 12:26:37 +02:00
|
|
|
} catch (XmppStringprepException e) {
|
|
|
|
throw new AssertionError(e);
|
|
|
|
}
|
2018-06-13 12:29:16 +02:00
|
|
|
|
|
|
|
omemoStore.removeAllRawSessionsOf(getOurDevice(), jid);
|
2017-06-02 12:26:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public SignedPreKeyRecord loadSignedPreKey(int i) throws InvalidKeyIdException {
|
2019-08-05 08:28:42 +02:00
|
|
|
SignedPreKeyRecord signedPreKeyRecord;
|
|
|
|
try {
|
|
|
|
signedPreKeyRecord = omemoStore.loadOmemoSignedPreKey(getOurDevice(), i);
|
|
|
|
} catch (IOException e) {
|
|
|
|
throw new IllegalStateException(e);
|
|
|
|
}
|
2018-06-13 12:29:16 +02:00
|
|
|
if (signedPreKeyRecord == null) {
|
|
|
|
throw new InvalidKeyIdException("No signed preKey with id " + i + " found.");
|
2017-06-02 12:26:37 +02:00
|
|
|
}
|
2018-06-13 12:29:16 +02:00
|
|
|
return signedPreKeyRecord;
|
2017-06-02 12:26:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public List<SignedPreKeyRecord> loadSignedPreKeys() {
|
2018-06-13 12:29:16 +02:00
|
|
|
|
2019-08-05 08:28:42 +02:00
|
|
|
TreeMap<Integer, SignedPreKeyRecord> signedPreKeyRecordHashMap;
|
|
|
|
try {
|
|
|
|
signedPreKeyRecordHashMap = omemoStore.loadOmemoSignedPreKeys(getOurDevice());
|
|
|
|
} catch (IOException e) {
|
|
|
|
throw new IllegalStateException(e);
|
|
|
|
}
|
2018-06-13 12:29:16 +02:00
|
|
|
return new ArrayList<>(signedPreKeyRecordHashMap.values());
|
2017-06-02 12:26:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void storeSignedPreKey(int i, SignedPreKeyRecord signedPreKeyRecord) {
|
2019-08-05 08:28:42 +02:00
|
|
|
try {
|
|
|
|
omemoStore.storeOmemoSignedPreKey(getOurDevice(), i, signedPreKeyRecord);
|
|
|
|
} catch (IOException e) {
|
|
|
|
throw new IllegalStateException(e);
|
|
|
|
}
|
2017-06-02 12:26:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean containsSignedPreKey(int i) {
|
|
|
|
try {
|
|
|
|
return loadSignedPreKey(i) != null;
|
|
|
|
} catch (InvalidKeyIdException e) {
|
|
|
|
LOGGER.log(Level.WARNING, "containsSignedPreKey has failed: " + e.getMessage());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void removeSignedPreKey(int i) {
|
2018-06-13 12:29:16 +02:00
|
|
|
omemoStore.removeOmemoSignedPreKey(getOurDevice(), i);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static OmemoDevice asOmemoDevice(SignalProtocolAddress address) throws XmppStringprepException {
|
|
|
|
return new OmemoDevice(JidCreate.bareFrom(address.getName()), address.getDeviceId());
|
|
|
|
}
|
|
|
|
|
|
|
|
public static SignalProtocolAddress asAddress(OmemoDevice device) {
|
|
|
|
return new SignalProtocolAddress(device.getJid().toString(), device.getDeviceId());
|
2017-06-02 12:26:37 +02:00
|
|
|
}
|
|
|
|
}
|