Spis treści

Security: Funkcje skrótu - hash functions

Do realizacji zadania wykorzystano WSL1 na systemie Windows 2019 server.
Dystrybucja uruchomiona pod systemem WSL to Ubuntu 22.04.4 LTS
Do włączenia legacy providera w OpenSSL wykorzystano poniższy tutorial:
https://lindevs.com/enable-openssl-legacy-provider-on-ubuntu

Zadanie 1

Treść Zadania:
Informacje dotyczące używania programu openssl dostępne są po wydaniu polecenia man openssl lub na stronie pod adresem : https://wiki.openssl.org/index.php/Manual:Dgst(1)
Krok1 : Stwórz plik tekstowy z wiadomością np. „This is a trial message to test digest functions!” i zapisz w pliku msg
Krok 2: Zapisz polecenie obliczające skrót tej wiadomości używając kolejno funkcji: MD5, SHA1, SHA-256.
Krok 3: Wykonaj obliczenia skrótu a wyniki zapisz w plikach MD5dgst, SHA1dgst, SHA256dgst.
Krok 4: Zmodyfikuj wiadomość msg poprzez skasowanie jednego znaku np. znaku !
Krok 5: Ponownie oblicz, wykorzystując w/w funkcje, skrót wiadomości msg po modyfikacji
Krok 6: Porównaj skróty wiadomości msg przed i po modyfikacji.

Realizacja

root@WSL:lab5_pliki> ls
'Lab5 - PI zaoczne - funkcje skrótu.pdf'  'Lab5 - funkcja skrótu.pdf'   iha1   iha1.c
root@WSL:lab5_pliki> echo "This is a trial message to test digest functions!" > msg
root@WSL:lab5_pliki> openssl dgst -md5 msg
MD5(msg)= 5f554df44e51b0eea071ae49a854223e
root@WSL:lab5_pliki> openssl dgst -sha1 msg
SHA1(msg)= fdc68fd28db0c992b376a3edf36727ea66d2d391
root@WSL:lab5_pliki> openssl dgst -sha256 msg
SHA2-256(msg)= a2636d4da000f8b9174e3e1135c6ccd70a098b1e7e3843c3505e54361a572b44
root@WSL:lab5_pliki> openssl dgst -md5 msg > MD5dgst
root@WSL:lab5_pliki> openssl dgst -sha1 msg > SHA1dgst
root@WSL:lab5_pliki> openssl dgst -sha256 msg > SHA256dgst
root@WSL:lab5_pliki> sed 's/!//' msg > msg_mod
root@WSL:lab5_pliki> mv msg_mod msg
root@WSL:lab5_pliki> openssl dgst -md5 msg > MD5dgst_mod
root@WSL:lab5_pliki> openssl dgst -sha1 msg > SHA1dgst_mod
root@WSL:lab5_pliki> openssl dgst -sha256 msg > SHA256dgst_mod
root@WSL:lab5_pliki> diff MD5dgst MD5dgst_mod
1c1
< MD5(msg)= 5f554df44e51b0eea071ae49a854223e
---
> MD5(msg)= 946e2d2c199b85da32acfd9177bf562c
root@WSL:lab5_pliki> diff SHA1dgst SHA1dgst_mod
1c1
< SHA1(msg)= fdc68fd28db0c992b376a3edf36727ea66d2d391
---
> SHA1(msg)= 5fcb355462f783d439712044e720091c2b6c8dbb
root@WSL:lab5_pliki> diff SHA256dgst SHA256dgst_mod
1c1
< SHA2-256(msg)= a2636d4da000f8b9174e3e1135c6ccd70a098b1e7e3843c3505e54361a572b44
---
> SHA2-256(msg)= cea38c49da18b7b1c2fee0cccbb51f48355b39cb3cd68588eba81a9f3f30ed97
root@WSL:lab5_pliki>

Pytania

Co można powiedzieć po porównaniu tych skrótów?

Po porównaniu skrótów wiadomości przed i po modyfikacji można zauważyć, że nawet drobna zmiana (np. usunięcie pojedynczego znaku) prowadzi do całkowicie innego wyniku funkcji skrótu. Jest to cecha charakterystyczna funkcji skrótu zwana efektem lawiny, która zapewnia, że zmiana choćby jednego bitu danych wejściowych znacznie zmienia wartość skrótu.

Jakiej długości (wyrażonej w bitach) są poszczególne skróty?

Każda funkcja skrótu zawsze generuje wynik o ustalonej długości niezależnie od długości danych wejściowych.

Zadanie 2

