Человечество отупело

Вот примерно каждый год с большим пафосом сообщают, что где-то в очередной раз с помощью “3D-печати” сумели построить дом – вот как-то так:

https://ru.euronews.com/2020/07/08/3d-printed-house-in-belgium

Комментаторы в интернете тут же предрекают революцию в строительстве и все такое – а мне что-то кажется, что все это в плане хайтека и рядом не стояло с индустриальным домостроением. Обычная панельная хрущевка, или, если вам милее одноэтажная Америка – Levitt house, в сравнении с любым таким напечатанным на 3D-принтере сарайчиком – технологический шедевр, причем во всех отношениях – от массового производства стандартных комплектующих до обеспечивающей все это логистики.

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

О чем говорят мужчины

Форум с преимущественно мужским населением, тема про тиндер. Фотки блядей, малолеток и блядей-малолеток с претензией на интеллектуальность в подписях обсуждать надоело, ждущих принца РСП – тем более, так что разговор плавно сполз в обсуждение методов триангуляции, геодезии и даже систем управления огнем английских линкоров ПМВ – а все оттого, что двое почти одновременно увидели одну и ту же смешную фотку.

Бесит, сука

Вот посмотрел я на то, из чего состоит rawStoreBallotTx, передающийся на сервер и сохраняющийся в блокчейн при электронном голосовании в Москве. Разбираться с обширным кодом клиента Exonum на Javascript я не стал, а просто глянул на сами данные.

58 1c c8 b3 ed 95 97 f3 f4 ab c7 76 1d 17 c3 02
a3 d5 3e 59 c8 c7 6c 55 04 4b f0 d3 91 e7 fc c1
00 00 e9 03 06 00 0a 40 64 61 38 35 66 61 66 38
34 66 61 65 62 30 65 39 30 65 66 33 31 61 32 65
37 32 35 32 34 31 35 31 66 62 61 63 30 65 32 66
36 65 36 63 34 39 38 33 36 30 31 33 64 36 34 35
32 39 34 36 39 38 38 64 10 4d 1a 56 0a 14 bc ef
fd 5e 5e a6 8a 6b b2 d9 04 31 dd 51 2e 11 5e 4c
44 e3 12 1a 0a 18 9b 37 cb a5 f0 26 25 ca 74 45
00 21 88 e1 59 ca 56 ee d5 24 dd 25 d3 26 1a 22
0a 20 57 1d b7 6c 24 f5 85 22 7e 75 4c ca e6 97
99 8f f6 22 6b 24 58 94 b9 0d 57 b0 f3 d5 9a 0c
16 01 9d c1 e2 eb 1d 8d 8a cf 12 60 27 3f f0 2e
cc ba 5d 9f 61 e0 ba 51 a7 d2 26 78 82 d1 05 6d
a5 92 93 75 7d 5a c5 23 e0 45 f7 f9 8d 6b ed 4f
33 fe d7 c2 e2 48 77 c6 61 9b 10 86 b3 c7 a7 73
3f 0d

Первые 32 байта – это некий идентификатор под названием accountAddressBlock; дальше начинается что-то непонятное – но резко выделяется довольно длинная последовательность из байт в диапазоне 30-39 и 60-66 – это цифры и буквы от a до f. Выпишем ее отдельно (справа – символы, которым эти байты соответствуют):

                        64 61 38 35 66 61 66 38            da85faf8
34 66 61 65 62 30 65 39 30 65 66 33 31 61 32 65    4faeb0e90ef31a2e
37 32 35 32 34 31 35 31 66 62 61 63 30 65 32 66    72524151fbac0e2f
36 65 36 63 34 39 38 33 36 30 31 33 64 36 34 35    6e6c49836013d645
32 39 34 36 39 38 38 64                            2946988d

Что это? А это как раз voting id, идентификатор голосования – только зачем-то переданный в виде текста. Сразу же хочется обратить внимание на байт 40, стоящий непосредственно перед voting id – да и вообще на всю последовательность 00 00 e9 03 06 00 0a 40 – хочется считать ее неким заголовком, а 40 – длиной поля. По аналогии находим еще три подобных последовательности – “заголовок” 10 4d 1a 56 0a 14 (в кавычках, и я сейчас объясню, почему) предшествует 20 байтам

                                          bc ef
fd 5e 5e a6 8a 6b b2 d9 04 31 dd 51 2e 11 5e 4c
44 e3

Заголовок 12 1a 0a 18 – последовательности из 24 байт:

                  9b 37 cb a5 f0 26 25 ca 74 45
00 21 88 e1 59 ca 56 ee d5 24 dd 25 d3 26

