Diplomamunka. Miskolci Egyetem. GPGPU technológia kriptográfiai alkalmazása. Készítette: Csikó Richárd VIJFZK mérnök informatikus



Hasonló dokumentumok
GPGPU alapok. GPGPU alapok Grafikus kártyák evolúciója GPU programozás sajátosságai

GPGPU-k és programozásuk Dezső, Sima Sándor, Szénási

OpenCL - The open standard for parallel programming of heterogeneous systems

GPGPU. GPU-k felépítése. Valasek Gábor

Grafikus csővezeték 1 / 44

OpenCL Kovács, György

Ismétlés: Moore törvény. Tranzisztorok mérőszáma: n*százmillió, n*milliárd.

Párhuzamos és Grid rendszerek

A CUDA előnyei: - Elszórt memória olvasás (az adatok a memória bármely területéről olvashatóak) PC-Vilag.hu CUDA, a jövő technológiája?!

Nagy adattömbökkel végzett FORRÓ TI BOR tudományos számítások lehetőségei. kisszámítógépes rendszerekben. Kutató Intézet

Haladó Grafika EA. Inkrementális képszintézis GPU-n

Bánsághi Anna 1 of 67

FPGA áramkörök alkalmazásainak vizsgálata

Videókártya - CUDA kompatibilitás: CUDA weboldal: Példaterületek:

VLIW processzorok (Működési elvük, jellemzőik, előnyeik, hátrányaik, kereskedelmi rendszerek)

AES kriptográfiai algoritmus

Grafika programozása

Dr. Illés Zoltán

Sapientia Egyetem, Műszaki és Humántudományok Tanszék.

OSZTOTT 2D RASZTERIZÁCIÓS MODELL TÖBBMAGOS PROCESSZOROK SZÁMÁRA

Számítógépes grafika

Bevitel-Kivitel. Eddig a számítógép agyáról volt szó. Szükség van eszközökre. Processzusok, memória, stb

Kódolás, hibajavítás. Tervezte és készítette Géczy LászlL. szló 2002

FIR SZŰRŐK TELJESÍTMÉNYÉNEK JAVÍTÁSA C/C++-BAN

14.2. OpenGL 3D: Mozgás a modellben

Hálózati biztonság ( ) Kriptográfia ( )

Eichhardt Iván GPGPU óra anyagai

(11) Lajstromszám: E (13) T2 EURÓPAI SZABADALOM SZÖVEGÉNEK FORDÍTÁSA

Óbudai Egyetem. Doktori (PhD) értekezés. Adatpárhuzamos sejtmagkeresési eljárás fejlesztése és paramétereinek optimalizálása Szénási Sándor

Eichhardt Iván GPGPU óra anyagai

GPGPU: Általános célú grafikus processzorok cgpu: computational GPU GPGPU = cgpu Adatpárhuzamos gyorsító: dedikált eszköz, ami eleve csak erre

WebSphere Adapters. 6. változat 2. alváltozat. WebSphere Adapter for SAP Software felhasználói kézikönyv 6. változat 2. kiadás

OpenCL alapú eszközök verifikációja és validációja a gyakorlatban

2. Generáció ( ) 3. Generáció (2001) NVIDIA TNT2, ATI Rage, 3dfx Voodoo3. Klár Gergely

VirtualBox, Debian telepítés

PCI Express szabvány

Számítógép Architektúrák

Négyprocesszoros közvetlen csatolású szerverek architektúrája:

A Szekszárdi I. Béla Gimnázium Helyi Tanterve

GPGPU programozás lehetőségei. Nagy Máté Ferenc Budapest ALICE ELTE TTK Fizika MSc 2011 e-science Café

Kriptográfia I. Kriptorendszerek

A HV-PCI6 VIDEODIGITALIZÁLÓ KÁRTYA ÉS ALKALMAZÁSAI (HV-PCI6 Video Digitizing Card and its Applications)

Programozható logikai vezérlõk

A Margit híd pillérszobrának 3D-s digitális alakzatrekonstrukciója Nagy Zoltán 1 Túri Zoltán 2

AutoCAD Architecture 2008 A magyar építész AutoCAD újdonságai

I 2 C, SPI, I 2 S, USB, PWM, UART, IrDA

Minden jog fenntartva, beleértve bárminemű sokszorosítás, másolás és közlés jogát is.

1. A Neumann-elvű számítógép felépítése

erettsegizz.com Érettségi tételek

Bírálat. Farkas András

GPGPU. Architektúra esettanulmány

15. Programok fordítása és végrehajtása

Dr. Pétery Kristóf: AutoCAD LT 2007 Fóliák, tulajdonságok

FIR és IIR szűrők tervezése digitális jelfeldolgozás területén

Joint Test Action Group (JTAG)

Szupermikroprocesszorok és alkalmazásaik

1. Az utasítás beolvasása a processzorba

Számítógépvezérelt rendszerek mérnöki tervezése

A mikroszámítógép felépítése.

Prezentáció használata

Virtualizációs Technológiák Bevezetés Kovács Ákos Forrás, BME-VIK Virtualizációs technológiák

Máté: Számítógép architektúrák

E7-DTSZ konfigurációs leírás

Szakmai zárójelentés

Veszteséges képtömörítő eljárások pszichovizuális összehasonlítása

Szoftverprototípus készítése. Szoftverprototípus készítése. Szoftverprototípus készítése

CONCRETE STEEL PRESTRESSING. IDEA StatiCa. Calculate yesterday s estimates

Modellalkotás UML-ben

A 3D képgenerálás komplexitása

Operációs rendszerek előadás Multiprogramozott operációs rendszerek

Tartalom. Történeti áttekintés. Történeti áttekintés Architektúra DCOM vs CORBA. Szoftvertechnológia

8. Mohó algoritmusok Egy esemény-kiválasztási probléma. Az esemény-kiválasztási probléma optimális részproblémák szerkezete

Adatfolyam alapú RACER tömbprocesszor és algoritmus implementációs módszerek valamint azok alkalmazásai parallel, heterogén számítási architektúrákra

Minden jog fenntartva, beleértve bárminemű sokszorosítás, másolás és közlés jogát is.

Book Template Title. Author Last Name, Author First Name

TANÚSÍTVÁNY. tanúsítja, hogy a Polysys Kft. által kifejlesztett és forgalmazott

Bevezetés. Kiknek szól a könyv?

Operációs rendszerek. A Windows NT felépítése

Képfeldolgozási módszerek a geoinformatikában

IBM WebSphere Adapters 7. változat 5. alváltozat. IBM WebSphere Adapter for felhasználói kézikönyv 7. változat 5.kiadás

Tanúsítási jelentés. Hung-TJ a MultiSigno Developer Professional. aláíró alkalmazás fejlesztő készletről. /Kopint-Datorg Rt.

Számítógép Architektúrák

PÁRHUZAMOS SZÁMÍTÁSTECHNIKA MODUL AZ ÚJ TECHNOLÓGIÁKHOZ KAPCSOLÓDÓ MEGKÖZELÍTÉSBEN

Az INTEL D-2920 analóg mikroprocesszor alkalmazása

Google Summer of Code OpenCL image support for the r600g driver

Operációs rendszerek MINB240 V kredit KF Nagyváradi Anett 0. előadás Bevezetés

AutoN cr. Automatikus Kihajlási Hossz számítás AxisVM-ben. elméleti háttér és szemléltető példák február

Mobil készülékek programozása

1. ábra: Perifériára való írás idődiagramja

Az anyagdefiníciók szerepe és használata az Architectural Desktop programban

CA Clarity PPM. Igénykezelés felhasználói útmutató. Release

Ismeretanyag Záróvizsgára való felkészüléshez

A Polycom RealPresence Group Series készülékek és tartozékok szoftverének és opcióinak telepítése. Áttekintés

Minden jog fenntartva, beleértve bárminemű sokszorosítás, másolás és közlés jogát is.

Rendszertervezés 2. IR elemzés Dr. Szepesné Stiftinger, Mária

Digitális technika VIMIAA01

Máté: Számítógép architektúrák

3. Gyakorlat Ismerkedés a Java nyelvvel

Az Oracle rendszer komponensei

OPERÁCIÓS RENDSZEREK. Célkitűzések, tárgyfelépítés. Módszerek. OS fogalom, struktúrák. 2005/2006. tanév II. félév Dr. Vadász Dénes

Átírás:

Diplomamunka Miskolci Egyetem GPGPU technológia kriptográfiai alkalmazása Készítette: Csikó Richárd VIJFZK mérnök informatikus Témavezető: Dr. Kovács László Miskolc, 2014

