Secure Memory Handling
MetaMUI Crypto Primitives implements comprehensive secure memory handling across all 10 supported platforms.
Key Features
Automatic Memory Clearing
- All sensitive data is automatically cleared after use
- No manual memory management required in most cases
- Platform-specific secure clearing functions
Memory Protection
- Guard pages for sensitive allocations
- Memory locking to prevent swapping (where supported)
- Constant-time memory operations
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);
}
}