| Crates.io | tap-agent |
| lib.rs | tap-agent |
| version | 0.4.0 |
| created_at | 2025-05-12 08:22:32.358074+00 |
| updated_at | 2025-06-17 07:34:16.404976+00 |
| description | Rust implementation of the Transaction Authorization Protocol (TAP) |
| homepage | |
| repository | https://github.com/TransactionAuthorizationProtocol/tap-rs |
| max_upload_size | |
| id | 1670206 |
| size | 711,388 |
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).
The TAP Agent serves as the foundation for secure communication in the TAP ecosystem, enabling entities to:
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
The TAP Agent supports environment variables to control storage location, which is particularly useful for testing and development:
TAP_HOME: Sets the TAP home directory (replaces ~/.tap)TAP_TEST_DIR: Sets a test directory where .tap will be createdTAP_ROOT: Alternative to TAP_HOME, used by some componentsPriority order:
TAP_HOME (highest priority)TAP_ROOTTAP_TEST_DIR~/.tap (default)This ensures that tests, examples, and benchmarks can run in isolated environments without affecting your production ~/.tap directory.
This architecture follows clean separation of concerns principles:
The Agent trait defines the core interface for TAP agents, with methods for:
The Agent implementation provides a production-ready implementation of this trait with:
KeyManager for cryptographic operationsThe AgentKey trait hierarchy provides a flexible abstraction for cryptographic keys:
AgentKey - Base trait with core properties (key ID, DID, key type)SigningKey - Extends AgentKey with capabilities for creating JWS signaturesVerificationKey - Trait for verifying signatures (can be implemented by public keys)EncryptionKey - Extends AgentKey with capabilities for creating JWE encryptionsDecryptionKey - Extends AgentKey with capabilities for decrypting JWEsThe LocalAgentKey implementation provides a concrete implementation that:
This trait-based approach enables:
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 the did:key method (Ed25519, P-256, Secp256k1)WebResolver - A resolver for the did:web method with HTTP resolutionMultiResolver - A resolver that manages multiple method-specific resolversThe system includes advanced features like:
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 metadataThe 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:
The TAP Agent can be created in multiple ways depending on your needs. Here are the main approaches:
The builder pattern provides a clean, fluent interface for creating agents:
use tap_agent::{Agent, AgentBuilder, DefaultKeyManager};
use std::sync::Arc;
// Create a key manager
let key_manager = Arc::new(DefaultKeyManager::new());
// Generate a new key or load an existing one
let key = key_manager.generate_key(DIDGenerationOptions {
key_type: KeyType::Ed25519,
})?;
// Build the agent with the generated key
let agent = AgentBuilder::new(key.did)
.with_debug(true)
.with_timeout(30)
.with_security_mode("SIGNED".to_string())
.build(key_manager);
Load keys from the default storage location (~/.tap/keys.json). Keys can be referenced by their DID or label:
use tap_agent::Agent;
// Use a stored key with a specific DID
let agent = Agent::from_stored_keys(
Some("did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".to_string()),
true
).await?;
// Or use a stored key by its label
let agent = Agent::from_stored_keys(
Some("production-key".to_string()),
true
).await?;
// Or use the default key from storage
let agent = Agent::from_stored_keys(None, true).await?;
Create an agent with a temporary key that is not persisted:
use tap_agent::{DefaultKeyManager, AgentBuilder};
use std::sync::Arc;
// Create a key manager
let key_manager = Arc::new(DefaultKeyManager::new());
// Generate a random key
let key = key_manager.generate_key(DIDGenerationOptions {
key_type: KeyType::Ed25519,
})?;
// Create an agent with the ephemeral key
let agent = AgentBuilder::new(key.did.clone())
.with_debug(true)
.build(key_manager);
println!("Created ephemeral agent with DID: {}", key.did);
For complete control over the agent configuration:
use tap_agent::{Agent, AgentConfig, DefaultKeyManager, KeyManagerPacking, Secret, SecretMaterial, SecretType};
use std::sync::Arc;
// Create agent configuration
let config = AgentConfig::new("did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".to_string());
// Set up a key manager
let mut key_manager = DefaultKeyManager::new();
// Add a secret to the key manager
let secret = Secret {
id: "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".to_string(),
type_: SecretType::JsonWebKey2020,
secret_material: SecretMaterial::JWK {
private_key_jwk: serde_json::json!({
"kty": "OKP",
"crv": "Ed25519",
"x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
"d": "nWGxne_9WmC6hEr-BQh-uDpW6n7dZsN4c4C9rFfIz3Yh",
"kid": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#keys-1"
}),
},
};
key_manager.add_secret(&secret.id, secret)?;
// Create the agent
let agent = Agent::new(config, Arc::new(key_manager));
The agent provides a flexible API for sending messages to one or more recipients:
use tap_agent::Agent;
use tap_msg::message::Transfer;
use tap_caip::AssetId;
use tap_msg::Participant;
use std::str::FromStr;
use std::collections::HashMap;
// Create a transfer message
let transfer = Transfer {
transaction_id: uuid::Uuid::new_v4().to_string(),
asset: AssetId::from_str("eip155:1/erc20:0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(),
originator: Participant {
id: "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".to_string(),
role: Some("originator".to_string()),
policies: None,
leiCode: None,
name: None,
},
beneficiary: Some(Participant {
id: "did:key:z6MkhFvVnYxkqLNEiWQmUwhQuVpXiCfNmRUVi5yZ4Cg9w15k".to_string(),
role: Some("beneficiary".to_string()),
policies: None,
leiCode: None,
name: None,
}),
amount: "100.0".to_string(),
agents: vec![],
settlement_id: None,
memo: Some("Test transfer".to_string()),
metadata: HashMap::new(),
};
// Option 1: Pack a message without delivery (for manual handling)
let recipient_did = "did:key:z6MkhFvVnYxkqLNEiWQmUwhQuVpXiCfNmRUVi5yZ4Cg9w15k";
let (packed_message, _) = agent.send_message(&transfer, vec![recipient_did], false).await?;
// You can now manually send the packed message however you want
// Option 2: Automatic delivery to service endpoints
let (packed_message, delivery_results) = agent.send_message(&transfer, vec![recipient_did], true).await?;
// Option 3: Send to multiple recipients
let recipients = vec![
"did:key:z6MkhFvVnYxkqLNEiWQmUwhQuVpXiCfNmRUVi5yZ4Cg9w15k",
"did:web:example.com"
];
let (packed_message, delivery_results) = agent.send_message(&transfer, recipients, true).await?;
// Check delivery results
for result in delivery_results {
if let Some(status) = result.status {
println!("Delivery status: {}", status);
} else if let Some(error) = &result.error {
println!("Error delivering to {}: {}", result.did, error);
}
}
// You can also look up service endpoints manually
if let Ok(Some(endpoint)) = agent.get_service_endpoint(recipient_did).await {
println!("Found service endpoint: {}", endpoint);
// Use the endpoint for custom delivery logic
}
The send_message method provides flexibility with three parameters:
TapMessageBody)The method returns:
The agent provides a simple API for unpacking and validating received messages:
use tap_agent::Agent;
use tap_msg::message::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(packed_message).await?;
// Now you can access the typed message content
println!("Received transfer:");
println!(" Transaction ID: {}", transfer.transaction_id);
println!(" Asset: {}", transfer.asset);
println!(" Amount: {}", transfer.amount);
println!(" From: {}", transfer.originator.id);
if let Some(beneficiary) = &transfer.beneficiary {
println!(" To: {}", beneficiary.id);
}
// 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.
The agent provides flexible DID resolution capabilities:
use tap_agent::did::MultiResolver;
use std::sync::Arc;
// Create a resolver with built-in support for did:key and did:web
let resolver = MultiResolver::default();
let resolver = Arc::new(resolver);
// Resolve any supported DID to its DID document
let did_doc = resolver.resolve("did:web:example.com").await?;
if let Some(doc) = did_doc {
println!("Resolved DID: {}", doc.id);
// Check verification methods
for vm in &doc.verification_method {
println!("Verification method: {}", vm.id);
println!(" Type: {:?}", vm.type_);
println!(" Controller: {}", vm.controller);
}
// Check authentication methods
if !doc.authentication.is_empty() {
println!("Authentication methods:");
for auth in &doc.authentication {
println!(" {}", auth);
}
}
// Check key agreement methods
if !doc.key_agreement.is_empty() {
println!("Key agreement methods:");
for ka in &doc.key_agreement {
println!(" {}", ka);
}
}
// Check service endpoints
if !doc.service.is_empty() {
println!("Service endpoints:");
for (i, service) in doc.service.iter().enumerate() {
println!(" [{}] ID: {}", i+1, service.id);
println!(" Type: {}", service.type_);
println!(" Endpoint: {}", service.service_endpoint);
}
}
}
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.
You can implement and register custom DID method resolvers to extend the agent's capabilities:
use tap_agent::did::{DIDMethodResolver, MultiResolver, DIDDoc, VerificationMethod};
use tap_agent::did::{VerificationMethodType, VerificationMaterial};
use async_trait::async_trait;
use tap_agent::error::Result;
#[derive(Debug)]
struct CustomResolver;
impl CustomResolver {
fn new() -> Self {
Self
}
}
#[async_trait]
impl DIDMethodResolver for CustomResolver {
fn method(&self) -> &str {
"example" // For did:example:123
}
async fn resolve_method(&self, did: &str) -> Result<Option<DIDDoc>> {
// Validate DID format
if !did.starts_with("did:example:") {
return Ok(None);
}
// Extract ID portion
let id_part = &did[12..]; // Skip "did:example:"
// Create a simple verification method
let vm_id = format!("{}#keys-1", did);
let vm = VerificationMethod {
id: vm_id.clone(),
type_: VerificationMethodType::Ed25519VerificationKey2018,
controller: did.to_string(),
verification_material: VerificationMaterial::Base58 {
public_key_base58: format!("custom-key-for-{}", id_part),
},
};
// Create a DID document
let doc = DIDDoc {
id: did.to_string(),
verification_method: vec![vm],
authentication: vec![vm_id.clone()],
key_agreement: vec![],
assertion_method: vec![],
capability_invocation: vec![],
capability_delegation: vec![],
service: vec![],
};
Ok(Some(doc))
}
}
// Create a resolver with the default resolvers
let mut resolver = MultiResolver::default();
// Register the custom resolver
resolver.register_method("example", CustomResolver::new());
// Now you can resolve did:example: DIDs
let doc = resolver.resolve("did:example:123").await?;
You can implement resolvers for any DID method, including:
The resolver will automatically route DID resolution requests to the appropriate method resolver based on the DID prefix.
The tap-agent crate implements several security features:
For production use, it's recommended to:
DebugSecretsResolver that integrates with a secure key management systemThe tap-agent crate integrates with other components in the TAP ecosystem:
The tap-agent crate is designed for high performance, with benchmarks showing:
Benchmarks can be run with:
cargo bench --bench agent_benchmark
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.
The CLI tool can be installed in several ways:
# From crates.io (recommended for most users)
cargo install tap-agent
# From the repository (if you have it cloned)
cargo install --path tap-agent
# Build without installing
cargo build --package tap-agent
After installation, the following commands will be available:
tap-agent-cli - Command-line tool for DID and key managementAfter installation, you can use the tap-agent-cli command to manage DIDs and keys. Here's a complete reference of available commands:
The generate command creates new DIDs with different key types and methods:
# Generate a did:key with Ed25519 (default)
tap-agent-cli generate
# Specify method and key type
tap-agent-cli generate --method key --key-type ed25519
tap-agent-cli generate --method key --key-type p256
tap-agent-cli generate --method key --key-type secp256k1
# Generate a did:web for a domain
tap-agent-cli generate --method web --domain example.com
# Save outputs to files
tap-agent-cli generate --output did.json --key-output key.json
# Save key to default storage (~/.tap/keys.json) and set as default
tap-agent-cli generate --save --default
# Generate with a custom label (instead of default agent-1, agent-2, etc.)
tap-agent-cli generate --save --label "my-signing-key"
tap-agent-cli generate --save --label "production-key" --default
The lookup command resolves DIDs to their DID documents:
# Look up a DID and display its DID document
tap-agent-cli lookup did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
# Look up a DID and save the document to a file
tap-agent-cli lookup did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK --output did-document.json
# Look up WebDIDs
tap-agent-cli lookup did:web:example.com
tap-agent-cli lookup did:web:example.com:path:to:resource
The resolver supports the following DID methods by default:
did:key - Resolves DIDs based on public keysdid:web - Resolves DIDs from web domainsThe keys command manages stored keys:
# List all stored keys (shows labels, DIDs, and key types)
tap-agent-cli keys list
# View details for a specific key (by DID or label)
tap-agent-cli keys view did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
tap-agent-cli keys view "my-signing-key"
tap-agent-cli keys view "agent-1"
# Set a key as the default (by DID or label)
tap-agent-cli keys set-default did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
tap-agent-cli keys set-default "production-key"
# Delete a key (by DID or label, with confirmation)
tap-agent-cli keys delete did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
tap-agent-cli keys delete "test-key"
# Delete a key (without confirmation)
tap-agent-cli keys delete "old-key" --force
# Relabel an existing key
tap-agent-cli keys relabel "agent-1" "development-key"
tap-agent-cli keys relabel did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK "new-label"
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.
The import command imports existing keys:
# Import a key from a file
tap-agent-cli import key.json
# Import and set as default
tap-agent-cli import key.json --default
The pack command securely packs a plaintext DIDComm message for transmission:
# Pack a message with the default security mode (signed)
tap-agent-cli pack --input plaintext.json --output packed.json
# Pack using a specific security mode
tap-agent-cli pack --input plaintext.json --mode plain
tap-agent-cli pack --input plaintext.json --mode signed
tap-agent-cli pack --input plaintext.json --mode authcrypt --recipient did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
# Specify sender and recipient DIDs
tap-agent-cli pack --input plaintext.json --sender did:key:z6Mky... --recipient did:key:z6Mkh...
# Display the packed message in the console (no output file)
tap-agent-cli pack --input plaintext.json
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, or authcrypt (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)The unpack command decrypts and verifies packed DIDComm messages:
# Unpack a message using the default key
tap-agent-cli unpack --input packed.json --output unpacked.json
# Specify a recipient DID (whose key should be used for decryption)
tap-agent-cli unpack --input packed.json --recipient did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
# Display the unpacked message in the console (no output file)
tap-agent-cli unpack --input packed.json
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)# Display general help
tap-agent-cli --help
# Display help for a specific command
tap-agent-cli generate --help
tap-agent-cli lookup --help
tap-agent-cli keys --help
tap-agent-cli import --help
tap-agent-cli pack --help
tap-agent-cli unpack --help
# Display help for a subcommand
tap-agent-cli keys delete --help
For did:web, you'll need to:
tap-agent-cli generate --method web --domain yourdomain.com --output did.jsondid:web:example.com: https://example.com/.well-known/did.jsondid:web:example.com:path:to:resource: https://example.com/path/to/resource/did.jsonService 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 fn work_with_service_endpoint(agent: &DefaultAgent, did: &str, message: &Transfer) -> Result<()> {
// Method 1: Get the service endpoint URL directly
match agent.get_service_endpoint(did).await? {
Some(endpoint) => {
println!("Found service endpoint for {}: {}", did, endpoint);
// Pack the message first (if you want to handle delivery manually)
let (packed_message, _) = agent.send_message(message, vec![did], false).await?;
// Now you can manually send to the endpoint:
// let client = reqwest::Client::new();
// let response = client.post(endpoint)
// .header("Content-Type", "application/didcomm-encrypted+json")
// .body(packed_message)
// .send()
// .await?;
},
None => println!("No service endpoint found for {}", did),
}
// Method 2: Let the agent handle delivery automatically
// The boolean parameter (true) tells the agent to attempt automatic delivery
let (_, delivery_results) = agent.send_message(message, vec![did], true).await?;
// Check the delivery results
for result in delivery_results {
if let Some(status) = result.status {
println!("Delivery to {} resulted in status: {}", result.did, status);
} else if let Some(error) = &result.error {
println!("Delivery to {} failed: {}", result.did, error);
}
}
Ok(())
}
The TAP Agent can work with different types of service endpoints defined in DID documents:
DIDCommMessaging endpoints (prioritized) - Specifically designed for secure message exchange:
{
"service": [{
"id": "did:example:123#didcomm",
"type": "DIDCommMessaging",
"serviceEndpoint": {
"uri": "https://example.com/didcomm",
"accept": ["didcomm/v2"],
"routingKeys": []
}
}]
}
Other endpoint types - Any service endpoint type that provides a URL:
{
"service": [{
"id": "did:example:123#agent",
"type": "TapAgent",
"serviceEndpoint": "https://agent.example.com/tap"
}]
}
Simple string endpoints - Basic URL string endpoints:
{
"service": [{
"id": "did:example:123#messaging",
"type": "MessagingService",
"serviceEndpoint": "https://example.com/messages"
}]
}
The agent supports multiple endpoint formats and follows these resolution rules:
type = "DIDCommMessaging" firstThe 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 {
asset: AssetId::from_str("eip155:1/erc20:0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(),
originator: create_originator(),
beneficiary: Some(create_beneficiary()),
amount: "100.0".to_string(),
// Other fields...
transaction_id: uuid::Uuid::new_v4().to_string(),
};
// The third parameter (true) enables automatic delivery
let (packed_message, delivery_results) =
agent.send_message(&transfer, vec![recipient_did], true).await?;
// Example 2: Send to multiple recipients
let recipients = vec!["did:web:example.com", "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"];
let (packed_message, delivery_results) =
agent.send_message(&transfer, recipients, true).await?;
// Process delivery results
for result in &delivery_results {
if let Some(status) = result.status {
if status >= 200 && status < 300 {
println!("✓ Successfully delivered to {} (status {})", result.did, status);
} else {
println!("! Delivery to {} failed with status {}", result.did, status);
}
} else if let Some(error) = &result.error {
println!("✗ Error delivering to {}: {}", result.did, error);
}
}
When automatic delivery is enabled, the agent follows this process:
Content-Type: application/didcomm-encrypted+jsonDeliveryResult structures 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.
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 (agent, did) = TapAgent::from_ephemeral_key().await?;
println!("TapAgent DID: {}", did);
// Option 2: Create an ephemeral agent with DefaultAgent
let (agent, did) = DefaultAgent::new_ephemeral()?;
println!("Agent DID: {}", did);
// This agent has a fully functional private key and can be used immediately
// for sending and receiving messages without any persistence
let (packed_message, _) = agent
.send_message(&transfer, vec!["did:example:recipient"], false)
.await?;
Ephemeral DIDs are useful for:
The crate provides several feature flags to customize functionality:
native (default): Enables native platform features including:
wasm: Enables WebAssembly support for browser environments:
When working with different environments:
Note that did:web resolution requires the native feature to be enabled, as it depends on HTTP requests to fetch DID documents.
The tap-agent crate implements comprehensive cryptographic operations with support for:
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 dataLocalAgentKey Implementation - Concrete implementation of the AgentKey traits:
JWS (JSON Web Signatures) - Standards-compliant digital signatures:
JWE (JSON Web Encryption) - Standards-compliant encryption:
The cryptographic implementations align with industry standards, allowing interoperability with other systems that support JWS and JWE formats.
This crate is licensed under the MIT License.