From 3574be913a117c81497134b5438eb5f1578abae0 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 1 Nov 2021 12:32:14 -0400 Subject: [PATCH] Log out sender key state for internal users. --- .../securesms/database/SenderKeyDatabase.java | 13 +++ .../database/SenderKeySharedDatabase.java | 8 ++ .../jobs/SenderKeyDistributionSendJob.java | 2 +- .../logsubmit/LogSectionSenderKey.java | 43 +++++++++ .../logsubmit/SubmitDebugLogRepository.java | 4 + .../java/org/signal/core/util/AsciiArt.kt | 88 +++++++++++++++++++ .../api/SignalServiceMessageSender.java | 11 ++- 7 files changed, 164 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionSenderKey.java create mode 100644 core-util/src/main/java/org/signal/core/util/AsciiArt.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SenderKeyDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SenderKeyDatabase.java index 443af82693..53c2ae0f81 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SenderKeyDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SenderKeyDatabase.java @@ -10,11 +10,13 @@ import androidx.annotation.Nullable; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; +import org.thoughtcrime.securesms.recipients.Recipient; import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.signalservice.api.push.DistributionId; import org.thoughtcrime.securesms.util.CursorUtil; import org.thoughtcrime.securesms.util.SqlUtil; import org.whispersystems.libsignal.groups.state.SenderKeyRecord; +import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.io.IOException; @@ -110,6 +112,17 @@ public class SenderKeyDatabase extends Database { db.delete(TABLE_NAME, query, args); } + /** + * Get metadata for all sender keys created by the local user. Used for debugging. + */ + public Cursor getAllCreatedBySelf() { + SQLiteDatabase db = databaseHelper.getSignalReadableDatabase(); + String query = ADDRESS + " = ?"; + String[] args = SqlUtil.buildArgs(Recipient.self().requireAci()); + + return db.query(TABLE_NAME, new String[]{ ID, DISTRIBUTION_ID, CREATED_AT }, query, args, null, null, CREATED_AT + " DESC"); + } + /** * Deletes all database state. */ diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SenderKeySharedDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SenderKeySharedDatabase.java index f497a968f7..d713819cf5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SenderKeySharedDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SenderKeySharedDatabase.java @@ -157,4 +157,12 @@ public class SenderKeySharedDatabase extends Database { SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); db.delete(TABLE_NAME, null, null); } + + /** + * Gets the shared state of all of our sender keys. Used for debugging. + */ + public Cursor getAllSharedWithCursor() { + SQLiteDatabase db = databaseHelper.getSignalReadableDatabase(); + return db.query(TABLE_NAME, null, null, null, null, null, DISTRIBUTION_ID + ", " + ADDRESS + ", " + DEVICE); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SenderKeyDistributionSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SenderKeyDistributionSendJob.java index 3bac97bada..0bc5233d9e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/SenderKeyDistributionSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SenderKeyDistributionSendJob.java @@ -102,7 +102,7 @@ public final class SenderKeyDistributionSendJob extends BaseJob { SenderKeyDistributionMessage message = messageSender.getOrCreateNewGroupSession(distributionId); List> access = UnidentifiedAccessUtil.getAccessFor(context, Collections.singletonList(recipient)); - SendMessageResult result = messageSender.sendSenderKeyDistributionMessage(address, access, message, groupId.getDecodedId()).get(0); + SendMessageResult result = messageSender.sendSenderKeyDistributionMessage(distributionId, address, access, message, groupId.getDecodedId()).get(0); if (result.isSuccess()) { List addresses = result.getSuccess() diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionSenderKey.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionSenderKey.java new file mode 100644 index 0000000000..cc4801690f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionSenderKey.java @@ -0,0 +1,43 @@ +package org.thoughtcrime.securesms.logsubmit; + +import android.content.Context; +import android.database.Cursor; + +import androidx.annotation.NonNull; + +import org.signal.core.util.AsciiArt; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; +import org.whispersystems.libsignal.SignalProtocolAddress; +import org.whispersystems.signalservice.api.push.DistributionId; + +import java.util.Map; +import java.util.Set; + +/** + * Renders data pertaining to sender key. While all private info is obfuscated, this is still only intended to be printed for internal users. + */ +public class LogSectionSenderKey implements LogSection { + + @Override + public @NonNull String getTitle() { + return "SENDER KEY"; + } + + @Override + public @NonNull CharSequence getContent(@NonNull Context context) { + StringBuilder builder = new StringBuilder(); + + builder.append("--- Sender Keys").append("\n\n"); + try (Cursor cursor = DatabaseFactory.getSenderKeyDatabase(context).getAllCreatedBySelf()) { + builder.append(AsciiArt.tableFor(cursor)).append("\n\n"); + } + + builder.append("--- Sender Key Shared State").append("\n\n"); + try (Cursor cursor = DatabaseFactory.getSenderKeySharedDatabase(context).getAllSharedWithCursor()) { + builder.append(AsciiArt.tableFor(cursor)).append("\n"); + } + + return builder; + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogRepository.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogRepository.java index 88dc806b73..f63605f016 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogRepository.java @@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.net.StandardUserAgentInterceptor; import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess; import org.thoughtcrime.securesms.util.ByteUnit; +import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.Stopwatch; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.libsignal.util.guava.Optional; @@ -86,6 +87,9 @@ public class SubmitDebugLogRepository { add(new LogSectionTrace()); add(new LogSectionThreads()); add(new LogSectionBlockedThreads()); + if (FeatureFlags.internalUser()) { + add(new LogSectionSenderKey()); + } add(new LogSectionLogcat()); add(new LogSectionLoggerHeader()); }}; diff --git a/core-util/src/main/java/org/signal/core/util/AsciiArt.kt b/core-util/src/main/java/org/signal/core/util/AsciiArt.kt new file mode 100644 index 0000000000..a8741f9854 --- /dev/null +++ b/core-util/src/main/java/org/signal/core/util/AsciiArt.kt @@ -0,0 +1,88 @@ +package org.signal.core.util + +import android.database.Cursor +import kotlin.math.max + +class AsciiArt { + + private class Table ( + private val columns: List, + private val rows: List> + ) { + override fun toString(): String { + val columnWidths = columns.map { column -> column.length }.toIntArray() + + rows.forEach { row: List -> + columnWidths.forEachIndexed { index, currentMax -> + columnWidths[index] = max(row[index].length, currentMax) + } + } + + val builder = StringBuilder() + + columns.forEachIndexed { index, column -> + builder.append(COLUMN_DIVIDER).append(" ").append(rightPad(column, columnWidths[index])).append(" ") + } + builder.append(COLUMN_DIVIDER) + + builder.append("\n") + + columnWidths.forEach { width -> + builder.append(COLUMN_DIVIDER) + builder.append(ROW_DIVIDER.repeat(width + 2)) + } + builder.append(COLUMN_DIVIDER) + + builder.append("\n") + + rows.forEach { row -> + row.forEachIndexed { index, column -> + builder.append(COLUMN_DIVIDER).append(" ").append(rightPad(column, columnWidths[index])).append(" ") + } + builder.append(COLUMN_DIVIDER) + builder.append("\n") + } + + return builder.toString() + } + } + + companion object { + private const val COLUMN_DIVIDER = "|" + private const val ROW_DIVIDER = "-" + + /** + * Will return a string representing a table of the provided cursor. The caller is responsible for the lifecycle of the cursor. + */ + @JvmStatic + fun tableFor(cursor: Cursor): String { + val columns: MutableList = mutableListOf() + val rows: MutableList> = mutableListOf() + + columns.addAll(cursor.columnNames) + + while (cursor.moveToNext()) { + val row: MutableList = mutableListOf() + + for (i in 0 until columns.size) { + row += cursor.getString(i) + } + + rows += row + } + + return Table(columns, rows).toString() + } + + private fun rightPad(value: String, length: Int): String { + if (value.length >= length) { + return value + } + val out = java.lang.StringBuilder(value) + while (out.length < length) { + out.append(" ") + } + return out.toString() + } + } +} diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index e68f047db4..7186bbf2a1 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -360,7 +360,8 @@ public class SignalServiceMessageSender { /** * Sends the provided {@link SenderKeyDistributionMessage} to the specified recipients. */ - public List sendSenderKeyDistributionMessage(List recipients, + public List sendSenderKeyDistributionMessage(DistributionId distributionId, + List recipients, List> unidentifiedAccess, SenderKeyDistributionMessage message, byte[] groupId) @@ -371,7 +372,7 @@ public class SignalServiceMessageSender { EnvelopeContent envelopeContent = EnvelopeContent.encrypted(content, ContentHint.IMPLICIT, Optional.of(groupId)); long timestamp = System.currentTimeMillis(); - Log.d(TAG, "[" + timestamp + "] Sending SKDM to " + recipients.size() + " recipients."); + Log.d(TAG, "[" + timestamp + "] Sending SKDM to " + recipients.size() + " recipients for DistributionId " + distributionId); return sendMessage(recipients, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, envelopeContent, false, null, null); } @@ -411,7 +412,7 @@ public class SignalServiceMessageSender { SenderKeyGroupEvents sendEvents) throws IOException, UntrustedIdentityException, NoSessionException, InvalidKeyException, InvalidRegistrationIdException { - Log.d(TAG, "[" + message.getTimestamp() + "] Sending a group data message to " + recipients.size() + " recipients."); + Log.d(TAG, "[" + message.getTimestamp() + "] Sending a group data message to " + recipients.size() + " recipients using DistributionId " + distributionId); Content content = createMessageContent(message); Optional groupId = message.getGroupId(); @@ -1600,6 +1601,8 @@ public class SignalServiceMessageSender { if (content.getContent().isPresent() && content.getContent().get().getSyncMessage() != null && content.getContent().get().getSyncMessage().hasSent()) { Log.d(TAG, "[sendMessage][" + timestamp + "] Sending a sent sync message to devices: " + messages.getDevices()); + } else if (content.getContent().isPresent() && content.getContent().get().hasSenderKeyDistributionMessage()) { + Log.d(TAG, "[sendMessage][" + timestamp + "] Sending a SKDM to " + messages.getDestination() + " for devices: " + messages.getDevices()); } if (cancelationSignal != null && cancelationSignal.isCanceled()) { @@ -1709,7 +1712,7 @@ public class SignalServiceMessageSender { }) .collect(Collectors.toList()); - List results = sendSenderKeyDistributionMessage(needsSenderKey, access, message, groupId); + List results = sendSenderKeyDistributionMessage(distributionId, needsSenderKey, access, message, groupId); List successes = results.stream() .filter(SendMessageResult::isSuccess)