Titok a szilíciumban

Kincskeresés egy EEPROM memóriában – titkos üzenet megfejtése Arduino segítségével

Képzeljük el, hogy egy régi elektronikus eszközben találtunk egy apró memórialapkát. A lapkában egy titkos üzenet rejtőzik. Az üzenet első része normál szöveg, de a második részét valaki elrejtette egy egyszerű titkosítással. A feladatunk olyan, mintha egy digitális kincskereső játékban vennénk részt: először ki kell olvasnunk az üzenetet a memóriából, majd meg kell fejtenünk a titkos részt. Az itt látható kis 8 lábú memóriából az üzenetet egy Arduino Nano nevű kis panelt segítségével tudjuk kiolvasni. A memóriában tárolt adatokat kiolvassuk, majd megjelenítjük a számítógépen. A feladat közben megismerkedünk az EEPROM működésével, a digitális adattárolással és egy több mint kétezer éves titkosítási módszerrel is.

Mi az a memória?

Minden számítógépnek, telefonnak vagy játékkonzolnak szüksége van memóriára. A memória olyan, mint egy nagy füzet, amelyben adatokat lehet tárolni. Minden oldala a füzetnek egy adatot tud tárolni, a címmel megmondjuk hányadik oldalon van az adat és odalapozunk, majd kiolvassuk róla az értéket, amit arra a lapra írtunk. Amikor a számítógép dolgozik, folyamatosan adatokat olvas ki és ír be a memóriába.

Mi az az EEPROM?

Az EEPROM neve az angol Electrically Erasable Programmable Read Only Memory kifejezés rövidítése. Ez elsőre nagyon bonyolultan hangzik, de valójában csak annyit jelent, hogy:

  • elektromosan írható,
  • elektromosan törölhető,
  • és akkor is megőrzi az adatokat, ha kikapcsoljuk az áramot.

Képzeljünk el egy füzetet, amelyet nem kell minden kikapcsolás után újraírni. Ha ma beleírunk valamit, és holnap kinyitjuk, ugyanaz a szöveg még mindig benne lesz. Pontosan ilyen az EEPROM is. Ez azért fontos, mert sok memória, például a SDRAM, DRAM (ilyen például a számítógép DDR4, vagy DDR5 RAM-ja is) ha kikapcsoljuk a gépet, nem kap tápfeszültséget a memória elfelejti, ami benne volt.

Az NMC93C46N memória

A mi feladatunkban egy NMC93C46N nevű EEPROM található. Ez egy nagyon pici integrált áramkör. Kívülről csak egy fekete műanyag tokot látunk néhány lábbal. Belül azonban sok apró memóriahely található. Ezeket a memóriahelyeket úgy képzelhetjük el, mint egy hosszú könyv oldalait. Minden oldalnak van egy címe, és minden oldalon tárolható valamilyen adat. Ezt a memóriát 4 csatlakozóval (CS, SK, DI, DO), plusz a két táp, tudjuk elérni, írni és olvasni. Ennek nagyon pontos leírása van ha például olvasni szeretnénk melyik lábra kell igaz és melyikre hamis értéket tenni és pontosan mikor. Nagyon leegyszerűsítve a DI lábra bitenként pontosan időzítve ki kell tenni a címet, majd a DO lábon az adat érkezik, amit össze kell gyűjteni, hiszen bitenként jön a 16 bites adat. Ez a kommunikáció nagyon hasonlít az SPI adatátvitelhez, de nem pontosan az, így nekünk kell megoldani, nincs kész megoldás rá.

Soros adatátvitel: https://www.youtube.com/watch?v=4Or_Kb4_g1M

SPI adatátvitel: https://www.youtube.com/watch?v=_jhTxQ-PPEg&t=99s

Arduino

Első lépésként ismerkedjünk meg az Arduino panellel.

Arduino bevezetés: https://www.youtube.com/watch?v=gsuZPhkhH9g

LED villogtatása: https://www.youtube.com/watch?v=_pbMKtE52C0

Hogyan tárolja az üzenetet a memória?

