\documentclass[a4paper,12pt]{article}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage[polish]{babel}
\usepackage{polski}
\usepackage[utf8]{inputenc}
\usepackage{indentfirst}
\usepackage{geometry}
\usepackage{array}
\usepackage[pdftex]{color,graphicx}
\usepackage{subfigure}
\usepackage{afterpage}
\usepackage{setspace}
\usepackage{color}
\usepackage{wrapfig}
\usepackage{listings}
\usepackage{datetime}

\renewcommand{\onehalfspacing}{\setstretch{1.6}}

\geometry{tmargin=2.5cm,bmargin=2.5cm,lmargin=2.5cm,rmargin=2.5cm}
\setlength{\parindent}{1cm}
\setlength{\parskip}{0mm}

\newenvironment{lista}{
\begin{itemize}
  \setlength{\itemsep}{1pt}
  \setlength{\parskip}{0pt}
  \setlength{\parsep}{0pt}
}{\end{itemize}}

\newcommand{\linia}{\rule{\linewidth}{0.4mm}}

\definecolor{lbcolor}{rgb}{0.95,0.95,0.95}
\lstset{
    backgroundcolor=\color{lbcolor},
    tabsize=4,
  language=C++,
  captionpos=b,
  tabsize=3,
  frame=lines,
  numbers=left,
  numberstyle=\tiny,
  numbersep=5pt,
  breaklines=true,
  showstringspaces=false,
  basicstyle=\footnotesize,
  identifierstyle=\color{magenta},
  keywordstyle=\color[rgb]{0,0,1},
  commentstyle=\color{Darkgreen},
  stringstyle=\color{red}
  }

\begin{document}

\begin{titlepage}
	\centering

	{\scshape\LARGE Programowanie równoległe i rozproszone\par}
	\vspace{1cm}
	{\scshape\Large Projekt końcowy\par}
	\vspace{1.5cm}
	{\huge\bfseries Łamanie hasła zakodowanego funkcją skrótu SHA-1 za pomocą algorytmu siłowego wykorzystując środowisko MPI.\par}
	\vspace{2cm}
	{\Large\itshape Piotr Dergun\par}
	{\Large\itshape Dominik Waśko\par}
	\vfill
	Prowadzący\par
	dr inż. Joanna Płażek

	\vfill


	{\large \today\par}
\end{titlepage}

\title{Łamanie hasła zakodowanego funkcją skrótu SHA-1 za pomocą algorytmu siłowego wykorzystując środowisko MPI. 
}

\section{Opis projektu}

Celem projektu było zaimplementowanie programu pozwalającego na załamanie hashu sha1. Zastosowano metodę typu brute force. Zrównoleglenia  programu dokonano za pomocą MPI. Program uruchomiono na klasterze komputerowym składającym się z 13 węzłów.

\section{Algorytm brute force }
Algorytm brute force, inaczej algorytm siłowy. Ten algorytm opiera się na sukcesywnym sprawdzaniu wszystkich możliwych kombinacji w poszukiwaniu rozwiązania problemu. Nie skupia się na jego szczegółowej analizie. Zwykle jest to nieoptymalna metoda, jednak jest prosta w do zaimplementowania. Teoretycznie pozwala ona złamać każde hasło.  praktycznie, w zależności od skomplikowania hasła, może to być bardzo długi czas (np. dłuższy niż istnieje Wszechświat). W zależności od kontekstu, w którym termin brute force zostaje użyty, może mieć on nieznacznie różne definicje. Metoda brute-force może być przeprowadzony wobec prawie wszystkich szyfrów symetrycznych i szyfrów asymetrycznych. 