А заголовок 1a 22 0a 20 – 32 байтам:

      57 1d b7 6c 24 f5 85 22 7e 75 4c ca e6 97
99 8f f6 22 6b 24 58 94 b9 0d 57 b0 f3 d5 9a 0c
16 01

Казалось бы, это вся информация для записи в блокчейн? Но нет – не хватает district id, номера “участка” (здесь он принимал логичные значения 77 и 52 для Москвы и Нижнего Новгорода соответственно). Где он мог потеряться? Оказывается, первый “заголовок” из 6 байт – это на самом деле еще и поле с номером участка, 10 4d (4d в шестнадцатиричной системе – это как раз 77).

В оставшихся 64 байтах я какой-то структуры уже не увидел, но в целом для расшифровки голоса они нам уже не нужны. Вспоминаем структуру данных для передачи – это должны быть зашифрованный голос, nonce и публичный ключ пользователя; в используемой для голосования библиотеке NaCl длина nonce и публичного ключа как раз составляет 24 и 32 байта соответственно, 20 байт – это длина сообщения. Кажется, мы нашли все необходимое? Проверяем несложной программой:

unsigned char message[] = {
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0xbc, 0xef, 0xfd, 0x5e, 0x5e, 0xa6, 0x8a, 0x6b,
    0xb2, 0xd9, 0x04, 0x31, 0xdd, 0x51, 0x2e, 0x11,
    0x5e, 0x4c, 0x44, 0xe3
}; // first 16 bytes must be 0

unsigned char nonce[] = {
    0x9b, 0x37, 0xcb, 0xa5, 0xf0, 0x26, 0x25, 0xca,
    0x74, 0x45, 0x00, 0x21, 0x88, 0xe1, 0x59, 0xca,
    0x56, 0xee, 0xd5, 0x24, 0xdd, 0x25, 0xd3, 0x26
};

unsigned char public_key[] = {
    0x57, 0x1d, 0xb7, 0x6c, 0x24, 0xf5, 0x85, 0x22,
    0x7e, 0x75, 0x4c, 0xca, 0xe6, 0x97, 0x99, 0x8f,
    0xf6, 0x22, 0x6b, 0x24, 0x58, 0x94, 0xb9, 0x0d,
    0x57, 0xb0, 0xf3, 0xd5, 0x9a, 0x0c, 0x16, 0x01
};

unsigned char dit_private_key[] = {
    0xdb, 0x77, 0xd6, 0x2f, 0xc8, 0x87, 0x26, 0xf1,
    0xc5, 0xa6, 0xb7, 0x9b, 0x00, 0x3b, 0x3b, 0xca,
    0x83, 0x34, 0x9e, 0x33, 0x43, 0x37, 0xde, 0x84,
    0xbd, 0x17, 0x34, 0x4a, 0xb6, 0x01, 0xdb, 0x74
};

unsigned char buf[sizeof(message)] = { 0 };

int res, vote;

res = crypto_box_open(buf, message, sizeof(message), nonce, public_key, dit_private_key);
vote = *((int*)(buf + crypto_secretbox_ZEROBYTES));
printf("%x\r\n", vote);

И действительно – расшифрованное сообщение содержит 4 байта 1a d5 be 0d – голос “против” на этом голосовании.

Так вот, возвращаясь к тому, что бесит. Во-первых, бесят просранные талантливыми фронтендерами 32 байта – зачем засовывать voting id в виде строки? Во-вторых – бесит полное отсутствие описание формата передаваемых данных – вот я его разобрал довольно элементарным способом, но пока так и не знаю о назначении еще 64 байт в конце – похоже, что это как-то связано с блокчейном и криптографической подписью для него, но я не уверен.

Боженька, побей их всех по голове ЕСПД.

Iditi srati!

К какой-то музыкальной программе есть плагин стоимостью 499$, умеющий изображать пение хора – и в числе прочих функций, можно заставить хор петь разные слоги. К сожалению, набор вариантов довольно ограничен, и на русском языке удалось собрать только фразу “Идите срать!” – точнее, “I-di-tis-ra-ti”. В видео – с 8:00:

Хор, старательно выводящий эту фразу, просто офигенен.

По мотивам очередного выступления

В эту субботу в очередной раз собрались обсуждать электронные выборы, и в этот раз ступора уже не было – то ли Шевяков не курил в камеру, то ли действительно надо привыкать к “публичным” выступлениям в Зуме :) Полную запись можно посмотреть на ютубе, а тут – перескажу свои основные тезисы.

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

