Привет, %username%!

Не всё в этом мире крутится вокруг браузеров и бывают ситуации, когда TLS избыточен или вообще неприменим. Далеко не всегда есть необходимость в сертификатах, очень часто хватает обычных публичных ключей, взять тот же SSH.

А еще есть IoT, где впихивать TLS целиком это вообще задача не для слабонервных. И бэкенд, который, я почти уверен, у всех после балансера общается друг с другом по обычному HTTP. И P2P и еще и еще и еще…

Не так давно в сети появилась спецификация Noise Protocol Framework. Это по сути конструктор протоколов безопасной передачи данных, который простым языком описывает стадию хэндшейка и то, что происходит после неё. Автор — Trevor Perrin, ведущий разработчик мессенджера Signal, а сам Noise используется в WhatsApp. Так что, был отличный повод рассмотреть этот протокольный фреймворк поближе.

Он так понравился нам своей простотой и лаконичностью, что мы решили на его основе запилить аж целый новый протокол сетевого уровня, который не уступает TLS в безопасности, а в чём-то даже превосходит. Мы презентовали его на DEF CON 25, где он был очень тепло принят. Пора поговорить о нём и у нас.

Сначала немного о непосредственно ядре NoiseSocket, а именно

Noise Protocol Framework


По сути, любой из описанных Noise Framework протоколов представлет собой последовательность из передаваемых публичных ключей и производимых над ними операций Diffie-Hellman.

Главная идея Noise Protocol Framework в том, что абсолютно все действия во время хэндшейка влияют на состояние протокола, а следовательно и на итоговые сессионные ключи. Все DH, все дополнительные данные, которые передаются или учитываются во время рукопожатия, подмешиваются к общему состоянию с помощью хэширования и формируют в итоге общие симметричные ключи.

Всё это происходит внутри нехитрой системы состояний, которая состоит из трёх частей.



Есть, кстати видео, на английском, где David Wong довольно доступно рассказывает, как там чего работает.



HandshakeState отвечает за процессинг токенов и сообщений.

SymmetricState формирует симметричные ключи из результатов DH и обновляет их с каждым новым DH. Таким образом сразу же после самого первого DH данные, которые идут после него (статические ключи, полезная нагрузка), уже шифруются каким-то симметричным ключом.
Еще SymmetricStatе хэширует в себя дополнительные данные, такие как сами ключи, опциональный пролог, имена протоколов и т.д. Всё это позволяет протоколу быть целостному и защищенному от вмешательства извне на всех этапах передачи данных.

CipherState это просто инициализированный каким то ключом симметричный AEAD шифр + nonce (счетчик), который инкрементится с каждым вызовом функции шифрования.

Протоколы в Noise описаны специальным языком, который состоит из паттернов, сообщений и токенов



Рассмотрим для примера один из протоколов, Noise_XX, который позволяет установить защищенное соединение, обменявшись в процессе статическими ключами сервера и клиента:

Noise_XX(s, rs):
-> e
<- e, ee, s, es
-> s, se


Noise_XX — это паттерн. Он описывает последовательность сообщений и их содержимое.

(s, rs) обозначает, что клиент и сервер инициализируются своими статическими (s) ключевыми парами. Это те, что генерируются единожды. r означает remote.

Как мы видим тут есть три строчки со стрелками. Одна строка — одно сообщение. Стрелка означает кто кому шлёт. Если направо, то клиент серверу, иначе наоборот.

Каждая строка состоит из токенов. Это одно или двухбуквенные выражения, разделенные запятыми. Однобуквенные токены бывают лишь e и s и означают эфемерный и статический публичные ключи соответственно. Эфемерный генерируется один раз на соединение, статический многоразовый.
Вообще в Noise все протоколы начинаются с передачи эфемерного ключа. Таким образом достигается Perfect Forward Secrecy. Примерно то же самое придумали в TLS 1.3 когда отменили все не-эфемерные ciphersuites.

Двухбуквенные токены означают Diffie-Hellman между одним из ключей клиента и сервера. Они бывают, как несложно догадаться, четырех видов:
ee, es, se, ss. В зависимости от того, между какими ключами делается DH, он выполняет различные функции. ee, к примеру, нужен чтобы рендомизировать итоговый ключ для транспортной сессии, а DH с участием статических ключей отвечают за взаимную аутентификацию.

Как видно, в паттерне XX статический ключ клиента передаётся серверу, ровно как и наоборот. Поэтому тут и используется три сообщения. Есть паттерны, которые предполагают наличие у клиента статического ключа сервера (например, когда он первый раз сделал XX) и сокращение количества сообщений до двух. К тому же, появляется возможность передавать зашифрованные данные сразу в первом сообщении, что зовётся 0-RTT и сокращает время ответа от сервера.



