February 27, 2023

Биткоин: работа продолжается. Глава 9

Технические инновации из окопов

Фото автора

Перевод NADO Book by Sjors Provoost.

Проект перевода организован HypeCoinNews.

Guix

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

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

Кроме того, даже если открытый исходный код надежен, это не гарантирует, что двоичные файлы (компьютерный код) действительно соответствуют открытому исходному коду. Первая попытка снизить этот риск для Биткоина включала процесс, называемый Gitian. Несколько разработчиков Bitcoin Core подписывают двоичные файлы на Git, если и только если все они получили одни и те же двоичные файлы из одного и того же исходного кода. Для этого требуются специализированные компиляторы.

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

Свободный или открытый код?

Перед тем, как перейти к деталям Git-сборки и Guix, в этом разделе кратко рассмотрим историческую разницу между свободным программным обеспечением и программным обеспечением с открытым исходным кодом и тем, как они были объединены в FOSS (свободное программное обеспечение с открытым исходным кодом).

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

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

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

Программисту по имени Ричард Столмен не нравилась идея закрытого софта, поэтому он начал движение за свободный софт, в котором указывалось, что исходный код должен быть доступен, чтобы люди реально могли проверить, что они запускают на своих компьютерах. Это, в свою очередь, устранило силовую динамику. Итак, свободный в этом контексте означает свободу; это не значит "бесплатно", как в "бесплатном пиве".

Несколько иная, но совместимая точка зрения была представлена Эриком С. Рэймондом в его книге 1999 года Собор и базар: размышления случайного революционера о Linux и открытом исходном коде. В нем он объяснил преимущества свободного программного обеспечения и то, как оно может обеспечить высококачественный код. По его словам, «если обеспечить достаточно глаз, все баги будут мелкими». Другими словами, чем больше фрагмент кода просматривается и проверяется, тем больше шансов, что все его ошибки будут найдены.

Следуя этому прагматичному рассуждению о качестве кода, ребята из Netscape Communications Corporation поддались на уговоры превратить свой внутренний браузер в проект с открытым исходным кодом, Mozilla. Теперь мы называем его открытым исходным кодом, потому что эта группа разработчиков переименовала свободное программное обеспечение в открытое (чтобы избежать путаницы с пивом). В этом и состоит разница между свободным программным обеспечением и открытым исходным кодом.

Биткоин - проект с открытым исходным кодом.

Теперь вопрос в том, как все это относится к Биткоину. Вот пример. Когда вы запускаете приложение кошелька, оно отображает адрес. Что, если окажется, что адрес принадлежит не вам, а контролируется разработчиком? Тогда каждый раз, когда кто-то платит вам, монеты достаются не вам. Вот почему вам действительно нужна максимальная прозрачность того, что именно работает на вашей машине.

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

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

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

Количество людей, которые могут прочитать этот код, зависит от того, что вы подразумеваете под словом "прочитать". Сколько людей вообще умеет что-то прочесть на компьютере? Сколько человек может приблизительно понять, что делает программа на C++? Вероятно, десятки миллионов. Но из них, возможно, только несколько тысяч когда-либо работали в сфере криптовалют или чего-то подобного. Каждый день десятки активных разработчиков отсматривают код. Но никто из них не может контролировать все изменения во всем проекте, потому что это требует специализации: например, один разработчик может знать все о коде одноранговой сети и абсолютно ничего о коде кошелька.

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

Например, очень серьезный баг CVE-2018-17144 был обнаружен разработчиком Bitcoin Cash с ником Awemany. Многие проекты альткоинов начинались с копипастинга исходного кода Биткоина, с последующим изменением нескольких вещей, чтобы выделиться. Например, Dogecoin изменил график инфляции, уменьшил время между блоками и использовал другой алгоритм proof-of-work. Но при этом 99% его кодовой базы остается идентичной Bitcoin Core: цифровые подписи проверяются таким же образом, транзакции и блоки проверяются таким же образом, одноранговая сеть работает так же и т. д. Поэтому, когда разработчики альткоинов работают над своими проектами, они могут обнаружить ошибки в тех 99% кода, которые они используют совместно с Bitcoin Core. Это повышает безопасность Биткоина.

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

