Skip to content

Conversation

@zolk3ri
Copy link
Contributor

@zolk3ri zolk3ri commented Dec 26, 2025

This PR adds crypto.utils to basis/crypto, providing common utility functions for cryptographic implementations.

Overview

Cryptographic code often requires operations that are resistant to side-channel attacks. This vocabulary provides such utilities, starting with three constant-time operations.

Implementation Details

constant-time= compares two byte sequences in constant time:

:: constant-time= ( a b -- ? )
    a length b length = [
        0 a b [ bitxor bitor ] 2each 0 =
    ] [ f ] if ;
  • XORs corresponding bytes (equal bytes produce 0)
  • ORs all results into an accumulator (any difference sets bits)
  • Returns true only if accumulator is zero
  • Always examines all bytes when lengths match

constant-time-zero? checks if all bytes are zero:

: constant-time-zero? ( seq -- ? )
    0 [ bitor ] reduce 0 = ;
  • ORs all bytes together
  • Returns true only if result is zero
  • Always examines all bytes

constant-time-select selects between two integers without branching:

:: constant-time-select ( flag a b -- a/b )
    flag neg :> mask
    a mask bitand
    b mask bitnot bitand
    bitor ;
  • flag neg converts flag to a bitmask: flag = 1 -> -1 (all bits set), flag = 0 -> 0
  • a mask bitand keeps a if mask is all 1s, zeroes it if mask is 0
  • b mask bitnot bitand does the opposite for b
  • bitor combines them: one term keeps its value, the other is zeroed
  • If flag = 1, returns a; if flag = 0, returns b

Note on flag type: The flag parameter is 0/1 rather than boolean because Factor's ? word introduces a branch, which would defeat the purpose of constant-time selection. Using flag neg is fully branchless.

If you have a boolean and the condition itself is not secret, you can convert with 1 0 ?:

5 3 > 1 0 ? 42 99 constant-time-select ! => 42

Why is this needed?

Standard comparison functions like = and sequence= return early on the first mismatch:

B{ 0 0 0 0 } B{ 1 0 0 0 } ! Returns after 1 comparison
B{ 0 0 0 0 } B{ 0 0 0 1 } ! Returns after 4 comparisons

An attacker can exploit this timing difference to break MAC verification byte-by-byte. For each byte position, they try all 256 possible values and measure which one takes longest (indicating a match). This reduces a 2^128 brute force to ~256 * 16 = 4096 attempts for a 16-byte tag.

Similarly, conditional branches can leak information through timing or speculative execution. The constant-time-select function avoids this by using arithmetic operations that always execute the same instructions regardless of the flag value.

The same principle applies to constant-time-zero?. A naive implementation like 0 [ = ] all? returns early on the first non-zero byte, revealing its position. The constant-time version always examines all bytes.

Usage

USING: crypto.utils byte-arrays ;

! Compare two byte sequences in constant time
B{ 0x1a 0x2b 0x3c 0x4d } B{ 0x1a 0x2b 0x3c 0x4d } constant-time=
! => t

B{ 0x1a 0x2b 0x3c 0x4d } B{ 0x1a 0x2b 0x3c 0x00 } constant-time=
! => f

! Check if all bytes are zero
B{ 0x00 0x00 0x00 0x00 } constant-time-zero?
! => t

B{ 0x00 0x01 0x00 0x00 } constant-time-zero?
! => f

! Select between two values without branching
! constant-time-select ( flag a b -- a/b ): flag = 1 returns a, flag = 0 returns b
1 42 99 constant-time-select
! => 42 (flag = 1, selected a)

0 42 99 constant-time-select
! => 99 (flag = 0, selected b)

Testing

constant-time= (14 tests):

  • Equal sequences (empty, single byte, multi-byte, all 255s)
  • Unequal sequences with same length (difference at start, middle, end)
  • Unequal sequences with different lengths
  • 16-byte arrays (typical MAC tag size)

constant-time-zero? (9 tests):

  • All zeros (empty, single, multi-byte, 16-byte)
  • Non-zero at various positions (start, middle, end)

constant-time-select (10 tests):

  • flag = 1 correctly returns a when a is positive, zero, or negative
  • flag = 0 correctly returns b when b is positive, zero, or negative
  • Edge cases where a and b are equal (verifies no corruption)

Future Work

This vocabulary is a prerequisite for crypto.poly1305 and other cryptographic implementations that require constant-time operations.

Constant-time operations for cryptographic implementations:
- constant-time= : byte sequence comparison
- constant-time-zero? : zero check
- constant-time-select : branchless selection

Prerequisite for crypto.poly1305 and future crypto vocabularies.
@mrjbq7 mrjbq7 merged commit 52347a3 into factor:master Dec 27, 2025
3 checks passed
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.

2 participants