feat: initial migration out of exp

This commit is contained in:
Artemis 2023-06-02 00:11:31 +00:00
commit 06c945c4aa
Signed by: artemismirai
GPG Key ID: 7D04E4915F2181D8
58 changed files with 10402 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
/.cargo
/pubkeys.ron
/ownedkeys.ron
/scripts/dist
/Cargo.lock
/target
/dilithium/target
/dilithium/Cargo.lock
/dilithium/.github

25
Cargo.toml Normal file
View File

@ -0,0 +1,25 @@
[package]
name = "kdt"
version = "0.1.0-alpha"
edition = "2021"
[dependencies]
rand = "0.8.5"
pqc_kyber = { git = "https://github.com/Argyle-Software/kyber.git", features = ["std", "kyber1024"] }
# normal dilithium lib with a patch to create `Keypair`s from their values
pqc_dilithium = { path = "./dilithium/" }
base64 = "0.21.2"
aes-gcm = "0.10.2"
generic-array = "0.14.7"
clap = { version = "4.3.0", features = ["derive"] }
sha2 = "0.10.6"
colored = "2.0.0"
ron = "0.8.0"
serde = { version = "1.0.163", features = ["derive"] }
[profile.release]
strip = true
opt-level = 3
codegen-units = 1
lto = true
panic = "abort"

21
README.md Normal file
View File

