feat(digest): constant-time comparison #149

Merged
argus merged 1 commit from loop/96-constant-time-compare into main 2026-10-07 01:12:25 +00:00
Collaborator

Closes #96

Digest.equalsConstantTime(other) is the primary API (owner decision recorded on the issue); constantTimeEqual(a: Bytes, b: Bytes) is a thin wrapper over it. Ordinary TorbScript in std/digest, no native, no seed change.

  • Length mismatch answers false (lengths are not secret); otherwise every byte pair is read and differences are combined by XOR/OR with no early exit.
  • Tests in std/digest/tests/constant-time.test.trb: equal, first/last/middle byte, lowest bit, unequal length (both orders), empty/empty, empty/non-empty, each checked against ==; plus an HmacSha256 result compared without conversion.
  • docs/standard-library/digest.md documents the method and wrapper with trb check examples, states what is promised (no data-dependent early exit in the source) and not (no guarantee against compiler or CPU timing effects); skills/ and the std index regenerated.
  • docs/design/WEB.md F7 row marked done for the comparison part.

Gates: sh tools/gates.sh a ran in a gcc container (no toolchain on the worker host). The first run was red on five gates, all caused by this change and fixed in the commit: format --check of the new test, the stale std index (docs index, docs check), stale skills/ (docs skill), and an example using Sha256.of(...) with parentheses (doc --check). After the fixes format --check ., docs index --check, docs skill --check, doc --check --no-run std and torb test std/digest (43 passed) are green; the remaining gates were green in the first run. Forge CI is the full confirmation.

Closes #96 `Digest.equalsConstantTime(other)` is the primary API (owner decision recorded on the issue); `constantTimeEqual(a: Bytes, b: Bytes)` is a thin wrapper over it. Ordinary TorbScript in `std/digest`, no native, no seed change. - Length mismatch answers `false` (lengths are not secret); otherwise every byte pair is read and differences are combined by XOR/OR with no early exit. - Tests in `std/digest/tests/constant-time.test.trb`: equal, first/last/middle byte, lowest bit, unequal length (both orders), empty/empty, empty/non-empty, each checked against `==`; plus an `HmacSha256` result compared without conversion. - `docs/standard-library/digest.md` documents the method and wrapper with `trb check` examples, states what is promised (no data-dependent early exit in the source) and not (no guarantee against compiler or CPU timing effects); `skills/` and the std index regenerated. - `docs/design/WEB.md` F7 row marked done for the comparison part. Gates: `sh tools/gates.sh a` ran in a gcc container (no toolchain on the worker host). The first run was red on five gates, all caused by this change and fixed in the commit: `format --check` of the new test, the stale std index (`docs index`, `docs check`), stale `skills/` (`docs skill`), and an example using `Sha256.of(...)` with parentheses (`doc --check`). After the fixes `format --check .`, `docs index --check`, `docs skill --check`, `doc --check --no-run std` and `torb test std/digest` (43 passed) are green; the remaining gates were green in the first run. Forge CI is the full confirmation.
feat(digest): constant-time comparison
Some checks failed
ci / every host emits the same C (pull_request) Successful in 10m29s
ci / the playground (torb as WebAssembly) (pull_request) Successful in 9m31s
ci / archives (pull_request) Has been skipped
ci / tier A (linux-x64) (pull_request) Successful in 8m53s
ci / tools/release-sync and tools/registry (linux-x64) (pull_request) Successful in 1m24s
ci / pack the seed (pull_request) Successful in 23s
ci / gates (pull_request) Successful in 0s
ci / commit messages (push) Successful in 9s
ci / what the change touches (push) Successful in 5s
images / cr.torb.dev/torbscript/registry (push) Has been skipped
images / cr.torb.dev/torbscript/release-sync (push) Has been skipped
images / cr.torb.dev/torbscript/site (push) Has been skipped
ci / bootstrap and conformance (windows-x64) (push) Has been skipped
ci / bootstrap and conformance (linux-arm64) (push) Has been skipped
ci / bootstrap and conformance (macos-arm64) (push) Has been skipped
images / cr.torb.dev/torbscript/site:edge (main) (push) Successful in 8m5s
ci / which runners exist (push) Successful in 1s
ci / bootstrap (linux-x64) (push) Successful in 16s
ci / cross-compiled on linux-x64 (windows-x64) (push) Has been skipped
ci / cross-compiled on linux-x64 (linux-arm64) (push) Has been skipped
ci / cross-compiled on linux-x64 (macos-arm64) (push) Has been skipped
ci / the VS Code extension (editors/vscode) (push) Successful in 20s
ci / tier A (linux-x64) (push) Successful in 9m19s
ci / tier B (linux-x64) (push) Successful in 10m5s
ci / pack the seed (push) Has been skipped
ci / tools/release-sync and tools/registry (linux-x64) (push) Successful in 47s
ci / every host emits the same C (push) Successful in 5m54s
ci / archives (push) Has been skipped
ci / the playground (torb as WebAssembly) (push) Failing after 3m9s
ci / gates (push) Failing after 0s
cd9c550032
Digest.equalsConstantTime compares every byte pair and combines the differences by XOR/OR, with no early exit; constantTimeEqual is a thin wrapper over it for plain Bytes. Tests, docs and the regenerated skills in the same commit; WEB.md F7 marks the comparison part done.
argus approved these changes 2026-10-07 01:12:23 +00:00
argus left a comment

Approved (head cd9c550, CI combined status success).

  • Tests: ok. New constant-time.test.trb covers equal, first/middle/last byte, lowest bit, unequal length (both orders), empty cases, each checked against ==, plus an HMAC case. CI green on this head.
  • Correctness: ok. Length is compared first (not secret), then every byte pair is OR-accumulated with no early exit. The wrapper delegates to the method.
  • Scope: ok. Matches #96 as the PR body states it. No native code and no seed change.
  • Security/secrets: ok. Nothing sensitive. The docs state what is and is not promised.
  • Data: n/a.
  • Docs: ok. digest.md, the std index, skills/ and the WEB.md F7 row are updated; the docs/skill/index gates are green.
  • Architecture/placement: no new boundary beyond a public function in the existing std/digest.

Non-blocking: none.

**Approved** (head cd9c550, CI combined status success). - Tests: ok. New `constant-time.test.trb` covers equal, first/middle/last byte, lowest bit, unequal length (both orders), empty cases, each checked against `==`, plus an HMAC case. CI green on this head. - Correctness: ok. Length is compared first (not secret), then every byte pair is OR-accumulated with no early exit. The wrapper delegates to the method. - Scope: ok. Matches #96 as the PR body states it. No native code and no seed change. - Security/secrets: ok. Nothing sensitive. The docs state what is and is not promised. - Data: n/a. - Docs: ok. digest.md, the std index, `skills/` and the WEB.md F7 row are updated; the docs/skill/index gates are green. - Architecture/placement: no new boundary beyond a public function in the existing `std/digest`. Non-blocking: none.
argus merged commit cd9c550032 into main 2026-10-07 01:12:25 +00:00
argus deleted branch loop/96-constant-time-compare 2026-10-07 01:12:25 +00:00
Sign in to join this conversation.
No reviewers
No labels
loop
ready
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
torbscript/language!149
No description provided.