| Title: | Post-Quantum Signing, Key Encapsulation and Sealed Folders |
|---|---|
| Description: | A post-quantum cryptography toolkit for R, built on the dependency-free 'NOQTIS' C library. Generate key pairs, then sign and verify with lattice-based 'ML-DSA', hash-based 'SLH-DSA', 'Ed25519' or hybrid 'ML-DSA'+'Ed25519' signatures that need both halves to verify; establish shared secrets with 'ML-KEM' or 'HQC' key encapsulation; and hash with 'SHA-2', 'SHA-3' or 'SHAKE', with 'HMAC'. A whole directory can be sealed under one signature and later verified offline; a Merkle proof shows chosen files to someone without revealing the others, and signed statements rotate or revoke a sealing key. Results can be wrapped in 'DSSE' envelopes. Keys and nonces come from the operating system's random generator, not from R's. |
| Authors: | Vansh Singh Ruhela [aut, cre, cph] (ORCID: <https://orcid.org/0009-0004-1750-3592>, GitHub: rootcoder007) |
| Maintainer: | Vansh Singh Ruhela <[email protected]> |
| License: | Apache License (== 2.0) | file LICENSE |
| Version: | 0.1.2 |
| Built: | 2026-10-31 07:13:18 UTC |
| Source: | https://github.com/rootcoder007/noqtis |
Lists every signature and key encapsulation algorithm the bundled library provides, with the sizes in bytes of its keys, signatures, ciphertexts and shared secrets.
nqalgs()nqalgs()
A data frame with one row per algorithm and the columns name,
type ("sig" or "kem"), pk, sk, sig, ct and ss (sizes
in bytes; NA where they do not apply) and id (the library's
number for the algorithm).
nqalgs()nqalgs()
nqcapid() gives a capsule's id: the SHA-256 of the bytes its seal
signs, which nqseal() records for its inputs. nqfiles() lists the
files a capsule's manifest holds. Neither checks anything: use
nqcheck() for that.
nqcapid(dir) nqfiles(dir) nqprove(dir, paths)nqcapid(dir) nqfiles(dir) nqprove(dir, paths)
dir |
Path of the capsule folder. |
paths |
The files to prove, as the manifest lists them ( |
nqprove() writes a proof (noqtis-proof-1, JSON) that some files are
in a format 2 capsule: the seal, the manifest's head and, per file, its
entry and Merkle audit path (RFC 9162). The proof holds nothing of the
capsule's other files. nqcheck(proof = ) checks it, alone or against
the files.
nqcapid(): a raw vector of 32 bytes. nqfiles(): a data
frame with the columns path, size and sha256 (64 hex digits),
in manifest order. nqprove(): the proof, a raw vector.
d <- file.path(tempdir(), "proved") dir.create(d) writeLines("one", file.path(d, "a.txt")) writeLines("two", file.path(d, "b.txt")) k <- nqkeygen("ML-DSA-44") nqseal(d, k$sk, k$pk, "ML-DSA-44", format = 2) nqfiles(d) p <- nqprove(d, "a.txt") attr(nqcheck(NULL, k$pk, proof = p), "ok") nqcapid(d) unlink(d, recursive = TRUE)d <- file.path(tempdir(), "proved") dir.create(d) writeLines("one", file.path(d, "a.txt")) writeLines("two", file.path(d, "b.txt")) k <- nqkeygen("ML-DSA-44") nqseal(d, k$sk, k$pk, "ML-DSA-44", format = 2) nqfiles(d) p <- nqprove(d, "a.txt") attr(nqcheck(NULL, k$pk, proof = p), "ok") nqcapid(d) unlink(d, recursive = TRUE)
nqdsse() wraps a payload in a DSSE envelope (the in-toto and SLSA
signing wrapper), signed with any signature algorithm of nqalgs()
over the envelope's pre-authentication encoding, as canonical JSON with
payload, payloadType and one signature whose keyid is the hex
SHA-256 of pk. nqdsseverify() checks one.
nqdsse(sk, pk, payload, type, alg) nqdsseverify(pk, env, alg)nqdsse(sk, pk, payload, type, alg) nqdsseverify(pk, env, alg)
sk, pk
|
The key pair ( |
payload |
The payload: a raw vector, or one string, taken as its UTF-8 bytes. |
type |
The payload type, 1 to 255 printable ASCII characters with
no space, quote or backslash, such as
|
alg |
Signature algorithm name. |
env |
The envelope: a raw vector, or one string. |
nqdsse(): the envelope, a raw vector. nqdsseverify(): a
list with type and payload (a raw vector) when one of the
envelope's signatures verifies under pk, NULL when none does; an
error when env is not an envelope.
k <- nqkeygen("ML-DSA-65") e <- nqdsse(k$sk, k$pk, '{"a":1}', "application/json", "ML-DSA-65") v <- nqdsseverify(k$pk, e, "ML-DSA-65") rawToChar(v$payload)k <- nqkeygen("ML-DSA-65") e <- nqdsse(k$sk, k$pk, '{"a":1}', "application/json", "ML-DSA-65") v <- nqdsseverify(k$pk, e, "ML-DSA-65") rawToChar(v$payload)
nqencap() makes a fresh shared secret and its ciphertext for a public
key (ML-KEM, FIPS 203, or HQC); nqdecap() recovers the shared secret
from the ciphertext with the secret key.
nqencap(pk, alg, seed = NULL) nqdecap(sk, ct, alg)nqencap(pk, alg, seed = NULL) nqdecap(sk, ct, alg)
pk |
Public key, a raw vector (as from |
alg |
Key encapsulation algorithm name, one of the |
seed |
|
sk |
Secret key, a raw vector. |
ct |
Ciphertext, a raw vector. |
Decapsulation uses implicit rejection, as FIPS 203 and HQC require: a changed ciphertext gives an unrelated pseudorandom secret, not an error. Only a key or ciphertext of the wrong length is an error.
nqencap(): a list with the raw vectors ct (ciphertext) and
ss (shared secret). nqdecap(): the shared secret, a raw vector.
k <- nqkeygen("ML-KEM-768") e <- nqencap(k$pk, "ML-KEM-768") identical(nqdecap(k$sk, e$ct, "ML-KEM-768"), e$ss)k <- nqkeygen("ML-KEM-768") e <- nqencap(k$pk, "ML-KEM-768") identical(nqdecap(k$sk, e$ct, "ML-KEM-768"), e$ss)
nqhash() computes a SHA-2 (FIPS 180-4), SHA-3 or SHAKE (FIPS 202)
digest of a raw vector, a string or a file; nqhmac() computes an HMAC.
nqhash(x, alg = "sha256", file = FALSE, hex = FALSE, len = NULL) nqhmac(key, x, alg = "sha256", hex = FALSE)nqhash(x, alg = "sha256", file = FALSE, hex = FALSE, len = NULL) nqhmac(key, x, alg = "sha256", hex = FALSE)
x |
A raw vector, or one string (hashed as its UTF-8 bytes), or,
with |
alg |
Hash name. |
file |
|
hex |
|
len |
For SHAKE only, the output length in bytes (default 32 for SHAKE128 and 64 for SHAKE256). |
key |
HMAC key, a raw vector or one string. |
Hash names: "sha224", "sha256", "sha384", "sha512",
"sha512-224", "sha512-256", "sha3-224", "sha3-256",
"sha3-384", "sha3-512", "shake128", "shake256" (case is
ignored).
The digest, a raw vector, or a string when hex = TRUE.
nqhash("abc", hex = TRUE) nqhash(charToRaw("abc"), "sha3-256") nqhash("abc", "shake128", len = 16) f <- tempfile() writeBin(charToRaw("abc"), f) nqhash(f, file = TRUE, hex = TRUE) unlink(f) nqhmac("key", "The quick brown fox jumps over the lazy dog", hex = TRUE)nqhash("abc", hex = TRUE) nqhash(charToRaw("abc"), "sha3-256") nqhash("abc", "shake128", len = 16) f <- tempfile() writeBin(charToRaw("abc"), f) nqhash(f, file = TRUE, hex = TRUE) unlink(f) nqhmac("key", "The quick brown fox jumps over the lazy dog", hex = TRUE)
An identity card (noqtis-idcard-1, canonical JSON) ties a signing
key to who holds it: a name and, when given, an ORCID iD, an e-mail
address, a web page and an affiliation, signed by the key itself under
the card's own context, so that a card is never taken for a seal or a
key statement. nqidcard() makes one; nqidcheck() checks one.
nqidcard( sk, pk, alg, name, orcid = NULL, email = NULL, url = NULL, affil = NULL, at = Sys.time() ) nqidcheck(card, pk = NULL)nqidcard( sk, pk, alg, name, orcid = NULL, email = NULL, url = NULL, affil = NULL, at = Sys.time() ) nqidcheck(card, pk = NULL)
sk, pk
|
The key pair the card is for; for |
alg |
Its signature algorithm. |
name |
Your name: 1 to 255 bytes of text, no control character, no space at either end. |
orcid |
|
email |
|
url |
|
affil |
|
at |
When the card is made: a |
card |
A card: a raw vector, or the path of a card file. |
A card is self-signed: it shows that whoever holds the key wrote it and
that it is unchanged, not that the name is true. Give the key's SHA-256
(key_sha256) where people already know you, or let readers pin your
public key with pk. The key must be post-quantum: Ed25519 alone is
refused. noqtis check --card names the sealer of a capsule from its
card.
nqidcard(): the card, a raw vector (write it with
writeBin()). nqidcheck(): a list with name, orcid, email,
url, affil (NULL for a field the card has not), created,
alg, pk and key_sha256 when the card verifies (and is for
pk); NULL when its signature does not verify or it is for another
key. Input that is not a card is an error.
k <- nqkeygen("ML-DSA-44") card <- nqidcard(k$sk, k$pk, "ML-DSA-44", "Ada Lovelace", orcid = "0000-0002-1825-0097") nqidcheck(card, k$pk)$orcidk <- nqkeygen("ML-DSA-44") card <- nqidcard(k$sk, k$pk, "ML-DSA-44", "Ada Lovelace", orcid = "0000-0002-1825-0097") nqidcheck(card, k$pk)$orcid
Generates a signature key pair (ML-DSA, SLH-DSA, Ed25519, the hybrids ML-DSA-44/65/87+Ed25519) or a key encapsulation key pair (ML-KEM, HQC).
nqkeygen(alg, seed = NULL)nqkeygen(alg, seed = NULL)
alg |
Algorithm name, one of |
seed |
|
A list with the raw vectors pk (public key) and sk (secret
key) in the standard's encoding, and alg, the algorithm name.
k <- nqkeygen("ML-DSA-44") length(k$pk) k2 <- nqkeygen("ML-KEM-512", seed = as.raw(0:63))k <- nqkeygen("ML-DSA-44") length(k$pk) k2 <- nqkeygen("ML-KEM-512", seed = as.raw(0:63))
Bytes from the operating system's cryptographically secure generator,
the same source key generation uses (see the Randomness section of
noqtis-package). Independent of set.seed().
nqrand(n)nqrand(n)
n |
Number of bytes. |
A raw vector of length n.
nqrand(16)nqrand(16)
nqreadkey() reads a key file written by noqtis keygen (or by
nqwritekey()), as strictly as the command-line tool reads it: the
exact header line noqtis-key-1 <algorithm> <pub|sec>, then the key as
padded base64 with LF or CRLF line ends and no other character,
canonical padding, and exactly the algorithm's key size.
nqwritekey() writes one.
nqreadkey(path) nqwritekey(path, key, alg, kind = c("sec", "pub"), force = FALSE)nqreadkey(path) nqwritekey(path, key, alg, kind = c("sec", "pub"), force = FALSE)
path |
The key file. |
key |
The key, a raw vector ( |
alg |
The algorithm name, one of |
kind |
|
force |
|
A secret key file is created readable by its owner only (on
Windows it takes the folder's permissions). An existing file is never
replaced unless force = TRUE. R's base64 here is not constant time:
read secret key files where timing cannot be observed.
nqreadkey(): a list with alg (the algorithm name as the
library spells it), kind ("pub" or "sec") and key (a raw
vector). nqwritekey(): path, invisibly.
k <- nqkeygen("ML-DSA-44") f <- tempfile() nqwritekey(f, k$sk, "ML-DSA-44", "sec") identical(nqreadkey(f)$key, k$sk) readLines(f, n = 1)k <- nqkeygen("ML-DSA-44") f <- tempfile() nqwritekey(f, k$sk, "ML-DSA-44", "sec") identical(nqreadkey(f)$key, k$sk) readLines(f, n = 1)
A key statement (noqtis-keys-1, canonical JSON) says that a signing
key is replaced by a new one from a given time (nqrotate(), signed by
both keys) or revoked from then (nqrevoke(), signed by the key).
nqcheck() follows statements from a trusted key with its keys
argument. Both algorithms must be post-quantum: Ed25519 alone is
refused.
nqrotate(sk, pk, alg, nsk, npk, nalg, at = Sys.time(), reason = NULL) nqrevoke(sk, pk, alg, at = Sys.time(), reason = NULL)nqrotate(sk, pk, alg, nsk, npk, nalg, at = Sys.time(), reason = NULL) nqrevoke(sk, pk, alg, at = Sys.time(), reason = NULL)
sk, pk
|
The key pair the statement is about. |
alg |
Its signature algorithm. |
nsk, npk
|
The new key pair. |
nalg |
The new key pair's algorithm. |
at |
When the statement takes effect: a |
reason |
|
The statement, a raw vector (write it with writeBin()).
old <- nqkeygen("ML-DSA-44") new <- nqkeygen("ML-DSA-65") s <- nqrotate(old$sk, old$pk, "ML-DSA-44", new$sk, new$pk, "ML-DSA-65", reason = "yearly") cat(rawToChar(s))old <- nqkeygen("ML-DSA-44") new <- nqkeygen("ML-DSA-65") s <- nqrotate(old$sk, old$pk, "ML-DSA-44", new$sk, new$pk, "ML-DSA-65", reason = "yearly") cat(rawToChar(s))
A capsule is a signed record of the exact files in a folder.
nqseal() writes noqtis.json (the manifest: every regular file with
its size, SHA-256 and SHA3-256) and noqtis.sig (the signature over the
manifest) into the folder. nqcheck() checks a folder against its seal
and reports every check.
nqseal( dir, sk, pk, alg, meta = NULL, format = if (is.null(inputs)) 1L else 2L, inputs = NULL ) nqcheck( dir, pk = NULL, anchors = NULL, only = NULL, lineage = NULL, keys = NULL, proof = NULL )nqseal( dir, sk, pk, alg, meta = NULL, format = if (is.null(inputs)) 1L else 2L, inputs = NULL ) nqcheck( dir, pk = NULL, anchors = NULL, only = NULL, lineage = NULL, keys = NULL, proof = NULL )
dir |
Path of the folder. |
sk |
Secret key, a raw vector (as from |
pk |
Public key, a raw vector; for |
alg |
Signature algorithm name, one of the |
meta |
|
format |
|
inputs |
|
anchors |
|
only |
|
lineage |
|
keys |
|
proof |
|
The creation time in the manifest is the SOURCE_DATE_EPOCH
environment variable when set, else the clock. Sealing refuses a folder
that holds a symbolic link or a special file. Capsules are the same
files the noqtis command-line tool writes and checks.
The library makes no network requests: an RFC 3161 timestamp is added
to a seal by other tools, and nqcheck() verifies it when anchors is
given.
nqseal(): the paths of the two files written, invisibly.
nqcheck(): a data frame with one row per check and the columns
check, ok (logical) and detail; its attribute "ok" is TRUE
only when every check passed.
d <- file.path(tempdir(), "capsule") dir.create(d) writeLines("hello", file.path(d, "a.txt")) k <- nqkeygen("ML-DSA-44") nqseal(d, k$sk, k$pk, "ML-DSA-44", meta = '{"project":"demo"}') r <- nqcheck(d, k$pk) r attr(r, "ok") writeLines("hellO", file.path(d, "a.txt")) attr(nqcheck(d, k$pk), "ok") attr(nqcheck(d, k$pk, only = "b.txt"), "ok") unlink(d, recursive = TRUE)d <- file.path(tempdir(), "capsule") dir.create(d) writeLines("hello", file.path(d, "a.txt")) k <- nqkeygen("ML-DSA-44") nqseal(d, k$sk, k$pk, "ML-DSA-44", meta = '{"project":"demo"}') r <- nqcheck(d, k$pk) r attr(r, "ok") writeLines("hellO", file.path(d, "a.txt")) attr(nqcheck(d, k$pk), "ok") attr(nqcheck(d, k$pk, only = "b.txt"), "ok") unlink(d, recursive = TRUE)
nqsign() signs a message with ML-DSA (FIPS 204), SLH-DSA (FIPS 205),
Ed25519 (RFC 8032) or a hybrid ML-DSA+Ed25519, whose signature verifies
only when both parts do; nqverify() checks such a signature.
nqsign(sk, msg, alg, ctx = NULL, det = FALSE, ph = NULL, rnd = NULL) nqverify(pk, msg, sig, alg, ctx = NULL, ph = NULL)nqsign(sk, msg, alg, ctx = NULL, det = FALSE, ph = NULL, rnd = NULL) nqverify(pk, msg, sig, alg, ctx = NULL, ph = NULL)
sk |
Secret key, a raw vector (as from |
msg |
The message: a raw vector, or one string, which is signed as its UTF-8 bytes. |
alg |
Signature algorithm name, one of the |
ctx |
|
det |
|
ph |
|
rnd |
|
pk |
Public key, a raw vector. |
sig |
Signature, a raw vector. |
nqsign(): the signature, a raw vector. nqverify(): TRUE
when the signature verifies, FALSE otherwise (including a signature,
key or context of the wrong length or encoding).
k <- nqkeygen("ML-DSA-44") s <- nqsign(k$sk, "hello", "ML-DSA-44") nqverify(k$pk, "hello", s, "ML-DSA-44") nqverify(k$pk, "hellO", s, "ML-DSA-44")k <- nqkeygen("ML-DSA-44") s <- nqsign(k$sk, "hello", "ML-DSA-44") nqverify(k$pk, "hello", s, "ML-DSA-44") nqverify(k$pk, "hellO", s, "ML-DSA-44")
Version of the bundled noqtis library
nqver()nqver()
A string, the version of the C library compiled into this package.
nqver()nqver()