nqseal(3)
NAME
nqseal, nqsealwhy
— seal a folder of files into a signed
capsule
LIBRARY
libnoqtis, -lnoqtis
SYNOPSIS
#include
<noqtis.h>
int
nqseal(const char *dir,
nqsalg alg, const uint8_t *sk,
size_t sklen, const uint8_t *pk,
size_t pklen, const char *meta,
const char *tsa);
int
nqsealwhy(const
char *dir, int
*why, char *path,
size_t cap);
DESCRIPTION
nqseal()
writes the manifest noqtis.json and the seal
noqtis.sig into the folder dir
(a UTF-8 path on every operating system), making it a capsule as
noqtis-capsule(5)
defines.
It lists every entry under dir without
following links, leaving out noqtis.json and
noqtis.sig at the top; records each regular file's
path, size, SHA-256 and SHA3-256 in canonical JSON (RFC 8785), sorted by
path; adds meta; signs the exact manifest bytes with
alg and the secret key sk under
the context string ‘noqtis-capsule-1’
(pure ML-DSA or SLH-DSA, hedged); verifies the new signature with
pk; and only then writes both files, replacing earlier
ones.
sklen and pklen must be exactly the algorithm's key sizes (nqsign(3)), and the keys one pair: a seal that would not verify is never written.
meta is NULL or a
NUL-terminated JSON object of at most 1 MiB, stored in canonical form as the
manifest's ‘meta’ member and covered
by the signature; noqtis does not interpret it.
The creation time is the environment variable
SOURCE_DATE_EPOCH (decimal seconds since the epoch)
when it is set and not empty, otherwise the clock, as an RFC 3339 UTC time
to the second.
tsa must be NULL.
The library makes no network requests, so it cannot obtain an RFC 3161
timestamp itself; any other value returns NQENOSUP.
To add one, build the request with
nqtsreq(3), send
it to the TSA, and store the reply with
nqtsput(3), as
noqtis-seal(1)
--tsa does.
The capsule and parser layers allocate through the
hook of
nqsetal(3).
nqseal()
is not safe to call on the same folder from two threads or processes at
once.
When
nqseal()
or nqseal2(3)
returns NQEARG for a folder,
nqsealwhy()
says why: it sets *why to the first thing in
dir that a capsule cannot hold and copies its path,
relative to dir, into path
(cap bytes, always NUL-terminated when
cap is not 0, cut when too long):
NQSWLINK- a symbolic link or reparse point;
NQSWODD- not a regular file or folder (a device, a FIFO, a socket);
NQSWNAME- a name noqtis-capsule(5) refuses;
NQSWCASE- a path equal to another but for ASCII case;
NQSWKEY- a file that starts like a noqtis secret key (its first line
‘
noqtis-key-1 <alg> sec’), which would ship with the capsule.
*why is 0 when nothing in the folder stops a
seal; the NQEARG was then about the arguments, most
often keys that are not one pair.
RETURN VALUES
NQOK (0) when both files were written, or
a negative error code; nothing is written on any error:
- [
NQEARG] - dir, sk or
pk is
NULL; alg is unknown; a key length is not the algorithm's; the keys are not a pair;SOURCE_DATE_EPOCHis not a plain decimal number (or after the year 9999); or the folder holds a symbolic link, a reparse point, a special file, a refused name (empty, a control character, ‘/’, ‘\’, ‘:’, invalid UTF-8), two paths equal but for ASCII case, or a file that starts like a noqtis secret key;nqsealwhy() says which. - [
NQELIM] - More than 2,000,000 files, a path over 4096 bytes, folders more than 64 levels deep, a manifest over 512 MiB, a file over 2^53-1 bytes, or meta over 1 MiB or nested more than 64 levels deep.
- [
NQEDEC] - meta is not a JSON object (strict JSON: no duplicate keys, valid UTF-8).
- [
NQEIO] - The folder or a file could not be read, or a file could not be written.
- [
NQEMEM] - The allocation hook returned
NULL. - [
NQENOSUP] - tsa is not
NULL. - [
NQERNG] - The random source failed while signing.
nqsealwhy() returns
NQOK, NQEARG when
dir or why is
NULL (or path is with
cap not 0), NQEIO when the
folder or a file cannot be read, or NQELIM or
NQEMEM as nqseal() does.
ENVIRONMENT
SOURCE_DATE_EPOCH- The creation time, for reproducible capsules.
EXAMPLES
Seal a folder with a fresh ML-DSA-65 key, then check it with that key pinned (the program docs/examples/seal.c of the source tree):
static uint8_t pk[1952], sk[4032];
nqrep *rep = NULL;
int rc = nqskg(NQMLDSA65, pk, sizeof pk, sk, sizeof sk);
if (rc == NQOK)
rc = nqseal(argv[1], NQMLDSA65, sk, sizeof sk, pk, sizeof pk,
"{\"tool\":\"example\"}", NULL);
nqwipe(sk, sizeof sk);
if (rc == NQOK)
rc = nqchk(argv[1], pk, sizeof pk, &rep);
SEE ALSO
noqtis-seal(1), noqtis(3), nqchk(3), nqsign(3), nqtsput(3), noqtis-capsule(5)