Steganografi, skjult informasjon i et bilde

Denne posten er del 7 av 7 i serien Bildebehandling med python i vgs

Om noen ønsker å kommunisere hemmelig, kan kryptering være en god løsning. Men det vil ofte være mulig for andre å oppdage for eksempel antallet ganger en kommuniserer, meldingenes lengde eller tidspunktene tar kontakt. Om derimot ingenting tyder på at to parter kommuniserer, kan det gå uoppdaget. I TV-serien Le Bureau laster spioner opp bilder av valper til et forum, men i bildene er det informasjon skjult med steganografi, gresk for «skjult skrift».

Bilder i totallsystemet

En enkel måte å representere et bilde på, kan være å ha en matrise med intensitetsinformasjon for hvert punkt i bildet, hver piksel. Verdiene for intensiteten kan lagres som et heltall, for et fargebilde er det en verdi for hver fargekanal. I dette enkle eksempelet har hver piksel en verdi mellom 0 og 255, verdiene kan representeres med 8 bits i totallsystemet.

Om verdien i en piksel er 164, vil det i totallsystemet bli representert som 10100100. Hvis den høyeste (lengst til venstre) biten blir endret til 0, vil tallverdien bli 36. En så stor endring vil kunne være synlig i bildet. Hvis derimot den laveste (lengst til høyre) biten blir endret til 1, blir verdien bare endret til 165, en endring som vil være vanskelig å merke om bildet har mye detaljer.

Øverst til venstre vises bare informasjonen fra den høyeste biten. Så følger de neste bitene, mindre og mindre av den store strukturen i bildet er synlig. Den åttende biten ser nesten tilfeldig ut.

Dermed kan vi gjøre om de laveste bitsene i et bilde til annen informasjon, for eksempel legge inn tekst eller et annet bilde der.

Tekst representert som tall

Først må en gjøre om informasjonen som skal lagres til en liste eller tekststreng som inneholder den binære informasjonen. Ulike tekstkodinger som ASCII, ISO 8859-1, utf-8 og slikt åpner en svær Pandoras eske, men for norsk tekst går det som regel bra selv om de særnorske vokalene kan lage problemer.

>>> ord('b')
98
>>> ord('å')
229
>>> chr(88)
'X'

I python kan en bruke kommandoen bin for å få gjort om et heltall i titallsystemet til totallsystemet, men da får en ikke med ledende 0. format er enklere å bruke. I parameteren "08b" betyr 0 at ledende 0 skal være med, i inntil 8 siffer og det skal bli binært.

>>> bin(65)
'0b1000001'
>>> format(65, "08b")
'01000001'         

Steganografi med bit-endringer

De to funksjonene under virker på et bilde lagret som en numpy-matrise, for eksempel lastet inn med hjelpekommandoen lesBilde. Ulike bibliotek vil gjøre det enklere og raskere å konvertere mellom tekst og binære verdier, men selve konverteringen er et godt eksempel på algoritmisk tenkning.

def skrivSteganografi(tekst, bilde):
    bits = ""
    for tegn in tekst:
        bits += format(ord(tegn), "08b")
        # Teksten blir gjort om til en streng av 0 og 1
  
    x, y = k.shape
    for i in range(len(bits)):
        rad, kol = divmod(int(i), x)
        # Heltallsdivisjon avgjør hvilken rad, resten er kolonnenummeret
        bilde[rad, kol] = (bilde[rad, kol] & 0b11111110) | int(bits[int(i)])
    return bilde

For å sette verdien av den siste biten, blir først den siste biten satt til 0. Bitvis «og» (&) med sju 1 beholder verdiene i de sju første bitene, men «og» med 0 blir alltid 0. Bitvis «eller» (|) mellom 0 og en annen verdi blir alltid den samme verdien.

Å lese ut verdiene igjen krever at en finner og bare leser de laveste bitene. Jeg har brukt to hjelpefunksjoner, lignende løkker som over kan en også bruke.

def hentSteganografi(bilde, lengde):
    minste = np.bitwise_and(k, 0b00000001)
    minste = minste.flatten()
    # To innebygde numpy-funksjoner: henter ut siste bit og
    # gjør matrisen endimensjonal.|

    tekst = ""
    byte = ""
    for i in range(lengde):
        byte = "".join([str(j) for j in minste[8i:8i+8]])
        # Åtte og åtte tegn leses inn og gjøres om til en streng
        tekst += chr(int(byte, 2))
    return tekst

Denne måten å lagre informasjon på er veldig lett å oppdage. Symbolene i tekst har et veldig begrenset antall mulige verdier (for de fleste europeiske språk) og kan for eksempel avsløres ved at de fleste har 010 som de tre første bitene. Dersom teksten blir kryptert eller komprimert først, vil det ikke være åpenbare mønster. Målet er først og fremst at ingen ser grunn til å lete.

For fargebilder kan en bruke samme metode fordelt på de tre fargekanalene. Bildeformat som jpeg, som ikke lagrer pikslene i matriser, kan en også gjøre lignende med. Computerphile har en video med eksempler på mer avansert steganografi.

Oppgaveforslag

  • Sjekk hvilken tekst som er lagret i bildet til over til høyre.
  • Tilpass funksjonen slik at den ikke prøver å lagre en melding som er for lang for bildet.
  • Gjør om funksjonene slik at en ikke trenger å gjette på lengden av meldingen.
  • Gjør om funksjonene slik at informasjonen blir lagret i de to laveste bitene og sammenlign hvor lett det er å oppdage at noe er uvanlig.
  • Lag et program som tar inn navnet på en bildefil og en tekstfil, og lagrer tekstfilen i bildet.

Serienavigasjon<< Å variere lysstyrken til bilder med numpy

Underviser i matematikk, fysikk og naturfag på Tryggheim vgs.