Чтобы понять, что означает подобное понимание, представьте, что вы смотрите на код и видите, что есть функция под названием «создать закрытый ключ». Ход ваших мыслей может быть таким: «Хорошо, что делает эта функция? О, она вызывает вот эту другую функцию. Где еще эта функция? О, она двадцатью тысячами строк выше в этом же файле. Давайте-ка прокрутим на 20 000 строк вверх и посмотрим на ее код. Ага, я вижу, что она обращается к переменной. Ну вот, а к этой переменной идут обращения еще из 15 разных мест в коде…»

Проверка валидности

Допустим, вы доверяете процессу разработки и выпуска ПО, поэтому загружаете двоичный файл с сайта bitcoincore.org. Первая проблема заключается в том, что вы не знаете, управляется ли bitcoincore.org разработчиками Биткоина. Но даже если бы вы были в этом уверены, может оказаться, что сайт взломан, или не сайт, а DNS. Есть много способов, которыми вы можете в конечном итоге загрузить вредоносное ПО.

Чтобы обойти эту проблему, проекты с открытым исходным кодом почти всегда публикуют контрольную сумму, которая представляет собой буквенно-цифровую последовательность. Это означает, что если вы загружаете файл и пропускаете его через нужный скрипт, полученная вами контрольная сумма должна совпадать с той, которую называют разработчики. Куратор проекта обычно публикует контрольную сумму на странице загрузки. Теоретически это работает. Однако тот, кто взломал сайт, мог взломать и контрольную сумму, так что это не слишком надежно.

Следующий шаг - это подписывание контрольной суммы. Так, например, известный человек — в данном случае Владимир ван дер Лаан, ведущий (голландский) куратор проекта Bitcoin Core — подписывает контрольную сумму, используя общеизвестный ключ PGP. Этот ключ не менялся уже 10 лет. Итак, если вас не обманули в первый раз, всякий раз, когда вы загружаете обновленную версию, вы знаете, каким ключом PGP должны быть подписаны контрольные суммы.

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

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

Но откуда вы знаете, что он действительно это сделал? Вот тут все становится немного сложнее. В идеале вы запускаете ту же команду и точно так же компилируете код, а затем, надеюсь, получаете тот же результат.

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

Возьмем тривиальную программу на C++

int main() {
  return 0;
}

Эта программа завершается и возвращает 0, что даже более скучно, чем “Hello, World!”.

Скажем, вы скомпилировали этот код на Mac и получили программу размером 16536 байт. Когда вы повторяете это на другом Mac, он создает идентичный файл, о чем свидетельствует его контрольная сумма SHA-256. Но когда вы скомпилируете его на машине с Ubuntu, вы получите результат размером 15768 байт.

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

Если скомпилированная программа включает библиотеку (см. главу 4), то конечный результат зависит от точной версии библиотеки, которой довелось стоять на машине разработчиков, когда они создавали двоичный файл.

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

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

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

Решение проблемы при помощи Gitian

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

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

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

До середины 2021 года Bitcoin Core делал это с помощью Gitian. Если вкратце, то вы брали виртуальный или физический компьютер, загружали установочный DVD с конкретной версией Ubuntu и устанавливали ее. Это гарантировало, что у всех будет одинаковая начальная позиция, а поскольку Ubuntu широко используется, есть некоторая уверенность в том, что на установочном диске нет биткоин-бэкдора.

Давным-давно вы могли заказать компакт-диск обычной почтой, тогда как сейчас вы, вероятно, загрузите образ (из Интернета) и поместите его на USB-накопитель. Когда вы устанавливаете Ubuntu на виртуальную машину, ваш компьютер создает виртуальный DVD-плеер, используя образ с сервера.

Внутри этой машины вы создаете еще одну виртуальную машину, которая была спроектирована специально для того, чтобы гарантировать, что она создает идентичные двоичные файлы для всех, кто ее использует. Например, она использует фиксированное фальшивое время, так что если в финальный двоичный файл попадает временная метка, она будет одинаковой независимо от того, в какое время вы запускали компилятор. Она гарантирует, что все библиотеки имеют одинаковые версии, использует очень специфическую версию компилятора и т.д. Затем вы создаете Bitcoin Core внутри этой виртуальной машины и смотрите на контрольную сумму. Теперь она должна соответствовать загружаемым с bitcoincore.org файлам.

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

