Skip to content

Commit

Permalink
fix #22 - [publish] - Implement a DB secretResolver
Browse files Browse the repository at this point in the history
- Refactor code
- Create a common interface to implement diverse secret resolvers
- Make DIDPeer a class
  • Loading branch information
rogelio-blanco committed Sep 13, 2022
1 parent 8d5de7c commit 6177166
Show file tree
Hide file tree
Showing 12 changed files with 256 additions and 140 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ permissions:

jobs:
build:

name: Compile and Test Code
runs-on: ubuntu-latest

steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ permissions:

jobs:
build:
name: Compile and Test code
name: Compile and Test Code
runs-on: ubuntu-latest

steps:
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
version=2.0.2-SNAPSHOT
version=2.0.3-SNAPSHOT

kotlin.code.style=official
186 changes: 90 additions & 96 deletions src/main/kotlin/com/rootsid/wal/library/didcom/DIDPeer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.didcommx.didcomm.message.Message
import org.didcommx.didcomm.model.PackEncryptedParams
import org.didcommx.didcomm.model.PackEncryptedResult
import org.didcommx.didcomm.model.UnpackParams
import org.didcommx.didcomm.secret.SecretResolverEditable
import org.didcommx.didcomm.secret.generateEd25519Keys
import org.didcommx.didcomm.secret.generateX25519Keys
import org.didcommx.didcomm.secret.jwkToSecret
Expand All @@ -15,108 +16,101 @@ import org.didcommx.didcomm.utils.toJson
import org.didcommx.peerdid.*
import java.util.*

