Builds an XMSS key pair: a Merkle tree over 2^height Winternitz
one-time keys, all derived from two 32-byte seeds. Security rests on
SHA-256 alone – no lattice assumption, no elliptic curve, nothing
Shor's algorithm breaks.
Arguments
- height
Tree height, 1 to 16 (default 10, i.e. 1024 signatures).
- sk_seed, pub_seed, sk_prf
64-character hex seeds (32 bytes each) – the three secrets RFC 8391's private key carries.
sk_seedderives the WOTS+ chains,pub_seedmasks the hashes, andsk_prfkeys the per-signature randomiser. Omit them and they are drawn from the operating system's CSPRNG viarandom_bytes(), which fails rather than falling back to R's reproducible generator. Supply them ONLY to reproduce a key deterministically in a test – a seed you can guess is a key you can forge.
Value
A list of class bricklayer_signing_key: root (the
public verification value), pub_seed, sk_seed (SECRET),
sk_prf (SECRET), height, next_index,
capacity, and scheme.
A height-h key signs exactly 2^h messages
Each signature consumes one leaf, and signing two different messages
with the same leaf index breaks the scheme outright – between two
signatures at one index an adversary can forge a third message.
capsule_sign() therefore tracks
next_index and refuses to reuse one. Do not hand-edit that field,
and do not copy a key to two machines that sign independently.
Key generation walks all 2^height leaves, so cost doubles with
each unit of height. The default 10 gives 1024 signatures and takes a
moment; heights above about 14 are slow enough to be worth avoiding
unless the key really must last that long.
Key format
A key made before the RFC 8391 conformance work
carries no sk_prf and cannot sign;
capsule_sign() raises rather than producing
a signature no other implementation could read. Generate a new one.
Examples
# A small key, to keep the example quick.
key <- pqc_keygen(height = 3)
key$capacity # 8 signatures
#> [1] 8
key$next_index # none used yet
#> [1] 0
# The public half is what a verifier needs; it carries no secret.
pub <- signing_public_key(key)
names(pub)
#> [1] "root" "pub_seed" "height" "scheme"
# Deterministic seeds reproduce the same key -- for tests only.
s1 <- paste(rep("11", 32), collapse = "")
s2 <- paste(rep("22", 32), collapse = "")
identical(pqc_keygen(3, s1, s2)$root, pqc_keygen(3, s1, s2)$root)
#> [1] TRUE