GOSTIM: GOST خفیہ نگاری کے ساتھ ایک شام میں P2P F2F E2EE IM

ایک ڈویلپر ہونا پیگوسٹ لائبریریاں (خالص ازگر میں GOST کرپٹوگرافک پرائمیٹوز)، مجھے اکثر سوالات موصول ہوتے ہیں کہ گھٹنے پر آسان ترین محفوظ پیغام رسانی کو کیسے نافذ کیا جائے۔ بہت سے لوگ اپلائیڈ کرپٹوگرافی کو کافی آسان سمجھتے ہیں، اور بلاک سائفر پر .encrypt() کو کال کرنا اسے مواصلاتی چینل پر محفوظ طریقے سے بھیجنے کے لیے کافی ہوگا۔ دوسروں کا خیال ہے کہ اپلائیڈ کرپٹوگرافی چند لوگوں کا مقدر ہے، اور یہ قابل قبول ہے کہ ٹیلیگرام جیسی امیر کمپنیاں اولمپیاڈ ریاضی دانوں کے ساتھ نافذ نہیں کر سکتے محفوظ پروٹوکول.

اس سب نے مجھے یہ مضمون لکھنے پر آمادہ کیا تاکہ یہ ظاہر کیا جا سکے کہ کرپٹوگرافک پروٹوکول اور محفوظ IM کو نافذ کرنا اتنا مشکل کام نہیں ہے۔ تاہم، یہ آپ کی اپنی تصدیق اور کلیدی معاہدے کے پروٹوکول کو ایجاد کرنے کے قابل نہیں ہے۔

GOSTIM: GOST خفیہ نگاری کے ساتھ ایک شام میں P2P F2F E2EE IM
مضمون لکھیں گے۔ پیر پیر, دوست سے دوست, اینڈ ٹو اینڈ انکرپٹڈ کے ساتھ فوری میسنجر سگما I تصدیق اور کلیدی معاہدے کا پروٹوکول (جس کی بنیاد پر اسے نافذ کیا جاتا ہے۔ IPsec IKE)، خصوصی طور پر GOST کرپٹوگرافک الگورتھم PyGOST لائبریری اور ASN.1 پیغام انکوڈنگ لائبریری کا استعمال کرتے ہوئے پائڈراسن (جس کے بارے میں میں پہلے ہی پہلے لکھا)۔ ایک شرط: یہ اتنا آسان ہونا چاہیے کہ اسے ایک شام (یا کام کے دن) میں شروع سے لکھا جا سکے، ورنہ یہ اب کوئی آسان پروگرام نہیں ہے۔ اس میں شاید غلطیاں، غیر ضروری پیچیدگیاں، کوتاہیاں ہیں، نیز یہ asyncio لائبریری کا استعمال کرنے والا میرا پہلا پروگرام ہے۔

آئی ایم ڈیزائن

سب سے پہلے، ہمیں یہ سمجھنے کی ضرورت ہے کہ ہمارا IM کیسا نظر آئے گا۔ سادگی کے لیے، اسے ایک ہم مرتبہ نیٹ ورک بننے دیں، بغیر کسی شرکاء کی دریافت کے۔ ہم ذاتی طور پر اشارہ کریں گے کہ کون سا پتہ: پورٹ سے رابطہ کرنے والے کے ساتھ بات چیت کرنے کے لیے۔

میں سمجھتا ہوں کہ، اس وقت، یہ مفروضہ کہ دو صوابدیدی کمپیوٹرز کے درمیان براہ راست مواصلت دستیاب ہے، عملی طور پر IM کے لاگو ہونے پر ایک اہم حد ہے۔ لیکن جتنے زیادہ ڈویلپر ہر قسم کی NAT-traversal بیساکھیوں کو لاگو کرتے ہیں، ہم اتنے ہی دیر تک IPv4 انٹرنیٹ پر رہیں گے، من مانے کمپیوٹرز کے درمیان مواصلت کے مایوس کن امکان کے ساتھ۔ آپ گھر اور کام پر IPv6 کی کمی کو کب تک برداشت کر سکتے ہیں؟

ہمارے پاس ایک دوست سے دوست کا نیٹ ورک ہوگا: تمام ممکنہ بات چیت کرنے والوں کو پہلے سے معلوم ہونا چاہیے۔ سب سے پہلے، یہ ہر چیز کو بہت آسان بنا دیتا ہے: ہم نے اپنا تعارف کرایا، نام/کلید پایا یا نہیں پایا، منقطع ہو گئے یا کام جاری رکھا، بات چیت کرنے والے کو جانتے ہوئے۔ دوم، عام طور پر، یہ محفوظ ہے اور بہت سے حملوں کو ختم کرتا ہے۔

IM انٹرفیس کلاسک حل کے قریب ہوگا۔ بیکار منصوبوں، جو میں واقعی میں ان کی minimalism اور یونکس وے فلسفہ کے لئے پسند کرتا ہوں۔ IM پروگرام ہر انٹرلوکیوٹر کے لیے تین یونکس ڈومین ساکٹ کے ساتھ ایک ڈائرکٹری بناتا ہے:

  • بات چیت کرنے والے کو بھیجے گئے پیغامات اس میں ریکارڈ کیے جاتے ہیں۔
  • باہر - بات چیت کرنے والے سے موصول ہونے والے پیغامات اس سے پڑھے جاتے ہیں؛
  • ریاست - اس سے پڑھ کر، ہمیں پتہ چلتا ہے کہ آیا بات چیت کرنے والا فی الحال منسلک ہے، کنکشن کا پتہ/پورٹ۔

اس کے علاوہ، میزبان پورٹ کو لکھ کر ایک کون ساکٹ بنایا جاتا ہے جس میں ہم ریموٹ انٹرلوکیوٹر سے کنکشن شروع کرتے ہیں۔

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

یہ نقطہ نظر آپ کو IM ٹرانسپورٹ اور یوزر انٹرفیس کے آزادانہ نفاذ کی اجازت دیتا ہے، کیونکہ کوئی دوست نہیں ہے، آپ سب کو خوش نہیں کر سکتے۔ استعمال کرنا tmux اور / یا۔ ملٹیل، آپ نحو کو نمایاں کرنے کے ساتھ ملٹی ونڈو انٹرفیس حاصل کرسکتے ہیں۔ اور مدد سے rlwrap آپ GNU ریڈ لائن سے مطابقت رکھنے والی میسج ان پٹ لائن حاصل کر سکتے ہیں۔

درحقیقت، سکل لیس پروجیکٹس FIFO فائلیں استعمال کرتے ہیں۔ ذاتی طور پر، میں سمجھ نہیں سکتا تھا کہ فائلوں کے ساتھ مسابقتی طور پر کیسے کام کیا جائے بغیر ہاتھ سے لکھے گئے پس منظر کے بغیر سرشار دھاگوں سے (میں ایک طویل عرصے سے ایسی چیزوں کے لیے زبان استعمال کر رہا ہوں Go)۔ لہذا، میں نے یونکس ڈومین ساکٹ کے ساتھ کرنے کا فیصلہ کیا۔ بدقسمتی سے، اس سے echo 2001:470:dead::babe 6666 > conn کرنا ناممکن ہو جاتا ہے۔ میں نے اس مسئلے کو استعمال کرکے حل کیا۔ socat: echo 2001:470:dead::bebe 6666 | socat - UNIX-CONNECT:conn، socat READLINE UNIX-CONNECT:alice/in۔

اصل غیر محفوظ پروٹوکول

TCP کو ٹرانسپورٹ کے طور پر استعمال کیا جاتا ہے: یہ ترسیل اور اس کے آرڈر کی ضمانت دیتا ہے۔ UDP اس بات کی ضمانت نہیں دیتا ہے (جو خفیہ نگاری کے استعمال کے وقت کارآمد ہوگا)، بلکہ سپورٹ کرتا ہے۔ ایس سی ٹی پی ازگر باکس سے باہر نہیں آتا۔

بدقسمتی سے، TCP میں پیغام کا کوئی تصور نہیں ہے، صرف بائٹس کا ایک سلسلہ ہے۔ اس لیے ضروری ہے کہ پیغامات کے لیے ایک فارمیٹ سامنے لایا جائے تاکہ وہ اس تھریڈ میں آپس میں شیئر کیے جا سکیں۔ ہم لائن فیڈ کریکٹر استعمال کرنے پر راضی ہو سکتے ہیں۔ یہ شروع کرنے والوں کے لیے ٹھیک ہے، لیکن ایک بار جب ہم اپنے پیغامات کو خفیہ کرنا شروع کر دیتے ہیں، تو یہ حرف سائفر ٹیکسٹ میں کہیں بھی ظاہر ہو سکتا ہے۔ نیٹ ورکس میں، اس لیے، مقبول پروٹوکول وہ ہیں جو پہلے پیغام کی لمبائی بائٹس میں بھیجتے ہیں۔ مثال کے طور پر، باکس کے باہر ازگر میں xdrlib ہے، جو آپ کو اسی طرح کی شکل کے ساتھ کام کرنے کی اجازت دیتا ہے۔ XDR.

ہم TCP پڑھنے کے ساتھ صحیح اور مؤثر طریقے سے کام نہیں کریں گے - ہم کوڈ کو آسان بنائیں گے۔ ہم ساکٹ سے ڈیٹا کو لامتناہی لوپ میں پڑھتے ہیں جب تک کہ ہم مکمل پیغام کو ڈی کوڈ نہ کر لیں۔ XML کے ساتھ JSON بھی اس نقطہ نظر کے لیے فارمیٹ کے طور پر استعمال کیا جا سکتا ہے۔ لیکن جب کرپٹوگرافی کو شامل کیا جاتا ہے، تو ڈیٹا پر دستخط اور تصدیق کرنی ہوگی - اور اس کے لیے اشیاء کی بائٹ فی بائٹ ایک جیسی نمائندگی کی ضرورت ہوگی، جو JSON/XML فراہم نہیں کرتا ہے (ڈمپ کے نتائج مختلف ہو سکتے ہیں)۔

XDR اس کام کے لیے موزوں ہے، تاہم میں DER انکوڈنگ کے ساتھ ASN.1 کا انتخاب کرتا ہوں اور پائڈراسن لائبریری، چونکہ ہمارے پاس اعلیٰ سطحی اشیاء ہوں گی جن کے ساتھ کام کرنا اکثر زیادہ خوشگوار اور آسان ہوتا ہے۔ اسکیم لیس کے برعکس بین کوڈ, میسج پیک یا سی بی او آر، 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│ └──┬┘──┘ ── آئی ڈی اے) │ │───────── ─────│ │ │ MsgText() │ │──── MsgText() │ │ │

خفیہ نگاری کے بغیر IM

جیسا کہ میں نے پہلے ہی کہا ہے، 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(","))

اپنا نام مقرر کریں (--ہمارا نام ایلس)۔ تمام متوقع بات چیت کرنے والوں کی فہرست کوما سے الگ کی گئی ہے (—ان کے نام باب، حوا)۔ بات چیت کرنے والوں میں سے ہر ایک کے لیے، یونکس ساکٹ کے ساتھ ایک ڈائرکٹری بنائی جاتی ہے، ساتھ ہی ساتھ ہر ایک کے اندر، باہر، حالت کے لیے ایک کورٹین:

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 سٹرنگز رکھتی ہے، لیکن 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 с IKE - سنگین خفیہ نگاری کے مسائل نہیں ہیں، حالانکہ وہ سادہ بھی نہیں ہیں۔ اگر آپ IKEv1 اور IKEv2 کے بارے میں پڑھتے ہیں، تو ان کا ماخذ ہے۔ STS، ISO/IEC IS 9798-3 اور SIGMA (SIGn-and-MAc) پروٹوکول - ایک شام میں لاگو کرنے کے لئے کافی آسان۔

SIGMA کے بارے میں کیا اچھا ہے، جیسا کہ STS/ISO پروٹوکول کی ترقی میں تازہ ترین لنک ہے؟ یہ ہماری تمام ضروریات کو پورا کرتا ہے (بشمول "چھپانے والے" انٹرلوکیوٹر شناخت کنندگان) اور اس میں کوئی معلوم خفیہ مسئلہ نہیں ہے۔ یہ کم سے کم ہے - پروٹوکول پیغام سے کم از کم ایک عنصر کو ہٹانا اس کے عدم تحفظ کا باعث بنے گا۔

آئیے سب سے آسان گھریلو پروٹوکول سے SIGMA پر جائیں۔ سب سے بنیادی آپریشن جس میں ہم دلچسپی رکھتے ہیں۔ اہم معاہدہ: ایک فنکشن جو دونوں شرکاء کو یکساں قیمت دیتا ہے، جسے ہم آہنگ کلید کے طور پر استعمال کیا جا سکتا ہے۔ تفصیلات میں جانے کے بغیر: فریقین میں سے ہر ایک عارضی (صرف ایک سیشن کے اندر استعمال کیا جاتا ہے) کلیدی جوڑا (عوامی اور نجی چابیاں) تیار کرتا ہے، عوامی چابیاں کا تبادلہ کرتا ہے، معاہدے کے فنکشن کو کال کرتا ہے، جس کے ان پٹ کو وہ اپنی نجی کلید اور عوام کو پاس کرتے ہیں۔ بات چیت کرنے والے کی کلید

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

کوئی بھی درمیان میں چھلانگ لگا سکتا ہے اور عوامی چابیاں اپنی مرضی سے بدل سکتا ہے - اس پروٹوکول میں بات چیت کرنے والوں کی کوئی توثیق نہیں ہے۔ آئیے طویل المدت چابیاں کے ساتھ ایک دستخط شامل کریں۔

┌─────┐ ┌─────┐ │PeerA│ │PeerB│ └──┬┘A─┘ ── نشان (SignPrvA, (PubA)) │ ╔═ │──────────── - ubA = load()║ │ │ ║PrvA، PubA = DHgen() ║ │ │ ╚══════════════════════════════════ , sign(SignPrvB, (PubB)) │ ╔═══└═══════════ ═════════════════════ ─────────── ───────────── ──│ ║SignPrvB، SignPubB = load( )║ │ │ ║PrvB، PubB = DH ═════════ ══════════════ ══╝ ────┐ ╔ ═══════════════ ═════╗ │ │ ║تصدیق کریں( 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│ └──┬┘ I─┘ ── │ ╔══════════ ═════════════════╗ │────────────────────── ────────── ─────────────>│ ║SignPrvA، SignPubA = load()║ │ │ ║PrvA، PubA = DHgen ⚕┕ ─── ═ ═══════ ═══════════════╝ │IdB، PubB، نشان (SignPrvB, (IdB, PubA, PubB)) │ ═════════════ ═════ ════════════╗ │<─────────────────────────── ────────── ─────────│ ║SignPrvB، SignPubB = load()║ │ │ ║PrvB، PubB = DHgen() ║ │ │ ═ ═ ═ ═ ═ ════════ ══════════╝ │ نشان (SignPrvA, (IdA, PubB, PubA)) │ ╔════════════════════ ════╗ │─ ─────────────────────────────────── ───>│ ║تصدیق کریں(SignPubB، ...) ║ │ │ ║key = dh (prva, PUBB) ║ │ │ │

تاہم، ہم نے ابھی تک "ثابت" نہیں کیا ہے کہ ہم نے اس سیشن کے لیے ایک ہی مشترکہ کلید تیار کی ہے۔ اصولی طور پر، ہم اس قدم کے بغیر بھی کر سکتے ہیں - پہلا ٹرانسپورٹ کنکشن غلط ہو جائے گا، لیکن ہم چاہتے ہیں کہ جب مصافحہ مکمل ہو جائے، تو ہمیں یقین ہو جائے گا کہ واقعی ہر چیز پر اتفاق ہے۔ اس وقت ہمارے پاس ISO/IEC IS 9798-3 پروٹوکول موجود ہے۔

ہم خود تیار کردہ کلید پر دستخط کر سکتے ہیں۔ یہ خطرناک ہے، کیونکہ یہ ممکن ہے کہ استعمال شدہ دستخطی الگورتھم میں لیک ہو سکتی ہے (اگرچہ بٹس فی دستخط، لیکن پھر بھی لیک ہو رہا ہے)۔ اخذ کردہ کلید کے ہیش پر دستخط کرنا ممکن ہے، لیکن اخذ کردہ کلید کے ہیش کو بھی لیک کرنا اخذ کرنے کے فنکشن پر بروٹ فورس حملے میں قیمتی ثابت ہو سکتا ہے۔ SIGMA ایک MAC فنکشن استعمال کرتا ہے جو بھیجنے والے کی شناخت کی تصدیق کرتا ہے۔

┌─────┐ ┌─────┐ │PeerA│ │PeerB│ └──┬┘ I─┘ ── │ ╔══════════ ═════════════════╗ │────────────────────── ────────── ──────────────────>│ ║SignPrvA، SignPubA = load()║ │ │ ╂Prv()║ │ │ ║Prv() ╚ ═══════ ═- ═══ │<───────────────── ─────────────────── ────────── ─│ ║SignPrvB، SignPubB = load()║ │ │ ║PrvB، PubB = DHgen() ║ │ │ ╚════════════════════ ═════════ - │ ║Key = DH( PrvA، PubB) ║ │───────────────────────────────────── ────────── ─────>│ ║تصدیق (کلید، IDB) ║ │ │ ║تصدیق (سائن پب بی، ...)║ │ │ ╚══════════════════════ ═════ ═╝ │ │

ایک اصلاح کے طور پر، کچھ لوگ اپنی عارضی چابیاں دوبارہ استعمال کرنا چاہتے ہیں (جو یقیناً PFS کے لیے بدقسمتی کی بات ہے)۔ مثال کے طور پر، ہم نے ایک کلیدی جوڑا بنایا، رابطہ قائم کرنے کی کوشش کی، لیکن TCP دستیاب نہیں تھا یا پروٹوکول کے بیچ میں کہیں رکاوٹ بنی تھی۔ ایک نئی جوڑی پر ضائع شدہ اینٹروپی اور پروسیسر کے وسائل کو ضائع کرنا شرم کی بات ہے۔ لہذا، ہم نام نہاد کوکی متعارف کرائیں گے - ایک چھدم بے ترتیب قدر جو عارضی پبلک کیز کو دوبارہ استعمال کرتے وقت ممکنہ رینڈم ری پلے حملوں سے بچائے گی۔ کوکی اور عارضی عوامی کلید کے درمیان پابند ہونے کی وجہ سے، مخالف فریق کی عوامی کلید کو غیر ضروری سمجھ کر دستخط سے ہٹایا جا سکتا ہے۔

┌─────┐ ┌─────┐ │PeerA│ │PeerB│ └──┬┘ I─┘ ──, P CookieA │ ╔════════ ═════└═════════════╗ │─────────────── ────────── ─────────────────────────────────── ─>│ ║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-I - شروع کرنے والے کو فعال حملوں سے، جواب دہندہ کو غیر فعال حملوں سے بچاتا ہے: شروع کرنے والا جواب دہندہ کی تصدیق کرتا ہے اور اگر کوئی چیز مماثل نہیں ہے، تو وہ اپنی شناخت نہیں بتاتا۔ مدعا علیہ اپنی شناخت بتاتا ہے اگر اس کے ساتھ ایک فعال پروٹوکول شروع کیا جاتا ہے۔ غیر فعال مبصر کچھ نہیں سیکھتا۔
    SIGMA-R - جواب دہندہ کو فعال حملوں سے بچاتا ہے، شروع کرنے والے کو غیر فعال حملوں سے۔ سب کچھ اس کے بالکل برعکس ہے لیکن اس پروٹوکول میں چار مصافحہ کے پیغامات پہلے ہی منتقل ہو چکے ہیں۔

    ہم SIGMA-I کا انتخاب کرتے ہیں کیونکہ یہ کلائنٹ-سرور سے واقف چیزوں سے ہماری توقع سے زیادہ مشابہت رکھتا ہے: کلائنٹ کو صرف تصدیق شدہ سرور سے پہچانا جاتا ہے، اور ہر کوئی پہلے سے ہی سرور کو جانتا ہے۔ نیز مصافحہ کے کم پیغامات کی وجہ سے اسے نافذ کرنا آسان ہے۔ ہم پروٹوکول میں صرف پیغام کے کچھ حصے کو خفیہ کرنا اور شناخت کنندہ A کو آخری پیغام کے خفیہ کردہ حصے میں منتقل کرنا ہے۔

    PubA، CookieA │ ╔══════════════════════════════════════════════ ─────────── ───── ────────── ───────────────────── ─────────── ───── ──────>│ ║SignPrvA , SignPubA = load()║ │ │ ║PrvA، PubA = DHgen () ║ ╂ ┕ ┕ ─ DHgen ═════════ ═════════ ════╝ │ PUBB ، کوکیئب ، ENC ((IDB ، SING (SINCPRVB ، (کوکیئیہ ، کوکیئب ، PUBB)) ، میک (IDB))) │ ╔═════ ═══════════════ ═══════╗ │<─└────────── ────────── + ════════ ════════════════╝ │ │ ╔═══════════════ ══╗ │ Enc((IdA، نشان ( SignPrvA, (CookieB, CookieA, PubA)), MAC(IdA))) │ ║Key = DH(PrvA, PubB) ║ │─────────────────────────────── ─ ────────────────── ───────────────── ─────────── ──────>│ ║تصدیق کریں(Key, IdB) ║ │ │ ║تصدیق (SignPubB, ...)║ │ │ ╚══════════════════════ ═════ ══╝ │ │
    
    • GOST R دستخط کے لیے استعمال ہوتا ہے۔ 34.10-2012 256 بٹ کیز کے ساتھ الگورتھم۔
    • عوامی کلید بنانے کے لیے، 34.10/2012/XNUMX VKO استعمال کیا جاتا ہے۔
    • CMAC کو MAC کے طور پر استعمال کیا جاتا ہے۔ تکنیکی طور پر، یہ بلاک سائفر کے آپریشن کا ایک خاص موڈ ہے، جو GOST R 34.13-2015 میں بیان کیا گیا ہے۔ اس موڈ کے لیے ایک انکرپشن فنکشن کے طور پر - گھاس باز (34.12 2015).
    • اس کی عوامی کلید کا ہیش انٹرلوکیوٹر کے شناخت کنندہ کے طور پر استعمال ہوتا ہے۔ ہیش کے طور پر استعمال کیا جاتا ہے۔ Stribog-256 (34.11/2012/256 XNUMX بٹس)۔

    مصافحہ کے بعد، ہم مشترکہ کلید پر متفق ہوں گے۔ ہم اسے نقل و حمل کے پیغامات کی تصدیق شدہ خفیہ کاری کے لیے استعمال کر سکتے ہیں۔ یہ حصہ بہت آسان اور غلطی کرنا مشکل ہے: ہم میسج کاؤنٹر کو بڑھاتے ہیں، میسج کو انکرپٹ کرتے ہیں، کاؤنٹر کی توثیق (MAC) کرتے ہیں اور سائفر ٹیکسٹ بھیجتے ہیں۔ جب کوئی پیغام موصول ہوتا ہے، تو ہم چیک کرتے ہیں کہ کاؤنٹر کی متوقع قدر ہے، کاؤنٹر کے ساتھ سائفر ٹیکسٹ کی توثیق کریں، اور اسے ڈکرپٹ کریں۔ ہینڈ شیک پیغامات، نقل و حمل کے پیغامات، اور ان کی توثیق کرنے کے لیے مجھے کون سی کلید استعمال کرنی چاہیے؟ ان تمام کاموں کے لیے ایک چابی کا استعمال خطرناک اور غیر دانشمندانہ ہے۔ خصوصی افعال کا استعمال کرتے ہوئے چابیاں پیدا کرنا ضروری ہے۔ کے ڈی ایف (کلیدی اخذ فنکشن)۔ ایک بار پھر، آئیے بالوں کو تقسیم نہ کریں اور کچھ ایجاد کریں: HKDF طویل عرصے سے جانا جاتا ہے، اچھی طرح سے تحقیق کی جاتی ہے اور کوئی معلوم مسئلہ نہیں ہے. بدقسمتی سے، مقامی ازگر کی لائبریری میں یہ فنکشن نہیں ہے، لہذا ہم استعمال کرتے ہیں۔ hkdf پلاسٹک بیگ. HKDF اندرونی طور پر استعمال کرتا ہے۔ ایچ ایم اے سی۔، جو بدلے میں ایک ہیش فنکشن استعمال کرتا ہے۔ ویکیپیڈیا صفحہ پر ازگر میں ایک مثال کے نفاذ کے لیے کوڈ کی صرف چند لائنیں لگتی ہیں۔ جیسا کہ 34.10/2012/256 کے معاملے میں ہے، ہم اسٹرائبوگ-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)
    

    ہینڈ شیک ٹی بی ایس پر دستخط کیے جائیں گے۔ HandshakeTBE - کیا خفیہ کیا جائے گا. میں آپ کی توجہ MsgHandshake1 میں ukm فیلڈ کی طرف مبذول کرواتا ہوں۔ 34.10 VKO، جنریٹڈ کیز کے اور بھی زیادہ بے ترتیب ہونے کے لیے، UKM (یوزر کینگ میٹریل) پیرامیٹر شامل کرتا ہے - صرف اضافی اینٹروپی۔

    کوڈ میں خفیہ نگاری شامل کرنا

    آئیے صرف اصل کوڈ میں کی گئی تبدیلیوں پر غور کریں، کیونکہ فریم ورک ایک ہی رہا (دراصل، حتمی نفاذ پہلے لکھا گیا تھا، اور پھر اس میں سے تمام خفیہ نگاری کاٹ دی گئی تھی)۔

    چونکہ بات چیت کرنے والوں کی توثیق اور شناخت عوامی کلیدوں کے ذریعے کی جائے گی، اس لیے انہیں اب کہیں زیادہ دیر تک ذخیرہ کرنے کی ضرورت ہے۔ سادگی کے لیے، ہم 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 بٹ بیضوی منحنی خطوط کے لیے 256 بٹ سائز۔ PyGOST بائٹس کے سیٹ کے ساتھ کام نہیں کرتا، لیکن اس کے ساتھ بڑی تعداد، لہذا ہماری نجی کلید (urandom(32)) کو gost3410.prv_unmarshal() کا استعمال کرتے ہوئے ایک نمبر میں تبدیل کرنے کی ضرورت ہے۔ عوامی کلید کا تعین نجی کلید سے gost3410.public_key() کا استعمال کرتے ہوئے کیا جاتا ہے۔ عوامی کلید 34.10 دو بڑی تعدادیں ہیں جنہیں gost3410.pub_marshal() کا استعمال کرتے ہوئے اسٹوریج اور ٹرانسمیشن میں آسانی کے لیے بائٹ کی ترتیب میں تبدیل کرنے کی بھی ضرورت ہے۔

    JSON فائل کو پڑھنے کے بعد، اس کے مطابق عوامی کلیدوں کو gost3410.pub_unmarshal() کا استعمال کرتے ہوئے واپس تبدیل کرنے کی ضرورت ہے۔ چونکہ ہمیں عوامی کلید سے ہیش کی شکل میں بات چیت کرنے والوں کے شناخت کنندگان موصول ہوں گے، اس لیے ان کا فوری طور پر پہلے سے حساب لگایا جا سکتا ہے اور فوری تلاش کے لیے ایک لغت میں رکھا جا سکتا ہے۔ Stribog-256 hash gost34112012256.GOST34112012256() ہے، جو ہیش فنکشنز کے hashlib انٹرفیس کو مکمل طور پر مطمئن کرتا ہے۔

    شروع کرنے والے کوروٹین میں کیسے تبدیلی آئی ہے؟ سب کچھ ہینڈ شیک اسکیم کے مطابق ہے: ہم ایک کوکی تیار کرتے ہیں (128 بٹ کافی ہے)، ایک مختصر کلیدی جوڑا 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 بٹ نمبر ہے (urandom(8))، جو gost3410_vko.ukm_unmarshal() کا استعمال کرتے ہوئے اپنی بائٹ نمائندگی سے ڈی سیریلائزیشن کی بھی ضرورت ہے۔ VKO فنکشن برائے 34.10/2012/256 3410-bit is gost34102012256_vko.kek_XNUMX() (KEK - انکرپشن کلید)۔

    تیار کردہ سیشن کی پہلے سے ہی ایک 256 بٹ سیوڈو رینڈم بائٹ ترتیب ہے۔ لہذا، یہ فوری طور پر HKDF افعال میں استعمال کیا جا سکتا ہے. چونکہ GOST34112012256 hashlib انٹرفیس کو مطمئن کرتا ہے، اس لیے اسے Hkdf کلاس میں فوری طور پر استعمال کیا جا سکتا ہے۔ ہم نمک (Hkdf کی پہلی دلیل) کی وضاحت نہیں کرتے ہیں، کیونکہ تیار کردہ کلید، حصہ لینے والے کلیدی جوڑوں کی عارضی حیثیت کی وجہ سے، ہر سیشن کے لیے مختلف ہوگی اور پہلے ہی کافی اینٹروپی پر مشتمل ہوگی۔ kdf.expand() بطور ڈیفالٹ پہلے ہی 256 بٹ کیز تیار کرتا ہے جو بعد میں گراس شاپر کے لیے درکار ہے۔

    اگلا، آنے والے پیغام کے TBE اور TBS حصوں کو چیک کیا جاتا ہے:

    • آنے والے سائفر ٹیکسٹ پر MAC کا حساب لگایا اور چیک کیا جاتا ہے۔
    • سائفر ٹیکسٹ کو ڈکرپٹ کیا گیا ہے۔
    • 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 بٹ گراس شاپر سائفر بلکہ XNUMX بٹ کو بھی بیان کرتا ہے۔ میگما - ایک قدرے ترمیم شدہ 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): اسے بلاک سائز میں پیڈنگ کی ضرورت نہیں ہے، متوازی کیا جا سکتا ہے، صرف انکرپشن فنکشن استعمال کرتا ہے، بڑی تعداد میں پیغامات کو خفیہ کرنے کے لیے محفوظ طریقے سے استعمال کیا جا سکتا ہے (CBC کے برعکس، جس میں نسبتاً تیزی سے تصادم ہوتا ہے)۔

    .mac() کی طرح، .ctr() بھی اسی طرح کا ان پٹ لیتا ہے: ciphertext = gost3413.ctr(GOST3412Kuznechik(key).encrypt، KUZNECHIK_BLOCKSIZE، سادہ متن، iv)۔ یہ ایک ابتدائی ویکٹر کی وضاحت کرنے کی ضرورت ہے جو انکرپشن بلاک کی لمبائی کے بالکل نصف ہے۔ اگر ہماری انکرپشن کلید صرف ایک پیغام کو خفیہ کرنے کے لیے استعمال کی جاتی ہے (بہت سے بلاکس کے باوجود)، تو صفر شروع کرنے والا ویکٹر سیٹ کرنا محفوظ ہے۔ ہینڈ شیک پیغامات کو خفیہ کرنے کے لیے، ہم ہر بار ایک الگ کلید استعمال کرتے ہیں۔

    دستخط gost3410.verify() کی تصدیق کرنا معمولی بات ہے: ہم بیضوی وکر سے گزرتے ہیں جس کے اندر ہم کام کر رہے ہیں (ہم اسے صرف اپنے GOSTIM پروٹوکول میں ریکارڈ کرتے ہیں)، دستخط کنندہ کی عوامی کلید (یہ نہ بھولیں کہ یہ دو کا مجموعہ ہونا چاہیے۔ بڑی تعداد، اور بائٹ سٹرنگ نہیں)، 34.11/2012/XNUMX ہیش اور خود دستخط۔

    اس کے بعد، انیشی ایٹر میں ہم ہینڈ شیک 2 کو ہینڈ شیک میسج تیار کرتے ہیں اور بھیجتے ہیں، وہی اعمال انجام دیتے ہیں جو ہم نے تصدیق کے دوران کیے تھے، صرف ہم آہنگی سے: چیک کرنے کے بجائے اپنی کلیدوں پر دستخط کرنا، وغیرہ۔

     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)
     

    جب سیشن قائم ہو جاتا ہے، نقل و حمل کی چابیاں تیار کی جاتی ہیں (انکرپشن کے لیے ایک علیحدہ کلید، تصدیق کے لیے، ہر فریق کے لیے)، اور گراس شاپر کو ڈیکرپٹ اور میک کو چیک کرنے کے لیے شروع کیا جاتا ہے:

     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 کورٹین اب پیغامات کو 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 مکمل طور پر ہے۔ مفت سافٹ ویئر، شرائط کے تحت تقسیم کیا گیا۔ GPLv3 +.

    سرگئی ماتویف, سائپرپنک، رکن ایس پی او فاؤنڈیشن, Python/Go ڈویلپر، چیف ماہر FSUE "STC "اٹلس".

ماخذ: www.habr.com

نیا تبصرہ شامل کریں