hash2curve 0.0.6

A suite of hash to curve ECC algorithms
Documentation
//! Implements hash to curve as described in Section 8.7.1 of
//! <https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/?include_text=1>
//! and Section 5 of
//!  <https://eprint.iacr.org/2019/403.pdf>

use crate::constants::bls381g1::*;
use crate::error::HashingError;
use crate::isogeny::bls381g1::*;
use crate::{expand_message_xmd, expand_message_xof, DomainSeparationTag};
use crate::{HashToCurveXmd, HashToCurveXof};
use amcl::arch::Chunk;
use amcl::bls381::{big::BIG, dbig::DBIG, ecp::ECP, rom};
use digest::generic_array::GenericArray;
use digest::{
    generic_array::typenum::{marker_traits::Unsigned, U128, U32, U48, U64, U96},
    BlockInput, Digest, ExtendableOutput, Input, Reset,
};
use failure::_core::fmt::Debug;
use std::{
    cmp::Ordering,
    fmt::{Display, Formatter, Result as FmtResult},
    str::FromStr,
};
use subtle::ConstantTimeEq;

/// To compute a `L` use the following formula
/// L = ceil(ceil(log2(p) + k) / 8). For example, in our case log2(p) = 381, k = 128
/// L = 64
type L = U64;
type TwoL = U128;

/// BLS12381G1_XMD:SHA-256_SSWU provides both
/// Random Oracle (RO)
/// Nonuniform (NU)
pub struct Bls12381G1Sswu {
    dst: DomainSeparationTag,
}

impl Bls12381G1Sswu {
    /// Create a new implementation with the default
    pub fn new(dst: DomainSeparationTag) -> Self {
        Self { dst }
    }
}

impl From<DomainSeparationTag> for Bls12381G1Sswu {
    fn from(dst: DomainSeparationTag) -> Self {
        Self { dst }
    }
}

impl HashToCurveXmd for Bls12381G1Sswu {
    type Output = G1;

    fn encode_to_curve_xmd<D: BlockInput + Digest<OutputSize = U32>>(
        &self,
        data: &[u8],
    ) -> Result<Self::Output, HashingError> {
        let u = hash_to_field_xmd_nu::<D>(data, &self.dst)?;
        Ok(encode_to_curve(u).into())
    }

    fn hash_to_curve_xmd<D: BlockInput + Digest<OutputSize = U32>>(
        &self,
        data: &[u8],
    ) -> Result<Self::Output, HashingError> {
        let (u0, u1) = hash_to_field_xmd_ro::<D>(data, &self.dst)?;
        Ok(hash_to_curve(u0, u1).into())
    }
}

impl HashToCurveXof for Bls12381G1Sswu {
    type Output = G1;

    fn encode_to_curve_xof<X: ExtendableOutput + Input + Reset + Default>(
        &self,
        data: &[u8],
    ) -> Result<Self::Output, HashingError> {
        let u = hash_to_field_xof_nu::<X>(data, &self.dst)?;
        Ok(encode_to_curve(u).into())
    }

    fn hash_to_curve_xof<X: ExtendableOutput + Input + Reset + Default>(
        &self,
        data: &[u8],
    ) -> Result<Self::Output, HashingError> {
        let (u0, u1) = hash_to_field_xof_ro::<X>(data, &self.dst)?;
        Ok(hash_to_curve(u0, u1).into())
    }
}

/// Represents a point on G1
#[derive(Copy, Clone)]
pub struct G1(pub ECP);

impl G1 {
    /// The bytes in G1 compressed form
    pub const COMPRESSED_BYTES: usize = rom::MODBYTES;
    /// The bytes in G1 uncompressed form
    pub const UNCOMPRESSED_BYTES: usize = 2 * rom::MODBYTES;
    const ECP_COMPRESSED: usize = Self::COMPRESSED_BYTES + 1;
    const ECP_UNCOMPRESSED: usize = Self::UNCOMPRESSED_BYTES + 1;
    const COMPRESSED_HEX_LENGTH: usize = Self::UNCOMPRESSED_BYTES;
    const UNCOMPRESSED_HEX_LENGTH: usize = Self::UNCOMPRESSED_BYTES * 2;

