Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import com.nimbusds.jose.jwk.JWK
import com.nimbusds.jose.jwk.RSAKey
import com.nimbusds.jwt.SignedJWT
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch

data class KeyAttestationJWT private constructor(
Expand Down Expand Up @@ -131,4 +131,4 @@ internal suspend fun signatureVerifiedByKey(vararg tasks: suspend () -> JWK?): J
tasks.forEach {
launch { send(it()) }
}
}.first { it != null }
}.firstOrNull { it != null }
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,39 @@ internal class WalletApiEncryptionOptionalKeyAttestationsRequiredTest : BaseWall
assertEquals("Invalid proof JWT: Key Attestation 'nonce' does not match JWT Proof 'nonce'", response.errorDescription)
}

@Test
internal fun `issuance with jwt proof that contains key attestation fails when proof is signed with non-attested key`() =
runTest {
val authentication = dPoPTokenAuthentication(clock = clock)
val cNonce = generateNonce(clock.now(), 5L.minutes)
val nonAttestedProofKey = ECKeyGenerator(Curve.P_256).generate()
val attestedKey = ECKeyGenerator(Curve.P_256).generate()
val keyAttestationJwt = keyAttestationJWT(proofSigningKey = attestedKey)

val proofs = jwtProof(credentialIssuerMetadata.id, clock, cNonce, nonAttestedProofKey) {
customParam("key_attestation", keyAttestationJwt.serialize())
}.toJwtProofs()

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

assertEquals(CredentialErrorTypeTo.INVALID_PROOF, response.type)
assertEquals(
"Invalid proof JWT: Key attestation does not contain a key that verifies the jwt proof signature",
response.errorDescription,
)
}

@Test
fun `issuance with attestation proof (without 'exp' claim) is successful`() = runTest {
val authentication = dPoPTokenAuthentication(clock = clock)
Expand Down Expand Up @@ -1527,7 +1560,7 @@ private suspend fun keyAttestationJWT(
clock: Clock = Clock.System,
expiresAt: Instant = clock.now() + 1.days,
includeExpiresAt: Boolean = true,
extraKeys: () -> List<ECKey>,
extraKeys: () -> List<ECKey> = { emptyList() },
): SignedJWT {
val keyAttestationSigningKey = loadECKey("key-attestation-key.pem")
val signer = ECDSASigner(keyAttestationSigningKey)
Expand Down
Loading