Conform to voprf spec (#71)
This commit is contained in:
+52
-51
@@ -11,47 +11,51 @@ use generic_array::GenericArray;
|
||||
use hkdf::Hkdf;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
pub struct OprfClientBytes<Grp: Group> {
|
||||
pub alpha: Grp,
|
||||
pub blinding_factor: Grp::Scalar,
|
||||
/// Used to store the OPRF input and blinding factor
|
||||
pub struct Token<Grp: Group> {
|
||||
pub(crate) data: Vec<u8>,
|
||||
pub(crate) blind: Grp::Scalar,
|
||||
}
|
||||
|
||||
static STR_VOPRF: &[u8] = b"VOPRF05";
|
||||
|
||||
/// Computes the first step for the multiplicative blinding version of DH-OPRF. This
|
||||
/// message is sent from the client (who holds the input) to the server (who holds the OPRF key).
|
||||
/// The client can also pass in an optional "pepper" string to be mixed in with the input through
|
||||
/// an HKDF computation.
|
||||
pub(crate) fn generate_oprf1<R: RngCore + CryptoRng, G: GroupWithMapToCurve>(
|
||||
pub(crate) fn blind_with_postprocessing<R: RngCore + CryptoRng, G: GroupWithMapToCurve>(
|
||||
input: &[u8],
|
||||
pepper: Option<&[u8]>,
|
||||
blinding_factor_rng: &mut R,
|
||||
) -> Result<OprfClientBytes<G>, InternalPakeError> {
|
||||
let mapped_point = G::map_to_curve(input, pepper);
|
||||
postprocess: fn(G::Scalar) -> G::Scalar,
|
||||
) -> Result<(Token<G>, G), InternalPakeError> {
|
||||
let mapped_point = G::map_to_curve(input, Some(STR_VOPRF)); // TODO: add contextString from RFC
|
||||
let blinding_factor = G::random_scalar(blinding_factor_rng);
|
||||
let alpha = mapped_point * &blinding_factor;
|
||||
Ok(OprfClientBytes {
|
||||
alpha,
|
||||
blinding_factor,
|
||||
})
|
||||
let blind = postprocess(blinding_factor);
|
||||
let blind_token = mapped_point * &blind;
|
||||
Ok((
|
||||
Token {
|
||||
data: input.to_vec(),
|
||||
blind,
|
||||
},
|
||||
blind_token,
|
||||
))
|
||||
}
|
||||
|
||||
/// Computes the second step for the multiplicative blinding version of DH-OPRF. This
|
||||
/// message is sent from the server (who holds the OPRF key) to the client.
|
||||
pub(crate) fn generate_oprf2<G: Group>(
|
||||
point: G,
|
||||
oprf_key: &G::Scalar,
|
||||
) -> Result<G, InternalPakeError> {
|
||||
pub(crate) fn evaluate<G: Group>(point: G, oprf_key: &G::Scalar) -> Result<G, InternalPakeError> {
|
||||
Ok(point * oprf_key)
|
||||
}
|
||||
|
||||
/// Computes the third step for the multiplicative blinding version of DH-OPRF, in which
|
||||
/// the client unblinds the server's message.
|
||||
pub(crate) fn generate_oprf3<G: Group, H: Hash>(
|
||||
input: &[u8],
|
||||
pub(crate) fn unblind_and_finalize<G: Group, H: Hash>(
|
||||
token: &Token<G>,
|
||||
point: G,
|
||||
blinding_factor: &G::Scalar,
|
||||
) -> Result<GenericArray<u8, <H as Digest>::OutputSize>, InternalPakeError> {
|
||||
let unblinded = point * &G::scalar_invert(&blinding_factor);
|
||||
let ikm: Vec<u8> = [&unblinded.to_arr()[..], input].concat();
|
||||
let unblinded = point * &G::scalar_invert(&token.blind);
|
||||
let ikm: Vec<u8> = [&unblinded.to_arr()[..], &token.data].concat();
|
||||
// TODO: implement proper finalizing code here
|
||||
let (prk, _) = Hkdf::<H>::extract(None, &ikm);
|
||||
Ok(prk)
|
||||
}
|
||||
@@ -59,31 +63,26 @@ pub(crate) fn generate_oprf3<G: Group, H: Hash>(
|
||||
// Benchmarking shims
|
||||
#[cfg(feature = "bench")]
|
||||
#[inline]
|
||||
pub fn generate_oprf1_shim<R: RngCore + CryptoRng, G: GroupWithMapToCurve>(
|
||||
pub fn blind_shim<R: RngCore + CryptoRng, G: GroupWithMapToCurve>(
|
||||
input: &[u8],
|
||||
pepper: Option<&[u8]>,
|
||||
blinding_factor_rng: &mut R,
|
||||
) -> Result<OprfClientBytes<G>, InternalPakeError> {
|
||||
generate_oprf1(input, pepper, blinding_factor_rng)
|
||||
) -> Result<(Token<G>, G), InternalPakeError> {
|
||||
blind_with_postprocessing(input, blinding_factor_rng, std::convert::identity)
|
||||
}
|
||||
|
||||
#[cfg(feature = "bench")]
|
||||
#[inline]
|
||||
pub fn generate_oprf2_shim<G: Group>(
|
||||
point: G,
|
||||
oprf_key: &G::Scalar,
|
||||
) -> Result<G, InternalPakeError> {
|
||||
generate_oprf2(point, oprf_key)
|
||||
pub fn evaluate_shim<G: Group>(point: G, oprf_key: &G::Scalar) -> Result<G, InternalPakeError> {
|
||||
evaluate(point, oprf_key)
|
||||
}
|
||||
|
||||
#[cfg(feature = "bench")]
|
||||
#[inline]
|
||||
pub fn generate_oprf3_shim<G: Group, H: Hash>(
|
||||
input: &[u8],
|
||||
pub fn unblind_and_finalize_shim<G: Group, H: Hash>(
|
||||
token: &Token<G>,
|
||||
point: G,
|
||||
blinding_factor: &G::Scalar,
|
||||
) -> Result<GenericArray<u8, <H as Digest>::OutputSize>, InternalPakeError> {
|
||||
generate_oprf3::<G, H>(input, point, blinding_factor)
|
||||
unblind_and_finalize::<G, H>(token, point)
|
||||
}
|
||||
|
||||
// Tests
|
||||
@@ -103,7 +102,7 @@ mod tests {
|
||||
input: &[u8],
|
||||
oprf_key: &[u8; 32],
|
||||
) -> GenericArray<u8, <RistrettoPoint as Group>::ElemLen> {
|
||||
let (hashed_input, _) = Hkdf::<Sha512>::extract(None, &input);
|
||||
let (hashed_input, _) = Hkdf::<Sha512>::extract(Some(STR_VOPRF), &input);
|
||||
let point = RistrettoPoint::hash_to_curve(GenericArray::from_slice(&hashed_input));
|
||||
let scalar =
|
||||
RistrettoPoint::from_scalar_slice(GenericArray::from_slice(&oprf_key[..])).unwrap();
|
||||
@@ -118,18 +117,19 @@ mod tests {
|
||||
fn oprf_retrieval() -> Result<(), InternalPakeError> {
|
||||
let input = b"hunter2";
|
||||
let mut rng = OsRng;
|
||||
let OprfClientBytes {
|
||||
alpha,
|
||||
blinding_factor,
|
||||
} = generate_oprf1::<_, RistrettoPoint>(&input[..], None, &mut rng)?;
|
||||
let salt_bytes = arr![
|
||||
let (token, alpha) = blind_with_postprocessing::<_, RistrettoPoint>(
|
||||
&input[..],
|
||||
&mut rng,
|
||||
std::convert::identity,
|
||||
)?;
|
||||
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 salt = RistrettoPoint::from_scalar_slice(&salt_bytes)?;
|
||||
let beta = generate_oprf2::<RistrettoPoint>(alpha, &salt)?;
|
||||
let res = generate_oprf3::<RistrettoPoint, sha2::Sha256>(input, beta, &blinding_factor)?;
|
||||
let res2 = prf(&input[..], &salt.as_bytes());
|
||||
let oprf_key = RistrettoPoint::from_scalar_slice(&oprf_key_bytes)?;
|
||||
let beta = evaluate::<RistrettoPoint>(alpha, &oprf_key)?;
|
||||
let res = unblind_and_finalize::<RistrettoPoint, sha2::Sha256>(&token, beta)?;
|
||||
let res2 = prf(&input[..], &oprf_key.as_bytes());
|
||||
assert_eq!(res, res2);
|
||||
Ok(())
|
||||
}
|
||||
@@ -139,14 +139,15 @@ mod tests {
|
||||
let mut rng = OsRng;
|
||||
let mut input = vec![0u8; 64];
|
||||
rng.fill_bytes(&mut input);
|
||||
let OprfClientBytes {
|
||||
alpha,
|
||||
blinding_factor,
|
||||
} = generate_oprf1::<_, RistrettoPoint>(&input, None, &mut rng).unwrap();
|
||||
let res = generate_oprf3::<RistrettoPoint, sha2::Sha256>(&input, alpha, &blinding_factor)
|
||||
.unwrap();
|
||||
let (token, alpha) = blind_with_postprocessing::<_, RistrettoPoint>(
|
||||
&input,
|
||||
&mut rng,
|
||||
std::convert::identity,
|
||||
)
|
||||
.unwrap();
|
||||
let res = unblind_and_finalize::<RistrettoPoint, sha2::Sha256>(&token, alpha).unwrap();
|
||||
|
||||
let (hashed_input, _) = Hkdf::<Sha512>::extract(None, &input);
|
||||
let (hashed_input, _) = Hkdf::<Sha512>::extract(Some(STR_VOPRF), &input);
|
||||
let mut bits = [0u8; 64];
|
||||
bits.copy_from_slice(&hashed_input);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user