Стеганаграфія ў GIF

Увядзенне

Вітаю.
Не так даўно, калі вучыўся ва ўніверсітэце, была курсавая па дысцыпліне "Праграмныя метады абароны інфармацыі". Па заданні патрабавалася зрабіць праграму, якая ўкараняе паведамленне ў файлы фармату GIF. Вырашыў рабіць на Java.

У дадзеным артыкуле я апішу некаторыя тэарэтычныя моманты, а таксама, як стваралася гэтая невялікая праграма.

Тэарэтычная частка

Фармат GIF

GIF (англ.: Graphics Interchange Format — фармат для абмену выявамі) — фармат захоўвання графічных выяў, здольны захоўваць сціснутыя дадзеныя без страты якасці ў фармаце да 256 колераў. Дадзены фармат быў распрацаваны ў 1987 году (GIF87a) фірмай CompuServe для перадачы растравых малюнкаў па сетках. У 1989-м фармат быў мадыфікаваны (GIF89a), былі дададзены падтрымка празрыстасці і анімацыі.

Файлы фармату GIF маюць блокавую структуру. Дадзеныя блокі заўсёды маюць фіксаваную даўжыню (ці яна залежыць ад некаторых сцягоў), так што памыліцца ў тым, дзе які блок знаходзіцца, практычна немагчыма. Структура найпростага неаніміраванага GIF-малюнка фармату GIF89a:

Стеганаграфія ў GIF

З усіх блокаў структуры ў дадзеным выпадку нас будуць цікавіць блок глабальнай палітры і параметры, якія адказваюць за палітру:

  • CT - Наяўнасць глабальнай палітры. Калі гэты сцяг устаноўлены, то адразу пасля дэскрыптара лагічнага экрана павінна пачынацца глабальная палітра.
  • Size - Памер палітры і лік кветак карцінкі. Значэнні дадзенага параметру:

памер
Колькасць кветак
Памер палітры, байт

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Метады шыфравання

У якасці метадаў зашыфравання паведамленняў у файлах малюнкаў будуць выкарыстоўвацца:

  • Метад LSB (Least Significant Bit, найменшы значны біт)
  • Метад дапаўнення палітры

Метад LSB - распаўсюджаны метад стэганаграфіі. Ён заключаецца ў замене апошніх значных біт у кантэйнеры (у нашым выпадку байты глабальнай палітры) на біты хаваецца паведамлення.

У праграме будуць выкарыстоўвацца ў рамках гэтага метаду два апошнія біта ў байтах глабальнай палітры. Гэта азначае, што для 24-бітнага малюнка, дзе колер палітры ўяўляе сабой тры байта для чырвонага, сіняга, і зялёнага колераў, пасля ўкаранення паведамлення ў яго, кожны складнік колеру зменіцца максімум на 3/255 градацыі. Такая змена, па-першае, будзе незаўважна ці цяжкапрыкметна для чалавечага вока, а па-другое, не будзе адрозна на нізкаякасных прыладах высновы інфармацыі.

Колькасць інфармацыі будзе напрамую залежаць ад памеру палітры выявы. Паколькі максімальны памер палітры 256 колераў і, калі запісваць па двух біта паведамлення ў складнік кожнага колеру, то максімальная даўжыня паведамлення (пры максімальнай палітры ў малюнку) складае 192 байта. Пасля ўкаранення паведамлення ў малюнак, памер файла не змяняецца.

Метад пашырэння палітры, які працуе толькі для структуры GIF. Ён будзе найболей эфектыўны ў малюнках з палітрай невялікіх памераў. Сутнасць яго складаецца ў тым, што ён павялічвае памер палітры, тым самым даўшы дадатковую прастору для запісу неабходных байт на месцы байт кветак. Калі ўлічыць што мінімальны памер палітры складае 2 колеры (6 байт), то максімальны памер укараняецца паведамлення можа быць 256×3–6=762 ​​байт. Недахоп - нізкая криптозащищенность, прачытаць укаранёнае паведамленне можна пры дапамозе любога тэкставага рэдактара, калі паведамленне не падвяргалася дадатковаму шыфраванню.

Практычная частка

Праектаванне праграмы

Усе неабходныя прылады для рэалізацыі алгарытмаў шыфравання і дэшыфраванні будуць знаходзіцца ў пакеце com.tsarik.steganography. Гэты пакет уключае ў сябе інтэрфейс Encryptor з метадамі encrypt и decrypt, клас Binary, які прадстаўляе магчымасць працы з масівамі бітаў, а таксама класы выключэнняў UnableToEncryptException и UnableToDecryptException, якія павінны быць выкарыстаны ў метадах інтэрфейсу Encryptor у выпадку памылак кадавання і дэкадавання адпаведна.

