Minor fixups, adding more tests, making p256 experimental (#14)
This commit is contained in:
+1
-2
@@ -5,8 +5,7 @@
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
//! Defines the Group trait to specify the underlying prime order group used in
|
||||
//! OPAQUE's OPRF
|
||||
//! Defines the Group trait to specify the underlying prime order group
|
||||
|
||||
mod expand;
|
||||
#[cfg(feature = "p256")]
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
// Note: This group implementation of p256 is experimental for now,
|
||||
// until hash-to-curve or crypto-bigint are fully supported.
|
||||
|
||||
#![allow(
|
||||
clippy::borrow_interior_mutable_const,
|
||||
clippy::declare_interior_mutable_const
|
||||
@@ -166,6 +169,11 @@ impl Group for ProjectivePoint {
|
||||
|
||||
/// Corresponds to the hash_to_curve_simple_swu() function defined in
|
||||
/// <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-F.2>
|
||||
///
|
||||
/// `cmov`, `mod_floor` and `modpow` needs to be made constant-time, which
|
||||
/// will be supported after crypto-bigint is no longer experimental. See
|
||||
/// https://github.com/novifinancial/opaque-ke/issues/239 for more context.
|
||||
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
fn hash_to_curve_simple_swu<N: ArrayLength<u8>>(
|
||||
u: &[u8],
|
||||
|
||||
+2
-1
@@ -459,7 +459,8 @@
|
||||
//!
|
||||
//! # Features
|
||||
//!
|
||||
//! - The `p256` feature enables using p256 as the underlying group for the [Ciphersuite] choice
|
||||
//! - The `p256` feature enables using p256 as the underlying group for the [Ciphersuite] choice.
|
||||
//! Note that this is currently an experimental feature ⚠️, and is not yet ready for production use.
|
||||
//!
|
||||
//! - The `serialize` feature, enabled by default, provides convenience functions for serializing and deserializing with
|
||||
//! [serde](https://serde.rs/).
|
||||
|
||||
+151
-29
@@ -35,6 +35,7 @@ static STR_VOPRF: &[u8] = b"VOPRF07-";
|
||||
|
||||
/// Determines the mode of operation (either base mode or
|
||||
/// verifiable mode)
|
||||
#[derive(Clone, Copy)]
|
||||
enum Mode {
|
||||
Base = 0,
|
||||
Verifiable = 1,
|
||||
@@ -778,50 +779,38 @@ fn get_context_string<CS: CipherSuite>(mode: Mode) -> Result<alloc::vec::Vec<u8>
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::group::Group;
|
||||
use generic_array::{arr, GenericArray};
|
||||
use generic_array::GenericArray;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
fn prf<CS: CipherSuite>(
|
||||
input: &[u8],
|
||||
oprf_key: &[u8],
|
||||
key: <CS::Group as Group>::Scalar,
|
||||
info: &[u8],
|
||||
mode: Mode,
|
||||
) -> GenericArray<u8, <CS::Hash as Digest>::OutputSize> {
|
||||
let dst = [
|
||||
STR_HASH_TO_GROUP,
|
||||
&get_context_string::<CS>(Mode::Base).unwrap(),
|
||||
]
|
||||
.concat();
|
||||
let dst = [STR_HASH_TO_GROUP, &get_context_string::<CS>(mode).unwrap()].concat();
|
||||
let point = CS::Group::hash_to_curve::<CS::Hash>(input, &dst).unwrap();
|
||||
let scalar = CS::Group::from_scalar_slice(GenericArray::from_slice(&oprf_key[..])).unwrap();
|
||||
|
||||
let context = [
|
||||
STR_CONTEXT,
|
||||
&get_context_string::<CS>(Mode::Base).unwrap(),
|
||||
&get_context_string::<CS>(mode).unwrap(),
|
||||
&serialize(info, 2).unwrap(),
|
||||
]
|
||||
.concat();
|
||||
let dst = [
|
||||
STR_HASH_TO_SCALAR,
|
||||
&get_context_string::<CS>(Mode::Base).unwrap(),
|
||||
]
|
||||
.concat();
|
||||
let dst = [STR_HASH_TO_SCALAR, &get_context_string::<CS>(mode).unwrap()].concat();
|
||||
let m = <CS::Group as Group>::hash_to_scalar::<CS::Hash>(&context, &dst).unwrap();
|
||||
|
||||
let res = point * &<CS::Group as Group>::scalar_invert(&(scalar + &m));
|
||||
let res = point * &<CS::Group as Group>::scalar_invert(&(key + &m));
|
||||
|
||||
finalize_after_unblind::<CS>(&[(input.to_vec(), res)], info, Mode::Base).unwrap()[0].clone()
|
||||
finalize_after_unblind::<CS>(&[(input.to_vec(), res)], info, mode).unwrap()[0].clone()
|
||||
}
|
||||
|
||||
fn oprf_retrieval<CS: CipherSuite>() {
|
||||
let input = b"hunter2";
|
||||
fn base_retrieval<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
let info = b"info";
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = NonVerifiableClient::<CS>::blind(&input[..], &mut rng).unwrap();
|
||||
let oprf_key_bytes = arr![
|
||||
u8; 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
|
||||
24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
];
|
||||
let server = NonVerifiableServer::<CS>::new_with_key(&oprf_key_bytes).unwrap();
|
||||
let server = NonVerifiableServer::<CS>::new(&mut rng).unwrap();
|
||||
let server_result = server
|
||||
.evaluate(client_blind_result.message, &Metadata(info.to_vec()))
|
||||
.unwrap();
|
||||
@@ -829,11 +818,136 @@ mod tests {
|
||||
.state
|
||||
.finalize(server_result.message, &Metadata(info.to_vec()))
|
||||
.unwrap();
|
||||
let res2 = prf::<CS>(&input[..], &oprf_key_bytes, info);
|
||||
let res2 = prf::<CS>(&input[..], server.get_private_key(), info, Mode::Base);
|
||||
assert_eq!(client_finalize_result.output, res2);
|
||||
}
|
||||
|
||||
fn oprf_inversion_unsalted<CS: CipherSuite>() {
|
||||
fn verifiable_retrieval<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
let info = b"info";
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = VerifiableClient::<CS>::blind(&input[..], &mut rng).unwrap();
|
||||
let server = VerifiableServer::<CS>::new(&mut rng).unwrap();
|
||||
let server_result = server
|
||||
.evaluate(
|
||||
&mut rng,
|
||||
client_blind_result.message,
|
||||
&Metadata(info.to_vec()),
|
||||
)
|
||||
.unwrap();
|
||||
let client_finalize_result = client_blind_result
|
||||
.state
|
||||
.finalize(
|
||||
server_result.message,
|
||||
server_result.proof,
|
||||
server.get_public_key(),
|
||||
&Metadata(info.to_vec()),
|
||||
)
|
||||
.unwrap();
|
||||
let res2 = prf::<CS>(&input[..], server.get_private_key(), info, Mode::Verifiable);
|
||||
assert_eq!(client_finalize_result.output, res2);
|
||||
}
|
||||
|
||||
fn verifiable_bad_public_key<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
let info = b"info";
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = VerifiableClient::<CS>::blind(&input[..], &mut rng).unwrap();
|
||||
let server = VerifiableServer::<CS>::new(&mut rng).unwrap();
|
||||
let server_result = server
|
||||
.evaluate(
|
||||
&mut rng,
|
||||
client_blind_result.message,
|
||||
&Metadata(info.to_vec()),
|
||||
)
|
||||
.unwrap();
|
||||
let wrong_pk = {
|
||||
// Choose a group element that is unlikely to be the right public key
|
||||
CS::Group::hash_to_curve::<CS::Hash>(b"msg", b"dst").unwrap()
|
||||
};
|
||||
let client_finalize_result = client_blind_result.state.finalize(
|
||||
server_result.message,
|
||||
server_result.proof,
|
||||
wrong_pk,
|
||||
&Metadata(info.to_vec()),
|
||||
);
|
||||
assert!(client_finalize_result.is_err());
|
||||
}
|
||||
|
||||
fn verifiable_batch_retrieval<CS: CipherSuite>() {
|
||||
let info = b"info";
|
||||
let mut rng = OsRng;
|
||||
let mut inputs = vec![];
|
||||
let mut client_states = vec![];
|
||||
let mut client_messages = vec![];
|
||||
let num_iterations = 10;
|
||||
for _ in 0..num_iterations {
|
||||
let mut input = vec![0u8; 32];
|
||||
rng.fill_bytes(&mut input);
|
||||
let client_blind_result = VerifiableClient::<CS>::blind(&input[..], &mut rng).unwrap();
|
||||
inputs.push(input);
|
||||
client_states.push(client_blind_result.state);
|
||||
client_messages.push(client_blind_result.message);
|
||||
}
|
||||
let server = VerifiableServer::<CS>::new(&mut rng).unwrap();
|
||||
let server_result = server
|
||||
.batch_evaluate(&mut rng, &client_messages, &Metadata(info.to_vec()))
|
||||
.unwrap();
|
||||
let batch_finalize_input = BatchFinalizeInput::new(client_states, server_result.messages);
|
||||
let client_finalize_result = VerifiableClient::batch_finalize(
|
||||
batch_finalize_input,
|
||||
server_result.proof,
|
||||
server.get_public_key(),
|
||||
&Metadata(info.to_vec()),
|
||||
)
|
||||
.unwrap();
|
||||
let mut res2 = vec![];
|
||||
for i in 0..num_iterations {
|
||||
let output = prf::<CS>(
|
||||
&inputs[i][..],
|
||||
server.get_private_key(),
|
||||
info,
|
||||
Mode::Verifiable,
|
||||
);
|
||||
res2.push(output);
|
||||
}
|
||||
assert_eq!(client_finalize_result.outputs, res2);
|
||||
}
|
||||
|
||||
fn verifiable_batch_bad_public_key<CS: CipherSuite>() {
|
||||
let info = b"info";
|
||||
let mut rng = OsRng;
|
||||
let mut inputs = vec![];
|
||||
let mut client_states = vec![];
|
||||
let mut client_messages = vec![];
|
||||
let num_iterations = 10;
|
||||
for _ in 0..num_iterations {
|
||||
let mut input = vec![0u8; 32];
|
||||
rng.fill_bytes(&mut input);
|
||||
let client_blind_result = VerifiableClient::<CS>::blind(&input[..], &mut rng).unwrap();
|
||||
inputs.push(input);
|
||||
client_states.push(client_blind_result.state);
|
||||
client_messages.push(client_blind_result.message);
|
||||
}
|
||||
let server = VerifiableServer::<CS>::new(&mut rng).unwrap();
|
||||
let server_result = server
|
||||
.batch_evaluate(&mut rng, &client_messages, &Metadata(info.to_vec()))
|
||||
.unwrap();
|
||||
let batch_finalize_input = BatchFinalizeInput::new(client_states, server_result.messages);
|
||||
let wrong_pk = {
|
||||
// Choose a group element that is unlikely to be the right public key
|
||||
CS::Group::hash_to_curve::<CS::Hash>(b"msg", b"dst").unwrap()
|
||||
};
|
||||
let client_finalize_result = VerifiableClient::batch_finalize(
|
||||
batch_finalize_input,
|
||||
server_result.proof,
|
||||
wrong_pk,
|
||||
&Metadata(info.to_vec()),
|
||||
);
|
||||
assert!(client_finalize_result.is_err());
|
||||
}
|
||||
|
||||
fn base_inversion_unsalted<CS: CipherSuite>() {
|
||||
let mut rng = OsRng;
|
||||
let mut input = alloc::vec![0u8; 64];
|
||||
rng.fill_bytes(&mut input);
|
||||
@@ -866,15 +980,23 @@ mod tests {
|
||||
fn test_functionality() -> Result<(), InternalError> {
|
||||
use crate::tests::Ristretto255Sha512;
|
||||
|
||||
oprf_retrieval::<Ristretto255Sha512>();
|
||||
oprf_inversion_unsalted::<Ristretto255Sha512>();
|
||||
base_retrieval::<Ristretto255Sha512>();
|
||||
base_inversion_unsalted::<Ristretto255Sha512>();
|
||||
verifiable_retrieval::<Ristretto255Sha512>();
|
||||
verifiable_batch_retrieval::<Ristretto255Sha512>();
|
||||
verifiable_bad_public_key::<Ristretto255Sha512>();
|
||||
verifiable_batch_bad_public_key::<Ristretto255Sha512>();
|
||||
|
||||
#[cfg(feature = "p256")]
|
||||
{
|
||||
use crate::tests::P256Sha256;
|
||||
|
||||
oprf_retrieval::<P256Sha256>();
|
||||
oprf_inversion_unsalted::<P256Sha256>();
|
||||
base_retrieval::<P256Sha256>();
|
||||
base_inversion_unsalted::<P256Sha256>();
|
||||
verifiable_retrieval::<P256Sha256>();
|
||||
verifiable_batch_retrieval::<P256Sha256>();
|
||||
verifiable_bad_public_key::<P256Sha256>();
|
||||
verifiable_batch_bad_public_key::<P256Sha256>();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user