diff --git a/Cargo.lock b/Cargo.lock index 586a43f..3c2838a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -280,6 +280,12 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +[[package]] +name = "fiat-crypto" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f6ab97095615857b6ad00a8330fff0e443f1def9fd357cef82d0ca0677b616b" + [[package]] name = "fnv" version = "1.0.7" @@ -465,6 +471,7 @@ dependencies = [ "curve25519-dalek", "digest", "displaydoc", + "fiat-crypto", "generic-array", "hex", "hkdf", @@ -476,6 +483,7 @@ dependencies = [ "scrypt", "serde_json", "sha2", + "subtle", "thiserror", "x25519-dalek", "zeroize", diff --git a/Cargo.toml b/Cargo.toml index 66e49b9..2e9be34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,12 +20,14 @@ u32_backend = ["curve25519-dalek/u32_backend", "x25519-dalek/u32_backend"] curve25519-dalek = { version = "3.0.0", default-features = false, features = ["std"] } digest = "0.9.0" displaydoc = "0.1.7" +fiat-crypto = { version = "0.1.5"} generic-array = "0.14.4" hkdf = "0.9.0" hmac = "0.9.0" rand_core = "0.5.1" scrypt = { version = "0.4.1", optional = true } sha2 = "0.9.1" +subtle = { version = "^2.2.1", default-features = false } thiserror = "1.0.20" x25519-dalek = { version = "1.0.1", default-features = false, features = ["std"] } zeroize = "1.1" diff --git a/src/elligator/field.rs b/src/elligator/field.rs new file mode 100644 index 0000000..1c070e7 --- /dev/null +++ b/src/elligator/field.rs @@ -0,0 +1,353 @@ +// 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. +#![allow(clippy::let_and_return)] + +//! Field arithmetic modulo \\(p = 2\^{255} - 19\\), using \\(64\\)-bit +//! limbs with \\(128\\)-bit products. + +use core::fmt::Debug; +use core::ops::Neg; +use core::ops::{Add, AddAssign}; +use core::ops::{Mul, MulAssign}; + +use subtle::Choice; +use subtle::ConditionallyNegatable; +use subtle::ConditionallySelectable; +use subtle::ConstantTimeEq; + +use zeroize::Zeroize; + +use fiat_crypto::curve25519_64::*; + +/// A `FieldElement51` represents an element of the field +/// \\( \mathbb Z / (2\^{255} - 19)\\). +/// +/// In the 64-bit implementation, a `FieldElement` is represented in +/// radix \\(2\^{51}\\) as five `u64`s; the coefficients are allowed to +/// grow up to \\(2\^{54}\\) between reductions modulo \\(p\\). +/// +/// # Note +/// +/// The `curve25519_dalek::field` module provides a type alias +/// `curve25519_dalek::field::FieldElement` to either `FieldElement51` +/// or `FieldElement2625`. +/// +/// The backend-specific type `FieldElement51` should not be used +/// outside of the `curve25519_dalek::field` module. +#[derive(Copy, Clone)] +pub struct FieldElement51(pub(crate) [u64; 5]); + +impl Debug for FieldElement51 { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "FieldElement51({:?})", &self.0[..]) + } +} + +impl Zeroize for FieldElement51 { + fn zeroize(&mut self) { + self.0.zeroize(); + } +} + +impl ConstantTimeEq for FieldElement51 { + /// Test equality between two `FieldElement`s. Since the + /// internal representation is not canonical, the field elements + /// are normalized to wire format before comparison. + fn ct_eq(&self, other: &FieldElement51) -> Choice { + self.to_bytes().ct_eq(&other.to_bytes()) + } +} + +impl<'b> AddAssign<&'b FieldElement51> for FieldElement51 { + fn add_assign(&mut self, _rhs: &'b FieldElement51) { + let input = self.0; + fiat_25519_add(&mut self.0, &input, &_rhs.0); + let input = self.0; + fiat_25519_carry(&mut self.0, &input); + } +} + +impl<'a, 'b> Add<&'b FieldElement51> for &'a FieldElement51 { + type Output = FieldElement51; + fn add(self, _rhs: &'b FieldElement51) -> FieldElement51 { + let mut output = *self; + fiat_25519_add(&mut output.0, &self.0, &_rhs.0); + let input = output.0; + fiat_25519_carry(&mut output.0, &input); + output + } +} + +impl<'b> MulAssign<&'b FieldElement51> for FieldElement51 { + fn mul_assign(&mut self, _rhs: &'b FieldElement51) { + let input = self.0; + fiat_25519_carry_mul(&mut self.0, &input, &_rhs.0); + } +} + +impl<'a, 'b> Mul<&'b FieldElement51> for &'a FieldElement51 { + type Output = FieldElement51; + fn mul(self, _rhs: &'b FieldElement51) -> FieldElement51 { + let mut output = *self; + fiat_25519_carry_mul(&mut output.0, &self.0, &_rhs.0); + output + } +} + +impl<'a> Neg for &'a FieldElement51 { + type Output = FieldElement51; + fn neg(self) -> FieldElement51 { + let mut output = *self; + fiat_25519_opp(&mut output.0, &self.0); + let input = output.0; + fiat_25519_carry(&mut output.0, &input); + output + } +} + +impl ConditionallySelectable for FieldElement51 { + fn conditional_select( + a: &FieldElement51, + b: &FieldElement51, + choice: Choice, + ) -> FieldElement51 { + let mut output = [0u64; 5]; + fiat_25519_selectznz(&mut output, choice.unwrap_u8() as fiat_25519_u1, &a.0, &b.0); + FieldElement51(output) + } + + fn conditional_swap(a: &mut FieldElement51, b: &mut FieldElement51, choice: Choice) { + u64::conditional_swap(&mut a.0[0], &mut b.0[0], choice); + u64::conditional_swap(&mut a.0[1], &mut b.0[1], choice); + u64::conditional_swap(&mut a.0[2], &mut b.0[2], choice); + u64::conditional_swap(&mut a.0[3], &mut b.0[3], choice); + u64::conditional_swap(&mut a.0[4], &mut b.0[4], choice); + } + + fn conditional_assign(&mut self, _rhs: &FieldElement51, choice: Choice) { + let mut output = [0u64; 5]; + let choicebit = choice.unwrap_u8() as fiat_25519_u1; + fiat_25519_cmovznz_u64(&mut output[0], choicebit, self.0[0], _rhs.0[0]); + fiat_25519_cmovznz_u64(&mut output[1], choicebit, self.0[1], _rhs.0[1]); + fiat_25519_cmovznz_u64(&mut output[2], choicebit, self.0[2], _rhs.0[2]); + fiat_25519_cmovznz_u64(&mut output[3], choicebit, self.0[3], _rhs.0[3]); + fiat_25519_cmovznz_u64(&mut output[4], choicebit, self.0[4], _rhs.0[4]); + *self = FieldElement51(output); + } +} + +impl FieldElement51 { + /// Construct zero. + pub fn zero() -> FieldElement51 { + FieldElement51([0, 0, 0, 0, 0]) + } + + /// Construct one. + pub fn one() -> FieldElement51 { + FieldElement51([1, 0, 0, 0, 0]) + } + + pub fn is_negative(&self) -> Choice { + let bytes = self.to_bytes(); + (bytes[0] & 1).into() + } + + /// Raise this field element to the power (p-5)/8 = 2^252 -3. + fn pow_p58(&self) -> FieldElement51 { + // The bits of (p-5)/8 are 101111.....11. + // + // nonzero bits of exponent + let (t19, _) = self.pow22501(); // 249..0 + let t20 = t19.pow2k(2); // 251..2 + let t21 = self * &t20; // 251..2,0 + + t21 + } + + /// Given a nonzero field element, compute its inverse. + /// + /// The inverse is computed as self^(p-2), since + /// x^(p-2)x = x^(p-1) = 1 (mod p). + /// + /// This function returns zero on input zero. + pub fn invert(&self) -> FieldElement51 { + // The bits of p-2 = 2^255 -19 -2 are 11010111111...11. + // + // nonzero bits of exponent + let (t19, t3) = self.pow22501(); // t19: 249..0 ; t3: 3,1,0 + let t20 = t19.pow2k(5); // 254..5 + let t21 = &t20 * &t3; // 254..5,3,1,0 + + t21 + } + + /// Compute (self^(2^250-1), self^11), used as a helper function + /// within invert() and pow22523(). + fn pow22501(&self) -> (FieldElement51, FieldElement51) { + // Instead of managing which temporary variables are used + // for what, we define as many as we need and leave stack + // allocation to the compiler + // + // Each temporary variable t_i is of the form (self)^e_i. + // Squaring t_i corresponds to multiplying e_i by 2, + // so the pow2k function shifts e_i left by k places. + // Multiplying t_i and t_j corresponds to adding e_i + e_j. + // + // Temporary t_i Nonzero bits of e_i + // + let t0 = self.square(); // 1 e_0 = 2^1 + let t1 = t0.square().square(); // 3 e_1 = 2^3 + let t2 = self * &t1; // 3,0 e_2 = 2^3 + 2^0 + let t3 = &t0 * &t2; // 3,1,0 + let t4 = t3.square(); // 4,2,1 + let t5 = &t2 * &t4; // 4,3,2,1,0 + let t6 = t5.pow2k(5); // 9,8,7,6,5 + let t7 = &t6 * &t5; // 9,8,7,6,5,4,3,2,1,0 + let t8 = t7.pow2k(10); // 19..10 + let t9 = &t8 * &t7; // 19..0 + let t10 = t9.pow2k(20); // 39..20 + let t11 = &t10 * &t9; // 39..0 + let t12 = t11.pow2k(10); // 49..10 + let t13 = &t12 * &t7; // 49..0 + let t14 = t13.pow2k(50); // 99..50 + let t15 = &t14 * &t13; // 99..0 + let t16 = t15.pow2k(100); // 199..100 + let t17 = &t16 * &t15; // 199..0 + let t18 = t17.pow2k(50); // 249..50 + let t19 = &t18 * &t13; // 249..0 + + (t19, t3) + } + + /// Load a `FieldElement51` from the low 255 bits of a 256-bit + /// input. + /// + /// # Warning + /// + /// This function does not check that the input used the canonical + /// representative. It masks the high bit, but it will happily + /// decode 2^255 - 18 to 1. Applications that require a canonical + /// encoding of every field element should decode, re-encode to + /// the canonical encoding, and check that the input was + /// canonical. + /// + pub fn from_bytes(bytes: &[u8; 32]) -> FieldElement51 { + let mut temp = [0u8; 32]; + temp.copy_from_slice(bytes); + temp[31] &= 127u8; + let mut output = [0u64; 5]; + fiat_25519_from_bytes(&mut output, &temp); + FieldElement51(output) + } + + /// Serialize this `FieldElement51` to a 32-byte array. The + /// encoding is canonical. + pub fn to_bytes(&self) -> [u8; 32] { + let mut bytes = [0u8; 32]; + fiat_25519_to_bytes(&mut bytes, &self.0); + bytes + } + + /// Given `k > 0`, return `self^(2^k)`. + pub fn pow2k(&self, mut k: u32) -> FieldElement51 { + let mut output = *self; + loop { + let input = output.0; + fiat_25519_carry_square(&mut output.0, &input); + k -= 1; + if k == 0 { + return output; + } + } + } + + /// Given `FieldElements` `u` and `v`, compute either `sqrt(u/v)` + /// or `sqrt(i*u/v)` in constant time. + /// + /// This function always returns the nonnegative square root. + /// + /// # Return + /// + /// - `(Choice(1), +sqrt(u/v)) ` if `v` is nonzero and `u/v` is square; + /// - `(Choice(1), zero) ` if `u` is zero; + /// - `(Choice(0), zero) ` if `v` is zero and `u` is nonzero; + /// - `(Choice(0), +sqrt(i*u/v))` if `u/v` is nonsquare (so `i*u/v` is square). + /// + pub fn sqrt_ratio_i(u: &FieldElement51, v: &FieldElement51) -> (Choice, FieldElement51) { + // Using the same trick as in ed25519 decoding, we merge the + // inversion, the square root, and the square test as follows. + // + // To compute sqrt(α), we can compute β = α^((p+3)/8). + // Then β^2 = ±α, so multiplying β by sqrt(-1) if necessary + // gives sqrt(α). + // + // To compute 1/sqrt(α), we observe that + // 1/β = α^(p-1 - (p+3)/8) = α^((7p-11)/8) + // = α^3 * (α^7)^((p-5)/8). + // + // We can therefore compute sqrt(u/v) = sqrt(u)/sqrt(v) + // by first computing + // r = u^((p+3)/8) v^(p-1-(p+3)/8) + // = u u^((p-5)/8) v^3 (v^7)^((p-5)/8) + // = (uv^3) (uv^7)^((p-5)/8). + // + // If v is nonzero and u/v is square, then r^2 = ±u/v, + // so vr^2 = ±u. + // If vr^2 = u, then sqrt(u/v) = r. + // If vr^2 = -u, then sqrt(u/v) = r*sqrt(-1). + // + // If v is zero, r is also zero. + + let v3 = &v.square() * v; + let v7 = &v3.square() * v; + let mut r = &(u * &v3) * &(u * &v7).pow_p58(); + let check = v * &r.square(); + + let i = &SQRT_M1; + + let correct_sign_sqrt = check.ct_eq(u); + let flipped_sign_sqrt = check.ct_eq(&(-u)); + let flipped_sign_sqrt_i = check.ct_eq(&(&(-u) * i)); + + let r_prime = &SQRT_M1 * &r; + r.conditional_assign(&r_prime, flipped_sign_sqrt | flipped_sign_sqrt_i); + + // Choose the nonnegative square root. + let r_is_negative = r.is_negative(); + r.conditional_negate(r_is_negative); + + let was_nonzero_square = correct_sign_sqrt | flipped_sign_sqrt; + + (was_nonzero_square, r) + } + + /// Returns the square of this field element. + pub fn square(&self) -> FieldElement51 { + let mut output = *self; + fiat_25519_carry_square(&mut output.0, &self.0); + output + } + + /// Returns 2 times the square of this field element. + pub fn square2(&self) -> FieldElement51 { + let mut output = *self; + let mut temp = *self; + // Void vs return type, measure cost of copying self + fiat_25519_carry_square(&mut temp.0, &self.0); + fiat_25519_add(&mut output.0, &temp.0, &temp.0); + let input = output.0; + fiat_25519_carry(&mut output.0, &input); + output + } +} + +/// Precomputed value of one of the square roots of -1 (mod p) +pub(crate) const SQRT_M1: FieldElement51 = FieldElement51([ + 1718705420411056, + 234908883556509, + 2233514472574048, + 2117202627021982, + 765476049583133, +]); diff --git a/src/elligator/mod.rs b/src/elligator/mod.rs new file mode 100644 index 0000000..2df11f7 --- /dev/null +++ b/src/elligator/mod.rs @@ -0,0 +1,176 @@ +// 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. +#![allow(non_snake_case)] + +mod field; + +use curve25519_dalek::{edwards::EdwardsPoint, montgomery::MontgomeryPoint}; +use field::FieldElement51; +use sha2::Digest; +use subtle::{ConditionallyNegatable, ConditionallySelectable}; + +const MONT_A: FieldElement51 = FieldElement51([486662, 0, 0, 0, 0]); + +fn elligator_signal(r_0: &FieldElement51) -> MontgomeryPoint { + let minus_a = -&MONT_A; /* A = 486662 */ + let one = FieldElement51::one(); + let d_1 = &one + &r_0.square2(); /* 2r^2 */ + + let d = &minus_a * &(d_1.invert()); /* A/(1+2r^2) */ + + let d_sq = &d.square(); + let au = &MONT_A * &d; + + let inner = &(d_sq + &au) + &one; + let eps = &d * &inner; /* eps = d^3 + Ad^2 + d */ + + let (eps_is_sq, _eps) = FieldElement51::sqrt_ratio_i(&eps, &one); + + let zero = FieldElement51::zero(); + let Atemp = FieldElement51::conditional_select(&MONT_A, &zero, eps_is_sq); /* 0, or A if nonsquare*/ + let mut u = &d + &Atemp; /* d, or d+A if nonsquare */ + u.conditional_negate(!eps_is_sq); /* d, or -d-A if nonsquare */ + + MontgomeryPoint(u.to_bytes()) +} + +pub fn hash_to_point(bytes: &[u8]) -> EdwardsPoint { + let mut hash = sha2::Sha512::new(); + hash.update(bytes); + let h = hash.finalize(); + let mut res = [0u8; 32]; + res.copy_from_slice(&h[..32]); + + let sign_bit = (res[31] & 0x80) >> 7; + + let fe = FieldElement51::from_bytes(&res); + + let M1 = elligator_signal(&fe); + let E1_opt = M1.to_edwards(sign_bit); + + E1_opt + .expect("Montgomery conversion to Edwards point in Elligator failed") + .mul_by_cofactor() +} + +#[cfg(test)] +mod tests { + use super::*; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Signal tests from // + // https://github.com/signalapp/libsignal-protocol-c/blob/master/src/curve25519/ed25519/tests/internal_fast_tests.c#L222-L282 // + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + const ELLIGATOR_CORRECT_OUTPUT: [u8; 32] = [ + 0x5f, 0x35, 0x20, 0x00, 0x1c, 0x6c, 0x99, 0x36, 0xa3, 0x12, 0x06, 0xaf, 0xe7, 0xc7, 0xac, + 0x22, 0x4e, 0x88, 0x61, 0x61, 0x9b, 0xf9, 0x88, 0x72, 0x44, 0x49, 0x15, 0x89, 0x9d, 0x95, + 0xf4, 0x6e, + ]; + + #[test] + fn elligator_correct() { + let bytes: Vec = (0u8..32u8).collect(); + let mut bits_in = [0u8; 32]; + bits_in.copy_from_slice(&bytes); + let fe = FieldElement51::from_bytes(&bits_in); + let eg = elligator_signal(&fe); + assert_eq!(eg.to_bytes(), ELLIGATOR_CORRECT_OUTPUT); + } + + #[test] + fn elligator_zero_zero() { + let zero = [0u8; 32]; + let fe = FieldElement51::from_bytes(&zero); + let eg = elligator_signal(&fe); + assert_eq!(eg.to_bytes(), zero); + } + + const HASHTOPOINT_CORRECT_OUTPUT1: [u8; 32] = [ + 0xce, 0x89, 0x9f, 0xb2, 0x8f, 0xf7, 0x20, 0x91, 0x5e, 0x14, 0xf5, 0xb7, 0x99, 0x08, 0xab, + 0x17, 0xaa, 0x2e, 0xe2, 0x45, 0xb4, 0xfc, 0x2b, 0xf6, 0x06, 0x36, 0x29, 0x40, 0xed, 0x7d, + 0xe7, 0xed, + ]; + + const HASHTOPOINT_CORRECT_OUTPUT2: [u8; 32] = [ + 0xa0, 0x35, 0xbb, 0xa9, 0x4d, 0x30, 0x55, 0x33, 0x0d, 0xce, 0xc2, 0x7f, 0x83, 0xde, 0x79, + 0xd0, 0x89, 0x67, 0x72, 0x4c, 0x07, 0x8d, 0x68, 0x9d, 0x61, 0x52, 0x1d, 0xf9, 0x2c, 0x5c, + 0xba, 0x77, + ]; + + #[test] + fn test_hash_to_point_1() { + let bits: Vec = (0u8..32u8).collect(); + let hashed = hash_to_point(&bits); + assert_eq!(hashed.compress().to_bytes(), HASHTOPOINT_CORRECT_OUTPUT1); + } + + #[test] + fn test_hash_to_point_2() { + let bits: Vec = (0u8..32u8).map(|u| u + 1).collect(); + let hashed = hash_to_point(&bits); + assert_eq!(hashed.compress().to_bytes(), HASHTOPOINT_CORRECT_OUTPUT2); + } + + ///////////////////////////////////////// + // Additional test vectors from Signal // + ///////////////////////////////////////// + + fn test_vectors() -> Vec> { + vec![ + vec![ + "214f306e1576f5a7577636fe303ca2c625b533319f52442b22a9fa3b7ede809f", + "c95becf0f93595174633b9d4d6bbbeb88e16fa257176f877ce426e1424626052", + ], + vec![ + "2eb10d432702ea7f79207da95d206f82d5a3b374f5f89f17a199531f78d3bea6", + "d8f8b508edffbb8b6dab0f602f86a9dd759f800fe18f782fdcac47c234883e7f", + ], + vec![ + "84cbe9accdd32b46f4a8ef51c85fd39d028711f77fb00e204a613fc235fd68b9", + "93c73e0289afd1d1fc9e4e78a505d5d1b2642fbdf91a1eff7d281930654b1453", + ], + vec![ + "c85165952490dc1839cb69012a3d9f2cc4b02343613263ab93a26dc89fd58267", + "43cbe8685fd3c90665b91835debb89ff1477f906f5170f38a192f6a199556537", + ], + vec![ + "26e7fc4a78d863b1a4ccb2ce0951fbcd021e106350730ee4157bacb4502e1b76", + "b6fc3d738c2c40719479b2f23818180cdafa72a14254d4016bbed8f0b788a835", + ], + vec![ + "1618c08ef0233f94f0f163f9435ec7457cd7a8cd4bb6b160315d15818c30f7a2", + "da0b703593b29dbcd28ebd6e7baea17b6f61971f3641cae774f6a5137a12294c", + ], + vec![ + "48b73039db6fcdcb6030c4a38e8be80b6390d8ae46890e77e623f87254ef149c", + "ca11b25acbc80566603eabeb9364ebd50e0306424c61049e1ce9385d9f349966", + ], + vec![ + "a744d582b3a34d14d311b7629da06d003045ae77cebceeb4e0e72734d63bd07d", + "fad25a5ea15d4541258af8785acaf697a886c1b872c793790e60a6837b1adbc0", + ], + vec![ + "80a6ff33494c471c5eff7efb9febfbcf30a946fe6535b3451cda79f2154a7095", + "57ac03913309b3f8cd3c3d4c49d878bb21f4d97dc74a1eaccbe5c601f7f06f47", + ], + vec![ + "f06fc939bc10551a0fd415aebf107ef0b9c4ee1ef9a164157bdd089127782617", + "785b2a6a00a5579cc9da1ff997ce8339b6f9fb46c6f10cf7a12ff2986341a6e0", + ], + ] + } + + #[test] + fn additional_signal_test_vectors() { + for vector in test_vectors().iter() { + let input = hex::decode(vector[0]).unwrap(); + let output = hex::decode(vector[1]).unwrap(); + + let point = hash_to_point(&input); + assert_eq!(point.compress().to_bytes(), output[..]); + } + } +} diff --git a/src/group.rs b/src/group.rs index d9c7f70..88fba28 100644 --- a/src/group.rs +++ b/src/group.rs @@ -6,6 +6,7 @@ //! Defines the Group trait to specify the underlying prime order group used in //! OPAQUE's OPRF +use crate::elligator; use crate::errors::InternalPakeError; use curve25519_dalek::{ @@ -13,13 +14,11 @@ use curve25519_dalek::{ ristretto::{CompressedRistretto, RistrettoPoint}, scalar::Scalar, }; -use digest::Digest; use generic_array::{ typenum::{U32, U64}, ArrayLength, GenericArray, }; use rand_core::{CryptoRng, RngCore}; -use sha2::Sha256; use std::ops::Mul; use zeroize::Zeroize; @@ -148,25 +147,7 @@ impl Group for EdwardsPoint { type UniformBytesLen = U32; fn hash_to_curve(uniform_bytes: &GenericArray) -> Self { - const HASH_SIZE: usize = 32; - let mut result = [0u8; HASH_SIZE]; - let mut counter = 0; - let mut wrapped_point: Option = None; - - while wrapped_point.is_none() { - result.copy_from_slice( - &Sha256::new() - .chain(&uniform_bytes[..HASH_SIZE]) - .chain(&[counter]) - .finalize()[..HASH_SIZE], - ); - wrapped_point = CompressedEdwardsY::from_slice(&result).decompress(); - counter += 1; - } - - wrapped_point - .expect("guarded by loop exit condition") - .mul_by_cofactor() + elligator::hash_to_point(uniform_bytes) } } diff --git a/src/lib.rs b/src/lib.rs index 9bd0657..1560775 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -413,7 +413,9 @@ pub mod ciphersuite; mod envelope; mod hash; +mod elligator; pub mod group; + pub mod map_to_curve; pub mod key_exchange; diff --git a/src/tests/opaque_ke_test.rs b/src/tests/opaque_ke_test.rs index bb92250..dc984a8 100644 --- a/src/tests/opaque_ke_test.rs +++ b/src/tests/opaque_ke_test.rs @@ -80,18 +80,18 @@ static TEST_VECTOR: &str = r#" "envelope_nonce": "b0076712e01fecdb12301d5d7da92236e47f20494e68defb32084f1ab6c3d4f8", "client_nonce": "b9f09e9b0606fa88c4194011d5c204861b73c43cbf1ea0d08c03ec2fd6d05572", "server_nonce": "a213c02274e7f20fc3b571d25e98854c5dae2cfde6c9bf228a66bf3eff3e2a97", - "r1": "7b7734033104b0b2726a0fc945e39d764b9d34a2658b4964e9e4227e9844bcb1", - "r2": "270c46234717792b166a11c8d215542b685925543a8f326bcdf79e2b26c42001", - "r3": "b0076712e01fecdb12301d5d7da92236e47f20494e68defb32084f1ab6c3d4f876b6e60dd2e246d54c3b85c80adb378f7fd5490b5efcb5be372f2dcc889378d0b8024cad3160a8c6a2d332fc2efe94fcb0b8def46bf4fdf2036167b4e5414e6eb2341df425f90244c72d8e19b249ca0d6d1a3a3dfe6ee1773e1b782a81efef29", - "l1": "7b7734033104b0b2726a0fc945e39d764b9d34a2658b4964e9e4227e9844bcb1b9f09e9b0606fa88c4194011d5c204861b73c43cbf1ea0d08c03ec2fd6d0557297cb1eb93a69542597517b110ccca457d5ce8d8bfcbfb2a9258bb7b4bd7f716e", - "l2": "270c46234717792b166a11c8d215542b685925543a8f326bcdf79e2b26c42001b0076712e01fecdb12301d5d7da92236e47f20494e68defb32084f1ab6c3d4f876b6e60dd2e246d54c3b85c80adb378f7fd5490b5efcb5be372f2dcc889378d0b8024cad3160a8c6a2d332fc2efe94fcb0b8def46bf4fdf2036167b4e5414e6ea0e59a07908fc793c590fd83343003a54330e24af908ed31c921e6e6504c3248f73d27d7ca78ded52209bc3bae000f9d95b147360edac1e97c148a3a7396a27955afc9bdd9729801043518f40e8b71e1a45468b13d8e80d71e38be36da7116f1", - "l3": "bb4ade16958bae3818d9d91fb72e9c6eba16dd4cbf51eed5eb3c09bfba200a40", + "r1": "7e2c67a156ab27490f20008fcae9e9f722d8a9f4eeac373a711259981ca05dd5", + "r2": "710fdd19883e869e784c84f2864fa0bfc227662404b77cc8a54d79ae7fb931ea", + "r3": "b0076712e01fecdb12301d5d7da92236e47f20494e68defb32084f1ab6c3d4f8923b1d26cac4e3d91cec445b3322f4cc69a727f184353cb4dfe6d55a4c7d2bb4b77fbd41eacb8434f102c8c29cd4831e708046d38615df566675421ae8eb4933b2341df425f90244c72d8e19b249ca0d6d1a3a3dfe6ee1773e1b782a81efef29", + "l1": "7e2c67a156ab27490f20008fcae9e9f722d8a9f4eeac373a711259981ca05dd5b9f09e9b0606fa88c4194011d5c204861b73c43cbf1ea0d08c03ec2fd6d0557297cb1eb93a69542597517b110ccca457d5ce8d8bfcbfb2a9258bb7b4bd7f716e", + "l2": "710fdd19883e869e784c84f2864fa0bfc227662404b77cc8a54d79ae7fb931eab0076712e01fecdb12301d5d7da92236e47f20494e68defb32084f1ab6c3d4f8923b1d26cac4e3d91cec445b3322f4cc69a727f184353cb4dfe6d55a4c7d2bb4b77fbd41eacb8434f102c8c29cd4831e708046d38615df566675421ae8eb4933a0e59a07908fc793c590fd83343003a54330e24af908ed31c921e6e6504c3248f73d27d7ca78ded52209bc3bae000f9d95b147360edac1e97c148a3a7396a27939ccf2a17a5b281068665b4865e6c6331533461a8e10a4ceffc4c6a6609c326a", + "l3": "127144e6469e001d56237a58c8c869a8173e042bf2ff19d8331441d36ada9c3f", "client_registration_state": "5a9a073b1a1efedebdb404bc073ae74b316920d68ab628bed0c500cae95d6e0270617373776f7264", - "client_login_state": "5a9a073b1a1efedebdb404bc073ae74b316920d68ab628bed0c500cae95d6e0280616968ed8daae02c02d3ba41a70104ed0deecd2276e058994d601a1351b359b9f09e9b0606fa88c4194011d5c204861b73c43cbf1ea0d08c03ec2fd6d055725132efb9cd93c58e53a5660b54470d3f30804e87caa28dcc2c4c8e8c1e2b827a70617373776f7264", + "client_login_state": "5a9a073b1a1efedebdb404bc073ae74b316920d68ab628bed0c500cae95d6e0280616968ed8daae02c02d3ba41a70104ed0deecd2276e058994d601a1351b359b9f09e9b0606fa88c4194011d5c204861b73c43cbf1ea0d08c03ec2fd6d05572f258311568d792d6ebecee225c0fde4512139e29a435e9f9a0b82dc3809a83ab70617373776f7264", "server_registration_state": "203fabe2af9c8dc668b81db1ece9c2412c94c276495f33202479886de1b12907", - "server_login_state": "ebc0953924d55ad66aa801a7c85f47f35889b90002451a04fb7134b8a2a5a33c1390cf9c145ac9df436527ec6d8e8d6c0160ebdd411802aace7eb6032589b3cb72b17f13bd41cbfbdfa8d74bc94ec1abcc77b9a3da8fbad918ca0a5f84a81443", - "password_file": "203fabe2af9c8dc668b81db1ece9c2412c94c276495f33202479886de1b12907b2341df425f90244c72d8e19b249ca0d6d1a3a3dfe6ee1773e1b782a81efef29b0076712e01fecdb12301d5d7da92236e47f20494e68defb32084f1ab6c3d4f876b6e60dd2e246d54c3b85c80adb378f7fd5490b5efcb5be372f2dcc889378d0b8024cad3160a8c6a2d332fc2efe94fcb0b8def46bf4fdf2036167b4e5414e6e", - "export_key": "69691c8a3fb5b78e5430cb1b42fd8262b444291d361e43535a0d2a49550b67a4", + "server_login_state": "ebc0953924d55ad66aa801a7c85f47f35889b90002451a04fb7134b8a2a5a33cd69098c0a81ce06f58cbe4fd6ba23c9c1404ad6f639ba64d5f0f7bf0a041fc5872b17f13bd41cbfbdfa8d74bc94ec1abcc77b9a3da8fbad918ca0a5f84a81443", + "password_file": "203fabe2af9c8dc668b81db1ece9c2412c94c276495f33202479886de1b12907b2341df425f90244c72d8e19b249ca0d6d1a3a3dfe6ee1773e1b782a81efef29b0076712e01fecdb12301d5d7da92236e47f20494e68defb32084f1ab6c3d4f8923b1d26cac4e3d91cec445b3322f4cc69a727f184353cb4dfe6d55a4c7d2bb4b77fbd41eacb8434f102c8c29cd4831e708046d38615df566675421ae8eb4933", + "export_key": "da3a52148a58168c9f804df5e216e3d3f16e935d4d70a5eb249433d88e02ae4c", "shared_secret": "72b17f13bd41cbfbdfa8d74bc94ec1abcc77b9a3da8fbad918ca0a5f84a81443" } "#;