From: AsamK Date: Sun, 10 Jan 2021 16:07:06 +0000 (+0100) Subject: Refactor register and verify X-Git-Tag: v0.7.3~43 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/e74be0c345321888c1fbfa05616cb90cf3f07ffb Refactor register and verify --- diff --git a/src/main/java/org/asamk/signal/Main.java b/src/main/java/org/asamk/signal/Main.java index 6204778d..b9209631 100644 --- a/src/main/java/org/asamk/signal/Main.java +++ b/src/main/java/org/asamk/signal/Main.java @@ -32,9 +32,12 @@ import org.asamk.signal.commands.DbusCommand; import org.asamk.signal.commands.ExtendedDbusCommand; import org.asamk.signal.commands.LocalCommand; import org.asamk.signal.commands.ProvisioningCommand; +import org.asamk.signal.commands.RegistrationCommand; import org.asamk.signal.dbus.DbusSignalImpl; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.NotRegisteredException; import org.asamk.signal.manager.ProvisioningManager; +import org.asamk.signal.manager.RegistrationManager; import org.asamk.signal.manager.ServiceConfig; import org.asamk.signal.util.IOUtils; 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.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; @@ -75,8 +77,14 @@ public class Main { } 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")) { - return initDbusClient(ns, ns.getBoolean("dbus_system")); + return initDbusClient(command, ns, ns.getBoolean("dbus_system")); } final String username = ns.getString("username"); @@ -99,12 +107,31 @@ public class Main { if (username == null) { 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; try { manager = Manager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT); + } catch (NotRegisteredException e) { + System.err.println("User is not registered."); + return 1; } catch (Throwable e) { logger.error("Error loading state file: {}", e.getMessage()); return 2; @@ -113,25 +140,19 @@ public class Main { try (Manager m = manager) { try { 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) { logger.error("Error while checking account: {}", e.getMessage()); return 2; } - return handleCommands(ns, m); + return handleCommand(command, ns, m); } catch (IOException e) { logger.error("Cleanup failed", e); 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 { DBusConnection.DBusBusType busType; if (systemBus) { @@ -144,7 +165,7 @@ public class Main { DbusConfig.SIGNAL_OBJECTPATH, Signal.class); - return handleCommands(ns, ts, dBusConn); + return handleCommand(command, ns, ts, dBusConn); } } catch (DBusException | IOException 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"); final Map commands = Commands.getCommands(); - if (commands.containsKey(commandKey)) { - Command command = commands.get(commandKey); + if (!commands.containsKey(commandKey)) { + return null; + } + return commands.get(commandKey); + } - 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; - } + private static int handleCommand(Command command, Namespace ns, Signal ts, DBusConnection dBusConn) { + 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("Command is not yet implemented via dbus"); + return 1; } - return 0; } - private static int handleCommands(Namespace ns, ProvisioningManager pm) { - String commandKey = ns.getString("command"); - final Map commands = Commands.getCommands(); - if (commands.containsKey(commandKey)) { - Command command = commands.get(commandKey); + private static int handleCommand(Command command, Namespace ns, ProvisioningManager pm) { + if (command instanceof ProvisioningCommand) { + return ((ProvisioningCommand) command).handleCommand(ns, pm); + } else { + System.err.println("Command only works with a username"); + return 1; + } + } - if (command instanceof ProvisioningCommand) { - return ((ProvisioningCommand) command).handleCommand(ns, pm); - } else { - System.err.println(commandKey + " only works with a username"); - return 1; - } + private static int handleCommand(Command command, Namespace ns, RegistrationManager m) { + if (command instanceof RegistrationCommand) { + return ((RegistrationCommand) command).handleCommand(ns, m); } - return 0; + return 1; } - private static int handleCommands(Namespace ns, Manager m) { - String commandKey = ns.getString("command"); - final Map commands = Commands.getCommands(); - if (commands.containsKey(commandKey)) { - Command command = commands.get(commandKey); - - if (command instanceof LocalCommand) { - return ((LocalCommand) command).handleCommand(ns, m); - } else if (command instanceof DbusCommand) { - 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 0; } /** diff --git a/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java b/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java index dab886d7..c5d18ab1 100644 --- a/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java +++ b/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java @@ -23,10 +23,6 @@ public class AddDeviceCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } try { m.addDeviceLink(new URI(ns.getString("uri"))); return 0; diff --git a/src/main/java/org/asamk/signal/commands/BlockCommand.java b/src/main/java/org/asamk/signal/commands/BlockCommand.java index 627be5a9..60009cfb 100644 --- a/src/main/java/org/asamk/signal/commands/BlockCommand.java +++ b/src/main/java/org/asamk/signal/commands/BlockCommand.java @@ -21,11 +21,6 @@ public class BlockCommand implements LocalCommand { @Override 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.getList("contact")) { try { m.setContactBlocked(contact_number, true); diff --git a/src/main/java/org/asamk/signal/commands/DaemonCommand.java b/src/main/java/org/asamk/signal/commands/DaemonCommand.java index 3caaaa37..c5ee2edc 100644 --- a/src/main/java/org/asamk/signal/commands/DaemonCommand.java +++ b/src/main/java/org/asamk/signal/commands/DaemonCommand.java @@ -35,10 +35,6 @@ public class DaemonCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } DBusConnection conn = null; try { try { diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java index 882f4a5c..0a1ddc4c 100644 --- a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -28,11 +28,6 @@ public class GetUserStatusCommand implements LocalCommand { @Override 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 ObjectMapper jsonProcessor = new ObjectMapper(); jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); diff --git a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java index 305bf55b..c5975b0c 100644 --- a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java @@ -29,11 +29,6 @@ public class JoinGroupCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } - final GroupInviteLinkUrl linkUrl; String uri = ns.getString("uri"); try { diff --git a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java index 2c98ec6b..1a14e8df 100644 --- a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java @@ -16,10 +16,6 @@ public class ListContactsCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } List contacts = m.getContacts(); for (ContactInfo c : contacts) { System.out.println(String.format("Number: %s Name: %s Blocked: %b", c.number, c.name, c.blocked)); diff --git a/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java b/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java index 4b9dac5c..a03b078f 100644 --- a/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java @@ -18,10 +18,6 @@ public class ListDevicesCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } try { List devices = m.getLinkedDevices(); for (DeviceInfo d : devices) { diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index b4be4ad0..97af502e 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -64,11 +64,6 @@ public class ListGroupsCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } - List groups = m.getGroups(); boolean detailed = ns.getBoolean("detailed"); diff --git a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java index 3f422cbd..4caeca29 100644 --- a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java @@ -30,10 +30,6 @@ public class ListIdentitiesCommand implements LocalCommand { @Override 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) { for (IdentityInfo identity : m.getIdentities()) { printIdentityFingerprint(m, identity); diff --git a/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java b/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java index c1f6bfbf..f258ae24 100644 --- a/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java @@ -31,11 +31,6 @@ public class QuitGroupCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } - try { final GroupId groupId = Util.decodeGroupId(ns.getString("group")); final Pair> results = m.sendQuitGroupMessage(groupId); diff --git a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java index 84a918fb..7dc9dcaf 100644 --- a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java +++ b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java @@ -146,10 +146,6 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } double timeout = 5; if (ns.getDouble("timeout") != null) { timeout = ns.getDouble("timeout"); diff --git a/src/main/java/org/asamk/signal/commands/RegisterCommand.java b/src/main/java/org/asamk/signal/commands/RegisterCommand.java index f69e0844..da652f7d 100644 --- a/src/main/java/org/asamk/signal/commands/RegisterCommand.java +++ b/src/main/java/org/asamk/signal/commands/RegisterCommand.java @@ -4,12 +4,12 @@ import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; 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 java.io.IOException; -public class RegisterCommand implements LocalCommand { +public class RegisterCommand implements RegistrationCommand { @Override public void attachToSubparser(final Subparser subparser) { @@ -21,7 +21,7 @@ public class RegisterCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public int handleCommand(final Namespace ns, final RegistrationManager m) { try { final boolean voiceVerification = ns.getBoolean("voice"); final String captcha = ns.getString("captcha"); diff --git a/src/main/java/org/asamk/signal/commands/RegistrationCommand.java b/src/main/java/org/asamk/signal/commands/RegistrationCommand.java new file mode 100644 index 00000000..8683570f --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/RegistrationCommand.java @@ -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); +} diff --git a/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java b/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java index 1e2343e7..78d14bbd 100644 --- a/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java +++ b/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java @@ -19,10 +19,6 @@ public class RemoveDeviceCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } try { int deviceId = ns.getInt("deviceId"); m.removeLinkedDevices(deviceId); diff --git a/src/main/java/org/asamk/signal/commands/RemovePinCommand.java b/src/main/java/org/asamk/signal/commands/RemovePinCommand.java index 95531249..ada9c446 100644 --- a/src/main/java/org/asamk/signal/commands/RemovePinCommand.java +++ b/src/main/java/org/asamk/signal/commands/RemovePinCommand.java @@ -17,10 +17,6 @@ public class RemovePinCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } try { m.setRegistrationLockPin(Optional.absent()); return 0; diff --git a/src/main/java/org/asamk/signal/commands/SendContactsCommand.java b/src/main/java/org/asamk/signal/commands/SendContactsCommand.java index 20e81a60..aaca283a 100644 --- a/src/main/java/org/asamk/signal/commands/SendContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendContactsCommand.java @@ -17,10 +17,6 @@ public class SendContactsCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } try { m.sendContacts(); return 0; diff --git a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java index c680bfd7..2a9afa74 100644 --- a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java @@ -47,11 +47,6 @@ public class SendReactionCommand implements LocalCommand { @Override 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) { System.err.println("No recipients given"); System.err.println("Aborting sending."); diff --git a/src/main/java/org/asamk/signal/commands/SetPinCommand.java b/src/main/java/org/asamk/signal/commands/SetPinCommand.java index c68ea3a9..ac601b3b 100644 --- a/src/main/java/org/asamk/signal/commands/SetPinCommand.java +++ b/src/main/java/org/asamk/signal/commands/SetPinCommand.java @@ -19,10 +19,6 @@ public class SetPinCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } try { String registrationLockPin = ns.getString("registrationLockPin"); m.setRegistrationLockPin(Optional.of(registrationLockPin)); diff --git a/src/main/java/org/asamk/signal/commands/TrustCommand.java b/src/main/java/org/asamk/signal/commands/TrustCommand.java index 076a86db..58c7371f 100644 --- a/src/main/java/org/asamk/signal/commands/TrustCommand.java +++ b/src/main/java/org/asamk/signal/commands/TrustCommand.java @@ -27,10 +27,6 @@ public class TrustCommand implements LocalCommand { @Override 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"); if (ns.getBoolean("trust_all_known_keys")) { boolean res = m.trustIdentityAllKeys(number); diff --git a/src/main/java/org/asamk/signal/commands/UnblockCommand.java b/src/main/java/org/asamk/signal/commands/UnblockCommand.java index 14ea2996..d191ef22 100644 --- a/src/main/java/org/asamk/signal/commands/UnblockCommand.java +++ b/src/main/java/org/asamk/signal/commands/UnblockCommand.java @@ -21,11 +21,6 @@ public class UnblockCommand implements LocalCommand { @Override 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.getList("contact")) { try { m.setContactBlocked(contact_number, false); diff --git a/src/main/java/org/asamk/signal/commands/UnregisterCommand.java b/src/main/java/org/asamk/signal/commands/UnregisterCommand.java index 7a7616bd..079070e6 100644 --- a/src/main/java/org/asamk/signal/commands/UnregisterCommand.java +++ b/src/main/java/org/asamk/signal/commands/UnregisterCommand.java @@ -16,10 +16,6 @@ public class UnregisterCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } try { m.unregister(); return 0; diff --git a/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java b/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java index 79459fe6..8211e190 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java @@ -16,10 +16,6 @@ public class UpdateAccountCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } try { m.updateAccountAttributes(); return 0; diff --git a/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java b/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java index da090209..c4da94a2 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java @@ -23,11 +23,6 @@ public class UpdateContactCommand implements LocalCommand { @Override 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 name = ns.getString("name"); diff --git a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java index 1e332fb4..968a8733 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java @@ -25,11 +25,6 @@ public class UpdateProfileCommand implements LocalCommand { @Override 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 avatarPath = ns.getString("avatar"); boolean removeAvatar = ns.getBoolean("remove_avatar"); diff --git a/src/main/java/org/asamk/signal/commands/VerifyCommand.java b/src/main/java/org/asamk/signal/commands/VerifyCommand.java index d4b0a7cb..7fa10b6a 100644 --- a/src/main/java/org/asamk/signal/commands/VerifyCommand.java +++ b/src/main/java/org/asamk/signal/commands/VerifyCommand.java @@ -3,14 +3,14 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; 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.KeyBackupSystemNoDataException; import org.whispersystems.signalservice.internal.push.LockedException; import java.io.IOException; -public class VerifyCommand implements LocalCommand { +public class VerifyCommand implements RegistrationCommand { @Override public void attachToSubparser(final Subparser subparser) { @@ -19,11 +19,7 @@ public class VerifyCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { - if (m.isRegistered()) { - System.err.println("User registration is already verified"); - return 1; - } + public int handleCommand(final Namespace ns, final RegistrationManager m) { try { String verificationCode = ns.getString("verificationCode"); String pin = ns.getString("pin"); diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 576dbb5a..9d090a58 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -79,14 +79,10 @@ import org.whispersystems.libsignal.ecc.ECKeyPair; import org.whispersystems.libsignal.ecc.ECPublicKey; import org.whispersystems.libsignal.state.PreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyRecord; -import org.whispersystems.libsignal.util.KeyHelper; import org.whispersystems.libsignal.util.Medium; import org.whispersystems.libsignal.util.Pair; 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.SignalServiceMessagePipe; 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.UnauthenticatedQuoteException; 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.UnsupportedDataMessageException; -import org.whispersystems.signalservice.internal.push.VerifyAccountResponse; import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; import org.whispersystems.signalservice.internal.util.Hex; import org.whispersystems.util.Base64; @@ -167,7 +161,6 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; -import java.security.KeyStore; import java.security.SignatureException; import java.util.ArrayList; import java.util.Arrays; @@ -176,7 +169,6 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -202,24 +194,21 @@ public class Manager implements Closeable { private final SignalServiceConfiguration serviceConfiguration; private final String userAgent; - // TODO make configurable - private final boolean discoverableByPhoneNumber = true; - private final boolean unrestrictedUnidentifiedAccess = false; - - private final SignalAccount account; + private SignalAccount account; private final PathConfig pathConfig; - private SignalServiceAccountManager accountManager; - private GroupsV2Api groupsV2Api; + private final SignalServiceAccountManager accountManager; + private final GroupsV2Api groupsV2Api; private final GroupsV2Operations groupsV2Operations; + private final SignalServiceMessageReceiver messageReceiver; + private final ClientZkProfileOperations clientZkProfileOperations; - private SignalServiceMessageReceiver messageReceiver = null; private SignalServiceMessagePipe messagePipe = null; private SignalServiceMessagePipe unidentifiedMessagePipe = null; private final UnidentifiedAccessHelper unidentifiedAccessHelper; private final ProfileHelper profileHelper; private final GroupHelper groupHelper; - private PinHelper pinHelper; + private final PinHelper pinHelper; Manager( SignalAccount account, @@ -233,7 +222,30 @@ public class Manager implements Closeable { this.userAgent = userAgent; this.groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create( 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); @@ -244,7 +256,7 @@ public class Manager implements Closeable { this.profileHelper = new ProfileHelper(account.getProfileStore()::getProfileKey, unidentifiedAccessHelper::getAccessFor, unidentified -> unidentified ? getOrCreateUnidentifiedMessagePipe() : getOrCreateMessagePipe(), - this::getOrCreateMessageReceiver); + () -> messageReceiver); this.groupHelper = new GroupHelper(this::getRecipientProfileKeyCredential, this::getRecipientProfile, account::getSelfAddress, @@ -261,30 +273,6 @@ public class Manager implements Closeable { 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() { return account.getSignalProtocolStore().getIdentityKeyPair(); } @@ -313,56 +301,20 @@ public class Manager implements Closeable { public static Manager init( String username, File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent - ) throws IOException { + ) throws IOException, NotRegisteredException { 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 Manager(account, pathConfig, serviceConfiguration, userAgent); + throw new NotRegisteredException(); } SignalAccount account = SignalAccount.load(pathConfig.getDataPath(), username); - Manager m = new Manager(account, pathConfig, serviceConfiguration, userAgent); - - 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()) { - 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); + if (!account.isRegistered()) { + throw new NotRegisteredException(); } - // Ensure our profile key is stored in profile store - account.getProfileStore().storeProfileKey(getSelfAddress(), account.getProfileKey()); + + return new Manager(account, pathConfig, serviceConfiguration, userAgent); } public void checkAccountState() throws IOException { @@ -401,25 +353,6 @@ public class Manager implements Closeable { 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 { accountManager.setAccountAttributes(account.getSignalingKey(), account.getSignalProtocolStore().getLocalRegistrationId(), @@ -427,10 +360,10 @@ public class Manager implements Closeable { // set legacy pin only if no KBS master key is set account.getPinMasterKey() == null ? account.getRegistrationLockPin() : null, account.getPinMasterKey() == null ? null : account.getPinMasterKey().deriveRegistrationLock(), - unidentifiedAccessHelper.getSelfUnidentifiedAccessKey(), - unrestrictedUnidentifiedAccess, + account.getSelfUnidentifiedAccessKey(), + account.isUnrestrictedUnidentifiedAccess(), capabilities, - discoverableByPhoneNumber); + account.isDiscoverableByPhoneNumber()); } 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 pin) throws IOException, UnauthenticatedResponseException { if (pin.isPresent()) { final MasterKey masterKey = account.getPinMasterKey() != null @@ -607,45 +483,21 @@ public class Manager implements Closeable { 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() { if (messagePipe == null) { - messagePipe = getOrCreateMessageReceiver().createMessagePipe(); + messagePipe = messageReceiver.createMessagePipe(); } return messagePipe; } private SignalServiceMessagePipe getOrCreateUnidentifiedMessagePipe() { if (unidentifiedMessagePipe == null) { - unidentifiedMessagePipe = getOrCreateMessageReceiver().createUnidentifiedMessagePipe(); + unidentifiedMessagePipe = messageReceiver.createUnidentifiedMessagePipe(); } return unidentifiedMessagePipe; } private SignalServiceMessageSender createMessageSender() { - final ClientZkProfileOperations clientZkProfileOperations = capabilities.isGv2() ? ClientZkOperations.create( - serviceConfiguration).getProfileOperations() : null; final ExecutorService executor = null; return new SignalServiceMessageSender(serviceConfiguration, account.getUuid(), @@ -2349,13 +2201,12 @@ public class Manager implements Closeable { GroupId groupId, GroupSecretParams groupSecretParams, String cdnKey ) throws IOException { IOUtils.createPrivateDirectories(pathConfig.getAvatarsPath()); - SignalServiceMessageReceiver receiver = getOrCreateMessageReceiver(); File outputFile = getGroupAvatarFile(groupId); GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams); File tmpFile = IOUtils.createTempFile(); tmpFile.deleteOnExit(); - try (InputStream input = receiver.retrieveGroupsV2ProfileAvatar(cdnKey, + try (InputStream input = messageReceiver.retrieveGroupsV2ProfileAvatar(cdnKey, tmpFile, ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) { byte[] encryptedData = IOUtils.readFully(input); @@ -2384,11 +2235,10 @@ public class Manager implements Closeable { SignalServiceAddress address, String avatarPath, ProfileKey profileKey ) throws IOException { IOUtils.createPrivateDirectories(pathConfig.getAvatarsPath()); - SignalServiceMessageReceiver receiver = getOrCreateMessageReceiver(); File outputFile = getProfileAvatarFile(address); File tmpFile = IOUtils.createTempFile(); - try (InputStream input = receiver.retrieveProfileAvatar(avatarPath, + try (InputStream input = messageReceiver.retrieveProfileAvatar(avatarPath, tmpFile, profileKey, ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) { @@ -2429,8 +2279,6 @@ public class Manager implements Closeable { } } - final SignalServiceMessageReceiver messageReceiver = getOrCreateMessageReceiver(); - File tmpFile = IOUtils.createTempFile(); try (InputStream input = messageReceiver.retrieveAttachment(pointer, tmpFile, @@ -2451,7 +2299,6 @@ public class Manager implements Closeable { private InputStream retrieveAttachmentAsStream( SignalServiceAttachmentPointer pointer, File tmpFile ) throws IOException, InvalidMessageException, MissingConfigurationException { - final SignalServiceMessageReceiver messageReceiver = getOrCreateMessageReceiver(); return messageReceiver.retrieveAttachment(pointer, tmpFile, ServiceConfig.MAX_ATTACHMENT_SIZE); } @@ -2737,6 +2584,10 @@ public class Manager implements Closeable { @Override public void close() throws IOException { + close(true); + } + + void close(boolean closeAccount) throws IOException { if (messagePipe != null) { messagePipe.shutdown(); messagePipe = null; @@ -2747,7 +2598,10 @@ public class Manager implements Closeable { unidentifiedMessagePipe = null; } - account.close(); + if (closeAccount && account != null) { + account.close(); + } + account = null; } public interface ReceiveMessageHandler { diff --git a/src/main/java/org/asamk/signal/manager/NotRegisteredException.java b/src/main/java/org/asamk/signal/manager/NotRegisteredException.java new file mode 100644 index 00000000..c1b35a1c --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/NotRegisteredException.java @@ -0,0 +1,8 @@ +package org.asamk.signal.manager; + +public class NotRegisteredException extends Exception { + + public NotRegisteredException() { + super("User is not registered."); + } +} diff --git a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index 4195e8af..475c90b8 100644 --- a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -124,8 +124,10 @@ public class ProvisioningManager { m.requestSyncBlocked(); m.requestSyncConfiguration(); - m.saveAccount(); + m.close(false); } + + account.save(); } return username; diff --git a/src/main/java/org/asamk/signal/manager/RegistrationManager.java b/src/main/java/org/asamk/signal/manager/RegistrationManager.java new file mode 100644 index 00000000..e740bb91 --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/RegistrationManager.java @@ -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 . + */ +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; + } + } +} diff --git a/src/main/java/org/asamk/signal/manager/ServiceConfig.java b/src/main/java/org/asamk/signal/manager/ServiceConfig.java index 55935ced..b6d4f4fd 100644 --- a/src/main/java/org/asamk/signal/manager/ServiceConfig.java +++ b/src/main/java/org/asamk/signal/manager/ServiceConfig.java @@ -6,6 +6,8 @@ import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.ecc.Curve; import org.whispersystems.libsignal.ecc.ECPublicKey; 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.push.TrustStore; 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() { try { return Curve.decodePoint(UNIDENTIFIED_SENDER_TRUST_ROOT, 0); diff --git a/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java b/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java index a994c40a..3930154c 100644 --- a/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java +++ b/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java @@ -36,7 +36,7 @@ public class UnidentifiedAccessHelper { this.senderCertificateProvider = senderCertificateProvider; } - public byte[] getSelfUnidentifiedAccessKey() { + private byte[] getSelfUnidentifiedAccessKey() { return UnidentifiedAccess.deriveAccessKeyFrom(selfProfileKeyProvider.getProfileKey()); } diff --git a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index 1c35b1fb..a030af3f 100644 --- a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -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.ThreadInfo; import org.asamk.signal.manager.util.IOUtils; +import org.asamk.signal.manager.util.KeyUtils; import org.asamk.signal.manager.util.Utils; import org.signal.zkgroup.InvalidInputException; 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.util.Medium; 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.push.SignalServiceAddress; import org.whispersystems.util.Base64; @@ -98,6 +100,8 @@ public class SignalAccount implements Closeable { try { SignalAccount account = new SignalAccount(pair.first(), pair.second()); account.load(dataPath); + account.migrateLegacyConfigs(); + return account; } catch (Throwable e) { pair.second().close(); @@ -169,6 +173,31 @@ public class SignalAccount implements Closeable { 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) { return new File(dataPath, username); } @@ -451,6 +480,10 @@ public class SignalAccount implements Closeable { return deviceId; } + public void setDeviceId(final int deviceId) { + this.deviceId = deviceId; + } + public String getPassword() { return password; } @@ -491,6 +524,10 @@ public class SignalAccount implements Closeable { this.profileKey = profileKey; } + public byte[] getSelfUnidentifiedAccessKey() { + return UnidentifiedAccess.deriveAccessKeyFrom(getProfileKey()); + } + public int getPreKeyIdOffset() { return preKeyIdOffset; } @@ -515,8 +552,19 @@ public class SignalAccount implements Closeable { isMultiDevice = multiDevice; } + public boolean isUnrestrictedUnidentifiedAccess() { + // TODO make configurable + return false; + } + + public boolean isDiscoverableByPhoneNumber() { + // TODO make configurable + return true; + } + @Override public void close() throws IOException { + save(); synchronized (fileChannel) { try { lock.close();