Köszönetnyilvánítás Ezúton szeretnék köszönetet mondani mindazoknak akik hozzásegítettek ahhoz, hogy ez a diplomamunka elkészülhessen. Elsősorban témavezetőmnek, Dr. Kovács Lászlónak, hogy elvállalta a téma vezetését, valamint a konzultációk alkalmával rendelkezésemre állt, tudásával és szakértelmével segítette, hogy ez a diplomamunka létrejöhessen. Továbbá köszönettel tartozom a Miskolci Egyetem minden egyes tanárának, hogy végig segítettek egyetemi tanulmányaim során. Különösen köszönöm Dr. Juhász Imrének, valamint Dr. Olajos Péternek, hogy olyan érdekes és izgalmas témákkal ismerkedhettem meg általuk, mint a GPGPU programozás vagy a kriptográfia.

Tartalomjegyzék 1. Bevezetés 1 2. GPGPU programozás 3 2.1. A GPGPU programozás története..................... 3 2.2. A modern GPU architektúrák kialakulása................ 4 2.2.1. Fix funkciós grafikus csővezeték.................. 4 2.2.2. Programozható csővezeték..................... 5 2.2.3. Unified Shader Modell és Architektúra.............. 7 2.3. GPU programozási modell......................... 7 2.4. GPGPU szoftver környezet......................... 9 2.5. OpenCL................................... 9 2.5.1. Platform Modell.......................... 10 2.5.2. Végrehajtási Modell........................ 11 2.5.3. Memória Modell.......................... 15 2.5.4. Programozási Modell........................ 16 3. AES 18 3.1. Blokkok, körök, körfüggvények....................... 18 3.1.1. SubBytes.............................. 19 3.1.2. ShiftRows.............................. 20 3.1.3. MixColums............................. 21 3.1.4. AddRoundKey........................... 21 3.2. A titkos kulcs................................ 21 3.2.1. Kulcskiterjesztés.......................... 22 3.2.2. A körkulcsok kiválasztása..................... 23 3.3. Kódolás és dekódolás............................ 23 3.4. Az algoritmus optimalizációja 32-bites processzorokra.......... 24 4. A Program 26 4.1. Felhasználói Felület............................. 27 4.1.1. Qt.................................. 27 4.2. A program használata........................... 28 4.2.1. Kódolás-dekódolás......................... 28 4.2.2. Benchmark............................. 30 4.3. Implementációk............................... 31 4.3.1. C implementációk.......................... 31 4.3.2. OpenCL implementációk...................... 37 4.3.3. Az OpenCL környezet felkonfigurálása.............. 43 4.3.4. RFC 5652.............................. 47 i

4.4. Teszteredmények.............................. 47 4.4.1. Futási idő.............................. 47 4.4.2. Gyorsítás.............................. 51 4.4.3. Áteresztőképesség.......................... 55 4.4.4. Végkövetkeztetés.......................... 56 5. Összegzés 57 6. Summary 58 CD melléklet tartalma 59 Irodalomjegyzék 60

1. fejezet Bevezetés Egyetemi tanulmányaim alatt még az alapképzés során találkoztam először a kriptográfia témakörével, amely napjaink informatikájának fontos szereplője, hiszen egyre több személyes adatot tárolunk különböző online tárhelyeken, felhőkben, amelyeket nem szeretnénk ha idegenek kezébe jutna. Egyre több dolgot intézünk online, amely során érzékeny adatokat küldünk és fogadunk a világhálón keresztül. Gondoljunk csak a manapság egyre népszerűbb internetes vásárlásra, amely során bankkártyánk adatainak védelme kiemelkedően fontos. Ezen dolgok miatt ma már mindannyiunk életében fontos szerepet tölt be a kriptográfia, és talán éppen ezért keltette fel érdeklődésem ez a témakör. A későbbiekben megismerkedhettem a számítógépi grafika alapjaival, ezen belül a GPUk alapvető felépítésével és működési elvével. A témával foglalkozva futottam össze a GPGPU (General-purpose computing on graphics processing units) programozással, amely egy nagyon érdekes témakört feszeget, vagyis, hogy hogyan lehet az elsősorban képi megjelenítésre kifejlesztett és használt GPU-kat bármilyen más, általános számítási feladat megoldására alkalmazni. Ez azért is érdekes, mivel a GPU-k felépítése és fejlődési útja merőben eltér az eredetileg is általános feladatok megoldására szánt központi egységek, azaz CPU-k felépítésétől. Éppen ezek a felépítésbeli különbségek teszik lehetővé, hogy bizonyos problémák megoldásában a GPU-k hatalmas előnyre tegyenek szert. Ezek a problémák nem mások, mint a jól párhuzamosítható feladatok. Ezen problémák megoldásában napjaink CPU-i nem képesek felvenni a versenyt a modern GPU-k számítási teljesítményével. Amikor diplomamunkámhoz kellett témát választanom, mindenképpen szerettem volna olyan témakörrel foglalkozni, ami valóban érdekel és a téma mélyebb ismerete hasznos lehet a jövőben. Így esett választásom a fenti két témakörre. Jelen diplomamunkámban szeretném bemutatni a GPGPU programozás alapjait egy kriptográfiai példán keresztül. Választásom az AES (Advanced Encryption Standard) algoritmusra esett, hiszen amellett, hogy nagyon jól párhuzamosítható, így kitűnő példa a GPGPU programozás bemutatásához, napjaink egyik legbiztonságosabb szimmetrikus kulcsú titkosítási eljárása, amelyet széles körben alkalmaznak az informatikában. A diplomamunkámhoz készített program több AES implementációt is tartalmaz, melyek közül néhány CPU-n néhány pedig GPU-n fut, így bemutatható, hogy mekkora potenciál rejlik a GPGPU programozásban a jól párhuzamosítható feladatok megoldása során. Napjaink két vezető GPGPU API-ja, az OpenCL és a CUDA között kellett választanom. Bár a két API koncepciója egymáshoz nagyon közeli, az OpenCL hardverek 1

széles körét támogatja (nem csak GPU-kat, de CPU-kat, DSP-ket, FPGA-kat, stb...), a CUDA viszont csak és kizárólag NVIDIA GPU-kat támogat. Mivel ez utóbbit túl nagy megkötésnek érzem, így választásom az OpenCL API-ra és platformra esett. 2

2. fejezet GPGPU programozás 2.1. A GPGPU programozás története Az elmúlt néhány évtizedben a számítógépekben használt központi egységek (CPU) teljesítménye rohamos ütemben fejlődött. A kétezres évek elejére az otthonainkban használt személyi számítógépek is elérték a több GFLOPS-os (Floating point Operations Per Second, másodpercenkénti lebegőpontos műveletek száma) számítási teljesítményt, melynek következtében a mindennapokban használt szoftvereink képesek hatalmas mennyiségű adat gyors feldolgozására. A rendelkezésünkre álló digitális adat mennyisége azonban robbanásszerűen nőtt az elmúlt évtizedben (és azóta is folyamatosan növekszik) így már ez a teljesítmény sem volt elegendő. A processzorgyártók az elmúlt évtizedben felhagytak a cpu órajel hatalmas ütemben történő növelésével (a fogyasztás és hőtermelés visszaszorítása érdekében) és inkább egy új megközelítést választottak, multi-core (többmagos) processzorokat kezdtek gyártani. Ezekben a processzorokban kettő vagy több egymástól független központi számítási egység (más néven "mag") található, melynek köszönhetően a cpu képes egyszerre több utasítást végrehajtani. Ez az architekturális váltás komoly hatással volt a szoftverfejlesztő társadalomra, hiszen az eddigi szekvenciális programjaik ezidáig mindenféle többletmunka nélkül, maguktól gyorsabban futottak az új, gyorsabb hardvereken. Ez azonban nem így történt a multi-core cpu-k megjelenésekor, hiszen a szekvenciális programok nem profitálnak a többmagos processzorok teljesítményfölényéből, mivel csak azok egyszerre csak egy processzormagon képesek futni. A szoftverfejlesztőknek tehát át kellett térniük az emberi gondolkodásmódhoz is közelebb álló szekvenciális programozási paradigmáról, a sokkal komplexebb, párhuzamos (többszálas), más néven konkurens programozásra, hogy képesek legyenek kihasználni a multi-core CPU-k teljesítménytöbbletét. Manapság szinte minden személyi számítógépben több magos CPU-k dolgoznak, amihez az elmúlt évek folyamán a szoftverfejlesztők is alkalmazkodtak. Azonban a processzor magok felépítése és az ebből adódó fizikai méret gátat szab az egy lapkára integrálható magok számának. Van azonban egy másik processzor, amely eredeti céljának köszönhetően több száz de akár több ezer magot is tartalmazhat és minden egyes számítógépben megtalálható. Ez nem más mint a GPU. Az elmúlt években nem csak a CPU-k, de az eredetileg grafikus feladatok elvégzésére kitalált grafikus gyorsítók is fejlődtek, melynek eredményeképpen a modern GPU-k egyre inkább tekinthetők sokmagos, általános célú processzornak mint célhardvernek. Az olyan magas szintű API-k megjelenésével mint a CUDA vagy 3

az OpenCL a fejlesztők könnyedén kiaknázhatják a GPU-ban rejlő lehetőségeket és hatalmas számítási potenciált. Ám ahhoz, hogy ezek az eszközök valóban helyesen (és lehetőleg optimálisan) legyenek használva, elengedhetetlen a GPU-k általános architektúrájának és ezen architektúra kialakulásának alapszintű ismerete ismerete. 2.2. A modern GPU architektúrák kialakulása A GPU egy erőteljes párhuzamosságot támogató mikroprocesszor, amelynek elsődleges feladata a 2D és 3D grafikus műveletek gyorsítása, elvégzése a CPU helyett. Kezdetben ezek a mikroprocesszorok csak speciális, a grafikus megjelenítéshez kapcsolódó feladatok ellátására voltak képesek (köszönhetően a fix funkciós hardveres csővezetéknek), de manapság a modern gpu-k teljes mértékben programozhatók és lebegőpontos számítási teljesítményük sok esetben többszörösen túlszárnyalja a modern cpu-két. 2.2.1. Fix funkciós grafikus csővezeték A grafikus csővezeték (vagy futószalag) egy modell, amely leírja, hogy a megjelenítendő adat milyen fázisokon megy keresztül a megjelenítésig. A folyamat célja, hogy a 3D-s tér megjeleníthető legyen egy 2D-s képernyőn. Ezen a leképzési folyamat fázisai két nagyobb csoportra bonthatóak: geometriai számítások és megjelenítés. Kezdetben a gpu-k, csak a megjelenítési fázist implementálták (hardveresen), így a cpu-nak kellett a megjelenítendő háromszögeket kiszámítania, ám idővel, ahogy a gpu-k fejlődtek, a csővezeték több és több funkcióját implementálták a gpu-k hardverébe, így tehermentesítve a cpu-t [6]. Az 1980-as évek elejétől az 1990-es évek végéig a grafikus hardverek fixfunkciós csővezetéket használtak, amely konfigurálható ám nem programozható. Kezdetben ezek a komponensek csak a nagy és drága rendszerekben voltak elérhetőek, ám szép lassan a 90-es évekre, a technológia fejlődésével áruk egyre csökkent és megjelentek az olcsóbb munkaállomásokban és személyi számítógépekben is. 1989-ben a Silicon Grapics Inc. (SGI) megalkotta az OpenGL-t, az iparág által széles körben használt és támogatott, platformfüggetlen 2D/3D grafikus api-t, ezzel komoly szerepet játszottak a korai grafikus csővezetékek fejlődésében. 1993-ban az SGI megjelentette a RealityEngine néven ismert grafikus hardverét, amely a grafikus csővezeték több későbbi fázisát is implementálta, ám még mindig nagy szerepet játszott mellette a cpu. A 90-es évek közepére a konzumer piacon is megjelentek olyan, olcsónak számító grafikus gyorsítók mint a Voodoo (3DFX), Riva TNT (NVIDIA) vagy a Rage (ATI). Ezek a korai gpu-k még mindig csak egyetlen pixelt tudtak előállítani ciklusonként, így jóval lassabban voltak képesek dolgozni, mint ahogy az cpu oldalról elvárt lett volna. Ennek következtében később több grafikus csővezetéket is ráraktak egy gpu-ra melynek következtében ciklusonként párhuzamosan több pixel előállítása vált lehetővé számukra. Egészen 1999-ig kellett várni hogy bárki megvásárolhassa az első olyan grafikus gyorsítókat, amelyek a teljes csővezetéket implementálták. Ezek voltak az NVIDIA GeForce 4

2.1. ábra. Egy fix funkciós grafikus csővezeték 256 és az ATI Radeon 7500. Ezeket a csővezetékeket fix funkciósnak nevezték, mivel miután a gpu megkapta az adatokat, azok nem voltak módosíthatóak. Az ilyen fix funkciós csővezetékek fő problémája (a rugalmatlanság) a játékiparban jelentkezett. Mivel a grakifus API-k (OpenGL, DirectX) által elérhető funkciók hardveresen voltak implementálva, hiába bővültek a grafikus API-k, az új funkciókat a fix funkciós hardver nem volt képes kihasználni. Érdekesség, hogy 1999-ig senki nem használta a "GPU" kifejezést, először az NVIDIA vezette be a GeForce 256 megjelenésével. A grafikus kártyák ezen generációja volt az első, ami már AGP (Accelerated Graphics Port) csatolófelületet használt PCI (Peripheral Component Interconnect) busz helyett. 2.2.2. Programozható csővezeték A fix funkciós csővezeték kötöttségeinek köszönhetően képtelen volt hatékonyan megvalósítani azokat a műveleteket, amelyeket az egyre komplexebb grafikus effektek megköveteltek. Kézenfekvő volt, hogy fix vertex és fragment műveleteket a programozó által befolyásolhatóvá kell tenni. 2001-ben az NVIDIA bemutatta a GPU-k egy következő generációját, az első olyan GPU-t amely már programozható grafikus csővezetékkel rendelkezett. Ez volt a Ge- Force 3. A programozható csővezeték hatalmas előrelépés volt a programozók számára, akik ettől a ponttól képesek voltak befolyásolni, programozni az addig fix csővezeték egyes részeinek viselkedését úgynevezett vertex vagy shader programokkal (amelyek 5

egy assembly szerű shader nyelven íródtak). Ám kezdetben a programozók befolyása csupán a vertex feldolgozás fázisára korlátozódott. A teljes egészében programozható csővezetékkel ellátott grafikus kártyákra további egy évet kellett várni. 2002-ben megjelent az NVIDIA GeForce FX és az ATI Radeion 9700, amelyek már különálló hardvereket tartalmaztak pixel shader és vertex shader feldolgozásra. 2.2. ábra. Példa a különálló vertex és pixel feldolgozóval ellátott programozható grafikus csővezetésre 2004-re a GPU-k fejlődésének üteme messze meghaladta a Moore törvényt. Megjelentek az elő PCI-Express buszt használó NVIDIA GeForce 6 és ATI Radeon X800 kártyák, valamint az első magasabb szintű GPU programozási nyelvek a Brooke és Sh személyében. Hardveres oldalról is történt fejlődés, 64bit-es dupla pontosságú lebegőpontos számok támogatása, több GPU memória és több render buffer. Ezen a ponton már tekinthetünk a GPU-kra olyan programozható processzorként is, amely rengeteg lebegőpontos műveletet képes elvégezni, ezáltal nem csak a grafikus megjelenítésre alkalmasak, hanem egyes számításigényes feladatok elvégzésére is, amiknek semmi köze a megjelenítéshez. Van azonban egy kis probléma azzal, ha a vertex és fragment műveleteket különálló feldolgozóegységek végzik, mégpedig az, hogy egyes számítások sokkal több vertex műveletet igényelhetnek, még mások szinte csak fragment műveletekből állnak. Ilyen esetekben a hardver egy része kihasználatlanul áll, és arra vár, hogy a másik rész végezzen a feladatával. A megoldás az lenne, ha képesek lennénk elérni, hogy az idő 100%-ában kihasznált legyen az egész hardver. Erre a problémára nyújt megoldást a Unified Shader Modell. 6

