## [Unreleased]
## [0.8.3] - 2021-05-13
+**Attention**: Now requires native libsignal-client version 0.5.1
+
### Fixed
- Upgrading from account files with older profiles
- Building native image with graalvm
"name":"org.whispersystems.libsignal.UntrustedIdentityException",
"methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }]
},
+{
+ "name":"org.whispersystems.libsignal.logging.Log",
+ "methods":[{"name":"log","parameterTypes":["int","java.lang.String","java.lang.String"] }]
+},
{
"name":"org.whispersystems.libsignal.protocol.PreKeySignalMessage",
- "methods":[{"name":"<init>","parameterTypes":["long"] }]
+ "methods":[
+ {"name":"<init>","parameterTypes":["long"] },
+ {"name":"nativeHandle","parameterTypes":[] }
+ ]
},
{
"name":"org.whispersystems.libsignal.protocol.SignalMessage",
- "methods":[{"name":"<init>","parameterTypes":["long"] }]
+ "methods":[
+ {"name":"<init>","parameterTypes":["long"] },
+ {"name":"nativeHandle","parameterTypes":[] }
+ ]
},
{
"name":"org.whispersystems.libsignal.state.IdentityKeyStore"
"fields":[
{"name":"accessControl_", "allowUnsafeAccess":true},
{"name":"avatar_", "allowUnsafeAccess":true},
+ {"name":"description_", "allowUnsafeAccess":true},
{"name":"disappearingMessagesTimer_", "allowUnsafeAccess":true},
{"name":"inviteLinkPassword_", "allowUnsafeAccess":true},
{"name":"members_", "allowUnsafeAccess":true},
{"name":"modifyAddFromInviteLinkAccess_", "allowUnsafeAccess":true},
{"name":"modifyAttributesAccess_", "allowUnsafeAccess":true},
{"name":"modifyAvatar_", "allowUnsafeAccess":true},
+ {"name":"modifyDescription_", "allowUnsafeAccess":true},
{"name":"modifyDisappearingMessagesTimer_", "allowUnsafeAccess":true},
{"name":"modifyInviteLinkPassword_", "allowUnsafeAccess":true},
{"name":"modifyMemberAccess_", "allowUnsafeAccess":true},
"fields":[
{"name":"accessControl_", "allowUnsafeAccess":true},
{"name":"avatar_", "allowUnsafeAccess":true},
+ {"name":"description_", "allowUnsafeAccess":true},
{"name":"disappearingMessagesTimer_", "allowUnsafeAccess":true},
{"name":"inviteLinkPassword_", "allowUnsafeAccess":true},
{"name":"members_", "allowUnsafeAccess":true},
{"name":"modifyMemberRoles_", "allowUnsafeAccess":true},
{"name":"newAttributeAccess_", "allowUnsafeAccess":true},
{"name":"newAvatar_", "allowUnsafeAccess":true},
+ {"name":"newDescription_", "allowUnsafeAccess":true},
{"name":"newInviteLinkAccess_", "allowUnsafeAccess":true},
{"name":"newInviteLinkPassword_", "allowUnsafeAccess":true},
{"name":"newMemberAccess_", "allowUnsafeAccess":true},
{"name":"groupV2_", "allowUnsafeAccess":true},
{"name":"group_", "allowUnsafeAccess":true},
{"name":"isViewOnce_", "allowUnsafeAccess":true},
+ {"name":"payment_", "allowUnsafeAccess":true},
{"name":"preview_", "allowUnsafeAccess":true},
{"name":"profileKey_", "allowUnsafeAccess":true},
{"name":"quote_", "allowUnsafeAccess":true},
{"name":"groups_", "allowUnsafeAccess":true},
{"name":"keys_", "allowUnsafeAccess":true},
{"name":"messageRequestResponse_", "allowUnsafeAccess":true},
+ {"name":"outgoingPayment_", "allowUnsafeAccess":true},
{"name":"padding_", "allowUnsafeAccess":true},
{"name":"read_", "allowUnsafeAccess":true},
{"name":"request_", "allowUnsafeAccess":true},
{"name":"sent_", "allowUnsafeAccess":true},
{"name":"stickerPackOperation_", "allowUnsafeAccess":true},
{"name":"verified_", "allowUnsafeAccess":true},
- {"name":"viewOnceOpen_", "allowUnsafeAccess":true}
+ {"name":"viewOnceOpen_", "allowUnsafeAccess":true},
+ {"name":"viewed_", "allowUnsafeAccess":true}
]
},
{
{"name":"type_", "allowUnsafeAccess":true}
]
},
+{
+ "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Viewed",
+ "fields":[
+ {"name":"bitField0_", "allowUnsafeAccess":true},
+ {"name":"senderE164_", "allowUnsafeAccess":true},
+ {"name":"senderUuid_", "allowUnsafeAccess":true},
+ {"name":"timestamp_", "allowUnsafeAccess":true}
+ ]
+},
{
"name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$TypingMessage",
"fields":[
}
dependencies {
- api("com.github.turasa:signal-service-java:2.15.3_unofficial_21")
+ api("com.github.turasa:signal-service-java:2.15.3_unofficial_22")
implementation("com.google.protobuf:protobuf-javalite:3.10.0")
implementation("org.bouncycastle:bcprov-jdk15on:1.68")
implementation("org.slf4j:slf4j-api:1.7.30")
import org.whispersystems.signalservice.api.SignalServiceMessagePipe;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
+import org.whispersystems.signalservice.api.SignalSessionLock;
import org.whispersystems.signalservice.api.crypto.SignalServiceCipher;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import java.util.stream.Collectors;
private final PinHelper pinHelper;
private final AvatarStore avatarStore;
private final AttachmentStore attachmentStore;
+ private final SignalSessionLock sessionLock = new SignalSessionLock() {
+ private final ReentrantLock LEGACY_LOCK = new ReentrantLock();
+
+ @Override
+ public Lock acquire() {
+ LEGACY_LOCK.lock();
+ return LEGACY_LOCK::unlock;
+ }
+ };
Manager(
SignalAccount account,
newProfile.getInternalServiceName(),
newProfile.getAbout() == null ? "" : newProfile.getAbout(),
newProfile.getAboutEmoji() == null ? "" : newProfile.getAboutEmoji(),
+ Optional.absent(),
streamDetails);
}
account.getPassword(),
account.getDeviceId(),
account.getSignalProtocolStore(),
+ sessionLock,
userAgent,
account.isMultiDevice(),
Optional.fromNullable(messagePipe),
}
}
- private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, SelfSendException, UnsupportedDataMessageException, org.whispersystems.libsignal.UntrustedIdentityException {
+ private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, SelfSendException, UnsupportedDataMessageException, ProtocolUntrustedIdentityException {
var cipher = new SignalServiceCipher(account.getSelfAddress(),
account.getSignalProtocolStore(),
+ sessionLock,
certificateValidator);
- try {
- return cipher.decrypt(envelope);
- } catch (ProtocolUntrustedIdentityException e) {
- if (e.getCause() instanceof org.whispersystems.libsignal.UntrustedIdentityException) {
- throw (org.whispersystems.libsignal.UntrustedIdentityException) e.getCause();
- }
- throw new AssertionError(e);
- }
+ return cipher.decrypt(envelope);
}
private void handleEndSession(RecipientId recipientId) {
if (!envelope.isReceipt()) {
try {
content = decryptMessage(envelope);
- } catch (org.whispersystems.libsignal.UntrustedIdentityException e) {
+ } catch (ProtocolUntrustedIdentityException e) {
if (!envelope.hasSource()) {
- final var identifier = ((org.whispersystems.libsignal.UntrustedIdentityException) e).getName();
+ final var identifier = e.getSender();
final var recipientId = resolveRecipient(identifier);
try {
account.getMessageCache().replaceSender(cachedMessage, recipientId);
handler.handleMessage(envelope, content, exception);
}
if (cachedMessage[0] != null) {
- if (exception instanceof org.whispersystems.libsignal.UntrustedIdentityException) {
- final var identifier = ((org.whispersystems.libsignal.UntrustedIdentityException) exception).getName();
+ if (exception instanceof ProtocolUntrustedIdentityException) {
+ final var identifier = ((ProtocolUntrustedIdentityException) exception).getSender();
final var recipientId = resolveRecipient(identifier);
queuedActions.add(new RetrieveProfileAction(recipientId));
if (!envelope.hasSource()) {
public static boolean isSignalClientAvailable() {
try {
- org.signal.client.internal.Native.DisplayableFingerprint_Format(new byte[30], new byte[30]);
+ org.signal.client.internal.Native.DeviceTransfer_GeneratePrivateKey();
return true;
} catch (UnsatisfiedLinkError ignored) {
return false;
import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.InvalidKeyIdException;
import org.whispersystems.libsignal.SignalProtocolAddress;
+import org.whispersystems.libsignal.groups.state.SenderKeyRecord;
import org.whispersystems.libsignal.state.IdentityKeyStore;
import org.whispersystems.libsignal.state.PreKeyRecord;
import org.whispersystems.libsignal.state.PreKeyStore;
import org.whispersystems.signalservice.api.SignalServiceSessionStore;
import java.util.List;
+import java.util.UUID;
public class SignalProtocolStore implements SignalServiceProtocolStore {
public void removeSignedPreKey(int signedPreKeyId) {
signedPreKeyStore.removeSignedPreKey(signedPreKeyId);
}
+
+ @Override
+ public void storeSenderKey(
+ final SignalProtocolAddress sender, final UUID distributionId, final SenderKeyRecord record
+ ) {
+ }
+
+ @Override
+ public SenderKeyRecord loadSenderKey(final SignalProtocolAddress sender, final UUID distributionId) {
+ return null;
+ }
}
name,
false,
false,
+ false,
preview,
0,
0,
) {
var profileCipher = new ProfileCipher(profileKey);
try {
- var name = decryptName(encryptedProfile.getName(), profileCipher);
- var about = decryptName(encryptedProfile.getAbout(), profileCipher);
- var aboutEmoji = decryptName(encryptedProfile.getAboutEmoji(), profileCipher);
+ var name = decrypt(encryptedProfile.getName(), profileCipher);
+ var about = decrypt(encryptedProfile.getAbout(), profileCipher);
+ var aboutEmoji = decrypt(encryptedProfile.getAboutEmoji(), profileCipher);
final var nameParts = splitName(name);
return new Profile(new Date().getTime(),
return capabilities;
}
- private static String decryptName(
+ private static String decrypt(
final String encryptedName, final ProfileCipher profileCipher
) throws InvalidCiphertextException {
try {
return encryptedName == null
? null
- : new String(profileCipher.decryptName(Base64.getDecoder().decode(encryptedName)));
+ : new String(profileCipher.decrypt(Base64.getDecoder().decode(encryptedName)));
} catch (IllegalArgumentException e) {
return null;
}
import org.asamk.signal.manager.groups.GroupUtils;
import org.asamk.signal.util.DateUtils;
import org.asamk.signal.util.Util;
+import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException;
import org.slf4j.helpers.MessageFormatter;
-import org.whispersystems.libsignal.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
-import java.io.IOException;
import java.util.Base64;
import java.util.stream.Collectors;
@Override
public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) {
- try {
- printMessage(envelope, content, exception);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- private void printMessage(
- SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception
- ) throws IOException {
PlainTextWriter writer = new PlainTextWriterImpl(System.out);
if (envelope.hasSource()) {
writer.println("Got receipt.");
} else if (envelope.isSignalMessage() || envelope.isPreKeySignalMessage() || envelope.isUnidentifiedSender()) {
if (exception != null) {
- if (exception instanceof UntrustedIdentityException) {
- var e = (UntrustedIdentityException) exception;
+ if (exception instanceof ProtocolUntrustedIdentityException) {
+ var e = (ProtocolUntrustedIdentityException) exception;
writer.println(
"The user’s key is untrusted, either the user has reinstalled Signal or a third party sent this message.");
- final var recipientName = m.resolveSignalServiceAddress(e.getName()).getLegacyIdentifier();
+ final var recipientName = m.resolveSignalServiceAddress(e.getSender()).getLegacyIdentifier();
writer.println(
"Use 'signal-cli -u {} listIdentities -n {}', verify the key and run 'signal-cli -u {} trust -v \"FINGER_PRINT\" {}' to mark it as trusted",
m.getUsername(),
private void printDataMessage(
PlainTextWriter writer, SignalServiceDataMessage message
- ) throws IOException {
+ ) {
writer.println("Message timestamp: {}", DateUtils.formatTimestamp(message.getTimestamp()));
if (message.isViewOnce()) {
writer.println("=VIEW ONCE=");
private void printTypingMessage(
final PlainTextWriter writer, final SignalServiceTypingMessage typingMessage
- ) throws IOException {
+ ) {
writer.println("Action: {}", typingMessage.getAction());
writer.println("Timestamp: {}", DateUtils.formatTimestamp(typingMessage.getTimestamp()));
if (typingMessage.getGroupId().isPresent()) {
private void printReceiptMessage(
final PlainTextWriter writer, final SignalServiceReceiptMessage receiptMessage
- ) throws IOException {
+ ) {
writer.println("When: {}", DateUtils.formatTimestamp(receiptMessage.getWhen()));
if (receiptMessage.isDeliveryReceipt()) {
writer.println("Is delivery receipt");
private void printCallMessage(
final PlainTextWriter writer, final SignalServiceCallMessage callMessage
- ) throws IOException {
+ ) {
if (callMessage.getDestinationDeviceId().isPresent()) {
final var deviceId = callMessage.getDestinationDeviceId().get();
writer.println("Destination device id: {}", deviceId);
private void printSyncMessage(
final PlainTextWriter writer, final SignalServiceSyncMessage syncMessage
- ) throws IOException {
+ ) {
if (syncMessage.getContacts().isPresent()) {
final var contactsMessage = syncMessage.getContacts().get();
var type = contactsMessage.isComplete() ? "complete" : "partial";
private void printPreview(
final PlainTextWriter writer, final SignalServiceDataMessage.Preview preview
- ) throws IOException {
+ ) {
writer.println("Title: {}", preview.getTitle());
writer.println("Description: {}", preview.getDescription());
writer.println("Date: {}", DateUtils.formatTimestamp(preview.getDate()));
private void printSticker(
final PlainTextWriter writer, final SignalServiceDataMessage.Sticker sticker
- ) throws IOException {
+ ) {
writer.println("Pack id: {}", Base64.getEncoder().encodeToString(sticker.getPackId()));
writer.println("Pack key: {}", Base64.getEncoder().encodeToString(sticker.getPackKey()));
writer.println("Sticker id: {}", sticker.getStickerId());
private void printReaction(
final PlainTextWriter writer, final SignalServiceDataMessage.Reaction reaction
- ) throws IOException {
+ ) {
writer.println("Emoji: {}", reaction.getEmoji());
writer.println("Target author: {}", formatContact(m.resolveSignalServiceAddress(reaction.getTargetAuthor())));
writer.println("Target timestamp: {}", DateUtils.formatTimestamp(reaction.getTargetSentTimestamp()));
private void printQuote(
final PlainTextWriter writer, final SignalServiceDataMessage.Quote quote
- ) throws IOException {
+ ) {
writer.println("Id: {}", quote.getId());
writer.println("Author: {}", m.resolveSignalServiceAddress(quote.getAuthor()).getLegacyIdentifier());
writer.println("Text: {}", quote.getText());
}
}
- private void printSharedContact(final PlainTextWriter writer, final SharedContact contact) throws IOException {
+ private void printSharedContact(final PlainTextWriter writer, final SharedContact contact) {
writer.println("Name:");
var name = contact.getName();
writer.indent(w -> {
private void printGroupContext(
final PlainTextWriter writer, final SignalServiceGroupContext groupContext
- ) throws IOException {
+ ) {
final var groupId = GroupUtils.getGroupId(groupContext);
if (groupContext.getGroupV1().isPresent()) {
var groupInfo = groupContext.getGroupV1().get();
}
}
- private void printGroupInfo(final PlainTextWriter writer, final GroupId groupId) throws IOException {
+ private void printGroupInfo(final PlainTextWriter writer, final GroupId groupId) {
writer.println("Id: {}", groupId.toBase64());
var group = m.getGroup(groupId);
private void printMention(
PlainTextWriter writer, SignalServiceDataMessage.Mention mention
- ) throws IOException {
+ ) {
final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), null));
writer.println("- {}: {} (length: {})", formatContact(address), mention.getStart(), mention.getLength());
}