mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 10:30:38 +00:00
Encrypt/decrypt device names
This commit is contained in:
parent
56ac648922
commit
6cb867cbeb
6 changed files with 70 additions and 21 deletions
|
@ -14,7 +14,7 @@ repositories {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api("com.github.turasa:signal-service-java:2.15.3_unofficial_20")
|
api("com.github.turasa:signal-service-java:2.15.3_unofficial_21")
|
||||||
implementation("com.google.protobuf:protobuf-javalite:3.10.0")
|
implementation("com.google.protobuf:protobuf-javalite:3.10.0")
|
||||||
implementation("org.bouncycastle:bcprov-jdk15on:1.68")
|
implementation("org.bouncycastle:bcprov-jdk15on:1.68")
|
||||||
implementation("org.slf4j:slf4j-api:1.7.30")
|
implementation("org.slf4j:slf4j-api:1.7.30")
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.asamk.signal.manager;
|
package org.asamk.signal.manager;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.api.Device;
|
||||||
import org.asamk.signal.manager.config.ServiceConfig;
|
import org.asamk.signal.manager.config.ServiceConfig;
|
||||||
import org.asamk.signal.manager.config.ServiceEnvironment;
|
import org.asamk.signal.manager.config.ServiceEnvironment;
|
||||||
import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
|
import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
|
||||||
|
@ -108,7 +109,6 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsO
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup;
|
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream;
|
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream;
|
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo;
|
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
|
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;
|
||||||
|
@ -119,6 +119,7 @@ import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException;
|
import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
||||||
|
import org.whispersystems.signalservice.api.util.DeviceNameUtil;
|
||||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||||
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
||||||
import org.whispersystems.signalservice.api.util.SleepTimer;
|
import org.whispersystems.signalservice.api.util.SleepTimer;
|
||||||
|
@ -341,7 +342,7 @@ public class Manager implements Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateAccountAttributes() throws IOException {
|
public void updateAccountAttributes() throws IOException {
|
||||||
accountManager.setAccountAttributes(account.getDeviceName(),
|
accountManager.setAccountAttributes(account.getEncryptedDeviceName(),
|
||||||
null,
|
null,
|
||||||
account.getLocalRegistrationId(),
|
account.getLocalRegistrationId(),
|
||||||
true,
|
true,
|
||||||
|
@ -422,10 +423,21 @@ public class Manager implements Closeable {
|
||||||
account.setRegistered(false);
|
account.setRegistered(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<DeviceInfo> getLinkedDevices() throws IOException {
|
public List<Device> getLinkedDevices() throws IOException {
|
||||||
var devices = accountManager.getDevices();
|
var devices = accountManager.getDevices();
|
||||||
account.setMultiDevice(devices.size() > 1);
|
account.setMultiDevice(devices.size() > 1);
|
||||||
return devices;
|
var identityKey = account.getIdentityKeyPair().getPrivateKey();
|
||||||
|
return devices.stream().map(d -> {
|
||||||
|
String deviceName = d.getName();
|
||||||
|
if (deviceName != null) {
|
||||||
|
try {
|
||||||
|
deviceName = DeviceNameUtil.decryptDeviceName(deviceName, identityKey);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.debug("Failed to decrypt device name, maybe plain text?", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Device(d.getId(), deviceName, d.getCreated(), d.getLastSeen());
|
||||||
|
}).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeLinkedDevices(int deviceId) throws IOException {
|
public void removeLinkedDevices(int deviceId) throws IOException {
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
|
import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
|
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
import org.whispersystems.signalservice.api.util.DeviceNameUtil;
|
||||||
import org.whispersystems.signalservice.api.util.SleepTimer;
|
import org.whispersystems.signalservice.api.util.SleepTimer;
|
||||||
import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
|
import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
|
||||||
import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider;
|
import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider;
|
||||||
|
@ -47,7 +48,7 @@ public class ProvisioningManager {
|
||||||
private final String userAgent;
|
private final String userAgent;
|
||||||
|
|
||||||
private final SignalServiceAccountManager accountManager;
|
private final SignalServiceAccountManager accountManager;
|
||||||
private final IdentityKeyPair identityKey;
|
private final IdentityKeyPair tempIdentityKey;
|
||||||
private final int registrationId;
|
private final int registrationId;
|
||||||
private final String password;
|
private final String password;
|
||||||
|
|
||||||
|
@ -56,7 +57,7 @@ public class ProvisioningManager {
|
||||||
this.serviceEnvironmentConfig = serviceEnvironmentConfig;
|
this.serviceEnvironmentConfig = serviceEnvironmentConfig;
|
||||||
this.userAgent = userAgent;
|
this.userAgent = userAgent;
|
||||||
|
|
||||||
identityKey = KeyUtils.generateIdentityKeyPair();
|
tempIdentityKey = KeyUtils.generateIdentityKeyPair();
|
||||||
registrationId = KeyHelper.generateRegistrationId(false);
|
registrationId = KeyHelper.generateRegistrationId(false);
|
||||||
password = KeyUtils.createPassword();
|
password = KeyUtils.createPassword();
|
||||||
final SleepTimer timer = new UptimeSleepTimer();
|
final SleepTimer timer = new UptimeSleepTimer();
|
||||||
|
@ -87,22 +88,26 @@ public class ProvisioningManager {
|
||||||
public URI getDeviceLinkUri() throws TimeoutException, IOException {
|
public URI getDeviceLinkUri() throws TimeoutException, IOException {
|
||||||
var deviceUuid = accountManager.getNewDeviceUuid();
|
var deviceUuid = accountManager.getNewDeviceUuid();
|
||||||
|
|
||||||
return new DeviceLinkInfo(deviceUuid, identityKey.getPublicKey().getPublicKey()).createDeviceLinkUri();
|
return new DeviceLinkInfo(deviceUuid, tempIdentityKey.getPublicKey().getPublicKey()).createDeviceLinkUri();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Manager finishDeviceLink(String deviceName) throws IOException, TimeoutException, UserAlreadyExists {
|
public Manager finishDeviceLink(String deviceName) throws IOException, TimeoutException, UserAlreadyExists {
|
||||||
var ret = accountManager.getNewDeviceRegistration(identityKey);
|
var ret = accountManager.getNewDeviceRegistration(tempIdentityKey);
|
||||||
var number = ret.getNumber();
|
var number = ret.getNumber();
|
||||||
|
|
||||||
if (SignalAccount.userExists(pathConfig.getDataPath(), number)) {
|
if (SignalAccount.userExists(pathConfig.getDataPath(), number)) {
|
||||||
throw new UserAlreadyExists(number, SignalAccount.getFileName(pathConfig.getDataPath(), number));
|
throw new UserAlreadyExists(number, SignalAccount.getFileName(pathConfig.getDataPath(), number));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var encryptedDeviceName = deviceName == null
|
||||||
|
? null
|
||||||
|
: DeviceNameUtil.encryptDeviceName(deviceName, ret.getIdentity().getPrivateKey());
|
||||||
|
|
||||||
var deviceId = accountManager.finishNewDeviceRegistration(ret.getProvisioningCode(),
|
var deviceId = accountManager.finishNewDeviceRegistration(ret.getProvisioningCode(),
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
registrationId,
|
registrationId,
|
||||||
deviceName);
|
encryptedDeviceName);
|
||||||
|
|
||||||
// Create new account with the synced identity
|
// Create new account with the synced identity
|
||||||
var profileKey = ret.getProfileKey() == null ? KeyUtils.createProfileKey() : ret.getProfileKey();
|
var profileKey = ret.getProfileKey() == null ? KeyUtils.createProfileKey() : ret.getProfileKey();
|
||||||
|
@ -113,7 +118,7 @@ public class ProvisioningManager {
|
||||||
number,
|
number,
|
||||||
ret.getUuid(),
|
ret.getUuid(),
|
||||||
password,
|
password,
|
||||||
deviceName,
|
encryptedDeviceName,
|
||||||
deviceId,
|
deviceId,
|
||||||
ret.getIdentity(),
|
ret.getIdentity(),
|
||||||
registrationId,
|
registrationId,
|
||||||
|
|
32
lib/src/main/java/org/asamk/signal/manager/api/Device.java
Normal file
32
lib/src/main/java/org/asamk/signal/manager/api/Device.java
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package org.asamk.signal.manager.api;
|
||||||
|
|
||||||
|
public class Device {
|
||||||
|
|
||||||
|
private final long id;
|
||||||
|
private final String name;
|
||||||
|
private final long created;
|
||||||
|
private final long lastSeen;
|
||||||
|
|
||||||
|
public Device(long id, String name, long created, long lastSeen) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.created = created;
|
||||||
|
this.lastSeen = lastSeen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCreated() {
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLastSeen() {
|
||||||
|
return lastSeen;
|
||||||
|
}
|
||||||
|
}
|
|
@ -74,7 +74,7 @@ public class SignalAccount implements Closeable {
|
||||||
|
|
||||||
private String username;
|
private String username;
|
||||||
private UUID uuid;
|
private UUID uuid;
|
||||||
private String deviceName;
|
private String encryptedDeviceName;
|
||||||
private int deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
|
private int deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
|
||||||
private boolean isMultiDevice = false;
|
private boolean isMultiDevice = false;
|
||||||
private String password;
|
private String password;
|
||||||
|
@ -172,7 +172,7 @@ public class SignalAccount implements Closeable {
|
||||||
String username,
|
String username,
|
||||||
UUID uuid,
|
UUID uuid,
|
||||||
String password,
|
String password,
|
||||||
String deviceName,
|
String encryptedDeviceName,
|
||||||
int deviceId,
|
int deviceId,
|
||||||
IdentityKeyPair identityKey,
|
IdentityKeyPair identityKey,
|
||||||
int registrationId,
|
int registrationId,
|
||||||
|
@ -191,7 +191,7 @@ public class SignalAccount implements Closeable {
|
||||||
account.uuid = uuid;
|
account.uuid = uuid;
|
||||||
account.password = password;
|
account.password = password;
|
||||||
account.profileKey = profileKey;
|
account.profileKey = profileKey;
|
||||||
account.deviceName = deviceName;
|
account.encryptedDeviceName = encryptedDeviceName;
|
||||||
account.deviceId = deviceId;
|
account.deviceId = deviceId;
|
||||||
|
|
||||||
account.initStores(dataPath, identityKey, registrationId);
|
account.initStores(dataPath, identityKey, registrationId);
|
||||||
|
@ -307,7 +307,7 @@ public class SignalAccount implements Closeable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rootNode.hasNonNull("deviceName")) {
|
if (rootNode.hasNonNull("deviceName")) {
|
||||||
deviceName = rootNode.get("deviceName").asText();
|
encryptedDeviceName = rootNode.get("deviceName").asText();
|
||||||
}
|
}
|
||||||
if (rootNode.hasNonNull("deviceId")) {
|
if (rootNode.hasNonNull("deviceId")) {
|
||||||
deviceId = rootNode.get("deviceId").asInt();
|
deviceId = rootNode.get("deviceId").asInt();
|
||||||
|
@ -579,7 +579,7 @@ public class SignalAccount implements Closeable {
|
||||||
rootNode.put("version", CURRENT_STORAGE_VERSION)
|
rootNode.put("version", CURRENT_STORAGE_VERSION)
|
||||||
.put("username", username)
|
.put("username", username)
|
||||||
.put("uuid", uuid == null ? null : uuid.toString())
|
.put("uuid", uuid == null ? null : uuid.toString())
|
||||||
.put("deviceName", deviceName)
|
.put("deviceName", encryptedDeviceName)
|
||||||
.put("deviceId", deviceId)
|
.put("deviceId", deviceId)
|
||||||
.put("isMultiDevice", isMultiDevice)
|
.put("isMultiDevice", isMultiDevice)
|
||||||
.put("password", password)
|
.put("password", password)
|
||||||
|
@ -708,8 +708,8 @@ public class SignalAccount implements Closeable {
|
||||||
return recipientStore.resolveRecipientTrusted(getSelfAddress());
|
return recipientStore.resolveRecipientTrusted(getSelfAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDeviceName() {
|
public String getEncryptedDeviceName() {
|
||||||
return deviceName;
|
return encryptedDeviceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDeviceId() {
|
public int getDeviceId() {
|
||||||
|
@ -823,7 +823,7 @@ public class SignalAccount implements Closeable {
|
||||||
|
|
||||||
public void finishRegistration(final UUID uuid, final MasterKey masterKey, final String pin) {
|
public void finishRegistration(final UUID uuid, final MasterKey masterKey, final String pin) {
|
||||||
this.pinMasterKey = masterKey;
|
this.pinMasterKey = masterKey;
|
||||||
this.deviceName = null;
|
this.encryptedDeviceName = null;
|
||||||
this.deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
|
this.deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
|
||||||
this.isMultiDevice = false;
|
this.isMultiDevice = false;
|
||||||
this.registered = true;
|
this.registered = true;
|
||||||
|
|
|
@ -7,10 +7,10 @@ import org.asamk.signal.PlainTextWriterImpl;
|
||||||
import org.asamk.signal.commands.exceptions.CommandException;
|
import org.asamk.signal.commands.exceptions.CommandException;
|
||||||
import org.asamk.signal.commands.exceptions.IOErrorException;
|
import org.asamk.signal.commands.exceptions.IOErrorException;
|
||||||
import org.asamk.signal.manager.Manager;
|
import org.asamk.signal.manager.Manager;
|
||||||
|
import org.asamk.signal.manager.api.Device;
|
||||||
import org.asamk.signal.util.DateUtils;
|
import org.asamk.signal.util.DateUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -27,7 +27,7 @@ public class ListDevicesCommand implements LocalCommand {
|
||||||
public void handleCommand(final Namespace ns, final Manager m) throws CommandException {
|
public void handleCommand(final Namespace ns, final Manager m) throws CommandException {
|
||||||
final var writer = new PlainTextWriterImpl(System.out);
|
final var writer = new PlainTextWriterImpl(System.out);
|
||||||
|
|
||||||
List<DeviceInfo> devices;
|
List<Device> devices;
|
||||||
try {
|
try {
|
||||||
devices = m.getLinkedDevices();
|
devices = m.getLinkedDevices();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue