PYTHON - KRYPTAUSOPAS Lappeenrannan teknillinen yliopisto 2007 Jussi Kasurinen
Johdanto Tässä oppaassa esitellään Python-ohjelmointikielen salaus- ja varmennustekniikoiden kirjastomoduuli Python Cryptography Toolkit. Oppaassa oletetaan, että ymmärrät Pythonohjelmointikielen perusasiat, jotka on opetettu mm. Python-ohjelmointioppaassa [1], mutta et välttämättä tarvitse aiempaa tietoa salausalgoritmeista. Huomaa myös, että oppaan luvut 2 (Huomioita PCT-moduulista) - 7 (Muita työkaluja) on vapaasti käännetty alkuperäisen kirjastomoduulin mukana toimitettavasta manuaalista [2], mutta siihen on lisätty mukaan myös uusia esimerkkejä sekä tarpeen mukaan täydentävää tekstiä. Kappaleet, joiden varsinaista asiasisältöä on muokattu, tai jotka ovat tätä opasta varten kirjoitettua täydentävää materiaalia, on merkattu tekstin vasemmassa reunamarginaalissa olevalla merkinnällä. Asennus Python Cryptography Toolkit (PCT) asennetaan tietokoneelle erillisenä pakettina Python Imaging Libraryn tai Py2Exe-pakkaajan tavoin. Tämä tarkoittaa siis sitä, että aloittaaksesi kryptauskirjastolla työskenetelyn, tarvitset verkkoyhteyden tietokoneeseesi. Tämän jälkeen toimi seuraavalla tavalla: Hae Python Cryptography Toolkit uusin versio (kirjoitushetkellä 2.0.1) osoitteesta http://www.voidspace.org.uk/python/modules.shtml#pycrypto. Valitse oikea paketti käyttämäsi tulkin version mukaisesti. Asenna PCT normaalin lisäkirjaston asennuskaavan mukaisesti. Asennus on suoraviivainen, mutta mikäli tarvitset ohjeet asennusta varten, löytyy sellaiset esimerkiksi Pythongrafiikkaohjelmointioppaasta [3]. Oppaan ohjeissa asennetaan Python Imaging Library, mutta asennusvaiheet ja tapahtumat ovat muutoin täysin samat. Asennuksen jälkeen käynnistä Python-tulkki ja kirjoita interaktiiviseen ikkunaan komento import Crypto huomaa iso C-kirjain. Mikäli tulkki ei antanut virheilmoitusta, olet asentanut PCT:n onnistuneesti ja voit jatkaa eteenpäin oppaan kanssa työskentelyä. 1
Huomioita PCT-moduulista Alkuperäinen lähde Python Crypthography Toolkit Manual, http://www.amk.ca/python/writing/pycrypt/ Python Cryptography Toolkit on kehitetty toimimaan luottavana sekä vakaana alustana, jonka päälle on mahdollista kirjoittaa ohjelmia, jotka vaativat tiedon salaukseen tai varmentamiseen tarvittavia funktioita. Keskeinen tavoite moduulin toteutuksessa onkin ollut toteuttaa yksinkertainen sekä yhdenmukainen rajapinta toistensa kaltaisille funktioille. Esimerkiksi kaikki salausobjektit (block cipher objects) sisältävät samannimiset metodit sekä palautusarvot sekä tukevat samoja palautemoodeja. Tarkastuslukujen (hash) laskemiseen käytettävillä funktioilla on tekniikoista johtuen erilaiset käyttöliittymät, mutta niiden yleinen toiminta on pyritty yhdenmukaistamaan. Lisäksi osalle funktioista on määritelty toiminta PEP (Python Enhancement Proposal)-dokumentteina [4,5]. Tällä toimenpiteellä pyritään helpottamaan salausalgoritmien korvaamista uudemmilla ja varmemmilla algoritmeilla. Jos olet aiemmin luonut ohjelman, joka käyttää esimerkiksi DESalgoritmia salauksen toteuttamiseen, voit helposti vaihtaa sen esimerkiksi AES-algoritmiin muuttamalla sisällytyskäskyn muodosta from Crypto.Cipher import DES muotoon from Crypto.Cipher import AES Tämän lisäksi riittää kun korvaat vielä käskyt DES.new() käskyllä AES.new(). Myös omien moduulien kirjoittaminen on helppoa: voit esimerkiksi toteuttaa moduulin joka käyttää tätä rajapintaa ja täten luoda salauksen, joka hyödyntää usean salausalgoritmin yhdistelmiä. Osa moduuleista on toteutettu C-kielellä laskentanopeuden parantamiseksi; muut taas Pythonilla muokattavuuden helpottamiseksi. Yleisesti tämä tarkoittaa sitä, että matalan tason laskentafunktiot, kuten salakirjoittajat (ciphers) sekä varmistussummalaskurit (hash functions) on toteutettu C- kielellä, kun taas niiden käyttämiseen tarkoitetut vähemmän laskentatehoa vaativat - funktiot on toteutettu Pythonilla. Kannattaa myös huomioida, että kun puhumme nopeudesta, tarkoittaa se 500 MHz Pentium 2 koneella laskettuja suoritusaikoja. Tarkka nopeus tietenkin riippuu monesta muustakin tekijästä, mutta oppaan aikavertailut ovat suuntaa antavia. Moduulien ohjelmakoodi on vapaasti muokattavissa ja levitettävissä. Kuitenkaan se ei vielä tarkoita sitä, että kaikki koodi olisi vapaasti käytettävissä: osa tähänkin moduuliin sisällytetyistä salausmekanismeista on patentoitu kaupallista käyttöä varten, joten mikäli toteutat kaupalliseen 2
levitykseen tarkoitettua projektia, varmista että käyttämäsi salausmekanismi ei kuulu näihin. Muussa tapauksessa joudut itse sopimaan patentinhaltijan kanssa asiasta. Lisäksi joissain maissa (esim. Yhdysvallat) salausalgoritmien käyttöä ja levitystä valvotaan kansallisilla lailla, joten varmista lisäksi ettet riko paikallisia lakeja. Suomessa kyseisiä rajoitteita tai lakeja ei tiettävästi ole, joten voit vapaasti käyttää ja levittää tekemiäsi ohjelmia kunhan et riko kaupallisen käytön lisenssiehtoja. Myöskään salausalgoritmien salauksen varmuus ei ole itsestäänselvyys. Osa salausalgoritmeista on onnistuttu murtamaan joko yksittäistapauksissa tai täydellisesti, tehden salauksesta triviaalin. Tästä puhutaan oppaassa myöhemmin lisää, mutta tärkeintä kaikilla näillä varoituksilla on tarkoitus: mikäli työskentelet ohjelman kanssa jonka haluat olevan oikeasti turvallinen, niin käytä ennemmin hieman aikaa taustatutkimukseen ennen kuin aloitat työstämään projektiasi huonoilla tai viallisilla osilla. 3
Crypto.Hash: Tarkastuslukufunktioita Alkuperäinen lähde Python Crypthography Toolkit Manual, http://www.amk.ca/python/writing/pycrypt/ Tarkastuslukufunktiot (hash-functions) ottavat syötteenään satunnaisen mittaisia merkkijonoja lähtöarvoina ja tuottavat tietyn mittaisia tulosjonoja eli tarkastussummia, joiden arvo riippuu annetusta lähtöarvosta. Tarkastulukufunktiot toimivat siten, että annettua lähtöarvoa ei pitäisi pystyä päättelemään palautetusta tulosjonosta, mutta lähtöarvon oikeellisuus pystytään varmentamaan palautetun tarkastusluvun avulla. Yksinkertaisimmillaan tämä tarkoittaa vaikkapa funktiota, joka laskee yhteen kaikki merkkijonon bittiarvot, ja ottaa tuloksesta jakojäännöksen arvolla 256: >>> sana = "Sihijuoma" >>> tulos = 0 >>> for i in range(0,len(sana)): tulos = tulos + ord(sana[i]) >>> tarkastusluku = tulos % 256 >>> tarkastusluku 169 >>> Esimerkissä syötteenä antamallemme merkkijonolle Sihijuoma saatiin tarkastusluvuksi arvo 169. Vastaavasti jos olisimme toteuttaneet laskennan merkkijonolle sihijuoma (pieni s-kirjain), olisi tulos ollut 201, ja merkkijonolle Ketsuppijäätelö 194. Kuten huomaamme, yhdessäkään tapauksessa emme pysty päättelemään, mikä alkuperäinen merkkijono oli, sekä samalla todistimme, että yhden merkin muuttaminen vaihtaa tarkastusluvun arvoa merkittävästi. Ollakseen varma sekä luotettava, on tarkastuslukufunktiot toimittava siten, että kahden saman tarkastusluvun tuottavan lähtöarvon löytäminen, tai tarkastusluvun arvon avulla tekstin generoiminen on vaikeaa. Vaikka yksinkertainen esimerkkimme ei täytäkään kumpaakaan ehtoa, on se silti perusajatukseltaan oikea. Jos taas haluamme käyttää turvallisia tarkastuslukufunktioita, niin esimerkiksi algoritmeja MD2, MD5, SHA, ja HAVAL on yleisesti pidetty turvallisina laskureina. Tarkastuslukufunktioita käytetään tavallisesti tiedostonsiirroissa varmennuslukuina sekä julkisten avainten yhteydessä digitaalisina allekirjoituksina. PCT tukee tällä hetkellä seuraavia tarkastuslukualgoritmeja: 4
Hash-funktio Luvun pituus MD2 128 bittiä MD4 128 bittiä MD5 128 bittiä RIPEMD 160 bittiä SHA 160 bittiä Kaikilla tarkastuslukufunktioilla on sama käyttöliittymä. Kun haluttu funktio on sisällytetty käskyllä from Crypto.Hash import nimi, voidaan funktiolla new() luoda uusi tarkastuslukuobjekti. Tälle objektille tämän jälkeen syötetään lähtöarvo update() metodilla. Tarkastusluku voidaan ottaa ulos joko tavallisena merkkijonona tai heksa-arvoina metodeilla digest() ja hexdigest(). Funktiolle new() voidaan myös suoraan kutsussa antaa parametrina lähtöarvo, joka samalla muutetaan tarkastusluvuksi. Ohessa esimerkki käyttäen MD5-algoritmia: >>> from Crypto.Hash import MD5 >>> tarkastusluku = MD5.new() >>> tarkastusluku.update('kurjenmutka') >>> tarkastusluku.digest() '\x06kk\x8d~\xe1>\xc0\xb1k\x96;\xf7o_\xb6' >>> tarkastusluku.hexdigest() '064b6b8d7ee13ec0b14b963bf76f5fb6' >>> Käyttöliittymien samankaltaisuudesta johtuen voimme toteuttaa saman uudelleen vaikkapa SHAalgoritmilla: >>> from Crypto.Hash import SHA >>> tarkastusluku = SHA.new() >>> tarkastusluku.update('hanhivaara') >>> tarkastusluku.digest() '\x9d\x06x\xb5xb,\xb6\x133\xfb\xbd\xd2-\xe5\xc4\xf9\xa7\x91\xad' >>> tarkastusluku.hexdigest() '9d0678b558622cb61333fbbdd22de5c4f9a791ad' >>> Huomioi, että SHA tuottaa pidemmän tarkastusluvun kuin esimerkiksi MD5. Tässä vielä kootusti tarkastuslukufunktioiden metodit ja muuttujat: digest_size Integer-arvo; Tämä arvo sisältää tiedon siitä, kuinka pitkän tarkastusluvun funktio palauttaa. Arvon muuttaminen ei vaikuta funktion toimintaan, vaan on lähinnä tarkoitettu nopeuttamaan tarkastusluvun koon hakemista. copy() Palauttaa kopion alkuperäisestä tarkastuslukuobjektista. Tämän objektin muuttaminen ei vaikuta alkuperäisen objektin arvoihin. digest() Palauttaa objektille annetusta lähtöarvosta tarkastusluvun merkkijonona. Objekti itsessään ei muutu funktion seurauksena, joten voit käyttää objektia uudelleen vaihtamalla lähtöarvoja update() metodilla ja kutsumalla digest() metodia uudelleen.. 5
hexdigest() Sama kuin digest(), mutta palauttaa tarkastusluvun heksa-arvoina. Tämän vuoksi merkkijono on aina kaksi kertaa pidempi kuindigest() metodin palauttama. update(arg) Antaa tarkastuslukuobjektille lähtöarvoksi parametrin arg. new(arg) Luo uuden tarkastuslukuobjektin. Ottaa vastaan myös parametrin arg, joka annetaan suoraan lähtöarvoksi. Huomioita turvallisuudesta Tarkastusluku-algoritmit pystytään rikkomaan kehittämällä algoritmi, joka generoi annettua tarkastuslukua vastaavia lähtöarvoja, tai tuottaa viestejä, joilla on sama tarkastusluku. Voimme osoittaa tämän ongelman käytännön esimerkin avulla: Hannu ja Aku käyttävät digitaalista allekirjoitusta vahvistaakseen sopimuksen. Aku laskee sopimuksen tekstistä tarkastusluvun ja allekirjoittaa sen omalla salatulla avaimellaan. Tämän jälkeen Hannun tarvitsisi ainoastaan korvata Akun kirjoittaman sopimuksen teksti sellaisella, joka antaa saman tarkastusluvun, eikä Aku pystyisi mitenkään todistamaan, että sopimuksen tekstiä on muutettu. Tällaisen tekstin löytäminen vaatisi Hannulta 2 b-1 testiä; b on tarkastusluvun pituus bitteinä. Jos Hannu pystyy tuottamaan kaksi tekstiä, jotka antavat saman tarkastusluvun mutta ei pysty vaikuttamaan suoraan tarkastusluvun arvoon, voisi hän valita kaksi tekstiä joiden merkitys on hyvin erilainen. Ensin hän allekirjoituttaisi Akulla normaalin sopimuksen, vaikkapa Ajan Hannun nurmikon mikäli en ui jouluaattona järven yli, ja jälkikäteen vaihtaa tekstiksi toisen tuloksen Annan taloni Hannulle ellen ui jouluaattona järven yli. Tämä olisi kaiken lisäksi Hannulle helpompaa: keskimäärin tarvitsisimme ainoastaan 2 b/2 yritystä. Jälleen kerran Aku ei pystyisi todistamaan, että allekirjoitettua sopimusta on muutettu. Aku voisi tietenkin turvata tilanteensa sotkemalla algoritmin syöttämällä tekstiin satunnaisesti generoidun lisärivin, jonka säilyttämällä hän pystyisi todistamaan sopimuksensa aitouden. Tämä ei vielä varsinaisesti ole huolenaihe PCT:n tarkastuslukualgoritmien kanssa. PCT:n käytössä olevista viidestä tarkastuslukualgoritmista yhtäkään ei ole vielä onnistuttu trivialisoimaan. MD2:ta vastaan ei ole olemassa tunnettuja hyökkäyksiä, mutta sen suoritusnopeus 1250 K/sek. tekee siitä varsin hitaan algoritmin. MD4 on nopeampi 44,500 K/sek mutta se on onnistuttu osittain murtamaan. MD4 sisältää kolme iteratiivista sekoituskierrosta: näistä kaksi on jo pystytty murtamaan mutta täyttä algoritmia ei vielä toistaiseksi saatu täysin rikottua. MD5 on MD4- algoritmista paranneltu versio ja se sisältää neljä iteraatiokierrosta. Tästäkin algoritmista yksi kierros on saatu murrettua. Siitäkin huolimatta MD5:ttä pidetään edelleen turvallisena, mutta silti useat tahot ovat siirtymässä kohti SHA-algoritmia, jolle ei vielä toistaiseksi ole olemassa tunnettuja hyökkäyksiä lukuun ottamatta SHA:n ensimmäisiä versioita, mutta nämä puutteet on myöhemmin korjattu. Lisäksi koska MD5-algoritmin suoritusnopeudeksi on mitattu 35,500 K/sek ja SHA:lle 21,000 K /sek, ei varsinaisesti ole mitään syytä käyttää heikompia algoritmeja.. 6
Crypto.Cipher: Salausfunktioita Alkuperäinen lähde Python Crypthography Toolkit Manual, http://www.amk.ca/python/writing/pycrypt/ Salausalgoritmit (encryption algorithms) toimivat siten, että ne muuntavat niille annetun syötetekstin (selkoteksti, plaintext) jonkin kaavan ja avaimen (variable key) avulla salatuksi tekstiksi (salateksti, ciphertext). Salaus- ja purkuoperaatio on tarkoitus olla helppo ja nopea suorittaa kumpaan tahansa suuntaan, mutta ainoastaan mikäli käyttäjällä on hallussaan salauksessa käytetty avain. Tämä tarkoittaa tietenkin sitä, että salausalgoritmi on luotettava ja varma silloin, kun mahdollisia avaimia on suuri määrä ja käytettävää avainta ei pystytä päättelemään tuotetusta salatekstistä. Yksinkertainen esimerkki kryptauksesta voisi olla algoritmi, joka vaihtaa kirjaimia aakkosissa eteen- tai taaksepäin tietyn verran: >>> teksti = "Karhuvuori" >>> avain = 3 >>> salattu = "" >>> for i in range(0,len(teksti)): salattu = salattu + chr(ord(teksti[i])+avain) >>> salattu 'Ndukxyxrul' >>> Tässä tapauksessa selkoteksti on siis Karhuvuori, salausavain 3 ja tuloksena saatava salateksti Ndukxyxrul. Tietenkään tällainen salaus ei ole kovin tehokas, koska potentiaalisia avaimia on ainoastaan 54 kappaletta. Lisäksi kirjainten suhteellinen määrä pysyy vakiona: jos tiedämme millä kielellä teksti on salattu, voimme muutaman kokeilun avulla purkaa tekstin kunhan ensin paikallistamme symbolit, jotka vastaavat yleisimmin käytettyjä kirjaimia, kuten a,e,l,n,k ja m. Luonnollisesti paremmat salausalgoritmit ottavat tämänkin huomioon, esimerkiksi muuttelemalla avainta jonkin kaavan mukaisesti: >>> teksti = "Peikonhammas" >>> avain = 3 >>> salattu = "" >>> for i in range(0,len(teksti)): salattu = salattu + chr(ord(teksti[i])+avain) avain = avain + 1 >>> salattu 'Sinqvvqkxyn\x81' 7
Nyt luotua salatekstiä on jo paljon vaikeampi tulkita. Ollakseen tehokas, täytyy salausalgoritmista yleensäkin olla mahdollisimman vaikeaa päätellä mitään tietoa selkotekstistä ilman avainta. Tämä tarkoittaa myös sitä, että salaukselle ei tulisi olla olemassa mitään tehokkaita hyökkäysmenetelmiä, ja että ainoa tapa avata salateksti ilman avainta on kokeilla kaikkia mahdollisia kombinaatioita. Koska avainkombinaatioita tavallisesti on vähintään 2 56 tai 2 128 kappaletta, ei tämä aiheuta ongelmia. Kannattaa kuitenkin huomata, että 2 56 avaimen algoritmit voidaan nykyään periaatteessa purkaa järkevässä ajassa (muutama tunti - muutama päivä) sitä tarkoitusta varten suunnitellulla rinnakkaislaskentatietokoneella. Kehittyneempiä salausalgoritmeja ovat lohkonsalausalgoritmit (block ciphers), jotka ottavat syötteinä vastaan määrätyn kokoisia syötteitä. Nämä syötteet ovat normaalisti kokoluokkaa 8 tai 16 tavua. Yksinkertaisimmillaan lohkonsalaajat toimivat siten, että ne ottavat tekstistä peräkkäisiä lohkoja, ja salaavat ne normaalin salausalgoritmin tavoin. Tätä toimintatapaa sanotaan Electronic Code Book (ECB)-tilaksi. ECB on kuitenkin vaarallinen, koska tällöin peräkkäiset samansisältöiset rivit kuten kommenttimerkeillä piirretyt laatikot lähdekoodissa tuottavat samanlaisia lohkoja salatekstiin. Tällöin tekstin purkamista yrittävillä henkilöillä on jo jonkinlainen lähtökohta tekstin purkamiseen. Tätä torjutaan tavallisesti takaisinsyöttötiloilla, joissa edellisen lohkon tulos vaikuttaa seuraavaan, eikä kaksi peräkkäistä lohkoa tällöin saa koskaan samaa arvoa. Muita toimintatiloja ovat mm. Cipher Block Chaining (CBC) sekä Cipher FeedBack (CFB). CBC pakkaa tietoa edelleen lohkoissa, ollen ainoastaan hieman ECB-menetelmiä hitaampi. CFB puolestaan pakkaa tietoa merkki kerrallaan, ollen huomattavasti muita hitaampi. CBC tilat tarvitsevat alustuksessa lähtöarvon, jota käytetään ensimmäisen lohkon salaamisessa. Python Cryptography Toolkit tukee tällä hetkellä seuraavia lohkonsalausalgorimeja. Ne kaikki löytyvät kirjastomoduulin Crypto.Cipher sisältä: Salaus ARC2 Blowfish CAST DES DES3 (Triple DES) IDEA RC5 AES Avaimen koko/lohkon koko Vaihtelee/8 tavua Vaihtelee /8 tavua Vaihtelee /8 tavua 8 tavua/8 tavua 16 tavua/8 tavua 16 tavua/8 tavua Vaihtelee /8 tavua 16, 24, tai 32 tavua/16 tavua On myös olemassa tietovirransalaukseen käytettäviä salausalgoritmeja (stream ciphers), jotka periaatteessa ovat merkki kerrallaan salaavia lohkonsalausalgoritmeja. Näillä algoritmeilla lohkon koko on aina 1 tavu, eikä niillä ole toimintatiloina kuin ainaostaan ECB. PCT tukee seuraavia virransalausalgoritmeja: Salaus Avaimen koko XOR Vaihtelee ARC4 Vaihtelee 8
ARC4 on lyhenne sanasta Alleged RC4. Tämä johtuu siitä, että oikea RC4-algoritmi on RSA Data Security Inc. omaisuutta, mutta PCT:hen sisällytetty versio on siitä verkkoon vuotanut algoritmiversio. Ei ole täysin varmaa, onko ARC4 oikeasti RC4, mutta salaus ei vaikuttaisi olevan helposti purettavissa. Kannattaa kuitenkin pitää mielessä, että kyseinen implementaatio ei ole virallinen, eikä tällöin sen mahdollisia takaportteja tai muita ongelmia välttämättä tiedetä. Salausalgoritmien toimintaa voimme testata myös esimerkin avulla: >>> from Crypto.Cipher import DES >>> avain = DES.new('ABCDEFGH') #Huomaa avaimen 8 merkkiä >>> selko="suo mulle hauta pohjassa meren,\ kun vanhuuden peikko mun hyytävi veren." >>> len(selko) 70 >>> avain.encrypt(selko) Traceback (most recent call last): File "<pyshell#42>", line 1, in <module> avain.encrypt(selko) ValueError: Input strings must be a multiple of 8 in length >>> salateksti = avain.encrypt(selko+"xx") >>> salateksti '4t\x97b\xdf\xe7\xbbRd`\r`,\xca`x\x16\xdc\xb17\xf0\xa4ow&\xe7,$\xf81\x02\xa0R\x8 7\x07<\x9c9\xacu.8\xf3z1T=8]8\x96\x02L\x07\xb69\xde\xb4\x90\x91!\xce)\r\x85j\x16!A\\\x12p' >>> avain.decrypt(salateksti) 'Suo mulle hauta pohjassa meren,kun vanhuuden peikko mun hyyt\xe4vi veren.xx' >>> Kuten koodista ilmenee, on tekstiä salattaessa muistettava, että selkotekstin on oltava jaollinen käytetyn algoritmin lohkon pituudella. Tässä tapauksessa käytimme DES-algoritmia, jonka lohko on 8 tavua pitkä. Ensimmäinen yrityksemme salata 70 merkkiä pitkä teksti kaatui siihen, että viimeiseen lohkoon olisi jääny ainoastaan 6 tavua, joten jouduimme pidentämään sitä lisäämällä loppuun kaksi X -kirjainta. Kaikki salausfunktiot toimivat samanlaisella käyttöliittymällä. Kun olemme sisällytyksen yhteydessä päättäneet, mitä algoritmia käytämme, pystymme käyttämään seuraavia metodeja sekä attribuutteja salauksen säätelemiseen: new(key, [ mode, IV]) Luo uuden salausobjektin käyttäen alustusarvoina syötettyä avainta key sekä toimintatilaa mode. Jos annettu tila on CBC tai CFB, täytyy lisäksi antaa aloitusarvo IV. Lisäksi joillain salausalgoritmeilla on lisäksi omia lisäasetuksia, niistä voit lukea seuraavalta sivula. block_size Integer-arvo, joka sisältää tiedon siitä minkä kokoisia lohkoja salausalgoritmi käyttää. key_size Integer-arvo, joka kertoo minkä pituisia avaimia algoritmi käyttää. Jos arvo on 0, on avaimen koko vaihteleva. IV Sisältää lähtöarvon jota käytetään lohkojen salauksessa. Alustuksen jälkeen sisältää aloitusarvon, salauksen jälkeen uusimman generoidun. Tätä arvoa et voi muuttaa käsin. 9
decrypt(string) Purkaa annetun merkkijonon string salauksen objektiin tallennetulla avaimella. Annettavan merkkijonon tulee aina olla salausalgoritmin käyttämän lohkon koon moninkerta. Palauttaa selkotekstin merkkijonona. encrypt(string) Salaa merkkijonon string käyttäen salausobjektille annettua avainta. Syötettävän merkkijonon tulee olla käytetyn salausalgoritmin lohkon koon moninkerta. Virtasalausalgoritmit ottavat vastaan minkä pituisen tekstin tahansa. Palauttaa salatekstin merkkijonona. Algoritmikohtaisia attribuutteja RC5-algoritmin yhteydessä voimme käyttää normaalien alustusparametrien lisäksi seuraavia lisäparametreja: version: Mitä salausalgoritmin implementaatiota käytetään. Ainoa PCT:hen toteutettu versio on tällä hetkellä 1.0, eli ainoa sopiva arvo on0x10. wordsize: Minkä pituisia sanoja käytetään; 16 ja 32 ovat ainoat sopivat arvot.. Yleisesti pidempi on parempi, joten on suositeltavaa käyttää 32 merkin sanapituutta. rounds: Kuinka monta kierrosta salausalgoritmia ajetaan, yleisesti ottaen enempi on parempi. Arvo voi olla mitä tahansa väliltä 0 ja 255, joten arvon valinta perustuu lähinnä nopeuden ja turvallisuuden väliseen valintaan. Huomioita turvallisuudesta Salausalgoritmit voidaan murtaa monella eri menetelmällä. Jos sinulla on salatekstiä ja tiedät (tai pystyt arvaamaan) mitä sanoja tai lauseita teksti sisältää, voit sen avulla yrittää known-plaintexthyökkäystä. Toisaalta, jos taas pystyt valitsemaan tekstin, joka salataan sinulle tuntemattomalla avaimella, pystyt analysoimaan avaimen arvon ja paljastamaan esimerkiksi jonkun henkilökohtaisen avaimen. Tätä kutsutaan chosen-plaintext-hyökkäykseksi. DES (5100 K/sek) käyttää 56-bittistä avainta; tämän kokoinen avain on käymässä liian pieneksi ollakseen varmasti turvallinen. Arviolta miljoona dollarilla riittää helposti supertietokoneeseen, joka pystyy muutamassa tunnissa murtamaan 56-bittisiä avaimia. Tähän operaatioon tarvitaan kuitenkin keskimäärin 2 43 operaatiota, joten tavallisilta tietovarkailta olet edelleen turvassa. Mikäli kuitenkin haluat suojautua kaikkia tilanteita varten, voit luonnollisesti käyttää DES3-salausta (1830 K/sek), joka käyttää 112- tai 168-bittisiä avaimia. Myöskään IDEA-algoritmia (3050 K/sek) vastaan ei ole olemassa julkisesti tunnettuja hyökkäyksiä, vaikka algoritmi onkin ollut olemassa jo kauan. Lisäksi ARC2 (2160 K/sek), ARC4 (8830 K/sek), Blowfish (9250 K/sek), CAST (2960 K/sek) ja RC5 (2060 K/sek) ovat edelleen turvallisia, mutta ne ovat edelleen melko uusia algoritmeja, joten niiden suojausongelmia ei välttämättä vielä ole edes tutkittu tarkkaan. AES-algoritmi valittiin US National Institute of Standards and Technology:n testeissä kuuden kilpailijan joukosta tehokkaimmaksi ja turvallisimmaksi, joten sitä voidaan pitää ainakin melko hyvänä vaihtoehtona. Lisäksi sen salausnopeus (7060 K/sek) on suhteellisen nopea. 10
Crypto.Protocol: Salausprotokollia Alkuperäinen lähde Python Crypthography Toolkit Manual, http://www.amk.ca/python/writing/pycrypt/ Crypto.Protocol.AllOrNothing Tämä moduuli sisältää toteutuksen kaikki-tai-ei-mitään-tyyppisille pakettimuunnoksille. Nämä pakettimuunnokset toimivat siten, että lähdeaineisto pakataan paketteihin, jotka kaikki pitää saada vastaanotettua ennen kuin salattu aineisto voidaan muuntaa takaisin alkuperäiseen muotoon. Tällöin pakettien rikkoutuminen, muunteleminen tai häviäminen estää viestin vastaanottamisen. Kaikki-tai-ei-mitään-paketit eivät varsinaisesti ole salattuja, vaikka niiden yhteydessä käytetäänkin lohkonsalausalgoritmeja. Salausavain näet luodaan satunnaisesti ja se on helposti pääteltävissä pakettien sisällöstä. Python Cryptography Toolkitin kaikki-tai-ei-mitään-menetelmän määrittelevä kirjasto sisältää seuraavat rakenteet: class AllOrNothing(ciphermodule, mode=none, IV=None) Luokkarakenne joka määrittelee pakettimuunnoksen. ciphermodule määrittelee käytettävän salausalgoritmin. Lisäparametrejä mode ja IV voidaan käyttää tarvittaessa salauksen tarkempaan määrittelyyn. Ciphermodule voi olla mikä tahansa edellisessä luvussa määritellyistä salausalgoritmeista. digest() Suorittaa update()-metodilla syötetylle lähdeaineistolle kaikki-tai-ei-mitään pakkauksen. Tuottaa listan merkkijonoja, jossa yksi jono vastaa yhtä pakettia. reset(text = "") Tyhjentää moduulin muistin. undigest(mblocks) Purkaa paketit selkotekstiksi. Parametri mblocks on oltava lista, jossa yksi listan alkio vastaa yhtä pakettia. Palauttaa selkokielisen merkkijonon. update(text) Syöttää parametrilla text lähdeaineiston moduulille pakkausta varten. 11
Crypto.Protocol.Chaffing Karsiminen ja sotkeminen (winnowing ja chaffing) on tekniikka, jolla pystymme parantamaan verkossa siirrettävän tiedon turvallisuutta joutumatta käyttämään raskaita salausalgoritmeja. Lyhyesti ajatus perustuu siihen, että otamme joukon varmennettuja paketteja jotka haluamme lähettää verkon yli, ja sotkemme lähetykseen sekaan ylimääräisiä paketteja, jotka haittaavat salakuuntelijoita. Koska salakuuntelija ei pysty avaimella tarkastamaan mitkä paketit ovat varmennettuja ja mitkä ylimääräisiä, voidaan ylimääräisillä paketeilla purkuoperaatiosta tehdä laskennallisesti mahdotonta. Jos ajattelemme asiaa käytännön esimerkin kautta, niin olettakaamme, että Roope lähettää viestiä Akulle. Roope pakkaa viestit kaikki-tai-ei-mitään menetelmällä paketteihin ja laskee paketeille varmennusluvut (message authentication code, MAC) etukäteen vaihdetulla varmennusavaimella. Lopuksi Roope vielä laittaa paketteihin järjestysnumerot ja lähettää ne Akulle. Kun Aku saa paketit, hän ensin varmentaa paketit saamallaan avaimella. Jos MAC-luku on väärin, voi Aku suoraan hylätä paketin koska se ei kuulu lähetykseen. Ne paketit, joiden varmennusluku oli oikein, Aku laittaa numerojärjestykseen, ja purkaa kaikki-tai-ei-mitään-paketoinnin saaden Roopen viestin. Jos joku pystyisi kaappaamaan paketeista kopiot verkosta, olisi hänellä seuraavanlainen ongelma: Koska kaapparilla ei olisi Roopen ja Akun käyttämää varmennusavainta, ei kaappaaja tietäisi mikä paketit ovat ylimääräisiä. Lisäksi, koska paketteja lähetettäisi useampi samalla järjestysnumerolla, joutuisi kaappaaja lisäksi kokeilemaan kaikkien pakettien kaikki kombinaatiot pystyäkseen murtamaan salauksen. Ja lopulta, koska Roope käytti kaikki-tai-ei-mitään-paketointia, on viesti hyödytön jos yksikin oikea paketti puuttuu. Erityisen nerokasta menetelmässä on se, että se ei vaadi Akulta eikä Roopelta ylimääräisiä varmennuksia: Akulle riittää, kun hän tarkastaa jokaisen saamansa paketin varmennusavaimella. Myöskään Roopen ei tarvitse itse tehdä mitään valmisteluja, ylimääräisiä paketteja voidaan laittaa sekaan automaattisesti vaikkapa 10 jokaista oikeaa pakettia kohti. Todennäköisyys, että satunnaisesti arvottu varmennusluku vastaisi satunnaisesti arvottua sisältöä ja että molemmat vastaisi käytettyä varmennusalgoritmia, on olematon. Ja mikäli näin pääsisi käymään, voisi Roope lähettää viestin uudelleen. Crypto.Protocol.Chaffing-moduulissa on seuraavat toiminnalliset rakenteet: class Chaff(factor=1.0, blocksper=1) Luokkarakenne, joka mahdollistaa ylimääräisten pakettien luomisen. Määrittelyssä annetaan kaksi parametria: factor, jolla määrätään kuinka monelle alkuperäiselle paketille lisätään ylimääräisiä paketteja. Lukuarvo annetaan prosenttikertoimena välillä 0.0 ja 1.0; oletusarvo on 1.0. Arvolla blocksper määrätään, kuinka monta ylimääräistä pakettia jokaista oikeaa pakettia kohti tehdään; oletusarvo on 1. 12
Chaff- rakenteella on seuraavat metodit: chaff(blocks) Python kryptausopas Lisää ylimääräisiä paketteja viestiosioon. blocks on lista 3-tupleja, jotka ovat muotoa (sarjanumero,data,mac). Metodi palauttaa samalla tavalla muotoillut listan, johon on lisätty alustusparametrien mukaisesti ylimääräisiä paketteja. Nämä paketit on luotu satunnaisesti annetun datan pohjalta. randnum(size) Palauttaa satunnaisesti generoituja tavujoukkoja jotka ovat size tavua pitkiä. Moduuli luottaa omaan satunnaisgeneraattoriinsa, koska Pythonin oma generaattori ei ole riittävän satunnainen, jonka seurauksena sillä luoduista paketeista voidaan nähdä tiettyjä toistuvia kuvioita. Näiden kuvioiden takia pakettien tunnistaminen olisi helppoa, ja siksi modulin mukana toimitetaan oma tehokkaampi satunnaislukugeneraattori. 13
Crypto.PublicKey: Julkisen avaimen algoritmeja Alkuperäinen lähde Python Crypthography Toolkit Manual, http://www.amk.ca/python/writing/pycrypt/ Tähän asti kaikki käyttämämme algoritmit ovat olleet yhteisen avaimen salausalgoritmeja. Tämä tarkoittaa siis sitä, että viestit puretaan ja pakataan samalla avaimella, joten kaikkien viestejä lähettävien ja vastaanottavien tulee tuntea tämä avain. Tähän juuri liittyykin seuraava ongelma: jos haluamme viestiä epäluotettavan verkon yli, voimme tietenkin salata viestimme, mutta kuinka alun perin aioimme saada avaimen perille jos oletamme että emme pysty sitä muilla keinoilla siirtämään? Emme voi lähettää avainta sähköpostilla, koska silloin se menisi selkokielisenä verkon yli, emmekä voi salata sitä toisella avaimella, koska tämäkin avain olisi lähetettävä selkokielisenä. Joutuisimme fyysisesti menemään paikan päälle viemään avaimen, ja tässä taas ei ole mitään järkeä. Toinen vaihtoehto olisi käyttää julkisia salausavaimia hyödyntäviä salausalgoritmeja. Käytettäessä julkisia avaimia, tuottaa salausalgoritmi kaksi erilaista avainta salauksia varten. Tiedon salaamista varten luodaan julkinen avain (public key) ja purkamista varten salainen avain (private key). Salauksessa käytettävää julkista avainta voidaan jakaa verkossa vapaasti, kun taas salainen avain tulee säilyttää turvallisessa paikassa. Jos joku haluaa lähettää sinulle varmennettua tietoa, hän voi salata viestin sinun julkisella avaimellasi. Koska julkista avainta ei voida käyttää sillä luodun viestin purkamiseen, voit ainoastaan sinä purkaa viestin omalla salaisella avaimellasi. Jotkin avainparit on lisäksi suunniteltu siten, että niillä voidaan tietoa salata ristiin molempiin suuntiin. Näitä julkisen avaimen algoritmeja voidaan käyttää tiedon lähettämisen lisäksi myös viestien allekirjoitukseen: koska avaimia voidaan käyttää ristiin kumpaankin suuntaan - eli siten että salaisella avaimella salattua tietoa voidaan purkaa julkisella avaimella voit salata viestin salaisella avaimellasi, ja vastaanottaja varmentaa allekirjoituksen purkamalla sen julkisella avaimella. Yleisesti julkisen avaimen salausalgoritmit toimivat siten, että salainen avain on vaikea päätellä julkisesta avaimesta. Tämä ei kuitenkaan ole mahdotonta mikäli resursseja on riittävästi, joten ainoa keino suojautua tältä on tehdä avaimesta riittävän pitkä. On kuitenkin pidettävä mielessä, että avaimen pituus kannattaa valita siten, että avaimen koko on vielä kohtuullinen, jotta salauksen käyttö olisi mielekästä. 14
Python Cryptography Toolkit sisältää seuraavat julkisen avaimen algoritmit: Algoritmi Käytettävyys RSA Salaus, varmennus/allekirjoitus ElGamal Salaus, varmennus/allekirjoitus DSA varmennus/allekirjoitus qnew varmennus/allekirjoitus Useimmat näistä algoritmeista on kaupalliseen käyttöön patentoituja. Mikäli käytät niitä projekteissasi, on suositeltavaa, että tarkastat niiden lisenssiehdot verkosta. Seuraavaksi tutustumme tarkemmin RSA-salausalgoritmiin esimerkin avulla: >>> from Crypto.Hash import MD5 >>> from Crypto.PublicKey import RSA >>> from Crypto.Util.randpool import RandomPool >>> lukulista = RandomPool() >>> RSAavain = RSA.generate(512, lukulista.get_bytes) >>> tarkastusluku = MD5.new("Joku porho kehui repineensä puukaupoilla niin paljon tuohta että heikkolatvaisia puistatti.").digest() >>> tunniste=rsaavain.sign(tarkastusluku,"") >>> tunniste (2488227805075508107753520610413128570898591316559627485443691432798630155485881 303000064980473774493643623749643164057793284807040713852933375541262459784L,) >>> RSAavain.verify(tarkastusluku,tunniste) 1 >>> RSAavain.verify(tarkastusluku[:-1],tunniste) 0 >>> Ohjelma toimii siten, että me tarvitsemme kolme asiaa: Tarkastuslukualgoritmin, joksi valitsimme MD5-algoritmin, avainalgoritmin, joksi valitsimme RSA:n sekä satunnaisgeneraattorin, joka otettiin moduulista Crypto.Util.randpool. Ensin loimme uuden satunnaislukujoukon lukulista, ja annoimme sen RSA-algoritmille, joka loi uuden 512-bittisen avainparin. Tämän jälkeen teimme selkotekstistämme MD5-tarkastusluvun. Seuraavaksi allekirjoitimme MD5-tarkastusluvun juuri luomallamme RSA-avaimella. Saatu tunniste on pitkä kokonaisluku, jonka lopuksi varmistimme verify-funktiolla. Huomaa, että alempana jätimme pois tarkastusluvun viimeisen merkin, jolloin tunniste ei enää vastannut tarkastuslukua. Voimme testata ohjelman avainparien toimintaa vielä toisella esimerkillä: >>> Julkinen = RSAavain.publickey() >>> selkoteksti = "Uniikki unikorni olikin korni koni." >>> viesti = Julkinen.encrypt(selkoteksti,"") >>> viesti ('Mr\xa3\x84\xe8\xddfv\n\x020\xec\x1a\xce\xdaKf\xfe\x1c\x97\xca?\xfc \x898\x8b\x a8\x1d\xe5\x8b\x1d)&&\xf0\x18\x124@=\xfd\x93\xd6\x9e{\xdek\xbff]\xb4"lb$\xcd\xe4 \x15\x8f\xdal\xa8\x03',) >>> RSAavain.decrypt(viesti) 'Uniikki unikorni olikin korni koni.' >>> allekirjoitus = RSAavain.decrypt("Uka Naakka") 15
>>> allekirjoitus ' \xa3m\xb0\xe9\x8f\x10uhj\x90>\xffj@\x18l\xfc\xba\xab\x94- \x89\xf3\x10\xbf\x96w\xe2\x96{n\xf5\x93j\xc4\xfc\xc1\xde\x9c\x97\x1b\xa5\xd2\x8f g\x98\x11 -\xb1\xe5\xbb\x98\xfe\xaf\xc9\x03\xe3\xf8f\xf5i\x0b' >>> Julkinen.encrypt(allekirjoitus,"") ('Uka Naakka',) >>> Nyt loimme RSAavaimesta julkisen avaimen, jolla salasimme viestin Vesihiisi. Käyttämämme salainen avain pystyi purkamaan viestin ilman ongelmia. Vastaavasti allekirjoitus sujui ilman ongelmia: ensin allekirjoitimme eli periaatteessa purimme tekstin Uka Naakka, jonka julkinen avain onnistui kokoamaan eli periaatteessa salaamaan oikein siten, että allekirjoitus tuli näkyviin. Julkisen avaimen salausalgoritmit sisältävät seuraavat yhteiset metodit: generate(size, randfunc, progress_func=none) Luo uuden avainparin. Avaimet ovat kokoluokkaa size, ja niiden luomisessa käytetään satunnaislukugeneraattoria randfunc. Käytettävän satunnaislukugeneraattorin tulee toimia siten, että se saa kutsussa parametrina yhden integer-arvon ja palauttaa näin monta satunnaista merkkiä. Esimerkkitehtävässä käyttämämme RandPool.get_bytes on hyvä generaattori: Älä käytä Pythonin omaa random-satunnaislukugeneraattoria. canencrypt() Palauttaa arvon True jos valittu avainalgoritmi pystyy purkamaan ja salaamaan tietoa. cansign() Palauttaa arvon True jos valittu avainalgoritmi pystyy allekirjoittamaan tietoa.. decrypt(tuple) Purkaa tuplen salaisella avaimella, palauttaa merkkijonon. encrypt(string, K) Salaa merkkijonon string salaisella avaimella. Parametrin K tulisi sisältää satunnaisia merkkejä joita ohjelma voi käyttää salauksessa apunaan. hasprivate() Palauttaa arvon True jos salausobjektille on syötetty salainen avain. publickey() Palauttaa uuden julkisen avaimen jolla on mahdollista salata tietoa salaisella avaimella purettavaksi. sign(string, K) Allekirjoittaa merkkijonon string palauttaen allekirjoituksen joka on muotoa tuple. Parametriin K tulee syöttää satunnaisia merkkejä, joiden avulla allekirjoitus tehdään. size() Palauttaa suurimman mahdollisen merkkijonon koon bitteinä, joka pystytään salaamaan tai allekirjoittamaan. Mikäli luku ei ole jaollinen kahdeksalla, on suurin mahdollinen koko merkkeinä palautettu arvo jaettuna kahdeksalla pyöristettynä alaspäin verify(string, signature) Palauttaa arvon True jos allekirjoitus signature vastaa annettua merkkijonoa string. Muutoin palauttaa arvon False. Lisäksi osalla salausalgoritmeista (Kuten ElGamal ja DSA) on omia lisätoimintoja, mutta niistä voit lukea tarkemmin alkuperäisestä manuaalista [2]. 16
Huomioita turvallisuudesta Python kryptausopas Mikä tahansa näistä algoritmeista voidaan periaatteessa purkaa triviaalisti: Esimerkiksi käyttämämme RSA-algoritmi voidaan purkaa helposti kokeilemalla kaikki alkulukuparit välillä kahdesta n:ään. Nämä arvot voidaan löytää helposti seuraavanlaisella koodilla: for i in range(2, n): if (n%i)==0: print i, 'is a factor' ; break Kannattaa kuitenkin huomata, että n on tavallisesti muutamia satoja bittejä pitkä, joten ohjelma ei luultavimmin löydä ratkaisussa käytettäviä lukuja ennen kuin aurinko sammuu lopullisesti. Älykkäämmät algoritmit löytävät ratkaisuja hieman nopeammin, mutta siitäkään huolimatta ratkaisuja ei löydetä järkevässä ajassa. Lisäksi ElGamal ja DSA käyttä diskreetteja logaritmejä, mutta niilläkin ajatus on sama. Turvallinen avainkoko riippuu murtamisessa käytetystä laitteistosta. Nykyisellä laitekannalla alkulukupohjaisen algoritmin avaimille 512 bittiä riittää kotikäytössä sekä 768 yritysmaailman tarpeisiin. 1024-bittinen avain alkaa lähestyä sotilastiedustelutason salausavainta, ja on kotikäytössä jo tarpeettoman vahva. ElGamal- tai DSA-algoritmit ovat logaritmipohjaisia, joten niiden yhteydessä on suositeltavaa kertoa nämä avainkoot kahdella. 17
Crypto.Util: Muita työkaluja Alkuperäinen lähde Python Crypthography Toolkit Manual, http://www.amk.ca/python/writing/pycrypt/ Tässä luvussa käsittelemme niitä osia Python Cryptography Toolkitistä, jotka eivät sovi muihin kategorioihin. Crypto.Util.number Tämä moduulin osa sisältää työkaluja, joilla voidaan toteuttaa salauksessa tarvittavia matemaattisia funktioita. GCD(x,y) Palauttaa x:n ja y:n suurimman yhteisen nimittäjän. getprime(n, randfunc) Palauttaa N-bittisen satunnaisen alkuluvun käyttäen apunaan käyttäjän määrittelemää satunnaislukugeneraattoria randfunc. Tällaisena satunnaislukugeneraattorina voidaan käyttää vaikkapa RandPool.get_bytes generaattoria. getrandomnumber(n, randfunc) Muuten sama kuin yllä, mutta palauttaa N-bittisen satunnaisluvun, joka ei välttämättä ole alkuluku. inverse(u, v) Palauttaa u:n jakojäännös v:stä laskutoimituksen käänteisarvon. isprime(n) Palauttaa arvon True jos N on alkuluku. Testaus suoritetaan Rabin-Miller-testillä. Crypto.Util.randpool Kryptografian tarkoituksiin joudutaan usein tuottamaan erillisiä satunnaislukugeneraattoreita juuri siitä syystä, että normaalit satunnaislukugeneraattorit eivät ole riittävän satunnaisia. Tämä taas aiheuttaa tilanteen, jossa seuraamalla pitkällä aikavälillä satunnaislukugeneraattorin toimintaa, voidaan sen tuottamia lukuja alkaa arvaamaan, ja pitkäaikaisen kertymän avulla jopa tunnistaa, miten satunnaislukuja on tuotettu. Tämä on ongelmallista, koska mikäli pystymme tietämään mitä lukuja tulevilla kierroksilla luultavasti esiintyy sekä miten generaattori toimii, voimme luoda tulevia avaimia ennen niiden käyttöönottoa. Tätä vastaan voidaan kuitenkin toimia käyttämällä satunnaislukuina salaus- tai tarkastuslukualgoritmin läpi ajettuja satunnaislukuja: tällöin satunnaislukujen ennustaminen on yhtä vaikeaa kuin itse algoritmin murtaminen. Käytettäessä satunnaislukugeneraattoreita tulisi entropian konsepti ymmärtää mahdollisimman hyvin. Entropialla tarkoitetaan tässä tapauksessa satunnaisuuden määrää järjestelmässä, ja sen mitääyksikkönä käytetään tavallisesti bittejä. Jos meillä on käytössämme yksi satunnainen bitti, on 18
sen entropia silloin yhden bitin verran, vastaavasti yhden satunnaisen tavun entropia on 8 bittiä. Entäpä jos meillä on käytössä tietokannan kenttä, johon valitaan henkilön sukupuolen mukaan M tai N? Vaikka yhden merkin luontainen entropia on 8 bittiä, on meillä siitäkin huolimatta käytössämme ainoastaan kaksi valintaa, jolloin entropia pienenee yhteen bittiin. Jos ajaisimme tälle yhden tavun kokoiselle kentälle tarkastusluvun joka ilmaistaan 128 bitillä, olisiko entropia silloin 128 bittiä? Ei tietenkään, koska kenttä voi tuottaa ainoastaan kaksi erilaista tarkastuslukua, joten sen entropia on edelleen yksi bitti. Jos yrittäisit rikkoa salausta, voisit tästä kohdasta päätellä kentän tarkastuslukua vastaavan arvon olevan joko M tai N. Koska kenttä ei hyväksy muita vaihtoehtoja - kuten A, H tai K - ei sinun tarvitse myöskään kokeilla niitä. Tämä sääntö pätee myös luonnolliseen kieleen: esimerkiksi englanninkielisessä tekstissä kuuden merkin entropia on huomattavasti vähemmän kuin 6*8 eli 48 bittiä. Koska selkokielessä ei käytetä kaikkia kombinaatioita kuten zd3yfx tai KMH,DB, on arvo huomattavasti vähemmän kuin kaikkien teoreettisten kombinaatioiden summa. Kuinka tämä sitten liittyy satunnaislukugeneraattoreihin? Me haluamme järjestelmään riittävästi entropiaa, että emme joudu alttiiksi hyökkäyksille. Esimerkiksi voisimme tehdä ohjelman, joka arpoo käyttäjille mitään tarkoittamattomia salasanoja: idea olisi hyvä, koska järjestelmä estäisi käyttäjiä valitsemasta itsestään selviä salasanoja, kuten etunimiä, nimikirjaimia tai lemmikkieläinten nimiä. Jos satunnaisgeneraattorimme kuitenkin toimisi siten, että se arpoisi 32 alkuosan joukosta alun, 32 toisen osan joukosta keskiosan ja loppuun vielä numeron väliltä 0-32 tyyliin mik-dak-23 tai rip-zok-11, saisimme varmaan turvallisen salasanan? Emme saisi, sillä oikeasti erilaisia salasanoja olisi ainoastaan 32*32*32 eli 32768 erilaista. Tämä määrä olisi täysin riittämätön suojaamaan järjestelmäämme ja käytännössä oikeilla työkaluilla triviaalisti rikottavissa. Vielä 32-bittinen RSA-avain, jossa on noin 4.2 miljardia erilaista kombinaatiota, on riittämätön puhuttaessa vakavasta tietoturvauhasta. Joka tapauksessa, randpool moduuli toteuttaa tehokkaan satunnaislukugeneraattorin RandomPool-rakenteen avulla. Tämä satunnaislukugeneraattori pitää kirjaa generaattorista poistuneesta entropiasta ja tasapainottaa sen määrää sisäisten funktioidensa avulla. Lisäksi kirjasto osaa myös kerätä aidosti satunnaista kohinaa mm. käyttäjän näppäinpainallusten tarkkaa aikaa ja painettua nappia hyödyntämällä. RandomPool([numbytes, cipher, hash]) RandomPool-objekti voidaan halutessa luoda kokonaan ilman parametreja edellisessä luvussa olleen esimerkin tavoin. Parametrilla numbytes voidaan säätää satunnaislukugeneraattorin luomien lukujen määrää, ja parametrilla hash voidaan valita tarkastuslukufunktio, jolla satunnaislukujoukkoa sekoitetaan. Parametri cipher on poistunut käytöstä version 1.1 jälkeen, mutta se on edelleen sisällytetty funktiokutsuun. Sen voi jättää tyhjäksi. RandomPool objekteilla on seuraavia muuttujia sekä metodeja: addevent(time[, string]) Syöttää satunnaistietoa generaattorille. Parametriksi time kannattaa antaa senhetkinen kellonaika, parametriksi string satunnaista tekstiä tai merkkijonoja. Hyvä keino on esimerkiksi ohjata käytettyjä tarkastuslukuja tai satunnaisten muistiosoitteiden tietoja generaattoriin, koska nämä ovat tietoa jota ei yleisesti voida käyttää generaattorin murtamiseen. Paluuarvona palautuu järjestelmän uusi entropia-arvo self.entropy. 19
bits Integer-arvo, joka kertoo kuinka monta tavua satunnaislukuja generaattori sisältää. Käytännössä arvo on bytes-arvo jaettuna kahdeksalla ja pyöristettynä alaspäin. bytes Integer-arvo, joka kertoo kuinka monta bittiä satunnaisdataa generaattorissa on. entropy Integer-arvo, joka kertoo kuinka paljon entropiaa satunnaislukugeneraattori sisältää. addevent() kasvattaa arvoa, getbytes laskee sitä. getbytes(num) Palauttaa merkkijonon jossa on num tavua satunnaista tietoa. Metodi ei aiheuta virhettä, vaikka satunnaisgeneraattorin entropia-arvo olisikin nolla tai pääsisi menemään nollaan. Tämä käytännössä vain tarkoittaisi sitä, että generaattorin luvut eivät enää ole täysin satunnaisia. Riittävän entropian säilyttäminen jätetäänkin käyttäjän vastuulle. stir() Sekoittaa satunnaislukulistaa käyttäen alustuksessa määrättyä tarkastuslukufunktiota. On suositeltavaa, että stir-metodia käytetään aina, kun generaattoriin lisätään lukuja addevent()-metodilla tai kun sieltä otetaan lukuja getbytes-metodilla. PersistentRandomPool-rakenne on alirakenne RandomPool:ille, joka mahdollistaa generaattorin sisällön lataamisen ja tallentamisen levyltä sekä kyvyn luoda satunnaisdataa näppäimistöpainalluksista. Rakenteella on muuten samat ominaisuudet, mutta seuraavissa toiminnoissa on eroavaisuuksia: PersistentRandomPool([filename, numbytes, cipher, hash]) Käytännössä sama kuin RandomPool, mutta ottaa lisäksi tiedostonnimen, josta generaattorin sisältö luetaan. Jos tiedostoa ei ole olemassa, sellainen luodaan ja generaattori alustetaan normaalisti. Jos tiedostonnimeä ei anneta, toimii rakenne kuten RandomPool-rakenne. randomize() (Unix -järjestelmäkohtainen) Ottaa satunnaistietoa käyttäjältä. Pyytää käyttäjää painamaan näppäimistön satunnaisia kirjaimia, joista muodostaa aidosti satunnaista tietoa generaattorille. save() Tallentaa generaattorin sisällön alustuksessa määriteltyyn tiedostoon. 20
Loppusanat Tässä on tällä erää kaikki, mitä Python Cryptography Toolkitin käyttöopas piti sisällään. Mikäli kiinnostuit salaustekniikoista tai niiden hyödyntämisestä omien ohjelmiesi parissa, kannattaa sinun tutustua PCT:n kotisivuilta [2] saatavilla oleviin esimerkkikoodeihin, joissa teknisesti pidemmälle menneiden esimerkkien avulla opetetaan käytännössä työkalun käyttöä. Tavallisesti salausalgoritmeja käytetään pääasiassa tietoliikennetekniikassa, joten et vielä tässä vaiheessa saanut oppaasta muuta irti kuin että pääsit alkuun erilaisten salausavainten kanssa. Kun jatkossa tutustut tietoliikenneohjelmointiin ja viestinvaihtoon, olet jo valmiiksi opetellut käyttämään tietoliikenteen salauksessa tarvittavia asioita. Lisäksi voit tietenkin soveltaa oppaan ohjeita, ja salata työasemaltasi tietoja, joiden et halua päätyvän muiden nähtäville. Kuitenkin kannattaa muistaa, että tärkein lähtökohta tietoturvassa ei ole mahdollisimman vahvan salausalgoritmin käyttäminen, vaan nimenomaan salasanan valinta: paraskaan algoritmi ei auta sinua, jos salasanasi on heikko tai itsestään selvä. Älä käytä omaa nimeäsi, nimikirjaimiasi, syntymäaikaasi, puolison nimeä tai lemmikkien kutsumanimiä salasanoina. Hyvässä salasanassa on aina sekaisin isoja ja pieniä kirjaimia sekä numeroita eikä se tarkoita mitään. On tietenkin ymmärrettävää, että tällaisen salasanan muistaminen on vaikeaa. Älä kuitenkaan kirjoita salasanaa lapulle ja jätä sitä mihinkään itsestään selvään paikkaan, kuten näppäimistön tai työtason alle tai avoimeen työpöydän laatikkoon. Jos ehdottomasti haluat kirjoittaa salasanan ylös, niin säilytä lappua edes lukitussa laatikossa, johon ainoastaan sinulla on avain, tai talleta se kännykkääsi SIMkortille viesteihin tai kontaktitietoihin ilman muita lisätietoja: luultavasti et anna sellaisten tahojen selata kännykkääsi joiden et myöskään haluaisi käyttävän tietokonettasi. Ja mikäli kännykkäsi häviää, lukittuu salasana SIM-kortin sisään kun kuoletat liittymäsi. Äläkä tietenkään kerro kenellekään, että olet tallentanut työaseman salasanasi kännykäsi viesteihin. Useimmiten tietoturva onkin juuri tämänkaltaisia pieniä asioita. Vahvimmatkin salaukset voidaan ohittaa luottamalla käyttäjien virheisiin, joten huolehdi että et vahingossa itse aiheuta suurinta tietoturvariskiä omalle työasemallesi. Siihen ei auta edes ElGamal-suojauksella allekirjoitettu MD5- tarkastussumma. 21
Lähteet [1] Kasurinen, Jussi (2006) Python-ohjelmointiopas., versio 1. Tietotekniikan käsikirjat 7, Lappeenrannan teknillinen yliopisto. [2] Kuchling, A.M. (2007) Python Cryptography Toolkit Manual. Viitattu 12.6.2007. Saatavilla osoitteesta http://www.amk.ca/python/writing/pycrypt/ [3] Kasurinen, Jussi (2007) Python grafiikkaohjelmointi Imaging Librarylla. Tietotekniikan käsikirjat 8, Lappeenrannan teknillinen yliopisto. [4] Kuchling, A.M. (2006), Python Enhancement Proposal 247: API for Cryptographic Hash Functions. Saatavilla osoitteesta http://www.python.org/dev/peps/pep-0247/, viitattu 28.6.2007. [5] Kuchling, A.M. (2006), Python Enhancement Proposal 272: API for Block Encryption Algorithms. Saatavilla osoitteesta http://www.python.org/dev/peps/pep-0272/, viitattu 28.6.2007. 22
Lisenssiehdot Kannen kuva: Nila Gurusinghe. Kuva julkaistu Creative Commons - Nimi mainittava 2.0 - lisenssillä. Tähän asiakirjaan sovelletaan Creative Commonsin Vapaa Yleinen Käyttö (Public Domain) lisenssiä. Esimerkit ja muut tehtävät on suunniteltu siten, että niiden käytöstä ei pitäisi koitua ongelmia, mutta siitäkin huolimatta lopullinen vastuu tehtävien ajamisesta jätetään lukijalle. Oppaan tekemiseen osallistuneet henkilöt tai eivät vastaa mahdollisista ongelmista, vahingoista, vioista, tappioista tai tuotannonmenetyksistä. Lappeenrannan teknillinen yliopisto 2007 23