A kis memória modulunkban egy szöveges üzenet található, amiben az angol ABC betűi szerepelnek. A számítógépek nem betűkkel dolgoznak, hanem bitekkel. A bit a számítástechnika legkisebb információegysége. Egy bit kétféle értéket vehet fel: 0 vagy 1. Ez olyan, mint egy villanykapcsoló, lekapcsolva = 0, felkapcsolva = 1. Nyolc bit együtt alkot egy bájtot. Egy bájt már elegendő ahhoz, hogy egy karaktert eltároljunk. A számítógépeknek meg kell állapodniuk abban, hogy melyik szám melyik karaktert jelenti. Erre szolgál az ASCII táblázat. Így a számítógép valójában számokat tárol, de mi betűként látjuk őket. Például:

BetűASCII kód decimálisanASCII kód binárisan
a6501000001
b6601000010
c6701000011

A memória szervezése

Az NMC93C46N ebben a feladatban 16 bites módban működik. Ez azt jelenti, hogy minden memóriarekord 16 bit hosszú. Mivel egy karakter 8 bit, ezért egy rekordban pontosan két karakter fér el. Képzeljük el egy dobozt, amelynek két rekesze van. Az első rekeszben van az első betű. A második rekeszben van a második betű. Például: h e a memória egyetlen rekordjában tárolható, a következő rekordban: l l, a harmadikban: o ! Így a memória szépen egymás után tárolja a teljes szöveget, ami a hello!

Hogyan kapcsolódik az Arduino?

Az Arduino Nano lesz a kis nyomozónk. Ő fogja végig kérdezni a memóriát. A memória és az Arduino között négy fontos vezeték található.

CS: A CS azt jelenti, hogy Chip Select. Ez olyan, mint amikor megszólítunk valakit. Ha a CS aktív, akkor az EEPROM figyel. Ha nem aktív, akkor hallgat.

SK: Az SK az órajel. Ez diktálja a kommunikáció ütemét. Olyan, mint amikor valaki tapsol, és minden tapsra egy új információ érkezik.

DI: Data Input. Ezen keresztül az Arduino küld adatokat az EEPROM-nak.

DO: Data Output. Ezen keresztül az EEPROM válaszol az Arduinónak.

A memóriát egy tetszőleges Arduino nano (UNO, MEGA, …) panelre mi is ráköthetjük, vagy az előre összeállított panelt is használhatjuk, ahol ezek a bekötések már készen vannak.

A memóriát a kapott paneleken ezeken a lábakon érjük el:

#define CS   9
#define SK   13
#define DI   12
#define DO_  11

Ezeket a lábakat inicializálni kell:

  pinMode(CS, OUTPUT);
pinMode(SK, OUTPUT);
pinMode(DI, OUTPUT);
pinMode(DO_, INPUT_PULLUP);

A memóriát olvasni ezzel a függvénnyel lehet uint16_t readWord(uint8_t addr):

void sendBit(bool b)
{
digitalWrite(DI, b);
delayMicroseconds(100);
digitalWrite(SK, HIGH);
delayMicroseconds(100);
digitalWrite(SK, LOW);
delayMicroseconds(100);
}
void sendBits(uint16_t value, uint8_t bits)
{
for (int i = bits - 1; i >= 0; i--)
sendBit((value >> i) & 1);
}

uint16_t readWord(uint8_t addr)
{
uint16_t value = 0;

digitalWrite(SK, LOW);
digitalWrite(CS, HIGH);

sendBit(1); //START
sendBits(0b10, 2); // READ opcode = 10

sendBits(addr, 6); // 6 bites cím

for(int i = 0; i < 16; i++)
{
digitalWrite(SK, HIGH);
delayMicroseconds(20);

value <<= 1; // adat a felfutó él után érvényes
if(digitalRead(DO_))
value |= 1;

digitalWrite(SK, LOW);
delayMicroseconds(20);
}
digitalWrite(CS, LOW);
return value;
}

Például a uint16_t d = readWord(3): ez visszaadja a 3. címen lévő adatot, ami a 16 bites d változóba kerül. (Az uint16_t egy előjeltelen int formátum, ami 16 bites.)

Hogyan történik az olvasás?

Tegyük fel, hogy a memória egyik rekordját szeretnénk kiolvasni.

Az Arduino először azt mondja: „Figyelj rám!” Ezt a CS jel aktiválásával teszi. digitalWrite(CS, HIGH);

Ezután elküldi: „Olvasni szeretnék!” sendBit(1); és sendBits(0b10, 2);

Majd elküldi annak a memóriahelynek a címét, amelyet ki akar olvasni. sendBits(addr, 6);

A memória ezután válaszol, és bitenként elküldi a 16 bites adatot. (for ciklus, ami utána olvassa)

