Zion Boggan
repos/Oversight/tests/test_rekor_backcompat.py
zionboggan.com ↗
119 lines · python
History for this file →
1
"""
2
test_rekor_backcompat
3
=====================
4
 
5
Confirms the v0.5 Rekor work has not broken any v0.4 behavior.
6
 
7
Per the v0.5 plan §6 (Backward compatibility rules):
8
  1. Every v0.4.1 bundle/.sealed file must still parse, open, verify exactly.
9
  2. Bundles missing ``tlog_kind`` / ``bundle_schema`` are interpreted as the
10
     v0.4 path (oversight-self-merkle-v1).
11
  3. JCS canonical ordering still applies; new fields are additions only.
12
 
13
These checks run fully offline.
14
"""
15
from __future__ import annotations
16
 
17
import sys
18
from pathlib import Path
19
 
20
ROOT = Path(__file__).resolve().parent.parent
21
sys.path.insert(0, str(ROOT))
22
 
23
from oversight_core.tlog import TransparencyLog, verify_inclusion_proof
24
from oversight_core import rekor as R
25
from oversight_core.jcs import jcs_dumps
26
 
27
 
28
def test_legacy_tlog_still_works(tmp_path):
29
    """A TransparencyLog built and verified the v0.4 way must still pass."""
30
    tl = TransparencyLog(tmp_path)
31
    for i in range(7):
32
        tl.append({"event": "register", "i": i, "file_id": f"f{i}"})
33
    size = tl.size()
34
    root = tl.root()
35
    assert size == 7, f"expected size 7, got {size}"
36
    assert len(root) == 32, "root must be 32 bytes (sha256)"
37
 
38
    proof = tl.inclusion_proof(3)
39
    assert proof is not None, "inclusion_proof returned None for valid index"
40
    ok = verify_inclusion_proof(
41
        leaf_hash=bytes.fromhex(proof["leaf_hash"]),
42
        index=proof["index"],
43
        proof=[bytes.fromhex(h) for h in proof["proof"]],
44
        tree_size=proof["tree_size"],
45
        expected_root=bytes.fromhex(proof["root"]),
46
    )
47
    assert ok, "RFC 6962 inclusion proof failed to verify"
48
 
49
 
50
def test_legacy_bundle_shape_default_kind():
51
    """A v0.4-shaped bundle (no tlog_kind, no bundle_schema) must be readable
52
    and interpretable as ``oversight-self-merkle-v1``."""
53
    legacy_bundle = {
54
        "version": "0.4",
55
        "file_id": "abcd" * 16,
56
        "issuer_pubkey_ed25519": "11" * 32,
57
        "tlog": {
58
            "size": 7,
59
            "root": "00" * 32,
60
            "signature": "22" * 64,
61
        },
62
        "inclusion_proof": {
63
            "index": 3,
64
            "leaf_hash": "33" * 32,
65
            "proof": ["44" * 32, "55" * 32],
66
            "tree_size": 7,
67
            "root": "00" * 32,
68
        },
69
    }
70
    assert "rekor" not in legacy_bundle, "v0.4 bundle must not have a rekor field"
71
    assert "bundle_schema" not in legacy_bundle, "v0.4 bundle must not advertise bundle_schema"
72
    inferred_kind = legacy_bundle.get("tlog_kind", R.LEGACY_TLOG_KIND)
73
    assert inferred_kind == R.LEGACY_TLOG_KIND, (
74
        f"missing tlog_kind must default to {R.LEGACY_TLOG_KIND}, got {inferred_kind!r}"
75
    )
76
    inferred_schema = legacy_bundle.get("bundle_schema", 1)
77
    assert inferred_schema == 1, "missing bundle_schema must default to 1 (v0.4 implicit)"
78
 
79
 
80
def test_v05_bundle_advertises_new_fields():
81
    """The new bundle the v0.5 path emits MUST advertise both fields explicitly
82
    so an old (v0.4) verifier fails fast with 'unknown schema' rather than
83
    silently mis-routing."""
84
    assert R.BUNDLE_SCHEMA == 2, f"BUNDLE_SCHEMA must be 2, got {R.BUNDLE_SCHEMA}"
85
    assert R.TLOG_KIND == "rekor-v2-dsse", f"TLOG_KIND drift: {R.TLOG_KIND!r}"
86
    assert R.LEGACY_TLOG_KIND == "oversight-self-merkle-v1"
87
 
88
 
89
def test_canonical_jcs_unchanged_for_legacy_payload():
90
    """The exact JCS encoding for a v0.4-shaped event must not have changed.
91
    If this fails, downstream verifiers re-checking historical signatures
92
    over canonical JSON will reject events they previously accepted."""
93
    event = {
94
        "event": "register",
95
        "file_id": "f0",
96
        "issuer_pub": "11" * 32,
97
        "n_beacons": 3,
98
        "n_watermarks": 1,
99
        "recipient_id": "r0",
100
        "timestamp": "2026-04-19T00:00:00Z",
101
    }
102
    expected = (
103
        '{"event":"register","file_id":"f0","issuer_pub":'
104
        + '"' + "11" * 32 + '",'
105
        + '"n_beacons":3,"n_watermarks":1,"recipient_id":"r0","timestamp":"2026-04-19T00:00:00Z"}'
106
    )
107
    actual = jcs_dumps(event).decode("utf-8")
108
    assert actual == expected, f"JCS drift!\n  exp: {expected}\n  got: {actual}"
109
 
110
 
111
def test_predicate_uri_resolves_at_tagged_path():
112
    """Sanity: the PREDICATE_TYPE URI references a git-tagged path. We don't
113
    fetch (the e2e test does that); we just confirm the URI shape so a typo
114
    like missing the tag won't make it through to a release."""
115
    assert R.PREDICATE_TYPE.startswith(
116
        "https://github.com/oversight-protocol/oversight/blob/v0.5"
117
    ), f"PREDICATE_TYPE not pinned to a v0.5 git tag: {R.PREDICATE_TYPE}"
118
    assert R.PREDICATE_TYPE.endswith("/docs/predicates/registration-v1.md")
119
    assert R.PREDICATE_VERSION == 1