Til Python 2. 5 , хеши и дайджесты были реализованы в своих собственных модулях (например, [Python 2.Docs]: md5 - MD5 алгоритм дайджеста сообщений ).
Начиная с v2.5 , [Python 2.6.Docs]: hashlib - добавлены защищенные хэши и дайджесты сообщений . Его целью было:
- Предложить единый метод доступа к хешам / дайджестам (через их имя)
- Переключить ( по умолчанию ) на внешнюю криптографию провайдера (кажется логичным шагом делегировать какую-то сущность, специализирующуюся в этой области, так как поддержание всех этих алгоритмов может быть излишним). В то время OpenSSL был лучшим выбором: достаточно зрелым, известным и совместимым (было множество похожих Java провайдеров, но они были довольно бесполезны)
В качестве побочного эффекта # 2. реализации Python были скрыты от публикуемого c API (переименовал их: _md5 , _sha1 , _sha256 , _sha512 , и добавлены последние: _blake2 _sha3 ), поскольку избыточность часто создает путаницу.
Но еще одним побочным эффектом была _hashlib.so зависимость от OpenSSL libcrypto * .so (это Nix (по крайней мере Lnx ), специфицирующий c, Win , статус c libeay32.lib был связан в _hashlib.pyd , а также _ssl.pyd (что я считаю хромым), до v3. 7 + , где OpenSSL .dll s являются частью установки Python).
Вероятно, на 90 +% машин все было гладко, так как OpenSSL был / установлен по умолчанию, но для тех, где это не так, многие вещи могут сломаться потому что, например, hashlib импортируется многими модулями (одним из таких примеров является random , который сам импортируется многими другими), поэтому тривиальные части кода, которые не связаны с все, чтобы криптография (по крайней мере, не в 1 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. gorithms_guaranteed :
Набор, содержащий имена алгоритмов ha sh, гарантированно поддерживаемых этот модуль на всех платформах. Обратите внимание, что «md5» находится в этом списке, несмотря на то, что некоторые вышестоящие поставщики предлагают странную «FIPS-совместимую» сборку Python, которая исключает ее.
Ниже приведен пример пользовательского Python 2.7 установка (которую я собрал довольно давно go, стоит упомянуть, что он динамически связывается с OpenSSL .dll s):
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, чем универсальная c версия, и также находится в python*. So (или сам исполняемый файл python)
- Реализация OpenSSL находится в libcrypto * .so , и к нему обращается оболочка _hashlib.so , которая выполняет обратное преобразование типов 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 .. ~ 1 КБ ], реализация OpenSSL , кажется, выполняет лучше, чем встроенный
- Я знаю, что слишком мало результатов, чтобы претендовать на шаблон, но кажется, что обе реализации кажутся линейно пропорциональными (с точки зрения времени) размеру сообщения (но встроенный наклон кажется быть немного круче - это означает, что в долгосрочной перспективе он будет работать хуже
В заключение, если все ваши сообщения маленькие, а встроенная реализация работает лучше для вас, используйте ее.
Обновление # 1
Графическое представление (мне пришлось уменьшить число итераций timeit на порядка величины, так как это займет слишком много времени для больших сообщений):
![Img0](https://i.stack.imgur.com/eY0ZR.png)
и масштабирование области, где пересекаются 2 графика:
![Img1](https://i.stack.imgur.com/YcFn8.png)