feat: prove PCEA 0.1.0 release readiness - #36
erinepshovel-code wants to merge 21 commits into
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 10da8a5434
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| - uses: actions/checkout@v7 | ||
| - uses: actions/setup-python@v7 |
There was a problem hiding this comment.
Pin the workflow to published Action majors
On every pull request and push inspected in .github/workflows/contract-boundary.yml, the job stops while resolving these actions because neither repository publishes a v7 ref, so the newly expanded pytest suite never runs. The upstream usage examples currently specify actions/checkout@v6 and actions/setup-python@v6; pin these steps to existing releases.
Useful? React with 👍 / 👎.
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v7 | ||
| - uses: actions/setup-python@v7 | ||
| with: | ||
| python-version: "3.11" | ||
| - name: Build wheel from declared package metadata | ||
| run: | | ||
| python -m pip install --upgrade pip | ||
| python -m pip wheel . --no-deps --wheel-dir dist | ||
| - name: Install wheel outside the source tree and smoke-test public package | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| wheel=$(printf '%s\n' dist/pcea-*.whl) | ||
| test -f "$wheel" | ||
| python -m venv /tmp/pcea-release-smoke | ||
| /tmp/pcea-release-smoke/bin/python -m pip install --no-deps "$wheel" | ||
| cd /tmp | ||
| /tmp/pcea-release-smoke/bin/python - <<'PY' | ||
| from importlib.metadata import version | ||
| import pcea | ||
|
|
||
| assert version("pcea") == "0.1.0" | ||
| assert callable(pcea.encrypt_state) | ||
| assert callable(pcea.decrypt_state) | ||
| assert pcea.PCEAInstance is not None | ||
| PY |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f69ca59a27
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| initial_transcript = hashlib.sha256( | ||
| f"{VERSION}|transcript|{session_id}".encode("utf-8") | ||
| ).digest() |
There was a problem hiding this comment.
Bind the word size into the authenticated session context
When paired peers are accidentally configured with different word_bits values (for example, a 64-bit sender and 32-bit receiver), they still derive identical authentication keys and transcripts because this context includes only the version and session ID. The receiver therefore accepts the tag, decrypts the ciphertext using the wrong width, returns corrupted plaintext, and increments receive_sequence; include the word size in the initial key/transcript derivation and authenticated packet metadata so this mismatch is rejected before state advancement.
Useful? React with 👍 / 👎.
| next_chain, next_transcript = _advance( | ||
| self._send_chain, self._send_transcript, material, tag | ||
| ) | ||
| self._send_last = copy.deepcopy(state) |
There was a problem hiding this comment.
Preserve the last state when encrypting an empty packet
When a caller sends an empty state, encrypt_state treats it as a supported passthrough, but this unconditional assignment replaces the session's non-empty previous state with [] (and decrypt does the same to the receiver at line 366). The empty packet itself succeeds, but the next non-empty packet raises a seed-count mismatch and leaves the session unusable; mirror PCEAInstance by advancing the ratchets and sequence while retaining the previous PCEA state when the plaintext is empty.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6b419055a1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| initial_transcript = hashlib.sha256( | ||
| f"{VERSION}|transcript|{session_id}".encode("utf-8") | ||
| ).digest() |
There was a problem hiding this comment.
Bind the initial PCEA state into authentication
When peers have different initial-state values but share the provisioned secret, session ID, roles, and word size—for example after restoring one peer from stale state—the chains and transcript here remain identical. The receiver therefore accepts the HMAC, decrypts with the wrong PCEA state to arbitrary plaintext, and advances normally; include a canonical digest of initial_state in the authenticated session/key context so this mismatch is rejected before recovery.
Useful? React with 👍 / 👎.
| Fixture secrets MUST be generated before attack execution from: | ||
|
|
||
| ```text | ||
| SHA256("pcea-arity-v1|secret-fixture|<b>|<trial>") |
There was a problem hiding this comment.
Define a hidden b-bit input for secret fixtures
When Phase 3 is reached, this formula contains only the public b and trial number and produces a full SHA-256 digest; it neither selects a hidden value from a 2^b space nor defines how an enumerated candidate maps to the API secret. Because the attacker is explicitly given all non-secret fixture-generation rules, using the digest directly reveals each fixture secret immediately, while any alternative candidate mapping would be an unfrozen implementation choice, invalidating the claimed exhaustive-recovery measurements. Freeze a concrete b-bit seed per trial and a domain-separated expansion function that takes that seed as input.
Useful? React with 👍 / 👎.
| - use the same contributor order and count; | ||
| - use the same public address information; | ||
| - use the same provisioned/master secret entropy when a secret is in scope; | ||
| - use SHA-256/HMAC-SHA256, matching the experiment lane's primitive; |
There was a problem hiding this comment.
Freeze the exact matched PRF construction
When Phase 1 compares PCEA against PRF-A_n, permitting either SHA-256 or HMAC-SHA256 without freezing the key, contributor serialization, domain separation, digest-to-output mapping, and transform makes the load-bearing control implementation-dependent. Those choices can change the control's algebraic degree, collisions, and output balance, so two compliant actors can reach different H-PCEA outcomes from the same fixtures; specify one exact byte-level control construction before results are observed.
Useful? React with 👍 / 👎.
| - no material difference beyond the frozen equality/tolerance rules -> `FALSIFIED_PCEA_SPECIFIC_ARITY` | ||
| - reproducible difference -> `SURVIVED_PCEA_SPECIFIC_ARITY` |
There was a problem hiding this comment.
Specify the H-PCEA decision threshold
When classifying H-PCEA, this rule refers to frozen equality/tolerance rules that the procedure never actually defines, while the alternative merely requires a “reproducible difference.” An actor can therefore choose after seeing the measurements what size or consistency of difference is material and select either outcome label; freeze exact per-metric comparison, aggregation, and pass/fail thresholds before execution.
Useful? React with 👍 / 👎.
| ciphertexts | ||
| chosen plaintexts supplied by the attack harness |
There was a problem hiding this comment.
Freeze the Phase 3 chosen-plaintext transcript
When Phase 3 measures secret recovery or next-output prediction, the procedure says the attacker receives chosen plaintexts but never fixes their values, count, ordering, AAD, or the target message. Those choices directly control how quickly candidate secrets are eliminated and the available prediction information, so compliant runs can report incomparable attack work; preregister the exact transcript and query budget for every trial.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3237ef4035
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| Run at minimum: | ||
|
|
||
| 1. **Exhaustive secret recovery** — enumerate candidate master-secret seeds until the transcript uniquely matches. | ||
| 2. **Lower-arity partition-assisted recovery** — give the attacker the complete frozen family of lower-arity subset observations allowed by the game and measure whether candidate pruning beats ordinary exhaustive recovery. |
There was a problem hiding this comment.
Freeze the partition-assist observations
When Phase 3 is reached, this attack receives a “complete frozen family” of lower-arity observations, but the game never defines which subsets are exposed, the contributor assignments and outputs they contain, or whether obtaining them consumes the query budget. Different actors can therefore give the attacker materially different pruning information and report incomparable candidate counts; specify the exact observation dataset/oracle and how each observation is charged before execution.
Useful? React with 👍 / 👎.
| 1. **Exhaustive secret recovery** — enumerate candidate master-secret seeds until the transcript uniquely matches. | ||
| 2. **Lower-arity partition-assisted recovery** — give the attacker the complete frozen family of lower-arity subset observations allowed by the game and measure whether candidate pruning beats ordinary exhaustive recovery. | ||
| 3. **Next-output prediction** — after the frozen chosen-plaintext transcript, predict the next target output without the secret; record exact success rate over trials. | ||
| 4. **Known-state versus hidden-state split** — run the same game once with previous plaintext state known and once with it withheld, keeping those results separate. |
There was a problem hiding this comment.
Freeze the hidden-state fixtures
When Phase 3 runs the hidden-state lane, the procedure says only to withhold the previous state and never freezes that state's values, generation rule, or entropy. Because a candidate master secret cannot reproduce a transcript without also accounting for the PCEA prior state, choosing a constant toy state versus an independently random state can change whether candidates are testable or uniquely recoverable at all; preregister the prior-state fixtures and the exact search knowledge available in both lanes.
Useful? React with 👍 / 👎.
| B = [8, 12] | ||
| ``` | ||
|
|
||
| A `b`-bit seed is deterministically expanded with SHA-256 into the API-compatible secret bytes so all constructions use the same actual secret length while the exhaustive search space remains exactly `2^b`. |
There was a problem hiding this comment.
Define how the secret drives each PCEA arity variant
When Phase 3 is reached, the baseline A_n variants defined earlier only change the prior-state contributor set, while the shipped PCEA transform has no master-secret input and the only secret-consuming session wrapper still invokes the unchanged A3 transform. This leaves actors free to inject the expanded secret into A2/A5/A7 in different places, producing different transcripts and attack costs; freeze the exact secret-to-state/key schedule and research adapter for every PCEA arity variant.
Useful? React with 👍 / 👎.
| - name: Run full repository test suite | ||
| run: | | ||
| PYTHONPATH=. python -m pytest -q tests/test_contract_spec.py | ||
| PYTHONPATH=. python -m pytest -q |
There was a problem hiding this comment.
Update the contributor instructions for the expanded CI gate
After this workflow starts running the entire repository suite, the checked CLAUDE.md development instructions at lines 113-137 and 221 still state that contract-boundary.yml runs only tests/test_contract_spec.py and present that narrow command as mirroring CI. Contributors following the repository's own workflow guidance can therefore miss failures in the newly gated tests and release-artifact checks until remote CI; update those instructions alongside this workflow change.
Useful? React with 👍 / 👎.
Superseded
This 0.1.0 release-readiness branch is no longer a valid integration surface.
Current
mainis already PCEA 0.2.0, and merged #37 moved mutable/speculative PCEA research out of the stable source repository intostack/research/pcea/. Merging this branch would reintroduce research that the current architecture intentionally removed.The still-useful release hygiene has been preserved on current main in #38:
The ratcheted-session and arity research remain outside stable PCEA rather than being silently restored.
hmmm
Independent cryptographic/security review remains separate from repository correctness and release packaging.