Validate plaintext hashes for archived attachments.

This commit is contained in:
Greyson Parrelli
2025-06-20 15:26:23 -04:00
committed by Cody Henthorne
parent 38c8f852bf
commit 607b83d65b
21 changed files with 470 additions and 436 deletions

View File

@@ -1,77 +0,0 @@
package org.thoughtcrime.securesms.mms;
import androidx.annotation.NonNull;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.data.DataFetcher;
import org.signal.core.util.logging.Log;
import org.signal.libsignal.protocol.InvalidMessageException;
import org.whispersystems.signalservice.api.crypto.AttachmentCipherInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
class AttachmentStreamLocalUriFetcher implements DataFetcher<InputStream> {
private static final String TAG = Log.tag(AttachmentStreamLocalUriFetcher.class);
private final File attachment;
private final byte[] key;
private final Optional<byte[]> digest;
private final long plaintextLength;
private InputStream is;
AttachmentStreamLocalUriFetcher(File attachment, long plaintextLength, byte[] key, Optional<byte[]> digest) {
this.attachment = attachment;
this.plaintextLength = plaintextLength;
this.digest = digest;
this.key = key;
}
@Override
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
try {
if (!digest.isPresent()) throw new InvalidMessageException("No attachment digest!");
is = AttachmentCipherInputStream.createForAttachment(attachment,
plaintextLength,
key,
digest.get(),
null,
0);
callback.onDataReady(is);
} catch (IOException | InvalidMessageException e) {
callback.onLoadFailed(e);
}
}
@Override
public void cleanup() {
try {
if (is != null) is.close();
is = null;
} catch (IOException ioe) {
Log.w(TAG, "ioe");
}
}
@Override
public void cancel() {}
@Override
public @NonNull Class<InputStream> getDataClass() {
return InputStream.class;
}
@Override
public @NonNull DataSource getDataSource() {
return DataSource.LOCAL;
}
}

View File

@@ -1,89 +0,0 @@
package org.thoughtcrime.securesms.mms;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.bumptech.glide.load.Key;
import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.model.ModelLoaderFactory;
import com.bumptech.glide.load.model.MultiModelLoaderFactory;
import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel;
import java.io.File;
import java.io.InputStream;
import java.security.MessageDigest;
import java.util.Optional;
public class AttachmentStreamUriLoader implements ModelLoader<AttachmentModel, InputStream> {
@Override
public @Nullable LoadData<InputStream> buildLoadData(@NonNull AttachmentModel attachmentModel, int width, int height, @NonNull Options options) {
return new LoadData<>(attachmentModel, new AttachmentStreamLocalUriFetcher(attachmentModel.attachment, attachmentModel.plaintextLength, attachmentModel.key, attachmentModel.digest));
}
@Override
public boolean handles(@NonNull AttachmentModel attachmentModel) {
return true;
}
static class Factory implements ModelLoaderFactory<AttachmentModel, InputStream> {
@Override
public @NonNull ModelLoader<AttachmentModel, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
return new AttachmentStreamUriLoader();
}
@Override
public void teardown() {
// Do nothing.
}
}
public static class AttachmentModel implements Key {
public @NonNull File attachment;
public @NonNull byte[] key;
public @NonNull Optional<byte[]> digest;
public @NonNull Optional<byte[]> incrementalDigest;
public int incrementalMacChunkSize;
public long plaintextLength;
public AttachmentModel(@NonNull File attachment,
@NonNull byte[] key,
long plaintextLength,
@NonNull Optional<byte[]> digest,
@NonNull Optional<byte[]> incrementalDigest,
int incrementalMacChunkSize)
{
this.attachment = attachment;
this.key = key;
this.digest = digest;
this.incrementalDigest = incrementalDigest;
this.incrementalMacChunkSize = incrementalMacChunkSize;
this.plaintextLength = plaintextLength;
}
@Override
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
messageDigest.update(attachment.toString().getBytes());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AttachmentModel that = (AttachmentModel)o;
return attachment.equals(that.attachment);
}
@Override
public int hashCode() {
return attachment.hashCode();
}
}
}

View File

@@ -41,7 +41,6 @@ import org.thoughtcrime.securesms.glide.cache.EncryptedCacheDecoder;
import org.thoughtcrime.securesms.glide.cache.EncryptedCacheEncoder;
import org.thoughtcrime.securesms.glide.cache.EncryptedGifDrawableResourceEncoder;
import org.thoughtcrime.securesms.glide.cache.WebpSanDecoder;
import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
import org.thoughtcrime.securesms.stickers.StickerRemoteUri;
import org.thoughtcrime.securesms.stickers.StickerRemoteUriLoader;
@@ -96,7 +95,6 @@ public class SignalGlideComponents implements RegisterGlideComponents {
registry.append(ConversationShortcutPhoto.class, Bitmap.class, new ConversationShortcutPhoto.Loader.Factory(context));
registry.append(ContactPhoto.class, InputStream.class, new ContactPhotoLoader.Factory(context));
registry.append(DecryptableUri.class, InputStream.class, new DecryptableStreamUriLoader.Factory(context));
registry.append(AttachmentModel.class, InputStream.class, new AttachmentStreamUriLoader.Factory());
registry.append(ChunkedImageUrl.class, InputStream.class, new ChunkedImageUrlLoader.Factory());
registry.append(StickerRemoteUri.class, InputStream.class, new StickerRemoteUriLoader.Factory());
registry.append(BlurHash.class, BlurHash.class, new BlurHashModelLoader.Factory());