2.3. ábra. GeForce 6 felépítése 2.2.3. Unified Shader Modell és Architektúra Unified Shader Modell szerint használhatnánk egyesített utasításkészletet a különböző típusú shader processzorokhoz. Ez azért lehetséges, mert az egyes shader típusok műveletei nagyrészt hasonlítanak egymásra. Ez a koncepció nagyban leegyszerűsíti a shader programok írását is, továbbá ez a gondolat vezetett a Unified Shader Architecture kialakulásához. 2006-ban az NVIDIA bemutatta a GeForce 8-as szériát. A G80 (GeForce 8800) architektúrával bemutatta az uj Unified (egyesített) Shader Architektúrát, melynek lényege, hogy a gpu nem tartalmaz különálló feldolgozó egységeket az egyes shader típusokhoz (geometry, vertex és pixel shadert). E helyett a feldolgozóegységek azonos felépítésűek és a shaderek típustól függetlenül képesek azokat feldolgozni. Vagyis ezek a GPU-k már több egymástól független, felépítésükben és funkcionalitásukban azonos, általános feldolgozóegységet tartalmaztak. Ezek a feldolgozóegységek általában nagyobb csoportokba vannak szervezve, amelyeket az NVIDIA Streaming multiprocessor-nak vagy röviden SM-nek nevez. Ettől a ponttól a hagyományos grafikus csővezeték eltűnt a hardverről, csupán szoftveres absztrakció formájában maradt meg. A GPU-k az elmúlt nagyjából 30 évben egy specifikus feladatot ellátó, egyetlen magot tartalmazó célhardverből, sokmagos, masszív párhuzamosítást erősen támogató, általános célokra is felhasználható és programozható részévé váltak számítógépeinknek. 2.3. GPU programozási modell A hatékonyság érdekében a GPU egyszerre sok adategységet dolgoz fel párhuzamosan, ugyanazokat a műveleteket végrehajtva rajtuk. Minden ilyen egység teljesen független a többitől. Ez a viselkedés megfelel a SIMD (Single Instruction Multiple Data) modellnek. Komplex számítások esetén (ahol az egyes adategységek más végrehajtási útvonalat követelnek meg a programon belül) azonban van egy hatalmas hátránya a 7

2.4. ábra. GeForce 8800 GTX felépítése 16 Streaming Multiprocessor-al 2.5. ábra. GeForce 8800 GTX két Streaming Multiprocessor-a. Mindegyik SM tartalmaz 8 stream processzort, 2 speciális funkciót ellátó egységet, közös utasítás és adat cache-t valamint 16 kb osztott memóriát SIMD modellnek, mégpedig az, hogy nem képes kezelni a feltételes elágazásokat. Ez vezetett a nagyon hasonló, ám a feltételes elágazásokat is kezelni képes SPMD (Single Program Multiple Data) modell kialakulásához. A GPU-k egyik nagy előnye a hatalmas mennyiségű számítási egység. Ahhoz, hogy minden egyes ilyen egység képes legyen kezelni a feltételes elágazásokat, komoly mennyiségű plusz hardver elemre lenne szükség. E helyett egy másik megközelítést választottak a probléma megoldására. A blokkokba szervezett feldolgozóegységek és az egyes blokkok is párhuzamosan dolgoznak. Ha egy blokkban feltételes elágazáshoz érkezik a végrehajtás, akkor a blokk kiszámítja az elágazás mindkét ágát, amely ezentúl elérhető a blokkon belül az összes feldolgozóegységnek. Habár a feltételes elágazások problémája ezzel megoldott, az ilyen műveletek mégis lassítják a végrehajtást, vagyis a legjobb teljesítmény elérése érdekében lehetőleg kerüljük a feltételes elágazásokat, vagy legalább próbáljuk minimalizálni a számukat. 8

2.4. GPGPU szoftver környezet Kezdetekben a GPU-k programozása csak és kizárólag grafikus API-kon (DrirectX, OpenGL) keresztül volt lehetséges. A programozható fragment és vertex processzorok megjelenésével ez némiképp egyszerűsödött, de még mindig nehéz volt általános célokra felhasználni a GPU erejét. A DirectX 9-el együtt megjelentek a magas szintű nyelvek mint a HLSL (High-level Shading Languge), az NVIDIA Cg vagy a GLSL amelyek már c-szerű programozási felületet biztosítottak a shader programok számára. Azonban ezen új nyelvek megjelenésével sem vált sokkal egyszerűbbé az GPGPU programozás, hiszen ezek eredetileg még mindig shader programozási nyelvek voltak, így még mindig olyan grafikai elemeket használtak mint vertexek, fragmentek és textúrák. Az első olyan nyelvek amelyek már képesek voltak elvonatkoztatni a GPU grafikus mivoltától a BrookGPU és az Sh voltak. Ezek már stream processzorként kezelték a GPU-t. A adatokat adatfolyamokba rendezték, a számításokat kernel-ek végezték, amelyek megfeleltek egy-egy függvénynek, amit a adatfolyam minden elemén végrehajtottak. A folyamat kimenete egy újabb adatfolyam. A kernel-ek Brook é s Sh alatt is C nyelven íródtak. Itt már nem jelentek meg a felhasználó előtt olyan kifejezések mint vertex vagy textúra, az absztrakció képes volt teljesen elfedni a GPU-k grafikus mivoltát. A későbbiekben többen is készítettek olyan library-ket amelyek magas szintű felületet biztosítanak a GPU-k párhuzamos számítási teljesítményének kihasználásához (Microsoft Accelerator, RapidMind), de az igazi áttörést a CUDA és az OpenCL jelentette. A CUDA (Compute Unified Device Architecture) az NVIDIA által 2007-ben bemutatott GPGPU platform és modell. Célja az NVIDIA GPU-k erejének felhasználása általános célú számítások elvégzésére. Segítségével a szoftverfejlesztők C, C++ vagy Fortran nyelven programozhatják a GPU-t, de léteznek harmadik fél által készített kiegészítések más népszerű nyelvekhez is, mint Java, Perl, Python, Ruby vagy Haskell. A C/C++-ban íródott CUDA programokhoz fordításához az NVIDIA LLVM alapú nvcc fordítója használható. A CUDA elérhető minden G80-asnál újabb NVIDIA GPU-ra, beleértve a GeForce, Quadro és Tesla sorozatot, valamint a három népszerű desktop operációs rendszer mindegyikére (Windows, Linux, Mac OS X). Egyik legnagyobb hátránya az OpenCL-el szemben, hogy csak és kizárólag NVIDIA GPU-kra elérhető. Az OpenCL (Open Computing Language) egy 2008-ban megjelent nyílt szabvány, amelyet kezdetben az Apple fejlesztett, de napjainkban a non-profit Khronos Group fejleszt. Segítségével olyan alkalmazások készíthetők amelyek több, különböző gyártó különböző eszközein képesek futni. Támogatja a párhuzamosságot, homogén vagy heterogén rendszereket, valamit eszközök széles skáláját mint CPU-k (x86, ARM valamint PowerPC architektúrákat egyaránt), GPU-k, DPS-ek (Digital Signal Processors), FPGA-k (Field-Programmable Gate Arrays), stb... Ezen eszközök programozására az OpenCL egy C-hez hasonló nyelvet használ (A C99 szbvány egy módosítását). 2.5. OpenCL Az OpenCL [5] négy fontos részre, úgynevezett modellekre bontható, melyek a következőek: 9

1. Platform Modell: Definiál egy absztrakt hardver modellt. A programozó ezt a hardver modellt használja amikor OpenCL C függvényeket (másnéven kerneleket) ír. Az eszközöket két csoportba sorolja: Host: egy kiemelt processzor amely felügyeli és irányítja a program végrehajtását. Device: az a processzor amely képes végrehajtani az OpenCL C kód-ot. Egy rendszerben több device is lehet. 2. Végrehajtási Modell: Leírja a host OpenCL környezet konfigurációját, valamint a kernel-ek futtatásának módját. Ide tartozik az OpenCL kontextus beállítása, host-device kommunikáció, valamint a kernel futása során használt konkurencia modell leírása. 3. Memória Modell: Definiál egy absztrakt memória hierarchiát. A kernel futás közben ezt az abstract memória felépítést látja függetlenül a device fizikai memória architektúrájától. Ez az absztrakt memória egyébként nagyban hasonlít napjaink GPU-inak memória hierarchiájához. 4. Programozási Modell: Leírja a konkurencia modell fizikai eszközre való leképzését. Napjaink személyi számítógépei tekinthetők olyan OpenCL rendszereknek amelyben található egy x86 architektúrájú host CPU, valamint egy GPU device. A platform modell leírja a host és device kapcsolatát. A host egy felparaméterezett kernelt ad a GPU-nak, amely majd végrehajtja azt. Ez a végrehajtási modell. A kernel a programozó által lefoglalt absztrakt memóriaterületen lévő adatokhoz fér hozzá, ezekkel dolgozik. Az OpenCL runtime és driver ezeket az absztrakt memória területeket leképzik a valós, fizikai memóriára. Végül a GPU végrehajtja a kerenl-t. 2.5.1. Platform Modell Az OpenCL platform modell egy egységes, magas szintű absztrakt ábrázolás, amely elrejti a háttérben lévő heterogén platform sajátosságait a programozó elől. Egy OpenCL platform mindíg tartalmaz pontosan egy host-ot, melynek feladata az OpenCL-en kívüli világgal való kapcsolattartás, beleértve az I/O műveleteket, vagy a program felhasználójával történő interakciókat. A host-hoz kapcsolódhat egy vagy több OpenCL device, amely majd végrehajtja az OpenCL függvényeket, vagy más néven kernel-eket. OpenCL device lehet egy CPU, GPU, DSP, FPGA vagy bármi, ami támogatja az OpenCL-t. Egy OpenCL device tovább bontható úgynevezett compute unit-okra (számítási egységek), röviden CU-kra. Minden egyes ilyen CU tartalmaz egy vagy több processing element-et (végrehajtó egység), röviden PE-kre. A valódi számításokat ezek a processig element-ek végzik. 10

