GOSTIM: P2P F2F E2EE IM mewn un noson gyda cryptograffeg GOST
Bod yn ddatblygwr PyGOST llyfrgelloedd (primitives cryptograffig GOST mewn Python pur), rwy'n aml yn derbyn cwestiynau am sut i weithredu'r negeseuon diogel symlaf ar y pen-glin. Mae llawer o bobl yn ystyried bod cryptograffeg gymhwysol yn eithaf syml, a bydd galw .encrypt() ar seiffr bloc yn ddigon i'w anfon yn ddiogel dros sianel gyfathrebu. Mae eraill yn credu mai cryptograffeg gymhwysol yw tynged yr ychydig, ac mae'n dderbyniol bod cwmnΓ―au cyfoethog fel Telegram ag olympiad-fathemategwyr methu gweithredu protocol diogel.
Fe wnaeth hyn oll fy ysgogi i ysgrifennu'r erthygl hon i ddangos nad yw gweithredu protocolau cryptograffig a diogel IM yn dasg mor anodd. Fodd bynnag, nid yw'n werth dyfeisio eich protocolau dilysu a chytundeb allweddol eich hun.
Bydd yr erthygl yn ysgrifennu cyfoedion-i-cyfoedion, ffrind-i-ffrind, pen-i-ben wedi'i amgryptio negesydd gwib gyda SIGMA-I protocol dilysu a chytundeb allweddol (y mae'n cael ei weithredu ar y sail honno IKE IPsec), gan ddefnyddio algorithmau cryptograffig GOST yn unig llyfrgell PyGOST a llyfrgell amgodio neges ASN.1 PyDERASN (am yr hwn yr wyf eisoes ysgrifennodd o'r blaen). Rhagofyniad: rhaid iddo fod mor syml fel y gellir ei ysgrifennu o'r dechrau mewn un noson (neu ddiwrnod gwaith), neu fel arall nid yw'n rhaglen syml mwyach. Mae'n debyg bod ganddo wallau, cymhlethdodau diangen, diffygion, a dyma fy rhaglen gyntaf gan ddefnyddio'r llyfrgell asyncio.
dylunio IM
Yn gyntaf, mae angen inni ddeall sut olwg fydd ar ein IM. Er mwyn symlrwydd, gadewch iddo fod yn rhwydwaith cyfoedion i gyfoedion, heb unrhyw gyfranogwyr wedi'u darganfod. Byddwn yn nodi'n bersonol pa gyfeiriad: porthladd i gysylltu ag ef i gyfathrebu Γ’'r cydgysylltydd.
Deallaf, ar hyn o bryd, fod y dybiaeth bod cyfathrebu uniongyrchol ar gael rhwng dau gyfrifiadur mympwyol yn gyfyngiad sylweddol ar gymhwysedd IM yn ymarferol. Ond po fwyaf y bydd datblygwyr yn gweithredu pob math o faglau croesi NAT, yr hiraf y byddwn yn aros ar y Rhyngrwyd IPv4, gyda thebygolrwydd digalon o gyfathrebu rhwng cyfrifiaduron mympwyol. Pa mor hir allwch chi oddef diffyg IPv6 gartref ac yn y gwaith?
Bydd gennym rwydwaith ffrind-i-ffrind: mae'n rhaid bod yn hysbys o flaen llaw pob cydweithiwr posibl. Yn gyntaf, mae hyn yn symleiddio popeth yn fawr: fe wnaethom gyflwyno ein hunain, dod o hyd i'r enw/allwedd neu fethu Γ’ dod o hyd iddo, wedi'i ddatgysylltu neu barhau i weithio, gan adnabod y cydgysylltydd. Yn ail, yn gyffredinol, mae'n ddiogel ac yn dileu llawer o ymosodiadau.
Bydd y rhyngwyneb IM yn agos at atebion clasurol prosiectau di-sugno, yr wyf yn ei hoffi'n fawr am eu minimaliaeth a'u hathroniaeth Unix-way. Mae'r rhaglen IM yn creu cyfeiriadur gyda thair soced parth Unix ar gyfer pob interlocutor:
i mewn - mae negeseuon a anfonir at y cydgysylltydd yn cael eu cofnodi ynddo;
out - darllenir negeseuon a dderbynnir oddi wrth yr interlocutor ohoni;
datgan - trwy ddarllen ohono, rydym yn darganfod a yw'r cydgysylltydd wedi'i gysylltu ar hyn o bryd, y cyfeiriad cysylltiad/porthladd.
Yn ogystal, mae soced conn yn cael ei greu, trwy ysgrifennu'r porthladd gwesteiwr yr ydym yn cychwyn cysylltiad Γ’'r interlocutor o bell iddo.
|-- alice
| |-- in
| |-- out
| `-- state
|-- bob
| |-- in
| |-- out
| `-- state
`- conn
Mae'r dull hwn yn caniatΓ‘u ichi wneud gweithrediadau annibynnol o gludiant IM a rhyngwyneb defnyddiwr, oherwydd nad oes ffrind, ni allwch blesio pawb. Defnyddio tmux a / neu amlgynffon, gallwch gael rhyngwyneb aml-ffenestr gydag amlygu cystrawen. A chyda chymorth rlwrap gallwch gael llinell fewnbynnu neges sy'n gydnaws Γ’ GNU Readline.
Mewn gwirionedd, mae prosiectau di-sugno'n defnyddio ffeiliau FIFO. Yn bersonol, ni allwn ddeall sut i weithio gyda ffeiliau yn gystadleuol mewn asyncio heb gefndir wedi'i ysgrifennu Γ’ llaw o edafedd pwrpasol (Rwyf wedi bod yn defnyddio'r iaith ar gyfer pethau o'r fath ers amser maith Go). Felly, penderfynais wneud y tro gyda socedi parth Unix. Yn anffodus, mae hyn yn ei gwneud hi'n amhosib gwneud adlais 2001:470:dead::babe 6666 > conn. Rwy'n datrys y broblem hon gan ddefnyddio socat: adlais 2001:470:marw::babe 6666 | socat - UNIX-CONNECT:conn, socat DARLLENWCH UNIX-CONNECT:alice/in.
Y protocol ansicr gwreiddiol
Defnyddir TCP fel cludiant: mae'n gwarantu danfoniad a'i archeb. Nid yw CDU yn gwarantu'r naill na'r llall (a fyddai'n ddefnyddiol pan ddefnyddir cryptograffeg), ond cefnogaeth SCTP Nid yw Python yn dod allan o'r bocs.
Yn anffodus, yn TCP nid oes cysyniad o neges, dim ond ffrwd o beit. Felly, mae angen llunio fformat ar gyfer negeseuon fel y gellir eu rhannu ymhlith ei gilydd yn yr edefyn hwn. Gallwn gytuno i ddefnyddio'r cymeriad porthiant llinell. Mae'n iawn i ddechreuwyr, ond ar Γ΄l i ni ddechrau amgryptio ein negeseuon, gall y nod hwn ymddangos yn unrhyw le yn y testun cipher. Mewn rhwydweithiau, felly, mae protocolau sy'n anfon hyd y neges yn gyntaf mewn beit yn boblogaidd. Er enghraifft, allan o'r bocs mae gan Python xdrlib, sy'n eich galluogi i weithio gyda fformat tebyg XDR.
Ni fyddwn yn gweithio'n gywir ac yn effeithlon gyda darllen TCP - byddwn yn symleiddio'r cod. Rydyn ni'n darllen data o'r soced mewn dolen ddiddiwedd nes i ni ddadgodio'r neges gyflawn. Gellir defnyddio JSON gyda XML hefyd fel fformat ar gyfer y dull hwn. Ond pan fydd cryptograffeg yn cael ei ychwanegu, bydd yn rhaid i'r data gael ei lofnodi a'i ddilysu - a bydd hyn yn gofyn am gynrychioliad beit-am-beit union yr un fath o wrthrychau, nad yw JSON/XML yn ei ddarparu (gall canlyniadau dympiau amrywio).
Mae XDR yn addas ar gyfer y dasg hon, fodd bynnag rwy'n dewis ASN.1 gydag amgodio DER a PyDERASN llyfrgell, gan y bydd gennym wrthrychau lefel uchel wrth law y bydd yn aml yn fwy dymunol a chyfleus i weithio gyda nhw. Yn wahanol i schemaless bengod, Pecyn Neges neu CBOR, Bydd ASN.1 yn gwirio'r data yn awtomatig yn erbyn sgema Γ’ chod caled.
Y neges a dderbynnir fydd Msg: naill ai neges destun MsgText (gydag un maes testun am y tro) neu neges ysgwyd llaw MsgHandshake (sy'n cynnwys enw'r cydgysylltydd). Nawr mae'n edrych yn or-gymhleth, ond dyma sylfaen ar gyfer y dyfodol.
Fel y dywedais eisoes, bydd y llyfrgell asyncio yn cael ei ddefnyddio ar gyfer pob gweithrediad soced. Gadewch i ni gyhoeddi'r hyn yr ydym yn ei ddisgwyl yn y lansiad:
Gosodwch eich enw eich hun (--ein-enw alice). Rhestrir yr holl gydryngwyr disgwyliedig wedi'u gwahanu gan atalnodau (βeu henwau bob, noson). Ar gyfer pob un o'r interlocutors, crΓ«ir cyfeiriadur gyda socedi Unix, yn ogystal Γ’ coroutine ar gyfer pob un i mewn, allan, datgan:
Wrth ddarllen o soced cyflwr, mae'r rhaglen yn edrych am gyfeiriad y cydgysylltydd yn y geiriadur PEER_ALIVE. Os nad oes cysylltiad Γ’'r cydgysylltydd eto, yna ysgrifennir llinell wag.
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()
Wrth ysgrifennu cyfeiriad i soced conn, mae'r swyddogaeth βcychwynnwrβ cysylltu yn cael ei lansio:
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))
Gadewch i ni ystyried y cychwynnwr. Yn gyntaf mae'n amlwg yn agor cysylltiad Γ’'r gwesteiwr / porthladd penodedig ac yn anfon neges ysgwyd llaw gyda'i enw:
Yna, mae'n aros am ymateb gan y parti anghysbell. Yn ceisio dadgodio'r ymateb sy'n dod i mewn gan ddefnyddio cynllun Msg ASN.1. Tybiwn y bydd y neges gyfan yn cael ei hanfon mewn un segment TCP a byddwn yn ei derbyn yn atomig wrth ffonio .read(). Rydym yn gwirio ein bod wedi derbyn y neges ysgwyd llaw.
Rydym yn gwirio ein bod yn gwybod enw'r cydgysylltydd a dderbyniwyd. Os na, yna rydym yn torri'r cysylltiad. Rydym yn gwirio a ydym eisoes wedi sefydlu cysylltiad ag ef (rhoddodd yr interlocutor y gorchymyn i gysylltu Γ’ ni eto) a'i gau. Mae'r ciw IN_QUEUES yn dal llinynnau Python gyda thestun y neges, ond mae ganddo werth arbennig o Dim sy'n arwydd i'r coroutine msg_sender roi'r gorau i weithio fel ei fod yn anghofio am ei ysgrifennwr sy'n gysylltiedig Γ’'r cysylltiad TCP etifeddiaeth.
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 # }}}
Mae msg_sender yn derbyn negeseuon sy'n mynd allan (wedi'u ciwio o soced mewn), yn eu cyfresoli i neges MsgText ac yn eu hanfon dros gysylltiad TCP. Gall dorri ar unrhyw adeg - rydym yn amlwg yn rhyng-gipio hyn.
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))
Ar y diwedd, mae'r cychwynnwr yn mynd i mewn i ddolen ddiddiwedd o negeseuon darllen o'r soced. Yn gwirio a yw'r negeseuon hyn yn negeseuon testun ac yn eu gosod yn y ciw OUT_QUEUES, y byddant yn cael eu hanfon ohono i soced allan y cydgysylltydd cyfatebol. Pam na allwch chi wneud .read() a dadgodio'r neges? Oherwydd ei bod yn bosibl y bydd nifer o negeseuon gan y defnyddiwr yn cael eu hagregu yn byffer y system weithredu a'u hanfon mewn un segment TCP. Gallwn ddadgodio'r un cyntaf, ac yna gall rhan o'r un dilynol aros yn y byffer. Yn achos unrhyw sefyllfa annormal, rydym yn cau'r cysylltiad TCP ac yn atal y coroutine msg_sender (trwy anfon Dim i'r ciw 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)
Gadewch i ni ddychwelyd at y prif god. Ar Γ΄l creu'r holl goroutines pan fydd y rhaglen yn dechrau, rydyn ni'n cychwyn y gweinydd TCP. Ar gyfer pob cysylltiad sefydledig, mae'n creu coroutine ymatebwr.
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()
mae'r ymatebwr yn debyg i'r cychwynnwr ac yn adlewyrchu'r un gweithredoedd, ond mae'r ddolen ddiddiwedd o negeseuon darllen yn cychwyn ar unwaith, er mwyn symlrwydd. Ar hyn o bryd, mae'r protocol ysgwyd llaw yn anfon un neges o bob ochr, ond yn y dyfodol bydd dwy gan y cychwynnwr cysylltiad, ac ar Γ΄l hynny gellir anfon negeseuon testun ar unwaith.
Mae'n bryd sicrhau ein cyfathrebiadau. Beth rydym yn ei olygu wrth ddiogelwch a beth rydym ei eisiau:
cyfrinachedd negeseuon a drosglwyddir;
dilysrwydd a chywirdeb negeseuon a drosglwyddir - rhaid canfod eu newidiadau;
amddiffyniad rhag ymosodiadau ailchwarae - rhaid canfod y ffaith bod negeseuon ar goll neu'n cael eu hailadrodd (a byddwn yn penderfynu terfynu'r cysylltiad);
adnabod a dilysu interlocutors gan ddefnyddio allweddi cyhoeddus a gofnodwyd ymlaen llaw - rydym eisoes wedi penderfynu yn gynharach ein bod yn gwneud rhwydwaith ffrind-i-ffrind. Dim ond ar Γ΄l dilysu y byddwn yn deall gyda phwy yr ydym yn cyfathrebu;
argaeledd cyfrinachedd perffaith ymlaen eiddo (PFS) - ni ddylai peryglu ein hallwedd arwyddo hirhoedlog arwain at y gallu i ddarllen pob gohebiaeth flaenorol. Mae cofnodi traffig rhyng-gipio yn mynd yn ddiwerth;
dilysrwydd/dilysrwydd negeseuon (cludiant ac ysgwyd llaw) o fewn un sesiwn TCP yn unig. Ni ddylai fod yn bosibl mewnosod negeseuon wedi'u harwyddo/dilysu'n gywir o sesiwn arall (hyd yn oed gyda'r un cydgysylltydd);
ni ddylai sylwedydd goddefol weld naill ai dynodwyr defnyddiwr, allweddi cyhoeddus hirhoedlog a drosglwyddir, na hashes oddi wrthynt. Rhywfaint o anhysbysrwydd gan arsylwr goddefol.
Yn syndod, mae bron pawb eisiau cael yr isafswm hwn mewn unrhyw brotocol ysgwyd llaw, ac ychydig iawn o'r uchod sy'n cael ei fodloni yn y pen draw ar gyfer protocolau βcynnyrch cartrefβ. Nawr ni fyddwn yn dyfeisio dim byd newydd. Byddwn yn bendant yn argymell defnyddio Fframwaith sΕ΅n ar gyfer adeiladu protocolau, ond gadewch i ni ddewis rhywbeth symlach.
Y ddau brotocol mwyaf poblogaidd yw:
TLS - protocol cymhleth iawn gyda hanes hir o chwilod, tagfeydd, gwendidau, meddwl gwael, cymhlethdod a diffygion (fodd bynnag, nid oes a wnelo hyn fawr ddim Γ’ TLS 1.3). Ond nid ydym yn ei ystyried oherwydd ei fod yn or-gymhleth.
IPsec Ρ IKE β nid oes ganddynt broblemau cryptograffig difrifol, er nad ydynt yn syml ychwaith. Os darllenwch am IKEv1 ac IKEv2, yna eu ffynhonnell yw STS, protocolau ISO/IEC IS 9798-3 a SIGMA (SIGn-and-MAc) - digon syml i'w gweithredu mewn un noson.
Beth sy'n dda am SIGMA, fel y cyswllt diweddaraf yn natblygiad protocolau STS/ISO? Mae'n cwrdd Γ’'n holl ofynion (gan gynnwys βcuddioβ dynodwyr interlocutor) ac nid oes ganddo unrhyw broblemau cryptograffig hysbys. Mae'n finimalaidd - bydd tynnu o leiaf un elfen o'r neges protocol yn arwain at ei ansicrwydd.
Gadewch i ni fynd o'r protocol cartref symlaf i SIGMA. Y gweithrediad mwyaf sylfaenol y mae gennym ddiddordeb ynddo yw cytundeb allweddol: Swyddogaeth sy'n allbynnu'r ddau gyfranogwr yr un gwerth, y gellir ei ddefnyddio fel allwedd cymesur. Heb fynd i fanylion: mae pob un o'r partΓ―on yn cynhyrchu pΓ’r allwedd byrhoedlog (a ddefnyddir o fewn un sesiwn yn unig) (allweddi cyhoeddus a phreifat), cyfnewid allweddi cyhoeddus, galw'r swyddogaeth cytundeb, y maent yn trosglwyddo eu hallwedd breifat a'r cyhoedd i'r mewnbwn. allwedd y interlocutor.
Gall unrhyw un neidio yn y canol a disodli allweddi cyhoeddus gyda'u rhai eu hunain - nid oes unrhyw ddilysiad o interlocutors yn y protocol hwn. Gadewch i ni ychwanegu llofnod gydag allweddi hirhoedlog.
Ni fydd llofnod o'r fath yn gweithio, gan nad yw'n gysylltiedig Γ’ sesiwn benodol. Mae negeseuon o'r fath hefyd yn βaddasβ ar gyfer sesiynau gyda chyfranogwyr eraill. Rhaid i'r cyd-destun cyfan danysgrifio. Mae hyn yn ein gorfodi i ychwanegu neges arall gan A.
Yn ogystal, mae'n hanfodol ychwanegu eich dynodwr eich hun o dan y llofnod, oherwydd fel arall gallwn ddisodli IdXXX ac ail-lofnodi'r neges gydag allwedd cydgysylltydd hysbys arall. Er mwyn atal ymosodiadau myfyrio, mae'n angenrheidiol bod yr elfennau o dan y llofnod mewn mannau wedi'u diffinio'n glir yn Γ΄l eu hystyr: os yw A yn arwyddo (PubA, PubB), yna mae'n rhaid i B lofnodi (PubB, PubA). Mae hyn hefyd yn sΓ΄n am bwysigrwydd dewis strwythur a fformat data cyfresol. Er enghraifft, mae setiau mewn amgodio ASN.1 DER yn cael eu didoli: bydd SET OF(PubA, PubB) yn union yr un fath Γ’ SET OF(PubB, PubA).
Fodd bynnag, nid ydym wedi βprofiβ o hyd ein bod wedi cynhyrchu'r un allwedd a rennir ar gyfer y sesiwn hon. Mewn egwyddor, gallwn wneud heb y cam hwn - bydd y cysylltiad trafnidiaeth cyntaf un yn annilys, ond rydym am, pan fydd yr ysgwyd llaw wedi'i gwblhau, y byddem yn sicr y cytunir ar bopeth mewn gwirionedd. Ar hyn o bryd mae gennym y protocol ISO/IEC IS 9798-3 wrth law.
Gallem lofnodi'r allwedd a gynhyrchir ei hun. Mae hyn yn beryglus, gan ei bod yn bosibl y bydd yr algorithm llofnod a ddefnyddir yn gollwng (er ei fod yn ddarnau fesul llofnod, ond yn dal i ollwng). Mae'n bosibl arwyddo hash o'r allwedd tarddiad, ond gall gollwng hyd yn oed stwnsh yr allwedd ddeilliadol fod yn werthfawr mewn ymosodiad 'n Ysgrublaidd ar y swyddogaeth tarddiad. Mae SIGMA yn defnyddio swyddogaeth MAC sy'n dilysu ID yr anfonwr.
Fel optimeiddio, efallai y bydd rhai am ailddefnyddio eu bysellau byrhoedlog (sydd, wrth gwrs, yn anffodus i PFS). Er enghraifft, fe wnaethom gynhyrchu pΓ’r allweddol, ceisio cysylltu, ond nid oedd TCP ar gael neu amharwyd arno rhywle yng nghanol y protocol. Mae'n drueni gwastraffu adnoddau entropi a phrosesydd ar bΓ’r newydd. Felly, byddwn yn cyflwyno'r cwci fel y'i gelwir - gwerth ffug-hap a fydd yn amddiffyn rhag ymosodiadau ailchwarae ar hap posibl wrth ailddefnyddio allweddi cyhoeddus byrhoedlog. Oherwydd y rhwymiad rhwng y cwci a'r allwedd gyhoeddus dros dro, gellir tynnu allwedd gyhoeddus y parti arall o'r llofnod fel rhywbeth diangen.
Yn olaf, rydym am gael preifatrwydd ein partneriaid sgwrsio gan arsylwr goddefol. I wneud hyn, mae SIGMA yn cynnig cyfnewid allweddi byrhoedlog yn gyntaf a datblygu allwedd gyffredin i amgryptio dilysu ac adnabod negeseuon arni. Mae SIGMA yn disgrifio dau opsiwn:
SIGMA-I - yn amddiffyn y cychwynnwr rhag ymosodiadau gweithredol, yr ymatebydd rhag rhai goddefol: mae'r cychwynnwr yn dilysu'r ymatebydd ac os nad yw rhywbeth yn cyfateb, yna nid yw'n rhoi ei hunaniaeth. Mae'r diffynnydd yn rhoi ei brawf adnabod os cychwynnir protocol gweithredol gydag ef. Nid yw'r sylwedydd goddefol yn dysgu dim;
SIGMA-R - yn amddiffyn yr ymatebydd rhag ymosodiadau gweithredol, y cychwynnwr rhag rhai goddefol. Mae popeth yn union i'r gwrthwyneb, ond yn y protocol hwn mae pedair neges ysgwyd llaw eisoes yn cael eu trosglwyddo.
Rydyn ni'n dewis SIGMA-I gan ei fod yn debycach i'r hyn rydyn ni'n ei ddisgwyl gan bethau cyfarwydd cleient-gweinydd: dim ond y gweinydd dilys sy'n cydnabod y cleient, ac mae pawb eisoes yn adnabod y gweinydd. Hefyd mae'n haws ei weithredu oherwydd llai o negeseuon ysgwyd llaw. Y cyfan rydyn ni'n ei ychwanegu at y protocol yw amgryptio rhan o'r neges a throsglwyddo'r dynodwr A i'r rhan o'r neges olaf sydd wedi'i hamgryptio:
Defnyddir GOST R ar gyfer llofnod 34.10-2012 algorithm gydag allweddi 256-did.
I gynhyrchu'r allwedd gyhoeddus, defnyddir 34.10/2012/XNUMX VKO.
Defnyddir CMAC fel y MAC. Yn dechnegol, mae hwn yn ddull gweithredu arbennig o seiffr bloc, a ddisgrifir yn GOST R 34.13-2015. Fel swyddogaeth amgryptio ar gyfer y modd hwn - Troellwr gwair (34.12-2015).
Mae stwnsh ei allwedd gyhoeddus yn cael ei ddefnyddio fel dynodwr yr interlocutor. Wedi'i ddefnyddio fel hash Stribog- 256 (34.11/2012/256 XNUMX did).
Ar Γ΄l yr ysgwyd llaw, byddwn yn cytuno ar allwedd a rennir. Gallwn ei ddefnyddio ar gyfer amgryptio dilys o negeseuon trafnidiaeth. Mae'r rhan hon yn syml iawn ac yn anodd gwneud camgymeriad: rydym yn cynyddu'r cownter neges, yn amgryptio'r neges, yn dilysu (MAC) y cownter a ciphertext, yn anfon. Wrth dderbyn neges, rydyn ni'n gwirio bod gan y cownter y gwerth disgwyliedig, yn dilysu'r testun cipher gyda'r cownter, ac yn ei ddadgryptio. Pa allwedd ddylwn i ei defnyddio i amgryptio negeseuon ysgwyd llaw, cludo negeseuon, a sut i'w dilysu? Mae defnyddio un allwedd ar gyfer yr holl dasgau hyn yn beryglus ac yn annoeth. Mae angen cynhyrchu allweddi gan ddefnyddio swyddogaethau arbenigol KDF (swyddogaeth tarddiad allweddol). Unwaith eto, gadewch i ni beidio Γ’ hollti blew a dyfeisio rhywbeth: HKDF wedi bod yn hysbys ers tro, wedi'i ymchwilio'n dda ac nid oes ganddo unrhyw broblemau hysbys. Yn anffodus, nid oes gan y llyfrgell Python brodorol y swyddogaeth hon, felly rydym yn defnyddio hkdf bag plastig. Mae HKDF yn defnyddio'n fewnol GLlEM, sydd yn ei dro yn defnyddio swyddogaeth hash. Mae gweithredu enghreifftiol yn Python ar y dudalen Wicipedia yn cymryd dim ond ychydig linellau o god. Fel yn achos 34.10/2012/256, byddwn yn defnyddio Stribog-XNUMX fel y swyddogaeth hash. Gelwir allbwn ein swyddogaeth cytundeb allweddol yn allwedd sesiwn, a bydd y rhai cymesur coll yn cael eu cynhyrchu ohono:
HandshakeTBS yw'r hyn a gaiff ei lofnodi. HandshakeTBE - beth fydd yn cael ei amgryptio. Tynnaf eich sylw at y maes ukm yn MsgHandshake1. 34.10 Mae VKO, ar gyfer hapnodi hyd yn oed yn fwy o'r allweddi a gynhyrchir, yn cynnwys y paramedr UKM (deunydd bysellu defnyddiwr) - dim ond entropi ychwanegol.
Ychwanegu Cryptograffeg at y Cod
Gadewch i ni ystyried y newidiadau a wnaed i'r cod gwreiddiol yn unig, gan fod y fframwaith wedi aros yr un fath (mewn gwirionedd, ysgrifennwyd y gweithrediad terfynol yn gyntaf, ac yna torrwyd yr holl cryptograffeg allan ohono).
Gan y bydd dilysu ac adnabod interlocutors yn cael ei wneud gan ddefnyddio allweddi cyhoeddus, bellach mae angen eu storio yn rhywle am amser hir. Er mwyn symlrwydd, rydym yn defnyddio JSON fel hyn:
ein - ein pΓ’r allweddol, allweddi preifat a chyhoeddus hecsadegol. eu β enwau interlocutors a'u allweddi cyhoeddus. Gadewch i ni newid y dadleuon llinell orchymyn ac ychwanegu Γ΄l-brosesu data 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),
}
# }}}
Allwedd breifat yr algorithm 34.10 yw rhif ar hap. Maint 256-did ar gyfer cromliniau eliptig 256-did. Nid yw PyGOST yn gweithio gyda set o beit, ond gyda niferoedd mawr, felly mae angen trosi ein allwedd breifat (urandom(32)) i rif gan ddefnyddio gost3410.prv_unmarshal(). Mae'r allwedd gyhoeddus yn cael ei phennu'n benderfynol o'r allwedd breifat gan ddefnyddio gost3410.public_key(). Mae allwedd gyhoeddus 34.10 yn ddau rif mawr y mae angen eu trosi hefyd yn ddilyniant beit er mwyn ei storio a'i drosglwyddo'n rhwydd gan ddefnyddio gost3410.pub_marshal().
Ar Γ΄l darllen y ffeil JSON, mae angen trosi'r allweddi cyhoeddus yn Γ΄l gan ddefnyddio gost3410.pub_unmarshal(). Gan y byddwn yn derbyn dynodwyr y interlocutors ar ffurf hash o'r allwedd gyhoeddus, gellir eu cyfrifo ar unwaith ymlaen llaw a'u gosod mewn geiriadur ar gyfer chwiliad cyflym. Mae hash Stribog-256 yn gost34112012256.GOST34112012256(), sy'n bodloni'n llawn y rhyngwyneb hashlib o swyddogaethau hash.
Sut mae coroutine y cychwynnwr wedi newid? Mae popeth yn unol Γ’'r cynllun ysgwyd llaw: rydym yn cynhyrchu cwci (mae 128-bit yn ddigon), pΓ’r allwedd byrhoedlog 34.10, a fydd yn cael ei ddefnyddio ar gyfer swyddogaeth cytundeb allwedd VKO.
Mae UKM yn rhif 64-did (wrandom(8)), sydd hefyd angen ei ddad-gyfeirio o'i gynrychiolaeth beit gan ddefnyddio gost3410_vko.ukm_unmarshal(). Swyddogaeth VKO ar gyfer 34.10/2012/256 3410-bit yw gost34102012256_vko.kek_XNUMX() (KEK - allwedd amgryptio).
Mae'r allwedd sesiwn a gynhyrchir eisoes yn ddilyniant beit ffug-hap 256-did. Felly, gellir ei ddefnyddio ar unwaith mewn swyddogaethau HKDF. Gan fod GOST34112012256 yn bodloni'r rhyngwyneb hashlib, gellir ei ddefnyddio ar unwaith yn y dosbarth Hkdf. Nid ydym yn nodi'r halen (dadl gyntaf Hkdf), gan y bydd yr allwedd a gynhyrchir, oherwydd byrhoedledd y parau allweddol sy'n cymryd rhan, yn wahanol ar gyfer pob sesiwn ac mae eisoes yn cynnwys digon o entropi. Mae kdf.expand() yn ddiofyn eisoes yn cynhyrchu'r bysellau 256-bit sydd eu hangen ar gyfer Grasshopper yn nes ymlaen.
Nesaf, mae rhannau TBE a TBS y neges sy'n dod i mewn yn cael eu gwirio:
mae'r MAC dros y testun cipher sy'n dod i mewn yn cael ei gyfrifo a'i wirio;
mae'r testun cipher wedi'i ddadgryptio;
Mae strwythur TBE wedi'i ddadgodio;
cymerir dynodydd yr ymddyddan oddiyno a gwirir a ydyw yn hysbys i ni o gwbl ;
Mae MAC dros y dynodwr hwn yn cael ei gyfrifo a'i wirio;
mae'r llofnod dros y strwythur TBS yn cael ei wirio, sy'n cynnwys cwci'r ddau barti ac allwedd gyhoeddus byrhoedlog y parti arall. Mae'r llofnod yn cael ei wirio gan allwedd llofnod hirhoedlog y cydgysylltydd.
Fel yr ysgrifennais uchod, mae 34.13/2015/XNUMX yn disgrifio amrywiol dulliau gweithredu seiffr bloc o 34.12/2015/3413. Yn eu plith mae modd cynhyrchu mewnosodiadau dynwared a chyfrifiadau MAC. Yn PyGOST mae hyn yn gost34.12.mac(). Mae'r modd hwn yn gofyn am basio'r swyddogaeth amgryptio (derbyn a dychwelyd un bloc o ddata), maint y bloc amgryptio ac, mewn gwirionedd, y data ei hun. Pam na allwch chi god caled maint y bloc amgryptio? Mae 2015/128/64 yn disgrifio nid yn unig y seiffr ceiliogod rhedyn XNUMX-did, ond hefyd y seiffr XNUMX-did Magma - GOST 28147-89 wedi'i addasu ychydig, a grΓ«wyd yn Γ΄l yn y KGB ac mae ganddo un o'r trothwyon diogelwch uchaf o hyd.
Mae Kuznechik yn cael ei gychwyn trwy ffonio gost.3412.GOST3412Kuznechik(allwedd) ac yn dychwelyd gwrthrych gyda dulliau .encrypt()/.decrypt() sy'n addas ar gyfer trosglwyddo i swyddogaethau 34.13. Cyfrifir MAC fel a ganlyn: gost3413.mac(GOST3412Kuznechik(key).encrypt, KUZNECHIK_BLOCKSIZE, ciphertext). I gymharu'r MAC a gyfrifwyd ac a dderbyniwyd, ni allwch ddefnyddio'r gymhariaeth arferol (==) o linynnau beit, gan fod y llawdriniaeth hon yn gollwng amser cymharu, a all, yn yr achos cyffredinol, arwain at wendidau angheuol fel BEAST ymosodiadau ar TLS. Mae gan Python swyddogaeth arbennig, hmac.compare_digest, ar gyfer hyn.
Dim ond un bloc o ddata y gall swyddogaeth seiffr bloc ei amgryptio. Ar gyfer nifer fwy, a hyd yn oed nid lluosrif o'r hyd, mae angen defnyddio'r modd amgryptio. Mae 34.13-2015 yn disgrifio'r canlynol: ECB, CTR, OFB, CBS, CFB. Mae gan bob un ei feysydd cymhwyso a nodweddion derbyniol ei hun. Yn anffodus, nid ydym wedi safoni o hyd dulliau amgryptio dilys (fel CCM, OCB, GCM ac yn y blaen) - rydym yn cael ein gorfodi i o leiaf ychwanegu MAC ein hunain. Rwy'n dewis modd cownter (CTR): nid oes angen padin i faint y bloc, gellir ei gyfochrog, mae'n defnyddio'r swyddogaeth amgryptio yn unig, gellir ei ddefnyddio'n ddiogel i amgryptio nifer fawr o negeseuon (yn wahanol i CBS, sydd Γ’ gwrthdrawiadau yn gymharol gyflym).
Fel .mac(), .ctr() yn cymryd mewnbwn tebyg: ciphertext = gost3413.ctr(GOST3412Kuznechik(key).encrypt, KUZNECHIK_BLOCKSIZE, plaintext, iv). Mae'n ofynnol nodi fector cychwyn sydd union hanner hyd y bloc amgryptio. Os mai dim ond i amgryptio un neges y defnyddir ein bysell amgryptio (er o sawl bloc), yna mae'n ddiogel gosod fector cychwyniad sero. I amgryptio negeseuon ysgwyd llaw, rydym yn defnyddio allwedd ar wahΓ’n bob tro.
Mae dilysu'r llofnod gost3410.verify() yn ddibwys: rydyn ni'n pasio'r gromlin eliptig rydyn ni'n gweithio o'i mewn (rydym yn ei gofnodi yn ein protocol GOSTIM), allwedd gyhoeddus yr arwyddwr (peidiwch ag anghofio y dylai hwn fod yn dwple o ddau niferoedd mawr, ac nid llinyn beit), 34.11/2012/XNUMX hash a'r llofnod ei hun.
Nesaf, yn y cychwynnwr rydym yn paratoi ac yn anfon neges ysgwyd llaw i ysgwyd llaw2, gan berfformio'r un gweithredoedd ag y gwnaethom yn ystod y dilysu, dim ond yn gymesur: llofnodi ein bysellau yn lle gwirio, ac ati ...
Pan fydd y sesiwn wedi'i sefydlu, cynhyrchir allweddi trafnidiaeth (allwedd ar wahΓ’n ar gyfer amgryptio, ar gyfer dilysu, ar gyfer pob un o'r partΓ―on), ac mae'r Grasshopper yn cael ei gychwyn i ddadgryptio a gwirio'r MAC:
Mae coroutine msg_sender bellach yn amgryptio negeseuon cyn eu hanfon ar gysylltiad TCP. Mae gan bob neges nonce sy'n cynyddu'n undonog, sef y fector ymgychwyn hefyd pan gaiff ei hamgryptio yn y modd cownter. Mae pob neges a bloc neges yn sicr o gael gwrthwerth gwahanol.
Bwriedir i GOSTIM gael ei ddefnyddio at ddibenion addysgol yn unig (gan nad yw'n cael ei gwmpasu gan brofion, o leiaf)! Gellir lawrlwytho cod ffynhonnell y rhaglen yma (Π‘ΡΡΠΈΠ±ΠΎΠ³-256 Ρ ΡΡ: 995bbd368c04e50a481d138c5fa2e43ec7c89bc77743ba8dbabee1fde45de120). ΠΠ°ΠΊ ΠΈ Π²ΡΠ΅ ΠΌΠΎΠΈ ΠΏΡΠΎΠ΅ΠΊΡΡ, ΡΠΈΠΏΠ° GoGOST, PyDERASN, NCCP, GoVPN, GOSTIM yn gwbl meddalwedd am ddimdosbarthu dan y telerau GPLv3 +.