diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f87f23..9ec2eac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ endif() # Projet ======================================================================= # Nom du projet et description. -project (StegX VERSION 1.0.0 +project (StegX VERSION 1.1.0 DESCRIPTION "Projet de Stéganographie pour l'UVSQ" LANGUAGES C ) diff --git a/README.md b/README.md index 1708010..e5c0c81 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Fonctionnalités * **AVI** : Junk Chunk. * **FLV** : EOF, EOC. * **WAVE** : LSB, EOF. - * **MP3** : À venir dans une prochaine version (1er juin). + * **MP3** : LSB, EOF. * _Description, avantages et inconvénients des algorithmes_ : * **LSB (Least Significant Bit)** : Cet algorithme consiste à modifier les diff --git a/env/test/mp3/MP3_Stereo_44,1kHz_160kbps.mp3 b/env/test/mp3/MP3_ID3v1_Stereo_44,1kHz_160kbps.mp3 similarity index 100% rename from env/test/mp3/MP3_Stereo_44,1kHz_160kbps.mp3 rename to env/test/mp3/MP3_ID3v1_Stereo_44,1kHz_160kbps.mp3 diff --git a/env/test/mp3/MP3_ID3v1_Stereo_44,1kHz_160kbps_EOF.mp3 b/env/test/mp3/MP3_ID3v1_Stereo_44,1kHz_160kbps_EOF.mp3 new file mode 100644 index 0000000..4f3a314 Binary files /dev/null and b/env/test/mp3/MP3_ID3v1_Stereo_44,1kHz_160kbps_EOF.mp3 differ diff --git a/env/test/mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_1.mp3 b/env/test/mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_1.mp3 new file mode 100644 index 0000000..34f4af3 Binary files /dev/null and b/env/test/mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_1.mp3 differ diff --git a/env/test/mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_1_EOF.mp3 b/env/test/mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_1_EOF.mp3 new file mode 100644 index 0000000..e1311e0 Binary files /dev/null and b/env/test/mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_1_EOF.mp3 differ diff --git a/env/test/mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_2.mp3 b/env/test/mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_2.mp3 new file mode 100644 index 0000000..4567798 Binary files /dev/null and b/env/test/mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_2.mp3 differ diff --git a/env/test/mp3/MP3_Mono_44,1kHz_64kbps.mp3 b/env/test/mp3/MP3_ID3v2.4_Mono_44,1kHz_64kbps.mp3 similarity index 100% rename from env/test/mp3/MP3_Mono_44,1kHz_64kbps.mp3 rename to env/test/mp3/MP3_ID3v2.4_Mono_44,1kHz_64kbps.mp3 diff --git a/env/test/mp3/MP3_ID3v2.4_Unsync_Stereo_44,1kHz_192kbps.mp3 b/env/test/mp3/MP3_ID3v2.4_Unsync_Stereo_44,1kHz_192kbps.mp3 new file mode 100644 index 0000000..6a86ce6 Binary files /dev/null and b/env/test/mp3/MP3_ID3v2.4_Unsync_Stereo_44,1kHz_192kbps.mp3 differ diff --git a/env/test/mp3/MP3_ID3v2.4_Unsync_Stereo_44,1kHz_192kbps_EOF.mp3 b/env/test/mp3/MP3_ID3v2.4_Unsync_Stereo_44,1kHz_192kbps_EOF.mp3 new file mode 100644 index 0000000..40ee358 Binary files /dev/null and b/env/test/mp3/MP3_ID3v2.4_Unsync_Stereo_44,1kHz_192kbps_EOF.mp3 differ diff --git a/report/4.CompteRendu/compteRendu2.tex b/report/4.CompteRendu/compteRendu2.tex new file mode 100644 index 0000000..0524f4d --- /dev/null +++ b/report/4.CompteRendu/compteRendu2.tex @@ -0,0 +1,190 @@ +\documentclass[11pt]{article} +%\documentclass{book} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage[french]{babel} +\usepackage[top=1.8cm, bottom=1.8cm, left=1.8cm, right=1.8cm]{geometry} +\usepackage[linktocpage,colorlinks=false]{hyperref} +\usepackage{graphicx} +\usepackage{epsfig} +\usepackage{amssymb} +\usepackage{amsmath} +\usepackage{array} +\usepackage{subfig} +\usepackage{multicol} +\usepackage{caption} +\usepackage{listings} +\usepackage{algorithm} +\usepackage{algorithmic} +\usepackage{array,multirow,makecell} +\hypersetup{ + colorlinks=true, + breaklinks=true, + urlcolor=red, +} +\parskip=5pt + +\title{\huge{\textbf Ajouts du compte rendu version du 1{er} juin}} +\author{AYOUB Pierre, BASKEVITCH Claire, BESSAC Tristan, \\ +CAUMES Clément, DELAUNAY Damien, DOUDOUH Yassin} +\date{Mercredi 1 Juin 2018} + +\begin{document} + +\maketitle +\vspace{20em} +\begin{center}\includegraphics{pictures/Application.png}\end{center} +\newpage + +\section{Introduction} + +L'équipe StegX a profité de la semaine jusqu'au 1er juin pour améliorer +l'application. De ce fait, sachant que le client ne proposait aucun format +à s'occuper, les développeurs ont voulu ajouter le format MP3. +Pourtant, le MP3 est un format très compliqué étant compressé. + +Pour la version du 25 mai, Pierre Ayoub et Damien Delaunay avaient réussi à +étudier les versions du format (MPEG 1 Layer III, MPEG 2 Layer III) et de +format de métadonnée (ID3 version 1 et ID3 version 2). +Et ils ne leur restaient plus qu'à implémenter un ou plusieurs algorithmes +pour le format MP3. +De ce fait, MP3 propose désormais les algorithmes EOF et LSB. + +Les études approfondies sur le MP3 ont mené à modifier certains choix dans +les spécifications en raison de la grande difficulté de ce format. + +\section{Changements des spécifications} + +\subsection{Structure MP3} + +\begin{lstlisting}[language=c] +struct mp3 { + long int fr_frst_adr; + long int fr_nb; + long int eof; +}; +\end{lstlisting} + +Cette structure comporte 3 champs : +\begin{itemize} +\item \textit{fr\_frst\_adr} est l'adresse du header de la première frame +MPEG 1/2 Layer III. +\item \textit{fr\_nb} est le nombre de frame MPEG 1/2 Layer III. +\item \textit{eof} est l'adresse de la fin du fichier officiel (sans signature et données cachées). +\end{itemize} + +Cette structure n'a pas pu être déduite lors des spécifications puisque +l'étude détaillée du format était trop bas niveau à cette période du projet. +En effet, la stéganographie sur le format MP3 est très expérimentale puisque +ce format est très complexe. + +\subsection{Ajouts de fonctions pour éviter la répétition de code} + +Pour réaliser correctement le remplissage de la structure afin d'éviter la +repétition de code lors des algorithmes proposés par le format MP3, +nous avons créé plusieurs fonctions internes au format : + +\begin{lstlisting}[language=c] +int mp3_mpeg_hdr_test(uint32_t hdr); +int mp3_id3v2_hdr_test(uint32_t hdr); +\end{lstlisting} + +Ces fonctions testent si le header hdr est un header MPEG 1/2 Layer III +ou un header ID3v2. +Elles renvoient 0 si le header est incorrect et 1 si le header est valide. +\newline + +\begin{lstlisting}[language=c] +int mp3_mpeg_fr_seek(uint32_t hdr, FILE * f); +long int mp3_mpeg_fr_find_first(FILE * f); +int mp3_mpeg_hdr_get_version(const uint32_t hdr); +int mp3_mpeg_hdr_is_padding(const uint32_t hdr); +int mp3_mpeg_hdr_get_samprate(const uint32_t hdr); +int mp3_mpeg_hdr_get_bitrate(const uint32_t hdr); +int mp3_mpeg_hdr_get_size(const uint32_t hdr); +int mp3_mpeg_fr_write(uint32_t hdr, FILE * src, FILE * dst); +\end{lstlisting} + +Ces fonctions concernent les tests internes au format MP3 et font les tests +en rapport aux blocs MPEG. +\newline + +\begin{lstlisting}[language=c] +int mp3_id3v1_hdr_test(uint32_t hdr); +int mp3_id3v1_tag_seek(FILE * f); +int mp3_id3v1_tag_write(FILE * src, FILE * dst); +\end{lstlisting} + +Ces fonctions concernent les tests internes au format MP3 et font les tests +en rapport aux blocs IDv1. +\newline +\begin{lstlisting}[language=c] +uint32_t mp3_id3v2_size_unsyncsafe(const uint32_t s); +int mp3_id3v2_tag_seek(FILE * f); +\end{lstlisting} + +Ces fonctions concernent les tests internes au format MP3 et font les tests +en rapport aux blocs IDv2. +\newline \newline +Le listing détaillé de ces fonctions est +présent dans les fichiers \textit{mp3.h/.c}. + +\subsection{Ajouts dûs à la résolution d'un bug} + +Lors de la préparation de la démonstration, nous voulions montrer l'aspect +multiplateforme de l'application. +Ainsi, nous avons remarqué que, lorsque l'on fait une insertion sur un +système d'exploitation, l'extraction doit se faire sur le même système +d'exploitation (pas forcément sur la même machine). +Ce bug est dû au fait que la suite pseudo aléatoire générée sur Linux n'est +pas la même que sur Windows. +De ce fait, il a fallu créer nos propres fonctions de randomisation pour que +la suite pseudo-aléatoire soit la même pour n'importe quelle système d'exploitation : + +\begin{lstlisting}[language=c] +void stegx_srand(unsigned int seed); +\end{lstlisting} + +Cette fonction va initialiser la graine de la suite pseudo aléatoire du +système. \textit{seed} correspond au nombre où la graine sera initialisée. +\newline + +\begin{lstlisting}[language=c] +int stegx_rand(); +\end{lstlisting} + +Cette fonction va renvoyer un entier pseudo-aléatoirement. + +\section{Bilan technique du produit pour la version du 1er juin} + +En ce qui concerne les algorithmes proposés par StegX en fonction des +formats pris en charge, voici le bilan de ce que propose l'application +après la version du 1er juin: +\newline + +\begin{tabular}{|l|c|c|c|c|c|} + \hline + \multirow{2}*{\textbf{Format pris en charge par l'application}} & \multicolumn{5}{c|}{\textbf{Algorithmes proposés}} \\ + \cline{2-6} + & \textbf{LSB} & \textbf{EOF} & \textbf{Metadata} + &\textbf{EOC} & \textbf{Junk Chunk} \\ + \hline + \textbf{BMP} (Clément Caumes \& Yassin Doudouh) & \textbf{\checkmark} & \textbf{\checkmark} & \textbf{\checkmark} & & \\ + \hline + \textbf{PNG} (Clément Caumes \& Yassin Doudouh) & & \textbf{\checkmark} & \textbf{\checkmark} & & \\ + \hline + \textbf{WAV} (Pierre Ayoub \& Damien Delaunay) & \textbf{\checkmark} & \textbf{\checkmark} & & & \\ + \hline + \textbf{MP3} (Pierre Ayoub \& Damien Delaunay) & \color{red}{\textbf{\checkmark}} & \color{red}{\textbf{\checkmark}} & & & \\ + \hline + \textbf{AVI} (Claire Baskevitch \& Tristan Bessac) & & & & & \textbf{\checkmark}\\ + \hline + \textbf{FLV} (Claire Baskevitch \& Tristan Bessac) & & \textbf{\checkmark} & & \textbf{\checkmark} & \\ + \hline +\end{tabular} +\vspace{0.5cm} + +On peut remarquer que la ligne pour le format MP3 est remplie. StegX propose +maintenant de la stéganographie sur les formats MP3. + +\end{document} diff --git a/report/6.Oraux/demonstration_diapo.tex b/report/6.Oraux/demonstration_diapo.tex new file mode 100644 index 0000000..0f0256f --- /dev/null +++ b/report/6.Oraux/demonstration_diapo.tex @@ -0,0 +1,91 @@ + \documentclass{beamer} + \usepackage[utf8]{inputenc} + \usetheme{Warsaw} + %\graphicspath{tabularx} + + \usepackage{graphicx} +\usepackage{epsfig} +\usepackage{amssymb} +\usepackage{amsmath} +\usepackage{array} +\usepackage{subfig} +\usepackage{multicol} +\usepackage{caption} +\usepackage{listings} +\usepackage{algorithm} +\usepackage{algorithmic} +\usepackage{array,multirow,makecell} + + \title{Stéganographie \& Stéganalyse : Démonstration} + \author{StegX} + \institute{UFR des Sciences Versailles - L3 Informatique} + \date{1er Juin 2018} + + \begin{document} + + \begin{frame} + \titlepage + \end{frame} + + \section{Que propose StegX ?} + + \begin{frame} + \includegraphics[scale=0.3]{pictures/bilan_0} + \end{frame} + + \subsection{Interfaces} + + \begin{frame} + \includegraphics[scale=0.3]{pictures/bilan_1} + \end{frame} + + \subsection{Multi-plateforme} + + \begin{frame} + \includegraphics[scale=0.3]{pictures/bilan_2} + \end{frame} + + \subsection{3 types dont 6 formats} + + \begin{frame} + \includegraphics[scale=0.3]{pictures/bilan_3} + \end{frame} + + \subsection{5 algorithmes de stéganographie} + + \begin{frame} + \includegraphics[scale=0.3]{pictures/bilan_4} + \end{frame} + + \section{Quels algos pour quels formats ?} + + \begin{frame} + + Le produit répond bien aux objectifs et propose ces algorithmes pour + ces formats : +\newline + +\begin{tabular}{|c|c|c|c|c|c|} + \hline + \multirow{2}*{\textbf{Format}} & \multicolumn{5}{c|}{\textbf{Algorithmes proposés}} \\ + \cline{2-6} + & \textbf{LSB} & \textbf{EOF} & \textbf{Metadata} + &\textbf{EOC} & \textbf{Junk Chunk} \\ + \hline + \textbf{BMP} & \textbf{\checkmark} & \textbf{\checkmark} & \textbf{\checkmark} & & \\ + \hline + \textbf{PNG} & & \textbf{\checkmark} & \textbf{\checkmark} & & \\ + \hline + \textbf{WAV} & \textbf{\checkmark} & \textbf{\checkmark} & & & \\ + \hline + \textbf{MP3} & \textbf{\checkmark} & \textbf{\checkmark} & & & \\ + \hline + \textbf{AVI} & & & & & \textbf{\checkmark}\\ + \hline + \textbf{FLV} & & \textbf{\checkmark} & & \textbf{\checkmark} & \\ + \hline +\end{tabular} + +\end{frame} + + \end{document} diff --git a/report/6.Oraux/demonstration_script.tex b/report/6.Oraux/demonstration_script.tex new file mode 100644 index 0000000..f5744ec --- /dev/null +++ b/report/6.Oraux/demonstration_script.tex @@ -0,0 +1,214 @@ +\documentclass[11pt]{article} +%\documentclass{book} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage[french]{babel} +\usepackage[top=1.8cm, bottom=1.8cm, left=1.8cm, right=1.8cm]{geometry} +\usepackage[linktocpage,colorlinks=false]{hyperref} +\usepackage{graphicx} +\usepackage{epsfig} +\usepackage{amssymb} +\usepackage{amsmath} +\usepackage{array} +\usepackage{subfig} +\usepackage{multicol} +\usepackage{caption} +\usepackage{listings} +\usepackage{algorithm} +\usepackage{algorithmic} +\usepackage{array,multirow,makecell} +\hypersetup{ + colorlinks=true, + breaklinks=true, + urlcolor=red, +} +\parskip=5pt + +\title{\huge{\textbf Démonstration}} +\author{AYOUB Pierre, BASKEVITCH Claire, BESSAC Tristan, \\ +CAUMES Clément, DELAUNAY Damien, DOUDOUH Yassin} +\date{Vendredi 1 Juin 2018} + +\begin{document} + +\maketitle +\vspace{20em} +\newpage + +\tableofcontents + +\newpage + +\section{Introduction et demande du client} + +\subsection{Définitions des termes du sujet} + +La stéganographie est l'art de la dissimulation, appliquée en informatique en +cachant des données dans d'autres données. Cette dissimulation se fait +généralement au sein de fichiers multimédias. La stéganographie se différencie +de la cryptographie, qui correspond à chiffrer un message afin qu'il soit +illisible par une personne différente de l'émetteur et du destinataire. En +effet, un message chiffré en cryptographie sera visible par tous mais illisible, +tandis qu'un message caché dans un fichier $f$ en stéganographie ne sera vu que +si un inconnu sait que $f$ contient un message et connaît l'algorithme pour +l'interpréter. + +La stéganalyse, quant à elle, est la recherche de données cachées dans des +fichiers suspects. Si ces données sont identifiées, il faut ensuite réussir à +les extraire pour les lire. Il s'agit donc de la méthode inverse à la +stéganographie. + +\subsection{Commande du client} + +Le client veut une application qui suit les caractéristiques suivantes : +\begin{itemize} + \item L'application doit permettre de cacher des données dans des fichiers + de type image, audio et vidéo. + Le programme fera l'extraction automatique des données cachées du + fichier à analyser. + \item Plusieurs formats de fichier devront être gérés par l'application, + ainsi qu'une diversité dans les algorithmes proposés. + \item L'application se présentera sous forme d'une bibliothèque partagée par + deux interfaces différentes, une graphique et une en ligne de commande. +\end{itemize} + +Le but du projet était donc de réaliser un logiciel de stéganographie permettant à des +personnes lambdas de communiquer sans que l'on soupçonne que leurs +communications soient en réalité compromettantes. + +\section{Enchainement des différents modules} + +\begin{description} +\item[a)] \textbf{Interfaces} : + interfaces permettant à l'utilisateur de choisir parmi les deux + fonctionnalités possibles de l'application. Il peut dissimuler des + données dans un fichier (dont le type et le format sont pris en charge + par l'application). Ou bien, il peut extraire les données cachées d'un + fichier. \newline + Il aura donc un mécanisme pour choisir le fichier hôte et le fichier + à cacher (pour la dissimulation de données), et un mécanisme pour choisir le + fichier contenant les données cachées à analyser (pour l'extraction de + données cachées), grâce à une interaction avec le gestionnaire d’entrées/sorties. + +\item[b)] \textbf{Vérification de la compatibilité des fichiers} : le format + du fichier hôte (pour le module \textit{Dissimulation de données}) ou + le format du fichier à analyser (pour le module \textit{Extraction de données + cachées}), choisis par l'utilisateur, sont vérifiés pour savoir s'ils sont + bien pris en charge par l'application. \newline + Il aura un mécanisme de lecture du fichier hôte : selon les formats + pris en charge, il y aura une série de tests successifs pour déterminer le + format du fichier. + +\item[c)] \textbf{Proposition des algorithmes de stéganographie} : en fonction + du type et du format du fichier hôte, ainsi que de la taille des données à + cacher, un mécanisme proposera un ou plusieurs algorithmes (avec ou sans + sécurité supplémentaire). + +\item[d)] \textbf{Détection de l'algorithme de stéganographie} : un mécanisme + d'analyse du fichier permettra de découvrir quel algorithme a été utilisé + afin de les extraire correctement par la suite. + +\item[e)] \textbf{Insertion des données} : la copie des données du fichier hôte + sera modifiée avec l'insertion des données à cacher à l'aide de l'algorithme + choisi par l'utilisateur. + +\item[f)] \textbf{Extraction} : les données cachées dans le fichier à analyser + sont extraites selon l'algorithme déduit. + +\end{description} + +\section{Algorithmes de stéganographie} + +\subsection{Least Significant Bit} + +Pour l'insertion des données, l'algorithme LSB va modifier les bits de +poids faible des différents octets des données de l'hôte. Selon la taille des +données à cacher et la taille du fichier hôte, les données cachées pourront +être dispersées dans les octets de l'hôte. Sinon, on exercera un XOR avec +la suite pseudo-aléatoire générée à partir du mot de passe. +Ensuite, quand toutes les données de l'hôte modifiées par LSB ont été écrites +dans le fichier résultat, la signature StegX est écrite. + +Pour l'extraction des données, la lecture de la fin du fichier permettra de +lire la signature StegX. Ensuite, les octets de l'hôte seront lues afin d'extraire +les données cachées. + +Il sera proposé pour les formats BMP (non compressé), WAV (PCM) et MP3. + +\subsection{End Of File} + +Pour l'insertion des données, cet algorithme consiste à recopier le fichier +hôte dans le fichier résultat, suivi de la signature, suivi des octets +représentant le fichier à cacher. Cet algorithme fonctionne car les éditeurs +des formats pour lesquels l'algorithme est proposé n'interprètent pas les données +après la fin << officielle >> du fichier. + +Pour l'extraction des données, il faut aller à la fin officielle du fichier +à analyser. A cet endroit précis est écrit la signature StegX. Ainsi, +les informations sur le fichier caché sont interprétées. Puis, il y a une +lecture après cette signature, correspondant aux données cachées. + +Il sera proposé pour les formats BMP, PNG, MP3, WAV et FLV. + +\subsection{Metadata} + +Pour chaque format pris en charge par StegX qui propose cet algorithme, +ce dernier diffère. En effet, l'étude approfondie du format du fichier est +nécessaire pour connaître les endroits du fichier où cacher des métadonnées. +Cela sera dans ces espaces que les données seront cachées. + +Pour l'insertion, il est nécessaire que la signature StegX soit écrite à +la fin du fichier afin que l'extraction puisse se dérouler correctement. + +Il sera proposé pour les formats BMP et PNG. + +\subsection{End Of Chunk} + +Cet algorithme est uniquement proposé par le format FLV et consiste à +écrire après les différents chunks du fichier hôte. En effet, lors de +son interprétation, les chunks non reconnus sont ignorés et ce sont à ces +endroits que les données seront cachées. +Les données cachées sont situées après chaque chunk vidéo et sont interprétés +mais ne modifient pas l'image de la vidéo. + +\subsection{Junk Chunk} + +L'algorithme Junk Chunk est seulement proposé par le format AVI. Il correspond +à la création d'un chunk poubelle. Ce chunk est spécifique au format car il +représente un chunk dans lequel les données ne seront pas interprétées par +les logiciels multimédia. + +\section{Conclusion} + +Le produit répond bien aux objectifs et propose ces formats avec ces +algorithmes : +\newline + +\begin{tabular}{|c|c|c|c|c|c|} + \hline + \multirow{2}*{\textbf{Format}} & \multicolumn{5}{c|}{\textbf{Algorithmes proposés}} \\ + \cline{2-6} + & \textbf{LSB} & \textbf{EOF} & \textbf{Metadata} + &\textbf{EOC} & \textbf{Junk Chunk} \\ + \hline + \textbf{BMP} & \textbf{\checkmark} & \textbf{\checkmark} & \textbf{\checkmark} & & \\ + \hline + \textbf{PNG} & & \textbf{\checkmark} & \textbf{\checkmark} & & \\ + \hline + \textbf{WAV} & \textbf{\checkmark} & \textbf{\checkmark} & & & \\ + \hline + \textbf{MP3} & \textbf{\checkmark} & \textbf{\checkmark} & & & \\ + \hline + \textbf{AVI} & & & & & \textbf{\checkmark}\\ + \hline + \textbf{FLV} & & \textbf{\checkmark} & & \textbf{\checkmark} & \\ + \hline +\end{tabular} +\vspace{0.5cm} + +Un des avantages de notre logiciel est le fait qu'il peut être constamment améliorer, +du fait que nous avons fait en sortie d'avoir une conception flexible et +modulaire permettant de rajouter facilement de nouveaux formats pris en charges, +ou bien de nouveau algorithmes. + +\end{document} diff --git a/report/6.Oraux/pictures/Application.png b/report/6.Oraux/pictures/Application.png new file mode 100644 index 0000000..46a134a Binary files /dev/null and b/report/6.Oraux/pictures/Application.png differ diff --git a/report/6.Oraux/pictures/alice_bob.png b/report/6.Oraux/pictures/alice_bob.png new file mode 100644 index 0000000..97ec982 Binary files /dev/null and b/report/6.Oraux/pictures/alice_bob.png differ diff --git a/report/6.Oraux/pictures/avi-512.png b/report/6.Oraux/pictures/avi-512.png new file mode 100644 index 0000000..51c3750 Binary files /dev/null and b/report/6.Oraux/pictures/avi-512.png differ diff --git a/report/6.Oraux/pictures/bilan_0.png b/report/6.Oraux/pictures/bilan_0.png new file mode 100644 index 0000000..778e30a Binary files /dev/null and b/report/6.Oraux/pictures/bilan_0.png differ diff --git a/report/6.Oraux/pictures/bilan_1.png b/report/6.Oraux/pictures/bilan_1.png new file mode 100644 index 0000000..80ce53d Binary files /dev/null and b/report/6.Oraux/pictures/bilan_1.png differ diff --git a/report/6.Oraux/pictures/bilan_2.png b/report/6.Oraux/pictures/bilan_2.png new file mode 100644 index 0000000..cbe2d45 Binary files /dev/null and b/report/6.Oraux/pictures/bilan_2.png differ diff --git a/report/6.Oraux/pictures/bilan_3.png b/report/6.Oraux/pictures/bilan_3.png new file mode 100644 index 0000000..a96d42d Binary files /dev/null and b/report/6.Oraux/pictures/bilan_3.png differ diff --git a/report/6.Oraux/pictures/bilan_4.png b/report/6.Oraux/pictures/bilan_4.png new file mode 100644 index 0000000..c8d7d62 Binary files /dev/null and b/report/6.Oraux/pictures/bilan_4.png differ diff --git a/report/6.Oraux/pictures/bmp-512.png b/report/6.Oraux/pictures/bmp-512.png new file mode 100644 index 0000000..5ffe519 Binary files /dev/null and b/report/6.Oraux/pictures/bmp-512.png differ diff --git a/report/6.Oraux/pictures/definition1.png b/report/6.Oraux/pictures/definition1.png new file mode 100644 index 0000000..9f1904b Binary files /dev/null and b/report/6.Oraux/pictures/definition1.png differ diff --git a/report/6.Oraux/pictures/definition2.png b/report/6.Oraux/pictures/definition2.png new file mode 100644 index 0000000..ee6cc85 Binary files /dev/null and b/report/6.Oraux/pictures/definition2.png differ diff --git a/report/6.Oraux/pictures/eof.png b/report/6.Oraux/pictures/eof.png new file mode 100644 index 0000000..acc99b6 Binary files /dev/null and b/report/6.Oraux/pictures/eof.png differ diff --git a/report/6.Oraux/pictures/flv_512.png b/report/6.Oraux/pictures/flv_512.png new file mode 100644 index 0000000..776975b Binary files /dev/null and b/report/6.Oraux/pictures/flv_512.png differ diff --git a/report/6.Oraux/pictures/lsb.png b/report/6.Oraux/pictures/lsb.png new file mode 100644 index 0000000..8f8d9e6 Binary files /dev/null and b/report/6.Oraux/pictures/lsb.png differ diff --git a/report/6.Oraux/pictures/meta.png b/report/6.Oraux/pictures/meta.png new file mode 100644 index 0000000..48f3c07 Binary files /dev/null and b/report/6.Oraux/pictures/meta.png differ diff --git a/report/6.Oraux/pictures/mp3-512.png b/report/6.Oraux/pictures/mp3-512.png new file mode 100644 index 0000000..85e50db Binary files /dev/null and b/report/6.Oraux/pictures/mp3-512.png differ diff --git a/report/6.Oraux/pictures/organigramme_extraction.png b/report/6.Oraux/pictures/organigramme_extraction.png new file mode 100644 index 0000000..a38985b Binary files /dev/null and b/report/6.Oraux/pictures/organigramme_extraction.png differ diff --git a/report/6.Oraux/pictures/organigramme_insertion.png b/report/6.Oraux/pictures/organigramme_insertion.png new file mode 100644 index 0000000..c80d8a8 Binary files /dev/null and b/report/6.Oraux/pictures/organigramme_insertion.png differ diff --git a/report/6.Oraux/pictures/organigramme_vide.png b/report/6.Oraux/pictures/organigramme_vide.png new file mode 100644 index 0000000..038403e Binary files /dev/null and b/report/6.Oraux/pictures/organigramme_vide.png differ diff --git a/report/6.Oraux/pictures/png_512.png b/report/6.Oraux/pictures/png_512.png new file mode 100644 index 0000000..8919b6b Binary files /dev/null and b/report/6.Oraux/pictures/png_512.png differ diff --git a/report/6.Oraux/pictures/signature_6.png b/report/6.Oraux/pictures/signature_6.png new file mode 100644 index 0000000..f5af41a Binary files /dev/null and b/report/6.Oraux/pictures/signature_6.png differ diff --git a/report/6.Oraux/pictures/wav-512.png b/report/6.Oraux/pictures/wav-512.png new file mode 100644 index 0000000..6a90e10 Binary files /dev/null and b/report/6.Oraux/pictures/wav-512.png differ diff --git a/report/6.Oraux/soutenance_diapo.tex b/report/6.Oraux/soutenance_diapo.tex new file mode 100644 index 0000000..439d2ef --- /dev/null +++ b/report/6.Oraux/soutenance_diapo.tex @@ -0,0 +1,422 @@ + \documentclass{beamer} + \usepackage[utf8]{inputenc} + \usetheme{Warsaw} + \graphicspath{images/} + \usepackage{amsmath} + \usepackage{amssymb} + + \title{Stéganographie \& Stéganalyse : Soutenance} + \author{StegX} + \institute{UFR des Sciences Versailles - L3 Informatique} + \date{1er Juin 2018} + + \begin{document} + + \begin{frame} + \titlepage + \end{frame} + + \section{Introduction et définitions} + + \subsection{Commande du client} + + \begin{frame} + \begin{block}{Demande du client} + + Réaliser un logiciel de stéganographie permettant à des + personnes lambdas de communiquer sans que l'on soupçonne que leurs + communications soient en réalité compromettantes. + + \begin{itemize} + \setbeamertemplate{itemize item}[circle] + \item Cacher des données dans des fichiers + de type image, audio et vidéo. + \item Faire l'extraction automatique des données cachées du + fichier à analyser. + \item Gestion de plusieurs formats et diversité dans les algorithmes proposés. + \item Proposition d'une bibliothèque partagée par + deux interfaces différentes, une graphique et une en ligne de commande. + \end{itemize} + \end{block} + + \end{frame} + + + \subsection{Définitions} + + \begin{frame} + \hspace{5cm} + \includegraphics[scale=0.7]{pictures/definition1.png} + \end{frame} + + \begin{frame} + \hspace{5cm} + \includegraphics[scale=0.7]{pictures/definition2.png} + \end{frame} + + + \section{Fonctionnement global} + + \begin{frame} + \hspace{1.5cm} + \includegraphics[scale=0.35]{pictures/organigramme_vide.png} + \end{frame} + + \begin{frame} + \hspace{1.5cm} + \includegraphics[scale=0.35]{pictures/organigramme_insertion.png} + \end{frame} + + \begin{frame} + \hspace{1.5cm} + \includegraphics[scale=0.25]{pictures/organigramme_extraction.png} + \end{frame} + + \begin{frame} %1 + \hspace{1.7cm} + \includegraphics[scale=0.5]{pictures/alice_bob.png} + + \begin{exampleblock}{Exemple} + Alice choisit de cacher \textcolor{red}{message.txt} dans un fichier + BMP \textcolor{red}{photo.bmp} et veut obtenir le fichier + \textcolor{red}{piece\_jointe.bmp} après la + dissimulation. Enfin, elle choisit le mot de passe \textcolor{red}{alicebob}. + \end{exampleblock} + \end{frame} + + \section{Algorithmique \& Stéganographie} + \begin{frame} %3 + \begin{block}{Algorithmes de stéganographie} + \begin{itemize} + \setbeamertemplate{itemize item}[circle] + \item Least Significant Bit (LSB) + \item End Of File (EOF) + \item Metadata + \item End Of Chunk (EOC) + \item Junk Chunk + \end{itemize} + \end{block} + + \begin{alertblock}{Besoin} + Nécessité d'obtenir les informations de l'insertion pendant l'extraction + impose une "signature". + \end{alertblock} + + \end{frame} + + \subsection{Signature StegX} + + \begin{frame} + + \begin{block}{Signature StegX} + \begin{itemize} + \setbeamertemplate{itemize item}[circle] + \item Identificateur de la méthode (1 octet). + \item Identificateur de l'algorithme (1 octet). + \item Taille du fichier caché (4 octets). + \item Taille du nom du fichier caché (1 octet). + \item Nom du fichier caché (entre 1 et 255 octets). + \item Mot de passe (64 octets). + \end{itemize} + \end{block} + + \includegraphics[scale=0.55]{pictures/signature_6.png} + \end{frame} + + \subsection{Description des algorithmes de stéganographie} %OK + + % LSB OK + \subsubsection{Least Significant Bit (LSB)} + \begin{frame} + + \begin{block}{Least Significant Bit} + \begin{itemize} + \setbeamertemplate{itemize item}[circle] + \item Modification des bits de poids faible des octets de données de + l'hôte. + \end{itemize} + \end{block} + + \begin{exampleblock}{Avantage} + L'utilisation de cet algorithme n'augmente pas la taille du fichier + résultat en fonction de la taille du fichier caché. + \end{exampleblock} + + \begin{alertblock}{Inconvénient} + Le fichier à cacher doit être assez petit pour pouvoir le cacher intégralement + dans le fichier hôte. + \end{alertblock} + + \hspace{2.5cm} + \includegraphics[scale=0.1]{pictures/lsb.png} + \end{frame} + + % EOF OK + \subsubsection{End Of File (EOF)} + \begin{frame} + + \begin{block}{End Of File} + \begin{itemize} + \setbeamertemplate{itemize item}[circle] + \item Écriture des données à cacher après la fin officielle du fichier + hôte. + \end{itemize} + \end{block} + + \begin{exampleblock}{Avantage} + Il n'y a pas de limite de taille pour cacher le fichier. + \end{exampleblock} + + \begin{alertblock}{Inconvénient} + L'utilisation de cet algorithme augmente considérablement la taille du + fichier résultat par rapport au fichier hôte. + \end{alertblock} + + \hspace{1cm} + \includegraphics[scale=0.2]{pictures/eof.png} + + \end{frame} + + % METADATA OK + \subsubsection{Metadata} + \begin{frame} + + \begin{block}{Metadata} + \begin{itemize} + \setbeamertemplate{itemize item}[circle] + \item Écriture des données à cacher dans des blocs de données spécifiques + qui ne modifieront pas les données originales. + \end{itemize} + \end{block} + + \begin{exampleblock}{Avantage} + Il n'y a pas de limite de taille pour cacher le fichier. + \end{exampleblock} + + \begin{alertblock}{Inconvénient} + L'utilisation de cet algorithme augmente considérablement la taille du + fichier résultat par rapport au fichier hôte. + \end{alertblock} + + \hspace{3.5cm} + \includegraphics[scale=0.2]{pictures/meta.png} + + \end{frame} + + %EOC OK + \subsubsection{End Of Chunk (EOC)} + \begin{frame} + + \begin{block}{End Of Chunk} + \begin{itemize} + \setbeamertemplate{itemize item}[circle] + \item Écriture des données à cacher après les différents chunks interprétables + du fichier hôte. Ces données seront non reconnus et donc ignorés. + \end{itemize} + \end{block} + + \begin{exampleblock}{Avantage} + Il n'y a pas de limite de taille pour cacher le fichier. + \end{exampleblock} + + \begin{alertblock}{Inconvénient} + L'utilisation de cet algorithme augmente considérablement la taille du + fichier résultat par rapport au fichier hôte. + \end{alertblock} + + \hspace{4.3cm} + \includegraphics[scale=0.08]{pictures/flv_512.png} + + \end{frame} + + %Junk Chunk OK + \subsubsection{Junk Chunk} + \begin{frame} + + \begin{block}{Junk Chunk} + \begin{itemize} + \setbeamertemplate{itemize item}[circle] + \item Écriture des données à cacher dans un chunk appelé "junk" : les + données ne seront pas interprétées + \end{itemize} + \end{block} + + \begin{exampleblock}{Avantage} + Il n'y a pas de limite de taille pour cacher le fichier. + \end{exampleblock} + + \begin{alertblock}{Inconvénient} + L'utilisation de cet algorithme augmente considérablement la taille du + fichier résultat par rapport au fichier hôte. + \end{alertblock} + + \hspace{4.4cm} + \includegraphics[scale=0.08]{pictures/avi-512.png} + + \end{frame} + + \section{Fonctionnement modulaire} + + \subsection{Vérification de la compatibilité des fichiers} + \begin{frame} %1 + \begin{block}{Description des modules} + \begin{itemize} + \setbeamertemplate{itemize item}[circle] + \item \textcolor{red}{Vérification de la compatibilité des fichiers.} + \item \textcolor{gray} {Proposition des algorithmes de stéganographie.} + \item \textcolor{gray} {Insertion des données.} + \item \textcolor{gray} {Détection de l'algorithme de stéganographie.} + \item \textcolor{gray} {Extraction des données.} + \end{itemize} + \end{block} + + \begin{exampleblock}{Exemple} + Avec la vérification de la compatibilité des fichiers, il s'agit d'un + format \textcolor{red}{BMP non compressé}. + \end{exampleblock} + \end{frame} + + \subsection{Proposition des algorithmes de stéganographie} + \begin{frame} %2 + \begin{block}{Description des modules} + \begin{itemize} + \setbeamertemplate{itemize item}[circle] + \item Vérification de la compatibilité des fichiers. + \item \textcolor{red}{Proposition des algorithmes de stéganographie.} + \item \textcolor{gray} {Insertion des données.} + \item \textcolor{gray} {Détection de l'algorithme de stéganographie.} + \item \textcolor{gray} {Extraction des données.} + \end{itemize} + \end{block} + + \begin{exampleblock}{Exemple} + \begin{itemize} + \setbeamertemplate{itemize item}[circle] + \item Grâce au module Proposition des algorithmes de stéganographie, les + spécificités du format de piece\_jointe.bmp ont été déduites. + \item Les algorithmes \textcolor{red}{EOF}, \textcolor{red}{LSB} et + \textcolor{red}{Metadata} sont proposés. + \item Alice choisit l'algorithme \textcolor{red}{EOF}. + \end{itemize} + + \end{exampleblock} + \end{frame} + + \subsection{Insertion des données} + \begin{frame} %3 + \begin{block}{Description des modules} + \begin{itemize} + \setbeamertemplate{itemize item}[circle] + \item Vérification de la compatibilité des fichiers. + \item Proposition des algorithmes de stéganographie. + \item \textcolor{red} {Insertion des données.} + \item \textcolor{gray} {Détection de l'algorithme de stéganographie.} + \item \textcolor{gray} {Extraction des données.} + \end{itemize} + \end{block} + + \begin{exampleblock}{Exemple} + Après que Alice ait fini la dissimulation, Bob va déduire les spécificités + du fichier piece\_jointe.bmp et déduire les informations sur le fichier + caché message.txt. + \end{exampleblock} + \end{frame} + + \subsection{Détection de l'algorithme de stéganographie} + \begin{frame} %4 + \begin{block}{Description des modules} + \begin{itemize} + \setbeamertemplate{itemize item}[circle] + \item Vérification de la compatibilité des fichiers. + \item Proposition des algorithmes de stéganographie. + \item Insertion des données. + \item \textcolor{red} {Détection de l'algorithme de stéganographie.} + \item \textcolor{gray} {Extraction des données.} + \end{itemize} + \end{block} + + \begin{exampleblock}{Exemple} + Lors de l'insertion de Alice, l'algorithme EOF sera utilisé où + les données de l'hôte, la signature StegX suivies des données du fichier + à cacher seront écrites dans piece\_jointe.bmp. + \end{exampleblock} + \end{frame} + + \subsection{Extraction des données} + \begin{frame} %5 + \begin{block}{Description des modules} + \begin{itemize} + \setbeamertemplate{itemize item}[circle] + \item Vérification de la compatibilité des fichiers. + \item Proposition des algorithmes de stéganographie. + \item Insertion des données. + \item Détection de l'algorithme de stéganographie. + \item \textcolor{red}{Extraction des données.} + \end{itemize} + \end{block} + + \begin{exampleblock}{Exemple} + L'algorithme EOF sera utilisé pour extraire les données du fichier + caché de message.txt. + \end{exampleblock} + \end{frame} + + + \section{Problèmes et points délicats rencontrés} + + \begin{frame} + + \begin{block}{Lecture et écriture de fichiers : endianness en fonction des formats} + \begin{itemize} + \setbeamertemplate{itemize item}[circle] + + \item Little endian et big endian selon les formats. + \item Travail de recherche poussé pour chaque format et pour chaque + algorithme de stéganographie. + \end{itemize} + \end{block} + + \begin{block}{Format MP3} + \begin{itemize} + \setbeamertemplate{itemize item}[circle] + \item Format compressé utilisant des algorithmes de compression complexes. + \item Étude des versions des formats (MPEG 1 Layer III, MPEG 2 Layer III). + \item Étude des versions de formats de métadonnée (ID3 version 1, ID3 version 2). + \end{itemize} + \end{block} + + \end{frame} + + + + \section{Bilan logiciel et humain} + + \begin{frame} + + \begin{block}{Bilan logiciel} + \begin{itemize} + \setbeamertemplate{itemize item}[circle] + \item Faire de la stéganographie sur des fichiers image, audio et vidéo + (insertion et extraction). \checkmark + \item Plusieurs formats sont gérés et une diversité dans les algorithmes + sont proposés. \checkmark + \item Réaliser deux interfaces : ligne de commande et graphique. \checkmark +\setbeamertemplate{itemize item}[triangle] + \item Futures améliorations : nouveaux formats et nouveaux algorithmes. + +\end{itemize} + + \end{block} + + \begin{block}{Bilan humain} + \begin{itemize} + \setbeamertemplate{itemize item}[circle] + \item L'équipe s'est efforcée à travailler de façon professionnelle et ordonnée. + \item Projet de grande envergure qui a nécessité de diviser la conception + selon les trois types de formats étudiés. + \end{itemize} + \end{block} + + \end{frame} + + \end{document} diff --git a/report/6.Oraux/soutenance_script.tex b/report/6.Oraux/soutenance_script.tex new file mode 100644 index 0000000..51d97cd --- /dev/null +++ b/report/6.Oraux/soutenance_script.tex @@ -0,0 +1,405 @@ +\documentclass[11pt]{article} +%\documentclass{book} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage[french]{babel} +\usepackage[top=1.8cm, bottom=1.8cm, left=1.8cm, right=1.8cm]{geometry} +\usepackage[linktocpage,colorlinks=false]{hyperref} +\usepackage{graphicx} +\usepackage{epsfig} +\usepackage{amssymb} +\usepackage{amsmath} +\usepackage{array} +\usepackage{subfig} +\usepackage{multicol} +\usepackage{caption} +\usepackage{algorithm} +\usepackage{algorithmic} +\hypersetup{ + colorlinks=true, + breaklinks=true, + urlcolor=red, +} +\parskip=5pt + +\title{\huge{\textbf Soutenance orale}} +\author{AYOUB Pierre, BASKEVITCH Claire, BESSAC Tristan, \\ +CAUMES Clément, DELAUNAY Damien, DOUDOUH Yassin} +\date{Vendredi 1 Juin 2018} + +\begin{document} + +\maketitle +\vspace{20em} +\newpage + +\tableofcontents + +\newpage + +\section{Introduction} + +\subsection{Commande du client} + +Pour le projet S6 de Licence informatique, un client nous a demandé de +réaliser un logiciel de stéganographie permettant à des +personnes lambdas de communiquer sans que l'on soupçonne que leurs +communications soient en réalité compromettantes. +Il voulait aussi que l'application propose plusieurs algorithmes, sur +plusieurs formats différents. + + +\subsection{Définitions des termes du sujet} + +La stéganographie est l'art de la dissimulation, appliquée en informatique en +cachant des données dans d'autres données. Cette dissimulation se fait +généralement au sein de fichiers multimédias. La stéganographie se différencie +de la cryptographie, qui correspond à chiffrer un message afin qu'il soit +illisible par une personne différente de l'émetteur et du destinataire. + +La stéganalyse, quant à elle, est la recherche de données cachées dans des +fichiers suspects et l'extraction. Il s'agit donc de la méthode inverse à la +stéganographie. + +Le but du projet était de réaliser un logiciel de stéganographie permettant à des +personnes lambdas de communiquer sans que l'on soupçonne que leurs +communications soient en réalité compromettantes. + +\section{Description générale des modules} + +L'étude des différentes propriétés de la stéganographie et de la stéganalyse +nous a mené vers cet organigramme. +Il permet de visualiser les étapes successives de l'insertion des données ainsi +que celle de la l'extraction des données. + +Pour la dissimulation de données, il va d'abord y avoir la \textit{Vérification +de la compatibilité} (b) du fichier hôte. +Il y a ensuite le module \textit{Proposition des algorithmes de stéganographie} +(c) qui va être utilisé. +La dernière étape consiste en la réelle insertion des données, où les +données à cacher seront dissimulées dans une copie du fichier hôte (e). + +Pour l'extraction de données, le module \textit{Vérification de la compatibilité +des fichiers} aura la même fonctionnalité que pour l'insertion. +Il y aura ensuite une \textit{Détection de l'algorithme de stéganographie} +(d). +Enfin, l'\textit{Extraction} (f) permettra de récupérer les données cachées +dans le fichier à analyser. + +Tous ces modules et sous-modules seront coordonnées grâce aux interfaces +graphique et en ligne de commande (a), proposées par StegX. + +\section{Présentation du fonctionnement} + +Nous allons maintenant décrire le fonctionnement interne de notre +application en illustrant par un exemple de dissimulation et d'extraction +de données, correspondant à une communication entre Alice et Bob, surveillée +par Oscar. Les données sont issues d'une réelle utilisation de StegX. + +Alice choisit de dissimuler le message dans une image qu'elle veut envoyer +à Bob à l'aide de l'interface graphique. +Dans cet exemple : +\begin{itemize} +\item le fichier à cacher est message.txt. +\item le fichier hôte est photo.bmp. +\item le chemin du fichier à créer est piece\_jointe.bmp +\item le mot de passe est "alicebob" (communiqué à Bob sur un canal sûr). +\end{itemize} + +\section{Algorithmique \& Stéganographie} + +\subsection{Présentation} + +L'application de stéganographie va proposer à l'utilisateur une suite +d'algorithmes (selon le format du fichier hôte) : +LSB, EOF, Metadata, EOC, Junk Chunk. + +De plus, pour chaque algorithme, on utilise 2 méthodes différentes selon +la taille du fichier hôte et/ou du fichier à cacher. + +Le grand nombre d'algorithmes ainsi que la nécessité d'obtenir certaines +informations pour l'extraction impose la création d'une signature. + +\subsection{Description de la signature StegX} + +La signature de StegX est primordiale dans l'insertion puisqu'elle permet au +destinataire de pouvoir interpréter ce que l'émetteur a voulu lui communiquer. +Pour cela, cette signature doit avoir plusieurs champs : +\begin {enumerate} +\item Identificateur de la méthode (1 octet). Cet octet ne +peut prendre que deux valeurs possibles : la méthode avec ou sans mot de +passe. Si l'émetteur choisit un mot de passe, StegX récupèrera ce mot de +passe pour réaliser l'insertion de façon sécurisée. Le destinataire devra +choisir le même mot de passe pour extraire les données correctement. +Si par contre, l'émetteur ne choisit pas de mot de passe, StegX va en choisir +un au hasard et faire les mêmes manipulations qu'avec un mot de passe. +Le mot de passe par défaut devra donc être écrit dans la signature afin que +le destinataire puisse extraire. +\item Identificateur de l'algorithme (1 octet) : cette variable représente +l'algorithme utilisé par l'émetteur pour que le récepteur extrait correctement +les données. +\item Taille du fichier caché (4 octets) : ce nombre représente en octets +la taille du fichier caché dans le fichier hôte. Il a une taille maximale +de $2^{32}$ octets, soit 4 GB. Ce nombre est écrit en little endian. +\item Taille du nom du fichier caché (1 octet) : cet octet représente le +nombre de caractères du nom du fichier caché sans compter le caractère de +fin '$\backslash$0'. +\item Nom du fichier caché (entre 1 et 255 octets) : représente le nom du +fichier caché. Ce nom va être XOR avec le mot de passe (par défaut ou choisi +par l'utilisateur) : ainsi, si le récepteur choisit un mot de passe différent, +il ne pourra même pas connaître le nom du fichier caché. +\item Mot de passe (64 octets) : représente le mot de passe par défaut choisi +aléatoirement par l'application. Si la méthode est avec mot de passe, +ce champ n'existe pas car c'est le destinataire qui doit le connaître et +non l'application qui doit l'extraire. +\end{enumerate} + +\subsection{Least Significant Bit} + +Pour l'insertion des données, l'algorithme LSB va modifier les bits de +poids faible des différents octets des données de l'hôte. Selon la taille des +données à cacher et la taille du fichier hôte, les données cachées pourront +être dispersées dans les octets de l'hôte. Sinon, on exercera un XOR avec +la suite pseudo-aléatoire générée à partir du mot de passe. + +Il sera proposé pour les formats BMP (non compressé) et WAV (PCM). + +L'avantage de cet algorithme est le fait que cela n'augmente pas la +taille du fichier hôte (à part pour l'ajout de la signature). +L'inconvénient est que le fait que le fichier a caché doit être assez petit +pour pouvoir le cacher intégralement dans l'hôte. De plus, la modification +des bits de poids faible peut être vu (BMP) ou entendu (WAV) par l'homme. + +\subsection{End Of File} + +Pour l'insertion des données, cet algorithme consiste à recopier le fichier +hôte dans le fichier résultat, suivi des octets représentant le fichier +à cacher. Cet algorithme fonctionne car les éditeurs +des formats pour lesquels l'algorithme est proposé n'interprètent pas les +données après la fin << officielle >> du fichier. + +Pour l'extraction des données, il faut aller à la fin officielle du fichier +à analyser pour lire les données cachées. + +Il sera proposé pour les formats BMP, PNG, MP3, WAV et FLV. + +L'avantage de cet algorithme est le fait qu'il n'y a pas de limite de +taille pour le fichier à cacher. L'inconvénient est le fait que la taille +du fichier résultat sera égale à la taille de l'hôte, plus la taille de la +signature, plus la taille du fichier à cacher. + +\subsection{Metadata} + +Pour chaque format pris en charge par StegX qui propose cet algorithme, +ce dernier diffère. En effet, l'étude approfondie du format du fichier est +nécessaire pour connaître les endroits du fichier où cacher des métadonnées. +Cela sera dans ces espaces que les données seront cachées. + +Pour l'insertion, il est nécessaire que la signature StegX soit écrite à +la fin du fichier afin que l'extraction puisse se dérouler correctement. + +L'avantage de cet algorithme est le fait qu'il n'y a pas de limite de +taille pour le fichier à cacher. L'inconvénient est le fait qu'il augmente +(comme pour EOF) la taille du fichier hôte. + +Il sera proposé pour les formats BMP et PNG. + +\subsection{End Of Chunk} + +Cet algorithme est uniquement proposé par le format FLV et consiste à +écrire après les différents chunks du fichier hôte. En effet, lors de +son interprétation, cela ne modifiera pas la vidéo d'origine. + +L'avantage de cet algorithme est le fait qu'il n'y a pas de limite de +taille pour le fichier à cacher. Par contre, l'inconvénient de cet algorithme +est le fait que cela peut créer des distorsions sur le fichier vidéo où +l'interprétation pour certains logiciels multimédia peut être fait sur ces +données cachées. + +\subsection{Junk Chunk} + +L'algorithme Junk Chunk est seulement proposé par le format AVI. Il correspond +à la création d'un chunk poubelle. Ce chunk est spécifique au format car il +représente un chunk dans lequel les données ne seront pas interprétées par +les logiciels multimédia. + +L'avantage de cet algorithme est le fait qu'il n'y a pas de limite de +taille pour le fichier à cacher. L'inconvénient est le fait qu'il augmente +(comme pour EOF) la taille du fichier hôte. + +\section{Description des modules} + +\subsection{Vérification de la compatibilité des fichiers} + +Ce module est commun à l'insertion et l'extraction. Il correspond à la +recherche détaillée de la nature du fichier hôte. +Pour cela, il va y avoir une série de tests durant lesquels les +signatures des différents formats proposés par l'application vont être recherchées. +Si l'une d'elle est reconnue, cela signifie qu'il s'agit du format en question. +A la fin de la série de tests, si le format n'a pas été reconnu, +l'application renvoie une erreur : en effet, elle ne peut pas utiliser ce +fichier là pour dissimuler ou extraire des données. + +Dans notre exemple, il s'agit d'un fichier BMP non compressé dont les +pixels sont codés sur 24 bits (8 bits par composantes couleurs). + +\subsection{Proposition des algorithmes de stéganographie} + +\begin{itemize} +\item Dans ce module, l'application va lire en détails les particularités du fichier +hôte. En effet, dans le module précédent, nous avons trouvé le format. Il +faut maintenant lire le header du fichier pour l'interpréter correctement +par la suite. Lorsque les données spécifiques du fichier ont été trouvées, +une série de tests sur les différents algorithmes proposés par l'application +permettra de déterminer quels algorithmes sont possibles avec la nature +du fichier hôte et la taille du fichier à cacher. +\newline +La structure spécifique \textit{infos.host.file\_info.bmp} sera remplie, +notamment avec la taille du header, la taille du bloc data, la longueur +des pixels et le nombre de pixels dans l'image. De cette structure +est déduit les algorithmes proposés (EOF, LSB et Metadata). +\newline +\item Après cette série de tests, la récupération du choix de l'utilisateur +permettra de déterminer son choix d'algorithme à utiliser lors de l'insertion. +\newline +Dans cet exemple, Alice choisit l'algorithme EOF. +\end{itemize} + +\subsection{Insertion des données} + +Lorsque le fichier à cacher, l'algorithme de stéganographie à utiliser, +la nature du fichier hôte, le mot de passe (s'il y en a un) et l'emplacement +du fichier à créer sont connus, il est temps de réaliser la dissimulation. +Chaque algorithme diffère plus ou moins pour chaque format. +L'étape commune est le fait que si le fichier à cacher est trop grand, +l'utilisation du mot de passe permettra de générer (à partir d'une graine), +une suite de nombres pseudo-aléatoires afin de faire un XOR avec chaque +octet du fichier à cacher. Si la taille le permet, les octets à cacher +seront mélangés (grâce au mot de passe) afin de rendre impossible l'extraction +par une personne inconnue. + +Dans notre exemple, suivant l'algorithme EOF, il va y avoir une écriture +dans piece\_jointe.bmp du fichier hôte. +Ensuite, la signature StegX est écrite. +Enfin, les données cachées sont également écrites à l'aide du mot de passe +grâce à l'algorithme de protection des données. + +Alice pourra donc envoyer le fichier piece\_jointe.bmp sur un canal non +sécurisé. + +Oscar, qui espionne les communications entre Bob et Alice, n'aura aucun +soupçon en voyant piece\_jointe.bmp qui semble être une image comme une +autre. + +\subsection{Détection de l'algorithme de stéganographie} + +Ce module correspond à la lecture de la signature StegX que nous expliquerons +par la suite. En fonction du format +du fichier à analyser, la position de la signature de StegX dans le fichier ne +sera pas la même. Cette lecture sera déterminante pour découvrir l'algorithme +utilisé, le nom du fichier caché et la taille de ce dernier. + +Lors de la détection de l'algorithme, Bob déduira les mêmes champs de +\textit{infos.host.file\_info.bmp} que Alice lors de l'insertion. En effet, +il va y avoir une lecture approfondie du fichier. Ensuite, la signature +StegX, qui a été écrite lors de l'insertion, sera lue. + +\subsection{Extraction des données} + +De la même manière que l'insertion, +après une vérification de la compatibilité et une détection de l'algorithme +de stéganographie, le nom du fichier caché, la taille du fichier caché, +l'algorithme, le mot de passe (s'il y en a un) et l'emplacement du fichier +prochainement extrait sont connus, l'extraction peut commencer. +Le mot de passe permettra de retrouver la même suite de nombres pseudo-aléatoires +pour XOR avec les octets extraits (si le fichier caché est trop gros). +Si sa taille le permet, le fichier caché sera créé en remettant dans l'ordre +les octets extraits. + +Dans notre exemple, les données cachées seront extraites grâce au mot de passe +"alicebob" choisi par Alice lors de l'insertion et nécessaire pour l'extraction +effectuée par Bob. +Les données cachées extraites seront écrites dans le fichier +/user/home/Bureau/message.txt. Bob pourra ainsi lire le message de Alice +contenu dans le fichier message.txt. + +\section{Problèmes et points délicats rencontrés} + +\subsection{Lecture et écriture de fichiers, endianness} + +La première difficulté est arrivée lors de l'implémentation du module +\textit{Vérification de la compatibilité des fichiers}. En effet, il fallait +lire la signature des fichiers hôtes pour l'insertion et l'extraction. + +Nous ne comprenions pas pourquoi, sur certains formats, le little endian était +utilisé tandis que sur d'autres, le big endian était utilisé. De plus, il +fallait également convertir les octets lus dans l'endian de la machine de +l'utilisateur. L'équipe de conception a fait des recherches à propos des +processeurs little/big endian et les processeurs fonctionnant en big endian sont +principalement des processeurs pour les systèmes embarqués. Dans le cas des +ordinateurs de bureau fonctionnement avec des processeur x86, le little endian +est utilisé. De ce fait, il fallait lire les données des fichiers et les +convertir en little endian. + +Nous avons donc trouvé une solution : la création de fonctions de conversion +entre les deux endianness. L'équipe StegX pouvait utiliser la les fonctions +fournies dans le header \textit{endian.h}, conforme à la norme POSIX du C. +Cependant, notre application étant multi-plateforme (Linux et Windows), il nous +fallait utiliser des fonctions conformes au C ANSI/ISO. Le meilleur moyen était +donc de reproduire nous même ces fonctions de conversion pour l'application +\label{endian}. + +\subsection{Étude des différents formats et algorithme Metadata} + +La deuxième difficulté rencontrée est celle de l'implémentation de l'algorithme +Metadata. La particularité de cet algorithme est le fait qu'elle dépend du +format du fichier hôte. Il fallait donc étudier avec précision la nature +des éléments qui composent le format ciblé. + +\subsection{Format MP3} + +Le format MP3 est sans nul doute le format le plus compliqué de ceux que +l'équipe StegX s'est occupé. En effet, il s'agit d'un format compressé utilisant +des algorithmes de compression divers et complexes. Pour ce format, il a fallu +que le binôme composé de Pierre Ayoub et Damien Delaunay, chargé de réaliser +l'insertion et l'extraction des formats audio, décortique les spécifications +précises de plusieurs versions du format (MPEG 1 Layer III, MPEG 2 Layer III), +et de plusieurs versions de formats de métadonnée (ID3 version 1 et ID3 version +2). Une fois cela fait, il a fallu récupérer plusieurs fichiers échantillons +correspondant aux différents formats et versions afin de les analyser en +hexadécimal et en faire la lecture pour en comprendre le mécanisme. Ces +opérations ont été longues et pointilleuses et aucun algorithme ne pouvait être +proposé dans la version de StegX rendue pour le 25 mai. + +\section{Bilan logiciel et humain} + +\subsection{Bilan logiciel} + +Le produit StegX répond bien aux objectifs : faire de la stéganographie sur des +fichiers de type image, audio et vidéo en utilisant plusieurs algorithmes. Par +ailleurs, StegX propose bien 2 interfaces : une en ligne de commande et une +autre graphique. Avoir choisi le langage C a été le meilleur choix car ce +langage nous a permis de manipuler facilement les données binaires des fichiers +en entrée. Elle propose également pour les futurs développeurs une librairie +afin de faire de la stéganographie. + +En plus de répondre aux objectifs, l'équipe de conception s'est efforcée +de répondre aux besoins du client. + +\subsection{Bilan humain} + +En ce qui concerne le bilan humain de ce projet, tous les membres de l'équipe +de conception se sont efforcés à travailler de façon professionnelle et ordonnée. +En effet, ils leur tenaient à coeur de mettre en application toute la méthodologie +informatique acquise durant la licence. Par ailleurs, en raison de la grande +charge de travail, il a fallu dès le début du projet diviser la conception +selon les trois différents types dont StegX doit se charger. + +Enfin, nous n'avions pas de chef de projet. Cette décision nous a été très +favorable du fait que chacun a eu la maturité de comprendre les enjeux de ce +grand projet. En effet, ces enjeux étaient grands et c'est pour cela que le +projet IN608 est la matière la plus travaillée du semestre. Cela aura été le +projet le plus apprécié de la licence par toute l'équipe, car il était de très +grande envergure et l'équipe a proposé son propre sujet. + +\end{document} diff --git a/src/lib/src/algo/eoc.c b/src/lib/src/algo/eoc.c index 7d8c507..73ac463 100644 --- a/src/lib/src/algo/eoc.c +++ b/src/lib/src/algo/eoc.c @@ -14,6 +14,7 @@ #include "../insert.h" #include "../protection.h" #include "../endian.h" +#include "../rand.h" int insert_eoc(info_s * infos) { @@ -108,6 +109,7 @@ int insert_eoc(info_s * infos) else data_size += data_per_vtag; } + data_size++; /* data_size ne peut pas faire plus de 3 octets */ if (data_size > 0xFFFFFF) return perror("Can't write data, hidden file to big."), 1; @@ -135,10 +137,15 @@ int insert_eoc(info_s * infos) fseek(infos->hidden, nb_block * data_per_vtag, SEEK_SET); limit = (nb_block == infos->host.file_info.flv.nb_video_tag-1) ? data_per_vtag + reste : data_per_vtag; - srand(create_seed(infos->passwd)); + stegx_srand(create_seed(infos->passwd)); + + //ajout d'un octet pour éviter les distortions + byte_cpy=28; + fwrite(&byte_cpy,sizeof(uint8_t),1,infos->res); + for(uint32_t i=0;ihidden); - byte_cpy ^= rand() % UINT8_MAX; + byte_cpy ^= stegx_rand() % UINT8_MAX; fwrite(&byte_cpy,sizeof(uint8_t),1,infos->res); } //lecture previous tagsize @@ -153,7 +160,9 @@ int insert_eoc(info_s * infos) /* previous_tag_size ne peut pas faire plus de 4 octets */ if (data_size > 0xFFFFFFFF) return perror("Can't write data, hidden file to big."), 1; + prev_tag_size++; prev_tag_size = stegx_htobe32(prev_tag_size); + fwrite(&prev_tag_size, sizeof(uint32_t), 1, infos->res); cpt_video_tag += 1; } @@ -180,7 +189,7 @@ int extract_eoc(info_s * infos) uint8_t byte_cpy; uint32_t data_size; uint8_t tag_type; - uint32_t cpt_video_tag = 0; + uint32_t cpt_video_tag = -1; uint32_t nb_block = 0; uint32_t data_jump; uint32_t write_data; @@ -213,21 +222,22 @@ int extract_eoc(info_s * infos) } datab=1; } - + //saute de header + fseek(infos->host.host, 13, SEEK_SET); do { - //saute de header - fseek(infos->host.host, 13, SEEK_SET); - cursor = 0; + if(datab){ while(data2[cursor] != nb_block) cursor++; - cpt_video_tag = -1; + /* Recherche du tag vidéo numéro cursor */ while(cursor != cpt_video_tag){ + fread(&tag_type, sizeof(uint8_t), 1, infos->host.host); - if(tag_type == 9) + if(tag_type == 9){ cpt_video_tag++; + } fread(&data_size, sizeof(uint32_t), 1, infos->host.host); //passage en 24 bits data_size = stegx_be32toh(data_size) >> 8; @@ -238,7 +248,9 @@ int extract_eoc(info_s * infos) } } else { - + //on revient à la fin du header + fseek(infos->host.host, 13, SEEK_SET); + cursor = 0; while(data[cursor] != nb_block) cursor++; cpt_video_tag = -1; @@ -265,16 +277,18 @@ int extract_eoc(info_s * infos) data_jump = data_size - data_per_vtag + 6; write_data = data_per_vtag; } + fseek(infos->host.host, data_jump, SEEK_CUR); - srand(create_seed(infos->passwd)); + stegx_srand(create_seed(infos->passwd)); /* Recopie des données dans le fichhier resultat */ for(uint32_t i = 0; i < write_data; i++){ fread(&byte_cpy, sizeof(uint8_t), 1, infos->host.host); - byte_cpy ^= rand() % UINT8_MAX; + byte_cpy ^= stegx_rand() % UINT8_MAX; fwrite(&byte_cpy,sizeof(uint8_t),1,infos->res); } nb_block++; + fseek(infos->host.host,4,SEEK_CUR); }while(nb_block < infos->host.file_info.flv.nb_video_tag); if (datab) free(data2); diff --git a/src/lib/src/algo/eof.c b/src/lib/src/algo/eof.c index 227f2ab..6b4b3b6 100644 --- a/src/lib/src/algo/eof.c +++ b/src/lib/src/algo/eof.c @@ -48,6 +48,16 @@ int insert_eof(info_s * infos) if (ferror(infos->host.host)) return perror("EOF: Can't read a copy of the host file"), 1; } + // Format MP3. + if (infos->host.type == MP3) { + // Recopie du fichier hôte. Utilisation de la taille et d'un compteur pour ne pas copier des données indésirables + // qui serait à la fin du fichier. + for (unsigned int cnt = 0, size = infos->host.file_info.mp3.eof, b; + fread(&b, sizeof(uint8_t), 1, infos->host.host) == 1 && cnt < size; cnt++) + fwrite(&b, sizeof(uint8_t), 1, infos->res); + if (ferror(infos->host.host) || ferror(infos->res)) + return perror("EOF MP3: Can't copy the host file"), 1; + } /* Écriture de la signature. */ if (write_signature(infos)) @@ -90,6 +100,9 @@ int extract_eof(info_s * infos) if (fseek(infos->host.host, infos->host.file_info.flv.file_size, SEEK_SET)) return perror("EOF: Can't jump to StegX signature"), 1; } + // Format MP3. + if (infos->host.type == MP3 && fseek(infos->host.host, infos->host.file_info.mp3.eof, SEEK_SET)) + return perror("EOF MP3: Can't jump to StegX signature"), 1; /* Saut de la signature. */ if (sig_fseek(infos->host.host, infos->hidden_name, infos->method)) diff --git a/src/lib/src/algo/lsb.c b/src/lib/src/algo/lsb.c index fec5402..5d76d31 100644 --- a/src/lib/src/algo/lsb.c +++ b/src/lib/src/algo/lsb.c @@ -14,6 +14,12 @@ #include "stegx_errors.h" #include "protection.h" #include "insert.h" +#include "rand.h" + +/** MP3 : masque à appliquer au header où cacher un bit. */ +static const uint32_t mp3_mask[MP3_HDR_NB_BITS_MODIF] = {0xFFFFFFFB, 0xFFFFFFF7, 0xFFFFFEFF}; +/** MP3 : offset à appliquer au bit à caché / déjà caché en fonction du masque. */ +static const uint32_t mp3_shift[MP3_HDR_NB_BITS_MODIF] = {2, 3, 8}; int protect_data_lsb(uint8_t * pixels, uint32_t pixels_length, uint8_t * data, uint32_t data_length, char *passwd, mode_e mode) @@ -35,7 +41,7 @@ int protect_data_lsb(uint8_t * pixels, uint32_t pixels_length, uint8_t * data, u } } // Création de la seed pour la generation pseudo aleatoires de nombres - srand(create_seed(passwd)); + stegx_srand(create_seed(passwd)); /* * hidden_length_recalcul : Nombre d'éléments du tableau data au fur @@ -59,7 +65,7 @@ int protect_data_lsb(uint8_t * pixels, uint32_t pixels_length, uint8_t * data, u l = m = 0; // on recherche un octet (composante de couleur où cacher les 2 bits) au hasard assert(hidden_length_recalcul != 1); - rang = rand() % (hidden_length_recalcul - 1); //-1 (au dernier tour rang vaudra 1) + rang = stegx_rand() % (hidden_length_recalcul - 1); //-1 (au dernier tour rang vaudra 1) // Cas spécial : si rang == 0 on cherche le prochain element non modifié if (rang == 0) { while (done[m] == 1) { @@ -128,18 +134,18 @@ int insert_lsb(info_s * infos) assert(infos); assert(infos->mode == STEGX_MODE_INSERT); assert(infos->algo == STEGX_ALGO_LSB); - uint32_t nb_cpy = 0; //nb doctets recopies - uint8_t byte_read_hidden, byte_read_host; - uint8_t mask_host, mask_hidden; - int i; if (fseek(infos->host.host, 0, SEEK_SET) == -1) return perror("Can't make jump host file"), 1; if (fseek(infos->hidden, 0, SEEK_SET) == -1) return perror("Can't make jump hidden file"), 1; // pour le format BMP et WAVE - assert(infos->host.type == BMP_UNCOMPRESSED || infos->host.type == WAV_PCM); + assert(infos->host.type == BMP_UNCOMPRESSED || infos->host.type == WAV_PCM || infos->host.type == MP3); if (infos->host.type == BMP_UNCOMPRESSED || infos->host.type == WAV_PCM) { + uint32_t nb_cpy = 0; //nb doctets recopies + uint8_t byte_read_hidden, byte_read_host; + uint8_t mask_host, mask_hidden; + int i; // Recopie du header dans le fichier resultat -> taille du header de l'hote while (nb_cpy < (infos->host.file_info.bmp.header_size)) { @@ -161,13 +167,13 @@ int insert_lsb(info_s * infos) || (infos->host.file_info.bmp.data_size > LENGTH_FILE_MAX)) { mask_host = 0xFC; // 11111100 en binaire - srand(create_seed(infos->passwd)); + stegx_srand(create_seed(infos->passwd)); // Cacher en LSB les donnees du fichier a cacher while (nb_cpy < (infos->hidden_length)) { // Lecture de l'octet du fichier a cacher if (fread(&byte_read_hidden, sizeof(uint8_t), 1, infos->hidden) == 0) return perror("Can't read data hidden"), 2; - byte_read_hidden ^= rand() % UINT8_MAX; + byte_read_hidden ^= stegx_rand() % UINT8_MAX; // pour chaque paire de bits dans un octet (soit 4) mask_hidden = 0xC0; // 11000000 en binaire @@ -250,6 +256,58 @@ int insert_lsb(info_s * infos) } return 0; } + + /* Insertion en LSB sur le format MP3. */ + if (infos->host.type == MP3) { + /* Initialisation. */ + FILE * h = infos->host.host, * r = infos->res; // Fichier hôte et fichier résultant. + mp3_s * hs = &(infos->host.file_info.mp3); // Structure du fichier hôte. + srand(create_seed(infos->passwd)); + + /* Recopie du header ID3v2 du fichier hôte s'il y en à un. */ + uint8_t buf[BUFSIZ], b = 0; // Buffer, octet temporaire lu. + for (int s = hs->fr_frst_adr; s && fread(buf, sizeof(*buf), s < BUFSIZ ? s : BUFSIZ, h);) + s -= fwrite(buf, sizeof(*buf), s < BUFSIZ ? s : BUFSIZ, r); + if (ferror(h) || ferror(r)) + return perror("insert_lsb MP3: Can't copy the header of the MP3 file"), 1; + + assert(ftell(h) == hs->fr_frst_adr); + /* Lecture successive des headers de chaque frame. "b_cnt" et "hdr_cnt" sont + * respectivement les compteurs des bits à traiter et traités de l'octet + * et du header venant d'être lu.*/ + uint32_t hdr = 0; // Header lu. + for (uint32_t hdr_cnt = 0, b_cnt = 0; fread(&hdr, sizeof(hdr), 1, h) && mp3_mpeg_hdr_test(hdr = stegx_be32toh(hdr)); hdr_cnt = 0) { + /* S'il ne reste plus de données déjà lues à cacher, on relis. Si on peux relire, on remet le compteur "b_cnt" égal à 8 bits à cacher. + * Tant qu'il reste des bits à caché dans l'octet lu et qu'on à pas saturé le header du MP3, on cache. On relis si besoin le fichier + * à cacher pour saturer le header du MP3 jusqu'à ce qu'on ai tout lu. */ + for (; (b_cnt = !b_cnt ? fread(&b, sizeof(b), 1, infos->hidden) * 8 : b_cnt) && hdr_cnt < MP3_HDR_NB_BITS_MODIF; + b >>= 1, b_cnt--, hdr_cnt++) { + /* Si on vient de lire un octet du fichier à cacher. */ + if (b_cnt == 8) + b ^= rand() % UINT8_MAX; + hdr = (hdr & mp3_mask[hdr_cnt]) | ((b & 1) << mp3_shift[hdr_cnt]); + } + /* On écrit le header éventuellement modifié puis les données de la frame. */ + if (!fwrite((hdr = stegx_htobe32(hdr), &hdr), sizeof(hdr), 1, r) || mp3_mpeg_fr_write(stegx_be32toh(hdr), h, r)) + return perror("insert_lsb MP3: Can't write current MPEG header and frame"), 1; + } + if (ferror(h)) + return perror("insert_lsb MP3: Can't read frame header"), 1; + + /* Curseur sur un tag ID3v1 => écris le tag. */ + if (mp3_id3v1_hdr_test(hdr)) { + if (!fwrite((hdr = stegx_htobe32(hdr), &hdr), sizeof(hdr), 1, r) || mp3_id3v1_tag_write(h, r)) + return perror("insert_lsb MP3: Can't write ID3v1 tag at the end of file"), 1; + } + + assert(ftell(h) == hs->eof); + assert(feof(infos->hidden)); + /* Écriture de la signature et fin du LSB. */ + if (write_signature(infos)) + return stegx_errno = ERR_INSERT, 1; + return 0; + } + // si les formats ne sont pas corrects erreur return 1; } @@ -259,14 +317,14 @@ int extract_lsb(info_s * infos) assert(infos); assert(infos->mode == STEGX_MODE_EXTRACT); assert(infos->algo == STEGX_ALGO_LSB); - uint32_t header_size; - uint32_t nb_cpy; - uint8_t byte_read_host; if (fseek(infos->host.host, 0, SEEK_SET) == -1) - return perror("Can't make extraction LSB"), 1; + return perror("extract_lsb: Can't jump to the beginning of the host file"), 1; // pour les formats BMP et WAVE if (infos->host.type == BMP_UNCOMPRESSED || infos->host.type == WAV_PCM) { + uint32_t header_size; + uint32_t nb_cpy; + uint8_t byte_read_host; header_size = infos->host.file_info.bmp.header_size; // déplacement jusqu'au debut de l'image brute @@ -278,7 +336,7 @@ int extract_lsb(info_s * infos) nb_cpy = 0; int i; uint8_t mask_host, byte_created; - srand(create_seed(infos->passwd)); + stegx_srand(create_seed(infos->passwd)); mask_host = 0x03; // 00000011 en binaire // Extraire en LSB les donnees du fichier a cacher -> taille du fichier a cacher @@ -299,7 +357,7 @@ int extract_lsb(info_s * infos) byte_read_host <<= (-2 * i + 6); // equation pour trouver le decalage a faire byte_created = byte_created + byte_read_host; } - byte_created ^= rand() % UINT8_MAX; + byte_created ^= stegx_rand() % UINT8_MAX; if (fwrite(&byte_created, sizeof(uint8_t), 1, infos->res) != 1) return perror("Sig: Can't write data hidden extracted"), 1; nb_cpy++; @@ -334,6 +392,46 @@ int extract_lsb(info_s * infos) return 0; } } + + /* Extraction en LSB sur le format MP3. */ + if (infos->host.type == MP3) { + /* Initialisation. */ + FILE * h = infos->host.host, * r = infos->res; // Fichier hôte et fichier résultant. + mp3_s * hs = &(infos->host.file_info.mp3); // Structure du fichier hôte. + srand(create_seed(infos->passwd)); + + /* Saut du header ID3v2 du fichier hôte s'il y en à un. */ + if (fseek(h, hs->fr_frst_adr, SEEK_SET)) + return perror("extract_lsb: Can't jump over ID3v2 tag"), -1; + + assert(ftell(h) == hs->fr_frst_adr); + /* Lecture successive des headers de chaque frame tant qu'on à pas fini + * d'écrire la taille du fichier qui était caché dans le fichier + * résultat. "s" correspond à la taille actuellement écrite dans le + * fichier resultat. "hdr" est le header lu. "b_cnt" et "hdr_cnt" sont + * respectivement les compteurs des bits à traiter et traités de l'octet + * et du header venant d'être lu.*/ + uint8_t b = 0; // Octet reconstitué à écrire dans le résultat. + for (uint32_t s = 0, hdr = 0, hdr_cnt = 0, b_cnt = 8; fread(&hdr, sizeof(hdr), 1, h) && s < infos->hidden_length; hdr_cnt = 0) { + hdr = stegx_be32toh(hdr); + /* Tant que l'on à pas lu les bits caché du header en cours. */ + for (; hdr_cnt < MP3_HDR_NB_BITS_MODIF; b_cnt--, hdr_cnt++) { + /* Si notre octet est complètement reconstitué. */ + for (; !b_cnt ; b = 0, s++) { + b ^= rand() % UINT8_MAX; + b_cnt = fwrite(&b, sizeof(b), 1, r) * 8; + } + b |= ((hdr & ~mp3_mask[hdr_cnt]) >> mp3_shift[hdr_cnt]) << (8 - b_cnt); + } + /* On saute la frame quand on à récupéré tout les bits du header. */ + if (mp3_mpeg_fr_seek(hdr, h)) + return perror("insert_lsb MP3: Can't skip current MP3 MPEG frame"), 1; + } + if (ferror(h) || ferror(r)) + return perror("insert_lsb MP3: Can't read the host file or write the res file"), 1; + return 0; + } + // si le format du fichier n'est pas correct -> renvoie une erreur return 1; } diff --git a/src/lib/src/detect_algo.c b/src/lib/src/detect_algo.c index fd6e658..bd5b1e9 100644 --- a/src/lib/src/detect_algo.c +++ b/src/lib/src/detect_algo.c @@ -43,6 +43,8 @@ static int read_signature(info_s * infos) infos->host.file_info.wav.data_size, SEEK_SET)) return perror("BMP, PNG & WAVE: Can't move to StegX signature"), 1; } else if (infos->host.type == MP3) { + if (fseek(infos->host.host, infos->host.file_info.mp3.eof, SEEK_SET)) + return perror("MP3: Can't move to StegX signature"), 1; } else if (infos->host.type == AVI_COMPRESSED || infos->host.type == AVI_UNCOMPRESSED) { fseek(infos->host.host, 4, SEEK_SET); uint32_t file_size; diff --git a/src/lib/src/file_type/bmp.c b/src/lib/src/file_type/bmp.c index 747504f..b01340b 100644 --- a/src/lib/src/file_type/bmp.c +++ b/src/lib/src/file_type/bmp.c @@ -16,6 +16,7 @@ #include "../insert.h" #include "../protection.h" #include "../endian.h" +#include "../rand.h" /** Signature BMP */ #define SIG_BMP 0x4D42 @@ -109,10 +110,10 @@ int insert_metadata_bmp(info_s * infos) * suite pseudo aleatoire générée avec le mot de passe * */ if (infos->hidden_length > LENGTH_FILE_MAX) { - srand(create_seed(infos->passwd)); + stegx_srand(create_seed(infos->passwd)); uint8_t random; while (fread(&byte_read_bmp, sizeof(uint8_t), 1, infos->hidden) != 0) { - random = rand() % UINT8_MAX; + random = stegx_rand() % UINT8_MAX; byte_read_bmp = byte_read_bmp ^ random; //XOR avec le nombre pseudo aleatoire generé if (fwrite(&byte_read_bmp, sizeof(uint8_t), 1, infos->res) == 0) return perror("Can't write hidden data"), 1; @@ -179,11 +180,11 @@ int extract_metadata_bmp(info_s * infos) * */ uint8_t byte_read, byte_cpy; if (infos->hidden_length > LENGTH_FILE_MAX) { - srand(create_seed(infos->passwd)); + stegx_srand(create_seed(infos->passwd)); int i = 0; uint8_t random; while (fread(&byte_cpy, sizeof(uint8_t), 1, infos->host.host) != 0) { - random = rand() % UINT8_MAX; + random = stegx_rand() % UINT8_MAX; byte_cpy = byte_cpy ^ random; //XOR avec le nombre pseudo aleatoire generé if (fwrite(&byte_cpy, sizeof(uint8_t), 1, infos->res) == 0) return perror("Can't write hidden data"), 1; diff --git a/src/lib/src/file_type/mp3.c b/src/lib/src/file_type/mp3.c index 362d0e8..6370fde 100644 --- a/src/lib/src/file_type/mp3.c +++ b/src/lib/src/file_type/mp3.c @@ -13,35 +13,211 @@ #include "common.h" #include "stegx_common.h" #include "stegx_errors.h" +#include "mp3.h" -/** Signature du MP3 version ID3V1. */ -#define SIG_MP3_ID3V1 0xFBFF -/** Signature du MP3 version ID3V2. */ -#define SIG_MP3_ID3V2 0x334449 +/** Signature du MPEG 1 Layer III. */ +#define SIG_MPEG1_LAYER3 0xFFFA0000 +/** Signature du MPEG 2 Layer III. */ +#define SIG_MPEG2_LAYER3 0xFFF20000 +/** Signature de l'ID3v1. */ +#define SIG_ID3V1 0x54414700 +/** Signature de l'ID3v2. */ +#define SIG_ID3V2 0x49443300 + +/** Masque à appliquer pour reconnaître la signature du MPEG 1/2 Layer III. */ +#define MASK_MPEG_LAYER3 0xFFFE0000 +/** Masque à appliquer pour reconnaître la signature de l'ID3. */ +#define MASK_ID3 0xFFFFFF00 + +/** Taille d'un TAG ID3v1. */ +#define TAG_ID3V1_SIZE 128 +/** Taille d'un header de tag ID3v2. */ +#define TAG_ID3V2_HEADER_SIZE 10 + +/** + * @brief Test si le header est un header ID3v2. + * @param hdr Header à tester. + * @return 0 si le header est incorrect, sinon 1 si le header est valide. + * @author Pierre Ayoub, Damien Delaunay + */ +static int mp3_id3v2_hdr_test(const uint32_t hdr) +{ + return (hdr & MASK_ID3) == SIG_ID3V2; +} + +/** + * @brief Retire la sécurité de synchronisation de la taille d'un tag ID3v2. + * @param s Taille lu du tag. + * @return Taille réelle du tag. + * @req "s" doit être un entier "syncsafe". + * @author Pierre Ayoub, Damien Delaunay + */ +static uint32_t mp3_id3v2_size_unsyncsafe(const uint32_t s) +{ + assert(!(0x80808080 & s) && "L'entier doit être un entier syncsafe"); + return (0x0000007F & s) | ((0x00007F00 & s) >> 1) | ((0x007F0000 & s) >> 2) | ((0x7F000000 & s) >> 3); +} + +/** + * @brief Saute le tag ID3v2 actuel. + * @param f Fichier MP3. + * @return Valeur de retour de fseek(). + * @req Le fichier f doit être ouvert en lecture. Lors du déplacement, il est + * considéré que la signature du tag vient d'être lu, le curseur de lecture + * est donc à "debut_du_fichier + sizeof(sig)". + * @sideeffect Le curseur de lecture du fichier est modifié pour sauter le + * tag. + * @author Pierre Ayoub, Damien Delaunay + */ +static int mp3_id3v2_tag_seek(FILE * f) +{ + assert(f && ftell(f) == 4 && "Les 4 premiers octets du fichier (header ID3v2) doivent êtres lus"); + uint8_t flags = 0; + uint32_t size = 0; + /* Lecture des flags et de la taille. */ + if (fseek(f, 1, SEEK_CUR) || fread(&flags, sizeof(flags), 1, f) != 1) + return perror("mp3_id3v2_tag_seek: Can't read flags of the header"), -1; + if (fread(&size, sizeof(size), 1, f) != 1) + return perror("mp3_id3v2_tag_seek: Can't read size of the header"), -1; + assert(ftell(f) == 10); + return fseek(f, mp3_id3v2_size_unsyncsafe(stegx_be32toh(size)) + TAG_ID3V2_HEADER_SIZE, SEEK_SET); +} + +/** + * @brief Obtient la version de la norme MPEG du header d'une frame MP3. + * @param hdr Header MPEG. + * @return 1 ou 2 si la version est reconnu, sinon retourne 0. + * @req Le header doit être un header MPEG. + * @author Pierre Ayoub, Damien Delaunay + */ +static int mp3_mpeg_hdr_get_version(const uint32_t hdr) +{ + assert(mp3_mpeg_hdr_test(hdr) && "Le header doit être un header MPEG 1/2 Layer III"); + static const int ver[4] = {0, 0, 2, 1}; + return ver[(hdr & 0x180000) >> 19]; +} + +/** + * @brief Permet de savoir si une frame MP3 contient du padding. + * @param hdr Header MPEG de la frame. + * @return 1 si la frame contient du padding, 0 sinon. + * @req Le header doit être un header MPEG. + * @author Pierre Ayoub, Damien Delaunay + */ +static int mp3_mpeg_hdr_is_padding(const uint32_t hdr) +{ + assert(mp3_mpeg_hdr_test(hdr) && "Le header doit être un header MPEG 1/2 Layer III"); + return (hdr & 0x00000200) >> 9; +} + +/** + * @brief Obtient la fréquence d'échantillonage d'une frame MP3. + * @param hdr Header MPEG de la frame. + * @return La fréquence d'échantillonage en Hz si elle est reconnu, sinon 0. + * @req Le header doit être un header MPEG. + * @author Pierre Ayoub, Damien Delaunay + */ +static int mp3_mpeg_hdr_get_samprate(const uint32_t hdr) +{ + assert(mp3_mpeg_hdr_test(hdr) && "Le header doit être un header MPEG 1/2 Layer III"); + static const int samp[3][4] = {{0}, {44100, 48000, 32000, 0}, {22050, 24000, 16000, 0}}; + return samp[mp3_mpeg_hdr_get_version(hdr)][(hdr & 0x00000C00) >> 10]; +} + +/** + * @brief Obtient la débit binaire d'une frame MP3. + * @param hdr Header MPEG de la frame. + * @return Le débit binaire en Kbps (10^3 bits par seconde) si il est reconnu, + * sinon 0. + * @req Le header doit être un header MPEG. + * @author Pierre Ayoub, Damien Delaunay + */ +static int mp3_mpeg_hdr_get_bitrate(const uint32_t hdr) +{ + assert(mp3_mpeg_hdr_test(hdr) && "Le header doit être un header MPEG 1/2 Layer III"); + static const int bit[3][16] = {{0}, + {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0}, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0}}; + return bit[mp3_mpeg_hdr_get_version(hdr)][(hdr & 0x0000F000) >> 12]; +} + +/** + * @brief Obtient la taille d'une frame MP3. + * @param hdr Header MPEG de la frame. + * @return Taille d'une frame MP3 (header + données). + * @req Le header doit être un header MPEG. + * @author Pierre Ayoub, Damien Delaunay + */ +static int mp3_mpeg_hdr_get_size(const uint32_t hdr) +{ + assert(mp3_mpeg_hdr_test(hdr) && "Le header doit être un header MPEG 1/2 Layer III"); + return ((144000 * mp3_mpeg_hdr_get_bitrate(hdr)) / mp3_mpeg_hdr_get_samprate(hdr)) + mp3_mpeg_hdr_is_padding(hdr); +} + +int mp3_mpeg_hdr_test(const uint32_t hdr) +{ + return (hdr & MASK_MPEG_LAYER3) == SIG_MPEG1_LAYER3 || (hdr & MASK_MPEG_LAYER3) == SIG_MPEG2_LAYER3; +} + +int mp3_mpeg_fr_seek(const uint32_t hdr, FILE * f) +{ + assert(mp3_mpeg_hdr_test(hdr) && "Le header doit être un header MPEG 1/2 Layer III"); + assert(f); + return fseek(f, mp3_mpeg_hdr_get_size(hdr) - sizeof(hdr), SEEK_CUR); +} + +int mp3_mpeg_fr_write(uint32_t hdr, FILE * src, FILE * dst) +{ + assert(mp3_mpeg_hdr_test(hdr) && "Le header doit être un header MPEG 1/2 Layer III"); + assert(src && dst); + uint8_t buf[BUFSIZ]; // Copie efficace avec un buffer. + int s = mp3_mpeg_hdr_get_size(hdr) - sizeof(hdr); + while (s && fread(buf, sizeof(*buf), s < BUFSIZ ? s : BUFSIZ, src)) + s -= fwrite(buf, sizeof(*buf), s < BUFSIZ ? s : BUFSIZ, dst); + return ferror(src) || ferror(dst) ? -1 : 0; +} + +long int mp3_mpeg_fr_find_first(FILE * f) +{ + assert(f); + uint32_t hdr = 0; + if (fseek(f, 0, SEEK_SET) || fread(&hdr, sizeof(hdr), 1, f) != 1) + return perror("mp3_mpeg_fr_find_first: Can't read MP3 first signature"), -1; + hdr = stegx_be32toh(hdr); + /* Si on est sur de l'ID3v2, on saute le tag. */ + if (mp3_id3v2_hdr_test(hdr) && mp3_id3v2_tag_seek(f)) + return perror("mp3_mpeg_fr_find_first: Can't skip over ID3v2 tag"), -1; + /* Si on était sur une frame MPEG 1/2 Layer III ou que l'on est dessus après avoir sauté le tag ID3v2. */ + return mp3_mpeg_hdr_test(hdr) || ((fread(&hdr, sizeof(hdr), 1, f) == 1) && mp3_mpeg_hdr_test(stegx_be32toh(hdr))) ? + ftell(f) - (int)sizeof(hdr) : -1; +} + +int mp3_id3v1_hdr_test(const uint32_t hdr) +{ + return (hdr & MASK_ID3) == SIG_ID3V1; +} + +int mp3_id3v1_tag_seek(FILE * f) +{ + assert(f); + return fseek(f, TAG_ID3V1_SIZE - sizeof(uint32_t), SEEK_CUR); +} + +int mp3_id3v1_tag_write(FILE * src, FILE * dst) +{ + assert(src && dst); + uint8_t buf[TAG_ID3V1_SIZE]; // Copie efficace avec un buffer. + int s = TAG_ID3V1_SIZE - sizeof(uint32_t); + fread(buf, sizeof(*buf), s, src), fwrite(buf, sizeof(*buf), s, dst); + return ferror(src) || ferror(dst) ? -1 : 0; +} type_e stegx_test_file_mp3(FILE * file) { assert(file); - /* Lecture de la signature MP3 version ID3V1. */ - uint16_t sig_16; - if (fseek(file, 0, SEEK_SET) == -1) - return perror("Can't move to MP3 ID3V1 signature"), -1; - if (fread(&sig_16, sizeof(uint16_t), 1, file) != 1) - return perror("Can't read MP3 ID3V1 signature"), -1; - if (sig_16 == SIG_MP3_ID3V1) - return MP3; - - /* Lecture de la signature MP3 version ID3V2. */ - uint32_t sig_32; - if (fseek(file, 0, SEEK_SET) == -1) - return perror("Can't move to MP3 ID3V2 signature"), -1; - if (fread(&sig_32, sizeof(uint32_t), 1, file) != 1) - return perror("Can't read MP3 ID3V2 signature"), -1; - /* Suppression du premier octet car on s'occupe uniquement des 3 derniers - octets. */ - sig_32 &= 0x00FFFFFF; - if (sig_32 == SIG_MP3_ID3V2) - return MP3; - else - return UNKNOWN; + uint32_t sig = 0; + if (fseek(file, 0, SEEK_SET) || (fread(&sig, sizeof(sig), 1, file) != 1)) + return perror("stegx_test_file_mp3: Can't read first 4 bytes"), -1; + sig = stegx_be32toh(sig); + return mp3_id3v2_hdr_test(sig) || mp3_mpeg_hdr_test(sig) ? MP3 : UNKNOWN; } diff --git a/src/lib/src/file_type/mp3.h b/src/lib/src/file_type/mp3.h index 3668c0b..57c85dc 100644 --- a/src/lib/src/file_type/mp3.h +++ b/src/lib/src/file_type/mp3.h @@ -9,25 +9,118 @@ #define MP3_H #include +#include #include "common.h" +/** Nombre de bit modifiable en LSB dans un header MPEG 1/2 Layer III. */ +#define MP3_HDR_NB_BITS_MODIF 3 + /** * @brief Structure du format MP3. * @author Pierre Ayoub et Damien Delaunay */ struct mp3 { + long int fr_frst_adr; /*!< Adresse du header de la première frame MPEG 1/2 Layer III. */ + long int fr_nb; /*!< Nombre de frame MPEG 1/2 Layer III. */ + long int eof; /*!< Adresse de la fin du fichier officiel (sans signature et données + cachées, après la dernière frame ou après le TAG ID3v1). */ }; /** Type du format MP3. */ typedef struct mp3 mp3_s; /** - * @brief Test si le fichier est un fichier WAVE. + * @brief Test si le header est un header MPEG 1/2 Layer III. + * @param hdr Header à tester. + * @return 0 si le header est incorrect, sinon 1 si le header est valide. + * @author Pierre Ayoub, Damien Delaunay + */ +int mp3_mpeg_hdr_test(uint32_t hdr); + +/** + * @brief Saute la frame MP3 actuelle. + * @param hdr Header de la frame MP3 à sauter. + * @param f Fichier MP3. + * @return Valeur de retour de fseek(). + * @req Le fichier f doit être ouvert en lecture. Lors du déplacement, il est + * considéré que le header de la frame vient d'être lu, le curseur de lecture + * est donc à "debut_de_la_frame + sizeof(hdr)". + * @sideeffect Le curseur de lecture du fichier est modifié pour sauter la + * frame. + * @author Pierre Ayoub, Damien Delaunay + */ +int mp3_mpeg_fr_seek(uint32_t hdr, FILE * f); + +/** + * @brief Écris la frame MP3 actuelle. + * @param hdr Header de la frame MP3 à écrire. + * @param src Fichier MP3 source. + * @param dst Fichier de destination. + * @return -1 sur une erreur, sinon 0. + * @req Les fichiers src et dst doivent êtres ouverts en lecture. Lors de + * l'écriture, il est considéré que le header de la frame à déjà été lu puis + * écrit, les curseurs de lecture et d'écriture sont donc à "debut_de_la_frame + + * sizeof(hdr)". + * @sideeffect Les curseurs de lecture du fichier src et d'écriture du fichier + * dst sont modifiés pour lire puis écrire la frame. + * @author Pierre Ayoub, Damien Delaunay + */ +int mp3_mpeg_fr_write(uint32_t hdr, FILE * src, FILE * dst); + +/** + * @brief Trouve la première frame MPEG 1/2 Layer III. + * @param f Fichier où chercher. + * @return L'adresse du header de la première frame si elle est trouvée + * (ftell(f)), sinon -1 sur une erreur. + * @req Le fichier f doit être ouvert en lecture et bien être un fichier MP3. + * @sideeffect Modifie l'emplacement du curseur de lecture dans le fichier f. + * @author Pierre Ayoub, Damien Delaunay + */ +long int mp3_mpeg_fr_find_first(FILE * f); + +/** + * @brief Test si le header est un header ID3v1. + * @param hdr Header à tester. + * @return 0 si le header est incorrect, sinon 1 si le header est valide. + * @author Pierre Ayoub, Damien Delaunay + */ +int mp3_id3v1_hdr_test(uint32_t hdr); + +/** + * @brief Saute le tag ID3v1 actuel. + * @param f Fichier MP3. + * @return Valeur de retour de fseek(). + * @req Le fichier f doit être ouvert en lecture. Lors du déplacement, il est + * considéré que la signature du tag vient d'être lu, le curseur de lecture + * est donc à "debut_du_tag + sizeof(sig)". + * @sideeffect Le curseur de lecture du fichier est modifié pour sauter le + * tag. + * @author Pierre Ayoub, Damien Delaunay + */ +int mp3_id3v1_tag_seek(FILE * f); + +/** + * @brief Écris le tag ID3v1 actuel. + * @param src Fichier MP3 source. + * @param dst Fichier de destination. + * @return -1 sur une erreur, sinon 0. + * @req Les fichiers src et dst doivent êtres ouverts en lecture. Lors de + * l'écriture, il est considéré que le header du tag à déjà été lu puis + * écrit, les curseurs de lecture et d'écriture sont donc à "debut_du_tag + + * sizeof(hdr)". + * @sideeffect Les curseurs de lecture du fichier src et d'écriture du fichier + * dst sont modifiés pour lire puis écrire la frame. + * @author Pierre Ayoub, Damien Delaunay + */ +int mp3_id3v1_tag_write(FILE * src, FILE * dst); + +/** + * @brief Test si le fichier est un fichier MP3. * @param file Fichier à tester. - * @req Le pointeur ne doit pas être null et le fichier ouvert en lecture. + * @req Le fichier doit être ouvert en lecture. * @return \r{MP3}, \r{UNKNOWN} ou -1 en cas d'erreur. - * @author Clément Caumes, Pierre Ayoub et Damien Delaunay + * @author Pierre Ayoub, Damien Delaunay */ type_e stegx_test_file_mp3(FILE * file); diff --git a/src/lib/src/file_type/png.c b/src/lib/src/file_type/png.c index bde703f..567e07b 100644 --- a/src/lib/src/file_type/png.c +++ b/src/lib/src/file_type/png.c @@ -16,6 +16,7 @@ #include "../insert.h" #include "../protection.h" #include "../endian.h" +#include "../rand.h" /** Signature PNG */ #define SIG_PNG 0x0A1A0A0D474E5089 @@ -85,10 +86,10 @@ int insert_metadata_png(info_s * infos) * du seed (grace au mot de passe) **/ if (infos->hidden_length > LENGTH_FILE_MAX) { - srand(create_seed(infos->passwd)); + stegx_srand(create_seed(infos->passwd)); uint8_t random; for (length = 0; length < infos->hidden_length; length++) { - random = rand() % UINT8_MAX; + random = stegx_rand() % UINT8_MAX; data[length] = data[length] ^ random; //XOR avec le nombre pseudo aleatoire generé } } @@ -221,10 +222,10 @@ int extract_metadata_png(info_s * infos) * du seed (grace au mot de passe) **/ if (infos->hidden_length > LENGTH_FILE_MAX) { - srand(create_seed(infos->passwd)); + stegx_srand(create_seed(infos->passwd)); uint8_t random; for (length = 0; length < infos->hidden_length; length++) { - random = rand() % UINT8_MAX; + random = stegx_rand() % UINT8_MAX; data[length] = data[length] ^ random; //XOR avec le nombre pseudo aleatoire generé } } diff --git a/src/lib/src/init.c b/src/lib/src/init.c index 9dc281c..5f52d5b 100644 --- a/src/lib/src/init.c +++ b/src/lib/src/init.c @@ -99,6 +99,22 @@ info_s *stegx_init(stegx_choices_s * choices) if ((s->host.host != stdin) && !(s->host.host = fopen(choices->host_path, "rb"))) return perror(NULL), stegx_errno = ERR_HOST, NULL; + /* Si on a une entrée sur stdin, il faut la stocker dans un fichier + * temporaire car on ne peux pas faire de fseek() sur un flux. */ + if (s->hidden == stdin || s->host.host == stdin) { + FILE * tmp = NULL; + /* Ouverture en lecture / écriture du fichier temporaire. */ + if (!(tmp = fopen("/tmp/stegx", "w+b"))) + return perror("Can't create a temporary file in /tmp"), NULL; + /* Copie efficace de stdin vers le fichier temporaire. */ + uint8_t buf[BUFSIZ]; + for (int i = 0; (i = fread(buf, sizeof(*buf), BUFSIZ, stdin)) ;) + fwrite(buf, sizeof(*buf), i, tmp); + fseek(tmp, 0, SEEK_SET); + s->hidden = s->hidden == stdin ? tmp : s->hidden; + s->host.host = s->host.host == stdin ? tmp : s->host.host; + } + assert(s->mode == STEGX_MODE_INSERT || s->mode == STEGX_MODE_EXTRACT); assert(s->algo >= STEGX_ALGO_LSB && s->algo < STEGX_NB_ALGO); assert(s->method == STEGX_WITHOUT_PASSWD || s->method == STEGX_WITH_PASSWD); diff --git a/src/lib/src/protection.c b/src/lib/src/protection.c index 138f85e..78366d0 100644 --- a/src/lib/src/protection.c +++ b/src/lib/src/protection.c @@ -17,19 +17,7 @@ #include "stegx_common.h" #include "stegx_errors.h" #include "protection.h" - -unsigned int create_seed(const char *passwd) -{ - unsigned int srand_nb = 0; - int j = 0; - char carac = passwd[j]; - do { - srand_nb = (srand_nb + carac) % UINT_MAX; - j++; - carac = passwd[j]; - } while (carac != '\0'); - return srand_nb; -} +#include "rand.h" int protect_data(uint8_t * tab, uint32_t hidden_length, const char *passwd, mode_e mode) { @@ -52,7 +40,7 @@ int protect_data(uint8_t * tab, uint32_t hidden_length, const char *passwd, mode } // Création de la seed pour la generation pseudo aleatoires de nombres - srand(create_seed(passwd)); + stegx_srand(create_seed(passwd)); /* * hidden_length_recalcul : Nombre d'éléments du tableau data au fur @@ -67,7 +55,7 @@ int protect_data(uint8_t * tab, uint32_t hidden_length, const char *passwd, mode for (i = 0; i < hidden_length; i++) { l = m = 0; // on choisit au hasard le n-ieme élément a cacher - rang = rand() % (hidden_length_recalcul - 1); //-1 (au dernier tour rang vaudra 1) + rang = stegx_rand() % (hidden_length_recalcul - 1); //-1 (au dernier tour rang vaudra 1) // Cas spécial : si rang == 0 on cherche le prochain element non rangé dans result if (rang == 0) { @@ -122,17 +110,17 @@ int protect_data(uint8_t * tab, uint32_t hidden_length, const char *passwd, mode int data_xor_write_file(FILE * src, FILE * res, const char *passwd) { - srand(create_seed(passwd)); + stegx_srand(create_seed(passwd)); for (uint8_t b; fread(&b, sizeof(b), 1, src) == 1;) - fwrite((b ^= rand() % UINT8_MAX, &b), sizeof(b), 1, res); + fwrite((b ^= stegx_rand() % UINT8_MAX, &b), sizeof(b), 1, res); return ferror(src); } void data_xor_write_tab(uint8_t * src, const char *passwd, const uint32_t len) { - srand(create_seed(passwd)); + stegx_srand(create_seed(passwd)); for (uint32_t i = 0; i < len; i++) - src[i] ^= rand() % UINT8_MAX; + src[i] ^= stegx_rand() % UINT8_MAX; } int data_scramble_write(FILE * src, FILE * res, const char *pass, diff --git a/src/lib/src/protection.h b/src/lib/src/protection.h index 0198841..f4b5502 100644 --- a/src/lib/src/protection.h +++ b/src/lib/src/protection.h @@ -24,14 +24,6 @@ * METADATA/EOF -> taille du fichier a cacher */ #define LENGTH_FILE_MAX 50000 -/** - * @brief Cree un seed a partir d'un mot de passe. - * @param passwd Mot de passe a partir duquel on veut creer un seed. - * @return unsigned int représentant le seed a creer. - * @author Pierre Ayoub - */ -unsigned int create_seed(const char *passwd); - /** * @brief Fait le mélange ou réarrange les octets selon l'algorithme de * protection des données. diff --git a/src/lib/src/rand.c b/src/lib/src/rand.c new file mode 100644 index 0000000..64bc40f --- /dev/null +++ b/src/lib/src/rand.c @@ -0,0 +1,38 @@ +/** + * @file rand.c + * @brief Module qui s'occupe de l'aléatoire. + */ + +#include +#include +#include +#include + +#include "rand.h" + +/** + * Variable globale représentant la seed pour la suite pseudo aléatoire. + */ +unsigned int stegx_seed=0; + +unsigned int create_seed(const char *passwd) +{ + unsigned int srand_nb = 0; + int j = 0; + char carac = passwd[j]; + do { + srand_nb = (srand_nb + carac) % UINT_MAX; + j++; + carac = passwd[j]; + } while (carac != '\0'); + return srand_nb; +} + +void stegx_srand(unsigned int seed){ + stegx_seed=seed; +} + +int stegx_rand(){ + stegx_seed=(1103515245*stegx_seed+12345)%UINT_MAX; + return stegx_seed%INT_MAX; +} diff --git a/src/lib/src/rand.h b/src/lib/src/rand.h new file mode 100644 index 0000000..84f1760 --- /dev/null +++ b/src/lib/src/rand.h @@ -0,0 +1,36 @@ +/** + * @file rand.h + * @brief Module qui s'occupe de l'aléatoire. + */ + +#ifndef RAND_H +#define RAND_H + +#include +#include +#include + +/** + * @brief Cree un seed a partir d'un mot de passe. + * @param passwd Mot de passe a partir duquel on veut creer un seed. + * @return unsigned int représentant le seed a creer. + * @author Pierre Ayoub + */ +unsigned int create_seed(const char *passwd); + +/** + * @brief Initialise la seed de la suite pseudo aléatoire. + * @param seed nombre qui représentera la seed. + * @author Clément Caumes + */ +void stegx_srand(unsigned int seed); + +/** + * @brief Renvoie un entier pseudo-aléatoire. + * @return renvoie l'entier de la suite pseudo aléatoire. + * @author Clément Caumes + */ +int stegx_rand(); + +#endif + diff --git a/src/lib/src/sugg_algo.c b/src/lib/src/sugg_algo.c index 39eec85..78eda67 100644 --- a/src/lib/src/sugg_algo.c +++ b/src/lib/src/sugg_algo.c @@ -15,6 +15,7 @@ #include "common.h" #include "stegx_common.h" #include "stegx_errors.h" +#include "rand.h" /** * @brief Teste si l'on peut utiliser l'algorithme LSB pour la dissimulation. @@ -57,6 +58,10 @@ static int can_use_lsb(info_s * infos) if ((infos->hidden_length * 8) <= nb_bits_modif) return 0; } + /* Si le fichier hote est un fichier MP3. */ + else if (infos->host.type == MP3 && + (infos->host.file_info.mp3.fr_nb * MP3_HDR_NB_BITS_MODIF >= infos->hidden_length * 8)) + return 0; /* Sinon, on ne peux pas utiliser LSB. */ return 1; } @@ -70,9 +75,8 @@ static int can_use_lsb(info_s * infos) static int can_use_eof(info_s * infos) { assert(infos); - // Pour tous les formats proposés par StegX sauf AVI et MP3, on propose EOF. - return infos->host.type == MP3 || infos->host.type == AVI_COMPRESSED - || infos->host.type == AVI_UNCOMPRESSED ? 1 : !IS_FILE_TYPE(infos->host.type); + // Pour tous les formats proposés par StegX sauf AVI, on propose EOF. + return infos->host.type == AVI_COMPRESSED || infos->host.type == AVI_UNCOMPRESSED ? 1 : !IS_FILE_TYPE(infos->host.type); } /** @@ -301,11 +305,39 @@ int fill_host_info(info_s * infos) return 0; } - /* Structures AVI et MP3 : structures vides. */ - else if (infos->host.type == MP3 - || infos->host.type == AVI_COMPRESSED || infos->host.type == AVI_UNCOMPRESSED) { + /* Fichier MP3. */ + else if (infos->host.type == MP3) { + uint32_t hdr = 0; + long int * n = &(infos->host.file_info.mp3.fr_nb); + long int * f = &(infos->host.file_info.mp3.fr_frst_adr); + FILE * h = infos->host.host; + /* Déplacement et stockage de l'adresse du header de la première frame du MP3 (pour le "LSB"). */ + if ((*f = mp3_mpeg_fr_find_first(h)) == -1 || fseek(h, *f, SEEK_SET)) + return perror("MP3 fill_host_info: Can't find first MPEG 1/2 Layer III frame"), 1; + /* Dénombrement du nombre de frame (pour "can_use_lsb"). */ + for (*n = 0 ; (fread(&hdr, sizeof(hdr), 1, h) == 1) && mp3_mpeg_hdr_test(hdr = stegx_be32toh(hdr)); (*n)++) { + if (mp3_mpeg_fr_seek(hdr, h)) + return perror("MP3 fill_host_info: Can't skip current MP3 MPEG frame"), 1; + } + if (ferror(h)) + return perror("MP3 fill_host_info: Can't read frame header"), 1; + + /* Curseur sur un tag ID3v1 => saut au-dessus du tag et fin du fichier définitive. */ + if (mp3_id3v1_hdr_test(hdr) && mp3_id3v1_tag_seek(h)) + return perror("MP3 fill_host_info: Can't skip over ID3v1 tag at the end of file"), 1; + /* Stockage de la fin du fichier (pour EOF). */ + if ((infos->host.file_info.mp3.eof = ftell(h)) == -1) + return perror("MP3 fill_host_info: Can't get end-of-file address"), 1; + /* Curseur n'était pas sur un tag ID3v1 et la fin du fichier n'as pas + * été lu => on à lu 4 octets de données en trop (exemple, la signature). */ + if (!mp3_id3v1_hdr_test(hdr) && !feof(h)) + infos->host.file_info.mp3.eof -= sizeof(hdr); return 0; } + + /* Structure AVI : structure vide. */ + else if (infos->host.type == AVI_COMPRESSED || infos->host.type == AVI_UNCOMPRESSED) + return 0; /* Format non reconnu => erreur. */ else return 1; @@ -348,13 +380,13 @@ int stegx_choose_algo(info_s * infos, algo_e algo_choosen) return stegx_errno = ERR_SUGG_ALGOS, 1; /* Si l'utilisateur n'a pas choisi de mot de passe, on en crée un par défaut aléatoirement. */ if (infos->method == STEGX_WITHOUT_PASSWD) { - srand(time(NULL)); + stegx_srand(time(NULL)); free(infos->passwd); if (!(infos->passwd = calloc((LENGTH_DEFAULT_PASSWD + 1), sizeof(char)))) return perror("Can't allocate memory for password string"), 1; // Génération de symboles ASCII >= 32 et <= 126. for (int i = 0; i < LENGTH_DEFAULT_PASSWD; i++) - infos->passwd[i] = 32 + (rand() % 95); + infos->passwd[i] = 32 + (stegx_rand() % 95); } assert(algo_choosen >= STEGX_ALGO_LSB && algo_choosen < STEGX_NB_ALGO); diff --git a/test/lib/algo/test_eof.c b/test/lib/algo/test_eof.c index 4b083bf..a4a4366 100644 --- a/test/lib/algo/test_eof.c +++ b/test/lib/algo/test_eof.c @@ -9,6 +9,8 @@ #include "stegx.h" #include "common.h" +#define TEST_DIR "../../../env/test/" + #define SIG_RIFF 0x46464952 /* @@ -1282,6 +1284,129 @@ void test_eof_big_flv_without_passwd(void **state) remove("./WAVE_PCM(ALAW_16)_Mono_44,1kHz_2.wav"); } +/* Test d'un EOF sur deux MP3 avec et sans mot de passe. */ +void test_eof_mp3(void **state) +{ + /* Initialisation. */ + (void) state; /* Unused. */ + stegx_choices_s choices_insert, choices_extract; + stegx_info_insert_s choices_insert_info; + info_s * infos_insert = NULL, * infos_extract = NULL; + int eof = 0, size = 0; + FILE * fstego = NULL, * forig = NULL; + FILE * fsrc = NULL, * fdst = NULL; + + /* Fichier en ID3v1. */ + + /* Initialisation insertion. */ + choices_insert.host_path = TEST_DIR"mp3/MP3_ID3v1_Stereo_44,1kHz_160kbps.mp3"; + choices_insert.res_path = "test_eof_mp3"; + choices_insert.passwd = "stegx"; + choices_insert.mode = STEGX_MODE_INSERT; + choices_insert.insert_info = &choices_insert_info; + choices_insert.insert_info->hidden_path = TEST_DIR"others/short.txt"; + choices_insert.insert_info->algo = STEGX_ALGO_EOF; + /* Insertion. */ + assert_non_null(infos_insert = stegx_init(&choices_insert)); + assert_false(stegx_check_compatibility(infos_insert)); + assert_false(stegx_suggest_algo(infos_insert)); + assert_false(stegx_choose_algo(infos_insert, choices_insert.insert_info->algo)); + assert_false(stegx_insert(infos_insert)); + eof = infos_insert->host.file_info.mp3.eof; + stegx_clear(infos_insert); + + /* Vérifie que le fichier stégo n'as pas été altéré. */ + assert_non_null(fstego = fopen("test_eof_mp3", "rb")); + assert_non_null(forig = fopen(TEST_DIR"mp3/MP3_ID3v1_Stereo_44,1kHz_160kbps.mp3", "rb")); + for (int i = 0, bsrc = 0, bdst = 0; i < eof; i++) + { + fread(&bdst, sizeof(uint8_t), 1, fstego); + fread(&bsrc, sizeof(uint8_t), 1, forig); + assert_int_equal(bsrc, bdst); + } + assert_false(fclose(fstego)), assert_false(fclose(forig)); + + /* Initialisation extraction. */ + choices_extract.host_path = "test_eof_mp3"; + choices_extract.res_path = "./"; + choices_extract.passwd = "stegx"; + choices_extract.mode = STEGX_MODE_EXTRACT; + choices_extract.insert_info = NULL; + /* Extraction. */ + assert_non_null(infos_extract = stegx_init(&choices_extract)); + assert_false(stegx_check_compatibility(infos_extract)); + assert_false(stegx_detect_algo(infos_extract)); + assert_false(stegx_extract(infos_extract, choices_extract.res_path)); + stegx_clear(infos_extract); + + /* Comparaison du fichier caché original avec le fichier extrait. */ + size = 0; + assert_non_null(fsrc = fopen(TEST_DIR"others/short.txt", "rb")); + assert_non_null(fdst = fopen("short.txt", "rb")); + for (uint8_t bsrc, bdst; fread(&bdst, sizeof(bdst), 1, fdst); size++) + fread(&bsrc, sizeof(bsrc), 1, fsrc), assert_int_equal(bsrc, bdst); + assert_int_equal(size, 27); + + /* Nettoyage. */ + assert_false(remove("test_eof_mp3")), assert_false(remove("short.txt")); + assert_false(fclose(fsrc)); assert_false(fclose(fdst)); + + /* Fichier en ID3v2.3. */ + + /* Initialisation insertion. */ + choices_insert.host_path = TEST_DIR"mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_1.mp3"; + choices_insert.res_path = "test_eof_mp3"; + choices_insert.passwd = NULL; + choices_insert.mode = STEGX_MODE_INSERT; + choices_insert.insert_info = &choices_insert_info; + choices_insert.insert_info->hidden_path = TEST_DIR"bmp/test9bis.bmp"; + choices_insert.insert_info->algo = STEGX_ALGO_EOF; + /* Insertion. */ + assert_non_null(infos_insert = stegx_init(&choices_insert)); + assert_false(stegx_check_compatibility(infos_insert)); + assert_false(stegx_suggest_algo(infos_insert)); + assert_false(stegx_choose_algo(infos_insert, choices_insert.insert_info->algo)); + assert_false(stegx_insert(infos_insert)); + eof = infos_insert->host.file_info.mp3.eof; + stegx_clear(infos_insert); + + /* Vérifie que le fichier stégo n'as pas été altéré. */ + assert_non_null(fstego = fopen("test_eof_mp3", "rb")); + assert_non_null(forig = fopen(TEST_DIR"mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_1.mp3", "rb")); + for (int i = 0, bsrc = 0, bdst = 0; i < eof; i++) + { + fread(&bdst, sizeof(uint8_t), 1, fstego); + fread(&bsrc, sizeof(uint8_t), 1, forig); + assert_int_equal(bsrc, bdst); + } + assert_false(fclose(fstego)), assert_false(fclose(forig)); + + /* Initialisation extraction. */ + choices_extract.host_path = "test_eof_mp3"; + choices_extract.res_path = "./"; + choices_extract.passwd = NULL; + choices_extract.mode = STEGX_MODE_EXTRACT; + choices_extract.insert_info = NULL; + /* Extraction. */ + assert_non_null(infos_extract = stegx_init(&choices_extract)); + assert_false(stegx_check_compatibility(infos_extract)); + assert_false(stegx_detect_algo(infos_extract)); + assert_false(stegx_extract(infos_extract, choices_extract.res_path)); + stegx_clear(infos_extract); + + /* Comparaison du fichier caché original avec le fichier extrait. */ + size = 0; + assert_non_null(fsrc = fopen(TEST_DIR"bmp/test9bis.bmp", "rb")); + assert_non_null(fdst = fopen("test9bis.bmp", "rb")); + for (uint8_t bsrc, bdst; fread(&bdst, sizeof(bdst), 1, fdst); size++) + fread(&bsrc, sizeof(bsrc), 1, fsrc), assert_int_equal(bsrc, bdst); + assert_int_equal(size, 35906); + + /* Nettoyage. */ + assert_false(remove("test_eof_mp3")), assert_false(remove("test9bis.bmp")); + assert_false(fclose(fsrc)); assert_false(fclose(fdst)); +} + /* Structure CMocka contenant la liste des tests. */ const struct CMUnitTest eof_tests[] = { cmocka_unit_test(test_eof_little_bmp_with_passwd), @@ -1301,6 +1426,8 @@ const struct CMUnitTest eof_tests[] = { cmocka_unit_test(test_eof_big_wav_without_passwd), cmocka_unit_test(test_eof_big_flv_with_passwd), cmocka_unit_test(test_eof_big_flv_without_passwd), + + cmocka_unit_test(test_eof_mp3) }; int main(void) diff --git a/test/lib/algo/test_lsb.c b/test/lib/algo/test_lsb.c index d1e24fa..f301152 100644 --- a/test/lib/algo/test_lsb.c +++ b/test/lib/algo/test_lsb.c @@ -10,6 +10,8 @@ #include "common.h" #include "algo/lsb.h" +#define TEST_DIR "../../../env/test/" + /* * Fonction recréée pour libérer la mémoire de la structure stegx_choices_s **/ @@ -354,113 +356,149 @@ void test_lsb_big_bmp_without_passwd(void **state) remove("./short.txt"); } -/* Test sur l'algorithme LSB insertion lorsque le nombre d'octets à cacher - * correspond au nombre d'octets disponible pour l'insertion LSB - **/ -void test_protection_lsb_insert_pixels_egal_data(void **state) +/* Test d'un LSB sur deux MP3 avec et sans mot de passe. */ +void test_lsb_mp3(void **state) { - (void)state; - uint8_t *pixels = malloc(8 * sizeof(uint8_t)); - pixels[0] = pixels[1] = pixels[2] = pixels[3] = pixels[4] = pixels[5] = pixels[6] = pixels[7] = - 255; - uint8_t *data = malloc(2 * sizeof(uint8_t)); - data[0] = 100; - data[1] = 231; - assert_int_equal(protect_data_lsb(pixels, 8, data, 2, "stegx", STEGX_MODE_INSERT), 0); - - // ordre aleatoire : 4,1,7,0,2,3,6,5 (stegx) - assert_int_equal(pixels[0], 252); - assert_int_equal(pixels[1], 254); - assert_int_equal(pixels[2], 255); - assert_int_equal(pixels[3], 254); - assert_int_equal(pixels[4], 253); - assert_int_equal(pixels[5], 255); - assert_int_equal(pixels[6], 253); - assert_int_equal(pixels[7], 253); - free(data); - free(pixels); -} + /* Initialisation. */ + (void) state; /* Unused. */ + stegx_choices_s choices_insert, choices_extract; + stegx_info_insert_s choices_insert_info; + info_s * infos_insert = NULL, * infos_extract = NULL; + FILE * fstego = NULL, * forig = NULL; + FILE * fsrc = NULL, * fdst = NULL; + uint32_t size = 0, src32 = 0, dst32 = 0; + + /* Fichier en ID3v1 avec mot de passe. */ + + /* Initialisation insertion. */ + choices_insert.host_path = TEST_DIR"mp3/MP3_ID3v1_Stereo_44,1kHz_160kbps.mp3"; + choices_insert.res_path = "test_lsb_mp3"; + choices_insert.passwd = "stegx"; + choices_insert.mode = STEGX_MODE_INSERT; + choices_insert.insert_info = &choices_insert_info; + choices_insert.insert_info->hidden_path = TEST_DIR"others/short.txt"; + choices_insert.insert_info->algo = STEGX_ALGO_LSB; + /* Insertion. */ + assert_non_null(infos_insert = stegx_init(&choices_insert)); + assert_false(stegx_check_compatibility(infos_insert)); + assert_false(stegx_suggest_algo(infos_insert)); + assert_false(stegx_choose_algo(infos_insert, choices_insert.insert_info->algo)); + assert_false(stegx_insert(infos_insert)); + stegx_clear(infos_insert); -/* Test sur l'algorithme LSB extraction lorsque le nombre d'octets à cacher - * correspond au nombre d'octets disponible pour l'extraction LSB - **/ -void test_protection_lsb_extract_pixels_egal_data(void **state) -{ - (void)state; - uint8_t *pixels = malloc(8 * sizeof(uint8_t)); - pixels[0] = 252; - pixels[1] = 254; - pixels[2] = 255; - pixels[3] = 254; - pixels[4] = 253; - pixels[5] = 255; - pixels[6] = 253; - pixels[7] = 253; - uint8_t *data = malloc(2 * sizeof(uint8_t)); - data[0] = data[1] = 0; - - protect_data_lsb(pixels, 8, data, 2, "stegx", STEGX_MODE_EXTRACT); - assert_int_equal(data[0], 100); - assert_int_equal(data[1], 231); - free(pixels); - free(data); -} + /* Vérifie que le fichier stégo n'as pas été altéré. */ + assert_non_null(fstego = fopen("test_lsb_mp3", "rb")); + assert_non_null(forig = fopen(TEST_DIR"mp3/MP3_ID3v1_Stereo_44,1kHz_160kbps.mp3", "rb")); + /* On prend quelques échantillons représentatif pour vérifier que le fichier n'as pas été + * altéré. */ + dst32 = src32 = 0; + /* Échantillon d'un header MPEG. */ + assert_false(fseek(fstego, 0x20A, SEEK_SET)), assert_false(fseek(forig, 0x20A, SEEK_SET)); + assert_true(fread(&dst32, sizeof(uint16_t), 1, fstego)), assert_true(fread(&src32, sizeof(uint16_t), 1, forig)); + assert_int_equal(dst32, src32); + /* Échantillon d'une frame MPEG. */ + assert_false(fseek(fstego, 0x100F, SEEK_SET)), assert_false(fseek(forig, 0x100F, SEEK_SET)); + assert_true(fread(&dst32, sizeof(dst32), 1, fstego)), assert_true(fread(&src32, sizeof(dst32), 1, forig)); + assert_int_equal(dst32, src32); + /* Échantillon d'un tag ID3v1. */ + assert_false(fseek(fstego, 0x3A023, SEEK_SET)), assert_false(fseek(forig, 0x3A023, SEEK_SET)); + assert_true(fread(&dst32, sizeof(dst32), 1, fstego)), assert_true(fread(&src32, sizeof(dst32), 1, forig)); + assert_int_equal(dst32, src32); + /* Fermeture. */ + assert_false(fclose(fstego)), assert_false(fclose(forig)); + + /* Initialisation extraction. */ + choices_extract.host_path = "test_lsb_mp3"; + choices_extract.res_path = "./"; + choices_extract.passwd = "stegx"; + choices_extract.mode = STEGX_MODE_EXTRACT; + choices_extract.insert_info = NULL; + /* Extraction. */ + assert_non_null(infos_extract = stegx_init(&choices_extract)); + assert_false(stegx_check_compatibility(infos_extract)); + assert_false(stegx_detect_algo(infos_extract)); + assert_false(stegx_extract(infos_extract, choices_extract.res_path)); + stegx_clear(infos_extract); -/* Test sur l'algorithme LSB insertion lorsque le nombre d'octets à cacher - * est inférieur au nombre d'octets disponible pour l'insertion LSB - **/ -void test_protection_lsb_insert_pixels_sup_data(void **state) -{ - (void)state; - uint8_t *pixels = malloc(10 * sizeof(uint8_t)); - pixels[0] = pixels[1] = pixels[2] = pixels[3] = pixels[4] = pixels[5] = pixels[6] = pixels[7] = - pixels[8] = pixels[9] = 255; - uint8_t *data = malloc(2 * sizeof(uint8_t)); - data[0] = 100; - data[1] = 231; - assert_int_equal(protect_data_lsb(pixels, 8, data, 2, "stegx", STEGX_MODE_INSERT), 0); - - // ordre aleatoire : 4,1,7,0,2,3,6,5 (stegx) - assert_int_equal(pixels[0], 252); - assert_int_equal(pixels[1], 254); - assert_int_equal(pixels[2], 255); - assert_int_equal(pixels[3], 254); - assert_int_equal(pixels[4], 253); - assert_int_equal(pixels[5], 255); - assert_int_equal(pixels[6], 253); - assert_int_equal(pixels[7], 253); - assert_int_equal(pixels[8], 255); // non modifié - assert_int_equal(pixels[9], 255); // non modifié - - free(data); - free(pixels); -} + /* Comparaison du fichier caché original avec le fichier extrait. */ + size = 0; + assert_non_null(fsrc = fopen(TEST_DIR"others/short.txt", "rb")); + assert_non_null(fdst = fopen("short.txt", "rb")); + for (uint8_t bsrc, bdst; fread(&bdst, sizeof(bdst), 1, fdst); size++) + fread(&bsrc, sizeof(bsrc), 1, fsrc), assert_int_equal(bsrc, bdst); + assert_int_equal(size, 27); + + /* Nettoyage. */ + assert_false(remove("test_lsb_mp3")), assert_false(remove("short.txt")); + assert_false(fclose(fsrc)); assert_false(fclose(fdst)); + + /* Fichier en ID3v2.3 sans mot de passe. */ + + /* Initialisation insertion. */ + choices_insert.host_path = TEST_DIR"mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_1.mp3"; + choices_insert.res_path = "test_lsb_mp3"; + choices_insert.passwd = NULL; + choices_insert.mode = STEGX_MODE_INSERT; + choices_insert.insert_info = &choices_insert_info; + choices_insert.insert_info->hidden_path = TEST_DIR"others/test16.txt"; + choices_insert.insert_info->algo = STEGX_ALGO_LSB; + /* Insertion. */ + assert_non_null(infos_insert = stegx_init(&choices_insert)); + assert_false(stegx_check_compatibility(infos_insert)); + assert_false(stegx_suggest_algo(infos_insert)); + assert_false(stegx_choose_algo(infos_insert, choices_insert.insert_info->algo)); + assert_false(stegx_insert(infos_insert)); + stegx_clear(infos_insert); -/* Test sur l'algorithme LSB extraction lorsque le nombre d'octets à cacher - * est inférieur au nombre d'octets disponible pour l'extraction LSB - **/ -void test_protection_lsb_extract_pixels_sup_data(void **state) -{ - (void)state; - uint8_t *pixels = malloc(8 * sizeof(uint8_t)); - pixels[0] = 252; - pixels[1] = 254; - pixels[2] = 255; - pixels[3] = 254; - pixels[4] = 253; - pixels[5] = 255; - pixels[6] = 253; - pixels[7] = 253; - pixels[8] = 255; - pixels[9] = 255; - uint8_t *data = malloc(2 * sizeof(uint8_t)); - data[0] = data[1] = 0; - - protect_data_lsb(pixels, 8, data, 2, "stegx", STEGX_MODE_EXTRACT); - assert_int_equal(data[0], 100); - assert_int_equal(data[1], 231); - free(pixels); - free(data); + /* Vérifie que le fichier stégo n'as pas été altéré. */ + assert_non_null(fstego = fopen("test_lsb_mp3", "rb")); + assert_non_null(forig = fopen(TEST_DIR"mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_1.mp3", "rb")); + /* On prend quelques échantillons représentatif pour vérifier que le fichier n'as pas été + * altéré. */ + dst32 = src32 = 0; + /* Échantillon d'un header MPEG. */ + assert_false(fseek(fstego, 0x395, SEEK_SET)), assert_false(fseek(forig, 0x395, SEEK_SET)); + assert_true(fread(&dst32, sizeof(uint16_t), 1, fstego)), assert_true(fread(&src32, sizeof(uint16_t), 1, forig)); + assert_int_equal(dst32, src32); + /* Échantillon d'un tag ID3v2.3. */ + assert_false(fseek(fstego, 0x93, SEEK_SET)), assert_false(fseek(forig, 0x93, SEEK_SET)); + assert_true(fread(&dst32, sizeof(dst32), 1, fstego)), assert_true(fread(&src32, sizeof(dst32), 1, forig)); + assert_int_equal(dst32, src32); + /* Échantillon d'une frame MPEG. */ + assert_false(fseek(fstego, 0x1995, SEEK_SET)), assert_false(fseek(forig, 0x1995, SEEK_SET)); + assert_true(fread(&dst32, sizeof(dst32), 1, fstego)), assert_true(fread(&src32, sizeof(dst32), 1, forig)); + assert_int_equal(dst32, src32); + /* Échantillon d'un tag ID3v1. */ + assert_false(fseek(fstego, 0x4947C0, SEEK_SET)), assert_false(fseek(forig, 0X4947C0, SEEK_SET)); + assert_true(fread(&dst32, sizeof(dst32), 1, fstego)), assert_true(fread(&src32, sizeof(dst32), 1, forig)); + assert_int_equal(dst32, src32); + /* Fermeture. */ + assert_false(fclose(fstego)), assert_false(fclose(forig)); + + /* Initialisation extraction. */ + choices_extract.host_path = "test_lsb_mp3"; + choices_extract.res_path = "./"; + choices_extract.passwd = NULL; + choices_extract.mode = STEGX_MODE_EXTRACT; + choices_extract.insert_info = NULL; + /* Extraction. */ + assert_non_null(infos_extract = stegx_init(&choices_extract)); + assert_false(stegx_check_compatibility(infos_extract)); + assert_false(stegx_detect_algo(infos_extract)); + assert_false(stegx_extract(infos_extract, choices_extract.res_path)); + stegx_clear(infos_extract); + + /* Comparaison du fichier caché original avec le fichier extrait. */ + size = 0; + assert_non_null(fsrc = fopen(TEST_DIR"others/test16.txt", "rb")); + assert_non_null(fdst = fopen("test16.txt", "rb")); + for (uint8_t bsrc, bdst; fread(&bdst, sizeof(bdst), 1, fdst); size++) + fread(&bsrc, sizeof(bsrc), 1, fsrc), assert_int_equal(bsrc, bdst); + assert_int_equal(size, 14); + + /* Nettoyage. */ + assert_false(remove("test_lsb_mp3")), assert_false(remove("test16.txt")); + assert_false(fclose(fsrc)); assert_false(fclose(fdst)); } /* Structure CMocka contenant la liste des tests. */ @@ -469,10 +507,7 @@ const struct CMUnitTest lsb_tests[] = { cmocka_unit_test(test_lsb_little_bmp_without_passwd), cmocka_unit_test(test_lsb_big_bmp_with_passwd), cmocka_unit_test(test_lsb_big_bmp_without_passwd), - cmocka_unit_test(test_protection_lsb_insert_pixels_egal_data), - cmocka_unit_test(test_protection_lsb_extract_pixels_egal_data), - cmocka_unit_test(test_protection_lsb_insert_pixels_sup_data), - cmocka_unit_test(test_protection_lsb_extract_pixels_sup_data), + cmocka_unit_test(test_lsb_mp3) }; int main(void) diff --git a/test/lib/file_type/test_mp3.c b/test/lib/file_type/test_mp3.c index bbe9337..9a4621c 100644 --- a/test/lib/file_type/test_mp3.c +++ b/test/lib/file_type/test_mp3.c @@ -7,49 +7,192 @@ #include #include "stegx.h" #include "common.h" +#include "endian.h" +#include "file_type/mp3.h" -/* - * Teste la vérification du format MP3. - * */ +#define TEST_DIR "../../../env/test/" -void test_file_mp3__wave_pcm_alaw(void **state) +/* Test la détection d'une frame de MP3. */ +void test_mp3_mpeg_hdr_test(void **state) { - (void)state; - FILE *f = fopen("../../../env/test/wave/WAVE_PCM(ALAW_16)_Mono_44,1kHz_1.wav", "r"); - assert_non_null(f), assert_int_equal(stegx_test_file_mp3(f), UNKNOWN); - fclose(f); + (void)state; /* Unused */ + assert_true(mp3_mpeg_hdr_test(0xFFFBA060)); + assert_false(mp3_mpeg_hdr_test(0xFBFBA060)); + assert_false(mp3_mpeg_hdr_test(0xFFEBA060)); } -void test_file_mp3__wave_pcm_s16le(void **state) +/* Test le saut d'une frame MPEG 1/2 Layer III. */ +void test_mp3_mpeg_fr_seek(void **state) { - (void)state; - FILE *f = fopen("../../../env/test/wave/WAVE_PCM(S16_LE)_Stereo_44,1kHz.wav", "r"); - assert_non_null(f), assert_int_equal(stegx_test_file_mp3(f), UNKNOWN); + /* Initialisation. */ + (void)state; /* Unused */ + uint32_t hdr; + FILE * f = fopen(TEST_DIR"mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_1.mp3", "rb"); + assert_non_null(f); + + /* Déplacement sur le header, lecture du header puis saut du tag. */ + assert_false(fseek(f, 0x395, SEEK_SET)); + assert_int_equal(fread(&hdr, sizeof(hdr), 1, f), 1); + assert_false(mp3_mpeg_fr_seek(stegx_be32toh(hdr), f)); + + /* Comparaison de la position réelle avec la position attendue. */ + assert_int_equal(ftell(f), 0x6D9); + /* Nettoyage. */ fclose(f); } -void test_file_mp3__mp3_mono(void **state) +/* Test d'écriture d'une frame de MP3. */ +void test_mp3_mpeg_fr_write(void **state) { - (void)state; - FILE *f = fopen("../../../env/test/mp3/MP3_Mono_44,1kHz_64kbps.mp3", "r"); - assert_non_null(f), assert_int_equal(stegx_test_file_mp3(f), MP3); + /* Initialisation. */ + (void)state; /* Unused */ + uint32_t hdr, size; + uint8_t bsrc, bdst; + FILE * fsrc = fopen(TEST_DIR"mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_1.mp3", "rb"); + FILE * fdst = fopen("test_mp3_mpeg_fr_write", "w+b"); + assert_non_null(fsrc), assert_non_null(fdst); + + /* Déplacement sur le header. */ + assert_false(fseek(fsrc, 0x395, SEEK_SET)); + /* Lecture puis écriture du header. */ + assert_int_equal(fread(&hdr, sizeof(hdr), 1, fsrc), 1); + assert_int_equal(fwrite(&hdr, sizeof(hdr), 1, fdst), 1); + /* Écriture du tag. */ + assert_false(mp3_mpeg_fr_write(stegx_be32toh(hdr), fsrc, fdst)); + + /* Comparaison du tag original avec tout le fichier resultant, qui ne + * devrait que contenir le tag. */ + assert_false(fseek(fsrc, 0x395, SEEK_SET)); + assert_false(fseek(fdst, 0, SEEK_SET)); + for (size = 0; fread(&bdst, sizeof(bdst), 1, fdst); size++) + fread(&bsrc, sizeof(bsrc), 1, fsrc), assert_int_equal(bsrc, bdst); + assert_int_equal(size, 836); + + /* Nettoyage. */ + assert_false(remove("test_mp3_mpeg_fr_write")); + fclose(fsrc), fclose(fdst); +} + +/* Test de la détection de la première frame MPEG 1/2 Layer III. */ +void test_mp3_mpeg_fr_find_first(void **state) +{ + /* Initialisation. */ + (void)state; /* Unused */ + FILE * f = NULL; + + /* Test sur tout types différents de MP3. */ + f = fopen(TEST_DIR"mp3/MP3_ID3v1_Stereo_44,1kHz_160kbps.mp3", "rb"); + assert_non_null(f), assert_int_equal(mp3_mpeg_fr_find_first(f), 0x0); + assert_false(fclose(f)); + f = fopen(TEST_DIR"mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_1.mp3", "rb"); + assert_non_null(f), assert_int_equal(mp3_mpeg_fr_find_first(f), 0x395); + assert_false(fclose(f)); + f = fopen(TEST_DIR"mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_2.mp3", "rb"); + assert_non_null(f), assert_int_equal(mp3_mpeg_fr_find_first(f), 0x8e5); + assert_false(fclose(f)); + f = fopen(TEST_DIR"mp3/MP3_ID3v2.4_Mono_44,1kHz_64kbps.mp3", "rb"); + assert_non_null(f), assert_int_equal(mp3_mpeg_fr_find_first(f), 0x2d); + assert_false(fclose(f)); + f = fopen(TEST_DIR"mp3/MP3_ID3v2.4_Unsync_Stereo_44,1kHz_192kbps.mp3", "rb"); + assert_non_null(f), assert_int_equal(mp3_mpeg_fr_find_first(f), 0x8f5); + assert_false(fclose(f)); +} + +/* Test la détection d'un tag ID3v1. */ +void test_mp3_id3v1_hdr_test(void **state) +{ + (void)state; /* Unused */ + assert_true(mp3_id3v1_hdr_test(0x54414734)); + assert_false(mp3_id3v1_hdr_test(0x54413447)); +} + +/* Test le saut d'un tag ID3v1. */ +void test_mp3_id3v1_tag_seek(void **state) +{ + /* Initialisation. */ + (void)state; /* Unused */ + uint32_t hdr; + FILE * f = fopen(TEST_DIR"mp3/MP3_ID3v1_Stereo_44,1kHz_160kbps.mp3", "rb"); + assert_non_null(f); + + /* Déplacement sur le header, lecture du header puis saut du tag. */ + assert_false(fseek(f, 0x00039FC6, SEEK_SET)); + assert_int_equal(fread(&hdr, sizeof(hdr), 1, f), 1); + assert_false(mp3_id3v1_tag_seek(f)); + + /* Comparaison de la position réelle avec la position attendue. */ + assert_int_equal(ftell(f), 0x0003A046); + /* Nettoyage. */ fclose(f); } -void test_file_mp3__mp3_stereo(void **state) +/* Test l'écriture d'un tag ID3v1. */ +void test_mp3_id3v1_tag_write(void **state) +{ + /* Initialisation. */ + (void)state; /* Unused */ + uint32_t hdr, size; + uint8_t bsrc, bdst; + FILE * fsrc = fopen(TEST_DIR"mp3/MP3_ID3v1_Stereo_44,1kHz_160kbps.mp3", "rb"); + FILE * fdst = fopen("test_mp3_id3v1_tag_write", "w+b"); + assert_non_null(fsrc), assert_non_null(fdst); + + /* Déplacement sur le header. */ + assert_false(fseek(fsrc, 0x00039FC6, SEEK_SET)); + /* Lecture puis écriture du header. */ + assert_int_equal(fread(&hdr, sizeof(hdr), 1, fsrc), 1); + assert_int_equal(fwrite(&hdr, sizeof(hdr), 1, fdst), 1); + /* Écriture du tag. */ + assert_false(mp3_id3v1_tag_write(fsrc, fdst)); + + /* Comparaison du tag original avec tout le fichier resultant, qui ne + * devrait que contenir le tag. */ + assert_false(fseek(fsrc, 0x00039FC6, SEEK_SET)); + assert_false(fseek(fdst, 0, SEEK_SET)); + for (size = 0; fread(&bdst, sizeof(bdst), 1, fdst); size++) + fread(&bsrc, sizeof(bsrc), 1, fsrc), assert_int_equal(bsrc, bdst); + assert_int_equal(size, 128); + + /* Nettoyage. */ + assert_false(remove("test_mp3_id3v1_tag_write")); + fclose(fsrc), fclose(fdst); +} + +/* Test la vérification du format MP3. */ +void test_stegx_test_file_mp3(void **state) { - (void)state; - FILE *f = fopen("../../../env/test/mp3/MP3_Stereo_44,1kHz_160kbps.mp3", "r"); + /* Initialisation. */ + (void)state; /* Unused */ + FILE * f = NULL; + + /* Test tout les types de fichier MP3 et certains autres formats. */ + f = fopen(TEST_DIR"wave/WAVE_PCM(ALAW_16)_Mono_44,1kHz_1.wav", "r"); + assert_non_null(f), assert_int_equal(stegx_test_file_mp3(f), UNKNOWN); + assert_false(fclose(f)); + f = fopen(TEST_DIR"bmp/test1.bmp", "r"); + assert_non_null(f), assert_int_equal(stegx_test_file_mp3(f), UNKNOWN); + assert_false(fclose(f)); + f = fopen(TEST_DIR"mp3/MP3_ID3v1_Stereo_44,1kHz_160kbps.mp3", "r"); assert_non_null(f), assert_int_equal(stegx_test_file_mp3(f), MP3); - fclose(f); + assert_false(fclose(f)); + f = fopen(TEST_DIR"mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_1.mp3", "r"); + assert_non_null(f), assert_int_equal(stegx_test_file_mp3(f), MP3); + assert_false(fclose(f)); + f = fopen(TEST_DIR"mp3/MP3_ID3v2.4_Mono_44,1kHz_64kbps.mp3", "r"); + assert_non_null(f), assert_int_equal(stegx_test_file_mp3(f), MP3); + assert_false(fclose(f)); } -/* Structure CMocka contenant la liste des tests. */ +/* Liste des tests. */ const struct CMUnitTest mp3_tests[] = { - cmocka_unit_test(test_file_mp3__wave_pcm_alaw), - cmocka_unit_test(test_file_mp3__wave_pcm_s16le), - cmocka_unit_test(test_file_mp3__mp3_mono), - cmocka_unit_test(test_file_mp3__mp3_stereo) + cmocka_unit_test(test_mp3_mpeg_hdr_test), + cmocka_unit_test(test_mp3_mpeg_fr_seek), + cmocka_unit_test(test_mp3_mpeg_fr_write), + cmocka_unit_test(test_mp3_mpeg_fr_find_first), + cmocka_unit_test(test_mp3_id3v1_hdr_test), + cmocka_unit_test(test_mp3_id3v1_tag_seek), + cmocka_unit_test(test_mp3_id3v1_tag_write), + cmocka_unit_test(test_stegx_test_file_mp3) }; int main(void) diff --git a/test/lib/test_check_compa.c b/test/lib/test_check_compa.c index 6fdea2b..84d8c61 100644 --- a/test/lib/test_check_compa.c +++ b/test/lib/test_check_compa.c @@ -102,7 +102,7 @@ void test_file_compa_v4(void **state) void test_file_compa_v5(void **state) { info_s *infos = *state; - infos->host.host = fopen("../../../env/test/mp3/MP3_Mono_44,1kHz_64kbps.mp3", "r"), + infos->host.host = fopen("../../../env/test/mp3/MP3_ID3v2.4_Mono_44,1kHz_64kbps.mp3", "r"), assert_non_null(infos->host.host); assert_int_equal(stegx_check_compatibility(infos), 0); diff --git a/test/lib/test_init.c b/test/lib/test_init.c index a1bc994..e3fa82d 100644 --- a/test/lib/test_init.c +++ b/test/lib/test_init.c @@ -82,7 +82,7 @@ void test_insert_init_with_passwd() strcpy(choices->passwd, "stegx"); choices->mode = STEGX_MODE_INSERT; choices->insert_info = malloc(sizeof(stegx_info_insert_s)); - choices->insert_info->hidden_path = "stdin"; + choices->insert_info->hidden_path = "../../../env/test/others/short.txt"; choices->insert_info->algo = STEGX_ALGO_EOF; info_s *infos = stegx_init(choices); @@ -96,15 +96,12 @@ void test_insert_init_with_passwd() test = (infos->host.host != NULL); assert_int_equal(test, 1); - // Teste si le fichier a cacher a bien ete ouvert - assert_ptr_equal(infos->hidden, stdin); - // Teste si le fichier resultat a bien ete cree test = (infos->res != NULL); assert_int_equal(test, 1); // Test que le nom du fichier à cacher à bien été découpé. */ - assert_string_equal(infos->hidden_name, "stdin"); + assert_string_equal(infos->hidden_name, "short.txt"); // Teste si le mot de passe a bien ete initialise test = (strcmp(infos->passwd, "stegx") == 0); @@ -215,8 +212,8 @@ void test_extract_init_without_passwd() void test_extract_init_with_passwd() { stegx_choices_s *choices = malloc(sizeof(stegx_choices_s)); - choices->host_path = malloc((strlen("stdin") + 1) * sizeof(char)); - strcpy(choices->host_path, "stdin"); + choices->host_path = malloc((strlen("../../../env/test/others/short.txt") + 1) * sizeof(char)); + strcpy(choices->host_path, "../../../env/test/others/short.txt"); choices->res_path = malloc((strlen("./") + 1) * sizeof(char)); strcpy(choices->res_path, "./"); choices->passwd = malloc((strlen("stegx") + 1) * sizeof(char)); @@ -232,9 +229,6 @@ void test_extract_init_with_passwd() test = (infos->mode == STEGX_MODE_EXTRACT); assert_int_equal(test, 1); - // Teste si le fichier host a bien ete ouvert - assert_ptr_equal(infos->host.host, stdin); - // Teste si le fichier a cacher a bien ete ouvert test = (infos->hidden == NULL); assert_int_equal(test, 1); diff --git a/test/lib/test_sugg_algo.c b/test/lib/test_sugg_algo.c index bdee7d3..cd2d1ee 100644 --- a/test/lib/test_sugg_algo.c +++ b/test/lib/test_sugg_algo.c @@ -9,6 +9,8 @@ #include "stegx.h" #include "common.h" +#define TEST_DIR "../../../env/test/" + /* Setup des tests unitaires pour les formats de fichiers */ static int test_file_info__setup(void **state) { @@ -261,7 +263,85 @@ static void test_file_info_wav__pcm_s16le(void **state) fclose(infos->host.host); } -/* +/** + * Tests MP3 + * ============================================================================= + */ + +static void test_file_info_mp3(void **state) +{ + /* Initialisation. */ + info_s *infos = *state; + infos->host.type = MP3; + + /* Test sur tout types différents de MP3. */ + + /* MP3 ID3v1. */ + infos->host.host = fopen(TEST_DIR"mp3/MP3_ID3v1_Stereo_44,1kHz_160kbps.mp3", "rb"); + assert_non_null(infos->host.host), stegx_suggest_algo(infos); + assert_int_equal(infos->host.file_info.mp3.fr_frst_adr, 0x0); + assert_int_equal(infos->host.file_info.mp3.fr_nb, 455); + assert_int_equal(infos->host.file_info.mp3.eof, 0x3A046); + assert_false(fclose(infos->host.host)); + + /* MP3 ID3v1 avec données à la fin. */ + infos->host.host = fopen(TEST_DIR"mp3/MP3_ID3v1_Stereo_44,1kHz_160kbps_EOF.mp3", "rb"); + assert_non_null(infos->host.host), stegx_suggest_algo(infos); + assert_int_equal(infos->host.file_info.mp3.fr_frst_adr, 0x0); + assert_int_equal(infos->host.file_info.mp3.fr_nb, 455); + assert_int_equal(infos->host.file_info.mp3.eof, 0x3A046); + assert_false(fclose(infos->host.host)); + + /* MP3 ID3v2.3 n°1. */ + infos->host.host = fopen(TEST_DIR"mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_1.mp3", "rb"); + assert_non_null(infos->host.host), stegx_suggest_algo(infos); + assert_int_equal(infos->host.file_info.mp3.fr_frst_adr, 0x395); + assert_int_equal(infos->host.file_info.mp3.fr_nb, 5744); + assert_int_equal(infos->host.file_info.mp3.eof, 0x494800); + assert_false(fclose(infos->host.host)); + + /* MP3 ID3v2.3 n°1 avec données à la fin. */ + infos->host.host = fopen(TEST_DIR"mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_1_EOF.mp3", "rb"); + assert_non_null(infos->host.host), stegx_suggest_algo(infos); + assert_int_equal(infos->host.file_info.mp3.fr_frst_adr, 0x395); + assert_int_equal(infos->host.file_info.mp3.fr_nb, 5744); + assert_int_equal(infos->host.file_info.mp3.eof, 0x494800); + assert_false(fclose(infos->host.host)); + + /* MP3 ID3v2.3 n°2. */ + infos->host.host = fopen(TEST_DIR"mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_2.mp3", "rb"); + assert_non_null(infos->host.host), stegx_suggest_algo(infos); + assert_int_equal(infos->host.file_info.mp3.fr_frst_adr, 0x8E5); + assert_int_equal(infos->host.file_info.mp3.fr_nb, 6928); + assert_int_equal(infos->host.file_info.mp3.eof, 0x58676E); + assert_false(fclose(infos->host.host)); + + /* MP3 ID3v2.4 n°1. */ + infos->host.host = fopen(TEST_DIR"mp3/MP3_ID3v2.4_Mono_44,1kHz_64kbps.mp3", "rb"); + assert_non_null(infos->host.host), stegx_suggest_algo(infos); + assert_int_equal(infos->host.file_info.mp3.fr_frst_adr, 0x2D); + assert_int_equal(infos->host.file_info.mp3.fr_nb, 94); + assert_int_equal(infos->host.file_info.mp3.eof, 0x4CE9); + assert_false(fclose(infos->host.host)); + + /* MP3 ID3v2.4 n°2. */ + infos->host.host = fopen(TEST_DIR"mp3/MP3_ID3v2.4_Unsync_Stereo_44,1kHz_192kbps.mp3", "rb"); + assert_non_null(infos->host.host), stegx_suggest_algo(infos); + assert_int_equal(infos->host.file_info.mp3.fr_frst_adr, 0x8F5); + assert_int_equal(infos->host.file_info.mp3.fr_nb, 2209); + assert_int_equal(infos->host.file_info.mp3.eof, 0x152AC0); + assert_false(fclose(infos->host.host)); + + /* MP3 ID3v2.4 n°2 avec des données à la fin. */ + infos->host.host = fopen(TEST_DIR"mp3/MP3_ID3v2.4_Unsync_Stereo_44,1kHz_192kbps_EOF.mp3", "rb"); + assert_non_null(infos->host.host), stegx_suggest_algo(infos); + assert_int_equal(infos->host.file_info.mp3.fr_frst_adr, 0x8F5); + assert_int_equal(infos->host.file_info.mp3.fr_nb, 2209); + assert_int_equal(infos->host.file_info.mp3.eof, 0x152AC0); + assert_false(fclose(infos->host.host)); +} + +/** * Tests généraux * ============================================================================= */ @@ -430,6 +510,37 @@ void test_propos_algos_v4(void **state) fclose(infos->hidden); } +/* Test sur du MP3. */ +void test_propos_algos_v5(void **state) +{ + info_s *infos = *state; + infos->host.type = MP3; + infos->host.host = fopen(TEST_DIR"mp3/MP3_ID3v2.3_Stereo_44,1kHz_256kbps_2.mp3", "rb"); + assert_non_null(infos->host.host); + + /* Valeurs à trouver */ + infos->hidden = fopen(TEST_DIR"others/short.txt", "rb"), assert_non_null(infos->hidden); + stegx_suggest_algo(infos); + assert_int_equal(stegx_propos_algos[STEGX_ALGO_LSB], 1); + assert_int_equal(stegx_propos_algos[STEGX_ALGO_EOF], 1); + assert_int_equal(stegx_propos_algos[STEGX_ALGO_METADATA], 0); + assert_int_equal(stegx_propos_algos[STEGX_ALGO_EOC], 0); + assert_int_equal(stegx_propos_algos[STEGX_ALGO_JUNK_CHUNK], 0); + assert_false(fclose(infos->hidden)); + + /* Valeurs à trouver */ + infos->hidden = fopen(TEST_DIR"bmp/test4.bmp", "rb"), assert_non_null(infos->hidden); + stegx_suggest_algo(infos); + assert_int_equal(stegx_propos_algos[STEGX_ALGO_LSB], 0); + assert_int_equal(stegx_propos_algos[STEGX_ALGO_EOF], 1); + assert_int_equal(stegx_propos_algos[STEGX_ALGO_METADATA], 0); + assert_int_equal(stegx_propos_algos[STEGX_ALGO_EOC], 0); + assert_int_equal(stegx_propos_algos[STEGX_ALGO_JUNK_CHUNK], 0); + assert_false(fclose(infos->hidden)); + + assert_false(fclose(infos->host.host)); +} + /* Test la taille par défaut du mot de passe lorsque la méthode sans * mot de passe a été choisit par l'utilisateur. */ void test_passwd_default_length(void **state) @@ -480,7 +591,8 @@ int main(void) cmocka_unit_test(test_propos_algos_v1), cmocka_unit_test(test_propos_algos_v2), cmocka_unit_test(test_propos_algos_v3), - cmocka_unit_test(test_propos_algos_v4) + cmocka_unit_test(test_propos_algos_v4), + cmocka_unit_test(test_propos_algos_v5) }; /* Liste des tests de chaque format. */ @@ -496,6 +608,8 @@ int main(void) cmocka_unit_test(test_file_info_wav__pcm_alaw_1), cmocka_unit_test(test_file_info_wav__pcm_alaw_2), cmocka_unit_test(test_file_info_wav__pcm_s16le), + //Liste des tests pour le MP3 + cmocka_unit_test(test_file_info_mp3), //Liste des tests pour le FLV cmocka_unit_test(test_file_info_flv_v1), cmocka_unit_test(test_file_info_flv_v2)