Skip to content

Commit 77b1f82

Browse files
committed
WIP Commit while getting new computer setup
1 parent 076b64c commit 77b1f82

4 files changed

Lines changed: 87 additions & 55 deletions

File tree

armadillo-datastore/src/androidTest/java/at/favre/lib/armadillo/datastore/UserStore.kt

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,25 @@ import androidx.datastore.createDataStore
66
import kotlinx.coroutines.flow.Flow
77
import kotlinx.serialization.ExperimentalSerializationApi
88
import kotlinx.serialization.protobuf.ProtoBuf
9+
import java.io.File
910

1011
@ExperimentalSerializationApi
11-
class UserStore(context: Context) {
12+
class UserStore(private val context: Context) {
13+
companion object {
14+
private const val fileName = "user"
15+
}
1216
private val serializer = User.serializer()
1317
private val protobuf = ProtoBuf {}
1418

1519
private val protocol = object : ProtobufProtocol<User> {
1620

17-
override fun fromBytes(bytes: ByteArray): User =
21+
override fun decode(bytes: ByteArray): User =
1822
protobuf.decodeFromByteArray(serializer, bytes)
1923

20-
override fun fromNothing(): User =
24+
override fun default(): User =
2125
User(name = "", email = "")
2226

23-
override fun toBytes(data: User): ByteArray =
27+
override fun encode(data: User): ByteArray =
2428
protobuf.encodeToByteArray(serializer, data)
2529
}
2630

@@ -31,7 +35,7 @@ class UserStore(context: Context) {
3135
)
3236

3337
private val store: DataStore<User> = context.createDataStore(
34-
fileName = "user.pb",
38+
fileName = fileName,
3539
serializer = userSerializer
3640
)
3741

@@ -42,4 +46,13 @@ class UserStore(context: Context) {
4246
val user: Flow<User>
4347
get() = store.data
4448

49+
fun clear() {
50+
with(context) {
51+
val dataStore = File(this.filesDir, "datastore/$fileName")
52+
if(dataStore.exists()) {
53+
dataStore.delete()
54+
}
55+
}
56+
}
57+
4558
}

armadillo-datastore/src/androidTest/java/at/favre/lib/armadillo/datastore/UserStoreTest.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,20 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
55
import kotlinx.coroutines.flow.first
66
import kotlinx.coroutines.runBlocking
77
import kotlinx.serialization.ExperimentalSerializationApi
8+
import org.junit.Before
89
import org.junit.Test
910
import org.junit.runner.RunWith
1011

1112
@ExperimentalSerializationApi
1213
@RunWith(AndroidJUnit4::class)
1314
class UserStoreTest {
1415

16+
@Before
17+
fun setup() {
18+
val store = UserStore(ApplicationProvider.getApplicationContext())
19+
store.clear()
20+
}
21+
1522
@Test
1623
fun canReadEmptyUserFromStore() {
1724
val store = UserStore(ApplicationProvider.getApplicationContext())

armadillo-datastore/src/main/java/at/favre/lib/armadillo/datastore/ArmadilloSerializer.kt

Lines changed: 35 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package at.favre.lib.armadillo.datastore
22

33
import android.content.Context
44
import android.os.Build
5-
import android.util.Log
65
import androidx.datastore.Serializer
76
import at.favre.lib.armadillo.*
87
import at.favre.lib.armadillo.Armadillo.CONTENT_KEY_OUT_BYTE_LENGTH
@@ -12,15 +11,10 @@ import java.io.OutputStream
1211
import java.security.Provider
1312
import java.security.SecureRandom
1413

15-
interface ProtobufProtocol<T> {
16-
fun toBytes(data: T): ByteArray
17-
fun fromBytes(bytes: ByteArray): T
18-
fun fromNothing(): T
19-
}
20-
2114
class ArmadilloSerializer<T>(
2215
context: Context,
2316
private val protocol: ProtobufProtocol<T>,
17+
password: CharArray? = null,
2418
fingerprintData: List<String> = emptyList(),
2519
secureRandom: SecureRandom = SecureRandom(),
2620
additionalDecryptionConfigs: List<EncryptionProtocolConfig> = listOf(),
@@ -29,36 +23,38 @@ class ArmadilloSerializer<T>(
2923
preferencesSalt: ByteArray = BuildConfig.PREF_SALT
3024
) : Serializer<T> {
3125

32-
private val password: ByteArrayRuntimeObfuscator?
26+
private val serializerPassword: ByteArrayRuntimeObfuscator?
3327
private val encryptionProtocol: EncryptionProtocol
3428
private val fingerprint: EncryptionFingerprint = EncryptionFingerprintFactory.create(
3529
context,
3630
buildString { fingerprintData.forEach(::append) }
3731
)
32+
private val defaultConfig = EncryptionProtocolConfig.newDefaultConfig()
33+
private val kitKatConfig by lazy {
34+
@Suppress("DEPRECATION")
35+
EncryptionProtocolConfig.newBuilder(defaultConfig.build())
36+
.authenticatedEncryption(AesCbcEncryption(secureRandom, provider))
37+
.protocolVersion(Armadillo.KITKAT_PROTOCOL_VERSION)
38+
.build()
39+
}
3840

3941
init {
40-
val defaultConfig = EncryptionProtocolConfig.newDefaultConfig()
4142

4243
val stringMessageDigest = HkdfMessageDigest(
4344
BuildConfig.PREF_SALT,
4445
CONTENT_KEY_OUT_BYTE_LENGTH
4546
)
46-
val kitKatConfig = takeIf { enabledKitkatSupport }?.run {
47-
@Suppress("DEPRECATION")
48-
EncryptionProtocolConfig.newBuilder(defaultConfig.build())
49-
.authenticatedEncryption(AesCbcEncryption(secureRandom, provider))
50-
.protocolVersion(Armadillo.KITKAT_PROTOCOL_VERSION)
51-
.build()
52-
}
47+
5348
val config =
54-
if (kitKatConfig != null && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
49+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
5550
kitKatConfig
5651
} else {
5752
EncryptionProtocolConfig
5853
.newBuilder(defaultConfig.build())
5954
.authenticatedEncryption(AesGcmEncryption(secureRandom, provider))
6055
.build()
61-
}.also { checkKitKatSupport(it.authenticatedEncryption) }
56+
}
57+
checkKitKatSupport(config.authenticatedEncryption)
6258

6359
val factory = DefaultEncryptionProtocol.Factory(
6460
config,
@@ -74,7 +70,7 @@ class ArmadilloSerializer<T>(
7470
)
7571

7672
encryptionProtocol = factory.create(preferencesSalt)
77-
password = null // TODO Add password config factory.obfuscatePassword()
73+
serializerPassword = password?.let(factory::obfuscatePassword)
7874
}
7975

8076

@@ -86,56 +82,45 @@ class ArmadilloSerializer<T>(
8682
}
8783

8884
companion object {
89-
private const val CRYPTO_KEY = "ArmadilloStore"
85+
private const val CRYPTO_KEY = "ArmadilloStoreSerializer"
86+
}
87+
88+
89+
private fun encrypt(content: ByteArray): ByteArray = with(encryptionProtocol) {
90+
encrypt(
91+
deriveContentKey(CRYPTO_KEY),
92+
deobfuscatePassword(serializerPassword),
93+
content
94+
)
9095
}
9196

9297

93-
private fun encrypt(content: ByteArray): ByteArray =
94-
try {
98+
private fun decrypt(encrypted: ByteArray): ByteArray? =
99+
if (encrypted.isEmpty()) {
100+
null
101+
} else {
95102
encryptionProtocol
96-
.encrypt(
103+
.decrypt(
97104
encryptionProtocol.deriveContentKey(CRYPTO_KEY),
98-
encryptionProtocol.deobfuscatePassword(password),
99-
content
105+
encryptionProtocol.deobfuscatePassword(serializerPassword),
106+
encrypted
100107
)
101-
} catch (e: Throwable) {
102-
throw IllegalStateException(e)
103108
}
104109

105-
106-
private fun decrypt(encrypted: ByteArray): ByteArray? {
107-
if (encrypted.isEmpty()) {
108-
return null
109-
}
110-
try {
111-
return encryptionProtocol
112-
.decrypt(
113-
encryptionProtocol.deriveContentKey(CRYPTO_KEY),
114-
encryptionProtocol.deobfuscatePassword(password),
115-
encrypted
116-
)
117-
} catch (e: Throwable) {
118-
Log.e("DataStrore", "decetyp", e)
119-
// recoveryPolicy.handleBrokenConte(e, keyHash, base64Encrypted, password != null, this)
120-
// TODO handle this
121-
}
122-
return null
123-
}
124-
125110
override fun readFrom(input: InputStream): T =
126111
input
127112
.readBytes()
128113
.let(::decrypt)
129114
.let {
130115
val bytes = it ?: byteArrayOf()
131-
if (bytes.isEmpty()) protocol.fromNothing()
132-
else protocol.fromBytes(bytes)
116+
if (bytes.isEmpty()) protocol.default()
117+
else protocol.decode(bytes)
133118
}
134119

135120

136121
override fun writeTo(t: T, output: OutputStream) {
137122
protocol
138-
.toBytes(t)
123+
.encode(t)
139124
.let(::encrypt)
140125
.also(output::write)
141126
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package at.favre.lib.armadillo.datastore
2+
3+
/**
4+
* The protocol is used to wrap the encoded/decode for any protobuff implementation.
5+
*
6+
* Datastore requies the type T MUST be immutable. Any mutable types will result in a broken DataStore.
7+
*
8+
* Datastore will always return an empty
9+
*/
10+
interface ProtobufProtocol<T> {
11+
/**
12+
* un-encrypted proto byte encoding of [T]
13+
*/
14+
fun encode(data: T): ByteArray
15+
16+
/**
17+
* un-encrypted proto bytes to proto class of [T]
18+
*/
19+
fun decode(bytes: ByteArray): T
20+
21+
/**
22+
* Returns a default value when the store is empty. This will
23+
* always happen on first read even when writing to the store
24+
* for the first time.
25+
*/
26+
fun default(): T
27+
}

0 commit comments

Comments
 (0)