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).
- All cryptographic comparisons use constant-time functions
- No secret-dependent branches in critical paths
- Memory containing secrets is zeroed after use
- Input validation prevents buffer overflows
- Side-channel protections are implemented
- Random number generation uses secure sources
- Error messages don’t leak sensitive information
- Timing variations are within acceptable bounds
- Code follows secure coding standards
- Security tests are comprehensive and passing