GOSTIM: P2P F2F E2EE IM هڪ شام ۾ GOST cryptography سان

ڊولپر ٿيڻ PyGOST لائبريريون (GOST cryptographic primitives in pure Python)، مون کي اڪثر سوال ملندا آهن ته ڪھڙيءَ طرح آسان ترين محفوظ ميسيجنگ تي عمل ڪجي. گھڻا ماڻھو اپلائيڊ ڪرپٽوگرافي کي بلڪل سادو سمجھن ٿا، ۽ .encrypt() کي بلاڪ سيفر تي ڪال ڪرڻ ڪافي ھوندو ته ان کي محفوظ طريقي سان ڪميونيڪيشن چينل تي موڪلڻ لاءِ. ٻيا مڃيندا آھن ته لاڳو ڪيل ڪرپٽوگرافي ڪجھھ ماڻھن جي قسمت آھي، ۽ اھو قابل قبول آھي ته امير ڪمپنيون ٽيليگرام وانگر اولمپياڊ-رياضيدانن سان. لاڳو نٿو ڪري سگهي محفوظ پروٽوڪول.

اهو سڀ ڪجهه مون کي هن مضمون لکڻ لاءِ حوصلا افزائي ڪرڻ لاءِ ڏيکاريو ته cryptographic پروٽوڪول ۽ محفوظ IM تي عمل ڪرڻ اهڙو ڏکيو ڪم ناهي. بهرحال، اهو توهان جي پنهنجي تصديق ۽ اهم معاهدي پروٽوڪول کي ايجاد ڪرڻ جي قابل ناهي.

GOSTIM: P2P F2F E2EE IM هڪ شام ۾ GOST cryptography سان
مضمون لکندو پير صاحب کي پير صاحب, دوست کان دوست, آخر کان آخر تائين انڪرپٽ ٿيل انسٽنٽ ميسينجر سان SIGMA-I تصديق ۽ اهم معاهدو پروٽوڪول (جنهن جي بنياد تي اهو لاڳو ٿئي ٿو IPsec IKE)، خاص طور تي استعمال ڪندي GOST cryptographic algorithms PyGOST لائبريري ۽ ASN.1 پيغام انڪوڊنگ لائبريري پيڊراسن (جنهن بابت آئون اڳ ۾ ئي اڳ لکيو). هڪ شرط: اهو ايترو سادو هجڻ گهرجي ته اهو شروع کان هڪ شام (يا ڪم جي ڏينهن) ۾ لکي سگهجي ٿو، ٻي صورت ۾ اهو هاڻي هڪ سادي پروگرام ناهي. ان ۾ شايد غلطيون، غير ضروري پيچيدگيون، خاميون آهن، ان سان گڏ هي منهنجو پهريون پروگرام آهي جيڪو استعمال ڪري رهيو آهيان asyncio لائبريري.

آئي ايم ڊيزائن

پهرين، اسان کي سمجهڻ جي ضرورت آهي ته اسان جي IM وانگر نظر ايندي. سادگي لاءِ، اچو ته ان کي هڪ پيئر-ٽو-پيئر نيٽ ورڪ، شرڪت ڪندڙن جي ڪنهن به دريافت کان سواءِ. اسان ذاتي طور تي ظاهر ڪنداسين ته ڪهڙي پتي: بندرگاهه سان ڳنڍڻ لاء رابطو ڪندڙ سان رابطو ڪرڻ لاء.

مان سمجهان ٿو ته، هن وقت، اهو مفروضو ته سڌو ڪميونيڪيشن ٻن بااختيار ڪمپيوٽرن جي وچ ۾ موجود آهي، عملي طور تي IM جي قابل اطلاق تي هڪ اهم حد آهي. پر جيترا وڌيڪ ڊولپر NAT-traversal cruchs جي سڀني قسمن تي عمل ڪندا، اوترو ئي وڌيڪ اسان IPv4 انٽرنيٽ تي رهنداسين، وچ ۾ رابطي جي هڪ مايوس ڪندڙ امڪان سان، پاڻمرادو ڪمپيوٽرن جي وچ ۾. توهان ڪيتري وقت تائين گهر ۽ ڪم تي IPv6 جي کوٽ کي برداشت ڪري سگهو ٿا؟

اسان وٽ هڪ دوست کان دوست نيٽ ورڪ هوندو: سڀني ممڪن ڳالهين کي اڳ ۾ ڄاڻڻ گهرجي. سڀ کان پهريان، اهو سڀ ڪجهه آسان بڻائي ٿو: اسان پنهنجو تعارف ڪرايو، نالو/ڪجهه لڌو يا نه مليو، منقطع يا ڪم جاري رکو، گفتگو ڪندڙ کي ڄاڻو. ٻيو، عام طور تي، اهو محفوظ آهي ۽ ڪيترن ئي حملن کي ختم ڪري ٿو.

IM انٽرفيس کلاسک حل جي ويجهو هوندو بيڪار منصوبا، جيڪو مان واقعي پسند ڪريان ٿو انهن جي minimalism ۽ يونڪس واٽ فلسفي لاءِ. IM پروگرام هڪ ڊاريڪٽري ٺاهي ٿو ٽن يونڪس ڊومين ساکٽ سان هر هڪ ڳالهائيندڙ لاءِ:

  • ان ۾ - ڳالهائيندڙ ڏانهن موڪليا ويا پيغام ان ۾ رڪارڊ ٿيل آهن؛
  • ٻاهر - ڳالهائيندڙ کان مليل پيغام ان مان پڙهيا ويندا آهن؛
  • رياست - ان مان پڙهڻ سان، اسان اهو معلوم ڪيو ته ڇا ڳالهائيندڙ هن وقت ڳنڍيل آهي، ڪنيڪشن ايڊريس/پورٽ.

ان کان علاوه، هڪ ڪني ساکٽ ٺاهي وئي آهي، ميزبان پورٽ لکڻ سان جنهن ۾ اسان ريموٽ انٽرلوڪٽر سان ڪنيڪشن شروع ڪريون ٿا.

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

اهو طريقو توهان کي اجازت ڏئي ٿو IM ٽرانسپورٽ ۽ يوزر انٽرفيس جي آزاد لاڳو ڪرڻ، ڇو ته ڪو دوست ناهي، توهان سڀني کي خوش نه ٿا ڪري سگهو. استعمال ڪندي ٽمڪس ۽ / يا گھڻائي وارو، توهان حاصل ڪري سگهو ٿا هڪ گھڻ-ونڊو انٽرفيس نحو نمايان ڪرڻ سان. ۽ مدد سان آر ايل رِپ توهان حاصل ڪري سگهو ٿا هڪ GNU Readline-مطابقت رکندڙ پيغام ان پٽ لائن.

حقيقت ۾، بيڪار پروجيڪٽ استعمال ڪندا آهن FIFO فائلون. ذاتي طور تي، مان سمجهي نه سگهيو آهيان ته ڪيئن فائلن سان مقابلي ۾ ڪم ڪرڻ لاءِ asyncio ۾ وقف ٿيل موضوعن جي هٿ سان لکيل پس منظر کان سواءِ (مان ڪافي وقت کان اهڙين شين لاءِ ٻولي استعمال ڪري رهيو آهيان. Go). تنهن ڪري، مون يونڪس ڊومين ساکٽ سان ڪرڻ جو فيصلو ڪيو. بدقسمتي سان، اهو ڪرڻ ناممڪن بڻائي ٿو گونج 2001:470:dead::babe 6666 > conn. مون هن مسئلي کي استعمال ڪندي حل ڪيو ساڪٽ: گونج 2001: 470: مئل:: بيبي 6666 | socat - UNIX-Connect:conn، socat READLINE UNIX-Connect:alice/in.

اصل غير محفوظ پروٽوڪول

TCP ٽرانسپورٽ طور استعمال ڪيو ويندو آهي: اهو ترسيل ۽ ان جي آرڊر جي ضمانت ڏئي ٿو. UDP ضمانت ڏئي ٿو نه (جيڪو ڪارائتو هوندو جڏهن ڪرپٽوگرافي استعمال ڪيو ويندو)، پر سپورٽ ايس ٽي پي پٿون دٻي مان ٻاهر نٿو اچي.

بدقسمتي سان، TCP ۾ پيغام جو ڪو تصور ناهي، صرف بائيٽ جو هڪ وهڪرو. تنهن ڪري، ضروري آهي ته پيغامن لاءِ هڪ فارميٽ تيار ڪيو وڃي ته جيئن اهي هن سلسلي ۾ پاڻ ۾ شيئر ڪري سگهجن. اسان لائن فيڊ ڪردار کي استعمال ڪرڻ تي متفق ٿي سگھون ٿا. اهو شروع ڪرڻ وارن لاءِ ٺيڪ آهي، پر هڪ دفعو اسان پنهنجي پيغامن کي انڪرپٽ ڪرڻ شروع ڪريون ٿا، اهو ڪردار ڪٿي به ciphertext ۾ ظاهر ٿي سگهي ٿو. نيٽ ورڪن ۾، تنهن ڪري، مشهور پروٽوڪول اهي آهن جيڪي پهريان پيغام جي ڊيگهه بائيٽ ۾ موڪليندا آهن. مثال طور، باڪس کان ٻاهر Python وٽ xdrlib آهي، جيڪا توهان کي ساڳي فارميٽ سان ڪم ڪرڻ جي اجازت ڏئي ٿي XDR.

اسان TCP پڙهڻ سان صحيح ۽ موثر طريقي سان ڪم نه ڪنداسين - اسان ڪوڊ کي آسان ڪنداسين. اسان ساکٽ مان ڊيٽا کي اڻ لامحدود لوپ ۾ پڙهون ٿا جيستائين اسان مڪمل پيغام کي ڊيڪوڊ نه ڪريون. JSON سان XML پڻ استعمال ڪري سگھجي ٿو فارميٽ طور ھن طريقي جي لاءِ. پر جڏهن ڪرپٽوگرافي شامل ڪئي ويندي، ڊيٽا کي سائن ان ۽ تصديق ڪرڻي پوندي - ۽ ان لاءِ بائيٽ جي بائيٽ جي ضرورت پوندي شين جي هڪجهڙائي واري نمائندگي، جيڪا JSON/XML مهيا نه ڪندي آهي (ڊمپ جا نتيجا مختلف ٿي سگهن ٿا).

XDR هن ڪم لاءِ موزون آهي، تنهن هوندي به مان ASN.1 چونڊيو DER انڪوڊنگ ۽ پيڊراسن لائبريري، ڇو ته اسان وٽ هٿ تي اعلي سطحي شيون هونديون جن سان اهو اڪثر ڪري وڌيڪ خوشگوار ۽ ڪم ڪرڻ آسان آهي. اسڪيم کان سواءِ بين ڪوڊ, ميسيج پيڪ يا CBOR، ASN.1 خودڪار طريقي سان ڊيٽا کي سخت ڪوڊ ٿيل اسڪيما جي خلاف چيڪ ڪندو.

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

وصول ٿيل پيغام Msg هوندو: يا ته هڪ ٽيڪسٽ MsgText (هاڻي هڪ ٽيڪسٽ فيلڊ سان) يا هڪ MsgHandshake هٿ ملائڻ وارو پيغام (جنهن ۾ ڳالهائيندڙ جو نالو هوندو). هاڻي اهو وڌيڪ پيچيده ڏسڻ ۾ اچي ٿو، پر اهو مستقبل لاء هڪ بنياد آهي.

     ┌─────┐ ┌─────┐ │PeerA│ │PeerB│ └──┬┘──┘ ─┬ آئي ڊي اي) │ │───────── ─────│───>│ │ │ │MsgHandshake(IdB) │ │<─────││ │ │ MsgText() │ │──── MsgText() │ │ │

IM بغير cryptography

جيئن مون اڳ ۾ ئي چيو آهي، asyncio لائبريري سڀني ساکٽ آپريشنز لاءِ استعمال ڪئي ويندي. اچو ته اعلان ڪريون ته اسان لانچ تي ڇا توقع ڪريون ٿا:

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

پنهنجو نالو مقرر ڪريو (--اسان جو نالو ايلس). سڀ متوقع گفتگو ڪندڙ ڪاما (- انهن جا نالا باب، eve) سان الڳ ٿيل آهن. هر هڪ ڳالهائيندڙ لاء، يونڪس ساکٽ سان گڏ هڪ ڊاريڪٽري ٺاهي وئي آهي، انهي سان گڏ هر هڪ اندر، ٻاهر، رياست لاء ڪوروٽين:

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

ان ساکٽ مان صارف کان ايندڙ پيغام IN_QUEUES قطار ۾ موڪليا ويندا آهن:

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

گفتگو ڪندڙن کان پيغام موڪليا ويندا آهن OUT_QUEUES قطارن ڏانهن، جن مان ڊيٽا ٻاهر ساکٽ ڏانهن لکيل آهي:

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

جڏهن رياستي ساکٽ مان پڙهي، پروگرام PEER_ALIVE ڊڪشنري ۾ ڳالهائيندڙ جو پتو ڳولي ٿو. جيڪڏهن اڃا تائين ڪو به ڪنيڪشن نه آهي ڳالهائيندڙ سان، پوء هڪ خالي لڪير لکيل آهي.

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

جڏهن ڪني ساکٽ ڏانهن ايڊريس لکندي، ڪنيڪشن "شروع ڪندڙ" فنڪشن شروع ڪيو ويو آهي:

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

اچو ته شروعات ڪندڙ تي غور ڪريو. پهرين اهو واضح طور تي مخصوص ميزبان / بندرگاهه سان ڪنيڪشن کوليندو آهي ۽ ان جي نالي سان هٿ ملائڻ وارو پيغام موڪلي ٿو:

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

ان کان پوء، اهو ريموٽ پارٽي کان جواب جو انتظار ڪري ٿو. Msg ASN.1 اسڪيم استعمال ڪندي ايندڙ جواب کي ڊيڪوڊ ڪرڻ جي ڪوشش ڪري ٿو. اسان فرض ڪريون ٿا ته سمورو پيغام هڪ TCP حصي ۾ موڪليو ويندو ۽ اسان ان کي ايٽمي طور وصول ڪنداسين جڏهن ڪال ڪندي .read(). اسان چيڪ ڪريون ٿا ته اسان کي هٿ ملائڻ جو پيغام مليو آهي.

 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     # }}}

اسان چيڪ ڪريون ٿا ته انٽرويو ڪندڙ جو نالو اسان کي معلوم آهي. جيڪڏهن نه، ته پوء اسان ڪنيڪشن ٽوڙيو. اسان چيڪ ڪريون ٿا ته ڇا اسان اڳ ۾ ئي هن سان رابطو قائم ڪيو آهي (ڳالهائيندڙ ٻيهر اسان سان ڳنڍڻ جو حڪم ڏنو) ۽ ان کي بند ڪريو. IN_QUEUES قطار پيغام جي متن سان گڏ Python strings رکي ٿي، پر None جي ھڪڙي خاص قدر آھي جيڪا msg_sender coroutine کي ڪم ڪرڻ بند ڪرڻ لاءِ اشارو ڪري ٿي ته جيئن اھو پنھنجي ليکڪ جي باري ۾ وساري وڃي جيڪا ورثي واري TCP ڪنيڪشن سان لاڳاپيل آھي.

 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 ٻاهر نڪرندڙ پيغامن کي قبول ڪري ٿو (ان ساکٽ مان قطار ۾)، انهن کي هڪ MsgText پيغام ۾ ترتيب ڏئي ٿو ۽ انهن کي TCP ڪنيڪشن تي موڪلي ٿو. اهو ڪنهن به وقت ڀڄي سگهي ٿو - اسان واضح طور تي هن کي روڪيو.

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

آخر ۾، شروعات ڪندڙ ساکٽ مان پيغام پڙهڻ جي لامحدود لوپ ۾ داخل ٿئي ٿو. چيڪ ڪري ٿو ته ڇا اهي نياپا ٽيڪسٽ پيغام آهن ۽ انهن کي OUT_QUEUES قطار ۾ رکي ٿو، جتان اهي لاڳاپيل ڳالهائيندڙ جي ٻاهرئين ساکٽ ڏانهن موڪليا ويندا. توهان ڇو نٿا ڪري سگهو صرف .read() ۽ پيغام کي ڊيڪوڊ ڪريو؟ ڇاڪاڻ ته اهو ممڪن آهي ته صارف جا ڪيترائي پيغام آپريٽنگ سسٽم بفر ۾ گڏ ڪيا ويندا ۽ هڪ TCP حصي ۾ موڪليا ويندا. اسان پهرين هڪ کي ڊيڪوڊ ڪري سگهون ٿا، ۽ پوء ايندڙ جو حصو بفر ۾ رهي سگهي ٿو. ڪنهن به غير معمولي صورتحال جي صورت ۾، اسان TCP ڪنيڪشن کي بند ڪريون ٿا ۽ بند ڪريون ٿا msg_sender coroutine (OUT_QUEUES قطار ۾ None موڪلڻ سان).

 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)

اچو ته مکيه ڪوڊ ڏانھن واپس وڃو. پروگرام شروع ٿيڻ وقت سڀ ڪوروٽين ٺاهڻ کان پوءِ، اسان TCP سرور شروع ڪريون ٿا. هر قائم ڪيل ڪنيڪشن لاء، اهو هڪ جواب ڏيڻ وارو ڪورين ٺاهي ٿو.

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

جواب ڏيڻ وارو initiator سان ملندڙ جلندڙ آهي ۽ سڀني ساڳين عملن کي آئيني ڪري ٿو، پر پيغام پڙهڻ جو لامحدود لوپ فوري طور تي شروع ٿئي ٿو، سادگي لاءِ. في الحال، هينڊ شيڪ پروٽوڪول هر پاسي کان هڪ پيغام موڪلي ٿو، پر مستقبل ۾ ڪنيڪشن جي شروعات ڪندڙ کان ٻه هوندا، جنهن کان پوء ٽيڪسٽ پيغام فوري طور تي موڪليا ويندا.

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

محفوظ پروٽوڪول

اهو اسان جي مواصلات کي محفوظ ڪرڻ جو وقت آهي. سيڪيورٽي مان اسان جو مطلب ڇا آهي ۽ اسان ڇا ٿا چاهيون:

  • منتقل ٿيل پيغامن جي رازداري؛
  • منتقل ٿيل پيغامن جي صداقت ۽ سالميت - انهن جي تبديلين کي ڳولڻ گهرجي؛
  • ريپلي حملن جي خلاف تحفظ - غائب يا بار بار پيغامن جي حقيقت کي ضرور معلوم ڪيو وڃي (۽ اسان ڪنيڪشن کي ختم ڪرڻ جو فيصلو ڪيو)؛
  • اڳ ۾ داخل ٿيل عوامي چابيون استعمال ڪندي ڳالهائيندڙن جي سڃاڻپ ۽ تصديق - اسان اڳ ۾ ئي فيصلو ڪيو آهي ته اسان هڪ دوست کان دوست نيٽ ورڪ ٺاهي رهيا آهيون. صرف تصديق کان پوءِ اسان سمجھنداسين ته اسان ڪنھن سان ڳالھائي رھيا آھيون؛
  • دستياب مڪمل اڳڀرائي رازداري پراپرٽيز (PFS) - اسان جي ڊگھي عرصي واري سائننگ ڪيچ کي سمجھوتو ڪرڻ سان اڳئين خط و ڪتابت کي پڙھڻ جي صلاحيت نه ھجڻ گھرجي. رڪارڊنگ روڪيل ٽرئفڪ بيڪار ٿي وڃي ٿي؛
  • صرف هڪ TCP سيشن جي اندر پيغامن جي صحيحيت (ٽرانسپورٽ ۽ هٿ ملائڻ) جي صحيحيت. ٻئي سيشن مان صحيح طور تي دستخط ٿيل / تصديق ٿيل پيغام داخل ڪرڻ (جيتوڻيڪ ساڳي گفتگو ڪندڙ سان) ممڪن نه هجڻ گهرجي؛
  • هڪ غير فعال مبصر کي نه ڏسڻ گهرجي صارف جي سڃاڻپ ڪندڙ، منتقل ٿيل ڊگهي-رهندڙ عوامي چابيون، يا انهن مان هيش. غير فعال مبصر کان هڪ خاص گمنام.

