Sender Key: Disable capability and bug fixes

This commit is contained in:
Scott Nonnenberg 2021-06-01 12:40:09 -07:00 committed by GitHub
parent 33595646c1
commit 5a4fc5e425
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 95 additions and 53 deletions

View file

@ -424,13 +424,18 @@ export async function startApp(): Promise<void> {
const { conversationId, senderUuid } = item; const { conversationId, senderUuid } = item;
const conversation = window.ConversationController.get(conversationId); const conversation = window.ConversationController.get(conversationId);
if (conversation) { if (conversation) {
const now = Date.now(); const receivedAt = Date.now();
const receivedAtCounter = window.Signal.Util.incrementMessageCounter();
conversation.queueJob(() => conversation.queueJob(() =>
conversation.addDeliveryIssue(now, senderUuid) conversation.addDeliveryIssue({
receivedAt,
receivedAtCounter,
senderUuid,
})
); );
} }
}); });
}, 5 * 60 * 1000); }, FIVE_MINUTES);
// These make key operations available to IPC handlers created in preload.js // These make key operations available to IPC handlers created in preload.js
window.Events = { window.Events = {
@ -2117,7 +2122,7 @@ export async function startApp(): Promise<void> {
await server.registerCapabilities({ await server.registerCapabilities({
'gv2-3': true, 'gv2-3': true,
'gv1-migration': true, 'gv1-migration': true,
senderKey: true, senderKey: false,
}); });
} catch (error) { } catch (error) {
window.log.error( window.log.error(
@ -3382,13 +3387,9 @@ export async function startApp(): Promise<void> {
sentAt, sentAt,
senderDevice, senderDevice,
} = retryRequest; } = retryRequest;
const logId = `${requesterUuid}.${requesterDevice} ${sentAt}-${senderDevice}`;
window.log.info('onRetryRequest:', { window.log.info(`onRetryRequest/${logId}: Starting...`);
requesterUuid,
requesterDevice,
sentAt,
senderDevice,
});
const requesterConversation = window.ConversationController.getOrCreate( const requesterConversation = window.ConversationController.getOrCreate(
requesterUuid, requesterUuid,
@ -3416,15 +3417,13 @@ export async function startApp(): Promise<void> {
}); });
if (!targetMessage) { if (!targetMessage) {
window.log.info( window.log.info(`onRetryRequest/${logId}: Did not find message`);
`onRetryRequest: Did not find message sent at ${sentAt}, sent to ${requesterUuid}`
);
return; return;
} }
if (targetMessage.isErased()) { if (targetMessage.isErased()) {
window.log.info( window.log.info(
`onRetryRequest: Message sent at ${sentAt} is erased, refusing to send again.` `onRetryRequest/${logId}: Message is erased, refusing to send again.`
); );
return; return;
} }
@ -3433,7 +3432,7 @@ export async function startApp(): Promise<void> {
const ONE_DAY = 24 * HOUR; const ONE_DAY = 24 * HOUR;
if (isOlderThan(sentAt, ONE_DAY)) { if (isOlderThan(sentAt, ONE_DAY)) {
window.log.info( window.log.info(
`onRetryRequest: Message sent at ${sentAt} is too old, refusing to send again.` `onRetryRequest/${logId}: Message is too old, refusing to send again.`
); );
return; return;
} }
@ -3448,15 +3447,11 @@ export async function startApp(): Promise<void> {
); );
if (sentUnidentified && wasDelivered) { if (sentUnidentified && wasDelivered) {
window.log.info( window.log.info(
`onRetryRequest: Message sent at ${sentAt} was sent sealed sender and was delivered, refusing to send again.` `onRetryRequest/${logId}: Message was sent sealed sender and was delivered, refusing to send again.`
); );
return; return;
} }
window.log.info(
`onRetryRequest: Resending message ${sentAt} to user ${requesterUuid}`
);
const ourDeviceId = parseIntOrThrow( const ourDeviceId = parseIntOrThrow(
window.textsecure.storage.user.getDeviceId(), window.textsecure.storage.user.getDeviceId(),
'onRetryRequest/getDeviceId' 'onRetryRequest/getDeviceId'
@ -3464,11 +3459,12 @@ export async function startApp(): Promise<void> {
if (ourDeviceId === senderDevice) { if (ourDeviceId === senderDevice) {
const address = `${requesterUuid}.${requesterDevice}`; const address = `${requesterUuid}.${requesterDevice}`;
window.log.info( window.log.info(
`onRetryRequest: Devices match, archiving session with ${address}` `onRetryRequest/${logId}: Devices match, archiving session`
); );
await window.textsecure.storage.protocol.archiveSession(address); await window.textsecure.storage.protocol.archiveSession(address);
} }
window.log.info(`onRetryRequest/${logId}: Resending message`);
targetMessage.resend(requesterUuid); targetMessage.resend(requesterUuid);
} }
@ -3478,9 +3474,10 @@ export async function startApp(): Promise<void> {
async function onDecryptionError(event: DecryptionErrorEventType) { async function onDecryptionError(event: DecryptionErrorEventType) {
const { decryptionError } = event; const { decryptionError } = event;
const { senderUuid, senderDevice } = decryptionError; const { senderUuid, senderDevice, timestamp } = decryptionError;
const logId = `${senderUuid}.${senderDevice} ${timestamp}`;
window.log.info(`onDecryptionError: ${senderUuid}.${senderDevice}`); window.log.info(`onDecryptionError/${logId}: Starting...`);
const conversation = window.ConversationController.getOrCreate( const conversation = window.ConversationController.getOrCreate(
senderUuid, senderUuid,
@ -3492,11 +3489,12 @@ export async function startApp(): Promise<void> {
} }
if (conversation.get('capabilities')?.senderKey) { if (conversation.get('capabilities')?.senderKey) {
requestResend(decryptionError); await requestResend(decryptionError);
return; } else {
await startAutomaticSessionReset(decryptionError);
} }
await startAutomaticSessionReset(decryptionError); window.log.info(`onDecryptionError/${logId}: ...complete`);
} }
async function requestResend(decryptionError: DecryptionErrorType) { async function requestResend(decryptionError: DecryptionErrorType) {
@ -3511,13 +3509,13 @@ export async function startApp(): Promise<void> {
senderUuid, senderUuid,
timestamp, timestamp,
} = decryptionError; } = decryptionError;
const logId = `${senderUuid}.${senderDevice} ${timestamp}`;
window.log.info(`requestResend: ${senderUuid}.${senderDevice}`, { window.log.info(`requestResend/${logId}: Starting...`, {
cipherTextBytesLength: cipherTextBytes?.byteLength, cipherTextBytesLength: cipherTextBytes?.byteLength,
cipherTextType, cipherTextType,
contentHint, contentHint,
groupId: groupId ? `groupv2(${groupId})` : undefined, groupId: groupId ? `groupv2(${groupId})` : undefined,
timestamp,
}); });
// 1. Find the target conversation // 1. Find the target conversation
@ -3532,9 +3530,12 @@ export async function startApp(): Promise<void> {
const conversation = group || sender; const conversation = group || sender;
function immediatelyAddError() { function immediatelyAddError() {
const receivedAt = Date.now();
conversation.queueJob(async () => { conversation.queueJob(async () => {
conversation.addDeliveryIssue(receivedAt, senderUuid); conversation.addDeliveryIssue({
receivedAt: receivedAtDate,
receivedAtCounter,
senderUuid,
});
}); });
} }
@ -3542,7 +3543,7 @@ export async function startApp(): Promise<void> {
if (!cipherTextBytes || !isNumber(cipherTextType)) { if (!cipherTextBytes || !isNumber(cipherTextType)) {
window.log.warn( window.log.warn(
'requestResend: Missing cipherText information, failing over to automatic reset' `requestResend/${logId}: Missing cipherText information, failing over to automatic reset`
); );
startAutomaticSessionReset(decryptionError); startAutomaticSessionReset(decryptionError);
return; return;
@ -3568,7 +3569,7 @@ export async function startApp(): Promise<void> {
} }
} catch (error) { } catch (error) {
window.log.error( window.log.error(
'requestResend: Failed to send retry request, failing over to automatic reset', `requestResend/${logId}: Failed to send retry request, failing over to automatic reset`,
error && error.stack ? error.stack : error error && error.stack ? error.stack : error
); );
startAutomaticSessionReset(decryptionError); startAutomaticSessionReset(decryptionError);
@ -3581,7 +3582,8 @@ export async function startApp(): Promise<void> {
// 3. Determine how to represent this to the user. Three different options. // 3. Determine how to represent this to the user. Three different options.
// This is a sync message of some kind that cannot be resent. Don't do anything. // This is a sync message of some kind that cannot be resent. Reset session but don't
// show any UI for it.
if (contentHint === ContentHint.SUPPLEMENTARY) { if (contentHint === ContentHint.SUPPLEMENTARY) {
scheduleSessionReset(senderUuid, senderDevice); scheduleSessionReset(senderUuid, senderDevice);
return; return;
@ -3592,7 +3594,7 @@ export async function startApp(): Promise<void> {
const { retryPlaceholders } = window.Signal.Services; const { retryPlaceholders } = window.Signal.Services;
assert(retryPlaceholders, 'requestResend: adding placeholder'); assert(retryPlaceholders, 'requestResend: adding placeholder');
window.log.warn('requestResend: Adding placeholder'); window.log.info(`requestResend/${logId}: Adding placeholder`);
await retryPlaceholders.add({ await retryPlaceholders.add({
conversationId: conversation.get('id'), conversationId: conversation.get('id'),
receivedAt: receivedAtDate, receivedAt: receivedAtDate,
@ -3604,6 +3606,9 @@ export async function startApp(): Promise<void> {
return; return;
} }
window.log.warn(
`requestResend/${logId}: No content hint, adding error immediately`
);
immediatelyAddError(); immediatelyAddError();
} }
@ -3618,7 +3623,10 @@ export async function startApp(): Promise<void> {
} }
function startAutomaticSessionReset(decryptionError: DecryptionErrorType) { function startAutomaticSessionReset(decryptionError: DecryptionErrorType) {
const { senderUuid, senderDevice } = decryptionError; const { senderUuid, senderDevice, timestamp } = decryptionError;
const logId = `${senderUuid}.${senderDevice} ${timestamp}`;
window.log.info(`startAutomaticSessionReset/${logId}: Starting...`);
scheduleSessionReset(senderUuid, senderDevice); scheduleSessionReset(senderUuid, senderDevice);
@ -3642,8 +3650,9 @@ export async function startApp(): Promise<void> {
} }
const receivedAt = Date.now(); const receivedAt = Date.now();
const receivedAtCounter = window.Signal.Util.incrementMessageCounter();
conversation.queueJob(async () => { conversation.queueJob(async () => {
conversation.addChatSessionRefreshed(receivedAt); conversation.addChatSessionRefreshed({ receivedAt, receivedAtCounter });
}); });
} }

View file

@ -193,7 +193,7 @@ window.addEventListener('unhandledrejection', rejectionEvent => {
}); });
initLogger( initLogger(
SignalClientLogLevel.Trace, SignalClientLogLevel.Warn,
( (
level: unknown, level: unknown,
target: string, target: string,

View file

@ -2450,7 +2450,13 @@ export class ConversationModel extends window.Backbone
return this.setVerified(); return this.setVerified();
} }
async addChatSessionRefreshed(receivedAt: number): Promise<void> { async addChatSessionRefreshed({
receivedAt,
receivedAtCounter,
}: {
receivedAt: number;
receivedAtCounter: number;
}): Promise<void> {
window.log.info( window.log.info(
`addChatSessionRefreshed: adding for ${this.idForLogging()}`, `addChatSessionRefreshed: adding for ${this.idForLogging()}`,
{ receivedAt } { receivedAt }
@ -2460,7 +2466,7 @@ export class ConversationModel extends window.Backbone
conversationId: this.id, conversationId: this.id,
type: 'chat-session-refreshed', type: 'chat-session-refreshed',
sent_at: receivedAt, sent_at: receivedAt,
received_at: window.Signal.Util.incrementMessageCounter(), received_at: receivedAtCounter,
received_at_ms: receivedAt, received_at_ms: receivedAt,
unread: 1, unread: 1,
// TODO: DESKTOP-722 // TODO: DESKTOP-722
@ -2481,10 +2487,15 @@ export class ConversationModel extends window.Backbone
this.trigger('newmessage', model); this.trigger('newmessage', model);
} }
async addDeliveryIssue( async addDeliveryIssue({
receivedAt: number, receivedAt,
senderUuid: string receivedAtCounter,
): Promise<void> { senderUuid,
}: {
receivedAt: number;
receivedAtCounter: number;
senderUuid: string;
}): Promise<void> {
window.log.info(`addDeliveryIssue: adding for ${this.idForLogging()}`, { window.log.info(`addDeliveryIssue: adding for ${this.idForLogging()}`, {
receivedAt, receivedAt,
senderUuid, senderUuid,
@ -2495,7 +2506,7 @@ export class ConversationModel extends window.Backbone
type: 'delivery-issue', type: 'delivery-issue',
sourceUuid: senderUuid, sourceUuid: senderUuid,
sent_at: receivedAt, sent_at: receivedAt,
received_at: window.Signal.Util.incrementMessageCounter(), received_at: receivedAtCounter,
received_at_ms: receivedAt, received_at_ms: receivedAt,
unread: 1, unread: 1,
// TODO: DESKTOP-722 // TODO: DESKTOP-722

View file

@ -1122,6 +1122,7 @@ class MessageReceiverInner extends EventTarget {
envelope: EnvelopeClass, envelope: EnvelopeClass,
ciphertext: ByteBufferClass ciphertext: ByteBufferClass
): Promise<ArrayBuffer | null> { ): Promise<ArrayBuffer | null> {
const logId = this.getEnvelopeId(envelope);
const { serverTrustRoot } = this; const { serverTrustRoot } = this;
const envelopeTypeEnum = window.textsecure.protobuf.Envelope.Type; const envelopeTypeEnum = window.textsecure.protobuf.Envelope.Type;
const unidentifiedSenderTypeEnum = const unidentifiedSenderTypeEnum =
@ -1149,6 +1150,7 @@ class MessageReceiverInner extends EventTarget {
>; >;
if (envelope.type === envelopeTypeEnum.PLAINTEXT_CONTENT) { if (envelope.type === envelopeTypeEnum.PLAINTEXT_CONTENT) {
window.log.info(`decrypt/${logId}: plaintext message`);
const buffer = Buffer.from(ciphertext.toArrayBuffer()); const buffer = Buffer.from(ciphertext.toArrayBuffer());
const plaintextContent = PlaintextContent.deserialize(buffer); const plaintextContent = PlaintextContent.deserialize(buffer);
@ -1156,7 +1158,7 @@ class MessageReceiverInner extends EventTarget {
this.unpad(typedArrayToArrayBuffer(plaintextContent.body())) this.unpad(typedArrayToArrayBuffer(plaintextContent.body()))
); );
} else if (envelope.type === envelopeTypeEnum.CIPHERTEXT) { } else if (envelope.type === envelopeTypeEnum.CIPHERTEXT) {
window.log.info('message from', this.getEnvelopeId(envelope)); window.log.info(`decrypt/${logId}: ciphertext message`);
if (!identifier) { if (!identifier) {
throw new Error( throw new Error(
'MessageReceiver.decrypt: No identifier for CIPHERTEXT message' 'MessageReceiver.decrypt: No identifier for CIPHERTEXT message'
@ -1184,7 +1186,7 @@ class MessageReceiverInner extends EventTarget {
zone zone
); );
} else if (envelope.type === envelopeTypeEnum.PREKEY_BUNDLE) { } else if (envelope.type === envelopeTypeEnum.PREKEY_BUNDLE) {
window.log.info('prekey message from', this.getEnvelopeId(envelope)); window.log.info(`decrypt/${logId}: prekey message`);
if (!identifier) { if (!identifier) {
throw new Error( throw new Error(
'MessageReceiver.decrypt: No identifier for PREKEY_BUNDLE message' 'MessageReceiver.decrypt: No identifier for PREKEY_BUNDLE message'
@ -1214,7 +1216,7 @@ class MessageReceiverInner extends EventTarget {
zone zone
); );
} else if (envelope.type === envelopeTypeEnum.UNIDENTIFIED_SENDER) { } else if (envelope.type === envelopeTypeEnum.UNIDENTIFIED_SENDER) {
window.log.info('received unidentified sender message'); window.log.info(`decrypt/${logId}: unidentified message`);
const buffer = Buffer.from(ciphertext.toArrayBuffer()); const buffer = Buffer.from(ciphertext.toArrayBuffer());
const decryptSealedSender = async (): Promise< const decryptSealedSender = async (): Promise<
@ -1241,6 +1243,7 @@ class MessageReceiverInner extends EventTarget {
['sourceUuid'], ['sourceUuid'],
'message_receiver::decrypt::UNIDENTIFIED_SENDER' 'message_receiver::decrypt::UNIDENTIFIED_SENDER'
); );
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
envelope.sourceDevice = certificate.senderDeviceId(); envelope.sourceDevice = certificate.senderDeviceId();
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
@ -1248,6 +1251,8 @@ class MessageReceiverInner extends EventTarget {
originalSource || originalSourceUuid originalSource || originalSourceUuid
); );
const unidentifiedLogId = this.getEnvelopeId(envelope);
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
envelope.contentHint = messageContent.contentHint(); envelope.contentHint = messageContent.contentHint();
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
@ -1275,6 +1280,9 @@ class MessageReceiverInner extends EventTarget {
messageContent.msgType() === messageContent.msgType() ===
unidentifiedSenderTypeEnum.PLAINTEXT_CONTENT unidentifiedSenderTypeEnum.PLAINTEXT_CONTENT
) { ) {
window.log.info(
`decrypt/${unidentifiedLogId}: unidentified message/plaintext contents`
);
const plaintextContent = PlaintextContent.deserialize( const plaintextContent = PlaintextContent.deserialize(
messageContent.contents() messageContent.contents()
); );
@ -1286,6 +1294,9 @@ class MessageReceiverInner extends EventTarget {
messageContent.msgType() === messageContent.msgType() ===
unidentifiedSenderTypeEnum.SENDERKEY_MESSAGE unidentifiedSenderTypeEnum.SENDERKEY_MESSAGE
) { ) {
window.log.info(
`decrypt/${unidentifiedLogId}: unidentified message/sender key contents`
);
const sealedSenderIdentifier = certificate.senderUuid(); const sealedSenderIdentifier = certificate.senderUuid();
const sealedSenderSourceDevice = certificate.senderDeviceId(); const sealedSenderSourceDevice = certificate.senderDeviceId();
const senderKeyStore = new SenderKeys(); const senderKeyStore = new SenderKeys();
@ -1307,6 +1318,9 @@ class MessageReceiverInner extends EventTarget {
); );
} }
window.log.info(
`decrypt/${unidentifiedLogId}: unidentified message/passing to sealedSenderDecryptMessage`
);
const sealedSenderIdentifier = envelope.sourceUuid || envelope.source; const sealedSenderIdentifier = envelope.sourceUuid || envelope.source;
const address = `${sealedSenderIdentifier}.${envelope.sourceDevice}`; const address = `${sealedSenderIdentifier}.${envelope.sourceDevice}`;
return window.textsecure.storage.protocol.enqueueSessionJob( return window.textsecure.storage.protocol.enqueueSessionJob(
@ -1725,8 +1739,8 @@ class MessageReceiverInner extends EventTarget {
envelope: EnvelopeClass, envelope: EnvelopeClass,
decryptionError: ByteBufferClass decryptionError: ByteBufferClass
) { ) {
const envelopeId = this.getEnvelopeId(envelope); const logId = this.getEnvelopeId(envelope);
window.log.info(`handleDecryptionError: ${envelopeId}`); window.log.info(`handleDecryptionError: ${logId}`);
const buffer = Buffer.from(decryptionError.toArrayBuffer()); const buffer = Buffer.from(decryptionError.toArrayBuffer());
const request = DecryptionErrorMessage.deserialize(buffer); const request = DecryptionErrorMessage.deserialize(buffer);
@ -1735,7 +1749,9 @@ class MessageReceiverInner extends EventTarget {
const { sourceUuid, sourceDevice } = envelope; const { sourceUuid, sourceDevice } = envelope;
if (!sourceUuid || !sourceDevice) { if (!sourceUuid || !sourceDevice) {
window.log.error('handleDecryptionError: Missing uuid or device!'); window.log.error(
`handleDecryptionError/${logId}: Missing uuid or device!`
);
return; return;
} }
@ -1754,7 +1770,7 @@ class MessageReceiverInner extends EventTarget {
distributionMessage: ByteBufferClass distributionMessage: ByteBufferClass
): Promise<void> { ): Promise<void> {
const envelopeId = this.getEnvelopeId(envelope); const envelopeId = this.getEnvelopeId(envelope);
window.log.info(`handleSenderKeyDistributionMessage: ${envelopeId}`); window.log.info(`handleSenderKeyDistributionMessage/${envelopeId}`);
// Note: we don't call removeFromCache here because this message can be combined // Note: we don't call removeFromCache here because this message can be combined
// with a dataMessage, for example. That processing will dictate cache removal. // with a dataMessage, for example. That processing will dictate cache removal.

View file

@ -1444,7 +1444,7 @@ export function initialize({
const capabilities: CapabilitiesUploadType = { const capabilities: CapabilitiesUploadType = {
'gv2-3': true, 'gv2-3': true,
'gv1-migration': true, 'gv1-migration': true,
senderKey: true, senderKey: false,
}; };
const { accessKey } = options; const { accessKey } = options;

View file

@ -111,7 +111,13 @@ export async function sendContentMessageToGroup({
'sendContentMessageToGroup: textsecure.messaging not available!' 'sendContentMessageToGroup: textsecure.messaging not available!'
); );
if (conversation.isGroupV2()) { const ourConversationId = window.ConversationController.getOurConversationIdOrThrow();
const ourConversation = window.ConversationController.get(ourConversationId);
if (
ourConversation?.get('capabilities')?.senderKey &&
conversation.isGroupV2()
) {
try { try {
return await sendToGroupViaSenderKey({ return await sendToGroupViaSenderKey({
contentHint, contentHint,