KDPika Licensing Sign inStart free
API v1
Show all languages
Browse documentation

Choose a page in the navigation drawer.

KDPika Licensing · API v1 Advanced

Python signature verification

Run the complete Python Ed25519 verifier against KDPika's real signed JSON and positive, tampered, malformed and expired test cases.

On this page
Updated 2026-10-17 UTCDownload Markdown

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

bashbash
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

bashbash
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.pypython
"""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

SymptomCauseAction
Positive case failsWrong trusted key, changed numeric tokens or missing dependencyRestore the exact archive/vector files and rerun the build
Tampered case returns falseExpected rejectionKeep the refusal; do not bypass verification
Expired case returns trueAuthenticity and expiry are separateReject access in the license policy layer
Cannot open a fileTerminal is outside the extracted security directoryChange to that directory and retry using quoted paths

Next steps

Run all test vectors and apply the signed-response policy checks.

Was this page helpful?

Choose an answer

Do not include personal data, license keys or credentials. Retained for up to 30 days. This is not a support inbox.

Search documentation

Arrow keys to move, Enter to open, Escape to close. Search stays in your browser.

Cookie settings