use super::Point;
use super::consts::*;
use super::field::{Fe, fe_div_pow_m1, fe_to_bytes_25_5};
use purecrypto::hash::{Digest, Keccak256};
fn load3(b: &[u8]) -> i64 {
(b[0] as i64) | ((b[1] as i64) << 8) | ((b[2] as i64) << 16)
}
fn load4(b: &[u8]) -> i64 {
(b[0] as i64) | ((b[1] as i64) << 8) | ((b[2] as i64) << 16) | ((b[3] as i64) << 24)
}
pub fn ge_from_fe_from_bytes_vartime(s: &[u8]) -> Point {
assert_eq!(s.len(), 32, "ge_fromfe_frombytes_vartime expects 32 bytes");
let mut h0 = load4(&s[0..4]);
let mut h1 = load3(&s[4..7]) << 6;
let mut h2 = load3(&s[7..10]) << 5;
let mut h3 = load3(&s[10..13]) << 3;
let mut h4 = load3(&s[13..16]) << 2;
let mut h5 = load4(&s[16..20]);
let mut h6 = load3(&s[20..23]) << 7;
let mut h7 = load3(&s[23..26]) << 5;
let mut h8 = load3(&s[26..29]) << 4;
let mut h9 = load3(&s[29..32]) << 2;
let carry9 = (h9 + (1 << 24)) >> 25;
h0 += carry9 * 19;
h9 -= carry9 << 25;
let carry1 = (h1 + (1 << 24)) >> 25;
h2 += carry1;
h1 -= carry1 << 25;
let carry3 = (h3 + (1 << 24)) >> 25;
h4 += carry3;
h3 -= carry3 << 25;
let carry5 = (h5 + (1 << 24)) >> 25;
h6 += carry5;
h5 -= carry5 << 25;
let carry7 = (h7 + (1 << 24)) >> 25;
h8 += carry7;
h7 -= carry7 << 25;
let carry0 = (h0 + (1 << 25)) >> 26;
h1 += carry0;
h0 -= carry0 << 26;
let carry2 = (h2 + (1 << 25)) >> 26;
h3 += carry2;
h2 -= carry2 << 26;
let carry4 = (h4 + (1 << 25)) >> 26;
h5 += carry4;
h4 -= carry4 << 26;
let carry6 = (h6 + (1 << 25)) >> 26;
h7 += carry6;
h6 -= carry6 << 26;
let carry8 = (h8 + (1 << 25)) >> 26;
h9 += carry8;
h8 -= carry8 << 26;
let u = Fe::from_bytes(&fe_to_bytes_25_5([h0, h1, h2, h3, h4, h5, h6, h7, h8, h9]));
let v = u.square().mul_small(2);
let w = v.add(&Fe::ONE);
let mut x = w.square().add(&FE_MA2.mul(&v));
let mut r_x = fe_div_pow_m1(&w, &x);
let y = r_x.square();
x = y.mul(&x);
let y = w.sub(&x);
let mut z = *FE_MA;
let sign;
if !y.is_zero() {
let y = w.add(&x);
if !y.is_zero() {
x = x.mul(&FE_SQRT_M1);
let y = w.sub(&x);
if !y.is_zero() {
let y = w.add(&x);
assert!(
y.is_zero(),
"assertion failed in ge_fromfe_frombytes_vartime"
);
r_x = r_x.mul(&FE_FFFB3);
} else {
r_x = r_x.mul(&FE_FFFB4);
}
sign = 1;
return finish(r_x, z, w, sign);
}
r_x = r_x.mul(&FE_FFFB1);
} else {
r_x = r_x.mul(&FE_FFFB2);
}
r_x = r_x.mul(&u);
z = z.mul(&v);
sign = 0;
finish(r_x, z, w, sign)
}
fn finish(mut r_x: Fe, z: Fe, w: Fe, sign: u8) -> Point {
if r_x.is_negative() != (sign == 1) {
assert!(!r_x.is_zero(), "unexpected zero field element in setsign");
r_x = r_x.neg();
}
let r_z = z.add(&w);
let r_y = z.sub(&w);
let r_x = r_x.mul(&r_z);
let zinv = r_z.invert();
let ax = r_x.mul(&zinv);
let ay = r_y.mul(&zinv);
let mut enc = ay.to_bytes();
enc[31] |= (ax.to_bytes()[0] & 1) << 7;
Point::decompress(&enc).expect("ge_fromfe_frombytes_vartime produced an off-curve point")
}
pub fn hash_to_point(h: &[u8]) -> Point {
ge_from_fe_from_bytes_vartime(h)
}
pub fn hash_to_ec(data: &[u8]) -> Point {
let h = Keccak256::digest(data);
ge_from_fe_from_bytes_vartime(&h).mul_by_cofactor()
}
pub fn hp(data: &[u8]) -> Point {
hash_to_ec(data)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hp_is_in_the_prime_order_subgroup() {
for i in 0u8..8 {
let p = hp(&[i; 32]);
assert!(bool::from(p.is_torsion_free()));
assert_ne!(p, Point::identity());
}
}
#[test]
fn hp_is_deterministic() {
assert_eq!(hp(b"zano"), hp(b"zano"));
assert_ne!(hp(b"zano"), hp(b"zanp"));
}
}