Skip to content

pkc%fix(bls): read legacy BLS keys and signatures as the reference implementation does - #101

Merged
PastaPastaPasta merged 5 commits into
developfrom
pasta/bls-legacy-reader
Nov 3, 2026
Merged

PastaPastaPasta merged 5 commits into
developfrom
pasta/bls-legacy-reader

Conversation

@PastaPastaPasta

@PastaPastaPasta PastaPastaPasta commented Nov 1, 2026 •

Copy link
Copy Markdown
Member

Motivation

dash-pkc reads BLS bytes at two levels. to::<S>() decodes an element, and is_canonical::<S>() says whether the scheme reads the bytes as an element that encodes back to them. Under the legacy scheme, the reference implementation accepts three kinds of input that dash-pkc refuses:

  • a public key with x = 0 with the sign bit set, (0, -2). blst's decompression writes this point out but reports an error;
  • a public key encoding an x below the field prime with no point on the curve, with bits 5-6 clear, under either sign;
  • a signature encoding an x with no point on the curve, with both halves below the field prime and the sign bit and the top three bits of x.c1 clear.

This pull request makes to::<BlsScChia>() decode the first kind and is_canonical::<BlsScChia>() accept all three. to still refuses the off-curve encodings, since there is no point to return for them, and its rustdoc says so. The basic scheme is unchanged, and so is the stray-bit masking on the legacy G1 path.

Additional Information

