Skip to content

Commit

Permalink
Added m51 implementation for GF255. Added primitive-enabling features.
Browse files Browse the repository at this point in the history
  • Loading branch information
Thomas Pornin committed Aug 10, 2023
1 parent 1ee7873 commit fdc8cba
Show file tree
Hide file tree
Showing 30 changed files with 5,729 additions and 124 deletions.
53 changes: 48 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "crrl"
version = "0.5.0"
version = "0.6.0"
authors = ["Thomas Pornin <[email protected]>"]
edition = "2018"
license = "MIT"
Expand Down Expand Up @@ -34,73 +34,116 @@ num-bigint = "0.4.3"
# Default feature 'std' enables uses of heap allocation, which is used by
# some functions. By disabling it, a core-only library can be obtained.
[features]
default = ["std"]
std = ["alloc"]
default = [ "std", "omnes" ]
std = [ "alloc" ]
alloc = []
w32_backend = []
w64_backend = []
gf255_m51 = []
gf255_m64 = []
omnes = [ "decaf448", "ed25519", "ed448", "frost", "jq255e", "jq255s", "lms", "p256", "ristretto255", "secp256k1", "x25519", "x448", "modint256", "gf255", "gfgen" ]
decaf448 = [ "ed448" ]
ed25519 = [ "gf25519", "modint256" ]
ed448 = [ "gf448", "gfgen" ]
frost = [ "alloc" ]
jq255e = [ "gf255e", "modint256" ]
jq255s = [ "gf255s", "modint256" ]
lms = []
p256 = [ "gfp256", "modint256" ]
ristretto255 = [ "ed25519" ]
secp256k1 = [ "gfsecp256k1", "modint256" ]
x25519 = [ "ed25519" ]
x448 = [ "ed448" ]
gfgen = []
gf255 = []
gf255e = []
gf255s = []
gf25519 = []
gfp256 = []
gfsecp256k1 = []
gf448 = []
modint256 = []

[[bench]]
name = "modint"
path = "benches/modint.rs"
harness = false
required-features = [ "modint256" ]

[[bench]]
name = "gf255"
path = "benches/gf255.rs"
name = "gf255e"
path = "benches/gf255e.rs"
harness = false
required-features = [ "gf255e" ]

[[bench]]
name = "gf25519"
path = "benches/gf25519.rs"
harness = false
required-features = [ "gf25519" ]

[[bench]]
name = "ed25519"
path = "benches/ed25519.rs"
harness = false
required-features = [ "ed25519" ]

[[bench]]
name = "x25519"
path = "benches/x25519.rs"
harness = false
required-features = [ "x25519" ]

[[bench]]
name = "p256"
path = "benches/p256.rs"
harness = false
required-features = [ "p256" ]

[[bench]]
name = "ristretto255"
path = "benches/ristretto255.rs"
harness = false
required-features = [ "ristretto255" ]

[[bench]]
name = "jq255e"
path = "benches/jq255e.rs"
harness = false
required-features = [ "jq255e" ]

[[bench]]
name = "jq255s"
path = "benches/jq255s.rs"
harness = false
required-features = [ "jq255s" ]

[[bench]]
name = "secp256k1"
path = "benches/secp256k1.rs"
harness = false
required-features = [ "secp256k1" ]

[[bench]]
name = "gf448"
path = "benches/gf448.rs"
harness = false
required-features = [ "gf448" ]

[[bench]]
name = "sc448"
path = "benches/sc448.rs"
harness = false
required-features = [ "ed448" ]

[[bench]]
name = "ed448"
path = "benches/ed448.rs"
harness = false
required-features = [ "ed448" ]

[[bench]]
name = "x448"
path = "benches/x448.rs"
harness = false
required-features = [ "x448" ]
104 changes: 86 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,30 +70,34 @@ are implemented:
leveraged to speed-up point multiplication (key exchange) and
signature verification.

