Update libsignal-service-java

- Use new ProfileKey class instead of byte array
- Add capabilities (for future support of uuid and groups v2)
This commit is contained in:
AsamK 2020-03-21 14:37:02 +01:00
parent 31434ac5ec
commit 0dc6b1327e
5 changed files with 57 additions and 23 deletions

View file

@ -20,7 +20,7 @@ repositories {
} }
dependencies { dependencies {
compile 'com.github.turasa:signal-service-java:2.15.3_unofficial_2' compile 'com.github.turasa:signal-service-java:2.15.3_unofficial_3'
compile 'org.bouncycastle:bcprov-jdk15on:1.64' compile 'org.bouncycastle:bcprov-jdk15on:1.64'
compile 'net.sourceforge.argparse4j:argparse4j:0.8.1' compile 'net.sourceforge.argparse4j:argparse4j:0.8.1'
compile 'org.freedesktop.dbus:dbus-java:2.7.0' compile 'org.freedesktop.dbus:dbus-java:2.7.0'

View file

@ -37,13 +37,16 @@ public class BaseConfig {
private final static List<Interceptor> interceptors = Collections.singletonList(userAgentInterceptor); private final static List<Interceptor> interceptors = Collections.singletonList(userAgentInterceptor);
private final static byte[] zkGroupServerPublicParams = new byte[]{};
final static SignalServiceConfiguration serviceConfiguration = new SignalServiceConfiguration( final static SignalServiceConfiguration serviceConfiguration = new SignalServiceConfiguration(
new SignalServiceUrl[]{new SignalServiceUrl(URL, TRUST_STORE)}, new SignalServiceUrl[]{new SignalServiceUrl(URL, TRUST_STORE)},
new SignalCdnUrl[]{new SignalCdnUrl(CDN_URL, TRUST_STORE)}, new SignalCdnUrl[]{new SignalCdnUrl(CDN_URL, TRUST_STORE)},
new SignalContactDiscoveryUrl[0], new SignalContactDiscoveryUrl[0],
new SignalKeyBackupServiceUrl[]{new SignalKeyBackupServiceUrl(SIGNAL_KEY_BACKUP_URL, TRUST_STORE)}, new SignalKeyBackupServiceUrl[]{new SignalKeyBackupServiceUrl(SIGNAL_KEY_BACKUP_URL, TRUST_STORE)},
new SignalStorageUrl[]{new SignalStorageUrl(STORAGE_URL, TRUST_STORE)}, new SignalStorageUrl[]{new SignalStorageUrl(STORAGE_URL, TRUST_STORE)},
interceptors interceptors,
zkGroupServerPublicParams
); );
private BaseConfig() { private BaseConfig() {

View file

@ -1,6 +1,8 @@
package org.asamk.signal.manager; package org.asamk.signal.manager;
import org.asamk.signal.util.RandomUtils; import org.asamk.signal.util.RandomUtils;
import org.signal.zkgroup.InvalidInputException;
import org.signal.zkgroup.profiles.ProfileKey;
import org.whispersystems.util.Base64; import org.whispersystems.util.Base64;
class KeyUtils { class KeyUtils {
@ -12,8 +14,12 @@ class KeyUtils {
return getSecret(52); return getSecret(52);
} }
static byte[] createProfileKey() { static ProfileKey createProfileKey() {
return getSecretBytes(32); try {
return new ProfileKey(getSecretBytes(32));
} catch (InvalidInputException e) {
throw new AssertionError("Profile key is guaranteed to be 32 bytes here");
}
} }
static String createPassword() { static String createPassword() {

View file

@ -41,6 +41,8 @@ import org.signal.libsignal.metadata.ProtocolLegacyMessageException;
import org.signal.libsignal.metadata.ProtocolNoSessionException; import org.signal.libsignal.metadata.ProtocolNoSessionException;
import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException; import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException;
import org.signal.libsignal.metadata.SelfSendException; import org.signal.libsignal.metadata.SelfSendException;
import org.signal.zkgroup.InvalidInputException;
import org.signal.zkgroup.profiles.ProfileKey;
import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.IdentityKeyPair; import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.InvalidKeyException;
@ -84,6 +86,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage; import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
import org.whispersystems.signalservice.api.push.ContactTokenDetails; import org.whispersystems.signalservice.api.push.ContactTokenDetails;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException; import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException;
@ -126,6 +129,8 @@ import java.util.concurrent.TimeoutException;
public class Manager implements Signal { public class Manager implements Signal {
private static final SignalServiceProfile.Capabilities capabilities = new SignalServiceProfile.Capabilities(false, false);
private final String settingsPath; private final String settingsPath;
private final String dataPath; private final String dataPath;
private final String attachmentsPath; private final String attachmentsPath;
@ -237,7 +242,7 @@ public class Manager implements Signal {
if (username == null) { if (username == null) {
account = SignalAccount.createTemporaryAccount(identityKey, registrationId); account = SignalAccount.createTemporaryAccount(identityKey, registrationId);
} else { } else {
byte[] profileKey = KeyUtils.createProfileKey(); ProfileKey profileKey = KeyUtils.createProfileKey();
account = SignalAccount.create(dataPath, username, identityKey, registrationId, profileKey); account = SignalAccount.create(dataPath, username, identityKey, registrationId, profileKey);
account.save(); account.save();
} }
@ -265,7 +270,7 @@ public class Manager implements Signal {
} }
public void updateAccountAttributes() throws IOException { public void updateAccountAttributes() throws IOException {
accountManager.setAccountAttributes(account.getSignalingKey(), account.getSignalProtocolStore().getLocalRegistrationId(), true, account.getRegistrationLockPin(), account.getRegistrationLock(), getSelfUnidentifiedAccessKey(), false); accountManager.setAccountAttributes(account.getSignalingKey(), account.getSignalProtocolStore().getLocalRegistrationId(), true, account.getRegistrationLockPin(), account.getRegistrationLock(), getSelfUnidentifiedAccessKey(), false, capabilities);
} }
public void setProfileName(String name) throws IOException { public void setProfileName(String name) throws IOException {
@ -314,9 +319,16 @@ public class Manager implements Signal {
} }
// Create new account with the synced identity // Create new account with the synced identity
byte[] profileKey = ret.getProfileKey(); byte[] profileKeyBytes = ret.getProfileKey();
if (profileKey == null) { ProfileKey profileKey;
if (profileKeyBytes == null) {
profileKey = KeyUtils.createProfileKey(); profileKey = KeyUtils.createProfileKey();
} else {
try {
profileKey = new ProfileKey(profileKeyBytes);
} catch (InvalidInputException e) {
throw new IOException("Received invalid profileKey", e);
}
} }
account = SignalAccount.createLinkedAccount(dataPath, username, account.getPassword(), ret.getDeviceId(), ret.getIdentity(), account.getSignalProtocolStore().getLocalRegistrationId(), account.getSignalingKey(), profileKey); account = SignalAccount.createLinkedAccount(dataPath, username, account.getPassword(), ret.getDeviceId(), ret.getIdentity(), account.getSignalProtocolStore().getLocalRegistrationId(), account.getSignalingKey(), profileKey);
@ -354,7 +366,7 @@ public class Manager implements Signal {
IdentityKeyPair identityKeyPair = account.getSignalProtocolStore().getIdentityKeyPair(); IdentityKeyPair identityKeyPair = account.getSignalProtocolStore().getIdentityKeyPair();
String verificationCode = accountManager.getNewDeviceVerificationCode(); String verificationCode = accountManager.getNewDeviceVerificationCode();
accountManager.addDevice(deviceIdentifier, deviceKey, identityKeyPair, Optional.of(account.getProfileKey()), verificationCode); accountManager.addDevice(deviceIdentifier, deviceKey, identityKeyPair, Optional.of(account.getProfileKey().serialize()), verificationCode);
account.setMultiDevice(true); account.setMultiDevice(true);
account.save(); account.save();
} }
@ -396,7 +408,7 @@ public class Manager implements Signal {
verificationCode = verificationCode.replace("-", ""); verificationCode = verificationCode.replace("-", "");
account.setSignalingKey(KeyUtils.createSignalingKey()); account.setSignalingKey(KeyUtils.createSignalingKey());
// TODO make unrestricted unidentified access configurable // TODO make unrestricted unidentified access configurable
accountManager.verifyAccountWithCode(verificationCode, account.getSignalingKey(), account.getSignalProtocolStore().getLocalRegistrationId(), true, pin, null, getSelfUnidentifiedAccessKey(), false); accountManager.verifyAccountWithCode(verificationCode, account.getSignalingKey(), account.getSignalProtocolStore().getLocalRegistrationId(), true, pin, null, getSelfUnidentifiedAccessKey(), false, capabilities);
//accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID))); //accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID)));
account.setRegistered(true); account.setRegistered(true);
@ -502,7 +514,7 @@ public class Manager implements Signal {
SignalServiceDataMessage.Reaction reaction = new SignalServiceDataMessage.Reaction(emoji, remove, targetAuthor, targetSentTimestamp); SignalServiceDataMessage.Reaction reaction = new SignalServiceDataMessage.Reaction(emoji, remove, targetAuthor, targetSentTimestamp);
final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder()
.withReaction(reaction) .withReaction(reaction)
.withProfileKey(account.getProfileKey()); .withProfileKey(account.getProfileKey().serialize());
if (groupId != null) { if (groupId != null) {
SignalServiceGroup group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.DELIVER) SignalServiceGroup group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.DELIVER)
.withId(groupId) .withId(groupId)
@ -685,7 +697,7 @@ public class Manager implements Signal {
messageBuilder.withAttachments(attachmentPointers); messageBuilder.withAttachments(attachmentPointers);
} }
messageBuilder.withProfileKey(account.getProfileKey()); messageBuilder.withProfileKey(account.getProfileKey().serialize());
sendMessageLegacy(messageBuilder, recipients); sendMessageLegacy(messageBuilder, recipients);
} }
@ -695,7 +707,7 @@ public class Manager implements Signal {
SignalServiceDataMessage.Reaction reaction = new SignalServiceDataMessage.Reaction(emoji, remove, targetAuthor, targetSentTimestamp); SignalServiceDataMessage.Reaction reaction = new SignalServiceDataMessage.Reaction(emoji, remove, targetAuthor, targetSentTimestamp);
final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder()
.withReaction(reaction) .withReaction(reaction)
.withProfileKey(account.getProfileKey()); .withProfileKey(account.getProfileKey().serialize());
sendMessageLegacy(messageBuilder, recipients); sendMessageLegacy(messageBuilder, recipients);
} }
@ -1117,7 +1129,10 @@ public class Manager implements Signal {
} }
if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) { if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) {
if (source.equals(username)) { if (source.equals(username)) {
this.account.setProfileKey(message.getProfileKey().get()); try {
this.account.setProfileKey(new ProfileKey(message.getProfileKey().get()));
} catch (InvalidInputException ignored) {
}
} }
ContactInfo contact = account.getContactStore().getContact(source); ContactInfo contact = account.getContactStore().getContact(source);
if (contact == null) { if (contact == null) {
@ -1413,7 +1428,7 @@ public class Manager implements Signal {
contact.color = c.getColor().get(); contact.color = c.getColor().get();
} }
if (c.getProfileKey().isPresent()) { if (c.getProfileKey().isPresent()) {
contact.profileKey = Base64.encodeBytes(c.getProfileKey().get()); contact.profileKey = Base64.encodeBytes(c.getProfileKey().get().serialize());
} }
if (c.getVerified().isPresent()) { if (c.getVerified().isPresent()) {
final VerifiedMessage verifiedMessage = c.getVerified().get(); final VerifiedMessage verifiedMessage = c.getVerified().get();
@ -1603,7 +1618,11 @@ public class Manager implements Signal {
} }
} }
byte[] profileKey = record.profileKey == null ? null : Base64.decode(record.profileKey); ProfileKey profileKey = null;
try {
profileKey = record.profileKey == null ? null : new ProfileKey(Base64.decode(record.profileKey));
} catch (InvalidInputException ignored) {
}
out.write(new DeviceContact(record.getAddress(), Optional.fromNullable(record.name), out.write(new DeviceContact(record.getAddress(), Optional.fromNullable(record.name),
createContactAvatarAttachment(record.number), Optional.fromNullable(record.color), createContactAvatarAttachment(record.number), Optional.fromNullable(record.color),
Optional.fromNullable(verifiedMessage), Optional.fromNullable(profileKey), record.blocked, Optional.fromNullable(verifiedMessage), Optional.fromNullable(profileKey), record.blocked,

View file

@ -16,6 +16,8 @@ import org.asamk.signal.storage.protocol.JsonSignalProtocolStore;
import org.asamk.signal.storage.threads.JsonThreadStore; import org.asamk.signal.storage.threads.JsonThreadStore;
import org.asamk.signal.util.IOUtils; import org.asamk.signal.util.IOUtils;
import org.asamk.signal.util.Util; import org.asamk.signal.util.Util;
import org.signal.zkgroup.InvalidInputException;
import org.signal.zkgroup.profiles.ProfileKey;
import org.whispersystems.libsignal.IdentityKeyPair; import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.state.PreKeyRecord; import org.whispersystems.libsignal.state.PreKeyRecord;
import org.whispersystems.libsignal.state.SignedPreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyRecord;
@ -42,7 +44,7 @@ public class SignalAccount {
private String password; private String password;
private String registrationLockPin; private String registrationLockPin;
private String signalingKey; private String signalingKey;
private byte[] profileKey; private ProfileKey profileKey;
private int preKeyIdOffset; private int preKeyIdOffset;
private int nextSignedPreKeyId; private int nextSignedPreKeyId;
@ -70,7 +72,7 @@ public class SignalAccount {
return account; return account;
} }
public static SignalAccount create(String dataPath, String username, IdentityKeyPair identityKey, int registrationId, byte[] profileKey) throws IOException { public static SignalAccount create(String dataPath, String username, IdentityKeyPair identityKey, int registrationId, ProfileKey profileKey) throws IOException {
IOUtils.createPrivateDirectories(dataPath); IOUtils.createPrivateDirectories(dataPath);
SignalAccount account = new SignalAccount(); SignalAccount account = new SignalAccount();
@ -87,7 +89,7 @@ public class SignalAccount {
return account; return account;
} }
public static SignalAccount createLinkedAccount(String dataPath, String username, String password, int deviceId, IdentityKeyPair identityKey, int registrationId, String signalingKey, byte[] profileKey) throws IOException { public static SignalAccount createLinkedAccount(String dataPath, String username, String password, int deviceId, IdentityKeyPair identityKey, int registrationId, String signalingKey, ProfileKey profileKey) throws IOException {
IOUtils.createPrivateDirectories(dataPath); IOUtils.createPrivateDirectories(dataPath);
SignalAccount account = new SignalAccount(); SignalAccount account = new SignalAccount();
@ -161,7 +163,11 @@ public class SignalAccount {
nextSignedPreKeyId = 0; nextSignedPreKeyId = 0;
} }
if (rootNode.has("profileKey")) { if (rootNode.has("profileKey")) {
profileKey = Base64.decode(Util.getNotNullNode(rootNode, "profileKey").asText()); try {
profileKey = new ProfileKey(Base64.decode(Util.getNotNullNode(rootNode, "profileKey").asText()));
} catch (InvalidInputException e) {
throw new IOException("Config file contains an invalid profileKey, needs to be base64 encoded array of 32 bytes", e);
}
} }
signalProtocolStore = jsonProcessor.convertValue(Util.getNotNullNode(rootNode, "axolotlStore"), JsonSignalProtocolStore.class); signalProtocolStore = jsonProcessor.convertValue(Util.getNotNullNode(rootNode, "axolotlStore"), JsonSignalProtocolStore.class);
@ -203,7 +209,7 @@ public class SignalAccount {
.put("signalingKey", signalingKey) .put("signalingKey", signalingKey)
.put("preKeyIdOffset", preKeyIdOffset) .put("preKeyIdOffset", preKeyIdOffset)
.put("nextSignedPreKeyId", nextSignedPreKeyId) .put("nextSignedPreKeyId", nextSignedPreKeyId)
.put("profileKey", Base64.encodeBytes(profileKey)) .put("profileKey", Base64.encodeBytes(profileKey.serialize()))
.put("registered", registered) .put("registered", registered)
.putPOJO("axolotlStore", signalProtocolStore) .putPOJO("axolotlStore", signalProtocolStore)
.putPOJO("groupStore", groupStore) .putPOJO("groupStore", groupStore)
@ -307,11 +313,11 @@ public class SignalAccount {
this.signalingKey = signalingKey; this.signalingKey = signalingKey;
} }
public byte[] getProfileKey() { public ProfileKey getProfileKey() {
return profileKey; return profileKey;
} }
public void setProfileKey(final byte[] profileKey) { public void setProfileKey(final ProfileKey profileKey) {
this.profileKey = profileKey; this.profileKey = profileKey;
} }