trustplane-sdk 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.
- trustplane_sdk-0.1.0/PKG-INFO +51 -0
- trustplane_sdk-0.1.0/README.md +42 -0
- trustplane_sdk-0.1.0/pyproject.toml +14 -0
- trustplane_sdk-0.1.0/setup.cfg +4 -0
- trustplane_sdk-0.1.0/tests/test_vector.py +34 -0
- trustplane_sdk-0.1.0/trustplane_sdk/__init__.py +155 -0
- trustplane_sdk-0.1.0/trustplane_sdk.egg-info/PKG-INFO +51 -0
- trustplane_sdk-0.1.0/trustplane_sdk.egg-info/SOURCES.txt +9 -0
- trustplane_sdk-0.1.0/trustplane_sdk.egg-info/dependency_links.txt +1 -0
- trustplane_sdk-0.1.0/trustplane_sdk.egg-info/requires.txt +1 -0
- trustplane_sdk-0.1.0/trustplane_sdk.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: trustplane-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Trustplane SDK (Python) for generating request proof headers
|
|
5
|
+
Project-URL: Homepage, https://trustplane.mergematter.io
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: pynacl>=1.5.0
|
|
9
|
+
|
|
10
|
+
# Trustplane Python SDK (v0.1)
|
|
11
|
+
|
|
12
|
+
Minimal SDK to generate Trustplane proof headers.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install trustplane-sdk
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from trustplane_sdk import sign
|
|
24
|
+
|
|
25
|
+
out = sign(
|
|
26
|
+
tenant_id="mergematter.io",
|
|
27
|
+
api_id="api_demo",
|
|
28
|
+
client_id="client_demo",
|
|
29
|
+
private_key_b64url="<private_key_b64url>",
|
|
30
|
+
method="GET",
|
|
31
|
+
path="/orders",
|
|
32
|
+
body=""
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
print(out["headers"])
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Config file
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from trustplane_sdk import from_file
|
|
42
|
+
|
|
43
|
+
client = from_file("./trustplane.json")
|
|
44
|
+
out = client.sign(method="GET", path="/orders", body="", private_key_b64url="<private_key_b64url>")
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Tests
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
python3 -m unittest sdk/python/tests/test_vector.py
|
|
51
|
+
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Trustplane Python SDK (v0.1)
|
|
2
|
+
|
|
3
|
+
Minimal SDK to generate Trustplane proof headers.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install trustplane-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from trustplane_sdk import sign
|
|
15
|
+
|
|
16
|
+
out = sign(
|
|
17
|
+
tenant_id="mergematter.io",
|
|
18
|
+
api_id="api_demo",
|
|
19
|
+
client_id="client_demo",
|
|
20
|
+
private_key_b64url="<private_key_b64url>",
|
|
21
|
+
method="GET",
|
|
22
|
+
path="/orders",
|
|
23
|
+
body=""
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
print(out["headers"])
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Config file
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from trustplane_sdk import from_file
|
|
33
|
+
|
|
34
|
+
client = from_file("./trustplane.json")
|
|
35
|
+
out = client.sign(method="GET", path="/orders", body="", private_key_b64url="<private_key_b64url>")
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Tests
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
python3 -m unittest sdk/python/tests/test_vector.py
|
|
42
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "trustplane-sdk"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Trustplane SDK (Python) for generating request proof headers"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
dependencies = ["pynacl>=1.5.0"]
|
|
12
|
+
|
|
13
|
+
[project.urls]
|
|
14
|
+
Homepage = "https://trustplane.mergematter.io"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from trustplane_sdk import sign
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestVector(unittest.TestCase):
|
|
7
|
+
def test_headers_shape(self) -> None:
|
|
8
|
+
out = sign(
|
|
9
|
+
tenant_id="mergematter.io",
|
|
10
|
+
api_id="api_demo",
|
|
11
|
+
client_id="client_demo",
|
|
12
|
+
private_key_b64url="dyIi-Xwr2tl6NdzkT0CXWUZkb9cPRXTSbDQgz-SCcORqDZPYyFAEXEGn6Eg6hlokBxvS8avUjpmk4VwaXmg7zQ",
|
|
13
|
+
method="GET",
|
|
14
|
+
path="/orders",
|
|
15
|
+
body="",
|
|
16
|
+
bucket_seconds=20,
|
|
17
|
+
time_bucket_override=88498363,
|
|
18
|
+
nonce_override="9biCQnN2URhTCqhn_-orBQ",
|
|
19
|
+
)
|
|
20
|
+
headers = out["headers"]
|
|
21
|
+
self.assertEqual(headers["x-tp-tenant-id"], "mergematter.io")
|
|
22
|
+
self.assertEqual(headers["x-tp-api-id"], "api_demo")
|
|
23
|
+
self.assertEqual(headers["x-tp-client-id"], "client_demo")
|
|
24
|
+
self.assertEqual(headers["x-tp-time-bucket"], "88498363")
|
|
25
|
+
self.assertEqual(headers["x-tp-nonce"], "9biCQnN2URhTCqhn_-orBQ")
|
|
26
|
+
self.assertTrue(headers["x-tp-signature"])
|
|
27
|
+
self.assertEqual(
|
|
28
|
+
headers["x-tp-body-hash"],
|
|
29
|
+
"sha256:47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU",
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
if __name__ == "__main__":
|
|
34
|
+
unittest.main()
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import hashlib
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import time
|
|
6
|
+
from typing import Any, Dict
|
|
7
|
+
|
|
8
|
+
from nacl.signing import SigningKey
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _b64url_encode(data: bytes) -> str:
|
|
12
|
+
return base64.urlsafe_b64encode(data).decode("utf-8").rstrip("=")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _b64url_decode(data: str) -> bytes:
|
|
16
|
+
pad = "=" * (-len(data) % 4)
|
|
17
|
+
return base64.urlsafe_b64decode(data + pad)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _sha256_b64url(data: bytes) -> str:
|
|
21
|
+
return _b64url_encode(hashlib.sha256(data).digest())
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _random_nonce() -> str:
|
|
25
|
+
return _b64url_encode(os.urandom(16))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _build_transcript(
|
|
29
|
+
tenant_id: str,
|
|
30
|
+
api_id: str,
|
|
31
|
+
client_id: str,
|
|
32
|
+
method: str,
|
|
33
|
+
path: str,
|
|
34
|
+
body_hash: str,
|
|
35
|
+
time_bucket: int,
|
|
36
|
+
nonce: str,
|
|
37
|
+
) -> str:
|
|
38
|
+
return "\n".join(
|
|
39
|
+
[
|
|
40
|
+
"TP1",
|
|
41
|
+
f"tenant_id={tenant_id}",
|
|
42
|
+
f"api_id={api_id}",
|
|
43
|
+
f"client_id={client_id}",
|
|
44
|
+
f"method={method}",
|
|
45
|
+
f"path={path}",
|
|
46
|
+
f"body_hash={body_hash}",
|
|
47
|
+
f"time_bucket={time_bucket}",
|
|
48
|
+
f"nonce={nonce}",
|
|
49
|
+
]
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def sign(
|
|
54
|
+
tenant_id: str,
|
|
55
|
+
api_id: str,
|
|
56
|
+
client_id: str,
|
|
57
|
+
private_key_b64url: str,
|
|
58
|
+
method: str = "GET",
|
|
59
|
+
path: str = "/",
|
|
60
|
+
body: str = "",
|
|
61
|
+
bucket_seconds: int = 20,
|
|
62
|
+
time_bucket_override: int = None,
|
|
63
|
+
nonce_override: str = None,
|
|
64
|
+
) -> Dict[str, Any]:
|
|
65
|
+
if not tenant_id or not api_id or not client_id:
|
|
66
|
+
raise ValueError("tenant_id, api_id, and client_id are required")
|
|
67
|
+
if not private_key_b64url:
|
|
68
|
+
raise ValueError("private_key_b64url is required")
|
|
69
|
+
if not path:
|
|
70
|
+
raise ValueError("path is required")
|
|
71
|
+
|
|
72
|
+
body_hash = f"sha256:{_sha256_b64url(body.encode('utf-8'))}"
|
|
73
|
+
now_seconds = int(time.time())
|
|
74
|
+
bucket = bucket_seconds if bucket_seconds > 0 else 20
|
|
75
|
+
time_bucket = time_bucket_override if time_bucket_override is not None else (now_seconds // bucket)
|
|
76
|
+
nonce = nonce_override if nonce_override is not None else _random_nonce()
|
|
77
|
+
|
|
78
|
+
transcript = _build_transcript(
|
|
79
|
+
tenant_id,
|
|
80
|
+
api_id,
|
|
81
|
+
client_id,
|
|
82
|
+
method.upper(),
|
|
83
|
+
path,
|
|
84
|
+
body_hash,
|
|
85
|
+
time_bucket,
|
|
86
|
+
nonce,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
digest = hashlib.sha256(transcript.encode("utf-8")).digest()
|
|
90
|
+
priv = _b64url_decode(private_key_b64url)
|
|
91
|
+
if len(priv) != 64:
|
|
92
|
+
raise ValueError("private_key_b64url must be ed25519 private key (64 bytes)")
|
|
93
|
+
signing_key = SigningKey(priv[:32])
|
|
94
|
+
signature = signing_key.sign(digest).signature
|
|
95
|
+
signature_b64url = _b64url_encode(signature)
|
|
96
|
+
|
|
97
|
+
headers = {
|
|
98
|
+
"x-tp-tenant-id": tenant_id,
|
|
99
|
+
"x-tp-api-id": api_id,
|
|
100
|
+
"x-tp-client-id": client_id,
|
|
101
|
+
"x-tp-time-bucket": str(time_bucket),
|
|
102
|
+
"x-tp-nonce": nonce,
|
|
103
|
+
"x-tp-signature": signature_b64url,
|
|
104
|
+
"x-tp-body-hash": body_hash,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
verify_payload = {
|
|
108
|
+
"tenant_id": tenant_id,
|
|
109
|
+
"api_id": api_id,
|
|
110
|
+
"client_id": client_id,
|
|
111
|
+
"method": method.upper(),
|
|
112
|
+
"path": path,
|
|
113
|
+
"body_hash": body_hash,
|
|
114
|
+
"time_bucket": time_bucket,
|
|
115
|
+
"nonce": nonce,
|
|
116
|
+
"signature": signature_b64url,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
"headers": headers,
|
|
121
|
+
"verify_payload": verify_payload,
|
|
122
|
+
"transcript": transcript,
|
|
123
|
+
"digest_b64url": _b64url_encode(digest),
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class TrustplaneClient:
|
|
128
|
+
def __init__(self, tenant_id: str, api_id: str, client_id: str, bucket_seconds: int = 20):
|
|
129
|
+
self.tenant_id = tenant_id
|
|
130
|
+
self.api_id = api_id
|
|
131
|
+
self.client_id = client_id
|
|
132
|
+
self.bucket_seconds = bucket_seconds
|
|
133
|
+
|
|
134
|
+
def sign(self, method: str, path: str, body: str, private_key_b64url: str) -> Dict[str, Any]:
|
|
135
|
+
return sign(
|
|
136
|
+
tenant_id=self.tenant_id,
|
|
137
|
+
api_id=self.api_id,
|
|
138
|
+
client_id=self.client_id,
|
|
139
|
+
private_key_b64url=private_key_b64url,
|
|
140
|
+
method=method,
|
|
141
|
+
path=path,
|
|
142
|
+
body=body,
|
|
143
|
+
bucket_seconds=self.bucket_seconds,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def from_file(path: str) -> TrustplaneClient:
|
|
148
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
149
|
+
cfg = json.load(f)
|
|
150
|
+
return TrustplaneClient(
|
|
151
|
+
tenant_id=cfg.get("tenant_id"),
|
|
152
|
+
api_id=cfg.get("api_id"),
|
|
153
|
+
client_id=cfg.get("client_id"),
|
|
154
|
+
bucket_seconds=cfg.get("bucket_seconds", 20),
|
|
155
|
+
)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: trustplane-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Trustplane SDK (Python) for generating request proof headers
|
|
5
|
+
Project-URL: Homepage, https://trustplane.mergematter.io
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: pynacl>=1.5.0
|
|
9
|
+
|
|
10
|
+
# Trustplane Python SDK (v0.1)
|
|
11
|
+
|
|
12
|
+
Minimal SDK to generate Trustplane proof headers.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install trustplane-sdk
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from trustplane_sdk import sign
|
|
24
|
+
|
|
25
|
+
out = sign(
|
|
26
|
+
tenant_id="mergematter.io",
|
|
27
|
+
api_id="api_demo",
|
|
28
|
+
client_id="client_demo",
|
|
29
|
+
private_key_b64url="<private_key_b64url>",
|
|
30
|
+
method="GET",
|
|
31
|
+
path="/orders",
|
|
32
|
+
body=""
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
print(out["headers"])
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Config file
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from trustplane_sdk import from_file
|
|
42
|
+
|
|
43
|
+
client = from_file("./trustplane.json")
|
|
44
|
+
out = client.sign(method="GET", path="/orders", body="", private_key_b64url="<private_key_b64url>")
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Tests
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
python3 -m unittest sdk/python/tests/test_vector.py
|
|
51
|
+
```
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
tests/test_vector.py
|
|
4
|
+
trustplane_sdk/__init__.py
|
|
5
|
+
trustplane_sdk.egg-info/PKG-INFO
|
|
6
|
+
trustplane_sdk.egg-info/SOURCES.txt
|
|
7
|
+
trustplane_sdk.egg-info/dependency_links.txt
|
|
8
|
+
trustplane_sdk.egg-info/requires.txt
|
|
9
|
+
trustplane_sdk.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pynacl>=1.5.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
trustplane_sdk
|