diff --git a/README.md b/README.md
index 09d4350..fe144ca 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,9 @@
-# ecrust
+
ecrust
+
+

+ |
+

+
@@ -25,7 +30,7 @@ isogeny ← isogeny and kernel abstractions (work in progress)
↓
ec ← elliptic-curve models and point arithmetic
↓
-fp ← finite fields: Fp, Fp^m, F2, F2^m
+fp ← finite fields: F_p, F_{p^m}, F_2, F_{2^m}
↓
crypto-bigint ← multi-precision integers / Montgomery arithmetic
```
diff --git a/fp/src/_doctest_support.rs b/fp/src/_doctest_support.rs
index b4503da..2b8be6b 100644
--- a/fp/src/_doctest_support.rs
+++ b/fp/src/_doctest_support.rs
@@ -65,3 +65,22 @@ pub mod _doctest_field_ops {
pub type F19_2 = FpExt;
}
+
+pub mod _doctest_f2_ext {
+ use crate::f2_ext::{BinaryIrreducible, F2Ext};
+ use crypto_bigint::Uint;
+
+ pub struct F4Poly;
+
+ impl BinaryIrreducible<1> for F4Poly {
+ fn modulus() -> Uint<1> {
+ Uint::<1>::from_u64(0b111) // x^2 + x + 1
+ }
+
+ fn degree() -> usize {
+ 2usize
+ }
+ }
+
+ pub type F4 = F2Ext<1, F4Poly>;
+}
diff --git a/fp/src/f2_element.rs b/fp/src/f2_element.rs
index 2acc063..28f54af 100644
--- a/fp/src/f2_element.rs
+++ b/fp/src/f2_element.rs
@@ -1,4 +1,18 @@
//! The binary field $\mathbb{F}_2 = \mathbb{Z} / 2\mathbb{Z}$
+//!
+//! # Examples
+//!
+//! ```
+//! use fp::f2_element::F2Element;
+//! use fp::field_ops::FieldOps;
+//! use crypto_bigint::Uint;
+//!
+//! let x = F2Element::from_u64(0);
+//! let y = F2Element::from_u64(1);
+//! assert!(bool::from(x.is_zero()));
+//! assert!(bool::from(y.is_one()));
+//! assert_eq!(F2Element::characteristic(), [2]);
+//! ```
use crate::field_ops::FieldFromRepr;
use crate::field_ops::{FieldOps, FieldRandom};
@@ -28,15 +42,6 @@ impl F2Element {
};
/// Create a new element of $\mathbb{F}_2$ from a `Uint<1>`
- ///
- /// # Arguments
- ///
- /// * `x` - An integer (type: `Uint<1>`)
- ///
- /// # Returns
- ///
- /// An element of $\mathbb{F}_2$, the reduction of `x` mod 2
- /// (type: `Self`)
fn new(x: Uint<1>) -> Self {
Self {
value: x & Uint::<1>::ONE,
@@ -53,11 +58,22 @@ impl F2Element {
///
/// An element of $\mathbb{F}_2$, the reduction of `x` mod 2
/// (type: `Self`)
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use fp::f2_element::F2Element;
+ /// # use fp::field_ops::FieldOps;
+ /// # use crypto_bigint::Uint;
+ /// let x = F2Element::from_u64(1);
+ /// let y = F2Element::from_u64(7);
+ /// assert_eq!(x, y);
+ /// ```
pub fn from_u64(x: u64) -> Self {
Self::new(Uint::from(x & 1))
}
- /// Get the `Uint<1>` from an element of $\mathbb{F}_2$
+ /// Get the `Uint<1>` from an element of $\mathbb{F}_2$ i.e.,
///
/// # Arguments
///
@@ -67,6 +83,15 @@ impl F2Element {
///
/// The integer in $\{ 0, 1 \}$ reducing to `x` mod 2 (type:
/// `Uint<1>`)
+ ///
+ /// ```
+ /// # use fp::f2_element::F2Element;
+ /// # use fp::field_ops::FieldOps;
+ /// # use crypto_bigint::Uint;
+ /// let x = F2Element::from_u64(7);
+ /// let my_int = x.value();
+ /// assert_eq!(my_int, Uint::<1>::from_u64(1));
+ /// ```
pub fn value(&self) -> Uint<1> {
self.value
}
@@ -80,7 +105,16 @@ impl F2Element {
/// # Returns
///
/// The integer in $\{ 0, 1 \}$ reducing to `x` mod 2 (type:
- /// `u8`)
+ /// `Uint<1>`)
+ ///
+ /// ```
+ /// # use fp::f2_element::F2Element;
+ /// # use fp::field_ops::FieldOps;
+ /// # use crypto_bigint::Uint;
+ /// let x = F2Element::from_u64(45);
+ /// let my_int = x.as_u8();
+ /// assert_eq!(my_int, 1_u8);
+ /// ```
pub fn as_u8(&self) -> u8 {
self.value.to_words()[0] as u8
}
diff --git a/fp/src/f2_ext.rs b/fp/src/f2_ext.rs
index 62cc15d..a00a818 100644
--- a/fp/src/f2_ext.rs
+++ b/fp/src/f2_ext.rs
@@ -1,4 +1,38 @@
//! Generic binary fields $\mathbb{F}\_{2^m} = \mathbb{F}\_2\[x\] / (f(x))$
+//!
+//! # Examples
+//!
+//! ```
+//! use crypto_bigint::Uint;
+//! use fp::f2_element::F2Element;
+//! use fp::f2_ext::{BinaryIrreducible, F2Ext};
+//! use fp::field_ops::FieldOps;
+//!
+//! /* Make the finite field F_4 */
+//! struct F4Poly;
+//! impl BinaryIrreducible<1> for F4Poly {
+//! fn modulus() -> Uint<1> {
+//! Uint::<1>::from_u64(0b111) // x^2 + x + 1
+//! }
+//!
+//! fn degree() -> usize {
+//! 2usize
+//! }
+//! }
+//! type F4 = F2Ext<1, F4Poly>;
+//!
+//! /* Elements are written down by binary integers */
+//! let zero = F4::from_u64(0);
+//! let one = F4::from_u64(1);
+//! let a = F4::from_u64(0b11);
+//! let b = F4::from_u64(0b10);
+//!
+//! let also_zero = F4::from_u64(0b111); // x^2 + x + 1 = 0
+//! let also_one = F4::from_u64(0b1000); // x^3 = x*x^2 = x^2 + x = 1
+//! assert_eq!(also_zero, zero);
+//! assert_eq!(also_one, one);
+//! assert_eq!(a.mul(&b), one);
+//! ```
use crate::field_ops::{FieldFromRepr, FieldOps, FieldRandom};
use core::ops::{Add, Mul, Neg, Sub};
@@ -21,9 +55,10 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
/// # Examples
///
/// ```
-/// # use crypto_bigint::Uint;
-/// # use fp::f2_element::F2Element;
-/// # use fp::f2_ext::{BinaryIrreducible, F2Ext};
+/// use crypto_bigint::Uint;
+/// use fp::f2_element::F2Element;
+/// use fp::f2_ext::{BinaryIrreducible, F2Ext};
+///
/// struct MyPoly;
///
/// impl BinaryIrreducible<1> for MyPoly {
@@ -63,11 +98,25 @@ pub trait BinaryIrreducible: 'static {
/// # Examples
///
/// ```
-/// # use crypto_bigint::Uint;
-/// # use fp::f2_element::F2Element;
-/// # use fp::f2_ext::{BinaryIrreducible, F2Ext};
-/// struct F2511Poly;
+/// use crypto_bigint::Uint;
+/// use fp::f2_element::F2Element;
+/// use fp::f2_ext::{BinaryIrreducible, F2Ext};
+///
+/// /* Make the finite field F_4 */
+/// struct F4Poly;
+/// impl BinaryIrreducible<1> for F4Poly {
+/// fn modulus() -> Uint<1> {
+/// Uint::<1>::from_u64(0b111) // x^2 + x + 1
+/// }
///
+/// fn degree() -> usize {
+/// 2usize
+/// }
+/// }
+/// type F4 = F2Ext<1, F4Poly>;
+///
+/// /* Make the finite field F_{2^511} */
+/// struct F2511Poly;
/// impl BinaryIrreducible<8> for F2511Poly {
/// fn modulus() -> Uint<8> {
/// let one = Uint::<8>::from_u64(1);
@@ -78,8 +127,7 @@ pub trait BinaryIrreducible: 'static {
/// 511
/// }
/// }
-///
-/// type GF2_511 = F2Ext<8, F2511Poly>;
+/// type F2_511 = F2Ext<8, F2511Poly>;
/// ```
pub struct F2Ext
where
@@ -98,30 +146,145 @@ impl F2Ext
where
P: BinaryIrreducible,
{
- /// Make an element from the limbs
- pub fn new(x: Uint) -> Self {
+ /// Make an element of the extension from the limbs
+ ///
+ /// # Arguments
+ ///
+ /// * `a` - An integer written in binary as $a_0 \dots a_{64 *
+ /// \texttt{LIMBS}}$ and padded with 0s if nessicary (type:
+ /// `Uint`).
+ ///
+ /// # Returns
+ ///
+ /// The element $\sum_{i=0}^M a_i x^i \in \mathbb{F}\_{2}\[x\] /
+ /// (f(x)) = \mathbb{F}\_{2^M}$ (type: `Self`).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use fp::_doctest_support::_doctest_f2_ext::*;
+ /// # use crypto_bigint::Uint;
+ /// # use fp::field_ops::FieldOps;
+ /// let a = F4::new(Uint::<1>::from_u64(0b111)); // x^2 + x + 1 should be zero
+ /// let b = F4::new(Uint::<1>::from_u64(0b11)); // x + 1 = x^2
+ /// let c = F4::new(Uint::<1>::from_u64(0b100)); // x^2 = x + 1
+ /// let d = F4::new(Uint::<1>::from_u64(0b1000)); // x^3 = x^2 * x = x^2 + x = 1
+ /// assert!(bool::from(a.is_zero()));
+ /// assert_eq!(b, c);
+ /// assert!(bool::from(d.is_one()));
+ /// ```
+ pub fn new(a: Uint) -> Self {
Self {
- value: reduce::(x),
+ value: reduce::(a),
_phantom: PhantomData,
}
}
- /// Make an element from a `u64`
+ /// Make an element of the extension from a `u64`
+ ///
+ /// # Arguments
+ ///
+ /// * `a` - An integer written in binary as $a_0 \dots a_{64}$ and
+ /// padded with 0s to 64 bits if nessicary (type: `u64`).
+ ///
+ /// # Returns
+ ///
+ /// The element $\sum_{i=0}^{64} a_i x^i \in \mathbb{F}\_{2}\[x\] /
+ /// (f(x)) = \mathbb{F}\_{2^M}$ (type: `Self`).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use fp::_doctest_support::_doctest_f2_ext::*;
+ /// # use crypto_bigint::Uint;
+ /// # use fp::field_ops::FieldOps;
+ /// let a = F4::from_u64(0b111); // x^2 + x + 1 should be zero
+ /// let b = F4::from_u64(0b11); // x + 1 should be x^2
+ /// let c = F4::from_u64(0b100); // x + 1 should be x^2
+ /// let d = F4::from_u64(0b1000); // x^3 = x^2 * x = x^2 + x = 1
+ /// assert!(bool::from(a.is_zero()));
+ /// assert_eq!(b, c);
+ /// assert!(bool::from(d.is_one()));
+ /// ```
pub fn from_u64(x: u64) -> Self {
Self::new(Uint::from(x))
}
- /// Make an element from a `Uint`
+ /// Make an element of the extension from the limbs
+ ///
+ /// # Arguments
+ ///
+ /// * `a` - An integer written in binary as $a_0 a_1 a_2 \dots
+ /// a_{M}$ and then padded to the nearest 64 bits (type:
+ /// `Uint`).
+ ///
+ /// # Returns
+ ///
+ /// The element $\sum_{i=0}^M a_i x^i \in \mathbb{F}\_{2}\[x\] /
+ /// (f(x)) = \mathbb{F}\_{2^M}$ (type: `Self`).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use fp::_doctest_support::_doctest_f2_ext::*;
+ /// # use crypto_bigint::Uint;
+ /// # use fp::field_ops::FieldOps;
+ /// let a = F4::from_uint(Uint::<1>::from_u64(0b111)); // x^2 + x + 1 should be zero
+ /// let b = F4::from_uint(Uint::<1>::from_u64(0b11)); // x + 1 = x^2
+ /// let c = F4::from_uint(Uint::<1>::from_u64(0b100)); // x^2 = x + 1
+ /// let d = F4::from_uint(Uint::<1>::from_u64(0b1000)); // x^3 = x^2 * x = x^2 + x = 1
+ /// assert!(bool::from(a.is_zero()));
+ /// assert_eq!(b, c);
+ /// assert!(bool::from(d.is_one()));
+ /// ```
+ ///
+ /// # Note
+ ///
+ /// This is just an alias for [`F2Ext::new`]
pub fn from_uint(x: Uint) -> Self {
Self::new(x)
}
- /// Get a `Unit` from an element
+ /// Get a `Uint` from an element, inverting [`F2Ext::new`].
+ ///
+ /// # Returns
+ ///
+ /// The integer $a$ of at most $M$ bits such that applying `new`
+ /// gives `self`. That is, if $a$ is written in binary as $a_0
+ /// \dots a_M$ then $\texttt{self} = \sum_{i=0}^M a_i x^i \in
+ /// \mathbb{F}\_{2}\[x\] / (f(x)) = \mathbb{F}\_{2^M}$ (type:
+ /// `Uint`).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use fp::_doctest_support::_doctest_f2_ext::*;
+ /// # use crypto_bigint::Uint;
+ /// # use fp::field_ops::FieldOps;
+ /// let a = F4::from_uint(Uint::<1>::from_u64(0b11)); // x + 1
+ /// let b = F4::from_uint(Uint::<1>::from_u64(0b1000)); // x^3 = x^2 * x = x^2 + x = 1
+ /// assert_eq!(a.as_uint(), Uint::<1>::from_u64(0b11));
+ /// assert_eq!(b.as_uint(), Uint::<1>::from_u64(0b1));
+ /// ```
pub fn as_uint(&self) -> Uint {
self.value
}
/// Get the degree of the field extension
+ ///
+ /// # Returns
+ ///
+ /// The degree of the field extension (type: `usize`).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use fp::_doctest_support::_doctest_f2_ext::*;
+ /// # use crypto_bigint::Uint;
+ /// # use fp::field_ops::FieldOps;
+ /// let d = F4::degree();
+ /// assert_eq!(d, 2_usize);
+ /// ```
pub fn degree() -> usize {
P::degree()
}
diff --git a/fp/src/field_ops.rs b/fp/src/field_ops.rs
index f78d95e..bac7ec1 100644
--- a/fp/src/field_ops.rs
+++ b/fp/src/field_ops.rs
@@ -7,8 +7,49 @@ use subtle::{Choice, ConditionallySelectable, CtOption};
///
/// Separated from [`FieldOps`] so that downstream code that doesn't
/// need randomness is free of the `rand` dependency.
+///
+/// # Required Methods
+///
+/// * `random` - Generate a random field element.
+///
+/// # Examples
+///
+/// ```
+/// use crypto_bigint::{const_prime_monty_params, Uint};
+/// use fp::fp_element::FpElement;
+/// use fp::field_ops::{FieldOps, FieldRandom};
+///
+/// /*
+/// We will set up the field F_19 using FpElement, see the docs
+/// of fp_element::FpElement.
+/// */
+///
+/// const_prime_monty_params!(Fp19Modulus, Uint<1>, "0000000000000013", 2);
+/// type F19 = FpElement;
+///
+/// let mut rng = rand::rng();
+/// let a = F19::random(&mut rng);
+/// ```
pub trait FieldRandom: Sized {
- /// Sample a uniformly random element using a cryptographic RNG.
+ /// Sample a uniformly random element using a cryptographic random
+ /// number generator.
+ ///
+ /// # Arguments
+ ///
+ /// * `rng` a cryptographic rng (type: `&mut (impl rand::CryptoRng + rand::Rng)`)
+ ///
+ /// # Returns
+ ///
+ /// A random element of $\mathbb{F}\_{q}$ (type: `Self`)
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use fp::_doctest_support::_doctest_field_ops::*;
+ /// # use fp::field_ops::{FieldOps, FieldRandom};
+ /// let mut rng = rand::rng();
+ /// let a = F19::random(&mut rng);
+ /// ```
fn random(rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self;
}
diff --git a/fp/src/fp_element.rs b/fp/src/fp_element.rs
index 3e10a3b..e6bf528 100644
--- a/fp/src/fp_element.rs
+++ b/fp/src/fp_element.rs
@@ -408,7 +408,7 @@ impl FieldRandom for FpElement
where
MOD: ConstPrimeMontyParams,
{
- /// Sample a uniformly random element of Fp using a CSPRNG.
+ /// Sample a uniformly random element of $\mathbb{F}\_p$ using a CSPRNG.
fn random(rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self {
let minus_one = ConstMontyForm::::ZERO - ConstMontyForm::::ONE;
let p_minus_1: Uint = minus_one.retrieve();