diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index da5cca7..eba4a42 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,6 +15,7 @@ jobs: backend_feature: - u64_backend - u32_backend + - p256,u64_backend toolchain: - stable - 1.51.0 @@ -43,6 +44,28 @@ jobs: command: test args: --no-default-features --features std --features ${{ matrix.backend_feature }} + build-no-std: + name: Build with no-std on ${{ matrix.target }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: + # for wasm + - wasm32-unknown-unknown + # for any no_std target + - thumbv6m-none-eabi + backend_feature: + - u64_backend + - u32_backend + - p256,u64_backend + steps: + - uses: actions/checkout@v2 + - uses: hecrj/setup-rust-action@v1 + - run: rustup target add ${{ matrix.target }} + - run: cargo build --verbose --target=${{ matrix.target }} --no-default-features --features ${{ matrix.backend_feature }} + + clippy: name: cargo clippy runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index 4e1553f..a03c348 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,15 +3,18 @@ name = "voprf" version = "0.0.0" description = "An implementation of a verifiable oblivious pseudorandom function (VOPRF)" authors = ["Kevin Lewi "] +categories = ["no-std"] license = "MIT" edition = "2018" readme = "README.md" +resolver = "2" [features] default = ["u64_backend"] +p256 = ["num-bigint", "num-integer", "num-traits", "once_cell", "p256_"] +std = ["curve25519-dalek/std", "getrandom", "rand/std", "rand/std_rng", "num-bigint/std", "num-integer/std", "num-traits/std"] u64_backend = ["curve25519-dalek/u64_backend"] u32_backend = ["curve25519-dalek/u32_backend"] -std = ["curve25519-dalek/std", "getrandom", "rand/std", "rand/std_rng"] [dependencies] constant_time_eq = "0.1" @@ -22,10 +25,18 @@ generic-array = "0.14" getrandom = { version = "0.2", optional = true } hkdf = "0.11" hmac = "0.11" +num-bigint = { version = "0.4", default-features = false, optional = true } +num-integer = { version = "0.1", default-features = false, optional = true } +num-traits = { version = "0.2", default-features = false, optional = true } +once_cell = { version = "1", default-features = false, optional = true } +p256_ = { package = "p256", version = "0.9", default-features = false, features = ["arithmetic", "zeroize"], optional = true } rand = { version = "0.8", default-features = false } subtle = { version = "2.3", default-features = false } zeroize = { version = "1", features = ["zeroize_derive"] } +[target.'cfg(target_arch = "wasm32")'.dependencies] +getrandom = { version = "0.2", features = ["js"], optional = true } + [dev-dependencies] base64 = "0.13" bincode = "1" @@ -37,3 +48,4 @@ serde_json = "1" sha2 = "0.9" proptest = "1" rustyline = "8" +voprf = { path = "", default-features = false, features = ["std"] } diff --git a/src/group/mod.rs b/src/group/mod.rs index f3a7757..d2c8d11 100644 --- a/src/group/mod.rs +++ b/src/group/mod.rs @@ -10,7 +10,6 @@ mod expand; #[cfg(feature = "p256")] pub(crate) mod p256; mod ristretto; -mod x25519; use crate::errors::InternalError; use crate::hash::Hash; diff --git a/src/group/p256.rs b/src/group/p256.rs index 7399d16..4269264 100644 --- a/src/group/p256.rs +++ b/src/group/p256.rs @@ -9,7 +9,7 @@ )] use super::Group; -use crate::errors::{InternalError, ProtocolError}; +use crate::errors::InternalError; use crate::hash::Hash; use core::ops::{Add, Div, Mul, Neg, Sub}; use core::str::FromStr; @@ -35,7 +35,7 @@ impl Group for ProjectivePoint { // Implements the `hash_to_curve()` function from // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3 - fn map_to_curve(msg: &[u8], dst: &[u8]) -> Result { + fn map_to_curve(msg: &[u8], dst: &[u8]) -> Result { // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.2 // `p: 2^256 - 2^224 + 2^192 + 2^96 - 1` const P: Lazy = Lazy::new(|| { @@ -83,7 +83,7 @@ impl Group for ProjectivePoint { // Implements the `HashToScalar()` function from // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.3 - fn hash_to_scalar(input: &[u8], dst: &[u8]) -> Result { + fn hash_to_scalar(input: &[u8], dst: &[u8]) -> Result { // https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf#[{%22num%22:211,%22gen%22:0},{%22name%22:%22XYZ%22},70,700,0] // P-256 `n` is defined as `115792089210356248762697446949407573529996955224135760342 422259061068512044369` const N: once_cell::unsync::Lazy = once_cell::unsync::Lazy::new(|| { diff --git a/src/group/x25519.rs b/src/group/x25519.rs deleted file mode 100644 index 6643037..0000000 --- a/src/group/x25519.rs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) Facebook, Inc. and its affiliates. -// -// This source code is licensed under the MIT license found in the -// LICENSE file in the root directory of this source tree. - -use super::Group; -use crate::errors::InternalError; -use crate::hash::Hash; -use curve25519_dalek::{constants::X25519_BASEPOINT, montgomery::MontgomeryPoint, scalar::Scalar}; -use generic_array::{typenum::U32, GenericArray}; -use rand::{CryptoRng, RngCore}; - -/// The implementation of such a subgroup for Ristretto -impl Group for MontgomeryPoint { - const SUITE_ID: usize = 0xFFFF; - - fn map_to_curve(_msg: &[u8], _dst: &[u8]) -> Result { - unreachable!("this algorithm should only be used as the `KeGroup`") - } - - fn hash_to_scalar(_input: &[u8], _dst: &[u8]) -> Result { - unreachable!("this algorithm should only be used as the `KeGroup`") - } - - type Scalar = Scalar; - type ScalarLen = U32; - fn from_scalar_slice( - scalar_bits: &GenericArray, - ) -> Result { - Ok(Scalar::from_bytes_mod_order(*scalar_bits.as_ref())) - } - fn random_nonzero_scalar(rng: &mut R) -> Self::Scalar { - loop { - let scalar = { - #[cfg(not(test))] - { - let mut scalar_bytes = [0u8; 64]; - rng.fill_bytes(&mut scalar_bytes); - Scalar::from_bytes_mod_order_wide(&scalar_bytes) - } - - // Tests need an exact conversion from bytes to scalar, sampling only 32 bytes from rng - #[cfg(test)] - { - let mut scalar_bytes = [0u8; 32]; - rng.fill_bytes(&mut scalar_bytes); - Scalar::from_bytes_mod_order(scalar_bytes) - } - }; - - if scalar != Scalar::zero() { - break scalar; - } - } - } - fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray { - scalar.to_bytes().into() - } - fn scalar_invert(_scalar: &Self::Scalar) -> Self::Scalar { - unreachable!("this algorithm should only be used as the `KeGroup`") - } - - // The byte length necessary to represent group elements - type ElemLen = U32; - fn from_element_slice( - element_bits: &GenericArray, - ) -> Result { - Ok(Self(*element_bits.as_ref())) - } - // serialization of a group element - fn to_arr(&self) -> GenericArray { - self.to_bytes().into() - } - - fn base_point() -> Self { - X25519_BASEPOINT - } - - fn mult_by_slice(&self, scalar: &GenericArray) -> Self { - self * Scalar::from_bits(*scalar.as_ref()) - } - - /// Returns if the group element is equal to the identity (1) - fn is_identity(&self) -> bool { - unreachable!("this algorithm should only be used as the `KeGroup`") - } - - fn ct_equal(&self, _other: &Self) -> bool { - unreachable!("this algorithm should only be used as the `KeGroup`") - } -} diff --git a/src/tests/voprf_test_vectors.rs b/src/tests/voprf_test_vectors.rs index 8d99fd4..87b63c0 100644 --- a/src/tests/voprf_test_vectors.rs +++ b/src/tests/voprf_test_vectors.rs @@ -89,26 +89,29 @@ fn populate_test_vectors(values: &Value) -> VOPRFTestVectorParameters { } } -struct Ristretto255Sha512; -impl CipherSuite for Ristretto255Sha512 { - type Group = RistrettoPoint; - type Hash = Sha512; -} - #[test] fn tests() -> Result<(), InternalError> { + struct Ristretto255Sha512; + impl CipherSuite for Ristretto255Sha512 { + type Group = RistrettoPoint; + type Hash = Sha512; + } + test_blind::(OPRF_RISTRETTO255_SHA512)?; test_evaluate::(OPRF_RISTRETTO255_SHA512)?; test_finalize::(OPRF_RISTRETTO255_SHA512)?; #[cfg(feature = "p256")] { - use p256_::ProjectivePoint; - use sha2::Sha256; + struct P256Sha256; + impl CipherSuite for P256Sha256 { + type Group = p256_::ProjectivePoint; + type Hash = sha2::Sha256; + } - test_blind::(OPRF_P256_SHA256)?; - test_evaluate::(OPRF_P256_SHA256)?; - test_finalize::(OPRF_P256_SHA256)?; + test_blind::(OPRF_P256_SHA256)?; + test_evaluate::(OPRF_P256_SHA256)?; + test_finalize::(OPRF_P256_SHA256)?; } Ok(())