304 lines
7.9 KiB
Text
304 lines
7.9 KiB
Text
#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.
|