From dfdcd05fe4c8cde38c1621cd91fa37c896bd7b05 Mon Sep 17 00:00:00 2001 From: bing Date: Mon, 8 Jul 2024 16:48:13 +0800 Subject: [PATCH] feat: introduce GF(2^8) in `field` crate and use in aes impl --- src/encryption/symmetric/aes/mod.rs | 52 +++++------- src/field/binary_towers/mod.rs | 4 - src/field/binary_towers/tests.rs | 123 +--------------------------- src/field/extension/gf_2_8.rs | 121 +++++++++++++++++++++++++++ src/field/extension/mod.rs | 6 ++ src/field/prime/mod.rs | 4 + 6 files changed, 152 insertions(+), 158 deletions(-) create mode 100644 src/field/extension/gf_2_8.rs diff --git a/src/encryption/symmetric/aes/mod.rs b/src/encryption/symmetric/aes/mod.rs index 0015534c..2b57f55f 100644 --- a/src/encryption/symmetric/aes/mod.rs +++ b/src/encryption/symmetric/aes/mod.rs @@ -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. @@ -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::::new(col_bits); - let mult_poly = Polynomial::::new(mult_bits); - // m(x) = x^8 + x^4 + x^3 + x + 1 - let reducer = Polynomial::::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 AES diff --git a/src/field/binary_towers/mod.rs b/src/field/binary_towers/mod.rs index 46610e11..1e9b7550 100644 --- a/src/field/binary_towers/mod.rs +++ b/src/field/binary_towers/mod.rs @@ -47,10 +47,6 @@ impl From for BinaryField { } } -impl From for u8 { - fn from(value: BinaryField) -> u8 { value.0 } -} - impl Add for BinaryField { type Output = Self; diff --git a/src/field/binary_towers/tests.rs b/src/field/binary_towers/tests.rs index 0db5bc08..feebcf1d 100644 --- a/src/field/binary_towers/tests.rs +++ b/src/field/binary_towers/tests.rs @@ -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 { - 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::::from(self); - let poly_rhs = Polynomial::::from(rhs); - let poly_irred = - Polynomial::::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>(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); diff --git a/src/field/extension/gf_2_8.rs b/src/field/extension/gf_2_8.rs new file mode 100644 index 00000000..0fb2f67e --- /dev/null +++ b/src/field/extension/gf_2_8.rs @@ -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 { + 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::::from(self); + let poly_rhs = Polynomial::::from(rhs); + let poly_irred = + Polynomial::::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>(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 } +} diff --git a/src/field/extension/mod.rs b/src/field/extension/mod.rs index 0e017753..1dba5861 100644 --- a/src/field/extension/mod.rs +++ b/src/field/extension/mod.rs @@ -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. diff --git a/src/field/prime/mod.rs b/src/field/prime/mod.rs index 6b9fb550..2c3ad789 100644 --- a/src/field/prime/mod.rs +++ b/src/field/prime/mod.rs @@ -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)]