GOSTIM: P2P F2F E2EE IM n'otu mgbede nwere GOST cryptography

Ịbụ onye nrụpụta PyGOST ọba akwụkwọ (GOST cryptographic primitives in pure Python), ana m anabatakarị ajụjụ gbasara otu esi etinye ozi nchekwa dị mfe na ikpere. Ọtụtụ ndị mmadụ na-ele cryptography etinyere anya dị ka ihe dị mfe, na ịkpọ .encrypt() na cipher ngọngọ ga-ezuru izipu ya na nzuzo na ọwa nkwukọrịta. Ndị ọzọ kwenyere na cryptography etinyere bụ akara aka nke mmadụ ole na ole, ọ dịkwa mma na ụlọ ọrụ bara ọgaranya dị ka Telegram nwere ndị ọkachamara olympiad-mathematicians. enweghị ike ime echekwabara protocol.

Ihe ndị a niile kpaliri m ide edemede a iji gosi na imejuputa usoro iwu cryptographic na nchekwa IM abụghị ọrụ siri ike. Agbanyeghị, ọ baghị uru ịmepụta njirimara gị na usoro nkwekọrịta isi.

GOSTIM: P2P F2F E2EE IM n'otu mgbede nwere GOST cryptography
Akụkọ ga-ede ọgbọ na ọgbọ, enyi-na-enyi, ezoro ezo na ngwụcha-na-ọgwụgwụ ngwa ngwa na SIGMA-I nkwenye na isi nkwekọrịta nkwekọrịta (na ndabere nke a na-emejuputa ya IPsec IKE), iji GOST cryptographic algọridim naanị PyGOST ọba akwụkwọ na ASN.1 ozi n'ime ọba akwụkwọ PYDERASN (nke m ugbua dere tupu). Ihe dị mkpa: ọ ga-adịrịrị mfe nke na enwere ike dee ya site na ọkọ n'otu mgbede (ma ọ bụ ụbọchị ọrụ), ma ọ bụghị ya abụghịzi mmemme dị mfe. O nwere ike inwe mmejọ, nsogbu ndị na-enweghị isi, adịghị ike, gbakwunyere na nke a bụ mmemme mbụ m na-eji ọbá akwụkwọ asyncio.

IM imewe

Nke mbụ, anyị kwesịrị ịghọta ihe IM anyị ga-adị ka. Maka mfe, ka ọ bụrụ netwọk ndị ọgbọ na ndị ọgbọ, na-enweghị nchọpụta ọ bụla nke ndị sonyere. Anyị ga-egosi n'onwe anyị adreesị nke: ọdụ ụgbọ mmiri iji jikọọ na onye na-emekọrịta ihe.

Aghọtara m na, n'oge a, echiche na nkwurịta okwu dị n'etiti kọmpụta abụọ na-ezighị ezi bụ njedebe dị ịrịba ama na ntinye nke IM na omume. Ma ka ndị mmepe na-emejuputa ụdị ọ bụla nke NAT-traversal crutches, ka anyị ga-anọ ogologo oge na Ịntanetị IPv4, na-enwe ike ịda mbà n'obi nke nkwurịta okwu n'etiti kọmputa na-ezighị ezi. Ogologo oge ole ka ị ga-anagide enweghị IPv6 n'ụlọ na n'ebe ọrụ?

Anyị ga-enwe netwọk enyi-na-enyi: niile kwere omume interlocutors ga-amata n'ọdịnihu. Nke mbụ, nke a na-eme ka ihe niile dị mfe: anyị webatara onwe anyị, chọta ma ọ bụ hụghị aha / igodo, kewapụrụ ma ọ bụ na-aga n'ihu na-arụ ọrụ, na-amata onye na-emekọrịta ihe. Nke abuo, n'ozuzu, ọ na-echekwa ma na-ewepụ ọtụtụ ọgụ.

The IM interface ga-adị nso kpochapụwo ngwọta suckless oru ngo, nke na-amasị m n'ezie maka minimalism ha na nkà ihe ọmụma Unix ụzọ. Mmemme IM na-emepụta ndekọ nwere oghere ngalaba Unix atọ maka onye ọ bụla na-emekọrịta ihe:

  • na-ozi ezigara onye interlocutor ka edere na ya;
  • apụ - a na-agụ site na ya ozi enwetara site na interlocutor;
  • steeti - site n'ịgụ site na ya, anyị na-achọpụta ma ọ na-ejikọta interlocutor ugbu a, adreesị njikọ / ọdụ ụgbọ mmiri.

Na mgbakwunye, a na-emepụta oghere conn, site na ịde ọdụ ụgbọ mmiri nke anyị na-amalite njikọ na interlocutor dịpụrụ adịpụ.