Однако, хотя в теории это звучит просто, на практике заставить систему работать всегда было очень сложно. Существует не так много проектов с открытым исходным кодом, которые используют Gitian — насколько нам известно, только Bitcoin Core и Tor. Даже большая часть альткоин-форков Bitcoin Core не заморачиваются этим процессом.

Зависимости, зависимости, зависимости

Однако это не единственная проблема.

Допустим, вы только что прочитали положения и условия Facebook, но оказалось, что эти положения и условия указывают на какой-то другой документ — возможно, на весь корпус законов США о копирайте. Так что теперь вы должны прочитать и его тоже.

Точно так же недостаточно просто просмотреть код Bitcoin Core, потому что, как и большинство компьютерных программ, он использует всевозможные другие вещи, известные как зависимости, в основном в виде библиотек (см. главу 4). И каждая библиотека, в свою очередь, может использовать какую-то другую библиотеку, и так далее, и тому подобное. Так что теперь вам нужно проверить их все.

Одно из самоограничений, принятых разработчиками Bitcoin Core - это сохранение как можно меньшего количества зависимостей, а также отказ от их постоянного обновления. Такие обновления требуют дополнительной проверки. И, конечно же, люди, которые поддерживают эти зависимости, знают, что Bitcoin Core их использует; тем больше причин быть в напряжении, чтобы убедиться, что эти проекты также тщательно проверяются.

И если окажется, что зависимость работает некорректно, она может украсть ваши монеты. Это действительно произошло по крайней мере в одном другом проекте в 2018 году. Проблема была связана с зависимостью зависимости от зависимости (sic) кошелька Copay. К счастью, ее быстро обнаружили, так что багом так никто и не воспользовался.

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

Они использовали npm, менеджер пакетов для Node.js. Это, в свою очередь, проект большого опенсорсного сообщества и весьма модульная система.
Каждый отдельный пакет ссылается на репозиторий на GitHub со своим куратором, который может в любое время выпускать обновления. Типичный фрагмент кода кошелька может косвенно привлекать до 10 000 зависимостей. Вы можете начать с пяти зависимостей, и каждая из них подтянет 50 зависимостей, а каждая из них подтянет еще 50 зависимостей. Если хотя бы один из разработчиков или кураторов любого из этих пакетов злонамерен, он смогут включить в код вредоносное ПО для кражи монет.

JavaScript-кошелек, такой как Copay, хранит закрытые ключи пользователя где-то в памяти браузера. К сожалению, это очень эгалитарное место, а это означает, что любой фрагмент кода на JavaScript может получить к нему доступ. Именно так вредоносное ПО, скрытое в подподзависимости, и может украсть монеты.
Для получения дополнительной информации см. эту статью: https://www.synopsys.com/blogs/software-security/malicious-dependency-supply-chain/

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

Решение

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

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

Кто собирает сборщик?

Ранее в этой главе мы обсуждали, как Gitian помогает создавать детерминированные сборки. Но что, если Gitian или любой из используемых им инструментов каким-то образом поврежден?

Например, поскольку Gitian использует Ubuntu, кто-то может сказать: «Эй, этот Биткойн - довольно крутой проект. И этот Ubuntu - довольно крутой проект. Позвольте мне внести небольшой вклад в исходники Ubuntu». Этим «вкладом» может быть небольшое изменение компилятора, поставляемого с Ubuntu. Он мог бы модифицировать этот компилятор так, чтобы всякий раз, когда тот компилирует Биткоин, он тайком менял некоторый код для кражи монет, но когда он компилирует любое другое программное обеспечение, то вел бы себя нормально.

Этот пример немного надуманный, и тот, кто попытается это сделать, скорее всего, будет пойман задолго до того, как нанесет какой-либо ущерб; коду компилятора и Ubuntu уделяется гораздо больше внимания, чем, например, экосистеме Node.js, о которой мы упоминали выше. Но общая стратегия атаки будет такой же. А когда на кону триллион долларов, злоумышленники могут быть очень изощренными и очень терпеливыми.

