Definições de soma e produto de 2 bytes em 1 byte para as quais os axiomas de soma e produto valem.
Para alguns sistemas criptográficos, especialmente o 4 AES, desejamos realizar transformações entre bytes que preservem os axiomas de anel (associatividade, comutatividade, existência de elemento neutro e distributividade) mas que mantém uma saída de mesmo comprimento que a entrada (i.e. multiplicar/somar dois bytes e obter um outro byte como resultado).
Para a soma, a operação escolhida é o XOR, denotada “\(\oplus\)”. De fato, temos: \[ \begin{gather} (A \oplus B) \oplus C = A \oplus (B \oplus C) \\ A \oplus B = B \oplus C \\ A \oplus 00000000 = A \end{gather} \] o que satisfaz os axiomas dados.
Segue uma implementação em Python do algoritmo XOR:
def xor(a, b):
output = ''
for i in range(len(a)):
if a[i] == b[i]:
output += '0'
else:
output += '1'
Para o produto, escolhemos a multiplicação em \(GF(2^8)\), denotada “\(\otimes\)”. Procedemos da seguinte forma:
De fato, esta definição de produto valida todos os axiomas dados.
Caso a multiplicação em questão seja \(a \otimes 00000010\), ou seja, a multiplicação pelo polinômio \(p_b(x) = x\), há um método bem mais rápido que o algoritmo acima:
Caso \(a_1 = 0\), o resultado é \(a \otimes b = a_2a_3a_4a_5a_6a_7a_8a_1\), ou seja, um shift circular.
Caso \(a_1 = 1\), o resultado é o byte correspondente a \(p_a(x) \cdot x + m(x) \text{ mod }2\).
Segue uma implementação em Python do algoritmo de produto em \(GF(2^8)\):
def xtime(a, m):
'''
str, str -> str
Recebe dois bytes a e m e retorna o produto [a * '00000010' mod m]
como definido em GF(2^8). Ou seja, com a notação polinomial,
retorna o produto [a(x) * x mod m(x)].
'''
if a[0] == '0':
# se a = 0bcdefgh, a * x mod m = bcdefgh0
return a[1:] + '0'
else:
# se a = 1bcdefgh, a * x mod m = (a * x) XOR m
# o maior bit sempre resulta 0 e é desprezado
return xor(byte_mult(a, '00000010'), m)[1:]
def byte_mult(a,b):
prod = 15*'0'
for i in range(8):
for j in range(8):
if a[i] == '1' and b[j] == '1':
prod = flip_bit(prod, i+j)
return prod
def byte_mult_mod(a, b, m):
# se a = x ou b = x, usamos um atalho
if a == '00000010':
return xtime(b,m)
if b == '00000010':
return xtime(a,m)
# produto dos polinômios correspondentes a a e b
prod = byte_mult(a,b)
# obter índices '1' de m
m_ind = []
for i in range(len(m)):
if m[::-1][i] == '1':
m_ind.append(i)
while prod[:7] != '0000000':
# grau do dividendo
for i in range(15):
if prod[i] == '1':
deg = 14 - i
break
for i in m_ind:
mult = deg - m_ind[-1]
prod = flip_bit(prod, 14 - (mult + i) )
return prod[7:]