Skip to content

v2.0.0 - post-quantum rewrite

Choose a tag to compare

@AdmWalker AdmWalker released this 18 Sep 07:42
· 31 commits to master since this release
d9697ee

A full rewrite. The previous version predated post-quantum identities and still declared support for PHP 5.6. It also could not produce the bytes the ledger signs: transaction JSON went through json_encode, which escapes forward slashes as \/ by default — and every base64 public key is full of them — so an onboard signed the wrong bytes and was rejected as 1220.

Post-quantum support

Key type Wire string Public Signature
ML-DSA-65 ml-dsa-65 1952 3309

Implemented with paragonie/pqcrypto_compat and verified against the vectors published by the ledger repository: all six reference signatures verify here. Pure PHP — no extension and no native library required. ext-pqcrypto is used automatically when it is installed.

Signing is hedged, matching the reference implementation, so signatures must be verified rather than compared.

Performance in pure PHP with no extension: key generation ~28 ms, signing ~68 ms, verification ~27 ms.

⚠️ Private keys are not portable

pqcrypto_compat implements FIPS 204 key generation from a 32-byte seed but not private key encoding or decoding, while the JavaScript, JVM, C#, Go and Rust SDKs all store the 4032-byte key.

An identity created here works everywhere, because the public key and the signatures are in the standard encodings. But a private key cannot cross in either direction — store seedBase64() to reuse a PHP identity.

The SDK refuses a 4032-byte key by name, because the library underneath does not. It accepts any string as seed material, derives a completely unrelated identity and signs happily; those signatures are then rejected as 1220 "Signature Incorrect", which points nowhere near the cause.

Also in this release

  • Canonical JSON, reproducing JSON.stringify exactly — including the PHP-specific trap that [] is both an empty array and an empty object while $o must be {}.
  • Transaction builder, client with $r / returnToRemote reads, and SSE subscriptions for both feeds.
  • PSR-4 under Activeledger\, PHP 8.1 through 8.4.
  • A rejected subscription now throws instead of becoming an empty stream, and a requested subscription timeout is treated as a normal ending rather than a transport failure.

Event streams are served by Activecore, a separate service from the node — a node answers 403 for every event route — so the core URL is supplied separately and a missing one is reported as such.

Testing

82 tests, on PHP 8.1, 8.2, 8.3 and 8.4. The live suite runs against a real 4-node network: it onboards post-quantum identities, asserts what the ledger recorded on every node rather than what it reported, confirms a tampered payload is rejected, and checks that an identity restored from its stored seed still controls its stream.

Breaking changes

The old lowercase classes are gone and RSA and EC key generation are not carried over. The README has a migration table, and the Signer interface is available for signing with anything else.

Not yet published to Packagist under the new version — install from the tag, or via composer require activeledger/sdk:^2 once it is.