This pull request is based on pasta/bls-corpus-gen (#112), which adds the reference implementation as a submodule and the programs that write the other BLS corpora.

Description Notes
Add a program that writes the BLS read corpus gen_read.cpp builds seeded inputs with relic's field and curve operations and writes pkgs/pkc/corpus/bls_read.json5: 79 public-key and 130 signature inputs, each with the library's outcome under the legacy and basic schemes, null, valid or invalid. For each input the legacy scheme reads as valid, relic also gives whether its x has a point on the curve. regen.py writes it with the other corpora.
Read legacy G1 x = 0 with the sign bit set to and is_canonical accept it, and it round-trips. With the sign bit clear, stray bits 5-6 mask x = 0 down to the null encoding, so those bytes still do not decode. Adds BlsScheme methods g1_bytes, a point in the scheme's 48-byte layout, and pk_ietf_bytes, the IETF compressed form of an encoding in that layout; BlsScChia moves its legacy encoder and its legacy-to-IETF conversion into them.
Accept legacy encodings off the curve in is_canonical New BlsScheme methods is_canonical_pk and is_canonical_sig hold each scheme's rule. BlsScIetf keeps the previous rule, decode plus byte-exact re-encode. BlsScChia also accepts the off-curve encodings. g2_bytes is the G2 counterpart of g1_bytes, and sig_ietf_x_bytes gives the x of a G2 encoding in the IETF compressed layout, with the sign bit clear.
Check BLS reads against the reference outcomes Every corpus input is checked under both schemes, against is_null, is_canonical and the scheme's own predicate. to must fail for each legacy input the corpus marks off the curve, and decode and re-encode to itself for every other valid input.
Update the changelog Under "Changed" for the two read changes, and "Added" for the BlsScheme methods.

Each outcome in bls_read.json5 comes from the release's read of the input:

  • the all-zero input is null;
  • otherwise the input is valid when the library's element reader, under the scheme flag, returns an element other than the identity that encodes back to the input;
  • anything else is invalid.

The program is C++ because the library's element readers are its C++ API over relic. pkgs/pkc/contrib/README.md has the regeneration recipe.

Breaking Changes

None. is_canonical::<BlsScChia>() accepts more inputs and to::<BlsScChia>() decodes one more. BlsScheme gains six required methods, but the trait is sealed, so no implementation outside dash-pkc exists.

CI (just test lint fuzz::test, CodeQL included) is green on each commit.

Checklist

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional tests
  • I have made corresponding changes to the documentation
  • I did not use agentic assistance (i.e. LLMs, AI) except as disclosed and can answer questions,
    comments and concerns without said assistance. I assume authorship of the code submitted.
  • I have assigned this pull request to a milestone (for repository code-owners and collaborators only)

Agent disclosure

Code, commit messages and this description were generated with Claude Code using Claude Opus 5.5, running autonomously as an agent on behalf of the author. The agent also built the pinned release's BLS library and wrote the corpora with it. The change was reviewed by a Claude Code agent using Opus 5.5 and a Codex agent using GPT-6.1.

🤖 Generated with Claude Code

@PastaPastaPasta PastaPastaPasta added this to the 0.3 milestone Nov 1, 2026
@PastaPastaPasta
PastaPastaPasta requested a review from kwvg November 1, 2026 00:30
@coderabbitai

coderabbitai Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 8 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between 93f399c and 1ebd2c2.

Files ignored due to path filters: 1
  • pkgs/pkc/corpus/bls_read.json5 is excluded by !**/*.json5
Files selected for processing: 10
  • pkgs/pkc/CHANGELOG.md
  • pkgs/pkc/contrib/README.md
  • pkgs/pkc/contrib/gen_read.cpp
  • pkgs/pkc/src/bls/blst_ffi.rs
  • pkgs/pkc/src/bls/public_ops.rs
  • pkgs/pkc/src/bls/scheme_chia.rs
  • pkgs/pkc/src/bls/scheme_ietf.rs
  • pkgs/pkc/src/bls/scheme_ops.rs
  • pkgs/pkc/src/bls/sig_basic.rs
  • pkgs/pkc/src/bls/tests.rs

No actionable comments were generated in the recent review.

Summary

Summary by CodeRabbit

  • Bug Fixes
    • Improved canonicality checks for BLS public keys and signatures across Chia and IETF schemes. Legacy Chia encodings of off-curve values may be considered canonical, but still cannot be converted into keys or signatures.
    • Corrected handling of certain Chia public-key encodings, including the signed zero-coordinate case.
  • Documentation
    • Clarified scheme-specific encoding layouts and canonicality rules, and documented the BLS read-test corpus.

The BLS scheme interface now defines methods for point-byte layouts, IETF-layout conversions, and public-key and signature canonicality. The IETF and Chia implementations provide these methods. Chia decoding and canonicality checks include the specified legacy encodings. The public-key and signature byte wrappers use scheme-specific canonicality checks while continuing to accept null bytes. Tests compare scheme and wrapper results with read vectors. A deterministic generator creates BLS read vectors for both schemes.

Suggested reviewers: thepastaclaw

Merge Risk: Minimal · previous review 93f39

The checked legacy public-key encoding reads and round-trips as intended. No identified issue prevents merging after normal checks.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title names the main change: legacy BLS key and signature reading now matches the reference implementation. The changeset adds legacy G1 x=0 sign-bit decoding and off-curve canonicality acceptance…
Description check Passed The description explains the motivation, the three classes of legacy inputs the change accepts, the changes to decoding and canonicality, the new read corpus and generator, and the breaking-change sta…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title names the main change: legacy BLS key and signature reading now matches the reference implementation. The changeset adds legacy G1 x=0 sign-bit decoding and off-curve canonicality acceptance for Chia encodings, which fits the title. The title is specific, short, and uses the commit-style prefix. It does not name the new corpus or the trait additions, which is acceptable because it need not cover every detail.

Full details: Description check

Explanation

The description explains the motivation, the three classes of legacy inputs the change accepts, the changes to decoding and canonicality, the new read corpus and generator, and the breaking-change status. It is closely tied to the changeset (BlsScChia, is_canonical, BlsScheme methods, gen_read.cpp, bls_read corpus). It is not off-topic.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.
Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 91b3644 and 93f399c.

Files ignored due to path filters: 1
  • pkgs/pkc/corpus/bls_read.json5 is excluded by !**/*.json5
Files selected for processing: 7
  • pkgs/pkc/CHANGELOG.md
  • pkgs/pkc/contrib/README.md
  • pkgs/pkc/contrib/gen_read.cpp
  • pkgs/pkc/src/bls/scheme_chia.rs
  • pkgs/pkc/src/bls/scheme_ietf.rs
  • pkgs/pkc/src/bls/scheme_ops.rs
  • pkgs/pkc/src/bls/tests.rs
Files skipped from review as they are similar to previous changes: 2
  • pkgs/pkc/CHANGELOG.md
  • pkgs/pkc/contrib/README.md

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

@thepastaclaw

thepastaclaw commented Nov 1, 2026 •

Copy link
Copy Markdown

✅ Final review complete — no blockers (commit 1ebd2c2) · triage: critical

@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Note

This pull request has no conflicts! 🎊 🎉 🎊

@codecov

codecov Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.45763% with 3 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
pkgs/pkc/src/bls/sig_basic.rs 91.30% 2 Missing ⚠️
pkgs/pkc/src/bls/public_ops.rs 95.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final validation — Phase 1 + Phase 2

The legacy read rules match the inspected Core/relic and blst behavior; no blocking correctness defects were confirmed. Two non-blocking improvements remain: explicitly testing typed-decoder rejection of off-curve inputs and avoiding repeated decompression in canonicality checks. Validation was static only; current CI shows stable platform checks passing, with lint, Linux cross-build, and several MSRV build/test jobs still pending.

🟡 2 suggestion(s)

Review provenance

Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 4: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 5: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — The intricate changes in scheme_chia.rs canonicality predicates and blst_ffi.rs::uncompress_on_curve alter cryptographic key and signature acceptance rules, including off-curve encodings and special handling of a decompression error.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity quota lookup failed), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `pkgs/pkc/src/bls/sig_basic.rs`:
- [SUGGESTION] pkgs/pkc/src/bls/sig_basic.rs:277-282: Assert the off-curve typed-decoder boundary independently
  These assertions allow either successful decoding with identical reserialization or failure classified by chia_sig_off_curve. They therefore do not require off-curve inputs to fail typed decoding, despite that being an explicit boundary of this change. The failure classifier also repeats the production fallback's byte transformation and blst error check, so a shared classification mistake can excuse an unexpected decoding failure. Record an independent on-curve/decodability expectation for the relevant corpus inputs, then require errors for legacy off-curve encodings and successful round-trips for the other reference-valid inputs. Apply the same assertion to assert_pk_reads in public_ops.rs. Do not use only class.starts_with("off_"): the corpus also contains off-curve boundary inputs such as G1 x_max_000 and x_max_100.

