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

Update logic for TrustedIssuerRegistryPolicy #69

Merged
merged 1 commit into from Nov 22, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 11 additions & 14 deletions src/main/kotlin/id/walt/auditor/VerificationPolicy.kt
@@ -1,5 +1,6 @@
package id.walt.auditor

import id.walt.model.AttributeInfo
import id.walt.model.TrustedIssuer
import id.walt.services.did.DidService
import id.walt.services.essif.TrustedIssuerClient
Expand All @@ -16,13 +17,14 @@ import mu.KotlinLogging
import java.text.SimpleDateFormat
import java.util.*

private const val TIR_TYPE_ATTRIBUTE = "attribute"
private const val TIR_NAME_ISSUER = "issuer"
private val log = KotlinLogging.logger {}
private val jsonLdCredentialService = JsonLdCredentialService.getService()
private val jwtCredentialService = JwtCredentialService.getService()
private val dateFormatter =
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").also { it.timeZone = TimeZone.getTimeZone("UTC") }


@Serializable
data class VerificationPolicyMetadata(val description: String, val id: String)

Expand Down Expand Up @@ -102,23 +104,18 @@ class TrustedIssuerRegistryPolicy : VerificationPolicy {
throw Exception("Could not resolve issuer TIR record of $issuerDid", e)
}

return validTrustedIssuerRecord(tirRecord)
return isValidTrustedIssuerRecord(tirRecord)

}

