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:
committed by
Lorenz Panny
parent
ff34a8cd18
commit
91e9e464fe
313
DEVELOPERS.md
313
DEVELOPERS.md
@@ -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.
|
||||
|
||||

|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user