Приложения для двухфакторной аутентификации, такие как Google Authenticator, периодически генерируют одноразовые коды, которые нужно вводить вместе с паролем для дополнительной защиты аккаунта. Такая схема называется TOTP (Time-based One-Time Password Algorithm).

image
Окно приложения Google Authenticator

Протокол TOTP:

  1. Клиент получает уникальный секрет для своего аккаунта
  2. На основе секрета и текущего времени приложение периодически генерирует коды
  3. Клиент вводит код для входа в аккаунт

image

Получение секрета

Обычно для получения секрета нужно отсканировать QR-код. Он содержит строку вида
otpauth://totp/Label?Parameters

Метка

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

Параметры

Parameters – параметры аутентификации:

Secret
Секрет. Значение закодировано в Base32 без паддинга. Кодировка Base32 содержит только печатные символы: латинские буквы в верхнем регистре и цифры. Благодаря этому секрет можно ввести вручную, если сканирование QR-кода недоступно.

Issuer
Содержит название сервиса, к которому относится аккаунт. Название должно совпадать с названием сервиса в Label. По стандарту рекомендуется указывать название сервиса и в Issuer и в Label, хотя и получается некоторая избыточность.

Algorithm
Определяет хеш-функцию, которая используется при генерации кодов. По умолчанию используется SHA-1.

Digits
Устанавливает количество цифр в одноразовом коде. По умолчанию 6.

Period
Период в секундах, в течение которого действителен код. По умолчанию 30 секунд. Используется отрезок времени, а не точное значение секунда в секунду по двум причинам:

  • клиенту нужно некоторое нужно время, чтобы скопировать и ввести код
  • время на сервере сервиса и на смартфоне клиента может немного отличаться

Пример:

otpauth://totp/Blog:seregablog?secret=ONSXEZLHMFRGY33HGQZDIMQK&issuer=Blog

Blog – название сервиса
seregablog – имя аккаунта для входа в сервис
ONSXEZLHMFRGY33HGQZDIMQK – секрет

Алгоритм генерации кодов

Коды генерируется на основе секрета и текущего времени с помощью схемы HMAC.


# initial_secret - полученный секрет
# period - период жизни кода
# digits - длина кода

def generateTotp(initial_secret, period, digits):
    
    # декодируем секрет Base32
    secret = BASE32_DECODE(secret)  

    # получаем текущее время и рассчитываем период жизни кода
    current_time = CURRENT_UNIX_TIME() / period  

    # Вычисляем HMAC. Используемая хеш-функция выбирается при начальной настройке   
    hash = HMAC(secret, current_time)  

    # получаем значение последних 4 бит хеша виде целого числа
    # это значение используется как индекс в массиве байтов хеша
    start_index = INT(LAST_FOUR_BIT(hash))

    # Получаем 4 байта, начиная с байта с номером start_index 
    four_bytes = GET_FOUR_BYTES(hash, start_index)

    # преобразуем 4 байта в целое число
    integer_four_bytes = INT(four_bytes)

    # значение всё ещё может быть слишком длинным - обрезаем до нужной длины
    code = integer_four_bytes % 10**(digits)

    # возвращаем результат
    return code
📢тг-канал