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

feat(agent): integrate key-manage into prism-agent server #77

Merged
merged 47 commits into from
Oct 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
61819e9
feat(agent): add keystore subproject
Oct 14, 2022
d8f7050
feat(agent): add intefaces & models for custodian layer
Oct 14, 2022
2645f1d
docs(agent): add readme about key-mangement
Oct 14, 2022
4a56d0d
Merge branch 'main' into feature/ATL-1925-define-km-storage
Oct 17, 2022
45cffc9
feat(agent): fix as reviewed
Oct 17, 2022
bdc9fd4
feat(agent): implement InMemoryDIDKeyStorage
Oct 17, 2022
77ebe50
feat(agent): add tests for InMemoryDIDKeyStorage
Oct 17, 2022
b85dbd9
Merge branch 'main' into feature/ATL-1925-define-km-storage-2
Oct 17, 2022
ec9d8e1
feat(agent): add createCustodialDID endpoint
Oct 17, 2022
a775475
Merge branch 'main' into feature/ATL-1925-define-km-storage-2
Oct 17, 2022
60bb1b9
feat(agent): refine createCustodialDID endpoint
Oct 17, 2022
7ebf180
feat(agent): refine createCustodialDID endpoint
Oct 17, 2022
249521f
Merge branch 'feature/ATL-1925-define-km-storage-3' into feature/ATL-…
Oct 17, 2022
0efd2e2
feat(agent): make DIDKeyStorage use PrismDID
Oct 17, 2022
70c7ca2
feat(castor): add LongFormPrismDID model
Oct 18, 2022
870e812
feat(agent): add DID commitment storage
Oct 18, 2022
732d8c6
feat(agent): add key generation in ManagedDIDService
Oct 18, 2022
5fbec58
feat(agent): fix failing test
Oct 18, 2022
cd8336f
Merge branch 'main' into feature/ATL-1925-define-km-storage-3
Oct 18, 2022
5db28f6
feat(agent): resolve merge conflict
Oct 18, 2022
7085d21
feat(agent): rename OAS model to avoid confusion
Oct 18, 2022
b8f44b8
feat(agent): rename subproject away from custodian
Oct 18, 2022
ab85e8f
feat(agent): make prism-crypto key generator works
Oct 18, 2022
e5f23d8
feat(agent): add tests for prism-crypto int conversion
Oct 19, 2022
f700135
feat(agent): store DID secret when create
Oct 19, 2022
2d70b1f
feat(agent): add validation when createManagedDID
Oct 19, 2022
f92e444
feat(agent): add DIDNonSecretStorage & duplicated DID validation
Oct 19, 2022
0b0c08e
feat(agent): add ability to publish stored did
Oct 20, 2022
90a835f
feat(agent): add commitment value InMemorySecretStorage tests
Oct 20, 2022
724ef60
feat(agent): add createAndStoreDID tests
Oct 20, 2022
e6dfeb5
feat(agent): add tests for publishStoredDID
Oct 20, 2022
3bc6c56
Merge branch 'main' into feature/ATL-1925-define-km-storage-3
Oct 20, 2022
5003c09
feat(prism-agent): pr diff cleanup
Oct 20, 2022
e8891df
Merge branch 'main' into feature/ATL-1925-define-km-storage-3
Oct 20, 2022
0ef6c16
Merge branch 'main' into feature/ATL-1925-define-km-storage-3
Oct 20, 2022
c76747a
Merge branch 'main' into feature/ATL-1925-define-km-storage-3
Oct 21, 2022
bb4e507
style(shared): run formatter on shared
Oct 21, 2022
3e9e8c2
fix(iris): align type signature
Oct 21, 2022
4a7610d
Merge branch 'fix/ATL-1791-correct-type-signature' into feature/ATL-1…
Oct 21, 2022
1bd7ce5
feat(agent): rename keymanagement to walletapi
Oct 21, 2022
eeacf84
Merge branch 'feature/ATL-1925-define-km-storage-3' into feature/ATL-…
Oct 21, 2022
db36da5
Merge branch 'main' into feature/ATL-1925-define-km-storage-4
Oct 21, 2022
88bdbc8
Merge branch 'main' into feature/ATL-1925-define-km-storage-4
Oct 21, 2022
7424fda
feat(agent): add mock stub & link ManagedDIDService to HTTP server
Oct 21, 2022
c94ab5a
feat(agent): integrate publishStoredDID in HTTP server
Oct 21, 2022
bd3d99b
feat(agent): integrate createManagedDID in HTTP server
Oct 21, 2022
76ecbfc
Merge branch 'main' into feature/ATL-1925-define-km-storage-4
Oct 21, 2022
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 @@ -30,6 +30,30 @@ sealed trait PrismDID {

}

