Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Generic Jsonrpc client service and Timestamp service implementation #82

Merged
merged 6 commits into from Dec 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,12 @@ Notable changes since the last release of the [SSI Kit](https://github.com/walt-

## [Unreleased]

- Roadmap Items
- EBSI Timestamping service https://github.com/walt-id/waltid-roadmap/issues/25

- Features
- Added generic jsonRpcService for working with the EBSI ledger https://github.com/walt-id/waltid-ssikit/pull/82

## [1.1.1] - 2021-12-03

- Features
Expand Down
1 change: 1 addition & 0 deletions service-matrix.properties
@@ -1,4 +1,5 @@
id.walt.services.essif.didebsi.DidEbsiService=id.walt.services.essif.didebsi.WaltIdDidEbsiService
id.walt.services.essif.jsonrpc.JsonRpcService=id.walt.services.essif.jsonrpc.WaltIdJsonRpcService
id.walt.services.vc.JsonLdCredentialService=id.walt.services.vc.WaltIdJsonLdCredentialService
id.walt.services.vc.JwtCredentialService=id.walt.services.vc.WaltIdJwtCredentialService
id.walt.services.crypto.CryptoService=id.walt.services.crypto.SunCryptoService
Expand Down
118 changes: 91 additions & 27 deletions src/main/kotlin/id/walt/cli/EssifCommand.kt
Expand Up @@ -14,7 +14,13 @@ import id.walt.services.did.DidService
import id.walt.services.essif.EssifClient
import id.walt.services.essif.EssifClientVcExchange
import id.walt.services.essif.TrustedIssuerClient
import id.walt.services.essif.timestamp.Timestamp
import id.walt.services.essif.timestamp.WaltIdTimestampService
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.io.File
import java.util.*

class EssifCommand : CliktCommand(
name = "essif",
Expand Down Expand Up @@ -94,45 +100,103 @@ class EssifDidRegisterCommand : CliktCommand(
}
}

class EssifVcIssuanceCommand : CliktCommand(
name = "vc-issuance",
help = """ESSIF VC issuance flow

ESSIF VC issuance flow"""
//class EssifVcIssuanceCommand : CliktCommand(
// name = "vc-issuance",
// help = """ESSIF VC issuance flow
//
// ESSIF VC issuance flow"""
//) {
// override fun run() {
//
// // Mocked flow:
// // EssifFlowRunner.vcIssuance()
//
// // This runs everything: EssifClient.authenticate()
// val did: String = DidService.create(DidMethod.ebsi) // Client DID
//
// val oidcReq = TrustedIssuerClient.generateAuthenticationRequest()
// echo("- Authentication request: \n$oidcReq\n\n")
//
// val didAuthReq = EssifClientVcExchange.validateAuthenticationRequest(oidcReq)
// echo("- Parsed and validated authentication request: \n$didAuthReq\n\n")
//
// val authResp = EssifClientVcExchange.generateAuthenticationResponse(did, didAuthReq)
// echo("- Authentication response JWT: \n$authResp\n\n")
//
// val encAccessToken = TrustedIssuerClient.openSession(authResp)
// echo("- Received encrypted access token: \n$encAccessToken\n\n")
//
// val accessToken = EssifClientVcExchange.decryptAccessToken(encAccessToken)
// echo("- Decrypted and verified access token: \n$accessToken\n\n")
//
// }
//}
//
//class EssifVcExchangeCommand : CliktCommand(
// name = "vc-exchange",
// help = """ESSIF VC exchange flow
//
// ESSIF VC exchange flow"""
//) {
// override fun run() = EssifClient.vcExchange()
//}

class EssifTimestampCommand : CliktCommand(
name = "timestamp",
help = """EBSI Timestamp API operations.

Create and retrieve a timestamp on the EBSI ledger."""
) {
override fun run() {

// Mocked flow:
// EssifFlowRunner.vcIssuance()
override fun run() {}
}

// This runs everything: EssifClient.authenticate()
val did: String = DidService.create(DidMethod.ebsi) // Client DID
class EssifTimestampCreateCommand : CliktCommand(
name = "create",
help = """Create timestamp.

val oidcReq = TrustedIssuerClient.generateAuthenticationRequest()
echo("- Authentication request: \n$oidcReq\n\n")
Create timestamp on the EBSI ledger."""
) {
val dataFile: File by argument("DATA-FILE", help = "File containing data to be used for the timestamp").file()
val did: String by option("-d", "--did", help = "DID of the issuer.").required()
val ethKeyAlias: String? by option("-e", "--eth-key", help = "ETH key alias.")

val didAuthReq = EssifClientVcExchange.validateAuthenticationRequest(oidcReq)
echo("- Parsed and validated authentication request: \n$didAuthReq\n\n")
override fun run() {
echo("Creating timestamp")

val authResp = EssifClientVcExchange.generateAuthenticationResponse(did, didAuthReq)
echo("- Authentication response JWT: \n$authResp\n\n")
if (!dataFile.exists()) throw Exception("File ${dataFile.absoluteFile} not found.")

val encAccessToken = TrustedIssuerClient.openSession(authResp)
echo("- Received encrypted access token: \n$encAccessToken\n\n")
val transactionHash = WaltIdTimestampService().createTimestamp(did, ethKeyAlias ?: did, "{\"test\": \"${UUID.randomUUID()}\"}")

val accessToken = EssifClientVcExchange.decryptAccessToken(encAccessToken)
echo("- Decrypted and verified access token: \n$accessToken\n\n")
echo("\nReturned transaction hash:\n")

echo(transactionHash)
}
}

class EssifVcExchangeCommand : CliktCommand(
name = "vc-exchange",
help = """ESSIF VC exchange flow
class EssifTimestampGetCommand : CliktCommand(
name = "get",
help = """Get timestamp.

ESSIF VC exchange flow"""
Get timestamp by its ID or transaction hash."""
) {
override fun run() = EssifClient.vcExchange()
val id: String? by option("-i", "--timestamp-id", help = "Timestamp ID.")
val hash: String? by option("-h", "--timestamp-hash", help = "Timestamp hash.")

override fun run() {
echo("Getting timestamp.")

val timestamp: Timestamp? = runBlocking {
when {
id != null -> WaltIdTimestampService().getByTimestampId(id!!)
hash != null -> WaltIdTimestampService().getByTransactionHash(hash!!)
else -> throw Exception("Either timestamp ID or transaction hash need to be specified")
}
}

echo("\nResult:\n")

echo(Json.encodeToString(timestamp))
}
}

class EssifTirCommand : CliktCommand(
Expand All @@ -147,7 +211,7 @@ class EssifTirCommand : CliktCommand(
fun getIssuerHelper(did: String, raw: Boolean) = when (raw) {
true -> TrustedIssuerClient.getIssuerRaw(did).prettyPrint()
else -> TrustedIssuerClient.getIssuer(did).encodePretty()
}
}

class EssifTirGetIssuerCommand : CliktCommand(
name = "get",
Expand Down
8 changes: 6 additions & 2 deletions src/main/kotlin/id/walt/cli/WaltCLI.kt
Expand Up @@ -114,14 +114,18 @@ object WaltCLI {
EssifCommand().subcommands(
EssifOnboardingCommand(),
EssifAuthCommand(),
EssifVcIssuanceCommand(),
EssifVcExchangeCommand(),
// EssifVcIssuanceCommand(),
// EssifVcExchangeCommand(),
EssifDidCommand().subcommands(
EssifDidRegisterCommand()
),
EssifTirCommand().subcommands(
EssifTirGetIssuerCommand()
),
EssifTimestampCommand().subcommands(
EssifTimestampCreateCommand(),
EssifTimestampGetCommand()
),
EssifTaorCommand(),
EssifTsrCommand()
),
Expand Down
59 changes: 3 additions & 56 deletions src/main/kotlin/id/walt/services/essif/didebsi/DidEbsiService.kt
Expand Up @@ -2,69 +2,16 @@ package id.walt.services.essif.didebsi

import id.walt.servicematrix.ServiceProvider
import id.walt.services.WaltIdService
import kotlinx.serialization.Serializable

@Serializable
sealed class JsonRpcParams

interface DidResponse

@Serializable
data class InsertDidDocumentParams(
val from: String,
val identifier: String,
val hashAlgorithmId: Int,
val hashValue: String,
val didVersionInfo: String,
val timestampData: String,
val didVersionMetadata: String
) : JsonRpcParams()

@Serializable
data class SignedTransactionParams(
val protocol: String,
val unsignedTransaction: UnsignedTransaction,
val r: String,
val s: String,
val v: String,
val signedRawTransaction: String
) : JsonRpcParams()


@Serializable
data class UnsignedTransaction(
val from: String,
val to: String,
val data: String,
val nonce: String,
val chainId: String,
val gasLimit: String,
val gasPrice: String,
val value: String
)

@Serializable
data class SignedTransaction(val r: String, val s: String, val v: String, val signedRawTransaction: String)

import id.walt.services.essif.jsonrpc.InsertDidDocumentParams

open class DidEbsiService : WaltIdService() {

override val implementation get() = serviceImplementation<DidEbsiService>()

open fun registerDid(did: String, ethKeyAlias: String): Unit = implementation.registerDid(did, ethKeyAlias)

// TODO: Verify all params are properly defined according to EBSI expectations => https://ec.europa.eu/cefdigital/wiki/pages/viewpage.action?spaceKey=EBP&title=DID+Registry+Smart+Contract
open fun buildInsertDocumentParams(did: String, ethKeyAlias: String? = null): List<InsertDidDocumentParams> =
implementation.buildInsertDocumentParams(did, ethKeyAlias)

open fun signTransaction(ethKeyAlias: String, unsignedTransaction: UnsignedTransaction): SignedTransaction =
implementation.signTransaction(ethKeyAlias, unsignedTransaction)

open fun buildSignedTransactionParams(
unsignedTransaction: UnsignedTransaction,
signedTransaction: SignedTransaction
): List<SignedTransactionParams> =
implementation.buildSignedTransactionParams(unsignedTransaction, signedTransaction)
open fun buildUnsignedTransactionParams(did: String, ethKeyAlias: String? = null): List<InsertDidDocumentParams> =
implementation.buildUnsignedTransactionParams(did, ethKeyAlias)

companion object : ServiceProvider {
override fun getService() = object : DidEbsiService() {}
Expand Down