//c_* sú konštanty //globálne premenné uzla AdrLen[id,mod] //pole mapujúce na dĺžku adresy Multicast[adr] //pole obsahujúce zoznam všetkych multicast skupín, ktorých som členom MulticastKey[adr] //pole obsahujúce dešifrovacie kľúče prislúchajúce k danej multicast skupine MulticastAdr[adr] //pole obsahujúce adresu prvého typu pre adresy druhého typu MojaAdresa[mod] //pole obsahujúce našu adresu v oboch adresných módoch Session[adr,id] //tabuľka spojení SEQ[adr,id] //tabuľka očakávaných sekvenčných čísel DlzkaPodpisu[typ] //vráti dĺžku podpisu podľa príslušného algoritmu ARP[adr,mod] //ARP cache, obidva adresne mody (prip. vratane vyssej vrstvy) //blackbox funkcie read(počet_octetov[,zdroj]) //prečíta príslušný počet octetov zo zdroja. ak zdroj nie je uvedený, tak je to Input isBroadcast(adresa) //true ak je adresa broadcastová, false ak nie je isMulticast(adresa) //true ak je adresa multicastová, false ak nie je GetTime() //vráti aktuálny čas (vo forme timestamp) DesifrovaciKluc(adr,id,typ,adr) //vráti dešifrovací kľúč. Pre prvý adresný mód buď vlastný súkromný, alebo multicastový. Pre druhý mód vyberie z tabuľky. Ak nič nenájde, vráti null. OverovaciKluc(adr,id,typ) //vráti kľúč na overenie podpisu. Pre prvý adresný mód je to časť adresy, pre druhý adresný mód z tabuľky. Decrypt(data,typ,kluc) //dešifruje data použitím kľúča a príslušného algoritmu; vracia cleartext v prípade úspechu, null v prípade neúspechu OverPodpis(data,typ,kluc) //dešifruje podpis použitím kľúča a príslušného algoritmu Podpis(data,typ,kluc) //podpíše dáta použitím kľúča a príslušného algoritmu Hash(data,typ) //vráti hash data podľa príslušného algoritmu Len(data) //vráti dĺžku data v octetoch Left(data,num) //vráti prvých num octetov z data. ak je data kratšie ako num, tak vráti celé data Right(data,num) //vráti posledných num octetov z data. ak je data kratšie ako num, tak vráti celé data Mid(data,od,kolko) //vráti kolko octetov začínajúcich na pozícii od. (Left(Right(data,Len(data) - od),kolko)) Concat(data,...) //vráti spojenie svojich argumentov v príslušnom poradí Spracuj(data) //pošle dáta vyššej vrstve na spracovanie //+- sessid, adr_from, ... Random() //vygeneruje náhodné číslo GetKeyData() //vygeneruje dáta, ktoré sa použijú na vygenerovanie Session Key TODO GetTimestamp() //vyberie z dát timestamp GetData() //vyberie z dát dáta, ktoré sa použijú na vygenerovanie Session Key TODO SendFrame(adr,sessid,ftyp,time,data) //odošle frame na adresu adr, session id bude sessid, typ frame ftyp, timestamp bude time a dátová časť bude data. Takisto sa stará o aktualizáciu timestampu v tabuľke spojení. Push(data) //uloží data do zásobníka Pop(data) //vyberie data zo zásobníka GetSessionID(l3data) //zistí Session ID z dát od vyššej vrstvy (layer 3) GetSessID(adr) //vráti číslo voľnej Session ID pre danú adresu (alebo null ak sa to nepodarí) procedure Prijat() begin //prišiel nový frame, začíname dekódovať alg_typ := read(c_typ_velkost); //prečítame typ algoritmu adr_mod := alg_typ shr (c_typ_velkost * 8 - 1); //adresný mód je určený najvýznamnejším bitom poľa typ algoritmu adr_len := read(c_adr_len_velkost); //prečítame dĺžku adresy adr_to := read(AdrLen[adr_len,adr_mod]); //prečítame adresu prijímateľa. Dĺžku zistíme z tabuľky if (isBroadcast(adr_to)or(isMulticast(adr_to)and(adr_to in Multicast))or(adr_to = MojaAdresa[adr_mod])) then //adresa je broadcast, alebo adresa je multicast a som členom skupiny, alebo adresa je naša. //spracúvame ďalej adr_from := read(AdrLen[adr_len,adr_mod]); //prečítame adresu odosielateľa if (isBroadcast(adr_from) or isMulticast(adr_from)) then //neplatný odosielateľ exit; else //platný odosielateľ sessid := read(c_sessid_velkost); //prečítame Session ID if exists Session[adr_from,sessid] then if (GetTime()-Session[adr_from,sessid].Time > c_timeout) then //skontrolujeme či nevypršala platnosť spojenia delete Session[adr_from,sessid]; //zrušíme staré spojenie end if end if end if flen := read(c_flen_velkost); //prečítame veľkosť celého frameu dek := DesifrovaciKluc(adr_from,sessid,alg_typ); if (dek is null)and(not isBroadcast(adr_to)) then //kľúč je neplatný a frame nie je broadcastový exit; end if podk := OverovaciKluc(adr_from,sessid,alg_typ); if (podk is null) then exit; end if elen := flen - c_flen_velkost - c_sessid_velkost - 2*AdrLen[adr_len,adr_mod] - c_adr_len_velkost - c_typ_velkost; //dĺžka šifrovanej časti frameu encrypt := read(elen); //prečítame zvyšok frameu decrypt := Decrypt(encrypt,alg_typ,dek); //dešifrujeme zvyšok frameu if decrypt is null then //ak sa nepodarilo dešifrovať exit; end if podpis := Right(decrypt,DlzkaPodpisu[alg_typ]); if Hash(Concat(alg_typ,adr_len,adr_to,adr_from,sessid,flen,Left(decrypt,Len(decrypt)-DlzkaPodpisu[alg_typ])),alg_typ) <> OverPodpis(podpis,alg_typ,podk) then //podpis nesúhlasí s dátami exit; end if ftyp := read(c_ftyp_velkost,decrypt); //načítame typ frameu if (ftyp <> 1)and(not exists Session[adr_from,sessid]) then //skontrolujeme, či existuje príslušné spojenie. ftyp 1 ignorujeme, lebo ten toto spojenie zakladá exit; end if seqno := read(c_seqno_velkost,decrypt); //načítame sekvenčné číslo if read(c_sessid_velkost,decrypt) <> sessid then //overíme session ID exit; end if timestamp := read(c_timestamp_velkost,decrypt); //načítame timestamp if (ftyp in (0,1,12,13)) then //v týchto prípadoch kontrolujeme iba vek timestampu: nesmie byť novší ako aktuálny čas a nesmie byť príliš starý if (timestamp > GetTime())or(timestamp + c_timestamp_platnost < GetTime()) then //overíme platnosť timestamp exit; end if else if (ftyp in (2,3,4,5,7,8,9,11) then) //skontrolujeme, či timestamp korešponduje s uloženým timestampom if timestamp <> Session[adr_from,sessid].Timestamp then exit; end if else //aspoň čiastočná kontrola pre ostatné typy if timestamp > GetTime() then exit; end if end if if ftyp <> 1 then if seqno < SEQ[adr_from,sessid] then exit; end if if seqno > SEQ[adr_from,sessid] then //stratili sa framey, treba ich vyžiadať resend := Concat(SEQ[adr_from,sessid],...,seqno); SendFrame(adr_from,sessid,13,GetTime(),resend); exit; //? alebo spracovat aj tento frame? end if SEQ[adr_from,sessid] := seqno + 1; end if dlen := read(c_dlen_velkost,decrypt); //načítame veľkosť dát data := read(dlen,decrypt); //načítame samotné dáta //padding ignorujeme, rovnako ako podpis if adr_mod = 0 then switch ftyp case 0 //dátový frame if (Session[adr_from,sessid].Stav <> c_aktivny)and(not isBroadcast(adr_to)) then exit; end if Spracuj(data); case 1 //začiatok novej komunikácie mac := data; if ARP[adr_from,1] is null then else ARP[adr_from,1]:=mac; //pridať adresu do arp cache if ARP[adr_from,1] <> mac then //záznamy nesúhlasia //jedna z nasledujúcich možností: //exit; //ignorovať //ARP[adr_from,1]:=mac; //upraviť záznam end if end if if exists Session[adr_from,sessid] then //konflikt s už existujúcim spojením sid := GetSessId(); if sid is null then //nie je voľná session ID exit; //ignorovať frame, na druhej strane sa zruší po uplynutí timeoutu end if SendFrame(adr_from,sid,1,GetTime(),MojaAdresa[1]); //poslať vlastný návrh else create Session[adr_from,sessid]; Session[adr_from,sessid].Timestamp := timestamp; Session[adr_from,sessid].rnd1 := Random(); Session[adr_from,sessid].Data := GetKeyData(); Session[adr_from,sessid].Mac := mac; Session[adr_from,sessid].Alg_typ := alg_typ; SendFrame(adr_from,sessid,2,timestamp,Concat(Session[adr_from,sessid].rnd1,Session[adr_from,sessid].Data)); Session[adr_from,sessid].Stav := c_handshake_challenge; end if case 2 //handshake challenge if Session[adr_from,sessid].Stav <> c_odoslany_request then exit; end if rnd1 := Read(c_rnd_velkost,data); keydata := Read(c_keydata_velkost,data); Session[adr_from,sessid].Data := GetKeyData(); Session[adr_from,sessid].Key := GenerateKey(keydata,Session[adr_from,sessid].Data); SendFrame(adr_from,sessid,3,timestamp,Concat(rnd1 + 1,Session[adr_from,sessid].Data)) Session[adr_from,sessid].Stav := c_handshake_response; case 3 //handshake response if Session[adr_from,sessid].Stav <> c_handshake_challenge then exit; end if rnd1 := GetRnd1(data); if (rnd1 - 1 <> Session[adr_from,sessid].rnd1) then delete Session[adr_from,sessid]; SendFrame(adr_from,sessid,5,timestamp,null) exit; end if keydata := GetData(data); Session[adr_from,sessid].Key := GenerateKey(keydata,Session[adr_from,sessid].Data); SendFrame(adr_from,sessid,4,timestamp,MojaAdresa[1]); Session[adr_from,sessid].Stav := c_aktivny; Session[adr_from,sessid].Timestamp := GetTime(); case 4 //handshake accept if Session[adr_from,sessid].Stav <> c_handshake_response then exit; end if Session[adr_from,sessid].Mac := data; //adresa druhého adresného módu create Session[data,sessid]; Session[data,sessid].Stav := c_aktivny; Session[adr_from,sessid].Stav := c_aktivny; Session[data,sessid].Timestamp := GetTime(); Session[adr_from,sessid].Timestamp := GetTime(); case 5 //handshake reject if Session[adr_from,sessid].Stav <> c_handshake_response then exit; end if delete Session[adr_from,sessid]; case 6 //žiadosť o nový Session Key if (Session[adr_from,sessid].Stav <> c_aktivny) then exit; end if if timestamp + c_min_trvanie_kluca > GetTime() then //mozno upravit cez Session[].KeyTime? SendFrame(adr_from,sessid,8,timestamp,null); exit; end if keydata := data; Session[adr_from,sessid].Data := GetKeyData(); Session[adr_from,sessid].TempKey := GenerateKey(keydata,Session[adr_from,sessid].Data); SendFrame(adr_from,sessid,7,timestamp,Session[adr_from,sessid].Data); Session[adr_from,sessid].Stav := c_kluc_response; case 7 //odpoveď na žiadosť o nový Session Key if Session[adr_from,sessid].Stav <> c_kluc_request then exit; end if keydata := data; Session[adr_from,sessid].Key := GenerateKey(Session[adr_from,sessid].Data,keydata); SendFrame(adr_from,sessid,9,timestamp,null);//alebo miesto null dat Session[adr_from,sessid].Key ? Session[adr_from,sessid].Stav := c_aktivny; case 8 //zamietnutie žiadosti o nový Session Key if Session[adr_from,sessid].Stav <> c_kluc_request then exit; end if Session[adr_from,sessid].Stav := c_aktivny; //pokračujeme ďalej s rovnakým kľúčom ako predtým case 9 //potvrdenie nového Session Key if Session[adr_from,sessid].Stav <> c_kluc_response then exit; end if Session[adr_from,sessid].Key := Session[adr_from,sessid].TempKey; //nastavíme kľúč na novo vygenerovaný Session[adr_from,sessid].Stav := c_aktivny; case 10 //žiadosť o zrušenie spojenia if timestamp + c_cas_zrusenia < GetTime() then //aby sa zabránilo replay útokom, ignorovať príliš staré framey exit; end if SendFrame(adr_from,sessid,11,timestamp,null); delete Session[adr_from,sessid]; case 11 //odpoveď na žiadosť o zrušenie spojenia if Session[adr_from,sessid].Stav <> c_zruseny then exit; end if delete Session[adr_from,sessid]; case 12 //keepalive if Session[adr_from,sessid].Stav <> c_aktivny then exit; end if case 13 //Resend request Resend[adr_from,sessid] := data; else //neznámy typ frameu exit; end switch else switch ftyp case 0 //dátový frame if Session[adr_from,sessid].Stav <> c_aktivny then exit; end if Spracuj(data); //ostatné ftypy ignorujeme, vyžadujú TTP case 12 //keepalive if Session[adr_from,sessid].Stav <> c_aktivny then exit; end if case 13 //resend request Resend[adr_from,sessid] := data; else //neznámy typ frameu exit; end switch end if if exists Session[adr_from,sessid] then Session[adr_from,sessid].Time := GetTime(); //ak frame nebol ignorovaný, aktualizujeme platnosť spojenia end if else //adresát je niekto iný exit; end if end //Prijat() procedure Odoslat() begin sessid := GetSessionID(L3Input); //ak staci sessid tak nema zmysel hladat v ARP adr3 := L3Input.Address; if (not exists ARP[adr3]) then SendFrame(c_broadcast,0,0,GetTime(),concat(c_arp_header,adr3)); timeout := GetTime() + c_arp_timeout; Push(L3Input); L3Input := null; repeat //počkať na odpoveď Loop(); until (exists ARP[adr3])or(GetTime() > timeout); Pop(L3Input); if (not exists ARP[adr3]) then exit; end if end if adr := ARP[adr3,1]; if (adr is null)or(sessid is null) then //treba založiť novú Session adr := ARP[adr3,0]; if (adr is null) then //vôbec sa nepodarilo nájsť adresu exit; end if if sessid is null then //potrebujeme novú Session ID sessid := GetSessID(adr); if sessid is null then //nie je voľné Session ID pre túto adresu exit; end if end if SendFrame(adr,sessid,1,GetTime(),MojaAdresa[1]); Push(L3Input); L3Input := null; timeout := GetTime() + c_session_timeout; repeat //počkať na odpoveď Loop(); until ((exists Session[adr,sessid])and(Session[adr,sessid].Stav = c_aktivny))or(GetTime() > timeout); Pop(L3Input); if (not exists Session[adr,sessid]) then //nepodarilo sa založiť nové spojenie exit; end if end if fnum := Ceil(Length(L3Input.Data) / c_max_data_lenght); //počet frameov potrebných na odoslanie všetkých dát for i:= 0 to fnum - 1 do data[i] := Mid(L3Input.Data,i*c_max_data_lenght,c_max_data_lenght); SendFrame(ARP[adr3,1],sessid,0,GetTime(),data[i]); od Resend[ARP[adr3,1],sessid]:=null; resends := 0; repeat timeout := GetTime()+c_resend_timeout; repeat //počkáme na timeout, alebo žiadosť o opätovné odoslanie Loop(); until (Resend[ARP[adr3,1],sessid] is not null)or(GetTime()>timeout); if (Resend[ARP[adr3,1],sessid] is not null)and(GetTime()<=timeout) then //treba znovu odoslať while (Resend[ARP[adr3,1],sessid] is not null) do i := Read(c_seqno_velkost,Resend[ARP[adr3,1],sessid]); //prečítame sekvenčné číslo frameu na preposlanie SendFrame(ARP[adr3,1],sessid,0,GetTime(),data[i]); od //všetky čísla boli prečítané a framey odoslané, Resend[ARP[adr3,1],sessid] je prázdny resends += 1; //zvýšime počítadlo end if until (GetTime()>timeout)or(resends > c_resend_max); //vypršal čas, alebo počet pokusov end //Odoslat() procedure Loop() //podľa potreby prijme frame (ak je nejaký na vstupe), odošle frame (ak sa má nejaký odoslať) a udržuje aktívne spojenia begin if Input <> null then //ak je na vstupe nový frame Prijat(); //spracujeme ho else //inak pozrieme, či neprišli dáta na odoslanie z vyššej vrstvy if L3Input <> null then Odoslat(); //ak áno, odošleme ich end if end if for all Session[adr,id] do if (Session[adr,id].Stav = c_aktivny)and(Session[adr,id].Timestamp + c_keepalive >= GetTime())and(Session[adr,id].Timestamp + c_timeout < GetTime()) then //ak je spojenie aktívne a prekročil sa určený čas, pošleme keepalive frame aby sa spojenie udržalo ďalej SendFrame(adr,id,12,GetTime(),null); end if od end //HLAVNÁ ČASŤ BEGIN //prípadná inicializácia uzla //... while (true) do //nekonečný cyklus Loop(); od END