Files
hotp_calc/README.md
T

354 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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).
## Сборка
```sh
make build # локальный бинарник ./hotpcalc
make test # тесты, включая контрольные векторы RFC 4226 и RFC 6238
make dist # бинарники под linux/macos/windows, amd64/arm64 в каталоге dist/
```
Требуется Go 1.26 или новее. Готовые бинарники в `dist/` ни от чего не зависят —
достаточно скопировать нужный файл и сделать его исполняемым:
```sh
chmod +x hotpcalc-linux-amd64
./hotpcalc-linux-amd64 -version
```
На macOS файл, скачанный через браузер, помечается карантином; снять метку:
```sh
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]
```
Самый короткий вызов — три позиционных аргумента:
```sh
./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 и коды в диалоге:
```sh
./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:
```sh
./hotpcalc -seed 765D7268D012090C28172A729BC9CC1AD82B382D -otp1 941595 -otp2 057938 -n 20
```
Восьмиразрядный токен (разрядность определяется автоматически по длине кодов):
```sh
./hotpcalc -seed 765D... -otp1 12345678 -otp2 87654321
```
TOTP с шагом 60 секунд и seed в Base32:
```sh
./hotpcalc -mode totp -step 60s -seed GEZDGNBVGY3TQOJQ -otp1 123456 -otp2 654321
```
Расчёт прямо с известного счётчика, без поиска:
```sh
./hotpcalc -seed 765D... -counter 100 -n 5
```
Токен с большим значением счётчика — расширяем диапазон перебора:
```sh
./hotpcalc -seed 765D... -otp1 941595 -otp2 057938 -span 10000000
```
Машиночитаемый вывод:
```sh
./hotpcalc -seed 765D... -otp1 941595 -otp2 057938 -n 3 -json
```
## Программный токен
Хранилище — ini-файл, по умолчанию `tokens.ini` рядом с исполняемым файлом.
Путь меняется флагом `-store` или переменной окружения `HOTPCALC_STORE`.
Выпуск токена: генерируется случайный seed (`crypto/rand`, 20 байт), серийный номер
и файл инициализации для сервера.
```sh
$ ./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` это одно нажатие кнопки:
```sh
$ ./hotpcalc next 0004003D473F7D
521232
```
Код уходит в stdout, служебная строка (`0004003D473F7D: счётчик 0 → 1`) — в stderr,
поэтому команду можно подставлять прямо в скрипты. Флаг `-n` выдаёт несколько
значений за раз.
Интерактивный режим ближе к настоящему токену:
```sh
$ ./hotpcalc token 0004003D473F7D
Токен 0004003D473F7D (Иванов И.И.)
HOTP, SHA1, 6 разрядов
Enter — нажатие кнопки, q — выход.
> 448181 (счётчик 0)
> 257719 (счётчик 1)
> q
```
Остальные команды:
```sh
./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`, поэтому параллельные вызовы не могут получить один и тот же
счётчик. Замок старше минуты считается брошенным и снимается автоматически.
### Формат хранилища
```ini
; 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
<?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-разрядный
```
Выпуск партии токенов одним файлом:
```sh
./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, а параллельные
нажатия не выдают повторов.
```sh
make test
```
Сверка с внешним инструментом:
```sh
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, переданный флагом, попадает в историю командной строки и в список процессов —
в чувствительных случаях вводите его в диалоге.