Skip to content

Commit

Permalink
Fix Encoding-Decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
n0900 committed Dec 7, 2023
1 parent 71256ae commit 8c57d91
Show file tree
Hide file tree
Showing 14 changed files with 246 additions and 249 deletions.
2 changes: 1 addition & 1 deletion kmp-crypto
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class MessageWrapper(

private fun parseJwsMessage(joseObject: JwsSigned, serialized: String): ReceivedMessage {
Napier.d("Parsing JWS ${joseObject.serialize()}")
if (!verifierJwsService.verifyJwsObject(joseObject, serialized))
if (!verifierJwsService.verifyJwsObject(joseObject))
return ReceivedMessage.Error
.also { Napier.w("Signature invalid") }
if (joseObject.header.contentType == JwsContentTypeConstants.DIDCOMM_PLAIN_JSON) {
Expand Down Expand Up @@ -99,7 +99,7 @@ class MessageWrapper(
JwsContentTypeConstants.DIDCOMM_SIGNED_JSON,
jwm.serialize().encodeToByteArray(),
JwsContentTypeConstants.DIDCOMM_PLAIN_JSON
)
)?.serialize()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ class OidcSiopVerifier(
JwsHeader(algorithm = JwsAlgorithm.ES256),
requestObjectSerialized.encodeToByteArray(),
true
)
)?.serialize()
return AuthenticationRequestParameters(clientId = relyingPartyUrl, request = signedJws)
}