object PrismDID {
// TODO: implement a proper DID parser (ATL-2031)
// For now, just make it work with a simple case of Prism DID V1
def parse(didRef: String): Either[String, PrismDID] = {
if (didRef.startsWith("did:prism:1:")) {
val suffix = didRef.drop("did:prism:1:".length)
suffix.split(':').toList match {
case network :: suffix :: encodedState :: Nil =>
Right(
LongFormPrismDIDV1(
network,
HexString.fromStringUnsafe(suffix),
Base64UrlString.fromStringUnsafe(encodedState)
)
)
case network :: suffix :: Nil => Right(PrismDIDV1(network, HexString.fromStringUnsafe(suffix)))
case _ => Left("Invalid DID syntax")
}
} else {
Left("DID parsing only supports Prism DID with did:prism:1 prefix")
}
}
}

final case class PrismDIDV1 private[did] (network: String, suffix: HexString) extends PrismDID {

override val version: PrismDIDVersion = PrismDIDVersion.V1
Expand Down
26 changes: 11 additions & 15 deletions prism-agent/api/http/castor/schemas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -206,24 +206,22 @@ components:
type: object
required:
- id
- purposes
- purpose
properties:
id:
type: string
description: Identifier of a verification material in the DID Document
example: key-01
purposes:
type: array
items:
type: string
enum:
[
"authentication",
"assertionMethod",
"keyAgreement",
"capabilityInvocation",
"capabilityDelegation",
]
purpose:
type: string
enum:
[
"authentication",
"assertionMethod",
"keyAgreement",
"capabilityInvocation",
"capabilityDelegation",
]
example: [ "authentication", "assertionMethod" ]
services:
type: array
Expand All @@ -236,8 +234,6 @@ components:
- did
- longFormDid
properties:
did:
$ref: "#/components/schemas/DID"
longFormDid:
type: string
description: A long-form DID for the created DID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,32 @@ import io.iohk.atala.agent.server.http.marshaller.{
DIDApiMarshallerImpl,
DIDAuthenticationApiMarshallerImpl,
DIDOperationsApiMarshallerImpl,
DIDRegistrarApiMarshallerImpl,
IssueCredentialsApiMarshallerImpl
}
import io.iohk.atala.agent.server.http.service.{
DIDApiServiceImpl,
DIDAuthenticationApiServiceImpl,
DIDOperationsApiServiceImpl,
DIDRegistrarApiServiceImpl,
IssueCredentialsApiServiceImpl
}
import io.iohk.atala.castor.core.repository.DIDOperationRepository
import io.iohk.atala.agent.openapi.api.{DIDApi, DIDAuthenticationApi, DIDOperationsApi, IssueCredentialsApi}
import io.iohk.atala.agent.openapi.api.{
DIDApi,
DIDAuthenticationApi,
DIDOperationsApi,
DIDRegistrarApi,
IssueCredentialsApi
}
import io.iohk.atala.castor.sql.repository.{JdbcDIDOperationRepository, TransactorLayer}
import zio.*
import zio.interop.catz.*
import cats.effect.std.Dispatcher
import com.typesafe.config.ConfigFactory
import io.grpc.ManagedChannelBuilder
import io.iohk.atala.agent.server.config.AppConfig
import io.iohk.atala.agent.walletapi.service.ManagedDIDService
import io.iohk.atala.castor.core.util.DIDOperationValidator
import io.iohk.atala.iris.proto.service.IrisServiceGrpc
import io.iohk.atala.iris.proto.service.IrisServiceGrpc.IrisServiceStub
Expand Down Expand Up @@ -79,8 +88,12 @@ object AppModule {
serviceLimit = 50
)
)

val didServiceLayer: TaskLayer[DIDService] =
(GrpcModule.layers ++ RepoModule.layers ++ didOpValidatorLayer) >>> DIDServiceImpl.layer

val manageDIDServiceLayer: TaskLayer[ManagedDIDService] =
(didOpValidatorLayer ++ didServiceLayer) >>> ManagedDIDService.inMemoryStorage()
}

