BIP47, гадкий утёнок |
Главная Top BIP47, гадкий утёнок
BIP47, гадкий утёнок
От vargoz — 21.11.2022 0 ПОДЕЛИТЬСЯ Facebook Twitter
Одно из главных бедствий протокола Bitcoin — повторное использование адресов. Прозрачность и распределенность сети делает эту практику опасной для конфиденциальности пользователей. Чтобы избежать связанных с этим проблем, рекомендуется для получения каждого нового платежа использовать новый пустой адрес […]
«Он слишком крупный, — сказали они все, и индийский петух, который, казалось, родился со шпорами и считал себя императором, надулся, как судно со всеми поднятыми парусами, и пошел прямо на него с красными от великой ярости глазами. Бедный утенок не знал, остановиться ли ему или идти: ему было горько оттого, что все утки двора презирали его».
Одно из главных бедствий протокола Bitcoin — повторное использование адресов. Прозрачность и распределенность сети делает эту практику опасной для конфиденциальности пользователей. Чтобы избежать связанных с этим проблем, рекомендуется использовать новый пустой адрес для получения каждого нового платежа, что в некоторых случаях может быть затруднительно.
Этот компромисс стар, как сама уайтпейпер Накамото. Уже в ней Сатоши предупреждал о рисках повторного использования адресов:
«В качестве дополнительной меры безопасности для каждой транзакции следует использовать новую пару ключей, чтобы они не были связаны с общим владельцем».
Существует множество решений для получения нескольких платежей без переиспользования адреса, каждое со своими компромиссами и недостатками. Одно из них — BIP47, предложение по реализации в Биткойне многоразовых платежных кодов, разработанное Юстусом Ранвье и опубликованное еще в 2015 году. Цель — позволить проводить несколько транзакций с одним и тем же пользователем без повторного использования адреса.
Первоначально это предложение было очень холодно принято частью сообщества, и так и не было добавлено в Bitcoin Core. До сих пор некоторые программы предпочитают внедрять его самостоятельно. Так, Samourai Wallet разработал собственную реализацию BIP47: PayNym. Сегодня эта реализация доступна, очевидно, в Samourai Wallet для смартфонов и Sparrow Wallet для ПК.
Со временем Samourai стали развивать и новые функции, непосредственно связанные с PayNym. К сегодняшнему дню они предлагают уже целую экосистему инструментов для оптимизации конфиденциальности пользователей на основе PayNym и BIP47.
В этой статье я расскажу подробно о принципах BIP47 и PayNym, механизмах работы этих протоколов и некоторых практических приложениях, которые из них следуют. Я здесь буду говорить только о первой версии BIP47, той, что сейчас используется для PayNym, однако версии 2, 3 и 4 работают практически таким же образом.
Единственное существенное различие заключается в транзакции уведомления. В v1 для уведомления используется простой адрес с OP_RETURN, в v2 – multisig-скрипт (bloom-multisig) с OP_RETURN, а в v3 и v4 — просто multisig-скрипт (cfilter-multisig). Поэтому описываемые в этой статье механизмы, включая криптографические методы, применимы ко всем четырем версиям. На сегодня в реализациях PayNym в Samourai и Sparrow Wallet используется первая версия BIP47.
Навигация:
- Проблема повторного использования адресов →
- Принципы работы BIP47 и PayNym →
- Инструкции: использование PayNym →
- Построение BIP47-транзакции в Samourai Wallet →
- Построение BIP47-транзакции в Sparrow Wallet →
- Как работает BIP47 →
- Многоразовый платежный код →
- Криптографический метод: Elliptic-Curve Diffie-Hellman (ECDH) →
- Транзакция уведомления →
- Построение транзакции уведомления →
- Получение транзакции уведомления →
- Платежная транзакция BIP47 →
- Получение BIP47-платежа и получение закрытого ключа →
- Возврат BIP47-платежа →
- Производные варианты использования PayNym →
- Мое мнение о BIP47 →
Проблема повторного использования адресов
Адрес получения используется для приема входящих биткойн-транзакций. Он генерируется из открытого ключа путем хеширования и применения к нему определенного формата. Это позволяет для каждого входящего платежа создавать новое условие расходования средств, изменяя его владельца.
Кроме того, вы наверняка уже слышали от опытных и информированных биткойнеров о том, что адреса получения предназначены для одноразового использования, и потому для каждого нового входящего платежа необходимо генерировать новый адрес. Ладно, но почему так?
В принципе, прямой угрозы вашим средствам повторное использование адресов не несет. Использование эллиптической криптографии позволяет доказать сети, что вы владеете закрытым ключом, без раскрытия самого ключа. Так что вы можете заблокировать на одном адресе несколько UTXO и потратить их в разное время. Это не откроет никому доступ к вашим средствам, если только вы не раскроете свой закрытый ключ, связанный с этим адресом. Но повторное использование адресов создает проблему с точки зрения конфиденциальности.
Как уже упоминалось выше, прозрачность и распределенность сети Bitcoin подразумевает, что любой пользователь, имеющий доступ к узлу сети, может наблюдать за всеми транзакциями платежной системы. Как результат, он может видеть и балансы адресов. Сатоши Накамото упоминал о возможности генерировать новые пары ключей — а значит, и новые адреса — для каждого нового платежа, поступающего в кошелек. Цель состоит в том, чтобы иметь дополнительную защиту — своего рода брандмауэр — на случай связывания идентификационных данных пользователя с одной из пар его ключей.
Сегодня, с развитием специализирующихся в блокчейн-анализе компаний и распространением на крипторынке практик KYC, генерирование новых пустых адресов для каждой новой транзакции является уже не дополнительной мерой безопасности, но обязательной мерой для каждого, кто хоть минимально беспокоится о собственной конфиденциальности.
Стремление к приватности — это не комфорт или фантазия биткойнера-максималиста. Этот параметр напрямую влияет на вашу личную безопасность и безопасность ваших средств. Приведу очень конкретный пример:
- Боб покупает биткойны понемногу через регулярные промежутки времени, чтобы сгладить цену входа (усреднение долларовой стоимости). Он систематически отправляет приобретенные средства на один и тот же адрес получения. Покупает каждую неделю по 0,01 BTC и отправляет их на один и тот же адрес. Через два года Боб накопил на этом адресе полный BTC.
- В пекарне за углом принимают оплату в BTC. Довольный возможностью удобно тратить BTC на повседневные нужды, Боб отправляется покупать булочку за сатоши. Для оплаты он использует сатоши с того же своего биткойн-адреса. Теперь пекарь знает, что Боб владеет биткойнами. Большой баланс кошелька (а пекарь может его посмотреть в один-два клика) может вызывать у людей зависть, и Боб рискует подвергнуться физическому нападению.
Повторное использование адресов позволяет наблюдателю установить неоспоримую связь между различными вашими UTXO, и иногда это значит — между вашей личностью и всем балансом вашего биткойн-кошелька.
Именно по этой причине большинство биткойн-кошельков автоматически генерируют новый адрес получения всякий раз, когда вы нажимаете кнопку «Получить». Поэтому для простого пользователя некастодиального кошелька необходимость использовать новые пустые адреса часто не доставляет больших неудобств. Однако для онлайн-бизнеса, биржи или кампании по сбору средств это ограничение может быстро стать неуправляемым.
Для таких организаций существует множество решений, каждое со своими преимуществами и недостатками, но на сегодняшний день и как мы увидим далее, BIP47 действительно выделяется из остальных.
Проблему повторного использования адресов в Биткойне не следует недооценивать. Как видно из приведенного ниже графика с oxt.me, общая доля повторного использования адресов пользователями Биткойна в настоящее время составляет ~50%:
Источник: https://oxt.me/charts
Большая часть таких случаев повторного использования приходится на биржи, которые при этом руководствуются соображениями простоты и эффективности. И на сегодняшний день BIP47 является наилучшим решением для пресечения этого явления на биржах. Переход биржевых кошельков на этот стандарт позволил бы значительно снизить общий уровень повторного использования адресов, не создавая больших сложностей для этих субъектов сети. Измерение общего показателя по сети в данном случае совершенно уместно. Действительно, переиспользование адреса является проблемой не только для его обладателя, но и для всех, кто отправляет транзакции на этот адрес. Утрата конфиденциальности в Биткойне действует как вирус, распространяясь от пользователя к пользователю. Отслеживание общего показателя для всех транзакций сети позволяет нам осознать масштабы явления.
Принципы работы BIP47 и PayNym
Цель BIP47 состоит в том, чтобы предложить пользователям простой способ получать большое количество платежей без повторного использования адресов. Принцип его работы основан на использовании многоразового платежного кода.
Протокол позволяет отправлять множество платежей на один многократно используемый платежный код без необходимости для получателя передавать новый пустой адрес для каждой новой транзакции.
Пользователь может свободно публиковать такой платежный код в социальных сетях или на сайте без риска потери конфиденциальности, в отличие от классического биткойн-адреса или открытого ключа.
Для совершения обмена обоим пользователям необходимо иметь биткойн-кошелек с поддержкой BIP47, как, например, PayNym в Samurai или Sparrow Wallet. Связывание платежных кодов двух пользователей позволит установить между ними секретный канал. Для этого отправителю понадобится выполнить транзакцию-уведомление в блокчейне Биткойна; далее я расскажу об этом подробнее.
Связывание платежных кодов двух пользователей создает совместный секрет, позволяющий генерировать большое количество уникальных адресов получения BTC (2^32, если быть точным). Таким образом, в действительности платеж с BIP47 отправляется не на платежный код, а на вполне классического вида адреса, генерируемые из платежных кодов протагонистов.
То есть платежный код работает как виртуальный идентификатор, полученный из seed-значения кошелька. В структуре деривации HD-кошелька платежный код находится на глубине 3, на уровне счетов кошелька.
Его цель деривации отмечена как 47′ (0x8000002F), от BIP47. Путь деривации многоразового платежного кода будет, например, таким:
m/47’/0’/0’/
И для примера того, как выглядит сам платежный код, вот мой:
PM8TJSBiQmNQDwTogMAbyqJe2PE2kQXjtgh88MRTxsrnHC8zpEtJ8j7Aj628oUFk8X6P5rJ7P5qDudE4Hwq9JXSRzGcZJbdJAjM9oVQ1UKU5j2nr7VR5
Он также может быть закодирован в QR-код для облегчения коммуникации:
Что касается PayNym-ботов — картинок с роботами, которые можно встретить в криптотвиттере, — то это просто визуальные представления вашего платежного кода, сгенерированные Samourai Wallet. Они создаются с помощью хеш-функции, что делает каждое такое изображение уникальным. Вот так выглядит мой, с идентификатором:
+throbbingpond8B1
Эти «боты» не имеют реального технического применения. Они лишь облегчают взаимодействие между пользователями, создавая виртуальную визуальную идентичность.
Для пользователя процесс платежа по BIP47 через PayNym выглядит очень просто. Допустим, Алиса хочет отправить несколько платежей Бобу:
Транзакция уведомления, т.е. установление соединения между вашим PayNym и PayNym получателя, является обязательным предварительным шагом для осуществления BIP47-платежей. С другой стороны, как только это будет сделано, отправитель сможет совершить множество (2^32) платежей получателю, не беспокоясь о повторном использовании адресов и без необходимости в повторной транзакции уведомления.
Как видите, есть две раздельные операции, позволяющие установить соединение между PayNym пользователей: Follow и Connect. Операция установления соединения («Connect») соответствует транзакции уведомления по BIP47, которая представляет собой простую биткойн-транзакцию с определенной информацией, передаваемой в OP_RETURN выходе. Она помогает установить зашифрованное соединение между двумя пользователями для последующего создания общего секрета, необходимого для генерации новых пустых адресов для получения BTC.
С другой стороны, операция включения отслеживания («Follow» или «Link») позволяет установить соединение между пользователями в Soroban, зашифрованном протоколе связи на основе Tor, специально разработанном командами Samourai.
Резюмируя:
- Простое отслеживание («follow») PayNym другого пользователя абсолютно бесплатно. Это помогает установить зашифрованное офчейн соединение, в частности, для использования инструментов коллаборативных (Stowaway или StonewallX2) транзакций в Samourai. Эта операция специфична для PayNym, в BIP47 она не описывается.
- Установление соединения между двумя PayNym — платная операция. Это подразумевает выполнение транзакции уведомления, инициирующей такое соединение. Ее стоимость складывается из комиссии сервиса, обычной комиссии за транзакцию в сети и 546 сатоши, отправляемых на адрес получателя для уведомления об открытии платежного тоннеля. Эта операция описывается в BIP47. После ее совершения отправитель может совершать множество конфиденциальных BIP47-платежей на многоразовый платежный код получателя.
Чтобы установить полноценное соединение между двумя PayNym, отправителю сначала нужно «зафолловить» PayNym получателя.
Инструкции: использование PayNym
Ознакомившись с теорией, давайте вместе пройдемся по практическому использованию. Идея приведенных ниже инструкций состоит в том, чтобы связать мой PayNym в Sparrow Wallet с другим моим PayNym в Samourai Wallet. В первом руководстве я показываю, как провести транзакцию с использованием многоразового платежного кода из Samourai в Sparrow Wallet, во втором — всё то же самое, но из Sparrow в Samourai.
Всю эту демонстрацию я выполнял в тестовой сети, это не настоящие биткойны.
Построение BIP47-транзакции в Samourai Wallet
Для начала вам, очевидно, понадобится приложение Samourai Wallet. Его можно установить из Google Play Store или непосредственно из APK-файла, доступного на официальном сайте Samourai.
После инициализации кошелька, если вы еще этого не сделали, сгенерируйте свой PayNym, нажав на (+) в правом нижнем углу и выбрав «PayNym».
Первым шагом к осуществлению BIP47-платежей будет получение многоразового платежного кода нашего получателя. Тогда мы сможем его зафолловить, а потом установить соединение:
https://video.wixstatic.com/video/23ab18_a8d44d8e844c4914a458628c98747f20/1080p/mp4/file.mp4
После подтверждения транзакции уведомления я смогу конфиденциально отправлять множество транзакций своему получателю. Каждая транзакция автоматически будет отправляться на новый пустой адрес, ключи к которому есть у получателя. Последнему не нужно совершать никаких действий, все рассчитывается на моей стороне.
Вот как можно сделать BIP47-транзакцию в Samourai Wallet:
https://video.wixstatic.com/video/23ab18_6bfa9190e89e49afbcfe2442cf9c2710/1080p/mp4/file.mp4
Построение BIP47-транзакции в Sparrow Wallet
Для этого, очевидно, вам понадобится установить приложение Sparrow. Оно доступно только для компьютера. Можно загрузить его с официального сайта.
Не забудьте перед установкой проверить подпись разработчика и целостность загруженного ПО.
Создайте кошелек и запросите свой PayNym, нажав на «Show PayNym» в меню «Tool» верхней панели:
Далее вам будет нужно привязать («Follow») свой PayNym к PayNym получателя и установить соединение между ними. Для этого введите многоразовый платежный код в окне «Find Contact», зафолловите его, затем выполните транзакцию уведомления, нажав «Link Contact»:
После подтверждения транзакции уведомления можно будет отправлять платежи на многоразовый платежный код получателя. Вот как это сделать:
https://video.wixstatic.com/video/23ab18_0e25345351f744d098987e695e6859c3/720p/mp4/file.mp4
Теперь, ознакомившись с практическим использованием PayNym, давайте разберемся, как эти механизмы работают и какие криптографические методы в них используются.
Как работает BIP47
Многоразовый платежный код
Как уже говорилось выше, во второй части этой статьи, многоразовый платежный код находится на глубине 3 HD-кошелька. Его в чем-то можно сравнить с xpub — как по расположению и структуре, так и по его роли.
80-битный платежный код состоит из следующих частей:
- Байт 0: версия. Если мы используем первую версию BIP47, этот байт будет равен 0x01.
- Байт 1: битовое поле. Это место зарезервировано для дополнительных указаний в случае специфического использования. При простом использовании PayNym этот байт будет равен 0x00.
- Байт 2: четность y. Этот байт может быть равен 0x02 или 0x03 в зависимости от четности значения ординаты нашего открытого ключа.
- Байты 3–34: значение x. Эти байты указывают на абсциссу нашего открытого ключа. Конкатенация x и четности y дает нам сжатый открытый ключ.
- Байты 35–66: строковый код. Это место зарезервировано для строкового кода, связанного с упомянутым выше открытым ключом.
- Байты 67–79: заполнение. Это место зарезервировано для возможных будущих разработок. В версии 1 оно просто заполняется нулями до 80 байт, размера данных OP_RETURN выхода.
Так выглядит шестнадцатеричное представление моего многоразового платежного кода, показанного в предыдущей части:
0x010002a0716529bae6b36c5c9aa518a52f9c828b46ad8d907747f0d09dcd4d9a39e97c3c5f37c470c390d842f364086362f6122f412e2b0c7e7fc6e32287e364a7a36a00000000000000000000000000
Затем необходимо добавить байт префикса «P», чтобы можно было сразу определить, что мы имеем дело с платежным кодом. Этот байт — 0x47.
0x47010002a0716529bae6b36c5c9aa518a52f9c828b46ad8d907747f0d09dcd4d9a39e97c3c5f37c470c390d842f364086362f6122f412e2b0c7e7fc6e32287e364a7a36a00000000000000000000000000
Наконец, мы вычисляем контрольную сумму этого платежного кода с помощью HASH256, то есть получаем двойной хеш с помощью функции SHA256. Извлекаем первые четыре байта полученного конденсата и конкатенируем их в конец.
0x47010002a0716529bae6b36c5c9aa518a52f9c828b46ad8d907747f0d09dcd4d9a39e97c3c5f37c470c390d842f364086362f6122f412e2b0c7e7fc6e32287e364a7a36a00000000000000000000000000567080c4
Платежный код готов, осталось только преобразовать его в Base58:
PM8TJSBiQmNQDwTogMAbyqJe2PE2kQXjtgh88MRTxsrnHC8zpEtJ8j7Aj628oUFk8X6P5rJ7P5qDudE4Hwq9JXSRzGcZJbdJAjM9oVQ1UKU5j2nr7VR5
Можно заметить, что эта конструкция сильно напоминает структуру расширенного открытого ключа типа xpub.
В этом процессе для получения платежного кода мы использовали сжатый открытый ключ и строковый код. Эти два элемента являются результатом детерминированной и иерархической деривации из seed-значения кошелька по следующему пути: m/47’/0’/0’/.
Конкретно, чтобы получить открытый ключ и цепной код многоразового платежного кода, мы вычислим главный закрытый ключ по seed-значению, а затем получим дочернюю пару с индексом 47 + 2^31 (усиленная деривация). Затем мы дважды получаем дочерние пары с индексом 2^31 (усиленная деривация).
Криптографический метод: Elliptic-Curve Diffie-Hellman (ECDH)
Криптографический метод, используемый в основе BIP47, — ECDH (Elliptic-Curve Diffie-Hellman = обмен ключами Диффи – Хеллмана на эллиптических кривых). Это вариация классического протокола обмена ключами Диффи – Хеллмана.
Протокол Диффи – Хеллмана в своей первой версии был представлен в 1976 году и позволяет двум людям, обмениваясь информацией по незащищенному каналу связи, безопасно определить общий секрет на основе двух пар ключей (открытого и закрытого).
Этот общий секрет (красный ключ) затем может быть использован для выполнения других задач. В основном этот общий секрет может использоваться для шифрования и дешифрования сообщений в незащищенной сети:
Для успешного обмена, протокол Диффи – Хеллмана использует для вычисления общего секрета модульную арифметику. В доступной форме схему его работы можно описать так:
- Алиса и Боб определяют общий цвет, в данном случае желтый. Цвет известен всем, это открытые данные.
- Алиса выбирает свой секретный цвет, в данном случае красный. Секретный цвет смешивается с общим, что дает оранжевый.
- Боб выбирает свой секретный цвет, в данном случае темно-синий. Секретный цвет смешивается с общим, что дает голубой.
- Алиса и Боб могут обменяться полученными цветами: оранжевым и голубым. Этот обмен можно производить в незащищенной сети, наблюдаемой злоумышленниками.
- Алиса смешивает голубой цвет, полученный от Боба, со своим секретным цветом (красным). Получает коричневый.
- Боб смешивает оранжевый цвет, полученный от Алисы, со своим секретным цветом (темно-синим). Получает тот же самый коричневый цвет.
В этой упрощенной наглядной схеме коричневый цвет представляет собой секрет, которым поделились друг с другом Алиса и Боб. Ну и в этом примере нам надо представить, что злоумышленнику невозможно разделить оранжевый и голубой цвета, чтобы определить секретные цвета Алисы и Боба.
Теперь давайте представим, как это работает на самом деле. На первый взгляд, алгоритм Диффи – Хеллмана кажется сложным для понимания. На самом деле принцип его работы чуть ли не детский. Прежде чем подробно описать этот механизм, напомню два математических понятия, которые нам для этого понадобятся (и используемые также во многих других криптографических методах).
Обмен ключами Диффи – Хеллмана между Алисой и Бобом происходит следующим образом:
- Алиса и Боб определяют два общих числа: p и g. p — простое число. Чем больше это число p, тем надежнее будет защита Диффи – Хеллмана. g — это первообразный корень из p. Эти два числа можно передавать по незащищенной сети — это эквиваленты желтого цвета в приведенной выше упрощенной схеме. Значения p и g Алисы и Боба должны быть одинаковыми.
- После выбора параметров Алиса и Боб определяют случайное секретное число. Случайное число, полученное Алисой, мы назовем a и оно эквивалентно красному цвету, а случайное число, полученное Бобом, — b, оно эквивалентно темно-синему цвету. Эти два числа должны оставаться в секрете.
- Вместо обмена непосредственно этими числами a и b, каждая сторона будет вычислять A (заглавную) и B (заглавную). Например:
# A равно g в степени a по модулю p: A = g^a % p # B равно g в степени b по модулю p: B = g^b % p
- Уже этими числами A (эквивалентно оранжевому цвету) и B (эквивалентно голубому цвету) стороны обменяются между собой. Обмен можно осуществлять открыто по незащищенной сети.
- Алиса, которая теперь знает B, вычислит значение z следующим образом:
# z равно B в степени a по модулю p: z = B^a % p
- Напомню, что B = g^b % p. Так что мы имеем:
z = B^a % p z = (g^b)^a % p # В соответствии с правилами расчета степеней: (x^n)^m = x^nm # Так что мы имеем: z = g^ba % p
- Боб, который теперь знает значение A, также вычислит значение z следующим образом:
# z равно A степени b по модулю p: z = A^b % p # Так что мы имеем: z = (g^a)^b % p z = g^ab % p z = g^ba % p
Алиса и Боб находят совершенно одинаковое значение z. Это число представляет собой их общий секрет, то есть эквивалент коричневого цвета в предыдущей популярной схеме. Они могут использовать этот общий секрет для шифрования сообщения между ними в незащищенной сети.
Злоумышленник, узнавший значения p, g, A и B, не сможет вычислить a, b или z. Для этого понадобилось бы провести вычисление, обратное возведению в степень. Это значение невозможно получить иначе как простым перебором вариантов, поскольку мы работаем с конечным полем. Это эквивалентно вычислению дискретного логарифма, т.е. обратного показателя экспоненты в конечной циклической группе.
Таким образом, если выбрать достаточно большие значения a, b и p, то реализация протокола Диффи – Хеллмана будет безопасна. Как правило, при параметрах в 2048 бит (600-значное число в десятичной системе) проверка всех возможностей для a и b считается химерической задачей. На сегодняшний день при числах такого порядка этот алгоритм считается безопасным.
Именно на этом уровне и лежит основной недостаток протокола Диффи – Хеллмана: чтобы алгоритм был безопасным, в нем необходимо использовать большие числа. Поэтому сегодня мы предпочитаем использовать ECDH, вариант алгоритма Диффи – Хеллмана, использующий в расчетах алгебраическую кривую, в данном случае эллиптическую. Это позволяет работать с гораздо меньшими числами, сохраняя при этом эквивалентный уровень безопасности, и, следовательно, сократить ресурсы, необходимые для вычислений и хранения.
Общий принцип работы алгоритма остается прежним. Но вместо того чтобы использовать случайное число a и A, вычисляемое из него через модульное возведение в степень, мы будем использовать пару ключей, созданных на эллиптической кривой. И вместо того чтобы полагаться на дистрибутивность оператора возведения в степень, мы будем использовать групповой закон эллиптических кривых, а точнее, ассоциативность этого закона.
Грубо говоря, закрытый ключ — это случайное число от 1 до n-1 (где n — порядок кривой), а открытый ключ — это единственная точка на кривой, определяемая закрытым ключом путем добавления и удвоения точек из генерирующей точки, так что:
K = k·G
Где K — открытый ключ, k — закрытый ключ, а G — генерирующая точка.
Одно из свойств этой пары ключей заключается в том, что очень легко определить K, зная k и G, но на сегодня невозможно определить k, зная K и G: это односторонняя функция.
Другими словами, можно легко вычислить открытый ключ, зная закрытый, но невозможно вычислить закрытый ключ на основе открытого ключа. Эта безопасность, опять же, основана на невозможности вычисления дискретного логарифма.
Поэтому мы будем использовать это свойство, чтобы адаптировать наш алгоритм Диффе – Хеллмана. Тогда принцип работы ECDH будет выглядеть следующим образом:
- Алиса и Боб совместно договариваются о криптографически безопасной эллиптической кривой и ее параметрах. Эта информация является открытой.
- Алиса генерирует случайное число ka, которое будет ее закрытым ключом. Этот закрытый ключ должен оставаться в секрете. По нему определяется открытый ключ Ka путем сложения и удвоения точек, выбранных на эллиптической кривой.
Ka = ka·G
- Боб также генерирует случайное число, которое будет его закрытым ключом kb. И вычисляет связанный с ним открытый ключ Kb.
Kb = kb·G
- Алиса и Боб обмениваются своими открытыми ключами Ka и Kb в незащищенной публичной сети.
- Алиса вычисляет точку (x,y) на кривой, применяя свой закрытый ключ ka из открытого ключа Боба Kb.
(x,y) = ka·Kb
- Боб вычисляет точку (x,y) на кривой, применяя свой закрытый ключ kb из открытого ключа Алисы Ka.
(x,y) = kb·Ka
- Алиса и Боб получают одну и ту же точку на эллиптической кривой. Общим секретом будет абсцисса x этой точки.
Они получают один и тот же общий секрет, потому что:
(x,y) = ka·Kb = ka·kb·G = kb·ka·G = kb·Ka
Потенциальный злоумышленник, наблюдающий за незащищенной публичной сетью, сможет получить только открытые ключи Алисы и Боба, а также параметры выбранной кривой. Как я уже объяснял выше, рассчитать закрытые ключи по этим двум фрагментам информации на сегодня невозможно, поэтому злоумышленник не может получить доступ к секрету.
ECDH — это алгоритм, позволяющий обмениваться ключами. Он часто используется для определения протокола наряду с другими криптографическими методами. Например, ECDH используется в основе Transport Layer Security (TLS), протокола шифрования и аутентификации, используемого на транспортном уровне интернета. TLS использует для обмена ключами ECDHE, вариант ECDH, в котором ключи являются эфемерными, чтобы обеспечить постоянную конфиденциальность. В дополнение к ECDHE, TLS использует также алгоритм аутентификации наподобие ECDSA, алгоритм шифрования типа AES и хеш-функцию типа SHA256.
TLS определяет, в частности, букву «s» в названии «https», а также состояние маленького замочка, который вы видите в начале строки поиска своего интернет-браузера, которые сигнализируют вам о том, что ваше интернет-соединение с данным сайтом зашифровано. Так что вы используете ECDH и читая эту статью, как, по-видимому, используете его ежедневно, даже не осознавая этого.
Транзакция уведомления
Как мы уже выяснили в предыдущей части, ECDH — это вариант протокола обмена Диффи – Хеллмана с использованием пар ключей, определенных на эллиптической кривой. Это хорошо, что пары ключей соответствуют этому стандарту, ведь у нас есть наши биткойн-кошельки!
Таким образом, идея заключается в использовании пар ключей иерархических детерминированных биткойн-кошельков двух заинтересованных сторон для создания общих и эфемерных секретов между ними. В рамках BIP47 мы используем ECDHE (Elliptic Curve Diffie-Hellman Ephemeral).
В первый раз ECDHE используется в BIP47 для передачи платежного кода от отправителя получателю. Это та самая транзакция уведомления. Действительно, для BIP47 необходимо, чтобы каждая из двух сторон (отправитель и получатель платежей) знали платежный код другой стороны. Это понадобится для получения эфемерных открытых ключей и, соответственно, новых пустых адресов получения.
До этого обмена отправитель логически уже знает платежный код получателя: он мог быть опубликован на сайте или где-то в социальных сетях. Но получатель не обязательно знает платежный код отправителя. Его необходимо передать получателю, иначе тот не сможет вычислить свои эфемерные ключи, а значит, и получить доступ к отправленным ему биткойнам. Передать ему платежный код отправителя можно и офчейн, с помощью любой другой системы связи, но это создаст проблему в случае восстановления кошелька из seed-значения.
Действительно, как я уже упомянул, BIP47-адреса нельзя получить из seed-значения получателя (в противном случае можно было бы использовать один из его xpub напрямую), они являются результатом вычисления на основе двух платежных кодов: получателя и отправителя. Поэтому, если получатель потеряет свой кошелек и попытается восстановить его из seed-значения, он обязательно должен будет иметь все платежные коды людей, которые отправили ему биткойны через BIP47.
Так что BIP47 легко можно было бы использовать и без этой транзакции уведомления, но тогда каждому пользователю пришлось бы хранить резервную копию платежных кодов своих пиров. И это будет оставаться нежизнеспособным вариантом до тех пор, пока не будет найден простой и надежный способ выполнения, хранения и обновления таких бэкапов. Так что при нынешнем положении вещей выполнение транзакции уведомления является практически обязательным.
В дополнение к сохранению платежных кодов, транзакция уведомления, как следует из названия, служит также для информирования получателя об открытии платежного тоннеля.
Прежде чем переходить к объяснению технической стороны того, как работает транзакция уведомления, я хочу рассказать немного о модели сохранения конфиденциальности в BIP47. Использование BIP47 в дальнейшем оправдывает необходимые приготовления с выполнением этой первоначальной транзакции и созданием платежного тоннеля.
Сам по себе платежный код не несет риска потери конфиденциальности. В отличие от классической модели адресов Биткойна, предполагающей по замыслу автора разрыв взаимосвязи между личностью пользователя и транзакциями, включая сохранение анонимности открытых ключей, платежный код можно напрямую связывать с личностью его владельца. Очевидно, что это не обязательное условие, но и никакого ущерба конфиденциальности транзакций пользователей наличие этой связи не несет.
Платежный код нельзя вычислить из адресов, используемых для получения BIP47-платежей. Вместо этого, адреса получаются путем применения ECDHE с дочерними ключами платежных кодов обеих сторон.
Поэтому платежный код сам по себе не несет прямого риска потери конфиденциальности, поскольку на его основе можно вычислить только адрес уведомления. Из этого адреса можно извлечь некоторую информацию, но узнать, с кем вы совершаете транзакции, как правило, невозможно.
Поэтому так важно поддерживать это строгое разделение между платежными кодами пользователей. С этой точки зрения начальный этап передачи кода является критически важным моментом для обеспечения конфиденциальности платежей и в то же время обязательным для правильного функционирования протокола. Если один из двух платежных кодов может быть опубликован в общем доступе (например, на веб-сайте), то второй код, то есть платежный код отправителя, не должен быть связан с первым.
Допустим, я хочу настроить с BIP47 регулярные пожертвования на движение мирного протеста в Канаде:
- Организаторы протестов опубликовали платежный код на своем сайте или в социальных сетях.
- Понятно, что этот код ассоциирован с движением.
- Я получаю этот платежный код из открытого источника.
- Прежде чем я смогу отправить им пожертвование, мне нужно убедиться, что они знают мой платежный код, который также связан с моей личностью, поскольку я публиковал его в своих социальных сетях и использовал для получения платежей.
Как я могу передать им свой платежный код? Если я отправлю его по обычным средствам связи, может произойти утечка, и я рискую быть попасть на радары контролирующих органов как негодяй, поддерживающий движения мирного протеста.
Транзакция уведомления — конечно, не единственное возможное решение для конфиденциальной передачи получателю платежного кода отправителя, но на данный момент она прекрасно справляется с этой задачей, применяя несколько уровней безопасности.
На схеме ниже красными линиями обозначены потоки информации, которые должны быть нарушены, а черными стрелками — неоспоримые связи, которые может установить сторонний наблюдатель.
В действительности для классической модели конфиденциальности Биткойна зачастую сложно полностью прервать поток информации между парой ключей и пользователем, особенно при удаленном совершении транзакций. Например, в случае кампании по сбору средств получатель должен будет раскрыть свой адрес или открытый ключ где-то на сайте или в социальных сетях. Собственное использование BIP47 — то есть с транзакцией уведомления — позволяет решить эту проблему благодаря ECDHE и уровню шифрования, который мы еще рассмотрим.
Очевидно, что на уровне эфемерных открытых ключей, полученных в результате объединения двух платежных кодов, классическая модель конфиденциальности биткойна по-прежнему соблюдается. Эти две модели взаимозависимы. Здесь я лишь хочу подчеркнуть, что, в отличие от использования для получения биткойнов классического открытого ключа, при связывании с личностью платежного кода ущерба для конфиденциальности не возникает, поскольку информационная связь «Боб совершает транзакцию с Алисой» нарушается в другой момент. Платежный код используется для генерации адресов, но на основе исключительно ончейн-данных платежную BIP47-транзакцию невозможно связать с платежными кодами, использованными для ее построения.
Построение транзакции уведомления
Теперь давайте подробно рассмотрим, как работает эта транзакция уведомления. Допустим, Алиса хочет отправить Бобу деньги с помощью BIP47. В моем примере Алиса будет отправителем, а Боб — получателем. Боб опубликовал свой платежный код на сайте, так что Алисе он уже известен.
1. Алиса вычисляет общий секрет с ECDH:
- Алгоритм выбирает пару ключей в своем HD-кошельке, которая находится на другой ветке его платежного кода. Будьте внимательны: эта пара не должна легко ассоциироваться ни с адресом уведомления, ни с личностью Алисы (см. предыдущую часть).
- Алиса выбирает закрытый ключ для этой пары. Обозначим его буквой «a» (строчной).
a
- Алиса получает открытый ключ, связанный с адресом уведомления Боба. Этот ключ является первым дочерним элементом, полученным после платежного кода Боба (индекс 0). Назовем его открытым ключом «B» (заглавная). А закрытый ключ, связанный с этим открытым ключом, назовем «b» (строчная). «B» определяется путем сложения и удвоения точек на эллиптической кривой от «G» (генераторная точка) к «b» (закрытый ключ).
B = b·G
- Алиса вычисляет секретную точку «S» (заглавная) на эллиптической кривой путем сложения и удвоения, применяя свой закрытый ключ «a» из открытого ключа Боба «B».
S = a·B
- Алиса вычисляет коэффициент ослепления «f», с которым зашифрует свой платежный код. Для этого алгоритм определяет псевдослучайное число с помощью функции HMAC-SHA512. Вторым входом в этой функции используется значение, которой сможет найти только Боб: (x), являющееся абсциссой вычисленной ранее секретной точки. Первый вход — (o), представляющее собой UTXO, используемый в качестве входа этой транзакции (outpoint).
f = HMAC-SHA512(o, x)
2. Алиса приводит свой личный платежный код к основанию 2 (в двоичный вид).
3. Этот коэффициент ослепления используется как ключ для выполнения симметричного шифрования своего платежного кода. В качестве алгоритма шифрования используется просто XOR. Проведенная операция сопоставима с шифром Вернама (его еще называют «одноразовым блокнотом»):
- Сначала Алиса разделяет свой коэффициент ослепления на две части: первые 32 байта называются «f1», вторые 32 байта — «f2». Так что мы имеем:
f = f1 || f2
- Алиса вычисляет по отдельности зашифрованную абсциссу (x’) открытого ключа (x) своего платежного кода, и зашифрованное (c’) своего строкового кода (c). «f1» и «f2» используются в качестве ключей шифрования соответственно. Используется операция XOR (или исключающая):
x’ = x XOR f1 c’ = c XOR f2
- Алиса заменяет фактические значения абсциссы открытого ключа (x) и строкового кода (c) в своем платежном коде на зашифрованные значения (x’) и (c’).
Прежде чем продолжить техническое описание нашей транзакции уведомления, давайте ненадолго остановимся на этой операции XOR. XOR — это логический оператор битового уровня, основанный на булевой алгебре. Из двух операндов в битах он возвращает 1, если биты одного ранга различаются, и 0, если биты одного ранга равны. Так выглядит таблица истинности XOR в соответствии со значениями операндов D и E:
Например:
0110 XOR 1110 = 1000
Или:
010011 XOR 110110 = 100101
С ECDH использование XOR в качестве уровня шифрования особенно логично. Во-первых, благодаря этому оператору шифрование является симметричным. Это позволяет получателю расшифровывать платежный код с помощью того же ключа, который использовался для шифрования. Ключ шифрования и дешифрования рассчитывается на основе общего секрета с помощью ECDH.
Эта симметрия возможна благодаря свойствам коммутативности и ассоциативности оператора XOR:
# Другие свойства: -> D ⊕ D = 0 -> D ⊕ 0 = D # Коммутативность: D ⊕ E = E ⊕ D # Ассоциативность: D ⊕ (E ⊕ Z) = (D ⊕ E) ⊕ Z = D ⊕ E ⊕ Z # Симметрия: If: D ⊕ E = L Тогда: D ⊕ L = D ⊕ (D ⊕ E) = D ⊕ D ⊕ E = 0 ⊕ E = E -> D ⊕ L = E
Этот метод шифрования очень похож на шифр Вернама («одноразовый блокнот»), единственный известный на сегодня алгоритм шифрования, обладающий безусловной (т.е. абсолютной) безопасностью. Для того чтобы шифр Вернама обладал этим свойством, ключ шифрования должен быть совершенно случайным, иметь тот же размер, что и сообщение, и использоваться только один раз. В методе шифрования, используемом здесь для BIP47, ключ действительно имеет тот же размер, что и сообщение, коэффициент ослепления имеет такой же размер, как конкатенация абсциссы открытого ключа с цепным кодом многоразового платежного кода. Этот ключ шифрования используется только один раз. С другой стороны, этот ключ не является совершенно случайным, поскольку он получен с HMAC. Это псевдослучайное значение. Так что это не шифр Вернама, но метод к нему приближается.
Давайте вернемся к построению нашей транзакции уведомления:
4. Итак, Алиса на текущий момент имеет свой платежный код с зашифрованной полезной нагрузкой. Она создаст и транслирует транзакцию со своим открытым ключом «A» на входе, выходом на адрес уведомления Боба и выходом OP_RETURN, состоящим из ее платежного кода с зашифрованной полезной нагрузкой. Это транзакция уведомления.
OP_RETURN — это опкод, т.е. скрипт, который позволяет пометить выход биткойн-транзакции как недействительный. Сегодня он используется для распространения или записи информации в блокчейн Биткойна. Он может хранить до 80 байт данных, которые записываются в блокчейн и, следовательно, видны всем остальным пользователям.
Как мы видели в предыдущей части, протокол Диффи – Хеллмана используется для создания общего секрета между двумя пользователями, обменивающимися данными по незащищенной сети и потенциально под наблюдением злоумышленников. В BIP47 ECDH используется для коммуникации через сеть Bitcoin, которая по своей природе прозрачна и за которой могут наблюдать все интересующиеся, включая множество потенциальных злоумышленников. Общий секрет, рассчитанный через обмен ключами по методу Диффи – Хеллмана на эллиптической кривой, затем используется для шифрования передаваемой секретной информации: платежного кода отправителя (Алисы).
Вот диаграмма из BIP47, иллюстрирующая этот процесс:
Если сопоставить эту схему с моим описанием, то:
- «Wallet Priv-Key» на стороне Алисы соответствует a.
- «Child Pub-Key 0» на стороне Боба соответствует B.
- «Notification Shared Secret» соответствует f.
- «Masked Payment Code» соответствует скрытому платежному коду, т.е. с зашифрованной полезной нагрузкой: x’ и c’.
- «Транзакция уведомления» — это транзакция, которая содержит OP_RETURN.
И резюмируем шаги для выполнения транзакции уведомления, которые мы только что рассмотрели:
- Алиса получает платежный код Боба и адрес уведомления.
- Алиса выбирает принадлежащий ей UTXO на своем HD-кошельке с соответствующей парой ключей.
- Она вычисляет секретную точку на эллиптической кривой с помощью ECDH.
- Эту секретную точку Алиса использует для вычисления HMAC, являющегося коэффициентом ослепления.
- Она использует этот коэффициент ослепления для шифрования полезной нагрузки своего персонального платежного кода.
- Она использует OP_RETURN выход транзакции для передачи скрытого платежного кода Бобу.
Чтобы более подробно понять, как это работает, включая использование OP_RETURN, давайте рассмотрим реальную транзакцию уведомления. Я сделал такую транзакцию в тестовой сети, к ней можно перейти по этой ссылке.
TXID:
0e2e4695a3c49272ef631426a9fd2dae6ec3a469e3a39a3db51aa476cd09de2e
Источник: https://blockstream.info/
Мы видим, что эта транзакция имеет один вход и 4 выхода:
- Первый выход — это OP_RETURN, который содержит мой скрытый платежный код.
- Второй выход с 546 сатоши указывает на адрес уведомления моего получателя.
- Третий выход с 15 000 сатоши содержит комиссию сервиса, поскольку я использовал для создания этой транзакции Samourai Wallet.
- Четвертый выход в два миллиона сатоши представляет собой сдачу, то есть остаток от номинала моего входа, возвращаемый на другой мой адрес.
Наибольший интерес, очевидно, представляет выход 0 с OP_RETURN. Давайте рассмотрим его подробнее:
Источник: https://blockstream.info/
Здесь мы видим скрипт выхода в шестнадцатеричном формате:
6a4c50010002b13b2911719409d704ecc69f74fa315a6cb20fdd6ee39bc9874667703d67b164927b0e88f89f3f8b963549eab2533b5d7ed481a3bea7e953b546b4e91b6f50d800000000000000000000000000
В этом скрипте можно выделить несколько частей:
6a4c50010002b13b2911719409d704ecc69f74fa315a6cb20fdd6ee39bc9874667703d67b164927b0e88f89f3f8b963549eab2533b5d7ed481a3bea7e953b546b4e91b6f50d800000000000000000000000000 # Опкоды: 6a4c50 # Метаданные моего платежного кода в чистом виде: 010002 # Зашифрованная абсцисса открытого ключа моего платежного кода: b13b2911719409d704ecc69f74fa315a6cb20fdd6ee39bc9874667703d67b164 # Зашифрованный цепной код моего платежного кода: 927b0e88f89f3f8b963549eab2533b5d7ed481a3bea7e953b546b4e91b6f50d8 # Заполнение до 80 байт: 00000000000000000000000000
Из опкодов можно выделить 0x6a, обозначающий OP_RETURN, 0x4c, обозначающий OP_PUSHDATA1, и 0x50, обозначающий OP_RESERVED.
Затем идет платежный код с зашифрованной полезной нагрузкой.
Вот мой платежный код в открытом виде, использованный в этой транзакции:
# С основанием 58: PM8TJQCyt6ovbozreUCBrfKqmSVmTzJ5vjqse58LnBzKFFZTwny3KfCDdwTqAEYVasn11tTMPc2FJsFygFd3YzsHvwNXLEQNADgxeGnMK8Ugmin62TZU # С основанием 16 (HEX): 4701000277507c9c17a89cfca2d3af554745d6c2db0e7f6b2721a3941a504933103cc42add94881210d6e752a9abc8a9fa0070e85184993c4f643f1121dd807dd556d1dc000000000000000000000000008604e4db
Если мы сравним мой платежный код (в открытом виде) с OP_RETURN, то увидим, что HRP и контрольная сумма не передаются. Это нормально, т.к. эта информация предназначена для людей.
И мы можем узнать также версию (0x01), битовое поле (0x00) и четность открытого ключа (0x02). А в конце платежного кода идут просто пустые байты (0x00), заполняющие оставшийся объем до 80 байт. Все эти метаданные передаются в открытом (незашифрованном) виде.
Наконец, можно видеть, что абсцисса открытого ключа и строковый код были зашифрованы. Это полезная нагрузка платежного кода.
Получение транзакции уведомления
Теперь, когда Алиса отправила Бобу транзакцию уведомления, давайте посмотрим, как Боб ее интерпретирует.
Напомню, что Боб должен иметь доступ к платежному коду Алисы. Без этой информации, как мы увидим в следующей части, он не сможет вывести пары ключей, созданные Алисой, и, следовательно, не сможет получить доступ к своим биткойнам, полученным с использованием BIP47. На данный момент полезная нагрузка платежного кода Алисы зашифрована. Давайте теперь посмотрим, как Боб ее расшифрует.
1. Боб мониторит транзакции, создающие выходы с его адресом уведомления.
2. Обнаружив такую транзакцию, Боб анализирует ее на наличие OP_RETURN выхода, соответствующего стандарту BIP47.
3. Если первый байт полезной нагрузки OP_RETURN равен 0x01, Боб начинает поиск возможного секрета, переданного с ECDH:
- Боб выбирает открытый ключ входа транзакции, то есть, открытый ключ «A» Алисы:
A = a·G
- Боб выбирает закрытый ключ «b», связанный с его адресом уведомления:
b
- Боб вычисляет секретную точку «S» (общий секрет ECDH) на эллиптической кривой путем сложения и удвоения точек, применяя свой закрытый ключ «b» к открытому ключу «A» Алисы:
S = b·A
- Боб определяет коэффициент ослепления «f», который расшифрует полезную нагрузку платежного кода Алисы. Точно так же, как ранее его вычислила Алиса, Боб найдет «f», применив HMAC-SHA512 к (x) значению абсциссы секретной точки «S» и к (o) UTXO, потребленного в качестве входа этой транзакции уведомления:
f = HMAC-SHA512(o, x)
4. Боб интерпретирует данные OP_RETURN в транзакции уведомления как платежный код. Он с легкостью расшифрует полезную нагрузку этого потенциального платежного кода благодаря коэффициенту ослепления «f»:
- Боб разделяет ослепляющий фактор «f» на две части: первые 32 байта «f» будут «f1», а вторые 32 байта — «f2».
- Боб расшифровывает значение зашифрованной абсциссы (x’) открытого ключа от платежного кода Алисы:
x = x’ XOR f1
- Боб расшифровывает значение зашифрованного строкового кода (c’) платежного кода Алисы:
c = c’ XOR f2
5. Боб проверяет, входит ли значение открытого ключа от платежного кода Алисы в группу secp256k1. Если входит, то он интерпретирует его как действительный платежный код. В противном случае он игнорирует эту транзакцию.
Теперь, когда Боб знает платежный код Алисы, она может отправить ему до 2^32 платежей без необходимости повторно выполнять подобную транзакцию уведомления.
Почему это работает? Как Боб может определить тот же коэффициент ослепления, что и Алиса, и, следовательно, расшифровать его платежный код? Давайте подробнее проследим действие ECDH в описанном только что процессе.
Прежде всего, мы имеем дело с симметричным шифрованием. Это означает, что ключ шифрования и ключ дешифрования — это одно и то же значение. В транзакции уведомления этот ключ является коэффициентом ослепления (f = f1 || f2). Поэтому Алиса и Боб должны получить одно и то же значение f без его прямой передачи, поскольку злоумышленник может украсть его и расшифровать секретную информацию.
Этот коэффициент ослепления получается путем применения хеш-алгоритма HMAC-SHA512 к двум значениям: абсциссе секретной точки и UTXO, потребленному в качестве входа транзакции. Чтобы расшифровать полезную нагрузку платежного кода Алисы, Бобу необходимы два этих фрагмента информации.
Что касается UTXO входа, то Боб может его получить просто из ончейн-данных о транзакции уведомления. Для секретной точки Бобу придется использовать ECDH.
Как показано в части об алгоритме Диффи – Хеллмана, просто обменявшись своими соответствующими открытыми ключами и применив свои закрытые ключи к каждому из открытых ключей партнера, Алиса и Боб могут определить в точности одну и ту же секретную точку на эллиптической кривой. На этом механизме основана транзакция уведомления:
# Пара ключей Боба: B = b·G # Пара ключей Алисы: A = a·G # Для секретной точки S (x,y): S = a·B = a·b·G = b·a·G = b·A
Теперь, когда Боб знает платежный код Алисы, он сможет определить ее BIP47-платежи в свой адрес и вычислить закрытые ключи, блокирующие получаемые в этих транзакциях биткойны.
Если сопоставить эту схему с моим описанием, то:
- «Wallet Pub-Key» на стороне Алисы соответствует A.
- «Child Priv-Key 0» на стороне Боба соответствует b.
- «Notification Shared Secret» соответствует f.
- «Masked Payment Code» соответствует скрытому платежному коду Алисы, т.е. с зашифрованной полезной нагрузкой: x’ и c’.
- «Транзакция уведомления» — это транзакция, которая содержит OP_RETURN.
И резюмируем шаги для выполнения транзакции уведомления, которые мы только что рассмотрели:
- Боб отслеживает транзакции с выходом на свой адрес уведомления.
- Обнаруживая такую транзакцию, он извлекает информацию, содержащуюся в OP_RETURN.
- Боб выбирает открытый ключ от входа транзакции и вычисляет секретную точку с помощью ECDH.
- Он использует эту секретную точку для вычисления HMAC, являющегося коэффициентом ослепления.
- Этот коэффициент ослепления он использует для расшифровки полезной нагрузки платежного кода Алисы, содержащегося в OP_RETURN.
Платежная транзакция BIP47
Теперь давайте вместе рассмотрим процесс платежа с BIP47. Кратко напомню о текущем положении дел:
- Алисе известен платежный код Боба: он был просто опубликован где-то на сайте.
- Боб знает платежный код Алисы благодаря транзакции уведомления.
- Теперь Алиса сделает первый платеж Бобу. Таким же образом она может сделать еще много платежей.
Прежде чем перейти к объяснению этого процесса, я думаю, важно напомнить, с каким индексом мы сейчас работаем:
Мы описываем путь деривации платежного кода следующим образом: m/47’/0’/0’/.
Следующая глубина распределяет индексы таким образом:
- Первая обычная (не усиленная) дочерняя пара используется для генерации адреса уведомления, о котором мы говорили в предыдущей части: m/47’/0’/0/0/.
- Пары обычных дочерних ключей используются в ECDH для генерации адресов приема BIP47 платежей, как мы увидим в этой части: m/47’/0’/0’/ от 0 до 2 147 483 647/ .
- Пары усиленных дочерних ключей — это эфемерные платежные коды: m/47’/0’/0’/ от 0′ до 2 147 483 647’/.
Каждый раз, когда Алиса хочет отправить Бобу платеж, она получает новый уникальный пустой адрес, опять же благодаря протоколу ECDH:
- Алиса выбирает первый закрытый ключ, полученный из ее личного многоразового платежного кода:
a
- Алиса выбирает первый неиспользованный открытый ключ, полученный из платежного кода Боба. Этот открытый ключ мы назовем «B». Он связан с закрытым ключом «b», известным только Бобу.
B = b·G
- Алиса вычисляет секретную точку «S» на эллиптической кривой путем сложения и удвоения точек, применяя свой закрытый ключ «a» из открытого ключа Боба «B».
S = a·B
- Из этой секретной точки Алиса вычислит (крошечный) общий секрет «S». Для этого она выбирает абсциссу из секретной точки «S» с именем «Sx» и передает это значение в хеш-функцию sha256.
s = SHA256(Sx)
- Алиса использует этот общий секрет «S» для вычисления адреса приема биткойн-платежа. Сначала она проверяет, что «S» действительно содержится в последовательности кривой SECP256K1. Если это не так, она увеличивает индекс открытого ключа Боба, чтобы получить другой общий секрет.
- Вторым шагом она вычисляет открытый ключ «K0», добавляя на эллиптической кривой точки «B» и «S · g». Другими словами, Алиса суммирует открытый ключ, полученный из платежного кода «B» Боба, с другой точкой, рассчитанную на эллиптической кривой путем добавления и удвоения точек с общим секретом «s» из генерирующей точки «G» кривой SECP256K1. Эта новая точка представляет собой открытый ключ, и мы назовем его «K0»:
K0 = B + s·G
- С помощью этого открытого ключа «K0» Алиса может получить новый пустой адрес приема стандартным способом (например, SegWit v0 или bech32).
Получив этот принадлежащий Бобу адрес приема «K0», Алиса может построить классическую биткойн-транзакцию, выбрав UTXO из другой ветки своего HD-кошелька и потратив его на «K0» адрес Боба.
Если сопоставить эту диаграмму из BIP47 с тем, что я описал ранее, то:
- «Child Priv-Key» на стороне Алисы соответствует a.
- «Child Pub-Key 0» на стороне Боба соответствует B.
- «Payment Secret 0» соответствует s.
- «Payment Pub-Key 0» соответствует K0.
И резюмируем шаги для отправки BIP47 платежа, которые мы только что рассмотрели:
- Алиса выбирает первый дочерний закрытый ключ, полученный из ее личного платежного кода:
- Она вычисляет с помощью ECDH секретную точку на эллиптической кривой по первому неиспользованному дочернему открытому ключу, полученному из платежного кода Боба.
- Она использует эту секретную точку для вычисления общего секрета с помощью SHA256.
- Этот общий секрет она использует для вычисления новой секретной точки на эллиптической кривой.
- Она складывает эту новую секретную точку с открытым ключом Боба.
- Получает новый эфемерный открытый ключ, соответствующий закрытый ключ для которого есть только у Боба.
- Алиса может отправить классическую транзакцию Бобу с эфемерным производным адресом приема.
Если она хочет произвести второй платеж, она воспроизводит все те же действия, но выбирает второй открытый ключ, полученный из платежного кода Боба, то есть следующий неиспользованный ключ. Тогда она получит второй принадлежащий Бобу адрес приема «K1».
Это можно повторять до 2^32 раз и получить до 2^32 новых пустых адресов приема, принадлежащих Бобу.
С точки зрения внешнего наблюдателя, отличить BIP47 платеж от обычной биткойн-транзакции теоретически невозможно. Вот пример платежной BIP47 транзакции в тестовой сети:
https://blockstream.info/testnet/tx/94b2e59510f2e1fa78411634c98a77bbb638e28fb2da00c9f359cd5fc8f87254
TXID:
94b2e59510f2e1fa78411634c98a77bbb638e28fb2da00c9f359cd5fc8f87254
Она выглядит как обычная транзакция с потребляемым входом, выходом с платежом в 210 000 сатоши и сдачей:
Источник: https://blockstream.info/
Получение BIP47-платежа и получение закрытого ключа
Итак, Алиса сделала свой первый BIP47-платеж на автоматически сгенерированный новый пустой адрес, принадлежащий Бобу. Теперь давайте вместе посмотрим, как Боб получает этот платеж. Мы увидим также, почему Алиса не имеет доступа к закрытому ключу от адреса, который она только что сгенерировала, и как Боб находит этот ключ, чтобы потратить биткойны, полученные в такой транзакции.
Когда Боб получает от Алисы транзакцию уведомления, он получает открытый ключ «K0» еще до того, как его корреспондент по BIP47 отправил платеж. Поэтому он следит за любыми платежами на связанный адрес. На самом деле он сразу вычислит несколько адресов, которые и будет наблюдать (K0, K1, K2, K3 …). Вот как он получает этот открытый ключ «K0»:
- Боб выбирает первый дочерний закрытый ключ, полученный из его платежного кода. Назовем этот закрытый ключ «b». Он связан с открытым ключом «B», с помощью которого Алиса произвела свои вычисления на предыдущем этапе:
b
- Боб выбирает первый открытый ключ Алисы, полученный из ее платежного кода. Назовем этот ключ «A». Он связан с закрытым ключом «a», с помощью которого Алиса производила свои вычисления и известный только Алисе. Боб может осуществить этот процесс, поскольку ему известен платежный код Алисы, который был ему передан с транзакцией уведомления.
A = a·G
- Боб вычисляет секретную точку «S» путем сложения и удвоения точек на эллиптической кривой, применяя свой закрытый ключ «b» к открытому ключу «A» Алисы. Здесь мы видим использование ECDH, гарантирующего нам, что эта точка «S» для Боба и для Алисы будет одинаковой.
S = b·A
- Точно так же, как это сделала Алиса, Боб выделяет абсциссу этой точки «S». Я назвал это значение «Sx». Он передает этот значение в функцию SHA256, чтобы найти общий секрет «s» (строчная).
s = SHA256(Sx)
- Так же, как и Алиса, Боб вычисляет точку «s·G» на эллиптической кривой. Эту секретную точку он добавляет к своему открытому ключу «B». Затем он получает новую точку на эллиптической кривой, которую интерпретирует как открытый ключ «K0»:
K0 = B + s·G
Получив этот открытый ключ «K0», Боб сможет получить соответствующий закрытый ключ, чтобы потратить свои биткойны. Это число может сгенерировать только он.
- Боб добавляет свой дочерний закрытый ключ «b», полученный из его личного платежного кода. Значение «b» может получить только он. Затем он добавляет «b» к общему секрету «s», чтобы получить k0, закрытый ключ от K0:
k0 = b + s
Благодаря групповому закону эллиптической кривой, Боб получает именно тот закрытый ключ, который соответствует открытому ключу, используемому Алисой. Так что мы имеем:
K0 = k0·G
Если сопоставить эту диаграмму из BIP47 с тем, что я описал ранее, то:
- «Child Priv-Key 0» на стороне Боба соответствует b.
- «Child Pub-Key 0» на стороне Алисы соответствует A.
- «Payment Secret 0» соответствует s.
- «Payment Pub-Key 0» соответствует K0.
- «Payment Priv-Key 0» соответствует k0.
И резюмируем шаги для получения BIP47 платежа и вычисления соответствующего закрытого ключа:
- Боб выбирает первый дочерний закрытый ключ, полученный из его личного платежного кода.
- Он вычисляет секретную точку на эллиптической кривой с помощью ECDH из первого дочернего открытого ключа, полученного из цепного кода Алисы.
- Она использует эту секретную точку для вычисления общего секрета с помощью SHA256.
- Этот общий секрет он использует для вычисления новой секретной точки на эллиптической кривой.
- Он складывает эту новую секретную точку с собственным открытым ключом.
- Получает новый эфемерный открытый ключ, на который Алиса отправит свой первый платеж.
- Боб рассчитывает закрытый ключ, связанный с этим эфемерным открытым ключом, добавляя свой дочерний закрытый ключ, полученный из платежного кода, и общий секрет.
Поскольку Алиса не может получить «b», закрытый ключ Боба, она не может определить k0, закрытый ключ, связанный с адресом получения BIP47 Боба.
Схематически вычисление общего секрета «S» можно представить следующим образом:
Найдя с помощью ECDH общий секрет, Алиса и Боб вычисляют открытый ключ платежа BIP47 «K0», а Боб вычисляет также соответствующий закрытый ключ «k0»:
Возврат BIP47-платежа
Поскольку Боб знает многоразовый платежный код Алисы, у него уже есть вся информация, необходимая для отправки ей возвратных платежей. Ему не придется снова связываться с Алисой, чтобы узнать какую-то дополнительную информацию. Он просто должен будет уведомить ее с помощью транзакции уведомления, в частности чтобы она могла восстановить свои BIP47-адреса из seed-значения, — и тогда он тоже сможет отправить Алисе до 2^32 платежей.
Боб сможет отправлять платежи Алисе тем же способом, каким она отправляла платежи ему. Роли при этом меняются местами:
Теперь вы знаете все принципы работы этого прекрасного платежного решения, которое собой представляет BIP47.
Производные варианты использования PayNym
Результатом реализации этого BIP47 в кошельке Samourai Wallet стали PayNym, идентификаторы, вычисленные из платежных кодов пользователей. Сегодня их полезность выходит уже далеко за рамки применения BIP47.
Команды разработчиков Samourai постепенно выстраивают целую экосистему инструментов и сервисов, основанных на PayNym пользователей. Среди них, очевидно, есть все инструменты для расходования, позволяющие оптимизировать конфиденциальность пользователя через добавление к транзакции энтропии, а значит, и возможности правдоподобного отрицания.
Использование Soroban (зашифрованной сети связи на основе Tor) совместно с PayNym позволило значительно оптимизировать пользовательский опыт при создании коллаборативных, совместных транзакций, сохранив при этом хороший уровень безопасности. Благодаря этому, Stowaway (PayJoin) и StonewallX2 транзакции можно легко осуществить без ручного проведения многочисленных обменов неподписанными транзакциями, необходимых для создания коллаборативной транзакции такого типа.
В отличие от BIP47, для использования этих инструментов достаточно просто связать PayNym(ы) через фолловинг, поскольку эти коллаборативные транзакции не требуют выполнения транзакции уведомления. Между ними нет необходимости устанавливать соединение.
Помимо этих коллаборативных транзакций, команды Samourai участвуют в разработке протокола аутентификации, связанного с PayNym, — Auth47. Этот инструмент уже работает и позволяет, например, авторизоваться по PayNym на сайтах, принимающих такой метод аутентификации. В будущем, я думаю, что помимо этой возможности аутентификации в интернете, Auth47 станет частью более глобального проекта вокруг экосистемы BIP47/PayNym/Samourai Wallet. Возможно, этот протокол будет использоваться для дальнейшей оптимизации пользовательского опыта Samourai Wallet, особенно в отношении инструментов расходования.
Мое мнение о BIP47
Очевидно, что основным недостатком BIP47 является транзакция уведомления. Это дополнительное действие, к тому же сопровождаемое комиссиями сети (за биткойн-транзакцию) и сервиса, что может вызывать раздражение у части пользователей. С другой стороны, аргумент о «спаме» блокчейна Биткойна абсолютно неприемлем. Любой, кто платит комиссию за транзакцию, должен иметь возможность записать ее в реестр, независимо от цели транзакции. Утверждать обратное — значит высказываться в пользу цензуры.
Возможно, что в будущем будут найдены другие, менее затратные решения для передачи получателю платежного кода отправителя и надежного его хранения. Но пока что транзакция уведомления остается наименее компромиссным решением.
А с учетом преимуществ BIP47, этот недостаток даже не выглядит настолько значимым. Из всех существующих предложений по решению проблемы повторного использования адресов, это, на мой взгляд, лучшее решение.
Как упоминалось выше, источником большей части повторного использования адресов являются биржи. BIP47 — единственное разумное решение, которое действительно решает эту проблему в самом ее источнике. Любое предложение по сокращению повторного использования адресов должно учитывать этот аспект и адаптировать решение под основной источник проблемы.
С точки зрения пользователя, несмотря на довольно сложный механизм работы, процесс платежа с BIP47 выглядит довольно детским. Поэтому многоразовые платежные коды могут быть довольно легко приняты даже начинающими пользователями.
С точки зрения конфиденциальности, BIP47 представляет очень большой интерес. Как я уже объяснял, рассказывая о транзакциях уведомления, платежные коды не раскрывают никакой информации о производных эфемерных адресах. Таким образом, они позволяют прервать поток информации между биткойн-транзакцией и идентификатором получателя, в отличие от традиционного использования адреса получателя.
И самое главное, реализация BIP47 в виде PayNym работает уже сейчас. В Samourai Wallet они доступны с 2016, а в Sparrow Wallet с начала этого года. Это не какой-то исследовательский эксперимент, а практическое решение, проверенное вчера и полностью работоспособное сегодня.
Надеюсь, что в будущем эти платежные коды будут приняты участниками экосистемы, поддержка их будет реализована в большем количестве кошельков и они получат распространение среди биткойнеров.
Любое действительно позитивное решение для обеспечения конфиденциальности пользователей нужно обсуждать, отстаивать и продвигать, чтобы Биткойн не превратился в площадку для централизованных игроков и инструмент правительственной слежки.
«Он думал о том, как его всюду оскорбляли и преследовали, а теперь он слышал, как все говорят о нем как о самой красивой из всех этих прекрасных птиц! И сама бузина склонила к нему свои ветви и солнце пролило такой теплый и благодатный свет! Тогда его перья распушились, стройная шея поднялась и он воскликнул от всего сердца: «Как я мог мечтать о таком счастье, когда был всего лишь гадким утенком!»»
Источник: bitnovosti.com