// kit 01

PQC Toolkit

Production-ready post-quantum crypto. Pick stack, pick primitive, copy the code.

$ pqc_jwt.php
L3
PHP
1<?php
2declare(strict_types=1);
3require __DIR__ . '/PqcSign.php'; // FFI wrapper for ML-DSA-65 via liboqs
4 
5final class HybridJwt
6{
7 public function __construct(
8 private string $edPriv,
9 private string $edPub,
10 private string $pqPriv,
11 private string $pqPub,
12 private PqcSign $pq,
13 ) {}
14 
15 public function issue(array $claims): string
16 {
17 $header = ['alg' => 'EdDSA+ML-DSA-65', 'typ' => 'JWT-HYBRID'];
18 $payload = $claims + ['iat' => time()];
19 $hb = self::b64(json_encode($header, JSON_UNESCAPED_SLASHES));
20 $pb = self::b64(json_encode($payload, JSON_UNESCAPED_SLASHES));
21 $msg = "$hb.$pb";
22 $sigEd = sodium_crypto_sign_detached($msg, $this->edPriv);
23 $sigPq = $this->pq->sign($msg, $this->pqPriv);
24 return "$msg." . self::b64($sigEd) . '.' . self::b64($sigPq);
25 }
26 
27 public function verify(string $token): array | null
28 {
29 $parts = explode('.', $token);
30 if (count($parts) !== 4) return null;
31 [$hb, $pb, $sed, $spq] = $parts;
32 $msg = "$hb.$pb";
33 if (!sodium_crypto_sign_verify_detached(self::ub64($sed), $msg, $this->edPub)) return null;
34 if (!$this->pq->verify($msg, self::ub64($spq), $this->pqPub)) return null;
35 return json_decode(self::ub64($pb), true);
36 }
37 
38 private static function b64(string $b): string { return rtrim(strtr(base64_encode($b), '+/', '-_'), '='); }
39 private static function ub64(string $s): string { return base64_decode(strtr($s, '-_', '+/')); }
40}
KEY SIZE
1952 B pub
SIG / CT
3293 B sig
NIST LEVEL
L3
NOTES

Hybrid JWT verification in PHP using libsodium + FFI to liboqs.

  • Verification cost via FFI is ~2 ms.
  • Cache the parsed PQ public key as a typed FFI array via APCu.
  • Verify the token once at the gateway and pass a small claim downstream.