Kveðjur.
Fyrir ekki svo löngu síðan, þegar ég var í háskólanámi, var námskeið í greininni „Hugbúnaðaraðferðir upplýsingaöryggis“. Verkefnið krafðist þess að við bjuggum til forrit sem fellir skilaboð í GIF skrár. Ég ákvað að gera það í Java.
Í þessari grein mun ég lýsa nokkrum fræðilegum atriðum, sem og hvernig þetta litla forrit varð til.
Bóklegur hluti
GIF snið
GIF (Graphics Interchange Format - snið til að skiptast á myndum) er snið til að geyma grafískar myndir, sem getur geymt þjöppuð gögn án þess að tapa gæðum á sniði sem er allt að 256 litir. Þetta snið var þróað árið 1987 (GIF87a) af CompuServe til að senda rastermyndir yfir netkerfi. Árið 1989 var sniðinu breytt (GIF89a), stuðningi við gagnsæi og hreyfimyndir bætt við.
GIF skrár hafa blokkbyggingu. Þessir kubbar hafa alltaf fasta lengd (eða það fer eftir einhverjum fánum), þannig að það er nánast ómögulegt að gera mistök um hvar hver kubbur er staðsettur. Uppbygging einföldustu GIF myndarinnar sem ekki er hreyfimynd á GIF89a sniði:
Af öllum blokkum uppbyggingarinnar, í þessu tilfelli munum við hafa áhuga á alþjóðlegu litatöflublokkinni og breytunum sem bera ábyrgð á stikunni:
CT - tilvist alþjóðlegrar litatöflu. Ef þetta flagg er stillt verður alþjóðlega litatöfluna að byrja strax á eftir rökréttu skjáhandfanginu.
Size — litastærð og fjöldi lita á myndinni. Gildi fyrir þessa færibreytu:
Size
Fjöldi lita
Litatöflustærð, bæti
7
256
768
6
128
384
5
64
192
4
32
96
3
16
48
2
8
24
1
4
12
0
2
6
Dulkóðunaraðferðir
Eftirfarandi aðferðir verða notaðar til að dulkóða skilaboð í myndskrám:
LSB (Least Significant Bit) aðferð
Aðferð til að bæta við litatöflu
LSB aðferð - algeng aðferð við stiganography. Það samanstendur af því að skipta út síðustu mikilvægu bitunum í ílátinu (í okkar tilfelli, alþjóðlegu litatöflubætin) fyrir bita falinna skilaboðanna.
Forritið mun nota síðustu tvo bitana í alþjóðlegu litatöflubætunum sem hluta af þessari aðferð. Þetta þýðir að fyrir 24-bita mynd, þar sem litapallettan er þrjú bæti fyrir rautt, blátt og grænt, eftir að skilaboð hafa verið felld inn í hana, mun hver litahluti að hámarki breytast um 3/255 stigbreytingar. Slík breyting verður í fyrsta lagi ósýnileg eða erfitt að sjá fyrir mannsauga og í öðru lagi verður hún ekki sýnileg á lággæða upplýsingaúttakstækjum.
Magn upplýsinga fer beint eftir stærð myndspjaldsins. Þar sem hámarksstærð litatöflunnar er 256 litir, og ef tveir skilaboðabitar eru skrifaðir inn í hluti hvers litar, þá er hámarkslengd skilaboða (með hámarksspjaldinu á myndinni) 192 bæti. Þegar skilaboðin eru felld inn í myndina breytist skráarstærðin ekki.
Palett stækkun aðferð, sem virkar aðeins fyrir GIF uppbyggingu. Það mun vera áhrifaríkast á myndum með lítilli litatöflu. Kjarni þess er að hún eykur stærð litatöflunnar og veitir þar með meira pláss til að skrifa nauðsynleg bæti í stað litabætisins. Ef við lítum svo á að lágmarksstærð litatöflunnar sé 2 litir (6 bæti), þá getur hámarksstærð innbyggðu skilaboðanna verið 256 × 3–6 = 762 bæti. Ókosturinn er lágt dulritunaröryggi; innfelldu skilaboðin er hægt að lesa með hvaða textaritli sem er ef skilaboðin hafa ekki verið háð viðbótar dulkóðun.
Hagnýt hluti
Hönnun dagskrár
Öll nauðsynleg verkfæri til að innleiða dulkóðunar- og afkóðunaralgrím verða innifalin í pakkanum com.tsarik.steganography. Þessi pakki inniheldur viðmótið Encryptor með aðferðum encrypt и decrypt, flokkur Binary, sem veitir möguleika á að vinna með bitafylki, sem og undantekningarflokka UnableToEncryptException и UnableToDecryptException, sem ætti að nota í viðmótsaðferðum Encryptor ef um er að ræða kóðun og umskráningarvillur í sömu röð.
Aðaldagskrárpakki com.tsarik.programs.gifed mun innihalda keyranlegan forritaflokk með kyrrstöðuaðferð main, sem gerir þér kleift að keyra forritið; flokkur sem geymir forritsfæribreytur; og pakka með öðrum flokkum.
Innleiðing reikniritanna sjálfra verður kynnt í pakkanum com.tsarik.programs.gifed.gif Flokkar GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Báðir þessir flokkar munu útfæra viðmótið Encryptor.
Byggt á uppbyggingu GIF sniðsins geturðu búið til almennt reiknirit til að koma skilaboðum inn í myndatöfluna:
Til að ákvarða tilvist skilaboða í mynd er nauðsynlegt að bæta ákveðinni röð bita við upphaf skilaboðanna sem afkóðarinn les fyrst og athugar hvort rétt sé. Ef það passar ekki, þá er litið svo á að engin falin skilaboð séu í myndinni. Næst þarftu að tilgreina lengd skilaboðanna. Síðan texti skilaboðanna sjálfs.
Bekkjarmynd af öllu forritinu:
Framkvæmd áætlunarinnar
Innleiðingu alls forritsins má skipta í tvo þætti: innleiðingu á dulkóðun viðmóts og afkóðunaraðferðir Encryptor, í bekkjum GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod, og útfærslu notendaviðmótsins.
Hugleiddu bekkinn GIFEncryptorByLSBMethod.
sviðum firstLSBit и secondLSBit innihalda fjölda bita af hverju bæti myndarinnar sem skilaboðin á að færa inn í og þaðan sem skilaboðin á að lesa. Field checkSequence geymir athugunarbitaröð til að tryggja viðurkenningu á innbyggðu skilaboðunum. Statísk aðferð getEncryptingFileParameters skilar breytum tilgreindrar skráar og eiginleikum hugsanlegra skilaboða.
@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();
}
Reiknirit og frumkóði aðferðarinnar decrypt bekknum GIFEncryptorByLSBMethod:
@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);
}
Framkvæmd bekkjarins GIFEncryptorByPaletteExtensionMethod verður svipað, aðeins aðferðin við að vista/lesa upplýsingar er önnur.
Í tíma MainFrame Umbúðaaðferðum er lýst: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), vinna úr niðurstöðum viðmótsaðferða Encryptor og samskipti við notandann, þ.e. opna valglugga fyrir skrár, sýna villuboð o.s.frv.; auk annarra aðferða: openImage(), sem gerir notandanum kleift að velja mynd, exit(), sem fer úr forritinu. Þessar aðferðir eru kallaðar frá Actionsamsvarandi valmyndaratriði. Þessi flokkur útfærir að auki viðbótaraðferðir: createComponents() - að búa til formhluta, loadImageFile(File f) — hlaða mynd í sérstakan hluta úr skrá. Framkvæmd bekkjarins GIFEncryptorByPaletteExtensionMethod svipað og bekkjarútfærslan GIFEncryptorByLSBMethod, aðalmunurinn er í því hvernig skilaboðabæti eru skrifuð og lesin af stikunni.
Rekstur forrita
LBS aðferð
Segjum að það sé mynd eins og þessi:
Á þessari mynd samanstendur pallettan af 256 litum (eins og Paint vistar). Fyrstu fjórir litirnir eru: hvítur, svartur, rauður, grænn. Aðrir litir eru svartir. Alþjóðlega litatöflubitaröðin verður sem hér segir:
Þegar skilaboðin hafa verið felld inn verður undirstrikuðu bitunum skipt út fyrir bitana úr skilaboðunum. Myndin sem myndast er nánast ekkert frábrugðin upprunalegu myndinni.
Original
Mynd með innfelldum skilaboðum
Palett stækkun aðferð
Þegar þú opnar mynd sem inniheldur skilaboð með þessari aðferð muntu sjá eftirfarandi mynd:
Það er ljóst að þessi aðferð mun ekki virka fyrir fullgilda njósnastarfsemi og gæti þurft viðbótar dulkóðun skilaboðanna.
Dulkóðun/afkóðun í hreyfimyndum virkar alveg eins og í venjulegum kyrrstæðum myndum, en hreyfimyndin er ekki biluð.