Skip to content

Commit

Permalink
feat: introduce GF(2^8) in field crate and use in aes impl
Browse files Browse the repository at this point in the history
  • Loading branch information
eightfilms committed Jul 8, 2024
1 parent 02c20ae commit dfdcd05
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 158 deletions.
52 changes: 19 additions & 33 deletions src/encryption/symmetric/aes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@
//! and decryption.
#![cfg_attr(not(doctest), doc = include_str!("./README.md"))]

use std::ops::{Mul, Rem};
use std::ops::Mul;

use itertools::Itertools;

use crate::field::{extension::AESFieldExtension, prime::AESField};

pub mod sbox;
#[cfg(test)] pub mod tests;

use super::SymmetricEncryption;
use crate::{
encryption::symmetric::aes::sbox::{INVERSE_SBOX, SBOX},
field::{binary_towers::BinaryField, FiniteField},
polynomial::{Monomial, Polynomial},
field::FiniteField,
};

/// A block in AES represents a 128-bit sized message data.
Expand Down Expand Up @@ -147,51 +148,36 @@ struct State([[u8; 4]; 4]);
/// This is defined on two bytes in two steps:
///
/// 1) The two polynomials that represent the bytes are multiplied as polynomials,
/// 2) The resulting polynomial is reduced modulo the following fixed polynomial:
/// 2) The resulting polynomial is reduced modulo the following fixed polynomial: m(x) = x^8 + x^4 +
/// x^3 + x + 1
///
/// m(x) = x^8 + x^4 + x^3 + x + 1
/// Note that you do not see this done here, this is implemented in [`AESFieldExtension`], within
/// the operation traits.
///
/// Note that in most AES implementations, this is done using "carry-less" multiplication -
/// to see how this works in more concretely in field arithmetic, this implementation uses an actual
/// polynomial implementation (a [`Polynomial`] of [`BinaryField`]s).
fn galois_multiplication(mut col: u8, mut multiplicant: u8) -> u8 {
fn galois_multiplication(mut col: u8, mut multiplicand: u8) -> u8 {
// Decompose bits into degree-7 polynomials.
let mut col_bits = [BinaryField::new(0); 8];
let mut mult_bits = [BinaryField::new(0); 8];
let mut col_bits: [AESField; 8] = [AESField::ZERO; 8];
let mut mult_bits: [AESField; 8] = [AESField::ZERO; 8];
for i in 0..8 {
col_bits[i] = BinaryField::new(col & 1);
mult_bits[i] = BinaryField::new(multiplicant & 1);
col_bits[i] = AESField::new((col & 1).into());
mult_bits[i] = AESField::new((multiplicand & 1).into());
col >>= 1;
multiplicant >>= 1;
multiplicand >>= 1;
}

let col_poly = Polynomial::<Monomial, BinaryField, 8>::new(col_bits);
let mult_poly = Polynomial::<Monomial, BinaryField, 8>::new(mult_bits);
// m(x) = x^8 + x^4 + x^3 + x + 1
let reducer = Polynomial::<Monomial, BinaryField, 9>::new([
BinaryField::ONE,
BinaryField::ONE,
BinaryField::ZERO,
BinaryField::ONE,
BinaryField::ONE,
BinaryField::ZERO,
BinaryField::ZERO,
BinaryField::ZERO,
BinaryField::ONE,
]);

let col_poly = AESFieldExtension::new(col_bits);
let mult_poly = AESFieldExtension::new(mult_bits);
let res = col_poly.mul(mult_poly);
let result = res.rem(reducer); // reduce resulting polynomial modulo a fixed polynomial

assert!(result.degree() < 8, "did not get a u8 out of multiplication in GF(2^8)");
// Recompose polynomial into a u8.
let mut fin: u8 = 0;
let mut product: u8 = 0;
for i in 0..8 {
let coeff: u8 = result.coefficients[i].into();
fin += coeff * (2_i16.pow(i as u32)) as u8;
product += res.coeffs[i].value as u8 * 2_u8.pow(i as u32);
}

fin
product
}

impl<const N: usize> AES<N>
Expand Down
4 changes: 0 additions & 4 deletions src/field/binary_towers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,6 @@ impl From<usize> for BinaryField {
}
}

impl From<BinaryField> for u8 {
fn from(value: BinaryField) -> u8 { value.0 }
}