@ -0,0 +1,21 @@
# KDT
Mirai's experimental, post-quantum successor to GPG 🔒
---
KDT is the experimental Kyber-Dilithium Toolset - like GPG, but post-quantum! It's not ready for production use just yet, but our aim is that it'll help usher in a "new era" of communications safety, and one that's not vulnerable to quantum attacks.
## To-dos
- [x] Store keyset and private keys in local files
- [x] Asymmetric encryption and decryption (CRYSTALS-Kyber-backed 256 bit AES)
- [ ] Wrap encryption with RSA to undoubtedly achieve the verified cryptographic strength of RSA (because CRYSTALS-Kyber's security hasn't been completely verified)
- [ ] Signing and signature verification (CRYSTALS-Dilithium)
- [ ] Improve user friendliness
---
Developed with <3 by [Mirai](https://git.disroot.org/mirai).
![Mirai organisation logo](https://git.disroot.org/avatars/8c879ce5f27a376a3f79b11a4e59c9fcb622d43de8a08dde39333aecf673ac9c)
みらい • ˶ᵔ ᵕ ᵔ˶ • Mirai

12
dilithium/CHANGELOG.md Normal file
View File

@ -0,0 +1,12 @@
## 0.1.1 - 2023-2-2
* Refactor: Consolidated symmetric modules
## 0.1.0 - 2023-2-1
Initial release
### Added
- AES mode
- Randomised signing
- CI runners for linux, macOS and Windows on stable and v1.50.0

41
dilithium/Cargo.toml Normal file
View File

@ -0,0 +1,41 @@
[package]
name = "pqc_dilithium"
version = "0.1.1"
authors = ["Mitchell Berry <foss@mitchellberry.com>"]
description = "A post-quantum cryptographic signature scheme based on the hardness of lattice problems over module lattices"
edition = "2018"
categories = ["cryptography"]
keywords = ["signature", "post-quantum", "signing"]
repository = "https://github.com/Argyle-Software/dilithium/"
license = "MIT OR Apache-2.0"
exclude = ["tests/KAT"]
rust-version = "1.50"
[dependencies]
rand = "0.8.5"
[dev-dependencies]
pqc_core = {version = "0.1.0", features = ["load"]}
[target.'cfg(bench)'.dev-dependencies.criterion]
criterion = "0.4.0"
[[bench]]
name = "api"
harness = false
[features]
# By default this library uses mode3, also called Dilithium3
mode2 = []
mode3 = []
mode5 = []
# Enables AES mode which uses AES-256 in counter mode instead of SHAKE
aes = []
# Produces a random signature everytime when signing the same message.
# One may want to consider randomized signatures in situations where the side channel
# attacks exploiting determinism are applicable. Another situation
# where one may want to avoid determinism is when the signer does not wish to reveal the
# message that is being signed.
random_signing = []

201
dilithium/LICENSE-APACHE Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

21
dilithium/LICENSE-MIT Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Mitchell Berry
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

143
dilithium/README.md Normal file
View File

@ -0,0 +1,143 @@
<p align="center">
<img src="./dilithium.png"/>
</p>
# Dilithium
[![Build Status](https://github.com/Argyle-Software/dilithium/actions/workflows/kat.yml/badge.svg)](https://github.com/Argyle-Software/dilithium/actions)
[![Crates](https://img.shields.io/crates/v/pqc-dilithium)](https://crates.io/crates/pqc-dilithium)
[![License](https://img.shields.io/crates/l/pqc_dilithium)](https://github.com/Argyle-Software/dilithium/blob/master/LICENSE-MIT)
[![License](https://img.shields.io/crates/l/pqc_dilithium)](https://github.com/Argyle-Software/dilithium/blob/master/LICENSE-APACHE)
A rust implementation of the Dilithium, a KEM standardised by the NIST Post-Quantum Standardization Project - fork with `KeyPair` restoration support.
See the [**features**](#features) section for different options regarding security levels and modes of operation. The default security setting is Dilithium3.
It is recommended to use Dilithium in a hybrid system alongside a traditional signature algorithm such as ed25519.
**Minimum Supported Rust Version: 1.50.0**
---
## Installation
```shell
cargo add pqc_dilithium
```
## Usage
```rust
use pqc_dilithium::*;
```
### Key Generation
```rust
let keys = Keypair::generate();
assert!(keys.public.len() == PUBLICKEYBYTES);
assert!(keys.expose_secret().len() == SECRETKEYBYTES);
```
### Signing
```rust
let msg = "Hello".as_bytes();
let sig = keys.sign(&msg);
assert!(sig.len() == SIGNBYTES);
```
### Verification
```rust
let sig_verify = verify(&sig, &msg, &keys.public);
assert!(sig_verify.is_ok());
```
---
## AES mode
Dilithium-AES, that uses AES-256 in counter mode instead of SHAKE to
expand the matrix and the masking vectors, and to sample the secret polynomials.
This offers hardware speedups on certain platforms.
---
## Randomized signing
One may want to consider randomized signatures in situations where the side channel
attacks of [SBB+18, PSS+18] exploiting determinism are applicable. Another situation
where one may want to avoid determinism is when the signer does not wish to reveal the
message that is being signed. While there is no timing leakage of the secret key, there is
timing leakage of the message if the scheme is deterministic. Since the randomness of the
scheme is derived from the message, the number of aborts for a particular message will
always be the same.
---
## Features
By default this library uses Dilithium3
| Name | Description |
|----------------|-------------------------------------------------------------------------------------------------------------------|
| mode2 | Uses Dilithium2 |
| mode5 | Uses Dilithium5 |
| aes | Uses AES256-CTR instead of SHAKE |
| random_signing | Enables randomized signing of messages |
---
## Testing
To run the known answer tests, you'll need to enable the `dilithium_kat` in `RUSTFLAGS` eg.
```shell
RUSTFLAGS="--cfg dilithium_kat" cargo test
```
To run through all possible features use the [`test_matrix.sh`](./tests/test_matrix.sh) script.
---
# Benchmarking
This library uses the criterion benchmarking suite. To use you must enable
`bench` eg.
```shell
RUSTFLAGS="--cfg bench" cargo bench
```
---
## Alternatives
The PQClean project has rust bindings for their C post quantum libraries.
https://github.com/rustpq/pqcrypto/tree/main/pqcrypto-dilithium
---
## About
Dilithium is a digital signature scheme that is strongly secure under chosen message attacks based on the hardness of lattice problems over module lattices. The security notion means that an adversary having access to a signing oracle cannot produce a signature of a message whose signature he hasn't yet seen, nor produce a different signature of a message that he already saw signed. Dilithium has been standardised by the [NIST post-quantum cryptography project](https://csrc.nist.gov/Projects/post-quantum-cryptography/selected-algorithms-2022).
The official website: https://pq-crystals.org/dilithium/
Authors of the Dilithium Algorithm:
* Roberto Avanzi, ARM Limited (DE)
* Joppe Bos, NXP Semiconductors (BE)
* Léo Ducas, CWI Amsterdam (NL)
* Eike Kiltz, Ruhr University Bochum (DE)
* Tancrède Lepoint, SRI International (US)
* Vadim Lyubashevsky, IBM Research Zurich (CH)
* John M. Schanck, University of Waterloo (CA)
* Peter Schwabe, Radboud University (NL)
* Gregor Seiler, IBM Research Zurich (CH)
* Damien Stehle, ENS Lyon (FR)
---
## Contributing
Contributions welcome. For pull requests create a feature fork, by submitting PR's you agree for the code to be dual licensed under MIT/Apache 2.0

View File

@ -0,0 +1,8 @@
# Benchmarking
This library uses the criterion benchmarking suite. To use you must enable
`bench` in `RUSTFLAGS` eg.
```shell
RUSTFLAGS="--cfg bench" cargo bench
```

22
dilithium/benches/api.rs Normal file
View File

@ -0,0 +1,22 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use pqc_dilithium::*;
fn sign_small_msg(c: &mut Criterion) {
let keys = Keypair::generate();
let msg = "Hello".as_bytes();
c.bench_function("Sign Small Message", |b| {
b.iter(|| keys.sign(black_box(msg)))
});
}
fn verify_small_msg(c: &mut Criterion) {
let keys = Keypair::generate();
let msg = "Hello".as_bytes();
let sig = keys.sign(msg);
c.bench_function("Verify Small Message", |b| {
b.iter(|| verify(black_box(sig), black_box(msg), black_box(&keys.public)))
});
}
criterion_group!(benches, sign_small_msg, verify_small_msg);
criterion_main!(benches);

BIN
dilithium/dilithium.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

2
dilithium/rustfmt.toml Normal file
View File

@ -0,0 +1,2 @@
tab_spaces = 2
max_width = 80

502
dilithium/src/aes256ctr.rs Normal file
View File

@ -0,0 +1,502 @@
pub const AES256CTR_BLOCKBYTES: usize = 64;
pub struct Aes256ctrCtx {
pub sk_exp: [u64; 120],
pub ivw: [u32; 16],
}
impl Default for Aes256ctrCtx {
fn default() -> Self {
Self {
sk_exp: [0u64; 120],
ivw: [0u32; 16],
}
}
}
fn br_dec32le(src: &[u8]) -> u32 {
src[0] as u32
| (src[1] as u32) << 8
| (src[2] as u32) << 16
| (src[3] as u32) << 24
}
fn br_range_dec32le(v: &mut [u32], mut num: usize, src: &[u8]) {
let mut v_idx: usize = 0;
let mut src_idx: usize = 0;
while num > 0 {
num -= 1;
v[v_idx] = br_dec32le(&src[src_idx..]);
v_idx += 1;
src_idx += 4;
}
}
fn br_swap32(mut x: u32) -> u32 {
x = ((x & 0x00FF00FFu32) << 8) | ((x >> 8) & 0x00FF00FFu32);
(x << 16) | (x >> 16)
}
fn br_enc32le(dst: &mut [u8], x: u32) {
dst[0] = x as u8;
dst[1] = (x >> 8) as u8;
dst[2] = (x >> 16) as u8;
dst[3] = (x >> 24) as u8;
}
fn br_range_enc32le(dst: &mut [u8], v: &[u32], mut num: usize) {
let mut v_idx = 0;
let mut dst_idx = 0;
while num > 0 {
br_enc32le(&mut dst[dst_idx..], v[v_idx]);
v_idx += 1;
dst_idx += 4;
num -= 1;
}
}
fn br_aes_ct64_bitslice_sbox(q: &mut [u64]) {
// This S-box implementation is a straightforward translation of
// the circuit described by Boyar and Peralta in "A new
// combinational logic minimization technique with applications
// to cryptology" (https://eprint.iacr.org/2009/191.pdf).
// Note that variables x(input) and s(output) are numbered
// in "reverse" order (x0 is the high bit, x7 is the low bit).
let x0 = q[7];
let x1 = q[6];
let x2 = q[5];
let x3 = q[4];
let x4 = q[3];
let x5 = q[2];
let x6 = q[1];
let x7 = q[0];
// Top linear transformation.
let y14 = x3 ^ x5;
let y13 = x0 ^ x6;
let y9 = x0 ^ x3;
let y8 = x0 ^ x5;
let t0 = x1 ^ x2;
let y1 = t0 ^ x7;
let y4 = y1 ^ x3;
let y12 = y13 ^ y14;
let y2 = y1 ^ x0;
let y5 = y1 ^ x6;
let y3 = y5 ^ y8;
let t1 = x4 ^ y12;
let y15 = t1 ^ x5;
let y20 = t1 ^ x1;
let y6 = y15 ^ x7;
let y10 = y15 ^ t0;
let y11 = y20 ^ y9;
let y7 = x7 ^ y11;
let y17 = y10 ^ y11;
let y19 = y10 ^ y8;
let y16 = t0 ^ y11;
let y21 = y13 ^ y16;
let y18 = x0 ^ y16;
// Non-linear section.
let t2 = y12 & y15;
let t3 = y3 & y6;
let t4 = t3 ^ t2;
let t5 = y4 & x7;
let t6 = t5 ^ t2;
let t7 = y13 & y16;
let t8 = y5 & y1;
let t9 = t8 ^ t7;
let t10 = y2 & y7;
let t11 = t10 ^ t7;
let t12 = y9 & y11;
let t13 = y14 & y17;
let t14 = t13 ^ t12;
let t15 = y8 & y10;
let t16 = t15 ^ t12;
let t17 = t4 ^ t14;
let t18 = t6 ^ t16;
let t19 = t9 ^ t14;
let t20 = t11 ^ t16;
let t21 = t17 ^ y20;
let t22 = t18 ^ y19;
let t23 = t19 ^ y21;
let t24 = t20 ^ y18;
let t25 = t21 ^ t22;
let t26 = t21 & t23;
let t27 = t24 ^ t26;
let t28 = t25 & t27;
let t29 = t28 ^ t22;
let t30 = t23 ^ t24;
let t31 = t22 ^ t26;
let t32 = t31 & t30;
let t33 = t32 ^ t24;
let t34 = t23 ^ t33;
let t35 = t27 ^ t33;
let t36 = t24 & t35;
let t37 = t36 ^ t34;
let t38 = t27 ^ t36;
let t39 = t29 & t38;
let t40 = t25 ^ t39;
let t41 = t40 ^ t37;
let t42 = t29 ^ t33;
let t43 = t29 ^ t40;
let t44 = t33 ^ t37;
let t45 = t42 ^ t41;
let z0 = t44 & y15;
let z1 = t37 & y6;
let z2 = t33 & x7;
let z3 = t43 & y16;
let z4 = t40 & y1;
let z5 = t29 & y7;
let z6 = t42 & y11;
let z7 = t45 & y17;
let z8 = t41 & y10;
let z9 = t44 & y12;
let z10 = t37 & y3;
let z11 = t33 & y4;
let z12 = t43 & y13;
let z13 = t40 & y5;
let z14 = t29 & y2;
let z15 = t42 & y9;
let z16 = t45 & y14;
let z17 = t41 & y8;
// Bottom linear transformation.
let t46 = z15 ^ z16;
let t47 = z10 ^ z11;
let t48 = z5 ^ z13;
let t49 = z9 ^ z10;
let t50 = z2 ^ z12;
let t51 = z2 ^ z5;
let t52 = z7 ^ z8;
let t53 = z0 ^ z3;
let t54 = z6 ^ z7;
let t55 = z16 ^ z17;
let t56 = z12 ^ t48;
let t57 = t50 ^ t53;
let t58 = z4 ^ t46;
let t59 = z3 ^ t54;
let t60 = t46 ^ t57;
let t61 = z14 ^ t57;
let t62 = t52 ^ t58;
let t63 = t49 ^ t58;
let t64 = z4 ^ t59;
let t65 = t61 ^ t62;
let t66 = z1 ^ t63;
let s0 = t59 ^ t63;
let s6 = t56 ^ !t62;
let s7 = t48 ^ !t60;
let t67 = t64 ^ t65;
let s3 = t53 ^ t66;
let s4 = t51 ^ t66;
let s5 = t47 ^ t65;
let s1 = t64 ^ !s3;
let s2 = t55 ^ !t67;
q[7] = s0;
q[6] = s1;
q[5] = s2;
q[4] = s3;
q[3] = s4;
q[2] = s5;
q[1] = s6;
q[0] = s7;
}
fn swapn(cl: u64, ch: u64, s: usize, x: u64, y: &mut u64) -> u64 {
let a = x;
let b = *y;
*y = ((a & ch) >> (s)) | (b & ch); // update y
(a & cl) | ((b & cl) << s) // return x
}
fn swap2(x: u64, y: &mut u64) -> u64 {
swapn(0x5555555555555555u64, 0xAAAAAAAAAAAAAAAAu64, 1, x, y)
}
fn swap4(x: u64, y: &mut u64) -> u64 {
swapn(0x3333333333333333u64, 0xCCCCCCCCCCCCCCCCu64, 2, x, y)
}
fn swap8(x: u64, y: &mut u64) -> u64 {
swapn(0x0F0F0F0F0F0F0F0Fu64, 0xF0F0F0F0F0F0F0F0u64, 4, x, y)
}
fn br_aes_ct64_ortho(q: &mut [u64]) {
q[0] = swap2(q[0], &mut q[1]);
q[2] = swap2(q[2], &mut q[3]);
q[4] = swap2(q[4], &mut q[5]);
q[6] = swap2(q[6], &mut q[7]);
q[0] = swap4(q[0], &mut q[2]);
q[1] = swap4(q[1], &mut q[3]);
q[4] = swap4(q[4], &mut q[6]);
q[5] = swap4(q[5], &mut q[7]);
q[0] = swap8(q[0], &mut q[4]);
q[1] = swap8(q[1], &mut q[5]);
q[2] = swap8(q[2], &mut q[6]);
q[3] = swap8(q[3], &mut q[7]);
}
fn br_aes_ct64_interleave_in(q0: &mut u64, q1: &mut u64, w: &[u32]) {
let mut x0 = w[0] as u64;
let mut x1 = w[1] as u64;
let mut x2 = w[2] as u64;
let mut x3 = w[3] as u64;
x0 |= x0 << 16;
x1 |= x1 << 16;
x2 |= x2 << 16;
x3 |= x3 << 16;
x0 &= 0x0000FFFF0000FFFFu64;
x1 &= 0x0000FFFF0000FFFFu64;
x2 &= 0x0000FFFF0000FFFFu64;
x3 &= 0x0000FFFF0000FFFFu64;
x0 |= x0 << 8;
x1 |= x1 << 8;
x2 |= x2 << 8;
x3 |= x3 << 8;
x0 &= 0x00FF00FF00FF00FFu64;
x1 &= 0x00FF00FF00FF00FFu64;
x2 &= 0x00FF00FF00FF00FFu64;
x3 &= 0x00FF00FF00FF00FFu64;
*q0 = x0 | (x2 << 8);
*q1 = x1 | (x3 << 8);
}
fn br_aes_ct64_interleave_out(w: &mut [u32], q0: u64, q1: u64) {
let mut x0 = q0 & 0x00FF00FF00FF00FFu64;
let mut x1 = q1 & 0x00FF00FF00FF00FFu64;
let mut x2 = (q0 >> 8) & 0x00FF00FF00FF00FFu64;
let mut x3 = (q1 >> 8) & 0x00FF00FF00FF00FFu64;
x0 |= x0 >> 8;
x1 |= x1 >> 8;
x2 |= x2 >> 8;
x3 |= x3 >> 8;
x0 &= 0x0000FFFF0000FFFFu64;
x1 &= 0x0000FFFF0000FFFFu64;
x2 &= 0x0000FFFF0000FFFFu64;
x3 &= 0x0000FFFF0000FFFFu64;
w[0] = x0 as u32 | (x0 >> 16) as u32;
w[1] = x1 as u32 | (x1 >> 16) as u32;
w[2] = x2 as u32 | (x2 >> 16) as u32;
w[3] = x3 as u32 | (x3 >> 16) as u32;
}
fn sub_word(x: u32) -> u32 {
let mut q = [0u64; 8];
q[0] = x as u64;
br_aes_ct64_ortho(&mut q);
br_aes_ct64_bitslice_sbox(&mut q);
br_aes_ct64_ortho(&mut q);
q[0] as u32
}
const RCON: [u32; 10] =
[0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36];
fn br_aes_ct64_keysched(comp_skey: &mut [u64], key: &[u8]) {
let (mut j, mut k) = (0usize, 0usize);
let mut skey = [0u32; 60];
let key_len = 32usize;
let nk = key_len >> 2;
let nkf = (14 + 1) << 2;
br_range_dec32le(&mut skey, (key_len >> 2) as usize, key);
let mut tmp = skey[(key_len >> 2) - 1];
for i in nk..nkf {
if j == 0 {
tmp = (tmp << 24) | (tmp >> 8);
tmp = sub_word(tmp) ^ RCON[k];
} else if nk > 6 && j == 4 {
tmp = sub_word(tmp);
}
tmp ^= skey[i - nk];
skey[i] = tmp;
j += 1;
if j == nk {
j = 0;
k += 1;
}
}
j = 0;
for idx in (0..nkf).step_by(4) {
let mut q = [0u64; 8];
let (q0, q1) = q.split_at_mut(4);
br_aes_ct64_interleave_in(&mut q0[0], &mut q1[0], &skey[idx..]);
q[1] = q[0];
q[2] = q[0];
q[3] = q[0];
q[5] = q[4];
q[6] = q[4];
q[7] = q[4];
br_aes_ct64_ortho(&mut q);
comp_skey[j] = (q[0] & 0x1111111111111111)
| (q[1] & 0x2222222222222222)
| (q[2] & 0x4444444444444444)
| (q[3] & 0x8888888888888888);
comp_skey[j + 1] = (q[4] & 0x1111111111111111)
| (q[5] & 0x2222222222222222)
| (q[6] & 0x4444444444444444)
| (q[7] & 0x8888888888888888);
j += 2;
}
}
fn br_aes_ct64_skey_expand(skey: &mut [u64], comp_skey: &[u64]) {
const N: usize = 15 << 1;
let mut u = 0;
let mut v = 0;
let mut x0: u64;
let mut x1: u64;
let mut x2: u64;
let mut x3: u64;
while u < N {
x0 = comp_skey[u];
x1 = comp_skey[u];
x2 = comp_skey[u];
x3 = comp_skey[u];
x0 &= 0x1111111111111111;
x1 &= 0x2222222222222222;
x2 &= 0x4444444444444444;
x3 &= 0x8888888888888888;
x1 >>= 1;
x2 >>= 2;
x3 >>= 3;
skey[v] = (x0 << 4).wrapping_sub(x0);
skey[v + 1] = (x1 << 4).wrapping_sub(x1);
skey[v + 2] = (x2 << 4).wrapping_sub(x2);
skey[v + 3] = (x3 << 4).wrapping_sub(x3);
v += 4;
u += 1;
}
}
fn add_round_key(q: &mut [u64], sk: &[u64]) {
q[0] ^= sk[0];
q[1] ^= sk[1];
q[2] ^= sk[2];
q[3] ^= sk[3];
q[4] ^= sk[4];
q[5] ^= sk[5];
q[6] ^= sk[6];
q[7] ^= sk[7];
}
fn shift_rows(q: &mut [u64]) {
for x in q.iter_mut() {
*x = (*x & 0x000000000000FFFF)
| ((*x & 0x00000000FFF00000) >> 4)
| ((*x & 0x00000000000F0000) << 12)
| ((*x & 0x0000FF0000000000) >> 8)
| ((*x & 0x000000FF00000000) << 8)
| ((*x & 0xF000000000000000) >> 12)
| ((*x & 0x0FFF000000000000) << 4)
}
}
fn rotr32(x: u64) -> u64 {
(x << 32) | (x >> 32)
}
fn mix_columns(q: &mut [u64]) {
let q0 = q[0];
let q1 = q[1];
let q2 = q[2];
let q3 = q[3];
let q4 = q[4];
let q5 = q[5];
let q6 = q[6];
let q7 = q[7];
let r0 = (q0 >> 16) | (q0 << 48);
let r1 = (q1 >> 16) | (q1 << 48);
let r2 = (q2 >> 16) | (q2 << 48);
let r3 = (q3 >> 16) | (q3 << 48);
let r4 = (q4 >> 16) | (q4 << 48);
let r5 = (q5 >> 16) | (q5 << 48);
let r6 = (q6 >> 16) | (q6 << 48);
let r7 = (q7 >> 16) | (q7 << 48);
q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0);
q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1);
q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2);
q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3);
q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4);
q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5);
q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6);
q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7);
}
fn inc4_be(x: u32) -> u32 {
let t = br_swap32(x) + 4;
br_swap32(t)
}
fn aes_ctr4x(out: &mut [u8], ivw: &mut [u32], sk_exp: &[u64]) {
let mut w = [0u32; 16];
w.copy_from_slice(&ivw);
let mut q = [0u64; 8];
let (q0, q1) = q.split_at_mut(4);
for i in 0..4 {
br_aes_ct64_interleave_in(&mut q0[i], &mut q1[i], &w[(i << 2)..]);
}
br_aes_ct64_ortho(&mut q);
add_round_key(&mut q, sk_exp);
for i in 1..14 {
br_aes_ct64_bitslice_sbox(&mut q);
shift_rows(&mut q);
mix_columns(&mut q);
add_round_key(&mut q, &sk_exp[(i << 3)..]);
}
br_aes_ct64_bitslice_sbox(&mut q);
shift_rows(&mut q);
add_round_key(&mut q, &sk_exp[112..]);
br_aes_ct64_ortho(&mut q);
for i in 0..4 {
br_aes_ct64_interleave_out(&mut w[(i << 2)..], q[i], q[i + 4]);
}
br_range_enc32le(out, &w, 16);
// Increase counter for next 4 blocks
ivw[3] = inc4_be(ivw[3]);
ivw[7] = inc4_be(ivw[7]);
ivw[11] = inc4_be(ivw[11]);
ivw[15] = inc4_be(ivw[15]);
}
fn br_aes_ct64_ctr_init(sk_exp: &mut [u64], key: &[u8]) {
let mut skey = [0u64; 30];
br_aes_ct64_keysched(&mut skey, key);
br_aes_ct64_skey_expand(sk_exp, &skey);
}
pub fn aes256ctr_init(s: &mut Aes256ctrCtx, key: &[u8], nonce: [u8; 12]) {
br_aes_ct64_ctr_init(&mut s.sk_exp, &key);
br_range_dec32le(&mut s.ivw, 3, &nonce);
let mut slice = [0u32; 3];
slice.copy_from_slice(&s.ivw[..3]);
s.ivw[4..7].copy_from_slice(&slice);
s.ivw[8..11].copy_from_slice(&slice);
s.ivw[12..15].copy_from_slice(&slice);
s.ivw[3] = br_swap32(0);
s.ivw[7] = br_swap32(1);
s.ivw[11] = br_swap32(2);
s.ivw[15] = br_swap32(3);
}
pub fn aes256ctr_squeezeblocks(
out: &mut [u8],
mut nblocks: u64,
s: &mut Aes256ctrCtx,
) {
let mut idx = 0;
while nblocks > 0 {
aes_ctr4x(&mut out[idx..], &mut s.ivw, &s.sk_exp);
idx += 64;
nblocks -= 1;
}
}

96
dilithium/src/api.rs Normal file
View File

@ -0,0 +1,96 @@
use crate::params::{PUBLICKEYBYTES, SECRETKEYBYTES, SIGNBYTES};
use crate::sign::*;
use std::convert::TryInto;
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct Keypair {
pub public: [u8; PUBLICKEYBYTES],
secret: [u8; SECRETKEYBYTES],
}
/// Secret key elided
impl std::fmt::Debug for Keypair {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "public: {:?}\nsecret: <elided>", self.public)
}
}
pub enum SignError {
Input,
Verify,
}
impl Keypair {
/// Explicitly expose secret key
/// ```
/// # use pqc_dilithium::*;
/// let keys = Keypair::generate();
/// let secret_key = keys.expose_secret();
/// assert!(secret_key.len() == SECRETKEYBYTES);
/// ```
pub fn expose_secret(&self) -> &[u8] {
&self.secret
}
/// Generates a keypair for signing and verification
///
/// Example:
/// ```
/// # use pqc_dilithium::*;
/// let keys = Keypair::generate();
/// assert!(keys.public.len() == PUBLICKEYBYTES);
/// assert!(keys.expose_secret().len() == SECRETKEYBYTES);
/// ```
pub fn generate() -> Keypair {
let mut public = [0u8; PUBLICKEYBYTES];
let mut secret = [0u8; SECRETKEYBYTES];
crypto_sign_keypair(&mut public, &mut secret, None);
Keypair { public, secret }
}
/// Generates a signature for the given message using a keypair
///
/// Example:
/// ```
/// # use pqc_dilithium::*;
/// # let keys = Keypair::generate();
/// let msg = "Hello".as_bytes();
/// let sig = keys.sign(&msg);
/// assert!(sig.len() == SIGNBYTES);
/// ```
pub fn sign(&self, msg: &[u8]) -> [u8; SIGNBYTES] {
let mut sig = [0u8; SIGNBYTES];
crypto_sign_signature(&mut sig, msg, &self.secret);
sig
}
/// Restores a `Keypair` from the specified `public` key and
/// `secret` key.
pub fn restore_from_keys(public: Vec<u8>, secret: Vec<u8>) -> Self {
Self {
public: public.try_into().unwrap(),
secret: secret.try_into().unwrap(),
}
}
}
/// Verify signature using keypair
///
/// Example:
/// ```
/// # use pqc_dilithium::*;
/// # let keys = Keypair::generate();
/// # let msg = [0u8; 32];
/// # let sig = keys.sign(&msg);
/// let sig_verify = verify(&sig, &msg, &keys.public);
/// assert!(sig_verify.is_ok());
pub fn verify(
sig: &[u8],
msg: &[u8],
public_key: &[u8],
) -> Result<(), SignError> {
if sig.len() != SIGNBYTES {
return Err(SignError::Input);
}
crypto_sign_verify(&sig, &msg, public_key)
}

525
dilithium/src/fips202.rs Normal file
View File

@ -0,0 +1,525 @@
#![allow(clippy::needless_range_loop)]
#[cfg(not(feature = "aes"))]
pub const SHAKE128_RATE: usize = 168;
pub const SHAKE256_RATE: usize = 136;
const NROUNDS: usize = 24;
#[derive(Copy, Clone)]
pub struct KeccakState {
pub s: [u64; 25],
pub pos: usize,
}
// Replaces init functions
impl Default for KeccakState {
fn default() -> Self {
KeccakState {
s: [0u64; 25],
pos: 0usize,
}
}
}
impl KeccakState {
pub fn init(&mut self) {
self.s.fill(0);
self.pos = 0;
}
}
fn rol(a: u64, offset: u64) -> u64 {
(a << offset) ^ (a >> (64 - offset))
}
/// Load 8 bytes into uint64_t in little-endian order/
pub fn load64(x: &[u8]) -> u64 {
let mut r = 0u64;
for i in 0..8 {
r |= (x[i] as u64) << (8 * i);
}
r
}
/// Store a 64-bit integer to array of 8 bytes in little-endian order
pub fn store64(x: &mut [u8], u: u64) {
for i in 0..8 {
x[i] = (u >> 8 * i) as u8;
}
}
/// Keccak round constants
const KECCAKF_ROUNDCONSTANTS: [u64; NROUNDS] = [
0x0000000000000001u64,
0x0000000000008082u64,
0x800000000000808au64,
0x8000000080008000u64,
0x000000000000808bu64,
0x0000000080000001u64,
0x8000000080008081u64,
0x8000000000008009u64,
0x000000000000008au64,
0x0000000000000088u64,
0x0000000080008009u64,
0x000000008000000au64,
0x000000008000808bu64,
0x800000000000008bu64,
0x8000000000008089u64,
0x8000000000008003u64,
0x8000000000008002u64,
0x8000000000000080u64,
0x000000000000800au64,
0x800000008000000au64,
0x8000000080008081u64,
0x8000000000008080u64,
0x0000000080000001u64,
0x8000000080008008u64,
];
/// The Keccak F1600 Permutation
pub fn keccakf1600_statepermute(state: &mut [u64]) {
//copyFromState(A, state)
let mut aba = state[0];
let mut abe = state[1];
let mut abi = state[2];
let mut abo = state[3];
let mut abu = state[4];
let mut aga = state[5];
let mut age = state[6];
let mut agi = state[7];
let mut ago = state[8];
let mut agu = state[9];
let mut aka = state[10];
let mut ake = state[11];
let mut aki = state[12];
let mut ako = state[13];
let mut aku = state[14];
let mut ama = state[15];
let mut ame = state[16];
let mut ami = state[17];
let mut amo = state[18];
let mut amu = state[19];
let mut asa = state[20];
let mut ase = state[21];
let mut asi = state[22];
let mut aso = state[23];
let mut asu = state[24];
for round in (0..NROUNDS).step_by(2) {
// prepareTheta
let mut bca = aba ^ aga ^ aka ^ ama ^ asa;
let mut bce = abe ^ age ^ ake ^ ame ^ ase;
let mut bci = abi ^ agi ^ aki ^ ami ^ asi;
let mut bco = abo ^ ago ^ ako ^ amo ^ aso;
let mut bcu = abu ^ agu ^ aku ^ amu ^ asu;
//thetaRhoPiChiIotaPrepareTheta(round , A, E)
let mut da = bcu ^ rol(bce, 1);
let mut de = bca ^ rol(bci, 1);
let mut di = bce ^ rol(bco, 1);
let mut d_o = bci ^ rol(bcu, 1);
let mut du = bco ^ rol(bca, 1);
aba ^= da;
bca = aba;
age ^= de;
bce = rol(age, 44);
aki ^= di;
bci = rol(aki, 43);
amo ^= d_o;
bco = rol(amo, 21);
asu ^= du;
bcu = rol(asu, 14);
let mut eba = bca ^ ((!bce) & bci);
eba ^= KECCAKF_ROUNDCONSTANTS[round];
let mut ebe = bce ^ ((!bci) & bco);
let mut ebi = bci ^ ((!bco) & bcu);
let mut ebo = bco ^ ((!bcu) & bca);
let mut ebu = bcu ^ ((!bca) & bce);
abo ^= d_o;
bca = rol(abo, 28);
agu ^= du;
bce = rol(agu, 20);
aka ^= da;
bci = rol(aka, 3);
ame ^= de;
bco = rol(ame, 45);
asi ^= di;
bcu = rol(asi, 61);
let mut ega = bca ^ ((!bce) & bci);
let mut ege = bce ^ ((!bci) & bco);
let mut egi = bci ^ ((!bco) & bcu);
let mut ego = bco ^ ((!bcu) & bca);
let mut egu = bcu ^ ((!bca) & bce);
abe ^= de;
bca = rol(abe, 1);
agi ^= di;
bce = rol(agi, 6);
ako ^= d_o;
bci = rol(ako, 25);
amu ^= du;
bco = rol(amu, 8);
asa ^= da;
bcu = rol(asa, 18);
let mut eka = bca ^ ((!bce) & bci);
let mut eke = bce ^ ((!bci) & bco);
let mut eki = bci ^ ((!bco) & bcu);
let mut eko = bco ^ ((!bcu) & bca);
let mut eku = bcu ^ ((!bca) & bce);
abu ^= du;
bca = rol(abu, 27);
aga ^= da;
bce = rol(aga, 36);
ake ^= de;
bci = rol(ake, 10);
ami ^= di;
bco = rol(ami, 15);
aso ^= d_o;
bcu = rol(aso, 56);
let mut ema = bca ^ ((!bce) & bci);
let mut eme = bce ^ ((!bci) & bco);
let mut emi = bci ^ ((!bco) & bcu);
let mut emo = bco ^ ((!bcu) & bca);
let mut emu = bcu ^ ((!bca) & bce);
abi ^= di;
bca = rol(abi, 62);
ago ^= d_o;
bce = rol(ago, 55);
aku ^= du;
bci = rol(aku, 39);
ama ^= da;
bco = rol(ama, 41);
ase ^= de;
bcu = rol(ase, 2);
let mut esa = bca ^ ((!bce) & bci);
let mut ese = bce ^ ((!bci) & bco);
let mut esi = bci ^ ((!bco) & bcu);
let mut eso = bco ^ ((!bcu) & bca);
let mut esu = bcu ^ ((!bca) & bce);
// prepareTheta
bca = eba ^ ega ^ eka ^ ema ^ esa;
bce = ebe ^ ege ^ eke ^ eme ^ ese;
bci = ebi ^ egi ^ eki ^ emi ^ esi;
bco = ebo ^ ego ^ eko ^ emo ^ eso;
bcu = ebu ^ egu ^ eku ^ emu ^ esu;
//thetaRhoPiChiIotaPrepareTheta(round+1, E, A)
da = bcu ^ rol(bce, 1);
de = bca ^ rol(bci, 1);
di = bce ^ rol(bco, 1);
d_o = bci ^ rol(bcu, 1);
du = bco ^ rol(bca, 1);
eba ^= da;
bca = eba;
ege ^= de;
bce = rol(ege, 44);
eki ^= di;
bci = rol(eki, 43);
emo ^= d_o;
bco = rol(emo, 21);
esu ^= du;
bcu = rol(esu, 14);
aba = bca ^ ((!bce) & bci);
aba ^= KECCAKF_ROUNDCONSTANTS[round + 1];
abe = bce ^ ((!bci) & bco);
abi = bci ^ ((!bco) & bcu);
abo = bco ^ ((!bcu) & bca);
abu = bcu ^ ((!bca) & bce);
ebo ^= d_o;
bca = rol(ebo, 28);
egu ^= du;
bce = rol(egu, 20);
eka ^= da;
bci = rol(eka, 3);
eme ^= de;
bco = rol(eme, 45);
esi ^= di;
bcu = rol(esi, 61);
aga = bca ^ ((!bce) & bci);
age = bce ^ ((!bci) & bco);
agi = bci ^ ((!bco) & bcu);
ago = bco ^ ((!bcu) & bca);
agu = bcu ^ ((!bca) & bce);
ebe ^= de;
bca = rol(ebe, 1);
egi ^= di;
bce = rol(egi, 6);
eko ^= d_o;
bci = rol(eko, 25);
emu ^= du;
bco = rol(emu, 8);
esa ^= da;
bcu = rol(esa, 18);
aka = bca ^ ((!bce) & bci);
ake = bce ^ ((!bci) & bco);
aki = bci ^ ((!bco) & bcu);
ako = bco ^ ((!bcu) & bca);
aku = bcu ^ ((!bca) & bce);
ebu ^= du;
bca = rol(ebu, 27);
ega ^= da;
bce = rol(ega, 36);
eke ^= de;
bci = rol(eke, 10);
emi ^= di;
bco = rol(emi, 15);
eso ^= d_o;
bcu = rol(eso, 56);
ama = bca ^ ((!bce) & bci);
ame = bce ^ ((!bci) & bco);
ami = bci ^ ((!bco) & bcu);
amo = bco ^ ((!bcu) & bca);
amu = bcu ^ ((!bca) & bce);
ebi ^= di;
bca = rol(ebi, 62);
ego ^= d_o;
bce = rol(ego, 55);
eku ^= du;
bci = rol(eku, 39);
ema ^= da;
bco = rol(ema, 41);
ese ^= de;
bcu = rol(ese, 2);
asa = bca ^ ((!bce) & bci);
ase = bce ^ ((!bci) & bco);
asi = bci ^ ((!bco) & bcu);
aso = bco ^ ((!bcu) & bca);
asu = bcu ^ ((!bca) & bce);
}
state[0] = aba;
state[1] = abe;
state[2] = abi;
state[3] = abo;
state[4] = abu;
state[5] = aga;
state[6] = age;
state[7] = agi;
state[8] = ago;
state[9] = agu;
state[10] = aka;
state[11] = ake;
state[12] = aki;
state[13] = ako;
state[14] = aku;
state[15] = ama;
state[16] = ame;
state[17] = ami;
state[18] = amo;
state[19] = amu;
state[20] = asa;
state[21] = ase;
state[22] = asi;
state[23] = aso;
state[24] = asu;
}
/// Absorb step of Keccak; incremental.
fn keccak_absorb(
state: &mut KeccakState,
r: usize,
input: &[u8],
mut inlen: usize,
) {
let mut idx = 0;
let mut pos = state.pos;
while pos + inlen >= r {
for i in pos..r {
state.s[i / 8] ^= (input[idx] as u64) << 8 * (i % 8);
idx += 1;
}
inlen -= r - pos;
keccakf1600_statepermute(&mut state.s);
pos = 0;
}
let mut i = pos;
while i < pos + inlen {
state.s[i / 8] ^= (input[idx] as u64) << 8 * (i % 8);
idx += 1;
i += 1
}
state.pos = i;
}
/// Finalize absorb step.
fn keccak_finalize(s: &mut [u64; 25], pos: usize, r: usize, p: u8) {
s[pos / 8] ^= (p as u64) << 8 * (pos % 8);
s[r / 8 - 1] ^= 1u64 << 63;
}
/// Squeeze step of Keccak. Squeezes arbitratrily many bytes.
/// Modifies the state. Can be called multiple times to keep
/// squeezing, i.e., is incremental.
///
// Returns new position pos in current block
fn keccak_squeeze(
out: &mut [u8],
mut outlen: usize,
s: &mut [u64; 25],
mut pos: usize,
r: usize,
) -> usize {
while outlen != 0 {
if pos == r {
keccakf1600_statepermute(s);
pos = 0;
}
let mut i = pos;
let mut idx = 0;
while i < r && i < pos + outlen {
out[idx] = (s[i / 8] >> 8 * (i % 8)) as u8;
idx += 1;
i += 1;
}
outlen -= i - pos;
pos = i;
}
return pos;
}
/// Absorb step of Keccak;
/// non-incremental, starts by zeroeing the state.
fn keccak_absorb_once(
s: &mut [u64; 25],
r: usize,
input: &[u8],
mut inlen: usize,
p: u8,
) {
s.fill(0);
let mut idx = 0;
while inlen >= r {
for i in 0..r / 8 {
s[i] ^= load64(&input[idx + 8 * i..]);
}
idx += r;
inlen -= r;
keccakf1600_statepermute(s);
}
for i in 0..inlen {
s[i / 8] ^= (input[idx + i] as u64) << 8 * (i % 8);
}
s[inlen / 8] ^= (p as u64) << 8 * (inlen % 8);
s[(r - 1) / 8] ^= 1u64 << 63;
}
/// Squeeze step of Keccak. Squeezes full blocks of r bytes each.
/// Modifies the state. Can be called multiple times to keep
/// squeezing, i.e., is incremental. Assumes zero bytes of current
/// block have already been squeezed.
fn keccak_squeezeblocks(
out: &mut [u8],
mut nblocks: usize,
s: &mut [u64],
r: usize,
) {
let mut idx = 0usize;
while nblocks > 0 {
keccakf1600_statepermute(s);
for i in 0..(r >> 3) {
store64(&mut out[idx + 8 * i..], s[i])
}
idx += r;
nblocks -= 1;
}
}
/// Description: Absorb step of the SHAKE128 XOF; incremental.
#[cfg(not(feature = "aes"))]
pub fn shake128_absorb(state: &mut KeccakState, input: &[u8], inlen: usize) {
keccak_absorb(state, SHAKE128_RATE, input, inlen);
}
/// Finalize absorb step of the SHAKE128 XOF.
#[cfg(not(feature = "aes"))]
pub fn shake128_finalize(state: &mut KeccakState) {
keccak_finalize(&mut state.s, state.pos as usize, SHAKE128_RATE, 0x1F);
state.pos = SHAKE128_RATE;
}
/// Squeeze step of SHAKE128 XOF. Squeezes full blocks of
/// SHAKE128_RATE bytes each. Can be called multiple times
/// to keep squeezing. Assumes new block has not yet been
/// started (state->pos = SHAKE128_RATE).
#[cfg(not(feature = "aes"))]
pub fn shake128_squeezeblocks(
output: &mut [u8],
nblocks: usize,
s: &mut KeccakState,
) {
keccak_squeezeblocks(output, nblocks, &mut s.s, SHAKE128_RATE);
}
/// Absorb step of the SHAKE256 XOF; incremental.
pub fn shake256_absorb(state: &mut KeccakState, input: &[u8], inlen: usize) {
keccak_absorb(state, SHAKE256_RATE, input, inlen);
}
/// Finalize absorb step of the SHAKE256 XOF.*/
pub fn shake256_finalize(state: &mut KeccakState) {
keccak_finalize(&mut state.s, state.pos, SHAKE256_RATE, 0x1F);
state.pos = SHAKE256_RATE;
}
///Squeeze step of SHAKE256 XOF. Squeezes arbitraily many
/// bytes. Can be called multiple times to keep squeezing.
pub fn shake256_squeeze(
out: &mut [u8],
outlen: usize,
state: &mut KeccakState,
) {
state.pos =
keccak_squeeze(out, outlen, &mut state.s, state.pos, SHAKE256_RATE);
}
/// Initialize, absorb into and finalize SHAKE256 XOF; non-incremental.
pub fn shake256_absorb_once(
state: &mut KeccakState,
input: &[u8],
inlen: usize,
) {
keccak_absorb_once(&mut state.s, SHAKE256_RATE, input, inlen, 0x1F);
state.pos = SHAKE256_RATE;
}
/// Squeeze step of SHAKE256 XOF. Squeezes full blocks of
/// SHAKE256_RATE bytes each. Can be called multiple times
/// to keep squeezing. Assumes next block has not yet been
/// started (state.pos = SHAKE256_RATE).
pub fn shake256_squeezeblocks(
out: &mut [u8],
nblocks: usize,
state: &mut KeccakState,
) {
keccak_squeezeblocks(out, nblocks, &mut state.s, SHAKE256_RATE);
}
/// SHAKE256 XOF with non-incremental API
pub fn shake256(
output: &mut [u8],
mut outlen: usize,
input: &[u8],
inlen: usize,
) {
let mut state = KeccakState::default();
shake256_absorb_once(&mut state, input, inlen);
let nblocks = outlen / SHAKE256_RATE;
shake256_squeezeblocks(output, nblocks, &mut state);
outlen -= nblocks * SHAKE256_RATE;
let idx = nblocks * SHAKE256_RATE;
shake256_squeeze(&mut output[idx..], outlen, &mut state);
}

22
dilithium/src/lib.rs Normal file
View File

@ -0,0 +1,22 @@
#[cfg(feature = "aes")]
mod aes256ctr;
mod api;
mod fips202;
mod ntt;
mod packing;
mod params;
mod poly;
mod polyvec;
mod randombytes;
mod reduce;
mod rounding;
mod sign;
mod symmetric;
pub use params::*;
pub use api::*;
#[cfg(dilithium_kat)]
pub use sign::{
crypto_sign_keypair, crypto_sign_signature, crypto_sign_verify,
};

243
dilithium/src/ntt.rs Normal file
View File

@ -0,0 +1,243 @@
use crate::{params::*, reduce::*};
// Roots of unity in order needed by forward ntt
pub const ZETAS: [i32; N] = [
0, 25847, -2608894, -518909, 237124, -777960, -876248, 466468, 1826347,
2353451, -359251, -2091905, 3119733, -2884855, 3111497, 2680103, 2725464,
1024112, -1079900, 3585928, -549488, -1119584, 2619752, -2108549, -2118186,
-3859737, -1399561, -3277672, 1757237, -19422, 4010497, 280005, 2706023,
95776, 3077325, 3530437, -1661693, -3592148, -2537516, 3915439, -3861115,
-3043716, 3574422, -2867647, 3539968, -300467, 2348700, -539299, -1699267,
-1643818, 3505694, -3821735, 3507263, -2140649, -1600420, 3699596, 811944,
531354, 954230, 3881043, 3900724, -2556880, 2071892, -2797779, -3930395,
-1528703, -3677745, -3041255, -1452451, 3475950, 2176455, -1585221, -1257611,
1939314, -4083598, -1000202, -3190144, -3157330, -3632928, 126922, 3412210,
-983419, 2147896, 2715295, -2967645, -3693493, -411027, -2477047, -671102,
-1228525, -22981, -1308169, -381987, 1349076, 1852771, -1430430, -3343383,
264944, 508951, 3097992, 44288, -1100098, 904516, 3958618, -3724342, -8578,
1653064, -3249728, 2389356, -210977, 759969, -1316856, 189548, -3553272,
3159746, -1851402, -2409325, -177440, 1315589, 1341330, 1285669, -1584928,
-812732, -1439742, -3019102, -3881060, -3628969, 3839961, 2091667, 3407706,
2316500, 3817976, -3342478, 2244091, -2446433, -3562462, 266997, 2434439,
-1235728, 3513181, -3520352, -3759364, -1197226, -3193378, 900702, 1859098,
909542, 819034, 495491, -1613174, -43260, -522500, -655327, -3122442,
2031748, 3207046, -3556995, -525098, -768622, -3595838, 342297, 286988,
-2437823, 4108315, 3437287, -3342277, 1735879, 203044, 2842341, 2691481,
-2590150, 1265009, 4055324, 1247620, 2486353, 1595974, -3767016, 1250494,
2635921, -3548272, -2994039, 1869119, 1903435, -1050970, -1333058, 1237275,
-3318210, -1430225, -451100, 1312455, 3306115, -1962642, -1279661, 1917081,
-2546312, -1374803, 1500165, 777191, 2235880, 3406031, -542412, -2831860,
-1671176, -1846953, -2584293, -3724270, 594136, -3776993, -2013608, 2432395,
2454455, -164721, 1957272, 3369112, 185531, -1207385, -3183426, 162844,
1616392, 3014001, 810149, 1652634, -3694233, -1799107, -3038916, 3523897,
3866901, 269760, 2213111, -975884, 1717735, 472078, -426683, 1723600,
-1803090, 1910376, -1667432, -1104333, -260646, -3833893, -2939036, -2235985,
-420899, -2286327, 183443, -976891, 1612842, -3545687, -554416, 3919660,
-48306, -1362209, 3937738, 1400424, -846154, 1976782,
];
/// Name: ntt
//
/// Forward NTT, in-place. No modular reduction is performed after
/// additions or subtractions. Output vector is in bitreversed order.
//
/// Arguments: - uint32_t p[N]: input/output coefficient array
pub fn ntt(a: &mut [i32]) {
let mut j;
let mut k = 0usize;
let mut len = 128;
let (mut t, mut zeta);
while len > 0 {
let mut start = 0;
while start < N {
k += 1;
zeta = ZETAS[k] as i64;
j = start;
while j < (start + len) {
t = montgomery_reduce(zeta * a[j + len] as i64);
a[j + len] = a[j] - t;
a[j] += t;
j += 1;
}
start = j + len;
}
len >>= 1;
}
}
/// Name: invntt
//
/// Inverse NTT and multiplication by Montgomery factor 2^32.
/// In-place. No modular reductions after additions or
/// subtractions; input coefficients need to be smaller than
/// Q in absolute value. Output coefficient are smaller than Q in
/// absolute value.
//
/// Arguments: - uint32_t p[N]: input/output coefficient array
pub fn invntt_tomont(a: &mut [i32]) {
let mut j;
let mut k = 256usize;
let mut len = 1;
let (mut t, mut zeta);
const F: i64 = 41978; // mont^2/256
while len < N {
let mut start = 0;
while start < 256 {
k -= 1;
zeta = -ZETAS[k] as i64;
j = start;
while j < (start + len) {
t = a[j];
a[j] = t + a[j + len];
a[j + len] = t - a[j + len];
a[j + len] = montgomery_reduce(zeta * a[j + len] as i64);
j += 1
}
start = j + len;
}
len <<= 1;
}
for j in 0..N {
a[j] = montgomery_reduce(F * a[j] as i64);
}
}
mod tests {
#[test]
fn ntt() {
let mut a = [
-1, 1, -4, -3, -4, 4, 1, 1, 2, 4, 1, 2, -2, 3, 1, 0, -3, 1, -1, -2, 4,
-4, -1, -3, -4, -3, 3, -3, -1, 0, 0, 2, 3, -4, 3, 4, 1, -3, -1, 3, 0, 0,
1, -4, -1, -2, -2, 2, 3, 0, 1, 1, 4, 1, 2, 2, 4, 1, -2, 2, 0, 3, 1, -3,
4, -3, 2, -1, 3, 2, -3, 4, 3, -4, -2, 2, 4, -1, 3, -2, -1, -4, -1, -4,
-3, 4, -1, -3, -2, 0, -4, 3, -4, -1, -1, 4, -4, 0, 1, -1, 4, 1, -4, 3, 4,
-3, -2, -2, -3, 0, 3, -1, 2, -4, -3, -2, 1, -3, -3, -3, 1, 1, 4, -1, 2,
4, 4, -3, 3, 1, -4, 2, -4, -3, 2, -4, -1, 3, 4, -4, 3, -3, 4, 0, -2, 2,
1, -3, 1, 0, 4, 2, 3, -2, 2, -2, 4, -1, 3, -2, 1, -3, 3, -3, 3, 4, 1, 4,
3, 3, -1, 0, -2, 1, 3, -4, 2, -1, 4, 1, 0, 2, -1, -2, 1, 4, -4, 0, 0, 3,
-3, 1, -4, 4, -3, 1, -4, 3, -1, 2, 4, -1, 0, -4, -2, 4, -4, 4, 4, -2, -3,
3, 2, -2, -4, -1, 3, 1, 3, 1, -4, 2, -3, 2, 4, 4, -3, 3, -4, 3, -4, 0,
-2, 3, -3, -2, 3, 1, -4, -3, 3, -2, -3, 4, -3, 1, 1, 2, -1, -1, 0, -3,
-3, 0, -1, 3,
];
let a_output = [
-262661, -8506629, 3427761, 2784265, 3141917, -2372709, -8049215,
-2353801, 3022212, 7731946, 1644566, 3053540, -255898, -2738860, 876613,
9132009, 10276178, 7874020, -11588, 1969958, 12632850, 8341150, 8287818,
3925510, -8773920, -747228, 898118, -2317034, 3897976, -4414032, -834621,
4071501, -1432521, -9685153, -13990476, -13659378, -7289253, -7755363,
3791227, -3833707, -9487410, -8901660, -19000363, -13214259, 544413,
-6844765, -6105586, -11435970, -2978543, -1014713, -5667934, -13166438,
-5875092, -12621756, -9964452, -5673368, -3935130, -4224380, -7354605,
-7906865, -4353982, 1618188, 4762265, 2833189, -310581, -3991895,
-11913640, -5312548, 3570135, -4704135, -3170969, -5592951, 1453785,
-4560687, -1182968, 2521098, -3751247, -4714079, 301131, -2224209,
-5642042, 458282, 5403769, 2707403, 12215228, 5224740, 6413907, 6815913,
4580029, 425581, 1318364, -783726, 7326262, 7568074, 689180, -547668,
-4653694, -11442820, -7832628, -5377838, -11257920, -8298570, -4596359,
-4150699, -3065694, -10682906, -2769659, 937211, 1653867, 3120263,
-1126589, 7211075, -14877134, -18262456, -8293726, -9183292, -10773770,
-6705792, -5315674, 2787012, -2270115, -9841689, -5051871, -7388413,
4234814, 1052190, -2471397, -6463519, 10137330, 5496024, 4753901,
-645043, 7223139, 1246181, 9902026, 8334682, 6254834, 13121436, 5157886,
5524544, 3335176, -1444146, 7151165, 8213713, 5425362, 2999358, 9659970,
4975850, 10923812, 12903596, 3807803, 8025009, -5468017, -1536129,
-2185854, 2751920, 6580366, 4468658, 8981903, 2405433, 8513375, 9863111,
7777890, 11623292, -484225, 2094367, 6670303, -1190953, -113377, 7560821,
-1565727, 512851, 2493128, -2496028, -2851481, -11119827, -3547676,
-6187460, 4743114, -1430434, 8695423, 404053, -5689198, -237334,
-10292594, -2482562, -4377285, -8964791, -8246037, -13793329, -106937,
-5784361, 16421657, 16169825, 17295701, 14750529, 15111685, 13365139,
5665684, 6845556, 3012973, 2469901, -34925, 3554251, 2179597, 9005783,
11178922, 11448090, 4172918, 6892142, -3097350, -2378354, -2508563,
-3588283, -7774392, -7426678, 3013820, 8971556, 11583329, 6634515,
657092, 6937984, -6083813, -1488819, -1315854, 5205578, -7934583,
-2186897, 6308547, 3324877, 10276044, 2595056, 8209625, 2358083, 6005802,
2915382, 1471531, -1624379, -3631962, -4158290, 544917, -6606555, 998696,
584554, 1926740, 844426, 1017867, -3082165, -5819081, -8499759, 521737,
-7231469, -4117257, -5370457, -6235069, 314971,
];
super::ntt(&mut a);
assert_eq!(a, a_output);
}
#[test]
fn invntt_tomont() {
let mut a = [
-410121, -3439227, -1510274, 1543151, -2293562, -1024787, -1768713,
3416108, -1829266, -39421, 1553720, 383193, -1303564, 2601404, 178534,
3075833, -1669488, 2938151, 419856, 3374895, -3659025, 2791925, -2014544,
-4116040, 528058, -460960, -498884, -3726436, 1576721, 1111713, -2404662,
289307, 3590938, 362196, -2812407, -1463477, 1001050, 2798150, -472041,
1298972, -601719, -3341683, -2239048, -3200450, -1250550, -2932037,
-158803, -42310, 1710565, 1768181, 3343337, -4124626, -970397, 2715826,
650012, -2014903, 4155312, -570039, -1014542, -1136867, -2854295,
3545986, 3534334, 1775063, 543277, 1440396, -2274992, -1666074, -2522410,
1374130, 2546411, 2357995, 3246445, -1637839, -3940900, -22882, 3617374,
3695910, 3135497, 3461903, 321845, 1837065, -4102518, 2025912, 4037956,
3262194, -1077158, -104681, 1543795, 2067328, -3128599, 3610959, 1154470,
-1950084, 3161277, -1484341, 3056494, 145395, -2412862, 3811285,
-2651526, 794539, -1077785, -3775793, 1851954, 925186, -1420046,
-2129292, -2572721, -1709299, 2466220, -1148025, -631434, 1616317,
1979838, 1787596, -2046122, 4145612, 2425669, -1207333, -3920849,
2920839, -1437055, 2250361, -2512504, -2660187, -2934830, 3926550,
1895481, -763893, 1348485, 3392907, -2603573, 748841, 3508436, -3904020,
800986, 1434045, 3024446, -2067148, 4058529, -1797892, 2428719, -1661266,
-1174918, -1085872, -437562, 2040521, 3475996, 35877, 3621178, -1269117,
-743638, 442376, -278296, -2265303, 1611044, -1440032, 545695, 3186212,
2126009, -93566, 1381483, -4000405, -1074727, 291811, 3040864, -1459167,
-3809294, -1446926, -2959019, 1110797, -2346583, 1125183, -1458376,
476652, 2246737, -1552594, -321172, -328787, 3727158, 2578512, -637322,
2145686, 124009, 316574, 598758, -2180345, -40300, 1777560, -712912,
3969900, -837615, 4186858, -1992625, 1592425, 4172055, 1030316, 1732649,
3365500, -2209545, -2216165, 1598405, 4122715, 4094540, 363060, 369838,
-1841354, 3772391, 1383675, -1268324, -237400, -3972626, 1314275,
-128037, -810499, -3799034, -1750396, 766339, 1350337, -519324, -3673999,
873780, -2497778, 1159808, 3552922, 900408, 3883840, -4073441, 2974189,
763212, -1033993, -1137934, -3359093, 452287, 2935586, -917124, 608771,
3625009, -2851574, -207975, -101949, -3075118, -1522612, -3819111,
3826184, 2419692, 1871952, -1722545, -710127, 3035522, -3436794,
-2649023, 383072, 2434951, 3052038,
];
let a_output = [
775832, -3236070, 3979117, -477690, -3845958, 3442130, 3288278, 3510572,
-4059961, 398894, -2353757, 2033799, 1142104, -1503727, -1966738,
-646014, -1208934, 2807801, 1786267, 77787, -3034606, -2018590, -4138114,
-119863, -2143281, -2656209, -1161853, -1355718, -3330698, 535482,
3053760, 1831603, 2290608, 2796725, -2609926, -663097, 1464007, 2958053,
-2816324, 2906926, 500159, 943996, 524888, -4163949, -834472, 964151,
2161202, 1983774, -3656476, -3020535, 952201, 2170010, 711358, 1519731,
-3738927, -3756753, 921738, -3392868, 2699399, 3324418, -1453193,
1620721, 449384, -39155, 3109821, 1029974, 142649, 921233, 3727107,
637146, -756794, 34504, -1893151, -603841, 2330154, 1322568, -1191265,
-748922, 1386438, 2435097, 355691, 705695, -504939, 1785625, 3011361,
2599030, -721431, 3258978, 826952, 3134195, 1786978, -2063102, 3719340,
-3791185, 766135, 1412965, 2002641, 344357, -3493197, -3971320, -1589376,
3333787, -886393, -3473781, 2615969, 258578, -1357515, 31929, -2417061,
3279057, 623958, 402549, -3223514, 1434146, 2674659, 4184013, 2350081,
-2255267, 3242948, -2755729, 3141850, -3642170, -410070, -3603228,
509187, 747235, -2733439, 2951878, 934726, -1464030, -3685617, -177281,
-2026915, -2014190, 3892744, -2765709, 178393, 1247779, -328544, -647840,
227976, -1303803, -407239, 2659686, 330785, 22660, 3150838, -49448,
-3247191, 3366409, 3515827, 2681493, -3955013, 957672, 2270951, -2324620,
848541, 252308, 2362816, -480849, 1886753, 1946701, -3448907, 3931255,
-2029213, -3650910, 3939281, 730275, 2214811, 1201755, -694587, -2004861,
2583875, 3279717, 3819665, 2574119, -2112908, -2400101, -1349537,
3495414, 1347624, -2225620, -2050682, -2951590, -3996103, -3607935,
3247458, -2175324, -1553702, -2091666, 1571828, -1878102, 2718529,
-541427, -662267, -1324341, -370258, -474473, 2224715, 2128310, -345001,
1208578, -1376070, 3433670, 3140605, 875985, -1831696, 449356, -220450,
1787974, 2388192, -34248, -3456146, 2415580, 1786863, -2849072, -212492,
59437, -716203, 1968326, 1107129, 434519, 185631, 3036577, -2325373,
567821, 1988023, 2839088, -1233636, -3135158, 4161465, -3730607, 1882010,
-3271303, -2556717, -1387745, -3161242, 1813752, -3561981, 130118,
-2005282, -3396498, 1716289, -3253036, -1302754, 2500212, -1918240,
1201565, -1106518, 743553, 902331, 804710, -3047027, 3654086, 911638,
3708051,
];
super::invntt_tomont(&mut a);
assert_eq!(a, a_output);
}
}

161
dilithium/src/packing.rs Normal file
View File

@ -0,0 +1,161 @@
use crate::{params::*, poly::*, polyvec::*, SignError};
/// Bit-pack public key pk = (rho, t1).
pub fn pack_pk(pk: &mut [u8], rho: &[u8], t1: &Polyveck) {
pk[..SEEDBYTES].copy_from_slice(&rho[..SEEDBYTES]);
for i in 0..K {
polyt1_pack(&mut pk[SEEDBYTES + i * POLYT1_PACKEDBYTES..], &t1.vec[i]);
}
}
/// Unpack public key pk = (rho, t1).
pub fn unpack_pk(rho: &mut [u8], t1: &mut Polyveck, pk: &[u8]) {
rho[..SEEDBYTES].copy_from_slice(&pk[..SEEDBYTES]);
for i in 0..K {
polyt1_unpack(&mut t1.vec[i], &pk[SEEDBYTES + i * POLYT1_PACKEDBYTES..])
}
}
/// Bit-pack secret key sk = (rho, key, tr, s1, s2, t0).
pub fn pack_sk(
sk: &mut [u8],
rho: &[u8],
tr: &[u8],
key: &[u8],
t0: &Polyveck,
s1: &Polyvecl,
s2: &Polyveck,
) {
let mut idx = 0usize;
sk[idx..SEEDBYTES].copy_from_slice(&rho[0..SEEDBYTES]);
idx += SEEDBYTES;
sk[idx..idx + SEEDBYTES].copy_from_slice(&key[0..SEEDBYTES]);
idx += SEEDBYTES;
sk[idx..idx + SEEDBYTES].copy_from_slice(&tr[0..SEEDBYTES]);
idx += SEEDBYTES;
for i in 0..L {
polyeta_pack(&mut sk[idx + i * POLYETA_PACKEDBYTES..], &s1.vec[i]);
}
idx += L * POLYETA_PACKEDBYTES;
for i in 0..K {
polyeta_pack(&mut sk[idx + i * POLYETA_PACKEDBYTES..], &s2.vec[i]);
}
idx += K * POLYETA_PACKEDBYTES;
for i in 0..K {
polyt0_pack(&mut sk[idx + i * POLYT0_PACKEDBYTES..], &t0.vec[i]);
}
}
/// Unpack secret key sk = (rho, key, tr, s1, s2, t0).
pub fn unpack_sk(
rho: &mut [u8],
tr: &mut [u8],
key: &mut [u8],
t0: &mut Polyveck,
s1: &mut Polyvecl,
s2: &mut Polyveck,
sk: &[u8],
) {
let mut idx = 0usize;
rho[..SEEDBYTES].copy_from_slice(&sk[..SEEDBYTES]);
idx += SEEDBYTES;
key[..SEEDBYTES].copy_from_slice(&sk[idx..idx + SEEDBYTES]);
idx += SEEDBYTES;
tr[..SEEDBYTES].copy_from_slice(&sk[idx..idx + SEEDBYTES]);
idx += SEEDBYTES;
for i in 0..L {
polyeta_unpack(&mut s1.vec[i], &sk[idx + i * POLYETA_PACKEDBYTES..]);
}
idx += L * POLYETA_PACKEDBYTES;
for i in 0..K {
polyeta_unpack(&mut s2.vec[i], &sk[idx + i * POLYETA_PACKEDBYTES..]);
}
idx += K * POLYETA_PACKEDBYTES;
for i in 0..K {
polyt0_unpack(&mut t0.vec[i], &sk[idx + i * POLYT0_PACKEDBYTES..]);
}
}
/// Bit-pack signature sig = (c, z, h).
pub fn pack_sig(sig: &mut [u8], c: Option<&[u8]>, z: &Polyvecl, h: &Polyveck) {
let mut idx = 0usize;
if let Some(challenge) = c {
sig[..SEEDBYTES].copy_from_slice(&challenge[..SEEDBYTES]);
}
idx += SEEDBYTES;
for i in 0..L {
polyz_pack(&mut sig[idx + i * POLYZ_PACKEDBYTES..], &z.vec[i]);
}
idx += L * POLYZ_PACKEDBYTES;
// Encode H
sig[idx..idx + OMEGA + K].copy_from_slice(&[0u8; OMEGA + K]);
let mut k = 0;
for i in 0..K {
for j in 0..N {
if h.vec[i].coeffs[j] != 0 {
sig[idx + k] = j as u8;
k += 1;
}
}
sig[idx + OMEGA + i] = k as u8;
}
}
/// Unpack signature sig = (z, h, c).
pub fn unpack_sig(
c: &mut [u8],
z: &mut Polyvecl,
h: &mut Polyveck,
sig: &[u8],
) -> Result<(), SignError> {
let mut idx = 0usize;
c[..SEEDBYTES].copy_from_slice(&sig[..SEEDBYTES]);
idx += SEEDBYTES;
for i in 0..L {
polyz_unpack(&mut z.vec[i], &sig[idx + i * POLYZ_PACKEDBYTES..]);
}
idx += L * POLYZ_PACKEDBYTES;
// Decode h
let mut k = 0usize;
for i in 0..K {
if sig[idx + OMEGA + i] < k as u8 || sig[idx + OMEGA + i] > OMEGA_U8 {
return Err(SignError::Input);
}
for j in k..sig[idx + OMEGA + i] as usize {
// Coefficients are ordered for strong unforgeability
if j > k && sig[idx + j as usize] <= sig[idx + j as usize - 1] {
return Err(SignError::Input);
}
h.vec[i].coeffs[sig[idx + j] as usize] = 1;
}
k = sig[idx + OMEGA + i] as usize;
}
// Extra indices are zero for strong unforgeability
for j in k..OMEGA {
if sig[idx + j as usize] > 0 {
return Err(SignError::Input);
}
}
Ok(())
}

57
dilithium/src/params.rs Normal file
View File

@ -0,0 +1,57 @@
#[cfg(feature = "mode2")]
mod mode_2;
#[cfg(not(any(feature = "mode2", feature = "mode5")))]
mod mode_3;
#[cfg(feature = "mode5")]
mod mode_5;
#[cfg(feature = "mode2")]
pub use mode_2::*;
#[cfg(not(any(feature = "mode2", feature = "mode5")))]
pub use mode_3::*;
#[cfg(feature = "mode5")]
pub use mode_5::*;
pub const SEEDBYTES: usize = 32;
pub const CRHBYTES: usize = 64;
pub const N: usize = 256;
pub const Q: usize = 8380417;
pub const D: usize = 13;
pub const ROOT_OF_UNITY: usize = 1753;
pub const POLYT1_PACKEDBYTES: usize = 320;
pub const POLYT0_PACKEDBYTES: usize = 416;
pub const POLYVECH_PACKEDBYTES: usize = OMEGA + K;
pub const POLYZ_PACKEDBYTES: usize =
if cfg!(feature = "mode2") { 576 } else { 640 };
pub const POLYW1_PACKEDBYTES: usize =
if cfg!(feature = "mode2") { 192 } else { 128 };
pub const POLYETA_PACKEDBYTES: usize =
if cfg!(not(any(feature = "mode2", feature = "mode5"))) {
128
} else {
96
};
// Concise types to avoid cast cluttering
pub const Q_I32: i32 = Q as i32;
pub const N_U32: u32 = N as u32;
pub const L_U16: u16 = L as u16;
pub const BETA_I32: i32 = BETA as i32;
pub const GAMMA1_I32: i32 = GAMMA1 as i32;
pub const GAMMA2_I32: i32 = GAMMA2 as i32;
pub const OMEGA_U8: u8 = OMEGA as u8;
pub const ETA_I32: i32 = ETA as i32;
pub const GAMMA1_SUB_BETA: i32 = (GAMMA1 - BETA) as i32;
pub const PUBLICKEYBYTES: usize = SEEDBYTES + K * POLYT1_PACKEDBYTES;
pub const SECRETKEYBYTES: usize = 3 * SEEDBYTES
+ L * POLYETA_PACKEDBYTES
+ K * POLYETA_PACKEDBYTES
+ K * POLYT0_PACKEDBYTES;
pub const SIGNBYTES: usize =
SEEDBYTES + L * POLYZ_PACKEDBYTES + POLYVECH_PACKEDBYTES;
pub const RANDOMIZED_SIGNING: bool = cfg!(feature = "random_signing");

View File

@ -0,0 +1,10 @@
use super::Q;
pub const K: usize = 4;
pub const L: usize = 4;
pub const ETA: usize = 2;
pub const TAU: usize = 39;
pub const BETA: usize = 78;
pub const GAMMA1: usize = 1 << 17;
pub const GAMMA2: usize = (Q - 1) / 88;
pub const OMEGA: usize = 80;

View File

@ -0,0 +1,10 @@
use super::Q;
pub const K: usize = 6;
pub const L: usize = 5;
pub const ETA: usize = 4;
pub const TAU: usize = 49;
pub const BETA: usize = 196;
pub const GAMMA1: usize = 1 << 19;
pub const GAMMA2: usize = (Q - 1) / 32;
pub const OMEGA: usize = 55;

View File

@ -0,0 +1,10 @@
use super::Q;
pub const K: usize = 8;
pub const L: usize = 7;
pub const ETA: usize = 2;
pub const TAU: usize = 60;
pub const BETA: usize = 120;
pub const GAMMA1: usize = 1 << 19;
pub const GAMMA2: usize = (Q - 1) / 32;
pub const OMEGA: usize = 75;

620
dilithium/src/poly.rs Normal file
View File

@ -0,0 +1,620 @@
use crate::{
fips202::*, ntt::*, params::*, reduce::*, rounding::*, symmetric::*,
};
const D_SHL: i32 = 1i32 << (D - 1);
#[derive(Copy, Clone)]
pub struct Poly {
pub coeffs: [i32; N],
}
impl Default for Poly {
fn default() -> Self {
Poly { coeffs: [0i32; N] }
}
}
/// Inplace reduction of all coefficients of polynomial to
/// representative in [0,2*Q].
pub fn poly_reduce(a: &mut Poly) {
for i in 0..N {
a.coeffs[i] = reduce32(a.coeffs[i]);
}
}
/// For all coefficients of in/out polynomial add Q if
/// coefficient is negative.
pub fn poly_caddq(a: &mut Poly) {
for i in 0..N {
a.coeffs[i] = caddq(a.coeffs[i]);
}
}
/// Add polynomials. No modular reduction is performed.
pub fn poly_add(c: &mut Poly, b: &Poly) {
for i in 0..N {
c.coeffs[i] = c.coeffs[i] + b.coeffs[i];
}
}
/// Subtract polynomials. Assumes coefficients of second input
/// polynomial to be less than 2*Q. No modular reduction is
/// performed.
pub fn poly_sub(c: &mut Poly, b: &Poly) {
for i in 0..N {
c.coeffs[i] = c.coeffs[i] - b.coeffs[i];
}
}
/// Multiply polynomial by 2^D without modular reduction. Assumes
/// input coefficients to be less than 2^{32-D}.
pub fn poly_shiftl(a: &mut Poly) {
for i in 0..N {
a.coeffs[i] <<= D;
}
}
/// Inplace forward NTT. Output coefficients can be up to
/// 16*Q larger than input coefficients.
pub fn poly_ntt(a: &mut Poly) {
ntt(&mut a.coeffs);
}
/// Inplace inverse NTT and multiplication by 2^{32}.
/// Input coefficients need to be less than 2*Q.
/// Output coefficients are less than 2*Q.
pub fn poly_invntt_tomont(a: &mut Poly) {
invntt_tomont(&mut a.coeffs);
}
/// Pointwise multiplication of polynomials in NTT domain
/// representation and multiplication of resulting polynomial
/// by 2^{-32}. Output coefficients are less than 2*Q if input
/// coefficient are less than 22*Q.
pub fn poly_pointwise_montgomery(c: &mut Poly, a: &Poly, b: &Poly) {
for i in 0..N {
c.coeffs[i] = montgomery_reduce((a.coeffs[i] as i64) * b.coeffs[i] as i64);
}
}
/// For all coefficients c of the input polynomial,
/// compute c0, c1 such that c mod Q = c1*2^D + c0
/// with -2^{D-1} < c0 <= 2^{D-1}. Assumes coefficients to be
/// standard representatives.
pub fn poly_power2round(a1: &mut Poly, a0: &mut Poly) {
for i in 0..N {
a1.coeffs[i] = power2round(a1.coeffs[i], &mut a0.coeffs[i]);
}
}
/// For all coefficients c of the input polynomial,
/// compute high and low bits c0, c1 such c mod Q = c1*ALPHA + c0
/// with -ALPHA/2 < c0 <= ALPHA/2 except c1 = (Q-1)/ALPHA where we
/// set c1 = 0 and -ALPHA/2 <= c0 = c mod Q - Q < 0.
/// Assumes coefficients to be standard representatives.
pub fn poly_decompose(a1: &mut Poly, a0: &mut Poly) {
for i in 0..N {
a1.coeffs[i] = decompose(&mut a0.coeffs[i], a1.coeffs[i]);
}
}
/// Compute hint polynomial. The coefficients of which indicate
/// whether the low bits of the corresponding coefficient of
/// the input polynomial overflow into the high bits.
pub fn poly_make_hint(h: &mut Poly, a0: &Poly, a1: &Poly) -> i32 {
let mut s = 0i32;
for i in 0..N {
h.coeffs[i] = make_hint(a0.coeffs[i], a1.coeffs[i]) as i32;
s += h.coeffs[i];
}
s
}
/// Use hint polynomial to correct the high bits of a polynomial.
///
/// Arguments: - poly *b: pointer to output polynomial with corrected high bits
/// - const poly *a: pointer to input polynomial
/// - const poly *h: pointer to input hint polynomial
pub fn poly_use_hint(b: &mut Poly, h: &Poly) {
for i in 0..N {
b.coeffs[i] = use_hint(b.coeffs[i], h.coeffs[i] as u8);
}
}
/// Check infinity norm of polynomial against given bound.
/// Assumes input coefficients to be standard representatives.
/// Returns 0 if norm is strictly smaller than B and 1 otherwise.
pub fn poly_chknorm(a: &Poly, b: i32) -> u8 {
// It is ok to leak which coefficient violates the bound since
// the probability for each coefficient is independent of secret
// data but we must not leak the sign of the centralized representative.
let mut t;
if b > (Q_I32 - 1) / 8 {
return 1;
}
for i in 0..N {
// Absolute value of centralized representative
t = a.coeffs[i] >> 31;
t = a.coeffs[i] - (t & 2 * a.coeffs[i]);
if t >= b {
return 1;
}
}
return 0;
}
/// Sample uniformly random coefficients in [0, Q-1] by
/// performing rejection sampling on array of random bytes.
/// Returns number of sampled coefficients. Can be smaller than len if not enough
/// random bytes were given.
pub fn rej_uniform(a: &mut [i32], len: u32, buf: &[u8], buflen: usize) -> u32 {
let (mut ctr, mut pos) = (0usize, 0usize);
let mut t;
while ctr < len as usize && pos + 3 <= buflen {
t = buf[pos] as u32;
pos += 1;
t |= (buf[pos] as u32) << 8;
pos += 1;
t |= (buf[pos] as u32) << 16;
pos += 1;
t &= 0x7FFFFF;
if t < Q as u32 {
a[ctr] = t as i32;
ctr += 1;
}
}
ctr as u32
}
const POLY_UNIFORM_NBLOCKS: usize =
(768 + STREAM128_BLOCKBYTES - 1) / STREAM128_BLOCKBYTES;
/// Sample polynomial with uniformly random coefficients
/// in [0, Q-1] by performing rejection sampling using the
/// output stream of SHAKE256(seed|nonce) or AES256CTR(seed,nonce).
pub fn poly_uniform(a: &mut Poly, seed: &[u8], nonce: u16) {
let mut buflen = POLY_UNIFORM_NBLOCKS * STREAM128_BLOCKBYTES;
let mut buf = [0u8; POLY_UNIFORM_NBLOCKS * STREAM128_BLOCKBYTES + 2];
let mut state = Stream128State::default();
stream128_init(&mut state, seed, nonce);
stream128_squeezeblocks(&mut buf, POLY_UNIFORM_NBLOCKS as u64, &mut state);
let mut ctr = rej_uniform(&mut a.coeffs, N_U32, &mut buf, buflen);
let mut off;
while ctr < N_U32 {
off = buflen % 3;
for i in 0..off {
buf[i] = buf[buflen - off + i];
}
buflen = STREAM128_BLOCKBYTES + off;
stream128_squeezeblocks(&mut buf[off..], 1, &mut state);
ctr += rej_uniform(
&mut a.coeffs[(ctr as usize)..],
N_U32 - ctr,
&mut buf,
buflen,
);
}
}
/// Sample uniformly random coefficients in [-ETA, ETA] by
/// performing rejection sampling using array of random bytes.
pub fn rej_eta(a: &mut [i32], len: usize, buf: &[u8], buflen: usize) -> u32 {
let (mut ctr, mut pos) = (0usize, 0usize);
let (mut t0, mut t1);
while ctr < len && pos < buflen {
t0 = (buf[pos] & 0x0F) as u32;
t1 = (buf[pos] >> 4) as u32;
pos += 1;
if ETA == 2 {
if t0 < 15 {
t0 = t0 - (205 * t0 >> 10) * 5;
a[ctr] = 2 - t0 as i32;
ctr += 1;
}
if t1 < 15 && ctr < len {
t1 = t1 - (205 * t1 >> 10) * 5;
a[ctr] = 2 - t1 as i32;
ctr += 1;
}
} else if ETA == 4 {
if t0 < 9 {
a[ctr] = 4 - t0 as i32;
ctr += 1;
}
if t1 < 9 && ctr < len {
a[ctr] = 4 - t1 as i32;
ctr += 1;
}
}
}
ctr as u32
}
/// Sample polynomial with uniformly random coefficients
/// in [-ETA,ETA] by performing rejection sampling using the
/// output stream from SHAKE256(seed|nonce) or AES256CTR(seed,nonce).
const POLY_UNIFORM_ETA_NBLOCKS: usize = if ETA == 2 {
(136 + STREAM256_BLOCKBYTES - 1) / STREAM256_BLOCKBYTES
} else {
(227 + STREAM256_BLOCKBYTES - 1) / STREAM256_BLOCKBYTES
};
pub fn poly_uniform_eta(a: &mut Poly, seed: &[u8], nonce: u16) {
let buflen = POLY_UNIFORM_ETA_NBLOCKS * STREAM256_BLOCKBYTES;
let mut buf = [0u8; POLY_UNIFORM_ETA_NBLOCKS * STREAM256_BLOCKBYTES];
let mut state = Stream256State::default();
stream256_init(&mut state, seed, nonce);
stream256_squeezeblocks(
&mut buf,
POLY_UNIFORM_ETA_NBLOCKS as u64,
&mut state,
);
let mut ctr = rej_eta(&mut a.coeffs, N, &buf, buflen);
while ctr < N_U32 {
stream256_squeezeblocks(&mut buf, 1, &mut state);
ctr += rej_eta(
&mut a.coeffs[ctr as usize..],
N - ctr as usize,
&buf,
STREAM256_BLOCKBYTES,
);
}
}
const POLY_UNIFORM_GAMMA1_NBLOCKS: usize =
(POLYZ_PACKEDBYTES + STREAM256_BLOCKBYTES - 1) / STREAM256_BLOCKBYTES;
/// Sample polynomial with uniformly random coefficients
/// in [-(GAMMA1 - 1), GAMMA1 - 1] by performing rejection
/// sampling on output stream of SHAKE256(seed|nonce)
/// or AES256CTR(seed,nonce).
pub fn poly_uniform_gamma1(a: &mut Poly, seed: &[u8], nonce: u16) {
let mut buf = [0u8; POLY_UNIFORM_GAMMA1_NBLOCKS * STREAM256_BLOCKBYTES];
let mut state = Stream256State::default();
stream256_init(&mut state, seed, nonce);
stream256_squeezeblocks(
&mut buf,
POLY_UNIFORM_GAMMA1_NBLOCKS as u64,
&mut state,
);
polyz_unpack(a, &mut buf);
}
/// Implementation of H. Samples polynomial with TAU nonzero
/// coefficients in {-1,1} using the output stream of
/// SHAKE256(seed).
pub fn poly_challenge(c: &mut Poly, seed: &[u8]) {
let mut _signs = 0u64;
let mut buf = [0u8; SHAKE256_RATE];
let mut state = KeccakState::default(); //shake256_init
shake256_absorb(&mut state, seed, SEEDBYTES);
shake256_finalize(&mut state);
shake256_squeezeblocks(&mut buf, 1, &mut state);
for i in 0..8 {
_signs |= (buf[i] as u64) << 8 * i;
}
let mut pos: usize = 8;
// let mut b = buf[pos];
let mut b;
c.coeffs.fill(0);
for i in N - TAU..N {
loop {
if pos >= SHAKE256_RATE {
shake256_squeezeblocks(&mut buf, 1, &mut state);
pos = 0;
}
b = buf[pos] as usize;
pos += 1;
if b <= i {
break;
}
}
c.coeffs[i] = c.coeffs[b as usize];
c.coeffs[b as usize] = 1i32 - 2 * (_signs & 1) as i32;
_signs >>= 1;
}
}
/// Bit-pack polynomial with coefficients in [-ETA,ETA].
/// Input coefficients are assumed to lie in [Q-ETA,Q+ETA].
pub fn polyeta_pack(r: &mut [u8], a: &Poly) {
let mut t = [0u8; 8];
if ETA == 2 {
for i in 0..N / 8 {
t[0] = (ETA_I32 - a.coeffs[8 * i + 0]) as u8;
t[1] = (ETA_I32 - a.coeffs[8 * i + 1]) as u8;
t[2] = (ETA_I32 - a.coeffs[8 * i + 2]) as u8;
t[3] = (ETA_I32 - a.coeffs[8 * i + 3]) as u8;
t[4] = (ETA_I32 - a.coeffs[8 * i + 4]) as u8;
t[5] = (ETA_I32 - a.coeffs[8 * i + 5]) as u8;
t[6] = (ETA_I32 - a.coeffs[8 * i + 6]) as u8;
t[7] = (ETA_I32 - a.coeffs[8 * i + 7]) as u8;
r[3 * i + 0] = (t[0] >> 0) | (t[1] << 3) | (t[2] << 6);
r[3 * i + 1] = (t[2] >> 2) | (t[3] << 1) | (t[4] << 4) | (t[5] << 7);
r[3 * i + 2] = (t[5] >> 1) | (t[6] << 2) | (t[7] << 5);
}
} else {
for i in 0..N / 2 {
t[0] = (ETA_I32 - a.coeffs[2 * i + 0]) as u8;
t[1] = (ETA_I32 - a.coeffs[2 * i + 1]) as u8;
r[i] = t[0] | (t[1] << 4);
}
}
}
/// Unpack polynomial with coefficients in [-ETA,ETA].
pub fn polyeta_unpack(r: &mut Poly, a: &[u8]) {
if ETA == 2 {
for i in 0..N / 8 {
r.coeffs[8 * i + 0] = (a[3 * i + 0] & 0x07) as i32;
r.coeffs[8 * i + 1] = ((a[3 * i + 0] >> 3) & 0x07) as i32;
r.coeffs[8 * i + 2] =
(((a[3 * i + 0] >> 6) | (a[3 * i + 1] << 2)) & 0x07) as i32;
r.coeffs[8 * i + 3] = ((a[3 * i + 1] >> 1) & 0x07) as i32;
r.coeffs[8 * i + 4] = ((a[3 * i + 1] >> 4) & 0x07) as i32;
r.coeffs[8 * i + 5] =
(((a[3 * i + 1] >> 7) | (a[3 * i + 2] << 1)) & 0x07) as i32;
r.coeffs[8 * i + 6] = ((a[3 * i + 2] >> 2) & 0x07) as i32;
r.coeffs[8 * i + 7] = ((a[3 * i + 2] >> 5) & 0x07) as i32;
r.coeffs[8 * i + 0] = (ETA_I32 - r.coeffs[8 * i + 0]) as i32;
r.coeffs[8 * i + 1] = (ETA_I32 - r.coeffs[8 * i + 1]) as i32;
r.coeffs[8 * i + 2] = (ETA_I32 - r.coeffs[8 * i + 2]) as i32;
r.coeffs[8 * i + 3] = (ETA_I32 - r.coeffs[8 * i + 3]) as i32;
r.coeffs[8 * i + 4] = (ETA_I32 - r.coeffs[8 * i + 4]) as i32;
r.coeffs[8 * i + 5] = (ETA_I32 - r.coeffs[8 * i + 5]) as i32;
r.coeffs[8 * i + 6] = (ETA_I32 - r.coeffs[8 * i + 6]) as i32;
r.coeffs[8 * i + 7] = (ETA_I32 - r.coeffs[8 * i + 7]) as i32;
}
} else {
for i in 0..N / 2 {
r.coeffs[2 * i + 0] = (a[i] & 0x0F) as i32;
r.coeffs[2 * i + 1] = (a[i] >> 4) as i32;
r.coeffs[2 * i + 0] = (ETA_I32 - r.coeffs[2 * i + 0]) as i32;
r.coeffs[2 * i + 1] = (ETA_I32 - r.coeffs[2 * i + 1]) as i32;
}
}
}
/// Bit-pack polynomial t1 with coefficients fitting in 10 bits.
/// Input coefficients are assumed to be standard representatives.
pub fn polyt1_pack(r: &mut [u8], a: &Poly) {
for i in 0..N / 4 {
r[5 * i + 0] = (a.coeffs[4 * i + 0] >> 0) as u8;
r[5 * i + 1] =
((a.coeffs[4 * i + 0] >> 8) | (a.coeffs[4 * i + 1] << 2)) as u8;
r[5 * i + 2] =
((a.coeffs[4 * i + 1] >> 6) | (a.coeffs[4 * i + 2] << 4)) as u8;
r[5 * i + 3] =
((a.coeffs[4 * i + 2] >> 4) | (a.coeffs[4 * i + 3] << 6)) as u8;
r[5 * i + 4] = (a.coeffs[4 * i + 3] >> 2) as u8;
}
}
/// Unpack polynomial t1 with 9-bit coefficients.
/// Output coefficients are standard representatives.
pub fn polyt1_unpack(r: &mut Poly, a: &[u8]) {
for i in 0..N / 4 {
r.coeffs[4 * i + 0] = (((a[5 * i + 0] >> 0) as u32
| (a[5 * i + 1] as u32) << 8)
& 0x3FF) as i32;
r.coeffs[4 * i + 1] = (((a[5 * i + 1] >> 2) as u32
| (a[5 * i + 2] as u32) << 6)
& 0x3FF) as i32;
r.coeffs[4 * i + 2] = (((a[5 * i + 2] >> 4) as u32
| (a[5 * i + 3] as u32) << 4)
& 0x3FF) as i32;
r.coeffs[4 * i + 3] = (((a[5 * i + 3] >> 6) as u32
| (a[5 * i + 4] as u32) << 2)
& 0x3FF) as i32;
}
}
/// Bit-pack polynomial t0 with coefficients in [-2^{D-1}, 2^{D-1}].
pub fn polyt0_pack(r: &mut [u8], a: &Poly) {
let mut t = [0i32; 8];
for i in 0..N / 8 {
t[0] = D_SHL - a.coeffs[8 * i + 0];
t[1] = D_SHL - a.coeffs[8 * i + 1];
t[2] = D_SHL - a.coeffs[8 * i + 2];
t[3] = D_SHL - a.coeffs[8 * i + 3];
t[4] = D_SHL - a.coeffs[8 * i + 4];
t[5] = D_SHL - a.coeffs[8 * i + 5];
t[6] = D_SHL - a.coeffs[8 * i + 6];
t[7] = D_SHL - a.coeffs[8 * i + 7];
r[13 * i + 0] = (t[0]) as u8;
r[13 * i + 1] = (t[0] >> 8) as u8;
r[13 * i + 1] |= (t[1] << 5) as u8;
r[13 * i + 2] = (t[1] >> 3) as u8;
r[13 * i + 3] = (t[1] >> 11) as u8;
r[13 * i + 3] |= (t[2] << 2) as u8;
r[13 * i + 4] = (t[2] >> 6) as u8;
r[13 * i + 4] |= (t[3] << 7) as u8;
r[13 * i + 5] = (t[3] >> 1) as u8;
r[13 * i + 6] = (t[3] >> 9) as u8;
r[13 * i + 6] |= (t[4] << 4) as u8;
r[13 * i + 7] = (t[4] >> 4) as u8;
r[13 * i + 8] = (t[4] >> 12) as u8;
r[13 * i + 8] |= (t[5] << 1) as u8;
r[13 * i + 9] = (t[5] >> 7) as u8;
r[13 * i + 9] |= (t[6] << 6) as u8;
r[13 * i + 10] = (t[6] >> 2) as u8;
r[13 * i + 11] = (t[6] >> 10) as u8;
r[13 * i + 11] |= (t[7] << 3) as u8;
r[13 * i + 12] = (t[7] >> 5) as u8;
}
}
/// Unpack polynomial t0 with coefficients in ]-2^{D-1}, 2^{D-1}].
/// Output coefficients lie in ]Q-2^{D-1},Q+2^{D-1}].
pub fn polyt0_unpack(r: &mut Poly, a: &[u8]) {
for i in 0..N / 8 {
r.coeffs[8 * i + 0] = a[13 * i + 0] as i32;
r.coeffs[8 * i + 0] |= (a[13 * i + 1] as i32) << 8;
r.coeffs[8 * i + 0] &= 0x1FFF;
r.coeffs[8 * i + 1] = (a[13 * i + 1] as i32) >> 5;
r.coeffs[8 * i + 1] |= (a[13 * i + 2] as i32) << 3;
r.coeffs[8 * i + 1] |= (a[13 * i + 3] as i32) << 11;
r.coeffs[8 * i + 1] &= 0x1FFF;
r.coeffs[8 * i + 2] = (a[13 * i + 3] as i32) >> 2;
r.coeffs[8 * i + 2] |= (a[13 * i + 4] as i32) << 6;
r.coeffs[8 * i + 2] &= 0x1FFF;
r.coeffs[8 * i + 3] = (a[13 * i + 4] as i32) >> 7;
r.coeffs[8 * i + 3] |= (a[13 * i + 5] as i32) << 1;
r.coeffs[8 * i + 3] |= (a[13 * i + 6] as i32) << 9;
r.coeffs[8 * i + 3] &= 0x1FFF;
r.coeffs[8 * i + 4] = (a[13 * i + 6] as i32) >> 4;
r.coeffs[8 * i + 4] |= (a[13 * i + 7] as i32) << 4;
r.coeffs[8 * i + 4] |= (a[13 * i + 8] as i32) << 12;
r.coeffs[8 * i + 4] &= 0x1FFF;
r.coeffs[8 * i + 5] = (a[13 * i + 8] as i32) >> 1;
r.coeffs[8 * i + 5] |= (a[13 * i + 9] as i32) << 7;
r.coeffs[8 * i + 5] &= 0x1FFF;
r.coeffs[8 * i + 6] = (a[13 * i + 9] as i32) >> 6;
r.coeffs[8 * i + 6] |= (a[13 * i + 10] as i32) << 2;
r.coeffs[8 * i + 6] |= (a[13 * i + 11] as i32) << 10;
r.coeffs[8 * i + 6] &= 0x1FFF;
r.coeffs[8 * i + 7] = (a[13 * i + 11] as i32) >> 3;
r.coeffs[8 * i + 7] |= (a[13 * i + 12] as i32) << 5;
r.coeffs[8 * i + 7] &= 0x1FFF; // TODO: Unnecessary mask?
r.coeffs[8 * i + 0] = D_SHL - r.coeffs[8 * i + 0];
r.coeffs[8 * i + 1] = D_SHL - r.coeffs[8 * i + 1];
r.coeffs[8 * i + 2] = D_SHL - r.coeffs[8 * i + 2];
r.coeffs[8 * i + 3] = D_SHL - r.coeffs[8 * i + 3];
r.coeffs[8 * i + 4] = D_SHL - r.coeffs[8 * i + 4];
r.coeffs[8 * i + 5] = D_SHL - r.coeffs[8 * i + 5];
r.coeffs[8 * i + 6] = D_SHL - r.coeffs[8 * i + 6];
r.coeffs[8 * i + 7] = D_SHL - r.coeffs[8 * i + 7];
}
}
/// Bit-pack polynomial z with coefficients
/// in [-(GAMMA1 - 1), GAMMA1 - 1].
/// Input coefficients are assumed to be standard representatives.*
pub fn polyz_pack(r: &mut [u8], a: &Poly) {
let mut t = [0i32; 4];
if GAMMA1 == (1 << 17) {
for i in 0..N / 4 {
t[0] = GAMMA1_I32 - a.coeffs[4 * i + 0];
t[1] = GAMMA1_I32 - a.coeffs[4 * i + 1];
t[2] = GAMMA1_I32 - a.coeffs[4 * i + 2];
t[3] = GAMMA1_I32 - a.coeffs[4 * i + 3];
r[9 * i + 0] = (t[0]) as u8;
r[9 * i + 1] = (t[0] >> 8) as u8;
r[9 * i + 2] = (t[0] >> 16) as u8;
r[9 * i + 2] |= (t[1] << 2) as u8;
r[9 * i + 3] = (t[1] >> 6) as u8;
r[9 * i + 4] = (t[1] >> 14) as u8;
r[9 * i + 4] |= (t[2] << 4) as u8;
r[9 * i + 5] = (t[2] >> 4) as u8;
r[9 * i + 6] = (t[2] >> 12) as u8;
r[9 * i + 6] |= (t[3] << 6) as u8;
r[9 * i + 7] = (t[3] >> 2) as u8;
r[9 * i + 8] = (t[3] >> 10) as u8;
}
} else if GAMMA1 == 1 << 19 {
for i in 0..N / 2 {
t[0] = GAMMA1_I32 - a.coeffs[2 * i + 0];
t[1] = GAMMA1_I32 - a.coeffs[2 * i + 1];
r[5 * i + 0] = (t[0]) as u8;
r[5 * i + 1] = (t[0] >> 8) as u8;
r[5 * i + 2] = (t[0] >> 16) as u8;
r[5 * i + 2] |= (t[1] << 4) as u8;
r[5 * i + 3] = (t[1] >> 4) as u8;
r[5 * i + 4] = (t[1] >> 12) as u8;
}
}
}
/// Unpack polynomial z with coefficients
/// in [-(GAMMA1 - 1), GAMMA1 - 1].
/// Output coefficients are standard representatives.
pub fn polyz_unpack(r: &mut Poly, a: &[u8]) {
if GAMMA1 == (1 << 17) {
for i in 0..N / 4 {
r.coeffs[4 * i + 0] = a[9 * i + 0] as i32;
r.coeffs[4 * i + 0] |= (a[9 * i + 1] as i32) << 8;
r.coeffs[4 * i + 0] |= (a[9 * i + 2] as i32) << 16;
r.coeffs[4 * i + 0] &= 0x3FFFF;
r.coeffs[4 * i + 1] = (a[9 * i + 2] as i32) >> 2;
r.coeffs[4 * i + 1] |= (a[9 * i + 3] as i32) << 6;
r.coeffs[4 * i + 1] |= (a[9 * i + 4] as i32) << 14;
r.coeffs[4 * i + 1] &= 0x3FFFF;
r.coeffs[4 * i + 2] = (a[9 * i + 4] as i32) >> 4;
r.coeffs[4 * i + 2] |= (a[9 * i + 5] as i32) << 4;
r.coeffs[4 * i + 2] |= (a[9 * i + 6] as i32) << 12;
r.coeffs[4 * i + 2] &= 0x3FFFF;
r.coeffs[4 * i + 3] = (a[9 * i + 6] as i32) >> 6;
r.coeffs[4 * i + 3] |= (a[9 * i + 7] as i32) << 2;
r.coeffs[4 * i + 3] |= (a[9 * i + 8] as i32) << 10;
r.coeffs[4 * i + 3] &= 0x3FFFF; // TODO: Unnecessary mask?
r.coeffs[4 * i + 0] = GAMMA1_I32 - r.coeffs[4 * i + 0];
r.coeffs[4 * i + 1] = GAMMA1_I32 - r.coeffs[4 * i + 1];
r.coeffs[4 * i + 2] = GAMMA1_I32 - r.coeffs[4 * i + 2];
r.coeffs[4 * i + 3] = GAMMA1_I32 - r.coeffs[4 * i + 3];
}
} else if GAMMA1 == 1 << 19 {
for i in 0..N / 2 {
r.coeffs[2 * i + 0] = a[5 * i + 0] as i32;
r.coeffs[2 * i + 0] |= (a[5 * i + 1] as i32) << 8;
r.coeffs[2 * i + 0] |= (a[5 * i + 2] as i32) << 16;
r.coeffs[2 * i + 0] &= 0xFFFFF;
r.coeffs[2 * i + 1] = (a[5 * i + 2] as i32) >> 4;
r.coeffs[2 * i + 1] |= (a[5 * i + 3] as i32) << 4;
r.coeffs[2 * i + 1] |= (a[5 * i + 4] as i32) << 12;
r.coeffs[2 * i + 0] &= 0xFFFFF; // TODO: Unnecessary mask?
r.coeffs[2 * i + 0] = GAMMA1_I32 - r.coeffs[2 * i + 0];
r.coeffs[2 * i + 1] = GAMMA1_I32 - r.coeffs[2 * i + 1];
}
}
}
/// Bit-pack polynomial w1 with coefficients in [0, 15].
/// Input coefficients are assumed to be standard representatives.
pub fn polyw1_pack(r: &mut [u8], a: &Poly) {
if GAMMA2 == (Q - 1) / 88 {
for i in 0..N / 4 {
r[3 * i + 0] = a.coeffs[4 * i + 0] as u8;
r[3 * i + 0] |= (a.coeffs[4 * i + 1] << 6) as u8;
r[3 * i + 1] = (a.coeffs[4 * i + 1] >> 2) as u8;
r[3 * i + 1] |= (a.coeffs[4 * i + 2] << 4) as u8;
r[3 * i + 2] = (a.coeffs[4 * i + 2] >> 4) as u8;
r[3 * i + 2] |= (a.coeffs[4 * i + 3] << 2) as u8;
}
} else {
for i in 0..N / 2 {
r[i] = (a.coeffs[2 * i + 0] | (a.coeffs[2 * i + 1] << 4)) as u8;
}
}
}

273
dilithium/src/polyvec.rs Normal file
View File

@ -0,0 +1,273 @@
use crate::params::*;
use crate::poly::*;
#[derive(Copy, Clone)]
pub struct Polyveck {
pub vec: [Poly; K],
}
impl Default for Polyveck {
fn default() -> Self {
Polyveck {
vec: [Poly::default(); K],
}
}
}
#[derive(Copy, Clone)]
pub struct Polyvecl {
pub vec: [Poly; L],
}
impl Default for Polyvecl {
fn default() -> Self {
Polyvecl {
vec: [Poly::default(); L],
}
}
}
/// Implementation of ExpandA. Generates matrix A with uniformly
/// random coefficients a_{i,j} by performing rejection
/// sampling on the output stream of SHAKE128(rho|j|i)
/// or AES256CTR(rho,j|i).
pub fn polyvec_matrix_expand(mat: &mut [Polyvecl], rho: &[u8]) {
for i in 0..K {
for j in 0..L {
poly_uniform(&mut mat[i].vec[j], rho, ((i << 8) + j) as u16);
}
}
}
pub fn polyvec_matrix_pointwise_montgomery(
t: &mut Polyveck,
mat: &[Polyvecl],
v: &Polyvecl,
) {
for i in 0..K {
polyvecl_pointwise_acc_montgomery(&mut t.vec[i], &mat[i], v);
}
}
//*********** Vectors of polynomials of length L ****************************
pub fn polyvecl_uniform_eta(v: &mut Polyvecl, seed: &[u8], mut nonce: u16) {
for i in 0..L {
poly_uniform_eta(&mut v.vec[i], seed, nonce);
nonce += 1;
}
}
pub fn polyvecl_uniform_gamma1(v: &mut Polyvecl, seed: &[u8], nonce: u16) {
for i in 0..L {
poly_uniform_gamma1(&mut v.vec[i], seed, L_U16 * nonce + i as u16);
}
}
pub fn polyvecl_reduce(v: &mut Polyvecl) {
for i in 0..L {
poly_reduce(&mut v.vec[i]);
}
}
/// Add vectors of polynomials of length L.
/// No modular reduction is performed.
pub fn polyvecl_add(w: &mut Polyvecl, v: &Polyvecl) {
for i in 0..L {
poly_add(&mut w.vec[i], &v.vec[i]);
}
}
/// Forward NTT of all polynomials in vector of length L. Output
/// coefficients can be up to 16*Q larger than input coefficients.*
pub fn polyvecl_ntt(v: &mut Polyvecl) {
for i in 0..L {
poly_ntt(&mut v.vec[i]);
}
}
pub fn polyvecl_invntt_tomont(v: &mut Polyvecl) {
for i in 0..L {
poly_invntt_tomont(&mut v.vec[i]);
}
}
pub fn polyvecl_pointwise_poly_montgomery(
r: &mut Polyvecl,
a: &Poly,
v: &Polyvecl,
) {
for i in 0..L {
poly_pointwise_montgomery(&mut r.vec[i], a, &v.vec[i]);
}
}
/// Pointwise multiply vectors of polynomials of length L, multiply
/// resulting vector by 2^{-32} and add (accumulate) polynomials
/// in it. Input/output vectors are in NTT domain representation.
/// Input coefficients are assumed to be less than 22*Q. Output
/// coeffcient are less than 2*L*Q.
pub fn polyvecl_pointwise_acc_montgomery(
w: &mut Poly,
u: &Polyvecl,
v: &Polyvecl,
) {
let mut t = Poly::default();
poly_pointwise_montgomery(w, &u.vec[0], &v.vec[0]);
for i in 1..L {
poly_pointwise_montgomery(&mut t, &u.vec[i], &v.vec[i]);
poly_add(w, &t);
}
}
/// Check infinity norm of polynomials in vector of length L.
/// Assumes input coefficients to be standard representatives.
/// Returns 0 if norm of all polynomials is strictly smaller than B and 1
/// otherwise.
pub fn polyvecl_chknorm(v: &Polyvecl, bound: i32) -> u8 {
for i in 0..L {
if poly_chknorm(&v.vec[i], bound) > 0 {
return 1;
}
}
return 0;
}
//*********** Vectors of polynomials of length K ****************************
pub fn polyveck_uniform_eta(v: &mut Polyveck, seed: &[u8], mut nonce: u16) {
for i in 0..K {
poly_uniform_eta(&mut v.vec[i], seed, nonce);
nonce += 1
}
}
/// Reduce coefficients of polynomials in vector of length K
/// to representatives in [0,2*Q].
pub fn polyveck_reduce(v: &mut Polyveck) {
for i in 0..K {
poly_reduce(&mut v.vec[i]);
}
}
/// For all coefficients of polynomials in vector of length K
/// add Q if coefficient is negative.
pub fn polyveck_caddq(v: &mut Polyveck) {
for i in 0..K {
poly_caddq(&mut v.vec[i]);
}
}
/// Add vectors of polynomials of length K.
/// No modular reduction is performed.
pub fn polyveck_add(w: &mut Polyveck, v: &Polyveck) {
for i in 0..K {
poly_add(&mut w.vec[i], &v.vec[i]);
}
}
/// Subtract vectors of polynomials of length K.
/// Assumes coefficients of polynomials in second input vector
/// to be less than 2*Q. No modular reduction is performed.
pub fn polyveck_sub(w: &mut Polyveck, v: &Polyveck) {
for i in 0..K {
poly_sub(&mut w.vec[i], &v.vec[i]);
}
}
/// Multiply vector of polynomials of Length K by 2^D without modular
/// reduction. Assumes input coefficients to be less than 2^{32-D}.
pub fn polyveck_shiftl(v: &mut Polyveck) {
for i in 0..K {
poly_shiftl(&mut v.vec[i]);
}
}
/// Forward NTT of all polynomials in vector of length K. Output
/// coefficients can be up to 16*Q larger than input coefficients.
pub fn polyveck_ntt(v: &mut Polyveck) {
for i in 0..K {
poly_ntt(&mut v.vec[i]);
}
}
/// Inverse NTT and multiplication by 2^{32} of polynomials
/// in vector of length K. Input coefficients need to be less
/// than 2*Q.
pub fn polyveck_invntt_tomont(v: &mut Polyveck) {
for i in 0..K {
poly_invntt_tomont(&mut v.vec[i]);
}
}
pub fn polyveck_pointwise_poly_montgomery(
r: &mut Polyveck,
a: &Poly,
v: &Polyveck,
) {
for i in 0..K {
poly_pointwise_montgomery(&mut r.vec[i], a, &v.vec[i]);
}
}
/// Check infinity norm of polynomials in vector of length K.
/// Assumes input coefficients to be standard representatives.
//
/// Returns 0 if norm of all polynomials are strictly smaller than B and 1
/// otherwise.
pub fn polyveck_chknorm(v: &Polyveck, bound: i32) -> u8 {
for i in 0..K {
if poly_chknorm(&v.vec[i], bound) > 0 {
return 1;
}
}
return 0;
}
/// For all coefficients a of polynomials in vector of length K,
/// compute a0, a1 such that a mod Q = a1*2^D + a0
/// with -2^{D-1} < a0 <= 2^{D-1}. Assumes coefficients to be
/// standard representatives.
pub fn polyveck_power2round(v1: &mut Polyveck, v0: &mut Polyveck) {
for i in 0..K {
poly_power2round(&mut v1.vec[i], &mut v0.vec[i]);
}
}
/// For all coefficients a of polynomials in vector of length K,
/// compute high and low bits a0, a1 such a mod Q = a1*ALPHA + a0
/// with -ALPHA/2 < a0 <= ALPHA/2 except a1 = (Q-1)/ALPHA where we
/// set a1 = 0 and -ALPHA/2 <= a0 = a mod Q - Q < 0.
/// Assumes coefficients to be standard representatives.
pub fn polyveck_decompose(v1: &mut Polyveck, v0: &mut Polyveck) {
for i in 0..K {
poly_decompose(&mut v1.vec[i], &mut v0.vec[i]);
}
}
/// Compute hint vector.
///
/// Returns number of 1 bits.
pub fn polyveck_make_hint(
h: &mut Polyveck,
v0: &Polyveck,
v1: &Polyveck,
) -> i32 {
let mut s = 0i32;
for i in 0..K {
s += poly_make_hint(&mut h.vec[i], &v0.vec[i], &v1.vec[i]);
}
s
}
/// Use hint vector to correct the high bits of input vector.
pub fn polyveck_use_hint(w: &mut Polyveck, h: &Polyveck) {
for i in 0..K {
poly_use_hint(&mut w.vec[i], &h.vec[i]);
}
}
pub fn polyveck_pack_w1(r: &mut [u8], w1: &Polyveck) {
for i in 0..K {
polyw1_pack(&mut r[i * POLYW1_PACKEDBYTES..], &w1.vec[i]);
}
}

View File

@ -0,0 +1,5 @@
use rand::prelude::*;
pub fn randombytes(x: &mut [u8], len: usize) {
thread_rng().fill_bytes(&mut x[..len])
}

30
dilithium/src/reduce.rs Normal file
View File

@ -0,0 +1,30 @@
use crate::params::*;
pub const QINV: i32 = 58728449; // q^(-1) mod 2^32
/// For finite field element a with -2^{31}Q <= a <= Q*2^31,
/// compute r \equiv a*2^{-32} (mod Q) such that -Q < r < Q.
///
/// Returns r.
pub fn montgomery_reduce(a: i64) -> i32 {
let mut t = (a as i32).wrapping_mul(QINV) as i64;
t = (a as i64 - t * Q as i64) >> 32;
t as i32
}
/// For finite field element a with a <= 2^{31} - 2^{22} - 1,
/// compute r \equiv a (mod Q) such that -6283009 <= r <= 6283007.
//
/// Returns r.
pub fn reduce32(a: i32) -> i32 {
let mut t = (a + (1 << 22)) >> 23;
t = a - t * Q as i32;
t
}
/// Add Q if input coefficient is negative.
///
/// Returns r.
pub fn caddq(a: i32) -> i32 {
a + ((a >> 31) & Q as i32)
}

77
dilithium/src/rounding.rs Normal file
View File

@ -0,0 +1,77 @@
use crate::params::*;
/// For finite field element a, compute a0, a1 such that
/// a mod^+ Q = a1*2^D + a0 with -2^{D-1} < a0 <= 2^{D-1}.
/// Assumes a to be standard representative.
///
/// Returns a1.
pub fn power2round(a: i32, a0: &mut i32) -> i32 {
let a1 = (a + (1 << (D - 1)) - 1) >> D;
*a0 = a - (a1 << D);
return a1;
}
/// For finite field element a, compute high and low bits a0, a1 such
/// that a mod^+ Q = a1*ALPHA + a0 with -ALPHA/2 < a0 <= ALPHA/2 except
/// if a1 = (Q-1)/ALPHA where we set a1 = 0 and
/// -ALPHA/2 <= a0 = a mod^+ Q - Q < 0. Assumes a to be standard
/// representative.
///
/// Returns a1.
pub fn decompose(a0: &mut i32, a: i32) -> i32 {
let mut a1 = (a + 127) >> 7;
if GAMMA2 == (Q - 1) / 32 {
a1 = (a1 * 1025 + (1 << 21)) >> 22;
a1 &= 15;
} else if GAMMA2 == (Q - 1) / 88 {
a1 = (a1 * 11275 + (1 << 23)) >> 24;
a1 ^= ((43 - a1) >> 31) & a1;
}
*a0 = a - a1 * 2 * GAMMA2_I32;
*a0 -= (((Q_I32 - 1) / 2 - *a0) >> 31) & Q_I32;
a1
}
/// Compute hint bit indicating whether the low bits of the
/// input element overflow into the high bits.
///
/// Returns 1 if overflow.
pub fn make_hint(a0: i32, a1: i32) -> u8 {
if a0 > GAMMA2_I32 || a0 < -GAMMA2_I32 || (a0 == -GAMMA2_I32 && a1 != 0) {
return 1;
}
return 0;
}
/// Correct high bits according to hint.
///
/// Returns corrected high bits.
pub fn use_hint(a: i32, hint: u8) -> i32 {
let mut a0 = 0i32;
let a1 = decompose(&mut a0, a);
if hint == 0 {
return a1;
}
if GAMMA2 == (Q - 1) / 32 {
if a0 > 0 {
return (a1 + 1) & 15;
} else {
return (a1 - 1) & 15;
}
} else {
if a0 > 0 {
if a1 == 43 {
return 0;
} else {
return a1 + 1;
};
} else {
if a1 == 0 {
return 43;
} else {
return a1 - 1;
}
}
}
}

247
dilithium/src/sign.rs Normal file
View File

@ -0,0 +1,247 @@
use crate::{
fips202::*, packing::*, params::*, poly::*, polyvec::*, randombytes::*,
SignError,
};
pub fn crypto_sign_keypair(
pk: &mut [u8],
sk: &mut [u8],
seed: Option<&[u8]>,
) -> u8 {
let mut init_seed = [0u8; SEEDBYTES];
match seed {
Some(x) => init_seed.copy_from_slice(x),
None => randombytes(&mut init_seed, SEEDBYTES),
};
let mut seedbuf = [0u8; 2 * SEEDBYTES + CRHBYTES];
let mut tr = [0u8; SEEDBYTES];
let (mut rho, mut rhoprime, mut key) =
([0u8; SEEDBYTES], [0u8; CRHBYTES], [0u8; SEEDBYTES]);
let mut mat = [Polyvecl::default(); K];
let mut s1 = Polyvecl::default();
let (mut s2, mut t1, mut t0) = (
Polyveck::default(),
Polyveck::default(),
Polyveck::default(),
);
// Get randomness for rho, rhoprime and key
shake256(
&mut seedbuf,
2 * SEEDBYTES + CRHBYTES,
&init_seed,
SEEDBYTES,
);
rho.copy_from_slice(&seedbuf[..SEEDBYTES]);
rhoprime.copy_from_slice(&seedbuf[SEEDBYTES..SEEDBYTES + CRHBYTES]);
key.copy_from_slice(&seedbuf[SEEDBYTES + CRHBYTES..]);
// Expand matrix
polyvec_matrix_expand(&mut mat, &rho);
// Sample short vectors s1 and s2
polyvecl_uniform_eta(&mut s1, &rhoprime, 0);
polyveck_uniform_eta(&mut s2, &rhoprime, L_U16);
// Matrix-vector multiplication
let mut s1hat = s1;
polyvecl_ntt(&mut s1hat);
polyvec_matrix_pointwise_montgomery(&mut t1, &mat, &s1hat);
polyveck_reduce(&mut t1);
polyveck_invntt_tomont(&mut t1);
// Add error vector s2
polyveck_add(&mut t1, &s2);
// Extract t1 and write public key
polyveck_caddq(&mut t1);
polyveck_power2round(&mut t1, &mut t0);
pack_pk(pk, &rho, &t1);
// Compute H(rho, t1) and write secret key
shake256(&mut tr, SEEDBYTES, pk, PUBLICKEYBYTES);
pack_sk(sk, &rho, &tr, &key, &t0, &s1, &s2);
return 0;
}
pub fn crypto_sign_signature(sig: &mut [u8], m: &[u8], sk: &[u8]) {
// `key` and `mu` are concatenated
let mut keymu = [0u8; SEEDBYTES + CRHBYTES];
let mut nonce = 0u16;
let mut mat = [Polyvecl::default(); K];
let (mut s1, mut y) = (Polyvecl::default(), Polyvecl::default());
let (mut s2, mut t0) = (Polyveck::default(), Polyveck::default());
let (mut w1, mut w0) = (Polyveck::default(), Polyveck::default());
let mut h = Polyveck::default();
let mut cp = Poly::default();
let mut state = KeccakState::default(); //shake256_init()
let mut rho = [0u8; SEEDBYTES];
let mut tr = [0u8; SEEDBYTES];
let mut rhoprime = [0u8; CRHBYTES];
unpack_sk(
&mut rho,
&mut tr,
&mut keymu[..SEEDBYTES],
&mut t0,
&mut s1,
&mut s2,
&sk,
);
// Compute CRH(tr, msg)
shake256_absorb(&mut state, &tr, SEEDBYTES);
shake256_absorb(&mut state, m, m.len());
shake256_finalize(&mut state);
shake256_squeeze(&mut keymu[SEEDBYTES..], CRHBYTES, &mut state);
if RANDOMIZED_SIGNING {
randombytes(&mut rhoprime, CRHBYTES);
} else {
shake256(&mut rhoprime, CRHBYTES, &keymu, SEEDBYTES + CRHBYTES);
}
// Expand matrix and transform vectors
polyvec_matrix_expand(&mut mat, &rho);
polyvecl_ntt(&mut s1);
polyveck_ntt(&mut s2);
polyveck_ntt(&mut t0);
loop {
// Sample intermediate vector y
polyvecl_uniform_gamma1(&mut y, &rhoprime, nonce);
nonce += 1;
// Matrix-vector multiplication
let mut z = y;
polyvecl_ntt(&mut z);
polyvec_matrix_pointwise_montgomery(&mut w1, &mat, &z);
polyveck_reduce(&mut w1);
polyveck_invntt_tomont(&mut w1);
// Decompose w and call the random oracle
polyveck_caddq(&mut w1);
polyveck_decompose(&mut w1, &mut w0);
polyveck_pack_w1(sig, &w1);
state.init();
shake256_absorb(&mut state, &keymu[SEEDBYTES..], CRHBYTES);
shake256_absorb(&mut state, &sig, K * POLYW1_PACKEDBYTES);
shake256_finalize(&mut state);
shake256_squeeze(sig, SEEDBYTES, &mut state);
poly_challenge(&mut cp, sig);
poly_ntt(&mut cp);
// Compute z, reject if it reveals secret
polyvecl_pointwise_poly_montgomery(&mut z, &cp, &s1);
polyvecl_invntt_tomont(&mut z);
polyvecl_add(&mut z, &y);
polyvecl_reduce(&mut z);
if polyvecl_chknorm(&z, (GAMMA1 - BETA) as i32) > 0 {
continue;
}
/* Check that subtracting cs2 does not change high bits of w and low bits
* do not reveal secret information */
polyveck_pointwise_poly_montgomery(&mut h, &cp, &s2);
polyveck_invntt_tomont(&mut h);
polyveck_sub(&mut w0, &h);
polyveck_reduce(&mut w0);
if polyveck_chknorm(&w0, (GAMMA2 - BETA) as i32) > 0 {
continue;
}
// Compute hints for w1
polyveck_pointwise_poly_montgomery(&mut h, &cp, &t0);
polyveck_invntt_tomont(&mut h);
polyveck_reduce(&mut h);
if polyveck_chknorm(&h, GAMMA2_I32) > 0 {
continue;
}
polyveck_add(&mut w0, &h);
let n = polyveck_make_hint(&mut h, &w0, &w1);
if n > OMEGA as i32 {
continue;
}
// Write signature
pack_sig(sig, None, &z, &h);
return;
}
}
pub fn crypto_sign_verify(
sig: &[u8],
m: &[u8],
pk: &[u8],
) -> Result<(), SignError> {
let mut buf = [0u8; K * POLYW1_PACKEDBYTES];
let mut rho = [0u8; SEEDBYTES];
let mut mu = [0u8; CRHBYTES];
let mut c = [0u8; SEEDBYTES];
let mut c2 = [0u8; SEEDBYTES];
let mut cp = Poly::default();
let (mut mat, mut z) = ([Polyvecl::default(); K], Polyvecl::default());
let (mut t1, mut w1, mut h) = (
Polyveck::default(),
Polyveck::default(),
Polyveck::default(),
);
let mut state = KeccakState::default(); // shake256_init()
if sig.len() != SIGNBYTES {
return Err(SignError::Input);
}
unpack_pk(&mut rho, &mut t1, pk);
if let Err(e) = unpack_sig(&mut c, &mut z, &mut h, sig) {
return Err(e);
}
if polyvecl_chknorm(&z, (GAMMA1 - BETA) as i32) > 0 {
return Err(SignError::Input);
}
// Compute CRH(CRH(rho, t1), msg)
shake256(&mut mu, SEEDBYTES, pk, PUBLICKEYBYTES);
shake256_absorb(&mut state, &mu, SEEDBYTES);
shake256_absorb(&mut state, m, m.len());
shake256_finalize(&mut state);
shake256_squeeze(&mut mu, CRHBYTES, &mut state);
// Matrix-vector multiplication; compute Az - c2^dt1
poly_challenge(&mut cp, &c);
polyvec_matrix_expand(&mut mat, &rho);
polyvecl_ntt(&mut z);
polyvec_matrix_pointwise_montgomery(&mut w1, &mat, &z);
poly_ntt(&mut cp);
polyveck_shiftl(&mut t1);
polyveck_ntt(&mut t1);
let t1_2 = t1.clone();
polyveck_pointwise_poly_montgomery(&mut t1, &cp, &t1_2);
polyveck_sub(&mut w1, &t1);
polyveck_reduce(&mut w1);
polyveck_invntt_tomont(&mut w1);
// Reconstruct w1
polyveck_caddq(&mut w1);
polyveck_use_hint(&mut w1, &h);
polyveck_pack_w1(&mut buf, &w1);
// Call random oracle and verify challenge
state.init();
shake256_absorb(&mut state, &mu, CRHBYTES);
shake256_absorb(&mut state, &buf, K * POLYW1_PACKEDBYTES);
shake256_finalize(&mut state);
shake256_squeeze(&mut c2, SEEDBYTES, &mut state);
// Doesn't require constant time equality check
if c != c2 {
Err(SignError::Verify)
} else {
Ok(())
}
}

106
dilithium/src/symmetric.rs Normal file
View File

@ -0,0 +1,106 @@
use crate::fips202::*;
use crate::params::{CRHBYTES, SEEDBYTES};
#[cfg(feature = "aes")]
use crate::aes256ctr::*;
#[cfg(not(feature = "aes"))]
pub type Stream128State = KeccakState;
#[cfg(feature = "aes")]
pub type Stream128State = Aes256ctrCtx;
#[cfg(not(feature = "aes"))]
pub type Stream256State = KeccakState;
#[cfg(feature = "aes")]
pub type Stream256State = Aes256ctrCtx;
#[cfg(feature = "aes")]
pub const STREAM128_BLOCKBYTES: usize = AES256CTR_BLOCKBYTES;
#[cfg(not(feature = "aes"))]
pub const STREAM128_BLOCKBYTES: usize = SHAKE128_RATE;
#[cfg(feature = "aes")]
pub const STREAM256_BLOCKBYTES: usize = AES256CTR_BLOCKBYTES;
#[cfg(not(feature = "aes"))]
pub const STREAM256_BLOCKBYTES: usize = SHAKE256_RATE;
pub fn _crh(out: &mut [u8], input: &[u8], inbytes: usize) {
shake256(out, CRHBYTES, input, inbytes)
}
pub fn stream128_init(state: &mut Stream128State, seed: &[u8], nonce: u16) {
#[cfg(not(feature = "aes"))]
dilithium_shake128_stream_init(state, seed, nonce);
#[cfg(feature = "aes")]
dilithium_aes256ctr_init(state, seed, nonce)
}
pub fn stream128_squeezeblocks(
out: &mut [u8],
outblocks: u64,
state: &mut Stream128State,
) {
#[cfg(not(feature = "aes"))]
shake128_squeezeblocks(out, outblocks as usize, state);
#[cfg(feature = "aes")]
aes256ctr_squeezeblocks(out, outblocks, state);
}
pub fn stream256_init(state: &mut Stream256State, seed: &[u8], nonce: u16) {
#[cfg(not(feature = "aes"))]
dilithium_shake256_stream_init(state, seed, nonce);
#[cfg(feature = "aes")]
dilithium_aes256ctr_init(state, seed, nonce)
}
pub fn stream256_squeezeblocks(
out: &mut [u8],
outblocks: u64,
state: &mut Stream256State,
) {
#[cfg(not(feature = "aes"))]
shake256_squeezeblocks(out, outblocks as usize, state);
#[cfg(feature = "aes")]
aes256ctr_squeezeblocks(out, outblocks, state);
}
#[cfg(feature = "aes")]
pub fn dilithium_aes256ctr_init(
state: &mut Aes256ctrCtx,
key: &[u8],
nonce: u16,
) {
let mut expnonce = [0u8; 12];
expnonce[0] = nonce as u8;
expnonce[1] = (nonce >> 8) as u8;
aes256ctr_init(state, key, expnonce);
}
#[cfg(not(feature = "aes"))]
pub fn dilithium_shake128_stream_init(
state: &mut KeccakState,
seed: &[u8],
nonce: u16,
) {
let t = [nonce as u8, (nonce >> 8) as u8];
state.init();
shake128_absorb(state, seed, SEEDBYTES);
shake128_absorb(state, &t, 2);
shake128_finalize(state);
}
#[cfg(not(feature = "aes"))]
pub fn dilithium_shake256_stream_init(
state: &mut KeccakState,
seed: &[u8],
nonce: u16,
) {
let t = [nonce as u8, (nonce >> 8) as u8];
state.init();
shake256_absorb(state, seed, CRHBYTES);
shake256_absorb(state, &t, 2);
shake256_finalize(state);
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,100 @@
7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2D
4B622DE1350119C45A9F2E2EF3DC5DF50A759D138CDFBD64C81CC7CC2F513345
1D836E889E46259BCD1CCD2B369583C5B47CFBB919EC2B72C280247CB15A5569
539577CB7F2088FBEDFF1B53F235D607321857DB32BBA645F8DF3A89DD426552
2CA59C6CF33C53803749F69EF5ABFA9482FCEE7EFD87FBF17135ECC3FF3FD7F7
E17E72290E49A44C9C534F211195257CF13B0D45405782CEDA2D7F982A551721
3B7388E675DE5C59A78AF095481C7DD999C6EEA898595B1E7DCDA7EDC3A2C25C
DC9F40CABE2E8E4F3D1538FBC1ADA27B61B99081455AB0C4C41B5B3DA8101000
1DADE637AE98C393260F5BBBE288373100DD7AF37EBA913C528D2B7B998767CB
8866693CEE12B909E32A0C64381796633666417E1246B51A2643564B464B4113
D6DAD5B2746422F4487B72536D70DF88AF4B2F9040AA45999F8D7784EF696DA0
68E7818F33B97BA6166768C395BD010CEF7BCE9995891D164303B53C1123A991
35B153A7706109D4A13D7C4B26AA5B56D9E3FAC53B47E91B0C10BD4E0EAAFC19
0E1A1634FB2396E187CD8980EF29663C42DC3EF963CCD491F817A84283A11FA0
B0BFA060F1C1A70F1AC55E321E6186A6613605DD732574B5FE6E14F0FF6F7A82
A33BC0A7A08C13C0D4C1174DDD886AAC4C5666E1F4831F006C9519D36B2CE882
C7E33FA5329142B668CCDDE1057EB7A8619397537F2B4C6D6755B3B9FF936441
7611B5B7D4195D5F8B97244B6811748EFEA929EA272E66435A36D0BD16E3BF21
5A1E3E05C72CEF1A73EF98840DA035E4FD2552912DB8DAE28A79011DE4BBC1A4
8F3920A235EEC3659CFCFE62931474204EAE264959702F901D461B66D9BB563D
0B2B3EB50681403A0B9A99B25041A489C6D45D2A49DE0EC83E1FD10922ABE2D5
8217D32CD15658D39CDCA92C41B59F5780869A68838A3579DEA48B5E3EA768AA
CC625322C9D52898E7F60AE47BC2847E20F3722794DE41E30FDB20CA1A093208
950226D6AB0B774C5F439AFCFD0113B5DBF5905960C445F5E6E03E5D5C687A9A
A6B534767A6D839FD19075AE0BA10147C46862BF7BBCBE83F2B72F72F1368A1F
103164ED522DF0DB131C15E139C0F83D9B1B7A1B6ECF7F89A5248CAD7E68DE8C
BC962D978F38881085C1B813BC90EEE44AD9E7651681C20BA46402F557C454DE
C3DE54854A4060EA09ED92A363F71C7863EBA64195E9AC79E7AD7EB6A183CFAC
828B9804524BDD17D0EB387368B01B0E95B4960057ED63FC2289D858201E207E
4A84CA5C3954FAAFA11AE87FCBE701EBB5AFBCC5F8ECAE7786D10821E01ADA5A
3E74AE2B1D49EE6F149076F0BAE2D26A5CADFD5DE7BEF66DFCAE6B588A1F4067
39550BD2782D66FA95380F5F101D827377B11410F8BF3BCCFBE0E504FC09AE38
B18F0FDF9DC4F514107F88CC43FB29190608EBC5A2CD00B49FE20631761038DF
D4FA14DA39548392300A41BE413EBD53BD7BCBD045B4D3C8CA44ABC9599E269D
C796FD12D1FEB1DF46B162C38292684C09059E4463CB95DBDBF498A4DD4F7F00
A18A366A5ECACAE4732DC9E954333EAD153203013BAC4E3C50BEE15269F983FB
585E714D565AA66078BC2B12699F1E86C6FF30A1ABC8CBD19563BCDDD2F1F6D2
662CF70D3D5E95A9C6A33BD7C6ABF0E8CD23AB2D2D9420878C4835DE14A6C606
1924A71628292AA3D2D34EA72E2BFC2520864205F54EC6F19F7714733AA34CC9
EF7BA21809AE7E0BC3230B6061C5FEE206D805572CF1345198E1EF22A8FE7322
CB0B305FB54E1CB23B63EC1F6F4689137E5048D095FB3EADC854C852CA86BE93
F92FFA3A36F43F9177763AD320FD651D9357C6D99F09549FE6AF12943B58BE90
5D3CCE926A795ABC5F6632CABFF8BF66275DCC7E4A4AB3B8399D23E62A28BD16
BA2386BA92AA89049C64ECFE60FDDBE136815D3874527414B63ED32215F2E06F
B4E1AF25E8DC6934BA391A89984A358702BDD36838BABEBD982638703F20EEF8
A62875A3A6D305E120DC7975962552126CD844554857C2943872A4E524A6EEB5
76AE71DED1F9E73AF77A2FEAE4EEF80F87414DFB7580FB4AE0325BFF20D74A5D
A8F65BE046001A6814F537915BE3F03F3670E1169E4AAA6D7E726174ACAEC77C
802E08C14F6E3446BBF7F4666C8DDF7755DC718C3E02B7865FF33E9D8290ABEC
23D7A85A824DF3D904A511281A973C979F67F5BFAF3AB0546E85D0597F91120F
771CBB7C9FBD9FC5DB93E3E4DE6C034E58BE9BADE93748C42297142124696234
FA812D8CC3A9631A0239474EB93AD3A2A3480F2D973D3324228EF92A3B043163
C660B84D558A7E6B4EAC47C7B62135668E0EF0FBF74D514EAA3D0D428014282A
929F309AB3F90CDD9C21EB77A7CA762CA3AFCACBFE3E67B056290835694BA3D8
DAE1EBA78AD1568590348088AAE88C1ABEB59626EF65991CD76AB81198E52837
15ED428927A7EB0C7C2DC7A98CFBB77BDD773FA8747B8232A6EC4B87CD7DBCE1
BA7E359B1F669783521AD35EDABE97141A816C2FABF0AD0E001E21F73CCF7736
9DAEF95C8D5A61D3A3A267FEFB9F37D6E677D7BA26A3A5BFDBDA8C281BE89CCB
0531B4105CB209585F9FBC29CAA57E64C2D40F0829931A42CAF7701717D9096E
7F8515AA82DBC9EC8CF1DED5AB58EC0D08CF686E25A8C01FB1109A3C68D19E48
5AF3838060E0F83352A75A0EC4ACE2CE8BA119BF89F34CB4D6B8E27007CAE7FB
B323D48B567F7EFFFFD47A7C9ABF0ADD5F11141737A8AF62B56E042EE498AD6E
C1CF3107EA9B283419E27DC563ECCE950BEA78C048A3F49FB42128819959E51C
A50FC40F0D9EFA5D254943DC599F7DCC2F6D197A4D2666D5D69CFACCDA560817
4C0F0EF1CA8073A562D5414584EDF268913D53D5FB39FA639E02E900891EA82C
5BA8AD9B66C93CDE7E7E616A97FED8AA91BC7235FB4DB086CB4021877780C6B4
25D957B9BF68326D2EFEC93DA464F43E3DF16DD6571CEB1AB68BD58E87734A51
24F0CD3B05D964F82D3702BF0A613139808D49283286294BE57E13A983C3C961
5EF2732B63A1CBD2CC239EA6FED62F7FEB3102715A5BAFD8C83AAC33702FACCF
6A27B1666AB6FC4483D14CF84EDE49FEDFE05BB24E008AE8A01C52D83B8D40FE
2A855572E7E2ADD2888022BDB585B61577A75A31AA8ACEDE59ACB27EEB2936AE
7967E9DE70A7F95E69371F812C2FBF932CEC07AB4C235AE9E8A6799F3F537D36
5ABA340A8E541568FBEF49E77F94CFB4B3A5E9CF14C6755CE6412CF86CF62898
DF4853F482CC1D0B3A2D71E9EACA064E57C5D100DF79BD004BA81B43EACEC401
95DA16B844BAF559C2CD6E68B237614BB9927D90811106347B5849FEE2F48640
F258FF1178CC42A3CEBE238C8418B4974812A05F43B8FA95639CC46BC0738BC5
5EFBBBE99CF5C2B6830FB8E990250BE308E662200526889EA973C8D33823EC19
47A048D8799784F6EC385EB984E70C62CE7C8A107232871B69B99F7BF4C3DBB5
1D9E243A35118BC7C50A50746E1CF19C9FC310C7D54181FB95F44753EAB1B94A
0A46FDA6BA71125F3415E8BB6C2D8C00601107FA563E7F6386486A88F87701FB
5229DFE11090EFFBE94EE161054CD5FF58B31E23F567B282DB42EB1FE42E44AA
18AA77795AA6D7ADE8B6CDCED81A1959A8329677F042283DC8CA71E13EB3ADEE
4B2D6EC32BE9C5D8FA11F3FC0008F4F26B945064D98362AD912F452692AC383D
196B8144DFBFB47EC01E96A6B8443211D6C9C4AA7853A8131B5218349BD6D953
C171793029D0CBAF8D2661A823243AD50D67F2619533180F25B50C94B1310389
615FA91F3D206B908649399F216950EC7B2420EB04AEC6ABFCB7B4528E8E33E6
626C19B8553B2D9E5A47A758615D80B15BE11FD016D3A1962D8DE58ED5CA2219
B0D4BA39ADB4E8712B3A3E6495ABA2F04A29E45C68671A960BC0D8D89900C97E
E04328A783C10DACD96702D2E726BB11CE4ECC571564CE7CB10722D1C98C2842
CEE2E53099D8CB576F8F76C5C155470F87A6EC5F7D73256A0A2AEE62CBC53597
02D9755C369932E7F99A2E1614B03E2C86D713563785965E008BA987A6C89F49
2B2BF7CBB0957A86BBEC97001B60C7C6AD98A56E94542FF561F78FED211DA755
049569CC5FB969C6EEBB8AEA1AF1FCAF46F8A9E6CD6C796FC7193592BCA9CF23
9D89B9A327DF0D341CD2968BA9218BBC3E934502CD88919D8BB16DD3D39FEBF7
4544C2F21054605B0EEE46F62A87DFCBC3BFEC473B9850886266F478BF9E33D7
D9931E321732BD82EC9CA1DF12BA48549BFC7D3E76A404B71892F4198777FFBB
F838451E4A5929B8BAE9084B40B1DC0EDFB76A9354BF27F981960C88B0BA3A11
8E4334B2589D0CAECF0FD9BA584EA26A4123D4543A8A0FE126D4A7E07F6067AF
37519A02E8021F2257259C0D2E499AF3533C8ED8DD5BF7751CCE920D79B518FA
690482BFF6C1D0BA6C071DD395ADF69E55E1BFC4E0992A8650FFB5E60A02B172

View File

@ -0,0 +1,18 @@
use pqc_dilithium::*;
#[test]
fn sign_then_verify_valid() {
let msg = b"Hello";
let keys = Keypair::generate();
let signature = keys.sign(msg);
assert!(verify(&signature, msg, &keys.public).is_ok())
}
#[test]
fn sign_then_verify_invalid() {
let msg = b"Hello";
let keys = Keypair::generate();
let mut signature = keys.sign(msg);
signature[..4].copy_from_slice(&[255u8; 4]);
assert!(verify(&signature, msg, &keys.public).is_err())
}

61
dilithium/tests/kat.rs Normal file
View File

@ -0,0 +1,61 @@
#![cfg(all(dilithium_kat, not(feature = "random_signing")))]
use pqc_core::load::*;
use pqc_dilithium::*;
use std::path::PathBuf;
const MODE: u8 = if cfg!(feature = "mode2") {
2
} else if cfg!(feature = "mode5") {
5
} else {
3
};
const AES: &str = if cfg!(feature = "aes") { "-AES" } else { "" };
#[test]
fn keypair() {
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let filename = format!("PQCsignKAT_Dilithium{}{}.rsp", MODE, AES);
let katvec = kats(&mut path.clone(), &filename);
let bufvec = bufs(&mut path, "SeedBuffer_Dilithium");
for (i, kat) in katvec.iter().enumerate() {
let pk = kat.pk.clone();
let sk = kat.sk.clone();
let mut pk2 = [0u8; PUBLICKEYBYTES];
let mut sk2 = [0u8; SECRETKEYBYTES];
crypto_sign_keypair(&mut pk2, &mut sk2, Some(&bufvec[i]));
assert_eq!(pk, pk2);
assert_eq!(sk, sk2);
}
}
#[test]
pub fn sign() {
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let filename = format!("PQCsignKAT_Dilithium{}{}.rsp", MODE, AES);
let katvec = kats(&mut path, &filename);
for kat in katvec {
let sm = kat.sm.clone();
let msg = kat.msg.clone();
let sk = kat.sk.clone();
let mut sig = vec![0u8; SIGNBYTES];
crypto_sign_signature(&mut sig, &msg, &sk);
assert_eq!(sm[..SIGNBYTES], sig);
}
}
#[test]
pub fn verify() {
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let filename = format!("PQCsignKAT_Dilithium{}{}.rsp", MODE, AES);
let katvec = kats(&mut path, &filename);
for kat in katvec {
let sm = kat.sm.clone();
let msg = kat.msg.clone();
let pk = kat.pk.clone();
let res = crypto_sign_verify(&sm[..SIGNBYTES], &msg, &pk);
assert!(res.is_ok());
}
}

View File

@ -0,0 +1,26 @@
#!/bin/bash
set -e
# Print test header
announce(){
title="# $1 #"
edge=$(echo "$title" | sed 's/./#/g')
echo -e "\n\n$edge"; echo "$title"; echo "$edge";
}
# Keep existing RUSTFLAGS
RUSTFLAGS=${RUSTFLAGS:-""}
RUSTFLAGS+=" --cfg dilithium_kat"
MODE=("mode2" "mode3" "mode5")
for mode in ${MODE[@]}; do
announce "Dilithium $mode"
RUSTFLAGS=$RUSTFLAGS cargo test --features "$mode"
announce "Dilithium $mode AES"
RUSTFLAGS=$RUSTFLAGS cargo test --features "$mode aes"
announce "Dilithium $mode Random Signing"
RUSTFLAGS=$RUSTFLAGS cargo test --features "$mode random_signing"
done

7
rustfmt.toml Normal file
View File

@ -0,0 +1,7 @@
unstable_features = true
wrap_comments = true
comment_width = 100
control_brace_style = "AlwaysSameLine"
fn_params_layout = "Compressed"
reorder_imports = true
imports_layout = "Vertical"

26
scripts/build.sh Executable file
View File

@ -0,0 +1,26 @@
#!/bin/bash
# This is a script to automatically create musl-linked
# executables so kdt can run on any system (any linux
# system*), regardless of the included libc. This doesn't
# do command existence checking, so it WILL fail if you're
# missing the dependencies!
_main () {
rm -rf dist
mkdir dist
echo "Building with \`cargo build --release --target=x86_64-unknown-linux-musl\`..."
cargo build --release --target=x86_64-unknown-linux-musl
echo "Successfully built musl release for kdt!"
echo "Packing into .tar.gz..."
mkdir kdt
cd kdt
cp ../../target/x86_64-unknown-linux-musl/release/kdt .
cd ..
tar -czf "kdt_$(cargo get version --pretty).tar.gz" ./kdt/
mv "kdt_$(cargo get version --pretty).tar.gz" dist
echo "Successfully packed executable into tar archive! View it at ./dist/kdt_$(cargo get version --pretty).tar.gz."
rm -rf kdt
}
_main

30
scripts/fmt.sh Executable file
View File

@ -0,0 +1,30 @@
#!/bin/bash
# Since our our `rustfmt.toml` uses some nightly formatting features, you can
# invoke this shell script as shorthand for everything you'd normally need to
# manually do (configure rustup, add the nightly profile, etc...)
_command_exists () {
command -v "${1}" 2>/dev/null >&2
}
_main () {
# get rustup if it's not installed
if ! _command_exists "rustup"; then
# panic if `curl` isn't installed
if ! _command_exists "curl"; then
echo "You need to install \`curl\`."
exit 1
fi
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
fi
# alias for easy usage
alias rustup="$HOME/.cargo/bin/rustup"
rustup toolchain install nightly-x86_64-unknown-linux-gnu &>/dev/null
echo "Formatting code..."
rustup run nightly cargo fmt
echo "Formatted code!"
}
_main

88
src/arguments.rs Normal file
View File

@ -0,0 +1,88 @@
// -- imports --
use crate::core::*;
use clap::Parser;
use std::error::Error;
// -- clap options --
/// Mirai's experimental, quantum-safe successor to GPG
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
pub struct Args {
/// Generates a new KDT owned key set and stores it in the
/// local owned key database
#[arg(short, long)]
pub gen_key: bool,
/// Imports a KDT public key from stdin and stores it in the
/// local public key database
#[arg(short, long)]
pub import: bool,
/// Launches KDT in decryption mode
#[arg(short, long, value_name = "PRIVATE_KEY_ID")]
pub decrypt: Option<String>,
/// Launches KDT in encryption mode
#[arg(short, long, value_name = "PUBLIC_KEY_ID")]
pub encrypt: Option<String>,
/// Lists all keys in the public key database
#[arg(long)]
pub list_keys: bool,
/// Lists all keys in the owned key database
#[arg(short, long)]
pub list_key_pairs: bool,
/// Exports the public key in the owned key set containing
/// a private key of id `PRIVATE_KEY_ID`
#[arg(long, value_name = "PRIVATE_KEY_ID")]
pub export_pubkey: Option<String>,
/// Removes the public key with id `PUBLIC_KEY_ID`
/// from the public key database
#[arg(long, value_name = "PUBLIC_KEY_ID")]
pub del_pubkey: Option<String>,
/// Removes the owned key set with private key of
/// id `PRIVATE_KEY_ID` from the owned key database
#[arg(long, value_name = "PRIVATE_KEY_ID")]
pub del_keyset: Option<String>,
/// Signs a message with the given private key
#[arg(short, long, value_name = "PRIVATE_KEY_ID")]
pub sign: Option<String>,
/// Verifies the integrity of the given signed message
/// against the given public key
#[arg(short, long, value_name = "PUBLIC_KEY_ID")]
pub verify: Option<String>,
}
impl Args {
pub fn get_num_called(&self) -> usize {
[
self.gen_key,
self.import,
self.list_keys,
self.list_key_pairs,
self.encrypt.is_some(),
self.export_pubkey.is_some(),
self.del_pubkey.is_some(),
self.del_keyset.is_some(),
self.decrypt.is_some(),
self.sign.is_some(),
self.verify.is_some(),
]
.iter()
.filter(|&b| *b)
.count()
}
pub fn fail_if_invalid(&self) -> Result<(), Box<dyn Error>> {
if self.get_num_called() > 1 {
return Err(Box::new(KdtErr::TooManyArgs));
}
Ok(())
}
}

154
src/core/crypto/mod.rs Normal file
View File

@ -0,0 +1,154 @@
// -- imports --
use crate::core::*;
use aes_gcm::{
aead::{
Aead,
AeadCore,
KeyInit,
OsRng,
},
Aes256Gcm,
Key,
};
use generic_array::GenericArray;
use pqc_kyber::{
decapsulate,
encapsulate,
SecretKey,
};
use std::{
error::Error,
fmt,
};
// -- base crypto handling --
/// Core cryptography handler for KDT. Handles everything when
/// it comes to AES and Kyber. Signatures are handled by the
/// `KdtSignageHandler` though.
pub struct KdtCryptoHandler;
impl KdtCryptoHandler {
/// Encrypts a string of text against the provided Kyber
/// public key. We use AES in the backend here because
/// the way Kyber works is that it establishes a shared
/// symmetric key inside of the asymmetric stuff. Magic!
pub fn encrypt_text(text: String, pubkey_str: String) -> Result<Message, Box<dyn Error>> {
let pubkey = PubKeyPair::from_str(pubkey_str).init();
let mut rng = rand::thread_rng();
let (encrypted_secret, secret_bytes) =
encapsulate(&Base64::decode_string(pubkey.crypto_key), &mut rng)?;
let key = Key::<Aes256Gcm>::from_slice(&secret_bytes);
let cipher = Aes256Gcm::new(&key);
let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
let encrypted_message =
Base64::encode_bytes(&cipher.encrypt(&nonce, text.as_ref()).unwrap());
let encoded_nonce = Base64::encode_bytes(&nonce.into_iter().collect::<Vec<u8>>());
Ok(Message::new(
Base64::encode_bytes(&encrypted_secret),
encrypted_message,
encoded_nonce,
))
}
/// Decrypts a pre-deserialized `Message` object with the
/// provided private key. Note that, as stated above, this uses
/// AES under the hood because of the magic way Kyber works -
/// a shared symmetric key is established using the asymmetric
/// keys, and then both parties can encrypt sensitive data
/// with that! Pure magic, obviously.
pub fn decrypt_msg(message: Message, privkey_str: String) -> String {
// Uses the private key we have to decrypt the symmetric
// shared secret.
let secret_bytes = decapsulate(
&Base64::decode_string(message.encrypted_secret),
&Base64::decode_string(privkey_str),
)
.expect("You used the wrong private key!");
let key = Key::<Aes256Gcm>::from_slice(&secret_bytes);
let cipher = Aes256Gcm::new(&key);
let nonce = Base64::decode_string(message.nonce);
// Converts the raw text bytes to a UTF-8 encoded string.
String::from_utf8_lossy(
&cipher
.decrypt(
&GenericArray::clone_from_slice(&nonce),
Base64::decode_string(message.encrypted_message).as_ref(),
)
.unwrap(),
)
.into()
}
}
pub struct Message {
/// The shared secret established by Kyber, encrypted
/// asymmetrically so only you can see it.
pub encrypted_secret: String,
/// The actual encrypted data. This is a base64 encoded
/// representation of the shifted bytes.
pub encrypted_message: String,
/// The AES-GCM nonce. From
/// https://cryptography.io/en/latest/hazmat/primitives/symmetric-encryption/#cryptography.hazmat.primitives.ciphers.algorithms.ChaCha20:
/// `The nonce does not need to be kept secret and may be included with the ciphertext.`
pub nonce: String,
}
impl Message {
/// Creates a new `Message` object from the given encrypted secret,
/// encrypted message, and unencrypted nonce.
pub fn new(encrypted_secret: String, encrypted_message: String, nonce: String) -> Self {
Self {
encrypted_secret,
encrypted_message,
nonce,
}
}
/// Restores a `Message` object from the given message string.
#[inline(always)]
pub fn from_str(message: String) -> Self {
let message_split: Vec<String> = message
.chars()
.skip(27)
.take(message.len() - 27 - 26)
.collect::<String>()
.replace("\n", "")
.split('*')
.map(String::from)
.collect();
Self {
encrypted_secret: message_split[0].to_owned(),
encrypted_message: message_split[1].to_owned(),
nonce: message_split[2].to_owned(),
}
}
}
impl fmt::Display for Message {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let message = format!(
"{}*{}*{}",
self.encrypted_secret, self.encrypted_message, self.nonce
)
.chars()
.enumerate()
.flat_map(|(i, c)| {
if (i + 1) % 64 == 0 {
vec![c, '\n']
} else {
vec![c]
}
})
.collect::<String>();
write!(
f,
"-----BEGIN KDT MESSAGE-----\n{}\n-----END KDT MESSAGE-----",
message.trim_end()
)
}
}

View File

@ -0,0 +1,26 @@
// -- imports --
use base64::{
engine::general_purpose,
Engine as _,
};
use std::fmt;
// -- simple base64 interface --
/// Incredibly minimalist base64 conversion object. Can
/// encode bytes, and decode strings. That's it, because that's
/// all we need.
pub struct Base64;
impl Base64 {
/// Converts a bytearray to a base64 string.
#[inline(always)]
pub fn encode_bytes(b: &[u8]) -> String {
general_purpose::STANDARD.encode(b)
}
/// Converts a base64 string to a bytearray.
#[inline(always)]
pub fn decode_string<S: fmt::Display>(s: S) -> Vec<u8> {
general_purpose::STANDARD.decode(s.to_string()).unwrap()
}
}

3
src/core/encoding/mod.rs Normal file
View File

@ -0,0 +1,3 @@
pub mod base64;
pub use self::base64::*;

36
src/core/errors.rs Normal file
View File

@ -0,0 +1,36 @@
// -- imports --
use core::fmt;
use std::error::Error;
// -- error struct --
#[derive(Debug)]
/// Custom error struct. Helps ensure error handling
/// doesn't get *too* ugly.
pub enum KdtErr {
TooManyArgs,
PubDbOpenFailed,
PrivDbOpenFailed,
DbDumpFailed,
KeyAlreadyExists,
}
impl fmt::Display for KdtErr {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::TooManyArgs => write!(
f,
"Too many arguments were passed! You can only use one argument at a time."
),
Self::PubDbOpenFailed => write!(f, "Failed to open public keys database!"),
Self::PrivDbOpenFailed => write!(f, "Failed to open private keys database!"),
Self::DbDumpFailed => write!(f, "Failed to dump to database!"),
Self::KeyAlreadyExists => write!(f, "This key already exists in the database!"),
}
}
}
// Pointless trait implementation to ensure `KdtErr` is
// considered an `Error` type so you can return it
// in a function that returns eg `Result<(), Box<dyn Error>>`.
impl Error for KdtErr {}

