Sign in

    justinwongcn/moonpgp/primitives does not have a README file

    BlockCipher

    pub(open) trait BlockCipher {
    fn block_size(Self) -> Int
    fn encrypt_block(Self, BytesView) -> Bytes
    fn decrypt_block(Self, BytesView) -> Bytes
    fn wipe(Self) -> Unit
    }

    Symmetric block cipher abstraction. OpenPGP modes (CFB variants, AEAD) only need single-block encrypt/decrypt with a fixed key, so this is all the rest of the module is allowed to know about the primitive.

    Block size is always 16 for every algorithm OpenPGP 0/1 modes use with AES; still, callers must consult block_size rather than assume.

    CipherAlgo

    pub(all) enum CipherAlgo {
    Tripledes
    Cast5
    Aes128
    Aes192
    Aes256
    } derive(Eq,
    Debug
    )

    Symmetric cipher algorithms, RFC 9580 §9.3 numbers. TripleDES (2) and CAST5 (3) exist for the read side only — RFC 4880 §9.2 makes 3DES a MUST-implement and CAST5 was GnuPG's pre-AES default; the generation paths always pick AES.

    CipherAlgo::equal

    fn CipherAlgo::equal(CipherAlgo, CipherAlgo) -> Bool

    CipherAlgo::key_size

    fn CipherAlgo::key_size(self : CipherAlgo) -> Int

    CipherAlgo::not_equal

    fn CipherAlgo::not_equal(x : CipherAlgo, y : CipherAlgo) -> Bool

    CipherAlgo::number

    fn CipherAlgo::number(self : CipherAlgo) -> Int

    HashId

    pub(all) enum HashId {
    Md5
    Sha1
    Ripemd160
    Sha224
    Sha256
    Sha384
    Sha512
    } derive(Eq,
    Debug
    )

    Hash algorithms used by OpenPGP, identified by their RFC 9580 §9.5 algorithm numbers. This is the PGP-level identity; concrete digest computation is delegated to moonbitlang/x/crypto inside this package only, so the hash backend can be swapped without touching callers.

    HashId::block_size

    fn HashId::block_size(self : HashId) -> Int

    Digest function block size in bytes (as used by HMAC, RFC 2104).

    HashId::der_prefix

    fn HashId::der_prefix(self : HashId) -> Bytes

    DER DigestInfo prefix for this hash (PKCS#1 v1.5 signatures).

    HashId::digest

    fn HashId::digest(self : HashId, data : BytesView) -> Bytes

    Digest a single buffer.

    HashId::digest_size

    fn HashId::digest_size(self : HashId) -> Int

    Size of the digest output in bytes.

    HashId::equal

    fn HashId::equal(HashId, HashId) -> Bool

    HashId::hmac

    fn HashId::hmac(self : HashId, key : BytesView, data : BytesView) -> Bytes

    HMAC (RFC 2104) over a single buffer, using this hash algorithm.

    HashId::not_equal

    fn HashId::not_equal(x : HashId, y : HashId) -> Bool

    HashId::number

    fn HashId::number(self : HashId) -> Int

    HashId::to_repr

    ARGON2_MAX_MEMORY_KIB

    let ARGON2_MAX_MEMORY_KIB : Int

    Hard ceiling on Argon2 memory, in KiB: 2^21 KiB = 2 GiB, the first RFC 9106 §4 recommended Argon2id option and what RFC 9580 Appendix A.5 and A.12 use. A 20-octet S2K specifier can ask for up to 2^31 KiB, so callers must never allocate an attacker-chosen size unchecked.

    BLOCK_SIZE

    let BLOCK_SIZE : Int

    Block size used by every supported algorithm.

    DEFAULT_ARGON2_MAX_MEMORY_KIB

    let DEFAULT_ARGON2_MAX_MEMORY_KIB : Int

    Default Argon2 memory policy (KiB) for the public decrypt entry points: 2^16 KiB = 64 MiB (S7).

    An Argon2 S2K specifier is attacker-controlled and the derivation runs before any authentication, so the cap decides how much memory one unauthenticated message can cost — and a malicious SKESK may pair the full cap with up to 255 passes, multiplying both memory and CPU. The 64 MiB default matches the profile every mainstream writer defaults to (go-crypto/gopenpgp, OpenPGP.js), so ordinary interop never needs to raise it, while GnuPG cannot produce an Argon2 S2K at all.

    Messages that genuinely ask for more — RFC 9580 Appendix A.5/A.12 use the RFC 9106 first recommended option (2 GiB, ARGON2_MAX_MEMORY_KIB) — must pass the wider policy explicitly:

    decrypt_with_password(msg, pass, argon2_max_memory_kib=@primitives.ARGON2_MAX_MEMORY_KIB)

    scripts/acceptance.sh and the slow:* tests do exactly that, so the widened call is exercised by the release gate.

    argon2id

    fn argon2id(pass : BytesView, salt : BytesView, passes : Int, m_kib : Int, parallelism : Int, tag_len : Int, max_memory_kib? : Int) -> Bytes raise

    Argon2id (RFC 9106) with empty secret and associated data: the exact invocation RFC 9580 §3.7.1.4 prescribes for S2K specifier type 4.

    m_kib is in kibibytes (not octets) and must be at least 8 * parallelism. max_memory_kib lets a caller parsing untrusted packets enforce a tighter policy than the 2 GiB hard ceiling.

    Example

    test {
    let tag = @primitives.argon2id(b"password", b"somesalt", 1, 64, 1, 24)
    inspect(
    @primitives.to_hex(tag),
    content="655ad15eac652dc59f7170a7332bf49b8469be1fdb9c28bb",
    )
    }

    argon2id_with_secret_ad

    fn argon2id_with_secret_ad(pass : BytesView, salt : BytesView, secret : BytesView, ad : BytesView, passes : Int, m_kib : Int, parallelism : Int, tag_len : Int, max_memory_kib? : Int) -> Bytes raise

    Argon2id with a caller-chosen secret and associated data. OpenPGP never uses secret/ad, but RFC 9106's own known-answer test does, and this is the entry point that reproduces it.

    blake2b

    fn blake2b(data : BytesView, out_len : Int) -> Bytes raise

    BLAKE2b with an explicit digest length out_len (1..64), unkeyed, over data. Argon2 calls this with 64 and with smaller sizes for its H' variable-length hash.

    Example

    test {
    let digest = @primitives.blake2b(b"abc", 64)
    inspect(
    @primitives.to_hex(digest),
    content=(
    #|ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923
    ),
    )
    }

    cipher_algo_of_number

    fn cipher_algo_of_number(n : Int) -> CipherAlgo?

    concat2

    fn concat2(a : BytesView, b : BytesView) -> Bytes

    Concatenate two byte strings.

    concat3

    fn concat3(a : BytesView, b : BytesView, c : BytesView) -> Bytes

    Concatenate three byte strings.

    concat4

    fn concat4(a : BytesView, b : BytesView, c : BytesView, d : BytesView) -> Bytes

    Concatenate four byte strings.

    ct_equal

    fn ct_equal(a : BytesView, b : BytesView) -> Bool

    Constant-time byte-string comparison for secret material (key check sums, MAC tags). Length mismatch returns false immediately; content comparison itself leaks nothing beyond the length.

    from_hex

    fn from_hex(s : String) -> Bytes raise

    Parse a lowercase/uppercase hex string.

    hash_id_of_number

    fn hash_id_of_number(n : Int) -> HashId?

    hkdf_sha256

    fn hkdf_sha256(ikm : BytesView, salt : BytesView, info : BytesView, out_len : Int) -> Bytes raise

    HKDF-SHA256 (RFC 5869 §2): extract then expand to out_len octets.

    Example

    test {
    let ikm = @primitives.from_hex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b")
    let okm = @primitives.hkdf_sha256(
    ikm,
    @primitives.from_hex("000102030405060708090a0b0c"),
    @primitives.from_hex("f0f1f2f3f4f5f6f7f8f9"),
    42,
    )
    inspect(
    @primitives.to_hex(okm),
    content=(
    #|3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865
    ),
    )
    }

    new_aes

    fn new_aes(key : BytesView) -> &BlockCipher raise

    Trait-object constructor used by every OpenPGP mode. x/crypto would raise CryptoError on every call for a bad key length; reject it up front instead.

    new_block_cipher

    fn new_block_cipher(algo : CipherAlgo, key : BytesView) -> &BlockCipher raise

    Construct the block cipher for algo under key.

    new_cast5

    fn new_cast5(key : BytesView) -> &BlockCipher raise

    Construct the CAST5 block cipher for OpenPGP algorithm 3 (16-octet key, 16 rounds).

    new_des

    fn new_des(key : BytesView) -> &BlockCipher raise

    Single DES — exposed for the FIPS known-answer tests only.

    new_des_ede3

    fn new_des_ede3(key : BytesView) -> &BlockCipher raise

    Construct the TripleDES (EDE, three independent 8-octet keys, 24 octets total) block cipher used for OpenPGP algorithm 2.

    pad_left

    fn pad_left(data : BytesView, size : Int) -> Bytes

    Left-pad data with zeros to size bytes (i2osp helper for fixed-width integers).

    pad_right

    fn pad_right(data : BytesView, size : Int) -> Bytes

    Right-pad data with zeros to size bytes.

    random_below

    fn random_below(max : Int) -> Int raise

    A uniform random integer in [0, max) — used for Miller-Rabin bases and PKCS#1 padding selection.

    random_bytes

    fn random_bytes(n : Int) -> Bytes raise

    Cryptographically strong random bytes from the platform entropy source.

    This deliberately does NOT go through @random.Rand::new(): the standard library silently falls back to a fixed seed when the platform cannot provide entropy, which is catastrophic for key/session material. Here a failed entropy read is a hard error.

    to_hex

    fn to_hex(data : BytesView) -> String

    Lowercase hex encoding (test vectors, fingerprints, key ids).

    unix_now

    fn unix_now() -> Int64

    utf8_bytes

    fn utf8_bytes(s : String) -> Bytes

    UTF-8 encoding of a string for PGP wire use (User IDs, passphrases, literal file names). String::to_bytes/Bytes::of_string are UTF-16LE and must never be used on wire material.

    xor_bytes

    fn xor_bytes(a : BytesView, b : BytesView) -> Bytes raise

    XOR two equally long byte strings.