Начну с того, что тайна голосования, которая волнует довольно многих избирателей и экспертов, действительно держалась на честном слове ДИТа. Это сложно назвать виной разработчиков и эксплуатантов системы – так уж получилось, что занимается этим ровно одно ведомство, а надо разделять систему как минимум на две части – чуть дальше станет понятно, почему, но Александр Исавнин действительно много раз задавал вопрос об этом, а ответов лучше, чем “поверьте нам на слово” техническая рабочая группа так и не получила. Опять же, отмечу, вслед за Олегом, что фактов нарушения тайны голосования пока никто не зафиксировал.

С другой стороны – некоторые технические меры для защиты тайны голосования были приняты. Да, при изучении кода системы заметно, что авторы попытались реализовать вариант хорошо известного “протокола двух агентств“, который защищает тайну голосования, и в принципе система позволяет разнести авторизацию пользователя и собственно голосование по разным ведомствам, что критично для этого семейства протоколов. Это в целом довольно положительно характеризует разработчиков системы – как минимум обращение к известным протоколам позволяет избавиться от всевозможных неожиданностей.

Чуть подробнее опишу протокол голосования – при этом постараюсь изложить это все максимально упрощенно. К сожалению, в Википедии описан похожий, но отличающийся протокол, а “Прикладная криптография” Шнайера есть под рукой далеко не у всех – хотя протокол, похожий на ДИТовский, там описан под названием “Голосование с двумя Центральными комиссиями”. Основаны все эти протоколы на криптографии с открытым ключом – здесь знать о ней надо лишь то, что используется пара из открытого и закрытого ключа, таких, что сообщение, зашифрованное открытым ключом, можно расшифровать только с помощью закрытого, и наоборот – если сообщение зашифровано закрытым ключом, то оно расшифровывается открытым. Закрытый ключ обычно держится в секрете, а открытый доступен всем.

Итак, для начала – подготовка к голосованию, которая с точки зрения криптографии состоит в том, что создается пара из открытого и закрытого ключа. Закрытый ключ разделяется на части, которые передаются членам избирательной комиссии – а они, как и на обычных выборах, представляют самые разные политические силы – например, в этот раз одним из членов комиссии был Григорий Мелконянц из “Голоса“. Главное здесь это то, что закрытый ключ не будет известен никому вплоть до момента его “сборки” – а открытый ключ, наоборот, будет опубликован во время голосования. Происходит процедура генерации и разделения ключей в торжественной обстановке, с банкетом, фуршетом, журналистками с декольте – в общем, “сожалею, что не был при сей баталии хотя бы мичманом!”

Дальше происходит собственно голосование – пройдя авторизацию на “Госуслугах” или mos.ru, избиратель получает от сервера регистрации уникальный номер (GUID) и перенаправляется на сервер голосования, по адресу “$host/election/check/$guid” (строка 94 файла Ballot.php в приложении form, опубликованном в ДИТовском гитхабе).

Сервер голосования – это приложение ballot (обратите внимание, что работало оно на другом домене – авторизация происходила на 2020og.ru, а собственно голосование – на elec.moscow, можно считать это демонстрацией возможности разделения авторизации и голосования), и там нам очень интересно содержимое файла election.js – точнее, то, что начинается после 243 строки. Там происходит следующее: с помощью библиотеки NaCl создается пара из “пользовательского” открытого и закрытого ключа, случайным образом выбирается значение nonce, сообщение из выбора избирателя и nonce зашифровывается сначала с помощью закрытого ключа избирателя, а затем – с помощью открытого ключа системы голосования. Пара из зашифрованного таким образом сообщения и открытого ключа избирателя обладает следующим очень полезным свойством – зная закрытый ключ системы голосования, можно убедиться, что сообщение (голос) зашифровано именно закрытым ключом избирателя, и никаким другим; не зная закрытого ключа избирателя, невозможно “подменить” голос; не зная закрытого ключа системы голосования – нельзя сделать с этим сообщением вообще ничего.

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

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

Попробуем теперь перечислить свойства такой системы.

  • Получить GUID и проголосовать могут лишь зарегистрированные избиратели – это обеспечивается не только криптографией, но и тем, что авторизацией пользователей занимаются “Госуслуги”;
  • Cервер голосования не может «вбрасывать» в хранилище голоса по своему усмотрению – их не может быть больше, чем на сервере авторизации было создано GUID’ов (список избирателей с отметками, кто голосовал, а кто нет, доступен для просмотра наблюдателям, как и на обычных «бумажных» выборах);
  • В ходе голосования голос пользователя расшифровать нельзя (и, допустим, отклонить его под видом технической ошибки);
  • После голосования голос пользователя нельзя изменить (так как никому, кроме самого пользователя, неизвестен закрытый ключ – и то для его сохранения надо предпринять некие усилия);
  • Каждый голосовавший может найти свой голос в опубликованных результатах;
  • Каждый может самостоятельно проверить правильность подсчета голосов;
  • Если сервер авторизации не сохраняет выданные пользователям GUID’ы, а сервер голосования не сохраняет связку между GUID и голосом (тут придется поверить разработчикам и эксплуатантам на слово – но скорее всего, это так) – никто не может узнать, какой голос принадлежит какому пользователю. Кстати, обращу внимание, что для раскрытия тайны голосования требуются согласованные действия операторов сервера авторизации и голосования – собственно, поэтому этот протокол и называется “протоколом двух агентств”.

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