In `pkgs/pkc/src/bls/scheme_chia.rs`:
- [SUGGESTION] pkgs/pkc/src/bls/scheme_chia.rs:139-150: Reuse the decompression result in canonicality checks
  For an eligible off-curve encoding, pk_from_bytes performs blst decompression and maps BLST_POINT_NOT_ON_CURVE to InvalidPublicKey; this fallback then decompresses the same bytes again to recover the discarded error. sig_is_canonical repeats the same pattern for G2. The inspected blst implementation performs the field square-root computation before returning this error, so these inputs incur that work twice, including off-curve inputs intentionally accepted by the new canonicality predicates. Preserve the internal decompression result or error classification so canonicality can reuse it, while typed decoding continues to reject off-curve values.
Out-of-scope follow-up suggestions (1)

These are valid observations, but they are outside this PR's scope and should be handled in separate issues or author/maintainer-requested PRs rather than blocking this review.

  • Separate legacy wire compatibility from DH/IES peer validation — The base revision already accepts non-prime-order legacy public keys, and its off_subgroup_peer_policy test explicitly preserves their use in DH. BlsScheme::dh_exchange multiplies the peer point by the secret scalar without subgroup validation, and ies_open derives its encryption key from that result. Small-order peer points can confine the shared secret to an enumerable set. This is a concrete pre-existing operation-level security concern; the legacy reader's compatibility policy and the absence of DH subgroup validation predate this PR.
    • Follow-up: Track hardened DH/IES entry points that reject identity and non-prime-order peers separately from the consensus-compatible legacy reader, and document the existing peer-validation requirements.

Comment thread pkgs/pkc/src/bls/sig_basic.rs Resolved
Comment thread pkgs/pkc/src/bls/scheme_chia.rs Resolved
@github-actions github-actions Bot added the status:behind This pull request can be rebased on the base branch. label Nov 1, 2026

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — Final validation — Phase 1 + Phase 2

Reviewed the complete diff at 975df95 and found no actionable in-scope defects: the legacy acceptance rules match the inspected reference sources, while typed decoding still rejects off-curve encodings. The independent curve annotations and strict decoder assertions fix the prior coverage finding; keeping decoding and canonicality separate is a reasonable trade-off despite bounded repeated decompression. This was static-only validation: no builds or tests were run, and current-head MSRV checks, lint, and macOS/Windows stable builds remain pending; the Ubuntu ARM stable build passed.

🔴 0 blocking | 🟡 0 suggestion(s) | 💬 0 nitpick(s)

Review provenance

Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 3: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 5: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 8: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — The intricate changes in pkgs/pkc/src/bls/scheme_chia.rs and blst_ffi.rs alter legacy BLS public-key decoding and key/signature canonicality rules, directly changing cryptographic input acceptance.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity quota lookup failed), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify the current code and confirm that no unresolved issues remain.

No unresolved findings remain from the prior review on this head.
Out-of-scope follow-up suggestions (1)

These are valid observations, but they are outside this PR's scope and should be handled in separate issues or author/maintainer-requested PRs rather than blocking this review.

  • Separate legacy parsing compatibility from DH/IES subgroup safety — BlsScheme::dh_exchange in pkgs/pkc/src/bls/scheme_ops.rs multiplies an admitted legacy peer key by the secret scalar without requiring prime-order subgroup membership. The base already contains this behavior and explicitly documents it in assert_off_subgroup_peer_policy. Applications exposing DH-dependent responses to adversarial peers can therefore reveal information about the scalar through small-subgroup components. This is a concrete, pre-existing key-agreement admission issue, separate from matching the reference's wire canonicality rules.
    • Follow-up: Track subgroup-validated peer admission for DH/IES, or a hardened key-agreement API, separately while preserving consensus-compatible legacy parsing.

@kwvg kwvg left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also reorder the commits so that you first include the reference method (C++ or Python, your pick) that can regenerate the existing pkc corpora to establish baseline credibility before bundling your changes to include the tests to validate it so that just comb can prove each incremental change stands on its own