69
src/core/keys/database.rs Normal file
View File

@ -0,0 +1,69 @@
// -- imports --
use crate::core::*;
use serde::{
Deserialize,
Serialize,
};
// -- public key database --
#[derive(Serialize, Deserialize)]
/// A database of public keys stored locally in
/// `pubkeys.ron`.
pub struct PubKeyDb {
/// A list of public key objects.
pub keys: Vec<PubKeyPair>,
}
impl PubKeyDb {
/// Takes in the hexadecimal string id of a public key, and returns
/// the public key object.
pub fn get_by_id(&self, id: String) -> PubKeyPair {
let filtered = self.keys.iter().filter(|k| k.id == id).collect::<Vec<_>>();
if filtered.len() > 1 {
unreachable!();
} else {
filtered[0].clone()
}
}
/// Shorthand for `self.keys.is_empty()` to avoid deep nesting
/// and pointlessly annoying function access.
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}
}
// -- owned key database --
#[derive(Serialize, Deserialize)]
/// A database of owned keys (ie private-public pairs) stored
/// locally in `ownedkeys.ron`. Remember, `ownedkeys.ron` is for your
/// eyes only - don't let anyone else see it!
pub struct OwnedKeyDb {
/// A list of private- and public-key pairs that you're in control of.
pub keys: Vec<OwnedKeySet>,
}
impl OwnedKeyDb {
/// Takes in the hexadecimal string id of a private key,
/// and returns the private-public key pair.
pub fn get_by_id(&self, id: String) -> OwnedKeySet {
let filtered = self
.keys
.iter()
.filter(|k| k.privkey_pair.id == id)
.collect::<Vec<_>>();
if filtered.len() > 1 {
unreachable!();
} else {
filtered[0].clone()
}
}
/// Shorthand for `self.keys.is_empty()` to avoid deep nesting
/// and pointlessly annoying function access.
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}
}

