From 9ce8d3a8742f86c9a593c705e0f3aa472ff10987 Mon Sep 17 00:00:00 2001 From: patlo-iog Date: Tue, 21 May 2024 12:40:33 +0700 Subject: [PATCH] fix: expose new key types in rest api (#1066) Signed-off-by: Pat Losoponkul --- .../castor/core/model/error/package.scala | 3 +- .../core/util/DIDOperationValidator.scala | 46 ++++---------- .../core/util/DIDOperationValidatorSpec.scala | 61 +++++++------------ .../identus/controller/http/ManagedDID.scala | 11 ++-- .../identus/pollux/vc/jwt/DidJWT.scala | 2 - 5 files changed, 40 insertions(+), 83 deletions(-) diff --git a/castor/src/main/scala/org/hyperledger/identus/castor/core/model/error/package.scala b/castor/src/main/scala/org/hyperledger/identus/castor/core/model/error/package.scala index e0f5297fd9..c8922e6bb0 100644 --- a/castor/src/main/scala/org/hyperledger/identus/castor/core/model/error/package.scala +++ b/castor/src/main/scala/org/hyperledger/identus/castor/core/model/error/package.scala @@ -23,8 +23,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 InvalidPublicKeyData(ids: Seq[String]) extends OperationValidationError - final case class InvalidMasterKeyType(ids: Seq[String]) extends OperationValidationError + final case class InvalidMasterKeyData(ids: Seq[String]) extends OperationValidationError } } diff --git a/castor/src/main/scala/org/hyperledger/identus/castor/core/util/DIDOperationValidator.scala b/castor/src/main/scala/org/hyperledger/identus/castor/core/util/DIDOperationValidator.scala index 33401840e2..fbeab5d5c6 100644 --- a/castor/src/main/scala/org/hyperledger/identus/castor/core/util/DIDOperationValidator.scala +++ b/castor/src/main/scala/org/hyperledger/identus/castor/core/util/DIDOperationValidator.scala @@ -8,7 +8,6 @@ import org.hyperledger.identus.shared.crypto.Apollo import zio.* import scala.collection.immutable.ArraySeq -import scala.util.Failure object DIDOperationValidator { final case class Config( @@ -51,7 +50,6 @@ private object CreateOperationValidator extends BaseOperationValidator { _ <- validateUniquePublicKeyId(operation, extractKeyIds) _ <- validateUniqueServiceId(operation, extractServiceIds) _ <- validateMasterKeyIsSecp256k1(operation, extractKeyData) - _ <- validateKeyData(operation, extractKeyData) _ <- validateKeyIdIsUriFragment(operation, extractKeyIds) _ <- validateKeyIdLength(config)(operation, extractKeyIds) _ <- validateServiceIdIsUriFragment(operation, extractServiceIds) @@ -102,7 +100,6 @@ private object UpdateOperationValidator extends BaseOperationValidator { _ <- validateMaxPublicKeysAccess(config)(operation, extractKeyIds) _ <- validateMaxServiceAccess(config)(operation, extractServiceIds) _ <- validateMasterKeyIsSecp256k1(operation, extractKeyData) - _ <- validateKeyData(operation, extractKeyData) _ <- validateKeyIdIsUriFragment(operation, extractKeyIds) _ <- validateKeyIdLength(config)(operation, extractKeyIds) _ <- validateServiceIdIsUriFragment(operation, extractServiceIds) @@ -360,45 +357,26 @@ private trait BaseOperationValidator { UriUtils.normalizeUri(uri).contains(uri) } - protected def validateKeyData[T <: PrismDIDOperation]( - operation: T, - keyDataExtractor: KeyDataExtractor[T] - ): Either[OperationValidationError, Unit] = { - val keys = keyDataExtractor(operation) - val apollo = Apollo.default - val parsedKeys = keys.map { case (id, _, keyData) => - val pk = keyData match { - case PublicKeyData.ECKeyData(EllipticCurve.SECP256K1, x, y) => - apollo.secp256k1.publicKeyFromCoordinate(x.toByteArray, y.toByteArray) - case PublicKeyData.ECKeyData(EllipticCurve.ED25519, x, _) => - apollo.ed25519.publicKeyFromEncoded(x.toByteArray) - case PublicKeyData.ECKeyData(EllipticCurve.X25519, x, _) => - apollo.x25519.publicKeyFromEncoded(x.toByteArray) - case PublicKeyData.ECCompressedKeyData(EllipticCurve.SECP256K1, data) => - apollo.secp256k1.publicKeyFromEncoded(data.toByteArray) - case PublicKeyData.ECCompressedKeyData(EllipticCurve.ED25519, data) => - apollo.ed25519.publicKeyFromEncoded(data.toByteArray) - case PublicKeyData.ECCompressedKeyData(EllipticCurve.X25519, data) => - apollo.x25519.publicKeyFromEncoded(data.toByteArray) - } - id -> pk - } - - val invalidKeyDataIds = parsedKeys.collect { case (id, Failure(_)) => id } - if (invalidKeyDataIds.isEmpty) Right(()) - else Left(OperationValidationError.InvalidPublicKeyData(invalidKeyDataIds)) - } - protected def validateMasterKeyIsSecp256k1[T <: PrismDIDOperation]( operation: T, keyDataExtractor: KeyDataExtractor[T] ): Either[OperationValidationError, Unit] = { val keys = keyDataExtractor(operation) val masterKeys = keys.collect { case (id, InternalKeyPurpose.Master, keyData) => id -> keyData } - val invalidKeyIds = masterKeys.filter(_._2.crv != EllipticCurve.SECP256K1).map(_._1) + val invalidKeyIds = masterKeys + .filter { case (_, pk) => + pk match { + case PublicKeyData.ECKeyData(EllipticCurve.SECP256K1, x, y) => + Apollo.default.secp256k1.publicKeyFromCoordinate(x.toByteArray, y.toByteArray).isFailure + case PublicKeyData.ECCompressedKeyData(EllipticCurve.SECP256K1, data) => + Apollo.default.secp256k1.publicKeyFromEncoded(data.toByteArray).isFailure + case _ => true // master key must be secp256k1 + } + } + .map(_._1) if (invalidKeyIds.isEmpty) Right(()) - else Left(OperationValidationError.InvalidMasterKeyType(invalidKeyIds)) + else Left(OperationValidationError.InvalidMasterKeyData(invalidKeyIds)) } } diff --git a/castor/src/test/scala/org/hyperledger/identus/castor/core/util/DIDOperationValidatorSpec.scala b/castor/src/test/scala/org/hyperledger/identus/castor/core/util/DIDOperationValidatorSpec.scala index 12fb2cf02a..6e2eee7396 100644 --- a/castor/src/test/scala/org/hyperledger/identus/castor/core/util/DIDOperationValidatorSpec.scala +++ b/castor/src/test/scala/org/hyperledger/identus/castor/core/util/DIDOperationValidatorSpec.scala @@ -340,24 +340,6 @@ object DIDOperationValidatorSpec extends ZIOSpecDefault { invalidArgumentContainsString("service id is invalid: [Wrong service]") ) }, - test("reject CreateOperation when publicKeyData is invalid") { - val op = createPrismDIDOperation( - publicKeys = Seq( - PublicKey( - id = "key-0", - purpose = VerificationRelationship.Authentication, - publicKeyData = PublicKeyData.ECKeyData( - crv = EllipticCurve.SECP256K1, - x = Base64UrlString.fromStringUnsafe("00"), - y = Base64UrlString.fromStringUnsafe("00") - ) - ) - ) - ) - assert(DIDOperationValidator(Config.default).validate(op))( - isLeft(equalTo(OperationValidationError.InvalidPublicKeyData(Seq("key-0")))) - ) - }, test("reject CreateOperation when master key is not a secp256k1 key") { val op = createPrismDIDOperation( internalKeys = Seq( @@ -369,11 +351,20 @@ object DIDOperationValidatorSpec extends ZIOSpecDefault { x = Base64UrlString.fromStringUnsafe("11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"), y = Base64UrlString.fromStringUnsafe("") ) + ), + InternalPublicKey( + id = "master1", + purpose = InternalKeyPurpose.Master, + publicKeyData = PublicKeyData.ECKeyData( + crv = EllipticCurve.SECP256K1, + x = Base64UrlString.fromStringUnsafe("11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"), + y = Base64UrlString.fromStringUnsafe("") + ) ) ) ) assert(DIDOperationValidator(Config.default).validate(op))( - isLeft(equalTo(OperationValidationError.InvalidMasterKeyType(Seq("master0")))) + isLeft(equalTo(OperationValidationError.InvalidMasterKeyData(Seq("master0", "master1")))) ) } ).provideLayer(testLayer) @@ -597,38 +588,32 @@ object DIDOperationValidatorSpec extends ZIOSpecDefault { invalidArgumentContainsString("must not have both 'type' and 'serviceEndpoints' empty") ) }, - test("reject UpdateOperation publicKeyData is invalid") { - val action = UpdateDIDAction.AddKey( - PublicKey( - id = "key0", - purpose = VerificationRelationship.Authentication, + test("reject UpdateOperation when master key is not a secp256k1 key") { + val action1 = UpdateDIDAction.AddInternalKey( + InternalPublicKey( + id = "master0", + purpose = InternalKeyPurpose.Master, publicKeyData = PublicKeyData.ECKeyData( - crv = EllipticCurve.SECP256K1, - x = Base64UrlString.fromStringUnsafe("00"), - y = Base64UrlString.fromStringUnsafe("00") + crv = EllipticCurve.ED25519, + x = Base64UrlString.fromStringUnsafe("11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"), + y = Base64UrlString.fromStringUnsafe("") ) ) ) - val op = updatePrismDIDOperation(Seq(action)) - assert(DIDOperationValidator(Config.default).validate(op))( - isLeft(equalTo(OperationValidationError.InvalidPublicKeyData(Seq("key0")))) - ) - }, - test("reject UpdateOperation when master key is not a secp256k1 key") { - val action = UpdateDIDAction.AddInternalKey( + val action2 = UpdateDIDAction.AddInternalKey( InternalPublicKey( - id = "master0", + id = "master1", purpose = InternalKeyPurpose.Master, publicKeyData = PublicKeyData.ECKeyData( - crv = EllipticCurve.ED25519, + crv = EllipticCurve.SECP256K1, x = Base64UrlString.fromStringUnsafe("11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"), y = Base64UrlString.fromStringUnsafe("") ) ) ) - val op = updatePrismDIDOperation(Seq(action)) + val op = updatePrismDIDOperation(Seq(action1, action2)) assert(DIDOperationValidator(Config.default).validate(op))( - isLeft(equalTo(OperationValidationError.InvalidMasterKeyType(Seq("master0")))) + isLeft(equalTo(OperationValidationError.InvalidMasterKeyData(Seq("master0", "master1")))) ) } ) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/controller/http/ManagedDID.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/controller/http/ManagedDID.scala index 021275998a..631711c7de 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/controller/http/ManagedDID.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/controller/http/ManagedDID.scala @@ -203,13 +203,10 @@ final case class ManagedDIDKeyTemplate( @description(ManagedDIDKeyTemplate.annotations.purpose.description) @encodedExample(ManagedDIDKeyTemplate.annotations.purpose.example) purpose: Purpose, - // @description(ManagedDIDKeyTemplate.annotations.curve.description) - // @encodedExample(ManagedDIDKeyTemplate.annotations.curve.example) - // curve: Option[Curve] -) { - // TODO: this curve option is hidden for now, to be added back after integration test with node - def curve: Option[Curve] = None -} + @description(ManagedDIDKeyTemplate.annotations.curve.description) + @encodedExample(ManagedDIDKeyTemplate.annotations.curve.example) + curve: Option[Curve] +) object ManagedDIDKeyTemplate { object annotations { diff --git a/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/DidJWT.scala b/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/DidJWT.scala index 19bf145f9d..1a8baa99ef 100644 --- a/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/DidJWT.scala +++ b/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/DidJWT.scala @@ -7,8 +7,6 @@ import com.nimbusds.jose.jwk.{Curve, ECKey} import com.nimbusds.jwt.{JWTClaimsSet, SignedJWT} import io.circe.* import zio.* -import pdi.jwt.algorithms.JwtECDSAAlgorithm -import pdi.jwt.{JwtAlgorithm, JwtCirce} import java.security.*