Treść Zadania:
W tym zadaniu wykorzystywany jest przykładowy algorytm skrótu zaimplementowany w języku C (program iha1.c) Algorytm nazywa się IHA1 (Insecure Hash Agorithm) i działa na zasadzie funkcji XOR wykonywanej bitowo, a wynikiem jest liczba reprezentowana na 4 bitach (jedna cyfra w zapisie hexadecymalnym).
Krok 1: Dokonaj kompilacji pliku iha1.c poprzez wydanie polecenia:

gcc iha1.c -o iha1

Krok 2: Przygotuj plik z tekstem jawnym np. „This is a test message”.
Krok 3: Użyj funkcji iha1 do obliczenia skrótu wiadomości zapisanej w w/w pliku.
Składnia wywołania jest następująca:

iha1 nazwa_pliku

Przykładowy wynik działania programu iha1:

root@kali:~/lab11/support> ./iha1 message
    iha1(message) = A

Krok 4: Spróbuj znaleźć kolizję (stwórz inną wiadomość jawną która będzie miała taki sam skrót co wiadomość stworzona w tym zadaniu). W tym celu wykonaj 160 prób, w każdej próbie podając na wejście funkcji skrótu inną wartość.

Realizacja

root@WSL:lab5_pliki> ls
'Lab5 - PI zaoczne - funkcje skrótu.pdf'  'Lab5 - funkcja skrótu.pdf'   MD5dgst   MD5dgst_mod   SHA1dgst   SHA1dgst_mod   SHA256dgst   SHA256dgst_mod   iha1.c   msg
root@WSL:lab5_pliki> gcc iha1.c -o iha1
root@WSL:lab5_pliki> echo "This is a test message" > message
root@WSL:lab5_pliki> ./iha1 message
iha1(message) = A
root@WSL:lab5_pliki> cat > script.sh
mkdir collisions
cd collisions
 
for i in $(seq 1 160); do
echo "Test message number $i" > test_$i.txt
../iha1 test_$i.txt >> results.txt
done
 
^C
root@WSL:lab5_pliki> chmod +x script.sh
root@WSL:lab5_pliki> ./script.sh
root@WSL:lab5_pliki> ls
'Lab5 - PI zaoczne - funkcje skrótu.pdf'   MD5dgst       SHA1dgst       SHA256dgst       collisions   iha1.c    msg
'Lab5 - funkcja skrótu.pdf'                MD5dgst_mod   SHA1dgst_mod   SHA256dgst_mod   iha1         message   script.sh
root@WSL:lab5_pliki> cd collisions/
root@WSL:collisions> ls
results.txt  test_11.txt  test_121.txt test_133.txt test_145.txt test_157.txt  
[...]
test_35.txt  test_47.txt  test_59.txt  test_70.txt  test_82.txt  test_94.txt
root@WSL:collisions> cat results.txt | grep '= A'
iha1(test_68.txt) = A
iha1(test_79.txt) = A
iha1(test_86.txt) = A
iha1(test_97.txt) = A
root@WSL:collisions>

Pytania

Ile średnio potrzebnych jest operacji, żeby znaleźć kolizję dla wyżej opisanego przypadku? Odpowiedź uzasadnij.

Algorytm IHA1 zwraca wynik skrótu reprezentowany jako jedna cyfra szesnastkowa, co oznacza że wynik może przyjąć jedną z 16 wartości (od 0 do F, czyli $2^4 = 16$ możliwych wyników).

Zgodnie z teorią prawdopodobieństwa oraz tzw. paradoksem urodzin, średnia liczba operacji potrzebna do znalezienia kolizji dla funkcji skrótu o $n$ możliwych wartościach wynosi około: $$\sqrt{\frac{\pi n}{2}} \approx 1.253 \cdot \sqrt{n}$$ Dla $n = 16$: $$\sqrt{\frac{\pi \cdot 16}{2}} \approx \sqrt{8\pi} \approx 5.01$$

Zatem średnio potrzeba około 5 różnych wiadomości (czyli 4–5 prób), aby z dużym prawdopodobieństwem znaleźć kolizję dla funkcji skrótu zwracającej 4-bitowy wynik. W najgorszym przypadku może to być do 16 prób, jednak statystycznie znacznie szybciej znajdziemy kolizję.

Zadanie 3

