/* * 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 roots = if inverse { &bls::singleton().ReverseRootsOfUnityBLS48581[&fft_width] } else { &bls::singleton().RootsOfUnityBLS48581[&fft_width] }; if out.len() <= 16 { let l = out.len() as u64; for i in 0..l { let mut last = big::BIG::modmul( &values[offset as usize], &roots[0], &big::BIG::new_ints(&rom::CURVE_ORDER), ); for j in 1..l { let mid = big::BIG::modmul( &values[(offset + j * stride) as usize], &roots[((i * j) % l) as usize * roots_stride as usize], &big::BIG::new_ints(&rom::CURVE_ORDER), ); last = big::BIG::modadd( &last, &mid, &big::BIG::new_ints(&rom::CURVE_ORDER), ); } out[i as usize] = last; } 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; } } fn fft( values: &[big::BIG], fft_width: u64, inverse: bool, ) -> Result, 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() <= 16 { let l = out.len() as u64; for i in 0..l { let mut last = ecp::ECP::mul(&values[offset as usize].clone(), &roots[0]); for j in 1..l { let mid = ecp::ECP::mul( &values[(offset + j * stride) as usize].clone(), &roots[((i * j) % l) as usize * roots_stride as usize], ); &last.add(&mid); } out[i as usize] = last.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; } } fn fft_g1( values: &[ecp::ECP], fft_width: u64, inverse: bool, ) -> Result, 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 { let size = bytes.len() / 64; let trunc_last = bytes.len() % 64 > 0; let mut poly = Vec::new(); 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; } fn point_linear_combination( points: &Vec<&ecp::ECP>, scalars: &Vec, ) -> Result> { if points.len() != scalars.len() { return Err(format!( "length mismatch between arguments, points: {}, scalars: {}", points.len(), scalars.len(), ).into()); } let mut result = ecp::ECP::new(); for (i, point) in points.iter().enumerate() { let c = point.clone(); let p = c.mul(&scalars[i]); &result.add(&p); } 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 { 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].iter().collect(), &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 { 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 mut o = big::BIG::new_int(1); let mut oinv = o.clone(); oinv.invmodp(&big::BIG::new_ints(&rom::CURVE_ORDER)); let divisors: Vec = vec![ subz, o ]; let invdivisors: Vec = vec![ subzinv, oinv ]; let mut a: Vec = 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 = 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)].iter().collect(), &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); let p = ecp::ECP::frombytes(proof); return verify( &c, &z, &y, &p, ); } pub fn init() { bls::singleton(); }