Een byte bestaat uit 8 bits. De bits aan de linkerkant hebben de grootste impact op de waarde van een byte, en dus spreken we van de meest significante bits. De bits aan de rechterkant worden de minst significante bits genoemd omdat ze slechts een kleine bijdrage leveren aan de waarde van byte.
Een afbeelding bestaat uit een rooster van pixels, waarvan de kleur gevormd wordt door de combinatie van vier kleurkanalen: rood, groen, blauw en alfa (RGBA). Het alfakanaal geeft aan hoe doorzichtig de pixel is. Per kleurkanaal wordt 1 byte gebruikt om aan te geven hoe sterk het kanaal aanwezig is in de ontbinding van de kleur. Daardoor gebruikt elke pixel in totaal 4 bytes.
Door de minst significante bits van elk kleurkanaal een klein beetje aan te passen, kan een geheime boodschap in een afbeelding verborgen worden die voor het blote oog onzichtbaar blijft. We hebben deze techniek toegepast om geheime boodschappen in PNG-afbeeldingen te verbergen en dagen je uit om een bash shell script te schrijven waarmee de geheime boodschappen uit de PNG-afbeeldingen terug kunnen uitgelezen worden.
Een PNG-afbeelding bevat een hoofding met metadata over de afbeelding. Daarna volgt een beschrijving van het rooster met de kleurwaarden van alle pixels, dat wordt opgeslagen na compressie met een lossless algoritme1. Omdat we de invididuele kleurkanalen van de pixels in een afbeelding willen aanpassen, moeten we het rooster kunnen extraheren en decomprimeren.
Daarvoor gebruiken we ImageMagick2, een open source bibliotheek die bestaat uit verschillende Unix commando's voor het weergeven, converteren en bewerken van digitale afbeeldingen (zowel rasterafbeeldingen als vectorafbeeldingen). De bibliotheek bevat onder andere het commando convert dat als volgt kan gebruikt worden om het rooster van een PNG-afbeelding om te zetten naar RGBA-formaat3:
$ convert afbeelding.png afbeelding.rgba
Met het volgende commando kan je ImageMagick lokaal installeren onder Ubuntu:
$ sudo apt install imagemagick
Een bestand in RGBA-formaat gebruikt traditioneel de extensie .rgba. De bytes van het bestand corresponderen met de opeenvolgende RGBA-kleurkanalen van het PNG-bestand. Voor een PNG-afbeelding bestaande uit 4 pixels levert dat bijvoorbeeld het volgende RGBA-bestand op. Elk getal in de voorstelling van het bestand komt overeen met de decimale voorstelling van 1 byte (waarde tussen 0 en 255).
Schrijf een bash shell script decodeer waaraan een PNG-afbeelding moet doorgegeven worden. In de PNG-afbeelding werd een geheime boodschap verborgen door de twee minst significante bits van de RGBA-kleurkanalen van een aantal pixels aan het begin van het rooster aan te passen. De combinatie van de 2 minst significante bits van de vier RGBA-kleurkanalen van één pixel vormt telkens 1 byte die de ASCII-waarde van één karakter van de geheime boodschap voorstelt. Op die manier coderen de opeenvolgende pixels voor de opeenvolgende karakters van de geheime boodschap. Een NULL-byte (decimale waarde 0) is de codering die het einde van de geheime boodschap aangeeft.
Het shell script moet de geheime boodschap die verborgen zit in de gegeven PNG-afbeelding uitschrijven naar stdout. Hiervoor moet het shell script dus de PNG-afbeelding omzetten naar RGBA-formaat, de twee minst significante bits van elke byte extraheren, de geëxtraheerde bits van vier opeenvolgende bytes (kleurkanalen van één pixel) samenvoegen tot één byte met de ASCII-waarde van het volgende karakter van de geheime boodschap. Dit moet herhaald worden tot alle pixels verwerkt werden, of tot er een NULL-byte gedecodeerd werd die het einde van de boodschap aangeeft. Alle bestanden die door het shell script aangemaakt worden, moeten finaal ook terug verwijderd worden.
In de editor hieronder hebben we reeds twee shell-functies geïmplementeerd die je kan gebruiken bij de implementatie van het shell script:
decToChar: zet een decimaal getal om naar het bijhorende ASCII-karakter
$ decToChar 65 a $ decToChar 44 ,
readDecimal: leest 4 opeenvolgende bytes uit een gegeven bestand; extraheert de 2 minst significante bits van elke byte, voegt die samen tot 1 byte en geeft de decimale waarde van die byte terug; deze functie kan je dus gebruiken om de decimale waarde van het volgende gecodeerde karakter uit te lezen uit het bestand
$ readDecimal 0 verbeelding.rgba 76 $ readDecimal 104 verbeelding.rgba 0
Onderstaande voorbeeldsessie geeft aan hoe het shell script decodeer moet kunnen gebruikt worden. Hierbij gaan we ervan uit dat de PNG-afbeeldingen verbeelding.png4 en city_and_sun.png5 zich in de huidige directory bevinden.
$ decodeer verbeelding.png Logica brengt je van A naar B. Verbeelding brengt je overal. $ decodeer city_and_sun.png De voornaamste kwaal van de mens is zijn tomeloze nieuwsgierigheid naar dingen die hij niet kan doorgronden.