Monografia sobre Criptografia baseada em Identidade.
Utilizada desde a Antiguidade (embora sem tanta
sofisticação) passando pelas duas grandes guerras até chegar ao homebanking, a
criptografia é a ciência de segurança de informações. O nome deriva do
grego kryptós, 'oculto, secreto, obscuro, initeligível' e -graphía,
'escrita'.
Utilizando um forte embasamento matemático, a criptografia permite a
você ocultar informação de tal forma que ela não possa ser recuperada por
ninguém além do destinatário designado.
Dois são os principais esquemas criptográficos atualmente utilizados:
Na Criptografia Simétrica - também chamada
de Criptografia de Chave Secreta - a chave que transforma o texto claro em
texto cifrado é a mesma que será usada mais tarde, pelo destinatário, para
converter o texto cifrado de volta em texto claro.
Nesse esquema, deve haver uma chave para cada par de usuários. Não é difícil notar que o número de chaves cresce bem mais rápido que o número de usuários (para ser exato, são necessárias n(n - 1)/2 chaves para n usuários. Além disso, estas chaves devem ser trocadas por algum canal seguro.
2
usuários - 1 chave
3 usuários - 3 chaves
4 usuários - 6 chaves
5 usuários - 10 chaves
Já na Criptografia Assimétrica - ou Criptografia
de Chave Pública - cada usuário deve possuir duas chaves: uma pública, usada
para criptografar, e outra privada, utilizada na decriptografia. Embora o
número de chaves necessárias seja bem menor que no esquema anterior (2n
chaves para n participantes), é necessário um repositório centralizado
que esteja sempre on-line para gerenciar todas elas.
Em 1985, Adi Shamir sugeriu um esquema similar ao sistema
postal, em que a chave de um usuário seria função de sua identidade, evitando
assim a necessidade de um diretório de chaves públicas, trocas de chaves ou uma
autoridade superior que esteja sempre presente. Como num sistema postal, se
você souber o nome e o endereço de alguém você pode enviar mensagens que apenas
essa pessoa consiga ler e pode verificar as assinaturas que somente ela
possa ter produzido.
Desde então, vários criptossistemas baseados em identidades têm sido propostos,
mas este esquema tem se mostrado difícil de encontrar implementações que sejam
práticas e ao mesmo tempo seguras.
1. Visão Geral
O sistema possui uma autoridade, chamada de Private Key
Generator, que calcula e publica o produto M de dois primos P e Q
(escolhidos em segredo). Esses primos são ambos congruentes a 3 mod 4. O
sistema também faz uso de uma função hash conhecida e segura.
Assim, se um usuário deseja se registrar para ser capaz de receber dados
codificados ele envia sua identidade (p.ex. endereço de e-mail) para a
autoridade, que lhe devolve uma chave privada. Quando um outro usuário deseja
enviar informação criptografada para outro, ele faz isso conhecendo a
chave pública do destinatário e os parâmetros globais. Não há necessidade de
nenhum diretório de chaves públicas.
Repare que pode ser difícil para o PKG verificar se a identidade
apresentada é mesmo do usuário que a manifestou.
2. Detalhes Sobre O Funcionamento
Quando um usuário apresenta sua identidade ao PKG, a função de hash é aplicada para produzir um valor a mod M tal que (a / M) = +1 (lê-se "a Jacobiano M"). Isso significa que a é resíduo quadrático módulo M, ou seja, existe um r Î Z*M tal que r2 = a mod M. Esse processo pode ser repetido por quem quiser. No entanto, há, tipicamente, vários candidatos "a". A escolha da raiz r, que depende de a, e que vai ser passado ao usuário é próprio do PKG, obviamente, mas há uma sugestão de como calcular uma das raízes de a com pouco processamento . Esse valor "r" é usado pelo usuário para recuperar mensagens enviadas a ele. Além disso, não sabemos se r2 = a mod M ou -a mod M.
3. Como assim?
Como (a / M) = +1, pela propriedade
desse símbolo, (a / M) = (a
/ P)(a / Q). Dado que os valores assumidos são
apenas +1, -1 e 0, segue que:
4. Como enviar mensagens?
Seja
x
o bit a ser enviado (usar -1 se o bit for 0 e +1 se o bit valer 1);
O remetente escolhe um valor
t
aleatório mod M, tal que (t
/ M) =
x;
Ele
então envia
s
= (t
+
a
/
t) mod M. Repare que
a
/
t
significa
a
*
t-1 mod
M.
Esses passos devem ser repetidos para cada bit que se
deseja transmitir. Caso o remetente não saiba se o destinatário recebeu a raiz
quadrada de a ou -a, ele deve enviar, para o mesmo valor x,
s = (t - a / t)
mod M, usando diferentes valores para t.
5. E como recuperar as mensagens?
Para recuperar x, o destinatário calcula s + 2r, pois:s + 2r | = (t + a / t) + 2r |
= (t + r2 / t) + 2r, pois r2 = a mod M | |
= t (1 + r2 / t2 + 2r / t) | |
= t (1 + r / t) (1 + r / t) |
6. Um exemplo didático
Suponha M = 21. E que o destinatário tenha recebido o valor 19.
Para enviar o bit 1, o remetente primeiramente mapeia o valor para +1. Em
seguida, escolhe o valor t = 10 ao acaso - mas respeitando ( t /
21 ) = +1.
t-1 vale 19. Finalmente o remetente calcula s
= 10 + 4 * 19 = 86 mod 21 = 2 e envia esse valor.
O destinatário recebe s e calcula:
( 2 + 38 / 21 ) | = ( 40 / 21 ) |
= ( 20 / 21 )( 2 /21 ) |
|
= (-1)(-1) | |
= +1 | |
= x |
7. Problemas
Como os bits são enviados individualmente, sem nenhum teste de integridade, cada
bit pode ser alterado independentemente. Além disso, a segurança desse esquema
está baseada na dificuldade de fatoração de números inteiros. Se um
mal-intencionado conseguir descobrir a fatoração de M, ele pode calcular todos
os possíveis valores a e suas raízes quadradas.
Outro inconveniente, desta vez sem ser relacionado a segurança, é a quantidade
de informação enviada para se criptografar um único bit.
Um último problema é quando s + 2r for múltiplo de algum dos primos P ou
Q. Quando o destinatário for calcular o jacobiano ( s + 2r / M ) irá obter o
valor zero e não conseguirá recuperar o valor daquele bit. A descrição do
algoritmo não especifica o que fazer nesse caso (na verdade, sequer menciona
esta falha).
8. Implementação
Programei este esquema de criptografia em C++, na plataforma WindowsT.
Util é uma classe estática que contém funções utilizadas por
todo o programa, como o algoritmo para calcular o jacobiano (cedido pelo
professor Routo), a função de hash (retirada do livro 'Programming Pearls') e
outras como o cálculo do inverso multiplicativo ou para descobrir se um número
é primo. Por estarmos tratando de uma implentação didática do algoritmo, esta
classe (assim como todas as outras) não foi escrita com a finalidade de
ser eficiente.
A classe singleton PKG representa a autoridade geradora das
chaves.
User é a classe que denota os usuários do sistema. É
nesta classe que estão programadas as funções de criptografia e decriptografia.
Coloquei nesta classe um método que diz se o usuário recebeu a raiz de a
ou de -a, para que o remetente não precise dobrar a quantidade
de dados enviados (isso foge à especificação do algoritmo).
Para que o destinatário consiga recuperar todos os bits recebidos, tive que
permitir que o remetente tenha acesso à sua chave privada para impedir que gere
valores que caiam no terceiro problema comentado acima. Isso inutiliza o
algoritmo mas garante a corretude do programa. Como alternativa, o destinatário
poderia pedir que o remetente enviasse novamente o bit, calculado de maneira
diferente. Mas essa informação pode facilitar que um mal-intencionado
descubra o valor da chave secreta do destinatário.
1. Visão Geral
Este esquema também é inspirado em criptosistemas
de chave pública onde ao invés de gerar um par de chaves pública/privada e
publicar uma delas, o usuário escolhe sua identidade (comumente e-mail) como
sua chave pública.
Desta vez também temos uma autoridade confiável, chamada de Trusted
Authority, cujo único propósito é definir os parâmetros globais
do sistema e dar a cada usuário uma chave que será usada para a assinatura de
mensagens.
Um usuário, após apresentar sua identidade, tem sua chave gerada. A partir
deste momento a autoridade não é mais necessária. Repare que, ao contrário da
criptografia de chave pública no sentido habitual, para um usuário
verificar a assinatura de outro usando sua chave pública ele também deve
verificar a autenticidade desta. Torna-se inevitável a presença da autoridade
certificadora.
2. Detalhes Sobre O Funcionamento
Antes de oferecer o serviço de geração de chaves, a TA deve configurar os seguintes parâmetros do sistema:
Os valores n, e e a descrição da função h
são conhecidos por todos os usuários. Uma vez tendo estes valores definidos, já
é possível gerar as chaves dos usuários.
Ao apresentar sua identidade, o usuário tem sua chave g calculada pela
TA através da fórmula g ≡ f(ID)d mod n,
onde f é uma função de hash segura (podendo ser a mesma função h).
3. Geração da assinatura
Para assinar uma mensagem m um usuário escolhe aleatoriamente um
inteiro r pertencente a Z*n e calcula t =
re mod n e s = grh(t,m)
mod n.
A assinatura é o par (s, t).
4. Verificação da assinatura
Ao receber uma mensagem m e uma assinatura (s, t), o
receptor usa a identidade ID do rementente para verificar a autenticidade
dos dados.
Portanto, não há necessidade de se realizar uma apuração separada.
5. Um exemplo didático
Suponha novamente n = 21.
φ(21)
= φ(3)φ(7)
= (3 - 1)(7 - 1)
= 12
e = 11
d = 11
f(ID) = 9
O remetente apresenta sua identidade à TA, que calcula sua chave:
g
= f(ID)d mod n
= 911 mod 21
= 18
Em seguida o remetente assina uma mensagem:
r = 8
h(t, m) = 2
t
= re mod n
= 811 mod 21
= 8
s
= grh(t,m) mod n
= 18.82 mod 21
= 18
Finalmente, o destinatário verifica a autenticidade da assinatura
recebida:
se
≡f(ID)th(t,m) mod n
1811
≡9.82 mod 21
9
9
E confirma que é uma assinatura válida.
6. Problemas
Este esquema não permite a revogação de chaves (necessária quando a chave
privada do usuário torna-se comprometida), já que há unicidade de chaves para
cada identidade.
Também precisamos lembrar que a autoridade confiável pode não ser confiável
(ela pode forjar qualquer chave de qualquer usuário). Isso restringe o uso
desse esquema a sistemas fechados onde há total convicção na integridade da
autoridade.
7. Implementação
Usando da mesma linguagem e plataforma, este programa tem estrutura muito
parecida com o anterior.
A classe Util é a mesma do projeto anterior, onde
encontramos funções como a que calcula m.d.c. de dois números ou o
inverso multiplicativo módulo n de um número
TA é a classe que faz o papel da autoridade. Sua única função
é gerar os valores globais e criar as chaves dos usuários.
User representa os usuários do esquema. Aqui estão
implementadas as funções de geração e verificação de assinatura.
Este programa, mesmo utilizando primos muito maiores do que o de criptografia,
executa muito mais rapidamente que o primeiro. Não por estar melhor
implementado, apenas por ter um algoritmo mais simples.
Chave: valor aplicado utilizando-se um algoritmo sobre informação não criptografada para produzir informação encriptada. Quanto maior o comprimento da chave, maior é também a dificuldade de se conseguir recuperar a informação original por força bruta - se o algoritmo de criptografia for seguro.
Texto claro: também chamado de texto comum, texto normal ou texto legível. É a informação (tipicamente texto, daí o nome) antes de ser criptografada (ou após ser decriptografada).
Texto cifrado: ou texto ilegível. A informação após passar pelo processo de criptografia.
Função de hash: também denominada função espalhamento. Transforma um dado texto legível em uma cadeia de caracteres de tamanho fixo relativamente menor que o comprimento do texto legível. Uma função hash é dita segura se os elementos do domínio são distribuídos uniforme e aleatoriamente pela imagem, de modo que textos legíveis muito parecidos tenham alta probabilidade de possuírem valores hash diferentes. Grosseiramente falando, queremos que H("Cauda") ≠ H("Calda").
Símbolo de Jacobi: este símbolo, escrito como ( n / m ) é definido para valores m ímpares positivos como ( n / m ) = ( n / p1 )a1 ( n / p2 )a2 ... ( n / pk )ak, onde m = p1a1p2a2...pkak é a fatoração prima de m e ( n / pi ) é o símbolo de Legendre (o símbolo de Legendre é igual a +1 ou -1 dependendo se n é ou não resíduo quadrático mod m e vale 0 se os dois termos não são primos entre si - isto é, mdc(n, p) ≠ 1).
Z*M: é o conjunto de todos os números inteiros mod M relativamente primos a M. No caso particular de M ser primo, Z*M = ZM - {0}
Já sabia desde o ano anterior o tipo de trabalho de formatura
que tentaria desenvolver. Tendo feito estágio desde o primeiro ano de curso,
era hora de tentar outro desafio. Dado o meu interesse pela área de segurança,
era natural que optasse pela iniciação científica em Criptografia.
Como nunca havia participado de uma iniciação,
confesso que subjulguei sua dificuldade. Mesmo tendo acertado com meu
orientador já no primeiro semestre, foi só a partir do segundo que comecei
efetivamente a procurá-lo para tratar dos assuntos que me havia indicado para
estudo.
Isso foi um problema, pois no segundo semestre fui monitor e também passei a
cursar muitas disciplinas, tanto no IME, quanto em outras unidades (FE, FFLCH),
como optativas livres ou eletivas. Tive uma falsa impressão que mesmo com a
quantidade absurda de créditos, seria fácil o semestre pois apenas em duas
disciplinas teria provas para fazer.
Não é preciso ser muito observador para perceber o quanto isso atrapalhou minha
iniciação (que poderia ter terminado ainda no primeiro semestre), de forma que
ao final do ano eu só trabalhava em "deadline".
Apesar dos tropeços, gostei de ter aprendido essa "nova" idéia de
criptografia, que apesar de seus problemas, tem uma abordagem inédita e
interessante, além de parecer uma área de estudo bem promissora. E também
confirma meu interesse nessa área da computação.
Além disso decidi que programaria os algoritmos usando C++. Pela qualidade do
código é fácil ver que ainda tenho muito o que aprender, mas serviu como um
ótimo pontapé inicial (e também serviu para que eu começasse a desenvolver uma
classe para representar inteiros módulo M de maneira eficiente e limpa).
Não quero cair no óbvio dizendo que as
matérias introdutórias da computação foram importantes. Vamos para o que
fez diferença:
MAT123 - Álgebra I: essa disciplina não é realmente da computação, mas
deveria, pois além de ser legal, é a base de metade dos conceitos usados em
criptografia.
MAT213 - Álgebra II: aqui está a base da outra metade dos conceitos.
Muito mais difícil, mas também garante criptossistemas mais seguros.
MAC336 - Criptografia para Segurança de Dados: obviamente não poderia
ficar de fora. Aqui estudamos os principais algoritmos de criptografia e suas
aplicações. Embora vejamos muitos algoritmos durante o curso, hoje sei que são
apenas uma pequena parte.
Referências
Wenbo Mao - A textbook of non-textbook cryptography
Adi Shamir - Identity-based cryptosystems and signature schemes
Clifford Cocks - An identity based encryption scheme based on quadratic
residues
Aram Khalili - Notes on identity-based encryption by Clifford Cocks
Routo Terada - Segurança de dados - criptografia em redes de computador
Shawna M. Meyer e Jonathan P. Sorenson - Efficient algorithms for computing the
Jacobi Symbol
Network Associates - An introduction to cryptography (fonte também de
"inspiração" das imagens usadas neste texto)
Whatis?com - Dicionário de tecnologia
Wikipedia - the free encyclopedia (http://www.wikipedia.org)
Mathworld - the web´s most extensive mathematics resource (http://mathworld.wolfram.com)
Planetmath.org - math for the people, by the people (http://planetmath.org)