    /// Serialize the point to compressed bytes in big endian form
    /// Only the x-coordinate
    ///
    /// NOTE: Must use `GenericArray` due to rust error
    /// error[E0277]: arrays only have std trait implementations for lengths 0..=32
    /// The caller can use section 4.3 in https://tools.ietf.org/id/draft-jivsov-ecc-compact-05.html
    /// to reconstruct Y if needed
    pub fn to_bytes(&self) -> [u8; Self::COMPRESSED_BYTES] {
        let mut bytes = [0u8; Self::ECP_COMPRESSED];
        let mut temp = ECP::new();
        temp.copy(&self.0);
        temp.tobytes(bytes.as_mut(), true);
        // GenericArray::clone_from_slice(&bytes[1..])
        let mut output = [0u8; Self::COMPRESSED_BYTES];
        output.copy_from_slice(&bytes[1..]);
        // Store the value of y as the MSB
        output[0] |= (bytes[0] & 1) << 7;
        output
    }

    /// Serialize the point to uncompressed bytes in big endian form
    /// The x-coordinate followed by the y-coordinate
    /// NOTE: Must use `GenericArray` due to rust error
    /// error[E0277]: arrays only have std trait implementations for lengths 0..=32
    pub fn to_bytes_uncompressed(&self) -> [u8; Self::UNCOMPRESSED_BYTES] {
        let mut bytes = [0u8; Self::ECP_UNCOMPRESSED];
        let mut temp = ECP::new();
        temp.copy(&self.0);
        temp.tobytes(bytes.as_mut(), false);
        // GenericArray::clone_from_slice(&bytes[1..])
        let mut output = [0u8; Self::UNCOMPRESSED_BYTES];
        output.copy_from_slice(&bytes[1..]);
        output
    }

    /// Serialize the point to compressed lower hex string
    /// Only the x-coordinate
    pub fn encode_to_hex(&self) -> String {
        String::from_utf8(subtle_encoding::hex::encode(&self.to_bytes()[..])).unwrap()
    }

    /// Serialize the point to uncompressed lower hex string
    /// The x-coordinate followed by the y-coordinate
    pub fn encode_to_hex_uncompressed(&self) -> String {
        String::from_utf8(subtle_encoding::hex::encode(
            &self.to_bytes_uncompressed()[..],
        ))
        .unwrap()
    }

    /// Convenience method when x and y are supplied separately
    pub fn decode_from_hex_points(x: &str, y: &str) -> Result<Self, String> {
        let mut s = x.to_string();
        s.push_str(y);
        Self::from_str(s.as_str())
    }

    /// Convenience method when x and y are supplied separately
    pub fn from_byte_points<B: AsRef<[u8]>>(x: B, y: B) -> Result<G1, String> {
        let a = x.as_ref();
        if a.len() != Self::COMPRESSED_BYTES {
            return Err(format!(
                "Invalid number of bytes for x. Expected '{}', supplied '{}'",
                Self::COMPRESSED_BYTES,
                a.len()
            ));
        }
        let b = y.as_ref();
        if b.len() != Self::COMPRESSED_BYTES {
            return Err(format!(
                "Invalid number of bytes for y. Expected '{}', supplied '{}'",
                Self::COMPRESSED_BYTES,
                b.len()
            ));
        }

        let x = BIG::frombytes(&a);
        let y = BIG::frombytes(&b);
        Ok(Self(ECP::new_bigs(&x, &y)))
    }

    /// Helper function for `Display` and `Debug`
    fn format(&self, f: &mut Formatter<'_>) -> FmtResult {
        let mut x = self.0.getx();
        let mut y = self.0.gety();
        write!(f, "G1 {{ x: {}, y: {} }}", x.to_hex(), y.to_hex())
    }
}

impl PartialEq<ECP> for G1 {
    fn eq(&self, other: &ECP) -> bool {
        self.0.eq(other)
    }
}

impl PartialEq for G1 {
    fn eq(&self, other: &G1) -> bool {
        self.0.eq(&other.0)
    }
}

impl PartialEq<[u8; G1::COMPRESSED_BYTES]> for G1 {
    fn eq(&self, other: &[u8; G1::COMPRESSED_BYTES]) -> bool {
        self.to_bytes().ct_eq(other).unwrap_u8() == 1
    }
}

impl PartialEq<[u8; G1::UNCOMPRESSED_BYTES]> for G1 {
    fn eq(&self, other: &[u8; G1::UNCOMPRESSED_BYTES]) -> bool {
        self.to_bytes_uncompressed().ct_eq(other).unwrap_u8() == 1
    }
}

impl PartialEq<GenericArray<u8, U48>> for G1 {
    fn eq(&self, other: &GenericArray<u8, U48>) -> bool {
        self.eq(array_ref![other, 0, G1::COMPRESSED_BYTES])
    }
}

impl PartialEq<Vec<u8>> for G1 {
    fn eq(&self, other: &Vec<u8>) -> bool {
        self.eq(other.as_slice())
    }
}

impl PartialEq<[u8]> for G1 {
    fn eq(&self, other: &[u8]) -> bool {
        match other.len() {
            G1::COMPRESSED_BYTES => self.eq(array_ref![other, 0, G1::COMPRESSED_BYTES]),
            G1::UNCOMPRESSED_BYTES => self.eq(array_ref![other, 0, G1::UNCOMPRESSED_BYTES]),
            _ => false,
        }
    }
}

impl From<ECP> for G1 {
    fn from(p: ECP) -> Self {
        Self(p)
    }
}

impl From<[u8; G1::COMPRESSED_BYTES]> for G1 {
    fn from(x: [u8; G1::COMPRESSED_BYTES]) -> Self {
        Self::from(&x)
    }
}

impl From<&[u8; G1::COMPRESSED_BYTES]> for G1 {
    fn from(x: &[u8; G1::COMPRESSED_BYTES]) -> Self {
        let parity = ((x[0] >> 7) & 1) as isize;
        let mut temp = x.clone();
        temp[0] = x[0] & 0x7F;
        let x = BIG::frombytes(&temp[..]);
        Self(ECP::new_bigint(&x, parity))
    }
}

impl From<[u8; G1::UNCOMPRESSED_BYTES]> for G1 {
    fn from(points: [u8; G1::UNCOMPRESSED_BYTES]) -> Self {
        Self::from(&points)
    }
}

impl From<&[u8; G1::UNCOMPRESSED_BYTES]> for G1 {
    fn from(points: &[u8; G1::UNCOMPRESSED_BYTES]) -> Self {
        let x = BIG::frombytes(&points[..Self::COMPRESSED_BYTES]);
        let y = BIG::frombytes(&points[Self::COMPRESSED_BYTES..]);
        Self(ECP::new_bigs(&x, &y))
    }
}

/// Deserialize the point from a compressed x-coordinate in big endian form
impl From<GenericArray<u8, U48>> for G1 {
    fn from(bytes: GenericArray<u8, U48>) -> Self {
        let t: &[u8; Self::COMPRESSED_BYTES] = array_ref![bytes, 0, G1::COMPRESSED_BYTES];
        Self::from(t)
    }
}

/// Deserialize the point from x and y coordinates in big endian form
impl From<GenericArray<u8, U96>> for G1 {
    fn from(bytes: GenericArray<u8, U96>) -> Self {
        let t: &[u8; Self::UNCOMPRESSED_BYTES] = array_ref![bytes, 0, G1::UNCOMPRESSED_BYTES];
        Self::from(t)
    }
}

/// Deserialize from a hex string. If the hex string is `COMPRESSED_HEX_LENGTH`
/// It will assume compressed form––x-coordinate only.
///
/// If the hex string is `UNCOMPRESSED_HEX_LENGTH`, it assumes uncompressed form––
/// x and y coordinates
impl FromStr for G1 {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // This is best effort constant time execution.
        // Constant time is important as hex is used during serialization and deserialization.
        // A seemingly effortless solution is to filter string for errors and pad with 0s before
        // passing to AMCL but that would be expensive as the string is scanned twice
        let mut val = s.to_lowercase();
        // Given hex cannot be bigger than max byte size
        if val.len() > Self::UNCOMPRESSED_HEX_LENGTH {
            return Err(format!(
                "Expected length '{}', found '{}'",
                val.len(),
                Self::UNCOMPRESSED_HEX_LENGTH
            ));
        }

        // Pad the string for constant time parsing.
        if Self::COMPRESSED_HEX_LENGTH < val.len() && val.len() < Self::UNCOMPRESSED_HEX_LENGTH {
            while val.len() < Self::UNCOMPRESSED_HEX_LENGTH {
                val.insert(0, '0');
            }
        } else {
            while val.len() < Self::COMPRESSED_HEX_LENGTH {
                val.insert(0, '0');
            }
        }

        let mut bytes = match subtle_encoding::hex::decode(val) {
            Ok(b) => b,
            Err(e) => return Err(format!("{}", e)),
        };

