// Secure memory overwrite primitives, extracted from Basic so that allocators can depend on // wiping without dragging in the rest of Basic. Apart from libsodium it imports Basic. // // USE_SODIUM routes the wipe through libsodium's sodium_memzero, the portable way to get a // store the optimizer is not permitted to elide. Without it, x64 wipes with inline assembly the // optimizer leaves alone, and other CPUs (arm64) with a plain loop it is allowed to remove. // // In this template it defaults to false, so the build needs no libsodium. Module parameters // only reach a module from the main program, and Basic's arenas import this one directly, so // this default is what they get. If the app keeps secrets in memory, add the jai-sodium module // to modules/ and set the default back to true. #module_parameters () (USE_SODIUM := false, LOAD_TESTS := false); Basic :: #import "Basic"; // assert #if USE_SODIUM { Sodium :: #import "jai-sodium"; // Byte counts are signed ints throughout this module; libsodium wants an unsigned length. secure_memzero :: inline (memory: *void, count: int) { if count <= 0 return; Sodium.sodium_memzero(memory, count.(u64,no_check)); } } #if LOAD_TESTS { #load "tests/secure_memory_tests.jai"; } // Wipe_Function :: #type (memory: *void, count: int); // Macros for ease-of-use secure_overwrite :: (s: string) #expand { memory_secure_overwrite(s.data, s.count); } secure_overwrite :: (a: []$T) #expand { memory_secure_overwrite(a.data, a.count * size_of(T)); } secure_overwrite :: (memory: *$T) #expand { memory_secure_overwrite(memory, size_of(T)); } secure_overwrite :: (memory: *$T, count: int) #expand { memory_secure_overwrite(memory, count); } // sodium_memzero only zeroes, so a non-zero fill still takes the rep stos / loop path. memory_secure_overwrite :: inline (memory: *void, count: int, value: u8 = 0) { Basic.assert(count >= 0); if count <= 0 return; #if USE_SODIUM { if value == 0 { secure_memzero(memory, count); return; } } #if CPU == .X64 { #asm { memory === di; value === a; count === c; rep_stos.b memory, value, count; } } else { memu8 := cast(*u8) memory; for 0..count-1 { memu8[it] = value; } } }