What
tools/integration/build_nistcurves_p256.sh builds our P-256 archives by editing upstream's member set:
:274 rm -f "$STAGING/mul_8x8.o" "$STAGING/data_shared.o" "$STAGING/reu_mul_init.o"
:278 # onchip-comb: rm -f fp384_onchip.o mod384.o curve384.o points384_core.o
# points384_comb.o data_p384.o data_p384_limlee.o ecdsa384*.o
# sha384*.o data_sha.o inv256.o data_p256_invref.o
:337 rm -f "$STAGING/mul_8x8_onchip.o"
c64-lib-contract SPEC §6.1 now names this as forbidden:
Consumers fetch build/lib/<shortname>[-<variant>].a and link directly. No mid-build sed, no copying intermediates around, and no ar65 member surgery — an archive whose member set a consumer has edited is outside every §5/§8.0 manifest claim it ships. §6.2 and §6.3 exist so surgery is never the only route to a configuration.
This is the root cause of two gaps we already document as deferred:
src/contract_footprint_asserts.s excludes the comb profile from the §6.6 assert, because the surviving manifest describes the pre-surgery archive (27,000 B declared against a 16,384 B region).src/lib_contract_asserts.s records the §8.0 disjointness/coverage asserts as "not writable today" for the same reason — the shipped mask describes upstream's archive, not the one we link.
What is now solvable, measured against the v0.10.2 pin
Upstream gained CONTRACT_DEFINES and APP_OWNED_DEFINES in the wave. The variant × APP_OWNED combination is reachable today — verified by building it:
make lib-p256-verify CONTRACT_DEFINES='-D SHARED_SQTAB_INIT -D SHARED_REU_MUL_INIT \
-D SHARED_REU_MUL_FETCH -D SHARED_CT_MUL_8X8'
exit 0, and od65 --dump-exports on the resulting members shows the switches do most of our surgery for us:
| member | exports under the switches | our current surgery |
|---|
reu_mul_init.o | nothing at all | rm -f — unnecessary, the object is inert |
mul_8x8.o | poly_prod_lo/hi, sqtab_lo/hi only (no sqtab_init, no ct_mul_8x8, no mul_8x8) | rm -f — nearly unnecessary |
data_shared.o | mul_dma_lo/hi, mul_cached_a, mul_src2_buf (+ prefixed) | rm -f — still collides |
So items 1 and 2 are close to free. Item 3 is not.
What is hard, and why I am not doing it under release pressure
Dropping the data_shared.o surgery means c64-https stops definingmul_dma_lo/hi, mul_cached_a, mul_src2_buf in src/data.s and imports the library's instead — which is what upstream's own lib-app-owned assumes, since it keeps data_shared.o in the member list. Those buffers are gated only by LIB_NO_BARE_EXPORTS (a naming switch), so there is no way to have the library ship the archive and not the buffers.
That is a re-pointing of the REU multiply row table, and it is the single worst place in this codebase to be wrong: CLAUDE.md's "VICE harness gotcha" records that when mul_dma_lo/hi holds the wrong contents, every fp_mul returns a*255*b mod p, the handshake fails minutes later at the first encrypted record, and there is no link error and no diagnostic. The same note records the near-miss where reu_mul_init would have bound to the wrong provider's table with no diagnostic either.
It is worth doing. It is not worth doing in the same change as a release.
What is blocked upstream regardless
There is no P-256 comb archive target. Upstream's full list is lib, lib-p256-verify, lib-p384-verify, lib-p384-sha384, lib-p384-curve, lib-app-owned, lib-onchip, lib-p256-verify-onchip, lib-p384-verify-onchip, lib-p384-curve-onchip. The comb objects exist only inside the full lib-onchip archive, which is why we narrow it by hand.
So even a perfect fix to items 1-3 leaves the comb profile non-conformant, and §6.3 ("every documented variant/profile axis MUST be reachable through §6.1 targets plus §6.2 defines, with no library edits") is not satisfiable for it from our side. Filed upstream as c64-nist-curves#TBD — link to follow.
Sequencing
- Upstream: a
lib-p256-verify-onchip-comb (or equivalent) target. Blocks the comb arm entirely. - Here: switch the REU and onchip arms to
CONTRACT_DEFINES + APP_OWNED switches, flipping poly_prod_lo/hi and the data_shared buffer ownership to imports. Wants its own change, its own KAT run on both profiles, and an e2e handshake — a link that succeeds proves nothing about this failure mode. - Then: delete the
.ifdef USE_NISTCURVES_COMB exclusion in src/contract_footprint_asserts.s and write the §8.0 disjointness/coverage asserts that the surgery currently makes meaningless.
Related: #70 (the other open consumer-side alignment item, §13 net ABI).
What
tools/integration/build_nistcurves_p256.shbuilds our P-256 archives by editing upstream's member set:c64-lib-contract SPEC §6.1 now names this as forbidden:
This is the root cause of two gaps we already document as deferred:
src/contract_footprint_asserts.sexcludes the comb profile from the §6.6 assert, because the surviving manifest describes the pre-surgery archive (27,000 B declared against a 16,384 B region).src/lib_contract_asserts.srecords the §8.0 disjointness/coverage asserts as "not writable today" for the same reason — the shipped mask describes upstream's archive, not the one we link.What is now solvable, measured against the v0.10.2 pin
Upstream gained
CONTRACT_DEFINESandAPP_OWNED_DEFINESin the wave. The variant × APP_OWNED combination is reachable today — verified by building it:exit 0, and
od65 --dump-exportson the resulting members shows the switches do most of our surgery for us:reu_mul_init.orm -f— unnecessary, the object is inertmul_8x8.opoly_prod_lo/hi,sqtab_lo/hionly (nosqtab_init, noct_mul_8x8, nomul_8x8)rm -f— nearly unnecessarydata_shared.omul_dma_lo/hi,mul_cached_a,mul_src2_buf(+ prefixed)rm -f— still collidesSo items 1 and 2 are close to free. Item 3 is not.
What is hard, and why I am not doing it under release pressure
Dropping the
data_shared.osurgery means c64-https stops definingmul_dma_lo/hi,mul_cached_a,mul_src2_bufinsrc/data.sand imports the library's instead — which is what upstream's ownlib-app-ownedassumes, since it keepsdata_shared.oin the member list. Those buffers are gated only byLIB_NO_BARE_EXPORTS(a naming switch), so there is no way to have the library ship the archive and not the buffers.That is a re-pointing of the REU multiply row table, and it is the single worst place in this codebase to be wrong: CLAUDE.md's "VICE harness gotcha" records that when
mul_dma_lo/hiholds the wrong contents, everyfp_mulreturnsa*255*b mod p, the handshake fails minutes later at the first encrypted record, and there is no link error and no diagnostic. The same note records the near-miss wherereu_mul_initwould have bound to the wrong provider's table with no diagnostic either.It is worth doing. It is not worth doing in the same change as a release.
What is blocked upstream regardless
There is no P-256 comb archive target. Upstream's full list is
lib,lib-p256-verify,lib-p384-verify,lib-p384-sha384,lib-p384-curve,lib-app-owned,lib-onchip,lib-p256-verify-onchip,lib-p384-verify-onchip,lib-p384-curve-onchip. The comb objects exist only inside the fulllib-onchiparchive, which is why we narrow it by hand.So even a perfect fix to items 1-3 leaves the comb profile non-conformant, and §6.3 ("every documented variant/profile axis MUST be reachable through §6.1 targets plus §6.2 defines, with no library edits") is not satisfiable for it from our side. Filed upstream as c64-nist-curves#TBD — link to follow.
Sequencing
lib-p256-verify-onchip-comb(or equivalent) target. Blocks the comb arm entirely.CONTRACT_DEFINES+ APP_OWNED switches, flippingpoly_prod_lo/hiand thedata_sharedbuffer ownership to imports. Wants its own change, its own KAT run on both profiles, and an e2e handshake — a link that succeeds proves nothing about this failure mode..ifdef USE_NISTCURVES_COMBexclusion insrc/contract_footprint_asserts.sand write the §8.0 disjointness/coverage asserts that the surgery currently makes meaningless.Related: #70 (the other open consumer-side alignment item, §13 net ABI).