Реализация Google Authenticator в Python

Я пытаюсь использовать одноразовые пароли, которые можно сгенерировать с помощью Приложение Google Authenticator .

Что делает Google Authenticator

По сути, Google Authenticator реализует два типа паролей:

  • HOTP - одноразовый пароль на основе HMAC, что означает, что пароль меняется при каждом вызове в соответствии с RFC4226 и
  • TOTP . Одноразовый пароль на основе времени, который меняется каждые 30 секунд (насколько я знаю).

Google Authenticator также доступен как открытый источник здесь: code.google.com/p/google. -authenticator

Текущий код

Я искал существующие решения для генерации паролей HOTP и TOTP, но не нашел много. У меня есть следующий фрагмент кода, который отвечает за генерацию HOTP:

import hmac, base64, struct, hashlib, time

def get_token(secret, digest_mode=hashlib.sha1, intervals_no=None):
    if intervals_no == None:
        intervals_no = int(time.time()) // 30
    key = base64.b32decode(secret)
    msg = struct.pack(">Q", intervals_no)
    h = hmac.new(key, msg, digest_mode).digest()
    o = ord(h[19]) & 15
    h = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000
    return h

Проблема, с которой я сталкиваюсь, заключается в том, что пароль, который я генерирую с помощью приведенного выше кода, не совпадает с паролем, созданным с помощью приложения Google Authenticator для Android. Несмотря на то, что я пробовал несколько значений intervals_no (ровно первые 10000, начиная с intervals_no = 0), где secret равно ключу, предоставленному в приложении GA.

У меня есть вопросы

Мои вопросы:

  1. Что я делаю не так?
  2. Как я могу генерировать HOTP и /или TOTP в Python?
  3. Существуют ли какие-либо библиотеки Python для этого?

Подводя итог: пожалуйста, дайте мне любые подсказки, которые помогут мне реализовать аутентификацию Google Authenticator в моем коде Python.

94 голоса | спросил Tadeck 16 FriEurope/Moscow2011-12-16T06:57:13+04:00Europe/Moscow12bEurope/MoscowFri, 16 Dec 2011 06:57:13 +0400 2011, 06:57:13

2 ответа


0

Я хотел назначить награду за мой вопрос, но мне удалось создать решение. Кажется, моя проблема связана с неверным значением ключа secret (это должен быть правильный параметр для base64.b32decode()).

Ниже я публикую полное рабочее решение с объяснением того, как его использовать.

код

Следующий код достаточно. Я также загрузил его на GitHub в виде отдельного модуля onetimepass (доступно здесь: https://github.com/tadeck /onetimepass ).

 import hmac, base64, struct, hashlib, time

def get_hotp_token(secret, intervals_no):
    key = base64.b32decode(secret, True)
    msg = struct.pack(">Q", intervals_no)
    h = hmac.new(key, msg, hashlib.sha1).digest()
    o = ord(h[19]) & 15
    h = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000
    return h

def get_totp_token(secret):
    return get_hotp_token(secret, intervals_no=int(time.time())//30)

Он имеет две функции:

  • get_hotp_token() генерирует одноразовый токен (который должен стать недействительным после однократного использования),
  • get_totp_token() генерирует токен на основе времени (изменяется с интервалом в 30 секунд),

Параметры

Когда дело доходит до параметров:

  • secret - это секретное значение, известное серверу (приведенный выше скрипт) и клиенту (Google Authenticator, предоставляя его в качестве пароля в приложении),
  • intervals_no - это число, увеличиваемое после каждого поколения токена (это, вероятно, следует решить на сервере, проверив некоторое конечное число целых чисел после последнего успешный проверенный в прошлом)

Как его использовать

  1. Создать secret (это должен быть правильный параметр для base64.b32decode()) - желательно 16-символьный (без знаков =), так как он наверняка работал как для сценария, так и для Google Authenticator.
  2. Используйте get_hotp_token(), если хотите, чтобы одноразовые пароли были недействительными после каждого использования. В Google Authenticator этот тип паролей я упоминал как основанный на счетчике. Для проверки на сервере вам необходимо проверить несколько значений intervals_no (поскольку у вас нет гарантии, что пользователь не сгенерировал передачу между запросов по какой-то причине), но не меньше, чем последнее рабочее значение intervals_no (таким образом, вы, вероятно, должны его где-то хранить).
  3. Используйте get_totp_token(), если хотите, чтобы токен работал с 30-секундными интервалами. Вы должны убедиться, что в обеих системах установлено правильное время (это означает, что обе они генерируют одну и ту же метку времени Unix в любой данный момент времени).
  4. Обязательно защитите себя от атаки методом перебора. Если используется основанный на времени пароль, то попытка 1000000 значений менее чем за 30 секунд дает 100% шанс угадать пароль. В случае с HMAC passowrds (HOTP) это выглядит еще хуже.

Пример

При использовании следующего кода для одноразового пароля на основе HMAC:

 secret = 'MZXW633PN5XW6MZX'
for i in xrange(1, 10):
    print i, get_hotp_token(secret, intervals_no=i)

вы получите следующий результат:

1 448400
2 656122
3 457125
4 35022
5 401553
6 581333
7 16329
8 529359
9 171710

, что соответствует токенам, сгенерированным приложением Google Authenticator (кроме случаев, когда оно короче 6 знаков, приложение добавляет нули в начало и достигает длины 6 символов).

ответил Tadeck 18 SunEurope/Moscow2011-12-18T08:47:18+04:00Europe/Moscow12bEurope/MoscowSun, 18 Dec 2011 08:47:18 +0400 2011, 08:47:18
0

Я хотел, чтобы скрипт Python генерировал пароль TOTP. Итак, я написал скрипт на Python. Это моя реализация. У меня есть эта информация о Википедии и некоторые знания о HOTP и TOTP для написания этот скрипт.

import hmac, base64, struct, hashlib, time, array

def Truncate(hmac_sha1):
    """
    Truncate represents the function that converts an HMAC-SHA-1
    value into an HOTP value as defined in Section 5.3.

    http://tools.ietf.org/html/rfc4226#section-5.3

    """
    offset = int(hmac_sha1[-1], 16)
    binary = int(hmac_sha1[(offset * 2):((offset * 2) + 8)], 16) & 0x7fffffff
    return str(binary)

def _long_to_byte_array(long_num):
    """
    helper function to convert a long number into a byte array
    """
    byte_array = array.array('B')
    for i in reversed(range(0, 8)):
        byte_array.insert(0, long_num & 0xff)
        long_num >>= 8
    return byte_array

def HOTP(K, C, digits=6):
    """
    HOTP accepts key K and counter C
    optional digits parameter can control the response length

    returns the OATH integer code with {digits} length
    """
    C_bytes = _long_to_byte_array(C)
    hmac_sha1 = hmac.new(key=K, msg=C_bytes, digestmod=hashlib.sha1).hexdigest()
    return Truncate(hmac_sha1)[-digits:]

def TOTP(K, digits=6, window=30):
    """
    TOTP is a time-based variant of HOTP.
    It accepts only key K, since the counter is derived from the current time
    optional digits parameter can control the response length
    optional window parameter controls the time window in seconds

    returns the OATH integer code with {digits} length
    """
    C = long(time.time() / window)
    return HOTP(K, C, digits=digits)
ответил Anish Shah 22 PMpTue, 22 Apr 2014 17:52:48 +040052Tuesday 2014, 17:52:48

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132