46
src/core/keys/key.rs Normal file
View File

@ -0,0 +1,46 @@
// -- imports --
use crate::core::*;
use pqc_dilithium::Keypair as dilithium_keypair;
use pqc_kyber::keypair as kyber_keypair;
// -- fully controlled keyset (privkey pair + pubkey pair) --
#[derive(Serialize, Deserialize, Clone)]
pub struct OwnedKeySet {
pub pubkey_pair: PubKeyPair,
pub privkey_pair: PrivKeyPair,
}
impl OwnedKeySet {
/// Generates a new key set (public, private; encryption, signage) on-demand.
/// No errors should occur here, but if they do they probably aren't our fault
/// (take a look at the libraries we use - they're probably the culprit!)
pub fn generate(owner_name: String) -> Self {
let encryption_keys = kyber_keypair(&mut rand::thread_rng());
let signage_keys = dilithium_keypair::generate();
let pubkey_pair = PubKeyPair::new(
Base64::encode_bytes(&encryption_keys.public),
Base64::encode_bytes(&signage_keys.public),
Base64::encode_bytes(owner_name.as_bytes()),
)
.init();
let privkey_pair = PrivKeyPair::new(
Base64::encode_bytes(&encryption_keys.secret),
Base64::encode_bytes(&signage_keys.expose_secret()),
Base64::encode_bytes(owner_name.as_bytes()),
)
.init();
Self {
pubkey_pair,
privkey_pair,
}
}
/// Derives an `OwnedKeySet` from a public- and private-key base64 string pair.
pub fn from_strs(pubkey_pair_str: String, privkey_pair_str: String) -> Self {
Self {
pubkey_pair: PubKeyPair::from_str(pubkey_pair_str).init(),
privkey_pair: PrivKeyPair::from_str(privkey_pair_str).init(),
}
}
}

