TAP Agent
The tap-agent crate implements the agent functionality for the Transaction Authorization Protocol (TAP), providing a secure and extensible framework for handling TAP messages, managing cryptographic operations, and resolving decentralized identifiers (DIDs).
Overview
The TAP Agent serves as the foundation for secure communication in the TAP ecosystem, enabling entities to:
- Establish secure identities using Decentralized Identifiers (DIDs)
- Exchange authenticated and encrypted messages with strong cryptographic guarantees
- Process and validate TAP protocol messages for compliant transfers and payments
- Manage cryptographic keys with support for multiple key types and algorithms
- Resolve DIDs across different methods with a pluggable resolver architecture
- Automatically deliver messages to service endpoints defined in DID documents
- Generate, store, and retrieve cryptographic keys for long-term use
Architecture
The tap-agent crate is designed with a modular architecture that separates concerns and allows for extensibility:
tap-agent
├── agent - Core agent implementation and traits
├── agent_key - Key abstraction for signing, verification, encryption, and decryption
├── config - Agent configuration parameters
├── cli - Command-line interface for DID and key management
├── crypto - Cryptographic operations and message security
├── did - DID resolution, generation, and validation
├── error - Error types and handling
├── key_manager - Key generation and management
├── local_agent_key - Concrete implementation of AgentKey traits
├── message - Message formatting and processing
├── message_packing - Message packing and unpacking utilities
├── storage - Persistent storage for keys and DIDs
This architecture follows clean separation of concerns principles:
- Core agent functionality is independent from specific cryptographic implementations
- DID resolution is pluggable with support for multiple methods
- Key management is abstracted to support different security approaches
- CLI tools provide user-friendly access to core functionality
Key Components
Agent
The Agent trait defines the core interface for TAP agents, with methods for:
- Retrieving the agent's DID
- Sending messages with appropriate security modes
- Finding service endpoints from DID documents
- Receiving and unpacking messages
- Validating message contents
The Agent implementation provides a production-ready implementation of this trait with:
- Multiple creation methods (builder pattern, from stored keys, ephemeral)
- Automatic service endpoint discovery and message delivery
- Configurable timeout and security settings
- Comprehensive logging for debugging
- Support for both native and WASM environments
- Integration with the
KeyManagerfor cryptographic operations
AgentKey
The AgentKey trait hierarchy provides a flexible abstraction for cryptographic keys:
AgentKey- Base trait with core properties (key ID, DID, key type)SigningKey- ExtendsAgentKeywith capabilities for creating JWS signaturesVerificationKey- Trait for verifying signatures (can be implemented by public keys)EncryptionKey- ExtendsAgentKeywith capabilities for creating JWE encryptionsDecryptionKey- ExtendsAgentKeywith capabilities for decrypting JWEs
The LocalAgentKey implementation provides a concrete implementation that:
- Stores key material locally (in memory or persistent storage)
- Supports multiple cryptographic algorithms (Ed25519, P-256, Secp256k1)
- Implements all the AgentKey-related traits for complete cryptographic functionality
- Works with the JWS/JWE standards for signatures and encryption
This trait-based approach enables:
- Clean separation between key management and cryptographic operations
- Support for different key storage mechanisms (local, HSM, remote, etc.)
- Flexible algorithm selection based on key types
- Simple interface for common cryptographic operations
DID Resolution
The DID resolution system supports multiple DID methods through a pluggable architecture:
SyncDIDResolver- A trait for resolving DIDs to DID documentsDIDMethodResolver- A trait for method-specific resolversKeyResolver- A resolver for thedid:keymethod (Ed25519, P-256, Secp256k1)WebResolver- A resolver for thedid:webmethod with HTTP resolutionMultiResolver- A resolver that manages multiple method-specific resolvers
The system includes advanced features like:
- Automatic conversion between Ed25519 verification keys and X25519 key agreement keys
- Support for JWK, Multibase, and Base58 key formats
- Integration with W3C compliant DID Document formats
- Caching for improved performance
Cryptographic Operations
The cryptographic system provides:
MessagePacker- A trait for packing and unpacking secure messagesDefaultMessagePacker- Standards-compliant implementation of JWS/JWE formatsBasicSecretResolver- A simple in-memory implementation for developmentKeyManager- A component for generating and managing cryptographic keysKeyStorage- Persistent storage for cryptographic keys and metadata
Security Modes
The agent supports different security modes for messages:
Plain- No security (for testing only)Signed- Messages are digitally signed but not encrypted (integrity protection)AuthCrypt- Messages are authenticated and encrypted (confidentiality + integrity)
Each security mode uses standards-compliant cryptographic approaches:
- Signing uses JSON Web Signatures (JWS) with algorithm selection based on key type
- Encryption uses JSON Web Encryption (JWE) with AES-GCM and ECDH-ES key agreement
- Message formats are compatible with broader DIDComm ecosystem standards
Features
- Secure Identity Management: Create and manage agent identities using DIDs
- Message Processing: Handle TAP message flows with proper validation
- DID Resolution: Resolve DIDs for message routing, key discovery, and service endpoints
- Cryptographic Operations: Sign, verify, encrypt, and decrypt messages
- Key Management: Generate, store, and retrieve cryptographic keys
- Message Delivery: Automatically deliver messages to service endpoints
- DID Generation CLI: Command-line tools for creating and managing DIDs and keys
- Ephemeral DIDs: Create temporary DIDs for testing or short-lived processes
- Asynchronous Processing: Process messages concurrently using Tokio
- WASM Support: Run in browser environments with WebAssembly
- Extensible DID Methods: Support for did:key and did:web, with architecture for adding more methods
- Performance Optimized: Benchmarked for high-throughput scenarios
- Native Cryptography: Direct implementation of cryptographic operations without external DIDComm dependencies
- Persistent Storage: Store keys securely for long-term use
- Multiple Key Types: Support for Ed25519, P-256, and Secp256k1 keys
- Standards-Compliant: Implementation follows W3C DID and IETF JWS/JWE standards
Usage Examples
Agent Creation
The TAP Agent can be created in multiple ways depending on your needs. Here are the main approaches:
1. Using Agent Builder (Recommended)
The builder pattern provides a clean, fluent interface for creating agents:
use ;
use Arc;
// Create a key manager
let key_manager = new;
// Generate a new key or load an existing one
let key = key_manager.generate_key?;
// Build the agent with the generated key
let agent = new
.with_debug
.with_timeout
.with_security_mode
.build;
2. Using Stored Keys
Load keys from the default storage location (~/.tap/keys.json):
use Agent;
// Use a stored key with a specific DID
let agent = from_stored_keys.await?;
// Or use the default key from storage
let agent = from_stored_keys.await?;
3. Using Ephemeral Keys
Create an agent with a temporary key that is not persisted:
use ;
use Arc;
// Create a key manager
let key_manager = new;
// Generate a random key
let key = key_manager.generate_key?;
// Create an agent with the ephemeral key
let agent = new
.with_debug
.build;
println!;
4. Manual Creation (Advanced)
For complete control over the agent configuration:
use ;
use Arc;
// Create agent configuration
let config = new;
// Set up a key manager
let mut key_manager = new;
// Add a secret to the key manager
let secret = Secret ;
key_manager.add_secret?;
// Create the agent
let agent = new;
Sending Messages
The agent provides a flexible API for sending messages to one or more recipients:
use Agent;
use Transfer;
use AssetId;
use Participant;
use FromStr;
use HashMap;
// Create a transfer message
let transfer = Transfer ;
// Option 1: Pack a message without delivery (for manual handling)
let recipient_did = "did:key:z6MkhFvVnYxkqLNEiWQmUwhQuVpXiCfNmRUVi5yZ4Cg9w15k";
let = agent.send_message.await?;
// You can now manually send the packed message however you want
// Option 2: Automatic delivery to service endpoints
let = agent.send_message.await?;
// Option 3: Send to multiple recipients
let recipients = vec!;
let = agent.send_message.await?;
// Check delivery results
for result in delivery_results
// You can also look up service endpoints manually
if let Ok = agent.get_service_endpoint.await
The send_message method provides flexibility with three parameters:
- The message to send (any type implementing
TapMessageBody) - A list of recipient DIDs
- A boolean indicating whether to attempt automatic delivery
The method returns:
- The packed message as a string (ready for transport)
- A vector of delivery results (when automatic delivery is requested)
Receiving Messages
The agent provides a simple API for unpacking and validating received messages:
use Agent;
use Transfer;
// Receive a message from some transport mechanism
let packed_message = "..."; // Received from HTTP, WebSockets, etc.
// Unpack and validate the message, converting to the expected type
let transfer: Transfer = agent.receive_message.await?;
// Now you can access the typed message content
println!;
println!;
println!;
println!;
println!;
if let Some = &transfer.beneficiary
// The agent automatically:
// 1. Verifies signatures (if the message is signed)
// 2. Decrypts content (if the message is encrypted)
// 3. Verifies the message is valid according to its type
// 4. Converts the message to the requested type
The agent handles the entire verification and decryption process, allowing you to focus on processing the message content rather than worrying about cryptographic details.
Both TapAgent and DefaultAgent implement the Agent trait, so the receiving API is the same regardless of which agent implementation you use.
Using DID Resolvers
The agent provides flexible DID resolution capabilities:
use MultiResolver;
use Arc;
// Create a resolver with built-in support for did:key and did:web
let resolver = default;
let resolver = new;
// Resolve any supported DID to its DID document
let did_doc = resolver.resolve.await?;
if let Some = did_doc
Supported DID Methods
The default resolver supports:
-
did:key - Self-contained DIDs with embedded public keys
did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK -
did:web - DIDs associated with web domains
did:web:example.com did:web:example.com:path:to:resource
You can extend the resolver with custom DID methods as shown in the next section.
Custom DID Method Resolver
You can implement and register custom DID method resolvers to extend the agent's capabilities:
use ;
use ;
use async_trait;
use Result;
;
// Create a resolver with the default resolvers
let mut resolver = default;
// Register the custom resolver
resolver.register_method;
// Now you can resolve did:example: DIDs
let doc = resolver.resolve.await?;
You can implement resolvers for any DID method, including:
- Blockchain-based DIDs (did:ethr, did:sol, etc.)
- Registry-based DIDs (did:ion, did:factom, etc.)
- Custom protocol DIDs for specific use cases
The resolver will automatically route DID resolution requests to the appropriate method resolver based on the DID prefix.
Security Considerations
The tap-agent crate implements several security features:
- Message Integrity: All messages can be digitally signed to ensure integrity
- Message Confidentiality: Messages can be encrypted for confidentiality
- Key Management: Proper key handling with separation of concerns
- DID Verification: Validation of DIDs and DID documents
- Secure Defaults: Secure defaults for message security modes
- Native Cryptography: Direct implementation of cryptographic algorithms using well-tested libraries
For production use, it's recommended to:
- Implement a custom
DebugSecretsResolverthat integrates with a secure key management system - Use proper key rotation and management practices
- Ensure secure transport for message exchange
- Regularly update dependencies to incorporate security fixes
Integration with Other TAP Components
The tap-agent crate integrates with other components in the TAP ecosystem:
- tap-msg: Uses message types and validation from tap-msg
- tap-caip: Validates chain-agnostic identifiers in messages
- tap-node: Provides the agent functionality for tap-node
- tap-http: Can be used with tap-http for HTTP-based secure messaging
- tap-wasm: Supports WASM bindings for browser environments
- tap-ts: Provides TypeScript bindings for the agent functionality
Performance
The tap-agent crate is designed for high performance, with benchmarks showing:
- Message packing/unpacking: Thousands of operations per second
- DID resolution: Fast caching of resolved DIDs
- Cryptographic operations: Optimized for common use cases
Benchmarks can be run with:
DID Generation CLI
The tap-agent crate includes a command-line interface (CLI) for generating and managing DIDs and keys. This makes it easy to create DIDs for testing, development, or production use.
Installation
The CLI tool can be installed in several ways:
# From crates.io (recommended for most users)
# From the repository (if you have it cloned)
# Build without installing
After installation, the following commands will be available:
tap-agent-cli- Command-line tool for DID and key management
Command Reference
After installation, you can use the tap-agent-cli command to manage DIDs and keys. Here's a complete reference of available commands:
Generate Command
The generate command creates new DIDs with different key types and methods:
# Generate a did:key with Ed25519 (default)
# Specify method and key type
# Generate a did:web for a domain
# Save outputs to files
# Save key to default storage (~/.tap/keys.json) and set as default
Lookup Command
The lookup command resolves DIDs to their DID documents:
# Look up a DID and display its DID document
# Look up a DID and save the document to a file
# Look up WebDIDs
The resolver supports the following DID methods by default:
did:key- Resolves DIDs based on public keysdid:web- Resolves DIDs from web domains
Keys Command
The keys command manages stored keys:
# List all stored keys
# View details for a specific key
# Set a key as the default
# Delete a key (with confirmation)
# Delete a key (without confirmation)
Keys are stored in ~/.tap/keys.json by default. This storage location is shared with other TAP tools like tap-http for consistent key management.
Import Command
The import command imports existing keys:
# Import a key from a file
# Import and set as default
Pack Command
The pack command securely packs a plaintext DIDComm message for transmission:
# Pack a message with the default security mode (signed)
# Pack using a specific security mode
# Specify sender and recipient DIDs
# Display the packed message in the console (no output file)
The pack command supports the following options:
--input, -i: The input file containing the plaintext DIDComm message (required)--output, -o: The output file to save the packed message (optional, displays in console if not provided)--sender, -s: The DID of the sender (optional, uses default key if not provided)--recipient, -r: The DID of the recipient (required for authcrypt mode, optional for other modes)--mode, -m: The security mode to use:plain,signed, orauthcrypt(default:signed)
Security modes:
plain: No security, message is sent as plaintext (use for testing only)signed: Message is digitally signed but not encrypted (integrity protection)authcrypt: Message is authenticated and encrypted (confidentiality and integrity)
Unpack Command
The unpack command decrypts and verifies packed DIDComm messages:
# Unpack a message using the default key
# Specify a recipient DID (whose key should be used for decryption)
# Display the unpacked message in the console (no output file)
The unpack command supports the following options:
--input, -i: The input file containing the packed DIDComm message (required)--output, -o: The output file to save the unpacked message (optional, displays in console if not provided)--recipient, -r: The DID of the recipient whose key should be used for unpacking (optional, uses default key if not provided)
Help and Documentation
# Display general help
# Display help for a specific command
# Display help for a subcommand
Using Generated DIDs
For did:web, you'll need to:
- Generate the DID using the CLI:
tap-agent-cli generate --method web --domain yourdomain.com --output did.json - Place the generated DID document at:
- For
did:web:example.com:https://example.com/.well-known/did.json - For
did:web:example.com:path:to:resource:https://example.com/path/to/resource/did.json
- For
Working with Service Endpoints
Service endpoints in DID documents provide URLs where the DID subject can receive messages. TAP Agents can automatically discover and use these endpoints for message delivery.
// Example: Find and use a service endpoint for a DID
async
Service Endpoint Types and Message Delivery
The TAP Agent can work with different types of service endpoints defined in DID documents:
-
DIDCommMessaging endpoints (prioritized) - Specifically designed for secure message exchange:
-
Other endpoint types - Any service endpoint type that provides a URL:
-
Simple string endpoints - Basic URL string endpoints:
The agent supports multiple endpoint formats and follows these resolution rules:
- Look for a service with
type = "DIDCommMessaging"first - Fall back to the first available service endpoint if no DIDCommMessaging endpoint is found
- Handle both object-style and string-style service endpoints
Automatic Message Delivery
The TAP Agent provides a seamless way to automatically deliver messages to service endpoints:
// Example 1: Send a message to a single recipient with automatic delivery
let transfer = Transfer ;
// The third parameter (true) enables automatic delivery
let =
agent.send_message.await?;
// Example 2: Send to multiple recipients
let recipients = vec!;
let =
agent.send_message.await?;
// Process delivery results
for result in &delivery_results
Delivery Process
When automatic delivery is enabled, the agent follows this process:
- Endpoint Resolution: Resolves each recipient's DID document to find service endpoints
- Message Packing: Packs the message with appropriate security for all recipients
- Delivery Attempts: For each recipient with a service endpoint:
- Sends the packed message via HTTP POST with
Content-Type: application/didcomm-encrypted+json - Records success (with HTTP status) or failure (with error message)
- Sends the packed message via HTTP POST with
- Result Collection: Returns a vector of
DeliveryResultstructures containing:did: The recipient DIDendpoint: The service endpoint URL usedstatus: HTTP status code (if successful)error: Error message (if failed)
The delivery mechanism is designed to be resilient - failures with one recipient won't prevent delivery attempts to others, and the operation won't fail even if no service endpoints are found.
Creating Ephemeral DIDs
For testing or short-lived processes, you can create ephemeral DIDs that exist only in memory:
// Option 1: Create an ephemeral agent with TapAgent (recommended, async API)
let = from_ephemeral_key.await?;
println!;
// Option 2: Create an ephemeral agent with DefaultAgent
let = new_ephemeral?;
println!;
// This agent has a fully functional private key and can be used immediately
// for sending and receiving messages without any persistence
let = agent
.send_message
.await?;
Ephemeral DIDs are useful for:
- Test environments where persistence isn't needed
- Short-lived agent instances that don't need to maintain state
- Situations where you want to minimize key management overhead
- Temporary identities for one-time operations
Feature Flags
The crate provides several feature flags to customize functionality:
-
native (default): Enables native platform features including:
- Tokio runtime for async functionality
- HTTP support for service endpoint delivery
- Complete DID resolution with HTTP requests for did:web
- File system access for key storage
-
wasm: Enables WebAssembly support for browser environments:
- Browser-compatible cryptography
- JavaScript integration
- Web-based service endpoint delivery
- Browser storage for keys
When working with different environments:
- For server applications: Use the default native feature
- For browser applications: Use the wasm feature
- For CLI tools: Use the default native feature
Note that did:web resolution requires the native feature to be enabled, as it depends on HTTP requests to fetch DID documents.
Cryptographic Support
The tap-agent crate implements comprehensive cryptographic operations with support for:
Key Abstraction
-
AgentKey Trait Hierarchy - Modular and extensible key capabilities:
AgentKey- Core trait with key ID, DID, and key type propertiesSigningKey- For creating digital signatures (JWS)VerificationKey- For verifying signaturesEncryptionKey- For encrypting data (JWE)DecryptionKey- For decrypting data
-
LocalAgentKey Implementation - Concrete implementation of the AgentKey traits:
- Stores key material in memory or persistent storage
- Implements all cryptographic operations locally
- Supports multiple key types and algorithms
- Compatible with JWS and JWE standards
Key Types
- Ed25519 - Fast digital signatures with small signatures (128 bits of security)
- P-256 - NIST standardized elliptic curve for ECDSA signatures and ECDH
- secp256k1 - Blockchain-compatible ECDSA signatures (used in Ethereum, Bitcoin)
Encryption & Signing
-
JWS (JSON Web Signatures) - Standards-compliant digital signatures:
- EdDSA algorithm for Ed25519 keys
- ES256 algorithm for P-256 keys
- ES256K algorithm for secp256k1 keys
-
JWE (JSON Web Encryption) - Standards-compliant encryption:
- AES-GCM (A256GCM) for authenticated encryption
- ECDH-ES+A256KW for key agreement and key wrapping
- Per-recipient encrypted content encryption keys (CEKs)
Security Modes
- Plain - No security (for testing only)
- Signed - Digital signatures without encryption (integrity protection)
- AuthCrypt - Authenticated encryption (confidentiality + integrity)
The cryptographic implementations align with industry standards, allowing interoperability with other systems that support JWS and JWE formats.
License
This crate is licensed under the MIT License.