Secure Memory Handling

MetaMUI Crypto Primitives implements comprehensive secure memory handling across all 10 supported platforms.

Key Features

Automatic Memory Clearing

Memory Protection

Platform Implementation

C

#include <metamui_crypto/secure_memory.h>
#include <string.h>

// Secure allocation with automatic clearing
secure_ptr_t key = secure_alloc(32);
if (key != NULL) {
    // Use key for cryptographic operations
    metamui_chacha20_poly1305_encrypt(key, plaintext, ciphertext);
    
    // Secure free automatically zeros memory
    secure_free(key);
}

// Manual secure clearing with memory barrier
void secure_clear_example() {
    uint8_t secret[32];
    // ... use secret for operations ...
    
    // Secure zeroing with compiler barrier
    secure_memzero(secret, sizeof(secret));
    
    // Alternative: explicit_bzero (when available)
    #ifdef __GLIBC__
    explicit_bzero(secret, sizeof(secret));
    #endif
}

// Memory locking for sensitive data
void lock_memory_example() {
    uint8_t sensitive_key[32];
    
    #ifdef _WIN32
    VirtualLock(sensitive_key, sizeof(sensitive_key));
    #else
    mlock(sensitive_key, sizeof(sensitive_key));
    #endif
    
    // Use locked memory
    // ...
    
    // Clear before unlocking
    secure_memzero(sensitive_key, sizeof(sensitive_key));
    
    #ifdef _WIN32
    VirtualUnlock(sensitive_key, sizeof(sensitive_key));
    #else
    munlock(sensitive_key, sizeof(sensitive_key));
    #endif
}

C#

using System;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
using MetaMUI.Crypto;

public class SecureMemoryExample
{
    // Using SecureString for passwords
    public static void SecurePasswordExample()
    {
        using (var securePassword = new SecureString())
        {
            // Build secure string character by character
            foreach (char c in "password")
                securePassword.AppendChar(c);
            
            securePassword.MakeReadOnly();
            
            // Use with cryptographic operations
            DeriveKeyFromPassword(securePassword);
        } // Automatically cleared
    }
    
    // Pinned memory for keys
    public static void PinnedMemoryExample()
    {
        byte[] key = new byte[32];
        GCHandle handle = GCHandle.Alloc(key, GCHandleType.Pinned);
        
        try
        {
            // Fill with random data
            using (var rng = RandomNumberGenerator.Create())
            {
                rng.GetBytes(key);
            }
            
            // Use pinned memory for crypto operations
            ProcessWithKey(handle.AddrOfPinnedObject());
        }
        finally
        {
            // Clear memory before unpinning
            Array.Clear(key, 0, key.Length);
            handle.Free();
        }
    }
    
    // Secure byte array wrapper
    public static void SecureBytesExample()
    {
        using (var secureBytes = new SecureBytes(32))
        {
            secureBytes.Fill(RandomNumberGenerator.GetBytes);
            
            // Use for cryptographic operations
            var cipher = new ChaCha20Poly1305(secureBytes.GetBytes());
            
        } // Automatically cleared and unpinned
    }
}

Python

from metamui_crypto import SecureBytes

# Automatic clearing with context manager
with SecureBytes(32) as key:
    # Use key for cryptographic operations
    cipher = ChaCha20Poly1305(key)
    encrypted = cipher.encrypt(data)
# key is automatically cleared here

Rust

use metamui_crypto::secure::{SecureVec, Zeroize};

// Automatic zeroization on drop
{
    let mut key = SecureVec::new(32);
    // Use key...
} // Automatically zeroed

// Manual zeroization
let mut secret = vec![0u8; 32];
secret.zeroize();

TypeScript/JavaScript

import { SecureBuffer } from '@metamui/crypto';

// Secure buffer with automatic clearing
const key = new SecureBuffer(32);
try {
    // Use key
} finally {
    key.clear(); // Explicit clearing
}

Swift

// Automatic clearing with defer
var key = Data(count: 32)
defer { 
    key.withUnsafeMutableBytes { 
        $0.initializeMemory(as: UInt8.self, repeating: 0) 
    } 
}

Kotlin

// Use try-finally for cleanup
val key = ByteArray(32)
try {
    // Use key
} finally {
    key.fill(0) // Clear memory
}

WASM

// Memory is managed by WASM runtime
// Automatic cleanup when objects go out of scope
const crypto = await MetaMUICrypto.initialize();

// Create secure key - memory managed by WASM
const key = crypto.generateKey(32);
try {
    const cipher = crypto.createCipher('ChaCha20-Poly1305', key);
    const encrypted = cipher.encrypt(data);
} finally {
    // Explicit cleanup if needed
    key.destroy();
}

Go

package main

import (
    "crypto/rand"
    "github.com/metamui/crypto/secure"
)

// Secure buffer with automatic clearing
func secureBufferExample() {
    key := secure.NewBuffer(32)
    defer key.Destroy() // Automatic zeroing on defer
    
    // Fill with random data
    rand.Read(key.Bytes())
    
    // Use for cryptographic operations
    cipher := NewChaCha20Poly1305(key.Bytes())
    encrypted := cipher.Encrypt(plaintext)
}