Теперь перейду к плохому. Во-первых – зачем я полез смотреть код? Полез я туда именно для того, чтобы посмотреть, какой конкретно протокол голосования там реализован, и повторюсь, остался более-менее доволен увиденным – но хочу сказать, что программистом на PHP и Javascript я не являюсь, и гораздо проще было бы прочитать описание алгоритма голосования в более явном виде. Те же ГОСТы 34 серии предусматривают документ под названием “Описание алгоритма” – и может быть, я бы успокоился, увидев его и сравнив с реализацией. Кстати, еще две шпилечки в адрес ДИТа – во-первых, на вопрос “где же, все-таки, предусмотренная ГОСТами документация”, был дан ответ в духе “ну мы систему запустили, документацию напишем потом”. Хочу заметить, что у них на сайте выложен ГОСТ 34.201-89, а жить по нему они не хотят – там ясно сказано, что документ под названием “Описание алгоритма”, например, пишется на этапе технического проекта, еще до разработки каких-либо программ, и уж точно – до ввода в эксплуатацию. Точно так же хотелось бы напомнить о существовании ГОСТ РД 50-34.698-90, где можно увидеть мои хотелки в плане описания криптографических протоколов в разделе 7.1.

Вообще, в плане открытости и прозрачности электронного голосования есть куда расти. Это вовсе не какие-то абстрактные соображения, информация о работе системы и том, как в ней все устроено, должна быть доступна для широкого круга заинтересованных лиц, а не избранных “экспертов” – с которыми еще и делились информацией крайне неохотно. В противном случае мы получаем, например, пресловутую публикацию “Медузы” о том, что оказывается, если знать ключи шифрования и случайное число (то самое nonce), выбранное браузером пользователя при подготовке зашифрованного бюллетеня к отправке, можно проверить, правильно ли учтен этот бюллетень в итоговом протоколе. Открытие это сродни торжественной констатации факта “вода – мокрая”.

Вообще, во многих алгоритмах тайного голосования возможность проверить правильность учета своего бюллетеня, найдя его в итоговом протоколе, предусмотрена штатно (к ним относятся и вышеназванные) – хотя тот же Шнайер все же намекает, что такая возможность при неправильном ее использовании – прямой путь к организованной скупке голосов. Здесь же получилось так, что из-за некоторой скрытности авторов системы голосования возможность проверить правильность своего голоса журналисты объявили “уязвимостью”.

Довольно странно была организована и публикация исходных кодов системы – опубликованы были лишь два обеспечивающих голосование веб-приложения и код смарт-контрактов блокчейна, причем некоторые особенно интересные с точки зрения стойкости шифрования и сохраняемой информации места в исходном коде были почищены. Примерно так же выглядела и переданная рабочей группе “документация”, причем местами доходило до смешного – цитирую один из документов: “хеш длиной YYY кодируется в XXX для сохранения целостности”. При этом узнать длину хеша и способ его кодирования можно было совершенно тривиальным образом – например, открыв отладчик страницы во время голосования или заглянув в опубликованные исходники. Стоит еще сказать, что местами исходный код производит впечатление полуготового прототипа, к примеру, в некоторых местах остались комментарии в духе “удалить после тестирования”.

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

Еще одно странное явление – это подключение некоей “системы антифрода” (по словам представителей ДИТ), которая изначально не была заявлена. Фактически на каждую веб-страницу голосования внедрялся посторонний код на Javascript, не выложенный в публичный доступ заранее.

Скажу еще немного о блокчейне. Довольно часто говорят, что именно благодаря блокчейну эта система голосования надежна и безопасна. Вот даже на ютубе в комментариях задали вопрос – “СМИ писали, что система надёжно защищена и использование блокчейна гарантирует от подмены голосов и деанонимизации, так ли это?” – на самом деле это не совсем так. Блокчейн обеспечивает хранение уже принятых голосов, и существенно затрудняет какие-либо изменения данных постфактум – но в “модели угроз” для электронного голосования это лишь один из многих рисков. О том, как система защищена от подмены голосов и деанонимизации я сказал выше, блокчейн как таковой для этого не нужен. Впрочем, если в этот раз блокчейн работал почти хорошо (за исключением его “падения” на несколько часов в первый день голосования) – то почему бы и нет? С другой стороны, представляется, что блокчейн в целом – это “узкое место” системы, Александр Щербаков высказывал свои соображения по этому поводу.

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

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

