We currently have ciphers optimized for performance, for
compatibility, for size and for specific CPUs.
However we lack a class of ciphers that is becoming increasingly
important, as Zig is being used for embedded systems, but also as
hardware-level side channels keep being found on (Intel) CPUs.
Here is ISAPv2, a construction specifically designed for resilience
against leakage and fault attacks.
ISAPv2 is obviously not optimized for performance, but can be an
option for highly sensitive data, when the runtime environment cannot
be trusted.
With the simple rule that whenever we have or will have 2 similar
functions, they should be in their own namespace.
Some of these new namespaces currently contain a single function.
This is to prepare for reduced-round versions that are likely to
be added later.
Leverage result location semantics for X25519 like we do everywhere
else in 25519/*
Also add the edwards25519->curve25519 map by the way since many
applications seem to use this to share the same key pair for encryption
and signature.
The NaCl constructions are available in pretty much all programming
languages, making them a solid choice for applications that require
interoperability.
Go includes them in the standard library, JavaScript has the popular
tweetnacl.js module, and reimplementations and ports of TweetNaCl
have been made everywhere.
Zig has almost everything that NaCl has at this point, the main
missing component being the Salsa20 cipher, on top on which NaCl's
secretboxes, boxes, and sealedboxes can be implemented.
So, here they are!
And clean the X25519 API up a little bit by the way.
- use `PascalCase` for all types. So, AES256GCM is now Aes256Gcm.
- consistently use `_length` instead of mixing `_size` and `_length` for the
constants we expose
- Use `minimum_key_length` when it represents an actual minimum length.
Otherwise, use `key_length`.
- Require output buffers (for ciphertexts, macs, hashes) to be of the right
size, not at least of that size in some functions, and the exact size elsewhere.
- Use a `_bits` suffix instead of `_length` when a size is represented as a
number of bits to avoid confusion.
- Functions returning a constant-sized slice are now defined as a slice instead
of a pointer + a runtime assertion. This is the case for most hash functions.
- Use `camelCase` for all functions instead of `snake_case`.
No functional changes, but these are breaking API changes.
Showcase that Zig can be a great option for high performance cryptography.
The AEGIS family of authenticated encryption algorithms was selected for
high-performance applications in the final portfolio of the CAESAR
competition.
They reuse the AES core function, but are substantially faster than the
CCM, GCM and OCB modes while offering a high level of security.
AEGIS algorithms are especially fast on CPUs with built-in AES support, and
the 128L variant fully takes advantage of the pipeline in modern Intel CPUs.
Performance of the Zig implementation is on par with libsodium.
Before:
gimli-hash: 120 MiB/s
gimli-aead: 130 MiB/s
After:
gimli-hash: 195 MiB/s
gimli-aead: 208 MiB/s
Also fixes in-place decryption by the way.
If the input & output buffers were the same, decryption used to fail.
Return on decryption error in the benchmark to detect similar issues
in future AEADs even in non release-fast mode.
* Reorganize crypto/aes in order to separate parameters, implementations and
modes.
* Add a zero-cost abstraction over the internal representation of a block,
so that blocks can be kept in vector registers in optimized implementations.
* Add architecture-independent aesenc/aesdec/aesenclast/aesdeclast operations,
so that any AES-based primitive can be implemented, including these that don't
use the original key schedule (AES-PRF, AEGIS, MeowHash...)
* Add support for parallelization/wide blocks to take advantage of hardware
implementations.
* Align T-tables to cache lines in the software implementations to slightly
reduce side channels.
* Add an optimized implementation for modern Intel CPUs with AES-NI.
* Add new tests (AES256 key expansion).
* Reimplement the counter mode to work with any block cipher, any endianness
and to take advantage of wide blocks.
* Add benchmarks for AES.
- 1MiB objects on the stack doesn't play well with wasmtime.
Reduce these to 512KiB so that the webassembly benchmarks can run.
- Pass expected results to a blackBox() function. Without this, in
release-fast mode, the compiler could detected unused return values,
and would produce results that didn't make sense for siphash.
- Add AEAD constructions to the benchmarks.
- Inline chacha20Core() makes it 4 times faster.
- benchmarkSignatures() -> benchmarkSignature() for consistency.
SipHash *is* a cryptographic function, with a 128-bit security level.
However, it is not a regular hash function: a secret key is required,
and knowledge of that key allows collisions to be quickly computed offline.
SipHash is therefore more suitable to be used as a MAC.
The same API as other MACs was implemented in addition to functions directly
returning an integer.
The benchmarks have been updated accordingly.
No changes to the SipHash implementation itself.
- This avoids having multiple `init()` functions for every combination
of optional parameters
- The API is consistent across all hash functions
- New options can be added later without breaking existing applications.
For example, this is going to come in handy if we implement parallelization
for BLAKE2 and BLAKE3.
- We don't have a mix of snake_case and camelCase functions any more, at
least in the public crypto API
Support for BLAKE2 salt and personalization (more commonly called context)
parameters have been implemented by the way to illustrate this.
Instead of having all primitives and constructions share the same namespace,
they are now organized by category and function family.
Types within the same category are expected to share the exact same API.
This is a translation of the [official reference implementation][1] with
few other changes. The bad news is that the reference implementation is
designed for simplicity and not speed, so there's a lot of room for
performance improvement. The good news is that, according to the crypto
benchmark, the implementation is still fast relative to the other
hashing algorithms:
```
md5: 430 MiB/s
sha1: 386 MiB/s
sha256: 191 MiB/s
sha512: 275 MiB/s
sha3-256: 233 MiB/s
sha3-512: 137 MiB/s
blake2s: 464 MiB/s
blake2b: 526 MiB/s
blake3: 576 MiB/s
poly1305: 1479 MiB/s
hmac-md5: 653 MiB/s
hmac-sha1: 553 MiB/s
hmac-sha256: 222 MiB/s
x25519: 8685 exchanges/s
```
[1]: https://github.com/BLAKE3-team/BLAKE3
Thanks to the Windows Process Environment Block, it is possible to
obtain handles to the standard input, output, and error streams without
possibility of failure.