Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dbus identities #1259

Merged
merged 6 commits into from
Jun 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions man/signal-cli-dbus.5.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,24 @@ Only works if sent from a secondary device.

Exceptions: Failure

==== Identity related methods

listIdentities() -> identities<a(oss)>::
* identities : Array of structs (objectPath, id, name)
** objectPath : DBusPath representing the identity object path
** uuid : Internal uuid of the identity
** number : Phone number of the identity (or uuid if not known)

Lists all know identities

getIdentity(Number<s>) -> identityPath<o>::
* Number : Phone number
* identityPath : DBusPath object for the identity

Gets the identity Dbus path for a given phone number

Exceptions: Failure

=== Signal.Group interface

The following methods listen to the group's object path, which can be obtained from the listGroups() method and is constructed as follows:
Expand Down Expand Up @@ -531,6 +549,42 @@ removeDevice() -> <>::

Exceptions: Failure

=== Signal.Identity interface

The following methods listen to the Identities object path, which is constructed as follows:

<ACCOUNT_PATH> + "/Identities/" + identity

identity : Either the phone number of a contact with underscore (_) replacing plus (+) , or if not known its uuid

Identities have the following (case-sensitive) properties:

* Number<s> (read-only) : Phone number of the contact
* Uuid<x> (read-only) : Internal uuid representing the contact
* Fingerprint<x> (read-only) : Byte array representing the fingerprint
* SafetyNumber<s> (read-only) : String representation of the safety number used to verify trust
* TrustLevel<s> (read-only) : Current trust level (UNSTRUSTED, TRUSTED_UNVERIFIED, TRUSTED_VERIFIED)
* AddedDate<x> (read-only) : Long representing the number of milliseconds since the Unix epoch
* ScannableSafetyNumber<x> (read-only) : Byte array representation of the safety number

To get a property, use (replacing `--session` with `--system` if needed):
`dbus-send --session --dest=org.asamk.Signal --print-reply $OBJECT_PATH org.freedesktop.DBus.Properties.Get string:org.asamk.Signal.Identity string:$PROPERTY_NAME`

To get all properties, use:
`dbus-send --session --dest=org.asamk.Signal --print-reply $OBJECT_PATH org.freedesktop.DBus.Properties.GetAll string:org.asamk.Signal.Identity`

trust() -> <>::

Establish trust with the given identity. TrustLevel will become TRUSTED_UNVERFIED

Exceptions: Failure

trustVerified(SafetyNumber<s>) -> <>::

Establish trust with the given identity using their safety number. TrustLevel will become TRUSTED_VERIFIED

Exceptions: Failure

=== Signal.Configuration interface