// Manual clearing for byte slices
func manualClearingExample() {
    secret := make([]byte, 32)
    defer func() {
        // Explicit zeroing with memory barrier
        for i := range secret {
            secret[i] = 0
        }
        // Force compiler to not optimize away the clearing
        runtime.KeepAlive(secret)
    }()
    
    // Use secret...
    processSecret(secret)
}

// Secure comparison
func secureCompareExample(a, b []byte) bool {
    // Use constant-time comparison
    return subtle.ConstantTimeCompare(a, b) == 1
}

// Protecting against string immutability
func stringHandlingExample(password string) {
    // Convert to mutable byte slice immediately
    passwordBytes := []byte(password)
    defer secure.Clear(passwordBytes)
    
    // Work with bytes, not strings
    key := deriveKey(passwordBytes)
    defer secure.Clear(key)
}

Java

import java.security.SecureRandom;
import java.util.Arrays;
import javax.crypto.SecretKey;
import javax.security.auth.DestroyFailedException;
import com.metamui.crypto.SecureBytes;
import com.metamui.crypto.ChaCha20Poly1305;

public class SecureMemoryExample {
    
    // Using try-with-resources for automatic clearing
    public static void secureByteExample() {
        try (SecureBytes key = SecureBytes.allocate(32)) {
            // Fill with secure random
            new SecureRandom().nextBytes(key.getBytes());
            
            // Use for cryptographic operations
            ChaCha20Poly1305 cipher = new ChaCha20Poly1305(key);
            byte[] encrypted = cipher.encrypt(plaintext);
            
        } // Automatically cleared on close
    }
    
    // Manual clearing with finally block
    public static void manualClearingExample() {
        byte[] secret = new byte[32];
        try {
            new SecureRandom().nextBytes(secret);
            
            // Use secret for operations
            processWithSecret(secret);
            
        } finally {
            // Explicit clearing
            Arrays.fill(secret, (byte) 0);
        }
    }
    
    // Using SecretKey with destroy
    public static void secretKeyExample() throws DestroyFailedException {
        SecretKey key = generateSecretKey();
        try {
            // Use key for operations
            Cipher cipher = Cipher.getInstance("ChaCha20-Poly1305");
            cipher.init(Cipher.ENCRYPT_MODE, key);
            
        } finally {
            // Destroy if destroyable
            if (key instanceof Destroyable && !((Destroyable) key).isDestroyed()) {
                ((Destroyable) key).destroy();
            }
        }
    }
    
    // Character array for passwords (not String)
    public static void passwordHandling(char[] password) {
        try {
            // Use password for key derivation
            byte[] key = deriveKey(password);
            try {
                processWithKey(key);
            } finally {
                Arrays.fill(key, (byte) 0);
            }
        } finally {
            // Clear password array
            Arrays.fill(password, '\0');
        }
    }
}

## Best Practices

1. **Use Secure Wrappers**: Always use platform-specific secure memory wrappers
2. **Minimize Lifetime**: Keep sensitive data in memory for the shortest time possible
3. **Avoid Copies**: Prevent unnecessary copies of sensitive data
4. **Clear on Error**: Ensure memory is cleared even when errors occur
5. **No Logging**: Never log sensitive data
6. **Language-Specific Considerations**:
   - **C/C++**: Use compiler barriers and platform-specific secure functions
   - **C#/Java**: Pin memory when needed, use SecureString/char[] for passwords
   - **Go**: Account for garbage collection, use byte slices over strings
   - **All platforms**: Prefer automatic cleanup mechanisms (RAII, defer, using, try-with-resources)

## Memory Security Guarantees

- **Constant-Time**: All operations on sensitive data are constant-time
- **No Optimization**: Compiler optimizations that might skip clearing are prevented
- **Platform-Specific**: Uses best practices for each platform
- **Tested**: Regular testing ensures memory is properly cleared

## Common Patterns

### Key Derivation
```python
# Python: Derive key and clear intermediate values
salt = secure_random(16)
key = Argon2.derive_key(password, salt)
# password cleared internally
// Go: Key derivation with cleanup
func deriveKeySecurely(password []byte) []byte {
    salt := make([]byte, 16)
    rand.Read(salt)
    defer secure.Clear(password)
    
    key := argon2.DeriveKey(password, salt)
    return key
}

Temporary Secrets

# Python: Use temporary secrets safely
with SecureBytes.random(32) as temp_key:
    encrypted = encrypt_with_temp_key(data, temp_key)
# temp_key cleared automatically
// Java: Temporary secrets with try-with-resources
try (SecureBytes tempKey = SecureBytes.random(32)) {
    byte[] encrypted = encryptWithTempKey(data, tempKey.getBytes());
    return encrypted;
} // tempKey cleared automatically

Error Handling

// C: Ensure cleanup on all paths
int process_secure_data() {
    uint8_t* key = secure_alloc(32);
    if (!key) return -1;
    
    int result = process_with_key(key);
    
    // Always clear, regardless of result
    secure_free(key);
    return result;
}
// C#: Cleanup in finally block
byte[] key = null;
try {
    key = DeriveKey(password);
    return ProcessWithKey(key);
} finally {
    if (key != null) {
        Array.Clear(key, 0, key.Length);
    }
}

See Also