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

Group trait overhaul part 2 #53

Merged
merged 11 commits into from
Jan 21, 2022
27 changes: 10 additions & 17 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,16 @@ jobs:
fail-fast: false
matrix:
backend_feature:
- ristretto255_u64
- ristretto255_u32
- p256
- ristretto255_u64,p256
- --features ristretto255-ciphersuite,ristretto255-u64
- --features ristretto255-ciphersuite,ristretto255-u32
-
frontend_feature:
-
- --features danger
- --features serde
toolchain:
- stable
- 1.51.0
exclude:
- backend_feature: p256
toolchain: 1.51.0
- backend_feature: ristretto255_u64,p256
toolchain: 1.51.0
- 1.57.0
name: test
steps:
- name: Checkout sources
Expand All @@ -67,19 +61,19 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: test
args: --no-default-features --features ${{ matrix.backend_feature }}
args: --no-default-features ${{ matrix.backend_feature }}

- name: Run cargo test with alloc
uses: actions-rs/cargo@v1
with:
command: test
args: --no-default-features ${{ matrix.frontend_feature }},alloc --features ${{ matrix.backend_feature }}
args: --no-default-features ${{ matrix.frontend_feature }},alloc ${{ matrix.backend_feature }}

- name: Run cargo test with std
uses: actions-rs/cargo@v1
with:
command: test
args: --no-default-features ${{ matrix.frontend_feature }},std --features ${{ matrix.backend_feature }}
args: --no-default-features ${{ matrix.frontend_feature }},std ${{ matrix.backend_feature }}

build-no-std:
name: Build with no-std on ${{ matrix.target }}
Expand All @@ -94,9 +88,8 @@ jobs:
- thumbv6m-none-eabi
backend_feature:
-
- --features ristretto255_u64
- --features ristretto255_u32
- --features p256
- --features ristretto255-ciphersuite,ristretto255-u64
- --features ristretto255-ciphersuite,ristretto255-u32
frontend_feature:
-
- --features danger
Expand Down Expand Up @@ -135,7 +128,7 @@ jobs:
RUSTDOCFLAGS: -D warnings
with:
command: doc
args: --no-deps --document-private-items --features std,p256
args: --no-deps --document-private-items --features danger,std


rustfmt:
Expand Down
45 changes: 20 additions & 25 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,60 @@
authors = ["Kevin Lewi <[email protected]>"]
categories = ["no-std", "algorithms", "cryptography"]
description = "An implementation of a verifiable oblivious pseudorandom function (VOPRF)"
edition = "2018"
edition = "2021"
keywords = ["oprf"]
license = "MIT"
name = "voprf"
readme = "README.md"
repository = "https://github.com/novifinancial/voprf/"
resolver = "2"
rust-version = "1.51"
rust-version = "1.57"
version = "0.3.0"

[features]
alloc = []
danger = []
default = ["ristretto255_u64", "serde"]
p256 = [
"alloc",
"num-bigint",
"num-integer",
"num-traits",
"once_cell",
"p256_",
]
default = ["ristretto255-ciphersuite", "ristretto255-u64", "serde"]
ristretto255 = ["generic-array/more_lengths"]
ristretto255_fiat_u32 = ["curve25519-dalek/fiat_u32_backend", "ristretto255"]
ristretto255_fiat_u64 = ["curve25519-dalek/fiat_u64_backend", "ristretto255"]
ristretto255_simd = ["curve25519-dalek/simd_backend", "ristretto255"]
ristretto255_u32 = ["curve25519-dalek/u32_backend", "ristretto255"]
ristretto255_u64 = ["curve25519-dalek/u64_backend", "ristretto255"]
ristretto255-ciphersuite = ["ristretto255", "sha2"]
ristretto255-fiat-u32 = ["curve25519-dalek/fiat_u32_backend", "ristretto255"]
ristretto255-fiat-u64 = ["curve25519-dalek/fiat_u64_backend", "ristretto255"]
ristretto255-simd = ["curve25519-dalek/simd_backend", "ristretto255"]
ristretto255-u32 = ["curve25519-dalek/u32_backend", "ristretto255"]
ristretto255-u64 = ["curve25519-dalek/u64_backend", "ristretto255"]
std = ["alloc"]

[dependencies]
curve25519-dalek = { version = "3", default-features = false, optional = true }
derive-where = { version = "1.0.0-rc.1", features = ["zeroize"] }
digest = "0.10"
displaydoc = { version = "0.2", default-features = false }
elliptic-curve = { version = "0.12.0-pre.1", features = [
"hash2curve",
"sec1",
"voprf",
] }
generic-array = "0.14"
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.10", default-features = false, features = [
"arithmetic",
], optional = true }
rand_core = { version = "0.6", default-features = false }
serde = { version = "1", default-features = false, features = [
"derive",
], optional = true }
sha2 = { version = "0.10", default-features = false, optional = true }
subtle = { version = "2.3", default-features = false }
zeroize = { version = "1", default-features = false }

[dev-dependencies]
generic-array = { version = "0.14", features = ["more_lengths"] }
hex = "0.4"
json = "0.12"
p256 = { version = "0.11.0-pre.0", default-features = false, features = [
"hash2curve",
"voprf",
] }
proptest = "1"
rand = "0.8"
regex = "1"
sha2 = "0.10"

