penumbra_proof_setup/single/group.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
//! This module serves as a small abstraction layer over the group operations we need.
//!
//! This simplifies logic in other parts of this crate, since we don't need to rely on
//! arkworks directly.
use ark_ec::pairing::{Pairing, PairingOutput};
use ark_ec::scalar_mul::{variable_base::VariableBaseMSM, ScalarMul};
use ark_serialize::CanonicalSerialize;
use decaf377::Bls12_377;
use rand_core::CryptoRngCore;
/// The group used for the left side of pairings.
pub type G1 = <Bls12_377 as Pairing>::G1;
/// A prepared version of G1 for more efficient pairings.
pub type G1Prepared = <Bls12_377 as Pairing>::G1Prepared;
/// The group used for the right side of pairings.
pub type G2 = <Bls12_377 as Pairing>::G2;
/// A prepared version of G2 for more efficient pairings.
pub type G2Prepared = <Bls12_377 as Pairing>::G2Prepared;
/// The group used for the output of pairings.
pub type GT = PairingOutput<Bls12_377>;
/// The field of scalars over which these groups form modules.
pub type F = <Bls12_377 as Pairing>::ScalarField;
/// The pairing operation between the two groups.
pub fn pairing(a: impl Into<G1Prepared>, b: impl Into<G2Prepared>) -> GT {
<Bls12_377 as Pairing>::pairing(a, b)
}
// # Batched Pairing Checks
/// Sample a random field that's "small" but still big enough for pairing checks.
fn rand_small_f<R: CryptoRngCore>(rng: &mut R) -> F {
// 128 bits of security
let mut bytes = [0u8; 16];
rng.fill_bytes(&mut bytes);
F::from_le_bytes_mod_order(&bytes)
}
// This is just a little trick to avoid code duplication in the variants of this pairing checker
// for different base groups.
macro_rules! make_batched_pairing_checker {
($name:ident, $gl:ident, $gr:ident, $glp:ident, $grp:ident, $pairing:ident) => {
/// A tool for efficiently making many pairing checks.
///
/// This version is for pairing checks where the varying parts
/// of each side of the pairing equality are in $gl and $gr, respectively.
pub struct $name {
// Invariant: both vecs have the same length.
vary_l: Vec<$gl>,
base_l: $glp,
vary_r: Vec<$gr>,
base_r: $grp,
}
impl $name {
pub fn new(base_l: impl Into<$glp>, base_r: impl Into<$grp>) -> Self {
Self {
vary_l: Vec::new(),
base_l: base_l.into(),
vary_r: Vec::new(),
base_r: base_r.into(),
}
}
pub fn add(&mut self, l: $gl, r: $gr) {
self.vary_l.push(l);
self.vary_r.push(r);
}
#[must_use]
pub fn check<R: CryptoRngCore>(self, rng: &mut R) -> bool {
let n = self.vary_l.len();
let scalars = (0..n).map(|_| rand_small_f(rng)).collect::<Vec<_>>();
let ready_to_msm_l = <$gl as ScalarMul>::batch_convert_to_mul_base(&self.vary_l);
let l = <$gl as VariableBaseMSM>::msm_unchecked(&ready_to_msm_l, &scalars);
let ready_to_msm_r = <$gr as ScalarMul>::batch_convert_to_mul_base(&self.vary_r);
let r = <$gr as VariableBaseMSM>::msm_unchecked(&ready_to_msm_r, &scalars);
pairing(l, self.base_l) == $pairing(self.base_r, r)
}
}
};
}
// This is just a gimmick to support our macro shenanigans
fn swapped_pairing(a: impl Into<G2Prepared>, b: impl Into<G1Prepared>) -> GT {
pairing(b, a)
}
make_batched_pairing_checker!(
BatchedPairingChecker11,
G1,
G1,
G2Prepared,
G2Prepared,
swapped_pairing
);
make_batched_pairing_checker!(
BatchedPairingChecker12,
G1,
G2,
G2Prepared,
G1Prepared,
pairing
);
/// The size of the hash we use.
pub(crate) const HASH_SIZE: usize = 32;
/// The hash output we use when we need bytes.
pub(crate) type Hash = [u8; 32];
/// A utility struct for hashing group elements and producing fields.
///
/// This avoids having to deal with some serialization and reduction code from arkworks.
///
/// All methods of this struct will handle separation between elements correctly.
/// This means that feeding in two elements is distinct from feeding in the "concatenation"
/// of this elements. One place where you still need manual effort on the user's end
/// is when you're hashing a variable number of elements.
#[derive(Clone)]
pub(crate) struct GroupHasher {
state: blake2b_simd::State,
}
impl GroupHasher {
/// Create a new hasher with a personalization string.
///
/// Because of BLAKE2's limitations, this has to be 16 bytes at most.
/// This function will panic if that isn't the case.
pub fn new(personalization: &'static [u8]) -> Self {
let state = blake2b_simd::Params::new()
.personal(personalization)
.to_state();
Self { state }
}
// Separate methods because the semantics of what this is trying to do are different,
// even if eating a usize happens to do the right thing.
fn write_len(&mut self, len: usize) {
self.eat_usize(len);
}
/// Consume some bytes, adding it to the state of the hash.
///
/// These bytes will be length prefixed, and so calling this function
/// multiple times is not the same as calling it with the concatenation
/// of those bytes.
pub fn eat_bytes(&mut self, x: &[u8]) {
self.write_len(x.len());
self.state.update(x);
}
/// Eat anything that's canonically serializable (yummy!).
///
/// This will handle padding between elements, using the declared length.
///
/// We keep this internal, to make a simpler public API, since we only have
/// a handful of types we actually need to use this for.
fn eat_canonical<T: CanonicalSerialize>(&mut self, x: &T) {
self.write_len(x.compressed_size());
x.serialize_compressed(&mut self.state)
.expect("failed to serialize element");
}
/// Consume a usize value, adding it into the state of this hash.
///
/// This is useful for (i.e. intended for) encoding metadata.
pub fn eat_usize(&mut self, x: usize) {
// On basically any platform this should fit in a u64
self.state.update(&(x as u64).to_le_bytes());
}
/// Consume a G1 group element, adding it into the state of this hash.
pub fn eat_g1(&mut self, x: &G1) {
self.eat_canonical(x);
}
/// Consume a G2 group element, adding it into the state of this hash.
pub fn eat_g2(&mut self, x: &G2) {
self.eat_canonical(x);
}
/// Consume a scalar, adding it into the state of this hash.
pub fn eat_f(&mut self, x: &F) {
self.eat_canonical(x);
}
/// Finalize this hash function, producing a scalar.
pub fn finalize(self) -> F {
F::from_le_bytes_mod_order(self.state.finalize().as_bytes())
}
/// Finalize this hash function, producing bytes.
pub fn finalize_bytes(self) -> Hash {
let mut out = [0u8; HASH_SIZE];
out.copy_from_slice(&self.state.finalize().as_bytes()[..HASH_SIZE]);
out
}
}