fun createPeerDID(
authKeysCount: Int = 1,
agreementKeysCount: Int = 1,
serviceEndpoint: String? = null,
serviceRoutingKeys: List<String>,
secretResolver: SecretResolverCustom
): String {
// 1. generate keys in JWK format
val x25519keyPairs = (1..agreementKeysCount).map { generateX25519Keys() }
val ed25519keyPairs = (1..authKeysCount).map { generateEd25519Keys() }
class DIDPeer(private val secretResolver: SecretResolverEditable = SecretResolverCustom()) {

// 2. prepare the keys for peer DID lib
val authPublicKeys = ed25519keyPairs.map {
VerificationMaterialAuthentication(
format = VerificationMaterialFormatPeerDID.JWK,
type = VerificationMethodTypeAuthentication.JSON_WEB_KEY_2020,
value = it.public
)
}
val agreemPublicKeys = x25519keyPairs.map {
VerificationMaterialAgreement(
format = VerificationMaterialFormatPeerDID.JWK,
type = VerificationMethodTypeAgreement.JSON_WEB_KEY_2020,
value = it.public
)
}
fun create(
authKeysCount: Int = 1, agreementKeysCount: Int = 1,
serviceEndpoint: String? = null, serviceRoutingKeys: List<String>
): String {
// 1. generate keys in JWK format
val x25519keyPairs = (1..agreementKeysCount).map { generateX25519Keys() }
val ed25519keyPairs = (1..authKeysCount).map { generateEd25519Keys() }

// 3. generate service
val service = serviceEndpoint?.let {
toJson(
DIDCommServicePeerDID(
id = "new-id",
type = SERVICE_DIDCOMM_MESSAGING,
serviceEndpoint = it,
routingKeys = serviceRoutingKeys,
accept = listOf("didcomm/v2")
).toDict()
)
}
// 2. prepare the keys for peer DID lib
val authPublicKeys = ed25519keyPairs.map {
VerificationMaterialAuthentication(
format = VerificationMaterialFormatPeerDID.JWK,
type = VerificationMethodTypeAuthentication.JSON_WEB_KEY_2020,
value = it.public
)
}
val agreemPublicKeys = x25519keyPairs.map {
VerificationMaterialAgreement(
format = VerificationMaterialFormatPeerDID.JWK,
type = VerificationMethodTypeAgreement.JSON_WEB_KEY_2020,
value = it.public
)
}

// 4. call peer DID lib
// if we have just one key (auth), then use numalg0 algorithm
// otherwise use numalg2 algorithm
val did = if (authPublicKeys.size == 1 && agreemPublicKeys.isEmpty() && service.isNullOrEmpty())
createPeerDIDNumalgo0(authPublicKeys[0])
else
createPeerDIDNumalgo2(
signingKeys = authPublicKeys,
encryptionKeys = agreemPublicKeys,
service = service
)
// 3. generate service
val service = serviceEndpoint?.let {
toJson(
DIDCommServicePeerDID(
id = "new-id",
type = SERVICE_DIDCOMM_MESSAGING,
serviceEndpoint = it,
routingKeys = serviceRoutingKeys,
accept = listOf("didcomm/v2")
).toDict()
)
}

// 5. set KIDs as in DID DOC for secrets and store the secret in the secrets resolver
val didDoc = DIDDocPeerDID.fromJson(resolvePeerDID(did, VerificationMaterialFormatPeerDID.JWK))
didDoc.agreementKids.zip(x25519keyPairs).forEach {
val privateKey = it.second.private.toMutableMap()
privateKey["kid"] = it.first
secretResolver.addKey(jwkToSecret(privateKey))
}
didDoc.authenticationKids.zip(ed25519keyPairs).forEach {
val privateKey = it.second.private.toMutableMap()
privateKey["kid"] = it.first
secretResolver.addKey(jwkToSecret(privateKey))
// 4. call peer DID lib
// if we have just one key (auth), then use numalg0 algorithm
// otherwise use numalg2 algorithm
val did = if (authPublicKeys.size == 1 && agreemPublicKeys.isEmpty() && service.isNullOrEmpty())
createPeerDIDNumalgo0(authPublicKeys[0])
else
createPeerDIDNumalgo2(signingKeys = authPublicKeys, encryptionKeys = agreemPublicKeys, service = service)

// 5. set KIDs as in DID DOC for secrets and store the secret in the secrets resolver
val didDoc = DIDDocPeerDID.fromJson(resolve(did, VerificationMaterialFormatPeerDID.JWK))
didDoc.agreementKids.zip(x25519keyPairs).forEach {
val privateKey = it.second.private.toMutableMap()
privateKey["kid"] = it.first
secretResolver.addKey(jwkToSecret(privateKey))
}
didDoc.authenticationKids.zip(ed25519keyPairs).forEach {
val privateKey = it.second.private.toMutableMap()
privateKey["kid"] = it.first
secretResolver.addKey(jwkToSecret(privateKey))
}
return did
}
return did
}

fun resolvePeerDID(did: String, format: VerificationMaterialFormatPeerDID) =
org.didcommx.peerdid.resolvePeerDID(did, format)
fun resolve(did: String, format: VerificationMaterialFormatPeerDID) = resolvePeerDID(did, format)

fun pack(
data: String,
to: String,
from: String? = null,
signFrom: String? = null,
protectSender: Boolean = true,
secretsResolver: SecretResolverCustom
): PackEncryptedResult {
val didComm = DIDComm(DIDDocResolverPeerDID(), secretsResolver)
val message = Message.builder(
id = UUID.randomUUID().toString(),
body = mapOf("msg" to data),
type = "my-protocol/1.0"
).build()
var builder = PackEncryptedParams
.builder(message, to)
.forward(false)
.protectSenderId(protectSender)
builder = from?.let { builder.from(it) } ?: builder
builder = signFrom?.let { builder.signFrom(it) } ?: builder
val params = builder.build()
return didComm.packEncrypted(params)
}
fun pack(
data: String,
to: String,
from: String? = null,
signFrom: String? = null,
protectSender: Boolean = true
): PackEncryptedResult {
val didComm = DIDComm(DIDDocResolverPeerDID(), secretResolver)
val message = Message.builder(
id = UUID.randomUUID().toString(),
body = mapOf("msg" to data),
type = "my-protocol/1.0"
).build()
var builder = PackEncryptedParams
.builder(message, to)
.forward(false)
.protectSenderId(protectSender)
builder = from?.let { builder.from(it) } ?: builder
builder = signFrom?.let { builder.signFrom(it) } ?: builder
val params = builder.build()

return didComm.packEncrypted(params)
}

fun unpack(packedMsg: String, secretResolver: SecretResolverCustom): UnpackResult {
val didComm = DIDComm(DIDDocResolverPeerDID(), secretResolver)
val res = didComm.unpack(UnpackParams.Builder(packedMsg).build())
val msg = res.message.body["msg"].toString()
val to = res.metadata.encryptedTo?.let { divideDIDFragment(it.first()).first() } ?: ""
val from = res.metadata.encryptedFrom?.let { divideDIDFragment(it).first() }
return UnpackResult(
message = msg,
from = from, to = to, res = res
)
fun unpack(packedMsg: String): UnpackResult {
val didComm = DIDComm(DIDDocResolverPeerDID(), secretResolver)
val res = didComm.unpack(UnpackParams.Builder(packedMsg).build())
val msg = res.message.body["msg"].toString()
val to = res.metadata.encryptedTo?.let { divideDIDFragment(it.first()).first() } ?: ""
val from = res.metadata.encryptedFrom?.let { divideDIDFragment(it).first() }

return UnpackResult(message = msg, from = from, to = to, res = res)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.rootsid.wal.library.didcom.model

import kotlinx.serialization.Contextual

interface DidComSecret {
val _id: String
val secret: Map<String, @Contextual Any>
}



Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.rootsid.wal.library.didcom.storage

import com.rootsid.wal.library.didcom.model.DidComSecret

interface DidComSecretStorage {
fun insert(kid: String, secretJson: Map<String, Any>): DidComSecret

fun findById(kid: String): DidComSecret

fun findIdsIn(kids: List<String>): Set<String>

fun listIds(): List<String>

fun list(): List<DidComSecret>
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,19 @@ import org.didcommx.didcomm.secret.Secret
import org.didcommx.didcomm.secret.SecretResolverEditable
import org.didcommx.didcomm.secret.jwkToSecret
import org.didcommx.didcomm.secret.secretToJwk
import org.didcommx.didcomm.utils.fromJsonToList
import org.didcommx.didcomm.utils.toJson
import java.io.File
import java.util.*
import kotlin.io.path.Path
import kotlin.io.path.exists

class SecretResolverCustom(private val filePath: String = "secrets.json") : SecretResolverEditable {

private val secrets: MutableMap<String, Secret>

init {
if (!Path(filePath).exists()) {
secrets = mutableMapOf()
save()
} else {
val secretsJson = File(filePath).readText()
secrets = if (secretsJson.isNotEmpty()) {
fromJsonToList(secretsJson).map { jwkToSecret(it) }.associate { it.kid to it }.toMutableMap()
} else {
mutableMapOf()
}
}
}

private fun save() {
val secretJson = toJson(secrets.values.map { secretToJwk(it) })
File(filePath).writeText(secretJson)
}
class SecretResolverCustom(private val didComSecretStorage: DidComSecretStorage = SecretResolverFileSystemStorage()) :
SecretResolverEditable {

override fun addKey(secret: Secret) {
secrets.put(secret.kid, secret)
save()
didComSecretStorage.insert(secret.kid, secretToJwk(secret))
}

override fun getKids(): List<String> =
secrets.keys.toList()

override fun findKey(kid: String): Optional<Secret> =
Optional.ofNullable(secrets.get(kid))
Optional.of(jwkToSecret(didComSecretStorage.findById(kid).secret))

override fun getKids(): List<String> = didComSecretStorage.listIds()

override fun findKeys(kids: List<String>): Set<String> =
kids.intersect(secrets.keys)
override fun findKeys(kids: List<String>): Set<String> = didComSecretStorage.findIdsIn(kids)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.rootsid.wal.library.didcom.storage

//import com.rootsid.wal.library.didcom.model.createAnonymousObj
import com.rootsid.wal.library.didcom.model.DidComSecret
import kotlinx.serialization.Contextual
import org.didcommx.didcomm.secret.Secret
import org.didcommx.didcomm.secret.jwkToSecret
import org.didcommx.didcomm.secret.secretToJwk
import org.didcommx.didcomm.utils.fromJsonToList
import org.didcommx.didcomm.utils.toJson
import java.io.File
import kotlin.collections.set
import kotlin.io.path.Path
import kotlin.io.path.exists

class SecretResolverFileSystemStorage(private val filePath: String = "secrets.json") : DidComSecretStorage {
private val secrets: MutableMap<String, Secret>

init {
if (!Path(filePath).exists()) {
secrets = mutableMapOf()
save()
} else {
val secretsJson = File(filePath).readText()
secrets = if (secretsJson.isNotEmpty()) {
fromJsonToList(secretsJson).map { jwkToSecret(it) }.associate { it.kid to it }.toMutableMap()
} else {
mutableMapOf()
}
}
}

override fun insert(kid: String, secretJson: Map<String, Any>): DidComSecret {
secrets[kid] = jwkToSecret(secretJson)
save()

return createAnonymousDidComSecret(kid, secretToJwk(secrets[kid]!!))
}

private fun save() {
val secretJson = toJson(secrets.values.map { secretToJwk(it) })
File(filePath).writeText(secretJson)
}

private fun createAnonymousDidComSecret(id: String, secret: Map<String, @Contextual Any>): DidComSecret =
object : DidComSecret {
override val _id: String
get() = id
override val secret: Map<String, Any>
get() = secret
}

override fun findById(kid: String): DidComSecret {
secrets[kid]?.let {
return createAnonymousDidComSecret(kid, secretToJwk(it))
}

throw RuntimeException("Secret '$kid' not found.")
}

override fun findIdsIn(kids: List<String>): Set<String> = kids.intersect(secrets.keys)

override fun listIds(): List<String> = secrets.keys.toList()

override fun list(): List<DidComSecret> = secrets.entries.map { createAnonymousDidComSecret(it.key, secretToJwk(it.value)) }
}
Loading

0 comments on commit 6177166

Please sign in to comment.