Vorgurakendused 2 prax 2 2018 kevad
Sisukord
- 1 Ülesanne
- 2 Esimene variant: terve distributed ledger
- 2.1 Transaktsioon
- 2.2 Digiallkirjastamine, avalikud ja salajased võtmed
- 2.3 Transaktsioonide kokkukogumine blokiks ja blokiandmete ülekontrollimine
- 2.4 Bloki Merkle juure (merkle root) arvutamine =
- 2.5 Bloki kaevandamine
- 2.6 Mitme paralleelbloki korral õige valimine
- 2.7 Loodud bloki automaatselt teistele edasisaatmine
- 3 Teine variant: autentimine ja framework
Ülesanne
Teise praktikumi jaoks võid valida ühe kahest ülesandest:
- Minimaalse funktsioneeriva distributed ledgeri ehitamine esimese praksi edasiarendusena.
- Mitmel viisil autentimist (näiteks nii paroolid kui facebook või id-kaart) vajava https kaudu töötava rakenduse loomine, kasutades mõnda levinud raamistikku (näiteks Laravel või Spring) nö suurel mainstream hostinguplatvormil.
Esimene variant: terve distributed ledger
See praktikum tuleb ehitada esimese ledgeri-praktikumi edasiarendusena. Eesmärk on saada käima lihtsustatud versioon bitcoini-tüüpi distributed ledgerist. Keerukaid nüansse - mis päris-bitcoini puhul vajalikud - realiseerida ei ole vaja.
Mis meil on - kokkuvõtlikult - vaja esimesele praksile lisada:
- Transaktsiooni sisestamine ja konkreetses formaadis edastamine: kes kannab kellele kui palju raha üle
- Digiallkirjastamine, avalikud ja salajased võtmed
- Transaktsioonide kokkukogumine blokiks ja bloki ülekontrollimine
- Bloki merkle juure ehitamine (salvestame lõpuks ainult tipmise hashi puust)
- Bloki kaevandamine: leia bloki hash, mis algaks N nulliga, katsetades eri nonce. Sea N nii, et kaevandamine võtaks ca 5 sek.
- Mitme paralleelbloki olemasolul pikema ahela või parema bloki valimine
- Loodud bloki automaatselt teistele edasisaatmine
Eeldame, et sinu programm juba kuulab ja salvestab ja saadab edasi talle saadetud transaktsioone ja blokke.
Konkreetseid detaile ei kirjuta me siin praksis kohustuslikuna ette, kuid järgmisena on toodud soovitusi ja nõuandeid.
Päris hea arusaamist tekitava veebidemo leiad siit: kindlasti vaata ka ülalt menüüst keys/signatures/transaction demosid.
Transaktsioon
Võiks olla ilma allkirjata kujul
{"from": "sdasd212121", "to": "dsda9sdasdas9", "sum": 0.01, "timestamp": "2012-04-21T18:25:43-05:00"}
kus timestamp on iso kujul. Seejuures transaktsioon tuleb enne signeerimist stringiks teisenda mingil ühesel universaalsel kujul (a la keyd konkreetses järjekorraks ja tühikud key-value paaride vahelt eemaldatud) ja allkirjaga kujul
{"signature":"alasasasp11", "transaction": {"from": "sdasd212121", "to": "dsda9sdasdas9", "sum": 0.01, "timestamp": "2012-04-21T18:25:43-05:00"}}
Mis on see "from" ja "to" väärtus, ehk, kuidas kasutajaid/kontosid identifitseerida? Las need olla lihtsalt kasutajate avalikud võtmed, millest me järgmisena räägime.
Sinu programm peaks olema võimeline tegema transaktsioone: näiteks nii, et käivitad ta käsurealt ja annad ette transaktsiooni jsoni, tema allkirjastab ja saadab allkirjaga edasi.
Või siis ta loeb iga N sekundi tagant faili, kuhu saad kirjutada transaktsioonide jsone.
Või mõnel muul sulle mugaval moel.
Allkirjastatud transaktsioon tuleks teistele progedele edasi saata.
Digiallkirjastamine, avalikud ja salajased võtmed
Sul on oma "konto" tegemiseks vaja genereerida mingi public-key krüptosüsteemi jaoks avaliku ja salajase võtme paar. Muidugi on oluline, et kõik võrgustiku osalised kasutaks sama public-key krüptosüsteemi.
Kui oled genereerinud uue avaliku võtme A ja vastava privaatvõtme P, siis, teades A-d, ei suuda keegi teine reaalselt leida sinu P-d.
Olgu sinu A avalik võti ja P sinu vastav privaatvõti. Siis krüpteeri(T,P) annab stringi K, nii et igaüks saab teha dekrüpteeri(K,A) mis annab tulemuse X ja näha, et tulemus X võrdub algse stringiga T. Samas ei suuda keegi genereerida ise stringi K (teadmata P-d), et saaksime dekrüptimisel sama tulemuse T.
Peamiselt kasutatakse kas RSA tüüpi või elliptilise kõvera tüüpi public-key krüptosüsteeme. Näiteks vana eesti id-kaart kasutas RSA-d, peale sügisest turvaauku hakati kasutama elliptilist kõverat. Bitcoin kasutab samuti elliptilist kõverat: ECDSA varianti standardse elliptise kurviga “secp256k1”. Meie praksis kasuta lihtsalt sellist süsteemi, mille saad kergemini käima ja mida on sul kergem kasutada.
Kust leida public key krüptograafia teeke?
- Pythoni jaoks on hea variant näiteks python-ecdsa
- Kõige mainstreamim universaalne teek (koos käsurea-utiliitidega) on openssl
- Pythonis saad openssl-i kasutada näiteks pyopenssl wrapperiga või käsurea-utiliitidega.
Hea mõte on wikipediast lisaks lugeda public key cryptography ja digital signature.
NB! Põhimõtteliselt võid digiallkirja (ehk, privaatvõtmega krüpteeritud stringi) arvutada kas otse transaktsiooni stringi-kujust või siis selle stringi-kuju SHA hashist. Miks SHA hashe kasutatakse? Sest pika stringi krüpteerimine on aeglane ja SHA hash on suhteliselt lühike ka pikkade algstringide korral.
Transaktsioonide kokkukogumine blokiks ja blokiandmete ülekontrollimine
Ütleme, et sinuni on jõudnud N transaktsiooni. Sina tahad nad blokiks kokku panna. Kõigepealt vaata, millised transaktsioonid juba on mõnes sinule teada olevas blokis: neid ära võta.
Järgmisena peaksid kontrollima, kas kõik transaktsioonid on õieti allkirjastatud: selleks pead transaktsiooni allkirjad transaktsiooni avaliku võtmega dekrüptima ja kontrollima, kas tuleb transaktsioonistring: vaata eelmist peatükki.
Järgmisena peaksid kontrollima, kas transaktsiooni tegijal on piisavalt raha: selleks summeeri kõik talle saadetud ja tema poolt saadetud summad üle kõigi sulle teada olevate blokkide.
Järgmisena peaksid kontrollima, kas mõni transaktsioon on juba uues kokkupandavas blokis või mõnes vanas blokis olemas: kui jah, siis seda ei tohi kasutada.
Kui mõni transaktsioon on valesti allkirjastatud või raha ei jätku või on topelt, viska see transaktsioon lihtsalt ära.
Reaalse bitcoini kontrolli-algoritmi leiad siit ja andmestrukuurid siit: seal on praktikumi jaoks liiga palju detaile, nii et soovitaks mitte hakata kogu seda "ametlikku" süsteemi implementeerima.
Lisa transaktsioonidele veel üks: endale nullist raha kandmine. Pane see näiteks viimaseks transaktsiooniks ja kanna endale 1 coin. Saatja identifikaatoriks pane näiteks 0. Seda transaktsiooni pole mõtet allkirjastada, jäägu see "eritransaktsiooni" jaoks tühjaks (kontrollimise käigus peaks siis vastavalt vaatama, et blokis võib olla üks selline 0-saatjaga ilma allkirjata transaktsioon ja see on ok).
Nüüd pane kontrolli edukalt läbinud transaktsioonid blokiks kokku. Selles tee näiteks key/value struktuur ehk json näiteks nii:
{ "nr": bloki_number_ehk_eelmise_bloki_nr+1, "previous_hash": ahelas_eelmise_bloki_hash, "timestamp": "2012-04-21T18:25:43-05:00", "nonce": "asasas11", "hash": "000000sasas0a0s0111", "creator": minu_avalik_võti, "merkle_root": "asasasas1w111", "count": number_of_transactions, "transactions": [{...}, ..., {....}] }
Bloki Merkle juure (merkle root) arvutamine =
Mõte selles, et seome kõik bloks olevad transaktsioonid nende hashide kaudu puukujuliselt üheks merkle hashiks ja ei oleks vaja hoida iga transaktsiooni hashi eraldi. Seda peab tegema kindlas järjekorras, et teised kontrollida saaks. Kõigepealt pead arvutama iga transaktsiooni hashi (selleks konverdi transaktsioon stringiks) ja siis hakka neid hashe hierarhiliselt paariviisi kokku konkateneerima ja selliselt paariviisi kokku liidetud hashidest uusi hashe arvutama.
Vaata Merkle puu kohta
Siit leitud seletus rehkendusele:
In each iteration, you concatenate two subsequent hashes of the previous level, and double-sha256 them. If there is an odd number of hashes, concatenate the last element with itself. This gives you a new list of hashes. Repeat, and stop when one hash remains. This is the merkle root.
Assume you have tx hashes Ha1,Ha2,Ha3,Ha4,Ha5,Ha6,Ha7
- First iteration: Hb1=Hash(Ha1|Ha2), Hb2=Hash(Ha3|Ha4), Hb3=Hash(Ha5|Ha6), Hb4=Hash(Ha7|Ha7)
- Second iteration: Hc1=Hash(Hb1|Hb2), Hc2=Hash(Hb3|Hb4)
- Third iteration: Hd1=Hash(Hc1|Hc2) => Merkle root
NB! Bitcoinis on kombeks arvutada topelt-hashe a la hash(hash(data)) aga meil ei ole mingit põhjust seda teha. Soovitan kasutada lihtsalt ühekordset hashi.
Bloki kaevandamine
Nüüd on sul vaja leida bloki oma hash, kusjuures sobivad ainult sellised hashid, kus on ees N nulli. Katseta, et mitu nulli peaks ees olema, et saaksid ühe sobiva hashi ca viie sekundi jooksul (päris bitcoinis on N-e niipalju, et keskmiselt tuleks üks blokk kümnes minutis kogu võrgu kohta). See nullide nõue on kasutusel ainult selleks, et blokke tuleks harvemini, n.ö. pidur blokkide kiirele genereerimisele.
Selleks võta kogu oma bloki sisu a la
{ "nr": bloki_number_ehk_eelmise_bloki_nr+1, "previous_hash": ahelas_eelmise_bloki_hash, "timestamp": "2012-04-21T18:25:43-05:00", "nonce": "asasas11", "hash": "000000sasas0a0s0111", "creator": minu_avalik_võti, "merkle_root": "asasasas1w111", "count": number_of_transactions, "transactions": [{...}, ..., {....}] }
aga ilma "nonce" ja "hash" väljadeta ja muuda ta stringiks.
Nüüd tee läbi tsükkel:
- leia lühike random string "nonce" N ja pane see eelnevalt leitud blokistringi S lõppu juurde, saad stringi X.
- arvuta saadud stringi X hash.
- kui saadud hash algab N nulliga, kõik ok ja lõpeta töö.
- vastasel korral mine tsükli esimesele reale tagasi.
Kui sobiv hash leitud, lisa blokile leitud sisuga "nonce" väli ja "hash" väli.
Mitme paralleelbloki korral õige valimine
Sul on üldjuhul suur hulk blokke ahelas. Kui mõni uuematest blokkidest sama "nr" väljaga on topelt, on sul hargnev blokiahel. Siis vali:
- Kõige pikem ahel (kõige suurema numbriga blokk sobib)
- Sama pikkusega ahela puhul vali see, kus
- harus on rohkem transaktsioone
- kui see ka sama, vali näiteks see, kus viimane timestamp uuem
- kui see ka sama, tee näiteks lihtsalt hashide stringivõrdlus ja vali stringijärjekorras väiksema hashiga blokk
Loodud bloki automaatselt teistele edasisaatmine
Kui sul blokk valmis, siis saada ta lihtsalt teistele programmidele edasi. Nemad saadavad seda omakorda edasi ja loodetavasti jõuab see blokk varsti kõigi hetkel võrgus töötavate ja korraliku ühendusega programmideni.
Teine variant: autentimine ja framework
Põhiülesanne on tehnoloogiastacki kasutamine, reaalne funktsionaalsus on vähem oluline: kuid mitte tähtusetu.
Konkreetse keele ja andmebaasisüsteemi osas on sul vabad käed, aga serveriraamistikud on muidugi konkreetsete keelte jaoks, seega raamistik ja keel tuleb paratamatult koos valida.
NB! Valitud raamistik ja hostinguplatvorm tuleb eelnevalt õppejõuga kooskõlastada (et sa ei valiks liiga keerulist või vastupidi, liiga lihtsat tehnoloogiastacki).
Tehnoloogiastack
Siin on kolm kohustuslikku osa: autentimine, hostinguplatvorm ja mõne veebirakenduse frameworki kasutamine.
Praktikumi hinnatakse selle järgi, kui keerulised tehnoloogiad oled valinud ja kui korralikult sinu süsteem töötab. Püüa leida sobiv kompromiss.
Kindlasti pead realiseerima (a) parooliga autentimise (b) mõne neist autentimisviisidest:
- facebookiga autentimine (soovitav)
- googlega autentimine
- id kaardiga autentimine
Väga soovitav on realiseerida süsteemi suurel hostinguplatvormil, valides ühe neist:
- amazoni AWS-s (soovitav, olemas tasuta variant, aga keeruline)
- Microsofti pilveteenus (umbes analoogne Amazoniga)
- digital ocean (lihtne)
- zone (lihtne)
Aga, võimalik on kasutada ka mõnda muud (virtuaal)serverit, kus sul on root access ja võid kõike teha: see tuleks eelnevalt kooskõlastada õppejõuga.
Kolmandaks, pead kasutama mõnda serveripoolset raamistikku (laravel, spring boot vms). Vali pigem mõni levinud raamistik. Kõige harivam on tõenäoliselt mõne Java raamistiku - a la Spring - valimine.
Soovitav, aga mitte kohustuslik, on kasutada front-endi jaoks samuti raamistikku: siin soovitaks kas
- reactjs
- vue
- angular
kusjuures vue on kõige lihtsam ja angular kõige keerulisem, reactjs aga kõige populaarsem.
Kui sa valid serveri jaoks väga lihtsa raamistiku, siis tuleks lisaks kasutada ka front end raamistikku: muidu on ülesanne liiga lihtne. Keerukama raamistiku a la Spring puhul ei ole front end raamistik kohustuslik.
Soovitav, aga mitte rangelt kohustuslik, on realiseerida https ühenduse, tehes selleks mh kas vajalikud serdid või kasutades tasuta letsencrypti.
Soovitav, aga mitte rangelt kohustuslik, on domeeni regamine ja kasutuselevõtt, vt ka tasuta dünaamilise dnsi teemat
Mida sinu veebirakendus peab kindlasti suutma teha
Pead salvestama datat andmebaasi ja võimaldama sealt otsida. Mida salvestada ja mida otsida, sõltub sinu enda valitud teemast. Vali ise mõni mõtestatud teema, mille jaoks teed nö lihtsa beeta: see teema tuleb õppejõuga enne kokku leppida.
Mõned tehnilised juhendid
AWS kasutamine
- AWS instance genereerimine
- AWS instancega ühenduse saamine
- Muid hostingu-tähelepanekuid (Kui Amazoni platvormi kasutaja saamine ei õnnestu, on järgmine eelistus siit digitaloceani kupongi kasutada)
https töölepanek ilma letsencryptita
Enne ära hakka id-kaardi autentimist tegema, kui lihtsalt https veel ei tööta. NB! Kui sulle tuleb ette "untrusted" vms, siis kinnita erand ja jätka rahulikult.
Creating https connection:
- edit existing security groups: click on them, in the panel below add rule for https (tcp and port 443)
- then follow https://www.digitalocean.com/community/tutorials/how-to-create-a-ssl-certificate-on-apache-for-ubuntu-14-04
- Tasuta CA sertifikaat (alternatiiv eelmisele): https://www.digitalocean.com/community/tutorials/how-to-secure-apache-with-let-s-encrypt-on-ubuntu-14-04 (Amazon AWS jaoks ei toimi)
- you may need to additionally do
/etc/apache2$ sudo cp mods-available/socache_shmcb.load mods-enabled/
- then test https
Kui https töötab, siis jätka:
ID kaardi kasutamine https-s
- id-kaardi kasutamise juhend siin võid crl-i värgi esialgu üldse ära jätta, st ka apache konfis võid esialgu jätta muutujad SSLCARevocationPath ja SSLCARevocationFile välja kommenteerituks.
- Uusim juursertifikaat on juhendist puudu, 2015 või uuemate idkaartide jaoks on vaja
https://sk.ee/upload/files/ESTEID-SK_2015.pem.crt lisaks
- Fiksitud php kood id-kaardi juhendist kasuta seda!
IT-maja arvutiklassis firefox vaikimisi ei suuda id-kaarti kasutada, küll aga chrome.
https id-kaardiga ja harilikult: kuidas teha
Facebooki autentimine
Facebooki app
Kõigepealt on sul vaja teha facebooki app:
- Mine https://developers.facebook.com/ vajuta "my apps" nuppu ja vali "Add a new app" menüüst.
- Pane mingi nime "Example" ja kategooria, näit. "Education". Lahenda captcha.
- Nüüd saad lisada "toote" (product). Kui klikkida "Facebook login" juures "Get started", läheb käima wizardilaadne asi
- Vali "Web" (teised valikud "ioS", "Android", "Canvas")
- Sisesta site Url, näit "ec2-52-59-46-144.eu-central-1.compute.amazonaws.com"
- Näidatakse valmis genereeritud javascripti juppe, aga neid on testimiseks hiljem mugavam dokumentatsiooni alt võtta
Kui protsess alates captchast kuidagi erinev on, siis igatahes peab lõpptulemusena vasakul menüüs "Products" all olema "Facebook Login". Kui ei ole, siis kliki "+Add Product".
"Dashboard" või "settings" alt saab vaadata oma App ID-d. See on kasulik kusagile edaspidiseks salvestada. Hiljem saab seadetele ligi https://developers.facebook.com/apps kaudu.
Veebileht
Järgmisena tee veebileht:
ametlik FB sisselogimisõpetus: töötab väga ok, olulise asjana pane sinna sisse oma FB appi id (eeldatavalt pikk number), mille saad, kui teed FB appi lehe https://developers.facebook.com/apps kaudu.
Testimiseks saab kopeerida kogu näidiskoodi (tegu on terve html lehega), salvesta see näiteks fb.html
nime all ning kopeeri /var/www/html
kataloogi oma VPS-is. Kui App ID õige on, siis login nupp ka toimib ja avab login dialoogi. Vt dokumentatsioonist, kuidas tõlgendada sisselogimise staatust ja lugeda välja isiku userid-d.
Google autentimine
vt http://lambda.ee/wiki/Autentimise_m%C3%A4rkmed:_facebook_ja_google#Google
mobiil-ID
vt http://lambda.ee/wiki/Autentimise_m%C3%A4rkmed:_mobiil-ID
Praktikumi kirjeldus täieneb jooksvalt semestri käigus