        if bytes.len() > Self::COMPRESSED_BYTES {
            let x = BIG::frombytes(&bytes[..Self::COMPRESSED_BYTES]);
            let y = BIG::frombytes(&bytes[Self::COMPRESSED_BYTES..]);

            Ok(Self(ECP::new_bigs(&x, &y)))
        } else {
            let parity = ((bytes[0] >> 7) & 1) as isize;
            bytes[0] = bytes[0] & 0x7F;
            let x = BIG::frombytes(bytes.as_slice());
            Ok(Self(ECP::new_bigint(&x, parity)))
        }
    }
}

impl Display for G1 {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        self.format(f)
    }
}

impl Debug for G1 {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        self.format(f)
    }
}

fn encode_to_curve(u: BIG) -> ECP {
    let q = map_to_curve(u);
    clear_cofactor(q)
}

fn hash_to_curve(u0: BIG, u1: BIG) -> ECP {
    let mut q0 = map_to_curve(u0);
    let q1 = map_to_curve(u1);
    q0.add(&q1);
    clear_cofactor(q0)
}

/// See Section 7 in
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/?include_text=1>
fn clear_cofactor(p: ECP) -> ECP {
    p.mul(&H_EFF)
}

/// See Section 6.2 in
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/?include_text=1>
fn map_to_curve(u: BIG) -> ECP {
    let (x, y) = map_to_curve_simple_swu(u);
    iso_map(x, y)
}

/// See Section 6.6.2.1 in
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/?include_text=1>
///
/// Only works if p is congruent to 3 mod 4
fn map_to_curve_simple_swu(u: BIG) -> (BIG, BIG) {
    // let u = BIG::fromstring("0CCB6BDA9B602AB82AAE21C0291623E2F639648A6ADA1C76D8FFB664130FD18D98A2CC6160624148827A9726678E7CD4".to_string());
    // tv1 = Z * u^2
    let mut tv1 = BIG::modmul(&Z, &BIG::modsqr(&u, &MODULUS), &MODULUS);
    tv1.norm();
    // tv2 = tv1^2
    let mut tv2 = BIG::modsqr(&tv1, &MODULUS);
    tv2.norm();

    // x1 = tv1 + tv2
    let mut x1 = BIG::new_big(&tv1);
    x1.add(&tv2);
    x1.rmod(&MODULUS);
    x1.norm();

    // x1 = inv0(x1)
    x1.invmodp(&MODULUS);
    x1.norm();

    let e1 = if x1.iszilch() { 1 } else { 0 };

    // x1 = x1 + 1
    x1.inc(1);

    // x1 = CMOV(x1, c2, e1)
    x1.cmove(&C2, e1);

    // x1 = x1 * c1
    x1 = BIG::modmul(&x1, &C1, &MODULUS);
    x1.norm();

    // gx1 = x1^2
    let mut gx1 = BIG::modsqr(&x1, &MODULUS);
    // gx1 = gx1 + A
    gx1.add(&ISO_A);
    gx1.rmod(&MODULUS);
    gx1.norm();

    // gx1 = gx1 * x1
    gx1 = BIG::modmul(&gx1, &x1, &MODULUS);

    // gx1 = gx1 + B
    gx1.add(&ISO_B);
    gx1.rmod(&MODULUS);

    // x2 = tv1 * x1
    let mut x2 = BIG::modmul(&tv1, &x1, &MODULUS);
    x2.norm();

    // tv2 = tv1 * tv2
    tv2 = BIG::modmul(&tv1, &tv2, &MODULUS);

    // gx2 = gx1 * tv2
    let mut gx2 = BIG::modmul(&gx1, &tv2, &MODULUS);
    gx2.norm();

    // e2 = is_square(gx1)
    let e2 = if is_square(&gx1) { 1 } else { 0 };

    // x = CMOV(x2, x1, e2)
    let mut x = BIG::new_copy(&x2);
    x.cmove(&x1, e2);

    // y2 = CMOV(gx2, gx1, e2)
    let mut y2 = BIG::new_copy(&gx2);
    y2.cmove(&gx1, e2);

    // y = sqrt(y2)
    let y = sqrt_3mod4(&y2);

    // e3 = sgn0(u) == sgn0(y)
    let e3 = if sgn0(&u) == sgn0(&y) { 1 } else { 0 };

    // y = CMOV(-y, y, e3)
    let mut y_neg = BIG::modneg(&y, &MODULUS);
    y_neg.norm();
    y_neg.cmove(&y, e3);

    (x, y_neg)
}

