From 95d76638dc43962588231168976ef5a990abae41 Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Sun, 18 Mar 2018 14:52:49 -0700 Subject: [PATCH] Simple encrypted glide disk cache --- .../securesms/components/ThumbnailView.java | 4 +- .../securesms/crypto/AttachmentSecret.java | 2 +- .../securesms/giph/ui/GiphyActivity.java | 7 +- .../securesms/giph/ui/GiphyAdapter.java | 23 +++-- .../glide/GiphyPaddedUrlFetcher.java | 2 +- .../cache/EncryptedBitmapCacheDecoder.java | 54 ++++++++++ .../cache/EncryptedBitmapResourceEncoder.java | 65 ++++++++++++ .../glide/cache/EncryptedCacheEncoder.java | 52 ++++++++++ .../securesms/glide/cache/EncryptedCoder.java | 98 +++++++++++++++++++ .../glide/cache/EncryptedGifCacheDecoder.java | 51 ++++++++++ .../EncryptedGifDrawableResourceEncoder.java | 45 +++++++++ .../securesms/mms/AttachmentManager.java | 2 +- .../securesms/mms/ImageSlide.java | 5 +- .../securesms/mms/SignalGlideModule.java | 27 ++++- .../providers/SingleUseBlobProvider.java | 1 + 15 files changed, 420 insertions(+), 18 deletions(-) create mode 100644 src/org/thoughtcrime/securesms/glide/cache/EncryptedBitmapCacheDecoder.java create mode 100644 src/org/thoughtcrime/securesms/glide/cache/EncryptedBitmapResourceEncoder.java create mode 100644 src/org/thoughtcrime/securesms/glide/cache/EncryptedCacheEncoder.java create mode 100644 src/org/thoughtcrime/securesms/glide/cache/EncryptedCoder.java create mode 100644 src/org/thoughtcrime/securesms/glide/cache/EncryptedGifCacheDecoder.java create mode 100644 src/org/thoughtcrime/securesms/glide/cache/EncryptedGifDrawableResourceEncoder.java diff --git a/src/org/thoughtcrime/securesms/components/ThumbnailView.java b/src/org/thoughtcrime/securesms/components/ThumbnailView.java index 3433c37b7e..bc35c1ae81 100644 --- a/src/org/thoughtcrime/securesms/components/ThumbnailView.java +++ b/src/org/thoughtcrime/securesms/components/ThumbnailView.java @@ -142,7 +142,7 @@ public class ThumbnailView extends FrameLayout { if (transferControls.isPresent()) getTransferControls().setVisibility(View.GONE); glideRequests.load(new DecryptableUri(uri)) - .diskCacheStrategy(DiskCacheStrategy.NONE) + .diskCacheStrategy(DiskCacheStrategy.RESOURCE) .transform(new RoundedCorners(radius)) .transition(withCrossFade()) .centerCrop() @@ -173,7 +173,7 @@ public class ThumbnailView extends FrameLayout { private RequestBuilder buildThumbnailGlideRequest(@NonNull GlideRequests glideRequests, @NonNull Slide slide) { RequestBuilder builder = glideRequests.load(new DecryptableUri(slide.getThumbnailUri())) - .diskCacheStrategy(DiskCacheStrategy.NONE) + .diskCacheStrategy(DiskCacheStrategy.RESOURCE) .transform(new RoundedCorners(radius)) .centerCrop() .transition(withCrossFade()); diff --git a/src/org/thoughtcrime/securesms/crypto/AttachmentSecret.java b/src/org/thoughtcrime/securesms/crypto/AttachmentSecret.java index 21ddeaa44b..f3e09b98b5 100644 --- a/src/org/thoughtcrime/securesms/crypto/AttachmentSecret.java +++ b/src/org/thoughtcrime/securesms/crypto/AttachmentSecret.java @@ -65,7 +65,7 @@ public class AttachmentSecret { } @JsonIgnore - byte[] getModernKey() { + public byte[] getModernKey() { return modernKey; } diff --git a/src/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java b/src/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java index 45333f8c12..ded8206104 100644 --- a/src/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java +++ b/src/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.giph.ui; +import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.net.Uri; @@ -19,6 +20,7 @@ import android.widget.Toast; import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.providers.PersistentBlobProvider; import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; import org.thoughtcrime.securesms.util.DynamicTheme; @@ -98,6 +100,7 @@ public class GiphyActivity extends PassphraseRequiredActionBarActivity this.stickerFragment.setLayoutManager(type); } + @SuppressLint("StaticFieldLeak") @Override public void onClick(final GiphyAdapter.GiphyViewHolder viewHolder) { if (finishingImage != null) finishingImage.gifProgress.setVisibility(View.GONE); @@ -108,7 +111,9 @@ public class GiphyActivity extends PassphraseRequiredActionBarActivity @Override protected Uri doInBackground(Void... params) { try { - return Uri.fromFile(viewHolder.getFile(forMms)); + byte[] data = viewHolder.getData(forMms); + + return PersistentBlobProvider.getInstance(GiphyActivity.this).create(GiphyActivity.this, data, "image/gif", null); } catch (InterruptedException | ExecutionException e) { Log.w(TAG, e); return null; diff --git a/src/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java b/src/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java index ab89e38ab9..938187fe9d 100644 --- a/src/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java +++ b/src/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java @@ -13,13 +13,16 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ProgressBar; -import com.bumptech.glide.Glide; import com.bumptech.glide.RequestBuilder; import com.bumptech.glide.load.DataSource; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.engine.GlideException; +import com.bumptech.glide.load.resource.gif.GifDrawable; +import com.bumptech.glide.load.resource.gif.GifDrawableEncoder; +import com.bumptech.glide.load.resource.gif.GifDrawableResource; import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.target.Target; +import com.bumptech.glide.util.ByteBufferUtil; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.color.MaterialColor; @@ -32,7 +35,10 @@ import org.thoughtcrime.securesms.util.ViewUtil; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.PrintWriter; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.WritableByteChannel; import java.util.List; import java.util.concurrent.ExecutionException; @@ -94,17 +100,20 @@ class GiphyAdapter extends RecyclerView.Adapter { } - public File getFile(boolean forMms) throws ExecutionException, InterruptedException { + public byte[] getData(boolean forMms) throws ExecutionException, InterruptedException { synchronized (this) { while (!modelReady) { Util.wait(this, 0); } } - return glideRequests.load(forMms ? new GiphyPaddedUrl(image.getGifMmsUrl(), image.getMmsGifSize()) : - new GiphyPaddedUrl(image.getGifUrl(), image.getGifSize())) - .downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) - .get(); + GifDrawable drawable = glideRequests.asGif() + .load(forMms ? new GiphyPaddedUrl(image.getGifMmsUrl(), image.getMmsGifSize()) : + new GiphyPaddedUrl(image.getGifUrl(), image.getGifSize())) + .submit(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) + .get(); + + return ByteBufferUtil.toBytes(drawable.getBuffer()); } public synchronized void setModelReady() { diff --git a/src/org/thoughtcrime/securesms/glide/GiphyPaddedUrlFetcher.java b/src/org/thoughtcrime/securesms/glide/GiphyPaddedUrlFetcher.java index 05ed981160..a1706d4141 100644 --- a/src/org/thoughtcrime/securesms/glide/GiphyPaddedUrlFetcher.java +++ b/src/org/thoughtcrime/securesms/glide/GiphyPaddedUrlFetcher.java @@ -45,7 +45,7 @@ class GiphyPaddedUrlFetcher implements DataFetcher { } @Override - public void loadData(Priority priority, DataCallback callback) { + public void loadData(@NonNull Priority priority, @NonNull DataCallback callback) { bodies = new LinkedList<>(); rangeStreams = new LinkedList<>(); stream = null; diff --git a/src/org/thoughtcrime/securesms/glide/cache/EncryptedBitmapCacheDecoder.java b/src/org/thoughtcrime/securesms/glide/cache/EncryptedBitmapCacheDecoder.java new file mode 100644 index 0000000000..362bb7a5a8 --- /dev/null +++ b/src/org/thoughtcrime/securesms/glide/cache/EncryptedBitmapCacheDecoder.java @@ -0,0 +1,54 @@ +package org.thoughtcrime.securesms.glide.cache; + + +import android.graphics.Bitmap; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.ResourceDecoder; +import com.bumptech.glide.load.engine.Resource; +import com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +public class EncryptedBitmapCacheDecoder extends EncryptedCoder implements ResourceDecoder { + + private static final String TAG = EncryptedBitmapCacheDecoder.class.getSimpleName(); + + private final StreamBitmapDecoder streamBitmapDecoder; + private final byte[] secret; + + public EncryptedBitmapCacheDecoder(@NonNull byte[] secret, @NonNull StreamBitmapDecoder streamBitmapDecoder) { + this.secret = secret; + this.streamBitmapDecoder = streamBitmapDecoder; + } + + @Override + public boolean handles(@NonNull File source, @NonNull Options options) + throws IOException + { + Log.w(TAG, "Checking item for encrypted Bitmap cache decoder: " + source.toString()); + + try (InputStream inputStream = createEncryptedInputStream(secret, source)) { + return streamBitmapDecoder.handles(inputStream, options); + } catch (IOException e) { + Log.w(TAG, e); + return false; + } + } + + @Nullable + @Override + public Resource decode(@NonNull File source, int width, int height, @NonNull Options options) + throws IOException + { + Log.w(TAG, "Encrypted Bitmap cache decoder running: " + source.toString()); + try (InputStream inputStream = createEncryptedInputStream(secret, source)) { + return streamBitmapDecoder.decode(inputStream, width, height, options); + } + } +} diff --git a/src/org/thoughtcrime/securesms/glide/cache/EncryptedBitmapResourceEncoder.java b/src/org/thoughtcrime/securesms/glide/cache/EncryptedBitmapResourceEncoder.java new file mode 100644 index 0000000000..104bd4e55a --- /dev/null +++ b/src/org/thoughtcrime/securesms/glide/cache/EncryptedBitmapResourceEncoder.java @@ -0,0 +1,65 @@ +package org.thoughtcrime.securesms.glide.cache; + + +import android.graphics.Bitmap; +import android.support.annotation.NonNull; +import android.util.Log; + +import com.bumptech.glide.load.EncodeStrategy; +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.ResourceEncoder; +import com.bumptech.glide.load.engine.Resource; +import com.bumptech.glide.load.resource.bitmap.BitmapEncoder; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; + +public class EncryptedBitmapResourceEncoder extends EncryptedCoder implements ResourceEncoder { + + private static final String TAG = EncryptedBitmapResourceEncoder.class.getSimpleName(); + + private final byte[] secret; + + public EncryptedBitmapResourceEncoder(@NonNull byte[] secret) { + this.secret = secret; + } + + @Override + public EncodeStrategy getEncodeStrategy(@NonNull Options options) { + return EncodeStrategy.TRANSFORMED; + } + + @SuppressWarnings("EmptyCatchBlock") + @Override + public boolean encode(@NonNull Resource data, @NonNull File file, @NonNull Options options) { + Log.w(TAG, "Encrypted resource encoder running: " + file.toString()); + + Bitmap bitmap = data.get(); + Bitmap.CompressFormat format = getFormat(bitmap, options); + int quality = options.get(BitmapEncoder.COMPRESSION_QUALITY); + + try (OutputStream os = createEncryptedOutputStream(secret, file)) { + bitmap.compress(format, quality, os); + os.close(); + return true; + } catch (IOException e) { + Log.w(TAG, e); + return false; + } + } + + private Bitmap.CompressFormat getFormat(Bitmap bitmap, Options options) { + Bitmap.CompressFormat format = options.get(BitmapEncoder.COMPRESSION_FORMAT); + + if (format != null) { + return format; + } else if (bitmap.hasAlpha()) { + return Bitmap.CompressFormat.PNG; + } else { + return Bitmap.CompressFormat.JPEG; + } + } + + +} diff --git a/src/org/thoughtcrime/securesms/glide/cache/EncryptedCacheEncoder.java b/src/org/thoughtcrime/securesms/glide/cache/EncryptedCacheEncoder.java new file mode 100644 index 0000000000..b98f71ac73 --- /dev/null +++ b/src/org/thoughtcrime/securesms/glide/cache/EncryptedCacheEncoder.java @@ -0,0 +1,52 @@ +package org.thoughtcrime.securesms.glide.cache; + + +import android.support.annotation.NonNull; +import android.util.Log; + +import com.bumptech.glide.load.Encoder; +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.engine.bitmap_recycle.ArrayPool; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class EncryptedCacheEncoder extends EncryptedCoder implements Encoder { + + private static final String TAG = EncryptedCacheEncoder.class.getSimpleName(); + + private final byte[] secret; + private final ArrayPool byteArrayPool; + + public EncryptedCacheEncoder(@NonNull byte[] secret, @NonNull ArrayPool byteArrayPool) { + this.secret = secret; + this.byteArrayPool = byteArrayPool; + } + + @SuppressWarnings("EmptyCatchBlock") + @Override + public boolean encode(@NonNull InputStream data, @NonNull File file, @NonNull Options options) { + Log.w(TAG, "Encrypted cache encoder running: " + file.toString()); + + byte[] buffer = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class); + + try (OutputStream outputStream = createEncryptedOutputStream(secret, file)) { + int read; + + while ((read = data.read(buffer)) != -1) { + outputStream.write(buffer, 0, read); + } + + return true; + } catch (IOException e) { + Log.w(TAG, e); + return false; + } finally { + byteArrayPool.put(buffer); + } + } + + +} diff --git a/src/org/thoughtcrime/securesms/glide/cache/EncryptedCoder.java b/src/org/thoughtcrime/securesms/glide/cache/EncryptedCoder.java new file mode 100644 index 0000000000..ed7f7d622f --- /dev/null +++ b/src/org/thoughtcrime/securesms/glide/cache/EncryptedCoder.java @@ -0,0 +1,98 @@ +package org.thoughtcrime.securesms.glide.cache; + + +import android.support.annotation.NonNull; + +import org.thoughtcrime.securesms.util.Util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +class EncryptedCoder { + + private static byte[] MAGIC_BYTES = {(byte)0x91, (byte)0x5e, (byte)0x6d, (byte)0xb4, + (byte)0x09, (byte)0xa6, (byte)0x68, (byte)0xbe, + (byte)0xe5, (byte)0xb1, (byte)0x1b, (byte)0xd7, + (byte)0x29, (byte)0xe5, (byte)0x04, (byte)0xcc}; + + OutputStream createEncryptedOutputStream(@NonNull byte[] masterKey, @NonNull File file) + throws IOException + { + try { + byte[] random = Util.getSecretBytes(32); + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(masterKey, "HmacSHA256")); + + FileOutputStream fileOutputStream = new FileOutputStream(file); + byte[] iv = new byte[16]; + byte[] key = mac.doFinal(random); + + Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv)); + + fileOutputStream.write(MAGIC_BYTES); + fileOutputStream.write(random); + + CipherOutputStream outputStream = new CipherOutputStream(fileOutputStream, cipher); + outputStream.write(MAGIC_BYTES); + + return outputStream; + } catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } + } + + InputStream createEncryptedInputStream(@NonNull byte[] masterKey, @NonNull File file) throws IOException { + try { + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(masterKey, "HmacSHA256")); + + FileInputStream fileInputStream = new FileInputStream(file); + byte[] theirMagic = new byte[MAGIC_BYTES.length]; + byte[] theirRandom = new byte[32]; + byte[] theirEncryptedMagic = new byte[MAGIC_BYTES.length]; + + Util.readFully(fileInputStream, theirMagic); + Util.readFully(fileInputStream, theirRandom); + + if (!MessageDigest.isEqual(theirMagic, MAGIC_BYTES)) { + throw new IOException("Not an encrypted cache file!"); + } + + byte[] iv = new byte[16]; + byte[] key = mac.doFinal(theirRandom); + + Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv)); + + CipherInputStream inputStream = new CipherInputStream(fileInputStream, cipher); + Util.readFully(inputStream, theirEncryptedMagic); + + if (!MessageDigest.isEqual(theirEncryptedMagic, MAGIC_BYTES)) { + throw new IOException("Key change on encrypted cache file!"); + } + + return inputStream; + } catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } + } + + +} diff --git a/src/org/thoughtcrime/securesms/glide/cache/EncryptedGifCacheDecoder.java b/src/org/thoughtcrime/securesms/glide/cache/EncryptedGifCacheDecoder.java new file mode 100644 index 0000000000..03ea73817c --- /dev/null +++ b/src/org/thoughtcrime/securesms/glide/cache/EncryptedGifCacheDecoder.java @@ -0,0 +1,51 @@ +package org.thoughtcrime.securesms.glide.cache; + + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.ResourceDecoder; +import com.bumptech.glide.load.engine.Resource; +import com.bumptech.glide.load.resource.gif.GifDrawable; +import com.bumptech.glide.load.resource.gif.StreamGifDecoder; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +public class EncryptedGifCacheDecoder extends EncryptedCoder implements ResourceDecoder { + + private static final String TAG = EncryptedGifCacheDecoder.class.getSimpleName(); + + private final byte[] secret; + private final StreamGifDecoder gifDecoder; + + public EncryptedGifCacheDecoder(@NonNull byte[] secret, @NonNull StreamGifDecoder gifDecoder) { + this.secret = secret; + this.gifDecoder = gifDecoder; + } + + @Override + public boolean handles(@NonNull File source, @NonNull Options options) { + Log.w(TAG, "Checking item for encrypted GIF cache decoder: " + source.toString()); + + try (InputStream inputStream = createEncryptedInputStream(secret, source)) { + return gifDecoder.handles(inputStream, options); + } catch (IOException e) { + Log.w(TAG, e); + return false; + } + } + + @Nullable + @Override + public Resource decode(@NonNull File source, int width, int height, @NonNull Options options) throws IOException { + Log.w(TAG, "Encrypted GIF cache decoder running..."); + try (InputStream inputStream = createEncryptedInputStream(secret, source)) { + return gifDecoder.decode(inputStream, width, height, options); + } + } + +} diff --git a/src/org/thoughtcrime/securesms/glide/cache/EncryptedGifDrawableResourceEncoder.java b/src/org/thoughtcrime/securesms/glide/cache/EncryptedGifDrawableResourceEncoder.java new file mode 100644 index 0000000000..5a72de5a59 --- /dev/null +++ b/src/org/thoughtcrime/securesms/glide/cache/EncryptedGifDrawableResourceEncoder.java @@ -0,0 +1,45 @@ +package org.thoughtcrime.securesms.glide.cache; + + +import android.support.annotation.NonNull; +import android.util.Log; + +import com.bumptech.glide.load.EncodeStrategy; +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.ResourceEncoder; +import com.bumptech.glide.load.engine.Resource; +import com.bumptech.glide.load.resource.gif.GifDrawable; +import com.bumptech.glide.util.ByteBufferUtil; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; + +public class EncryptedGifDrawableResourceEncoder extends EncryptedCoder implements ResourceEncoder { + + private static final String TAG = EncryptedGifDrawableResourceEncoder.class.getSimpleName(); + + private final byte[] secret; + + public EncryptedGifDrawableResourceEncoder(@NonNull byte[] secret) { + this.secret = secret; + } + + @Override + public EncodeStrategy getEncodeStrategy(@NonNull Options options) { + return EncodeStrategy.TRANSFORMED; + } + + @Override + public boolean encode(@NonNull Resource data, @NonNull File file, @NonNull Options options) { + GifDrawable drawable = data.get(); + + try (OutputStream outputStream = createEncryptedOutputStream(secret, file)) { + ByteBufferUtil.toStream(drawable.getBuffer(), outputStream); + return true; + } catch (IOException e) { + Log.w(TAG, e); + return false; + } + } +} diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java index 1b3e11621c..4810fffb13 100644 --- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -208,7 +208,7 @@ public class AttachmentManager { { inflateStub(); - new AsyncTask() { + new AsyncTask() { @Override protected void onPreExecute() { thumbnail.clear(glideRequests); diff --git a/src/org/thoughtcrime/securesms/mms/ImageSlide.java b/src/org/thoughtcrime/securesms/mms/ImageSlide.java index 8ce4ec52f5..795b651f0c 100644 --- a/src/org/thoughtcrime/securesms/mms/ImageSlide.java +++ b/src/org/thoughtcrime/securesms/mms/ImageSlide.java @@ -47,10 +47,7 @@ public class ImageSlide extends Slide { @Override public @Nullable Uri getThumbnailUri() { - Uri thumbnailUri = super.getThumbnailUri(); - - if (thumbnailUri == null) return getUri(); - else return thumbnailUri; + return getUri(); } @Override diff --git a/src/org/thoughtcrime/securesms/mms/SignalGlideModule.java b/src/org/thoughtcrime/securesms/mms/SignalGlideModule.java index 76006d3bc9..6400f1beb1 100644 --- a/src/org/thoughtcrime/securesms/mms/SignalGlideModule.java +++ b/src/org/thoughtcrime/securesms/mms/SignalGlideModule.java @@ -1,6 +1,8 @@ package org.thoughtcrime.securesms.mms; import android.content.Context; +import android.graphics.Bitmap; +import android.support.annotation.NonNull; import android.util.Log; import com.bumptech.glide.Glide; @@ -10,16 +12,29 @@ import com.bumptech.glide.annotation.GlideModule; import com.bumptech.glide.load.engine.cache.DiskCache; import com.bumptech.glide.load.engine.cache.DiskCacheAdapter; import com.bumptech.glide.load.model.GlideUrl; +import com.bumptech.glide.load.resource.bitmap.Downsampler; +import com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder; +import com.bumptech.glide.load.resource.gif.ByteBufferGifDecoder; +import com.bumptech.glide.load.resource.gif.GifDrawable; +import com.bumptech.glide.load.resource.gif.StreamGifDecoder; import com.bumptech.glide.module.AppGlideModule; import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; +import org.thoughtcrime.securesms.crypto.AttachmentSecret; +import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; import org.thoughtcrime.securesms.giph.model.GiphyPaddedUrl; import org.thoughtcrime.securesms.glide.ContactPhotoLoader; +import org.thoughtcrime.securesms.glide.cache.EncryptedBitmapCacheDecoder; +import org.thoughtcrime.securesms.glide.cache.EncryptedCacheEncoder; +import org.thoughtcrime.securesms.glide.cache.EncryptedGifCacheDecoder; +import org.thoughtcrime.securesms.glide.cache.EncryptedBitmapResourceEncoder; +import org.thoughtcrime.securesms.glide.cache.EncryptedGifDrawableResourceEncoder; import org.thoughtcrime.securesms.glide.GiphyPaddedUrlLoader; import org.thoughtcrime.securesms.glide.OkHttpUrlLoader; import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; +import java.io.File; import java.io.InputStream; @GlideModule @@ -37,7 +52,17 @@ public class SignalGlideModule extends AppGlideModule { } @Override - public void registerComponents(Context context, Glide glide, Registry registry) { + public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) { + AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(); + byte[] secret = attachmentSecret.getModernKey(); + + registry.prepend(InputStream.class, new EncryptedCacheEncoder(secret, glide.getArrayPool())); + registry.prepend(File.class, Bitmap.class, new EncryptedBitmapCacheDecoder(secret, new StreamBitmapDecoder(new Downsampler(registry.getImageHeaderParsers(), context.getResources().getDisplayMetrics(), glide.getBitmapPool(), glide.getArrayPool()), glide.getArrayPool()))); + registry.prepend(File.class, GifDrawable.class, new EncryptedGifCacheDecoder(secret, new StreamGifDecoder(registry.getImageHeaderParsers(), new ByteBufferGifDecoder(context, registry.getImageHeaderParsers(), glide.getBitmapPool(), glide.getArrayPool()), glide.getArrayPool()))); + + registry.prepend(Bitmap.class, new EncryptedBitmapResourceEncoder(secret)); + registry.prepend(GifDrawable.class, new EncryptedGifDrawableResourceEncoder(secret)); + 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()); diff --git a/src/org/thoughtcrime/securesms/providers/SingleUseBlobProvider.java b/src/org/thoughtcrime/securesms/providers/SingleUseBlobProvider.java index 736c4ec0c5..fcc839f857 100644 --- a/src/org/thoughtcrime/securesms/providers/SingleUseBlobProvider.java +++ b/src/org/thoughtcrime/securesms/providers/SingleUseBlobProvider.java @@ -14,6 +14,7 @@ import java.util.Map; public class SingleUseBlobProvider { + @SuppressWarnings("unused") private static final String TAG = SingleUseBlobProvider.class.getSimpleName(); public static final String AUTHORITY = "org.thoughtcrime.securesms";