legion-foundation 0.1.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.
- legion_foundation-0.1.0/LICENSE +201 -0
- legion_foundation-0.1.0/NOTICE +23 -0
- legion_foundation-0.1.0/PKG-INFO +399 -0
- legion_foundation-0.1.0/README.md +374 -0
- legion_foundation-0.1.0/hatch_build.py +416 -0
- legion_foundation-0.1.0/pyproject.toml +77 -0
- legion_foundation-0.1.0/src/legion_foundation/__init__.py +88 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/jcs-vectors/input/arrays.json +8 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/jcs-vectors/input/french.json +6 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/jcs-vectors/input/structures.json +8 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/jcs-vectors/input/unicode.json +3 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/jcs-vectors/input/values.json +5 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/jcs-vectors/input/weird.json +11 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/jcs-vectors/output/arrays.json +1 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/jcs-vectors/output/french.json +1 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/jcs-vectors/output/structures.json +1 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/jcs-vectors/output/unicode.json +1 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/jcs-vectors/output/values.json +1 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/jcs-vectors/output/weird.json +1 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/receipt/chain.vectors.json +1119 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/syscall/authorize.vectors.json +3912 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/syscall/constants.json +7 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/syscall/mint.vectors.json +328 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/trust/weight.vectors.json +1460 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/verdict/canonicalization.vectors.json +131910 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/verdict/contentkey.vectors.json +93 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/verdict/ed25519-smallorder-blocklist.json +21 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/verdict/goodstanding.vectors.json +971 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/verdict/negative.vectors.json +228 -0
- legion_foundation-0.1.0/src/legion_foundation/_corpus/verdict/signing.vectors.json +360 -0
- legion_foundation-0.1.0/src/legion_foundation/conformance.py +2168 -0
- legion_foundation-0.1.0/src/legion_foundation/contentkey.py +129 -0
- legion_foundation-0.1.0/src/legion_foundation/corpus.sha256 +23 -0
- legion_foundation-0.1.0/src/legion_foundation/crypto.py +168 -0
- legion_foundation-0.1.0/src/legion_foundation/didkey.py +89 -0
- legion_foundation-0.1.0/src/legion_foundation/ed25519strict.py +130 -0
- legion_foundation-0.1.0/src/legion_foundation/jcs.py +123 -0
- legion_foundation-0.1.0/src/legion_foundation/py.typed +0 -0
- legion_foundation-0.1.0/src/legion_foundation/receipt.py +284 -0
- legion_foundation-0.1.0/src/legion_foundation/syscall.py +1095 -0
- legion_foundation-0.1.0/src/legion_foundation/trust.py +380 -0
- legion_foundation-0.1.0/src/legion_foundation/verdict.py +431 -0
- legion_foundation-0.1.0/tests/conformance.py +120 -0
- legion_foundation-0.1.0/tests/tamper_bundled_digest.py +233 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or Derivative
|
|
95
|
+
Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright 2026 Copia Wealth Studios Inc. (LEGION-OS) and Michael J. Sikorsky
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
legion-foundation
|
|
2
|
+
Copyright 2026 Copia Wealth Studios Inc. (LEGION-OS) and Michael J. Sikorsky
|
|
3
|
+
|
|
4
|
+
This product is licensed under the Apache License, Version 2.0 (see LICENSE).
|
|
5
|
+
|
|
6
|
+
The Apache-2.0 patent grant (§3) is intentional: adopters of the verdict spine
|
|
7
|
+
receive a patent license to the mechanism, so it is safe to build on. The family
|
|
8
|
+
monetizes the data and product layers, not by holding the primitive's patents
|
|
9
|
+
over the people who adopt it — "open mechanism, private data."
|
|
10
|
+
|
|
11
|
+
Third-party content
|
|
12
|
+
--------------------
|
|
13
|
+
This distribution bundles a conformance corpus at
|
|
14
|
+
`legion_foundation/_corpus/`. Twelve of its files, under
|
|
15
|
+
`legion_foundation/_corpus/jcs-vectors/`, are the official RFC 8785 (JSON
|
|
16
|
+
Canonicalization Scheme) test vectors from the `cyberphone/json-canonicalization`
|
|
17
|
+
project (Copyright 2018 Anders Rundgren, the RFC's author), licensed under the
|
|
18
|
+
Apache License, Version 2.0. They are redistributed here unmodified, as an
|
|
19
|
+
independent third-party conformance oracle this package's JCS implementation is
|
|
20
|
+
checked against.
|
|
21
|
+
|
|
22
|
+
All other files under `legion_foundation/_corpus/` (the `verdict/`, `syscall/`,
|
|
23
|
+
`trust/`, and `receipt/` vector files) are original to this project.
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: legion-foundation
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: The verdict spine in Python: RFC 8785 canonicalization, EdDSA/ES256/HS256 signing with kid-binds-alg, two-gate verify + certify_close, and UCAN-style capabilities. Gated by a language-neutral conformance corpus.
|
|
5
|
+
Author: Copia Wealth Studios Inc. (LEGION-OS), Michael J. Sikorsky
|
|
6
|
+
Maintainer-email: LEGION-OS Security <security@copiawealthstudios.com>
|
|
7
|
+
License-Expression: Apache-2.0
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
License-File: NOTICE
|
|
10
|
+
Keywords: canonicalization,capabilities,did-key,ed25519,es256,jcs,rfc8785,ucan
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Security :: Cryptography
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: cryptography>=42
|
|
23
|
+
Requires-Dist: rfc8785>=0.1.4
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# legion-foundation
|
|
27
|
+
|
|
28
|
+
The **verdict spine** in Python — an *independent* implementation of a frozen,
|
|
29
|
+
cross-language signing contract, with **no dependency on the TypeScript
|
|
30
|
+
reference binding**. It exists to prove the underlying wire format is
|
|
31
|
+
genuinely polyglot: this binding reproduces, byte-for-byte, the same pinned
|
|
32
|
+
canonical bytes and verifies the same pinned signatures that nine other
|
|
33
|
+
independent language bindings do, against one shared conformance corpus. The
|
|
34
|
+
corpus is the contract; if a binding can't reproduce a vector, it fails — the
|
|
35
|
+
tape is never edited to make a binding pass.
|
|
36
|
+
|
|
37
|
+
**Provenance boundary.** In-source `ADR-NNNN` / `PB-NNNN` markers scattered
|
|
38
|
+
through this codebase's comments refer to entries in the parent project's
|
|
39
|
+
private architectural decision record — they are not resolvable outside that
|
|
40
|
+
repository. The same applies to citations embedded inside the bundled
|
|
41
|
+
conformance corpus itself: several vector files carry a `spec` or `note` field
|
|
42
|
+
that names an internal document (`SIGNING-syscall.md`, `ADR-0012`, `ADR-0027`,
|
|
43
|
+
`ADR-0035`, ...) or contains internal review shorthand such as the literal
|
|
44
|
+
string `Generated — never hand-edit (drift-gated by pnpm corpus:drift)`. None
|
|
45
|
+
of these are resolvable outside the private repository either. The normative,
|
|
46
|
+
versioned contract for the wire formats this package implements is a document
|
|
47
|
+
called `SIGNING.md`, which currently also lives in that private repository and
|
|
48
|
+
is **not** distributed with this package. So: this README and the bundled
|
|
49
|
+
conformance corpus are, today, the complete publicly available description of
|
|
50
|
+
the wire format. Where a comment or a corpus `spec`/`note` field cites
|
|
51
|
+
`SIGNING.md §N` or another private document, treat the corpus vectors
|
|
52
|
+
themselves as the operative specification — they are the thing every binding
|
|
53
|
+
is actually gated against, independent of whether the cited document is
|
|
54
|
+
reachable.
|
|
55
|
+
|
|
56
|
+
Every signed message in this family follows the same construction:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
signed_bytes = utf8("<DOMAIN>\n") || JCS(projection)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
RFC 8785 JCS (JSON Canonicalization Scheme) over a null-stripped projection of
|
|
63
|
+
the message, prefixed with an off-wire domain-separation tag that never
|
|
64
|
+
appears in the message itself. This package implements several subsystems built
|
|
65
|
+
on that one mechanism, each with its own distinct DOMAIN tag — so a signature
|
|
66
|
+
for one can never validate as another:
|
|
67
|
+
|
|
68
|
+
- **verdict** — DOMAIN `legion.os/v/1`: `utf8("legion.os/v/1\n") || JCS(projection)`
|
|
69
|
+
- **syscall** — DOMAIN `legion.os/syscall/v/1`: `utf8("legion.os/syscall/v/1\n") || JCS(projection)`
|
|
70
|
+
- **trust** — DOMAIN `legion.os/trust/v/1`: `utf8("legion.os/trust/v/1\n") || JCS(projection)`
|
|
71
|
+
|
|
72
|
+
**The exported `DOMAIN` / `DOMAIN_SYSCALL` / `DOMAIN_TRUST` constants already
|
|
73
|
+
carry the trailing newline** — measured: `DOMAIN == "legion.os/v/1\n"`,
|
|
74
|
+
`DOMAIN_SYSCALL == "legion.os/syscall/v/1\n"`, `DOMAIN_TRUST ==
|
|
75
|
+
"legion.os/trust/v/1\n"`. The formula above is written with an explicit `\n`
|
|
76
|
+
only to show where the newline lives conceptually. In code, construct the
|
|
77
|
+
signed bytes as:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
signed_input = DOMAIN.encode("utf-8") + jcs(projection) # NOT DOMAIN.encode() + b"\n" + jcs(projection)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Appending a separate `\n` double-newlines the signed bytes and every signature
|
|
84
|
+
fails to verify.
|
|
85
|
+
|
|
86
|
+
## What it implements
|
|
87
|
+
|
|
88
|
+
The verdict spine is checked by two ordered gates, referred to below as
|
|
89
|
+
**GATE 1** and **GATE 2** — GATE 1 (`verify`) establishes authenticity and
|
|
90
|
+
well-formedness; GATE 2 (`certify_close`) then checks standing (expiry and
|
|
91
|
+
revocation) on an already-verified envelope. Both are `async def` functions —
|
|
92
|
+
see the calling convention under **`VerifyResult` / `CertifyResult` are
|
|
93
|
+
unions, not classes** below.
|
|
94
|
+
|
|
95
|
+
- **RFC 8785 JCS** (`jcs.py`) via the [`rfc8785`](https://pypi.org/project/rfc8785/)
|
|
96
|
+
package (maintained by Trail of Bits, itself adapted from the RFC author's
|
|
97
|
+
own reference implementation). Cross-checked in the conformance test against
|
|
98
|
+
the official cyberphone vectors (the independent oracle) **and** the pinned
|
|
99
|
+
canonical hex in the committed corpus.
|
|
100
|
+
- **GATE 1 — `verify(envelope, resolve_key)`** (`verdict.py`): kid-binds-alg
|
|
101
|
+
(the wire `alg` is never used to select the algorithm; the keystore's bound
|
|
102
|
+
alg is, and a mismatch is rejected), integrity over
|
|
103
|
+
`utf8("legion.os/v/1\n") || JCS(null-stripped projection)`, body_type↔body
|
|
104
|
+
disjointness, and a canonical-number guard. Returns the **canonical
|
|
105
|
+
projection**, not the raw input.
|
|
106
|
+
- **GATE 2 — `certify_close(verified, ...)`** (`verdict.py`): expiry +
|
|
107
|
+
`status_ref` → active/revoked/suspended/unknown (fail-closed),
|
|
108
|
+
`require_status`, expiry-checked-before-status.
|
|
109
|
+
- **Crypto** (`crypto.py`) via [`cryptography`](https://pypi.org/project/cryptography/):
|
|
110
|
+
Ed25519 (default), HMAC-SHA-256 (constant-time compare, single trust domain),
|
|
111
|
+
and ECDSA P-256/SHA-256.
|
|
112
|
+
- **`content_key(input)`** (`contentkey.py`): the Legion-defined DERIVED id,
|
|
113
|
+
`"sha256:" + base64url-UNPADDED(SHA-256(JCS(NFC-deep(input))))`. `nfc_deep`
|
|
114
|
+
recursively NFC-normalizes every string — object **keys and values** — via the
|
|
115
|
+
stdlib `unicodedata.normalize("NFC", …)` (native, Python's own UCD — not ICU),
|
|
116
|
+
then the existing `jcs` sorts the already-normalized keys. **Additive** — the
|
|
117
|
+
sign/verify path is untouched, so a composed/decomposed pair shares a
|
|
118
|
+
`content_key` but keeps **different** signed bytes (audit fidelity). An NFC key
|
|
119
|
+
collision **raises** (never merges); scope is **encoding only** (`"100"` !=
|
|
120
|
+
`100`, dates stay distinct). Apps get normalized identity for free and must
|
|
121
|
+
never hand-normalize. Gated against the project's committed conformance
|
|
122
|
+
vectors.
|
|
123
|
+
|
|
124
|
+
### The ES256 cross-language detail (the whole point)
|
|
125
|
+
|
|
126
|
+
SIGNING.md §4.1 fixes the on-wire ES256 signature as **raw `r‖s`, 64 bytes
|
|
127
|
+
(IEEE P1363)** — *not* ASN.1 DER. Python's `cryptography` ECDSA `verify`
|
|
128
|
+
expects DER, so this binding:
|
|
129
|
+
|
|
130
|
+
1. **rejects** any ES256 signature that is not exactly 64 bytes — a DER-encoded
|
|
131
|
+
signature presented on the wire (~70 bytes) is rejected (the
|
|
132
|
+
`es256_der_catcher` vector, `must_verify=false`); and
|
|
133
|
+
2. converts the 64-byte `r‖s` to DER via `encode_dss_signature` before handing
|
|
134
|
+
it to `cryptography`.
|
|
135
|
+
|
|
136
|
+
A binding that accepts DER on the wire is non-conformant.
|
|
137
|
+
|
|
138
|
+
## Import paths
|
|
139
|
+
|
|
140
|
+
`legion_foundation`'s top-level package re-exports the verdict-spine surface
|
|
141
|
+
directly:
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from legion_foundation import (
|
|
145
|
+
jcs, canonicalize, CanonicalizationLimitError,
|
|
146
|
+
MAX_CANON_DEPTH, MAX_CANON_NODES, MAX_CANON_SCALAR,
|
|
147
|
+
content_key, nfc, nfc_deep,
|
|
148
|
+
verify_bytes, sign_bytes, MintRefusedError,
|
|
149
|
+
ED25519_SMALLORDER_BLOCKLIST, ed25519_strict_profile_fails, es256_is_high_s,
|
|
150
|
+
b64u_decode, b64u_encode,
|
|
151
|
+
verify, certify_close,
|
|
152
|
+
VerifyResult, CertifyResult, VerifyingKey,
|
|
153
|
+
NonCanonicalNumber, assert_canonical_numbers,
|
|
154
|
+
well_formed_error, signed_view, is_alg, is_tier, is_credential, is_verdict,
|
|
155
|
+
ALLOWED_ALGS, BODY_TYPE_CREDENTIAL, BODY_TYPE_VERDICT, DOMAIN,
|
|
156
|
+
)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The `syscall`, `trust`, `receipt`, and `didkey` modules are **not** re-exported
|
|
160
|
+
at the top level — import them by their submodule path:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
from legion_foundation.syscall import authorize, mint_delegation, attenuate
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### `VerifyResult` / `CertifyResult` are unions, not classes
|
|
167
|
+
|
|
168
|
+
Both are `Union` type aliases over a `Literal`-tagged success/failure pair. That
|
|
169
|
+
tagging is what makes the canonical idiom narrow correctly — after `if r.ok:`, a
|
|
170
|
+
type checker knows `r.envelope` is a `dict` rather than `dict | None`.
|
|
171
|
+
|
|
172
|
+
**`verify` and `certify_close` are both `async def` coroutines** — calling
|
|
173
|
+
either returns a coroutine object, not a `VerifyResult`/`CertifyResult`, until
|
|
174
|
+
it is `await`ed. `await` is only legal inside an `async def` function, so the
|
|
175
|
+
canonical idiom is:
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
import asyncio
|
|
179
|
+
|
|
180
|
+
async def check(envelope, resolve_key):
|
|
181
|
+
r = await verify(envelope, resolve_key)
|
|
182
|
+
if r.ok:
|
|
183
|
+
issuer = r.envelope["issuer"] # narrows; no assert or cast needed
|
|
184
|
+
else:
|
|
185
|
+
log(r.reason)
|
|
186
|
+
|
|
187
|
+
asyncio.run(check(envelope, resolve_key))
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Calling `r = verify(envelope, resolve_key)` without `await` binds `r` to an
|
|
191
|
+
un-awaited coroutine: `r.ok` raises `AttributeError` (coroutines have no `.ok`
|
|
192
|
+
attribute), and Python additionally emits a `RuntimeWarning: coroutine … was
|
|
193
|
+
never awaited` because the verification never actually ran.
|
|
194
|
+
|
|
195
|
+
The practical consequence: `isinstance(r, VerifyResult)` works, but the alias is
|
|
196
|
+
**not callable** — `VerifyResult(...)` raises `TypeError`. Construct or match the
|
|
197
|
+
variants instead, which are exported for exactly that purpose:
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
from legion_foundation import VerifyOk, VerifyFail, CertifyOk, CertifyFail
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
This package ships a PEP 561 `py.typed` marker, so these shapes are authoritative
|
|
204
|
+
to `mypy` and `pyright`.
|
|
205
|
+
|
|
206
|
+
## Run the conformance gate
|
|
207
|
+
|
|
208
|
+
Requires **Python >= 3.11**. Runtime dependencies are `rfc8785` (RFC 8785 JCS)
|
|
209
|
+
and `cryptography` (Ed25519 / ECDSA P-256 / HMAC-SHA-256) — both installed
|
|
210
|
+
automatically by `pip`.
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
pip install legion-foundation
|
|
214
|
+
python -m legion_foundation.conformance # prints N/N; exits non-zero on any failure
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
That command is the one that actually works for a PyPI consumer: the
|
|
218
|
+
distribution bundles a pinned 23-file slice of the conformance corpus
|
|
219
|
+
(`jcs-vectors/`, `verdict/`, `syscall/`, `trust/`, `receipt/`) as package data,
|
|
220
|
+
so the gate can run against an **installed** copy with no repo checkout in
|
|
221
|
+
sight. It mirrors the TS binding's three angles against the bundled tape: (1)
|
|
222
|
+
JCS == official vectors == pinned hex, (2) `signed_input` reconstruction +
|
|
223
|
+
crypto-primitive verify/reject per `must_verify`, (3) the real `verify()` gate
|
|
224
|
+
agrees with `must_verify` (incl. the DER-catcher and the null-omission lenient
|
|
225
|
+
input), plus `certify_close` asserted by name.
|
|
226
|
+
|
|
227
|
+
Also run `python -m legion_foundation.conformance --self-test` to check the
|
|
228
|
+
gate's own failure mode rather than its happy path: it copies a real corpus
|
|
229
|
+
tree into a temp directory, mutates it three ways (deletes a whitelisted file,
|
|
230
|
+
truncates a vector array, points at an empty directory), and asserts that
|
|
231
|
+
*each* mutation makes the gate fail non-zero. A green `259/259` from a gate
|
|
232
|
+
that would also pass against a silently truncated tape is not evidence of
|
|
233
|
+
anything; `--self-test` is the check that it would not.
|
|
234
|
+
|
|
235
|
+
**Contributors working from a monorepo checkout do NOT run the same command
|
|
236
|
+
against the same tape.** `python -m legion_foundation.conformance` alone reads
|
|
237
|
+
whichever corpus resolved into the **installed** package at build time
|
|
238
|
+
(`mode: bundled`) — measured, in a clean venv from the repo root: `mode:
|
|
239
|
+
bundled`. The command that actually prefers the live, freshly regenerated
|
|
240
|
+
`corpus/` tree is the in-repo shim:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
python3 packages/foundation-py/tests/conformance.py
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
The shim prepends the repo's own `src/` tree to `PYTHONPATH` and relaunches the
|
|
247
|
+
gate in a fresh subprocess. That is the whole mechanism: the module the
|
|
248
|
+
subprocess imports **is** the repo's copy, so its own `__file__`-anchored
|
|
249
|
+
resolution — marker-anchored on `legion.manifest.json`, walking up from
|
|
250
|
+
`__file__`, never from `cwd` — naturally lands on `mode: repo` against the live
|
|
251
|
+
tree. Repo source against the repo tape, with no flag and no hand-off.
|
|
252
|
+
|
|
253
|
+
There is deliberately **no** command-line option to point the gate at an
|
|
254
|
+
arbitrary corpus. An earlier revision had one; it was removed rather than
|
|
255
|
+
hardened, because a flag that redirects the tape is indistinguishable from the
|
|
256
|
+
attack it enables — it let an installed copy verify an unpinned tree while
|
|
257
|
+
skipping the bundled digest check. Resolution is now a property of where the
|
|
258
|
+
imported module lives, which a caller cannot forge by passing an argument.
|
|
259
|
+
|
|
260
|
+
A bare `python -m legion_foundation.conformance` therefore resolves `mode: repo`
|
|
261
|
+
whenever the module it imports lives under a checkout (via `PYTHONPATH`, or an
|
|
262
|
+
editable install), and `mode: bundled` otherwise — which is the case for an
|
|
263
|
+
ordinary `pip install legion-foundation`.
|
|
264
|
+
|
|
265
|
+
Every run — through the shim or bare — prints a line naming which tape it
|
|
266
|
+
actually tested, so you never have to guess:
|
|
267
|
+
|
|
268
|
+
```
|
|
269
|
+
conformance gate resolution -> mode: bundled, root: <path>
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
or `mode: repo` when the module resolved a live checkout. Check that line before
|
|
273
|
+
trusting a green result to mean "the tape in this checkout," not "whatever was
|
|
274
|
+
staged into the wheel."
|
|
275
|
+
|
|
276
|
+
One caveat worth knowing, since this package hardens against it internally: a
|
|
277
|
+
bare `python -m <module>` puts the current working directory on `sys.path`, so
|
|
278
|
+
running the gate from a directory you do not control could import something
|
|
279
|
+
other than the installed package. That is ordinary CPython `-m` behaviour rather
|
|
280
|
+
than anything specific to this package, and the two subprocess relaunches this
|
|
281
|
+
package performs internally both set `PYTHONSAFEPATH=1` against it. If you are
|
|
282
|
+
running the gate somewhere untrusted, do the same — `PYTHONSAFEPATH=1 python -m
|
|
283
|
+
legion_foundation.conformance`.
|
|
284
|
+
|
|
285
|
+
**What a green run proves, and what it does not.** A passing run proves that
|
|
286
|
+
*this artifact* — this interpreter, this build of `cryptography`, this build
|
|
287
|
+
of OpenSSL — reproduces the pinned bytes and pinned signatures on *your*
|
|
288
|
+
machine. That is genuine environment-drift detection: it did not exist before
|
|
289
|
+
this package shipped its own corpus slice, and no other check available to a
|
|
290
|
+
PyPI consumer offers it. It does **not** prove cross-binding conformance —
|
|
291
|
+
that the Go, Rust, Zig, Swift, Java, Haskell, Elixir, C, and TypeScript
|
|
292
|
+
bindings agree with this one is established by a ten-job CI pipeline a
|
|
293
|
+
consumer of this package cannot see or rerun; running this gate is not a
|
|
294
|
+
substitute for that pipeline, and no claim to the contrary should be inferred
|
|
295
|
+
from a green result here.
|
|
296
|
+
|
|
297
|
+
**A green run also does not prove the artifact you installed is authentic.**
|
|
298
|
+
The gate ships *inside* the same distribution it tests: anyone who can replace
|
|
299
|
+
the contents of this package on your machine (a compromised mirror, a tampered
|
|
300
|
+
wheel, a supply-chain substitution) can replace the gate along with it, so a
|
|
301
|
+
self-test can never catch tampering to the artifact carrying it. That is a
|
|
302
|
+
property of every self-test, not something specific to this design. Verifying
|
|
303
|
+
that the bits you installed are the bits actually published requires
|
|
304
|
+
publish-side attestation — Sigstore / PEP 740 provenance — which this release
|
|
305
|
+
does not carry.
|
|
306
|
+
|
|
307
|
+
**The bundled corpus is deliberately NOT all non-forgeable, and only the
|
|
308
|
+
ES256 vectors are.** The JCS vectors are the **official third-party cyberphone
|
|
309
|
+
RFC 8785 test suite** — an oracle external to this project, not authored by
|
|
310
|
+
it, and untouched. The ES256 signing vectors publish a **public key with no
|
|
311
|
+
matching private key** bundled anywhere in this distribution, so verifying
|
|
312
|
+
those pinned signatures cannot be faked by regenerating both sides of the
|
|
313
|
+
check from a private key you also control. The EdDSA and HS256 portions of the
|
|
314
|
+
tape are different: the corpus deliberately publishes the raw Ed25519
|
|
315
|
+
`seed_hex` and the HS256 `secret_hex` used to mint those vectors — both are
|
|
316
|
+
byte-counting sequences (`000102...1f`), oracle material with no purpose
|
|
317
|
+
beyond reproducing this exact tape, securing nothing, and identical across
|
|
318
|
+
every language binding. If you run a secret scanner over your installed
|
|
319
|
+
`site-packages`, it **will** flag these as apparent private keys/secrets — they
|
|
320
|
+
are intentional, public, non-sensitive test fixtures, not a leaked credential.
|
|
321
|
+
|
|
322
|
+
## `syscall` — capabilities
|
|
323
|
+
|
|
324
|
+
`legion_foundation.syscall` is the Python binding for the second subsystem
|
|
325
|
+
(import it explicitly — see **Import paths** above). It **reuses this
|
|
326
|
+
package's mechanism** — `jcs` (RFC 8785), `crypto.verify_bytes` (Ed25519 via
|
|
327
|
+
`cryptography`), and `crypto.b64u_decode`/`b64u_encode` — and adds only the
|
|
328
|
+
syscall-specific parts: a **distinct** DOMAIN tag `legion.os/syscall/v/1` (so a
|
|
329
|
+
verdict signature can never verify as a syscall one — pinned by a cross-domain
|
|
330
|
+
test), and `did:key` Ed25519 principals (base58btc decoded by a
|
|
331
|
+
**dependency-free ~12-line decoder**, then strip the `0xed01` multicodec).
|
|
332
|
+
|
|
333
|
+
It also carries the UCAN `pol` evaluator — `==`/`!=`/`<`/`<=`/`>`/`>=`/`like`/
|
|
334
|
+
`not`/`and`/`or`/`all`/`any` over dotted+index selectors; empty policy = permit;
|
|
335
|
+
an unresolvable selector evaluates to `false` rather than raising; the
|
|
336
|
+
evaluator is finite by construction.
|
|
337
|
+
|
|
338
|
+
The **`authorize`** gate is the syscall analogue of `certify_close`, and is
|
|
339
|
+
fail-closed in a fixed order: gate 0 size/depth → gate 1 nonce-freshness
|
|
340
|
+
*first* (replay exits before any crypto) → verify → walk the proof chain →
|
|
341
|
+
root anchor. `authorize`'s exact refusal reasons are gated against the
|
|
342
|
+
project's committed conformance vectors.
|
|
343
|
+
|
|
344
|
+
**One caller-visible failure mode is not a return value.** If the opt-in
|
|
345
|
+
`require_root_pol` argument is present and is not a `Mapping` of selector →
|
|
346
|
+
JSON scalar, `authorize` raises `TypeError` — before gate 0, so ahead of every
|
|
347
|
+
refusal path. That is deliberate: it is caller-side API misuse, not folded
|
|
348
|
+
into a token refusal, because the `AuthorizeResult.reason` strings are
|
|
349
|
+
corpus-pinned and reporting a misconfiguration as a request refusal would send
|
|
350
|
+
whoever debugs it to inspect an innocent request instead. Pass `None` for "no
|
|
351
|
+
pin." The argument is snapshotted at entry, so mutating the mapping later in
|
|
352
|
+
the call — from an injected `resolve_prf`, say — cannot void the pin.
|
|
353
|
+
|
|
354
|
+
## `trust` — the recursion
|
|
355
|
+
|
|
356
|
+
`legion_foundation.trust` is the third subsystem's binding (import it
|
|
357
|
+
explicitly by submodule path). It answers a different question than `verdict`
|
|
358
|
+
or `syscall`: not "is this envelope authentic" but "how much should a verifier
|
|
359
|
+
be trusted, given its measured track record." DOMAIN `legion.os/trust/v/1`,
|
|
360
|
+
distinct from the other two. `weight_from_record` turns a track record (true
|
|
361
|
+
positives, false positives, false negatives, and the ground-truth base rate)
|
|
362
|
+
into a calibrated integer-PPM weight — deliberately **float-free**, ≈0 at cold
|
|
363
|
+
start (no graded verdicts yet), and ≈0 for a "fabricator" — a verifier whose
|
|
364
|
+
measured precision is at or below the base rate, i.e. no better than guessing
|
|
365
|
+
the prior and contributing no skill above chance. `mint_weight_verdict` signs
|
|
366
|
+
the resulting weight as a spine CREDENTIAL over `utf8(DOMAIN_TRUST) ||
|
|
367
|
+
JCS(projection)` — reusing the same verdict-spine shape and signing mechanism,
|
|
368
|
+
with only the DOMAIN tag distinct — and structurally refuses to mint a weight
|
|
369
|
+
for a SEMANTIC verifier (one with no answer key to measure against), since
|
|
370
|
+
there is no track record to calibrate against in that case.
|
|
371
|
+
|
|
372
|
+
## `receipt` — the closing leg
|
|
373
|
+
|
|
374
|
+
`legion_foundation.receipt` (import it explicitly by submodule path) wires up
|
|
375
|
+
the third leg of the UCAN triad — delegation → invocation → receipt — but
|
|
376
|
+
**mints no DOMAIN tag and no new wire primitive of its own.** It is pure
|
|
377
|
+
orchestration over the existing spine: an execution record is an ordinary
|
|
378
|
+
`verdict` **credential** (the executor's claim: "I ran invocation X, status S,
|
|
379
|
+
output digest D"), and a receipt is an ordinary `verdict` **verdict** envelope
|
|
380
|
+
— an independent party's signed appraisal *of* that execution record. The one
|
|
381
|
+
invariant `receipt` adds is that the receipt's signer must not be the
|
|
382
|
+
execution record's signer (verifier != executor); a receipt signed by the same
|
|
383
|
+
key that signed the execution record would merely notarize the executor's own
|
|
384
|
+
claim rather than independently confirm it, so `legion_foundation.receipt`
|
|
385
|
+
refuses to build one (`ProposerIsVerifierError`) and re-checks the same
|
|
386
|
+
condition at verify time.
|
|
387
|
+
|
|
388
|
+
## Security
|
|
389
|
+
|
|
390
|
+
This package implements a cryptographic signing and authorization system. The
|
|
391
|
+
source repository is private and has no public issue tracker, so please do
|
|
392
|
+
not open a public issue for a suspected vulnerability. Instead, report it
|
|
393
|
+
directly to:
|
|
394
|
+
|
|
395
|
+
security@copiawealthstudios.com
|
|
396
|
+
|
|
397
|
+
## License
|
|
398
|
+
|
|
399
|
+
Apache-2.0. This distribution includes the `LICENSE` and `NOTICE` files.
|