(sigil crypto minisign)
(sigil crypto minisign) - Verify minisign signatures natively
Parses minisign public keys and .minisig signature files and verifies them with (sigil crypto)'s Ed25519 and BLAKE2b-512, so no external minisign binary is needed. Verification only; there is no signing.
(import (sigil crypto minisign) (sigil fs))
(define key (minisign-parse-public-key
"RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3"))
(define sig (minisign-parse-signature
(read-file-bytes "SHA256SUMS.minisig")))
(minisign-verify-prehashed key sig (read-file-bytes "SHA256SUMS"))
; => the trusted comment string when valid, #f otherwiseWhat is checked
The same things minisign -V checks, in the same order:
- The signature's key id equals the public key's key id.
- The signature over the file:
ED(prehashed, minisign's default since 0.10) is Ed25519 over BLAKE2b-512 of the file;Ed(legacy) is Ed25519 over the file itself.minisign-verify-prehashedrefusesEd, likeminisign -V -H. - The global signature: Ed25519 over the 64 signature bytes followed by the trusted comment's bytes. This is what makes the trusted comment trusted.
The trusted comment is only ever returned by a successful verification; the parsed signature does not expose it, so an unverified comment cannot be read by mistake.
Use minisign-verify-prehashed for anything new. The algorithm bytes are not signed, so a prehashed ED signature relabelled as legacy Ed verifies under minisign-verify as a signature over the 64-byte BLAKE2b digest of the file. That is also what minisign -V without -H accepts; refusing legacy signatures closes it.
The trusted comment comes back as a string made from its bytes as signed. If the signer put invalid UTF-8 there, the string carries those bytes; only the key holder can do that.
Ed25519 here accepts what RFC 8032 section 5.1.7 accepts (cofactored equation, canonical encodings, S < L) and rejects small-order keys and R values as libsodium does. Verification is cofactored, deliberately: RFC 8032 allows it and it agrees with batch verification. It accepts more than libsodium (which the minisign tool uses) only when the key or R has a small-order component: ed25519-speccheck cases 4 and 5 (a mixed-order key), and a signature under an honest key whose R carries a torsion component. Only the key holder can produce either, and minisign itself never does.
Parsing is strict
Any .minisig or minisign.pub file that minisign 0.12 rejects is rejected here, including its line-length limits (1022 bytes for a comment line, 8190 for the trusted comment line, a trailing CR counted). A few things it tolerates are rejected too: base64 must be canonical (standard alphabet, correct padding, zero pad bits), lines may end in LF or CRLF but the signature and trusted comment lines may not contain other CR or NUL bytes, and nothing but blank lines may follow the fourth line of a signature file. The bare key line given in place of a file may end in a newline. Parse errors raise; a well-formed signature that does not verify is not an error.
Every target, including the browser
This module and the natives under it build on native targets and on wasm32-wasi. The browser build deliberately uses the same compiled implementation rather than WebCrypto, to keep the API synchronous. The consumer that makes this load-bearing is Slate running in the browser and installing extension packages from the registry: there, verifying the signed index is the security boundary, because a hash check only proves the bytes match an index that an attacker could have served too. Verification needs no entropy and no clock, so it runs anywhere the module loads.
Exports
minisign-public-keyprocedureConstruct a minisign-public-key struct.
minisign-public-key?procedureTest if a value is a minisign-public-key struct.
minisign-public-key-raw-key-idprocedureGet the raw-key-id field of a minisign-public-key struct.
minisign-public-key-raw-bytesprocedureGet the raw-bytes field of a minisign-public-key struct.
%minisign-signature--typevariableGet the raw-bytes field of a minisign-public-key struct.
minisign-signatureprocedureConstruct a minisign-signature struct.
minisign-signature?procedureTest if a value is a minisign-signature struct.
minisign-signature-algorithmprocedureGet the algorithm field of a minisign-signature struct.
minisign-signature-raw-key-idprocedureGet the raw-key-id field of a minisign-signature struct.
minisign-signature-raw-bytesprocedureGet the raw-bytes field of a minisign-signature struct.
minisign-signature-commentprocedureGet the comment field of a minisign-signature struct.
minisign-signature-globalprocedureGet the global field of a minisign-signature struct.
minisign-parse-public-keyprocedureParse a minisign public key.
text (string or bytevector) is either the contents of a minisign.pub file (an untrusted comment: line first, then the key line) or the bare base64 key line, as given to minisign -P. Only blank lines may follow the key. Raises on anything else.
minisign-public-key-idprocedureThe key id of a parsed public key, as a 16-digit upper-case hex string in the order minisign prints it.
minisign-public-key-bytesprocedureThe raw 32-byte Ed25519 public key.
minisign-parse-signatureprocedureParse the contents of a .minisig file (string or bytevector).
Four lines: untrusted comment: ..., the base64 signature, trusted comment: ..., the base64 global signature. Raises when the file is malformed or uses an algorithm other than ED or Ed.
minisign-signature-key-idprocedureThe key id the signature names, as minisign-public-key-id formats it. Read from the signature file, so it is not authenticated.
minisign-failure-reasonprocedureCheck sig against message (string or bytevector) under key.
Returns #f when the signature verifies, and otherwise a symbol naming the first check that failed. #f for success is deliberate: a caller who tests the result for truth by mistake then rejects every valid signature, which the first real use shows, rather than accepting every invalid one, which nothing shows.
'key-id-mismatch: the signature names a different key'legacy-refused:prehashed-only?is true and the signature is a legacyEdone'bad-signature: the signature over the message is wrong'bad-trusted-comment-signature: the message verifies but the trusted comment has been altered
Most callers want minisign-verify or minisign-verify-prehashed, which return the authenticated trusted comment or #f.
minisign-verifyprocedureVerify like minisign -V: accepts prehashed ED and legacy Ed signatures. Returns the authenticated trusted comment (a string) when everything verifies, and #f otherwise.
minisign-verify-prehashedprocedureVerify like minisign -V -H: as minisign-verify, but a legacy Ed signature is refused (#f).