К каждому handshake сообщению можно дописывать полезную нагрузку. Это могут быть настройки протокола верхнего уровня, те же сертификаты, просто цифровые подписи, в общем, всё что угодно в пределах 64к байт. Все Noise пакеты ограничены этим размером. Так упрощается парсинг, длина всегда помещается в 2 байта, проще работать с памятью.



В результате хэндшейка у нас на руках остаётся по сути лишь два симметричных ключа, которые являются результатом всех DH, произошедших ранее. Один для отправки сообщений, второй для их приёма. Всё, после этого мы можем отправлять шифрованные пакеты, инкрементируя nonce каждый раз после отправки.

Помимо паттерна Noise Protocol характеризуется алгоритмами, которые использует в каждом конкретном случае. В спецификации указаны алгоритмы для DH, AEAD и хэш. Большe Noise ничего не нужно.

DH: Curve25519, Curve448,
AEAD: AES-GCM, ChachaPoly1305,
Hash: Blake2, SHA2

Все примитивы очень быстрые, никаких RSA и прочего тормозного старья. Хотя, конечно, если хочется, можно сделать самому, никто не запрещает.

NoiseSocket


Увидели мы всю эту красоту и поняли, что это идеальный кандидат на роль транспортного протокола следующего поколения. Ведь тут сразу из коробки есть все нужные security фичи, производительность, возможность прикручивать свои механизмы аутентификации. А прогнизируемый размер кода должен позволить создавать нормальные защищенные соединения из любых, даже самых мелких девайсов. И начали думать.

Первый PoC я написал на Go где-то в районе нового 2017 года. Почти ничего не добавлял к оригинальному Noise, только длину пакетов. Показал это ребятам, написал в Noise Mailing List и к концу июня мы наконец пришли к общему знаменателю, который можно было реализовывать на большем количестве платформ.

Итак, что же мы в итоге добавили к Noise? После долгих разговоров и десятков вариантов остались по сути только три вещи:

  1. Negotiation data
  2. Padding
  3. Processing rules

Negotiation data



Это просто набор байт, в который вы можете положить всё что угодно. Он нужен для того, чтобы согласовать алгоритмы и паттерны между клиентом и сервером. В нашей версии он выглядит следующим образом:



Всего 6 байт, но этого достаточно чтобы сервер понял как обрабатывать любые Noise сообщения, которые к нему попадают.

Padding


Очень рад, что он попал в итоговую спеку, иначе всем бы пришлось придумывать его самим. Это выравнивание пакетов, которое позволяет скрыть их истинный размер и предотвратить угадывание содержимого даже без расшифровки. Реализуется как дополнительные 2 байта вначале зашифрованных данных, которые указывают на настоящий размер пакета. Всё, что после — мусор, который нужно выкинуть.



Processing rules


Это несколько несложных правил того, как сервер отвечает клиенту, если он принимает его сообщение или наоборот, не понимает его и хочет переключитсья на другой протокол.

Why?


В Virgil есть свой PKI и нам очень не хватало возможности устанавливать защищенные соединения сразу с использованием публичных ключей, а не с помощью сертификатов и потом сверху всё еще раз валидировать. А теперь мы сделали NGINX модуль и можем весь бекенд обслуживать через NoiseSocket, добавив к нему цифровые подписи статических ключей.

Вы наверное думаете, что нужно всё менять для того, чтобы перейти на NoiseSocket? А вот нет.
Если вы пишете на Go и у вас уже есть HTTP сервисы, то нужно лишь подменить метод DialTLS для клиентов и Listen для серверов, всё остальное будет думать, что работает по TLS. Это всё благодаря Go библиотеке, которую мы тоже реализовали.



Конечно, предстоит еще много работы по коду и спецификации, но, черт возьми, у нас есть альтернатива TLS!

Теперь не нужно выдумывать что-то своё когда вы хотите построить защищенный линк между двумя узлами, имея на руках лишь публичные ключи. Tor, i2p, bitcoin, где нода часто идентифицируется именно публичным ключом, могут использовать NoiseSocket сразу безо всяких добавок.

SSH, VPN, всевозможные туннели могут добавить цифровые подписи статических ключей и получить полноценный защищенный линк с минимальным оверхедом без необходимости тащить к себе openssl, можно обойтись Libsodium или вообще Nacl.