Treść Zadania:
Krok 1: Na maszynie A przygotuj wiadomość zapisaną w pliku np. „This is a test message used to calculate a keyed digest”.
Krok 2: Na maszynie A przygotuj klucz i zapisz go w pliku.
Krok 3: Zapisz i wykonaj polecenie openssl, które pozwoli na obliczenie funkcji HMAC na przygotowanej wiadomości z kluczem zapisanym w pliku z użyciem algorytmu MD5.
Krok 4: Zapisz i wykonaj polecenie openssl, które pozwoli na weryfikację poprawności obliczonej funkcji HMAC na podstawie znanej wiadomości oraz znanego klucza.

Realizacja

root@WSL:lab5_pliki> ls
'Lab5 - PI zaoczne - funkcje skrótu.pdf'   MD5dgst       SHA1dgst       SHA256dgst       collisions   iha1.c    msg
'Lab5 - funkcja skrótu.pdf'                MD5dgst_mod   SHA1dgst_mod   SHA256dgst_mod   iha1         message   script.sh
root@WSL:lab5_pliki> echo "This is a test message used to calculate a keyed digest" > message.txt
root@WSL:lab5_pliki> echo "secretkey123" > key.txt
root@WSL:lab5_pliki> chmod 600 key.txt
root@WSL:lab5_pliki> openssl dgst -md5 -mac HMAC -macopt key:file:key.txt message.txt > hmac.md5
root@WSL:lab5_pliki> openssl dgst -md5 -mac HMAC -macopt key:file:key.txt message.txt
HMAC-MD5(message.txt)= b6e3401fc3288777c285655aabd5d6d6
root@WSL:lab5_pliki> cat hmac.md5
HMAC-MD5(message.txt)= b6e3401fc3288777c285655aabd5d6d6
root@WSL:lab5_pliki>

Pytania

Co musi wiedzieć użytkownik maszyny B jeżeli chce zweryfikować prawdziwość skrótu HMAC z kluczem? Odpowiedź uzasadnij.

Użytkownik maszyny B musi znać dokładnie ten sam klucz symetryczny, który został użyty przez maszynę A do obliczenia funkcji HMAC. Jest to niezbędne, ponieważ funkcja HMAC wykorzystuje klucz nie tylko do skrócenia wiadomości, ale do generowania unikalnego podpisu kryptograficznego. Bez znajomości tego klucza niemożliwe jest odtworzenie tego samego HMAC i tym samym jego weryfikacja.

Jakie operacje musi wykonać użytkownik maszyny B jeżeli chce zweryfikować prawdziwość wiadomości i skrótu otrzymanych od użytkownika maszyny A? Odpowiedź uzasadnij.

Aby zweryfikować prawdziwość wiadomości i skrótu HMAC, użytkownik maszyny B musi:

  1. Otrzymać wiadomość i oryginalny HMAC od użytkownika maszyny A.
  2. Upewnić się, że posiada ten sam klucz, który został użyty do wygenerowania HMAC.
  3. Obliczyć lokalnie nowy HMAC z otrzymanej wiadomości, używając tego samego algorytmu (MD5) i klucza.
  4. Porównać wynik z otrzymanym HMAC.

Jeśli oba skróty są identyczne, oznacza to, że wiadomość nie została zmodyfikowana oraz że HMAC został wygenerowany z właściwego klucza. W przeciwnym razie wiadomość mogła zostać zmieniona lub klucz jest niepoprawny.

Zadanie 4

Treść Zadania: Dokonaj oceny wydajności algorytmów obliczających skrót wiadomości. Wypełnij Tabelę 1 wpisując wartość średnią czasu obliczania funkcji skrótu obliczoną z 12 pomiarów. Stwórz pliki o rozmiarach: 100kB, 1MB, 10MB, 100MB. Do tego celu można użyć poniższej komendy:

openssl rand -out nazwa_pliku.txt rozmiar_pliku_w_bajtach

Do sprawdzenia czasu wykonywania operacji obliczania skrótu można wykorzystać komendę ’time’:

time polecenie_obliczające_skrót

Przykładowy wynik polecenia: time

real 0m5.126s
user 0m0.004s
sys 0m0.012s

Wpisz przedziały ufności obliczone dla poziomu ufności równego 95%. Wyniki przedstaw również w postaci wykresu. W sprawozdaniu umieść analizę wyników wydajności obliczania funkcji skrótu (sprawdź wyniki – uzyskane czasy dla różnych rozmiarów wiadomości wejściowych oraz dla różnych rozmiarów funkcji skrótu).

Realizacja