impl Add for BinaryField {
type Output = Self;

Expand Down
123 changes: 2 additions & 121 deletions src/field/binary_towers/tests.rs
Original file line number Diff line number Diff line change
@@ -1,129 +1,10 @@
use std::array;

use rand::{thread_rng, Rng};
use rstest::rstest;

use super::*;
use crate::{
field::{extension::ExtensionField, prime::PrimeField, GaloisField},
polynomial::Monomial,
Polynomial,
};

pub type TestBinaryField = PrimeField<2>;
pub type TestBinaryExtensionField = GaloisField<8, 2>;

impl FiniteField for TestBinaryExtensionField {
const ONE: Self = Self::new([
TestBinaryField::ONE,
TestBinaryField::ZERO,
TestBinaryField::ZERO,
TestBinaryField::ZERO,
TestBinaryField::ZERO,
TestBinaryField::ZERO,
TestBinaryField::ZERO,
TestBinaryField::ZERO,
]);
const ORDER: usize = TestBinaryField::ORDER.pow(8);
const PRIMITIVE_ELEMENT: Self = Self::new([
TestBinaryField::ONE,
TestBinaryField::ONE,
TestBinaryField::ZERO,
TestBinaryField::ZERO,
TestBinaryField::ONE,
TestBinaryField::ZERO,
TestBinaryField::ZERO,
TestBinaryField::ZERO,
]);
const ZERO: Self = Self::new([
TestBinaryField::ZERO,
TestBinaryField::ZERO,
TestBinaryField::ZERO,
TestBinaryField::ZERO,
TestBinaryField::ZERO,
TestBinaryField::ZERO,
TestBinaryField::ZERO,
TestBinaryField::ZERO,
]);

/// Computes the multiplicative inverse of `a`, i.e. 1 / (a0 + a1 * t).
fn inverse(&self) -> Option<Self> {
if *self == Self::ZERO {
return None;
}

let res = self.pow(Self::ORDER - 2);
Some(res)
}

fn pow(self, power: usize) -> Self {
if power == 0 {
Self::ONE
} else if power == 1 {
self
} else if power % 2 == 0 {
self.pow(power / 2) * self.pow(power / 2)
} else {
self.pow(power / 2) * self.pow(power / 2) * self
}
}
}

impl ExtensionField<8, 2> for TestBinaryExtensionField {
const IRREDUCIBLE_POLYNOMIAL_COEFFICIENTS: [TestBinaryField; 9] = [
TestBinaryField::ONE, // 1
TestBinaryField::ONE, // a
TestBinaryField::ZERO, // a^2
TestBinaryField::ONE, // a^3
TestBinaryField::ONE, // a^4
TestBinaryField::ZERO, // a^5
TestBinaryField::ZERO, // a^6
TestBinaryField::ZERO, // a^7
TestBinaryField::ONE, // a^8
];
}
use crate::field::prime::PrimeField;

/// Returns the multiplication of two [`Ext<2, GF101>`] elements by reducing result modulo
/// irreducible polynomial.
impl Mul for TestBinaryExtensionField {
type Output = Self;

fn mul(self, rhs: Self) -> Self::Output {
let poly_self = Polynomial::<Monomial, TestBinaryField, 8>::from(self);
let poly_rhs = Polynomial::<Monomial, TestBinaryField, 8>::from(rhs);
let poly_irred =
Polynomial::<Monomial, TestBinaryField, 8>::from(Self::IRREDUCIBLE_POLYNOMIAL_COEFFICIENTS);
let product = (poly_self * poly_rhs) % poly_irred;
let res: [TestBinaryField; 8] =
array::from_fn(|i| product.coefficients.get(i).cloned().unwrap_or(TestBinaryField::ZERO));
Self::new(res)
}
}
impl MulAssign for TestBinaryExtensionField {
fn mul_assign(&mut self, rhs: Self) { *self = *self * rhs; }
}
impl Product for TestBinaryExtensionField {
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.reduce(|x, y| x * y).unwrap_or(Self::ONE)
}
}

impl Div for TestBinaryExtensionField {
type Output = Self;

#[allow(clippy::suspicious_arithmetic_impl)]
fn div(self, rhs: Self) -> Self::Output { self * rhs.inverse().expect("invalid inverse") }
}

impl DivAssign for TestBinaryExtensionField {
fn div_assign(&mut self, rhs: Self) { *self = *self / rhs }
}

impl Rem for TestBinaryExtensionField {
type Output = Self;

fn rem(self, rhs: Self) -> Self::Output { self - (self / rhs) * rhs }
}
type TestBinaryField = PrimeField<2>;

