« Stéganographie "BPC" » : différence entre les versions

De Wiki du LAMA (UMR 5127)
Aller à la navigation Aller à la recherche
Aucun résumé des modifications
Aucun résumé des modifications
Ligne 16 : Ligne 16 :
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.<br>
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.<br>
===Exemples===
===Exemples===
===Code===
<code>
rom PIL import Image

def cache(img_name: str, msg: str, nb_bits: int = 1, nom_img_fin: str = "SECRETFIN_BYTE2.png"):
if not (1 <= nb_bits <= 4):
print("Le nombre de bits par couleur doit être entre 1 et 4.")
return

# Ouvrir et convertir l'image en mode RGB :
image_ori = Image.open(img_name)
image_rgb = image_ori.convert("RGB") # Evite les problèmes liés à la transparence.
data_bytes = bytearray(image_rgb.tobytes())
# Encodage du message en binaire.
msg_bin = "".join(bin(ord(c))[2:].zfill(8) for c in msg)
msg_len = len(msg_bin)
msg_len_bin = bin(msg_len)[2:].zfill(16) # 16 bits pour représenter la taille du message
message_complet = msg_len_bin + msg_bin

# Vérification de la capacité
capacite = len(data_bytes) * nb_bits
if len(message_complet) > capacite:
print("Le message est trop long pour être encodé dans cette image avec " + str(nb_bits) + " bit(s) par couleur.")
return

# Encodage dans le bytearray.
bit_index = 0
for i in range(len(data_bytes)):
byte = data_bytes[i]
byte_bin = list(bin(byte)[2:].zfill(8)) # On s'assure que la chaîne fait 8 bits
for b in range(nb_bits):
if bit_index >= len(message_complet):
break
# Remplacer les bits les moins significatifs
byte_bin[-(b + 1)] = message_complet[bit_index]
bit_index += 1
data_bytes[i] = int("".join(byte_bin), 2)
if bit_index >= len(message_complet):
break

# Reconstruction de l'image.
img_fin = Image.frombytes("RGB", image_rgb.size, bytes(data_bytes))
img_fin.save(nom_img_fin)
print("Message encodé avec succès dans " + nom_img_fin + " avec " + str(nb_bits) + " bit(s) par couleur.")

def discover(img_name: str, nb_bits: int = 1):
if not (1 <= nb_bits <= 4):
print("Le nombre de bits par couleur doit être entre 1 et 4.")
return

# Ouvrir l'image.
image_ori = Image.open(img_name)
image_rgb = image_ori.convert("RGB")
data_bytes = bytearray(image_rgb.tobytes())

bits_lus = ""
taille_msg = None

for byte in data_bytes:
byte_bin = bin(byte)[2:].zfill(8)
for b in range(nb_bits):
bits_lus += byte_bin[-(b + 1)]

# Lecture des 16 premiers bits, qui contiennent la taille.
if len(bits_lus) == 16 and taille_msg is None:
taille_msg = int(bits_lus, 2)
total_bits = 16 + taille_msg

if taille_msg is not None and len(bits_lus) >= total_bits:
break
if taille_msg is not None and len(bits_lus) >= total_bits:
break

# Reconstruction du message.
message_bits = bits_lus[16:16 + taille_msg]
octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]
message = "".join(chr(int(o, 2)) for o in octets)

print("MESSAGE DÉCODÉ :", message)
return message
</code>
===Problèmes===
===Problèmes===
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.<br>
Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.<br>

Version du 12 mai 2025 à 04:56

Tuteur : Pierre Hyvernat
Elève : Hania Boudjaj

Présentation du projet

Histoire

"Ensemble de techniques permettant de transmettre une information en la dissimulant au sein d'une autre information (photo, vidéo, texte, etc.) sans rapport avec la première et le plus souvent anodine, essentiellement à l'aide de logiciels spécialisés."
On attribue la première mention de la stéganographie telle que définieci-dessus à l'historien grec Hérodote. Il décrit dans son ouvrage Historia, une tablette de bois gravée qu’on recouvre de cire.
Dans le cadre de ce projet, nous nous intéresserons à des algorithmes de stéganographie qui permettent de cacher du texte dans une image.

La stéganographie LSB

Principe de base

Une image est composée de pixels eux-même composés de trois octets. Chacun de ces octets correspond à une composante couleur du pixel, Red, Green et Blue. Chaque composante peut prendre une valeur allant de 0 à 255.
Ainsi, on peut modifier les n bits de poids faible d’un octet sans que cela soit visible à l'œil nu. Notons cependant que plus le poids du bit modifié augmente, plus la modification sera visible.
La stéganographie LSB permet donc de remplacer les n bits de poids faible des pixels d’une image par les bits d’une chaîne de caractère. On peut alors cacher un message dans une image.