13
src/core/keys/mod.rs Normal file
View File

@ -0,0 +1,13 @@
// -- compiler flags --
#![allow(dead_code)]
// -- local modules --
pub mod database;
pub mod key;
pub mod privkey;
pub mod pubkey;
pub use database::*;
pub use key::*;
pub use privkey::*;
pub use pubkey::*;

113
src/core/keys/privkey.rs Normal file
View File

@ -0,0 +1,113 @@
// -- imports --
use serde::{
Deserialize,
Serialize,
};
use sha2::{
Digest,
Sha256,
};
use std::fmt;
// -- private key pair (signing key + crypto key) --
#[derive(Serialize, Deserialize, Clone)]
pub struct PrivKeyPair {
/// Base64-encoded bytes for private cryptographic
/// (ie Kyber) key.
pub crypto_key: String,
/// Base64-encoded bytes for private signage
/// (ie Dilithium) key.
pub signage_key: String,
/// Base64-encoded representation of the key
/// owner's name.
pub owner: String,
/// Sha256 hashsum of this object when the two
/// above values are set. On initialization, this is
/// `String::new()`, but a `PrivKeyPair::init()` call
/// generates it on-demand. Ids are very important for key
/// identification, so you should *always* call `PrivKeyPair::init()`
/// after `PrivKeyPair::new()`.
pub id: String,
}
impl PrivKeyPair {
/// Creates a new `PrivKeyPair` object from the provided cryptographic key
/// base64 string, the provided signage key string, and the owner base64
/// string. This doesn't validate the passed inputs, so it *will* panic if
/// you pass bad inputs.
pub fn new(crypto_key: String, signage_key: String, owner: String) -> Self {
Self {
crypto_key,
signage_key,
owner,
id: String::new(),
}
}
/// Computes a hash for the key pair, then sets the id as the hash. This
/// helps maintain distinctness between key ids.
#[inline(always)]
pub fn init(mut self) -> Self {
let mut hasher = Sha256::new();
hasher.update(self.to_string());
self.id = format!("{:X}", hasher.finalize());
self
}
/// Derives a `PrivKeyPair` object from the provided KDT private
/// key string. Doesn't validate input, so it *will* panic if you pass
/// invalid inputs.
pub fn from_str(privkey_str: String) -> Self {
let privkey: Vec<String> = privkey_str
.chars()
// Removes the `-----BEGIN KDT PRIVKEY BLOCK-----` header.
.skip(33)
// Removes the `-----END KDT PRIVKEY BLOCK-----` footer.
.take(privkey_str.len() - 33 - 32)
.collect::<String>()
// Turns the human-readable formatting to something that can be parsed
// programmatically.
.replace("\n", "")
// Splits the private key into a cryptographic key and signage key.
.split('*')
.map(String::from)
.collect();
Self {
crypto_key: privkey[0].to_owned(),
signage_key: privkey[1].to_owned(),
owner: privkey[2].to_owned(),
id: String::new(),
}
}
}
// -- human-readable key output impl --
impl fmt::Display for PrivKeyPair {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// An asterisk separates the encryption key from the
// signing key during key exchanges.
let keypair = format!("{}*{}*{}", &self.crypto_key, &self.signage_key, &self.owner)
.chars()
.enumerate()
// This helps maintain readability when printing messages. It
// inserts a new line at every nth (n = multiple of 64) character.
// This is identical to GPG's output style.
.flat_map(|(i, c)| {
if (i + 1) % 64 == 0 {
vec![c, '\n']
} else {
vec![c]
}
})
.collect::<String>();
write!(
f,
"-----BEGIN KDT PRIVKEY BLOCK-----\n{}\n-----END KDT PRIVKEY BLOCK-----",
keypair.trim_end()
)
}
}

