До Python 2.5 хэши и дайджесты были реализованы в отдельных модулях (например, [Python 2.Docs]: md5 — алгоритм дайджеста сообщений MD5).
Начиная с v2.5, [Python 2.6. Docs]: hashlib — добавлены безопасные хэши и дайджесты сообщений. Его целью было:
- Предложите унифицированный метод доступа к хэшам/дайджестам (через их имя)
- Переключитесь (по умолчанию) на внешнего поставщика криптографии (кажется логичным шагом делегировать полномочия какой-либо организации, специализирующейся в этой области, поскольку поддержка всех этих алгоритмов может оказаться излишним). В то время OpenSSL был лучшим выбором: достаточно зрелый, известный и совместимый (была куча похожих провайдеров Java, но они были довольно бесполезны).
В качестве побочного эффекта #2. реализации Python были скрыты от общедоступного API (их переименовали: _md5, _sha1, _sha256, _sha512, а последние добавлены: _blake2 , _sha3), так как избыточность часто приводит к путанице.
Но другим побочным эффектом была зависимость _hashlib.so от OpenSSL libcrypto*.so ( это специфично для Nix (как минимум Lnx), в Win статическая libeay32.lib была связана в < em>_hashlib.pyd, а также _ssl.pyd (который я считаю хромым), до v3.7+, где OpenSSL .dll являются частью установки Python).
Вероятно, на 90+% машин все было гладко, так как OpenSSL был/установлен по умолчанию, но на тех, где это не так, многие вещи могут ломаются, потому что, например, hashlib импортируется многими модулями (один из таких примеров — случайный, который сам импортируется множеством других), поэтому тривиальные фрагменты кода, которые вообще не связаны с криптографией (по крайней мере, на первыйst взгляд) перестанут работать. Вот почему старые реализации сохраняются (но опять же, они являются лишь запасными вариантами, поскольку версии OpenSSL поддерживаются/должны поддерживаться лучше).
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q059955854]> ~/sopr.sh
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***
[064bit-prompt]> python3 -c "import sys, hashlib as hl, _md5, ssl;print(\"{0:}\n{1:}\n{2:}\n{3:}\".format(sys.version, _md5, hl._hashlib, ssl.OPENSSL_VERSION))"
3.5.2 (default, Oct 8 2019, 13:06:37)
[GCC 5.4.0 20160609]
<module '_md5' (built-in)>
<module '_hashlib' from '/usr/lib/python3.5/lib-dynload/_hashlib.cpython-35m-x86_64-linux-gnu.so'>
OpenSSL 1.0.2g 1 Mar 2016
[064bit-prompt]>
[064bit-prompt]> ldd /usr/lib/python3.5/lib-dynload/_hashlib.cpython-35m-x86_64-linux-gnu.so
linux-vdso.so.1 => (0x00007fffa7d0b000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f50d9e4d000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f50d9a83000)
libcrypto.so.1.0.0 => /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 (0x00007f50d963e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f50da271000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f50d943a000)
[064bit-prompt]>
[064bit-prompt]> openssl version -a
OpenSSL 1.0.2g 1 Mar 2016
built on: reproducible build, date unspecified
platform: debian-amd64
options: bn(64,64) rc4(16x,int) des(idx,cisc,16,int) blowfish(idx)
compiler: cc -I. -I.. -I../include -fPIC -DOPENSSL_PIC -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -m64 -DL_ENDIAN -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -Wl,-Bsymbolic-functions -Wl,-z,relro -Wa,--noexecstack -Wall -DMD32_REG_T=int -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DMD5_ASM -DAES_ASM -DVPAES_ASM -DBSAES_ASM -DWHIRLPOOL_ASM -DGHASH_ASM -DECP_NISTZ256_ASM
OPENSSLDIR: "/usr/lib/ssl"
[064bit-prompt]>
[064bit-prompt]> python3 -c "import _md5, hashlib as hl;print(_md5.md5(b\"A\").hexdigest(), hl.md5(b\"A\").hexdigest())"
7fc56270e7a70fa81a5935b72eacbe29 7fc56270e7a70fa81a5935b72eacbe29
Согласно [Python 3.Docs]: hashlib.algorithms_guaranteed< /сильный>а>:
Набор, содержащий имена алгоритмов хеширования, которые гарантированно поддерживаются этим модулем на всех платформах. Обратите внимание, что «md5» есть в этом списке, несмотря на то, что некоторые поставщики исходных продуктов предлагают странную «совместимую с FIPS» сборку Python, которая исключает его.
Ниже приведен пример пользовательской установки Python 2.7 (которую я создал довольно давно, стоит отметить, что она динамически связывается с OpenSSL .dll):
e:\Work\Dev\StackOverflow\q059955854>sopr.bat
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***
[prompt]> "F:\Install\pc064\HPE\OPSWpython\2.7.10__00\python.exe" -c "import sys, ssl;print(\"{0:}\n{1:}\".format(sys.version, ssl.OPENSSL_VERSION))"
2.7.10 (default, Mar 8 2016, 15:02:46) [MSC v.1600 64 bit (AMD64)]
OpenSSL 1.0.2j-fips 26 Sep 2016
[prompt]> "F:\Install\pc064\HPE\OPSWpython\2.7.10__00\python.exe" -c "import hashlib as hl;print(hl.md5(\"A\").hexdigest())"
7fc56270e7a70fa81a5935b72eacbe29
[prompt]> "F:\Install\pc064\HPE\OPSWpython\2.7.10__00\python.exe" -c "import ssl;ssl.FIPS_mode_set(True);import hashlib as hl;print(hl.md5(\"A\").hexdigest())"
Traceback (most recent call last):
File "<string>", line 1, in <module>
ValueError: error:060A80A3:digital envelope routines:FIPS_DIGESTINIT:disabled for fips
Что касается вопроса скорости, я могу только предполагать:
- Реализация Python была (очевидно) написана специально для Python, что означает, что она "более оптимизирована" (да, это грамматически неправильно) для Python, чем универсальная версия, а также находится в python*.so (или в самом исполняемом файле python)
- Реализация OpenSSL находится в libcrypto*.so, и к ней обращается оболочка _hashlib.so, которая выполняет обратное и обратное преобразование между < em>Python (PyObject*) и OpenSSL (EVP_MD_CTX*)
Принимая во внимание вышеизложенное, логично предположить, что первый вариант (немного) быстрее (по крайней мере, для небольших сообщений, где накладные расходы (вызов функции и другие базовые операции Python) занимают значительный процент общего времени. по сравнению с самим хешированием). Также необходимо учитывать и другие факторы (например, использовались ли ускорения ассемблера OpenSSL).
Обновить #0
Ниже приведены некоторые мои собственные тесты.
code00.py:
#!/usr/bin/env python
import sys
from hashlib import md5 as md5_openssl
from _md5 import md5 as md5_builtin
import timeit
def main(*argv):
base_text = b"A"
number = 1000000
print("timeit attempts number: {0:d}".format(number))
#x = []
#y = {}
for count in range(0, 16):
factor = 2 ** count
text = base_text * factor
globals_dict = {"text": text}
#x.append(factor)
print("\nUsing a {0:8d} (2 ** {1:2d}) bytes message".format(len(text), count))
for func in [
md5_openssl,
md5_builtin,
]:
globals_dict["md5"] = func
t = timeit.timeit(stmt="md5(text)", globals=globals_dict, number=number)
print(" {0:12s} took: {1:11.6f} seconds".format(func.__name__, t))
#y.setdefault(func.__name__, []).append(t)
#print(x, y)
if __name__ == "__main__":
print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
main(*sys.argv[1:])
print("\nDone.")
Вывод:
Результат, кажется, сильно отличается от вашего. В моем случае:
- Начиная где-то с сообщений размером [~512B .. ~1KiB] реализация OpenSSL работает лучше, чем встроенная
- Я знаю, что результатов слишком мало, чтобы претендовать на закономерность, но похоже, что обе реализации линейно пропорциональны (с точки зрения времени) размеру сообщения (но встроенный наклон кажется немного круче, что означает, что он будет работать хуже). в долгосрочной перспективе)
В заключение, если все ваши сообщения небольшие, а встроенная реализация работает лучше всего для вас, то используйте ее.
Обновление #1
Графическое представление (мне пришлось уменьшить количество итераций timeit на порядок, так как для больших сообщений это занимало бы слишком много времени):
а>
и масштабирование области, где пересекаются два графика:
а>
person
CristiFati
schedule
17.02.2020