2.6. ábra. Az OpenCL platform modell. Egy host és a hozzá kapcsolódó egy vagy több OpenCL device. Minden egyes device tartalmaz egy vagy több compute unit-ot, melyek mindegyike tartalmaz egy vagy több processig element-et 2.5.2. Végrehajtási Modell Egy OpenCL program két részből tevődik össze: Az egyik az úgynevezett host program, amely a host-on fut. A másik az ugynevezett kernel. Kernelből több is létezhet egy programon belül. Ezek a kernel-ek általában egyszerű függvények, amelyek bemenő adatokból valamilyen kimenetet állítanak elő és az OpenCL device-on futnak. Két típusú kernelt különböztetünk meg: Az OpenCL kernel-ek, olyan függvények amely OpenCL C nyelven íródtak, fordításuk pedig az OpenCL fordító feladata. Ezeket a kernel-eket minden OpenCL implementációnak támogatnia kell. A Natív kernel-ek olyan függvények amelyek az OpenCL-től függetlenül, azon kívül íródtak így az OpenCL kizearólag egy függvény pointer-en keresztül éri el őket. Ezek tipikusan a host programban, vagy külső library-ban deklarált függvények. A natív kernel-ek kezelését nem követeli meg az OpenCL specifikáció, ez csupán egy opcionális funkció. NDRange A Végrehajtási modell azt definiálja, hogy az OpenCL hogyan hajtsa végre a kerneleket. A kernel-ek futtatását a host program kezdeményezi. Amikor a host futtatni kíván egy kernelt, az OpenCL runtime létrehoz egy index tartományt (NDRange). A tartomány minden elemére létrejön egy kernel példány, ezeket work-item-nek nevezzük. Az egyes work-item-ek a tartománybeli indexükkel (más néven global ID) azonosíthatóak futás közben. Egy kernelhez tartozó összes work-item ugyanazokat az utasítás 11

szekvenciákat hajtja végre, ám az esetleges feltételes utasítások és eltérő bemenő adatok (például minden work-item a global ID-ja alapján választja ki a feldolgozandó adatot) miatt az eredmény eltérhet. A work-item-ek work-group-okba szerveződnek. A work-group-ok segítségével még finomabban bonthatjuk fel az index tartományunkat. Az egyes work-group-oknak saját, egyedi azonosítójuk van, továbbá a work-group-on belül minden egyes work-item egyedi lokális azonosítóval (local id) rendelkezik. Ennek következtében egy work-item pontosan azonosítható egy global ID-val, vagy egy work-group ID és egy local ID kombinációjával. Az egy work-group-ba tartozó work-item-ek konkurensen futnak egy compute unit feldolgozóegységein. Az OpenCL csak azt biztosítja, hogy az egy work-group-ba tartozó work-item-ek konkurens módon futnak, így arra nem hagyatkozhatunk hogy az egyes work-group-ok számításai vagy a kernel végrehajtások egyszerre történnek (még ha ez gyakran így is van). Az NDRange lehet 1, 2 vagy 3 dimenziós. Minden work-item lokális és globális azonosítója egy N dimenziós vektor, ahol n az NDRage dimenziószáma. Az indexelés 0-tól kezdődik és N 1-ig tart. Vegyünk például egy 2D-s NDRange-et, ahol a work-item-ek global ID-ját jelölje g. Jelölje továbbá G az index tartomány méretét. Ekkor az egyes work-item-ek koordinátái (g x, g y ) (G x, G y ). Az így kapott index teret felbonthatjuk W számú work-group-ra amelyeket jelöljük w-vel. Az OpenCL megköveteli, hogy a work-group-ok száma minden dimenzióban maradék nélkül ossza az NDRange méretét, ezáltal minden egyes workgroup azonos elemszámú. Ez az elemszám meghatároz egy lokális index teret minden dimenzióban, amelynek méretét jelölje L, elemeit pedig l. Vagyis az NDRange-ünk (G x, G y ) méretű index tartománya felbontható W x W y workgroup-ra. Minden work-group L x L y méretű, vagyis: L x = G x /W x L y = G y /W y Egy work-item meghatározható a (g x, g y ) globális ID-jával, vagy az (l x, l y ) lokális és (w x, w y ) work-group ID-ja kombinációjaként: g x = w x L x + l x g y = w y L y + l y Vagy ha arra van szükség a work-item local és work-group ID-ja meghatározható mint: w x = g x /L x w y = g y /L y 12

és l x = g x mod L x l y = g y mod L y Az előbbiekben feltételeztük, hogy az indexek minden dimenzióban 0-tól kezdődnek. Az OpenCL 1.1 azonban lehetőséget ad arra, ahogy a globális index terünkhöz meghatározzunk egy offset vektort, ezáltal az indexeket az adott problémához igazíthatjuk. Jelöljük ezt a offset-et o-val. Ekkor az előbbi egyenletünk a következőképp módosul: g x = w x L x + l x + o x g y = w y L y + l y + o y A 2.7 ábra szemlélteti egy 2D-s NDRange felépítését. Az ábrán látható minden kis négyzet egy work-item, az offset minden dimenzióban 0. A szürke négyzet az (1, 1) indexű work-group tagja, (2, 1) lokális, valamint (6, 5) globális indexel. 2.7. ábra. Példa egy 2D-s NDRange felépítésére OpenCL kontextus Egy OpenCL alkalmazásban a lényegi munkát az OpenCL device végzi, ám a host-nak is nagyon fontos szerepe van. A host hozza létre az OpenCL kernel-ek kontextusát. Ő 13

határozza meg az NDRange-et, valamint azokat a sorokat (OpenCL queue) amelyek meghatározzák, hogy hogyan és mikor hajtódjanak végre a kernel-ek. Az OpenCL kontextus meghatároz egy környezetet, amelyben a kernel-ek végrehajtódnak. Elemei: Device-ok: a host rendelkezésére álló OpenCL képse eszközök. Kernel-ek: azok az OpenCL függvények, amelyek majd az OpenCL device-on futnak. Program objektumok: a programok forráskódja, amelyek a kernel implementációkat tartalmazzák. Memória objektumok: olyan memória területek, amelyek az OpenCL device számára láthatóak és elérhetőek. Az itt található adatokkal dolgozhatnak a kernel-ek. Ezt a környezetet a host az OpenCL API-n keresztül képes manipulálni. A host program feltérképezi a rendszerben elérhető OpenCL erőforrásokat, majd kiválasztja a megfelelő OpenCL device-t (esetleg device-okat) az aktuális kontextus számára. A kontextus tartalmaz még egy vagy több program objektumot. Ezek a program objektumok futási időben fordulnak. Erre azért van szükség, mivel a programozó a program írása közben nem tudhatja, hogy a felhasználó milyen CPU-n, GPU-n, esetleg más eszközön fogja futtatni az alkalmazását. A program objektumok forráskódja lehet egy statikus string a host programban, betölthető futási időben egy forrás file-ból, vagy akár dinamikusan generálható a host program által. A kernel a kontextusban definiált memória objektumokon keresztül éri el a memóriát. Egy heterogén rendszerben gyakran több memória címtér is található. Egy CPU host és egy GPU device például teljesen különálló memóriát használnak. Erre a problémára ad megoldást az OpenCL memória objektum modellje, amely lehetővé teszi, hogy a host memóriájában lévő adatokat átmozgathassuk a device memóriájába és vissza. Parancs sorok A host és a device közti interakcióra az OpenCL úgynevezett parancsokat (command) használ, amelyek egy parancs soron (command-queue) keresztül jutnak el a host-tól a device-ig. Ezek a parancsok mindaddig a sorban maradnak ameddig azt a device végre nem hajtja. A parancs sorokat a host hozza létre egy kiválasztott device-hoz, miután már definiálta az OpenCL kontextust. Az OpenCL három különböző típusú parancsot támogat: Kernel végrehajtási parancs, amely végrehajt egy kernel-t a device-on. Memória parancs, amely adatot mozgat a host és a device közt. Szinkronizációs parancs, amely képes befolyásolni a parancsok végrehajtási sorrendjét. 14

