second-round version of SQIsign

Co-authored-by: Marius A. Aardal <marius.andre.aardal@gmail.com>
Co-authored-by: Gora Adj <gora.adj@tii.ae>
Co-authored-by: Diego F. Aranha <dfaranha@cs.au.dk>
Co-authored-by: Andrea Basso <sqisign@andreabasso.com>
Co-authored-by: Isaac Andrés Canales Martínez <icanalesm0500@gmail.com>
Co-authored-by: Jorge Chávez-Saab <jorgechavezsaab@gmail.com>
Co-authored-by: Maria Corte-Real Santos <mariascrsantos98@gmail.com>
Co-authored-by: Luca De Feo <github@defeo.lu>
Co-authored-by: Max Duparc <max.duparc@epfl.ch>
Co-authored-by: Jonathan Komada Eriksen <jonathan.eriksen97@gmail.com>
Co-authored-by: Décio Luiz Gazzoni Filho <decio@decpp.net>
Co-authored-by: Basil Hess <bhe@zurich.ibm.com>
Co-authored-by: Antonin Leroux <antonin.leroux@polytechnique.org>
Co-authored-by: Patrick Longa <plonga@microsoft.com>
Co-authored-by: Luciano Maino <mainoluciano.96@gmail.com>
Co-authored-by: Michael Meyer <michael@random-oracles.org>
Co-authored-by: Hiroshi Onuki <onuki@mist.i.u-tokyo.ac.jp>
Co-authored-by: Lorenz Panny <lorenz@yx7.cc>
Co-authored-by: Giacomo Pope <giacomopope@gmail.com>
Co-authored-by: Krijn Reijnders <reijnderskrijn@gmail.com>
Co-authored-by: Damien Robert <damien.robert@inria.fr>
Co-authored-by: Francisco Rodríguez-Henriquez <francisco.rodriguez@tii.ae>
Co-authored-by: Sina Schaeffler <sschaeffle@student.ethz.ch>
Co-authored-by: Benjamin Wesolowski <benjamin.wesolowski@ens-lyon.fr>
This commit is contained in:
SQIsign team
2025-02-06 00:00:00 +00:00
committed by Lorenz Panny
parent ff34a8cd18
commit 91e9e464fe
481 changed files with 80785 additions and 55963 deletions

View File

