crypton: Cryptography Primitives sink

[ bsd3, cryptography, library ] [ Propose Tags ] [ Report a vulnerability ]

A repository of cryptographic primitives. . * Symmetric ciphers: AES, DES, 3DES, CAST5, Blowfish, Twofish, Camellia, RC4, Salsa, XSalsa, ChaCha. . * Hash: SHA1, SHA2, SHA3, SHAKE, MD2, MD4, MD5, Keccak, Skein, Ripemd, Tiger, Whirlpool, Blake2 . * MAC: HMAC, KMAC, Poly1305 . * Asymmetric crypto: DSA, RSA, DH, ECDH, ECDSA, ECC, Curve25519, Curve448, Ed25519, Ed448 . * Key Derivation Function: PBKDF2, Scrypt, HKDF, Argon2, BCrypt, BCryptPBKDF . * Cryptographic Random generation: System Entropy, Deterministic Random Generator . * Data related: Anti-Forensic Information Splitter (AFIS) . If anything cryptographic related is missing from here, submit a pull request to have it added. This package strives to be a cryptographic kitchen sink that provides cryptography for everyone. . Evaluate the security related to your requirements before using. . Read Crypto.Tutorial for a quick start guide.


[Skip to Readme]

Modules

[Index] [Quick Jump]

Flags

Manual Flags

NameDescriptionDefault
support_aesni

allow compilation with AESNI on system and architecture that supports it

Enabled
support_rdrand

allow compilation with RDRAND on system and architecture that supports it

Enabled
support_pclmuldq

Allow compilation with pclmuldq on architecture that supports it

Enabled
support_sse

Use SSE optimized version of (BLAKE2, ARGON2)

Disabled
support_s2n_bignum

Use the vendored s2n-bignum assembly for the NIST prime curves on x86-64 and AArch64. See cbitss2nREADME.md.

Enabled
integer-gmp

Whether or not to use GMP for some functions

Enabled
support_deepseq

add deepseq instances for cryptographic types

Enabled
old_toolchain_inliner

use -fgnu89-inline to workaround an old compiler linker glibc issue.

Disabled
check_alignment

extra check on alignment in C layers, which cause lowlevel assert errors. for debugging only.

Disabled
use_target_attributes

use GCC / clang function attributes instead of global target options.

Enabled

Use -f <flag> to enable a flag, or -f -<flag> to disable that flag. More info

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.31, 0.32, 0.33, 0.34, 1.0.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4, 1.0.5, 1.0.6, 1.1.0, 1.1.1, 1.1.2, 1.1.3, 1.1.4, 1.1.5, 2.0.0, 2.0.1, 2.1.0, 2.1.1, 2.1.2 (info)
Change log CHANGELOG.md
Dependencies base (>=4.13 && <5), base16 (>=1.0), bytestring, deepseq, integer-gmp, primitive (>=0.9), ram (>=0.20.1 && <0.23), text, Win32 [details]
Tested with ghc ==9.2.8 || ==9.4.8 || ==9.6.7 || ==9.8.4 || ==9.10.2 || ==9.12.4 || ==9.14.1
License BSD-3-Clause
Copyright Vincent Hanquez <vincent@snarc.org>
Author Vincent Hanquez <vincent@snarc.org>
Maintainer Kazu Yamamoto <kazu@iij.ad.jp>
Uploaded by KazuYamamoto at 2026-09-26T08:43:37Z
Category Cryptography
Home page https://github.com/kazu-yamamoto/crypton
Bug tracker https://github.com/kazu-yamamoto/crypton/issues
Source repo head: git clone https://github.com/kazu-yamamoto/crypton
Distributions Arch:1.0.5, Fedora:1.0.5, LTSHaskell:1.0.6, Stackage:1.1.5
Reverse Dependencies 125 direct, 3864 indirect [details]
Downloads 7913 total (103 in the last 30 days)
Rating 2.0 (votes: 1) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2026-09-26 [all 1 reports]

Readme for crypton-2.1.2

[back to package description]

GitHub Actions status

crypton

Crypton is a fork from cryptonite with the original author's permission.

Crypton is a haskell repository of cryptographic primitives. Each crypto algorithm has specificities that are hard to wrap in common APIs and types, so instead of trying to provide a common ground for algorithms, this package provides a non-consistent low-level API.

If you have no idea what you're doing, please do not use this directly. Instead, rely on higher level protocols or implementations.

Side channels

AES is where this matters most, and which implementation runs is decided at runtime from what the processor has.

