attacks but do not protect against other side-channel attacks such as power analysis or electromagnetic emanation. Additionally, the compiler and CPU may introduce timing variations through optimizations or speculative execution.
ddn.crypto.ct
Constant-Time Arithmetic Utilities for Side-Channel Resistance.
This module provides constant-time operations that do not leak information through timing side-channels. All operations execute in the same amount of time regardless of the values of their operands.
Security
Note
Types 2
Compares two byte arrays in constant time.
This function always examines all bytes regardless of whether a mismatch is found, preventing timing attacks that could determine the position of the first differing byte.
This is an alias of safeEqual from ddn.api.crypto.common, so the two names can never drift apart.
Parameters
a | First byte array to compare. |
b | Second byte array to compare. |
Returns
true if the arrays have the same length and content,
false otherwise.
Side-Channel Resistance Guidelines
This module provides building blocks for constant-time cryptographic implementations. To properly use these functions:
- Always use
safeEqual(aliased asctEqual) instead of `==`for secret comparisons.
- Use
ctSelectandctSwapinstead of conditional brancheswhen the condition depends on secret data.
- Ensure loop bounds do not depend on secret values.
- Be aware that BigInt operations like `%` and `*` may not be
fully constant-time; use Montgomery multiplication for modular arithmetic when timing is critical.
- Memory access patterns should not depend on secrets; use
constant-time table lookups or avoid tables entirely.
Limitations:
- Compiler optimizations may introduce timing variations.
- CPU features like branch prediction, caching, and speculative
execution can leak information.
- These functions protect against timing attacks but not against
other side-channel attacks like power analysis.
Functions 10
T ctSelect(T)(uint condition, T a, T b) if (is(T == ubyte) || is(T == ushort) || is(T == uint) || is(T == ulong)) pure nothrow @nogc @safeSelects between two values in constant time.void ctSwap(T)(uint doSwap, ref T a, ref T b) if (is(T == ubyte) || is(T == ushort) || is(T == uint) || is(T == ulong)) pure nothrow @nogc @safeSwaps two values conditionally in constant time.void ctSwapBigInt(uint doSwap, ref BigInt a, ref BigInt b) pure nothrow @safeSwaps two BigInt values conditionally in constant time.BigInt ctSelectBigInt(uint condition, BigInt a, BigInt b) pure nothrow @safeSelects between two BigInt values in constant time.void ctSwapBytes(uint doSwap, ubyte[] a, ubyte[] b) pure nothrow @nogc @safeSwaps two byte arrays conditionally in constant time.void ctCopyBytes(uint doCopy, ubyte[] dst, const(ubyte)[] src) pure nothrow @nogc @safeCopies `src` to `dst` conditionally in constant time.uint ctLessThan(T)(T a, T b) if (is(T == ubyte) || is(T == ushort) || is(T == uint) || is(T == ulong)) pure nothrow @nogc @safeCompares two unsigned integers in constant time, returning 1 if a < b.uint ctEquals(T)(T a, T b) if (is(T == ubyte) || is(T == ushort) || is(T == uint) || is(T == ulong)) pure nothrow @nogc @safeCompares two unsigned integers in constant time, returning 1 if a == b.uint ctGetBit(const(ubyte)[] data, size_t bitIndex) pure nothrow @nogc @safeExtracts a bit from a byte array in constant time.uint ctGetBitBigInt(BigInt scalar, size_t bitIndex) pure nothrow @safeExtracts a bit from a BigInt scalar in constant time.