Repository navigation
v2.1.0 - secp256k1 support
Adds secp256k1 alongside ML-DSA-65, using ext-openssl — no new Composer package, and no ext-gmp.
Use it unless the identity must outlive a cryptographically relevant quantum computer: roughly 22× smaller per transaction, works with hardware wallets and HSMs, and it's the only way to sign for an identity created before post-quantum support.
It also closes the gap this SDK had alone. ML-DSA private keys are not portable here, because the underlying library only does seeds. secp256k1 private keys are — so a PHP caller can share one identity with every other Activeledger SDK.
PHP's OpenSSL is the same implementation the ledger verifies with, so interop is structural rather than hopeful.
Measured against the published vectors
openssl_verifyaccepts all 7 high-S signatures — what's needed, since the ledger produces high-S freely and a verifier enforcing low-S would reject roughly half of everything it makes.- Public keys build from raw SEC1 bytes in both forms, and private keys from a bare 32-byte scalar.
Signing folds S low, because OpenSSL doesn't normalise and @noble/curves rejects high-S by default.
⚠️ One difference from the other SDKs
Signing is not RFC 6979 deterministic — OpenSSL uses a random k with no way to inject one, so this SDK cannot reproduce the published reference bytes.
That costs a test, not correctness: everything it emits is a valid, canonical, low-S signature, and the suite verifies the published deterministic signatures even though it cannot reproduce them.
Encoding
- Keys are
0x-prefixed hex, not base64 — the prefix is required. - Two valid public key lengths: 33 compressed (default), 65 uncompressed.
- Private scalars are always 32 bytes.
bitcoin and ethereum parse as secp256k1 and are never emitted.
⚠️ Breaking
Signer::publicKeyBase64 → Signer::publicKey.
Testing
97 unit tests across PHP 8.1–8.5, plus 10 live tests against a real 4-node network onboarding both public key forms.