Skip to content

utils ct: replace black_box with a volatile optimization barrier in ct_eq_bytes and friends - #131

Open
ounsworth wants to merge 1 commit into
bcgit:release/0.1.3alphafrom
ounsworth:feature/ct_eq
Open

utils ct: replace black_box with a volatile optimization barrier in ct_eq_bytes and friends#131
ounsworth wants to merge 1 commit into
bcgit:release/0.1.3alphafrom
ounsworth:feature/ct_eq

Conversation

@ounsworth

Copy link
Copy Markdown
Contributor

Survey of other libraries (from their current upstream sources): libsodium's sodium_memcmp uses a volatile accumulator and volatile input pointers, and subtle's Choice::from is an #[inline(never)] read_volatile, so both place the barrier inside the loop per byte. constant_time_eq and RustCrypto's cmov chunk into machine words and apply an inline-asm barrier (or cmov/csel) per word, which is the shape adopted here. Graviola writes the whole byte loop in inline asm on x86_64 and aarch64. OpenSSL's CRYPTO_memcmp reads through volatile pointers only, and BoringSSL's uses no barrier at all.

Assembly was inspected by Claude from release builds on x86_64, i686, thumbv7em (Cortex-M4), riscv32imac, wasm32, msp430 (16-bit) and avr-none (8-bit): in every case the loop is load, xor, or, one native-width store to a stack slot and one reload, with a byte-wise tail, no bcmp/memcmp call and no data-dependent branch, and the final == 0 tests the volatile-loaded value.

Integration tests now sweep every length from 0 to 40 with a single bit flipped at every position, covering the word and tail paths on both sides of every boundary for 2, 4 and 8-byte words.

Co-Authored-By: Claude Fable 5

Closes #128

…t_eq_bytes and friends

Survey of other libraries (from their current upstream sources): libsodium's
`sodium_memcmp` uses a `volatile` accumulator and volatile input pointers,
and subtle's `Choice::from` is an `#[inline(never)]` `read_volatile`, so both
place the barrier inside the loop per byte. `constant_time_eq` and
RustCrypto's `cmov` chunk into machine words and apply an inline-asm barrier
(or `cmov`/`csel`) per word, which is the shape adopted here. Graviola writes
the whole byte loop in inline asm on x86_64 and aarch64. OpenSSL's
`CRYPTO_memcmp` reads through volatile pointers only, and BoringSSL's uses no
barrier at all.

Assembly was inspected by Claude from release builds on x86_64, i686, thumbv7em
(Cortex-M4), riscv32imac, wasm32, msp430 (16-bit) and avr-none (8-bit): in
every case the loop is load, xor, or, one native-width store to a stack slot
and one reload, with a byte-wise tail, no `bcmp`/`memcmp` call and no
data-dependent branch, and the final `== 0` tests the volatile-loaded value.

Integration tests now sweep every length from 0 to 40 with a single bit flipped at every
position, covering the word and tail paths on both sides of every boundary
for 2, 4 and 8-byte words.

Co-Authored-By: Claude Fable 5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant