GOSTIM: P2P F2F E2EE IM Π·Π° ΠΎΠ΄ΠΈΠ½ Π²Π΅Ρ‡Π΅Ρ€ с Π“ΠžΠ‘Π’-ΠΊΡ€ΠΈΠΏΡ‚ΠΎΠ³Ρ€Π°Ρ„ΠΈΠ΅ΠΉ

Π‘ΡƒΠ΄ΡƒΡ‡ΠΈ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΎΠΌ PyGOST Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ (Π“ΠžΠ‘Π’ΠΎΠ²Ρ‹Π΅ криптографичСскиС ΠΏΡ€ΠΈΠΌΠΈΡ‚ΠΈΠ²Ρ‹ Π½Π° чистом Python), я Π½Π΅Ρ€Π΅Π΄ΠΊΠΎ ΠΏΠΎΠ»ΡƒΡ‡Π°ΡŽ вопросы ΠΎ Ρ‚ΠΎΠΌ ΠΊΠ°ΠΊ Π½Π° ΠΊΠΎΠ»Π΅Π½ΠΊΠ΅ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΡΡ‚Π΅ΠΉΡˆΠΈΠΉ бСзопасный ΠΎΠ±ΠΌΠ΅Π½ сообщСниями. МногиС ΡΡ‡ΠΈΡ‚Π°ΡŽΡ‚ ΠΏΡ€ΠΈΠΊΠ»Π°Π΄Π½ΡƒΡŽ ΠΊΡ€ΠΈΠΏΡ‚ΠΎΠ³Ρ€Π°Ρ„ΠΈΡŽ достаточно простой ΡˆΡ‚ΡƒΠΊΠΎΠΉ, ΠΈ .encrypt() Π²Ρ‹Π·ΠΎΠ²Π° Ρƒ Π±Π»ΠΎΡ‡Π½ΠΎΠ³ΠΎ ΡˆΠΈΡ„Ρ€Π° Π±ΡƒΠ΄Π΅Ρ‚ достаточно для бСзопасной отсылки ΠΏΠΎ ΠΊΠ°Π½Π°Π»Ρƒ связи. Π”Ρ€ΡƒΠ³ΠΈΠ΅ ΠΆΠ΅ ΡΡ‡ΠΈΡ‚Π°ΡŽΡ‚, Ρ‡Ρ‚ΠΎ прикладная криптография β€” ΡƒΠ΄Π΅Π» Π½Π΅ΠΌΠ½ΠΎΠ³ΠΈΡ…, ΠΈ ΠΏΡ€ΠΈΠ΅ΠΌΠ»Π΅ΠΌΠΎ, Ρ‡Ρ‚ΠΎ Π±ΠΎΠ³Π°Ρ‚Ρ‹Π΅ ΠΊΠΎΠΌΠΏΠ°Π½ΠΈΠΈ Ρ‚ΠΈΠΏΠ° Telegram с ΠΎΠ»ΠΈΠΌΠΏΠΈΠ°Π΄Π½ΠΈΠΊΠ°ΠΌΠΈ-ΠΌΠ°Ρ‚Π΅ΠΌΠ°Ρ‚ΠΈΠΊΠ°ΠΌΠΈ Π½Π΅ ΠΌΠΎΠ³ΡƒΡ‚ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ бСзопасный ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ».

Всё это ΠΏΠΎΠ±ΡƒΠ΄ΠΈΠ»ΠΎ мСня Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ Π΄Π°Π½Π½ΡƒΡŽ ΡΡ‚Π°Ρ‚ΡŒΡŽ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ, Ρ‡Ρ‚ΠΎ рСализация криптографичСских ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»ΠΎΠ² ΠΈ бСзопасного IM-Π° Π½Π΅ такая слоТная Π·Π°Π΄Π°Ρ‡Π°. Однако, ΠΈΠ·ΠΎΠ±Ρ€Π΅Ρ‚Π°Ρ‚ΡŒ собствСнныС ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Ρ‹ Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ ΠΈ согласования ΠΊΠ»ΡŽΡ‡Π΅ΠΉ Π½Π΅ стоит.

GOSTIM: P2P F2F E2EE IM Π·Π° ΠΎΠ΄ΠΈΠ½ Π²Π΅Ρ‡Π΅Ρ€ с Π“ΠžΠ‘Π’-ΠΊΡ€ΠΈΠΏΡ‚ΠΎΠ³Ρ€Π°Ρ„ΠΈΠ΅ΠΉ
Π’ ΡΡ‚Π°Ρ‚ΡŒΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ написан peer-to-peer, friend-to-friend, end-to-end Π·Π°ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°Π½Π½Ρ‹ΠΉ instant messenger с SIGMA-I ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»ΠΎΠΌ Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ ΠΈ согласования ΠΊΠ»ΡŽΡ‡Π΅ΠΉ (Π½Π° Π±Π°Π·Π΅ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½ IPsec IKE), ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ ΠΈΡΠΊΠ»ΡŽΡ‡ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ Π“ΠžΠ‘Π’ΠΎΠ²Ρ‹Π΅ криптографичСскиС Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΡ‹ PyGOST Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ ΠΈ ASN.1 ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ сообщСний Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΎΠΉ PyDERASN (ΠΏΡ€ΠΎ ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ я ΡƒΠΆΠ΅ писал Ρ€Π°Π½ΡŒΡˆΠ΅). НСобходимоС условиС: ΠΎΠ½ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ Π½Π°ΡΡ‚ΠΎΠ»ΡŒΠΊΠΎ прост, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π΅Π³ΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π±Ρ‹Π»ΠΎ Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ с нуля Π·Π° ΠΎΠ΄ΠΈΠ½ Π²Π΅Ρ‡Π΅Ρ€ (ΠΈΠ»ΠΈ Ρ€Π°Π±ΠΎΡ‡ΠΈΠΉ дСнь), ΠΈΠ½Π°Ρ‡Π΅ это ΡƒΠΆΠ΅ Π½Π΅ простая ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ°. Π’ Π½Π΅ΠΉ навСрняка Π΅ΡΡ‚ΡŒ ошибки, излишниС слоТности, Π½Π΅Π΄ΠΎΡ‡Ρ‘Ρ‚Ρ‹, плюс это моя пСрвая ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ° с использованиСм asyncio Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ.

Π”ΠΈΠ·Π°ΠΉΠ½ IM

Для Π½Π°Ρ‡Π°Π»Π°, Π½Π°Π΄ΠΎ ΠΏΠΎΠ½ΡΡ‚ΡŒ ΠΊΠ°ΠΊ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹Π³Π»ΡΠ΄Π΅Ρ‚ΡŒ наш IM. Для простоты, пускай это Π±ΡƒΠ΄Π΅Ρ‚ peer-to-peer ΡΠ΅Ρ‚ΡŒ, Π±Π΅Π· ΠΊΠ°ΠΊΠΎΠ³ΠΎ-Π»ΠΈΠ±ΠΎ обнаруТСния участников. БобствСнноручно Π±ΡƒΠ΄Π΅ΠΌ ΡƒΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ ΠΊ ΠΊΠ°ΠΊΠΎΠΌΡƒ адрСсу: ΠΏΠΎΡ€Ρ‚Ρƒ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π°Ρ‚ΡŒΡΡ для общСния с собСсСдником.

Π― понимаю, Ρ‡Ρ‚ΠΎ, Π½Π° Π΄Π°Π½Π½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚, ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΠΎ доступности прямой связи ΠΌΠ΅ΠΆΠ΄Ρƒ двумя ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½Ρ‹ΠΌΠΈ ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Π°ΠΌΠΈ β€” сущСствСнноС ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅ примСнимости IM Π½Π° ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠ΅. Но Ρ‡Π΅ΠΌ большС Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΎΠ² Π±ΡƒΠ΄ΡƒΡ‚ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Ρ‹Π²Π°Ρ‚ΡŒ всякиС NAT-traversal костыли, Ρ‚Π΅ΠΌ дольшС ΠΌΡ‹ Ρ‚Π°ΠΊ ΠΈ Π±ΡƒΠ΄Π΅ΠΌ ΠΎΡΡ‚Π°Π²Π°Ρ‚ΡŒΡΡ Π² IPv4 Π˜Π½Ρ‚Π΅Ρ€Π½Π΅Ρ‚Π΅, с ΡƒΠ΄Ρ€ΡƒΡ‡Π°ΡŽΡ‰Π΅ΠΉ Π²Π΅Ρ€ΠΎΡΡ‚Π½ΠΎΡΡ‚ΡŒΡŽ связи ΠΌΠ΅ΠΆΠ΄Ρƒ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½Ρ‹ΠΌΠΈ ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€Π°ΠΌΠΈ. Ну сколько ΠΌΠΎΠΆΠ½ΠΎ Ρ‚Π΅Ρ€ΠΏΠ΅Ρ‚ΡŒ отсутствиС IPv6 Π΄ΠΎΠΌΠ° ΠΈ Π½Π° Ρ€Π°Π±ΠΎΡ‚Π΅?

Π£ нас Π±ΡƒΠ΄Π΅Ρ‚ friend-to-friend ΡΠ΅Ρ‚ΡŒ: всС Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹Π΅ собСсСдники Π·Π°Ρ€Π°Π½Π΅Π΅ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ извСстны. Π’ΠΎ-ΠΏΠ΅Ρ€Π²Ρ‹Ρ…, это сильно всё ΡƒΠΏΡ€ΠΎΡ‰Π°Π΅Ρ‚: ΠΏΡ€Π΅Π΄ΡΡ‚Π°Π²ΠΈΠ»ΠΈΡΡŒ, нашли ΠΈΠ»ΠΈ Π½Π΅ нашли имя/ΠΊΠ»ΡŽΡ‡, ΠΎΡ‚ΠΊΠ»ΡŽΡ‡ΠΈΠ»ΠΈΡΡŒ ΠΈΠ»ΠΈ ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ°Π΅ΠΌ Ρ€Π°Π±ΠΎΡ‚Ρƒ, зная собСсСдника. Π’ΠΎ-Π²Ρ‚ΠΎΡ€Ρ‹Ρ…, Π² ΠΎΠ±Ρ‰Π΅ΠΌ случаС, это бСзопасно ΠΈ ΠΈΡΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ мноТСство Π°Ρ‚Π°ΠΊ.

Π˜Π½Ρ‚Π΅Ρ€Ρ„Π΅ΠΉΡ IM-Π° Π±ΡƒΠ΄Π΅Ρ‚ Π±Π»ΠΈΠ·ΠΎΠΊ ΠΊ классичСским Ρ€Π΅ΡˆΠ΅Π½ΠΈΡΠΌ suckless-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΎΠ², ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠ½Π΅ ΠΎΡ‡Π΅Π½ΡŒ нравятся своим ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΠΈΠ·ΠΌΠΎΠΌ ΠΈ Unix-way философиСй. IM ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ° для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ собСсСдника создаёт Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΎΡ€ΠΈΡŽ с трСмя Unix domain socket:

  • in β€” Π² Π½Π΅Π³ΠΎ Π·Π°ΠΏΠΈΡΡ‹Π²Π°ΡŽΡ‚ΡΡ отправляСмыС собСсСднику сообщСния;
  • out β€” ΠΈΠ· Π½Π΅Π³ΠΎ Ρ‡ΠΈΡ‚Π°ΡŽΡ‚ΡΡ ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅ΠΌΡ‹Π΅ ΠΎΡ‚ собСсСдника сообщСния;
  • state β€” читая ΠΈΠ· Π½Π΅Π³ΠΎ, ΠΌΡ‹ ΡƒΠ·Π½Π°Ρ‘ΠΌ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Ρ‘Π½ Π»ΠΈ сСйчас собСсСдник, адрСс/ΠΏΠΎΡ€Ρ‚ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ.

ΠšΡ€ΠΎΠΌΠ΅ Ρ‚ΠΎΠ³ΠΎ, создаётся conn сокСт, записав Π² ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ хост ΠΏΠΎΡ€Ρ‚, ΠΌΡ‹ ΠΈΠ½ΠΈΡ†ΠΈΠΈΡ€ΡƒΠ΅ΠΌ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ ΠΊ ΡƒΠ΄Π°Π»Ρ‘Π½Π½ΠΎΠΌΡƒ собСсСднику.

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

Π’Π°ΠΊΠΎΠΉ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ позволяСт Π΄Π΅Π»Π°Ρ‚ΡŒ нСзависимыС Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ IM транспорта ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΎΠ³ΠΎ интСрфСйса, вСдь Π½Π° вкус ΠΈ Ρ†Π²Π΅Ρ‚ Ρ‚ΠΎΠ²Π°Ρ€ΠΈΡ‰Π° Π½Π΅Ρ‚, ΠΊΠ°ΠΆΠ΄ΠΎΠΌΡƒ Π½Π΅ ΡƒΠ³ΠΎΠ΄ΠΈΡˆΡŒ. Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ tmux ΠΈ/ΠΈΠ»ΠΈ multitail, ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΌΠ½ΠΎΠ³ΠΎΠΎΠΊΠΎΠ½Π½Ρ‹ΠΉ интСрфСйс с синтаксичСской подсвСткой. А с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ rlwrap ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ GNU Readline-ΡΠΎΠ²ΠΌΠ΅ΡΡ‚ΠΈΠΌΡƒΡŽ строку для Π²Π²ΠΎΠ΄Π° сообщСний.

На самом Π΄Π΅Π»Π΅, suckless ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Ρ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ FIFO-Ρ„Π°ΠΉΠ»Ρ‹. Π›ΠΈΡ‡Π½ΠΎ я Π½Π΅ смог ΠΏΠΎΠ½ΡΡ‚ΡŒ ΠΊΠ°ΠΊ Π² asyncio Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ с Ρ„Π°ΠΉΠ»Π°ΠΌΠΈ ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π½ΠΎ Π±Π΅Π· собствСнноручной ΠΏΠΎΠ΄Π»ΠΎΠΆΠΊΠΈ ΠΈΠ· Π²Ρ‹Π΄Π΅Π»Π΅Π½Π½Ρ‹Ρ… Ρ‚Ρ€Π΅Π΄ΠΎΠ² (для Ρ‚Π°ΠΊΠΈΡ… Π²Π΅Ρ‰Π΅ΠΉ Π΄Π°Π²Π½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽ язык Go). ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ Ρ€Π΅ΡˆΠΈΠ» ΠΎΠ±ΠΎΠΉΡ‚ΠΈΡΡŒ Unix domain сокСтами. К соТалСнию, это Π»ΠΈΡˆΠ°Π΅Ρ‚ возмоТности ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ echo 2001:470:dead::babe 6666 > conn. Π― Ρ€Π΅ΡˆΠΈΠ» эту ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡƒ, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ socat: echo 2001:470:dead::babe 6666 | socat β€” UNIX-CONNECT:conn, socat READLINE UNIX-CONNECT:alice/in.

ΠŸΠ΅Ρ€Π²ΠΎΠ½Π°Ρ‡Π°Π»ΡŒΠ½Ρ‹ΠΉ нСбСзопасный ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»

Π’ качСствС транспорта ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ TCP: ΠΎΠ½ Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚ доставку ΠΈ Π΅Ρ‘ порядок. UDP Π½Π΅ Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚ Π½ΠΈ Ρ‚ΠΎΠ³ΠΎ, Π½ΠΈ Π΄Ρ€ΡƒΠ³ΠΎΠ³ΠΎ (Ρ‡Ρ‚ΠΎ Π±Ρ‹Π»ΠΎ Π±Ρ‹ ΠΏΠΎΠ»Π΅Π·Π½Ρ‹ΠΌ, ΠΊΠΎΠ³Π΄Π° примСнится криптография), Π° ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠΈ SCTP Π² Python ΠΈΠ· ΠΊΠΎΡ€ΠΎΠ±ΠΊΠΈ Π½Π΅Ρ‚.

К соТалСнию, Π² TCP Π½Π΅Ρ‚ понятия сообщСния, Π° Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΏΠΎΡ‚ΠΎΠΊΠ° Π±Π°ΠΉΡ‚. ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΏΡ€ΠΈΠ΄ΡƒΠΌΠ°Ρ‚ΡŒ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ для сообщСний, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΈΡ… ΠΌΠΎΠΆΠ½ΠΎ Π±Ρ‹Π»ΠΎ Ρ€Π°Π·Π΄Π΅Π»ΡΡ‚ΡŒ ΠΌΠ΅ΠΆΠ΄Ρƒ собой Π² этом ΠΏΠΎΡ‚ΠΎΠΊΠ΅. МоТСм ΡƒΡΠ»ΠΎΠ²ΠΈΡ‚ΡŒΡΡ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ символ ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ΄Π° строки. Для Π½Π°Ρ‡Π°Π»Π° ΠΏΠΎΠ΄ΠΎΠΉΠ΄Ρ‘Ρ‚, ΠΎΠ΄Π½Π°ΠΊΠΎ, ΠΊΠΎΠ³Π΄Π° ΠΌΡ‹ Π½Π°Ρ‡Π½Ρ‘ΠΌ ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°Ρ‚ΡŒ наши сообщСния, этот символ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΠΎΡΠ²ΠΈΡ‚ΡŒΡΡ Π³Π΄Π΅ ΡƒΠ³ΠΎΠ΄Π½ΠΎ Π² ΡˆΠΈΡ„Ρ€ΠΎΡ‚Π΅ΠΊΡΡ‚Π΅. Π’ сСтях поэтому популярны ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Ρ‹ ΠΎΡ‚ΠΏΡ€Π°Π²Π»ΡΡŽΡ‰ΠΈΠ΅ сначала Π΄Π»ΠΈΠ½Ρƒ сообщСния Π² Π±Π°ΠΉΡ‚Π°Ρ…. НапримСр, Π² Python ΠΈΠ· ΠΊΠΎΡ€ΠΎΠ±ΠΊΠΈ Π΅ΡΡ‚ΡŒ xdrlib ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡŽΡ‰Π°Ρ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ с ΠΏΠΎΠ΄ΠΎΠ±Π½Ρ‹ΠΌ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ΠΎΠΌ XDR.

ΠœΡ‹ Π½Π΅ Π±ΡƒΠ΄Π΅ΠΌ ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎ ΠΈ эффСктивно Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ с TCP Ρ‡Ρ‚Π΅Π½ΠΈΠ΅ΠΌ β€” упростим ΠΊΠΎΠ΄. Π§ΠΈΡ‚Π°Π΅ΠΌ Π² бСсконСчном Ρ†ΠΈΠΊΠ»Π΅ Π΄Π°Π½Π½Ρ‹Π΅ ΠΈΠ· сокСта, ΠΏΠΎΠΊΠ° Π½Π΅ Π΄Π΅ΠΊΠΎΠ΄ΠΈΡ€ΡƒΠ΅ΠΌ ΠΏΠΎΠ»Π½ΠΎΠ΅ сообщСниС. Π’ качСствС Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π° для Ρ‚Π°ΠΊΠΎΠ³ΠΎ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Π° ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΈ JSON с XML. Но, ΠΊΠΎΠ³Π΄Π° добавится криптография, Ρ‚ΠΎ Π΄Π°Π½Π½Ρ‹Π΅ придётся ΠΏΠΎΠ΄ΠΏΠΈΡΡ‹Π²Π°Ρ‚ΡŒ ΠΈ Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ β€” Π° это ΠΏΠΎΡ‚Ρ€Π΅Π±ΡƒΠ΅Ρ‚ Π±Π°ΠΉΡ‚-Π²-Π±Π°ΠΉΡ‚ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ‡Π½ΠΎΠ³ΠΎ прСдставлСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ², Ρ‡Π΅Π³ΠΎ Π½Π΅ ΠΎΠ±Π΅ΡΠΏΠ΅Ρ‡ΠΈΠ²Π°ΡŽΡ‚ JSON/XML (dumps Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΎΡ‚Π»ΠΈΡ‡Π°Ρ‚ΡŒΡΡ).

XDR ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΠΈΡ‚ для Ρ‚Π°ΠΊΠΎΠΉ Π·Π°Π΄Π°Ρ‡ΠΈ, ΠΎΠ΄Π½Π°ΠΊΠΎ я Π²Ρ‹Π±ΠΈΡ€Π°ΡŽ ASN.1 с DER-ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ ΠΈ PyDERASN Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΡƒ, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ Π½Π° Ρ€ΡƒΠΊΠ°Ρ… Ρƒ нас Π±ΡƒΠ΄ΡƒΡ‚ высокоуровнСвыС ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ с ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌΠΈ часто приятнСС ΠΈ ΡƒΠ΄ΠΎΠ±Π½Π΅Π΅ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ. Π’ ΠΎΡ‚Π»ΠΈΡ‡ΠΈΠΈ ΠΎΡ‚ schemaless bencode, MessagePack ΠΈΠ»ΠΈ 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(IdA) β”‚
        │─────────────────>β”‚
        β”‚                  β”‚
        β”‚MsgHandshake(IdB) β”‚
        β”‚<─────────────────│
        β”‚                  β”‚
        β”‚    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(","))

Задаётся собствСнноС имя (—our-name alice). Π§Π΅Ρ€Π΅Π· Π·Π°ΠΏΡΡ‚ΡƒΡŽ ΠΏΠ΅Ρ€Π΅Ρ‡ΠΈΡΠ»ΡΡŽΡ‚ΡΡ всС ΠΎΠΆΠΈΠ΄Π°Π΅ΠΌΡ‹Π΅ собСсСдники (—their-names bob,eve). Для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΈΠ· собСсСдников, создаётся дирСктория с Unix сокСтами, Π° Ρ‚Π°ΠΊΠΆΠ΅ ΠΏΠΎ ΠΊΠΎΡ€ΡƒΡ‚ΠΈΠ½Π΅ Π½Π° ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ in, out, state:

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 сокСта ΠΎΡ‚ΠΏΡ€Π°Π²Π»ΡΡŽΡ‚ΡΡ Π² 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 ΠΎΡ‡Π΅Ρ€Π΅Π΄ΠΈ, ΠΈΠ· ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… Π΄Π°Π½Π½Ρ‹Π΅ Π·Π°ΠΏΠΈΡΡ‹Π²Π°ΡŽΡ‚ΡΡ Π² out сокСт:

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

ΠŸΡ€ΠΈ Ρ‡Ρ‚Π΅Π½ΠΈΠΈ ΠΈΠ· state сокСта, ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ° ΠΈΡ‰Π΅Ρ‚ Π² 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()

ΠŸΡ€ΠΈ записи адрСса Π² conn сокСт, запускаСтся функция Β«ΠΈΠ½ΠΈΡ†ΠΈΠ°Ρ‚ΠΎΡ€Π°Β» соСдинСния:

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

Рассмотрим ΠΈΠ½ΠΈΡ†ΠΈΠ°Ρ‚ΠΎΡ€Π°. Π‘Π½Π°Ρ‡Π°Π»Π° ΠΎΠ½, ΠΎΡ‡Π΅Π²ΠΈΠ΄Π½ΠΎ, ΠΎΡ‚ΠΊΡ€Ρ‹Π²Π°Π΅Ρ‚ соСдинСниС Π΄ΠΎ ΡƒΠΊΠ°Π·Π°Π½Π½ΠΎΠ³ΠΎ хоста/ΠΏΠΎΡ€Ρ‚Π° ΠΈ отправляСт handshake сообщСниС со своим ΠΈΠΌΠ΅Π½Π΅ΠΌ:

 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(). ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ Ρ‡Ρ‚ΠΎ ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ»ΠΈ ΠΈΠΌΠ΅Π½Π½ΠΎ handshake сообщСниС.

 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 ΠΊΠΎΡ€ΡƒΡ‚ΠΈΠ½Ρƒ ΠΏΡ€Π΅ΠΊΡ€Π°Ρ‚ΠΈΡ‚ΡŒ Ρ€Π°Π±ΠΎΡ‚Ρƒ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΎΠ½Π° Π·Π°Π±Ρ‹Π»Π° ΠΎ своём writer, связанным с ΡƒΡΡ‚Π°Ρ€Π΅Π²ΡˆΠΈΠΌ 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 ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ исходящиС сообщСния (ΠΏΠΎΠ΄ΠΊΠ»Π°Π΄Ρ‹Π²Π°Π΅ΠΌΡ‹Π΅ Π² ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ ΠΈΠ· in сокСта), сСриализуСт ΠΈΡ… Π² 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 ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ, ΠΈΠ· ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ ΠΎΠ½ΠΈ Π±ΡƒΠ΄ΡƒΡ‚ ΠΎΡ‚ΠΏΡ€Π°Π²Π»Π΅Π½Ρ‹ Π² out сокСт ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰Π΅Π³ΠΎ собСсСдника. ΠŸΠΎΡ‡Π΅ΠΌΡƒ нСльзя просто Π΄Π΅Π»Π°Ρ‚ΡŒ .read() ΠΈ Π΄Π΅ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ сообщСниС? ΠŸΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ Π½Π΅ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½Π° ситуация, ΠΊΠΎΠ³Π΄Π° нСсколько сообщСний ΠΎΡ‚ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ Π±ΡƒΠ΄ΡƒΡ‚ Π°Π³Ρ€Π΅Π³ΠΈΡ€ΠΎΠ²Π°Π½Ρ‹ Π² Π±ΡƒΡ„Π΅Ρ€Π΅ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΎΠ½Π½ΠΎΠΉ систСмы ΠΈ ΠΎΡ‚ΠΏΡ€Π°Π²Π»Π΅Π½Ρ‹ ΠΎΠ΄Π½ΠΈΠΌ TCP-сСгмСнтом. Π”Π΅ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ‚ΠΎ ΠΌΡ‹ смоТСм ΠΏΠ΅Ρ€Π²ΠΎΠ΅, Π° дальшС Π² Π±ΡƒΡ„Π΅Ρ€Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΎΡΡ‚Π°Ρ‚ΡŒΡΡ Ρ‡Π°ΡΡ‚ΡŒ ΠΎΡ‚ ΠΏΠΎΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅Π³ΠΎ. ΠŸΡ€ΠΈ любой Π½Π΅ΡˆΡ‚Π°Ρ‚Π½ΠΎΠΉ ситуации ΠΌΡ‹ Π·Π°ΠΊΡ€Ρ‹Π²Π°Π΅ΠΌ TCP-соСдинСниС ΠΈ останавливаСм msg_sender ΠΊΠΎΡ€ΡƒΡ‚ΠΈΠ½Ρƒ (посылкой None Π² OUT_QUEUES ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ).

 174     buf = b""
 175     # Wait for test messages {{{
 176     while True:
 177         data = await reader.read(MaxMsgLen)
 178         if data == b"":
 179             break
 180         buf += data
 181         if len(buf) > MaxMsgLen:
 182             logging.warning("%s: max buffer size exceeded", _id)
 183             break
 184         try:
 185             msg, tail = Msg().decode(buf)
 186         except ASN1Error:
 187             continue
 188         buf = tail
 189         if msg.choice != "text":
 190             logging.warning("%s: unexpected %s message", _id, msg.choice)
 191             break
 192         try:
 193             await msg_receiver(msg.value, peer_name)
 194         except ValueError as err:
 195             logging.warning("%s: %s", err)
 196             break
 197     # }}}
 198     logging.info("%s: disconnecting: %s", _id, peer_name)
 199     IN_QUEUES[peer_name].put(None)
 200     writer.close()

  66 async def msg_receiver(msg_text: MsgText, peer_name: str) -> None:
  67     text = str(msg_text["text"])
  68     logging.info("%s: received %d characters message", peer_name, len(text))
  69     await OUT_QUEUES[peer_name].put(text)

ВСрнёмся ΠΊ основному ΠΊΠΎΠ΄Ρƒ. ПослС создания всСх ΠΊΠΎΡ€ΡƒΡ‚ΠΈΠ½ Π² ΠΌΠΎΠΌΠ΅Π½Ρ‚ запуска ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹, ΠΌΡ‹ стартуСм TCP-сСрвСр. На ΠΊΠ°ΠΆΠ΄ΠΎΠ΅ установлСнноС соСдинСниС ΠΎΠ½ создаёт responder (ΠΎΡ‚Π²Π΅Ρ‚Ρ‡ΠΈΠΊ) ΠΊΠΎΡ€ΡƒΡ‚ΠΈΠ½Ρƒ.

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

responder схоТ с 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()

БСзопасный ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»

ΠŸΡ€ΠΈΡˆΠ»ΠΎ врСмя ΠΎΠ±Π΅Π·ΠΎΠΏΠ°ΡΠΈΡ‚ΡŒ нашС ΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅. Π§Ρ‚ΠΎ ΠΆΠ΅ ΠΌΡ‹ ΠΏΠΎΠ΄Ρ€Π°Π·ΡƒΠΌΠ΅Π²Π°Π΅ΠΌ ΠΏΠΎΠ΄ Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΡΡ‚ΡŒΡŽ ΠΈ Ρ‡Ρ‚ΠΎ Ρ…ΠΎΡ‚ΠΈΠΌ:

  • ΠΊΠΎΠ½Ρ„ΠΈΠ΄Π΅Π½Ρ†ΠΈΠ°Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ ΠΏΠ΅Ρ€Π΅Π΄Π°Π²Π°Π΅ΠΌΡ‹Ρ… сообщСний;
  • Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ‡Π½ΠΎΡΡ‚ΡŒ ΠΈ Ρ†Π΅Π»ΠΎΡΡ‚Π½ΠΎΡΡ‚ΡŒ ΠΏΠ΅Ρ€Π΅Π΄Π°Π²Π°Π΅ΠΌΡ‹Ρ… сообщСний β€” ΠΈΡ… ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ΠΎ;
  • Π·Π°Ρ‰ΠΈΡ‚Π° ΠΎΡ‚ Π°Ρ‚Π°ΠΊ пСрСпроигрывания (replay attack) β€” Ρ„Π°ΠΊΡ‚ ΠΏΡ€ΠΎΠΏΠ°ΠΆΠΈ ΠΈΠ»ΠΈ ΠΏΠΎΠ²Ρ‚ΠΎΡ€Π° сообщСний Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ (ΠΈ ΠΌΡ‹ Ρ€Π΅ΡˆΠ°Π΅ΠΌ ΠΎΠ±Ρ€Ρ‹Π²Π°Ρ‚ΡŒ соСдинСниС);
  • идСнтификация ΠΈ аутСнтификация собСсСдников ΠΏΠΎ Π·Π°Ρ€Π°Π½Π΅Π΅ Π²Π±ΠΈΡ‚Ρ‹ΠΌ ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΌ ΠΊΠ»ΡŽΡ‡Π°ΠΌ β€” ΠΌΡ‹ ΡƒΠΆΠ΅ Ρ€Π΅ΡˆΠΈΠ»ΠΈ Ρ€Π°Π½Π΅Π΅, Ρ‡Ρ‚ΠΎ Π΄Π΅Π»Π°Π΅ΠΌ friend-to-friend ΡΠ΅Ρ‚ΡŒ. Волько послС Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ ΠΌΡ‹ ΠΏΠΎΠΉΠΌΡ‘ΠΌ с ΠΊΠ΅ΠΌ общаСмся;
  • Π½Π°Π»ΠΈΡ‡ΠΈΠ΅ perfect forward secrecy свойства (PFS) β€” компромСтация нашСго Π΄ΠΎΠ»Π³ΠΎΠΆΠΈΠ²ΡƒΡ‰Π΅Π³ΠΎ ΠΊΠ»ΡŽΡ‡Π° подписи Π½Π΅ Π΄ΠΎΠ»ΠΆΠ½Π° ΠΏΡ€ΠΈΠ²ΠΎΠ΄ΠΈΡ‚ΡŒ ΠΊ возмоТности чтСния всСй ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΉ пСрСписки. Π—Π°ΠΏΠΈΡΡŒ ΠΏΠ΅Ρ€Π΅Ρ…Π²Π°Ρ‡Π΅Π½Π½ΠΎΠ³ΠΎ Ρ‚Ρ€Π°Ρ„ΠΈΠΊΠ° становится бСсполСзной;
  • Π΄Π΅ΠΉΡΡ‚Π²ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ/Π²Π°Π»ΠΈΠ΄Π½ΠΎΡΡ‚ΡŒ сообщСний (транспортных ΠΈ рукопоТатия) Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π² ΠΏΡ€Π΅Π΄Π΅Π»Π°Ρ… ΠΎΠ΄Π½ΠΎΠΉ TCP-сСссии. Вставка ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ подписанных/Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹Ρ… сообщСний ΠΈΠ· Π΄Ρ€ΡƒΠ³ΠΎΠΉ сСссии (Π΄Π°ΠΆΠ΅ с этим ΠΆΠ΅ собСсСдником) Π½Π΅ Π΄ΠΎΠ»ΠΆΠ½Π° Π±Ρ‹Ρ‚ΡŒ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΠΉ;
  • пассивный Π½Π°Π±Π»ΡŽΠ΄Π°Ρ‚Π΅Π»ΡŒ Π½Π΅ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π²ΠΈΠ΄Π΅Ρ‚ΡŒ Π½ΠΈ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠ² ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ, Π½ΠΈ ΠΏΠ΅Ρ€Π΅Π΄Π°Π²Π°Π΅ΠΌΡ‹Ρ… Π΄ΠΎΠ»Π³ΠΎΠΆΠΈΠ²ΡƒΡ‰ΠΈΡ… ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹Ρ… ΠΊΠ»ΡŽΡ‡Π΅ΠΉ, Π½ΠΈ Ρ…ΡΡˆΠ΅ΠΉ ΠΎΡ‚ Π½ΠΈΡ…. НСкая Π°Π½ΠΎΠ½ΠΈΠΌΠ½ΠΎΡΡ‚ΡŒ ΠΎΡ‚ пассивного Π½Π°Π±Π»ΡŽΠ΄Π°Ρ‚Π΅Π»Ρ.

Π£Π΄ΠΈΠ²ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ, Π½ΠΎ этот ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ практичСски всС хотят ΠΈΠΌΠ΅Ρ‚ΡŒ Π² любом ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Π΅ рукопоТатия, ΠΈ ΠΊΡ€Π°ΠΉΠ½Π΅ ΠΌΠ°Π»ΠΎ ΠΈΠ· пСрСчислСнного Π² ΠΈΡ‚ΠΎΠ³Π΅ выполняСтся для Β«Π΄ΠΎΠΌΠΎΡ€ΠΎΡ‰Π΅Π½Π½Ρ‹Ρ…Β» ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»ΠΎΠ². Π’ΠΎΡ‚ ΠΈ сСйчас Π½Π΅ Π±ΡƒΠ΄Π΅ΠΌ ΠΈΠ·ΠΎΠ±Ρ€Π΅Ρ‚Π°Ρ‚ΡŒ Π½ΠΎΠ²ΠΎΠ³ΠΎ. Π― Π±Ρ‹ ΠΎΠ΄Π½ΠΎΠ·Π½Π°Ρ‡Π½ΠΎ Ρ€Π΅ΠΊΠΎΠΌΠ΅Π½Π΄ΠΎΠ²Π°Π» ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Noise framework для построСния ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»ΠΎΠ², Π½ΠΎ Π²Ρ‹Π±Π΅Ρ€Π΅ΠΌ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ ΠΏΠΎΠΏΡ€ΠΎΡ‰Π΅.

НаиболСС популярны Π΄Π²Π° ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Π°:

  • TLS β€” слоТнСйший ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ» с Π΄Π»ΠΈΠ½Π½ΠΎΠΉ историСй Π±Π°Π³ΠΎΠ², косяков, уязвимостСй, ΠΏΠ»ΠΎΡ…ΠΎΠΉ продуманности, слоТности ΠΈ Π½Π΅Π΄ΠΎΡ‡Ρ‘Ρ‚ΠΎΠ² (Π²ΠΏΡ€ΠΎΡ‡Π΅ΠΌ, ΠΊ TLS 1.3 это ΠΌΠ°Π»ΠΎ относится). Но Π½Π΅ рассматриваСм Π΅Π³ΠΎ ΠΈΠ·-Π·Π° пСрСуслоТнённости.
  • IPsec с IKE β€” Π½Π΅ ΠΈΠΌΠ΅ΡŽΡ‚ ΡΠ΅Ρ€ΡŒΡ‘Π·Π½Ρ‹Ρ… криптографичСских ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ, хотя Ρ‚ΠΎΠΆΠ΅ Π½Π΅ просты. Если ΠΏΠΎΡ‡ΠΈΡ‚Π°Ρ‚ΡŒ ΠΏΡ€ΠΎ IKEv1 ΠΈ IKEv2, Ρ‚ΠΎ ΠΈΡ… истоком ΡΠ²Π»ΡΡŽΡ‚ΡΡ STS, ISO/IEC IS 9798-3 ΠΈ SIGMA (SIGn-and-MAc) ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Ρ‹ β€” достаточно простыми для Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ Π·Π° ΠΎΠ΄ΠΈΠ½ Π²Π΅Ρ‡Π΅Ρ€.

Π§Π΅ΠΌ SIGMA, ΠΊΠ°ΠΊ послСднСС Π·Π²Π΅Π½ΠΎ развития STS/ISO ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»ΠΎΠ², Ρ…ΠΎΡ€ΠΎΡˆ? Он удовлСтворяСт всСм нашим трСбованиям (Π² Ρ‚ΠΎΠΌ числС «скрытия» ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠ² собСсСдников), Π½Π΅ ΠΈΠΌΠ΅Π΅Ρ‚ извСстных криптографичСских ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ. Он минималистичСн β€” ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠ΅ хотя Π±Ρ‹ ΠΎΠ΄Π½ΠΎΠ³ΠΎ элСмСнта ΠΈΠ· сообщСния ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Π° ΠΏΡ€ΠΈΠ²Π΅Π΄Ρ‘Ρ‚ ΠΊ Π΅Π³ΠΎ нСбСзопасности.

Π”Π°Π²Π°ΠΉΡ‚Π΅ пройдёмся ΠΎΡ‚ ΠΏΡ€ΠΎΡΡ‚Π΅ΠΉΡˆΠ΅Π³ΠΎ Π΄ΠΎΠΌΠΎΡ€ΠΎΡ‰Π΅Π½Π½ΠΎΠ³ΠΎ ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Π° Π΄ΠΎ SIGMA. Бамая базовая ΠΈΠ½Ρ‚Π΅Ρ€Π΅ΡΡƒΡŽΡ‰Π°Ρ нас опСрация это согласованиС ΠΊΠ»ΡŽΡ‡Π΅ΠΉ: функция, Π½Π° Π²Ρ‹Ρ…ΠΎΠ΄Π΅ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ ΠΎΠ±Π° участника ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ ΠΎΠ΄Π½ΠΎ ΠΈ Ρ‚ΠΎ ΠΆΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ ΠΌΠΎΠΆΠ½ΠΎ Π±ΡƒΠ΄Π΅Ρ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π² качСствС симмСтричного ΠΊΠ»ΡŽΡ‡Π°. НС вдаваясь Π² подробности: каТдая ΠΈΠ· сторон Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΠ΅Ρ‚ ΡΡ„Π΅ΠΌΠ΅Ρ€Π½ΡƒΡŽ (ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‰ΡƒΡŽΡΡ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π² ΠΏΡ€Π΅Π΄Π΅Π»Π°Ρ… ΠΎΠ΄Π½ΠΎΠΉ сСссии) ΠΊΠ»ΡŽΡ‡Π΅Π²ΡƒΡŽ ΠΏΠ°Ρ€Ρƒ (ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΈ ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½Ρ‹ΠΉ ΠΊΠ»ΡŽΡ‡ΠΈ), ΠΎΠ±ΠΌΠ΅Π½ΠΈΠ²Π°ΡŽΡ‚ΡΡ ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΌΠΈ ΠΊΠ»ΡŽΡ‡Π°ΠΌΠΈ, Π²Ρ‹Π·Ρ‹Π²Π°ΡŽΡ‚ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ согласования, Π½Π° Π²Ρ…ΠΎΠ΄ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ ΠΏΠ΅Ρ€Π΅Π΄Π°ΡŽΡ‚ свой ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½Ρ‹ΠΉ ΠΊΠ»ΡŽΡ‡ ΠΈ ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ»ΡŽΡ‡ собСсСдника.

β”Œβ”€β”€β”€β”€β”€β”          β”Œβ”€β”€β”€β”€β”€β”
β”‚PeerAβ”‚          β”‚PeerBβ”‚
β””β”€β”€β”¬β”€β”€β”˜          β””β”€β”€β”¬β”€β”€β”˜
   β”‚   IdA, PubA    β”‚ ╔════════════════════╗
   │───────────────>β”‚ β•‘PrvA, PubA = DHgen()β•‘
   β”‚                β”‚ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
   β”‚   IdB, PubB    β”‚ ╔════════════════════╗
   β”‚<───────────────│ β•‘PrvB, PubB = DHgen()β•‘
   β”‚                β”‚ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
   ────┐    ╔═══════╧════════════╗
       β”‚    β•‘Key = DH(PrvA, PubB)β•‘
   <β”€β”€β”€β”˜    β•šβ•β•β•β•β•β•β•β•€β•β•β•β•β•β•β•β•β•β•β•β•β•
   β”‚                β”‚
   β”‚                β”‚

Π›ΡŽΠ±ΠΎΠΉ ΠΌΠΎΠΆΠ΅Ρ‚ Π²ΡΡ‚Ρ€ΡΡ‚ΡŒ-ΠΏΠΎ-сСрСдинС ΠΈ Π·Π°ΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹Π΅ ΠΊΠ»ΡŽΡ‡ΠΈ своими собствСнными β€” Π² Π΄Π°Π½Π½ΠΎΠΌ ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Π΅ Π½Π΅Ρ‚ Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ собСсСдников. Π”ΠΎΠ±Π°Π²ΠΈΠΌ подпись Π΄ΠΎΠ»Π³ΠΎΠΆΠΈΠ²ΡƒΡ‰ΠΈΠΌΠΈ ΠΊΠ»ΡŽΡ‡Π°ΠΌΠΈ.

β”Œβ”€β”€β”€β”€β”€β”                            β”Œβ”€β”€β”€β”€β”€β”
β”‚PeerAβ”‚                            β”‚PeerBβ”‚
β””β”€β”€β”¬β”€β”€β”˜                            β””β”€β”€β”¬β”€β”€β”˜
   β”‚IdA, PubA, sign(SignPrvA, (PubA)) β”‚ ╔═══════════════════════════╗
   │─────────────────────────────────>β”‚ β•‘SignPrvA, SignPubA = load()β•‘
   β”‚                                  β”‚ β•‘PrvA, PubA = DHgen()       β•‘
   β”‚                                  β”‚ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
   β”‚IdB, PubB, sign(SignPrvB, (PubB)) β”‚ ╔═══════════════════════════╗
   β”‚<─────────────────────────────────│ β•‘SignPrvB, SignPubB = load()β•‘
   β”‚                                  β”‚ β•‘PrvB, PubB = DHgen()       β•‘
   β”‚                                  β”‚ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
   ────┐    ╔═════════════════════╗   β”‚
       β”‚    β•‘verify(SignPubB, ...)β•‘   β”‚
   <β”€β”€β”€β”˜    β•‘Key = DH(PrvA, PubB) β•‘   β”‚
   β”‚        β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•   β”‚
   β”‚                                  β”‚

Вакая подпись Π½Π΅ ΠΏΠΎΠ΄ΠΎΠΉΠ΄Ρ‘Ρ‚, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ ΠΎΠ½Π° Π½Π΅ привязана ΠΊ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎΠΉ сСссии. Π’Π°ΠΊΠΈΠ΅ сообщСния Β«ΠΏΠΎΠ΄ΠΎΠΉΠ΄ΡƒΡ‚Β» ΠΈ для сСссий с Π΄Ρ€ΡƒΠ³ΠΈΠΌΠΈ участниками. ΠŸΠΎΠ΄ΠΏΠΈΡΡ‹Π²Π°Ρ‚ΡŒΡΡ Π΄ΠΎΠ»ΠΆΠ΅Π½ вСсь контСкст. Π­Ρ‚ΠΎ Π²Ρ‹Π½ΡƒΠΆΠ΄Π°Π΅Ρ‚ Ρ‚Π°ΠΊΠΆΠ΅ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ посылку Π΅Ρ‰Ρ‘ ΠΎΠ΄Π½ΠΎΠ³ΠΎ сообщСния ΠΎΡ‚ A.

ΠšΡ€ΠΎΠΌΠ΅ Ρ‚ΠΎΠ³ΠΎ, ΠΊΡ€ΠΈΡ‚ΠΈΡ‡Π½ΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΏΠΎΠ΄ подпись ΠΈ собствСнный ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ, Π² ΠΏΡ€ΠΎΡ‚ΠΈΠ²Π½ΠΎΠΌ случаС, ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΎΠ΄ΠΌΠ΅Π½ΠΈΡ‚ΡŒ IdXXX ΠΈ ΠΏΠ΅Ρ€Π΅ΠΏΠΎΠ΄ΠΏΠΈΡΠ°Ρ‚ΡŒ сообщСниС ΠΊΠ»ΡŽΡ‡ΠΎΠΌ Π΄Ρ€ΡƒΠ³ΠΎΠ³ΠΎ извСстным собСсСдника. Для прСдотвращСния reflection Π°Ρ‚Π°ΠΊ, Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Ρ‡Ρ‚ΠΎΠ±Ρ‹ элСмСнты ΠΏΠΎΠ΄ подписью Π½Π°Ρ…ΠΎΠ΄ΠΈΠ»ΠΈΡΡŒ Π² Ρ‡Ρ‘Ρ‚ΠΊΠΎ Π·Π°Π΄Π°Π½Π½Ρ‹Ρ… мСстах ΠΏΠΎ своСму смыслу: Ссли A подписываСт (PubA, PubB), Ρ‚ΠΎ B Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΏΠΎΠ΄ΠΏΠΈΡΡ‹Π²Π°Ρ‚ΡŒ (PubB, PubA). Π­Ρ‚ΠΎ Π΅Ρ‰Ρ‘ ΠΈ Π³ΠΎΠ²ΠΎΡ€ΠΈΡ‚ ΠΎ ваТности Π²Ρ‹Π±ΠΎΡ€Π° структуры ΠΈ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π° сСриализованных Π΄Π°Π½Π½Ρ‹Ρ…. НапримСр, мноТСства Π² ASN.1 DER ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠΈ ΡΠΎΡ€Ρ‚ΠΈΡ€ΡƒΡŽΡ‚ΡΡ: SET OF(PubA, PubB) Π±ΡƒΠ΄Π΅Ρ‚ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ‡Π΅Π½ SET OF(PubB, PubA).

β”Œβ”€β”€β”€β”€β”€β”                                       β”Œβ”€β”€β”€β”€β”€β”
β”‚PeerAβ”‚                                       β”‚PeerBβ”‚
β””β”€β”€β”¬β”€β”€β”˜                                       β””β”€β”€β”¬β”€β”€β”˜
   β”‚                 IdA, PubA                   β”‚ ╔═══════════════════════════╗
   │────────────────────────────────────────────>β”‚ β•‘SignPrvA, SignPubA = load()β•‘
   β”‚                                             β”‚ β•‘PrvA, PubA = DHgen()       β•‘
   β”‚                                             β”‚ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
   β”‚IdB, PubB, sign(SignPrvB, (IdB, PubA, PubB)) β”‚ ╔═══════════════════════════╗
   β”‚<────────────────────────────────────────────│ β•‘SignPrvB, SignPubB = load()β•‘
   β”‚                                             β”‚ β•‘PrvB, PubB = DHgen()       β•‘
   β”‚                                             β”‚ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
   β”‚     sign(SignPrvA, (IdA, PubB, PubA))       β”‚ ╔═════════════════════╗
   │────────────────────────────────────────────>β”‚ β•‘verify(SignPubB, ...)β•‘
   β”‚                                             β”‚ β•‘Key = DH(PrvA, PubB) β•‘
   β”‚                                             β”‚ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
   β”‚                                             β”‚

Однако ΠΌΡ‹ всё Π΅Ρ‰Ρ‘ Π½Π΅ Β«Π΄ΠΎΠΊΠ°Π·Π°Π»ΠΈΒ» Ρ‡Ρ‚ΠΎ Π²Ρ‹Ρ€Π°Π±ΠΎΡ‚Π°Π»ΠΈ ΠΎΠ΄ΠΈΠ½Π°ΠΊΠΎΠ²Ρ‹ΠΉ ΠΎΠ±Ρ‰ΠΈΠΉ ΠΊΠ»ΡŽΡ‡ для этой сСссии. Π’ ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏΠ΅, ΠΌΠΎΠΆΠ½ΠΎ ΠΎΠ±ΠΎΠΉΡ‚ΠΈΡΡŒ ΠΈ Π±Π΅Π· этого шага β€” ΠΏΠ΅Ρ€Π²ΠΎΠ΅ ΠΆΠ΅ транспортноС сообщСниС Π±ΡƒΠ΄Π΅Ρ‚ Π½Π΅Π²Π°Π»ΠΈΠ΄Π½Ρ‹ΠΌ, Π½ΠΎ ΠΌΡ‹ Ρ…ΠΎΡ‚ΠΈΠΌ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΊΠΎΠ³Π΄Π° Ρ€ΡƒΠΊΠΎΠΏΠΎΠΆΠ°Ρ‚ΠΈΠ΅ Π·Π°Π²Π΅Ρ€ΡˆΠΈΠ»ΠΎΡΡŒ, Ρ‚ΠΎ Π±Ρ‹Π»ΠΈ Π±Ρ‹ ΡƒΠ²Π΅Ρ€Π΅Π½Ρ‹ Ρ‡Ρ‚ΠΎ всё Π΄Π΅ΠΉΡΡ‚Π²ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ согласовано. На Π΄Π°Π½Π½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚ Ρƒ нас Π½Π° Ρ€ΡƒΠΊΠ°Ρ… ISO/IEC IS 9798-3 ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ».

ΠœΡ‹ ΠΌΠΎΠ³Π»ΠΈ Π±Ρ‹ ΠΏΠΎΠ΄ΠΏΠΈΡΡ‹Π²Π°Ρ‚ΡŒ ΠΈ сам Π²Ρ‹Ρ€Π°Π±ΠΎΡ‚Π°Π½Π½Ρ‹ΠΉ ΠΊΠ»ΡŽΡ‡. Π­Ρ‚ΠΎ опасно, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ Π½Π΅ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΎ, Ρ‡Ρ‚ΠΎ Π² ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΠΎΠΌ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ΅ подписи ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ ΡƒΡ‚Π΅Ρ‡ΠΊΠΈ (пускай Π±ΠΈΡ‚Ρ‹-Π½Π°-подпись, Π½ΠΎ всё ΠΆΠ΅ ΡƒΡ‚Π΅Ρ‡ΠΊΠΈ). МоТно ΠΏΠΎΠ΄ΠΏΠΈΡΡ‹Π²Π°Ρ‚ΡŒ Ρ…ΡΡˆ ΠΎΡ‚ Π²Ρ‹Ρ€Π°Π±ΠΎΡ‚Π°Π½Π½ΠΎΠ³ΠΎ ΠΊΠ»ΡŽΡ‡Π°, Π½ΠΎ ΡƒΡ‚Π΅Ρ‡ΠΊΠ° Π΄Π°ΠΆΠ΅ Ρ…ΡΡˆΠ° ΠΎΡ‚ Π²Ρ‹Ρ€Π°Π±ΠΎΡ‚Π°Π½Π½ΠΎΠ³ΠΎ ΠΊΠ»ΡŽΡ‡Π° ΠΌΠΎΠΆΠ΅Ρ‚ ΠΈΠΌΠ΅Ρ‚ΡŒ Ρ†Π΅Π½Π½ΠΎΡΡ‚ΡŒ ΠΏΡ€ΠΈ brute-force Π°Ρ‚Π°ΠΊΠ΅ Π½Π° Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ Π²Ρ‹Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ. SIGMA ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ MAC Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ, Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€ΡƒΡŽΡ‰ΡƒΡŽ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ отправитСля.

β”Œβ”€β”€β”€β”€β”€β”                                            β”Œβ”€β”€β”€β”€β”€β”
β”‚PeerAβ”‚                                            β”‚PeerBβ”‚
β””β”€β”€β”¬β”€β”€β”˜                                            β””β”€β”€β”¬β”€β”€β”˜
   β”‚                    IdA, PubA                     β”‚ ╔═══════════════════════════╗
   │─────────────────────────────────────────────────>β”‚ β•‘SignPrvA, SignPubA = load()β•‘
   β”‚                                                  β”‚ β•‘PrvA, PubA = DHgen()       β•‘
   β”‚                                                  β”‚ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
   β”‚IdB, PubB, sign(SignPrvB, (PubA, PubB)), MAC(IdB) β”‚ ╔═══════════════════════════╗
   β”‚<─────────────────────────────────────────────────│ β•‘SignPrvB, SignPubB = load()β•‘
   β”‚                                                  β”‚ β•‘PrvB, PubB = DHgen()       β•‘
   β”‚                                                  β”‚ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
   β”‚                                                  β”‚ ╔═════════════════════╗
   β”‚     sign(SignPrvA, (PubB, PubA)), MAC(IdA)       β”‚ β•‘Key = DH(PrvA, PubB) β•‘
   │─────────────────────────────────────────────────>β”‚ β•‘verify(Key, IdB)     β•‘
   β”‚                                                  β”‚ β•‘verify(SignPubB, ...)β•‘
   β”‚                                                  β”‚ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
   β”‚                                                  β”‚

Π’ качСствС ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ, Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠΎΠ³ΡƒΡ‚ Π·Π°Ρ…ΠΎΡ‚Π΅Ρ‚ΡŒ ΠΏΠ΅Ρ€Π΅ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ свои эфСмСрныС ΠΊΠ»ΡŽΡ‡ΠΈ (Ρ‡Ρ‚ΠΎ, ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎ, ΠΏΠ»Π°Ρ‡Π΅Π²Π½ΠΎ для PFS). НапримСр, ΠΌΡ‹ сгСнСрировали ΠΊΠ»ΡŽΡ‡Π΅Π²ΡƒΡŽ ΠΏΠ°Ρ€Ρƒ, ΠΏΠΎΠΏΡ‹Ρ‚Π°Π»ΠΈΡΡŒ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒΡΡ, Π½ΠΎ TCP Π½Π΅ Π±Ρ‹Π» доступСн ΠΈΠ»ΠΈ оборвался Π³Π΄Π΅-Ρ‚ΠΎ Π½Π° сСрСдинС ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Π°. Π–Π°Π»ΠΊΠΎ Ρ‚Ρ€Π°Ρ‚ΠΈΡ‚ΡŒ ΠΏΠΎΡ‚Ρ€Π°Ρ‡Π΅Π½Π½ΡƒΡŽ ΡΠ½Ρ‚Ρ€ΠΎΠΏΠΈΡŽ ΠΈ рСсурсы процСссора Π½Π° Π½ΠΎΠ²ΡƒΡŽ ΠΏΠ°Ρ€Ρƒ. ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ Π²Π²Π΅Π΄Ρ‘ΠΌ, Ρ‚Π°ΠΊ Π½Π°Π·Ρ‹Π²Π°Π΅ΠΌΡ‹ΠΉ, cookie β€” псСвдослучайноС Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π·Π°Ρ‰ΠΈΡ‚ΠΈΡ‚ ΠΎΡ‚ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹Ρ… случайных replay Π°Ρ‚Π°ΠΊ ΠΏΡ€ΠΈ ΠΏΠΎΠ²Ρ‚ΠΎΡ€Π½ΠΎΠΌ использовании эфСмСрных ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹Ρ… ΠΊΠ»ΡŽΡ‡Π΅ΠΉ. Из-Π·Π° binding-Π° ΠΌΠ΅ΠΆΠ΄Ρƒ cookie ΠΈ эфСмСрным ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΌ ΠΊΠ»ΡŽΡ‡ΠΎΠΌ, ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ»ΡŽΡ‡ ΠΏΡ€ΠΎΡ‚ΠΈΠ²ΠΎΠΏΠΎΠ»ΠΎΠΆΠ½ΠΎΠ³ΠΎ участника ΠΌΠΎΠΆΠ½ΠΎ ΡƒΠ±Ρ€Π°Ρ‚ΡŒ ΠΈΠ· подписи Π·Π° Π½Π΅Π½Π°Π΄ΠΎΠ±Π½ΠΎΡΡ‚ΡŒΡŽ.

β”Œβ”€β”€β”€β”€β”€β”                                                                 β”Œβ”€β”€β”€β”€β”€β”
β”‚PeerAβ”‚                                                                 β”‚PeerBβ”‚
β””β”€β”€β”¬β”€β”€β”˜                                                                 β””β”€β”€β”¬β”€β”€β”˜
   β”‚                          IdA, PubA, CookieA                           β”‚ ╔═══════════════════════════╗
   │──────────────────────────────────────────────────────────────────────>β”‚ β•‘SignPrvA, SignPubA = load()β•‘
   β”‚                                                                       β”‚ β•‘PrvA, PubA = DHgen()       β•‘
   β”‚                                                                       β”‚ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
   β”‚IdB, PubB, CookieB, sign(SignPrvB, (CookieA, CookieB, PubB)), MAC(IdB) β”‚ ╔═══════════════════════════╗
   β”‚<──────────────────────────────────────────────────────────────────────│ β•‘SignPrvB, SignPubB = load()β•‘
   β”‚                                                                       β”‚ β•‘PrvB, PubB = DHgen()       β•‘
   β”‚                                                                       β”‚ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
   β”‚                                                                       β”‚ ╔═════════════════════╗
   β”‚          sign(SignPrvA, (CookieB, CookieA, PubA)), MAC(IdA)           β”‚ β•‘Key = DH(PrvA, PubB) β•‘
   │──────────────────────────────────────────────────────────────────────>β”‚ β•‘verify(Key, IdB)     β•‘
   β”‚                                                                       β”‚ β•‘verify(SignPubB, ...)β•‘
   β”‚                                                                       β”‚ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
   β”‚                                                                       β”‚

