|
| 1 | +// This module contains a safe wrapper around the blst library. |
| 2 | + |
| 3 | +use crate::bls12_381_const::SCALAR_LENGTH; |
| 4 | +use blst::{ |
| 5 | + blst_final_exp, blst_fp, blst_fp12, blst_fp12_is_one, blst_fp12_mul, blst_fp2, blst_map_to_g1, |
| 6 | + blst_map_to_g2, blst_miller_loop, blst_p1, blst_p1_add_or_double_affine, blst_p1_affine, |
| 7 | + blst_p1_from_affine, blst_p1_to_affine, blst_p2, blst_p2_add_or_double_affine, blst_p2_affine, |
| 8 | + blst_p2_from_affine, blst_p2_to_affine, MultiPoint, |
| 9 | +}; |
| 10 | + |
| 11 | +#[inline] |
| 12 | +fn p1_to_affine(p: &blst_p1) -> blst_p1_affine { |
| 13 | + let mut p_affine = blst_p1_affine::default(); |
| 14 | + // SAFETY: both inputs are valid blst types |
| 15 | + unsafe { blst_p1_to_affine(&mut p_affine, p) }; |
| 16 | + p_affine |
| 17 | +} |
| 18 | + |
| 19 | +#[inline] |
| 20 | +fn p1_from_affine(p_affine: &blst_p1_affine) -> blst_p1 { |
| 21 | + let mut p = blst_p1::default(); |
| 22 | + // SAFETY: both inputs are valid blst types |
| 23 | + unsafe { blst_p1_from_affine(&mut p, p_affine) }; |
| 24 | + p |
| 25 | +} |
| 26 | + |
| 27 | +#[inline] |
| 28 | +fn p1_add_or_double(p: &blst_p1, p_affine: &blst_p1_affine) -> blst_p1 { |
| 29 | + let mut result = blst_p1::default(); |
| 30 | + // SAFETY: all inputs are valid blst types |
| 31 | + unsafe { blst_p1_add_or_double_affine(&mut result, p, p_affine) }; |
| 32 | + result |
| 33 | +} |
| 34 | + |
| 35 | +#[inline] |
| 36 | +fn p2_to_affine(p: &blst_p2) -> blst_p2_affine { |
| 37 | + let mut p_affine = blst_p2_affine::default(); |
| 38 | + // SAFETY: both inputs are valid blst types |
| 39 | + unsafe { blst_p2_to_affine(&mut p_affine, p) }; |
| 40 | + p_affine |
| 41 | +} |
| 42 | + |
| 43 | +#[inline] |
| 44 | +fn p2_from_affine(p_affine: &blst_p2_affine) -> blst_p2 { |
| 45 | + let mut p = blst_p2::default(); |
| 46 | + // SAFETY: both inputs are valid blst types |
| 47 | + unsafe { blst_p2_from_affine(&mut p, p_affine) }; |
| 48 | + p |
| 49 | +} |
| 50 | + |
| 51 | +#[inline] |
| 52 | +fn p2_add_or_double(p: &blst_p2, p_affine: &blst_p2_affine) -> blst_p2 { |
| 53 | + let mut result = blst_p2::default(); |
| 54 | + // SAFETY: all inputs are valid blst types |
| 55 | + unsafe { blst_p2_add_or_double_affine(&mut result, p, p_affine) }; |
| 56 | + result |
| 57 | +} |
| 58 | + |
| 59 | +/// p1_add_affine adds two G1 points in affine form, returning the result in affine form |
| 60 | +/// |
| 61 | +/// Note: `a` and `b` can be the same, ie this method is safe to call if one wants |
| 62 | +/// to essentially double a point |
| 63 | +#[inline] |
| 64 | +pub(super) fn p1_add_affine(a: &blst_p1_affine, b: &blst_p1_affine) -> blst_p1_affine { |
| 65 | + // Convert first point to Jacobian coordinates |
| 66 | + let a_jacobian = p1_from_affine(a); |
| 67 | + |
| 68 | + // Add second point (in affine) to first point (in Jacobian) |
| 69 | + let sum_jacobian = p1_add_or_double(&a_jacobian, b); |
| 70 | + |
| 71 | + // Convert result back to affine coordinates |
| 72 | + p1_to_affine(&sum_jacobian) |
| 73 | +} |
| 74 | + |
| 75 | +/// Add two G2 points in affine form, returning the result in affine form |
| 76 | +#[inline] |
| 77 | +pub(super) fn p2_add_affine(a: &blst_p2_affine, b: &blst_p2_affine) -> blst_p2_affine { |
| 78 | + // Convert first point to Jacobian coordinates |
| 79 | + let a_jacobian = p2_from_affine(a); |
| 80 | + |
| 81 | + // Add second point (in affine) to first point (in Jacobian) |
| 82 | + let sum_jacobian = p2_add_or_double(&a_jacobian, b); |
| 83 | + |
| 84 | + // Convert result back to affine coordinates |
| 85 | + p2_to_affine(&sum_jacobian) |
| 86 | +} |
| 87 | + |
| 88 | +/// Performs multi-scalar multiplication (MSM) for G1 points |
| 89 | +/// |
| 90 | +/// Takes a vector of G1 points and corresponding scalars, and returns their weighted sum |
| 91 | +/// |
| 92 | +/// Note: This method assumes that `g1_points` does not contain any points at infinity. |
| 93 | +#[inline] |
| 94 | +pub(super) fn p1_msm( |
| 95 | + g1_points: Vec<blst_p1_affine>, |
| 96 | + scalars_bytes: Vec<u8>, |
| 97 | + nbits: usize, |
| 98 | +) -> blst_p1_affine { |
| 99 | + assert!( |
| 100 | + scalars_bytes.len() % SCALAR_LENGTH == 0, |
| 101 | + "Each scalar should be {SCALAR_LENGTH} bytes" |
| 102 | + ); |
| 103 | + |
| 104 | + assert_eq!( |
| 105 | + g1_points.len(), |
| 106 | + scalars_bytes.len() / SCALAR_LENGTH, |
| 107 | + "number of scalars should equal the number of g1 points" |
| 108 | + ); |
| 109 | + // When no inputs are given, we trigger an assert. |
| 110 | + // While it is mathematically sound to have no inputs (can return point at infinity) |
| 111 | + // EIP2537 forbids this and since this is the only function that |
| 112 | + // currently calls this method, we have this assert. |
| 113 | + assert!( |
| 114 | + !g1_points.is_empty(), |
| 115 | + "number of inputs to pairing should be non-zero" |
| 116 | + ); |
| 117 | + |
| 118 | + // Perform multi-scalar multiplication |
| 119 | + let multiexp = g1_points.mult(&scalars_bytes, nbits); |
| 120 | + |
| 121 | + // Convert result back to affine coordinates |
| 122 | + p1_to_affine(&multiexp) |
| 123 | +} |
| 124 | + |
| 125 | +/// Performs multi-scalar multiplication (MSM) for G2 points |
| 126 | +/// |
| 127 | +/// Takes a vector of G2 points and corresponding scalars, and returns their weighted sum |
| 128 | +/// |
| 129 | +/// Note: This method assumes that `g2_points` does not contain any points at infinity. |
| 130 | +#[inline] |
| 131 | +pub(super) fn p2_msm( |
| 132 | + g2_points: Vec<blst_p2_affine>, |
| 133 | + scalars_bytes: Vec<u8>, |
| 134 | + nbits: usize, |
| 135 | +) -> blst_p2_affine { |
| 136 | + assert!( |
| 137 | + scalars_bytes.len() % SCALAR_LENGTH == 0, |
| 138 | + "Each scalar should be {SCALAR_LENGTH} bytes" |
| 139 | + ); |
| 140 | + |
| 141 | + assert_eq!( |
| 142 | + g2_points.len(), |
| 143 | + scalars_bytes.len() / SCALAR_LENGTH, |
| 144 | + "number of scalars should equal the number of g2 points" |
| 145 | + ); |
| 146 | + // When no inputs are given, we trigger an assert. |
| 147 | + // While it is mathematically sound to have no inputs (can return point at infinity) |
| 148 | + // EIP2537 forbids this and since this is the only function that |
| 149 | + // currently calls this method, we have this assert. |
| 150 | + assert!( |
| 151 | + !g2_points.is_empty(), |
| 152 | + "number of inputs to pairing should be non-zero" |
| 153 | + ); |
| 154 | + |
| 155 | + // Perform multi-scalar multiplication |
| 156 | + let multiexp = g2_points.mult(&scalars_bytes, nbits); |
| 157 | + |
| 158 | + // Convert result back to affine coordinates |
| 159 | + p2_to_affine(&multiexp) |
| 160 | +} |
| 161 | + |
| 162 | +/// Maps a field element to a G1 point |
| 163 | +/// |
| 164 | +/// Takes a field element (blst_fp) and returns the corresponding G1 point in affine form |
| 165 | +#[inline] |
| 166 | +pub(super) fn map_fp_to_g1(fp: &blst_fp) -> blst_p1_affine { |
| 167 | + // Create a new G1 point in Jacobian coordinates |
| 168 | + let mut p = blst_p1::default(); |
| 169 | + |
| 170 | + // Map the field element to a point on the curve |
| 171 | + // SAFETY: `p` and `fp` are blst values |
| 172 | + // Third argument is unused if null |
| 173 | + unsafe { blst_map_to_g1(&mut p, fp, core::ptr::null()) }; |
| 174 | + |
| 175 | + // Convert to affine coordinates |
| 176 | + p1_to_affine(&p) |
| 177 | +} |
| 178 | + |
| 179 | +/// Maps a field element to a G2 point |
| 180 | +/// |
| 181 | +/// Takes a field element (blst_fp2) and returns the corresponding G2 point in affine form |
| 182 | +#[inline] |
| 183 | +pub(super) fn map_fp2_to_g2(fp2: &blst_fp2) -> blst_p2_affine { |
| 184 | + // Create a new G2 point in Jacobian coordinates |
| 185 | + let mut p = blst_p2::default(); |
| 186 | + |
| 187 | + // Map the field element to a point on the curve |
| 188 | + // SAFETY: `p` and `fp2` are blst values |
| 189 | + // Third argument is unused if null |
| 190 | + unsafe { blst_map_to_g2(&mut p, fp2, core::ptr::null()) }; |
| 191 | + |
| 192 | + // Convert to affine coordinates |
| 193 | + p2_to_affine(&p) |
| 194 | +} |
| 195 | + |
| 196 | +/// Computes a single miller loop for a given G1, G2 pair |
| 197 | +#[inline] |
| 198 | +fn compute_miller_loop(g1: &blst_p1_affine, g2: &blst_p2_affine) -> blst_fp12 { |
| 199 | + let mut result = blst_fp12::default(); |
| 200 | + |
| 201 | + // SAFETY: All arguments are valid blst types |
| 202 | + unsafe { blst_miller_loop(&mut result, g2, g1) } |
| 203 | + |
| 204 | + result |
| 205 | +} |
| 206 | + |
| 207 | +/// multiply_fp12 multiplies two fp12 elements |
| 208 | +#[inline] |
| 209 | +fn multiply_fp12(a: &blst_fp12, b: &blst_fp12) -> blst_fp12 { |
| 210 | + let mut result = blst_fp12::default(); |
| 211 | + |
| 212 | + // SAFETY: All arguments are valid blst types |
| 213 | + unsafe { blst_fp12_mul(&mut result, a, b) } |
| 214 | + |
| 215 | + result |
| 216 | +} |
| 217 | + |
| 218 | +/// final_exp computes the final exponentiation on an fp12 element |
| 219 | +#[inline] |
| 220 | +fn final_exp(f: &blst_fp12) -> blst_fp12 { |
| 221 | + let mut result = blst_fp12::default(); |
| 222 | + |
| 223 | + // SAFETY: All arguments are valid blst types |
| 224 | + unsafe { blst_final_exp(&mut result, f) } |
| 225 | + |
| 226 | + result |
| 227 | +} |
| 228 | + |
| 229 | +/// is_fp12_one checks if an fp12 element equals |
| 230 | +/// multiplicative identity element, one |
| 231 | +#[inline] |
| 232 | +fn is_fp12_one(f: &blst_fp12) -> bool { |
| 233 | + // SAFETY: argument is a valid blst type |
| 234 | + unsafe { blst_fp12_is_one(f) } |
| 235 | +} |
| 236 | + |
| 237 | +/// pairing_check performs a pairing check on a list of G1 and G2 point pairs and |
| 238 | +/// returns true if the result is equal to the identity element. |
| 239 | +#[inline] |
| 240 | +pub(super) fn pairing_check(pairs: &[(blst_p1_affine, blst_p2_affine)]) -> bool { |
| 241 | + // When no inputs are given, we trigger an assert. |
| 242 | + // While it is mathematically sound to have no inputs (can return true) |
| 243 | + // EIP2537 forbids this and since this is the only function that |
| 244 | + // currently calls this method, we have this assert. |
| 245 | + assert!( |
| 246 | + !pairs.is_empty(), |
| 247 | + "number of inputs to pairing should be non-zero" |
| 248 | + ); |
| 249 | + |
| 250 | + // Compute the miller loop for the first pair |
| 251 | + let (first_g1, first_g2) = &pairs[0]; |
| 252 | + let mut acc = compute_miller_loop(first_g1, first_g2); |
| 253 | + |
| 254 | + // For the remaining pairs, compute miller loop and multiply with the accumulated result |
| 255 | + for (g1, g2) in pairs.iter().skip(1) { |
| 256 | + let ml = compute_miller_loop(g1, g2); |
| 257 | + acc = multiply_fp12(&acc, &ml); |
| 258 | + } |
| 259 | + |
| 260 | + // Apply final exponentiation and check if result is 1 |
| 261 | + let final_result = final_exp(&acc); |
| 262 | + |
| 263 | + // Check if the result is one (identity element) |
| 264 | + is_fp12_one(&final_result) |
| 265 | +} |
0 commit comments