Как использовать base32 в сочетании с hotp (одноразовые пароли) в python? - PullRequest
1 голос
/ 07 июня 2019

для университетского упражнения Я хочу разработать простую систему клиент-сервер hotp на python. В этом случае клиент отправляет на сервер пароль и одноразовый пароль. Сервер знает секрет, вычисляет текущее hotp и сравнивает полученные значения. Все идет нормально. С открытым текстом это прекрасно работает, и рассчитанные значения такие же, какие я получаю, когда использую iOS-приложение «OTP Auth». Но есть также возможность рассчитать OTP в сочетании с base32. Поэтому я добавил несколько строк для кодирования открытого текста в base32, но теперь вывод неправильный.

Давайте предположим, что мы используем секретный "1234", поэтому вывод в виде открытого текста будет "110366". Это работает Но если я кодирую секрет в base32, вывод должен быть "807244", но моя программа вычисляет "896513". Кто-нибудь знает, почему это происходит?

Я уже пытался использовать разные секреты и проверял это в разных приложениях. Всегда один и тот же результат.

import hmac
import hashlib
import array
import base64

counter = 0
digits = 6                      #Anzahl der Zeichen

def hotp(secret, c):
    global digits
    counter = extendCounter(c)
    hmac_sha1 = hmac.new(secret, counter, hashlib.sha1).hexdigest()
    return truncate(hmac_sha1)[-digits:]


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


def extendCounter(long_num):
    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 main():
    secret = "1234"
    bSecret = secret.encode("UTF-8")
    bSecret = base64.b32encode(bSecret)
    otp = hotp(bSecret, counter)
    one_time_password = otp

Я ожидаю 807244 в качестве выхода, но выход 896513

Ответы [ 2 ]

1 голос
/ 08 июня 2019

Во-первых, важно отметить, что результат secret.encode('UTF-8') имеет точно такой же тип, как и результат base64.b32encode(bSecret) (и в этом отношении base64.b64encode(bSecret)) - все они возвращают bytes объекты. Также стоит отметить, что реализация hmac в Python не упоминает о кодировке base64 / base32. Таким образом, краткий ответ заключается в том, что ожидаемый результат 807244 действителен только в том случае, если общий секретный код является двоичным объектом в кодировке base64 / UTF-8.

Этот быстрый фрагмент показывает, что вы действительно можете дать любые байты, которые вам нравятся, hotp, и он даст некоторый результат (потому что hotp вызывается несколько раз в примере, counter изменяется)

# ... everything from your example above ...
secret = "1234"
secret_bytes = secret.encode("UTF-8")
secret_bytes
>>> b'1234'
b32_secret = base64.b32encode(bSecret)
b32_secret
>>> b'GEZDGNA='
b64_secret = base64.b64encode(bSecret)
b64_secret
>>> b'MTIzNA=='
hotp(secret_bytes, counter)  # just a UTF-8 blob works
>>> '110366'
hotp(b32_secret, counter)  # base32/UTF-8 also works
>>> '896513'
hotp(b64_secret, counter)  # base64/UTF-8 works as well
>>> '806744'

Если у вас есть более подробная информация о том, почему вы ожидали 807244 для BLOB-объекта base32 / UTF8, я с удовольствием исправлю этот ответ.

0 голосов
/ 10 июня 2019

Нашли ошибку: Вместо перевода секрета в base32 секрет должен быть декодированным значением Base32. Кроме того, вместо того, чтобы кодировать это значение, оно должно быть декодировано («base64.b32decode (bytes (save_secret, 'utf-8'))»)

Итак, правильное основное выглядит так:

def main():
    secret = "V6X27L5P" #Base32 value
    secret = base64.b32decode(bytes(secret, 'utf-8'))
    one_time_password = hotp(secret, counter)
...