Skip to content

Commit

Permalink
Merge branch 'next' into plafer-add-smt-struct
Browse files Browse the repository at this point in the history
  • Loading branch information
plafer committed Jan 18, 2024
2 parents 88797c3 + 12df4c8 commit dbbbf28
Show file tree
Hide file tree
Showing 18 changed files with 138 additions and 101 deletions.
28 changes: 26 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ jobs:
args: ${{matrix.features}}

no-std:
name: build ${{matrix.toolchain}} no-std for wasm32-unknown-unknown
name: build ${{matrix.toolchain}} no-std for wasm32-unknown-unknown
runs-on: ubuntu-latest
strategy:
fail-fast: false
Expand All @@ -106,4 +106,28 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: build
args: --no-default-features --target wasm32-unknown-unknown
args: --no-default-features --target wasm32-unknown-unknown

docs:
name: Verify the docs on ${{matrix.toolchain}}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
toolchain: [stable]
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install rust
uses: actions-rs/toolchain@v1
with:
toolchain: ${{matrix.toolchain}}
override: true
- name: Check docs
uses: actions-rs/cargo@v1
env:
RUSTDOCFLAGS: -D warnings
with:
command: doc
args: --verbose --all-features --keep-going
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ For example, a new change to the AIR crate might have the following message: `fe
// ================================================================================
```
- [Rustfmt](https://github.com/rust-lang/rustfmt) and [Clippy](https://github.com/rust-lang/rust-clippy) linting is included in CI pipeline. Anyways it's prefferable to run linting locally before push:
- [Rustfmt](https://github.com/rust-lang/rustfmt) and [Clippy](https://github.com/rust-lang/rust-clippy) linting is included in CI pipeline. Anyways it's preferable to run linting locally before push:
```
cargo fix --allow-staged --allow-dirty --all-targets --all-features; cargo fmt; cargo clippy --workspace --all-targets --all-features -- -D warnings
```
Expand Down
4 changes: 2 additions & 2 deletions src/dsa/rpo_falcon512/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ const NONCE_LEN: usize = 40;
const NONCE_ELEMENTS: usize = 8;

/// Public key length as a u8 vector.
const PK_LEN: usize = 897;
pub const PK_LEN: usize = 897;

/// Secret key length as a u8 vector.
const SK_LEN: usize = 1281;
pub const SK_LEN: usize = 1281;

/// Signature length as a u8 vector.
const SIG_LEN: usize = 626;
Expand Down
24 changes: 12 additions & 12 deletions src/dsa/rpo_falcon512/polynomial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use core::ops::{Add, Mul, Sub};
// FALCON POLYNOMIAL
// ================================================================================================

/// A polynomial over Z_p[x]/(phi) where phi := x^512 + 1
/// A polynomial over Z_p\[x\]/(phi) where phi := x^512 + 1
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Polynomial([u16; N]);

Expand All @@ -24,7 +24,7 @@ impl Polynomial {
Self(data)
}

/// Decodes raw bytes representing a public key into a polynomial in Z_p[x]/(phi).
/// Decodes raw bytes representing a public key into a polynomial in Z_p\[x\]/(phi).
///
/// # Errors
/// Returns an error if:
Expand Down Expand Up @@ -69,14 +69,14 @@ impl Polynomial {
}
}

/// Decodes the signature into the coefficients of a polynomial in Z_p[x]/(phi). It assumes
/// Decodes the signature into the coefficients of a polynomial in Z_p\[x\]/(phi). It assumes
/// that the signature has been encoded using the uncompressed format.
///
/// # Errors
/// Returns an error if:
/// - The signature has been encoded using a different algorithm than the reference compressed
/// encoding algorithm.
/// - The encoded signature polynomial is in Z_p[x]/(phi') where phi' = x^N' + 1 and N' != 512.
/// - The encoded signature polynomial is in Z_p\[x\]/(phi') where phi' = x^N' + 1 and N' != 512.
/// - While decoding the high bits of a coefficient, the current accumulated value of its
/// high bits is larger than 2048.
/// - The decoded coefficient is -0.
Expand Down Expand Up @@ -149,12 +149,12 @@ impl Polynomial {
// POLYNOMIAL OPERATIONS
// --------------------------------------------------------------------------------------------

/// Multiplies two polynomials over Z_p[x] without reducing modulo p. Given that the degrees
/// Multiplies two polynomials over Z_p\[x\] without reducing modulo p. Given that the degrees
/// of the input polynomials are less than 512 and their coefficients are less than the modulus
/// q equal to 12289, the resulting product polynomial is guaranteed to have coefficients less
/// than the Miden prime.
///
/// Note that this multiplication is not over Z_p[x]/(phi).
/// Note that this multiplication is not over Z_p\[x\]/(phi).
pub fn mul_modulo_p(a: &Self, b: &Self) -> [u64; 1024] {
let mut c = [0; 2 * N];
for i in 0..N {
Expand All @@ -166,8 +166,8 @@ impl Polynomial {
c
}

/// Reduces a polynomial, that is the product of two polynomials over Z_p[x], modulo
/// the irreducible polynomial phi. This results in an element in Z_p[x]/(phi).
/// Reduces a polynomial, that is the product of two polynomials over Z_p\[x\], modulo
/// the irreducible polynomial phi. This results in an element in Z_p\[x\]/(phi).
pub fn reduce_negacyclic(a: &[u64; 1024]) -> Self {
let mut c = [0; N];
for i in 0..N {
Expand All @@ -181,7 +181,7 @@ impl Polynomial {
Self(c)
}

/// Computes the norm squared of a polynomial in Z_p[x]/(phi) after normalizing its
/// Computes the norm squared of a polynomial in Z_p\[x\]/(phi) after normalizing its
/// coefficients to be in the interval (-p/2, p/2].
pub fn sq_norm(&self) -> u64 {
let mut res = 0;
Expand All @@ -203,7 +203,7 @@ impl Default for Polynomial {
}
}

/// Multiplication over Z_p[x]/(phi)
/// Multiplication over Z_p\[x\]/(phi)
impl Mul for Polynomial {
type Output = Self;

Expand All @@ -227,7 +227,7 @@ impl Mul for Polynomial {
}
}

/// Addition over Z_p[x]/(phi)
/// Addition over Z_p\[x\]/(phi)
impl Add for Polynomial {
type Output = Self;

Expand All @@ -239,7 +239,7 @@ impl Add for Polynomial {
}
}

/// Subtraction over Z_p[x]/(phi)
/// Subtraction over Z_p\[x\]/(phi)
impl Sub for Polynomial {
type Output = Self;

Expand Down
4 changes: 2 additions & 2 deletions src/dsa/rpo_falcon512/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use core::cell::OnceCell;

/// An RPO Falcon512 signature over a message.
///
/// The signature is a pair of polynomials (s1, s2) in (Z_p[x]/(phi))^2, where:
/// The signature is a pair of polynomials (s1, s2) in (Z_p\[x\]/(phi))^2, where:
/// - p := 12289
/// - phi := x^512 + 1
/// - s1 = c - s2 * h
Expand Down Expand Up @@ -86,7 +86,7 @@ impl Signature {
// HASH-TO-POINT
// --------------------------------------------------------------------------------------------

/// Returns a polynomial in Z_p[x]/(phi) representing the hash of the provided message.
/// Returns a polynomial in Z_p\[x\]/(phi) representing the hash of the provided message.
pub fn hash_to_point(&self, message: Word) -> Polynomial {
hash_to_point(message, &self.nonce())
}
Expand Down
7 changes: 6 additions & 1 deletion src/hash/rescue/rpo/digest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ impl RpoDigest {
{
digests.flat_map(|d| d.0.iter())
}

/// Returns hexadecimal representation of this digest prefixed with `0x`.
pub fn to_hex(&self) -> String {
bytes_to_hex_string(self.as_bytes())
}
}

impl Digest for RpoDigest {
Expand Down Expand Up @@ -158,7 +163,7 @@ impl From<RpoDigest> for [u8; DIGEST_BYTES] {
impl From<RpoDigest> for String {
/// The returned string starts with `0x`.
fn from(value: RpoDigest) -> Self {
bytes_to_hex_string(value.as_bytes())
value.to_hex()
}
}

Expand Down
10 changes: 2 additions & 8 deletions src/hash/rescue/rpo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ mod tests;
/// * Number of founds: 7.
/// * S-Box degree: 7.
///
/// The above parameters target 128-bit security level. The digest consists of four field elements
/// The above parameters target a 128-bit security level. The digest consists of four field elements
/// and it can be serialized into 32 bytes (256 bits).
///
/// ## Hash output consistency
Expand Down Expand Up @@ -55,13 +55,7 @@ mod tests;
pub struct Rpo256();

impl Hasher for Rpo256 {
/// Rpo256 collision resistance is the same as the security level, that is 128-bits.
///
/// #### Collision resistance
///
/// However, our setup of the capacity registers might drop it to 126.
///
/// Related issue: [#69](https://github.com/0xPolygonMiden/crypto/issues/69)
/// Rpo256 collision resistance is 128-bits.
const COLLISION_RESISTANCE: u32 = 128;

type Digest = RpoDigest;
Expand Down
7 changes: 6 additions & 1 deletion src/hash/rescue/rpx/digest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ impl RpxDigest {
{
digests.flat_map(|d| d.0.iter())
}

/// Returns hexadecimal representation of this digest prefixed with `0x`.
pub fn to_hex(&self) -> String {
bytes_to_hex_string(self.as_bytes())
}
}

impl Digest for RpxDigest {
Expand Down Expand Up @@ -158,7 +163,7 @@ impl From<RpxDigest> for [u8; DIGEST_BYTES] {
impl From<RpxDigest> for String {
/// The returned string starts with `0x`.
fn from(value: RpxDigest) -> Self {
bytes_to_hex_string(value.as_bytes())
value.to_hex()
}
}

Expand Down
66 changes: 26 additions & 40 deletions src/hash/rescue/rpx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use super::{
add_constants, add_constants_and_apply_inv_sbox, add_constants_and_apply_sbox, apply_inv_sbox,
apply_mds, apply_sbox, CubeExtension, Digest, ElementHasher, Felt, FieldElement, Hasher,
StarkField, ARK1, ARK2, BINARY_CHUNK_SIZE, CAPACITY_RANGE, DIGEST_BYTES, DIGEST_RANGE,
DIGEST_SIZE, INPUT1_RANGE, INPUT2_RANGE, MDS, NUM_ROUNDS, ONE, RATE_RANGE, RATE_WIDTH,
STATE_WIDTH, ZERO,
DIGEST_SIZE, INPUT1_RANGE, INPUT2_RANGE, MDS, NUM_ROUNDS, RATE_RANGE, RATE_WIDTH, STATE_WIDTH,
ZERO,
};
use core::{convert::TryInto, ops::Range};

Expand All @@ -30,7 +30,7 @@ pub type CubicExtElement = CubeExtension<Felt>;
/// - (M): `apply_mds` → `add_constants`.
/// * Permutation: (FB) (E) (FB) (E) (FB) (E) (M).
///
/// The above parameters target 128-bit security level. The digest consists of four field elements
/// The above parameters target a 128-bit security level. The digest consists of four field elements
/// and it can be serialized into 32 bytes (256 bits).
///
/// ## Hash output consistency
Expand Down Expand Up @@ -58,13 +58,7 @@ pub type CubicExtElement = CubeExtension<Felt>;
pub struct Rpx256();

impl Hasher for Rpx256 {
/// Rpx256 collision resistance is the same as the security level, that is 128-bits.
///
/// #### Collision resistance
///
/// However, our setup of the capacity registers might drop it to 126.
///
/// Related issue: [#69](https://github.com/0xPolygonMiden/crypto/issues/69)
/// Rpx256 collision resistance is 128-bits.
const COLLISION_RESISTANCE: u32 = 128;

type Digest = RpxDigest;
Expand All @@ -73,14 +67,16 @@ impl Hasher for Rpx256 {
// initialize the state with zeroes
let mut state = [ZERO; STATE_WIDTH];

// set the capacity (first element) to a flag on whether or not the input length is evenly
// divided by the rate. this will prevent collisions between padded and non-padded inputs,
// and will rule out the need to perform an extra permutation in case of evenly divided
// inputs.
let is_rate_multiple = bytes.len() % RATE_WIDTH == 0;
if !is_rate_multiple {
state[CAPACITY_RANGE.start] = ONE;
}
// determine the number of field elements needed to encode `bytes` when each field element
// represents at most 7 bytes.
let num_field_elem = bytes.len().div_ceil(BINARY_CHUNK_SIZE);

// set the first capacity element to `RATE_WIDTH + (num_field_elem % RATE_WIDTH)`. We do
// this to achieve:
// 1. Domain separating hashing of `[u8]` from hashing of `[Felt]`.
// 2. Avoiding collisions at the `[Felt]` representation of the encoded bytes.
state[CAPACITY_RANGE.start] =
Felt::from((RATE_WIDTH + (num_field_elem % RATE_WIDTH)) as u8);

// initialize a buffer to receive the little-endian elements.
let mut buf = [0_u8; 8];
Expand All @@ -94,7 +90,7 @@ impl Hasher for Rpx256 {
let i = bytes.chunks(BINARY_CHUNK_SIZE).fold(0, |i, chunk| {
// the last element of the iteration may or may not be a full chunk. if it's not, then
// we need to pad the remainder bytes of the chunk with zeroes, separated by a `1`.
// this will avoid collisions.
// this will avoid collisions at the bytes level.
if chunk.len() == BINARY_CHUNK_SIZE {
buf[..BINARY_CHUNK_SIZE].copy_from_slice(chunk);
} else {
Expand All @@ -120,10 +116,10 @@ impl Hasher for Rpx256 {
// if we absorbed some elements but didn't apply a permutation to them (would happen when
// the number of elements is not a multiple of RATE_WIDTH), apply the RPX permutation. we
// don't need to apply any extra padding because the first capacity element contains a
// flag indicating whether the input is evenly divisible by the rate.
// flag indicating the number of field elements constituting the last block when the latter
// is not divisible by `RATE_WIDTH`.
if i != 0 {
state[RATE_RANGE.start + i..RATE_RANGE.end].fill(ZERO);
state[RATE_RANGE.start + i] = ONE;
Self::apply_permutation(&mut state);
}

Expand All @@ -148,25 +144,20 @@ impl Hasher for Rpx256 {
fn merge_with_int(seed: Self::Digest, value: u64) -> Self::Digest {
// initialize the state as follows:
// - seed is copied into the first 4 elements of the rate portion of the state.
// - if the value fits into a single field element, copy it into the fifth rate element
// and set the sixth rate element to 1.
// - if the value fits into a single field element, copy it into the fifth rate element and
// set the first capacity element to 5.
// - if the value doesn't fit into a single field element, split it into two field
// elements, copy them into rate elements 5 and 6, and set the seventh rate element
// to 1.
// - set the first capacity element to 1
// elements, copy them into rate elements 5 and 6 and set the first capacity element to 6.
let mut state = [ZERO; STATE_WIDTH];
state[INPUT1_RANGE].copy_from_slice(seed.as_elements());
state[INPUT2_RANGE.start] = Felt::new(value);
if value < Felt::MODULUS {
state[INPUT2_RANGE.start + 1] = ONE;
state[CAPACITY_RANGE.start] = Felt::from(5_u8);
} else {
state[INPUT2_RANGE.start + 1] = Felt::new(value / Felt::MODULUS);
state[INPUT2_RANGE.start + 2] = ONE;
state[CAPACITY_RANGE.start] = Felt::from(6_u8);
}

// common padding for both cases
state[CAPACITY_RANGE.start] = ONE;

// apply the RPX permutation and return the first four elements of the state
Self::apply_permutation(&mut state);
RpxDigest::new(state[DIGEST_RANGE].try_into().unwrap())
Expand All @@ -181,11 +172,9 @@ impl ElementHasher for Rpx256 {
let elements = E::slice_as_base_elements(elements);

// initialize state to all zeros, except for the first element of the capacity part, which
// is set to 1 if the number of elements is not a multiple of RATE_WIDTH.
// is set to `elements.len() % RATE_WIDTH`.
let mut state = [ZERO; STATE_WIDTH];
if elements.len() % RATE_WIDTH != 0 {
state[CAPACITY_RANGE.start] = ONE;
}
state[CAPACITY_RANGE.start] = Self::BaseField::from((elements.len() % RATE_WIDTH) as u8);

// absorb elements into the state one by one until the rate portion of the state is filled
// up; then apply the Rescue permutation and start absorbing again; repeat until all
Expand All @@ -202,11 +191,8 @@ impl ElementHasher for Rpx256 {

// if we absorbed some elements but didn't apply a permutation to them (would happen when
// the number of elements is not a multiple of RATE_WIDTH), apply the RPX permutation after
// padding by appending a 1 followed by as many 0 as necessary to make the input length a
// multiple of the RATE_WIDTH.
// padding by as many 0 as necessary to make the input length a multiple of the RATE_WIDTH.
if i > 0 {
state[RATE_RANGE.start + i] = ONE;
i += 1;
while i != RATE_WIDTH {
state[RATE_RANGE.start + i] = ZERO;
i += 1;
Expand Down Expand Up @@ -354,7 +340,7 @@ impl Rpx256 {
add_constants(state, &ARK1[round]);
}

/// Computes an exponentiation to the power 7 in cubic extension field
/// Computes an exponentiation to the power 7 in cubic extension field.
#[inline(always)]
pub fn exp7(x: CubeExtension<Felt>) -> CubeExtension<Felt> {
let x2 = x.square();
Expand Down
Loading

0 comments on commit dbbbf28

Please sign in to comment.