Verify KDPika payload authenticity in Python without losing the server's signed numeric tokens.
What you will learn
- Build and run the complete Python verifier.
- Reject altered, ambiguous and untrusted envelopes.
- Keep signature checking separate from permission to use a license.
Prerequisites
- Read signed responses.
- Install Python 3.9+; cryptography 46.0.3.
- Download the complete verification source archive and extract it.
- Open a terminal in the extracted security directory; all paths below are relative to it.
- Use Python 3.9+ only for the optional shared test runner, not as a dependency of this verifier.
Install and build
python3 -m venv .venv
. .venv/bin/activate
python -m pip install cryptography==46.0.3
Expected result: dependencies resolve and the build exits successfully. Version commands print the installed runtime. Use a maintained runtime compatible with the minimum; these are tested compatibility floors, not claims of ongoing vendor support.
The program uses no OS-specific identity or machine hardware identifier. File paths and executable suffixes may differ on Windows; use your shell's path quoting.
Run the positive and negative cases
python python/verify.py vectors/positive.key.json vectors/positive.envelope.json
python python/verify.py vectors/tampered.key.json vectors/tampered.envelope.json
python python/verify.py vectors/expired.key.json vectors/expired.envelope.json
python3 selftest.py --command python python/verify.py
Expected output for the first three commands is signature_valid=true, signature_valid=false and signature_valid=true. The tampered command exits 1 intentionally; run commands separately rather than stopping a batch at that expected refusal. The shared runner prints PASS23 and exits 0 when every expected acceptance/refusal matches.
Expired remains cryptographically authentic but must not unlock access after its expiry. This program returns signature validity only. It does not replace a complete license client, network transport, offline policy or the official SDK.
Complete source
"""Verify a raw KDPika envelope, not permission to use a license.
Python 3.9+, cryptography==46.0.3. Run: python verify.py key.json envelope.json
Output: signature_valid=true (exit 0) or signature_valid=false (exit 1).
Keep key.json separately trusted; never take it from the untrusted envelope.
"""
import base64
import hashlib
import json
import re
import sys
from pathlib import Path
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
class Number(str):
"""Retain the original server JSON numeric token, including 19.0."""
def pairs(items):
result = {}
for key, value in items:
if key in result:
raise ValueError('Duplicate JSON property')
result[key] = value
return result
def parse(raw):
if len(raw) > 1048576:
raise ValueError('JSON exceeds 1 MiB')
return json.loads(raw.decode('utf-8'), parse_int=Number, parse_float=Number,
parse_constant=lambda value: (_ for _ in ()).throw(ValueError(value)),
object_pairs_hook=pairs)
def quote(value):
value.encode('utf-8', errors='strict')
return json.dumps(value, ensure_ascii=False).replace('\u2028', '\\u2028').replace('\u2029', '\\u2029')
def canonical(value, depth=0):
if depth > 64:
raise ValueError('JSON exceeds 64 levels')
if isinstance(value, Number):
return str(value)
if isinstance(value, dict):
return '{' + ','.join(quote(k) + ':' + canonical(value[k], depth + 1)
for k in sorted(value, key=lambda k: k.encode('utf-8'))) + '}'
if isinstance(value, list):
return '[' + ','.join(canonical(v, depth + 1) for v in value) + ']'
if isinstance(value, str):
return quote(value)
if value is None:
return 'null'
if isinstance(value, bool):
return 'true' if value else 'false'
raise ValueError('Unsupported JSON value')
def decode(value, size):
if not isinstance(value, str) or not re.fullmatch(r'[A-Za-z0-9_-]+', value):
raise ValueError('Invalid base64url')
raw = base64.urlsafe_b64decode(value + '=' * (-len(value) % 4))
if len(raw) != size or base64.urlsafe_b64encode(raw).decode().rstrip('=') != value:
raise ValueError('Invalid base64url size or padding bits')
return raw
def verify_envelope(trusted_key_raw, envelope_raw):
"""False on malformed data or signature failure; does not check license policy."""
try:
key, env = parse(trusted_key_raw), parse(envelope_raw)
public = decode(key['public_key'], 32)
key_id = 'ed25519_' + hashlib.sha256(public).hexdigest()[:16]
if (key['algorithm'] != 'Ed25519' or key['encoding'] != 'base64url'
or key['key_id'] != key_id or env['algorithm'] != 'Ed25519'
or env['key_id'] != key_id or not isinstance(env['payload'], dict)):
return False
Ed25519PublicKey.from_public_bytes(public).verify(
decode(env['signature'], 64), canonical(env['payload']).encode('utf-8'))
return True
except (Exception, RecursionError):
return False
if __name__ == '__main__':
try:
if len(sys.argv) != 3:
raise ValueError('Expected trusted key and envelope paths')
valid = verify_envelope(Path(sys.argv[1]).read_bytes(), Path(sys.argv[2]).read_bytes())
except Exception:
valid = False
print('signature_valid=' + str(valid).lower())
sys.exit(0 if valid else 1)
Expected result
All 23 cases pass in the shared runner, including integral floats, non-BMP key order, Unicode separators, duplicate JSON names, invalid signatures and excessive depth. The original response's numeric tokens are retained, not recalculated from binary floating-point values.
Common mistakes
- Taking the verification key from the same untrusted message being checked.
- Treating a successful signature as proof of active access, correct installation or recent payment.
- Parsing and reserializing numbers before passing the envelope into this sample.
Security notes
The key input must come from your trusted pin or deliberate fixed-origin HTTPS trust policy. Public vector keys are test anchors, never production configuration. This sample fails closed on malformed data; never fall back to unverified features.
After signature success, check product, environment, installation hash, active state, start/expiry, application-version policy and signed validation/grace deadlines. See offline mode. The current official SDKs have documented canonicalization limitations; this independent verifier does not silently repair their caches or alter their public APIs.
Troubleshooting
| Symptom | Cause | Action |
|---|---|---|
| Positive case fails | Wrong trusted key, changed numeric tokens or missing dependency | Restore the exact archive/vector files and rerun the build |
| Tampered case returns false | Expected rejection | Keep the refusal; do not bypass verification |
| Expired case returns true | Authenticity and expiry are separate | Reject access in the license policy layer |
| Cannot open a file | Terminal is outside the extracted security directory | Change to that directory and retry using quoted paths |
Next steps
Run all test vectors and apply the signed-response policy checks.