object GrpcModule {
Expand Down Expand Up @@ -121,13 +134,21 @@ object HttpModule {
(apiServiceLayer ++ apiMarshallerLayer) >>> ZLayer.fromFunction(new DIDAuthenticationApi(_, _))
}

val didRegistrarApiLayer: TaskLayer[DIDRegistrarApi] = {
val serviceLayer = AppModule.manageDIDServiceLayer
val apiServiceLayer = serviceLayer >>> DIDRegistrarApiServiceImpl.layer
val apiMarshallerLayer = DIDRegistrarApiMarshallerImpl.layer
(apiServiceLayer ++ apiMarshallerLayer) >>> ZLayer.fromFunction(new DIDRegistrarApi(_, _))
}

val issueCredentialsApiLayer: ULayer[IssueCredentialsApi] = {
val apiServiceLayer = IssueCredentialsApiServiceImpl.layer
val apiMarshallerLayer = IssueCredentialsApiMarshallerImpl.layer
(apiServiceLayer ++ apiMarshallerLayer) >>> ZLayer.fromFunction(new IssueCredentialsApi(_, _))
}

val layers = didApiLayer ++ didOperationsApiLayer ++ didAuthenticationApiLayer ++ issueCredentialsApiLayer
val layers =
didApiLayer ++ didOperationsApiLayer ++ didAuthenticationApiLayer ++ didRegistrarApiLayer ++ issueCredentialsApiLayer
}