В целом, конечно, вопросы к разработчикам имеющейся системы голосования – скорее не технические, а организационные. Повторюсь, что с точки зрения используемых протоколов голосования система довольно адекватна, а с точки зрения реализации – к сожалению, пока приходится верить на слово, что используемая система голосования совпадает с тем, что было показано публике. В будущем, конечно, хотелось бы иметь возможность изучить систему голосования не только по кускам кода на Github, а еще и по более-менее нормальной документации, причем целиком, а не отдельными частями “тут покажем, тут не покажем”.

Диалоги про демократию

monarch

Замечу, что 99% диалогов с борцами за чистоту выборов заканчиваются тем, что они скатываются в авторитаризм в диапазоне от просвещенной монархии до диктатуры Туркмен-баши.

А вот вам красивый график

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

http://observer2020.mos.ru/observer/downloads/ballots_decrypted_2020-07-01T19:47:00.csv

Получился такой вот забавный график (кликабельно):

e-voting

Интересно было бы сопоставить явно видимые на графике “скачки” с разными событиями прошедшей недели – например, резкий прирост голосов “за” в Москве в 17:00 25 июня, или синхронные скачки “против” в Москве и Нижнем в 16:00 26 июня и в 14:00 29 июня.

Наговняканный за полчаса код прилагается:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct voteblock {
	int time; // time in seconds since day start
	int res[2][2];
};

voteblock votes[2300000]; // max. block number is about 2.27M

int gettime(char *token){
	token[2] = '\0';
	token[5] = '\0';

	int hours = atoi(token);
	int minutes = atoi(token + 3);
	int seconds = atoi(token + 6);

	return hours * 60 * 60 + minutes * 60 + seconds;
}

void process_vote(char *buf){
	char *token;
	int district, res;

	token = strtok(buf, "\";"); // номер голоса
	int n = atoi(token);

	token = strtok(NULL, "\";"); // избир. округ
	if (strcmp(token, "77") == 0){
		district = 0;
	} else if (strcmp(token, "52") == 0) {
		district = 1;
	} else {
		fprintf(stderr, "Unknown district %s\r\n", token);
		return;
	}

	strtok(NULL, "\";"); // адрес голоса в блокчейне
	token = strtok(NULL, "\";#"); // блок голоса
	int block = atoi(token);

	token = strtok(NULL, "\";"); // время записи блока голоса
	int time = gettime(token);

	strtok(NULL, ";"); // зашифрованный голос
	token = strtok(NULL, "\";"); // расшифрованный голос
	if (strcmp(token, "2212294583") == 0){
		res = 0;
	} else if (strcmp(token, "450215437") == 0) {
		res = 1;
	} else {
		fprintf(stderr, "Unknown vote result %s\r\n", token);
		return;
	}

	strtok(NULL, "\";"); // блок расшифровки
	strtok(NULL, "\";"); // время записи блока расшифровки

	strtok(NULL, "\";"); // транзакция

	votes[block].time = time;
	votes[block].res[district][res]++;
}

void print_votes_table(){
	int day = 0;
	int starttime = 7 * 3600; // 10:00:00 MSK = 7:00:00 UTC
	int delta = 10 * 60; // 10 minutes granularity
	int allvotes[2][2] = { 0 };

	// to put the last line
	votes[sizeof(votes) / sizeof(voteblock) - 1].time = INT_MAX;

	for (int i = 0; i < sizeof(votes) / sizeof(voteblock); i++){
		if (votes[i].time >= 0){
			if ((votes[i].time > starttime + delta) || (votes[i].time < starttime)) {
				/* either end of current stats block or end of day*/
				if (votes[i].time < starttime)	{
					starttime = 0;
					day++;
				} else {
					starttime += delta;
				}

				// finalize this line
				printf("%i\t%i\t%i\t%i\t%i\t%i\r\n", day, starttime, allvotes[0][0], allvotes[0][1], allvotes[1][0], allvotes[1][1]);
			}

			allvotes[0][0] += votes[i].res[0][0];
			allvotes[0][1] += votes[i].res[0][1];
			allvotes[1][0] += votes[i].res[1][0];
			allvotes[1][1] += votes[i].res[1][1];
		}
	}
}

