@@ -18,28 +18,44 @@ package com.nativeapptemplate.nativeapptemplatefree.datastore
1818
1919import androidx.datastore.core.CorruptionException
2020import androidx.datastore.core.Serializer
21+ import com.google.crypto.tink.Aead
2122import com.google.protobuf.InvalidProtocolBufferException
2223import com.nativeapptemplate.nativeapptemplatefree.UserPreferences
2324import java.io.InputStream
2425import java.io.OutputStream
26+ import java.security.GeneralSecurityException
2527import javax.inject.Inject
2628
2729/* *
2830 * An [androidx.datastore.core.Serializer] for the [UserPreferences] proto.
31+ *
32+ * Encrypts data at rest using Tink AEAD. On read, falls back to parsing
33+ * unencrypted proto for migration from the previous unencrypted format.
2934 */
30- class UserPreferencesSerializer @Inject constructor() : Serializer<UserPreferences> {
35+ class UserPreferencesSerializer @Inject constructor(
36+ private val aead : Aead ,
37+ ) : Serializer<UserPreferences> {
3138 override val defaultValue: UserPreferences = UserPreferences .getDefaultInstance()
3239
33- override suspend fun readFrom (input : InputStream ): UserPreferences =
34- try {
35- // readFrom is already called on the data store background thread
36- UserPreferences .parseFrom(input)
37- } catch (exception: InvalidProtocolBufferException ) {
38- throw CorruptionException (" Cannot read proto." , exception)
40+ override suspend fun readFrom (input : InputStream ): UserPreferences {
41+ val bytes = input.readBytes()
42+ if (bytes.isEmpty()) return defaultValue
43+ return try {
44+ val decrypted = aead.decrypt(bytes, null )
45+ UserPreferences .parseFrom(decrypted)
46+ } catch (_: GeneralSecurityException ) {
47+ // Fallback: try parsing as unencrypted proto (migration from legacy format).
48+ // The data will be re-encrypted on the next write.
49+ try {
50+ UserPreferences .parseFrom(bytes)
51+ } catch (e: InvalidProtocolBufferException ) {
52+ throw CorruptionException (" Cannot read proto." , e)
53+ }
3954 }
55+ }
4056
4157 override suspend fun writeTo (t : UserPreferences , output : OutputStream ) {
42- // writeTo is already called on the data store background thread
43- t.writeTo(output )
58+ val encrypted = aead.encrypt(t.toByteArray(), null )
59+ output.write(encrypted )
4460 }
4561}
0 commit comments