Last active
January 3, 2023 14:02
-
-
Save juliusgeo/9e4eff4c0519f7f7b9af122d59a3253e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from dataclasses import dataclass | |
from functools import reduce | |
@dataclass | |
class bff: | |
""" | |
An implementation of binary finite field arithmetic on fields of order 2^n using integers and bitwise operators. | |
Adaptation of algorithms in: | |
https://en.m.wikiversity.org/wiki/Reed–Solomon_codes_for_coders | |
https://gist.github.com/matejker/e88719bf5fd0e0e41eeeaa917a0ff583 | |
""" | |
a: int # our polynomial | |
n: int # the value 2^n | |
r: int # an irreducible polynomial over GF(2^n) | |
def __add__(self, other): | |
return bff(self.a ^ other.a, self.n) | |
# russian peasant multiplication algorithm | |
def __mul__(self, other): | |
x, y, r = self.a, other.a, 0 | |
while y: | |
r = (r, r ^ x)[y & 1] | |
y >>= 1 | |
x <<= 1 | |
x = (x, x ^ self.r)[bool(x & (self.n))] | |
return bff(r, self.n, self.r) | |
# fermat's lil theorem | |
def __pow__(self, pow: int): | |
prod = bff(1, self.n, self.r) | |
while pow != 0: | |
if pow & 1: | |
prod *= self | |
pow >>= 1 | |
self *= self | |
return prod | |
def __invert__(self): | |
return self ** (self.n-2) | |
def __truediv__(self, other): | |
return self * ~other | |
def __len__(self): | |
return int.bit_length(self.a) | |
def __repr__(self): | |
return f"element polynomial: {self.a}, GF(2^{self.n}), irreducible polynomial: {self.r}" | |
def affine(self, affine_mat, affine_c): | |
multiplicand = [int(i) for i in list(bin(self.a)[2:])][::-1] | |
res = [sum([(i * n) for i, n in zip(row, multiplicand)])%2 for row in affine_mat] | |
res = [(i+j)%2 for i, j in zip(res, affine_c)][::-1] | |
return bff(reduce(lambda x, y: 2*x+y, res), self.n, self.r) | |
def aff_from_col(col): | |
aff = [col] | |
for a in col[::-1]: | |
col = [a] + col[:-1] | |
aff.append(col) | |
return list(zip(*aff)) | |
def generate_s_box(): | |
return list(zip(*[['0x%02x' % (~bff((i<<4)+j, 256, r=0b100011011)).affine(aff_from_col([1, 1, 1, 1, 1, 0, 0, 0]), [1, 1, 0, 0, 0, 1, 1, 0]).a for i in range(0x0, 0x10)] for j in range(0x0, 0x10)])) | |
def generate_inv_s_box(): | |
return list(zip(*[['0x%02x' % (~(bff((i<<4)+j, 256, r=0b100011011).affine(aff_from_col([0, 1, 0, 1, 0, 0, 1, 0]), [1, 0, 1, 0, 0, 0, 0, 0]))).a for i in range(0x0, 0x10)] for j in range(0x0, 0x10)])) | |
# helper functions | |
def pprint_box(box): | |
print(" ", ) | |
axis = ['0x%02x' % i for i in range(0x0, 0x10)][::-1] | |
print(" ", ', '.join(axis[::-1])) | |
for row in box: | |
print(axis[-1] + ",", ', '.join(row)) | |
axis.pop(-1) | |
if __name__ == "__main__": | |
pprint_box(generate_s_box()) | |
pprint_box(generate_inv_s_box()) |
I added inverse Rijndael S-boxes:
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
0x00, 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb
0x01, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb
0x02, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e
0x03, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25
0x04, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92
0x05, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84
0x06, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06
0x07, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b
0x08, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73
0x09, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e
0x0a, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b
0x0b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4
0x0c, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f
0x0d, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef
0x0e, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61
0x0f, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is my implementation of binary finite field arithmetic using integer bitwise operations. To prove the correctness of my implementation, I use it to generate the AES Rijndael S-box as can be seen below: