Skip to content

Commit

Permalink
Merge pull request #143 from a-sit-plus/feature/refactorRequestParser
Browse files Browse the repository at this point in the history
Extend RequestParsing to SignatureRequests
  • Loading branch information
n0900 authored Oct 16, 2024
2 parents 1be694e + cc90a97 commit d1c106f
Show file tree
Hide file tree
Showing 19 changed files with 420 additions and 184 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

Release 5.1.0:
- tbd
- New Class `SignatureRequestFrom` to handle signature requests by reference
- Rename `AuthenticationRequestParser` to `RequestParser`
- `RequestParser` can now handle `SignatureRequestFrom`

Release 5.0.0:
- Remove `OidcSiopWallet.newDefaultInstance()` and replace it with a constructor
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
@file:UseSerializers(SignatureRequestParameterSerializer::class)

package at.asitplus.dif.rqes

import at.asitplus.dif.rqes.serializers.Asn1EncodableBase64Serializer
import at.asitplus.dif.rqes.serializers.SignatureRequestParameterSerializer
import at.asitplus.dif.rqes.serializers.CSCSignatureRequestParameterSerializer
import at.asitplus.dif.rqes.collection_entries.Document
import at.asitplus.dif.rqes.collection_entries.DocumentDigestEntries.CscDocumentDigest
import at.asitplus.dif.rqes.enums.OperationModeEnum
Expand All @@ -15,10 +13,9 @@ import at.asitplus.signum.indispensable.asn1.ObjectIdentifier
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.UseSerializers