Exemples

Code

rom PIL import Image

def cache(img_name: str, msg: str, nb_bits: int = 1, nom_img_fin: str = "SECRETFIN_BYTE2.png"):

   if not (1 <= nb_bits <= 4):
       print("Le nombre de bits par couleur doit être entre 1 et 4.")
       return
   # Ouvrir et convertir l'image en mode RGB :
   image_ori = Image.open(img_name)
   image_rgb = image_ori.convert("RGB") # Evite les problèmes liés à la transparence.
   data_bytes = bytearray(image_rgb.tobytes())
   
   # Encodage du message en binaire.
   msg_bin = "".join(bin(ord(c))[2:].zfill(8) for c in msg) 
   msg_len = len(msg_bin)
   msg_len_bin = bin(msg_len)[2:].zfill(16) # 16 bits pour représenter la taille du message
   message_complet = msg_len_bin + msg_bin
   # Vérification de la capacité
   capacite = len(data_bytes) * nb_bits
   if len(message_complet) > capacite:
       print("Le message est trop long pour être encodé dans cette image avec " + str(nb_bits) + " bit(s) par couleur.")
       return
   # Encodage dans le bytearray.
   bit_index = 0
   for i in range(len(data_bytes)):
       byte = data_bytes[i]
       byte_bin = list(bin(byte)[2:].zfill(8)) # On s'assure que la chaîne fait 8 bits
       for b in range(nb_bits):
           if bit_index >= len(message_complet):
               break
           # Remplacer les bits les moins significatifs
           byte_bin[-(b + 1)] = message_complet[bit_index] 
           bit_index += 1
       data_bytes[i] = int("".join(byte_bin), 2)
       if bit_index >= len(message_complet):
           break
   # Reconstruction de l'image.
   img_fin = Image.frombytes("RGB", image_rgb.size, bytes(data_bytes))
   img_fin.save(nom_img_fin)
   print("Message encodé avec succès dans " + nom_img_fin + " avec " + str(nb_bits) + " bit(s) par couleur.")

def discover(img_name: str, nb_bits: int = 1):

   if not (1 <= nb_bits <= 4):
       print("Le nombre de bits par couleur doit être entre 1 et 4.")
       return
   # Ouvrir l'image.
   image_ori = Image.open(img_name)
   image_rgb = image_ori.convert("RGB")
   data_bytes = bytearray(image_rgb.tobytes())
   bits_lus = ""
   taille_msg = None
   for byte in data_bytes:
       byte_bin = bin(byte)[2:].zfill(8)
       for b in range(nb_bits):
           bits_lus += byte_bin[-(b + 1)]
           # Lecture des 16 premiers bits, qui contiennent la taille.
           if len(bits_lus) == 16 and taille_msg is None:
               taille_msg = int(bits_lus, 2)
               total_bits = 16 + taille_msg
           if taille_msg is not None and len(bits_lus) >= total_bits:
               break
       if taille_msg is not None and len(bits_lus) >= total_bits:
           break
   # Reconstruction du message.
   message_bits = bits_lus[16:16 + taille_msg]
   octets = [message_bits[i:i+8] for i in range(0, len(message_bits), 8)]
   message = "".join(chr(int(o, 2)) for o in octets)
   print("MESSAGE DÉCODÉ :", message)
   return message

Problèmes

Plus la taille du message à cacher est importante, plus le nombre de bit nécessaire à l’encodage augmente.
Or, comme mentionné plus tôt, plus le poids du bit modifié augmente, plus la modification sera visible.
Pour remédier à ce problème, nous pouvons nous tourner vers la Stéganographie BPC.

La stéganographie BPC

Principe de base

Contrairement à la stéganographie LSB, nous allons maintenant choisir dans quelles parties de l’image cacher notre message. Pour cela, nous allons nous intéresser à la complexité d’une image.

Soit une image composée de pixel RGB. Nous allons diviser cette image en blocs de 8*8 pixels. Pour décider si oui ou non nous cachons une partie du message dans le bloc actuel, nous allons calculer sa complexité. Pour cela, il suffit de compter le nombre de changement entre deux bits consécutifs en ligne et en colonnes? Plus le nombre de changements est élevé dans un bloc, plus il est complexe. Intervient alors un nouveau problème : lorsqu’on encode une partie du message, il y est possible que le bloc change soudainement de complexité, ce qui pose problème pour le décodage. Pour y remédier, nous effectuons l’opération arithmétique XOR entre le bloc et un damier. Cette méthode permet de faire passer la complexité d’un bloc de simple à complexe.

Exemples

Code