/// Section F.1 in
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/?include_text=1>
fn sqrt_3mod4(x: &BIG) -> BIG {
    let mut t = BIG::new_big(x);
    t.powmod(&SQRT_C1, &MODULUS)
}

/// is_square(x) := { True,  if x^((q - 1) / 2) is 0 or 1 in F;
///                 { False, otherwise.
fn is_square(x: &BIG) -> bool {
    let mut t = BIG::new_copy(x);
    t = t.powmod(&PM1DIV2, &MODULUS);
    let mut sum = 0;
    for i in 1..t.w.len() {
        sum |= t.w[i];
    }
    sum == 0 && (t.w[0] == 0 || t.w[0] == 1)
}

/// See Section 4.1 in
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/?include_text=1>
fn sgn0(x: &BIG) -> Ordering {
    if *x > PM1DIV2 {
        Ordering::Less
    } else {
        Ordering::Greater
    }
}

/// See Section 4.3 in
/// <https://eprint.iacr.org/2019/403.pdf>
fn iso_map(x_prime: BIG, y_prime: BIG) -> ECP {
    let mut x_values: [BIG; 16] = [BIG::new(); 16];
    x_values[0] = BIG::new_int(1);
    x_values[1] = x_prime;
    x_values[2] = BIG::modsqr(&x_prime, &MODULUS);
    x_values[3] = BIG::modmul(&x_values[2], &x_prime, &MODULUS);
    x_values[4] = BIG::modmul(&x_values[3], &x_prime, &MODULUS);
    x_values[5] = BIG::modmul(&x_values[4], &x_prime, &MODULUS);
    x_values[6] = BIG::modmul(&x_values[5], &x_prime, &MODULUS);
    x_values[7] = BIG::modmul(&x_values[6], &x_prime, &MODULUS);
    x_values[8] = BIG::modmul(&x_values[7], &x_prime, &MODULUS);
    x_values[9] = BIG::modmul(&x_values[8], &x_prime, &MODULUS);
    x_values[10] = BIG::modmul(&x_values[9], &x_prime, &MODULUS);
    x_values[11] = BIG::modmul(&x_values[10], &x_prime, &MODULUS);
    x_values[12] = BIG::modmul(&x_values[11], &x_prime, &MODULUS);
    x_values[13] = BIG::modmul(&x_values[12], &x_prime, &MODULUS);
    x_values[14] = BIG::modmul(&x_values[13], &x_prime, &MODULUS);
    x_values[15] = BIG::modmul(&x_values[14], &x_prime, &MODULUS);

    let mut x = iso_map_helper(&x_values, &X_NUM);
    let mut x_den = iso_map_helper(&x_values, &X_DEN);
    let mut y = iso_map_helper(&x_values, &Y_NUM);
    let mut y_den = iso_map_helper(&x_values, &Y_DEN);

    x_den.invmodp(&MODULUS);
    x = BIG::modmul(&x, &x_den, &MODULUS);

    y_den.invmodp(&MODULUS);
    y = BIG::modmul(&y, &y_den, &MODULUS);
    y = BIG::modmul(&y, &y_prime, &MODULUS);

    ECP::new_bigs(&x, &y)
}

/// Compute a section of iso map
fn iso_map_helper(x: &[BIG], k: &[BIG]) -> BIG {
    let mut new_x = BIG::new();
    for i in 0..k.len() {
        let t = BIG::modmul(&x[i], &k[i], &MODULUS);
        new_x.add(&t);
        new_x.rmod(&MODULUS);
    }
    new_x
}

/// Hash to field using expand_message_xmd to compute `u` as specified in Section 5.2 in
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/?include_text=1>
fn hash_to_field_xmd_nu<D: BlockInput + Digest<OutputSize = U32>>(
    msg: &[u8],
    dst: &DomainSeparationTag,
) -> Result<BIG, HashingError> {
    // length_in_bytes = count * m * L = 1 * 1 * 64 = 64
    let random_bytes = expand_message_xmd::<D, L>(msg, dst)?;
    // elm_offset = L * (j + i * m) = 64 * (0 + 0 * 1) = 0
    // tv = substr(random_bytes, 0, 64)
    Ok(field_elem_from_larger_bytearray(random_bytes.as_slice()))
}

