[download] check hash for images loaded with glide

This commit is contained in:
Torsten Grote
2024-11-07 11:30:54 -03:00
parent 7022b771ac
commit dc2c65ebd6
2 changed files with 53 additions and 1 deletions

View File

@@ -0,0 +1,47 @@
package org.fdroid.download.glide
import org.fdroid.fdroid.isMatching
import java.io.IOException
import java.io.InputStream
import java.security.DigestInputStream
import java.security.MessageDigest
/**
* An [InputStream] that automatically verifies the [expectedHash] in lower-case SHA-256 format
* and ensures that not more than [maxBytesToRead] are read from the stream.
* This is useful to put an upper bound on data read to not exhaust memory.
*/
internal class AutoVerifyingInputStream(
inputStream: InputStream,
private val expectedHash: String,
private val maxBytesToRead: Long = Runtime.getRuntime().maxMemory() / 2,
) : DigestInputStream(inputStream, MessageDigest.getInstance("SHA-256")) {
private var bytesRead = 0
override fun read(): Int {
val readByte = super.read()
if (readByte != -1) {
bytesRead++
if (bytesRead > maxBytesToRead) {
throw IOException("Read $bytesRead bytes, above maximum allowed.")
}
} else {
if (!digest.isMatching(expectedHash)) throw IOException("Hash not matching.")
}
return readByte
}
override fun read(b: ByteArray?, off: Int, len: Int): Int {
val read = super.read(b, off, len)
if (read != -1) {
bytesRead += read
if (bytesRead > maxBytesToRead) {
throw IOException("Read $bytesRead bytes, above maximum allowed.")
}
} else {
if (!digest.isMatching(expectedHash)) throw IOException("Hash not matching.")
}
return read
}
}

View File

@@ -26,7 +26,12 @@ internal class HttpFetcher(
try {
// glide should take care of closing this stream and the underlying channel
val inputStream = httpManager.getChannel(downloadRequest).toInputStream()
callback.onDataReady(inputStream)
val sha256 = downloadRequest.indexFile.sha256
if (sha256 == null) {
callback.onDataReady(inputStream)
} else {
callback.onDataReady(AutoVerifyingInputStream(inputStream, sha256))
}
} catch (e: Exception) {
callback.onLoadFailed(e)
}