From 98371cbcc8d2560ae15fb83f851871fc5db8416e Mon Sep 17 00:00:00 2001 From: Robin Winzler Date: Wed, 24 Nov 2021 14:57:01 +0100 Subject: [PATCH] Refactor pqcrypto-templates build.rs.j2 and ffi.rs.j2 --- CHANGELOG.md | 33 +- README.md | 4 +- generate-implementations.py | 16 +- implementations.yaml | 344 ++++++---------- pqcrypto-internals/Cargo.toml | 2 +- pqcrypto-template/scheme/Cargo.toml.j2 | 11 +- pqcrypto-template/scheme/README.md.j2 | 20 +- pqcrypto-template/scheme/build.rs.j2 | 117 ++++-- pqcrypto-template/scheme/src/ffi.rs.j2 | 467 ++++++++-------------- pqcrypto-template/scheme/src/lib.rs.j2 | 2 +- pqcrypto-template/scheme/src/scheme.rs.j2 | 113 +++++- pqcrypto/Cargo.toml | 22 +- 12 files changed, 515 insertions(+), 636 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65fa5572..a4396fb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 2021-11-24 + +* Add a general implementation list for each scheme in implementations.yaml which is used by build.rs.j2 +* Each scheme now has a list of supported implementation variants +* Refactor build.rs.js2 to use macro calls +* Update the other template files to adapt to this change +* Slight modifications to README.md + +## 2021-10-26 + +* Make `pqcrypto-internals` cross-compilable + ## 2021-10-18 * Fix small issue in randombytes implementation: should return 0 @@ -9,9 +21,9 @@ * `no_std` support thanks to @rozbb (PR#25) * Extract randombytes from PQClean-provided APIs (avoids symbol conflict) (PR #24) * Update PQClean: - * NTRUPrime new parametersets - * Small Falcon fixes - * Small NTRU fix + * NTRUPrime new parametersets + * Small Falcon fixes + * Small NTRU fix ## 2021-07-28 @@ -20,6 +32,10 @@ * NTRU Prime updates * Move common files into `pqcrypto-internals` and out of individual libs +## 2021-06-28 + +* Refactor the wrapper methods in scheme.rs.js2 file to macro calls + ## 2021-06-10 * Add optional `serde` support @@ -120,10 +136,10 @@ * Update FALCON from PQClean * Update SPHINCS+ from PQClean * Package LEDAcryptKEM - * **Warning:** The LEDAcryptKEM implementations currently packaged are known to have timing side-channel vulnerabilities. + * **Warning:** The LEDAcryptKEM implementations currently packaged are known to have timing side-channel vulnerabilities. * Package Rainbow - * The ``clean`` implementations are currently known to have undefined behaviour. - See https://github.com/PQClean/PQClean/issues/220 + * The ``clean`` implementations are currently known to have undefined behaviour. + See [pqclean/issues/220](https://github.com/PQClean/PQClean/issues/220) * Hide a internal enum variable from ``pqcrypto_traits::sign::VerificationError`` ## 2019-07-24 @@ -138,23 +154,28 @@ * Update `rand` crate to `0.7.0` ## 2019-07-18 + * Update PQClean implementations * SPHINCS+ is now thread-safe. * Frodo now uses ``opt`` implementation by default. * Allow for multiple implementations in the ``ffi`` interface. ## 2019-07-09 + * Make ``encapsulate`` and ``decapsulate`` take references. * Add Dilithium * Add SABER ## 2019-07-08 + * Remove ``pqcrypto-internals`` ## 2019-05-22 + * Added ``pqcrypto_traits::{Error,Result}`` to ``from_bytes`` signature. * Added ``pqcrypto::prelude`` to allow importing all traits in one easy go. * Removed all uses of ``mem::uninitialized()`` ## 2019-05-21 + * Added MQDSS diff --git a/README.md b/README.md index 20b25252..0acffda6 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,15 @@ [![dependency status](https://deps.rs/repo/github/rustpq/pqcrypto/status.svg)](https://deps.rs/repo/github/rustpq/pqcrypto) - This repository contains bindings to C implementations of cryptographic algorithms part of the [NIST competition][nist]. These bindings are generated based on the [PQClean][pqclean] project, which aims to collect 'clean' implementations of cryptographic algorithms. -## How this project works. +## How to generate the bindings The `pqcrypto-templates` folder contains the master copies of the Rust files. The binding libraries are generated from the PQClean meta files and PQClean specified API. The file `implementations.yaml` controls the version numbers and included variants of each scheme. +The generation of the different pq-crates is done by the `generate-implementation.py` script. ## Documentation diff --git a/generate-implementations.py b/generate-implementations.py index 636bb42f..98c317a8 100755 --- a/generate-implementations.py +++ b/generate-implementations.py @@ -9,6 +9,7 @@ DEFAULT_AVX2_GUARD = 'avx2_enabled && target_arch == "x86_64"' +DEFAULT_AES_GUARD = 'aes_enabled && target_arch == "x86_64"' def read_yaml(): @@ -64,13 +65,6 @@ def generate_scheme(name, type, properties): except FileExistsError: pass - has_avx2 = False - for scheme in properties['schemes']: - if 'avx2_implementation' in scheme: - has_avx2 = True - if 'avx2_feature' not in scheme: - scheme['avx2_feature'] = 'avx2' - render_template( target_dir, 'Cargo.toml', 'scheme/Cargo.toml.j2', traits_version=implementations['traits_version'], @@ -79,15 +73,17 @@ def generate_scheme(name, type, properties): type=type, insecure=properties.get('insecure', False), version=properties['version'], - has_avx2=has_avx2, + implementations=properties['implementations'], ) render_template( target_dir, 'build.rs', 'scheme/build.rs.j2', name=name, type=type, + implementations=properties['implementations'], schemes=properties['schemes'], - avx2_guard=properties.get('avx2_guard', DEFAULT_AVX2_GUARD) + avx2_guard=properties.get('avx2_guard', DEFAULT_AVX2_GUARD), + aes_guard=properties.get('aes_guard', DEFAULT_AES_GUARD), ) metadatas = dict() @@ -100,7 +96,7 @@ def generate_scheme(name, type, properties): type=type, name=name, metadatas=metadatas, - schemes=properties['schemes'] + schemes=properties['schemes'], ) for scheme in properties['schemes']: diff --git a/implementations.yaml b/implementations.yaml index f6cb8e4f..0ac95868 100644 --- a/implementations.yaml +++ b/implementations.yaml @@ -5,375 +5,263 @@ traits_version: 0.3.4 kems: kyber: - version: 0.7.3 + version: 0.7.4 avx2_guard: 'avx2_enabled && !is_windows && !is_macos && target_arch == "x86_64"' + implementations: [clean, avx2] schemes: - name: kyber512 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: kyber768 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: kyber1024 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: kyber512-90s - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: kyber768-90s - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: kyber1024-90s - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] frodo: - version: 0.4.9 + version: 0.4.10 notes: | If you use it via the FFI interface: The ``clean`` implementation of Frodo needs a lot of stack space, specify env variable `RUST_MIN_STACK` to make sure it has enough stack space in threads. This is not relevant for the 'normal' api methods. + implementations: [opt, clean] schemes: - name: frodokem640aes - implementation: opt - alt_implementations: - - clean + implementations: [opt, clean] - name: frodokem640shake - implementation: opt - alt_implementations: - - clean + implementations: [opt, clean] - name: frodokem976aes - implementation: opt - alt_implementations: - - clean + implementations: [opt, clean] - name: frodokem976shake - implementation: opt - alt_implementations: - - clean + implementations: [opt, clean] - name: frodokem1344aes - implementation: opt - alt_implementations: - - clean + implementations: [opt, clean] - name: frodokem1344shake - implementation: opt - alt_implementations: - - clean + implementations: [opt, clean] ntru: - version: 0.5.6 + version: 0.5.7 + implementations: [clean, avx2] schemes: - name: ntruhps2048509 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: ntruhps2048677 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: ntruhps4096821 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: ntruhrss701 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] ntruprime: - version: 0.1.4 + version: 0.1.5 + implementations: [clean, avx2] schemes: - name: ntrulpr653 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: ntrulpr761 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: ntrulpr857 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: ntrulpr953 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: ntrulpr1013 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: ntrulpr1277 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sntrup653 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sntrup761 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sntrup857 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sntrup953 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sntrup1013 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sntrup1277 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] saber: - version: 0.1.9 + version: 0.1.10 + implementations: [clean, avx2] schemes: - name: firesaber - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: lightsaber - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: saber - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] classicmceliece: - version: 0.1.5 + version: 0.1.6 notes: | This implementation requires a lot of stack space. You need to specify ``RUST_MIN_STACK=800000000``, probably. avx2_guard: 'avx2_enabled && !is_windows && target_arch == "x86_64"' + implementations: [vec, clean, avx] schemes: - name: mceliece348864 - implementation: vec - avx2_implementation: avx - alt_implementations: - - clean + implementations: [vec, clean, avx] - name: mceliece348864f - implementation: vec - avx2_implementation: avx - alt_implementations: - - clean + implementations: [vec, clean, avx] - name: mceliece460896 - implementation: vec - avx2_implementation: avx - alt_implementations: - - clean + implementations: [vec, clean, avx] - name: mceliece460896f - implementation: vec - avx2_implementation: avx - alt_implementations: - - clean + implementations: [vec, clean, avx] - name: mceliece6688128 + implementations: [vec, clean, avx] doctest: no - implementation: vec - avx2_implementation: avx - alt_implementations: - - clean - name: mceliece6688128f + implementations: [vec, clean, avx] doctest: no - implementation: vec - avx2_implementation: avx - alt_implementations: - - clean - name: mceliece6960119 + implementations: [vec, clean, avx] doctest: no - implementation: vec - avx2_implementation: avx - alt_implementations: - - clean - name: mceliece6960119f + implementations: [vec, clean, avx] doctest: no - implementation: vec - avx2_implementation: avx - alt_implementations: - - clean - name: mceliece8192128 + implementations: [vec, clean, avx] doctest: no - implementation: vec - avx2_implementation: avx - alt_implementations: - - clean - name: mceliece8192128f + implementations: [vec, clean, avx] doctest: no - implementation: vec - avx2_implementation: avx - alt_implementations: - - clean hqc: - version: 0.1.3 + version: 0.1.4 + implementations: [clean] schemes: - name: hqc-rmrs-128 - implementation: clean - #avx2_implementation: avx2 + implementations: [clean] # avx2 is excluded - name: hqc-rmrs-192 - implementation: clean - #avx2_implementation: avx2 + implementations: [clean] # avx2 is excluded - name: hqc-rmrs-256 - implementation: clean - #avx2_implementation: avx2 + implementations: [clean] # avx2 is excluded signs: dilithium: - version: 0.4.3 + version: 0.4.4 avx2_guard: 'avx2_enabled && !is_windows && target_arch == "x86_64"' + implementations: [clean, avx2] schemes: - name: dilithium2 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: dilithium3 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: dilithium5 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] falcon: - version: 0.2.9 + version: 0.2.10 + implementations: [clean, avx2] schemes: - name: falcon-512 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: falcon-1024 - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] rainbow: - version: 0.2.3 + version: 0.2.4 notes: | This implementation requires a lot of stack space. You need to specify ``RUST_MIN_STACK=800000000``, probably. + implementations: [clean] schemes: - name: rainbowI-circumzenithal - implementation: clean + implementations: [clean] - name: rainbowI-classic - implementation: clean + implementations: [clean] - name: rainbowI-compressed - implementation: clean + implementations: [clean] - name: rainbowIII-circumzenithal - implementation: clean + implementations: [clean] - name: rainbowIII-classic - implementation: clean + implementations: [clean] - name: rainbowIII-compressed - implementation: clean + implementations: [clean] - name: rainbowV-circumzenithal + implementations: [clean] doctest: no # Doc test fail because of too small stack size - implementation: clean - name: rainbowV-classic + implementations: [clean] doctest: no # Doc test fail because of too small stack size - implementation: clean - name: rainbowV-compressed + implementations: [clean] doctest: no # Doc test fail because of too small stack size - implementation: clean sphincsplus: - version: 0.6.2 + version: 0.6.3 + implementations: [clean, aesni, avx2] schemes: - name: sphincs-haraka-128f-robust - implementation: clean - avx2_implementation: aesni - avx2_feature: 'aes' + implementations: [clean, aesni] - name: sphincs-haraka-128f-simple - avx2_implementation: aesni - avx2_feature: 'aes' - implementation: clean + implementations: [clean, aesni] - name: sphincs-haraka-128s-robust - implementation: clean - avx2_implementation: aesni - avx2_feature: 'aes' + implementations: [clean, aesni] - name: sphincs-haraka-128s-simple - implementation: clean - avx2_implementation: aesni - avx2_feature: 'aes' + implementations: [clean, aesni] - name: sphincs-haraka-192f-robust - implementation: clean - avx2_implementation: aesni - avx2_feature: 'aes' + implementations: [clean, aesni] - name: sphincs-haraka-192f-simple - implementation: clean - avx2_implementation: aesni - avx2_feature: 'aes' + implementations: [clean, aesni] - name: sphincs-haraka-192s-robust - implementation: clean - avx2_implementation: aesni - avx2_feature: 'aes' + implementations: [clean, aesni] - name: sphincs-haraka-192s-simple - implementation: clean - avx2_implementation: aesni - avx2_feature: 'aes' + implementations: [clean, aesni] - name: sphincs-haraka-256f-robust - implementation: clean - avx2_implementation: aesni - avx2_feature: 'aes' + implementations: [clean, aesni] - name: sphincs-haraka-256f-simple - implementation: clean - avx2_implementation: aesni - avx2_feature: 'aes' + implementations: [clean, aesni] - name: sphincs-haraka-256s-robust - implementation: clean - avx2_implementation: aesni - avx2_feature: 'aes' + implementations: [clean, aesni] - name: sphincs-haraka-256s-simple - implementation: clean - avx2_implementation: aesni - avx2_feature: 'aes' + implementations: [clean, aesni] - name: sphincs-shake256-128f-robust - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-shake256-128f-simple - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-shake256-128s-robust - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-shake256-128s-simple - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-shake256-192f-robust - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-shake256-192f-simple - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-shake256-192s-robust - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-shake256-192s-simple - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-shake256-256f-robust - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-shake256-256f-simple - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-shake256-256s-robust - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-shake256-256s-simple - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-sha256-128f-robust - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-sha256-128f-simple - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-sha256-128s-robust - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-sha256-128s-simple - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-sha256-192f-robust - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-sha256-192f-simple - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-sha256-192s-robust - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-sha256-192s-simple - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-sha256-256f-robust - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-sha256-256f-simple - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-sha256-256s-robust - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] - name: sphincs-sha256-256s-simple - implementation: clean - avx2_implementation: avx2 + implementations: [clean, avx2] # vim: set ft=yaml ts=2 sw=2 tw=0 et : diff --git a/pqcrypto-internals/Cargo.toml b/pqcrypto-internals/Cargo.toml index c4b840cb..d57b08a6 100644 --- a/pqcrypto-internals/Cargo.toml +++ b/pqcrypto-internals/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pqcrypto-internals" -version = "0.2.2" +version = "0.2.3" edition = "2018" description = "bindings to common cryptography" license = "MIT OR Apache-2.0" diff --git a/pqcrypto-template/scheme/Cargo.toml.j2 b/pqcrypto-template/scheme/Cargo.toml.j2 index 8b7ca18a..ca12cbbe 100644 --- a/pqcrypto-template/scheme/Cargo.toml.j2 +++ b/pqcrypto-template/scheme/Cargo.toml.j2 @@ -13,17 +13,18 @@ categories = ["cryptography", "no-std"] [dependencies] pqcrypto-internals = { path = "../pqcrypto-internals", version = "0.2" } -pqcrypto-traits = {path = "../pqcrypto-traits", version = "{{ traits_version }}", default-features = false} +pqcrypto-traits = { path = "../pqcrypto-traits", version = "{{ traits_version }}", default-features = false } libc = "0.2.0" serde = { version = "1.0", features = ["derive"], optional = true } serde-big-array = { version = "0.3.2", features = ["const-generics"], optional = true } [features] -{% if has_avx2 %} -default = ["avx2", "std"] +default = [{% if 'avx2' in implementations or 'avx' in implementations %}"avx2", {% endif %}{% if 'aesni' in implementations %}"aes", {% endif %}"std"] +{% if 'avx2' in implementations or 'avx' in implementations %} avx2 = ["std"] -{% else %} -default = ["std"] +{% endif %} +{% if 'aesni' in implementations %} +aes = ["std"] {% endif %} std = ["pqcrypto-traits/std"] serialization = ["serde", "serde-big-array"] diff --git a/pqcrypto-template/scheme/README.md.j2 b/pqcrypto-template/scheme/README.md.j2 index 6080c285..6e6273ca 100644 --- a/pqcrypto-template/scheme/README.md.j2 +++ b/pqcrypto-template/scheme/README.md.j2 @@ -25,16 +25,20 @@ sourced from [PQClean][pqclean]. The "default" implementation is used in the Rust-friendly interface, alternative implementations are exposed as ``ffi`` methods only. -{% for scheme in schemes %} * ``{{ scheme.name }}`` -{% if 'avx2_implementation' in scheme %} - * ``{{ scheme.avx2_implementation }}`` (if supported) +{% for scheme in schemes %} +* ``{{ scheme.name }}`` +{% for implementation in scheme.implementations %} +{% if implementation == 'avx2' or implementation == 'avx' or implementation == 'aesni' %} + * ``{{ implementation }}`` (if supported) +{% else %} + * ``{{ implementation }}`` {% endif %} - * ``{{ scheme.implementation }}`` (default) -{% for implementation in scheme.alt_implementations|default([]) %} - * ``{{ implementation }}`` (included as ``ffi`` only) -{% endfor %}{% endfor %} +{% endfor %}{# implementations #} +{% endfor %}{# schemes #} + +{% if notes %} +## Notes -{% if notes %}# Notes {{ notes }} {% endif %} diff --git a/pqcrypto-template/scheme/build.rs.j2 b/pqcrypto-template/scheme/build.rs.j2 index 33a5ee71..0db7fec3 100644 --- a/pqcrypto-template/scheme/build.rs.j2 +++ b/pqcrypto-template/scheme/build.rs.j2 @@ -4,15 +4,69 @@ extern crate glob; use std::env; use std::path::{Path, PathBuf}; -{% set globals = namespace(have_avx2=False) %} +{% set globals = namespace() %} +{% set globals.have_avx2 = False %} +{% set globals.have_aes = False %} -fn main() { - let internals_include_path = &std::env::var("DEP_PQCRYPTO_INTERNALS_INCLUDEPATH").unwrap(); - let common_dir = Path::new("pqclean/common"); +{% for implementation in implementations %} +macro_rules! build_{{ implementation }} { + ($variant:expr) => { + let internals_include_path = &std::env::var("DEP_PQCRYPTO_INTERNALS_INCLUDEPATH").unwrap(); + let common_dir = Path::new("pqclean/common"); + + let mut builder = cc::Build::new(); + let target_dir: PathBuf = ["pqclean", "crypto_{{ type }}", $variant, "{{ implementation }}"] + .iter() + .collect(); + {% if implementation == 'avx2' or implementation == 'avx' %} + {% set globals.have_avx2 = True %} + let scheme_files = glob::glob(target_dir.join("*.[csS]").to_str().unwrap()).unwrap(); + if cfg!(target_env = "msvc") { + builder.flag("/arch:AVX2"); + } else { + builder + .flag("-mavx2") + .flag("-mbmi2") + .flag("-mbmi") + .flag("-maes") + .flag("-mpopcnt") + .flag("-mpclmul"); + } + {% elif implementation == 'aesni' %} + {% set globals.have_aes = True %} + let scheme_files = glob::glob(target_dir.join("*.[csS]").to_str().unwrap()).unwrap(); + if cfg!(target_env = "msvc") { + builder.flag("/arch:AVX2"); + } else { + builder + .flag("-maes"); + } + {% else %} + let scheme_files = glob::glob(target_dir.join("*.c").to_str().unwrap()).unwrap(); + {% endif %} + + builder + .include(internals_include_path) + .include(&common_dir) + .include(target_dir) + .files( + scheme_files + .into_iter() + .map(|p| p.unwrap().to_string_lossy().into_owned()), + ); + builder.compile(format!("{}_{{ implementation }}", $variant).as_str()); + }; +} + +{% endfor %} + +fn main() { #[allow(unused_variables)] let avx2_enabled = env::var("CARGO_FEATURE_AVX2").is_ok(); #[allow(unused_variables)] + let aes_enabled = env::var("CARGO_FEATURE_AES").is_ok(); + #[allow(unused_variables)] let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); #[allow(unused_variables)] let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); @@ -22,52 +76,31 @@ fn main() { let is_macos = target_os == "macos"; {% for scheme in schemes %} - {% for implementation in [scheme.implementation] + scheme.alt_implementations|default([]) %} - { - let mut builder = cc::Build::new(); - let target_dir: PathBuf = ["pqclean", "crypto_{{ type }}", "{{ scheme.name }}", "{{ implementation }}"].iter().collect(); - let scheme_files = glob::glob(target_dir.join("*.c").to_str().unwrap()).unwrap(); - builder.include(internals_include_path) - .include(&common_dir) - .include(target_dir) - .files(scheme_files.into_iter().map(|p| p.unwrap().to_string_lossy().into_owned())); - builder.compile("{{ scheme.name }}_{{ implementation }}"); - } - {% endfor %} - - {# Do we have an AVX2 version? #} - {% if 'avx2_implementation' in scheme %} - {% set implementation = scheme.avx2_implementation %} - {% set globals.have_avx2 = True %} + {% for implementation in scheme.implementations %} + {% if implementation == 'avx2' or implementation == 'avx' %} if {{ avx2_guard }} { - let target_dir: PathBuf = ["pqclean", "crypto_{{ type }}", "{{ scheme.name }}", "{{ implementation }}"].iter().collect(); - let scheme_files = glob::glob(target_dir.join("*.[csS]").to_str().unwrap()).unwrap(); - let mut builder = cc::Build::new(); - - if cfg!(target_env = "msvc") { - builder.flag("/arch:AVX2"); - } else { - builder.flag("-mavx2") - .flag("-mbmi2") - .flag("-mbmi") - .flag("-maes") - .flag("-mpopcnt") - .flag("-mpclmul"); - } - builder - .include(internals_include_path) - .include(&common_dir) - .include(target_dir) - .files(scheme_files.into_iter().map(|p| p.unwrap().to_string_lossy().into_owned())) - .compile("{{ scheme.name }}_{{ implementation }}"); + build_{{ implementation }}!("{{ scheme.name }}"); + } + {% elif implementation == 'aesni' %} + if {{ aes_guard }} { + build_{{ implementation }}!("{{ scheme.name }}"); } + {% else %} + build_{{ implementation }}!("{{ scheme.name }}"); {% endif %} {% endfor %} + {% endfor %} {% if globals.have_avx2 %} - // Print enableing flag for AVX2 implementation if {{ avx2_guard }} { + // Print enableing flag for AVX2 implementation println!("cargo:rustc-cfg=enable_avx2"); } {% endif %} + {% if globals.have_aes %} + if {{ aes_guard }} { + // Print enableing flag for AES implementation + println!("cargo:rustc-cfg=enable_aes"); + } + {% endif %} } diff --git a/pqcrypto-template/scheme/src/ffi.rs.j2 b/pqcrypto-template/scheme/src/ffi.rs.j2 index ca367c5e..78c209fc 100644 --- a/pqcrypto-template/scheme/src/ffi.rs.j2 +++ b/pqcrypto-template/scheme/src/ffi.rs.j2 @@ -15,192 +15,192 @@ use pqcrypto_internals::*; {% for scheme in schemes %} {% set metadata = metadatas[scheme.name] %} -{% for implementation in [scheme.implementation] + scheme.alt_implementations|default([]) %} -{% set implementation_data = metadata['implementations']|selectattr('name', 'eq', implementation)|first %} -{% set NS_NAME = [scheme.name|namespaceize, implementation|namespaceize]|join('_') %} -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} -pub const PQCLEAN_{{ NS_NAME }}_CRYPTO_SECRETKEYBYTES: usize = {{ metadata['length-secret-key'] }}; -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} -pub const PQCLEAN_{{ NS_NAME }}_CRYPTO_PUBLICKEYBYTES: usize = {{ metadata['length-public-key'] }}; -{% if type == "kem" %} -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} -pub const PQCLEAN_{{ NS_NAME }}_CRYPTO_CIPHERTEXTBYTES: usize = {{ metadata['length-ciphertext'] }}; -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} -pub const PQCLEAN_{{ NS_NAME }}_CRYPTO_BYTES: usize = {{ metadata['length-shared-secret'] }}; -{% else %} -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} -pub const PQCLEAN_{{ NS_NAME }}_CRYPTO_BYTES: usize = {{ metadata['length-signature'] }}; -{% endif %} -{% endfor %} -{% if 'avx2_implementation' in scheme %} -{% set implementation = scheme.avx2_implementation %} -{% set implementation_data = metadata['implementations']|selectattr('name', 'eq', implementation)|first %} +{% for implementation in scheme.implementations %} {% set NS_NAME = [scheme.name|namespaceize, implementation|namespaceize]|join('_') %} + +{% if implementation == 'avx2' or implementation == 'avx' %} #[cfg(enable_avx2)] +{% elif implementation == 'aesni' %} +#[cfg(enable_aes)] +{% endif %} {% if insecure %} #[deprecated(note = "Insecure cryptography, do not use in production")] {% endif %} pub const PQCLEAN_{{ NS_NAME }}_CRYPTO_SECRETKEYBYTES: usize = {{ metadata['length-secret-key'] }}; +{% if implementation == 'avx2' or implementation == 'avx' %} #[cfg(enable_avx2)] +{% elif implementation == 'aesni' %} +#[cfg(enable_aes)] +{% endif %} {% if insecure %} #[deprecated(note = "Insecure cryptography, do not use in production")] {% endif %} pub const PQCLEAN_{{ NS_NAME }}_CRYPTO_PUBLICKEYBYTES: usize = {{ metadata['length-public-key'] }}; {% if type == "kem" %} -#[cfg(enable_avx2)] -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} -pub const PQCLEAN_{{ NS_NAME }}_CRYPTO_CIPHERTEXTBYTES: usize = {{ metadata['length-ciphertext'] }}; -#[cfg(enable_avx2)] -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} -pub const PQCLEAN_{{ NS_NAME }}_CRYPTO_BYTES: usize = {{ metadata['length-shared-secret'] }}; + {% if implementation == 'avx2' or implementation == 'avx' %} + #[cfg(enable_avx2)] + {% elif implementation == 'aesni' %} + #[cfg(enable_aes)] + {% endif %} + {% if insecure %} + #[deprecated(note = "Insecure cryptography, do not use in production")] + {% endif %} + pub const PQCLEAN_{{ NS_NAME }}_CRYPTO_CIPHERTEXTBYTES: usize = {{ metadata['length-ciphertext'] }}; + {% if implementation == 'avx2' or implementation == 'avx' %} + #[cfg(enable_avx2)] + {% elif implementation == 'aesni' %} + #[cfg(enable_aes)] + {% endif %} + {% if insecure %} + #[deprecated(note = "Insecure cryptography, do not use in production")] + {% endif %} + pub const PQCLEAN_{{ NS_NAME }}_CRYPTO_BYTES: usize = {{ metadata['length-shared-secret'] }}; {% else %} -#[cfg(enable_avx2)] -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} -pub const PQCLEAN_{{ NS_NAME }}_CRYPTO_BYTES: usize = {{ metadata['length-signature'] }}; -{% endif %} -{% endif %} -{% endfor %} + {% if implementation == 'avx2' or implementation == 'avx' %} + #[cfg(enable_avx2)] + {% elif implementation == 'aesni' %} + #[cfg(enable_aes)] + {% endif %} + {% if insecure %} + #[deprecated(note = "Insecure cryptography, do not use in production")] + {% endif %} + pub const PQCLEAN_{{ NS_NAME }}_CRYPTO_BYTES: usize = {{ metadata['length-signature'] }}; +{% endif %} {# KEM or SIGN #} +{% endfor %} {# implementations #} +{% endfor %} {# schemes #} + {% for scheme in schemes %} -{% for implementation in [scheme.implementation] + scheme.alt_implementations|default([]) %} -#[link(name = "{{ scheme.name }}_{{ implementation }}")] -extern "C" { +{% for implementation in scheme.implementations %} {% set NS_NAME = [scheme.name|namespaceize, implementation|namespaceize]|join('_') %} -{% if type == "kem" %} -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} - pub fn PQCLEAN_{{ NS_NAME }}_crypto_kem_keypair(pk: *mut u8, sk: *mut u8) -> c_int; -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} - pub fn PQCLEAN_{{ NS_NAME }}_crypto_kem_enc(ct: *mut u8, ss: *mut u8, pk: *const u8) -> c_int; -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} - pub fn PQCLEAN_{{ NS_NAME }}_crypto_kem_dec( - ss: *mut u8, - ct: *const u8, - sk: *const u8, - ) -> c_int; -{% else %} -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} - pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign_keypair(pk: *mut u8, sk: *mut u8) -> c_int; -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} - pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign(sm: *mut u8, smlen: *mut usize, msg: *const u8, len: usize, sk: *const u8) -> c_int; -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} - pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign_open(m: *mut u8, mlen: *mut usize, sm: *const u8, smlen: usize, pk: *const u8) -> c_int; -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} - pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign_signature(sig: *mut u8, siglen: *mut usize, m: *const u8, mlen: usize, sk: *const u8) -> c_int; -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} - pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign_verify(sig: *const u8, siglen: usize, m: *const u8, mlen: usize, pk: *const u8) -> c_int; -{% endif %} -} -{% endfor %} -{# AVX2 variants #} -{% if 'avx2_implementation' in scheme %} +{% if implementation == 'avx2' or implementation == 'avx' %} #[cfg(enable_avx2)] -#[link(name = "{{ scheme.name }}_{{ scheme.avx2_implementation }}")] +{% elif implementation == 'aesni' %} +#[cfg(enable_aes)] +{% endif %} +#[link(name = "{{ scheme.name }}_{{ implementation }}")] extern "C" { - {% set NS_NAME = [scheme.name|namespaceize, scheme.avx2_implementation|namespaceize]|join('_') %} - {% if type == "kem" %} +{% if type == "kem" %} + {% if implementation == 'avx2' or implementation == 'avx' %} #[cfg(enable_avx2)] -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} - pub fn PQCLEAN_{{ NS_NAME }}_crypto_kem_keypair(pk: *mut u8, sk: *mut u8) -> c_int; - + {% elif implementation == 'aesni' %} + #[cfg(enable_aes)] + {% endif %} + {% if insecure %} + #[deprecated(note = "Insecure cryptography, do not use in production")] + {% endif %} + pub fn PQCLEAN_{{ NS_NAME }}_crypto_kem_keypair(pk: *mut u8, sk: *mut u8) -> c_int; + {% if implementation == 'avx2' or implementation == 'avx' %} #[cfg(enable_avx2)] -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} - pub fn PQCLEAN_{{ NS_NAME }}_crypto_kem_enc(ct: *mut u8, ss: *mut u8, pk: *const u8) -> c_int; - + {% elif implementation == 'aesni' %} + #[cfg(enable_aes)] + {% endif %} + {% if insecure %} + #[deprecated(note = "Insecure cryptography, do not use in production")] + {% endif %} + pub fn PQCLEAN_{{ NS_NAME }}_crypto_kem_enc(ct: *mut u8, ss: *mut u8, pk: *const u8) -> c_int; + {% if implementation == 'avx2' or implementation == 'avx' %} #[cfg(enable_avx2)] -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} - pub fn PQCLEAN_{{ NS_NAME }}_crypto_kem_dec( - ss: *mut u8, - ct: *const u8, - sk: *const u8, - ) -> c_int; - {% else %} + {% elif implementation == 'aesni' %} + #[cfg(enable_aes)] + {% endif %} + {% if insecure %} + #[deprecated(note = "Insecure cryptography, do not use in production")] + {% endif %} + pub fn PQCLEAN_{{ NS_NAME }}_crypto_kem_dec( + ss: *mut u8, + ct: *const u8, + sk: *const u8, + ) -> c_int; +{% else %} + {% if implementation == 'avx2' or implementation == 'avx' %} #[cfg(enable_avx2)] -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} - pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign_keypair(pk: *mut u8, sk: *mut u8) -> c_int; -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} + {% elif implementation == 'aesni' %} + #[cfg(enable_aes)] + {% endif %} + {% if insecure %} + #[deprecated(note = "Insecure cryptography, do not use in production")] + {% endif %} + pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign_keypair(pk: *mut u8, sk: *mut u8) -> c_int; + {% if implementation == 'avx2' or implementation == 'avx' %} #[cfg(enable_avx2)] -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} - pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign(sm: *mut u8, smlen: *mut usize, msg: *const u8, len: usize, sk: *const u8) -> c_int; + {% elif implementation == 'aesni' %} + #[cfg(enable_aes)] + {% endif %} + {% if insecure %} + #[deprecated(note = "Insecure cryptography, do not use in production")] + {% endif %} + pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign(sm: *mut u8, smlen: *mut usize, msg: *const u8, len: usize, sk: *const u8) -> c_int; + {% if implementation == 'avx2' or implementation == 'avx' %} #[cfg(enable_avx2)] -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} - pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign_open(m: *mut u8, mlen: *mut usize, sm: *const u8, smlen: usize, pk: *const u8) -> c_int; + {% elif implementation == 'aesni' %} + #[cfg(enable_aes)] + {% endif %} + {% if insecure %} + #[deprecated(note = "Insecure cryptography, do not use in production")] + {% endif %} + pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign_open(m: *mut u8, mlen: *mut usize, sm: *const u8, smlen: usize, pk: *const u8) -> c_int; + {% if implementation == 'avx2' or implementation == 'avx' %} #[cfg(enable_avx2)] -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} - pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign_signature(sig: *mut u8, siglen: *mut usize, m: *const u8, mlen: usize, sk: *const u8) -> c_int; + {% elif implementation == 'aesni' %} + #[cfg(enable_aes)] + {% endif %} + {% if insecure %} + #[deprecated(note = "Insecure cryptography, do not use in production")] + {% endif %} + pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign_signature(sig: *mut u8, siglen: *mut usize, m: *const u8, mlen: usize, sk: *const u8) -> c_int; + {% if implementation == 'avx2' or implementation == 'avx' %} #[cfg(enable_avx2)] -{% if insecure %} -#[deprecated(note = "Insecure cryptography, do not use in production")] -{% endif %} - pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign_verify(sig: *const u8, siglen: usize, m: *const u8, mlen: usize, pk: *const u8) -> c_int; + {% elif implementation == 'aesni' %} + #[cfg(enable_aes)] {% endif %} -} + {% if insecure %} + #[deprecated(note = "Insecure cryptography, do not use in production")] + {% endif %} + pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign_verify(sig: *const u8, siglen: usize, m: *const u8, mlen: usize, pk: *const u8) -> c_int; {% endif %} -{% endfor %} +} +{% endfor %} {# implementations #} +{% endfor %} {# schemes #} + {% for scheme in schemes %} -{% for implementation in [scheme.implementation] + scheme.alt_implementations|default([]) %} +{% for implementation in scheme.implementations %} {% set NS_NAME = [scheme.name|namespaceize, implementation|namespaceize]|join('_') %} + +{% if implementation == 'avx2' or implementation == 'avx' %} +#[cfg(all(test, enable_avx2, feature = "avx2"))] +{% elif implementation == 'aesni' %} +#[cfg(all(test, enable_aes, feature = "aes"))] +{% else %} #[cfg(test)] +{% endif %} mod test_{{ scheme.name|nameize }}_{{ implementation|nameize }} { use super::*; use alloc::vec; -{% if type == "sign" %} + {% if implementation == 'avx2' or implementation == 'avx' %} + use std::is_x86_feature_detected; + {% elif implementation == 'aesni' %} + use std::is_x86_feature_detected; + {% endif %} + {% if type == "sign" %} use alloc::vec::Vec; use rand::prelude::*; -{% endif %} + {% endif %} {% if type == "kem" %} #[test] fn test_ffi() { + {% if implementation == 'avx2' or implementation == 'avx' %} + if !is_x86_feature_detected!("avx2") { + return; + } + {% elif implementation == 'aesni' %} + if !is_x86_feature_detected!("aes") { + return; + } + {% endif %} unsafe { let mut pk = vec![0u8; PQCLEAN_{{ NS_NAME }}_CRYPTO_PUBLICKEYBYTES]; let mut sk = vec![0u8; PQCLEAN_{{ NS_NAME }}_CRYPTO_SECRETKEYBYTES]; @@ -227,11 +227,20 @@ mod test_{{ scheme.name|nameize }}_{{ implementation|nameize }} { assert_eq!(&ss1[..], &ss2[..], "Shared secrets should be equal"); } } - {% endif %} + {% endif %} {# KEM #} {% if type == "sign" %} #[test] fn test_ffi() { + {% if implementation == 'avx2' or implementation == 'avx' %} + if !is_x86_feature_detected!("avx2") { + return; + } + {% elif implementation == 'aesni' %} + if !is_x86_feature_detected!("aes") { + return; + } + {% endif %} unsafe { let mut rng = rand::thread_rng(); let mut mlen: usize = rng.gen::() as usize; @@ -273,14 +282,14 @@ mod test_{{ scheme.name|nameize }}_{{ implementation|nameize }} { 0, PQCLEAN_{{ NS_NAME }}_crypto_sign_keypair(pk_alt.as_mut_ptr(), sk_alt.as_mut_ptr()) ); - assert!( + assert_eq!( + -1, PQCLEAN_{{ NS_NAME }}_crypto_sign_open( unpacked_m.as_mut_ptr(), &mut mlen as *mut usize, sm.as_ptr(), sm.len(), pk_alt.as_ptr() - ) < 0 + ) ); - assert_eq!( 0, PQCLEAN_{{ NS_NAME }}_crypto_sign_signature( @@ -297,167 +306,23 @@ mod test_{{ scheme.name|nameize }}_{{ implementation|nameize }} { msg.as_ptr(), msg.len(), pk.as_ptr()) ); - assert!( + assert_eq!( + -1, PQCLEAN_{{ NS_NAME }}_crypto_sign_verify( detached_sig.as_ptr(), smlen, msg.as_ptr(), msg.len(), - pk_alt.as_ptr()) < 0 + pk_alt.as_ptr()) ); - - assert!( + assert_eq!( + -1, PQCLEAN_{{ NS_NAME }}_crypto_sign_verify( detached_sig.as_ptr(), smlen, msg.as_ptr(), msg.len()-1, - pk.as_ptr()) < 0 + pk.as_ptr()) ); } } - {% endif %} -} -{% endfor %} -{% if 'avx2_implementation' in scheme %} -{% set implementation = scheme.avx2_implementation %} -{% set NS_NAME = [scheme.name|namespaceize, implementation|namespaceize]|join('_') %} -#[cfg(all(test, enable_avx2, feature = "avx2"))] -mod test_{{ scheme.name|nameize }}_{{ implementation|nameize }} { - use super::*; - use alloc::vec; - use std::is_x86_feature_detected; -{% if type == "sign" %} - use alloc::vec::Vec; - use rand::prelude::*; -{% endif %} - - {% if type == "kem" %} - #[test] - fn test_ffi() { - if !is_x86_feature_detected!("{{ scheme.avx2_feature }}") { - return; - } - unsafe { run_test_ffi(); } - } - - #[target_feature(enable = "{{ scheme.avx2_feature }}")] - unsafe fn run_test_ffi() { - let mut pk = vec![0u8; PQCLEAN_{{ NS_NAME }}_CRYPTO_PUBLICKEYBYTES]; - let mut sk = vec![0u8; PQCLEAN_{{ NS_NAME }}_CRYPTO_SECRETKEYBYTES]; - let mut ct = vec![0u8; PQCLEAN_{{ NS_NAME }}_CRYPTO_CIPHERTEXTBYTES]; - let mut ss1 = vec![0u8; PQCLEAN_{{ NS_NAME }}_CRYPTO_BYTES]; - let mut ss2 = vec![0u8; PQCLEAN_{{ NS_NAME }}_CRYPTO_BYTES]; - - assert_eq!( - 0, - PQCLEAN_{{ NS_NAME }}_crypto_kem_keypair(pk.as_mut_ptr(), sk.as_mut_ptr()) - ); - assert_eq!( - 0, - PQCLEAN_{{ NS_NAME }}_crypto_kem_enc( - ct.as_mut_ptr(), - ss1.as_mut_ptr(), - pk.as_ptr() - ) - ); - assert_eq!( - 0, - PQCLEAN_{{ NS_NAME }}_crypto_kem_dec(ss2.as_mut_ptr(), ct.as_ptr(), sk.as_ptr()) - ); - assert_eq!(&ss1[..], &ss2[..], "Shared secrets should be equal"); - } - {% endif %} - - {% if type == "sign" %} - #[test] - fn test_ffi() { - if !is_x86_feature_detected!("{{ scheme.avx2_feature }}") { - return; - } - unsafe { run_test_ffi() }; - } - - #[target_feature(enable = "{{ scheme.avx2_feature }}")] - unsafe fn run_test_ffi() { - let mut rng = rand::thread_rng(); - let mut mlen: usize = rng.gen::() as usize; - let msg: Vec = (0..mlen).map(|_| rng.gen()).collect(); - - let mut pk = vec![0u8; PQCLEAN_{{ NS_NAME }}_CRYPTO_PUBLICKEYBYTES]; - let mut sk = vec![0u8; PQCLEAN_{{ NS_NAME }}_CRYPTO_SECRETKEYBYTES]; - let mut pk_alt = vec![0u8; PQCLEAN_{{ NS_NAME }}_CRYPTO_PUBLICKEYBYTES]; - let mut sk_alt = vec![0u8; PQCLEAN_{{ NS_NAME }}_CRYPTO_SECRETKEYBYTES]; - let mut detached_sig = vec![0u8; PQCLEAN_{{ NS_NAME }}_CRYPTO_BYTES]; - let mut sm = Vec::with_capacity(mlen + PQCLEAN_{{ NS_NAME }}_CRYPTO_BYTES); - let mut smlen = 0; - assert_eq!( - 0, - PQCLEAN_{{ NS_NAME }}_crypto_sign_keypair(pk.as_mut_ptr(), sk.as_mut_ptr()) - ); - assert_eq!( - 0, - PQCLEAN_{{ NS_NAME }}_crypto_sign( - sm.as_mut_ptr(), &mut smlen as *mut usize, - msg.as_ptr(), mlen, sk.as_ptr()) - ); - sm.set_len(smlen); - - let mut unpacked_m = Vec::with_capacity(mlen + PQCLEAN_{{ NS_NAME }}_CRYPTO_BYTES); - assert_eq!( - 0, - PQCLEAN_{{ NS_NAME }}_crypto_sign_open( - unpacked_m.as_mut_ptr(), &mut mlen as *mut usize, - sm.as_ptr(), sm.len(), - pk.as_ptr() - ) - ); - unpacked_m.set_len(mlen); - assert_eq!(unpacked_m, msg); - - // check verification fails with wrong pk - assert_eq!( - 0, - PQCLEAN_{{ NS_NAME }}_crypto_sign_keypair(pk_alt.as_mut_ptr(), sk_alt.as_mut_ptr()) - ); - assert_eq!( - -1, - PQCLEAN_{{ NS_NAME }}_crypto_sign_open( - unpacked_m.as_mut_ptr(), &mut mlen as *mut usize, - sm.as_ptr(), sm.len(), - pk_alt.as_ptr() - ) - ); - - assert_eq!( - 0, - PQCLEAN_{{ NS_NAME }}_crypto_sign_signature( - detached_sig.as_mut_ptr(), &mut smlen as *mut usize, - msg.as_ptr(), msg.len(), - sk.as_ptr()) - ); - assert!(smlen <= PQCLEAN_{{ NS_NAME }}_CRYPTO_BYTES, - "Signed message length should be ≤ CRYPTO_BYTES"); - assert_eq!( - 0, - PQCLEAN_{{ NS_NAME }}_crypto_sign_verify( - detached_sig.as_ptr(), smlen, - msg.as_ptr(), msg.len(), - pk.as_ptr()) - ); - assert_eq!( - -1, - PQCLEAN_{{ NS_NAME }}_crypto_sign_verify( - detached_sig.as_ptr(), smlen, - msg.as_ptr(), msg.len(), - pk_alt.as_ptr()) - ); - - assert_eq!( - -1, - PQCLEAN_{{ NS_NAME }}_crypto_sign_verify( - detached_sig.as_ptr(), smlen, - msg.as_ptr(), msg.len()-1, - pk.as_ptr()) - ); - } - {% endif %} + {% endif %} {# SIGN #} } -{% endif %} -{% endfor %} +{% endfor %} {# implementations #} +{% endfor %} {# schemes #} diff --git a/pqcrypto-template/scheme/src/lib.rs.j2 b/pqcrypto-template/scheme/src/lib.rs.j2 index 2b31b035..897b75df 100644 --- a/pqcrypto-template/scheme/src/lib.rs.j2 +++ b/pqcrypto-template/scheme/src/lib.rs.j2 @@ -3,7 +3,7 @@ //! This crate provides bindings to and wrappers around the following //! implementations from [PQClean][pqc]: //! -{% for scheme in schemes %}//! * {{ scheme.name }} - {{ scheme.implementation }} +{% for scheme in schemes %}//! * {{ scheme.name }} - {{ scheme.implementations[0] }} {% endfor %} //! //! [pqc]: https://github.com/pqclean/pqclean/ diff --git a/pqcrypto-template/scheme/src/scheme.rs.j2 b/pqcrypto-template/scheme/src/scheme.rs.j2 index b783f990..580c66ad 100644 --- a/pqcrypto-template/scheme/src/scheme.rs.j2 +++ b/pqcrypto-template/scheme/src/scheme.rs.j2 @@ -1,6 +1,6 @@ //! {{ scheme.name }} //! -//! These bindings use the {{ scheme.implementation }} version from [PQClean][pqc] +//! These bindings use the {{ scheme.implementations[0] }} version from [PQClean][pqc] {% if insecure %} //! //! **This algorithm has security problems** @@ -29,6 +29,10 @@ // This file is generated. +{% set globals = namespace() %} +{% set globals.have_avx2 = False %} +{% set globals.have_aes = False %} + #[cfg(feature = "serialization")] use serde::{Deserialize, Serialize}; #[cfg(feature = "serialization")] @@ -95,9 +99,16 @@ macro_rules! simple_struct { }; } -{% set NS_NAME = [scheme.name|namespaceize, scheme.implementation|namespaceize]|join('_') %} -{% if 'avx2_implementation' in scheme %} -{% set AVX2_NAME = [scheme.name|namespaceize, scheme.avx2_implementation|namespaceize]|join('_') %} +{% set NS_NAME = [scheme.name|namespaceize, scheme.implementations[0]|namespaceize]|join('_') %} +{% if 'avx2' in scheme.implementations %} +{% set globals.have_avx2 = True %} +{% set AVX2_NAME = [scheme.name|namespaceize, 'avx2'|namespaceize]|join('_') %} +{% elif 'avx' in scheme.implementations %} +{% set globals.have_avx2 = True %} +{% set AVX2_NAME = [scheme.name|namespaceize, 'avx'|namespaceize]|join('_') %} +{% elif 'aesni' in scheme.implementations %} +{% set globals.have_aes = True %} +{% set AES_NAME = [scheme.name|namespaceize, 'aesni'|namespaceize]|join('_') %} {% endif %} simple_struct!(PublicKey, ffi::PQCLEAN_{{ NS_NAME }}_CRYPTO_PUBLICKEYBYTES); @@ -172,7 +183,7 @@ impl SignedMessage { self.0.len() } } -{% endif %} +{% endif %} {# KEM or SIGN #} /// Get the number of bytes for a public key pub const fn public_key_bytes() -> usize { @@ -220,10 +231,10 @@ macro_rules! gen_keypair { #[deprecated(note = "Insecure cryptography, do not use in production")] {% endif %} pub fn keypair() -> (PublicKey, SecretKey) { - {% if 'avx2_implementation' in scheme %} + {% if globals.have_avx2 %} #[cfg(all(enable_avx2, feature = "avx2"))] { - if std::is_x86_feature_detected!("{{ scheme.avx2_feature }}") { + if std::is_x86_feature_detected!("avx2") { {% if type == "kem" %} return gen_keypair!(PQCLEAN_{{ AVX2_NAME }}_crypto_kem_keypair); {% else %} @@ -232,6 +243,18 @@ pub fn keypair() -> (PublicKey, SecretKey) { } } {% endif %} + {% if globals.have_aes %} + #[cfg(all(enable_aes, feature = "aes"))] + { + if std::is_x86_feature_detected!("aes") { + {% if type == "kem" %} + return gen_keypair!(PQCLEAN_{{ AES_NAME }}_crypto_kem_keypair); + {% else %} + return gen_keypair!(PQCLEAN_{{ AES_NAME }}_crypto_sign_keypair); + {% endif %} + } + } + {% endif %} {% if type == "kem" %} gen_keypair!(PQCLEAN_{{ NS_NAME }}_crypto_kem_keypair) {% else %} @@ -262,14 +285,22 @@ macro_rules! encap { #[deprecated(note = "Insecure cryptography, do not use in production")] {% endif %} pub fn encapsulate(pk: &PublicKey) -> (SharedSecret, Ciphertext) { - {% if 'avx2_implementation' in scheme %} + {% if globals.have_avx2 %} #[cfg(all(enable_avx2, feature = "avx2"))] { - if std::is_x86_feature_detected!("{{ scheme.avx2_feature }}") { + if std::is_x86_feature_detected!("avx2") { return encap!(PQCLEAN_{{ AVX2_NAME }}_crypto_kem_enc, pk); } } {% endif %} + {% if globals.have_aes %} + #[cfg(all(enable_aes, feature = "aes"))] + { + if std::is_x86_feature_detected!("aes") { + return encap!(PQCLEAN_{{ AES_NAME }}_crypto_kem_enc, pk); + } + } + {% endif %} encap!(PQCLEAN_{{ NS_NAME }}_crypto_kem_enc, pk) } @@ -297,14 +328,22 @@ macro_rules! decap { #[deprecated(note = "Insecure cryptography, do not use in production")] {% endif %} pub fn decapsulate(ct: &Ciphertext, sk: &SecretKey) -> SharedSecret { - {% if 'avx2_implementation' in scheme %} + {% if globals.have_avx2 %} #[cfg(all(enable_avx2, feature = "avx2"))] { - if std::is_x86_feature_detected!("{{ scheme.avx2_feature }}") { + if std::is_x86_feature_detected!("avx2") { return decap!(PQCLEAN_{{ AVX2_NAME }}_crypto_kem_dec, ct, sk); } } {% endif %} + {% if globals.have_aes %} + #[cfg(all(enable_aes, feature = "aes"))] + { + if std::is_x86_feature_detected!("aes") { + return decap!(PQCLEAN_{{ AES_NAME }}_crypto_kem_dec, ct, sk); + } + } + {% endif %} decap!(PQCLEAN_{{ NS_NAME }}_crypto_kem_dec, ct, sk) } @@ -339,14 +378,22 @@ macro_rules! gen_signature { #[deprecated(note = "Insecure cryptography, do not use in production")] {% endif %} pub fn sign(msg: &[u8], sk: &SecretKey) -> SignedMessage { - {% if 'avx2_implementation' in scheme %} + {% if globals.have_avx2 %} #[cfg(all(enable_avx2, feature = "avx2"))] { - if std::is_x86_feature_detected!("{{ scheme.avx2_feature }}") { + if std::is_x86_feature_detected!("avx2") { return gen_signature!(PQCLEAN_{{ AVX2_NAME }}_crypto_sign, msg, sk); } } {% endif %} + {% if globals.have_aes %} + #[cfg(all(enable_aes, feature = "aes"))] + { + if std::is_x86_feature_detected!("aes") { + return gen_signature!(PQCLEAN_{{ AES_NAME }}_crypto_sign, msg, sk); + } + } + {% endif %} gen_signature!(PQCLEAN_{{ NS_NAME }}_crypto_sign, msg, sk) } @@ -383,14 +430,22 @@ pub fn open( sm: &SignedMessage, pk: &PublicKey ) -> core::result::Result,primitive::VerificationError> { - {% if 'avx2_implementation' in scheme %} + {% if globals.have_avx2 %} #[cfg(all(enable_avx2, feature = "avx2"))] { - if std::is_x86_feature_detected!("{{ scheme.avx2_feature }}") { + if std::is_x86_feature_detected!("avx2") { return open_signed!(PQCLEAN_{{ AVX2_NAME }}_crypto_sign_open, sm, pk); } } {% endif %} + {% if globals.have_aes %} + #[cfg(all(enable_aes, feature = "aes"))] + { + if std::is_x86_feature_detected!("aes") { + return open_signed!(PQCLEAN_{{ AES_NAME }}_crypto_sign_open, sm, pk); + } + } + {% endif %} open_signed!(PQCLEAN_{{ NS_NAME }}_crypto_sign_open, sm, pk) } @@ -417,14 +472,22 @@ macro_rules! detached_signature { {% endif %} /// Create a detached signature on the message pub fn detached_sign(msg: &[u8], sk: &SecretKey) -> DetachedSignature { - {% if 'avx2_implementation' in scheme %} + {% if globals.have_avx2 %} #[cfg(all(enable_avx2, feature = "avx2"))] { - if std::is_x86_feature_detected!("{{ scheme.avx2_feature }}") { + if std::is_x86_feature_detected!("avx2") { return detached_signature!(PQCLEAN_{{ AVX2_NAME }}_crypto_sign_signature, msg, sk); } } {% endif %} + {% if globals.have_aes %} + #[cfg(all(enable_aes, feature = "aes"))] + { + if std::is_x86_feature_detected!("aes") { + return detached_signature!(PQCLEAN_{{ AES_NAME }}_crypto_sign_signature, msg, sk); + } + } + {% endif %} detached_signature!(PQCLEAN_{{ NS_NAME }}_crypto_sign_signature, msg, sk) } @@ -454,19 +517,27 @@ macro_rules! verify_detached_sig { #[deprecated(note = "Insecure cryptography, do not use in production")] {% endif %} pub fn verify_detached_signature(sig: &DetachedSignature, msg: &[u8], pk: &PublicKey) -> core::result::Result<(), primitive::VerificationError> { - {% if 'avx2_implementation' in scheme %} + {% if globals.have_avx2 %} #[cfg(all(enable_avx2, feature = "avx2"))] { - if std::is_x86_feature_detected!("{{ scheme.avx2_feature }}") { + if std::is_x86_feature_detected!("avx2") { return verify_detached_sig!(PQCLEAN_{{ AVX2_NAME }}_crypto_sign_verify, sig, msg, pk); } } {% endif %} + {% if globals.have_aes %} + #[cfg(all(enable_aes, feature = "aes"))] + { + if std::is_x86_feature_detected!("aes") { + return verify_detached_sig!(PQCLEAN_{{ AES_NAME }}_crypto_sign_verify, sig, msg, pk); + } + } + {% endif %} verify_detached_sig!(PQCLEAN_{{ NS_NAME }}_crypto_sign_verify, sig, msg, pk) } -{% endif %} +{% endif %} {# KEM or SIGN #} #[cfg(test)] @@ -508,5 +579,5 @@ mod test { assert!(verify_detached_signature(&sig, &message, &pk).is_ok()); assert!(!verify_detached_signature(&sig, &message[..message.len()-1], &pk).is_ok()); } -{% endif %} +{% endif %} {# KEM or SIGN #} } diff --git a/pqcrypto/Cargo.toml b/pqcrypto/Cargo.toml index 90842b0c..cac3953a 100644 --- a/pqcrypto/Cargo.toml +++ b/pqcrypto/Cargo.toml @@ -13,17 +13,17 @@ categories = ["cryptography"] [dependencies] pqcrypto-traits = { path = "../pqcrypto-traits", version = "0.3.4" } -pqcrypto-kyber = { path = "../pqcrypto-kyber", version = "0.7.3", optional = true } -pqcrypto-frodo = { path = "../pqcrypto-frodo", version = "0.4.9", optional = true } -pqcrypto-ntru = { path = "../pqcrypto-ntru", version = "0.5.6", optional = true } -pqcrypto-ntruprime = { path = "../pqcrypto-ntruprime", version = "0.1.4", optional = true } -pqcrypto-saber = { path = "../pqcrypto-saber", version = "0.1.9", optional = true } -pqcrypto-classicmceliece = { path = "../pqcrypto-classicmceliece", version = "0.1.5", optional = true } -pqcrypto-hqc = { path = "../pqcrypto-hqc", version = "0.1.3", optional = true } -pqcrypto-dilithium = { path = "../pqcrypto-dilithium", version = "0.4.3", optional = true } -pqcrypto-falcon = { path = "../pqcrypto-falcon", version = "0.2.9", optional = true } -pqcrypto-rainbow = { path = "../pqcrypto-rainbow", version = "0.2.3", optional = true } -pqcrypto-sphincsplus = { path = "../pqcrypto-sphincsplus", version = "0.6.2", optional = true } +pqcrypto-kyber = { path = "../pqcrypto-kyber", version = "0.7.4", optional = true } +pqcrypto-frodo = { path = "../pqcrypto-frodo", version = "0.4.10", optional = true } +pqcrypto-ntru = { path = "../pqcrypto-ntru", version = "0.5.7", optional = true } +pqcrypto-ntruprime = { path = "../pqcrypto-ntruprime", version = "0.1.5", optional = true } +pqcrypto-saber = { path = "../pqcrypto-saber", version = "0.1.10", optional = true } +pqcrypto-classicmceliece = { path = "../pqcrypto-classicmceliece", version = "0.1.6", optional = true } +pqcrypto-hqc = { path = "../pqcrypto-hqc", version = "0.1.4", optional = true } +pqcrypto-dilithium = { path = "../pqcrypto-dilithium", version = "0.4.4", optional = true } +pqcrypto-falcon = { path = "../pqcrypto-falcon", version = "0.2.10", optional = true } +pqcrypto-rainbow = { path = "../pqcrypto-rainbow", version = "0.2.4", optional = true } +pqcrypto-sphincsplus = { path = "../pqcrypto-sphincsplus", version = "0.6.3", optional = true } [features] default = ["pqcrypto-kyber","pqcrypto-frodo","pqcrypto-ntru","pqcrypto-ntruprime","pqcrypto-saber","pqcrypto-classicmceliece","pqcrypto-hqc","pqcrypto-dilithium","pqcrypto-falcon","pqcrypto-rainbow","pqcrypto-sphincsplus",]