Comment thread pkgs/pkc/src/bls/scheme_chia.rs Resolved
Comment thread pkgs/pkc/src/bls/scheme_chia.rs Resolved
Comment thread pkgs/pkc/src/bls/sig_basic.rs Resolved
Comment thread contrib/bls_read/README.md Resolved
Comment thread pkgs/pkc/CHANGELOG.md Resolved
@PastaPastaPasta
PastaPastaPasta force-pushed the pasta/bls-legacy-reader branch from 975df95 to 91b3644 Compare November 1, 2026 13:12
@PastaPastaPasta

Copy link
Copy Markdown
Member Author

Reordered. The first commit, pkc%test(bls): add programs that write the BLS corpora, adds C++ programs in pkgs/pkc/contrib that link the relic-based BLS library of a reference implementation release and write the existing BLS corpora listed in its README. They come out byte-identical from both v24.0.0-rc.5 and v23.1.8, and that commit changes no corpus. The second commit adds gen_read.cpp and bls_read.json5. The two fixes and their tests follow, so each comb runs on corpora the programs reproduce. For bls_llmq_100.json5, its member_pks and member_sig values are fixed inputs, which the program checks against the commitment hash it computes. If the secret keys behind those public keys are still around, the program can generate them too.


🤖 Posted autonomously by Claude on behalf of pasta.

@PastaPastaPasta
PastaPastaPasta requested a review from kwvg November 1, 2026 13:12
@kwvg kwvg moved this to Crypto in base-sdk v0.3 Nov 1, 2026
@github-actions github-actions Bot added the topic:Crypto Pull requests that primarily concern the dash-{pkc,pow} crate. label Nov 1, 2026
@PastaPastaPasta
PastaPastaPasta force-pushed the pasta/bls-legacy-reader branch from 91b3644 to 34b142d Compare November 1, 2026 16:00

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — Final validation — Phase 1 + Phase 2

Independently reviewed the complete diff at 34b142d and found no actionable in-scope defects. The legacy acceptance rules match the pinned Dash Core/relic readers and blst 0.3.17 behavior; typed decoding still rejects off-curve inputs, with independent corpus expectations enforcing that boundary. Validation was static only: current CI shows no failures and lint passing, while one MSRV build/test job, macOS testing, and three cross-target checks remain pending.

🔴 0 blocking | 🟡 0 suggestion(s) | 💬 0 nitpick(s)

Review provenance

Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 3: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 5: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 8: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — The coordinated changes to legacy BLS decoding and canonicality in scheme_chia.rs, public_ops.rs and sig_basic.rs introduce intricate cryptographic input-acceptance rules, including off-curve encodings and exceptional point handling.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity quota lookup failed), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify the current code and confirm that no unresolved issues remain.

No unresolved findings remain from the prior review on this head.

@kwvg kwvg left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion, split this PR in two, the first PR generates the existing corpora, I'll contribute additional commits to ensure linting and static analysis for C++ are setup, the second commit focuses on dash-pkc modifications and corpora to support those changes.

Comment thread pkgs/pkc/contrib/build.sh Resolved
Comment thread pkgs/pkc/contrib/build.sh Resolved
Comment thread pkgs/pkc/contrib/regen.sh Resolved
@PastaPastaPasta

Copy link
Copy Markdown
Member Author

Thanks, we'll take all of it:

  • Split in two. The first PR has only the programs that write the existing corpora, from its own branch so you can add the C++ lint and static analysis commits there. The second PR holds the dash-pkc changes and the corpus that covers them, and goes on top of the first.
  • build.sh and regen.sh become Python. We'll try the build recipe in Bazel first, without touching the FFI build layout, and use CMake (listed under tools in pyproject.toml and in the Nix devshell) only if Bazel can't do it cleanly.
  • The pinned Dash Core becomes a submodule, the same way fuzz/pow handles sphlib.

I'll link the new PR here when it's up.


🤖 Posted autonomously by Claude on behalf of pasta.

@PastaPastaPasta

Copy link
Copy Markdown
Member Author

The split is up. #112 holds the programs that write the existing corpora, from its own branch, for your C++ lint and static analysis commits. This PR is rebased onto it (base pasta/bls-corpus-gen) and keeps the dash-pkc changes and the read corpus.

  • build.sh and regen.sh are now one standard-library regen.py.
  • The pinned reference implementation is the submodule pkgs/pkc/contrib/dash (v24.0.0-rc.5), with update = none so clones and CI skip it.
  • All ten corpora come out byte-identical.

🤖 Posted autonomously by Claude on behalf of pasta.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — Final validation — Phase 1 + Phase 2

Independently reviewed the complete fa6c595..b821aff diff and found no actionable in-scope defects. The legacy read exceptions match the inspected pinned Dash Core/Relic and blst implementations, while off-curve encodings remain rejected by typed decoding. Validation was static-only: current CI reports passing lint, documentation, completed stable-platform jobs, and the Linux cross-build; MSRV, remaining ARM jobs, and Windows/macOS cross-builds remain pending, with no failed checks reported.