/// Hash to field using expand_message_xmd to compute two `u`s as specified in Section 5.2 in
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/?include_text=1>
///
/// We avoid the loop and get compile time checking this way
fn hash_to_field_xmd_ro<D: BlockInput + Digest<OutputSize = U32>>(
    msg: &[u8],
    dst: &DomainSeparationTag,
) -> Result<(BIG, BIG), HashingError> {
    // length_in_bytes = count * m * L = 2 * 1 * 64 = 128
    let random_bytes = expand_message_xmd::<D, TwoL>(msg, dst)?;
    // elm_offset_0 = L * (j + i * m) = 64 * (0 + 0 * 1) = 0
    // elm_offset_1 = L * (j + i * m) = 64 * (0 + 1 * 1) = 64
    // tv_0 = substr(random_bytes, 0, 64)
    // tv_1 = substr(random_bytes, 64, 64)
    let u_0 = field_elem_from_larger_bytearray(&random_bytes[0..L::to_usize()]);
    let u_1 = field_elem_from_larger_bytearray(&random_bytes[L::to_usize()..]);
    Ok((u_0, u_1))
}

/// Hash to field using expand_message_xof to compute `u` as specified in Section 5.2 in
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/?include_text=1>
fn hash_to_field_xof_nu<X: ExtendableOutput + Input + Reset + Default>(
    msg: &[u8],
    dst: &DomainSeparationTag,
) -> Result<BIG, HashingError> {
    // length_in_bytes = count * m * L = 1 * 1 * 64 = 64
    let random_bytes = expand_message_xof::<X, L>(msg, dst)?;
    // elm_offset = L * (j + i * m) = 64 * (0 + 0 * 1) = 0
    // tv = substr(random_bytes, 0, 64)
    Ok(field_elem_from_larger_bytearray(random_bytes.as_slice()))
}

/// Hash to field using expand_message_xof to compute two `u`s as specified in Section 5.2 in
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/?include_text=1>
///
/// We avoid the loop and get compile time checking this way
fn hash_to_field_xof_ro<X: ExtendableOutput + Input + Reset + Default>(
    msg: &[u8],
    dst: &DomainSeparationTag,
) -> Result<(BIG, BIG), HashingError> {
    // length_in_bytes = count * m * L = 2 * 1 * 64 = 128
    let random_bytes = expand_message_xof::<X, TwoL>(msg, dst)?;
    // elm_offset_0 = L * (j + i * m) = 64 * (0 + 0 * 1) = 0
    // elm_offset_1 = L * (j + i * m) = 64 * (0 + 1 * 1) = 64
    // tv_0 = substr(random_bytes, 0, 64)
    // tv_1 = substr(random_bytes, 64, 64)
    let u_0 = field_elem_from_larger_bytearray(&random_bytes[0..L::to_usize()]);
    let u_1 = field_elem_from_larger_bytearray(&random_bytes[L::to_usize()..]);
    Ok((u_0, u_1))
}

/// FIELD_ELEMENT_SIZE <= random_bytes.len() <= FIELD_ELEMENT_SIZE * 2
fn field_elem_from_larger_bytearray(random_bytes: &[u8]) -> BIG {
    // e_j = OS2IP(tv) mod p
    let mut d = DBIG::new();
    for i in random_bytes {
        d.shl(8);
        d.w[0] += *i as Chunk;
    }
    // u = (e_0, ..., e_( m - 1 ) )
    d.dmod(&MODULUS)
}

#[cfg(test)]
mod tests {
    use crate::bls381g1::{hash_to_field_xmd_nu, hash_to_field_xmd_ro, map_to_curve};
    use crate::DomainSeparationTag;
    use amcl::bls381::{big::BIG, ecp::ECP};

