a.gritsenkoandClaude Sonnet 5 8f2f1d8527
CI / test (push) Successful in 42s
release: fall back to PAT secret for package publish
Built-in job token (secrets.GITHUB_TOKEN) returns 401 against the
Package Registry API on this Gitea instance even with
permissions: packages: write, so use a manual PACKAGE_TOKEN secret
instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 20:13:27 +03:00
2026-09-20 20:04:18 +03:00
2026-09-20 20:04:18 +03:00
2026-09-20 20:04:18 +03:00
2026-09-20 20:04:18 +03:00
2026-09-20 20:04:18 +03:00
2026-09-20 20:04:18 +03:00

hotpcalc

Инструмент для работы с токенами SafeNet eToken PASS — HOTP (RFC 4226) и TOTP (RFC 6238). Умеет три вещи:

  1. Калькулятор. По вектору инициализации (seed) и двум последовательным значениям, снятым с токена, определяет его текущую позицию (счётчик для HOTP, временной шаг для TOTP) и рассчитывает следующие N значений.
  2. Программный токен. Хранит seed и счётчик в ini-файле и по команде выдаёт следующее значение — как физический брелок.
  3. Выпуск токенов. Генерирует новый токен и выгружает файл инициализации в XML-формате SafeNet/Aladdin для импорта на сервер аутентификации.

Написан на Go без внешних зависимостей; собирается в один статический бинарник под Linux, macOS и Windows (amd64 и arm64).

Сборка

make build          # локальный бинарник ./hotpcalc
make test           # тесты, включая контрольные векторы RFC 4226 и RFC 6238
make dist           # бинарники под linux/macos/windows, amd64/arm64 в каталоге dist/

Требуется Go 1.26 или новее. Готовые бинарники в dist/ ни от чего не зависят — достаточно скопировать нужный файл и сделать его исполняемым:

chmod +x hotpcalc-linux-amd64
./hotpcalc-linux-amd64 -version

На macOS файл, скачанный через браузер, помечается карантином; снять метку:

xattr -d com.apple.quarantine hotpcalc-darwin-arm64

В консоли Windows перед запуском может понадобиться переключение на UTF-8, иначе русский текст выведется нечитаемо (в Windows Terminal это не нужно):

chcp 65001
hotpcalc-windows-amd64.exe -seed 765D... -otp1 941595 -otp2 057938

Команды

hotpcalc [флаги] [seed] [код1] [код2]   калькулятор (режим по умолчанию)

hotpcalc new       выпустить новый токен: seed, серийник, запись в ini, выгрузка XML
hotpcalc add       добавить в ini уже известный токен по его seed
hotpcalc import    загрузить токены из XML-файла инициализации
hotpcalc list      список токенов в хранилище
hotpcalc show      параметры и состояние токена (счётчик не двигается)
hotpcalc next      выдать следующее значение — «нажатие кнопки»
hotpcalc token     интерактивный режим: Enter = нажатие кнопки
hotpcalc sync      найти позицию по двум кодам и записать её в хранилище
hotpcalc set       изменить параметры токена
hotpcalc export    выгрузить XML-файл инициализации
hotpcalc remove    удалить токен из хранилища
hotpcalc calc      явный вызов калькулятора

Справка по любой команде — hotpcalc <команда> -h.

Калькулятор

hotpcalc [флаги] [seed] [код1] [код2]

Самый короткий вызов — три позиционных аргумента:

./hotpcalc 765D7268D012090C28172A729BC9CC1AD82B382D 941595 057938
Seed:    765D7268…382D (20 байт, hex)
Режим:   HOTP, SHA1, 6 разрядов
Позиция: код 941595 → счётчик 41, код 057938 → счётчик 42

Следующие значения (10):

    #  Счётчик     Код
    1       43  937300
    2       44  991694
    3       45  146545
    ...

Без аргументов программа спрашивает seed и коды в диалоге:

./hotpcalc
Введите seed (hex или Base32): ...
Введите первый код с токена: ...
Введите второй (следующий) код: ...

Флаги