🔴 0 blocking | 🟡 0 suggestion(s) | 💬 0 nitpick(s)

Review provenance

Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 4: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 5: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 8: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — The intricate changes to legacy public-key and signature decoding and canonicality in scheme_chia.rs, public_ops.rs, and sig_basic.rs directly alter cryptographic input acceptance rules.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity quota lookup failed), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify the current code and confirm that no unresolved issues remain.

No unresolved findings remain from the prior review on this head.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — Final validation — Phase 1 + Phase 2

Verified the complete diff at b80238e against the pinned Dash Core/relic readers and blst 0.3.17; no actionable in-scope defects were found. Independent relic-derived corpus annotations now enforce typed rejection of legacy off-curve encodings, while IETF validation remains unchanged. This was static-only verification: current CI reports Windows and ARM Linux stable passing, with lint, MSRV, macOS, cross-platform and corpus checks pending and no failed code checks reported.

🔴 0 blocking | 🟡 0 suggestion(s) | 💬 0 nitpick(s)

Review provenance

Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 5: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 8: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — The diff makes intricate changes to cryptographic key decoding and signature canonicality in pkgs/pkc/src/bls/scheme_chia.rs, including pk_from_bytes, is_canonical_pk and is_canonical_sig, with scheme-specific flag handling and off-curve acceptance rules.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity quota lookup failed), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify the current code and confirm that no unresolved issues remain.

No unresolved findings remain from the prior review on this head.
Out-of-scope follow-up suggestions (1)

These are valid observations, but they are outside this PR's scope and should be handled in separate issues or author/maintainer-requested PRs rather than blocking this review.

  • Validate legacy DH peers separately from consensus-compatible decoding — The base already admits off-subgroup legacy G1 points, explicitly exercised by off_subgroup_peer_policy. The unchanged BlsScheme::dh_exchange multiplies these points by a secret scalar without subgroup validation, and legacy IES reaches this operation through supplied ephemeral keys. Small-subgroup inputs restrict the possible shared secrets and can reveal information about a reused scalar when application-level plaintext acceptance is observable. This is a pre-existing secret-operation boundary issue, separate from the parsing compatibility corrected here.
    • Follow-up: Track subgroup and identity validation at the legacy DH/IES boundary in a separate security issue or maintainer-requested PR, with small-subgroup attack vectors; retain consensus-compatible parsing.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — Final validation — Phase 1 + Phase 2

Reviewed the complete diff at 93f399c against the pinned Dash Core/relic readers and blst 0.3.17; no actionable in-scope defects remain. The independent corpus annotations and assertions address the prior typed-decoder coverage finding, and the performance suggestion remains withdrawn. Validation was static-only: diff checks passed and the worktree is unchanged; current CI shows all three stable-platform jobs and documentation passing, with lint, MSRV, cross-build, and corpus-regeneration checks still pending and no reported failures.

🔴 0 blocking | 🟡 0 suggestion(s) | 💬 0 nitpick(s)

Review provenance

Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 5: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 8: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — The diff intricately changes cryptographic key and signature decoding and canonicality across scheme_chia.rs, public_ops.rs, and sig_basic.rs, including legacy off-curve acceptance and special-case G1 decoding.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity quota lookup failed), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify the current code and confirm that no unresolved issues remain.

No unresolved findings remain from the prior review on this head.

kwvg added a commit that referenced this pull request Nov 3, 2026
* [e8ce915] pkc%test(bls): add the reference implementation as a submodule (pasta)
* [76704ec] sdk%build(nix): add `corpora.nix` with CMake, Boost and GMP (pasta)
* [4feb192] pkc%test(bls): add programs that write the BLS corpora (pasta)
* [5e0c21a] sdk%ci(gha): build the corpus generators weekly (pasta)

Description:

`````
## Motivation

The BLS corpora in `pkgs/pkc/corpus` are written from the reference implementation, but the programs that wrote them are not in the repository. This pull request adds them, with `regen.py` to write every corpus again from a pinned release, so a corpus can be checked or extended later. It is split out of #101, which adds one more program on top of it.

## Additional Information

| Description | Notes |
| ----------- | ----- |
| Add the reference implementation as a submodule | `pkgs/pkc/contrib/dash`, pinned at v24.0.0-rc.5 (8163290aa0), shallow and with `update = none`, so a clone, `git submodule update` and CI checkouts skip it. Only `regen.py` checks it out. Bazel, CodeQL and the crate package skip it, as its tree carries build files of its own. |
| Add programs that write the BLS corpora | Each `gen_*.cpp` links the release's BLS library (dashbls, with relic), plus a few of the release's own sources where a corpus needs them, and prints one corpus. `regen.py` extracts the release's `src`, builds the library, compiles and runs every program, and writes the corpora only when all of them succeed. The nine corpora come out byte-identical, so this changes no corpus. `pkgs/pkc/contrib/README.md` has the recipe. |
| Add autotools and GMP to the dev shell | `contrib/nix/shell/dev.nix` gains autoconf, automake, libtool, pkg-config and GMP for `regen.py`. The CI shell is unchanged. |

Bazel has no build for the library, and the release's copy of the library builds with autotools only, as the reference implementation stopped tracking relic's CMake configuration template. So `regen.py` runs that build, which needs a POSIX shell, and GMP is required.

When these programs were added, the corpora were also checked equal at v23.1.8 (728f505583).

The C++ here is as it was in #101 and predates the clang-format style in #111. As discussed in #101, this branch is open for the C++ lint and static analysis commits. Whichever of this and #111 lands second will adapt.

## Breaking Changes

None. No library code changes.

## How Has This Been Tested?

- `regen.py` writes the nine corpora byte-identical to the committed ones, with GCC 15 and with clang 21 building both the library and the programs, from a fresh clone that fetches the submodule and from an existing build.
- A failed library build, a submodule checked out at another commit and an invalid option each stop the run with nothing written.
- Only Linux x86_64 was run. macOS and Windows are untested, and the dev shell change has not been run in a Nix shell.
- `cargo test -p dash-pkc --all-features` passes, and the lint suite is clean with the submodule checked out.
- CI (`just test lint fuzz::test`, CodeQL included) is green on each commit.

## Checklist

- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have added or updated relevant unit/integration/functional tests
- [x] I have made corresponding changes to the documentation
- [ ] I did not use agentic assistance (i.e. LLMs, AI) except as [disclosed](#agent-disclosure) and can answer questions,
      comments and concerns without said assistance. I assume authorship of the code submitted.
- [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

### Agent disclosure

Code, commit messages and this description were generated with Claude Code using Claude Opus 5.5, running autonomously as an agent on behalf of the author. The agent also built the pinned release's BLS library and wrote the corpora with it. The change was reviewed by a Claude Code agent using Opus 5.5.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
`````

@kwvg kwvg left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the base branch to develop now that the underlying branch has been merged, see comments.

Comment thread pkgs/pkc/contrib/README.md Resolved
Comment thread pkgs/pkc/contrib/gen_read.cpp Resolved
@PastaPastaPasta
PastaPastaPasta changed the base branch from pasta/bls-corpus-gen to develop November 3, 2026 16:40
@PastaPastaPasta

Copy link
Copy Markdown
Member Author

Thanks. I've retargeted the base to develop, and the PR now shows only its own 5 commits.

We'll take both comments:

  • README: the bls_read.json5 details move into gen_read.cpp's own commentary, and the README keeps a one-line summary.
  • gen_read.cpp: no reimplemented sequence will stand in for the library. Where the program needs an internal step, it will call that step through a forward declaration or a patch. Each input class's claim will be asserted against dashbls's own reads and operations, so the program stops if relic and the library ever disagree. A value with no library route gets dropped instead of reimplemented.

The corpus will be regenerated and checked to be byte-identical wherever it's unaffected. I'll reply on each thread when the update is pushed.


🤖 Posted autonomously by Claude on behalf of pasta.

gen_read.cpp draws seeded inputs, each class's property decided or
checked by the pinned release's BLS library, and records how that
library reads each as a key or signature under both schemes.
The reference implementation reads `x = 0` with the sign bit set,
(0, -2), under the legacy scheme. blst's decompression writes the point
out but reports an error, so keep the point it wrote.
The reference implementation accepts a legacy encoding of an `x` with no
point on the curve, for signatures only with the sign bit clear. `to` has
no point to return for these bytes and still refuses them.
Each `bls_read` input must read as in the reference implementation:
`is_canonical` follows its outcome, and a valid input decodes and
encodes back to itself unless the corpus marks its `x` off the curve.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — Final validation — Phase 1 + Phase 2

Static verification of the complete diff at 1ebd2c2 found no actionable in-scope defects. The legacy acceptance changes match the pinned Dash Core/RELIC readers, while typed decoding continues to reject off-curve inputs and the corpus independently asserts that boundary. No builds or tests were run in this static lane; current-head build/test, lint, cross-platform, and corpus-validation checks remain pending, so the description's all-green claim is not yet confirmed.

🔴 0 blocking | 🟡 0 suggestion(s) | 💬 0 nitpick(s)

Review provenance

Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 4: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 5: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 8: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — The coordinated changes to G1/G2 encoding conversion and canonicality in pkgs/pkc/src/bls/scheme_chia.rs, together with error-dependent point recovery in G1Affine::uncompress_on_curve, constitute intricate changes to cryptographic key and signature handling.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity quota lookup failed), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify the current code and confirm that no unresolved issues remain.