@Serializable(with = SignatureRequestParameterSerializer::class)
sealed interface SignatureRequestParameters {
@Serializable(with = CSCSignatureRequestParameterSerializer::class)
sealed interface CSCSignatureRequestParameters {
/**
* The credentialID as defined in the Input parameter table in `/credentials/info`
*/
Expand Down Expand Up @@ -113,7 +110,7 @@ data class SignHashParameters(
@Serializable(with = Asn1EncodableBase64Serializer::class)
val signAlgoParams: Asn1Element? = null,

) : SignatureRequestParameters {
) : CSCSignatureRequestParameters {

@Transient
val signAlgorithm: SignatureAlgorithm? = getSignAlgorithm(signAlgoOid, signAlgoParams)
Expand Down Expand Up @@ -201,7 +198,7 @@ data class SignDocParameters(
@SerialName("returnValidationInformation")
val returnValidationInformation: Boolean = false,

) : SignatureRequestParameters {
) : CSCSignatureRequestParameters {
init {
require(credentialId != null || signatureQualifier != null) { "Either credentialId or signatureQualifier must not be null (both can be present)" }
require(documentDigests != null || documents != null) { "Either documentDigests or documents must not be null (both can be present)" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ internal fun getSignAlgorithm(signAlgoOid: ObjectIdentifier, signAlgoParams: Asn
}

@Throws(Exception::class)
internal fun getHashAlgorithm(hashAlgorithmOid: ObjectIdentifier?, signatureAlgorithm: SignatureAlgorithm?) =
fun getHashAlgorithm(hashAlgorithmOid: ObjectIdentifier?, signatureAlgorithm: SignatureAlgorithm? = null) =
hashAlgorithmOid?.let {
Digest.entries.find { digest -> digest.oid == it }
} ?: when(signatureAlgorithm) {
Expand All @@ -32,4 +32,4 @@ internal fun getHashAlgorithm(hashAlgorithmOid: ObjectIdentifier?, signatureAlgo
is SignatureAlgorithm.HMAC -> signatureAlgorithm.digest
is SignatureAlgorithm.RSA -> signatureAlgorithm.digest
null -> null
} ?: throw Exception("Unknown hashing algorithm")
} ?: throw Exception("Unknown hashing algorithm defined with oid $hashAlgorithmOid or signature algorithm $signatureAlgorithm")
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import kotlinx.serialization.json.JsonObject
@Serializable
data class Document(
/**
* TODO
* base64-encoded document content to be signed, testcases weird so for now string
*/
@SerialName("document")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package at.asitplus.dif.rqes.serializers

import at.asitplus.dif.rqes.CSCSignatureRequestParameters
import at.asitplus.dif.rqes.SignDocParameters
import at.asitplus.dif.rqes.SignHashParameters
import at.asitplus.dif.rqes.SignatureRequestParameters
import kotlinx.serialization.json.JsonContentPolymorphicSerializer
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.jsonObject

object SignatureRequestParameterSerializer :
JsonContentPolymorphicSerializer<SignatureRequestParameters>(SignatureRequestParameters::class) {
object CSCSignatureRequestParameterSerializer :
JsonContentPolymorphicSerializer<CSCSignatureRequestParameters>(CSCSignatureRequestParameters::class) {
override fun selectDeserializer(element: JsonElement) = when {
"hashes" in element.jsonObject -> SignHashParameters.serializer()
else -> SignDocParameters.serializer()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package at.asitplus.dif

import at.asitplus.dif.rqes.CSCSignatureRequestParameters
import at.asitplus.dif.rqes.SignDocParameters
import at.asitplus.dif.rqes.SignHashParameters
import at.asitplus.dif.rqes.SignatureRequestParameters
import at.asitplus.signum.indispensable.X509SignatureAlgorithm
import at.asitplus.signum.indispensable.io.Base64Strict
import io.github.aakira.napier.Napier
Expand Down Expand Up @@ -155,16 +155,18 @@ class AuthenticationRequestParameterFromSerializerTest : FreeSpec({
)
)
dummyEntries.forEachIndexed { i, dummyEntry ->
"Entry ${i+1}" {
val serialized = jsonSerializer.encodeToString(SignatureRequestParameters.serializer(), dummyEntry)
"Entry ${i + 1}" {
val serialized = jsonSerializer.encodeToString(CSCSignatureRequestParameters.serializer(), dummyEntry)
.also { Napier.d("serialized ${dummyEntry::class}: $it") }
val deserialized = jsonSerializer.decodeFromString(SignatureRequestParameters.serializer(), serialized)
val deserialized =
jsonSerializer.decodeFromString(CSCSignatureRequestParameters.serializer(), serialized)

deserialized shouldBe dummyEntry
}
}
}

//TODO fix asn1 parsing
"CSC Test vectors".config(enabled = false) - {
listOf(
cscTestVectorSignHash1,
Expand All @@ -174,10 +176,11 @@ class AuthenticationRequestParameterFromSerializerTest : FreeSpec({
cscTestVectorSignDoc2,
cscTestVectorSignDoc3
).forEachIndexed { i, vec ->
"Testvector ${i+1}" - {
"Testvector ${i + 1}" - {
val expected = jsonSerializer.decodeFromString<JsonObject>(vec)
val actual = jsonSerializer.decodeFromString(SignatureRequestParameters.serializer(), vec)
val sanitycheck = jsonSerializer.decodeFromJsonElement(SignatureRequestParameters.serializer(), expected)
val actual = jsonSerializer.decodeFromString(CSCSignatureRequestParameters.serializer(), vec)
val sanitycheck =
jsonSerializer.decodeFromJsonElement(CSCSignatureRequestParameters.serializer(), expected)
"sanitycheck" {
actual shouldBe sanitycheck
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ data class AuthenticationRequestParameters(
* Optional when JAR (RFC9101) is used.
*/
@SerialName("response_type")
val responseType: String? = null,
override val responseType: String? = null,

/**
* OIDC: REQUIRED. OAuth 2.0 Client Identifier valid at the Authorization Server.
*/
@SerialName("client_id")
val clientId: String? = null,
override val clientId: String? = null,

/**
* OIDC: REQUIRED. Redirection URI to which the response will be sent. This URI MUST exactly match one of the
Expand All @@ -64,15 +64,15 @@ data class AuthenticationRequestParameters(
* parameter with a browser cookie.
*/
@SerialName("state")
val state: String? = null,
override val state: String? = null,

/**
* OIDC: OPTIONAL. String value used to associate a Client session with an ID Token, and to mitigate replay attacks.
* The value is passed through unmodified from the Authentication Request to the ID Token. Sufficient entropy MUST
* be present in the nonce values used to prevent attackers from guessing values.
*/
@SerialName("nonce")
val nonce: String? = null,
override val nonce: String? = null,

/**
* OIDC: OPTIONAL. This parameter is used to request that specific Claims be returned. The value is a JSON object
Expand Down Expand Up @@ -173,7 +173,7 @@ data class AuthenticationRequestParameters(
* scheme.
*/
@SerialName("client_id_scheme")
val clientIdScheme: OpenIdConstants.ClientIdScheme? = null,
override val clientIdScheme: OpenIdConstants.ClientIdScheme? = null,

/**
* OID4VP: OPTIONAL. String containing the Wallet's identifier. The Credential Issuer can use the discovery process
Expand Down Expand Up @@ -207,7 +207,7 @@ data class AuthenticationRequestParameters(
* authentication process to a certain endpoint using the HTTP POST method.
*/
@SerialName("response_mode")
val responseMode: OpenIdConstants.ResponseMode? = null,
override val responseMode: OpenIdConstants.ResponseMode? = null,

/**
* OID4VP: OPTIONAL. The Response URI to which the Wallet MUST send the Authorization Response using an HTTPS POST
Expand All @@ -218,7 +218,7 @@ data class AuthenticationRequestParameters(
* `invalid_request` Authorization Response error.
*/
@SerialName("response_uri")
val responseUrl: String? = null,
override val responseUrl: String? = null,

/**
* OAuth 2.0 JAR: If signed, the Authorization Request Object SHOULD contain the Claims `iss` (issuer) and `aud`
Expand Down Expand Up @@ -339,7 +339,7 @@ data class AuthenticationRequestParameters(
*/
@SerialName("clientData")
val clientData: String? = null,
) {
): RequestParameters {

override fun equals(other: Any?): Boolean {
if (this === other) return true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package at.asitplus.openid

import at.asitplus.signum.indispensable.josef.JwsSigned
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder

object JwsSignedSerializer : KSerializer<JwsSigned> {

override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("JwsSignedSerializer", PrimitiveKind.STRING)

override fun deserialize(decoder: Decoder): JwsSigned = JwsSigned.deserialize(decoder.decodeString()).getOrThrow()

override fun serialize(encoder: Encoder, value: JwsSigned) {
encoder.encodeString(value.serialize())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package at.asitplus.openid

import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonContentPolymorphicSerializer
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.jsonObject

@Serializable
sealed interface RequestParameters {
val responseType: String?
val clientId: String?
val clientIdScheme: OpenIdConstants.ClientIdScheme?
val responseMode: OpenIdConstants.ResponseMode?
val responseUrl: String?
val nonce: String?
val state: String?
}

object RequestParametersSerializer : JsonContentPolymorphicSerializer<RequestParameters>(RequestParameters::class) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<RequestParameters> {
val parameters = element.jsonObject
return when {
"signatureQualifier" in parameters -> SignatureRequestParameters.serializer()
else -> AuthenticationRequestParameters.serializer()
}
}
}


Loading

0 comments on commit d1c106f

Please sign in to comment.