Security Implementation Guide

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.

Overview

This guide collects coding patterns for comparing, selecting and clearing secrets, per language. The snippets are illustrations for code that handles secrets; they do not describe how each MetaMUI binding is implemented. Neither the snippets nor any MetaMUI implementation has been measured for timing or any other side-channel leakage (finding A48), so nothing here claims constant-time behaviour. In interpreted, JIT-compiled or garbage-collected runtimes (Python, JavaScript/TypeScript, the JVM) a branch-free source pattern does not guarantee branch-free machine code.

Language-Specific Security Implementations

Rust Implementation

Dependencies

[dependencies]
subtle = "2.6"
zeroize = { version = "1.8", features = ["derive"] }

Branch-Free Comparison and Selection (timing unmeasured)

use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};

// Equality through subtle::ConstantTimeEq (timing not measured)
pub fn ct_eq(a: &[u8], b: &[u8]) -> bool {
    a.ct_eq(b).into()
}

// Selection through subtle::ConditionallySelectable (timing not measured)
pub fn ct_select(a: u32, b: u32, choice: bool) -> u32 {
    u32::conditional_select(&a, &b, Choice::from(choice as u8))
}

// Byte-array comparison; the length check is public (timing not measured)
pub fn secure_compare(a: &[u8], b: &[u8]) -> bool {
    if a.len() != b.len() {
        return false;
    }
    a.ct_eq(b).into()
}

Memory Security

use zeroize::{Zeroize, ZeroizeOnDrop};

#[derive(Zeroize, ZeroizeOnDrop)]
pub struct SecretKey {
    data: Vec<u8>,
}

impl Drop for SecretKey {
    fn drop(&mut self) {
        self.data.zeroize();
    }
}

Python Implementation

Branch-Free Comparison and Selection (timing unmeasured)

import hmac
import struct

def constant_time_compare(a: bytes, b: bytes) -> bool:
    """Compare with hmac.compare_digest, the stdlib's comparison for secrets (timing not measured)."""
    return hmac.compare_digest(a, b)

def constant_time_select(a: int, b: int, choice: bool) -> int:
    """Select without a branch on `choice`; Python ints are arbitrary-precision, so no timing guarantee."""
    mask = -int(choice)  # 0x00000000 or 0xFFFFFFFF
    return (a & mask) | (b & ~mask)

def constant_time_is_zero(value: int) -> bool:
    """Zero test without a branch on `value`; no timing guarantee in Python."""
    # Propagate any set bit to all positions
    value = value | (value >> 16)
    value = value | (value >> 8)
    value = value | (value >> 4)
    value = value | (value >> 2)
    value = value | (value >> 1)
    return (value & 1) == 0

Memory Security

import ctypes
import sys

def secure_zero(data: bytearray) -> None:
    """Securely zero memory."""
    if sys.platform == "win32":
        ctypes.windll.kernel32.SecureZeroMemory(
            ctypes.c_char_p(ctypes.addressof(ctypes.c_char.from_buffer(data))),
            ctypes.c_size_t(len(data))
        )
    else:
        # Use ctypes memset
        ctypes.memset(
            ctypes.addressof(ctypes.c_char.from_buffer(data)),
            0,
            len(data)
        )

class SecureBytes:
    def __init__(self, data: bytes):
        self._data = bytearray(data)
    
    def __del__(self):
        secure_zero(self._data)
    
    @property
    def data(self) -> bytes:
        return bytes(self._data)

TypeScript Implementation

Branch-Free Comparison and Selection (timing unmeasured)

/**
 * Equality for Uint8Arrays without an early exit (a JS engine gives no timing guarantee)
 */
export function constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean {
    if (a.length !== b.length) {
        return false;
    }
    
    let result = 0;
    for (let i = 0; i < a.length; i++) {
        result |= a[i] ^ b[i];
    }
    
    return result === 0;
}

/**
 * Selection between two values without a branch on the values
 */
export function constantTimeSelect(a: number, b: number, choice: boolean): number {
    const mask = -(choice ? 1 : 0);
    return (a & mask) | (b & ~mask);
}

/**
 * Conditional copy through a mask
 */
export function constantTimeCopy(
    dest: Uint8Array,
    src: Uint8Array,
    choice: boolean
): void {
    const mask = choice ? 0xFF : 0x00;
    for (let i = 0; i < dest.length; i++) {
        dest[i] = (src[i] & mask) | (dest[i] & ~mask);
    }
}

Memory Security

/**
 * Securely clear a Uint8Array
 */
export function secureClear(data: Uint8Array): void {
    // Overwrite with random data first
    if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
        crypto.getRandomValues(data);
    }
    // Then zero
    data.fill(0);
}

export class SecureBuffer {
    private data: Uint8Array;
    
    constructor(size: number) {
        this.data = new Uint8Array(size);
    }
    
    get(): Uint8Array {
        return this.data;
    }
    
    clear(): void {
        secureClear(this.data);
    }
    
    // Automatically clear on garbage collection (if possible)
    [Symbol.dispose](): void {
        this.clear();
    }
}

Swift Implementation

Branch-Free Comparison and Selection (timing unmeasured)

import Foundation

/// Equality without an early exit (timing not measured)
func constantTimeEqual(_ a: [UInt8], _ b: [UInt8]) -> Bool {
    guard a.count == b.count else { return false }
    
    var result: UInt8 = 0
    for i in 0..<a.count {
        result |= a[i] ^ b[i]
    }
    
    return result == 0
}

