Replies: 1 comment 1 reply
|
For a v1 format, I would keep your canonical framing as the mandatory cross-algorithm contract rather than make verification semantics depend on optional context-string support. For ML-DSA interoperability, however, sign(SHA3-512(file)) with pure ML-DSA should be treated as a pqsum-specific scheme, not HashML-DSA: FIPS 204 defines separate domain separation and pre-hash identification for HashML-DSA. Supporting that standardized path explicitly would avoid two constructions sharing an ambiguous label. I would also persist pqsum-owned numeric identifiers and map them to liboqs names, instead of making implementation strings part of the permanent file format. Zeroizing the Rust-owned key buffer is good, but document that guarantee only for memory pqsum controls; avoid promising that every temporary allocation below the FFI boundary is wiped. References: FIPS 204 and the liboqs signature API. |
Uh oh!
There was an error while loading. Please reload this page.
Hi all,
I've built
pqsum, a small Rust CLI that uses liboqs (vialiboqs-rust) to givesha256sum-style ergonomics for post-quantum file signatures:It exposes ML-DSA 44/65/87, all twelve SLH-DSA parameter sets and Falcon 512/1024, plus a signed-manifest mode that covers file names as well as contents. Files are streamed through a digest so a 2 GiB image signs in about 3.5 MiB of RAM.
To be clear about scope: there is no new cryptography here. liboqs does all key generation, signing and verification. pqsum is framing, file formats and CLI behaviour — which is precisely why I'd like a second opinion on the two places where I made a construction choice rather than following a standard.
Repo: https://github.com/iA7maz/pqsum · Release: https://github.com/iA7maz/pqsum/releases/tag/v0.1.0
1. I rolled my own domain separation instead of using the context string
Rather than signing file bytes, pqsum signs a canonical, length-prefixed encoding, through the plain
OQS_SIG_sign:The intent is that a signature can't be relabelled as covering a different digest algorithm, or replayed into a different context (manifests and key fingerprints use distinct prefixes).
Since writing that I noticed
OQS_SIG_sign_with_ctx_strand FIPS 204'sctxparameter, which is the standard's own mechanism for exactly this.Question: should a downstream tool prefer the context string over an in-message prefix? My hesitation is that context support isn't uniform across schemes, so using it would make pqsum's behaviour algorithm-dependent, and I'd need a fallback for algorithms without it — which reintroduces the bespoke encoding for some subset. Is there a recommended way to handle that heterogeneity, or a pattern other OQS-adjacent tools have settled on?
2. Bespoke pre-hash rather than HashML-DSA
pqsum streams the file through SHA3-512 and signs the digest with pure ML-DSA. FIPS 204 standardises HashML-DSA for the pre-hashed case, with a defined encoding of the digest OID and value.
Question: is a bespoke digest-then-pure-sign construction a mistake when a standardised pre-hash variant exists? I chose it for uniformity — the same code path drives ML-DSA, SLH-DSA and Falcon, and the digest algorithm is recorded in the signature file so verification is self-describing. But it does mean pqsum signatures aren't interoperable with anything that implements HashML-DSA properly, and I'd rather understand the cost now than after people depend on the format.
3. Secret key memory
Secret keys are held in zeroizing buffers on the Rust side and are never rendered in
Debugoutput. Is there anything liboqs-specific I should be doing beyond that — does liboqs zero its own internal buffers on free, and is there a recommended practice around howoqs-syshands back secret key material?4. Are algorithm identifier strings safe to persist?
Signature files record the algorithm as the liboqs name (
ML-DSA-65,SPHINCS+-SHA2-128f-simple) so that adding schemes needs no format change. A test asserts the internal table matchesAlgorithm::name()so a rename is caught at build time rather than by writing files nobody can read back.Question: are those names intended as stable identifiers across liboqs releases, or is persisting them on disk relying on something I shouldn't?
On maturity
pqsum is 0.1.0 and unaudited, and its SECURITY.md says so plainly, along with what it does not do: no key distribution, no revocation or expiry, and private keys unencrypted at rest. It also carries liboqs's own note that the library is research-quality and that its algorithm implementations have not all had the review that, say, OpenSSL's have — I'd rather that reach users through my docs than surprise them later.
Happy to go into detail on any of the above, and grateful for the work that made this possible in the first place.
All reactions