int main(void){
	char buf[1024]; // should be enough

	fgets(buf, sizeof(buf), stdin); // skip first line

	while (!feof(stdin)){
		fgets(buf, sizeof(buf), stdin);
		process_vote(buf);
	}

	print_votes_table();
	return 0;
}

Обожаю медузоньку

Вот чувачки пишут: “мы нашли уязвимость в электронном голосовании, не знаем, как этим воспользоваться, но нас всех наебут!!!!1111″:

https://meduza.io/feature/2020/07/01/meduza-nashla-uyazvimost-v-sisteme-internet-golosovaniya-chast-golosov-mozhno-rasshifrovat-esche-do-ofitsialnogo-podscheta

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

А вообще такие громкие заявления – от банальной недоступности информации, как это все работает. Можно взять пример хотя бы с Эстонии, систему электронного голосования которой разобрали вдоль и поперек:

https://arxiv.org/pdf/1606.08654.pdf

Хотите описания “для широкой аудитории”? Пожалуйста:

http://www.vvk.ee/public/dok/General_Description_E-Voting_2010.pdf

Хотите анализ уязвимостей? Никаких проблем:

http://www.vvk.ee/public/dok/E-voting_concept_security_analysis_and_measures_2010.pdf

Опубликован полный исходный код (а не как у ДИТ Москвы, с купюрами в самых интересных местах):

https://github.com/vvk-ehk/ivxv

Вот такой уровень открытости и не позволяет выдавать очевидное за невероятные находки.

Кстати, на злободневную тему

Проголосовал против. И вот почему: не имею вообще ничего против “политической” части поправок. Даже пресловутое “обнуление” – акция разовая, а сторонникам святости конституции тут хорошо бы порадоваться тому, что уже очень скоро (в историческом масштабе) так ненавистное им слово “подряд” наконец-то уберут.

Но простите, поправки в главу 3 – это что-то кошмарное. Местами – популизм, особенно некоторые формулировки, по сути повторяющие и так уже имеющиеся федеральные законы. Местами – плохой популизм, типа “отмены” приоритета международных договоров – его и так никогда не было. Кое-где текст впихнут просто по принципу “чтобы было” – поправкам в 75 статью было бы самое место в 37, например. Где-то по этому принципу получается просто сборная солянка вроде статьи 671. И как положено – все это написано совершенно чудовищно. И прежняя версия была не фонтан, но вот некоторые формулировки из новой – это просто шедевр косноязычия, с моей точки зрения. В общем, за это – незачет.

PS Не знаю, говорили об этом или нет, но вообще принять эти поправки можно было и без всякого голосования, по описанной в статье 136 процедуре, но это было бы куда дольше. В чем причина спешки – не знаю, скорее всего, хотели успеть до единого дня голосования осенью (поправки в статьи 77 и 78, например).

PS/2 Агитация как “за”, так и “против” была совершенно омерзительна.

S in IoT stands for security

smart-scale

А вот вопрос имею

Зачем у автоматического выключателя вот эти отверстия на корпусе?

autoswitch

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

Угробил планшет

Уронил и разбил экран на своем заслуженном Lenovo A10-70, с которым были выиграны многочисленные соревнования по р3к (блин, надо бы про это все как-нибудь написать, а то я с сезона 2017 года писать про свои успехи бросил). Короче говоря – а что сейчас стоит брать с учетом не сильно изменившихся с 2015 года требований:

- свежая версия Android, с минимальным количеством постороннего говна;
- экран 10″, не менее 200 dpi;
- сотовая связь, WiFi, Bluetooth (не хуже 4.0), хорошо работающий GPS.

Я так понимаю, стоит остаться лояльным марке и взять Lenovo TB-X605L?

Еще про доставку у строительных интернет-магазинов

На этой неделе решил пощупать “220 Вольт“. Эти товарищи вообще отличились – две пачки со сварочными электродами везут DPD аж из Питера. По каким-то странным причинам доставка в пункты выдачи в виде их же собственных магазинов невозможна.

Боженька, почему они с такой клоунской логистикой еще не сдохли?

Про IT-компании

Обсуждают льготы для “IT-компаний”, предложенные вчера Путиным. Во-первых, некоторое недоумение вызывает вопрос – что же такое “IT-компания”? Ответ на него очевиден, но многим неизвестен – это компания, включенная в особый список Минцирка (МИНистерство ЦИфрового Развития, связи и массовых Коммуникаций):

https://digital.gov.ru/ru/activity/govservices/1/

