Secure Memory Handling

← Back to Cryptography I: Blockchain Security and Cryptographic Foundations

Introduction

Cryptographic algorithms are only as secure as the secrets they protect. If an attacker can read your secret keys from memory, all the mathematical sophistication in the world won't help. This chapter explores how rippled protects sensitive data in memory, why it matters, and how to write code that doesn't leak secrets.

The Memory Problem

Where Secrets Live

void processTransaction() {
    SecretKey sk = loadKeyFromFile();
    // Secret key is now in memory:
    // - Stack frame
    // - CPU registers
    // - Potentially CPU cache
    // - Maybe swapped to disk

    auto sig = sign(pk, sk, tx);

    // Function returns
    // Stack frame deallocated
    // But what happens to the secret key bytes?
}

The problem: Memory isn't automatically erased when you're done with it.

Attack Vectors

1. Memory Dumps

2. Swap Files

3. Hibernation

4. Cold Boot Attacks

5. Debugging/Inspection

The Solution: Secure Erasure

Why memset() Isn't Enough

Compiler optimization example:

The OPENSSL_cleanse Solution

Why OPENSSL_cleanse works:

Key properties:

  1. Cannot be optimized away: Compiler forced to execute it

  2. Overwrites memory: Zeros written to actual memory

  3. Works cross-platform: Handles different compiler optimizations

  4. Validated: Extensively tested across compilers and architectures

RAII: Resource Acquisition Is Initialization

The Pattern

Why RAII Matters

Automatic cleanup:

Exception safety:

No forgetting:

Secure String Handling

The Problem with std::string

Solutions

1. Explicit erasure:

2. Use SecretKey wrapper:

3. Avoid std::string for secrets:

Secure Allocators (Advanced)

For highly sensitive applications:

Benefits:

  • Memory cannot be swapped to disk

  • Automatically erased on deallocation

  • Protected against paging attacks

Drawbacks:

  • Limited by OS limits on locked memory

  • Performance overhead

  • Complexity

When to use:

  • Extremely sensitive operations

  • Long-lived secrets

  • High-security requirements

Stack Scrubbing

The Problem

Solution: Overwrite Stack

Note: This is paranoid and rarely needed. RAII is usually sufficient.

CPU Registers and Cache

The Challenge

Mitigations

1. Minimize lifetime:

2. Overwrite with new data:

3. Trust hardware:

Best Practices

✅ DO:

❌ DON'T:

Defensive Programming

Assume the Worst

Multiple Layers

Testing Secure Erasure

Verification (Debug Build)

Memory Inspection (Advanced)

Summary

Secure memory handling protects secrets from attackers who can read process memory:

  1. Use OPENSSL_cleanse: Cannot be optimized away by compiler

  2. Wrap in RAII classes: Automatic cleanup, exception-safe

  3. Minimize lifetime: Create secrets late, destroy early

  4. Explicit erasure: Clean up temporary buffers

  5. Avoid std::string: For long-lived secrets

  6. Test verification: Ensure erasure actually happens

Key principle: Assume attacker can read your memory. Make sure there's nothing to find.

Implementation:

  • secure_erase() wraps OPENSSL_cleanse()

  • SecretKey class uses RAII

  • Destructors automatically erase

  • Minimize copies and lifetime

Last updated