feat: migrate secret management to datastore proto and tink

This commit is contained in:
2026-06-02 18:15:22 +02:00
parent 2be68d38a5
commit e8b3f898e8
16 changed files with 328 additions and 128 deletions
@@ -0,0 +1,53 @@
package com.bitcointxoko.gudariwallet.security
import android.util.Log
import androidx.datastore.core.CorruptionException
import androidx.datastore.core.Serializer
import com.google.crypto.tink.StreamingAead
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.InputStream
import java.io.OutputStream
class CredentialsSerializer(
private val streamingAead: StreamingAead
) : Serializer<Credentials> {
private val TAG = "CredentialsSerializer"
override val defaultValue: Credentials = Credentials.getDefaultInstance()
override suspend fun readFrom(input: InputStream): Credentials =
withContext(Dispatchers.IO) {
try {
val result = Credentials.parseFrom(
streamingAead.newDecryptingStream(input, AAD)
)
Log.d(TAG, "Credentials decrypted successfully — isOnboarded: ${result.isOnboarded}")
result
} catch (e: Exception) {
Log.e(TAG, "Decryption failed — DataStore may be corrupt or key rotated", e)
throw CorruptionException("Cannot read/decrypt credentials DataStore", e)
}
}
override suspend fun writeTo(t: Credentials, output: OutputStream): Unit =
withContext(Dispatchers.IO) {
try {
val encryptingStream = streamingAead.newEncryptingStream(output, AAD)
t.writeTo(encryptingStream) // returns Int — discarded by Unit return type
encryptingStream.close()
Log.d(TAG, "Credentials encrypted and written — isOnboarded: ${t.isOnboarded}, " +
"hasInvoiceKey: ${t.invoiceKey.isNotBlank()}, " +
"hasAdminKey: ${t.adminKey.isNotBlank()}, " +
"hasBaseUrl: ${t.baseUrl.isNotBlank()}")
} catch (e: Exception) {
Log.e(TAG, "Encryption/write failed", e)
throw e
}
}
companion object {
private val AAD = "credentials_store".toByteArray(Charsets.UTF_8)
}
}