Ensure sender info propagates after sealed sender decrypt error

This commit is contained in:
Scott Nonnenberg 2021-07-15 12:13:48 -07:00 committed by GitHub
parent 668a4a5402
commit f7c85432a5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -39,6 +39,7 @@ import {
Sessions, Sessions,
SignedPreKeys, SignedPreKeys,
} from '../LibSignalStores'; } from '../LibSignalStores';
import { verifySignature } from '../Curve';
import { BackOff, FIBONACCI_TIMEOUTS } from '../util/BackOff'; import { BackOff, FIBONACCI_TIMEOUTS } from '../util/BackOff';
import { assert, strictAssert } from '../util/assert'; import { assert, strictAssert } from '../util/assert';
import { BatcherType, createBatcher } from '../util/batcher'; import { BatcherType, createBatcher } from '../util/batcher';
@ -1147,7 +1148,10 @@ class MessageReceiverInner extends EventTarget {
{ sessionStore, identityKeyStore, zone }: LockedStores, { sessionStore, identityKeyStore, zone }: LockedStores,
envelope: ProcessedEnvelope, envelope: ProcessedEnvelope,
ciphertext: Uint8Array ciphertext: Uint8Array
): Promise<DecryptSealedSenderResult> { ): Promise<{
error?: Error;
result: DecryptSealedSenderResult;
}> {
const buffer = Buffer.from(ciphertext); const buffer = Buffer.from(ciphertext);
const localE164 = window.textsecure.storage.user.getNumber(); const localE164 = window.textsecure.storage.user.getNumber();
@ -1200,7 +1204,9 @@ class MessageReceiverInner extends EventTarget {
'MessageReceiver.decryptSealedSender: Dropping blocked message after ' + 'MessageReceiver.decryptSealedSender: Dropping blocked message after ' +
'partial sealed sender decryption' 'partial sealed sender decryption'
); );
return { isBlocked: true, envelope: newEnvelope }; return {
result: { isBlocked: true, envelope: newEnvelope },
};
} }
if (!newEnvelope.serverTimestamp) { if (!newEnvelope.serverTimestamp) {
@ -1210,6 +1216,39 @@ class MessageReceiverInner extends EventTarget {
); );
} }
const serverCertificate = certificate.serverCertificate();
if (
!verifySignature(
typedArrayToArrayBuffer(this.serverTrustRoot),
typedArrayToArrayBuffer(serverCertificate.certificateData()),
typedArrayToArrayBuffer(serverCertificate.signature())
)
) {
throw new Error(
'MessageReceiver.decryptSealedSender: Server certificate trust root validation failed'
);
}
if (
!verifySignature(
typedArrayToArrayBuffer(serverCertificate.key().serialize()),
typedArrayToArrayBuffer(certificate.certificate()),
typedArrayToArrayBuffer(certificate.signature())
)
) {
throw new Error(
'MessageReceiver.decryptSealedSender: Server certificate server signature validation failed'
);
}
if (newEnvelope.serverTimestamp > certificate.expiration()) {
const envelopeId = this.getEnvelopeId(newEnvelope);
throw new Error(
`MessageReceiver.decryptSealedSender: Sender certificate is expired for envelope ${envelopeId}`
);
}
const unidentifiedSenderTypeEnum = const unidentifiedSenderTypeEnum =
Proto.UnidentifiedSenderMessage.Message.Type; Proto.UnidentifiedSenderMessage.Message.Type;
@ -1224,8 +1263,10 @@ class MessageReceiverInner extends EventTarget {
); );
return { return {
plaintext: plaintextContent.body(), result: {
envelope: newEnvelope, plaintext: plaintextContent.body(),
envelope: newEnvelope,
},
}; };
} }
@ -1241,24 +1282,34 @@ class MessageReceiverInner extends EventTarget {
const address = `${sealedSenderIdentifier}.${sealedSenderSourceDevice}`; const address = `${sealedSenderIdentifier}.${sealedSenderSourceDevice}`;
const plaintext = await window.textsecure.storage.protocol.enqueueSenderKeyJob( try {
address, const plaintext = await window.textsecure.storage.protocol.enqueueSenderKeyJob(
() => address,
groupDecrypt( () =>
ProtocolAddress.new( groupDecrypt(
sealedSenderIdentifier, ProtocolAddress.new(
sealedSenderSourceDevice sealedSenderIdentifier,
sealedSenderSourceDevice
),
senderKeyStore,
messageContent.contents()
), ),
senderKeyStore, zone
messageContent.contents() );
), return {
zone result: {
); plaintext,
envelope: newEnvelope,
return { },
plaintext, };
envelope: newEnvelope, } catch (error) {
}; return {
error,
result: {
envelope: newEnvelope,
},
};
}
} }
window.log.info( window.log.info(
@ -1270,32 +1321,46 @@ class MessageReceiverInner extends EventTarget {
const sealedSenderIdentifier = newEnvelope.sourceUuid || newEnvelope.source; const sealedSenderIdentifier = newEnvelope.sourceUuid || newEnvelope.source;
const address = `${sealedSenderIdentifier}.${newEnvelope.sourceDevice}`; const address = `${sealedSenderIdentifier}.${newEnvelope.sourceDevice}`;
const unsealedPlaintext = await window.textsecure.storage.protocol.enqueueSessionJob( try {
address, const unsealedPlaintext = await window.textsecure.storage.protocol.enqueueSessionJob(
() => address,
sealedSenderDecryptMessage( () =>
buffer, sealedSenderDecryptMessage(
PublicKey.deserialize(Buffer.from(this.serverTrustRoot)), buffer,
newEnvelope.serverTimestamp, PublicKey.deserialize(Buffer.from(this.serverTrustRoot)),
localE164 || null, newEnvelope.serverTimestamp,
localUuid, localE164 || null,
localDeviceId, localUuid,
sessionStore, localDeviceId,
identityKeyStore, sessionStore,
preKeyStore, identityKeyStore,
signedPreKeyStore preKeyStore,
), signedPreKeyStore
zone ),
); zone
);
return { unsealedPlaintext, envelope: newEnvelope }; return {
result: { unsealedPlaintext, envelope: newEnvelope },
};
} catch (error) {
return {
error,
result: {
envelope: newEnvelope,
},
};
}
} }
private async innerDecrypt( private async innerDecrypt(
stores: LockedStores, stores: LockedStores,
envelope: ProcessedEnvelope, envelope: ProcessedEnvelope,
ciphertext: Uint8Array ciphertext: Uint8Array
): Promise<InnerDecryptResult> { ): Promise<{
error?: Error;
result: InnerDecryptResult;
}> {
const { sessionStore, identityKeyStore, zone } = stores; const { sessionStore, identityKeyStore, zone } = stores;
const logId = this.getEnvelopeId(envelope); const logId = this.getEnvelopeId(envelope);
@ -1313,8 +1378,10 @@ class MessageReceiverInner extends EventTarget {
const plaintextContent = PlaintextContent.deserialize(buffer); const plaintextContent = PlaintextContent.deserialize(buffer);
return { return {
plaintext: this.unpad(plaintextContent.body()), result: {
envelope, plaintext: this.unpad(plaintextContent.body()),
envelope,
},
}; };
} }
if (envelope.type === envelopeTypeEnum.CIPHERTEXT) { if (envelope.type === envelopeTypeEnum.CIPHERTEXT) {
@ -1332,21 +1399,24 @@ class MessageReceiverInner extends EventTarget {
const signalMessage = SignalMessage.deserialize(Buffer.from(ciphertext)); const signalMessage = SignalMessage.deserialize(Buffer.from(ciphertext));
const address = `${identifier}.${sourceDevice}`; const address = `${identifier}.${sourceDevice}`;
const plaintext = await window.textsecure.storage.protocol.enqueueSessionJob(
address,
async () =>
this.unpad(
await signalDecrypt(
signalMessage,
ProtocolAddress.new(identifier, sourceDevice),
sessionStore,
identityKeyStore
)
),
zone
);
return { return {
plaintext: await window.textsecure.storage.protocol.enqueueSessionJob( result: {
address, plaintext,
async () => envelope,
this.unpad( },
await signalDecrypt(
signalMessage,
ProtocolAddress.new(identifier, sourceDevice),
sessionStore,
identityKeyStore
)
),
zone
),
envelope,
}; };
} }
if (envelope.type === envelopeTypeEnum.PREKEY_BUNDLE) { if (envelope.type === envelopeTypeEnum.PREKEY_BUNDLE) {
@ -1366,42 +1436,64 @@ class MessageReceiverInner extends EventTarget {
); );
const address = `${identifier}.${sourceDevice}`; const address = `${identifier}.${sourceDevice}`;
const plaintext = await window.textsecure.storage.protocol.enqueueSessionJob(
address,
async () =>
this.unpad(
await signalDecryptPreKey(
preKeySignalMessage,
ProtocolAddress.new(identifier, sourceDevice),
sessionStore,
identityKeyStore,
preKeyStore,
signedPreKeyStore
)
),
zone
);
return { return {
plaintext: await window.textsecure.storage.protocol.enqueueSessionJob( result: {
address, plaintext,
async () => envelope,
this.unpad( },
await signalDecryptPreKey(
preKeySignalMessage,
ProtocolAddress.new(identifier, sourceDevice),
sessionStore,
identityKeyStore,
preKeyStore,
signedPreKeyStore
)
),
zone
),
envelope,
}; };
} }
if (envelope.type === envelopeTypeEnum.UNIDENTIFIED_SENDER) { if (envelope.type === envelopeTypeEnum.UNIDENTIFIED_SENDER) {
window.log.info(`decrypt/${logId}: unidentified message`); window.log.info(`decrypt/${logId}: unidentified message`);
const { result, error } = await this.decryptSealedSender(
stores,
envelope,
ciphertext
);
if (error) {
return {
error,
result,
};
}
const { const {
plaintext, plaintext,
unsealedPlaintext, unsealedPlaintext,
isBlocked, isBlocked,
envelope: newEnvelope, envelope: newEnvelope,
} = await this.decryptSealedSender(stores, envelope, ciphertext); } = result;
if (isBlocked) { if (isBlocked) {
return { isBlocked: true, envelope: newEnvelope }; return {
result: {
isBlocked: true,
envelope: newEnvelope,
},
};
} }
if (plaintext) { if (plaintext) {
return { return {
plaintext: this.unpad(plaintext), result: {
envelope: newEnvelope, plaintext: this.unpad(plaintext),
envelope: newEnvelope,
},
}; };
} }
@ -1417,8 +1509,10 @@ class MessageReceiverInner extends EventTarget {
// Return just the content because that matches the signature of the other // Return just the content because that matches the signature of the other
// decrypt methods used above. // decrypt methods used above.
return { return {
plaintext: this.unpad(content), result: {
envelope: newEnvelope, plaintext: this.unpad(content),
envelope: newEnvelope,
},
}; };
} }
@ -1434,9 +1528,16 @@ class MessageReceiverInner extends EventTarget {
): Promise<DecryptResult> { ): Promise<DecryptResult> {
let newEnvelope: DecryptedEnvelope = envelope; let newEnvelope: DecryptedEnvelope = envelope;
try { try {
const result = await this.innerDecrypt(stores, envelope, ciphertext); const { result, error } = await this.innerDecrypt(
stores,
envelope,
ciphertext
);
newEnvelope = result.envelope; newEnvelope = result.envelope || envelope;
if (error) {
throw error;
}
const { isBlocked, plaintext } = result; const { isBlocked, plaintext } = result;
@ -1449,7 +1550,6 @@ class MessageReceiverInner extends EventTarget {
return { plaintext: new FIXMEU8(plaintext), envelope: newEnvelope }; return { plaintext: new FIXMEU8(plaintext), envelope: newEnvelope };
} catch (error) { } catch (error) {
this.removeFromCache(newEnvelope); this.removeFromCache(newEnvelope);
const uuid = newEnvelope.sourceUuid; const uuid = newEnvelope.sourceUuid;
const deviceId = newEnvelope.sourceDevice; const deviceId = newEnvelope.sourceDevice;