113
src/core/keys/pubkey.rs Normal file
View File

@ -0,0 +1,113 @@
// -- imports --
use serde::{
Deserialize,
Serialize,
};
use sha2::{
Digest,
Sha256,
};
use std::fmt;
// -- public key pair (signing key + crypto key) --
#[derive(Serialize, Deserialize, Clone)]
pub struct PubKeyPair {
/// Base64-encoded bytes for public cryptographic
/// (ie Kyber) key.
pub crypto_key: String,
/// Base64-encoded bytes for public signage
/// (ie Dilithium) key.
pub signage_key: String,
/// Base64-encoded representation of the key
/// owner's name.
pub owner: String,
/// Sha256 hashsum of this object when the two
/// above values are set. On initialization, this is
/// `String::new()`, but a `PrivKeyPair::init()` call
/// generates it on-demand. Ids are very important for key
/// identification, so you should *always* call `PrivKeyPair::init()`
/// after `PrivKeyPair::new()`.
pub id: String,
}
impl PubKeyPair {
/// Creates a new `PubKeyPair` object from the provided cryptographic key
/// base64 string, the provided signage key string, and the owner base64
/// string. This doesn't validate the passed inputs, so it *will* panic if
/// you pass bad inputs.
pub fn new(crypto_key: String, signage_key: String, owner: String) -> Self {
Self {
crypto_key,
signage_key,
owner,
id: String::new(),
}
}
/// Computes a hash for the key pair, then sets the id as the hash. This
/// helps maintain distinctness between key ids.
#[inline(always)]
pub fn init(mut self) -> Self {
let mut hasher = Sha256::new();
hasher.update(self.to_string());
self.id = format!("{:X}", hasher.finalize());
self
}
/// Derives a `PubKeyPair` object from the provided KDT public
/// key string. Doesn't validate input, so it *will* panic if you pass
/// invalid inputs.
pub fn from_str(pubkey_str: String) -> Self {
let pubkey: Vec<String> = pubkey_str
.chars()
// Removes the `-----BEGIN KDT PUBKEY BLOCK-----` header.
.skip(32)
// Removes the `-----END KDT PRIVKEY BLOCK-----` footer.
.take(pubkey_str.len() - 32 - 31)
.collect::<String>()
// Turns the human-readable formatting to something that can be parsed
// programmatically.
.replace("\n", "")
// Splits the public key into a cryptographic key and signage key.
.split('*')
.map(String::from)
.collect();
Self {
crypto_key: pubkey[0].to_owned(),
signage_key: pubkey[1].to_owned(),
owner: pubkey[2].to_owned(),
id: String::new(),
}
}
}
// -- human-readable key output impl --
impl fmt::Display for PubKeyPair {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// An asterisk separates the encryption key from the
// signing key during key exchanges.
let keypair = format!("{}*{}*{}", &self.crypto_key, &self.signage_key, &self.owner)
.chars()
.enumerate()
// This helps maintain readability when printing messages. It
// inserts a new line at every nth (n = multiple of 64) character.
// This is identical to GPG's output style.
.flat_map(|(i, c)| {
if (i + 1) % 64 == 0 {
vec![c, '\n']
} else {
vec![c]
}
})
.collect::<String>();
write!(
f,
"-----BEGIN KDT PUBKEY BLOCK-----\n{}\n-----END KDT PUBKEY BLOCK-----",
keypair.trim_end()
)
}
}