حيرت انگيز طور تي، تقريبن هرڪو چاهي ٿو ته هي گهٽ ۾ گهٽ ڪنهن به هٿ ملائڻ واري پروٽوڪول ۾، ۽ مٿين مان تمام ٿورڙو آخرڪار "گهر جي" پروٽوڪول لاء ملي ٿو. هاڻي اسان ڪا نئين شيءِ ايجاد نه ڪنداسين. مان ضرور استعمال ڪرڻ جي سفارش ڪندس شور فريم ورڪ پروٽوڪول ٺاهڻ لاءِ، پر اچو ته ڪجھ سادو چونڊيو.

ٻه مشهور پروٽوڪول آهن:

  • TLS - هڪ تمام پيچيده پروٽوڪول جنهن جي ڊگهي تاريخ جي بگن، جهيڙن، ڪمزورين، خراب سوچ، پيچيدگي ۽ خاميون آهن (جڏهن ته، هن جو TLS 1.3 سان ٿورو تعلق آهي). پر اسان ان تي غور نه ڪندا آهيون ڇاڪاڻ ته اهو وڌيڪ پيچيده آهي.
  • IPsec с آئي - سنگين cryptographic مسئلا نه آهن، جيتوڻيڪ اهي پڻ سادي نه آهن. جيڪڏهن توهان IKEv1 ۽ IKEv2 بابت پڙهو ٿا، پوء انهن جو ذريعو آهي ايس ايس, ISO/IEC IS 9798-3 ۽ SIGMA (SIGn-and-MAc) پروٽوڪول - هڪ شام ۾ لاڳو ڪرڻ لاءِ ڪافي سادو.

SIGMA بابت ڇا سٺو آهي، جيئن STS/ISO پروٽوڪول جي ترقي ۾ جديد ڪڙي؟ اهو اسان جي سڀني ضرورتن کي پورو ڪري ٿو (بشمول "لڪائڻ وارو" مداخلت ڪندڙ سڃاڻپ ڪندڙ) ۽ ڪو به ڄاڻ نه آهي cryptographic مسئلا. اهو گهٽ ۾ گهٽ آهي - پروٽوڪول پيغام مان گهٽ ۾ گهٽ هڪ عنصر کي هٽائڻ ان جي عدم تحفظ جو سبب بڻجندو.

اچو ته سڀ کان آسان گھر جي ترقي يافته پروٽوڪول کان SIGMA ڏانهن وڃو. سڀ کان وڌيڪ بنيادي آپريشن جنهن ۾ اسان کي دلچسپي آهي اهم معاهدو: هڪ فنڪشن جيڪو ٻنهي شرڪت ڪندڙن کي هڪجهڙائي ڏي ٿو، جنهن کي استعمال ڪري سگهجي ٿو سميٽري ڪيئي طور. تفصيل ۾ وڃڻ کان سواءِ: هر هڪ پارٽي هڪ عارضي (صرف هڪ سيشن ۾ استعمال ٿيل) اهم جوڙو (عوامي ۽ خانگي چاٻيون) ٺاهي ٿي، عوامي چابيون مٽائي، معاهدي جي فنڪشن کي سڏين، جنهن جي ان پٽ تي اهي پنهنجي نجي ڪي ۽ عوام کي پاس ڪن. ڳالهائيندڙ جي ڪنجي.

┌─────┐ ┌─────┐ │PeerA│ │PeerB│ └──┬───┘ ─┬ │ ╔══════════ ══════════╗ │───────────────────────╔──>│ ║PrvA ═ ═════════ ═══════════╝ │ IdB, PubB │ ╔══════════════════════ │<───────── ──────│ ║PrvB، PubB = DHgen()║ │ │ ╚════════════════════════════ ───┐ ╔════ ═══╦ ═══════ ════╝ │ │ │ │

ڪو به ماڻهو وچ ۾ ٽپو ڏئي سگهي ٿو ۽ عوامي چابمن کي پنهنجو پاڻ سان تبديل ڪري سگهي ٿو - هن پروٽوڪول ۾ مداخلت ڪندڙن جي ڪا به تصديق نه آهي. اچو ته هڪ دستخط شامل ڪريو ڊگهي-رهندڙ چابمن سان.

┌─────┐ ┌─────┐ │PeerA│ │PeerB│ └──┬┘──┘ ─┬ نشاني (SignPrvA، (PubA)) │ ╔═ │──────────── - ubA = لوڊ()║ │ │ ║PrvA، PubA = DHgen() ║ │ │ ╚═══════ ═══════ ════════════════ , نشان (SignPrvB, (PubB)) │ ╔═══└═══════════ ═════════════════ ─────────── ───────────── ──│ ║SignPrvB، SignPubB = لوڊ() ║ │ │ ║PrvB، PubB = D ═════════ ══════════════ ══╝ ────┐ ╔ ═════════════ ═════╗ │ │ ║تصديق ڪريو( SignPubB, ...)║ │ <───┘ ║Key = DH(Pr vA, PubB) ║ │ │ ╚═════════════════════════ ═╝ │ │ │

اهڙو دستخط ڪم نه ڪندو، ڇاڪاڻ ته اهو ڪنهن مخصوص سيشن سان ڳنڍيل ناهي. اهڙا پيغام ٻين شرڪت ڪندڙن سان گڏ سيشن لاء پڻ "مناسب" آهن. سڄو احوال سبسڪرائب ڪرڻ گهرجي. هي اسان کي مجبور ڪري ٿو ته A کان ٻيو پيغام پڻ شامل ڪريو.

اضافي طور تي، اهو ضروري آهي ته توهان جي پنهنجي سڃاڻپ ڪندڙ کي دستخط جي تحت شامل ڪيو وڃي، ڇاڪاڻ ته ٻي صورت ۾ اسان IdXXX کي تبديل ڪري سگهون ٿا ۽ پيغام کي ٻيهر سائن ان ڪري سگھون ٿا ڪنهن ٻئي ڄاڻايل ڳالهائيندڙ جي ڪنجي سان. کي روڪڻ لاءِ عڪس حملا, اهو ضروري آهي ته دستخط هيٺ عناصر واضح طور تي بيان ڪيل جڳهن ۾ انهن جي معني مطابق آهن: جيڪڏهن A نشانيون (PubA، PubB)، پوء B کي لازمي آهي (PubB، PubA). هي پڻ سيريل ٿيل ڊيٽا جي جوڙجڪ ۽ فارميٽ کي چونڊڻ جي اهميت کي ٻڌائي ٿو. مثال طور، ASN.1 DER انڪوڊنگ ۾ سيٽون ترتيب ڏنل آھن: SET OF(PubA, PubB) SET OF(PubB, PubA) سان هڪجهڙائي رکن ٿا.

