2

I am using pyfinte to calculate multiplication for AES over the field it uses which is F(2^8) but when I do as following:

from pyfinite import ffield

a = 0xbf
b = 0x03
F = ffield.FField(8)
c = F.Multiply(a, b)
print(hex(c))

the actual result is 0xdc but it should be 0xda here is how I solve it by hand:

0xbf*0x03 = 0xbf * 0x02 + 0xbf * 0x01 = 0b10111111 * 0x02 + 0xbf = 0b01111110 + 0x1B + 0xbf = 0b11011010 = 0xda

here is better format of my hand soloving:

enter image description here

so where is the wrong? is it on my solving by hand? or in pyfinite? how to solve this?

mark
  • 351
  • 1
  • 3
  • 16
  • Could you format and/or annotate your hand math a little more? I'm having a hard time following it. It's been a while, but I followed [Wikipedia's](https://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplication) guidance and got 0xdc just like pyfinite did. (Edit: which I guess makes this more of a math question than a programming question.) – glibdud Jan 04 '19 at 13:43
  • @glibdud just a minute to formate my hand hand solving better – mark Jan 04 '19 at 13:50
  • @glibdud I did what you asked and updated my question to include latex detailed formatted solving by hand – mark Jan 04 '19 at 14:18
  • How do you know that the modulus is 0x11B? – Matt Timmermans Jan 04 '19 at 14:31
  • @MattTimmermans this is the way we learned in college couple days ago but I do not know if I misunderstood here is summary: when multiplying any number X by 03 in MixColumns stage in AES we multipy X by 02 and then Xor it with X . Multiplying X by 2 is as follow: we convert X to it is binary representation and we look at the leftmost bit if it is one or zero then we shift X to the left by one and Xor it with 0x1B if the leftmost bit was one. I do not know if I am confusing what we learn with what I read online about AES I think what we learned is shortcut for multiply over GF(2^8) – mark Jan 04 '19 at 14:38
  • @MattTimmermans here is the [method](https://crypto.stackexchange.com/a/2403) I used to solve by hanf – mark Jan 04 '19 at 15:05

2 Answers2

3

I'm not intimately familiar with AES, but from the link you provided it appears that the generator polynomial for the field is 0x11b. By default, pyfinite uses a different generator, which will produce different multiplication results:

>>> f = ffield.FField(8)
>>> hex(f.generator)
'0x11d'

If you specify the generator when you create the field object, you get the result you're expecting:

>>> f = ffield.FField(8, gen=0x11b, useLUT=0)
>>> hex(f.Multiply(0xbf, 0x03))
'0xda'
glibdud
  • 7,550
  • 4
  • 27
  • 37
  • I can confirm AES uses 0x11b, where all non-zero elements can be considered to be some power of 0x03. For 0x11d, all non-zero elements can be considered to be a power of 0x02. Most implementations involving finite fields will choose a polynomial where all non-zero elements are a power of 2. I don't know why AES choose 0x11b. – rcgldr Jan 04 '19 at 16:50
0

The issue is that AES uses an irreducible, but not primitive, polynomial for its extension field. Most extension fields use a primitive polynomial so that x is a primitive element of the field. I imagine that's what pyfinite has as its default.

Instead, AES uses x^8 + x^4 + x^3 + x + 1 as its irreducible polynomial and has x + 1 as a primitive element (or generator of the multiplicative group).

While I don't know the details of pyfinite, I created a similar library galois that extends NumPy arrays to operate over Galois fields. It supports arbitrarily-sized array arithmetic, linear algebra on Galois field matrices, polynomials over Galois field, and more. The library is written in Python but JIT compiled using Numba so the array arithmetic is as fast as native NumPy.

Here is an example doing what you desired.

In [1]: import galois                                                                                                                                                                          

In [2]: GF = galois.GF(2**8, irreducible_poly=0x11b)                                                                                                                                           

In [3]: print(GF.properties)                                                                                                                                                                   
GF(2^8):
  characteristic: 2
  degree: 8
  order: 256
  irreducible_poly: x^8 + x^4 + x^3 + x + 1
  is_primitive_poly: False
  primitive_element: x + 1

In [4]: a = GF(0xbf); a                                                                                                                                                                        
Out[4]: GF(191, order=2^8)

In [5]: b = GF(0x03); b                                                                                                                                                                        
Out[5]: GF(3, order=2^8)

In [6]: a * b                                                                                                                                                                                  
Out[6]: GF(218, order=2^8)

In [7]: 0xda                                                                                                                                                                                   
Out[7]: 218
Matt Hostetter
  • 360
  • 1
  • 10