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

test: add tests for the new key types #1044

Merged
merged 4 commits into from
May 16, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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*/ =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this do anything different?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No difference. Just cleaning up deprecation warning.

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
Loading