A three-level trust system for server-to-server email authentication using PKI and web-of-trust principles. This allows mail servers to establish cryptographic trust relationships without requiring centralized certificate authorities.
Current email authentication (SPF, DKIM, DMARC) is domain-centric and doesn't establish server-to-server trust relationships. This design creates a peer-to-peer trust network where servers can:
- Verify each other's identities cryptographically
- Build trust networks through peer endorsements
- Optionally use a public registry for discovery
- Reject untrusted connections at the SMTP level
Description: Strongest trust level based on manual administrator configuration.
Process:
- Admin of
mail.alice.comandmail.bob.commeet/communicate - Exchange public keys out-of-band (email, phone, in-person)
- Each admin adds the other's key to their trusted peers list
- Servers can now authenticate each other cryptographically
Trust Level: Highest Setup: Manual Use Case: Known partners, frequent communication, high-value relationships
Example Configuration:
Trusted Peers for mail.alice.com:
- mail.bob.com: <Bob's Public Key>
- mail.carol.com: <Carol's Public Key>
Description: Discover new trusted servers through existing Level 1 relationships.
Process:
- Alice's server receives connection from unknown
mail.dave.com - Alice queries her Level 1 peers: "Do you trust dave?"
- Bob responds: "Yes, here's Dave's public key and my signature"
- Alice caches Dave's key with reduced trust level
- Future connections from Dave are verified against cached key
Trust Level: Medium Setup: Automatic via peer queries Use Case: Extended network, partners of partners
Example:
Alice trusts Bob (Level 1)
Bob trusts Dave (Level 1)
→ Alice trusts Dave (Level 2, via Bob)
Query Protocol:
Alice → Bob: "ESRV QUERY-TRUST mail.dave.com"
Bob → Alice: "ESRV RESPONSE-TRUST LEVEL1 <dave-public-key> <bob-signature>"
Description: Lookup unknown servers in a public registry with reputation scores.
Process:
- Server receives connection from unknown domain
- No Level 1 or Level 2 trust exists
- Query public registry for domain's public key and reputation
- Verify signature against registry key
- Check trust score and flags
- Accept/reject based on policy thresholds
Trust Level: Lowest Setup: Automatic via registry API Use Case: First contact, cold outreach, general public
Registry Entry Example:
{
"domain": "mail.example.com",
"public_key": "-----BEGIN PUBLIC KEY-----...",
"key_fingerprint": "SHA256:abc123...",
"ip_addresses": ["203.0.113.5", "203.0.113.6"],
"registered_date": "2024-01-15T10:00:00Z",
"trust_score": 95,
"flags": [],
"endorsements": [
{
"endorsed_by": "mail.trusted.com",
"date": "2024-01-20T12:00:00Z",
"signature": "..."
}
],
"last_seen": "2024-01-25T08:30:00Z"
}S: 250-mail.alice.com
S: 250-ESRV TRUST-LEVELS=1,2,3
S: 250-ESRV KEY-FINGERPRINT=SHA256:abc123...
S: 250 STARTTLS
Identify Server:
C: ESRV IDENTIFY mail.bob.com SHA256:def456...
S: 250 IDENTIFIED
Authenticate with Signature:
C: ESRV AUTH LEVEL1
C: SIGNATURE: <base64-signature-of-session-id>
S: 250 AUTH VERIFIED LEVEL1
Query Trust (Level 2):
C: ESRV QUERY-TRUST mail.dave.com
S: 250 TRUST-INFO LEVEL1 <public-key> <signature>
Verify Signature:
C: ESRV VERIFY <signature-data>
S: 250 SIGNATURE VALID
1. EHLO with ESRV support announcement
2. ESRV IDENTIFY (optional, for tracking)
3. ESRV AUTH (required for trusted delivery)
4. Standard SMTP commands (MAIL FROM, RCPT TO, DATA)
CREATE TABLE trusted_peers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
domain TEXT NOT NULL UNIQUE,
public_key TEXT NOT NULL,
key_fingerprint TEXT NOT NULL,
private_note TEXT,
added_by TEXT,
added_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_verified TIMESTAMP,
connection_count INTEGER DEFAULT 0
);
CREATE INDEX idx_trusted_peers_domain ON trusted_peers(domain);
CREATE INDEX idx_trusted_peers_fingerprint ON trusted_peers(key_fingerprint);CREATE TABLE transitive_trust (
id INTEGER PRIMARY KEY AUTOINCREMENT,
domain TEXT NOT NULL UNIQUE,
public_key TEXT NOT NULL,
key_fingerprint TEXT NOT NULL,
trusted_via TEXT NOT NULL, -- which Level 1 peer vouched
trust_level INTEGER DEFAULT 2,
voucher_signature TEXT NOT NULL,
cached_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_date TIMESTAMP NOT NULL,
last_used TIMESTAMP,
use_count INTEGER DEFAULT 0
);
CREATE INDEX idx_transitive_trust_domain ON transitive_trust(domain);
CREATE INDEX idx_transitive_trust_expires ON transitive_trust(expires_date);CREATE TABLE registry_cache (
id INTEGER PRIMARY KEY AUTOINCREMENT,
domain TEXT NOT NULL UNIQUE,
public_key TEXT NOT NULL,
key_fingerprint TEXT NOT NULL,
ip_addresses TEXT, -- JSON array
trust_score INTEGER,
flags TEXT, -- JSON array
endorsements TEXT, -- JSON array
cached_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
cache_expires TIMESTAMP NOT NULL,
last_verified TIMESTAMP
);
CREATE INDEX idx_registry_cache_domain ON registry_cache(domain);
CREATE INDEX idx_registry_cache_score ON registry_cache(trust_score);CREATE TABLE trust_connections (
id INTEGER PRIMARY KEY AUTOINCREMENT,
remote_domain TEXT NOT NULL,
remote_ip TEXT NOT NULL,
trust_level INTEGER, -- 1, 2, 3, or NULL (untrusted)
authenticated BOOLEAN DEFAULT 0,
signature_valid BOOLEAN,
connection_result TEXT, -- accepted, rejected, relayed
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
email_count INTEGER DEFAULT 0
);
CREATE INDEX idx_trust_connections_domain ON trust_connections(remote_domain);
CREATE INDEX idx_trust_connections_timestamp ON trust_connections(timestamp DESC);
CREATE INDEX idx_trust_connections_result ON trust_connections(connection_result);CREATE TABLE server_identity (
id INTEGER PRIMARY KEY CHECK (id = 1), -- Single row table
domain TEXT NOT NULL,
private_key_path TEXT NOT NULL,
public_key TEXT NOT NULL,
key_fingerprint TEXT NOT NULL,
key_generated_date TIMESTAMP NOT NULL,
key_algorithm TEXT DEFAULT 'RSA-4096'
);# Trust System
ENABLE_TRUST_SYSTEM=true
TRUST_REQUIRE_AUTH_PORT25=false # Allow unauthenticated on port 25?
TRUST_REQUIRE_AUTH_PORT587=true # Always require auth on 587
# Trust Levels Enabled
TRUST_ALLOW_LEVEL1=true
TRUST_ALLOW_LEVEL2=true
TRUST_ALLOW_LEVEL3=false # Registry optional
# Level 2 Settings
TRUST_L2_CACHE_DURATION_HOURS=168 # 1 week
TRUST_L2_MAX_HOPS=2 # How many levels of transitive trust
# Level 3 Registry
TRUST_REGISTRY_URL=https://mailreg.example.org
TRUST_REGISTRY_MIN_SCORE=70 # Minimum trust score to accept
TRUST_REGISTRY_CACHE_HOURS=24
# Server Identity
SERVER_DOMAIN=mail.example.com
SERVER_PRIVATE_KEY=./keys/server.key
SERVER_PUBLIC_KEY=./keys/server.pub
# Fallback Behavior
TRUST_REJECT_UNTRUSTED=true # Reject if no trust established
TRUST_FALLBACK_TO_SPF=false # Use SPF/DKIM if ESRV not supported# trust-policy.yaml
trust_policy:
# Port 25 (incoming from other servers)
port_25:
level_1: accept
level_2: accept
level_3: accept_if_score_above_80
untrusted: reject
# Port 587 (submission from clients)
port_587:
require_authentication: true
allow_relay: level_1_and_2_only
# Rate limits by trust level
rate_limits:
level_1:
emails_per_hour: unlimited
recipients_per_email: 100
level_2:
emails_per_hour: 100
recipients_per_email: 50
level_3:
emails_per_hour: 10
recipients_per_email: 10
untrusted:
emails_per_hour: 0https://mailreg.example.org/api/v1
Register Server:
POST /servers
Content-Type: application/json
{
"domain": "mail.example.com",
"public_key": "-----BEGIN PUBLIC KEY-----...",
"admin_email": "admin@example.com",
"ip_addresses": ["203.0.113.5"],
"proof_of_domain": "<DNS-TXT-record-value>"
}
Response: 201 Created
{
"server_id": "uuid",
"verification_status": "pending",
"verification_method": "dns_txt"
}
Lookup Server:
GET /servers/mail.example.com
Response: 200 OK
{
"domain": "mail.example.com",
"public_key": "-----BEGIN PUBLIC KEY-----...",
"key_fingerprint": "SHA256:abc123...",
"ip_addresses": ["203.0.113.5"],
"registered_date": "2024-01-15T10:00:00Z",
"verified": true,
"trust_score": 95,
"flags": [],
"endorsements_count": 5,
"last_seen": "2024-01-25T08:30:00Z"
}
Endorse Server:
POST /servers/mail.example.com/endorse
Content-Type: application/json
{
"endorser_domain": "mail.alice.com",
"signature": "<signature-of-endorsed-domain-by-endorser>"
}
Response: 201 Created
Report Server:
POST /servers/mail.example.com/report
Content-Type: application/json
{
"reporter_domain": "mail.alice.com",
"reason": "spam",
"evidence": "...",
"signature": "<signature>"
}
Response: 201 Created
Search Servers:
GET /servers?min_score=80&verified=true&limit=100
Response: 200 OK
{
"servers": [...],
"total": 1523,
"page": 1
}
Goal: Basic PKI authentication between manually configured peers.
Features:
- Generate server key pair
- CLI tool to manage trusted peers
- ESRV IDENTIFY and AUTH commands
- Signature verification
- Reject untrusted connections (optional)
Deliverables:
- Key generation tool:
mailctl keygen - Peer management:
mailctl peer:add,mailctl peer:list - Modified SMTP handler with ESRV support
- Database migrations for trusted_peers table
- Configuration options
Testing:
- Setup 2 SteveMail instances
- Exchange public keys
- Send authenticated email
- Verify rejection of untrusted server
Goal: Discover trusted servers through peer recommendations.
Features:
- ESRV QUERY-TRUST command
- Peer query protocol
- Trust cache with expiration
- Transitive trust verification
- Configurable trust depth (max hops)
Deliverables:
- Trust query handler
- Cache management
- Signature chain verification
- CLI:
mailctl trust:query <domain> - Database migrations for transitive_trust
Testing:
- 3 server chain: A trusts B, B trusts C
- A queries B about C
- Verify cache expiration
- Test trust depth limits
Goal: Simple HTTP registry for testing Level 3 trust.
Features:
- Basic HTTP API server
- SQLite backend
- Server registration
- Lookup by domain
- Simple trust scoring
- No authentication (MVP)
Deliverables:
- Registry HTTP server
- Registration API
- Lookup API
- Basic web UI
- Integration with SteveMail
Testing:
- Register multiple servers
- Lookup servers
- Query from SteveMail
- Verify trust score filtering
Goal: Security, scalability, and reliability.
Features:
- Key rotation protocol
- Revocation lists
- Rate limiting by trust level
- Prometheus metrics
- TLS for registry API
- Registry replication
- DNSSEC integration
- Reputation scoring algorithm
- Private keys stored encrypted at rest
- Key rotation every 1-2 years
- Revocation mechanism for compromised keys
- Separate keys for different purposes (signing vs encryption)
1. Man-in-the-Middle:
- Mitigated by: Signature verification, key fingerprint verification
2. Key Theft:
- Mitigated by: Encrypted storage, file permissions, HSM support
3. Replay Attacks:
- Mitigated by: Session-specific signatures, timestamps, nonces
4. Trust Poisoning:
- Mitigated by: Limited transitive trust depth, cache expiration, manual Level 1
5. Registry Compromise:
- Mitigated by: Registry is lowest trust level, manual overrides, multiple registries
6. Sybil Attack:
- Mitigated by: Domain ownership verification, IP reputation, endorsements
- Connection logs retention policy
- Registry should not log queries
- Optional anonymous queries to registry
- Local caching to minimize registry dependencies
- Server identity vs domain identity
- Cryptographic proof vs DNS records
- Peer-to-peer trust vs centralized DNS
- Reject at connection vs after content analysis
- No cost for basic trust (Level 1 & 2)
- Decentralized trust network
- Purpose-built for email
- Web of trust model vs hierarchical CA
- Cryptographic authentication vs IP address
- Works across IP changes
- Portable trust (key follows server)
- Granular trust levels
- Key Algorithm: RSA 4096? Ed25519? Support multiple?
- Signature Format: JWS? OpenPGP? Custom?
- Trust Decay: Should Level 2 trust degrade over time?
- Registry Federation: Multiple registries? Consensus mechanism?
- Backward Compatibility: How to handle non-ESRV servers?
- Performance: Signature verification on every connection?
- Key Discovery: Alternative to registry for Level 3?
- Standards: Propose as IETF RFC?
- End-to-end email encryption using server keys
- Reputation scoring based on connection success
- Machine learning for trust scoring
- Blockchain-based registry (for immutability)
- Federation protocol between registries
- Integration with existing PKI (DANE/TLSA)
- Message-level signatures (not just connection)
- Trust visualization tools
- Automated key rotation
- Hardware security module (HSM) support
- RFC 5321: SMTP
- RFC 7208: SPF
- RFC 6376: DKIM
- RFC 7489: DMARC
- RFC 6698: DANE
- RFC 4880: OpenPGP
- PGP Web of Trust
- SSL/TLS with client certificates
- DNSSEC
- Certificate Transparency
# Generate server key pair
mailctl keygen --domain mail.example.com
# Export public key
mailctl key:export > server.pub
# Show key fingerprint
mailctl key:fingerprint
# Rotate keys
mailctl key:rotate --backup# Add trusted peer
mailctl peer:add mail.bob.com --key ./bob.pub --note "Bob's mail server"
# List trusted peers
mailctl peer:list
# Remove peer
mailctl peer:remove mail.bob.com
# Show peer details
mailctl peer:show mail.bob.com
# Verify peer signature
mailctl peer:verify mail.bob.com --signature <sig># Query trust for domain
mailctl trust:query mail.dave.com
# Show trust cache
mailctl trust:cache
# Clear trust cache
mailctl trust:clear-cache
# Test trust path
mailctl trust:path mail.target.com# Register with public registry
mailctl registry:register
# Lookup domain in registry
mailctl registry:lookup mail.example.com
# Endorse another server
mailctl registry:endorse mail.friend.com
# Update registry info
mailctl registry:update --ips 203.0.113.5,203.0.113.6# Show connection log
mailctl connections --filter level=1 --limit 100
# Trust statistics
mailctl stats:trust
# Show untrusted connection attempts
mailctl connections --untrustedSetup:
- Server A (mail.alice.com)
- Server B (mail.bob.com)
Steps:
1. A and B exchange keys out-of-band
2. Each adds other to trusted_peers
3. B connects to A
4. B authenticates via ESRV
5. B sends email
6. A delivers to local mailbox
Verify:
- Connection logged with trust_level=1
- Email delivered successfully
- No warnings or rejections
Setup:
- Server A trusts B (Level 1)
- Server B trusts C (Level 1)
- C is unknown to A
Steps:
1. C connects to A
2. A has no Level 1 trust for C
3. A queries B: "Do you trust C?"
4. B responds with C's key + signature
5. A caches C with Level 2 trust
6. C authenticates and sends email
7. Future connections use cache
Verify:
- First connection: query to B
- Cache entry created
- Subsequent connections: no query
- trust_level=2 logged
Setup:
- Server A with registry enabled
- Server D (unknown to A)
- D registered in registry
Steps:
1. D connects to A
2. No Level 1 or 2 trust
3. A queries registry for D
4. Registry returns D's key + trust_score
5. If score > threshold, accept
6. Cache registry data
Verify:
- Registry query logged
- Trust score evaluated
- Accept/reject based on policy
- trust_level=3 if accepted
Setup:
- Server A with TRUST_REJECT_UNTRUSTED=true
- Server E (unknown, not in registry)
Steps:
1. E connects to A
2. E does not support ESRV
3. No trust levels match
4. A rejects connection
Verify:
- Connection rejected at SMTP level
- No email accepted
- Rejection logged with trust_level=NULL
- Proper SMTP error code returned
This trust system provides a flexible, cryptographically secure method for mail servers to establish trust relationships. The three-level model allows for strong manual trust, scalable web-of-trust, and optional public discovery. Implementation can be phased, with each phase providing incremental value.
The system is designed to be backward-compatible with existing SMTP while providing significant security improvements for participating servers.