Sign in

    mooncrypt

    Cryptographic algorithms for MoonBit, one per package.

    crypto
    cryptography
    hash
    sha256
    hmac
    ed25519
    ecdsa
    rsa
    jwt
    moonbit
    Download zip
    Version
    0.4.4
    License
    Apache-2.0
    Last updated
    48 minutes ago
    Downloads
    2K
    mooncrypt

    Cryptographic algorithms for MoonBit, one algorithm to a package.

    let sha256 = fn() -> &@spec.Hash { @sha2.Hasher::new() }

    // A digest, a MAC, and a key derived from a password.
    let digest = @sha2.hash(b"the quick brown fox"[:])
    let tag = @hmac.mac(b"shared secret"[:], b"the quick brown fox"[:], sha256)
    let key = @pbkdf2.key(b"correct horse"[:], b"battery staple"[:], sha256,
    rounds=600000, len=32)

    // Authenticated encryption, over any 128-bit block cipher.
    let box = @sm4gcm.new(key[0:16])
    let sealed = box.seal(nonce=nonce[:], plain=b"the quick brown fox"[:], aad=b"v1"[:])
    box.open(nonce=nonce[:], cipher=sealed[:], aad=b"v1"[:]) // the plaintext, or a raise

    // A signature. Ed25519 signing is deterministic: the same key and message
    // always give the same bytes. A fresh key takes its randomness from a source
    // the caller hands over, here the platform's, from moonrand.
    let sk = @ed25519.PrivateKey::generate(@system.source)
    let sig = sk.sign(b"the quick brown fox"[:])
    sk.public().verify(b"the quick brown fox"[:], sig[:]) // true

    Run moon run src/examples/tour for the whole surface in one go — fifty-odd algorithms, each reached through the same handful of traits.

    #Packages

    Import the ones you use. A package nothing imports is not linked, which is what features buys in other languages, without a build-time switch.

    PackageWhatSpecification
    specThe Hash, Mac, Block, Aead, Signer and Verifier traits, constant-time comparison, erasure—
    hash/sha2SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256FIPS 180-4
    hash/sha1SHA-1 — legacy: collisions are cheap enough to be routineFIPS 180-4
    hash/sha3SHA3-224/256/384/512, SHAKE128 and SHAKE256, and the cSHAKE encoding rules KMAC is built fromFIPS 202, SP 800-185
    hash/blake2BLAKE2b and BLAKE2s, with key, salt and personalisationRFC 7693
    hash/blake3BLAKE3, including keyed and derive-key modes and the extendable outputBLAKE3 spec
    hash/sm3SM3GB/T 32905-2016
    hash/md5MD5 — legacy, and broken for anything adversarialRFC 1321
    hash/ripemdRIPEMD-160 — legacy: a 1996 design kept for the data that already uses itDobbertin–Bosselaers–Preneel
    mac/hmacHMAC over any HashRFC 2104, FIPS 198-1
    mac/poly1305Poly1305, the one-time authenticatorRFC 8439 §2.5
    mac/cmacCMAC over any Block, any tag length up to the block sizeSP 800-38B
    mac/gmacGMAC, GCM's authenticator on its ownSP 800-38D
    mac/kmacKMAC128 and KMAC256, and the XOF formSP 800-185 §4
    mac/siphashSipHash-2-4, a 64-bit keyed hashAumasson & Bernstein 2012
    sum/crc32CRC-32 and CRC-32CIEEE 802.3, RFC 3720
    sum/crc16The common CRC-16 variantsCRC catalogue
    sum/adler32Adler-32RFC 1950 §9
    sum/fnvFNV-1 and FNV-1a, 32 and 64 bitFowler–Noll–Vo
    sum/xxhashXXH32 and XXH64, with a seedxxHash spec
    sum/murmur3MurmurHash3 x86_32 and x64_128Appleby
    kdf/hkdfHKDF, extract and expand separatelyRFC 5869
    kdf/pbkdf2PBKDF2RFC 8018 §5.2
    kdf/scryptscrypt, with presets for interactive and sensitive use, and the parameter checkRFC 7914
    kdf/argon2Argon2d, Argon2i and Argon2id, with secret and associated dataRFC 9106
    kdf/bcryptbcrypt, the $2b$ text format and a constant-time verifyProvos & Mazières 1999
    kdf/concatThe Concat KDF, as a NIST key agreement derives from a shared secretSP 800-56A §5.8.1
    curveThe curves an EC key can live on — P-256, P-384, P-521, secp256k1 — and their coordinate width; sign/ecdsa and kex/ecdh re-export it, import it to name a curveFIPS 186-4, SEC 2
    sign/ed25519Ed25519RFC 8032
    sign/ed448Ed448, with context and the prehash formRFC 8032
    sign/ecdsaECDSA over P-256, P-384, P-521 and secp256k1, with RFC 6979 noncesFIPS 186-4, RFC 6979
    sign/schnorrSchnorr over secp256k1, x-only keys and allBIP 340
    sign/sm2SM2, with the signer identity mixed into the hashGB/T 32918.2-2016
    rsaRSA PKCS#1 v1.5 and PSS signatures, OAEP encryptionRFC 8017
    cipher/aesAES-128, AES-192 and AES-256, both directionsFIPS 197
    cipher/sm4SM4GB/T 32907-2016
    cipher/camelliaCamellia-128/192/256RFC 3713
    cipher/modeCBC and CTR over any Block, with PKCS#7 paddingSP 800-38A
    cipher/chacha20ChaCha20 with the 96-bit nonce, and one block on its own for QUIC's header protectionRFC 8439 §2.3–2.4
    cipher/xchacha20XChaCha20, the 192-bit nonce formdraft-irtf-cfrg-xchacha
    cipher/blowfishBlowfish — legacy, and the core of bcryptSchneier 1993
    cipher/desDES and triple DES, E-D-E in both key forms — legacy: a 56-bit keyFIPS 46-3
    cipher/rc4RC4 and its keystream — legacy: the first bytes are biased—
    cipher/salsa20Salsa20 — legacy: superseded by ChaCha20Bernstein 2005
    aead/gcmGCM over any 128-bit block cipher, any nonce length, the seven tag lengthsSP 800-38D
    aead/chachapolyChaCha20-Poly1305RFC 8439 §2.8
    aead/sm4gcmSM4-GCMSP 800-38D, GB/T 32907
    aead/ccmCCM over any 128-bit block cipher, any even tag length from 4 to 16 bytesSP 800-38C, RFC 3610
    aead/sivAES-SIV, nonce misuse resistant, with the multi-AD formRFC 5297
    kex/x25519X25519, with a low-order point refusedRFC 7748
    kex/x448X448, with the same refusalRFC 7748
    kex/ecdhECDH over P-256, P-384, P-521 and secp256k1, with an off-curve point refusedSEC 1 §3.3.1
    kex/dhFinite-field Diffie-Hellman over the five ffdhe groups, with a range check on the peer's shareRFC 7919
    otpHOTP and TOTP, 6 to 9 digits, with a clock skew windowRFC 4226, RFC 6238
    keyKey files: PEM around DER, in PKCS#1, PKCS#8, SPKI and SEC1 shapesRFC 7468, RFC 8017, RFC 5958, RFC 5915
    asn1ASN.1 DER, read and write — the layer a key file and a certificate are built onITU-T X.690

    Each package's moon.pkg names the specification it implements and links to it.

    #The contracts

    Everything is written against five traits in spec, so a digest or a signature scheme that is not here becomes usable throughout the library the day it implements one of them. HMAC takes a function that makes a hash rather than a hash function, which is why one HMAC serves every digest — including yours.

    Failures raise. @spec.Broken says whether a length was wrong, and with what was expected, or whether an authentication tag did not match. Verification is the exception: it answers Bool, because a bad signature is an expected outcome, not an exceptional one.

    Secrets are compared with @spec.eq, which reads both inputs to the end. It is the only comparison the library offers, so there is no wrong one to reach for.

    #Configuration

    Everything a caller chooses is already an argument: kind picks the SHA-2, SHA-3, BLAKE2, KMAC or xxHash variant, curve the ECDSA curve, digest and scheme the RSA padding and its hash, salt and info the HKDF inputs, params the scrypt and Argon2 cost, cost bcrypt's, tag the CCM, SIV and GMAC tag length, group the RFC 7919 group, digits the one-time password length, and Pss(salt=32) carries its own salt length rather than assuming one.

    What is not an argument is what a specification fixes: HMAC's inner and outer pads, MD5's constants, SHA-2's round constants and initial values, the curve parameters, Ed25519's field constants. Those are not configuration, and making them settable would only let a caller compute something that is not the algorithm.

    Three things have deliberately no default:

    SettingWhy there is no default
    pbkdf2.key(rounds~)The iteration count is the whole security argument and goes out of date silently. Python's hashlib.pbkdf2_hmac, Go's pbkdf2.Key and Node's crypto.pbkdf2 all require it, so requiring it is the mainstream default
    len~ on HKDF and PBKDF2How many bytes of key are wanted is a property of what the key is for
    gcm.Gcm::new(tag~)16 — the full 128-bit tag, the only length SP 800-38D appendix C leaves unqualified. The shorter ones it defines are there, and each costs forgery resistance the appendix works out

    The checks that could have been switches are not. aes.Cipher::new and camellia.Cipher::new read the variant off the key length rather than taking one, because the length is what distinguishes the variants and a key that disagrees with a named variant would be a second way to be wrong. x25519.shared and x448.shared always refuse a low-order point: the check is a MAY in RFC 7748 §6.1 and a MUST in TLS 1.3, and a switch to turn it off would only make the dangerous call the short one. dh.Private::shared always refuses a peer share outside [2, p-2] for the same reason, and every AEAD's open raises rather than returning an empty plaintext when the tag does not match.

    The aborts in this library are programming errors rather than runtime conditions: fewer than one PBKDF2 round, a negative key length, more than 255 HKDF digests from one key, a modulus too small for the digest it must carry. None of them can arise from input, so none of them is a raise a caller could usefully catch.

    #What is checked

    Every algorithm is tested against the vectors its specification publishes — FIPS 180-4's worked examples, RFC 4231, RFC 5869 appendix A, RFC 6070, RFC 8032 §7.1, RFC 6979 §A.2.5, RFC 3713 appendix A, RFC 6229, RFC 9106 §5, RFC 7914 §12, RFC 4226 appendix D, RFC 6238 appendix B, BIP 340's CSV, and the CAVP response files where NIST publishes them — and, where a specification publishes none, against signatures produced by a third-party implementation. The curve parameters were derived from a third-party library rather than transcribed, and checked: the generator on the curve, the order annihilating it.

    Where a generator exists in scripts/, it is held to two conditions before it will emit a row: it must first reproduce a published answer, and then agree with an independent implementation. A misread specification breaks the first; a shared mistake with the reference breaks the second. Between them they caught two real bugs during this work — a wrong RIPEMD-160 finalisation and a Schnorr tag hash that concatenated its prefix without hashing the result.

    Beyond the vectors, each hash is checked for agreement between one-shot and chunked writes at every split around a block boundary, and each signature scheme is checked to refuse a bent byte, a truncated signature, a foreign key and an out-of-range scalar. Each AEAD is checked at the tag lengths it defines and with and without associated data, and each block cipher at every key length it takes, because those are the branches a vector copied straight from a specification never reaches.

    #Randomness

    No algorithm package reaches for entropy itself. Every private key type has generate(source), and X25519, X448 and ECDH have a generate(source) for their scalars; source is any moonrand Source, the platform's (@system.source) or one of the program's own. Curve scalars are drawn as FIPS 186-5 A.2.2 draws them, and the ECDSA tests replay the bytes Go's ecdsa.GenerateKey sampled and expect the key Go made. An SM2 key is drawn from [1, n - 2], as GB/T 32918.1 asks, and a key of n - 1 is refused.

    #What is not here

    Post-quantum schemes: the rest of the plan, with the tracking list kept with the project.

    Certificates and tokens are not cryptography and live in mooncred; protocols live in moonhttp, moontls and moonquic; encodings live in moonbase.

    #Legacy algorithms

    Five packages implement things no new design should start with, and each says so at the top of its module: hash/md5, hash/sha1, cipher/des, cipher/rc4 and cipher/salsa20, along with the older entries in hash/ripemd and cipher/blowfish. They are here because data encrypted or signed with them still has to be read, and because a protocol in use may still mandate one.

    They are not deprecated past the point of working: each passes the same vectors as everything else, and MD5's and SHA-1's documentation say what they are still good for (nothing that involves an adversary choosing the input) and what they are not.

    #Install

    moon add moonbitstack/mooncrypt

    #Licence

    Apache-2.0.