|
| 1 | +# Applying IMAGEHARDER-style hardening to libsodium |
| 2 | + |
| 3 | +The same practices used in IMAGEHARDER (bounded inputs, explicit error mapping, metrics, and sandboxing) can be mirrored when embedding |
| 4 | +[libsodium](https://github.com/jedisct1/libsodium) for cryptographic workloads. This note outlines a secure-integration profile |
| 5 | +suitable for Xen domU/CI builders. |
| 6 | + |
| 7 | +## Goals |
| 8 | +- Stable, minimal FFI boundary with explicit error types. |
| 9 | +- Deterministic builds that pin versions and compiler flags. |
| 10 | +- Isolation of key material and misuse-resistant APIs. |
| 11 | +- Observability: metrics, tamper-evident audit logs, and build provenance. |
| 12 | + |
| 13 | +## Build strategy |
| 14 | +- **Version pinning**: vendor a known-good libsodium release and verify with `sha256sum` before build. |
| 15 | +- **Compiler flags**: prefer `-fstack-protector-strong -D_FORTIFY_SOURCE=3 -fPIC -O2 -pipe -fno-plt` plus |
| 16 | + `-fstack-clash-protection` on supported toolchains. Enable `-fcf-protection=full` for CET-capable targets. |
| 17 | +- **Linking**: prefer static linking in minimal domU images; use `RUSTFLAGS="-C target-feature=+crt-static"` when pairing with |
| 18 | + Rust consumers to avoid dynamic search-path issues. |
| 19 | +- **Reproducibility**: capture build metadata (compiler, flags, git SHA) in an SBOM; wire `generate-sbom.sh` as part of CI. |
| 20 | + |
| 21 | +## FFI surface design |
| 22 | +- Wrap each libsodium primitive in narrow functions that accept length-checked slices and return `Result<Output, CryptoError>`. |
| 23 | +- Prohibit raw pointer exposure; translate C error codes into typed Rust errors with context. |
| 24 | +- Add zeroization hooks (`zeroize::Zeroize`) for buffers that carry keys, nonces, or secrets. |
| 25 | +- Enforce domain separation by tagging APIs per purpose (e.g., `Auth`, `Aead`, `Kdf`) to discourage misuse. |
| 26 | + |
| 27 | +## Runtime safeguards |
| 28 | +- **Initialization guard**: perform a one-time `sodium_init()` with failure handling before exposing any primitive. |
| 29 | +- **Key lifecycle**: keep keys in guarded memory (e.g., `secrecy::SecretVec`) and avoid serialization. Provide key-generation |
| 30 | + helpers that default to high-entropy RNGs (libsodium already defaults to `randombytes_buf`), and expose optional hardware RNG |
| 31 | + seeding for dom0/domU if available. |
| 32 | +- **Parameter validation**: validate nonce sizes, tag lengths, and buffer boundaries before calling into libsodium. |
| 33 | +- **Constant-time expectations**: document which APIs are constant-time and block callers from using them for unrelated purposes |
| 34 | + (e.g., no generic equality on MACs without constant-time compare). |
| 35 | + |
| 36 | +## Observability and policy |
| 37 | +- Emit Prometheus counters for successes/failures per primitive (without leaking secrets) and gauge unexpected parameter |
| 38 | + rejections. |
| 39 | +- Capture audit logs that include operation type, key identifier, and policy decisions; ship logs over mTLS to central storage. |
| 40 | +- Include a policy module that enforces approved cipher suites (e.g., `xchacha20poly1305_ietf`) and blocks deprecated ones. |
| 41 | + |
| 42 | +## Testing and validation |
| 43 | +- **KATs**: integrate libsodium's known-answer tests and add fuzzers around the FFI boundary to catch length/parameter issues. |
| 44 | +- **Memory checks**: run under `valgrind`/`ASan` in CI; ensure zeroization paths are covered. |
| 45 | +- **Cross-platform**: validate builds under Xen domU, containers, and bare-metal to confirm consistent instruction sets and |
| 46 | + `RDRAND` availability. |
| 47 | + |
| 48 | +## Example integration sketch |
| 49 | + |
| 50 | +```rust |
| 51 | +// Pseudocode illustrating a narrow AEAD wrapper |
| 52 | +pub fn seal(key: &SecretVec<u8>, nonce: &[u8; crypto_aead_xchacha20poly1305_ietf_NPUBBYTES], |
| 53 | + aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, CryptoError> { |
| 54 | + if plaintext.len() > MAX_MESSAGE || aad.len() > MAX_AAD { |
| 55 | + return Err(CryptoError::InputTooLarge); |
| 56 | + } |
| 57 | + |
| 58 | + let mut ciphertext = vec![0u8; plaintext.len() + TAG_LEN]; |
| 59 | + let mut clen = 0usize; |
| 60 | + let rc = unsafe { |
| 61 | + crypto_aead_xchacha20poly1305_ietf_encrypt( |
| 62 | + ciphertext.as_mut_ptr(), |
| 63 | + &mut clen, |
| 64 | + plaintext.as_ptr(), |
| 65 | + plaintext.len() as u64, |
| 66 | + aad.as_ptr(), |
| 67 | + aad.len() as u64, |
| 68 | + std::ptr::null(), |
| 69 | + nonce.as_ptr(), |
| 70 | + key.expose_secret().as_ptr(), |
| 71 | + ) |
| 72 | + }; |
| 73 | + |
| 74 | + if rc != 0 { |
| 75 | + return Err(CryptoError::EncryptionFailed); |
| 76 | + } |
| 77 | + |
| 78 | + ciphertext.truncate(clen); |
| 79 | + Ok(ciphertext) |
| 80 | +} |
| 81 | +``` |
| 82 | + |
| 83 | +## Operational checklist |
| 84 | +- Verify SBOM provenance for each release and store alongside build artifacts. |
| 85 | +- Rotate keys on a fixed schedule; enforce non-reuse of nonces via per-key counters. |
| 86 | +- Run `cargo deny` (or equivalent) to flag transitive CVEs in Rust bindings. |
| 87 | +- Add a chaos test that simulates allocator failures to ensure safe unwinding. |
| 88 | + |
| 89 | +Following this pattern mirrors IMAGEHARDER's focus on bounded inputs, explicit error handling, and strong observability—adapted to the |
| 90 | +cryptographic domain served by libsodium. |
0 commit comments