Приблизительный размер скомпилированных криптопримитивов конечно зависит от архитектуры, но вселяет надежду, что мы влезем в 30, а то и 20 килобайт с минимальной реализацией NoiseSocket. В девайсах, где приватный ключ зашит в железе, это решение просто не будет иметь особых альтернатив.

Заключение


У меня были большие надежды на TLS 1.3, так как там и handshake roundtrips уменьшены с 8-9 до трех, как в Noise, и добавили 25519. Но

Во-первых решили не добавлять возможность работать без сертификатов, просто по ключам, хотя такое предложение было.
Во-вторых, сертификаты ed25519 непонятно когда появятся, а в Noise я могу использовать подписи 25519 уже сегодня.

К тому же, не так давно один из паттернов Noise, IK (который 0-RTT) получил формальное доказательство корректности от авторов WireGuard VPN, что лишь усилило нашу уверенность в правильности выбора.

Я предлагаю ознакомиться со спецификацией NoiseSocket и уверен, в каких-то проектах он подойдет вам больше, чем TLS. А мы тем временем ждем ваших комментариев.

Ссылки


Спецификация Noise Socket
Github
Спецификация Noise Protocol Framework
Поделиться с друзьями
-->

Комментарии (65)


  1. saintbyte
    01.08.2017 11:25
    -5

    прикрутили Diffie-Hellman алгоритм к tcp — впрочем нового ноль, раньше это называлось pgp и вместо tcp была электронная почта.


  1. Disasm
    01.08.2017 12:05
    -2

    Я смотрю, в последние годы стало можно совать Curve25519 во всё подряд не разбираясь.


    1. Scratch
      01.08.2017 12:08
      +1

      Поясните, если не затруднит, в чём именно мы не разобрались?


      1. Disasm
        01.08.2017 12:47
        -4

        Моё личное мнение таково, что Curve25519 это абсолютно дилетантская крипта, придуманная исключительно скорости ради. И вся статья о Curve25519 тому яркое подтверждение.


        1. Scratch
          01.08.2017 12:56
          +8

          Не знаю о какой из сотен статей о curve25519 вы говорите, но на сегодня это по сути стандарт, который включен в том числе в TLS ciphersuites. Предлагаю пройти в CFRG Mailing list и предупредить всех о том, что они включили в стандарт дилетантскую крипту.


          1. Disasm
            01.08.2017 13:04
            -2

            Я говорю о самой первой статье: Curve25519: new Diffie-Hellman speed records (https://cr.yp.to/ecdh/curve25519-20060209.pdf)
            И не знаю, о каких сотнях статей вы говорите, я их вижу здесь только шесть:
            https://cr.yp.to/ecdh.html#papers


            Аргумент в стиле "тысячи мух не могут ошибаться" не принимается. Покажите хоть одну нормальную статью про исследование безопасности этой кривой.


            1. Scratch
              01.08.2017 14:00
              +2

              Это не «какая-то еще одна кривая», а вполне себе обычная кривая монтгомери, у которой порядок — простое число 2255 — 19. Одного этого факта достаточно, чтобы утверждать о её безопасности. Есть конечно слабые кривые, но их довольно просто вычислить. Вот еще доп информация на этот счет


              1. dakuan
                01.08.2017 18:10

                Одобряю то, что вы делаете, и тоже давно слежу за проектами Noise и Wireguard. Но все же не могу не заметить, что в криптографии с заявлениями типа «Одного этого факта достаточно, чтобы утверждать о её безопасности» нужно быть крайне осторожным.


                1. Scratch
                  01.08.2017 18:12

                  Справедливо. Но тут так же работает презумция невиновности. По ссылке есть критерии слабых кривых, 25519 под них очевидно не подходит. Если есть какие то другие критерии, то они ударят не только по ней, но скорее и по всем остальным


                  1. Labunsky
                    01.08.2017 21:50
                    +1

                    Простите, вклинюсь, просто зацепился взглядом за это:

                    Но тут так же работает презумция невиновности
                    Но это не так. Наоборот, в криптографии работает презумция виновности, в которой «все опасно, что не доказательно безопасно теоретически или хоть практически»


                    1. Scratch
                      01.08.2017 21:58

                      Кроме одноразового блокнота ни один из алгоритмов не имеет 100% доказательства стойкости, по сути мы все верим криптографам на слово. Я это имел в виду


                      1. Labunsky
                        01.08.2017 22:01

                        Имеют, не обязательно же иметь абсолютную стойкость. Достаточно гарантированной, с доказательством «что, да как и почему»


  1. j_wayne
    01.08.2017 12:14

    ОК, это интересно, но вы говорите про IoT — а есть реализация на С/С++ для эмбеда?


    1. Scratch
      01.08.2017 12:18
      +2

      Библиотека в процессе разработки, я поэтому и привел приблизительные цифры, собранные по разным имплементациям примитивов. А под какую архитектуру интересно?


      1. j_wayne
        01.08.2017 12:45

        AVR/ESP8266/ARM


        1. Scratch
          01.08.2017 12:52
          +2

          Да, это то над чем мы работаем. Как только появятся результаты мы опубликуем или тут или на гитхабе


    1. Nikolay_Ch
      01.08.2017 18:10

      В IoT проблема скорее не в секурной передаче, а в удостоверении, что передатчик именно тот, кто имеет на это право (контроллер или датчик). Шифрование передачи здесь не поможет.


      1. Scratch
        01.08.2017 18:16
        +1

        Если датчик передаёт что-то полезное для потенциального взломщика, то лучше данные зашифровать. Тот же датчик присутствия, например


      1. j_wayne
        01.08.2017 18:17

        Смотря что передавать. Да и тот же указанный в статье AES-GCM — режим шифрования с аутентификацией. Два в одном.


      1. eov
        02.08.2017 11:15

        На сколько я понимаю, применять дополнительное шифрование для IoT не нужно. Оно только будет увеличивать энергопотребление и снижать время работы до замены элемента питания. В табличках вот здесь есть строка шифрование.


        1. Scratch
          02.08.2017 11:22

          Есть-то оно есть, но во-первых там, условно, «AES», я ничего не нашел про нормальную ассиметричную криптографию. Во-вторых на беспроводным IoT мир не заканчивается, есть еще PLC и там тоже нужна защита


  1. stargrave2
    01.08.2017 13:16
    +2

    Огромное спасибо что привлекаете внимание к Noise framework-у! Среди всех протоколов которые стремятся к стандартизации, на мой взгляд, Noise наиболее продуман и прост. Подписан на их рассылку почти с самого зарождения и очень нравится их подходы: серьёзны, консервативны, вдумчивы. Самое главное: его стараются делать максимально простым и разумно гибким, без 100500 опций и возможностей. Если бы кто-то спрашивал какой ему протокол выбрать для задачи где надо обеспечить защиту трафика и аутентификацию собеседников, то в 99.99% случаев одного из Noise pattern-ов им будет достаточно.


    1. Scratch
      01.08.2017 14:04
      +1

      мы очень хотим когда-нибудь сделать Noise стандартом и NoiseSocket — первый шаг


  1. hdfan2
    01.08.2017 13:19

    xkcd://927


  1. Ivan_83
    01.08.2017 15:30
    +1

    Поди и туда АНБ напихало «особенностей реализации/применения».

    Схемы, картинки и свой язык (прямо как у сектантов) — это наверное хорошо, но ещё ни о чём не говорит, ровно как и аудиты и прочие реферансы от тех кому лично не доверяешь.

    2 Disasm
    DJB же писал что основные фичи:
    1. стойкость к тайминг атакам
    2. скорость работы
    Я когда начинал въезжать в тему тоже несколько смутился его State-of-art и запилил для себя стандартную ECDSA.
    Да, ECDSA почти не реально сделать выполняемой всегда за одно и тоже время, в этом DJB прав.
    И там ходят слухи про параметры всякие разные, особенно те которые появились после 1999 года…

    2 Scratch
    Я не математик и для меня совсем не очевидно что кривая какого то Монтгомери с параметрами (2^255)-19 безопасна.
    И даже более того, не факт что не существует каких то хитрых решений для каких то отдельных кривых, например этой.

    Как не математик я уверен только в одном: есть на этом шарике всё ещё противостоящие друг другу школы/державы, и для более-менее уверенности нужно брать не один набор крипты одного вендора/школы а целую пачку, и использовать всё сразу.
    И похер что ключи будут большие, и шифрование медленное, зато хрен его просто так сломаешь, даже если где то и есть закладки.

    RSA хоть и тормозной, но есть гипотеза что он более стойкий чем ECDSA и 25519 за счёт того что у него разрядность сильно выше, как минимум квантовые компы под него обойдутся сильно дороже.

    Ну и последнее.
    Я не верю людям (в их честность, и добрые намерения) которые активно вкладываются в разработку месенгеров прибитых гвоздями к номеру телефона.
    Телефония это настолько гнилая тема что дальше некуда.
    Весь перар вокруг сигнала это волна чтобы задавить токс и битмесадж, которые предоставляют своим пользователям реальную свободу и анонимность.
    А касательно безопасности: мобилки и безопасность вещи не совместимые в принципе.


    1. stek29
      01.08.2017 18:09

      RSA хоть и тормозной, но есть гипотеза что он более стойкий чем ECDSA и 25519 за счёт того что у него разрядность сильно выше, как минимум квантовые компы под него обойдутся сильно дороже.

      Кривые на квантовых компьютерах ещё быстро ломать не умеют, в отличие от того же RSA.


      Согласен насчёт паранойи насчёт Signal — уж слишком его пиарят, аж подозрительно. Даже если там нет никаких закладок, и вообще он великолепен, слишком привязываться к одному транспорту нельзя.


      1. BalinTomsk
        02.08.2017 00:07

        Есть тут обшее правило, которое я закладываю в архитектуру.

        Если государтво продвигает стандарт с длиной ключа ХХХ, то скорее всего, он знает как его ломать, поэтому рекомендую ключ ХХХ + NNN.

        Поскольку можно допустить что алгоритм имеет backdoor независимо от длины ключа, то дополнительно нужно криптовать данные алгоритмом конкуририющей страны.

        Например АЕS 512 + ГОСТ Р 34.10-2001 512 вполне покрывают большинство потребностей.


        1. varnav
          02.08.2017 14:03
          +1

          Но AES 512 не существует!


      1. varnav
        02.08.2017 14:01

        Странно, в Википедии написано противоположное про квантовую стойкость кривых:

        https://en.wikipedia.org/wiki/Elliptic_curve_cryptography#Quantum_computing_attacks


  1. nazarpc
    01.08.2017 17:26

    Хм… вот это бы на С/С++ и в WebAssembly для браузеров скомпилировать, как раз работаю над одной штуковиной, где всё это будет нужно, очень не хотелось бы изобретать велосипеды.


  1. bormental
    01.08.2017 17:28

    В целом, Noise приятный протокол. Но, увы, есть некоторые ограничения. Например:


    All Noise messages are less than or equal to 65535 bytes in length.

    Я понимаю доводы авторов. Но, тем не менее, для некоторых условий это ограничение слишком жестко. До неприемлемости.


    1. Scratch
      01.08.2017 18:08

      А это на самом деле легко делается, можно о размере пакета договориться во время хэндшейка. В спеку не вошло, но никто не мешает сделать самому. Мы тоже скоро выложим «наш» вариант NoiseSocket, возможно там появится в том числе и ограничение на размер пакета


      1. bormental
        01.08.2017 18:46

        это на самом деле легко делается, можно о размере пакета договориться во время хэндшейка

        Не совсем понял, что Вы имеете в виду под "это". Изменить спеку? Изменить какую-то стороннюю (или свою) опенсорсную реализацию протокола? Или что-то уже предусмотрено на уровне хэндшейка о чем Вы вскользь упомянули и что я, по-видимому, упустил?


        1. Scratch
          01.08.2017 19:04

          Спецификация протокола позволяет писать друг другу любые данные во время хэндшейка внутри полезной нагрузки. Можно добавить туда максимальный размер пакета и учитывать его во время транспортной фазы.


          1. bormental
            01.08.2017 19:16

            То, что в хэндшейк можно добавить прикладную информацию — это хорошо.
            Но как это повлияет на жестко заданную в спеке длину мессаджей Noise? Как его парсер узнает, что мы хотим иметь длину более 65К?
            Я понимаю, что можно внутрь Noise инкапсулировать свой прикладной протокол и резать его на любые мессаджи по-своему. Но хотелось бы как раз обойтись, т.е. сделать так, чтобы мессадж своего протокола целиком помещался внутрь мессаджа Noise.


            1. Scratch
              01.08.2017 19:26

              Вон оно что, я сразу не понял вопроса. Потому что IoTшники наборот хотят, чтобы пакет был не больше 2х кб, например.

              Вообще да, разбивать придется в любом случае, т.к. подразумевается что мы там и гигабайты можем слать. Это не так уж и сложно, вполне соответствует семантике ReadXXX/WriteXXX. Вот как я например сделал в go
              А читать из этого коннекшена и писать в него можно буферами любого размера.


              1. bormental
                01.08.2017 19:37

                Это не так уж и сложно

                Разумеется :) Мы сейчас TLS используем. На скоростях существенно выше 10Gbps. И тут каждое копирование память-память (что есть и в Вашем примере) стоит дорого. Хотелось бы избегать этого. Я рассматривал Noise как-то, как альтернативу TLS, но вот то ограничение и не понравилось.
                Понятно, что в "80%" случаев 65К будет достаточно, но вот нам тесты дают несколько другое оптимальное значение размера мессаджа в нашем приложении — 2М. Было бы неплохо, если б в протокол добавили бы, например, в тот же хэндшейк, scale поля длины мессаджа. Меня б такой "костыль" устроил бы более чем ;-)


                1. Scratch
                  01.08.2017 19:48

                  в TLS же вообще 16кб, если мне не изменяет память. Но я вас понял, подумаем )
                  Заходите в Noise Mailing List, там можно это дальше обсудить


                  1. Scratch
                    01.08.2017 19:54

                    Ну и раз уж на то пошло, то транспортный пакет — это всего лишь AEAD шифротекст с длиной спереди. Да, в либах этого нет, но по сути можно делать эти шифротексты хоть по гигабайту размером и спереди писать не 2, а 3 или 4 байта длины. Как вы можете видеть, NoiseSocket больше концепция, а не законченный протокол.


                    1. bormental
                      01.08.2017 20:04

                      NoiseSocket больше концепция, а не законченный протокол.

                      Ну, для нас больше был актуален Noise в чистом виде, т.к. у нас всё сильно C++&boost::asio зависимо. Т.е. внутри приложения нечто своё, похожее на NoiceSocket, работающее к тому же через несколько параллельных TCP-коннектов...


                  1. bormental
                    01.08.2017 19:58

                    в TLS же вообще 16кб

                    Угу, там есть много ещё другой боли.


                    Заходите в Noise Mailing List, там можно это дальше обсудить

                    Спасибо. Как только опять вернемся к вопросу замены-ускорения TLS у нас...


  1. Sklott
    01.08.2017 18:08

    А зачем в NoiseSocket настолько легко-определимый пролог? С таким прологом вам разработчики DPI только спасибо скажут.


    1. Scratch
      01.08.2017 18:14

      не легче, чем в TLS. Можно если есть желание его обфусцировать, конечно. Спека это не регламентирует


      1. Sklott
        02.08.2017 09:28

        Да я про то в общем-то и говорю, что спека Noise достаточно устойчива к DPI и даже дает рекомендации как обфусцировать публичные ключи которые всегда посылаются в открытом виде.
        А вот в спеке NoiseSocket четко в прологе предлагается слать некороткую специфическую ASCII строку.


        1. Scratch
          02.08.2017 10:02

          Ну не Ascii, просто данные в открытом виде. Но ведь нужно как то согласовать используемые алгоритмы пока еще не было никакого обмена ключами. Можно этого и не делать, если вы заранее знаете что к вам придёт. А иначе как?


          1. Sklott
            02.08.2017 10:21

            Ээээ… Мы об одном и том-же говорим? В спецификации NoiseSocket, в секции 4, прямо говорится что пролог должен начинаться с: The UTF-8 string «NoiseSocketInit1»


            1. Scratch
              02.08.2017 11:09

              пролог! Это не negotiation_data, это, условно, строка, которую мы скармливаем фреймворку, чтобы состояние на обоих концах было одинаковое. Её передавать не нужно, её нужно сконкатенировать


              1. Sklott
                02.08.2017 11:14

                Ясно. Спасибо за разъяснение.


                1. Scratch
                  02.08.2017 11:29

                  обращайтесь )


  1. F0iL
    01.08.2017 20:06
    -1

    > Это выравнивание пакетов, которое позволяет скрыть их истинный размер и предотвратить угадывание содержимого даже без расшифровки.

    Не совсем понятно, а что мешает тому, кто перехватывает сообщения, использовать соответствующее поле в пакете и анализировать уже данные с отброшенным «мусором»?


    1. Scratch
      01.08.2017 20:19
      +2

      это поле находится внутри зашифрованных данных, без расшифровки никак. Там даже слайд про это есть )


  1. Scf
    02.08.2017 10:07

    Поправьте меня, если я неправ, но у предлагаемого решения ровно одно преимущество перед своим протоколом, завернутым в TLS: более быстрое установление соединения.


    1. Sklott
      02.08.2017 11:01

      Не только. Преимуществ много, и в статье упомянуты:
      — не нужен сертификат
      — сильно проще реализация (жрет гораздо меньше памяти)
      — более гибкая настройка хендшейка в зависимости от ваших требований

      По поводу реализации, где-то лет около 15 назад затаскивали OpenSSL на один из первых сотовых телефонов. Так вот, в телефоне было что-то около 1Мб RAM, сейчас уже точно не вспомню. Одна SSL сессия «кушала» где-то порядка 150-200 Кб. Понятно что это было абсолютно неприемлемо и проект умер не родившись.
      Для Noise как уже говорилось достаточно несколько Кб.


  1. mwizard
    02.08.2017 12:43

    Как уже упомянул Sklott несколькими комментариями выше, этот протокол очень легко обнаружить по хендшейку и отфильтровать посредством DPI. Несколько лет назад у меня возникла идея, как можно с этим бороться и сделать создание правила для DPI-фильтра невозможным на практике.


    Суть идеи заключается в том, чтобы вместо открытых данных пересылать случайные данные, но которые можно по некоторому заранее известному, желательно трудозатратному алгоритму без секретной компоненты, преобразовать обратно в открытые данные.


    Таким образом, это не добавит какой-либо секретности в пересылаемые данные, но затруднит их непосредственный анализ без предварительной обработки, которая в масштабах глобального прослушивания повлечет за собой как минимум ложнопозитивные сработки, и как максимум — невозможность справиться с потоком данных с точки зрения требуемых ресурсов.


    Для кодирования следует разбить открытые данные на чанки по N бит, затем для каждого чанка нужно брутфорсом подобрать такой случайный блок данных фиксированного размера H, который при декодировании превратится в ожидаемый чанк открытых данных в N бит размером. Пример такой процедуры декодирования — циклически посчитать SHA256 от блока случайных данных 1000 раз, и затем вернуть младшие N бит хэша.




    Приведу пример декодирования готовой строки. Допустим, у нас есть следующие бинарные данные:


    4b 82 93 6a b5 0f 39 9b 33 18 2a 7d b4 96 c2 f8 | K..j..9.3.*}....
    cb 57 49 b7 67 6d f6 c2 9a e2 73 64 54 1f 92 7d | .WI.gm....sdT..}
    65 44 12 7e de 07 c2 85 41 15 9e 32 28 3b c5 3a | eD.~....A..2(;.:
    0f 16 3a d9                                     | ..:.

    Мы заранее знаем, что открытый текст делился на блоки по 8 бит каждый, и что мы использовали блоки "шифротекста" по 32 бита.


    Первый блок — 4b 82 93 6a.
    SHA256 от него = 486b84b02736885238a0d8ba871a78262362f1dfe7422f1d50b257ed6c99bcc3.
    Младшие 8 байт этого хэша в little endian = 0x48, т.е. "H" в ASCII.


    Следующий блок — b5 0f 39 9b.
    SHA256 от него = 6546ec1ccea61e68c70671b6a04d9a074c0bf024f92d72d9103e78c3641801eb.
    Младшие 8 байт этого хэша в little endian = 0x65, т.е. "e" в ASCII.


    Продолжая декодирование таким же образом, получим строку "Hello, world!".




    И для демонстрации, я сделал наивную PoC-реализацию описанного выше подхода на Python: https://gist.github.com/toriningen/7c56c262bff38fda40b5cb6a014b87a2


    1. mwizard
      02.08.2017 12:47

      Хотя, возможно, мое замечание нерелевантно… но мне тогда жалко удалять написанное.


      1. Sklott
        02.08.2017 13:15

        Вполне релевантно. Просто я думал что там совсем все плохо, но оказалось что там «слегка все плохо» :)
        Т.е. все равно есть немного данных поддающихся некоторому анализу и скрыть их все же хотелось бы.

        Но с другой стороны, не совсем понятно зачем для такого скрытия делать именно «трудозатратный» алгоритм. Мне кажется достаточно сделать так чтобы количество ложнопозитивных срабатываний было достаточно большим. А иначе теряется смысл использования в некоторых приложениях (IoT и т.п.)


      1. Sklott
        02.08.2017 13:24

        Дополню. Хотя ваш алгоритм и «трудозатратный», но с другой стороны он не увеличивает количество ложнопозитивных срабатываний, а наоборот уменьшает их.

        Что я имею в виду. Если мы знаем алгоритм и применяем его ко всем пакетам, то с очень большой вероятностью только «ваши» пакеты на выходе дадут что-то осмысленное, т.к. вероятность что что-то осмысленное получится после 1000 раундов SHA256 очень мала, а умножив это на количество блоков мы получим вообще исчезающе малую величину.

        Мне кажется чтобы увеличить количество ложноположительных срабатываний нужно наоборот использовать «нестойкие» алгоритмы которые с большей вероятностью будут выдавать нечто «осмысленное».


        1. mwizard
          03.08.2017 09:46

          т.к. вероятность что что-то осмысленное получится после 1000 раундов SHA256 очень мала, а умножив это на количество блоков мы получим вообще исчезающе малую величину.

          Вы не совсем правы. В предложенной мной схеме речь не идет о поиске такого прообраза, после хэширования которого мы получим "осмысленный пакет" (т.е. некую фиксированную битовую последовательность) — вероятность угадать такой прообраз, учитывая, что выхлоп сильной хэш-функции можно полагать случайным, составляет 2**(-N), где N — длина искомого "смысла". 100-битный осмысленный пакет можно искать неимоверно долго, и абсолютно впустую — добывать биткоины при таком количестве свободного времени и ресурсов явно выгоднее :)


          Предложенную схему не стоит рассматривать как средство сокрытия информации — очевидно же, что если ваш пакет начинается с "Lorem ipsum" (88 бит), то каждый 309485009821345068724781056-й в среднем пакет должен будет "расшировываться" в "Lorem ipsum", и если наблюдаемая частота таких сообщений значительно превышает ожидаемый среднестатистический, то это "бжжж" тут явно неспроста.


          Суть же предложенной мной схемы как раз заключается в том, чтобы сделать слежку невозможной именно в глобальных масштабах, основываясь на, скажем, эдаком proof-of-work. Если невозможно заранее, глядя только на мусорные блоки, отличить их от настоящего мусора, то атакующей стороне в любом случае потребуется потратить энное количество ватт (времени, денег) на вычисление хэш-функции от этого мусорного блока.


          И если поток сквозных данных превысит возможности атакующей стороны по вычислению хэшей, им придется либо пропускать часть трафика, либо в принципе отказаться от идеи вычислять хэши для произвольных данных. Причем инициатору соединения даже необязательно использовать истинно случайные блоки — можно притворяться чем-то другим, лишь бы все еще было достаточно степеней свободы для подбора хэша с требуемыми свойствами.


          Утрируя, мы можем передавать 1 бит данных в 128 битах мусора. Уже даже с такой избыточностью невозможно составить таблицу соответствия мусорных данных истинным данным, значит, DPI-фильтру придется "играть честно", и чем сложнее будет для вычисления хэш-функция (pbkdf2? scrypt? bcrypt?), тем меньшее количество сообщений выйдет декодировать, потратив 1 кВт, и тем быстрее правительству^W атакующей стороне это надоест.


          Ну а процент "ложноположительных срабатываний" у этой схемы и так статистически неотличим от шума, куда уж лучше?


          1. Sklott
            03.08.2017 10:18

            В принципе вы наверно правы. В том смысле что если мы получаем из 4-х байт «шума» 1 байт «выхода», то теоретически неважно каким алгоритмом мы это делаем. Просто интуитивно кажется, что чем «навороченней» алгоритм, тем менее вероятно случайное совпадение ;)


        1. mwizard
          03.08.2017 09:53

          Насчет IoT — насколько вероятна угроза цензуры общения между IoT-устройствами? Если не слишком вероятна, то, возможно, подобные меры просто не стоит в них использовать?


          Например, "десктопное" устройство может сначала пытаться работать с прологом, как с настоящим пакетом, и в случае некорректных входящих данных, пытаться декодировать его, как пакет с мусорными блоками. IoT-устройства могут даже не пытаться декодировать мусор и просто считать это некорректной передачей.


          1. Sklott
            03.08.2017 10:17

            Ну тут IoT по большому счету просто в качестве примера «слабой» железяки. Да и вообще кто его знает как оно в жизни сложится.


  1. Sklott
    02.08.2017 15:10

    Scratch, почитал повнимательней спеки, и вот какой у меня народился вопрос:
    а почему negotiation_data передается перед noise_message, да еще и в открытом виде, вместо того чтобы передаваться например как message_payload, да еще желательно только после начала шифрования.

    Или, как вариант, почему сначала не установить безопасный канал, например по Noise_NN, а в нем уже договориться о дальнейшем протоколе и не провести re-handshake по новому протоколу?


    1. Scratch
      02.08.2017 16:44

      Мы можем не смочь прочитать первый payload, потому что там могут быть
      1) разные кривые, хэши и алгоритмы
      2) Noise_IK, у которого payload сразу зашифрован

      мы должны знать паттерн и алгоритмы заранее, иначе ничего не прочитается. NN конечно можно было бы сделать, но это лишний раундтрип и нагрузка на CPU.


  1. rumkin
    02.08.2017 20:32

    Только собрался писать статью о том как шифровать соединение с применением DH на nodejs. А тут оказывается и велосипед изобретать не придется. Можете посоветовать проверенный пакет под nodejs?


    1. Scratch
      02.08.2017 20:39

      Насколько мне известно, на JS Noise еще никто не сделал. Можете стать первым ) Спека самого нойза приятно читается и реализовывать её одно удовольствие. Пока поддерживается только C, Java, Go, Haskell и Rust.