On x86-64 with AES-NI and carry-less multiply, and on AArch64 with the ARMv8 cryptographic extension, AES and GHASH are instructions rather than tables. crypton's AES and AES-GCM then make no branch and no memory access that depends on the key or on the data: the secrets stay in vector registers and never reach one a branch can test, which the generated code is checked against. Every x86-64 part since about 2010 and every AArch64 part in ordinary use has these.

Where neither is present crypton falls back to a table-driven AES, which indexes a 256-byte substitution table with data derived from the key and the input. That is not constant time, and on a machine where an attacker can observe the cache it is open to a timing attack. The fallback exists so that the library builds and runs everywhere; it is not meant for a setting where that matters.

Crypto.System.CPU.processorOptions says which is in use. AESNI in that list means the instruction path, and PCLMUL that GHASH has its instruction too; without AESNI it is the tables. The list also reports RDRAND, which is unrelated to this.

ghci> import Crypto.System.CPU
ghci> processorOptions
[AESNI,PCLMUL]

RSA is the other place to know about, and there the choice is the caller's. The private key operations in Crypto.PubKey.RSA.PKCS15, .OAEP and .PSS take a Maybe Blinder, and Nothing is no harder to write than the safe form:

decrypt     :: Maybe Blinder -> PrivateKey -> ByteString -> ...
decryptSafer :: MonadRandom m => PrivateKey -> ByteString -> m ...

The exponent itself is not what is at risk. expSafe keeps the value of an exponent out of the work it does, so the private exponent does not leak through the exponentiation. What a blinder covers is the other side: without one, the operation runs on the ciphertext the caller was handed, so how long it takes depends on a number an attacker may have chosen and can vary. That is what a remote timing attack on RSA needs. With a blinder the input is multiplied by a random value first and the result divided out afterwards, so the timing carries nothing an attacker can steer.

decryptSafer and signSafer generate the blinder themselves and are the ones to reach for. Pass Nothing only where the input is not attacker controlled and you have decided that it is not.

The RSA rows in the tables below are the unblinded path. A blinder costs one more exponentiation, by the public exponent, which is the cheap direction: measured on the M4, signing goes from about 601 to about 620 microseconds, three per cent.

Performance

The algorithms a TLS connection uses, measured against the previous release and against OpenSSL on the same machine. Throughput is over 16 KiB messages; the public key operations are one operation each; every figure is the best of several runs, and crypton and OpenSSL are run alternately so that neither gets the quieter machine.

The columns read 2.0.0 and are what 2.1.0 measures on every row but two. The only C 2.1.0 changed is AES-GCM's and P-256's, and of those only P-256 is on a path these tables take: measured on the same EPYC 7763, ECDH P-256 goes 164.3 to 159.5 microseconds and ECDSA P-256 verification 231.1 to 226.2, so those two rows are about 3% better than they read here. ECDSA P-256 signing does not move, being the base-point path. What 2.1.0 did to AES-GCM was add a one-call interface beside the one measured here -- key expanded once, additional data, payload and tag together -- which is worth 3.6 times at 100 bytes and nothing at 16 KiB, so it does not show in a 16 KiB throughput figure at all.

Bulk encryption and hashing are measured through crypton's C layer, as openssl speed measures OpenSSL's. The public key operations are measured through crypton's Haskell API, since that is where ECDSA and RSA live and it is what a program actually calls; the Haskell layer adds well under a microsecond, which the X25519 and ECDH P-256 rows confirm by agreeing with a C-level measurement to within a percent. Both releases of crypton are built the same way -- -optc-O3, which is what both of them ask for -- and each column of a table comes from one run on the machine named above it.

x86-64

An AMD EPYC 7763, which has AES-NI, PCLMULQDQ, AVX2, ADX and the SHA extensions, against OpenSSL 3.0.13.

Throughput in MB/s, higher is better:

crypton 1.1.5 crypton 2.0.0 OpenSSL 2.0.0 / OpenSSL
AES-128-GCM 1331 4118 4264 0.97
AES-256-GCM 1090 3810 3951 0.96
ChaCha20-Poly1305 398 2195 2191 1.00
SHA-1 738 1678 1672 1.00
SHA-256 286 1585 1570 1.01
SHA-512 448 769 746 1.03
SHA3-256 109 421 426 0.99

Time per operation in microseconds, lower is better -- so the last column divides OpenSSL's time by crypton's, and is again better the larger it is:

