diff --git a/.run/Web wallet frontend (dev).run.xml b/.run/Web wallet frontend (dev).run.xml
index d5377fc07..ea7f2cb83 100644
--- a/.run/Web wallet frontend (dev).run.xml
+++ b/.run/Web wallet frontend (dev).run.xml
@@ -10,4 +10,4 @@
-
\ No newline at end of file
+
diff --git a/build.gradle.kts b/build.gradle.kts
index e10cc9bb7..eb3fb25a3 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,3 +1,5 @@
+import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
+
allprojects {
version = "1.0.0-SNAPSHOT"
@@ -23,3 +25,11 @@ repositories {
kotlin {
jvmToolchain(8)
}
+
+allprojects {
+ tasks.withType {
+ rejectVersionIf {
+ listOf("-beta", "-alpha", "-rc").any { it in candidate.version.lowercase() } || candidate.version.takeLast(4).contains("RC")
+ }
+ }
+}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 1af9e0930..a80b22ce5 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew.bat b/gradlew.bat
index 93e3f59f1..25da30dbd 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
diff --git a/waltid-cli/README.md b/waltid-cli/README.md
index 1a3836313..9f5077358 100644
--- a/waltid-cli/README.md
+++ b/waltid-cli/README.md
@@ -1 +1,229 @@
# walt.id CLI
+
+Manage keys, DIDs, issue Verifiable Credentials, and verify them using the WaltId command line tool.
+
+# How to use
+
+## In development
+
+* `git clone https://github.com/walt-id/waltid-identity.git`
+* `cd waltid-identity/waltid-cli`
+* `../gradlew clean build`
+* `alias waltid="./waltid-cli-development.sh"`
+
+Now, you can run:
+
+| Command | What it does |
+|:------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------|
+| `waltid -h` | Print usage message |
+| `waltid --help` | Print WaltId CLI usage message |
+| `waltid key -h` | Print WaltId CLI key command usage message |
+| `waltid key generate -h` | Print WaltId CLI key generate command usage message |
+| `waltid key convert -h` | Print WaltId CLI key generate command usage message |
+| `waltid key generate` | Generates a cryptographic key of type Ed25519 |
+| `waltid key generate -tsecp256k1` | Generates a cryptographic key of type secp256k1 |
+| `waltid key generate --keyType=RSA` | Generates a cryptographic key of type RSA |
+| `waltid key generate --keyType=RSA -omyRSAKey.json` | Generates a cryptographic key of type RSA and save it in a file called myRSAKey.json |
+| `waltid key generate --keyType=Ed25519 -o myEd25519Key.json` | Generates a cryptographic key of type Ed25519 and save it in a file called myEd25519.json |
+| `waltid key generate --keyType=secp256k1 -o mySecp256k1Key.json` | Generates a cryptographic key of type Secp256k1 and save it in a file called mySecp256k1.json |
+| `waltid key generate --keyType=secp256r1 -o mySecp256r1Key.json` | Generates a cruyptographic key of type Secp256r1 and save it in a file called mySecp256r1.json |
+| `waltid key convert -i myRSAKey.json` | Convert the given JWK file with an RSA key to the PEM format. The converted file will be called myRSA.pem |
+| `waltid key convert -i myEd25519Key.json` | ⚠️ Not yet implemented. We don't export Ed25519 keys in PEM format yet. |
+| `waltid key convert -i mySecp256k1Key.json` | Convert the given JWK with a Secp256k1 key file to the PEM format. The converted file will be called mySecp256k1Key.pem |
+| `waltid key convert -i mySecp256r1Key.json` | Convert the given JWK with a Secp256r1 key file to the PEM format. The converted file will be called mySecp256r1Key.pem |
+| `waltid key convert --input=./myRSAKey.pem` | ⚠️ Not yet implemented. |
+| `waltid key convert --input=./myEd25519Key.pem` | ⚠️ Not yet implemented. |
+| `waltid key convert --input=./mySecp256k1Key.pem` | Converts the given PEM with a Sec256k1 key to the JWK format. The converted file will be called mySecp256k1Key.jwk |
+| `waltid key convert --input=./mySecp256r1Key.pem --output=./convertedSecp256r1.jwk` | Converts the given PEM with a Sec256r1 key to the JWK format. The converted file will be called convertedSecp256r1.jwk |
+| `openssl ecparam -genkey -name secp256k1 -out secp256k1_by_openssl_pub_pvt_key.pem` | Uses OpenSSL to generate a pair of keys in a PEM file. | |
+| `waltid key convert --verbose -i secp256k1_by_openssl_pub_pvt_key.pem` | Converts the Secp256k1 key in the given PEM file to the JWK format. |
+
+## In production
+
+We are still preparing a nice distribution strategy. It will be available soon.
+
+In the meantime, you can use Gradle to generate the distribution package:
+
+* `cd waltid-identity/waltid-cli`
+* `../gradlew distZip` or `../gradlew distTar`
+
+A `waltid-cli-1.0.0-SNAPSHOT` file will be created in the `build/distributions` directory.
+
+```bash
+$ pwd
+.../waltid-identity/waltid-cli
+
+$ ls -la build/distributions/
+total 67024
+drwxr-xr-x@ 3 waltian staff 96 Feb 20 18:41 .
+drwxr-xr-x@ 12 waltian staff 384 Feb 20 18:41 ..
+-rw-r--r--@ 1 waltian staff 34062716 Feb 20 18:41 waltid-cli-1.0.0-SNAPSHOT.zip
+```
+
+Extract it somewhere and you will find two folders inside:
+
+```bash
+$ unzip build/distributions/waltid-cli-1.0.0-SNAPSHOT.zip -d /tmp
+(...)
+
+$ ll /tmp/waltid-cli-1.0.0-SNAPSHOT
+total 0
+drwxr-xr-x@ 4 waltian wheel 128B Feb 20 18:41 bin
+drwxr-xr-x@ 72 waltian wheel 2.3K Feb 20 18:41 lib
+```
+
+The `bin` folder has the CLI execution script compatible with common operating systems.
+
+Set execution rights to the script
+
+`$ chmod a+x /tmp/waltid-cli-1.0.0-SNAPSHOT/bin/waltid`
+
+Add the `/tmp/waltid-cli-1.0.0-SNAPSHOT/bin` to the PATH.
+
+`$ export PATH="/tmp/waltid-cli-1.0.0-SNAPSHOT/bin:$PATH"`
+
+Execute the walt.id CLI
+
+`$ waltid`
+
+# Supported commands
+
+| Command | Subommand | Description | Ready to use |
+|:-------:|:---------:|------------------------------------------------|:------------:|
+| key | generate | Generates a new cryptographic key. | ✔️ |
+| | convert | Convert key files between PEM and JWK formats. | |
+| | | from PEM to JWK | ✔️ |
+| | | Convertion from JWK to PEM | ✖️ |
+| did | create | Create a new DID. | ✖️ |
+| | resolve | Resolve a DID. | ✖️ |
+| vc | issue | Issue a verifiable credential | ✖️ |
+| | verify | Verify a verifiable credential | ✖️ |
+| | present | Present a verifiable credential | ✖️ |
+| | ... | | |
+
+# Reference
+
+## `waltid` command
+
+```bash
+Usage: waltid [] []...
+
+ WaltId CLI
+
+ ╭─────────────────────────────────────────────────────────────────────────╮
+ │ The walt.id CLI is a command line tool that allows you to onboard and│
+ │ use a SSI (Self-Sovereign-Identity) ecosystem. You can manage │
+ │ cryptographic keys, generate and register W3C Decentralized │
+ │ Identifiers (DIDs) as well as create, issue & verify W3C Verifiable │
+ │ credentials (VCs). │
+ │ │
+ │ Example commands are: │
+ │ │
+ │ Print usage instructions │
+ │ ------------------------- │
+ │ waltid -h │
+ │ waltid --help │
+ │ waltid key -h │
+ │ waltid key generate -h │
+ │ waltid key convert -h │
+ │ │
+ │ Key generation │
+ │ --------------- │
+ │ waltid key generate │
+ │ waltid key generate -t secp256k1 │
+ │ waltid key generate --keyType=RSA │
+ │ waltid key generate --keyType=RSA -o myRsaKey.json │
+ │ │
+ │ Key conversion │
+ │ --------------- │
+ │ waltid key convert --input=myRsaKey.pem │
+ ╰─────────────────────────────────────────────────────────────────────────╯
+
+Options:
+ -h, --help Show this message and exit
+
+Commands:
+ key Key management features
+```
+
+## `waltid key` command
+
+```bash
+Usage: waltid key [] []...
+
+ Key management features
+
+Options:
+ -h, --help Show this message and exit
+
+Commands:
+ generate Generates a new cryptographic key.
+ convert Convert key files between PEM and JWK formats.
+```
+
+## `waltid key generate` command
+
+```bash
+Usage: waltid key generate []
+
+ Generates a new cryptographic key.
+
+Options:
+ -t, --keyType=(Ed25519|secp256k1|secp256r1|RSA)
+ Key type to use. Possible values are: [Ed25519 |
+ secp256k1 | secp256r1 | RSA]. Default value is Ed25519
+ -o, --output= File path to save the generated key. Default value is
+ .json
+ -h, --help Show this message and exit
+```
+
+## `waltid key convert` command
+
+```bash
+Usage: waltid key convert []
+
+ Convert key files between PEM and JWK formats.
+
+Options:
+ -i, --input= The input file path. Accepted formats are: JWK and
+ PEM
+ -o, --output= The output file path. Accepted formats are: JWK and
+ PEM. If not provided the input filename will be used
+ with a different extension.
+ -p, --passphrase= Passphrase to open an encrypted PEM
+ -h, --help Show this message and exit
+```
+
+# Compatibility
+
+This project is still a work in progress. As such, not all features are already implemented.
+
+## Key Management
+
+### key generate
+
+* Supported key types
+ * Ed25519 ✅
+ * secp256k1 ✅
+ * secp256r1 ✅
+ * RSA ✅
+* Export formats
+ * JWK ✅
+ * PEM ❌
+
+### `key convert
+
+* Input formats
+ * PEM ✅
+ * JWK ✅
+* Output formats
+ * PEM ❌
+ * JWK ✅
+* PEM Content
+ * RSA Private Key ✅
+ * RSA Public Key ✅
+ * RSA Encrypted Private Key ✅
+ * Ed25519 ❌
+ * secp256k1 ❌
+ * secp256r1 ❌
+
diff --git a/waltid-cli/build.gradle.kts b/waltid-cli/build.gradle.kts
index 9ed81ade4..003ce7dad 100644
--- a/waltid-cli/build.gradle.kts
+++ b/waltid-cli/build.gradle.kts
@@ -5,6 +5,8 @@ plugins {
kotlin("plugin.serialization")
id("maven-publish")
id("com.github.ben-manes.versions")
+ // Apply the application plugin to add support for building a CLI application in Java.
+ application
}
group = "id.walt.cli"
@@ -43,6 +45,9 @@ kotlin {
api(project(":waltid-sdjwt"))
api(project(":waltid-openid4vc"))
+ // kotlinx-io
+ implementation("org.jetbrains.kotlinx:kotlinx-io-core:0.3.1")
+
// CLI
implementation("com.varabyte.kotter:kotter-jvm:1.1.2")
implementation("com.github.ajalt.mordant:mordant:2.3.0")
@@ -65,12 +70,18 @@ kotlin {
dependencies {
// Logging
implementation("org.slf4j:slf4j-simple:2.0.12")
+
+ // JOSE
+ implementation("com.nimbusds:nimbus-jose-jwt:9.37.3")
+
+ // BouncyCastle for PEM import
+ implementation("org.bouncycastle:bcpkix-lts8on:2.73.4")
}
}
val jvmTest by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
- implementation("org.junit.jupiter:junit-jupiter-params:5.10.2")
+ implementation("com.wolpl.clikt-testkit:clikt-testkit:2.0.0")
}
}
/*publishing {
@@ -104,9 +115,14 @@ kotlin {
}
}
+application {
+ // Define the main class for the application.
+ // Works with:
+ // ../gradlew run --args="--help"
+ mainClass = "id.walt.cli.MainKt"
-tasks.withType {
- rejectVersionIf {
- listOf("-beta", "-alpha", "-rc").any { it in candidate.version.lowercase() } || candidate.version.takeLast(4).contains("RC")
- }
+}
+
+tasks.test {
+ useJUnitPlatform()
}
diff --git a/waltid-cli/src/commonMain/kotlin/id/walt/cli/Main.kt b/waltid-cli/src/commonMain/kotlin/id/walt/cli/Main.kt
index d3dde7a0c..e6221183b 100644
--- a/waltid-cli/src/commonMain/kotlin/id/walt/cli/Main.kt
+++ b/waltid-cli/src/commonMain/kotlin/id/walt/cli/Main.kt
@@ -1,24 +1,77 @@
package id.walt.cli
-import com.github.ajalt.clikt.core.subcommands
-import id.walt.cli.commands.Did
+import com.github.ajalt.clikt.core.*
+import com.github.ajalt.clikt.output.ParameterFormatter
+import com.github.ajalt.mordant.rendering.TextColors
+import com.github.ajalt.mordant.rendering.Whitespace
+import com.github.ajalt.mordant.widgets.Panel
+import com.github.ajalt.mordant.widgets.Text
+import kotlin.system.exitProcess
fun main(args: Array) {
- println("-- walt.id SSI Kit v2 CLI --")
+ val cmd = WaltIdCmd()
+ try {
+ cmd.parse(args)
+ } catch (e: PrintHelpMessage) {
+ cmd.echoFormattedHelp(e)
+ exitProcess(e.statusCode)
+ } catch (e: InvalidFileFormat) {
+ printError(cmd, e)
+ printUsage(cmd, e)
+ exitProcess(e.statusCode)
+ } catch (e: MultiUsageError) {
+ var msgs = "Invalid command. Please, review the usage instructions bellow and try again."
+ // for (error in e.errors) {
+ // if (msgs.length == 0) {
+ // // msgs = error.formatMessage(error.context!!.localization, parameterFormatter(error.context!!))
+ // msgs = "${error.localizedMessage} - ${error.message} "
+ // } else {
+ // msgs = """${msgs} ${error.toString() ?: ""}"""
+ // }
+ // }
+ printError(cmd, e, msgs)
+ printUsage(cmd, e)
- MainCommand()
- .subcommands(
- Did().subcommands(
- Did.Create()
- )
+ } catch (e: CliktError) {
+ printError(cmd, e)
+ printUsage(cmd, e)
+ exitProcess(e.statusCode)
+ }
+}
+
+fun printError(cmd: CliktCommand, e: CliktError? = null, msg: String? = null) {
+ println("\n")
+ val msgToPrint = msg ?: e?.let { it.localizedMessage }
+ cmd.terminal.println(
+ Panel(
+ content = Text(TextColors.brightRed(msgToPrint!!), whitespace = Whitespace.NORMAL, width = 70),
+ title = Text(TextColors.red("ERROR"))
)
- .main(args)
+ )
+ println("\n")
+}
+
+fun printUsage(cmd: CliktCommand, e: CliktError) {
+ val ctx = (e as ContextCliktError).context
+ cmd.echoFormattedHelp(PrintHelpMessage(ctx))
+}
+
+fun parameterFormatter(context: Context): ParameterFormatter {
+ return object : ParameterFormatter {
+ override fun formatOption(name: String): String {
+ // return styleOptionName(name)
+ return context.theme.style("info")(name)
+ }
+ override fun formatArgument(name: String): String {
+ // return styleArgumentName(normalizeParameter(name))
+ return context.theme.style("info")("<${name.lowercase()}>")
+ }
- /*WaltidServices.init()
- WaltidServices.minimalInit()
+ override fun formatSubcommand(name: String): String {
+ // return styleSubcommandName(name)
+ return context.theme.style("info")(name)
+ }
- val dr = DidService.register(DidWebCreateOptions("localhost:3000"))
- println(dr.did)
- println(dr.didDocument)*/
+ }
}
diff --git a/waltid-cli/src/commonMain/kotlin/id/walt/cli/MainCommand.kt b/waltid-cli/src/commonMain/kotlin/id/walt/cli/MainCommand.kt
deleted file mode 100644
index c3742574a..000000000
--- a/waltid-cli/src/commonMain/kotlin/id/walt/cli/MainCommand.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package id.walt.cli
-
-import com.github.ajalt.clikt.core.CliktCommand
-
-class MainCommand() : CliktCommand() {
- override fun run() = Unit
-}
diff --git a/waltid-cli/src/commonMain/kotlin/id/walt/cli/WaltIdCmd.kt b/waltid-cli/src/commonMain/kotlin/id/walt/cli/WaltIdCmd.kt
new file mode 100644
index 000000000..b6c34a039
--- /dev/null
+++ b/waltid-cli/src/commonMain/kotlin/id/walt/cli/WaltIdCmd.kt
@@ -0,0 +1,49 @@
+package id.walt.cli
+
+import com.github.ajalt.clikt.core.CliktCommand
+import com.github.ajalt.clikt.core.subcommands
+import com.github.ajalt.clikt.parameters.groups.provideDelegate
+import id.walt.cli.commands.CommonOptions
+import id.walt.cli.commands.KeyCmd
+
+class WaltIdCmd : CliktCommand(
+ name = "waltid",
+ help = """walt.id CLI
+
+ The walt.id CLI is a command line tool that allows you to onboard and
+ use a SSI (Self-Sovereign-Identity) ecosystem. You can manage
+ cryptographic keys, generate and register W3C Decentralized
+ Identifiers (DIDs) as well as create, issue & verify W3C Verifiable
+ credentials (VCs).
+
+ Example commands are:
+
+ Print usage instructions
+ -------------------------
+ waltid -h
+ waltid --help
+ waltid key -h
+ waltid key generate -h
+ waltid key convert -h
+
+ Key generation
+ ---------------
+ waltid key generate
+ waltid key generate -t secp256k1
+ waltid key generate --keyType=RSA
+ waltid key generate --keyType=RSA -o myRsaKey.json
+
+ Key conversion
+ ---------------
+ waltid key convert --input=myRsaKey.pem
+ """,
+ printHelpOnEmptyArgs = true
+) {
+ init {
+ subcommands(KeyCmd())
+ }
+
+ private val commonOptions by CommonOptions()
+
+ override fun run() = Unit
+}
diff --git a/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/CommandConfigUtils.kt b/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/CommandConfigUtils.kt
index 3ff78145e..0e17e0b22 100644
--- a/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/CommandConfigUtils.kt
+++ b/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/CommandConfigUtils.kt
@@ -4,5 +4,7 @@ import kotlin.reflect.KClass
object CommandConfigUtils {
operator fun Map.get(key: KClass<*>): String = this[key.simpleName]!!
- operator fun MutableMap.set(key: KClass<*>, value: String) { this[key.simpleName!!] = value }
+ operator fun MutableMap.set(key: KClass<*>, value: String) {
+ this[key.simpleName!!] = value
+ }
}
diff --git a/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/CommonOptions.kt b/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/CommonOptions.kt
new file mode 100644
index 000000000..1a1167a3a
--- /dev/null
+++ b/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/CommonOptions.kt
@@ -0,0 +1,11 @@
+package id.walt.cli.commands
+
+import com.github.ajalt.clikt.parameters.groups.OptionGroup
+import com.github.ajalt.clikt.parameters.options.flag
+import com.github.ajalt.clikt.parameters.options.help
+import com.github.ajalt.clikt.parameters.options.option
+
+class CommonOptions : OptionGroup("Common Options") {
+ val verbose by option().flag().help("Set verbose mode ON")
+
+}
\ No newline at end of file
diff --git a/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/Did.kt b/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/Did.kt
index beaa37260..a5fac3768 100644
--- a/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/Did.kt
+++ b/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/Did.kt
@@ -2,14 +2,9 @@ package id.walt.cli.commands
import com.github.ajalt.clikt.completion.CompletionCandidates
import com.github.ajalt.clikt.core.CliktCommand
-import com.github.ajalt.clikt.core.findOrSetObject
-import com.github.ajalt.clikt.core.requireObject
-import com.github.ajalt.clikt.output.TermUi
import com.github.ajalt.clikt.parameters.options.default
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.types.enum
-import id.walt.cli.commands.CommandConfigUtils.get
-import id.walt.cli.commands.CommandConfigUtils.set
import id.walt.did.dids.DidService
import kotlinx.coroutines.runBlocking
diff --git a/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/KeyCmd.kt b/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/KeyCmd.kt
new file mode 100644
index 000000000..ceb345455
--- /dev/null
+++ b/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/KeyCmd.kt
@@ -0,0 +1,22 @@
+package id.walt.cli.commands
+
+import com.github.ajalt.clikt.core.CliktCommand
+import com.github.ajalt.clikt.core.subcommands
+import com.github.ajalt.clikt.parameters.groups.provideDelegate
+
+class KeyCmd : CliktCommand(
+ name = "key",
+ help = "Key management features",
+ printHelpOnEmptyArgs = true
+) {
+
+ init {
+ subcommands(KeyGenerateCmd(), KeyConvertCmd())
+ }
+
+ private val commonOptions by CommonOptions()
+
+ override fun run() = Unit
+}
+
+fun main(args: Array) = KeyCmd().main(args)
diff --git a/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/KeyConvertCmd.kt b/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/KeyConvertCmd.kt
new file mode 100644
index 000000000..2b2a8e0c8
--- /dev/null
+++ b/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/KeyConvertCmd.kt
@@ -0,0 +1,231 @@
+package id.walt.cli.commands
+
+import com.github.ajalt.clikt.core.*
+import com.github.ajalt.clikt.parameters.groups.provideDelegate
+import com.github.ajalt.clikt.parameters.options.defaultLazy
+import com.github.ajalt.clikt.parameters.options.help
+import com.github.ajalt.clikt.parameters.options.option
+import com.github.ajalt.clikt.parameters.options.required
+import com.github.ajalt.clikt.parameters.types.file
+import com.github.ajalt.mordant.markdown.Markdown
+import com.github.ajalt.mordant.rendering.TextColors
+import com.github.ajalt.mordant.rendering.TextStyles
+import com.github.ajalt.mordant.terminal.YesNoPrompt
+import id.walt.crypto.keys.Key
+import id.walt.crypto.keys.LocalKey
+import kotlinx.coroutines.runBlocking
+import org.bouncycastle.jce.provider.BouncyCastleProvider
+import org.bouncycastle.openssl.PEMEncryptedKeyPair
+import org.bouncycastle.openssl.PEMParser
+import org.bouncycastle.openssl.bc.BcPEMDecryptorProvider
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter
+import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo
+import org.bouncycastle.pkcs.PKCSException
+import org.bouncycastle.pkcs.jcajce.JcePKCSPBEInputDecryptorProviderBuilder
+import java.io.File
+import java.io.FileReader
+import java.security.PrivateKey
+import java.security.Security
+import java.util.*
+import kotlin.io.path.useLines
+import kotlin.js.ExperimentalJsExport
+
+
+enum class KeyFileFormat {
+ JWK, PEM, ENCRYPTED_PEM;
+
+ companion object {
+ fun getNames() = KeyFileFormat.entries.joinToString { it.name }
+ }
+}
+
+@OptIn(ExperimentalJsExport::class)
+class KeyConvertCmd : CliktCommand(
+ name = "convert", help = "Convert key files between PEM and JWK formats.", printHelpOnEmptyArgs = true
+) {
+
+ private val input by option("-i", "--input").help("The input file path. Accepted formats are: JWK and PEM")
+ .file(mustExist = true, canBeFile = true, canBeDir = false, mustBeReadable = true).required()
+
+
+ private val sourceKeyType by lazy { getKeyType(input) }
+ private val targetKeyType by lazy { getOutputKeyType(sourceKeyType) }
+
+ private val output by option(
+ "-o",
+ "--output"
+ ).help("The output file path. Accepted formats are: JWK and PEM. If not provided the input filename will be used with a different extension.")
+ .file().defaultLazy {
+ File(input.parent, "${input.nameWithoutExtension}.${targetKeyType.toString().lowercase()}")
+ }
+
+ private val passphrase by option("-p", "--passphrase").help("Passphrase to open an encrypted PEM")
+ // .prompt(text = "Please, inform the PEM passphrase", hideInput = true)
+
+ private val commonOptions by CommonOptions()
+
+ override fun run() {
+ // Read the source key from the input file
+ echo(TextStyles.dim("Reading key \"${input.absolutePath}\"..."))
+ val inputKey = runBlocking { getKey(input) }
+
+ echo(TextStyles.dim("Converting key \"${input.absolutePath}\"..."))
+ val outputContent = runBlocking { convertKey(inputKey, targetKeyType) }
+
+ echo(TextColors.green("Converted Key (${targetKeyType}):"))
+ terminal.println(
+ Markdown(
+ """
+ |```${if (targetKeyType == KeyFileFormat.JWK) "json" else ""}
+ |$outputContent
+ |```
+ """.trimMargin()
+ )
+ )
+
+ if (output.exists()
+ && YesNoPrompt(
+ "The file \"${output.absolutePath}\" already exists, do you want to overwrite it?",
+ terminal
+ ).ask() == false
+ ) {
+ echo("Will not overwrite output file.")
+ return
+ }
+
+ output.writeText(outputContent)
+
+ echo("${TextColors.brightGreen("Done.")} Converted \"${input.absolutePath}\" to \"${output.absolutePath}\".")
+ }
+
+ private operator fun Regex.contains(text: CharSequence?): Boolean = this.matches(text ?: "")
+ private fun getKeyType(input: File): KeyFileFormat = input.toPath().useLines { lines ->
+ when (lines.firstOrNull()) {
+ null -> throw InvalidFileFormat(input.path, "No lines in file.")
+
+ // other PEM content types: https://github.com/openssl/openssl/blob/master/include/openssl/pem.h
+ in Regex("""^\{.*""") -> KeyFileFormat.JWK
+ in Regex("""^-+BEGIN .*PUBLIC KEY-+""") -> KeyFileFormat.PEM
+ in Regex("""^-+BEGIN .*OPENSSH PRIVATE KEY-+""") -> KeyFileFormat.PEM
+ in Regex("""^-+BEGIN .*EC PARAMETERS-+""") -> KeyFileFormat.PEM
+ in Regex("""^-+BEGIN .*ENCRYPTED PRIVATE KEY-+""") -> KeyFileFormat.ENCRYPTED_PEM
+ in Regex("""^-+BEGIN .*PRIVATE KEY-+""") -> KeyFileFormat.PEM
+ else -> throw InvalidFileFormat(input.path, "Unknown file format (expected ${KeyFileFormat.getNames()}).")
+ }
+ }
+
+ private suspend fun getKey(input: File, keyType: KeyFileFormat = getKeyType(input)): LocalKey {
+ val inputContent = input.readText()
+ try {
+ return runCatching {
+ when (keyType) {
+ KeyFileFormat.JWK -> LocalKey.importJWK(inputContent).getOrThrow()
+ KeyFileFormat.PEM -> LocalKey.importPEM(inputContent).getOrThrow()
+ KeyFileFormat.ENCRYPTED_PEM -> LocalKey.importPEM(decrypt(input).getOrThrow()).getOrThrow()
+ }
+ }.getOrThrow()
+ } catch (e: Throwable) {
+ var mainMsg = "Invalid file format."
+ var complementaryMsg = "Use the --verbose flag to get more details."
+
+ if (this.commonOptions.verbose) {
+ complementaryMsg = e.localizedMessage ?: "No more details to show."
+ }
+ throw InvalidFileFormat(input.absolutePath, "$mainMsg $complementaryMsg")
+ }
+
+ }
+
+ /**
+ * If the provided file is of type JWK, convert it to PEM.
+ * If PEM, convert it to JWK.
+ */
+ private fun getOutputKeyType(inputKeyType: KeyFileFormat): KeyFileFormat =
+ if (inputKeyType == KeyFileFormat.JWK) KeyFileFormat.PEM else KeyFileFormat.JWK
+
+
+ /**
+ Convert provided source key to specified target key type
+ */
+ private suspend fun convertKey(inputKey: Key, targetKeyType: KeyFileFormat): String {
+ try {
+ return when (targetKeyType) {
+ KeyFileFormat.JWK -> inputKey.exportJWK()
+ KeyFileFormat.PEM -> inputKey.exportPEM()
+ KeyFileFormat.ENCRYPTED_PEM -> inputKey.exportPEM()
+ }
+ } catch (e: Throwable) {
+ val mainMsg = "Oops. Something went wrong when converting the key."
+ var complementaryMsg = "Use the --verbose flag to get more details."
+
+ if (this.commonOptions.verbose) {
+ complementaryMsg = e.localizedMessage ?: "No more details to show."
+ }
+
+ throw UsageError("$mainMsg $complementaryMsg")
+ }
+ }
+
+ private fun decrypt(input: File): Result {
+
+ lateinit var k: PrivateKey
+
+ try {
+ val decipherKey: String
+
+
+ if (passphrase == null) {
+ decipherKey = terminal.prompt("Key encrypted. Please, inform the passphrase to decipher it")!!
+ if (decipherKey == null) { // TODO: Can happen?
+ return Result.failure(BadParameterValue(passphrase!!))
+ }
+ } else {
+ decipherKey = passphrase as String
+ }
+ val decryptedPEM = decryptKey(decipherKey) // as BCRSAPrivateCrtKey
+ return Result.success(decryptedPEM)
+ } catch (e: Exception) {
+ return Result.failure(e)
+ }
+ }
+
+ // TODO: Extract
+ fun decryptKey(passphrase: String): String {
+ Security.addProvider(BouncyCastleProvider()) // TODO: do not add at every function call
+
+ try {
+ val pemParser = PEMParser(FileReader(input))
+ val pemObject = runCatching {
+ pemParser.readObject() ?: error("No object in PEM")
+ }.getOrElse { throw IllegalArgumentException("Could not parse object from PEM", it) }
+ val pki = when (pemObject) {
+ is PKCS8EncryptedPrivateKeyInfo -> {
+ val decryptorProviderBuilder = JcePKCSPBEInputDecryptorProviderBuilder().setProvider("BC")
+ val inputDecryptorProvider = decryptorProviderBuilder.build(passphrase.toCharArray())
+
+ pemObject.decryptPrivateKeyInfo(inputDecryptorProvider)
+ }
+
+ is PEMEncryptedKeyPair -> {
+ val pkp = pemObject.decryptKeyPair(BcPEMDecryptorProvider(passphrase.toCharArray()))
+
+ pkp.privateKeyInfo
+ }
+
+ else -> throw PKCSException("Invalid encrypted private key class: " + pemObject::class.qualifiedName)
+ }
+
+ val converter = JcaPEMKeyConverter().setProvider("BC")
+
+ val encodedKey = Base64.getEncoder().encodeToString(converter.getPrivateKey(pki).encoded)
+
+ return """
+ -----BEGIN PRIVATE KEY-----
+ $encodedKey
+ -----END PRIVATE KEY-----
+ """.trimIndent()
+ } catch (e: Exception) {
+ throw Exception("Failed to load key from $input: $e")
+ }
+ }
+}
diff --git a/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/KeyGenerateCmd.kt b/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/KeyGenerateCmd.kt
new file mode 100644
index 000000000..0dbc10a7e
--- /dev/null
+++ b/waltid-cli/src/commonMain/kotlin/id/walt/cli/commands/KeyGenerateCmd.kt
@@ -0,0 +1,77 @@
+package id.walt.cli.commands
+
+import com.github.ajalt.clikt.core.CliktCommand
+import com.github.ajalt.clikt.core.terminal
+import com.github.ajalt.clikt.parameters.groups.provideDelegate
+import com.github.ajalt.clikt.parameters.options.default
+import com.github.ajalt.clikt.parameters.options.help
+import com.github.ajalt.clikt.parameters.options.option
+import com.github.ajalt.clikt.parameters.types.enum
+import com.github.ajalt.clikt.parameters.types.path
+import com.github.ajalt.mordant.markdown.Markdown
+import com.github.ajalt.mordant.rendering.TextColors
+import com.github.ajalt.mordant.rendering.TextStyles
+import com.github.ajalt.mordant.terminal.YesNoPrompt
+import id.walt.crypto.keys.KeyType
+import id.walt.crypto.keys.LocalKey
+import kotlinx.coroutines.runBlocking
+import kotlin.io.path.Path
+import kotlin.io.path.absolutePathString
+import kotlin.io.path.exists
+import kotlin.io.path.writeText
+import kotlin.js.ExperimentalJsExport
+
+@OptIn(ExperimentalJsExport::class)
+class KeyGenerateCmd : CliktCommand(
+ name = "generate",
+ help = "Generates a new cryptographic key.",
+ // printHelpOnEmptyArgs = true
+) {
+
+ private val acceptedKeyTypes = KeyType.entries.joinToString(" | ")
+
+ private val keyType by option("-t", "--keyType")
+ .enum()
+ .help("Key type to use. Possible values are: [${acceptedKeyTypes}]. Default value is " + KeyType.Ed25519.name)
+ .default(KeyType.Ed25519)
+
+ private val optOutputFilePath by option("-o", "--output")
+ .path()
+ .help("File path to save the generated key. Default value is .json")
+
+ private val commonOptions by CommonOptions()
+
+ override fun run() {
+ echo(TextStyles.dim("Generating key of type ${keyType.name}..."))
+ runBlocking {
+ val key = LocalKey.generate(keyType)
+
+ echo(TextStyles.dim("Key thumbprint is: ${key.getThumbprint()}"))
+
+ val jwk = key.exportJWKPretty()
+
+ echo(TextColors.green("Generated Key (JWK):"))
+ terminal.println(Markdown("""
+ |```json
+ |$jwk
+ |```
+ """.trimMargin()))
+
+ val outputFile = optOutputFilePath ?: Path("${key.getKeyId()}.json")
+
+ if (outputFile.exists()
+ && YesNoPrompt(
+ "The file \"${outputFile.absolutePathString()}\" already exists, do you want to overwrite it?",
+ terminal
+ ).ask() == false
+ ) {
+ echo("Will not overwrite output file.")
+ return@runBlocking
+ }
+
+ outputFile.writeText(jwk)
+ echo("${TextColors.brightGreen("Done.")} Key saved at file \"${outputFile.absolutePathString()}\".")
+ }
+ }
+}
+
diff --git a/waltid-cli/src/jvmTest/kotlin/id/walt/cli/ImportAndExportTests.kt b/waltid-cli/src/jvmTest/kotlin/id/walt/cli/ImportAndExportTests.kt
new file mode 100644
index 000000000..6ec8d9144
--- /dev/null
+++ b/waltid-cli/src/jvmTest/kotlin/id/walt/cli/ImportAndExportTests.kt
@@ -0,0 +1,63 @@
+package id.walt.cli
+
+import id.walt.crypto.keys.KeyType
+import id.walt.crypto.keys.LocalKey
+import kotlinx.coroutines.runBlocking
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.jsonObject
+import org.junit.jupiter.api.assertDoesNotThrow
+import kotlin.test.Ignore
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class ImportAndExportTests {
+
+ @Test
+ @Ignore
+ fun testRSA() {
+ testBidirectionalConversion(KeyType.RSA) // Not working. Keving is looking at it.
+ }
+
+ @Test
+ @Ignore
+ fun testEd25519() {
+ // kotlin.NotImplementedError: Ed25519 keys cannot be exported as PEM yet.
+ testBidirectionalConversion(KeyType.Ed25519)
+ }
+
+ @Test
+ fun testSecp256k1() {
+ testBidirectionalConversion(KeyType.secp256k1)
+ }
+
+ @Test
+ fun testSecp256r1() {
+ testBidirectionalConversion(KeyType.secp256r1)
+ }
+
+ private fun testBidirectionalConversion(keyType: KeyType) {
+
+ val generatedKey = runBlocking { LocalKey.generate(keyType) }
+ val generatedJwk = runBlocking { generatedKey.exportJWK() }
+
+ val importedKeyFromJwk = assertDoesNotThrow {
+ runBlocking { LocalKey.importJWK(generatedJwk).getOrThrow() }
+ }
+
+ println("Export PEM:")
+ val exportedPem = runBlocking { importedKeyFromJwk.exportPEM() }
+ println(exportedPem)
+
+ val importedKeyFromPem = assertDoesNotThrow {
+ runBlocking { LocalKey.importPEM(exportedPem).getOrThrow() }
+ }
+
+ val exportedJwk = runBlocking { importedKeyFromPem.exportJWK() }
+
+
+ assertEquals(
+ expected = Json.parseToJsonElement(generatedJwk).jsonObject.filterKeys { it != "kid" },
+ actual = Json.parseToJsonElement(exportedJwk).jsonObject.filterKeys { it != "kid" },
+ )
+ }
+}
diff --git a/waltid-cli/src/jvmTest/kotlin/id/walt/cli/PemImportTests.kt b/waltid-cli/src/jvmTest/kotlin/id/walt/cli/PemImportTests.kt
new file mode 100644
index 000000000..a94a9a808
--- /dev/null
+++ b/waltid-cli/src/jvmTest/kotlin/id/walt/cli/PemImportTests.kt
@@ -0,0 +1,40 @@
+package id.walt.cli
+
+import id.walt.crypto.keys.LocalKey
+import kotlinx.coroutines.test.runTest
+import kotlin.test.Test
+
+class PemImportTests {
+
+ private val publicSecp256k1Key = """
+ -----BEGIN PUBLIC KEY-----
+ MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAED7KA0wd4qOX37HvlneDt1XdfV8fVRG2x
+ WQGCjsO8s0fxZ09kNE4bMKQoDfpRSnplCBJ93SnPHUFSJQu5CeFMew==
+ -----END PUBLIC KEY-----
+ """.trimIndent()
+
+ private val privateSecp256k1Key = """
+ -----BEGIN PRIVATE KEY-----
+ MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQghItf3kprlQm9bYmnDKch
+ RxBRCWaQBhKi+b2sSjCxCKmhRANCAAQPsoDTB3io5ffse+Wd4O3Vd19Xx9VEbbFZ
+ AYKOw7yzR/FnT2Q0ThswpCgN+lFKemUIEn3dKc8dQVIlC7kJ4Ux7
+ -----END PRIVATE KEY-----
+ -----BEGIN PUBLIC KEY-----
+ MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAED7KA0wd4qOX37HvlneDt1XdfV8fVRG2x
+ WQGCjsO8s0fxZ09kNE4bMKQoDfpRSnplCBJ93SnPHUFSJQu5CeFMew==
+ -----END PUBLIC KEY-----
+ """.trimIndent()
+
+ @Test
+ fun testImportPublicSecp256k1KeyPem() = runTest {
+ val imported = LocalKey.importPEM(publicSecp256k1Key)
+ println(imported.getOrThrow())
+ }
+
+ @Test
+ fun testImportPrivateSecp256k1KeyPem() = runTest {
+ val imported = LocalKey.importPEM(privateSecp256k1Key)
+ println(imported.getOrThrow())
+ }
+
+}
diff --git a/waltid-cli/src/jvmTest/kotlin/id/walt/cli/WaltIdCmdTest.kt b/waltid-cli/src/jvmTest/kotlin/id/walt/cli/WaltIdCmdTest.kt
new file mode 100644
index 000000000..138afb3ab
--- /dev/null
+++ b/waltid-cli/src/jvmTest/kotlin/id/walt/cli/WaltIdCmdTest.kt
@@ -0,0 +1,29 @@
+package id.walt.cli
+
+import com.github.ajalt.clikt.core.PrintHelpMessage
+import com.github.ajalt.clikt.testing.test
+import kotlinx.coroutines.test.runTest
+import kotlin.test.Test
+import kotlin.test.assertContains
+import kotlin.test.assertFailsWith
+
+class WaltIdCmdTest {
+
+ @Test
+ fun testMainNoArgs() = runTest {
+ val command = WaltIdCmd()
+ assertFailsWith {
+ command.parse(emptyList())
+ }
+ val result = command.test()
+ assertContains(result.stdout, "The walt.id CLI is a command line tool")
+ }
+
+ @Test
+ fun testMainHelp() = runTest {
+ val command = WaltIdCmd()
+ assertFailsWith(message = "Walt.id CLI usage") {
+ command.parse(listOf("--help"))
+ }
+ }
+}
\ No newline at end of file
diff --git a/waltid-cli/src/jvmTest/kotlin/id/walt/cli/WaltIdKeyCmdTest.kt b/waltid-cli/src/jvmTest/kotlin/id/walt/cli/WaltIdKeyCmdTest.kt
new file mode 100644
index 000000000..fa2c81b2c
--- /dev/null
+++ b/waltid-cli/src/jvmTest/kotlin/id/walt/cli/WaltIdKeyCmdTest.kt
@@ -0,0 +1,35 @@
+package id.walt.cli
+
+import com.github.ajalt.clikt.core.PrintHelpMessage
+import com.github.ajalt.clikt.testing.test
+import id.walt.cli.commands.KeyCmd
+import kotlinx.coroutines.test.runTest
+import kotlin.test.Test
+import kotlin.test.assertFailsWith
+import kotlin.test.assertTrue
+
+class WaltIdKeyCmdTest {
+
+ @Test
+ fun testKeyCmd() = runTest {
+ val command = KeyCmd()
+ val result = assertFailsWith {
+ command.parse(emptyList())
+ }
+
+ // TODO worth it?
+ result.message?.let { assertTrue(it.contains("Key management")) }
+
+ val result2 = command.test()
+ assertTrue(result2.stdout.contains("Key management features", ignoreCase = true))
+ }
+
+ @Test
+ fun testKeyHelp() {
+ val command = KeyCmd()
+
+ assertFailsWith {
+ command.parse(listOf("--help"))
+ }
+ }
+}
diff --git a/waltid-cli/src/jvmTest/kotlin/id/walt/cli/WaltIdKeyConvertCmdTest.kt b/waltid-cli/src/jvmTest/kotlin/id/walt/cli/WaltIdKeyConvertCmdTest.kt
new file mode 100644
index 000000000..a702d7680
--- /dev/null
+++ b/waltid-cli/src/jvmTest/kotlin/id/walt/cli/WaltIdKeyConvertCmdTest.kt
@@ -0,0 +1,530 @@
+package id.walt.cli
+
+import com.github.ajalt.clikt.core.*
+import com.github.ajalt.clikt.testing.test
+import com.github.ajalt.mordant.rendering.AnsiLevel
+import com.github.ajalt.mordant.terminal.PrintRequest
+import com.github.ajalt.mordant.terminal.Terminal
+import com.github.ajalt.mordant.terminal.TerminalInfo
+import com.github.ajalt.mordant.terminal.TerminalInterface
+import id.walt.cli.commands.KeyConvertCmd
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.test.runTest
+import java.io.File
+import java.io.FileNotFoundException
+import java.io.StringWriter
+import java.net.URI
+import kotlin.test.*
+
+class WaltIdKeyConvertCmdTest {
+
+ fun checkHelpMessage(command: KeyConvertCmd, args: List) {
+ assertFailsWith {
+ command.parse(args)
+ }
+
+ val result = command.test(args)
+ val reHelpMsg = "Convert key files".toRegex()
+ val reOpt1 = "-i, --input".toRegex()
+ val reOpt2 = "-o, --output".toRegex()
+
+ assertContains(result.stdout, reHelpMsg)
+ assertContains(result.stdout, reOpt1)
+ assertContains(result.stdout, reOpt2)
+ }
+
+ @Test
+ fun `should print a help message`() {
+ checkHelpMessage(KeyConvertCmd(), listOf("--help"))
+ }
+
+ // class WaltIdKeyConvertCmdStdOutTest {
+ @Test
+ fun `should print help message when called with no parameter`() = runTest {
+ val command = KeyConvertCmd()
+ val result = command.test(emptyList())
+ val expected = "Convert key files.*".toRegex()
+ assertContains(result.output, expected)
+ }
+
+ @Test
+ fun `should fail if --input is provided with no value`() {
+ val command = KeyConvertCmd()
+
+ val failure = assertFailsWith {
+ command.parse(listOf("-i"))
+ }
+
+ assertTrue(failure.errors.any { it is IncorrectOptionValueCount })
+ assertTrue(failure.errors.any { it is MissingOption })
+ }
+
+ @Test
+ fun `should fail if --ouput is provided with no value`() {
+ val command = KeyConvertCmd()
+
+ val failure = assertFailsWith {
+ command.parse(listOf("-o"))
+ }
+
+ assertTrue(failure.errors.any { it is IncorrectOptionValueCount })
+ assertTrue(failure.errors.any { it is MissingOption })
+ }
+
+ @Test
+ fun `should NOT fail if --output is not provided`() {
+
+ // openssl genrsa -out src/jvmTest/resources/rsa_by_openssl_pvt_key.pem 3072
+ // openssl rsa -in src/jvmTest/resources/rsa_by_openssl_pvt_key.pem -pubout -out src/jvmTest/resources/rsa_by_openssl_pub_key.pem
+ val inputFileName = "rsa_by_openssl_pvt_key.pem"
+ val outputFileName = "rsa_by_openssl_pvt_key.jwk"
+
+ val inputFilePath = getFilePath(inputFileName)
+ val outputFilePath = getOutputFilePath(inputFilePath, outputFileName)
+
+ KeyConvertCmd().parse(listOf("--input=${inputFilePath}"))
+
+ // If the execution reaches this point, it means the command above didn't throw any exception
+ assertTrue(true)
+
+ deleteOutputFile(outputFilePath)
+ }
+
+ @Test
+ fun `should fail if a non-existent input file is provided`() {
+ val command = KeyConvertCmd()
+
+ val inputFilename = "foo.jwk"
+ val result = command.test(listOf("-i$inputFilename"))
+
+ val expected = """.*file "$inputFilename" does not exist.*""".toRegex(RegexOption.IGNORE_CASE)
+ assertContains(result.stderr, expected)
+ }
+
+ @Test
+ fun `should fail with invalid input file`() {
+
+ // Stored in src/jvmTest/resources
+ val inputFileName = "invalidKey.jwk"
+ var inputFilePath = getFilePath(inputFileName)
+
+ var result = KeyConvertCmd().test("--input=\"$inputFilePath\" --verbose")
+ var expectedErrorMessage = ".*Invalid file format*".toRegex()
+ assertContains(result.stderr, expectedErrorMessage)
+
+ result = KeyConvertCmd().test("--input=\"$inputFilePath\" --verbose")
+ expectedErrorMessage = ".*Missing key type \"kty\" parameter*".toRegex()
+ assertContains(result.stderr, expectedErrorMessage)
+ }
+
+ @Test
+ fun `should prompt for overwrite confirmation when the output file already exists`() {
+ // Stored in src/jvmTest/resources
+ val inputFileName = "rsa_by_openssl_pub_key.pem"
+ val outputFileName = "existingFile.jwk"
+
+ val inputFilePath = getFilePath(inputFileName)
+ val outputFilePath = getOutputFilePath(inputFilePath, outputFileName)
+
+ // Creates the output file to simulate its previous existence
+ File(outputFilePath).createNewFile()
+
+ val result = KeyConvertCmd().test("--input=\"$inputFilePath\" --output=\"$outputFilePath\"")
+ val expectedOutput = """.*The file "$outputFilePath" already exists.*""".toRegex()
+
+ // Assert successful logging message
+ assertContains(result.stdout, expectedOutput)
+ }
+
+ @Test
+ fun `should convert JWT input file to PEM`() {
+
+ // Stored in src/jvmTest/resources
+ val inputFileName = "ed25519_by_waltid_pvt_key.jwk"
+ val outputFileName = "ed25519_by_waltid_pvt_key.pem"
+
+ val inputFilePath = getFilePath(inputFileName)
+ val outputFilePath = getOutputFilePath(inputFilePath, outputFileName)
+
+ // Only as long as Ed25519 is not fully supported in LocalKey.exportPEM()
+ val result1 = KeyConvertCmd().test("--input=\"$inputFilePath\"")
+ assertContains(result1.stderr, "Something went wrong when converting the key")
+
+ val result2 = KeyConvertCmd().test("--input=\"$inputFilePath\" --verbose")
+ assertContains(result2.stderr, "Ed25519 keys cannot be exported as PEM yet")
+
+ deleteOutputFile(outputFilePath)
+ }
+
+ @Test
+ fun `should convert a PEM file to JWK`() {
+
+ // Stored in src/jvmTest/resources
+ val inputFileName = "rsa_by_openssl_pub_key.pem"
+ val outputFileName = "rsa_by_openssl_pub_key.jwk"
+
+ val inputFilePath = getFilePath(inputFileName)
+ val outputFilePath = getOutputFilePath(inputFilePath, outputFileName)
+
+ deleteOutputFile(outputFilePath)
+
+ val result = KeyConvertCmd().test("--input=\"$inputFilePath\"")
+ val expectedOutput = """.*Done. Converted "$inputFilePath" to "$outputFilePath".*""".toRegex()
+
+ // Assert successful logging message
+ assertContains(result.stdout, expectedOutput)
+
+ deleteOutputFile(outputFilePath)
+ }
+
+ @Test
+ fun `should convert RSA public key PEM file to a valid JWK`() {
+
+ // Stored in src/jvmTest/resources
+ val inputFileName = "rsa_by_openssl_pub_key.pem"
+ val outputFileName = "rsa_by_openssl_pub_key.jwk"
+
+ // Assert output file content
+ val expectedJWKFragments = listOf(
+ """"kty":"RSA"""",
+ """("kid":".*")*""", // Key id
+ """"n":".*"""", // Modulus parameter
+ """"e":"AQAB"""", // Exponent parameter
+ // "\"p\":\".*\"", // First prime factor
+ // "\"q\":\".*\"", // Second prime factor
+ // "\"d\":\".*\"", // Private expoent
+ // "\"qi\":\".*\"", // First CRT coefficient
+ // "\"dp\":\".*\"", // First factor CRT expoent
+ // "\"dq\":\".*\"", // Secobnd factor CRT expoent
+ )
+
+ testSuccessfulConvertion(inputFileName, outputFileName, expectedJWKFragments)
+ }
+
+ @Test
+ fun `should convert RSA private key PEM file to a valid JWK`() {
+
+ // Stored in src/jvmTest/resources
+ val inputFileName = "rsa_by_openssl_pvt_key.pem"
+ val outputFileName = "rsa_by_openssl_pvt_key.jwk"
+
+ // Assert output file content
+ val expectedJWKFragments = listOf(
+ """"kty":"RSA"""", // Key type
+ """("kid":".*")*""", // Key id
+ """"n":".*"""", // Modulus parameter
+ """"e":".*"""", // Exponent parameter
+ """"p":".*"""", // First prime factor
+ """"q":".*"""", // Second prime factor
+ """"d":".*"""", // Private expoent
+ """"qi":".*"""", // First CRT coefficient
+ """"dp":".*"""", // First factor CRT exponent
+ """"dq":".*"""", // Second factor CRT exponent
+ )
+
+ testSuccessfulConvertion(inputFileName, outputFileName, expectedJWKFragments)
+ }
+
+ @Test
+ @Ignore
+ fun `should ask for the passphrase if input PEM file is encrypted and no passphrase is provided`() = runTest {
+ val inputFileName = "rsa_encrypted_private_key.pem"
+ val outputFileName = "rsa_encrypted_private_key.jwk"
+
+ val inputFilePath = getFilePath(inputFileName)
+
+ class PassphraseTerminal : TerminalInterface {
+ override val info: TerminalInfo
+ get() = TerminalInfo(
+ width = 0,
+ height = 0,
+ ansiLevel = AnsiLevel.NONE,
+ ansiHyperLinks = false,
+ outputInteractive = false,
+ inputInteractive = false,
+ crClearsLine = false
+ )
+
+ override fun completePrintRequest(request: PrintRequest) {
+ StringWriter().write(request.text)
+ }
+
+ override fun readLineOrNull(hideInput: Boolean): String {
+ return "123123"
+ }
+
+ suspend fun answerPrompt(input: String) = Channel().send(input)
+
+ }
+
+ val result = KeyConvertCmd().context { terminal = Terminal(terminalInterface = PassphraseTerminal()) }
+ .test("--input=\"${inputFilePath}\"")
+
+ println(result)
+
+ //
+ // KeyConvertCmd().testkit("--input", inputFilePath) {
+ // expectOutput() // Reading key ...
+ // assertContains(expectOutput(), ".*Key encrypted. Please, inform the passphrase to decipher it.*".toRegex())
+ // provideInput("123123")
+ // // expectOutput() // Converting key
+ // // assertContains(expectOutput(), ".*Converted Key .JWK.*".toRegex())
+ // // ignoreOutputs()
+ // }
+ }
+
+ @Test
+ fun `should NOT ask for the passphrase if input PEM file is encrypted and --passphrase is provided`() {
+
+ // openssl genrsa -aes256 -passout pass:123123 -out rsa_by_openssl_encrypted_pvt_key.pem 2048
+ val inputFileName = "rsa_by_openssl_encrypted_pvt_key.pem"
+ val outputFileName = "rsa_by_openssl_encrypted_pvt_key.jwk"
+
+ val inputFilePath = getFilePath(inputFileName)
+ val outputFilePath = getOutputFilePath(inputFilePath, outputFileName)
+
+ val result = KeyConvertCmd().test("--input=\"$inputFilePath\" --passphrase=123123")
+
+ val expectedOutput = """.*Done. Converted "$inputFilePath" to "$outputFilePath".*""".toRegex()
+
+ // Assert successful logging message
+ assertContains(result.stdout, expectedOutput)
+
+ deleteOutputFile(outputFilePath)
+ }
+
+ @Test
+ fun `should convert RSA PEM public key extracted from an encrypted private key`() {
+
+ // openssl rsa -in rsa_by_openssl_encrypted_pvt_key.pem -passin pass:123123 -pubout -out rsa_by_openssl_encrypted_pub_key.pem
+ val inputFileName = "rsa_by_openssl_encrypted_pub_key.pem"
+ val outputFileName = "rsa_by_openssl_encrypted_pub_key.jwk"
+
+ val inputFilePath = getFilePath(inputFileName)
+ val outputFilePath = getOutputFilePath(inputFilePath, outputFileName)
+
+ val result = KeyConvertCmd().test("--input=\"$inputFilePath\"")
+
+ val expectedOutput = """.*Done. Converted "$inputFilePath" to "$outputFilePath".*""".toRegex()
+
+ // Assert successful logging message
+ assertContains(result.stdout, expectedOutput)
+
+ deleteOutputFile(outputFilePath)
+
+ }
+
+ @Test
+ fun `should convert encrypted RSA private key PEM file to a valid JWK`() {
+
+ // Stored in src/jvmTest/resources
+ val inputFileName = "rsa_by_openssl_encrypted_pvt_key.pem"
+ val outputFileName = "rsa_by_openssl_encrypted_pvt_key.jwk"
+
+ // Assert output file content
+ val expectedJWKFragments = listOf(
+ """"kty":"RSA"""", // Key type
+ """("kid":".*")*""", // Key id
+ """"n":".*"""", // Modulus parameter
+ """"e":".*"""", // Exponent parameter
+ """"p":".*"""", // First prime factor
+ """"q":".*"""", // Second prime factor
+ """"d":".*"""", // Private expoent
+ """"qi":".*"""", // First CRT coefficient
+ """"dp":".*"""", // First factor CRT exponent
+ """"dq":".*"""", // Second factor CRT exponent
+ )
+
+ val extraArgs = "--passphrase=123123"
+
+ testSuccessfulConvertion(inputFileName, outputFileName, expectedJWKFragments, extraArgs)
+ }
+
+ @Test
+ // @ValueSource(strings = {"ed25519_pub_key.pem", "ed25519_pvt_key.pem"}) --> JUnit dependency :-(
+ fun `should fail when trying to convert Ed25519 PEM file`() {
+
+ // ssh-keygen -t ed25519 -f ed25519_pvt_key_by_openssh.pem
+ testFailingConversion("ed25519_by_openssh_pvt_key.pem", "Invalid file format", false)
+ testFailingConversion("ed25519_by_openssh_pvt_key.pem", "unrecognised object: OPENSSH PRIVATE KEY", true)
+
+ // openssl genpkey -algorithm ed25519 -out ed25519_pvt_key_by_openssl.pem
+ testFailingConversion("ed25519_by_openssl_pvt_key.pem", "Invalid file format", false)
+ testFailingConversion("ed25519_by_openssl_pvt_key.pem", "Missing PEM-encoded public key to construct JWK", true)
+
+ // openssl pkey -pubout -in src/jvmTest/resources/ed25519_pvt_key_by_openssl.pem -out src/jvmTest/resources/ed25519_pub_key_by_openssl.pem
+ testFailingConversion("ed25519_by_openssl_pub_key.pem", "Invalid file format", false)
+ testFailingConversion("ed25519_by_openssl_pub_key.pem", "Unsupported algorithm of PEM-encoded key: EdDSA", true)
+ }
+
+ @Test
+ @Ignore
+ fun `should convert Ed25519 OpenSSL PEM file`() = Unit
+
+ @Test
+ @Ignore
+ fun `should convert Ed25519 OpenSSH PEM file`() = Unit
+
+ @Test
+ fun `should convert secp256k1 PEM file with public and private key inside to JWK`() {
+
+ // openssl ecparam -genkey -name secp256k1 -out src/jvmTest/resources/secp256k1_by_openssl_pub_pvt_key.pem
+ val inputFileName = "secp256k1_by_openssl_pub_pvt_key.pem"
+ val outputFileName = "secp256k1_by_openssl_pub_pvt_key.jwk"
+
+ // Assert output file content
+ val expectedJWKFragments = listOf(
+ """"kty":"EC"""",
+ """"crv":"secp256k1"""",
+ """"x":".*"""",
+ """"y":".*""""
+ )
+
+ testSuccessfulConvertion(inputFileName, outputFileName, expectedJWKFragments, "")
+ }
+
+ @Test
+ @Ignore
+ fun `should convert secp256k1 PEM file only with public key inside`() {
+
+ // Doesn't work yet because BouncyCastle doesn't supoprt PEM object "BEGIN EC PUBLIC KEY"
+
+ // openssl pkey -in secp256k1_by_openssl_pvt_key.pem -pubout -out secp256k1_by_openssl_pub_key.pem
+ val inputFileName = "secp256k1_by_openssl_pub_key.pem"
+ val outputFileName = "secp256k1_by_openssl_pub_key.jwk"
+
+ // Assert output file content
+ val expectedJWKFragments = listOf(
+ """"kty":"EC"""",
+ """"crv":"secp256k1"""",
+ """"x":".*"""",
+ """"y":".*""""
+ )
+
+ testSuccessfulConvertion(inputFileName, outputFileName, expectedJWKFragments, "")
+ }
+
+ @Test
+ fun `should fail when trying to convert secp256k1 PEM file only with private key inside`() {
+
+ // ./waltid-cli.sh key generate -tsecp256k1 --output=src/jvmTest/resources/secp256k1_by_waltid_pvt_key.jwk
+ // ./waltid-cli.sh key convert --input=src/jvmTest/resources/secp256k1_by_waltid_pvt_key.jwk
+ testFailingConversion(
+ "secp256k1_by_waltid_pvt_key.pem",
+ "incorrect format in file",
+ false,
+ )
+
+ testFailingConversion(
+ "secp256k1_by_waltid_pvt_key.pem",
+ """the return value of "org.bouncycastle.openssl.PEMKeyPair.getPublicKeyInfo()" is null""",
+ true,
+ )
+
+ // openssl storeutl -keys src/jvmTest/resources/secp256k1_by_openssl_pub_pvt_key.pem > src/jvmTest/resources/secp256k1_by_openssl_pvt_key.pem
+ testFailingConversion(
+ "secp256k1_by_openssl_pvt_key.pem",
+ "Invalid file format",
+ false,
+ )
+
+ testFailingConversion(
+ "secp256k1_by_openssl_pvt_key.pem",
+ "Missing PEM-encoded public key to construct JWK",
+ true,
+ )
+ }
+
+ @Test
+ @Ignore
+ fun `should convert Ed25519 public key PEM file to a valid JWK`() = Unit
+
+ @Test
+ @Ignore
+ fun `should convert Ed25519 private key PEM file to a valid JWK`() = Unit
+
+ @Test
+ @Ignore
+ fun `should convert Ed25519 pub & pvt key PEM file to a valid JWK`() = Unit
+
+ @Test
+ @Ignore
+ fun `should convert secp256k1 public key PEM file to a valid JWK`() = Unit
+
+ @Test
+ @Ignore
+ fun `should convert secp256k1 private key PEM file to a valid JWK`() = Unit
+
+ @Test
+ @Ignore
+ fun `should convert secp256k1 pub & pvt key PEM file to a valid JWK`() = Unit
+
+ @Test
+ @Ignore
+ fun `should convert secp256r1 public key PEM file to a valid JWK`() = Unit
+
+ @Test
+ @Ignore
+ fun `should convert secp256r1 private key PEM file to a valid JWK`() = Unit
+
+ @Test
+ @Ignore
+ fun `should convert secp256r1 pub & pvt key PEM file to a valid JWK`() = Unit
+
+ fun getFilePath(filename: String): String {
+ // The returned URL has white spaces replaced by %20.
+ // So, we need to decode it first to get rid of %20 from the file path
+ this.javaClass.getClassLoader().getResource(filename)?.let {
+ return URI(it.toString()).path
+ }
+
+ throw FileNotFoundException(filename)
+ }
+
+ fun getOutputFilePath(inputFilePath: String, outputFileName: String): String {
+ return "${inputFilePath.dropLastWhile { it != '/' }}$outputFileName"
+ }
+
+ fun deleteOutputFile(outputFilePath: String) {
+ // TODO: Need to check if exists?
+ File(outputFilePath).delete()
+ }
+
+ private fun testSuccessfulConvertion(
+ inputFileName: String, outputFileName: String, expectedFragments: List, extraArgs: String = ""
+ ) {
+ val inputFilePath = getFilePath(inputFileName)
+ val outputFilePath = getOutputFilePath(inputFilePath, outputFileName)
+
+ deleteOutputFile(outputFilePath)
+ val result = KeyConvertCmd().test("--input=\"$inputFilePath\" $extraArgs")
+
+ val expectedOutput = """.*Done. Converted "$inputFilePath" to "$outputFilePath".*""".toRegex()
+
+ // Assert successful logging message
+ assertContains(result.stdout, expectedOutput)
+
+ val convertedContent = File(outputFilePath).readText()
+
+ // Assert the converted file structure
+ expectedFragments.forEach {
+ assertContains(convertedContent, it.toRegex())
+ }
+
+ deleteOutputFile(outputFilePath)
+ }
+
+ fun testFailingConversion(inputFileName: String, expectedErrorMessage: String, verbose: Boolean = false) {
+
+ val inputFilePath = getFilePath(inputFileName)
+ var args = "--input=\"$inputFilePath\""
+
+ if (verbose) {
+ args = "$args --verbose"
+ }
+
+ val result = KeyConvertCmd().test(args)
+ assertContains(result.stderr, expectedErrorMessage)
+ }
+
+}
diff --git a/waltid-cli/src/jvmTest/kotlin/id/walt/cli/WaltIdKeyGenerateCmdTest.kt b/waltid-cli/src/jvmTest/kotlin/id/walt/cli/WaltIdKeyGenerateCmdTest.kt
new file mode 100644
index 000000000..3923ca7a2
--- /dev/null
+++ b/waltid-cli/src/jvmTest/kotlin/id/walt/cli/WaltIdKeyGenerateCmdTest.kt
@@ -0,0 +1,215 @@
+package id.walt.cli
+
+import com.github.ajalt.clikt.core.BadParameterValue
+import com.github.ajalt.clikt.core.IncorrectOptionValueCount
+import com.github.ajalt.clikt.core.PrintHelpMessage
+import com.github.ajalt.clikt.testing.CliktCommandTestResult
+import com.github.ajalt.clikt.testing.test
+import id.walt.cli.commands.KeyGenerateCmd
+import id.walt.crypto.keys.KeyType
+import kotlinx.coroutines.test.runTest
+import java.io.File
+import kotlin.test.Test
+import kotlin.test.assertContains
+import kotlin.test.assertFailsWith
+import kotlin.test.assertTrue
+
+class WaltIdKeyGenerateCmdTest {
+
+ private fun checkHelpMessage(command: KeyGenerateCmd, args: List) {
+ assertFailsWith {
+ command.parse(args)
+ }
+
+ val result = command.test(args)
+ val reHelpMsg = "Generates a new cryptographic key".toRegex()
+ val reOpt1 = "-t, --keyType".toRegex()
+
+ assertContains(result.stdout, reHelpMsg)
+ assertContains(result.stdout, reOpt1)
+ }
+
+ fun deleteGeneratedFile(output: String) {
+ val generatedFilePath = Regex(".*Key saved at file \"(.*?)\"").findAll(output).toList()[0].groupValues[1]
+ File(generatedFilePath).delete()
+ }
+
+ @Test
+ fun `should print a help message`() {
+ checkHelpMessage(KeyGenerateCmd(), listOf("--help"))
+ }
+
+ @Test
+ fun `should generate an Ed25519 key when no --keyType is provided`() = runTest {
+ val command = KeyGenerateCmd()
+ val result = command.test(emptyList())
+ val expected = ".*Generating key of type ${KeyType.Ed25519.name}.*".toRegex()
+ assertContains(result.output, expected)
+
+ deleteGeneratedFile(result.output)
+ }
+
+ @Test
+ fun `should fail if --keyType is provided with no value`() {
+ val command = KeyGenerateCmd()
+
+ assertFailsWith {
+ command.parse(listOf("-t"))
+ }
+
+ assertFailsWith {
+ command.parse(listOf("--keyType"))
+ }
+ }
+
+ @Test
+ fun `should fail if an invalid --keyType is provided`() {
+ val command = KeyGenerateCmd()
+
+ // val result = command.test(listOf("-t foo"))
+ // --- result.output
+ // Usage: generate []
+ //
+ // Error: invalid value for -t: invalid choice: foo. (choose from ED25519, SECP256K1, SECP256R1, RSA)
+
+ val failure = assertFailsWith {
+ command.parse(listOf("-t foo"))
+ }
+
+ val expected = "invalid choice".toRegex()
+ failure.message?.let { assertContains(it, expected) }
+ }
+
+ @Test
+ fun `should generate key of type Ed25519`() = runTest {
+ val command = KeyGenerateCmd()
+
+ val result = command.test("--keyType=Ed25519")
+ val expected1 = ".*Generated Key.*"
+ val expected2 = listOf(
+ """"kty": "OKP"""",
+ """"d": ".*?"""",
+ """"crv": "Ed25519"""",
+ """"kid": ".*?"""",
+ """"x": ".*?""""
+ )
+
+ assertExpectations(result, expected1, expected2)
+
+ deleteGeneratedFile(result.output)
+ }
+
+ @Test
+ fun `should generate key of type secp256k1`() = runTest {
+ val command = KeyGenerateCmd()
+
+ val result = command.test("--keyType=secp256k1")
+ val expected1 = ".*Done. Key saved at file.*"
+ val expected2 = listOf(
+ """"kty": "EC"""",
+ """"d": ".*?"""",
+ """"crv": "secp256k1"""",
+ """"kid": ".*?"""",
+ """"x": ".*?""""
+ )
+
+ assertExpectations(result, expected1, expected2)
+
+ deleteGeneratedFile(result.output)
+ }
+
+ @Test
+ fun `should ignore key type case`() = runTest {
+ val command = KeyGenerateCmd()
+
+ val expected1 = ".*Done. Key saved at file.*"
+ val expected2 = listOf(
+ """"kty": "EC"""",
+ """"d": ".*?"""",
+ """"crv": "secp256k1"""",
+ """"kid": ".*?"""",
+ """"x": ".*?"""",
+ """"y": ".*?""""
+ )
+
+
+ val result1 = command.test("--keyType=secp256k1")
+ assertExpectations(result1, expected1, expected2)
+
+ val result2 = command.test("--keyType=SECP256k1")
+ assertExpectations(result2, expected1, expected2)
+
+ val result3 = command.test("--keyType=SECP256K1")
+ assertExpectations(result3, expected1, expected2)
+
+ deleteGeneratedFile(result1.output)
+ deleteGeneratedFile(result2.output)
+ deleteGeneratedFile(result3.output)
+ }
+
+ @Test
+ fun `should save generated key in file`() {
+ val result = KeyGenerateCmd().test("--keyType=Ed25519")
+ val expectedAtStdOut = ".*Done. Key saved at file \"(.*)\"".toRegex()
+ assertContains(result.stdout, expectedAtStdOut)
+
+ val filePath = expectedAtStdOut.find(result.stdout)!!.groupValues[1]
+ assertTrue(File(filePath).exists())
+
+ deleteGeneratedFile(result.output)
+ }
+
+ @Test
+ fun `should generate file with a valid JWK`() {
+ val result = KeyGenerateCmd().test("--keyType=Ed25519")
+
+ val expectedAtStdOut = ".*Done. Key saved at file \"(.*)\"".toRegex()
+ assertContains(result.stdout, expectedAtStdOut)
+
+ val filePath = expectedAtStdOut.find(result.stdout)!!.groupValues[1]
+ val fileContent = File(filePath).readText()
+
+ val validJWKFragments = listOf(
+ """"kty": "OKP"""",
+ """"d": ".*?"""",
+ """"crv": "Ed25519"""",
+ """"kid": ".*?"""",
+ """"x": ".*?""""
+ )
+
+ validJWKFragments.forEach {
+ assertContains(fileContent, it.toRegex())
+ }
+
+ deleteGeneratedFile(result.output)
+ }
+
+ @Test
+ fun `should override default file name`() {
+ val outputFileName = "myKey.json"
+
+ File(outputFileName).delete()
+
+ val result = KeyGenerateCmd().test("--keyType=Ed25519 --output=${outputFileName}")
+ val expectedAtStdOut = ".*Done. Key saved at file \"(.*)\"".toRegex()
+
+ assertContains(result.stdout, expectedAtStdOut)
+
+ val filePath = expectedAtStdOut.find(result.stdout)!!.groupValues[1]
+ assertTrue(File(filePath).exists())
+
+ deleteGeneratedFile(result.output)
+ }
+
+ private fun assertExpectations(
+ result: CliktCommandTestResult,
+ expectedMessage: String,
+ expectedFileFormat: List
+ ) {
+ assertContains(result.stdout, expectedMessage.toRegex())
+
+ expectedFileFormat.forEach {
+ assertContains(result.stdout, it.toRegex())
+ }
+ }
+}
\ No newline at end of file
diff --git a/waltid-cli/src/jvmTest/resources/ed25519_by_openssh_pvt_key.pem b/waltid-cli/src/jvmTest/resources/ed25519_by_openssh_pvt_key.pem
new file mode 100644
index 000000000..eb1730b4e
--- /dev/null
+++ b/waltid-cli/src/jvmTest/resources/ed25519_by_openssh_pvt_key.pem
@@ -0,0 +1,8 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBhpyuc1QRr08fLYZfRR0p1mUf3aRrwagJ1g+r0X1CBbgAAAKi7DEYIuwxG
+CAAAAAtzc2gtZWQyNTUxOQAAACBhpyuc1QRr08fLYZfRR0p1mUf3aRrwagJ1g+r0X1CBbg
+AAAEBYxAb/cQS+CLRFSE8H0BJ252f/SG2EQapO+QcyeU4gKGGnK5zVBGvTx8thl9FHSnWZ
+R/dpGvBqAnWD6vRfUIFuAAAAH2FsZWdvbWVzQE1CUDE2QUxFUGVyc29uYWwubG9jYWwBAg
+MEBQY=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/waltid-cli/src/jvmTest/resources/ed25519_by_openssl_pub_key.pem b/waltid-cli/src/jvmTest/resources/ed25519_by_openssl_pub_key.pem
new file mode 100644
index 000000000..111aea8e9
--- /dev/null
+++ b/waltid-cli/src/jvmTest/resources/ed25519_by_openssl_pub_key.pem
@@ -0,0 +1,3 @@
+-----BEGIN PUBLIC KEY-----
+MCowBQYDK2VwAyEACm26Tn3wQ2qj78tXfZmVjXOWO/V0JYwnqMWJIrwlAd0=
+-----END PUBLIC KEY-----
diff --git a/waltid-cli/src/jvmTest/resources/ed25519_by_openssl_pvt_key.pem b/waltid-cli/src/jvmTest/resources/ed25519_by_openssl_pvt_key.pem
new file mode 100644
index 000000000..6799a84b0
--- /dev/null
+++ b/waltid-cli/src/jvmTest/resources/ed25519_by_openssl_pvt_key.pem
@@ -0,0 +1,3 @@
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VwBCIEIMoZ3BrOH9yneQl9cQdyNWl2s2Zm24W9W4+xXnDVWs73
+-----END PRIVATE KEY-----
diff --git a/waltid-cli/src/jvmTest/resources/ed25519_by_waltid_pvt_key.jwk b/waltid-cli/src/jvmTest/resources/ed25519_by_waltid_pvt_key.jwk
new file mode 100644
index 000000000..ff4351dee
--- /dev/null
+++ b/waltid-cli/src/jvmTest/resources/ed25519_by_waltid_pvt_key.jwk
@@ -0,0 +1,7 @@
+{
+ "kty": "OKP",
+ "d": "Uau3y-0bThfD5e2_sGPhFkosg97wQqW-bqyNhCadU5o",
+ "crv": "Ed25519",
+ "kid": "xBlnePRZ_vbsBJTCFdWE3FKzHJimxnw_7uwhUpqvNW8",
+ "x": "j277kMAZAA06U0E8dRmz3ypN2xFEQRxlxHbtxbcXziw"
+}
\ No newline at end of file
diff --git a/waltid-cli/src/jvmTest/resources/invalidKey.jwk b/waltid-cli/src/jvmTest/resources/invalidKey.jwk
new file mode 100644
index 000000000..55cfba5f9
--- /dev/null
+++ b/waltid-cli/src/jvmTest/resources/invalidKey.jwk
@@ -0,0 +1 @@
+{"xxkty":"OKP","xd":"yDANQWc2b22uuL9gBqgwVU96XJr9-l948fLLQzAJO0o","crv":"Ed25519","kid":"rtnKhFx38n9lUgTNJbaUBMBcjLN_PhHkx0gu6oMusJk","x":"Xa1CvkhiAVvTSlKgUeiU_nw6k2TdRsfm7uuO0JEBdpw"}
\ No newline at end of file
diff --git a/waltid-cli/src/jvmTest/resources/rsa_by_openssl_encrypted_pub_key.pem b/waltid-cli/src/jvmTest/resources/rsa_by_openssl_encrypted_pub_key.pem
new file mode 100644
index 000000000..4ff0ecb9f
--- /dev/null
+++ b/waltid-cli/src/jvmTest/resources/rsa_by_openssl_encrypted_pub_key.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArhO4+TKkvEIzLw6vx7Tl
+Knb2fmGvmvlDM1Nw5c3bYuhwbHJQMtqz0vjlLVIYg0nL/NoR1nQWvcBquZnrhXQ7
+HymwZcW5dbeORqqCMIW20GEZN3+3senJGDkl4wMMe/qBJMn6RutNKTTbycmvtXwp
+NGSK90TRbRihPE2ljCGs32YalaPfKelYvHJ91CuG9lceMt7B3KUQ5L6SGPY5kuJj
+WP47pP1cpafvdOEQd/VAai9s4zMvt91/cTZJoyJM/KeAvkRkyq8yBPQoa+2DMXmN
+0iUFkiwLEsFYF330CUplXvZJ/LJXgRzCfFmYyc4Y3BlAbGd9JnFk4SH8pVBK43+s
+nQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/waltid-cli/src/jvmTest/resources/rsa_by_openssl_encrypted_pvt_key.pem b/waltid-cli/src/jvmTest/resources/rsa_by_openssl_encrypted_pvt_key.pem
new file mode 100644
index 000000000..eeb1c880a
--- /dev/null
+++ b/waltid-cli/src/jvmTest/resources/rsa_by_openssl_encrypted_pvt_key.pem
@@ -0,0 +1,30 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFNTBfBgkqhkiG9w0BBQ0wUjAxBgkqhkiG9w0BBQwwJAQQ72u42p1klnLY7ktW
+4MQbLAICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEBsOy35OhQFY5ha2
+p9q19ekEggTQGKDe7phrUXKZii/66GrLuz1Gzu5ho2EO6Ow0SGXA+n3QyTWhSHI+
+nrHbbh4/qLEbCfvIrjEgmT86DPGtEDs4UUMK6DpBoIMtyg/bRlgzIrR7Nj2Q2R9c
+G6QWPNR6y/Up6P1lmggqfMmRcVTI1/WEpTBppbXJdk71lEPQZpEK7C3cl4Q/Fo7C
+e9FWdNHeks36BKen595QkbAlkyAvcE+uJVugld1sov80HPiQFjCxhBuQddGbBLq+
+VsTAh78+geYB12NqumsNvNK+ilfojqe8TyZJJqZ2qOiZXd+eHDoR34MNG3tQHkdB
+SNo6X/nUV/N1VnEkXDMLGV0t4FNStxpZEz5vgXvCefX4hvWkgj9OAxSkYeqDBiNx
+L/oVh4V1C8TH/uEnGnhFd2VsY1UfT+quL5eM6Ryz4tLJd4jW5eaytLXjiJuyMfkE
+KnYBtzgPLGSEqIEAxnMq6ZsFb1DT68rkmF2jU2/BHArq+4uPe16uJf+x6u0jKzqF
+/qmnBNKA3bwDq6Z7CbU/qOvR5QP0JPkLnrpACgtMqyd3452FmACZpst9KX1dZGHg
+Qv9u5ILFooti98ZzHYLP2Yf4GhcQYF5ztLVSd0BTaqOZRCsfP+oUqRps6tR2K7IX
+7dQYZzQorjZNdCDGXavq96QHq2xNytoqyIw3O8MrGsPvQmIYSYCn7ir7qNiVSPE6
+kUmA96sczHhS0c/VF+yRj7uFpwlf9uQunuW2VIvmyQsmiEH6uR3HsXQgqDG4zBjA
+DRkwTM0UZv+Wh2fbABcN62KEFACKkYayssYibo6/T5oZYqZcgdjYkPrBKI1xBVou
+AYWQRb2RjYexQQnZhlNHGJ/C9oknjEEb7nXRgQdUvvu9pVAubvGcIR/VfF5nUfS5
+ehlV5haCvPf7F1ncvB9rjLNLOQS+m3plAIVYratT1BDVMyRQiWuLC+paSADBdgLG
+AIYD7LZj6lV5CmIimr7TvyrEDfbd9KpAhgOwJ0faPHUcQwisCg86Mp9Q3pTiP+IM
+fhSwpBMM1lmoeiGRWHzj/ndO+ct0N1dsBinFGbqZVA6iee6TRMXdQVwWE7OZdRSk
+NojFcmLyet//dABYD4CXT9pZoydmWwbKB0ou9NvylvG/FxyMeAaMjchRwzwv2Wbi
+DpCTRRq8AdzblEhRhpbdPeSXCPt5StjSH6hezuWPbAGdyZC8IsLiXtW4OEO8f4W6
+SrD7CGeehp5q6XACe3hdsS9hnTeQLiIUBK9jrpfIzmvfiswnwCN2Yqsjwkazmf9t
+OHvccg24oGmFUkh1yGW+CVppMgSqZD/q1//3p+SuylTop5Bx4UMhKpF3/K2GBzak
+NqxRzMJFIOFPAS+7pKl3AMvSSdYvi3bXjq78822o4g3TelpbCh1qkCvACl3gMDfV
+Jfv7XYpfpKjsgOidSVKebHES3M9CB/JUoaOmbmt82lpiFeIJflo1VjYZYK77D2Dn
+Fbvh4dKlrjejOTXoVwQeChxkuBLjop8CWvFg42aMh8dzwU0OkDIgmhqtEp+3SPYt
+TQfjHwb6RXJnPk9jg/hfjxfFrel4xwbYddcQJ5QETZVHEZlnOA90Itm/WIXXZRzW
+/aeKlo1OmCz5AAo3/XgY88QlpVWMz/rqCc1r2GdvEHN8tmYOwna7ibk=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/waltid-cli/src/jvmTest/resources/rsa_by_openssl_pub_key.jwk b/waltid-cli/src/jvmTest/resources/rsa_by_openssl_pub_key.jwk
new file mode 100644
index 000000000..acbbd26e2
--- /dev/null
+++ b/waltid-cli/src/jvmTest/resources/rsa_by_openssl_pub_key.jwk
@@ -0,0 +1 @@
+{"kty":"RSA","e":"AQAB","n":"mxq0EXs-Y5i_by9CgzjO2HjRe5PTse_hMxf3dKyp4zPE87UnxpoCCanAnYvZj2KvNdl-IPpbIzFg8jsd5hopiO_BjXkTIVcp4JClB-gs94TCJxQaRBFEUn49Iffa7V1j5Vm2E6t6biG2tcgscDzvb_x32HKCGKC05MiXBTiBJ7s42Jrz9qCrRKJqf0QBp0-fHmhk5FsBZ1YfStyQY8xlxLyGolOU0REKnkaXGi2S0yHmUWMGzkPeByyLzDshcqgjw1Yv_ImftJoYe7zyp7cJeHkMRmu1ysCrP1TZv2s1tIbtYBGujFAVj1DDCuAO1ve-ISZwDBwJYuEGNKfH7kwqRThASn9Cy9o_Kuo7f1DrZKunJMpSOtfor1zumxezQoYyzVZ8jC22q5FJcoUH7zEIDQ_JmLOEw3uKyi9Qj3BgQDwkWm0oIJS1zg-q714PjTprGguHV1DThbzC46QomflZRowRACTTWcR3Lq2DguCZRfTZKWhitp4D8qlwTbEHgGuP"}
\ No newline at end of file
diff --git a/waltid-cli/src/jvmTest/resources/rsa_by_openssl_pub_key.pem b/waltid-cli/src/jvmTest/resources/rsa_by_openssl_pub_key.pem
new file mode 100644
index 000000000..73aea5fc2
--- /dev/null
+++ b/waltid-cli/src/jvmTest/resources/rsa_by_openssl_pub_key.pem
@@ -0,0 +1,11 @@
+-----BEGIN PUBLIC KEY-----
+MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAmxq0EXs+Y5i/by9CgzjO
+2HjRe5PTse/hMxf3dKyp4zPE87UnxpoCCanAnYvZj2KvNdl+IPpbIzFg8jsd5hop
+iO/BjXkTIVcp4JClB+gs94TCJxQaRBFEUn49Iffa7V1j5Vm2E6t6biG2tcgscDzv
+b/x32HKCGKC05MiXBTiBJ7s42Jrz9qCrRKJqf0QBp0+fHmhk5FsBZ1YfStyQY8xl
+xLyGolOU0REKnkaXGi2S0yHmUWMGzkPeByyLzDshcqgjw1Yv/ImftJoYe7zyp7cJ
+eHkMRmu1ysCrP1TZv2s1tIbtYBGujFAVj1DDCuAO1ve+ISZwDBwJYuEGNKfH7kwq
+RThASn9Cy9o/Kuo7f1DrZKunJMpSOtfor1zumxezQoYyzVZ8jC22q5FJcoUH7zEI
+DQ/JmLOEw3uKyi9Qj3BgQDwkWm0oIJS1zg+q714PjTprGguHV1DThbzC46QomflZ
+RowRACTTWcR3Lq2DguCZRfTZKWhitp4D8qlwTbEHgGuPAgMBAAE=
+-----END PUBLIC KEY-----
diff --git a/waltid-cli/src/jvmTest/resources/rsa_by_openssl_pvt_key.jwk b/waltid-cli/src/jvmTest/resources/rsa_by_openssl_pvt_key.jwk
new file mode 100644
index 000000000..79c8501bb
--- /dev/null
+++ b/waltid-cli/src/jvmTest/resources/rsa_by_openssl_pvt_key.jwk
@@ -0,0 +1 @@
+{"p":"x12f3gmshajc8bgOg6yuscy8TkvwDBO-rdK8qb_V8Fv21wXZxlr6jN9Qko5C6iLHLXl3MuWhtREHUTI9EZ6ZBXe2OmSHUouAaJp1F2m2wevWmYZl24Gc_4vls9jZlss_jggwFq4q2_bk145uBL9jE3iBuwvQmo1aZLI-uaxb8VT90nHSIG2_g171fzxrieJlzDFnpG3Ny9P7Wr5s0u31Te9d2os9-9S9CFB-s4ndJb1w_HJINKkbPDo4NZd1uId9","kty":"RSA","q":"xypHLxvz1CWIy5zOWbtrzg4xmOYfN5VY_I-1Yg5z_BPvEl3mDaeT_ZCGsx7uRlzvjTtgRqdbKAxpHr3Uph7L-jY3Zw45EHCF-wnZiy4nLLOAtHVSfp_XsyEEs7l4cJdRg3o72cH63ioVOjvxtlCqxpuz3p-WV81LCJRqO32OcbE_53NGtzlBEeA4558ak7v_7F0Spy-uXOe0Xp3GuPVpdA2STiIbEl_9giIm7R9kp8fJsFTFwVI_pJ92DystfiT7","d":"IHUpmoDqcOgto2RF7HTuYEGEQPKR-eoDe_A3ggyB7sBOJlvSC479_yytWKrD5-wUU1YEvXz2pno2WeqCGr191KCrpeHg3XjClDJgvrNY-aEoCgp8ZqMgY4z0WQ_nmgWRmpS39AzN7Y8Tj53oosI1rv8ryzlHPUUgJcJOTDLjYD-fKUDf7cv4mz_Lvn8qQ2t3lmLtPhZHSGsu2Dko5CGJaG61U_HeshN5X97QzxMbZyBwdLae3Xo5U9IAHUiAeuBxbBCBFcucy9DVwl_As5nqC0_3diD31sxZv3Esv1sGwlQDZVQHoTzkFOLGo_k9hhEXJ1p9Pao-yh1AUa7KLEebQ66rFqI-Bhj-08CvzQ7SoNH5wS9x5yL3SblB39A5JSmTNl2t7hFw6_1_NvRIr8P458Na2KNHrhROuy8ap7-3-YdGkHB4ll_cLRv_-4cpoJgLTjffi4PHs_g-HPp67ywtRSE4k652gqp9JPcGqMjA_ryI83bFaByB-e66oajVhtM9","e":"AQAB","qi":"CKy4nQIdf2aS-gAx-yrhSzd5Q7S7M7UO5036MvLkpCv_iha2hrmxFcnelOJO3HJ_6ljV79lTg-TJEq-sXsXp-bFeWMqj1TOgWXsTXaKPw4PUZ_fEx--CPC7XyNjnyz1Tao9RBtPM5c_38J6LZ_Z9Q4ELdFKcVmpEChN1fwJ2ht21ki2H6ihIQEGJNerxxijWXuKJlxascl05ZqqNkBXPsjs1VvWQhjoubr3UY0rGKf7tzLMi95-emIO3EuvuZwwJ","dp":"Z74RbaJNEzRe3K2xZ9WZBk6KgpfDbxVrONqbcB2yPyQr25Jg03YOQPYH4GuE6H92c_RsEaEqt6UH0Lm6y4tjB1RXECW1wT90b3pIiglpn5mQj00_fa0BvHzY5_BksbJL_SXHmFXDWbktNfoYyAGrlbs0jtfEEliR_CpAt6-4HGnktvihplxVtw_X4gDX2OVloY7n5sl4uKMzffHvgQdwicCQbyPb-kqmn0f71oNb_8KHo-X4KucAlCObkk-hY4el","dq":"OBWQvBRcAjabofLDLQOZJQpcLxlGWymkSGLTigxV3vtiDEMC4H97LiE_vTsNkCTllFjPELZZ9hogk_aS5kCv4gLYcR3RNe7p27p3Vzkk8PKPYMHU_DFY1WmL4GxvHQ2Pd725EuYMFfm1xpNQyq1Gme0Ipr074fe-lGjuzVfa_-sQ-sU8eaYWy8jfXWIxYr7DH7VJ5miH6kOZSDeX2UfKbVLRC02RYAhF5Bpn6cg4WQLqqcwFcB4QK_R3k2fuRKJ3","n":"mxq0EXs-Y5i_by9CgzjO2HjRe5PTse_hMxf3dKyp4zPE87UnxpoCCanAnYvZj2KvNdl-IPpbIzFg8jsd5hopiO_BjXkTIVcp4JClB-gs94TCJxQaRBFEUn49Iffa7V1j5Vm2E6t6biG2tcgscDzvb_x32HKCGKC05MiXBTiBJ7s42Jrz9qCrRKJqf0QBp0-fHmhk5FsBZ1YfStyQY8xlxLyGolOU0REKnkaXGi2S0yHmUWMGzkPeByyLzDshcqgjw1Yv_ImftJoYe7zyp7cJeHkMRmu1ysCrP1TZv2s1tIbtYBGujFAVj1DDCuAO1ve-ISZwDBwJYuEGNKfH7kwqRThASn9Cy9o_Kuo7f1DrZKunJMpSOtfor1zumxezQoYyzVZ8jC22q5FJcoUH7zEIDQ_JmLOEw3uKyi9Qj3BgQDwkWm0oIJS1zg-q714PjTprGguHV1DThbzC46QomflZRowRACTTWcR3Lq2DguCZRfTZKWhitp4D8qlwTbEHgGuP"}
\ No newline at end of file
diff --git a/waltid-cli/src/jvmTest/resources/rsa_by_openssl_pvt_key.pem b/waltid-cli/src/jvmTest/resources/rsa_by_openssl_pvt_key.pem
new file mode 100644
index 000000000..a5b90650d
--- /dev/null
+++ b/waltid-cli/src/jvmTest/resources/rsa_by_openssl_pvt_key.pem
@@ -0,0 +1,40 @@
+-----BEGIN PRIVATE KEY-----
+MIIG/AIBADANBgkqhkiG9w0BAQEFAASCBuYwggbiAgEAAoIBgQCbGrQRez5jmL9v
+L0KDOM7YeNF7k9Ox7+EzF/d0rKnjM8TztSfGmgIJqcCdi9mPYq812X4g+lsjMWDy
+Ox3mGimI78GNeRMhVyngkKUH6Cz3hMInFBpEEURSfj0h99rtXWPlWbYTq3puIba1
+yCxwPO9v/HfYcoIYoLTkyJcFOIEnuzjYmvP2oKtEomp/RAGnT58eaGTkWwFnVh9K
+3JBjzGXEvIaiU5TREQqeRpcaLZLTIeZRYwbOQ94HLIvMOyFyqCPDVi/8iZ+0mhh7
+vPKntwl4eQxGa7XKwKs/VNm/azW0hu1gEa6MUBWPUMMK4A7W974hJnAMHAli4QY0
+p8fuTCpFOEBKf0LL2j8q6jt/UOtkq6ckylI61+ivXO6bF7NChjLNVnyMLbarkUly
+hQfvMQgND8mYs4TDe4rKL1CPcGBAPCRabSgglLXOD6rvXg+NOmsaC4dXUNOFvMLj
+pCiZ+VlGjBEAJNNZxHcurYOC4JlF9NkpaGK2ngPyqXBNsQeAa48CAwEAAQKCAYAg
+dSmagOpw6C2jZEXsdO5gQYRA8pH56gN78DeCDIHuwE4mW9ILjv3/LK1YqsPn7BRT
+VgS9fPamejZZ6oIavX3UoKul4eDdeMKUMmC+s1j5oSgKCnxmoyBjjPRZD+eaBZGa
+lLf0DM3tjxOPneiiwjWu/yvLOUc9RSAlwk5MMuNgP58pQN/ty/ibP8u+fypDa3eW
+Yu0+FkdIay7YOSjkIYlobrVT8d6yE3lf3tDPExtnIHB0tp7dejlT0gAdSIB64HFs
+EIEVy5zL0NXCX8CzmeoLT/d2IPfWzFm/cSy/WwbCVANlVAehPOQU4saj+T2GERcn
+Wn09qj7KHUBRrsosR5tDrqsWoj4GGP7TwK/NDtKg0fnBL3HnIvdJuUHf0DklKZM2
+Xa3uEXDr/X829Eivw/jnw1rYo0euFE67Lxqnv7f5h0aQcHiWX9wtG//7hymgmAtO
+N9+Lg8ez+D4c+nrvLC1FITiTrnaCqn0k9waoyMD+vIjzdsVoHIH57rqhqNWG0z0C
+gcEAx12f3gmshajc8bgOg6yuscy8TkvwDBO+rdK8qb/V8Fv21wXZxlr6jN9Qko5C
+6iLHLXl3MuWhtREHUTI9EZ6ZBXe2OmSHUouAaJp1F2m2wevWmYZl24Gc/4vls9jZ
+lss/jggwFq4q2/bk145uBL9jE3iBuwvQmo1aZLI+uaxb8VT90nHSIG2/g171fzxr
+ieJlzDFnpG3Ny9P7Wr5s0u31Te9d2os9+9S9CFB+s4ndJb1w/HJINKkbPDo4NZd1
+uId9AoHBAMcqRy8b89QliMuczlm7a84OMZjmHzeVWPyPtWIOc/wT7xJd5g2nk/2Q
+hrMe7kZc7407YEanWygMaR691KYey/o2N2cOORBwhfsJ2YsuJyyzgLR1Un6f17Mh
+BLO5eHCXUYN6O9nB+t4qFTo78bZQqsabs96fllfNSwiUajt9jnGxP+dzRrc5QRHg
+OOefGpO7/+xdEqcvrlzntF6dxrj1aXQNkk4iGxJf/YIiJu0fZKfHybBUxcFSP6Sf
+dg8rLX4k+wKBwGe+EW2iTRM0XtytsWfVmQZOioKXw28Vazjam3Adsj8kK9uSYNN2
+DkD2B+BrhOh/dnP0bBGhKrelB9C5usuLYwdUVxAltcE/dG96SIoJaZ+ZkI9NP32t
+Abx82OfwZLGyS/0lx5hVw1m5LTX6GMgBq5W7NI7XxBJYkfwqQLevuBxp5Lb4oaZc
+VbcP1+IA19jlZaGO5+bJeLijM33x74EHcInAkG8j2/pKpp9H+9aDW//Ch6Pl+Crn
+AJQjm5JPoWOHpQKBwDgVkLwUXAI2m6Hywy0DmSUKXC8ZRlsppEhi04oMVd77YgxD
+AuB/ey4hP707DZAk5ZRYzxC2WfYaIJP2kuZAr+IC2HEd0TXu6du6d1c5JPDyj2DB
+1PwxWNVpi+Bsbx0Nj3e9uRLmDBX5tcaTUMqtRpntCKa9O+H3vpRo7s1X2v/rEPrF
+PHmmFsvI311iMWK+wx+1SeZoh+pDmUg3l9lHym1S0QtNkWAIReQaZ+nIOFkC6qnM
+BXAeECv0d5Nn7kSidwKBwAisuJ0CHX9mkvoAMfsq4Us3eUO0uzO1DudN+jLy5KQr
+/4oWtoa5sRXJ3pTiTtxyf+pY1e/ZU4PkyRKvrF7F6fmxXljKo9UzoFl7E12ij8OD
+1Gf3xMfvgjwu18jY58s9U2qPUQbTzOXP9/Cei2f2fUOBC3RSnFZqRAoTdX8Cdobd
+tZIth+ooSEBBiTXq8cYo1l7iiZcWrHJdOWaqjZAVz7I7NVb1kIY6Lm691GNKxin+
+7cyzIvefnpiDtxLr7mcMCQ==
+-----END PRIVATE KEY-----
diff --git a/waltid-cli/src/jvmTest/resources/secp256k1_by_openssl_pub_key.jwk b/waltid-cli/src/jvmTest/resources/secp256k1_by_openssl_pub_key.jwk
new file mode 100644
index 000000000..76654ac0b
--- /dev/null
+++ b/waltid-cli/src/jvmTest/resources/secp256k1_by_openssl_pub_key.jwk
@@ -0,0 +1 @@
+{"kty":"EC","crv":"secp256k1","x":"D7KA0wd4qOX37HvlneDt1XdfV8fVRG2xWQGCjsO8s0c","y":"8WdPZDROGzCkKA36UUp6ZQgSfd0pzx1BUiULuQnhTHs"}
\ No newline at end of file
diff --git a/waltid-cli/src/jvmTest/resources/secp256k1_by_openssl_pub_key.pem b/waltid-cli/src/jvmTest/resources/secp256k1_by_openssl_pub_key.pem
new file mode 100644
index 000000000..b19f8bb6b
--- /dev/null
+++ b/waltid-cli/src/jvmTest/resources/secp256k1_by_openssl_pub_key.pem
@@ -0,0 +1,4 @@
+-----BEGIN EC PUBLIC KEY-----
+MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAED7KA0wd4qOX37HvlneDt1XdfV8fVRG2x
+WQGCjsO8s0fxZ09kNE4bMKQoDfpRSnplCBJ93SnPHUFSJQu5CeFMew==
+-----END EC PUBLIC KEY-----
diff --git a/waltid-cli/src/jvmTest/resources/secp256k1_by_openssl_pub_pvt_key.jwk b/waltid-cli/src/jvmTest/resources/secp256k1_by_openssl_pub_pvt_key.jwk
new file mode 100644
index 000000000..79466e280
--- /dev/null
+++ b/waltid-cli/src/jvmTest/resources/secp256k1_by_openssl_pub_pvt_key.jwk
@@ -0,0 +1 @@
+{"kty":"EC","d":"kOSSmKKR1ztgHIMF6cbLDqPk4Cau6axPd86-SZeVFoY","crv":"secp256k1","x":"DbdohN6Kpm67iVqmvz8aR-tAtatk2SUjgB4tl3j3Nb4","y":"8i_kz3rn1GlAY0ZqAQGbbdbwYPH86Ytn9TGbZB4Vbpg"}
\ No newline at end of file
diff --git a/waltid-cli/src/jvmTest/resources/secp256k1_by_openssl_pub_pvt_key.pem b/waltid-cli/src/jvmTest/resources/secp256k1_by_openssl_pub_pvt_key.pem
new file mode 100644
index 000000000..87448ddcc
--- /dev/null
+++ b/waltid-cli/src/jvmTest/resources/secp256k1_by_openssl_pub_pvt_key.pem
@@ -0,0 +1,8 @@
+-----BEGIN EC PARAMETERS-----
+BgUrgQQACg==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MHQCAQEEIJDkkpiikdc7YByDBenGyw6j5OAmrumsT3fOvkmXlRaGoAcGBSuBBAAK
+oUQDQgAEDbdohN6Kpm67iVqmvz8aR+tAtatk2SUjgB4tl3j3Nb7yL+TPeufUaUBj
+RmoBAZtt1vBg8fzpi2f1MZtkHhVumA==
+-----END EC PRIVATE KEY-----
diff --git a/waltid-cli/src/jvmTest/resources/secp256k1_by_openssl_pvt_key.pem b/waltid-cli/src/jvmTest/resources/secp256k1_by_openssl_pvt_key.pem
new file mode 100644
index 000000000..6a24aeeab
--- /dev/null
+++ b/waltid-cli/src/jvmTest/resources/secp256k1_by_openssl_pvt_key.pem
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQghItf3kprlQm9bYmnDKch
+RxBRCWaQBhKi+b2sSjCxCKmhRANCAAQPsoDTB3io5ffse+Wd4O3Vd19Xx9VEbbFZ
+AYKOw7yzR/FnT2Q0ThswpCgN+lFKemUIEn3dKc8dQVIlC7kJ4Ux7
+-----END PRIVATE KEY-----
diff --git a/waltid-cli/src/jvmTest/resources/secp256k1_by_waltid_pvt_key.jwk b/waltid-cli/src/jvmTest/resources/secp256k1_by_waltid_pvt_key.jwk
new file mode 100644
index 000000000..d6b9b470b
--- /dev/null
+++ b/waltid-cli/src/jvmTest/resources/secp256k1_by_waltid_pvt_key.jwk
@@ -0,0 +1,8 @@
+{
+ "kty": "EC",
+ "d": "mRKHNNlqkRxLtTBd4zSDtuPjV36NY7Kg6yusAIQlH_g",
+ "crv": "secp256k1",
+ "kid": "xxWVWbA0sgAAyeq3YyrA_-DQYbKtzmZJKeDn_qRwZYE",
+ "x": "9uDtgwPgXmV3QdQYNwdurDMptuW9nicx3vpqRzo69Zc",
+ "y": "nZRz7ZyUxIMslHlyouR0IdZ1Z2Sl5-_Q_2xYA_XupIA"
+}
\ No newline at end of file
diff --git a/waltid-cli/src/jvmTest/resources/secp256k1_by_waltid_pvt_key.pem b/waltid-cli/src/jvmTest/resources/secp256k1_by_waltid_pvt_key.pem
new file mode 100644
index 000000000..94973fbb0
--- /dev/null
+++ b/waltid-cli/src/jvmTest/resources/secp256k1_by_waltid_pvt_key.pem
@@ -0,0 +1,4 @@
+-----BEGIN EC PRIVATE KEY-----
+MD4CAQAwEAYHKoZIzj0CAQYFK4EEAAoEJzAlAgEBBCCZEoc02WqRHEu1MF3jNIO2
+4+NXfo1jsqDrK6wAhCUf+A==
+-----END EC PRIVATE KEY-----
diff --git a/waltid-cli/waltid-cli-development.sh b/waltid-cli/waltid-cli-development.sh
new file mode 100755
index 000000000..0e67e4662
--- /dev/null
+++ b/waltid-cli/waltid-cli-development.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env sh
+
+# Launcher script which rebuilds the waltid-cli from source and executes the command supplied
+
+gradle --quiet installDist && build/install/waltid-cli/bin/waltid-cli "$@"
diff --git a/waltid-cli/waltid-cli.sh b/waltid-cli/waltid-cli.sh
new file mode 100755
index 000000000..9fcb2c4f7
--- /dev/null
+++ b/waltid-cli/waltid-cli.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env sh
+
+# Launcher script which builds the waltid-cli from source **once** and executes the command supplied
+# If changes are made to the source then waltid-cli has to manually be rebuild (e.g. through waltid-cli-development.sh)
+
+if [ -f 'build/install/waltid-cli/bin/waltid-cli' ]; then
+ build/install/waltid-cli/bin/waltid-cli "$@"
+else
+ echo "waltid-cli not yet build."
+
+ echo "Running build..."
+ ../gradlew installDist
+
+ echo "Trying to run CLI command after build..."
+ build/install/waltid-cli/bin/waltid-cli "$@"
+fi
diff --git a/waltid-crypto/build.gradle.kts b/waltid-crypto/build.gradle.kts
index 7a362d93a..98b2249d3 100644
--- a/waltid-crypto/build.gradle.kts
+++ b/waltid-crypto/build.gradle.kts
@@ -28,7 +28,6 @@ suspendTransform {
js {
}*/
- useJvmDefault()
useJsDefault()
}
@@ -101,8 +100,8 @@ kotlin {
//implementation("dev.whyoleg.cryptography:cryptography-jdk:0.1.0")
implementation("com.google.crypto.tink:tink:1.12.0") // for JOSE using Ed25519
- implementation("org.bouncycastle:bcprov-jdk18on:1.77") // for secp256k1 (which was removed with Java 17)
- implementation("org.bouncycastle:bcpkix-jdk18on:1.77") // PEM import
+ implementation("org.bouncycastle:bcprov-lts8on:2.73.4") // for secp256k1 (which was removed with Java 17)
+ implementation("org.bouncycastle:bcpkix-lts8on:2.73.4") // PEM import
// Ktor client
implementation("io.ktor:ktor-client-cio:2.3.8")
@@ -114,7 +113,7 @@ kotlin {
implementation("com.nimbusds:nimbus-jose-jwt:9.37.3")
// Multibase
- implementation("com.github.multiformats:java-multibase:v1.1.1")
+// implementation("com.github.multiformats:java-multibase:v1.1.1")
}
}
val jvmTest by getting {
@@ -132,7 +131,7 @@ kotlin {
implementation(npm("jose", "4.14.4"))
// Multibase
- implementation(npm("multiformats", "12.1.2"))
+ // implementation(npm("multiformats", "12.1.2"))
}
}
val jsTest by getting {
@@ -180,10 +179,3 @@ extensions.getByType().apply {
)
)
}
-
-
-tasks.withType {
- rejectVersionIf {
- listOf("-beta", "-alpha", "-rc").any { it in candidate.version.lowercase() } || candidate.version.takeLast(4).contains("RC")
- }
-}
diff --git a/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/Key.kt b/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/Key.kt
index 86deb9e44..77325d208 100644
--- a/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/Key.kt
+++ b/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/Key.kt
@@ -1,7 +1,10 @@
package id.walt.crypto.keys
+import id.walt.crypto.utils.JsonUtils.prettyJson
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
+import kotlinx.serialization.encodeToString
+import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonClassDiscriminator
import kotlinx.serialization.json.JsonObject
import love.forte.plugin.suspendtrans.annotation.JsPromise
@@ -42,6 +45,13 @@ abstract class Key {
@JsPromise
@JsExport.Ignore
abstract suspend fun exportJWK(): String
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
+ open suspend fun exportJWKPretty(): String = prettyJson.encodeToString(Json.parseToJsonElement(exportJWK()))
+
+
@JvmBlocking
@JvmAsync
@JsPromise
diff --git a/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/LocalKey.kt b/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/LocalKey.kt
index 8fe1264a1..fdf023e88 100644
--- a/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/LocalKey.kt
+++ b/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/keys/LocalKey.kt
@@ -8,6 +8,7 @@ expect class LocalKey(jwk: String?) : Key {
override suspend fun getThumbprint(): String
override suspend fun exportJWK(): String
+
override suspend fun exportJWKObject(): JsonObject
override suspend fun exportPEM(): String
diff --git a/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/utils/Base58Utils.kt b/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/utils/Base58Utils.kt
new file mode 100644
index 000000000..4056ddb19
--- /dev/null
+++ b/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/utils/Base58Utils.kt
@@ -0,0 +1,115 @@
+package id.walt.crypto.utils
+
+// https://github.com/komputing/KBase58/blob/master/kbase58/src/main/kotlin/org/komputing/kbase58/Base58.kt
+
+private const val ENCODED_ZERO = '1'
+
+private const val alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
+private val alphabetIndices by lazy {
+ IntArray(128) { alphabet.indexOf(it.toChar()) }
+}
+
+/**
+ * Encodes the bytes as a base58 string (no checksum is appended).
+ *
+ * @return the base58-encoded string
+ */
+fun ByteArray.encodeToBase58String(): String {
+
+ val input = copyOf(size) // since we modify it in-place
+ if (input.isEmpty()) {
+ return ""
+ }
+ // Count leading zeros.
+ var zeros = 0
+ while (zeros < input.size && input[zeros].toInt() == 0) {
+ ++zeros
+ }
+ // Convert base-256 digits to base-58 digits (plus conversion to ASCII characters)
+ val encoded = CharArray(input.size * 2) // upper bound
+ var outputStart = encoded.size
+ var inputStart = zeros
+ while (inputStart < input.size) {
+ encoded[--outputStart] = alphabet[divmod(input, inputStart.toUInt(), 256.toUInt(), 58.toUInt()).toInt()]
+ if (input[inputStart].toInt() == 0) {
+ ++inputStart // optimization - skip leading zeros
+ }
+ }
+ // Preserve exactly as many leading encoded zeros in output as there were leading zeros in data.
+ while (outputStart < encoded.size && encoded[outputStart] == ENCODED_ZERO) {
+ ++outputStart
+ }
+ while (--zeros >= 0) {
+ encoded[--outputStart] = ENCODED_ZERO
+ }
+ // Return encoded string (including encoded leading zeros).
+ return encoded.concatToString(outputStart, outputStart + (encoded.size - outputStart))
+}
+
+/**
+ * Decodes the base58 string into a [ByteArray]
+ *
+ * @return the decoded data bytes
+ * @throws NumberFormatException if the string is not a valid base58 string
+ */
+@Throws(NumberFormatException::class)
+fun String.decodeBase58(): ByteArray {
+ if (isEmpty()) {
+ return ByteArray(0)
+ }
+ // Convert the base58-encoded ASCII chars to a base58 byte sequence (base58 digits).
+ val input58 = ByteArray(length)
+ for (i in 0 until length) {
+ val c = this[i]
+ val digit = if (c.toInt() < 128) alphabetIndices[c.toInt()] else -1
+ if (digit < 0) {
+ throw NumberFormatException("Illegal character $c at position $i")
+ }
+ input58[i] = digit.toByte()
+ }
+ // Count leading zeros.
+ var zeros = 0
+ while (zeros < input58.size && input58[zeros].toInt() == 0) {
+ ++zeros
+ }
+ // Convert base-58 digits to base-256 digits.
+ val decoded = ByteArray(length)
+ var outputStart = decoded.size
+ var inputStart = zeros
+ while (inputStart < input58.size) {
+ decoded[--outputStart] = divmod(input58, inputStart.toUInt(), 58.toUInt(), 256.toUInt()).toByte()
+ if (input58[inputStart].toInt() == 0) {
+ ++inputStart // optimization - skip leading zeros
+ }
+ }
+ // Ignore extra leading zeroes that were added during the calculation.
+ while (outputStart < decoded.size && decoded[outputStart].toInt() == 0) {
+ ++outputStart
+ }
+ // Return decoded data (including original number of leading zeros).
+ return decoded.copyOfRange(outputStart - zeros, decoded.size)
+}
+
+/**
+ * Divides a number, represented as an array of bytes each containing a single digit
+ * in the specified base, by the given divisor. The given number is modified in-place
+ * to contain the quotient, and the return value is the remainder.
+ *
+ * @param number the number to divide
+ * @param firstDigit the index within the array of the first non-zero digit
+ * (this is used for optimization by skipping the leading zeros)
+ * @param base the base in which the number's digits are represented (up to 256)
+ * @param divisor the number to divide by (up to 256)
+ * @return the remainder of the division operation
+ */
+private fun divmod(number: ByteArray, firstDigit: UInt, base: UInt, divisor: UInt): UInt {
+ // this is just long division which accounts for the base of the input digits
+ var remainder = 0.toUInt()
+ for (i in firstDigit until number.size.toUInt()) {
+ val digit = number[i.toInt()].toUByte()
+ val temp = remainder * base + digit
+ number[i.toInt()] = (temp / divisor).toByte()
+ remainder = temp % divisor
+ }
+ return remainder
+}
diff --git a/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/utils/JsonUtils.kt b/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/utils/JsonUtils.kt
index a5584fa35..837c8e9b6 100644
--- a/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/utils/JsonUtils.kt
+++ b/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/utils/JsonUtils.kt
@@ -9,6 +9,8 @@ import kotlin.js.JsName
@JsExport
object JsonUtils {
+ val prettyJson by lazy { Json { prettyPrint = true } }
+
fun Any?.toJsonElement(): JsonElement =
when (this) {
is JsonElement -> this
diff --git a/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/utils/MultiBaseUtils.kt b/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/utils/MultiBaseUtils.kt
index e8f32343a..f74844ddf 100644
--- a/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/utils/MultiBaseUtils.kt
+++ b/waltid-crypto/src/commonMain/kotlin/id/walt/crypto/utils/MultiBaseUtils.kt
@@ -1,10 +1,48 @@
package id.walt.crypto.utils
-expect object MultiBaseUtils {
+object MultiBaseUtils {
- fun convertRawKeyToMultiBase58Btc(key: ByteArray, code: UInt): String
+ fun convertRawKeyToMultiBase58Btc(key: ByteArray, code: UInt): String {
+ val codeVarInt = MultiCodecUtils.UVarInt(code)
+ val multicodecAndRawKey = ByteArray(key.size + codeVarInt.length)
+ codeVarInt.bytes.copyInto(multicodecAndRawKey)
+ key.copyInto(multicodecAndRawKey, codeVarInt.length)
+ return encodeMultiBase58Btc(multicodecAndRawKey)
+ }
- fun convertMultiBase58BtcToRawKey(mb: String): ByteArray
+ fun convertMultiBase58BtcToRawKey(mb: String): ByteArray {
+ val bytes = decodeMultiBase58Btc(mb)
+ val code = MultiCodecUtils.UVarInt.fromBytes(bytes)
+ return bytes.drop(code.length).toByteArray()
+ }
- fun decodeMultiBase58Btc(mb: String): ByteArray
-}
\ No newline at end of file
+ fun decodeMultiBase58Btc(mb: String): ByteArray = mb.substring(1).decodeBase58()
+
+ fun encodeMultiBase58Btc(byteArray: ByteArray): String = 'z' + byteArray.encodeToBase58String()
+}
+
+/*
+For use with: implementation("com.github.multiformats:java-multibase:v1.1.1")
+
+actual object MultiBaseUtils {
+
+ actual fun convertRawKeyToMultiBase58Btc(key: ByteArray, code: UInt): String {
+ val codeVarInt = MultiCodecUtils.UVarInt(code)
+ val multicodecAndRawKey = ByteArray(key.size + codeVarInt.length)
+ codeVarInt.bytes.copyInto(multicodecAndRawKey)
+ key.copyInto(multicodecAndRawKey, codeVarInt.length)
+ return encodeMultiBase58Btc(multicodecAndRawKey)
+ }
+
+ actual fun convertMultiBase58BtcToRawKey(mb: String): ByteArray {
+ val bytes = decodeMultiBase58Btc(mb)
+ val code = MultiCodecUtils.UVarInt.fromBytes(bytes)
+ return bytes.drop(code.length).toByteArray()
+ }
+
+ actual fun decodeMultiBase58Btc(mb: String): ByteArray = Multibase.decode(mb)
+
+ private fun encodeMultiBase58Btc(byteArray: ByteArray): String =
+ Multibase.encode(Multibase.Base.Base58BTC, byteArray)
+}
+ */
diff --git a/waltid-crypto/src/jsMain/kotlin/crypto/crypto.kt b/waltid-crypto/src/jsMain/kotlin/crypto/crypto.kt
new file mode 100644
index 000000000..c651b9d01
--- /dev/null
+++ b/waltid-crypto/src/jsMain/kotlin/crypto/crypto.kt
@@ -0,0 +1,6 @@
+@JsModule("crypto")
+@JsNonModule
+external object crypto{
+ fun sign(algorithm: String?, data: ByteArray, key: String): ByteArray
+ fun verify(algorithm: String?, data: ByteArray, key: String, signature: ByteArray): Boolean
+}
\ No newline at end of file
diff --git a/waltid-crypto/src/jsMain/kotlin/id/walt/crypto/keys/LocalKey.js.kt b/waltid-crypto/src/jsMain/kotlin/id/walt/crypto/keys/LocalKey.js.kt
index 2947d000c..77d5db665 100644
--- a/waltid-crypto/src/jsMain/kotlin/id/walt/crypto/keys/LocalKey.js.kt
+++ b/waltid-crypto/src/jsMain/kotlin/id/walt/crypto/keys/LocalKey.js.kt
@@ -2,9 +2,11 @@ package id.walt.crypto.keys
import JWK
import KeyLike
+import crypto
import id.walt.crypto.utils.ArrayUtils.toByteArray
import id.walt.crypto.utils.JwsUtils.jwsAlg
import id.walt.crypto.utils.PromiseUtils.await
+import io.ktor.utils.io.core.*
import jose
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@@ -39,6 +41,7 @@ actual class LocalKey actual constructor(
_internalJwk = JSON.parse(jwk!!)
}
}
+
@JsPromise
@JsExport.Ignore
override suspend fun init() {
@@ -88,8 +91,11 @@ actual class LocalKey actual constructor(
@JsPromise
@JsExport.Ignore
- actual override suspend fun exportJWK(): String =
- JSON.stringify(_internalJwk)
+ actual override suspend fun exportJWK(): String = JSON.stringify(_internalJwk)
+
+ @JsPromise
+ @JsExport.Ignore
+ override suspend fun exportJWKPretty(): String = JSON.stringify(_internalJwk, null, 4)
@JsPromise
@JsExport.Ignore
@@ -109,7 +115,15 @@ actual class LocalKey actual constructor(
@JsPromise
@JsExport.Ignore
actual override suspend fun signRaw(plaintext: ByteArray): ByteArray {
- TODO("Not yet implemented")
+ check(hasPrivateKey) { "No private key is attached to this key!" }
+ return crypto.sign(
+ when (keyType) {
+ KeyType.Ed25519 -> null
+ else -> "sha256"
+ },
+ plaintext,
+ exportPEM()
+ )
}
/**
@@ -135,7 +149,22 @@ actual class LocalKey actual constructor(
@JsPromise
@JsExport.Ignore
actual override suspend fun verifyRaw(signed: ByteArray, detachedPlaintext: ByteArray?): Result {
- TODO("Not yet implemented")
+ return runCatching {
+ val verified = crypto.verify(
+ when (keyType) {
+ KeyType.Ed25519 -> null
+ else -> "sha256"
+ },
+ detachedPlaintext ?: signed,
+ getPublicKey().exportPEM(),
+ signed
+ )
+ if (verified) {
+ "true".toByteArray()
+ } else {
+ throw IllegalArgumentException("Signature verification failed")
+ }
+ }
}
/**
@@ -170,8 +199,7 @@ actual class LocalKey actual constructor(
@JsPromise
@JsExport.Ignore
actual override suspend fun getPublicKeyRepresentation(): ByteArray {
-
- TODO("Not yet implemented")
+ return getPublicKey().exportPEM().toByteArray()
}
override val keyType: KeyType
@@ -234,6 +262,7 @@ actual class LocalKey actual constructor(
@JsPromise
@JsExport.Ignore
actual override suspend fun importJWK(jwk: String): Result = JsLocalKeyCreator.importJWK(jwk)
+
@JsPromise
@JsExport.Ignore
actual override suspend fun importPEM(pem: String): Result = JsLocalKeyCreator.importPEM(pem)
diff --git a/waltid-crypto/src/jsMain/kotlin/id/walt/crypto/keys/LocalKeyGenerator.js.kt b/waltid-crypto/src/jsMain/kotlin/id/walt/crypto/keys/LocalKeyGenerator.js.kt
index 6ccaf9120..b0088aa7c 100644
--- a/waltid-crypto/src/jsMain/kotlin/id/walt/crypto/keys/LocalKeyGenerator.js.kt
+++ b/waltid-crypto/src/jsMain/kotlin/id/walt/crypto/keys/LocalKeyGenerator.js.kt
@@ -1,5 +1,6 @@
package id.walt.crypto.keys
+import JWK
import KeyLike
import id.walt.crypto.utils.JwsUtils.jwsAlg
import id.walt.crypto.utils.PromiseUtils.await
@@ -25,13 +26,20 @@ object JsLocalKeyCreator : LocalKeyCreator {
@JsPromise
@JsExport.Ignore
override suspend fun importRawPublicKey(type: KeyType, rawPublicKey: ByteArray, metadata: LocalKeyMetadata): Key {
- TODO("Not yet implemented")
+ val key: KeyLike = await(jose.importSPKI(rawPublicKey.decodeToString(), type.jwsAlg()))
+ return LocalKey(key).apply { init() }
}
@JsPromise
@JsExport.Ignore
override suspend fun importJWK(jwk: String): Result =
- runCatching { LocalKey(await(jose.importJWK(JSON.parse(jwk))), JSON.parse(jwk)).apply { init() } }
+ runCatching {
+ var jsonJWK = JSON.parse(jwk)
+ while (jsonJWK::class == String::class) {
+ jsonJWK = JSON.parse(jsonJWK as String)
+ }
+ LocalKey(await(jose.importJWK(jsonJWK)), jsonJWK).apply { init() }
+ }
/**
diff --git a/waltid-crypto/src/jsMain/kotlin/id/walt/crypto/utils/MultiBaseUtils.kt b/waltid-crypto/src/jsMain/kotlin/id/walt/crypto/utils/MultiBaseUtils.kt
deleted file mode 100644
index 810ea5c92..000000000
--- a/waltid-crypto/src/jsMain/kotlin/id/walt/crypto/utils/MultiBaseUtils.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package id.walt.crypto.utils
-
-@OptIn(ExperimentalJsExport::class)
-@JsExport
-actual object MultiBaseUtils {
- actual fun convertRawKeyToMultiBase58Btc(key: ByteArray, code: UInt): String = TODO("Not yet implemented")
-
- actual fun convertMultiBase58BtcToRawKey(mb: String): ByteArray = TODO("Not yet implemented")
-
- actual fun decodeMultiBase58Btc(mb: String): ByteArray = TODO("Not yet implemented")
-}
diff --git a/waltid-crypto/src/jsMain/kotlin/multibase/multibase.kt b/waltid-crypto/src/jsMain/kotlin/multibase/multibase.kt
new file mode 100644
index 000000000..257ab6da8
--- /dev/null
+++ b/waltid-crypto/src/jsMain/kotlin/multibase/multibase.kt
@@ -0,0 +1,8 @@
+import org.khronos.webgl.Uint8Array
+
+@JsModule("multibase")
+@JsNonModule
+external object multibase {
+ fun encode(base: String, data: ByteArray): ByteArray
+ fun decode(data: Uint8Array): ByteArray
+}
\ No newline at end of file
diff --git a/waltid-crypto/src/jsTest/kotlin/KeySignTests.kt b/waltid-crypto/src/jsTest/kotlin/KeySignTests.kt
new file mode 100644
index 000000000..aa07ca84c
--- /dev/null
+++ b/waltid-crypto/src/jsTest/kotlin/KeySignTests.kt
@@ -0,0 +1,34 @@
+import id.walt.crypto.keys.LocalKey
+import io.ktor.util.*
+import io.ktor.utils.io.core.*
+import kotlinx.coroutines.test.runTest
+import kotlin.test.Test
+
+class KeySignTests {
+ @Test
+ fun signAndVerifyRaw() = runTest {
+ val privateKeyJsonString = """
+ {
+ "kty": "RSA",
+ "kid": "288WlRQvku-zrHFmvcAW86jnTF3qsMoEUKEbteI2K4A",
+ "n": "qV-fGqTo8r6L52sIJpt44bxLkODaF0_wvIL_eYYDL55H-Ap-b1q4pd4YyZb7pARor_1mob6sMRnnAr5htmO1XucmKBEiNY-12zza0q9smjLm3-eNqq-8PgsEqBz4lU1YIBeQzsCR0NTa3J3OHfr-bADVystQeonSPoRLqSoO78oAtonQWLX1MUfS9778-ECcxlM21-JaUjqMD0nQR6wl8L6oWGcR7PjcjPQAyuS_ASTy7MO0SqunpkGzj_H7uFbK9Np_dLIOr9ZqrkCSdioA_PgDyk36E8ayuMnN1HDy4ak_Q7yEX4R_C75T0JxuuYio06hugwyREgOQNID-DVUoLw",
+ "e": "AQAB",
+ "d": "hndR21ddUYqRi9JfkDcSSzSwUX8R5jwjBaaCqLoKQX3J6VR7eHBv889VooXplheh_UaSeorkLb9Atd7ruF-EmKmuk1S28gr79-hiWa3H7MvIm647vGz0Z9VbhxQpDm9vLVtILbyYh1DVyRzHjOm9n4UyNmQfqolMjzF81_p6DUfpkMcDBJSlsTmRKMWPG0u8mFm8aB9ZftbryPO36QOny7g4_M8SZG1yxGTbypjyDTP9WqMmHpaC-66gLszjyxwEbVh69m-HsDEs7Qg9oMVG2FiwtDSXIApwfLk2v2Yk4-TeAD49rZzw-QcJ0yqPeVdq8cgyFOhwX-cPtQIm8X7AgQ",
+ "p": "0bNpJzzOOgzpqaWkb-5PuUgY9AedUsnze24AtukXaN9VY7e5BLYcbE11RGeyj8kkhpotvZQ6WrYEfvSkfxBvoVc1q86FXiqlpwmUL-_jO4BbgESOK9eaWP1iWmWNrZpqwdnIeF3VZHfCIoFxRV_Tb_Sp8UNSueFgCH6IVJlfwSE",
+ "q": "zsTarRYo9lLE8XvzpGzpjtrOHsnLuk2n5GXP6M2X89BL8yc8_5Fp99m_Em9vGAOhZBK9ActZuZEGSVVhfV1ImGw17tLyQZSCAvSzQpZSYpT9EDeZgn_oSorfUgMKppm1X4rl5Yz7lMR1khljdKt_X6gFA6ADL2h_ARK1bBRjr08",
+ "dp": "lOfqTmN-KXiL39xwdM7rq6zHk1lo3KXtEIOfXEMOTXjxQJrwdaj_a-Rg1g8wm6uAFVicDFeaTFmdvazothWsvwuXYAWJbMGp2YASyytz1wehcea8ceNqhbB_y6L7RQA2uKp2EQrIgcwMfcYe8d1G3eQFXP2qW7XvJHj9Q92ZQiE",
+ "dq": "E7TDOpfQE5nT10f-8n7Gy6yi1GBbIEhiZewmIoPlpYEGnAfzUlAjj1GbWkBwkBNYgFcg2FjvFjZyKO8QOYh4cL5vbXGBUSq8MVfs9b2p4Gdervr9kGhsVR5jJkfP7gzcMlzkiDoliAopQmFVDzuBCjbTM4M-inglEo8b508SKRU",
+ "qi": "aJsDBhxQFDbpQr20TjgxImwBslVP9xIauy3ncCmjHix6Fc1l51gL71V1OWGnXaStGfoWy0gKkUnJuU3_X_xA_QwzAXPJYa-juRlD8BxTf7rmR_HC-XiVdyNnkU3afHtK4nShS2EuN2EXOrYDrbQoA13_a6Itk_55vDpJ3jciwS8"
+ }
+ """.trimIndent()
+ val privateKeyResult = LocalKey.importJWK(privateKeyJsonString)
+ val privateKey = privateKeyResult.getOrThrow()
+
+ val res = privateKey.signRaw("Hello world!".toByteArray())
+ println((res.encodeBase64()))
+
+ val res2 = privateKey.verifyRaw(res, "Hello world!".toByteArray())
+ val res2Result = res2.getOrThrow()
+ println(res2Result.decodeToString())
+ }
+}
\ No newline at end of file
diff --git a/waltid-crypto/src/jvmMain/kotlin/id/walt/crypto/keys/LocalKey.jvm.kt b/waltid-crypto/src/jvmMain/kotlin/id/walt/crypto/keys/LocalKey.jvm.kt
index ac082d08f..a3d8c9446 100644
--- a/waltid-crypto/src/jvmMain/kotlin/id/walt/crypto/keys/LocalKey.jvm.kt
+++ b/waltid-crypto/src/jvmMain/kotlin/id/walt/crypto/keys/LocalKey.jvm.kt
@@ -18,12 +18,18 @@ import org.bouncycastle.asn1.edec.EdECObjectIdentifiers
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo
import org.bouncycastle.asn1.x509.AlgorithmIdentifier
import org.bouncycastle.jce.provider.BouncyCastleProvider
+import org.bouncycastle.util.io.pem.PemObject
+import org.bouncycastle.util.io.pem.PemWriter
+import java.io.ByteArrayOutputStream
import java.security.*
import java.security.spec.PKCS8EncodedKeySpec
import java.util.*
+import kotlin.js.ExperimentalJsExport
private val bouncyCastleProvider = BouncyCastleProvider()
+
+@Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
@Serializable
@SerialName("local")
actual class LocalKey actual constructor(
@@ -57,11 +63,44 @@ actual class LocalKey actual constructor(
}
actual override suspend fun exportJWK(): String = _internalJwk.toJSONString()
+
actual override suspend fun exportJWKObject(): JsonObject =
JsonObject(_internalJwk.toJSONObject().mapValues { JsonPrimitive(it.value as String) })
actual override suspend fun exportPEM(): String {
- TODO("Not yet implemented")
+ val pemObjects = ArrayList()
+
+ when (keyType) {
+ KeyType.secp256r1, KeyType.secp256k1 -> _internalJwk.toECKey().let {
+ if (hasPrivateKey) {
+ pemObjects.add(PemObject("PRIVATE KEY", it.toECPrivateKey().encoded))
+ pemObjects.add(PemObject("PUBLIC KEY", getPublicKey()._internalJwk.toECKey().toECPublicKey().encoded))
+ } else {
+ pemObjects.add(PemObject("PUBLIC KEY", it.toECPublicKey().encoded))
+ }
+ }
+
+ KeyType.Ed25519 -> throw NotImplementedError("Ed25519 keys cannot be exported as PEM yet.")
+
+ KeyType.RSA -> _internalJwk.toRSAKey().let {
+ if (hasPrivateKey) {
+ pemObjects.add(PemObject("RSA PRIVATE KEY", it.toRSAPrivateKey().encoded))
+ pemObjects.add(PemObject("RSA PUBLIC KEY", getPublicKey()._internalJwk.toRSAKey().toRSAPublicKey().encoded))
+ } else {
+ pemObjects.add(PemObject("RSA PUBLIC KEY", it.toRSAPublicKey().encoded))
+ }
+ }
+ }
+
+ val pem = ByteArrayOutputStream().apply {
+ PemWriter(writer()).use {
+ pemObjects.forEach { pemObject ->
+ it.writeObject(pemObject)
+ }
+ }
+ }.toByteArray().toString(Charsets.UTF_8)
+
+ return pem
}
private val _internalSigner: JWSSigner by lazy {
@@ -220,6 +259,9 @@ actual class LocalKey actual constructor(
}
actual companion object : LocalKeyCreator {
+
+ val prettyJson = Json { prettyPrint = true }
+
actual override suspend fun generate(type: KeyType, metadata: LocalKeyMetadata): LocalKey =
JvmLocalKeyCreator.generate(type, metadata)
diff --git a/waltid-crypto/src/jvmMain/kotlin/id/walt/crypto/utils/MultiBaseUtils.kt b/waltid-crypto/src/jvmMain/kotlin/id/walt/crypto/utils/MultiBaseUtils.kt
deleted file mode 100644
index 6d349b0aa..000000000
--- a/waltid-crypto/src/jvmMain/kotlin/id/walt/crypto/utils/MultiBaseUtils.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package id.walt.crypto.utils
-
-import io.ipfs.multibase.Multibase
-
-actual object MultiBaseUtils {
-
- actual fun convertRawKeyToMultiBase58Btc(key: ByteArray, code: UInt): String {
- val codeVarInt = MultiCodecUtils.UVarInt(code)
- val multicodecAndRawKey = ByteArray(key.size + codeVarInt.length)
- codeVarInt.bytes.copyInto(multicodecAndRawKey)
- key.copyInto(multicodecAndRawKey, codeVarInt.length)
- return encodeMultiBase58Btc(multicodecAndRawKey)
- }
-
- actual fun convertMultiBase58BtcToRawKey(mb: String): ByteArray {
- val bytes = decodeMultiBase58Btc(mb)
- val code = MultiCodecUtils.UVarInt.fromBytes(bytes)
- return bytes.drop(code.length).toByteArray()
- }
-
- actual fun decodeMultiBase58Btc(mb: String): ByteArray = Multibase.decode(mb)
-
- private fun encodeMultiBase58Btc(byteArray: ByteArray): String =
- Multibase.encode(Multibase.Base.Base58BTC, byteArray)
-}
\ No newline at end of file
diff --git a/waltid-crypto/src/jvmTest/kotlin/TestUtils.kt b/waltid-crypto/src/jvmTest/kotlin/TestUtils.kt
index afbb2a72a..5fd2e6f2b 100644
--- a/waltid-crypto/src/jvmTest/kotlin/TestUtils.kt
+++ b/waltid-crypto/src/jvmTest/kotlin/TestUtils.kt
@@ -1,4 +1,5 @@
import java.io.File
+import java.net.URLDecoder
object TestUtils {
fun loadJwkLocal(filename: String): String = loadResource("jwk/$filename")
@@ -6,7 +7,9 @@ object TestUtils {
fun loadSerializedLocal(filename: String): String = loadResource("serialized/local/$filename")
fun loadSerializedTse(filename: String): String = loadResource("serialized/tse/$filename")
fun loadResource(relativePath: String): String =
- this::class.java.classLoader.getResource(relativePath)!!.path.let { File(it).readText() }
+ URLDecoder.decode(this::class.java.classLoader.getResource(relativePath)!!.path, "UTF-8")
+ .let { File(it).readText() }
fun loadResourceBytes(relativePath: String): ByteArray =
- this::class.java.classLoader.getResource(relativePath)!!.path.let { File(it).readBytes() }
+ URLDecoder.decode(this::class.java.classLoader.getResource(relativePath)!!.path, "UTF-8")
+ .let { File(it).readBytes() }
}
\ No newline at end of file
diff --git a/waltid-did/README.md b/waltid-did/README.md
index a4e6b63ae..831089a2b 100644
--- a/waltid-did/README.md
+++ b/waltid-did/README.md
@@ -79,7 +79,7 @@ val key = LocalKey.generate(KeyType.Ed25519)
val options = DidKeyCreateOptions(
useJwkJcsPub = true
)
-val didResult = DidService.register(
+val didResult = DidService.registerByKey(
method = "key",
key = key,
options = options
diff --git a/waltid-did/build.gradle.kts b/waltid-did/build.gradle.kts
index 4734e0b7f..b4c21f922 100644
--- a/waltid-did/build.gradle.kts
+++ b/waltid-did/build.gradle.kts
@@ -1,10 +1,16 @@
import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
+import love.forte.plugin.suspendtrans.ClassInfo
+import love.forte.plugin.suspendtrans.SuspendTransformConfiguration
+import love.forte.plugin.suspendtrans.TargetPlatform
+import love.forte.plugin.suspendtrans.gradle.SuspendTransformGradleExtension
+
plugins {
kotlin("multiplatform")
kotlin("plugin.serialization")
id("maven-publish")
id("com.github.ben-manes.versions")
+ id("love.forte.plugin.suspend-transform") version "0.6.0"
}
group = "id.walt.did"
@@ -19,6 +25,18 @@ java {
targetCompatibility = JavaVersion.VERSION_15
}
+suspendTransform {
+ enabled = true
+ includeRuntime = true
+ /*jvm {
+
+ }
+ js {
+
+ }*/
+ useJsDefault()
+}
+
kotlin {
jvmToolchain(15)
@@ -72,6 +90,9 @@ kotlin {
// Crypto
api(project(":waltid-crypto"))
+ // Encodings
+ implementation("net.thauvin.erik.urlencoder:urlencoder-lib:1.4.0")
+
// Logging
implementation("io.github.oshai:kotlin-logging:6.0.3")
@@ -95,7 +116,7 @@ kotlin {
implementation("io.github.erdtman:java-json-canonicalization:1.1")
// Multiformat
- implementation("com.github.multiformats:java-multibase:v1.1.1")
+// implementation("com.github.multiformats:java-multibase:v1.1.1")
}
}
val jvmTest by getting {
@@ -145,10 +166,12 @@ kotlin {
}
}
-
-
-tasks.withType {
- rejectVersionIf {
- listOf("-beta", "-alpha", "-rc").any { it in candidate.version.lowercase() } || candidate.version.takeLast(4).contains("RC")
- }
+extensions.getByType().apply {
+ transformers[TargetPlatform.JS] = mutableListOf(
+ SuspendTransformConfiguration.jsPromiseTransformer.copy(
+ copyAnnotationExcludes = listOf(
+ ClassInfo("kotlin.js", "JsExport.Ignore")
+ )
+ )
+ )
}
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/DidManager.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/DidManager.kt
index 392d12e92..8d828b6f2 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/DidManager.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/DidManager.kt
@@ -1,5 +1,10 @@
package id.walt.did.dids
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+
+@ExperimentalJsExport
+@JsExport
interface DidManager {
fun resolve(did: String)
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/DidService.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/DidService.kt
index 559ee489a..622d38632 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/DidService.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/DidService.kt
@@ -12,8 +12,15 @@ import id.walt.did.dids.resolver.DidResolverRegistrations
import id.walt.did.dids.resolver.LocalResolver
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.serialization.json.JsonObject
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
@Suppress("OPT_IN_USAGE")
+@ExperimentalJsExport
+@JsExport
object DidService {
private val log = KotlinLogging.logger {}
@@ -47,6 +54,10 @@ object DidService {
}
}
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun init() {
registerAllResolvers(DidResolverRegistrations.didResolvers)
registerAllRegistrars(DidRegistrarRegistrations.didRegistrars)
@@ -58,6 +69,10 @@ object DidService {
log.debug { "INIT -> REGISTRARS: $registrarMethods" }
}
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun minimalInit() {
registerAllResolvers(setOf(LocalResolver()))
registerAllRegistrars(setOf(LocalRegistrar()))
@@ -70,6 +85,10 @@ object DidService {
fun registerResolverForMethod(method: String, resolver: DidResolver) = resolverMethods.put(method, resolver)
fun registerRegistrarForMethod(method: String, registrar: DidRegistrar) = registrarMethods.put(method, registrar)
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun updateResolversForMethods() {
didResolvers.forEach { resolver ->
val methods = resolver.getSupportedMethods()
@@ -83,6 +102,10 @@ object DidService {
}
}
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun updateRegistrarsForMethods() {
didRegistrars.forEach { registrar ->
val methods = registrar.getSupportedMethods()
@@ -107,18 +130,34 @@ object DidService {
}
/* - Did methods - */
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun resolve(did: String): Result =
getResolverForDid(did).resolve(did)
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun resolveToKey(did: String): Result =
getResolverForDid(did).resolveToKey(did)
private fun getRegistrarForMethod(method: String): DidRegistrar =
registrarMethods[method] ?: throw IllegalArgumentException("No registrar for did method: $method")
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun register(options: DidCreateOptions) =
getRegistrarForMethod(options.method).create(options)
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun registerByKey(
method: String, key: Key, options: DidCreateOptions = DidCreateOptions(method, emptyMap())
): DidResult = getRegistrarForMethod(method).createByKey(key, options)
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/DidUtils.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/DidUtils.kt
index 38bd4d451..f61190de4 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/DidUtils.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/DidUtils.kt
@@ -1,5 +1,10 @@
package id.walt.did.dids
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+
+@ExperimentalJsExport
+@JsExport
object DidUtils {
private const val PATTERN = "^did:([a-z]+):(.+)"
fun methodFromDid(did: String) = did.removePrefix("did:").substringBefore(":")
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidCheqdDocument.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidCheqdDocument.kt
index 7ab5b96ce..ca018dd71 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidCheqdDocument.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidCheqdDocument.kt
@@ -9,7 +9,12 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.jsonObject
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+import kotlin.js.JsName
+@ExperimentalJsExport
+@JsExport
@OptIn(ExperimentalSerializationApi::class)
@Serializable
data class DidCheqdDocument(
@@ -38,6 +43,7 @@ data class DidCheqdDocument(
fun toMap() = Json.encodeToJsonElement(this).jsonObject.toMap()
+ @JsName("secondaryConstructor")
constructor(didDoc: DidDocument, jwk: JsonObject? = null) : this(
context = DEFAULT_CONTEXT,
id = didDoc.id,
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidDocument.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidDocument.kt
index 7887c51a0..763821db3 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidDocument.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidDocument.kt
@@ -4,7 +4,12 @@ import id.walt.crypto.utils.JsonUtils.printAsJson
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+import kotlin.js.JsName
+@ExperimentalJsExport
+@JsExport
@Serializable
class DidDocument(
private val content: Map
@@ -16,6 +21,7 @@ class DidDocument(
/**
* From JsonObject
*/
+ @JsName("secondaryConstructor")
constructor(jsonObject: JsonObject) : this(jsonObject.toMap())
/**
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidJwkDocument.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidJwkDocument.kt
index 5f24e32fc..85d374e1b 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidJwkDocument.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidJwkDocument.kt
@@ -9,6 +9,9 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.jsonObject
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+import kotlin.js.JsName
/*
* if JWK has `use`="sig", no `keyAgreement` is included
@@ -20,6 +23,8 @@ import kotlinx.serialization.json.jsonObject
* update & deactivate not supported
*/
+@ExperimentalJsExport
+@JsExport
@OptIn(ExperimentalSerializationApi::class)
@Serializable
data class DidJwkDocument(
@@ -48,6 +53,7 @@ data class DidJwkDocument(
fun toMap() = Json.encodeToJsonElement(this).jsonObject.toMap()
+ @JsName("secondaryConstructor")
constructor(did: String, didJwk: JsonObject) : this(
context = DEFAULT_CONTEXT,
id = did,
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidKeyDocument.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidKeyDocument.kt
index e7db25666..c38fe312a 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidKeyDocument.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidKeyDocument.kt
@@ -8,7 +8,12 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.jsonObject
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+import kotlin.js.JsName
+@ExperimentalJsExport
+@JsExport
@OptIn(ExperimentalSerializationApi::class)
@Serializable
data class DidKeyDocument(
@@ -37,10 +42,11 @@ data class DidKeyDocument(
fun toMap() = Json.encodeToJsonElement(this).jsonObject.toMap()
+ @JsName("secondaryConstructor")
constructor(did: String, identifier: String, didKey: JsonObject) : this(
context = DEFAULT_CONTEXT,
id = did,
- verificationMethod = listOf(VerificationMethod(did, "JsonWebKey2020", did, didKey)),
+ verificationMethod = listOf(VerificationMethod("$did#$identifier", "JsonWebKey2020", did, didKey)),
assertionMethod = listOf("$did#$identifier"),
authentication = listOf("$did#$identifier"),
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidWebDocument.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidWebDocument.kt
index d596a9997..b1894d550 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidWebDocument.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/document/DidWebDocument.kt
@@ -8,7 +8,12 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.jsonObject
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+import kotlin.js.JsName
+@ExperimentalJsExport
+@JsExport
@OptIn(ExperimentalSerializationApi::class)
@Serializable
data class DidWebDocument(
@@ -37,10 +42,11 @@ data class DidWebDocument(
fun toMap() = Json.encodeToJsonElement(this).jsonObject.toMap()
+ @JsName("secondaryConstructor")
constructor(did: String, keyId: String, didKey: JsonObject) : this(
context = DEFAULT_CONTEXT,
id = did,
- verificationMethod = listOf(VerificationMethod(did, "JsonWebKey2020", did, didKey)),
+ verificationMethod = listOf(VerificationMethod("$did#$keyId", "JsonWebKey2020", did, didKey)),
assertionMethod = listOf("$did#$keyId"),
authentication = listOf("$did#$keyId"),
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/DidRegistrar.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/DidRegistrar.kt
index abbe21279..ce957f2e6 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/DidRegistrar.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/DidRegistrar.kt
@@ -2,15 +2,42 @@ package id.walt.did.dids.registrar
import id.walt.crypto.keys.Key
import id.walt.did.dids.registrar.dids.DidCreateOptions
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
interface DidRegistrar {
val name: String
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun getSupportedMethods(): Result>
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun create(options: DidCreateOptions): DidResult
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun createByKey(key: Key, options: DidCreateOptions): DidResult
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun update()
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun delete()
}
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/DidRegistrarRegistrations.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/DidRegistrarRegistrations.kt
index d16e471c9..c8563343d 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/DidRegistrarRegistrations.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/DidRegistrarRegistrations.kt
@@ -1,5 +1,10 @@
package id.walt.did.dids.registrar
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+
+@ExperimentalJsExport
+@JsExport
object DidRegistrarRegistrations {
val didRegistrars = setOf(
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/DidResult.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/DidResult.kt
index 92d097d93..3b332ac44 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/DidResult.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/DidResult.kt
@@ -2,7 +2,11 @@ package id.walt.did.dids.registrar
import id.walt.did.dids.document.DidDocument
import kotlinx.serialization.Serializable
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
@Serializable
data class DidResult(
val did: String,
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/LocalRegistrar.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/LocalRegistrar.kt
index dbd2248c6..eadb5a15d 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/LocalRegistrar.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/LocalRegistrar.kt
@@ -7,7 +7,14 @@ import id.walt.did.dids.registrar.local.cheqd.DidCheqdRegistrar
import id.walt.did.dids.registrar.local.jwk.DidJwkRegistrar
import id.walt.did.dids.registrar.local.key.DidKeyRegistrar
import id.walt.did.dids.registrar.local.web.DidWebRegistrar
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
class LocalRegistrar : DidRegistrar {
override val name = "walt.id local registrar"
@@ -18,23 +25,43 @@ class LocalRegistrar : DidRegistrar {
DidCheqdRegistrar(),
).associateBy { it.method }
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun getSupportedMethods() = Result.success(setOf("key", "jwk", "web", "cheqd" /*"ebsi",*/))
//override suspend fun getSupportedMethods() = Result.success(registrarMethods.values.toSet())
private fun getRegistrarForMethod(method: String) =
registrarMethods[method] ?: throw IllegalArgumentException("No local registrar for method: $method")
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun create(options: DidCreateOptions): DidResult =
getRegistrarForMethod(options.method).register(options)
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun createByKey(key: Key, options: DidCreateOptions): DidResult =
getRegistrarForMethod(options.method).registerByKey(key, options)
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun update() {
TODO("Not yet implemented")
}
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun delete() {
TODO("Not yet implemented")
}
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/UniregistrarRegistrar.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/UniregistrarRegistrar.kt
index cdc48915c..ba67edcf4 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/UniregistrarRegistrar.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/UniregistrarRegistrar.kt
@@ -12,7 +12,14 @@ import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.jsonPrimitive
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
class UniregistrarRegistrar : DidRegistrar {
@Suppress("MemberVisibilityCanBePrivate")
@@ -35,6 +42,10 @@ class UniregistrarRegistrar : DidRegistrar {
}
}
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun getSupportedMethods() = runCatching { lazyOf(getMethods()).value }
private suspend fun getMethods(): Set =
@@ -43,20 +54,36 @@ class UniregistrarRegistrar : DidRegistrar {
.map { it.jsonPrimitive.content }
.toSet()
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun create(options: DidCreateOptions): DidResult {
return DidResult("TODO" /* TODO */, http.post("$registrarUrl/create?method=${options.method}") {
setBody(options.options)
}.body())
}
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun createByKey(key: Key, options: DidCreateOptions): DidResult {
TODO("Not yet implemented")
}
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun update() {
TODO("Not yet implemented")
}
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun delete() {
TODO("Not yet implemented")
}
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidBtcrCreateOptions.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidBtcrCreateOptions.kt
index 0a018a01b..bf1ed7eb2 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidBtcrCreateOptions.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidBtcrCreateOptions.kt
@@ -1,5 +1,10 @@
package id.walt.did.dids.registrar.dids
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+
+@ExperimentalJsExport
+@JsExport
class DidBtcrCreateOptions(chain: String) : DidCreateOptions(
method = "btcr",
options = options("chain" to chain)
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidCheqdCreateOptions.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidCheqdCreateOptions.kt
index 4b6e858f3..847c5b44f 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidCheqdCreateOptions.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidCheqdCreateOptions.kt
@@ -1,5 +1,10 @@
package id.walt.did.dids.registrar.dids
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+
+@ExperimentalJsExport
+@JsExport
class DidCheqdCreateOptions(network: String) : DidCreateOptions(
method = "cheqd",
options = mapOf("network" to network)
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidCreateOptions.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidCreateOptions.kt
index 1e3882118..78b95e756 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidCreateOptions.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidCreateOptions.kt
@@ -6,11 +6,18 @@ import id.walt.did.utils.EnumUtils.enumValueIgnoreCase
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+import kotlin.js.JsName
+@ExperimentalJsExport
+@JsExport
open class DidCreateOptions(val method: String, val options: JsonElement) {
+ @JsName("secondaryConstructor")
constructor(method: String, options: Map) : this(method, options.toJsonElement())
+ @JsExport.Ignore
inline operator fun get(name: String): T? =
options.jsonObject["options"]?.jsonObject?.get(name)?.jsonPrimitive?.content?.let {
when (T::class) {
@@ -25,6 +32,8 @@ open class DidCreateOptions(val method: String, val options: JsonElement) {
}
}
+@ExperimentalJsExport
+@JsExport
internal fun options(options: Map, secret: Map = emptyMap()) = mapOf(
"options" to options,
"didDocument" to mapOf(
@@ -35,4 +44,6 @@ internal fun options(options: Map, secret: Map = empty
"secret" to secret
)
+@ExperimentalJsExport
+@JsExport
internal fun options(vararg inlineOptions: Pair) = options(mapOf(*inlineOptions))
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidEbsiCreateOptions.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidEbsiCreateOptions.kt
index 61dfc539c..f979240b9 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidEbsiCreateOptions.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidEbsiCreateOptions.kt
@@ -1,5 +1,10 @@
package id.walt.did.dids.registrar.dids
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+
+@ExperimentalJsExport
+@JsExport
class DidEbsiCreateOptions(version: Int, token: String) : DidCreateOptions(
method = "ebsi",
options = options(options = mapOf("version" to version), secret = mapOf("token" to token))
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidEthrCreateOptions.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidEthrCreateOptions.kt
index 4b1fbfdeb..6fd4a5ba8 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidEthrCreateOptions.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidEthrCreateOptions.kt
@@ -1,5 +1,10 @@
package id.walt.did.dids.registrar.dids
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+
+@ExperimentalJsExport
+@JsExport
class DidEthrCreateOptions(network: String = "goerli") : DidCreateOptions(
method = "ethr",
options = options("network" to network)
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidIonCreateOptions.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidIonCreateOptions.kt
index 3ac9c8726..96003fe5e 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidIonCreateOptions.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidIonCreateOptions.kt
@@ -1,5 +1,10 @@
package id.walt.did.dids.registrar.dids
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+
+@ExperimentalJsExport
+@JsExport
class DidIonCreateOptions : DidCreateOptions(
method = "ion",
options = emptyMap()
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidJwkCreateOptions.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidJwkCreateOptions.kt
index 2629b1ed3..d0d359a42 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidJwkCreateOptions.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidJwkCreateOptions.kt
@@ -1,7 +1,11 @@
package id.walt.did.dids.registrar.dids
import id.walt.crypto.keys.KeyType
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
class DidJwkCreateOptions(keyType: KeyType = KeyType.Ed25519) : DidCreateOptions(
method = "jwk",
options = options("keyType" to keyType.name.lowercase())
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidKeyCreateOptions.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidKeyCreateOptions.kt
index b218810de..30653816e 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidKeyCreateOptions.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidKeyCreateOptions.kt
@@ -1,12 +1,16 @@
package id.walt.did.dids.registrar.dids
import id.walt.crypto.keys.KeyType
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
/**
* The did:key implementation defaults to the W3C CCG spec https://w3c-ccg.github.io/did-method-key/. When
* _useJwkJcsPub_ is set to `true` the EBSI implementation (jwk_jcs-pub encoding) according
* https://hub.ebsi.eu/tools/libraries/key-did-resolver is performed.
*/
+@ExperimentalJsExport
+@JsExport
class DidKeyCreateOptions(keyType: KeyType = KeyType.Ed25519, useJwkJcsPub: Boolean = false) : DidCreateOptions(
method = "key",
options = options("keyType" to keyType.name.lowercase(), "useJwkJcsPub" to useJwkJcsPub)
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidOydCreateOptions.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidOydCreateOptions.kt
index 9ab8fc814..54c759d9b 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidOydCreateOptions.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidOydCreateOptions.kt
@@ -1,7 +1,11 @@
package id.walt.did.dids.registrar.dids
import kotlinx.serialization.json.JsonObject
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
class DidOydCreateOptions(document: JsonObject) : DidCreateOptions(
method = "oyd",
options = mapOf("didDocument" to document)
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidSovCreateOptions.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidSovCreateOptions.kt
index fc3e4fa9f..6d052ea79 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidSovCreateOptions.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidSovCreateOptions.kt
@@ -1,5 +1,10 @@
package id.walt.did.dids.registrar.dids
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+
+@ExperimentalJsExport
+@JsExport
class DidSovCreateOptions(network: String) : DidCreateOptions(
method = "sov",
options = options("network" to network)
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidV1CreateOptions.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidV1CreateOptions.kt
index f636779bc..5986fe22d 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidV1CreateOptions.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidV1CreateOptions.kt
@@ -1,7 +1,11 @@
package id.walt.did.dids.registrar.dids
import id.walt.crypto.keys.KeyType
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
class DidV1CreateOptions(ledger: String = "test", keyType: KeyType) : DidCreateOptions(
method = "v1",
options = options("ledger" to ledger, "keytype" to keyType.name.lowercase())
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidWebCreateOptions.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidWebCreateOptions.kt
index 47ad7560c..a1a1e4049 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidWebCreateOptions.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/dids/DidWebCreateOptions.kt
@@ -1,7 +1,11 @@
package id.walt.did.dids.registrar.dids
import id.walt.crypto.keys.KeyType
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
class DidWebCreateOptions(domain: String, path: String = "", keyType: KeyType = KeyType.Ed25519) : DidCreateOptions(
method = "web",
options = options("domain" to domain, "path" to path, "keyType" to keyType)
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/LocalRegistrarMethod.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/LocalRegistrarMethod.kt
index 0b1beafe6..040beb226 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/LocalRegistrarMethod.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/LocalRegistrarMethod.kt
@@ -3,10 +3,25 @@ package id.walt.did.dids.registrar.local
import id.walt.crypto.keys.Key
import id.walt.did.dids.registrar.DidResult
import id.walt.did.dids.registrar.dids.DidCreateOptions
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
abstract class LocalRegistrarMethod(val method: String) {
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
abstract suspend fun register(options: DidCreateOptions): DidResult
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
abstract suspend fun registerByKey(key: Key, options: DidCreateOptions): DidResult
}
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/DidCheqdRegistrar.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/DidCheqdRegistrar.kt
index 179f39401..1113d4e91 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/DidCheqdRegistrar.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/DidCheqdRegistrar.kt
@@ -32,7 +32,16 @@ import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.encodeToJsonElement
-
+import kotlin.io.encoding.Base64
+import kotlin.io.encoding.ExperimentalEncodingApi
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+
+@ExperimentalJsExport
+@JsExport
class DidCheqdRegistrar : LocalRegistrarMethod("cheqd") {
private val log = KotlinLogging.logger { }
@@ -63,9 +72,17 @@ class DidCheqdRegistrar : LocalRegistrarMethod("cheqd") {
}
}
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun register(options: DidCreateOptions): DidResult =
registerByKey(LocalKey.generate(KeyType.Ed25519), options)
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun registerByKey(key: Key, options: DidCreateOptions): DidResult =
createDid(key, options.get("network") ?: "testnet").let {
DidResult(it.id, id.walt.did.dids.document.DidDocument(DidCheqdDocument(it, key.exportJWKObject()).toMap()))
@@ -139,16 +156,17 @@ class DidCheqdRegistrar : LocalRegistrarMethod("cheqd") {
}.body()
}
+ @OptIn(ExperimentalEncodingApi::class)
private suspend fun signPayload(key: Key, job: JobActionResponse): List = let {
val state = (job.didState as? ActionDidState) ?: error("Unexpected did state")
if (!state.action.equals("signPayload", true)) error("Unexpected state action: ${state.action}")
val payloads = state.signingRequest.map {
- id.walt.did.utils.EncodingUtils.base64Decode(it.serializedPayload)
+ Base64.decode(it.serializedPayload)
}
// TODO: sign with key having alias from verification method
payloads.map {
- id.walt.did.utils.EncodingUtils.base64Encode(key.signRaw(it) as ByteArray)
+ Base64.encode(key.signRaw(it) as ByteArray)
}
}
-}
\ No newline at end of file
+}
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/DidState.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/DidState.kt
index 93f035ff2..c3851d888 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/DidState.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/DidState.kt
@@ -10,7 +10,11 @@ import kotlinx.serialization.json.JsonClassDiscriminator
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.modules.subclass
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
@OptIn(ExperimentalSerializationApi::class)
@Polymorphic
@Serializable
@@ -19,6 +23,8 @@ abstract class DidState {
abstract val state: String
}
+@ExperimentalJsExport
+@JsExport
val didStateSerializationModule = SerializersModule {
polymorphic(DidState::class) {
subclass(ActionDidState::class)
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/Secret.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/Secret.kt
index 48f0bf41d..41c713bff 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/Secret.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/Secret.kt
@@ -1,7 +1,11 @@
package id.walt.did.dids.registrar.local.cheqd.models.job.didstates
import kotlinx.serialization.Serializable
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
@Serializable
data class Secret(
val signingResponse: List
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/SigningResponse.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/SigningResponse.kt
index 2f1439d3e..e038e54d7 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/SigningResponse.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/SigningResponse.kt
@@ -1,7 +1,11 @@
package id.walt.did.dids.registrar.local.cheqd.models.job.didstates
import kotlinx.serialization.Serializable
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
@Serializable
data class SigningResponse(
val signature: String,
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/VerificationMethod.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/VerificationMethod.kt
index 4ca97ceff..2688158ea 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/VerificationMethod.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/VerificationMethod.kt
@@ -2,7 +2,11 @@ package id.walt.did.dids.registrar.local.cheqd.models.job.didstates
//import com.beust.klaxon.Json
import kotlinx.serialization.Serializable
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
@Serializable
data class VerificationMethod(
val controller: String,
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/action/ActionDidState.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/action/ActionDidState.kt
index dc57d6431..d54c1b167 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/action/ActionDidState.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/action/ActionDidState.kt
@@ -3,7 +3,11 @@ package id.walt.did.dids.registrar.local.cheqd.models.job.didstates.action
import id.walt.did.dids.registrar.local.cheqd.models.job.didstates.DidState
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
@Serializable
@SerialName("action")
data class ActionDidState(
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/action/Secret.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/action/Secret.kt
index 7c694a94b..a0d12b54a 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/action/Secret.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/action/Secret.kt
@@ -1,7 +1,11 @@
package id.walt.did.dids.registrar.local.cheqd.models.job.didstates.action
import kotlinx.serialization.Serializable
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
@Serializable
data class Secret(
val signingResponse: List
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/action/SigningRequest.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/action/SigningRequest.kt
index 277189881..73616731b 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/action/SigningRequest.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/action/SigningRequest.kt
@@ -1,7 +1,11 @@
package id.walt.did.dids.registrar.local.cheqd.models.job.didstates.action
import kotlinx.serialization.Serializable
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
@Serializable
data class SigningRequest(
val alg: String,
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/failed/FailedDidState.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/failed/FailedDidState.kt
index f63dbd289..30a0a4899 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/failed/FailedDidState.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/failed/FailedDidState.kt
@@ -3,7 +3,11 @@ package id.walt.did.dids.registrar.local.cheqd.models.job.didstates.failed
import id.walt.did.dids.registrar.local.cheqd.models.job.didstates.DidState
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
@Serializable
@SerialName("failed")
data class FailedDidState(
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/finished/DidDocument.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/finished/DidDocument.kt
index 589cd3b40..3c3a25de5 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/finished/DidDocument.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/finished/DidDocument.kt
@@ -2,7 +2,11 @@ package id.walt.did.dids.registrar.local.cheqd.models.job.didstates.finished
import id.walt.did.dids.registrar.local.cheqd.models.job.didstates.VerificationMethod
import kotlinx.serialization.Serializable
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
@Serializable
data class DidDocument(
val authentication: List,
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/finished/FinishedDidState.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/finished/FinishedDidState.kt
index b37892623..a1d126911 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/finished/FinishedDidState.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/didstates/finished/FinishedDidState.kt
@@ -4,7 +4,11 @@ import id.walt.did.dids.registrar.local.cheqd.models.job.didstates.DidState
import id.walt.did.dids.registrar.local.cheqd.models.job.didstates.Secret
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
@Serializable
@SerialName("finished")
data class FinishedDidState(
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/request/JobCreateRequest.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/request/JobCreateRequest.kt
index 3be48e459..23979c493 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/request/JobCreateRequest.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/request/JobCreateRequest.kt
@@ -2,7 +2,11 @@ package id.walt.did.dids.registrar.local.cheqd.models.job.request
import id.walt.did.dids.registrar.local.cheqd.models.job.response.didresponse.DidDocObject
import kotlinx.serialization.Serializable
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
@Serializable
data class JobCreateRequest(
val didDocument: DidDocObject
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/request/JobDeactivateRequest.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/request/JobDeactivateRequest.kt
index be5185626..b9ef9ae2c 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/request/JobDeactivateRequest.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/request/JobDeactivateRequest.kt
@@ -1,7 +1,11 @@
package id.walt.did.dids.registrar.local.cheqd.models.job.request
import kotlinx.serialization.Serializable
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
@Serializable
data class JobDeactivateRequest(
val did: String
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/request/JobSignRequest.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/request/JobSignRequest.kt
index 0dc1767ec..135406f4c 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/request/JobSignRequest.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/request/JobSignRequest.kt
@@ -2,7 +2,11 @@ package id.walt.did.dids.registrar.local.cheqd.models.job.request
import id.walt.did.dids.registrar.local.cheqd.models.job.didstates.Secret
import kotlinx.serialization.Serializable
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
@Serializable
data class JobSignRequest(
val jobId: String,
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/response/JobActionResponse.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/response/JobActionResponse.kt
index b82a8ce75..ca0bf73db 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/response/JobActionResponse.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/response/JobActionResponse.kt
@@ -2,7 +2,11 @@ package id.walt.did.dids.registrar.local.cheqd.models.job.response
import id.walt.did.dids.registrar.local.cheqd.models.job.didstates.DidState
import kotlinx.serialization.Serializable
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
@Serializable
data class JobActionResponse(
val didState: DidState,
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/response/didresponse/CheqdKey.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/response/didresponse/CheqdKey.kt
index 78c285944..f61afc87b 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/response/didresponse/CheqdKey.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/response/didresponse/CheqdKey.kt
@@ -1,7 +1,11 @@
package id.walt.did.dids.registrar.local.cheqd.models.job.response.didresponse
import kotlinx.serialization.Serializable
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
@Serializable
data class CheqdKey(
val publicKeyHex: String,
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/response/didresponse/DidDocObject.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/response/didresponse/DidDocObject.kt
index 7067f652b..9e4624128 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/response/didresponse/DidDocObject.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/response/didresponse/DidDocObject.kt
@@ -2,7 +2,11 @@ package id.walt.did.dids.registrar.local.cheqd.models.job.response.didresponse
import id.walt.did.dids.registrar.local.cheqd.models.job.didstates.VerificationMethod
import kotlinx.serialization.Serializable
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
@Serializable
data class DidDocObject(
val authentication: List,
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/response/didresponse/DidGetResponse.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/response/didresponse/DidGetResponse.kt
index dd32c3250..376c7612b 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/response/didresponse/DidGetResponse.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/cheqd/models/job/response/didresponse/DidGetResponse.kt
@@ -1,7 +1,11 @@
package id.walt.did.dids.registrar.local.cheqd.models.job.response.didresponse
import kotlinx.serialization.Serializable
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
@Serializable
data class DidGetResponse(
val didDoc: DidDocObject,
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/jwk/DidJwkRegistrar.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/jwk/DidJwkRegistrar.kt
index 9af9ac489..a6dc77b0d 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/jwk/DidJwkRegistrar.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/jwk/DidJwkRegistrar.kt
@@ -10,12 +10,27 @@ import id.walt.did.dids.registrar.DidResult
import id.walt.did.dids.registrar.dids.DidCreateOptions
import id.walt.did.dids.registrar.local.LocalRegistrarMethod
import io.ktor.utils.io.core.*
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
class DidJwkRegistrar : LocalRegistrarMethod("jwk") {
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun register(options: DidCreateOptions) = options.get("keyType")?.let {
registerByKey(LocalKey.generate(it), options)
} ?: throw IllegalArgumentException("KeyType option not found.")
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun registerByKey(key: Key, options: DidCreateOptions): DidResult {
val did = "did:jwk:${key.getPublicKey().exportJWK().toByteArray().encodeToBase64Url()}"
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/key/DidKeyRegistrar.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/key/DidKeyRegistrar.kt
index 135e362ae..26a90a918 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/key/DidKeyRegistrar.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/key/DidKeyRegistrar.kt
@@ -14,13 +14,28 @@ import id.walt.did.dids.registrar.dids.DidCreateOptions
import id.walt.did.dids.registrar.local.LocalRegistrarMethod
import id.walt.did.utils.JsonCanonicalization
import kotlinx.serialization.json.JsonObject
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
class DidKeyRegistrar : LocalRegistrarMethod("key") {
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun register(options: DidCreateOptions): DidResult = options.get("keyType")?.let {
registerByKey(LocalKey.generate(it), options)
} ?: throw IllegalArgumentException("KeyType option not found.")
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun registerByKey(key: Key, options: DidCreateOptions): DidResult = options.let {
if (key.keyType !in setOf(
KeyType.Ed25519,
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/key/IdentifierComponents.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/key/IdentifierComponents.kt
index e1ce84f47..33e9f76c1 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/key/IdentifierComponents.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/key/IdentifierComponents.kt
@@ -1,5 +1,10 @@
package id.walt.did.dids.registrar.local.key
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+
+@ExperimentalJsExport
+@JsExport
internal data class IdentifierComponents(
val multiCodecKeyCode: UInt,
val pubKeyBytes: ByteArray,
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/web/DidWebRegistrar.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/web/DidWebRegistrar.kt
index fc9633b4c..3ff5550e0 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/web/DidWebRegistrar.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/registrar/local/web/DidWebRegistrar.kt
@@ -8,21 +8,36 @@ import id.walt.did.dids.document.DidWebDocument
import id.walt.did.dids.registrar.DidResult
import id.walt.did.dids.registrar.dids.DidCreateOptions
import id.walt.did.dids.registrar.local.LocalRegistrarMethod
-import id.walt.did.utils.EncodingUtils.urlEncode
import id.walt.did.utils.ExtensionMethods.ensurePrefix
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+import net.thauvin.erik.urlencoder.UrlEncoderUtil
+@ExperimentalJsExport
+@JsExport
class DidWebRegistrar : LocalRegistrarMethod("web") {
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun register(options: DidCreateOptions): DidResult = options.get("keyType")?.let {
registerByKey(LocalKey.generate(it), options)
} ?: throw IllegalArgumentException("keyType option not found.")
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun registerByKey(key: Key, options: DidCreateOptions): DidResult =
options.get("domain")?.takeIf {
it.isNotEmpty()
}?.let {
- val domain = urlEncode(it)
+ val domain = UrlEncoderUtil.encode(it)
val path = options.get("path")?.takeIf { it.isNotEmpty() }?.let {
- it.ensurePrefix("/").split("/").joinToString(":") { part -> urlEncode(part) }
+ it.ensurePrefix("/").split("/").joinToString(":") { part -> UrlEncoderUtil.encode(part) }
} ?: ""
DidResult(
"did:web:$domain$path", DidDocument(
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/DidResolver.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/DidResolver.kt
index 3471a1ba2..98140f96a 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/DidResolver.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/DidResolver.kt
@@ -2,12 +2,31 @@ package id.walt.did.dids.resolver
import id.walt.crypto.keys.Key
import kotlinx.serialization.json.JsonObject
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
interface DidResolver {
val name: String
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun getSupportedMethods(): Result>
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun resolve(did: String): Result
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun resolveToKey(did: String): Result
}
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/DidResolverRegistrations.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/DidResolverRegistrations.kt
index e3504f29b..36822b45c 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/DidResolverRegistrations.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/DidResolverRegistrations.kt
@@ -1,5 +1,10 @@
package id.walt.did.dids.resolver
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+
+@ExperimentalJsExport
+@JsExport
object DidResolverRegistrations {
val didResolvers = setOf(
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/LocalResolver.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/LocalResolver.kt
index 4c477f90b..b9e1e5cd8 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/LocalResolver.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/LocalResolver.kt
@@ -10,7 +10,14 @@ import io.ktor.client.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.JsonObject
-
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+
+@ExperimentalJsExport
+@JsExport
class LocalResolver : DidResolver {
override val name = "walt.id local resolver"
private val http = HttpClient() {
@@ -29,6 +36,10 @@ class LocalResolver : DidResolver {
resolvers.remove(method)
}
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun getSupportedMethods(): Result> = Result.success(resolvers.keys)
private fun getResolverForDid(did: String): LocalResolverMethod {
@@ -36,9 +47,17 @@ class LocalResolver : DidResolver {
return resolvers[method] ?: throw IllegalArgumentException("No resolver for method: $did")
}
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun resolve(did: String): Result =
getResolverForDid(did).resolve(did).map { it.toJsonObject() }
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun resolveToKey(did: String): Result =
getResolverForDid(did).resolveToKey(did)
}
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/UniresolverResolver.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/UniresolverResolver.kt
index 8eb70418f..0ab168bf1 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/UniresolverResolver.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/UniresolverResolver.kt
@@ -13,7 +13,14 @@ import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonPrimitive
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
class UniresolverResolver : DidResolver {
@Suppress("MemberVisibilityCanBePrivate")
//var resolverUrl = "http://localhost:8080/1.0"
@@ -22,6 +29,10 @@ class UniresolverResolver : DidResolver {
override val name = "uniresolver @ $resolverUrl"
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun getSupportedMethods() = runCatching { lazyOf(getMethods()).value }
private val http = HttpClient {
@@ -33,9 +44,17 @@ class UniresolverResolver : DidResolver {
}
}
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun resolve(did: String): Result =
runCatching { http.get("$resolverUrl/identifiers/$did").body() }
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun resolveToKey(did: String): Result = resolve(did).fold(
onSuccess = {
VerificationMaterial.get(it)?.let {
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/DidCheqdResolver.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/DidCheqdResolver.kt
index d540b4547..8e030ba66 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/DidCheqdResolver.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/DidCheqdResolver.kt
@@ -10,14 +10,29 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
class DidCheqdResolver : LocalResolverMethod("cheqd") {
private val httpClient = HttpClient() //TODO: inject
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun resolve(did: String): Result = runCatching {
resolveDid(did)
}
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun resolveToKey(did: String): Result {
TODO("Not yet implemented")
// response verificationMethod contains only publicKeyMultibase
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/DidJwkResolver.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/DidJwkResolver.kt
index e8118767f..159532c64 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/DidJwkResolver.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/DidJwkResolver.kt
@@ -6,8 +6,19 @@ import id.walt.crypto.utils.Base64Utils.base64UrlDecode
import id.walt.did.dids.DidUtils
import id.walt.did.dids.document.DidDocument
import id.walt.did.dids.document.DidJwkDocument
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
class DidJwkResolver : LocalResolverMethod("jwk") {
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun resolve(did: String): Result {
val keyResult = resolveToKey(did)
if (keyResult.isFailure) return Result.failure(keyResult.exceptionOrNull()!!)
@@ -19,6 +30,10 @@ class DidJwkResolver : LocalResolverMethod("jwk") {
return Result.success(didDocument)
}
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun resolveToKey(did: String): Result =
LocalKey.importJWK(DidUtils.pathFromDid(did)!!.base64UrlDecode().decodeToString())
}
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/DidKeyResolver.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/DidKeyResolver.kt
index 5906dade7..a6604eaf3 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/DidKeyResolver.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/DidKeyResolver.kt
@@ -5,8 +5,19 @@ import id.walt.did.dids.DidUtils
import id.walt.did.dids.document.DidDocument
import id.walt.did.dids.document.DidKeyDocument
import id.walt.did.utils.KeyUtils
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
class DidKeyResolver : LocalResolverMethod("key") {
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun resolve(did: String): Result = resolveToKey(did).fold(
onSuccess = {
Result.success(
@@ -21,6 +32,10 @@ class DidKeyResolver : LocalResolverMethod("key") {
}
)
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun resolveToKey(did: String): Result = DidUtils.identifierFromDid(did)?.let {
KeyUtils.fromPublicKeyMultiBase(it)
} ?: Result.failure(Throwable("Failed to extract identifier from: $did"))
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/DidWebResolver.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/DidWebResolver.kt
index 818e76d45..b5a2d44a5 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/DidWebResolver.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/DidWebResolver.kt
@@ -12,9 +12,20 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
class DidWebResolver(private val client: HttpClient) : LocalResolverMethod("web") {
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun resolve(did: String): Result {
val url = resolveDidToUrl(did)
@@ -27,6 +38,10 @@ class DidWebResolver(private val client: HttpClient) : LocalResolverMethod("web"
return response
}
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
override suspend fun resolveToKey(did: String): Result {
val didDocumentResult = resolve(did)
if (didDocumentResult.isFailure) return Result.failure(didDocumentResult.exceptionOrNull()!!)
@@ -58,6 +73,10 @@ class DidWebResolver(private val client: HttpClient) : LocalResolverMethod("web"
"$URL_PROTOCOL://$domain$path"
} ?: throw IllegalArgumentException("Unexpected did format (missing identifier): $did")
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun tryConvertAnyPublicKeyJwkToKey(publicKeyJwks: List): Result {
publicKeyJwks.forEach { publicKeyJwk ->
val result = LocalKey.importJWK(publicKeyJwk)
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/LocalResolverMethod.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/LocalResolverMethod.kt
index 7c9641c90..098003740 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/LocalResolverMethod.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/dids/resolver/local/LocalResolverMethod.kt
@@ -1,10 +1,25 @@
package id.walt.did.dids.resolver.local
import id.walt.did.dids.document.DidDocument
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
abstract class LocalResolverMethod(val method: String) {
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
abstract suspend fun resolve(did: String): Result
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
abstract suspend fun resolveToKey(did: String): Result
}
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/helpers/WaltidServices.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/helpers/WaltidServices.kt
index bb5b4e04c..53fe14b44 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/helpers/WaltidServices.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/helpers/WaltidServices.kt
@@ -1,13 +1,28 @@
package id.walt.did.helpers
import id.walt.did.dids.DidService
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
object WaltidServices {
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun init() {
DidService.init()
}
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun minimalInit() {
DidService.minimalInit()
}
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/utils/EncodingUtils.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/utils/EncodingUtils.kt
deleted file mode 100644
index c0fa4b98b..000000000
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/utils/EncodingUtils.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package id.walt.did.utils
-
-expect object EncodingUtils {
- fun urlEncode(path: String): String
- fun urlDecode(path: String): String
- fun base64Encode(data: ByteArray): String
- fun base64Decode(data: String): ByteArray
- fun base58Encode(byteArray: ByteArray): String
- fun base58Decode(base58String: String): ByteArray
- fun fromHexString(hexString: String): ByteArray
- fun toHexString(byteArray: ByteArray): String
-}
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/utils/EnumUtils.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/utils/EnumUtils.kt
index 85135b853..3f18c1515 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/utils/EnumUtils.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/utils/EnumUtils.kt
@@ -1,11 +1,17 @@
package id.walt.did.utils
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+
+@ExperimentalJsExport
+@JsExport
object EnumUtils {
/**
* Gets the enum value by its name
* @param [value] enum value
* @return The enum value if found, otherwise - null
*/
+ @JsExport.Ignore
inline fun > enumValueIgnoreCase(value: String): T? = enumValues().firstOrNull {
it.name.equals(value, true)
}
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/utils/ExtensionMethods.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/utils/ExtensionMethods.kt
index 29cf6d0b5..f08f99d81 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/utils/ExtensionMethods.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/utils/ExtensionMethods.kt
@@ -1,5 +1,10 @@
package id.walt.did.utils
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+
+@ExperimentalJsExport
+@JsExport
object ExtensionMethods {
fun String.ensurePrefix(prefix: String) = this.takeIf { it.startsWith(prefix) } ?: prefix.plus(this)
}
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/utils/KeyMaterial.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/utils/KeyMaterial.kt
index 468b168aa..303b5d45b 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/utils/KeyMaterial.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/utils/KeyMaterial.kt
@@ -4,14 +4,26 @@ import id.walt.crypto.keys.Key
import id.walt.crypto.keys.KeyType
import id.walt.crypto.keys.LocalKey
import id.walt.crypto.keys.LocalKeyMetadata
+import id.walt.crypto.utils.decodeBase58
import id.walt.did.utils.KeyUtils.fromPublicKeyMultiBase
import id.walt.did.utils.KeyUtils.getKeyTypeForVerificationMaterialType
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
object KeyMaterial {
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun get(element: JsonElement): Result = when (element) {
is JsonObject -> importKey(element)
// is JsonPrimitive -> TODO: did
@@ -39,12 +51,18 @@ object KeyMaterial {
private suspend fun importJwk(element: JsonObject): Result = LocalKey.importJWK(element.toString())
private suspend fun importBase58(content: String, type: KeyType): Result = runCatching {
- LocalKey.importRawPublicKey(type, EncodingUtils.base58Decode(content), LocalKeyMetadata())
+ LocalKey.importRawPublicKey(type, content.decodeBase58(), LocalKeyMetadata())
}
private suspend fun importMultibase(content: String): Result = fromPublicKeyMultiBase(content)
private suspend fun importHex(content: String, type: KeyType): Result = runCatching {
- LocalKey.importRawPublicKey(type, EncodingUtils.fromHexString(content), LocalKeyMetadata())
+ LocalKey.importRawPublicKey(type, fromHexString(content), LocalKeyMetadata())
}
-}
\ No newline at end of file
+
+ private fun fromHexString(hexString: String) =
+ hexString.replace(" ", "").chunked(2).map { it.toInt(16).toByte() }.toByteArray()
+
+ /*private fun toHexString(byteArray: ByteArray) =
+ byteArray.joinToString("") { String.format("%02X ", (it.toInt() and 0xFF)) }*/
+}
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/utils/KeyUtils.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/utils/KeyUtils.kt
index 8f4a22355..e3890842e 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/utils/KeyUtils.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/utils/KeyUtils.kt
@@ -6,8 +6,19 @@ import id.walt.crypto.keys.LocalKey
import id.walt.crypto.keys.LocalKeyMetadata
import id.walt.crypto.utils.MultiBaseUtils
import id.walt.crypto.utils.MultiCodecUtils
+import love.forte.plugin.suspendtrans.annotation.JsPromise
+import love.forte.plugin.suspendtrans.annotation.JvmAsync
+import love.forte.plugin.suspendtrans.annotation.JvmBlocking
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
object KeyUtils {
+ @JvmBlocking
+ @JvmAsync
+ @JsPromise
+ @JsExport.Ignore
suspend fun fromPublicKeyMultiBase(identifier: String): Result {
val publicKeyRaw = MultiBaseUtils.convertMultiBase58BtcToRawKey(identifier)
//TODO: externalize import call
diff --git a/waltid-did/src/commonMain/kotlin/id/walt/did/utils/VerificationMaterial.kt b/waltid-did/src/commonMain/kotlin/id/walt/did/utils/VerificationMaterial.kt
index 7c9247a40..421ddb670 100644
--- a/waltid-did/src/commonMain/kotlin/id/walt/did/utils/VerificationMaterial.kt
+++ b/waltid-did/src/commonMain/kotlin/id/walt/did/utils/VerificationMaterial.kt
@@ -2,7 +2,11 @@ package id.walt.did.utils
import id.walt.crypto.utils.JsonUtils.toJsonElement
import kotlinx.serialization.json.*
+import kotlin.js.ExperimentalJsExport
+import kotlin.js.JsExport
+@ExperimentalJsExport
+@JsExport
object VerificationMaterial {
private val verificationMethods = arrayOf(
"verificationMethod",
diff --git a/waltid-did/src/jsMain/kotlin/bs58/bs58.kt b/waltid-did/src/jsMain/kotlin/bs58/bs58.kt
new file mode 100644
index 000000000..898e2d026
--- /dev/null
+++ b/waltid-did/src/jsMain/kotlin/bs58/bs58.kt
@@ -0,0 +1,8 @@
+import org.khronos.webgl.Uint8Array
+
+@JsModule("bs58")
+@JsNonModule
+external object bs58 {
+ fun encode(data: Uint8Array): String
+ fun decode(base58String: String): Uint8Array
+}
\ No newline at end of file
diff --git a/waltid-did/src/jsMain/kotlin/id/walt/did/utils/EncodingUtils.kt b/waltid-did/src/jsMain/kotlin/id/walt/did/utils/EncodingUtils.kt
deleted file mode 100644
index 248d085a5..000000000
--- a/waltid-did/src/jsMain/kotlin/id/walt/did/utils/EncodingUtils.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package id.walt.did.utils
-
-import kotlin.js.js
-
-actual object EncodingUtils {
- actual fun urlEncode(path: String): String = js("encodeURIComponent")(path)
-
- actual fun urlDecode(path: String): String = js("decodeURIComponent")(path)
-
- actual fun base64Encode(data: ByteArray): String = js("btoa")(data)
-
- actual fun base64Decode(data: String): ByteArray = js("atob")(data)
- actual fun base58Encode(byteArray: ByteArray): String {
- TODO("Not yet implemented")
- }
-
- actual fun base58Decode(base58String: String): ByteArray {
- TODO("Not yet implemented")
- }
-
- actual fun fromHexString(hexString: String): ByteArray {
- TODO("Not yet implemented")
- }
-
- actual fun toHexString(byteArray: ByteArray): String {
- TODO("Not yet implemented")
- }
-}
\ No newline at end of file
diff --git a/waltid-did/src/jsMain/kotlin/id/walt/did/utils/JsonCanonicalization.kt b/waltid-did/src/jsMain/kotlin/id/walt/did/utils/JsonCanonicalization.kt
index aeee192c0..76c341dbd 100644
--- a/waltid-did/src/jsMain/kotlin/id/walt/did/utils/JsonCanonicalization.kt
+++ b/waltid-did/src/jsMain/kotlin/id/walt/did/utils/JsonCanonicalization.kt
@@ -3,6 +3,8 @@ package id.walt.did.utils
import canonicalize
import io.ktor.utils.io.core.*
+@ExperimentalJsExport
+@JsExport
actual object JsonCanonicalization {
actual fun getCanonicalBytes(json: String): ByteArray = canonicalize(json).toByteArray()
actual fun getCanonicalString(json: String): String = canonicalize(json)
diff --git a/waltid-did/src/jsMain/kotlin/id/walt/did/utils/UUID.kt b/waltid-did/src/jsMain/kotlin/id/walt/did/utils/UUID.kt
index 681632a12..0dc4c8c16 100644
--- a/waltid-did/src/jsMain/kotlin/id/walt/did/utils/UUID.kt
+++ b/waltid-did/src/jsMain/kotlin/id/walt/did/utils/UUID.kt
@@ -2,4 +2,6 @@ package id.walt.did.utils
import uuid
+@ExperimentalJsExport
+@JsExport
actual fun randomUUID(): String = uuid.v4()
\ No newline at end of file
diff --git a/waltid-did/src/jvmMain/kotlin/id/walt/did/utils/EncodingUtils.kt b/waltid-did/src/jvmMain/kotlin/id/walt/did/utils/EncodingUtils.kt
deleted file mode 100644
index 6a3e74c35..000000000
--- a/waltid-did/src/jvmMain/kotlin/id/walt/did/utils/EncodingUtils.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package id.walt.did.utils
-
-import io.ipfs.multibase.Base58
-import java.net.URLDecoder
-import java.net.URLEncoder
-import java.nio.charset.StandardCharsets
-
-actual object EncodingUtils {
- actual fun urlEncode(path: String): String = URLEncoder.encode(path, StandardCharsets.UTF_8)
-
- actual fun urlDecode(path: String): String = URLDecoder.decode(path, StandardCharsets.UTF_8)
-
- actual fun base64Encode(data: ByteArray): String = java.util.Base64.getEncoder().encodeToString(data)
-
- actual fun base64Decode(data: String): ByteArray = java.util.Base64.getDecoder().decode(data)
- actual fun base58Encode(byteArray: ByteArray): String = Base58.encode(byteArray)
-
- actual fun base58Decode(base58String: String): ByteArray = Base58.decode(base58String)
- actual fun fromHexString(hexString: String): ByteArray =
- hexString.replace(" ", "").chunked(2).map { it.toInt(16).toByte() }.toByteArray()
-
- actual fun toHexString(byteArray: ByteArray): String =
- byteArray.joinToString("") { String.format("%02X ", (it.toInt() and 0xFF)) }
-}
diff --git a/waltid-did/src/jvmTest/kotlin/DidCreationTest.kt b/waltid-did/src/jvmTest/kotlin/DidCreationTest.kt
index e83975346..5cf68d404 100644
--- a/waltid-did/src/jvmTest/kotlin/DidCreationTest.kt
+++ b/waltid-did/src/jvmTest/kotlin/DidCreationTest.kt
@@ -1,9 +1,12 @@
+import id.walt.did.dids.registrar.dids.DidKeyCreateOptions
import id.walt.did.dids.registrar.dids.DidWebCreateOptions
+import id.walt.did.dids.registrar.local.key.DidKeyRegistrar
import id.walt.did.dids.registrar.local.web.DidWebRegistrar
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
+import kotlinx.serialization.json.jsonPrimitive
import kotlin.test.*
class DidCreationTest {
@@ -50,4 +53,19 @@ class DidCreationTest {
assertTrue { didDoc2["@context"]!!.jsonArray.isNotEmpty() }
}
+ @Test
+ fun checkReferencedDidMethods() = runTest {
+ val resultWeb = registrar
+ .register(DidWebCreateOptions("localhost", "/abc/xyz"))
+ val resultKey = DidKeyRegistrar().register(DidKeyCreateOptions())
+
+ arrayOf(resultKey, resultWeb).forEach { result ->
+ val didDoc1 = result.didDocument.toString()
+ println("DID doc: $didDoc1")
+ val id =
+ result.didDocument["verificationMethod"]?.jsonArray?.get(0)?.jsonObject?.get("id")?.jsonPrimitive?.content
+ val refId = result.didDocument["assertionMethod"]?.jsonArray?.get(0)?.jsonPrimitive?.content
+ assertEquals(id, refId)
+ }
+ }
}
diff --git a/waltid-did/src/jvmTest/kotlin/registrars/DidWebRegistrarTest.kt b/waltid-did/src/jvmTest/kotlin/registrars/DidWebRegistrarTest.kt
index 4701a95c3..32480058a 100644
--- a/waltid-did/src/jvmTest/kotlin/registrars/DidWebRegistrarTest.kt
+++ b/waltid-did/src/jvmTest/kotlin/registrars/DidWebRegistrarTest.kt
@@ -7,9 +7,9 @@ import id.walt.did.dids.DidUtils
import id.walt.did.dids.registrar.dids.DidCreateOptions
import id.walt.did.dids.registrar.dids.DidWebCreateOptions
import id.walt.did.dids.registrar.local.web.DidWebRegistrar
-import id.walt.did.utils.EncodingUtils
import id.walt.did.utils.ExtensionMethods.ensurePrefix
import kotlinx.coroutines.runBlocking
+import net.thauvin.erik.urlencoder.UrlEncoderUtil
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.Arguments.arguments
@@ -91,7 +91,7 @@ class DidWebRegistrarTest : DidRegistrarTestBase(DidWebRegistrar()) {
// assert [did identifier] and [domain + path] are identical
assert(
//TODO: avoid computations in result comparison
- EncodingUtils.urlDecode(DidUtils.identifierFromDid(did)!!) == domain.plus(
+ UrlEncoderUtil.decode(DidUtils.identifierFromDid(did)!!) == domain.plus(
path.takeIf { !it.isNullOrEmpty() }?.ensurePrefix("/")?.replace("/", ":") ?: ""
)
)
diff --git a/waltid-did/src/jvmTest/kotlin/resolvers/UniResolverTest.kt b/waltid-did/src/jvmTest/kotlin/resolvers/UniResolverTest.kt
index 9edc72211..4fc4fcaae 100644
--- a/waltid-did/src/jvmTest/kotlin/resolvers/UniResolverTest.kt
+++ b/waltid-did/src/jvmTest/kotlin/resolvers/UniResolverTest.kt
@@ -111,4 +111,4 @@ class UniResolverTest {
runBlocking { UniresolverResolver().getSupportedMethods() }
}.fold(onSuccess = { it.isSuccess }, onFailure = { false })
}
-}
\ No newline at end of file
+}
diff --git a/waltid-issuer-api/build.gradle.kts b/waltid-issuer-api/build.gradle.kts
index f1d908385..7e88f6095 100644
--- a/waltid-issuer-api/build.gradle.kts
+++ b/waltid-issuer-api/build.gradle.kts
@@ -1,4 +1,3 @@
-import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
object Versions {
@@ -198,11 +197,3 @@ publishing {
renderers = arrayOf(InventoryHtmlReportRenderer("xyzkit-licenses-report.html", "XYZ Kit"))
filters = arrayOf(LicenseBundleNormalizer())
}*/
-
-
-
-tasks.withType {
- rejectVersionIf {
- listOf("-beta", "-alpha", "-rc").any { it in candidate.version.lowercase() } || candidate.version.takeLast(4).contains("RC")
- }
-}
diff --git a/waltid-mdoc-credentials/build.gradle.kts b/waltid-mdoc-credentials/build.gradle.kts
index 0343d840e..e2495d68d 100644
--- a/waltid-mdoc-credentials/build.gradle.kts
+++ b/waltid-mdoc-credentials/build.gradle.kts
@@ -68,8 +68,8 @@ kotlin {
}
val jvmTest by getting {
dependencies {
- implementation("org.bouncycastle:bcprov-jdk15to18:1.76")
- implementation("org.bouncycastle:bcpkix-jdk15to18:1.76")
+ implementation("org.bouncycastle:bcprov-lts8on:2.73.4")
+ implementation("org.bouncycastle:bcpkix-lts8on:2.73.4")
implementation("io.mockk:mockk:1.13.2")
implementation("io.kotest:kotest-runner-junit5:5.5.5")
@@ -127,10 +127,3 @@ npmPublish {
}
}
}
-
-
-tasks.withType {
- rejectVersionIf {
- listOf("-beta", "-alpha", "-rc").any { it in candidate.version.lowercase() } || candidate.version.takeLast(4).contains("RC")
- }
-}
diff --git a/waltid-openid4vc/build.gradle.kts b/waltid-openid4vc/build.gradle.kts
index 89b8279d2..fabf68056 100644
--- a/waltid-openid4vc/build.gradle.kts
+++ b/waltid-openid4vc/build.gradle.kts
@@ -14,11 +14,11 @@ group = "id.walt"
repositories {
mavenCentral()
- maven("https://jitpack.io") {
+ /*maven("https://jitpack.io") {
content {
includeGroup("com.github.multiformats")
}
- }
+ }*/
maven("https://repo.danubetech.com/repository/maven-public/")
maven("https://maven.walt.id/repository/waltid/") {
content {
@@ -62,7 +62,7 @@ kotlin {
}
}
}
- nodejs() {
+ nodejs {
generateTypeScriptDefinitions()
}
binaries.library()
@@ -117,14 +117,14 @@ kotlin {
implementation("io.kotest:kotest-assertions-json:5.8.0")
implementation("com.google.crypto.tink:tink:1.12.0") // for JOSE using Ed25519
// Multibase
- implementation("com.github.multiformats:java-multibase:v1.1.1")
+ // implementation("com.github.multiformats:java-multibase:v1.1.1")
// TODO: current version implementation("id.walt:waltid-ssikit:1.2311131043.0")
//implementation("id.walt:waltid-ssikit:1.JWTTYP") {
// exclude("waltid-sd-jwt-jvm")
// exclude(module = "waltid-sd-jwt-jvm")
//}
- implementation("org.bouncycastle:bcprov-jdk18on:1.77") // for secp256k1 (which was removed with Java 17)
- implementation("org.bouncycastle:bcpkix-jdk18on:1.77") // PEM import
+ implementation("org.bouncycastle:bcprov-lts8on:2.73.4") // for secp256k1 (which was removed with Java 17)
+ implementation("org.bouncycastle:bcpkix-lts8on:2.73.4") // PEM import
implementation("io.github.oshai:kotlin-logging-jvm:6.0.3")
implementation("io.ktor:ktor-server-core-jvm:$ktor_version")
@@ -206,11 +206,3 @@ npmPublish {
}
}
}
-
-
-
-tasks.withType {
- rejectVersionIf {
- listOf("-beta", "-alpha", "-rc").any { it in candidate.version.lowercase() } || candidate.version.takeLast(4).contains("RC")
- }
-}
diff --git a/waltid-reporting/build.gradle.kts b/waltid-reporting/build.gradle.kts
index 611ef740d..05000f87f 100644
--- a/waltid-reporting/build.gradle.kts
+++ b/waltid-reporting/build.gradle.kts
@@ -118,11 +118,3 @@ kotlin {
}
}
}
-
-
-
-tasks.withType {
- rejectVersionIf {
- listOf("-beta", "-alpha", "-rc").any { it in candidate.version.lowercase() } || candidate.version.takeLast(4).contains("RC")
- }
-}
diff --git a/waltid-sdjwt/README.md b/waltid-sdjwt/README.md
index 628fe3a03..f663d7529 100644
--- a/waltid-sdjwt/README.md
+++ b/waltid-sdjwt/README.md
@@ -71,7 +71,7 @@ specification: [draft-ietf-oauth-selective-disclosure-jwt-04](https://datatrack
[...]
id.walt
-waltid-sd-jwt-jvm
+waltid-sdjwt-jvm
[ version ]
```
@@ -89,7 +89,7 @@ repositories {
val sdJwtVersion = "1.2306071235.0"
[...]
dependencies {
- implementation("id.walt:waltid-sd-jwt-jvm:$sdJwtVersion")
+ implementation("id.walt:waltid-sdjwt-jvm:$sdJwtVersion")
}
```
diff --git a/waltid-sdjwt/build.gradle.kts b/waltid-sdjwt/build.gradle.kts
index ff5830f94..c54b94b4d 100644
--- a/waltid-sdjwt/build.gradle.kts
+++ b/waltid-sdjwt/build.gradle.kts
@@ -65,7 +65,6 @@ kotlin {
if (hostOs in listOf("Windows", "Linux") && hostArch == "aarch64") {
println("Native compilation is not yet supported for aarch64 on Windows / Linux.")
} else {
- println("Running with $hostOs under $hostArch")
val isMingwX64 = hostOs.startsWith("Windows")
val nativeTarget = when {
hostOs == "Mac OS X" -> macosX64("native")
@@ -83,6 +82,7 @@ kotlin {
else -> listOf()
}.forEach {
+ println("Native compilation for target: ${it.name}")
val platform = when (it.name) {
"iosArm64" -> "iphoneos"
else -> "iphonesimulator"
@@ -214,11 +214,3 @@ npmPublish {
}
}
}
-
-
-
-tasks.withType {
- rejectVersionIf {
- listOf("-beta", "-alpha", "-rc").any { it in candidate.version.lowercase() } || candidate.version.takeLast(4).contains("RC")
- }
-}
diff --git a/waltid-verifiable-credentials/build.gradle.kts b/waltid-verifiable-credentials/build.gradle.kts
index 4730798c3..54bf70a31 100644
--- a/waltid-verifiable-credentials/build.gradle.kts
+++ b/waltid-verifiable-credentials/build.gradle.kts
@@ -29,7 +29,6 @@ suspendTransform {
js {
}*/
- useJvmDefault()
useJsDefault()
}
@@ -182,11 +181,3 @@ npmPublish {
}
}
}
-
-
-
-tasks.withType {
- rejectVersionIf {
- listOf("-beta", "-alpha", "-rc").any { it in candidate.version.lowercase() } || candidate.version.takeLast(4).contains("RC")
- }
-}
diff --git a/waltid-verifiable-credentials/src/commonMain/kotlin/id/walt/credentials/utils/EncodingUtils.kt b/waltid-verifiable-credentials/src/commonMain/kotlin/id/walt/credentials/utils/EncodingUtils.kt
deleted file mode 100644
index f32b4c571..000000000
--- a/waltid-verifiable-credentials/src/commonMain/kotlin/id/walt/credentials/utils/EncodingUtils.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package id.walt.credentials.utils
-
-expect object EncodingUtils {
- fun urlEncode(path: String): String
- fun urlDecode(path: String): String
-}
diff --git a/waltid-verifiable-credentials/src/jsMain/kotlin/id/walt/credentials/utils/EncodingUtils.kt b/waltid-verifiable-credentials/src/jsMain/kotlin/id/walt/credentials/utils/EncodingUtils.kt
deleted file mode 100644
index d3c311141..000000000
--- a/waltid-verifiable-credentials/src/jsMain/kotlin/id/walt/credentials/utils/EncodingUtils.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package id.walt.credentials.utils
-@OptIn(ExperimentalJsExport::class)
-@JsExport
-actual object EncodingUtils {
- actual fun urlEncode(path: String): String = js("encodeURIComponent")(path)
-
- actual fun urlDecode(path: String): String = js("decodeURIComponent")(path)
-}
diff --git a/waltid-verifiable-credentials/src/jvmMain/kotlin/id/walt/credentials/utils/EncodingUtils.kt b/waltid-verifiable-credentials/src/jvmMain/kotlin/id/walt/credentials/utils/EncodingUtils.kt
deleted file mode 100644
index f062b8216..000000000
--- a/waltid-verifiable-credentials/src/jvmMain/kotlin/id/walt/credentials/utils/EncodingUtils.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package id.walt.credentials.utils
-
-import java.net.URLDecoder
-import java.net.URLEncoder
-
-actual object EncodingUtils {
- actual fun urlEncode(path: String): String = URLEncoder.encode(path, Charsets.UTF_8)
-
- actual fun urlDecode(path: String): String = URLDecoder.decode(path, Charsets.UTF_8)
-}
diff --git a/waltid-verifier-api/build.gradle.kts b/waltid-verifier-api/build.gradle.kts
index fdb04a4c6..9dd112f12 100644
--- a/waltid-verifier-api/build.gradle.kts
+++ b/waltid-verifier-api/build.gradle.kts
@@ -194,10 +194,3 @@ publishing {
// renderers = arrayOf(InventoryHtmlReportRenderer("waltid-verifier-licenses-report.html", "walt.id verifier"))
// filters = arrayOf(LicenseBundleNormalizer())
//}
-
-
-tasks.withType {
- rejectVersionIf {
- listOf("-beta", "-alpha", "-rc").any { it in candidate.version.lowercase() } || candidate.version.takeLast(4).contains("RC")
- }
-}
diff --git a/waltid-wallet-api/Dockerfile b/waltid-wallet-api/Dockerfile
index 19bf0054c..6d0510037 100644
--- a/waltid-wallet-api/Dockerfile
+++ b/waltid-wallet-api/Dockerfile
@@ -5,6 +5,7 @@ COPY gradle/ /work/gradle
COPY settings.gradle.kts build.gradle.kts gradle.properties gradlew /work/
COPY waltid-openid4vc/build.gradle.kts /work/waltid-openid4vc/
COPY waltid-sdjwt/build.gradle.kts /work/waltid-sdjwt/
+COPY waltid-crypto/build.gradle.kts /work/waltid-crypto/
COPY waltid-did/build.gradle.kts /work/waltid-did/
WORKDIR /work/waltid-wallet-api/
@@ -13,6 +14,7 @@ RUN gradle build || return 0
COPY waltid-openid4vc/. /work/waltid-openid4vc
COPY waltid-sdjwt/. /work/waltid-sdjwt
+COPY waltid-crypto/. /work/waltid-crypto
COPY waltid-did/. /work/waltid-did
COPY waltid-wallet-api/src/ /work/waltid-wallet-api/src
diff --git a/waltid-wallet-api/build.gradle.kts b/waltid-wallet-api/build.gradle.kts
index e2637d992..754748c46 100644
--- a/waltid-wallet-api/build.gradle.kts
+++ b/waltid-wallet-api/build.gradle.kts
@@ -99,14 +99,15 @@ dependencies {
/* -- Security -- */
// Bouncy Castle
- implementation("org.bouncycastle:bcprov-jdk18on:1.77")
+ implementation("org.bouncycastle:bcprov-lts8on:2.73.4")
// Argon2
implementation("de.mkammerer:argon2-jvm:2.11")
// waltid-did
- implementation(project(":waltid-did"))//id.walt.crypto provided by id.walt.did:waltid-did
+ implementation(project(":waltid-crypto"))
+ implementation(project(":waltid-did"))
// OIDC
implementation(project(":waltid-openid4vc"))
@@ -159,11 +160,3 @@ dependencies {
testImplementation("io.kotest.extensions:kotest-assertions-ktor:2.0.0")*/
testImplementation("io.ktor:ktor-server-tests-jvm:$ktorVersion")
}
-
-
-
-tasks.withType {
- rejectVersionIf {
- listOf("-beta", "-alpha", "-rc").any { it in candidate.version.lowercase() } || candidate.version.takeLast(4).contains("RC")
- }
-}
diff --git a/waltid-web-wallet/src/components/credentials/VerifiableCredentialCard.vue b/waltid-web-wallet/src/components/credentials/VerifiableCredentialCard.vue
index 04047a7f3..9173e6620 100644
--- a/waltid-web-wallet/src/components/credentials/VerifiableCredentialCard.vue
+++ b/waltid-web-wallet/src/components/credentials/VerifiableCredentialCard.vue
@@ -57,7 +57,7 @@ const props = defineProps({
const credential = props.credential?.parsedDocument;
const isDetailView = props.isDetailView ?? false;
const manifest = props.credential?.manifest != "{}" ? props.credential?.manifest : null
-const manifestDisplay = manifest ? JSON.parse(manifest)?.display : null;
+const manifestDisplay = manifest ? (typeof manifest === 'string' ? JSON.parse(manifest) : manifest)?.display : null;
const manifestCard = manifestDisplay?.card;
const title = manifestDisplay?.title ?? credential.type?.at(-1);
diff --git a/waltid-web-wallet/src/pages/wallet/[wallet]/credentials/[credentialId].vue b/waltid-web-wallet/src/pages/wallet/[wallet]/credentials/[credentialId].vue
index 1515ad027..1e39e0140 100644
--- a/waltid-web-wallet/src/pages/wallet/[wallet]/credentials/[credentialId].vue
+++ b/waltid-web-wallet/src/pages/wallet/[wallet]/credentials/[credentialId].vue
@@ -403,7 +403,7 @@ type WalletCredential = {
const { data: credential, pending, refresh, error } = await useLazyFetch(`/wallet-api/wallet/${currentWallet.value}/credentials/${encodeURIComponent(credentialId)}`);
refreshNuxtData();
-const manifest = computed(() => (credential.value?.manifest && credential.value?.manifest != "{}" ? JSON.parse(credential.value?.manifest) : null));
+const manifest = computed(() => (credential.value?.manifest && credential.value?.manifest != "{}" ? (typeof credential.value?.manifest === 'string' ? JSON.parse(credential.value?.manifest) : credential.value?.manifest) : null));
const manifestClaims = computed(() => manifest.value?.display?.claims);
const issuerName = ref(null);