mirror of
https://github.com/bitwarden/android.git
synced 2024-11-22 01:16:02 +03:00
Add KdfTypeJson and associated BaseEnumeratedIntSerializer (#131)
This commit is contained in:
parent
aafd32fbc3
commit
a55d6a519a
5 changed files with 131 additions and 14 deletions
|
@ -11,7 +11,7 @@ import kotlinx.serialization.Serializable
|
|||
@Serializable
|
||||
data class InternalPreLoginResponseJson(
|
||||
@SerialName("kdf")
|
||||
val kdfType: Int,
|
||||
val kdfType: KdfTypeJson,
|
||||
@SerialName("kdfIterations")
|
||||
val kdfIterations: UInt,
|
||||
@SerialName("kdfMemory")
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package com.x8bit.bitwarden.data.auth.datasource.network.model
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.serializer.BaseEnumeratedIntSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Represents different key derivation functions (KDFs).
|
||||
*/
|
||||
@Serializable(KdfTypeSerializer::class)
|
||||
enum class KdfTypeJson {
|
||||
@SerialName("1")
|
||||
ARGON2_ID,
|
||||
|
||||
@SerialName("0")
|
||||
PBKDF2_SHA256,
|
||||
}
|
||||
|
||||
private class KdfTypeSerializer :
|
||||
BaseEnumeratedIntSerializer<KdfTypeJson>(KdfTypeJson.values())
|
|
@ -3,9 +3,6 @@ package com.x8bit.bitwarden.data.auth.datasource.network.model
|
|||
import com.x8bit.bitwarden.data.platform.datasource.network.serializer.BaseSurrogateSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
private const val KDF_TYPE_ARGON2_ID = 1
|
||||
private const val KDF_TYPE_PBKDF2_SHA256 = 0
|
||||
|
||||
/**
|
||||
* Response body for pre login.
|
||||
*/
|
||||
|
@ -21,6 +18,11 @@ data class PreLoginResponseJson(
|
|||
*/
|
||||
sealed class KdfParams {
|
||||
|
||||
/**
|
||||
* The associated [KdfTypeJson].
|
||||
*/
|
||||
abstract val kdfTypeJson: KdfTypeJson
|
||||
|
||||
/**
|
||||
* Models params for the Argon2id algorithm.
|
||||
*/
|
||||
|
@ -28,12 +30,20 @@ data class PreLoginResponseJson(
|
|||
val iterations: UInt,
|
||||
val memory: UInt,
|
||||
val parallelism: UInt,
|
||||
) : KdfParams()
|
||||
) : KdfParams() {
|
||||
override val kdfTypeJson: KdfTypeJson
|
||||
get() = KdfTypeJson.ARGON2_ID
|
||||
}
|
||||
|
||||
/**
|
||||
* Models params for the PBKDF2 algorithm.
|
||||
*/
|
||||
data class Pbkdf2(val iterations: UInt) : KdfParams()
|
||||
data class Pbkdf2(
|
||||
val iterations: UInt,
|
||||
) : KdfParams() {
|
||||
override val kdfTypeJson: KdfTypeJson
|
||||
get() = KdfTypeJson.PBKDF2_SHA256
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,13 +55,13 @@ private class PreLoginResponseSerializer :
|
|||
override fun InternalPreLoginResponseJson.toExternalType(): PreLoginResponseJson =
|
||||
PreLoginResponseJson(
|
||||
kdfParams = when (this.kdfType) {
|
||||
KDF_TYPE_PBKDF2_SHA256 -> {
|
||||
KdfTypeJson.PBKDF2_SHA256 -> {
|
||||
PreLoginResponseJson.KdfParams.Pbkdf2(
|
||||
iterations = this.kdfIterations,
|
||||
)
|
||||
}
|
||||
|
||||
KDF_TYPE_ARGON2_ID -> {
|
||||
KdfTypeJson.ARGON2_ID -> {
|
||||
@Suppress("UnsafeCallOnNullableType")
|
||||
PreLoginResponseJson.KdfParams.Argon2ID(
|
||||
iterations = this.kdfIterations,
|
||||
|
@ -59,10 +69,6 @@ private class PreLoginResponseSerializer :
|
|||
parallelism = this.kdfParallelism!!,
|
||||
)
|
||||
}
|
||||
|
||||
else -> throw IllegalStateException(
|
||||
"Unable to parse KDF params for unknown kdfType: ${this.kdfType}",
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -70,7 +76,7 @@ private class PreLoginResponseSerializer :
|
|||
when (val params = this.kdfParams) {
|
||||
is PreLoginResponseJson.KdfParams.Argon2ID -> {
|
||||
InternalPreLoginResponseJson(
|
||||
kdfType = KDF_TYPE_ARGON2_ID,
|
||||
kdfType = params.kdfTypeJson,
|
||||
kdfIterations = params.iterations,
|
||||
kdfMemory = params.memory,
|
||||
kdfParallelism = params.parallelism,
|
||||
|
@ -79,7 +85,7 @@ private class PreLoginResponseSerializer :
|
|||
|
||||
is PreLoginResponseJson.KdfParams.Pbkdf2 -> {
|
||||
InternalPreLoginResponseJson(
|
||||
kdfType = KDF_TYPE_PBKDF2_SHA256,
|
||||
kdfType = params.kdfTypeJson,
|
||||
kdfIterations = params.iterations,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package com.x8bit.bitwarden.data.platform.datasource.network.serializer
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
/**
|
||||
* Base [KSerializer] for mapping an [Enum] with possible values given by [values] to/from integer
|
||||
* values, which should be specified using [SerialName].
|
||||
*/
|
||||
@Suppress("UnnecessaryAbstractClass")
|
||||
abstract class BaseEnumeratedIntSerializer<T : Enum<T>>(
|
||||
private val values: Array<T>,
|
||||
) : KSerializer<T> {
|
||||
|
||||
override val descriptor: SerialDescriptor
|
||||
get() = PrimitiveSerialDescriptor(
|
||||
serialName = this::class.java.simpleName,
|
||||
kind = PrimitiveKind.INT,
|
||||
)
|
||||
|
||||
override fun deserialize(decoder: Decoder): T {
|
||||
val decodedValue = decoder.decodeInt().toString()
|
||||
return values.first { it.serialNameAnnotation?.value == decodedValue }
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: T) {
|
||||
encoder.encodeInt(
|
||||
requireNotNull(
|
||||
value.serialNameAnnotation?.value?.toInt(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private val Enum<*>.serialNameAnnotation: SerialName?
|
||||
get() = javaClass.getDeclaredField(name).getAnnotation(SerialName::class.java)
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package com.x8bit.bitwarden.data.platform.datasource.network.serializer
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.encodeToJsonElement
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class BaseEnumeratedIntSerializerTest {
|
||||
private val json = Json
|
||||
|
||||
@Test
|
||||
fun `properly deserializes integers to enums`() {
|
||||
assertEquals(
|
||||
TestEnum.CASE_1,
|
||||
json.decodeFromString<TestEnum>(
|
||||
"""
|
||||
1
|
||||
""",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `properly serializes enums back to integers`() {
|
||||
assertEquals(
|
||||
json.parseToJsonElement(
|
||||
"""
|
||||
1
|
||||
""",
|
||||
),
|
||||
json.encodeToJsonElement(
|
||||
TestEnum.CASE_1,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable(TestEnumSerializer::class)
|
||||
private enum class TestEnum {
|
||||
@SerialName("1")
|
||||
CASE_1,
|
||||
|
||||
@SerialName("2")
|
||||
CASE_2,
|
||||
}
|
||||
|
||||
private class TestEnumSerializer :
|
||||
BaseEnumeratedIntSerializer<TestEnum>(values = TestEnum.values())
|
Loading…
Reference in a new issue