- CS-EJ4404
- 5. Avaimen käyttö, jono- ja lohkosalain
- 5.4 Lohkosalain
Lohkosalain¶
Tässä osiossa opimme lohkosalaimen toimintaperiaatteen.
Tämä osio on viimeinen kerta kurssilla, kun luomme itse periaatteessa oikein toimivia, mutta todennäköisesti turvattomia kryptografisten algoritmien prototyyppejä. Tämän vuoksi tässä osiossa kysymykset painottuvat hieman enemmän annettujen koodien ymmärtämiseen.
Yksi tehtävien tavoite on auttaa hahmottamaan, että myöhemmissä opetusmoduuleissa käyttämämme kryptografiset kirjastot tekevät paljon tarkistuksia ja muunnoksia datalle sekä avaimille.
Kuten videossa kerrotaan, lohkosalaimessa on kolme toiminnallisuutta:
datan lohkoihin jakava toiminnallisuus
salain, joka enkoodaa tai dekoodaa yksittäisen lohkon
lohkojen yhdistäjä, joka kokoaa salaimen tuottamat lohkot kokonaisuudeksi
Enkoodaus ja dekoodaus, eli tässä tapauksessa salaus ja purku, käyttävät samaa koneistoa.
Yksinkertainen lohkosalain¶
Alla on koodiesimerkki, joka toteuttaa yksinkertaisen lohkosalaimen merkkijonolle. Funktiolle annettu merkkijono operoidaan kirjainmerkki kerrallaan XOR-operaatiolla avaimen kanssa. Jokaisen kirjaimen oletetaan olevan koodattuna 8 bitin eli yhden tavun kokoinen.
Aja koodi seuraavassa solussa. Siinä kiinnitämme avaimet sekä näytämme salattavan informaation, eli merkkijonon, sisällön. Jos tehtävät vaativat avaimen tai viestin muuttamista, on muutokset tehtävä tähän seuraavaan koodisoluun. Kiinteän avaimen tai kertakäyttöluvun alustaminen algoritmin testivaiheessa on ok.
Kiinnitämme 8-bittiseksi k1-avaimeksi heksatavun
0x6A
Kiinnitämme 64-bittiseksi k2-avaimeksi
0xA1B2C3D4E5F60718
.
Kannattaa huomata, että 8-bittinen avain on tyypiltään integer ja 64-bittinen on string.
Tämä valinta on tehty vain sen takia, että voimme käsitellä 64-bittistä avainta ascii-merkkeinä joissain tehtävissä.
k2 voisi suoraan olla k2=0xA1B2C3D4E5F60718
.
Luonnollisesti voisimme käyttää kryptografisesti vahvaa satunnaislukua avaimena, mikä onkin kryptografiassa oikea tapa. Tämä kuitenkin vaikeuttaisi algoritmin toiminnan demonstroimista ja yksityiskohtien selvittämistä esimerkkitehtävissä.
# Aktivoidaan kirjastot joita käytämme
from numpy import binary_repr
import binascii
# Kuten tiedämme yksi tapa tuottaa oikea avain on käyttää entropia-allasta: secrets.randbelow(2**bittejä)
# Tässä esimerkissä lukitsemme avaimen, jotta voimme selittää bittitasolla mitä tapahtuu
# Lukitaan avaimet
k1 = 0x6A
k2 = "0xA1B2C3D4E5F60718"
# Näytetään avaimet
print(f" 8-bittinen avain, jota käytämme yksinkertaisessa lohkosalaimessa: 0x{k1:X}")
print("64-bittinen avain, jota käytämme yleisessä lohkosalaimessa on:",k2)
# Käytämme viestinä vanhaa tuttua virkettä
viesti = "Kahvi Charlotassa on hyvää ja vahvaa!"
print("Viestimme kuuluu:", viesti)
Seuraava koodisolu luo yksinkertaisen lohkosalaimen. Lohkosalain ottaa prosessoitavaksi dataksi merkkijonomuodossa olevan informaation ja avaimen.
Tämän yksinkertaisen lohkosalaimen lohkoja ovat merkkijonon yksittäiset merkit. Esimerkiksi ”Kissa”, prosessoitaisiin lohkoina ’K’, ’i’, ’s’, ’s’ ja viimeisenä ’a’.
Lohkosalain pystyy enkryptaamaan ja dekryptaamaan annetun merkkijonon avaimella. Menetelmä on symmetrinen, ja käytämme salainta siten, ettei meidän tarvitse kertoa olemmeko enkryptaamassa vai dekryptaamassa.
Alla oleva koodi luo ensin yksinkertaisen salaimen
Sen jälkeen salataan teksti ”Kissa” avaimella
0x6A
ja salattu lohko näytetään.Salauksen lopuksi näytetään ”Kissa” salattuna, jolloin näkyviin tulee symboleita.
Tämän jälkeen salaus puretaan samalla avaimella
0x6A
lohkoittain ja purettu lohko näytetään.Salauksen purun jälkeen näytetään saatu selväkielinen viesti. Sen pitäisi olla ”Kissa”
from numpy import binary_repr
#
# Luodaan yksinkertainen lohkosalain
#
def yksinkertainen_lohkosalain(merkkijono, avain, näytälohkot=False):
"""Yksinkertainen lohkosalain demo-funktio
Parametrit:
merkkijono (string): Lohkosalaimella enkoodattava tai dekoodattava tieto, oletus "latin-1" koodattu
avain (int): Symmetrinen avain, jota käytetään enkoodauksessa ja dekoodauksessa
näytälohkot (bool): Lippu, jolla määritetään näytetäänkö lohkosalauksen välivaiheet.
Palauttaa:
string: Enkoodattu tai dekoodattu merkkijono.
"""
# Salaimissa on useita tarkistuksia suojattavan tiedon tyypeille sekä annettujen parametrien hyvyyksille.
# Nämä tarkistukset ja algoritmin sisäiset tyyppimuunnokset ovat salaimen käyttäjälle näkymättömiä.
# Seuraavassa muutetaan merkkijonon ASCII merkit tavuiksi, olettaen että merkkijono on 'latin-1' merkistö-koodattua.
# Tämä muunnos ei välttämättä toimi muilla merkki-koodauksilla.
tavutettu_merkkijono = bytes(merkkijono, encoding="latin_1")
# Luodaan paikka enkoodauksen tai dekoodauksen tulokselle
XOR_tulos = []
# Pilkotaan merkkijono lohkoiksi. Jokainen tavu (eli merkki) muodostaa oman lohkon.
for lohko in tavutettu_merkkijono:
# Suoritetaan salaimen matemaattinen operaatio (XOR) tavuittain avaimen kanssa.
XOR_tulos.append(avain ^ lohko)
# Jos haluat nähdä miten lohko käsitellään aseta funktiokutsuun näytälohkot=True
if näytälohkot:
print("Lohko on {}, avain {} ja XOR {}".format(
chr(lohko), hex(avain), binary_repr(avain ^ lohko, 8)))
# Muunnetaan salaimen tulos merkkijonoksi, yleensä haluamme käsitellä vain bittejä ja tavuja.
# Tässä esimerkissä käsittelemme kirjain-merkkejä, joten demonstraation vuoksi muunnamme salaimen tuottaman datan merkkijonoksi.
XOR_merkkijonona = "".join([chr(lohko) for lohko in XOR_tulos])
# Palautetaan merkkijonona.
return XOR_merkkijonona
#
# -- käytetään salainta -- #
#
selväkieli = "Kissa"
tehtäväavain = 0x6A
print("Salataan sana \"{}\", näytetään lohkot:".format(selväkieli))
salakieli=yksinkertainen_lohkosalain(selväkieli, tehtäväavain, näytälohkot=True)
print("{} salattuna on:{}\n".format(selväkieli, salakieli))
print("Puretaan salaus, näytetään lohkot")
purettu_salakieli = yksinkertainen_lohkosalain(salakieli, tehtäväavain, näytälohkot=True)
print("Salauksen purun tulos on:{}\n".format(purettu_salakieli))
if purettu_salakieli == selväkieli:
print("Jee saimme salattua ja purettua {}-sanan".format(selväkieli))
else:
print("Jokin meni pieleen.")
Edellisessä koodissa käytimme funktiokutsussa argumenttia näytälohkot
,
jolla näemme miten salaimen koneisto lohkoo sanan ”Kissa” 8-bittisiksi lohkoiksi.
Funktiosta toimii seuraavasti:
Funktio olettaa että annettu informaatio on tekstiä ja muuttaa sen tavukoodiksi.
Funktio lohkoo tavukoodimuodossa olevan informaation 8-bittisiksi lohkoiksi (eli tavuiksi).
Varsinainen lohkon salaus tapahtuu kohdassa
avain^lohko
.Lopuksi tuotetut lohkot yhdistetään, ja yritämme näyttää informaatiota ascii-merkkeinä.
Seuraavassa koodisolussa käytämme tätä yksinkertaista lohkosalainta.
Otamme salaimen käyttöön kirjastosta, kuten myös tehtävään kuuluvan viestin ja avaimen.
Näytämme viestin ja avaimen.
Enkoodaamme (eli salaamme) viestin lohkot (eli merkit) avaimella k1.
Yhdistämme lohkot salakirjoitetuksi merkkijonoksi.
Näytämme salatun viestin, jossa pitäisi olla näkyvissä symboleita kirjaimien sijaan.
Lopuksi suoritetaan dekoodaus (eli salakirjoituksen purku) lohkoittain avaimella k1.
from xip import alusta_t542
viesti, yksinkertainen_lohkosalain, k1, _ = alusta_t542()
print("Viesti on :", viesti)
print("Avain k1 on:", k1)
# Suoritetaan salaus yksinkertaisella lohkosalaimella viestille avaimella k1
salakirjoitus_merkkijono=yksinkertainen_lohkosalain(merkkijono=viesti, avain=k1)
print("Salattu viesti on :", salakirjoitus_merkkijono)
# Suoritetaan salauksen purku yksinkertaisella lohkosalaimella avaimella k1
purettu_salakirjoitus=yksinkertainen_lohkosalain(merkkijono=salakirjoitus_merkkijono, avain=k1)
print("Purettu viesti on :",purettu_salakirjoitus)
Voit kokeilla koodin toimintaa vaihtelemalla sekä avainta että viestiä. Voit käyttää salainta suoraan ja enkoodata vapaavalintaisen viestin haluamallasi avaimella:
from xip import yksinkertainen_lohkosalain
# Voit muuttaa seuraavien muuttujien arvoja seuraavaan tehtävään, esim salaus_avain = 0xAB
viestini = "::::Tärkeä salattava viesti:::: TF:n lihapullat on hyviä!"
salaus_avain = 0xFF
purku_avain = 0xA5
salattu_viesti = yksinkertainen_lohkosalain(merkkijono = viestini, avain=salaus_avain, näytälohkot=False)
purettu_viesti = yksinkertainen_lohkosalain(merkkijono = salattu_viesti, avain=purku_avain, näytälohkot=False)
print("Selvä :",viestini)
print("Salattu:",salattu_viesti)
print("Purettu:",purettu_viesti)
Onko yksinkertainen lohkosalain taipumaton?¶
Yksi hyvän salaimen ominaisuuksista on taipumattomuus (engl. non-malleability ). Tämä tarkoittaa sitä, että jos salaimelle annettavaan dataan tai avaimeen tehdään muutos, ei muutos saa näkyä merkityksellisellä tavalla salaimen tuottamassa datassa.
Testataan, kuinka hyvä yksinkertainen lohkosalaimemme on. Lohkosalaimemme käsittelee yksittäisiä merkkejä lohkoina. Alla olevassa tekstissä sanoja merkki ja lohko käytetään synonyymeinä.
viesti1
on jo aikaisemmista koodiesimerkeistä tuttu viesti.viesti2
:ssa ensimmäinen h-kirjain on korvattu i-kirjaimella. Näiden kirjaimien bittiero on yksi bitti. Voit tarkastaa esimerkiksi h-kirjaimen bittikuvion ajamalla seuraavan koodin:print(bin(ord('h')))
viesti3
:ssa ensimmäinen a-kirjain on korvattu q-kirjaimella. Näiden kirjaimien bittiero on myös yksi bitti, mutta muuttunut bitti sijaitsee eri kohdassa.
Viestien luomisen jälkeen näytetään, mikä ero viesteillä 1 ja 2 sekä 1 ja 3 välillä on.
Luomme kaksi 8-bittistä avainta
ka
jakb
.Näiden avainten ero on yksi bitti (LSB-päässä eli oikealla).
Tämä näytetään tulostamalla XOR avaimista
ka^kb
.
Sen jälkeen muodostamme neljä salakirjoitusta:
sk1
on muodostettuviesti1
:stä avaimellaka
sk2
on muodostettuviesti1
:stä avaimellakb
sk3
on muodostettuviesti2
:sta avaimellaka
sk4
on muodostettuviesti3
:sta avaimellaka
Lopuksi näytetään miten viestit poikkeavat.
from numpy import binary_repr
from xip import yksinkertainen_lohkosalain
# Luodaan kolme viestiä
viesti1 = "Kahvi Charlotassa on hyvää ja vahvaa!"
viesti2 = "Kaivi Charlotassa on hyvää ja vahvaa!"
viesti3 = "Kqhvi Charlotassa on hyvää ja vahvaa!"
# Näytetään mikä ero on viesti1:llä ja viesti2:lla.
for m1,m2 in zip(viesti1, viesti2):
if m1!=m2:
print("\nViestien 1 ja 2 lohkot eroavat ja ero on:")
print("1:n lohkon merkki:n \033[1;30;48m{}\033[0;0m ".format(m1),end="")
print("ja 2:n lohkon merkki: \033[1;30;48m{}\033[0;0m ".format(m2),end="")
print("ja ero bittikuviona 0b{}.".format(binary_repr(ord(m1)^ord(m2),8)))
# Katsotaan mikä ero on viesti1:llä ja viesti3:lla
for m1,m2 in zip(viesti1, viesti3):
if m1!=m2:
print("\nViestien 1 ja 3 lohkot eroavat ja ero on:")
print("1:n lohkon merkki:n \033[1;30;48m{}\033[0;0m ".format(m1),end="")
print("ja 3:n lohkon merkki: \033[1;30;48m{}\033[0;0m ".format(m2),end="")
print("ja ero bittikuviona 0b{}.".format(binary_repr(ord(m1)^ord(m2),8)))
# Luodaan kaksi avainta, joiden viimeinen bitti eroaa.
ka = 0b01011010
kb = 0b01011011
# Näytetään miten avain ka ja kb poikkeavat toisistaan suorittamalla XOR näiden välillä
print("\nAvaimen ka ja kb ero bitteinä on :0b{}".format(binary_repr(ka^kb,8)))
# Luodaan neljä salakirjoitusta
sk1=yksinkertainen_lohkosalain(merkkijono=viesti1, avain=ka)
sk2=yksinkertainen_lohkosalain(merkkijono=viesti1, avain=kb)
sk3=yksinkertainen_lohkosalain(merkkijono=viesti2, avain=ka)
sk4=yksinkertainen_lohkosalain(merkkijono=viesti3, avain=ka)
print("Neljä salakirjoitusta sk1, sk2, sk3 ja sk4 on luotu")
Seuraavissa soluissa ladataan samat muuttujat suoraan kirjastosta (arvot toki lasketaan kirjastofunktion sisällä).
Tarkistetaan miten viestistä 1 kahdella eri avaimella ka
ja kb
tuotetut salakirjoituksen lohkot poikkeavat.
Koodi vertaa salattua informaatiota sk1
ja sk2
lohkoittain ja tulostaa jokaisen lohkon kohdalla näiden eron.
from xip import alusta_t545
from numpy import binary_repr
v1, v2, v3, ka, kb, sk1, sk2, sk3, sk4 = alusta_t545()
# Salakirjoitukset 1 ja 2 on tuotettu samasta viestistä 'viesti1' kahdella eri avaimella ka ja kb.
# Lasketaan salakirjoitusten 1 ja 2 merkkien erot XOR-funktiolla ja näytetään eroavaisuus bitteinä.
for m1, m2 in zip(sk1, sk2):
print("Salattujen lohkojen ero bitteinä:0b{}".format(binary_repr(ord(m1)^ord(m2),8)))
print("\nJokainen salattu lohko poikkeaa yhdellä bitillä")
print("Tämä on sama ero kuin avaimilla:0b{}".format(binary_repr(ka^kb,8)))
Seuraavaksi tarkistetaan miten selväkielen viesteistä 1 ja 2 samalla avaimella ka
tuotetut salakirjoitukset 1 ja 3 (sk1
ja sk3
) poikkeavat.
from xip import alusta_t545
from numpy import binary_repr
v1, v2, v3, ka, kb, sk1, sk2, sk3, sk4 = alusta_t545()
# Salakirjoitukset 1 ja 3 on tuotettu samalla avaimella ka.
# Viesti 1 eroaa viestistä 3 siten, että yksi merkki on muutettu yhtä LSB-bitin muunnosta vastaavasti.
# Lasketaan salakirjoitusten 1 ja 2 merkkien erot XOR-funktiolla ja näytetään eroavaisuus bitteinä.
for m1, m2 in zip(sk1, sk3):
if m1!=m2:
print("Salattujen lohkojen ero bitteinä:0b{}".format(binary_repr(ord(m1)^ord(m2),8)))
else:
print("Lohkot identtiset")
print("\nSelväkirjoitus viestien 1 ja 3 eroa on yksi bitti yhdessä merkissä")
print("Tämä ero näkyy suoraan salakirjoituksissa yhden lohkon yhden bitin erona.")
Lopuksi tarkistetaan, miten selväkielen viesteistä 1 ja 3 (v1
, v3
) samalla avaimella ka
tuotetut salakirjoitukset 1 ja 4 (sk1
ja sk4
) poikkeavat.
from xip import alusta_t545
from numpy import binary_repr
v1, v2, v3, ka, kb, sk1, sk2, sk3, sk4 = alusta_t545()
# Selväkielen viestien 1 ja 3 ero on yksi bitti yhdessä merkissä.
# Salakielet 1 ja 4 on tuotettu samalla avaimella ka.
for m1, m2 in zip(sk1, sk4):
if m1!=m2:
print("Salakirjoitus lohkojen ero bitteinä:0b{}".format(binary_repr(ord(m1)^ord(m2),8)))
else:
print("Lohkot identtiset")
print("Salakirjoituksissa on eroa vain yhden bitin verran, kuten selväkielen viesteissä.")
Käytimme yllä lohkosalainta kolmella viestillä, jotka keskenään keskenään vain hieman, sekä kahta avainta, jotka poikkeavat yhdellä bitillä. Viestit ja avaimet oli valittu edellisiin esimerkkeihin niin, että selväkielen viesteissä ja avaimissa on pienin mahdollinen ero, eli 1:n bitin ero.
Seuraava 64-bittinen lohkosalain kärsii samasta ongelmasta. Käytetty bittimäärä ei vaikuta siihen, että kehnon lohkosalaimen sisäänmenevän ja ulostulevan informaation välillä on selkeä yhteys.
64-bittinen lohkosalain¶
Aiemmilla opetuskerroilla olet käyttänyt valmiita funktioita, joiden sisällä olevaa koodia ei ole esitelty. Seuraavaksi muunnamme aiemmin luomamme 8-bittisen lohkosalaimen toimimaan 64-bittisenä. Näet koodista, että yksinkertaisenkin lohkosalauksen tekeminen vaatii tiettyjä välivaiheita ja muunnoksia. Koodin pitäisi myös tarkistaa muuttujatyypit ja huolehtia ettei koodin suoritusaika poikkea eri avaimilla. Kaikki nämä oikean maailman vaatimukset kryptografisten funktioiden hyvyyksistä on jätetty nyt pois.
Tämän osuuden jälkeen emme enää rakenna esimerkkifunktioita itse. Tärkeä periaate:
KÄYTÄ VALMIITA TESTATTUJA FUNKTIOITA KRYPTOGRAFISISSA OPERAATIOISSA - ÄLÄ TEE FUNKTIOITA ITSE.
Lohkosalaimemme lohkon koko on 64 bittiä. Tämä tarkoittaa sitä, että salaimelle tuleva data käsitellään 64 bitin kokoisina paloina. Merkkijonoon pohjautuva esimerkkisalaimemme lohko täytetään 8 tavulla, joista jokainen on 8 bittiä (8 tavua x 8 bittiä tavussa = 64 bittiä).
Joudumme nyt lohkotuksen ja lohkojen yhdistämisen lisäksi takaamaan, että lohko täyttyy.
Jos informaatiota on lohkon kokoa vähemmän, on lohko täytettävä. Salaimelle tuotetaan aina ”täysi lohko” dataa enkoodattavaksi tai dekoodattavaksi.
Jos lohkoon pitää keinotekoisesti tuottaa sisältöä, niin tällaista operaatiota kutsutaan päddäykseksi (engl. padding). Alla on näytetty esimerkin omaisesti miten, tällainen päddäys voitaisiin tehdä.
Päddäys suoritetaan seuraavasti:
Tässä omassa päddyksessämme lisäämme annetun datan perään tavuja järjestyksessä alkaen 0x00:sta aina 0x06:n asti, kunnes lohko on täynnä.
Jos informaatiossa on seitsemän merkkiä, esimerkiksi ”91AB4d?”, pädätään siihen yksi tavu 0x00, jolloin lohkossa on kahdeksan tavua.
Jos informaatiossa on esimerkiksi viisi kirjainta, esimerkiksi ”ABCDE”, täytyy informaation perään pädätä 0x00 + 0x01 + 0x02, jolloin lohko täyttyy kahdeksaan tavuun.
Samoin jos informaatiossa on vain yksi kirjain, esimerkisi ”Ä”, siihen pädätään 0x00 + … + 0x06, jolloin saavutetaan kahdeksan tavua.
Päddäys pitää myös pystyä poistamaan. Tähän on tehty oma funktio, joka käy dekoodatun informaation läpi ja palauttaa datan siihen asti kunnes kohtaa arvon 0x00. Tällainen päddäys on melko heikko ja haavoittuvainen tietyille hyökkäyksille. Tässä omassa salaimessamme suoritamme päddäyksen ennen enkoodausta ja poistamme päddäyksen dekoodauksen jälkeen.
Enkoodatessa lohko päddäyksen jälkeen muunnetaan biteiksi, jolle suoritetaan avaimella XOR.
Salain suorittaa operaation kiinteällä 64-bittisellä avaimella k2
.
Alla olevat funktiot on tuotu nähtäville, mutta ne ladataan myöhemmin kirjastosta.
# Tätä funktiota käytetään kun selvätekstiin lisätään merkkejä lohkon täyttämiseksi.
def päddäys(merkkijono, lohkon_koko=64, merkistö='latin-1'):
""" Päddäys-funktio, lisää merkkijonon loppuun heksoja 0x00, 0x01, ... kunnes lohkon koko bitteinä täyttyy
Parametrit:
merkkijono (string): Merkkijono johon lisätään päddäys, oletus "latin-1"-merkistökoodaus
lohkon_koko (int): Lohkon koko bitteinä, oletus 64-bittiä.
Palauttaa:
string: Merkkijono, jonka bittimäärä on jaollinen lohkon koon bittimäärällä.
"""
mj_pituus_bits = len(merkkijono.encode(merkistö))*8
return merkkijono + "".join([chr(laskuri) for laskuri in range(int((lohkon_koko - (mj_pituus_bits)%lohkon_koko)/8))])
# Tätä funktiota käytetään poistamaan päddäys dekryptatusta merkkijonosta.
def päddäyksen_poisto(merkkijono, pad_alku='\x00'):
""" Päddäyksen poisto -funktio, palauttaa merkkijonon ilman päddäystä
Parametrit:
merkkijono (string): Merkkijono johon lisätään päddäys, oletus "latin-1"-merkistökoodaus
pad_alku (chr): Tunniste mikä määrittää milloin päddäys alkaa.
Palauttaa:
string: Merkkijono, jossa ei ole enää päddäystä.
"""
return merkkijono[:merkkijono.find(pad_alku)]
print("Päddäyksen-funktiot luotu")
Seuraavassa koodi-lohkossa luomme yleisen lohkosalaimen. Tämä lohkosalain salaa merkkijonoja, mutta periaatteessa se voisi prosessoida mitä tahansa tavumuodossa olevaa dataa.
# Lohkosalain olettaa että se saa tavujonon, jonka koko on jaollinen lohkon koolla.
def yleinen_lohkosalain(tavujono, avain, lohkon_koko=64, merkistö='latin-1',näytälohkot=False):
assert (len(tavujono)*8)%lohkon_koko == 0, "Päddäys unohtunut"
# Tarvittaessa muutetaan merkkijono tavujonoksi 'latin-1' merkistökoodauksella
if type(tavujono) is str:
bytejono = tavujono.encode(merkistö)
# Jos avain on merkkijono (heksadesimaaleja), muunnetaan avain luvuksi
if type(avain) is str:
avain = int(avain,16)
# Paikka enkoodatulle viestille
XOR_tulos_merkkijonona = ""
# Lohkotaan bytejono siten että jokainen tavu muodostaa oman lohkon
for lohkon_numero in range((len(bytejono)*8)//lohkon_koko) :
# Otetaan yksi lohko ja tehdään siitä 64-bittinen luku
lohko = bytejono[(lohkon_numero*8):((lohkon_numero+1))*(lohkon_koko//8)]
# Ja tehdään lohkosta yksi 64-bittinen luku
lohko_int = int.from_bytes(lohko ,"big")
# Lasketaan siitä XOR avaimen kanssa.
lohkon_xor = lohko_int^avain
# Muunnetaan tulos tavujonoksi
lohkon_xor_bytes = lohkon_xor.to_bytes(8,'big')
# Tallenetaan XOR tulos listaan.
XOR_tulos_merkkijonona += "".join([chr(tavu) for tavu in lohkon_xor_bytes])
if näytälohkot:
print("Lohkon numero :", lohkon_numero)
print("Lohkon sisältö :", lohko)
print("Lohkon xor :", lohkon_xor)
print("Lohkon xor Bytes:", lohkon_xor_bytes)
print("Merkkijono nyt :", XOR_tulos_merkkijonona)
return XOR_tulos_merkkijonona
print("Yleinen merkkijonoilla toimiva lohkosalain luotu.")
Käytetään seuraavaksi yleistä salainta. Aloitetaan suorittamalla päddäys viestille.
from xip import alusta_t546
viesti, päddäys = alusta_t546()
# Suoritetaan viestille päddäys, eli tehdään siitä sellainen että viimeinenkin lohko täyttyy.
pädätty_viesti= päddäys(viesti)
print("Viesti ennen päddäystä :", viesti)
print("Viesti päddäyksen jälkeen:", pädätty_viesti)
print("\nAlkuperäisen viestin perään on lisätty tulostumattomia täytemerkkejä")
Sitten salaamme viestimme avaimella k2
.
from xip import alusta_t547
pädätty_viesti, k2, yleinen_lohkosalain = alusta_t547()
salattu_yleisellä_lohkosalaimella = yleinen_lohkosalain(pädätty_viesti, k2)
print("64-bittisellä lohkosalaimella enkoodauksen tulos on:", salattu_yleisellä_lohkosalaimella)
Ja lopuksi puretaan salattu viesti ja verrataan onko se sama kuin ennen salausta.
from xip import alusta_t548
viesti, salattu_yleisellä_lohkosalaimella, k2, yleinen_lohkosalain, päddäyksen_poisto = alusta_t548()
# Dekoodataan viesti salaimella käyttäen avainta k2.
purettu_yleisellä_lohkosalaimella_pädding = yleinen_lohkosalain(salattu_yleisellä_lohkosalaimella, k2)
# Poistetaan päddäys dekoodatusta viestistä.
purettu_yleisellä_lohkosalaimella = päddäyksen_poisto(purettu_yleisellä_lohkosalaimella_pädding)
# Näytetään miltä purettu viesti näyttää.
print("64-bittisellä lohkosalaimella dekoodauksen tulos on:", purettu_yleisellä_lohkosalaimella)
if purettu_yleisellä_lohkosalaimella == viesti:
print("Meidän 64-bittinen salain tuntuu toimivan!")
else:
print("Voi voi, 64-bittinen salain on rikki!")
Murra avain ja viesti¶
Nyt pääset itse kokeilemaan salauksen murtamista. Alice lähettää Bobille yleisellä lohkosalaimella salatun viestin. Hänen käyttämänsä lohkosalaimen lohkokoko on 64 bittiä. Tehtävänäsi on selvittää käytetty avain ja vastata sitten alla esitettyihin väittämiin.
Tuodaan salakirjoitettu viesti muuttujaan bin_viesti
.
# Tuodaan salateksti muuttujaan 'bin_viesti'
import glob
with open(glob.glob("Tekstit/*bin")[0], "r", encoding="utf_8") as file:
bin_viesti = "".join(file.readlines())
print(bin_viesti)
Olemme antaneet avaimen tähän mennessä heksadesimaaleina. Alice ja Bob käyttävät kuitenkin ihan arkisia avaimia. Seuraava koodi muuttaa kahdeksanmerkkisen tavallisen tekstin merkkijono-heksadesimaaliksi, jota käytämme avaimena salaimessa.
# Funktio palauttaa annetusta 8-merkkisestä salasanasta heksadesimaalimuodon (merkkijonona)
# Tätä palautettua merkkijonoa voi käyttää salaimen kanssa.
def merkit_heksamerkeiksi(avain, pituus=8):
assert len(avain)==pituus, "merkkijonossa pitää olla kahdeksan merkkiä"
heksa_avain="0x"
for byte in bytes(avain, encoding='latin-1'):
heksa_avain += str(hex(byte))[2:].upper()
return heksa_avain
print("Funktio luotu")
Tehtävänäsi on selvittää, mitä salakirjoitetussa viestissä sanotaan. Selvittääksesi viestin on sinun selvitettävä, mitä 8-merkkistä avainta Alice ja Bob ovat käyttäneet. Avain on hyvin ilmeinen asiayhteydestä, mutta voit joutua tekemään hieman salapoliisintyötä. Voit hyödyntää heikon lohkosalaimemme taipuvaisuutta, eli muunnella yksittäisiä avaimen merkkejä ja kokeilla mitä saat tulokseksi.
Vihje 1: Avaimessa on vain isoja ja pieniä kirjaimia. Siinä ei siis ole numeroita eikä erikoismerkkejä.
Vihje 2: Voit hyödyntää alempaa löytyvän tehtävän viimeisen kysymyksen vastausvaihtoehtoja, koska salain ei ole taipumaton.
Vihje 3: Muista, että aivan dekoodatun viestin lopussa voi olla korkeintaan seitsemän päddäysmerkkiä 0x00…0x06.
Vihje 4: Muista, että
päddäyksen_poisto
, palauttaa merkkijonon siihen asti kunnes ensimmäinen0x00
arvo esiintyy.Vihje 5: Tätä tehtävää kannattaa tehdä yhdessä kaverin kanssa. Älkää kuitenkaan jakako suoraan vastauksia, koska se vie ilon häkkeröinnistä.
from xip import alusta_t549
import glob
with open(glob.glob("Tekstit/*bin")[0], "r", encoding="utf_8") as file:
bin_viesti = "".join(file.readlines())
merkit_heksamerkeiksi, yleinen_lohkosalain, _ = alusta_t549()
# 'avaimemme' muuttujaan sijoitetaan avain millä kokeillaan murtamista.
avaimemme = "Kokeilen"
# Muunnetaan avain heksa-merkkijonomuotoon.
hex_avaimemme = merkit_heksamerkeiksi(avaimemme)
# Suoritetaan dekoodaus
dekoodattu = yleinen_lohkosalain(bin_viesti, hex_avaimemme)
# Emme suorita nyt päddäyksen poistoa, koska lelu-päddäyksemme saattaa lyehtää
# dekoodattua viestiä kun sopiva "päddäys" osuu vahingossa kohdilleen.
# Näytetään mitä saimme tulokseksi.
print("Dekoodattu viestimme on:")
print(dekoodattu)
Tässä vielä ylimääräinen alustettu solu, johon voit kirjoitella koodia.
from xip import alusta_t549
import glob
with open(glob.glob("Tekstit/*bin")[0], "r", encoding="utf_8") as file:
bin_viesti = "".join(file.readlines())
merkit_heksamerkeiksi, yleinen_lohkosalain, _ = alusta_t549()
print("Vapaa koodauslaatikko kokeiluihin.")
Pystyt vastaamaan seuraaviin kysymyksiin joko arvaamalla tai selvittämällä avaimen ja siten salakirjoituksen sisällön.
Yhteenveto¶
Tässä kappaleessa esitelty lohkosalain on pelkkä prototyyppi, mutta sen kaltaisia toimintoja suoritetaan ihan oikeassa salauksessa.
Tässä osiossa esitelty lohkosalain:
Pilkkoo enkoodattavan ja dekoodattavan informaation lohkoiksi.
Vaatii että data täyttää lohkon.
Käyttää samaa avainta enkoodauksessa ja dekoodauksessa, eli on symmetrinen.
Aikaisemmassa moduulissa opimme klassisten salainten yhteydessä permutaatiosta ja substituutiosta. Kun lohkosalaimeen lisätään substituutio-permutaatio-verkko, on mahdollista tuottaa lohkosalain, joka on käytännössä kryptografisesti taipumaton.
Käytämme seuraavassa opetusmoduulissa yhtä tällaista lohkosalainta ja opimme, miten miten sellainen toimii käytännössä.
Seuraavaksi vielä lyhyt, mutta tärkeä osio aiheesta, jota sivuttiinkin tässä osiossa.