-
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.
Implement simple COSE signing and verification
- Loading branch information
Showing
10 changed files
with
399 additions
and
14 deletions.
There are no files selected for viewing
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
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
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
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
89 changes: 89 additions & 0 deletions
89
vclib/src/commonMain/kotlin/at/asitplus/wallet/lib/cbor/CoseService.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,89 @@ | ||
package at.asitplus.wallet.lib.cbor | ||
|
||
import at.asitplus.KmmResult | ||
import at.asitplus.wallet.lib.agent.CryptoService | ||
import at.asitplus.wallet.lib.agent.DefaultVerifierCryptoService | ||
import at.asitplus.wallet.lib.agent.VerifierCryptoService | ||
import at.asitplus.wallet.lib.jws.JwsExtensions.extractSignatureValues | ||
import io.github.aakira.napier.Napier | ||
import kotlinx.serialization.cbor.ByteStringWrapper | ||
|
||
/** | ||
* Creates and parses COSE objects. | ||
*/ | ||
interface CoseService { | ||
|
||
/** | ||
* Appends correct values for [CoseHeader.kid], [CoseHeader.algorithm], | ||
* if the corresponding options are set | ||
*/ | ||
suspend fun createSignedCose( | ||
protectedHeader: CoseHeader, | ||
unprotectedHeader: CoseHeader, | ||
payload: ByteArray, | ||
addKeyId: Boolean = true, | ||
): KmmResult<CoseSigned> | ||
} | ||
|
||
interface VerifierCoseService { | ||
|
||
fun verifyCose(coseSigned: CoseSigned, signer: CoseKey): KmmResult<Boolean> | ||
|
||
} | ||
|
||
class DefaultCoseService(private val cryptoService: CryptoService) : CoseService { | ||
|
||
override suspend fun createSignedCose( | ||
protectedHeader: CoseHeader, | ||
unprotectedHeader: CoseHeader, | ||
payload: ByteArray, | ||
addKeyId: Boolean, | ||
): KmmResult<CoseSigned> { | ||
var copy = protectedHeader.copy(algorithm = cryptoService.coseAlgorithm) | ||
if (addKeyId) | ||
copy = copy.copy(kid = cryptoService.identifier) | ||
|
||
val signatureInput = CoseSignatureInput( | ||
contextString = "Signature1", | ||
protectedHeader = ByteStringWrapper(copy), | ||
payload = payload, | ||
).serialize() | ||
|
||
val signature = cryptoService.sign(signatureInput).getOrElse { | ||
Napier.w("No signature from native code", it) | ||
return KmmResult.failure(it) | ||
} | ||
val rawSignature = signature.extractSignatureValues(cryptoService.coseAlgorithm.signatureValueLength) | ||
return KmmResult.success( | ||
CoseSigned(ByteStringWrapper(copy), unprotectedHeader, payload, rawSignature) | ||
) | ||
} | ||
} | ||
|
||
class DefaultVerifierCoseService( | ||
private val cryptoService: VerifierCryptoService = DefaultVerifierCryptoService() | ||
) : VerifierCoseService { | ||
|
||
/** | ||
* Verifiers the signature of [coseSigned] by using [signer]. | ||
*/ | ||
override fun verifyCose(coseSigned: CoseSigned, signer: CoseKey): KmmResult<Boolean> { | ||
val signatureInput = CoseSignatureInput( | ||
contextString = "Signature1", | ||
protectedHeader = ByteStringWrapper(coseSigned.protectedHeader.value), | ||
payload = coseSigned.payload, | ||
).serialize() | ||
|
||
val algorithm = coseSigned.protectedHeader.value.algorithm | ||
?: return KmmResult.failure(IllegalArgumentException("Algorithm not specified")) | ||
val verified = cryptoService.verify(signatureInput, coseSigned.signature, algorithm, signer) | ||
val result = verified.getOrElse { | ||
Napier.w("No verification from native code", it) | ||
return KmmResult.failure(it) | ||
} | ||
return KmmResult.success(result) | ||
} | ||
} | ||
|
||
|
||
|
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
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
44 changes: 44 additions & 0 deletions
44
vclib/src/commonTest/kotlin/at/asitplus/wallet/lib/cbor/CoseServiceTest.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,44 @@ | ||
package at.asitplus.wallet.lib.cbor | ||
|
||
import at.asitplus.wallet.lib.agent.CryptoService | ||
import at.asitplus.wallet.lib.agent.DefaultCryptoService | ||
import io.kotest.core.spec.style.FreeSpec | ||
import io.kotest.matchers.nulls.shouldNotBeNull | ||
import io.kotest.matchers.shouldBe | ||
import io.matthewnelson.component.encoding.base16.encodeBase16 | ||
import kotlin.random.Random | ||
|
||
class CoseServiceTest : FreeSpec({ | ||
|
||
lateinit var cryptoService: CryptoService | ||
lateinit var coseService: CoseService | ||
lateinit var verifierCoseService: VerifierCoseService | ||
lateinit var randomPayload: ByteArray | ||
|
||
beforeEach { | ||
cryptoService = DefaultCryptoService() | ||
coseService = DefaultCoseService(cryptoService) | ||
verifierCoseService = DefaultVerifierCoseService() | ||
randomPayload = Random.nextBytes(32) | ||
} | ||
|
||
"signed object with bytes can be verified" { | ||
val signed = | ||
coseService.createSignedCose(CoseHeader(algorithm = CoseAlgorithm.ES256), CoseHeader(), randomPayload, true) | ||
.getOrThrow() | ||
signed.shouldNotBeNull() | ||
println(signed.serialize().encodeBase16()) | ||
|
||
signed.payload shouldBe randomPayload | ||
signed.signature.shouldNotBeNull() | ||
|
||
// TODO activate once serialization works | ||
//val parsed = CoseSigned.deserialize(signed.serialize()) | ||
//parsed.shouldNotBeNull() | ||
val parsed = signed | ||
|
||
val result = verifierCoseService.verifyCose(parsed, cryptoService.toCoseKey()).getOrThrow() | ||
result shouldBe true | ||
} | ||
|
||
}) |
Oops, something went wrong.