- The paper introduces DALC-CT, a dynamic analysis framework that verifies constant-time execution by ensuring invariant low-level instruction traces across secret inputs.
- It employs Valgrind-based instrumentation and a 13-category instruction classification system to detect variations caused by secret-dependent operations.
- Evaluation on cryptographic benchmarks demonstrates that DALC-CT effectively distinguishes constant-time and vulnerable implementations across different compilers and optimization levels.
Dynamic Analysis of Low-Level Code Traces for Constant-Time Verification
Motivation and Problem Statement
Timing side-channel attacks exploit minute variations in program execution characteristics, especially execution time, to exfiltrate sensitive data from high-assurance code such as cryptographic implementations. The traditional defense—constant-time programming—requires that code execution be completely insensitive to secret input values, guaranteeing that different secrets cannot result in diverging program paths, differential instruction mix, or secret-dependent data-dependent memory and control flow. Despite the importance of this property, practical and accurate verification remains unsolved. Existing methods either oversimplify (by analyzing source-level abstractions or intermediate representations) or are unreliable due to environmental noise (timing-based measurement). This paper introduces DALC-CT, a new framework that addresses these weaknesses by performing direct dynamic analysis over low-level binary instruction traces.
Approach
DALC-CT verifies constant-time execution by instrumenting binaries using Valgrind's Dynamic Binary Instrumentation (DBI) engine. Given a compiled program binary, a target function, and a set of secret input values, DALC-CT executes the function across the input space, records the instruction-level traces, and classifies each executed instruction into one of thirteen architecture-relevant categories (covering memory access, ALU, and control-flow, with subdivision by operation type and data width).
For each execution, DALC-CT logs a vector of per-class instruction counts—its “instruction mix.” Execution traces are collected for all secret input values, generating a multi-dimensional map of program execution. DALC-CT defines constant-time as trace invariance: for all secret input pairs, the trace vectors must be equivalent. Any difference signals a violation of constant-time logic and a potential side-channel vulnerability.
Figure 1: DALC-CT system architecture: input program, secrets, and target function are processed through DBI and instruction analysis to produce a formal verification report.
Implementation Details
DALC-CT is implemented as an open-source tool leveraging Valgrind for heavyweight dynamic instrumentation. For each function invocation, instructions are grouped and counted post-execution. Critical design decisions include the instruction classification system—partitioned by class and data type to account for operations with both uniform and varied latencies—and a lightweight fuzzer for secret input generation.
The classification system comprises 13 distinct classes split over memory loads/stores, ALU (light and heavy), and control-flow (conditional/unconditional), for integer, floating-point, and vector datatypes. Light ALU refers to single-cycle operations (e.g., addition, subtraction), while heavy ALU denotes multi-cycle operations (e.g., multiplication, division). This abstraction provides high diagnostic power while keeping analysis tractable, as exact instruction enumeration is infeasible on modern ISAs.
DalC-CT's system design ensures platform, compiler, and optimization independence; it operates strictly on the post-compile binary, downstream from all high-level abstractions and transformations, and thus directly reflects the program's real execution characteristics.
Evaluation and Empirical Validation
Evaluation demonstrates two case studies: password checking and modular exponentiation, both central operations in cryptographic libraries, leveraging datasets adopted from ct-tools and contemporary side-channel literature. DALC-CT consistently distinguishes constant-time and non-constant-time variants:
- Password Checking: Traditional character-wise equality check (with early exit on mismatch) produces instruction trace variations precisely correlated with the length of correct prefix matches, immediately exposing secret leakage. The constant-time version (XOR-based, full-scan) yields invariant trace vectors.
- Modular Exponentiation: The naive implementation, branching on secret exponent bits, produces trace vectors dependent on the Hamming weight of the exponent. When rewritten to mask and flatten all secret-dependent branches, trace invariance is restored.
Results are robust across compilation toolchains (GCC and Clang) and across optimization settings (O0–O2), confirming that DALC-CT is resilient to compiler-induced differences.
Comparison With Prior Art
Conventional methods fall into three categories: runtime statistical timing analysis (e.g., Dudect [reparaz2017dude]), dynamic (symbolic/concrete) instrumentation on intermediate representations (e.g., CTgrind [langley2010ctgrind], Binsec/Rel [daniel2020binsec]), and formal verification at the source or IR level (e.g., ct-verif [almeida2016verifying]). Each is limited in observability (due to abstraction, optimism, or noise) or in practicality (due to scalability).
DALC-CT’s approach of instruction trace logging overcomes these barriers: it assesses behaviors that directly result from optimizations, link-time arrangements, and platform idiosyncrasies, thereby reflecting the binary's execution as experienced on real hardware.
Limitations and Extensions
While DALC-CT is effective for logical constant-time property detection, it does not account for microarchitectural timing side channels—such as cache behavior, prefetch effects, or speculative execution artifacts—because it focuses on logical (retired) instruction streams rather than performance events. Incorporation of statistical methods or microarchitectural event modeling could address this limitation, but accurate modeling remains an unsolved systems research problem. Furthermore, while DALC-CT efficiently exposes violations on practical secret input sets, full coverage requires expansive input generation strategies (e.g., coverage-guided fuzzing or concolic execution).
The modular system design admits extensibility: the granularity of instruction classes can be increased for higher-resolution diagnostics, and support for new ISAs or environments can be integrated. These properties make DALC-CT a suitable foundation for high-assurance software development pipelines targeting platforms with evolving security requirements.
Theoretical and Practical Implications
Theoretically, DALC-CT unifies low-level program verification with constant-time security analysis via trace vector equivalence—a formalism that is both sound (in detecting secret-dependent control and data divergence) and implementation-aware. This bridges the longstanding divide between source-level theoretical assurance and executable-level empirical assurance.
Practically, DALC-CT equips developers, auditors, and cryptographic library maintainers with a concrete, automatable workflow for binary-level verification, reducing reliance on noisy, non-repeatable, or non-generalizable tests. Its independence from toolchain and platform specifics facilitates reproducible and reliable security auditing essential in high-assurance environments.
Future Directions
Immediate research avenues include tighter integration with advanced fuzzing engines (for improved path coverage), hybridization with symbolic execution for completeness guarantees, and co-analysis with microarchitectural event tracing to detect “second-order” side-channel vulnerabilities. As environments enforce stricter isolation and timing attack mitigations, frameworks like DALC-CT will be core to the development lifecycle.
Conclusion
DALC-CT provides a principled, robust, and scalable method for verifying the constant-time property at the binary execution level via analysis of low-level instruction traces. It eliminates critical blind spots inherent in abstraction- or timing-based approaches, fosters trust in cryptographic and side-channel-sensitive software, and paves the way for future tools with expanded semantic reach.