Skip to content

Commit

Permalink
doc
Browse files Browse the repository at this point in the history
  • Loading branch information
eschorn1 committed Jan 6, 2024
1 parent 6f72bef commit beb0f0f
Show file tree
Hide file tree
Showing 5 changed files with 292 additions and 110 deletions.
9 changes: 8 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,11 @@ jobs:
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
- run: cargo doc --all-features
- run: cargo doc --all-features

clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Clippy
run: cargo clippy --all-targets --all-features
78 changes: 29 additions & 49 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@
![Apache2/MIT licensed][license-image]
![Rust Version][rustc-image]

[FIPS 203] (Initial Public Draft) Module-Lattice-Based Key-Encapsulation Mechanism
Standard written in pure Rust.
[FIPS 203] (Initial Public Draft) Module-Lattice-Based Key-Encapsulation Mechanism Standard written in pure Rust for
server, desktop, browser and embedded applications.

This library implements the FIPS 203 **draft** standard in pure Rust with minimal and
mainstream dependencies. All three security parameter sets are fully functional. The
code does not require the standard library, e.g. `#[no_std]`, and has no heap
allocations so will be suitable for WASM, embedded and bare-metal applications.
Significant performance optimizations are forthcoming.
This crate implements the FIPS 203 **draft** standard in pure Rust with minimal and mainstream dependencies. All three
security parameter sets are fully functional. The implementation does not require the standard library, e.g.
`#[no_std]`, has no heap allocations, e.g. no `alloc` needed, and exposes the `RNG` so it is suitable for the full range
of applications down to the bare-metal. The API is stabilized and the code is heavily biased towards safety and
correctness; further performance optimizations will be implemented as the standard matures. This crate will quickly
follow any changes to FIPS 204 as they become available.

See <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.ipd.pdf> for a full
description of the target functionality.
See <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.ipd.pdf> for a full description of the target functionality.

The functionality is extremely simple to use, as demonstrated by the following example.

Expand Down Expand Up @@ -49,66 +49,46 @@ let alice_ssk_bytes = alice_dk.try_decaps_vt(&alice_ct).unwrap();
assert_eq!(bob_ssk_bytes, alice_ssk_bytes);
~~~