Egy általános host programban a programozó definiálja az OpenCL kontextust, a parancs sorokat, memória és program objektumokat, valamint létrehozza a program szamara szükséges adatszerkezeteket. Ezután a memória objektumokat a host memóriájából átmozgatja a device memóriájába, meghatározza a kernel argumentumokat, majd továbbítja azokat a parancs sorba, ahol végrehajtásra várnak. Amikor a kernel végrehajtása befejeződött az eredmény a memória objektumokon keresztül visszakerülhet a host-hoz. Több kernel esetén a kernelek közt lehetnek interakciók. Például egy kernel eredménye lehet egy másik kernel bemenete. Ebben az esetben szinkronizációs parancsok alkalmazására van szükség, hogy biztosítsuk a kernel-ek végrehajtási sorrendjét. A parancsok és a host mindig aszinkron módon futnak. A host továbbít egy parancsot a sorba, majd fojtatja a végrehajtást, nem vár a parancs befejezésére. Abban az esetben ha mégis szükség van arra, hogy a host megvárja egy parancs végrehajtás végét, kényszeríthetjük erre explicit szinkronizációs parancsokkal. Egy parancs sorban a parancsok két módon futhatnak: In-order: a parancsok abban a sorrendben kerülnek végrehajtásra amelyben bekerültek a sorba. Vagyis az a parancs hajtódik végre hamarabb, amelyik előbb bekerül a sor-ba. Out-of-order: a parancsok nem a bekerülési sorrendben hajtódnak végre, az egyes parancsok végrehajtásával a rendszer nem várja meg, hogy az előző befejeződjön. Az in-order módot mindent OpenCL platformnak támogatnia kell, azonban az outof-order mód csupán opcionális. Az out-of-order végrehajtás akkor jön jól, ha olyan kernel-eink vannak, amelyek végrehajtása különböző hosszúságú időt vesz igénybe. Ez esetben az in-order végrehajtás nem biztos, hogy tudja biztosítani (vagy csak nagyon nehezen) a megfelelő terheléselosztást, ezáltal futás közben lehetnek olyan időszakok, amikor a hardvernek csak egyes részei dolgoznak. Ezt elkerülendő, out-of-order végrehajtás esetén a parancsok bármilyen sorrendben végrehajtódhatnak, vagyis ha egy CU végzett a munkával, azonnal belekezdhet egy új parancs végrehajtásába. Ezt automatikus terheléselosztásnak nevezik. 2.5.3. Memória Modell Az OpenCL két típusú memória objektumot különböztet meg: Buffer objektumok: Folytonos memória blokk, amely elérhető a kernel számára. Tetszőleges adatstruktúrákat tárolhatunk benne, pointeren keresztül érhető el. Image objektumok: Kifejezetten képek számára létrehozott memória terület. Az OpenCL memória modell öt különböző memória területet különböztet meg: Host memória: Ez a memória terület csak a host számára látható 15

Globális memória: Ehhez a memória területhez hozzáfér az összes work-group összer work-item-je. Ezek képesek olvasni vagy írni bármely memóriaobjektumot amely a globealis memóriában található. Az OpenCl device-tól függően a globális memória írás/olvasás lehet cach-elt. Konstans memória: A globális memória egy része, amely a teljes futási idő alatt változatlan marad. Az itt található memóriaobjektumokat a host hozza létre és inicializálja. Minden work-item olvashatja. Lokális memória: Egy work-group-hoz tartozó memória rész, amely az összes, adott Work-group-ban lévő work-item számára elérhető, ezáltal használható a work-group-on belüli adatmegosztásra. Egyes hardvereken fizikailag is különálló memóriarész, egyébként a globális memória speciális, elkülönített része. Privát memória: Egy work-item-hez tartozó memória terület, egyetlen másik work-item számára sem érhető el. A memória területeket és azok kapcsolatát a work-group-okkal és work-item-ekkel a 2.8 ábra szemlélteti. 2.8. ábra. Az OpenCL memória modell 2.5.4. Programozási Modell Az OpenCL programozási modell meghatározza, hogy hogyan ültessünk át párhuzamos algoritmusokat OpenCL-re. Az OpenCL két különböző párhuzamos programozási modellt támogat: feladatközpontú (task-parall model) és adatközpontú (data-parall model) modellt. 16

Adatközpontú párhuzamosság Adatközpontú párhuzamosságról akkor beszélhetünk, ha egy utasítás szekvenciát hajtunk végre egy nagy adathalmaz egyes részein konkurens módon, egymástól függetlenül. Az OpenCL esetén ugyanazt a kernel-ben megírt műveletsorozatot hajtják végre az egyes work-item-ek az adat különböző részein. Komplexebb esetben szükség lehet arra, hogy az egy work-group-ba tartozó work-itemek megosszanak valamilyen adatot egymással. Erre a lokális memórián keresztül van lehetőség. Ilyen esetekben szükség lehet a work-item-ek szinkronizálására, hogy a kívánt eredmény érjük el. Az azonos work-group-ban lévő work-item-ek esetén erre használhatunk work-group barrier-eket. Ezáltal biztosíthatjuk, hogy a work-group összes work-item-je elért egy bizonyos pontig a kernel futtatásában, mielőtt továbblépnének. Az OpenCl 1.1 specifikáció nem biztosít lehetőséget a különböző work-group-okban található work-item-ek szinkronizációjára. Az OpenCL work-item-ek és work-group-ok szintjén is értelmezi az adat párhuzamosságot. A programozónak lehetősége van meghatározni a work-group-ok pontos méretét, vagy csak az NDRange mértét definiálja, a work-group-okét pedig a rendszerre bízza. Előbbit az OpenCL explicit modellnek, utóbbit implicit modellnek nevezi. Abban az esetben, ha a kernel nem tartalmaz feltételes elágazást, minden egyes workitem ugyanazokat a műveleteket hajtva végre az adat hozzá tartozó darabján. Ekkor SIMD (Single Instruction Multiple Data) modellről beszélhetünk. Ha azonban a kernel tartalmaz feltételes elágazást, az egyes work-item-ek által végrehajtott utasítások eltérhetnek, vagyis bár minden work-item ugyanazt a "programot" (ebben az esetben kernelt) hajtja végre, az elvégzett munka különböző lehetnek. Ez a modell SPMD (Single Program Multiple Data) néven ismert. Az OpenCL mindkét modellt támogatja, ám bizonyos rendszereken a SIMD modell sokkal hatékonyabbnak bizonyul. Feladatközpontú párhuzamosság Feladatközpontú párhuzamosság esetén a különböző working-item-ek eltérő utasítításszekvenciákat (esetünkben kerneleket) hajthatnak végre különböző, vagy egyazon adaton. Ilyen esetekben gyakran szükség van az egyes szálak közti kommunikációra, adatcserére. 17

3. fejezet AES Az AES (Advanced Encryption Standard) egy kriptográfiai algoritmus, amelynek alapja a Joan Daemen és Vincent Rijmen által kidolgozott Rijndael algoritmus család. Az AES egy iteratív, szimmetrikus blokk kódoló. Három szabványosított változata az AES- 128, AES-192 és AES-256. Ezek rendre 128, 192 és 256 bit hosszú kulcsokat használnak, a blokk méret mindhárom esetben fixen 128 bit. Bár ismertek olyan támadások az AES ellen amelyek gyorsabbak mint egy brute-force támadás, ezek a támadások a napjainkban rendelkezésre álló eszközökkel nem hajthatóak végre belátható időn belül, így még manapság is ez az egyik legbiztonságosabb, széles körben hasznát szimmetrikus kulcsú titkosítási eljárás. 3.1. Blokkok, körök, körfüggvények Mivel az AES egy blokk kódoló, így a bemenő adatokat fix hosszúságú (128 bit) blokkokra bontja, majd ezeket a blokkokat külön-külön titkosítja. Az egyes blokkokat az algoritmus state-nek nevezi, amelyekre egy két dimenziós byte tömbként tekint: 3.1. ábra. A state változó Kódolás és dekódolás során az algoritmus egy state változón többször, egymás után, ciklikusan végrehajt egy transzformációs folyamatot. A folyamatot egyes részeit az algoritmus köröknek (round) nevezi. Az, hogy ez hányszor történik meg, a kulcs méretétől függ. A körök számát Nr jelöli, ekkor a kulcsméret és a körök száma közötti összefüggés: 18

