hekate_math/lib.rs
1// SPDX-License-Identifier: Apache-2.0
2// This file is part of the hekate-math project.
3// Copyright (C) 2026 Andrei Kochergin <andrei@oumuamua.dev>
4// Copyright (C) 2026 Oumuamua Labs <info@oumuamua.dev>.
5//
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Binary tower fields GF(2^8) through GF(2^256), each with a
19//! second, polynomial basis for carry-less multiplication hardware.
20//!
21//! Every level is a quadratic extension of the one below:
22//! GF(2^(2m)) = GF(2^m)\[X\] / (X^2 + X + τ), τ the lower level's
23//! [`TowerField::EXTENSION_TAU`]. [`Block8`] is the AES field,
24//! GF(2)\[x\] / (x^8 + x^4 + x^3 + x + 1) (FIPS-197 §4.2), and [`Bit`]
25//! is GF(2) inside it. A level stores its `(lo, hi)` pair as one
26//! integer, `Block16(u16)` to `Block128(u128)`; `Block256` holds
27//! `[u128; 2]`.
28//!
29//! # Two bases
30//!
31//! Tower elements have type `F`. The same field in the flat basis
32//! has type [`Flat<F>`], and the two types never mix in arithmetic.
33//! [`HardwareField::to_hardware`] maps tower to flat and
34//! [`HardwareField::from_hardware`] maps back. The flat basis is
35//! GF(2)\[x\] modulo:
36//!
37//! | Field | Flat modulus |
38//! |:----------|:---------------------------|
39//! | GF(2^8) | x^8 + x^4 + x^3 + x + 1 |
40//! | GF(2^16) | x^16 + x^5 + x^3 + x + 1 |
41//! | GF(2^32) | x^32 + x^7 + x^3 + x^2 + 1 |
42//! | GF(2^64) | x^64 + x^4 + x^3 + x + 1 |
43//! | GF(2^128) | x^128 + x^7 + x^2 + x + 1 |
44//!
45//! `Flat<Block256>` extends the GF(2^128) flat basis by
46//! y^2 + y + φ(τ), φ the tower-to-flat map.
47//!
48//! [`PackableField`] groups `WIDTH` elements into one packed value and
49//! [`PackedFlat<F>`] is its flat form. [`FlatPromote`] embeds a flat
50//! subfield element into a flat superfield.
51//!
52//! # Timing
53//!
54//! By default the basis maps are bit-sliced matrix products: no
55//! branch or memory access depends on the value. Timing is not
56//! formally verified. The `table-math` feature swaps in lookup tables
57//! for the basis maps, the subfield lifts and GF(2^8) arithmetic;
58//! their memory access depends on the operands, for public data only.
59//!
60//! Flat multiplication from GF(2^32) up uses PMULL on aarch64 with
61//! the `aes` target feature, which `aarch64-unknown-linux-gnu` leaves
62//! off by default (`-C target-feature=+aes`). Other targets take a
63//! software path with the same results.
64//!
65//! # Features
66//!
67//! | Feature | Default | Effect |
68//! |:-------------|:--------|:--------------------------------------|
69//! | `std` | yes | without it: `no_std` with `alloc` |
70//! | `parallel` | yes | Rayon for FFT buffers of 1 MiB and up |
71//! | `table-math` | no | variable-time lookup tables, above |
72//!
73//! # Contents
74//!
75//! [`fft`] holds the additive FFT over the Cantor basis
76//! ([`AdditiveFft`], [`CantorBasis`]) and a systematic Reed-Solomon
77//! encoder ([`ReedSolomon`]). [`BinaryFieldExtras`] adds square,
78//! Frobenius, trace and a root of x^2 + x = c, for Block16 through
79//! Block256.
80//!
81//! # Examples
82//!
83//! ## Isomorphic workflow
84//!
85//! ```
86//! use hekate_math::{Block128, HardwareField, TowerField};
87//!
88//! let a = Block128::from_uniform_bytes(&[0xaa; 32]);
89//! let b = Block128::from_uniform_bytes(&[0xbb; 32]);
90//!
91//! let product = (a.to_hardware() * b.to_hardware()).to_tower();
92//! assert_eq!(product, a * b);
93//! ```
94//!
95//! ## SIMD vectorization
96//!
97//! ```
98//! use hekate_math::{Block32, Flat, HardwareField, PackableField};
99//!
100//! let data: Vec<Flat<Block32>> = (1..=8u32)
101//! .map(|i| Block32::from(i).to_hardware())
102//! .collect();
103//!
104//! let a = Flat::<Block32>::pack(&data[..4]);
105//! let b = Flat::<Block32>::pack(&data[4..]);
106//!
107//! let mut out = [Flat::<Block32>::default(); 4];
108//! Flat::<Block32>::unpack(a * b, &mut out);
109//!
110//! for (i, lane) in out.iter().enumerate() {
111//! let expected = data[i].to_tower() * data[4 + i].to_tower();
112//! assert_eq!(lane.to_tower(), expected);
113//! }
114//! ```
115//!
116//! ## Additive FFT
117//!
118//! ```
119//! use hekate_math::{AdditiveFft, Block16, Flat, HardwareField};
120//!
121//! let log_n = 10;
122//! let fft = AdditiveFft::<Block16>::new(log_n)?;
123//!
124//! let coeffs: Vec<Flat<Block16>> = (0..1u32 << log_n)
125//! .map(|i| Block16::from(i).to_hardware())
126//! .collect();
127//!
128//! let mut data = coeffs.clone();
129//! fft.forward_scalar(&mut data)?;
130//! fft.inverse_scalar(&mut data)?;
131//!
132//! assert_eq!(data, coeffs);
133//! # Ok::<(), hekate_math::FftError>(())
134//! ```
135//!
136//! # Formal verification
137//!
138//! Tower `mul` and `invert`, the NEON kernels, the constant-time
139//! basis maps and the additive FFT have Verus proofs against a
140//! GF(2^k) model, kept outside the crate build in [`verus/`][verus].
141//!
142//! [verus]: https://github.com/oumuamua-labs/hekate-math/tree/main/verus
143
144#![cfg_attr(not(feature = "std"), no_std)]
145#![warn(missing_docs)]
146
147extern crate alloc;
148
149mod algebra;
150mod field;
151mod towers;
152
153mod constants;
154mod hardware;
155mod packable;
156
157pub mod fft;
158
159pub use algebra::BinaryFieldExtras;
160pub use fft::{AdditiveFft, CantorBasis, CantorError, FftError, ReedSolomon, RsError};
161pub use field::*;
162pub use hardware::{Flat, FlatPromote, HardwareField};
163pub use packable::{PackableField, PackedFlat};
164pub use towers::*;