use crate::helper::FIELD_SIZE;
use tiny_ec::{
curve::{Affine, Field, Jacobian, Scalar},
ECMULT_GEN_CONTEXT,
};
use tiny_keccak::{Hasher, Keccak};
pub fn new_candidate_point(b: &[u8]) -> Affine {
let mut x = field_hash(b);
let (mut y, _) = y_squared(&x).sqrt();
x.normalize();
y.normalize();
if y.is_odd() {
y = y.neg(1);
y.normalize();
}
Affine::new(x, y)
}
pub fn y_squared(x: &Field) -> Field {
let mut t = *x;
t = t * t * t + Field::from_int(7);
t.normalize();
t
}
pub fn is_on_curve(point: &Affine) -> bool {
y_squared(&point.x) == point.y * point.y
}
pub fn hash_to_curve_prefix(alpha: &Scalar, pk: &Affine) -> Affine {
let mut tpk = *pk;
tpk.x.normalize();
tpk.y.normalize();
let packed = [
Field::from_int(1).b32().to_vec(),
tpk.x.b32().to_vec(),
tpk.y.b32().to_vec(),
alpha.b32().to_vec(),
]
.concat();
let mut rv = new_candidate_point(&packed);
while !is_on_curve(&rv) {
rv = new_candidate_point(rv.x.b32().as_ref());
}
rv
}
pub fn field_hash(b: &[u8]) -> Field {
let mut output = [0u8; 32];
let mut hasher = Keccak::v256();
hasher.update(b);
hasher.finalize(&mut output);
let mut s = Scalar::from(&output);
if s >= FIELD_SIZE {
let mut hasher = Keccak::v256();
hasher.update(&output);
hasher.finalize(&mut output);
assert!(bool::from(s.set_b32(&output)), "Unable to set field");
}
let mut f = Field::default();
if !f.set_b32(&s.b32()) {
f.normalize();
}
f
}
pub fn hash_points(
g: &Affine,
h: &Affine,
pk: &Affine,
gamma: &Affine,
kg: &Affine,
kh: &Affine,
) -> Scalar {
let mut output = [0u8; 32];
let mut hasher = Keccak::v256();
let all_points = [g, h, pk, gamma, kg, kh];
for point in all_points {
hasher.update(point.x.b32().as_ref());
hasher.update(point.y.b32().as_ref());
}
hasher.finalize(&mut output);
Scalar::from(&output)
}
pub fn hash_points_prefix(
hash: &Affine,
pk: &Affine,
gamma: &Affine,
u_witness: &[u8; 20],
v: &Affine,
) -> Scalar {
let mut output = [0u8; 32];
let mut hasher = Keccak::v256();
let all_points = [hash, pk, gamma, v];
hasher.update(Scalar::from_int(2).b32().as_ref());
for point in all_points {
hasher.update(point.x.b32().as_ref());
hasher.update(point.y.b32().as_ref());
}
hasher.update(u_witness);
hasher.finalize(&mut output);
Scalar::from(&output)
}
pub fn hash_to_curve(alpha: &Scalar, y: Option<&Affine>) -> Affine {
let mut r = Jacobian::default();
ECMULT_GEN_CONTEXT.ecmult_gen(&mut r, alpha);
match y {
Some(v) => {
r = r.add_ge(v);
r
}
None => r,
};
Affine::from(&r)
}
#[cfg(test)]
mod tests {
use crate::{
hash::{is_on_curve, new_candidate_point},
helper::random_bytes,
};
use tiny_ec::curve::Scalar;
#[test]
fn point_must_be_on_curve() {
let mut buf = [0u8; 32];
random_bytes(&mut buf);
let mut rv = new_candidate_point(buf.as_ref());
while !is_on_curve(&rv) {
rv = new_candidate_point(&rv.x.b32());
}
assert!(is_on_curve(&rv));
}
#[test]
fn test_scalar_is_gte() {
let data_set = [
Scalar([0, 1, 1, 1, 1, 1, 1, 0]),
Scalar([1, 0, 0, 0, 0, 0, 0, 1]),
Scalar([0, 1, 1, 1, 1, 1, 0, 1]),
Scalar([1, 0, 0, 0, 0, 0, 1, 0]),
Scalar([0, 1, 1, 1, 1, 1, 0, 1]),
Scalar([0, 1, 1, 1, 1, 1, 0, 1]),
];
let require_output = [
true, false, false, true, false, false, true, true, false, true, false, false, true,
true, true, true, true, true, false, false, false, true, false, false, true, true,
true, true, true, true, true, true, true, true, true, true,
];
for x in 0..data_set.len() {
for y in 0..data_set.len() {
assert!(
(data_set[x] >= data_set[y]) == require_output[x * data_set.len() + y],
"scalar_is_gte() is broken"
);
}
}
}
#[test]
fn test_scalar_is_gt() {
let data_set = [
Scalar([0, 1, 1, 1, 1, 1, 0, 1]),
Scalar([0, 1, 1, 1, 1, 1, 0, 1]),
Scalar([1, 1, 1, 1, 1, 1, 1, 1]),
Scalar([0, 0, 0, 0, 0, 0, 0, 0]),
Scalar([1, 1, 1, 1, 1, 1, 1, 1]),
Scalar([0, 0, 0, 0, 0, 0, 0, 2]),
Scalar([1, 1, 1, 1, 1, 1, 1, 1]),
Scalar([0, 0, 0, 0, 0, 1, 1, 1]),
Scalar([0, 1, 1, 1, 1, 1, 1, 1]),
Scalar([1, 1, 1, 1, 1, 1, 1, 1]),
];
let require_output = [
false, false, false, true, false, false, false, false, false, false, false, false,
false, true, false, false, false, false, false, false, true, true, false, true, false,
false, false, true, true, false, false, false, false, false, false, false, false,
false, false, false, true, true, false, true, false, false, false, true, true, false,
true, true, true, true, true, false, true, true, true, true, true, true, false, true,
false, false, false, true, true, false, true, true, false, true, false, false, false,
false, false, false, true, true, false, true, false, false, false, true, false, false,
true, true, false, true, false, false, false, true, true, false,
];
for x in 0..data_set.len() {
for y in 0..data_set.len() {
assert!(
data_set[x].gt(&data_set[y]) == require_output[x * data_set.len() + y],
"scalar_is_gt() is broken"
);
}
}
}
}