root@WSL:lab5_pliki> ls
'Lab5 - PI zaoczne - funkcje skrótu.pdf'   MD5dgst       SHA1dgst       SHA256dgst       collisions   iha1     key.txt   message.txt   script.sh
'Lab5 - funkcja skrótu.pdf'                MD5dgst_mod   SHA1dgst_mod   SHA256dgst_mod   hmac.md5     iha1.c   message   msg
root@WSL:lab5_pliki> openssl rand -out file_100KB.txt 102400
root@WSL:lab5_pliki> openssl rand -out file_1MB.txt 1048576
root@WSL:lab5_pliki> openssl rand -out file_10MB.txt 10485760
root@WSL:lab5_pliki> openssl rand -out file_100MB.txt 104857600
root@WSL:lab5_pliki> ls -lah file_*
-rwxrwxrwx 1 root root 100K May  4 12:01 file_100KB.txt
-rwxrwxrwx 1 root root 100M May  4 12:02 file_100MB.txt
-rwxrwxrwx 1 root root  10M May  4 12:02 file_10MB.txt
-rwxrwxrwx 1 root root 1.0M May  4 12:01 file_1MB.txt
root@WSL:lab5_pliki> for i in {1..12}; do time openssl dgst -md5 file_100KB.txt; done
MD5(file_100KB.txt)= 4f03d8f7e57ee31b3c17495e0ce06af5
 
real    0m0.024s
user    0m0.000s
sys     0m0.016s
MD5(file_100KB.txt)= 4f03d8f7e57ee31b3c17495e0ce06af5
 
[...]
 
SHA2-512(file_100MB.txt)= a36f6a029dcf6195926179cdd260e6133e8638a300f83eeaf74589ff639b267837f846108dcffb85734da58037dd274013f5a649c1d0107b13f8a0872f909a1e
 
real    0m0.429s
user    0m0.297s
sys     0m0.141s
root@WSL:lab5_pliki>

Analiza wydajności dla każdego algorytmu

Poniżej zestawiono średnie czasy „real” (w sekundach) i 95% przedziały ufności dla 12 pomiarów każdego algorytmu i rozmiaru pliku. Wspólny współczynnik t-Studenta dla 11 stopni swobody: $t_{0.975,11}\approx2.201$.

Wzory obliczeniowe

Średnia arytmetyczna

$$\bar{x} \;=\;\frac{1}{n}\sum_{i=1}^{n}x_i$$

Odchylenie standardowe próbki

$$s \;=\;\sqrt{\frac{1}{n-1}\sum_{i=1}^{n}(x_i - \bar{x})^2}$$

Przedział ufności 95%

$$\bar{x} \;\pm\; t_{\alpha/2,\;n-1}\,\frac{s}{\sqrt{n}}$$

MD5

100 kB

1 MB

10 MB

100 MB

SHA-1

100 kB

1 MB

10 MB

100 MB

RIPEMD-160

100 kB

1 MB

10 MB

100 MB

SHA-256

100 kB

1 MB

10 MB

100 MB

SHA-512

100 kB

1 MB

10 MB

100 MB

Wykres

graph_hash.py
import matplotlib.pyplot as plt
 
# Average times (in seconds) for each algorithm and file size
file_sizes = ["100 kB", "1 MB", "10 MB", "100 MB"]
md5_times = [0.01592, 0.01958, 0.05375, 0.35517]
sha1_times = [0.02275, 0.01992, 0.05183, 0.34858]
ripemd160_times = [0.01808, 0.02317, 0.08158, 0.61450]
sha256_times = [0.01775, 0.02483, 0.10167, 0.53550]
sha512_times = [0.04625, 0.02167, 0.08858, 0.45167]
 
plt.figure()
plt.plot(file_sizes, md5_times, marker='o', label='MD5')
plt.plot(file_sizes, sha1_times, marker='o', label='SHA1')
plt.plot(file_sizes, ripemd160_times, marker='o', label='RIPEMD-160')
plt.plot(file_sizes, sha256_times, marker='o', label='SHA-256')
plt.plot(file_sizes, sha512_times, marker='o', label='SHA-512')
 
plt.xlabel("Rozmiar pliku")
plt.ylabel("Sredni czas obliczen [s]")
plt.title("Porównanie wydajności funkcji skrótu")
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()  

Wykres porównujący wydajność funkcji skrótu

Tabela

Wydajność obliczania funkcji skrótu (średni czas „real” w sekundach z 12 pomiarów)

100 kB 1 MB 10 MB 100 MB
MD5 0.0159 0.0196 0.0538 0.3552
SHA1 0.0228 0.0199 0.0518 0.3486
RMD160 0.0181 0.0232 0.0816 0.6145
SHA256 0.0178 0.0248 0.1017 0.5355
SHA512 0.0463 0.0217 0.0886 0.4517