Este artigo detalha o algoritmo de criptografia de bloco definido no GOST R 34.12-2015 como o Grasshopper. Em que se baseia, qual é a matemática dos algoritmos criptográficos de blocos e como esse algoritmo é implementado em java.
Quem, como, quando e por que desenvolveu esse algoritmo permanecerá fora do escopo do artigo, pois neste caso temos pouco interesse, exceto:
Gafanhoto = Kuznetsov, Nechaev E Companhia.

Como a criptografia é baseada principalmente em matemática, para que uma explicação adicional não cause muitas perguntas, você deve primeiro analisar os conceitos básicos e as funções matemáticas nas quais esse algoritmo é construído.
Campos Galois
A aritmética dos campos de Galois é aritmética polinomial, ou seja, cada elemento desse campo é um polinômio. O resultado de qualquer operação também é um elemento desse campo. Um campo Galois específico consiste em um intervalo fixo de números. A característica do campo é chamada de número primo p. Ordem de campo, ou seja, a quantidade de seus elementos é um certo grau natural de característica
pm , onde m ϵ N. Para m = 1, o campo é chamado simples. Nos casos em que m> 1, para a formação de um campo, também é necessário um polinômio gerador de grau m; esse campo é chamado de estendido.
Gf(pm) - designação do campo de Galois. O polinômio gerador é irredutível, isto é, simples (por analogia com números primos, é divisível por 1 e por si só, sem resto). Como trabalhar com qualquer informação está funcionando com bytes e um byte é de 8 bits, use como campo
Gf(28) e o polinômio gerador:
x8+x7+x6+x+1.
No entanto, para começar, analisaremos as operações básicas em um campo mais simples
Gf(23) com a geração de polinômios
f(x)=x3+x+1 .
Operação de adição
A mais simples é a operação de adição, que na aritmética de campo de Galois é uma simples adição bit a módulo 2 (R).
Chamo imediatamente a atenção para o fato de que o sinal "+" aqui e a seguir se refere à operação do XOR bit a bit, e não à adição na forma usual.
A tabela verdade da função HOR

Um exemplo:
5+3=101+011=1102=610Na forma polinomial, esta operação será semelhante a
(x2+1)+(x+1)=x2+x=1102=610
Operação de multiplicação
Para realizar a operação de multiplicação, é necessário converter os números em forma polinomial:
5=1012=1∗x2+0∗x1+1∗x0=x2+1
Como você pode ver, o número na forma polinomial é um polinômio cujos coeficientes são os valores dos dígitos na representação binária do número.
Multiplique dois números na forma polinomial:

=x4+x3+x+1=110112=2710
O resultado da multiplicação 27 não está no campo usado.
Gf(23) (consiste em números de 0 a 7, como mencionado acima). Para combater esse problema, é necessário usar um polinômio gerador.
Supõe-se também que x satisfaz a equação
f(x)=x3+x+1=0 então

Vamos fazer uma tabuada de multiplicação:

De grande importância é a tabela de graus dos elementos do campo de Galois. A elevação a uma potência também é realizada em forma polinomial, semelhante à multiplicação.
Um exemplo:

=x(x3+x+1)+x2+x+1=x2+x+1=1112=710
Assim, compilamos uma tabela de graus:

A tabela de graus é cíclica: o sétimo grau corresponde a zero, então o oitavo corresponde ao primeiro, etc. Você pode verificar isso se desejar.
Nos campos de Galois, existe o conceito de um termo primitivo - um elemento de um campo cujos graus contêm todos os elementos diferentes de zero do campo. Pode-se ver que todos os elementos correspondem a essa condição (bem, exceto 1, é claro). No entanto, esse nem sempre é o caso.
Para os campos que estamos considerando, ou seja, com a característica 2, sempre escolha 2. Como membro primitivo, dada sua propriedade, qualquer elemento do campo pode ser expresso em termos do grau do membro primitivo.

