#!/usr/bin/env python3 """ CDDL of draft-hawkins-scitt-attested-agent-payment-01 against test vectors. (v3) Text under test: draft-hawkins-scitt-attested-agent-payment-01.txt sha256 9e6deb7c735a5f776809e3e1431c7e67e1ecc664ab2c0a94895d51778f4080a7 Grammar under test: Section 3, "Definition", extracted verbatim (RFC page-margin indent removed). See scope.cddl in this directory. IMPORTANT: THE DOCUMENT SHIPS NO TEST VECTORS. The word "example" occurs twice in the text, both times in prose. Every vector below was written by the reviewer from the prose of Section 3. Nothing here was validated against an example the author provided, because there is none. Method: Each vector states, from the prose, what the expected outcome is (ACCEPT / REFUSE / NOT-DECIDABLE-BY-GRAMMAR), then what the grammar does. A row is a FINDING only where the prose gives a decidable expectation and the grammar disagrees. Rows where the prose is silent, or where the constraint is cross-field and outside what CDDL can express, are marked as such and are NOT findings against the grammar. Encoding: CBOR, deterministic encoding per RFC 8949 Section 4.2.1 (canonical=True in cbor2), as the text requires for the scope digest. """ import cbor2 from pycddl import Schema schema = Schema(open('scope.cddl').read()) APK = bytes.fromhex('00' * 32) CODE = {"alg": "sha-256", "artifact": "tdx-mrtd", "digest": bytes.fromhex('11' * 32)} BASE = {"apk": APK, "code": CODE, "expiry": 1786000000} PP_ONLY = {"currency": "USDC", "scale": 6, "per_payment": 1000} FULL = {**PP_ONLY, "aggregate": 50000, "window": 86400} def encode(obj): return cbor2.dumps(obj, canonical=True) def grammar(obj): try: schema.validate_cbor(encode(obj)) return "ACCEPT" except Exception: return "REFUSE" # (id, description, object, expected, basis-in-prose, line refs in -01) VECTORS = [ ("V1", "limits: per_payment only; no aggregate, no window, no executor", {**BASE, "limits": PP_ONLY}, "CONTRADICTORY", "'limits' prose: minimum is per-payment AND aggregate over a window; " "'executor' prose: per-payment-only scopes MAY omit executor; grammar makes " "aggregate optional. The minimum-limits prose conflicts with the per-payment-only " "allowance; the grammar follows the latter.", "l.304-305, l.323-324, l.431-432"), ("V2", "aggregate present, window ABSENT", {**BASE, "limits": {**PP_ONLY, "aggregate": 50000}, "executor": "exec.example"}, "REFUSE", "The prose defines the aggregate as a maximum over a stated window and " "defines the window as rolling seconds; the CDDL comment repeats that window " "is REQUIRED with aggregate; the executable grammar accepts aggregate " "without it. Expressible in CDDL by group choice.", "l.304-305, l.310-311, l.432"), ("V3", "aggregate + window present, executor ABSENT", {**BASE, "limits": FULL}, "REFUSE", "'executor' prose: REQUIRED when limits carries an aggregate bound. " "Expressible in CDDL by group choice; the published grammar does not enforce it.", "l.316"), ("V4", "crit names a member not present in the map", {**BASE, "limits": FULL, "executor": "exec.example", "crit": ["jurisdiction"]}, "NOT-DECIDABLE-BY-GRAMMAR", "Prose: 'member names critical to this scope'. No MUST that a critical " "member be present. Not a finding; noted as an undefined case.", "l.351"), ("V5", "on_indeterminate.deadline LATER than expiry", {**BASE, "limits": FULL, "executor": "exec.example", "on_indeterminate": {"mode": "hold", "deadline": 1786000000 + 999999}}, "NOT-DECIDABLE-BY-GRAMMAR", "Prose: deadline MUST NOT exceed expiry. Cross-field constraint; outside " "CDDL. Belongs in a conformance test, not in the grammar. Not a finding.", "l.371"), ("V6", "bounded-fail-open carrying a deadline", {**BASE, "limits": FULL, "executor": "exec.example", "on_indeterminate": {"mode": "bounded-fail-open", "deadline": 1785000000}}, "NOT-DECIDABLE-BY-GRAMMAR", "CDDL comment: 'meaningful for hold'. No MUST NOT elsewhere. Not a finding.", "l.439-440"), ("V7", "unknown member, not named in crit", {**BASE, "limits": FULL, "executor": "exec.example", "vendor_hint": "x"}, "ACCEPT", "Prose: unknown members not named in crit MUST be ignored. Grammar is open " "(* tstr => any). Desired behaviour; this row is a PASS.", "l.271, l.417"), ("V8", "control: every required member, nothing else", {**BASE, "limits": FULL, "executor": "exec.example"}, "ACCEPT", "control", "-"), ] print("Grammar: scope.cddl parsed OK\n") hdr = f"{'id':<4}{'vector':<58}{'expected':<28}{'grammar':<9}{'verdict':<10}lines" print(hdr); print("-" * len(hdr)) findings = 0 for vid, desc, obj, expected, basis, lines in VECTORS: got = grammar(obj) if expected in ("ACCEPT", "REFUSE"): verdict = "PASS" if got == expected else "FINDING" elif expected == "CONTRADICTORY": verdict = "FINDING" # the finding is in the prose, not the grammar else: verdict = "n/a" if verdict == "FINDING": findings += 1 print(f"{vid:<4}{desc:<58}{expected:<28}{got:<9}{verdict:<10}{lines}") print(f" {basis}") print(f"\nFindings: {findings} (V1 prose contradiction; V2, V3 grammar does not " f"enforce stated requirement).\nV4-V6: not decidable by grammar, not findings. " f"V7: pass. V8: control.")