0
0
mirror of https://github.com/signalapp/libsignal.git synced 2024-09-20 03:52:17 +02:00

SealedSessionCipher decrypt

This commit is contained in:
Jack Lloyd 2020-11-18 17:17:53 -05:00
parent 405d071ead
commit 786b9b5792
11 changed files with 264 additions and 150 deletions

View File

@ -136,6 +136,7 @@ public final class Native {
public static native boolean ScannableFingerprint_Compare(byte[] fprint1, byte[] fprint2);
public static native long SealedSessionCipher_DecryptToUsmc(byte[] ctext, long trustRoot, long timestamp, IdentityKeyStore identityStore);
public static native byte[] SealedSessionCipher_Encrypt(long destination, long senderCert, byte[] ptext, SessionStore sessionStore, IdentityKeyStore identityStore);
public static native long SenderCertificate_Deserialize(byte[] data);
@ -149,6 +150,7 @@ public final class Native {
public static native byte[] SenderCertificate_GetSerialized(long handle);
public static native long SenderCertificate_GetServerCertificate(long handle);
public static native byte[] SenderCertificate_GetSignature(long handle);
public static native long SenderCertificate_PreferredAddress(long cert, SessionStore sessionStore);
public static native boolean SenderCertificate_Validate(long cert, long key, long time);
public static native long SenderKeyDistributionMessage_Deserialize(byte[] data);

View File

@ -1,6 +1,5 @@
package org.signal.libsignal.metadata;
import org.signal.libsignal.metadata.certificate.CertificateValidator;
import org.signal.libsignal.metadata.certificate.InvalidCertificateException;
import org.signal.libsignal.metadata.certificate.SenderCertificate;
@ -18,34 +17,16 @@ import org.whispersystems.libsignal.NoSessionException;
import org.whispersystems.libsignal.SessionCipher;
import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.UntrustedIdentityException;
import org.whispersystems.libsignal.ecc.Curve;
import org.whispersystems.libsignal.ecc.ECKeyPair;
import org.whispersystems.libsignal.ecc.ECPrivateKey;
import org.whispersystems.libsignal.ecc.ECPublicKey;
import org.whispersystems.libsignal.kdf.HKDFv3;
import org.whispersystems.libsignal.protocol.CiphertextMessage;
import org.whispersystems.libsignal.protocol.PreKeySignalMessage;
import org.whispersystems.libsignal.protocol.SignalMessage;
import org.whispersystems.libsignal.state.SignalProtocolStore;
import org.whispersystems.libsignal.util.ByteUtil;
import org.whispersystems.libsignal.util.guava.Optional;
import org.signal.client.internal.Native;
import java.security.InvalidAlgorithmParameterException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.util.UUID;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class SealedSessionCipher {
private static final String TAG = SealedSessionCipher.class.getSimpleName();
@ -87,36 +68,23 @@ public class SealedSessionCipher {
SelfSendException
{
UnidentifiedSenderMessageContent content;
try {
IdentityKeyPair ourIdentity = signalProtocolStore.getIdentityKeyPair();
UnidentifiedSenderMessage wrapper = new UnidentifiedSenderMessage(ciphertext);
byte[] ephemeralSalt = ByteUtil.combine("UnidentifiedDelivery".getBytes(), ourIdentity.getPublicKey().getPublicKey().serialize(), wrapper.getEphemeral().serialize());
EphemeralKeys ephemeralKeys = calculateEphemeralKeys(wrapper.getEphemeral(), ourIdentity.getPrivateKey(), ephemeralSalt);
byte[] staticKeyBytes = decrypt(ephemeralKeys.cipherKey, ephemeralKeys.macKey, wrapper.getEncryptedStatic());
ECPublicKey staticKey = Curve.decodePoint(staticKeyBytes, 0);
byte[] staticSalt = ByteUtil.combine(ephemeralKeys.chainKey, wrapper.getEncryptedStatic());
StaticKeys staticKeys = calculateStaticKeys(staticKey, ourIdentity.getPrivateKey(), staticSalt);
byte[] messageBytes = decrypt(staticKeys.cipherKey, staticKeys.macKey, wrapper.getEncryptedMessage());
content = new UnidentifiedSenderMessageContent(messageBytes);
validator.validate(content.getSenderCertificate(), timestamp);
if (!MessageDigest.isEqual(content.getSenderCertificate().getKey().serialize(), staticKeyBytes)) {
throw new InvalidKeyException("Sender's certificate key does not match key used in message");
}
boolean isLocalE164 = localE164Address != null && localE164Address.equals(content.getSenderCertificate().getSenderE164().orNull());
boolean isLocalUuid = localUuidAddress != null && localUuidAddress.equals(content.getSenderCertificate().getSenderUuid().orNull());
if ((isLocalE164 || isLocalUuid) && content.getSenderCertificate().getSenderDeviceId() == localDeviceId) {
throw new SelfSendException();
}
} catch (InvalidKeyException | InvalidMacException | InvalidCertificateException e) {
content = new UnidentifiedSenderMessageContent(
Native.SealedSessionCipher_DecryptToUsmc(ciphertext,
validator.getTrustRoot().nativeHandle(),
timestamp,
this.signalProtocolStore));
} catch (Exception e) {
throw new InvalidMetadataMessageException(e);
}
boolean isLocalE164 = localE164Address != null && localE164Address.equals(content.getSenderCertificate().getSenderE164().orNull());
boolean isLocalUuid = localUuidAddress != null && localUuidAddress.equals(content.getSenderCertificate().getSenderUuid().orNull());
if ((isLocalE164 || isLocalUuid) && content.getSenderCertificate().getSenderDeviceId() == localDeviceId) {
throw new SelfSendException();
}
try {
return new DecryptionResult(content.getSenderCertificate().getSenderUuid(),
content.getSenderCertificate().getSenderE164(),
@ -149,34 +117,10 @@ public class SealedSessionCipher {
return new SessionCipher(signalProtocolStore, remoteAddress).getRemoteRegistrationId();
}
private EphemeralKeys calculateEphemeralKeys(ECPublicKey ephemeralPublic, ECPrivateKey ephemeralPrivate, byte[] salt) throws InvalidKeyException {
try {
byte[] ephemeralSecret = Curve.calculateAgreement(ephemeralPublic, ephemeralPrivate);
byte[] ephemeralDerived = new HKDFv3().deriveSecrets(ephemeralSecret, salt, null, 96);
byte[][] ephemeralDerivedParts = ByteUtil.split(ephemeralDerived, 32, 32, 32);
return new EphemeralKeys(ephemeralDerivedParts[0], ephemeralDerivedParts[1], ephemeralDerivedParts[2]);
} catch (ParseException e) {
throw new AssertionError(e);
}
}
private StaticKeys calculateStaticKeys(ECPublicKey staticPublic, ECPrivateKey staticPrivate, byte[] salt) throws InvalidKeyException {
try {
byte[] staticSecret = Curve.calculateAgreement(staticPublic, staticPrivate);
byte[] staticDerived = new HKDFv3().deriveSecrets(staticSecret, salt, null, 96);
byte[][] staticDerivedParts = ByteUtil.split(staticDerived, 32, 32, 32);
return new StaticKeys(staticDerivedParts[1], staticDerivedParts[2]);
} catch (ParseException e) {
throw new AssertionError(e);
}
}
private byte[] decrypt(UnidentifiedSenderMessageContent message)
throws InvalidVersionException, InvalidMessageException, InvalidKeyException, DuplicateMessageException, InvalidKeyIdException, UntrustedIdentityException, LegacyMessageException, NoSessionException
{
SignalProtocolAddress sender = getPreferredAddress(signalProtocolStore, message.getSenderCertificate());
SignalProtocolAddress sender = new SignalProtocolAddress(Native.SenderCertificate_PreferredAddress(message.getSenderCertificate().nativeHandle(), signalProtocolStore));
switch (message.getType()) {
case CiphertextMessage.WHISPER_TYPE: return new SessionCipher(signalProtocolStore, sender).decrypt(new SignalMessage(message.getContent()));
@ -185,47 +129,6 @@ public class SealedSessionCipher {
}
}
private byte[] decrypt(SecretKeySpec cipherKey, SecretKeySpec macKey, byte[] ciphertext) throws InvalidMacException {
try {
if (ciphertext.length < 10) {
throw new InvalidMacException("Ciphertext not long enough for MAC!");
}
byte[][] ciphertextParts = ByteUtil.split(ciphertext, ciphertext.length - 10, 10);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(macKey);
byte[] digest = mac.doFinal(ciphertextParts[0]);
byte[] ourMac = ByteUtil.trim(digest, 10);
byte[] theirMac = ciphertextParts[1];
if (!MessageDigest.isEqual(ourMac, theirMac)) {
throw new InvalidMacException("Bad mac!");
}
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, cipherKey, new IvParameterSpec(new byte[16]));
return cipher.doFinal(ciphertextParts[0]);
} catch (NoSuchAlgorithmException | java.security.InvalidKeyException | NoSuchPaddingException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
throw new AssertionError(e);
}
}
private static SignalProtocolAddress getPreferredAddress(SignalProtocolStore store, SenderCertificate certificate) {
SignalProtocolAddress uuidAddress = certificate.getSenderUuid().isPresent() ? new SignalProtocolAddress(certificate.getSenderUuid().get(), certificate.getSenderDeviceId()) : null;
SignalProtocolAddress e164Address = certificate.getSenderE164().isPresent() ? new SignalProtocolAddress(certificate.getSenderE164().get(), certificate.getSenderDeviceId()) : null;
if (uuidAddress != null && store.containsSession(uuidAddress)) {
return uuidAddress;
} else if (e164Address != null && store.containsSession(e164Address)) {
return e164Address;
} else {
return new SignalProtocolAddress(certificate.getSender(), certificate.getSenderDeviceId());
}
}
public static class DecryptionResult {
private final Optional<String> senderUuid;
private final Optional<String> senderE164;
@ -255,27 +158,4 @@ public class SealedSessionCipher {
return paddedMessage;
}
}
private static class EphemeralKeys {
private final byte[] chainKey;
private final SecretKeySpec cipherKey;
private final SecretKeySpec macKey;
private EphemeralKeys(byte[] chainKey, byte[] cipherKey, byte[] macKey) {
this.chainKey = chainKey;
this.cipherKey = new SecretKeySpec(cipherKey, "AES");
this.macKey = new SecretKeySpec(macKey, "HmacSHA256");
}
}
private static class StaticKeys {
private final SecretKeySpec cipherKey;
private final SecretKeySpec macKey;
private StaticKeys(byte[] cipherKey, byte[] macKey) {
this.cipherKey = new SecretKeySpec(cipherKey, "AES");
this.macKey = new SecretKeySpec(macKey, "HmacSHA256");
}
}
}

View File

@ -13,6 +13,10 @@ public class CertificateValidator {
this.trustRoot = trustRoot;
}
public ECPublicKey getTrustRoot() {
return this.trustRoot;
}
public void validate(SenderCertificate certificate, long validationTime) throws InvalidCertificateException {
try {
if (!Native.SenderCertificate_Validate(certificate.nativeHandle(), trustRoot.nativeHandle(), validationTime)) {

View File

@ -14,6 +14,10 @@ public class UnidentifiedSenderMessageContent {
Native.UnidentifiedSenderMessageContent_Destroy(this.handle);
}
public UnidentifiedSenderMessageContent(long nativeHandle) {
this.handle = nativeHandle;
}
public UnidentifiedSenderMessageContent(byte[] serialized) throws InvalidMetadataMessageException, InvalidCertificateException {
try {
this.handle = Native.UnidentifiedSenderMessageContent_Deserialize(serialized);

View File

@ -14,6 +14,10 @@ public class SignalProtocolAddress {
this.handle = Native.ProtocolAddress_New(name, deviceId);
}
public SignalProtocolAddress(long handle) {
this.handle = handle;
}
@Override
protected void finalize() {
Native.ProtocolAddress_Destroy(this.handle);

View File

@ -1687,6 +1687,23 @@ jni_fn_get_new_boxed_obj!(Java_org_signal_client_internal_Native_SenderCertifica
jni_fn_get_optional_jstring!(Java_org_signal_client_internal_Native_SenderCertificate_1GetSenderUuid(SenderCertificate) using SenderCertificate::sender_uuid);
jni_fn_get_optional_jstring!(Java_org_signal_client_internal_Native_SenderCertificate_1GetSenderE164(SenderCertificate) using SenderCertificate::sender_e164);
#[no_mangle]
pub unsafe extern "C" fn Java_org_signal_client_internal_Native_SenderCertificate_1PreferredAddress(
env: JNIEnv,
_class: JClass,
cert: ObjectHandle,
session_store: JavaSessionStore,
) -> ObjectHandle {
run_ffi_safe(&env, || {
let cert = native_handle_cast::<SenderCertificate>(cert)?;
let session_store = JniSessionStore::new(&env, session_store)?;
let address = expect_ready(cert.preferred_address(&session_store, None))?;
box_object::<ProtocolAddress>(Ok(address))
})
}
#[no_mangle]
pub unsafe extern "C" fn Java_org_signal_client_internal_Native_SenderCertificate_1Validate(
env: JNIEnv,
@ -1792,7 +1809,7 @@ pub unsafe extern "C" fn Java_org_signal_client_internal_Native_SealedSessionCip
let mut rng = rand::rngs::OsRng;
let ctext = block_on(sealed_sender_encrypt(
let ctext = expect_ready(sealed_sender_encrypt(
destination,
sender_cert,
&ptext,
@ -1804,3 +1821,30 @@ pub unsafe extern "C" fn Java_org_signal_client_internal_Native_SealedSessionCip
to_jbytearray(&env, Ok(ctext))
})
}
#[no_mangle]
pub unsafe extern "C" fn Java_org_signal_client_internal_Native_SealedSessionCipher_1DecryptToUsmc(
env: JNIEnv,
_class: JClass,
ctext: jbyteArray,
trust_root: ObjectHandle,
timestamp: jlong,
identity_store: JavaIdentityKeyStore,
) -> ObjectHandle {
run_ffi_safe(&env, || {
let ctext = env.convert_byte_array(ctext)?;
let trust_root = native_handle_cast::<PublicKey>(trust_root)?;
let timestamp = jlong_to_u64(timestamp)?;
let mut identity_store = JniIdentityKeyStore::new(&env, identity_store)?;
let usmc = expect_ready(sealed_sender_decrypt_to_usmc(
&ctext,
trust_root,
timestamp,
&mut identity_store,
None,
))?;
box_object::<UnidentifiedSenderMessageContent>(Ok(usmc))
})
}

View File

@ -142,6 +142,10 @@ pub fn throw_error(env: &JNIEnv, error: SignalJniError) {
"java/lang/IllegalStateException"
}
SignalJniError::Signal(SignalProtocolError::SealedSenderSelfSend) => {
"org/signal/libsignal/metadata/SelfSendException"
}
SignalJniError::Signal(SignalProtocolError::InvalidArgument(_)) => {
"java/lang/IllegalArgumentException"
}
@ -322,6 +326,16 @@ pub fn jlong_from_u64(value: Result<u64, SignalProtocolError>) -> Result<jlong,
}
}
/*
pub fn convert_optional_jstring(env: &JNIEnv, value: JString) -> Result<Option<String>, SignalJniError> {
if value.is_null() {
return Ok(None)
}
let rstr = env.get_string(value)?.into();
Ok(Some(rstr))
}
*/
pub fn exception_check(env: &JNIEnv, fn_name: &'static str) -> Result<(), SignalJniError> {
fn exception_class_name(env: &JNIEnv, exn: JThrowable) -> Result<String, SignalJniError> {
let class_type = env.call_method(exn, "getClass", "()Ljava/lang/Class;", &[])?;

View File

@ -11,6 +11,7 @@ use block_modes::{block_padding::Pkcs7, BlockMode, Cbc};
use ctr::Ctr128;
use hmac::{Hmac, Mac, NewMac};
use sha2::Sha256;
use subtle::ConstantTimeEq;
pub fn aes_256_ctr_encrypt(ptext: &[u8], key: &[u8]) -> Result<Vec<u8>> {
if key.len() != 32 {
@ -66,6 +67,28 @@ pub fn hmac_sha256(key: &[u8], input: &[u8]) -> Result<[u8; 32]> {
Ok(hmac.finalize().into_bytes().into())
}
pub fn aes256_ctr_hmacsha256_encrypt(msg: &[u8], cipher_key: &[u8], mac_key: &[u8]) -> Result<Vec<u8>> {
let ctext = aes_256_ctr_encrypt(msg, cipher_key)?;
let mac = hmac_sha256(mac_key, &ctext)?;
let mut result = Vec::with_capacity(ctext.len() + 10);
result.extend_from_slice(&ctext);
result.extend_from_slice(&mac[..10]);
Ok(result)
}
pub fn aes256_ctr_hmacsha256_decrypt(ctext: &[u8], cipher_key: &[u8], mac_key: &[u8]) -> Result<Vec<u8>> {
if ctext.len() < 10 {
return Err(SignalProtocolError::InvalidCiphertext);
}
let ptext_len = ctext.len() - 10;
let our_mac = hmac_sha256(mac_key, &ctext[..ptext_len])?;
let same : bool = our_mac[..10].ct_eq(&ctext[ptext_len..]).into();
if !same {
return Err(SignalProtocolError::InvalidCiphertext);
}
aes_256_ctr_decrypt(&ctext[..ptext_len], cipher_key)
}
#[cfg(test)]
mod test {

View File

@ -66,6 +66,7 @@ pub enum SignalProtocolError {
InvalidSealedSenderMessage(String),
UnknownSealedSenderVersion(u8),
SealedSenderSelfSend,
}
impl Error for SignalProtocolError {
@ -202,6 +203,9 @@ impl fmt::Display for SignalProtocolError {
SignalProtocolError::UnknownSealedSenderVersion(v) => {
write!(f, "unknown sealed sender message version {}", v)
}
SignalProtocolError::SealedSenderSelfSend => {
write!(f, "self send of a sealed sender message")
}
}
}
}

View File

@ -46,8 +46,9 @@ pub use {
AliceSignalProtocolParameters, BobSignalProtocolParameters, ChainKey, MessageKeys, RootKey,
},
sealed_sender::{
sealed_sender_encrypt, SenderCertificate, ServerCertificate, UnidentifiedSenderMessage,
UnidentifiedSenderMessageContent,
sealed_sender_encrypt, sealed_sender_decrypt, sealed_sender_decrypt_to_usmc,
SenderCertificate, ServerCertificate, UnidentifiedSenderMessage,
UnidentifiedSenderMessageContent, SealedSenderDecryptionResult,
},
sender_keys::{
SenderChainKey, SenderKeyName, SenderKeyRecord, SenderKeyState, SenderMessageKey,

View File

@ -7,12 +7,15 @@ use crate::crypto;
use crate::error::{Result, SignalProtocolError};
use crate::kdf::HKDF;
use crate::proto;
use crate::session_cipher;
use crate::{
message_encrypt, Context, IdentityKeyStore, KeyPair, PrivateKey, ProtocolAddress, PublicKey,
SessionStore,
SessionStore, PreKeyStore, SignedPreKeyStore, SignalMessage, PreKeySignalMessage,
};
use prost::Message;
use rand::{CryptoRng, Rng};
use subtle::ConstantTimeEq;
use std::convert::TryFrom;
#[derive(Debug, Clone)]
pub struct ServerCertificate {
@ -292,6 +295,33 @@ impl SenderCertificate {
pub fn signature(&self) -> Result<&[u8]> {
Ok(&self.signature)
}
pub async fn preferred_address(&self, session_store: &dyn SessionStore, ctx: Context) -> Result<ProtocolAddress> {
if let Some(uuid) = self.sender_uuid()? {
let uuid_address = ProtocolAddress::new(uuid.to_owned(), self.sender_device_id()?);
if session_store.load_session(&uuid_address, ctx).await?.is_some() {
return Ok(uuid_address);
}
}
if let Some(e164) = self.sender_e164()? {
let e164_address = ProtocolAddress::new(e164.to_owned(), self.sender_device_id()?);
if session_store.load_session(&e164_address, ctx).await?.is_some() {
return Ok(e164_address);
}
}
/*
* This logic of preferring e164 over uuid comes from Java, but seems incorrect.
*/
let best_address = match (self.sender_e164()?, self.sender_uuid()?) {
(Some(e164),_) => e164.to_owned(),
(None,Some(uuid)) => uuid.to_owned(),
(None,None) => return Err(SignalProtocolError::InvalidSealedSenderMessage("No sender in sender cert".to_owned())),
};
Ok(ProtocolAddress::new(best_address, self.sender_device_id()?))
}
}
pub struct UnidentifiedSenderMessageContent {
@ -382,7 +412,7 @@ const SEALED_SENDER_VERSION: u8 = 1;
impl UnidentifiedSenderMessage {
pub fn deserialize(data: &[u8]) -> Result<Self> {
if data.len() == 0 {
if data.is_empty() {
return Err(SignalProtocolError::InvalidSealedSenderMessage(
"Message was empty".to_owned(),
));
@ -464,15 +494,6 @@ impl UnidentifiedSenderMessage {
}
}
fn aes256_ctr_hmacsha256_encrypt(msg: &[u8], cipher_key: &[u8], mac_key: &[u8]) -> Result<Vec<u8>> {
let ctext = crypto::aes_256_ctr_encrypt(msg, cipher_key)?;
let mac = crypto::hmac_sha256(mac_key, &ctext)?;
let mut result = Vec::with_capacity(ctext.len() + 10);
result.extend_from_slice(&ctext);
result.extend_from_slice(&mac[..10]);
Ok(result)
}
struct EphemeralKeys {
derived_values: Box<[u8]>,
}
@ -571,7 +592,7 @@ pub async fn sealed_sender_encrypt<R: Rng + CryptoRng>(
true,
)?;
let static_key_ctext = aes256_ctr_hmacsha256_encrypt(
let static_key_ctext = crypto::aes256_ctr_hmacsha256_encrypt(
&our_identity.public_key().serialize(),
&eph_keys.cipher_key()?,
&eph_keys.mac_key()?,
@ -589,7 +610,7 @@ pub async fn sealed_sender_encrypt<R: Rng + CryptoRng>(
sender_cert.clone(),
message.serialize().to_vec(),
)?;
let message_data = aes256_ctr_hmacsha256_encrypt(
let message_data = crypto::aes256_ctr_hmacsha256_encrypt(
usmc.serialized()?,
&static_keys.cipher_key()?,
&static_keys.mac_key()?,
@ -601,3 +622,116 @@ pub async fn sealed_sender_encrypt<R: Rng + CryptoRng>(
.to_vec(),
)
}
pub async fn sealed_sender_decrypt_to_usmc(ciphertext: &[u8],
trust_root: &PublicKey,
timestamp: u64,
identity_store: &mut dyn IdentityKeyStore,
ctx: Context) -> Result<UnidentifiedSenderMessageContent> {
let our_identity = identity_store.get_identity_key_pair(ctx).await?;
let usm = UnidentifiedSenderMessage::deserialize(ciphertext)?;
let eph_keys = EphemeralKeys::calculate(
&usm.ephemeral_public()?,
&our_identity.public_key(),
&our_identity.private_key(),
false,
)?;
let static_key_bytes = crypto::aes256_ctr_hmacsha256_decrypt(usm.encrypted_static()?, &eph_keys.cipher_key()?, &eph_keys.mac_key()?)?;
let static_key = PublicKey::deserialize(&static_key_bytes)?;
let static_keys = StaticKeys::calculate(
&static_key,
our_identity.private_key(),
eph_keys.chain_key()?,
usm.encrypted_static()?,
)?;
let message_bytes = crypto::aes256_ctr_hmacsha256_decrypt(usm.encrypted_message()?, &static_keys.cipher_key()?, &static_keys.mac_key()?)?;
let usmc = UnidentifiedSenderMessageContent::deserialize(&message_bytes)?;
if !usmc.sender()?.validate(trust_root, timestamp)? {
return Err(SignalProtocolError::InvalidSealedSenderMessage("trust root validation failed".to_string()));
}
if ! bool::from(static_key_bytes.ct_eq(&usmc.sender()?.key()?.serialize())) {
return Err(SignalProtocolError::InvalidSealedSenderMessage("sender certificate key does not match message key".to_string()));
}
Ok(usmc)
}
pub struct SealedSenderDecryptionResult {
pub sender_uuid: Option<String>,
pub sender_e164: Option<String>,
pub device_id: u32,
pub message: Vec<u8>
}
pub async fn sealed_sender_decrypt(ciphertext: &[u8],
trust_root: &PublicKey,
timestamp: u64,
local_e164: Option<String>,
local_uuid: Option<String>,
local_device_id: u32,
identity_store: &mut dyn IdentityKeyStore,
session_store: &mut dyn SessionStore,
pre_key_store: &mut dyn PreKeyStore,
signed_pre_key_store: &mut dyn SignedPreKeyStore,
ctx: Context) -> Result<SealedSenderDecryptionResult> {
let usmc = sealed_sender_decrypt_to_usmc(
ciphertext, trust_root, timestamp, identity_store, ctx).await?;
let is_local_e164 = match (local_e164, usmc.sender()?.sender_e164()?) {
(Some(l),Some(s)) => l == s,
(_,_) => false
};
let is_local_uuid = match (local_uuid, usmc.sender()?.sender_uuid()?) {
(Some(l),Some(s)) => l == s,
(_,_) => false
};
if (is_local_e164 || is_local_uuid) && usmc.sender()?.sender_device_id()? == local_device_id {
return Err(SignalProtocolError::SealedSenderSelfSend);
}
let mut rng = rand::rngs::OsRng;
let remote_address = usmc.sender()?.preferred_address(session_store, ctx).await?;
let message = match usmc.msg_type()? {
2 => {
let ctext = SignalMessage::try_from(usmc.contents()?)?;
session_cipher::message_decrypt_signal(&ctext,
&remote_address,
session_store,
identity_store,
&mut rng,
ctx).await?
}
3 => {
let ctext = PreKeySignalMessage::try_from(usmc.contents()?)?;
session_cipher::message_decrypt_prekey(&ctext,
&remote_address,
session_store,
identity_store,
pre_key_store,
signed_pre_key_store,
&mut rng,
ctx).await?
}
_ => return Err(SignalProtocolError::InvalidSealedSenderMessage("Unknown message type".to_owned()))
};
Ok(SealedSenderDecryptionResult {
sender_uuid: usmc.sender()?.sender_uuid()?.map(|s| s.to_string()),
sender_e164: usmc.sender()?.sender_e164()?.map(|s| s.to_string()),
device_id: usmc.sender()?.sender_device_id()?,
message,
})
}