    #[test]
    fn map_to_curve_ro_tests() {
        let dst = DomainSeparationTag::new(
            b"BLS12381G1_XMD:SHA-256_SSWU_RO_",
            Some(b"TESTGEN"),
            None,
            None,
        )
        .unwrap();
        let msgs = [
            "",
            // "abc",
            // "abcdef0123456789",
            // "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        ];

        let expected_q = [
            ("02f2686965a4dd27ccb11119f2e131aefee818744a414d23ecef4db1407991fdf058f0affaee18fd586a9ab81060ae20",
             "0341a16c88a39b3d111b36b7cf885b7147b1d54b9201faaba5b47d7839bcf433cc35bb1f7b8e55aa9382a52fe4d84370",
             "1357bddd2bc6c8e752f3cf498ffe29ae87d8ff933701ae76f82d2839b0d9aee5229d4fff54dfb8223be0d88fa4485863",
             "09ba0ec3c78cf1e65330721f777b529aef27539642c39be11f459106b890ec5eb4a21c5d94885603e822cfa765170857"),

            ("119cc1d21e3e494d388a8718fe9f8ec6d8ff134486ce5c1f97129797616c4b8125f0dc568c59836cbf064496136438bc",
             "19e6c998825ee57b82c4808e4df477680f0f254c9edce228104422494a4e5d40d11ee676f6b861b6c49cf7de9d777aef",
             "0d1783f40bd83461b921c3fcd0e9ba326ef75272b122cf44338f0060d7179995a38ea9c66f3ce800e2f693d2634a4524",
             "017b2566d55fa7ee43844f1fa068cb0a11d5889c11607d939da046697c8ba25cf71054c2a8eb2189d3680485a39f5bdd"),

            ("1614d05720a39379fb89469883f90ae3e50995def9e17f8f8566a3f6cfb4fe88267eac1dc7834406fc597965065ef100",
             "1060e5aab331ac4940693a936ea80029bb2c4a3945add7ae35bce805e767af827c4a9ffcb5842fbc50ab234716d895f6",
             "0f612cda21cee750b1ccff361a4ce047e70d9a9e152e96a60aa29b5d8a5dcd25f7c5bd71bb56bd34e6a8af7532afaa4f",
             "1878f926302468949ef290b4fee621d1172e072eda1b42e366df68fc87f53c35583dbc043009e0b38a04a9b1ff617efe"),

            ("0a817078e7f30f08e94a25c2a1947160db1fe52042626660b8252cd339e678a1fecc0e6da60390a203532bd089a426b6",
             "097bd5d6ae3f5b5d0ba5e4099485caa2c505a1d900e4525af10254b3927ae0c82611be944ff8fdc6b278aab9e17ee27c",
             "1098f203da72c58dca61ffd52a3de82603d3154c527df51c2efe6298ea0eeaa065d57ba3a809b5e32d9d56dade119006",
             "0bcbd9df3505f049476f060c1d1c958fe8b34e426fd7e75424c9e227d9c4d3edbd5eddb8b1e89cc91b4a7bd3275d4d70"),
        ];

        for i in 0..msgs.len() {
            let u = hash_to_field_xmd_ro::<sha2::Sha256>(msgs[i].as_bytes(), &dst).unwrap();
            let exp_q = ECP::new_bigs(
                &BIG::from_hex(expected_q[i].0.to_string()),
                &BIG::from_hex(expected_q[i].1.to_string()),
            );
            let actual_q = map_to_curve(u.0);
            assert_eq!(exp_q, actual_q);
            let exp_q = ECP::new_bigs(
                &BIG::from_hex(expected_q[i].2.to_string()),
                &BIG::from_hex(expected_q[i].3.to_string()),
            );
            let actual_q = map_to_curve(u.1);
            assert_eq!(exp_q, actual_q);
        }
    }

    // Take from section G.9.2
    #[test]
    fn map_to_curve_nu_tests() {
        let dst = DomainSeparationTag::new(
            b"BLS12381G1_XMD:SHA-256_SSWU_NU_",
            Some(b"TESTGEN"),
            None,
            None,
        )
        .unwrap();
        let msgs = [
            "",
            "abc",
            "abcdef0123456789",
            "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        ];
        let q_s = [
            ("0dddf77f320e7848a457358ab8d3b84cbaf19307be26b91a10c211651691cd736b1f59d77aed3954f857f108d6966f5b", "0450ab32020649f22a2fca166a1d8a59d4c93f1eb078a4bedd6c48027b9933507a2a8ae4d915305f58ede781283325a9"),
            ("12897a9a513b12303a7f0f3a3cc7c838d16847a31507980945312bede915848159bd390b16b8e378b398e31a385d9180", "1372530cc0811d70071e50640281aa8aaf96ee09c01281ccfead92296cb9dacf5054aa51dbea730e46239e709042a15d"),
            ("08459bd42a955d6e247fce6c81eda0ad9645f9e666d141a71f0afa3fbc509b2c58550fe077d073cc752493400399fddd", "169d35a8c6bb915ae910f4c6cde359622746b0c8b2b241b411d0e92ef991d3e6a7b0fafabb93c1de2e3997d6e362ce8a"),
            ("08c937d529c01ab2398b85b0bff6da465ed6265d4944dbbef7d383eea40157927082739c7b5417027d2225c6cb9d5ef0", "059047d83b5ea1ff7f0665b406acede27f233d3414055cbff25b37614b679f08fd6d807b5956edec6abad36c5321d99e"),
        ];

        for i in 0..msgs.len() {
            let u = hash_to_field_xmd_nu::<sha2::Sha256>(msgs[i].as_bytes(), &dst).unwrap();
            let expected_q = ECP::new_bigs(
                &BIG::from_hex(q_s[i].0.to_string()),
                &BIG::from_hex(q_s[i].1.to_string()),
            );
            let actual_q1 = map_to_curve(u);
            assert_eq!(expected_q, actual_q1);
        }
    }

    // Take from section G.9.2
    #[test]
    fn hash_to_field_xmd_nu_tests() {
        let dst = DomainSeparationTag::new(
            b"BLS12381G1_XMD:SHA-256_SSWU_NU_",
            Some(b"TESTGEN"),
            None,
            None,
        )
        .unwrap();
        let msgs = [
            "",
            "abc",
            "abcdef0123456789",
            "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        ];
        let executed_u_s = [
            "0ccb6bda9b602ab82aae21c0291623e2f639648a6ada1c76d8ffb664130fd18d98a2cc6160624148827a9726678e7cd4",
            "08accd9a1bd4b75bb2e9f014ac354a198cbf607f0061d00a6286f5544cf4f9ecc1439e3194f570cbbc7b96d1a754f231",
            "0a359cf072db3a39acf22f086d825fcf49d0daf241d98902342380fc5130b44e55de8f684f300bc11c44dee526413363",
            "181d09392c52f7740d5eaae52123c1dfa4808343261d8bdbaf19e7773e5cdfd989165cd9ecc795500e5da2437dde2093",
        ];

        for i in 0..msgs.len() {
            let expected_u = BIG::from_hex(executed_u_s[i].to_string());
            let actual_u = hash_to_field_xmd_nu::<sha2::Sha256>(msgs[i].as_bytes(), &dst);
            assert!(actual_u.is_ok());
            assert_eq!(actual_u.unwrap(), expected_u);
        }
    }

    // Take from section G.9.1
    #[test]
    fn hash_to_field_xmd_ro_tests() {
        let dst = DomainSeparationTag::new(
            b"BLS12381G1_XMD:SHA-256_SSWU_RO_",
            Some(b"TESTGEN"),
            None,
            None,
        )
        .unwrap();
        let msgs = [
            "",
            "abc",
            "abcdef0123456789",
            "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        ];
        let expected_u_s = [
            ("14700e34d15178550475044b044b4e41ca8d52a655c34f8afea856d21d499f48c9370d2bae4ae8351305493e48d36ab5", "17e2da57f6fd3f11dba6119db4cd26b03e63e67b4e42db678d9c41fdfcaff00ba336d8563abcd9da6c17d2e1784ee858"),
            ("10c84aa245c74ee20579a27e63199be5d19cdfb5e44c6b587765931605d7790a1df6e1433f78bcddb4edb8553374f75e", "0f73433dcc2b5f9905c49d905bd62e1a1529b057c77194e56d196860d9d645167e0430aec9d3c70de31dd046fcab4a20"),
            ("11503eb4a558d0d2c5fc7cdddb51ba715c33577cf1a7f2f21a7eee6d2a570332bbbe53ae3392c9f8d8f6c172ae484692", "0efd59b8d98be7c491dfdb9d2a669e32e9bb348f8a64dbf7e47708dd5d40f484b1439109a3f96230bf63af72b908c43d"),
            ("134dc7f817cc08c5a3128892385ff6e9dd55f5e39d9a2d74ac74058d5dfc025d507806ab5d9254bd2334defbb477400d", "0eeaf2c6f4c1ca5cc039d99cb94234f67e65968f36d9dd77e95da55dadd085b50fbb11489167ded9157e5aac0d99d5be"),
        ];

        for i in 0..msgs.len() {
            let expected_u0 = BIG::from_hex(expected_u_s[i].0.to_string());
            let expected_u1 = BIG::from_hex(expected_u_s[i].1.to_string());
            let res = hash_to_field_xmd_ro::<sha2::Sha256>(msgs[i].as_bytes(), &dst);
            assert!(res.is_ok());
            let (actual_u0, actual_u1) = res.unwrap();
            assert_eq!(actual_u0, expected_u0);
            assert_eq!(actual_u1, expected_u1);
        }
    }
}