Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitmodules
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,3 +4,7 @@
[submodule "libs/nistcurves"]
path = libs/nistcurves
url = https://github.com/JC-000/c64-nist-curves.git
[submodule "libs/x25519"]
path = libs/x25519
url = https://github.com/JC-000/c64-x25519.git
branch = master
36 changes: 30 additions & 6 deletions CLAUDE.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,7 +51,11 @@ Public symbols (calling conventions are AX=pointer-low/high-byte except
where noted, buffers provided by caller, keys/IVs passed via fixed
buffers in the crypto BSS — see per-module headers for details):

X25519 / field arithmetic (in-tree; c64-x25519 overlay deferred, see #33)
X25519 / field arithmetic
Default: in-tree `src/crypto/{x25519,fe25519}.s`.
Opt-in: sibling `libs/x25519@v0.4.0` via `make USE_X25519_SIBLING=1`
(UCI backend only — see Known issues for the ip65 fit blocker; Phase
C.5). Sibling and in-tree both expose the same ABI:
x25519_scalarmult — X25519 scalar × point, 32-byte buffers
fe25519_mul, fe25519_sqr, fe25519_inv

Expand DownExpand Up@@ -338,11 +342,31 @@ Five latent bugs and three new ones were cleared to get here:
`tools/uci/test_https_print_body.py` with a mixed-case response
body. `http_resp_buf` still holds raw ASCII — only the render
pipeline is translated.
- X25519 REU overlay deferred (c64-x25519 #33). Phase C.1 (`6c9d2a3`)
integrated the sibling optimised X25519 as a REU overlay but hung
inside the Montgomery ladder under BACKEND=uci at 48 MHz; rolled
back in `b133ac7`. A retry against the v0.3.0 tag failed the same
way. X25519 stays in-tree until the upstream hang is resolved.
- **X25519 sibling (Phase C.5)** — `make USE_X25519_SIBLING=1` builds
against `libs/x25519@v0.4.0`. Default is OFF; the in-tree
implementation remains the shipped default until the flag flip is
decided. The Phase C.1 hang and v0.3.0 retry rollback are both
closed by upstream PR #36 + v0.4.0 H2 (defensive REU register init
at every `x25519_scalarmult` / `fe25519_mul` / `_sqr` / `_mul_a24`
/ `_inv` entry — eliminates the `do_swap` residue confound on
`$DF04`/`$DF0A` that produced the wrong-result symptom). Verified
on U64E at 48 MHz: HTTPS handshake completes in ~101 s
(vs ~87 s under in-tree X25519; the +14 s is consistent with
v0.4.0's release-notes-documented +27 % scalarmult cost over v0.3.0
for the L1-L29 CT closures). **ip65 backend overflows
CRYPTO_RESIDENT by 1 KB under the flag** — UCI is the supported
path; ip65 fit is a separate cfg-restructure follow-up. See
`tools/integration/build_x25519.sh` for the staging layout.
- **CRYPTO_OVERLAY address collision lesson**: under the Phase C.5
flag, `X25519_RODATA` + `X25519_BSS` live at `$4200-$50FF`.
Any test harness that DMA-injects a 6502 stub or scratch into
`CRYPTO_OVERLAY` must avoid that range. `tools/uci/test_https_local.py`
historically placed `ROUTINE_ADDR=$4200` (+ 5 sibling addresses up
to `$4542`) inside that range and silently corrupted `x25_basepoint`,
`fe_p`, `mul38_*_tab`, and `sqr_lo/hi` — every `fe25519_mul`/`_sqr`
then produced garbage and X25519 emitted wrong-but-deterministic
output. Fixed by relocating to `$5100-$5442` (past the X25519_BSS
tail). New harnesses that touch CRYPTO_OVERLAY: prefer `$5100+`.
- `make p384-overlay` has a pre-existing unresolved-symbol bug:
`points384_raw.s` references `ec_base384_x` / `ec_base384_y`
which aren't exported by the current sibling build. Not a Phase C
Expand Down
45 changes: 43 additions & 2 deletions Makefile
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,6 +20,16 @@ VICE ?= x64sc
BACKEND ?= ip65
CFG := cfg/c64-https-$(BACKEND).cfg

# --- Sibling X25519 integration (Phase C.5, c64-x25519 v0.4.0) ---
# `USE_X25519_SIBLING=1` swaps in `build/lib/x25519.a` for the in-tree
# `src/crypto/fe25519.s` + `src/crypto/x25519.s` + in-tree X25519 data
# buffers from `src/data.s`. Default OFF — the in-tree implementation
# stays the shipped default until the supervisor + validator sign off
# on the sibling drop-in. The flag is read at link time; both code
# paths coexist on the branch so an A/B comparison is `make` vs
# `make USE_X25519_SIBLING=1`.
USE_X25519_SIBLING ?= 0

IP65_DIR := ip65
IP65_BUILD := ip65-build
IP65_BIN := $(IP65_BUILD)/ip65-c64.bin
Expand DownExpand Up@@ -53,13 +63,33 @@ UCI_SRCS := src/net/uci/net.s src/net/uci/uci_cmd.s
# for BOTH backends (replaces the in-tree ecdsa_{curve,fp,mod,points}.s).
SIBLING_LIB_ARCHIVES := build/lib/nistcurves-p256.a

# Phase C.5 (USE_X25519_SIBLING=1): c64-x25519 v0.4.0 sibling, always-resident,
# replaces in-tree fe25519.s + x25519.s + X25519 buffers in src/data.s.
# Off by default.
ifeq ($(USE_X25519_SIBLING),1)
SIBLING_LIB_ARCHIVES += build/lib/x25519.a
# Propagate the flag to ca65 so src/data.s suppresses the in-tree X25519
# buffer declarations (the sibling's data_x25519_raw.s provides them).
CA65FLAGS += -D USE_X25519_SIBLING=1
endif

# Phase C.5: under USE_X25519_SIBLING=1, evict the in-tree X25519
# implementation from the link line — the sibling archive
# (build/lib/x25519.a) provides byte-compatible exports for x25519_*
# and a richer fe25519_* surface than the in-tree fe_* symbols.
ifeq ($(USE_X25519_SIBLING),1)
CRYPTO_SRCS_EFFECTIVE := $(filter-out src/crypto/fe25519.s src/crypto/x25519.s,$(CRYPTO_SRCS_ALL))
else
CRYPTO_SRCS_EFFECTIVE := $(CRYPTO_SRCS_ALL)
endif

# Per-backend source + object selection.
ifeq ($(BACKEND),ip65)
NET_SRCS := $(IP65_SRCS)
CRYPTO_SRCS := $(CRYPTO_SRCS_ALL)
CRYPTO_SRCS := $(CRYPTO_SRCS_EFFECTIVE)
else ifeq ($(BACKEND),uci)
NET_SRCS := $(UCI_SRCS)
CRYPTO_SRCS := $(CRYPTO_SRCS_ALL)
CRYPTO_SRCS := $(CRYPTO_SRCS_EFFECTIVE)
# Phase C.3: add c64-nist-curves P-384 primitives as a REU overlay.
# Variable-base P-384 point ops (double/add/jacobian-to-affine) only —
# see tools/integration/build_nistcurves_p384.sh for the scope rationale.
Expand DownExpand Up@@ -135,6 +165,17 @@ build/lib/nistcurves-p256.a:
@mkdir -p build/lib
bash tools/integration/build_nistcurves_p256.sh

# Phase C.5: c64-x25519 v0.4.0 X25519 archive — replaces the in-tree
# fe25519.s + x25519.s + X25519 buffer declarations in src/data.s when
# USE_X25519_SIBLING=1. Linked into the PRG under BOTH backends. The
# sibling's reu_mul_init is called from src/boot.s in place of the
# in-tree REU mul table generator; sqtab_init is still served by the
# in-tree src/crypto/poly1305.s (sibling's mul_8x8.s is excluded from
# the archive to avoid duplicate-symbol with poly1305's mul_8x8).
build/lib/x25519.a:
@mkdir -p build/lib
bash tools/integration/build_x25519.sh

# Phase C.3b: P-384 overlay IMAGE + labels for harness-time use only.
# The production PRG does NOT link nistcurves-p384.a — this is smoke-test
# infrastructure. tools/test_p384_symbols.py loads overlay-p384.bin into
Expand Down
11 changes: 11 additions & 0 deletions cfg/c64-https-ip65.cfg
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,6 +88,17 @@ SEGMENTS {
OVERLAY_P256: load = CRYPTO_RESIDENT, type = ro, optional = yes;
OVERLAY_P384: load = CRYPTO_RESIDENT, type = ro, optional = yes;

# Phase C.5: sibling c64-x25519 rodata + bss segments. Under ip65
# there is no spare 4 KB region available — CRYPTO_OVERLAY is a
# zero-sized alias and NET_BSS_TAIL/NET_CODE both have <1 KB of
# slack. The segments are anchored at CRYPTO_RESIDENT and will
# overflow by ~3.3 KB under USE_X25519_SIBLING=1 until the cfg is
# restructured. Reported as a partial blocker for the integrator;
# USE_X25519_SIBLING=1 works under BACKEND=uci where CRYPTO_OVERLAY
# provides the headroom.
X25519_RODATA: load = CRYPTO_RESIDENT, type = ro, optional = yes, align = $100;
X25519_BSS: load = CRYPTO_RESIDENT, type = bss, optional = yes, align = $100;

# --- Resident crypto + TLS code / rodata. ---
# Phase C.2 backend-divergence: under UCI, TLS_CODE and CRYPTO_AUX_CODE
# (SHA-256 + HMAC-DRBG + ecdsa_verify dispatcher) relocate to NET_CODE
Expand Down
13 changes: 13 additions & 0 deletions cfg/c64-https-uci.cfg
Original file line numberDiff line numberDiff line change
Expand Up@@ -87,6 +87,19 @@ SEGMENTS {
OVERLAY_P256: load = CRYPTO_OVERLAY, type = ro, optional = yes;
OVERLAY_P384: load = CRYPTO_OVERLAY, type = ro, optional = yes;

# Phase C.5: sibling c64-x25519 rodata tables (mul38, sqr_lo/hi,
# a24_b0..b3 — ~2 KB) AND the sibling's page-aligned BSS buffers
# (fe25519_tmp1..4, x25_*, mul_dma_lo/hi/carry — ~1.5 KB) ride
# CRYPTO_OVERLAY under UCI to keep CRYPTO_RESIDENT inside its
# 24 KB budget. CRYPTO_OVERLAY is otherwise unused in the
# production UCI build (only the P-384 external smoke test DMAs
# into it at test time, and that's a harness operation rather
# than a production path). align = $100 so the .align 256
# directives in data_x25519_{rodata,bss}_raw.s land on real
# page boundaries.
X25519_RODATA: load = CRYPTO_OVERLAY, type = ro, optional = yes, align = $100;
X25519_BSS: load = CRYPTO_OVERLAY, type = bss, optional = yes, align = $100;

# --- Resident crypto + TLS code / rodata. ---
CRYPTO_CODE: load = CRYPTO_RESIDENT, type = ro;
CRYPTO_RODATA: load = CRYPTO_RESIDENT, type = ro;
Expand Down
1 change: 1 addition & 0 deletions libs/x25519
Submodule x25519 added at 47c0ad
24 changes: 24 additions & 0 deletions src/boot.s
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,8 +11,18 @@
.export print_resp_body

; ---- exports: REU multiply table routines ----
; Phase C.5: under USE_X25519_SIBLING=1 the sibling's
; libs/x25519/src/x25519_init.s owns reu_mul_init +
; reu_fetch_mul_row + reu_fetch_doubled_row + reu_clear_wide.
; The in-tree definitions below are guarded out to avoid
; duplicate-symbol errors at link time; the boot caller below
; imports `reu_mul_init` from the sibling archive instead.
.ifdef USE_X25519_SIBLING
.import reu_mul_init
.else
.export reu_mul_init
.export reu_fetch_mul_row
.endif

; ---- exports: menu handlers ----
.export do_net_init
Expand DownExpand Up@@ -619,6 +629,14 @@ ascii_chrout:
; REU multiply table initialization (from c64-x25519 optimizations)
; =============================================================================

; Phase C.5: in-tree reu_mul_init / reu_fetch_mul_row are guarded out
; under USE_X25519_SIBLING=1. The sibling's libs/x25519/src/x25519_init.s
; supplies a richer initializer that also populates REU banks 2-5 with
; the zero block + doubled tables required by the sibling's
; fe25519_sqr. Calling the in-tree version would leave those banks
; unset and corrupt every fe25519_sqr.
.ifndef USE_X25519_SIBLING

; =============================================================================
; reu_mul_init - Generate 256 full multiplication rows and stash in REU
;
Expand DownExpand Up@@ -735,6 +753,8 @@ reu_fetch_mul_row:
sta reu_command
rts

.endif ; .ifndef USE_X25519_SIBLING (in-tree reu_mul_init / reu_fetch_mul_row)

; =============================================================================
; Strings (read-only)
; =============================================================================
Expand DownExpand Up@@ -876,5 +896,9 @@ http_path_root:
.segment "BSS"

net_initialized: .res 1
; Phase C.5: reu_init_a/b are state for the in-tree reu_mul_init loop.
; Sibling's reu_mul_init keeps its own state.
.ifndef USE_X25519_SIBLING
reu_init_a: .res 1
reu_init_b: .res 1
.endif
17 changes: 17 additions & 0 deletions src/crypto/shared/reu_layout.inc
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,6 +48,23 @@ REU_P256_PRECOMPUTE_BASE = $30000
REU_P384_PRECOMPUTE_BASE = $40000
.endif

; --- Phase C.5 collision note (USE_X25519_SIBLING=1) ---
; The sibling c64-x25519 v0.4.0 reu_mul_init populates banks 3, 4, and 5
; with its own doubled-product and 17th-bit-carry tables for fe25519_sqr.
; Banks 3 / 4 / 5 are also nominally reserved here for P-256 precompute
; (bank 3) and P-384 precompute (banks 4-5). The collision is THEORETICAL
; under the current TLS path:
; - P-256 always uses ec_scalar_mul_var (variable-base) which does not
; touch REU_P256_PRECOMPUTE_BASE.
; - P-384 is stubbed at the TLS layer (see project_p384_stubbed memory
; note); the smoke-test overlay loader uses banks 4-5 only at test
; time, not in the production handshake.
; If either P-256 fixed-base scalar mul or P-384 precompute is ever
; wired into the production TLS path while USE_X25519_SIBLING=1, the
; sibling's banks 3-5 tables get clobbered and X25519 silently corrupts.
; Resolve at that time by relocating one or the other (the sibling's
; cfg/x25519.cfg pins these via SYMBOLS — downstream override available).

; --- overlay slot size (bytes) ---
; Each overlay image occupies exactly this many bytes in the REU store and
; is DMA'd into the live CRYPTO_OVERLAY region at runtime.
Expand Down
34 changes: 34 additions & 0 deletions src/data.s
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,6 +32,13 @@ sqtab2_hi:
.byte >(((256-(I+1))*(256-(I+1)))/4 - 1)
.endrepeat

; Phase C.5: under USE_X25519_SIBLING=1 the c64-x25519 archive owns
; mul38_lo_tab / mul38_hi_tab / fe_p / x25_basepoint (plus sqr_lo,
; sqr_hi, a24_b0..b3 which the in-tree fe25519 does not have at all).
; Suppress the in-tree definitions in that mode to avoid duplicate-
; symbol errors at link time.
.ifndef USE_X25519_SIBLING

; --- mul_by_38 lookup tables ---
.export mul38_lo_tab
.export mul38_hi_tab
Expand DownExpand Up@@ -60,6 +67,8 @@ x25_basepoint:
.byte 9
.res 31, 0

.endif ; .ifndef USE_X25519_SIBLING

; =============================================================================
; Initialized mutable data (needs DATA segment — small defaults)
; =============================================================================
Expand DownExpand Up@@ -102,13 +111,25 @@ zp_save_buf: .res 26 ; saves $02-$1B during ip65 calls

.segment "TABLES_BSS"

; Phase C.5: under USE_X25519_SIBLING=1 the sibling's data_x25519_raw.s
; declares mul_dma_lo / mul_dma_hi (and the additional mul_dma_carry
; needed by the sibling's reu_fetch_doubled_row). nistcurves-p256
; imports mul_dma_lo/hi via the in-tree shared mul row-fetch pipeline,
; so we MUST still provide the symbol from somewhere — under the
; sibling path that "somewhere" is the sibling data module rather
; than this file.
.ifndef USE_X25519_SIBLING
.align 256
.export mul_dma_lo
.export mul_dma_hi
mul_dma_lo: .res 256 ; DMA target: lo bytes of a*b for current a
mul_dma_hi: .res 256 ; DMA target: hi bytes of a*b for current a
.endif ; .ifndef USE_X25519_SIBLING

; --- Quarter-square tables (runtime-generated by sqtab_init in poly1305.asm) ---
; These remain in-tree under both modes: sqtab_init is still served by
; src/crypto/poly1305.s (the sibling's mul_8x8.s is intentionally
; excluded from build/lib/x25519.a — see tools/integration/build_x25519.sh).
.align 256
.export sqtab_lo
.export sqtab_hi
Expand DownExpand Up@@ -450,6 +471,18 @@ aead_scratch: .res 16 ; Poly1305 padding/length block
cc20_remain_hi: .res 1 ; high byte of 16-bit ChaCha20/Poly1305 length counter
; (low byte lives in ZP at cc20_remain = $18)

; Phase C.5: the sibling owns the fe25519/X25519 buffers under
; USE_X25519_SIBLING=1. In the sibling layout:
; - fe_wide is a ZP equate ($40-$7F), NOT a BSS label (hard-asserted
; by constants.s) — the in-tree fe_wide BSS declaration would
; collide as a duplicate symbol and break the SMC patch sites in
; fe25519_mul/sqr that depend on the high byte being $00.
; - fe25519_tmp1..4 replace fe_tmp1..4 (renamed, page-aligned).
; - x25_* / mul_cached_a / mul_src2_buf are re-exported by the
; sibling's data.s with correct 32-byte alignment.
; nistcurves-p256 imports mul_cached_a + mul_src2_buf; both are
; satisfied by the sibling's data_x25519_raw.s in the sibling path.
.ifndef USE_X25519_SIBLING
; -----------------------------------------------------------------------------
; fe25519 field arithmetic temporaries
; -----------------------------------------------------------------------------
Expand DownExpand Up@@ -501,6 +534,7 @@ mul_src2_buf: .res 35 ; absolute copy of src2 for fast indexed access
; c64-nist-curves fp256 4x-unrolled mul can
; over-read past j=31 into zeros for its
; fast-skip fast path — Phase C.4)
.endif ; .ifndef USE_X25519_SIBLING

; -----------------------------------------------------------------------------
; ECDSA signature verification
Expand Down
Loading