Reusable Components Across Protocols
Overview
Many components implemented for DIDComm are reusable across other protocols (OIDC4VCI, CHAPI, etc.). This document outlines what’s reusable and how to use it.
✅ Fully Reusable Components
1. Encryption & Key Management
Location: credentials/credential-core/src/main/kotlin/com/trustweave/credential/
KeyEncryption (crypto/secret/encryption/KeyEncryption.kt)
- Reusable: ✅ Yes - Generic AES-256-GCM encryption
- Usage: Any protocol that needs to encrypt keys locally
- Example:
1 2
val keyEncryption = KeyEncryption(masterKey) val encrypted = keyEncryption.encrypt(keyData)
LocalKeyStore (crypto/secret/LocalKeyStore.kt)
- Reusable: ✅ Yes - Generic key storage interface
- Usage: OIDC4VCI, CHAPI, or any protocol using keys
- Example:
1 2
val keyStore: LocalKeyStore = EncryptedFileLocalKeyStore(...) keyStore.store("key-id", secret)
MessageEncryption (storage/encryption/MessageEncryption.kt)
- Reusable: ✅ Yes - Encrypts any data at rest
- Usage: OIDC4VCI offers, CHAPI messages, any protocol data
- Example:
1 2
val encryption = AesMessageEncryption(encryptionKey, keyVersion = 1) val encrypted = encryption.encrypt(messageBytes)
EncryptionKeyManager (storage/encryption/EncryptionKeyManager.kt)
- Reusable: ✅ Yes - Key versioning and rotation
- Usage: Any protocol needing encryption key management
- Example:
1 2
val keyManager = InMemoryEncryptionKeyManager() val newVersion = keyManager.rotateKey()
KeyRotationPolicy (crypto/rotation/KeyRotationPolicy.kt)
- Reusable: ✅ Yes - Any protocol using cryptographic keys
- Usage: OIDC4VCI, CHAPI, or any key-based protocol
- Example:
1 2
val policy = TimeBasedRotationPolicy(maxAgeDays = 90) val shouldRotate = policy.shouldRotate(keyId, metadata)
2. Storage Infrastructure
ProtocolMessage Interface (storage/ProtocolMessage.kt)
- Reusable: ✅ Yes - Generic message interface
- Usage: All protocols should implement this
- Example:
1 2 3 4 5
data class DidCommMessage(...) : ProtocolMessage { override val messageId: String get() = id override val messageType: String get() = type // ... implement other properties }
ProtocolMessageStorage Interface (storage/ProtocolMessageStorage.kt)
- Reusable: ✅ Yes - Generic storage interface
- Usage: All protocols can use this
- Example:
1 2
val storage: ProtocolMessageStorage<DidCommMessage> = PostgresMessageStorage(serializer, dataSource)
PostgresMessageStorage (storage/database/PostgresMessageStorage.kt)
- Reusable: ✅ Yes - Works with any ProtocolMessage
- Usage: DIDComm, OIDC4VCI, CHAPI, or any protocol
- Example:
1 2 3 4 5 6 7 8 9 10 11 12 13
// DIDComm val didCommStorage = PostgresMessageStorage( serializer = DidCommMessage.serializer(), dataSource = dataSource, tableName = "didcomm_messages" ) // OIDC4VCI val oidcStorage = PostgresMessageStorage( serializer = Oidc4VciOffer.serializer(), dataSource = dataSource, tableName = "oidc4vci_offers" )
3. Advanced Features
Message Archiving (storage/archive/)
- Reusable: ✅ Yes - Can archive any data
- Usage: OIDC4VCI offers, CHAPI requests, any protocol data
- Example:
1 2
val archiver = S3MessageArchiver(storage, s3Client, bucketName) val result = archiver.archiveMessages(policy)
Message Replication (storage/replication/ReplicationManager.kt)
- Reusable: ✅ Yes - Works with any ProtocolMessageStorage
- Usage: High availability for any protocol
- Example:
1 2 3 4 5
val replicationManager = ReplicationManager( primary = storage1, replicas = listOf(storage2, storage3), replicationMode = ReplicationMode.ASYNC )
Advanced Search (storage/search/)
- Reusable: ✅ Partially - PostgreSQL full-text search is generic
- Usage: Search any stored protocol messages
- Example:
1 2
val search = PostgresFullTextSearch(dataSource) val results = search.fullTextSearch("credential offer")
Message Analytics (storage/analytics/)
- Reusable: ✅ Partially - Analytics logic is generic
- Usage: Analyze traffic for any protocol
- Example:
1 2
val analytics = PostgresMessageAnalytics(dataSource) val stats = analytics.getStatistics(startTime, endTime)
🔄 Needs Abstraction (Currently DIDComm-Specific)
Storage Implementations
- Current:
PostgresDidCommMessageStorage,MongoDidCommMessageStorage - Solution: Use generic
PostgresMessageStorage<T>,MongoMessageStorage<T> - Status: ✅ Generic implementations created
Storage Interfaces
- Current:
DidCommMessageStorageusesDidCommMessage - Solution: Use generic
ProtocolMessageStorage<T> - Status: ✅ Generic interface created
❌ Not Reusable (Protocol-Specific)
DIDComm-Specific Components
- DidCommMessage Model - DIDComm-specific structure
- DidCommPacker - DIDComm packing/unpacking
- DidCommCrypto - DIDComm-specific encryption adapters
Usage Examples
OIDC4VCI with Generic Storage
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
// 1. Make OIDC4VCI offer implement ProtocolMessage
data class Oidc4VciOffer(
val id: String,
val type: String,
val issuerDid: String,
// ... other fields
) : ProtocolMessage {
override val messageId: String get() = id
override val messageType: String get() = type
override val from: String? get() = issuerDid
override val to: List<String> get() = listOf(holderDid)
// ... implement other properties
}
// 2. Use generic storage
val oidcStorage = PostgresMessageStorage(
serializer = Oidc4VciOffer.serializer(),
dataSource = dataSource,
tableName = "oidc4vci_offers",
encryption = AesMessageEncryption(encryptionKey)
)
// 3. Use encryption
val encryption = AesMessageEncryption(
encryptionKey = EncryptionKeyManager.generateRandomKey(),
keyVersion = 1
)
// 4. Use key rotation
val rotationPolicy = TimeBasedRotationPolicy(maxAgeDays = 90)
val rotationManager = KeyRotationManager(keyStore, kms, rotationPolicy)
CHAPI with Generic Storage
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
// 1. Make CHAPI offer implement ProtocolMessage
data class ChapiOffer(
val id: String,
val type: String,
val issuerDid: String,
// ... other fields
) : ProtocolMessage {
override val messageId: String get() = id
override val messageType: String get() = type
override val from: String? get() = issuerDid
override val to: List<String> get() = emptyList()
// ... implement other properties
}
// 2. Use generic storage
val chapiStorage = PostgresMessageStorage(
serializer = ChapiOffer.serializer(),
dataSource = dataSource,
tableName = "chapi_offers"
)
// 3. Use archiving
val archiver = S3MessageArchiver(chapiStorage, s3Client, bucketName)
val policy = AgeBasedArchivePolicy(maxAgeDays = 30)
archiver.archiveMessages(policy)
Migration Guide
For DIDComm
- Update DidCommMessage:
1 2 3 4
data class DidCommMessage(...) : ProtocolMessage { override val messageId: String get() = id // ... implement ProtocolMessage properties }
- Use Generic Storage:
1 2 3 4 5 6 7
val genericStorage = PostgresMessageStorage( serializer = DidCommMessage.serializer(), dataSource = dataSource, tableName = "didcomm_messages" ) val didCommStorage = DidCommMessageStorageAdapter(genericStorage)
For OIDC4VCI
- Create ProtocolMessage Implementation:
1
data class Oidc4VciOffer(...) : ProtocolMessage { ... }
- Use Generic Storage:
1 2 3 4 5
val storage = PostgresMessageStorage( serializer = Oidc4VciOffer.serializer(), dataSource = dataSource, tableName = "oidc4vci_offers" )
- Replace In-Memory Maps:
1 2 3 4 5
// Before private val offers = mutableMapOf<String, Oidc4VciOffer>() // After private val storage: ProtocolMessageStorage<Oidc4VciOffer> = ...
For CHAPI
- Create ProtocolMessage Implementation:
1
data class ChapiOffer(...) : ProtocolMessage { ... }
- Use Generic Storage:
1 2 3 4 5
val storage = PostgresMessageStorage( serializer = ChapiOffer.serializer(), dataSource = dataSource, tableName = "chapi_offers" )
Benefits
- Code Reuse: Share storage, encryption, and key management across protocols
- Consistency: Same storage patterns across all protocols
- Maintainability: Fix bugs once, benefit everywhere
- Features: Get archiving, replication, search, analytics for free
- Flexibility: Easy to add new protocols
Summary
✅ 6 Fully Reusable Components: Encryption, key management, rotation, archiving, replication, generic storage
✅ 4 Reusable with Abstraction: Storage implementations, search, analytics (now abstracted)
❌ 3 Protocol-Specific: DIDComm message models, packing, crypto adapters
All reusable components are now in credentials/credential-core and can be used by any protocol!