Флаг Назначение По умолчанию
-mode режим: hotp или totp hotp
-seed вектор инициализации —
-format формат seed: auto, hex, base32 auto
-otp1, -otp2 два последовательных значения с токена —
-n сколько следующих значений рассчитать 10
-digits разрядность кода (6 или 8) по длине введённых кодов
-alg sha1, sha256, sha512 sha1
-step шаг времени TOTP 30s
-start, -span диапазон перебора счётчика (HOTP) 0, 1000000
-window окно перебора вокруг текущего времени (TOTP); понимает d и w 90d
-counter считать прямо с указанного счётчика/шага, без поиска —
-at считать текущим моментом указанное время, RFC 3339 (TOTP) —
-all вывести результат для всех найденных совпадений выкл.
-json машиночитаемый вывод выкл.
-version показать версию —

Флаги можно ставить и после позиционных аргументов: ./hotpcalc <seed> <код1> <код2> -n 20.

Примеры

Двадцать следующих значений в режиме HOTP:

./hotpcalc -seed 765D7268D012090C28172A729BC9CC1AD82B382D -otp1 941595 -otp2 057938 -n 20

Восьмиразрядный токен (разрядность определяется автоматически по длине кодов):

./hotpcalc -seed 765D... -otp1 12345678 -otp2 87654321

TOTP с шагом 60 секунд и seed в Base32:

./hotpcalc -mode totp -step 60s -seed GEZDGNBVGY3TQOJQ -otp1 123456 -otp2 654321

Расчёт прямо с известного счётчика, без поиска:

./hotpcalc -seed 765D... -counter 100 -n 5

Токен с большим значением счётчика — расширяем диапазон перебора:

./hotpcalc -seed 765D... -otp1 941595 -otp2 057938 -span 10000000

Машиночитаемый вывод:

./hotpcalc -seed 765D... -otp1 941595 -otp2 057938 -n 3 -json

Программный токен

Хранилище — ini-файл, по умолчанию tokens.ini рядом с исполняемым файлом. Путь меняется флагом -store или переменной окружения HOTPCALC_STORE.

Выпуск токена: генерируется случайный seed (crypto/rand, 20 байт), серийный номер и файл инициализации для сервера.

$ ./hotpcalc new -label "Иванов И.И." -xml ivanov.xml
Выпущено токенов: 1

  0004003D473F7D  (Иванов И.И.)
    тип     HOTP, SHA1, 6 разрядов
    seed    281CCDCD…84F1 (20 байт)
    счётчик 0 (значение следующего нажатия)
    выпущен 2026-09-04 11:23:14

Хранилище: /opt/hotpcalc/tokens.ini
XML:       ivanov.xml

Дальше токен работает как брелок — каждое next это одно нажатие кнопки:

$ ./hotpcalc next 0004003D473F7D
521232

Код уходит в stdout, служебная строка (0004003D473F7D: счётчик 0 → 1) — в stderr, поэтому команду можно подставлять прямо в скрипты. Флаг -n выдаёт несколько значений за раз.

Интерактивный режим ближе к настоящему токену:

$ ./hotpcalc token 0004003D473F7D
Токен 0004003D473F7D (Иванов И.И.)
HOTP, SHA1, 6 разрядов

Enter — нажатие кнопки, q — выход.

> 448181   (счётчик 0)
> 257719   (счётчик 1)
> q

Остальные команды:

./hotpcalc list                                   # что вообще есть в хранилище
./hotpcalc show 0004003D473F7D -peek 5            # состояние и будущие коды, счётчик не двигая
./hotpcalc sync 0004003D473F7D 394216 767221      # ресинхронизация по двум кодам
./hotpcalc set 0004003D473F7D -counter 100        # правка вручную
./hotpcalc add -label "Старый токен" 765D7268...  # завести токен по известному seed
./hotpcalc remove 0004003D473F7D                  # удалить

Состояние сохраняется до вывода кода, а хранилище на время работы блокируется файлом tokens.ini.lock, поэтому параллельные вызовы не могут получить один и тот же счётчик. Замок старше минуты считается брошенным и снимается автоматически.

Формат хранилища

; hotpcalc — хранилище токенов
; Файл содержит секреты в открытом виде: защищайте его как пароль.
; Одна секция — один токен, имя секции — серийный номер.

[0004003D473F7D]
type      = hotp
seed      = 281CCDCD4E26ACFD57A09450358FF257FEA484F1
digits    = 6
algorithm = sha1
counter   = 0
label     = Иванов И.И.
created   = 2026-09-04T11:23:14+03:00
last_used = 2026-09-04T11:23:24+03:00