The configuration's object path, which exists only for primary devices, is constructed as follows:
Expand Down
48 changes: 48 additions & 0 deletions src/main/java/org/asamk/Signal.java
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ byte[] updateGroup(

DBusPath getDevice(long deviceId);

public DBusPath getIdentity(String number);

public List<StructIdentity> listIdentities();

List<StructDevice> listDevices() throws Error.Failure;

DBusPath getThisDevice();
Expand Down Expand Up @@ -551,6 +555,50 @@ interface Group extends DBusInterface, Properties {
void enableLink(boolean requiresApproval) throws Error.Failure;
}

class StructIdentity extends Struct {

@Position(0)
DBusPath objectPath;

@Position(1)
String uuid;

@Position(2)
String name;

public StructIdentity(final DBusPath objectPath, final String uuid, final String name) {
this.objectPath = objectPath;
this.uuid = uuid;
this.name = name;
}

public DBusPath getObjectPath() {
return objectPath;
}

public String getUuid() {
return uuid;
}

public String getName() {
return name;
}
}

@DBusProperty(name = "Number", type = String.class, access = DBusProperty.Access.READ)
@DBusProperty(name = "Uuid", type = String.class, access = DBusProperty.Access.READ)
@DBusProperty(name = "Fingerprint", type = Byte[].class, access = DBusProperty.Access.READ)
@DBusProperty(name = "SafetyNumber", type = String.class, access = DBusProperty.Access.READ)
@DBusProperty(name = "TrustLevel", type = String.class, access = DBusProperty.Access.READ)
@DBusProperty(name = "AddedDate", type = Integer.class, access = DBusProperty.Access.READ)
@DBusProperty(name = "ScannableSafetyNumber", type = Byte[].class, access = DBusProperty.Access.READ)
interface Identity extends DBusInterface, Properties {

void trust() throws Error.Failure;

void trustVerified(String safetyNumber) throws Error.Failure;
}

interface Error {

class AttachmentInvalid extends DBusExecutionException {
Expand Down
108 changes: 108 additions & 0 deletions src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.asamk.signal.manager.api.UpdateGroup;
import org.asamk.signal.manager.api.UpdateProfile;
import org.asamk.signal.manager.api.UserStatus;
import org.asamk.signal.manager.api.IdentityVerificationCode;
import org.asamk.signal.util.SendMessageResultUtils;
import org.freedesktop.dbus.DBusPath;
import org.freedesktop.dbus.connections.impl.DBusConnection;
Expand All @@ -55,6 +56,7 @@
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.UUID;

import static org.asamk.signal.dbus.DbusUtils.makeValidObjectPathElement;

Expand All @@ -68,6 +70,7 @@ public class DbusSignalImpl implements Signal {
private DBusPath thisDevice;
private final List<StructDevice> devices = new ArrayList<>();
private final List<StructGroup> groups = new ArrayList<>();
private final List<StructIdentity> identities = new ArrayList<>();
private DbusReceiveMessageHandler dbusMessageHandler;
private int subscriberCount;

Expand Down Expand Up @@ -100,6 +103,7 @@ private void exportObjects() {
updateDevices();
updateGroups();
updateConfiguration();
updateIdentities();
}

public void close() {
Expand All @@ -114,6 +118,7 @@ private void unExportObjects() {
unExportDevices();
unExportGroups();
unExportConfiguration();
unExportIdentities();
connection.unExportObject(this.objectPath);
}

Expand Down Expand Up @@ -1029,6 +1034,109 @@ private void exportObject(final DBusInterface object) {
}
}

private void updateIdentities() {
List<org.asamk.signal.manager.api.Identity> identities;
identities = m.getIdentities();

unExportIdentities();

identities.forEach(i -> {
final var object = new DbusSignalIdentityImpl(i);
exportObject(object);
this.identities.add(new StructIdentity(new DBusPath(object.getObjectPath()),
emptyIfNull(i.recipient().getIdentifier()),
i.recipient().getLegacyIdentifier()));
});
}

private static String getIdentityObjectPath(String basePath, String id) {
return basePath + "/Identities/" + makeValidObjectPathElement(id);
}

private void unExportIdentities() {
this.identities.stream().map(StructIdentity::getObjectPath).map(DBusPath::getPath).forEach(connection::unExportObject);
this.identities.clear();
}

@Override
public DBusPath getIdentity(String number) throws Error.Failure {

final var found = identities.stream()
.filter(identity -> identity.getName().equals(number))
.findFirst();

if (found.isEmpty()) {
throw new Error.Failure("Identity for " + number + " unkown");
}
return found.get().getObjectPath();
}

@Override
public List<StructIdentity> listIdentities() {
updateIdentities();
return this.identities;
}

public class DbusSignalIdentityImpl extends DbusProperties implements Signal.Identity {

private final org.asamk.signal.manager.api.Identity identity;

public DbusSignalIdentityImpl(final org.asamk.signal.manager.api.Identity identity) {
this.identity=identity;
super.addPropertiesHandler(new DbusInterfacePropertiesHandler("org.asamk.Signal.Identity",
List.of(new DbusProperty<>("Number", () -> identity.recipient().number().orElse("")),
new DbusProperty<>("Uuid", () -> identity.recipient().uuid().map(UUID::toString).orElse("")),
new DbusProperty<>("Fingerprint", () -> identity.getFingerprint()),
new DbusProperty<>("SafetyNumber", identity::safetyNumber),
new DbusProperty<>("ScannableSafetyNumber", identity::scannableSafetyNumber),
new DbusProperty<>("TrustLevel", identity::trustLevel),
new DbusProperty<>("AddedDate", identity::dateAddedTimestamp)
)));
}

@Override
public String getObjectPath() {
return getIdentityObjectPath(objectPath, identity.recipient().getLegacyIdentifier());
}

@Override
public void trust() throws Error.Failure {
var recipient=RecipientIdentifier.Single.fromAddress(identity.recipient());
try {
m.trustIdentityAllKeys(recipient);
} catch (UnregisteredRecipientException e) {
throw new Error.Failure("The user " + e.getSender().getIdentifier() + " is not registered.");
}
};

@Override
public void trustVerified(String safetyNumber) throws Error.Failure {
var recipient = RecipientIdentifier.Single.fromAddress(identity.recipient());

if (safetyNumber == null) {
throw new Error.Failure(
"You need to specify a fingerprint/safety number");
}
final IdentityVerificationCode verificationCode;
try {
verificationCode = IdentityVerificationCode.parse(safetyNumber);
} catch (Exception e) {
throw new Error.Failure(
"Safety number has invalid format, either specify the old hex fingerprint or the new safety number");
}

try {
final var res = m.trustIdentityVerified(recipient, verificationCode);
if (!res) {
throw new Error.Failure(
"Failed to set the trust for this number, make sure the number and the fingerprint/safety number are correct.");
}
} catch (UnregisteredRecipientException e) {
throw new Error.Failure("The user " + e.getSender().getIdentifier() + " is not registered.");
}
}
}

public class DbusSignalDeviceImpl extends DbusProperties implements Signal.Device {

private final org.asamk.signal.manager.api.Device device;
Expand Down