Repository navigation
sovp 1.1.0
·
5 commits
to main
since this release
Immutable
release. Only release title and notes can be modified.
Added
sovp/core.py—generate_identity_document()now signs afreshnessobject (created,nonce,expiresAt) as part of the Ed25519-signed scope; defaultcontext_versionchanged from"v1.4"to"v2.0". Previously these fields lived inside the unsignedintegrity_proofand were forgeable without invalidating the signature — same weakness as the already-documentedexpiresAtforgeability insovp-engine's scanner. Closes the replay-protection gap described indraft-litzki-sovp-04.tests/test_vectors.py— Test Vector Set 2 (schema v2.0, real generated+verified signatures) covering the newfreshnessobject, including tamper rejection forfreshness.created/freshness.expiresAt. Test Vector Set 1 (v1.4, locked) is unchanged.sovp/document_safety.py—parse_unverified_sovp_document(), a minimal JSON parser enforcing draft-litzki-sovp-04's "Resource Limits for Unverified Documents": rejects a document larger than 64 KiB, nested deeper than 16 levels, or containing a duplicate member name in any object, before the document is trusted with any further processing.
Changed
pyproject.toml— thecryptographybound stays at>=38.0.0,!=40.0.0,!=40.0.1,<42. It matches theRequires-Distline of the OS-packaged pyOpenSSL 23.2.0 on Ubuntu 24.04, whichcertbotimports. A globalpip install sovpwith a higher bound shadows that version and breaks certificate renewal. The library itself runs on newercryptographyreleases; the bound protects the system install.CHANGELOG.md— the 1.0.1 entry referenced a parameter count. It now states that the parameter count references were aligned with the protocol specification.README.md— the introduction namespip install sovpas the normal installation route and keeps the editable clone for development.sovp/core.py—verify_identity()'scheck_timestamppath now readsfreshness.createdfirst and falls back to the unsignedintegrity_proof.createdonly for documents whose@contextpredates v2.0; this fallback affects timestamp freshness checking only, never signature verification.README.md— schema example and "Replay protection" table row updated to the v2.0/freshnessshape; added an explicit callout that the tamper-resistance guarantee is new as of v2.0 and did not hold under v1.4.README.md—contentAddressexample corrected from the stale prefixed"sha256:<hex>"single-field form to the shapegenerate_identity_document()actually ships:{"alg": "sha256", "digest": "<hex>"}.README.md— removed theparameters(entropy_threshold/determinism_score) field from the schema example; it does not exist in any reference implementation (sovp-python,sovp-engine, or the Cloudflare Worker) and was never anything other than an illustrative artifact. Found while reconcilingdraft-litzki-sovp-04against the running implementations — the draft's own schema example had the same field, removed there too.sovp/core.py— Draft 04 host binding is enforced throughexpected_host; v2.0 freshness remains signed and expiry is always validated. Generated DNS key references use the URL hostname, excluding ports and normalizing trailing dots and case.sovp/resolver.py— DNS TXT parsing now accepts only the exactv=SOVP1; k=<base64>record shape and requires a valid 32-byte Ed25519 public key. Legacy endpoint fallback remains restricted to HTTP 404.examples/— live validation, end-to-end verification, and the ARD trustManifest example now identify Draft 04 explicitly and demonstrate v2.0 host binding and the currentcontentAddressshape.workers/sovp-identity/— the reference worker now serves a publisher-supplied, pre-signed Draft 04 v2.0 document from a Cloudflare secret instead of embedding a stale v1.4 artifact.
Fixed
tests/test_vectors.py—contentAddress.digestin Test Vector Set 2 was wrong. The vector shipped33465a2b…3ae2b5; the correct value oversha256(JCS(signed scope))isd00c6db5…30b460. No test read the field, so the vector and the specification drifted apart unnoticed.sovp/core.py—verify_identity()no longer fails open whenintegrity_proof.createdis missing; withcheck_timestamp=Truea missingcreatedis now a rejection (Psi_core = 0), closing a replay bypass of draft Section 7.2sovp/core.py—verify_identity()no longer raisesAttributeErrorwhenintegrity_proofis a non-dict value (attacker-controlled since it's excluded from the signed payload); now returnsFalseper the documentedbool-only contractsovp/resolver.py—fetch_identity_document()usedresp.json()(==json.loads()) to parse a remote, not-yet-signature-checked document, with no size, depth, or duplicate-key limit. Now parses viaparse_unverified_sovp_document().
Tests
tests/test_vectors.py— two tests now recomputecontentAddress.digestfrom Test Vector Set 2 itself and assert that a change inside the signed scope changes the recomputed digest. This keeps the vector and the specification from drifting apart again.- Added Draft 04 conformance coverage for freshness clock-skew boundaries, the 600-second issuance window, signed unknown top-level members, unsigned
contentAddressandscanextensions, host normalization, URL ports, and exact DNS TXT syntax.