Números inteiros e criptogra a

Propaganda
Números inteiros e criptograa
S. C. Coutinho
Este arquivo reúne as atividades de laboratório desenvolvidas para a disciplina
inteiros e criptograa
números
(MAB 624) oferecida pelo Departamento de Ciência da Compu-
tação da UFRJ no segundo semestre de 2014.
1
2
Laboratório 1: kRSA
1.
Cada objeto do
Axiom tem um tipo extremamente bem denido.
Verique qual o tipo, e
correspondente abreviação, cada um dos seguintes números:
32
3 ,
Use
)show,
0,
1
,
2
7
1−2 ,
1+
√
2,
√
1+ 3
,
2
0.425414,
√
−5,
√
−5 × 0.876
seguido do tipo, para uma lista das funções relativas àquele tipo, que inclui sua abrevi-
ação. Qual a diferença entre
PI, NNI
e
INT?
2. Para declarar o tipo de uma variável usamos dois pontos seguidos de tipo desejado e para atribuir
:=. Por
inteiro e lhe atribui o valor 2. Os
esvaziados usando-se )clear all.
um valor à variável usamos

exemplo,
x:INT := 2
declara a variável
x
como sendo do tipo
valores e tipos de todas as variáveis já utilizadas podem ser
É possível declarar o tipo de uma variável sem lhe atribuir nenhum valor e vice-versa. Porém, quando
você não declara o tipo da variável, mas apenas lhe atribui um valor, o sistema escolhe o tipo da
variável como sendo o mesmo do valor que lhe foi atribuído, o que pode levar a erros na execução de um
programa. Por isso é muito importante declarar o tipo das variáveis antes de usá-las.
3. O que vimos já é suciente para para implementarmos o kRSA. Este sistema, como seu irmão
mais velho RSA, requer dois programas distintos: um gerador de chaves e um programa que codica
e decodica a mensagem dada. Siga o seguinte passo-a-passo para gerar a chave pública e chave
secreta a serem usadas na sua versão do kRSA:
•
•
•
•
•
a, b, a0 e b0 inteiros positivos quaisquer > 104 .
variável M o valor ab − 1.
e o valor a0 M + a e a d o valor b0 M + b.
n o valor (ed − 1)/M .
Atribua às variáveis
Atribua à
Atribua a
Atribua a
Sua chave secreta será
(n, d)
e sua chave pública será
(n, e).
4. A codicação e decodicação no kRSA é feita usando a mesma função, que chamaremos simples-
kRSA. Esta função tem como entrada três inteiros: os dois inteiros positivos correspondentes
à chave (pública ou secreta) e a mensagem m que suporemos que é um número de cartão de crédito
e, portanto, tem 16 algarismos. A função deve retornar o resto, na divisão por n, de cm, em que
escolhemos c = e se queremos codicar m e c = d se queremos decodicar m. No Axiom esta função
mente de
pode ser programada de maneira bastante simples, como:
kRSA(n : PI, c : PI, m : NNI) : NNI == rem(c ∗ m, n)
3
:NNI corresponde ao tipo esperado da saída desta função e o símbolo == indica que o que
* para indicar a
multiplicação de c por m.
O segundo
vem em seguida são os comandos que denem a função. Observe também o uso de
(a) Por que tipamos
n
e
c
na entrada como
PI
e
m
e a saída como
NNI?
(b) Escolha um número de cartão de crédito (inteiro com 16 algarismos) e codique-o usando
a sua chave pública.
(c) O número
472460344996534
é o resultado da codicação kRSA de um número
chave que aparece no exercício 6 desta lista. Decodique a mensagem: o que
m
m
com a
representa?
5. Para encerrar, escreveremos uma função que gera chaves para o kRSA a partir de inteiro positivos
gedit e escreva geraChavesKRSA(a,b,a',b')==
na margem esquerda, sem esquecer de tipar as variáveis de entrada. A saída será a lista [n,e,d], que
tem tipo LIST(NNI). Abaixo desta linha, escreva, uma abaixo da outra e endentadas relativamente
a, b, a0 e b0 dados como entrada.
Abra um arquivo no
à margem esquerda, as instruções do exercício 3 acima, exceto as atribuições dos valores às variáveis
a, b, a0 e b0 , que passaram a ser parte da entrada da função. Ao nal do programa acrescente a
linha return([n,e,d]) para que o sistema retorne esta lista de valores. A endentação, que deve ser
a mesma para todas estas linhas, é a maneira usada na sintaxe do
Axiom para indicar que fazem
parte de um único bloco de código. Grave o arquivo com a terminação
e leia-o no
.input e nome à sua escolha
Axiom usando o comando )read seguido do nome do arquivo, que deve vir acompanhado
do caminho indicando onde foi gravado. Depois de ler o programa, teste-o chamando-o no
por
geraChavesKRSA(#,#,#,#),
com os
#
prompt
substituídos por inteiros positivos à sua escolha.
geraChavesKRSA(a,b,a',b') é fácil, mas incoveniente, porque
n, e e d aparecem nesta ordem na lista de saída. Para evitar isto
podemos usar um tipo chamado Record. Ao contrário das listas, cujas entradas são numeradas,
as entradas de um Record são chamadas por nomes que devem ser declarados quando tipamos a
saída de geraChavesKRSA. Por exemplo, ao tipar a saída como Record(n:PI,e:PI,d:PI) e inventar
6. Usar lista como tipo de saída para
requer que o usuário lembre que
valores para a entrada, obtive como saída
[n = 1554916332496649, e = 178479852747, d = 606678245512]
Altere, o tipo de saída no seu programa de
LIST(NNI)
para o
sistema deve retornar os valores corretos, mas vai acusar um
Record
acima e teste-o de novo. O
erro de compilação porque
n, e
e
d
estão sendo usados simultaneamente, como variáveis internas do programa e nomes de campos no
Record.
Para evisar isso basta alterar os nomes das variáveis.
7. Acrescente no início do seu arquivo os nomes e DRE dos dois membros da sua dupla, precedidos
de
- - e a função kRSA e envie o arquivo para criptolab dcc.ufrj.br.
Os dois traços avisam ao sistema
que o que segue naquela linha são comentários e não códigos de programas.
4
Laboratório 2: algoritmo euclidiano estendido
1. Neste laboratório implementaremos o
a
dois inteiros positivos
tais que
d = αa + βb.
e
b
algoritmo euclidiano estendido que, tendo como entrada
retornará o máximo divisor comum
d
de
a
e
b
Para construir a função abra um arquivo no
esquecer de tipar
comum de
a
e
b.
a
e
e
β
Faremos a implmentação do algoritmo em duas etapas. Primeiro a parte da
função que calcula apenas o máximo divisor comum e,em seguida, a parte que calcula
2.
α
e dois inteiros
b
gedit
e escreva
α
e
β.
mdcEstendido(a,b) ==
sem
e a saída que, nesta primeira etapa, correspoderá apenas ao máximo divisor
Não esqueca que o arquivo deve ter a terminação
.input
e que o bloco de código
que corresponde às instruções da função deve estar endentado. Como dois restos são usados a cada
laço que o programa executa, precisamos inicializar
R1:PI := a
R2:NNI := b
R1
Em cada etapa o algoritmo vai dividir
por
R2
e obter um novo resto, até que o valor de
anule. Quando isto acontecer, o máximo divisor comum de
Para pôr este laço em funcionamento, precisamos usar um
a e b estará guardado
while, que no
R2 se
R1.
na variável
Axiom tem a seguinte
sintaxe:
while
Condição
repeat
Instruções
Note que as
Instruções devem aparecer endentadas em relação à posição do
deve ser vericada para que o laço
while.
A
condição que
while continue a ser executado é R2 6= 0 e as instruções a serem
executadas são as seguintes:
•
•
calcule o resto
guarde o
r
da divisão de
antigo valor de
Para que isto possa ser feito
R2
R1 por R2
R1.
e guarde-o em
R2;
em
sem que nenhum dos valores anteriores das variáveis se perca antes
de podermos usá-los, precisaremos de uma variável temporária, que chamaremos de
necessidade de inicializar esta variável fora do laço principal mas, por segurança,
while
seu tipo. Com isto as instruções dentro do
•
•
•
R2
em
Rtemp;
guarde em
R2
o resto da divisão de
guarde
guarde o valor de
Rtemp
em
R1
Não há
você deve declarar
terão a forma
por
R2 ;
R1.
Com isto, ao nal de uma passagem pelo laço, o
resto da divisão dos
Rtemp.
antigos valores de
R1
por
R2
antigo valor de
R2
estará guardado em
estará guardado em
R2.
R1
e o
Para retornar o valor de
5
R1
use
return(R1),
que precisa estar fora do
while;
isto é, deve ter a mesma endentação do bloco
principal do algoritmo .

Para calcular o resto e o quociente da divisão de
Na linguagem do
operador; assim, diferente corresponde a
3.
a
por
b
use
rem(a,b)
e
quo(a,b)
respectivamente.
Axiom a negativa de uma armação é obtida acrescentando um til na frente do
=.
Guarde o arquivo e carregue a função usando
)readsem
esquecer o parêntesisseguido do
caminho exato do local onde o arquivo foi gravado. Se o sistema acusar um erro de compilação, use
a seguinte checklist para achar possíveis erros, antes de pedir ajuda:
•
•
•
•
•
uso de
=
em vez de
:=;
endentação incorreta;
funções escritas erradas;
confusão entre maiúsculas e minúsculas nos nomes das variáveis;
multiplicação sem
*.
Para forçar o sistema a exibir os valores de uma variável
z ao longo da execução de uma função use
output(z).
4. Para converter o algoritmo que acabamos de programar no algoritmo estendido, precisamos de
uma nova variável
x.
Conforme a descrição apresentada na aula, precisamos lembrar dos valores de
x referentes aos dois últimos passos do algoritmo. No livro e na aula, a notação usada para os valores
de x na inicialização foram x−1 e x0 . Como o
não aceita x−1 como variável, escreveremos
xm1 e x0. Inicialize estas variáveis com 1 e 0, antes do laço while, sem esquecer de tipá-las. Dentro
do while você precisará acrescentar as seguintes instruções:
Axiom
•
•
•
cálcule o quociente
guarde
q
de
R1
por
x−1 − qx0 em x0;
xm1 o antigo valor
guarde em
R2 ;
de
x0.
Para executar estas instruções de maneira correta será necessário criar uma nova variável
proceder como zemos com os restos. Não há necessidade de inicializar
xtemp
xtemp e
while,
fora do laço
mas não esqueça de declarar seu tipo. Como vimos na aula, o cálculo do valor de
β
pode ser feito
ao nal da execução do laço. Finalmente, será necessário alterar o tipo da saída para que a função
α e β.
Record com três campos chamados de mdc, alfa e beta.
retorne o máximo divisor comum e os valores de
A melhor maneira de fazer isto é usar um
Não esqueça de atualizar o que o algoritmo
deve retornar. Acrescente no início do seu arquivo os nomes e DRE dos dois membros da sua dupla,
precedidos de

- - (sem espaço entre eles) e envie o arquivo para o endereço [email protected].
Nossa função foi construída supondo-se que os valores da entrada satisfazem
se isto não for verdade?
a > b.
O que acontece
6
Laboratório 3: método cíclico para resolver a equação de Pell
1.
Neste laboratório implementaremos a função
inteiro
achaSolucaoPell
que, tendo como entrada um
d que não é quadrado perfeito, retorna uma solução não trivial da equação de Pell x2 −dy 2 = 1.
O algoritmo, como descrito em sala, é o seguinte.
Algoritmo (Método cíclico).
Dado um inteiro positivo
d, que não é um quadrado perfeito, o algo-
ritmo retorna uma solução não trivial da equação de Pell
x2 − dy 2 = 1.
Inicialize um contador
k := 0 e P := [x, y].
Inicialize A com 1, B com 0 e C com d.
Enquanto k 6= 0, A 6= 1 e k for ímpar repita:
Determine o maior inteiro
q menor que a raiz positiva da equação At2 − 2Bt − C = 0.
A := −(Aq 2 − 2Bq − C), B := Aq − B e C := A.
Substitua x por qx + y e y por x em P .
Incremente k de uma unidade.
Substitua x por 1 e y por 0.
Retorne P .
Faça
2. Para implementar este algoritmo, você vai precisar das seguintes funções do
Axiom:
solve(f,0.1): retorna uma aproximação das raízes do polinômio f em ponto
wholePart(r): retorna a parte inteira do número real r;
L.k: retorna a k-ésima posição de uma lista L;
rhs(E): retorna o lado direito de uma expressão E da forma E1 = E2 ;
odd?(n): determina se o número inteiro n é ímpar;
eval(E,[x=f,y=g]): substitui x por f e y por g em um polinômio E(x, y);
Todas as funções do
true
ou
false,
Axiom que acabam com uma interrogação (como odd?
e
utuante;
prime?)
retornam
dependendo da entrada.
x e y . Se você deixar a
List(OrderedVariableList([x,y])).
Entretanto, ao longo da execução do algoritmo, as entradas de P serão polinômios lineares em x e y , que
não são variáveis e, por isso, não podem ser atribuídos a P . Para evitar o problema, basta tipar P como
LIST(MPOLY([x,y],INT)). Use )show para descobrir o que signica o tipo MPOLY.

