Stratum V2 Security Analysis for Zcash

This document analyzes known attacks against the Stratum V2 mining protocol and evaluates the exposure of this Zcash implementation to each attack vector.

Overview

Stratum V2 was designed to address significant security weaknesses in Stratum V1, including lack of encryption and vulnerability to man-in-the-middle attacks. However, academic research has identified several attack vectors that remain relevant even with V2’s improvements.

This analysis covers:

  1. Network-level routing attacks (EROSION)
  2. Privacy attacks (StraTap, ISP Log)
  3. Hash hijacking attacks (BiteCoin)
  4. Noise protocol cryptographic considerations
  5. Template manipulation attacks
  6. Resource exhaustion attacks

Attack Analysis

1. EROSION Attack (Network Routing Attack)

Source: Routing Attacks on Cryptocurrency Mining Pools - ETH Zurich, 2024

Attack Description:

The EROSION attack exploits BGP (Border Gateway Protocol) hijacking to intercept traffic between miners and pools. Researchers discovered that:

  • An adversary can use BGP hijacking to intercept mining pool traffic
  • By tampering with a single encrypted packet, the attacker can persistently disrupt connections
  • The Stratum V2 cryptography specification had a vulnerability allowing stealthier attacks
  • 91% of mining pools across top cryptocurrencies are vulnerable to this attack class
  • One malicious Autonomous System could theoretically take down 96% of Bitcoin mining power

This Implementation’s Exposure: PARTIALLY VULNERABLE

Code Analysis:

In crates/bedrock-noise/src/transport.rs, decryption failures return an error:

transport
    .read_message(&ciphertext, &mut plaintext)
    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))?

In crates/zcash-pool-server/src/session.rs, read errors terminate the session:

Err(e) => {
    error!("Read error for channel {}: {}", channel_id, e);
    break;
}

Impact:

An attacker who intercepts traffic and corrupts a single encrypted packet will cause:

  1. Decryption failure in the Noise layer
  2. Session termination
  3. Miner disconnection

The miner must then reconnect, losing any work in progress. Sustained attacks could significantly degrade pool hashrate.

Mitigation Status:

The official SV2 reference implementation patched this by implementing automatic reconnection on decryption failures (December 2023). This implementation terminates on failure but does not implement automatic reconnection with backoff.

Recommendations:

  1. Implement automatic reconnection with exponential backoff
  2. Add detection for repeated short-lived connections (potential attack indicator)
  3. Log decryption failure patterns for monitoring
  4. Consider multi-AS deployment for the pool infrastructure

2. StraTap and ISP Log Attacks (Privacy Attacks)

Source: Hardening Stratum, the Bitcoin Pool Mining Protocol - 2017

Attack Description:

  • StraTap: Passively infers miner earnings by analyzing Stratum communications
  • ISP Log Attack: Reconstructs miner earnings from packet timestamps alone, even without payload access

These attacks compromise financial privacy of mining operations, potentially enabling:

  • Competitive intelligence gathering
  • Targeted attacks on profitable miners
  • Tax/regulatory surveillance

This Implementation’s Exposure: PROTECTED (when Noise enabled)

Code Analysis:

The implementation supports both encrypted and plaintext modes in crates/zcash-pool-server/src/session.rs:

pub enum Transport {
    Plain(TcpStream),
    Noise(NoiseStream<TcpStream>),
}

When noise_enabled=true in configuration, all traffic uses Noise_NK encryption.

Residual Risks:

RiskSeverityNotes
Plain mode exposureHighAll share data visible if Noise disabled
Timing analysisMediumShare acceptance/rejection patterns leak timing information
Traffic analysisLowMessage sizes may reveal share submission patterns

Recommendations:

  1. Consider removing plain mode or requiring explicit opt-in with warnings
  2. Add random delays to share responses to mitigate timing attacks
  3. Document that Noise should always be enabled in production

3. BiteCoin Attack (Hash Hijacking)

