crypto3/main.typ

304 lines
7.9 KiB
Text
Raw Normal View History

2023-10-25 05:02:29 +00:00
#import "@preview/algo:0.3.2": algo, i, d, comment, code
#set page(
numbering: "1 / 1",
header: [
#set text(8pt)
_IFT436, Devoir 1 #h(1fr) Paulin Violette, 2023_
],
)
#let code = rect.with(
inset: 8pt,
fill: rgb("e4e5fa"),
width: 100%,
radius: 6pt
)
#let title(content) = {
pagebreak(weak:true)
set text(size:15pt)
set align(center)
v(10pt)
[#content]
v(10pt)
}
#title[= Devoir 1]
#line(length: 100%)
#v(70pt)
#line(length: 100%)
#set par(
first-line-indent: 1em,
justify: true,
)
= Notes
Le code présenté est retrouvable sur #link("ssh://sherbrooke@bigblase.xyz:/srv/git/crypto2")\
mot de passe : `FDS8EbKiDNoJh2QN`\
Le code a été testé sous linux, kernel 6.5 et librairie à jour (sept 2023), en
utilisant gcc 13.2.1.
Pour compiler, il faut d'abord créer le dossier _build_ à la racine du projet.
Pour tester, il faut faire ```sh make run part=<PART_NUMBER> eve=<ARGS_EVE>```.
Il _devrait_ être portable (C99, POSIX compliant)
#set heading(numbering: "I) a) i)")
= Diffie Hellman
On implémente les fonctions demandées. Ceci nous donne :
#code[```c
#define MOD2POW63(n) ((n) % UINT64_MAX)
static inline uint32_t
gen()
{
/* rand is between 0 and UINT32_MAX - 1 / 2 (because sign) */
return rand() + rand() + (rand() % 1);
}
```
```c
int
main(int argc, char *argv[])
{
srand(time(0)); /* init rand */
uint32_t key_alice = gen();
uint32_t key_bob = gen();
uint64_t h_a = MOD2POW63(ipow(3, key_alice));
uint64_t h_b = MOD2POW63(ipow(3, key_bob));
printf("Alice generated key %d, sent h_a=%lu\n", key_alice, h_a);
printf("Bob generated key %d, sent h_b=%lu\n", key_bob, h_b);
#ifdef EVE_INTERCEPT
printf("Eve intercepted h_b and h_a.\n");
uint32_t key_eve = gen();
uint64_t h_e = MOD2POW63(ipow(3, key_eve));
printf("Eve generated key %d. She sends h_a to Bob, and h_b to Alice.\n",
key_eve);
uint64_t k_e_a = MOD2POW63(ipow(h_a, key_eve));
uint64_t k_e_b = MOD2POW63(ipow(h_b, key_eve));
printf("Eve comptued k_e_a=%lu, k_e_b=%lu\n", k_e_a, k_e_b);
printf("Eve sends h_e to Bob and Alice\n");
h_a = h_e;
h_b = h_e;
#endif
uint64_t k_a = MOD2POW63(ipow(h_b, key_alice));
uint64_t k_b = MOD2POW63(ipow(h_a, key_bob));
printf("Alice computed k_a=%lu\n", k_a);
printf("Bob computed k_b=%lu\n", k_b);
...
return 0;
}
```]
Pour pouvoir avoir une exponentiation rapide sur les entier, je l'ai
#strike[trouvée sur stackoverflow] implémentée. Je laisse aussi
l'exponentiation modulaire qui sera utile pour la partie 2:
#code[
```c
/* https://stackoverflow.com/a/101613/13156585 */
static uint64_t
ipow(uint32_t base, uint32_t exp)
{
int result = 1;
for (;;)
{
if (exp & 1)
result *= base;
exp >>= 1;
if (!exp)
break;
base *= base;
}
return result;
}
static uint64_t
ipow_mod(uint32_t base, uint32_t exponent, uint32_t mod) {
uint64_t x = 1;
uint64_t y = base;
while (exponent > 0) {
if (exponent % 2 == 1)
x = (x * y) % mod;
y = (y * y) % mod;
exponent = exponent / 2;
}
return (x % mod);
}
```]
==
On a ici $(Pi_(A)^i, Pi_(B)^i) = "DHKEP" ; i=32$
==
On fait tourner une fois sans qu'Eve n'intervienne.
#code[`Alice generated key -1792100893, sent h_a=18446744073108443291
Bob generated key -1044643954, sent h_b=18446744072800391545
Alice computed k_a=1639053353
Bob computed k_b=1639053353
Bob and Alice key match
`]
Eve voit passer $h_b$ et $h_a$.
Cette fois, Eve intervient, et intercepte $h_b$ et $h_a$ (ils ne seront donc
pas déliverés).
On obtient :
#code[`Alice generated key -2077545126, sent h_a=2145716457
Bob generated key 1787548291, sent h_b=18446744071694128667
Eve intercepted h_b and h_a.
Eve generated key -1461028833. She sends h_a to Bob, and h_b to Alice.
Eve comptued k_e_a=18446744073569067097, k_e_b=18446744073273089683
Eve sends h_e_b to Bob and h_e_a to Alice
Alice computed k_a=18446744073569067097
Bob computed k_b=18446744073273089683
Bob and Alice keys match with Eve's!
`]
On note qu'a chaque envoie d'Alice ou Bob, Eve reçoit les messages, et choisis
ou non de les faires passer, et chaque message qu'Alice ou Bob reçoivent
provient donc de Eve.
==
Alice et Bob ne voit aucune différence, si ce n'est un temps entre les
messages plus important. En dehors de cela, il n'y veront aucune différence.
==
On voit pourtant que $k_b != k_a$ quand Eve manipule les hash. Cependant,
comme Alice et Bob ne partagent pas leurs clef privées, il ne le verront pas.
= Chiffrement RSA
C'est les mêmes nombres que dans l'exercice de la semaine dernière. On a donc:
$N = 143, p = 11, q = 13, e = 7, d = 103$
==
#code[```c
static uint32_t
E1(char message, uint32_t key_priv, uint32_t n){
return ipow_mod(message, key_priv, n);
}
static uint32_t
D1(char cypher, uint32_t key_pub, uint32_t n){
return ipow_mod(cypher, key_pub, n);
}
```]
Pour tester, j'ai il faudra aussi passer la valeur du message au moment
d'exécuter le programme:`make run part=2 m=5`.
Lignes utils : #code[```c
int
main(int argc, char *argv[])
{
...
uint32_t e1 = E1(atoi(argv[1]), e, n);
uint32_t d1 = D1(e1, d, n);
printf("m: %d, cypher: %u, decrypt: %u\n", atoi(argv[1]), e1, d1);
return 0;
}
```]
On obtient :
#table(
columns: (1fr, 1fr, 1fr),
inset: 10pt,
align: center,
[`m: 3, cypher: 42, decrypt: 3`],
[`m: 5, cypher: 47, decrypt: 5`],
[`m: 7, cypher: 6, decrypt: 7`]
)
On remarque qu'à chaque fois, on a bien $m = d$, ce qui montre que le message
d'origine est bien celui déchiffré.
La clef privée est le couple $(d, N)$, la clef publique le couple $(e, N)$.
Ainsi, Alice dispose de $(d, e, N)$, et Bob de $(d, N)$. Alice connait aussi
$p, q$ permettant de générer $N$.
Si Alice transmet la clef privée "Willy Nilly" à Bob,
alors Eve peut intercepter $(N, d)$. Ainsi, elle pourrait
déchiffrer les messages de Alice chiffré par cette clef, tout comme Bob.
==
#table(
columns: (1fr, 1fr),
inset: 10pt,
align: center,
[`m: 0, cypher: 0, decrypt: 0`],
[`m: 1, cypher: 1, decrypt: 1`]
)
On remarque que $forall x in bb(N)^* 0^x eq.triple 0 [N]$, et que
$forall x in bb(N)^* 1^x eq.triple 1 [N]$. Pas de surprise dans le résultat.
Eve peut donc deviner le message pour $m=0, m=1$, même sans connaitre les clefs.
== Signatures RSA
On fait comme avant, mais on utilise la clef privée pour signer, et la clef
publique pour vérifier.
On fait alors les fonctions suivantes :
#code[```c
inline static uint32_t
Sign2(char message, uint32_t key_priv, uint32_t n){
return E1(message, key_priv, n);
}
inline static uint32_t
Verif2(char signature, uint32_t key_pub, uint32_t n){
return D1(signature, key_pub, n);
}
```]
#highlight(fill:rgb("e4e5ea"))[Notes : en `inline`-ant la fonction, on ne fait aucun appel supplémentaire,
on peut le voir comme un alias aux fonctions précédentes :)]
Si le message était plus compliqué, (ex : string) on pourrait alors faire
la signature sur le hash du message. On le fait pas ici.
#table(
columns: (1fr, 1fr, 1fr),
inset: 10pt,
align: center,
[`m: 3, sign: 16, verif: 3`],
[`m: 5, sign: 125, verif: 5`],
[`m: 7, sign: 123, verif: 7`]
)
Où $italic("sk") = (d, N) = (103, 143), italic("pk") = (e, N) = (7, 143)$
Eve voit passer _pk_, le message et la signature.
Elle peut donc vérifier l'origine du message, mais ne peut pas reproduire de
message avec une signature qui serait cohérente avec ses clef.
Elle pourrait essayer de recomposer une clef privée qui convient.
Elle connait $N$. Il faut qu'elle trouve une décomposition de $N$ en nombre
premiers, avec seuelement deux composantes premières.
Or, une décomposition en nombre premier est en temps #link("https://en.wikipedia.org/wiki/Integer_factorization#Difficulty_and_complexity")[#text(blue)[#underline[subexponentiel]]],
et non polynomial.
Eve ne peut donc pas reconstruire de clef privée pour resigner un message.
Elle devra utiliser une autre stratégie. Par exemple, elle pourrait essayer
de regenerer une clef publique chez Bob dont elle maitrise la clef publique.