xcrypt 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Rakefile +106 -0
- data/ext/libxcrypt/AUTHORS +38 -0
- data/ext/libxcrypt/COPYING.LIB +502 -0
- data/ext/libxcrypt/ChangeLog +239 -0
- data/ext/libxcrypt/INSTALL +380 -0
- data/ext/libxcrypt/LICENSING +152 -0
- data/ext/libxcrypt/Makefile.am +704 -0
- data/ext/libxcrypt/Makefile.in +4110 -0
- data/ext/libxcrypt/NEWS +630 -0
- data/ext/libxcrypt/README +1 -0
- data/ext/libxcrypt/README.md +179 -0
- data/ext/libxcrypt/THANKS +13 -0
- data/ext/libxcrypt/TODO +1 -0
- data/ext/libxcrypt/TODO.md +100 -0
- data/ext/libxcrypt/aclocal.m4 +2617 -0
- data/ext/libxcrypt/autogen.sh +33 -0
- data/ext/libxcrypt/autom4te.cache/output.0 +19884 -0
- data/ext/libxcrypt/autom4te.cache/output.1 +19884 -0
- data/ext/libxcrypt/autom4te.cache/output.2 +19884 -0
- data/ext/libxcrypt/autom4te.cache/output.3 +19885 -0
- data/ext/libxcrypt/autom4te.cache/requests +714 -0
- data/ext/libxcrypt/autom4te.cache/traces.0 +4088 -0
- data/ext/libxcrypt/autom4te.cache/traces.1 +1060 -0
- data/ext/libxcrypt/autom4te.cache/traces.2 +4088 -0
- data/ext/libxcrypt/autom4te.cache/traces.3 +1060 -0
- data/ext/libxcrypt/build-aux/ci/ci-log-dependency-versions +79 -0
- data/ext/libxcrypt/build-aux/ci/ci-log-logfiles +22 -0
- data/ext/libxcrypt/build-aux/ci/clang-gcov-wrapper +2 -0
- data/ext/libxcrypt/build-aux/ci/configure-wrapper +10 -0
- data/ext/libxcrypt/build-aux/ci/summarize-coverage +24 -0
- data/ext/libxcrypt/build-aux/m4/ax_append_compile_flags.m4 +46 -0
- data/ext/libxcrypt/build-aux/m4/ax_append_flag.m4 +50 -0
- data/ext/libxcrypt/build-aux/m4/ax_check_compile_flag.m4 +53 -0
- data/ext/libxcrypt/build-aux/m4/ax_check_vscript.m4 +142 -0
- data/ext/libxcrypt/build-aux/m4/ax_gcc_func_attribute.m4 +246 -0
- data/ext/libxcrypt/build-aux/m4/ax_require_defined.m4 +37 -0
- data/ext/libxcrypt/build-aux/m4/ax_valgrind_check.m4 +239 -0
- data/ext/libxcrypt/build-aux/m4/libtool.m4 +8488 -0
- data/ext/libxcrypt/build-aux/m4/ltoptions.m4 +467 -0
- data/ext/libxcrypt/build-aux/m4/ltsugar.m4 +124 -0
- data/ext/libxcrypt/build-aux/m4/ltversion.m4 +24 -0
- data/ext/libxcrypt/build-aux/m4/lt~obsolete.m4 +99 -0
- data/ext/libxcrypt/build-aux/m4/pkg_compat.m4 +88 -0
- data/ext/libxcrypt/build-aux/m4/zw_alignment.m4 +90 -0
- data/ext/libxcrypt/build-aux/m4/zw_automodern.m4 +307 -0
- data/ext/libxcrypt/build-aux/m4/zw_detect_asan.m4 +24 -0
- data/ext/libxcrypt/build-aux/m4/zw_endianness.m4 +152 -0
- data/ext/libxcrypt/build-aux/m4/zw_ld_wrap.m4 +47 -0
- data/ext/libxcrypt/build-aux/m4/zw_prog_perl.m4 +40 -0
- data/ext/libxcrypt/build-aux/m4/zw_simple_warnings.m4 +150 -0
- data/ext/libxcrypt/build-aux/m4/zw_static_assert.m4 +68 -0
- data/ext/libxcrypt/build-aux/m4-autogen/compile +364 -0
- data/ext/libxcrypt/build-aux/m4-autogen/config.guess +1815 -0
- data/ext/libxcrypt/build-aux/m4-autogen/config.sub +2354 -0
- data/ext/libxcrypt/build-aux/m4-autogen/depcomp +792 -0
- data/ext/libxcrypt/build-aux/m4-autogen/install-sh +541 -0
- data/ext/libxcrypt/build-aux/m4-autogen/ltmain.sh +11524 -0
- data/ext/libxcrypt/build-aux/m4-autogen/missing +236 -0
- data/ext/libxcrypt/build-aux/m4-autogen/test-driver +160 -0
- data/ext/libxcrypt/build-aux/scripts/BuildCommon.pm +712 -0
- data/ext/libxcrypt/build-aux/scripts/check-perlcritic-config +76 -0
- data/ext/libxcrypt/build-aux/scripts/compute-symver-floor +116 -0
- data/ext/libxcrypt/build-aux/scripts/expand-selected-hashes +80 -0
- data/ext/libxcrypt/build-aux/scripts/gen-crypt-h +131 -0
- data/ext/libxcrypt/build-aux/scripts/gen-crypt-hashes-h +141 -0
- data/ext/libxcrypt/build-aux/scripts/gen-crypt-symbol-vers-h +150 -0
- data/ext/libxcrypt/build-aux/scripts/gen-libcrypt-map +67 -0
- data/ext/libxcrypt/build-aux/scripts/move-if-change +84 -0
- data/ext/libxcrypt/build-aux/scripts/skip-if-exec-format-error +78 -0
- data/ext/libxcrypt/codecov.yml +4 -0
- data/ext/libxcrypt/config.h.in +303 -0
- data/ext/libxcrypt/configure +19885 -0
- data/ext/libxcrypt/configure.ac +549 -0
- data/ext/libxcrypt/doc/crypt.3 +512 -0
- data/ext/libxcrypt/doc/crypt.5 +343 -0
- data/ext/libxcrypt/doc/crypt_checksalt.3 +106 -0
- data/ext/libxcrypt/doc/crypt_gensalt.3 +285 -0
- data/ext/libxcrypt/doc/crypt_gensalt_ra.3 +1 -0
- data/ext/libxcrypt/doc/crypt_gensalt_rn.3 +1 -0
- data/ext/libxcrypt/doc/crypt_preferred_method.3 +68 -0
- data/ext/libxcrypt/doc/crypt_r.3 +1 -0
- data/ext/libxcrypt/doc/crypt_ra.3 +1 -0
- data/ext/libxcrypt/doc/crypt_rn.3 +1 -0
- data/ext/libxcrypt/lib/alg-des-tables.c +3858 -0
- data/ext/libxcrypt/lib/alg-des.c +269 -0
- data/ext/libxcrypt/lib/alg-des.h +74 -0
- data/ext/libxcrypt/lib/alg-gost3411-2012-const.h +313 -0
- data/ext/libxcrypt/lib/alg-gost3411-2012-core.c +238 -0
- data/ext/libxcrypt/lib/alg-gost3411-2012-core.h +51 -0
- data/ext/libxcrypt/lib/alg-gost3411-2012-hmac.c +78 -0
- data/ext/libxcrypt/lib/alg-gost3411-2012-hmac.h +46 -0
- data/ext/libxcrypt/lib/alg-gost3411-2012-precalc.h +1426 -0
- data/ext/libxcrypt/lib/alg-gost3411-2012-ref.h +67 -0
- data/ext/libxcrypt/lib/alg-hmac-sha1.c +140 -0
- data/ext/libxcrypt/lib/alg-hmac-sha1.h +35 -0
- data/ext/libxcrypt/lib/alg-md4.c +270 -0
- data/ext/libxcrypt/lib/alg-md4.h +43 -0
- data/ext/libxcrypt/lib/alg-md5.c +291 -0
- data/ext/libxcrypt/lib/alg-md5.h +43 -0
- data/ext/libxcrypt/lib/alg-sha1.c +288 -0
- data/ext/libxcrypt/lib/alg-sha1.h +34 -0
- data/ext/libxcrypt/lib/alg-sha256.c +630 -0
- data/ext/libxcrypt/lib/alg-sha256.h +123 -0
- data/ext/libxcrypt/lib/alg-sha512.c +311 -0
- data/ext/libxcrypt/lib/alg-sha512.h +81 -0
- data/ext/libxcrypt/lib/alg-sm3-hmac.c +113 -0
- data/ext/libxcrypt/lib/alg-sm3-hmac.h +42 -0
- data/ext/libxcrypt/lib/alg-sm3.c +449 -0
- data/ext/libxcrypt/lib/alg-sm3.h +63 -0
- data/ext/libxcrypt/lib/alg-yescrypt-common.c +713 -0
- data/ext/libxcrypt/lib/alg-yescrypt-opt.c +1568 -0
- data/ext/libxcrypt/lib/alg-yescrypt-platform.c +106 -0
- data/ext/libxcrypt/lib/alg-yescrypt.h +360 -0
- data/ext/libxcrypt/lib/byteorder.h +164 -0
- data/ext/libxcrypt/lib/crypt-bcrypt.c +1061 -0
- data/ext/libxcrypt/lib/crypt-des-obsolete.c +215 -0
- data/ext/libxcrypt/lib/crypt-des.c +491 -0
- data/ext/libxcrypt/lib/crypt-gensalt-static.c +40 -0
- data/ext/libxcrypt/lib/crypt-gost-yescrypt.c +182 -0
- data/ext/libxcrypt/lib/crypt-md5.c +232 -0
- data/ext/libxcrypt/lib/crypt-nthash.c +134 -0
- data/ext/libxcrypt/lib/crypt-obsolete.h +40 -0
- data/ext/libxcrypt/lib/crypt-pbkdf1-sha1.c +260 -0
- data/ext/libxcrypt/lib/crypt-port.h +514 -0
- data/ext/libxcrypt/lib/crypt-scrypt.c +247 -0
- data/ext/libxcrypt/lib/crypt-sha256.c +308 -0
- data/ext/libxcrypt/lib/crypt-sha512.c +323 -0
- data/ext/libxcrypt/lib/crypt-sm3-yescrypt.c +189 -0
- data/ext/libxcrypt/lib/crypt-sm3.c +308 -0
- data/ext/libxcrypt/lib/crypt-static.c +44 -0
- data/ext/libxcrypt/lib/crypt-sunmd5.c +314 -0
- data/ext/libxcrypt/lib/crypt-yescrypt.c +177 -0
- data/ext/libxcrypt/lib/crypt.c +421 -0
- data/ext/libxcrypt/lib/crypt.h.in +249 -0
- data/ext/libxcrypt/lib/gen-des-tables.c +363 -0
- data/ext/libxcrypt/lib/hashes.conf +59 -0
- data/ext/libxcrypt/lib/libcrypt.map.in +48 -0
- data/ext/libxcrypt/lib/libcrypt.minver +97 -0
- data/ext/libxcrypt/lib/libxcrypt.pc.in +15 -0
- data/ext/libxcrypt/lib/util-base64.c +26 -0
- data/ext/libxcrypt/lib/util-gensalt-sha.c +88 -0
- data/ext/libxcrypt/lib/util-get-random-bytes.c +154 -0
- data/ext/libxcrypt/lib/util-make-failure-token.c +48 -0
- data/ext/libxcrypt/lib/util-xbzero.c +43 -0
- data/ext/libxcrypt/lib/util-xstrcpy.c +42 -0
- data/ext/libxcrypt/lib/xcrypt.h.in +58 -0
- data/ext/libxcrypt/libxcrypt.spec.rpkg +481 -0
- data/ext/libxcrypt/rpkg.conf +2 -0
- data/ext/libxcrypt/rpkg.macros +86 -0
- data/ext/libxcrypt/test/TestCommon.pm +326 -0
- data/ext/libxcrypt/test/alg-des.c +80 -0
- data/ext/libxcrypt/test/alg-gost3411-2012-hmac.c +90 -0
- data/ext/libxcrypt/test/alg-gost3411-2012.c +191 -0
- data/ext/libxcrypt/test/alg-hmac-sha1.c +187 -0
- data/ext/libxcrypt/test/alg-md4.c +111 -0
- data/ext/libxcrypt/test/alg-md5.c +134 -0
- data/ext/libxcrypt/test/alg-pbkdf-hmac-sha256.c +269 -0
- data/ext/libxcrypt/test/alg-sha1.c +111 -0
- data/ext/libxcrypt/test/alg-sha256.c +141 -0
- data/ext/libxcrypt/test/alg-sha512.c +170 -0
- data/ext/libxcrypt/test/alg-sm3-hmac.c +149 -0
- data/ext/libxcrypt/test/alg-sm3.c +168 -0
- data/ext/libxcrypt/test/alg-yescrypt.c +466 -0
- data/ext/libxcrypt/test/badsalt.c +726 -0
- data/ext/libxcrypt/test/badsetting.c +350 -0
- data/ext/libxcrypt/test/byteorder.c +254 -0
- data/ext/libxcrypt/test/checksalt.c +265 -0
- data/ext/libxcrypt/test/compile-strong-alias.c +43 -0
- data/ext/libxcrypt/test/crypt-badargs.c +392 -0
- data/ext/libxcrypt/test/crypt-gost-yescrypt.c +149 -0
- data/ext/libxcrypt/test/crypt-nested-call.c +180 -0
- data/ext/libxcrypt/test/crypt-sm3-yescrypt.c +149 -0
- data/ext/libxcrypt/test/crypt-too-long-phrase.c +157 -0
- data/ext/libxcrypt/test/des-cases.h +196 -0
- data/ext/libxcrypt/test/des-obsolete.c +206 -0
- data/ext/libxcrypt/test/des-obsolete_r.c +207 -0
- data/ext/libxcrypt/test/explicit-bzero.c +334 -0
- data/ext/libxcrypt/test/gensalt-bcrypt_x.c +54 -0
- data/ext/libxcrypt/test/gensalt-extradata.c +246 -0
- data/ext/libxcrypt/test/gensalt-nested-call.c +126 -0
- data/ext/libxcrypt/test/gensalt-nthash.c +65 -0
- data/ext/libxcrypt/test/gensalt.c +599 -0
- data/ext/libxcrypt/test/getrandom-fallbacks.c +295 -0
- data/ext/libxcrypt/test/getrandom-interface.c +211 -0
- data/ext/libxcrypt/test/ka-table-gen.py +945 -0
- data/ext/libxcrypt/test/ka-table.inc +5849 -0
- data/ext/libxcrypt/test/ka-tester.c +240 -0
- data/ext/libxcrypt/test/preferred-method.c +133 -0
- data/ext/libxcrypt/test/short-outbuf.c +119 -0
- data/ext/libxcrypt/test/special-char-salt.c +1160 -0
- data/ext/libxcrypt/test/symbols-compat.pl +137 -0
- data/ext/libxcrypt/test/symbols-renames.pl +107 -0
- data/ext/libxcrypt/test/symbols-static.pl +87 -0
- data/ext/xcrypt/xcrypt.c +9 -0
- data/lib/xcrypt/ffi.rb +76 -0
- data/lib/xcrypt/version.rb +5 -0
- data/lib/xcrypt.rb +89 -0
- metadata +267 -0
|
@@ -0,0 +1,945 @@
|
|
|
1
|
+
#! /usr/bin/python3
|
|
2
|
+
# Compute test cases for ka-* tests.
|
|
3
|
+
#
|
|
4
|
+
# Written by Zack Weinberg <zackw at panix.com> in 2019.
|
|
5
|
+
# To the extent possible under law, Zack Weinberg has waived all
|
|
6
|
+
# copyright and related or neighboring rights to this work.
|
|
7
|
+
#
|
|
8
|
+
# See https://creativecommons.org/publicdomain/zero/1.0/ for further
|
|
9
|
+
# details.
|
|
10
|
+
|
|
11
|
+
# This program generates ka-table.inc, which defines the set
|
|
12
|
+
# of tests performed by the ka-*.c tests. It is not run automatically
|
|
13
|
+
# during the build for two reasons: it's very slow, and it requires Python
|
|
14
|
+
# 3.6 or greater with Passlib <https://passlib.readthedocs.io/en/stable/>
|
|
15
|
+
# available.
|
|
16
|
+
#
|
|
17
|
+
# If you modify this program, make sure to update ka-table.inc,
|
|
18
|
+
# by running 'make regen-ka-table' (libcrypt.so must already have been
|
|
19
|
+
# built), and then check in the updates to that file in the same
|
|
20
|
+
# commit as your changes to this program. You will need to install
|
|
21
|
+
# Passlib itself, but not any other libraries.
|
|
22
|
+
#
|
|
23
|
+
# This program intentionally uses Passlib's slow pure-Python back
|
|
24
|
+
# ends, rather than accelerated C modules that tend to be, at their
|
|
25
|
+
# core, the same code libxcrypt uses itself, so that we really are
|
|
26
|
+
# testing libxcrypt against known answers generated with a different
|
|
27
|
+
# implementation.
|
|
28
|
+
|
|
29
|
+
import array
|
|
30
|
+
import ctypes
|
|
31
|
+
import multiprocessing
|
|
32
|
+
import os
|
|
33
|
+
import re
|
|
34
|
+
import sys
|
|
35
|
+
|
|
36
|
+
# force passlib to allow use of its built-in bcrypt implementation
|
|
37
|
+
os.environ["PASSLIB_BUILTIN_BCRYPT"] = "enabled"
|
|
38
|
+
|
|
39
|
+
import passlib.hash
|
|
40
|
+
|
|
41
|
+
# In order to tickle various bugs and limitations in older hashing
|
|
42
|
+
# methods precisely, we need to test several passphrases whose byte
|
|
43
|
+
# sequences are not valid Unicode text in any encoding. We therefore
|
|
44
|
+
# use exclusively byte strings in this array.
|
|
45
|
+
PHRASES = [
|
|
46
|
+
# All ASCII printable, various lengths. Most of these were taken
|
|
47
|
+
# from older known-answer tests for specific hashing methods.
|
|
48
|
+
b'',
|
|
49
|
+
b' ',
|
|
50
|
+
b'a',
|
|
51
|
+
b'ab',
|
|
52
|
+
b'abc',
|
|
53
|
+
b'U*U',
|
|
54
|
+
b'U*U*',
|
|
55
|
+
b'U*U*U',
|
|
56
|
+
b'.....',
|
|
57
|
+
b'dragon',
|
|
58
|
+
b'dRaGoN',
|
|
59
|
+
b'DrAgOn',
|
|
60
|
+
b'PAROLX',
|
|
61
|
+
b'U*U***U',
|
|
62
|
+
b'abcdefg',
|
|
63
|
+
b'01234567',
|
|
64
|
+
b'726 even',
|
|
65
|
+
b'zyxwvuts',
|
|
66
|
+
b'ab1234567',
|
|
67
|
+
b'alexander',
|
|
68
|
+
b'beautiful',
|
|
69
|
+
b'challenge',
|
|
70
|
+
b'chocolate',
|
|
71
|
+
b'cr1234567',
|
|
72
|
+
b'katherine',
|
|
73
|
+
b'stephanie',
|
|
74
|
+
b'sunflower',
|
|
75
|
+
b'basketball',
|
|
76
|
+
b'porsche911',
|
|
77
|
+
b'|_337T`/p3',
|
|
78
|
+
b'thunderbird',
|
|
79
|
+
b'Hello world!',
|
|
80
|
+
b'pleaseletmein',
|
|
81
|
+
b'a short string',
|
|
82
|
+
b'zxyDPWgydbQjgq',
|
|
83
|
+
b'photojournalism',
|
|
84
|
+
b'ecclesiastically',
|
|
85
|
+
b'congregationalism',
|
|
86
|
+
b'dihydrosphingosine',
|
|
87
|
+
b'semianthropological',
|
|
88
|
+
b'palaeogeographically',
|
|
89
|
+
b'electromyographically',
|
|
90
|
+
b'noninterchangeableness',
|
|
91
|
+
b'abcdefghijklmnopqrstuvwxyz',
|
|
92
|
+
b'electroencephalographically',
|
|
93
|
+
b'antidisestablishmentarianism',
|
|
94
|
+
b'cyclotrimethylenetrinitramine',
|
|
95
|
+
b'dichlorodiphenyltrichloroethane',
|
|
96
|
+
b'multiple words seperated by spaces',
|
|
97
|
+
b'supercalifragilisticexpialidocious',
|
|
98
|
+
b'we have a short salt string but not a short password',
|
|
99
|
+
b'multiple word$ $eperated by $pace$ and $pecial character$',
|
|
100
|
+
b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
|
|
101
|
+
(b'1234567890123456789012345678901234567890'
|
|
102
|
+
b'1234567890123456789012345678901234567890'),
|
|
103
|
+
(b'a very much longer text to encrypt. This one even stretches over more'
|
|
104
|
+
b'than one line.'),
|
|
105
|
+
|
|
106
|
+
# ASCII printables with their high bits flipped - DES-based hashes collide.
|
|
107
|
+
# All of these have an exact counterpart above.
|
|
108
|
+
b'\xd0\xc1\xd2\xcf\xcc\xd8', # 'PAROLX'
|
|
109
|
+
b'\xd5\xaa\xd5\xaa\xaa\xaa\xd5\xaa', # 'U*U***U*'
|
|
110
|
+
b'\xe1\xec\xe5\xf8\xe1\xee\xe4\xe5\xf2', # 'alexander'
|
|
111
|
+
b'\xf3\xf4\xe5\xf0\xe8\xe1\xee\xe9\xe5', # 'stephanie'
|
|
112
|
+
# '*U*U*U*U*U*U*U*U*'
|
|
113
|
+
b'\xaa\xd5\xaa\xd5\xaa\xd5\xaa\xd5\xaa\xd5\xaa\xd5\xaa\xd5\xaa\xd5\xaa',
|
|
114
|
+
|
|
115
|
+
# A few UTF-8 strings and what they will collide with for
|
|
116
|
+
# DES-based hashes.
|
|
117
|
+
b'\xC3\xA9tude', b'C)tude', # UTF-8(NFC(étude))
|
|
118
|
+
b'Chl\xC3\xB6e', b'ChlC6e', # UTF-8(NFC(Chlöe))
|
|
119
|
+
# Eight letters, but 10 bytes: UTF-8(NFC(Ångström))
|
|
120
|
+
b'\xC3\x85ngstr\xC3\xB6m', b'C\x05ngstrC6m', b'C\x05ngstrC'
|
|
121
|
+
|
|
122
|
+
# descrypt truncates everything to 8 characters.
|
|
123
|
+
b'U*U***U*', b'U*U***U*ignored',
|
|
124
|
+
b'U*U*U*U*', b'U*U*U*U*ignored',
|
|
125
|
+
b'*U*U*U*U', b'*U*U*U*U*', b'*U*U*U*U*U*U*U*U', b'*U*U*U*U*U*U*U*U*',
|
|
126
|
+
|
|
127
|
+
# Patterns designed to tickle the bcrypt $2x$ sign-extension bug.
|
|
128
|
+
b'\xa3',
|
|
129
|
+
b'\xa3a',
|
|
130
|
+
b'\xd1\x91',
|
|
131
|
+
b'\xa3ab',
|
|
132
|
+
b'\xff\xff\xa3',
|
|
133
|
+
b'1\xa3345',
|
|
134
|
+
b'\xff\xa3345',
|
|
135
|
+
b'\xff\xa334\xff\xff\xff\xa3345',
|
|
136
|
+
|
|
137
|
+
(b'\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff'
|
|
138
|
+
b'\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff'
|
|
139
|
+
b'\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff'
|
|
140
|
+
b'\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff'
|
|
141
|
+
b'\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff'
|
|
142
|
+
b'\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff'),
|
|
143
|
+
|
|
144
|
+
(b'\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55'
|
|
145
|
+
b'\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55'
|
|
146
|
+
b'\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55'
|
|
147
|
+
b'\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55'
|
|
148
|
+
b'\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55'
|
|
149
|
+
b'\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55'),
|
|
150
|
+
|
|
151
|
+
# bcrypt truncates to 72 characters
|
|
152
|
+
(b'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
153
|
+
b'0123456789'),
|
|
154
|
+
(b'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
155
|
+
b'0123456789chars after 72 are ignored'),
|
|
156
|
+
|
|
157
|
+
(b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'
|
|
158
|
+
b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'
|
|
159
|
+
b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'
|
|
160
|
+
b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'
|
|
161
|
+
b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'
|
|
162
|
+
b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'),
|
|
163
|
+
|
|
164
|
+
(b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'
|
|
165
|
+
b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'
|
|
166
|
+
b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'
|
|
167
|
+
b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'
|
|
168
|
+
b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'
|
|
169
|
+
b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'
|
|
170
|
+
b'chars after 72 are ignored as usual'),
|
|
171
|
+
|
|
172
|
+
# bigcrypt truncates to 128 characters
|
|
173
|
+
# (first sentence of _Twenty Thousand Leagues Under The Sea_)
|
|
174
|
+
(b'THE YEAR 1866 was marked by a bizarre development, an unexplained and '
|
|
175
|
+
b'downright inexplicable phenomenon that surely no one has forgotten.'),
|
|
176
|
+
|
|
177
|
+
# first 8 characters of above (des)
|
|
178
|
+
b'THE YEAR',
|
|
179
|
+
# first 72 characters (bcrypt)
|
|
180
|
+
b'THE YEAR 1866 was marked by a bizarre development, an unexplained and do',
|
|
181
|
+
# first 128 characters (bigcrypt)
|
|
182
|
+
(b'THE YEAR 1866 was marked by a bizarre development, an unexplained and '
|
|
183
|
+
b'downright inexplicable phenomenon that surely no one has f')
|
|
184
|
+
]
|
|
185
|
+
|
|
186
|
+
# passlib does not support all of the hashing methods we do, no longer
|
|
187
|
+
# supports generation of bare setting strings, and in some cases does
|
|
188
|
+
# not support all of the variants we need to generate. Therefore,
|
|
189
|
+
# there is a shim for each hashing method (variant). Shims must be
|
|
190
|
+
# named 'h_METHOD' where METHOD is the name for the method used by the
|
|
191
|
+
# INCLUDE_method macros.
|
|
192
|
+
#
|
|
193
|
+
# Each shim function takes at least the arguments phrase, rounds, and
|
|
194
|
+
# salt, in that order. Additional optional arguments are allowed. It
|
|
195
|
+
# should do 'yield (phrase, setting, expected)' at least once, where
|
|
196
|
+
# phrase is the phrase argument, setting is a setting string generated
|
|
197
|
+
# from rounds and salt, and expected is the hashed passphrase expected
|
|
198
|
+
# to be generated from that combination of phrase and setting.
|
|
199
|
+
#
|
|
200
|
+
# When implementing new shims, use of passlib's pure-Python "backends"
|
|
201
|
+
# is strongly preferred where possible, because the speed of this
|
|
202
|
+
# program does not matter, and the C backends tend to be based on some
|
|
203
|
+
# incarnation of the same code that libxcrypt uses itself, so it
|
|
204
|
+
# wouldn't be a proper interop test.
|
|
205
|
+
|
|
206
|
+
# straightforward passlib wrappers, sorted by age of algorithm
|
|
207
|
+
DES_CRYPT = passlib.hash.des_crypt
|
|
208
|
+
DES_CRYPT.set_backend("builtin")
|
|
209
|
+
def h_descrypt(phrase, rounds, salt):
|
|
210
|
+
expected = DES_CRYPT.using(
|
|
211
|
+
salt=salt, truncate_error=False
|
|
212
|
+
).hash(phrase)
|
|
213
|
+
setting = expected[:2]
|
|
214
|
+
yield (phrase, setting, expected)
|
|
215
|
+
|
|
216
|
+
BIGCRYPT = passlib.hash.bigcrypt
|
|
217
|
+
# BIGCRYPT.set_backend("builtin") # currently p.h.bigcrypt always uses builtin
|
|
218
|
+
def h_bigcrypt(phrase, rounds, salt):
|
|
219
|
+
# p.h.bigcrypt doesn't truncate to 128 chars.
|
|
220
|
+
# The bigcrypt implementation in libxcrypt was reverse engineered
|
|
221
|
+
# from a closed-source original and it's possible that they could
|
|
222
|
+
# have gotten it wrong, but let's stick to what we have.
|
|
223
|
+
expected = BIGCRYPT.using(
|
|
224
|
+
salt=salt
|
|
225
|
+
).hash(phrase[:128])
|
|
226
|
+
# bigcrypt has no prefix, so our crypt() looks at the length of
|
|
227
|
+
# the setting string to decide whether it should use bigcrypt or
|
|
228
|
+
# descrypt. For bigcrypt to be used, the setting must be too long
|
|
229
|
+
# to be a traditional DES hashed password.
|
|
230
|
+
setting = expected[:2] + ".............."
|
|
231
|
+
yield (phrase, setting, expected)
|
|
232
|
+
|
|
233
|
+
BSDI_CRYPT = passlib.hash.bsdi_crypt
|
|
234
|
+
BSDI_CRYPT.set_backend("builtin")
|
|
235
|
+
def h_bsdicrypt(phrase, rounds, salt):
|
|
236
|
+
expected = BSDI_CRYPT.using(
|
|
237
|
+
salt=salt, rounds=rounds
|
|
238
|
+
).hash(phrase)
|
|
239
|
+
setting = expected[:9]
|
|
240
|
+
yield (phrase, setting, expected)
|
|
241
|
+
|
|
242
|
+
MD5_CRYPT = passlib.hash.md5_crypt
|
|
243
|
+
MD5_CRYPT.set_backend("builtin")
|
|
244
|
+
def h_md5crypt(phrase, rounds, salt):
|
|
245
|
+
expected = MD5_CRYPT.using(
|
|
246
|
+
salt=salt
|
|
247
|
+
).hash(phrase)
|
|
248
|
+
setting = expected[:expected.rfind('$')]
|
|
249
|
+
yield (phrase, setting, expected)
|
|
250
|
+
|
|
251
|
+
BSD_NTHASH = passlib.hash.bsd_nthash
|
|
252
|
+
#BSD_NTHASH.set_backend("builtin") # has only the built-in backend
|
|
253
|
+
def h_nt(phrase, rounds, salt):
|
|
254
|
+
# passlib.hash.bsd_nthash attempts to decode byte strings as UTF-8, which
|
|
255
|
+
# is Not What We Want. Python's iso_8859_1 is an identity map from 00..FF
|
|
256
|
+
# to U+0000..U+00FF, which is correct for this application.
|
|
257
|
+
expected = BSD_NTHASH.hash(phrase.decode("iso_8859_1"))
|
|
258
|
+
# NTHash doesn't have a salt.
|
|
259
|
+
# Older versions of libxcrypt generated a fake salt which
|
|
260
|
+
# we should ensure is ignored.
|
|
261
|
+
yield (phrase, "$3$", expected)
|
|
262
|
+
yield (phrase, "$3$__not_used__0123456789abcdef", expected)
|
|
263
|
+
|
|
264
|
+
SHA1_CRYPT = passlib.hash.sha1_crypt
|
|
265
|
+
SHA1_CRYPT.set_backend("builtin")
|
|
266
|
+
def h_sha1crypt(phrase, rounds, salt):
|
|
267
|
+
expected = SHA1_CRYPT.using(
|
|
268
|
+
salt=salt, rounds=rounds
|
|
269
|
+
).hash(phrase)
|
|
270
|
+
setting = expected[:expected.rfind('$')]
|
|
271
|
+
yield (phrase, setting, expected)
|
|
272
|
+
|
|
273
|
+
SHA256_CRYPT = passlib.hash.sha256_crypt
|
|
274
|
+
SHA256_CRYPT.set_backend("builtin")
|
|
275
|
+
def h_sha256crypt(phrase, rounds, salt):
|
|
276
|
+
expected = SHA256_CRYPT.using(
|
|
277
|
+
salt=salt, rounds=rounds
|
|
278
|
+
).hash(phrase)
|
|
279
|
+
setting = expected[:expected.rfind('$')]
|
|
280
|
+
yield (phrase, setting, expected)
|
|
281
|
+
|
|
282
|
+
SHA512_CRYPT = passlib.hash.sha512_crypt
|
|
283
|
+
SHA512_CRYPT.set_backend("builtin")
|
|
284
|
+
def h_sha512crypt(phrase, rounds, salt):
|
|
285
|
+
expected = SHA512_CRYPT.using(
|
|
286
|
+
salt=salt, rounds=rounds
|
|
287
|
+
).hash(phrase)
|
|
288
|
+
setting = expected[:expected.rfind('$')]
|
|
289
|
+
yield (phrase, setting, expected)
|
|
290
|
+
|
|
291
|
+
# these need to do more work by hand
|
|
292
|
+
|
|
293
|
+
# We need to test setting strings both with and without the suffix
|
|
294
|
+
# that triggers the off-by-one error in the original Sun hash parser
|
|
295
|
+
# (which must be preserved by all interoperable implementations).
|
|
296
|
+
# passlib.hash.sun_md5_crypt.using(..., bare_salt=True) does not
|
|
297
|
+
# actually work.
|
|
298
|
+
from passlib.handlers.sun_md5_crypt import raw_sun_md5_crypt
|
|
299
|
+
def h_sunmd5(phrase, rounds, salt):
|
|
300
|
+
|
|
301
|
+
# sunmd5 is extremely slow in this test, compared to all the other
|
|
302
|
+
# hashes, because we have to do extra tests of bug-compatibility,
|
|
303
|
+
# because its round count cannot be reduced below 4096, and
|
|
304
|
+
# because on approximately half of those rounds it feeds an
|
|
305
|
+
# additional 1.5k of text to MD5_Update. The only optimization
|
|
306
|
+
# that wouldn't break compatibility would be to plug in a faster
|
|
307
|
+
# MD5 core, but that's not worth the engineering effort since it
|
|
308
|
+
# would only benefit obsolete hashes. Instead, skip most of the
|
|
309
|
+
# test phrases for this hash. This cuts the wall-clock time for
|
|
310
|
+
# ka-sunmd5 (on a current-generation x86-64) from fifty to nine
|
|
311
|
+
# seconds, which we can live with. sunmd5 feeds the phrase
|
|
312
|
+
# verbatim to MD5_Update, only once, with no length limit, so we
|
|
313
|
+
# don't need a lot of careful testing of different phrases. We do
|
|
314
|
+
# still include at least a few of the non-ASCII test phrases, and
|
|
315
|
+
# one very long phrase.
|
|
316
|
+
if 6 <= len(phrase) <= 128:
|
|
317
|
+
return
|
|
318
|
+
|
|
319
|
+
if rounds == 0:
|
|
320
|
+
bare_setting = "$md5$" + salt
|
|
321
|
+
else:
|
|
322
|
+
bare_setting = "$md5,rounds={}${}".format(rounds, salt)
|
|
323
|
+
|
|
324
|
+
suff_setting = bare_setting + "$"
|
|
325
|
+
bare_cksum = raw_sun_md5_crypt(phrase, rounds, bare_setting.encode("ascii"))
|
|
326
|
+
suff_cksum = raw_sun_md5_crypt(phrase, rounds, suff_setting.encode("ascii"))
|
|
327
|
+
|
|
328
|
+
bare_cksum = bare_cksum.decode("ascii")
|
|
329
|
+
suff_cksum = suff_cksum.decode("ascii")
|
|
330
|
+
|
|
331
|
+
yield (phrase, bare_setting, bare_setting + "$" + bare_cksum)
|
|
332
|
+
yield (phrase, bare_setting + "$x", bare_setting + "$" + bare_cksum)
|
|
333
|
+
yield (phrase, suff_setting, suff_setting + "$" + suff_cksum)
|
|
334
|
+
yield (phrase, suff_setting + "$", suff_setting + "$" + suff_cksum)
|
|
335
|
+
|
|
336
|
+
# testing bcrypt $2b$ and $2y$ is easy, but ...
|
|
337
|
+
BCRYPT = passlib.hash.bcrypt
|
|
338
|
+
BCRYPT.set_backend("builtin")
|
|
339
|
+
def h_bcrypt(phrase, rounds, salt):
|
|
340
|
+
expected = BCRYPT.using(
|
|
341
|
+
salt=salt, rounds=rounds, ident="2b"
|
|
342
|
+
).hash(phrase)
|
|
343
|
+
setting = expected[:-31]
|
|
344
|
+
yield (phrase, setting, expected)
|
|
345
|
+
|
|
346
|
+
def h_bcrypt_y(phrase, rounds, salt):
|
|
347
|
+
expected = BCRYPT.using(
|
|
348
|
+
salt=salt, rounds=rounds, ident="2y"
|
|
349
|
+
).hash(phrase)
|
|
350
|
+
setting = expected[:-31]
|
|
351
|
+
yield (phrase, setting, expected)
|
|
352
|
+
|
|
353
|
+
# ...passlib doesn't implement the quirks of crypt_blowfish's $2a$ or
|
|
354
|
+
# $2x$, but we really must test them. It is theoretically possible to
|
|
355
|
+
# implement the $2x$ quirk by a transformation on the input
|
|
356
|
+
# passphrase, but it would be hard to get right, and the $2a$ quirk
|
|
357
|
+
# cannot be implemented this way. The path of least resistance is to
|
|
358
|
+
# compute the $2b$ hash and then look up its output in a table of
|
|
359
|
+
# substitutions for each quirk. The collision resistance of the $2b$
|
|
360
|
+
# hash should protect us from false hits in these tables. (Remember
|
|
361
|
+
# that the $2x$ quirk is a _bug_, preserved for backward compatibility's
|
|
362
|
+
# sake, that causes output collisions; duplicate entries on the
|
|
363
|
+
# right-hand side of the $2x$ substitution table are expected.)
|
|
364
|
+
bcrypt_a_substitutions = {
|
|
365
|
+
'HdhhdUXVgLADnbTYf12kvsasO1gS51C': '5jlqAXzFdq.3//pJFBa432Pepsclbdu',
|
|
366
|
+
'caGU5ROXj4M8Tgsx3s/D5BQIuhazIWa': '7N5c8AaH.dbqz7.2o.V2mRkUDV0TZnO',
|
|
367
|
+
'8jdeg8QqT4CX3ERA9vZPFZAkxZRpxJW': 'D8jTC5oJeIumVOhMVpz79BzoGVhCjrW',
|
|
368
|
+
'9f4sA9SRA0scUKcRyC5kce8dao2.GKe': 'E8Lpo1/qkGPTBDBxEsJjeEzh9nkZ9uW',
|
|
369
|
+
'MOaOTHB4gEm.rriBjXNwBNh.Oc4mKGG': 'ZQhQBRpiYJCaQFgyHlB.t/F01cqLCIu',
|
|
370
|
+
'Qjdj3GXX7D0sFE9jji6wxSTWIhqI3US': 'euRNRfAA6e0fjpTfQPPAMU1PCOf9IHq',
|
|
371
|
+
'PIeeyENZVZmrKLAq5lwBUU9fMRVfV2m': 'h87NWu/js59XXaIj1hDyHxnjw7MJ5K6',
|
|
372
|
+
'VmFQpoXeVuKTzkg2ZRsAf.8PZJZg142': 'vrukkCtLqHBLoBDsz6QoBtSwzI9Qxiq',
|
|
373
|
+
}
|
|
374
|
+
def h_bcrypt_a(phrase, rounds, salt):
|
|
375
|
+
base = BCRYPT.using(
|
|
376
|
+
salt=salt, rounds=rounds, ident="2b"
|
|
377
|
+
).hash(phrase)
|
|
378
|
+
base_setting = base[4:-31]
|
|
379
|
+
base_output = base[-31:]
|
|
380
|
+
output = bcrypt_a_substitutions.get(base_output, base_output)
|
|
381
|
+
|
|
382
|
+
setting = "$2a$" + base_setting
|
|
383
|
+
expected = setting + output
|
|
384
|
+
yield (phrase, setting, expected)
|
|
385
|
+
|
|
386
|
+
bcrypt_x_substitutions = {
|
|
387
|
+
'xGPMyJSPyyeICKolPQ2gecm8rOgHwz.': '.sDifhVkUxvjPx6U4yeM2tC411Wuc.W',
|
|
388
|
+
'SRMKxjeMqVSDMhSLhOnvtEZ/p5KhUbq': '1Z0zKnHbUU3q/kk//Pknlv19a4/T8.K',
|
|
389
|
+
'PQInhDOdCKnXeUE.n/L.kQmTKM9ldK2': '25hhqa/GOJGmXui3avNI5MN8lOI2bCW',
|
|
390
|
+
'7UwHe/ywPmdp.nr.ZLQSxd8hqn7qURW': '2E0h7UFL/4fALemA5ApWrCWllQXSPTu',
|
|
391
|
+
'FZEYKXyyMgG13MK0uV8dwNotWf4Wm6e': '2M7Vc.sF98e8DDmnxFjRfAmrudbv6y.',
|
|
392
|
+
'ZHZAnvydiPNiYH2VjRNhEAD6BEiyaWS': '3kVpkKaj1q2TAXm.ptIi98Nj3zVV8A2',
|
|
393
|
+
'I7vjUzDOKf8XqcK8VSCm9b0bwoSm1Qm': '6He0iAS8JsdM.iB4OQ4fbsKMXPagLhy',
|
|
394
|
+
'tLCLEXAt3RUjOgs.yvfWSni4j1JX/JS': '7bLwFi3rlVcl.xfhc7LxjqwOExfxki2',
|
|
395
|
+
'zQPVIDk6wF8XmESji30KDHFabTlu0WK': '8gGm3RkYFflDX50UQs.tJ8InKNy.HGO',
|
|
396
|
+
'KBv1eBM2he2T/QheS8zPHMejn5fMNRe': '8jdeg8QqT4CX3ERA9vZPFZAkxZRpxJW',
|
|
397
|
+
'XEhidoDX1kz.RFnwWIzMJvtW2aP/k4e': '8jdeg8QqT4CX3ERA9vZPFZAkxZRpxJW',
|
|
398
|
+
'UIzecIiCguM2sPh1F7L9IS3zOtmg5Im': '9f4sA9SRA0scUKcRyC5kce8dao2.GKe',
|
|
399
|
+
'Xl2V4mQ7s0zX0b4XXH/b9UkRRCWpq3e': '9f4sA9SRA0scUKcRyC5kce8dao2.GKe',
|
|
400
|
+
'tHwehGUs3q0b/Ejn42MsoM4Yv/iA1rq': '9iflVP0Ezo/iaxO0XS74wFglLNeryTS',
|
|
401
|
+
'K2KXzhsMefB8v6hJJG3bOyKV.XRf6Qe': 'C3nvb4DotHdnRYgOPcdiK0C4q.DkIDO',
|
|
402
|
+
'EuEnx.TyCLYgkTV/uhWL5xJTHV7ZMjG': 'HdhhdUXVgLADnbTYf12kvsasO1gS51C',
|
|
403
|
+
'oaE0x.b2rUEITWgPdg.GEcU4ePHLh6m': 'IZEWKJgp.b.KG29zgOadgd2rUt5iV1e',
|
|
404
|
+
'3cFqlEl7Y0HWaVLHpyyCqG8dBNk1DSm': 'IvJ5WHgSbYKj7g9hhdVsAjyzcnVT/.m',
|
|
405
|
+
'gI4g/1M6K/Sz2bsgu9VDeEl6reszuXa': 'J6Y/kPTV/aHj7iJKuDfD5OPjVTvT2BK',
|
|
406
|
+
'rz1efvzeJjL4mQ813hrZNg3p1.ivOii': 'MOaOTHB4gEm.rriBjXNwBNh.Oc4mKGG',
|
|
407
|
+
'Tp1b9XCEV16BcrQ.0k4xf7V/OGPZLnK': 'N5E4WSTo/R5henexIN1o8xkGwe2V86W',
|
|
408
|
+
'CARhc7ugFdgoPjDb7LUG.yQF2lboK6e': 'NjlOVoE5aHHQGtU9zc25wu0VykHnD1G',
|
|
409
|
+
'nCdKcLO57oLlc6J6sNnGyfT9FrIawiW': 'PIeeyENZVZmrKLAq5lwBUU9fMRVfV2m',
|
|
410
|
+
'DQjlTXDA5PBQ97.qBJY/vsHPQhLJDMe': 'PMOS6ygjFMSbDo.iJJam/G63inGIOBO',
|
|
411
|
+
'1qOUgfpg30XDHLx/zrbWiMRcWyFhwye': 'QZ7A0p9q1Ag9Utfnfl/xif8NiDtVhO.',
|
|
412
|
+
'h0JFRyDXfP0duxAkVxWGr8nMDEDvPca': 'QiT.KUY9PXgIzL2aECMKb0EvVl0Pzw6',
|
|
413
|
+
'BvtRGGx3p8o0C5C36uS442Qqnrwofrq': 'Qjdj3GXX7D0sFE9jji6wxSTWIhqI3US',
|
|
414
|
+
'UTFLPGm29p.YVzcY6pqejGEql1x8Ccq': 'TYqa73Yp3leHe3D6.ysuJtNLwOma87C',
|
|
415
|
+
'YdPam5/ypFIyDUQMyCCEIwzVsTi0Sa6': 'TmFuGBy/Zgc6JVAr667oHeCvGQGyS1q',
|
|
416
|
+
'gbhoNOH4mWxoEhRrQNdeI.rpk9XeuZS': 'UPPO3QqmgMIXGHvbOLe2IkNzHLAToY2',
|
|
417
|
+
'ZkQGqjbMpqQ9oCsxNZjN8LQJaHFqPMC': 'VTMVcF7YBLV2/O6V1PNcQw0BD3hTN6a',
|
|
418
|
+
'RbKkfW2ph8bd8B5yul5E97DxgDw9cT.': 'VmFQpoXeVuKTzkg2ZRsAf.8PZJZg142',
|
|
419
|
+
'WI7ZNXFtzCd9mN1mWoNMQRHEmkDsZnm': 'VmFQpoXeVuKTzkg2ZRsAf.8PZJZg142',
|
|
420
|
+
'2WegkGS5Xr/qYNkfEi6JmnR16WVSwcW': 'Xv3TUB0NdnMpyn4cfg4g48oZxRSIrNC',
|
|
421
|
+
'LN/CEHXLfFeYdOOxbdxKu8ZqSIKgqAu': 'YdqUOXeMKw7X6zbqBXP6c1xqIKun7Oq',
|
|
422
|
+
'VAQY6kySmwStlNY.sut9Y87njVr0mm.': 'Ysbn1VpHCTzInfW/z/8Q3k676rxfmSW',
|
|
423
|
+
'qJn4AY9ch/WAR5JXeeJtVGeovjQrhd2': 'ZH9vItRapPbkFKo0iQqU4v71o0e19Mm',
|
|
424
|
+
'4QrucGf30zIbQA.sO0d1QrU63xBrEYq': 'bqJMLkbvnTFj0OYMu9tPnQXstRzX/e6',
|
|
425
|
+
'HD8RnTmGEavoR3LFVfdHvh3xA0QPka2': 'cYbtH8J2lfpMIiBKfF3pKpMno7JlLui',
|
|
426
|
+
'6WgD2zYQDPgxR2sXlUeEeGKknxt95W.': 'caGU5ROXj4M8Tgsx3s/D5BQIuhazIWa',
|
|
427
|
+
'rPSVExmrZ2WB1xntSbqZ/DRQRlKtVw.': 'caGU5ROXj4M8Tgsx3s/D5BQIuhazIWa',
|
|
428
|
+
'PYLCOpTKZmhFn1CoBM2XNbWgqMX4Jk2': 'cxMAJfIx3T.Fv3O0KjL9VdM/oSSUVRK',
|
|
429
|
+
'w8RVl3rh7sNazq544l0944qGq4GUFUq': 'fOj7giyJz5k22FHTKGVo8o1o5zGzPsq',
|
|
430
|
+
'KHsCqMFVxOAGJObHwEBR3JaEdKVu1.m': 'fRmxM11/x97bxCrhecMENdkPm7YpRbe',
|
|
431
|
+
'3wn02pxRJPnFwvlGt75DURDbt4g7om.': 'fY4v5x6.8txtKUKDP86z1xjlXG/GgZO',
|
|
432
|
+
'k9Hv17Gha84losGKAq61csCZokj5pyy': 'gLfxf5sydYesf658mrFYb51nLrn/4Sm',
|
|
433
|
+
'uTNb9MEHVGI7kd6UnQjYxgRNiKJM01S': 'h.z2vLHB/tYSU5fPXkrYB7TxLHGJnI6',
|
|
434
|
+
'heAts1y/8kcTTP0/vD3yeuMX1ihF8dO': 'j72N2Fi2j3pGalOZvTqtyH3bYGotuju',
|
|
435
|
+
'SqNATdQiNEckAKLsqgsKbAM5.hZoMCq': 'mjGosqV8OkKEcduYTNz5PKN2scswFya',
|
|
436
|
+
'k786rdsOdUP4cRi.dLa3dsYueMj5UnS': 'nQF1kDoMDjBBwXy2wwMni2gJLKqA0ta',
|
|
437
|
+
'G6PeIXKiqeNUPUbqFkMJvvI7G9hd51W': 'oBvt6zJCTP5OED1esTYUYPn31cWqwsa',
|
|
438
|
+
'TszY8.avBpwJ6xbNjwws3SKBbK6kj6S': 'odAvHZH9azlhi1x4pBLF25.hj08RMFi',
|
|
439
|
+
'z4QFggBRTVUeHRGL/CQxlAYHraYPcpa': 'ojiyBkc.4HZ2y5Yh0LxBbI6ZkLiRg0C',
|
|
440
|
+
'PPtdI0NcxZ4Txyv/Y5ORfcP1XFriKT2': 'pjce7u/YRnectNa8DXjsSGzRdyH2PSG',
|
|
441
|
+
'uIZ1Lgb.jHRDU/Z/LVXfpQCK72fTEHq': 'q5NMeQZ0UTyP/bILj02wdQ.Si5KHU1K',
|
|
442
|
+
'51cV.PJOQVwmiao4t4lXsb9Cc3Jnuem': 'rB3dV.fJGdSihNlP0vo5PemoaZRp6LS',
|
|
443
|
+
's6h1E6A2RzVn2KxXLQXsKosQeRo8bLa': 'sND7G4.cx6Dzn6TqbXfK99bElU0a7P.',
|
|
444
|
+
'A96emG/jBf0K1K6vCG.eZGdLkSridom': 'tlD3cmtHgs/TwWAvy5E3F.freZS1bau',
|
|
445
|
+
'hWYb0x3Q3zM0aBkB2G1arbzmWxRQS/i': 'whpbcVuyGrJbgveSSM3XQKa8G5alyRm',
|
|
446
|
+
'AqM0XavJxJXeVlJ3Te3umGJaPOCYmZi': 'xP2lldc1.10LvZDjJZXNBKLzWqnkbOa',
|
|
447
|
+
'UPBzTBMwJb5mKWflQ.5Rid4481RrxVy': 'xSD.pz8Zg3vt0Jiovghl5Dqrs8aw8ni',
|
|
448
|
+
'yM59Cq5iVZDB3u45gTNhRSnOgrY1tdG': 'yED5tIjzyeH90te88BUWvTrMFHsWgCi',
|
|
449
|
+
'k.qekGiJym3QgfeFCwNhPHg0Zk99KSa': 'yphVralDu2JlxYbCqwwGli/H6wBgBtC',
|
|
450
|
+
'iYbzuFNFwSfCgqTGNsUFtSDh8PJAqSe': 'zAUUWh4XGsBGYs6yyUJTSfEgzoLXO6G',
|
|
451
|
+
}
|
|
452
|
+
def h_bcrypt_x(phrase, rounds, salt):
|
|
453
|
+
base = BCRYPT.using(
|
|
454
|
+
salt=salt, rounds=rounds, ident="2b"
|
|
455
|
+
).hash(phrase)
|
|
456
|
+
base_setting = base[4:-31]
|
|
457
|
+
base_output = base[-31:]
|
|
458
|
+
output = bcrypt_x_substitutions.get(base_output, base_output)
|
|
459
|
+
|
|
460
|
+
setting = "$2x$" + base_setting
|
|
461
|
+
expected = setting + output
|
|
462
|
+
yield (phrase, setting, expected)
|
|
463
|
+
|
|
464
|
+
# passlib includes an scrypt implementation, but its encoded password
|
|
465
|
+
# format is not the $7$ format we implement, so instead we use the
|
|
466
|
+
# stdlib's hashlib.scrypt (this is why 3.6+ is required) and encode
|
|
467
|
+
# the setting string by hand. This may produce strings encoding N/r/p
|
|
468
|
+
# combinations that don't normally occur in the wild, but that's OK.
|
|
469
|
+
# The algorithm implemented by hashlib.scrypt is standardized as
|
|
470
|
+
# RFC 7914, so it's not an issue where that implementation came from.
|
|
471
|
+
# Yes, the salt is properly passed to raw_scrypt as-is.
|
|
472
|
+
from hashlib import scrypt as raw_scrypt
|
|
473
|
+
from passlib.utils.binary import h64 as hash64
|
|
474
|
+
def h_scrypt(phrase, rounds, salt):
|
|
475
|
+
p = 1
|
|
476
|
+
r = 8
|
|
477
|
+
log2N = rounds + 7
|
|
478
|
+
N = 1 << log2N
|
|
479
|
+
|
|
480
|
+
bytesalt = salt.encode("ascii")
|
|
481
|
+
setting = (b"$7$" +
|
|
482
|
+
hash64.encode_int6(log2N) +
|
|
483
|
+
hash64.encode_int30(r) +
|
|
484
|
+
hash64.encode_int30(p) +
|
|
485
|
+
bytesalt)
|
|
486
|
+
|
|
487
|
+
binhash = raw_scrypt(phrase, salt=bytesalt, p=p, r=r, n=N, dklen=32)
|
|
488
|
+
|
|
489
|
+
yield (phrase, setting, setting + b'$' + hash64.encode_bytes(binhash))
|
|
490
|
+
|
|
491
|
+
#
|
|
492
|
+
# passlib does not support either yescrypt or gost-yescrypt. In fact,
|
|
493
|
+
# as far as I can tell, at the time of writing, there exists only one
|
|
494
|
+
# implementation of yescrypt and gost-yescrypt, by Solar Designer et al
|
|
495
|
+
# which is the code we use ourselves. However, a test for round-
|
|
496
|
+
# trippability and API consistency is still worthwhile, as is a test
|
|
497
|
+
# that the implementation's current behavior is compatible with its
|
|
498
|
+
# behavior some time ago. Therefore, we encode setting strings by
|
|
499
|
+
# hand, and ctypes is used to access crypt_ra in the just-built
|
|
500
|
+
# libcrypt.so. This will only work if the library was configured with
|
|
501
|
+
# --enable-hashes=yescrypt,gost-yescrypt,[others] and --enable-shared,
|
|
502
|
+
# which is OK, since it's not run during a normal build. Remove this
|
|
503
|
+
# once passlib supports these hashes.
|
|
504
|
+
#
|
|
505
|
+
# crypt_ra is used because it's thread-safe but doesn't require us to
|
|
506
|
+
# know how big struct crypt_data is. There is no good way to arrange
|
|
507
|
+
# for the data object to be deallocated. Oh well.
|
|
508
|
+
LIBCRYPT = ctypes.cdll.LoadLibrary(os.path.join(os.getcwd(), ".libs",
|
|
509
|
+
"libcrypt.so"))
|
|
510
|
+
_xcrypt_crypt_ra = LIBCRYPT.crypt_ra
|
|
511
|
+
_xcrypt_crypt_ra.argtypes = [ctypes.c_char_p, ctypes.c_char_p,
|
|
512
|
+
ctypes.POINTER(ctypes.c_void_p),
|
|
513
|
+
ctypes.POINTER(ctypes.c_int)]
|
|
514
|
+
_xcrypt_crypt_ra.restype = ctypes.c_char_p
|
|
515
|
+
_xcrypt_crypt_ra_data = ctypes.c_void_p(0)
|
|
516
|
+
_xcrypt_crypt_ra_datasize = ctypes.c_int(0)
|
|
517
|
+
|
|
518
|
+
def xcrypt_crypt(phrase, setting):
|
|
519
|
+
global _xcrypt_crypt_ra_data, _xcrypt_crypt_ra_datasize
|
|
520
|
+
if not isinstance(phrase, bytes): phrase = phrase.encode("utf-8")
|
|
521
|
+
if not isinstance(setting, bytes): setting = setting.encode("ascii")
|
|
522
|
+
rv = _xcrypt_crypt_ra(phrase, setting,
|
|
523
|
+
ctypes.byref(_xcrypt_crypt_ra_data),
|
|
524
|
+
ctypes.byref(_xcrypt_crypt_ra_datasize))
|
|
525
|
+
if not rv:
|
|
526
|
+
err = ctypes.get_errno()
|
|
527
|
+
raise OSError(err, os.strerror(err))
|
|
528
|
+
return bytes(rv)
|
|
529
|
+
|
|
530
|
+
def h_sm3crypt(phrase, rounds, salt):
|
|
531
|
+
setting = "$sm3$rounds={r}${s}".format(r=rounds, s=salt)
|
|
532
|
+
yield (phrase, setting, xcrypt_crypt(phrase, setting))
|
|
533
|
+
|
|
534
|
+
def yescrypt_gensalt(ident, rounds, salt):
|
|
535
|
+
if rounds == 1:
|
|
536
|
+
params = "j75"
|
|
537
|
+
elif rounds == 2:
|
|
538
|
+
params = "j85"
|
|
539
|
+
else:
|
|
540
|
+
raise RuntimeError("don't know how to encode rounds={}"
|
|
541
|
+
.format(rounds))
|
|
542
|
+
|
|
543
|
+
return "${}${}${}".format(ident, params, salt)
|
|
544
|
+
|
|
545
|
+
def h_yescrypt(phrase, rounds, salt):
|
|
546
|
+
setting = yescrypt_gensalt("y", rounds, salt)
|
|
547
|
+
yield (phrase, setting, xcrypt_crypt(phrase, setting))
|
|
548
|
+
|
|
549
|
+
def h_gost_yescrypt(phrase, rounds, salt):
|
|
550
|
+
setting = yescrypt_gensalt("gy", rounds, salt)
|
|
551
|
+
yield (phrase, setting, xcrypt_crypt(phrase, setting))
|
|
552
|
+
|
|
553
|
+
def h_sm3_yescrypt(phrase, rounds, salt):
|
|
554
|
+
setting = yescrypt_gensalt("sm3y", rounds, salt)
|
|
555
|
+
yield (phrase, setting, xcrypt_crypt(phrase, setting))
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
# Each method should contribute a group of parameters to the array
|
|
559
|
+
# below. Each block has the form
|
|
560
|
+
#
|
|
561
|
+
# ('method', [
|
|
562
|
+
# (rounds, salt),
|
|
563
|
+
# (rounds, salt),
|
|
564
|
+
# ...
|
|
565
|
+
# ])
|
|
566
|
+
#
|
|
567
|
+
# where 'method' is the method name used in the INCLUDE_ macros,
|
|
568
|
+
# rounds is a number and salt is a salt string. The appropriate
|
|
569
|
+
# h_method function will be called with arguments (phrase, *params)
|
|
570
|
+
# where params is one of the tuples from its block, so it is OK to add
|
|
571
|
+
# extra arguments after the salt if necessary.
|
|
572
|
+
#
|
|
573
|
+
# If the method has a tunable rounds parameter, its array of
|
|
574
|
+
# (rounds, salt) pairs should have two salts * at least two values of
|
|
575
|
+
# the rounds parameter. If it does not, it should have two salts and
|
|
576
|
+
# use 0 for the rounds parameter.
|
|
577
|
+
#
|
|
578
|
+
# The point of this test is not to exercise brute force resistance,
|
|
579
|
+
# so keep cost parameters low.
|
|
580
|
+
#
|
|
581
|
+
# Methods should be in alphabetical order by their INCLUDE_macro name.
|
|
582
|
+
|
|
583
|
+
SETTINGS = [
|
|
584
|
+
('bcrypt', [
|
|
585
|
+
(5, 'CCCCCCCCCCCCCCCCCCCCC.'),
|
|
586
|
+
(5, 'abcdefghijklmnopqrstuu'),
|
|
587
|
+
(4, 'CCCCCCCCCCCCCCCCCCCCC.'),
|
|
588
|
+
(4, 'abcdefghijklmnopqrstuu'),
|
|
589
|
+
]),
|
|
590
|
+
|
|
591
|
+
('bcrypt_y', [
|
|
592
|
+
(5, 'CCCCCCCCCCCCCCCCCCCCC.'),
|
|
593
|
+
(5, 'abcdefghijklmnopqrstuu'),
|
|
594
|
+
(4, 'CCCCCCCCCCCCCCCCCCCCC.'),
|
|
595
|
+
(4, 'abcdefghijklmnopqrstuu'),
|
|
596
|
+
]),
|
|
597
|
+
|
|
598
|
+
('bcrypt_a', [
|
|
599
|
+
(5, 'CCCCCCCCCCCCCCCCCCCCC.'),
|
|
600
|
+
(5, 'abcdefghijklmnopqrstuu'),
|
|
601
|
+
(4, 'CCCCCCCCCCCCCCCCCCCCC.'),
|
|
602
|
+
(4, 'abcdefghijklmnopqrstuu'),
|
|
603
|
+
]),
|
|
604
|
+
|
|
605
|
+
('bcrypt_x', [
|
|
606
|
+
(5, 'CCCCCCCCCCCCCCCCCCCCC.'),
|
|
607
|
+
(5, 'abcdefghijklmnopqrstuu'),
|
|
608
|
+
(4, 'CCCCCCCCCCCCCCCCCCCCC.'),
|
|
609
|
+
(4, 'abcdefghijklmnopqrstuu'),
|
|
610
|
+
]),
|
|
611
|
+
|
|
612
|
+
('bigcrypt', [
|
|
613
|
+
(0, 'CC'),
|
|
614
|
+
(0, 'ab'),
|
|
615
|
+
]),
|
|
616
|
+
|
|
617
|
+
# The bsdicrypt round count is required to be odd.
|
|
618
|
+
('bsdicrypt', [
|
|
619
|
+
(1, 'CCCC'),
|
|
620
|
+
(1, 'abcd'),
|
|
621
|
+
(13, 'CCCC'),
|
|
622
|
+
(13, 'abcd'),
|
|
623
|
+
]),
|
|
624
|
+
|
|
625
|
+
('descrypt', [
|
|
626
|
+
(0, 'CC'),
|
|
627
|
+
(0, 'ab'),
|
|
628
|
+
]),
|
|
629
|
+
|
|
630
|
+
('gost_yescrypt', [
|
|
631
|
+
(1, '.......'),
|
|
632
|
+
(1, 'LdJMENpBABJJ3hIHjB1Bi.'),
|
|
633
|
+
(2, '.......'),
|
|
634
|
+
(2, 'LdJMENpBABJJ3hIHjB1Bi.'),
|
|
635
|
+
]),
|
|
636
|
+
|
|
637
|
+
('md5crypt', [
|
|
638
|
+
(0, 'CCCCCCCC'),
|
|
639
|
+
(0, 'abcdefgh'),
|
|
640
|
+
]),
|
|
641
|
+
|
|
642
|
+
('nt', [
|
|
643
|
+
(0, ''),
|
|
644
|
+
]),
|
|
645
|
+
|
|
646
|
+
('scrypt', [
|
|
647
|
+
(1, 'SodiumChloride'),
|
|
648
|
+
(1, 'unUNunUNunUNun'),
|
|
649
|
+
(2, 'SodiumChloride'),
|
|
650
|
+
(2, 'unUNunUNunUNun'),
|
|
651
|
+
]),
|
|
652
|
+
|
|
653
|
+
('sha1crypt', [
|
|
654
|
+
(12, 'GGXpNqoJvglVTkGU'),
|
|
655
|
+
(12, 'xSZGpk6Bp4SA3.cR'),
|
|
656
|
+
(456, 'GGXpNqoJvglVTkGU'),
|
|
657
|
+
(456, 'xSZGpk6Bp4SA3.cR'),
|
|
658
|
+
]),
|
|
659
|
+
|
|
660
|
+
('sha256crypt', [
|
|
661
|
+
(1000, 'saltstring'),
|
|
662
|
+
(1000, 'short'),
|
|
663
|
+
(5000, 'saltstring'),
|
|
664
|
+
(5000, 'short'),
|
|
665
|
+
]),
|
|
666
|
+
|
|
667
|
+
('sha512crypt', [
|
|
668
|
+
(1000, 'saltstring'),
|
|
669
|
+
(1000, 'short'),
|
|
670
|
+
(5000, 'saltstring'),
|
|
671
|
+
(5000, 'short'),
|
|
672
|
+
]),
|
|
673
|
+
|
|
674
|
+
('sm3crypt', [
|
|
675
|
+
(1000, 'saltstring'),
|
|
676
|
+
(1000, 'short'),
|
|
677
|
+
(5000, 'saltstring'),
|
|
678
|
+
(5000, 'short'),
|
|
679
|
+
]),
|
|
680
|
+
|
|
681
|
+
('sm3_yescrypt', [
|
|
682
|
+
(1, '.......'),
|
|
683
|
+
(1, 'LdJMENpBABJJ3hIHjB1Bi.'),
|
|
684
|
+
(2, '.......'),
|
|
685
|
+
(2, 'LdJMENpBABJJ3hIHjB1Bi.'),
|
|
686
|
+
]),
|
|
687
|
+
|
|
688
|
+
('sunmd5', [
|
|
689
|
+
(0, '9ZLwtuTO'),
|
|
690
|
+
(0, '1xMeE.at'),
|
|
691
|
+
(12, '9ZLwtuTO'),
|
|
692
|
+
(12, '1xMeE.at'),
|
|
693
|
+
]),
|
|
694
|
+
|
|
695
|
+
('yescrypt', [
|
|
696
|
+
(1, '.......'),
|
|
697
|
+
(1, 'LdJMENpBABJJ3hIHjB1Bi.'),
|
|
698
|
+
(2, '.......'),
|
|
699
|
+
(2, 'LdJMENpBABJJ3hIHjB1Bi.'),
|
|
700
|
+
]),
|
|
701
|
+
]
|
|
702
|
+
|
|
703
|
+
# Normally, we expect that (1) for fixed salt, no two phrases hash to
|
|
704
|
+
# the same string; (2) for fixed phrase, no two settings produce the
|
|
705
|
+
# same string. The known exceptions are all due to limitations and/or
|
|
706
|
+
# bugs in the hashing method. Check the table produced by this
|
|
707
|
+
# program to ensure that all of the collisions in the ->expected
|
|
708
|
+
# strings are due to one of the known exceptions. test-crypt-kat.c
|
|
709
|
+
# itself doesn't need to do this test; as long as all of the hashes
|
|
710
|
+
# produced by the just-built crypt() match the appropriate ->expected
|
|
711
|
+
# string, no new collisions can have been introduced.
|
|
712
|
+
|
|
713
|
+
def strneq_7bit (p1, p2, limit):
|
|
714
|
+
n1 = len(p1)
|
|
715
|
+
n2 = len(p2)
|
|
716
|
+
for i in range(limit):
|
|
717
|
+
if i == n1 and i == n2:
|
|
718
|
+
# strings are the same length, within the limit, and no
|
|
719
|
+
# mismatched characters were found
|
|
720
|
+
return True
|
|
721
|
+
if i == n1 or i == n2:
|
|
722
|
+
# one string is longer than the other, within the limit
|
|
723
|
+
return False
|
|
724
|
+
if (p1[i] & 0x7F) != (p2[i] & 0x7F):
|
|
725
|
+
# characters not equal, after masking the 8th bit
|
|
726
|
+
return False
|
|
727
|
+
# reached the limit, no mismatches found
|
|
728
|
+
return True
|
|
729
|
+
|
|
730
|
+
# The bug in bcrypt mode "x" (preserved from the original
|
|
731
|
+
# implementation of bcrypt) is, at its root, that the code below
|
|
732
|
+
# sign- rather than zero-extends *p before or-ing it into 'tmp'.
|
|
733
|
+
# When *p has its 8th bit set, it is therefore or-ed in as
|
|
734
|
+
# 0xFF_FF_FF_xx rather than 0x00_00_00_xx, and clobbers the other
|
|
735
|
+
# three bytes in 'tmp'. Depending on its position within the input,
|
|
736
|
+
# this can erase up to three other characters of the passphrase.
|
|
737
|
+
# The exact set of strings involved in any one group of collisions is
|
|
738
|
+
# difficult to describe in words and may depend on the endianness of
|
|
739
|
+
# the CPU. The test cases in this file have only been verified on
|
|
740
|
+
# a little-endian CPU.
|
|
741
|
+
BF_KEY_LEN = 18
|
|
742
|
+
|
|
743
|
+
def buggy_expand_BF_key(phrase):
|
|
744
|
+
p = 0
|
|
745
|
+
lp = len(phrase)
|
|
746
|
+
expanded = [0]*BF_KEY_LEN
|
|
747
|
+
if lp > 0:
|
|
748
|
+
for i in range(BF_KEY_LEN):
|
|
749
|
+
tmp = 0
|
|
750
|
+
for j in range(4):
|
|
751
|
+
if p == lp:
|
|
752
|
+
c = 0
|
|
753
|
+
else:
|
|
754
|
+
c = phrase[p]
|
|
755
|
+
stmp = ((c & 0x7F) - (c & 0x80)) & 0xFFFFFFFF
|
|
756
|
+
tmp = ((tmp << 8) | stmp) & 0xFFFFFFFF
|
|
757
|
+
p += 1
|
|
758
|
+
if p == lp + 1:
|
|
759
|
+
p = 0
|
|
760
|
+
expanded[i] = tmp
|
|
761
|
+
return expanded
|
|
762
|
+
|
|
763
|
+
def sign_extension_collision_p(p1, p2):
|
|
764
|
+
return buggy_expand_BF_key(p1) == buggy_expand_BF_key(p2)
|
|
765
|
+
|
|
766
|
+
def equivalent_sunmd5_settings_p(s1, s2):
|
|
767
|
+
if s1[:4] != "$md5": return False
|
|
768
|
+
if s2[:4] != "$md5": return False
|
|
769
|
+
|
|
770
|
+
l1 = len(s1)
|
|
771
|
+
l2 = len(s2)
|
|
772
|
+
if l1 < l2:
|
|
773
|
+
ll = l1
|
|
774
|
+
lh = l2
|
|
775
|
+
sl = s1
|
|
776
|
+
sh = s2
|
|
777
|
+
else:
|
|
778
|
+
ll = l2
|
|
779
|
+
lh = l1
|
|
780
|
+
sl = s2
|
|
781
|
+
sh = s1
|
|
782
|
+
if sl[:ll] != sh[:ll]:
|
|
783
|
+
return False
|
|
784
|
+
|
|
785
|
+
# The two cases where sunmd5 settings are equivalent:
|
|
786
|
+
# $md5...$ and $md5...$$
|
|
787
|
+
# $md5... and $md5...$x
|
|
788
|
+
if sl[ll-1] == '$':
|
|
789
|
+
if ll+1 != lh or sh[ll] != '$':
|
|
790
|
+
return False
|
|
791
|
+
else:
|
|
792
|
+
if ll+2 != lh or sh[ll] != '$' or sh[ll+1] != 'x':
|
|
793
|
+
return False
|
|
794
|
+
return True
|
|
795
|
+
|
|
796
|
+
def collision_expected(p1, p2, s1, s2):
|
|
797
|
+
if not isinstance(p1, bytes): p1 = p1.encode("iso_8859_1")
|
|
798
|
+
if not isinstance(p2, bytes): p2 = p2.encode("iso_8859_1")
|
|
799
|
+
if isinstance(s1, bytes): s1 = s1.decode("ascii")
|
|
800
|
+
if isinstance(s2, bytes): s2 = s2.decode("ascii")
|
|
801
|
+
# Under no circumstances should two hashes with different settings
|
|
802
|
+
# collide, except...
|
|
803
|
+
if s1 != s2:
|
|
804
|
+
# a descrypt hash can collide with a bigcrypt hash when the phrase
|
|
805
|
+
# input to bigcrypt was fewer than 8 characters long
|
|
806
|
+
if ( s1[0] != '$' and s1[0] != '_'
|
|
807
|
+
and s2[0] != '$' and s2[0] != '_'
|
|
808
|
+
and ( (len(s1) == 2 and len(s2) > 2 and len(p2) <= 8)
|
|
809
|
+
or (len(s2) == 2 and len(s1) > 2 and len(p1) <= 8))):
|
|
810
|
+
return strneq_7bit(p1, p2, 8)
|
|
811
|
+
|
|
812
|
+
# all settings for NTHASH are equivalent
|
|
813
|
+
if s1[:3] == '$3$' and s2[:3] == '$3$':
|
|
814
|
+
return p1 == p2
|
|
815
|
+
|
|
816
|
+
# sunmd5 has pairs of equivalent settings
|
|
817
|
+
if equivalent_sunmd5_settings_p (s1, s2):
|
|
818
|
+
return p1 == p2
|
|
819
|
+
|
|
820
|
+
return False
|
|
821
|
+
|
|
822
|
+
if s1[:2] == '$2':
|
|
823
|
+
# bcrypt truncates passphrases to 72 characters
|
|
824
|
+
if p1[:72] == p2[:72]:
|
|
825
|
+
return True
|
|
826
|
+
# preserved bcrypt $2x bug?
|
|
827
|
+
if s1[:3] == '$2x' and sign_extension_collision_p(p1, p2):
|
|
828
|
+
return True
|
|
829
|
+
return False
|
|
830
|
+
|
|
831
|
+
if s1[0] != '$' and s1[0] != '_':
|
|
832
|
+
if len(s1) == 2:
|
|
833
|
+
# descrypt truncates passphrases to 8 characters and strips the
|
|
834
|
+
# 8th bit
|
|
835
|
+
return strneq_7bit(p1, p2, 8)
|
|
836
|
+
else:
|
|
837
|
+
# bigcrypt truncates passphrases to 128 characters and strips the
|
|
838
|
+
# 8th bit
|
|
839
|
+
return strneq_7bit(p1, p2, 128)
|
|
840
|
+
|
|
841
|
+
if s1[0] == '_':
|
|
842
|
+
# bsdicrypt does not truncate but does still strip the 8th bit
|
|
843
|
+
return strneq_7bit(p1, p2, max(len(p1), len(p2)))
|
|
844
|
+
|
|
845
|
+
return False
|
|
846
|
+
|
|
847
|
+
def report_unexpected_collision(p1, p2, s1, s2, expected):
|
|
848
|
+
sys.stderr.write("UNEXPECTED HASH COLLISION:\n"
|
|
849
|
+
" hash = {}\n"
|
|
850
|
+
" p1 = {!r}\n"
|
|
851
|
+
" p2 = {!r}\n"
|
|
852
|
+
" s1 = {!r}\n"
|
|
853
|
+
" s2 = {!r}\n"
|
|
854
|
+
"\n".format(expected, p1, p2, s1, s2))
|
|
855
|
+
|
|
856
|
+
# Master control.
|
|
857
|
+
#
|
|
858
|
+
# To reduce the painful slowness of this program _somewhat_,
|
|
859
|
+
# we use a multiprocessing pool to compute all of the hashes.
|
|
860
|
+
|
|
861
|
+
def generate_phrase_setting_combs():
|
|
862
|
+
for macro_name, settings in SETTINGS:
|
|
863
|
+
for phrase in PHRASES:
|
|
864
|
+
for setting in settings:
|
|
865
|
+
yield (macro_name, phrase, setting)
|
|
866
|
+
|
|
867
|
+
def worker_compute_one(args):
|
|
868
|
+
method, phrase, setting = args
|
|
869
|
+
|
|
870
|
+
import __main__
|
|
871
|
+
sfunc = getattr(__main__, 'h_' + method)
|
|
872
|
+
return [(method, case) for case in sfunc(phrase, *setting)]
|
|
873
|
+
|
|
874
|
+
# Python specifies that an \x escape in a string literal consumes
|
|
875
|
+
# exactly two subsequent hexadecimal digits. C, on the other hand,
|
|
876
|
+
# specifies that \x in a string literal consumes *any number of*
|
|
877
|
+
# hexadecimal digits, and if the hexadecimal number is larger than the
|
|
878
|
+
# range representable by 'unsigned char' the result is
|
|
879
|
+
# implementation-defined. For instance, "\x303" == "03" in Python,
|
|
880
|
+
# but in C the string on the left could be anything. The simplest way
|
|
881
|
+
# to deal with this is to escape the string Python's way and then
|
|
882
|
+
# replace sequences like '\x303' with '\x30""3'.
|
|
883
|
+
c_hex_escape_fixup_re_ = re.compile(
|
|
884
|
+
r"(\\x[0-9a-fA-F]{2})([0-9a-fA-F])")
|
|
885
|
+
|
|
886
|
+
def c_hex_escape(s):
|
|
887
|
+
if isinstance(s, bytes):
|
|
888
|
+
s = s.decode("iso_8859_1")
|
|
889
|
+
s = s.encode("unicode_escape").decode("ascii")
|
|
890
|
+
|
|
891
|
+
return c_hex_escape_fixup_re_.sub(r'\1""\2', s)
|
|
892
|
+
|
|
893
|
+
def format_case(phrase, setting, expected):
|
|
894
|
+
return (' {{ "{}", "{}", "{}" }},\n'
|
|
895
|
+
.format(c_hex_escape(setting),
|
|
896
|
+
c_hex_escape(expected),
|
|
897
|
+
c_hex_escape(phrase)))
|
|
898
|
+
|
|
899
|
+
def main():
|
|
900
|
+
# FIXME: This only detects collisions that actually happen, not
|
|
901
|
+
# collisions that ought to have happened but didn't. (Detecting
|
|
902
|
+
# collisions that ought to have happened, but didn't, would be
|
|
903
|
+
# unavoidably quadratic in the total number of test cases, so I'm
|
|
904
|
+
# not sure it's worth it.)
|
|
905
|
+
items = []
|
|
906
|
+
collisions = {}
|
|
907
|
+
collision_error = False
|
|
908
|
+
with multiprocessing.Pool() as pool:
|
|
909
|
+
for group in pool.imap(worker_compute_one,
|
|
910
|
+
generate_phrase_setting_combs(),
|
|
911
|
+
chunksize=100):
|
|
912
|
+
for method, (phrase, setting, expected) in group:
|
|
913
|
+
if expected in collisions:
|
|
914
|
+
p1, s1 = collisions[expected]
|
|
915
|
+
if not collision_expected(p1, phrase, s1, setting):
|
|
916
|
+
report_unexpected_collision(p1, phrase, s1, setting,
|
|
917
|
+
expected)
|
|
918
|
+
collision_error = True
|
|
919
|
+
else:
|
|
920
|
+
collisions[expected] = (phrase, setting)
|
|
921
|
+
items.append((method, format_case(phrase, setting, expected)))
|
|
922
|
+
|
|
923
|
+
if collision_error:
|
|
924
|
+
sys.exit(1)
|
|
925
|
+
|
|
926
|
+
sys.stdout.write(
|
|
927
|
+
"/* Known-answer tests for passphrase hashes. -*- mode: c -*-\n"
|
|
928
|
+
" Automatically generated by ka-table-gen.py.\n"
|
|
929
|
+
" Do not edit this file by hand. */\n\n")
|
|
930
|
+
|
|
931
|
+
prev_method = None
|
|
932
|
+
for method, case in items:
|
|
933
|
+
if method != prev_method:
|
|
934
|
+
if prev_method is not None:
|
|
935
|
+
sys.stdout.write("#endif // {}\n\n".format(prev_method))
|
|
936
|
+
sys.stdout.write("#if INCLUDE_{} && defined TEST_{}\n"
|
|
937
|
+
.format(method, method))
|
|
938
|
+
prev_method = method
|
|
939
|
+
sys.stdout.write(case)
|
|
940
|
+
|
|
941
|
+
if prev_method is not None:
|
|
942
|
+
sys.stdout.write("#endif // {}\n".format(prev_method))
|
|
943
|
+
|
|
944
|
+
if __name__ == '__main__':
|
|
945
|
+
main()
|