По состоянию на 15.06.2020 в списке, представленном в виде таблички формата xls (КМПВ, узнаем, почему не csv или что-то “открытое”, а за использование старых форматов M$ будем отправлять на Колыму), числится 11452 организации. Кроме того, в списке есть ОГРНы и ИННы компаний – а поэтому на сайтах вроде какого-нибудь zachestnyibiznes несложно найти информацию о любой из них – например, о ООО “ТЛМ Ком” под номером 1 в списке:

https://zachestnyibiznes.ru/company/ul/1045207810218_5262128494_OOO-TLM-KOM

К сожалению, фирма уже ликвидирована, а вот номер 2 в списке – ООО “ТЭЛМА Софт” – живет и здравствует, переименовавшись в “Харман”:

https://zachestnyibiznes.ru/company/ul/1025203734841_5262068365_OOO-HARMAN

Конечно, правильнее было бы брать эту информацию у налоговой – как, собственно, все эти зачестные бизнесы и руспрофайлы и поступают – но выгрузка ЕГРЮЛ довольно сложна и кишит странностями.

И еще пара вводных – во-первых, льготы по налогу на прибыль, очевидно, распространяются на тех, кто его платит – то есть сидит на общей системе налогообложения, а не на “упрощенке”; во-вторых, количество работников в компании должно быть не менее 7 человек. Если вы представляете себе среднюю российскую ИТ-компанию – то, наверное, уже почувствовали, что на льготы могут расчитывать далеко не все. Скажем, очень многие сидят на “упрощенке” – она действительно сильно упрощает жизнь, особенно если ваши клиенты – такие же “упрощенщики” или физические лица. Численность в 7+ человек – тоже сравнительно много, особенно учитывая популярность работы с фрилансерами, когда в штате фирмы – буквально три-четыре наиболее незаменимых человека, а остальные работают по договорам ГПХ или регистрируются, как индивидуальные предприниматели (в особо запущенных случаях это превращается в “схему Ходорковского”, но не будем показывать пальцем :) ).

В общем, возникает желание проверить – а многие ли в этом списке подпадают под льготы. Основная проблема – невозможно надежно определить, пользуется ли контора УСН или нет по страничке с данными. Для части фирм на УСН встречается надпись “Упрощенная система налогообложения (УСН) (на 2018 год)” – но сделать по ней или ее отсутствию надежный вывод о том, каким налоговым режимом пользуется фирма, нельзя. Впрочем, как первое приближение – сойдет. Не для всех фирм есть сведения о количестве работников – в первую очередь это касается недавно зарегистрированных, но им все равно придется ждать не менее 9 месяцев, чтобы получить право на льготы. В общем, пишем под пивко несложный питоновский скрипт (я нихера не питонист, так что особо палками и камнями не кидайтесь):


import requests
import re
from bs4 import BeautifulSoup
import pandas as pd

elegible = 0

def get_info(id):
    global elegible

    working = False
    workers = False
    osn = True # так как система налогообложения определяется неоднозначно

    # на случай 1167847383824 (бывш. 1037841001681) и т. п.
    if not isinstance(id, int):
        id = int(id.split(' ')[0])

    # поиск по ОГРН
    url = 'https://zachestnyibiznes.ru/search?query=' + str(id)
    r = requests.get(url) # получаем результаты поиска
    if r.status_code != 200:
        print('Error ' + str(r.status_code) + ' for OGRN=' + str(id))
        return

    # вытаскиваем ссылку на страницу компании
    soup = BeautifulSoup(r.content, features='lxml')
    res = soup.find_all('td', class_='hidden-print')
    url = res[0].find_all('a')[0].get('href')

    # загружаем страницу с информацией о компании
    url = 'https://zachestnyibiznes.ru' + url
    r = requests.get(url) # получаем результаты поиска
    if r.status_code != 200:
        print('Error ' + str(r.status_code) + ' for OGRN=' + str(id))
        return

    # вытаскиваем из страницы нужную нам информацию
    soup = BeautifulSoup(r.content, features='lxml')
    # для начала sanity check, проверим ОГРН на совпадение
    res = soup.find_all('span', id='ogrn')
    ogrn = res[0].contents[0]
    if id != int(ogrn):
        print('Bad data page for OGRN ' + str(id))

    # в этом div-е сосредоточена вся интересующая нас информация
    info = soup.find('div', class_='m-t-5')

    # ищем, действующее предприятие или нет
    status_string = info.find(string=re.compile('Статус'))
    if status_string:
        status_value = status_string.next_sibling.next_sibling.next_sibling.string
        if status_value == 'Действующее':
            working = True

    # проверяем численность работников
    workers_string = info.find(string=re.compile('Среднесписочная численность работников'))
    if workers_string:
        workers_value = workers_string.next_sibling.next_sibling.next_sibling.next_sibling.string
        if int(workers_value) >= 7:
            workers = True

    # проверяем систему налогообложения
    usn_string = info.find(string=re.compile('Упрощенная система налогообложения'))
    if usn_string:
        osn = False

    if working and workers and osn:
        elegible = elegible+1

