Skip to content

Commit

Permalink
fix: expose new key types in rest api (#1066)
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 committed May 21, 2024
1 parent 364a5dc commit 9ce8d3a
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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)
Expand Down Expand Up @@ -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"))))
)
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.*

Expand Down

0 comments on commit 9ce8d3a

Please sign in to comment.