- Types `jq255e::Point` and `jq255s::Point` implement the [double-odd
curves](https://doubleodd.group/) jq255e and jq255s (along with the
corresponding scalar types `jq255e::Scalar` and `jq255s::Scalar`).
Key exchange and Schnorr signatures are implemented. These curves
provide a prime-order group abstraction, similar to ristretto255,
but with somewhat better performance at the same security level.
Moreover, the relevant signatures are both shorter (48 bytes instead
of 64) and faster than the usual Ed25519 signatures.

- Function `x25519::x25519()` implements the [X25519 function](https://datatracker.ietf.org/doc/html/rfc7748#section-5).
- Types `jq255e::Point` and `jq255s::Point` implement the
[double-odd curves](https://doubleodd.group/) jq255e and jq255s
(along with the corresponding scalar types `jq255e::Scalar` and
`jq255s::Scalar`). Key exchange and Schnorr signatures are
implemented. These curves provide a prime-order group abstraction,
similar to ristretto255, but with somewhat better performance at the
same security level. Moreover, the relevant signatures are both
shorter (48 bytes instead of 64) and faster than the usual Ed25519
signatures.

- Function `x25519::x25519()` implements the
[X25519 function](https://datatracker.ietf.org/doc/html/rfc7748#section-5).
An optimized `x25519::x2559_base()` function is provided when X25519
is applied to the conventional base point. Similarly, `x448::x448()`
and `x448::x448_base()` provide the same functionality for the
X448 function.

Types `GF255` and `ModInt256` have a 32-bit and a 64-bit implementations
each. The code is portable (it was tested on 32-bit and 64-bit x86, and
64-bit aarch64). 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:
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;
Expand Down Expand Up @@ -129,6 +133,70 @@ which is quite close to the corresponding description in RFC 7748:
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:

- `omnes`: enables all of the following.

- `decaf448`: decaf448 prime-order group (based on edwards448)

- `ed25519`: edwards25519 curve and signatures (RFC 8032: Ed25519)

- `ed448`: edwards448 curve and signatures (RFC 8032: Ed448)

- `frost`: FROST threshold signatures (support macros + standard
ciphersuites, but only for the curves which are also enabled in
this build)

- `jq255e`: jq255e prime-order group and signatures

- `jq255s`: jq255s prime-order group and signatures

- `lms`: LMS support (hash-based signatures)

- `p256`: NIST P-256 curve and signatures (ECDSA)

- `ristretto255`: ristretto255 prime-order group (based on edwards25519)

- `secp256k1`: secp256k1 curve and signatures (ECDSA)

- `x25519`: X25519 key exchange primitive (RFC 7748)

- `x448`: X448 key exchange primitive (RFC 7748)

- `modint256`: generic finite field implementation (prime order of up to
256 bits)

- `gf255`: generic finite field implementation (for prime order
`q = 2^255 - MQ` with `MQ < 2^15`)

- `gfgen`: generic finite field implementation (generating macro; prime
modulus of arbitrary length)

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

- `w32_backend`: enforce use of the 32-bit code, even on 64-bit systems.

- `w64_backend`: enforce use of the 64-bit code, even on 32-bit systems.

- `gf255_m64`: enforce use of 64-bit limbs for `GF255<MQ>`; this is the
default on 64-bit machines, except RISC-V (riscv64) where 51-bit
limbs are used by default. This feature has no effect if the 32-bit code
is used.

- `gf255_m51`: encode use of 51-bit limbs for `GF255<MQ>`; this is the
default on 64-bit RISC-V targets (riscv64), but not on other 64-bit
architectures where 64-bit limbs are normally preferred. This feature
has no effect if the 32-bit code is used.

# Security and Compliance

All the code is strict, both in terms of timing-based side-channels
Expand Down
1 change: 1 addition & 0 deletions benches/ed25519.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![allow(non_snake_case)]
#![cfg(feature = "ed25519")]

mod util;
use util::core_cycles;
Expand Down
1 change: 1 addition & 0 deletions benches/ed448.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![allow(non_snake_case)]
#![cfg(feature = "ed448")]

mod util;
use util::core_cycles;
Expand Down
Loading

0 comments on commit fdc8cba

Please sign in to comment.