Um exemplo:
5=26,7=25Usando essa propriedade e levando em consideração a ciclicidade da tabela de graus, tentaremos multiplicar os números novamente:
5∗7=26∗25=2(6+5)=2(11mod7)=24=6
O resultado coincidiu com o que calculamos anteriormente.
Agora vamos fazer a divisão:
6/5=24/26=2(4−6)=2((−2)mod7)=25=7
O resultado obtido também é verdadeiro.
Bem, por uma questão de completude, vejamos o aumento da potência:
52=(26)2=2(6∗2)=2(12mod7)=25=7
Essa abordagem para multiplicação e divisão é muito mais simples do que operações reais usando polinômios, e para elas não há necessidade de armazenar uma grande tabela de multiplicação, mas apenas uma linha de graus de um membro de campo primitivo.
Agora de volta ao nosso campo
Gf(28)O elemento zero do campo é um, o 1º é um dois, cada elemento subsequente do 2º ao 254º elemento é calculado como o anterior, multiplicado por 2, e se o elemento estiver fora do campo, ou seja, seu valor será maior que
(28−1) então XOR é feito com o número
19510 , esse número representa o polinômio irredutível do campo

, trazemos esse número para o campo
451−256=$19 . E o 255º elemento é novamente 1. Portanto, temos um campo contendo 256 elementos, ou seja, um conjunto completo de bytes, e analisamos as operações básicas que são executadas nesse campo.

Tabela de potências de dois para o campo
Gf(28)Por que foi necessário - parte dos cálculos no algoritmo Grasshopper é realizada no campo Galois, e os resultados dos cálculos, respectivamente, são elementos desse campo.
Rede Feistel
A Rede Feistel é um método de criptografia em bloco desenvolvido por Horst Feistel na IBM em 1971. Hoje, a rede de Feistel é a base de um grande número de protocolos criptográficos.
A rede Feistel opera com blocos de texto não criptografado:
- O bloco é dividido em duas partes iguais - esquerda e direita R.
- O sub-bloco esquerdo L é alterado pela função f usando a tecla K: X = f (L, K). Como função, pode haver qualquer transformação.
- O sub-bloco resultante X é adicionado ao módulo 2 com o sub-bloco direito R, que permanece inalterado: X = X + R.
- As peças resultantes são trocadas e coladas.
Essa sequência de ações é chamada de célula Feistel.
Figura 1. Célula FeistelA rede Feistel consiste em várias células. Os sub-blocos obtidos na saída da primeira célula vão para a entrada da segunda célula, os sub-blocos resultantes da segunda célula vão para a entrada da terceira célula e assim por diante.
Algoritmo de criptografia
Agora, nos familiarizamos com as operações usadas e podemos passar para o tópico principal - ou seja, o algoritmo de criptografia Grasshopper.
A base do algoritmo é a chamada rede SP - rede de permuta-substituição. A cifra baseada na rede SP recebe um bloco e uma chave na entrada e executa várias rodadas alternadas que consistem em estágios de substituição e estágios de permutação. O Grasshopper realiza nove rodadas completas, cada uma das quais inclui três operações consecutivas:
- A operação de aplicação de uma chave redonda ou XOR bit a bit de uma chave e um bloco de dados de entrada;
- Conversão não linear, que é uma simples substituição de um byte por outro, de acordo com a tabela;
- Transformação linear. Cada byte do bloco é multiplicado no campo Galois por um dos coeficientes da série (148, 32, 133, 16, 194, 192, 1, 251, 1, 192, 194, 16, 133, 32, 148, 1), dependendo do ordinal números de bytes (uma série é apresentada para números de série de 15 a 0, conforme mostrado na figura). Os bytes são adicionados juntos ao módulo 2, e todos os 16 bytes do bloco são deslocados para a ordem baixa, e o número resultante é gravado no lugar do byte lido.