/// Selection through a mask (timing not measured)
func constantTimeSelect<T>(_ a: T, _ b: T, _ choice: Bool) -> T 
    where T: FixedWidthInteger {
    let mask = T(choice ? -1 : 0)
    return (a & mask) | (b & ~mask)
}

/// Conditional swap through a mask (timing not measured)
func constantTimeSwap<T>(_ a: inout T, _ b: inout T, _ choice: Bool)
    where T: FixedWidthInteger {
    let mask = T(choice ? -1 : 0)
    let temp = (a ^ b) & mask
    a ^= temp
    b ^= temp
}

Memory Security

import Foundation

class SecureData {
    private var data: Data
    
    init(_ data: Data) {
        self.data = data
    }
    
    deinit {
        clear()
    }
    
    func clear() {
        data.withUnsafeMutableBytes { bytes in
            if let baseAddress = bytes.baseAddress {
                memset_s(baseAddress, bytes.count, 0, bytes.count)
            }
        }
    }
    
    func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
        try data.withUnsafeBytes(body)
    }
}

Kotlin Implementation

Branch-Free Comparison and Selection (timing unmeasured)

/**
 * Equality without an early exit (the JVM gives no timing guarantee)
 */
fun constantTimeEqual(a: ByteArray, b: ByteArray): Boolean {
    if (a.size != b.size) return false
    
    var result = 0
    for (i in a.indices) {
        result = result or (a[i].toInt() xor b[i].toInt())
    }
    
    return result == 0
}

/**
 * Selection through a mask
 */
fun constantTimeSelect(a: Int, b: Int, choice: Boolean): Int {
    val mask = if (choice) -1 else 0
    return (a and mask) or (b and mask.inv())
}

/**
 * Byte-array selection through a mask
 */
fun constantTimeSelectBytes(a: ByteArray, b: ByteArray, choice: Boolean): ByteArray {
    require(a.size == b.size) { "Arrays must have same size" }
    
    val mask = if (choice) 0xFF else 0x00
    return ByteArray(a.size) { i ->
        ((a[i].toInt() and mask) or (b[i].toInt() and mask.inv())).toByte()
    }
}

Memory Security

import java.security.SecureRandom
import java.util.Arrays

class SecureByteArray(size: Int) : AutoCloseable {
    private val data = ByteArray(size)
    private val random = SecureRandom()
    
    fun get(): ByteArray = data.clone()
    
    fun clear() {
        // Overwrite with random data
        random.nextBytes(data)
        // Then zero
        Arrays.fill(data, 0.toByte())
    }
    
    override fun close() {
        clear()
    }
    
    protected fun finalize() {
        clear()
    }
}

Common Security Patterns

1. Branch-Free Modular Reduction (timing unmeasured)

// Barrett reduction without a data-dependent branch (timing not measured)
pub fn barrett_reduce(a: u32) -> u16 {
    const BARRETT_MULTIPLIER: u32 = 20159;
    const Q: u32 = 3329;
    
    let t = (a * BARRETT_MULTIPLIER) >> 26;
    let t = a - t * Q;
    
    // Conditional subtraction through a mask
    let mask = ((t >= Q) as u32).wrapping_neg();
    (t - (Q & mask)) as u16
}

2. Secure Key Derivation

def derive_key_secure(password: bytes, salt: bytes, iterations: int = 100000) -> bytes:
    """Derive key using PBKDF2 with secure parameters."""
    import hashlib
    
    # Use PBKDF2 with SHA-256
    key = hashlib.pbkdf2_hmac('sha256', password, salt, iterations, dklen=32)
    
    # Clear password from memory
    password_array = bytearray(password)
    secure_zero(password_array)
    
    return key

3. Scalar Multiplication (side-channel behaviour not claimed)

// Montgomery ladder: the same operations for every scalar bit (design intent;
// a JavaScript engine gives no timing guarantee)
function scalarMultiply(scalar: Uint8Array, point: Point): Point {
    let r0 = Point.IDENTITY;
    let r1 = point.clone();
    
    for (let i = 255; i >= 0; i--) {
        const bit = (scalar[Math.floor(i / 8)] >> (i % 8)) & 1;
        
        // Conditional swap through a mask (helper not shown)
        constantTimeSwap(r0, r1, bit === 1);
        
        // Point operations
        r1 = r0.add(r1);
        r0 = r0.double();
        
        // Swap back
        constantTimeSwap(r0, r1, bit === 1);
    }
    
    return r0;
}

Security Testing

Timing Variance Smoke Check

def test_constant_time_operation(operation, inputs):
    """Coarse timing smoke check.

    Passing it does not show constant-time behaviour: wall-clock jitter hides
    small leaks, and a claim needs a statistical leakage test (for example
    dudect) on the target. MetaMUI has run no such measurement (A48).
    """
    import time
    import statistics
    
    times = []
    for input_data in inputs:
        start = time.perf_counter_ns()
        operation(input_data)
        end = time.perf_counter_ns()
        times.append(end - start)
    
    # Check for statistical anomalies
    mean = statistics.mean(times)
    stdev = statistics.stdev(times)
    
    # Timing should not vary significantly
    assert stdev < mean * 0.05, "Operation may not be constant-time"

Memory Leak Detection

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_no_memory_leaks() {
        // Run with valgrind or similar tool
        let secret = SecretKey::new(&[0u8; 32]);
        drop(secret);
        // Memory should be cleared
    }
}

Security Audit Checklist

The checklist is for any code that handles secrets. For MetaMUI the side-channel and timing items are open: no target has been measured (A48).

References

  1. Constant-Time Crypto
  2. Side-Channel Attacks
  3. Secure Coding Practices
  4. OWASP Cryptographic Storage