Skip to content

Commit

Permalink
Fix: JSONPathParser and other tests
Browse files Browse the repository at this point in the history
  • Loading branch information
acrusage-iaik committed Apr 9, 2024
1 parent 32400e6 commit 76fc307
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -268,26 +268,12 @@ class PresentProofProtocol(
RequestPresentationAttachment.deserialize(it)
} ?: return problemReporter.problemLastMessage(lastMessage.threadId, "attachments-format")
// TODO Is ISO supported here?
val constraintFields = requestPresentationAttachment.presentationDefinition.inputDescriptors
.mapNotNull { it.constraints }
.flatMap { it.fields?.toList() ?: listOf() }
val requestedTypes = constraintFields
.filter { it.path.contains("\$.vc[*].type") }
.mapNotNull { it.filter }
.filter { it.type == "string" }
.mapNotNull { it.const }
.mapNotNull { AttributeIndex.resolveAttributeType(it) }
val requestedClaims = constraintFields
.filter { it.path.contains("\$.vc[*].name") }
.mapNotNull { it.filter }
.filter { it.type == "string" }
.mapNotNull { it.const }
val vp = holder?.createPresentation(
val presentationResult = holder?.createPresentation(
challenge = requestPresentationAttachment.options.challenge,
audienceId = requestPresentationAttachment.options.verifier ?: senderKey.identifier,
credentialSchemes = requestedTypes.ifEmpty { null },
requestedClaims = requestedClaims.ifEmpty { null },
) ?: return problemReporter.problemInternal(lastMessage.threadId, "vp-empty")
presentationDefinition = requestPresentationAttachment.presentationDefinition
)?.getOrNull() ?: return problemReporter.problemInternal(lastMessage.threadId, "vp-empty")
val vp = presentationResult.verifiablePresentations.firstOrNull()
// TODO is ISO supported here?
if (vp !is Holder.CreatePresentationResult.Signed) {
return problemReporter.problemInternal(lastMessage.threadId, "vp-not-signed")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,9 +342,9 @@ class OidcSiopWallet(
challenge = params.nonce,
audienceId = audience,
presentationDefinition = presentationDefinition,
).getOrElse {
).getOrElse { exception ->
return KmmResult.failure<AuthenticationResponseParameters>(OAuth2Exception(Errors.USER_CANCELLED))
.also { Napier.w("Could not create presentation") }
.also { Napier.w("Could not create presentation: ${exception.message}") }
}

params.clientMetadata.vpFormats?.let { supportedFormats ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ class DummyCredentialDataProvider(
val claims = claimNames?.map {
ClaimToBeIssued(it, "${it}_DUMMY_VALUE")
} ?: listOfNotNull(
optionalClaim(claimNames, "given-name", "Susanne"),
optionalClaim(claimNames, "family-name", "Meier"),
optionalClaim(claimNames, "date-of-birth", "1990-01-01"),
optionalClaim(claimNames, "is-active", true)
ClaimToBeIssued("given-name", "Susanne"),
ClaimToBeIssued("family-name", "Meier"),
ClaimToBeIssued("date-of-birth", "1990-01-01"),
ClaimToBeIssued("is-active", true)
)
credentials += when (representation) {
ConstantIndex.CredentialRepresentation.SD_JWT -> listOf(
Expand Down Expand Up @@ -110,8 +110,11 @@ class DummyCredentialDataProvider(

if (credentialScheme == EudiwPidCredentialScheme) {
val subjectId = subjectPublicKey.didEncoded
val claims = listOfNotNull(
optionalClaim(claimNames, "family_name", "someone"),
val claims = claimNames?.map {
// TODO: remove this workaround and actually issue the credentials correctly
ClaimToBeIssued(it, "${it}_DUMMY_VALUE")
} ?: listOfNotNull(
ClaimToBeIssued("family_name", "someone"),
)
credentials += when (representation) {
ConstantIndex.CredentialRepresentation.SD_JWT -> listOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,79 @@ object EudiwPidCredentialScheme : ConstantIndex.CredentialScheme {
override val vcType: String = "EudiwPid1"
override val isoNamespace: String = "eu.europa.ec.eudiw.pid.1"
override val isoDocType: String = "eu.europa.ec.eudiw.pid.1"
override val claimNames: Collection<String> = listOf("family_name")
override val claimNames: Collection<String> = listOf(
Attributes.FAMILY_NAME,
Attributes.GIVEN_NAME,
Attributes.BIRTH_DATE,

Attributes.AGE_OVER_18,
Attributes.AGE_IN_YEARS,
Attributes.AGE_BIRTH_YEAR,
Attributes.FAMILY_NAME_BIRTH,
Attributes.GIVEN_NAME_BIRTH,
Attributes.BIRTH_PLACE,
Attributes.BIRTH_COUNTRY,
Attributes.BIRTH_STATE,
Attributes.BIRTH_CITY,

Attributes.RESIDENT_ADDRESS,
Attributes.RESIDENT_COUNTRY,
Attributes.RESIDENT_STATE,
Attributes.RESIDENT_CITY,
Attributes.RESIDENT_POSTAL_CODE,
Attributes.RESIDENT_STREET,
Attributes.RESIDENT_HOUSE_NUMBER,

Attributes.GENDER,
Attributes.NATIONALITY,

Attributes.EXPIRY_DATE,
Attributes.ISSUANCE_DATE,
Attributes.ISSUING_AUTHORITY,
Attributes.DOCUMENT_NUMBER,
Attributes.ADMINISTRATIVE_NUMBER,
Attributes.ISSUING_COUNTRY,
Attributes.ISSUING_JURISTICTION,
)
val requiredClaimNames: Collection<String> = listOf(
Attributes.FAMILY_NAME,
Attributes.GIVEN_NAME,
Attributes.BIRTH_DATE
)

object Attributes {
const val FAMILY_NAME = "family_name"
const val GIVEN_NAME = "given_name"
const val BIRTH_DATE = "birth_date"

const val AGE_OVER_18 = "age_over_18"
const val AGE_IN_YEARS = "age_in_years"
const val AGE_BIRTH_YEAR = "age_birth_year"
const val FAMILY_NAME_BIRTH = "family_name_birth"
const val GIVEN_NAME_BIRTH = "given_name_birth"

const val BIRTH_PLACE = "birth_place"
const val BIRTH_COUNTRY = "birth_country"
const val BIRTH_STATE = "birth_state"
const val BIRTH_CITY = "birth_city"

const val RESIDENT_ADDRESS = "resident_address"
const val RESIDENT_COUNTRY = "resident_country"
const val RESIDENT_STATE = "resident_state"
const val RESIDENT_CITY = "resident_city"
const val RESIDENT_POSTAL_CODE = "resident_postal_code"
const val RESIDENT_STREET = "resident_street"
const val RESIDENT_HOUSE_NUMBER = "resident_house_number"

const val GENDER = "gender"
const val NATIONALITY = "nationality"

const val EXPIRY_DATE = "expiry_date"
const val ISSUANCE_DATE = "issuance_date"
const val ISSUING_AUTHORITY = "issuing_authority"
const val DOCUMENT_NUMBER = "document_number"
const val ADMINISTRATIVE_NUMBER = "administrative_number"
const val ISSUING_COUNTRY = "issuing_country"
const val ISSUING_JURISTICTION = "issuing_jurisdiction"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,18 @@ class OidcSiopInteropTest : FreeSpec({
holderCryptoService = DefaultCryptoService()
holderAgent = HolderAgent.newDefaultInstance(holderCryptoService)
runBlocking {
val issuedCredential = IssuerAgent.newDefaultInstance(
DefaultCryptoService(),
dataProvider = DummyCredentialDataProvider(),
).issueCredential(
subjectPublicKey = holderCryptoService.publicKey,
attributeTypes = listOf(EudiwPidCredentialScheme.vcType),
representation = ConstantIndex.CredentialRepresentation.ISO_MDOC,
claimNames = EudiwPidCredentialScheme.claimNames
)

holderAgent.storeCredentials(
IssuerAgent.newDefaultInstance(
DefaultCryptoService(),
dataProvider = DummyCredentialDataProvider(),
).issueCredential(
subjectPublicKey = holderCryptoService.publicKey,
attributeTypes = listOf(EudiwPidCredentialScheme.vcType),
representation = ConstantIndex.CredentialRepresentation.ISO_MDOC,
).toStoreCredentialInput()
issuedCredential.toStoreCredentialInput()
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import at.asitplus.wallet.lib.data.jsonSerializer
import at.asitplus.wallet.lib.iso.DeviceAuth
import at.asitplus.wallet.lib.iso.DeviceSigned
import at.asitplus.wallet.lib.iso.Document
import at.asitplus.wallet.lib.iso.ElementValue
import at.asitplus.wallet.lib.iso.IssuerSigned
import at.asitplus.wallet.lib.iso.IssuerSignedItem
import at.asitplus.wallet.lib.iso.IssuerSignedList
Expand All @@ -33,10 +34,13 @@ import io.github.aakira.napier.Napier
import kotlinx.datetime.Clock
import kotlinx.serialization.cbor.ByteStringWrapper
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.add
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.jsonObject


/**
Expand Down Expand Up @@ -237,25 +241,28 @@ class HolderAgent(

val matches = presentationDefinition.inputDescriptors.map { inputDescriptor ->
inputs.filter { credential ->
// assume credential format to be supported by the verifier if no format holder is specified
val supportedFormats = inputDescriptor.format ?: presentationDefinition.formats
supportedFormats?.let { formatHolder ->
when (credential) {
is SubjectCredentialStore.StoreEntry.Vc -> formatHolder.jwtVp != null
is SubjectCredentialStore.StoreEntry.SdJwt -> formatHolder.jwtSd != null
is SubjectCredentialStore.StoreEntry.Iso -> formatHolder.msoMdoc != null
}
}
?: true // assume credential format to be supported by the verifier if no format holder is specified
} ?: true
}.firstNotNullOfOrNull { credential ->
StandardInputEvaluator().evaluateMatch(
inputDescriptor = inputDescriptor,
credential = credential.toJsonElement(),
)?.let {
).getOrNull()?.let {
CandidateInputMatchContainer(
inputDescriptor = inputDescriptor,
inputMatch = it,
credential = credential,
)
} ?: null.also {
Napier.d("Credential does not satisfy input descriptor: $credential")
Napier.d("Credential as json element: ${credential.toJsonElement()}")
}
} ?: return KmmResult.failure(
MissingInputDescriptorMatchException(inputDescriptor)
Expand Down Expand Up @@ -432,7 +439,16 @@ fun SubjectCredentialStore.StoreEntry.toJsonElement(): JsonElement {
val credential = this
return when (credential) {
is SubjectCredentialStore.StoreEntry.Vc -> {
jsonSerializer.encodeToJsonElement(credential.vc.vc.credentialSubject)
buildJsonObject {
put("type", JsonPrimitive(credential.scheme.vcType))
jsonSerializer.encodeToJsonElement(credential.vc.vc.credentialSubject).jsonObject.entries.forEach {
put(it.key, it.value)
}
// TODO: Remove this when there is a clear specification on how to encode vc credentials
put("vc", buildJsonArray {
add(jsonSerializer.encodeToJsonElement(credential.vc.vc.credentialSubject))
})
}
}

is SubjectCredentialStore.StoreEntry.SdJwt -> {
Expand All @@ -459,35 +475,23 @@ fun SubjectCredentialStore.StoreEntry.toJsonElement(): JsonElement {
buildJsonObject {
put("mdoc", buildJsonObject {
put("doctype", JsonPrimitive(credential.scheme.isoDocType))
// TODO: remove the rest here as soon as the eudiw verifier has found a better way to specify their presentation definition
put("namespace", JsonPrimitive(credential.scheme.isoNamespace))
credential.issuerSigned.namespaces?.forEach {
it.value.entries.forEach { signedItem ->
put(
signedItem.value.elementIdentifier,
signedItem.value.elementValue.toJsonElement(),
)
}
}
})
credential.issuerSigned.namespaces?.forEach {
put(it.key, buildJsonObject {
it.value.entries.forEach { signedItem ->
put(
signedItem.value.elementIdentifier,
signedItem.value.elementValue.let { value ->
value.boolean?.let { JsonPrimitive(it) }
?: value.string?.let { JsonPrimitive(it) }
?: value.bytes?.let {
buildJsonArray {
it.forEach {
this.add(JsonPrimitive(it.toInt()))
}
}
} ?: value.drivingPrivilege?.let { drivingPriviledgeArray ->
buildJsonArray {
drivingPriviledgeArray.forEach { drivingPriviledge ->
this.add(
jsonSerializer.encodeToJsonElement(
drivingPriviledge
)
)
}
}
} ?: value.date?.let {
JsonPrimitive(it.toString())
} ?: JsonPrimitive(null)
}
signedItem.value.elementValue.toJsonElement()
)
}
})
Expand All @@ -497,6 +501,30 @@ fun SubjectCredentialStore.StoreEntry.toJsonElement(): JsonElement {
}
}

private fun ElementValue.toJsonElement(): JsonElement {
return this.boolean?.let { JsonPrimitive(it) }
?: this.string?.let { JsonPrimitive(it) }
?: this.bytes?.let {
buildJsonArray {
it.forEach {
this.add(JsonPrimitive(it.toInt()))
}
}
} ?: this.drivingPrivilege?.let { drivingPriviledgeArray ->
buildJsonArray {
drivingPriviledgeArray.forEach { drivingPriviledge ->
this.add(
jsonSerializer.encodeToJsonElement(
drivingPriviledge
)
)
}
}
} ?: this.date?.let {
JsonPrimitive(it.toString())
} ?: JsonNull
}

open class PresentationException(message: String) : Exception(message)

class CredentialRetrievalException :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ class JSONPathParser(val jsonPath: String) {
}
val selectorStartSymbol = jsonPath[0].toString()
val selectorEndSymbolIndex = when (selectorStartSymbol) {
"." -> jsonPath.indexOf(".", 1).let {
if (it != -1) it else jsonPath.indexOf("[", 1).let {
"." -> {
// may end at '.', at '[' or with end of string
jsonPath.indexOfAny(listOf(".", "["), startIndex = 1).let {
if (it != -1) it else jsonPath.lastIndex + 1
}
}
Expand All @@ -64,6 +65,7 @@ class JSONPathParser(val jsonPath: String) {

val selectorDetails = jsonPath.substring(1, selectorEndSymbolIndex)
val remainingPath = jsonPath.substring(nextStartSymbol)
Napier.d("selectorDetails: $selectorDetails")
Napier.d("consumedPath: ${jsonPath.substring(0, selectorEndSymbolIndex)}")
Napier.d("remaining path: $remainingPath")

Expand Down
Loading

0 comments on commit 76fc307

Please sign in to comment.