object RepoModule {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,25 @@ package io.iohk.atala.agent.server.http
import akka.http.scaladsl.model.ContentType
import akka.http.scaladsl.server.Route
import akka.http.scaladsl.server.Directives.*
import io.iohk.atala.agent.openapi.api.{DIDApi, DIDAuthenticationApi, DIDOperationsApi, IssueCredentialsApi}
import io.iohk.atala.agent.openapi.api.{
DIDApi,
DIDAuthenticationApi,
DIDOperationsApi,
DIDRegistrarApi,
IssueCredentialsApi
}
import zio.*

object HttpRoutes {

def routes: URIO[DIDApi & DIDOperationsApi & DIDAuthenticationApi & IssueCredentialsApi, Route] =
def routes: URIO[DIDApi & DIDOperationsApi & DIDAuthenticationApi & DIDRegistrarApi & IssueCredentialsApi, Route] =
for {
didApi <- ZIO.service[DIDApi]
didOperationsApi <- ZIO.service[DIDOperationsApi]
didAuthApi <- ZIO.service[DIDAuthenticationApi]
disRegistrarApi <- ZIO.service[DIDRegistrarApi]
issueCredentialApi <- ZIO.service[IssueCredentialsApi]
} yield didApi.route ~ didOperationsApi.route ~ didAuthApi.route ~ issueCredentialApi.route ~ additionalRoute
} yield didApi.route ~ didOperationsApi.route ~ didAuthApi.route ~ disRegistrarApi.route ~ issueCredentialApi.route ~ additionalRoute

private def additionalRoute: Route = {
// swagger-ui expects this particular header when resolving relative $ref
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.iohk.atala.agent.server.http.marshaller

import akka.http.scaladsl.marshalling.ToEntityMarshaller
import akka.http.scaladsl.unmarshalling.FromEntityUnmarshaller
import io.iohk.atala.agent.openapi.api.DIDRegistrarApiMarshaller
import io.iohk.atala.agent.openapi.model.{
CreateManagedDIDResponse,
CreateManagedDidRequest,
DIDOperationResponse,
ErrorResponse
}
import spray.json.RootJsonFormat
import zio.*

object DIDRegistrarApiMarshallerImpl extends JsonSupport {

val layer: ULayer[DIDRegistrarApiMarshaller] = ZLayer.succeed {
new DIDRegistrarApiMarshaller:
override implicit def fromEntityUnmarshallerCreateManagedDidRequest
: FromEntityUnmarshaller[CreateManagedDidRequest] = summon[RootJsonFormat[CreateManagedDidRequest]]

override implicit def toEntityMarshallerDIDOperationResponse: ToEntityMarshaller[DIDOperationResponse] =
summon[RootJsonFormat[DIDOperationResponse]]

override implicit def toEntityMarshallerCreateManagedDIDResponse: ToEntityMarshaller[CreateManagedDIDResponse] =
summon[RootJsonFormat[CreateManagedDIDResponse]]

override implicit def toEntityMarshallerErrorResponse: ToEntityMarshaller[ErrorResponse] =
summon[RootJsonFormat[ErrorResponse]]
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,24 @@ trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol {
given RootJsonFormat[CreateAuthenticationChallengeResponse] = jsonFormat2(CreateAuthenticationChallengeResponse.apply)
given RootJsonFormat[CreateDIDRequest] = jsonFormat4(CreateDIDRequest.apply)
given RootJsonFormat[CreateDIDRequestDocument] = jsonFormat2(CreateDIDRequestDocument.apply)
given RootJsonFormat[CreateManagedDidRequest] = jsonFormat1(CreateManagedDidRequest.apply)
given RootJsonFormat[CreateManagedDidRequestDocumentTemplate] = jsonFormat3(
CreateManagedDidRequestDocumentTemplate.apply
)
given RootJsonFormat[CreateManagedDidRequestDocumentTemplatePublicKeysInner] = jsonFormat2(
CreateManagedDidRequestDocumentTemplatePublicKeysInner.apply
)
given RootJsonFormat[CreateManagedDIDResponse] = jsonFormat1(CreateManagedDIDResponse.apply)
given RootJsonFormat[DeactivateDIDRequest] = jsonFormat4(DeactivateDIDRequest.apply)
given RootJsonFormat[Delta] = jsonFormat2(Delta.apply)
given RootJsonFormat[DeltaUpdate] = jsonFormat2(DeltaUpdate.apply)
given RootJsonFormat[DID] = jsonFormat8(DID.apply)
given RootJsonFormat[DidOperation] = jsonFormat4(DidOperation.apply)
given RootJsonFormat[DIDOperationResponse] = jsonFormat1(DIDOperationResponse.apply)
given RootJsonFormat[DidOperationStatus] = jsonFormat0(DidOperationStatus.apply)
given RootJsonFormat[DidOperationSubmission] = jsonFormat2(DidOperationSubmission.apply)
given RootJsonFormat[DidOperationType] = jsonFormat0(DidOperationType.apply)
given RootJsonFormat[DIDResponse] = jsonFormat2(DIDResponse.apply)
given RootJsonFormat[DIDOperationResponse] = jsonFormat1(DIDOperationResponse.apply)
given RootJsonFormat[DidOperationSubmission] = jsonFormat2(DidOperationSubmission.apply)
given RootJsonFormat[ErrorResponse] = jsonFormat5(ErrorResponse.apply)
given RootJsonFormat[JsonWebKey2020] = jsonFormat1(JsonWebKey2020.apply)
given RootJsonFormat[PublicKey] = jsonFormat5(PublicKey.apply)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ package io.iohk.atala.agent.server.http.model

import io.iohk.atala.agent.openapi.model.{
CreateDIDRequest,
CreateManagedDidRequestDocumentTemplate,
CreateManagedDidRequestDocumentTemplatePublicKeysInner,
DIDOperationResponse,
DidOperation,
DidOperationSubmission,
JsonWebKey2020,
PublicKey,
PublicKeyJwk,
Service,
DidOperationSubmission
Service
}
import io.iohk.atala.castor.core.model.did as domain
import io.iohk.atala.castor.core.model.did as castorDomain
import io.iohk.atala.castor.core.model.did.PublishedDIDOperation
import io.iohk.atala.agent.walletapi.model as walletDomain
import io.iohk.atala.shared.models.HexStrings.*
import io.iohk.atala.shared.models.Base64UrlStrings.*
import io.iohk.atala.shared.utils.Traverse.*
Expand All @@ -22,7 +25,7 @@ import scala.util.Try
trait OASDomainModelHelper {

extension (req: CreateDIDRequest) {
def toDomain: Either[String, domain.PublishedDIDOperation.Create] = {
def toDomain: Either[String, castorDomain.PublishedDIDOperation.Create] = {
for {
updateCommitmentHex <- HexString
.fromString(req.updateCommitment)
Expand All @@ -36,25 +39,25 @@ trait OASDomainModelHelper {
.map(_ => "unable to convert recoveryCommitment to hex string")
publicKeys <- req.document.publicKeys.getOrElse(Nil).traverse(_.toDomain)
services <- req.document.services.getOrElse(Nil).traverse(_.toDomain)
} yield domain.PublishedDIDOperation.Create(
} yield castorDomain.PublishedDIDOperation.Create(
updateCommitment = updateCommitmentHex,
recoveryCommitment = recoveryCommitmentHex,
storage = domain.DIDStorage.Cardano(req.storage),
document = domain.DIDDocument(publicKeys = publicKeys, services = services)
storage = castorDomain.DIDStorage.Cardano(req.storage),
document = castorDomain.DIDDocument(publicKeys = publicKeys, services = services)
)
}
}

extension (service: Service) {
def toDomain: Either[String, domain.Service] = {
def toDomain: Either[String, castorDomain.Service] = {
for {
serviceEndpoint <- Try(URI.create(service.serviceEndpoint)).toEither.left.map(_ =>
s"unable to parse serviceEndpoint ${service.serviceEndpoint} as URI"
)
serviceType <- domain.ServiceType
serviceType <- castorDomain.ServiceType
.parseString(service.`type`)
.toRight(s"unsupported serviceType ${service.`type`}")
} yield domain.Service(
} yield castorDomain.Service(
id = service.id,
`type` = serviceType,
serviceEndpoint = serviceEndpoint
Expand All @@ -63,24 +66,24 @@ trait OASDomainModelHelper {
}

extension (key: PublicKey) {
def toDomain: Either[String, domain.PublicKey] = {
def toDomain: Either[String, castorDomain.PublicKey] = {
for {
purposes <- key.purposes.traverse(i =>
domain.VerificationRelationship
castorDomain.VerificationRelationship
.parseString(i)
.toRight(s"unsupported verificationRelationship $i")
)
publicKeyJwk <- key.jsonWebKey2020.publicKeyJwk.toDomain
} yield domain.PublicKey.JsonWebKey2020(id = key.id, purposes = purposes, publicKeyJwk = publicKeyJwk)
} yield castorDomain.PublicKey.JsonWebKey2020(id = key.id, purposes = purposes, publicKeyJwk = publicKeyJwk)
}
}

extension (jwk: PublicKeyJwk) {
def toDomain: Either[String, domain.PublicKeyJwk] = {
def toDomain: Either[String, castorDomain.PublicKeyJwk] = {
for {
crv <- jwk.crv
.toRight("expected crv field in JWK")
.flatMap(i => domain.EllipticCurve.parseString(i).toRight(s"unsupported curve $i"))
.flatMap(i => castorDomain.EllipticCurve.parseString(i).toRight(s"unsupported curve $i"))
x <- jwk.x
.toRight("expected x field in JWK")
.flatMap(
Expand All @@ -91,11 +94,37 @@ trait OASDomainModelHelper {
.flatMap(
Base64UrlString.fromString(_).toEither.left.map(_ => "unable to convert y coordinate to base64url string")
)
} yield domain.PublicKeyJwk.ECPublicKeyData(crv = crv, x = x, y = y)
} yield castorDomain.PublicKeyJwk.ECPublicKeyData(crv = crv, x = x, y = y)
}
}

extension (outcome: domain.PublishedDIDOperationOutcome) {
extension (template: CreateManagedDidRequestDocumentTemplate) {
def toDomain: Either[String, walletDomain.ManagedDIDTemplate] = {
for {
services <- template.services.traverse(_.toDomain)
publicKeys <- template.publicKeys.traverse(_.toDomain)
} yield walletDomain.ManagedDIDTemplate(
storage = template.storage,
publicKeys = publicKeys,
services = services
)
}
}

extension (publicKeyTemplate: CreateManagedDidRequestDocumentTemplatePublicKeysInner) {
def toDomain: Either[String, walletDomain.DIDPublicKeyTemplate] = {
for {
purpose <- castorDomain.VerificationRelationship
.parseString(publicKeyTemplate.purpose)
.toRight(s"unsupported verificationRelationship ${publicKeyTemplate.purpose}")
} yield walletDomain.DIDPublicKeyTemplate(
id = publicKeyTemplate.id,
purpose = purpose
)
}
}

extension (outcome: castorDomain.PublishedDIDOperationOutcome) {
def toOAS: DIDOperationResponse = DIDOperationResponse(
scheduledOperation = DidOperationSubmission(
id = outcome.operationId.toString,
Expand Down
Loading