Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rand_core trait support #16

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@ wasm-bindgen = { version = "0.2.87", optional = true }

[dependencies.rand]
version = "0.8.5"
default-features = false
features = ["getrandom"]
optional = true

[dev-dependencies]
rand = "0.8.5"
pqc_core = {version = "0.3.0", features = ["load"]}


[target.'cfg(bench)'.dev-dependencies.criterion]
criterion = "0.4.0"

Expand All @@ -46,8 +50,10 @@ aes = []
# message that is being signed.
random_signing = []

# For compiling to wasm targets
wasm = ["wasm-bindgen", "getrandom/js"]
# For compiling to wasm targets
wasm = ["wasm-bindgen", "getrandom/js", "rand"]

std = []

[lib]
crate-type = ["cdylib", "rlib"]
34 changes: 18 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ A rust implementation of the Dilithium, a KEM standardised by the NIST Post-Quan

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.
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**

Expand All @@ -23,25 +23,27 @@ It is recommended to use Dilithium in a hybrid system alongside a traditional si

```shell
cargo add pqc_dilithium
```
```

## Usage
## Usage

```rust
use pqc_dilithium::*;
use rand_core::OsRng;
```

### Key Generation
```rust
let keys = Keypair::generate();
use rand_core::
let keys = Keypair::generate(&mut OsRng);
assert!(keys.public.len() == PUBLICKEYBYTES);
assert!(keys.expose_secret().len() == SECRETKEYBYTES);
```

### Signing
### Signing
```rust
let msg = "Hello".as_bytes();
let sig = keys.sign(&msg);
let sig = keys.sign(&msg, &mut OsRng);
assert!(sig.len() == SIGNBYTES);
```

Expand All @@ -55,7 +57,7 @@ assert!(sig_verify.is_ok());

## AES mode

Dilithium-AES, that uses AES-256 in counter mode instead of SHAKE to
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.

Expand Down Expand Up @@ -87,7 +89,7 @@ By default this library uses Dilithium3

---

## Testing
## Testing

To run the known answer tests, you'll need to enable the `dilithium_kat` in `RUSTFLAGS` eg.

Expand Down Expand Up @@ -120,16 +122,16 @@ For example, using [wasm-pack](https://rustwasm.github.io/wasm-pack/installer/):
wasm-pack build -- --features wasm
```

Which will export the wasm, javascript and typescript files into `./pkg/`.
Which will export the wasm, javascript and typescript files into `./pkg/`.

To compile a different variant into a separate folder:
To compile a different variant into a separate folder:
```shell
wasm-pack build --out-dir pkg_mode5/ -- --features "wasm mode5"
wasm-pack build --out-dir pkg_mode5/ -- --features "wasm mode5"
```

There is also a basic html demo in the [www](./www/readme.md) folder.
From the www folder run:

From the www folder run:

