Generated from each package's interface file and the comments above its declarations. 557 public declarations across 56 packages.
The traits every package answers, and the two helpers they share
Constant-time comparison, NIST SP 800-57 Part 1 §8 (key destruction): https://doi.org/10.6028/NIST.SP.800-57pt1r5
The contracts every other package in mooncrypt is written against, plus the two operations that must not be written casually: comparing secrets and erasing them.
Authenticated encryption with associated data.
[`open`] raises rather than returning an option, because "the tag did not match" is the one outcome a caller must not silently treat as empty plaintext. `cipher~` is the ciphertext with its tag appended, the layout every AEAD construction in use writes.
A block cipher: a keyed permutation of a fixed-size block.
It is the thing a mode of operation is built on, which is why it is a contract rather than a concrete type — GCM is defined for any 128-bit block cipher, so it takes one of these and SM4-GCM costs nothing more than an SM4 that implements it.
Both directions are here although a counter mode needs only [`encrypt`]: a permutation that cannot be inverted is not a block cipher, and a mode such as CBC needs the inverse.
A cryptographic hash, as a state you feed and then read.
Five methods and no more, because everything mooncrypt builds on top of a hash needs exactly these: HMAC needs [`block`] to size its pads, HKDF and PBKDF2 need [`size`] to count output blocks, and every one-shot needs [`write`] then [`finish`]. A third-party digest becomes usable throughout the library the moment it implements this.
[`finish`] does not consume the state, so a caller may keep writing and read again; [`reset`] returns it to the initial vector, which is how HMAC reuses one allocation for both passes.
A message authentication code: a hash that a key has been mixed into.
It is deliberately not a [`Hash`] — a MAC has no block size worth exposing and must never be used where an unkeyed digest is expected.
Something that holds a private key and can sign with it.
Something that holds a public key and can check a signature.
It answers `Bool` and never raises: a bad signature is an expected answer, not an exceptional one, and raising here invites a caller to swallow the error and carry on.
A ciphertext that did not authenticate, or a key that cannot be used.
One whole message through a hash, in one call.
It resets first, so passing a state that has already been used is not a silent corruption of the result.
Compare two byte strings in time that does not depend on their contents.
The obvious loop returns at the first differing byte, and the time it took tells an attacker how long a prefix they guessed right — enough to recover a tag or a token byte by byte across many requests. This one reads both strings to the end and folds the differences together.
Length is not treated as a secret: unequal lengths answer immediately, which is what every library does, because the length is visible on the wire anyway.
This is the only comparison mooncrypt offers. Use it for tags, digests and anything else derived from a key.
Overwrite a buffer with zeroes.
Key material lives longer than the code that used it: a scratch array stays in whatever memory the runtime hands out next until something else writes over it. Erasing at the end of the operation shortens that window. Under a garbage collector it is a best-effort measure, not a guarantee — a copy the collector moved is out of reach — which is why keys are held in [`FixedArray`] here rather than in [`Bytes`], where they could not be erased at all.
One-way functions, one shot or streamed
BLAKE2b and BLAKE2s, RFC 7693: https://www.rfc-editor.org/rfc/rfc7693
Both widths in one package because they are one design at two word sizes, exactly as SHA-2's two halves are. Keying, salting and personalisation are part of the parameter block, so a keyed BLAKE2 is a different function rather than a wrapper around the unkeyed one.
Which BLAKE2. `b` is the 64-bit variant and `s` the 32-bit one; the digest length, the salt length and the personalisation length all follow from that.
The last block is compressed here and nowhere else: BLAKE2 holds one block back, because the final block is marked as final and a block that was already compressed cannot be told otherwise.
It runs on copies, so a caller may read a digest and keep writing — the reading is what makes the copy worth it.
A fresh hasher.
`len` is the digest length, and 0 means the full length for the variant. `salt` and `person` are fields of the parameter block, so their lengths must be exactly 0 or the variant's salt size — a short one is not zero-padded, it is a different parameter block. `key` is what makes this a MAC; it may be at most 64 bytes for `b` and 32 for `s`.
Back to the parameterised state, key block and all.
The block length: 128 or 64 bytes.
The salt and personalisation lengths, 16 or 8 bytes.
The longest digest this variant can produce: 64 or 32 bytes.
Hash one whole message.
One whole message as a keyed MAC of the variant's full length.
BLAKE3, the specification at https://github.com/BLAKE3-team/BLAKE3-specs
One compression function, a binary tree of chunks, and an output that can be read to any length. The tree is built as the message arrives, so a hasher holds one chunk in progress and the roots of the subtrees already finished.
The block length in bytes.
The chunk length in bytes — the unit the tree is built from.
The digest length in bytes, which is what [`@spec.Hash`] reports.
Key derivation mode: the context string is hashed first, and its digest becomes the key, so two contexts give two unrelated functions.
A keyed hasher, which is a MAC: the key stands in for the initial vector, so a keyed hash has no relation to the unkeyed one.
A plain BLAKE3 hasher.
Read `len` bytes of output from everything written so far, without disturbing the state: a caller may read and then keep writing.
A keyed or deriving hasher cannot be reset to a bare state without losing what makes it that hasher, so this keeps the key and the flags.
Hash one whole message, read to `len` bytes.
Derive `len` bytes of key material for `context` from `material`.
Hash one whole message to 32 bytes.
Keyed hash of one whole message, read to `len` bytes.
MD5, RFC 1321: https://www.rfc-editor.org/rfc/rfc1321
Kept for PostgreSQL's `md5` authentication and the long tail of legacy checksums, not because it is fit for new work.
MD5 counts and emits little-endian, where SHA-1 and SHA-2 are big-endian — the one place the two designs part company in the padding.
Hash one whole message to a 16-byte digest.
RIPEMD-160, from the European RIPE project (Dobbertin, Bosselaers, Preneel, 1996).
Two parallel lines of eighty rounds, mixed at the end rather than during: the design is MD4's compression function run twice with different constants and message orders, and the result combined across both lines. That is what makes it a different hash rather than a stronger MD4, and it is why the two lines cannot be computed separately.
It is legacy. No new protocol should pick it — SHA-2 and SHA-3 are faster on every target that matters and have had far more attention — but Bitcoin's address format, OpenPGP fingerprints and a long tail of older systems all still name it, and a library that cannot compute it cannot interoperate.
The padding is MD4's — a one bit, zeroes, and the length in bits as a little-endian sixty-four bit number — and the digest is emitted the same way, five words least significant byte first.
The five chaining words start at the same values MD4 and MD5 use, which is not a coincidence: the compression function was inherited and only the round structure changed.
Hash one whole message to a 20-byte digest.
SHA-1, FIPS 180-4 §6.1: https://doi.org/10.6028/NIST.FIPS.180-4
Kept for the protocols that still mandate it — the WebSocket handshake (RFC 6455 §4.2.2), MySQL's native password, Git object names — not because it is fit for new work.
Hash one whole message to a 20-byte digest.
SHA-2, FIPS 180-4: https://doi.org/10.6028/NIST.FIPS.180-4
All six members: SHA-224 and SHA-256 over 32-bit words, SHA-384, SHA-512, SHA-512/224 and SHA-512/256 over 64-bit words. They differ only in word width, initial vector and how much of the final state is published, so the two compression functions are written once and the family is a `Kind`.
Which member of the SHA-2 family.
The six are one design at two word widths. `Sha512_224` and `Sha512_256` are SHA-512 with a different initial vector and a truncated digest, which is why they are not simply SHA-512 cut short — cutting SHA-512 short would leave the discarded half recoverable in a length-extension.
A fresh hasher, SHA-256 unless another member is named.
The compression block length in bytes.
The digest length in bytes.
Hash one whole message.
SHA-3 and SHAKE, FIPS 202: https://doi.org/10.6028/NIST.FIPS.202 cSHAKE and the encoding rules it is built from, NIST SP 800-185: https://doi.org/10.6028/NIST.SP.800-185
One sponge, four fixed output lengths and two extendable ones. Keccak-f[1600] is the same permutation throughout; what separates the members is the rate and the domain-separation suffix, so they are a `Kind` over one state.
Which member of the Keccak family.
The four SHA-3 lengths and the two SHAKE lengths are the same permutation with a different rate — a wider rate means fewer rounds of work per byte, so SHAKE128 is the fastest and the weakest, and the numbers in the names are the security level in bits, not the output size.
Reads on a copy of the state, so a caller may keep writing afterwards — the same rule SHA-2 follows here.
A fresh hasher, SHA3-256 unless another member is named.
The rate in bytes: how much of the 200-byte state a block of message fills before the permutation runs. The rest is capacity, and the security level is half of it.
The digest length in bytes. SHAKE has no fixed length; the value here is the conventional default output, 32 or 64 bytes, used when a caller asks for a `Hash` rather than a stream.
Absorb more message.
A SHAKE, or a cSHAKE when `name` or `custom` is non-empty (SP 800-185 §3.3).
The two strings are not secret. `name` distinguishes one function from another and is fixed by the standard that defines the function; `custom` distinguishes one application from another. Both empty means plain SHAKE, whose suffix differs — 0x1f rather than 0x04 — so the two can never collide.
Start over, keeping the rate and the customisation.
Read `len` bytes, continuing where the last read stopped.
`bytepad(x, w)`: `x` padded with zeroes to a multiple of `w`, after its own length. A rate-sized `w` makes the following message start on a block boundary, which is what keeps a key from being read as message.
One whole message through cSHAKE128.
One whole message through cSHAKE256.
`encode_string(s)`: the bit length, then the bytes (SP 800-185 §2.3.2).
Hash one whole message.
`left_encode(x)`: the length of `x` in bytes, then `x`, both big-endian (SP 800-185 §2.3.1).
`right_encode(x)`: `x` big-endian, then its length in bytes (SP 800-185 §2.3.1). What tells a reader where an output length ends.
One whole message through SHAKE128.
One whole message through SHAKE256.
SM3, GB/T 32905-2016: https://openstd.samr.gov.cn/bzgk/gb/newGbInfo?hcno=45B1A67F20F3BF339211C391E9278F5E
A 256-bit digest over 512-bit blocks, built the way SHA-256 is built, with a different round function and a message expansion that mixes in a second, wider permutation.
The block length in bytes.
The digest length in bytes.
Hash one whole message.
Keyed digests, from HMAC to KMAC
CMAC, NIST SP 800-38B: https://doi.org/10.6028/NIST.SP.800-38B
CBC-MAC was already known to be broken for messages of different lengths: the tag of one message is the tag of a longer one that continues it. CMAC fixes that by deriving two subkeys from the cipher and mixing a different one into the last block depending on whether the message ended on a block boundary. The result is a MAC that works for any length and over any block cipher.
CMAC over any block cipher, as a state you feed and then read.
The two subkeys are computed once, when the context is made, from a single encryption of the zero block — that is the whole of the setup.
@spec.MacThe tag for everything written so far. The state is left as it was, so more message can be added and the tag read again — which is what makes a MAC usable for a running transcript.
Start a CMAC context over a block cipher.
`tag` is the length of the tag to produce. SP 800-38B allows any prefix of the full block, but each bit given up halves the forgery resistance, so the default is the whole block and shorter values are a deliberate trade.
Put the context back to the state a newly made one is in, keeping the subkeys.
The tag length in bytes.
Overwrite the subkeys and the chain.
Feed more message. Any length is fine; the last block is held back until [`finish`](#finish) says which subkey it takes.
The tag of a message, in one call.
GMAC, NIST SP 800-38D: https://doi.org/10.6028/NIST.SP.800-38D
What GCM reduces to when there is no plaintext: GHASH over the associated data and the length block, encrypted with the counter. In other words the tag half of GCM on its own, which is worth having for a message that is authenticated but not encrypted — a session token, a configuration blob, a signed header.
GMAC: the authentication half of GCM, with the message authenticated instead of encrypted.
The nonce makes it a MAC rather than a hash. It must never repeat under one key: two tags under the same nonce give away the hash subkey, and from there every tag under that key is forgeable. Twelve bytes is the length the standard prefers, because it skips a derivation step and so is both faster and easier to reason about.
@spec.MacThe tag for everything written so far. Reading it does not consume the message, so a transcript can be tagged as it goes and continue afterwards.
Start a GMAC context.
`tag` is the tag length in bytes; the same values GCM allows are allowed here, and the default is the full sixteen.
Throw away the message, keeping the nonce, so the context can tag another transcript. Only useful when the same nonce is meant to be reused, which for a MAC means the two transcripts must be compared, never both trusted.
The tag length in bytes.
Compare a tag with what this context would produce, in constant time.
Feed more of the message.
The tag of a message, in one call.
HMAC, RFC 2104: https://www.rfc-editor.org/rfc/rfc2104 and FIPS 198-1: https://doi.org/10.6028/NIST.FIPS.198-1
Written once over the `Hash` contract rather than once per digest: the construction only ever needs a block size and a way to start a fresh hash.
Key an HMAC with `hash` as its digest.
A key longer than the hash's block is replaced by its digest and a shorter one is zero-padded, both per RFC 2104 §2 — so a 100-byte key and the 20-byte digest of that key authenticate identically, which is a property of the standard and not a defect here.
Start over on the same key, so one keyed state can authenticate a stream of messages without re-deriving the pads.
Authenticate one whole message.
KMAC, NIST SP 800-185 §4: https://doi.org/10.6028/NIST.SP.800-185
A keyed cSHAKE, and the only MAC here that is defined for an output of any length. KMAC128 is cSHAKE128 under the name "KMAC"; the number is the security level, not the tag size.
Which KMAC. The number is the security level in bits and picks the cSHAKE underneath, so it also sets the largest key that fits in one block.
The cSHAKE rate in bytes, which is also the longest key KMAC accepts (SP 800-185 §4.2 requires the encoded key to fit in one block).
The conventional tag length in bytes, 32 or 64. KMAC itself has no fixed length; this is what [`Kmac`] uses when the length is not given.
Key a KMAC. `custom` separates the uses of one key: two tags computed under different strings are independent even when the key and message are the same.
Read `len` bytes, continuing where the last read stopped.
One whole message through KMAC, with a tag of `len` bytes.
One whole message through KMACXOF.
Poly1305, RFC 8439 §2.5: https://www.rfc-editor.org/rfc/rfc8439#section-2.5
A one-time authenticator: a key must never sign two messages. AEAD use derives a fresh key per nonce; see `aead/chachapoly`.
A Poly1305 computation under one key, fed in pieces. The accumulator is held in five 26-bit limbs so every product fits in 64 bits.
@spec.MacThe tag length in bytes.
The tag. The computation is spent: a one-time key signs one message.
A computation under the 32-byte one-time `key`: `r` (clamped) then `s`.
The tag of `msg` under the one-time `key`.
SipHash-2-4: https://131002.net/siphash/siphash.pdf and the reference implementation: https://github.com/veorq/SipHash
A 64-bit keyed MAC that costs a handful of instructions per byte, built to defend a hash table against collision-flooding attacks. It is a MAC, not a hash: the key is what stops an attacker from producing inputs that all land in one bucket, and an unkeyed SipHash is not a thing.
The round function is the same as ChaCha's quarter round with different constants, and the whole state is 256 bits — small enough to keep in registers, which is the entire point of it.
A SipHash-2-4 in progress.
The buffer needs at most seven bytes: a full eight-byte word is mixed in the moment it is available, and only a trailing partial word waits for the length, which goes into the top byte of the last block.
@spec.MacThe key length in bytes. SipHash is defined for exactly 16.
The tag length in bytes.
Key a SipHash-2-4. The key must be exactly [`key_size`] bytes; anything else is refused rather than padded, because a key that silently became shorter would be a weaker MAC that still looked like a working one.
Feed more bytes. Whole eight-byte words are mixed in as they arrive; whatever does not fill one is held until [`value`](#value).
The tag as a number.
The last block carries the message length in its top byte, so a message and the same message with a zero byte appended hash differently — without it, padding ambiguities would let an attacker move bytes between messages. The finalisation is four rounds, the "4" in the name.
The state is copied rather than consumed, so this can be called more than once.
The SipHash-2-4 tag of a whole message.
Non-cryptographic digests for integrity against noise
Adler-32, RFC 1950 §9: https://www.rfc-editor.org/rfc/rfc1950
Two running sums instead of one: a byte sum and a sum of those sums, both modulo 65521. It is much weaker than CRC-32 against burst errors, and much faster — which is why zlib offers it for data whose integrity something else has already covered.
A checksum, not a hash: it catches accidental corruption and nothing else.
An Adler-32 in progress.
@spec.HashThe digest length in bytes.
Start an Adler-32.
The initial sums are `a = 1` and `b = 0`, which is why the empty message has a checksum of 1 and not 0.
Feed more bytes.
The checksum of everything fed so far: `b` in the high half, `a` in the low, which is the order every format that carries it writes.
The Adler-32 of a whole message.
CRC-16, the common variants: https://reveng.sourceforge.io/crc-catalogue/
The same shift-and-xor machine as CRC-32 with a 16-bit register, but the variants disagree about more than the polynomial: which way round the bits go, what the register starts at, and whether the answer is complemented at the end. Those four choices are what a CRC catalogue lists, and they are what this package parameterises rather than hard-coding one of them.
A checksum, not a hash. It detects the burst errors a wire produces and detects nothing against someone who wants to change the message.
A CRC-16 in progress.
@spec.HashWhich CRC-16.
Named after the catalogue, which is the only way to be unambiguous: "CRC-16" alone has meant at least four different functions in shipping protocols. The check value of each is stated on its case so that a reader can tell them apart at a glance.
Start a CRC-16. The default is CRC-16/ARC, the oldest of the six.
Feed more bytes.
The checksum of everything fed so far, as the number the formats that write it store.
One byte at a time, which is what [`@spec.Hash::block`] reports.
The checksum length in bytes.
The CRC-16 of a whole message.
CRC-32, ISO-HDLC / ITU-T V.42 / IEEE 802.3, and CRC-32C, RFC 3720: https://www.rfc-editor.org/rfc/rfc3720
A checksum, not a hash: it detects the kind of corruption a wire or a disk produces — a burst of flipped bits in one place — and detects nothing at all against someone who means to change the message. Use it for integrity, never for authentication.
Both members are the same machine with a different polynomial. They are reflected, meaning the bits of each byte are processed least significant first, which in practice means shifting right and xoring the table entry into the low byte rather than the high one. CRC-32C uses a polynomial chosen so that it catches the kinds of multi-byte burst that the original does not, and is what iSCSI, ext4 and Btrfs write to disk.
A CRC-32 in progress.
The running value is kept complemented, as the reference implementation does, so the final xor is one operation at the end rather than a correction at every step.
@spec.HashWhich CRC-32.
The two differ only in the polynomial, but they detect different faults: the IEEE one is the one on Ethernet and in zip files, and the Castagnoli one has better distance properties for the short blocks a filesystem or a storage protocol writes.
Start a CRC-32. The default is the IEEE polynomial, which is what "CRC-32" means when nothing else is said.
Feed more bytes.
The checksum of everything fed so far, as the number the formats that write it actually store.
One byte at a time: that is the stride this machine works in, and it is what [`@spec.Hash::block`] reports.
The checksum length in bytes.
The CRC-32 of a whole message.
FNV-1 and FNV-1a, 32 and 64 bit: http://www.isthe.com/chongo/tech/comp/fnv/
Multiply, then xor (FNV-1), or xor, then multiply (FNV-1a). The two differ in one line and produce different digests, so both are here and the default is FNV-1a, which has better avalanche and is what everything means by "FNV" today.
A checksum, not a hash: it is fast and it distributes well, and it resists nothing. The 128-bit variants are left out because a 64-bit multiply is the widest thing this machine has, and doing 128 bits on top of it costs more than the occasional collision is worth for the use these get.
An FNV digest in progress.
The state is always 64 bits wide and only the low half is used for the 32-bit kinds, because the multiply has to be truncated to the width anyway and keeping one register is simpler than two.
@spec.HashWhich FNV.
Two widths and two orderings, and all four produce different answers. The `_1a` variants xor the byte in before the multiply, which is what gives them their better avalanche; the `_1` variants are the original and are kept because existing formats use them.
Start an FNV digest. The default is FNV-1a at 64 bits, which is the one new code should use.
Feed more bytes.
The digest of everything fed so far, as the number the formats that write it store.
The stride, which for FNV is one byte.
How many bytes the digest occupies.
The FNV digest of a whole message.
MurmurHash3: https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp
Two finalisers, two block sizes, one idea: mix each word into an accumulator with a multiply and two rotations, then avalanche the result so that every bit of the input affects every bit of the digest. It is the hash Redis and Guava use for their maps, and the default `hash` in several language runtimes.
A checksum, not a hash: it is fast, it distributes well, and it resists nothing. The 128-bit form is not "wider and therefore stronger" — it is the variant that keeps two lanes partially coupled, which gives a better distribution for a longer digest at twice the work per block.
A MurmurHash3 in progress.
Bytes are buffered until a whole block is present. That is not an optimisation but a requirement: the trailing partial block is mixed differently from a whole one, and its length is only known at the end.
@spec.HashWhich MurmurHash3.
The names are the ones the reference implementation uses: `x86_32` is the 32-bit-output variant tuned for 32-bit machines, `x64_128` the 128-bit-output variant for 64-bit machines. There is also an `x86_128` upstream, left out because `x64_128` dominates it on the machines anyone runs today.
The block length: how many bytes one pass of the mixing loop consumes, and what [`@spec.Hash::block`] reports.
The digest length in bytes.
Start a MurmurHash3. The seed is mixed in from the first byte, so two seeds give unrelated digests of the same message.
Feed more bytes, a block at a time, holding back whatever does not fill one.
The digest as a pair of 64-bit halves, high half first. The 32-bit kind gives `(digest, 0)`.
The MurmurHash3 of a whole message, as the 128-bit pair.
xxHash, XXH32 and XXH64: https://github.com/Cyan4973/xxHash/blob/dev/doc/xxhash_spec.md
Four lanes fed interleaved 4-byte (XXH32) or 8-byte (XXH64) words, then merged with a final avalanche. It is the fastest of the non-cryptographic hashes here and the one hash tables and file-integrity tools reach for when CRC-32 is too weak and SHA-256 too slow.
A checksum, not a hash: it resists accidental corruption and nothing else. The seed is part of the contract — two seeds give unrelated digests — which is what makes it usable for hash tables that need to re-key an existing table.
XXH3 is a different algorithm with its own secret and is not here yet; XXH32 and XXH64 are what "xxHash" meant for the decade before XXH3 arrived, and they are what the formats on disk still store.
An xxHash in progress.
Bytes are buffered until a whole stripe is present, because a stripe is the smallest unit the lanes can consume: a partial stripe has to be re-read from the start at [`finish`](#finish), so it cannot be folded in early.
@spec.HashWhich xxHash.
The two are not a truncation of each other: the 64-bit one multiplies 64-bit words and rotates by different amounts, so XXH64 of a message says nothing about its XXH32.
The stripe length: how many bytes the four lanes consume before the state moves to the next stripe, and what [`@spec.Hash::block`] reports.
The digest length in bytes.
Start an xxHash. The seed is zero by default, which is what a plain checksum wants; a hash table passes its own so that the same keys land in a different order.
Feed more bytes, a stripe at a time, holding back whatever does not fill one.
The digest as a number.
The buffered partial stripe is folded in here: whole 4- or 8-byte words first, then the remaining bytes one at a time, then the avalanche.
The xxHash of a whole message.
Turning one secret into the keys a protocol needs
Argon2, RFC 9106: https://www.rfc-editor.org/rfc/rfc9106
The memory-hard function that won the Password Hashing Competition. Where scrypt ties its guarantee to one pass over a buffer, Argon2 builds a thousand-block matrix and reads it back at pseudo-random positions, so the cost of a guess is set by how much memory an attacker is willing to buy.
All three variants are here because they share everything except where the addresses come from: Argon2d reads them from the data, which is fastest but leaks through cache timing; Argon2i derives them from a counter, which is side-channel free but needs more passes; Argon2id does the first half-pass data-independently and the rest not, which is why RFC 9106 recommends it.
The three numbers a derivation is defined by.
`memory` is in kibibytes, and it is what the function is really about: the derivation allocates that much and reads it back in a password-dependent order, so an attacker who cannot afford the memory cannot compute the hash cheaply at all. `passes` multiplies the time without changing the memory. `lanes` is how many independent chains are kept; it is the only knob that helps when the work runs on a GPU, and each lane needs eight blocks.
The tag length is not here: it is an argument to the derivation, because two calls that differ only in how long an output they want are still the same function with the same cost.
Which of the three functions to run. They differ only in where the index of the reference block comes from.
The second option: 64 MiB, three passes, four lanes. A tenth of the memory and about the same time, for the case where 2 GiB per login is not affordable.
The smallest derivation that is still Argon2: 8 KiB, one pass, one lane. It exists so that tests and protocol fixtures can name a size rather than invent one, and it is not a safe way to store a password.
The first option RFC 9106 §4 recommends: 2 GiB, one pass, four lanes. Big enough that a dedicated attacker has to buy the memory, and one pass keeps the wall-clock time at a single sweep.
The number the function is identified by inside the initial hash.
The spelling the RFC uses, for error messages and parameter records.
How many bytes a derivation with these parameters will actually allocate: the memory is rounded down to a whole number of blocks per slice, and each block is 1 KiB.
Check the three numbers before a derivation allocates anything.
`memory` has to leave room for eight blocks per lane, which is the point below which the reference block rule in §3.4.1 stops working; `lanes` has to be at least one and small enough that the lane number fits in the address computation; and `passes` has to be at least one, since zero passes would leave the memory untouched.
Derive a key with Argon2.
`secret` is a pepper: a value kept outside the database, so that a stolen table is useless without it. `ad` is associated data, which binds the output to a context such as a user name. Both are optional and both change the result, which is why the RFC gives them their own length fields.
`len` is the tag length; anything from 4 bytes up is allowed, and the default matches what a password hash is normally stored as. `params` sets the cost, and `kind` picks the variant — leave both alone unless you know why not.
```moonbit nocheck let tag = @argon2.key( password, salt, kind=@argon2.Argon2id, params=@argon2.interactive, ) ```
bcrypt, from Provos and Mazières, "A Future-Adaptable Password Scheme", USENIX 1999: https://www.usenix.org/legacy/events/usenix99/provos.html
A password hash whose cost is a number you choose rather than a number the algorithm fixes: the same password and salt are run through 2^cost rekeyings of Blowfish, so raising the cost doubles the work for everyone, attacker included, and old hashes stay verifiable because the cost travels with them.
The text format is the one crypt(3) settled on:
$2b$12$<22 characters of salt><31 characters of hash>
bcrypt has two quirks worth knowing. It stops at 72 bytes of password, and it appends a NUL terminator, so a password and the same password with a trailing zero are the same password. Both are reproduced here rather than fixed, because a hash that does not match everyone else's is not a password hash.
Twelve rounds of key setup: about a quarter of a second on the machine this was written on, which is the usual compromise between logging in and guessing.
The dearest cost the format allows. A cost of 31 is expressible and would take a machine a lifetime, which is the point of allowing it.
The most password bytes the format can use. Anything past it cannot affect the result, so it is refused rather than silently ignored — a caller who truncates their own password knows they did.
The cheapest cost the format allows.
The salt is always sixteen bytes; the format has no way to say otherwise.
Format a password and salt as a `$2b$` hash string.
This is what gets written to a database. The cost travels inside it, so raising [`default_cost`] later leaves existing hashes verifiable.
Hash a password with a caller-supplied salt.
The salt is sixteen raw bytes; the format only stores its base64 form, so callers usually let [`parse`] hand it back from a stored hash instead of making one. The result is the 24-byte output of the scheme, of which the text format keeps 23.
Raises rather than truncating if the password is longer than [`max_password`] bytes, because a password that silently becomes a prefix of itself is a password that can be confused with another.
Read the cost and the salt out of a stored hash.
Returns `None` for anything that is not a well-formed `$2a$`, `$2b$`, `$2x$` or `$2y$` string. The three prefixes are the same function here: the differences between them are historical bugs in other implementations that only showed up for passwords with the high bit set or longer than 72 bytes.
Check a password against a stored hash.
Never raises: a malformed string is simply a wrong password, because a caller who has to handle the difference between "this row is corrupt" and "this password is wrong" can call [`parse`] first and get it there.
The comparison is constant time, so the time it takes says nothing about how much of the stored hash the password got right.
The concatenation KDF of NIST SP 800-56A §5.8.1, also known as Concat KDF.
One digest per block, with a four-byte big-endian counter in front of the counters and the protocol's own OtherInfo behind it:
K(i) = H(counter(i) || Z || OtherInfo)
The counter leads. SEC1 §3.6.1 puts Z first instead and is a different function; a protocol that names one of them and gets the other produces a key that never matches, and nothing about the output says which happened.
Derive `len` bytes of key from a shared secret.
`z` is the secret the key exchange agreed on; `other_info` is everything the protocol wants bound into the derivation — the algorithm identifiers, the two parties' key info, the length of the key being made. SP 800-56A §5.8.1 spells that structure out as a DER `OtherInfo` and calls it optional; the octets are taken as given here, because a protocol that defines its own layout (TLS 1.3, CMS, S/MIME each do) has to pass its own.
The counter starts at one, and a call longer than one digest concatenates as many blocks as it needs, each counted from one upwards.
HKDF, RFC 5869: https://www.rfc-editor.org/rfc/rfc5869
Extract and expand are exported separately because TLS 1.3 uses them separately — it extracts once per handshake secret and expands many times.
The expand step (RFC 5869 §2.3): stretch a pseudorandom key to `len` bytes.
The counter is one byte, so no more than 255 digests can be produced from one key; asking for more is a programming error, not a runtime condition.
The extract step (RFC 5869 §2.2): concentrate whatever entropy the input has into one uniform pseudorandom key of the hash's own length.
An absent salt is a string of zeroes as long as the digest, which is what the RFC specifies — not a special case.
Extract-then-expand in one call: turn input keying material into `len` bytes of key.
`info` separates the uses of one secret — two derivations with different `info` are independent keys, which is how one handshake secret yields a separate key for each direction. `salt` need not be secret and need not be present; when it is, the extraction is stronger against a non-uniform input.
PBKDF2, RFC 8018 §5.2: https://www.rfc-editor.org/rfc/rfc8018#section-5.2
The work factor is the caller's: this only counts iterations. SCRAM authentication and password storage are the two callers that matter.
Stretch a password into `len` bytes of key by iterating an HMAC `rounds` times (RFC 8018 §5.2).
The whole security argument is the iteration count: it costs the defender one derivation and the attacker one per guess. Pick it from what the deployment can afford today, not from a number copied out of an old document — SCRAM servers name their own count on the wire, and for password storage the memory-hard KDFs are the better answer where they are available.
`rounds` and `len` have no defaults, which is itself the mainstream choice: Python's `hashlib.pbkdf2_hmac`, Go's `pbkdf2.Key` and Node's `crypto.pbkdf2` all require both. A default iteration count is a number that goes out of date silently, and this is the one place where being made to type it is the point.
A count below one and a negative length abort rather than raise. Neither can come from input: they are the calling code saying something it cannot mean.
scrypt, RFC 7914: https://www.rfc-editor.org/rfc/rfc7914
Memory-hard by construction: the inner loop fills a buffer of `128 * r * n` bytes and reads it back in an order the password decides, so an attacker cannot trade memory for time the way PBKDF2 lets them.
The three numbers a scrypt derivation is defined by.
`n` is the number of blocks the inner loop keeps, so the memory it takes is `128 * r * n` bytes; `r` is the block size divided by 128; and `p` is how many of those passes run in parallel, which multiplies the time but not the memory. Doubling `n` doubles both, which is why it is the knob to turn.
A preset for logging in: 16 MiB of memory, one pass. Fast enough to answer a request, slow enough that a guess costs a real machine a real fraction of a second.
The smallest `n` RFC 7914 allows: it must be a power of two above one.
A preset for storing a password that will not be asked for often: 1 GiB of memory. Anything that must run on a small device cannot afford it.
Check the three numbers before a derivation spends memory on them.
The RFC's own limits: `n` a power of two greater than one, `r` and `p` positive, and `128 * r * p` below 2³⁰ so that the block counter cannot overflow.
Derive `len` bytes from a password and a salt (RFC 7914 §6).
`n`, `r` and `p` are the cost. The memory is `128 * r * n` bytes, so the default preset's 16 MiB is deliberate rather than incidental: it is what makes a hardware attack cost memory as well as time.
Sixteen-byte blocks and the modes that chain them
AES, FIPS 197: https://doi.org/10.6028/NIST.FIPS.197-upd1
All three key lengths. The key is the only thing that varies, so there is no variant to name: the length says which one it is.
An expanded key.
The schedule is held in a [`FixedArray`] so it can be erased; see [`@spec.wipe`] for why that matters and what it does not promise.
@spec.BlockThe block length, in bytes. AES is defined for one block size only; the key is what varies.
The block length in bytes, which is 16 whatever the key.
Decrypt one block (FIPS 197 §5.3 InvCipher).
Encrypt one block (FIPS 197 §5.1 Cipher).
The state is column-major: byte `k` is row `k % 4`, column `k / 4`.
Expand a key, which is 16, 24 or 32 bytes for AES-128, AES-192 or AES-256.
The key length is the only thing that distinguishes the three, so there is no variant to name: `new` reads it off the key, as every AES interface in use does. A length that is none of the three raises rather than being rounded to one, because a key that is not the key is not a smaller mistake than no key.
The key length in bytes: 16, 24 or 32.
Forget the expanded key. See [`@spec.wipe`] for what that is worth.
Blowfish, as described by Bruce Schneier: https://www.schneier.com/academic/blowfish/
A 16-round Feistel network keyed by a deliberately expensive key schedule: the subkeys start as the digits of pi and are then overwritten by encrypting the subkeys themselves. That is what bcrypt leans on — it makes the schedule repeat, salted, so the cost of a guess becomes the cost of a key setup rather than the cost of a block.
Deprecated as a cipher: 64-bit blocks are too small for a mode of operation to be safe in general. It is here because bcrypt is defined on top of it, and because the key schedule is the interesting part.
A Blowfish key schedule. The subkeys are the whole state; there is nothing else to keep.
@spec.BlockThe block length in bytes. Sixty-four bits, which is why this cipher is only safe inside something that does not care about birthday bounds — bcrypt, for instance, encrypts a fixed 24-byte string and nothing else.
How many bytes the bcrypt key schedule may feed it: 72 characters plus the terminator that the format appends.
The longest key the original specification allows.
The shortest key the original specification allows.
The block length in bytes.
Decrypt one block.
Encrypt one block.
Key the cipher the ordinary way.
The key must be between [`min_key`] and [`max_key`] bytes, the range the original specification names.
The expensive key schedule bcrypt is built on: one salted pass, then `2^cost` rounds of rekeying with the password and with the salt alternately.
Every round throws away the whole schedule and rebuilds it, which is why a guess at a password costs one full setup rather than one block encryption. The salt is always sixteen bytes, because that is the only width the format stores; the key may be up to [`eks_max_key`] bytes, since bcrypt appends a terminator to the password.
Overwrite the subkeys. After a bcrypt derivation the schedule is as secret as the password, so it should not be left for a later allocation to hand back out.
Camellia, RFC 3713: https://www.rfc-editor.org/rfc/rfc3713
A 128-bit block cipher with 128, 192 or 256-bit keys, designed in Japan at the same time as AES and to the same strength. It is a Feistel network — so the same circuit both encrypts and decrypts, with the subkeys reversed — with an FL/FLINV layer every six rounds that mixes in a little key material without touching the Feistel structure. That layer is what makes Camellia resistant to the meet-in-the-middle and subkey-guessing attacks a plain 18-round Feistel of this width would otherwise admit.
A Camellia key, with both directions already scheduled.
Decryption uses the same network as encryption with every list reversed, so the two schedules are built once here and the entry point is all that differs between [`encrypt`] and [`decrypt`].
@spec.BlockThe block length in bytes. Camellia is defined for one block size only; the key is what varies, over 128, 192 or 256 bits.
The block length in bytes.
Decrypt one 16-byte block.
Encrypt one 16-byte block.
The key schedule (RFC 3713 §2.2).
KL is the leftmost 128 bits of the key. KR is the rest of it, except that a 192-bit key has only 64 bits of remainder, so those are followed by their own complement — a trick that keeps KR 128 bits wide without inventing key material. KA and KB are then derived by running the round function a few times over KL, KR and the sigma constants.
Overwrite both schedules.
The key is at most 256 bits but the schedule is up to twenty-four subkeys and two whitening sets, and anyone holding them can decrypt everything the key can without ever seeing the key. They are wiped for the same reason a key is.
Both are legacy. DES's 56-bit key is small enough to search exhaustively — it was in 1998 and it is much cheaper now — and Triple DES exists only to stretch that key by encrypting three times. NIST withdrew approval for both in 2023, and every new protocol should use AES.
DES and Triple DES, the Data Encryption Standard of 1977 and its successor.
They are here because a great deal of data and a great many protocols still name them: EMV cards, some TLS and IPsec configurations, older Kerberos and a long tail of hardware. Reading that data is the reason this package exists.
A DES key, expanded into its sixteen forty-eight-bit round keys.
@spec.BlockA Triple DES key: three DES keys, applied in the order E-D-E.
@spec.BlockThe block length in bytes.
The key length in bytes. Eight, but only 56 of the 64 bits are key material: the low bit of every byte is a parity bit that DES ignores.
The Triple DES key length in bytes: three DES keys, used encrypt-decrypt-encrypt.
The block length in bytes.
Decrypt one 8-byte block: the same cipher with the round keys in reverse.
Encrypt one 8-byte block.
The sixteen round keys of FIPS 46-3 §3.2: permute the key to 56 bits, split it in half, rotate the halves by the schedule, and squeeze each pair through PC-2.
Overwrite the round keys.
There are sixteen of them, each forty-eight bits, so a whole DES schedule holds nearly the entire key — and unlike the key it is not parity-masked.
The block length in bytes.
Decrypt one 8-byte block: E-D-E in reverse, which is D-E-D.
Encrypt one 8-byte block.
Key Triple DES from a sixteen-byte key, the two-key form where the third key repeats the first.
Two-key triple DES gives about 80 bits of security rather than 112, which is why it was deprecated before the three-key form was. It is still what a lot of stored data was encrypted with.
Key Triple DES.
The key is three DES keys and the operation is encrypt with the first, decrypt with the second, encrypt with the third — E-D-E, chosen so that a key of `K ‖ K ‖ K` is exactly single DES and a deployment could be upgraded one endpoint at a time.
Overwrite all three round-key schedules.
Modes of operation for a block cipher, NIST SP 800-38A: https://doi.org/10.6028/NIST.SP.800-38A
A block cipher on its own only permutes one block, which leaks every repetition in the plaintext. CBC chains the blocks so each one depends on all the ones before, hiding that; CTR turns the cipher into a stream, so any length encrypts without padding and decryption is the same operation.
Neither authenticates anything. A CBC or CTR ciphertext can be edited by anyone who knows the format — a flipped bit flips a bit in the plaintext — so anything that matters wants an AEAD or a MAC alongside.
Cipher Block Chaining: each block is XORed with the ciphertext of the one before it, so the same plaintext block at two positions encrypts differently and a change anywhere propagates to everything after it.
The chaining state carries across calls, so a message can be encrypted in pieces as long as every piece is a whole number of blocks. Use [`pad`] and [`unpad`] when the message is not.
Counter mode: the cipher encrypts a counter that goes up by one per block, and the result is the keystream. Encryption and decryption are the same operation, no padding is needed, and blocks can be processed in parallel — which is why it is the mode inside GCM.
It authenticates nothing on its own, and reusing a counter value under the same key reuses the keystream, which reveals the XOR of the two plaintexts.
The block length in bytes.
Decrypt a whole number of blocks, continuing the chain from where the last call left off.
Encrypt a whole number of blocks, continuing the chain from where the last call left off.
Start a CBC context.
The initialisation vector does not have to be secret, but it must not repeat under the same key: two messages that begin with the same blocks would then begin with the same ciphertext, which is the one thing CBC exists to avoid.
Put the chaining value back to the initialisation vector, so the context can be used again for another message.
XOR a byte string with the keystream, continuing from wherever the last call stopped. Encrypting and decrypting are the same call.
The block length in bytes.
Start a counter-mode context.
The nonce is the whole initial counter block, so for a 128-bit block cipher it is sixteen bytes. It must never repeat under the same key.
Put the counter back to the initial block and forget the leftover keystream, so the context can encrypt another message from the same nonce.
PKCS#7 padding: append `n` bytes each holding the value `n`, where `n` is whatever brings the length up to a multiple of the block size.
A message that is already a whole number of blocks gains a whole extra block of padding, which is what makes the padding removable without guessing: the last byte always says how much to take off.
Strip PKCS#7 padding, raising [`@spec.Broken::Tag`] if it is not there.
The error is deliberately the same one an AEAD gives for a bad tag: whether the padding was wrong or the key was, a caller that reacts differently to the two has built a padding oracle, and that is a complete break of CBC. Treat every failure as "the ciphertext is wrong" and nothing else.
SM4, GB/T 32907-2016: https://openstd.samr.gov.cn/bzgk/gb/newGbInfo?hcno=7803DE42D3BC5E80B0C3E5D8E873D56A
A 128-bit block cipher with a 128-bit key. It is a Feistel-free design in the same family as AES's round structure — four S-box lookups and a linear diffusion — with a key schedule that runs the same round function backwards.
An SM4 key schedule: thirty-two round keys, either direction.
@spec.BlockThe block length in bytes.
The key lengths this cipher takes: one, 16 bytes (GB/T 32907 §6.2).
The block length in bytes.
Decrypt one block.
Encrypt one block.
Expand a 16-byte key (GB/T 32907 §7.3).
Overwrite the round keys.
A keystream, exclusive-ored with the message
ChaCha20, RFC 8439 §2.3–2.4: https://www.rfc-editor.org/rfc/rfc8439#section-2.3
The 96-bit nonce and 32-bit counter of the IETF variant. `block` is exposed on its own because QUIC's header protection uses one block as a mask.
A ChaCha20 key, ready to produce keystream for any nonce and counter.
The key length in bytes.
The nonce length in bytes.
One 64-byte keystream block for `counter` and `nonce` (RFC 8439 §2.3).
The cipher for a 32-byte `key`.
`data` XORed with the keystream that starts at block `counter` (RFC 8439 §2.4): encryption and decryption are the same operation. The counter starts at 0, as libsodium's does; AEAD_CHACHA20_POLY1305 starts the payload at 1.
HChaCha20, draft-irtf-cfrg-xchacha §2.2.
The same twenty rounds as [`Cipher::block`], with two changes: the nonce is sixteen bytes and takes the whole fourth row, and the feed-forward addition of the input state is left out. What comes back is therefore not keystream but a value that looks like a key — which is exactly how XChaCha20 turns a twenty-four byte nonce into the twelve-byte nonce ChaCha20 has room for.
The halves of the output that are kept are the first and last rows, the two the nonce did not touch, so the derived key depends on the whole key and the whole nonce.
RC4, the stream cipher from 1987 that is now a cautionary tale.
It is here for interoperability and nothing else: WEP, WPA-TKIP, older TLS suites, Kerberos' RC4-HMAC and a long tail of file formats still carry it, and a library that cannot read them is a library that cannot talk to them.
Do not reach for it. RC4's keystream is measurably biased — the second byte is zero twice as often as it should be, and long keystreams correlate with keys — and RFC 7465 forbids it in TLS for that reason. Its biases are a property of the algorithm, not of any implementation, so nothing here can be tuned to fix them.
An RC4 keystream.
The state advances as bytes are produced, so one `Cipher` encrypts a stream and cannot be rewound. Two messages under the same key therefore need two instances — or, in practice, two keys, because reusing one is what broke WEP.
The longest key the algorithm has room for.
The key is used to seed a permutation of 256 bytes, so anything longer than 256 bytes would be folded back onto itself. Shorter keys are the practical case: 5, 13 and 16 bytes cover WEP, WPA-TKIP and Kerberos.
The next `n` bytes of keystream.
Key the cipher: the key-scheduling algorithm of RFC 6229 §2.1.
The permutation starts as the identity and is then shuffled by the key, one swap per byte. A key of zero bytes is refused — it has no entropy to begin with, and accepting it would make an empty key and a missing key both "RC4 with nothing".
The next byte of keystream (RFC 6229 §2.2).
One swap and two lookups: the generator is four lines long, which is why it could be implemented from memory in every language, and why the statistical weakness is worth taking seriously rather than checking for.
`data` XORed with the keystream that follows the current state: encryption and decryption are the same operation, which is what a stream cipher means.
The first `n` bytes of keystream for `key`, without consuming a cipher.
Encrypt `data` in one call, starting from the beginning of the keystream.
Salsa20, the stream cipher from 2005 that ChaCha20 replaced.
The two are the same design with a different round function: Salsa20's quarter round adds and rotates, ChaCha20's adds, rotates and permutes, so ChaCha20 diffuses faster without costing anything. Salsa20 is what is in older NaCl boxes, and what `crypto_stream` in libsodium still means.
It is superseded rather than broken — the family was never attacked — but there is no reason to choose it today, and the reason to have it is that data written with it is still data.
A Salsa20 key, ready to produce keystream for any nonce and counter.
The key length in bytes.
Salsa20 also defines a sixteen-byte key, which uses a different constant in the middle of the state; only the thirty-two byte form is here, because it is the one that appears in every protocol that uses Salsa20 at all.
The nonce length in bytes. Salsa20 proper has sixty-four bits of nonce and sixty-four of counter, which is what XSalsa20 extends with a longer nonce.
One 64-byte keystream block for `counter` and `nonce`.
The counter is 64 bits. That is not academic: a 32-bit counter wraps after 256 GiB of keystream, and two blocks that share a counter share their keystream entirely.
The cipher for a 32-byte `key`.
`data` XORed with the keystream that starts at block `counter`: encryption and decryption are the same operation.
Encrypt `data` in one call, starting from block zero.
`n` bytes of keystream from the start.
XChaCha20, draft-irtf-cfrg-xchacha-03: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha-03
ChaCha20 with a twenty-four byte nonce, which it gets by deriving a fresh key from the first sixteen bytes of that nonce and then running ordinary ChaCha20 under it with the remaining eight. The derivation is HChaCha20, the same twenty rounds stopped before the feed-forward, so the output looks like a key rather than keystream.
The point is not more security per block but a nonce large enough to pick at random: twenty-four bytes means a system can generate a nonce per message from a good random source and never coordinate, where eight or twelve bytes forces a counter and therefore state.
An XChaCha20 key, ready to produce keystream for any nonce and counter.
The key is kept rather than the derived subkeys, because a subkey belongs to one nonce and a cipher is made once and used with many.
The key length in bytes.
The nonce length in bytes.
One 64-byte keystream block for `counter` and a 24-byte `nonce`.
The cipher for a 32-byte `key`.
`data` XORed with the keystream that starts at block `counter`. Encryption and decryption are the same operation.
The counter starts at 0, matching [`@chacha20`]; XChaCha20-Poly1305 starts its payload at 1 because block 0 is spent on the Poly1305 key.
The subkey HChaCha20 derives from a 32-byte `key` and a 16-byte `nonce`.
This is ChaCha20's block function with the feed-forward left out, so what comes back is a value that looks like a key. It is public because it is useful on its own — libsodium's sealed boxes derive a key from a shared secret and a nonce the same way — and because it is the one part of XChaCha20 a reader is likely to want to check in isolation.
Encryption that carries its own tag
CCM, NIST SP 800-38C and RFC 3610: https://www.rfc-editor.org/rfc/rfc3610
Counter mode with CBC-MAC: the same key drives a MAC over the plaintext and a stream cipher over the same bytes, and the tag is masked with one more block of keystream. Two passes over the message is the price, and the reward is a construction simple enough to be worth using on hardware that has nothing but a block cipher — which is why 802.11 wrote it into WPA2.
The nonce length is a parameter here, not a fixed number: 13 bytes is the usual choice, and the constraint is only that together with the message length it fills a block.
CCM over any 128-bit block cipher.
The tag length is fixed when the context is made because it goes into the first byte of the MAC input: a 12-byte tag and a 16-byte tag are not the same computation truncated, they are different computations. It is also why CCM cannot be used with a tag shorter than four bytes — the encoding has no way to say so.
@spec.AeadThe default tag length in bytes.
Wrap a block cipher in CCM.
The cipher must have a sixteen-byte block. Everything else — the key — is the cipher's business.
Authenticate and decrypt.
Raises [`@spec.Broken::Tag`] if the tag does not match. The tag is checked before the plaintext is returned, and the comparison is constant time, so a caller learns only that the message was wrong and not which part of it.
Encrypt and authenticate.
The result is the ciphertext followed by the tag; the tag length comes from the context. The nonce may be 7 to 13 bytes, and shorter is not better: the number of messages that are safe under one key is bounded by the nonce space, so 13 bytes — the longest that leaves room for a two-byte counter — is what the standard recommends.
The tag length in bytes.
Authenticate and decrypt in one call.
Encrypt and authenticate in one call, with a context that lives just long enough.
AEAD_CHACHA20_POLY1305, RFC 8439 §2.8: https://www.rfc-editor.org/rfc/rfc8439#section-2.8
The AEAD TLS 1.3 and QUIC name second, and the one to prefer where AES has no hardware: it is constant-time by construction, not by careful table avoidance.
ChaCha20-Poly1305 under one 32-byte key. Nonces are 12 bytes and must never repeat under the same key.
@spec.AeadThe tag length in bytes.
The plaintext of `cipher` (ciphertext then tag), checked before anything is decrypted. Every failure is the same `Tag`, so a caller learns nothing about which part was wrong.
The ciphertext of `plain` followed by its tag. A nonce that is not 12 bytes is a programming error and aborts: `seal` has no error to return.
Galois/Counter Mode, NIST SP 800-38D: https://doi.org/10.6028/NIST.SP.800-38D
Over any 128-bit block cipher, because the mode is defined that way. Nonces of any length, and the tag lengths SP 800-38D names.
The tag length every implementation writes unless told otherwise: the full 128 bits, which is the only length SP 800-38D appendix C leaves unqualified.
Wrap a block cipher in GCM.
`tag` is the authentication tag length in bytes. The default is the full 16; SP 800-38D §5.2.1.2 also allows 15, 14, 13 and 12, and 8 or 4 only for applications whose own analysis says so — appendix C works out how much forgery resistance each one costs, so a shorter tag is a decision to take deliberately rather than a default to inherit.
The cipher must have a 16-byte block; GCM is not defined for any other.
Check the tag and, only then, decrypt.
It raises rather than returning an option because "the tag did not match" is the one outcome a caller must not quietly read as empty plaintext. Nothing decrypted is returned before the tag is checked, so a caller cannot act on plaintext that was never authenticated.
Encrypt and authenticate, returning the ciphertext with its tag appended.
The nonce is at least one octet; an empty one aborts. It must never repeat under one key: GCM's confidentiality and its authentication both collapse if it does, and recovering the hash subkey from two messages sharing a nonce is arithmetic rather than cryptanalysis. That is the caller's to guarantee, because only the caller knows where its nonces come from.
The tag length in bytes this instance writes and checks.
SIV, RFC 5297: https://www.rfc-editor.org/rfc/rfc5297
SIV is the one widely deployed mode that does *not* fail catastrophically when a nonce is repeated. Instead of an independent random value, the nonce is just the last component of a vector of associated data, and that vector is hashed with CMAC to produce the synthetic IV. Repeating a nonce therefore reveals only that the same message was sent twice, not the XOR of two plaintexts, which is what a repeated nonce costs in CTR, GCM and CCM.
The price is that the whole plaintext is read twice: once for the CMAC that produces the IV, and once for the CTR pass. SIV is for small, high-value messages — key wrapping, database fields, headers — not for bulk data.
The key is split in half and each half drives a different cipher: one for the CMAC, one for the CTR. Both are passed in, so nothing here assumes AES.
SIV over two 128-bit block ciphers.
The two ciphers are keyed separately — see [`halves`] — and the same object can seal any number of messages. It also keeps one CMAC context between calls so that the two subkeys are not recomputed for every block of every message.
@spec.AeadThe tag length in bytes, which for SIV is also the length of the synthetic IV.
Wrap two block ciphers in SIV.
`mac` produces and checks the synthetic IV and must be keyed with K1; `ctr` encrypts and must be keyed with K2. Both must have a sixteen-byte block.
Open with one associated data string.
Open with a vector of associated data, RFC 5297 §2.7.
Authenticates before anything is returned, and compares the recomputed IV to the one that came with the ciphertext in constant time.
Seal with one associated data string, which is all most callers have.
Seal with a vector of associated data, RFC 5297 §2.6.
The result is the synthetic IV followed by the ciphertext — the IV first, unlike GCM and CCM, which append the tag. A non-empty `nonce` becomes the last associated data component, which is the nonce-based mode of §2.6: it costs nothing extra and makes a repeated message look different.
Passing the components as a vector rather than one string is the point of SIV: `["user", "1"]` and `["user1"]` are different messages, and a header that is parsed in two places can be authenticated field by field without having to be re-serialised.
The tag length in bytes.
Split a key into the two halves SIV wants.
RFC 5297 §2.6 divides the key into K1, which drives the CMAC, and K2, which drives the counter. The halves must be independent: reusing one key for both halves would let the CTR keystream be related to the CMAC output, and the construction's security argument assumes they are separate.
Open in one call.
Seal in one call, with a context that lives just long enough.
SM4-GCM: SM4 under Galois/Counter Mode, NIST SP 800-38D: https://doi.org/10.6028/NIST.SP.800-38D
GCM is defined for any 128-bit block cipher, so this is SM4 plugged into the mode that already exists, offered ready-made because that is how it is used.
The key length in bytes.
The tag length in bytes.
A GCM instance keyed with SM4, ready to seal and open.
Open one message in one call, raising [`@spec.Broken`] if it does not authenticate.
Seal one message in one call.
Five schemes, two traits
RSA, RFC 8017 (PKCS #1 v2.2): https://www.rfc-editor.org/rfc/rfc8017
Signatures — PKCS#1 v1.5 (§9.2), what JOSE calls RS256 and what certificates carry, and PSS (§9.1), what JOSE calls PS256 and what new designs should use — and encryption, RSAES-OAEP (§7.1).
It sits at the top rather than under `sign/` because RSA is one primitive doing two jobs over one key type, and a method must live in its type's package: splitting it would put decryption where the private key is not.
Which digest a signature is taken over.
PKCS#1 v1.5 embeds an ASN.1 algorithm identifier in the block it signs, so the scheme cannot be written over an arbitrary hash the way HMAC can: only a digest with a registered identifier is signable. That constraint is this enum.
Which padding a signature uses.
`Pkcs1` is the deterministic block of RFC 8017 §9.2, which JOSE calls RS256 and almost every certificate in existence carries. `Pss` is the probabilistic scheme of §9.1, which JOSE calls PS256 and which new designs should prefer — its security rests on a proof rather than on the absence of a known attack.
Decrypt an RSAES-OAEP block (RFC 8017 §7.1.2).
Every way the block can be wrong reports the same [`@spec.Tag`], and the checks all run before any of them is acted on: which check failed is exactly what Manger's 2001 attack reads off, so it is not told apart here.
`digest`, `mgf` and `label` mean what they do in [`PublicKey::encrypt`] and must match what encrypted the block.
Read a signing key from its modulus and its two exponents, big-endian.
The public half of a signing key — what verifies what it signs.
The modulus width in bytes, which is also the signature width.
The same key, signing under `digest` and `scheme` instead of the ones it was built with — TLS 1.3 negotiates the PSS digest per handshake, an X.509 issuer names its own per certificate.
Encrypt under RSAES-OAEP (RFC 8017 §7.1.1), returning a block exactly as wide as the modulus.
`seed` is the `hLen` random octets OAEP masks with, and it is a required parameter rather than something drawn here: this library holds no entropy source, and the seed is what makes the same message encrypt differently twice. Go's `rsa.EncryptOAEP` and Rust's `rsa` crate take the randomness the same way. **Reusing a seed across messages destroys OAEP's security** — draw it fresh from the platform's CSPRNG for every call.
`digest` is the hash OAEP runs, and `mgf` the one MGF1 masks with; leaving `mgf` out uses `digest`, which is what Go and Node do, while Python and Java let the two differ, which is why it is a parameter here.
`digest` defaults to SHA-1: it is RFC 8017's own default, and what OpenSSL, Java's `OAEPParameterSpec.DEFAULT` and Node all take. OAEP does not rest on the hash's collision resistance, so this is not the choice it would be for a signature — but Go and Python require the caller to name it, and a new design should pass `digest=Sha256`.
The key's own `digest` and `scheme` govern signatures and are not consulted here; encryption and signing are separate schemes over the same key.
The public exponent, big-endian with no leading zeroes.
The modulus, big-endian and fixed-width.
Read a verification key from its modulus and exponent, big-endian.
The modulus width in bytes, which is also the signature width.
The same key, verifying under `digest` and `scheme`.
ECDSA, FIPS 186-4: https://doi.org/10.6028/NIST.FIPS.186-4 Deterministic nonces, RFC 6979: https://www.rfc-editor.org/rfc/rfc6979 Curve parameters, SEC 2 v2: https://www.secg.org/sec2-v2.pdf
Signatures are `r ∥ s`, fixed-width, which is what JOSE and most wire formats carry; the ASN.1 DER wrapper OpenSSL writes belongs with the other DER.
The scalar this key is written as, big-endian and fixed-width.
Which curve this key is on.
Read a signing key from its scalar, big-endian and fixed-width.
The verification key that matches this signing key: `Q = [d]G`.
A signing key read against a digest of the caller's choosing.
The key as an uncompressed SEC 1 point, `04 ∥ X ∥ Y`.
Which curve this key is on.
Read a public key from a SEC 1 §2.3.3 encoded point.
Both forms are accepted: `04 ∥ X ∥ Y` uncompressed, and `02`/`03 ∥ X` compressed, where the prefix gives the parity of `Y` and the rest is recovered from the curve equation.
The digest is the one the curve is standardly paired with — SHA-256 for the 256-bit curves, SHA-384 for P-384, SHA-512 for P-521 — which is what ES256, ES384 and ES512 mean. Use [`PublicKey::with_hash`] for any other pairing.
Read a public key from its two affine coordinates — the shape a JWK carries.
A public key read against a digest of the caller's choosing.
The two affine coordinates, each fixed-width — the shape a JWK wants.
Ed25519, RFC 8032: https://www.rfc-editor.org/rfc/rfc8032
Signatures are deterministic — the nonce comes out of the key and the message — so there is no entropy source to get wrong, which is the whole reason this scheme replaced ECDSA wherever it could.
The length of a seed, a public key and half a signature.
The length of a signature: `R ∥ S`.
The secret this key was built from — the 32-byte seed for a key from [`PrivateKey::new`], and whatever was handed to [`PrivateKey::of_secret`] otherwise.
Read a signing key from its 32-byte seed.
A signing key from a secret of any length.
[`PrivateKey::new`] takes the 32-byte seed the key format stores. The expansion itself — SHA-512 over the secret, then clamping — is defined for an input of any length; RFC 8032 fixes the seed at 32 octets so that keys have one written form, not because the arithmetic needs it. MariaDB's `client_ed25519` keys a connection with the password directly, which is the protocol this entry point exists for.
Anything that holds a real seed wants `new`: it is the one that refuses a secret of the wrong length, and a length check is worth having.
The verification key that matches this signing key: `A = [s]B`.
The 32 bytes this key is written as.
Read a public key from its 32 compressed bytes.
Ed448, RFC 8032: https://www.rfc-editor.org/rfc/rfc8032
The same scheme as Ed25519 over a wider curve, with three things different enough that the code cannot be shared:
* the curve is untwisted (`a = 1`, not `a = -1`), so the addition law has a different shape; * the hash is SHAKE256 at 114 bytes rather than SHA-512 at 64, because 114 bytes covers two 456-bit scalars; * every scalar and coordinate is 57 bytes, not 32.
The context string is supported, since the domain separator `dom4` is what makes a signature unusable in a protocol that did not ask for it. Both Ed448 and the prehashed Ed448ph are offered, because Ed448ph is the variant for messages that do not fit in memory.
The length of a secret, a public key and half a signature: 57 bytes, which is 456 bits — the bit width `b` in RFC 8032's table, not the field width.
The longest context RFC 8032 allows, since `dom4` writes its length in one octet.
The length of a signature: `R ∥ S`, 114 bytes.
The secret this key was built from.
Read a signing key from its 57-byte secret.
The verification key that matches this signing key: `A = [s]B`.
The point is decoded from its own encoding so that a `PublicKey` always holds the pair together. That decode cannot fail — `[s]B` is a point on the curve by construction — so a failure here is a bug in this file, not something a caller could cause or handle.
Sign a message with a context, RFC 8032 §5.2.6.
The context is optional and defaults to empty, which is the plain Ed448 of the RFC. `prehash` selects Ed448ph, where the message is replaced by its 64-byte SHAKE256 digest before signing — the variant for streams too large to hold in memory.
As with Ed25519 the signature is deterministic: `r` comes from the key's prefix and the message, not from a random source.
The 57 bytes this key is written as.
Read a public key from its 57-byte encoding.
Raises [`@spec.Broken::Weak`] when the bytes are not a point on the curve: a key that does not decode cannot verify anything, and accepting it invites a caller to compare garbage against garbage.
Check a signature with a context, RFC 8032 §5.2.7.
Returns `false` for anything wrong with the signature — a point that does not decode, `S` at or above the group order, the equation not holding — and raises only when the *caller* is at fault, which for Ed448 means a context longer than 255 bytes.
Schnorr signatures over secp256k1, as BIP 340 defines them (2020).
The scheme signs with a key whose public point has an even y coordinate, and verifies by lifting the 32-byte x coordinate back to that same point. That is the whole trick of the x-only public key: a point and its negation share an x, so half of each key pair is discarded, and a signature that would only hold for one of the two is what makes the encoding unambiguous again.
Three tagged hashes in one signature, each with its own tag so that a value hashed for one purpose can never be reinterpreted as another:
aux the optional randomness mixed into the nonce nonce r = H(aux-mixed secret || P || m) challenge e = H(R || P || m)
The nonce is derived rather than random. BIP 340 takes 32 bytes of auxiliary randomness so that a compromised entropy source cannot leak the key, while the derivation still makes a signature reproducible when that input is fixed.
A signing key.
@spec.SignerA verification key: the even-y point whose x coordinate is the key.
@spec.VerifierThe length of a secret key, a public key and half a signature.
A public key is 32 bytes and not 33, because BIP 340 drops the parity byte that SEC1 keeps: the scheme only ever uses the even-y point for a given x.
The length of a signature: `R.x ∥ s`, sixty-four bytes.
The 32 bytes this key is written as.
Read a signing key from its 32 bytes.
The scalar has to lie in `[1, n-1]`. Zero is not a key, and a value at or above the group order is a different scalar — the same number modulo `n` — which would give a signature that verifies against a different key than the one the caller thought they were using.
The verification key that matches this signing key.
The x coordinate is taken from `d·G` and the parity is thrown away; signing then works with whichever of `d` and `n-d` lands on the even-y point.
Sign a message with fresh auxiliary randomness mixed into the nonce.
`aux` is 32 bytes that do not have to be secret and do not have to be random to be correct — but they are what protects the key if the entropy source ever repeats itself, which is why BIP 340 asks for them. Deterministic signing is `sign` with none.
The 32 bytes this key is written as: the x coordinate.
Read a verification key from its 32 bytes.
The bytes are an x coordinate, and not every 32-byte string is one: it has to be below the field prime, and `x³+7` has to be a square. Either failing is a key that cannot exist, so both are refused here rather than at verification time, where the answer would be a silent `false`.
SM2 digital signature, GB/T 32918.2-2016: https://openstd.samr.gov.cn/bzgk/gb/newGbInfo?hcno=A6A3A9F0C2E4B6E9D0C4B1B0A9E1C2C1
The curve is a 256-bit short Weierstrass curve of its own, and the digest is SM3 — a signature is defined over both, so this package carries its own curve arithmetic rather than borrowing another curve's.
A signing key: a scalar in `[1, n-1]`.
@spec.SignerA verification key: the public point.
@spec.VerifierThe default identity a signature is bound to (GB/T 32918.2-2016 §5.5). It is not a secret and not a key; it separates one application's signatures from another's, the way a domain separator does.
The coordinate width in bytes, and the width of each half of a signature.
The 32-byte big-endian encoding of the scalar.
A private key from a scalar, refused unless it is in range.
A private key from its 32-byte big-endian encoding.
The public point this key signs under.
Sign `msg`, bound to `id`. The signature is `r ∥ s`, thirty-two bytes each.
The identity is part of what is signed, so a signature made under one `id` does not verify under another — which is what stops a signature from being lifted from one protocol into a different one.
The uncompressed encoding, `04 ∥ X ∥ Y`.
A public key from a SEC 1 §2.3.3 point: `04 ∥ X ∥ Y` uncompressed, or `02`/`03 ∥ X` compressed. A point off the curve is refused.
Check `sig` against `msg` and `id`. A malformed signature is a `false` answer, not an error: it is an expected thing for the peer to send.
Two parties arriving at one secret
Finite-field Diffie-Hellman over the ffdhe groups of RFC 7919.
The alternative to an elliptic-curve exchange, and the one that still buys something where a curve is not available. Every group here is a safe prime of the form the RFC searched for: p = 2^n - 2^(n-64) + { ... } - 1, generator 2, with the top and bottom 64 bits all ones. Because the subgroup order is the prime (p-1)/2, a peer's value that is not in [2, p-2] is either a unit or a member of a small subgroup, and both are refused rather than exponentiated.
The primes are RFC 7919 constants, and the script that wrote them checked each one for bit length, generator, the all-ones ends, primality and being 7 mod 8 before it emitted a digit.
A private key: a group and an exponent.
One of the five finite-field groups of RFC 7919.
They differ only in size, and the size is the whole choice: a larger group is slower and buys a larger margin against a discrete logarithm. `ffdhe2048` is the smallest the RFC defines and the one TLS picks when it has to.
The generator every ffdhe group uses.
Two, and not because it is convenient: RFC 7919 §3 fixes it, and a group with a different generator is a different group.
The size of the group in bits.
The name the group is known by, which is what a protocol writes in a negotiated list.
The prime, decoded on demand.
Decoding costs a hex conversion; an exponentiation over the same number costs thousands of multiplications, so there is nothing to gain by caching it and a mutable global to lose.
How much security the group is estimated to give, in bits (RFC 7919 §5.1).
It is the number to compare against a symmetric key size: `ffdhe3072` is roughly a 128-bit cipher, and anything smaller than that is the weak link in a handshake that otherwise negotiates AES-128.
The size of the group in bytes, which is also the width every value in it is written at.
The group this exponent belongs to.
Key an exponent.
The exponent is taken as given. RFC 7919 §5.1 suggests a length of twice the security level — 225 bits and up — and leaves it to the caller because a short exponent weakens nothing that the group size has not already fixed, but it does weaken the exchange, so the length is worth thinking about.
Zero is refused: it produces a public key of one and a shared secret of one, which is not a secret at all.
The public value: the generator raised to the secret, modulo the prime, written at the width of the group.
The public value for a secret, without keeping the secret.
Elliptic-curve Diffie-Hellman over the NIST primes, SEC 1 §3.3.1: https://www.secg.org/sec1-v2.pdf
The shape of `kex/x25519`: a public value from a scalar, a shared secret from a scalar and the peer's value. TLS 1.3 names secp256r1 and secp384r1 (RFC 8446 §4.2.7); the secret is the X coordinate (§7.4.2).
The public value for the private `scalar` (big-endian, `curve.size()` bytes, in `[1, n)`): the uncompressed SEC 1 point `04 ∥ X ∥ Y`, which is what a TLS key share carries.
X25519, RFC 7748: https://www.rfc-editor.org/rfc/rfc7748
The Curve25519 Diffie-Hellman, over the field mod 2^255-19. A low-order point is refused rather than returned as an all-zero secret.
The base point, `u = 9`, little-endian (RFC 7748 §4.1).
The length of a scalar, a u-coordinate and a shared secret, in bytes. All three are 32; X25519 has no other size.
The public key for a private one: the scalar applied to the base point.
X448, RFC 7748: https://www.rfc-editor.org/rfc/rfc7748
The Curve448 Diffie-Hellman, over the field mod 2^448 - 2^224 - 1. The same ladder as X25519 with a wider field, which buys a larger security margin than the 128 bits Curve25519 offers — and costs about three times the work per operation.
Unlike X25519 there is no high bit to mask off the u-coordinate: 448 bits is exactly 56 bytes, so every bit of the input is part of the value. A low-order point is refused rather than returned as an all-zero secret.
The base point, `u = 5`, little-endian (RFC 7748 §4.2).
The length of a scalar, a u-coordinate and a shared secret, in bytes. All three are 56; X448 has no other size.
The public key for a private one: the scalar applied to the base point.
ASN.1, PEM, and the formats built on them
ASN.1 Distinguished Encoding Rules, ITU-T X.690: https://www.itu.int/rec/T-REC-X.690
The type-length-value layer that key files and certificates are built on. Nothing here is cryptography; it is here because everything cryptographic that has a file format needs it.
A decoded BIT STRING: the content bytes and how many low bits of the last byte are padding.
One decoded element: the tag byte, the contents, and `raw` — tag, length and contents together. `raw` is what a signature covers and how far to step to reach the next element.
A refused encoding. `Malformed` is a hostile or corrupt certificate; `Unsupported` is well-formed DER this parser deliberately does not read, and the two are separated so a caller can log an interop gap without calling it an attack.
ShowWhether bit `i` is set, counting from the most significant bit of the first byte — the numbering X.509 uses for keyUsage. Bits inside the padding, or past the end, read as unset.
A BIT STRING.
A BOOLEAN. DER admits only 0x00 and 0xFF, so any other octet is refused.
An INTEGER, two's complement as X.690 §8.3 defines it.
The elements a constructed value holds, in encoding order.
An OCTET STRING's contents.
An OBJECT IDENTIFIER in dotted-decimal form. The first octet packs two arcs (X.690 §8.19.4); the rest are base-128, high bit set on every byte but an arc's last.
A DirectoryString or IA5String as text — the string types an X.509 Name and a SAN entry are written in. UniversalString is refused: it is UTF-32 and no certificate in practice uses it.
A UTCTime or GeneralizedTime as seconds since the Unix epoch. RFC 5280 §4.1.2.5 pins both to the seconds-and-`Z` form, and a two-digit year to 1950..2049, so the other shapes X.690 allows are refused rather than guessed at.
Octets of one of the ASCII string types as text. Taken on its own because a subjectAltName tags its entries with the field number, not the string type, so the octets arrive with nothing left to dispatch on.
A DER BIT STRING (tag 0x03) with no unused trailing bits: a leading 0x00 count then `content`.
Whether `tag` is the context-specific `[n]` of an IMPLICIT or EXPLICIT field.
A context-specific constructed value `[tag_num]` wrapping `content` (tag `0xA0 | tag_num`) — the EXPLICIT tagging X.509 uses for the certificate version and extensions.
A DER INTEGER (tag 0x02) from a big-endian magnitude: strip redundant leading zero bytes, then prepend one 0x00 if the top bit is set, so the value stays non-negative (X.690 §8.3).
Encode a definite length (X.690 §8.1.3): the short form is the single byte for lengths under 128; the long form is `0x80 | n` followed by the length in `n` big-endian bytes.
A DER NULL (tag 0x05, empty).
A DER OCTET STRING (tag 0x04).
A DER OBJECT IDENTIFIER (tag 0x06) from its arcs (X.690 §8.19): the first byte encodes `40·arc1 + arc2`, and each later arc is base-128 big-endian with the high bit set on every byte but the last.
Read the one element that is the whole of `input`. Bytes after it are an error — a certificate with a payload glued to its end is two documents, and the second one is not covered by the signature over the first.
Read the element at the head of `input`. Raises rather than reading past the end: the length is checked against the bytes that remain, in 64-bit arithmetic so a length near 2^31 cannot wrap the check.
A DER SEQUENCE (tag 0x30): the concatenated `elements`.
A DER SET (tag 0x31): the concatenated `elements`.
A tag-length-value: the `tag` byte, the definite length of `value`, then `value`.
A DER UTCTime (tag 0x17), the `YYMMDDHHMMSSZ` form X.509 validity uses before 2050.
A DER UTF8String (tag 0x0c).
The elliptic curves a key can live on, by name: FIPS 186-4 §D.1.2 for the NIST primes, SEC 2 v2 §2.4.1 for secp256k1 (https://www.secg.org/sec2-v2.pdf).
A public package of its own because the name is vocabulary every caller needs — to build a key, to read one out of a certificate — while the arithmetic behind it stays internal.
Which curve a key lives on.
The four short-Weierstrass curves that protocols in use actually name: the three NIST primes, which TLS and JOSE speak, and secp256k1, which Bitcoin and Ethereum speak. They differ only in their parameters, so the arithmetic below is written once and each curve is a row in a table.
The width in bytes of a coordinate, a scalar, and each half of a signature.
PKCS#1 (RFC 8017 appendix A) is RSA on its own: two integers for a public key, nine for a private one. PKCS#8 (RFC 5958) is the generic wrapper that says which algorithm the octets inside belong to, and SubjectPublicKeyInfo is the same idea for the public half — those two are what a modern key file holds. SEC1 (RFC 5915) is the elliptic-curve private key, with the curve name and the public point beside the scalar.
Key files: PEM (RFC 7468) around DER (X.690), in the four shapes a key is actually stored in.
This package is an encoding layer only: it moves numbers between bytes and structures and never touches an algorithm. Building a usable key from what it returns is one call into `mooncrypt/rsa` or `mooncrypt/curve`.
An elliptic-curve private key as SEC1 (RFC 5915) holds it.
The curve name and the public point are both optional in the format. A key without them is still a key — the scalar is the secret — but it cannot be used without being told which curve it is on, so [`curve`](#curve) being `None` is the parser saying "this file is not enough on its own".
An RSA signing key as PKCS#1 holds it.
The seven numbers after the modulus and the two exponents are the CRT parameters: they let a signer do two half-size exponentiations instead of one full-size one. They are carried rather than recomputed because a format that stores them would lose information otherwise, and because computing them back from silence costs two big-integer factorisations per load.
An RSA verification key as PKCS#1 holds it: two integers.
How wide a PEM body line is before it wraps, in base64 characters.
RFC 7468 §2 asks for exactly 64, and every other width in the world is a parser's problem rather than a writer's — but no width at all is what makes a file hard to read in a terminal, so this is not optional.
Wrap a DER blob as a PEM block: the `BEGIN` line, the base64 body wrapped at [`line_width`], and the `END` line.
The label is written as given and appears twice, so the two lines agree — RFC 7468 §3 requires that, and a reader that checks it is entitled to reject the file otherwise.
The curve a namedCurve identifier names, or `None` for a curve this library does not have parameters for.
The namedCurve identifier of a curve, as the arcs a SEC1 key writes in its `[0]` field and a SubjectPublicKeyInfo writes in its parameters.
Read a PEM block: its label and the DER it wraps.
Loose in the ways a key file is loose in practice — leading text, blank lines, carriage returns and trailing whitespace are all tolerated, because a key that came out of an email or a Windows editor is still the same key. Strict about the two things that matter: the `END` label must match the `BEGIN` label, and the body must decode as base64.
Read a PKCS#8 `PrivateKeyInfo` and return the octets inside, which for an RSA key are a PKCS#1 `RSAPrivateKey`.
The algorithm identifier is checked rather than returned: a caller that got back the octets of an EC key and parsed them as RSA integers would not find out until the numbers failed to make sense, and that is a long way from the mistake.
Read a PKCS#1 `RSAPrivateKey`.
Read a PKCS#1 `RSAPublicKey`.
Read a SEC1 `ECPrivateKey`.
The optional fields are found by their tag rather than by their position, because RFC 5915 marks them `OPTIONAL` and a writer that omits the curve is encoding the same type as one that includes it.
Read a `SubjectPublicKeyInfo` and return the DER inside its BIT STRING.
Read an EC `SubjectPublicKeyInfo`: the curve it names and the point.
Wrap a PKCS#1 `RSAPrivateKey` in the PKCS#8 `PrivateKeyInfo` of RFC 5958.
The PKCS#1 `RSAPrivateKey` of RFC 8017 §A.1.2, version 0.
The two-prime form is the only one written: `version` 1 exists for keys with more than two primes, which no library here generates and no format in practice carries.
The PKCS#1 `RSAPublicKey` of RFC 8017 §A.1.1.
The SEC1 `ECPrivateKey` of RFC 5915 §3, with the curve always written.
`public` is written when it is given. It is derivable from the scalar — it is the scalar times the generator — so a file that carries it is carrying a convenience, not a second secret.
Wrap a PKCS#1 `RSAPublicKey` in a `SubjectPublicKeyInfo` (RFC 5280 §4.1.2.7).
A `SubjectPublicKeyInfo` for an EC key: the algorithm identifier names the curve, and the BIT STRING holds the point in its uncompressed form.
A MAC of the clock
One-time passwords (RFC 4226) and their time-based form (RFC 6238).
The two standards are one algorithm with a different counter: HOTP counts events, TOTP counts thirty-second steps since the Unix epoch, and both HMAC that counter and read four bytes out of the digest.
This package depends on a hash rather than on a clock. A time-based code is computed from a unix time the caller passes in, which keeps the whole package testable against a fixed table and leaves "what time is it" to the program that already knows.
A one-time password generator: a shared secret and a code length.
The secret is the only thing that has to be kept, and it is shared out of band — the QR code an authenticator app scans is nothing but this key and a label.
How many digits a code carries.
An enumeration rather than a number because the value is a modulus, not a quantity: six digits means "modulo one million, printed with leading zeroes". RFC 4226 §5.3 fixes the range at six to eight; nine is here because a nine-digit code still fits the 31-bit value dynamic truncation produces, and because banks use them.
Thirty seconds: the time step TOTP is defined over (RFC 6238 §5.2).
A different step is legal and some systems use one, which is why every time-based function here takes it — but it is not a tuning knob, because a step that disagrees with the verifier's produces codes that never match.
How many digits a code of this kind has.
Whether `code` is the time-based code for `unix_time`, or for one of the `skew` steps on either side of it.
Both directions are checked because the clock that produced the code and the clock that checks it are different clocks, and either may be the fast one.
Whether `code` is the counter-based code for `counter`, or for one of the `ahead` counters after it.
The look-ahead is the HOTP resynchronisation of RFC 4226 §7.4: a token that has been pressed more times than the server counted is ahead of it, and accepting a few steps forward is what lets the two catch up. It is a small number for a reason — every step tried is another guess an attacker gets.
The counter-based code of RFC 4226 §5.3, keyed with SHA-1 as that standard specifies.
The counter-based code of RFC 4226 §5.3 over any hash.
RFC 6238 extends HOTP by allowing SHA-256 and SHA-512, and appendix B of that RFC gives a table for each — which is why this takes a factory rather than hard-coding SHA-1.
Key a generator.
The secret is taken as given: RFC 4226 leaves its length to the application, and the 20-byte recommendation is a recommendation because the security is the HMAC's, not the code's.
The time-based code of RFC 6238, keyed with SHA-1.
`unix_time` is seconds since 1970, taken as an argument rather than read from a clock: the package then has no opinion about time and can be tested against a fixed table.
The time-based code of RFC 6238 over any hash.
The counter-based code of RFC 4226 for a six-digit code, the shape the standard's own examples have.
The time-based code of RFC 6238 for a six-digit code.