appmattus / certificatetransparency

Certificate transparency for Android and JVM
Apache License 2.0
142 stars 29 forks source link

SDK updates and log_list.json #108

Open gnair03 opened 5 months ago

gnair03 commented 5 months ago

Is there anyway to supply my own log_list.json file? Thing is, I am using 2.4.0 of this lib in my app. If logserver fetch fails, the fallback log_list.json embedded in the SDK is outdated (older than 70 days). Due to the logic added in the SDK, CT Checks get disabled in this particular scenario. The only solution would be to upgrade the SDK. This can be a constant overhead, considering that the SDK is updated with loglist.json every week or two. To overcome this issue, is there anyway to supply my own log_list.json file? Or anyway to override the 70 days logic within the SDK?

@mattmook pls let me know, since my app is on production and this needs to be solved for asap.

gnair03 commented 5 months ago

@mattmook - I was thinking about using the below method:

LogListDataSourceFactory.createDataSource(
                logListService = LogListDataSourceFactory.createLogListService(LOG_SERVER_BASE_URL),
                diskCache = AndroidDiskCache(appContext)
)

but provide my own implementation of the AndroidDiskCache class. But I dont understand how to create the log-list.sig file at my end. Could you pls help here? Is this even do-able?

dsatija commented 4 months ago

Hi @gnair03 , were you able to resolve this?

sergeich commented 4 months ago

Temporary workaround. It's mainly a copy of AndroidDiskCache from the library but with some changes for the case of not existent cache. I've put log_list.json and log_list.sig to raw resource folder and renamed them. Files could be downloaded from the last commit: https://github.com/appmattus/certificatetransparency/commit/8efb1612f2ca0e75850ddc50a072676b4ea54741

internal class AndroidDiskCache(context: Context) : DiskCache {
    private val cacheDirPath = "${context.cacheDir.path}/certificate-transparency-android"
    private val resources = context.resources

    override suspend fun get(): RawLogListResult? {
        mutex.withLock {
            return try {
                val jsonFile = File(cacheDirPath, LOG_LIST_FILE)
                val sigFile = File(cacheDirPath, SIG_FILE)
                val logList = jsonFile.readBytes()
                val signature = sigFile.readBytes()

                RawLogListResult.Success(logList, signature)
            } catch (ignored: IOException) {
                with(this::class.java.classLoader) {
                    val logList = resources.openRawResource(R.raw.log_list_json)?.use { it.readBytes() } ?: return RawLogListResult.Failure()
                    val signature = resources.openRawResource(R.raw.log_list_sig)?.use { it.readBytes() } ?: return RawLogListResult.Failure()

                    return RawLogListResult.Success(logList, signature)
                }
            }
        }
    }

    override suspend fun set(value: RawLogListResult) {
        mutex.withLock {
            if (value is RawLogListResult.Success) {
                try {
                    File(cacheDirPath).mkdirs()

                    File(cacheDirPath, LOG_LIST_FILE).writeBytes(value.logList)
                    File(cacheDirPath, SIG_FILE).writeBytes(value.signature)
                } catch (ignored: IOException) {
                    // non fatal
                }
            }
        }
    }

    public companion object {
        private const val LOG_LIST_FILE = "loglist.json"
        private const val SIG_FILE = "loglist.sig"

        /**
         * Ensure only one instance of AndroidDiskCache can read/write at a time
         */
        private val mutex = Mutex()
    }
}
dsatija commented 4 months ago

Thankyou so much ! @sergeich do you know what happens after 70 days , since the last commit was on mar 7 ? may 17 would make it 70 days old

mattmook commented 3 months ago

@dsatija CT checks will continue to work even after 70 days assuming the device is caching locally and/or can download a fresh copy of the log list files. Basically the small edge case is a fresh install where the list cannot be updated to a newer version (for example if google were to move the file) then at that point the client follows how Chrome works in that scenario and will disable CT checks.

mattmook commented 3 months ago

@gnair03 The log-list.sig file is signed by Googles private key, so you cannot simply create one directly. Google provide a zip file which contains matching json and sig so in that sense it doesn't necessarily matter. Of course, if you want to sign the log-list file yourself with your own private key then you can do that and provide a public key to the createDataSource function.

mattmook commented 3 months ago

@gnair03 The longer answer to your initial question around the 70 days is while we attempt to update the library with an updated cached version as per my comment 2 messages up in most use cases it doesn't really matter.

Indeed, the original version of the library didn't have any log-list file embedded and solely relies on being able to download the file. So, the newer version is an improvement on that giving that 70 day period where checks can still be enforced even if the client is unable to update the list itself.

So as long as the log-list is cached then the risk is to clients who are unable to update for 70 days; the hope is this is quite a small number of clients.