Skip to content

Commit

Permalink
test: add tests for the new key types (#1044)
Browse files Browse the repository at this point in the history
Signed-off-by: Pat Losoponkul <pat.losoponkul@iohk.io>
  • Loading branch information
patlo-iog authored May 16, 2024
1 parent 998a13a commit f5f2d75
Show file tree
Hide file tree
Showing 28 changed files with 403 additions and 102 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import scala.language.implicitConversions
import zio.*
import zio.test.*
import zio.test.Assertion.*
import org.hyperledger.identus.castor.core.model.ProtoModelHelper

object ProtoModelHelperSpec extends ZIOSpecDefault {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import zio.*
import zio.test.*
import zio.test.Assertion.*
import org.hyperledger.identus.castor.core.util.GenUtils
import org.hyperledger.identus.castor.core.model.did.{DID, PrismDID}

object DIDSpec extends ZIOSpecDefault {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import org.hyperledger.identus.shared.models.Base64UrlString
import zio.*
import zio.test.*
import zio.test.Assertion.*
import org.hyperledger.identus.castor.core.model.did.PrismDID

object PrismDIDSpec extends ZIOSpecDefault {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package org.hyperledger.identus.castor.core.model.did
import zio.*
import zio.test.*
import zio.test.Assertion.*
import org.hyperledger.identus.castor.core.model.did.ServiceEndpoint

object ServiceEndpointSpec extends ZIOSpecDefault {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package org.hyperledger.identus.castor.core.model.did
import zio.*
import zio.test.*
import zio.test.Assertion.*
import org.hyperledger.identus.castor.core.model.did.ServiceType

object ServiceTypeSpec extends ZIOSpecDefault {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import org.hyperledger.identus.castor.core.util.GenUtils
import zio.*
import zio.test.*
import zio.test.Assertion.*
import org.hyperledger.identus.castor.core.model.did.w3c.W3CModelHelper

object W3CModelHelperSpec extends ZIOSpecDefault {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import zio.test.*
import zio.test.Assertion.*

import scala.concurrent.Future
import org.hyperledger.identus.castor.core.service.{DIDService, DIDServiceImpl}

object DIDServiceSpec extends ZIOSpecDefault {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ object AppConfig {
val urlRegex = """^(http|https)://[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*(:[0-9]{1,5})?(/.*)?$""".r
urlRegex.findFirstMatchIn(url) match
case Some(_) =>
Try(java.net.URL(url)).toEither.left.map(ex /*java.net.MalformedURLException*/ =>
Try(java.net.URI(url).toURL()).toEither.left.map(ex /*java.net.MalformedURLException*/ =>
Config.Error.InvalidData(zio.Chunk.empty, ex.getMessage())
)
case _ => Left(Config.Error.InvalidData(zio.Chunk.empty, s"Invalid URL: $url"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import org.hyperledger.identus.iam.wallet.http.controller.WalletManagementContro
import org.hyperledger.identus.shared.models.WalletAccessContext
import zio.*

import java.net.URL
import java.net.URI
import scala.language.implicitConversions
import java.util.UUID

Expand Down Expand Up @@ -44,7 +44,9 @@ class EventControllerImpl(service: WalletManagementService) extends EventControl
request: CreateWebhookNotification
)(implicit rc: RequestContext): ZIO[WalletAccessContext, ErrorResponse, WebhookNotification] = {
for {
url <- ZIO.attempt(new URL(request.url)).mapError(e => ErrorResponse.badRequest(detail = Some(e.toString())))
url <- ZIO
.attempt(new URI(request.url).toURL())
.mapError(e => ErrorResponse.badRequest(detail = Some(e.toString())))
notificationConfig <- EventNotificationConfig.applyWallet(url, request.customHeaders.getOrElse(Map.empty))
_ <- service
.createWalletNotification(notificationConfig)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import zio.test.*
import zio.test.Assertion.*
import zio.test.ZIOSpecDefault

import java.net.URI
import java.net.URL

object AgentInitializationSpec extends ZIOSpecDefault, PostgresTestContainerSupport, ApolloSpecHelper {
Expand Down Expand Up @@ -113,7 +114,7 @@ object AgentInitializationSpec extends ZIOSpecDefault, PostgresTestContainerSupp
test("create wallet with provided webhook") {
val url = "http://example.com"
for {
_ <- AgentInitialization.run.overrideConfig(webhookUrl = Some(URL(url)))
_ <- AgentInitialization.run.overrideConfig(webhookUrl = Some(URI(url).toURL()))
webhooks <- ZIO
.serviceWithZIO[WalletNonSecretStorage](
_.walletNotification
Expand All @@ -127,7 +128,7 @@ object AgentInitializationSpec extends ZIOSpecDefault, PostgresTestContainerSupp
val url = "http://example.com"
val apiKey = "secret"
for {
_ <- AgentInitialization.run.overrideConfig(webhookUrl = Some(URL(url)), webhookApiKey = Some(apiKey))
_ <- AgentInitialization.run.overrideConfig(webhookUrl = Some(URI(url).toURL()), webhookApiKey = Some(apiKey))
webhooks <- ZIO
.serviceWithZIO[WalletNonSecretStorage](
_.walletNotification
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,32 +242,6 @@ class JdbcDIDNonSecretStorage(xa: Transactor[ContextAwareTask], xb: Transactor[T
}
}

override def listHdKeyPath(
did: PrismDID
): RIO[WalletAccessContext, Seq[(String, ArraySeq[Byte], ManagedDIDHdKeyPath)]] = {
val cxnIO =
sql"""
| SELECT
| key_id,
| operation_hash,
| key_usage,
| key_index
| FROM public.prism_did_key
| WHERE did = $did AND key_mode = ${KeyManagementMode.HD}
""".stripMargin
.query[(String, ArraySeq[Byte], VerificationRelationship | InternalKeyPurpose, Int)]
.to[List]

for {
state <- getManagedDIDState(did)
paths <- cxnIO.transactWallet(xa)
} yield state.map(_.didIndex).fold(Nil) { didIndex =>
paths.map { (keyId, operationHash, keyUsage, keyIndex) =>
(keyId, operationHash, ManagedDIDHdKeyPath(didIndex, keyUsage, keyIndex))
}
}
}

override def insertKeyMeta(
did: PrismDID,
keyId: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import zio.json.*
import zio.json.ast.Json
import zio.json.ast.Json.*

import java.net.URI
import java.net.URL
import java.time.Instant
import java.util.UUID
Expand Down Expand Up @@ -123,7 +124,7 @@ package object sql {
given arraySeqByteGet: Get[ArraySeq[Byte]] = Get[Array[Byte]].map(ArraySeq.from)
given arraySeqBytePut: Put[ArraySeq[Byte]] = Put[Array[Byte]].contramap(_.toArray)

given urlGet: Get[URL] = Get[String].map(URL(_))
given urlGet: Get[URL] = Get[String].map(URI(_).toURL())
given urlPut: Put[URL] = Put[String].contramap(_.toString())

given octetKeyPairGet: Get[OctetKeyPair] = Get[String].map(OctetKeyPair.parse)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import org.hyperledger.identus.mercury.model.DidId
import org.hyperledger.identus.shared.models.WalletAccessContext
import zio.*

import scala.collection.immutable.ArraySeq

trait DIDNonSecretStorage {

def getManagedDIDState(did: PrismDID): RIO[WalletAccessContext, Option[ManagedDIDState]]
Expand All @@ -25,6 +23,7 @@ trait DIDNonSecretStorage {

def getHdKeyCounter(did: PrismDID): RIO[WalletAccessContext, Option[HdKeyIndexCounter]]

/** Return a tuple of key metadata and the operation hash */
def getKeyMeta(did: PrismDID, keyId: String): RIO[WalletAccessContext, Option[(ManagedDIDKeyMeta, Array[Byte])]]

def insertKeyMeta(
Expand All @@ -34,8 +33,6 @@ trait DIDNonSecretStorage {
operationHash: Array[Byte]
): RIO[WalletAccessContext, Unit]

def listHdKeyPath(did: PrismDID): RIO[WalletAccessContext, Seq[(String, ArraySeq[Byte], ManagedDIDHdKeyPath)]]

/** Return a list of Managed DID as well as a count of all filtered items */
def listManagedDID(
offset: Option[Int],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@ package org.hyperledger.identus.agent.walletapi.util

import org.hyperledger.identus.agent.walletapi.model.UpdateManagedDIDAction
import org.hyperledger.identus.agent.walletapi.service.ManagedDIDService
import org.hyperledger.identus.castor.core.model.did.EllipticCurve
import org.hyperledger.identus.castor.core.model.did.VerificationRelationship

object UpdateManagedDIDActionValidator {

def validate(actions: Seq[UpdateManagedDIDAction]): Either[String, Unit] = validateReservedKeyId(actions)
def validate(actions: Seq[UpdateManagedDIDAction]): Either[String, Unit] =
for {
_ <- validateReservedKeyId(actions)
_ <- validateCurveUsage(actions)
} yield ()

private def validateReservedKeyId(actions: Seq[UpdateManagedDIDAction]): Either[String, Unit] = {
val keyIds = actions.flatMap {
Expand All @@ -22,4 +28,27 @@ object UpdateManagedDIDActionValidator {
else Right(())
}

private def validateCurveUsage(actions: Seq[UpdateManagedDIDAction]): Either[String, Unit] = {
val ed25519AllowedUsage = Set(VerificationRelationship.Authentication, VerificationRelationship.AssertionMethod)
val x25519AllowedUsage = Set(VerificationRelationship.KeyAgreement)
val publicKeys = actions.collect { case UpdateManagedDIDAction.AddKey(template) => template }
val disallowedKeys = publicKeys
.filter { k =>
k.curve match {
case EllipticCurve.ED25519 => !ed25519AllowedUsage.contains(k.purpose)
case EllipticCurve.X25519 => !x25519AllowedUsage.contains(k.purpose)
case _ => false
}
}
.map(_.id)

if (disallowedKeys.isEmpty) Right(())
else
Left(
s"Invalid key purpose for key ${disallowedKeys.mkString("[", ", ", "]")}. " +
s"Ed25519 must be used in ${ed25519AllowedUsage.mkString("[", ", ", "]")}. " +
s"X25519 must be used in ${x25519AllowedUsage.mkString("[", ", ", "]")}"
)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import org.hyperledger.identus.castor.core.model.error
import org.hyperledger.identus.castor.core.service.DIDService
import org.hyperledger.identus.castor.core.util.DIDOperationValidator
import org.hyperledger.identus.shared.crypto.ApolloSpecHelper
import org.hyperledger.identus.shared.crypto.Ed25519KeyPair
import org.hyperledger.identus.shared.crypto.Secp256k1KeyPair
import org.hyperledger.identus.shared.crypto.X25519KeyPair
import org.hyperledger.identus.shared.models.WalletAccessContext
import org.hyperledger.identus.shared.models.WalletAdministrationContext
import org.hyperledger.identus.sharedtest.containers.PostgresTestContainerSupport
Expand Down Expand Up @@ -239,14 +242,34 @@ object ManagedDIDServiceSpec
val template = generateDIDTemplate(
publicKeys = Seq(
DIDPublicKeyTemplate("key1", VerificationRelationship.Authentication, EllipticCurve.SECP256K1),
DIDPublicKeyTemplate("key2", VerificationRelationship.KeyAgreement, EllipticCurve.SECP256K1)
DIDPublicKeyTemplate("key2", VerificationRelationship.Authentication, EllipticCurve.SECP256K1),
DIDPublicKeyTemplate("key3", VerificationRelationship.AssertionMethod, EllipticCurve.ED25519),
DIDPublicKeyTemplate("key4", VerificationRelationship.KeyAgreement, EllipticCurve.X25519),
)
)
for {
svc <- ZIO.service[ManagedDIDService]
did <- svc.createAndStoreDID(template).map(_.asCanonical)
keyPaths <- svc.nonSecretStorage.listHdKeyPath(did)
} yield assert(keyPaths.map(_._1))(hasSameElements(Seq("key1", "key2", ManagedDIDService.DEFAULT_MASTER_KEY_ID)))
masterKey <- svc.nonSecretStorage.getKeyMeta(did, ManagedDIDService.DEFAULT_MASTER_KEY_ID).some.map(_._1)
key1 <- svc.nonSecretStorage.getKeyMeta(did, "key1").some.map(_._1)
key2 <- svc.nonSecretStorage.getKeyMeta(did, "key2").some.map(_._1)
key3 <- svc.nonSecretStorage.getKeyMeta(did, "key3").some.map(_._1)
key4 <- svc.nonSecretStorage.getKeyMeta(did, "key4").some.map(_._1)
masterKeyPair <- svc.findDIDKeyPair(did, ManagedDIDService.DEFAULT_MASTER_KEY_ID).some
key1KeyPair <- svc.findDIDKeyPair(did, "key1").some
key2KeyPair <- svc.findDIDKeyPair(did, "key2").some
key3KeyPair <- svc.findDIDKeyPair(did, "key3").some
key4KeyPair <- svc.findDIDKeyPair(did, "key4").some
} yield assert(masterKey)(isSubtype[ManagedDIDKeyMeta.HD](anything)) &&
assert(key1)(isSubtype[ManagedDIDKeyMeta.HD](anything)) &&
assert(key2)(isSubtype[ManagedDIDKeyMeta.HD](anything)) &&
assert(key3)(isSubtype[ManagedDIDKeyMeta.Rand](anything)) &&
assert(key4)(isSubtype[ManagedDIDKeyMeta.Rand](anything)) &&
assert(masterKeyPair)(isSubtype[Secp256k1KeyPair](anything)) &&
assert(key1KeyPair)(isSubtype[Secp256k1KeyPair](anything)) &&
assert(key2KeyPair)(isSubtype[Secp256k1KeyPair](anything)) &&
assert(key3KeyPair)(isSubtype[Ed25519KeyPair](anything)) &&
assert(key4KeyPair)(isSubtype[X25519KeyPair](anything))
},
test("created DID have corresponding public keys in CreateOperation") {
val template = generateDIDTemplate(
Expand Down Expand Up @@ -320,7 +343,7 @@ object ManagedDIDServiceSpec
ctx2 = ZLayer.succeed(WalletAccessContext(walletId2))
svc <- ZIO.service[ManagedDIDService]
storage <- ZIO.service[DIDNonSecretStorage]
urlTmp = java.net.URL("http://example.com")
urlTmp = java.net.URI("http://example.com").toURL()
peerDid1 <- svc.createAndStorePeerDID(urlTmp).provide(ctx1)
peerDid2 <- svc.createAndStorePeerDID(urlTmp).provide(ctx2)
record1 <- storage.getPeerDIDRecord(peerDid1.did)
Expand Down Expand Up @@ -393,34 +416,55 @@ object ManagedDIDServiceSpec
testDIDSvc <- ZIO.service[TestDIDService]
did <- initPublishedDID
_ <- testDIDSvc.setResolutionResult(Some(resolutionResult()))
actions = Seq("key-1", "key-2").map(id =>
actions = Seq(
UpdateManagedDIDAction.AddKey(
DIDPublicKeyTemplate(id, VerificationRelationship.Authentication, EllipticCurve.SECP256K1)
DIDPublicKeyTemplate("key-1", VerificationRelationship.Authentication, EllipticCurve.SECP256K1)
),
UpdateManagedDIDAction.AddKey(
DIDPublicKeyTemplate("key-2", VerificationRelationship.Authentication, EllipticCurve.ED25519)
),
UpdateManagedDIDAction.AddKey(
DIDPublicKeyTemplate("key-3", VerificationRelationship.KeyAgreement, EllipticCurve.X25519)
)
)
_ <- svc.updateManagedDID(did, actions)
keyPaths <- svc.nonSecretStorage.listHdKeyPath(did)
} yield assert(keyPaths.map(_._1))(
hasSameElements(Seq(ManagedDIDService.DEFAULT_MASTER_KEY_ID, "key-1", "key-2"))
)
_ <- svc.syncUnconfirmedUpdateOperations
key1KeyPair <- svc.findDIDKeyPair(did, "key-1").some
key2KeyPair <- svc.findDIDKeyPair(did, "key-2").some
key3KeyPair <- svc.findDIDKeyPair(did, "key-3").some
} yield assert(key1KeyPair)(isSubtype[Secp256k1KeyPair](anything)) &&
assert(key2KeyPair)(isSubtype[Ed25519KeyPair](anything)) &&
assert(key3KeyPair)(isSubtype[X25519KeyPair](anything))
},
test("store private keys with the same key-id across multiple update operation") {
for {
svc <- ZIO.service[ManagedDIDService]
testDIDSvc <- ZIO.service[TestDIDService]
did <- initPublishedDID
_ <- testDIDSvc.setResolutionResult(Some(resolutionResult()))
actions = Seq("key-1", "key-2").map(id =>
actions = Seq(
UpdateManagedDIDAction.AddKey(
DIDPublicKeyTemplate("key-1", VerificationRelationship.Authentication, EllipticCurve.SECP256K1)
),
UpdateManagedDIDAction.AddKey(
DIDPublicKeyTemplate(id, VerificationRelationship.Authentication, EllipticCurve.SECP256K1)
DIDPublicKeyTemplate("key-2", VerificationRelationship.Authentication, EllipticCurve.ED25519)
)
)
_ <- svc.updateManagedDID(did, actions) // 1st update
_ <- svc.updateManagedDID(did, actions.take(1)) // 2nd update: key-1 is added twice
keyPaths <- svc.nonSecretStorage.listHdKeyPath(did)
} yield assert(keyPaths.map(_._1))(
hasSameElements(Seq(ManagedDIDService.DEFAULT_MASTER_KEY_ID, "key-1", "key-1", "key-2"))
)
_ <- testDIDSvc.setOperationStatus(ScheduledDIDOperationStatus.Confirmed)
_ <- svc.syncUnconfirmedUpdateOperations
key1KeyPair1 <- svc.findDIDKeyPair(did, "key-1").some
key2KeyPair1 <- svc.findDIDKeyPair(did, "key-2").some
_ <- svc.updateManagedDID(did, actions) // 2nd update
_ <- testDIDSvc.setOperationStatus(ScheduledDIDOperationStatus.Rejected)
_ <- svc.syncUnconfirmedUpdateOperations
key1KeyPair2 <- svc.findDIDKeyPair(did, "key-1").some
key2KeyPair2 <- svc.findDIDKeyPair(did, "key-2").some
} yield assert(key1KeyPair1)(isSubtype[Secp256k1KeyPair](anything)) &&
assert(key2KeyPair1)(isSubtype[Ed25519KeyPair](anything)) &&
// 2nd update with rejected status does not update the key pair
assert(key1KeyPair2)(equalTo(key1KeyPair1)) &&
assert(key2KeyPair2)(equalTo(key2KeyPair1))
},
test("store did lineage for each update operation") {
for {
Expand Down Expand Up @@ -519,7 +563,7 @@ object ManagedDIDServiceSpec
ctx1 = ZLayer.succeed(WalletAccessContext(walletId1))
ctx2 = ZLayer.succeed(WalletAccessContext(walletId2))
svc <- ZIO.service[ManagedDIDService]
urlTmp = java.net.URL("http://example.com")
urlTmp = java.net.URI("http://example.com").toURL()
dids1 <- ZIO.foreach(1 to 3)(_ => svc.createAndStorePeerDID(urlTmp)).provide(ctx1)
dids2 <- ZIO.foreach(1 to 3)(_ => svc.createAndStorePeerDID(urlTmp)).provide(ctx2)
ownWalletDids1 <- ZIO.foreach(dids1)(d => svc.getPeerDID(d.did).exit).provide(ctx1)
Expand Down
Loading

0 comments on commit f5f2d75

Please sign in to comment.