PGP ключи в виде QR-кодов

Опубликовано 18 Июня, 2023 под тегами PGP, GPG, QR

Если хочется сделать hard copy ключа PGP/GPG (что в общем случае может быть хорошей идеей, не забываем про мантру делайбэкапыкаждыйдень), но перспектива при необходимости вбивать посимвольно или возиться с OCR не воодушевляет, на помощь приходят неожиданно QR-коды.

Генерировать QR можно, например, небольшой консольной утилитой qrencode. Прочитать QR кроме телефона можно например утилитой zbar. Это не очень интересно. Интереснее оказывается вопрос как закодировать ключ в QR наиболее эффективным образом.

В принципе, для эллиптических ключей, просто экспорт через gpg --armor даст умеренно сносные результаты, i.e. результат уместится в один QR-код. А ключи RSA вменяемой длины (2048-4096), как их ни сжимай, на один QR-код не влезут.

Тем не менее, эксперименты показали, что двоичное представление ключа (т.е. без --armor), ужатое либо lzma либо zstd и преобразованное в base32 даёт наилучшие результаты. Base32 потому что QR-коды наиболее оптимально кодируют латинские буквы без учёта регистра и цифры, поэтому base64 оказывается в итоге дороже, несмотря на то что сам текст короче.

Не буду больше тянуть, однострочник такой:

gpg --export-secret-keys <keyid> | lzma | base32 | qrencode -o- -lH -tEPS | lpr

Можно указать несколько ключей, если понизить избыточность (параметр -lL вместо -lH), можно уместить по крайней мере 5 эллиптических ключей на один QR. Но мне несколько претит понижать избыточность без крайней нужды, бумага – не слишком надёжный носитель.

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

gpg --export-secret-keys <keyid> | lzma | base32 | qrencode -ooutput.eps -lH -tEPS -S -v40

Эта команда создаст несколько файлов вида output-nn.eps, где nn это номер.

Ключ -S включает structured QR, -v40 устанавливает максимальный размер для каждого индивидуального QR-кода. С высокой степенью избыточности (-lH) можно уместить 4096-битный ключ на 7 QR-кодах (каждый размером с лист A4), а с низкой (-lL) – “всего” на 3-х.

Нюанс в том, что structured QR не так-то просто прочитать обратно. Мне это удалось только отсканировав распечатки, склеив их в один файл в правильном порядке и скормив это в zbarimg. Отдельно отмечу, что фотографии на камеру телефона оказались недостаточно чёткими, чтобы их потом прочитать (по крайней мере с -lL).

Альтернативные варианты для больших ключей:

  • Кодировать в формат datamatrix вместо QR, например используя dmtx-utils
  • Использовать paperkey чтобы удалить “несекретные” части секретного ключа. Публичный ключ тогда должен быть доступен по каким-то другим каналам, например загружен на серверы ключей и т.п.

Более подробно эти альтернативы рассмотрены, например здесь

Ну и в заключение, я эту технику применил чтобы сделать hard copy, но в общем случае можно аналогичный подход использовать для обмена публичными ключами офлайн – эллиптические публичные ключи тоже достаточно короткие. На правах proof of concept:

QR-код c публичным ключом PGP, gpg --export --armor
QR-код c публичным ключом PGP, сжатый и закодированный в base32, gpg --export | lzma | base32