mirror of
https://github.com/AsamK/signal-cli.git
synced 2026-08-14 03:36:01 +00:00
Explicitly store pni signature verified
This commit is contained in:
parent
e78521bb5c
commit
854cb35d15
@ -498,10 +498,11 @@ public final class IncomingMessageHandler {
|
||||
}
|
||||
|
||||
logger.debug("Verified association of ACI {} with PNI {}", aci, pni);
|
||||
account.getRecipientTrustedResolver()
|
||||
final var recipientId = account.getRecipientTrustedResolver()
|
||||
.resolveRecipientTrusted(Optional.of(ACI.from(aci.getRawUuid())),
|
||||
Optional.of(pni),
|
||||
senderAddress.getNumber());
|
||||
account.getRecipientStore().markPniSignatureVerified(recipientId);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -33,7 +33,7 @@ import java.util.UUID;
|
||||
public class AccountDatabase extends Database {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AccountDatabase.class);
|
||||
private static final long DATABASE_VERSION = 29;
|
||||
private static final long DATABASE_VERSION = 30;
|
||||
|
||||
private AccountDatabase(final HikariDataSource dataSource) {
|
||||
super(logger, DATABASE_VERSION, dataSource);
|
||||
@ -635,6 +635,14 @@ public class AccountDatabase extends Database {
|
||||
""");
|
||||
}
|
||||
}
|
||||
if (oldVersion < 30) {
|
||||
logger.debug("Updating database: Create pni_signature_verified column");
|
||||
try (final var statement = connection.createStatement()) {
|
||||
statement.executeUpdate("""
|
||||
ALTER TABLE recipient ADD pni_signature_verified INTEGER NOT NULL DEFAULT FALSE;
|
||||
""");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void createUuidMappingTable(
|
||||
|
||||
@ -100,6 +100,7 @@ public class LegacyRecipientStore2 {
|
||||
profile,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
null);
|
||||
}).collect(Collectors.toMap(Recipient::getRecipientId, r -> r));
|
||||
|
||||
|
||||
@ -25,6 +25,8 @@ public class Recipient {
|
||||
|
||||
private final Long unregisteredTimestamp;
|
||||
|
||||
private final boolean pniSignatureVerified;
|
||||
|
||||
private final byte[] storageRecord;
|
||||
|
||||
public Recipient(
|
||||
@ -36,6 +38,7 @@ public class Recipient {
|
||||
final Profile profile,
|
||||
final Boolean discoverable,
|
||||
final Long unregisteredTimestamp,
|
||||
final boolean pniSignatureVerified,
|
||||
final byte[] storageRecord
|
||||
) {
|
||||
this.recipientId = recipientId;
|
||||
@ -46,6 +49,7 @@ public class Recipient {
|
||||
this.profile = profile;
|
||||
this.discoverable = discoverable;
|
||||
this.unregisteredTimestamp = unregisteredTimestamp;
|
||||
this.pniSignatureVerified = pniSignatureVerified;
|
||||
this.storageRecord = storageRecord;
|
||||
}
|
||||
|
||||
@ -58,6 +62,7 @@ public class Recipient {
|
||||
profile = builder.profile;
|
||||
discoverable = builder.discoverable;
|
||||
unregisteredTimestamp = builder.unregisteredTimestamp;
|
||||
pniSignatureVerified = builder.pniSignatureVerified;
|
||||
storageRecord = builder.storageRecord;
|
||||
}
|
||||
|
||||
@ -73,6 +78,9 @@ public class Recipient {
|
||||
builder.profileKey = copy.getProfileKey();
|
||||
builder.expiringProfileKeyCredential = copy.getExpiringProfileKeyCredential();
|
||||
builder.profile = copy.getProfile();
|
||||
builder.discoverable = copy.getDiscoverable();
|
||||
builder.unregisteredTimestamp = copy.getUnregisteredTimestamp();
|
||||
builder.pniSignatureVerified = copy.isPniSignatureVerified();
|
||||
builder.storageRecord = copy.getStorageRecord();
|
||||
return builder;
|
||||
}
|
||||
@ -113,6 +121,10 @@ public class Recipient {
|
||||
return unregisteredTimestamp == null;
|
||||
}
|
||||
|
||||
public boolean isPniSignatureVerified() {
|
||||
return pniSignatureVerified;
|
||||
}
|
||||
|
||||
public byte[] getStorageRecord() {
|
||||
return storageRecord;
|
||||
}
|
||||
@ -127,12 +139,19 @@ public class Recipient {
|
||||
&& Objects.equals(contact, recipient.contact)
|
||||
&& Objects.equals(profileKey, recipient.profileKey)
|
||||
&& Objects.equals(expiringProfileKeyCredential, recipient.expiringProfileKeyCredential)
|
||||
&& Objects.equals(profile, recipient.profile);
|
||||
&& Objects.equals(profile, recipient.profile)
|
||||
&& pniSignatureVerified == recipient.pniSignatureVerified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(recipientId, address, contact, profileKey, expiringProfileKeyCredential, profile);
|
||||
return Objects.hash(recipientId,
|
||||
address,
|
||||
contact,
|
||||
profileKey,
|
||||
expiringProfileKeyCredential,
|
||||
profile,
|
||||
pniSignatureVerified);
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
@ -145,6 +164,7 @@ public class Recipient {
|
||||
private Profile profile;
|
||||
private Boolean discoverable;
|
||||
private Long unregisteredTimestamp;
|
||||
private boolean pniSignatureVerified;
|
||||
private byte[] storageRecord;
|
||||
|
||||
private Builder() {
|
||||
@ -190,6 +210,11 @@ public class Recipient {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withPniSignatureVerified(final boolean val) {
|
||||
pniSignatureVerified = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withStorageRecord(final byte[] val) {
|
||||
storageRecord = val;
|
||||
return this;
|
||||
|
||||
@ -54,14 +54,16 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||
|
||||
private static final int MAX_RECIPIENT_CACHE_SIZE = 2000;
|
||||
|
||||
private final Map<ServiceId, RecipientWithAddress> recipientAddressCache = Collections.synchronizedMap(
|
||||
new LinkedHashMap<>(16, 0.75f, true) {
|
||||
private final Map<ServiceId, RecipientWithAddress> recipientAddressCache = Collections.synchronizedMap(new LinkedHashMap<>(
|
||||
16,
|
||||
0.75f,
|
||||
true) {
|
||||
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<ServiceId, RecipientWithAddress> eldest) {
|
||||
return size() > MAX_RECIPIENT_CACHE_SIZE;
|
||||
}
|
||||
});
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<ServiceId, RecipientWithAddress> eldest) {
|
||||
return size() > MAX_RECIPIENT_CACHE_SIZE;
|
||||
}
|
||||
});
|
||||
|
||||
public static void createSql(Connection connection) throws SQLException {
|
||||
// When modifying the CREATE statement here, also add a migration in AccountDatabase.java
|
||||
@ -80,6 +82,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||
profile_key BLOB,
|
||||
profile_key_credential BLOB,
|
||||
needs_pni_signature INTEGER NOT NULL DEFAULT FALSE,
|
||||
pni_signature_verified INTEGER NOT NULL DEFAULT FALSE,
|
||||
|
||||
given_name TEXT,
|
||||
family_name TEXT,
|
||||
@ -372,6 +375,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||
SELECT r._id,
|
||||
r.number, r.aci, r.pni, r.username,
|
||||
r.profile_key, r.profile_key_credential,
|
||||
r.pni_signature_verified,
|
||||
r.given_name, r.family_name, r.nick_name, r.nick_name_given_name, r.nick_name_family_name, r.note, r.expiration_time, r.expiration_time_version, r.mute_until, r.hide_story, r.profile_sharing, r.color, r.blocked, r.archived, r.hidden, r.unregistered_timestamp,
|
||||
r.profile_last_update_timestamp, r.profile_given_name, r.profile_family_name, r.profile_about, r.profile_about_emoji, r.profile_avatar_url_path, r.profile_mobile_coin_address, r.profile_unidentified_access_mode, r.profile_capabilities, r.profile_phone_number_sharing,
|
||||
r.discoverable,
|
||||
@ -392,6 +396,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||
SELECT r._id,
|
||||
r.number, r.aci, r.pni, r.username,
|
||||
r.profile_key, r.profile_key_credential,
|
||||
r.pni_signature_verified,
|
||||
r.given_name, r.family_name, r.nick_name, r.nick_name_given_name, r.nick_name_family_name, r.note, r.expiration_time, r.expiration_time_version, r.mute_until, r.hide_story, r.profile_sharing, r.color, r.blocked, r.archived, r.hidden, r.unregistered_timestamp,
|
||||
r.profile_last_update_timestamp, r.profile_given_name, r.profile_family_name, r.profile_about, r.profile_about_emoji, r.profile_avatar_url_path, r.profile_mobile_coin_address, r.profile_unidentified_access_mode, r.profile_capabilities, r.profile_phone_number_sharing,
|
||||
r.discoverable,
|
||||
@ -441,6 +446,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||
SELECT r._id,
|
||||
r.number, r.aci, r.pni, r.username,
|
||||
r.profile_key, r.profile_key_credential,
|
||||
r.pni_signature_verified,
|
||||
r.given_name, r.family_name, r.nick_name, r.nick_name_given_name, r.nick_name_family_name, r.note, r.expiration_time, r.expiration_time_version, r.mute_until, r.hide_story, r.profile_sharing, r.color, r.blocked, r.archived, r.hidden, r.unregistered_timestamp,
|
||||
r.profile_last_update_timestamp, r.profile_given_name, r.profile_family_name, r.profile_about, r.profile_about_emoji, r.profile_avatar_url_path, r.profile_mobile_coin_address, r.profile_unidentified_access_mode, r.profile_capabilities, r.profile_phone_number_sharing,
|
||||
r.discoverable,
|
||||
@ -961,6 +967,69 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||
}
|
||||
}
|
||||
|
||||
public void markPniSignatureVerified(final RecipientId recipientId) {
|
||||
logger.debug("Marking {} as pni signature verified", recipientId);
|
||||
try (final var connection = database.getConnection()) {
|
||||
connection.setAutoCommit(false);
|
||||
if (storePniSignatureVerified(connection, recipientId, true)) {
|
||||
rotateStorageId(connection, recipientId);
|
||||
}
|
||||
connection.commit();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Failed update recipient store", e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean storePniSignatureVerified(
|
||||
final Connection connection,
|
||||
final RecipientId recipientId,
|
||||
final boolean value
|
||||
) throws SQLException {
|
||||
return storePniSignatureVerified(connection, recipientId, value, false);
|
||||
}
|
||||
|
||||
private boolean storePniSignatureVerified(
|
||||
final Connection connection,
|
||||
final RecipientId recipientId,
|
||||
final boolean value,
|
||||
final boolean force
|
||||
) throws SQLException {
|
||||
if (!force && isPniSignatureVerified(connection, recipientId) == value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final var sql = (
|
||||
"""
|
||||
UPDATE %s
|
||||
SET pni_signature_verified = ?
|
||||
WHERE _id = ?
|
||||
"""
|
||||
).formatted(TABLE_RECIPIENT);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setBoolean(1, value);
|
||||
statement.setLong(2, recipientId.id());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isPniSignatureVerified(
|
||||
final Connection connection,
|
||||
final RecipientId recipientId
|
||||
) throws SQLException {
|
||||
final var sql = (
|
||||
"""
|
||||
SELECT pni_signature_verified
|
||||
FROM %s
|
||||
WHERE _id = ?
|
||||
"""
|
||||
).formatted(TABLE_RECIPIENT);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setLong(1, recipientId.id());
|
||||
return Utils.executeQuerySingleRow(statement, resultSet -> resultSet.getBoolean("pni_signature_verified"));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean needsPniSignature(final RecipientId recipientId) {
|
||||
try (final var connection = database.getConnection()) {
|
||||
final var sql = (
|
||||
@ -1255,6 +1324,9 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||
final List<RecipientId> toBeMergedRecipientIds
|
||||
) throws SQLException {
|
||||
for (final var toBeMergedRecipientId : toBeMergedRecipientIds) {
|
||||
if (isPniSignatureVerified(connection, toBeMergedRecipientId)) {
|
||||
storePniSignatureVerified(connection, recipientId, true, true);
|
||||
}
|
||||
recipientMergeHandler.mergeRecipients(connection, recipientId, toBeMergedRecipientId);
|
||||
deleteRecipient(connection, toBeMergedRecipientId);
|
||||
recipientAddressCache.entrySet().removeIf(e -> e.getValue().id().equals(toBeMergedRecipientId));
|
||||
@ -1349,7 +1421,12 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||
final var sql = (
|
||||
"""
|
||||
UPDATE %s
|
||||
SET number = NULL, aci = NULL, pni = NULL, username = NULL, storage_id = NULL
|
||||
SET number = NULL,
|
||||
aci = NULL,
|
||||
pni = NULL,
|
||||
username = NULL,
|
||||
storage_id = NULL,
|
||||
pni_signature_verified = FALSE
|
||||
WHERE _id = ?
|
||||
"""
|
||||
).formatted(TABLE_RECIPIENT);
|
||||
@ -1365,10 +1442,18 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||
final RecipientAddress address
|
||||
) throws SQLException {
|
||||
recipientAddressCache.entrySet().removeIf(e -> e.getValue().id().equals(recipientId));
|
||||
final var existingAddress = resolveRecipientAddress(connection, recipientId);
|
||||
final var keepPniSignatureVerified = Objects.equals(existingAddress.aci(), address.aci()) && Objects.equals(
|
||||
existingAddress.pni(),
|
||||
address.pni());
|
||||
final var sql = (
|
||||
"""
|
||||
UPDATE %s
|
||||
SET number = ?, aci = ?, pni = ?, username = ?
|
||||
SET number = ?,
|
||||
aci = ?,
|
||||
pni = ?,
|
||||
username = ?,
|
||||
pni_signature_verified = ?
|
||||
WHERE _id = ?
|
||||
"""
|
||||
).formatted(TABLE_RECIPIENT);
|
||||
@ -1377,7 +1462,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||
statement.setString(2, address.aci().map(ACI::toString).orElse(null));
|
||||
statement.setString(3, address.pni().map(PNI::toString).orElse(null));
|
||||
statement.setString(4, address.username().orElse(null));
|
||||
statement.setLong(5, recipientId.id());
|
||||
statement.setBoolean(5, keepPniSignatureVerified && isPniSignatureVerified(connection, recipientId));
|
||||
statement.setLong(6, recipientId.id());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
rotateStorageId(connection, recipientId);
|
||||
@ -1595,6 +1681,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
||||
getProfileFromResultSet(resultSet),
|
||||
getDiscoverableFromResultSet(resultSet),
|
||||
getUnregisteredTimestampFromResultSet(resultSet),
|
||||
resultSet.getBoolean("pni_signature_verified"),
|
||||
getStorageRecordFromResultSet(resultSet));
|
||||
}
|
||||
|
||||
|
||||
@ -357,6 +357,7 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
||||
logger.warn("Received invalid contact identity key from storage");
|
||||
}
|
||||
}
|
||||
account.getRecipientStore().storePniSignatureVerified(connection, recipientId, contactProto.pniSignatureVerified);
|
||||
account.getRecipientStore()
|
||||
.storeStorageRecord(connection, recipientId, contactRecord.getId(), contactProto.encode());
|
||||
}
|
||||
|
||||
@ -100,6 +100,7 @@ public final class StorageSyncModels {
|
||||
final var builder = SignalContactRecord.Companion.newBuilder(recipient.getStorageRecord())
|
||||
.e164(address.number().orElse(""))
|
||||
.username(address.username().orElse(""))
|
||||
.pniSignatureVerified(recipient.isPniSignatureVerified())
|
||||
.profileKey(recipient.getProfileKey() == null
|
||||
? ByteString.EMPTY
|
||||
: ByteString.of(recipient.getProfileKey().serialize()));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user