Expand Down Expand Up @@ -293,7 +293,7 @@ class OidcSiopVerifier(
val jwsSigned = JwsSigned.parse(idTokenJws)
?: return AuthnResponseResult.ValidationError("idToken", params.state)
.also { Napier.w("Could not parse JWS from idToken: $idTokenJws") }
if (!verifierJwsService.verifyJwsObject(jwsSigned, idTokenJws))
if (!verifierJwsService.verifyJwsObject(jwsSigned))
return AuthnResponseResult.ValidationError("idToken", params.state)
.also { Napier.w { "JWS of idToken not verified: $idTokenJws" } }
val idToken = IdToken.deserialize(jwsSigned.payload.decodeToString())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ class OidcSiopWallet(
private fun extractRequestObject(params: AuthenticationRequestParameters): AuthenticationRequestParameters? {
params.request?.let { requestObject ->
JwsSigned.parse(requestObject)?.let { jws ->
if (verifierJwsService.verifyJwsObject(jws, requestObject)) {
if (verifierJwsService.verifyJwsObject(jws)) {
return kotlin.runCatching {
jsonSerializer.decodeFromString<AuthenticationRequestParameters>(jws.payload.decodeToString())
}.getOrNull()
Expand Down Expand Up @@ -224,8 +224,8 @@ class OidcSiopWallet(
nonce = params.nonce,
)
val jwsPayload = idToken.serialize().encodeToByteArray()
val jwsHeader = JwsHeader(JwsAlgorithm.ES256)
val signedIdToken = jwsService.createSignedJwsAddingParams(jwsHeader, jwsPayload)
val jwsHeader = JwsHeader(algorithm = JwsAlgorithm.ES256)
val signedIdToken = jwsService.createSignedJwsAddingParams(jwsHeader, jwsPayload)?.serialize()
?: return KmmResult.failure<AuthenticationResponseParameters>(OAuth2Exception(Errors.USER_CANCELLED))
.also { Napier.w("Could not sign id_token") }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class WalletService(
).serialize().encodeToByteArray(),
addKeyId = true,
addJsonWebKey = true
)!!
)!!.serialize()
)
return when (credentialScheme.credentialFormat) {
ConstantIndex.CredentialFormat.ISO_18013 -> CredentialRequestParameters(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ class HolderAgent(
val jws = jwsService.createSignedJwt(JwsContentTypeConstants.JWT, jwsPayload)
?: return null
.also { Napier.w("Could not create JWS for presentation") }
return Holder.CreatePresentationResult.Signed(jws)
return Holder.CreatePresentationResult.Signed(jws.serialize())
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ class IssuerAgent(

private suspend fun wrapVcInJws(vc: VerifiableCredential): String? {
val jwsPayload = vc.toJws().serialize().encodeToByteArray()
return jwsService.createSignedJwt(JwsContentTypeConstants.JWT, jwsPayload)
return jwsService.createSignedJwt(JwsContentTypeConstants.JWT, jwsPayload)?.serialize()
}

private fun getRevocationListUrlFor(timePeriod: Int) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class Validator(
val jws = JwsSigned.parse(it)
?: return false
.also { Napier.w("Revocation List: Could not parse JWS") }
if (!verifierJwsService.verifyJwsObject(jws, it))
if (!verifierJwsService.verifyJwsObject(jws))
return false
.also { Napier.w("Revocation List: Signature invalid") }
val payload = jws.payload.decodeToString()
Expand Down Expand Up @@ -140,7 +140,7 @@ class Validator(
val jws = JwsSigned.parse(it)
?: return Verifier.VerifyPresentationResult.InvalidStructure(it)
.also { Napier.w("VP: Could not parse JWS") }
if (!verifierJwsService.verifyJwsObject(jws, it))
if (!verifierJwsService.verifyJwsObject(jws))
return Verifier.VerifyPresentationResult.InvalidStructure(it)
.also { Napier.w("VP: Signature invalid") }
val payload = jws.payload.decodeToString()
Expand Down Expand Up @@ -290,7 +290,7 @@ class Validator(
val jws = JwsSigned.parse(it)
?: return Verifier.VerifyCredentialResult.InvalidStructure(it)
.also { Napier.w("VC: Could not parse JWS") }
if (!verifierJwsService.verifyJwsObject(jws, it))
if (!verifierJwsService.verifyJwsObject(jws))
return Verifier.VerifyCredentialResult.InvalidStructure(it)
.also { Napier.w("VC: Signature invalid") }
val payload = jws.payload.decodeToString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import at.asitplus.wallet.lib.agent.CryptoService
import at.asitplus.wallet.lib.agent.DefaultVerifierCryptoService
import at.asitplus.wallet.lib.agent.VerifierCryptoService
import io.github.aakira.napier.Napier
import io.ktor.util.*
import io.matthewnelson.encoding.core.Decoder.Companion.decodeToByteArray
import io.matthewnelson.encoding.core.Encoder.Companion.encodeToByteArray
import io.matthewnelson.encoding.core.Encoder.Companion.encodeToString
Expand All @@ -23,9 +24,9 @@ interface JwsService {
type: String,
payload: ByteArray,
contentType: String? = null
): String?
): JwsSigned?

suspend fun createSignedJws(header: JwsHeader, payload: ByteArray): String?
suspend fun createSignedJws(header: JwsHeader, payload: ByteArray): JwsSigned?

/**
* Appends correct values for [JweHeader.keyId], [JwsHeader.algorithm] and [JwsHeader.jsonWebKey],
Expand All @@ -36,7 +37,7 @@ interface JwsService {
payload: ByteArray,
addKeyId: Boolean = true,
addJsonWebKey: Boolean = true
): String?
): JwsSigned?

fun encryptJweObject(
type: String,
Expand All @@ -53,7 +54,7 @@ interface JwsService {

interface VerifierJwsService {

fun verifyJwsObject(jwsObject: JwsSigned, serialized: String? = null): Boolean
fun verifyJwsObject(jwsObject: JwsSigned): Boolean

fun verifyJws(jwsObject: JwsSigned, signer: JsonWebKey): Boolean

Expand All @@ -65,7 +66,7 @@ class DefaultJwsService(private val cryptoService: CryptoService) : JwsService {
type: String,
payload: ByteArray,
contentType: String?
): String? {
): JwsSigned? {
val jwsHeader = JwsHeader(
algorithm = cryptoService.algorithm,
keyId = cryptoService.publicKey.keyId,
Expand All @@ -75,29 +76,28 @@ class DefaultJwsService(private val cryptoService: CryptoService) : JwsService {
return createSignedJws(jwsHeader, payload)
}

override suspend fun createSignedJws(header: JwsHeader, payload: ByteArray): String? {
override suspend fun createSignedJws(header: JwsHeader, payload: ByteArray): JwsSigned? {
if (header.algorithm != cryptoService.algorithm
|| header.keyId?.let { it != cryptoService.publicKey.keyId } == true
|| header.jsonWebKey?.let { it != cryptoService.jsonWebKey } == true
) {
return null.also { Napier.w("Algorithm or keyId not matching to cryptoService") }
}
val signatureInput = header.serialize().encodeToByteArray().encodeToString(Base64UrlStrict) +
"." + payload.encodeToString(Base64UrlStrict)
val signatureInputBytes = signatureInput.encodeToByteArray()

val signatureInputBytes = prepareJwsSignatureInput(header, payload).encodeToByteArray()
val signature = cryptoService.sign(signatureInputBytes).getOrElse {
Napier.w("No signature from native code", it)
return null
}
return JwsSigned(header, payload, signature, signatureInput).serialize()
return JwsSigned(header, payload, signature)
}

override suspend fun createSignedJwsAddingParams(
header: JwsHeader,
payload: ByteArray,
addKeyId: Boolean,
addJsonWebKey: Boolean
): String? {
): JwsSigned? {
var copy = header.copy(algorithm = cryptoService.algorithm)
if (addKeyId)
copy = copy.copy(keyId = cryptoService.publicKey.keyId)
Expand Down Expand Up @@ -211,7 +211,7 @@ class DefaultVerifierJwsService(
* Verifies the signature of [jwsObject], by extracting the public key from [JwsHeader.keyId] (`kid`),
* or from [JwsHeader.jsonWebKey] (`jwk`), or from [JwsHeader.certificateChain] (`x5c`).
*/
override fun verifyJwsObject(jwsObject: JwsSigned, serialized: String?): Boolean {
override fun verifyJwsObject(jwsObject: JwsSigned): Boolean {
val header = jwsObject.header
val publicKey = header.publicKey
?: return false
Expand All @@ -222,11 +222,12 @@ class DefaultVerifierJwsService(
algorithm = header.algorithm,
publicKey = publicKey
)
val falseVar = false //workaround kotlin bug for linking xcframework
return verified.getOrElse {
Napier.w("No verification from native code")
falseVar
}
// val falseVar = false //workaround kotlin bug for linking xcframework
// return verified.getOrElse {
// Napier.w("No verification from native code", it)
// falseVar
// }
return verified.getOrThrow()
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package at.asitplus.wallet.lib.agent

import at.asitplus.crypto.datatypes.JwsAlgorithm
import at.asitplus.crypto.datatypes.io.Base64UrlStrict
import at.asitplus.crypto.datatypes.jws.JwsContentTypeConstants
import at.asitplus.crypto.datatypes.jws.JwsHeader
import at.asitplus.crypto.datatypes.jws.JwsSigned
import at.asitplus.crypto.datatypes.jws.prepareJwsSignatureInput
import at.asitplus.wallet.lib.data.*
import at.asitplus.wallet.lib.jws.DefaultJwsService
import at.asitplus.wallet.lib.jws.JwsService
Expand All @@ -14,7 +14,6 @@ import io.kotest.datatest.withData
import io.kotest.matchers.nulls.shouldNotBeNull
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeInstanceOf
import io.matthewnelson.encoding.core.Encoder.Companion.encodeToString
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlin.time.Duration.Companion.hours
Expand Down Expand Up @@ -243,10 +242,8 @@ class ValidatorVcTest : FreeSpec() {
verifier.identifier,
attributeTypes = listOf(ConstantIndex.AtomicAttribute2023.vcType)
).getOrThrow()
) {
issueCredential(it, expirationDate = null)
.let { wrapVcInJws(it) }
.let { signJws(it) }
) { it ->
signJws(wrapVcInJws(issueCredential(it, expirationDate = null)))
?.let {
verifier.verifyVcJws(it).shouldBeInstanceOf<Verifier.VerifyCredentialResult.Success>()
}
Expand All @@ -260,10 +257,13 @@ class ValidatorVcTest : FreeSpec() {
verifier.identifier,
attributeTypes = listOf(ConstantIndex.AtomicAttribute2023.vcType)
).getOrThrow()
) {
issueCredential(it, expirationDate = Clock.System.now() + 1.hours)
.let { wrapVcInJws(it, expirationDate = Clock.System.now() - 1.hours) }
.let { signJws(it) }
) { it ->
signJws(
wrapVcInJws(
issueCredential(it, expirationDate = Clock.System.now() + 1.hours),
expirationDate = Clock.System.now() - 1.hours
)
)
?.let {
verifier.verifyVcJws(it)
.shouldBeInstanceOf<Verifier.VerifyCredentialResult.InvalidStructure>()
Expand All @@ -278,18 +278,16 @@ class ValidatorVcTest : FreeSpec() {
verifier.identifier,
attributeTypes = listOf(ConstantIndex.AtomicAttribute2023.vcType)
).getOrThrow()
) {
it.let {
issueCredential(it, expirationDate = Clock.System.now() + 1.hours)
) { it ->
signJws(
wrapVcInJws(
issueCredential(it, expirationDate = Clock.System.now() + 1.hours),
expirationDate = Clock.System.now() + 2.hours
)
)?.let {
verifier.verifyVcJws(it)
.shouldBeInstanceOf<Verifier.VerifyCredentialResult.InvalidStructure>()
}
.let {
wrapVcInJws(it, expirationDate = Clock.System.now() + 2.hours)
}
.let { signJws(it) }
?.let {
verifier.verifyVcJws(it)
.shouldBeInstanceOf<Verifier.VerifyCredentialResult.InvalidStructure>()
}
}
}

Expand All @@ -300,12 +298,8 @@ class ValidatorVcTest : FreeSpec() {
verifier.identifier,
attributeTypes = listOf(ConstantIndex.AtomicAttribute2023.vcType)
).getOrThrow()
) {
it.let { issueCredential(it) }
.let {
wrapVcInJws(it, issuanceDate = Clock.System.now() + 2.hours)
}
.let { signJws(it) }
) { it ->
signJws(wrapVcInJws(issueCredential(it), issuanceDate = Clock.System.now() + 2.hours))
?.let {
verifier.verifyVcJws(it)
.shouldBeInstanceOf<Verifier.VerifyCredentialResult.InvalidStructure>()
Expand Down Expand Up @@ -338,10 +332,8 @@ class ValidatorVcTest : FreeSpec() {
verifier.identifier,
attributeTypes = listOf(ConstantIndex.AtomicAttribute2023.vcType)
).getOrThrow()
) {
issueCredential(it, issuanceDate = Clock.System.now() - 1.hours)
.let { wrapVcInJws(it, issuanceDate = Clock.System.now()) }
.let { signJws(it) }
) { it ->
signJws(wrapVcInJws(issueCredential(it, issuanceDate = Clock.System.now() - 1.hours), issuanceDate = Clock.System.now()))
?.let {
verifier.verifyVcJws(it)
.shouldBeInstanceOf<Verifier.VerifyCredentialResult.InvalidStructure>()
Expand Down Expand Up @@ -405,7 +397,7 @@ class ValidatorVcTest : FreeSpec() {
private suspend fun signJws(vcJws: VerifiableCredentialJws): String? {
val vcSerialized = vcJws.serialize()
val jwsPayload = vcSerialized.encodeToByteArray()
return issuerJwsService.createSignedJwt(JwsContentTypeConstants.JWT, jwsPayload)
return issuerJwsService.createSignedJwt(JwsContentTypeConstants.JWT, jwsPayload)?.serialize()
}

private suspend fun wrapVcInJwsWrongKey(vcJws: VerifiableCredentialJws): String? {
Expand All @@ -415,13 +407,10 @@ class ValidatorVcTest : FreeSpec() {
type = JwsContentTypeConstants.JWT
)
val jwsPayload = vcJws.serialize().encodeToByteArray()
val signatureInput =
jwsHeader.serialize().encodeToByteArray().encodeToString(Base64UrlStrict) +
"." + jwsPayload.encodeToString(Base64UrlStrict)
val signatureInputBytes = signatureInput.encodeToByteArray()
val signature = issuerCryptoService.sign(signatureInputBytes)
val signatureInput = prepareJwsSignatureInput(jwsHeader, jwsPayload).encodeToByteArray()
val signature = issuerCryptoService.sign(signatureInput)
.getOrElse { return null }
return JwsSigned(jwsHeader, jwsPayload, signature, signatureInput).serialize()
return JwsSigned(jwsHeader, jwsPayload, signature).serialize()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class ValidatorVpTest : FreeSpec({
audienceId = verifier.identifier,
).serialize()
val jwsPayload = vpSerialized.encodeToByteArray()
val vpJws = holderJwsService.createSignedJwt(JwsContentTypeConstants.JWT, jwsPayload)
val vpJws = holderJwsService.createSignedJwt(JwsContentTypeConstants.JWT, jwsPayload)?.serialize()
vpJws.shouldNotBeNull()

verifier.verifyPresentation(vpJws, challenge)
Expand All @@ -156,7 +156,7 @@ class ValidatorVpTest : FreeSpec({
jwtId = vp.id,
).serialize()
val jwsPayload = vpSerialized.encodeToByteArray()
val vpJws = holderJwsService.createSignedJwt(JwsContentTypeConstants.JWT, jwsPayload)
val vpJws = holderJwsService.createSignedJwt(JwsContentTypeConstants.JWT, jwsPayload)?.serialize()
vpJws.shouldNotBeNull()

verifier.verifyPresentation(vpJws, challenge)
Expand All @@ -176,7 +176,7 @@ class ValidatorVpTest : FreeSpec({
jwtId = "wrong_jwtId",
).serialize()
val jwsPayload = vpSerialized.encodeToByteArray()
val vpJws = holderJwsService.createSignedJwt(JwsContentTypeConstants.JWT, jwsPayload)
val vpJws = holderJwsService.createSignedJwt(JwsContentTypeConstants.JWT, jwsPayload)?.serialize()
vpJws.shouldNotBeNull()

verifier.verifyPresentation(vpJws, challenge)
Expand All @@ -198,7 +198,7 @@ class ValidatorVpTest : FreeSpec({
audienceId = verifier.identifier,
).serialize()
val jwsPayload = vpSerialized.encodeToByteArray()
val vpJws = holderJwsService.createSignedJwt(JwsContentTypeConstants.JWT, jwsPayload)
val vpJws = holderJwsService.createSignedJwt(JwsContentTypeConstants.JWT, jwsPayload)?.serialize()
vpJws.shouldNotBeNull()

verifier.verifyPresentation(vpJws, challenge)
Expand Down
Loading

0 comments on commit 8c57d91

Please sign in to comment.