\section{SHA-1} 
SHA-1 (Secure Hash Algorithm) jest jednokierunkową funkcją hashującą zaprojektowaną przez National Security Agency (NSA) i opublikowaną przez National Institute of Standards and Technology (NIST. Wytwarza ona skrót o długości 160 bitów z wiadomości o dowolnym rozmiarze, nie większym niż ${}2^{64}$ bitów. Funkcja SHA-1 jest
oparta na zasadach podobnych, do użytych przez profesora Ronalda L. Rivesta z MIT przy projektowaniu   funkcji   hashujących   MD4   i   MD5, i w znacznym stopniu naśladuje te algorytmy. Oryginalna specyfikacja tego algorytmu została opublikowana w 1993 roku jako Secure Hash Standard, FIPS PUB 180. Ta wersja jest teraz często nazywana jako SHA-0. Była ona wycofana przez NSA krótko po publikacji i zastąpiona przez poprawioną wersję
opublikowaną w 1995 roku w FIPS PUB 180-1 i nazwaną SHA-1.

\section*{Opis algorytmu SHA-1}
W pierwszej kolejności wiadomość jest uzupełniana w ten sposób, że jej długość
jest wielokrotnością 512 bitów (uzupełnienie to jest takie samo jak w algorytmie MD5).
Na początku jest dopisywana jedynka, a później tyle zer, ile jest niezbędnych, aby
uzyskać długość o 64 bity krótszą od wielkości liczby 512. Na końcu jest dołączany 64-
bitowy ciąg, w którym jest zapisana długość wiadomości przed rozpoczęciem operacji
dopisywania.
\newline

Pięć 32-bitowych zmiennych jest inicjowanych następująco:
\newline
A = 67 45 23 01
\newline
B = EF CD AB 89
\newline
C = 98 BA DC FE
\newline
E = C3 D2 EI F0
\newline

Następnie rozpoczyna się główna pętla algorytmu. W pętli tej jest przetwarzany
każdorazowo 512-bitowy blok wiadomości i proces ten jest powtarzany tyle razy, ile 512-
bitowych bloków zawiera ta wiadomość.

Najpierw wartości powyższych pięciu zmiennych są kopiowane na wartości pięciu
innych zmiennych: A NA AA, B na BB, C na CC, D na DD i E na EE.

Główna pętla składa się z czterech cykli, z których każdy zawiera 20 operacji.
Każda operacja składa się z nieliniowej operacji na trzech spośród czterech zmiennych A, B, C i D oraz  z  przesunięcia   i  sumowania. Zbiór nieliniowych funkcji SHA jest następujący:
\newline

f(X,Y,Z) = XY or (not X)Z - dla pierwszych dwudziestu operacji,

f(X,Y,Z) = X xor Y xor z - dla kolejnych dwudziestu operacji,

f(X,Y,Z) = XY or XY or YZ - dla trzeciej dwudziestu operacji,

f(X,Y,Z) = X xor Y xor z - dla ostatniej dwudziestki operacji. 



W algorytmie używa się też czterech stałych:
K1 = 5A827999 - dla pierwszych dwudziestu operacji
K2 = 6ED9EBA1 - dla drugiej dwudziestki operacji
K3 = 8F1BBCDC - dla trzeciej dwudziestki operacji
K4 = CA62C1D6 - dla czwartej dwudziestki operacji
\newline


Pojedynczy blok wiadomości składający się z szesnastu 32-bitowych słów (od M0 do M15) jest transformowany do osiemdziesięciu słów 32-bitowych(od W0 do W79) przy wykorzystaniu następującego algorytmu: 

${}W_{i}$ = ${}M_{i}$ dla i = 0,1,....,15;

${}W_{i}$ = ${}M_{i-3}  \oplus {}M_{i-8} \oplus {}M_{i-14} \oplus {}M_{i-16}$ dla i = 16,17,....,79;
\newline

Jeśli i oznacza numer operacji (od 0 do 79), Mj reprezentuje podblok wiadomości o numerze j (od 0 do 15), a<<< s oznacza cykliczne przesunięcie w w lewo o s bitów, to te 80 operacji może być przestawionych następująco:

TEMP = (A<<<5) + f(B,C,D) + E + Wi +Ki;

E = D;

D = C;

C = (B <<< 30);

B = A;

A = TEMP;
\newline

Po wykonaniu wszystkich powyższych działań zmienne  A,  B,  C,  D  i  E są dodawane odpowiednio do zmiennych AA, BB, CC, DD i EE i algorytm jest kontynuowany dla
następnego bloku wiadomości. Ostatecznie wyjściem algorytmu jest konkatenacja zmiennych A, B, C, D i E.


\section*{Stopień bezpieczeństwa SHA-1}
SHA-1 jest nazywany bezpiecznym algorytmem, gdyż jest on tak zaprojektowany,
aby było obliczeniowo niewykonalne uzyskanie wiadomości odpowiadającej danemu
skrótowi wiadomości lub znalezienie dwóch różnych wiadomości mających takie same
skróty. Jakakolwiek zmiana w wiadomości, powstała podczas jej przesyłania, spowoduje,
z bardzo dużym prawdopodobieństwem, powstanie innego skrótu wiadomość

\section*{Inne warianty SHA}
NIST opublikował trzy dodatkowe warianty SHA, każdy z dłuższym skrótem. Ich nazwy,
są zależne od długości wytwarzanego skrótu (w bitach): SHA-256, SHA-384 i SHA-512.
Funkcje te były pierwszy raz opublikowane w 2001 roku w projekcie FIPS PUB 180-2,
który także zawiera SHA-1. Projekt ten został uznany za oficjalny standard w 2002 roku. Te nowe funkcje nie zostały jeszcze tak dobrze zbadane jak SHA-1 i nie są uważane
jeszcze za bezpieczne. 


\section{Programowanie równoległe }
W programowaniu równoległym mamy wiele podejść do kwestii wymiany danych między poszczególnymi procesami obliczeniowymi, w ramach jednego zadania równoległego. Począwszy od modeli, w których każdy proces ma bezpośredni dostęp do pamięci wszystkich pozostałych procesów (rozwiązanie rzadko spotykane w praktyce, poza komputerami o pamięci współdzielonej fizycznie), poprzez bezpośredni dostęp tylko do części danych, deklarowanych jako współdzielone (przykładem jest Co-Array Fortran), aż do modeli, w których nie ma bezpośredniego dostępu do pamięci innych procesów obliczeniowych, a wszelka wymiana danych z innymi procesami musi zachodzić przy współpracy stron, poprzez wymianę komunikatów. 

\section{MPI}
W projekcie wykorzystano MPI. Message Passing Interface, jest przykładem ostatniego podejścia. To jest specyfikacją biblioteki funkcji opartych na modelu wymiany komunikatów dla potrzeb programowania równoległego. Transfer danych pomiędzy poszczególnymi procesami programu wykonywanymi na procesorach maszyn będących węzłami klastra odbywa się za pośrednictwem sieci.

MPI nie został usankcjonowany przez żaden z głównych standardów; mimo tego został de facto standardem komunikacyjnym pomiędzy procesami w programowaniu równoległym korzystającym z rozproszonego systemu pamięci. Główny model MPI-1 nie wspiera koncepcji współdzielonej pamięci, MPI-2 wspiera (w sposób ograniczony) rozproszony system pamięci dzielonej. Mimo tego programy MPI są bardzo często uruchamiane na komputerach o współdzielonej pamięci.

Mimo iż MPI należy do piątej (lub wyższych) warstw w modelu OSI, jego implementacje mogą pokrywać większość warstw modelu z gniazdem oraz TCP użytym jako warstwa transportowa. Większość implementacji MPI składa się ze specyficznego zestawu operacji, mogących zostać wywoływanymi z poziomu języków programowania.

Program w MPI składa się z niezależnych procesów operujących na różnych danych (MIMD). Każdy proces wykonuje się we własnej przestrzeni adresowej, aczkolwiek wykorzystanie pamięci współdzielonej też jest możliwe.

Zaletami MPI nad starszymi bibliotekami przekazywania wiadomości są przenośność oraz prędkość. Przenośność, ponieważ MPI został zaimplementowany dla każdej architektury opartej na rozproszonej pamięci. Prędkość, ponieważ każda implementacja jest zoptymalizowana pod sprzęt, na którym działa. Standard udostępnia zbiór precyzyjnie zdefiniowanych metod, które mogą być efektywnie zaimplementowane. Stał się on punktem wyjściowym do stworzenia praktycznego, przenośnego, elastycznego i efektywnego narzędzia do przesyłania komunikatów (ang. message passing). Standard MPI pozwala na jego zastosowanie zarówno w komputerach równoległych, jak i heterogenicznych sieciach stacji roboczych.

Standard nie zabrania, aby poszczególne procesy były wielowątkowe. Nie są też udostępnione mechanizmy związane z rozłożeniem obciążenia pomiędzy poszczególne procesy, z architekturą rozkładu procesorów, z dynamicznym tworzeniem i usuwaniem procesów. Procesy są identyfikowane poprzez ich numer w grupie w zakresie od 0 do groupsize – 1.

\section{Opis klastra}
Program został uruchomiony na klastrze komputerowym składającym się z 13 wezłów i w sumie dysponującym 48 rdzeniami. W tabeli 1. przedstawiono dokładną konfigurację klastra.  

\begin{table}

\centering
\begin{tabular}{|c|c|c|c|c|}
  \hline 
   \textbf{Hardware} & \textbf{Ilość} & \textbf{CPU} & \textbf{Ilość rdzeni CPU} & \textbf{Wykorzystane rdzenie}\\
  \hline
  \
iMac6,1 & 4 & Core2Duo 2,16 GHz & 2 & 1\\
  \hline
iMac14,2 & 3 & i5-4570 3,2Ghz & 4 & 2\\
  \hline
MacPro3,1 & 3 & 2x Xeon E5462 2,8 GHz & 8 & 6\\
  \hline
MacPro4,1 & 2 & 2x Xeon E5520 2,26 GHz & 8 & 6\\
  \hline
MacPro5,1 & 1 & 2x Xeon E5620 2,4 GHz & 8 & 8\\
  \hline
\end{tabular} 
\caption{Konfiguracja klastra}
\end{table}

\section{Opis programu}
Program został napisany w języku C++. Wykorzystana implementacja MPI to MPICH2. Zastosowany w programie algorytm jest algorytmem siłowym. Najpierw generuje hasło, potem oblicza z niego sha1 i porównuje z hashem podanym jako argument programu. Gdy są takie same program wypisuje hasło oraz czas obliczeń. Jeśli nie są zgodne wtedy próbuje szczęścia z kolejnym hasłem. Dla potrzeb tego projektu dokonano kilku założeń dotyczących hasła:

\begin{lista}
 \item Hasło może mieć długość od 2 do 10 znaków.
 \item Może składać się ze znaków alfanumerycznych(wielkie oraz oraz litery) oraz znaków specjalnych: \_  ! -  oraz spacja. W sumie 64 znaki. 
 \item  Ze względu na długi czas obliczeń (ponad 20 minut) nie testowano haseł o długości powyżej 7 znaków. 
\end{lista}  

Takie założenia pozwalają na wygenerowanie od ${}64^{2}$ (hasła dwuznakowe) do ${}64^{10}$  (hasła 10-znakowe) różnych haseł. 

Hasło są przydzielane do procesów kolejno, czyli dla n-ty proces otrzymuje n-te wygenerowane hasło. Taki podział obliczeń pozwala na równomierne obciążenie procesów a co za tym idzie poszczególnych węzłów.

W programie istnieje pewnego rodzaju ograniczenie co do ilości znaków z których  generowane jest hasło. Powinna to być liczba typu ${}2^{n}$ (w tym projekcie jest to 64). Wynika to z tego, że główny licznik pętli odpowiedzialnej za generowanie haseł operuje przepełnieniem bitowym. Możliwość użycia innej niż  ${}2^{n}$ liczby znaków nie pozostała by bez wpływu na szybkość całego programu. 

Funkcja generująca hash SHA-1 została zaimplementowana przez autorów. W stopniu na jaki pozwalały umiejętność autorów została również zoptymalizowana pod kątem jak najszybszego wykonania.
   
\section{Wyniki}

Program testowano na dwóch hasłach 3 znakowych  oraz dwóch hasłach 5-cio znakowych. Wykresy (Rysunek 2. do 9.) przedstawiają uzyskane wyniki. Można na nich zauważać, że wzrost przyspieszenia uzyskuje się tylko do momentu gdy liczba procesów jest mniejsza lub równa ilości fizycznych rdzeni. Powyżej 48 rdzeni następuje już przetwarzanie współbieżne, co powoduje duże wahania w czasie obliczeń. 

Mało równomierny wzrost przyspieszenia można wytłumaczyć tym że w pewnych przypadkach podziału hashy do obliczenia proces może szybciej natrafić na rozwiązanie. Obrazuje to Rysunek 1. W przypadku podziału na 3 cześć rozwiązanie jest znajdowane już w pierwszej iteracji. Przy podziale na 4 każdy proces ma mniej do zadań ale rozwiązanie będzie dopiero w ostatniej iteracji. 

\begin{figure}[h]
\includegraphics[width=1.1\textwidth]{wykr/podzial.png}
\caption{Podział zadań pomiędzy procesy}
\label{fig:figure2}
\end{figure}


\begin{figure}[h]
\includegraphics[width=1.1\textwidth]{wykr/1_3_czas.jpg}
\caption{Wykres czasu obliczeń od ilości procesów dla hasła: 884}
\label{fig:figure2}
\end{figure}

\begin{figure}[h]
\includegraphics[width=1.1\textwidth]{wykr/1_3_przysp.jpg}
\caption{Wykres przyspieszenia od ilości procesów dla hasła: 884}
\label{fig:figure2}
\end{figure}

\begin{figure}[h]
\includegraphics[width=1.1\textwidth]{wykr/2_3_czas.jpg}
\caption{Wykres czasu obliczeń od ilości procesów dla hasła: h81}
\label{fig:figure2}
\end{figure}

\begin{figure}[h]
\includegraphics[width=1.1\textwidth]{wykr/2_3_przysp.jpg}
\caption{Wykres przyspieszenia od ilości procesów dla hasła: h81}
\label{fig:figure2}
\end{figure}


\begin{figure}[h]
\includegraphics[width=1.1\textwidth]{wykr/1_5_czas.jpg}
\caption{Wykres czasu obliczeń od ilości procesów dla hasła: 58JjA}
\label{fig:figure2}
\end{figure}

\begin{figure}[h]
\includegraphics[width=1.1\textwidth]{wykr/1_5_przysp.jpg}
\caption{Wykres przyspieszenia od ilości procesów dla hasła: 58JjA}
\label{fig:figure2}
\end{figure}

\begin{figure}[h]
\includegraphics[width=1.1\textwidth]{wykr/2_5_czas.jpg}
\caption{Wykres czasu obliczeń od ilości procesów dla hasła: hjwe}
\label{fig:figure2}
\end{figure}

\begin{figure}[h]
\includegraphics[width=1.1\textwidth]{wykr/2_5_przysp.jpg}
\caption{Wykres przyspieszenia od ilości procesów dla hasła: hjwe}
\label{fig:figure2}
\end{figure}












\end{document}
