Skip to content

feat(xtest): DSPX-5520 add shared assertion conformance vectors - #705

Draft
dmihalcik-virtru wants to merge 3 commits into
DSPX-5520-reference-readerfrom
DSPX-5520-assertion-vectors
Draft

dmihalcik-virtru wants to merge 3 commits into
DSPX-5520-reference-readerfrom
DSPX-5520-assertion-vectors

Conversation

@dmihalcik-virtru

@dmihalcik-virtru dmihalcik-virtru commented Nov 9, 2026 •

Copy link
Copy Markdown
Member

Summary

Split out of #702 (draft; the complete change). Stacked on #704.

xtest/vectors/assertions-phase0.json is the shared vector set for Phase 0 of the draft ZTDF assertions contract (DSPX-5519). It holds 288 vectors covering parsing, hash input (starting with the Section 4.4 worked examples), binding forms, and single-assertion verification. Each vector gives an expected assertionHash or failure reason. The Go, Java, and JS SDKs vendor it byte for byte and run every vector in their unit tests.

  • Generator. vectors/generate_assertion_vectors.py produces the file deterministically from two committed, test-only RSA-2048 keys.
  • Offline test. test_assertion_vectors_units.py regenerates the file and requires a byte-identical match. It checks the Section 4.4 anchor hashes and runs every vector against the reference reader. check.yml now runs it.
  • Shared helpers. JWS, JWK, certificate, and binding-claims helpers move into assertions.py, and the JSON-text builder into xtest/jsontext.py, so xtest cases build assertions the same way the vectors do.
  • Whole-manifest case variants. The case-variant rule covers every modelled level of the manifest. The file carries the level table (modelledNames) and the IsStreamable exception, so each SDK can check its own table against it.

Every expected assertionHash was also cross-checked against Go github.com/gowebpki/jcs and JS json-canonicalize.

The three commits review separately: the vectors, a byte-identical refactor of the helpers, then the whole-manifest rule with its added vectors.

Testing

  • cd xtest && uv run ruff check . && uv run ruff format --check . && uv run pyright.
  • uv run pytest test_assertions_units.py test_assertion_vectors_units.py, plus the rest of the offline unit tests from check.yml.

Related pull requests

xtest/vectors/assertions-phase0.json is the shared vector set for Phase 0
of the ZTDF assertions contract (DSPX-5519). The Go, Java, and JS SDKs
vendor it byte-for-byte and run every vector in their unit tests. It
covers parsing, the hash input (starting with the spec Section 4.4 worked
examples), binding forms, and single-assertion verification.

vectors/generate_assertion_vectors.py produces it deterministically from
two committed, test-only RSA-2048 keys. The offline
test_assertion_vectors_units.py, now run by the check workflow,
regenerates the file and requires a byte-identical match, checks the
Section 4.4 anchor hashes, and runs every vector against the reference
reader in assertions.py.

Every expected assertionHash was also cross-checked against Go
github.com/gowebpki/jcs and JS json-canonicalize.

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
Moves the vector generator's JSON-text builder into xtest/jsontext.py
and its JWS, JWK, certificate, and binding-claims helpers into
assertions.py, so the xtest cases that put assertions into real TDFs
build them the same way the DSPX-5520 vectors do. The generated vector
file is unchanged byte for byte.

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
…est in the shared vectors

Extend the shared assertion vectors and the reference reader to the
revised parsing rule: a member is rejected when it is a case variant of a
name listed for its level, or when two members of a listed object are case
variants of each other. The file now carries the level table
(modelledNames) and the IsStreamable exception, so SDK tests can check
their own tables against it.

Also add vectors for strict base64url and canonical x5c base64, minimal
RSA integers, well-formed EC and OKP embedded keys, x5c leaf structure,
duplicate JWS header and claim members, ignored registered claims, invalid
UTF-8 and BOMs anywhere in the manifest, and unusable configured keys.

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
@coderabbitai

coderabbitai Bot commented Nov 9, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • 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

@sonarqubecloud

sonarqubecloud Bot commented Nov 9, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
E Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

dmihalcik-virtru added a commit that referenced this pull request Nov 9, 2026
Every CI step named its test files, so each new suite edited the same lines of
xtest.yml or check.yml (see #703, #706, #705) and conflicted with every other
PR doing the same. Worse, a forgotten entry looks exactly like a pass:
test_root_signature.py and test_audit_logs_integration.py are on main and run
in no workflow at all.

Each test module now declares one tier -- units, harness, core, legacy,
multikas, benchmark, zip64 -- with pytestmark, *_units.py files are units by
name, and each step selects with -m. conftest.py fails collection on any test
without exactly one tier. check.yml's -m units step collects every module, so
an untiered suite fails every PR rather than going unrun.

Behaviour changes:
- test_root_signature.py (core) and test_audit_logs_integration.py (multikas)
  now run in CI for the first time.
- test_audit_logs.py runs offline in check.yml (units) instead of in the
  platform-backed helper step; it never needed the platform.
- test_audit_logs_on_tampered_file is xfail(strict=True) pending #709: it
  has failed since it was written, unseen because nothing ran it.

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
dmihalcik-virtru added a commit that referenced this pull request Nov 9, 2026
Every CI step named its test files, so each new suite edited the same lines of
xtest.yml or check.yml (see #703, #706, #705) and conflicted with every other
PR doing the same. Worse, a forgotten entry looks exactly like a pass:
test_root_signature.py and test_audit_logs_integration.py are on main and run
in no workflow at all.

Each test module now declares one tier -- units, harness, core, legacy,
multikas, benchmark, zip64 -- with pytestmark, *_units.py files are units by
name, and each step selects with -m. conftest.py fails collection on any test
without exactly one tier. check.yml's -m units step collects every module, so
an untiered suite fails every PR rather than going unrun.

Behaviour changes:
- test_root_signature.py (core) and test_audit_logs_integration.py (multikas)
  now run in CI for the first time.
- test_audit_logs.py runs offline in check.yml (units) instead of in the
  platform-backed helper step; it never needed the platform.
- test_audit_logs_on_tampered_file xfails its audit assertion pending #709:
  it has failed since it was written, unseen because nothing ran it. The
  decrypt-rejection check stays blocking, and an audit pass fails the test
  so the xfail gets removed.

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant