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.
Files changed (199) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +106 -0
  3. data/ext/libxcrypt/AUTHORS +38 -0
  4. data/ext/libxcrypt/COPYING.LIB +502 -0
  5. data/ext/libxcrypt/ChangeLog +239 -0
  6. data/ext/libxcrypt/INSTALL +380 -0
  7. data/ext/libxcrypt/LICENSING +152 -0
  8. data/ext/libxcrypt/Makefile.am +704 -0
  9. data/ext/libxcrypt/Makefile.in +4110 -0
  10. data/ext/libxcrypt/NEWS +630 -0
  11. data/ext/libxcrypt/README +1 -0
  12. data/ext/libxcrypt/README.md +179 -0
  13. data/ext/libxcrypt/THANKS +13 -0
  14. data/ext/libxcrypt/TODO +1 -0
  15. data/ext/libxcrypt/TODO.md +100 -0
  16. data/ext/libxcrypt/aclocal.m4 +2617 -0
  17. data/ext/libxcrypt/autogen.sh +33 -0
  18. data/ext/libxcrypt/autom4te.cache/output.0 +19884 -0
  19. data/ext/libxcrypt/autom4te.cache/output.1 +19884 -0
  20. data/ext/libxcrypt/autom4te.cache/output.2 +19884 -0
  21. data/ext/libxcrypt/autom4te.cache/output.3 +19885 -0
  22. data/ext/libxcrypt/autom4te.cache/requests +714 -0
  23. data/ext/libxcrypt/autom4te.cache/traces.0 +4088 -0
  24. data/ext/libxcrypt/autom4te.cache/traces.1 +1060 -0
  25. data/ext/libxcrypt/autom4te.cache/traces.2 +4088 -0
  26. data/ext/libxcrypt/autom4te.cache/traces.3 +1060 -0
  27. data/ext/libxcrypt/build-aux/ci/ci-log-dependency-versions +79 -0
  28. data/ext/libxcrypt/build-aux/ci/ci-log-logfiles +22 -0
  29. data/ext/libxcrypt/build-aux/ci/clang-gcov-wrapper +2 -0
  30. data/ext/libxcrypt/build-aux/ci/configure-wrapper +10 -0
  31. data/ext/libxcrypt/build-aux/ci/summarize-coverage +24 -0
  32. data/ext/libxcrypt/build-aux/m4/ax_append_compile_flags.m4 +46 -0
  33. data/ext/libxcrypt/build-aux/m4/ax_append_flag.m4 +50 -0
  34. data/ext/libxcrypt/build-aux/m4/ax_check_compile_flag.m4 +53 -0
  35. data/ext/libxcrypt/build-aux/m4/ax_check_vscript.m4 +142 -0
  36. data/ext/libxcrypt/build-aux/m4/ax_gcc_func_attribute.m4 +246 -0
  37. data/ext/libxcrypt/build-aux/m4/ax_require_defined.m4 +37 -0
  38. data/ext/libxcrypt/build-aux/m4/ax_valgrind_check.m4 +239 -0
  39. data/ext/libxcrypt/build-aux/m4/libtool.m4 +8488 -0
  40. data/ext/libxcrypt/build-aux/m4/ltoptions.m4 +467 -0
  41. data/ext/libxcrypt/build-aux/m4/ltsugar.m4 +124 -0
  42. data/ext/libxcrypt/build-aux/m4/ltversion.m4 +24 -0
  43. data/ext/libxcrypt/build-aux/m4/lt~obsolete.m4 +99 -0
  44. data/ext/libxcrypt/build-aux/m4/pkg_compat.m4 +88 -0
  45. data/ext/libxcrypt/build-aux/m4/zw_alignment.m4 +90 -0
  46. data/ext/libxcrypt/build-aux/m4/zw_automodern.m4 +307 -0
  47. data/ext/libxcrypt/build-aux/m4/zw_detect_asan.m4 +24 -0
  48. data/ext/libxcrypt/build-aux/m4/zw_endianness.m4 +152 -0
  49. data/ext/libxcrypt/build-aux/m4/zw_ld_wrap.m4 +47 -0
  50. data/ext/libxcrypt/build-aux/m4/zw_prog_perl.m4 +40 -0
  51. data/ext/libxcrypt/build-aux/m4/zw_simple_warnings.m4 +150 -0
  52. data/ext/libxcrypt/build-aux/m4/zw_static_assert.m4 +68 -0
  53. data/ext/libxcrypt/build-aux/m4-autogen/compile +364 -0
  54. data/ext/libxcrypt/build-aux/m4-autogen/config.guess +1815 -0
  55. data/ext/libxcrypt/build-aux/m4-autogen/config.sub +2354 -0
  56. data/ext/libxcrypt/build-aux/m4-autogen/depcomp +792 -0
  57. data/ext/libxcrypt/build-aux/m4-autogen/install-sh +541 -0
  58. data/ext/libxcrypt/build-aux/m4-autogen/ltmain.sh +11524 -0
  59. data/ext/libxcrypt/build-aux/m4-autogen/missing +236 -0
  60. data/ext/libxcrypt/build-aux/m4-autogen/test-driver +160 -0
  61. data/ext/libxcrypt/build-aux/scripts/BuildCommon.pm +712 -0
  62. data/ext/libxcrypt/build-aux/scripts/check-perlcritic-config +76 -0
  63. data/ext/libxcrypt/build-aux/scripts/compute-symver-floor +116 -0
  64. data/ext/libxcrypt/build-aux/scripts/expand-selected-hashes +80 -0
  65. data/ext/libxcrypt/build-aux/scripts/gen-crypt-h +131 -0
  66. data/ext/libxcrypt/build-aux/scripts/gen-crypt-hashes-h +141 -0
  67. data/ext/libxcrypt/build-aux/scripts/gen-crypt-symbol-vers-h +150 -0
  68. data/ext/libxcrypt/build-aux/scripts/gen-libcrypt-map +67 -0
  69. data/ext/libxcrypt/build-aux/scripts/move-if-change +84 -0
  70. data/ext/libxcrypt/build-aux/scripts/skip-if-exec-format-error +78 -0
  71. data/ext/libxcrypt/codecov.yml +4 -0
  72. data/ext/libxcrypt/config.h.in +303 -0
  73. data/ext/libxcrypt/configure +19885 -0
  74. data/ext/libxcrypt/configure.ac +549 -0
  75. data/ext/libxcrypt/doc/crypt.3 +512 -0
  76. data/ext/libxcrypt/doc/crypt.5 +343 -0
  77. data/ext/libxcrypt/doc/crypt_checksalt.3 +106 -0
  78. data/ext/libxcrypt/doc/crypt_gensalt.3 +285 -0
  79. data/ext/libxcrypt/doc/crypt_gensalt_ra.3 +1 -0
  80. data/ext/libxcrypt/doc/crypt_gensalt_rn.3 +1 -0
  81. data/ext/libxcrypt/doc/crypt_preferred_method.3 +68 -0
  82. data/ext/libxcrypt/doc/crypt_r.3 +1 -0
  83. data/ext/libxcrypt/doc/crypt_ra.3 +1 -0
  84. data/ext/libxcrypt/doc/crypt_rn.3 +1 -0
  85. data/ext/libxcrypt/lib/alg-des-tables.c +3858 -0
  86. data/ext/libxcrypt/lib/alg-des.c +269 -0
  87. data/ext/libxcrypt/lib/alg-des.h +74 -0
  88. data/ext/libxcrypt/lib/alg-gost3411-2012-const.h +313 -0
  89. data/ext/libxcrypt/lib/alg-gost3411-2012-core.c +238 -0
  90. data/ext/libxcrypt/lib/alg-gost3411-2012-core.h +51 -0
  91. data/ext/libxcrypt/lib/alg-gost3411-2012-hmac.c +78 -0
  92. data/ext/libxcrypt/lib/alg-gost3411-2012-hmac.h +46 -0
  93. data/ext/libxcrypt/lib/alg-gost3411-2012-precalc.h +1426 -0
  94. data/ext/libxcrypt/lib/alg-gost3411-2012-ref.h +67 -0
  95. data/ext/libxcrypt/lib/alg-hmac-sha1.c +140 -0
  96. data/ext/libxcrypt/lib/alg-hmac-sha1.h +35 -0
  97. data/ext/libxcrypt/lib/alg-md4.c +270 -0
  98. data/ext/libxcrypt/lib/alg-md4.h +43 -0
  99. data/ext/libxcrypt/lib/alg-md5.c +291 -0
  100. data/ext/libxcrypt/lib/alg-md5.h +43 -0
  101. data/ext/libxcrypt/lib/alg-sha1.c +288 -0
  102. data/ext/libxcrypt/lib/alg-sha1.h +34 -0
  103. data/ext/libxcrypt/lib/alg-sha256.c +630 -0
  104. data/ext/libxcrypt/lib/alg-sha256.h +123 -0
  105. data/ext/libxcrypt/lib/alg-sha512.c +311 -0
  106. data/ext/libxcrypt/lib/alg-sha512.h +81 -0
  107. data/ext/libxcrypt/lib/alg-sm3-hmac.c +113 -0
  108. data/ext/libxcrypt/lib/alg-sm3-hmac.h +42 -0
  109. data/ext/libxcrypt/lib/alg-sm3.c +449 -0
  110. data/ext/libxcrypt/lib/alg-sm3.h +63 -0
  111. data/ext/libxcrypt/lib/alg-yescrypt-common.c +713 -0
  112. data/ext/libxcrypt/lib/alg-yescrypt-opt.c +1568 -0
  113. data/ext/libxcrypt/lib/alg-yescrypt-platform.c +106 -0
  114. data/ext/libxcrypt/lib/alg-yescrypt.h +360 -0
  115. data/ext/libxcrypt/lib/byteorder.h +164 -0
  116. data/ext/libxcrypt/lib/crypt-bcrypt.c +1061 -0
  117. data/ext/libxcrypt/lib/crypt-des-obsolete.c +215 -0
  118. data/ext/libxcrypt/lib/crypt-des.c +491 -0
  119. data/ext/libxcrypt/lib/crypt-gensalt-static.c +40 -0
  120. data/ext/libxcrypt/lib/crypt-gost-yescrypt.c +182 -0
  121. data/ext/libxcrypt/lib/crypt-md5.c +232 -0
  122. data/ext/libxcrypt/lib/crypt-nthash.c +134 -0
  123. data/ext/libxcrypt/lib/crypt-obsolete.h +40 -0
  124. data/ext/libxcrypt/lib/crypt-pbkdf1-sha1.c +260 -0
  125. data/ext/libxcrypt/lib/crypt-port.h +514 -0
  126. data/ext/libxcrypt/lib/crypt-scrypt.c +247 -0
  127. data/ext/libxcrypt/lib/crypt-sha256.c +308 -0
  128. data/ext/libxcrypt/lib/crypt-sha512.c +323 -0
  129. data/ext/libxcrypt/lib/crypt-sm3-yescrypt.c +189 -0
  130. data/ext/libxcrypt/lib/crypt-sm3.c +308 -0
  131. data/ext/libxcrypt/lib/crypt-static.c +44 -0
  132. data/ext/libxcrypt/lib/crypt-sunmd5.c +314 -0
  133. data/ext/libxcrypt/lib/crypt-yescrypt.c +177 -0
  134. data/ext/libxcrypt/lib/crypt.c +421 -0
  135. data/ext/libxcrypt/lib/crypt.h.in +249 -0
  136. data/ext/libxcrypt/lib/gen-des-tables.c +363 -0
  137. data/ext/libxcrypt/lib/hashes.conf +59 -0
  138. data/ext/libxcrypt/lib/libcrypt.map.in +48 -0
  139. data/ext/libxcrypt/lib/libcrypt.minver +97 -0
  140. data/ext/libxcrypt/lib/libxcrypt.pc.in +15 -0
  141. data/ext/libxcrypt/lib/util-base64.c +26 -0
  142. data/ext/libxcrypt/lib/util-gensalt-sha.c +88 -0
  143. data/ext/libxcrypt/lib/util-get-random-bytes.c +154 -0
  144. data/ext/libxcrypt/lib/util-make-failure-token.c +48 -0
  145. data/ext/libxcrypt/lib/util-xbzero.c +43 -0
  146. data/ext/libxcrypt/lib/util-xstrcpy.c +42 -0
  147. data/ext/libxcrypt/lib/xcrypt.h.in +58 -0
  148. data/ext/libxcrypt/libxcrypt.spec.rpkg +481 -0
  149. data/ext/libxcrypt/rpkg.conf +2 -0
  150. data/ext/libxcrypt/rpkg.macros +86 -0
  151. data/ext/libxcrypt/test/TestCommon.pm +326 -0
  152. data/ext/libxcrypt/test/alg-des.c +80 -0
  153. data/ext/libxcrypt/test/alg-gost3411-2012-hmac.c +90 -0
  154. data/ext/libxcrypt/test/alg-gost3411-2012.c +191 -0
  155. data/ext/libxcrypt/test/alg-hmac-sha1.c +187 -0
  156. data/ext/libxcrypt/test/alg-md4.c +111 -0
  157. data/ext/libxcrypt/test/alg-md5.c +134 -0
  158. data/ext/libxcrypt/test/alg-pbkdf-hmac-sha256.c +269 -0
  159. data/ext/libxcrypt/test/alg-sha1.c +111 -0
  160. data/ext/libxcrypt/test/alg-sha256.c +141 -0
  161. data/ext/libxcrypt/test/alg-sha512.c +170 -0
  162. data/ext/libxcrypt/test/alg-sm3-hmac.c +149 -0
  163. data/ext/libxcrypt/test/alg-sm3.c +168 -0
  164. data/ext/libxcrypt/test/alg-yescrypt.c +466 -0
  165. data/ext/libxcrypt/test/badsalt.c +726 -0
  166. data/ext/libxcrypt/test/badsetting.c +350 -0
  167. data/ext/libxcrypt/test/byteorder.c +254 -0
  168. data/ext/libxcrypt/test/checksalt.c +265 -0
  169. data/ext/libxcrypt/test/compile-strong-alias.c +43 -0
  170. data/ext/libxcrypt/test/crypt-badargs.c +392 -0
  171. data/ext/libxcrypt/test/crypt-gost-yescrypt.c +149 -0
  172. data/ext/libxcrypt/test/crypt-nested-call.c +180 -0
  173. data/ext/libxcrypt/test/crypt-sm3-yescrypt.c +149 -0
  174. data/ext/libxcrypt/test/crypt-too-long-phrase.c +157 -0
  175. data/ext/libxcrypt/test/des-cases.h +196 -0
  176. data/ext/libxcrypt/test/des-obsolete.c +206 -0
  177. data/ext/libxcrypt/test/des-obsolete_r.c +207 -0
  178. data/ext/libxcrypt/test/explicit-bzero.c +334 -0
  179. data/ext/libxcrypt/test/gensalt-bcrypt_x.c +54 -0
  180. data/ext/libxcrypt/test/gensalt-extradata.c +246 -0
  181. data/ext/libxcrypt/test/gensalt-nested-call.c +126 -0
  182. data/ext/libxcrypt/test/gensalt-nthash.c +65 -0
  183. data/ext/libxcrypt/test/gensalt.c +599 -0
  184. data/ext/libxcrypt/test/getrandom-fallbacks.c +295 -0
  185. data/ext/libxcrypt/test/getrandom-interface.c +211 -0
  186. data/ext/libxcrypt/test/ka-table-gen.py +945 -0
  187. data/ext/libxcrypt/test/ka-table.inc +5849 -0
  188. data/ext/libxcrypt/test/ka-tester.c +240 -0
  189. data/ext/libxcrypt/test/preferred-method.c +133 -0
  190. data/ext/libxcrypt/test/short-outbuf.c +119 -0
  191. data/ext/libxcrypt/test/special-char-salt.c +1160 -0
  192. data/ext/libxcrypt/test/symbols-compat.pl +137 -0
  193. data/ext/libxcrypt/test/symbols-renames.pl +107 -0
  194. data/ext/libxcrypt/test/symbols-static.pl +87 -0
  195. data/ext/xcrypt/xcrypt.c +9 -0
  196. data/lib/xcrypt/ffi.rb +76 -0
  197. data/lib/xcrypt/version.rb +5 -0
  198. data/lib/xcrypt.rb +89 -0
  199. 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()