Home

Awesome

crrl

This library implements some primitives for purposes of cryptographic research. Its point is to provide efficient, optimized and constant-time implementations that are supposed to be representative of production-ready code, so that realistic performance benchmarks may be performed. Thus, while meant primarily for research, the code here should be fine for production use (though of course I offer no such guarantee; use at your own risks).

So far, only some primitives related to elliptic curve cryptography are implemented:

Types GF255 and ModInt256 have a 32-bit and a 64-bit implementations each (actually two 64-bit implementations, see later the discussion about the gf255_m51 feature). The code is portable (it was tested on 32-bit and 64-bit x86, 64-bit aarch64, and 64-bit riscv64). Performance is quite decent; e.g. Ed25519 signatures are computed in about 51500 cycles, and verified in about 114000 cycles, on an Intel "Coffee Lake" CPU; this is not too far from the best assembly-optimized implementations. At the same time, use of operator overloading allows to express formulas on points and scalar with about the same syntax as their mathematical description. For instance, the core of the X25519 implementation looks like this:

        let A = x2 + z2;
        let B = x2 - z2;
        let AA = A.square();
        let BB = B.square();
        let C = x3 + z3;
        let D = x3 - z3;
        let E = AA - BB;
        let DA = D * A;
        let CB = C * B;
        x3 = (DA + CB).square();
        z3 = x1 * (DA - CB).square();
        x2 = AA * BB;
        z2 = E * (AA + E.mul_small(121665));

which is quite close to the corresponding description in RFC 7748:

       A = x_2 + z_2
       AA = A^2
       B = x_2 - z_2
       BB = B^2
       E = AA - BB
       C = x_3 + z_3
       D = x_3 - z_3
       DA = D * A
       CB = C * B
       x_3 = (DA + CB)^2
       z_3 = x_1 * (DA - CB)^2
       x_2 = AA * BB
       z_2 = E * (AA + a24 * E)

Optional Features

By default, everything in crrl is compiled, which unfortunately makes for a relatively long compilation time, especially on not-so-fast systems. To only compile support for some primitives, use --no-default-features then add selectively the features you are interested in with -F; e.g. use cargo build --no-default-features -F ed25519 to only compile the Ed25519 support (and the primitives that it needs, such as its base field). The defined primitive-controlling features are the following:

Some operations have multiple backends. An appropriate backend is selected at compile-time, but this can be overridden by enabling some features:

Security and Compliance

All the code is strict, both in terms of timing-based side-channels (everything is constant-time, except if explicitly stated otherwise, e.g. in a function whose name includes vartime) and in compliance to relevant standards. For instance, the Ed25519 signature support applies and enforces canonical encodings of both points and scalars.

There is no attempt at "zeroizing memory" anywhere in the code. In general, such memory cleansing is a fool's quest. Note that since most of the library use no_std rules, dynamic allocation happens only on the stack, thereby limiting the risk of leaving secret information lingering all over the RAM. The only functions that use heap allocation only store public data there.

WARNING: I reiterate what was written above: although all of the code aims at being representative of optimized production-ready code, it is still fairly recent and some bugs might still lurk, however careful I am when writing code. Any assertion of suitability to any purpose is explcitly denied. The primary purpose is to help with "trying out stuff" in cryptographic research, by offering an easy-to-use API backed by performance close enough to what can be done in actual applications.

Truncated Signatures

Support for truncated signatures is implemented for Ed25519 and ECDSA/P-256. Standard signatures can be shortened by 8 to 32 bits (i.e. the size may shrink from 64 down to 60 bytes), and the verifier rebuilds the original signature during verification (at some computational cost). This is not a ground-breaking feature, but it can be very convenient in some situations with tight constraints on bandwidth and a requirement to work with standard signature formats. See ed25519::PublicKey::verify_trunc_raw() and p256::PublicKey::verify_trunc_hash() for details.

FROST Threshold Schnorr Signatures

The FROST protocol for a distributed Schnorr signature generation scheme has been implemented, as per the v14 draft specification: draft-irtf-cfrg-frost-14. Four ciphersuites are provided, with similar APIs, in the frost::ed25519, frost::ristretto255, frost::ed448, frost::p256 and frost::secp256k1 modules. A sample code showing how to use the API is provided in the frost-sample.rs file.

While FROST is inherently a distributed scheme, the implementation can also be used in a single signer mode by using the "group" private key directly.

Benchmarks

cargo bench runs some benchmarks, but there are a few caveats:

TODO

In general, about anything related to cryptography may show up here, if there is a use case for it.