A última décima rodada não está concluída, inclui apenas a primeira operação XOR.
O Grasshopper é um algoritmo de bloco, funciona com blocos de dados de 128 bits ou 16 bytes de comprimento. O comprimento da chave é de 256 bits (32 bytes).
Figura 2. O esquema de criptografia e descriptografia do bloco de dadosO diagrama mostra a sequência de operações, onde S é uma transformação não linear, L é uma transformação linear, Ki são teclas redondas. A questão surge imediatamente - de onde vêm as teclas redondas.
Formação de Teclas Redondas
Chaves iterativas (ou redondas) são obtidas por certas transformações baseadas em uma chave mestra, cujo comprimento, como já sabemos, é de 256 bits. Esse processo começa dividindo a chave mestra ao meio, para que o primeiro par de chaves redondas seja obtido. Oito iterações da rede Feistel são usadas para gerar cada par subsequente de chaves redondas, uma constante é usada em cada iteração, que é calculada aplicando uma transformação linear do algoritmo no valor do número da iteração.
O esquema para obter chaves iterativas (redondas)Se lembrarmos da Figura 1, o sub-bloco esquerdo L é a metade esquerda da chave original, o sub-bloco direito R é a metade direita da chave original, K é a constante Ci, a função f é a sequência de operações R XOR Ci, transformação não linear, transformação linear.
As constantes de iteração Ci são obtidas usando a transformação L do número de sequência da iteração.
Portanto, para criptografar um bloco de texto, primeiro precisamos calcular 32 constantes iterativas, depois calcular 10 chaves redondas com base na chave e executar a sequência de operações mostrada na Figura 2.
Vamos começar calculando as constantes:
Primeira const
C1=110=000000012=0116 No entanto, todas as transformações em nosso algoritmo são realizadas com blocos de 16 bytes, portanto é necessário suplementar a constante com o comprimento do bloco, ou seja, adicionar 15 bytes zero à direita, obtemos
C1=010000000000000000000000000000000000
Multiplique-o por uma série (1, 148, 32, 133, 16, 194, 192, 1, 251, 1, 192, 194, 16, 133, 32, 148) da seguinte maneira:
a15=a15∗148+a14∗32+a13∗133+a12∗16+
+a11∗194+a10∗192+a9∗1+a8∗251+a7∗1+a6∗192+
+a5∗194+a4∗16+a3∗133+a2∗32+a1∗148+a0∗1
(essa igualdade é dada nas operações dos campos de Galois)
Como tudo, exceto o byte zero, é igual a 0 e o byte zero é multiplicado por 1, obtemos 1 e o escrevemos na ordem mais alta do número, deslocando todos os bytes para a ordem mais baixa, obtemos:
C1=0000000000000000000000000000000000
Repita as mesmas operações. Desta vez
a15=1 , todos os outros bytes são 0, portanto, apenas o primeiro permanece nos termos
a15∗148=1∗148=14810=9416 nós obtemos:
C1=00000000000000000000000000000000194
Fazemos a terceira iteração, eis dois termos diferentes de zero:
a15∗148+a14∗32=148∗148+1∗32=
100100100∗10010100+00000001∗00100000=
=(x7+x4+x2)∗(x7+x4+x2)+1∗x5=x14+x8+x4+x5=
=x6(x8+x7+x6+x+1)+x13+x12+x7+x6+x8+x4+x5=
=x5(x8+x7+x6+x+1)+x11+x5+x7+x8+x4+x5=
=x3(x8+x7+x6+x+1)+x10+x9+x3+x8+x7=
=x2(x8+x7+x6+x+1)+x2+x7=x7+x2=13210
13210=8416
De acordo com a tabela de graus, isso poderia ser resolvido com muito mais facilidade:
148∗148+1∗32=245∗245+25=290+25=164+32=132
C1=00000000000000000000000000019484
Além disso, tudo é exatamente o mesmo, apenas 16 iterações para cada constante
C1=000000000000000000000000019484DD
C1=0000000000000000000000019484DD10
C1=00000000000000000000019484DD10BD
C1=000000000000000000019484DD10BD27
C1=0000000000000000019484DD10BD275D
C1=00000000000000019484DD10BD275DB8
C1=000000000000019484DD10BD275DB87A
C1=0000000000019484DD10BD275DB87A48
C1=00000000019484DD10BD275DB87A486C
C1=000000019484DD10BD275DB87A486C72
C1=0000019484DD10BD275DB87A486C727
C1=00019484DD10BD275DB87A486C7276A2
E a constante final:
C1=019484DD10BD275DB87A486C7276A2E6
Outras constantes:
C2=02EBCB7920B94EBAB3F490D8E4EC87DC
C3=037F4FA4300469E70B8ED8B4969A25B2
C4=041555F240B19CB7A52BE3730B1BCD7B
C5=0581D12F500CBBEA1D51AB1F796D6F15
C6=06FE9E8B6008D20D16DF73ABEFF74AA7
C7=076A1A5670B5F550AEA53BC79D81E8C9
C8=082AAA2780A1FBAD895605E6163659F6
C9=09BE2EFA901CDCF0312C4D8A6440FB98
C10=0AC1615EA018B5173AA2953EF2DADE2A
C11=0B55E583B0A5924A82D8DD5280AC7C44
C12=0C3FFFD5C010671A2C7DE6951D2D948D
C13=0DAB7B08D0AD40479407AEF96F5B36E3
C14=0ED434ACE0A929A09F89764DF9C11351
C15=0F40B071F0140EFD27F33E218BB7B13F
C16=1054974EC3813599D1AC0A0F2C6CB22F
C17=11C01393D33C12C469D642635E1A1041
C18=12BF5C37E3387B2362589AD7C88035F3
C19=132BD8EAF3855C7EDA22D2BBBAF6979D
C20=1441C2BC8330A92E7487E97C27777F54
C21=15D54661938D8E73CCFDA1105501DD3A
C22=16AA09C5A389E794C77379A4C39BF888
C23=173E8D18B334C0C97F0931C8B1ED5AE6
C24=187E3D694320CE3458FA0FE93A5AEBD9
C25=19EAB9B4539DE969E0804785482C49B7
C26=1A95F6106399808EEB0E9F31DEB66C05
C27=1B0172CD7324A7D35374D75DACC0CE6B
C28=1C6B689B03915283FDD1EC9A314126A2
C29=1DFFEC46132C75DE45ABA4F6433784CC
C30=1E80A3E223281C394E257C42D5ADA17E
C31=1F14273F33953B64F65F342EA7DB0310
C32=20A8ED9C45C16AF1619B141E58D8A75E
Agora vamos calcular as chaves redondas de acordo com o esquema apresentado acima, pegue a chave de criptografia:
K=7766554433221100FFEEDDCCBBAA9988

