diff --git a/benches/oprf.rs b/benches/oprf.rs index f3b8682..b3fbfe7 100644 --- a/benches/oprf.rs +++ b/benches/oprf.rs @@ -14,7 +14,7 @@ use opaque_ke::{ oprf::{blind_shim, evaluate_shim, unblind_and_finalize_shim}, }; use rand::{prelude::ThreadRng, thread_rng}; -use sha2::Sha256; +use sha2::{Sha256, Sha512}; fn oprf1(c: &mut Criterion) { let mut csprng: ThreadRng = thread_rng(); @@ -22,7 +22,7 @@ fn oprf1(c: &mut Criterion) { c.bench_function("blind with Ristretto", move |b| { b.iter(|| { - blind_shim::<_, RistrettoPoint>(&input[..], &mut csprng).unwrap(); + blind_shim::<_, RistrettoPoint, Sha512>(&input[..], &mut csprng).unwrap(); }) }); } @@ -33,7 +33,7 @@ fn oprf1_edwards(c: &mut Criterion) { c.bench_function("blind with Edwards", move |b| { b.iter(|| { - blind_shim::<_, EdwardsPoint>(&input[..], &mut csprng).unwrap(); + blind_shim::<_, EdwardsPoint, Sha256>(&input[..], &mut csprng).unwrap(); }) }); } @@ -42,7 +42,7 @@ fn oprf2(c: &mut Criterion) { let mut csprng: ThreadRng = thread_rng(); let input = b"hunter2"; - let (_, alpha) = blind_shim::<_, RistrettoPoint>(&input[..], &mut csprng).unwrap(); + let (_, alpha) = blind_shim::<_, RistrettoPoint, Sha512>(&input[..], &mut csprng).unwrap(); let salt_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, @@ -60,7 +60,7 @@ fn oprf2_edwards(c: &mut Criterion) { let mut csprng: ThreadRng = thread_rng(); let input = b"hunter2"; - let (_, alpha) = blind_shim::<_, EdwardsPoint>(&input[..], &mut csprng).unwrap(); + let (_, alpha) = blind_shim::<_, EdwardsPoint, Sha256>(&input[..], &mut csprng).unwrap(); let salt_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, @@ -78,7 +78,7 @@ fn oprf3(c: &mut Criterion) { let mut csprng: ThreadRng = thread_rng(); let input = b"hunter2"; - let (token, alpha) = blind_shim::<_, RistrettoPoint>(&input[..], &mut csprng).unwrap(); + let (token, alpha) = blind_shim::<_, RistrettoPoint, Sha512>(&input[..], &mut csprng).unwrap(); let salt_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, @@ -88,7 +88,7 @@ fn oprf3(c: &mut Criterion) { c.bench_function("unblind_and_finalize with Ristretto", move |b| { b.iter(|| { - let _res = unblind_and_finalize_shim::(&token, beta).unwrap(); + let _res = unblind_and_finalize_shim::(&token, beta).unwrap(); }) }); } @@ -97,7 +97,7 @@ fn oprf3_edwards(c: &mut Criterion) { let mut csprng: ThreadRng = thread_rng(); let input = b"hunter2"; - let (token, alpha) = blind_shim::<_, EdwardsPoint>(&input[..], &mut csprng).unwrap(); + let (token, alpha) = blind_shim::<_, EdwardsPoint, Sha256>(&input[..], &mut csprng).unwrap(); let salt_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, diff --git a/src/group.rs b/src/group.rs index a04e812..78e3f61 100644 --- a/src/group.rs +++ b/src/group.rs @@ -29,7 +29,7 @@ pub trait Group: Sized + for<'a> Mul<&'a ::Scalar, Output = Self> type Scalar: Zeroize; /// The byte length necessary to represent scalars type ScalarLen: ArrayLength; - /// Return a scalat from its fixed-length bytes representation + /// Return a scalar from its fixed-length bytes representation fn from_scalar_slice( scalar_bits: &GenericArray, ) -> Result; diff --git a/src/map_to_curve.rs b/src/map_to_curve.rs index 41e8e54..20655a8 100644 --- a/src/map_to_curve.rs +++ b/src/map_to_curve.rs @@ -15,7 +15,6 @@ use digest::{BlockInput, Digest}; use generic_array::typenum::Unsigned; use generic_array::GenericArray; use hkdf::Hkdf; -use sha2::Sha256; /// A subtrait of Group specifying how to hash a password into a point pub trait GroupWithMapToCurve: Group { @@ -24,7 +23,7 @@ pub trait GroupWithMapToCurve: Group { const SUITE_ID: usize; /// transforms a password and domain separation tag (DST) into a curve point - fn map_to_curve(msg: &[u8], dst: &[u8]) -> Result; + fn map_to_curve(msg: &[u8], dst: &[u8]) -> Result; /// Generates the contextString parameter as defined in /// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-05.txt @@ -34,12 +33,12 @@ pub trait GroupWithMapToCurve: Group { } impl GroupWithMapToCurve for RistrettoPoint { - const SUITE_ID: usize = 0x01; + const SUITE_ID: usize = 0x0001; // Implements the hash_to_ristretto255() function from // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt - fn map_to_curve(msg: &[u8], dst: &[u8]) -> Result { - let uniform_bytes = expand_message_xmd::(msg, dst, 64)?; + fn map_to_curve(msg: &[u8], dst: &[u8]) -> Result { + let uniform_bytes = expand_message_xmd::(msg, dst, 64)?; Ok(::hash_to_curve( &GenericArray::clone_from_slice(&uniform_bytes[..]), )) @@ -47,10 +46,12 @@ impl GroupWithMapToCurve for RistrettoPoint { } impl GroupWithMapToCurve for EdwardsPoint { - const SUITE_ID: usize = 0x09; // FIXME, seemingly unsupported by VOPRF RFC? - fn map_to_curve(msg: &[u8], dst: &[u8]) -> Result { - let (hashed_input, _) = Hkdf::::extract(Some(dst), msg); - Ok(::hash_to_curve(&hashed_input)) + const SUITE_ID: usize = 0x0009; // FIXME, seemingly unsupported by VOPRF RFC? + fn map_to_curve(msg: &[u8], dst: &[u8]) -> Result { + let (hashed_input, _) = Hkdf::::extract(Some(dst), msg); + Ok(::hash_to_curve( + &GenericArray::clone_from_slice(&hashed_input[..]), + )) } } diff --git a/src/opaque.rs b/src/opaque.rs index 37a0d87..4a89edf 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -137,7 +137,7 @@ impl ClientRegistration { ClientRegistrationStartParameters::WithIdentifiers(id_u, id_s) => (id_u, id_s), }; - let (token, alpha) = oprf::blind::( + let (token, alpha) = oprf::blind::( &password, blinding_factor_rng, #[cfg(test)] @@ -584,7 +584,7 @@ impl ClientLogin { } }; - let (token, alpha) = oprf::blind::( + let (token, alpha) = oprf::blind::( &password, rng, #[cfg(test)] diff --git a/src/oprf.rs b/src/oprf.rs index 29a68f7..0cac7a2 100644 --- a/src/oprf.rs +++ b/src/oprf.rs @@ -17,21 +17,21 @@ pub struct Token { pub(crate) blind: Grp::Scalar, } -static STR_VOPRF: &[u8] = b"VOPRF05"; -static STR_VOPRF_FINALIZE: &[u8] = b"VOPRF05-Finalize-"; -static MODE_BASE: u8 = 0x01; +static STR_VOPRF: &[u8] = b"VOPRF06-HashToGroup-"; +static STR_VOPRF_FINALIZE: &[u8] = b"VOPRF06-Finalize-"; +static MODE_BASE: u8 = 0x00; /// 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 blind( +pub(crate) fn blind( input: &[u8], blinding_factor_rng: &mut R, #[cfg(test)] postprocess: fn(G::Scalar) -> G::Scalar, ) -> Result<(Token, G), InternalPakeError> { let dst = [STR_VOPRF, &G::get_context_string(MODE_BASE)].concat(); - let mapped_point = G::map_to_curve(input, &dst)?; + let mapped_point = G::map_to_curve::(input, &dst)?; let blinding_factor = G::random_scalar(blinding_factor_rng); #[cfg(test)] let blind = postprocess(blinding_factor); @@ -84,11 +84,11 @@ pub(crate) fn finalize( #[cfg(feature = "bench")] #[doc(hidden)] #[inline] -pub fn blind_shim( +pub fn blind_shim( input: &[u8], blinding_factor_rng: &mut R, ) -> Result<(Token, G), InternalPakeError> { - blind( + blind::( input, blinding_factor_rng, #[cfg(test)] @@ -129,13 +129,14 @@ mod tests { use curve25519_dalek::ristretto::RistrettoPoint; use generic_array::{arr, GenericArray}; use rand_core::OsRng; + use sha2::Sha256; fn prf( input: &[u8], oprf_key: &[u8; 32], ) -> GenericArray::ElemLen> { let dst = [STR_VOPRF, &RistrettoPoint::get_context_string(MODE_BASE)].concat(); - let point = RistrettoPoint::map_to_curve(input, &dst).unwrap(); + let point = RistrettoPoint::map_to_curve::(input, &dst).unwrap(); let scalar = RistrettoPoint::from_scalar_slice(GenericArray::from_slice(&oprf_key[..])).unwrap(); let res = point * scalar; @@ -148,7 +149,7 @@ mod tests { let input = b"hunter2"; let mut rng = OsRng; let (token, alpha) = - blind::<_, RistrettoPoint>(&input[..], &mut rng, std::convert::identity)?; + blind::<_, RistrettoPoint, Sha256>(&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, @@ -168,12 +169,12 @@ mod tests { let mut input = vec![0u8; 64]; rng.fill_bytes(&mut input); let (token, alpha) = - blind::<_, RistrettoPoint>(&input, &mut rng, std::convert::identity).unwrap(); + blind::<_, RistrettoPoint, Sha256>(&input, &mut rng, std::convert::identity).unwrap(); let res = finalize::(&token.data, &unblind(&token, alpha), b""); let dst = [STR_VOPRF, &RistrettoPoint::get_context_string(MODE_BASE)].concat(); - let point = RistrettoPoint::map_to_curve(&input, &dst).unwrap(); + let point = RistrettoPoint::map_to_curve::(&input, &dst).unwrap(); let res2 = finalize::(&input, &point.to_arr().to_vec(), b""); assert_eq!(res, res2); diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 1449398..8c642ba 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -5,3 +5,4 @@ pub mod mock_rng; mod opaque_ke_test; +mod voprf_test_vectors; diff --git a/src/tests/opaque_ke_test.rs b/src/tests/opaque_ke_test.rs index c606f3c..3272d3b 100644 --- a/src/tests/opaque_ke_test.rs +++ b/src/tests/opaque_ke_test.rs @@ -72,40 +72,40 @@ pub struct TestVectorParameters { static TEST_VECTOR: &str = r#" { - "client_s_pk": "9e1a7d68007f9db6f2cc515c9e42ce52eee78a6e4d6bca5a16d49bbfa4cd462e", - "client_s_sk": "101719337a212b3e55872a0c25874f420bb4510e445c527159ec3e23afe13c7c", - "client_e_pk": "5abe58c49c4fe4b3c82f6313857969cc9a2bc913865026ff39b0235636434844", - "client_e_sk": "78a41d08c2cb8e1dd3818a494136f5ee321ea2478ba161f0bcbc282c68467a5a", - "server_s_pk": "96118ab2892cb29ab29af9e8f0cf7d4d336e757e4f56bbe6afde887714118e12", - "server_s_sk": "70240d3fb3f49a289488f0b89b5f956276766efd5c1254317e72c4cb4d452840", - "server_e_pk": "9fe1377a64db22c3ae9ebe7d6016b5cbb8060448d8b5582548566f41202f7f33", - "server_e_sk": "2021fce09d946296e0a0671555d7552c7f1f69ed9d116cc3b6605449abcf6951", + "client_s_pk": "5320b55752ad2061c1804050f8a225a4ab1184bc17d8fa6b3c86470c33d9ce42", + "client_s_sk": "e8719d2f7aca0ab85a991ac4221ff7fde7665b9bd17658777c5417ef5c59796a", + "client_e_pk": "77fc3fefb8178ae08461756b54364c4f2d1363d5ce3187af128a3f84a6c5722a", + "client_e_sk": "48b452f6d0b28387cfa98245bda9230b6df215ed5b820bffb8511ec6802d8075", + "server_s_pk": "b8c5defc933aaf3640d13f217c392e06fe5fd41fae8571204fcbb804a566cd2e", + "server_s_sk": "60f5ab9ff1a3ba9cf6242812be91635db16e8e1826bf06fb97893d7cf82a2656", + "server_e_pk": "5a314dc389c12bf041b14e131fadebcc98e0fc33d3cd996ad9392c7ae6bff468", + "server_e_sk": "606034b9be54759ea5802a2dd71e4413e98d52c46e60f2d0868642ad9e6a9b46", "id_u": "696455", "id_s": "696453", "password": "70617373776f7264", - "blinding_factor": "2d33e5917ed8ed3b4318dc15d6844fa7fae0b84210ac588350fccf2aa7e26b03", - "oprf_key": "90f32248c84a99c303dd97f5a6b80106a7324dd27cf1a9bdec725ce5be11f205", - "envelope_nonce": "491dc86966491b649bab099c1dd3a7927ea0ecb74b88d5d08c7267f151e9bf67", - "client_nonce": "741fbafaf51abbeab80434c1d1afccc81059b356b84ab704447d9632d5696d91", - "server_nonce": "745d196eac5679020332a2c237e63f56310741dbceb714cad4ea28680e5e52c9", + "blinding_factor": "c0accb2010d728cfce827d4cb3769000c8b42ac341db8ba196fbe75809d91300", + "oprf_key": "7f1d2a048a7aad9d1cf2b96473e3adbb1e0626eafe0abdfd09f1f700beda9d0f", + "envelope_nonce": "abb706633ac7092e2aa63dedd4b456d7d99870f099c4f2c51ba75da0f20db8e8", + "client_nonce": "1a92d39b2f9acdbef96dcc586b35ae056a085ede41b05f9f81801f69558d44c7", + "server_nonce": "e04e9f1a4d35882c0ce435e3f467f601f26fe246e5f60ee9b600d9a63b91c0ad", "info1": "696e666f31", "info2": "696e666f32", "einfo2": "65696e666f32", "info3": "696e666f33", "einfo3": "65696e666f33", - "r1": "00204e61b9b1f2efbc4da7f114b9b8585ece25b3faec9c45e49ff60f310f61586417", - "r2": "0020fe43bff51e3868ecc913c97774e070e68acf5532f1862da20e554450f43856a2002096118ab2892cb29ab29af9e8f0cf7d4d336e757e4f56bbe6afde887714118e1201010103", - "r3": "491dc86966491b649bab099c1dd3a7927ea0ecb74b88d5d08c7267f151e9bf67002351e93c57b3c307d92ae0d38cbc9c66636511a3fa97bb34947e85c79dbafd670a22152a002303002096118ab2892cb29ab29af9e8f0cf7d4d336e757e4f56bbe6afde887714118e120020626884cb268cfbb7af1edd23ebe12c0cc995a95dffcf225ad9db330185846e9400209e1a7d68007f9db6f2cc515c9e42ce52eee78a6e4d6bca5a16d49bbfa4cd462e", - "l1": "00204e61b9b1f2efbc4da7f114b9b8585ece25b3faec9c45e49ff60f310f61586417741fbafaf51abbeab80434c1d1afccc81059b356b84ab704447d9632d5696d910005696e666f315abe58c49c4fe4b3c82f6313857969cc9a2bc913865026ff39b0235636434844", - "l2": "0020fe43bff51e3868ecc913c97774e070e68acf5532f1862da20e554450f43856a2491dc86966491b649bab099c1dd3a7927ea0ecb74b88d5d08c7267f151e9bf67002351e93c57b3c307d92ae0d38cbc9c66636511a3fa97bb34947e85c79dbafd670a22152a002303002096118ab2892cb29ab29af9e8f0cf7d4d336e757e4f56bbe6afde887714118e120020626884cb268cfbb7af1edd23ebe12c0cc995a95dffcf225ad9db330185846e942021fce09d946296e0a0671555d7552c7f1f69ed9d116cc3b6605449abcf69510005696e666f329fe1377a64db22c3ae9ebe7d6016b5cbb8060448d8b5582548566f41202f7f33000665696e666f32317af5691eb732163f54e2e497b5a883598baafce9a3a87a710f374b611aa3d5", - "l3": "0005696e666f33000665696e666f339ca8f1957db30ea986d9d29d9efc458476959dde2efeb4308bcde0e807152987", - "client_registration_state": "000369645500036964532d33e5917ed8ed3b4318dc15d6844fa7fae0b84210ac588350fccf2aa7e26b0370617373776f7264", - "client_login_state": "000369645500036964532d33e5917ed8ed3b4318dc15d6844fa7fae0b84210ac588350fccf2aa7e26b0378a41d08c2cb8e1dd3818a494136f5ee321ea2478ba161f0bcbc282c68467a5a741fbafaf51abbeab80434c1d1afccc81059b356b84ab704447d9632d5696d91c346939f310305b1a7e4a3d0958c3c8ecb62ba654f99a3f291aa17f665db721070617373776f7264", - "server_registration_state": "90f32248c84a99c303dd97f5a6b80106a7324dd27cf1a9bdec725ce5be11f205", - "server_login_state": "6b4d09544d05b91e31dca9fc27da533acd02bd7aafd10a715b479c7de2aedcdc5bf8effba8cc499e32894279cd7b70774a264c7813ad97e0360697b0a81ce1adcfa865f87aa285b8938e46b53f497852f2d88cf4c9302bd27debb02f582fb392", - "password_file": "90f32248c84a99c303dd97f5a6b80106a7324dd27cf1a9bdec725ce5be11f2059e1a7d68007f9db6f2cc515c9e42ce52eee78a6e4d6bca5a16d49bbfa4cd462e491dc86966491b649bab099c1dd3a7927ea0ecb74b88d5d08c7267f151e9bf67002351e93c57b3c307d92ae0d38cbc9c66636511a3fa97bb34947e85c79dbafd670a22152a002303002096118ab2892cb29ab29af9e8f0cf7d4d336e757e4f56bbe6afde887714118e120020626884cb268cfbb7af1edd23ebe12c0cc995a95dffcf225ad9db330185846e94", - "export_key": "a8a7ecd9f8e4fe53fd883655fb7eb1b52df11b5956b066dca30af60288520611", - "shared_secret": "cfa865f87aa285b8938e46b53f497852f2d88cf4c9302bd27debb02f582fb392" + "r1": "0020c4ad91692b704470e0613850b9bfcc5b265d43f9ba03dd6ff028f0fb6b365957", + "r2": "00205cbfd7b74c7fe6088f8117e7e63000675762920b75e0f4630ed67d4d960c7aa80020b8c5defc933aaf3640d13f217c392e06fe5fd41fae8571204fcbb804a566cd2e01010103", + "r3": "abb706633ac7092e2aa63dedd4b456d7d99870f099c4f2c51ba75da0f20db8e80023e76da9b9fbf400fea214ecdab24966e4d3fea9d0c3d5d672597e1e702eb30547495ada0023030020b8c5defc933aaf3640d13f217c392e06fe5fd41fae8571204fcbb804a566cd2e00204e0e5754fbbd48efcdab43cee37f6455f357f3edc54316cb69da8f8e92f5ed8900205320b55752ad2061c1804050f8a225a4ab1184bc17d8fa6b3c86470c33d9ce42", + "l1": "0020c4ad91692b704470e0613850b9bfcc5b265d43f9ba03dd6ff028f0fb6b3659571a92d39b2f9acdbef96dcc586b35ae056a085ede41b05f9f81801f69558d44c70005696e666f3177fc3fefb8178ae08461756b54364c4f2d1363d5ce3187af128a3f84a6c5722a", + "l2": "00205cbfd7b74c7fe6088f8117e7e63000675762920b75e0f4630ed67d4d960c7aa8abb706633ac7092e2aa63dedd4b456d7d99870f099c4f2c51ba75da0f20db8e80023e76da9b9fbf400fea214ecdab24966e4d3fea9d0c3d5d672597e1e702eb30547495ada0023030020b8c5defc933aaf3640d13f217c392e06fe5fd41fae8571204fcbb804a566cd2e00204e0e5754fbbd48efcdab43cee37f6455f357f3edc54316cb69da8f8e92f5ed89606034b9be54759ea5802a2dd71e4413e98d52c46e60f2d0868642ad9e6a9b460005696e666f325a314dc389c12bf041b14e131fadebcc98e0fc33d3cd996ad9392c7ae6bff468000665696e666f32661aa1d3e579032df30a78312d38a4c3d8d8a0f1b9bb7bbdbff040e0881a248e", + "l3": "0005696e666f33000665696e666f3317090781af5a220941ddd6db7d4f2af33d9b316a48aee163647131970bae50ba", + "client_registration_state": "00036964550003696453c0accb2010d728cfce827d4cb3769000c8b42ac341db8ba196fbe75809d9130070617373776f7264", + "client_login_state": "00036964550003696453c0accb2010d728cfce827d4cb3769000c8b42ac341db8ba196fbe75809d9130048b452f6d0b28387cfa98245bda9230b6df215ed5b820bffb8511ec6802d80751a92d39b2f9acdbef96dcc586b35ae056a085ede41b05f9f81801f69558d44c7288b735853d9e1d8ab699b7c4aef54680f036c56140dc0a4991f6d02a95babc970617373776f7264", + "server_registration_state": "7f1d2a048a7aad9d1cf2b96473e3adbb1e0626eafe0abdfd09f1f700beda9d0f", + "server_login_state": "2bfb97200c110fa3a9a020920829ce9e7e74a9e843ae86949e16300dc6e25b09e7e0522de623f021a7ba5d150ab8cba2eca4be458d3c22ae6109813a215a03d88f757f3390c767cd9433007c1abf9226c9e0da8e5d1a3d5722773c070b05f44b", + "password_file": "7f1d2a048a7aad9d1cf2b96473e3adbb1e0626eafe0abdfd09f1f700beda9d0f5320b55752ad2061c1804050f8a225a4ab1184bc17d8fa6b3c86470c33d9ce42abb706633ac7092e2aa63dedd4b456d7d99870f099c4f2c51ba75da0f20db8e80023e76da9b9fbf400fea214ecdab24966e4d3fea9d0c3d5d672597e1e702eb30547495ada0023030020b8c5defc933aaf3640d13f217c392e06fe5fd41fae8571204fcbb804a566cd2e00204e0e5754fbbd48efcdab43cee37f6455f357f3edc54316cb69da8f8e92f5ed89", + "export_key": "139998e9d44e2fa629689d8bef9f900a60278e2acd55f4e59906255b10494d58", + "shared_secret": "8f757f3390c767cd9433007c1abf9226c9e0da8e5d1a3d5722773c070b05f44b" } "#; diff --git a/src/tests/voprf_test_vectors.rs b/src/tests/voprf_test_vectors.rs new file mode 100644 index 0000000..6fe1b32 --- /dev/null +++ b/src/tests/voprf_test_vectors.rs @@ -0,0 +1,182 @@ +// 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 crate::{errors::*, group::Group, oprf}; +use curve25519_dalek::ristretto::RistrettoPoint; +use generic_array::GenericArray; +use rand_core::OsRng; +use serde_json::Value; +use sha2::Sha512; + +struct VOPRFTestVectorParameters { + sksm: Vec, + input: Vec, + blind: Vec, + blinded_element: Vec, + evaluation_element: Vec, + unblinded_element: Vec, + info: Vec, + output: Vec, +} + +// Taken from https://github.com/cfrg/draft-irtf-cfrg-voprf/blob/master/draft-irtf-cfrg-voprf.md +// in base mode +static OPRF_RISTRETTO255_SHA512: &'static [&str] = &[ + r#" + { + "sksm": "c604c785ada70d77a5256ae21767de8c3304115237d262134f5e46e512cf8e03", + "input": "00", + "blind": "5ed895206bfc53316d307b23e46ecc6623afb3086da74189a416012be037e50b", + "blinded_element": "5cccd309ec729aebe398c53e19c0ab09c24a29f01036960bdad109852e7bdb44", + "evaluation_element": "86bd5eeabf29a87cb4a5c7207cb3ade5297e65f9b74c979bd3551891f4b21515", + "unblinded_element": "3c7f2d901c0d4f245503a186086fbdf5d8b4408432b25c5163e8b5a19c258348", + "info": "4f505246207465737420766563746f7273", + "output": "0bb570873cc0402ca38f1a2c395301f2a3627616e305f2bc54bb08c3f6ea9871eb71074e52e36b90778ba7c3e3429ef7170245c9e01647f3827fdef84d3ba930" + } + "#, + r#" + { + "sksm": "c604c785ada70d77a5256ae21767de8c3304115237d262134f5e46e512cf8e03", + "input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "blind": "ed8366feb6b1d05d1f46acb727061e43aadfafe9c10e5a64e7518d63e3263503", + "blinded_element": "227d63ca69e93bd062193c1e97fff3d5ebf628f646009d77c4e22ba6429be154", + "evaluation_element": "063b91a12e7cbb98dfeb75d8a7eeb83aacf9fd6df7e0b4197466fb77a27fa631", + "unblinded_element": "804ec6774764ed50a0bbad0a5f477aa04df7323acab8f98ca6e468b7790bca4c", + "info": "4f505246207465737420766563746f7273", + "output": "af7cc264dbc96a6b898ba0fa33bfa9e1407bf1dcfbf8772204d470d4458b8f047806679dbfa251f656b906edf9fa638e268adf979bd0e2380a092047d61f9db9" + } + "#, +]; + +fn decode(values: &Value, key: &str) -> Option> { + values[key] + .as_str() + .and_then(|s| hex::decode(&s.to_string()).ok()) +} + +fn populate_test_vectors(values: &Value) -> VOPRFTestVectorParameters { + VOPRFTestVectorParameters { + sksm: decode(&values, "sksm").unwrap(), + input: decode(&values, "input").unwrap(), + blind: decode(&values, "blind").unwrap(), + blinded_element: decode(&values, "blinded_element").unwrap(), + evaluation_element: decode(&values, "evaluation_element").unwrap(), + unblinded_element: decode(&values, "unblinded_element").unwrap(), + info: decode(&values, "info").unwrap(), + output: decode(&values, "output").unwrap(), + } +} + +// For fixing the blinding factor + +fn postprocess_blinding_factor_oprf_ristretto255_sha512_0(_: G::Scalar) -> G::Scalar { + let parameters = + populate_test_vectors(&serde_json::from_str(OPRF_RISTRETTO255_SHA512[0]).unwrap()); + G::from_scalar_slice(GenericArray::from_slice(¶meters.blind[..])).unwrap() +} + +fn postprocess_blinding_factor_oprf_ristretto255_sha512_1(_: G::Scalar) -> G::Scalar { + let parameters = + populate_test_vectors(&serde_json::from_str(OPRF_RISTRETTO255_SHA512[1]).unwrap()); + G::from_scalar_slice(GenericArray::from_slice(¶meters.blind[..])).unwrap() +} + +// Tests input -> blind, blinded_element +#[test] +fn test_blind() -> Result<(), PakeError> { + for (i, tv) in OPRF_RISTRETTO255_SHA512.iter().enumerate() { + let parameters = populate_test_vectors(&serde_json::from_str(tv).unwrap()); + let mut rng = OsRng; + + let postprocess_fn: fn( + ::Scalar, + ) -> ::Scalar = match i { + 0 => postprocess_blinding_factor_oprf_ristretto255_sha512_0::, + 1 => postprocess_blinding_factor_oprf_ristretto255_sha512_1::, + _ => panic!("Need to cover each test vector"), + }; + + let (token, blinded_element) = oprf::blind::( + ¶meters.input, + &mut rng, + postprocess_fn, + )?; + + assert_eq!( + ¶meters.blind, + &RistrettoPoint::scalar_as_bytes(&token.blind).to_vec() + ); + assert_eq!( + ¶meters.blinded_element, + &blinded_element.to_arr().to_vec() + ); + } + Ok(()) +} + +// Tests sksm, blinded_element -> evaluation_element +#[test] +fn test_evaluate() -> Result<(), PakeError> { + for tv in OPRF_RISTRETTO255_SHA512 { + let parameters = populate_test_vectors(&serde_json::from_str(tv).unwrap()); + let evaluation_element = oprf::evaluate::( + RistrettoPoint::from_element_slice(GenericArray::from_slice( + ¶meters.blinded_element, + )) + .unwrap(), + &RistrettoPoint::from_scalar_slice(GenericArray::from_slice(¶meters.sksm)).unwrap(), + ); + + assert_eq!( + ¶meters.evaluation_element, + &evaluation_element.to_arr().to_vec() + ); + } + Ok(()) +} + +// Tests sksm, evaluation_element -> evaluation_element +#[test] +fn test_unblind() -> Result<(), PakeError> { + for tv in OPRF_RISTRETTO255_SHA512 { + let parameters = populate_test_vectors(&serde_json::from_str(tv).unwrap()); + + let token = oprf::Token { + data: parameters.input, + blind: RistrettoPoint::from_scalar_slice(GenericArray::from_slice( + ¶meters.blind[..], + )) + .unwrap(), + }; + + let unblinded_element = oprf::unblind::( + &token, + RistrettoPoint::from_element_slice(GenericArray::from_slice( + ¶meters.evaluation_element, + )) + .unwrap(), + ); + + assert_eq!(¶meters.unblinded_element, &unblinded_element); + } + Ok(()) +} + +// Tests input, unblinded_element, info -> output +#[test] +fn test_finalize() -> Result<(), PakeError> { + for tv in OPRF_RISTRETTO255_SHA512 { + let parameters = populate_test_vectors(&serde_json::from_str(tv).unwrap()); + + let output = oprf::finalize::( + ¶meters.input, + ¶meters.unblinded_element, + ¶meters.info, + ); + + assert_eq!(¶meters.output, &output.to_vec()); + } + Ok(()) +}