Skip to content

Commit

Permalink
place all algorithm related stuff in SupportedProofType.Kt; improve C…
Browse files Browse the repository at this point in the history
…WTProofSigner and JWSProofSigner; place all credential configuration filtering in CredentialConfigurationFilter.Kt; deprecate Constants; more testing
  • Loading branch information
vkanellopoulos committed Jun 5, 2024
1 parent 7114733 commit 0370dd2
Show file tree
Hide file tree
Showing 16 changed files with 473 additions and 182 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ import android.content.Context
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import eu.europa.ec.eudi.openid4vci.*
import eu.europa.ec.eudi.wallet.document.Constants.EU_PID_DOCTYPE
import eu.europa.ec.eudi.wallet.document.CreateIssuanceRequestResult
import eu.europa.ec.eudi.wallet.document.DocumentManager
import eu.europa.ec.eudi.wallet.issue.openid4vci.CWTProofSigner
import eu.europa.ec.eudi.wallet.issue.openid4vci.ProofSigner
import eu.europa.ec.eudi.wallet.issue.openid4vci.SupportedProofAlgorithm
import eu.europa.ec.eudi.wallet.issue.openid4vci.UserAuthRequiredException
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.junit.Assert
Expand Down Expand Up @@ -67,13 +67,13 @@ class CWTProofSignerTest {
.build()


val issuanceRequestResult = documentManager.createIssuanceRequest(EU_PID_DOCTYPE, false)
val issuanceRequestResult = documentManager.createIssuanceRequest("eu.europa.ec.eudiw.pid.1", false)
assertTrue(issuanceRequestResult is CreateIssuanceRequestResult.Success)

val issuanceRequest =
(issuanceRequestResult as CreateIssuanceRequestResult.Success).issuanceRequest

val proofSigner = CWTProofSigner(issuanceRequest, CoseAlgorithm.ES256, CoseCurve.P_256)
val proofSigner = CWTProofSigner(issuanceRequest, SupportedProofAlgorithm.Cose.ES256_P_256)
assertEquals(CoseAlgorithm.ES256, proofSigner.popSigner.algorithm)
assertEquals(CoseCurve.P_256, proofSigner.popSigner.curve)

Expand All @@ -97,13 +97,13 @@ class CWTProofSignerTest {
.build()


val issuanceRequestResult = documentManager.createIssuanceRequest(EU_PID_DOCTYPE, false)
val issuanceRequestResult = documentManager.createIssuanceRequest("eu.europa.ec.eudiw.pid.1", false)
assertTrue(issuanceRequestResult is CreateIssuanceRequestResult.Success)

val issuanceRequest =
(issuanceRequestResult as CreateIssuanceRequestResult.Success).issuanceRequest

val proofSigner = CWTProofSigner(issuanceRequest, CoseAlgorithm.ES256, CoseCurve.P_256)
val proofSigner = CWTProofSigner(issuanceRequest, SupportedProofAlgorithm.Cose.ES256_P_256)
assertEquals(CoseAlgorithm.ES256, proofSigner.popSigner.algorithm)
assertEquals(CoseCurve.P_256, proofSigner.popSigner.curve)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,17 @@ import android.content.Context
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.nimbusds.jose.JOSEObjectType
import com.nimbusds.jose.JWSAlgorithm
import com.nimbusds.jose.JWSHeader
import com.nimbusds.jose.crypto.ECDSAVerifier
import com.nimbusds.jwt.JWTClaimsSet
import com.nimbusds.jwt.SignedJWT
import eu.europa.ec.eudi.openid4vci.CNonce
import eu.europa.ec.eudi.openid4vci.JwtBindingKey
import eu.europa.ec.eudi.wallet.document.Constants.EU_PID_DOCTYPE
import eu.europa.ec.eudi.wallet.document.CreateIssuanceRequestResult
import eu.europa.ec.eudi.wallet.document.DocumentManager
import eu.europa.ec.eudi.wallet.issue.openid4vci.JWSProofSigner
import eu.europa.ec.eudi.wallet.issue.openid4vci.ProofSigner
import eu.europa.ec.eudi.wallet.issue.openid4vci.SupportedProofAlgorithm
import eu.europa.ec.eudi.wallet.issue.openid4vci.UserAuthRequiredException
import org.junit.Assert
import org.junit.Assert.assertEquals
Expand Down Expand Up @@ -69,13 +68,13 @@ class JWSProofSignerTest {
.build()


val issuanceRequestResult = documentManager.createIssuanceRequest(EU_PID_DOCTYPE, false)
val issuanceRequestResult = documentManager.createIssuanceRequest("eu.europa.ec.eudiw.pid.1", false)
assertTrue(issuanceRequestResult is CreateIssuanceRequestResult.Success)

val issuanceRequest =
(issuanceRequestResult as CreateIssuanceRequestResult.Success).issuanceRequest

val proofSigner = JWSProofSigner(issuanceRequest, JWSAlgorithm.ES256)
val proofSigner = JWSProofSigner(issuanceRequest, SupportedProofAlgorithm.Jws.ES256)
val algorithm = proofSigner.popSigner.algorithm

assertTrue(proofSigner.popSigner.bindingKey is JwtBindingKey.Jwk)
Expand Down Expand Up @@ -108,13 +107,13 @@ class JWSProofSignerTest {
.enableUserAuth(false)
.build()

val issuanceRequestResult = documentManager.createIssuanceRequest(EU_PID_DOCTYPE, false)
val issuanceRequestResult = documentManager.createIssuanceRequest("eu.europa.ec.eudiw.pid.1", false)
assertTrue(issuanceRequestResult is CreateIssuanceRequestResult.Success)

val issuanceRequest =
(issuanceRequestResult as CreateIssuanceRequestResult.Success).issuanceRequest

val proofSigner = JWSProofSigner(issuanceRequest, JWSAlgorithm.ES256)
val proofSigner = JWSProofSigner(issuanceRequest, SupportedProofAlgorithm.Jws.ES256)
val algorithm = proofSigner.popSigner.algorithm

assertTrue(proofSigner.popSigner.bindingKey is JwtBindingKey.Jwk)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
/*
* Copyright (c) 2023 European Commission
* Copyright (c) 2023-2024 European Commission
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package eu.europa.ec.eudi.wallet.document
Expand All @@ -21,6 +21,7 @@ package eu.europa.ec.eudi.wallet.document
*
* @constructor Create empty Constants
*/
@Deprecated(message = "Will be removed completely in following version. Replace with actual values.")
object Constants {

// EU PID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,8 @@
package eu.europa.ec.eudi.wallet.issue.openid4vci

import com.nimbusds.jose.jwk.JWK
import eu.europa.ec.eudi.openid4vci.CoseAlgorithm
import eu.europa.ec.eudi.openid4vci.CoseCurve
import eu.europa.ec.eudi.openid4vci.CwtBindingKey
import eu.europa.ec.eudi.openid4vci.PopSigner
import eu.europa.ec.eudi.wallet.document.Algorithm
import eu.europa.ec.eudi.wallet.document.IssuanceRequest

/**
Expand All @@ -36,26 +33,20 @@ import eu.europa.ec.eudi.wallet.document.IssuanceRequest
*/
internal class CWTProofSigner(
private val issuanceRequest: IssuanceRequest,
private val coseAlgorithm: CoseAlgorithm,
private val coseCurve: CoseCurve
private val supportedProofAlgorithm: SupportedProofAlgorithm.Cose
) : ProofSigner() {

/**
* The JWK of the public key.
*/
private val jwk = JWK.parseFromPEMEncodedObjects(issuanceRequest.publicKey.pem)
override val popSigner: PopSigner.Cwt = PopSigner.Cwt(
algorithm = coseAlgorithm,
curve = coseCurve,
algorithm = supportedProofAlgorithm.coseAlgorithm,
curve = supportedProofAlgorithm.coseCurve,
bindingKey = CwtBindingKey.CoseKey(jwk),
sign = this::sign
)

private val algorithm
get() = Pair(coseAlgorithm, coseCurve).let {
CWTAlgorithmMap[it] ?: throw UnsupportedAlgorithmException()
}

/**
* Signs the signing input with the authentication key from issuance request for the given algorithm and curve.
* @param signingInput The input to sign.
Expand All @@ -64,12 +55,6 @@ internal class CWTProofSigner(
* @return The signature of the signing input.
*/
fun sign(signingInput: ByteArray): ByteArray {
return doSign(issuanceRequest, signingInput, algorithm)
}

companion object {
private val CWTAlgorithmMap = mapOf(
Pair(CoseAlgorithm.ES256, CoseCurve.P_256) to Algorithm.SHA256withECDSA,
)
return doSign(issuanceRequest, signingInput, supportedProofAlgorithm.signAlgorithmName)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2024 European Commission
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package eu.europa.ec.eudi.wallet.issue.openid4vci

import eu.europa.ec.eudi.openid4vci.CredentialConfiguration
import eu.europa.ec.eudi.openid4vci.MsoMdocCredential
import kotlin.reflect.KClass

internal fun interface CredentialConfigurationFilter {
operator fun invoke(conf: CredentialConfiguration): Boolean

/**
*
*/
class DocTypeFilterFactory(private val docType: String) : CredentialConfigurationFilter {
override fun invoke(conf: CredentialConfiguration): Boolean {
return conf is MsoMdocCredential && conf.docType == docType
}
}

class FormatFilterFactory<in F : CredentialConfiguration>(private val format: KClass<F>) :
CredentialConfigurationFilter {
override fun invoke(conf: CredentialConfiguration): Boolean {
return format.isInstance(conf)
}
}

/**
* Default [CredentialConfigurationFilter] implementations for credential format and proof type.
* @property MsoMdocFormatFilter filter for credential format
* @property ProofTypeFilter filter for proof type
*/
companion object {
@JvmSynthetic
internal val MsoMdocFormatFilter: CredentialConfigurationFilter =
FormatFilterFactory(MsoMdocCredential::class)

@JvmSynthetic
internal val ProofTypeFilter: CredentialConfigurationFilter = CredentialConfigurationFilter { conf ->
SupportedProofType.selectProofType(conf)?.selectAlgorithm(conf)?.let { true } ?: false
}


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ import eu.europa.ec.eudi.wallet.document.DocumentId
import eu.europa.ec.eudi.wallet.document.DocumentManager
import eu.europa.ec.eudi.wallet.document.IssuanceRequest
import eu.europa.ec.eudi.wallet.internal.mainExecutor
import eu.europa.ec.eudi.wallet.issue.openid4vci.CredentialConfigurationFilter.Companion.MsoMdocFormatFilter
import eu.europa.ec.eudi.wallet.issue.openid4vci.CredentialConfigurationFilter.Companion.ProofTypeFilter
import eu.europa.ec.eudi.wallet.issue.openid4vci.CredentialConfigurationFilter.DocTypeFilterFactory
import eu.europa.ec.eudi.wallet.issue.openid4vci.IssueEvent.Companion.failure
import eu.europa.ec.eudi.wallet.issue.openid4vci.ProofSigner.UserAuthStatus
import kotlinx.coroutines.*
Expand Down Expand Up @@ -59,7 +62,7 @@ internal class DefaultOpenId4VciManager(
val credentialConfigurationId =
credentialIssuerMetadata.credentialConfigurationsSupported.filter { (_, conf) ->
listOf(
FormatFilter,
MsoMdocFormatFilter,
DocTypeFilterFactory(docType),
ProofTypeFilter
).all { filter -> filter(conf) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,32 @@

package eu.europa.ec.eudi.wallet.issue.openid4vci

import com.nimbusds.jose.JWSAlgorithm
import eu.europa.ec.eudi.openid4vci.CoseAlgorithm
import eu.europa.ec.eudi.openid4vci.CoseCurve
import eu.europa.ec.eudi.openid4vci.ProofType

internal class UserAuthRequiredException : Throwable()
class UnsupportedAlgorithmException : Throwable()
class UnsupportedProofTypeException : Throwable()
class UnsupportedAlgorithmException internal constructor(supportedAlgorithms: Collection<Any>? = null) : Throwable(
message = supportedAlgorithms?.joinToString(", ") {
when (it) {
is Pair<*, *> -> when (val alg = it.first) {
is CoseAlgorithm -> when (val crv = it.second) {
is CoseCurve -> "${alg.name()}:${crv.name()}"
else -> ""
}

else -> null
}

is JWSAlgorithm -> it.name
is String -> it
else -> null
}?.let { msg -> "Supported algorithms are: $msg" } ?: ""
}
)

class UnsupportedProofTypeException internal constructor(supportedProofTypes: Collection<ProofType>? = null) :
Throwable(
message = "Supported proof types are: " + supportedProofTypes?.joinToString(", ") { it.name }
)
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ import com.nimbusds.jose.jwk.JWK
import com.nimbusds.jose.util.Base64URL
import eu.europa.ec.eudi.openid4vci.JwtBindingKey
import eu.europa.ec.eudi.openid4vci.PopSigner
import eu.europa.ec.eudi.openid4vci.ProofType
import eu.europa.ec.eudi.openid4vci.ProofTypeMeta
import eu.europa.ec.eudi.wallet.document.Algorithm
import eu.europa.ec.eudi.wallet.document.IssuanceRequest

/**
Expand All @@ -41,7 +38,7 @@ import eu.europa.ec.eudi.wallet.document.IssuanceRequest
*/
internal class JWSProofSigner(
private val issuanceRequest: IssuanceRequest,
jwsAlgorithm: JWSAlgorithm
private val supportedProofAlgorithm: SupportedProofAlgorithm.Jws
) : ProofSigner(), JWSSigner {

private val jcaContext = JCAContext()
Expand All @@ -52,32 +49,29 @@ internal class JWSProofSigner(
private val jwk = JWK.parseFromPEMEncodedObjects(issuanceRequest.publicKey.pem)

override val popSigner: PopSigner.Jwt = PopSigner.Jwt(
algorithm = jwsAlgorithm,
algorithm = supportedProofAlgorithm.algorithm,
bindingKey = JwtBindingKey.Jwk(jwk),
jwsSigner = this
)

override fun getJCAContext(): JCAContext = jcaContext

override fun supportedJWSAlgorithms(): MutableSet<JWSAlgorithm> {
return (SupportedProofTypes[ProofType.JWT] as ProofTypeMeta.Jwt).algorithms.toMutableSet()
return SupportedProofType.SupportedProofTypes.filterIsInstance<SupportedProofType.Jwt>()
.firstOrNull()?.jwsAlgorithms?.toMutableSet() ?: mutableSetOf(supportedProofAlgorithm.algorithm)
}

override fun sign(header: JWSHeader, signingInput: ByteArray): Base64URL {
val alg = JWSAlgorithmsMap[header.algorithm] ?: throw JOSEException(
AlgorithmSupportMessage.unsupportedJWSAlgorithm(
header.algorithm,
supportedJWSAlgorithms()
if (header.algorithm != supportedProofAlgorithm.algorithm) {
throw JOSEException(
AlgorithmSupportMessage.unsupportedJWSAlgorithm(
header.algorithm,
supportedJWSAlgorithms()
)
)
)
return doSign(issuanceRequest, signingInput, alg).let { signature ->
}
return doSign(issuanceRequest, signingInput, supportedProofAlgorithm.signAlgorithmName).let { signature ->
Base64URL.encode(signature.derToJose(header.algorithm))
}
}

companion object {
private val JWSAlgorithmsMap = mapOf(
JWSAlgorithm.ES256 to Algorithm.SHA256withECDSA,
)
}
}
Loading

0 comments on commit 0370dd2

Please sign in to comment.