tap-agent 0.1.0

Rust implementation of the Transaction Authorization Protocol (TAP)
Documentation

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 and verify identities using DIDs
  • Exchange secure messages with cryptographic guarantees
  • Process and validate TAP protocol messages
  • Manage cryptographic keys and operations
  • Integrate with various DID methods and resolvers

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
├── config      - Agent configuration
├── crypto      - Cryptographic operations and message packing
├── did         - DID resolution and management
├── error       - Error types and handling
├── message     - Message processing utilities

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
  • Receiving and unpacking messages
  • Validating message contents

The DefaultAgent implementation provides a standard implementation of this trait, using DIDComm for secure message exchange.

DID Resolution

The DID resolution system supports multiple DID methods through a pluggable architecture:

  • SyncDIDResolver - A trait for resolving DIDs to DID documents
  • DIDMethodResolver - A trait for method-specific resolvers
  • KeyResolver - A resolver for the did:key method
  • MultiResolver - A resolver that manages multiple method-specific resolvers

The system supports conversion between Ed25519 verification keys and X25519 key agreement keys, enabling the same keypair to be used for both signing and encryption.

Cryptographic Operations

The cryptographic system provides:

  • MessagePacker - A trait for packing and unpacking DIDComm messages
  • DefaultMessagePacker - An implementation using DIDComm v2
  • DebugSecretsResolver - A trait for resolving cryptographic secrets
  • BasicSecretResolver - A simple in-memory implementation for development

Security Modes

The agent supports different security modes for messages:

  • Plain - No security (for testing only)
  • Signed - Messages are digitally signed but not encrypted
  • AuthCrypt - Messages are both signed and encrypted (authenticated encryption)

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 and key discovery
  • Cryptographic Operations: Sign, verify, encrypt, and decrypt messages
  • Key Management: Securely manage cryptographic keys
  • Asynchronous Processing: Process messages concurrently using Tokio
  • WASM Support: Run in browser environments with WebAssembly
  • Extensible DID Methods: Support for did:key, with architecture for adding more methods
  • Performance Optimized: Benchmarked for high-throughput scenarios

Usage Examples

Basic Agent Setup

use tap_agent::agent::{Agent, DefaultAgent};
use tap_agent::config::AgentConfig;
use tap_agent::crypto::{DefaultMessagePacker, BasicSecretResolver};
use tap_agent::did::{MultiResolver, KeyResolver};
use didcomm::secrets::{Secret, SecretMaterial, SecretType};
use std::sync::Arc;

// Create agent configuration with a DID
let config = AgentConfig::new("did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".to_string());

// Set up DID resolver with support for did:key
let mut did_resolver = MultiResolver::new();
did_resolver.register_method("key", KeyResolver::new());
let did_resolver = Arc::new(did_resolver);

// Set up secret resolver with the agent's key
let mut secret_resolver = BasicSecretResolver::new();
let secret = Secret {
    id: "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#keys-1".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"
        }),
    },
};
secret_resolver.add_secret("did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", secret);
let secret_resolver = Arc::new(secret_resolver);

// Create message packer
let message_packer = Arc::new(DefaultMessagePacker::new(did_resolver, secret_resolver));

// Create the agent
let agent = DefaultAgent::new(config, message_packer);

Sending a Message

use tap_msg::message::Transfer;
use tap_caip::AssetId;
use std::str::FromStr;
use std::collections::HashMap;

// Create a transfer message
let transfer = Transfer {
    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,
    },
    beneficiary: Some(Participant {
        id: "did:key:z6MkhFvVnYxkqLNEiWQmUwhQuVpXiCfNmRUVi5yZ4Cg9w15k".to_string(),
        role: Some("beneficiary".to_string()),
        policies: None,
        leiCode: None,
    }),
    amount: "100.0".to_string(),
    agents: vec![],
    settlement_id: None,
    memo: Some("Test transfer".to_string()),
    metadata: HashMap::new(),
};

// Send the message to the recipient
let recipient_did = "did:key:z6MkhFvVnYxkqLNEiWQmUwhQuVpXiCfNmRUVi5yZ4Cg9w15k";
let packed_message = agent.send_message(&transfer, recipient_did).await?;

// The packed_message can now be transmitted to the recipient

Receiving a Message

// Receive and process an incoming message
let packed_message = "..."; // Received from network/transport
let transfer: Transfer = agent.receive_message(packed_message).await?;

// Now you can access the transfer details
println!("Received transfer of {} {}", transfer.amount, transfer.asset);

Custom DID Method Resolver

use tap_agent::did::{DIDMethodResolver, MultiResolver};
use didcomm::did::DIDDoc;
use async_trait::async_trait;

#[derive(Debug)]
struct WebResolver;

#[async_trait]
impl DIDMethodResolver for WebResolver {
    fn method(&self) -> &str {
        "web"
    }

    async fn resolve_method(&self, did: &str) -> Result<Option<DIDDoc>> {
        // Implementation for resolving did:web identifiers
        // ...
    }
}

// Register the custom resolver with the MultiResolver
let mut resolver = MultiResolver::default();
resolver.register_method("web", WebResolver);

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

For production use, it's recommended to:

  1. Implement a custom DebugSecretsResolver that integrates with a secure key management system
  2. Use proper key rotation and management practices
  3. Ensure secure transport for message exchange
  4. 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 DIDComm 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:

cargo bench --bench agent_benchmark

Feature Flags

The crate provides several feature flags to customize functionality:

  • native (default): Enables native platform features using Tokio
  • wasm: Enables WebAssembly support for browser environments

License

This crate is licensed under the MIT License.