Każdy dziś wie, a przynajmniej powinien wiedzieć, jak ważną rzeczą w czasach powszechnej inwigilacji jest sprawa zachowania prywatności. W systemach linuksowych mamy dostępny cały szereg bezpłatnych narzędzi dających możliwość zabezpieczenia wrażliwych danych. Jedne z tych narzędzi są bardziej popularne inne mniej. Chciałbym dziś powiedzieć o pakiecie stosunkowo rzadko wykorzystywanym w domowej kryptografii. A szkoda, ponieważ jego użycie jest bardzo proste i efektywne. Mam tu na myśli Image Magic – popularny pakiet do obróbki grafiki. Mało kto wie, że Image Magic ma też możliwość szyfrowania plików graficznych, przy czym robi to w taki sposób, że szyfrowane są wyłącznie piksele zawierające obraz, natomiast wszystkie metadane pozostają nienaruszone. Stwarza to unikalną możliwość rozpoznawania zaszyfrowanych plików przez programy graficzne nadal jako pliki graficzne. Zazwyczaj podczas szyfrowania oryginalna wersja pliku zostaje zastąpiona jego zaszyfrowaną wersją.
Jak sprawdzić, czy mamy zainstalowany pakiet Image Magick (dalej IM)? Najprościej otworzyć terminal i wpisać zaklęcie:
convert -version
Jeżeli IM jest zainstalowany pojawi się informacja o zainstalowanej wersji programu:
$ convert -version
Version: ImageMagick 6.9.11-60 Q16 x86_64 2021-01-25 https://imagemagick.org
Copyright: (C) 1999-2021 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC Modules OpenMP(4.5)
Delegates (built-in): bzlib djvu fftw fontconfig freetype heic jbig jng jp2 jpeg lcms lqr ltdl lzma openexr pangocairo png tiff webp wmf x xml zlib
Jeżeli IM nie jest zainstalowany otrzymamy informację o błędzie. Jak zainstalować IM w przypadku, gdy nie jest zainstalowany? Na linuksach wywodzących się od Debiana najprostszy sposób to zaklęcie:
sudo apt install imagemagick
po czym postępujemy zgodnie ze scenariuszem instalatora.
Do szyfrowania plików graficznych użyjemy polecenia convert z parametrem -encipher. Należy pamiętać, że niektóre formaty graficzne – na przykład jpg i gif – nie obsługują szyfrowania pikseli, dlatego plik wyjściowy musimy wtedy zapisać w formacie obsługującym takie szyfrowanie: na przykład w formacie png. Jeżeli o tym zapomnimy zaszyfrowany obraz utracimy na zawsze. Czy format pliku, który chcemy zaszyfrować obsługuje szyfrowanie pikseli najlepiej przekonać się w drodze eksperymentu: szyfrując kopię tego pliku. Jeżeli po zaszyfrowaniu i odszyfrowaniu otrzymamy oryginalny plik wejściowy wnioskujemy, że dany format jest obsługiwany.
Przejdźmy do ćwiczeń.
Po uruchomieniu terminala, żeby nie podawać za każdym razem pełnej ścieżki do pliku przechodzimy do katalogu z plikami do zaszyfrowania. W moim przypadku katalog nazywa się ImageMagick i jest podkatalogiem folderu /Obrazy
cd Obrazy/ImageMagick
Żeby się upewnić, że znajdujemy się we właściwym katalogu wylistujmy pliki:
$ ls
backup img1.jpg img1.png img2.jpg img2.png monalisa.jpeg
Jak widać mamy tu 5 plików graficznych: 2 z rozszerzeniem png, 2 z rozszerzeniem jpg i 1 z rozszerzeniem jpeg. Czym się różni format jpg od formatu zapisywanego z rozszerzeniem jpeg ? Niczym: to ten sam format.
Do zaszyfrowania pliku z obrazem IM wymaga podania hasła. Hasłem może być dowolna kombinacja liter i symboli (co najmniej 12 znaków) zapisana w pliku tekstowym. Jako hasło możemy też wykorzystać jeden z plików graficznych, które pozostaną niezaszyfrowane. To ciekawa alternatywa dla hasła w postaci ciągu znaków. Jak przystało na użytkowników linuksa plik tekstowy z hasłem utworzymy przy pomocy komendy cat i zapiszemy go w bieżącym katalogu jako info.txt
cat > info.txt
29.12.2022: pożyczyłem Mirkowi 230 zł.
^D (Ctrl + D)
Skąd takie dziwne hasło ? Dobre hasło tak właśnie powinno wyglądać. Nawet, jeżeli ktoś uzyska do niego dostęp jest bardzo małe prawdopodobieństwo, że zorientuje się, że ma przed sobą nasze super tajne hasło.
Plik z hasłem pojawił się w naszym katalogu roboczym: możemy zaczynać.
Tak wygląda składnia zaklęcia, z którego będziemy korzystali: convert nazwa_pliku_do zaszyfrowania -encipher plik_z_hasłem nazwa_pliku_po_zaszyfrowaniu
convert img1.png -encipher info.txt img1.png
Jak widać podgląd pliku w systemie uległ zmianie. Zamiast grafiki pojawił się nieuporządkowany szum. Plik jest rozpoznawany przez programy graficzne jako plik graficzny, ale jedyne do czego mamy dostęp to metadane pliku. Notabene metadane można bez problemu usunąć korzystając z innej komendy pakietu IM.
Spróbujmy teraz odszyfrować nasz plik. Składnia zaklęcia pozostaje ta sama, zamieniamy tylko parametr -encipher na -decipher.
convert img1.png -decipher info.txt img1.png
Rewelacja. Odzyskaliśmy plik wejściowy. Spróbujmy teraz jako hasło wykorzystać plik monalisa.jpeg
convert img1.png -encipher monalisa.jpeg img1.png
Jak widać na podglądzie plik img1.png ponownie uległ zaszyfrowaniu. Wygląda na to, że nasze szyfrowanie działa perfekcyjnie.
Spróbujmy odzyskać plik.
convert img1.png -decipher monalisa.jpeg img1.png
I znowu sukces. Mamy plik w pierwotnej postaci.
Jak wcześniej wspomniałem, niektóre formaty plików nie mają możliwości szyfrowania pikseli. Do takich formatów należy jpg. Co się stanie, gdy zaszyfrujemy taki plik nie zmieniając przy tym formatu pliku wyjściowego ?
convert img1.jpg -encipher info.txt img1.jpg
Wgląda na to, że plik został zaszyfrowany. Na podglądzie pojawił się graficzny szum.
To teraz w drugą stronę.
convert img1.jpg -decipher info.txt img1.jpg
I tu nas spotyka niespodzianka. Co prawda coś pozostało z pliku pierwotnego, ale wyraźnie został on uszkodzony i jeżeli nie pozostawiliśmy sobie kopii zapasowej utraciliśmy plik bezpowrotnie. Ja oczywiście zostawiłem sobie taką kopię, zaraz ją przywrócę.
Jak szyfrować formaty, które nie obsługują szyfrowania pikseli ? Wystarczy plik wyjściowy zapisać w formacie obsługującym takie szyfrowanie: najprościej w formacie png. Image Magick sam przekonwertuje plik wejściowy do nowego formatu na podstawie rozszerzenia w nazwie pliku wyjściowego. Wróćmy do poprzedniego przykładu.
convert img1.jpg -encipher info.txt img1.jpg.png
Jak widać powstał nowy, zaszyfrowany plik img1.jpg.png; oryginalny plik img1.jpg pozostał nienaruszony.
Spróbujmy odszyfrować img1.jpg.png, ale wcześniej – dla przejrzystości – usuniemy plik img1.jpg (tak naprawdę to nie musimy tego robić, oryginalny plik zostanie nadpisany)
convert img1.jpg.png -decipher info.txt img1.jpg
Sukces. Odzyskaliśmy nasz pierwotny plik. Podsumowując: nie wszystkie formaty graficzne obsługują szyfrowanie pikseli, dlatego zanim zdecydujemy się na zaszyfrowanie pliku w formacie, o którym nie wiemy, czy obsługuje takie szyfrowanie konieczne jest przeprowadzenie testu. Dopiero po uzyskaniu pozytywnego wyniku testu szyfrujemy oryginalny plik, nie pozostawiając żadnych jego kopii zapasowych.
Uwaga: przy szyfrowaniu i odszyfrowywaniu pliku ze zmianą formatu może się zdarzyć, że system nie odświeży przechowywanej w cache’u miniatury pliku i będziemy widzieli na niej fałszywy obraz utworzony dla poprzedniej wersji pliku. Najlepiej otworzyć wówczas plik w programie graficznym, gdzie zobaczymy faktyczny jego stan: czy plik jest zaszyfrowany, czy nie jest zaszyfrowany. Miniatury plików można odświeżyć przy pomocy komendy,
rm ~/.cache/thumbnails/normal/*
która usunie wszystkie stare miniatury plików graficznych i wymusi na systemie utworzenie nowych miniatur.
Szyfrowanie pojedynczych plików jest pasjonujące, ale niezbyt praktyczne. Zazwyczaj mamy do zaszyfrowania dziesiątki a nawet setki plików. Szyfrowanie kilkuset plików graficznych jeden po drugim z pewnością mocno nadszarpnęłoby nasze zdrowie psychiczne i zachwiało naszą wiarę w to, że wybraliśmy właściwą metodę szyfrowania. Na szczęście zaszyfrowanie nawet 1000 plików jest zadaniem niezwykle prostym. Wystarczy, że zastosujemy nasze zaklęcie w pętli. Wygląda ono teraz tak:
for f in *.png; do convert "$f" -encipher info.txt "$f"; done
Jak widać wszystkie pliki z rozszerzeniem .png zostały zaszyfrowane.
Jak to działa? Początkowa pętla tworzy listę plików z rozszerzeniem png znajdujących się w bieżącym katalogu. Następnie nazwa każdego z tych plików przekazywana jest w postaci zmiennej „$f” do polecenia convert, które ma postać dokładnie taką samą jak w przypadku szyfrowania pojedynczego pliku. Plik wyjściowy jest zapisywany pod tą samą nazwą zakodowaną w zmiennej „$f”. Zaszyfrowanie pliku kończy sekwencję działań na tym pliku i pętla pobiera kolejny plik z listy.
W identyczny sposób rozszyfrowujemy grupę plików.
for f in *.png; do convert "$f" -decipher info.txt "$f"; done
Parametr -encipher zamieniliśmy parametrem -decipher, cała reszta zaklęcia pozostała bez zmian.
Zaszyfrujmy teraz wszystkie pliki z rozszerzeniem jpg, a jako hasło wskażemy plik monalisa.jpeg. Pamiętamy, że musimy zmienić rozszerzenie pliku wyjściowego na png.
for f in *.jpg; do convert "$f" -encipher monalisa.jpeg "$f".png; rm "$f"; done
Jak widać do nazwy pliku pobranego w pętli dodaliśmy rozszerzenie png. W ten sposób zaszyfrowane pliki w końcówce nazwy mają 2 rozszerzenia .jpg.png. Dzięki temu zachowana została informacja, że oryginalne pliki były w formacie jpg.
for f in *.jpg.png; do convert "$f" -decipher monalisa.jpeg "${f//.png/}"; rm "$f"; done
Zaklęcie na odszyfrowanie zadziałało zgodnie z oczekiwaniami. Tajemniczy kawałek „${f//.png/}” po prostu tworzy nazwę pliku wyjściowego wyrzucając z nazwy, którą otrzymuje w zmiennej „$f” podciąg znaków „.png”. W rezultacie w nazwie pozostaje tylko rozszerzenie „.jpg”, czyli dokładnie takie, jakie miały oryginalne pliki.
Na zakończenie, przed zamknięciem terminala – obowiązkowo – należy wyczyścić historię wprowadzanych komend zapamiętaną dla bieżącej sesji (znajduje się w nich informacja o naszych „hasłach” użytych do szyfrowania). W tym celu wykonujemy komendę „history -c”.
img1.jpg > Carl Larsson; „Kwiaty w oknie”, 1894 r.
img2.jpg > Carl Larsson;
img1.png > Owe Zege; „Malte Schildt, 13 lat”, 1925 r.
img2.png > Owe Zerge;
monalisa.jpeg > Leonardo da Vinci; Mona Lisa, 1503 r.