No algoritmo o ponto
P
é inicializado como uma lista com duas variáveis
escolha do tipo por conta do
3.
Axiom, ele atribuirá a P
o tipo
Escrevendo a Frenicle em 1657, Fermat propôs que ele achasse soluções para as equações de
Pell com
d = 61
e com
d = 109
porque, segundo ele, esses dois exemplos não lhe dariam muito
problema. Use o seu algoritmo para determinar uma solução para as equações de Pell para as quais
d = 61
e
d = 109.
Quantos passos o algoritmo executou até encontrar a solução? Acrescente no
início do seu arquivo os nomes e DRE dos dois membros da sua dupla, precedidos de
entre eles) e envie o arquivo para o endereço
[email protected].
- - (sem espaço
7
Laboratório 4: algoritmo de fatoração de Fermat
Algoritmo (Algoritmo de Fermat).
fator próprio de
n, o algoritmo retorna um
n ou a mensagem eh primo.
Inicialize
Se
Dado um inteiro positivo ímpar
x com a parte inteira da raiz quadrada de n.
2
n = x então
retorne([x, x])
senão
x := x + 1
Enquanto
√
x2 − n não for inteiro repita
x := x + 1
Se x = (n + 1)/2 então
retorne(eh primo).
Senão
√
x2 − n.
retorne([x − y, x + y]).
y :=
O algoritmo deve ser implementado como a função
cuja saída será uma lista ou uma
diferentes, o
o tipo
string.
fermat,
cuja entrada é um inteiro positivo e
Para lidar com saídas que podem ser de vários tipos
Axiom tem o tipo Union. Por exemplo, no caso do algoritmo de Fermat, a saída terá
Union(List(PI),String). Para alguns dos testes que são realizados ao longo
if-then-else que, no Axiom, tem a seguinte estrutura:
do algoritmo,
precisamos da construção
if
condição
then
Instruções
else
Instruções
√
while, para determinar se x2 − n é ou não
integer? do Axiom, cuja saída é true ou false.
Já o teste feito no laço
usando a função
inteiro pode implementado
Certique-se de que o algoritmo está funcionando corretamente quando a entrada são números
inteiros positivos
ímpares que sejam quadrados perfeitos, primos ou produtos de dois primos distin-
tos. Adicione ao algoritmo um contador para descobrir quantos passos foram executados antes da
resposta ser encontrada e use a função
do
while.
Determine, então, quantas vezes o laço é executado quando a entrada é o produto de
um primo com
do
output para retornar o valor do contador ao nal da execução
10
nextPrime
maior que m.
algarismos pelo seu sucessor. Para achar estes primos use a função
Axiom, que tendo como entrada um inteiro positivo m retorna o menor primo
Acrescente no início do seu arquivo os nomes e DRE dos dois membros da sua dupla, precedidos de
--
(sem espaço entre eles) e envie o arquivo para o endereço
[email protected].
8
Laboratório 5: crivo de Eratóstenes
1. O crivo de Eratóstenes será implementado na função
n,
inteiro positivo
eratostenes
que, tendo como entrada um
retorna a lista dos números primos positivos menores ou iguais a
n.
A função
será implementada em duas partes. Na primeira efetuamos o risca-risca da lista de ímpares; na
segunda, coletamos os números da lista anterior que não foram riscados. Lembre-se que riscar
0
um número neste contexto corresponde a trocar por
o valor da posição da lista que corresponde
àquele número.
2. Antes de começarmos a descrever o algoritmo, há alguns comandos relativos a listas que você
precisa saber. Suponha que
L
e
L'
são duas listas de elementos do mesmo tipo, então:
L:=[1 for i in 1..m] cria uma lista de m elementos, todos
L.k:=a troca por a o valor da k-ésima posição de L;
append(L,L') cria a lista obtida acrescentando L' ao nal L.
Para acrescentar um elemento
iguais a
1;
a ao nal de L basta tomar L' como sendo [a] na construção acima.
tamanhoDaLista com a parte inteira de n/2
tamanhoDaLista posições, todas preenchidas com 1s. Serão
necessários dois laços while aninhados para crivar ListaBase. O laço mais externo percorre a lista
a partir do início. Para cada posição k , verica-se primeiramente se L.k é ou não igual a 1. Se for:
3.
A primeira etapa começa com a inicialização de
e de
ListaBase
•
•
•
calculamos o primo correspondente a
calculamos a posição
2
` = 2k + 2k
k,
por
0
termo é
nas posições de
`
e cuja razão é
que é
da lista
efetuamos o risca-risca dos múltiplos de
1

como uma lista de
ListaBase
p.
L
p = 2k + 1;
em que
p2
está posicionado;
p usando um segundo laço while, no qual trocamos
que pertencem à progressão aritmética cujo primeiro
Não esqueça de inicializar e de incrementar as variáveis que controlam os laços
valor de
k
deve ser incrementado ao nal do
if-then
que determina se
L.k = 1
while.
Note que o
e que contém o laço
que efetua o risca-risca.
[2] e um contador com k:PI:=1.
Criamos, então, um laço while que varre a lista e, toda vez que ListaBase.k = 1, acrescenta 2k +1
à lista primos. Ao nal deste laço o programa retorna a lista primos.
4. Começamos a segunda etapa inicializando a lista
5.
primos
com
A partir deste laboratório você deve comentar os seus programas, explicando o que cada
parte do algoritmo faz.
O mesmo se aplica aos laços
while
aninhados.
As linhas de comentário
devem ser precedidas de dois travessões. Acrescente nome e DRE dos dois membros da equipe e
envie o arquivo para o endereço
[email protected].
Download