|-- alice
|   |-- in
|   |-- out
|   `-- state
|-- bob
|   |-- in
|   |-- out
|   `-- state
`- conn

Ụzọ a na-enye gị ohere ịme mmejuputa nnwere onwe nke njem IM na interface onye ọrụ, n'ihi na ọ dịghị enyi, ị nweghị ike ime onye ọ bụla ụtọ. Iji udochi na / ma ọ bụ ọtụtụ, ị nwere ike nweta interface multi-window na syntax pụta ìhè. Na site n'enyemaka Rlwrap ị nwere ike nweta ahịrị ntinye ozi dakọtara GNU Readline.

N'ezie, ọrụ na-enweghị atụ na-eji faịlụ FIFO. Onwe m, enweghị m ike ịghọta otu esi arụ ọrụ na faịlụ asọmpi na asyncio na-enweghị ndabere ejiri aka dee site na eriri raara onwe ya nye (Ana m eji asụsụ ahụ maka ihe ndị dị otú ahụ ogologo oge. Go). Ya mere, ekpebiri m ime ya na ngalaba ngalaba Unix. N'ụzọ dị mwute, nke a na-eme ka ọ ghara ikwe omume ime echo 2001:470: nwụrụ anwụ :: babe 6666 > conn. Eji m dozie nsogbu a socat: echo 2001:470: nwụrụ anwụ:: babe 6666 | socat - UNIX-njikọ: conn, socat READLINE UNIX-njikọ: alice/in.

Usoro izizi enweghị nchebe

A na-eji TCP dị ka ụgbọ njem: ọ na-ekwe nkwa nnyefe na iwu ya. UDP anaghị ekwe nkwa na ọ nweghị (nke ga-aba uru mgbe ejiri nzuzo mee ihe), mana nkwado SCTP Python anaghị apụta na igbe.

N'ụzọ dị mwute, na TCP enweghị echiche nke ozi, naanị iyi nke bytes. Ya mere, ọ dị mkpa ka e wepụta usoro maka ozi ka ha wee kekọrịtara onwe ha na eri a. Anyị nwere ike ikwenye iji agwa ndepụta ahịrị. Ọ dị mma maka mbido, mana ozugbo anyị malitere izochi ozi anyị, agwa a nwere ike ịpụta ebe ọ bụla na ederede ciphertext. N'ime netwọkụ, ya mere, usoro iwu ewu ewu bụ ndị na-ebu ụzọ zipu ogologo ozi na bytes. Dịka ọmụmaatụ, site na igbe Python nwere xdrlib, nke na-enye gị ohere iji usoro yiri ya rụọ ọrụ XDR.

Anyị agaghị arụ ọrụ nke ọma na nke ọma na ịgụ TCP - anyị ga-eme ka koodu ahụ dị mfe. Anyị na-agụ data sitere na oghere na akaghị agwụ agwụ ruo mgbe anyị depụtara ozi zuru oke. JSON nwere XML nwekwara ike iji dị ka usoro maka ụzọ a. Ma mgbe agbakwunyere cryptography, a ga-edebanye aha data ahụ ma gosipụta ya - nke a ga-achọkwa ihe nnọchianya nke byte-for-byte, nke JSON/XML adịghị enye (nsonaazụ mkpofu nwere ike ịdị iche).

XDR dabara maka ọrụ a, agbanyeghị, a na m ahọrọ ASN.1 na DER encoding na PYDERASN Ọbá akwụkwọ, ebe ọ bụ na anyị ga-enwe ihe dị elu n'aka nke ọ na-adịkarị ụtọ ma dị mma iji rụọ ọrụ. N'adịghị ka schemaless bencode, Mpempe ozi ma ọ bụ CBOR, ASN.1 ga-enyocha data na-akpaghị aka megide atụmatụ nwere koodu siri ike.

# Msg ::= CHOICE {
#       text      MsgText,
#       handshake [0] EXPLICIT MsgHandshake }
class Msg(Choice):
    schema = ((
        ("text", MsgText()),
        ("handshake", MsgHandshake(expl=tag_ctxc(0))),
    ))

# MsgText ::= SEQUENCE {
#       text UTF8String (SIZE(1..MaxTextLen))}
class MsgText(Sequence):
    schema = ((
        ("text", UTF8String(bounds=(1, MaxTextLen))),
    ))

# MsgHandshake ::= SEQUENCE {
#       peerName UTF8String (SIZE(1..256)) }
class MsgHandshake(Sequence):
    schema = ((
        ("peerName", UTF8String(bounds=(1, 256))),
    ))

Ozi enwetara ga-abụ Msg: ma ọ bụ ederede MsgText (nwere otu mpaghara ederede ugbu a) ma ọ bụ ozi aka aka MsgHandshake (nke nwere aha onye na-emekọrịta ihe). Ugbu a, ọ dị ka ihe mgbagwoju anya, ma nke a bụ ntọala maka ọdịnihu.

     ┌────┐ ┌──┐ ─┐ │ PeerA│ │ PeerB│ └──┬───└──┘└── (na m) │ ───── ─ ────────>││ │ │ MsgHandshake (IdB) │ │ <──── │ │ MsgText() │ │─── MsgText() │ │ │

IM na-enweghị cryptography

Dịka m kwurula, a ga-eji ọba akwụkwọ asyncio arụ ọrụ sọket niile. Ka anyị kwupụta ihe anyị tụrụ anya na mmalite:

parser = argparse.ArgumentParser(description="GOSTIM")
parser.add_argument(
    "--our-name",
    required=True,
    help="Our peer name",
)
parser.add_argument(
    "--their-names",
    required=True,
    help="Their peer names, comma-separated",
)
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()
OUR_NAME = UTF8String(args.our_name)
THEIR_NAMES = set(args.their_names.split(","))

Tọọ aha nke gị (--anyị-aha alice). Edepụtara ndị mmekọ niile a na-atụ anya ya site na rịkọm (-aha ha bob,eve). Maka onye ọ bụla n'ime ndị na-emekọrịta ihe, a na-emepụta ndekọ nwere sọket Unix, yana coroutine maka onye ọ bụla n'ime, pụta, kwuo:

for peer_name in THEIR_NAMES:
    makedirs(peer_name, mode=0o700, exist_ok=True)
    out_queue = asyncio.Queue()
    OUT_QUEUES[peer_name] = out_queue
    asyncio.ensure_future(asyncio.start_unix_server(
        partial(unixsock_out_processor, out_queue=out_queue),
        path.join(peer_name, "out"),
    ))
    in_queue = asyncio.Queue()
    IN_QUEUES[peer_name] = in_queue
    asyncio.ensure_future(asyncio.start_unix_server(
        partial(unixsock_in_processor, in_queue=in_queue),
        path.join(peer_name, "in"),
    ))
    asyncio.ensure_future(asyncio.start_unix_server(
        partial(unixsock_state_processor, peer_name=peer_name),
        path.join(peer_name, "state"),
    ))
asyncio.ensure_future(asyncio.start_unix_server(unixsock_conn_processor, "conn"))

A na-eziga ozi sitere n'aka onye ọrụ site na oghere na IN_QUEUES kwụ n'ahịrị:

async def unixsock_in_processor(reader, writer, in_queue: asyncio.Queue) -> None:
    while True:
        text = await reader.read(MaxTextLen)
        if text == b"":
            break
        await in_queue.put(text.decode("utf-8"))

A na-eziga ozi ndị na-emekọrịta ihe na ahịrị OUT_QUEUES, nke e si na ya dee data na oghere:

async def unixsock_out_processor(reader, writer, out_queue: asyncio.Queue) -> None:
    while True:
        text = await out_queue.get()
        writer.write(("[%s] %s" % (datetime.now(), text)).encode("utf-8"))
        await writer.drain()

Mgbe ị na-agụ site na oghere steeti, mmemme a na-achọ adreesị onye na-emekọrịta ihe na ọkọwa okwu PEER_ALIVE. Ọ bụrụ na enweghị njikọ na interlocutor ma, mgbe ahụ, a na-edeghị ahịrị efu.

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()

Mgbe ị na-ede adreesị na oghere conn, a na-amalite ọrụ "onye mbido" njikọ:

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))

Ka anyị tụlee onye mmalite. Nke mbụ, o doro anya na ọ ga-emepe njikọ na ndị ọbịa/ọdụ ụgbọ mmiri akọwapụtara wee ziga ozi mmetụ aka na aha ya:

 130 async def initiator(host, port):
 131     _id = repr((host, port))
 132     logging.info("%s: dialing", _id)
 133     reader, writer = await asyncio.open_connection(host, port)
 134     # Handshake message {{{
 135     writer.write(Msg(("handshake", MsgHandshake((
 136         ("peerName", OUR_NAME),
 137     )))).encode())
 138     # }}}
 139     await writer.drain()

Mgbe ahụ, ọ na-eche nzaghachi sitere na ndị otu dịpụrụ adịpụ. Na-agbalị iji atụmatụ Msg ASN.1 dekọọ nzaghachi na-abata. Anyị na-eche na a ga-eziga ozi niile n'otu akụkụ TCP na anyị ga-enweta ya n'ụzọ nkịtị mgbe ị na-akpọ .read(). Anyị na-enyocha na anyị nwetara ozi aka aka.

 141     # Wait for Handshake message {{{
 142     data = await reader.read(256)
 143     if data == b"":
 144         logging.warning("%s: no answer, disconnecting", _id)
 145         writer.close()
 146         return
 147     try:
 148         msg, _ = Msg().decode(data)
 149     except ASN1Error:
 150         logging.warning("%s: undecodable answer, disconnecting", _id)
 151         writer.close()
 152         return
 153     logging.info("%s: got %s message", _id, msg.choice)
 154     if msg.choice != "handshake":
 155         logging.warning("%s: unexpected message, disconnecting", _id)
 156         writer.close()
 157         return
 158     # }}}

Anyị na-enyocha na anyị maara aha onye na-emekọrịta ihe natara. Ọ bụrụ na ọ bụghị, mgbe ahụ, anyị na-agbaji njikọ. Anyị na-elele ma anyị emebela njikọ na ya (onye na-emekọrịta ihe ọzọ nyere iwu ka ijikọ anyị) wee mechie ya. The IN_QUEUES kwụ n'ahịrị na-ejide Python strings na ederede ozi, ma nwere uru pụrụ iche nke Ọ dịghị nke na-egosi msg_sender coroutine ka ọ kwụsị ịrụ ọrụ ka o chefuo banyere onye dere ya metụtara njikọ TCP nketa.

 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 na-anabata ozi ndị na-apụ apụ (nke kwụ n'ahịrị site na oghere), na-edobe ha n'usoro n'ime ozi MsgText wee zipụ ha site na njikọ TCP. Ọ nwere ike imebi n'oge ọ bụla - anyị na-egbochi nke a n'ụzọ doro anya.

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))

Na njedebe, onye mmalite na-abanye na njedebe na-enweghị njedebe nke ịgụ ozi sitere na oghere. Na-enyocha ma ozi ndị a ọ bụ ozi ederede wee debe ya n'ahịrị OUT_QUEUES, nke a ga-esi na ya ziga ya na oghere nke onye na-emekọrịta ihe kwekọrọ. Gịnị kpatara na ịgaghị eme .gụọ () wee dekọọ ozi ahụ? N'ihi na ọ ga-ekwe omume na a ga-achịkọta ọtụtụ ozi sitere n'aka onye ọrụ na nchekwa sistemụ ma ziga ya n'otu akụkụ TCP. Anyị nwere ike depụta nke mbụ, na mgbe ahụ akụkụ nke na-esote nwere ike ịnọgide na nchekwa. N'ọnọdụ ọ bụla na-adịghị mma, anyị na-emechi njikọ TCP wee kwụsị msg_sender coroutine (site na izipu Ọ dịghị onye na OUT_QUEUES kwụ n'ahịrị).

 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)

Ka anyị laghachi na koodu isi. Mgbe ịmepụtara coroutines niile n'oge mmemme ahụ malitere, anyị na-amalite ihe nkesa TCP. Maka njikọ ọ bụla etinyere, ọ na-emepụta corutine nzaghachi.

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()

onye na-azaghachi yiri onye mmalite na enyo gosipụtara otu omume niile, mana enweghị ngwụcha nke ịgụ ozi na-amalite ozugbo, maka ịdị mfe. Ugbu a, usoro nkwekọrịta aka na-eziga otu ozi site n'akụkụ ọ bụla, ma n'ọdịnihu a ga-enwe abụọ site na njikọ njikọ, mgbe nke ahụ gasịrị, enwere ike izipu ozi ederede ozugbo.

  72 async def responder(reader, writer):
  73     _id = writer.get_extra_info("peername")
  74     logging.info("%s: connected", _id)
  75     buf = b""
  76     msg_expected = "handshake"
  77     peer_name = None
  78     while True:
  79         # Read until we get Msg message {{{
  80         data = await reader.read(MaxMsgLen)
  81         if data == b"":
  82             logging.info("%s: closed connection", _id)
  83             break
  84         buf += data
  85         if len(buf) > MaxMsgLen:
  86             logging.warning("%s: max buffer size exceeded", _id)
  87             break
  88         try:
  89             msg, tail = Msg().decode(buf)
  90         except ASN1Error:
  91             continue
  92         buf = tail
  93         # }}}
  94         if msg.choice != msg_expected:
  95             logging.warning("%s: unexpected %s message", _id, msg.choice)
  96             break
  97         if msg_expected == "text":
  98             try:
  99                 await msg_receiver(msg.value, peer_name)
 100             except ValueError as err:
 101                 logging.warning("%s: %s", err)
 102                 break
 103         # Process Handshake message {{{
 104         elif msg_expected == "handshake":
 105             logging.info("%s: got %s message", _id, msg_expected)
 106             msg_handshake = msg.value
 107             peer_name = str(msg_handshake["peerName"])
 108             if peer_name not in THEIR_NAMES:
 109                 logging.warning("unknown peer name: %s", peer_name)
 110                 break
 111             writer.write(Msg(("handshake", MsgHandshake((
 112                 ("peerName", OUR_NAME),
 113             )))).encode())
 114             await writer.drain()
 115             logging.info("%s: session established: %s", _id, peer_name)
 116             peer_alive = PEER_ALIVES.pop(peer_name, None)
 117             if peer_alive is not None:
 118                 peer_alive.close()
 119                 await IN_QUEUES[peer_name].put(None)
 120             PEER_ALIVES[peer_name] = writer
 121             asyncio.ensure_future(msg_sender(peer_name, writer))
 122             msg_expected = "text"
 123         # }}}
 124     logging.info("%s: disconnecting", _id)
 125     if msg_expected == "text":
 126         IN_QUEUES[peer_name].put(None)
 127     writer.close()

Protocol echekwara

Ọ bụ oge iji chekwaa nkwukọrịta anyị. Kedu ihe anyị pụtara site na nchekwa na ihe anyị chọrọ:

  • nzuzo nke ozi ezigara;
  • eziokwu na iguzosi ike n'ezi ihe nke ozi a na-ebufe - a ghaghị ịhụ mgbanwe ha;
  • nchebe megide mwakpo mmeghari - a ghaghị ịchọpụta eziokwu nke na-efu ma ọ bụ ozi ugboro ugboro (ma anyị kpebiri ịkwụsị njikọ ahụ);
  • njirimara na nkwenye nke interlocutors site na iji igodo ọha abanyelarị - anyị ekpebiela na mbụ na anyị na-eme netwọk enyi na enyi. Naanị mgbe nyochachara ka anyị ga-aghọta onye anyị na-akpakọrịta;
  • nnweta zuru oke nzuzo nzuzo Njirimara (PFS) - imebi igodo mbinye aka ogologo ndụ anyị ekwesịghị iduga n'ikike ịgụ akwụkwọ ozi niile gara aga. Ịdekọ okporo ụzọ egbochiri abaghị uru;
  • ndaba/izizi ozi (mbufe na aka aka) naanị n'ime otu nnọkọ TCP. Ịtinye ozi abịanyere aka n'ụzọ ziri ezi/ezitere site na nnọkọ ọzọ (ọbụlagodi na otu onye na-emekọrịta ihe) agaghị ekwe omume;
  • onye na-ekiri ihe na-adịghị mma ekwesịghị ịhụ ma ọ bụ njirimara njirimara, igodo ọha ebitere ogologo oge, ma ọ bụ hashes site na ha. Ụfọdụ amaghị aha sitere n'aka onye na-ekiri ihe.

N'ụzọ dị ịtụnanya, ihe fọrọ nke nta ka ọ bụrụ onye ọ bụla chọrọ ịnwe opekempe nke a na usoro ịmegharị aka ọ bụla, na ntakịrị ihe ndị a dị n'elu bụ n'ikpeazụ ezute maka ụkpụrụ "ụlọ toro". Ugbu a, anyị agaghị echepụta ihe ọhụrụ. M ga-akwado iji Usoro mkpọtụ maka iwu ụlọ, mana ka anyị họrọ ihe dị mfe.

Protocol abụọ kacha ewu ewu bụ:

  • TLS - Usoro dị mgbagwoju anya nke nwere ogologo akụkọ ihe mere eme nke ahụhụ, jambs, adịghị ike, echiche na-adịghị mma, mgbagwoju anya na adịghị ike (agbanyeghị, nke a enweghị ihe jikọrọ ya na TLS 1.3). Ma anyị anaghị atụle ya n'ihi na ọ dị mgbagwoju anya.
  • IPsec с Ike - enweghị nnukwu nsogbu cryptographic, ọ bụ ezie na ha adịghịkwa mfe. Ọ bụrụ na ị gụọ gbasara IKEv1 na IKEv2, mgbe ahụ isi iyi ha bụ STS, ISO/IEC IS 9798-3 na SIGMA (SIGn-and-MAc) protocols - dị mfe iji mejuputa n'otu mgbede.

Kedu ihe dị mma gbasara SIGMA, dịka njikọ kachasị ọhụrụ na mmepe nke STS/ISO protocol? Ọ na-emezu ihe niile anyị chọrọ (gụnyere ihe nchọpụta “zoo” interlocutor) na enweghị nsogbu nzuzo ama ama. Ọ dị ntakịrị - iwepu opekata mpe otu ihe sitere na ozi protocol ga-eduga na enweghị nchekwa ya.

Ka anyị si na protocol toro ụlọ kacha dị mfe gaa na SIGMA. Ọrụ kachasị mkpa anyị nwere mmasị na ya bụ isi nkwekọrịta: Ọrụ na-ewepụta ma ndị sonyere otu uru, nke enwere ike iji dị ka igodo symmetric. Na-abanyeghị n'ime nkọwa: onye ọ bụla n'ime ndị ahụ na-emepụta ephemeral (eji naanị n'ime otu nnọkọ) isi ụzọ abụọ (igodo ọha na nke nzuzo), gbanwee igodo ọha, kpọọ ọrụ nkwekọrịta, na ntinye nke ha na-agafe igodo nzuzo ha na ọha. igodo nke interlocutor.

┌────┐ ┌───┐ ┐ │ PeerA│ │ PeerB│ └──┬──└ ─ I b, ╔═════════ ═════════╗ │──────────│ ║PrvA, (b) ════════ ══════════════╝ IdB, PubB │ ╔═════════════════ ──────── ──────│ ║PrvB, PubB = DHgen()║ │ │ ╚═══════════════════ ──┐ ╔═══ ═══╧════════════╗ │ ║Key = DH(PrvA, PubB)║ <───╘ ═══════ ═══════ ════╝ │ │ │

Onye ọ bụla nwere ike ịwụli n'etiti wee jiri nke ya dochie igodo ọha - enweghị nkwenye nke ndị na-emekọrịta ihe na protocol a. Ka anyị tinye mbinye aka nwere igodo dị ogologo.

┌─────┐ ┌────┐ │ PeerA│ │ PeerB│ └──┬───────└───────────│, rvA, (PubA)) │ ╔═ │─────────────────────────────────────── = ibu()║ │ │ ║PrvA, PubA = DHgen() ║ │ │ ╚════════ SignPrvB, (PubB)) │ ╔══════════════════════┐═——. ──────── ───────────── ─│ ║ SignPrvB, SignPubB = ibu ( )║ │ │ ║PrvB, PubB = DHgen() ═ ═ ════════ ══════════════════════════════ ═════╗ │ ║ chọpụta( SignPubB, ...)║ │ <───┘ ║ Key = DH(PrvA , PubB) ║ │ │ ╚══════════════════ ═╝││ │

Ụdị mbinye aka dị otú ahụ agaghị arụ ọrụ, ebe ọ bụ na ejikọtaghị ya na otu nnọkọ. Ozi ndị dị otú ahụ dịkwa “dabara” maka nnọkọ gị na ndị ọzọ so. Ihe niile gbara ya gburugburu ga-enwerịrị ike ịdenye aha. Nke a na-amanyekwa anyị ịgbakwunye ozi ọzọ sitere na A.

Na mgbakwunye, ọ dị oke mkpa ịgbakwunye njirimara nke gị n'okpuru mbinye aka, ebe ọ bụ na ma ọ bụghị ya, anyị nwere ike dochie IdXXX wee jiri igodo nke onye ọzọ ama ama ọzọ bịanye aka na ozi ahụ. Iji gbochie ọgụ echiche, ọ dị mkpa na ihe ndị dị n'okpuru mbinye aka dị na ebe akọwapụtara nke ọma dịka ihe ha pụtara: ọ bụrụ na akara A (PubA, PubB), mgbe ahụ B ga-abanyerịrị (PubB, PubA). Nke a na-ekwukwa maka mkpa ọ dị ịhọrọ nhazi na usoro nke data serialized. Dịka ọmụmaatụ, a na-ahazi ntọala dị na ASN.1 DER encoding: SET OF(PubA, PubB) ga-adị ka SET OF(PubB, PubA).

┌────┐ ┌───┐ ┐ │ PeerA│ │ PeerB│ └──┬──└ ─ I b, ╔═════════ ═══════════════╗─────────—— ──────── ────────────>│ ║SignPrvA, SignPubA = ibu()║ │ │ ║PrvA, PubA = DHgen() ════ ═ ══════ ══════════════════╝ │IdB, PubB, akara(SignPrvB, (IdB, PubA, PubB))│ ═════ ═══════════╗│<────────── ──────── ─────────│ ║ SignPrvB, SignPubB = ibu ()║ │ │ ║PrvB, PubB = DHgen () ║ │ ╂ ═════════════════ ═══════ ══════════╝ │ akara(SignPrvA, (IdA, PubB, PubA)) │ ═══╗│─ ─────────────────── ──>│ ║ nyochaa (SignPubB, ...)║ │ │ ║Key = DH(PrvA, PubB)

Agbanyeghị, anyị enwetabeghị “gosipụta” na anyị ewepụtala otu igodo ekekọrịtara maka nnọkọ a. Na ụkpụrụ, anyị nwere ike ime na-enweghị usoro a - njikọ mbufe ụgbọ njem nke mbụ agaghị adị irè, mana anyị chọrọ na mgbe emechara aka ahụ, anyị ga-ejide n'aka na ekwenyela ihe niile. N'oge a, anyị nwere ISO/IEC IS 9798-3 protocol n'aka.

Anyị nwere ike bịanye aka na igodo emepụtara n'onwe ya. Nke a dị ize ndụ, ebe ọ bụ na ọ ga-ekwe omume na enwere ike ịpụpụ na algọridim mbinye aka ejiri mee ihe (ọ bụ ezie na bits-kwa-mbinye aka, ma ka na-agbapụta). Ọ ga-ekwe omume ịbịanye aka hash nke igodo iwepụta, mana ịwụpụ ọbụna hash nke igodo ewepụtara nwere ike ịba uru na mwakpo ike na-arụ ọrụ mmepụta ahụ. SIGMA na-eji ọrụ MAC nke na-akwado NJ onye zitere ya.

┌────┐ ┌───┐ ┐ │ PeerA│ │ PeerB│ └──┬──└ ─ I b, ╔═════════ ═══════════════╗─────────—— ──────── ────────────────────>│ ║SignPrvA, SignPubA = ibu ()║ │ │ ║ │ │ ║PrvA, ═ ══════ ═══════════════════════╝ ═══ ═════════════════╗ │<──────———— ─────── ──────────────────────│ ───│ ║ SignPrvB, SignPubB = ibu () ║ │ ─ ─ DHV │ ╚════ ═ ════════════════════════════ ════════╗│ akara (SignPrvA, (PubB, PubA)), MAC (IdA) │ ║ Key = DH (PrvA, PubB) ║ │──────── ─ ────────────────────────────>│ ║ify (Key, IdB) ─ ║ │ │ ╚ ══════════════════╝ │ │

Dị ka njikarịcha, ụfọdụ nwere ike chọọ iji igodo ephemeral ha (nke bụ, n'ezie, ihe nwute maka PFS). Dịka ọmụmaatụ, anyị mepụtara otu ụzọ igodo, nwara ijikọ, mana TCP adịghị ma ọ bụ kwụsịtụrụ ebe n'etiti protocol. Ọ bụ ihe ihere imefusị entropy na processor akụrụngwa na ọhụrụ ụzọ. Ya mere, anyị ga-ewebata ihe a na-akpọ kuki - uru pseudo-random nke ga-echebe megide mwakpo ịmegharị random mgbe ị na-eji igodo ọha emegharị emegharị. N'ihi njikọ dị n'etiti kuki na igodo ọha nke ephemeral, igodo ọha nke ndị ọzọ nwere ike wepu ya na mbinye aka dị ka ọ dịghị mkpa.

┌────┐ ┌────┐ │ PeerA│ │ PeerB│ └──┬──└ └──, Cook │ ╔════════ ════════════════╗ │───────——— ──────── ─────────────────── >│ ║ SignPrvA, SignPubA = ibu( )║ │ │ ║PrvA, PubA = DHgen() ║ │ ╚═══════════════════════ ═╝ │IdB, PubB, kukiB , akara (SignPrvB, (CookieA, CookieB, PubB)), MAC(IdB) │ ╔══════════════════════ │ < ─────────────────── ──────── ─────────────────────│ ─ SignPrvB, SignPubB = ibu ()║ │ ││ ║ │ │ │ ║ │ │ │ │ ║ │ │ │ ║ │ │ │ ║ │ │ │ ║ │ │ │ ║ │ │ │ ║ │ │ │ │ ║ ╚══════ ════════════════════════════ ══════╗ │ akara( SignPrvA, (KukiB, KukiA, PubA)), MAC (IdA) │ ║ Key = DH (PrvA, PubB) ║ │──────── ─ ─────────────────── ──────>│ ║ nyochaa (Igodo, IdB) ║ │ ║verify(SignPubB, ...)║ │ │ ╚══════════════════ │

N'ikpeazụ, anyị chọrọ ịnweta nzuzo nke ndị mmekọ anyị na-akparịta ụka site n'aka onye na-ekiri ihe na-adịghị mma. Iji mee nke a, SIGMA na-atụ aro ka e buru ụzọ gbanwee igodo ephemeral wee mepụta igodo nkịtị nke a ga-eji ezoro ezo izi ezi na ịchọpụta ozi. SIGMA na-akọwa nhọrọ abụọ:

  • SIGMA-I - na-echebe onye mbido site na mwakpo ndị na-arụ ọrụ, onye na-azaghachi site na ndị na-agafe agafe: onye mmalite na-akwado onye na-azaghachi ma ọ bụrụ na ihe adabaghị, mgbe ahụ ọ naghị enye njirimara ya. Onye na-azara ọnụ na-enye njirimara ya ma ọ bụrụ na ejiri ya malite protocol na-arụ ọrụ. Onye na-ekiri ihe na-adịghị mma na-amụta ihe ọ bụla;
    SIGMA-R - na-echebe onye na-azaghachi site na mwakpo na-arụ ọrụ, onye mbido site na ndị na-agafe agafe. Ihe niile bụ ihe dị iche, mana n'usoro iwu a ebutelarị ozi aka aka anọ.

    Anyị na-ahọrọ SIGMA-I n'ihi na ọ dị ka ihe anyị na-atụ anya n'aka onye ahịa-ihe nkesa maara nke ọma: a na-amata onye ahịa naanị site na ihe nkesa kwadoro, na onye ọ bụla maralarị ihe nkesa ahụ. gbakwunyere ọ dị mfe itinye n'ọrụ n'ihi ozi aka aka dị ole na ole. Naanị ihe anyị na-agbakwunye na protocol bụ izochi akụkụ nke ozi ahụ wee bufee ihe nchọpụta A na akụkụ ezoro ezo nke ozi ikpeazụ:

    ┌────┐ ┌──┐ ─┐ │ PeerA│ │ PeerB│ ╔═════════ ═══════════════╗─────────—— ──────── ─────────────────── ─────>│ ║ SignPrvA , SignPubA = ibu ()║ │ │ ║PrvA, PubA = DHgen() ║ │ ═══════════════════════ ════╝ │ PubB, CookieB, Enc ((IdB, akara (SignPrvB, (CookieA, CookieB, PubB)), MAC(IdB)) │ ╔═══════════════════ ══════╗│<────────── ──────── ║ SignPrv B, SignPubB = ibu ()║ │ │ ║ PrvB, PubB = DHgen() ║ │ │ ══════ ═╝ │ │ ╔ ════════ ═════════════╗ ═══╗ │, MAC((Id) (IdA) )) │ ║ Key = DH (PrvA, PubB) ║ │──────── ───── ─────────────── ─>│ ║ nyochaa (Igodo, IdB) ║ │ │ ║verify( Sign PubB, ...)║ │ ╚═════════════╂═══╂
    
    • A na-eji GOST R mee ihe maka mbinye aka 34.10-2012 algọridim nwere igodo 256-bit.
    • Iji mepụta igodo ọha, 34.10/2012/XNUMX na-eji VKO.
    • A na-eji CMAC dị ka MAC. Teknụzụ, nke a bụ ụdị ọrụ pụrụ iche nke ihe mgbochi, nke akọwapụtara na GOST R 34.13-2015. Dị ka ọrụ nzuzo maka ọnọdụ a - Oku (34.12-2015).
    • A na-eji hash igodo ọha ya dị ka njirimara onye na-emekọrịta ihe. Ejiri ya dị ka hash Stribog-256 (34.11/2012/256 XNUMX ibe n'ibe).

    Ka emechara aka, anyị ga-ekwenye na igodo ekekọrịtara. Anyị nwere ike iji ya maka izo ya ezo nke ozi njem. Akụkụ a dị nnọọ mfe ma sie ike imehie: anyị na-abawanye counter ozi, zoo ozi ahụ, nyochaa (MAC) counter na ciphertext, zipu. Mgbe ị na-enweta ozi, anyị na-elele na counter nwere uru a na-atụ anya ya, jiri mpempe akwụkwọ jiri chọpụta ciphertext, wee decrypt ya. Kedu igodo m ga-eji ezoro ezo ozi aka aka, bufee ozi yana otu esi achọpụta ha? Iji otu igodo maka ọrụ ndị a niile dị ize ndụ na enweghị amamihe. Ọ dị mkpa ịmepụta igodo site na iji ọrụ pụrụ iche KDF (ọrụ mmepụta isi). Ọzọ, ka anyị ghara kewaa ntutu ma chepụta ihe: HKDF amatala ogologo oge, nyocha nke ọma na enweghị nsogbu a ma ama. Ọ dị nwute, ọba akwụkwọ Python nke ala enweghị ọrụ a, yabụ anyị na-eji hkdf akpa rọba. HKDF na-eji n'ime HMAC, nke n'aka nke ya na-eji ọrụ hash. Ihe atụ mmejuputa iwu na Python na ibe Wikipedia na-ewe naanị ahịrị koodu ole na ole. Dị ka ọ dị na 34.10/2012/256, anyị ga-eji Stribog-XNUMX dị ka ọrụ hash. A ga-akpọ nsonaazụ nke ọrụ nkwekọrịta isi anyị igodo nnọkọ, nke a ga-esi na ya nweta ndị symmetric na-efu:

    kdf = Hkdf(None, key_session, hash=GOST34112012256)
    kdf.expand(b"handshake1-mac-identity")
    kdf.expand(b"handshake1-enc")
    kdf.expand(b"handshake1-mac")
    kdf.expand(b"handshake2-mac-identity")
    kdf.expand(b"handshake2-enc")
    kdf.expand(b"handshake2-mac")
    kdf.expand(b"transport-initiator-enc")
    kdf.expand(b"transport-initiator-mac")
    kdf.expand(b"transport-responder-enc")
    kdf.expand(b"transport-responder-mac")
    

    Ọdịdị/atụmatụ

    Ka anyị leba anya n'ụdị ASN.1 anyị nwere ugbu a maka ịnyefe data niile a:

    class Msg(Choice):
        schema = ((
            ("text", MsgText()),
            ("handshake0", MsgHandshake0(expl=tag_ctxc(0))),
            ("handshake1", MsgHandshake1(expl=tag_ctxc(1))),
            ("handshake2", MsgHandshake2(expl=tag_ctxc(2))),
        ))
    
    class MsgText(Sequence):
        schema = ((
            ("payload", MsgTextPayload()),
            ("payloadMac", MAC()),
        ))
    
    class MsgTextPayload(Sequence):
        schema = ((
            ("nonce", Integer(bounds=(0, float("+inf")))),
            ("ciphertext", OctetString(bounds=(1, MaxTextLen))),
        ))
    
    class MsgHandshake0(Sequence):
        schema = ((
            ("cookieInitiator", Cookie()),
            ("pubKeyInitiator", PubKey()),
        ))
    
    class MsgHandshake1(Sequence):
        schema = ((
            ("cookieResponder", Cookie()),
            ("pubKeyResponder", PubKey()),
            ("ukm", OctetString(bounds=(8, 8))),
            ("ciphertext", OctetString()),
            ("ciphertextMac", MAC()),
        ))
    
    class MsgHandshake2(Sequence):
        schema = ((
            ("ciphertext", OctetString()),
            ("ciphertextMac", MAC()),
        ))
    
    class HandshakeTBE(Sequence):
        schema = ((
            ("identity", OctetString(bounds=(32, 32))),
            ("signature", OctetString(bounds=(64, 64))),
            ("identityMac", MAC()),
        ))
    
    class HandshakeTBS(Sequence):
        schema = ((
            ("cookieTheir", Cookie()),
            ("cookieOur", Cookie()),
            ("pubKeyOur", PubKey()),
        ))
    
    class Cookie(OctetString): bounds = (16, 16)
    class PubKey(OctetString): bounds = (64, 64)
    class MAC(OctetString): bounds = (16, 16)
    

    AkakweTBS bụ ihe a ga-edebanye aha. HandshakeTBE - ihe a ga-ezoro ezo. Ana m adọta uche gị na ubi ukm na MsgHandshake1. 34.10 VKO, n'ihi na ọbụna ukwuu randomization nke igodo emepụtara, na-agụnye UKM (onye ọrụ keying ihe) paramita - nnọọ ọzọ entropy.

    Na-agbakwụnye Cryptography na Koodu

    Ka anyị tụlee naanị mgbanwe ndị e mere na koodu mbụ ahụ, ebe ọ bụ na usoro ahụ nọgidere bụrụ otu (n'ezie, e debere mmejuputa ikpeazụ na mbụ, mgbe ahụ, e wepụrụ ihe nzuzo niile na ya).

    Ebe ọ bụ na a ga-eji igodo ọha mee nyocha na njirimara nke ndị na-emekọrịta ihe, a ga-echekwa ha ugbu a ebe ogologo oge. Maka mfe, anyị na-eji JSON dị ka nke a:

    {
        "our": {
            "prv": "21254cf66c15e0226ef2669ceee46c87b575f37f9000272f408d0c9283355f98",
            "pub": "938c87da5c55b27b7f332d91b202dbef2540979d6ceaa4c35f1b5bfca6df47df0bdae0d3d82beac83cec3e353939489d9981b7eb7a3c58b71df2212d556312a1"
        },
        "their": {
            "alice": "d361a59c25d2ca5a05d21f31168609deeec100570ac98f540416778c93b2c7402fd92640731a707ec67b5410a0feae5b78aeec93c4a455a17570a84f2bc21fce",
            "bob": "aade1207dd85ecd283272e7b69c078d5fae75b6e141f7649ad21962042d643512c28a2dbdc12c7ba40eb704af920919511180c18f4d17e07d7f5acd49787224a"
        }
    }
    

    anyị - isi ụzọ anyị, hexadecimal igodo nzuzo na nke ọha. ha - aha interlocutors na igodo ọha ha. Ka anyị gbanwee arụmụka ahịrị iwu wee gbakwunye nhazi 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),
        }
    # }}}
    

    Igodo nzuzo nke 34.10 algọridim bụ ọnụọgụ enweghị usoro. 256-bit size maka 256-bit elliptik akụkụ. PyGOST anaghị arụ ọrụ na set nke bytes, kama ọ na-arụ ọrụ ọnụ ọgụgụ buru ibu, yabụ igodo nzuzo anyị (urandom(32)) kwesịrị ịgbanwe gaa na nọmba site na iji gost3410.prv_unmarshal(). A na-ekpebi igodo ọha site na igodo nzuzo site na iji gost3410.public_key(). Igodo ọha 34.10 bụ ọnụọgụ abụọ buru ibu nke dịkwa mkpa ka a gbanwee n'ime usoro byte maka ịdị mfe nke nchekwa na nnyefe site na iji gost3410.pub_marshal ().

    Mgbe ị gụchara faịlụ JSON, igodo ọha na-achọ ka atụgharịghachi azụ site na iji gost3410.pub_unmarshal(). Ebe ọ bụ na anyị ga-enweta ihe nchọpụta nke ndị na-emekọrịta ihe n'ụdị hash site na igodo ọha, enwere ike gbakọọ ha ozugbo tupu oge ma tinye ya na akwụkwọ ọkọwa okwu maka ọchụchọ ngwa ngwa. Stribog-256 hash bụ gost34112012256.GOST34112012256(), nke na-eju afọ hashlib interface nke ọrụ hash.

    Kedu ka coroutine onye mmalite siri gbanwee? Ihe niile dị ka atụmatụ aka aka: anyị na-emepụta kuki (128-bit dị ukwuu), otu ụzọ igodo ephemeral 34.10, nke a ga-eji maka ọrụ nkwekọrịta igodo VKO.

     395 async def initiator(host, port):
     396     _id = repr((host, port))
     397     logging.info("%s: dialing", _id)
     398     reader, writer = await asyncio.open_connection(host, port)
     399     # Generate our ephemeral public key and cookie, send Handshake 0 message {{{
     400     cookie_our = Cookie(urandom(16))
     401     prv = gost3410.prv_unmarshal(urandom(32))
     402     pub_our = gost3410.public_key(CURVE, prv)
     403     pub_our_raw = PubKey(gost3410.pub_marshal(pub_our))
     404     writer.write(Msg(("handshake0", MsgHandshake0((
     405         ("cookieInitiator", cookie_our),
     406         ("pubKeyInitiator", pub_our_raw),
     407     )))).encode())
     408     # }}}
     409     await writer.drain()
    

    • anyị na-eche nzaghachi wee dekọọ ozi Msg na-abata;
    • jide n'aka na ị ga-enweta aka1;
    • decode igodo ọha ephemeral nke ndị ọzọ wee gbakọọ igodo nnọkọ;
    • Anyị na-ewepụta igodo symmetric dị mkpa maka ịhazi akụkụ TBE nke ozi ahụ.

     423     logging.info("%s: got %s message", _id, msg.choice)
     424     if msg.choice != "handshake1":
     425         logging.warning("%s: unexpected message, disconnecting", _id)
     426         writer.close()
     427         return
     428     # }}}
     429     msg_handshake1 = msg.value
     430     # Validate Handshake message {{{
     431     cookie_their = msg_handshake1["cookieResponder"]
     432     pub_their_raw = msg_handshake1["pubKeyResponder"]
     433     pub_their = gost3410.pub_unmarshal(bytes(pub_their_raw))
     434     ukm_raw = bytes(msg_handshake1["ukm"])
     435     ukm = ukm_unmarshal(ukm_raw)
     436     key_session = kek_34102012256(CURVE, prv, pub_their, ukm, mode=2001)
     437     kdf = Hkdf(None, key_session, hash=GOST34112012256)
     438     key_handshake1_mac_identity = kdf.expand(b"handshake1-mac-identity")
     439     key_handshake1_enc = kdf.expand(b"handshake1-enc")
     440     key_handshake1_mac = kdf.expand(b"handshake1-mac")
    

    UKM bụ a 64-bit nọmba (urandom(8)), nke na-achọkwa deserialization si byte nnọchiteanya iji gost3410_vko.ukm_unmarshal(). Ọrụ VKO maka 34.10/2012/256 3410-bit bụ gost34102012256_vko.kek_XNUMX () (KEK - igodo nzuzo).

    Igodo nnọkọ emepụtara abụrụla usoro 256-bit pseudo-random byte. Ya mere, enwere ike iji ya ozugbo na ọrụ HKDF. Ebe GOST34112012256 na-emeju interface hashlib, enwere ike iji ya ozugbo na klas Hkdf. Anyị anaghị akọwapụta nnu (arụmụka mbụ nke Hkdf), ebe ọ bụ na igodo emepụtara, n'ihi na ephemeralality nke isi ụzọ abụọ na-ekere òkè ga-adị iche maka nnọkọ ọ bụla ma nweelarị entropy zuru ezu. kdf.expand() na ndabara ewepụtala igodo 256-bit achọrọ maka Grasshopper ma emechaa.

    Na-esote, a na-enyocha akụkụ TBE na TBS nke ozi mbata:

    • A na-agbakọ ma lelee MAC n'elu ciphertext na-abata;
    • a na-emezigharị ederede ederede;
    • A na-ahazi usoro TBE;
    • A na-ewepụ ihe njirimara nke onye na-emekọrịta ihe na ya wee lelee ma ọ bụ onye anyị maara ma ọlị;
    • A na-agbakọ MAC n'elu njirimara a ma lelee ya;
    • akwadoro mbinye aka n'elu ihe owuwu TBS, nke gụnyere kuki nke otu abụọ na igodo ephemeral ọha nke ndị ọzọ. A na-enyocha mbinye aka ahụ site na igodo mbinye aka dị ogologo ndụ nke interlocutor.

     441     try:
     442         peer_name = validate_tbe(
     443             msg_handshake1,
     444             key_handshake1_mac_identity,
     445             key_handshake1_enc,
     446             key_handshake1_mac,
     447             cookie_our,
     448             cookie_their,
     449             pub_their_raw,
     450         )
     451     except ValueError as err:
     452         logging.warning("%s: %s, disconnecting", _id, err)
     453         writer.close()
     454         return
     455     # }}}
    
     128 def validate_tbe(
     129         msg_handshake: Union[MsgHandshake1, MsgHandshake2],
     130         key_mac_identity: bytes,
     131         key_enc: bytes,
     132         key_mac: bytes,
     133         cookie_their: Cookie,
     134         cookie_our: Cookie,
     135         pub_key_our: PubKey,
     136 ) -> str:
     137     ciphertext = bytes(msg_handshake["ciphertext"])
     138     mac_tag = mac(GOST3412Kuznechik(key_mac).encrypt, KUZNECHIK_BLOCKSIZE, ciphertext)
     139     if not compare_digest(mac_tag, bytes(msg_handshake["ciphertextMac"])):
     140         raise ValueError("invalid MAC")
     141     plaintext = ctr(
     142         GOST3412Kuznechik(key_enc).encrypt,
     143         KUZNECHIK_BLOCKSIZE,
     144         ciphertext,
     145         8 * b"x00",
     146     )
     147     try:
     148         tbe, _ = HandshakeTBE().decode(plaintext)
     149     except ASN1Error:
     150         raise ValueError("can not decode TBE")
     151     key_sign_pub_hash = bytes(tbe["identity"])
     152     peer = KEYS.get(key_sign_pub_hash)
     153     if peer is None:
     154         raise ValueError("unknown identity")
     155     mac_tag = mac(
     156         GOST3412Kuznechik(key_mac_identity).encrypt,
     157         KUZNECHIK_BLOCKSIZE,
     158         key_sign_pub_hash,
     159     )
     160     if not compare_digest(mac_tag, bytes(tbe["identityMac"])):
     161         raise ValueError("invalid identity MAC")
     162     tbs = HandshakeTBS((
     163         ("cookieTheir", cookie_their),
     164         ("cookieOur", cookie_our),
     165         ("pubKeyOur", pub_key_our),
     166     ))
     167     if not gost3410.verify(
     168         CURVE,
     169         peer["pub"],
     170         GOST34112012256(tbs.encode()).digest(),
     171         bytes(tbe["signature"]),
     172     ):
     173         raise ValueError("invalid signature")
     174     return peer["name"]
    

    Dị ka m dere n'elu, 34.13/2015/XNUMX na-akọwa dị iche iche igbochi cipher ụdịdị ọrụ site na 34.12/2015/3413. N'ime ha enwere ọnọdụ maka ịmepụta ntinye nṅomi na mgbakọ MAC. Na PyGOST nke a bụ gost34.12.mac (). Ọnọdụ a chọrọ ịgafe ọrụ ezoro ezo (nata na iweghachi otu ngọngọ data), nha nke ngọngọ nzuzo na, n'ezie, data n'onwe ya. Kedu ihe kpatara na ị nweghị ike hardcode nha ngọngọ nzuzo ahụ? 2015/128/64 na-akọwa ọ bụghị naanị XNUMX-bit Grasshopper cipher, kamakwa XNUMX-bit. Magma - GOST 28147-89 a gbanwere ntakịrị, emebere azụ na KGB ma ka nwere otu n'ime ebe nchekwa kachasị elu.

    A malitere Kuznechik site na ịkpọ gost.3412.GOST3412Kuznechik (igodo) wee weghachi ihe na ụzọ .encrypt () / .decrypt () dabara adaba maka ịgafe na ọrụ 34.13. A na-agbakọ MAC dị ka ndị a: gost3413.mac(GOST3412Kuznechik(key).encrypt, KUZNECHIK_BLOCKSIZE, ciphertext). Iji tụnyere MAC gbakọrọ na natara, ị nweghị ike iji ntụnyere mbụ (==) nke eriri byte, ebe ọ bụ na ọrụ a na-ewepụta oge ntụnyere, nke, n'ozuzu, nwere ike ibute adịghị ike dị ka. IGBO mbuso agha na TLS. Python nwere ọrụ pụrụ iche, hmac.compare_digest, maka nke a.

    Ọrụ cipher ngọngọ nwere ike zoo naanị otu ngọngọ data. Maka ọnụ ọgụgụ buru ibu, ma ọ bụghịkwa ọtụtụ ogologo, ọ dị mkpa iji ọnọdụ ezoro ezo. 34.13-2015 na-akọwa ihe ndị a: ECB, CTR, OFB, CBC, CFB. Nke ọ bụla nwere akụkụ ngwa na njirimara nke ya a na-anabata. N'ụzọ dị mwute, anyị ka enwebeghị ọkọlọtọ ụdịdị nzuzo akwadoro (dị ka CCM, OCB, GCM na ihe ndị ọzọ) - a na-amanye anyị ma ọ dịkarịa ala tinye MAC n'onwe anyị. m na-ahọrọ counter mode (CTR): ọ dịghị achọ padding ka ngọngọ size, nwere ike yiri, na-eji naanị ezo ọrụ, nwere ike iji n'enweghị na-ezoro ọnụ ọgụgụ dị ukwuu nke ozi (n'adịghị ka CBC, nke nwere ndakpọ ngwa ngwa).

    Dị ka .mac (), .ctr () na-ewere ntinye yiri nke ahụ: ciphertext = gost3413.ctr(GOST3412Kuznechik(key) .encrypt, KUZNECHIK_BLOCKSIZE, plaintext, iv). Ọ chọrọ ka ezipụta vector mmalite nke bụ kpọmkwem ọkara ogologo nke mgbochi nzuzo. Ọ bụrụ na a na-eji igodo nzuzo anyị naanị iji zoo otu ozi (n'agbanyeghị site na ọtụtụ ngọngọ), mgbe ahụ ọ dị mma ịtọ vector mmalite efu. Iji zoo ozi aka aka, anyị na-eji igodo dị iche oge ọ bụla.

    Ịkwado mbinye aka gost3410.verify() bụ ihe na-adịghị mkpa: anyị na-agafe elliptik curve n'ime nke anyị na-arụ ọrụ (anyị nanị na-edekọ ya na anyị GOSTIM protocol), igodo ọha nke signer (echefula na nke a kwesịrị ịbụ a tuple nke abụọ. ọnụ ọgụgụ buru ibu, ma ọ bụghị eriri byte), 34.11/2012/XNUMX hash na mbinye aka n'onwe ya.

    Na-esote, n'ime onye mmalite anyị na-akwado ma zipụ ozi mmekọ aka na aka aka2, na-eme otu omume ahụ anyị mere n'oge nkwenye, naanị n'ụzọ nkịtị: ịbanye na igodo anyị kama ịlele, wdg ...

     456     # Prepare and send Handshake 2 message {{{
     457     tbs = HandshakeTBS((
     458         ("cookieTheir", cookie_their),
     459         ("cookieOur", cookie_our),
     460         ("pubKeyOur", pub_our_raw),
     461     ))
     462     signature = gost3410.sign(
     463         CURVE,
     464         KEY_OUR_SIGN_PRV,
     465         GOST34112012256(tbs.encode()).digest(),
     466     )
     467     key_handshake2_mac_identity = kdf.expand(b"handshake2-mac-identity")
     468     mac_tag = mac(
     469         GOST3412Kuznechik(key_handshake2_mac_identity).encrypt,
     470         KUZNECHIK_BLOCKSIZE,
     471         bytes(KEY_OUR_SIGN_PUB_HASH),
     472     )
     473     tbe = HandshakeTBE((
     474         ("identity", KEY_OUR_SIGN_PUB_HASH),
     475         ("signature", OctetString(signature)),
     476         ("identityMac", MAC(mac_tag)),
     477     ))
     478     tbe_raw = tbe.encode()
     479     key_handshake2_enc = kdf.expand(b"handshake2-enc")
     480     key_handshake2_mac = kdf.expand(b"handshake2-mac")
     481     ciphertext = ctr(
     482         GOST3412Kuznechik(key_handshake2_enc).encrypt,
     483         KUZNECHIK_BLOCKSIZE,
     484         tbe_raw,
     485         8 * b"x00",
     486     )
     487     mac_tag = mac(
     488         GOST3412Kuznechik(key_handshake2_mac).encrypt,
     489         KUZNECHIK_BLOCKSIZE,
     490         ciphertext,
     491     )
     492     writer.write(Msg(("handshake2", MsgHandshake2((
     493         ("ciphertext", OctetString(ciphertext)),
     494         ("ciphertextMac", MAC(mac_tag)),
     495     )))).encode())
     496     # }}}
     497     await writer.drain()
     498     logging.info("%s: session established: %s", _id, peer_name)
     

    Mgbe emebere nnọkọ ahụ, a na-emepụta igodo njem (igodo dị iche iche maka izo ya ezo, maka nyocha, maka otu nke ọ bụla), a na-ebido Grasshopper iji decrypt na lelee MAC:

     499     # Run text message sender, initialize transport decoder {{{
     500     key_initiator_enc = kdf.expand(b"transport-initiator-enc")
     501     key_initiator_mac = kdf.expand(b"transport-initiator-mac")
     502     key_responder_enc = kdf.expand(b"transport-responder-enc")
     503     key_responder_mac = kdf.expand(b"transport-responder-mac")
     ...
     509     asyncio.ensure_future(msg_sender(
     510         peer_name,
     511         key_initiator_enc,
     512         key_initiator_mac,
     513         writer,
     514     ))
     515     encrypter = GOST3412Kuznechik(key_responder_enc).encrypt
     516     macer = GOST3412Kuznechik(key_responder_mac).encrypt
     517     # }}}
     519     nonce_expected = 0
    
     520     # Wait for test messages {{{
     521     while True:
     522         data = await reader.read(MaxMsgLen)
     ...
     530             msg, tail = Msg().decode(buf)
     ...
     537         try:
     538             await msg_receiver(
     539                 msg.value,
     540                 nonce_expected,
     541                 macer,
     542                 encrypter,
     543                 peer_name,
     544             )
     545         except ValueError as err:
     546             logging.warning("%s: %s", err)
     547             break
     548         nonce_expected += 1
     549     # }}}
    

    Coroutine msg_sender na-ezochi ozi tupu izipu ha na njikọ TCP. Ozi ọ bụla nwere ihe na-arị elu n'otu n'otu, nke bụkwa vector mmalite mgbe ezoro ezo na ọnọdụ counter. A na-ekwe nkwa na ozi na ngọngọ ozi ọ bụla nwere uru counter dị iche.

    async def msg_sender(peer_name: str, key_enc: bytes, key_mac: bytes, writer) -> None:
        nonce = 0
        encrypter = GOST3412Kuznechik(key_enc).encrypt
        macer = GOST3412Kuznechik(key_mac).encrypt
        in_queue = IN_QUEUES[peer_name]
        while True:
            text = await in_queue.get()
            if text is None:
                break
            ciphertext = ctr(
                encrypter,
                KUZNECHIK_BLOCKSIZE,
                text.encode("utf-8"),
                long2bytes(nonce, 8),
            )
            payload = MsgTextPayload((
                ("nonce", Integer(nonce)),
                ("ciphertext", OctetString(ciphertext)),
            ))
            mac_tag = mac(macer, KUZNECHIK_BLOCKSIZE, payload.encode())
            writer.write(Msg(("text", MsgText((
                ("payload", payload),
                ("payloadMac", MAC(mac_tag)),
            )))).encode())
            nonce += 1
    

    A na-ahazi ozi mbata site na msg_receiver coroutine, nke na-ejikwa nyocha na nbipu:

    async def msg_receiver(
            msg_text: MsgText,
            nonce_expected: int,
            macer,
            encrypter,
            peer_name: str,
    ) -> None:
        payload = msg_text["payload"]
        if int(payload["nonce"]) != nonce_expected:
            raise ValueError("unexpected nonce value")
        mac_tag = mac(macer, KUZNECHIK_BLOCKSIZE, payload.encode())
        if not compare_digest(mac_tag, bytes(msg_text["payloadMac"])):
            raise ValueError("invalid MAC")
        plaintext = ctr(
            encrypter,
            KUZNECHIK_BLOCKSIZE,
            bytes(payload["ciphertext"]),
            long2bytes(nonce_expected, 8),
        )
        text = plaintext.decode("utf-8")
        await OUT_QUEUES[peer_name].put(text)
    

    nkwubi

    GOSTIM bu n'obi ka ejiri ya naanị maka ebumnuche agụmakwụkwọ (ebe ọ bụ na ule anaghị ekpuchi ya, opekata mpe)! Enwere ike ibudata koodu isi mmalite nke mmemme ahụ ebe a (Стрибог-256 хэш: 995bbd368c04e50a481d138c5fa2e43ec7c89bc77743ba8dbabee1fde45de120). Как и все мои проекты, типа GoGOST, PYDERASN, NCCP, GoVPN, GOSTIM bụ kpamkpam software efu, kesara n'okpuru usoro GPLv3 +.

    Sergey Matveev, cypherpunk, otu Ụlọ ọrụ SPO Foundation, Python/Go onye nrụpụta, onye isi ọkachamara FSUE "STC "Atlas".

isi: www.habr.com

Tinye a comment