qpher 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.
- qpher-0.1.0/.gitignore +82 -0
- qpher-0.1.0/LICENSE +21 -0
- qpher-0.1.0/PKG-INFO +265 -0
- qpher-0.1.0/README.md +236 -0
- qpher-0.1.0/pyproject.toml +45 -0
- qpher-0.1.0/qpher/__init__.py +53 -0
- qpher-0.1.0/qpher/_version.py +1 -0
- qpher-0.1.0/qpher/client.py +50 -0
- qpher-0.1.0/qpher/errors.py +114 -0
- qpher-0.1.0/qpher/http_client.py +128 -0
- qpher-0.1.0/qpher/kem.py +91 -0
- qpher-0.1.0/qpher/keys.py +167 -0
- qpher-0.1.0/qpher/signatures.py +78 -0
- qpher-0.1.0/qpher/types.py +99 -0
- qpher-0.1.0/tests/__init__.py +1 -0
- qpher-0.1.0/tests/conftest.py +37 -0
- qpher-0.1.0/tests/test_client.py +100 -0
- qpher-0.1.0/tests/test_errors.py +271 -0
- qpher-0.1.0/tests/test_http_client.py +302 -0
- qpher-0.1.0/tests/test_kem.py +236 -0
- qpher-0.1.0/tests/test_keys.py +289 -0
- qpher-0.1.0/tests/test_signatures.py +213 -0
- qpher-0.1.0/tests/test_types.py +223 -0
qpher-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# ============================================================
|
|
2
|
+
# Qpher PQC Security Cloud — .gitignore
|
|
3
|
+
# ============================================================
|
|
4
|
+
|
|
5
|
+
# --- Python ---
|
|
6
|
+
__pycache__/
|
|
7
|
+
*.py[cod]
|
|
8
|
+
*$py.class
|
|
9
|
+
*.so
|
|
10
|
+
*.egg-info/
|
|
11
|
+
dist/
|
|
12
|
+
build/
|
|
13
|
+
*.egg
|
|
14
|
+
.eggs/
|
|
15
|
+
|
|
16
|
+
# --- Virtual Environments ---
|
|
17
|
+
venv/
|
|
18
|
+
.venv/
|
|
19
|
+
env/
|
|
20
|
+
ENV/
|
|
21
|
+
|
|
22
|
+
# --- IDE ---
|
|
23
|
+
.vscode/
|
|
24
|
+
.idea/
|
|
25
|
+
*.swp
|
|
26
|
+
*.swo
|
|
27
|
+
*~
|
|
28
|
+
.DS_Store
|
|
29
|
+
Thumbs.db
|
|
30
|
+
|
|
31
|
+
# --- Testing ---
|
|
32
|
+
.coverage
|
|
33
|
+
.coverage.*
|
|
34
|
+
htmlcov/
|
|
35
|
+
.pytest_cache/
|
|
36
|
+
.mypy_cache/
|
|
37
|
+
.ruff_cache/
|
|
38
|
+
|
|
39
|
+
# --- Environment / Secrets ---
|
|
40
|
+
.env
|
|
41
|
+
.env.local
|
|
42
|
+
.env.production
|
|
43
|
+
.env.staging
|
|
44
|
+
# NOTE: .env.example IS tracked (template only, no real secrets)
|
|
45
|
+
|
|
46
|
+
# --- Database ---
|
|
47
|
+
*.db
|
|
48
|
+
*.sqlite3
|
|
49
|
+
|
|
50
|
+
# --- Logs ---
|
|
51
|
+
logs/
|
|
52
|
+
*.log
|
|
53
|
+
audit_logs/
|
|
54
|
+
|
|
55
|
+
# --- Docker ---
|
|
56
|
+
docker-compose.override.yml
|
|
57
|
+
|
|
58
|
+
# --- Node.js (frontend apps) ---
|
|
59
|
+
node_modules/
|
|
60
|
+
.next/
|
|
61
|
+
.docusaurus/
|
|
62
|
+
out/
|
|
63
|
+
.turbo/
|
|
64
|
+
.vercel/
|
|
65
|
+
|
|
66
|
+
# --- KMS Key Files (NEVER commit private keys) ---
|
|
67
|
+
kms_keys/
|
|
68
|
+
*.pem
|
|
69
|
+
*.key
|
|
70
|
+
|
|
71
|
+
# --- OS ---
|
|
72
|
+
Desktop.ini
|
|
73
|
+
|
|
74
|
+
# --- Build artifacts ---
|
|
75
|
+
*.tar.gz
|
|
76
|
+
*.whl
|
|
77
|
+
|
|
78
|
+
# --- Jupyter ---
|
|
79
|
+
.ipynb_checkpoints/
|
|
80
|
+
|
|
81
|
+
# --- Claude Code ---
|
|
82
|
+
.claude/
|
qpher-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Qpher
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
qpher-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: qpher
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python SDK for the Qpher Post-Quantum Cryptography API
|
|
5
|
+
Project-URL: Homepage, https://qpher.ai
|
|
6
|
+
Project-URL: Documentation, https://docs.qpher.ai/sdks/python
|
|
7
|
+
Project-URL: Repository, https://github.com/qpher/qpher-python
|
|
8
|
+
Project-URL: Issues, https://github.com/qpher/qpher-python/issues
|
|
9
|
+
Author-email: Qpher <sdk@qpher.ai>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: cryptography,dilithium,encryption,kyber,post-quantum,pqc,signatures
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Security :: Cryptography
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Requires-Dist: requests>=2.31.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest>=7.4.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: responses>=0.24.0; extra == 'dev'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# Qpher Python SDK
|
|
31
|
+
|
|
32
|
+
Official Python SDK for the [Qpher](https://qpher.ai) Post-Quantum Cryptography API.
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install qpher
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Requirements
|
|
41
|
+
|
|
42
|
+
- Python 3.9+
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from qpher import Qpher
|
|
48
|
+
|
|
49
|
+
# Initialize the client
|
|
50
|
+
client = Qpher(api_key="qph_live_your_api_key")
|
|
51
|
+
|
|
52
|
+
# Encrypt data using Kyber768 KEM
|
|
53
|
+
result = client.kem.encrypt(
|
|
54
|
+
plaintext=b"Hello, Quantum World!",
|
|
55
|
+
key_version=1,
|
|
56
|
+
)
|
|
57
|
+
print(f"Ciphertext: {result.ciphertext.hex()}")
|
|
58
|
+
|
|
59
|
+
# Decrypt data
|
|
60
|
+
decrypted = client.kem.decrypt(
|
|
61
|
+
ciphertext=result.ciphertext,
|
|
62
|
+
key_version=result.key_version,
|
|
63
|
+
)
|
|
64
|
+
print(f"Plaintext: {decrypted.plaintext}")
|
|
65
|
+
|
|
66
|
+
# Sign a message using Dilithium3
|
|
67
|
+
sig_result = client.signatures.sign(
|
|
68
|
+
message=b"Invoice #12345",
|
|
69
|
+
key_version=1,
|
|
70
|
+
)
|
|
71
|
+
print(f"Signature: {sig_result.signature.hex()}")
|
|
72
|
+
|
|
73
|
+
# Verify a signature
|
|
74
|
+
verify_result = client.signatures.verify(
|
|
75
|
+
message=b"Invoice #12345",
|
|
76
|
+
signature=sig_result.signature,
|
|
77
|
+
key_version=sig_result.key_version,
|
|
78
|
+
)
|
|
79
|
+
print(f"Valid: {verify_result.valid}")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## API Reference
|
|
83
|
+
|
|
84
|
+
### Client Initialization
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from qpher import Qpher
|
|
88
|
+
|
|
89
|
+
client = Qpher(
|
|
90
|
+
api_key="qph_live_your_api_key", # Required
|
|
91
|
+
base_url="https://api.qpher.ai", # Optional, default
|
|
92
|
+
timeout=30, # Optional, seconds
|
|
93
|
+
max_retries=3, # Optional
|
|
94
|
+
)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### KEM Operations (Kyber768)
|
|
98
|
+
|
|
99
|
+
#### Encrypt
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
result = client.kem.encrypt(
|
|
103
|
+
plaintext=b"secret data",
|
|
104
|
+
key_version=1,
|
|
105
|
+
mode="standard", # Optional: "standard" or "deterministic"
|
|
106
|
+
salt=b"...", # Required if mode="deterministic" (min 32 bytes)
|
|
107
|
+
)
|
|
108
|
+
# result.ciphertext: bytes
|
|
109
|
+
# result.key_version: int
|
|
110
|
+
# result.algorithm: str ("Kyber768")
|
|
111
|
+
# result.request_id: str
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### Decrypt
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
result = client.kem.decrypt(
|
|
118
|
+
ciphertext=encrypted_data,
|
|
119
|
+
key_version=1,
|
|
120
|
+
)
|
|
121
|
+
# result.plaintext: bytes
|
|
122
|
+
# result.key_version: int
|
|
123
|
+
# result.algorithm: str
|
|
124
|
+
# result.request_id: str
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Signature Operations (Dilithium3)
|
|
128
|
+
|
|
129
|
+
#### Sign
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
result = client.signatures.sign(
|
|
133
|
+
message=b"document to sign",
|
|
134
|
+
key_version=1,
|
|
135
|
+
)
|
|
136
|
+
# result.signature: bytes (3,293 bytes)
|
|
137
|
+
# result.key_version: int
|
|
138
|
+
# result.algorithm: str ("Dilithium3")
|
|
139
|
+
# result.request_id: str
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
#### Verify
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
result = client.signatures.verify(
|
|
146
|
+
message=b"document to sign",
|
|
147
|
+
signature=signature_bytes,
|
|
148
|
+
key_version=1,
|
|
149
|
+
)
|
|
150
|
+
# result.valid: bool
|
|
151
|
+
# result.key_version: int
|
|
152
|
+
# result.algorithm: str
|
|
153
|
+
# result.request_id: str
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Key Management
|
|
157
|
+
|
|
158
|
+
#### Generate Key
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
result = client.keys.generate(algorithm="Kyber768")
|
|
162
|
+
# result.key_version: int
|
|
163
|
+
# result.algorithm: str
|
|
164
|
+
# result.status: str ("active")
|
|
165
|
+
# result.public_key: bytes
|
|
166
|
+
# result.created_at: str
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
#### Rotate Key
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
result = client.keys.rotate(algorithm="Kyber768")
|
|
173
|
+
# result.key_version: int (new)
|
|
174
|
+
# result.old_key_version: int
|
|
175
|
+
# result.algorithm: str
|
|
176
|
+
# result.public_key: bytes
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
#### Get Active Key
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
key_info = client.keys.get_active(algorithm="Kyber768")
|
|
183
|
+
# key_info.key_version: int
|
|
184
|
+
# key_info.algorithm: str
|
|
185
|
+
# key_info.status: str
|
|
186
|
+
# key_info.public_key: bytes
|
|
187
|
+
# key_info.created_at: str
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
#### List Keys
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
result = client.keys.list(
|
|
194
|
+
algorithm="Kyber768", # Optional filter
|
|
195
|
+
status="active", # Optional filter: "active", "retired", "archived"
|
|
196
|
+
)
|
|
197
|
+
# result.keys: List[KeyInfo]
|
|
198
|
+
# result.total: int
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
#### Retire Key
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
result = client.keys.retire(algorithm="Kyber768", key_version=1)
|
|
205
|
+
# result.key_version: int
|
|
206
|
+
# result.status: str ("retired")
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Error Handling
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
from qpher import (
|
|
213
|
+
Qpher,
|
|
214
|
+
QpherError,
|
|
215
|
+
AuthenticationError,
|
|
216
|
+
ValidationError,
|
|
217
|
+
NotFoundError,
|
|
218
|
+
RateLimitError,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
try:
|
|
222
|
+
result = client.kem.encrypt(plaintext=b"data", key_version=99)
|
|
223
|
+
except NotFoundError as e:
|
|
224
|
+
print(f"Key not found: {e.message}")
|
|
225
|
+
print(f"Error code: {e.error_code}")
|
|
226
|
+
print(f"Request ID: {e.request_id}")
|
|
227
|
+
except RateLimitError as e:
|
|
228
|
+
print("Rate limit exceeded, please retry later")
|
|
229
|
+
except AuthenticationError as e:
|
|
230
|
+
print("Invalid API key")
|
|
231
|
+
except ValidationError as e:
|
|
232
|
+
print(f"Invalid input: {e.message}")
|
|
233
|
+
except QpherError as e:
|
|
234
|
+
print(f"API error: {e.message}")
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Error Types
|
|
238
|
+
|
|
239
|
+
| Exception | HTTP Status | Description |
|
|
240
|
+
|-----------|-------------|-------------|
|
|
241
|
+
| `AuthenticationError` | 401 | Invalid or missing API key |
|
|
242
|
+
| `ValidationError` | 400 | Invalid request parameters |
|
|
243
|
+
| `ForbiddenError` | 403 | Operation not allowed |
|
|
244
|
+
| `NotFoundError` | 404 | Resource not found |
|
|
245
|
+
| `RateLimitError` | 429 | Rate limit exceeded |
|
|
246
|
+
| `ServerError` | 500+ | Server-side errors |
|
|
247
|
+
| `TimeoutError` | 504 | Request timed out |
|
|
248
|
+
| `ConnectionError` | 503 | Connection failed |
|
|
249
|
+
|
|
250
|
+
## Supported Algorithms
|
|
251
|
+
|
|
252
|
+
| Algorithm | Type | Security Level |
|
|
253
|
+
|-----------|------|----------------|
|
|
254
|
+
| Kyber768 | KEM (Encryption) | NIST Level 3 |
|
|
255
|
+
| Dilithium3 | Digital Signatures | NIST Level 3 |
|
|
256
|
+
|
|
257
|
+
## License
|
|
258
|
+
|
|
259
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
260
|
+
|
|
261
|
+
## Links
|
|
262
|
+
|
|
263
|
+
- [Qpher Website](https://qpher.ai)
|
|
264
|
+
- [API Documentation](https://docs.qpher.ai)
|
|
265
|
+
- [GitHub Repository](https://github.com/qpher/qpher-python)
|
qpher-0.1.0/README.md
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# Qpher Python SDK
|
|
2
|
+
|
|
3
|
+
Official Python SDK for the [Qpher](https://qpher.ai) Post-Quantum Cryptography API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install qpher
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
|
|
13
|
+
- Python 3.9+
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from qpher import Qpher
|
|
19
|
+
|
|
20
|
+
# Initialize the client
|
|
21
|
+
client = Qpher(api_key="qph_live_your_api_key")
|
|
22
|
+
|
|
23
|
+
# Encrypt data using Kyber768 KEM
|
|
24
|
+
result = client.kem.encrypt(
|
|
25
|
+
plaintext=b"Hello, Quantum World!",
|
|
26
|
+
key_version=1,
|
|
27
|
+
)
|
|
28
|
+
print(f"Ciphertext: {result.ciphertext.hex()}")
|
|
29
|
+
|
|
30
|
+
# Decrypt data
|
|
31
|
+
decrypted = client.kem.decrypt(
|
|
32
|
+
ciphertext=result.ciphertext,
|
|
33
|
+
key_version=result.key_version,
|
|
34
|
+
)
|
|
35
|
+
print(f"Plaintext: {decrypted.plaintext}")
|
|
36
|
+
|
|
37
|
+
# Sign a message using Dilithium3
|
|
38
|
+
sig_result = client.signatures.sign(
|
|
39
|
+
message=b"Invoice #12345",
|
|
40
|
+
key_version=1,
|
|
41
|
+
)
|
|
42
|
+
print(f"Signature: {sig_result.signature.hex()}")
|
|
43
|
+
|
|
44
|
+
# Verify a signature
|
|
45
|
+
verify_result = client.signatures.verify(
|
|
46
|
+
message=b"Invoice #12345",
|
|
47
|
+
signature=sig_result.signature,
|
|
48
|
+
key_version=sig_result.key_version,
|
|
49
|
+
)
|
|
50
|
+
print(f"Valid: {verify_result.valid}")
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## API Reference
|
|
54
|
+
|
|
55
|
+
### Client Initialization
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from qpher import Qpher
|
|
59
|
+
|
|
60
|
+
client = Qpher(
|
|
61
|
+
api_key="qph_live_your_api_key", # Required
|
|
62
|
+
base_url="https://api.qpher.ai", # Optional, default
|
|
63
|
+
timeout=30, # Optional, seconds
|
|
64
|
+
max_retries=3, # Optional
|
|
65
|
+
)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### KEM Operations (Kyber768)
|
|
69
|
+
|
|
70
|
+
#### Encrypt
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
result = client.kem.encrypt(
|
|
74
|
+
plaintext=b"secret data",
|
|
75
|
+
key_version=1,
|
|
76
|
+
mode="standard", # Optional: "standard" or "deterministic"
|
|
77
|
+
salt=b"...", # Required if mode="deterministic" (min 32 bytes)
|
|
78
|
+
)
|
|
79
|
+
# result.ciphertext: bytes
|
|
80
|
+
# result.key_version: int
|
|
81
|
+
# result.algorithm: str ("Kyber768")
|
|
82
|
+
# result.request_id: str
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
#### Decrypt
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
result = client.kem.decrypt(
|
|
89
|
+
ciphertext=encrypted_data,
|
|
90
|
+
key_version=1,
|
|
91
|
+
)
|
|
92
|
+
# result.plaintext: bytes
|
|
93
|
+
# result.key_version: int
|
|
94
|
+
# result.algorithm: str
|
|
95
|
+
# result.request_id: str
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Signature Operations (Dilithium3)
|
|
99
|
+
|
|
100
|
+
#### Sign
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
result = client.signatures.sign(
|
|
104
|
+
message=b"document to sign",
|
|
105
|
+
key_version=1,
|
|
106
|
+
)
|
|
107
|
+
# result.signature: bytes (3,293 bytes)
|
|
108
|
+
# result.key_version: int
|
|
109
|
+
# result.algorithm: str ("Dilithium3")
|
|
110
|
+
# result.request_id: str
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### Verify
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
result = client.signatures.verify(
|
|
117
|
+
message=b"document to sign",
|
|
118
|
+
signature=signature_bytes,
|
|
119
|
+
key_version=1,
|
|
120
|
+
)
|
|
121
|
+
# result.valid: bool
|
|
122
|
+
# result.key_version: int
|
|
123
|
+
# result.algorithm: str
|
|
124
|
+
# result.request_id: str
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Key Management
|
|
128
|
+
|
|
129
|
+
#### Generate Key
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
result = client.keys.generate(algorithm="Kyber768")
|
|
133
|
+
# result.key_version: int
|
|
134
|
+
# result.algorithm: str
|
|
135
|
+
# result.status: str ("active")
|
|
136
|
+
# result.public_key: bytes
|
|
137
|
+
# result.created_at: str
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### Rotate Key
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
result = client.keys.rotate(algorithm="Kyber768")
|
|
144
|
+
# result.key_version: int (new)
|
|
145
|
+
# result.old_key_version: int
|
|
146
|
+
# result.algorithm: str
|
|
147
|
+
# result.public_key: bytes
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### Get Active Key
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
key_info = client.keys.get_active(algorithm="Kyber768")
|
|
154
|
+
# key_info.key_version: int
|
|
155
|
+
# key_info.algorithm: str
|
|
156
|
+
# key_info.status: str
|
|
157
|
+
# key_info.public_key: bytes
|
|
158
|
+
# key_info.created_at: str
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### List Keys
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
result = client.keys.list(
|
|
165
|
+
algorithm="Kyber768", # Optional filter
|
|
166
|
+
status="active", # Optional filter: "active", "retired", "archived"
|
|
167
|
+
)
|
|
168
|
+
# result.keys: List[KeyInfo]
|
|
169
|
+
# result.total: int
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
#### Retire Key
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
result = client.keys.retire(algorithm="Kyber768", key_version=1)
|
|
176
|
+
# result.key_version: int
|
|
177
|
+
# result.status: str ("retired")
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Error Handling
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from qpher import (
|
|
184
|
+
Qpher,
|
|
185
|
+
QpherError,
|
|
186
|
+
AuthenticationError,
|
|
187
|
+
ValidationError,
|
|
188
|
+
NotFoundError,
|
|
189
|
+
RateLimitError,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
result = client.kem.encrypt(plaintext=b"data", key_version=99)
|
|
194
|
+
except NotFoundError as e:
|
|
195
|
+
print(f"Key not found: {e.message}")
|
|
196
|
+
print(f"Error code: {e.error_code}")
|
|
197
|
+
print(f"Request ID: {e.request_id}")
|
|
198
|
+
except RateLimitError as e:
|
|
199
|
+
print("Rate limit exceeded, please retry later")
|
|
200
|
+
except AuthenticationError as e:
|
|
201
|
+
print("Invalid API key")
|
|
202
|
+
except ValidationError as e:
|
|
203
|
+
print(f"Invalid input: {e.message}")
|
|
204
|
+
except QpherError as e:
|
|
205
|
+
print(f"API error: {e.message}")
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Error Types
|
|
209
|
+
|
|
210
|
+
| Exception | HTTP Status | Description |
|
|
211
|
+
|-----------|-------------|-------------|
|
|
212
|
+
| `AuthenticationError` | 401 | Invalid or missing API key |
|
|
213
|
+
| `ValidationError` | 400 | Invalid request parameters |
|
|
214
|
+
| `ForbiddenError` | 403 | Operation not allowed |
|
|
215
|
+
| `NotFoundError` | 404 | Resource not found |
|
|
216
|
+
| `RateLimitError` | 429 | Rate limit exceeded |
|
|
217
|
+
| `ServerError` | 500+ | Server-side errors |
|
|
218
|
+
| `TimeoutError` | 504 | Request timed out |
|
|
219
|
+
| `ConnectionError` | 503 | Connection failed |
|
|
220
|
+
|
|
221
|
+
## Supported Algorithms
|
|
222
|
+
|
|
223
|
+
| Algorithm | Type | Security Level |
|
|
224
|
+
|-----------|------|----------------|
|
|
225
|
+
| Kyber768 | KEM (Encryption) | NIST Level 3 |
|
|
226
|
+
| Dilithium3 | Digital Signatures | NIST Level 3 |
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
231
|
+
|
|
232
|
+
## Links
|
|
233
|
+
|
|
234
|
+
- [Qpher Website](https://qpher.ai)
|
|
235
|
+
- [API Documentation](https://docs.qpher.ai)
|
|
236
|
+
- [GitHub Repository](https://github.com/qpher/qpher-python)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "qpher"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Official Python SDK for the Qpher Post-Quantum Cryptography API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [{ name = "Qpher", email = "sdk@qpher.ai" }]
|
|
13
|
+
keywords = ["post-quantum", "cryptography", "pqc", "kyber", "dilithium", "encryption", "signatures"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.9",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Topic :: Security :: Cryptography",
|
|
24
|
+
]
|
|
25
|
+
dependencies = ["requests>=2.31.0"]
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
dev = [
|
|
29
|
+
"pytest>=7.4.0",
|
|
30
|
+
"pytest-cov>=4.1.0",
|
|
31
|
+
"responses>=0.24.0",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://qpher.ai"
|
|
36
|
+
Documentation = "https://docs.qpher.ai/sdks/python"
|
|
37
|
+
Repository = "https://github.com/qpher/qpher-python"
|
|
38
|
+
Issues = "https://github.com/qpher/qpher-python/issues"
|
|
39
|
+
|
|
40
|
+
[tool.pytest.ini_options]
|
|
41
|
+
testpaths = ["tests"]
|
|
42
|
+
asyncio_mode = "auto"
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build.targets.wheel]
|
|
45
|
+
packages = ["qpher"]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Qpher Python SDK - Official client for the Qpher Post-Quantum Cryptography API."""
|
|
2
|
+
|
|
3
|
+
from qpher.client import Qpher
|
|
4
|
+
from qpher.errors import (
|
|
5
|
+
QpherError,
|
|
6
|
+
AuthenticationError,
|
|
7
|
+
RateLimitError,
|
|
8
|
+
NotFoundError,
|
|
9
|
+
ValidationError,
|
|
10
|
+
ForbiddenError,
|
|
11
|
+
ServerError,
|
|
12
|
+
TimeoutError,
|
|
13
|
+
ConnectionError,
|
|
14
|
+
)
|
|
15
|
+
from qpher.types import (
|
|
16
|
+
EncryptResult,
|
|
17
|
+
DecryptResult,
|
|
18
|
+
SignResult,
|
|
19
|
+
VerifyResult,
|
|
20
|
+
KeyInfo,
|
|
21
|
+
KeyListResult,
|
|
22
|
+
RotateResult,
|
|
23
|
+
GenerateResult,
|
|
24
|
+
RetireResult,
|
|
25
|
+
)
|
|
26
|
+
from qpher._version import __version__
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
# Main client
|
|
30
|
+
"Qpher",
|
|
31
|
+
# Errors
|
|
32
|
+
"QpherError",
|
|
33
|
+
"AuthenticationError",
|
|
34
|
+
"RateLimitError",
|
|
35
|
+
"NotFoundError",
|
|
36
|
+
"ValidationError",
|
|
37
|
+
"ForbiddenError",
|
|
38
|
+
"ServerError",
|
|
39
|
+
"TimeoutError",
|
|
40
|
+
"ConnectionError",
|
|
41
|
+
# Types
|
|
42
|
+
"EncryptResult",
|
|
43
|
+
"DecryptResult",
|
|
44
|
+
"SignResult",
|
|
45
|
+
"VerifyResult",
|
|
46
|
+
"KeyInfo",
|
|
47
|
+
"KeyListResult",
|
|
48
|
+
"RotateResult",
|
|
49
|
+
"GenerateResult",
|
|
50
|
+
"RetireResult",
|
|
51
|
+
# Version
|
|
52
|
+
"__version__",
|
|
53
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|