Асноўны пакет праграмы com.tsarik.programs.gifed будзе ўключаць у сябе які запускаецца клас праграмы са статычным метадам main, які дазваляе запускаць праграму; клас, які захоўвае ў сабе параметры праграмы; і пакеты з іншымі класамі.

Рэалізацыя непасрэдна саміх алгарытмаў будзе прадстаўлена ў пакеце com.tsarik.programs.gifed.gif класамі GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Абодва гэтых класа будуць рэалізаваць інтэрфейс Encryptor.

На аснове структуры фармату GIF можна скласці агульны алгарытм укаранення паведамлення ў палітру малюнка:

Стеганаграфія ў GIF

Для вызначэння прысутнасці паведамлення ў малюнку неабходна ў пачатак паведамлення дадаваць нейкую паслядоўнасць біт, якую дэшыфратар счытвае ў першую чаргу і правярае на карэктнасць. Калі яна не супадае, тое лічыцца, што ў малюнку няма ўтоенага паведамлення. Далей трэба ўказваць даўжыню паведамлення. Затым сам тэкст паведамлення.

Дыяграма класаў усяго прыкладання:

Стеганаграфія ў GIF

Рэалізацыя праграмы

Рэалізацыю ўсёй праграмы можна разбіць на дзве складнікі: рэалізацыя метадаў шыфравання і дэшыфраванні інтэрфейсу Encryptor, у класах GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod, і рэалізацыя інтэрфейсу карыстальніка.

Разгледзім клас GIFEncryptorByLSBMethod.

Стеганаграфія ў GIF

поля firstLSBit и secondLSBit утрымоўваюць нумары бітаў кожнага байта выявы, у якія павінна заносіцца і адкуль счытвацца паведамленне. Поле checkSequence захоўвае кантрольную паслядоўнасць біт для забеспячэння распазнання убудаванага паведамлення. Статычны метад getEncryptingFileParameters вяртае параметры паказанага файла і характарыстыкі патэнцыйнага паведамлення.

Алгарытм метаду encrypt класа GIFEncryptorByLSBMethod:

Стеганаграфія ў GIF

І яго код:

@Override
public void encrypt(File in, File out, String text) throws UnableToEncodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("Input file is null");
	}
	if (out == null) {
		throw new NullPointerException("Output file is null");
	}
	if (text == null) {
		throw new NullPointerException("Text is null");
	}
	
	// read bytes from input file
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// check format
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToEncodeException("Input file has wrong GIF format");
	}
	
	// read palette size property from first three bits in the 10-th byte from the file
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// calculate color count and possible message length
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2;// one byte for check and one byte for message length
	
	if (possibleTextLength < text.length()) {
		throw new UnableToEncodeException("Text is too big");
	}
	
	int n = 13;
	
	// write check sequence
	for (int i = 0; i < checkSequence.length/2; i++) {
		byte[] ba = Binary.toBitArray(bytes[n]);
		ba[firstLSBit] = checkSequence[2*i];
		ba[secondLSBit] = checkSequence[2*i+1];
		bytes[n] = Binary.toByte(ba);
		n++;
	}
	
	// write text length
	byte[] cl = Binary.toBitArray((byte)text.length());
	for (int i = 0; i < cl.length/2; i++) {
		byte[] ba = Binary.toBitArray(bytes[n]);
		ba[firstLSBit] = cl[2*i];
		ba[secondLSBit] = cl[2*i+1];
		bytes[n] = Binary.toByte(ba);
		n++;
	}
	
	// write message
	byte[] textBytes = text.getBytes();
	for (int i = 0; i < textBytes.length; i++) {
		byte[] c = Binary.toBitArray(textBytes[i]);
		for (int ci = 0; ci < c.length/2; ci++) {
			byte[] ba = Binary.toBitArray(bytes[n]);
			ba[firstLSBit] = c[2*ci];
			ba[secondLSBit] = c[2*ci+1];
			bytes[n] = Binary.toByte(ba);
			n++;
		}
	}
	
	// write output file
	OutputStream os = new FileOutputStream(out);
	os.write(bytes);
	os.close();
}

Алгарытм і зыходны код метаду decrypt класа GIFEncryptorByLSBMethod:

Стеганаграфія ў GIF

