ceremonyclient/crates/bls48581/src/lib.rs
2024-10-12 11:55:17 -07:00

499 lines
12 KiB
Rust

/*
* Copyright (c) 2012-2020 MIRACL UK Ltd.
*
* This file is part of MIRACL Core
* (see https://github.com/miracl/core).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#![allow(clippy::many_single_char_names)]
#![allow(clippy::needless_range_loop)]
#![allow(clippy::manual_memcpy)]
#![allow(clippy::new_without_default)]
pub mod bls48581;
pub mod bls;
pub mod arch;
pub mod rand;
pub mod hmac;
pub mod hash256;
pub mod hash384;
pub mod hash512;
pub mod sha3;
use std::error::Error;
use bls48581::big;
use bls48581::ecp;
use bls48581::ecp8;
use bls48581::rom;
use bls48581::pair8;
uniffi::include_scaffolding!("lib");
fn recurse_fft(
values: &[big::BIG],
offset: u64,
stride: u64,
roots_stride: u64,
out: &mut [big::BIG],
fft_width: u64,
inverse: bool,
) {
let M = &big::BIG::new_ints(&rom::CURVE_ORDER);
let roots = if inverse {
&bls::singleton().ReverseRootsOfUnityBLS48581[&fft_width]
} else {
&bls::singleton().RootsOfUnityBLS48581[&fft_width]
};
if out.len() == 1 {
// optimization: we're working in bls48-581, the first roots of unity
// value is always 1 no matter the fft width, so we can skip the
// multiplication:
out[0] = values[offset as usize].clone();
return;
}
let half = (out.len() as u64) >> 1;
// slide to the left
recurse_fft(
values,
offset,
stride << 1,
roots_stride << 1,
&mut out[..half as usize],
fft_width,
inverse,
);
// slide to the right
recurse_fft(
values,
offset + stride,
stride << 1,
roots_stride << 1,
&mut out[half as usize..],
fft_width,
inverse,
);
// cha cha now, y'all
for i in 0..half {
let mul = big::BIG::modmul(
&out[(i + half) as usize],
&roots[(i * roots_stride) as usize],
&big::BIG::new_ints(&rom::CURVE_ORDER),
);
let mul_add = big::BIG::modadd(
&out[i as usize],
&mul,
&big::BIG::new_ints(&rom::CURVE_ORDER),
);
out[(i + half) as usize] = big::BIG::modadd(
&out[i as usize],
&big::BIG::modneg(&mul, &big::BIG::new_ints(&rom::CURVE_ORDER)),
&big::BIG::new_ints(&rom::CURVE_ORDER),
);
out[i as usize] = mul_add;
}
}
pub fn fft(
values: &[big::BIG],
fft_width: u64,
inverse: bool,
) -> Result<Vec<big::BIG>, String> {
let mut width = values.len() as u64;
if width > fft_width {
return Err("invalid width of values".into());
}
if width & (width - 1) != 0 {
width = nearest_power_of_two(width);
}
// We make a copy so we can mutate it during the work.
let mut working_values = vec![big::BIG::new(); width as usize];
for i in 0..values.len() {
working_values[i] = values[i].clone();
}
for i in values.len()..width as usize {
working_values[i] = big::BIG::new();
}
let mut out = vec![big::BIG::new(); width as usize];
let stride = fft_width / width;
if inverse {
let mut inv_len = big::BIG::new_int(width as isize);
inv_len.invmodp(&big::BIG::new_ints(&rom::CURVE_ORDER));
recurse_fft(&working_values, 0, 1, stride, &mut out, fft_width, inverse);
for i in 0..out.len() {
out[i] = big::BIG::modmul(&out[i], &inv_len, &big::BIG::new_ints(&rom::CURVE_ORDER));
}
Ok(out)
} else {
recurse_fft(&working_values, 0, 1, stride, &mut out, fft_width, inverse);
Ok(out)
}
}
fn recurse_fft_g1(
values: &[ecp::ECP],
offset: u64,
stride: u64,
roots_stride: u64,
out: &mut [ecp::ECP],
fft_width: u64,
inverse: bool,
) {
let roots = if inverse {
&bls::singleton().ReverseRootsOfUnityBLS48581[&fft_width]
} else {
&bls::singleton().RootsOfUnityBLS48581[&fft_width]
};
if out.len() == 1 {
out[0] = values[offset as usize].clone();
return;
}
let half = (out.len() as u64) >> 1;
// slide to the left
recurse_fft_g1(
values,
offset,
stride << 1,
roots_stride << 1,
&mut out[..half as usize],
fft_width,
inverse,
);
// slide to the right
recurse_fft_g1(
values,
offset + stride,
stride << 1,
roots_stride << 1,
&mut out[half as usize..],
fft_width,
inverse,
);
// cha cha now, y'all
for i in 0..half {
let mul = out[(i + half) as usize].clone().mul(
&roots[(i * roots_stride) as usize].clone(),
);
let mut mul_add = out[i as usize].clone();
mul_add.add(&mul.clone());
out[(i + half) as usize] = out[i as usize].clone();
out[(i + half) as usize].sub(&mul);
out[i as usize] = mul_add;
}
}
pub fn fft_g1(
values: &[ecp::ECP],
fft_width: u64,
inverse: bool,
) -> Result<Vec<ecp::ECP>, String> {
let mut width = values.len() as u64;
if width > fft_width {
return Err("invalid width of values".into());
}
if width & (width - 1) != 0 {
width = nearest_power_of_two(width);
}
let mut working_values = vec![ecp::ECP::new(); width as usize];
for i in 0..values.len() {
working_values[i] = values[i].clone();
}
for i in values.len()..width as usize {
working_values[i] = ecp::ECP::generator();
}
let mut out = vec![ecp::ECP::new(); width as usize];
let stride = fft_width / width;
if inverse {
let mut inv_len = big::BIG::new_int(width as isize);
inv_len.invmodp(&big::BIG::new_ints(&rom::CURVE_ORDER));
recurse_fft_g1(&working_values, 0, 1, stride, &mut out, fft_width, inverse);
for i in 0..out.len() {
out[i] = out[i].clone().mul(&inv_len);
}
Ok(out)
} else {
recurse_fft_g1(&working_values, 0, 1, stride, &mut out, fft_width, inverse);
Ok(out)
}
}
fn nearest_power_of_two(number: u64) -> u64 {
let mut power = 1;
while number > power {
power <<= 1;
}
power
}
fn bytes_to_polynomial(
bytes: &[u8],
) -> Vec<big::BIG> {
let size = bytes.len() / 64;
let trunc_last = bytes.len() % 64 > 0;
let mut poly = Vec::with_capacity(size + (if trunc_last { 1 } else { 0 }));
for i in 0..size {
let scalar = big::BIG::frombytes(&bytes[i * 64..(i + 1) * 64]);
poly.push(scalar);
}
if trunc_last {
let scalar = big::BIG::frombytes(&bytes[size * 64..]);
poly.push(scalar);
}
return poly;
}
pub fn point_linear_combination(
points: &[ecp::ECP],
scalars: &Vec<big::BIG>,
) -> Result<ecp::ECP, Box<dyn Error>> {
if points.len() != scalars.len() {
return Err(format!(
"length mismatch between arguments, points: {}, scalars: {}",
points.len(),
scalars.len(),
).into());
}
let result = ecp::ECP::muln(points.len(), points, scalars.as_slice());
Ok(result)
}
fn verify(
commitment: &ecp::ECP,
z: &big::BIG,
y: &big::BIG,
proof: &ecp::ECP,
) -> bool {
let z2 = ecp8::ECP8::generator().mul(z);
let y1 = ecp::ECP::generator().mul(y);
let mut xz = bls::singleton().CeremonyBLS48581G2[1].clone();
xz.sub(&z2);
let mut cy = commitment.clone();
cy.sub(&y1);
cy.neg();
let mut r = pair8::initmp();
pair8::another(&mut r, &xz, &proof);
pair8::another(&mut r, &ecp8::ECP8::generator(), &cy);
let mut v = pair8::miller(&mut r);
v = pair8::fexp(&v);
return v.isunity();
}
pub fn commit_raw(
data: &[u8],
poly_size: u64,
) -> Vec<u8> {
let mut poly = bytes_to_polynomial(data);
while poly.len() < poly_size as usize {
poly.push(big::BIG::new());
}
match point_linear_combination(
&bls::singleton().FFTBLS48581[&poly_size],
&poly,
) {
Ok(commit) => {
let mut b = [0u8; 74];
commit.tobytes(&mut b, true);
return b.to_vec();
}
Err(_e) => {
return [].to_vec();
}
}
}
pub fn prove_raw(
data: &[u8],
index: u64,
poly_size: u64,
) -> Vec<u8> {
let mut poly = bytes_to_polynomial(data);
while poly.len() < poly_size as usize {
poly.push(big::BIG::new());
}
let z = bls::singleton().RootsOfUnityBLS48581[&poly_size][index as usize];
match fft(
&poly,
poly_size,
true,
) {
Ok(eval_poly) => {
let mut subz = big::BIG::new_int(0);
subz = big::BIG::modadd(&subz, &big::BIG::modneg(&z, &big::BIG::new_ints(&rom::CURVE_ORDER)), &big::BIG::new_ints(&rom::CURVE_ORDER));
let mut subzinv = subz.clone();
subzinv.invmodp(&big::BIG::new_ints(&rom::CURVE_ORDER));
let o = big::BIG::new_int(1);
let mut oinv = o.clone();
oinv.invmodp(&big::BIG::new_ints(&rom::CURVE_ORDER));
let divisors: Vec<big::BIG> = vec![
subz,
o
];
let invdivisors: Vec<big::BIG> = vec![
subzinv,
oinv
];
let mut a: Vec<big::BIG> = eval_poly.iter().map(|x| x.clone()).collect();
// Adapted from Feist's amortized proofs:
let mut a_pos = a.len() - 1;
let b_pos = divisors.len() - 1;
let mut diff = a_pos as isize - b_pos as isize;
let mut out: Vec<big::BIG> = vec![big::BIG::new(); (diff + 1) as usize];
while diff >= 0 {
out[diff as usize] = a[a_pos].clone();
out[diff as usize] = big::BIG::modmul(&out[diff as usize], &invdivisors[b_pos], &big::BIG::new_ints(&rom::CURVE_ORDER));
for i in (0..=b_pos).rev() {
let den = &out[diff as usize].clone();
a[diff as usize + i] = a[diff as usize + i].clone();
a[diff as usize + i] = big::BIG::modadd(
&a[diff as usize + i],
&big::BIG::modneg(
&big::BIG::modmul(&den, &divisors[i], &big::BIG::new_ints(&rom::CURVE_ORDER)),
&big::BIG::new_ints(&rom::CURVE_ORDER)
),
&big::BIG::new_ints(&rom::CURVE_ORDER)
);
}
let mut b = [0u8;73];
out[diff as usize].tobytes(&mut b);
a_pos -= 1;
diff -= 1;
}
match point_linear_combination(
&bls::singleton().CeremonyBLS48581G1[..(poly_size as usize - 1)],
&out,
) {
Ok(proof) => {
let mut b = [0u8; 74];
proof.tobytes(&mut b, true);
return b.to_vec();
}
Err(_e) => {
return [].to_vec();
}
}
},
Err(_e) => {
return [].to_vec();
}
}
}
pub fn verify_raw(
data: &[u8],
commit: &[u8],
index: u64,
proof: &[u8],
poly_size: u64,
) -> bool {
let z = bls::singleton().RootsOfUnityBLS48581[&poly_size][index as usize];
let y = big::BIG::frombytes(data);
let c = ecp::ECP::frombytes(commit);
if c.is_infinity() || c.equals(&ecp::ECP::generator()) {
return false;
}
let p = ecp::ECP::frombytes(proof);
if p.is_infinity() || p.equals(&ecp::ECP::generator()) {
return false;
}
if poly_size > 1024 {
let mut xc = c.clone();
xc.sub(&bls::singleton().FFTBLS48581[&poly_size][index as usize].clone().mul(&y));
let mut check = c.clone();
check.neg();
let yp = &bls::singleton().CeremonyBLS48581G2[1].clone().mul(&y);
let mut r = pair8::initmp();
pair8::another(&mut r, &bls::singleton().CeremonyBLS48581G2[1], &check);
pair8::another(&mut r, &yp, &bls::singleton().FFTBLS48581[&poly_size][index as usize]);
pair8::another(&mut r, &bls::singleton().CeremonyBLS48581G2[1], &xc);
let mut v = pair8::miller(&mut r);
v = pair8::fexp(&v);
return v.isunity();
}
return verify(
&c,
&z,
&y,
&p,
);
}
pub fn init() {
bls::singleton();
}
#[cfg(test)]
mod tests {
use ecp::ECP;
use super::*;
#[test]
fn fft_matches_fft_g1_when_raised() {
init();
let mut rand = rand::RAND::new();
let mut v = vec![big::BIG::new(); 16];
let mut vp = vec![ECP::new(); 16];
for i in 0..16 {
v[i] = big::BIG::random(&mut rand);
vp[i] = ECP::generator().mul(&v[i]);
}
let scalars = fft(v.as_slice(), 16, false).unwrap();
let points = fft_g1(vp.as_slice(), 16, false).unwrap();
for (i, s) in scalars.iter().enumerate() {
let sp = ECP::generator().mul(&s);
assert!(points[i].equals(&sp));
}
}
}