trustplane-auth-sdk 0.1.0rc1__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.
- trustplane_auth_sdk-0.1.0rc1/.github/workflows/pr.yml +43 -0
- trustplane_auth_sdk-0.1.0rc1/.github/workflows/release-pypi.yml +226 -0
- trustplane_auth_sdk-0.1.0rc1/.gitignore +9 -0
- trustplane_auth_sdk-0.1.0rc1/AGENTS.md +8 -0
- trustplane_auth_sdk-0.1.0rc1/LICENSE +51 -0
- trustplane_auth_sdk-0.1.0rc1/PKG-INFO +137 -0
- trustplane_auth_sdk-0.1.0rc1/README.md +110 -0
- trustplane_auth_sdk-0.1.0rc1/SECURITY.md +7 -0
- trustplane_auth_sdk-0.1.0rc1/docs/release-pypi.md +105 -0
- trustplane_auth_sdk-0.1.0rc1/pyproject.toml +61 -0
- trustplane_auth_sdk-0.1.0rc1/scripts/scan-leaks.sh +7 -0
- trustplane_auth_sdk-0.1.0rc1/scripts/scan-wording.sh +7 -0
- trustplane_auth_sdk-0.1.0rc1/scripts/smoke-wheel.sh +26 -0
- trustplane_auth_sdk-0.1.0rc1/src/trustplane_auth/__init__.py +49 -0
- trustplane_auth_sdk-0.1.0rc1/src/trustplane_auth/py.typed +1 -0
- trustplane_auth_sdk-0.1.0rc1/src/trustplane_auth/signing.py +274 -0
- trustplane_auth_sdk-0.1.0rc1/src/trustplane_auth/transcript.py +369 -0
- trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/README.md +11 -0
- trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/body-sha256-v1.json +18 -0
- trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/broker-ipc-v1.denied.json +90 -0
- trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/broker-ipc-v1.success.json +94 -0
- trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/bundle-v1.source-rules.json +97 -0
- trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/manifest.json +68 -0
- trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/passport-v1.minimal-valid-claims.json +35 -0
- trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/signer-taxonomy-v1.json +31 -0
- trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/transcript-v1.ambiguous-query-headers.json +109 -0
- trustplane_auth_sdk-0.1.0rc1/testdata/conformance/v1/transcript-v1.json +109 -0
- trustplane_auth_sdk-0.1.0rc1/tests/test_sdk.py +503 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: pr
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
- "feature/**"
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
checks:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
- uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
cache: pip
|
|
24
|
+
- name: Upgrade pip
|
|
25
|
+
run: python -m pip install -U pip
|
|
26
|
+
- name: Install package and dev dependencies
|
|
27
|
+
run: python -m pip install -e ".[dev]"
|
|
28
|
+
- name: Format check
|
|
29
|
+
run: ruff format --check .
|
|
30
|
+
- name: Lint
|
|
31
|
+
run: ruff check .
|
|
32
|
+
- name: Type check
|
|
33
|
+
run: mypy
|
|
34
|
+
- name: Tests
|
|
35
|
+
run: pytest
|
|
36
|
+
- name: Build sdist and wheel
|
|
37
|
+
run: python -m build
|
|
38
|
+
- name: Clean wheel install/import smoke
|
|
39
|
+
run: scripts/smoke-wheel.sh
|
|
40
|
+
- name: Leak scan
|
|
41
|
+
run: scripts/scan-leaks.sh
|
|
42
|
+
- name: Wording scan
|
|
43
|
+
run: scripts/scan-wording.sh
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
name: Manual PyPI Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
version:
|
|
7
|
+
description: TrustPlane release tag to prepare, for example v0.1.0-rc.1
|
|
8
|
+
required: true
|
|
9
|
+
type: string
|
|
10
|
+
publish_package:
|
|
11
|
+
description: Publish to PyPI, create the release commit, and push the annotated tag
|
|
12
|
+
required: true
|
|
13
|
+
default: false
|
|
14
|
+
type: boolean
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: write
|
|
18
|
+
id-token: write
|
|
19
|
+
|
|
20
|
+
env:
|
|
21
|
+
PACKAGE_NAME: trustplane-auth-sdk
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
release:
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
steps:
|
|
27
|
+
- name: Require main
|
|
28
|
+
run: |
|
|
29
|
+
if [ "${GITHUB_REF_NAME}" != "main" ]; then
|
|
30
|
+
echo "release workflow must run from main"
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
with:
|
|
35
|
+
fetch-depth: 0
|
|
36
|
+
- uses: actions/setup-python@v5
|
|
37
|
+
with:
|
|
38
|
+
python-version: "3.12"
|
|
39
|
+
cache: pip
|
|
40
|
+
- name: Upgrade pip
|
|
41
|
+
run: python -m pip install -U pip
|
|
42
|
+
- name: Install package and dev dependencies
|
|
43
|
+
run: python -m pip install -e ".[dev]"
|
|
44
|
+
- name: Configure release git identity
|
|
45
|
+
run: |
|
|
46
|
+
git config user.name "Medh Mesh"
|
|
47
|
+
git config user.email "maintainer@trustplane.dev"
|
|
48
|
+
- name: Validate version input
|
|
49
|
+
run: |
|
|
50
|
+
if ! printf '%s\n' "${{ inputs.version }}" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?$'; then
|
|
51
|
+
echo "version must look like v0.1.0 or v0.1.0-rc.1"
|
|
52
|
+
exit 1
|
|
53
|
+
fi
|
|
54
|
+
python - <<'PY'
|
|
55
|
+
import os
|
|
56
|
+
import re
|
|
57
|
+
|
|
58
|
+
version = os.environ["INPUT_VERSION"]
|
|
59
|
+
match = re.fullmatch(r"v([0-9]+)\.([0-9]+)\.([0-9]+)(?:-rc\.([0-9]+))?", version)
|
|
60
|
+
if match is None:
|
|
61
|
+
raise SystemExit("invalid version")
|
|
62
|
+
major, minor, patch, rc = match.groups()
|
|
63
|
+
package_version = f"{major}.{minor}.{patch}"
|
|
64
|
+
if rc is not None:
|
|
65
|
+
package_version = f"{package_version}rc{rc}"
|
|
66
|
+
with open(os.environ["GITHUB_ENV"], "a", encoding="utf-8") as env:
|
|
67
|
+
env.write(f"PYTHON_PACKAGE_VERSION={package_version}\n")
|
|
68
|
+
print(f"TrustPlane version: {version}")
|
|
69
|
+
print(f"Python package version: {package_version}")
|
|
70
|
+
PY
|
|
71
|
+
env:
|
|
72
|
+
INPUT_VERSION: ${{ inputs.version }}
|
|
73
|
+
- name: Refuse existing git tag
|
|
74
|
+
run: |
|
|
75
|
+
if git rev-parse -q --verify "refs/tags/${{ inputs.version }}" >/dev/null; then
|
|
76
|
+
echo "local tag already exists: ${{ inputs.version }}"
|
|
77
|
+
exit 1
|
|
78
|
+
fi
|
|
79
|
+
if git ls-remote --exit-code --tags origin "refs/tags/${{ inputs.version }}" >/dev/null 2>&1; then
|
|
80
|
+
echo "remote tag already exists: ${{ inputs.version }}"
|
|
81
|
+
exit 1
|
|
82
|
+
fi
|
|
83
|
+
- name: Refuse existing PyPI version
|
|
84
|
+
run: |
|
|
85
|
+
python - <<'PY'
|
|
86
|
+
import json
|
|
87
|
+
import os
|
|
88
|
+
import sys
|
|
89
|
+
import urllib.error
|
|
90
|
+
import urllib.request
|
|
91
|
+
|
|
92
|
+
package_name = os.environ["PACKAGE_NAME"]
|
|
93
|
+
package_version = os.environ["PYTHON_PACKAGE_VERSION"]
|
|
94
|
+
url = f"https://pypi.org/pypi/{package_name}/json"
|
|
95
|
+
try:
|
|
96
|
+
with urllib.request.urlopen(url, timeout=20) as response:
|
|
97
|
+
data = json.load(response)
|
|
98
|
+
except urllib.error.HTTPError as exc:
|
|
99
|
+
if exc.code == 404:
|
|
100
|
+
print(f"PyPI project does not exist yet: {package_name}")
|
|
101
|
+
sys.exit(0)
|
|
102
|
+
raise
|
|
103
|
+
|
|
104
|
+
if package_version in data.get("releases", {}):
|
|
105
|
+
raise SystemExit(f"PyPI version already exists: {package_name}=={package_version}")
|
|
106
|
+
print(f"PyPI version is unused: {package_name}=={package_version}")
|
|
107
|
+
PY
|
|
108
|
+
- name: Format check
|
|
109
|
+
run: ruff format --check .
|
|
110
|
+
- name: Lint
|
|
111
|
+
run: ruff check .
|
|
112
|
+
- name: Type check
|
|
113
|
+
run: mypy
|
|
114
|
+
- name: Tests
|
|
115
|
+
run: pytest
|
|
116
|
+
- name: Leak scan
|
|
117
|
+
run: scripts/scan-leaks.sh
|
|
118
|
+
- name: Wording scan
|
|
119
|
+
run: scripts/scan-wording.sh
|
|
120
|
+
- name: Verify clean worktree before version update
|
|
121
|
+
run: |
|
|
122
|
+
status="$(git status --short)"
|
|
123
|
+
printf '%s\n' "$status"
|
|
124
|
+
if [ -n "$status" ]; then
|
|
125
|
+
echo "worktree must be clean before version update"
|
|
126
|
+
exit 1
|
|
127
|
+
fi
|
|
128
|
+
- name: Set package version and README install command
|
|
129
|
+
run: |
|
|
130
|
+
python - <<'PY'
|
|
131
|
+
import os
|
|
132
|
+
from pathlib import Path
|
|
133
|
+
|
|
134
|
+
package_version = os.environ["PYTHON_PACKAGE_VERSION"]
|
|
135
|
+
path = Path("pyproject.toml")
|
|
136
|
+
lines = path.read_text(encoding="utf-8").splitlines()
|
|
137
|
+
changed = False
|
|
138
|
+
for index, line in enumerate(lines):
|
|
139
|
+
if line.startswith("version = "):
|
|
140
|
+
lines[index] = f'version = "{package_version}"'
|
|
141
|
+
changed = True
|
|
142
|
+
break
|
|
143
|
+
if not changed:
|
|
144
|
+
raise SystemExit("pyproject.toml project version not found")
|
|
145
|
+
path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
146
|
+
|
|
147
|
+
readme = Path("README.md")
|
|
148
|
+
text = readme.read_text(encoding="utf-8")
|
|
149
|
+
prefix, install_marker, rest = text.partition("## Install\n")
|
|
150
|
+
old_install, package_marker, suffix = rest.partition("## Package Name\n")
|
|
151
|
+
if install_marker == "" or package_marker == "":
|
|
152
|
+
raise SystemExit("README package section marker not found")
|
|
153
|
+
install_section = (
|
|
154
|
+
"## Install\n\n"
|
|
155
|
+
"Install released preview versions from PyPI with an exact version pin:\n\n"
|
|
156
|
+
"```sh\n"
|
|
157
|
+
f"python -m pip install trustplane-auth-sdk=={package_version}\n"
|
|
158
|
+
"```\n\n"
|
|
159
|
+
"For unreleased local changes, test this repository through a local checkout or built wheel.\n\n"
|
|
160
|
+
)
|
|
161
|
+
readme.write_text(prefix + install_section + package_marker + suffix, encoding="utf-8")
|
|
162
|
+
PY
|
|
163
|
+
- name: Verify only release metadata is dirty
|
|
164
|
+
run: |
|
|
165
|
+
status="$(git status --short)"
|
|
166
|
+
printf '%s\n' "$status"
|
|
167
|
+
python - <<'PY'
|
|
168
|
+
import subprocess
|
|
169
|
+
|
|
170
|
+
dirty = subprocess.check_output(
|
|
171
|
+
["git", "diff", "--name-only"],
|
|
172
|
+
text=True,
|
|
173
|
+
).splitlines()
|
|
174
|
+
if dirty != ["README.md", "pyproject.toml"]:
|
|
175
|
+
raise SystemExit(f"unexpected worktree state after version update: {dirty}")
|
|
176
|
+
PY
|
|
177
|
+
- name: Build sdist and wheel
|
|
178
|
+
run: python -m build
|
|
179
|
+
- name: Clean wheel install/import smoke
|
|
180
|
+
run: scripts/smoke-wheel.sh
|
|
181
|
+
- name: Dry-run summary
|
|
182
|
+
if: ${{ inputs.publish_package == false }}
|
|
183
|
+
run: |
|
|
184
|
+
{
|
|
185
|
+
echo "### PyPI release readiness complete"
|
|
186
|
+
echo
|
|
187
|
+
echo "Prepared and smoke-tested $PACKAGE_NAME==$PYTHON_PACKAGE_VERSION."
|
|
188
|
+
echo
|
|
189
|
+
echo "TrustPlane release version: ${{ inputs.version }}"
|
|
190
|
+
echo
|
|
191
|
+
echo "publish_package=false, so this run did not create a commit, tag, PyPI publish, or GitHub Release."
|
|
192
|
+
echo
|
|
193
|
+
echo "To publish later, rerun from main with publish_package=true after PyPI Trusted Publishing is configured."
|
|
194
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
195
|
+
- name: Require PyPI Trusted Publishing
|
|
196
|
+
if: ${{ inputs.publish_package }}
|
|
197
|
+
run: |
|
|
198
|
+
if [ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" ] || [ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" ]; then
|
|
199
|
+
echo "PyPI Trusted Publishing/OIDC is not available to this job."
|
|
200
|
+
echo "Configure PyPI Trusted Publishing for $PACKAGE_NAME and keep id-token: write enabled."
|
|
201
|
+
exit 1
|
|
202
|
+
fi
|
|
203
|
+
- name: Publish package with PyPI Trusted Publishing
|
|
204
|
+
if: ${{ inputs.publish_package }}
|
|
205
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
206
|
+
- name: Commit version, tag, and push
|
|
207
|
+
if: ${{ inputs.publish_package }}
|
|
208
|
+
run: |
|
|
209
|
+
git add README.md pyproject.toml
|
|
210
|
+
git commit -m "Release Python SDK ${{ inputs.version }}"
|
|
211
|
+
git tag -a "${{ inputs.version }}" -m "TrustPlane Auth Python SDK ${{ inputs.version }}"
|
|
212
|
+
git push origin "HEAD:main" "refs/tags/${{ inputs.version }}"
|
|
213
|
+
- name: Publish summary
|
|
214
|
+
if: ${{ inputs.publish_package }}
|
|
215
|
+
run: |
|
|
216
|
+
{
|
|
217
|
+
echo "### PyPI release published"
|
|
218
|
+
echo
|
|
219
|
+
echo "Published $PACKAGE_NAME==$PYTHON_PACKAGE_VERSION with PyPI Trusted Publishing."
|
|
220
|
+
echo
|
|
221
|
+
echo "TrustPlane release version: ${{ inputs.version }}"
|
|
222
|
+
echo
|
|
223
|
+
echo "No GitHub Release was created by this workflow. Track any GitHub Release as a follow-up release task if repo policy later requires one."
|
|
224
|
+
echo
|
|
225
|
+
echo "Recovery note: PyPI publish runs before git commit/tag push. If PyPI publish fails, there is no release commit or tag to repair. If git push fails after PyPI publish, do not republish or overwrite PyPI; repair the git commit/tag publication from the same workflow commit and package version."
|
|
226
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Agent Instructions
|
|
2
|
+
|
|
3
|
+
- Do not commit directly to `main`.
|
|
4
|
+
- Use branch `feature/python-sdk-foundation` for TP-054A work.
|
|
5
|
+
- Do not use a `[codex]` prefix in branch names, commits, PR titles, or PR bodies.
|
|
6
|
+
- Every commit must be authored and committed as `Medh Mesh <maintainer@trustplane.dev>`.
|
|
7
|
+
- Do not publish to PyPI, create tags, or create GitHub Releases from this repository.
|
|
8
|
+
- Keep public docs free of private paths, personal names, token values, internal process chatter, and premature package availability claims.
|
|
@@ -0,0 +1,51 @@
|
|
|
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, and distribution as defined by Sections 1 through 9 of this document.
|
|
10
|
+
|
|
11
|
+
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
|
|
12
|
+
|
|
13
|
+
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
|
14
|
+
|
|
15
|
+
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
|
|
16
|
+
|
|
17
|
+
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
|
|
18
|
+
|
|
19
|
+
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
|
|
20
|
+
|
|
21
|
+
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work.
|
|
22
|
+
|
|
23
|
+
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on or derived from the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link or bind by name to the interfaces of the Work.
|
|
24
|
+
|
|
25
|
+
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner.
|
|
26
|
+
|
|
27
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
|
|
28
|
+
|
|
29
|
+
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
|
|
30
|
+
|
|
31
|
+
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work.
|
|
32
|
+
|
|
33
|
+
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, provided that You meet the following conditions:
|
|
34
|
+
|
|
35
|
+
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
|
|
36
|
+
|
|
37
|
+
(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
|
|
38
|
+
|
|
39
|
+
(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
|
|
40
|
+
|
|
41
|
+
(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file.
|
|
42
|
+
|
|
43
|
+
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License.
|
|
44
|
+
|
|
45
|
+
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work.
|
|
46
|
+
|
|
47
|
+
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
48
|
+
|
|
49
|
+
8. Limitation of Liability. In no event and under no legal theory shall any Contributor be liable to You for damages arising from use of the Work.
|
|
50
|
+
|
|
51
|
+
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer support, warranty, indemnity, or other liability obligations. In accepting such obligations, You may act only on Your own behalf and on Your sole responsibility.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: trustplane-auth-sdk
|
|
3
|
+
Version: 0.1.0rc1
|
|
4
|
+
Summary: Python SDK foundation for TrustPlane Auth request material and local Ed25519 signing
|
|
5
|
+
Project-URL: Homepage, https://github.com/trustplane-dev/trustplane-auth-sdk-python
|
|
6
|
+
Project-URL: Repository, https://github.com/trustplane-dev/trustplane-auth-sdk-python
|
|
7
|
+
Author: TrustPlane
|
|
8
|
+
License-Expression: Apache-2.0
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
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
|
+
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: cryptography>=42
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
23
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
25
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# TrustPlane Auth SDK for Python
|
|
29
|
+
|
|
30
|
+
Preview Python caller SDK for TrustPlane Auth request signing.
|
|
31
|
+
|
|
32
|
+
This package provides caller-side helpers for:
|
|
33
|
+
|
|
34
|
+
- building `transcript-v1` request material
|
|
35
|
+
- computing body SHA-256 values
|
|
36
|
+
- parsing passport claims needed by signing
|
|
37
|
+
- raw local Ed25519 software signing
|
|
38
|
+
- returning adapter-ready TrustPlane Auth headers
|
|
39
|
+
|
|
40
|
+
It does not include a verifier, broker, adapter, policy engine, SPIFFE issuer, deployment code, enrollment flow, bundle mutation, or TrustPlane Control API.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
Install released preview versions from PyPI with an exact version pin:
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
python -m pip install trustplane-auth-sdk==0.1.0rc1
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
For unreleased local changes, test this repository through a local checkout or built wheel.
|
|
51
|
+
|
|
52
|
+
## Package Name
|
|
53
|
+
|
|
54
|
+
The Python distribution name candidate is `trustplane-auth-sdk`.
|
|
55
|
+
|
|
56
|
+
The import module is `trustplane_auth`:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from trustplane_auth import body_sha256, build_request, sign_request
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The package version remains `0.0.0` until the release workflow is introduced.
|
|
63
|
+
|
|
64
|
+
PyPI trusted publishing is planned in a follow-up.
|
|
65
|
+
|
|
66
|
+
## Build Request Example
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from trustplane_auth import SOFTWARE_KEY_BINDING, Header, build_request
|
|
70
|
+
|
|
71
|
+
material = build_request(
|
|
72
|
+
method="POST",
|
|
73
|
+
scheme="https",
|
|
74
|
+
authority="orders.example",
|
|
75
|
+
path="/v1/orders",
|
|
76
|
+
audience="orders-api",
|
|
77
|
+
route_id="orders.create",
|
|
78
|
+
content_encoding="identity",
|
|
79
|
+
body=b'{"order_id":"ord_123","amount":"42.00"}',
|
|
80
|
+
headers=[
|
|
81
|
+
Header(name="Content-Type", value="application/json"),
|
|
82
|
+
Header(name="X-TrustPlane-Nonce", value="nonce-v1-001"),
|
|
83
|
+
],
|
|
84
|
+
header_allow_list=["content-type", "x-trustplane-nonce"],
|
|
85
|
+
passport_jti="passport-v1-minimal-001",
|
|
86
|
+
nonce="nonce-v1-001",
|
|
87
|
+
issued_at_unix=1740000000,
|
|
88
|
+
key_binding=SOFTWARE_KEY_BINDING,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
print(material.transcript_sha256)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Signing Example
|
|
95
|
+
|
|
96
|
+
Signing requires a real passport whose `cnf.key_binding` is `software` and whose `cnf.public_key_b64url` matches the Ed25519 private key.
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from pathlib import Path
|
|
100
|
+
|
|
101
|
+
from trustplane_auth import Header, sign_request
|
|
102
|
+
|
|
103
|
+
passport = "header.payload.signature"
|
|
104
|
+
private_key_pem = Path("ed25519-private-key.pem").read_bytes()
|
|
105
|
+
|
|
106
|
+
signed = sign_request(
|
|
107
|
+
request={
|
|
108
|
+
"method": "POST",
|
|
109
|
+
"scheme": "https",
|
|
110
|
+
"authority": "orders.example",
|
|
111
|
+
"path": "/v1/orders",
|
|
112
|
+
"route_id": "orders.create",
|
|
113
|
+
"content_encoding": "identity",
|
|
114
|
+
"body": b'{"order_id":"ord_123","amount":"42.00"}',
|
|
115
|
+
"headers": [
|
|
116
|
+
Header(name="Content-Type", value="application/json"),
|
|
117
|
+
Header(name="X-TrustPlane-Nonce", value="nonce-v1-001"),
|
|
118
|
+
],
|
|
119
|
+
"header_allow_list": ["content-type", "x-trustplane-nonce"],
|
|
120
|
+
"nonce": "nonce-v1-001",
|
|
121
|
+
},
|
|
122
|
+
passport_token=passport,
|
|
123
|
+
private_key=private_key_pem,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
headers = signed.headers
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`sign_request` reads passport-bound fields from the passport and fails if caller-supplied consistency checks conflict. It does not infer or repair `aud`, `jti`, `iat`, `cnf.kid`, `cnf.key_binding`, or `cnf.public_key_b64url` from caller request inputs.
|
|
130
|
+
|
|
131
|
+
## Conformance Posture
|
|
132
|
+
|
|
133
|
+
`testdata/conformance/v1` contains public-safe contract vectors copied from the TrustPlane Auth reference. Tests assert exact canonical transcript lines, transcript SHA-256 values, and body SHA-256 values.
|
|
134
|
+
|
|
135
|
+
## Security Rule
|
|
136
|
+
|
|
137
|
+
This SDK signs only the verifier-rebuilt request transcript. Raw local signing is software-only and requires an Ed25519 private key whose public key exactly matches the passport `cnf.public_key_b64url` claim.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# TrustPlane Auth SDK for Python
|
|
2
|
+
|
|
3
|
+
Preview Python caller SDK for TrustPlane Auth request signing.
|
|
4
|
+
|
|
5
|
+
This package provides caller-side helpers for:
|
|
6
|
+
|
|
7
|
+
- building `transcript-v1` request material
|
|
8
|
+
- computing body SHA-256 values
|
|
9
|
+
- parsing passport claims needed by signing
|
|
10
|
+
- raw local Ed25519 software signing
|
|
11
|
+
- returning adapter-ready TrustPlane Auth headers
|
|
12
|
+
|
|
13
|
+
It does not include a verifier, broker, adapter, policy engine, SPIFFE issuer, deployment code, enrollment flow, bundle mutation, or TrustPlane Control API.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
Install released preview versions from PyPI with an exact version pin:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
python -m pip install trustplane-auth-sdk==0.1.0rc1
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
For unreleased local changes, test this repository through a local checkout or built wheel.
|
|
24
|
+
|
|
25
|
+
## Package Name
|
|
26
|
+
|
|
27
|
+
The Python distribution name candidate is `trustplane-auth-sdk`.
|
|
28
|
+
|
|
29
|
+
The import module is `trustplane_auth`:
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from trustplane_auth import body_sha256, build_request, sign_request
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The package version remains `0.0.0` until the release workflow is introduced.
|
|
36
|
+
|
|
37
|
+
PyPI trusted publishing is planned in a follow-up.
|
|
38
|
+
|
|
39
|
+
## Build Request Example
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from trustplane_auth import SOFTWARE_KEY_BINDING, Header, build_request
|
|
43
|
+
|
|
44
|
+
material = build_request(
|
|
45
|
+
method="POST",
|
|
46
|
+
scheme="https",
|
|
47
|
+
authority="orders.example",
|
|
48
|
+
path="/v1/orders",
|
|
49
|
+
audience="orders-api",
|
|
50
|
+
route_id="orders.create",
|
|
51
|
+
content_encoding="identity",
|
|
52
|
+
body=b'{"order_id":"ord_123","amount":"42.00"}',
|
|
53
|
+
headers=[
|
|
54
|
+
Header(name="Content-Type", value="application/json"),
|
|
55
|
+
Header(name="X-TrustPlane-Nonce", value="nonce-v1-001"),
|
|
56
|
+
],
|
|
57
|
+
header_allow_list=["content-type", "x-trustplane-nonce"],
|
|
58
|
+
passport_jti="passport-v1-minimal-001",
|
|
59
|
+
nonce="nonce-v1-001",
|
|
60
|
+
issued_at_unix=1740000000,
|
|
61
|
+
key_binding=SOFTWARE_KEY_BINDING,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
print(material.transcript_sha256)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Signing Example
|
|
68
|
+
|
|
69
|
+
Signing requires a real passport whose `cnf.key_binding` is `software` and whose `cnf.public_key_b64url` matches the Ed25519 private key.
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from pathlib import Path
|
|
73
|
+
|
|
74
|
+
from trustplane_auth import Header, sign_request
|
|
75
|
+
|
|
76
|
+
passport = "header.payload.signature"
|
|
77
|
+
private_key_pem = Path("ed25519-private-key.pem").read_bytes()
|
|
78
|
+
|
|
79
|
+
signed = sign_request(
|
|
80
|
+
request={
|
|
81
|
+
"method": "POST",
|
|
82
|
+
"scheme": "https",
|
|
83
|
+
"authority": "orders.example",
|
|
84
|
+
"path": "/v1/orders",
|
|
85
|
+
"route_id": "orders.create",
|
|
86
|
+
"content_encoding": "identity",
|
|
87
|
+
"body": b'{"order_id":"ord_123","amount":"42.00"}',
|
|
88
|
+
"headers": [
|
|
89
|
+
Header(name="Content-Type", value="application/json"),
|
|
90
|
+
Header(name="X-TrustPlane-Nonce", value="nonce-v1-001"),
|
|
91
|
+
],
|
|
92
|
+
"header_allow_list": ["content-type", "x-trustplane-nonce"],
|
|
93
|
+
"nonce": "nonce-v1-001",
|
|
94
|
+
},
|
|
95
|
+
passport_token=passport,
|
|
96
|
+
private_key=private_key_pem,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
headers = signed.headers
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`sign_request` reads passport-bound fields from the passport and fails if caller-supplied consistency checks conflict. It does not infer or repair `aud`, `jti`, `iat`, `cnf.kid`, `cnf.key_binding`, or `cnf.public_key_b64url` from caller request inputs.
|
|
103
|
+
|
|
104
|
+
## Conformance Posture
|
|
105
|
+
|
|
106
|
+
`testdata/conformance/v1` contains public-safe contract vectors copied from the TrustPlane Auth reference. Tests assert exact canonical transcript lines, transcript SHA-256 values, and body SHA-256 values.
|
|
107
|
+
|
|
108
|
+
## Security Rule
|
|
109
|
+
|
|
110
|
+
This SDK signs only the verifier-rebuilt request transcript. Raw local signing is software-only and requires an Ed25519 private key whose public key exactly matches the passport `cnf.public_key_b64url` claim.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
Please report suspected vulnerabilities through the repository security advisory flow or the TrustPlane security contact used for project disclosures.
|
|
4
|
+
|
|
5
|
+
Do not include private keys, bearer tokens, passports, or production request transcripts in public issues.
|
|
6
|
+
|
|
7
|
+
This package currently supports only raw local Ed25519 software signing. It rejects non-software key bindings for local signing.
|