@Override
public String decrypt(File in) throws UnableToDecodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("Input file is null");
	}
	
	// read bytes from input file
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// check format
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToDecodeException("Input file has wrong GIF format");
	}
	
	// read palette size property from first three bits in the 10-th byte from the file
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// calculate color count and possible message length
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2;	// one byte for check and one byte for message length
	
	int n = 13;
	
	// read check sequence
	byte[] csBits = new byte[checkSequence.length];
	for (int i = 0; i < 4; i++) {
		byte[] ba = Binary.toBitArray(bytes[n]);
		csBits[2*i] = ba[firstLSBit];
		csBits[2*i+1] = ba[secondLSBit];
		n++;
	}
	byte cs = Binary.toByte(csBits);
	
	if (cs != Binary.toByte(checkSequence)) {
		throw new UnableToDecodeException("There is no encrypted message in the image (Check sequence is incorrect)");
	}
	
	// read text length
	byte[] cl = new byte[8];
	for (int i = 0; i < 4; i++) {
		byte[] ba = Binary.toBitArray(bytes[n]);
		cl[2*i] = ba[firstLSBit];
		cl[2*i+1] = ba[secondLSBit];
		n++;
	}
	byte textLength = Binary.toByte(cl);
	
	if (textLength < 0) {
		throw new UnableToDecodeException("Decoded text length is less than 0");
	}
	if (possibleTextLength < textLength) {
		throw new UnableToDecodeException("There is no messages (Decoded message length (" + textLength + ") is less than Possible message length (" + possibleTextLength + "))");
	}
	
	// read text bits and make text bytes
	byte[] bt = new byte[textLength];
	for (int i = 0; i < bt.length; i++) {
		byte[] bc = new byte[8];
		for (int bci = 0; bci < bc.length/2; bci++) {
			byte[] ba = Binary.toBitArray(bytes[n]);
			bc[2*bci] = ba[firstLSBit];
			bc[2*bci+1] = ba[secondLSBit];
			n++;
		}
		bt[i] = Binary.toByte(bc);
	}
	
	return new String(bt);
}

Рэалізацыя класа GIFEncryptorByPaletteExtensionMethod будзе аналагічнай, толькі адрозніваецца метад захавання/счытвання інфармацыі.

У класе MainFrame апісаны метады-«абгорткі»: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), якія апрацоўваюць вынікі метадаў інтэрфейсу Encryptor і якія ажыццяўляюць узаемадзеянне з карыстачом, т.е адчыняюць дыялог выбару файлаў, паказваюць паведамленні пра памылкі і г.д.; а таксама і іншыя метады: openImage(), які дае магчымасць карыстачу выбару выявы, exit(), Які ажыццяўляе выхад з прыкладання. Гэтыя метады выклікаюцца з Action'аў адпаведных пунктаў меню. У гэтым класе дадаткова рэалізаваны дапаможныя метады: createComponents() - стварэнне кампанентаў формы, loadImageFile(File f) — загрузка выявы ў спецыяльны кампанент з файла. Рэалізацыя класа GIFEncryptorByPaletteExtensionMethod аналагічная рэалізацыі класа GIFEncryptorByLSBMethod, асноўнае адрозненне складаецца ў спосабе запісу і чытанні байтаў паведамлення з палітры.

Праца праграмы

Метад LBS

Дапушчальны ёсць такі малюнак:

Стеганаграфія ў GIF

У дадзеным малюнку палітра складаецца з 256 колераў (так захоўвае Paint). Першыя чатыры колеры: белы, чорны, чырвоны, зялёны. Астатнія колеры - чорныя. Паслядоўнасць біт глабальнай палітры будзе наступная:

11111111 11111111 11111111 00000000 00000000 00000000 11111111 00000000 00000000 00000000 11111111 00000000...

Стеганаграфія ў GIF

Пасля ўкаранення паведамлення падкрэсленыя біты будуць заменены бітамі з паведамлення. Атрыманая выява амаль не адрозніваецца ад арыгінала.

Арыгінал
Выява з укаранёным паведамленнем

Стеганаграфія ў GIF
Стеганаграфія ў GIF

Метад пашырэння палітры

Адкрыўшы малюнак, у якое змешчана паведамленне па дадзеным метадзе, можна выявіцца такую ​​карціну:

Стеганаграфія ў GIF

Зразумелая справа, што для паўнавартаснай шпіёнскай дзейнасці такі метад не пайдзе, і патрабуе, можа, дадатковай шыфроўкі паведамлення.

Шыфраванне/дэшыфраванне ў анімаваных выявах працуе, як і ў звычайных статычных выявах, пры гэтым анімацыя не парушаецца.

Выкарыстоўваныя крыніцы:

Спампаваць:

Крыніца: habr.com

Дадаць каментар