Source: Hardening Stratum

Attack Description:

An active man-in-the-middle attack that:

  1. Intercepts shares submitted by miners
  2. Claims the shares and associated payouts for the attacker
  3. Uses the “WireGhost” technique to surreptitiously maintain Stratum connections

This directly steals mining revenue from victims.

This Implementation’s Exposure: PROTECTED (when Noise enabled)

Code Analysis:

The Noise_NK pattern provides server authentication in crates/bedrock-noise/src/handshake.rs:

let mut handshake = builder
    .remote_public_key(self.server_public_key.as_bytes())
    .build_initiator()
    .map_err(HandshakeError::Snow)?;

Key protections:

  • Miners must know the pool’s public key out-of-band (prevents impersonation)
  • AEAD encryption prevents tampering with messages in transit
  • Handshake establishes session keys that only legitimate parties possess

Residual Risks:

RiskSeverityNotes
Plain modeCriticalNo protection against hash hijacking without Noise
No miner authenticationMediumPool authenticates to miners, but miners don’t authenticate to pool
Public key distributionMediumMiners must securely obtain pool’s public key

Recommendations:

  1. Mandatory Noise encryption for production deployments
  2. Publish pool public key via multiple channels (website, DNS TXT, signed announcements)
  3. Consider adding miner authentication for premium/trusted miner tiers

4. Noise NK Protocol Cryptographic Considerations

Source: Noise Explorer - NK Pattern

Pattern Description:

Noise_NK means:

  • N: Initiator (miner) has no static key
  • K: Responder’s (pool) static key is known to initiator

This provides:

  • Server authentication (miner verifies pool identity)
  • Forward secrecy (with caveats)
  • Encrypted communications

Known Limitations:

VulnerabilityImpactThis Implementation
Replay attacksAttacker could replay messagesNo application-layer replay protection
Key Compromise Impersonation (KCI)If pool key compromised, attacker can impersonate minersVulnerable
Limited forward secrecyPool key compromise decrypts past trafficVulnerable
No sender authenticationFirst message has no sender identity proofBy design (anonymous miners)

Code Analysis:

In crates/bedrock-noise/src/transport.rs, messages are processed without sequence validation:

pub async fn read_message(&mut self) -> io::Result<Vec<u8>> {
    let len = self.inner.read_u16().await? as usize;
    // ... decrypt and return
    // No sequence number or timestamp validation
}

Recommendations:

  1. Add application-layer sequence numbers to detect replayed messages
  2. Implement message timestamps with reasonable clock skew tolerance
  3. Document key rotation procedures (periodic pool key regeneration)
  4. Consider upgrading to NK1 or XX patterns for mutual authentication in future versions

5. Template Manipulation Attacks (Job Declaration)

Attack Description:

Miners using Job Declaration could attempt to:

  • Submit templates with invalid transactions
  • Include transactions that don’t pay the pool
  • Build on stale chain tips
  • Exhaust server resources with rapid job declarations

This Implementation’s Exposure: WELL PROTECTED

Code Analysis:

The JD Server in crates/zcash-jd-server/src/server.rs implements multiple layers of protection:

Token Validation:

match self.token_manager.validate_token(&request.mining_job_token) {
    Ok(_) => {}
    Err(JdServerError::InvalidToken) => { /* reject */ }
    Err(JdServerError::TokenExpired) => { /* reject */ }
}

Stale Prevention:

if let Some(expected_prev_hash) = *current_prev_hash {
    if request.prev_hash != expected_prev_hash {
        return Err(SetCustomMiningJobError::new(
            // StalePrevHash error
        ));
    }
}

Mode Enforcement:

if token_info.granted_mode != JobDeclarationMode::FullTemplate {
    return Err(/* ModeMismatch */);
}

Template Validation:

  • Validates transaction structure
  • Verifies merkle root computation
  • Checks pool payout requirements

Resource Limits:

LimitValueLocation
Frame size1 MBserver.rs:604
Shares per job100,000duplicate.rs:12
Buffer size64 KBsession.rs:195
Jobs per channel10channel.rs:146

6. Resource Exhaustion Attacks

Attack Description:

Attackers could attempt to exhaust pool resources by:

  • Flooding with duplicate shares
  • Submitting shares faster than validation can process
  • Creating many connections
  • Sending oversized messages

This Implementation’s Exposure: PROTECTED

Code Analysis:

Rate Limiting:

pub fn check_rate_limit(&mut self) -> bool {
    self.rate_limiter.check().is_allowed()
}

Applied before validation:

if !channel.check_rate_limit() {
    debug!("Rate limiting channel {}", channel_id);
    return /* rejection */;
}

Duplicate Detection:

const MAX_SHARES_PER_JOB: usize = 100_000;

fn check_and_record(&self, job_id: u32, nonce_2: &[u8], solution: &[u8]) -> bool {
    if shares.len() >= MAX_SHARES_PER_JOB {
        return true; // Reject as duplicate
    }
    !shares.insert(hash)
}

Connection Limits:

if current_connections >= self.config.max_connections {
    warn!("Connection limit reached, rejecting {}", addr);
    continue;
}

Message Size Limits:

  • Frame header validation before reading payload
  • Maximum frame size enforced at protocol layer

Summary Matrix

AttackSeverityExposurePrimary Mitigation
EROSION (BGP hijack)HighPartialReconnection logic needed
StraTap/ISP LogMediumLow*Noise encryption
BiteCoin (hash hijack)CriticalLow*Noise encryption
Noise NK weaknessesMediumPartialApplication-layer protection
Template manipulationHighLowToken/validation controls
Resource exhaustionMediumLowRate limiting, size limits

*With Noise encryption enabled


Configuration Recommendations

Production Security Checklist

# pool-config.toml

# REQUIRED: Enable Noise encryption
noise_enabled = true

# REQUIRED: Use persistent key (don't regenerate on restart)
noise_private_key_path = "/secure/path/pool_noise_key.hex"

# RECOMMENDED: Enable JD Noise as well
jd_noise_enabled = true

# RECOMMENDED: Strict connection limits
max_connections = 10000

# RECOMMENDED: Enable metrics for monitoring
metrics_addr = "127.0.0.1:9090"

Operational Security

  1. Key Management:

    • Generate pool Noise key securely: openssl rand -hex 32 > pool_noise_key.hex
    • Store key with restricted permissions: chmod 600 pool_noise_key.hex
    • Publish public key via authenticated channels
    • Plan for key rotation (annual or after suspected compromise)
  2. Network Architecture:

    • Deploy pool servers across multiple Autonomous Systems (AS)
    • Use anycast for geographic distribution
    • Implement DDoS protection at network edge
    • Monitor BGP announcements for your IP prefixes
  3. Monitoring:

    • Alert on unusual disconnection rates
    • Track share rejection rates per miner
    • Monitor for repeated short-lived connections
    • Log Noise handshake failures

Future Work

  1. Automatic Reconnection: Implement reconnection with exponential backoff to mitigate EROSION
  2. Sequence Numbers: Add application-layer replay protection
  3. Mutual Authentication: Consider optional miner authentication for trusted pools
  4. Audit: Commission formal security audit before mainnet deployment

References

  1. Tran, M., von Arx, T., Vanbever, L. (2024). “Routing Attacks on Cryptocurrency Mining Pools.” IEEE S&P.
  2. Recabarren, R., Carbunar, B. (2017). “Hardening Stratum, the Bitcoin Pool Mining Protocol.”
  3. Perrin, T. “The Noise Protocol Framework.”
  4. Symbolic Analysis of Noise NK Pattern.
  5. Stratum V2 Specification.
  6. Secureworks. “BGP Hijacking for Cryptocurrency Profit.”