Файл можно править вручную: комментарии, порядок секций и порядок ключей сохраняются при перезаписи. Для TOTP вместо counter пишутся period и last_step.

Файл инициализации (XML)

new -xml, add -xml и export выгружают токены в родном формате SafeNet/Aladdin — том самом, что поставляется с закупленными eToken PASS. Его импортируют SafeNet Authentication Manager, privacyIDEA, LinOTP и совместимые серверы.

<?xml version="1.0" encoding="UTF-8"?>
<Tokens>
  <Token serial="0004003D473F7D">
    <CaseModel>5</CaseModel>
    <Model>101</Model>
    <ProductionDate>09/04/2026</ProductionDate>
    <ProductName>eToken PASS</ProductName>
    <Applications>
      <Application ConnectorID="{ab1397d2-ddb6-4705-b66e-9f83f322deb9}">
        <Seed>281CCDCD4E26ACFD57A09450358FF257FEA484F1</Seed>
        <MovingFactor>0</MovingFactor>
      </Application>
    </Applications>
  </Token>
</Tokens>

Заводские поля меняются флагами -connector-id, -product-name, -case-model, -model, -production-date.

Ограничение формата. В нём нет полей для разрядности, режима и периода: он описывает шестиразрядный HOTP, а алгоритм импортирующая сторона выводит из длины seed (64 hex-символа — SHA256, иначе SHA1). Если выгружается токен с другими параметрами, программа предупреждает об этом явно:

Внимание: формат не хранит разрядность — сервер импортирует токен как 6-разрядный, а не 8-разрядный

Выпуск партии токенов одним файлом:

./hotpcalc new -count 50 -label "Отдел продаж" -xml batch.xml
./hotpcalc export -all -o all.xml        # выгрузить всё хранилище
./hotpcalc import postavka.xml           # загрузить закупленные токены

Форматы seed

Формат определяется автоматически:

  • hex — как на бланке eToken PASS: 40 шестнадцатеричных символов (20 байт). Пробелы, дефисы и двоеточия игнорируются;
  • Base32 — как в Google Authenticator, регистр и padding = не важны;
  • otpauth:// URI — из QR-кода. Параметры digits, algorithm, period и counter берутся из URI, если не заданы флагами явно.

Как работает поиск позиции

Токен eToken PASS в режиме HOTP хранит счётчик, который увеличивается при каждом нажатии кнопки. Два подряд снятых значения однозначно задают позицию: программа перебирает счётчики в диапазоне [-start, -start + -span) и ищет тот, для которого первый код приходится на счётчик C, а второй — на C+1.

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

Если совпадений найдено несколько (вероятность порядка span / 10^12 — практически исключено), программа предупреждает об этом; полный список выводится с флагом -all. Если совпадений нет, стоит проверить seed, разрядность, режим и порядок кодов, а затем расширить диапазон перебора.

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

Реализация проходит контрольные векторы:

  • RFC 4226, Приложение D — все десять значений HOTP;
  • RFC 6238, Приложение B — TOTP для SHA1, SHA256 и SHA512.

Отдельно проверяется, что программный токен и калькулятор дают одно и то же: коды из next сверяются с расчётом по seed из выгруженного XML, а параллельные нажатия не выдают повторов.

make test

Сверка с внешним инструментом:

oathtool --hotp -c 0 3132333435363738393031323334353637383930   # 755224
./hotpcalc -seed 3132333435363738393031323334353637383930 -counter 0 -n 1

Безопасность

Seed — это секрет, эквивалентный самому токену. Программа никуда его не передаёт: сетевых обращений нет. По умолчанию seed печатается сокращённо (765D7268…382D), полностью — только по флагу -show-seed и в выводе -json.

Ini-хранилище и XML-файлы создаются с правами 0600 (на Windows биты прав не действуют — полагайтесь на права каталога). Оба содержат секреты открытым текстом:

  • удаляйте XML-файл инициализации после импорта на сервер — об этом программа предупреждает сама;
  • относитесь к tokens.ini как к файлу паролей;
  • шифрование хранилища не реализовано: если это важно, держите файл на зашифрованном разделе.

Seed, переданный флагом, попадает в историю командной строки и в список процессов — в чувствительных случаях вводите его в диалоге.

S
Description
No description provided
Readme
94 KiB
Languages
Go 99.4%
Makefile 0.6%