@@ -4,106 +4,196 @@ Please read carefully before contributing to this repo.
## Code structure
The code is split into the modules below:
The source code is in the [`src/`](src) directory and it is split into the
following modules:
- `common`: common code for AES, SHAKE, (P)RNG, memory handling. Every
module that needs a hash function, seed expansion (e.g., KLPT),
module that needs a hash function, seed expansion,
deterministic alea for tests, should call to this module.
- `uintbig`: multi-precision big integers.
- `mp`: code for saturated-representation multiprecision arithmetic.
- `gf`: GF(p^2) and GF(p) arithmetic.
- `ec`: elliptic curves, isogenies and pairings. Everything that is
purely finite-fieldy.
- `quaternion`: quaternion orders and ideals. This is, essentially,
replacing PARI/GP.
- `klpt`: implementation of KLPT.
- `precomp`: constants and precomputed values.
- `quaternion`: quaternion orders and ideals.
- `hd`: code to compute (2,2)-isogenies in the theta model.
- `id2iso`: code for Iso <-> Ideal.
- `util`: auxilary code shared among libraries.
The sources for the modules are in [`src/`](src). Each module is
structured as follows:
```
SQIsign
└── src
└── <module_name>
├── <arch>
│ ├── generic
│ └── lvl1
├── opt
│ ├── generic
│ ├── lvl1
│ ├── lvl1_var1
│ ├── lvl3
│ └── lvl5
└── ref
└── generic
```
where:
- `<module_name>` is the name of the module.
- `<arch>` are optional architecture-specific implementations of the
module (e.g., `broadwell` for code using assembly instructions
specific to the Broadwell platform).
- `opt` and `ref` are the portable *optimized* and *reference*
implementations.
- `lvl1`, `lvl3`, `lvl5` are parameter-dependent implementations of
the module, corresponding to NIST levels 1, 3 and 5, respectively.
- `lvl1_var1` is a variant of `lvl1`, e.g., using a different prime
characteristic. The naming is free, and implementors are encouraged
to choose more explicit naming, e.g., `lvl1_varp6983` for the
variant using the `p6983` prime defined in the SQIsign AC20 variant.
- `generic` is a parameter-independent implementation of the module.
If no folder is named like the currently selected variant (see
[Build](README.md#Build)), then this is compiled instead.
Each of the folders above is allowed to be a symlink. E.g., if a
module has no separate optimized and reference implementation, then
`opt` can be a symlink to `ref`. Other example: a module's code only
depends on the field size, but not the specific prime, then
`lvl1_varp6983` could be a symlink to `lvl1`.
- `verification`: code for the verification protocol.
- `signature`: code for the key generation and signature protocols.
### Contents of a module
The leaf folders described above should arrange code as described
below. We use the `generic` implementation of the `uintbig` module as
an example.
Each module is comprised of *implementation types* and common code. An
implementation type refers to the *portable optimized*, *portable reference* or
any architecture-specific implementation of the module; a module must contain at
least one implementation type. Each implementation type must be in its own
directory within the directory of the module. The optimized and reference
implementation types must be placed in the `opt` and `ref` directories,
respectively; there is no rule for naming other architecture-specific
implementations. Common code refers to optional generic code that is shared
among all implementation types. Common code is placed in special directories
within the directory of a module: header files in the `include` directory and
source files in the `<module_name>x` directory, where `<module_name>` is the
name of the module. An example of a module is given below:
```
generic
── bench
├── CmakeLists.txt
├── bench1.c
── bench2.c
├── include
└── uintbig.h
├── test
│ ├── CmakeLists.txt
│ ├── test1.c
│ └── test2.c
├── CmakeLists.txt
├── internal_header.h
├── soruce1.c
└── soruce2.c
src
── <module_name>
├── include
├── <module_name>x
── opt
├── ref
└── <arch>
```
where:
- `<module_name>` is the name of the module.
- `opt` and `ref` are the portable optimized and reference
implementation types, respectively.
- `<arch>` is an optional architecture-specific implementation type of the
module (e.g., `broadwell` for code using assembly instructions
specific to the Broadwell platform).
- `include` contains header files common to all implementation types.
- `<module_name>x` contains source files common to all implementation types
(i.e., `opt`, `ref` and `<arch>`).
- `include/` shall contain a **unique header file** named
`<module_name>.h`, where `<module_name>` is the name of the module.
This header contains the public API of the module, and is the only
header that can be included by other modules (e.g., via `#include
<uintbig.h>`). These files must contain extensive doxygen-formatted
documentation describing the module, see
[Documentation](#Documentation).
- `bench` and `test` contain one executable per file, containing,
well, benchmarks and unit tests. Refer to [Benchmarks](#Benchmarks)
and [Tests](#Tests) for instructions on how to write these.
- Internal headers for the private use of the module, such as
`internal_header.h` go to the root. Include these using `#include
"internal_header.h"`.
- The implementation of the module also goes into the root.
Header files in the `include` directory above can be included by other modules
and must contain extensive doxygen-formatted documentation describing the
functions declared there; see [Documentation](#Documentation). Any
implementation-type directory above is allowed to be a symlink; e.g., if a
module has no separate optimized and reference implementation, then
`opt` can be a symlink to `ref`.
Similar to a module, each implementation type is comprised of implementation
*variants* and common code. A variant refers to either a *generic*
implementation, an implementation whose parameters are defined by one of the
NIST levels (i.e., 1, 3 or 5) or a variation of the latter. An implementation
type must contain at least one variant. Each variant must be in its own
directory within that of the implementation type. The generic variant must be
placed in the `generic` directory and variants corresponding to NIST levels 1,
3 and 5 are placed in the directories `lvl1`, `lvl3` and `lvl5`, respectively;
there is no rule for naming the directory of a NIST variation, but
implementors are encouraged to choose informative namings. Common code refers to
optional variant-independent code that is shared among all variants of the same
implementation type. Common code is placed in special directories within that of
the implementation type: header files in the `include` directory and source
files in the `lvlx` directory. Expanding on the example above, we show the
details of its implementation types:
```
src
└── <module_name>
├── include
├── <module_name>x
├── opt
│ ├── include
│ ├── lvlx
│ ├── lvl1
│ ├── lvl1_var1
│ ├── lvl3
│ └── lvl5
├── ref
│ ├── generic
│ └── lvl1
└── <arch>
├── include
├── lvlx
├── lvl3
└── lvl5
```
where:
- `lvl1`, `lvl3`, `lvl5` are implementations of NIST levels 1, 3 and 5,
respectively, for the corresponding implementation type.
- `lvl1_var1` is a variation of `lvl1` for the `opt` implementation type (e.g.,
using a different prime characteristic).
- `opt/include` contains header files common to all variants in the `opt`
implementation type (i.e., `lvl1`, `lvl1_var1`, `lvl3` and `lvl5`).
Similarly, `<arch>/include` for all variants in the `<arch>` implementation
type (i.e., `lvl3` and `lvl5`).
- `opt/lvlx` contains source files common to all variants in the `opt`
implementation type. Similarly, `<arch>/lvlx` for all variants in the `<arch>`
implementation type.
- `generic` contains a parameter-independent implementation of the `ref`
implementation type.
As the name suggests, the `generic` variant is a generic implementation which
does not depend on the parameters defined by the NIST levels or any variation
of these. If this directory is present, all other parameter-dependent
implementations are ignored and the `generic` implementation is built instead.
As with modules, header files in the `include` directory of an implementation
type (e.g., `opt/include` and `<arch>/include` above) can be included by other
modules and must contain extensive doxygen-formatted documentation describing
the functions declared there.
Each implementation variant must be organized as follows:
- Header files that can be included by other modules are placed in the `include`
directory. These files must contain extensive doxygen-formatted documentation
describing the functions declared there.
- Source files of the implementation and their private internal header files are
placed directly in the implementation variant directory.
- Source files of unit tests and their private internal header files are placed
in the `test` directory. Refer to [Tests](#Tests) for instructions on how to
write these.
Common code (in `lvlx`) for all variants in an implementation type follows the
same organization as above, with the exception that `lvlx` never contains an
`include` directory. This role is taken by the `include` directory in the
implementation type. Below is an example with the detailed organization of the
common code and the `lvl1` variant for the `ref` implementation type of a
module:
```
<module_name>
├──ref
│ ├── include
│ │ └── header_ref.h
│ ├──lvlx
│ │ ├── test
│ │ │ ├── test_internal_header_ref.h
│ │ │ │ ...
│ │ │ ├── test1_ref.c
│ │ │ └── test2_ref.c
│ │ ├── internal_header_ref.h
│ │ ├── source1_ref.c
│ │ └── source2_ref.c
│ ├──lvl1
│ │ ├── include
│ │ │ └── header_ref_lvl1.h
│ │ ├── test
│ │ │ ├── test_internal_header_ref_lvl1.h
│ │ │ │ ...
│ │ │ ├── test1_ref_lvl1.c
│ │ │ └── test2_ref_lvl1.c
│ │ ├── internal_header_ref_lvl1.h
│ │ ├── source1_ref_lvl1.c
│ │ └── source2_ref_lvl1.c
│ ├──lvl3
│ └──lvl5
```
Finally, common code for a module must be organized as follows:
- Header files that can be included by other modules are placed in the `include`
directory. As mentionde before, these files must contain extensive
doxygen-formatted documentation describing the functions declared there.
- Source files and their private internal header files are placed in the
`<module_name>x` directory.
- Source files of unit tests and their private internal header files are placed
in the `<module_name>x/test` directory. Again, refer to [Tests](#Tests) for
instructions on how to write these.
The example below shows the detailed organization of the common code of a
module:
```
<module_name>
├── include
│ └── header.h
├── <module_name>x
│ ├── test
│ │ ├── test_internal_header.h
│ │ │ ...
│ │ ├── test1.c
│ │ └── test2.c
│ ├── internal_header.h
│ ├── source1.c
│ └── source2.c
├── opt
└── ref
```
## Tests
@@ -113,27 +203,28 @@ to ensure consistency across the modules.
### Unit tests
These go into `src/<module_name>/<ref|opt|...>/<generic|lvl1|...>/test/`.
Refer to ... for an example of how to write tests.
These go in the `src/<module_name>/<module_name>x/test` and
`src/<module_name>/<ref|opt|...>/<generic|lvlx|lvl1|...>/test/` directories.
Refer to [`src/gf/gfx/test/test_fp.c`](src/gf/gfx/test/test_fp.c) for an example
of how to write tests.
### Integration tests
These go into `test/`. Refer to
These go in the `test/` directory. Refer to
[`test/test_sqisign.c`](test/test_sqisign.c) for an example.
### Known Answer Tests (KAT)
KATs help validate consistency across implementations. By ensuring
that, e.g., the optimized and reference implementation produce the
same signatures.
See [Known Answer Tests in README.md](README.md#Known Answer Tests (KAT)).
same signatures. KATs are generated by executing `PQCgenKAT_sign_<level>` in
the `apps` directory. KAT tests go in the `test/` directory.
## Benchmarks
Benchmarks for a module go into
`src/<module_name>/<ref|opt|...>/<generic|lvl1|...>/bench/`. Global
benchmarks go...
Benchmarks for a module go in the same directories as for tests.
Global benchmarks go in the `apps` directory; e.g.,
[`apps/benchmark.c`](apps/benchmark.c).
## Documentation
@@ -145,9 +236,9 @@ All code should be extensively documented. The public module headers
CI automatically builds a PDF of the doc every time code is pushed.
To download the PDF, go to
[Actions](https://github.com/SQIsign/sqisign-nist/actions), click on
[Actions](https://github.com/SQIsign/sqisign-nist2/actions), click on
the workflow run you're interested in, then go to Artifacts -> docs
(see figure).
(see figure). PDFs are retained for 2 days.
![](https://user-images.githubusercontent.com/149199/231756751-0f2780f8-33fe-4db9-8800-b5f145423b65.png)
@@ -155,11 +246,11 @@ the workflow run you're interested in, then go to Artifacts -> docs
Always work on topic branches, never push work in progress on the
`main` branch. Once a task / issue / work unit is completed, create a
pull-request and ask your team leader for a review.
pull-request and ask for at least one review.
## Coding style
- **C version**: All code must compile cleanly as *C99*, without
- **C version**: All code must compile cleanly as *C11*, without
emitting any warnings, using recent versions of GCC and clang.
- **Names**: Externally visible functions and types should be prefixed
@@ -187,7 +278,23 @@ pull-request and ask your team leader for a review.
Global *state* (modifiable global variables), on the other hand, is
strictly forbidden.
- **Whitespace**: Try not to mix tabs and spaces. Line endings
should be UNIX-style (i.e., `\n` rather than `\r\n`). Whitespace
characters at the end of a line, or by themselves on an otherwise
empty line, are to be avoided.
- **Formatting**: This project uses
[`clang-format`](https://clang.llvm.org/docs/ClangFormat.html) to
format the code. From the root of the project run the following
command:
```
find ./src -path ./src/precomp -prune -type f -o -iname '*.h' -o -iname '*.c' | xargs clang-format -i
```
to automatically format all appropriate files with `clang-format`.
If you want, you can install a [pre-commit
hook](https://pre-commit.com/) to ensure that your work is correctly
formatted before pushing
```
pre-commit install
```
Will use the `.pre-commit-config.yaml` file.