fp12_2over3over2.rs
1 // Copyright (c) 2019-2025 Alpha-Delta Network Inc. 2 // This file is part of the deltavm library. 3 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at: 7 8 // http://www.apache.org/licenses/LICENSE-2.0 9 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 use crate::{Field, Fp2, Fp2Parameters, One, Zero, fp6_3over2::*}; 17 use deltavm_utilities::{FromBytes, ToBits, ToBytes, bititerator::BitIteratorBE, rand::Uniform, serialize::*}; 18 19 use rand::{ 20 Rng, 21 distributions::{Distribution, Standard}, 22 }; 23 use serde::{Deserialize, Serialize}; 24 use std::{ 25 cmp::Ordering, 26 fmt::Debug, 27 hash::Hash, 28 io::{Read, Result as IoResult, Write}, 29 ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}, 30 }; 31 32 pub trait Fp12Parameters: 'static + Copy + Clone + Debug + Default + PartialEq + Eq + Hash + Send + Sync { 33 type Fp6Params: Fp6Parameters; 34 35 /// Coefficients for the Frobenius automorphism. 36 const FROBENIUS_COEFF_FP12_C1: [Fp2<Fp2Params<Self>>; 12]; 37 } 38 39 type Fp2Params<P> = <<P as Fp12Parameters>::Fp6Params as Fp6Parameters>::Fp2Params; 40 41 /// An element of Fp12, represented by c0 + c1 * v 42 #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)] 43 pub struct Fp12<P: Fp12Parameters> { 44 pub c0: Fp6<P::Fp6Params>, 45 pub c1: Fp6<P::Fp6Params>, 46 } 47 48 impl<P: Fp12Parameters> Fp12<P> { 49 /// Initializes an element of `Fp12` from two `Fp6` elements. 50 pub const fn new(c0: Fp6<P::Fp6Params>, c1: Fp6<P::Fp6Params>) -> Self { 51 Self { c0, c1 } 52 } 53 } 54 55 impl<P: Fp12Parameters> Fp12<P> { 56 pub fn conjugate(&mut self) { 57 self.c1 = self.c1.neg(); 58 } 59 60 pub fn mul_by_fp(&mut self, element: &<<P::Fp6Params as Fp6Parameters>::Fp2Params as Fp2Parameters>::Fp) { 61 self.c0.mul_by_fp(element); 62 self.c1.mul_by_fp(element); 63 } 64 65 /// Multiply by quadratic nonresidue v. 66 #[inline(always)] 67 pub(crate) fn mul_fp6_by_nonresidue(fe: &Fp6<P::Fp6Params>) -> Fp6<P::Fp6Params> { 68 let new_c0 = P::Fp6Params::mul_fp2_by_nonresidue(&fe.c2); 69 let new_c1 = fe.c0; 70 let new_c2 = fe.c1; 71 Fp6::new(new_c0, new_c1, new_c2) 72 } 73 74 pub fn mul_by_034(&mut self, c0: &Fp2<Fp2Params<P>>, c3: &Fp2<Fp2Params<P>>, c4: &Fp2<Fp2Params<P>>) { 75 let a0 = self.c0.c0 * c0; 76 let a1 = self.c0.c1 * c0; 77 let a2 = self.c0.c2 * c0; 78 let a = Fp6::new(a0, a1, a2); 79 let mut b = self.c1; 80 b.mul_by_01(c3, c4); 81 82 let c0 = *c0 + c3; 83 let c1 = c4; 84 let mut e = self.c0 + self.c1; 85 e.mul_by_01(&c0, c1); 86 self.c1 = e - (a + b); 87 self.c0 = a + Self::mul_fp6_by_nonresidue(&b); 88 } 89 90 pub fn mul_by_014(&mut self, c0: &Fp2<Fp2Params<P>>, c1: &Fp2<Fp2Params<P>>, c4: &Fp2<Fp2Params<P>>) { 91 let mut aa = self.c0; 92 aa.mul_by_01(c0, c1); 93 let mut bb = self.c1; 94 bb.mul_by_1(c4); 95 let mut o = *c1; 96 o.add_assign(c4); 97 self.c1.add_assign(self.c0); 98 self.c1.mul_by_01(c0, &o); 99 self.c1.sub_assign(&aa); 100 self.c1.sub_assign(&bb); 101 self.c0 = bb; 102 self.c0 = Self::mul_fp6_by_nonresidue(&self.c0); 103 self.c0.add_assign(aa); 104 } 105 106 pub fn cyclotomic_square(&self) -> Self { 107 let mut result = Self::zero(); 108 let fp2_nr = <P::Fp6Params as Fp6Parameters>::mul_fp2_by_nonresidue; 109 110 let mut z0 = self.c0.c0; 111 let mut z4 = self.c0.c1; 112 let mut z3 = self.c0.c2; 113 let mut z2 = self.c1.c0; 114 let mut z1 = self.c1.c1; 115 let mut z5 = self.c1.c2; 116 117 // t0 + t1*y = (z0 + z1*y)^2 = a^2 118 let mut tmp = z0 * z1; 119 let t0 = (z0 + z1) * (z0 + fp2_nr(&z1)) - tmp - fp2_nr(&tmp); 120 let t1 = tmp.double(); 121 122 // t2 + t3*y = (z2 + z3*y)^2 = b^2 123 tmp = z2 * z3; 124 let t2 = (z2 + z3) * (z2 + fp2_nr(&z3)) - tmp - fp2_nr(&tmp); 125 let t3 = tmp.double(); 126 127 // t4 + t5*y = (z4 + z5*y)^2 = c^2 128 tmp = z4 * z5; 129 let t4 = (z4 + z5) * (z4 + fp2_nr(&z5)) - tmp - fp2_nr(&tmp); 130 let t5 = tmp.double(); 131 132 // for A 133 134 // z0 = 3 * t0 - 2 * z0 135 z0 = t0 - z0; 136 z0 = z0 + z0; 137 result.c0.c0 = z0 + t0; 138 139 // z1 = 3 * t1 + 2 * z1 140 z1 = t1 + z1; 141 z1 = z1 + z1; 142 result.c1.c1 = z1 + t1; 143 144 // for B 145 146 // z2 = 3 * (xi * t5) + 2 * z2 147 tmp = fp2_nr(&t5); 148 z2 = tmp + z2; 149 z2 = z2 + z2; 150 result.c1.c0 = z2 + tmp; 151 152 // z3 = 3 * t4 - 2 * z3 153 z3 = t4 - z3; 154 z3 = z3 + z3; 155 result.c0.c2 = z3 + t4; 156 157 // for C 158 159 // z4 = 3 * t2 - 2 * z4 160 z4 = t2 - z4; 161 z4 = z4 + z4; 162 result.c0.c1 = z4 + t2; 163 164 // z5 = 3 * t3 + 2 * z5 165 z5 = t3 + z5; 166 z5 = z5 + z5; 167 result.c1.c2 = z5 + t3; 168 169 result 170 } 171 172 pub fn cyclotomic_exp<S: AsRef<[u64]>>(&self, exp: S) -> Self { 173 let mut res = Self::one(); 174 175 let mut found_one = false; 176 177 for i in BitIteratorBE::new(exp) { 178 if !found_one { 179 if i { 180 found_one = true; 181 } else { 182 continue; 183 } 184 } 185 186 res = res.cyclotomic_square(); 187 188 if i { 189 res *= self; 190 } 191 } 192 res 193 } 194 } 195 196 impl<P: Fp12Parameters> std::fmt::Display for Fp12<P> { 197 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { 198 write!(f, "Fp12({} + {} * w)", self.c0, self.c1) 199 } 200 } 201 202 impl<P: Fp12Parameters> Distribution<Fp12<P>> for Standard { 203 #[inline] 204 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Fp12<P> { 205 Fp12::new(Uniform::rand(rng), Uniform::rand(rng)) 206 } 207 } 208 209 impl<P: Fp12Parameters> Zero for Fp12<P> { 210 fn zero() -> Self { 211 Self::new(Fp6::zero(), Fp6::zero()) 212 } 213 214 fn is_zero(&self) -> bool { 215 self.c0.is_zero() && self.c1.is_zero() 216 } 217 } 218 219 impl<P: Fp12Parameters> One for Fp12<P> { 220 fn one() -> Self { 221 Self::new(Fp6::one(), Fp6::zero()) 222 } 223 224 fn is_one(&self) -> bool { 225 self.c0.is_one() && self.c1.is_zero() 226 } 227 } 228 229 impl<P: Fp12Parameters> Field for Fp12<P> { 230 type BasePrimeField = <Fp6<P::Fp6Params> as Field>::BasePrimeField; 231 232 fn from_base_prime_field(other: Self::BasePrimeField) -> Self { 233 Self::new(Fp6::from_base_prime_field(other), Fp6::zero()) 234 } 235 236 #[inline] 237 fn characteristic<'a>() -> &'a [u64] { 238 Fp6::<P::Fp6Params>::characteristic() 239 } 240 241 fn double(&self) -> Self { 242 let mut copy = *self; 243 copy.double_in_place(); 244 copy 245 } 246 247 #[inline] 248 fn from_random_bytes_with_flags<F: Flags>(bytes: &[u8]) -> Option<(Self, F)> { 249 let split_at = bytes.len() / 2; 250 if let Some(c0) = Fp6::<P::Fp6Params>::from_random_bytes(&bytes[..split_at]) { 251 if let Some((c1, flags)) = Fp6::<P::Fp6Params>::from_random_bytes_with_flags::<F>(&bytes[split_at..]) { 252 return Some((Fp12::new(c0, c1), flags)); 253 } 254 } 255 None 256 } 257 258 #[inline] 259 fn from_random_bytes(bytes: &[u8]) -> Option<Self> { 260 Self::from_random_bytes_with_flags::<EmptyFlags>(bytes).map(|f| f.0) 261 } 262 263 fn double_in_place(&mut self) { 264 self.c0.double_in_place(); 265 self.c1.double_in_place(); 266 } 267 268 fn frobenius_map(&mut self, power: usize) { 269 self.c0.frobenius_map(power); 270 self.c1.frobenius_map(power); 271 272 self.c1.c0.mul_assign(&P::FROBENIUS_COEFF_FP12_C1[power % 12]); 273 self.c1.c1.mul_assign(&P::FROBENIUS_COEFF_FP12_C1[power % 12]); 274 self.c1.c2.mul_assign(&P::FROBENIUS_COEFF_FP12_C1[power % 12]); 275 } 276 277 fn square(&self) -> Self { 278 let mut copy = *self; 279 copy.square_in_place(); 280 copy 281 } 282 283 fn square_in_place(&mut self) -> &mut Self { 284 let mut ab = self.c0; 285 ab.mul_assign(&self.c1); 286 let mut c0c1 = self.c0; 287 c0c1.add_assign(self.c1); 288 let mut c0 = self.c1; 289 c0 = Self::mul_fp6_by_nonresidue(&c0); 290 c0.add_assign(self.c0); 291 c0.mul_assign(&c0c1); 292 c0.sub_assign(&ab); 293 self.c1 = ab; 294 self.c1.add_assign(ab); 295 ab = Self::mul_fp6_by_nonresidue(&ab); 296 c0.sub_assign(&ab); 297 self.c0 = c0; 298 self 299 } 300 301 fn inverse(&self) -> Option<Self> { 302 if self.is_zero() { 303 None 304 } else { 305 let mut c0s = self.c0; 306 c0s.square_in_place(); 307 let mut c1s = self.c1; 308 c1s.square_in_place(); 309 c1s = Self::mul_fp6_by_nonresidue(&c1s); 310 c0s.sub_assign(&c1s); 311 312 c0s.inverse().map(|t| { 313 let mut tmp = Fp12::new(t, t); 314 tmp.c0.mul_assign(&self.c0); 315 tmp.c1.mul_assign(&self.c1); 316 tmp.c1 = -tmp.c1; 317 318 tmp 319 }) 320 } 321 } 322 323 fn inverse_in_place(&mut self) -> Option<&mut Self> { 324 match self.inverse() { 325 Some(inv) => { 326 *self = inv; 327 Some(self) 328 } 329 None => None, 330 } 331 } 332 } 333 334 impl<P: Fp12Parameters> Neg for Fp12<P> { 335 type Output = Self; 336 337 #[inline] 338 fn neg(self) -> Self { 339 let mut copy = Self::zero(); 340 copy.c0 = self.c0.neg(); 341 copy.c1 = self.c1.neg(); 342 copy 343 } 344 } 345 346 impl_add_sub_from_field_ref!(Fp12, Fp12Parameters); 347 impl_mul_div_from_field_ref!(Fp12, Fp12Parameters); 348 349 impl<P: Fp12Parameters> Add<&'_ Self> for Fp12<P> { 350 type Output = Self; 351 352 #[inline] 353 fn add(self, other: &Self) -> Self { 354 let mut result = self; 355 result.add_assign(other); 356 result 357 } 358 } 359 360 impl<P: Fp12Parameters> Sub<&'_ Self> for Fp12<P> { 361 type Output = Self; 362 363 #[inline] 364 fn sub(self, other: &Self) -> Self { 365 let mut result = self; 366 result.sub_assign(&other); 367 result 368 } 369 } 370 371 impl<P: Fp12Parameters> Mul<&'_ Self> for Fp12<P> { 372 type Output = Self; 373 374 #[inline] 375 fn mul(self, other: &Self) -> Self { 376 let mut result = self; 377 result.mul_assign(&other); 378 result 379 } 380 } 381 382 impl<P: Fp12Parameters> Div<&'_ Self> for Fp12<P> { 383 type Output = Self; 384 385 #[inline] 386 fn div(self, other: &Self) -> Self { 387 let mut result = self; 388 result.mul_assign(&other.inverse().unwrap()); 389 result 390 } 391 } 392 393 impl<P: Fp12Parameters> AddAssign<&'_ Self> for Fp12<P> { 394 #[inline] 395 fn add_assign(&mut self, other: &Self) { 396 self.c0.add_assign(other.c0); 397 self.c1.add_assign(other.c1); 398 } 399 } 400 401 impl<P: Fp12Parameters> SubAssign<&'_ Self> for Fp12<P> { 402 #[inline] 403 fn sub_assign(&mut self, other: &Self) { 404 self.c0.sub_assign(&other.c0); 405 self.c1.sub_assign(&other.c1); 406 } 407 } 408 409 impl<P: Fp12Parameters> MulAssign<&'_ Self> for Fp12<P> { 410 #[inline] 411 #[allow(clippy::suspicious_op_assign_impl)] 412 fn mul_assign(&mut self, other: &Self) { 413 let v0 = self.c0 * other.c0; 414 let v1 = self.c1 * other.c1; 415 self.c1 = (self.c0 + self.c1) * (other.c0 + other.c1) - v0 - v1; 416 self.c0 = v0 + Self::mul_fp6_by_nonresidue(&v1); 417 } 418 } 419 420 impl<P: Fp12Parameters> DivAssign<&'_ Self> for Fp12<P> { 421 #[inline] 422 fn div_assign(&mut self, other: &Self) { 423 self.mul_assign(&other.inverse().unwrap()); 424 } 425 } 426 427 impl<P: Fp12Parameters> Ord for Fp12<P> { 428 #[inline(always)] 429 fn cmp(&self, other: &Self) -> Ordering { 430 let c1_cmp = self.c1.cmp(&other.c1); 431 if c1_cmp == Ordering::Equal { self.c0.cmp(&other.c0) } else { c1_cmp } 432 } 433 } 434 435 impl<P: Fp12Parameters> PartialOrd for Fp12<P> { 436 #[inline(always)] 437 fn partial_cmp(&self, other: &Self) -> Option<Ordering> { 438 Some(self.cmp(other)) 439 } 440 } 441 442 impl<P: Fp12Parameters> From<u128> for Fp12<P> { 443 fn from(other: u128) -> Self { 444 Self::new(other.into(), Fp6::zero()) 445 } 446 } 447 448 impl<P: Fp12Parameters> From<u64> for Fp12<P> { 449 fn from(other: u64) -> Self { 450 Self::new(other.into(), Fp6::zero()) 451 } 452 } 453 454 impl<P: Fp12Parameters> From<u32> for Fp12<P> { 455 fn from(other: u32) -> Self { 456 Self::new(other.into(), Fp6::zero()) 457 } 458 } 459 460 impl<P: Fp12Parameters> From<u16> for Fp12<P> { 461 fn from(other: u16) -> Self { 462 Self::new(other.into(), Fp6::zero()) 463 } 464 } 465 466 impl<P: Fp12Parameters> From<u8> for Fp12<P> { 467 fn from(other: u8) -> Self { 468 Self::new(other.into(), Fp6::zero()) 469 } 470 } 471 472 impl<P: Fp12Parameters> ToBits for Fp12<P> { 473 fn write_bits_le(&self, vec: &mut Vec<bool>) { 474 self.c0.write_bits_le(vec); 475 self.c1.write_bits_le(vec); 476 } 477 478 fn write_bits_be(&self, vec: &mut Vec<bool>) { 479 self.c0.write_bits_be(vec); 480 self.c1.write_bits_be(vec); 481 } 482 } 483 484 impl<P: Fp12Parameters> ToBytes for Fp12<P> { 485 #[inline] 486 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> { 487 self.c0.write_le(&mut writer)?; 488 self.c1.write_le(&mut writer) 489 } 490 } 491 492 impl<P: Fp12Parameters> FromBytes for Fp12<P> { 493 #[inline] 494 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> { 495 let c0 = Fp6::read_le(&mut reader)?; 496 let c1 = Fp6::read_le(&mut reader)?; 497 Ok(Fp12::new(c0, c1)) 498 } 499 } 500 501 impl<P: Fp12Parameters> CanonicalSerializeWithFlags for Fp12<P> { 502 #[inline] 503 fn serialize_with_flags<W: Write, F: Flags>(&self, mut writer: W, flags: F) -> Result<(), SerializationError> { 504 self.c0.serialize_uncompressed(&mut writer)?; 505 self.c1.serialize_with_flags(&mut writer, flags)?; 506 Ok(()) 507 } 508 509 fn serialized_size_with_flags<F: Flags>(&self) -> usize { 510 self.c0.uncompressed_size() + self.c1.serialized_size_with_flags::<F>() 511 } 512 } 513 514 impl<P: Fp12Parameters> CanonicalSerialize for Fp12<P> { 515 #[inline] 516 fn serialize_with_mode<W: Write>(&self, writer: W, _compress: Compress) -> Result<(), SerializationError> { 517 self.serialize_with_flags(writer, EmptyFlags) 518 } 519 520 #[inline] 521 fn serialized_size(&self, compress: Compress) -> usize { 522 self.c0.serialized_size(compress) + self.c1.serialized_size(compress) 523 } 524 } 525 526 impl<P: Fp12Parameters> CanonicalDeserializeWithFlags for Fp12<P> { 527 #[inline] 528 fn deserialize_with_flags<R: Read, F: Flags>(mut reader: R) -> Result<(Self, F), SerializationError> { 529 let c0 = CanonicalDeserialize::deserialize_uncompressed(&mut reader)?; 530 let (c1, flags) = Fp6::deserialize_with_flags(&mut reader)?; 531 Ok((Self::new(c0, c1), flags)) 532 } 533 } 534 535 impl<P: Fp12Parameters> Valid for Fp12<P> { 536 fn check(&self) -> Result<(), deltavm_utilities::SerializationError> { 537 Ok(()) 538 } 539 540 fn batch_check<'a>(_batch: impl Iterator<Item = &'a Self>) -> Result<(), deltavm_utilities::SerializationError> 541 where 542 Self: 'a, 543 { 544 Ok(()) 545 } 546 } 547 548 impl<P: Fp12Parameters> CanonicalDeserialize for Fp12<P> { 549 #[inline] 550 fn deserialize_with_mode<R: Read>( 551 mut reader: R, 552 compress: Compress, 553 validate: Validate, 554 ) -> Result<Self, SerializationError> { 555 let c0 = CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; 556 let c1 = CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?; 557 Ok(Fp12::new(c0, c1)) 558 } 559 }