pub(super) fn num_digits(n: u64) -> usize {
let r = format!("{:b}", n);
Expand Down
121 changes: 121 additions & 0 deletions src/field/extension/gf_2_8.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//! This module contains an implementation of the quadratic extension field GF(2^8).
//! Elements represented as coefficients of a [`Polynomial`] in the [`Monomial`] basis of degree 1
//! in form: `a_0 + a_1*t`` where {a_0, a_1} \in \mathhbb{F}. Uses irreducible poly of the form:
//! (X^2-K).
//!
//! This extension field is used for our [AES implementation][`crate::encryption::symmetric::aes`].
use self::field::prime::AESField;
use super::*;

impl FiniteField for GaloisField<8, 2> {
const ONE: Self = Self::new([
AESField::ONE,
AESField::ZERO,
AESField::ZERO,
AESField::ZERO,
AESField::ZERO,
AESField::ZERO,
AESField::ZERO,
AESField::ZERO,
]);
const ORDER: usize = AESField::ORDER.pow(8);
const PRIMITIVE_ELEMENT: Self = Self::new([
AESField::ONE,
AESField::ONE,
AESField::ZERO,
AESField::ZERO,
AESField::ONE,
AESField::ZERO,
AESField::ZERO,
AESField::ZERO,
]);
const ZERO: Self = Self::new([
AESField::ZERO,
AESField::ZERO,
AESField::ZERO,
AESField::ZERO,
AESField::ZERO,
AESField::ZERO,
AESField::ZERO,
AESField::ZERO,
]);

/// Computes the multiplicative inverse of `a`, i.e. 1 / (a0 + a1 * t).
fn inverse(&self) -> Option<Self> {
if *self == Self::ZERO {
return None;
}

let res = self.pow(Self::ORDER - 2);
Some(res)
}

fn pow(self, power: usize) -> Self {
if power == 0 {
Self::ONE
} else if power == 1 {
self
} else if power % 2 == 0 {
self.pow(power / 2) * self.pow(power / 2)
} else {
self.pow(power / 2) * self.pow(power / 2) * self
}
}
}

impl ExtensionField<8, 2> for GaloisField<8, 2> {
/// Represents the irreducible polynomial x^8 + x^4 + x^3 + x + 1.
const IRREDUCIBLE_POLYNOMIAL_COEFFICIENTS: [AESField; 9] = [
AESField::ONE, // 1
AESField::ONE, // a
AESField::ZERO, // a^2
AESField::ONE, // a^3
AESField::ONE, // a^4
AESField::ZERO, // a^5
AESField::ZERO, // a^6
AESField::ZERO, // a^7
AESField::ONE, // a^8
];
}

/// Returns the multiplication of two [`Ext<8, GF2>`] elements by reducing result modulo
/// irreducible polynomial.
impl Mul for GaloisField<8, 2> {
type Output = Self;

fn mul(self, rhs: Self) -> Self::Output {
let poly_self = Polynomial::<Monomial, AESField, 8>::from(self);
let poly_rhs = Polynomial::<Monomial, AESField, 8>::from(rhs);
let poly_irred =
Polynomial::<Monomial, AESField, 9>::from(Self::IRREDUCIBLE_POLYNOMIAL_COEFFICIENTS);
let product = (poly_self * poly_rhs) % poly_irred;
let res: [AESField; 8] =
array::from_fn(|i| product.coefficients.get(i).cloned().unwrap_or(AESField::ZERO));
Self::new(res)
}
}
impl MulAssign for GaloisField<8, 2> {
fn mul_assign(&mut self, rhs: Self) { *self = *self * rhs; }
}
impl Product for GaloisField<8, 2> {
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.reduce(|x, y| x * y).unwrap_or(Self::ONE)
}
}

impl Div for GaloisField<8, 2> {
type Output = Self;

#[allow(clippy::suspicious_arithmetic_impl)]
fn div(self, rhs: Self) -> Self::Output { self * rhs.inverse().expect("invalid inverse") }
}

impl DivAssign for GaloisField<8, 2> {
fn div_assign(&mut self, rhs: Self) { *self = *self / rhs }
}

impl Rem for GaloisField<8, 2> {
type Output = Self;

fn rem(self, rhs: Self) -> Self::Output { self - (self / rhs) * rhs }
}
6 changes: 6 additions & 0 deletions src/field/extension/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,18 @@ use super::*;

mod arithmetic;
pub mod gf_101_2;
pub mod gf_2_8;

/// The [`PlutoBaseFieldExtension`] is a specific instance of the [`GaloisField`] struct with the
/// order set to the prime number `101^2`. This is the quadratic extension field over the
/// [`PlutoBaseField`] used in the Pluto `ronkathon` system.
pub type PlutoBaseFieldExtension = GaloisField<2, 101>;

/// The [`AESFieldExtension`] is a specific instance of the [`GaloisField`] struct with the
/// order set to the number `2^8`. This is the quadratic extension field over the
/// [`PlutoBaseField`] used in the Pluto `ronkathon` system.
pub type AESFieldExtension = GaloisField<8, 2>;

/// The [`PlutoScalarFieldExtension`] is a specific instance of the [`GaloisField`] struct with the
/// order set to the prime number `17^2`. This is the quadratic extension field over the
/// [`field::prime::PlutoScalarField`] used in the Pluto `ronkathon` system.
Expand Down
4 changes: 4 additions & 0 deletions src/field/prime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ pub type PlutoBaseField = PrimeField<{ PlutoPrime::Base as usize }>;
/// to the prime number `17`. This is the scalar field used in the Pluto `ronkathon` system.
pub type PlutoScalarField = PrimeField<{ PlutoPrime::Scalar as usize }>;

/// The [`AESField`] is just a field over the prime 2, used within
/// [`AES`][crate::encryption::symmetric::aes]
pub type AESField = PrimeField<2>;

/// The [`PrimeField`] struct represents elements of a field with prime order. The field is defined
/// by a prime number `P`, and the elements are integers modulo `P`.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default, PartialOrd)]
Expand Down

0 comments on commit dfdcd05

Please sign in to comment.