94
src/core/logging/log.rs Normal file
View File

@ -0,0 +1,94 @@
/*
* The MIT License (MIT)
* Copyright (c) 2023-present Artemis Mirai <artemismirai@waifu.club>
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
// -- imports --
use colored::*;
use std::{
fmt::Display,
io::{
self,
Read,
Write,
},
process,
};
/// Base styled logs object. Object stores `debug`
/// boolean which dictates whether or not anything should
/// actually be logged to stdout or not.
pub struct Logger {
debug: bool,
}
// -- core implementation --
impl Logger {
/// Creates a new `Logger` object based on a
/// `debug` boolean that determines whether or
/// not anything should actually be logged.
pub fn new(debug: bool) -> Self {
Self { debug }
}
/// Writes information to stdout.
pub fn info<S: Display>(&self, s: S) {
if self.debug {
println!("{} {}", "(info)".bright_yellow(), s);
}
}
/// Writes a warn to stdout.
pub fn warn<S: Display>(&self, s: S) {
if self.debug {
println!("{} {}", "(warn)".bright_red(), s);
}
}
/// Writes a fatal log to stdout, then panics. Doesn't
/// care if the logger is in debug mode or not because
/// users should always see fatal logs.
pub fn fatal<S: Display>(&self, s: S) -> ! {
println!("{} {}", "(FATAL)".red(), s);
process::exit(1);
}
/// Writes a success message to stdout.
pub fn success<S: Display>(&self, s: S) {
if self.debug {
println!("{} {}", "(success)".bright_green(), s);
}
}
/// Gets user input from stdin.
pub fn input(&self) -> String {
let mut i = String::new();
io::stdout()
.flush()
.expect("Failed to flush standard output!");
io::stdin()
.read_to_string(&mut i)
.expect("Failed to read input!");
i.trim().to_string()
}
}

26
src/core/logging/mod.rs Normal file
View File

@ -0,0 +1,26 @@
/*
* The MIT License (MIT)
* Copyright (c) 2023-present Artemis Mirai <artemismirai@waifu.club>
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
pub mod log;
pub use log::*;

237
src/core/mod.rs Normal file
View File

@ -0,0 +1,237 @@
// -- compiler flags --
#![allow(unused_imports)]
// -- local modules (+ exports) --
pub mod crypto;
pub mod encoding;
pub mod errors;
pub mod keys;
pub mod logging;
pub mod signing;
pub use crypto::*;
pub use encoding::*;
pub use errors::*;
pub use keys::*;
pub use logging::*;
pub use signing::*;
// -- external imports --
use ron::{
de::from_reader,
ser::{
to_string_pretty,
PrettyConfig,
},
};
use serde::{
Deserialize,
Serialize,
};
use std::{
error::Error,
fmt,
fs::{
self,
File,
},
io::{
Read,
Write,
},
path::Path,
};
// -- core kdt object --
/// Highest level KDT object. Handles encryption, decryption,
/// signing, input validation, key importation, database loading,
/// etc.
pub struct CoreKdtHandler {
/// Public key database, loaded in ram from `pubkeys.ron`.
pub pubkey_db: PubKeyDb,
/// Owned key database, loaded in ram from `ownedkeys.ron`.
pub ownedkey_db: OwnedKeyDb,
}
impl CoreKdtHandler {
/// Creates a new `CoreKdtHandler` by loading the necessary
/// databases, and creating them if they don't exist.
pub fn new() -> Result<Self, Box<dyn Error>> {
if !Path::new("pubkeys.ron").exists() {
let mut f = File::create("pubkeys.ron").unwrap();
f.write_all(
r#"(
keys: []
)"#
.as_bytes(),
)
.unwrap();
}
if !Path::new("ownedkeys.ron").exists() {
let mut f = File::create("ownedkeys.ron").unwrap();
f.write_all(
r#"(
keys: []
)"#
.as_bytes(),
)
.unwrap();
}
let pubkey_db: PubKeyDb = match File::open("pubkeys.ron") {
Ok(f) => match from_reader(f) {
Ok(database) => database,
Err(_) => return Err(Box::new(KdtErr::PubDbOpenFailed)),
},
Err(_) => return Err(Box::new(KdtErr::PubDbOpenFailed)),
};
let ownedkey_db: OwnedKeyDb = match File::open("ownedkeys.ron") {
Ok(f) => match from_reader(f) {
Ok(database) => database,
Err(_) => return Err(Box::new(KdtErr::PrivDbOpenFailed)),
},
Err(_) => return Err(Box::new(KdtErr::PrivDbOpenFailed)),
};
Ok(Self {
pubkey_db,
ownedkey_db,
})
}
/// Generates a new owned key set on demand, then
/// appends that new keyset to the owned key database.
pub fn gen_key(&mut self, name: String) -> String {
let key = OwnedKeySet::generate(name);
self.ownedkey_db.keys.push(key.clone());
key.privkey_pair.id
}
/// Removes the public key with the specified id from the
/// public key database in memory.
pub fn del_pubkey(&mut self, keyid: String) {
self.pubkey_db.keys = self
.pubkey_db
.keys
.iter()
.filter(|x| x.id != keyid)
.map(|k| k.to_owned())
.collect();
}
/// Removes the owned key set with the specified id from the
/// owned key database in memory.
pub fn del_ownedkey(&mut self, keyid: String) {
self.ownedkey_db.keys = self
.ownedkey_db
.keys
.iter()
.filter(|k| k.privkey_pair.id != keyid)
.map(|k| k.to_owned())
.collect();
}
/// Adds the given public key to the in-memory key
/// database.
pub fn register_pubkey<S: fmt::Display>(
&mut self, pubkey_str: S,
) -> Result<String, Box<dyn Error>> {
// Construct a public key using the given string
let pubkey = PubKeyPair::from_str(pubkey_str.to_string()).init();
// Make sure this public key isn't already registered to the database
if self
.pubkey_db
.keys
.iter()
.filter(|k| k.id == pubkey.id)
.collect::<Vec<_>>()
.len()
!= 0
{
Err(Box::new(KdtErr::KeyAlreadyExists))
} else {
// Add it to the database, then return the id
self.pubkey_db.keys.push(pubkey.clone());
Ok(pubkey.id)
}
}
/// Dumps the public- and owned-key-databases to their
/// respective files.
pub fn dump_db(self) -> Result<(), Box<dyn Error>> {
let pretty = PrettyConfig::new()
.depth_limit(6)
.separate_tuple_members(true);
match File::create("pubkeys.ron") {
Ok(ref mut f) => f
.write_all(to_string_pretty(&self.pubkey_db, pretty.clone())?.as_bytes())
.unwrap(),
Err(_) => return Err(Box::new(KdtErr::DbDumpFailed)),
}
match File::create("ownedkeys.ron") {
Ok(ref mut f) => f
.write_all(format!("// This file contains the private-key-public-key pairs for your owned keys. These are used for decryption and signing. Don't share this file's contents with anyone!\n{}", to_string_pretty(&self.ownedkey_db, pretty)?).as_bytes())
.unwrap(),
Err(_) => return Err(Box::new(KdtErr::DbDumpFailed)),
}
Ok(())
}
/// Encrypts the given message against the public key of the given
/// id.
pub fn encrypt(&self, pubkey_id: String, text: String) -> String {
let public_key = self.pubkey_db.get_by_id(pubkey_id).to_string();
KdtCryptoHandler::encrypt_text(text, public_key)
.unwrap()
.to_string()
}
/// Decrypts the given message with the private key of the given id.
pub fn decrypt(&self, privkey_id: String, message: String) -> String {
let message = Message::from_str(message);
let private_key = self
.ownedkey_db
.get_by_id(privkey_id)
.privkey_pair
.crypto_key;
KdtCryptoHandler::decrypt_msg(message, private_key).to_string()
}
/// Signs the given message with the private key of the given id.
pub fn sign(&self, privkey_id: String, text: String) -> String {
let signing_pubkey = self
.ownedkey_db
.get_by_id(privkey_id.clone())
.pubkey_pair
.signage_key;
let signing_privkey = self
.ownedkey_db
.get_by_id(privkey_id.clone())
.privkey_pair
.signage_key;
KdtSignageHandler::sign_text(text, signing_privkey, signing_pubkey).to_string()
}
/// Verifies the given KDT-signed message with the public key of the
/// given id.
pub fn verify(&self, pubkey_id: String, full_text: String) -> Option<bool> {
let verification_pubkey = self.pubkey_db.get_by_id(pubkey_id).signage_key;
let parts: Vec<String> = full_text
.chars()
.skip(35)
.take(full_text.len() - 35 - 27)
.collect::<String>()
.split("-----BEGIN KDT SIGNATURE-----")
.map(|x| x.trim())
.map(String::from)
.collect();
let text = parts.first()?.trim().to_owned();
let signature = parts.last()?.replace("\n", "");
Some(KdtSignageHandler::verify(
text,
signature,
verification_pubkey,
))
}
}

6
src/core/signing/mod.rs Normal file
View File

@ -0,0 +1,6 @@
// -- compiler flags --
#![allow(dead_code)]
pub mod signatures;
pub use signatures::*;

View File

@ -0,0 +1,42 @@
// -- imports --
use crate::core::*;
use pqc_dilithium::verify as dilithium_verify;
use pqc_dilithium::Keypair;
// -- signage handler struct --
/// Base Dilithium signature handler object. Incredibly WIP.
pub struct KdtSignageHandler;
impl KdtSignageHandler {
/// Generates a KDT Dilithium signature from a passed message
/// and a key pair, then formats the message with the signature
/// in a visually appealing way (mostly just stole GPG's output
/// styling).
#[inline(always)]
pub fn sign_text(text: String, privkey_str: String, pubkey_str: String) -> String {
let signkey = Keypair::restore_from_keys(
Base64::decode_string(pubkey_str),
Base64::decode_string(privkey_str),
);
format!("-----BEGIN KDT SIGNED MESSAGE-----\n{}\n\n-----BEGIN KDT SIGNATURE-----\n{}\n-----END KDT SIGNATURE-----", text, Base64::encode_bytes(&signkey.sign(text.as_bytes())).chars()
.enumerate()
.flat_map(|(i, c)| {
if (i + 1) % 64 == 0 {
vec![c, '\n']
} else {
vec![c]
}
})
.collect::<String>())
}
/// Verifies a KDT dilithium-signed message against its
/// corresponding public key.
#[inline(always)]
pub fn verify(text: String, signature: String, pubkey_str: String) -> bool {
let text_bytes = text.as_bytes();
let sig_bytes = Base64::decode_string(signature);
let pubkey = Base64::decode_string(pubkey_str);
dilithium_verify(&sig_bytes, &text_bytes, &pubkey).is_ok()
}
}

157
src/main.rs Normal file
View File

@ -0,0 +1,157 @@
// -- imports --
mod arguments;
mod core;
use crate::arguments::*;
use crate::core::*;
use clap::Parser;
fn main() {
let args = Args::parse();
let logger = Logger::new(true);
if let Err(e) = args.fail_if_invalid() {
logger.fatal(e);
}
if args.get_num_called() == 0 {
logger.fatal(
"You didn't pass any arguments! Run `kdt -h` for a list of possible flags and args.",
);
}
let mut kdt = match CoreKdtHandler::new() {
Ok(k) => k,
Err(e) => logger.fatal(e),
};
// ~ arg handling ~
// Note to self - figure out how to make this
// part of the code less ugly.
{
// options
// `--export-pubkey`
if let Some(pubkey_id) = args.export_pubkey {
logger.success(format!("Public key for key id {}:", pubkey_id));
println!(
"{}",
kdt.ownedkey_db.get_by_id(pubkey_id).pubkey_pair.to_string()
);
}
// `--del-pubkey`
if let Some(pubkey_id) = args.del_pubkey {
logger.info(format!("Removing public key with id {}...", pubkey_id));
kdt.del_pubkey(pubkey_id);
logger.success("Successfully removed the public key!");
}
// `--del-keyset`
if let Some(privkey_id) = args.del_keyset {
logger.info(format!(
"Removing owned key set with private key id {}...",
privkey_id
));
kdt.del_ownedkey(privkey_id);
logger.success("Succesfully removed the owned key set!");
}
// `-e | --encrypt`
if let Some(id) = args.encrypt {
logger.info("Type your message below (CTRL-D to finish):");
let message = logger.input();
logger.info("Encrypted message:");
println!("{}", kdt.encrypt(id, message));
}
// `-d | --decrypt`
if let Some(privkey_id) = args.decrypt {
logger.info("Input the encrypted message below (CTRL-D to finish):");
let message = logger.input();
logger.info("Decrypted message:");
println!("{}", kdt.decrypt(privkey_id, message));
}
// `-s | --sign`
if let Some(privkey_id) = args.sign {
logger.info("Input the message to sign below (CTRL-D to finish):");
let message = logger.input();
logger.info("Signed message:");
println!("{}", kdt.sign(privkey_id, message));
}
// `-v | --verify`
if let Some(pubkey_id) = args.verify {
logger.info("Input the signed message below (CTRL-D to finish):");
let message = logger.input();
let is_valid = kdt.verify(pubkey_id, message);
match is_valid {
Some(v) => {
if v {
logger.success("The provided message is valid!");
} else {
logger.warn("The given message is not valid!");
}
}
None => logger.fatal("There was an error parsing your input!"),
}
}
// flags
// `-l | --list-key-pairs`
if args.list_key_pairs {
if kdt.ownedkey_db.is_empty() {
logger.fatal("You don't have any private keys!");
}
logger.info("Keys in your owned key database:");
for key in &kdt.ownedkey_db.keys {
println!(
"ID: {}\nOwner: {}",
key.privkey_pair.id,
String::from_utf8_lossy(&Base64::decode_string(key.clone().privkey_pair.owner))
);
}
}
// `-g | --gen-key`
if args.gen_key {
logger.info("Type your name below. Note that this will be visible to everyone who imports your public key.");
let name = logger.input();
logger.info("Generating owned key set...");
let privkey_id = kdt.gen_key(if name.is_empty() {
String::from("No name was provided by the key owner!")
} else {
name
});
logger.success(format!(
"Successfully created owned key with private id {}!",
privkey_id
))
}
// `-i | --import`
if args.import {
logger.info("Input the public KDT key below (CTRL-D to finish):");
let pubkey = logger.input();
let maybe_pubkey_id = kdt.register_pubkey(pubkey);
match maybe_pubkey_id {
Ok(id) => logger.success(format!(
"Successfully imported KDT public key with id `{}`!",
id
)),
Err(e) => {
logger.fatal(e);
}
}
}
// `--list-keys`
if args.list_keys {
if kdt.pubkey_db.is_empty() {
logger.fatal("You don't have any public keys!");
}
logger.info("Keys in your public key database:");
for key in &kdt.pubkey_db.keys {
println!(
"ID: {}\nOwner: {}",
key.id,
String::from_utf8_lossy(&Base64::decode_string(key.clone().owner))
);
}
}
}
// Dump the datasets loaded in memory back to
// their respective files. If this fails, write
// a fatal log then panic.
if let Err(e) = kdt.dump_db() {
logger.fatal(e);
}
}