You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					206 lines
				
				7.0 KiB
			
		
		
			
		
	
	
					206 lines
				
				7.0 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								"""passlib.tests -- tests for passlib.pwd"""
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# imports
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# core
							 | 
						||
| 
								 | 
							
								import itertools
							 | 
						||
| 
								 | 
							
								import logging; log = logging.getLogger(__name__)
							 | 
						||
| 
								 | 
							
								# site
							 | 
						||
| 
								 | 
							
								# pkg
							 | 
						||
| 
								 | 
							
								from passlib.tests.utils import TestCase
							 | 
						||
| 
								 | 
							
								# local
							 | 
						||
| 
								 | 
							
								__all__ = [
							 | 
						||
| 
								 | 
							
								    "UtilsTest",
							 | 
						||
| 
								 | 
							
								    "GenerateTest",
							 | 
						||
| 
								 | 
							
								    "StrengthTest",
							 | 
						||
| 
								 | 
							
								]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								class UtilsTest(TestCase):
							 | 
						||
| 
								 | 
							
								    """test internal utilities"""
							 | 
						||
| 
								 | 
							
								    descriptionPrefix = "passlib.pwd"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_self_info_rate(self):
							 | 
						||
| 
								 | 
							
								        """_self_info_rate()"""
							 | 
						||
| 
								 | 
							
								        from passlib.pwd import _self_info_rate
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertEqual(_self_info_rate(""), 0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertEqual(_self_info_rate("a" * 8), 0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertEqual(_self_info_rate("ab"), 1)
							 | 
						||
| 
								 | 
							
								        self.assertEqual(_self_info_rate("ab" * 8), 1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertEqual(_self_info_rate("abcd"), 2)
							 | 
						||
| 
								 | 
							
								        self.assertEqual(_self_info_rate("abcd" * 8), 2)
							 | 
						||
| 
								 | 
							
								        self.assertAlmostEqual(_self_info_rate("abcdaaaa"), 1.5488, places=4)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # def test_total_self_info(self):
							 | 
						||
| 
								 | 
							
								    #     """_total_self_info()"""
							 | 
						||
| 
								 | 
							
								    #     from passlib.pwd import _total_self_info
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    #     self.assertEqual(_total_self_info(""), 0)
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    #     self.assertEqual(_total_self_info("a" * 8), 0)
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    #     self.assertEqual(_total_self_info("ab"), 2)
							 | 
						||
| 
								 | 
							
								    #     self.assertEqual(_total_self_info("ab" * 8), 16)
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    #     self.assertEqual(_total_self_info("abcd"), 8)
							 | 
						||
| 
								 | 
							
								    #     self.assertEqual(_total_self_info("abcd" * 8), 64)
							 | 
						||
| 
								 | 
							
								    #     self.assertAlmostEqual(_total_self_info("abcdaaaa"), 12.3904, places=4)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# word generation
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# import subject
							 | 
						||
| 
								 | 
							
								from passlib.pwd import genword, default_charsets
							 | 
						||
| 
								 | 
							
								ascii_62 = default_charsets['ascii_62']
							 | 
						||
| 
								 | 
							
								hex = default_charsets['hex']
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class WordGeneratorTest(TestCase):
							 | 
						||
| 
								 | 
							
								    """test generation routines"""
							 | 
						||
| 
								 | 
							
								    descriptionPrefix = "passlib.pwd.genword()"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def setUp(self):
							 | 
						||
| 
								 | 
							
								        super(WordGeneratorTest, self).setUp()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # patch some RNG references so they're reproducible.
							 | 
						||
| 
								 | 
							
								        from passlib.pwd import SequenceGenerator
							 | 
						||
| 
								 | 
							
								        self.patchAttr(SequenceGenerator, "rng",
							 | 
						||
| 
								 | 
							
								                       self.getRandom("pwd generator"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def assertResultContents(self, results, count, chars, unique=True):
							 | 
						||
| 
								 | 
							
								        """check result list matches expected count & charset"""
							 | 
						||
| 
								 | 
							
								        self.assertEqual(len(results), count)
							 | 
						||
| 
								 | 
							
								        if unique:
							 | 
						||
| 
								 | 
							
								            if unique is True:
							 | 
						||
| 
								 | 
							
								                unique = count
							 | 
						||
| 
								 | 
							
								            self.assertEqual(len(set(results)), unique)
							 | 
						||
| 
								 | 
							
								        self.assertEqual(set("".join(results)), set(chars))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_general(self):
							 | 
						||
| 
								 | 
							
								        """general behavior"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # basic usage
							 | 
						||
| 
								 | 
							
								        result = genword()
							 | 
						||
| 
								 | 
							
								        self.assertEqual(len(result), 9)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # malformed keyword should have useful error.
							 | 
						||
| 
								 | 
							
								        self.assertRaisesRegex(TypeError, "(?i)unexpected keyword.*badkwd", genword, badkwd=True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_returns(self):
							 | 
						||
| 
								 | 
							
								        """'returns' keyword"""
							 | 
						||
| 
								 | 
							
								        # returns=int option
							 | 
						||
| 
								 | 
							
								        results = genword(returns=5000)
							 | 
						||
| 
								 | 
							
								        self.assertResultContents(results, 5000, ascii_62)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # returns=iter option
							 | 
						||
| 
								 | 
							
								        gen = genword(returns=iter)
							 | 
						||
| 
								 | 
							
								        results = [next(gen) for _ in range(5000)]
							 | 
						||
| 
								 | 
							
								        self.assertResultContents(results, 5000, ascii_62)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # invalid returns option
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, genword, returns='invalid-type')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_charset(self):
							 | 
						||
| 
								 | 
							
								        """'charset' & 'chars' options"""
							 | 
						||
| 
								 | 
							
								        # charset option
							 | 
						||
| 
								 | 
							
								        results = genword(charset="hex", returns=5000)
							 | 
						||
| 
								 | 
							
								        self.assertResultContents(results, 5000, hex)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # chars option
							 | 
						||
| 
								 | 
							
								        # there are 3**3=27 possible combinations
							 | 
						||
| 
								 | 
							
								        results = genword(length=3, chars="abc", returns=5000)
							 | 
						||
| 
								 | 
							
								        self.assertResultContents(results, 5000, "abc", unique=27)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # chars + charset
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, genword, chars='abc', charset='hex')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # TODO: test rng option
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# phrase generation
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# import subject
							 | 
						||
| 
								 | 
							
								from passlib.pwd import genphrase
							 | 
						||
| 
								 | 
							
								simple_words = ["alpha", "beta", "gamma"]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class PhraseGeneratorTest(TestCase):
							 | 
						||
| 
								 | 
							
								    """test generation routines"""
							 | 
						||
| 
								 | 
							
								    descriptionPrefix = "passlib.pwd.genphrase()"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def assertResultContents(self, results, count, words, unique=True, sep=" "):
							 | 
						||
| 
								 | 
							
								        """check result list matches expected count & charset"""
							 | 
						||
| 
								 | 
							
								        self.assertEqual(len(results), count)
							 | 
						||
| 
								 | 
							
								        if unique:
							 | 
						||
| 
								 | 
							
								            if unique is True:
							 | 
						||
| 
								 | 
							
								                unique = count
							 | 
						||
| 
								 | 
							
								            self.assertEqual(len(set(results)), unique)
							 | 
						||
| 
								 | 
							
								        out = set(itertools.chain.from_iterable(elem.split(sep) for elem in results))
							 | 
						||
| 
								 | 
							
								        self.assertEqual(out, set(words))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_general(self):
							 | 
						||
| 
								 | 
							
								        """general behavior"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # basic usage
							 | 
						||
| 
								 | 
							
								        result = genphrase()
							 | 
						||
| 
								 | 
							
								        self.assertEqual(len(result.split(" ")), 4)  # 48 / log(7776, 2) ~= 3.7 -> 4
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # malformed keyword should have useful error.
							 | 
						||
| 
								 | 
							
								        self.assertRaisesRegex(TypeError, "(?i)unexpected keyword.*badkwd", genphrase, badkwd=True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_entropy(self):
							 | 
						||
| 
								 | 
							
								        """'length' & 'entropy' keywords"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # custom entropy
							 | 
						||
| 
								 | 
							
								        result = genphrase(entropy=70)
							 | 
						||
| 
								 | 
							
								        self.assertEqual(len(result.split(" ")), 6)  # 70 / log(7776, 2) ~= 5.4 -> 6
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # custom length
							 | 
						||
| 
								 | 
							
								        result = genphrase(length=3)
							 | 
						||
| 
								 | 
							
								        self.assertEqual(len(result.split(" ")), 3)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # custom length < entropy
							 | 
						||
| 
								 | 
							
								        result = genphrase(length=3, entropy=48)
							 | 
						||
| 
								 | 
							
								        self.assertEqual(len(result.split(" ")), 4)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # custom length > entropy
							 | 
						||
| 
								 | 
							
								        result = genphrase(length=4, entropy=12)
							 | 
						||
| 
								 | 
							
								        self.assertEqual(len(result.split(" ")), 4)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_returns(self):
							 | 
						||
| 
								 | 
							
								        """'returns' keyword"""
							 | 
						||
| 
								 | 
							
								        # returns=int option
							 | 
						||
| 
								 | 
							
								        results = genphrase(returns=1000, words=simple_words)
							 | 
						||
| 
								 | 
							
								        self.assertResultContents(results, 1000, simple_words)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # returns=iter option
							 | 
						||
| 
								 | 
							
								        gen = genphrase(returns=iter, words=simple_words)
							 | 
						||
| 
								 | 
							
								        results = [next(gen) for _ in range(1000)]
							 | 
						||
| 
								 | 
							
								        self.assertResultContents(results, 1000, simple_words)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # invalid returns option
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, genphrase, returns='invalid-type')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_wordset(self):
							 | 
						||
| 
								 | 
							
								        """'wordset' & 'words' options"""
							 | 
						||
| 
								 | 
							
								        # wordset option
							 | 
						||
| 
								 | 
							
								        results = genphrase(words=simple_words, returns=5000)
							 | 
						||
| 
								 | 
							
								        self.assertResultContents(results, 5000, simple_words)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # words option
							 | 
						||
| 
								 | 
							
								        results = genphrase(length=3, words=simple_words, returns=5000)
							 | 
						||
| 
								 | 
							
								        self.assertResultContents(results, 5000, simple_words, unique=3**3)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # words + wordset
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, genphrase, words=simple_words, wordset='bip39')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# eof
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 |