fipsign-sdk 0.5.2__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.
- fipsign_sdk-0.5.2/LICENSE +21 -0
- fipsign_sdk-0.5.2/PKG-INFO +414 -0
- fipsign_sdk-0.5.2/README.md +369 -0
- fipsign_sdk-0.5.2/fipsign/__init__.py +14 -0
- fipsign_sdk-0.5.2/fipsign/async_client.py +238 -0
- fipsign_sdk-0.5.2/fipsign/client.py +443 -0
- fipsign_sdk-0.5.2/fipsign/errors.py +36 -0
- fipsign_sdk-0.5.2/fipsign/middleware.py +234 -0
- fipsign_sdk-0.5.2/fipsign/py.typed +0 -0
- fipsign_sdk-0.5.2/fipsign/types.py +178 -0
- fipsign_sdk-0.5.2/fipsign/webhooks.py +119 -0
- fipsign_sdk-0.5.2/fipsign_sdk.egg-info/PKG-INFO +414 -0
- fipsign_sdk-0.5.2/fipsign_sdk.egg-info/SOURCES.txt +17 -0
- fipsign_sdk-0.5.2/fipsign_sdk.egg-info/dependency_links.txt +1 -0
- fipsign_sdk-0.5.2/fipsign_sdk.egg-info/requires.txt +21 -0
- fipsign_sdk-0.5.2/fipsign_sdk.egg-info/top_level.txt +1 -0
- fipsign_sdk-0.5.2/pyproject.toml +62 -0
- fipsign_sdk-0.5.2/setup.cfg +4 -0
- fipsign_sdk-0.5.2/tests/test_sdk.py +529 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 FIPSign
|
|
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
|
|
20
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
21
|
+
DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fipsign-sdk
|
|
3
|
+
Version: 0.5.2
|
|
4
|
+
Summary: Post-quantum signing SDK for Python — ML-DSA-65 (NIST FIPS 204)
|
|
5
|
+
Author-email: FIPSign <sdk@fipsign.dev>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://fipsign.dev
|
|
8
|
+
Project-URL: Dashboard, https://app.fipsign.dev
|
|
9
|
+
Project-URL: Guide, https://fipsign.dev/guide
|
|
10
|
+
Project-URL: Repository, https://github.com/fipsign/fipsign-sdk-python
|
|
11
|
+
Project-URL: Bug Tracker, https://github.com/fipsign/fipsign-sdk-python/issues
|
|
12
|
+
Keywords: post-quantum,cryptography,signing,ml-dsa,fips,pqc,auth
|
|
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
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.9
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: requests>=2.28
|
|
28
|
+
Provides-Extra: async
|
|
29
|
+
Requires-Dist: httpx>=0.24; extra == "async"
|
|
30
|
+
Provides-Extra: flask
|
|
31
|
+
Requires-Dist: flask>=2.0; extra == "flask"
|
|
32
|
+
Provides-Extra: fastapi
|
|
33
|
+
Requires-Dist: fastapi>=0.95; extra == "fastapi"
|
|
34
|
+
Requires-Dist: python-multipart>=0.0.6; extra == "fastapi"
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
37
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
|
|
38
|
+
Requires-Dist: httpx>=0.24; extra == "dev"
|
|
39
|
+
Requires-Dist: flask>=2.0; extra == "dev"
|
|
40
|
+
Requires-Dist: fastapi>=0.95; extra == "dev"
|
|
41
|
+
Requires-Dist: uvicorn>=0.20; extra == "dev"
|
|
42
|
+
Requires-Dist: build; extra == "dev"
|
|
43
|
+
Requires-Dist: twine; extra == "dev"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
# fipsign-sdk
|
|
47
|
+
|
|
48
|
+
Post-quantum signing SDK for Python.
|
|
49
|
+
|
|
50
|
+
Signs and verifies any payload using **ML-DSA-65** (NIST FIPS 204) — the post-quantum digital signature standard resistant to Shor's algorithm. Standardized by NIST in August 2024.
|
|
51
|
+
|
|
52
|
+
**Not just for auth.** Sign users, orders, documents, devices, events — any entity that needs a tamper-proof, quantum-resistant signature.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Install
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install fipsign-sdk
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
For async support (httpx-based):
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install fipsign-sdk[async]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Quick start
|
|
71
|
+
|
|
72
|
+
**1.** Create a free account at [app.fipsign.dev](https://app.fipsign.dev)
|
|
73
|
+
— enter your email, verify the OTP code sent to your inbox.
|
|
74
|
+
|
|
75
|
+
**2.** In the dashboard, create a project, then create an API key inside that project.
|
|
76
|
+
Save the key — it will not be shown again.
|
|
77
|
+
|
|
78
|
+
**3.** Use the key in your app:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from fipsign import PQAuth
|
|
82
|
+
|
|
83
|
+
pq = PQAuth("pqa_your_api_key")
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## sign() — Sign anything
|
|
89
|
+
|
|
90
|
+
The only required argument is `sub` — any string identifying the entity you want to sign. All other keyword arguments are stored in the payload and returned on verify. Cost: 1 token.
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
# Sign a user session
|
|
94
|
+
result = pq.sign("user_123", email="user@example.com", role="admin", expires_in_seconds=3600)
|
|
95
|
+
token = result.token
|
|
96
|
+
meta = result.meta
|
|
97
|
+
usage = result.usage
|
|
98
|
+
|
|
99
|
+
# Sign an order
|
|
100
|
+
result = pq.sign("order_456", amount=299.99, currency="USD", expires_in_seconds=300)
|
|
101
|
+
|
|
102
|
+
# Sign a document
|
|
103
|
+
result = pq.sign("doc_789", hash="sha256:abc...", signed_by="alice")
|
|
104
|
+
|
|
105
|
+
# Sign a device
|
|
106
|
+
result = pq.sign("device_iot_001", firmware="2.1.4")
|
|
107
|
+
|
|
108
|
+
# Monitor quota and token source
|
|
109
|
+
print(f"{usage.freeRemaining} free tokens remaining this month")
|
|
110
|
+
print(f"{usage.packRemaining} pack tokens remaining")
|
|
111
|
+
print(f"{usage.totalRemaining} total remaining")
|
|
112
|
+
print(f"charged from: {meta.source}") # "free" | "pack" | "free+pack"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### sign() response shape
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
SignResult
|
|
119
|
+
.token PQToken
|
|
120
|
+
.payload str # base64 encoded payload
|
|
121
|
+
.signature str # ML-DSA-65 signature
|
|
122
|
+
.algorithm str # "ML-DSA-65"
|
|
123
|
+
.issuedAt int # Unix timestamp
|
|
124
|
+
.meta SignMeta
|
|
125
|
+
.algorithm str
|
|
126
|
+
.standard str # "NIST FIPS 204"
|
|
127
|
+
.quantumResistant bool
|
|
128
|
+
.expiresIn int # seconds
|
|
129
|
+
.issuedFor str # your developer account email
|
|
130
|
+
.projectId str
|
|
131
|
+
.tokenCost int # always 1
|
|
132
|
+
.source str # "free" | "pack" | "free+pack"
|
|
133
|
+
.usage SignUsage
|
|
134
|
+
.freeRemaining int
|
|
135
|
+
.packRemaining int
|
|
136
|
+
.totalRemaining int
|
|
137
|
+
.month str # e.g. "2026-05"
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## verify() — Verify a token
|
|
143
|
+
|
|
144
|
+
**Never raises.** Returns a `VerifyResult` with `valid=False` and an `error` message on any failure.
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
result = pq.verify(token)
|
|
148
|
+
|
|
149
|
+
if not result.valid:
|
|
150
|
+
raise PermissionError(result.error)
|
|
151
|
+
|
|
152
|
+
print(result.payload["sub"]) # "user_123"
|
|
153
|
+
print(result.payload["exp"]) # expiry timestamp (Unix)
|
|
154
|
+
print(result.payload["iat"]) # issued at timestamp (Unix)
|
|
155
|
+
# All custom fields passed to sign() are in payload too
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## revoke() — Revoke a token
|
|
161
|
+
|
|
162
|
+
Immediately and permanently invalidates a token. Future `verify()` calls will reject it even if the signature is valid and it hasn't expired. Cost: 1 token.
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
pq.revoke(token, "user logged out")
|
|
166
|
+
pq.revoke(token, "order cancelled")
|
|
167
|
+
pq.revoke(token, "suspicious activity detected")
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Revoking an already-revoked token returns success without consuming an extra token — the operation is idempotent.
|
|
171
|
+
|
|
172
|
+
> **Note:** Calling `revoke()` on an already-expired token raises `PQAuthError(code="API_ERROR", status=400)`.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Flask middleware
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
from flask import Flask, g
|
|
180
|
+
from fipsign import PQAuth, flask_middleware
|
|
181
|
+
|
|
182
|
+
app = Flask(__name__)
|
|
183
|
+
pq = PQAuth("pqa_your_api_key")
|
|
184
|
+
auth = flask_middleware(pq)
|
|
185
|
+
|
|
186
|
+
@app.route("/login", methods=["POST"])
|
|
187
|
+
def login():
|
|
188
|
+
import base64, json
|
|
189
|
+
# authenticate user however you like, then:
|
|
190
|
+
result = pq.sign(user.id, email=user.email, role=user.role, expires_in_seconds=3600)
|
|
191
|
+
encoded = base64.b64encode(json.dumps(result.token.__dict__).encode()).decode()
|
|
192
|
+
return {"token": encoded}
|
|
193
|
+
|
|
194
|
+
@app.route("/logout", methods=["POST"])
|
|
195
|
+
def logout():
|
|
196
|
+
import base64, json
|
|
197
|
+
from flask import request
|
|
198
|
+
header = request.headers.get("Authorization", "")
|
|
199
|
+
if header.startswith("Bearer "):
|
|
200
|
+
from fipsign.types import PQToken
|
|
201
|
+
token = PQToken(**json.loads(base64.b64decode(header[7:]).decode()))
|
|
202
|
+
pq.revoke(token, "user logged out")
|
|
203
|
+
return {"success": True}
|
|
204
|
+
|
|
205
|
+
@app.route("/api/profile")
|
|
206
|
+
@auth
|
|
207
|
+
def profile():
|
|
208
|
+
return {"user": g.fipsign_user}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## FastAPI middleware
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
from fastapi import FastAPI, Depends
|
|
217
|
+
from fipsign import PQAuth, fastapi_middleware
|
|
218
|
+
|
|
219
|
+
app = FastAPI()
|
|
220
|
+
pq = PQAuth("pqa_your_api_key")
|
|
221
|
+
require_auth = fastapi_middleware(pq)
|
|
222
|
+
|
|
223
|
+
@app.get("/api/profile")
|
|
224
|
+
def profile(user=Depends(require_auth)):
|
|
225
|
+
return {"sub": user["sub"], "role": user.get("role")}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Async client
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
from fipsign.async_client import AsyncPQAuth
|
|
234
|
+
|
|
235
|
+
async with AsyncPQAuth("pqa_your_api_key") as pq:
|
|
236
|
+
result = await pq.sign("user_123", role="admin", expires_in_seconds=3600)
|
|
237
|
+
v = await pq.verify(result.token)
|
|
238
|
+
print(v.valid, v.payload["sub"])
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## usage() — Token balance
|
|
244
|
+
|
|
245
|
+
Free tokens reset on the 1st of each month (UTC). Pack tokens never expire and accumulate across purchases. No token cost.
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
u = pq.usage()
|
|
249
|
+
|
|
250
|
+
# Current balance
|
|
251
|
+
print(f"Month: {u.current.month}")
|
|
252
|
+
print(f"Free: {u.current.freeRemaining} / {u.current.freeLimit}")
|
|
253
|
+
print(f"Used: {u.current.freeUsed} this month")
|
|
254
|
+
print(f"Pack: {u.current.packRemaining}")
|
|
255
|
+
print(f"Total: {u.current.totalRemaining}")
|
|
256
|
+
print(f"Account: {u.developer['email']}")
|
|
257
|
+
|
|
258
|
+
# 6-month history (always 6 entries, months with no activity show 0)
|
|
259
|
+
for entry in u.monthlyHistory:
|
|
260
|
+
print(f"{entry.month}: {entry.tokensUsed} used ({entry.fromFree} free + {entry.fromPack} pack)")
|
|
261
|
+
|
|
262
|
+
# Purchased packs
|
|
263
|
+
from datetime import datetime
|
|
264
|
+
for pack in u.packs:
|
|
265
|
+
date = datetime.fromtimestamp(pack.purchasedAt).strftime("%Y-%m-%d")
|
|
266
|
+
print(f"{pack.packType}: {pack.tokensPurchased} tokens — {date}")
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## webhooks — Real-time notifications
|
|
272
|
+
|
|
273
|
+
**Events:** `token.signed` · `token.rejected` · `token.revoked` · `limit.warning` · `limit.reached`
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
# Register
|
|
277
|
+
result = pq.webhooks.register(
|
|
278
|
+
url="https://yourapp.com/webhooks/fipsign",
|
|
279
|
+
events=["limit.warning", "limit.reached", "token.revoked"],
|
|
280
|
+
)
|
|
281
|
+
print(result.webhook.secret) # store this — shown only once
|
|
282
|
+
|
|
283
|
+
# Send a test event
|
|
284
|
+
pq.webhooks.test()
|
|
285
|
+
|
|
286
|
+
# Get current config (secret is never returned after registration)
|
|
287
|
+
config = pq.webhooks.get()
|
|
288
|
+
if config.webhook is None:
|
|
289
|
+
print("No webhook configured")
|
|
290
|
+
|
|
291
|
+
# Delete
|
|
292
|
+
pq.webhooks.delete()
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Verifying incoming webhook requests
|
|
296
|
+
|
|
297
|
+
```python
|
|
298
|
+
from fipsign.middleware import verify_webhook_signature
|
|
299
|
+
|
|
300
|
+
# Flask
|
|
301
|
+
@app.route("/webhooks/fipsign", methods=["POST"])
|
|
302
|
+
def webhook():
|
|
303
|
+
from flask import request
|
|
304
|
+
sig = request.headers.get("X-PQAuth-Signature", "")
|
|
305
|
+
if not verify_webhook_signature(request.data, sig, WEBHOOK_SECRET):
|
|
306
|
+
return {"error": "Invalid signature"}, 401
|
|
307
|
+
event = request.json
|
|
308
|
+
if event["event"] == "limit.warning":
|
|
309
|
+
print(f"Warning — {event['data']['freeRemaining']} tokens left")
|
|
310
|
+
return "ok", 200
|
|
311
|
+
|
|
312
|
+
# FastAPI
|
|
313
|
+
from fastapi import Request, HTTPException
|
|
314
|
+
|
|
315
|
+
@app.post("/webhooks/fipsign")
|
|
316
|
+
async def webhook(request: Request):
|
|
317
|
+
body = await request.body()
|
|
318
|
+
sig = request.headers.get("X-PQAuth-Signature", "")
|
|
319
|
+
if not verify_webhook_signature(body, sig, WEBHOOK_SECRET):
|
|
320
|
+
raise HTTPException(401, detail="Invalid signature")
|
|
321
|
+
event = await request.json()
|
|
322
|
+
return "ok"
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Error handling
|
|
328
|
+
|
|
329
|
+
`verify()` never raises — it returns `VerifyResult(valid=False, error="...")` on any failure.
|
|
330
|
+
All other methods raise `PQAuthError` on failure.
|
|
331
|
+
|
|
332
|
+
```python
|
|
333
|
+
from fipsign import PQAuth, PQAuthError
|
|
334
|
+
|
|
335
|
+
try:
|
|
336
|
+
result = pq.sign("user_123")
|
|
337
|
+
except PQAuthError as err:
|
|
338
|
+
match err.code:
|
|
339
|
+
case "INVALID_API_KEY": # key missing or doesn't start with pqa_
|
|
340
|
+
...
|
|
341
|
+
case "API_ERROR": # server returned an error (check err.status)
|
|
342
|
+
...
|
|
343
|
+
case "TIMEOUT": # request exceeded timeout
|
|
344
|
+
...
|
|
345
|
+
case "NETWORK_ERROR": # connection failed
|
|
346
|
+
...
|
|
347
|
+
case "MISSING_SUB": # sign() called without sub
|
|
348
|
+
...
|
|
349
|
+
print(err.code, err.message, err.status)
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## Token quota
|
|
355
|
+
|
|
356
|
+
Every account gets **10,000 free tokens per month**, reset on the 1st (UTC). Unused free tokens do not carry over. Additional tokens are available as non-expiring packs, purchased from the dashboard.
|
|
357
|
+
|
|
358
|
+
Each of these operations costs **1 token**: signing, verification, and revocation. Checking usage and fetching the public key are free.
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## Rate limits
|
|
363
|
+
|
|
364
|
+
300 requests per minute per API key on `/sign`, `/verify`, and `/revoke`. On excess the API returns HTTP 429.
|
|
365
|
+
|
|
366
|
+
Token quota and rate limits are separate controls:
|
|
367
|
+
- `"Rate limit exceeded"` → back off and retry with exponential backoff
|
|
368
|
+
- `"Token limit reached"` → purchase a pack from the dashboard, retrying won't help
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## Constructor options
|
|
373
|
+
|
|
374
|
+
```python
|
|
375
|
+
pq = PQAuth(
|
|
376
|
+
api_key="pqa_...", # required — must start with pqa_
|
|
377
|
+
base_url="https://api.fipsign.dev", # optional, override for self-hosting
|
|
378
|
+
timeout=10, # optional, seconds (default: 10)
|
|
379
|
+
)
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
| Option | Type | Default | Description |
|
|
383
|
+
|---|---|---|---|
|
|
384
|
+
| `api_key` | str | — | Required. From the dashboard. Raises `INVALID_API_KEY` immediately if not prefixed with `pqa_`. |
|
|
385
|
+
| `base_url` | str | `https://api.fipsign.dev` | Override for local dev or self-hosted instances. |
|
|
386
|
+
| `timeout` | float | `10` | Request timeout in seconds. Raises `TIMEOUT` on exceeded. |
|
|
387
|
+
| `session` | requests.Session | — | Custom session (e.g. for proxies or custom TLS). |
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## Why ML-DSA-65?
|
|
392
|
+
|
|
393
|
+
JWT with RS256/ES256 and standard OAuth tokens use ECDSA or RSA — both vulnerable to Shor's algorithm running on a sufficiently powerful quantum computer. ML-DSA-65 is based on the hardness of lattice problems (Module-LWE / Module-SIS), which have no known quantum speedup. It was standardized by NIST in August 2024 as FIPS 204.
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## Integration tests
|
|
398
|
+
|
|
399
|
+
```bash
|
|
400
|
+
FIPSIGN_API_KEY=pqa_your_key \
|
|
401
|
+
WEBHOOK_URL=https://webhook.site/your-uuid \
|
|
402
|
+
WEBHOOK_SITE_TOKEN=your-uuid \
|
|
403
|
+
python tests/test_sdk.py
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## Links
|
|
409
|
+
|
|
410
|
+
- Dashboard: [app.fipsign.dev](https://app.fipsign.dev)
|
|
411
|
+
- Developer guide: [fipsign.dev/guide](https://fipsign.dev/guide)
|
|
412
|
+
- API status: [api.fipsign.dev/health](https://api.fipsign.dev/health)
|
|
413
|
+
- JS SDK: [npmjs.com/package/fipsign-sdk](https://www.npmjs.com/package/fipsign-sdk)
|
|
414
|
+
- NIST FIPS 204: [csrc.nist.gov/pubs/fips/204/final](https://csrc.nist.gov/pubs/fips/204/final)
|