mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 10:30:38 +00:00
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:
parent
31434ac5ec
commit
0dc6b1327e
5 changed files with 57 additions and 23 deletions
|
@ -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'
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue