Skip to content

Commit 8d80507

Browse files
committed
PinRepositorySoftware now uses DeviceInfo when hashing PIN
1 parent 54dfefe commit 8d80507

File tree

5 files changed

+53
-30
lines changed

5 files changed

+53
-30
lines changed

app/src/main/kotlin/com/darkrockstudios/app/securecamera/AppModule.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ val appModule = module {
4747
val detector = get<SecurityLevelDetector>()
4848
when (detector.detectSecurityLevel()) {
4949
SecurityLevel.SOFTWARE ->
50-
PinRepositorySoftware(get())
50+
PinRepositorySoftware(get(), get())
5151

5252
SecurityLevel.TEE, SecurityLevel.STRONGBOX -> {
5353
PinRepositoryHardware(get(), get())

app/src/main/kotlin/com/darkrockstudios/app/securecamera/security/pin/PinRepository.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ interface PinRepository {
1212
return verifyPin(pin, storedHashedPin)
1313
}
1414

15-
fun hashPin(pin: String): HashedPin
16-
fun verifyPin(inputPin: String, storedHash: HashedPin): Boolean
15+
suspend fun hashPin(pin: String): HashedPin
16+
suspend fun verifyPin(inputPin: String, storedHash: HashedPin): Boolean
1717
suspend fun setPoisonPillPin(pin: String)
1818
suspend fun getPlainPoisonPillPin(): String?
1919
suspend fun getHashedPoisonPillPin(): HashedPin?
@@ -34,4 +34,9 @@ interface PinRepository {
3434
val storedHashedPin = getHashedPoisonPillPin() ?: return false
3535
return verifyPin(pin, storedHashedPin)
3636
}
37+
38+
companion object {
39+
const val ARGON_ITERATIONS = 5
40+
const val ARGON_COST = 65536
41+
}
3742
}

app/src/main/kotlin/com/darkrockstudios/app/securecamera/security/pin/PinRepositoryHardware.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package com.darkrockstudios.app.securecamera.security.pin
22

33
import com.darkrockstudios.app.securecamera.preferences.*
44
import com.darkrockstudios.app.securecamera.security.SchemeConfig
5+
import com.darkrockstudios.app.securecamera.security.pin.PinRepository.Companion.ARGON_COST
6+
import com.darkrockstudios.app.securecamera.security.pin.PinRepository.Companion.ARGON_ITERATIONS
57
import com.darkrockstudios.app.securecamera.security.schemes.EncryptionScheme
68
import com.lambdapioneer.argon2kt.Argon2Kt
79
import com.lambdapioneer.argon2kt.Argon2KtResult
@@ -36,13 +38,13 @@ class PinRepositoryHardware(
3638
return Json.decodeFromString(HashedPin.serializer(), hashedPinJson)
3739
}
3840

39-
override fun hashPin(pin: String): HashedPin {
41+
override suspend fun hashPin(pin: String): HashedPin {
4042
val salt = CryptographyRandom.nextBytes(16)
4143
val hashResult: Argon2KtResult = argon2Kt.hash(
4244
mode = Argon2Mode.ARGON2_I,
4345
password = pin.toByteArray(Charsets.UTF_8),
4446
salt = salt,
45-
tCostInIterations = ARGON_ITTERATIONS,
47+
tCostInIterations = ARGON_ITERATIONS,
4648
mCostInKibibyte = ARGON_COST,
4749
)
4850

@@ -52,7 +54,7 @@ class PinRepositoryHardware(
5254
)
5355
}
5456

55-
override fun verifyPin(
57+
override suspend fun verifyPin(
5658
inputPin: String,
5759
storedHash: HashedPin
5860
): Boolean {
@@ -107,7 +109,5 @@ class PinRepositoryHardware(
107109

108110
companion object {
109111
const val PIN_KEY_ALIAS = "pin_key"
110-
const val ARGON_ITTERATIONS = 5
111-
const val ARGON_COST = 65536
112112
}
113113
}

app/src/main/kotlin/com/darkrockstudios/app/securecamera/security/pin/PinRepositorySoftware.kt

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
package com.darkrockstudios.app.securecamera.security.pin
22

33
import com.darkrockstudios.app.securecamera.preferences.*
4+
import com.darkrockstudios.app.securecamera.security.DeviceInfo
45
import com.darkrockstudios.app.securecamera.security.SchemeConfig
6+
import com.darkrockstudios.app.securecamera.security.pin.PinRepository.Companion.ARGON_COST
7+
import com.darkrockstudios.app.securecamera.security.pin.PinRepository.Companion.ARGON_ITERATIONS
58
import com.lambdapioneer.argon2kt.Argon2Kt
69
import com.lambdapioneer.argon2kt.Argon2KtResult
710
import com.lambdapioneer.argon2kt.Argon2Mode
811
import dev.whyoleg.cryptography.random.CryptographyRandom
12+
import kotlinx.coroutines.runBlocking
913
import kotlinx.serialization.encodeToString
1014
import kotlinx.serialization.json.Json
1115
import kotlin.io.encoding.ExperimentalEncodingApi
1216

1317
class PinRepositorySoftware(
1418
private val dataSource: AppPreferencesDataSource,
19+
private val deviceInfo: DeviceInfo,
1520
) : PinRepository {
1621
private val argon2Kt = Argon2Kt()
1722

@@ -31,13 +36,14 @@ class PinRepositorySoftware(
3136
}
3237

3338
@OptIn(ExperimentalStdlibApi::class)
34-
override fun hashPin(pin: String): HashedPin {
39+
override suspend fun hashPin(pin: String): HashedPin {
3540
val salt = CryptographyRandom.nextBytes(16)
41+
val password = pin.toByteArray(Charsets.UTF_8) + runBlocking { deviceInfo.getDeviceIdentifier() }
3642
val hashResult: Argon2KtResult = argon2Kt.hash(
3743
mode = Argon2Mode.ARGON2_I,
38-
password = pin.toByteArray(Charsets.UTF_8),
44+
password = password,
3945
salt = salt,
40-
tCostInIterations = ARGON_ITTERATIONS,
46+
tCostInIterations = ARGON_ITERATIONS,
4147
mCostInKibibyte = ARGON_COST,
4248
)
4349

@@ -48,11 +54,12 @@ class PinRepositorySoftware(
4854
}
4955

5056
@OptIn(ExperimentalStdlibApi::class)
51-
override fun verifyPin(inputPin: String, storedHash: HashedPin): Boolean {
57+
override suspend fun verifyPin(inputPin: String, storedHash: HashedPin): Boolean {
58+
val password = inputPin.toByteArray() + deviceInfo.getDeviceIdentifier()
5259
return argon2Kt.verify(
5360
mode = Argon2Mode.ARGON2_I,
5461
encoded = String(storedHash.hash.base64DecodeUrlSafe()),
55-
password = inputPin.toByteArray(),
62+
password = password,
5663
)
5764
}
5865

@@ -104,9 +111,4 @@ class PinRepositorySoftware(
104111
override suspend fun removePoisonPillPin() {
105112
dataSource.removePoisonPillPin()
106113
}
107-
108-
companion object {
109-
private const val ARGON_ITTERATIONS = 5
110-
private const val ARGON_COST = 65536
111-
}
112114
}

app/src/main/kotlin/com/darkrockstudios/app/securecamera/usecases/MigratePinHash.kt

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ package com.darkrockstudios.app.securecamera.usecases
22

33
import at.favre.lib.crypto.bcrypt.BCrypt
44
import com.darkrockstudios.app.securecamera.preferences.*
5+
import com.darkrockstudios.app.securecamera.security.DeviceInfo
56
import com.darkrockstudios.app.securecamera.security.SecurityLevel
67
import com.darkrockstudios.app.securecamera.security.SecurityLevelDetector
7-
import com.darkrockstudios.app.securecamera.security.pin.PinRepositoryHardware.Companion.ARGON_COST
8-
import com.darkrockstudios.app.securecamera.security.pin.PinRepositoryHardware.Companion.ARGON_ITTERATIONS
8+
import com.darkrockstudios.app.securecamera.security.pin.PinRepository.Companion.ARGON_COST
9+
import com.darkrockstudios.app.securecamera.security.pin.PinRepository.Companion.ARGON_ITERATIONS
910
import com.darkrockstudios.app.securecamera.security.pin.PinRepositoryHardware.Companion.PIN_KEY_ALIAS
1011
import com.darkrockstudios.app.securecamera.security.schemes.EncryptionScheme
1112
import com.darkrockstudios.app.securecamera.security.schemes.HardwareBackedEncryptionScheme
@@ -21,6 +22,7 @@ class MigratePinHash(
2122
private val dataSource: AppPreferencesDataSource,
2223
private val encryptionScheme: EncryptionScheme,
2324
private val removePoisonPillIUseCase: RemovePoisonPillIUseCase,
25+
private val deviceInfo: DeviceInfo,
2426
) {
2527
private val argon2Kt = Argon2Kt()
2628

@@ -50,7 +52,7 @@ class MigratePinHash(
5052

5153
private suspend fun migrateToHardware(pin: String) {
5254
val legacyPin = getLegacyHashedPin()
53-
val newPin = argonHashPin(pin, legacyPin!!.salt)
55+
val newPin = argonHashPinHardware(pin, legacyPin!!.salt)
5456

5557
val hashedPinJson = Json.Default.encodeToString(newPin)
5658

@@ -64,23 +66,21 @@ class MigratePinHash(
6466
// Migrate the encryption key, just need to rename it
6567
encryptionScheme as HardwareBackedEncryptionScheme
6668
// There should only be 1 key because we removed the Poison Pill
67-
val oldKeyFile =
68-
encryptionScheme.keyDir().listFiles()?.first() ?: error("No key file found! Migration will fail")
69-
val newKeyFile = encryptionScheme.dekFile(newPin)
70-
oldKeyFile.renameTo(newKeyFile)
69+
encryptionScheme.keyDir().listFiles()?.first()?.let { oldKeyFile ->
70+
val newKeyFile = encryptionScheme.dekFile(newPin)
71+
oldKeyFile.renameTo(newKeyFile)
72+
}
7173
}
7274

73-
private suspend fun argonHashPin(pin: String, salt: String): HashedPin {
75+
private suspend fun argonHashPinHardware(pin: String, salt: String): HashedPin {
7476
val hashResult: Argon2KtResult = argon2Kt.hash(
7577
mode = Argon2Mode.ARGON2_I,
7678
password = pin.toByteArray(Charsets.UTF_8),
7779
salt = salt.toByteArray(),
78-
tCostInIterations = ARGON_ITTERATIONS,
80+
tCostInIterations = ARGON_ITERATIONS,
7981
mCostInKibibyte = ARGON_COST,
8082
)
8183

82-
Timber.i("argonHashPin done")
83-
8484
return HashedPin(
8585
hashResult.encodedOutputAsString().toByteArray().base64EncodeUrlSafe(),
8686
salt
@@ -93,11 +93,27 @@ class MigratePinHash(
9393
val schemeConfig = dataSource.getSchemeConfig()
9494
val key = dataSource.getCipherKey()
9595

96-
val hashedPin = argonHashPin(pin, legacyPin!!.salt)
96+
val hashedPin = argonHashPinSoftware(pin, legacyPin!!.salt)
9797

9898
val cipheredHash = XorCipher.encrypt(Json.Default.encodeToString(hashedPin), key)
9999
val configJson = Json.encodeToString(schemeConfig)
100100

101101
dataSource.setAppPin(cipheredHash, configJson)
102102
}
103+
104+
private suspend fun argonHashPinSoftware(pin: String, salt: String): HashedPin {
105+
val hashResult: Argon2KtResult = argon2Kt.hash(
106+
mode = Argon2Mode.ARGON2_I,
107+
password = pin.toByteArray(Charsets.UTF_8) + deviceInfo.getDeviceIdentifier(),
108+
salt = salt.toByteArray(),
109+
tCostInIterations = ARGON_ITERATIONS,
110+
mCostInKibibyte = ARGON_COST,
111+
)
112+
113+
return HashedPin(
114+
hashResult.encodedOutputAsString().toByteArray().base64EncodeUrlSafe(),
115+
salt
116+
)
117+
}
118+
103119
}

0 commit comments

Comments
 (0)