Então
K1=7766554433221100FFEEDDCCBBAA9988
K2=EFCDAB89674523011032547698BADCFE
K1 será o sub-bloco esquerdo da rede Feistel e
K2 - certo.
Vamos fazer a operação
K1+C1Primeiro byte
K1 é igual a
7716=011101112Primeiro byte
C1 é igual a
0116=000000012011101112+000000012=011101102=7616
os bytes restantes são convertidos da mesma maneira, como resultado
X(K1,C1)=K1+C1 :
X(K1,C1)=76F2D199239F365D479495A0C9DC3BE6
Em seguida, realizamos uma transformação não linear
S(X(K1,C1)) . É realizado de acordo com a tabela:
Tabela de conversão não linearO número 0 é substituído por 252, 1 por 238, 17 por 119, etc.
7616=11810
S(118)=13810=8A16
S(X(K1,C1))=8A741BE85A4A8FB7AB7A94A737CA9809
Agora execute uma transformação linear
L(S(X(K1,C1))) , foi considerado em detalhes ao calcular constantes iterativas; portanto, fornecemos apenas o resultado final:
L(S(X(K1,C1)))=A644615E1D0757926A5DB79D9940093D
De acordo com o esquema da célula Feistel, realizamos XOR com o sub-bloco direito, ou seja, com
K2 :
X(L(S(X(K1,C1))),K2)=4989CAD77A4274937A6FE3EB01FAD5C3
E o resultado obtido na saída da primeira célula Feistel:
EFCDAB89674523011032547698BADCFE4989CAD77A4274937A6FE3EB01FAD5C3
Esse valor é dividido pela metade e vai para a entrada da segunda célula Feistel, onde a segunda constante já é usada
C2 . Depois de passar por oito células, obtemos as 2 chaves a seguir
K3 e
K4 . Executaremos oito iterações da rede Feistel com eles, obteremos o próximo par de chaves e assim por diante. Oito iterações por par de chaves, já que inicialmente temos o primeiro par, então 32 iterações são realizadas no total, cada uma com sua própria constante.
Chaves restantes:
K3=448CC78CEF6A8D2243436915534831DB
K4=04FD9F0AC4ADEB1568ECCFE9D853453D
K5=ACF129F44692E5D3285E4AC468646457
K6=1B58DA3428E832B532645C16359407BD
K7=B198005A26275770DE45877E7540E651
K8=84F98622A2912AD73EDD9F7B0125795A
K9=17E5B6CD732FF3A52331C77853E244BB
K10=43404A8EA8BA5D755BF4BC1674DDE972
Bloquear criptografia
Calculamos todas as chaves e agora finalmente podemos ir diretamente para a criptografia do bloco de texto e, se você ler cuidadosamente tudo o que foi escrito acima, a criptografia do texto não será difícil, pois todas as operações usadas neste processo e sua sequência foram examinadas em detalhes.
Pegue o bloco de texto simples:
T=8899AABBCCDDEEFF0077665544332211
executar a sequência de operações X, S, L
X(T,K1)=FFFFFFFFFFFFFFFFFFFF99BB99FF99BB99
S(X(T,K1))=B6B6B6B6B6B6B6B6B6E87DE8B6E87DE8
L(S(X(T,K1)))=30081449922F4ACFA1B055E386B697E2
T1=30081449922F4ACFA1B055E386B697E2
X(T1,K2)=DFC5BFC0F56A69CEB18201951E0C4B1C
S(X(T1,K2))=61AC3B07F47891E74524EE945F23A214
L(S(X(T1,K2)))=7290C6A158426FB396D562087A495E28
T2=7290C6A158426FB396D562087A495E28
e assim por diante, o resultado final ficará assim:
T10=CDEDD4B9428D465A3024BCBE909D677F
Descriptografia de bloco
Para descriptografar o texto, você precisa usar as operações inversas na ordem inversa (veja a Fig. 2).
A operação XOR é inversa a si mesma, a inversa à operação S é a substituição de acordo com a tabela a seguir:
Tabela de Transformação Não Linear InversaA transformação inversa para a função L será:
a0=a15∗148+a14∗32+a13∗133+a12∗16+
+a11∗194+a10∗192+a9∗1+a8∗251+a7∗1+a6∗192+a5∗194+
+a4∗16+a3∗133+a2∗32+a1∗148+a0∗1
e uma mudança na direção do nível sênior. (Repita a operação 16 vezes)
Implementação Java
Primeiro, definimos as constantes necessárias
static final int BLOCK_SIZE = 16;
Vamos criar todas as principais funções:
Para implementar a função L, precisamos de várias funções auxiliares, uma para calcular a multiplicação de números no campo de Galois e outra para a mudança.
Funções inversas:
Bem, e a principal função
static byte[] key_1 = {0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00, (byte) 0xff, (byte) 0xee, (byte) 0xdd, (byte) 0xcc, (byte) 0xbb, (byte) 0xaa, (byte) 0x99, (byte) 0x88}; static byte[] key_2 = {(byte) 0xef, (byte) 0xcd, (byte) 0xab, (byte) 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32, 0x54, 0x76, (byte) 0x98, (byte) 0xba, (byte) 0xdc, (byte) 0xfe}; static byte[] blk = DatatypeConverter.parseHexBinary("8899aabbccddeeff0077665544332211"); public static void main(String[] args) { GOST_Kuz_Expand_Key(key_1, key_2); byte[] encriptBlok = GOST_Kuz_Encript(blk); System.out.println(DatatypeConverter.printHexBinary(encriptBlok)); byte[] decriptBlok = GOST_Kuz_Decript(encriptBlok); System.out.println(DatatypeConverter.printHexBinary(decriptBlok)); }
Aprendemos a criptografar um bloco de dados para criptografar texto cujo tamanho é maior que o comprimento do bloco; existem vários modos descritos no padrão - GOST 34.13-2015:
- modo de substituição simples (livro de códigos eletrônico, BCE);
- modo gama (contador, CTR);
- modo gama com realimentação de saída (realimentação de saída, OFB);
- modo de engrenagem de substituição simples (Cipher Block Chaining, CBC);
- modo gama com feedback em texto cifrado (Cipher Feedback, CFB);
- Código de autenticação de mensagem (MAC).
Em todos os modos, o comprimento do texto deve sempre ser múltiplo do comprimento do bloco, para que o texto seja sempre preenchido à direita com um único bit e zeros ao comprimento do bloco.
O modo mais fácil é o modo de substituição simples. Nesse modo, o texto é dividido em blocos, então cada bloco é criptografado separadamente do restante, os blocos do texto cifrado são colados e recebemos uma mensagem criptografada. Esse modo é o mais simples e o mais vulnerável e quase nunca é aplicado na prática.
Outros modos podem ser considerados em detalhes posteriormente.