Skip to content

Commit

Permalink
Merge branch 'refs/heads/main' into fix-readme
Browse files Browse the repository at this point in the history
  • Loading branch information
philpotisk committed Jul 8, 2024
2 parents b4eec89 + 04d36ec commit b6d4e6d
Show file tree
Hide file tree
Showing 14 changed files with 637 additions and 47 deletions.
16 changes: 11 additions & 5 deletions waltid-libraries/waltid-did/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ represented as **_walt.id crypto_** `Key`.
<td align="center">rsa</td>
<td align="center">&check;</td>
<td align="center">&dash;</td>
<td align="center">&cross;</td>
<td align="center">&check;</td>
</tr>
<!-- end key -->
<tr><td colspan="5"></td></tr>
Expand Down Expand Up @@ -214,7 +214,7 @@ represented as **_walt.id crypto_** `Key`.
<td align="center">rsa</td>
<td align="center">&check;</td>
<td align="center">&dash;</td>
<td align="center">&cross;</td>
<td align="center">&check;</td>
</tr>
<!-- end jwk -->
<tr><td colspan="5"></td></tr>
Expand Down Expand Up @@ -246,7 +246,7 @@ represented as **_walt.id crypto_** `Key`.
<td align="center">rsa</td>
<td align="center">&check;</td>
<td align="center">&cross;</td>
<td align="center">&cross;</td>
<td align="center">&check;</td>
</tr>
<!-- end web -->
<tr><td colspan="5"></td></tr>
Expand All @@ -263,11 +263,17 @@ represented as **_walt.id crypto_** `Key`.
<tr><td colspan="5"></td></tr>
<!-- ebsi -->
<tr>
<td align="center">ebsi</td>
<td align="center" rowspan="2">ebsi</td>
<td align="center">secp256r1</td>
<td align="center">&cross;</td>
<td align="center">&cross;</td>
<td align="center">&check;</td>
</tr>
<tr>
<td align="center">secp256k1</td>
<td align="center">&cross;</td>
<td align="center">&cross;</td>
<td align="center">&check;</td>
</tr>
<!-- end ebsi -->
<tr><td colspan="5"></td></tr>
Expand All @@ -286,7 +292,7 @@ represented as **_walt.id crypto_** `Key`.
(*) The did:key implementation defaults to W3C CCG https://w3c-ccg.github.io/did-method-key/. By setting _useJwkJcsPub_ to `true` the EBSI
implementation (jwk_jcs-pub encoding) according https://hub.ebsi.eu/tools/libraries/key-did-resolver is performed.

## Remote DID operations by 3d party services (fallback)
## Remote DID operations by 3rd party services (fallback)

According Universal Resolver: https://github.com/decentralized-identity/universal-resolver/

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package id.walt.did.dids.document

import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlin.js.ExperimentalJsExport
import kotlin.js.JsExport
import kotlin.js.JsName

@JsExport
@OptIn(ExperimentalSerializationApi::class, ExperimentalJsExport::class)
@Serializable
data class DidEbsiDocument(
@EncodeDefault @SerialName("@context") val context: List<String> = DEFAULT_CONTEXT,
val id: String, // did:ebsi:
val controller: List<String>?,
val verificationMethod: List<VerificationMethod>?,
val authentication: List<String>?,
val assertionMethod: List<String>?,
val capabilityInvocation: List<String>?,
val capabilityDelegation: List<String>?,
val keyAgreement: List<String>?,
) {
companion object {
private val DEFAULT_CONTEXT =
listOf("https://www.w3.org/ns/did/v1", "https://w3id.org/security/suites/jws-2020/v1")
private val json = Json {
explicitNulls = false
}
}

@Serializable
data class VerificationMethod(
val id: String, // did:ebsi:
val type: String, // JsonWebKey2020
val controller: String, // did:ebsi:
val publicKeyJwk: JsonObject // jwk
)

fun toMap() = json.encodeToJsonElement(this).jsonObject.toMap()

@JsName("secondaryConstructor")
constructor(didDoc: DidDocument) : this(
context = DEFAULT_CONTEXT,
id = didDoc["id"]!!.jsonPrimitive.content,
controller = didDoc["controller"]?.jsonArray?.map {
it.jsonPrimitive.content
},
verificationMethod = didDoc["verificationMethod"]?.jsonArray?.map {
val verificationMethod = it.jsonObject
val id = verificationMethod["id"]!!.jsonPrimitive.content
val type = verificationMethod["type"]!!.jsonPrimitive.content
val controller = verificationMethod["controller"]!!.jsonPrimitive.content
val publicKeyJwk = verificationMethod["publicKeyJwk"]!!.jsonObject
VerificationMethod(id, type, controller, publicKeyJwk)
},
authentication = didDoc["authentication"]?.jsonArray?.map {
it.jsonPrimitive.content
},
assertionMethod = didDoc["assertionMethod"]?.jsonArray?.map {
it.jsonPrimitive.content
},
capabilityInvocation = didDoc["capabilityInvocation"]?.jsonArray?.map {
it.jsonPrimitive.content
},
capabilityDelegation = didDoc["capabilityDelegation"]?.jsonArray?.map {
it.jsonPrimitive.content
},
keyAgreement = didDoc["keyAgreement"]?.jsonArray?.map {
it.jsonPrimitive.content
},
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class LocalResolver : DidResolver {
DidJwkResolver(),
DidWebResolver(http),
DidKeyResolver(),
DidEbsiResolver()
DidEbsiResolver(http)
).associateBy { it.method }.toMutableMap()

fun deactivateMethod(method: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package id.walt.did.dids.resolver.local
import id.walt.crypto.keys.Key
import id.walt.crypto.keys.jwk.JWKKey
import id.walt.did.dids.document.DidDocument
import id.walt.did.dids.document.DidEbsiDocument
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.logging.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
Expand All @@ -22,36 +24,33 @@ import kotlin.js.JsExport

@OptIn(ExperimentalJsExport::class)
@JsExport
class DidEbsiResolver : LocalResolverMethod("ebsi") {
class DidEbsiResolver(
private val client: HttpClient,
) : LocalResolverMethod("ebsi") {

val httpLogging = false
private val didRegistryUrlBaseURL = "https://api-conformance.ebsi.eu/did-registry/v5/identifiers/"
private val json = Json { ignoreUnknownKeys = true }

@JvmBlocking
@JvmAsync
@JsPromise
@JsExport.Ignore
override suspend fun resolve(did: String): Result<DidDocument> {
val url = "https://api-conformance.ebsi.eu/did-registry/v5/identifiers/${did}"

val httpClient = HttpClient {
install(ContentNegotiation) {
json(Json { ignoreUnknownKeys = true })
}
if (httpLogging) {
install(Logging) {
logger = Logger.SIMPLE
level = LogLevel.HEADERS
}
}
}
override suspend fun resolve(did: String): Result<DidDocument> = runCatching {
resolveDid(did)
}

val response = runCatching {
private suspend fun resolveDid(did: String): DidDocument {
val url = didRegistryUrlBaseURL + did
return client.get(url).bodyAsText().let {
DidDocument(
jsonObject = httpClient.get(url).body<JsonObject>()
DidEbsiDocument(
DidDocument(
jsonObject = Json.parseToJsonElement(it).jsonObject
)
).toMap()
)
}

return response
}

@JvmBlocking
Expand All @@ -73,6 +72,13 @@ class DidEbsiResolver : LocalResolverMethod("ebsi") {
return tryConvertAnyPublicKeyJwkToKey(publicKeyJwks)
}

/*
Note from Christos:
There exist cases of EBSI DID Documents that only have secp256k1. There is nothing invalid
in not having a secp256r1 key. Hence, this function was changed to prioritize secp256r1 keys,
but not to return a failure result otherwise.
* NOTE:
* */
@JvmBlocking
@JvmAsync
@JsPromise
Expand All @@ -82,6 +88,6 @@ class DidEbsiResolver : LocalResolverMethod("ebsi") {
val result = JWKKey.importJWK(publicKeyJwk)
if (result.isSuccess && publicKeyJwk.contains("P-256")) return result
}
return Result.failure(NoSuchElementException("No key could be imported"))
return JWKKey.importJWK(publicKeyJwks.first())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import id.walt.crypto.keys.KeyType
import id.walt.crypto.keys.jwk.JWKKey
import id.walt.did.dids.registrar.dids.DidJwkCreateOptions
import id.walt.did.dids.registrar.local.jwk.DidJwkRegistrar
import id.walt.did.dids.resolver.local.DidJwkResolver
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals

class LocalDidJwkConsistencyTest {

private val localResolver = DidJwkResolver()
private val localRegistrar = DidJwkRegistrar()

@Test
fun testDidJwkCreateAndResolveConsistency() = runTest{
val keyList: List<JWKKey> = KeyType.entries.map { JWKKey.generate(it) }
for (key in keyList) {
val didResult = localRegistrar.registerByKey(key, DidJwkCreateOptions(keyType = key.keyType))
val resolvedKey = localResolver.resolveToKey(didResult.did).getOrThrow()
assertEquals(key.getThumbprint(),resolvedKey.getThumbprint())
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import id.walt.crypto.keys.KeyType
import id.walt.crypto.keys.jwk.JWKKey
import id.walt.did.dids.registrar.dids.DidKeyCreateOptions
import id.walt.did.dids.registrar.local.key.DidKeyRegistrar
import id.walt.did.dids.resolver.local.DidKeyResolver
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals

class LocalDidKeyConsistencyTest {

private val localResolver = DidKeyResolver()
private val localRegistrar = DidKeyRegistrar()

@Test
fun testDidKeyCreateAndResolveConsistency() = runTest{
val keyList: List<JWKKey> = KeyType.entries.map { JWKKey.generate(it) }
for (key in keyList) {
val didResult = localRegistrar.registerByKey(key, DidKeyCreateOptions(keyType = key.keyType))
val resolvedKey = localResolver.resolveToKey(didResult.did).getOrThrow()
assertEquals(key.getThumbprint(),resolvedKey.getThumbprint())
}
}

@Test
fun testDidKeyCreateAndResolveJwkJcsPubConsistency() = runTest {
val keyList: List<JWKKey> = KeyType.entries.map { JWKKey.generate(it) }
for (key in keyList) {
val didResult = localRegistrar.registerByKey(key, DidKeyCreateOptions(keyType = key.keyType, useJwkJcsPub = true))
localResolver.resolveToKey(didResult.did)
val resolvedKey = localResolver.resolveToKey(didResult.did).getOrThrow()
assertEquals(key.getThumbprint(),resolvedKey.getThumbprint())
}
}
}
Loading

0 comments on commit b6d4e6d

Please sign in to comment.