risc0_zkp_core/
fp4.rs

1// Copyright 2022 Risc0, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Support for the rank 4 extension field of the base field.
16
17use core::ops;
18
19use bytemuck::{Pod, Zeroable};
20use rand::Rng;
21
22use crate::{
23    fp::{Fp, FpMul, P},
24    Random,
25};
26
27const BETA: Fp = Fp::new(11);
28const NBETA: Fp = Fp::new(P - 11);
29
30/// The size of the extension field in elements, 4 in this case.
31pub const EXT_SIZE: usize = 4;
32
33/// Instances of `Fp4` are elements of a finite field `F_p^4`. They are
34/// represented as elements of `F_p[X] / (X^4 - 11)`. Basically, this is a *big*
35/// finite field (about `2^128` elements), which is used when the security of
36/// various operations depends on the size of the field. It has the field
37/// `Fp` as a subfield, which means operations by the two are compatable, which
38/// is important. The irreducible polynomial was choosen to be the most simple
39/// possible one, `x^4 - B`, where `11` is the smallest `B` which makes the
40/// polynomial irreducable.
41#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Zeroable, Pod)]
42#[repr(transparent)]
43pub struct Fp4([Fp; EXT_SIZE]);
44
45impl Fp4 {
46    /// Explicitly construct an Fp4 from parts.
47    pub fn new(x0: Fp, x1: Fp, x2: Fp, x3: Fp) -> Self {
48        Self([x0, x1, x2, x3])
49    }
50
51    /// Create a [Fp4] from a [Fp].
52    pub fn from_fp(x: Fp) -> Self {
53        Self([x, Fp::new(0), Fp::new(0), Fp::new(0)])
54    }
55
56    /// Create a [Fp4] from a raw integer.
57    pub fn from_u32(x0: u32) -> Self {
58        Self([Fp::new(x0), Fp::new(0), Fp::new(0), Fp::new(0)])
59    }
60
61    /// Returns the value zero.
62    pub fn zero() -> Self {
63        Self::from_u32(0)
64    }
65
66    /// Returns the value one.
67    pub fn one() -> Self {
68        Self::from_u32(1)
69    }
70
71    /// Returns the constant portion of a [Fp].
72    pub fn const_part(self) -> Fp {
73        self.0[0]
74    }
75
76    /// Returns the elements of a [Fp].
77    pub fn elems(&self) -> &[Fp] {
78        &self.0
79    }
80
81    /// Raise a [Fp4] to a power of `n`.
82    pub fn pow(self, n: usize) -> Self {
83        let mut n = n;
84        let mut tot = Fp4::from(1);
85        let mut x = self;
86        while n != 0 {
87            if n % 2 == 1 {
88                tot *= x;
89            }
90            n = n / 2;
91            x *= x;
92        }
93        tot
94    }
95
96    /// Compute the multiplicative inverse of an `Fp4`.
97    pub fn inv(self) -> Self {
98        let a = &self.0;
99        // Compute the multiplicative inverse by looking at `Fp4` as a composite field
100        // and using the same basic methods used to invert complex numbers. We imagine
101        // that initially we have a numerator of `1`, and a denominator of `a`.
102        // `out = 1 / a`; We set `a'` to be a with the first and third components
103        // negated. We then multiply the numerator and the denominator by `a'`,
104        // producing `out = a' / (a * a')`. By construction `(a * a')` has `0`s
105        // in its first and third elements. We call this number, `b` and compute
106        // it as follows.
107        let mut b0 = a[0] * a[0] + BETA * (a[1] * (a[3] + a[3]) - a[2] * a[2]);
108        let mut b2 = a[0] * (a[2] + a[2]) - a[1] * a[1] + BETA * (a[3] * a[3]);
109        // Now, we make `b'` by inverting `b2`. When we muliply both sizes by `b'`, we
110        // get `out = (a' * b') / (b * b')`.  But by construction `b * b'` is in
111        // fact an element of `Fp`, call it `c`.
112        let c = b0 * b0 + BETA * b2 * b2;
113        // But we can now invert `C` direcly, and multiply by `a' * b'`:
114        // `out = a' * b' * inv(c)`
115        let ic = c.inv();
116        // Note: if c == 0 (really should only happen if in == 0), our 'safe' version of
117        // inverse results in ic == 0, and thus out = 0, so we have the same 'safe'
118        // behavior for Fp4.  Oh, and since we want to multiply everything by ic, it's
119        // slightly faster to pre-multiply the two parts of b by ic (2 multiplies
120        // instead of 4).
121        b0 *= ic;
122        b2 *= ic;
123        Fp4([
124            a[0] * b0 + BETA * a[2] * b2,
125            -a[1] * b0 + NBETA * a[3] * b2,
126            -a[0] * b2 + a[2] * b0,
127            a[1] * b2 - a[3] * b0,
128        ])
129    }
130}
131
132impl FpMul for Fp4 {
133    fn fp_mul(self, x: Fp) -> Self {
134        self * x
135    }
136}
137
138impl Random for Fp4 {
139    /// Generate a random field element uniformly.
140    fn random<R: Rng>(rng: &mut R) -> Self {
141        Self([
142            Fp::random(rng),
143            Fp::random(rng),
144            Fp::random(rng),
145            Fp::random(rng),
146        ])
147    }
148}
149
150impl ops::Add for Fp4 {
151    type Output = Self;
152    fn add(self, rhs: Self) -> Self {
153        let mut lhs = self;
154        lhs += rhs;
155        lhs
156    }
157}
158
159impl ops::AddAssign for Fp4 {
160    fn add_assign(&mut self, rhs: Self) {
161        for i in 0..self.0.len() {
162            self.0[i] += rhs.0[i];
163        }
164    }
165}
166
167impl ops::Sub for Fp4 {
168    type Output = Self;
169    fn sub(self, rhs: Self) -> Self {
170        let mut lhs = self;
171        lhs -= rhs;
172        lhs
173    }
174}
175
176impl ops::SubAssign for Fp4 {
177    fn sub_assign(&mut self, rhs: Self) {
178        for i in 0..self.0.len() {
179            self.0[i] -= rhs.0[i];
180        }
181    }
182}
183
184/// Implement the simple multiplication case by the subfield Fp.
185impl ops::MulAssign<Fp> for Fp4 {
186    fn mul_assign(&mut self, rhs: Fp) {
187        for i in 0..self.0.len() {
188            self.0[i] *= rhs;
189        }
190    }
191}
192
193impl ops::Mul<Fp> for Fp4 {
194    type Output = Self;
195    fn mul(self, rhs: Fp) -> Self {
196        let mut lhs = self;
197        lhs *= rhs;
198        lhs
199    }
200}
201
202impl ops::Mul<Fp4> for Fp {
203    type Output = Fp4;
204    fn mul(self, rhs: Fp4) -> Fp4 {
205        rhs * self
206    }
207}
208
209// Now we get to the interesting case of multiplication. Basically, multiply
210// out the polynomial representations, and then reduce module `x^4 - B`, which
211// means powers >= 4 get shifted back 4 and multiplied by `-beta`. We could
212// write this as a double loops with some `if`s and hope it gets unrolled
213// properly, but it's small enough to just hand write.
214impl ops::MulAssign for Fp4 {
215    fn mul_assign(&mut self, rhs: Self) {
216        // Rename the element arrays to something small for readability.
217        let a = &self.0;
218        let b = &rhs.0;
219        self.0 = [
220            a[0] * b[0] + NBETA * (a[1] * b[3] + a[2] * b[2] + a[3] * b[1]),
221            a[0] * b[1] + a[1] * b[0] + NBETA * (a[2] * b[3] + a[3] * b[2]),
222            a[0] * b[2] + a[1] * b[1] + a[2] * b[0] + NBETA * (a[3] * b[3]),
223            a[0] * b[3] + a[1] * b[2] + a[2] * b[1] + a[3] * b[0],
224        ];
225    }
226}
227
228impl ops::Mul for Fp4 {
229    type Output = Fp4;
230    fn mul(self, rhs: Fp4) -> Fp4 {
231        let mut lhs = self;
232        lhs *= rhs;
233        lhs
234    }
235}
236
237impl ops::Neg for Fp4 {
238    type Output = Self;
239    fn neg(self) -> Self {
240        Fp4::default() - self
241    }
242}
243
244impl From<u32> for Fp4 {
245    fn from(x: u32) -> Self {
246        Self([Fp::from(x), Fp::default(), Fp::default(), Fp::default()])
247    }
248}
249
250impl From<Fp> for Fp4 {
251    fn from(x: Fp) -> Self {
252        Self([x, Fp::default(), Fp::default(), Fp::default()])
253    }
254}
255
256#[cfg(test)]
257mod tests {
258    use super::Fp4;
259    use crate::Random;
260    use rand::SeedableRng;
261
262    #[test]
263    fn isa_field() {
264        let mut rng = rand::rngs::SmallRng::seed_from_u64(2);
265        // Pick random sets of 3 elements of Fp4, and verify they meet the requirements
266        // of a field.
267        for _ in 0..1_000 {
268            let a = Fp4::random(&mut rng);
269            let b = Fp4::random(&mut rng);
270            let c = Fp4::random(&mut rng);
271            // Addition + multiplication commute
272            assert_eq!(a + b, b + a);
273            assert_eq!(a * b, b * a);
274            // Addition + multiplication are associative
275            assert_eq!(a + (b + c), (a + b) + c);
276            assert_eq!(a * (b * c), (a * b) * c);
277            // Distributive property
278            assert_eq!(a * (b + c), a * b + a * c);
279            // Inverses
280            if a != Fp4::default() {
281                assert_eq!(a.inv() * a, Fp4::from(1));
282            }
283            assert_eq!(Fp4::default() - a, -a);
284            assert_eq!(a + (-a), Fp4::default());
285        }
286    }
287}