Kulcshossz Blokkméret Körök száma (bit) (bit) AES-128 128 128 10 AES-192 192 128 12 AES-256 256 128 14 Az AES transzformációs folyamatának egyes lépéseit körfüggvényeknek nevezzük. Ezek mindegyike egy-egy byte-orientált művelet: SubBytes, amely egy byte helyettesítés, ShiftRows, amely a state változó sorainak változó mértékű eltolása, MixColums, amely a state változó egyes oszlopainak és a bennük lévő adatok összekeverése, AddRoundKey, amely a titkos kulcs felhasználásával kulcsfüggővé teszi a folyamatot. Az utolsó kör minden esetben eltér a többitől, ott a MixColums lépés kimarad. 3.1.1. SubBytes A SubBytes transzformáció egy helyettesítő táblát használ, amely segítségével a state struktúra minden egyes byte-ját egy újabbal helyettesíti. Az új érték természetesen minden esetben az eredeti byte-tól függ, az összefüggést a következő invertálható affin transzformáció írja le: b i = b i b (i+4) mod 8 b (i+5) mod 8 b (i+6) mod 8 b (i+7) mod 8 c i (3.1) ahol, 0 i < 8, b i az eredeti b byte i-ik bitje, b a b byte új értéke, és c i a konstans c = 0x63 byte i-ik bitje. A transzformáció leírható mátrix alakban is: b 0 1 0 0 0 1 1 1 1 b 1 1 1 0 0 0 1 1 1 b 2 1 1 1 0 0 0 1 1 b 3 1 1 1 1 0 0 0 1 = b 4 1 1 1 1 1 0 0 0 b 5 0 1 1 1 1 1 0 0 b 6 0 0 1 1 1 1 1 0 0 0 0 1 1 1 1 1 b 7 b 0 b 1 b 2 b 3 b 4 b 5 b 6 b 7 c1 1 0 0 + 0 1 1 0 (3.2) 19

3.2. ábra. Helyettesítő tábla előre kiszámított hexadecimális értékei. A hexadecimális xy értékű byte helyettesítési értéke az x-ik sor és y-ik oszlop találkozási pontjában található. 3.1.2. ShiftRows A ShiftRows transzformáció egy ciklikus balra eltolást jelent a state változó sorain, különböző mértékben. Az első sor változatlan marad, a második sor byte-jai egy, a harmadik sor byte-jai kettő még a negyedik sor byte-jai három pozícióval kerülnek eltolásra. Képlettel: s r,c = s r,(c+shift(r,nb)) mod Nb, 0 < r < 4 és 0 c < Nb (3.3) ahol r a sor száma, c az oszlop száma, Nb pedig a blokk méret négy byte-os szavakban, jelen esetben 4. 3.3. ábra. A ShiftRows transzformáció hatása a state struktúrára. 20

