INSTITUTO DE COMPUTAÇÃO
Roberto Cabral Rabêlo Filho
Implementação eficiente das funções de resumo
criptográfico: SHA-3 e QUARK.
CAMPINAS
2015
Implementação eficiente das funções de resumo criptográfico:
SHA-3 e QUARK.
Dissertação apresentada ao Instituto de Computação da Universidade Estadual de Campinas como parte dos requisitos para a obtenção do título de Mestre em Ciência da Computação.
Orientador: Prof. Dr. Julio César López Hernández
Este exemplar corresponde à versão final da Dissertação defendida por Roberto Cabral Rabêlo Filho e orientada pelo Prof. Dr. Julio César López Hernández.
CAMPINAS
2015
Ficha catalográfica
Universidade Estadual de Campinas
Biblioteca do Instituto de Matemática, Estatística e Computação Científica Ana Regina Machado - CRB 8/5467
Rabêlo Filho, Roberto Cabral,
R112i RabImplementação eficiente das funções de resumo criptográfico : SHA-3 e QUARK / Roberto Cabral Rabêlo Filho. – Campinas, SP : [s.n.], 2015.
RabOrientador: Julio César López Hernández.
RabDissertação (mestrado) – Universidade Estadual de Campinas, Instituto de Computação.
Rab1. Hashing (Computação). 2. Criptografia. 3. Arquitetura de computador. I. López Hernández, Julio César,1961-. II. Universidade Estadual de Campinas. Instituto de Computação. III. Título.
Informações para Biblioteca Digital
Título em outro idioma: Efficient implementation of cryptography hash functions : SHA-3 and QUARK
Palavras-chave em inglês: Hashing (Computer science) Cryptography
Computer architecture
Área de concentração: Ciência da Computação Titulação: Mestre em Ciência da Computação Banca examinadora:
Julio César López Hernández [Orientador] Marcos Antonio Simplicio Junior
Marco Aurélio Amaral Henriques Data de defesa: 16-12-2015
Programa de Pós-Graduação: Ciência da Computação
INSTITUTO DE COMPUTAÇÃO
Roberto Cabral Rabêlo Filho
Implementação eficiente das funções de resumo criptográfico:
SHA-3 e QUARK.
Banca Examinadora:
• Prof. Dr. Julio César López Hernández IC/UNICAMP
• Prof. Dr. Marcos Antonio Simplicio Junior LARC/USP
• Dr. Marco Aurélio Amaral Henriques FEEC/UNICAMP
A ata da defesa com as respectivas assinaturas dos membros da banca encontra-se no processo de vida acadêmica do aluno.
Agradeço primeiramente a Deus, por me dar força diariamente para continuar nessa jor-nada, me dando forças e sabedoria para trilhar o meu caminho.
Depois à minha família, que mesmo distante geograficamente, sempre me deram o apoio necessário, acreditaram em mim e me incentivaram a seguir minha jornada. Meu muito obrigado a minha mãe, Rosângela, meu pai, Roberto, e meus irmãos Ana e Robert. Eu não teria conseguido sem o apoio de vocês.
À minha noiva, Leonara, que me apoiou durante toda essa caminhada, muito obrigado por seu carinho, alegria e atenção; pela sua vibração com minhas conquistas e teu ombro amigo em cada momento difícil. Sem você, essa caminhada teria sido muito mais difícil. Ao meu orientador, professor Julio, por me guiar durante o desenvolvimento deste tra-balho. Por compartilhar seus conhecimentos, pela paciência, dedicação e disponibilidade. Agradeço por me proporcionar essas experiências que levarei comido para sempre. Aos membros da banca, professor Marcos Simplicio e professor Marco Aurelio, pelo tempo dedicado, pelas correções e propostas de melhorias.
Agradeço ao meu amigo Armando, por se disponibilizar, sempre que necessário, a me ajudar, com discussões pertinentes e troca de experiência e conhecimento. Também agra-deço pelo tempo disponibilizado para realizar revisões em meu trabalho.
Aos companheiros do LASCA que me auxiliaram, direta ou indiretamente, durante a minha pesquisa.
Registro meu reconhecimento à Intel, por financiar o projeto ISRA, ao CNPq pela bolsa de Desenvolvimento Tecnológico e à CAPES pelo auxílio financeiro.
Finalmente agradeço aos demais que, de alguma forma, me ajudaram na conclusão deste trabalho: docentes, funcionários e colegas do Instituto de Computação da UNICAMP. Muito Obrigado!
As funções de resumo criptográfico são primitivas de extrema importância, pois são ne-cessárias para a construção de inúmeros protocolos de segurança. Em 2007, o Instituto Nacional de Padrões e Tecnologias americano (NIST) iniciou um concurso público para escolher um novo padrão de funções de resumo criptográfico, conhecido por SHA-3; tal concurso teve fim em 2012, tendo como vencedor o algoritmo Keccak. A família SHA-3 tende a ser usada em larga escala nos próximos anos e é importante desenvolver implemen-tações eficientes e seguras destas funções; uma estratégia que pode ser usada é aproveitar conjuntos avançados de instruções vetoriais. Os processadores de propósito geral estão expandindo cada vez mais seus conjuntos de instruções; em 2013, a microarquitetura Haswell da Intel trouxe consigo o conjunto de instruções vetoriais AVX2, que expandiu as instruções de aritmética inteira nos registradores de 128 e 256 bits. Um dos objetivos gerais desta dissertação foi prover técnicas que viabilizem o uso eficiente dos conjuntos de instruções vetoriais na implementação do algoritmo SHA-3; este estudo culminou em um conjunto de implementações eficientes que usam registradores de 128 e 256 bits para cal-cular um, dois ou quatro valores de resumo concorrentemente, permitindo uma aceleração de 1,89 e 2,57 vezes na computação de dois e quatro valores de resumo, respectivamente. Adicionalmente, também foram estudadas funções de resumo criptográfico para disposi-tivos com recursos limitados; tais disposidisposi-tivos estão ganhando cada vez mais mercado, sendo usados principalmente para a Internet das Coisas (IoT). A maioria dos algoritmos criptográficos desenvolvidos para esses dispositivos foram projetados para serem imple-mentados em hardware; do ponto de vista de implementação em software, esses algoritmos apresentam desafios para realizar uma implementação eficiente e segura. O QUARK é um algoritmo que foi projetado neste contexto; ele é uma família de funções de resumo que possui um bom desempenho em hardware, mas não possui implementações em software eficientes para processadores de 32 bits. Neste contexto, foram realizadas otimizações algorítmicas e de implementação no QUARK que permitiram acelerar sua execução na plataforma Intel Galileo, atingindo um ganho de desempenho de aproximadamente 15 vezes quando comparado com a implementação de referência.
Cryptographic hash functions are extremely important primitives, as they are necessary for the construction of several cryptographic protocols. In 2007, the National Institute of Standards and Technology (NIST) started a competition to select the new version of the SHA algorithm family, called SHA-3; this competition ended in 2012, having Keccak as the winner. The SHA-3 family is expected to be used on a large scale in the coming years, so it is important to have fast and secure implementations of these functions; a strategy that can be used is to take advantage of the advanced instruction sets. General purpose processors are increasingly expanding their instruction sets; in 2013 the Intel’s microarchitecture, Haswell, brought the set of vector instructions AVX2 that expanded the integer arithmetic instructions in the registers of 128 and 256 bits. One of the general goals of this work was to provide techniques that facilitate the efficient use of sets of vector instructions in implementing the SHA-3 algorithm; this study culminated in a set of efficient implementations that use registers of 128 and 256 bits to calculate one, two or four hash values concurrently, allowing to compute two and four hash values 1.89 and 2.57 times faster, respectively. Additionally, cryptographic hash functions for resource-constrained devices were also studied; such devices are being used widely, mainly for the Internet of Things (IoT). Most of the cryptographic algorithms developed for these devices were designed to be implemented in hardware; from the point of view of software implementation, those algorithms present challenges to achieve a fast and secure implementation. QUARK is a family of lightweight hash functions that was designed for hardware implementation. In this work, we propose some algorithmic optimizations to obtain a fast software implementation of QUARK on the Intel Galileo platform, achieving a speedup of 15 times compared to the reference implementation.
2.1 As três propriedades de segurança de uma função de resumo, adaptado de
[42]. . . 16
2.2 Ataque do Homem no meio, adaptado de [51]. . . 17
2.3 Construção esponja: Z = ESPONJA[f , pad, r](M, d). . . 21
2.4 Permutação P . . . 27
3.1 Conjunto de registradores vetoriais e de propósito geral da arquitetura Intel. 31 4.1 Organização do estado para implementação sequencial usando variáveis de 256 bits. . . 41
4.2 Passo a passo da etapa de mapeamento θ na implementação sequencial usando as instruções de 256 bits. . . 41
4.3 Passo a passo das etapas de mapeamento ρ e π na implementação sequencial usando as instruções de 256 bits. . . 43
4.4 Valores a serem rotados na etapa ρ para a implementação sequencial usando as registradores de 256 bits. . . 43
4.5 Passo a passo das etapas de mapeamento χ e ι na implementação sequencial usando as instruções de 256 bits. . . 44
4.6 Organização do estado para as iterações subsequentes. . . 46
4.7 Organização do estado entre a execução das etapas de mapeamento na implementação sequencial usando instruções de 128 bits. . . 47
4.8 Organização do estado para implementação vetorizada (2M) usando ins-truções de 128 bits. . . 51
4.9 Organização do estado para a iteração i + 1 na implementação vetorizada usando instruções de 128 bits. . . 52
4.10 Organização do estado para implementação vetorizada (4M) usando ins-truções de 256 bits. . . 54
4.11 Organização do estado para implementação vetorizada (2M) usando ins-truções de 256 bits. . . 55
4.12 Teste de desempenho da família SHA-3. . . 56
4.13 Teste de desempenho da família SHA-3. . . 57
5.1 Inicialização das palavras t0, . . . , t12, usadas como entrada na primeira ite-ração da função u do algoritmo S-QUARK. . . 62
5.2 Inicialização das palavras, t0, . . . , t12, usadas como entrada na primeira iteração da função u1 do algoritmo D-QUARK. . . 66
2.1 Nível de segurança atingível pelas funções de resumo. . . 19
2.2 Nível de segurança das funções SHA-1, SHA-2 e SHA-3 [40]. . . 26
2.3 Parâmetros da família QUARK. . . 29
2.4 Nível de segurança da família QUARK [3]. . . 29
3.1 Latência, vazão (instruções executadas por ciclo quando não há dependên-cias) e portas de execução de algumas instruções AVX2. . . 36
3.2 Latência, vazão e portas de execução de das instruções do conjunto BMI1 e BMI2. . . 37
3.3 Principais características do microcontrolador Intel Galileo. . . 38
3.4 Latência de algumas instruções da microarquitetura Quark. . . 39
4.1 Valores a serem rotados na etapa de mapeamento ρ na implementação sequencial usando instruções de 128 bits. . . 48
4.2 Especificações técnicas dos computadores HW-DESKTOP e HW-ULTRA. 56 4.3 Ciclos por bytes e speedup das implementações paralelas vetorizadas. . . . 57
5.1 Desempenho do código de referência usando as funções otimizadas. . . 67
5.2 Desempenho para nível de segurança de 112 bits. . . 67
1 Introdução 12
2 Funções de Resumo Criptográfico 15
2.1 Propriedades . . . 15 2.2 Aplicações . . . 16 2.2.1 Autenticador de mensagem . . . 16 2.2.2 Assinatura digital . . . 17 2.2.3 Armazenamento de senhas . . . 18 2.3 Segurança . . . 18 2.3.1 Ataque do Aniversário . . . 18
2.3.2 Modelo do oráculo aleatório . . . 19
2.4 Construção de funções de resumo . . . 19
2.4.1 Construção Merkle-Damgård . . . 19
2.4.2 Construção Esponja . . . 20
2.5 Funções de resumo baseadas na construção esponja . . . 21
2.5.1 SHA-3 . . . 21
2.5.2 QUARK . . . 25
2.6 Resumo . . . 29
3 Micro Arquitetura Intel 30 3.1 Instruções vetoriais . . . 30
3.1.1 O conjunto de instruções AVX2 . . . 31
3.2 Instruções de manipulação de bits . . . 35
3.3 Intel Galileo . . . 37
3.4 Conjunto de instruções . . . 38
3.5 Resumo . . . 39
4 Implementação da família SHA-3 40 4.1 Implementação sequencial usando instruções de 256 bits . . . 40
4.2 Implementação sequencial usando instruções de 128 bits . . . 45
4.3 Implementação vetorizada (2M) usando instruções de 128 bits . . . 50
4.4 Implementação vetorizada (4M) usando instruções de 256 bits . . . 53
4.5 Implementação vetorizada (2M) usando instruções de 256 bits . . . 54
4.6 Testes de desempenho . . . 55
5.2 S-QUARK . . . 61
5.3 D-QUARK . . . 63
5.4 Testes de desempenho . . . 66
5.5 Conclusão . . . 68
6 Conclusões e trabalhos futuros 69 Referências Bibliográficas 72 A 77 A.1 Família QUARK . . . 77
Introdução
A segurança da informação é um requisito básico para o bom funcionamento de muitas aplicações que utilizam informações sensíveis. Para prover esse requisito, as aplicações precisam usar protocolos criptográficos, que por sua vez devem ser implementados de forma eficiente e segura. Uma primitiva básica, que é crucial na construção de muitos protocolos criptográficos, chama-se função de resumo criptográfico; ela atua como uma função de compressão que mapeia uma cadeia de bits de tamanho arbitrário em uma cadeia de bits de tamanho fixo; essa flexibilidade nas aplicações lhes rendeu o apelido de canivete suíço da criptografia (Swiss army knives of cryptography) [43].
Durante a década de 80, percebeu-se a necessidade da criação de funções de resumo criptográfico eficientes e seguras; em pouco tempo, surgiram inúmeras propostas de fun-ções de resumo, sendo encontradas vulnerabilidades na maioria delas [43]. Em 1993, o Instituto Nacional de Padrões e Tecnologias americano (NIST) padronizou uma função de resumo que ficou conhecida por SHA-0 [45], a qual foi retirada pouco tempo após seu lançamento por conter problemas de segurança. Em 1995, o NIST publicou o SHA-1 [41] que possui um funcionamento interno muito similar ao SHA-0 e em 2001 foi publicado o padrão SHA-2 [44], que é bastante utilizado nos dias atuais.
Esses padrões, entretanto, vêm sofrendo alguns ataques nos últimos anos; em 2005, os trabalhos [12] e [48] mostraram ataques baseados em colisões contra uma versão reduzida do SHA-1; no mesmo ano, Wang et al. [50] mostraram um ataque que quebra, teorica-mente, a resistência a colisão do SHA-1. A segunda versão da família, conhecida como SHA-2, possui uma estrutura muito similar ao SHA-1 e já possui alguns ataques à sua versão reduzida, como é mostrado em [31]. Por conta desses ataques à família SHA, em 2007 o NIST decidiu publicar uma chamada aberta para selecionar um novo algoritmo, que viria a ficar conhecido como SHA-3. Em 2012, após três rodadas de competição, o algoritmo Keccak [9] foi escolhido como vencedor [35].
SHA-3
O algoritmo SHA-3 baseia-se na construção esponja definida sobre a função de permutação Keccak-p[1600, 24] [40]; internamente, essa função trabalha sobre um estado de 1600 bits que pode ser visto como uma matriz de 25 palavras de 64 bits. A família SHA-3 é
composta por quatro funções de resumo criptográfico e duas funções de resumo com saída variável, Extendable Output Functions (XOF). Por ter vencido o concurso, espera-se que o algoritmo SHA-3 venha a ser usado nas aplicações criptográficas por muito anos. Assim, é importante fornecer implementações eficientes e seguras desse algoritmo nas arquiteturas modernas.
Em 2013 a Intel lançou a microarquitetura Haswell, trazendo consigo o conjunto de instruções AVX2. Esse conjunto trouxe instruções que permitem acelerar a computação da permutação Keccak-p[1600, 24], como a instrução de deslocamento variável, que permite deslocar quantidades diferentes de bits em cada palavra de um registrador com apenas uma instrução. Nesta dissertação, foram aproveitados os conjuntos avançados de instruções vetoriais dos processadores atuais para implementar eficientemente a família de funções de resumo criptográfico SHA-3.
Dispositivos com recursos limitados
O forte crescimento da Internet das Coisas (IoT) tem aumentado a necessidade de im-plementação de protocolos criptográficos em dispositivos com recursos limitados. Neste cenário, as implementações devem ser rápidas, compactas, consumir pouca energia e serem resistentes a ataques de canal colateral. Nos últimos anos, apareceram alguns candidatos a funções de resumo leve, tais como o PHOTON [28], QUARK [2] e SPONGENT [13].
Em plataformas que não possuem um componente de hardware dedicado para funções de resumo criptográfico, uma abordagem comum é aproveitar o conjunto de instruções do dispositivo para implementar essas funções em software. Recentemente, funções de resumo criptográfico leves têm sido implementadas em software para microcontroladores de 8 bits; em [5] foi apresentado uma implementação em software das funções de resumo leves QUARK, PHOTON e SPONGENT para um nível de segurança de 80 e 112 bits; em [22] foram mostradas implementações em software de cifras de blocos leves. Por outro lado, para arquiteturas de 32 bits existem poucas implementações de funções de resumo leve. Um dos poucos exemplos é o trabalho realizado em [29], onde é apresentada uma implementação do PHOTON baseada em tabelas. Nesta dissertação foram propostas técnicas que permitiram implementar o algoritmo QUARK eficientemente na arquitetura Intel Galileo de 32 bits; tais técnicas permitiram um ganho substancial em desempenho e em tamanho de código.
Organização do documento
O restante do texto está dividido como segue:
No Capítulo 2 são apresentadas, brevemente, as definições e propriedades das funções de resumo criptográfico, mostrando o nível de segurança atingível por essas funções e duas formas diferentes de construí-las; usando a construção Merkle-Damgård e usando a construção esponja. Adicionalmente, foram definidas duas famílias de funções de resumo criptográfico baseadas na construção esponja: a família SHA-3 e a família QUARK.
são apresentados alguns conjuntos de instruções vetoriais, com ênfase no conjunto de instrução AVX2, onde são destacadas as instruções mais relevantes para este trabalho e por fim é apresentada a plataforma Intel Galileo, que foi a arquitetura usada no contexto de dispositivos com recursos limitados.
No Capítulo 4 são detalhadas as cinco implementações em software do algoritmo SHA-3 propostas neste trabalho. A primeira usa registradores de 256 bits para imple-mentar uma instância do algoritmo SHA-3; a segunda também calcula uma instância, mas agora usando registradores de 128 bits. Também são usados registradores de 128 bits para calcular duas mensagens, independentes e de mesmo tamanho, concorrentemente; e as outras duas implementações usam registradores de 256 bits para calcular dois e quatro valores de resumo por vez.
No Capítulo 5 é apresentado como adaptar os algoritmo da família QUARK para viabilizar uma implementação eficiente em software para arquiteturas de 32 bits. Primei-ramente são apresentadas otimizações algorítmicas na função de permutação do algoritmo e em seguida é mostrado como tirar proveito do paralelismo inerente do algoritmo para implementá-lo eficientemente.
Finalmente, no Capítulo 6 são apresentadas as conclusões do trabalho e possíveis caminhos a serem seguidos.
Funções de Resumo Criptográfico
As funções de resumo criptográfico agem como uma função de compressão que mapeia uma cadeia de bits de tamanho arbitrário1 em uma cadeia de bits de tamanho fixo. Elas são muito usadas na criptografia moderna e seu uso é essencial em várias aplicações de segurança, tais como: esquemas de assinatura digital, verificação da integridade dos dados, geração de números pseudo aleatórios, geração de chaves, dentre outras. A cadeia de bits gerada é chamada de valor de resumo criptográfico ou digest e, para efeitos práticos, deve identificar a mensagem de forma única.
Definição 2.1 Uma função de resumo h : M → R, com |M | > |R|, mapeia uma cadeia de bits m ∈ M de tamanho finito e arbitrário em uma cadeia de bits r ∈ R de tamanho fixo n. Escreve-se, r = h(m).
Pode-se notar que mais de uma mensagem pode ser mapeada em um mesmo valor de resumo, visto que o domínio M de h é maior que sua imagem R. Porém, algumas aplica-ções, como as assinaturas digitais, por exemplo, requerem que seja computacionalmente inviável encontrar duas mensagens diferentes que gerem o mesmo valor de resumo; outras apenas necessitam que seja inviável encontrar uma mensagem dado o valor de resumo.
As primeiras definições, análises e construções de funções de resumo criptográfico podem ser encontradas nos trabalhos de Rabin [46], Yuval [52] e Merkle [37]. Rabin propôs um modelo baseado no algoritmo de encriptação DES; Yuval usou o paradoxo de aniversário para mostrar como encontrar colisões para uma função de resumo de n bits com 2n2 operações; e Merkle introduziu que esse tipo de função deveria ser resistente a
colisões, primeira pré-imagem e segunda pré-imagem.
2.1
Propriedades
As funções de resumo criptográfico, diferente dos algoritmos de encriptação, não possuem chaves secretas. Assim, para uma função de resumo ser considerada segura nas aplicações criptográficas ela deve ser resistente à colisão, à primeira imagem e à segunda pré-imagem. A seguir é apresentada uma definição informal sobre essas propriedades de segurança:
1Existe um limite físico do tamanho da mensagem, mas esse número é muito grande, por exemplo, no
SHA-2 é 264−1 bits.
h m =? r = h(m) (a) Pré-imagem. h m0 m1 =? h(m0) = h(m1) (b) Segunda pré-imagem. h m1 =? m0 =? h(m0) = h(m1) (c) Colisões.
Figura 2.1: As três propriedades de segurança de uma função de resumo, adaptado de [42].
Resistência à primeira pré-imagem
Dada uma função h : M → R e um valor de resumo r ∈ R, é computacionalmente inviável encontrar uma mensagem m ∈ M tal que h(m) = r.
Resistência à segunda pré-imagem
Dada uma função h : M → R e uma mensagem m0 ∈ M , é computacionalmente
inviável encontrar uma mensagem m1 ∈ M tal que m0 6= m1 e h(m0) = h(m1).
Resistência à colisão
Dada uma função h : M → R, é computacionalmente inviável encontrar duas mensagens m0, m1 ∈ M tal que m0 6= m1 e h(m0) = h(m1).
Essas propriedades são ilustradas na Figura 2.1, suas definições formais podem ser encon-tradas em [49].
Uma função de resumo resistente a colisões também é resistente à segunda pré-imagem, pois, se a partir de uma mensagem m0 um adversário conseguir encontrar uma mensagem
m1tal que m0 6= m1 e h(m0) = h(m1), ele conseguiria encontrar um par m0e m1 diferentes
que produzem o mesmo valor de resumo [36]. Entretanto, uma função de resumo ser resistente à colisão não garante que ela seja resistente à primeira pré-imagem [49].
2.2
Aplicações
As funções de resumo são amplamente utilizadas na construção de protocolos criptográfi-cos. Nesta seção, são apresentadas algumas aplicações que utilizam as funções de resumo.
2.2.1
Autenticador de mensagem
Um autenticador de mensagem é um mecanismo usado para verificar a integridade de uma mensagem. Esse mecanismo provê garantias de que uma informação recebida é exatamente a mesma que foi enviada, isto é, seu conteúdo não foi modificado durante a transmissão.
As funções de resumo podem ser usadas para prover a integridade de uma mensagem da seguinte forma: primeiramente o emissor calcula o valor de resumo r de uma mensagem m
Figura 2.2: Ataque do Homem no meio, adaptado de [51].
e envia a mensagem juntamente com o valor de resumo; o receptor aplica a mesma função de resumo sobre a mensagem m0 obtendo um valor de resumo r0; por fim, o receptor compara o valor de resumo calculado, r0, com o valor de resumo recebido, r, e caso esses valores não sejam iguais o receptor conclui que a mensagem ou o valor de resumo foram alterados em trânsito.
O valor de resumo deve ser transmitido de forma segura, isto é, um adversário não pode ter acesso a tal informação, pois ele poderia modificar o valor de resumo r pelo valor de resumo r0 = h(m0), onde m0 é a mensagem modificada pelo atacante, e enviar para o receptor a mensagem m0 e o valor de resumo r0. Este cenário é conhecido por ataque do homem no meio (Man-in-the-middle) e é ilustrado na Figura 2.2, no qual o emissor transmite uma mensagem concatenada com seu valor de resumo; no meio do caminho um atacante intercepta essa informação, altera a mensagem, calcula um novo valor de resumo, concatena esse valor à mensagem e envia a informação para o receptor; ao receber a mensagem, o receptor não consegue detectar qualquer modificação na mesma.
2.2.2
Assinatura digital
Outra aplicação importante, que é similar ao autenticador de mensagem, é a assinatura digital. Existem inúmeros métodos de assinaturas digitais que usam criptografia de chave pública; tais algoritmos, normalmente, possuem um alto custo computacional para assinar dados. Assim sendo, para gerar a assinatura digital de um documento, normalmente, calcula-se o valor de resumo do mesmo e então a assinatura é gerada a partir deste resumo.
Como exemplo de uso da assinatura digital pode-se imaginar uma empresa de software que deseja distribuir uma atualização autenticada, isto é, o cliente deve conseguir iden-tificar que a atualização do sistema é autêntica e uma terceira parte maliciosa não deve conseguir se passar pela empresa e fazer o cliente instalar uma atualização que não foi dis-ponibilizada pela empresa. Para fazer isso, a empresa pode assinar a atualização com sua chave privada e distribuir a assinatura juntamente com a atualização; assim, cada cliente pode usar a chave pública da empresa para verificar a autenticidade da atualização.
2.2.3
Armazenamento de senhas
A maioria dos sistemas usados atualmente necessitam da autenticação dos usuários; uma forma de implementar a autenticação é pelo uso de senhas, associando uma senha a cada usuário de modo que para ele ter acesso a algum recurso do sistema ele deve apresentá-la. Uma forma trivial de gerenciar as senhas do sistema é colocá-las em um único arquivo; no entanto, essa técnica apresenta problemas de segurança, pois caso um atacante tenha acesso a esse arquivo ele teria acesso a todas as senhas dos usuários.
Uma solução para esse problema é usar funções de resumo. Neste cenário, ao invés de guardar a senha propriamente dita, é guardado o valor de resumo e no momento da autenticação é computado o valor de resumo da senha do usuário e esse valor é comparado com o valor armazenado no arquivo de senhas; assim, mesmo que o atacante tenha acesso ao arquivo de senhas, por conta da propriedade de resistência a pré-imagem, ele não teria acesso às senhas dos usuários.
Uma informação útil que o atacante poderia ter acesso nesse cenário seria saber quais usuários possuem a mesma senha, visto que a aplicação de uma função de resumo em duas mensagens iguais deve resultar em duas saídas iguais. Para dificultar tal ataque, é introduzida uma palavra aleatória de n bits, denominada salt, que é utilizada juntamente com a senha para calcular o resumo. O acréscimo do salt baixa substancialmente a possibilidade de resumos idênticos no arquivo de senhas, pois agora mesmo que dois usuários possuam a mesma senha, se o valor do salt for diferente, o valor de resumo será diferente.
2.3
Segurança
2.3.1
Ataque do Aniversário
Esse ataque possui ênfase na propriedade de resistência à colisão, consistindo em achar duas mensagens diferentes que gerem o mesmo valor de resumo, isto é, encontrar duas mensagens m0, m1 ∈ M tal que m0 6= m1 e h(m0) = h(m1). Ele recebe este nome pois
baseia-se no paradoxo do aniversário [52], que é propriedade estatística frequentemente usada para realizar criptoanálise. O paradoxo do aniversário consiste em determinar a probabilidade de que, dado k pessoas em uma sala, existam duas pessoas que fazem aniversário no mesmo dia.
Encontrar colisões em uma função de resumo pode ser mapeado ao problema de en-contrar duas pessoas que fazem aniversário no mesmo dia [42]. Fazendo o mapeamento direto, o número de possíveis valores de saída passa de 365, quantidade de dias no ano, para 2n, onde n é o tamanho do valor de resumo. Neste cenário, deseja-se saber qual a quantidade de mensagens mi um atacante precisa testar até encontrar duas mensagens
diferentes que produzam o mesmo valor de resumo.
Em [42] Paar e Pelzl mostraram que o número de mensagens que precisam ser testadas para encontrar uma colisão equivale à raiz quadrada do número de possíveis valores de resumo, ou seja,√2n; assim, pode-se dizer que são necessárias 2n2 operações para encontrar
um nível de segurança de n2 bits.
Segundo Katz e Lindell [36] não existe qualquer ataque genérico à segunda e à pri-meira pré-imagem que possa ser executado com menos que 2n operações; na Tabela 2.1 é apresentado um resumo do nível de segurança atingível por uma função de resumo com saída de n bits.
Tabela 2.1: Nível de segurança atingível pelas funções de resumo. Propriedades Nível de segurança em bits
Primeira pré-imagem n
Segunda pré-imagem n
Colisão n2
2.3.2
Modelo do oráculo aleatório
O oráculo aleatório pode ser visto como uma caixa preta que recebe como entrada uma cadeia de bits de tamanho variado e retorna uma cadeia de bits de tamanho arbitrário. Qualquer um pode interagir com ele, seja uma entidade honesta ou um atacante, sendo que tal interação consiste de uma cadeia de bits x como entrada e uma cadeia de bits y como saída.
O modelo do oráculo aleatório foi introduzido em 1993 por Mihir Bellare e Phillip Rogaway em [6] e fornece uma metodologia formal que pode ser usada no projeto e validação de esquemas criptográficos; tal metodologia consiste em provar a segurança de um esquema considerando a existência de um oráculo aleatório e, ao implementar esse esquema no mundo real, substituir o oráculo aleatório por uma função de resumo apropriada [36]. Entretanto, Canetti, Goldreich e Halevi mostraram em [17] esquemas que são seguros no modelo oráculo aleatório, mas são inseguros para qualquer instância concreta do oráculo aleatório usando funções de resumo criptográfico.
2.4
Construção de funções de resumo
Nesta seção são apresentadas duas construções amplamente usadas no projeto de funções de resumo criptográfico: a construção Merkle-Damgård e a construção esponja.
2.4.1
Construção Merkle-Damgård
Uma forma comum de construir funções de resumo é projetar uma função de compressão resistente à colisão com tamanho de entrada fixo e então usar extensão de domínio para criar uma função com tamanho de entrada arbitrário [36]. De forma independente, Merkle e Damgård provaram em [39] e [20] que uma função de resumo criptográfico resistente à colisão pode ser construída a partir de uma função de compressão resistente à colisão e uma regra de preenchimento adequada. Assim, o esforço de construir uma função de
resumo resistente a colisões pode ser reduzido a produzir uma função de compressão segura. Esse método foi muito utilizado na construção de funções de resumo, incluindo os algoritmos MD5, SHA-1 e SHA-2.
Seja f : {0, 1}2n → {0, 1}n uma função de compressão com 2n bits de entrada e n bits
de saída. A construção de uma função de resumo criptográfico h que recebe como entrada uma mensagem m de b bits seguindo a metodologia Merkle-Damgård é descrita a seguir: 1. Divida a mensagem m em k blocos (x1, . . . , xk) de n bits, completando o último
bloco com bits zero, caso necessário.
2. Defina um bloco adicional xk+1 contendo a representação binária de b, ou seja, o
tamanho da mensagem.
3. Seja z0 = 0n(O valor de z0 é conhecido por vetor de inicialização e pode ser trocado
por qualquer constante).
4. Para 1 ≤ i ≤ k + 1, calcule zi = f (zi−1||xi).
5. O valor de resumo da mensagem m (de n bits) é calculado como h(m) = zk+1 =
f (zk||xk+1).
2.4.2
Construção Esponja
A construção esponja foi proposta por Bertoni, Daemen, Peeters e Van Assche no Ecrypt Hash Workshop em maio de 2007 [10]. Ela é um modo de operação baseado em uma permutação f de tamanho fixo e uma regra de preenchimento p que constrói uma função, denominada função esponja, com tamanho variado de entrada e um tamanho arbitrário de saída. A função esponja imita o comportamento de um oráculo aleatório, exceto pelo fato de que na construção esponja podem ocorrer colisões internas, uma vez que ela possui uma espaço de memória finito [10].
Do ponto de vista prático, a construção esponja pode ser usada na elaboração de muitos algoritmos simétricos, tais como: funções de resumo, geração de números pseudo-aleatórios, derivação de chaves, encriptação, autenticador de mensagens (MAC - Message Authentication Code) e encriptação autenticada [27]. Essa vasta aplicabilidade fornece ao usuário uma gama de funcionalidades a partir de uma função de permutação, facilitando assim o reuso de implementações da função de permutação.
Do ponto de vista teórico, uma função esponja aleatória (que é uma instância da construção esponja com a permutação f escolhida aleatoriamente dentre um conjunto de permutações) é uma alternativa ao modelo de oráculo aleatório para expressar provas de segurança [27], uma vez que foi provado em [8] que uma função esponja aleatória é tão forte quanto um oráculo aleatório, exceto pelo efeito introduzido pela memória finita.
A construção esponja [10] define uma função ESPONJA[f , pad, r] com domínio {0, 1}∗ e codomínio {0, 1}∞ usando uma permutação de tamanho fixo f , uma regra de preenchi-mento pad e uma taxa de bits r. A partir desta função, uma saída de tamanho finito pode ser obtida pelo truncamento dos primeiros l bits. Uma instância da construção esponja é chamada de função esponja.
m
2m
1m
3m
kP
P
P
P
...
h
1h
2h
3P
P
P
... h
nM
S
Figura 2.3: Construção esponja: Z = ESPONJA[f , pad, r](M, d).
A função esponja opera sobre um estado de b = r + c bits, onde r é a taxa de bits e c é a capacidade. Primeiramente, todos os bits do estado são inicializados com zeros. A regra de preenchimento pad é aplicada sobre a mensagem de entrada e posteriormente a mensagem é dividida em k blocos de r bits; feito isso, o processamento é composto por duas fases: a fase de absorção e a fase de extração.
Na fase de absorção é aplicado uma operação XOR entre um bloco de r bits da mensagem de entrada com os primeiros r bits do estado atual. Então, o estado é atualizado por meio de uma permutação f ; esse processo é aplicado para cada um dos k blocos da mensagem.
Na fase de extração os primeiros r bits do estado são usados como saída; se o tamanho de l for maior que o tamanho de r, o estado é processado novamente pela permutação f e os r bits retornados são concatenados com os r bits retornados previamente; esse processo é repetido até que os l bits requeridos sejam extraídos.
Os últimos c bits do estado nunca são afetados diretamente pelos blocos de entrada na fase de absorção e nem extraídos na fase de extração; o parâmetro c determina a segurança atingível pela construção [8]. A construção esponja é ilustrada na Figura 2.3 e seu pseudo-código é apresentado no Algoritmo 1.
2.5
Funções de resumo baseadas na construção esponja
Nesta seção são apresentadas duas famílias de funções de resumo criptográfico baseadas na construção esponja: a família SHA-3 e a família QUARK.
2.5.1
SHA-3
A família de funções de resumo criptográfico SHA-3 baseia-se na construção esponja definida sobre a função de permutação Keccak-p[1600,24]; ela está definida no documento oficial FIPS 202 [40], o qual descreve os detalhes da família SHA-3 e padroniza quatro funções de resumo criptográfico: SHA3-224, SHA3-256, SHA3-384 e SHA3-512; e duas funções de resumo com saída variável, Extendable Output Functions (XOF), chamadas
Entrada: Cadeia de dados M e um inteiro não negativo d. Saída: Cadeia de dados Z, tal que |Z| = d.
1: P = M k pad(r, |M |) 2: n = |P |/r
3: c = b − r
4: Seja P = P0 k . . . k Pn−1 a divisão de P em blocos de tamanho r.
5: S = 0b
6: for all i tal que 0 ≤ i < n do 7: S = f (S ⊕ (Pi k 0c))
8: end for
9: Seja Z uma cadeia de dados vazia. 10: while d > |Z| do
11: Z = Z k Truncr(S)
12: Se d > |Z| então S = f (S) 13: end while
14: Retorno: Truncd(Z)
Algoritmo 1: ESPONJA[f , pad, r](M, d)
SHAKE128 e SHAKE256. A família SHA-3 baseada no algoritmo Keccak é apresentada a seguir.
Permutações Keccak-p
Uma permutação Keccak-p que possui nr rodadas e trabalha sobre um estado S de
b bits, é denotada por Keccak-p[b, nr]; a permutação é definida para quaisquer b ∈
{25, 50, 100, 200, 400, 800, 1600} e qualquer inteiro positivo nr. Uma rodada da
permuta-ção Keccak-p consiste de uma sequência de cinco transformações, chamadas de etapas de mapeamento.
O estado interno do algoritmo pode ser organizado em uma matriz 5 × 5 × w, onde w = b/25 pode ser visto como o tamanho das palavras do estado em bits. As 25 palavras de w bits de um estado S são denotadas por si para 0 ≤ i < 25 como pode ser visto a
seguir: S = s0 s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 s12 s13 s14 s15 s16 s17 s18 s19 s20 s21 s22 s23 s24 ; S[x, y] = s5x+y para 0 ≤ x, y < 5. (2.1)
Etapas de mapeamento. As cinco etapas de mapeamento usadas durante uma rodada da função Keccak-p[b, nr] são denotadas por θ, ρ, π, χ e ι. O algoritmo para cada uma
dessas etapas tem como entrada um estado S e como saída esse estado atualizado. A etapa de mapeamento ι é a única que possui uma entrada adicional, um inteiro irchamado índice
da rodada.
O tamanho do estado é um parâmetro que está omitido da notação, pois b sempre é especificado quando as etapas de mapeamento são chamadas; visando simplificar a
notação, todas as operações lógicas e de rotação usadas nas etapas de mapeamento são aplicadas sobre palavras de tamanho w.
As especificações das etapas de mapeamento são apresentadas a seguir:
• Etapa de mapeamento θ. O efeito da etapa θ é aplicar uma operação XOR entre cada palavra do estado com a paridade de duas colunas do estado.
• Etapa de mapeamento ρ. Nesta etapa cada palavra do estado é rotacionada uma quantidade fixa de ri bits. Na Equação 2.2 pode-se ver a matriz R, onde cada
elemento ri representa a quantidade de bits que a palavra si será rotada.
R = 0 1 62 28 27 36 44 6 55 20 3 10 43 25 39 41 45 15 21 8 18 2 61 56 14 ; R[x, y] = r5x+y para 0 ≤ x, y < 5. (2.2)
• Etapa de mapeamento π. O efeito da etapa de mapeamento π é embaralhar as palavras do estado. Ela promove uma difusão a longo prazo dentro das rodadas, a fim de evitar que padrões sejam explorados em determinados ataques.
• Etapa de mapeamento χ. O efeito da etapa de mapeamento χ é aplicar uma operação XOR em cada palavra do estado si com a saída de uma função não linear
aplicada a duas palavras da mesma linha de si.
• Etapa de mapeamento ι. A etapa de mapeamento ι consiste na aplicação de uma operação XOR entre o elemento s0 com um valor contante rc(ir), onde os
valores de rc são definidos em [40] e são gerados a partir de um Linear Feedback Shift Register (LFSR). O efeito desta etapa consiste em modificar alguns bits da palavra s0 de acordo com o índice da rodada ir. As outras 24 palavras do estado
nunca são afetadas diretamente pela etapa de mapeamento ι.
Dado um estado S e o índice de uma rodada ir, uma função Rnd é a transformação
que resulta na aplicação das etapas de mapeamento θ, ρ, π, χ e ι, da seguinte forma: Rnd(S, ir) = ι(χ(π(ρ(θ(S)))), ir). (2.3)
A permutação Keccak-p[b, nr] consiste de nr iterações de Rnd. Um pseudocódigo da
função Rnd é apresentado no Algoritmo 2.
A família Keccak
Keccak é uma família de funções de resumo originalmente definida em [9]. A seguir, são descritas a permutação fundamental e a regra de preenchimento usadas pela família Keccak:
Entrada: Estado S e ir o índice da rodada.
Saída: Estado S atualizado S = Rnd(S). Etapa de mapeamento θ
1: C[y] = S[0, y] ⊕ S[5, y] ⊕ S[10, y] ⊕ S[15, y] ⊕ S[20, y] para 0 ≤ y < 5 2: D[x] = C[(x − 1) mod 5] ⊕ (C[(x + 1) mod 5] ≪ 1) para 0 ≤ x < 5 3: S[x, y] = S[x, y] ⊕ D[x] para 0 ≤ x, y < 5
Etapa de mapeamento ρ
4: S[x, y] = (S[x, y] ≪ R[x, y]) para 0 ≤ x, y ≤ 4 Etapa de mapeamento π
5: S0[(2y + 3x) mod 5, x] = S[x, y] para 0 ≤ x, y ≤ 4 Etapa de mapeamento χ
6: for all (x, y) tal que 0 ≤ x, y < 5 do
7: T = (S0[x, (y + 2) mod 5] ∧ (¬S0[x, (y + 1) mod 5]) 8: S[x, y] = S0[x, y] ⊕ T 9: end for Etapa de mapeamento ι 10: s0 = s0⊕ rc(ir) 11: Retorno: S. Algoritmo 2: Rnd(S).
• Regra de preenchimento pad10∗1. A família Keccak usa a regra de
preenchi-mento multi-taxa, denotada por pad10∗1, que recebe como entrada o tamanho da mensagem m e a taxa de bits r, retornando uma cadeia binária Z = 1||0j||1, onde
j = −(m + 2) mod r.
• Especificação de Keccak[c]. A família de funções esponjas Keccak com a função de permutação Keccak-p[b, nr] e a regra de preenchimento pad10∗1 está definida
para quaisquer par de taxa de bits r e capacidade c tal que r + c ∈ {25, 50, 100, 200, 400, 800, 1600}.
Quando o tamanho do estado é restrito a b = 1600, a família Keccak é denotada Keccak[c]; nesse caso, r é determinado pela escolha do parâmetro c. Em particular,
Keccak[c] = ESPONJA[Keccak-p[1600, 24], pad10∗1, 1600 − c]. Assim, dada uma mensagem M e um tamanho de saída d, tem-se
Keccak[c](M, d) = ESPONJA[Keccak-p[1600, 24], pad10∗1, 1600 − c](M, d).
Especificações da família SHA-3
As quatro funções de resumo SHA-3 são definidas a partir da aplicação da função Keccak[c] sobre uma mensagem M concatenada com dois bits e a especificação do tamanho de saída, como pode ser visto a seguir:
SHA3-224(M) = Keccak[448](M||01,224) SHA3-256(M) = Keccak[512](M||01,256) SHA3-384(M) = Keccak[768](M||01,384) SHA3-512(M) = Keccak[1024](M||01,512).
Em cada caso, a capacidade sempre é o dobro do tamanho da saída; os dois bits adici-onados à mensagem (01) servem para dar suporte à separação de domínio, por exemplo, distinguir as mensagens processadas pela função de resumo SHA-3 e pelas funções com saída variável.
As duas funções com saída variável, SHAKE128 e SHAKE256 são definidas a partir da função Keccak[c], uma mensagem M concatenada com quatro bits e pela especificação do tamanho de saída d, como é apresentado a seguir:
SHAKE128(M) = Keccak[256](M||1111,d) SHAKE256(M) = Keccak[512](M||1111,d).
Os primeiros dois bits servem para dar suporte a separação de domínio e os dois bits adicionais (11) são usados para prover compatibilidade ao esquema Sakura [11]. Esse esquema facilitará o desenvolvimento de uma extensão dessas funções, chamada resumos em árvore [38], onde a mensagem pode ser processada em paralelo, conseguindo assim computar mensagens longas de forma mais eficiente.
Segurança
Usualmente, as aplicações requerem que as funções de resumo sejam resistentes contra ataques baseados em colisões, pré-imagem e segunda pré-imagem. Um resumo desses parâmetros para todas as funções da família SHA pode ser encontrado na Tabela 2.2. Para o nível de segurança contra ataques à segunda pré-imagem em uma mensagem M , a função L(M ) é definida como dlog2(len(M )/B)e, onde B é o tamanho do bloco da função, por exemplo, 512 bits para SHA-1, SHA-224 e SHA-256 e 1024 bits para o SHA-512.
As quatro funções de resumo SHA-3 são alternativas à funções da família SHA-2, provendo um nível de segurança pelo menos igual ao da família SHA-2 contra os ataque à colisões, à primeira pré-imagem e à segunda pré-imagem. As duas funções SHA-3 com saída variável foram projetadas para serem resistentes à colisão, à primeira pré-imagem e à segunda pré-imagem, e a outros ataque que sejam resistentes por uma função aleatória de mesmo tamanho, até o nível de segurança de 128 bits para o SHAKE128 e 256 bits para o SHAKE256. Essas funções, assim como uma função aleatória, não conseguem prover um nível de segurança maior que d/2 bits contra ataques baseados em colisões e d bits contra ataques de pré-imagem e de segunda pré-imagem [40].
2.5.2
QUARK
A família de funções de resumo leve, QUARK, foi apresentada pela primeira vez em 2010 por Aumasson, Henzen, Meier e Naya-Plasencia em [2]. Em 2013, uma versão estendida foi publicada em [3], onde foi feita uma pequena correção no tamanho da saída do algoritmo, de modo que o parâmetro n foi incrementado para corrigir uma falha na análise inicial.
Funções Tamanho de saída
Nível de segurança em bits
Colisão Pré-imagem 2a Pré-imagem
SHA-1 160 <80 160 160-L(M ) SHA-224 224 112 224 min(224, 256-L(M )) SHA-512/224 224 112 224 224 SHA-256 256 128 256 256-L(M ) SHA-512/256 256 128 256 256 SHA-384 384 192 384 384 SHA-512 512 256 512 512-L(M ) SHA3-224 224 112 224 224 SHA3-256 256 128 256 256 SHA3-384 384 192 384 384 SHA3-512 512 256 512 512
SHAKE128 d min(d/2, 128) ≥ min(d, 128) min(d, 128) SHAKE256 d min(d/2, 256) ≥ min(d, 256) min(d, 256)
Tabela 2.2: Nível de segurança das funções SHA-1, SHA-2 e SHA-3 [40].
Como na família SHA-3, o QUARK também baseia-se na construção esponja; entre-tanto, eles possuem objetivos diferentes, uma vez que a família SHA-3 foi projetada para ser um algoritmo de propósito geral e a família QUARK foi pensada para arquiteturas com recursos limitados. A família de funções de resumo leve QUARK é apresentada a seguir.
Permutação P
A permutação do QUARK foi inspirada no algoritmo de cifra de fluxo Grain [30] e no ci-frador de bloco KATAN [21]. Internamente, a função P é composta por três Registradores de Deslocamento com Retroalimentação (FSRs), dos quais um é linear (L) e dois são não lineares (X e Y ), o diagrama que representa o comportamento interno desta permutação é mostrado na Figura 2.4.
A permutação P é a função crítica em termos de desempenho do algoritmo QUARK; sua entrada é um estado de b bits e sua saída é o estado atualizado. A computação da permutação P pode ser dividida em três etapas básicas, que são:
1. Inicialização: Nesta etapa, o registrador X é inicializado com os primeiros 2b bits do estado S, o vetor Y com os últimos b2 bits e o vetor L, de tamanho q = dlog 4be bits, é inicializado com todos os bits um.
S =X||Y.
L =(1, 1, . . . , 1).
2. Atualização: Esta etapa é executada 4b vezes. Em cada iteração, os i-ésimos bits de X, Y e L, denotados por Xi, Yi e Li, são atualizados como segue:
Xi =f (X) ⊕ h(X, Y, L) ⊕ Yi.
Yi =g(Y ) ⊕ h(X, Y, L).
Li =p(L).
3. Saída: Ao final da etapa de atualização, a saída é do estado S é composta pelos valores de X e Y , sendo que os primeiros 2b bits do estado são atualizados com os valores contidos em X e os últimos 2b bits são atualizados com os valores contidos em Y .
Os valores de inicialização do LFSR L são constantes; desse modo, seus valores para cada iteração podem ser pré-computados antes da execução do algoritmo. O algoritmo usado para calcular a permutação P pode ser visto no Algoritmo 3. As definições das funções f , g e h são diferentes para cada uma das instâncias e estão definidas no Apêndice A.1.
Figura 2.4: Permutação P .
Construção Esponja
Como já mencionado, a função esponja opera sobre um estado de b = r + c bits. Dado um estado S2 (de b bits) e uma mensagem M (de tamanho arbitrário), a construção esponja 2O estado de cada instância da família QUARK é inicializado com os primeiros b bits do valor de
processa a mensagem em três etapas:
1. Inicialização. A mensagem é completada pela aplicação de uma regra de pre-enchimento pad, que concatena o bit “1” a mensagem M seguido por j bits zeros M = M ||10j, onde j = −(|M | + 1) mod r.
2. Absorção. A mensagem é dividida em k blocos de r bits (M1, . . . , Mk). Então, em
cada iteração o estado é atualizado pela computação de uma operação XOR entre um bloco da mensagem com os últimos r bits do estado atual. Uma vez atualizado o estado, ele é processado pela permutação P ; esse processamento é aplicado para cada um dos k blocos da mensagem.
3. Extração. Os últimos r bits do estado são usados como saída; se o tamanho da saída for maior que r, o estado é processado novamente pela permutação P e os r bits retornados são concatenados com os r bits retornados previamente; esse processo é repetido até que os bits requeridos sejam extraídos.
Entrada: Um estado S de b bits.
Saída: Um estado S atualizado S = P (S). 1: X = (S0, . . . , S(b/2)−1) 2: Y = (Sb/2, . . . , Sb−1) 3: for j=0 to 7 do 4: for i=0 to (4b8) − 1 do 5: h0 = h(X, Y, Lj∗4b 8+i) 6: Xi = f (X) ⊕ h0 ⊕ Yi 7: Yi = g(Y ) ⊕ h0 8: end for 9: end for 10: (S0, . . . , S(2b−1) = X 11: (Sb 2, . . . , Sb−1) = Y 12: Retorno: S.
Algoritmo 3: Função de Permutação P
Na fase de absorção da família QUARK é aplicada uma operação XOR entre os blocos da mensagem com os últimos r bits do estado, no lugar dos primeiros r bits como definido pela construção esponja em [10]. Segundo [3], usar os últimos r bits na absorção para a família QUARK permite uma melhor difusão, pois a diferença introduzida nos últimos bits permaneceram nos registradores por mais tempo, uma vez que é usado FSRs. Na fase de extração, os bits extraídos também são os últimos r bits do estado; isso acontece porque esses são os últimos bits calculados pela permutação, extrair os primeiros bits tornaria as últimas iterações desnecessárias.
Família QUARK
A família de funções de resumo leve QUARK é composta por três diferentes instâncias -U-QUARK, D-QUARK e S-QUARK (definidas no Apêndice A.1) - com diferentes valores
Tabela 2.3: Parâmetros da família QUARK.
Instância Taxa de bits (r) Capacidade (c) Rodadas (4b) Valor de saída (n) Grau de Paralelismo
U-QUARK 8 128 544 136 8
D-QUARK 16 160 704 176 8
S-QUARK 32 224 1024 256 16
de capacidade c, taxa de bits r, valor de saída n e as funções f , g e h. Os parâmetros para cada uma das instâncias são mostrados na Tabela 2.3.
Segurança
Como a família SHA-3, a segurança do QUARK baseia-se nas propriedades da construção esponja e em uma permutação. Um resumo do nível de segurança da famílias QUARK para ataques de colisão, pré-imagem e segunda pré-imagem é apresentado na Tabela 2.4.
Funções Tamanho de saída
Nível de segurança em bits Colisão Pré-imagem 2a Pré-imagem
U-QUARK 136 64 64 128
D-QUARK 176 80 80 160
S-QUARK 256 112 112 224
Tabela 2.4: Nível de segurança da família QUARK [3].
Em [3] foi aplicado um conjunto de técnicas de criptoanálise sobre todos os membros da família QUARK e a permutação P se mostrou indistinguível de uma permutação aleatória, para todas as instâncias, a partir de 25% das rodadas.
2.6
Resumo
Neste capítulo, foram apresentados os conceitos básicos das funções de resumo cripto-gráfico e suas principais aplicações; adicionalmente, foram descritas duas construções amplamente usadas no projeto destas funções: a construção Merkle-Damgård e a cons-trução esponja. Finalmente, duas famílias de funções de resumo baseadas na conscons-trução esponja foram detalhadas - a família SHA-3 e a família QUARK.
Micro Arquitetura Intel
Neste Capítulo são apresentados os conjuntos avançados de instruções vetoriais presentes nos processadores da Intel relevantes para este trabalho e a primeira arquitetura da Intel direcionada para a internet das coisas, Intel Galileo.
3.1
Instruções vetoriais
No fim da década de 1990, os fabricantes de processadores concentraram seus esforços em explorar o paralelismo de dados ao invés do paralelismo de instruções, como era feito nas arquiteturas RISC. Para isso, foram incorporadas unidades funcionais capazes de pro-cessar um conjunto de dados com a execução uma única instrução; esse processamento encaixa-se no paradigma conhecido como SIMD (Single Instruction Multiple Data), in-troduzido em [24].
Na arquitetura Intel, um dos primeiros conjuntos de instruções a implementar o pa-radigma SIMD foi lançado em 1997, sendo conhecido por MMX (Multimedia eXtensi-ons) [18]. O MMX adicionou registradores de 64 bits e instruções vetoriais que habili-tavam o processamento de duas operações de 32 bits simultaneamente; nessa época as arquiteturas possuíam registradores nativos de 32 bits. Essas instruções definem a se-mântica do conteúdo do registrador, isto é, um registrador de 64 bits poderia ser operado como um vetor de oito palavras de 8 bits, um vetor de quatro palavras de 16 bits, um vetor de duas palavras de 32 bits ou como um vetor de 64 bits.
O conjunto de instruções MMX possui duas limitações principais, que são: ele só trabalha com inteiros e reusa os registradores de ponto flutuante, impossibilitando assim trabalhar concorrentemente com operações de ponto flutuante e vetoriais. Para sanar esses problemas a Intel lançou em 1999 o SSE (Streaming SIMD Extensions) que adicionou oito registradores de 128 bits (denotados XMM0-XMM7) e incluiu instruções para dar suporte à computação de aritmética de ponto flutuante.
No ano 2000 o tamanho dos registradores nativos aumentou de 32 bits para 64 bits e o número de registradores vetoriais da arquitetura Intel 64 (denotados XMM0-XMM15) foi duplicado. Nos anos seguintes o conjunto de instruções SSE foi evoluindo com o lançamento dos novos conjuntos SSE2, SSE3, e SSE4. O SSE2 foi lançado para dar suporte às operações de aritmética inteira. Os outros conjuntos, entretanto, foram incorporando
Figura 3.1: Conjunto de registradores vetoriais e de propósito geral da arquitetura Intel.
outros tipos de instruções vetoriais; desse modo, começaram a surgir instruções para manipulação de cadeias de caracteres, permutação de palavras dentro dos registradores e códigos de correção de erros.
Em 2011 foi lançado o conjunto de instruções AVX (Advanced Vector Extensions), que trouxe contribuições relevantes à arquitetura. Ele incluiu registradores de 256 bits, chamados YMM, que encontram-se sobrepostos sobre os registradores XMM; o conjunto de registradores, vetoriais e de propósito geral, suportados pela arquitetura Intel podem ser vistos na Figura 3.1. Além disso, o AVX introduziu um novo formato de codificação que permite utilizar código de montagem de três operandos, tornando a atribuição de registradores mais flexível. O mais recente conjunto de instruções vetoriais é a segunda versão do AVX, chamada AVX2. Esse conjunto será detalhado na seção seguinte.
Como foi apresentado, as primeiras instruções vetoriais foram direcionadas ao proces-samento gráfico, esse cenário vem mudando nos últimos anos e agora está disponível uma vasta diversidade de instruções.
3.1.1
O conjunto de instruções AVX2
A microarquitetura Haswell foi lançada no início de 2013 e trouxe consigo uma série de inovações para acelerar a execução dos programas. Dentre as quais destacam-se: (i) a inclusão de mais duas portas de execução; (ii) um novo multiplicador inteiro; (iii) novas instruções de manipulação de bits; (iv) o aumento da largura de banda da cache de dados, que foi dobrada para permitir duas leituras e uma escrita por ciclo de relógio. No entanto, o suporte ao conjunto de instruções vetoriais AVX2 é a característica mais relevante desta microarquitetura.
O AVX2 contém novas instruções que expandem a computação de aritmética inteira nos registradores de 256 bits; pois o conjunto AVX continha apenas instruções para a aritmética de ponto flutuante. Além disso, AVX2 conta com instruções de permutação e combinação, que permitem movimentar as palavras contidas nos registradores vetoriais,
entre outras características. A seguir, são destacadas algumas instruções que fazem parte do conjunto AVX2 e que foram relevantes para este trabalho:
1. Acesso a memória:
(a) LOAD/STORE. Essas funções permitem carregar/armazenar um conjunto de da-dos de/para um endereço de memória de/para um registrador vetorial. Vale a pena mencionar que o endereço de memória deve estar alinhado para 32 bits, ou seja, o valor do endereço deve ser um múltiplo de 32, caso contrário o desempenho da aplicação é afetado.
(b) BRCAST. Essa instrução replica um valor de 8, 16, 32, 64 ou 128 bits de um registrador vetorial (ou endereço de memória) em um registrador vetorial.
0x75
0x75 0x75 0x75 0x75
2. Funções lógicas:
(a) XOR/AND/OR/ANDNOT. Essas funções calculam operações lógicas entre dois re-gistradores de 256 bits A e B produzindo um novo registrador C.
A = a3 a2 a1 a0
⊕ ⊕ ⊕ ⊕ B = b3 b2 b1 b0
= = = =
C = c3 c2 c1 c0
(b) SHL/SHR. Para cada palavra armazenada em um registrador vetorial A, essas instruções deslocam para esquerda ou direta, respectivamente, uma quantidade fixa de bits b, produzindo um novo registrador C.
A =
C =
a3 a2 a1 a0
b b
c3 c2 c1 c0
(c) SHLV/SHRV. Essas instruções potencializam o processamento paralelo das truções de deslocamento, pois ao invés de fazer um deslocamento fixo, a ins-trução recebe, além do registrador alvo A, um segundo registrador B contendo a quantidade de bits a serem deslocados; desta forma, cada palavra de A será deslocada de acordo com às quantidades especificadas no registrador B, pro-duzindo um novo registrador C.
A = B = C = a3 a2 a1 a0 b3 b2 b1 b0 = = = = c3 c2 c1 c0
3. Permutações internas no registrador:
(a) PERM. Instruções para embaralhar a posição das palavras dentro do registrador são chamadas de permutações. No entanto, nas primeiras versões do SSE e AVX, foram também conhecidos como shuffle ou shuffling. Essas instruções visualizam um registrador A como palavras de 8, 16, 32 ou 64 bits e as em-baralham de acordo com uma máscara M , produzindo um novo registrador C. A = M = C = a3 a2 a1 a0 [2] [0] [3] [1] = = = = a2 a0 a3 a1 4. Combinação de registradores:
(a) UPCK. Essas instruções produzem um registrador C a partir da intercalação das palavras da parte alta/baixa de dois registradores A e B. Ela está presente tanto para registradores de 128 bits como para registradores de 256 bits.
A = B = C = a1 a0 b1 b0 a0 b0 A = B = C = a3 a2 a1 a0 b3 b2 b1 b0 a0 b0 a2 b2
(b) BLEND. Esse tipo de instrução preenche um registrador C a partir de palavras contidas em dois registradores A e B; a escolha está baseada no valor de uma máscara binária M . A = B = M = C = a3 a2 a1 a0 b3 b2 b1 b0 0 1 1 0 = = = = a3 b2 b1 a0 mask
(c) PRBLEND. Essa instrução combina partes de 128 bits de dois registradores de 256 bits A e B em um novo registrador C; a escolha está baseada no valor de uma máscara binária M .
A = B = M = C = a1 a0 b1 b0 [3] [0] b1 a0
(d) ALIGNR. Essa instrução usando registradores de 128 bits concatena dois regis-tradores A e B e desloca b bytes, produzindo um registrador intermediário de 256 bits I, os 128 bits menos significativos de I são armazenado no registrador destino C. No conjunto AVX2 usando registradores de 256 bits o comporta-mento dela é diferente do esperado, pois esperava-se que ela concatenasse dois registradores de 256 bits, deslocasse b bytes e armazenasse os 256 bits menos significativos em um novo registrador. Entretanto, ao usar registradores de 256 bits ela se comporta como duas instruções de 128 bits operando sobre as partes menos e mais significativas dos registradores de 256 bits A e B.
A = B = I = C = a1 a0 b1 b0 a1 a0 b1 b0 b c1 c0 A = B = I0 = I00 = C0 = C00= C = a3 a2 a1 a0 b3 b2 b1 b0 a3 a2 b3 b2 a1 a0 b1 b0 b b c3 c2 c1 c0 = c3 c2 c1 c0 5. Conversão de elementos:
(a) CAST. São pseudo-instruções que mudam apenas a semântica do conteúdo dos registradores. A maioria dessas instruções são tratadas diretamente pelos com-piladores e não geram nenhuma instrução em código de montagem.
(b) EXTRACT. Essa instrução retorna um registrador de 128 bits composto pela parte alta (ou baixa) de um registrador de 256 bits. Diferente das instruções de CAST, esta é uma instrução explicita que é codificada em linguagem de montagem. (c) INSERT. Essa instrução tem a funcionalidade inversa à instrução EXTRACT, pois
ela permite colocar o conteúdo de um registrador de 128 bits na parte alta (ou baixa) de um registrador de 256 bits.
Com as instruções acima apresentadas, é possível criar as seguintes operações que serão úteis no decorrer do texto:
1. ROT. Essa operação não está presente no conjunto de instruções AVX2, mas pode ser emulada a partir de três instruções, a seguir é apresentado a definição dessa instrução onde X é um registrador de 128 ou 256 bits e b é a quantidade de bits que cada palavra de w bits do vetor será rotada:
ROT(X, b) = XOR(SHL(X, b), SHR(w − b)).
2. ROTV. Essa operação é ainda mais poderosa que a anterior, pois ao invés de fazer a rotação de um valor fixo, ela recebe, além do registrador alvo, um segundo registra-dor contendo a quantidade de bits a serem rotados por cada palavra; desta forma, cada palavra do vetor alvo será rotada de acordo com as quantidades especificadas no segundo registrador, sua definição é apresentada a seguir, onde X é o registrador alvo e B é o registrador contendo os valores a serem rotados por cada palavra de w bits:
ROTV(X, B) = XOR(SHL(X, B), SHRV(SUB(W, B), X)).
As instruções aqui apresentadas não representam todas as instruções pertencentes ao conjunto de instruções AVX2; é recomendado consultar [19] para informação adicional sobre esse conjunto. Na Tabela 3.1 são mostradas as latências, a vazão e as portas de execução das instruções AVX2 acima citadas; essas informações foram extraídas do relatório técnico produzido por Agner Fog [25].
Como pode ser visto, as instruções que transitam entre as partes altas e baixas dos registradores possuem uma latência de pelo menos 3 ciclos. Essa característica foi encon-trada não apenas nesse conjunto limitado de instruções, mas em todas as instruções do conjunto AVX2 que movem dados da parte baixa/alta para a alta/baixa.
3.2
Instruções de manipulação de bits
Os conjuntos de instruções de manipulação de bits BMI1 e BMI2 foram introduzidos na microarquitetura Haswell com o intuito de acelerar a manipulação de bits. As instruções desses conjuntos não são vetoriais e operam apenas com os registradores de propósito geral. A seguir são apresentadas as instruções presentes nesses conjuntos:
1. ANDN. Aplica uma operação lógica AND entre um registrador A e o inverso de um registrador B, salvando o resultado em um registrador C.
2. BEXTR. Extrai bits contínuos de um registrador. Essa instrução recebe como entrada a posição do primeiro bit a ser extraído i e a quantidade de bits a serem extraídos n, retornando os n primeiros bits a partir de i.
3. BLSI. Retorna o bit menos significativo ativo. Dado um registrador A como entrada a instrução BLSI retorna (−A) AND A.
4. BLSMSK. Ativa todos os bits, até o bit menos significativo, de um registrador A. Dado um registrador A como entrada a instrução BLSMSK retorna (A − 1) XOR A.
Instrução vetorial Latência Vazão Porta de execução 0 1 2 3 4 5 6 7 LOAD 3 2 × × STORE 3 1 × × × × BRCAST 5 2 × × ADD/SUB 1 2 × × MUL 5 1 × XOR 1 3 × × × SHL/SHR 1 1 × SHLV SHRV 2 0.5 × × PERM 3 1 × BLEND 1 3 × × × PRBLEND 3 1 × ALIGNR 1 1 × UPCK 1 1 × CAST 0 -EXTRACT 3 1 × INSERT 3 1 ×
Tabela 3.1: Latência, vazão (instruções executadas por ciclo quando não há dependências) e portas de execução de algumas instruções AVX2.
5. BLSR. Copia todos os bits de um registrador A, zerando o bit menos significativo. Dado um registrador A como entrada a instrução BLSR retorna (A − 1) AND A. 6. BZHI. Zera todos os bits mais significativos a partir de um bit especificado i. 7. PDEP. Usa uma máscara para determinar em quais posições de um registrador de
saída C serão armazenados os primeiros n bits menos significativos, os outros bits são preenchidos com o valor zero; seu funcionamento é ilustrado a seguir, onde cada bloco corresponde um bit de um registrador de 64 bits:
A = a63 a62 a61 a60 . . . a4 a3 a2 a1 a0
M = 0 1 0 1 0.. ..0 1 0 1 0 0
= = = = = = = = = = =
C = 0 a3 0 a2 0.. ..0 a1 0 a0 0 0
8. PEXT. Armazena continuamente n bits, determinados por uma máscara M , de um registrador A em um registrador C, os outros bits são preenchidos com o valor zero; seu funcionamento é ilustrado a seguir, onde cada bloco corresponde um bit de um registrador de 64 bits:
A = a63 a62 a61 a60 . . . a4 a3 a2 a1 a0
M = 0 1 0 1 0.. ..0 1 0 1 0 0
= = = = = = = = = = =
C = 0 0 0 0 0.. ..0 0 a62 a60 a4 a2
9. RORX, SARX/SHLX/SHRX. São instruções de rotação e de deslocamento. 10. TZCNT. Retorna o número de zeros a direita.
Na Tabela 3.2 são mostradas as latências, a vazão e as portas de execução das ins-truções dos conjuntos BMI1 e BMI2 acima citadas; essas informações foram extraídas do relatório técnico produzido por Agner Fog [25].
Instrução vetorial Latência Vazão Porta de execução
0 1 2 3 4 5 6 7 SHLX SHRX SARX 1 2 × RORX 1 2 × TZCNT 3 1 × ANDN 1 2 × × BLSI 1 2 × × BSLR 1 2 × × BLSMSK 1 2 × × BEXTR 2 2 × × × × BZHI 1 2 × × PDEP 3 1 × PEXT 3 2 ×
Tabela 3.2: Latência, vazão e portas de execução de das instruções do conjunto BMI1 e BMI2.
3.3
Intel Galileo
A plataforma Intel Galileo é um microcontolador que usa o processador Intel Quark SoC X1000, que é um processador de 32 bits projetado para um baixo consumo energético, compatível com a família x86 e que foi baseado na microarquitetura Pentium clássico. Esse processador é o primeiro da Intel direcionado para a internet das coisas (Internet of Things IoT) e para o mercado wearable [47].
O processador Intel Quark SoC X1000 tem uma arquitetura de 32 bits e possui uma cache 4-way set associative de 16KB. A cache é uma memória de acesso rápido onde é mantida uma cópia temporária de instruções, operandos e dados usados frequentemente; a política de escrita na cache usada por padrão na microarquitetura Quark é o write-through [33].
Modelo Intel QuarkR TM SoC X1000
Frequência 400 MHz
Cores/Threads 1/1
Conjunto de instrução(ISA) 32-bit Intel PentiumR processor-compatible ISAR
Cache L1 16 KB
SRAM 512 KB on-die, embedded
Packaging 15 mm × 15 mm BGA
ACPI-compatible with CPU sleep states
DRAM 256 MB DDR3; 800 MT/s
Tabela 3.3: Principais características do microcontrolador Intel Galileo.
No processador Intel Quark a execução de uma instrução é subdividida em fases, onde cada fase ocupa uma parte específica da unidade de processamento [33]. Apesar de cada instrução ser processada sequencialmente, diversas instruções estão em diferentes fases de execução do processador em um determinado momento; essa técnica de execução é chamada de pipeline.
Para tornar a execução mais eficiente, o processador usa um mecanismo chamado de prefetch de instruções, que busca prever qual é a próxima instrução a ser executada observando padrões de acesso. A cache e a unidade de prefetch são fortemente ligadas, de modo que um bloco de 16 bytes de instruções da cache pode ser passado rapidamente para a unidade de prefetch. Uma vez feito o prefetch de uma instrução ela será decodificada, caso o fluxo de execução seja desviado e não passe mais por essa instrução todo esse processamento é descartado [33].
Essa arquitetura possui oito registradores de propósito geral, denotados por EAX, EBX, ECX, EDX, ESI, EDI, EBP e ESP [32]. Esse processador não suporta a tecnologia de execução de instruções fora de ordem, mas possui dois pipelines para a execução de instruções e em certas condições pode executar duas instruções consecutivas simultane-amente, usando um mecanismo de emparelhamento descrito em [26]. Na Tabela 3.3 são apresentadas as principais características do microcontrolador Intel Galileo.
3.4
Conjunto de instruções
O conjunto de instruções do processador Quark, quando comparado com o do Haswell, é bem reduzido. Entretanto, isso não significa que não existem instruções poderosas neste conjunto, como as instruções que serão apresentadas a seguir:
1. ROL/ROR. Essas instruções permitem rotacionar bits dentro de um registrador. Ela rotaciona o valor armazenado no primeiro operando um número especificado de bits para a esquerda/direita, que é definido no segundo operando, armazenando o resultado no primeiro operando.
2. BSWAP Essa instrução permite inverter a ordem dos bytes dentro de um registrador. Ela funciona como segue: dado um registrador de 32 bits com quatro bytes (x0, x1, x2
e x3), estando o x0 na parte menos significativa e o x3 na parte mais significativa
essa instrução inverte a ordem desses bytes para (x3, x2, x1 e x0).
Essa microarquitetura também possui as instruções básicas, como instruções de carre-gamento, deslocamento, operações lógicas, etc. Na Tabela 3.4 são mostradas as latências de algumas instruções da microarquitetura Quark ; essas informações foram extraídas do relatório técnico produzido por Agner Fog [25].
Instrução vetorial Latência
LOAD 1
AND OR XOR 1
SHR SHL 1
ROL ROR 1-3
BSWAP 1
Tabela 3.4: Latência de algumas instruções da microarquitetura Quark.
3.5
Resumo
Neste capítulo, foram apresentados os conjuntos avançados de instruções vetoriais pre-sentes nos processadores da Intel, com ênfase nos conjuntos AVX e AVX2. No contexto de Internet das Coisas, foi apresentada a primeira arquitetura da Intel direcionada a esse contexto, o Intel Galileo.