Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/signature services #127

Draft
wants to merge 51 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
d5f9077
OID4VCI: Support parsing multiple proofs and JWK key binding
nodh Sep 16, 2024
6b92654
Fix comment
n0900 Sep 9, 2024
01c0713
Add stumps for signature creation
n0900 Sep 9, 2024
6d6366f
Add relevant Enum classes
n0900 Sep 11, 2024
5894263
Restructure folders
n0900 Sep 11, 2024
9a69573
Add Test vectors
n0900 Sep 11, 2024
8c72bae
Add Test vectors
n0900 Sep 13, 2024
1120c9e
Add SignatureQualifier
n0900 Sep 13, 2024
33dd4e8
Add RqesRequest
n0900 Sep 13, 2024
a4557ce
Add RqesRequest
n0900 Sep 13, 2024
b9c6a59
Change default values
n0900 Sep 13, 2024
b637fde
Add SignDoc helper function
n0900 Sep 13, 2024
e285f85
Add SignDocParameter Init
n0900 Sep 13, 2024
fc04ea5
Change authorizationDetails type
n0900 Sep 16, 2024
5ce153e
Change authorizationDetails type
n0900 Sep 16, 2024
bc8cd10
Fix artifactVersion
n0900 Sep 16, 2024
62b803b
Add Serialization Annotation
n0900 Sep 16, 2024
5e4caae
Change url to string java compatability
n0900 Sep 16, 2024
daf05e4
Fix constructor
n0900 Sep 19, 2024
20731c8
Extend RqesWalletService
n0900 Sep 19, 2024
a779167
Extend RqesWalletService
n0900 Sep 19, 2024
c3c90a2
Refactor RqesWalletService
n0900 Sep 25, 2024
50fb66a
Change credentialID to bytearray
n0900 Sep 25, 2024
536046a
Fix variable names in test cases
n0900 Oct 2, 2024
c211cc1
Add custom serializers
n0900 Oct 3, 2024
1d86034
Integrate indispensable data classes
n0900 Oct 3, 2024
cd70d07
Integrate indispensable data classes
n0900 Oct 3, 2024
94030ce
Integrate indispensable data classes
n0900 Oct 7, 2024
d8752a3
Integrate indispensable data classes
n0900 Oct 7, 2024
ee73016
Integrate indispensable data classes
n0900 Oct 9, 2024
a84103f
Integrate indispensable data classes
n0900 Oct 9, 2024
48ffebe
Refactor signAlgorithm function
n0900 Oct 14, 2024
65820f0
Refactor hashAlgorithm function
n0900 Oct 14, 2024
49dd43b
Revert TokenResponse changes
n0900 Oct 14, 2024
70fe45a
Revert change
n0900 Oct 14, 2024
1be694e
serializer package to lower case
n0900 Oct 14, 2024
912e885
Refactor Requestparser
n0900 Oct 4, 2024
50db0ab
Make RequestParser public
n0900 Oct 4, 2024
bac72d3
Refactor parseRequestParameters
n0900 Oct 4, 2024
6fe05e2
Refactor parseRequestParameters
n0900 Oct 7, 2024
ed9ef85
Refactor parseRequestParameters
n0900 Oct 7, 2024
8b001a8
Clean up
n0900 Oct 7, 2024
6ea7246
Update Readme
n0900 Oct 7, 2024
c036b8b
Clean up
n0900 Oct 7, 2024
0dde2d6
Add todos for other branch
n0900 Oct 7, 2024
01f693c
Add documentation
n0900 Oct 9, 2024
4fadb72
Make clientId nullable
n0900 Oct 14, 2024
13c5ae4
Fix import
n0900 Oct 14, 2024
0bb9de5
Add simple test
n0900 Oct 14, 2024
cc90a97
Add simple test
n0900 Oct 14, 2024
d1c106f
Merge pull request #143 from a-sit-plus/feature/refactorRequestParser
n0900 Oct 16, 2024
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 @@ -2,8 +2,8 @@

