mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 18:40:39 +00:00
Refactor register and verify
This commit is contained in:
parent
6c8a1ff3d3
commit
e74be0c345
33 changed files with 405 additions and 362 deletions
|
@ -32,9 +32,12 @@ import org.asamk.signal.commands.DbusCommand;
|
||||||
import org.asamk.signal.commands.ExtendedDbusCommand;
|
import org.asamk.signal.commands.ExtendedDbusCommand;
|
||||||
import org.asamk.signal.commands.LocalCommand;
|
import org.asamk.signal.commands.LocalCommand;
|
||||||
import org.asamk.signal.commands.ProvisioningCommand;
|
import org.asamk.signal.commands.ProvisioningCommand;
|
||||||
|
import org.asamk.signal.commands.RegistrationCommand;
|
||||||
import org.asamk.signal.dbus.DbusSignalImpl;
|
import org.asamk.signal.dbus.DbusSignalImpl;
|
||||||
import org.asamk.signal.manager.Manager;
|
import org.asamk.signal.manager.Manager;
|
||||||
|
import org.asamk.signal.manager.NotRegisteredException;
|
||||||
import org.asamk.signal.manager.ProvisioningManager;
|
import org.asamk.signal.manager.ProvisioningManager;
|
||||||
|
import org.asamk.signal.manager.RegistrationManager;
|
||||||
import org.asamk.signal.manager.ServiceConfig;
|
import org.asamk.signal.manager.ServiceConfig;
|
||||||
import org.asamk.signal.util.IOUtils;
|
import org.asamk.signal.util.IOUtils;
|
||||||
import org.asamk.signal.util.SecurityProvider;
|
import org.asamk.signal.util.SecurityProvider;
|
||||||
|
@ -43,7 +46,6 @@ import org.freedesktop.dbus.connections.impl.DBusConnection;
|
||||||
import org.freedesktop.dbus.exceptions.DBusException;
|
import org.freedesktop.dbus.exceptions.DBusException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException;
|
|
||||||
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
||||||
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
|
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
|
||||||
|
|
||||||
|
@ -75,8 +77,14 @@ public class Main {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int init(Namespace ns) {
|
public static int init(Namespace ns) {
|
||||||
|
Command command = getCommand(ns);
|
||||||
|
if (command == null) {
|
||||||
|
logger.error("Command not implemented!");
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
if (ns.getBoolean("dbus") || ns.getBoolean("dbus_system")) {
|
if (ns.getBoolean("dbus") || ns.getBoolean("dbus_system")) {
|
||||||
return initDbusClient(ns, ns.getBoolean("dbus_system"));
|
return initDbusClient(command, ns, ns.getBoolean("dbus_system"));
|
||||||
}
|
}
|
||||||
|
|
||||||
final String username = ns.getString("username");
|
final String username = ns.getString("username");
|
||||||
|
@ -99,12 +107,31 @@ public class Main {
|
||||||
|
|
||||||
if (username == null) {
|
if (username == null) {
|
||||||
ProvisioningManager pm = new ProvisioningManager(dataPath, serviceConfiguration, BaseConfig.USER_AGENT);
|
ProvisioningManager pm = new ProvisioningManager(dataPath, serviceConfiguration, BaseConfig.USER_AGENT);
|
||||||
return handleCommands(ns, pm);
|
return handleCommand(command, ns, pm);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command instanceof RegistrationCommand) {
|
||||||
|
final RegistrationManager manager;
|
||||||
|
try {
|
||||||
|
manager = RegistrationManager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
logger.error("Error loading or creating state file: {}", e.getMessage());
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
try (RegistrationManager m = manager) {
|
||||||
|
return handleCommand(command, ns, m);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Cleanup failed", e);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Manager manager;
|
Manager manager;
|
||||||
try {
|
try {
|
||||||
manager = Manager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT);
|
manager = Manager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT);
|
||||||
|
} catch (NotRegisteredException e) {
|
||||||
|
System.err.println("User is not registered.");
|
||||||
|
return 1;
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
logger.error("Error loading state file: {}", e.getMessage());
|
logger.error("Error loading state file: {}", e.getMessage());
|
||||||
return 2;
|
return 2;
|
||||||
|
@ -113,25 +140,19 @@ public class Main {
|
||||||
try (Manager m = manager) {
|
try (Manager m = manager) {
|
||||||
try {
|
try {
|
||||||
m.checkAccountState();
|
m.checkAccountState();
|
||||||
} catch (AuthorizationFailedException e) {
|
|
||||||
if (!"register".equals(ns.getString("command"))) {
|
|
||||||
// Register command should still be possible, if current authorization fails
|
|
||||||
System.err.println("Authorization failed, was the number registered elsewhere?");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Error while checking account: {}", e.getMessage());
|
logger.error("Error while checking account: {}", e.getMessage());
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return handleCommands(ns, m);
|
return handleCommand(command, ns, m);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Cleanup failed", e);
|
logger.error("Cleanup failed", e);
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int initDbusClient(final Namespace ns, final boolean systemBus) {
|
private static int initDbusClient(final Command command, final Namespace ns, final boolean systemBus) {
|
||||||
try {
|
try {
|
||||||
DBusConnection.DBusBusType busType;
|
DBusConnection.DBusBusType busType;
|
||||||
if (systemBus) {
|
if (systemBus) {
|
||||||
|
@ -144,7 +165,7 @@ public class Main {
|
||||||
DbusConfig.SIGNAL_OBJECTPATH,
|
DbusConfig.SIGNAL_OBJECTPATH,
|
||||||
Signal.class);
|
Signal.class);
|
||||||
|
|
||||||
return handleCommands(ns, ts, dBusConn);
|
return handleCommand(command, ns, ts, dBusConn);
|
||||||
}
|
}
|
||||||
} catch (DBusException | IOException e) {
|
} catch (DBusException | IOException e) {
|
||||||
logger.error("Dbus client failed", e);
|
logger.error("Dbus client failed", e);
|
||||||
|
@ -152,56 +173,51 @@ public class Main {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int handleCommands(Namespace ns, Signal ts, DBusConnection dBusConn) {
|
private static Command getCommand(Namespace ns) {
|
||||||
String commandKey = ns.getString("command");
|
String commandKey = ns.getString("command");
|
||||||
final Map<String, Command> commands = Commands.getCommands();
|
final Map<String, Command> commands = Commands.getCommands();
|
||||||
if (commands.containsKey(commandKey)) {
|
if (!commands.containsKey(commandKey)) {
|
||||||
Command command = commands.get(commandKey);
|
return null;
|
||||||
|
|
||||||
if (command instanceof ExtendedDbusCommand) {
|
|
||||||
return ((ExtendedDbusCommand) command).handleCommand(ns, ts, dBusConn);
|
|
||||||
} else if (command instanceof DbusCommand) {
|
|
||||||
return ((DbusCommand) command).handleCommand(ns, ts);
|
|
||||||
} else {
|
|
||||||
System.err.println(commandKey + " is not yet implemented via dbus");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return commands.get(commandKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int handleCommands(Namespace ns, ProvisioningManager pm) {
|
private static int handleCommand(Command command, Namespace ns, Signal ts, DBusConnection dBusConn) {
|
||||||
String commandKey = ns.getString("command");
|
if (command instanceof ExtendedDbusCommand) {
|
||||||
final Map<String, Command> commands = Commands.getCommands();
|
return ((ExtendedDbusCommand) command).handleCommand(ns, ts, dBusConn);
|
||||||
if (commands.containsKey(commandKey)) {
|
} else if (command instanceof DbusCommand) {
|
||||||
Command command = commands.get(commandKey);
|
return ((DbusCommand) command).handleCommand(ns, ts);
|
||||||
|
} else {
|
||||||
if (command instanceof ProvisioningCommand) {
|
System.err.println("Command is not yet implemented via dbus");
|
||||||
return ((ProvisioningCommand) command).handleCommand(ns, pm);
|
return 1;
|
||||||
} else {
|
}
|
||||||
System.err.println(commandKey + " only works with a username");
|
}
|
||||||
return 1;
|
|
||||||
}
|
private static int handleCommand(Command command, Namespace ns, ProvisioningManager pm) {
|
||||||
}
|
if (command instanceof ProvisioningCommand) {
|
||||||
return 0;
|
return ((ProvisioningCommand) command).handleCommand(ns, pm);
|
||||||
}
|
} else {
|
||||||
|
System.err.println("Command only works with a username");
|
||||||
private static int handleCommands(Namespace ns, Manager m) {
|
return 1;
|
||||||
String commandKey = ns.getString("command");
|
}
|
||||||
final Map<String, Command> commands = Commands.getCommands();
|
}
|
||||||
if (commands.containsKey(commandKey)) {
|
|
||||||
Command command = commands.get(commandKey);
|
private static int handleCommand(Command command, Namespace ns, RegistrationManager m) {
|
||||||
|
if (command instanceof RegistrationCommand) {
|
||||||
if (command instanceof LocalCommand) {
|
return ((RegistrationCommand) command).handleCommand(ns, m);
|
||||||
return ((LocalCommand) command).handleCommand(ns, m);
|
}
|
||||||
} else if (command instanceof DbusCommand) {
|
return 1;
|
||||||
return ((DbusCommand) command).handleCommand(ns, new DbusSignalImpl(m));
|
}
|
||||||
} else if (command instanceof ExtendedDbusCommand) {
|
|
||||||
System.err.println(commandKey + " only works via dbus");
|
private static int handleCommand(Command command, Namespace ns, Manager m) {
|
||||||
}
|
if (command instanceof LocalCommand) {
|
||||||
|
return ((LocalCommand) command).handleCommand(ns, m);
|
||||||
|
} else if (command instanceof DbusCommand) {
|
||||||
|
return ((DbusCommand) command).handleCommand(ns, new DbusSignalImpl(m));
|
||||||
|
} else {
|
||||||
|
System.err.println("Command only works via dbus");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -23,10 +23,6 @@ public class AddDeviceCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
m.addDeviceLink(new URI(ns.getString("uri")));
|
m.addDeviceLink(new URI(ns.getString("uri")));
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -21,11 +21,6 @@ public class BlockCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String contact_number : ns.<String>getList("contact")) {
|
for (String contact_number : ns.<String>getList("contact")) {
|
||||||
try {
|
try {
|
||||||
m.setContactBlocked(contact_number, true);
|
m.setContactBlocked(contact_number, true);
|
||||||
|
|
|
@ -35,10 +35,6 @@ public class DaemonCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
DBusConnection conn = null;
|
DBusConnection conn = null;
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -28,11 +28,6 @@ public class GetUserStatusCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup the json object mapper
|
// Setup the json object mapper
|
||||||
ObjectMapper jsonProcessor = new ObjectMapper();
|
ObjectMapper jsonProcessor = new ObjectMapper();
|
||||||
jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
|
jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
|
||||||
|
|
|
@ -29,11 +29,6 @@ public class JoinGroupCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
final GroupInviteLinkUrl linkUrl;
|
final GroupInviteLinkUrl linkUrl;
|
||||||
String uri = ns.getString("uri");
|
String uri = ns.getString("uri");
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -16,10 +16,6 @@ public class ListContactsCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
List<ContactInfo> contacts = m.getContacts();
|
List<ContactInfo> contacts = m.getContacts();
|
||||||
for (ContactInfo c : contacts) {
|
for (ContactInfo c : contacts) {
|
||||||
System.out.println(String.format("Number: %s Name: %s Blocked: %b", c.number, c.name, c.blocked));
|
System.out.println(String.format("Number: %s Name: %s Blocked: %b", c.number, c.name, c.blocked));
|
||||||
|
|
|
@ -18,10 +18,6 @@ public class ListDevicesCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
List<DeviceInfo> devices = m.getLinkedDevices();
|
List<DeviceInfo> devices = m.getLinkedDevices();
|
||||||
for (DeviceInfo d : devices) {
|
for (DeviceInfo d : devices) {
|
||||||
|
|
|
@ -64,11 +64,6 @@ public class ListGroupsCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<GroupInfo> groups = m.getGroups();
|
List<GroupInfo> groups = m.getGroups();
|
||||||
boolean detailed = ns.getBoolean("detailed");
|
boolean detailed = ns.getBoolean("detailed");
|
||||||
|
|
||||||
|
|
|
@ -30,10 +30,6 @@ public class ListIdentitiesCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (ns.get("number") == null) {
|
if (ns.get("number") == null) {
|
||||||
for (IdentityInfo identity : m.getIdentities()) {
|
for (IdentityInfo identity : m.getIdentities()) {
|
||||||
printIdentityFingerprint(m, identity);
|
printIdentityFingerprint(m, identity);
|
||||||
|
|
|
@ -31,11 +31,6 @@ public class QuitGroupCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final GroupId groupId = Util.decodeGroupId(ns.getString("group"));
|
final GroupId groupId = Util.decodeGroupId(ns.getString("group"));
|
||||||
final Pair<Long, List<SendMessageResult>> results = m.sendQuitGroupMessage(groupId);
|
final Pair<Long, List<SendMessageResult>> results = m.sendQuitGroupMessage(groupId);
|
||||||
|
|
|
@ -146,10 +146,6 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
double timeout = 5;
|
double timeout = 5;
|
||||||
if (ns.getDouble("timeout") != null) {
|
if (ns.getDouble("timeout") != null) {
|
||||||
timeout = ns.getDouble("timeout");
|
timeout = ns.getDouble("timeout");
|
||||||
|
|
|
@ -4,12 +4,12 @@ import net.sourceforge.argparse4j.impl.Arguments;
|
||||||
import net.sourceforge.argparse4j.inf.Namespace;
|
import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
import net.sourceforge.argparse4j.inf.Subparser;
|
import net.sourceforge.argparse4j.inf.Subparser;
|
||||||
|
|
||||||
import org.asamk.signal.manager.Manager;
|
import org.asamk.signal.manager.RegistrationManager;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.CaptchaRequiredException;
|
import org.whispersystems.signalservice.api.push.exceptions.CaptchaRequiredException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class RegisterCommand implements LocalCommand {
|
public class RegisterCommand implements RegistrationCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void attachToSubparser(final Subparser subparser) {
|
public void attachToSubparser(final Subparser subparser) {
|
||||||
|
@ -21,7 +21,7 @@ public class RegisterCommand implements LocalCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final RegistrationManager m) {
|
||||||
try {
|
try {
|
||||||
final boolean voiceVerification = ns.getBoolean("voice");
|
final boolean voiceVerification = ns.getBoolean("voice");
|
||||||
final String captcha = ns.getString("captcha");
|
final String captcha = ns.getString("captcha");
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
package org.asamk.signal.commands;
|
||||||
|
|
||||||
|
import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.RegistrationManager;
|
||||||
|
|
||||||
|
public interface RegistrationCommand extends Command {
|
||||||
|
|
||||||
|
int handleCommand(Namespace ns, RegistrationManager m);
|
||||||
|
}
|
|
@ -19,10 +19,6 @@ public class RemoveDeviceCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
int deviceId = ns.getInt("deviceId");
|
int deviceId = ns.getInt("deviceId");
|
||||||
m.removeLinkedDevices(deviceId);
|
m.removeLinkedDevices(deviceId);
|
||||||
|
|
|
@ -17,10 +17,6 @@ public class RemovePinCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
m.setRegistrationLockPin(Optional.absent());
|
m.setRegistrationLockPin(Optional.absent());
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -17,10 +17,6 @@ public class SendContactsCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
m.sendContacts();
|
m.sendContacts();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -47,11 +47,6 @@ public class SendReactionCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((ns.getList("recipient") == null || ns.getList("recipient").size() == 0) && ns.getString("group") == null) {
|
if ((ns.getList("recipient") == null || ns.getList("recipient").size() == 0) && ns.getString("group") == null) {
|
||||||
System.err.println("No recipients given");
|
System.err.println("No recipients given");
|
||||||
System.err.println("Aborting sending.");
|
System.err.println("Aborting sending.");
|
||||||
|
|
|
@ -19,10 +19,6 @@ public class SetPinCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
String registrationLockPin = ns.getString("registrationLockPin");
|
String registrationLockPin = ns.getString("registrationLockPin");
|
||||||
m.setRegistrationLockPin(Optional.of(registrationLockPin));
|
m.setRegistrationLockPin(Optional.of(registrationLockPin));
|
||||||
|
|
|
@ -27,10 +27,6 @@ public class TrustCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
String number = ns.getString("number");
|
String number = ns.getString("number");
|
||||||
if (ns.getBoolean("trust_all_known_keys")) {
|
if (ns.getBoolean("trust_all_known_keys")) {
|
||||||
boolean res = m.trustIdentityAllKeys(number);
|
boolean res = m.trustIdentityAllKeys(number);
|
||||||
|
|
|
@ -21,11 +21,6 @@ public class UnblockCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String contact_number : ns.<String>getList("contact")) {
|
for (String contact_number : ns.<String>getList("contact")) {
|
||||||
try {
|
try {
|
||||||
m.setContactBlocked(contact_number, false);
|
m.setContactBlocked(contact_number, false);
|
||||||
|
|
|
@ -16,10 +16,6 @@ public class UnregisterCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
m.unregister();
|
m.unregister();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -16,10 +16,6 @@ public class UpdateAccountCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
m.updateAccountAttributes();
|
m.updateAccountAttributes();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -23,11 +23,6 @@ public class UpdateContactCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
String number = ns.getString("number");
|
String number = ns.getString("number");
|
||||||
String name = ns.getString("name");
|
String name = ns.getString("name");
|
||||||
|
|
||||||
|
|
|
@ -25,11 +25,6 @@ public class UpdateProfileCommand implements LocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final Manager m) {
|
||||||
if (!m.isRegistered()) {
|
|
||||||
System.err.println("User is not registered.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
String name = ns.getString("name");
|
String name = ns.getString("name");
|
||||||
String avatarPath = ns.getString("avatar");
|
String avatarPath = ns.getString("avatar");
|
||||||
boolean removeAvatar = ns.getBoolean("remove_avatar");
|
boolean removeAvatar = ns.getBoolean("remove_avatar");
|
||||||
|
|
|
@ -3,14 +3,14 @@ package org.asamk.signal.commands;
|
||||||
import net.sourceforge.argparse4j.inf.Namespace;
|
import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
import net.sourceforge.argparse4j.inf.Subparser;
|
import net.sourceforge.argparse4j.inf.Subparser;
|
||||||
|
|
||||||
import org.asamk.signal.manager.Manager;
|
import org.asamk.signal.manager.RegistrationManager;
|
||||||
import org.whispersystems.signalservice.api.KeyBackupServicePinException;
|
import org.whispersystems.signalservice.api.KeyBackupServicePinException;
|
||||||
import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException;
|
import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException;
|
||||||
import org.whispersystems.signalservice.internal.push.LockedException;
|
import org.whispersystems.signalservice.internal.push.LockedException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class VerifyCommand implements LocalCommand {
|
public class VerifyCommand implements RegistrationCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void attachToSubparser(final Subparser subparser) {
|
public void attachToSubparser(final Subparser subparser) {
|
||||||
|
@ -19,11 +19,7 @@ public class VerifyCommand implements LocalCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int handleCommand(final Namespace ns, final Manager m) {
|
public int handleCommand(final Namespace ns, final RegistrationManager m) {
|
||||||
if (m.isRegistered()) {
|
|
||||||
System.err.println("User registration is already verified");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
String verificationCode = ns.getString("verificationCode");
|
String verificationCode = ns.getString("verificationCode");
|
||||||
String pin = ns.getString("pin");
|
String pin = ns.getString("pin");
|
||||||
|
|
|
@ -79,14 +79,10 @@ import org.whispersystems.libsignal.ecc.ECKeyPair;
|
||||||
import org.whispersystems.libsignal.ecc.ECPublicKey;
|
import org.whispersystems.libsignal.ecc.ECPublicKey;
|
||||||
import org.whispersystems.libsignal.state.PreKeyRecord;
|
import org.whispersystems.libsignal.state.PreKeyRecord;
|
||||||
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
||||||
import org.whispersystems.libsignal.util.KeyHelper;
|
|
||||||
import org.whispersystems.libsignal.util.Medium;
|
import org.whispersystems.libsignal.util.Medium;
|
||||||
import org.whispersystems.libsignal.util.Pair;
|
import org.whispersystems.libsignal.util.Pair;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.KbsPinData;
|
|
||||||
import org.whispersystems.signalservice.api.KeyBackupService;
|
import org.whispersystems.signalservice.api.KeyBackupService;
|
||||||
import org.whispersystems.signalservice.api.KeyBackupServicePinException;
|
|
||||||
import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException;
|
|
||||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessagePipe;
|
import org.whispersystems.signalservice.api.SignalServiceMessagePipe;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
|
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
|
||||||
|
@ -144,10 +140,8 @@ import org.whispersystems.signalservice.internal.configuration.SignalServiceConf
|
||||||
import org.whispersystems.signalservice.internal.contacts.crypto.Quote;
|
import org.whispersystems.signalservice.internal.contacts.crypto.Quote;
|
||||||
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedQuoteException;
|
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedQuoteException;
|
||||||
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException;
|
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException;
|
||||||
import org.whispersystems.signalservice.internal.push.LockedException;
|
|
||||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
|
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
|
||||||
import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException;
|
import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException;
|
||||||
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse;
|
|
||||||
import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider;
|
import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider;
|
||||||
import org.whispersystems.signalservice.internal.util.Hex;
|
import org.whispersystems.signalservice.internal.util.Hex;
|
||||||
import org.whispersystems.util.Base64;
|
import org.whispersystems.util.Base64;
|
||||||
|
@ -167,7 +161,6 @@ import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.security.KeyStore;
|
|
||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -176,7 +169,6 @@ import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -202,24 +194,21 @@ public class Manager implements Closeable {
|
||||||
private final SignalServiceConfiguration serviceConfiguration;
|
private final SignalServiceConfiguration serviceConfiguration;
|
||||||
private final String userAgent;
|
private final String userAgent;
|
||||||
|
|
||||||
// TODO make configurable
|
private SignalAccount account;
|
||||||
private final boolean discoverableByPhoneNumber = true;
|
|
||||||
private final boolean unrestrictedUnidentifiedAccess = false;
|
|
||||||
|
|
||||||
private final SignalAccount account;
|
|
||||||
private final PathConfig pathConfig;
|
private final PathConfig pathConfig;
|
||||||
private SignalServiceAccountManager accountManager;
|
private final SignalServiceAccountManager accountManager;
|
||||||
private GroupsV2Api groupsV2Api;
|
private final GroupsV2Api groupsV2Api;
|
||||||
private final GroupsV2Operations groupsV2Operations;
|
private final GroupsV2Operations groupsV2Operations;
|
||||||
|
private final SignalServiceMessageReceiver messageReceiver;
|
||||||
|
private final ClientZkProfileOperations clientZkProfileOperations;
|
||||||
|
|
||||||
private SignalServiceMessageReceiver messageReceiver = null;
|
|
||||||
private SignalServiceMessagePipe messagePipe = null;
|
private SignalServiceMessagePipe messagePipe = null;
|
||||||
private SignalServiceMessagePipe unidentifiedMessagePipe = null;
|
private SignalServiceMessagePipe unidentifiedMessagePipe = null;
|
||||||
|
|
||||||
private final UnidentifiedAccessHelper unidentifiedAccessHelper;
|
private final UnidentifiedAccessHelper unidentifiedAccessHelper;
|
||||||
private final ProfileHelper profileHelper;
|
private final ProfileHelper profileHelper;
|
||||||
private final GroupHelper groupHelper;
|
private final GroupHelper groupHelper;
|
||||||
private PinHelper pinHelper;
|
private final PinHelper pinHelper;
|
||||||
|
|
||||||
Manager(
|
Manager(
|
||||||
SignalAccount account,
|
SignalAccount account,
|
||||||
|
@ -233,7 +222,30 @@ public class Manager implements Closeable {
|
||||||
this.userAgent = userAgent;
|
this.userAgent = userAgent;
|
||||||
this.groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create(
|
this.groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create(
|
||||||
serviceConfiguration)) : null;
|
serviceConfiguration)) : null;
|
||||||
createSignalServiceAccountManager();
|
this.accountManager = new SignalServiceAccountManager(serviceConfiguration,
|
||||||
|
new DynamicCredentialsProvider(account.getUuid(),
|
||||||
|
account.getUsername(),
|
||||||
|
account.getPassword(),
|
||||||
|
account.getSignalingKey(),
|
||||||
|
account.getDeviceId()),
|
||||||
|
userAgent,
|
||||||
|
groupsV2Operations,
|
||||||
|
timer);
|
||||||
|
this.groupsV2Api = accountManager.getGroupsV2Api();
|
||||||
|
final KeyBackupService keyBackupService = ServiceConfig.createKeyBackupService(accountManager);
|
||||||
|
this.pinHelper = new PinHelper(keyBackupService);
|
||||||
|
this.clientZkProfileOperations = capabilities.isGv2() ? ClientZkOperations.create(serviceConfiguration)
|
||||||
|
.getProfileOperations() : null;
|
||||||
|
this.messageReceiver = new SignalServiceMessageReceiver(serviceConfiguration,
|
||||||
|
account.getUuid(),
|
||||||
|
account.getUsername(),
|
||||||
|
account.getPassword(),
|
||||||
|
account.getDeviceId(),
|
||||||
|
account.getSignalingKey(),
|
||||||
|
userAgent,
|
||||||
|
null,
|
||||||
|
timer,
|
||||||
|
clientZkProfileOperations);
|
||||||
|
|
||||||
this.account.setResolver(this::resolveSignalServiceAddress);
|
this.account.setResolver(this::resolveSignalServiceAddress);
|
||||||
|
|
||||||
|
@ -244,7 +256,7 @@ public class Manager implements Closeable {
|
||||||
this.profileHelper = new ProfileHelper(account.getProfileStore()::getProfileKey,
|
this.profileHelper = new ProfileHelper(account.getProfileStore()::getProfileKey,
|
||||||
unidentifiedAccessHelper::getAccessFor,
|
unidentifiedAccessHelper::getAccessFor,
|
||||||
unidentified -> unidentified ? getOrCreateUnidentifiedMessagePipe() : getOrCreateMessagePipe(),
|
unidentified -> unidentified ? getOrCreateUnidentifiedMessagePipe() : getOrCreateMessagePipe(),
|
||||||
this::getOrCreateMessageReceiver);
|
() -> messageReceiver);
|
||||||
this.groupHelper = new GroupHelper(this::getRecipientProfileKeyCredential,
|
this.groupHelper = new GroupHelper(this::getRecipientProfileKeyCredential,
|
||||||
this::getRecipientProfile,
|
this::getRecipientProfile,
|
||||||
account::getSelfAddress,
|
account::getSelfAddress,
|
||||||
|
@ -261,30 +273,6 @@ public class Manager implements Closeable {
|
||||||
return account.getSelfAddress();
|
return account.getSelfAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createSignalServiceAccountManager() {
|
|
||||||
this.accountManager = new SignalServiceAccountManager(serviceConfiguration,
|
|
||||||
new DynamicCredentialsProvider(account.getUuid(),
|
|
||||||
account.getUsername(),
|
|
||||||
account.getPassword(),
|
|
||||||
null,
|
|
||||||
account.getDeviceId()),
|
|
||||||
userAgent,
|
|
||||||
groupsV2Operations,
|
|
||||||
timer);
|
|
||||||
this.groupsV2Api = accountManager.getGroupsV2Api();
|
|
||||||
this.pinHelper = new PinHelper(createKeyBackupService());
|
|
||||||
}
|
|
||||||
|
|
||||||
private KeyBackupService createKeyBackupService() {
|
|
||||||
KeyStore keyStore = ServiceConfig.getIasKeyStore();
|
|
||||||
|
|
||||||
return accountManager.getKeyBackupService(keyStore,
|
|
||||||
ServiceConfig.KEY_BACKUP_ENCLAVE_NAME,
|
|
||||||
ServiceConfig.KEY_BACKUP_SERVICE_ID,
|
|
||||||
ServiceConfig.KEY_BACKUP_MRENCLAVE,
|
|
||||||
10);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IdentityKeyPair getIdentityKeyPair() {
|
private IdentityKeyPair getIdentityKeyPair() {
|
||||||
return account.getSignalProtocolStore().getIdentityKeyPair();
|
return account.getSignalProtocolStore().getIdentityKeyPair();
|
||||||
}
|
}
|
||||||
|
@ -313,56 +301,20 @@ public class Manager implements Closeable {
|
||||||
|
|
||||||
public static Manager init(
|
public static Manager init(
|
||||||
String username, File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent
|
String username, File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent
|
||||||
) throws IOException {
|
) throws IOException, NotRegisteredException {
|
||||||
PathConfig pathConfig = PathConfig.createDefault(settingsPath);
|
PathConfig pathConfig = PathConfig.createDefault(settingsPath);
|
||||||
|
|
||||||
if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) {
|
if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) {
|
||||||
IdentityKeyPair identityKey = KeyUtils.generateIdentityKeyPair();
|
throw new NotRegisteredException();
|
||||||
int registrationId = KeyHelper.generateRegistrationId(false);
|
|
||||||
|
|
||||||
ProfileKey profileKey = KeyUtils.createProfileKey();
|
|
||||||
SignalAccount account = SignalAccount.create(pathConfig.getDataPath(),
|
|
||||||
username,
|
|
||||||
identityKey,
|
|
||||||
registrationId,
|
|
||||||
profileKey);
|
|
||||||
account.save();
|
|
||||||
|
|
||||||
return new Manager(account, pathConfig, serviceConfiguration, userAgent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalAccount account = SignalAccount.load(pathConfig.getDataPath(), username);
|
SignalAccount account = SignalAccount.load(pathConfig.getDataPath(), username);
|
||||||
|
|
||||||
Manager m = new Manager(account, pathConfig, serviceConfiguration, userAgent);
|
if (!account.isRegistered()) {
|
||||||
|
throw new NotRegisteredException();
|
||||||
m.migrateLegacyConfigs();
|
|
||||||
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void migrateLegacyConfigs() {
|
|
||||||
if (account.getProfileKey() == null && isRegistered()) {
|
|
||||||
// Old config file, creating new profile key
|
|
||||||
account.setProfileKey(KeyUtils.createProfileKey());
|
|
||||||
account.save();
|
|
||||||
}
|
}
|
||||||
// Store profile keys only in profile store
|
|
||||||
for (ContactInfo contact : account.getContactStore().getContacts()) {
|
return new Manager(account, pathConfig, serviceConfiguration, userAgent);
|
||||||
String profileKeyString = contact.profileKey;
|
|
||||||
if (profileKeyString == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final ProfileKey profileKey;
|
|
||||||
try {
|
|
||||||
profileKey = new ProfileKey(Base64.decode(profileKeyString));
|
|
||||||
} catch (InvalidInputException | IOException e) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
contact.profileKey = null;
|
|
||||||
account.getProfileStore().storeProfileKey(contact.getAddress(), profileKey);
|
|
||||||
}
|
|
||||||
// Ensure our profile key is stored in profile store
|
|
||||||
account.getProfileStore().storeProfileKey(getSelfAddress(), account.getProfileKey());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkAccountState() throws IOException {
|
public void checkAccountState() throws IOException {
|
||||||
|
@ -401,25 +353,6 @@ public class Manager implements Closeable {
|
||||||
return numbers.stream().collect(Collectors.toMap(x -> x, registeredUsers::contains));
|
return numbers.stream().collect(Collectors.toMap(x -> x, registeredUsers::contains));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void register(boolean voiceVerification, String captcha) throws IOException {
|
|
||||||
account.setPassword(KeyUtils.createPassword());
|
|
||||||
|
|
||||||
// Resetting UUID, because registering doesn't work otherwise
|
|
||||||
account.setUuid(null);
|
|
||||||
createSignalServiceAccountManager();
|
|
||||||
|
|
||||||
if (voiceVerification) {
|
|
||||||
accountManager.requestVoiceVerificationCode(Locale.getDefault(),
|
|
||||||
Optional.fromNullable(captcha),
|
|
||||||
Optional.absent());
|
|
||||||
} else {
|
|
||||||
accountManager.requestSmsVerificationCode(false, Optional.fromNullable(captcha), Optional.absent());
|
|
||||||
}
|
|
||||||
|
|
||||||
account.setRegistered(false);
|
|
||||||
account.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateAccountAttributes() throws IOException {
|
public void updateAccountAttributes() throws IOException {
|
||||||
accountManager.setAccountAttributes(account.getSignalingKey(),
|
accountManager.setAccountAttributes(account.getSignalingKey(),
|
||||||
account.getSignalProtocolStore().getLocalRegistrationId(),
|
account.getSignalProtocolStore().getLocalRegistrationId(),
|
||||||
|
@ -427,10 +360,10 @@ public class Manager implements Closeable {
|
||||||
// set legacy pin only if no KBS master key is set
|
// set legacy pin only if no KBS master key is set
|
||||||
account.getPinMasterKey() == null ? account.getRegistrationLockPin() : null,
|
account.getPinMasterKey() == null ? account.getRegistrationLockPin() : null,
|
||||||
account.getPinMasterKey() == null ? null : account.getPinMasterKey().deriveRegistrationLock(),
|
account.getPinMasterKey() == null ? null : account.getPinMasterKey().deriveRegistrationLock(),
|
||||||
unidentifiedAccessHelper.getSelfUnidentifiedAccessKey(),
|
account.getSelfUnidentifiedAccessKey(),
|
||||||
unrestrictedUnidentifiedAccess,
|
account.isUnrestrictedUnidentifiedAccess(),
|
||||||
capabilities,
|
capabilities,
|
||||||
discoverableByPhoneNumber);
|
account.isDiscoverableByPhoneNumber());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProfile(String name, File avatar) throws IOException {
|
public void setProfile(String name, File avatar) throws IOException {
|
||||||
|
@ -519,63 +452,6 @@ public class Manager implements Closeable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void verifyAccount(
|
|
||||||
String verificationCode, String pin
|
|
||||||
) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException {
|
|
||||||
verificationCode = verificationCode.replace("-", "");
|
|
||||||
account.setSignalingKey(KeyUtils.createSignalingKey());
|
|
||||||
VerifyAccountResponse response;
|
|
||||||
try {
|
|
||||||
response = verifyAccountWithCode(verificationCode, pin, null);
|
|
||||||
} catch (LockedException e) {
|
|
||||||
if (pin == null) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
KbsPinData registrationLockData = pinHelper.getRegistrationLockData(pin, e);
|
|
||||||
if (registrationLockData == null) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
String registrationLock = registrationLockData.getMasterKey().deriveRegistrationLock();
|
|
||||||
try {
|
|
||||||
response = verifyAccountWithCode(verificationCode, null, registrationLock);
|
|
||||||
} catch (LockedException _e) {
|
|
||||||
throw new AssertionError("KBS Pin appeared to matched but reg lock still failed!");
|
|
||||||
}
|
|
||||||
account.setPinMasterKey(registrationLockData.getMasterKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO response.isStorageCapable()
|
|
||||||
//accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID)));
|
|
||||||
|
|
||||||
account.setRegistered(true);
|
|
||||||
account.setUuid(UuidUtil.parseOrNull(response.getUuid()));
|
|
||||||
account.setRegistrationLockPin(pin);
|
|
||||||
account.getSignalProtocolStore()
|
|
||||||
.saveIdentity(account.getSelfAddress(),
|
|
||||||
getIdentityKeyPair().getPublicKey(),
|
|
||||||
TrustLevel.TRUSTED_VERIFIED);
|
|
||||||
|
|
||||||
refreshPreKeys();
|
|
||||||
account.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
private VerifyAccountResponse verifyAccountWithCode(
|
|
||||||
final String verificationCode, final String legacyPin, final String registrationLock
|
|
||||||
) throws IOException {
|
|
||||||
return accountManager.verifyAccountWithCode(verificationCode,
|
|
||||||
account.getSignalingKey(),
|
|
||||||
account.getSignalProtocolStore().getLocalRegistrationId(),
|
|
||||||
true,
|
|
||||||
legacyPin,
|
|
||||||
registrationLock,
|
|
||||||
unidentifiedAccessHelper.getSelfUnidentifiedAccessKey(),
|
|
||||||
unrestrictedUnidentifiedAccess,
|
|
||||||
capabilities,
|
|
||||||
discoverableByPhoneNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRegistrationLockPin(Optional<String> pin) throws IOException, UnauthenticatedResponseException {
|
public void setRegistrationLockPin(Optional<String> pin) throws IOException, UnauthenticatedResponseException {
|
||||||
if (pin.isPresent()) {
|
if (pin.isPresent()) {
|
||||||
final MasterKey masterKey = account.getPinMasterKey() != null
|
final MasterKey masterKey = account.getPinMasterKey() != null
|
||||||
|
@ -607,45 +483,21 @@ public class Manager implements Closeable {
|
||||||
accountManager.setPreKeys(identityKeyPair.getPublicKey(), signedPreKeyRecord, oneTimePreKeys);
|
accountManager.setPreKeys(identityKeyPair.getPublicKey(), signedPreKeyRecord, oneTimePreKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalServiceMessageReceiver createMessageReceiver() {
|
|
||||||
final ClientZkProfileOperations clientZkProfileOperations = capabilities.isGv2() ? ClientZkOperations.create(
|
|
||||||
serviceConfiguration).getProfileOperations() : null;
|
|
||||||
return new SignalServiceMessageReceiver(serviceConfiguration,
|
|
||||||
account.getUuid(),
|
|
||||||
account.getUsername(),
|
|
||||||
account.getPassword(),
|
|
||||||
account.getDeviceId(),
|
|
||||||
account.getSignalingKey(),
|
|
||||||
userAgent,
|
|
||||||
null,
|
|
||||||
timer,
|
|
||||||
clientZkProfileOperations);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SignalServiceMessageReceiver getOrCreateMessageReceiver() {
|
|
||||||
if (messageReceiver == null) {
|
|
||||||
messageReceiver = createMessageReceiver();
|
|
||||||
}
|
|
||||||
return messageReceiver;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SignalServiceMessagePipe getOrCreateMessagePipe() {
|
private SignalServiceMessagePipe getOrCreateMessagePipe() {
|
||||||
if (messagePipe == null) {
|
if (messagePipe == null) {
|
||||||
messagePipe = getOrCreateMessageReceiver().createMessagePipe();
|
messagePipe = messageReceiver.createMessagePipe();
|
||||||
}
|
}
|
||||||
return messagePipe;
|
return messagePipe;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalServiceMessagePipe getOrCreateUnidentifiedMessagePipe() {
|
private SignalServiceMessagePipe getOrCreateUnidentifiedMessagePipe() {
|
||||||
if (unidentifiedMessagePipe == null) {
|
if (unidentifiedMessagePipe == null) {
|
||||||
unidentifiedMessagePipe = getOrCreateMessageReceiver().createUnidentifiedMessagePipe();
|
unidentifiedMessagePipe = messageReceiver.createUnidentifiedMessagePipe();
|
||||||
}
|
}
|
||||||
return unidentifiedMessagePipe;
|
return unidentifiedMessagePipe;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalServiceMessageSender createMessageSender() {
|
private SignalServiceMessageSender createMessageSender() {
|
||||||
final ClientZkProfileOperations clientZkProfileOperations = capabilities.isGv2() ? ClientZkOperations.create(
|
|
||||||
serviceConfiguration).getProfileOperations() : null;
|
|
||||||
final ExecutorService executor = null;
|
final ExecutorService executor = null;
|
||||||
return new SignalServiceMessageSender(serviceConfiguration,
|
return new SignalServiceMessageSender(serviceConfiguration,
|
||||||
account.getUuid(),
|
account.getUuid(),
|
||||||
|
@ -2349,13 +2201,12 @@ public class Manager implements Closeable {
|
||||||
GroupId groupId, GroupSecretParams groupSecretParams, String cdnKey
|
GroupId groupId, GroupSecretParams groupSecretParams, String cdnKey
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
IOUtils.createPrivateDirectories(pathConfig.getAvatarsPath());
|
IOUtils.createPrivateDirectories(pathConfig.getAvatarsPath());
|
||||||
SignalServiceMessageReceiver receiver = getOrCreateMessageReceiver();
|
|
||||||
File outputFile = getGroupAvatarFile(groupId);
|
File outputFile = getGroupAvatarFile(groupId);
|
||||||
GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams);
|
GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams);
|
||||||
|
|
||||||
File tmpFile = IOUtils.createTempFile();
|
File tmpFile = IOUtils.createTempFile();
|
||||||
tmpFile.deleteOnExit();
|
tmpFile.deleteOnExit();
|
||||||
try (InputStream input = receiver.retrieveGroupsV2ProfileAvatar(cdnKey,
|
try (InputStream input = messageReceiver.retrieveGroupsV2ProfileAvatar(cdnKey,
|
||||||
tmpFile,
|
tmpFile,
|
||||||
ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) {
|
ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) {
|
||||||
byte[] encryptedData = IOUtils.readFully(input);
|
byte[] encryptedData = IOUtils.readFully(input);
|
||||||
|
@ -2384,11 +2235,10 @@ public class Manager implements Closeable {
|
||||||
SignalServiceAddress address, String avatarPath, ProfileKey profileKey
|
SignalServiceAddress address, String avatarPath, ProfileKey profileKey
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
IOUtils.createPrivateDirectories(pathConfig.getAvatarsPath());
|
IOUtils.createPrivateDirectories(pathConfig.getAvatarsPath());
|
||||||
SignalServiceMessageReceiver receiver = getOrCreateMessageReceiver();
|
|
||||||
File outputFile = getProfileAvatarFile(address);
|
File outputFile = getProfileAvatarFile(address);
|
||||||
|
|
||||||
File tmpFile = IOUtils.createTempFile();
|
File tmpFile = IOUtils.createTempFile();
|
||||||
try (InputStream input = receiver.retrieveProfileAvatar(avatarPath,
|
try (InputStream input = messageReceiver.retrieveProfileAvatar(avatarPath,
|
||||||
tmpFile,
|
tmpFile,
|
||||||
profileKey,
|
profileKey,
|
||||||
ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) {
|
ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) {
|
||||||
|
@ -2429,8 +2279,6 @@ public class Manager implements Closeable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final SignalServiceMessageReceiver messageReceiver = getOrCreateMessageReceiver();
|
|
||||||
|
|
||||||
File tmpFile = IOUtils.createTempFile();
|
File tmpFile = IOUtils.createTempFile();
|
||||||
try (InputStream input = messageReceiver.retrieveAttachment(pointer,
|
try (InputStream input = messageReceiver.retrieveAttachment(pointer,
|
||||||
tmpFile,
|
tmpFile,
|
||||||
|
@ -2451,7 +2299,6 @@ public class Manager implements Closeable {
|
||||||
private InputStream retrieveAttachmentAsStream(
|
private InputStream retrieveAttachmentAsStream(
|
||||||
SignalServiceAttachmentPointer pointer, File tmpFile
|
SignalServiceAttachmentPointer pointer, File tmpFile
|
||||||
) throws IOException, InvalidMessageException, MissingConfigurationException {
|
) throws IOException, InvalidMessageException, MissingConfigurationException {
|
||||||
final SignalServiceMessageReceiver messageReceiver = getOrCreateMessageReceiver();
|
|
||||||
return messageReceiver.retrieveAttachment(pointer, tmpFile, ServiceConfig.MAX_ATTACHMENT_SIZE);
|
return messageReceiver.retrieveAttachment(pointer, tmpFile, ServiceConfig.MAX_ATTACHMENT_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2737,6 +2584,10 @@ public class Manager implements Closeable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
|
close(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void close(boolean closeAccount) throws IOException {
|
||||||
if (messagePipe != null) {
|
if (messagePipe != null) {
|
||||||
messagePipe.shutdown();
|
messagePipe.shutdown();
|
||||||
messagePipe = null;
|
messagePipe = null;
|
||||||
|
@ -2747,7 +2598,10 @@ public class Manager implements Closeable {
|
||||||
unidentifiedMessagePipe = null;
|
unidentifiedMessagePipe = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
account.close();
|
if (closeAccount && account != null) {
|
||||||
|
account.close();
|
||||||
|
}
|
||||||
|
account = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ReceiveMessageHandler {
|
public interface ReceiveMessageHandler {
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.asamk.signal.manager;
|
||||||
|
|
||||||
|
public class NotRegisteredException extends Exception {
|
||||||
|
|
||||||
|
public NotRegisteredException() {
|
||||||
|
super("User is not registered.");
|
||||||
|
}
|
||||||
|
}
|
|
@ -124,8 +124,10 @@ public class ProvisioningManager {
|
||||||
m.requestSyncBlocked();
|
m.requestSyncBlocked();
|
||||||
m.requestSyncConfiguration();
|
m.requestSyncConfiguration();
|
||||||
|
|
||||||
m.saveAccount();
|
m.close(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
account.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
return username;
|
return username;
|
||||||
|
|
194
src/main/java/org/asamk/signal/manager/RegistrationManager.java
Normal file
194
src/main/java/org/asamk/signal/manager/RegistrationManager.java
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2015-2021 AsamK and contributors
|
||||||
|
|
||||||
|
This program 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.asamk.signal.manager;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.helper.PinHelper;
|
||||||
|
import org.asamk.signal.manager.storage.SignalAccount;
|
||||||
|
import org.asamk.signal.manager.util.KeyUtils;
|
||||||
|
import org.signal.zkgroup.profiles.ProfileKey;
|
||||||
|
import org.whispersystems.libsignal.IdentityKeyPair;
|
||||||
|
import org.whispersystems.libsignal.util.KeyHelper;
|
||||||
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
import org.whispersystems.signalservice.api.KbsPinData;
|
||||||
|
import org.whispersystems.signalservice.api.KeyBackupService;
|
||||||
|
import org.whispersystems.signalservice.api.KeyBackupServicePinException;
|
||||||
|
import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException;
|
||||||
|
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||||
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
import org.whispersystems.signalservice.api.util.SleepTimer;
|
||||||
|
import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
|
||||||
|
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||||
|
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
|
||||||
|
import org.whispersystems.signalservice.internal.push.LockedException;
|
||||||
|
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse;
|
||||||
|
import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class RegistrationManager implements AutoCloseable {
|
||||||
|
|
||||||
|
private SignalAccount account;
|
||||||
|
private final PathConfig pathConfig;
|
||||||
|
private final SignalServiceConfiguration serviceConfiguration;
|
||||||
|
private final String userAgent;
|
||||||
|
|
||||||
|
private final SignalServiceAccountManager accountManager;
|
||||||
|
private final PinHelper pinHelper;
|
||||||
|
|
||||||
|
public RegistrationManager(
|
||||||
|
SignalAccount account,
|
||||||
|
PathConfig pathConfig,
|
||||||
|
SignalServiceConfiguration serviceConfiguration,
|
||||||
|
String userAgent
|
||||||
|
) {
|
||||||
|
this.account = account;
|
||||||
|
this.pathConfig = pathConfig;
|
||||||
|
this.serviceConfiguration = serviceConfiguration;
|
||||||
|
this.userAgent = userAgent;
|
||||||
|
|
||||||
|
final SleepTimer timer = new UptimeSleepTimer();
|
||||||
|
this.accountManager = new SignalServiceAccountManager(serviceConfiguration, new DynamicCredentialsProvider(
|
||||||
|
// Using empty UUID, because registering doesn't work otherwise
|
||||||
|
null,
|
||||||
|
account.getUsername(),
|
||||||
|
account.getPassword(),
|
||||||
|
account.getSignalingKey(),
|
||||||
|
SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, null, timer);
|
||||||
|
final KeyBackupService keyBackupService = ServiceConfig.createKeyBackupService(accountManager);
|
||||||
|
this.pinHelper = new PinHelper(keyBackupService);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RegistrationManager init(
|
||||||
|
String username, File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent
|
||||||
|
) throws IOException {
|
||||||
|
PathConfig pathConfig = PathConfig.createDefault(settingsPath);
|
||||||
|
|
||||||
|
if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) {
|
||||||
|
IdentityKeyPair identityKey = KeyUtils.generateIdentityKeyPair();
|
||||||
|
int registrationId = KeyHelper.generateRegistrationId(false);
|
||||||
|
|
||||||
|
ProfileKey profileKey = KeyUtils.createProfileKey();
|
||||||
|
SignalAccount account = SignalAccount.create(pathConfig.getDataPath(),
|
||||||
|
username,
|
||||||
|
identityKey,
|
||||||
|
registrationId,
|
||||||
|
profileKey);
|
||||||
|
account.save();
|
||||||
|
|
||||||
|
return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalAccount account = SignalAccount.load(pathConfig.getDataPath(), username);
|
||||||
|
|
||||||
|
return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register(boolean voiceVerification, String captcha) throws IOException {
|
||||||
|
if (account.getPassword() == null) {
|
||||||
|
account.setPassword(KeyUtils.createPassword());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (voiceVerification) {
|
||||||
|
accountManager.requestVoiceVerificationCode(Locale.getDefault(),
|
||||||
|
Optional.fromNullable(captcha),
|
||||||
|
Optional.absent());
|
||||||
|
} else {
|
||||||
|
accountManager.requestSmsVerificationCode(false, Optional.fromNullable(captcha), Optional.absent());
|
||||||
|
}
|
||||||
|
|
||||||
|
account.setRegistered(false);
|
||||||
|
account.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void verifyAccount(
|
||||||
|
String verificationCode, String pin
|
||||||
|
) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException {
|
||||||
|
verificationCode = verificationCode.replace("-", "");
|
||||||
|
if (account.getSignalingKey() == null) {
|
||||||
|
account.setSignalingKey(KeyUtils.createSignalingKey());
|
||||||
|
}
|
||||||
|
VerifyAccountResponse response;
|
||||||
|
try {
|
||||||
|
response = verifyAccountWithCode(verificationCode, pin, null);
|
||||||
|
account.setPinMasterKey(null);
|
||||||
|
} catch (LockedException e) {
|
||||||
|
if (pin == null) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
KbsPinData registrationLockData = pinHelper.getRegistrationLockData(pin, e);
|
||||||
|
if (registrationLockData == null) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
String registrationLock = registrationLockData.getMasterKey().deriveRegistrationLock();
|
||||||
|
try {
|
||||||
|
response = verifyAccountWithCode(verificationCode, null, registrationLock);
|
||||||
|
} catch (LockedException _e) {
|
||||||
|
throw new AssertionError("KBS Pin appeared to matched but reg lock still failed!");
|
||||||
|
}
|
||||||
|
account.setPinMasterKey(registrationLockData.getMasterKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO response.isStorageCapable()
|
||||||
|
//accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID)));
|
||||||
|
|
||||||
|
account.setDeviceId(SignalServiceAddress.DEFAULT_DEVICE_ID);
|
||||||
|
account.setMultiDevice(false);
|
||||||
|
account.setRegistered(true);
|
||||||
|
account.setUuid(UuidUtil.parseOrNull(response.getUuid()));
|
||||||
|
account.setRegistrationLockPin(pin);
|
||||||
|
account.getSignalProtocolStore()
|
||||||
|
.saveIdentity(account.getSelfAddress(),
|
||||||
|
account.getSignalProtocolStore().getIdentityKeyPair().getPublicKey(),
|
||||||
|
TrustLevel.TRUSTED_VERIFIED);
|
||||||
|
|
||||||
|
try (Manager m = new Manager(account, pathConfig, serviceConfiguration, userAgent)) {
|
||||||
|
|
||||||
|
m.refreshPreKeys();
|
||||||
|
|
||||||
|
m.close(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
account.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private VerifyAccountResponse verifyAccountWithCode(
|
||||||
|
final String verificationCode, final String legacyPin, final String registrationLock
|
||||||
|
) throws IOException {
|
||||||
|
return accountManager.verifyAccountWithCode(verificationCode,
|
||||||
|
account.getSignalingKey(),
|
||||||
|
account.getSignalProtocolStore().getLocalRegistrationId(),
|
||||||
|
true,
|
||||||
|
legacyPin,
|
||||||
|
registrationLock,
|
||||||
|
account.getSelfUnidentifiedAccessKey(),
|
||||||
|
account.isUnrestrictedUnidentifiedAccess(),
|
||||||
|
ServiceConfig.capabilities,
|
||||||
|
account.isDiscoverableByPhoneNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws Exception {
|
||||||
|
if (account != null) {
|
||||||
|
account.close();
|
||||||
|
account = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,8 @@ import org.whispersystems.libsignal.InvalidKeyException;
|
||||||
import org.whispersystems.libsignal.ecc.Curve;
|
import org.whispersystems.libsignal.ecc.Curve;
|
||||||
import org.whispersystems.libsignal.ecc.ECPublicKey;
|
import org.whispersystems.libsignal.ecc.ECPublicKey;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
import org.whispersystems.signalservice.api.KeyBackupService;
|
||||||
|
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||||
import org.whispersystems.signalservice.api.account.AccountAttributes;
|
import org.whispersystems.signalservice.api.account.AccountAttributes;
|
||||||
import org.whispersystems.signalservice.api.push.TrustStore;
|
import org.whispersystems.signalservice.api.push.TrustStore;
|
||||||
import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl;
|
import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl;
|
||||||
|
@ -109,6 +111,16 @@ public class ServiceConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static KeyBackupService createKeyBackupService(SignalServiceAccountManager accountManager) {
|
||||||
|
KeyStore keyStore = ServiceConfig.getIasKeyStore();
|
||||||
|
|
||||||
|
return accountManager.getKeyBackupService(keyStore,
|
||||||
|
ServiceConfig.KEY_BACKUP_ENCLAVE_NAME,
|
||||||
|
ServiceConfig.KEY_BACKUP_SERVICE_ID,
|
||||||
|
ServiceConfig.KEY_BACKUP_MRENCLAVE,
|
||||||
|
10);
|
||||||
|
}
|
||||||
|
|
||||||
static ECPublicKey getUnidentifiedSenderTrustRoot() {
|
static ECPublicKey getUnidentifiedSenderTrustRoot() {
|
||||||
try {
|
try {
|
||||||
return Curve.decodePoint(UNIDENTIFIED_SENDER_TRUST_ROOT, 0);
|
return Curve.decodePoint(UNIDENTIFIED_SENDER_TRUST_ROOT, 0);
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class UnidentifiedAccessHelper {
|
||||||
this.senderCertificateProvider = senderCertificateProvider;
|
this.senderCertificateProvider = senderCertificateProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getSelfUnidentifiedAccessKey() {
|
private byte[] getSelfUnidentifiedAccessKey() {
|
||||||
return UnidentifiedAccess.deriveAccessKeyFrom(selfProfileKeyProvider.getProfileKey());
|
return UnidentifiedAccess.deriveAccessKeyFrom(selfProfileKeyProvider.getProfileKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.asamk.signal.manager.storage.stickers.StickerStore;
|
||||||
import org.asamk.signal.manager.storage.threads.LegacyJsonThreadStore;
|
import org.asamk.signal.manager.storage.threads.LegacyJsonThreadStore;
|
||||||
import org.asamk.signal.manager.storage.threads.ThreadInfo;
|
import org.asamk.signal.manager.storage.threads.ThreadInfo;
|
||||||
import org.asamk.signal.manager.util.IOUtils;
|
import org.asamk.signal.manager.util.IOUtils;
|
||||||
|
import org.asamk.signal.manager.util.KeyUtils;
|
||||||
import org.asamk.signal.manager.util.Utils;
|
import org.asamk.signal.manager.util.Utils;
|
||||||
import org.signal.zkgroup.InvalidInputException;
|
import org.signal.zkgroup.InvalidInputException;
|
||||||
import org.signal.zkgroup.profiles.ProfileKey;
|
import org.signal.zkgroup.profiles.ProfileKey;
|
||||||
|
@ -36,6 +37,7 @@ import org.whispersystems.libsignal.state.PreKeyRecord;
|
||||||
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
||||||
import org.whispersystems.libsignal.util.Medium;
|
import org.whispersystems.libsignal.util.Medium;
|
||||||
import org.whispersystems.libsignal.util.Pair;
|
import org.whispersystems.libsignal.util.Pair;
|
||||||
|
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
||||||
import org.whispersystems.signalservice.api.kbs.MasterKey;
|
import org.whispersystems.signalservice.api.kbs.MasterKey;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.util.Base64;
|
import org.whispersystems.util.Base64;
|
||||||
|
@ -98,6 +100,8 @@ public class SignalAccount implements Closeable {
|
||||||
try {
|
try {
|
||||||
SignalAccount account = new SignalAccount(pair.first(), pair.second());
|
SignalAccount account = new SignalAccount(pair.first(), pair.second());
|
||||||
account.load(dataPath);
|
account.load(dataPath);
|
||||||
|
account.migrateLegacyConfigs();
|
||||||
|
|
||||||
return account;
|
return account;
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
pair.second().close();
|
pair.second().close();
|
||||||
|
@ -169,6 +173,31 @@ public class SignalAccount implements Closeable {
|
||||||
return account;
|
return account;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void migrateLegacyConfigs() {
|
||||||
|
if (getProfileKey() == null && isRegistered()) {
|
||||||
|
// Old config file, creating new profile key
|
||||||
|
setProfileKey(KeyUtils.createProfileKey());
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
// Store profile keys only in profile store
|
||||||
|
for (ContactInfo contact : getContactStore().getContacts()) {
|
||||||
|
String profileKeyString = contact.profileKey;
|
||||||
|
if (profileKeyString == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final ProfileKey profileKey;
|
||||||
|
try {
|
||||||
|
profileKey = new ProfileKey(Base64.decode(profileKeyString));
|
||||||
|
} catch (InvalidInputException | IOException e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
contact.profileKey = null;
|
||||||
|
getProfileStore().storeProfileKey(contact.getAddress(), profileKey);
|
||||||
|
}
|
||||||
|
// Ensure our profile key is stored in profile store
|
||||||
|
getProfileStore().storeProfileKey(getSelfAddress(), getProfileKey());
|
||||||
|
}
|
||||||
|
|
||||||
public static File getFileName(File dataPath, String username) {
|
public static File getFileName(File dataPath, String username) {
|
||||||
return new File(dataPath, username);
|
return new File(dataPath, username);
|
||||||
}
|
}
|
||||||
|
@ -451,6 +480,10 @@ public class SignalAccount implements Closeable {
|
||||||
return deviceId;
|
return deviceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDeviceId(final int deviceId) {
|
||||||
|
this.deviceId = deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
public String getPassword() {
|
public String getPassword() {
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
@ -491,6 +524,10 @@ public class SignalAccount implements Closeable {
|
||||||
this.profileKey = profileKey;
|
this.profileKey = profileKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] getSelfUnidentifiedAccessKey() {
|
||||||
|
return UnidentifiedAccess.deriveAccessKeyFrom(getProfileKey());
|
||||||
|
}
|
||||||
|
|
||||||
public int getPreKeyIdOffset() {
|
public int getPreKeyIdOffset() {
|
||||||
return preKeyIdOffset;
|
return preKeyIdOffset;
|
||||||
}
|
}
|
||||||
|
@ -515,8 +552,19 @@ public class SignalAccount implements Closeable {
|
||||||
isMultiDevice = multiDevice;
|
isMultiDevice = multiDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isUnrestrictedUnidentifiedAccess() {
|
||||||
|
// TODO make configurable
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDiscoverableByPhoneNumber() {
|
||||||
|
// TODO make configurable
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
|
save();
|
||||||
synchronized (fileChannel) {
|
synchronized (fileChannel) {
|
||||||
try {
|
try {
|
||||||
lock.close();
|
lock.close();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue