Skip to content

Commit

Permalink
feat: Add KID to the credential-offers API - ATL-7704 (#1320)
Browse files Browse the repository at this point in the history
Signed-off-by: FabioPinheiro <[email protected]>
Co-authored-by: Yurii Shynbuiev <[email protected]>
  • Loading branch information
FabioPinheiro and yshyn-iohk authored Sep 18, 2024
1 parent 6567950 commit 56200cf
Show file tree
Hide file tree
Showing 43 changed files with 322 additions and 235 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ import org.hyperledger.identus.castor.core.model.did.{
UpdateDIDAction,
VerificationRelationship
}
import org.hyperledger.identus.castor.core.model.did.ServiceEndpoint.value
import org.hyperledger.identus.castor.core.model.did.ServiceEndpoint.UriOrJsonEndpoint
import org.hyperledger.identus.shared.models.Base64UrlString
import org.hyperledger.identus.shared.models.KeyId
import org.hyperledger.identus.shared.utils.Traverse.*
import zio.*

Expand Down Expand Up @@ -122,7 +124,7 @@ private[castor] trait ProtoModelHelper {
extension (publicKey: PublicKey) {
def toProto: node_models.PublicKey = {
node_models.PublicKey(
id = publicKey.id,
id = publicKey.id.value,
usage = publicKey.purpose match {
case VerificationRelationship.Authentication => node_models.KeyUsage.AUTHENTICATION_KEY
case VerificationRelationship.AssertionMethod => node_models.KeyUsage.ISSUING_KEY
Expand All @@ -140,7 +142,7 @@ private[castor] trait ProtoModelHelper {
extension (internalPublicKey: InternalPublicKey) {
def toProto: node_models.PublicKey = {
node_models.PublicKey(
id = internalPublicKey.id,
id = internalPublicKey.id.value,
usage = internalPublicKey.purpose match {
case InternalKeyPurpose.Master => node_models.KeyUsage.MASTER_KEY
case InternalKeyPurpose.Revocation => node_models.KeyUsage.REVOCATION_KEY
Expand Down Expand Up @@ -313,13 +315,13 @@ private[castor] trait ProtoModelHelper {
} yield purpose match {
case purpose: VerificationRelationship =>
PublicKey(
id = publicKey.id,
id = KeyId(publicKey.id),
purpose = purpose,
publicKeyData = keyData
)
case purpose: InternalKeyPurpose =>
InternalPublicKey(
id = publicKey.id,
id = KeyId(publicKey.id),
purpose = purpose,
publicKeyData = keyData
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package org.hyperledger.identus.castor.core.model.did

import org.hyperledger.identus.shared.models.Base64UrlString
import org.hyperledger.identus.shared.models.KeyId

final case class PublicKey(
id: String,
id: KeyId,
purpose: VerificationRelationship,
publicKeyData: PublicKeyData
)
Expand All @@ -14,7 +15,7 @@ enum InternalKeyPurpose {
}

final case class InternalPublicKey(
id: String,
id: KeyId,
purpose: InternalKeyPurpose,
publicKeyData: PublicKeyData
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.hyperledger.identus.castor.core.model

import org.hyperledger.identus.shared.models.KeyId

package object error {

sealed trait DIDOperationError
Expand Down Expand Up @@ -27,7 +29,7 @@ package object error {
final case class TooManyDidPublicKeyAccess(limit: Int, access: Option[Int]) extends OperationValidationError
final case class TooManyDidServiceAccess(limit: Int, access: Option[Int]) extends OperationValidationError
final case class InvalidArgument(msg: String) extends OperationValidationError
final case class InvalidMasterKeyData(ids: Seq[String]) extends OperationValidationError
final case class InvalidMasterKeyData(ids: Seq[KeyId]) extends OperationValidationError
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import org.hyperledger.identus.castor.core.model.error.OperationValidationError
import org.hyperledger.identus.castor.core.util.DIDOperationValidator.Config
import org.hyperledger.identus.castor.core.util.Prelude.*
import org.hyperledger.identus.shared.crypto.Apollo
import org.hyperledger.identus.shared.models.KeyId
import zio.*

import scala.collection.immutable.ArraySeq
Expand Down Expand Up @@ -70,14 +71,14 @@ private object CreateOperationValidator extends BaseOperationValidator {
else Left(OperationValidationError.InvalidArgument("create operation must contain at least 1 master key"))
}

private def extractKeyIds(operation: PrismDIDOperation.Create): Seq[String] = operation.publicKeys.map {
private def extractKeyIds(operation: PrismDIDOperation.Create): Seq[KeyId] = operation.publicKeys.map {
case PublicKey(id, _, _) => id
case InternalPublicKey(id, _, _) => id
}

private def extractKeyData(
operation: PrismDIDOperation.Create
): Seq[(String, VerificationRelationship | InternalKeyPurpose, PublicKeyData)] =
): Seq[(KeyId, VerificationRelationship | InternalKeyPurpose, PublicKeyData)] =
operation.publicKeys.map {
case PublicKey(id, purpose, data) => (id, purpose, data)
case InternalPublicKey(id, purpose, data) => (id, purpose, data)
Expand Down Expand Up @@ -139,15 +140,15 @@ private object UpdateOperationValidator extends BaseOperationValidator {
)
}

private def extractKeyIds(operation: PrismDIDOperation.Update): Seq[String] = operation.actions.collect {
private def extractKeyIds(operation: PrismDIDOperation.Update): Seq[KeyId] = operation.actions.collect {
case UpdateDIDAction.AddKey(pk) => pk.id
case UpdateDIDAction.AddInternalKey(pk) => pk.id
case UpdateDIDAction.RemoveKey(id) => id
case UpdateDIDAction.RemoveKey(id) => KeyId(id)
}

private def extractKeyData(
operation: PrismDIDOperation.Update
): Seq[(String, VerificationRelationship | InternalKeyPurpose, PublicKeyData)] =
): Seq[(KeyId, VerificationRelationship | InternalKeyPurpose, PublicKeyData)] =
operation.actions.collect {
case UpdateDIDAction.AddKey(pk) => (pk.id, pk.purpose, pk.publicKeyData)
case UpdateDIDAction.AddInternalKey(pk) => (pk.id, pk.purpose, pk.publicKeyData)
Expand Down Expand Up @@ -182,12 +183,12 @@ private object DeactivateOperationValidator extends BaseOperationValidator {

private trait BaseOperationValidator {

type KeyIdExtractor[T] = T => Seq[String]
type KeyIdExtractor[T] = T => Seq[KeyId]
type ServiceIdExtractor[T] = T => Seq[String]
type ServiceTypeExtractor[T] = T => Seq[(String, ServiceType)]
type ServiceEndpointExtractor[T] = T => Seq[(String, ServiceEndpoint)]
type ContextExtractor[T] = T => Seq[Seq[String]]
type KeyDataExtractor[T] = T => Seq[(String, VerificationRelationship | InternalKeyPurpose, PublicKeyData)]
type KeyDataExtractor[T] = T => Seq[(KeyId, VerificationRelationship | InternalKeyPurpose, PublicKeyData)]

protected def validateMaxPublicKeysAccess[T <: PrismDIDOperation](
config: Config
Expand Down Expand Up @@ -266,7 +267,7 @@ private trait BaseOperationValidator {
keyIdExtractor: KeyIdExtractor[T]
): Either[OperationValidationError, Unit] = {
val ids = keyIdExtractor(operation)
val invalidIds = ids.filterNot(UriUtils.isValidUriFragment)
val invalidIds = ids.map(_.value).filterNot(UriUtils.isValidUriFragment)
if (invalidIds.isEmpty) Right(())
else
Left(
Expand All @@ -289,7 +290,7 @@ private trait BaseOperationValidator {
config: Config
)(operation: T, keyIdExtractor: KeyIdExtractor[T]): Either[OperationValidationError, Unit] = {
val ids = keyIdExtractor(operation)
val invalidIds = ids.filter(_.length > config.maxIdSize)
val invalidIds = ids.filter(_.value.length > config.maxIdSize)
if (invalidIds.isEmpty) Right(())
else
Left(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import org.hyperledger.identus.castor.core.model.did.{
VerificationRelationship
}
import org.hyperledger.identus.castor.core.util.GenUtils
import org.hyperledger.identus.shared.models.KeyId
import zio.*
import zio.test.*
import zio.test.Assertion.*
Expand All @@ -17,22 +18,22 @@ object W3CModelHelperSpec extends ZIOSpecDefault {

import W3CModelHelper.*

private def generateInternalPublicKey(id: String, purpose: InternalKeyPurpose = InternalKeyPurpose.Master) =
private def generateInternalPublicKey(id: KeyId, purpose: InternalKeyPurpose = InternalKeyPurpose.Master) =
GenUtils.internalPublicKey
.map(_.copy(id = id, purpose = purpose))
.runCollectN(1)
.map(_.head)

private def generatePublicKey(id: String, purpose: VerificationRelationship) =
private def generatePublicKey(id: KeyId, purpose: VerificationRelationship) =
GenUtils.publicKey.map(_.copy(id = id, purpose = purpose)).runCollectN(1).map(_.head)

private def generateService(id: String) =
GenUtils.service.map(_.copy(id = id)).runCollectN(1).map(_.head)

private def generateDIDData(
did: CanonicalPrismDID,
masterKeyId: String = "master-0",
keyIds: Seq[(String, VerificationRelationship)] = Seq.empty,
masterKeyId: KeyId = KeyId("master-0"),
keyIds: Seq[(KeyId, VerificationRelationship)] = Seq.empty,
serviceIds: Seq[String] = Seq.empty,
context: Seq[String] = Seq.empty
) =
Expand All @@ -49,11 +50,11 @@ object W3CModelHelperSpec extends ZIOSpecDefault {
didData <- generateDIDData(
did = did,
keyIds = Seq(
"auth-0" -> VerificationRelationship.Authentication,
"iss-0" -> VerificationRelationship.AssertionMethod,
"comm-0" -> VerificationRelationship.KeyAgreement,
"capinv-0" -> VerificationRelationship.CapabilityInvocation,
"capdel-0" -> VerificationRelationship.CapabilityDelegation
KeyId("auth-0") -> VerificationRelationship.Authentication,
KeyId("iss-0") -> VerificationRelationship.AssertionMethod,
KeyId("comm-0") -> VerificationRelationship.KeyAgreement,
KeyId("capinv-0") -> VerificationRelationship.CapabilityInvocation,
KeyId("capdel-0") -> VerificationRelationship.CapabilityDelegation
),
serviceIds = Seq("service-0")
)
Expand Down Expand Up @@ -92,7 +93,7 @@ object W3CModelHelperSpec extends ZIOSpecDefault {
longFormDID = PrismDID.buildLongFormFromOperation(PrismDIDOperation.Create(Nil, Nil, Nil))
didData <- generateDIDData(
did = did,
keyIds = Seq("auth-0" -> VerificationRelationship.Authentication)
keyIds = Seq(KeyId("auth-0") -> VerificationRelationship.Authentication)
)
didDoc = didData.toW3C(longFormDID)
} yield assert(didDoc.id)(equalTo(longFormDID.toString)) &&
Expand All @@ -104,7 +105,7 @@ object W3CModelHelperSpec extends ZIOSpecDefault {
did <- ZIO.fromEither(PrismDID.buildCanonicalFromSuffix("0" * 64))
didData <- generateDIDData(
did = did,
keyIds = Seq("auth-0" -> VerificationRelationship.Authentication),
keyIds = Seq(KeyId("auth-0") -> VerificationRelationship.Authentication),
serviceIds = Seq("service-0"),
context = Seq("user-defined-context")
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import org.hyperledger.identus.castor.core.model.did.*
import org.hyperledger.identus.castor.core.model.error
import org.hyperledger.identus.shared.crypto.{Apollo, Secp256k1KeyPair}
import org.hyperledger.identus.shared.models.Base64UrlString
import org.hyperledger.identus.shared.models.KeyId
import zio.{mock, IO, URLayer, ZIO, ZLayer}
import zio.mock.{Expectation, Mock, Proxy}
import zio.test.Assertion
Expand Down Expand Up @@ -47,15 +48,15 @@ object MockDIDService extends Mock[DIDService] {
val createOperation = PrismDIDOperation.Create(
publicKeys = Seq(
InternalPublicKey(
id = "master-0",
id = KeyId("master-0"),
purpose = InternalKeyPurpose.Master,
publicKeyData = PublicKeyData.ECCompressedKeyData(
crv = EllipticCurve.SECP256K1,
data = Base64UrlString.fromByteArray(masterKeyPair.publicKey.getEncodedCompressed)
)
),
PublicKey(
id = "key-0",
id = KeyId("key-0"),
purpose = verificationRelationship,
publicKeyData = PublicKeyData.ECCompressedKeyData(
crv = EllipticCurve.SECP256K1,
Expand Down
Loading

0 comments on commit 56200cf

Please sign in to comment.