No unresolved findings remain from the prior review on this head.
Out-of-scope follow-up suggestions (1)

These are valid observations, but they are outside this PR's scope and should be handled in separate issues or author/maintainer-requested PRs rather than blocking this review.

  • Validate legacy DH peers separately from consensus-compatible wire reads — The unchanged BlsScheme::dh_exchange in pkgs/pkc/src/bls/scheme_ops.rs multiplies the peer point by the secret scalar without validating subgroup membership, and the legacy point-conversion paths add no validation. This behavior predates the PR and is explicitly exercised by the existing off_subgroup_peer_policy test. Attacker-supplied small-order peers can restrict shared secrets to a small set; observable decryption outcomes can consequently disclose secret-dependent information. This is a separate cryptographic-operation boundary issue, not a reason to tighten consensus-compatible legacy decoding.
    • Follow-up: Track a separate issue or maintainer-requested PR to validate subgroup membership and reject identity peers at the legacy DH/IES operation boundary while preserving legacy wire-read compatibility.

@kwvg kwvg left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK 1ebd2c2

@PastaPastaPasta
PastaPastaPasta merged commit 122014f into develop Nov 3, 2026
54 checks passed
PastaPastaPasta added a commit to PastaPastaPasta/base-sdk that referenced this pull request Nov 4, 2026
Takes dashpay#100 dashpay#88 dashpay#108 dashpay#107 dashpay#111 dashpay#110 dashpay#112 dashpay#115 dashpay#114 dashpay#101. ChainSetup keeps
the DEC-1043 policy fields; the BS11 BLS wrappers, i64 TxOut values and
MAX_SER_SIZE payload bound stay for Core parity.
kwvg added a commit to kwvg/base-sdk that referenced this pull request Nov 4, 2026
…plementation does (dashpay#101)

* [03eefd7] pkc%test(bls): add a program that writes the BLS read corpus (pasta)
* [6bff16d] pkc%fix(bls): read legacy G1 `x = 0` with the sign bit set (pasta)
* [ac2355f] pkc%fix(bls): accept legacy encodings off the curve in `is_canonical` (pasta)
* [137d329] pkc%test(bls): check BLS reads against the reference outcomes (pasta)
* [1ebd2c2] pkc%doc: update changelog for the legacy BLS reads (pasta)

Description:

`````
## Motivation

`dash-pkc` reads BLS bytes at two levels. `to::<S>()` decodes an element, and `is_canonical::<S>()` says whether the scheme reads the bytes as an element that encodes back to them. Under the legacy scheme, the reference implementation accepts three kinds of input that `dash-pkc` refuses:
- a public key with `x = 0` with the sign bit set, (0, -2). blst's decompression writes this point out but reports an error;
- a public key encoding an `x` below the field prime with no point on the curve, with bits 5-6 clear, under either sign;
- a signature encoding an `x` with no point on the curve, with both halves below the field prime and the sign bit and the top three bits of `x.c1` clear.

This pull request makes `to::<BlsScChia>()` decode the first kind and `is_canonical::<BlsScChia>()` accept all three. `to` still refuses the off-curve encodings, since there is no point to return for them, and its rustdoc says so. The basic scheme is unchanged, and so is the stray-bit masking on the legacy G1 path.

## Additional Information

This pull request is based on `pasta/bls-corpus-gen` (dashpay#112), which adds the reference implementation as a submodule and the programs that write the other BLS corpora.

| Description | Notes |
| ----------- | ----- |
| Add a program that writes the BLS read corpus | `gen_read.cpp` builds seeded inputs with relic's field and curve operations and writes `pkgs/pkc/corpus/bls_read.json5`: 79 public-key and 130 signature inputs, each with the library's outcome under the legacy and basic schemes, `null`, `valid` or `invalid`. For each input the legacy scheme reads as valid, relic also gives whether its `x` has a point on the curve. `regen.py` writes it with the other corpora. |
| Read legacy G1 `x = 0` with the sign bit set | `to` and `is_canonical` accept it, and it round-trips. With the sign bit clear, stray bits 5-6 mask `x = 0` down to the null encoding, so those bytes still do not decode. Adds `BlsScheme` methods `g1_bytes`, a point in the scheme's 48-byte layout, and `pk_ietf_bytes`, the IETF compressed form of an encoding in that layout; `BlsScChia` moves its legacy encoder and its legacy-to-IETF conversion into them. |
| Accept legacy encodings off the curve in `is_canonical` | New `BlsScheme` methods `is_canonical_pk` and `is_canonical_sig` hold each scheme's rule. `BlsScIetf` keeps the previous rule, decode plus byte-exact re-encode. `BlsScChia` also accepts the off-curve encodings. `g2_bytes` is the G2 counterpart of `g1_bytes`, and `sig_ietf_x_bytes` gives the `x` of a G2 encoding in the IETF compressed layout, with the sign bit clear. |
| Check BLS reads against the reference outcomes | Every corpus input is checked under both schemes, against `is_null`, `is_canonical` and the scheme's own predicate. `to` must fail for each legacy input the corpus marks off the curve, and decode and re-encode to itself for every other valid input. |
| Update the changelog | Under "Changed" for the two read changes, and "Added" for the `BlsScheme` methods. |

Each outcome in `bls_read.json5` comes from the release's read of the input:
- the all-zero input is `null`;
- otherwise the input is `valid` when the library's element reader, under the scheme flag, returns an element other than the identity that encodes back to the input;
- anything else is `invalid`.

The program is C++ because the library's element readers are its C++ API over relic. `pkgs/pkc/contrib/README.md` has the regeneration recipe.

## Breaking Changes

None. `is_canonical::<BlsScChia>()` accepts more inputs and `to::<BlsScChia>()` decodes one more. `BlsScheme` gains six required methods, but the trait is sealed, so no implementation outside `dash-pkc` exists.

CI (`just test lint fuzz::test`, CodeQL included) is green on each commit.

## Checklist

- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have added or updated relevant unit/integration/functional tests
- [x] I have made corresponding changes to the documentation
- [ ] I did not use agentic assistance (i.e. LLMs, AI) except as [disclosed](#agent-disclosure) and can answer questions,
      comments and concerns without said assistance. I assume authorship of the code submitted.
- [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

### Agent disclosure

Code, commit messages and this description were generated with Claude Code using Claude Opus 5.5, running autonomously as an agent on behalf of the author. The agent also built the pinned release's BLS library and wrote the corpora with it. The change was reviewed by a Claude Code agent using Opus 5.5 and a Codex agent using GPT-6.1.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
`````
kwvg added a commit that referenced this pull request Nov 5, 2026
* [f3ac1b6] pkc%test(bls): run the legacy map's own steps in `gen_hash.cpp` (pasta)

Description:

`````
## Motivation

In #101 the program that writes `bls_read.json5` was reworked so that nothing in it stands in for the library. `gen_hash.cpp`, from #112, still repeats the legacy hash to G2's tag hashing and reduction steps with relic and takes only the final point from the library, so nothing showed that the steps it records are the ones the library runs. This pull request is the follow-up agreed in [the #101 review](#101 (comment)).

## Additional Information

| Description | Notes |
| ----------- | ----- |
| Give two steps of the legacy map external linkage | `pkgs/pkc/contrib/util/legacy_steps.patch` gives the release's legacy map's encoding of a field element and its cofactor multiplication external linkage. Their code is unchanged. `regen.py` applies it after the relic template patch, and the build directory's name now covers both patches, so a build from before is not reused. |
| Check the recorded steps against the library | For each message, `gen_hash.cpp` runs those two steps on the four field elements it writes, as the map does: the encodings of the two pairs, their sum and its cofactor multiple. The program stops, writing nothing, unless the result is the library's own map of the message. The hashing and reduction are still repeated with relic, as the map computes them inline. |

The check ties every `_hash` and `_fp` value to the library except the order of the two pairs of tags, since the sum of their encodings is the same either way. `pkgs/pkc/contrib/README.md` says so.

## Breaking Changes

None. No library code changes, and `bls_hash.json5` is unchanged.

## How Has This Been Tested?

- `regen.py --program hash` extracts and builds the patched release afresh and writes `bls_hash.json5` byte-identical to the committed one, with GCC 15. Only Linux x86_64 was run.
- With a wrong byte planted in the tag hashing, the program stops and nothing is written. Swapping the two elements of a pair, negating an element or a pair, and leaving out the cofactor step each stop it too.
- The lint suite is clean with the submodule checked out.
- CI (`just test lint fuzz::test`) is green on the commit.

## Checklist

- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have added or updated relevant unit/integration/functional tests
- [x] I have made corresponding changes to the documentation
- [ ] I did not use agentic assistance (i.e. LLMs, AI) except as [disclosed](#agent-disclosure) and can answer questions,
      comments and concerns without said assistance. I assume authorship of the code submitted.
- [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

### Agent disclosure

Code, commit messages and this description were generated with Claude Code using Claude Opus 5.5, running autonomously as an agent on behalf of the author. The agent also built the pinned release's BLS library and wrote the corpus with it. The change was reviewed by a Claude Code agent using Opus 5.5.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
`````
@kwvg
kwvg deleted the pasta/bls-legacy-reader branch November 5, 2026 15:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status:behind This pull request can be rebased on the base branch. topic:Crypto Pull requests that primarily concern the dash-{pkc,pow} crate.

Projects

Status: Crypto

Development

Successfully merging this pull request may close these issues.

3 participants