3.1.3. MixColums A MixColuns transzformáció a State struktúra egyes oszlopait a GF (2 8 ) véges test feletti negyedfokú polinomként értelmezi, majd ezeket a polinomokat megszorozza egy fix a(x) polinommal. Végül veszi az eredmény modulusát a x 4 + 1 polinommal, ez lesz a State uj oszlopa. vagy mátrix formában: minden 0 c < 4 esetén. a(x) = {03x 3 + {01x 2 + {01x + {02 (3.4) s 0,c s 1,c s 2,c s 3,c 02 03 01 01 = 01 02 03 01 01 01 02 03 03 01 01 02 A transzformáció eredményeként egy oszlop új byte-jai a következőek: s 0,c s 1,c s 2,c s 3,c (3.5) s 0,c = ({02 s 0,c ) ({03 s 1,c ) s 2,c s 3,c s 1,c = s 0,c ({02 s 1,c ) ({03 s 2,c ) s 3,c s 2,c = s 0,c s 1,c ({02 s 2,c ) ({03 s 3,c ) s 3,c = ({03 s 0,c ) s 1,c s 2,c ({02 s 3,c ) 3.1.4. AddRoundKey Az AES minden egyes körhöz hozzárendel egy úgynevezett körkulcsot. Ez a körkulcs ugyanúgy 128 bit-es mint a state struktúra. Az AddRoundKey transzformáció minden kör végén az adott körkulcsot adja hozzá (XOR művelettel) a state struktúrához, ezáltal kulcsfüggővé téve a kör transzformációit. Képlettel: [ ] ] ] s 0,c s 1,c s 2,c s 3,c = [s 0,c s 1,c s 2,c s 3,c [w round 4+c, 0 c < 4 (3.6) ahol round az aktuális kör száma, w i pedig a kiterjesztett kulcs i-ik szava. A kiterjesztett kulcsról a későbbiekben lesz szó. A transzformáció hatását a 3.4-ös ábra mutatja be, ahol l = round Nb. 3.2. A titkos kulcs Mivel az AES a szimmetrikus kódolók családjába tartozik, így a kódolás és dekódolás folyamán ugyanazt a titkos kulcsot használja. A titkos kulcs hossza a szabvány szerint 128, 192 vagy 256 bit lehet. 21

3.4. ábra. A State körkulcs "összeadása". 3.2.1. Kulcskiterjesztés Ahogy azt már a 3.1.4 bekezdésben említettük, az AES a kódolás/dekódolás folyamán minden kör végén egy körkulcs segítségével teszi kulcsfüggővé a folyamatot. Ezek a körkulcsok a kiterjesztett kulcs részeit képzik. A kiterjesztett kulcs a titkos kulcsból származtatható. Jelölje a titkos kulcs hosszát 4 byte-os szavakban N k, ekkor a kulcskiterjesztés algoritmusa: Nk 6 esetén: KeyExpansion(byte Key[4*i], word W[Nb*(Nr+1)]) { for(i = 0; i < Nk; i++) W[i] = (Key[4*1], Key[4*1+1], Key[4*1+2], Key[4*1+3]); for(i = Nk; i < Nb * (Nr + 1); i++) { temp = W[i - 1]; if(i % Nk == 0) temp = SubByte(RotByte(temp))^Rcon[i / Nk]; W[i] = W[i - Nk]^temp; Nk > 6 esetén: KeyExpansion(byte Key[4*i], word W[Nb*(Nr+1)]) { for(i = 0; i < Nk; i++) W[i] = (Key[4*1], Key[4*1+1], Key[4*1+2], Key[4*1+3]); for(i = Nk; i < Nb * (Nr + 1); i++) { temp = W[i - 1]; if(i % Nk == 0) temp = SubByte(RotByte(temp))^Rcon[i / Nk]; else if (i % Nk == 4) temp = SubByte(temp); 22

W[i] = W[i - Nk]^temp; 3.2.2. A körkulcsok kiválasztása Az i-ik körkulcs minden esetben a kiterjesztett kulcs i-ik, i 4 + 1-ik, i 4 + 2-ik és i 4 + 3-ik szava. 3.5. ábra. A körkulcsok kiválasztása. 3.3. Kódolás és dekódolás Az üzenetek kódolása a következő lépésekből áll: Egy kezdeti AddRoundKey transzformáció Majd Nr 1 db normál kör, melynek lépései a következős: SubBytes, ShiftRows, MixColumns, AddRoundKey Végül egy speciális kör amelyből kimarad a MixColumns lépés. Az üzenetek dekódolása során a körfüggvények inverz műveleteit használjuk és a körök sorrendje is megfordul: Első a speciális kör visszafelé, amelyből kimarad a InvMixColumns: AddRoundKey InvShiftRows, InvSubBytes, Majd Nr 1 db normál kör visszafelé: AddRoundKey InvMixColumns, InvShiftRows, InvSubBytes, Végül egy AddRoundKey transzformáció. 23

3.4. Az algoritmus optimalizációja 32-bites processzorokra Azokon a processzorokon amelyek szóhossza legalább 32 bit lehetőségünk van a körfüggvényeket összekombinálni, azokat előre kiszámítani és az eredményeket helyettesítőtáblákban (melyeket T-tábláknak neveznek) elhelyezni. Ezáltal az algoritmus drasztikusan gyorsítható, hiszen a komplex műveletsorok kiválthatóak néhány tömb elem eléréssel valamint néhány XOR művelettel. Jelölje az egyes körök bemeneti state struktúráját a, a kimenetet pedig e, továbbá a i, j az a i-ik sorának és j-ik oszlopának találkozásánál lévő byte-ot, a j pedig a j. Ekkor az egyes transzformációk leírhatóak mint: SubBytes: ] b i,j = S [a i,j (3.7) ShiftRows: c 0,j c 1,j c 2,j = b 0,j b 1,j C1 b 2,j C2 (3.8) c 3,j b 3,j C3 MixColumns: d 0,j d 1,j d 2,j d 3,j 02 03 01 01 = 01 02 03 01 01 01 02 03 03 01 01 02 c 0,j c 1,j c 2,j c 3,j (3.9) AddRoundKey: e 0,j e 1,j e 2,j = d 0,j d 1,j d 2,j k 0,j k 1,j k 2,j (3.10) e 3,j d 3,j k 3,j A lépések kombinációjával egy kör a következőképpen fejezhető ki: e 0,j e 1,j e 2,j e 3,j 02 03 01 01 S[a 0,j ] = 01 02 03 01 S[a 1,j C1 ] 01 01 02 03 S[a 2,j C2 ] 03 01 01 02 S[a 3,j C3 ] k 0,j k 1,j k 2,j k 3,j (3.11) 24

A fenti mátrixművelet kifejezhető vektorok lineáris kombinációjaként: e 0,j 02 03 01 01 e 1,j 01 02 03 01 = S[a e 2,j 0,j ] S[a 01 1,j C1 ] S[a 01 1,j C2 ] S[a 02 1,j C3 ] 03 03 01 01 02 e 3,j k 0,j k 1,j k 2,j k 3,j Ezután a helyettesítőtábláink: S[a] 02 S[a] 03 S[a] S[a] S[a] S[a] 02 S[a] 03 S[a] T 0 [a] =, T S[a] 1 [a] =, T S[a] 2 [a] =, T S[a] 02 3 [a] = S[a] 03 S[a] 03 S[a] S[a] S[a] 02 Kaptunk négy táblát, egyenként 256 elemmel. Minden elem egy 4 byte-os szó, azaz a táblák összesen 4KB memóriát foglalnak. Ezek segítségével az egyes körök: e j = T 0 [a 0,j ] T 1 [a 0,j C1 ] T 2 [a 0,j C2 ] T 3 [a 0,j C3 ] k j (3.12) Vagyis ezzel a megoldással egy oszlop értékeinek kiszámítását egy körben 4 tömbelem elérésre és 4 XOR műveletre redukáltuk, vagyis a teljes kör 12 tömbelem elérés és 12 XOR művelet. Mivel az utolsó körből hiányzik a MixColumns transzformáció, így ott nem használhatóak a T-táblák. Helyette szükség lenne a SusBytes lépés helyettesítő táblájára. Ez a tábla azonban bitmaszkolássál visszaállítható a T-táblákból. 25

4. fejezet A Program A diplomamunkámhoz készített alkalmazás célja, hogy bemutassam, milyen előnyök származhatnak a grafikus processzorok általános célú számításokra történő felhasználásából. Erre a célra egy, a kriptográfiában jól ismert és széles körben alkalmazott szimmetrikus titkosítási algoritmust, az AES-t választottam. Ez az algoritmus nagyon jól párhuzamosítható, így megfelelő példaként szolgál a GPGPU technológia bemutatásához, illetve jól szemléltethető vele a mai modern grafikus processzorok számítási teljesítménye. Az implementáció két nagyobb részre bontható: Az algoritmusokat megvalósító rész, amelyet C nyelven implementáltam, valamint a felhasználói felület, amelyhez C++-t illetve a Qt keretrendszert használtam. A program több különböző AES implementációt is tartalmaz. Egyrészt az eredeti algoritmus szekvenciális implementációját C nyelven, valamint ugyanezt az algoritmust párhuzamosan, OpenCL-el megvalósítva. Másrészt egy 32 bit-es processzorokra optimalizált változatot, melyre a későbbiekben T-táblás implementációként fogunk hivatkozni, ugyancsak szekvenciálisan, illetve párhuzamosan, OpenCL-el implementálva. Ezáltal az alkalmazás hat különböző algoritmust/implementációt tartalmaz: CPU-n futó implementációk: hagyományos algoritmus szekvenciális C implementációja hagyományos algoritmus párhuzamos OpenCL implementációja optimalizált algoritmus szekvenciális C implementációja optimalizált algoritmus párhuzamos OpenCL implementációja GPU-n futó implementációk: hagyományos algoritmus párhuzamos OpenCL implementációja optimalizált algoritmus párhuzamos OpenCL implementációja A program funkcionalitása két fő részre bontható. Egyrészt képes tetszőleges file-t titkosítani és visszafejteni, másrészt került bele egy benchmark mód, amely az egyes algoritmusok és implementációk teljesítményének összehasonlítására szolgál. 26

4.1. Felhasználói Felület A felhasználói felületet C++ nyelven, a Qt keretrendszer segítségével készítettem. 4.1.1. Qt A Qt egy cross-platform alkalmazás keretrendszer, melynek célja, hogy ugyanaz a kódbázis változtatások nélkül (vagy csak nagyon kevés változtatással) natívan futtatható legyen különböző hardver és szoftver környezetben legyen az desktop (Windows, Linux, OS X) vagy mobil (ios, Android, QNX/BlackBarry 10 és részben Windows RT). A Qt GUI elemei minden platformon natív kinézettel rendelkeznek, így a Qt-vel készült alkalmazások nem lógnak ki a platform natív alkalmazásai közül, így ideálisak cross platform alkalmazások felhasználói felületének elkészítéséhez. Természetesen nem csak felhasználói felületek készítésére alkalmas, készíthetünk vele GUI nélküli, command-line alkalmazásokat is. Programozási nyelve standard C++ néhány kiegészítéssel, mint például a signal/slot mechanizmus amely a különböző event-ek kezelését könnyíti meg. Ezeket az extra funkciókat a Qt meta object compiler-e (röviden moc) teszi lehetővé, amely a kódban található speciális makrókat, vagy ha úgy tetszik annotációkat értelmezve, fordítás előtt kiegészíti az általunk írt C++ kódot, ezzel lehetővé téve olyan funkciókat amelyeket a natív C++ nem tartalmaz, mint például az előbb említett signal/slot mechanizmus, introspection, vagy az aszinkron függvényívás. E mellett használhatjuk a Qt saját JavaScript/CSS szerű programozási nyelvét is melynek neve QML, de rengeteg más nyelvhez létezik nem hivatalos támogatás mint például a Python, Java, JavaScript, Ada vagy a Haskell. A Qt több különböző licence feltétellel is használható. A nyílt forráskódú GPLv3 és LGPLv2.1 licence-ek mellett létezik egy kereskedelmi változata is. Jelenleg legfrissebb verziója a 2014 májusában megjelent Qt 5.3, de 2014 decemberében tervezik megjelentetni a Qt 5.4-et. 27

4.2. A program használata Az általam készített program kétféle feladatot képes ellátni. Az első, kódolás-dekódolás módban az alkalmazásban szereplő algoritmusok és azok implementációjának helyessége mutatható be azáltal, hogy az alkalmazás tetszőleges file kódolására, majd dekódolására képes. A második, Benchmark mód az algoritmusok és implementációk közötti teljesítménykülönbségeket hivatott bemutatni. Itt kiválaszthatjuk a tesztelésre használt file vagy file-ok méretét és kódoláshoz használt algoritmusokat vagy implementációkat. Ezután az alkalmazás lefuttatja az összes kiválasztott algoritmust a kiválasztott méretű teszt file-okon, majd egy összesítő táblázatban megjeleníti a futási időket. 4.2.1. Kódolás-dekódolás Az alkalmazást elindítva a file kódolás/dekódolás fül fogadja a felhasználót. Ezen a felületen a felhasználó kiválaszthat egy tetszőleges file-t amelyet kódolni szeretne, majd megadhatja a folyamat néhány paraméterét az Options csoportban: Algorithm: A kódoláshoz használt algoritmus kiválasztása. Normal: Hagyományos AES algoritmus T-table: 32 bit-es processzorokra optimalizált AES algoritmus 28