Az Arduino szépen összegyűjti ezeket a biteket.

Amikor mind a 16 bit megérkezett, már tudja, hogy milyen két karakter volt eltárolva.

Egy adat olvasása

Először olvassunk ki egy adatot a memóriából. Például a 0. címről. A kiolvasott adatot bontsuk két részre, hiszen a 16 bites adat 2 betűt tárol. Ez a c1 és c2 változóba kerül:

A d változó alsó 8 bitjét megtartjuk és char típusra, vagyis 8 bites-re alakítjuk, így az alsó 8 bit marad meg: char c1 = (char)(d & 0x00FF);

A d változó bitjeit jobbra 8-al elmozgatjuk, és az előző módon átalakítjuk 8 bitessé: char c2 = (char)((d>>8) & 0x00FF);

A c1 és c2 1-1 betű ASCII kódját tartalmazza. Ezt soros porton küldjük el a PC fele és nézzük meg. Ha space, vagy angol abc kis betű ASCII kódját látjuk sikerült a kiolvasás. (32, vagy 97 és 122 közötti értékek lehetnek a memóriában, ha mást olvasunk akkor oda lehet nem lett beírva semmi, vagy hibásan olvasunk.)

Soros port használata: https://www.youtube.com/watch?v=TLgnAq0RrPE

Az alábbi példa mutatja, hogy a c1 változóban az a betü van éppen. Ezt 3 féle képpen is kiirhatjuk. 1. esetben a betü jelenik meg, második esetben 61 hexa érték és a 3. esetben 01100001 binári érték jelenik meg a PC oldalán. Nekünk a végén majd az 1. eset kell, mert a betüket akarjuk látni, de a tesztelésnél a többi is jól jöhet.

char c = 'a';

Serial.println(c);
Serial.println(c, HEX);
Serial.println(c, BIN);

A szöveg összeállítása

Miután az Arduino kiolvasta az első rekordot, abból két karakter lesz. Utána jön a második rekord. Abból is két karakter lesz. Majd a harmadik. És így tovább. A végén a sok kis darabkából összeáll a teljes üzenet. Pontosan úgy, mint amikor egy puzzle darabjait összerakjuk. Ezeket sorban küldjük el a soros porton és ott egyben látjuk az üzenetet. De, ha minden jól csináltunk csak az üzenet első felének lesz értelme, a második fele titkosítva lesz. Ezt vissza kell fejteni.

Miért titkosították az üzenet végét?

A játék készítője azt szerette volna, hogy ne legyen túl egyszerű a feladat. Ezért az üzenet második felét elrejtette. Ehhez egy nagyon régi módszert használt. Ez a Caesar-kód.

Ki volt Caesar?

Julius Caesar több mint kétezer évvel ezelőtt élt. Amikor titkos üzeneteket küldött a katonáinak, nem akarta, hogy az ellenség elolvassa őket. Ezért kitalált egy egyszerű titkosítást.

Hogyan működik a Caesar-kód?

Tegyük fel, hogy minden betűt három hellyel előrébb tolunk az ábécében. Így:

a → d
b → e
c → f

és így tovább.

A HELLO szó például: hello titkosítva: khoor lesz. Aki nem ismeri a szabályt, csak értelmetlen betűket lát.

A visszafejtés

A megfejtéshez pontosan az ellenkezőjét kell tenni. Ha a titkosításnál hárommal előre léptünk, akkor most hárommal vissza kell lépnünk. Például:

k → h
h → e
o → l
o → l
r → o

Így visszakapjuk az eredeti szót.

Mi történik, ha nem tudjuk az eltolást?

A Caesar-kód egyik gyengesége, hogy nagyon kevés lehetőség van. Mindössze 25 különböző eltolás próbálható ki. Ezért a számítógép egyszerűen kipróbálhatja az összeset. Olyan ez, mint amikor egy lakatnál sorban végig próbáljuk az összes kombinációt. Előbb-utóbb megtaláljuk a helyeset.

Összefoglalás

Ebben a feladatban egyszerre találkozhatunk az elektronika, a programozás és a titkosírás világával. Tulajdonképpen ugyanazt csináljuk, mint egy digitális kincsvadász: először megtaláljuk a rejtett üzenetet a memória mélyén, majd megfejtjük a titkos részt, hogy kiderüljön, mit akart üzenni a feladat készítője.