crypton 1.1.5 crypton 2.0.0 OpenSSL OpenSSL / 2.0.0
X25519 43.57 43.52 36.58 0.84
ECDH P-256 163.8 163.7 52.36 0.32
ECDH P-384 2241 1101 857.1 0.78
Ed25519 sign 28.52 28.25 43.49 1.54
Ed25519 verify 46.06 46.16 119.3 2.59
ECDSA P-256 sign 76.04 75.02 22.91 0.31
ECDSA P-256 verify 229.2 228.5 67.98 0.30
ECDSA P-384 sign 2271 387.6 904.3 2.33
ECDSA P-384 verify 2667 1493 746.2 0.50
RSA-2048 sign/decrypt 759.8 1311 660.1 0.50
RSA-2048 verify/encrypt 31.62 28.59 18.63 0.65

AArch64

An Apple M4, which has the AES, PMULL, SHA-1, SHA-2, SHA-512 and SHA-3 instructions, against OpenSSL 3.6.4.

Throughput in MB/s, higher is better:

crypton 1.1.5 crypton 2.0.0 OpenSSL 2.0.0 / OpenSSL
AES-128-GCM 126 8702 10719 0.81
AES-256-GCM 98 7648 9154 0.84
ChaCha20-Poly1305 758 2323 2244 1.04
SHA-1 1199 3380 3346 1.01
SHA-256 467 3394 3352 1.01
SHA-512 723 1868 1851 1.01
SHA3-256 548 1091 1054 1.04

Time per operation in microseconds, lower is better; the last column again divides OpenSSL's time by crypton's:

crypton 1.1.5 crypton 2.0.0 OpenSSL OpenSSL / 2.0.0
X25519 18.44 18.41 18.41 1.00
ECDH P-256 69.46 56.24 24.68 0.44
ECDH P-384 3252 511.1 379.7 0.74
Ed25519 sign 13.75 13.14 15.90 1.21
Ed25519 verify 18.17 18.04 39.27 2.18
ECDSA P-256 sign 32.56 27.91 11.05 0.40
ECDSA P-256 verify 96.50 80.19 32.82 0.41
ECDSA P-384 sign 3219 169.3 403.4 2.38
ECDSA P-384 verify 3807 688.1 335.1 0.49
RSA-2048 sign/decrypt 451.8 605.4 325.0 0.54
RSA-2048 verify/encrypt 18.32 15.28 8.50 0.56

What the numbers say

1.1.5 had no AArch64 code of its own at all, which is why AES-GCM there is sixty-nine times what it was. On x86-64 it had AES-NI and nothing else. The curves over a prime field other than P-256 moved from Haskell Integer arithmetic into C, which is the nineteenfold change in ECDSA P-384 signing on the M4. X25519 and Ed25519 are unchanged between the two releases, and the rows say so: where they differ by half a percent, that is the measurement and not the code. P-256 is unchanged on x86-64 and a fifth faster on AArch64, which is the paragraph below.

Where crypton is behind, it is behind for three separate reasons.

P-256. crypton's field arithmetic is C where OpenSSL's is hand-written assembly, and that is what is left of the difference: the two differ by about the same factor on every P-256 row, and nothing above the field -- a wider window, a different addition formula, another field representation -- recovers a useful part of it.

The AArch64 rows are better than the x86-64 ones because of where a field multiplication's latency goes. It ends in a carry chain the width of the number, and the curve arithmetic has independent products that could cover that chain -- but only if the compiler inlines the reduction instead of calling it, since a call is a fence. Asking it to costs code and pays where there are registers enough to hold two chains at once: a quarter on AArch64, where there are thirty-one, and nothing on x86-64, where there are fifteen and the same request makes it slower. So x86-64 is left to the compiler's own judgement and stays at 0.3.

RSA signing. 2.0.0 is slower than 1.1.5 here on purpose. Its modular exponentiation no longer indexes a table with the bits of the exponent, and hiding the exponent is what the difference buys. What is left of the gap against OpenSSL is the Montgomery multiplication, which is assembly there and C here.

The AVX-512 instructions. Neither machine above has them. On one that does -- an EPYC 9V74, measured the same way -- OpenSSL uses them for AES-GCM and ChaCha20 and reaches 12003 and 3789 MB/s, against 4745 and 2372 for crypton, whose vendored assembly is generated without them. Those ratios are 0.40 and 0.63 rather than 0.97 and 1.00. Nothing else in either table moves by more than a few percent between the two processors.

One row wants a word of its own: crypton's Ed25519.sign derives the public key from the secret key every time it signs, so that a caller who passes a public key that does not match cannot be made to leak the private one. That costs a second scalar multiplication, which OpenSSL's signing does not pay.

SHA-1 is in the tables because a number of protocols and file formats still ask for it, not because it is a good choice for anything new. The algorithms that nothing should ask for any more -- MD5, 3DES, RC4, CBC mode -- are left out.