From 4e9a59c44e94fc5f51d388b352af412bde2389d2 Mon Sep 17 00:00:00 2001 From: Jim Marino Date: Tue, 3 Oct 2023 10:06:08 +0200 Subject: [PATCH] docs: Add architecture docs for the Identity and Trust protocol (#149) * Add architecture docs * Update docs/developer/architecture/identity-trust-protocol/identity.hub.architecture.md Co-authored-by: Benjamin Scholtes <88310985+bscholtes1A@users.noreply.github.com> * Changes based on Ben's comments * Changes based on Ben's comments --------- Co-authored-by: Benjamin Scholtes <88310985+bscholtes1A@users.noreply.github.com> --- .../identity-trust-protocol/README.md | 6 + .../identity.hub.architecture.md | 466 ++++++++++++++++++ .../identity.hub.modules.png | Bin 0 -> 34969 bytes .../identity.hub.modules.puml | 58 +++ .../architecture/styles/diagram.styles.puml | 83 ++++ 5 files changed, 613 insertions(+) create mode 100644 docs/developer/architecture/identity-trust-protocol/README.md create mode 100644 docs/developer/architecture/identity-trust-protocol/identity.hub.architecture.md create mode 100644 docs/developer/architecture/identity-trust-protocol/identity.hub.modules.png create mode 100644 docs/developer/architecture/identity-trust-protocol/identity.hub.modules.puml create mode 100644 docs/developer/architecture/styles/diagram.styles.puml diff --git a/docs/developer/architecture/identity-trust-protocol/README.md b/docs/developer/architecture/identity-trust-protocol/README.md new file mode 100644 index 000000000..cf18c42f7 --- /dev/null +++ b/docs/developer/architecture/identity-trust-protocol/README.md @@ -0,0 +1,6 @@ +# Identity and Trust Protocol Architecture + +The EDC Identity Hub will be upgraded to implement and be fully compliant to the __Tractus X Identity And Trust +Protocols__ as defined in +this [Decision Record](../../decision-records/2023-09-27-adoption_of_the_identity_and_trust_protocol/README.md). +Documents included here define the architecture that will be used to implement this feature. diff --git a/docs/developer/architecture/identity-trust-protocol/identity.hub.architecture.md b/docs/developer/architecture/identity-trust-protocol/identity.hub.architecture.md new file mode 100644 index 000000000..4d9aa0c96 --- /dev/null +++ b/docs/developer/architecture/identity-trust-protocol/identity.hub.architecture.md @@ -0,0 +1,466 @@ +# 1. Introduction + +An `Identity Hub` (IH) manages identity resources in a dataspace. Specifically, it provides two services: + +- **Credential Service (CS)**. The CS manages [Verifiable Credentials](https://www.w3.org/TR/vc-data-model/). + This includes read and write endpoints for Verifiable Presentations (VPs) and Verifiable Credentials (VCs). +- **DID Service (DIDS)**. The DIDS is responsible for the creation, management and resolution of participant DIDs. + +## 1.2. EDC Foundation + +The Identity Hub is built using the EDC modularity and extensibility system. It also relies on core EDC components such +as cryptographic primitives, Json-Ld processing, and DID resolution. + +The EDC modularity and extensibility system supports flexible deployment topologies where the Identity Hub services can +be collocated in the same process or distributed across different clusters. For example, it is possible to deploy all +three IH services (STS, CS, and DIDS) as a single unit, or separate them into individually managed services. + +## 1.3. Deployment Topologies + +Two deployment topologies will be supported: + +1. **Embedded**: The Identity Hub can be embedded in an EDC control-plane runtime +2. **Standalone**: The Identity Hub can be deployed as a single or clustered standalone runtime + +## 1.4. Key Use Cases + +1. Initiate and manage a credential request +2. Serve VPs +3. Monitor and manage re-issuance +4. Manage DIDs, DID document resources, and DID document publication + +# 2. Architecture Concepts + +## 2.1. The Participant Context + +A participant context (PC) functions as a unit of management and control for `identity resources` in the Identity Hub. +All resources are contained and accessed through a PC. Contexts are tied to the participant identity as defined in +the [DSP specifications](https://github.com/International-Data-Spaces-Association/ids-specification) and created through the [IH Management API](#311-elevated-privilege-operations). + +Access control for public client API endpoints is scoped to a specific PC. For example, an access token as defined in +the [Base Identity Protocol specification]() is associated with a specific context and may not be used to access +resources in another context. + +The lifecycle of identity resources is bound to their containing context; if a PC is removed, this operation will +cascade to all contained resources. + +Services may register to receive participant context events, for example, when a context is created or deleted. + +## 2.2. Identity Resources + +An `identity resource` can be an `attestation`, `cryptographic material`, or an `identifier` associated with a dataspace +participant. A `Verifiable Credential` (VC), `DID`, and `DID Document` are identity resources. + +### 2.2.1. Verifiable Credential Resources + +A `VerifiableCredentialResource` (VCR) is a type of `identity resource` and will be stored on the holder side: + +```java +class VerifiableCredentialResource implements IdentityResource { + String id; + long timestamp; + VcState state; + String issuerId; + String holderId; + Policy issuancePolicy; + Policy reissuancePolicy; + VerifiableCredential verifiableCredential; +} +``` + + +The `issuerId` is a `URN`, typically a DID, that can be resolved to return the service endpoint of the Credential +Issuer. + +The `issuancePolicy` and `reissuancePolicy` fields are `ODRL` policies provided by the Credential Issuer according to +the VPP specification. They are used to determine which VCs or other prerequisites are needed to initiate a flow. + +The `state` and `timestamp` fields are used to determine when the resource entered a particular state, for example, when +the resource was requested. + +On the issuer the record for an already issued VC would look like this: +```java +class VerifiableCredentialRecord { + VcState state; // + String credentialType; // what VC was issued + String holderId; //who the VC was issued to + long expiryDate; // when the VC will expire + Long renewalDateSent; // non-null if a credential offer request was sent +} +``` + +> The `issuerId` should be resolved using an EDC registry extension + +> Do we need a list of multiple credentials? + +Credential states are defined as follows: + +```java +enum VcState { + INITIAL, REQUESTING, REQUESTED, ISSUING, ISSUED, REISSUE_REQUESTING, REISSUE_REQUESTED, TERMINATED, ERROR +} +``` + +#### 2.2.1.1 The VerifiableCredentialManager + +The `VerifiableCredentialManager` (VCM) manages `VerifiableCredentialResources` across participant contexts. For +example, it monitors outstanding requests and initiates reissue flows. The VCM is cluster-aware and guarantees that only +one flow is in effect for a particular VCR across all runtime instances. + +Services may register to receive `VerifiableCredentialManager` events, for example, when a flow is initiated. + +### 2.2.3. KeyPair Resources + +A `KeyPairResource` is a type of `identity resource` that manages public/private key pairs: + +```java +class KeyPairResource { + String id; + String groupName; + String name; + KeyPairState state; + long timestamp; + boolean defaultPair; + long useDuration; + long rotationDuration; + PublicKey publicKey; + String privateKeyId; +} +``` + +> Note this resource will also be used by the `Issuer Service`. + +The `id` must be a unique to all instances of a participant context and ideally should be a GUID. + +The `groupName` identifies which group the key pair belongs to. Other services can be configured with a group to +indicate which key pair to use when signing proofs. Only one `KeyPairResource` should be active for a group. +Inactive `KeyPairResource` are rotated or revoked. + +The `name` property is a user-friendly name for the pair. + +The `defaultPair` property is set to true if the key pair should be used to sign resources when a specific kay pair is +not specified. + +The `useDuration` specifies how long in milliseconds the key pair should be in the `ACTIVATED` state before a rotation +is started. If `-1`, the duration is indefinite and a rotation must be manually triggered. + +The `rotationDuration` specifies how long in milliseconds the key pair should be in the `ROTATED` state before a +revocation is started. If `-1`, the duration is indefinite and a revocation must be manually triggered. + +KeyPair states are defined as follows: + +```java +enum KeyPairState { + INITIAL, ACTIVATED, ROTATED, REVOKED, ERROR +} +``` + +#### 2.2.3.1. The KeyPairManager + +The `KeyPairManager` (KPM) manages `KeyPairResources` across participant contexts. For example, it monitors when keys +need to be rotated and revoked. The KPM is cluster-aware and guarantees that only one flow is in effect for a +particular `KeyPairResource` across all runtime instances. + +Services may register to receive `KeyPairManager` events, for example, when a rotation or revocation is initiated. + +### 2.2.4. DID Resources + +A `DIDResource` is a `DID` and associated entries in a `DID` document. + +```java +class DidResource { + String did; + DidState state; + long timestamp; + List serviceEndpoints; + List verificationMethods; + List verificationRelationships; +} + +class VerificationMethod { + String id; + String type; + String material; + String keyPairResourceId; +} + +class VerificationRelationship { + RelationshipType type; + List methods; +} +``` + +The `serviceEndpoints` property contains a collection of `ServiceEndpoints` that can be added through configuration or +an API invocation. + +The `verificationMethods` property contains a collection of `VerificationMethods` associated with Key + +The `verificationRelationships` property contains a collection of `VerificationRelationships` associated with Key + +> NB: There is no DID manager. + +### 2.2.5. DID Publisher + +The `DidPublisher` is responsible for taking a `DidResource,` generating a W3C-compliant `DID Document`, and publishing +it to a `Verifiable Data Registry` (VDR). For example, an implementation may publish to a CDN. The `DidPublisher` is +also responsible for un-publishing `DID Documents,` for example, when a participant context is removed. + +> We need to create a publisher that is cluster-aware and performant as an `out-of-the-box` option. One solution would +> be to publish to a database and have local caches on each runtime that periodically refresh an in-memory version. + +## 2.3. Verifiable Presentation Generation + +When a VP is requested, a `VerifiablePresentation` is created from a VC by the `VerifiablePresentationGenerator`. The +generator is responsible for assembling the VP (potentially from multiple VCs) and creating a proof. + +> **TODO** Figure out how to select proof types and potentially delegate to a `ProofGenerator` + +> **TODO** Investigate how the generator could take DIF Presentation Exchange to generate the VP + +## 2.4. Authorization + +### 2.4.1. Security Token Service + +The Security Token Service is and `IdentityService` implementation responsible for creating self-issued tokens per +the [Base Identity Protocol](). + +When creating a self-issued token, The STS will support the creation of a `VP Access Token` using the scope scheme +specified in the [Base Identity Protocol]](). Access tokens are always scoped to a participant context. + +### 2.4.2. Authorization Service + +The `AuthorizationService` authorizes read and write requests for credential resources. It is built on the policy +engine. + +`AccessDefinitions` specify an access policy for a group of resources, identified by a `ResourceType`. +Multiple `AccessDefinitions` may select the same resource; in this case, all `AccessDefinition` policies must be +satisfied to obtain access to the resource. If no `AccessDefinitions` select a resource, the resource is not accessible +to clients via the read or write API. + +```java +public class AccessDefinition { + List resourcesSelector; + List operations; + Policy policy; +} +``` + +> TODO should write access have a policy constraint always requires a write token? That can be done with a wildcard +> AccessDefinition which is registered (based on config) at startup. +> Note read access should not since some resources should be public. However, for C-X a read token for most types should +> be required. + +## 2.5. Management APIs + +The `Management API` executes authorized client requests to perform operational tasks against the Identity Hub +installation. Operations fall into two groups: those requiring elevated privileges that affect the installation as a +whole, and those that are scoped to a specific participant context. + +> Note that the Identity Hub will not include a Management UI or operator authentication. It is expected that deployment +> environments will provide their own authentication/authorization service for operators that map to one of the two +> operation types and a UI for initiating requests. + +# 3. Identity Hub Modules and Services + +The following diagram depicts the Identity Hub module dependency graph: + +![](identity.hub.modules.png)[](./identity.hub.modules.png) + +The term `installation` is used to denote an Identity Hub deployment that is operated as a unit. An `installation` may +be a single runtime instance or a cluster of instances. + +## 3.1. Management API + +The `Management API` is executes authorized client requests to perform operational tasks against the Identity Hub +installation. + +### 3.1.1. Elevated Privilege Operations + +These operations require `superuser` access and include: + +- Create/delete/list participant contexts +- Generate new participant context and authorization key + +### 3.1.2. Participant Context Operations + +These operations are scoped to a participant context and require an API key: + +- Rotate/revoke keys +- Create a DID and DID document + - Add DID elements (verification method, etc.) + - Publish DID +- List verifiable credentials +- Delete a verifiable credential by id +- Delete verifiable credentials by type +- Initiate key rotation/revocation + +All operations publish events. + +## 3.2. Hub API + +The `Hub API` is an API for external clients that implements the `Verifiable Presentation Protocol` (VPP) and +the `Credential Issuance Protocol` (CIP). It includes the following operations: + +- Write a verifiable credential (VPP) +- Present a verifiable credential as a VP (VPP) + - (support presentation exchange) +- Resolve a verifiable credential by id or type (VPP) + - (scope/presentation query) (VPP) +- Receive a credential offer (CIP) + +## 3.3. Aggregate Services + +Aggregate services perform operations that involve the orchestration of cross-module services. + +### 3.3.1 ParticipantService + +The `ParticipantService` orchestrates participant context state changes and resources. It performs the following +operations: + +#### 3.3.1.1 Initialization + +Initializes a context and contained resources based on a `ParticipantManifest`, including: + +- Generating key pairs +- Creating a DID and generate document +- Activating the context, including publishing the DID document (optional) +- Sending the result to an async callback destination which can be an endpoint or async servlet response + +#### 3.3.1.1 Deactivation + +Removes a context and all contained resources, sending the result to an async callback destination which can be an +endpoint or async servlet response. + +### 3.3.2 OfferProcessor + +The offer processor received a `Credential Offer` (CIP) and performs one of the following actions: + +- If a `VC Resouce` exists for the offer (type and issuer), and the resource is configured to auto-renew, the processor + initiates a re-issue request +- Otherwise, if an offer callback is configured, it calls the callback endpoint. The callback mechanism uses the + callback infrastructure provided by the EDC. +- Otherwise, it ignores the request + +### 3.3.3 KeyService + +The `KeyService` provides the following operations. + +#### 3.3.3.1. Rotation + +Key rotation for a `KeyPairResource` is performed as follows: + +- A new `KeyPairResource` is created and associated with the same `groupName` as the `KeyPairResource` being rotated +- The private key of the rotated `KeyPairResource` is destroyed. +- A new verification method is added using the `DidDocumentService`. +- The DID document is (optionally) published. Publication may not be performed if the rotation is performed along with + revocation (see below). + +#### 3.3.3.1. Revocation + +Key revocation for a `KeyPairResource` is performed as follows: + +- The `KeyPairResource` must be in the ROTATED state and is transitioned to the REVOKED state. +- The `verificationMethod` associated with the revoked `KeyPairResource` is removed using the `DidDocumentService`. +- The DID document is published. + +## 3.4. Participant Context + +The `Participant Context` module is responsible for managing the lifecycle of participant contexts and their +contained resources. Each context is tied to a `dataspace participant id`. + +The module: + +- Provides persistent storage for created contexts. +- Generates and stores API access tokens +- Emits context events for the following lifecycle events that other systems can listen for: + - CREATED + - ACTIVATED + - DESTROYED + +## 3.5. Verifiable Credential Module + +The Verifiable Credential module manages VC resources. All resource operations are scoped to the current participant +context. The module contains the following services. + +### 3.5.1.CredentialService + +The `CredentialService` is responsible for credentials CRUD operations. It delegates to a `CredentialStore` for +persistence. + +All CRUD operations publish events. + +### 3.5.2. VerifiablePresentationGenerator + +The `VerifiablePresentationGenerator` is responsible for generating Verifiable Presentations (VP) from a (VC). + +### 3.5.3. CredentialRequestManager + +The `CredentialRequestManager` manages VC resources. It dispatches client requests (via the +EDC `RemoteMessageDispatcher`) using the `Credential Issuance Protocol`. It operates across participant contexts and is +updates resource states. For example, the `CredentialRequestManager` handles VC issuance requests and re-issuance +requests. + +> The `CredentialRequestManager` is cluster-aware and will guarantee operations are not duplicated across nodes via +> locking. + +The `CredentialRequestManager` will delegate to the EDC `PolicyEngine` to ensure the `issuancePolicy` +and `reissuancePolicy` are evaluated when a self-issued ID token is generated as part of an issuer request. + +Request operations publish events. + +### 3.5.4. CredentialStore + +The `CredentialStore` (de)serializes VC resources from persistent storage. + +## 3.6. DID Module + +The DID module manages DIDs and DID documents for participant contexts. The DID module may be disabled for a +particular participant context. The DID module makes use of the EDC `Identity DID Core` extension. + +### 3.6.1. DidDocumentPublisher + +The `DidDocumentPublisher` is responsible for generating, provisioning and deprovisioning DID documents to +a `Verifiable Data Registry` (VDR) such as a CDN that serves a Web domain. The publisher is a state machine that can +asynchronously transition as follows: + +- **Publish**: GENERATED -> PROVISIONING -> PROVISIONED +- **Unpublish**: PROVISIONED -> DEPROVISIONING -> DEPROVISIONED +- **Republish**: PROVISIONED -> GENERATED -> PROVISIONING -> PROVISIONED + +All operations publish events. + +The `DidDocumentPublisher` delegates to extensions for handling provisioning to VDRs. + +### 3.6.2. DidDocumentService + +The `DidDocumentService` returns a **managed** DID document to the requesting client. Note that it _**does not**_ +resolve foreign DID documents. Note also this service is intended for internal use. DID resolution should be performed +through specific DID methods that work directly with a VDR. + +## 3.7. Auth/Permission Module + +The `Auth/Permission` module includes services that delegate to the EDC `PolicyEngine` for access control. + +## 3.8. Key Pair module + +The `Key Pair` module is manages `KeyPairResources.` it does this by using database storage for queryable properties and +a secure vault for sensitive material (private keys). The Key Pair module will be shared with the `Issuer Service`. + +## 3.9. Crypto Core Library + +The `crypto core library` is part of the core EDC and will be used by Identity Hub services. + +## 3.10. Identity DID Core Module + +This module is part of the core EDC and will be used by Identity Hub services. + + + + + + + + + + + diff --git a/docs/developer/architecture/identity-trust-protocol/identity.hub.modules.png b/docs/developer/architecture/identity-trust-protocol/identity.hub.modules.png new file mode 100644 index 0000000000000000000000000000000000000000..9839d6c26e29552f7adbc8141609daf5f2fccf30 GIT binary patch literal 34969 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 literal 0 HcmV?d00001 diff --git a/docs/developer/architecture/identity-trust-protocol/identity.hub.modules.puml b/docs/developer/architecture/identity-trust-protocol/identity.hub.modules.puml new file mode 100644 index 000000000..77787ed36 --- /dev/null +++ b/docs/developer/architecture/identity-trust-protocol/identity.hub.modules.puml @@ -0,0 +1,58 @@ +@startuml +!pragma layout smetana + +!include ../styles/diagram.styles.puml + +hide stereotype + +skinparam component { + backgroundColor<> #9ee4aa + borderColor<> #185522 + backgroundColor<> #FFFFFF + backgroundColor<> Green +} + +skinparam frame { + backgroundColor<> #1F6F2C +} + +title Identity Hub Modules + +frame "APIs" { + component "Hub API" as HubApi <> + component "Management API" as ManagementApi <> +} + +component "Aggregate Services Module" as AggregateServices +component "VC Module" as VcModule +component "DID Module" as DidModule +component "Auth/Permission Module" as AuthModule +component "Participant Context \nModule" as PcModule + +frame "Shared Modules" { + component "Identity DID Core" as DidCore <> + component "KeyPair Module" as KpModule <> +} + +frame "Libraries" { + component "Crypto Core Library" as CryptoCore <> +} + +AggregateServices -up-> HubApi +AggregateServices -up-> ManagementApi + +AuthModule -up-> AggregateServices + +PcModule -up-> AuthModule +PcModule -up-> AggregateServices + +KpModule -up-> AggregateServices +CryptoCore -up-> KpModule + +VcModule -up-> AggregateServices + +DidModule -up->AggregateServices +DidCore -up-> DidModule + + +@enduml diff --git a/docs/developer/architecture/styles/diagram.styles.puml b/docs/developer/architecture/styles/diagram.styles.puml new file mode 100644 index 000000000..911f758dc --- /dev/null +++ b/docs/developer/architecture/styles/diagram.styles.puml @@ -0,0 +1,83 @@ + + +!$blue = "#066DFA" +!$gray = "#98A3AB" +!$lightGray = "#F4F5F9" +!$darkGray = "#747C94" +!$green = "#24DCC1" +!$blueFill = "#E4EBFA" +!$greenFill = "#CFFBDE" +!$borderRed = "#F40000" +!$lightRed = "#FDCCCC" + +skinparam sequence { +ArrowColor Black +LifeLineBorderColor 000000 +LifeLineBackgroundColor #A9DCDF +} + +skinparam roundCorner 10 + +skinparam ArrowColor $darkGray +skinparam ArrowHeadColor $darkGray +skinparam LifeLineBackgroundColor $darkGray + + +skinparam CloudBackgroundColor $lightGray +skinparam CloudBorderColor $gray +skinparam CloudFontColor Black + +skinparam ArtifactBackgroundColor $lightGray +skinparam ArtifactBorderColor $gray + +skinparam ComponentBackgroundColor $blueFill +skinparam ComponentBorderColor $blue +skinparam ComponentFontColor Black + +skinparam DatabaseBackgroundColor $greenFill +skinparam DatabaseBorderColor $green +skinparam DatabaseFontColor Black + +skinparam ParticipantBorderColor $blue +skinparam ParticipantBackgroundColor $blueFill +skinparam SequenceGroupBorderColor #DADADA +skinparam SequenceGroupHeaderFontColor #898A89 +skinparam BoxPadding 20 +skinparam ParticipantPadding 10 + +skinparam AgentBackgroundColor White +skinparam AgentBackgroundColor $blueFill +skinparam AgentBorderColor #066DFA +skinparam noteBorderColor FEF3BF +skinparam SequenceBoxBorderColor F2F2F2 +skinparam shadowing false +skinparam AgentBorderThickness 1 + +skinparam NoteBackgroundColor FEFFDD + +skinparam FrameBackgroundColor $lightGray +skinparam FrameBorderColor $lightGray +skinparam FrameFontColor $darkGray + +skinparam ActivityBackgroundColor $blueFill + +skinparam StateBackgroundColor $blueFill +skinparam StateBorderColor $blue +skinparam StateStartColor $blue +skinparam StateEndColor $blue +skinparam StateFontSize 12 + +skinparam ActivityBackgroundColor $blueFill +skinparam ActivityBorderColor $blue +skinparam ActivityEndColor $blue +skinparam ActivityStartColor $blue +skinparam ActivityDiamondBackgroundColor $lightGray +skinparam ActivityDiamondBorderColor $blue + +skinparam PartitionBackgroundColor $lightGray +skinparam PartitionBorderColor $darkGray +skinparam PartitionFontColor $darkGray + +skinparam ClassAttributeFontColor Black +skinparam ClassBorderColor $blue +skinparam ClassBackgroundColor $blueFill