```shell
npm install
Expand All @@ -140,19 +142,19 @@ npm run start

## Alternatives

The PQClean project has rust bindings for their C post quantum libraries.
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:
Authors of the Dilithium Algorithm:

* Roberto Avanzi, ARM Limited (DE)
* Joppe Bos, NXP Semiconductors (BE)
Expand Down
11 changes: 6 additions & 5 deletions benches/api.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use pqc_dilithium::*;
use rand_core::OsRng;

fn sign_small_msg(c: &mut Criterion) {
let keys = Keypair::generate();
let keys = Keypair::generate(&mut OsRng).unwrap();
let msg = "Hello".as_bytes();
c.bench_function("Sign Small Message", |b| {
b.iter(|| keys.sign(black_box(msg)))
b.iter(|| keys.sign(&black_box(msg), &mut OsRng).unwrap())
});
}

fn verify_small_msg(c: &mut Criterion) {
let keys = Keypair::generate();
let keys = Keypair::generate(&mut OsRng).unwrap();
let msg = "Hello".as_bytes();
let sig = keys.sign(msg);
let sig = keys.sign(msg, &mut OsRng).unwrap();
c.bench_function("Verify Small Message", |b| {
b.iter(|| verify(black_box(sig), black_box(msg), black_box(&keys.public)))
b.iter(|| verify(black_box(&sig), black_box(msg), black_box(&keys.public)))
});
}

Expand Down
51 changes: 31 additions & 20 deletions src/api.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::error::*;
use crate::params::{PUBLICKEYBYTES, SECRETKEYBYTES, SIGNBYTES};
use crate::sign::*;
use rand_core::{CryptoRng, RngCore};

#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct Keypair {
Expand All @@ -14,16 +16,12 @@ impl std::fmt::Debug for Keypair {
}
}

pub enum SignError {
Input,
Verify,
}

impl Keypair {
/// Explicitly expose secret key
/// ```
/// # use pqc_dilithium::*;
/// let keys = Keypair::generate();
/// use rand_core::OsRng;
/// let keys = Keypair::generate(&mut OsRng).expect("couldn't obtain random bytes");
/// let secret_key = keys.expose_secret();
/// assert!(secret_key.len() == SECRETKEYBYTES);
/// ```
Expand All @@ -36,31 +34,43 @@ impl Keypair {
/// Example:
/// ```
/// # use pqc_dilithium::*;
/// let keys = Keypair::generate();
/// # use rand_core::OsRng;
/// let keys = Keypair::generate(&mut OsRng).expect("couldn't obtain random bytes");
/// assert!(keys.public.len() == PUBLICKEYBYTES);
/// assert!(keys.expose_secret().len() == SECRETKEYBYTES);
/// ```
pub fn generate() -> Keypair {
pub fn generate<R>(rng: &mut R) -> Result<Keypair, DilithiumError>
where
R: RngCore + CryptoRng,
{
let mut public = [0u8; PUBLICKEYBYTES];
let mut secret = [0u8; SECRETKEYBYTES];
crypto_sign_keypair(&mut public, &mut secret, None);
Keypair { public, secret }
crypto_sign_keypair(&mut public, &mut secret, rng, None)?;
Ok(Keypair { public, secret })
}

/// Generates a signature for the given message using a keypair
///
/// Example:
/// ```
/// # use pqc_dilithium::*;
/// # let keys = Keypair::generate();
/// # use rand_core::OsRng;
/// # let keys = Keypair::generate(&mut OsRng).unwrap();
/// let msg = "Hello".as_bytes();
/// let sig = keys.sign(&msg);
/// let sig = keys.sign(&msg, &mut OsRng).expect("couldn't obtain random bytes");
/// assert!(sig.len() == SIGNBYTES);
/// ```
pub fn sign(&self, msg: &[u8]) -> [u8; SIGNBYTES] {
/// ```
pub fn sign<R>(
&self,
msg: &[u8],
rng: &mut R,
) -> Result<[u8; SIGNBYTES], DilithiumError>
where
R: RngCore + CryptoRng,
{
let mut sig = [0u8; SIGNBYTES];
crypto_sign_signature(&mut sig, msg, &self.secret);
sig
crypto_sign_signature(&mut sig, msg, &self.secret, rng)?;
Ok(sig)
}
}

Expand All @@ -69,18 +79,19 @@ impl Keypair {
/// Example:
/// ```
/// # use pqc_dilithium::*;
/// # let keys = Keypair::generate();
/// # use rand_core::OsRng;
/// # let keys = Keypair::generate(&mut OsRng).unwrap();
/// # let msg = [0u8; 32];
/// # let sig = keys.sign(&msg);
/// # let sig = keys.sign(&msg, &mut OsRng).unwrap();
/// 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> {
) -> Result<(), DilithiumError> {
if sig.len() != SIGNBYTES {
return Err(SignError::Input);
return Err(DilithiumError::Input);
}
crypto_sign_verify(&sig, &msg, public_key)
}
9 changes: 9 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#[derive(Debug, PartialEq)]
pub enum DilithiumError {
Input,
Verify,
RandomBytesGeneration,
}

#[cfg(feature = "std")]
impl std::error::Error for DilithiumError {}
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
#[cfg(feature = "aes")]
mod aes256ctr;
mod api;
mod error;
mod fips202;
mod ntt;
mod packing;
mod params;
mod poly;
mod polyvec;
mod randombytes;
mod reduce;
mod rng;
mod rounding;
mod sign;
mod symmetric;
Expand Down
10 changes: 5 additions & 5 deletions src/packing.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{params::*, poly::*, polyvec::*, SignError};
use crate::{error::DilithiumError, params::*, poly::*, polyvec::*};

/// Bit-pack public key pk = (rho, t1).
pub fn pack_pk(pk: &mut [u8], rho: &[u8], t1: &Polyveck) {
Expand Down Expand Up @@ -123,7 +123,7 @@ pub fn unpack_sig(
z: &mut Polyvecl,
h: &mut Polyveck,
sig: &[u8],
) -> Result<(), SignError> {
) -> Result<(), DilithiumError> {
let mut idx = 0usize;

c[..SEEDBYTES].copy_from_slice(&sig[..SEEDBYTES]);
Expand All @@ -138,12 +138,12 @@ pub fn unpack_sig(
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);
return Err(DilithiumError::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);
return Err(DilithiumError::Input);
}
h.vec[i].coeffs[sig[idx + j] as usize] = 1;
}
Expand All @@ -153,7 +153,7 @@ pub fn unpack_sig(
// Extra indices are zero for strong unforgeability
for j in k..OMEGA {
if sig[idx + j as usize] > 0 {
return Err(SignError::Input);
return Err(DilithiumError::Input);
}
}

Expand Down
5 changes: 0 additions & 5 deletions src/randombytes.rs

This file was deleted.

16 changes: 16 additions & 0 deletions src/rng.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use crate::error::DilithiumError;
use rand_core::*;

pub fn randombytes<R>(
x: &mut [u8],
len: usize,
rng: &mut R,
) -> Result<(), DilithiumError>
where
R: RngCore + CryptoRng,
{
match rng.try_fill_bytes(&mut x[..len]) {
Ok(_) => Ok(()),
Err(_) => Err(DilithiumError::RandomBytesGeneration),
}
}
Loading