diff --git a/build.gradle.kts b/build.gradle.kts index c8c6844..9968e97 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,6 @@ plugins { kotlin("jvm") version "1.6.10" + kotlin("plugin.serialization") version "1.6.10" id("maven-publish") } @@ -23,6 +24,8 @@ repositories { dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib:1.6.10") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2") + implementation("org.litote.kmongo:kmongo:4.4.0") // needed for cryptography primitives implementation diff --git a/src/main/kotlin/com/rootsid/wal/library/DLT.kt b/src/main/kotlin/com/rootsid/wal/library/DLT.kt index 4b3f699..d01b4ba 100644 --- a/src/main/kotlin/com/rootsid/wal/library/DLT.kt +++ b/src/main/kotlin/com/rootsid/wal/library/DLT.kt @@ -70,13 +70,13 @@ private fun waitUntilConfirmed(nodePublicApi: NodePublicApi, operationId: AtalaO /** * Derive key pair * - * @param keyPaths + * @param keyPairs * @param seed * @param keyId * @return */ -private fun deriveKeyPair(keyPaths: MutableList, seed: ByteArray, keyId: String): ECKeyPair { - val keyPathList = keyPaths.filter { it.keyId == keyId } +private fun deriveKeyPair(keyPairs: MutableList, seed: ByteArray, keyId: String): ECKeyPair { + val keyPathList = keyPairs.filter { it.keyId == keyId } if (keyPathList.isNotEmpty()) { val keyPath = keyPathList[0] return KeyGenerator.deriveKeyFromFullPath(seed, keyPath.didIdx, keyPath.keyType, keyPath.keyIdx) @@ -119,28 +119,46 @@ fun newWallet(name: String, mnemonic: String, passphrase: String): Wallet { fun newDid(wallet: Wallet, didAlias: String, issuer: Boolean): Wallet { // To keep DID index sequential val didIdx = wallet.dids.size - val keyPaths = mutableListOf() - val masterKeyPathData = KeyPath(PrismDid.DEFAULT_MASTER_KEY_ID, didIdx, PrismKeyType.MASTER_KEY, 0) - + val keyPairs = mutableListOf() val seed = KeyDerivation.binarySeed(MnemonicCode(wallet.mnemonic), wallet.passphrase) val masterKeyPair = KeyGenerator.deriveKeyFromFullPath( - seed, masterKeyPathData.didIdx, masterKeyPathData.keyType, masterKeyPathData.keyIdx + seed, didIdx, PrismKeyType.MASTER_KEY, 0 + ) + val masterKeyPairData = KeyPair( + PrismDid.DEFAULT_MASTER_KEY_ID, + didIdx, + PrismKeyType.MASTER_KEY, + 0, + masterKeyPair.privateKey.getHexEncoded(), + masterKeyPair.publicKey.getHexEncoded() ) - keyPaths.add(masterKeyPathData) + keyPairs.add(masterKeyPairData) val unpublishedDid = if (issuer) { - val issuingKeyPathData = KeyPath(PrismDid.DEFAULT_ISSUING_KEY_ID, didIdx, PrismKeyType.ISSUING_KEY, 0) - val revocationKeyPathData = KeyPath(PrismDid.DEFAULT_REVOCATION_KEY_ID, didIdx, PrismKeyType.REVOCATION_KEY, 0) - - keyPaths.add(issuingKeyPathData) - keyPaths.add(revocationKeyPathData) - val issuingKeyPair = KeyGenerator.deriveKeyFromFullPath( - seed, issuingKeyPathData.didIdx, issuingKeyPathData.keyType, issuingKeyPathData.keyIdx + seed, didIdx, PrismKeyType.ISSUING_KEY, 0 ) val revocationKeyPair = KeyGenerator.deriveKeyFromFullPath( - seed, revocationKeyPathData.didIdx, revocationKeyPathData.keyType, revocationKeyPathData.keyIdx + seed, didIdx, PrismKeyType.REVOCATION_KEY, 0 ) + val issuingKeyPairData = KeyPair( + PrismDid.DEFAULT_ISSUING_KEY_ID, + didIdx, + PrismKeyType.ISSUING_KEY, + 0, + issuingKeyPair.privateKey.getHexEncoded(), + issuingKeyPair.publicKey.getHexEncoded() + ) + val revocationKeyPairData = KeyPair( + PrismDid.DEFAULT_REVOCATION_KEY_ID, + didIdx, PrismKeyType.REVOCATION_KEY, + 0, + revocationKeyPair.privateKey.getHexEncoded(), + revocationKeyPair.publicKey.getHexEncoded() + ) + keyPairs.add(issuingKeyPairData) + keyPairs.add(revocationKeyPairData) + PrismDid.buildExperimentalLongFormFromKeys( masterKeyPair.publicKey, issuingKeyPair.publicKey, revocationKeyPair.publicKey ) @@ -148,7 +166,7 @@ fun newDid(wallet: Wallet, didAlias: String, issuer: Boolean): Wallet { PrismDid.buildLongFormFromMasterPublicKey(masterKeyPair.publicKey) } wallet.dids.add( - DID(didAlias, didIdx, unpublishedDid.asCanonical().did.toString(), unpublishedDid.did.toString(), "", keyPaths) + DID(didAlias, didIdx, unpublishedDid.asCanonical().did.toString(), unpublishedDid.did.toString(), "", keyPairs) ) return wallet } @@ -199,9 +217,9 @@ fun publishDid(wallet: Wallet, didAlias: String): Wallet { val prismDid = PrismDid.fromString(did.uriLongForm) // Key pairs to get private keys val seed = KeyDerivation.binarySeed(MnemonicCode(wallet.mnemonic), wallet.passphrase) - val masterKeyPair = deriveKeyPair(did.keyPaths, seed, PrismDid.DEFAULT_MASTER_KEY_ID) - val issuingKeyPair = deriveKeyPair(did.keyPaths, seed, PrismDid.DEFAULT_ISSUING_KEY_ID) - val revocationKeyPair = deriveKeyPair(did.keyPaths, seed, PrismDid.DEFAULT_REVOCATION_KEY_ID) + val masterKeyPair = deriveKeyPair(did.keyPairs, seed, PrismDid.DEFAULT_MASTER_KEY_ID) + val issuingKeyPair = deriveKeyPair(did.keyPairs, seed, PrismDid.DEFAULT_ISSUING_KEY_ID) + val revocationKeyPair = deriveKeyPair(did.keyPairs, seed, PrismDid.DEFAULT_REVOCATION_KEY_ID) val nodePayloadGenerator = NodePayloadGenerator( prismDid as LongFormPrismDid, @@ -250,7 +268,7 @@ fun issueCredential(wallet: Wallet, didAlias: String, credential: Credential): P val claims = mutableListOf() // Key pairs to get private keys val seed = KeyDerivation.binarySeed(MnemonicCode(wallet.mnemonic), wallet.passphrase) - val issuingKeyPair = deriveKeyPair(issuerDid.keyPaths, seed, PrismDid.DEFAULT_ISSUING_KEY_ID) + val issuingKeyPair = deriveKeyPair(issuerDid.keyPairs, seed, PrismDid.DEFAULT_ISSUING_KEY_ID) claims.add(credential.claim.toCredentialClaim()) @@ -300,7 +318,7 @@ fun revokeCredential(wallet: Wallet, didAlias: String, credential: Credential) { val nodeAuthApi = NodeAuthApiImpl(GrpcConfig.options) // Key pairs to get private keys val seed = KeyDerivation.binarySeed(MnemonicCode(wallet.mnemonic), wallet.passphrase) - val revocationKeyPair = deriveKeyPair(issuerDid.keyPaths, seed, PrismDid.DEFAULT_REVOCATION_KEY_ID) + val revocationKeyPair = deriveKeyPair(issuerDid.keyPairs, seed, PrismDid.DEFAULT_REVOCATION_KEY_ID) val nodePayloadGenerator = NodePayloadGenerator( PrismDid.fromString(issuerDid.uriLongForm) as LongFormPrismDid, diff --git a/src/main/kotlin/com/rootsid/wal/library/Model.kt b/src/main/kotlin/com/rootsid/wal/library/Model.kt index d001bc2..6e00edc 100644 --- a/src/main/kotlin/com/rootsid/wal/library/Model.kt +++ b/src/main/kotlin/com/rootsid/wal/library/Model.kt @@ -3,6 +3,7 @@ package com.rootsid.wal.library import io.iohk.atala.prism.api.CredentialClaim import io.iohk.atala.prism.identity.PrismDid import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json @@ -15,6 +16,7 @@ import kotlinx.serialization.json.Json * @property dids * @constructor Create empty Wallet */ +@Serializable data class Wallet( val _id: String, // name val mnemonic: List, @@ -30,16 +32,17 @@ data class Wallet( * @property uriCanonical * @property uriLongForm * @property operationHash - * @property keyPaths + * @property keyPairs * @constructor Create empty D i d */ +@Serializable data class DID( val alias: String, val didIdx: Int, val uriCanonical: String, val uriLongForm: String, var operationHash: String = "", - var keyPaths: MutableList = mutableListOf() + var keyPairs: MutableList = mutableListOf() ) /** @@ -51,11 +54,14 @@ data class DID( * @property keyIdx * @constructor Create empty Key path */ -data class KeyPath( +@Serializable +data class KeyPair( val keyId: String, val didIdx: Int, val keyType: Int, - val keyIdx: Int + val keyIdx: Int, + val privateKey: String, + val publicKey: String ) /** @@ -65,6 +71,7 @@ data class KeyPath( * @property proof * @constructor Create empty Verified credential */ +@Serializable data class VerifiedCredential( val encodedSignedCredential: String, val proof: String @@ -77,6 +84,7 @@ data class VerifiedCredential( * @property content * @constructor Create empty Claim */ +@Serializable data class Claim( val subjectDid: String, val content: String @@ -101,11 +109,12 @@ fun Claim.toCredentialClaim() = CredentialClaim( * @property verifiedCredential * @constructor Create empty Credential */ +@Serializable data class Credential( val _id: String, // Plain json claim val claim: Claim, - // Signed VC and proof + // Signed VC and proof (This is the real VC) var verifiedCredential: VerifiedCredential, // Required for revocation var batchId: String, diff --git a/src/main/kotlin/com/rootsid/wal/library/Storage.kt b/src/main/kotlin/com/rootsid/wal/library/Storage.kt index e56cd0b..df208f1 100644 --- a/src/main/kotlin/com/rootsid/wal/library/Storage.kt +++ b/src/main/kotlin/com/rootsid/wal/library/Storage.kt @@ -1,12 +1,7 @@ package com.rootsid.wal.library import com.mongodb.client.MongoDatabase -import org.litote.kmongo.KMongo -import org.litote.kmongo.MongoOperator -import org.litote.kmongo.eq -import org.litote.kmongo.findOne -import org.litote.kmongo.getCollection -import org.litote.kmongo.updateOne +import org.litote.kmongo.* /** * Open db @@ -104,6 +99,6 @@ fun didAliasExists(db: MongoDatabase, walletName: String, didAlias: String): Boo */ fun updateWallet(db: MongoDatabase, wallet: Wallet): Boolean { val collection = db.getCollection("wallet") - val result = collection.updateOne(wallet) + val result = collection.updateOne(wallet, upsert()) return result.wasAcknowledged() }