[package.metadata.docs.rs]
features = ["danger", "p256", "std"]
features = ["danger", "std"]
targets = []
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ voprf = "0.3"

### Minimum Supported Rust Version

Rust **1.51** or higher.
Rust **1.57** or higher.

Contributors
------------
Expand Down
48 changes: 48 additions & 0 deletions src/ciphersuite.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) Facebook, Inc. and its affiliates.
//
// This source code is licensed under both the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.

//! Defines the CipherSuite trait to specify the underlying primitives for VOPRF

use digest::core_api::BlockSizeUser;
use digest::{Digest, OutputSizeUser};
use elliptic_curve::VoprfParameters;
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};

use crate::Group;

/// Configures the underlying primitives used in VOPRF
pub trait CipherSuite
where
<Self::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<Self::Hash as BlockSizeUser>::BlockSize>,
{
/// The ciphersuite identifier as dictated by
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html>
const ID: u16;

/// A finite cyclic group along with a point representation that allows some
/// customization on how to hash an input to a curve point. See [`Group`].
type Group: Group;

/// The main hash function to use (for HKDF computations and hashing
/// transcripts).
type Hash: BlockSizeUser + Digest;
}

impl<T: VoprfParameters> CipherSuite for T
where
T: Group,
T::Hash: BlockSizeUser + Digest,
<T::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<T::Hash as BlockSizeUser>::BlockSize>,
{
const ID: u16 = T::ID;

type Group = T;

type Hash = T::Hash;
}
113 changes: 113 additions & 0 deletions src/group/elliptic_curve.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright (c) Facebook, Inc. and its affiliates.
//
// This source code is licensed under both the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.

use digest::core_api::BlockSizeUser;
use digest::OutputSizeUser;
use elliptic_curve::group::cofactor::CofactorGroup;
use elliptic_curve::hash2curve::{ExpandMsgXmd, FromOkm, GroupDigest};
use elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint};
use elliptic_curve::{
AffinePoint, Field, FieldSize, Group as _, ProjectivePoint, PublicKey, Scalar, SecretKey,
};
use generic_array::sequence::Concat;
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
use generic_array::GenericArray;
use rand_core::{CryptoRng, RngCore};

use super::Group;
use crate::group::{STR_HASH_TO_GROUP, STR_HASH_TO_SCALAR};
use crate::voprf::{self, Mode};
use crate::{CipherSuite, Error, Result};

impl<C> Group for C
where
C: GroupDigest,
ProjectivePoint<Self>: CofactorGroup,
FieldSize<Self>: ModulusSize,
AffinePoint<Self>: FromEncodedPoint<Self> + ToEncodedPoint<Self>,
Scalar<Self>: FromOkm,
{
type Elem = ProjectivePoint<Self>;

type ElemLen = <FieldSize<Self> as ModulusSize>::CompressedPointSize;

type Scalar = Scalar<Self>;

type ScalarLen = FieldSize<Self>;

// Implements the `hash_to_curve()` function from
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
fn hash_to_curve<CS: CipherSuite>(msg: &[&[u8]], mode: Mode) -> Result<Self::Elem>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let dst =
GenericArray::from(STR_HASH_TO_GROUP).concat(voprf::get_context_string::<CS>(mode));

Self::hash_from_bytes::<ExpandMsgXmd<CS::Hash>>(msg, &dst).map_err(|_| Error::PointError)
}

// Implements the `HashToScalar()` function
fn hash_to_scalar<CS: CipherSuite>(input: &[&[u8]], mode: Mode) -> Result<Self::Scalar>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let dst =
GenericArray::from(STR_HASH_TO_SCALAR).concat(voprf::get_context_string::<CS>(mode));

<Self as GroupDigest>::hash_to_scalar::<ExpandMsgXmd<CS::Hash>>(input, &dst)
.map_err(|_| Error::PointError)
}

fn base_elem() -> Self::Elem {
ProjectivePoint::<Self>::generator()
}

fn identity_elem() -> Self::Elem {
ProjectivePoint::<Self>::identity()
}

fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen> {
let point: AffinePoint<Self> = elem.into();
let bytes = point.to_encoded_point(true);
let bytes = bytes.as_bytes();
let mut result = GenericArray::default();
result[..bytes.len()].copy_from_slice(bytes);
result
}

fn deserialize_elem(element_bits: &GenericArray<u8, Self::ElemLen>) -> Result<Self::Elem> {
PublicKey::<Self>::from_sec1_bytes(element_bits)
.map(|public_key| public_key.to_projective())
.map_err(|_| Error::PointError)
}

fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
*SecretKey::<Self>::random(rng).to_nonzero_scalar()
}

fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar {
Option::from(scalar.invert()).unwrap()
}

#[cfg(test)]
fn zero_scalar() -> Self::Scalar {
Scalar::<Self>::zero()
}

fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
scalar.into()
}

fn deserialize_scalar(scalar_bits: &GenericArray<u8, Self::ScalarLen>) -> Result<Self::Scalar> {
SecretKey::<Self>::from_be_bytes(scalar_bits)
.map(|secret_key| *secret_key.to_nonzero_scalar())
.map_err(|_| Error::ScalarError)
}
}
Loading