norcaster-verify 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- norcaster_verify-0.4.0/.gitignore +7 -0
- norcaster_verify-0.4.0/CHANGELOG.md +83 -0
- norcaster_verify-0.4.0/LICENSE +202 -0
- norcaster_verify-0.4.0/PKG-INFO +205 -0
- norcaster_verify-0.4.0/README.md +187 -0
- norcaster_verify-0.4.0/SECURITY.md +7 -0
- norcaster_verify-0.4.0/docs/algorithm.md +46 -0
- norcaster_verify-0.4.0/examples/audit-bundle.json +102 -0
- norcaster_verify-0.4.0/pyproject.toml +36 -0
- norcaster_verify-0.4.0/spec/REVIEW.md +88 -0
- norcaster_verify-0.4.0/spec/shared-record-v0.md +463 -0
- norcaster_verify-0.4.0/spec/vectors/acknowledged-on-time-by-log.json +412 -0
- norcaster_verify-0.4.0/spec/vectors/acknowledgement-blocked.json +376 -0
- norcaster_verify-0.4.0/spec/vectors/acknowledgement-mismatch.json +376 -0
- norcaster_verify-0.4.0/spec/vectors/broken-log-consistency.json +392 -0
- norcaster_verify-0.4.0/spec/vectors/conflicting-acknowledgements.json +409 -0
- norcaster_verify-0.4.0/spec/vectors/duplicate-registration.json +384 -0
- norcaster_verify-0.4.0/spec/vectors/expired-key-document.json +335 -0
- norcaster_verify-0.4.0/spec/vectors/key-document-unlogged-dispute.json +391 -0
- norcaster_verify-0.4.0/spec/vectors/key-document-unlogged.json +383 -0
- norcaster_verify-0.4.0/spec/vectors/key-id-pem-mismatch.json +376 -0
- norcaster_verify-0.4.0/spec/vectors/missing-acknowledgement-registered-early.json +370 -0
- norcaster_verify-0.4.0/spec/vectors/missing-acknowledgement.json +372 -0
- norcaster_verify-0.4.0/spec/vectors/negative/acknowledgement-names-other-key.json +130 -0
- norcaster_verify-0.4.0/spec/vectors/negative/cbor-bom-issuer.json +125 -0
- norcaster_verify-0.4.0/spec/vectors/negative/cbor-bool-map-key.json +125 -0
- norcaster_verify-0.4.0/spec/vectors/negative/cbor-deep-nesting.json +125 -0
- norcaster_verify-0.4.0/spec/vectors/negative/cbor-kid-not-ascii.json +125 -0
- norcaster_verify-0.4.0/spec/vectors/negative/cbor-null-map-key.json +125 -0
- norcaster_verify-0.4.0/spec/vectors/negative/checkpoint-in-entries.json +136 -0
- norcaster_verify-0.4.0/spec/vectors/negative/checkpoint-root-uppercase.json +115 -0
- norcaster_verify-0.4.0/spec/vectors/negative/consistency-path-bad-hex.json +120 -0
- norcaster_verify-0.4.0/spec/vectors/negative/consistency-tree-size-not-integer.json +74 -0
- norcaster_verify-0.4.0/spec/vectors/negative/content-alg-not-hmac.json +74 -0
- norcaster_verify-0.4.0/spec/vectors/negative/content-check-not-delivered-but-delivered.json +130 -0
- norcaster_verify-0.4.0/spec/vectors/negative/content-check-unknown.json +131 -0
- norcaster_verify-0.4.0/spec/vectors/negative/content-not-base64.json +125 -0
- norcaster_verify-0.4.0/spec/vectors/negative/delivered-null.json +74 -0
- norcaster_verify-0.4.0/spec/vectors/negative/delivered-zero.json +74 -0
- norcaster_verify-0.4.0/spec/vectors/negative/json-bom.json +34 -0
- norcaster_verify-0.4.0/spec/vectors/negative/json-duplicate-member.json +34 -0
- norcaster_verify-0.4.0/spec/vectors/negative/json-exponent-token.json +34 -0
- norcaster_verify-0.4.0/spec/vectors/negative/json-float-token.json +34 -0
- norcaster_verify-0.4.0/spec/vectors/negative/json-infinity-token.json +34 -0
- norcaster_verify-0.4.0/spec/vectors/negative/json-lone-surrogate.json +34 -0
- norcaster_verify-0.4.0/spec/vectors/negative/json-nan-token.json +34 -0
- norcaster_verify-0.4.0/spec/vectors/negative/json-not-an-object.json +34 -0
- norcaster_verify-0.4.0/spec/vectors/negative/json-proto-member.json +90 -0
- norcaster_verify-0.4.0/spec/vectors/negative/json-too-deep.json +34 -0
- norcaster_verify-0.4.0/spec/vectors/negative/json-unsafe-integer.json +34 -0
- norcaster_verify-0.4.0/spec/vectors/negative/log-receipt-proof-not-bytes.json +125 -0
- norcaster_verify-0.4.0/spec/vectors/negative/log-receipt-proofs-not-a-map.json +125 -0
- norcaster_verify-0.4.0/spec/vectors/negative/log-receipt-tree-size-huge.json +125 -0
- norcaster_verify-0.4.0/spec/vectors/negative/missing-acknowledgement-names-other-key.json +130 -0
- norcaster_verify-0.4.0/spec/vectors/negative/nested-unknown-member.json +74 -0
- norcaster_verify-0.4.0/spec/vectors/negative/org-id-null.json +120 -0
- norcaster_verify-0.4.0/spec/vectors/negative/salt-short.json +125 -0
- norcaster_verify-0.4.0/spec/vectors/negative/salt-uppercase.json +125 -0
- norcaster_verify-0.4.0/spec/vectors/negative/salt-with-spaces.json +125 -0
- norcaster_verify-0.4.0/spec/vectors/negative/sequence-true.json +74 -0
- norcaster_verify-0.4.0/spec/vectors/negative/signature-value-trailing-garbage.json +74 -0
- norcaster_verify-0.4.0/spec/vectors/negative/signature-value-uppercase.json +74 -0
- norcaster_verify-0.4.0/spec/vectors/negative/statement-hex-uppercase.json +74 -0
- norcaster_verify-0.4.0/spec/vectors/negative/statement-hex-with-spaces.json +74 -0
- norcaster_verify-0.4.0/spec/vectors/negative/timestamp-fullwidth-digits.json +74 -0
- norcaster_verify-0.4.0/spec/vectors/negative/timestamp-month-13.json +74 -0
- norcaster_verify-0.4.0/spec/vectors/negative/timestamp-trailing-newline.json +74 -0
- norcaster_verify-0.4.0/spec/vectors/negative/timestamp-year-0050.json +125 -0
- norcaster_verify-0.4.0/spec/vectors/negative/trust-duplicate-member.json +34 -0
- norcaster_verify-0.4.0/spec/vectors/negative/trust-quorum-negative.json +34 -0
- norcaster_verify-0.4.0/spec/vectors/negative/trust-quorum-too-large.json +34 -0
- norcaster_verify-0.4.0/spec/vectors/negative/trust-quorum-true.json +34 -0
- norcaster_verify-0.4.0/spec/vectors/negative/unknown-entry-type.json +136 -0
- norcaster_verify-0.4.0/spec/vectors/negative/version-true.json +120 -0
- norcaster_verify-0.4.0/spec/vectors/negative/witness-id-not-a-string.json +125 -0
- norcaster_verify-0.4.0/spec/vectors/revocation-omitted-dispute.json +350 -0
- norcaster_verify-0.4.0/spec/vectors/revocation-omitted.json +339 -0
- norcaster_verify-0.4.0/spec/vectors/revoked-key-backdated.json +393 -0
- norcaster_verify-0.4.0/spec/vectors/revoked-key.json +392 -0
- norcaster_verify-0.4.0/spec/vectors/root-rotation-signed-by-new-root.json +402 -0
- norcaster_verify-0.4.0/spec/vectors/root-rotation.json +412 -0
- norcaster_verify-0.4.0/spec/vectors/sequence-fork.json +444 -0
- norcaster_verify-0.4.0/spec/vectors/sequence-gap.json +442 -0
- norcaster_verify-0.4.0/spec/vectors/tampered-content-hash.json +336 -0
- norcaster_verify-0.4.0/spec/vectors/valid-acknowledgement.json +378 -0
- norcaster_verify-0.4.0/spec/vectors/valid-receipt.json +342 -0
- norcaster_verify-0.4.0/spec/vectors/witness-key-counted-once.json +330 -0
- norcaster_verify-0.4.0/spec/vectors/wrong-key.json +336 -0
- norcaster_verify-0.4.0/src/norcaster_verify/__init__.py +16 -0
- norcaster_verify-0.4.0/src/norcaster_verify/audit_bundle_verify_lib.py +737 -0
- norcaster_verify-0.4.0/src/norcaster_verify/cli.py +9 -0
- norcaster_verify-0.4.0/src/norcaster_verify/shared_record/__init__.py +28 -0
- norcaster_verify-0.4.0/src/norcaster_verify/shared_record/__main__.py +80 -0
- norcaster_verify-0.4.0/src/norcaster_verify/shared_record/canonical.py +117 -0
- norcaster_verify-0.4.0/src/norcaster_verify/shared_record/cbor.py +146 -0
- norcaster_verify-0.4.0/src/norcaster_verify/shared_record/cose.py +227 -0
- norcaster_verify-0.4.0/src/norcaster_verify/shared_record/es256.py +127 -0
- norcaster_verify-0.4.0/src/norcaster_verify/shared_record/merkle.py +136 -0
- norcaster_verify-0.4.0/src/norcaster_verify/shared_record/objects.py +407 -0
- norcaster_verify-0.4.0/src/norcaster_verify/shared_record/strict_json.py +116 -0
- norcaster_verify-0.4.0/src/norcaster_verify/shared_record/verify.py +870 -0
- norcaster_verify-0.4.0/src/norcaster_verify/verify_audit_bundle.py +108 -0
- norcaster_verify-0.4.0/test/test_package.py +81 -0
- norcaster_verify-0.4.0/test/test_readme_examples.py +107 -0
- norcaster_verify-0.4.0/test/test_shared_record.py +296 -0
- norcaster_verify-0.4.0/test/test_shared_record_fuzz.py +121 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Versions apply to both packages, `norcaster-verify` (PyPI) and `@norcaster/verify` (npm). The
|
|
4
|
+
top entry must match the release tag, `pyproject.toml` and `package.json`; the release workflow
|
|
5
|
+
checks it.
|
|
6
|
+
|
|
7
|
+
## 0.4.0 — first public release
|
|
8
|
+
|
|
9
|
+
The first version published to PyPI and npm. It carries everything prepared as 0.3.0 (below,
|
|
10
|
+
never released) and fixes from an internal pre-review of the Shared Record protocol and its
|
|
11
|
+
verifiers (findings F-01 to F-11; an AI-assisted review by Norcaster, not the independent outside
|
|
12
|
+
review). The protocol stays `shared-record/0`: no object format, digest or signature input
|
|
13
|
+
changed. Verification rules did, and evidence that passed the unreleased 0.3.0 rules can fail or
|
|
14
|
+
skip here; spec §16 lists each rule change.
|
|
15
|
+
|
|
16
|
+
- **Revocation by log order (F-01).** For an object with a witnessed log position, a key revoked
|
|
17
|
+
by a key-document version registered before it is invalid whatever the object's `created_at`
|
|
18
|
+
says. `revoked_at` decides only for objects without a log position.
|
|
19
|
+
- **One key per key id (F-03).** A key document is invalid when an entry's `key_id` is not the key
|
|
20
|
+
id of its PEM, when a version lists a key id twice, when a key id maps to another PEM than in an
|
|
21
|
+
earlier version, or when a later version undoes a revocation.
|
|
22
|
+
- **Only witnessed positions order anything; several registrations (F-04).** Leaf indexes count
|
|
23
|
+
only from registrations a witnessed checkpoint covers. Each registration of an object is
|
|
24
|
+
verified and the earliest witnessed one is its position; identity is the object digest.
|
|
25
|
+
- **No silent timestamp fallback; dispute profile (F-02, verifier side).** A logged object whose
|
|
26
|
+
organisation has a key-document version without a witnessed position gets `sender_key` /
|
|
27
|
+
`sender_agent` SKIP ("key history not fully logged"). Timestamps order only objects with no log
|
|
28
|
+
position, and the detail says so. `--profile dispute` (Python and Node) requires every logged
|
|
29
|
+
check to PASS (SKIP counts as FAIL) and adds `key_documents[org].complete` (SKIP: v0 evidence
|
|
30
|
+
cannot show the key history is complete). Reports carry `profile` and `evidence_complete_for`.
|
|
31
|
+
- **Identical reports, no crashes (F-05, F-11).** Both verifiers read evidence and trust as text
|
|
32
|
+
under a strict JSON profile (spec §4.1): no number token with a fraction or exponent, no
|
|
33
|
+
`NaN`/`Infinity`, integers within ±(2^53 − 1), no duplicate member names, no unpaired
|
|
34
|
+
surrogates, at most 64 levels deep. New entry points `verify_evidence_text` (Python) and
|
|
35
|
+
`verifyEvidenceText` (Node); the CLIs use them and exit 2 on unusable input. Every object and
|
|
36
|
+
nested member is closed and typed; hex is lowercase only; timestamps are ASCII digits and real
|
|
37
|
+
dates in both; CBOR rejects map keys other than integers and text and nesting deeper than 16;
|
|
38
|
+
a protected header text starting with U+FEFF and a non-ASCII `kid` are refused. Nothing an
|
|
39
|
+
input contains makes either verifier raise.
|
|
40
|
+
- **Acknowledgements (F-06).** Lateness is decided by the acknowledgement's log registration time
|
|
41
|
+
when it has one; a missing-acknowledgement record registered more than 300 s before the
|
|
42
|
+
deadline (log time) fails. Different acknowledgements (or missing-acknowledgement records) for
|
|
43
|
+
one receipt fail as conflicting. `content_check` is validated and `not_delivered` must match
|
|
44
|
+
`delivered: false`. New states `acknowledged_mismatch` and `acknowledged_blocked`; each exchange
|
|
45
|
+
has a `state_detail`.
|
|
46
|
+
- **Sequence (F-07).** Two receipts with the same pair and sequence fail as a fork; a new `pairs`
|
|
47
|
+
section reports, per organisation pair, the numbers missing from the evidence.
|
|
48
|
+
- **Validation (F-09, F-10).** `expires_at` is enforced (a key document is not in effect for
|
|
49
|
+
objects created or registered after it); revealed salts are exactly 32 bytes; acknowledgements'
|
|
50
|
+
`receiver.key_id` and missing-acknowledgement records' `recorded_by.key_id` must be the signing
|
|
51
|
+
key; unknown entry types fail. Quorum counts distinct witness keys; a trust configuration's
|
|
52
|
+
quorum must be from 1 to the number of witnesses.
|
|
53
|
+
- **Report.** New sections `input` (where `format` now appears, with `trust`, `entries`,
|
|
54
|
+
`entry[i]`, `revealed`) and `pairs`; the check `receipt_fields` is gone (a malformed object is an
|
|
55
|
+
`entry[i]` FAIL and takes no further part). The report format stays
|
|
56
|
+
`norcaster.shared-record.report/0`.
|
|
57
|
+
- **Vectors.** 25 vectors in `spec/vectors/` (16 new), each with its profile and its full report,
|
|
58
|
+
and 52 negative vectors in `spec/vectors/negative/` (malformed input, as text). Python and Node
|
|
59
|
+
must give byte-identical reports (canonical JSON) on all of them; a seeded differential fuzz
|
|
60
|
+
test compares the two on mutated vectors.
|
|
61
|
+
|
|
62
|
+
## 0.3.0 — prepared, never released
|
|
63
|
+
|
|
64
|
+
- **Shared Record verifier (draft protocol, in development).** Offline verification of Shared
|
|
65
|
+
Record evidence and dispute exports: key-document chains from a pinned root (including root
|
|
66
|
+
rotation signed by the old root), signing-key validity and revocation by log order, receipt,
|
|
67
|
+
acknowledgement and missing-acknowledgement signatures, content commitments for revealed
|
|
68
|
+
exchanges, RFC 9162 log inclusion and consistency, checkpoint signatures and witness quorum.
|
|
69
|
+
Python: `python -m norcaster_verify.shared_record verify EVIDENCE --trust TRUST`. Node:
|
|
70
|
+
`norcaster-verify-record EVIDENCE --trust TRUST`. Both give the same report on every vector.
|
|
71
|
+
- **Protocol draft and review material:** `spec/shared-record-v0.md`, the nine test vectors in
|
|
72
|
+
`spec/vectors/` with their expected results, and `spec/REVIEW.md`, the brief for an outside
|
|
73
|
+
review.
|
|
74
|
+
- **Audit bundles:** org-scoped decisions (no deployment) resolve their `org_policy_version`
|
|
75
|
+
against `org_policy_version_snapshots`.
|
|
76
|
+
- **Examples:** `examples/audit-bundle.json`, a synthetic audit bundle for the README.
|
|
77
|
+
- No tag was pushed and nothing was uploaded; 0.4.0 supersedes it.
|
|
78
|
+
|
|
79
|
+
## 0.2.0 — prepared, never released
|
|
80
|
+
|
|
81
|
+
- Audit-bundle verifier (`norcaster-verify`) and trace integrity proof verifier
|
|
82
|
+
(`norcaster-verify-proof`), prepared for the public repository and PyPI. No tag was pushed and
|
|
83
|
+
nothing was uploaded; 0.3.0 supersedes it.
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: norcaster-verify
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Standalone verifier for Norcaster audit bundles: manifest content digest, integrity references, and public Rekor transparency-log anchors. Verifies evidence without a Norcaster account.
|
|
5
|
+
Project-URL: Homepage, https://www.norcaster.com
|
|
6
|
+
Project-URL: Documentation, https://www.norcaster.com/trust/integrity/algorithm
|
|
7
|
+
Project-URL: Repository, https://github.com/norcaster/verify
|
|
8
|
+
Project-URL: Issues, https://github.com/norcaster/verify/issues
|
|
9
|
+
Author-email: "Norcaster (Navego AB)" <security@norcaster.com>
|
|
10
|
+
License: Apache-2.0
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ai-governance,audit,eu-ai-act,evidence,rekor,soc2,verification
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Requires-Dist: httpx>=0.24
|
|
15
|
+
Provides-Extra: signatures
|
|
16
|
+
Requires-Dist: cryptography>=42; extra == 'signatures'
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# norcaster-verify
|
|
20
|
+
|
|
21
|
+
Independent verification tooling for [Norcaster](https://www.norcaster.com) evidence. It verifies
|
|
22
|
+
without a Norcaster account, without API access and without trusting Norcaster. Everything below
|
|
23
|
+
runs offline.
|
|
24
|
+
|
|
25
|
+
Two uses:
|
|
26
|
+
|
|
27
|
+
1. **Verify an audit bundle**: the evidence package Norcaster exports for SOC 2 examinations and
|
|
28
|
+
EU AI Act readiness reviews (Python, `norcaster-verify` on PyPI).
|
|
29
|
+
2. **Verify a Shared Record exchange or dispute export**: what two companies' agents exchanged,
|
|
30
|
+
signed by both and logged as hashes (Python or Node; the protocol is a draft, see below).
|
|
31
|
+
|
|
32
|
+
The examples run from a clone of this repository, against files committed in it:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
git clone https://github.com/norcaster/verify.git && cd verify
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 1. Verify an audit bundle
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install norcaster-verify
|
|
42
|
+
norcaster-verify examples/audit-bundle.json --offline
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`examples/audit-bundle.json` is a small synthetic bundle. Expected result: `PASS`, with the
|
|
46
|
+
content digest, governing configuration and approval provenance checks passing, and the trace
|
|
47
|
+
chain snapshot and public Rekor anchors reported as `SKIP` because the example carries none. A
|
|
48
|
+
SKIP is never a PASS.
|
|
49
|
+
|
|
50
|
+
Change one recorded decision and verify again:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
python -c "import json; b = json.load(open('examples/audit-bundle.json')); b['sections']['enforcement_records'][2]['outcome'] = 'allowed'; json.dump(b, open('tampered-bundle.json', 'w'))"
|
|
54
|
+
norcaster-verify tampered-bundle.json --offline
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Expected result: `FAIL` on the bundle content digest, exit code 1.
|
|
58
|
+
|
|
59
|
+
For a real bundle, run `norcaster-verify bundle.json` (online, it looks up the Rekor anchors),
|
|
60
|
+
`--offline` (it uses the Rekor snapshots embedded in the bundle) or `--json` (a machine-readable
|
|
61
|
+
report). Exit code 0 means no check failed.
|
|
62
|
+
|
|
63
|
+
What it checks:
|
|
64
|
+
|
|
65
|
+
- the manifest's content digest;
|
|
66
|
+
- the internal integrity references;
|
|
67
|
+
- the public Sigstore Rekor anchors;
|
|
68
|
+
- **governing-configuration consistency**: every enforcement decision's
|
|
69
|
+
`deployment_policy_version` / `policy_content_hash` resolves to a policy snapshot in the bundle
|
|
70
|
+
whose hash recomputes. Org-scoped decisions, which have no deployment, resolve their
|
|
71
|
+
`org_policy_version` against `org_policy_version_snapshots`;
|
|
72
|
+
- **approval provenance**: policy versions produced by a config proposal name an independent
|
|
73
|
+
approver and appear in `change_history`.
|
|
74
|
+
|
|
75
|
+
For anchors written by a signing-enabled chain it also checks that the Rekor entry embeds the
|
|
76
|
+
tenant's public key named in the manifest and a valid ES256 signature over the segment root. That
|
|
77
|
+
needs `pip install "norcaster-verify[signatures]"`; without `cryptography` that part reports
|
|
78
|
+
SKIP, not PASS.
|
|
79
|
+
|
|
80
|
+
The same two files (`verify_audit_bundle.py`, `audit_bundle_verify_lib.py`) ship inside every
|
|
81
|
+
Norcaster audit-bundle zip. This package and the bundled copies are interchangeable, and a test
|
|
82
|
+
enforces that they are identical byte for byte.
|
|
83
|
+
|
|
84
|
+
## 2. Verify a Shared Record exchange or dispute export
|
|
85
|
+
|
|
86
|
+
A Shared Record export is one JSON evidence file: the signed receipts, acknowledgements and
|
|
87
|
+
missing-acknowledgement records for the exchanges in question, their log proofs, a witnessed
|
|
88
|
+
checkpoint, both organisations' key documents and, for exchanges a party chose to reveal, the
|
|
89
|
+
content and salt. You verify it against **your own** trust file: the root fingerprints you pinned
|
|
90
|
+
for each organisation and the log and witness keys you trust.
|
|
91
|
+
|
|
92
|
+
Each test vector in `spec/vectors/` holds both, plus the expected result. Split one into the two
|
|
93
|
+
files the verifier takes:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
python -c "import json; v = json.load(open('spec/vectors/valid-acknowledgement.json')); json.dump(v['evidence'], open('evidence.json', 'w')); json.dump(v['trust'], open('trust.json', 'w'))"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Python:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
pip install "norcaster-verify[signatures]"
|
|
103
|
+
python -m norcaster_verify.shared_record verify evidence.json --trust trust.json
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Node (18 or later), the same checks:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npx --yes --package @norcaster/verify norcaster-verify-record evidence.json --trust trust.json
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Expected result: the exchange `state=acknowledged`, `Result: no check failed` and exit code 0.
|
|
113
|
+
Every check passes except two that do not apply here and say so as `SKIP`: there is no
|
|
114
|
+
missing-acknowledgement record, and the evidence holds one checkpoint, so there is no earlier one
|
|
115
|
+
to prove consistency with. The receipt's content commitment is checked against the revealed
|
|
116
|
+
content and salt (`content_commitment: revealed content and salt match the commitment`).
|
|
117
|
+
|
|
118
|
+
Now a copy whose receipt was changed after it was signed:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
python -c "import json; v = json.load(open('spec/vectors/tampered-content-hash.json')); json.dump(v['evidence'], open('evidence.json', 'w')); json.dump(v['trust'], open('trust.json', 'w'))"
|
|
122
|
+
python -m norcaster_verify.shared_record verify evidence.json --trust trust.json
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Expected result: `FAIL` on the receipt signature, the content commitment and the receipt's log
|
|
126
|
+
inclusion, and exit code 1. Add `--json` to either command for a machine-readable report. Without
|
|
127
|
+
the `signatures` extra the Python verifier reports signature checks as SKIP, never PASS; Node
|
|
128
|
+
always checks them. Python and Node give the same report, byte for byte, on every input.
|
|
129
|
+
|
|
130
|
+
**Profiles.** The default, `--profile basic`, reports what the evidence shows. For a dispute, add
|
|
131
|
+
`--profile dispute`: every log check (receipts, acknowledgements, missing-acknowledgement records,
|
|
132
|
+
key-document versions and root rotations) must then PASS, and a SKIP counts as a FAIL. Either way
|
|
133
|
+
the report says, per organisation (`evidence_complete_for`), that the result assumes the export
|
|
134
|
+
holds the organisation's complete key history, which v0 evidence cannot show:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
python -m norcaster_verify.shared_record verify evidence.json --trust trust.json --profile dispute
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Input.** Both verifiers read the evidence and trust files as text under a strict JSON profile
|
|
141
|
+
(spec §4.1): no number with a fraction or an exponent, no `NaN`, no duplicate member names. A file
|
|
142
|
+
outside it is unusable input: the report's `input` section says why, and the exit code is 2. From
|
|
143
|
+
code, use `verify_evidence_text(evidence_text, trust_text, profile="basic")` in Python or
|
|
144
|
+
`verifyEvidenceText(evidenceText, trustText, { profile })` in Node; `verify_evidence` and
|
|
145
|
+
`verifyEvidence` take parsed JSON, which can no longer show those mistakes.
|
|
146
|
+
|
|
147
|
+
The 25 vectors and what each must give are listed in `spec/vectors/`, and 52 negative vectors
|
|
148
|
+
(malformed input, as text) in `spec/vectors/negative/`. An export from a Norcaster organisation
|
|
149
|
+
(`POST /orgs/{org}/shared-record/dispute-exports`) or from the self-hosted counterparty component
|
|
150
|
+
(`norcaster-counterparty export`) verifies the same way.
|
|
151
|
+
|
|
152
|
+
### Shared Record status
|
|
153
|
+
|
|
154
|
+
In development · pilot pairs by application. A pilot version is built and in testing. It is not
|
|
155
|
+
in production or available to customers.
|
|
156
|
+
|
|
157
|
+
`spec/shared-record-v0.md` is a **draft protocol, in development, published for review**. It is
|
|
158
|
+
designed on the IETF SCITT architecture (RFC 9943), using COSE hash envelopes (RFC 9995), COSE
|
|
159
|
+
Receipts (RFC 9942) and the RFC 9162 Merkle tree. It may change. The Python module
|
|
160
|
+
`norcaster_verify.shared_record` is its reference implementation and `lib/shared-record/` its
|
|
161
|
+
Node verifier. `spec/REVIEW.md` is the brief for an outside review of the protocol. The record
|
|
162
|
+
shows what each party signed and what the log recorded; it does not decide who is right.
|
|
163
|
+
|
|
164
|
+
## 3. Verify a trace integrity proof (Node)
|
|
165
|
+
|
|
166
|
+
Verifies a per-run **trace integrity proof bundle** (`norcaster-proof-<run_id>.json`): the local
|
|
167
|
+
hash-chain proof, the Merkle path, and one Rekor lookup to confirm the segment root against the
|
|
168
|
+
public transparency log. This one is online.
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
npx --yes --package @norcaster/verify norcaster-verify-proof --proof norcaster-proof-<run_id>.json
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
What it checks: the recorded chain-proof result carried in the bundle, the Merkle inclusion path
|
|
175
|
+
when one is present, the tenant-key (ES256) signature over the entry and, where the Merkle path
|
|
176
|
+
allows, over the segment root, and that the segment root and public key in the Rekor entry match
|
|
177
|
+
what the bundle carries. It does not recompute the full hash chain, and the Rekor URL comes from
|
|
178
|
+
the bundle itself. See `docs/algorithm.md` for exactly what each step proves. Unsigned
|
|
179
|
+
(version 1) bundles are reported as `unsigned`, never as verified. The npm package installs two
|
|
180
|
+
commands, `norcaster-verify-proof` and `norcaster-verify-record`, distinct from the Python
|
|
181
|
+
`norcaster-verify` command.
|
|
182
|
+
|
|
183
|
+
## What verification does not prove
|
|
184
|
+
|
|
185
|
+
Completeness of an AI estate, compliance with any framework, or authenticity of the export
|
|
186
|
+
source. Norcaster produces evidence; only a licensed CPA firm can attest under SOC 2, and EU AI
|
|
187
|
+
Act conformity determinations require qualified counsel. A Shared Record verification shows what
|
|
188
|
+
was signed and logged, not which party is right about a dispute.
|
|
189
|
+
|
|
190
|
+
## Source and releases
|
|
191
|
+
|
|
192
|
+
This repository is a published snapshot of `packages/norcaster-verify` in Norcaster's main
|
|
193
|
+
repository, which is the source of truth. The Python files here are byte-for-byte the ones that
|
|
194
|
+
ship inside every audit-bundle zip, and a test in the main repository enforces that. Open issues
|
|
195
|
+
here.
|
|
196
|
+
|
|
197
|
+
Releases are cut from version tags (`v0.4.0`). The `release.yml` workflow builds the Python sdist
|
|
198
|
+
and wheel and publishes them to PyPI, and publishes the npm package, using each registry's
|
|
199
|
+
trusted-publishing (OIDC) flow, with no long-lived tokens. The versions in `pyproject.toml` and
|
|
200
|
+
`package.json` and the top entry of `CHANGELOG.md` must all equal the tag. CI runs the Node and
|
|
201
|
+
Python tests on every push.
|
|
202
|
+
|
|
203
|
+
Security reports: see [SECURITY.md](SECURITY.md).
|
|
204
|
+
|
|
205
|
+
Apache-2.0 · security@norcaster.com
|