Теперь предположим, что все используют свой сборщик Gitian, который включает в себя этот гипотетический скомпрометированный компилятор Ubuntu. Это было бы очень, очень страшно, потому что это все еще будет инструмент для детерминированной сборки, ведь все будут использовать для сборки одно и то же вредоносное ПО.

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

Поэтому, если хотя бы один из инструментов, используемых разработчиками для создания Bitcoin Core, скомпрометирован, детерминированные сборки не помогут. Каждый разработчик, использующий процесс сборки Gitian, будет усердно создавать одно и то же вредоносное ПО. Бинарный код не будет соответствовать исходному коду.

Есть надежда, что люди, которые поддерживают все эти компиляторы и все остальное, знают, что делают, и никогда не пропустят никакой лазейки. Это проблема не только пользователей Биткоина. Весь мир полагается на эту проверку, которая в основном проводится волонтерами.

Но что тут можно было бы улучшить?

Внедрение Guix

Ключ в том, чтобы сделать все с открытым исходным кодом и детерминированной сборкой. Каждая библиотека, каждый драйвер принтера, каждый компилятор — все. Чтобы Bitcoin Core действительно был детерминированной сборкой, каждая из его зависимостей должна быть детерминированной сборкой, как и каждый инструмент, используемый для ее сборки, включая компилятор. В идеале аппаратное обеспечение тоже, но это совсем другой клубок змей.

Вот здесь-то и появляется Guix. Этот GNU проект существует уже десять лет, но несколько лет назад Карл Донг из Chaincode Labs начал работу по замене Gitian на Guix, что, наконец, и произошло в версии 22 Bitcoin Core. Это включало в себя внесение изменений как в Bitcoin Core, так и в Guix.

См. также презентацию Карла Донга: https://www.youtube.com/watch?v=I2iShmUTEl8

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

Даже двоичный код можно рассматривать как исходный код. Это просто набор инструкций для процессора. И этот конкретный машинный код хорошо задокументирован: https://git.savannah.nongnu.org/cgit/stage0.git/tree/README.org

Все, что он делает - это читает исходный код и компилирует его. Но как это сделать, если компилятора нет?

Ну, фактически, этот первый бинарник обеспечивает начальную загрузку.

Лишь в теории, поскольку пока этот идеал еще не достигнут. В 2020 году проект Guix поставлялся с бинарными файлами объемом 60 МБ, которым приходится доверять. Это большое улучшение по сравнению с загрузкой Ubuntu объемом более 1 ГБ, используемой Gitian: https://guix.gnu.org/en/blog/2020/guix-further-reduces-bootstrap-seed-to-25/.

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

Первая программа, которую вы ему скармливаете — чрезвычайно простой компилятор.

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

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

Затем этот компилятор собирает кучу инструментов из исходного кода, в конечном итоге создавая систему, очень похожую на Gitian, то есть систему сборки, которая избегает временных меток, не использует ничего другого с вашего компьютера и т. д. В принципе, он мог бы собрать целую операционную систему. Таким образом, ваша виртуальная или физическая машина работала бы под управлением операционной системы, которую вы создали с нуля. Но в нашем случае с нуля создаются только инструменты для компиляции, и как только эти скомпилированные инструменты у нас есть, они могут просто начать сборку Bitcoin Core, как это делалось бы и без них.

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

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

Поддержите проект(ы) на цепочке

HCN имеет две активные краудфандинговые компании на TallyCoin, которые собирают средства ончейн:

https://tallycoin.app/@hypecoinnews/

Или LN платежом

LNURL1DP68GURN8GHJ7AMPD3KX2AR0VEEKZAR0WD5XJTNRDAKJ7TNHV4KXCTTTDEHHWM30D3H82UNVWQHKXETWW3EXZMRKD9HKCCF4XYK4YTL3

Или [email protected]

Например из @LightningTipBot в Телеграме

/send 100 [email protected]

Или начните пользоваться LN кошельком типа Valet.