ProHoster > blog > Utawala > GOSTIM: P2P F2F E2EE IM jioni moja na kriptografia ya GOST
GOSTIM: P2P F2F E2EE IM jioni moja na kriptografia ya GOST
Kuwa msanidi programu PyGOST maktaba (GOST cryptographic primitives katika Python safi), mara nyingi mimi hupokea maswali kuhusu jinsi ya kutekeleza ujumbe salama zaidi kwenye goti. Watu wengi huchukulia kriptografia iliyotumika kuwa rahisi sana, na kupiga simu .encrypt() kwenye nambari ya kuzuia kutatosha kuituma kwa usalama kupitia njia ya mawasiliano. Wengine wanaamini kuwa maandishi ya maandishi yaliyotumika ndio hatima ya wachache, na inakubalika kuwa kampuni tajiri kama Telegraph na wanahisabati wa olympiad. haiwezi kutekeleza itifaki salama.
Yote hii ilinisukuma kuandika nakala hii ili kuonyesha kuwa kutekeleza itifaki za kriptografia na usalama wa IM sio kazi ngumu sana. Hata hivyo, haifai kuvumbua uthibitishaji wako mwenyewe na itifaki kuu za makubaliano.
Makala itaandika rika-kwa-rika, rafiki kwa rafiki, umesimbwa kwa njia fiche kutoka mwisho hadi mwisho mjumbe wa papo hapo na SIGMA-I uthibitishaji na itifaki muhimu ya makubaliano (kwa misingi ambayo inatekelezwa IPsec IKE), kwa kutumia algoriti za kriptografia za GOST pekee maktaba ya PyGOST na maktaba ya usimbaji ya ujumbe wa ASN.1 PyDERASN (ambayo mimi tayari aliandika kabla) Sharti: lazima iwe rahisi sana kwamba inaweza kuandikwa kutoka mwanzo jioni moja (au siku ya kazi), vinginevyo sio programu rahisi tena. Pengine ina makosa, matatizo yasiyo ya lazima, mapungufu, pamoja na hii ni programu yangu ya kwanza kutumia maktaba ya asyncio.
Ubunifu wa IM
Kwanza, tunahitaji kuelewa jinsi IM yetu itaonekana. Kwa urahisi, iwe mtandao wa rika-kwa-rika, bila ugunduzi wowote wa washiriki. Sisi binafsi tutaonyesha anwani gani: bandari ya kuunganisha ili kuwasiliana na interlocutor.
Ninaelewa kuwa, kwa wakati huu, dhana kwamba mawasiliano ya moja kwa moja yanapatikana kati ya kompyuta mbili za kiholela ni kizuizi kikubwa cha utumiaji wa IM katika mazoezi. Lakini kadiri watengenezaji wanavyozidi kutekeleza kila aina ya mikongojo ya NAT-traversal, ndivyo tutakavyobaki kwenye Mtandao wa IPv4 kwa muda mrefu, kukiwa na uwezekano wa kukandamiza wa mawasiliano kati ya kompyuta kiholela. Je, ni muda gani unaweza kuvumilia ukosefu wa IPv6 nyumbani na kazini?
Tutakuwa na mtandao wa marafiki-kwa-rafiki: waingiliaji wote wanaowezekana lazima wajulikane mapema. Kwanza, hii hurahisisha kila kitu sana: tulijitambulisha, kupatikana au hatukupata jina / ufunguo, kukatwa au kuendelea kufanya kazi, tukijua mpatanishi. Pili, kwa ujumla, ni salama na huondoa mashambulizi mengi.
Kiolesura cha IM kitakuwa karibu na suluhisho za kawaida miradi isiyo na maana, ambayo ninaipenda sana kwa udogo wao na falsafa ya njia ya Unix. Mpango wa IM huunda saraka na soketi tatu za kikoa cha Unix kwa kila mpatanishi:
katika-ujumbe uliotumwa kwa interlocutor umeandikwa ndani yake;
nje - ujumbe uliopokelewa kutoka kwa mpatanishi unasomwa kutoka kwake;
hali - kwa kusoma kutoka kwake, tunapata ikiwa interlocutor imeunganishwa kwa sasa, anwani ya uunganisho / bandari.
Kwa kuongeza, tundu la conn linaundwa, kwa kuandika bandari ya mwenyeji ambayo tunaanzisha uunganisho kwa interlocutor ya mbali.
|-- alice
| |-- in
| |-- out
| `-- state
|-- bob
| |-- in
| |-- out
| `-- state
`- conn
Njia hii inakuwezesha kufanya utekelezaji wa kujitegemea wa usafiri wa IM na kiolesura cha mtumiaji, kwa sababu hakuna rafiki, huwezi kumpendeza kila mtu. Kutumia tmux na / au mikia mingi, unaweza kupata kiolesura cha madirisha mengi kwa kuangazia sintaksia. Na kwa msaada rlwrap unaweza kupata laini ya ingizo ya ujumbe inayooana na GNU.
Kwa kweli, miradi isiyofaa hutumia faili za FIFO. Binafsi, sikuweza kuelewa jinsi ya kufanya kazi na faili kwa ushindani katika asyncio bila msingi ulioandikwa kwa mkono kutoka kwa nyuzi zilizojitolea (nimekuwa nikitumia lugha kwa vitu kama hivyo kwa muda mrefu. Go) Kwa hivyo, niliamua kufanya na soketi za kikoa cha Unix. Kwa bahati mbaya, hii inafanya kuwa haiwezekani kufanya echo 2001:470:dead::babe 6666 > conn. Nilitatua shida hii kwa kutumia socat: echo 2001:470:wafu::babe 6666 | socat - UNIX-CONNECT:conn, socat READLINE UNIX-CONNECT:alice/in.
Itifaki ya asili isiyo salama
TCP inatumika kama usafiri: inahakikisha utoaji na utaratibu wake. UDP haihakikishii (ambayo inaweza kuwa muhimu wakati cryptography inatumiwa), lakini msaada SCTP Python haitoki nje ya boksi.
Kwa bahati mbaya, katika TCP hakuna dhana ya ujumbe, tu mkondo wa ka. Kwa hiyo, ni muhimu kuja na muundo wa ujumbe ili waweze kushirikiwa kati yao wenyewe katika thread hii. Tunaweza kukubali kutumia herufi ya mlisho wa laini. Ni sawa kwa wanaoanza, lakini mara tu tunapoanza kusimba barua pepe zetu, herufi hii inaweza kuonekana popote katika maandishi ya siri. Katika mitandao, kwa hiyo, itifaki zinazotuma kwanza urefu wa ujumbe katika byte ni maarufu. Kwa mfano, nje ya kisanduku Python ina xdrlib, ambayo hukuruhusu kufanya kazi na umbizo sawa XDR.
Hatutafanya kazi kwa usahihi na kwa ufanisi na usomaji wa TCP - tutarahisisha msimbo. Tunasoma data kutoka kwa soketi kwa kitanzi kisicho na mwisho hadi tusimbue ujumbe kamili. JSON iliyo na XML pia inaweza kutumika kama umbizo la mbinu hii. Lakini kriptografia inapoongezwa, data italazimika kutiwa saini na kuthibitishwa - na hii itahitaji uwakilishi sawa wa vitu, ambao JSON/XML haitoi (matokeo ya utupaji yanaweza kutofautiana).
XDR inafaa kwa kazi hii, hata hivyo ninachagua ASN.1 iliyo na usimbaji wa DER na PyDERASN maktaba, kwa kuwa tutakuwa na vitu vya hali ya juu ambavyo mara nyingi ni vya kupendeza na rahisi kufanya kazi. Tofauti na schemaless msimbo, MessagePack au CBOR, ASN.1 itaangalia data kiotomatiki dhidi ya schema yenye msimbo mgumu.
Ujumbe uliopokelewa utakuwa Msg: ama maandishi ya MsgText (pamoja na sehemu moja ya maandishi kwa sasa) au ujumbe wa kupeana mkono wa MsgHandshake (ulio na jina la mpatanishi). Sasa inaonekana kuwa ngumu zaidi, lakini hii ni msingi wa siku zijazo.
Weka jina lako mwenyewe (--jina letu alice). Wapatanishi wote wanaotarajiwa wameorodheshwa wakitenganishwa na koma (-majina-yao bob, eve). Kwa kila moja ya waingiliaji, saraka iliyo na soketi za Unix huundwa, na vile vile utaratibu wa kila ndani, nje, hali:
Unaposoma kutoka kwa soketi ya serikali, programu hutafuta anwani ya mpatanishi katika kamusi ya PEER_ALIVE. Ikiwa hakuna uhusiano na interlocutor bado, basi mstari tupu umeandikwa.
async def unixsock_state_processor(reader, writer, peer_name: str) -> None:
peer_writer = PEER_ALIVES.get(peer_name)
writer.write(
b"" if peer_writer is None else (" ".join([
str(i) for i in peer_writer.get_extra_info("peername")[:2]
]).encode("utf-8") + b"n")
)
await writer.drain()
writer.close()
Wakati wa kuandika anwani kwenye tundu la conn, kitendakazi cha "kianzisha" cha unganisho kinazinduliwa:
async def unixsock_conn_processor(reader, writer) -> None:
data = await reader.read(256)
writer.close()
host, port = data.decode("utf-8").split(" ")
await initiator(host=host, port=int(port))
Hebu fikiria aliyeanzisha. Kwanza ni wazi inafungua muunganisho kwa mwenyeji/bandari maalum na kutuma ujumbe wa kupeana mkono na jina lake:
Kisha, inasubiri jibu kutoka kwa chama cha mbali. Inajaribu kusimbua jibu linaloingia kwa kutumia mpango wa Msg ASN.1. Tunadhani kwamba ujumbe wote utatumwa katika sehemu moja ya TCP na tutaupokea kwa njia ya atomi tunapopiga simu .soma(). Tunaangalia kama tumepokea ujumbe wa kupeana mkono.
Tunaangalia kwamba jina lililopokelewa la interlocutor linajulikana kwetu. Ikiwa sio, basi tunavunja uunganisho. Tunaangalia ikiwa tayari tumeanzisha unganisho naye (mingiliaji tena alitoa amri ya kuungana nasi) na kuifunga. Foleni ya IN_QUEUES hushikilia nyuzi za Chatu na maandishi ya ujumbe, lakini ina thamani maalum ya None inayoashiria utaratibu wa msg_sender kuacha kufanya kazi ili isahau kuhusu mwandishi wake anayehusishwa na muunganisho wa TCP uliopitwa na wakati.
159 msg_handshake = msg.value
160 peer_name = str(msg_handshake["peerName"])
161 if peer_name not in THEIR_NAMES:
162 logging.warning("unknown peer name: %s", peer_name)
163 writer.close()
164 return
165 logging.info("%s: session established: %s", _id, peer_name)
166 # Run text message sender, initialize transport decoder {{{
167 peer_alive = PEER_ALIVES.pop(peer_name, None)
168 if peer_alive is not None:
169 peer_alive.close()
170 await IN_QUEUES[peer_name].put(None)
171 PEER_ALIVES[peer_name] = writer
172 asyncio.ensure_future(msg_sender(peer_name, writer))
173 # }}}
msg_sender hupokea ujumbe unaotoka (uliowekwa kwenye foleni kutoka kwenye tundu), huisasisha hadi ujumbe wa MsgText na kuzituma kupitia muunganisho wa TCP. Inaweza kuvunja wakati wowote - tunakataza wazi hii.
async def msg_sender(peer_name: str, writer) -> None:
in_queue = IN_QUEUES[peer_name]
while True:
text = await in_queue.get()
if text is None:
break
writer.write(Msg(("text", MsgText((
("text", UTF8String(text)),
)))).encode())
try:
await writer.drain()
except ConnectionResetError:
del PEER_ALIVES[peer_name]
return
logging.info("%s: sent %d characters message", peer_name, len(text))
Mwishoni, mwanzilishi huingia kwenye kitanzi kisicho na kikomo cha kusoma ujumbe kutoka kwa tundu. Hukagua kama ujumbe huu ni ujumbe wa maandishi na kuziweka kwenye foleni ya OUT_QUEUES, ambapo zitatumwa kwenye soketi ya nje ya mpatanishi husika. Kwa nini huwezi kufanya .read() na kusimbua ujumbe? Kwa sababu inawezekana kwamba ujumbe kadhaa kutoka kwa mtumiaji utaunganishwa katika bafa ya mfumo wa uendeshaji na kutumwa katika sehemu moja ya TCP. Tunaweza kusimbua ya kwanza, na kisha sehemu ya inayofuata inaweza kubaki kwenye bafa. Katika hali yoyote isiyo ya kawaida, tunafunga muunganisho wa TCP na kusimamisha utaratibu wa msg_sender (kwa kutuma Hakuna kwenye foleni ya OUT_QUEUES).
174 buf = b""
175 # Wait for test messages {{{
176 while True:
177 data = await reader.read(MaxMsgLen)
178 if data == b"":
179 break
180 buf += data
181 if len(buf) > MaxMsgLen:
182 logging.warning("%s: max buffer size exceeded", _id)
183 break
184 try:
185 msg, tail = Msg().decode(buf)
186 except ASN1Error:
187 continue
188 buf = tail
189 if msg.choice != "text":
190 logging.warning("%s: unexpected %s message", _id, msg.choice)
191 break
192 try:
193 await msg_receiver(msg.value, peer_name)
194 except ValueError as err:
195 logging.warning("%s: %s", err)
196 break
197 # }}}
198 logging.info("%s: disconnecting: %s", _id, peer_name)
199 IN_QUEUES[peer_name].put(None)
200 writer.close()
66 async def msg_receiver(msg_text: MsgText, peer_name: str) -> None:
67 text = str(msg_text["text"])
68 logging.info("%s: received %d characters message", peer_name, len(text))
69 await OUT_QUEUES[peer_name].put(text)
Wacha turudi kwenye nambari kuu. Baada ya kuunda coroutines zote wakati programu inapoanza, tunaanza seva ya TCP. Kwa kila muunganisho ulioanzishwa, huunda utaratibu wa kiitikio.
logging.basicConfig(
level=logging.INFO,
format="%(levelname)s %(asctime)s: %(funcName)s: %(message)s",
)
loop = asyncio.get_event_loop()
server = loop.run_until_complete(asyncio.start_server(responder, args.bind, args.port))
logging.info("Listening on: %s", server.sockets[0].getsockname())
loop.run_forever()
kijibu ni sawa na kianzisha na huakisi vitendo vyote sawa, lakini kitanzi kisicho na kikomo cha kusoma ujumbe huanza mara moja, kwa urahisi. Hivi sasa, itifaki ya kushikana mikono hutuma ujumbe mmoja kutoka kwa kila upande, lakini katika siku zijazo kutakuwa na mbili kutoka kwa mwanzilishi wa uunganisho, baada ya hapo ujumbe wa maandishi unaweza kutumwa mara moja.
Ni wakati wa kulinda mawasiliano yetu. Tunamaanisha nini kwa usalama na tunataka nini:
usiri wa ujumbe unaotumwa;
uhalisi na uadilifu wa ujumbe unaopitishwa - mabadiliko yao lazima yatambuliwe;
ulinzi dhidi ya mashambulizi ya replay - ukweli wa kukosa au kurudiwa ujumbe lazima kugunduliwa (na sisi kuamua kusitisha uhusiano);
utambulisho na uthibitishaji wa waingiliaji kwa kutumia funguo za umma zilizoingizwa awali - tayari tuliamua mapema kwamba tulikuwa tukifanya mtandao wa kirafiki-kwa-rafiki. Ni baada tu ya uthibitishaji ndipo tutaelewa tunawasiliana na nani;
upatikanaji usiri kamili wa mbele properties (PFS) - kuhatarisha ufunguo wetu wa kutia sahihi wa muda mrefu haufai kusababisha uwezo wa kusoma barua zote za awali. Kurekodi trafiki iliyozuiliwa inakuwa kazi bure;
uhalali/uhalali wa ujumbe (usafiri na kupeana mkono) ndani ya kipindi kimoja cha TCP pekee. Kuingiza ujumbe uliotiwa sahihi/kuidhinishwa kwa usahihi kutoka kwa kipindi kingine (hata kwa mpatanishi sawa) haufai kuwezekana;
mtazamaji tu hapaswi kuona vitambulishi vya mtumiaji, funguo za umma zinazotumika kwa muda mrefu, au heshi kutoka kwao. Kutokujulikana fulani kutoka kwa mtazamaji tu.
Kwa kushangaza, karibu kila mtu anataka kuwa na kiwango cha chini hiki katika itifaki yoyote ya kupeana mkono, na kidogo sana ya hapo juu hatimaye hufikiwa kwa itifaki za "nyumbani". Sasa hatutabuni chochote kipya. Ningependa kupendekeza kutumia Mfumo wa kelele kwa itifaki za ujenzi, lakini wacha tuchague kitu rahisi zaidi.
Itifaki mbili maarufu zaidi ni:
TLS - itifaki ngumu sana na historia ndefu ya mende, jambs, udhaifu, mawazo duni, utata na mapungufu (hata hivyo, hii haina uhusiano mdogo na TLS 1.3). Lakini hatuzingatii kwa sababu ni ngumu zaidi.
IPsec Ρ Ike - usiwe na shida kubwa za kriptografia, ingawa pia sio rahisi. Ikiwa unasoma kuhusu IKEv1 na IKEv2, basi chanzo chao ni STS, Itifaki za ISO/IEC IS 9798-3 na SIGMA (SIGn-and-MAc) - rahisi kutosha kutekeleza jioni moja.
Je, ni nini kizuri kuhusu SIGMA, kama kiungo cha hivi punde katika uundaji wa itifaki za STS/ISO? Inakidhi mahitaji yetu yote (ikiwa ni pamoja na vitambulishi vya "kuficha" interlocutor) na haina matatizo ya kriptografia inayojulikana. Ni minimalistic - kuondoa angalau kipengele kimoja kutoka kwa ujumbe wa itifaki itasababisha ukosefu wake wa usalama.
Hebu tutoke kwenye itifaki rahisi zaidi ya watu wazima nyumbani hadi SIGMA. Operesheni ya kimsingi ambayo tunavutiwa nayo ni makubaliano muhimu: Chaguo za kukokotoa zinazotoa washiriki wote wawili thamani sawa, ambayo inaweza kutumika kama kitufe cha ulinganifu. Bila kuingia katika maelezo: kila moja ya wahusika hutoa ephemeral (inayotumiwa tu ndani ya kikao kimoja) jozi muhimu (funguo za umma na za kibinafsi), kubadilishana funguo za umma, piga kazi ya makubaliano, kwa ingizo ambalo hupitisha ufunguo wao wa kibinafsi na umma. ufunguo wa interlocutor.
Mtu yeyote anaweza kuruka katikati na kuchukua nafasi ya funguo za umma na wao wenyewe - hakuna uthibitishaji wa interlocutors katika itifaki hii. Hebu tuongeze saini na funguo za muda mrefu.
Saini kama hiyo haitafanya kazi, kwani haijafungwa kwenye kikao maalum. Ujumbe kama huo pia "unafaa" kwa vipindi na washiriki wengine. Muktadha mzima lazima ujisajili. Hii inatulazimisha pia kuongeza ujumbe mwingine kutoka kwa A.
Kwa kuongeza, ni muhimu kuongeza kitambulisho chako mwenyewe chini ya sahihi, kwa kuwa vinginevyo tunaweza kuchukua nafasi ya IdXXX na kutia sahihi tena ujumbe kwa ufunguo wa mpatanishi mwingine anayejulikana. Ili kuzuia mashambulizi ya kutafakari, ni muhimu kwamba vipengele vilivyo chini ya saini viwe katika maeneo yaliyofafanuliwa wazi kulingana na maana yao: ikiwa ishara A (PubA, PubB), basi B lazima isaini (PubB, PubA). Hii pia inazungumza juu ya umuhimu wa kuchagua muundo na muundo wa data ya mfululizo. Kwa mfano, seti katika usimbaji wa ASN.1 DER zimepangwa: SET OF(PubA, PubB) itakuwa sawa na SET OF(PubB, PubA).
Hata hivyo, bado "hatujathibitisha" kwamba tumetoa ufunguo sawa wa pamoja wa kipindi hiki. Kimsingi, tunaweza kufanya bila hatua hii - kiunganisho cha kwanza cha usafiri kitakuwa batili, lakini tunataka kwamba wakati kushikana mikono kukamilika, tutakuwa na uhakika kwamba kila kitu kinakubaliwa. Kwa sasa tuna itifaki ya ISO/IEC IS 9798-3 mkononi.
Tunaweza kusaini ufunguo uliotengenezwa wenyewe. Hii ni hatari, kwani kuna uwezekano kwamba kunaweza kuwa na uvujaji katika algorithm ya saini inayotumiwa (ingawa bits-per-signature, lakini bado inavuja). Inawezekana kutia saini heshi ya ufunguo wa utokaji, lakini kuvuja hata heshi ya ufunguo uliotolewa kunaweza kuwa muhimu katika shambulio la nguvu-katili kwenye chaguo la kukokotoa. SIGMA hutumia chaguo la kukokotoa la MAC ambalo huthibitisha kitambulisho cha mtumaji.
Kama uboreshaji, wengine wanaweza kutaka kutumia tena vitufe vyao vya muda mfupi (ambayo, bila shaka, ni bahati mbaya kwa PFS). Kwa mfano, tulizalisha jozi muhimu, tulijaribu kuunganisha, lakini TCP haipatikani au iliingiliwa mahali fulani katikati ya itifaki. Ni aibu kupoteza rasilimali za entropy na processor kwenye jozi mpya. Kwa hivyo, tutaanzisha kinachojulikana kama kuki - thamani ya uwongo ya nasibu ambayo italinda dhidi ya mashambulio ya uchezaji wa nasibu yanayoweza kutokea wakati wa kutumia tena funguo za muda mfupi za umma. Kwa sababu ya kufungana kati ya kidakuzi na ufunguo wa umma wa muda mfupi, ufunguo wa umma wa upande mwingine unaweza kuondolewa kutoka kwa sahihi kama si lazima.
Hatimaye, tunataka kupata ufaragha wa washirika wetu wa mazungumzo kutoka kwa mtazamaji tu. Ili kufanya hivyo, SIGMA inapendekeza kwanza kubadilishana vitufe vya muda mfupi na kuunda ufunguo wa kawaida ambao unaweza kusimba kwa njia fiche uthibitishaji na kutambua ujumbe. SIGMA inaelezea chaguzi mbili:
SIGMA-I - inalinda mwanzilishi kutokana na mashambulizi ya kazi, jibu kutoka kwa wale watazamaji: mwanzilishi huthibitisha jibu na ikiwa kitu hailingani, basi haitoi kitambulisho chake. Mshtakiwa anatoa kitambulisho chake ikiwa itifaki hai imeanza naye. Mtazamaji tu hajifunzi chochote;
SIGMA-R - inalinda jibu kutokana na mashambulizi ya kazi, mwanzilishi kutoka kwa wale watazamaji. Kila kitu ni kinyume kabisa, lakini katika itifaki hii jumbe nne za kupeana mikono tayari zimetumwa.
Tunachagua SIGMA-I kwa kuwa inafanana zaidi na kile tunachotarajia kutoka kwa vitu vinavyojulikana vya seva ya mteja: mteja anatambuliwa tu na seva iliyoidhinishwa, na kila mtu tayari anaijua seva. Pamoja na hayo ni rahisi kutekeleza kutokana na ujumbe mdogo wa kupeana mkono. Tunachoongeza kwenye itifaki ni kusimba sehemu ya ujumbe kwa njia fiche na kuhamisha kitambulisho A hadi sehemu iliyosimbwa ya ujumbe wa mwisho:
GOST R hutumiwa kwa saini 34.10-2012 algorithm na funguo 256-bit.
Ili kuzalisha ufunguo wa umma, 34.10/2012/XNUMX VKO hutumiwa.
CMAC inatumika kama MAC. Kitaalam, hii ni njia maalum ya uendeshaji wa block cipher, iliyoelezwa katika GOST R 34.13-2015. Kama kazi ya usimbaji fiche kwa modi hii - Nyasi (34.12-2015).
Heshi ya ufunguo wake wa umma hutumiwa kama kitambulisho cha mpatanishi. Inatumika kama heshi Stribog-256 (34.11/2012/256 XNUMX bits).
Baada ya kushikana mkono, tutakubaliana juu ya ufunguo ulioshirikiwa. Tunaweza kuitumia kwa usimbaji fiche ulioidhinishwa wa ujumbe wa usafiri. Sehemu hii ni rahisi sana na ni ngumu kufanya makosa: tunaongeza kihesabu ujumbe, tunasimba ujumbe kwa njia fiche, thibitisha (MAC) kihesabu na maandishi ya siri, tuma. Tunapopokea ujumbe, tunaangalia kuwa kaunta ina thamani inayotarajiwa, thibitisha maandishi ya siri kwa kaunta, na usimbue. Je, ni ufunguo gani ninaopaswa kutumia kusimba ujumbe wa kupeana mkono kwa njia fiche, ujumbe wa usafiri, na jinsi ya kuzithibitisha? Kutumia ufunguo mmoja kwa kazi hizi zote ni hatari na sio busara. Ni muhimu kuzalisha funguo kwa kutumia kazi maalum KDF (kazi muhimu ya derivation). Tena, tusigawanye nywele na kubuni kitu: HKDF inajulikana kwa muda mrefu, imetafitiwa vyema na haina matatizo yanayojulikana. Kwa bahati mbaya, maktaba ya asili ya Python haina kazi hii, kwa hivyo tunatumia hkdf mfuko wa plastiki. HKDF hutumia ndani HMAC, ambayo kwa upande wake hutumia kazi ya hashi. Utekelezaji wa mfano katika Python kwenye ukurasa wa Wikipedia huchukua mistari michache tu ya nambari. Kama ilivyo kwa 34.10/2012/256, tutatumia Stribog-XNUMX kama chaguo la kukokotoa la heshi. Toleo la chaguo la kukokotoa la makubaliano yetu litaitwa ufunguo wa kikao, ambapo zile zinazokosekana za ulinganifu zitatolewa:
HandshakeTBS ndiyo itatiwa saini. HandshakeTBE - ni nini kitakachosimbwa. Ninakuvutia kwa uga wa ukm katika MsgHandshake1. 34.10 VKO, kwa ujanibishaji mkubwa zaidi wa funguo zinazozalishwa, inajumuisha parameta ya UKM (nyenzo za ufunguo wa mtumiaji) - entropy ya ziada tu.
Kuongeza Cryptography kwa Kanuni
Wacha tuchunguze mabadiliko tu yaliyofanywa kwa nambari ya asili, kwani mfumo ulibaki sawa (kwa kweli, utekelezaji wa mwisho uliandikwa kwanza, na kisha maandishi yote yamekatwa kutoka kwake).
Kwa kuwa uthibitishaji na utambulisho wa interlocutors utafanyika kwa kutumia funguo za umma, sasa zinahitaji kuhifadhiwa mahali fulani kwa muda mrefu. Kwa unyenyekevu, tunatumia JSON kama hii:
zetu - jozi zetu muhimu, funguo za hexadecimal za kibinafsi na za umma. yao - majina ya interlocutors na funguo zao za umma. Wacha tubadilishe hoja za safu ya amri na tuongeze usindikaji wa baada ya data ya JSON:
from pygost import gost3410
from pygost.gost34112012256 import GOST34112012256
CURVE = gost3410.GOST3410Curve(
*gost3410.CURVE_PARAMS["GostR3410_2001_CryptoPro_A_ParamSet"]
)
parser = argparse.ArgumentParser(description="GOSTIM")
parser.add_argument(
"--keys-gen",
action="store_true",
help="Generate JSON with our new keypair",
)
parser.add_argument(
"--keys",
default="keys.json",
required=False,
help="JSON with our and their keys",
)
parser.add_argument(
"--bind",
default="::1",
help="Address to listen on",
)
parser.add_argument(
"--port",
type=int,
default=6666,
help="Port to listen on",
)
args = parser.parse_args()
if args.keys_gen:
prv_raw = urandom(32)
pub = gost3410.public_key(CURVE, gost3410.prv_unmarshal(prv_raw))
pub_raw = gost3410.pub_marshal(pub)
print(json.dumps({
"our": {"prv": hexenc(prv_raw), "pub": hexenc(pub_raw)},
"their": {},
}))
exit(0)
# Parse and unmarshal our and their keys {{{
with open(args.keys, "rb") as fd:
_keys = json.loads(fd.read().decode("utf-8"))
KEY_OUR_SIGN_PRV = gost3410.prv_unmarshal(hexdec(_keys["our"]["prv"]))
_pub = hexdec(_keys["our"]["pub"])
KEY_OUR_SIGN_PUB = gost3410.pub_unmarshal(_pub)
KEY_OUR_SIGN_PUB_HASH = OctetString(GOST34112012256(_pub).digest())
for peer_name, pub_raw in _keys["their"].items():
_pub = hexdec(pub_raw)
KEYS[GOST34112012256(_pub).digest()] = {
"name": peer_name,
"pub": gost3410.pub_unmarshal(_pub),
}
# }}}
Ufunguo wa kibinafsi wa algoriti ya 34.10 ni nambari ya nasibu. Ukubwa wa biti 256 kwa mikondo ya duaradufu ya biti 256. PyGOST haifanyi kazi na seti ya ka, lakini kwa idadi kubwa, kwa hivyo ufunguo wetu wa faragha (urandom(32)) unahitaji kubadilishwa kuwa nambari kwa kutumia gost3410.prv_unmarshal(). Ufunguo wa umma hubainishwa kwa kubainisha kutoka kwa ufunguo wa faragha kwa kutumia gost3410.public_key(). Ufunguo wa umma 34.10 ni nambari mbili kubwa ambazo pia zinahitaji kubadilishwa kuwa mfuatano wa baiti kwa urahisi wa kuhifadhi na uwasilishaji kwa kutumia gost3410.pub_marshal().
Baada ya kusoma faili ya JSON, funguo za umma ipasavyo zinahitaji kubadilishwa kwa kutumia gost3410.pub_unmarshal(). Kwa kuwa tutapokea vitambulisho vya interlocutors kwa namna ya hash kutoka kwa ufunguo wa umma, wanaweza kuhesabiwa mara moja mapema na kuwekwa kwenye kamusi kwa utafutaji wa haraka. Stribog-256 heshi ni gost34112012256.GOST34112012256(), ambayo inakidhi kikamilifu kiolesura cha hash cha vitendaji vya heshi.
Je, utaratibu wa mwanzilishi umebadilikaje? Kila kitu ni kulingana na mpango wa kupeana mikono: tunatengeneza kuki (128-bit ni nyingi), jozi ya ufunguo wa muda mfupi 34.10, ambayo itatumika kwa kazi ya makubaliano muhimu ya VKO.
UKM ni nambari ya biti 64 (urandom(8)), ambayo pia inahitaji kuondolewa kutoka kwa uwakilishi wake kwa kutumia gost3410_vko.ukm_unmarshal(). Kazi ya VKO kwa 34.10/2012/256 3410-bit ni gost34102012256_vko.kek_XNUMX () (KEK - ufunguo wa encryption).
Kitufe cha kipindi kilichoundwa tayari ni mlolongo wa baiti wa 256-bit pseudo-random. Kwa hiyo, inaweza kutumika mara moja katika kazi za HKDF. Kwa kuwa GOST34112012256 inakidhi interface ya hahlib, inaweza kutumika mara moja katika darasa la Hkdf. Hatuelezi chumvi (hoja ya kwanza ya Hkdf), kwa kuwa ufunguo unaozalishwa, kutokana na ephemerality ya jozi muhimu zinazoshiriki, itakuwa tofauti kwa kila kikao na tayari ina entropy ya kutosha. kdf.expand() kwa chaguo-msingi tayari hutoa vitufe vya 256-bit vinavyohitajika kwa Grasshopper baadaye.
Ifuatayo, sehemu za TBE na TBS za ujumbe unaoingia huangaliwa:
MAC juu ya maandishi yanayoingia huhesabiwa na kuangaliwa;
maandishi ya siri yamesimbwa;
Muundo wa TBE umeamuliwa;
kitambulisho cha interlocutor kinachukuliwa kutoka humo na kinaangaliwa ikiwa anajulikana kwetu kabisa;
MAC juu ya kitambulisho hiki huhesabiwa na kuangaliwa;
sahihi juu ya muundo wa TBS imethibitishwa, ambayo inajumuisha kidakuzi cha pande zote mbili na ufunguo wa muda wa umma wa upande mwingine. Sahihi inathibitishwa na ufunguo wa saini wa muda mrefu wa interlocutor.
Kama nilivyoandika hapo juu, tarehe 34.13/2015/XNUMX inaeleza mambo mbalimbali zuia njia za uendeshaji za cipher kuanzia tarehe 34.12/2015/3413. Miongoni mwao kuna mode ya kuzalisha kuingiza kuiga na mahesabu ya MAC. Katika PyGOST hii ni gost34.12.mac(). Hali hii inahitaji kupitisha kazi ya usimbuaji (kupokea na kurudisha kizuizi kimoja cha data), saizi ya kizuizi cha usimbuaji na, kwa kweli, data yenyewe. Kwa nini huwezi kuweka msimbo mkali wa saizi ya usimbaji fiche? Tarehe 2015/128/64 inaeleza si tu XNUMX-bit Grasshopper cipher, lakini pia XNUMX-bit. Magma - GOST 28147-89 iliyobadilishwa kidogo, iliyoundwa nyuma katika KGB na bado ina moja ya vizingiti vya juu zaidi vya usalama.
Kuznechik huanzishwa kwa kupiga simu gost.3412.GOST3412Kuznechik(ufunguo) na hurejesha kitu chenye mbinu za .encrypt()/.decrypt() zinazofaa kupitisha vipengele 34.13. MAC imekokotolewa kama ifuatavyo: gost3413.mac(GOST3412Kuznechik(key).encrypt, KUZNECHIK_BLOCKSIZE, ciphertext). Ili kulinganisha MAC iliyohesabiwa na kupokea, huwezi kutumia ulinganisho wa kawaida (==) wa kamba za baiti, kwani operesheni hii huvuja wakati wa kulinganisha, ambayo, kwa ujumla, inaweza kusababisha udhaifu mbaya kama vile. BORA mashambulizi dhidi ya TLS. Python ina kazi maalum, hmac.compare_digest, kwa hili.
Chaguo la kukokotoa la msimbo wa kuzuia linaweza tu kusimba kizuizi kimoja cha data. Kwa nambari kubwa, na hata sio nyingi ya urefu, ni muhimu kutumia hali ya usimbuaji. 34.13-2015 inaeleza yafuatayo: ECB, CTR, OFB, CBC, CFB. Kila mmoja ana maeneo yake ya kukubalika ya maombi na sifa. Kwa bahati mbaya, bado hatuna viwango njia za usimbaji zilizothibitishwa (kama vile CCM, OCB, GCM na kadhalika) - tunalazimika angalau kuongeza MAC sisi wenyewe. na chagua hali ya kukabiliana (CTR): haihitaji pedi kwa saizi ya kizuizi, inaweza kusawazishwa, hutumia tu kazi ya usimbaji fiche, inaweza kutumika kwa usalama kusimba idadi kubwa ya ujumbe (tofauti na CBC, ambayo ina migongano kwa haraka).
Kama .mac(), .ctr() huchukua ingizo sawa: ciphertext = gost3413.ctr(GOST3412Kuznechik(key).encrypt, KUZNECHIK_BLOCKSIZE, plaintext, iv). Inahitajika kutaja vekta ya uanzishaji ambayo ni nusu ya urefu wa kizuizi cha usimbaji. Ikiwa ufunguo wetu wa usimbuaji unatumiwa tu kusimba ujumbe mmoja (ingawa kutoka kwa vizuizi kadhaa), basi ni salama kuweka vekta ya uanzishaji sifuri. Ili kusimba ujumbe wa kupeana mikono kwa njia fiche, tunatumia kitufe tofauti kila wakati.
Kuthibitisha sahihi ya gost3410.verify() ni jambo dogo: tunapitisha mduara wa duaradufu ambamo tunafanya kazi (tunarekodi tu katika itifaki yetu ya GOSTIM), ufunguo wa umma wa aliyetia sahihi (usisahau kwamba hii inapaswa kuwa nakala mbili. idadi kubwa, na sio kamba ya baiti), 34.11/2012/XNUMX hashi na saini yenyewe.
Kisha, katika kianzisha tunatayarisha na kutuma ujumbe wa kupeana mkono kwa kupeana mkono2, tukifanya vitendo sawa na tulivyofanya wakati wa uthibitishaji, kwa ulinganifu tu: kusaini kwenye funguo zetu badala ya kuangalia, nk...
Kipindi kinapoanzishwa, funguo za usafiri hutolewa (ufunguo tofauti wa usimbaji fiche, kwa uthibitishaji, kwa kila wahusika), na Panzi inaanzishwa ili kusimbua na kuangalia MAC:
Utaratibu wa msg_sender sasa husimba barua pepe kabla ya kuzituma kwenye muunganisho wa TCP. Kila ujumbe una nonce inayoongezeka mara moja, ambayo pia ni vekta ya uanzishaji inaposimbwa kwa njia fiche katika hali ya kaunta. Kila uzuiaji wa ujumbe na ujumbe umehakikishiwa kuwa na thamani tofauti ya kaunta.
GOSTIM imekusudiwa kutumiwa pekee kwa madhumuni ya kielimu (kwani haijashughulikiwa na vipimo, angalau)! Nambari ya chanzo ya programu inaweza kupakuliwa hapa (Π‘ΡΡΠΈΠ±ΠΎΠ³-256 Ρ ΡΡ: 995bbd368c04e50a481d138c5fa2e43ec7c89bc77743ba8dbabee1fde45de120). ΠΠ°ΠΊ ΠΈ Π²ΡΠ΅ ΠΌΠΎΠΈ ΠΏΡΠΎΠ΅ΠΊΡΡ, ΡΠΈΠΏΠ° GoGOST, PyDERASN, NCCP, GoVPN, GOSTIM ni kabisa programu ya bure, kusambazwa chini ya masharti GPLv3 +.