private fun validTrustedIssuerRecord(tirRecord: TrustedIssuer): Boolean {
var issuerRecordValid = true

if (tirRecord.attributes[0].body != "eyJAY29udGV4dCI6Imh0dHBzOi8vZWJzaS5ldSIsInR5cGUiOiJhdHRyaWJ1dGUiLCJuYW1lIjoiaXNzdWVyIiwiZGF0YSI6IjVkNTBiM2ZhMThkZGUzMmIzODRkOGM2ZDA5Njg2OWRlIn0=") {
issuerRecordValid = false
log.debug { "Body of TIR record $tirRecord not valid." }
}

if (tirRecord.attributes[0].hash != "14f2d3c3320f65b6fd9413608e4c17f831e3c595ad61222ec12f899752348718") {
issuerRecordValid = false
log.debug { "Body of TIR record $tirRecord not valid." }
private fun isValidTrustedIssuerRecord(tirRecord: TrustedIssuer): Boolean {
for (attribute in tirRecord.attributes) {
val attributeInfo = AttributeInfo.from(attribute.body)
if(TIR_TYPE_ATTRIBUTE.equals(attributeInfo?.type) && TIR_NAME_ISSUER.equals(attributeInfo?.name)) {
return true
}
}
return issuerRecordValid
return false
}
}

Expand Down
29 changes: 29 additions & 0 deletions src/main/kotlin/id/walt/model/Ebsi.kt
Expand Up @@ -2,13 +2,17 @@

package id.walt.model

import com.beust.klaxon.Json
import com.beust.klaxon.Klaxon
import com.beust.klaxon.KlaxonException
import com.nimbusds.jose.jwk.ECKey
import id.walt.common.prettyPrint
import id.walt.crypto.decBase64
import id.walt.vclib.model.Proof
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
import mu.KotlinLogging
import java.time.LocalDateTime


Expand Down Expand Up @@ -99,6 +103,31 @@ data class Attribute(
val body: String,
)

private val attributeInfoLog = KotlinLogging.logger("AttributeInfo")

@Serializable
data class AttributeInfo(
@SerialName("@context")
@Json(name = "@context")
val context: String = "",
val type: String = "",
val name: String = "",
val data: String = ""
) {

companion object {
fun from(base64Body: String): AttributeInfo? {
val decodedAttributeBody = String(decBase64(base64Body))
try {
return Klaxon().parse<AttributeInfo>(decodedAttributeBody)
} catch(e: KlaxonException) {
attributeInfoLog.debug("Klaxon error (${e.message}) for attribute body ($base64Body)")
return AttributeInfo()
}
}
}
}

@Serializable
data class TrustedIssuer(
val did: String,
Expand Down
5 changes: 5 additions & 0 deletions src/test/kotlin/id/walt/auditor/AuditorApiTest.kt
Expand Up @@ -116,6 +116,11 @@ class AuditorApiTest : AnnotationSpec() {
postAndVerify(readVerifiableCredential("VerifiableDiploma"))
}

@Test
fun testTrustedIssuerRegistryPolicy() {
postAndVerify(readVerifiableCredential("VerifiableDiplomaWithIssuerTirRecord"), "TrustedIssuerRegistryPolicy")
}

@Test
fun testVerifiableId() {
postAndVerify(readVerifiableCredential("VerifiableId"))
Expand Down
82 changes: 82 additions & 0 deletions src/test/kotlin/id/walt/auditor/TrustedIssuerRegistryPolicyTest.kt
@@ -0,0 +1,82 @@
package id.walt.auditor

import id.walt.crypto.encBase64Str
import id.walt.model.Attribute
import id.walt.model.DidMethod
import id.walt.model.TrustedIssuer
import id.walt.services.did.DidService
import id.walt.services.essif.TrustedIssuerClient
import id.walt.signatory.ProofConfig
import id.walt.signatory.ProofType
import id.walt.signatory.Signatory
import id.walt.vclib.Helpers.toCredential
import id.walt.vclib.model.VerifiableCredential
import io.kotest.core.spec.Spec
import io.kotest.core.spec.style.AnnotationSpec
import io.kotest.matchers.shouldBe
import io.mockk.every
import io.mockk.mockkObject

class TrustedIssuerRegistryPolicyTest : AnnotationSpec() {

private val testedPolicy = TrustedIssuerRegistryPolicy()
private val mockedHash = "mockHash"
private val validAttrInfoJson = "{\"@context\":\"https://ebsi.eu\",\"type\":\"attribute\",\"name\":\"issuer\",\"data\":\"5d50b3fa18dde32b384d8c6d096869de\"}"
private lateinit var did: String
private lateinit var verifiableCredential: VerifiableCredential

override fun beforeSpec(spec: Spec) {
super.beforeSpec(spec)

val signatory = Signatory.getService()

did = DidService.create(DidMethod.key)

println("Generated: $did")

val vcStr = signatory.issue(
"VerifiableDiploma", ProofConfig(
issuerDid = did,
subjectDid = did,
issuerVerificationMethod = "Ed25519Signature2018",
proofPurpose = "Testing",
proofType = ProofType.LD_PROOF
)
)

verifiableCredential = vcStr.toCredential()
}

@Test
fun whenTrustedIssuerRegistryContainsValidAttributeThenReturnTrue() {
val attributeList = listOf(Attribute(mockedHash, encBase64Str(validAttrInfoJson)))
mockTrustedIssuerWithAttributes(attributeList)

testedPolicy.verify(verifiableCredential) shouldBe true
}

@Test
fun whenTrustedIssuerRegistryContainsInvalidBase64AttributeThenReturnFalse() {
val attributeList = listOf(Attribute(mockedHash, "invalidBase64EncodedString"))
mockTrustedIssuerWithAttributes(attributeList)

testedPolicy.verify(verifiableCredential) shouldBe false
}

@Test
fun whenTrustedIssuerRegistryContainsMultipleAttributesWithLastValidThenReturnTrue() {
val attr1 = Attribute(mockedHash, "invalidBase64EncodedString")
val attr2 = Attribute(mockedHash, encBase64Str("invalidAttr"))
val attr3 = Attribute(mockedHash, encBase64Str(validAttrInfoJson))
val attributeList = listOf(attr1, attr2, attr3)
mockTrustedIssuerWithAttributes(attributeList)

testedPolicy.verify(verifiableCredential) shouldBe true
}

private fun mockTrustedIssuerWithAttributes(attributeList: List<Attribute>) {
val tirRecord = TrustedIssuer(did, attributeList)
mockkObject(TrustedIssuerClient)
every { TrustedIssuerClient.getIssuer(any()) } returns tirRecord
}
}
@@ -0,0 +1,57 @@
{
"@context" : [ "https://www.w3.org/2018/credentials/v1" ],
"credentialSchema" : {
"id" : "https://api.preprod.ebsi.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd",
"type" : "JsonSchemaValidator2018"
},
"credentialSubject" : {
"awardingOpportunity" : {
"awardingBody" : {
"homepage" : "https://leaston.bcdiploma.com/",
"id" : "did:ebsi:zfPHQeMuKeSE7Ff35BcH7xL",
"preferredName" : "Leaston University",
"registration" : "0597065J"
},
"endedAtTime" : "2020-06-26T00:00:00Z",
"id" : "https://leaston.bcdiploma.com/law-economics-management#AwardingOpportunity",
"identifier" : "https://certificate-demo.bcdiploma.com/check/87ED2F2270E6C41456E94B86B9D9115B4E35BCCAD200A49B846592C14F79C86BV1Fnbllta0NZTnJkR3lDWlRmTDlSRUJEVFZISmNmYzJhUU5sZUJ5Z2FJSHpWbmZZ",
"location" : "FRANCE",
"startedAtTime" : "2019-09-02T00:00:00Z"
},
"dateOfBirth" : "1993-04-08",
"familyName" : "DOE",
"givenNames" : "Jane",
"gradingScheme" : {
"id" : "https://leaston.bcdiploma.com/law-economics-management#GradingScheme",
"title" : "Lower Second-Class Honours"
},
"id" : "did:ebsi:z21BT4RrmLyZJYVzd9vpUGCw",
"identifier" : "0904008084H",
"learningAchievement" : {
"additionalNote" : [ "DISTRIBUTION MANAGEMENT" ],
"description" : "MARKETING AND SALES",
"id" : "https://leaston.bcdiploma.com/law-economics-management#LearningAchievment",
"title" : "MASTERS LAW, ECONOMICS AND MANAGEMENT"
},
"learningSpecification" : {
"ectsCreditPoints" : 120,
"eqfLevel" : 7,
"id" : "https://leaston.bcdiploma.com/law-economics-management#LearningSpecification",
"iscedfCode" : [ "7" ],
"nqfLevel" : [ "7" ]
}
},
"id" : "identity#VerifiableDiploma#0f8522b8-89f1-4262-b6ef-d5f2a4e14c90",
"issuanceDate" : "2021-11-12T11:55:40Z",
"issuer" : "did:ebsi:zfPHQeMuKeSE7Ff35BcH7xL",
"validFrom" : "2021-11-12T11:55:40Z",
"type" : [ "VerifiableCredential", "VerifiableAttestation", "VerifiableDiploma" ],
"proof" : {
"type" : "EcdsaSecp256k1Signature2019",
"creator" : "did:ebsi:zfPHQeMuKeSE7Ff35BcH7xL",
"created" : "2021-11-12T11:55:49Z",
"domain" : "https://api.preprod.ebsi.eu",
"nonce" : "f7b4f1ae-174c-4c94-9198-167aa7e325e6",
"jws" : "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFUzI1NksifQ..t2ov2H3RW7gLOKRkkG6Pm2sXjeSoFoE4bIZf7hw73i483pJYr7LTaCEH_9zrLh-NvKHJ0XLTpCjDP4AHL_O6gQ"
}
}