-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add data classes for CBOR/COSE signing
- Loading branch information
Showing
10 changed files
with
486 additions
and
2 deletions.
There are no files selected for viewing
35 changes: 35 additions & 0 deletions
35
vclib/src/commonMain/kotlin/at/asitplus/wallet/lib/cbor/CoseAlgorithm.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package at.asitplus.wallet.lib.cbor | ||
|
||
import kotlinx.serialization.KSerializer | ||
import kotlinx.serialization.Serializable | ||
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 | ||
|
||
@Serializable(with = CoseAlgorithmSerializer::class) | ||
enum class CoseAlgorithm(val value: Int) { | ||
|
||
ES256(-7), | ||
ES384(-35), | ||
ES512(-36); | ||
|
||
} | ||
|
||
|
||
object CoseAlgorithmSerializer : KSerializer<CoseAlgorithm> { | ||
|
||
override val descriptor: SerialDescriptor = | ||
PrimitiveSerialDescriptor("CoseAlgorithmSerializer", PrimitiveKind.INT) | ||
|
||
override fun serialize(encoder: Encoder, value: CoseAlgorithm) { | ||
value.let { encoder.encodeInt(it.value) } | ||
} | ||
|
||
override fun deserialize(decoder: Decoder): CoseAlgorithm { | ||
val decoded = decoder.decodeInt() | ||
return CoseAlgorithm.values().first { it.value == decoded } | ||
} | ||
|
||
} |
37 changes: 37 additions & 0 deletions
37
vclib/src/commonMain/kotlin/at/asitplus/wallet/lib/cbor/CoseEllipticCurve.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package at.asitplus.wallet.lib.cbor | ||
|
||
import kotlinx.serialization.KSerializer | ||
import kotlinx.serialization.Serializable | ||
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 | ||
|
||
@Serializable(with = CoseEllipticCurveSerializer::class) | ||
enum class CoseEllipticCurve(val value: Int) { | ||
|
||
P256(1), | ||
P384(2), | ||
P521(3), | ||
X25519(4), | ||
X448(5), | ||
Ed25519(6), | ||
Ed448(7); | ||
|
||
} | ||
|
||
object CoseEllipticCurveSerializer : KSerializer<CoseEllipticCurve?> { | ||
|
||
override val descriptor: SerialDescriptor = | ||
PrimitiveSerialDescriptor("CoseEllipticCurveSerializer", PrimitiveKind.INT) | ||
|
||
override fun serialize(encoder: Encoder, value: CoseEllipticCurve?) { | ||
value?.let { encoder.encodeInt(it.value) } | ||
} | ||
|
||
override fun deserialize(decoder: Decoder): CoseEllipticCurve? { | ||
val decoded = decoder.decodeInt() | ||
return CoseEllipticCurve.values().firstOrNull { it.value == decoded } | ||
} | ||
} |
83 changes: 83 additions & 0 deletions
83
vclib/src/commonMain/kotlin/at/asitplus/wallet/lib/cbor/CoseHeader.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package at.asitplus.wallet.lib.cbor | ||
|
||
import at.asitplus.wallet.lib.iso.cborSerializer | ||
import io.github.aakira.napier.Napier | ||
import kotlinx.serialization.ExperimentalSerializationApi | ||
import kotlinx.serialization.SerialName | ||
import kotlinx.serialization.Serializable | ||
import kotlinx.serialization.cbor.ByteString | ||
import kotlinx.serialization.cbor.SerialLabel | ||
import kotlinx.serialization.decodeFromByteArray | ||
import kotlinx.serialization.encodeToByteArray | ||
|
||
/** | ||
* Protected header of a [CoseSigned]. | ||
*/ | ||
@OptIn(ExperimentalSerializationApi::class) | ||
@Serializable | ||
data class CoseHeader( | ||
@SerialLabel(1) | ||
@SerialName("alg") | ||
val algorithm: CoseAlgorithm? = null, | ||
@SerialLabel(2) | ||
@SerialName("crit") | ||
val criticalHeaders: String? = null, | ||
@SerialLabel(3) | ||
@SerialName("content type") | ||
val contentType: String? = null, | ||
@SerialLabel(4) | ||
@SerialName("kid") | ||
val kid: String? = null, | ||
@SerialLabel(5) | ||
@SerialName("IV") | ||
@ByteString | ||
val iv: ByteArray? = null, | ||
@SerialLabel(6) | ||
@SerialName("Partial IV") | ||
@ByteString | ||
val partialIv: ByteArray? = null, | ||
) { | ||
|
||
fun serialize() = cborSerializer.encodeToByteArray(this) | ||
|
||
override fun equals(other: Any?): Boolean { | ||
if (this === other) return true | ||
if (other == null || this::class != other::class) return false | ||
|
||
other as CoseHeader | ||
|
||
if (algorithm != other.algorithm) return false | ||
if (criticalHeaders != other.criticalHeaders) return false | ||
if (contentType != other.contentType) return false | ||
if (kid != other.kid) return false | ||
if (iv != null) { | ||
if (other.iv == null) return false | ||
if (!iv.contentEquals(other.iv)) return false | ||
} else if (other.iv != null) return false | ||
if (partialIv != null) { | ||
if (other.partialIv == null) return false | ||
if (!partialIv.contentEquals(other.partialIv)) return false | ||
} else if (other.partialIv != null) return false | ||
|
||
return true | ||
} | ||
|
||
override fun hashCode(): Int { | ||
var result = algorithm?.hashCode() ?: 0 | ||
result = 31 * result + (criticalHeaders?.hashCode() ?: 0) | ||
result = 31 * result + (contentType?.hashCode() ?: 0) | ||
result = 31 * result + (kid?.hashCode() ?: 0) | ||
result = 31 * result + (iv?.contentHashCode() ?: 0) | ||
result = 31 * result + (partialIv?.contentHashCode() ?: 0) | ||
return result | ||
} | ||
|
||
companion object { | ||
fun deserialize(it: ByteArray) = kotlin.runCatching { | ||
cborSerializer.decodeFromByteArray<CoseHeader>(it) | ||
}.getOrElse { | ||
Napier.w("deserialize failed", it) | ||
null | ||
} | ||
} | ||
} |
115 changes: 115 additions & 0 deletions
115
vclib/src/commonMain/kotlin/at/asitplus/wallet/lib/cbor/CoseKey.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
package at.asitplus.wallet.lib.cbor | ||
|
||
import at.asitplus.wallet.lib.iso.cborSerializer | ||
import io.github.aakira.napier.Napier | ||
import io.matthewnelson.component.base64.encodeBase64 | ||
import kotlinx.serialization.ExperimentalSerializationApi | ||
import kotlinx.serialization.SerialName | ||
import kotlinx.serialization.Serializable | ||
import kotlinx.serialization.cbor.ByteString | ||
import kotlinx.serialization.cbor.SerialLabel | ||
import kotlinx.serialization.decodeFromByteArray | ||
import kotlinx.serialization.encodeToByteArray | ||
|
||
@OptIn(ExperimentalSerializationApi::class) | ||
@Serializable | ||
data class CoseKey( | ||
@SerialLabel(1) | ||
@SerialName("kty") | ||
val type: CoseKeyType, | ||
@SerialLabel(2) | ||
@SerialName("kid") | ||
@ByteString | ||
val keyId: ByteArray? = null, | ||
@SerialLabel(3) | ||
@SerialName("alg") | ||
val algorithm: CoseAlgorithm? = null, | ||
@SerialLabel(4) | ||
@SerialName("key_ops") | ||
val operations: Array<CoseKeyOperation>? = null, | ||
@SerialLabel(5) | ||
@SerialName("Base IV") | ||
@ByteString | ||
val baseIv: ByteArray? = null, | ||
@SerialLabel(-1) | ||
@SerialName("crv") | ||
val curve: CoseEllipticCurve? = null, | ||
@SerialLabel(-2) | ||
@SerialName("x") | ||
val x: ByteArray? = null, | ||
@SerialLabel(-3) | ||
@SerialName("y") // TODO might also be bool | ||
val y: ByteArray? = null, | ||
@SerialLabel(-4) | ||
@SerialName("d") | ||
val d: ByteArray? = null, | ||
) { | ||
fun serialize() = cborSerializer.encodeToByteArray(this) | ||
|
||
companion object { | ||
|
||
fun deserialize(it: ByteArray) = kotlin.runCatching { | ||
cborSerializer.decodeFromByteArray<CoseHeader>(it) | ||
}.getOrElse { | ||
Napier.w("deserialize failed", it) | ||
null | ||
} | ||
|
||
} | ||
|
||
override fun equals(other: Any?): Boolean { | ||
if (this === other) return true | ||
if (other == null || this::class != other::class) return false | ||
|
||
other as CoseKey | ||
|
||
if (type != other.type) return false | ||
if (keyId != null) { | ||
if (other.keyId == null) return false | ||
if (!keyId.contentEquals(other.keyId)) return false | ||
} else if (other.keyId != null) return false | ||
if (algorithm != other.algorithm) return false | ||
if (operations != null) { | ||
if (other.operations == null) return false | ||
if (!operations.contentEquals(other.operations)) return false | ||
} else if (other.operations != null) return false | ||
if (baseIv != null) { | ||
if (other.baseIv == null) return false | ||
if (!baseIv.contentEquals(other.baseIv)) return false | ||
} else if (other.baseIv != null) return false | ||
if (curve != other.curve) return false | ||
if (x != null) { | ||
if (other.x == null) return false | ||
if (!x.contentEquals(other.x)) return false | ||
} else if (other.x != null) return false | ||
if (y != null) { | ||
if (other.y == null) return false | ||
if (!y.contentEquals(other.y)) return false | ||
} else if (other.y != null) return false | ||
if (d != null) { | ||
if (other.d == null) return false | ||
if (!d.contentEquals(other.d)) return false | ||
} else if (other.d != null) return false | ||
|
||
return true | ||
} | ||
|
||
override fun hashCode(): Int { | ||
var result = type.hashCode() | ||
result = 31 * result + (keyId?.contentHashCode() ?: 0) | ||
result = 31 * result + (algorithm?.hashCode() ?: 0) | ||
result = 31 * result + (operations?.contentHashCode() ?: 0) | ||
result = 31 * result + (baseIv?.contentHashCode() ?: 0) | ||
result = 31 * result + (curve?.hashCode() ?: 0) | ||
result = 31 * result + (x?.contentHashCode() ?: 0) | ||
result = 31 * result + (y?.contentHashCode() ?: 0) | ||
result = 31 * result + (d?.contentHashCode() ?: 0) | ||
return result | ||
} | ||
|
||
override fun toString(): String { | ||
return "CoseKey(type=$type, keyId=${keyId?.encodeBase64()}, algorithm=$algorithm, operations=${operations?.contentToString()}, baseIv=${baseIv?.encodeBase64()}, curve=$curve, x=${x?.encodeBase64()}, y=${y?.encodeBase64()}, d=${d?.encodeBase64()})" | ||
} | ||
|
||
|
||
} |
41 changes: 41 additions & 0 deletions
41
vclib/src/commonMain/kotlin/at/asitplus/wallet/lib/cbor/CoseKeyOperation.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package at.asitplus.wallet.lib.cbor | ||
|
||
import kotlinx.serialization.KSerializer | ||
import kotlinx.serialization.Serializable | ||
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 | ||
|
||
@Serializable(with = CoseKeyOperationSerializer::class) | ||
enum class CoseKeyOperation(val value: Int) { | ||
|
||
SIGN(1), | ||
VERIFY(2), | ||
ENCRYPT(3), | ||
DECRYPT(4), | ||
WRAP_KEY(5), | ||
UNWRAP_KEY(6), | ||
DERIVE_KEY(7), | ||
DERIVE_BITS(8), | ||
MAC_CREATE(9), | ||
MAC_VERIFY(10); | ||
} | ||
|
||
|
||
object CoseKeyOperationSerializer : KSerializer<CoseKeyOperation> { | ||
|
||
override val descriptor: SerialDescriptor = | ||
PrimitiveSerialDescriptor("CoseKeyOperationSerializer", PrimitiveKind.INT) | ||
|
||
override fun serialize(encoder: Encoder, value: CoseKeyOperation) { | ||
value.let { encoder.encodeInt(it.value) } | ||
} | ||
|
||
override fun deserialize(decoder: Decoder): CoseKeyOperation { | ||
val decoded = decoder.decodeInt() | ||
return CoseKeyOperation.values().first { it.value == decoded } | ||
} | ||
|
||
} |
35 changes: 35 additions & 0 deletions
35
vclib/src/commonMain/kotlin/at/asitplus/wallet/lib/cbor/CoseKeyType.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package at.asitplus.wallet.lib.cbor | ||
|
||
import kotlinx.serialization.KSerializer | ||
import kotlinx.serialization.Serializable | ||
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 | ||
|
||
@Serializable(with = CoseKeyTypeSerializer::class) | ||
enum class CoseKeyType(val value: Int) { | ||
|
||
OKP(1), | ||
EC2(2), | ||
SYMMETRIC(4), | ||
RESERVED(0); | ||
|
||
} | ||
|
||
object CoseKeyTypeSerializer : KSerializer<CoseKeyType> { | ||
|
||
override val descriptor: SerialDescriptor = | ||
PrimitiveSerialDescriptor("CoseKeyTypeSerializer", PrimitiveKind.INT) | ||
|
||
override fun serialize(encoder: Encoder, value: CoseKeyType) { | ||
value.let { encoder.encodeInt(it.value) } | ||
} | ||
|
||
override fun deserialize(decoder: Decoder): CoseKeyType { | ||
val decoded = decoder.decodeInt() | ||
return CoseKeyType.values().firstOrNull { it.value == decoded } | ||
?: throw IllegalArgumentException("Not known: $decoded") | ||
} | ||
} |
Oops, something went wrong.