data = pd.read_excel('200615-reestrot15062020.xls', usecols='C,D', names=['Name', 'OGRN'])
for id in data.OGRN:
    try:
        get_info(id)
    except:
        print('Something went wrong for OGRN ' + str(id))
print('Number of elegible companies ' + str(elegible) + '/' + str(data.OGRN.size))

Запускаем и идем допивать пиво – работает долго, при этом в списке обнаруживается куча странностей – вроде компаний с несуществующими ОГРН; иногда это просто опечатки, типа лишней или забытой цифры, иногда – что-то непонятное. Минцирк оправдывает свое название.

В конечном итоге оказывается, что на льготы могут претендовать всего лишь 3028 компаний из списка (я не разбирал косячные ОГРНы вручную – их у меня набралось 18 штук) – впечатляет, да? В общем, радоваться могут лишь “избранные”, остальных это вряд ли коснется.

Да, оценка несколько завышена – если внимательно почитать требования, необходимые для получения льгот, на сайте министерства, то там обнаружатся дополнительные требования – например, 90% дохода фирма должна получать от “околоайтишной” деятельности (всякого рода Яндексы с Додо-пиццей в пролете):

доля доходов от реализации экземпляров программ для ЭВМ, баз данных, передачи исключительных прав на программы для ЭВМ, базы данных, предоставления прав использования программ для ЭВМ, баз данных по лицензионным договорам, от оказания услуг (выполнения работ) по разработке, адаптации и модификации программ для ЭВМ, баз данных (программных средств и информационных продуктов вычислительной техники), а также услуг (работ) по установке, тестированию и сопровождению указанных программ для ЭВМ, баз данных по итогам отчетного (расчетного) периода составляет не менее 90 процентов в сумме всех доходов организации за указанный период;

Люблю ютуб

Из каментов под сварочным видео в ютубе:

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

Леруа Мерлен сегодня не обрадовал

В общем, решил я тут немного облагородить компрессор имени совавтопрома и сделать для него металлическую раму. Прикинул, что лучше всего для этого подойдет профильная труба 40*20 мм и заказал в леруашечке трубу и электроды “МР-3С” (в кавычках, потому что я как-то сомневаюсь, что там продается что-то, соответствующее этому названию) 2 мм – так как варить тонкую трубу валявшимися у меня 4 мм “Монолит РЦ” было как-то очково.

Для начала – на сайте наебывают со сроками доставки :) На страничке товара радостно пишут “доставка завтра”, при оформлении заказа “завтра” превращается в “скажите спасибо, что через три дня”. Во-вторых – какой-то Чурбанбек на складе умудрился вместо трубы 40*20 положить трубу 40*40, которая в полтора раза тяжелее, ну и в третьих – леруашные электроды вообще нифига не зажигают дугу – такое впечатление, что деревянной палочкой варить получится лучше. Пацаны, не берите это говно:

https://leroymerlin.ru/product/elektrody-stal-mr-3s-2-mm-1-kg-18261583/

Ну а так – адово сварил раму, дичайше покрасил ее черным антигравием и перекинул туда все части компрессора:

kompressor-metal

Окрошка по-французски

Рассказали рецепт – режем оливье, заливаем шампанским.

Надо попробовать.

Randall Kanna

Пацаны и пацанессы, а что вы можете сказать о вот этой мадам?

https://randallkanna.com

Я пока что увидел на ее сайте только многочисленные советы из серии “Как пройти программистское собеседование” и “Как построить личный бренд” вместе с самолюбованием из серии “как я за полтора года стала senior engineer“.

Программисты накосячили – и лучше б не исправляли

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

Было:

https://www.youtube.com/watch?v=I6n9LNtRItc

Стало:

https://www.youtube.com./watch?v=I6n9LNtRItc

Про народную технику

Довольно забавно, что на всякого рода “интернет-форумах про технику” самые популярные и посещаемые темы посвящены откровенному говну, а большая часть их содержимого – “как сделать из этого говна подобие конфетки“. Счастливые обладатели нормальной техники просто пользуются ей – а решившие всех наебать нищеброды (”знаем-знаем, все делается в одном и том же китае!”) годами варятся в собственном соку, обсуждая “типичные проблемы” – еще один признак того, что производитель ложил известный орган на мнение покупателей.