package at.asitplus.dif

import at.asitplus.dif.rqes.Base64URLTransactionDataSerializer
import at.asitplus.dif.rqes.TransactionDataEntry
import at.asitplus.dif.rqes.Serializer.Base64URLTransactionDataSerializer
import at.asitplus.dif.rqes.CollectionEntries.TransactionData
import com.benasher44.uuid.uuid4
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
Expand Down Expand Up @@ -63,13 +63,6 @@ data class QesInputDescriptor(
@SerialName("constraints")
override val constraints: Constraint? = null,
@SerialName("transaction_data")
val transactionData: List<@Serializable(Base64URLTransactionDataSerializer::class) TransactionDataEntry>,
val transactionData: List<@Serializable(Base64URLTransactionDataSerializer::class) TransactionData>,
) : InputDescriptor


object InputDescriptorSerializer : JsonContentPolymorphicSerializer<InputDescriptor>(InputDescriptor::class) {
override fun selectDeserializer(element: JsonElement) = when {
"transaction_data" in element.jsonObject -> QesInputDescriptor.serializer()
else -> DifInputDescriptor.serializer()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package at.asitplus.dif

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


object InputDescriptorSerializer : JsonContentPolymorphicSerializer<InputDescriptor>(InputDescriptor::class) {
override fun selectDeserializer(element: JsonElement) = when {
"transaction_data" in element.jsonObject -> QesInputDescriptor.serializer()
else -> DifInputDescriptor.serializer()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package at.asitplus.dif.rqes.CollectionEntries
nodh marked this conversation as resolved.
Show resolved Hide resolved

import at.asitplus.dif.rqes.Enums.ConformanceLevelEnum
import at.asitplus.dif.rqes.Enums.SignatureFormat
import at.asitplus.dif.rqes.Enums.SignedEnvelopeProperty
import at.asitplus.dif.rqes.Serializer.Asn1EncodableBase64Serializer
import at.asitplus.signum.indispensable.Digest
import at.asitplus.signum.indispensable.SignatureAlgorithm
import at.asitplus.signum.indispensable.X509SignatureAlgorithm
import at.asitplus.signum.indispensable.asn1.Asn1Element
import at.asitplus.signum.indispensable.asn1.ObjectIdentifier
import at.asitplus.signum.indispensable.asn1.encoding.Asn1
import at.asitplus.signum.indispensable.io.ByteArrayBase64Serializer
import io.github.aakira.napier.Napier
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.json.JsonObject

/**
* CSC: Class used as part of [SignatureRequestParameters]
*/
@Serializable
data class Document(
/**
* base64-encoded document content to be signed, testcases weird so for now string
*/
@SerialName("document")
@Serializable(ByteArrayBase64Serializer::class)
val document: ByteArray,

/**
* Requested Signature Format
*/
@SerialName("signature_format")
val signatureFormat: SignatureFormat,

/**
* Requested conformance level. If omitted its value is "Ades-B-B"
nodh marked this conversation as resolved.
Show resolved Hide resolved
*/
@SerialName("conformance_level")
val conformanceLevel: ConformanceLevelEnum? = null,

/**
* The OID of the algorithm to use for signing
*/
@SerialName("signAlgo")
val signAlgoOid: ObjectIdentifier,

/**
* The Base64-encoded DER-encoded ASN.1 signature parameters
*/
@SerialName("signAlgoParams")
@Serializable(Asn1EncodableBase64Serializer::class)
val signAlgoParams: Asn1Element? = null,

/**
* Defined in CSC v2.0.0.2 P. 81
* Defines a second way to encode all attributes, none of which are necessary
* Will be ignored until use-case arises
*/
@SerialName("signed_props")
val signedProps: List<JsonObject>? = null,

/**
* if omitted/null it is assumed to have value
* `SignedEnvelopeProperty.defaultProperty(signatureFormat)`
*/
@SerialName("signed_envelope_property")
val signedEnvelopeProperty: SignedEnvelopeProperty? = null,
) {
@Transient
val signAlgorithm: SignatureAlgorithm? =
kotlin.runCatching {
X509SignatureAlgorithm.doDecode(Asn1.Sequence {
+signAlgoOid
+(signAlgoParams ?: Asn1.Null())
}).also {
require(it.digest != Digest.SHA1)
}.algorithm
}.getOrElse {
Napier.w { "Could not resolve $signAlgoOid" }
null
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false

other as Document

if (!document.contentEquals(other.document)) return false
if (signatureFormat != other.signatureFormat) return false
if (conformanceLevel != other.conformanceLevel) return false
if (signAlgoOid != other.signAlgoOid) return false
if (signAlgoParams != other.signAlgoParams) return false
if (signedProps != other.signedProps) return false

n0900 marked this conversation as resolved.
Show resolved Hide resolved
return true
}

override fun hashCode(): Int {
n0900 marked this conversation as resolved.
Show resolved Hide resolved
var result = document.contentHashCode()
result = 31 * result + signatureFormat.hashCode()
result = 31 * result + conformanceLevel.hashCode()
n0900 marked this conversation as resolved.
Show resolved Hide resolved
result = 31 * result + signAlgoOid.hashCode()
result = 31 * result + (signAlgoParams?.hashCode() ?: 0)
result = 31 * result + (signedProps?.hashCode() ?: 0)
return result
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package at.asitplus.dif.rqes.CollectionEntries.DocumentDigestEntries

import at.asitplus.dif.rqes.Enums.ConformanceLevelEnum
import at.asitplus.dif.rqes.Enums.SignatureFormat
import at.asitplus.dif.rqes.Enums.SignedEnvelopeProperty
import at.asitplus.dif.rqes.Hashes
import at.asitplus.dif.rqes.Serializer.Asn1EncodableBase64Serializer
import at.asitplus.dif.rqes.contentEquals
import at.asitplus.dif.rqes.contentHashCode
import at.asitplus.signum.indispensable.Digest
import at.asitplus.signum.indispensable.SignatureAlgorithm
import at.asitplus.signum.indispensable.X509SignatureAlgorithm
import at.asitplus.signum.indispensable.asn1.Asn1Element
import at.asitplus.signum.indispensable.asn1.ObjectIdentifier
import at.asitplus.signum.indispensable.asn1.encoding.Asn1
import io.github.aakira.napier.Napier
import io.ktor.util.reflect.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.json.JsonObject


/**
* CSC: Class used as part of [SignatureRequestParameters]
* TODO finish member description
n0900 marked this conversation as resolved.
Show resolved Hide resolved
*/
@Serializable
data class CscDocumentDigest(
/**
* One or more hash values representing one or more SDRs. This
* parameter SHALL contain the Base64-encoded hash(es) of the
* documents to be signed.
* Does not use hashes serializer as it is defined as array of string instead of string.
*/
@SerialName("hashes")
val hashes: Hashes,

/**
* Hashing algorithm OID used to calculate document(s) hash(es). This
* parameter MAY be omitted or ignored if the hash algorithm is
* implicitly specified by the signAlgo algorithm. Only hashing algorithms
* as strong or stronger than SHA256 SHALL be used
*/
@SerialName("hashAlgorithmOID")
val hashAlgorithmOid: ObjectIdentifier? = null,

/**
* Requested Signature Format
*/
@SerialName("signature_format")
val signatureFormat: SignatureFormat,

/**
* Requested conformance level. If omitted its value is "AdES-B-B"
*/
@SerialName("conformance_level")
val conformanceLevel: ConformanceLevelEnum? = null,

/**
* The OID of the algorithm to use for signing
*/
@SerialName("signAlgo")
val signAlgoOid: ObjectIdentifier,

/**
* The Base64-encoded DER-encoded ASN.1 signature algorithm parameters if required by
* the signature algorithm - Necessary for RSASSA-PSS for example
*/
@SerialName("signAlgoParams")
@Serializable(with = Asn1EncodableBase64Serializer::class)
val signAlgoParams: Asn1Element? = null,

/**
* Defined in CSC v2.0.0.2 P. 81
* Defines a second way to encode all attributes, none of which are necessary
* Will be ignored until use-case arises
*/
@SerialName("signed_props")
val signedProps: List<JsonObject>? = null,

/**
* if omitted/null it is assumed to have value
* `SignedEnvelopeProperty.defaultProperty(signatureFormat)`
*/
@SerialName("signed_envelope_property")
val signedEnvelopeProperty: SignedEnvelopeProperty? = null,
) {

@Transient
val signAlgorithm: SignatureAlgorithm? =
kotlin.runCatching {
X509SignatureAlgorithm.doDecode(Asn1.Sequence {
+signAlgoOid
+(signAlgoParams ?: Asn1.Null())
}).also {
require(it.digest != Digest.SHA1)
}.algorithm
}.getOrElse {
Napier.w { "Could not resolve $signAlgoOid" }
null
}

@Transient
val hashAlgorithm: Digest = hashAlgorithmOid?.let {
Digest.entries.find { digest -> digest.oid == it }
} ?: when(signAlgorithm) {
//TODO change as soon as digest is a member of the interface
is SignatureAlgorithm.ECDSA -> signAlgorithm.digest
is SignatureAlgorithm.HMAC -> signAlgorithm.digest
is SignatureAlgorithm.RSA -> signAlgorithm.digest
null -> null
} ?: throw Exception("Unknown hashing algorithm in $hashAlgorithmOid and $signAlgoOid")

override fun equals(other: Any?): Boolean {
n0900 marked this conversation as resolved.
Show resolved Hide resolved
if (this === other) return true
if (other == null || this::class != other::class) return false

other as CscDocumentDigest

if (!hashes.contentEquals(other.hashes)) return false
if (hashAlgorithmOid != other.hashAlgorithmOid) return false
if (signatureFormat != other.signatureFormat) return false
if (conformanceLevel != other.conformanceLevel) return false
if (signAlgoOid != other.signAlgoOid) return false
if (signAlgoParams != other.signAlgoParams) return false
if (signedProps != other.signedProps) return false
if (signedEnvelopeProperty != other.signedEnvelopeProperty) return false

return true
}

override fun hashCode(): Int {
var result = hashes.contentHashCode()
result = 31 * result + (hashAlgorithmOid?.hashCode() ?: 0)
result = 31 * result + signatureFormat.hashCode()
result = 31 * result + conformanceLevel.hashCode()
result = 31 * result + signAlgoOid.hashCode()
result = 31 * result + (signAlgoParams?.hashCode() ?: 0)
result = 31 * result + (signedProps?.hashCode() ?: 0)
result = 31 * result + signedEnvelopeProperty.hashCode()
return result
n0900 marked this conversation as resolved.
Show resolved Hide resolved
}
}



Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package at.asitplus.openid
package at.asitplus.dif.rqes.CollectionEntries.DocumentDigestEntries

import at.asitplus.signum.indispensable.io.ByteArrayBase64Serializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

/**
* CSC: Entry for document to be signed
* Used as part of [AuthorizationDetails]
*/
@Serializable
data class DocumentDigestCSCEntry (
data class OAuthDocumentDigest (
/**
* CSC: Conditional String containing the actual Base64-
* encoded octet-representation of the hash of the document
Expand All @@ -28,7 +29,7 @@ data class DocumentDigestCSCEntry (
if (this === other) return true
if (other == null || this::class != other::class) return false

other as DocumentDigestCSCEntry
other as OAuthDocumentDigest

if (!hash.contentEquals(other.hash)) return false
if (label != other.label) return false
Expand Down
Loading
Loading