Skip to content

Commit

Permalink
fix: restore process duplicates did peers (#173)
Browse files Browse the repository at this point in the history
fix: restore process from swift/ts jwe
Signed-off-by: Cristian G <cristian.castro@iohk.io>

fix: linting
Signed-off-by: Cristian G <cristian.castro@iohk.io>
  • Loading branch information
cristianIOHK committed Jul 16, 2024
1 parent 82b03cc commit f654937
Show file tree
Hide file tree
Showing 11 changed files with 267 additions and 65 deletions.
2 changes: 2 additions & 0 deletions edge-agent-sdk/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ kotlin {
implementation("com.apicatalog:titanium-json-ld-jre8:1.4.0")
implementation("org.glassfish:jakarta.json:2.0.1")
implementation("io.setl:rdf-urdna:1.3")

implementation("app.cash.sqldelight:sqlite-driver:2.0.1")
}
}
val commonTest by getting {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -386,5 +386,13 @@ interface Pluto {

fun getAllKeysForBackUp(): Flow<List<BackupV0_0_1.Key>>

/**
* Retrieves a list of all private keys.
*
* @return A flow that emits a list of nullable [PrivateKey] objects. In case a private key is not found, null is emitted.
*/
fun getAllPrivateKeys(): Flow<List<PrivateKey?>>


suspend fun start(context: Any? = null)
}
Original file line number Diff line number Diff line change
Expand Up @@ -1278,7 +1278,7 @@ class EdgeAgent {
val backupObject = Json.decodeFromString<BackupV0_0_1>(json)

// 7. Restore the pluto instance
val restoreTask = PlutoRestoreTask(pluto, backupObject)
val restoreTask = PlutoRestoreTask(castor, pluto, backupObject)
restoreTask.run()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.hyperledger.identus.walletsdk.pluto

import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
import org.hyperledger.identus.walletsdk.SdkPlutoDb
import org.hyperledger.identus.walletsdk.pluto.data.DbConnection

class DbConnectionInMemory : DbConnection {
override var driver: SqlDriver? = null

override suspend fun connectDb(context: Any?): SqlDriver {
val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)
SdkPlutoDb.Schema.create(driver)
this.driver = driver
return driver
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1235,4 +1235,33 @@ class PlutoImpl(private val connection: DbConnection) : Pluto {
val keys = keysWithDID + keysWithNoDID
return flowOf(keys)
}

override fun getAllPrivateKeys(): Flow<List<PrivateKey?>> {
return getInstance().privateKeyQueries
.fetchAllPrivateKeys()
.asFlow()
.map {
it.executeAsList()
.map { storableKey ->
when (storableKey.restorationIdentifier) {
"secp256k1+priv" -> {
Secp256k1PrivateKey(storableKey.data_.base64UrlDecodedBytes)
}

"ed25519+priv" -> {
Ed25519PrivateKey(storableKey.data_.base64UrlDecodedBytes)
}

"x25519+priv" -> {
X25519PrivateKey(storableKey.data_.base64UrlDecodedBytes)
}

else -> {
throw PlutoError.InvalidRestorationIdentifier()
}
}
}
}
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
@file:Suppress("ktlint:standard:import-ordering")

package org.hyperledger.identus.walletsdk.pluto

import java.util.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.serialization.EncodeDefault
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
Expand All @@ -22,21 +28,24 @@ import org.hyperledger.identus.apollo.base64.base64UrlDecodedBytes
import org.hyperledger.identus.walletsdk.apollo.utils.Ed25519PrivateKey
import org.hyperledger.identus.walletsdk.apollo.utils.Secp256k1PrivateKey
import org.hyperledger.identus.walletsdk.apollo.utils.X25519PrivateKey
import org.hyperledger.identus.walletsdk.domain.buildingblocks.Castor
import org.hyperledger.identus.walletsdk.domain.buildingblocks.Pluto
import org.hyperledger.identus.walletsdk.domain.models.AttachmentDescriptor
import org.hyperledger.identus.walletsdk.domain.models.Curve
import org.hyperledger.identus.walletsdk.domain.models.DID
import org.hyperledger.identus.walletsdk.domain.models.DIDDocument
import org.hyperledger.identus.walletsdk.domain.models.Message
import org.hyperledger.identus.walletsdk.domain.models.UnknownError
import org.hyperledger.identus.walletsdk.domain.models.keyManagement.IndexKey
import org.hyperledger.identus.walletsdk.domain.models.keyManagement.JWK
import org.hyperledger.identus.walletsdk.domain.models.keyManagement.PrivateKey
import org.hyperledger.identus.walletsdk.domain.models.keyManagement.StorableKey
import org.hyperledger.identus.walletsdk.pluto.PlutoRestoreTask.BackUpMessage.JsonAsStringSerializer.descriptor
import org.hyperledger.identus.walletsdk.pluto.PlutoRestoreTask.BackUpMessage.JsonAsStringSerializer.deserialize
import org.hyperledger.identus.walletsdk.pluto.PlutoRestoreTask.BackUpMessage.JsonAsStringSerializer.serialize
import org.hyperledger.identus.walletsdk.pluto.backup.models.BackupV0_0_1
import org.hyperledger.identus.walletsdk.pollux.models.AnonCredential
import org.hyperledger.identus.walletsdk.pollux.models.JWTCredential
import java.util.*
import kotlin.jvm.Throws
import kotlin.time.DurationUnit
import kotlin.time.toDuration

Expand All @@ -47,6 +56,7 @@ import kotlin.time.toDuration
* @param backup The Pluto backup object containing the data to be restored.
*/
open class PlutoRestoreTask(
private val castor: Castor,
private val pluto: Pluto,
private val backup: BackupV0_0_1
) {
Expand All @@ -56,13 +66,15 @@ open class PlutoRestoreTask(
* This method should be called to initialize or restore the necessary components.
*/
fun run() {
restoreCredentials()
restoreDids()
restoreDidPairs()
restoreKeys()
restoreLinkSecret()
restoreMessages()
restoreMediators()
CoroutineScope(Dispatchers.Default).launch {
restoreCredentials()
restoreDidPairs()
restoreKeys()
restoreLinkSecret()
restoreMessages()
restoreMediators()
restoreDids()
}
}

/**
Expand Down Expand Up @@ -136,7 +148,7 @@ open class PlutoRestoreTask(
*
* @throws UnknownError.SomethingWentWrongError if the key is invalid, it does not have index or DID
*/
private fun restoreKeys() {
private suspend fun restoreKeys() {
this.backup.keys.map {
val jwkJson = it.key.base64UrlDecoded
val jwk = Json.decodeFromString<JWK>(jwkJson)
Expand Down Expand Up @@ -169,14 +181,20 @@ open class PlutoRestoreTask(
}.forEach {
if (it.third is DID) {
if (it.third.toString().contains("peer")) {
val metaId = (it.third as DID).toString()
pluto.storePrivateKeys(
it.first as StorableKey,
it.third as DID,
(it.first.keySpecification[IndexKey().property])?.toInt(),
metaId
)
pluto.storePeerDID(it.third as DID)
val did = (it.third as DID)
val keyId = did.toString()
if (keyId.contains("#")) {
pluto.storePrivateKeys(
it.first as StorableKey,
did,
(it.first.keySpecification[IndexKey().property])?.toInt(),
keyId
)
} else {
// This else is for jwe coming from TS/Swift which do not use didUrl for private key id
// and is a requirement for us.
resolveDIDForPrivateKey(keyId, it.first)
}
} else {
pluto.storePrismDIDAndPrivateKeys(
it.third as DID,
Expand All @@ -191,6 +209,35 @@ open class PlutoRestoreTask(
}
}

private suspend fun resolveDIDForPrivateKey(did: String, privateKey: PrivateKey) {
val document = castor.resolveDID(did)
val listOfVerificationMethods: MutableList<DIDDocument.VerificationMethod> =
mutableListOf()
document.coreProperties.forEach {
if (it is DIDDocument.Authentication) {
listOfVerificationMethods.addAll(it.verificationMethods)
}
if (it is DIDDocument.KeyAgreement) {
listOfVerificationMethods.addAll(it.verificationMethods)
}
}
val verificationMethods =
DIDDocument.VerificationMethods(listOfVerificationMethods.toTypedArray())

verificationMethods.values.forEach {
if (it.type.contains("X25519") && privateKey is X25519PrivateKey ||
it.type.contains("Ed25519") && privateKey is Ed25519PrivateKey
) {
pluto.storePrivateKeys(
privateKey as StorableKey,
DID(did),
(privateKey.keySpecification[IndexKey().property])?.toInt(),
it.id.toString()
)
}
}
}

/**
* Restores the link secret stored in the backup.
* If the backup object has a link secret, it is decoded from base64 URL encoding and stored using the pluto.storeLinkSecret method.
Expand Down Expand Up @@ -271,14 +318,13 @@ open class PlutoRestoreTask(
* @throws IndexOutOfBoundsException If the input string is not in the required format `x:y:z`
*/
@Throws(IndexOutOfBoundsException::class)
private fun String.toDID(alias: String? = null): DID {
return DID(
private fun String.toDID(alias: String? = null): DID =
DID(
DID.getSchemaFromString(this),
DID.getMethodFromString(this),
DID.getMethodIdFromString(this),
alias
)
}

/**
* Represents the various types of backup restoration IDs that can be used.
Expand All @@ -295,13 +341,12 @@ open class PlutoRestoreTask(
*
* @return The corresponding RestorationID object based on the current BackUpRestorationId.
*/
fun toRestorationId(): RestorationID {
return when (this) {
fun toRestorationId(): RestorationID =
when (this) {
JWT -> RestorationID.JWT
ANONCRED -> RestorationID.ANONCRED
W3C -> RestorationID.W3C
}
}
}

/**
Expand Down Expand Up @@ -520,8 +565,8 @@ open class PlutoRestoreTask(
*
* @return The converted Message object.
*/
fun toMessage(): Message {
return Message(
fun toMessage(): Message =
Message(
id,
piuri,
from,
Expand All @@ -537,7 +582,6 @@ open class PlutoRestoreTask(
ack,
direction
)
}

/**
* Serializer for the [Message.Direction] enum class.
Expand Down Expand Up @@ -571,9 +615,8 @@ open class PlutoRestoreTask(
* @param decoder The decoder used for deserialization.
* @return The deserialized Message.Direction value.
*/
override fun deserialize(decoder: Decoder): Message.Direction {
return Message.Direction.fromValue(decoder.decodeInt())
}
override fun deserialize(decoder: Decoder): Message.Direction =
Message.Direction.fromValue(decoder.decodeInt())
}

/**
Expand Down Expand Up @@ -722,9 +765,8 @@ open class PlutoRestoreTask(
* @param decoder The Decoder object used for deserialization.
* @return A new instance of DID.
*/
override fun deserialize(decoder: Decoder): DID {
return DID(decoder.decodeString())
}
override fun deserialize(decoder: Decoder): DID =
DID(decoder.decodeString())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -273,4 +273,8 @@ class PlutoMock : Pluto {
override suspend fun start(context: Any?) {
TODO("Not yet implemented")
}

override fun getAllPrivateKeys(): Flow<List<PrivateKey?>> {
TODO("Not yet implemented")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -216,4 +216,8 @@ class PlutoMock : Pluto {
override suspend fun start(context: Any?) {
TODO("Not yet implemented")
}

override fun getAllPrivateKeys(): Flow<List<PrivateKey?>> {
TODO("Not yet implemented")
}
}
Loading

0 comments on commit f654937

Please sign in to comment.