НаконСц, ΠΌΡ‹ Ρ…ΠΎΡ‚ΠΈΠΌ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½ΠΎΡΡ‚ΡŒ Π½Π°ΡˆΠΈΡ… ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠ² собСсСдников ΠΎΡ‚ пассивного Π½Π°Π±Π»ΡŽΠ΄Π°Ρ‚Π΅Π»Ρ. Для этого SIGMA ΠΏΡ€Π΅Π΄Π»Π°Π³Π°Π΅Ρ‚ сначала ΠΎΠ±ΠΌΠ΅Π½ΡΡ‚ΡŒΡΡ эфСмСрными ΠΊΠ»ΡŽΡ‡Π°ΠΌΠΈ, Π²Ρ‹Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ ΠΎΠ±Ρ‰ΠΈΠΉ ΠΊΠ»ΡŽΡ‡, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ Π·Π°ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°Ρ‚ΡŒ Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€ΡƒΡŽΡ‰ΠΈΠ΅ ΠΈ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€ΡƒΡŽΡ‰ΠΈΠ΅ сообщСния. SIGMA описываСт Π΄Π²Π° Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π°:

  • SIGMA-I β€” Π·Π°Ρ‰ΠΈΡ‰Π°Π΅Ρ‚ ΠΈΠ½ΠΈΡ†ΠΈΠ°Ρ‚ΠΎΡ€Π° ΠΎΡ‚ Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Ρ… Π°Ρ‚Π°ΠΊ, ΠΎΡ‚Π²Π΅Ρ‚Ρ‡ΠΈΠΊΠ° ΠΎΡ‚ пассивных: ΠΈΠ½ΠΈΡ†ΠΈΠ°Ρ‚ΠΎΡ€ Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€ΡƒΠ΅Ρ‚ ΠΎΡ‚Π²Π΅Ρ‚Ρ‡ΠΈΠΊΠ° ΠΈ Ссли Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ Π½Π΅ сошлось, Ρ‚ΠΎ свою ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΡŽ ΠΎΠ½ Π½Π΅ Π²Ρ‹Π΄Π°Ρ‘Ρ‚. ΠžΡ‚Π²Π΅Ρ‚Ρ‡ΠΈΠΊ ΠΆΠ΅ Π²Ρ‹Π΄Π°Ρ‘Ρ‚ свою ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΡŽ Ссли с Π½ΠΈΠΌ Π½Π°Ρ‡Π°Ρ‚ΡŒ Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹ΠΉ ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ». ΠŸΠ°ΡΡΠΈΠ²Π½Ρ‹ΠΉ Π½Π°Π±Π»ΡŽΠ΄Π°Ρ‚Π΅Π»ΡŒ Π½ΠΈΡ‡Π΅Π³ΠΎ Π½Π΅ ΡƒΠ·Π½Π°Π΅Ρ‚;
    SIGMA-R β€” Π·Π°Ρ‰ΠΈΡ‰Π°Π΅Ρ‚ ΠΎΡ‚Π²Π΅Ρ‚Ρ‡ΠΈΠΊΠ° ΠΎΡ‚ Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Ρ… Π°Ρ‚Π°ΠΊ, ΠΈΠ½ΠΈΡ†ΠΈΠ°Ρ‚ΠΎΡ€Π° ΠΎΡ‚ пассивных. Всё с Ρ‚ΠΎΡ‡Π½ΠΎΡΡ‚ΡŒΡŽ Π΄ΠΎ Π½Π°ΠΎΠ±ΠΎΡ€ΠΎΡ‚, Π½ΠΎ Π² этом ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Π΅ ΡƒΠΆΠ΅ Ρ‡Π΅Ρ‚Ρ‹Ρ€Π΅ сообщСния рукопоТатия пСрСдаётся.

    Π’Ρ‹Π±ΠΈΡ€Π°Π΅ΠΌ SIGMA-I ΠΊΠ°ΠΊ Π±ΠΎΠ»Π΅Π΅ ΠΏΠΎΡ…ΠΎΠΆΠΈΠΉ Π½Π° Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ ΠΌΡ‹ ΠΎΠΆΠΈΠ΄Π°Π΅ΠΌ ΠΎΡ‚ ΠΊΠ»ΠΈΠ΅Π½Ρ‚-сСрвСрных ΠΏΡ€ΠΈΠ²Ρ‹Ρ‡Π½Ρ‹Ρ… Π²Π΅Ρ‰Π΅ΠΉ: ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° ΡƒΠ·Π½Π°Π΅Ρ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹ΠΉ сСрвСр, Π° сСрвСр ΠΈ Ρ‚Π°ΠΊ Π·Π½Π°ΡŽΡ‚ всС. Плюс ΠΎΠ½ ΠΏΡ€ΠΎΡ‰Π΅ Π² Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΈΠ·-Π·Π° мСньшСго количСства сообщСний рукопоТатия. Всё Ρ‡Ρ‚ΠΎ ΠΌΡ‹ вносим Π² ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ», Ρ‚Π°ΠΊ это ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°Π½ΠΈΠ΅ части сообщСния ΠΈ пСрСнос ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€Π° A Π² ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°Π½Π½ΡƒΡŽ Ρ‡Π°ΡΡ‚ΡŒ послСднСго сообщСния:

    β”Œβ”€β”€β”€β”€β”€β”                                                                        β”Œβ”€β”€β”€β”€β”€β”
    β”‚PeerAβ”‚                                                                        β”‚PeerBβ”‚
    β””β”€β”€β”¬β”€β”€β”˜                                                                        β””β”€β”€β”¬β”€β”€β”˜
       β”‚                                PubA, CookieA                                 β”‚ ╔═══════════════════════════╗
       │─────────────────────────────────────────────────────────────────────────────>β”‚ β•‘SignPrvA, SignPubA = load()β•‘
       β”‚                                                                              β”‚ β•‘PrvA, PubA = DHgen()       β•‘
       β”‚                                                                              β”‚ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
       β”‚PubB, CookieB, Enc((IdB, sign(SignPrvB, (CookieA, CookieB, PubB)), MAC(IdB))) β”‚ ╔═══════════════════════════╗
       β”‚<─────────────────────────────────────────────────────────────────────────────│ β•‘SignPrvB, SignPubB = load()β•‘
       β”‚                                                                              β”‚ β•‘PrvB, PubB = DHgen()       β•‘
       β”‚                                                                              β”‚ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
       β”‚                                                                              β”‚ ╔═════════════════════╗
       β”‚       Enc((IdA, sign(SignPrvA, (CookieB, CookieA, PubA)), MAC(IdA)))         β”‚ β•‘Key = DH(PrvA, PubB) β•‘
       │─────────────────────────────────────────────────────────────────────────────>β”‚ β•‘verify(Key, IdB)     β•‘
       β”‚                                                                              β”‚ β•‘verify(SignPubB, ...)β•‘
       β”‚                                                                              β”‚ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
       β”‚                                                                              β”‚
    
    • Для подписи ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π“ΠžΠ‘Π’ Π  34.10-2012 Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌ с 256-Π±ΠΈΡ‚ ΠΊΠ»ΡŽΡ‡Π°ΠΌΠΈ.
    • Для Π²Ρ‹Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ΠΎΠ±Ρ‰Π΅Π³ΠΎ ΠΊΠ»ΡŽΡ‡Π° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ 34.10-2012 VKO.
    • Π’ качСствС MAC ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ CMAC. ВСхничСски это особый Ρ€Π΅ΠΆΠΈΠΌ Ρ€Π°Π±ΠΎΡ‚Ρ‹ Π±Π»ΠΎΡ‡Π½ΠΎΠ³ΠΎ ΡˆΠΈΡ„Ρ€Π°, описанный Π² Π“ΠžΠ‘Π’ Π  34.13-2015. Π’ качСствС Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°Π½ΠΈΡ для этого Ρ€Π΅ΠΆΠΈΠΌΠ° β€” ΠšΡƒΠ·Π½Π΅Ρ‡ΠΈΠΊ (34.12-2015).
    • Π’ качСствС ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€Π° собСсСдника ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Ρ…ΡΡˆ ΠΎΡ‚ Π΅Π³ΠΎ ΠΏΡƒΠ±Π»ΠΈΡ‡Π½ΠΎΠ³ΠΎ ΠΊΠ»ΡŽΡ‡Π°. Π’ качСствС Ρ…ΡΡˆΠ° примСняСтся Π‘Ρ‚Ρ€ΠΈΠ±ΠΎΠ³-256 (34.11-2012 256 Π±ΠΈΡ‚).

    ПослС рукопоТатия Ρƒ нас Π±ΡƒΠ΄Π΅Ρ‚ согласован ΠΎΠ±Ρ‰ΠΈΠΉ ΠΊΠ»ΡŽΡ‡. Π•Π³ΠΎ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ для Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€ΠΎΠ²Π°Π½Π½ΠΎΠ³ΠΎ ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°Π½ΠΈΡ транспортных сообщСний. Π­Ρ‚Π° Ρ‡Π°ΡΡ‚ΡŒ совсСм простая ΠΈ Π² Π½Π΅ΠΉ слоТно ΠΎΡˆΠΈΠ±ΠΈΡ‚ΡŒΡΡ: ΠΈΠ½ΠΊΡ€Π΅ΠΌΠ΅Π½Ρ‚ΠΈΡ€ΡƒΠ΅ΠΌ счётчик сообщСний, ΡˆΠΈΡ„Ρ€ΡƒΠ΅ΠΌ сообщСниС, Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€ΡƒΠ΅ΠΌ (MAC) счётчик ΠΈ ΡˆΠΈΡ„Ρ€ΠΎΡ‚Π΅ΠΊΡΡ‚, отправляСм. ΠŸΡ€ΠΈ ΠΏΡ€ΠΈΡ‘ΠΌΠ΅ сообщСния провСряСм Ρ‡Ρ‚ΠΎ счётчик ΠΈΠΌΠ΅Π΅Ρ‚ ΠΎΠΆΠΈΠ΄Π°Π΅ΠΌΠΎΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€ΡƒΠ΅ΠΌ ΡˆΠΈΡ„Ρ€ΠΎΡ‚Π΅ΠΊΡΡ‚ с счётчиком, Π΄Π΅ΡˆΠΈΡ„Ρ€ΡƒΠ΅ΠΌ. Каким ΠΊΠ»ΡŽΡ‡ΠΎΠΌ ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°Ρ‚ΡŒ сообщСния рукопоТатия, транспортныС, ΠΊΠ°ΠΊΠΈΠΌ Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ? Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΎΠ΄ΠΈΠ½ ΠΊΠ»ΡŽΡ‡ для всСх этих Π·Π°Π΄Π°Ρ‡ опасно ΠΈ Π½Π΅Ρ€Π°Π·ΡƒΠΌΠ½ΠΎ. НСобходимо Π²Ρ‹Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Ρ‚ΡŒ ΠΊΠ»ΡŽΡ‡ΠΈ, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ спСциализированныС Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ KDF (key derivation function). ΠžΠΏΡΡ‚ΡŒ ΠΆΠ΅, Π½Π΅ Π±ΡƒΠ΄Π΅ΠΌ ΠΌΡƒΠ΄Ρ€ΠΈΡ‚ΡŒ ΠΈ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ ΠΈΠ·ΠΎΠ±Ρ€Π΅Ρ‚Π°Ρ‚ΡŒ: HKDF Π΄Π°Π²Π½ΠΎ извСстна, Ρ…ΠΎΡ€ΠΎΡˆΠΎ исслСдована ΠΈ Π½Π΅ ΠΈΠΌΠ΅Π΅Ρ‚ извСстных ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ. К соТалСнию, Π² Ρ€ΠΎΠ΄Π½ΠΎΠΉ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ΅ Python Π½Π΅Ρ‚ этой Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ, поэтому ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ hkdf ΠΏΠ°ΠΊΠ΅Ρ‚. HKDF Π²Π½ΡƒΡ‚Ρ€ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ HMAC, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ, Π² свою ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ Ρ…ΡΡˆ-Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ. ΠŸΡ€ΠΈΠΌΠ΅Ρ€ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ Π½Π° Python Π½Π° страницС Wikipedia Π·Π°Π½ΠΈΠΌΠ°Π΅Ρ‚ считанныС строки ΠΊΠΎΠ΄Π°. Как ΠΈ Π² случаС с 34.10-2012, Π² качСствС Ρ…ΡΡˆ-Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Π±ΡƒΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π‘Ρ‚Ρ€ΠΈΠ±ΠΎΠ³-256. Π’Ρ‹Ρ…ΠΎΠ΄ нашСй Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ согласования ΠΊΠ»ΡŽΡ‡Π΅ΠΉ Π±ΡƒΠ΄Π΅Ρ‚ Π½Π°Π·Ρ‹Π²Π°Ρ‚ΡŒΡΡ сСссионным ΠΊΠ»ΡŽΡ‡ΠΎΠΌ, ΠΈΠ· ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ Π±ΡƒΠ΄ΡƒΡ‚ Π²Ρ‹Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Ρ‚ΡŒΡΡ Π½Π΅Π΄ΠΎΡΡ‚Π°ΡŽΡ‰ΠΈΠ΅ симмСтричныС:

    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 β€” Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠΎΠ΄ΠΏΠΈΡΡ‹Π²Π°Ρ‚ΡŒΡΡ (to be signed). HandshakeTBE β€” Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ Π±ΡƒΠ΄Π΅Ρ‚ Π·Π°ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°Π½ΠΎ (to be encrypted). ΠžΠ±Ρ€Π°Ρ‰Π°ΡŽ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅ Π½Π° ΠΏΠΎΠ»Π΅ ukm Π² MsgHandshake1. 34.10 VKO, для Π΅Ρ‰Ρ‘ большСй Ρ€Π°Π½Π΄ΠΎΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ Π²Ρ‹Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π΅ΠΌΡ‹Ρ… ΠΊΠ»ΡŽΡ‡Π΅ΠΉ, Π²ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ UKM (user keying material) β€” просто Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Π°Ρ энтропия.

    Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΊΡ€ΠΈΠΏΡ‚ΠΎΠ³Ρ€Π°Ρ„ΠΈΠΈ Π² ΠΊΠΎΠ΄

    Рассмотрим лишь Ρ‚ΠΎΠ»ΡŒΠΊΠΎ внСсённыС ΠΊ ΠΎΡ€ΠΈΠ³ΠΈΠ½Π°Π»ΡŒΠ½ΠΎΠΌΡƒ ΠΊΠΎΠ΄Ρƒ измСнСния, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ каркас остался ΠΏΡ€Π΅ΠΆΠ½ΠΈΠΌ (Π½Π° самом Π΄Π΅Π»Π΅, сначала Π±Ρ‹Π»Π° написана ΠΎΠΊΠΎΠ½Ρ‡Π°Ρ‚Π΅Π»ΡŒΠ½Π°Ρ рСализация, Π° ΠΏΠΎΡ‚ΠΎΠΌ ΠΈΠ· Π½Π΅Ρ‘ Π²Ρ‹ΠΏΠΈΠ»ΠΈΠ²Π°Π»Π°ΡΡŒ вся криптография).

    Π’Π°ΠΊ ΠΊΠ°ΠΊ аутСнтификация ΠΈ идСнтификация собСсСдников Π±ΡƒΠ΄Π΅Ρ‚ проводится ΠΏΠΎ ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΌ ΠΊΠ»ΡŽΡ‡Π°ΠΌ, Ρ‚ΠΎ Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΈΡ… Π½Π°Π΄ΠΎ Π³Π΄Π΅-Ρ‚ΠΎ Π΄ΠΎΠ»Π³ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎ Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ. Для простоты ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ JSON Ρ‚Π°ΠΊΠΎΠ³ΠΎ Π²ΠΈΠ΄Π°:

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

    our β€” наша ΠΊΠ»ΡŽΡ‡Π΅Π²Π°Ρ ΠΏΠ°Ρ€Π°, ΡˆΠ΅ΡΡ‚Π½Π°Π΄Ρ†Π°Ρ‚Π΅Ρ€ΠΈΡ‡Π½Ρ‹Π΅ ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½Ρ‹ΠΉ ΠΈ ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹Π΅ ΠΊΠ»ΡŽΡ‡ΠΈ. their β€” ΠΈΠΌΠ΅Π½Π° собСсСдников ΠΈ ΠΈΡ… ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹Π΅ ΠΊΠ»ΡŽΡ‡ΠΈ. ИзмСним Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Ρ‹ ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ строки ΠΈ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ постобработку 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(). Π’Π°ΠΊ ΠΊΠ°ΠΊ Π½Π°ΠΌ Π±ΡƒΠ΄ΡƒΡ‚ ΠΏΡ€ΠΈΡ…ΠΎΠ΄ΠΈΡ‚ΡŒ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€Ρ‹ собСсСдников Π² Π²ΠΈΠ΄Π΅ Ρ…ΡΡˆΠ° ΠΎΡ‚ ΠΏΡƒΠ±Π»ΠΈΡ‡Π½ΠΎΠ³ΠΎ ΠΊΠ»ΡŽΡ‡Π°, Ρ‚ΠΎ ΠΈΡ… ΠΌΠΎΠΆΠ½ΠΎ сразу ΠΆΠ΅ Π·Π°Ρ€Π°Π½Π΅Π΅ Π²Ρ‹Ρ‡ΠΈΡΠ»ΠΈΡ‚ΡŒ ΠΈ ΠΏΠΎΠΌΠ΅ΡΡ‚ΠΈΡ‚ΡŒ Π² ΡΠ»ΠΎΠ²Π°Ρ€ΡŒ для быстрого поиска. Π‘Ρ‚Ρ€ΠΈΠ±ΠΎΠ³-256 Ρ…ΡΡˆ это gost34112012256.GOST34112012256(), ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ ΡƒΠ΄ΠΎΠ²Π»Π΅Ρ‚Π²ΠΎΡ€ΡΡŽΡ‰ΠΈΠΉ hashlib интСрфСйсу Ρ…ΡΡˆ-Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ.

    Как измСнилась ΠΊΠΎΡ€ΡƒΡ‚ΠΈΠ½Π° ΠΈΠ½ΠΈΡ†ΠΈΠ°Ρ‚ΠΎΡ€Π°? Всё, ΠΊΠ°ΠΊ ΠΏΠΎ схСмС рукопоТатия: Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΠ΅ΠΌ cookie (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()
    

    • ΠΆΠ΄Ρ‘ΠΌ ΠΎΡ‚Π²Π΅Ρ‚Π° ΠΈ Π΄Π΅ΠΊΠΎΠ΄ΠΈΡ€ΡƒΠ΅ΠΌ ΠΏΡ€ΠΈΡˆΠ΅Π΄ΡˆΠ΅Π΅ Msg сообщСниС;
    • убСТдаСмся Ρ‡Ρ‚ΠΎ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ»ΠΈ handshake1;
    • Π΄Π΅ΠΊΠΎΠ΄ΠΈΡ€ΡƒΠ΅ΠΌ эфСмСрный ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ»ΡŽΡ‡ ΠΏΡ€ΠΎΡ‚ΠΈΠ²ΠΎΠΏΠΎΠ»ΠΎΠΆΠ½ΠΎΠΉ стороны ΠΈ вычисляСм сСссионный ΠΊΠ»ΡŽΡ‡;
    • Π²Ρ‹Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π΅ΠΌ симмСтричныС ΠΊΠ»ΡŽΡ‡ΠΈ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹Π΅ для ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ 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-Π±ΠΈΡ‚ это gost3410_vko.kek_34102012256() (KEK β€” key encryption key).

    Π’Ρ‹Ρ€Π°Π±ΠΎΡ‚Π°Π½Π½Ρ‹ΠΉ сСссионный ΠΊΠ»ΡŽΡ‡ ΡƒΠΆΠ΅ являСтся 256-Π±ΠΈΡ‚ Π±Π°ΠΉΡ‚ΠΎΠ²ΠΎΠΉ псСвдослучайной ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒΡŽ. ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ Π΅Π³ΠΎ сразу ΠΆΠ΅ ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π² HKDF Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ. Π’Π°ΠΊ ΠΊΠ°ΠΊ GOST34112012256 удовлСтворяСт hashlib интСрфСйсу, Ρ‚ΠΎ Π΅Π³ΠΎ ΠΌΠΎΠΆΠ½ΠΎ сразу ΠΆΠ΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π² Hkdf классС. Боль (ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ Hkdf) ΠΌΡ‹ Π½Π΅ ΡƒΠΊΠ°Π·Ρ‹Π²Π°Π΅ΠΌ, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ Π²Ρ‹Ρ€Π°Π±ΠΎΡ‚Π°Π½Π½Ρ‹ΠΉ ΠΊΠ»ΡŽΡ‡ ΠΈΠ·-Π·Π° эфСмСрности ΡƒΡ‡Π°ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΡ… ΠΊΠ»ΡŽΡ‡Π΅Π²Ρ‹Ρ… ΠΏΠ°Ρ€ Π±ΡƒΠ΄Π΅Ρ‚ Ρ€Π°Π·Π½Ρ‹ΠΌ для ΠΊΠ°ΠΆΠ΄ΠΎΠΉ сСссии ΠΈ Π² Π½Ρ‘ΠΌ ΡƒΠΆΠ΅ достаточно энтропии. kdf.expand() ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ ΡƒΠΆΠ΅ Π²Ρ‹Π΄Π°Ρ‘Ρ‚ ΠΊΠ»ΡŽΡ‡ΠΈ Π΄Π»ΠΈΠ½ΠΎΠΉ 256-Π±ΠΈΡ‚, Ρ‚Ρ€Π΅Π±ΡƒΠ΅ΠΌΡ‹Π΅ для ΠšΡƒΠ·Π½Π΅Ρ‡ΠΈΠΊΠ° Π² дальнСйшСм.

    Π”Π°Π»Π΅Π΅ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡŽΡ‚ΡΡ TBE ΠΈ TBS части ΠΏΡ€ΠΈΡˆΠ΅Π΄ΡˆΠ΅Π³ΠΎ сообщСния:

    • вычисляСтся ΠΈ провСряСтся MAC Π½Π°Π΄ ΠΏΡ€ΠΈΡˆΠ΅Π΄ΡˆΠΈΠΌ ΡˆΠΈΡ„Ρ€ΠΎΡ‚Π΅ΠΊΡΡ‚ΠΎΠΌ;
    • Π΄Π΅ΡˆΠΈΡ„Ρ€ΡƒΠ΅Ρ‚ΡΡ ΡˆΠΈΡ„Ρ€ΠΎΡ‚Π΅ΠΊΡΡ‚;
    • дСкодируСтся TBE структура;
    • ΠΈΠ· Π½Π΅Ρ‘ бСрётся ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ собСсСдника ΠΈ провСряСтся извСстСн Π»ΠΈ ΠΎΠ½ Π½Π°ΠΌ Π²ΠΎΠΎΠ±Ρ‰Π΅;
    • вычисляСтся ΠΈ провСрятся MAC Π½Π°Π΄ этим ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠΌ;
    • провСряСтся подпись Π½Π°Π΄ TBS структурой, Π² ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ входят cookie ΠΎΠ±Π΅ΠΈΡ… сторон ΠΈ ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ эфСмСрный ΠΊΠ»ΡŽΡ‡ ΠΏΡ€ΠΎΡ‚ΠΈΠ²ΠΎΠΏΠΎΠ»ΠΎΠΆΠ½ΠΎΠΉ стороны. Подпись провСряСтся Π΄ΠΎΠ»Π³ΠΎΠΆΠΈΠ²ΡƒΡ‰ΠΈΠΌ ΠΊΠ»ΡŽΡ‡ΠΎΠΌ подписи собСсСдника.

     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 описываСт Ρ€Π°Π·Π»ΠΈΡ‡Π½Ρ‹Π΅ Ρ€Π΅ΠΆΠΈΠΌΡ‹ Ρ€Π°Π±ΠΎΡ‚Ρ‹ Π±Π»ΠΎΡ‡Π½Ρ‹Ρ… ΡˆΠΈΡ„Ρ€ΠΎΠ² ΠΈΠ· 34.12-2015. Π‘Ρ€Π΅Π΄ΠΈ Π½ΠΈΡ… Π΅ΡΡ‚ΡŒ Ρ€Π΅ΠΆΠΈΠΌ Π²Ρ‹Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ имитовставки, вычислСния MAC-Π°. Π’ PyGOST это gost3413.mac(). Π­Ρ‚ΠΎΡ‚ Ρ€Π΅ΠΆΠΈΠΌ Ρ‚Ρ€Π΅Π±ΡƒΠ΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡ΠΈ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°Π½ΠΈΡ (ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°ΡŽΡ‰Π°Ρ ΠΈ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°ΡŽΡ‰Π°Ρ ΠΎΠ΄ΠΈΠ½ Π±Π»ΠΎΠΊ Π΄Π°Π½Π½Ρ‹Ρ…), Ρ€Π°Π·ΠΌΠ΅Ρ€Π° ΡˆΠΈΡ„Ρ€ΠΎΠ±Π»ΠΎΠΊΠ° ΠΈ, собствСнно, самих Π΄Π°Π½Π½Ρ‹Ρ…. ΠŸΠΎΡ‡Π΅ΠΌΡƒ нСльзя hardcode-ΠΈΡ‚ΡŒ Ρ€Π°Π·ΠΌΠ΅Ρ€ ΡˆΠΈΡ„Ρ€ΠΎΠ±Π»ΠΎΠΊΠ°? 34.12-2015 описываСт Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ 128-Π±ΠΈΡ‚Π½Ρ‹ΠΉ ΡˆΠΈΡ„Ρ€ ΠšΡƒΠ·Π½Π΅Ρ‡ΠΈΠΊ, Π½ΠΎ Π΅Ρ‰Ρ‘ ΠΈ 64-Π±ΠΈΡ‚Π½ΡƒΡŽ ΠœΠ°Π³ΠΌΡƒ β€” Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ ΠΈΠ·ΠΌΠ΅Π½Ρ‘Π½Π½Ρ‹ΠΉ Π“ΠžΠ‘Π’ 28147-89, созданный Π΅Ρ‰Ρ‘ Π² ΠšΠ“Π‘ ΠΈ Π΄ΠΎ сих ΠΏΠΎΡ€ ΠΈΠΌΠ΅ΡŽΡ‰ΠΈΠΉ ΠΎΠ΄ΠΈΠ½ ΠΈΠ· самых высоких ΠΏΠΎΡ€ΠΎΠ³ΠΎΠ² бСзопасности.

    ΠšΡƒΠ·Π½Π΅Ρ‡ΠΈΠΊ инициализируСтся gost.3412.GOST3412Kuznechik(key) Π²Ρ‹Π·ΠΎΠ²ΠΎΠΌ ΠΈ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ с .encrypt()/.decrypt() ΠΌΠ΅Ρ‚ΠΎΠ΄Π°ΠΌΠΈ, ΠΏΡ€ΠΈΠ³ΠΎΠ΄Π½Ρ‹ΠΌΠΈ для ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡ΠΈ Π² 34.13 Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ. MAC вычисляСтся ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ: gost3413.mac(GOST3412Kuznechik(key).encrypt, KUZNECHIK_BLOCKSIZE, ciphertext). Для сравнСния вычислСнного ΠΈ ΠΏΡ€ΠΈΡˆΠ΅Π΄ΡˆΠ΅Π³ΠΎ MAC-Π° нСльзя ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΎΠ±Ρ‹Ρ‡Π½ΠΎΠ΅ сравнСниС (==) Π±Π°ΠΉΡ‚ΠΎΠ²Ρ‹Ρ… строк, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ это опСрация Π΄Π°Ρ‘Ρ‚ ΡƒΡ‚Π΅Ρ‡ΠΊΠΈ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ сравнСния, Ρ‡Ρ‚ΠΎ, Π² ΠΎΠ±Ρ‰Π΅ΠΌ случаС, ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΡ€ΠΈΠ²ΠΎΠ΄ΠΈΡ‚ΡŒ ΠΊ Ρ„Π°Ρ‚Π°Π»ΡŒΠ½Ρ‹ΠΌ уязвимостям Ρ‚ΠΈΠΏΠ° BEAST Π°Ρ‚Π°ΠΊΠΈ Π½Π° TLS. Π’ Python имССтся ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Π°Ρ 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, plaintext, iv). ВрСбуСтся Π·Π°Π΄Π°Π½ΠΈΠ΅ Π²Π΅ΠΊΡ‚ΠΎΡ€Π° ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ, Π΄Π»ΠΈΠ½ΠΎΠΉ Ρ€ΠΎΠ²Π½ΠΎ Π² ΠΏΠΎΠ»ΠΎΠ²ΠΈΠ½Ρƒ ΡˆΠΈΡ„Ρ€ΠΎΠ±Π»ΠΎΠΊΠ°. Если наш ΠΊΠ»ΡŽΡ‡ ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°Π½ΠΈΡ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ для ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°Π½ΠΈΡ ΠΎΠ΄Π½ΠΎΠ³ΠΎ сообщСния (пускай ΠΈ ΠΈΠ· Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… Π±Π»ΠΎΠΊΠΎΠ²), Ρ‚ΠΎ бСзопасно Π·Π°Π΄Π°Ρ‚ΡŒ Π½ΡƒΠ»Π΅Π²ΠΎΠΉ Π²Π΅ΠΊΡ‚ΠΎΡ€ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ. Для ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°Π½ΠΈΡ handshake сообщСний Ρƒ нас ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ Ρ€Π°Π· ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹ΠΉ ΠΊΠ»ΡŽΡ‡.

    ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° подписи gost3410.verify() Ρ‚Ρ€ΠΈΠ²ΠΈΠ°Π»ΡŒΠ½Π°: ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‘ΠΌ ΡΠ»Π»ΠΈΠΏΡ‚ΠΈΡ‡Π΅ΡΠΊΡƒΡŽ ΠΊΡ€ΠΈΠ²ΡƒΡŽ Π² ΠΏΡ€Π΅Π΄Π΅Π»Π°Ρ… ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ Ρ€Π°Π±ΠΎΡ‚Π°Π΅ΠΌ (Π΅Ρ‘ ΠΌΡ‹ просто фиксируСм Π² нашСм GOSTIM ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Π΅), ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ»ΡŽΡ‡ подписанта (Π½Π΅ Π·Π°Π±Ρ‹Π²Π°Π΅ΠΌ, Ρ‡Ρ‚ΠΎ это Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ ΠΊΠΎΡ€Ρ‚Π΅ΠΆ ΠΈΠ· Π΄Π²ΡƒΡ… Π±ΠΎΠ»ΡŒΡˆΠΈΡ… чисСл, Π° Π½Π΅ байтовая строка), 34.11-2012 Ρ…ΡΡˆ ΠΈ сама ΠΏΡ€ΠΈΡˆΠ΅Π΄ΡˆΠ°Ρ подпись.

    Π”Π°Π»Π΅Π΅, Π² ΠΈΠ½ΠΈΡ†ΠΈΠ°Ρ‚ΠΎΡ€Π΅ ΠΌΡ‹ ΠΏΠΎΠ΄Π³ΠΎΡ‚Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ ΠΈ отсылаСм 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 ΠΊΠΎΡ€ΡƒΡ‚ΠΈΠ½Π° Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΡˆΠΈΡ„Ρ€ΡƒΠ΅Ρ‚ сообщСния, ΠΏΠ΅Ρ€Π΅Π΄ ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΎΠΉ Π² TCP-соСдинСниС. Π£ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ сообщСния ΠΌΠΎΠ½ΠΎΡ‚ΠΎΠ½Π½ΠΎ Π²ΠΎΠ·Ρ€Π°ΡΡ‚Π°ΡŽΡ‰ΠΈΠΉ nonce, Ρ‚Π°ΠΊΠΆΠ΅ ΡΠ²Π»ΡΡŽΡ‰ΠΈΠΉΡΡ ΠΈ Π²Π΅ΠΊΡ‚ΠΎΡ€ΠΎΠΌ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΏΡ€ΠΈ ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°Π½ΠΈΠΈ Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ счётчика. Π£ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ сообщСния ΠΈ Π±Π»ΠΎΠΊΠ° сообщСния Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½Π½ΠΎ Π±ΡƒΠ΄ΡƒΡ‚ ΠΎΡ‚Π»ΠΈΡ‡Π°ΡŽΡ‰ΠΈΠ΅ΡΡ значСния счётчика.

    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, Π·Π°Π½ΠΈΠΌΠ°ΡŽΡ‰Π΅ΠΉΡΡ Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠ΅ΠΉ ΠΈ Π΄Π΅ΡˆΠΈΡ„Ρ€Π°Ρ†ΠΈΠ΅ΠΉ:

    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, PyDERASN, NNCP, GoVPN, GOSTIM являСтся ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ свободным ПО, распространяСмым Π½Π° условиях GPLv3+.

    Π‘Π΅Ρ€Π³Π΅ΠΉ ΠœΠ°Ρ‚Π²Π΅Π΅Π², ΡˆΠΈΡ„Ρ€ΠΎΠΏΠ°Π½ΠΊ, Ρ‡Π»Π΅Π½ Π€ΠΎΠ½Π΄Π° БПО, Python/Go-Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ, Π³Π»Π°Π²Π½Ρ‹ΠΉ спСциалист Π€Π“Π£ΠŸ «НВЦ β€žΠΡ‚Π»Π°Ρβ€œ.

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com