Rust [Documentation][docs-link]
The Rust [Documentation][docs-link] lives under each **Module** corresponding to the desired
[security parameter](#modules) below.

## Security Notes
## Notes

This crate is functional and corresponds to the first initial public draft of FIPS 203.
This crate is still under construction/refinement -- USE AT YOUR OWN RISK!

## Supported Parameter Sets

- ML-KEM-512
- ML-KEM-768
- ML-KEM-1023

## Minimum Supported Rust Version

Rust **1.72** or higher.

Minimum supported Rust version can be changed in the future, but it will be
done with a minor version bump.

## SemVer Policy

- All on-by-default features of this library are covered by SemVer
- MSRV is considered exempt from SemVer as noted above
* This crate is fully functional and corresponds to the first initial public draft of FIPS 203.
* Constant-time assurances target the source-code level only, and are a work in progress.
* Note that FIPS 203 places specific requirements on randomness per section 3.5.1, hence the exposed `RNG`.
* Requires Rust **1.72** or higher. The minimum supported Rust version may be changed in the future, but
it will be done with a minor version bump.
* All on-by-default features of this library are covered by SemVer.
* This software is experimental and still under active development -- USE AT YOUR OWN RISK!

## License

All crates licensed under either of

* [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
* [MIT license](http://opensource.org/licenses/MIT)

at your option.
Contents are licensed under either the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
or [MIT license](http://opensource.org/licenses/MIT) at your option.

### Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as
defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

[//]: # (badges)

[crate-image]: https://buildstats.info/crate/ml-kem-rs
[crate-image]: https://buildstats.info/crate/fips203

[crate-link]: https://crates.io/crates/ml-kem-rs
[crate-link]: https://crates.io/crates/fips203

[docs-image]: https://docs.rs/ml-kem-rs/badge.svg
[docs-image]: https://docs.rs/fips203/badge.svg

[docs-link]: https://docs.rs/ml-kem-rs/
[docs-link]: https://docs.rs/fips203/

[build-image]: https://github.com/integritychain/ml-kem-rs/workflows/test/badge.svg

[build-link]: https://github.com/integritychain/ml-kem-rs/actions?query=workflow%3Atest
[build-image]: https://github.com/integritychain/fips203/workflows/test/badge.svg

[build-link]: https://github.com/integritychain/fips203/actions?query=workflow%3Atest
[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg

[rustc-image]: https://img.shields.io/badge/rustc-1.72+-blue.svg

[//]: # (general links)

[IntegrityChain]: https://github.com/integritychain/

[FIPS 203]: https://csrc.nist.gov/pubs/fips/203/ipd
83 changes: 47 additions & 36 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
#![deny(clippy::pedantic)]
#![deny(warnings)]
#![deny(missing_docs)]
#![allow(clippy::cast_lossless)] // TODO
#![allow(clippy::cast_possible_truncation)] // TODO

#![doc = include_str!("../README.md")]

/// Implements FIPS 203 draft Module-Lattice-based Key-Encapsulation Mechanism Standard.
Expand All @@ -10,6 +13,8 @@
// Supports automatically clearing sensitive data on drop
use zeroize::{Zeroize, ZeroizeOnDrop};

use crate::traits::SerDes;

// Functionality map per FIPS 203 draft
//
// Algorithm 2 BitsToBytes(b) on page 17 --> optimized out (byte_fns.rs)
Expand Down Expand Up @@ -44,7 +49,7 @@ mod ntt;
mod sampling;
mod types;

/// TKTK
/// All functionality is covered by traits, such that consumers can utilize trait objects as desired.
pub mod traits;

// Relevant to all parameter sets
Expand All @@ -53,15 +58,18 @@ const Q: u32 = 3329;
const ZETA: u32 = 17;
const SSK_LEN: usize = 32;

// Relevant to all parameter sets
/// The (opaque) secret key that can be deserialized by each party.
/// The (opaque) secret key that can be de/serialized by each party.
#[derive(Clone, Debug, Zeroize, ZeroizeOnDrop)]
pub struct SharedSecretKey([u8; SSK_LEN]);

impl SharedSecretKey {
/// The `to_bytes` function deserializes a shared secret key into a byte array.
#[must_use]
pub fn into_bytes(self) -> [u8; SSK_LEN] { self.0 }
impl SerDes for SharedSecretKey {
type ByteArray = [u8; SSK_LEN];

fn into_bytes(self) -> Self::ByteArray { self.0 }

fn try_from_bytes(skk: Self::ByteArray) -> Result<Self, &'static str> {
Ok(SharedSecretKey(skk))
}
}


Expand All @@ -80,28 +88,27 @@ impl PartialEq for SharedSecretKey {
// This common functionality is injected into each parameter set module
macro_rules! functionality {
() => {
const ETA1_64: usize = ETA1 * 64; // Currently, Rust does not allow expressions involving
const ETA2_64: usize = ETA2 * 64; // So this is handled manually...what a pain
const ETA1_64: usize = ETA1 * 64; // Currently, Rust does not allow expressions involving constants...
const ETA2_64: usize = ETA2 * 64; // ...so these are handled manually.
const J_LEN: usize = 32 + 32 * (DU * K + DV);

use crate::byte_fns::byte_decode;
use crate::ml_kem::{ml_kem_decaps, ml_kem_encaps, ml_kem_key_gen};
use crate::traits::{Decaps, Encaps, KeyGen, SerDes};
use crate::types::Z256;
use crate::SharedSecretKey;

use rand_core::CryptoRngCore;
use zeroize::{Zeroize, ZeroizeOnDrop};

/// Correctly sized encapsulation key specific to the target parameter set.
/// Correctly sized encapsulation key specific to the target security parameter set.
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
pub struct EncapsKey([u8; EK_LEN]);

/// Correctly sized decapsulation key specific to the target parameter set.
/// Correctly sized decapsulation key specific to the target security parameter set.
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
pub struct DecapsKey([u8; DK_LEN]);

/// Correctly sized ciphertext specific to the target parameter set.
/// Correctly sized ciphertext specific to the target security parameter set.
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
pub struct CipherText([u8; CT_LEN]);

Expand All @@ -117,7 +124,6 @@ macro_rules! functionality {
type DecapsKey = DecapsKey;
type EncapsKey = EncapsKey;

/// TKTK
fn try_keygen_with_rng_vt(
rng: &mut impl CryptoRngCore,
) -> Result<(EncapsKey, DecapsKey), &'static str> {
Expand All @@ -131,7 +137,6 @@ macro_rules! functionality {
type CipherText = CipherText;
type SharedSecretKey = SharedSecretKey;

/// TKTK
fn try_encaps_with_rng_vt(
&self, rng: &mut impl CryptoRngCore,
) -> Result<(Self::SharedSecretKey, Self::CipherText), &'static str> {
Expand All @@ -147,7 +152,6 @@ macro_rules! functionality {
type CipherText = CipherText;
type SharedSecretKey = SharedSecretKey;

///TKTK
fn try_decaps_vt(&self, ct: &CipherText) -> Result<SharedSecretKey, &'static str> {
let ssk = ml_kem_decaps::<K, ETA1, ETA1_64, ETA2, ETA2_64, DU, DV, J_LEN, CT_LEN>(
&self.0, &ct.0,
Expand Down Expand Up @@ -176,7 +180,7 @@ macro_rules! functionality {
type ByteArray = [u8; DK_LEN];

fn try_from_bytes(dk: Self::ByteArray) -> Result<Self, &'static str> {
// TODO: validation here
// TODO: additional validation here
Ok(DecapsKey(dk))
}

Expand All @@ -187,7 +191,7 @@ macro_rules! functionality {
type ByteArray = [u8; CT_LEN];

fn try_from_bytes(ct: Self::ByteArray) -> Result<Self, &'static str> {
// TODO: validation here
// TODO: additional validation here
Ok(CipherText(ct))
}

Expand All @@ -197,18 +201,21 @@ macro_rules! functionality {
}


/// ML-KEM-512 is claimed to be in security category 1, see table 2 & 3 on page 33.
/// Functionality for the ML-KEM-512 security parameter set, which is claimed to be in security category 1, see
/// table 2 & 3 on page 33 of spec.
#[cfg(feature = "ml-kem-512")]
pub mod ml_kem_512 {
//!
//! See <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.ipd.pdf>
//!
//! Typical usage flow entails:
//! 1. The originator runs `key_gen()` to get an encaps key `encapsKey` and decaps key `decapsKey`.
//! 2. The originator deserializes the encaps key via `encapsKey.to_bytes()` and sends to the remote party.
//! 3. The remote party serializes the bytes via `new_ek(<bytes>)` to get the shared secret key `ssk` and ciphertext `cipherText`.
//! 4. The remote party deserializes the cipertext via `cipherText.to_bytes()` and sends to the originator.
//! 5. The originator serializes the ciphertext via `new_ct(<bytes>)` then runs `decapsKey.decaps(cipherText)` to the get shared secret ket `ssk`.
//! 1. The originator runs `try_keygen_vt()` to get an encaps key `encapsKey` and decaps key `decapsKey`.
//! 2. The originator serializes the encaps key via `encapsKey.into_bytes()` and sends to the remote party.
//! 3. The remote party deserializes the bytes via `try_from_bytes(<bytes>)` and runs `try_encaps_vt()` to get the
//! shared secret key `ssk` and ciphertext `cipherText`.
//! 4. The remote party serializes the cipertext via `cipherText.into_bytes()` and sends to the originator.
//! 5. The originator deserializes the ciphertext via `try_from_bytes(<bytes>)` then
//! runs `decapsKey.try_decaps_vt(cipherText)` to the get shared secret ket `ssk`.
//! 6. Both the originator and remote party now have the same shared secret key `ssk`.
const K: usize = 2;
Expand All @@ -220,23 +227,25 @@ pub mod ml_kem_512 {
const DK_LEN: usize = 1632;
const CT_LEN: usize = 768;


functionality!();
}


/// ML-KEM-768 is claimed to be in security category 3, see table 2 & 3 on page 33.
/// Functionality for the ML-KEM-768 security parameter set, which is claimed to be in security category 3, see
/// table 2 & 3 on page 33 of spec.
#[cfg(feature = "ml-kem-768")]
pub mod ml_kem_768 {
//!
//! See <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.ipd.pdf>
//!
//! Typical usage flow entails:
//! 1. The originator runs `key_gen()` to get an encaps key `encapsKey` and decaps key `decapsKey`.
//! 2. The originator deserializes the encaps key via `encapsKey.to_bytes()` and sends to the remote party.
//! 3. The remote party serializes the bytes via `new_ek(<bytes>)` to get the shared secret key `ssk` and ciphertext `cipherText`.
//! 4. The remote party deserializes the cipertext via `cipherText.to_bytes()` and sends to the originator.
//! 5. The originator serializes the ciphertext via `new_ct(<bytes>)` then runs `decapsKey.decaps(cipherText)` to the get shared secret ket `ssk`.
//! 1. The originator runs `try_keygen_vt()` to get an encaps key `encapsKey` and decaps key `decapsKey`.
//! 2. The originator serializes the encaps key via `encapsKey.into_bytes()` and sends to the remote party.
//! 3. The remote party deserializes the bytes via `try_from_bytes(<bytes>)` and runs `try_encaps_vt()` to get the
//! shared secret key `ssk` and ciphertext `cipherText`.
//! 4. The remote party serializes the cipertext via `cipherText.into_bytes()` and sends to the originator.
//! 5. The originator deserializes the ciphertext via `try_from_bytes(<bytes>)` then
//! runs `decapsKey.try_decaps_vt(cipherText)` to the get shared secret ket `ssk`.
//! 6. Both the originator and remote party now have the same shared secret key `ssk`.
const K: usize = 3;
Expand All @@ -259,11 +268,13 @@ pub mod ml_kem_1024 {
//! See <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.ipd.pdf>
//!
//! Typical usage flow entails:
//! 1. The originator runs `key_gen()` to get an encaps key `encapsKey` and decaps key `decapsKey`.
//! 2. The originator deserializes the encaps key via `encapsKey.to_bytes()` and sends to the remote party.
//! 3. The remote party serializes the bytes via `new_ek(<bytes>)` to get the shared secret key `ssk` and ciphertext `cipherText`.
//! 4. The remote party deserializes the cipertext via `cipherText.to_bytes()` and sends to the originator.
//! 5. The originator serializes the ciphertext via `new_ct(<bytes>)` then runs `decapsKey.decaps(cipherText)` to the get shared secret ket `ssk`.
//! 1. The originator runs `try_keygen_vt()` to get an encaps key `encapsKey` and decaps key `decapsKey`.
//! 2. The originator serializes the encaps key via `encapsKey.into_bytes()` and sends to the remote party.
//! 3. The remote party deserializes the bytes via `try_from_bytes(<bytes>)` and runs `try_encaps_vt()` to get the
//! shared secret key `ssk` and ciphertext `cipherText`.
//! 4. The remote party serializes the cipertext via `cipherText.into_bytes()` and sends to the originator.
//! 5. The originator deserializes the ciphertext via `try_from_bytes(<bytes>)` then
//! runs `decapsKey.try_decaps_vt(cipherText)` to the get shared secret ket `ssk`.
//! 6. Both the originator and remote party now have the same shared secret key `ssk`.
const K: usize = 4;
Expand Down
3 changes: 2 additions & 1 deletion src/sampling.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use sha3::digest::XofReader;

use crate::Q;
//use crate::byte_fns::bytes_to_bits;
//use crate::helpers::ensure;
use crate::types::Z256;
use crate::Q;

/// Algorithm 6 `SampleNTT(B)` on page 20.
/// If the input is a stream of uniformly random bytes, the output is a uniformly random element of `T_q`.
Expand Down Expand Up @@ -61,6 +61,7 @@ pub fn sample_ntt(mut byte_stream_b: impl XofReader) -> [Z256; 256] {

/// Algorithm 7 `SamplePolyCBDη(B)` on page 20.
/// If the input is a stream of uniformly random bytes, outputs a sample from the distribution Dη (Rq ).
#[allow(clippy::unnecessary_wraps)] // TODO: revisit
pub fn sample_poly_cbd(eta: u32, byte_array_b: &[u8]) -> Result<[Z256; 256], &'static str> {
let mut array_f: [Z256; 256] = [Z256(0); 256];
let mut temp = 0;
Expand Down
Loading

0 comments on commit beb0f0f

Please sign in to comment.