┌─────┐ ┌─────┐ │PeerA│ │PeerB│ └──┬───┘ ─┬ │ ╔══════════ ════════════════╗ │───────────└───── ────────── ─────────────>│ ║SignPrvA، SignPubA = load()║ │ │ ║PrvA، PubA = DHgen ║═ ═ ═══════ ═══════════════╝ │IdB، PubB، نشان (SignPrvB، (IdB، PubA، PubB)) │ ══════════════ ═════ ════════════╗ │<─────────────────────────── ────────── ─────────│ ║SignPrvB، SignPubB = لوڊ()║ │ │ ║PrvB، PubB = DHgen() ║ │ │ ═ ═ ═ ═ ═ ═ ═ ═════════ ══════════╝ │ نشان (SignPrvA, (IdA, PubB, PubA)) │ ╔═════════════════════ ════╗ │─ ──────────────────────────────── ───>│ ║تصديق ڪريو(SignPubB, ...) ║ │ │ ║key = dh (prva, PUBB) ║ │ │ │

بهرحال، اسان اڃا تائين ”ثابت“ نه ڪيو آهي ته اسان هن سيشن لاءِ ساڳي شيئر ڪيل چيڪ ٺاهيا آهن. اصولي طور تي، اسان هن قدم کان سواء ڪري سگهون ٿا - پهريون ٽرانسپورٽ ڪنيڪشن غلط ٿي ويندو، پر اسان چاهيون ٿا ته جڏهن هٿ ملائي مڪمل ڪيو وڃي، اسان کي پڪ ٿي ويندي ته هر شيء تي اتفاق ڪيو ويو آهي. هن وقت اسان وٽ ISO/IEC IS 9798-3 پروٽوڪول هٿ تي آهي.

اسان ٺاهيل چيڪ پاڻ سائن ڪري سگهون ٿا. اهو خطرناڪ آهي، ڇاڪاڻ ته اهو ممڪن آهي ته استعمال ٿيل دستخط الورورٿم ۾ لڪ موجود هجي (جيتوڻيڪ بٽ-في-دستخط، پر اڃا تائين ليکي). اهو ممڪن آهي ته ڊيريويشن ڪي جي هڪ هيش کي سائن ڪرڻ، پر ڊريويشن ڪيل ڪي جي هيش کي به ليڪ ڪرڻ به قيمتي ٿي سگهي ٿو ڊيريويشن فنڪشن تي برٽ فورس حملي ۾. SIGMA هڪ MAC فنڪشن استعمال ڪري ٿو جيڪو موڪليندڙ جي سڃاڻپ جي تصديق ڪري ٿو.

┌─────┐ ┌─────┐ │PeerA│ │PeerB│ └──┬───┘ ─┬ │ ╔══════════ ════════════════╗ │───────────└───── ────────── ──────────────────>│ ║SignPrvA، SignPubA = load()║ │ │ ║Prv ╚ ═══════ ═════════════════════╝ │IdB، PubB، نشان (SignPrvB، (PubA ═ ═Bd))، MAC ═══ │<───────────────── ────────────── ────────── ─│ ║SignPrvB، SignPubB = لوڊ()║ │ │ ║PrvB، PubB = DHgen() ║ │ │ ╚══════════════════════ ═════════ ═- │ ║ ڪي = ڊي ايڇ ( PrvA، PubB) ║ │────────────────────────────────────── ────────── ─────>│ ║تصديق (Key, IdB) ║ │ │ ║تصديق ڪريو(سائن پب بي، ...)║ │ │ ╚════════════════════════ ═════ ═╝ │ │

هڪ اصلاح جي طور تي، ڪجهه شايد انهن جي عارضي ڪنجين کي ٻيهر استعمال ڪرڻ چاهيندا (جيڪو، يقينا، PFS لاء بدقسمتي آهي). مثال طور، اسان هڪ اهم جوڙو ٺاهيو، ڳنڍڻ جي ڪوشش ڪئي، پر TCP موجود نه هو يا پروٽوڪول جي وچ ۾ ڪٿي مداخلت ڪئي وئي. اهو هڪ نئين جوڙي تي ضايع ٿيل اينٽروپي ۽ پروسيسر وسيلن کي برباد ڪرڻ لاء شرم آهي. تنهن ڪري، اسين نام نهاد ڪوڪي متعارف ڪرائينداسين - هڪ pseudo-random value جيڪا امڪاني بي ترتيب ريپلي حملن جي خلاف حفاظت ڪندي جڏهن عارضي پبلڪ ڪيز کي ٻيهر استعمال ڪندي. ڪوڪيز ۽ ephemeral عوامي ڪيئي جي وچ ۾ پابند ٿيڻ جي ڪري، مخالف ڌر جي عوامي ڪنجي کي دستخط مان خارج ڪري سگھجي ٿو غير ضروري طور.

┌─────┐ ┌─────┐ │PeerA│ │PeerB│ └──┬┘آئي─┘ ─┬ ڪوڪي اي │ ╔════════ ══════════════════╗ │────└────└───── ────────── ──────────────────────────────── ─>│ ║SignPrvA، SignPubA = لوڊ( )║ │ │ ║PrvA، PubA = DHgen() ║ │ │ ╚═══════════════════════════ ══╝ │IdB، PubB، CookieB , sign(SignPrvB, (CookieA, CookieB, PubB)), MAC(IdB) │ ╔═══════════════════════════ ═ ╗ │< ──────────────────────────────── ────────── . │ ╚══════ ════════════════════════════ │ │ ╔══════ ═══════╗ │ نشان ( SignPrvA, (CookieB, CookieA, PubA)), MAC(IdA) │ ║Key = DH(PrvA, PubB) ║ │──────────────────────────────────── ─ ── ──────────────────────────────── ───────>│ ║ verify(Key, IdB) ║ │ │ ║تصديق (SignPubB, ...)║ │ │ ╚════════════════════════════════ │ │

