DIDComm V2 Plugin

DIDComm V2 implementation for TrustWeave, providing secure, private, and decentralized messaging using Decentralized Identifiers (DIDs).

Overview

This plugin implements the DIDComm V2 protocol specification, enabling:

  • Secure Messaging: Encrypted communication using ECDH-1PU (AuthCrypt)
  • Protocol Support: Issue Credential, Present Proof, Basic Message protocols
  • Message Threading: Support for conversation threads
  • Credential Exchange: Secure credential issuance and presentation

Features

  • JWM (JSON Web Message) format support
  • Message packing and unpacking
  • Protocol message builders (Issue Credential, Present Proof, Basic Message)
  • In-memory message storage
  • Thread-based message organization
  • Crypto:
    • Default path: DidCommCryptoDidcomm uses didcomm-java (org.didcommx:didcomm) for pack/unpack (AuthCrypt / ECDH-1PU per library defaults; forward(false) in the integration layer).
    • Secrets: You must supply a SecretResolver (private JWK material per kid) when calling DidCommFactory.createInMemoryService / createPacker.
    • Placeholder: DidCommFactory.createInMemoryServiceWithPlaceholderCrypto for local demos only (not interoperable).

Installation

Add the DIDComm plugin to your dependencies:

1
2
3
dependencies {
    implementation(project(":credentials:plugins:didcomm"))
}

Usage

Basic Setup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import org.trustweave.credential.didcomm.*
import org.trustweave.credential.didcomm.protocol.*
import org.trustweave.kms.*
import org.trustweave.did.*

val kms = InMemoryKeyManagementService()

// DID resolution function
val resolveDid: suspend (String) -> DidDocument? = { did ->
    // Your DID resolution logic
    resolveDidDocument(did)
}

// Build a SecretResolver (e.g. MapSecretResolver) with private keys for each agreement key `kid`
val secretResolver: org.didcommx.didcomm.secret.SecretResolver = TODO("populate MapSecretResolver or KMS-backed resolver")

// Create DIDComm service (didcomm-java crypto)
val didcomm = DidCommFactory.createInMemoryService(kms, resolveDid, secretResolver)

// Or, for non-cryptographic demos only:
// val didcomm = DidCommFactory.createInMemoryServiceWithPlaceholderCrypto(kms, resolveDid)

Sending a Basic Message

1
2
3
4
5
6
7
8
9
10
11
12
13
14
val message = BasicMessageProtocol.createBasicMessage(
    fromDid = "did:key:alice",
    toDid = "did:key:bob",
    content = "Hello, Bob!"
)

val messageId = didcomm.sendMessage(
    message = message,
    fromDid = "did:key:alice",
    fromKeyId = "did:key:alice#key-1",
    toDid = "did:key:bob",
    toKeyId = "did:key:bob#key-1",
    encrypt = true
)

Receiving a Message

1
2
3
4
5
6
7
8
9
val received = didcomm.receiveMessage(
    packedMessage = packedMessageJson,
    recipientDid = "did:key:bob",
    recipientKeyId = "did:key:bob#key-1",
    senderDid = "did:key:alice"
)

val content = BasicMessageProtocol.extractContent(received)
println("Received: $content")

Credential Exchange

Issuing a Credential

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 1. Create credential offer
val preview = CredentialProtocol.CredentialPreview(
    attributes = listOf(
        CredentialProtocol.CredentialAttribute("name", "Alice"),
        CredentialProtocol.CredentialAttribute("email", "alice@example.com")
    )
)

val offer = CredentialProtocol.createCredentialOffer(
    fromDid = issuerDid,
    toDid = holderDid,
    credentialPreview = preview
)

didcomm.sendMessage(offer, issuerDid, issuerKeyId, holderDid, holderKeyId)

// 2. Holder requests credential
val request = CredentialProtocol.createCredentialRequest(
    fromDid = holderDid,
    toDid = issuerDid,
    thid = offer.id
)

didcomm.sendMessage(request, holderDid, holderKeyId, issuerDid, issuerKeyId)

// 3. Issuer issues credential
val credential = // ... create verifiable credential
val issue = CredentialProtocol.createCredentialIssue(
    fromDid = issuerDid,
    toDid = holderDid,
    credential = credential,
    thid = request.id
)

didcomm.sendMessage(issue, issuerDid, issuerKeyId, holderDid, holderKeyId)

// 4. Extract credential from message
val receivedIssue = didcomm.receiveMessage(packedIssue, holderDid, holderKeyId, issuerDid)
val receivedCredential = CredentialProtocol.extractCredential(receivedIssue)

Proof Presentation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 1. Verifier requests proof
val proofRequest = ProofProtocol.ProofRequest(
    name = "Proof of Age",
    requestedAttributes = mapOf(
        "age" to ProofProtocol.RequestedAttribute(
            name = "age",
            restrictions = listOf(
                ProofProtocol.AttributeRestriction(
                    schemaId = "https://example.com/schemas/age"
                )
            )
        )
    )
)

val request = ProofProtocol.createProofRequest(
    fromDid = verifierDid,
    toDid = proverDid,
    proofRequest = proofRequest
)

didcomm.sendMessage(request, verifierDid, verifierKeyId, proverDid, proverKeyId)

// 2. Prover presents proof
val presentation = // ... create verifiable presentation
val presentationMsg = ProofProtocol.createProofPresentation(
    fromDid = proverDid,
    toDid = verifierDid,
    presentation = presentation,
    thid = request.id
)

didcomm.sendMessage(presentationMsg, proverDid, proverKeyId, verifierDid, verifierKeyId)

// 3. Extract presentation
val receivedPresentation = didcomm.receiveMessage(
    packedPresentation,
    verifierDid,
    verifierKeyId,
    proverDid
)
val presentation = ProofProtocol.extractPresentation(receivedPresentation)

Message Threading

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
val thid = "conversation-123"

val message1 = BasicMessageProtocol.createBasicMessage(
    fromDid = "did:key:alice",
    toDid = "did:key:bob",
    content = "First message",
    thid = thid
)

val message2 = BasicMessageProtocol.createBasicMessage(
    fromDid = "did:key:bob",
    toDid = "did:key:alice",
    content = "Reply",
    thid = thid
)

// Retrieve all messages in thread
val threadMessages = didcomm.getThreadMessages(thid)

Architecture

Components

  • DidCommMessage: Core message model following JWM format
  • DidCommEnvelope: Encrypted message envelope
  • DidCommCrypto: Placeholder cryptographic operations (non-standard; development only)
  • DidCommCryptoDidcomm: Pack/unpack via org.didcommx.didcomm.DIDComm
  • DidCommCryptoAdapter: Selects placeholder vs didcomm-java (useDidcommJava + SecretResolver)
  • DidCommPacker: Message packing/unpacking
  • DidCommService: Service interface for messaging
  • Protocol Helpers: Builders for common protocols

Message Flow

  1. Packing: Message → JSON → Encryption → Envelope
  2. Transport: Envelope sent via HTTP, WebSocket, etc.
  3. Unpacking: Envelope → Decryption → JSON → Message

Encryption

DIDComm V2 uses:

  • ECDH-1PU: Authenticated key agreement (AuthCrypt)
  • AES-256-GCM: Content encryption
  • AES-256-KW: Key wrapping

Implementation status

  • Interoperable encryption uses didcomm-java; DID documents are mapped from TrustWeave models (TrustWeaveDidDocMapper).
  • Mediators: the TrustWeave integration sets forward(false) on pack; enable forwarding in a custom layer if you use mediators.

Protocol Support

Issue Credential Protocol

  • offer-credential: Issuer offers credential
  • request-credential: Holder requests credential
  • issue-credential: Issuer issues credential
  • ack: Acknowledgment

Present Proof Protocol

  • request-presentation: Verifier requests proof
  • presentation: Prover presents proof
  • ack: Acknowledgment

Basic Message Protocol

  • message: Simple text messages

Production setup

  1. Dependency: The plugin already depends on org.didcommx:didcomm:0.3.2.
  2. Secrets: Implement SecretResolver so every kid used in encryption can be resolved to a Secret (typically VerificationMethodType.JSON_WEB_KEY_2020 + JWK including private d).
  3. DID documents: Resolve DidDocuments that list key agreement keys compatible with the sender (same curve family as didcomm-java expects, e.g. X25519 JsonWebKey2020 in tests).
  4. Factory:
    1
    
    val didcomm = DidCommFactory.createInMemoryService(kms, resolveDid, secretResolver)
    

See Integration Guide and STORAGE_AND_SECRET_RESOLVER for resolver patterns.

Limitations

  1. Message Delivery: The in-memory service doesn’t actually deliver messages. Implement HTTP/WebSocket delivery for real-world use.

  2. Key Management: Ensure proper key management for encryption keys (private key access required).

  3. Error Handling: Add comprehensive error handling for edge cases.

  4. Testing: Expand test coverage for cryptographic operations.

References

  • DIDComm V2 Specification](https://didcomm.org/book/v2/)
  • JWM Specification](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-jwsreq-26)
  • Issue Credential Protocol](https://github.com/decentralized-identity/waci-presentation-exchange)
  • Present Proof Protocol](https://github.com/decentralized-identity/presentation-exchange)

License

Part of TrustWeave - see main project LICENSE file.


This site uses Just the Docs, a documentation theme for Jekyll.