Ответ 1
Я хотел установить щедрость по моему вопросу, но мне удалось создать решение. Моя проблема, похоже, связана с неправильным значением клавиши secret
(это должен быть правильный параметр для функции base64.b32decode()
).
Ниже я публикую полное рабочее решение с объяснением того, как его использовать.
код
Достаточно следующего кода. Я также загрузил его в GitHub как отдельный модуль с именем ontimepass (здесь: 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
- секретное значение, известное серверу (выше script) и клиенту (Google Authenticator, предоставляя его как пароль в приложении), -
intervals_no
- это число, увеличивающееся после каждого поколения токена (это, вероятно, должно быть разрешено на сервере путем проверки некоторого конечного числа целых чисел после последнего успешного завершения проверки в прошлом)
Как использовать его
- Создать
secret
(это должен быть правильный параметр дляbase64.b32decode()
) - желательно 16- char (нет=
знаков), так как он, безусловно, работал как для script, так и для Google Authenticator. - Используйте
get_hotp_token()
, если вы хотите, чтобы одноразовые пароли были недействительными после каждого использования. В Google Authenticator этот тип паролей, о которых я упоминал, основывается на счетчике. Для проверки на сервере вам нужно будет проверить несколько значенийintervals_no
(так как у вас нет гарантии того, что пользователь не генерировал пропуск между запросами по какой-либо причине), но не менее последнего рабочего значенияintervals_no
( таким образом, вы должны, вероятно, хранить его где-нибудь). - Используйте
get_totp_token()
, если вы хотите, чтобы токен работал с интервалом в 30 секунд. Вы должны убедиться, что обе системы имеют правильное время (это означает, что они оба генерируют одну и ту же временную метку Unix в любой момент времени). - Обязательно защитите себя от атаки грубой силы. Если используется пароль, основанный на времени, то попытка 1000000 значений менее чем за 30 секунд дает 100% вероятность угадать пароль. В случае пассивных пакетов HMAC (HOTPs) это выглядит еще хуже.
Пример
При использовании следующего кода для одноразового пароля на основе 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 символов).