Secure Memory Handling
Side-channel claim limit. Nothing on this page claims constant-time behaviour or side-channel resistance for any target: the side-channel harness covers nothing (finding A48) and no measurement exists. The Rust and C cores are designed for constant time and unmeasured; managed runtimes (JIT, GC, bounds checks) make no timing claim. Where this page names constant-time or side-channel techniques, in prose or in code, they are design intent or recommendations, not measured properties.
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 (design intent; not claimed, unmeasured, A48)
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/SovereignWallet-Network/metamui-crypto-go/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**: not claimed (unmeasured, A48)
- **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);
}
}