Skip to content

Commit

Permalink
Add extra test cases.
Browse files Browse the repository at this point in the history
  • Loading branch information
dzarras committed Sep 26, 2024
1 parent 9fd1830 commit 4f3a914
Showing 1 changed file with 85 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ import eu.europa.ec.eudi.pidissuer.adapter.input.web.security.DPoPConfigurationP
import eu.europa.ec.eudi.pidissuer.adapter.input.web.security.DPoPTokenAuthentication
import eu.europa.ec.eudi.pidissuer.adapter.out.persistence.InMemoryCNonceRepository
import eu.europa.ec.eudi.pidissuer.adapter.out.pid.*
import eu.europa.ec.eudi.pidissuer.domain.*
import eu.europa.ec.eudi.pidissuer.domain.CNonce
import eu.europa.ec.eudi.pidissuer.domain.CredentialIssuerId
import eu.europa.ec.eudi.pidissuer.domain.CredentialIssuerMetaData
import eu.europa.ec.eudi.pidissuer.domain.Scope
import eu.europa.ec.eudi.pidissuer.port.input.*
import eu.europa.ec.eudi.pidissuer.port.out.persistence.GenerateCNonce
import kotlinx.coroutines.runBlocking
Expand Down Expand Up @@ -146,7 +149,13 @@ internal class BaseWalletApiTest {
/**
* Test cases for [WalletApi] when encryption is optional.
*/
@TestPropertySource(properties = ["issuer.credentialResponseEncryption.required=false"])
@TestPropertySource(
properties = [
"issuer.credentialResponseEncryption.required=false",
"issuer.credentialEndpoint.batchIssuance.enabled=true",
"issuer.credentialEndpoint.batchIssuance.batchSize=3",
],
)
internal class WalletApiEncryptionOptionalTest : BaseWalletApiTest() {

/**
Expand Down Expand Up @@ -252,7 +261,7 @@ internal class WalletApiEncryptionOptionalTest : BaseWalletApiTest() {
.post()
.uri(WalletApi.CREDENTIAL_ENDPOINT)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(requestByFormat())
.bodyValue(requestByFormat(proofs = ProofsTO(jwtProofs = listOf("proof"))))
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isEqualTo(HttpStatus.BAD_REQUEST)
Expand Down Expand Up @@ -322,10 +331,10 @@ internal class WalletApiEncryptionOptionalTest : BaseWalletApiTest() {
val key = ECKeyGenerator(Curve.P_256).generate()
val proof = jwtProof(credentialIssuerMetadata.id, clock, previousCNonce, key) {
jwk(key.toPublicJWK())
}
val proofs = jwtProofs(credentialIssuerMetadata.id, clock, previousCNonce, key) {
}.toProof()
val proofs = jwtProof(credentialIssuerMetadata.id, clock, previousCNonce, key) {
jwk(key.toPublicJWK())
}
}.toProofs()

val response = client()
.mutateWith(mockAuthentication(authentication))
Expand Down Expand Up @@ -355,6 +364,47 @@ internal class WalletApiEncryptionOptionalTest : BaseWalletApiTest() {
)
}

@Test
fun `fails when providing more proofs than allowed batch_size`() = runTest {
val authentication = dPoPTokenAuthentication(clock = clock)
val previousCNonce = generateCNonce(authentication.accessToken.toAuthorizationHeader(), clock)
cNonceRepository.upsertCNonce(previousCNonce)

val keys = List(5) { ECKeyGenerator(Curve.P_256).generate() }
val proofs = keys.map { key ->
jwtProof(credentialIssuerMetadata.id, clock, previousCNonce, key) {
jwk(key.toPublicJWK())
}
}.toProofs()

val response = client()
.mutateWith(mockAuthentication(authentication))
.post()
.uri(WalletApi.CREDENTIAL_ENDPOINT)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(requestByFormat(proofs = proofs))
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isBadRequest
.expectBody<IssueCredentialResponse.FailedTO>()
.returnResult()
.let { assertNotNull(it.responseBody) }

val newCNonce =
checkNotNull(cNonceRepository.loadCNonceByAccessToken(authentication.accessToken.toAuthorizationHeader()))
assertNotEquals(previousCNonce, newCNonce)

assertEquals(
IssueCredentialResponse.FailedTO(
CredentialErrorTypeTo.INVALID_PROOF,
"You can provide at most '3' proofs",
newCNonce.nonce,
newCNonce.expiresIn.toSeconds(),
),
response,
)
}

/**
* Verifies issuance success.
* Creates a CNonce value before doing the request.
Expand All @@ -371,7 +421,7 @@ internal class WalletApiEncryptionOptionalTest : BaseWalletApiTest() {
val key = ECKeyGenerator(Curve.P_256).generate()
val proof = jwtProof(credentialIssuerMetadata.id, clock, previousCNonce, key) {
jwk(key.toPublicJWK())
}
}.toProof()

val response = client()
.mutateWith(mockAuthentication(authentication))
Expand Down Expand Up @@ -413,7 +463,7 @@ internal class WalletApiEncryptionOptionalTest : BaseWalletApiTest() {
val key = ECKeyGenerator(Curve.P_256).generate()
val proof = jwtProof(credentialIssuerMetadata.id, clock, previousCNonce, key) {
jwk(key.toPublicJWK())
}
}.toProof()

val response = client()
.mutateWith(mockAuthentication(authentication))
Expand Down Expand Up @@ -462,7 +512,7 @@ internal class WalletApiEncryptionRequiredTest : BaseWalletApiTest() {
val key = ECKeyGenerator(Curve.P_256).generate()
val proof = jwtProof(credentialIssuerMetadata.id, clock, previousCNonce, key) {
jwk(key.toPublicJWK())
}
}.toProof()

val response = client()
.mutateWith(mockAuthentication(authentication))
Expand Down Expand Up @@ -502,7 +552,7 @@ internal class WalletApiEncryptionRequiredTest : BaseWalletApiTest() {
val walletKey = ECKeyGenerator(Curve.P_256).generate()
val proof = jwtProof(credentialIssuerMetadata.id, clock, previousCNonce, walletKey) {
jwk(walletKey.toPublicJWK())
}
}.toProof()
val encryptionKey = RSAKeyGenerator(4096).keyUse(KeyUse.ENCRYPTION).generate()
val encryptionParameters = encryptionParameters(encryptionKey)

Expand All @@ -526,7 +576,12 @@ internal class WalletApiEncryptionRequiredTest : BaseWalletApiTest() {
val claims = run {
val jwt = EncryptedJWT.parse(response)
.also {
it.decrypt(DefaultJWEDecrypterFactory().createJWEDecrypter(it.header, encryptionKey.toRSAPrivateKey()))
it.decrypt(
DefaultJWEDecrypterFactory().createJWEDecrypter(
it.header,
encryptionKey.toRSAPrivateKey(),
),
)
}
jwt.jwtClaimsSet
}
Expand All @@ -551,7 +606,7 @@ internal class WalletApiEncryptionRequiredTest : BaseWalletApiTest() {
val key = ECKeyGenerator(Curve.P_256).generate()
val proof = jwtProof(credentialIssuerMetadata.id, clock, previousCNonce, key) {
jwk(key.toPublicJWK())
}
}.toProof()

val response = client()
.mutateWith(mockAuthentication(authentication))
Expand Down Expand Up @@ -600,7 +655,12 @@ internal class WalletApiEncryptionRequiredTest : BaseWalletApiTest() {
.post()
.uri(WalletApi.CREDENTIAL_ENDPOINT)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(requestByCredentialIdentifier(proof = proof, credentialResponseEncryption = encryptionParameters))
.bodyValue(
requestByCredentialIdentifier(
proof = proof.toProof(),
credentialResponseEncryption = encryptionParameters,
),
)
.accept(MediaType.parseMediaType("application/jwt"))
.exchange()
.expectStatus().isOk()
Expand All @@ -615,7 +675,12 @@ internal class WalletApiEncryptionRequiredTest : BaseWalletApiTest() {
val claims = run {
val jwt = EncryptedJWT.parse(response)
.also {
it.decrypt(DefaultJWEDecrypterFactory().createJWEDecrypter(it.header, encryptionKey.toRSAPrivateKey()))
it.decrypt(
DefaultJWEDecrypterFactory().createJWEDecrypter(
it.header,
encryptionKey.toRSAPrivateKey(),
),
)
}
jwt.jwtClaimsSet
}
Expand Down Expand Up @@ -660,7 +725,7 @@ private fun dPoPTokenAuthentication(
}

private fun requestByFormat(
proof: ProofTo? = ProofTo(type = ProofTypeTO.JWT, jwt = "123456"),
proof: ProofTo? = null,
proofs: ProofsTO? = null,
credentialResponseEncryption: CredentialResponseEncryptionTO? = null,
): CredentialRequestTO =
Expand Down Expand Up @@ -697,7 +762,7 @@ private fun jwtProof(
nonce: CNonce,
key: ECKey,
headerCustomizer: JWSHeader.Builder.() -> Unit = { },
): ProofTo {
): SignedJWT {
val header = JWSHeader.Builder(JWSAlgorithm.ES256)
.type(JOSEObjectType("openid4vci-proof+jwt"))
.apply(headerCustomizer)
Expand All @@ -710,27 +775,9 @@ private fun jwtProof(
val jwt = SignedJWT(header, claims)
jwt.sign(ECDSASigner(key))

return ProofTo(type = ProofTypeTO.JWT, jwt = jwt.serialize())
return jwt
}

private fun jwtProofs(
audience: CredentialIssuerId,
clock: Clock,
nonce: CNonce,
key: ECKey,
headerCustomizer: JWSHeader.Builder.() -> Unit = { },
): ProofsTO {
val header = JWSHeader.Builder(JWSAlgorithm.ES256)
.type(JOSEObjectType("openid4vci-proof+jwt"))
.apply(headerCustomizer)
.build()
val claims = JWTClaimsSet.Builder()
.audience(audience.externalForm)
.issueTime(Date.from(clock.instant()))
.claim("nonce", nonce.nonce)
.build()
val jwt = SignedJWT(header, claims)
jwt.sign(ECDSASigner(key))

return ProofsTO(jwtProofs = listOf(jwt.serialize()))
}
private fun SignedJWT.toProof(): ProofTo = ProofTo(type = ProofTypeTO.JWT, jwt = serialize())
private fun SignedJWT.toProofs(): ProofsTO = ProofsTO(jwtProofs = listOf(serialize()))
private fun Iterable<SignedJWT>.toProofs(): ProofsTO = ProofsTO(jwtProofs = map { it.serialize() })

0 comments on commit 4f3a914

Please sign in to comment.