1"""passlib.tests.test_handlers_argon2 - tests for passlib hash algorithms"""
2#=============================================================================
3# imports
4#=============================================================================
5# core
6import logging
7log = logging.getLogger(__name__)
8import re
9import warnings
10# site
11# pkg
12from passlib import hash
13from passlib.utils.compat import unicode
14from passlib.tests.utils import HandlerCase, TEST_MODE
15from passlib.tests.test_handlers import UPASS_TABLE, PASS_TABLE_UTF8
16# module
17
18#=============================================================================
19# a bunch of tests lifted nearlky verbatim from official argon2 UTs...
20# https://github.com/P-H-C/phc-winner-argon2/blob/master/src/test.c
21#=============================================================================
22def hashtest(version, t, logM, p, secret, salt, hex_digest, hash):
23    return dict(version=version, rounds=t, logM=logM, memory_cost=1<<logM, parallelism=p,
24                secret=secret, salt=salt, hex_digest=hex_digest, hash=hash)
25
26# version 1.3 "I" tests
27version = 0x10
28reference_data = [
29    hashtest(version, 2, 16, 1, "password", "somesalt",
30             "f6c4db4a54e2a370627aff3db6176b94a2a209a62c8e36152711802f7b30c694",
31             "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ"
32             "$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"),
33    hashtest(version, 2, 20, 1, "password", "somesalt",
34             "9690ec55d28d3ed32562f2e73ea62b02b018757643a2ae6e79528459de8106e9",
35             "$argon2i$m=1048576,t=2,p=1$c29tZXNhbHQ"
36             "$lpDsVdKNPtMlYvLnPqYrArAYdXZDoq5ueVKEWd6BBuk"),
37    hashtest(version, 2, 18, 1, "password", "somesalt",
38             "3e689aaa3d28a77cf2bc72a51ac53166761751182f1ee292e3f677a7da4c2467",
39             "$argon2i$m=262144,t=2,p=1$c29tZXNhbHQ"
40             "$Pmiaqj0op3zyvHKlGsUxZnYXURgvHuKS4/Z3p9pMJGc"),
41    hashtest(version, 2, 8, 1, "password", "somesalt",
42             "fd4dd83d762c49bdeaf57c47bdcd0c2f1babf863fdeb490df63ede9975fccf06",
43             "$argon2i$m=256,t=2,p=1$c29tZXNhbHQ"
44             "$/U3YPXYsSb3q9XxHvc0MLxur+GP960kN9j7emXX8zwY"),
45    hashtest(version, 2, 8, 2, "password", "somesalt",
46             "b6c11560a6a9d61eac706b79a2f97d68b4463aa3ad87e00c07e2b01e90c564fb",
47             "$argon2i$m=256,t=2,p=2$c29tZXNhbHQ"
48             "$tsEVYKap1h6scGt5ovl9aLRGOqOth+AMB+KwHpDFZPs"),
49    hashtest(version, 1, 16, 1, "password", "somesalt",
50             "81630552b8f3b1f48cdb1992c4c678643d490b2b5eb4ff6c4b3438b5621724b2",
51             "$argon2i$m=65536,t=1,p=1$c29tZXNhbHQ"
52             "$gWMFUrjzsfSM2xmSxMZ4ZD1JCytetP9sSzQ4tWIXJLI"),
53    hashtest(version, 4, 16, 1, "password", "somesalt",
54             "f212f01615e6eb5d74734dc3ef40ade2d51d052468d8c69440a3a1f2c1c2847b",
55             "$argon2i$m=65536,t=4,p=1$c29tZXNhbHQ"
56             "$8hLwFhXm6110c03D70Ct4tUdBSRo2MaUQKOh8sHChHs"),
57    hashtest(version, 2, 16, 1, "differentpassword", "somesalt",
58             "e9c902074b6754531a3a0be519e5baf404b30ce69b3f01ac3bf21229960109a3",
59             "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ"
60             "$6ckCB0tnVFMaOgvlGeW69ASzDOabPwGsO/ISKZYBCaM"),
61    hashtest(version, 2, 16, 1, "password", "diffsalt",
62             "79a103b90fe8aef8570cb31fc8b22259778916f8336b7bdac3892569d4f1c497",
63             "$argon2i$m=65536,t=2,p=1$ZGlmZnNhbHQ"
64             "$eaEDuQ/orvhXDLMfyLIiWXeJFvgza3vaw4kladTxxJc"),
65]
66
67# version 1.9 "I" tests
68version = 0x13
69reference_data.extend([
70    hashtest(version, 2, 16, 1, "password", "somesalt",
71             "c1628832147d9720c5bd1cfd61367078729f6dfb6f8fea9ff98158e0d7816ed0",
72             "$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ"
73             "$wWKIMhR9lyDFvRz9YTZweHKfbftvj+qf+YFY4NeBbtA"),
74    hashtest(version, 2, 20, 1, "password", "somesalt",
75             "d1587aca0922c3b5d6a83edab31bee3c4ebaef342ed6127a55d19b2351ad1f41",
76             "$argon2i$v=19$m=1048576,t=2,p=1$c29tZXNhbHQ"
77             "$0Vh6ygkiw7XWqD7asxvuPE667zQu1hJ6VdGbI1GtH0E"),
78    hashtest(version, 2, 18, 1, "password", "somesalt",
79             "296dbae80b807cdceaad44ae741b506f14db0959267b183b118f9b24229bc7cb",
80             "$argon2i$v=19$m=262144,t=2,p=1$c29tZXNhbHQ"
81             "$KW266AuAfNzqrUSudBtQbxTbCVkmexg7EY+bJCKbx8s"),
82    hashtest(version, 2, 8, 1, "password", "somesalt",
83             "89e9029f4637b295beb027056a7336c414fadd43f6b208645281cb214a56452f",
84             "$argon2i$v=19$m=256,t=2,p=1$c29tZXNhbHQ"
85             "$iekCn0Y3spW+sCcFanM2xBT63UP2sghkUoHLIUpWRS8"),
86    hashtest(version, 2, 8, 2, "password", "somesalt",
87             "4ff5ce2769a1d7f4c8a491df09d41a9fbe90e5eb02155a13e4c01e20cd4eab61",
88             "$argon2i$v=19$m=256,t=2,p=2$c29tZXNhbHQ"
89             "$T/XOJ2mh1/TIpJHfCdQan76Q5esCFVoT5MAeIM1Oq2E"),
90    hashtest(version, 1, 16, 1, "password", "somesalt",
91             "d168075c4d985e13ebeae560cf8b94c3b5d8a16c51916b6f4ac2da3ac11bbecf",
92             "$argon2i$v=19$m=65536,t=1,p=1$c29tZXNhbHQ"
93             "$0WgHXE2YXhPr6uVgz4uUw7XYoWxRkWtvSsLaOsEbvs8"),
94    hashtest(version, 4, 16, 1, "password", "somesalt",
95             "aaa953d58af3706ce3df1aefd4a64a84e31d7f54175231f1285259f88174ce5b",
96             "$argon2i$v=19$m=65536,t=4,p=1$c29tZXNhbHQ"
97             "$qqlT1YrzcGzj3xrv1KZKhOMdf1QXUjHxKFJZ+IF0zls"),
98    hashtest(version, 2, 16, 1, "differentpassword", "somesalt",
99             "14ae8da01afea8700c2358dcef7c5358d9021282bd88663a4562f59fb74d22ee",
100             "$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ"
101             "$FK6NoBr+qHAMI1jc73xTWNkCEoK9iGY6RWL1n7dNIu4"),
102    hashtest(version, 2, 16, 1, "password", "diffsalt",
103             "b0357cccfbef91f3860b0dba447b2348cbefecadaf990abfe9cc40726c521271",
104             "$argon2i$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ"
105             "$sDV8zPvvkfOGCw26RHsjSMvv7K2vmQq/6cxAcmxSEnE"),
106])
107
108# version 1.9 "ID" tests
109version = 0x13
110reference_data.extend([
111    hashtest(version, 2, 16, 1, "password", "somesalt",
112             "09316115d5cf24ed5a15a31a3ba326e5cf32edc24702987c02b6566f61913cf7",
113             "$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ"
114             "$CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc"),
115    hashtest(version, 2, 18, 1, "password", "somesalt",
116             "78fe1ec91fb3aa5657d72e710854e4c3d9b9198c742f9616c2f085bed95b2e8c",
117             "$argon2id$v=19$m=262144,t=2,p=1$c29tZXNhbHQ"
118             "$eP4eyR+zqlZX1y5xCFTkw9m5GYx0L5YWwvCFvtlbLow"),
119    hashtest(version, 2, 8, 1, "password", "somesalt",
120             "9dfeb910e80bad0311fee20f9c0e2b12c17987b4cac90c2ef54d5b3021c68bfe",
121             "$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ"
122             "$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4"),
123    hashtest(version, 2, 8, 2, "password", "somesalt",
124             "6d093c501fd5999645e0ea3bf620d7b8be7fd2db59c20d9fff9539da2bf57037",
125             "$argon2id$v=19$m=256,t=2,p=2$c29tZXNhbHQ"
126             "$bQk8UB/VmZZF4Oo79iDXuL5/0ttZwg2f/5U52iv1cDc"),
127    hashtest(version, 1, 16, 1, "password", "somesalt",
128             "f6a5adc1ba723dddef9b5ac1d464e180fcd9dffc9d1cbf76cca2fed795d9ca98",
129             "$argon2id$v=19$m=65536,t=1,p=1$c29tZXNhbHQ"
130             "$9qWtwbpyPd3vm1rB1GThgPzZ3/ydHL92zKL+15XZypg"),
131    hashtest(version, 4, 16, 1, "password", "somesalt",
132             "9025d48e68ef7395cca9079da4c4ec3affb3c8911fe4f86d1a2520856f63172c",
133             "$argon2id$v=19$m=65536,t=4,p=1$c29tZXNhbHQ"
134             "$kCXUjmjvc5XMqQedpMTsOv+zyJEf5PhtGiUghW9jFyw"),
135    hashtest(version, 2, 16, 1, "differentpassword", "somesalt",
136             "0b84d652cf6b0c4beaef0dfe278ba6a80df6696281d7e0d2891b817d8c458fde",
137             "$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ"
138             "$C4TWUs9rDEvq7w3+J4umqA32aWKB1+DSiRuBfYxFj94"),
139    hashtest(version, 2, 16, 1, "password", "diffsalt",
140             "bdf32b05ccc42eb15d58fd19b1f856b113da1e9a5874fdcc544308565aa8141c",
141             "$argon2id$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ"
142             "$vfMrBczELrFdWP0ZsfhWsRPaHppYdP3MVEMIVlqoFBw"),
143])
144
145#=============================================================================
146# argon2
147#=============================================================================
148class _base_argon2_test(HandlerCase):
149    handler = hash.argon2
150
151    known_correct_hashes = [
152        #
153        # custom
154        #
155
156        # sample test
157        ("password", '$argon2i$v=19$m=256,t=1,p=1$c29tZXNhbHQ$AJFIsNZTMKTAewB4+ETN1A'),
158
159        # sample w/ all parameters different
160        ("password", '$argon2i$v=19$m=380,t=2,p=2$c29tZXNhbHQ$SrssP8n7m/12VWPM8dvNrw'),
161
162        # ensures utf-8 used for unicode
163        (UPASS_TABLE, '$argon2i$v=19$m=512,t=2,p=2$1sV0O4PWLtc12Ypv1f7oGw$'
164                      'z+yqzlKtrq3SaNfXDfIDnQ'),
165        (PASS_TABLE_UTF8, '$argon2i$v=19$m=512,t=2,p=2$1sV0O4PWLtc12Ypv1f7oGw$'
166                          'z+yqzlKtrq3SaNfXDfIDnQ'),
167
168        # ensure trailing null bytes handled correctly
169        ('password\x00', '$argon2i$v=19$m=512,t=2,p=2$c29tZXNhbHQ$Fb5+nPuLzZvtqKRwqUEtUQ'),
170
171        # sample with type D (generated via argon_cffi2.PasswordHasher)
172        ("password", '$argon2d$v=19$m=102400,t=2,p=8$g2RodLh8j8WbSdCp+lUy/A$zzAJqL/HSjm809PYQu6qkA'),
173
174        ]
175
176    known_malformed_hashes = [
177        # unknown hash type
178        "$argon2qq$v=19$t=2,p=4$c29tZXNhbHQAAAAAAAAAAA$QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY",
179
180        # missing 'm' param
181        "$argon2i$v=19$t=2,p=4$c29tZXNhbHQAAAAAAAAAAA$QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY",
182
183        # 't' param > max uint32
184        "$argon2i$v=19$m=65536,t=8589934592,p=4$c29tZXNhbHQAAAAAAAAAAA$QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY",
185
186        # unexpected param
187        "$argon2i$v=19$m=65536,t=2,p=4,q=5$c29tZXNhbHQAAAAAAAAAAA$QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY",
188
189        # wrong param order
190        "$argon2i$v=19$t=2,m=65536,p=4,q=5$c29tZXNhbHQAAAAAAAAAAA$QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY",
191
192        # constraint violation: m < 8 * p
193        "$argon2i$v=19$m=127,t=2,p=16$c29tZXNhbHQ$IMit9qkFULCMA/ViizL57cnTLOa5DiVM9eMwpAvPwr4",
194    ]
195
196    known_parsehash_results = [
197        ('$argon2i$v=19$m=256,t=2,p=3$c29tZXNhbHQ$AJFIsNZTMKTAewB4+ETN1A',
198         dict(type="i", memory_cost=256, rounds=2, parallelism=3, salt=b'somesalt',
199              checksum=b'\x00\x91H\xb0\xd6S0\xa4\xc0{\x00x\xf8D\xcd\xd4')),
200    ]
201
202    def setUpWarnings(self):
203        super(_base_argon2_test, self).setUpWarnings()
204        warnings.filterwarnings("ignore", ".*Using argon2pure backend.*")
205
206    def do_stub_encrypt(self, handler=None, **settings):
207        if self.backend == "argon2_cffi":
208            # overriding default since no way to get stub config from argon2._calc_hash()
209            # (otherwise test_21b_max_rounds blocks trying to do max rounds)
210            handler = (handler or self.handler).using(**settings)
211            self = handler(use_defaults=True)
212            self.checksum = self._stub_checksum
213            assert self.checksum
214            return self.to_string()
215        else:
216            return super(_base_argon2_test, self).do_stub_encrypt(handler, **settings)
217
218    def test_03_legacy_hash_workflow(self):
219        # override base method
220        raise self.skipTest("legacy 1.6 workflow not supported")
221
222    def test_keyid_parameter(self):
223        # NOTE: keyid parameter currently not supported by official argon2 hash parser,
224        #       even though it's mentioned in the format spec.
225        #       we're trying to be consistent w/ this, so hashes w/ keyid should
226        #       always through a NotImplementedError.
227        self.assertRaises(NotImplementedError, self.handler.verify, 'password',
228                          "$argon2i$v=19$m=65536,t=2,p=4,keyid=ABCD$c29tZXNhbHQ$"
229                          "IMit9qkFULCMA/ViizL57cnTLOa5DiVM9eMwpAvPwr4")
230
231    def test_data_parameter(self):
232        # NOTE: argon2 c library doesn't support passing in a data parameter to argon2_hash();
233        #       but argon2_verify() appears to parse that info... but then discards it (!?).
234        #       not sure what proper behavior is, filed issue -- https://github.com/P-H-C/phc-winner-argon2/issues/143
235        #       For now, replicating behavior we have for the two backends, to detect when things change.
236        handler = self.handler
237
238        # ref hash of 'password' when 'data' is correctly passed into argon2()
239        sample1 = '$argon2i$v=19$m=512,t=2,p=2,data=c29tZWRhdGE$c29tZXNhbHQ$KgHyCesFyyjkVkihZ5VNFw'
240
241        # ref hash of 'password' when 'data' is silently discarded (same digest as w/o data)
242        sample2 = '$argon2i$v=19$m=512,t=2,p=2,data=c29tZWRhdGE$c29tZXNhbHQ$uEeXt1dxN1iFKGhklseW4w'
243
244        # hash of 'password' w/o the data field
245        sample3 = '$argon2i$v=19$m=512,t=2,p=2$c29tZXNhbHQ$uEeXt1dxN1iFKGhklseW4w'
246
247        #
248        # test sample 1
249        #
250
251        if self.backend == "argon2_cffi":
252            # argon2_cffi v16.1 would incorrectly return False here.
253            # but v16.2 patches so it throws error on data parameter.
254            # our code should detect that, and adapt it into a NotImplementedError
255            self.assertRaises(NotImplementedError, handler.verify, "password", sample1)
256
257            # incorrectly returns sample3, dropping data parameter
258            self.assertEqual(handler.genhash("password", sample1), sample3)
259
260        else:
261            assert self.backend == "argon2pure"
262            # should parse and verify
263            self.assertTrue(handler.verify("password", sample1))
264
265            # should preserve sample1
266            self.assertEqual(handler.genhash("password", sample1), sample1)
267
268        #
269        # test sample 2
270        #
271
272        if self.backend == "argon2_cffi":
273            # argon2_cffi v16.1 would incorrectly return True here.
274            # but v16.2 patches so it throws error on data parameter.
275            # our code should detect that, and adapt it into a NotImplementedError
276            self.assertRaises(NotImplementedError, handler.verify,"password", sample2)
277
278            # incorrectly returns sample3, dropping data parameter
279            self.assertEqual(handler.genhash("password", sample1), sample3)
280
281        else:
282            assert self.backend == "argon2pure"
283            # should parse, but fail to verify
284            self.assertFalse(self.handler.verify("password", sample2))
285
286            # should return sample1 (corrected digest)
287            self.assertEqual(handler.genhash("password", sample2), sample1)
288
289    def test_keyid_and_data_parameters(self):
290        # test combination of the two, just in case
291        self.assertRaises(NotImplementedError, self.handler.verify, 'stub',
292                          "$argon2i$v=19$m=65536,t=2,p=4,keyid=ABCD,data=EFGH$c29tZXNhbHQ$"
293                          "IMit9qkFULCMA/ViizL57cnTLOa5DiVM9eMwpAvPwr4")
294
295    def test_type_kwd(self):
296        cls = self.handler
297
298        # XXX: this mirrors test_30_HasManyIdents();
299        #      maybe switch argon2 class to use that mixin instead of "type" kwd?
300
301        # check settings
302        self.assertTrue("type" in cls.setting_kwds)
303
304        # check supported type_values
305        for value in cls.type_values:
306            self.assertIsInstance(value, unicode)
307        self.assertTrue("i" in cls.type_values)
308        self.assertTrue("d" in cls.type_values)
309
310        # check default
311        self.assertTrue(cls.type in cls.type_values)
312
313        # check constructor validates ident correctly.
314        handler = cls
315        hash = self.get_sample_hash()[1]
316        kwds = handler.parsehash(hash)
317        del kwds['type']
318
319        # ... accepts good type
320        handler(type=cls.type, **kwds)
321
322        # XXX: this is policy "ident" uses, maybe switch to it?
323        # # ... requires type w/o defaults
324        # self.assertRaises(TypeError, handler, **kwds)
325        handler(**kwds)
326
327        # ... supplies default type
328        handler(use_defaults=True, **kwds)
329
330        # ... rejects bad type
331        self.assertRaises(ValueError, handler, type='xXx', **kwds)
332
333    def test_type_using(self):
334        handler = self.handler
335
336        # XXX: this mirrors test_has_many_idents_using();
337        #      maybe switch argon2 class to use that mixin instead of "type" kwd?
338
339        orig_type = handler.type
340        for alt_type in handler.type_values:
341            if alt_type != orig_type:
342                break
343        else:
344            raise AssertionError("expected to find alternate type: default=%r values=%r" %
345                                 (orig_type, handler.type_values))
346
347        def effective_type(cls):
348            return cls(use_defaults=True).type
349
350        # keep default if nothing else specified
351        subcls = handler.using()
352        self.assertEqual(subcls.type, orig_type)
353
354        # accepts alt type
355        subcls = handler.using(type=alt_type)
356        self.assertEqual(subcls.type, alt_type)
357        self.assertEqual(handler.type, orig_type)
358
359        # check subcls actually *generates* default type,
360        # and that we didn't affect orig handler
361        self.assertEqual(effective_type(subcls), alt_type)
362        self.assertEqual(effective_type(handler), orig_type)
363
364        # rejects bad type
365        self.assertRaises(ValueError, handler.using, type='xXx')
366
367        # honor 'type' alias
368        subcls = handler.using(type=alt_type)
369        self.assertEqual(subcls.type, alt_type)
370        self.assertEqual(handler.type, orig_type)
371
372        # check type aliases are being honored
373        self.assertEqual(effective_type(handler.using(type="I")), "i")
374
375    def test_needs_update_w_type(self):
376        handler = self.handler
377
378        hash = handler.hash("stub")
379        self.assertFalse(handler.needs_update(hash))
380
381        hash2 = re.sub(r"\$argon2\w+\$", "$argon2d$", hash)
382        self.assertTrue(handler.needs_update(hash2))
383
384    def test_needs_update_w_version(self):
385        handler = self.handler.using(memory_cost=65536, time_cost=2, parallelism=4,
386                                     digest_size=32)
387        hash = ("$argon2i$m=65536,t=2,p=4$c29tZXNhbHQAAAAAAAAAAA$"
388                "QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY")
389        if handler.max_version == 0x10:
390            self.assertFalse(handler.needs_update(hash))
391        else:
392            self.assertTrue(handler.needs_update(hash))
393
394    def test_argon_byte_encoding(self):
395        """verify we're using right base64 encoding for argon2"""
396        handler = self.handler
397        if handler.version != 0x13:
398            # TODO: make this fatal, and add refs for other version.
399            raise self.skipTest("handler uses wrong version for sample hashes")
400
401        # 8 byte salt
402        salt = b'somesalt'
403        temp = handler.using(memory_cost=256, time_cost=2, parallelism=2, salt=salt,
404                             checksum_size=32, type="i")
405        hash = temp.hash("password")
406        self.assertEqual(hash, "$argon2i$v=19$m=256,t=2,p=2"
407                               "$c29tZXNhbHQ"
408                               "$T/XOJ2mh1/TIpJHfCdQan76Q5esCFVoT5MAeIM1Oq2E")
409
410        # 16 byte salt
411        salt = b'somesalt\x00\x00\x00\x00\x00\x00\x00\x00'
412        temp = handler.using(memory_cost=256, time_cost=2, parallelism=2, salt=salt,
413                             checksum_size=32, type="i")
414        hash = temp.hash("password")
415        self.assertEqual(hash, "$argon2i$v=19$m=256,t=2,p=2"
416                               "$c29tZXNhbHQAAAAAAAAAAA"
417                               "$rqnbEp1/jFDUEKZZmw+z14amDsFqMDC53dIe57ZHD38")
418
419    class FuzzHashGenerator(HandlerCase.FuzzHashGenerator):
420
421        settings_map = HandlerCase.FuzzHashGenerator.settings_map.copy()
422        settings_map.update(memory_cost="random_memory_cost", type="random_type")
423
424        def random_type(self):
425            return self.rng.choice(self.handler.type_values)
426
427        def random_memory_cost(self):
428            if self.test.backend == "argon2pure":
429                return self.randintgauss(128, 384, 256, 128)
430            else:
431                return self.randintgauss(128, 32767, 16384, 4096)
432
433        # TODO: fuzz parallelism, digest_size
434
435#-----------------------------------------
436# test suites for specific backends
437#-----------------------------------------
438
439class argon2_argon2_cffi_test(_base_argon2_test.create_backend_case("argon2_cffi")):
440
441    # add some more test vectors that take too long under argon2pure
442    known_correct_hashes = _base_argon2_test.known_correct_hashes + [
443        #
444        # sample hashes from argon2 cffi package's unittests,
445        # which in turn were generated by official argon2 cmdline tool.
446        #
447
448        # v1.2, type I, w/o a version tag
449        ('password', "$argon2i$m=65536,t=2,p=4$c29tZXNhbHQAAAAAAAAAAA$"
450                     "QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY"),
451
452        # v1.3, type I
453        ('password', "$argon2i$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$"
454                     "IMit9qkFULCMA/ViizL57cnTLOa5DiVM9eMwpAvPwr4"),
455
456        # v1.3, type D
457        ('password', "$argon2d$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$"
458                     "cZn5d+rFh+ZfuRhm2iGUGgcrW5YLeM6q7L3vBsdmFA0"),
459
460        # v1.3, type ID
461        ('password', "$argon2id$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$"
462                     "GpZ3sK/oH9p7VIiV56G/64Zo/8GaUw434IimaPqxwCo"),
463
464        #
465        # custom
466        #
467
468        # ensure trailing null bytes handled correctly
469        ('password\x00', "$argon2i$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$"
470                         "Vpzuc0v0SrP88LcVvmg+z5RoOYpMDKH/lt6O+CZabIQ"),
471
472        ]
473
474    # add reference hashes from argon2 clib tests
475    known_correct_hashes.extend(
476        (info['secret'], info['hash']) for info in reference_data
477        if info['logM'] <= (18 if TEST_MODE("full") else 16)
478    )
479
480class argon2_argon2pure_test(_base_argon2_test.create_backend_case("argon2pure")):
481
482    # XXX: setting max_threads at 1 to prevent argon2pure from using multiprocessing,
483    #      which causes big problems when testing under pypy.
484    #      would like a "pure_use_threads" option instead, to make it use multiprocessing.dummy instead.
485    handler = hash.argon2.using(memory_cost=32, parallelism=2)
486
487    # don't use multiprocessing for unittests, makes it a lot harder to ctrl-c
488    # XXX: make this controlled by env var?
489    handler.pure_use_threads = True
490
491    # add reference hashes from argon2 clib tests
492    known_correct_hashes = _base_argon2_test.known_correct_hashes[:]
493
494    known_correct_hashes.extend(
495        (info['secret'], info['hash']) for info in reference_data
496        if info['logM'] < 16
497    )
498
499    class FuzzHashGenerator(_base_argon2_test.FuzzHashGenerator):
500
501        def random_rounds(self):
502            # decrease default rounds for fuzz testing to speed up volume.
503            return self.randintgauss(1, 3, 2, 1)
504
505#=============================================================================
506# eof
507#=============================================================================
508