آخرڪار، اسان هڪ غير فعال مبصر کان اسان جي گفتگو جي ڀائيوارن جي رازداري حاصل ڪرڻ چاهيون ٿا. ائين ڪرڻ لاءِ، SIGMA تجويز ڪري ٿو ته پھرين عارضي ڪنجين جي تبادلي ۽ ھڪڙي عام ڪنجي کي ٺاھيو جنھن تي پيغامن جي تصديق ۽ سڃاڻپ ڪرڻ لاءِ انڪرپٽ ڪجي. SIGMA ٻن اختيارن کي بيان ڪري ٿو:

  • SIGMA-I - شروعات ڪندڙ کي فعال حملن کان بچائيندو آهي، جوابدار کي غير فعال حملن کان: شروعات ڪندڙ جوابدار کي تصديق ڪندو آهي ۽ جيڪڏهن ڪا شيءِ نه ملندي آهي، ته پوءِ ان جي سڃاڻپ نه ڪندو آهي. مدعا پنهنجي سڃاڻپ ڏئي ٿو جيڪڏهن هڪ فعال پروٽوڪول هن سان شروع ڪيو ويو آهي. غير فعال مبصر ڪجهه به نه سکندو آهي؛
    SIGMA-R - جوابدار کي فعال حملن کان بچائيندو آهي، شروعات ڪندڙ کي غير فعال حملن کان. هر شيء بلڪل سامهون آهي، پر هن پروٽوڪول ۾ چار هٿ ملائڻ وارا پيغام اڳ ۾ ئي منتقل ڪيا ويا آهن.

    اسان چونڊون ٿا SIGMA-I جيئن ته اهو وڌيڪ ساڳيو آهي جيڪو اسان ڪلائنٽ-سرور کان واقف شين مان توقع ڪندا آهيون: ڪلائنٽ کي صرف تصديق ٿيل سرور جي سڃاڻپ آهي، ۽ هرڪو اڳ ۾ ئي سرور کي ڄاڻي ٿو. ان سان گڏ ان کي لاڳو ڪرڻ آسان آهي گهٽ هٿ ملائڻ واري پيغامن جي ڪري. پروٽوڪول ۾ جيڪو اسان شامل ڪيو آهي اهو پيغام جي حصي کي انڪرپٽ ڪرڻ ۽ سڃاڻپ ڪندڙ A کي آخري پيغام جي انڪريپٽ ٿيل حصي ڏانهن منتقل ڪرڻ آهي:

    PubA، ڪوڪيا │ ╔═══════════════════════════════ ─────────── ───── ────────── ───────────────── ─────────── ───── ──────>│ ║SignPrvA , SignPubA = لوڊ()║ │ │ ║PrvA، PubA = DHgen ═════════ ══════════ آر بي، مهرباني (IDB (IDB))))))) ═══════════════════════│<─└──── ────────── . ════════ ════════════════╝ │ │ ╔════════════ ══╗ │ Enc((IdA، نشاني( SignPrvA, (CookieB, CookieA, PubA)), MAC(IdA))) │ ║Key = DH(PrvA, PubB) ║ │─────────────────────────────────── ─ ────────────────── ────────────── ─────────── ──────>│ ║تصديق (Key, IdB) ║ │ │ ║تصديق (SignPubB, ...)║ │ │ ╚══════════════════════ ═════ ══╝ │ │
    
    • GOST R دستخط لاء استعمال ڪيو ويندو آهي 34.10-2012 256-bit ڪنجين سان الگورٿم.
    • عوامي ڪنجي پيدا ڪرڻ لاء، 34.10/2012/XNUMX VKO استعمال ڪيو ويندو آهي.
    • CMAC کي MAC طور استعمال ڪيو ويندو آھي. تخنيقي طور تي، هي هڪ بلاڪ سيفر جي آپريشن جو هڪ خاص طريقو آهي، جيڪو GOST R 34.13-2015 ۾ بيان ڪيو ويو آهي. هن موڊ لاءِ هڪ انڪرپشن فنڪشن جي طور تي - گراهپر (34.12-2015).
    • هن جي عوامي ڪنجي جو هيش استعمال ڪيو ويندو آهي interlocutor جي سڃاڻپ ڪندڙ. هڪ hash طور استعمال ڪيو اسٽريبوگ-256 (34.11/2012/256 XNUMX بٽ).

    هٿ ملائڻ کان پوءِ، اسان هڪ گڏيل چاٻي تي اتفاق ڪنداسين. اسان ان کي استعمال ڪري سگھون ٿا ٽرانسپورٽ پيغامن جي تصديق ٿيل انڪرپشن لاءِ. هي حصو تمام سادو آهي ۽ غلطي ڪرڻ ڏکيو آهي: اسان پيغام جي ڪائونٽر کي وڌايو، پيغام کي انڪرپٽ ڪريو، تصديق ڪريو (MAC) انسداد ۽ ciphertext، موڪليو. جڏهن هڪ پيغام ملي رهيو آهي، اسان چيڪ ڪريون ٿا ته ڪائونٽر وٽ متوقع قدر آهي، ڪاؤنٽر سان ciphertext جي تصديق ڪريو، ۽ ان کي رد ڪري ڇڏيو. هينڊ شيڪ پيغامن، ٽرانسپورٽ جا پيغام، ۽ انهن کي ڪيئن تصديق ڪرڻ لاءِ مون کي ڪهڙي ڪنجي استعمال ڪرڻ گهرجي؟ انهن سڀني ڪمن لاءِ هڪ ڪنجي استعمال ڪرڻ خطرناڪ ۽ بي عقل آهي. اهو ضروري آهي ته خاص فنڪشن استعمال ڪندي ڪنجيون ٺاهي ڪي ڊي ايف (اهم نڪتل فنڪشن). ٻيهر، اچو ته وار نه ورهايون ۽ ڪجهه ايجاد ڪيون: HKDF ڊگھي ڄاڻ ڏني وئي آھي، چڱي طرح تحقيق ڪئي وئي آھي ۽ ڪو به معلوم مسئلو نه آھي. بدقسمتي سان، اصلي Python لائبريري ۾ هي فنڪشن ناهي، تنهنڪري اسان استعمال ڪريون ٿا hkdf پلاسٽڪ جي ٿيلھي. HKDF اندروني طور استعمال ڪري ٿو ايڇ ايم اي سي، جيڪو موڙ ۾ هڪ hash فنڪشن استعمال ڪري ٿو. وڪيپيڊيا پيج تي پٿون ۾ مثال لاڳو ڪرڻ لاءِ صرف چند لائينون ڪوڊ لڳن ٿيون. جيئن ته 34.10/2012/256 جي صورت ۾، اسان استعمال ڪنداسين Stribog-XNUMX هيش فنڪشن جي طور تي. اسان جي اهم معاهدي جي ڪارڪردگي جي پيداوار کي سيشن ڪيچ سڏيو ويندو، جنهن مان غائب سميٽرڪ ٺاهيا ويندا:

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

    ڍانچو / اسڪيمون

    اچو ته ڏسون ته هي سڀ ڊيٽا منتقل ڪرڻ لاءِ اسان وٽ ڪهڙيون ASN.1 جوڙجڪ آهن:

    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)
    

    HandshakeTBS اهو آهي جيڪو دستخط ڪيو ويندو. HandshakeTBE - ڇا انڪوڊ ڪيو ويندو. مان توھان جو ڌيان MsgHandshake1 ۾ ukm فيلڊ ڏانھن ڏيان ٿو. 34.10 VKO، ٺاهيل ڪنجين جي اڃا به وڌيڪ بي ترتيب ڪرڻ لاءِ، شامل آهي UKM (يوزر ڪيئنگ مواد) پيٽرول - صرف اضافي اينٽراپي.

    ڪوڊ ۾ Cryptography شامل ڪرڻ

    اچو ته صرف اصل ڪوڊ ۾ ڪيل تبديلين تي غور ڪريون، ڇاڪاڻ ته فريم ورڪ ساڳيو ئي رهيو (حقيقت ۾، حتمي عمل پهريون ڀيرو لکيو ويو هو، ۽ پوء ان کان پوء سڀني ڪرپٽوگرافي کي ڪٽيو ويو).

    جيئن ته تصديق ۽ سڃاڻپ ڪرڻ وارن جي سڃاڻپ عوامي چابيون استعمال ڪندي ڪيو ويندو، انهن کي هاڻي هڪ ڊگهي وقت تائين محفوظ ڪرڻ جي ضرورت آهي. سادگي لاءِ، اسان استعمال ڪريون ٿا JSON هن طرح:

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

    اسان جو - اسان جو اهم جوڙو، هيڪساڊيڪل پرائيويٽ ۽ عوامي ڪنجيون. انهن جا - ڳالهائيندڙن جا نالا ۽ انهن جون عوامي چابيون. اچو ته ڪمانڊ لائن دليلن کي تبديل ڪريو ۽ 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),
        }
    # }}}
    

    34.10 الورورٿم جي خانگي چيڪ هڪ بي ترتيب نمبر آهي. 256-bit سائيز 256-bit elliptic curves لاءِ. PyGOST بائيٽ جي سيٽ سان ڪم نٿو ڪري، پر ان سان وڏو انگ، تنهنڪري اسان جي خانگي ڪي (urandom(32)) کي gost3410.prv_unmarshal() استعمال ڪندي نمبر ۾ تبديل ڪرڻ جي ضرورت آهي. عوامي ڪيچي مقرر ڪئي وئي آهي ذاتي ڪيئي مان gost3410.public_key() استعمال ڪندي. عوامي ڪي 34.10 ٻه وڏا انگ آهن جن کي بائيٽ جي ترتيب ۾ تبديل ڪرڻ جي ضرورت آهي اسٽوريج ۽ ٽرانسميشن جي آسانيءَ لاءِ gost3410.pub_marshal().

    JSON فائل پڙهڻ کان پوء، عوامي چابيون مطابق مطابق واپس تبديل ٿيڻ جي ضرورت آهي gost3410.pub_unmarshal(). جيئن ته اسان کي عوامي ڪيئي مان هڪ هيش جي صورت ۾ ڳالهائيندڙن جي سڃاڻپ ڪندڙ وصول ڪنداسين، انهن کي فوري طور تي اڳ ۾ حساب ڪري سگهجي ٿو ۽ جلدي ڳولا لاء لغت ۾ رکيل آهي. Stribog-256 hash is gost34112012256.GOST34112012256()، جيڪو مڪمل طور تي هيش افعال جي hashlib انٽرفيس کي پورو ڪري ٿو.

    شروعاتي ڪورين ڪيئن تبديل ٿي وئي آهي؟ هينڊ شيڪ اسڪيم جي مطابق سڀ ڪجهه آهي: اسان هڪ ڪوڪي ٺاهيندا آهيون (128-bit ڪافي آهي)، هڪ ephemeral key pair 34.10، جيڪو 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()
    

    • اسان جواب جو انتظار ڪريون ٿا ۽ ايندڙ پيغام کي ڊيڪوڊ ڪريون ٿا؛
    • پڪ ڪريو ته توهان هٿ ملائي 1؛
    • مخالف ڌر جي عارضي عوامي ڪنجي کي ڊيڪوڊ ڪريو ۽ سيشن جي چيڪ کي ڳڻيو؛
    • اسان پيغام جي TBE حصي کي پروسيس ڪرڻ لاءِ ضروري سميٽرڪ ڪيز ٺاهيندا آهيون.

     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 ھڪڙو 64-bit نمبر آھي (urandom(8))، جنھن کي پڻ ضرورت آھي ان جي بائيٽ جي نمائندگي کان deserialization استعمال ڪندي gost3410_vko.ukm_unmarshal(). VKO فنڪشن لاءِ 34.10/2012/256 3410-bit is gost34102012256_vko.kek_XNUMX() (KEK - encryption key).

    ٺاهيل سيشن ڪيئي اڳ ۾ ئي 256-bit pseudo-random byte sequence آهي. تنهن ڪري، اهو فوري طور تي HKDF افعال ۾ استعمال ڪري سگهجي ٿو. جيئن ته GOST34112012256 hashlib انٽرفيس کي مطمئن ڪري ٿو، ان کي فوري طور تي Hkdf ڪلاس ۾ استعمال ڪري سگهجي ٿو. اسان لوڻ جي وضاحت نٿا ڪريون (Hkdf جو پھريون دليل)، ڇو ته ٺاھيل ڪيئي، حصو وٺندڙ ڪي جوڙن جي عارضي جي ڪري، ھر سيشن لاءِ مختلف ھوندي ۽ اڳ ۾ ئي ڪافي اينٽراپي تي مشتمل ھوندي. kdf.expand() ڊفالٽ طور تي اڳ ۾ ئي 256-bit ڪنجيون ٺاهي ٿو گراس شاپر لاءِ بعد ۾.

    اڳيون، ايندڙ پيغام جا TBE ۽ TBS حصا چيڪ ڪيا ويا آهن:

    • ايندڙ ciphertext مٿان MAC ڳڻپيو ويو آهي ۽ چيڪ ڪيو ويو آهي؛
    • ciphertext decrypted آهي؛
    • TBE جوڙجڪ ڊيڪوڊ ٿيل آهي؛
    • ڳالهه ٻولهه ڪندڙ جي سڃاڻپ ان کان ورتي ويندي آهي ۽ اها جانچ ڪئي ويندي آهي ته ڇا هو اسان کي سڃاڻي ٿو.
    • هن سڃاڻپ ڪندڙ مٿان MAC ڳڻپيو ويو آهي ۽ چيڪ ڪيو ويو آهي؛
    • TBS جي ڍانچي تي دستخط جي تصديق ڪئي وئي آهي، جنهن ۾ ٻنهي ڌرين جي ڪوڪيز ۽ مخالف ڌر جي عوامي عارضي ڪنجي شامل آهي. دستخط جي تصديق ڪئي وئي آهي ڊگهي-رهندڙ دستخط جي ڪنجي ذريعي.

     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"]
    

    جيئن مون مٿي لکيو، 34.13/2015/XNUMX مختلف بيان ڪري ٿو بلاڪ سيفر آپريٽنگ موڊس کان وٺي 34.12/2015/3413. انھن مان ھڪڙو موڊ آھي تقليد داخل ڪرڻ ۽ ميڪ حساب ڪرڻ لاء. PyGOST ۾ هي gost34.12.mac(). ھن موڊ کي انڪرپشن فنڪشن پاس ڪرڻ جي ضرورت آھي (ڊيٽا جي ھڪڙي بلاڪ کي وصول ڪرڻ ۽ واپس ڪرڻ)، انڪريپشن بلاڪ جي سائيز ۽ حقيقت ۾، ڊيٽا پاڻ کي. توهان ڇو نٿا ڪري سگهو انڪرپشن بلاڪ جي سائيز کي هارڊ ڪوڊ؟ 2015/128/64 بيان ڪري ٿو نه رڳو XNUMX-bit Grasshopper cipher، پر XNUMX-bit پڻ ميگما - ھڪڙو ٿورڙو تبديل ٿيل GOST 28147-89، جيڪو واپس KGB ۾ ٺاھيو ويو آھي ۽ اڃا تائين اعليٰ ترين حفاظتي حدن مان ھڪڙو آھي.

    Kuznechik جي شروعات gost.3412.GOST3412Kuznechik(key) کي ڪال ڪندي ڪئي وئي آهي ۽ .encrypt()/.decrypt() طريقن سان هڪ شئي واپس ڪري ٿي جيڪا 34.13 افعال ڏانهن منتقل ڪرڻ لاءِ مناسب آهي. MAC جو حساب ھيٺ ڏنل آھي: gost3413.mac(GOST3412Kuznechik(key).encrypt, KUZNECHIK_BLOCKSIZE, ciphertext). ڳڻپيوڪر ۽ وصول ڪيل MAC جو مقابلو ڪرڻ لاءِ، توهان بائيٽ اسٽرنگ جي معمولي مقابلي (==) کي استعمال نٿا ڪري سگهو، ڇو ته هي آپريشن موازن جو وقت لڪي ٿو، جيڪو عام صورت ۾، موتمار نقصانن جو سبب بڻجي سگهي ٿو جهڙوڪ حيوان TLS تي حملا. پٿون وٽ ھڪڙو خاص فنڪشن آھي، hmac.compare_digest، ھن لاء.

    بلاڪ سيفر فنڪشن صرف ڊيٽا جي هڪ بلاڪ کي انڪرپٽ ڪري سگهي ٿو. وڏي تعداد لاءِ، ۽ ان کان به گھڻا ڊگھا نه، ان کي استعمال ڪرڻ ضروري آھي انڪريشن موڊ. 34.13-2015 ھيٺ ڏنل بيان ڪري ٿو: ECB، CTR، OFB، CBC، CFB. هر هڪ کي پنهنجي قابل قبول علائقن جي درخواست ۽ خاصيتون آهن. بدقسمتي سان، اسان اڃا تائين معياري نه ڪيو آهي تصديق ٿيل انڪرپشن موڊس (جهڙوڪ CCM، OCB، GCM ۽ جهڙوڪ) - اسان کي مجبور ڪيو وڃي ٿو ته گهٽ ۾ گهٽ MAC پاڻ کي شامل ڪريو. مان چونڊيو جوابي موڊ (CTR): ان کي بلاڪ جي سائيز ۾ پيڊنگ جي ضرورت ناهي، متوازي ٿي سگهي ٿو، صرف انڪرپشن فنڪشن استعمال ڪري ٿو، محفوظ طور تي وڏي تعداد ۾ پيغامن کي انڪرپٽ ڪرڻ لاءِ استعمال ڪري سگهجي ٿو (سي بي سي جي برعڪس، جنهن ۾ نسبتا جلدي ٽڪراءُ آهي).

    جهڙوڪ .mac()، .ctr() ساڳيو انپٽ وٺندو آهي: ciphertext = gost3413.ctr(GOST3412Kuznechik(key).encrypt، KUZNECHIK_BLOCKSIZE، plaintext، iv). اهو ضروري آهي ته هڪ شروعاتي ویکٹر جي وضاحت ڪئي وڃي جيڪا انڪريشن بلاڪ جي اڌ ڊگھائي آهي. جيڪڏهن اسان جي انڪرپشن ڪيئي استعمال ٿئي ٿي صرف هڪ پيغام کي انڪرپٽ ڪرڻ لاءِ (جيتوڻيڪ ڪيترن ئي بلاڪن کان)، پوءِ صفر شروعاتي ویکٹر مقرر ڪرڻ محفوظ آهي. هٿ ملائڻ واري پيغامن کي انڪرپٽ ڪرڻ لاءِ، اسين هر دفعي هڪ الڳ چيڪ استعمال ڪندا آهيون.

    دستخط جي تصديق ڪرڻ gost3410.verify() معمولي ڳالهه آهي: اسان بيضوي وکر کي پاس ڪريون ٿا جنهن جي اندر اسين ڪم ڪري رهيا آهيون (اسان صرف ان کي پنهنجي GOSTIM پروٽوڪول ۾ رڪارڊ ڪريون ٿا)، دستخط ڪندڙ جي عوامي ڪنجي (نه وساريو ته اهو ٻن جو ٽڪر هجڻ گهرجي. وڏا انگ، ۽ نه بائيٽ اسٽرنگ)، 34.11/2012/XNUMX هيش ۽ دستخط پاڻ.

    اڳيون، شروعات ڪندڙ ۾ اسان تيار ڪريون ٿا ۽ هٿ ملائڻ جو پيغام موڪليو handshake2 تي، ساڳيو عمل انجام ڏيو جيئن اسان تصديق جي دوران ڪيو، صرف سميٽري طور: چيڪ ڪرڻ بدران اسان جي چاٻين تي دستخط ڪرڻ، وغيره.

     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)
     

    جڏهن سيشن قائم ڪيو ويندو آهي، ٽرانسپورٽ ڪيز ٺاهيا ويندا آهن (انڪرپشن لاءِ هڪ الڳ چيڪ، تصديق لاءِ، هر هڪ پارٽيءَ لاءِ)، ۽ گراس شاپر کي شروع ڪيو ويندو آهي ڊيڪرپٽ ڪرڻ ۽ 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     # }}}
    

    msg_sender coroutine هاڻي پيغامن کي TCP ڪنيڪشن تي موڪلڻ کان اڳ انڪريپٽ ڪري ٿو. ھر پيغام ۾ ھڪڙي غير معمولي طور تي وڌندڙ نونس آھي، جيڪو پڻ شروعاتي ویکٹر آھي جڏھن ڪاؤنٽر موڊ ۾ انڪرپٽ ڪيو ويو آھي. هر پيغام ۽ پيغام بلاڪ جي ضمانت آهي ته هڪ مختلف انسداد قيمت آهي.

    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
    

    ايندڙ پيغامن تي عمل ڪيو ويندو آهي msg_receiver coroutine، جيڪو تصديق ۽ ڊسڪشن کي سنڀاليندو آهي:

    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)
    

    ٿڪل

    GOSTIM خاص طور تي تعليمي مقصدن لاءِ استعمال ڪرڻ جو ارادو ڪيو ويو آهي (جيئن ته اهو ٽيسٽن ۾ شامل نه آهي، گهٽ ۾ گهٽ)! پروگرام جو سورس ڪوڊ ڊائون لوڊ ڪري سگھجي ٿو هتي (Стрибог-256 хэш: 995bbd368c04e50a481d138c5fa2e43ec7c89bc77743ba8dbabee1fde45de120). Как и все мои проекты, типа GoGOST, پيڊراسن, اين اين پي سي, GoVPN، GOSTIM مڪمل طور تي آهي مفت سافٽ ويئر، ورهايل شرطن ۾ جي پي ايل 3 +.

    سرجي Matveev, cypherpunk، ميمبر SPO فائونڊيشنPython/Go ڊولپر، چيف اسپيشلسٽ FSUE "STC" Atlas.

جو ذريعو: www.habr.com

تبصرو شامل ڪريو