Ensure sender info propagates after sealed sender decrypt error
This commit is contained in:
parent
668a4a5402
commit
f7c85432a5
1 changed files with 182 additions and 82 deletions
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue