{ "info": { "author": "Mark Vartanyan", "author_email": "kolypto@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "[![Build Status](https://api.travis-ci.org/kolypto/py-password-strength.png?branch=master)](https://travis-ci.org/kolypto/py-password-strength)\n[![Pythons](https://img.shields.io/badge/python-2.7%20%7C%203.4%E2%80%933.7%20%7C%20pypy-blue.svg)](.travis.yml)\n\n\nPassword Strength\n=================\n\nPassword strength and validation.\n\n\nTutorial\n========\n\n\n### Uppercase, Numbers, Special Characters\n\nYou test your passwords using the Policy object that controls what kind of password is acceptable in your system.\n\nFirst, create the Policy object and define the rules that apply to passwords in your system:\n\n```python\nfrom password_strength import PasswordPolicy\n\npolicy = PasswordPolicy.from_names(\n length=8, # min length: 8\n uppercase=2, # need min. 2 uppercase letters\n numbers=2, # need min. 2 digits\n special=2, # need min. 2 special characters\n nonletters=2, # need min. 2 non-letter characters (digits, specials, anything)\n)\n```\n\nNow, when you have the `PasswordPolicy` object, you can use it to test your passwords,\nand it will tell you which tests have failed:\n\n```python\npolicy.test('ABcd12!')\n# -> [Length(8), Special(2)]\n```\n\nThis tells us that 2 tests have failed: password is not long enough, and it does not have enough special characters.\nYou can use this information to tell the user what precisely is wrong with their password.\n\n```python\npolicy.test('ABcd12!@')\n# -> []\n```\n\nEmpty list tells us that this password is alright.\n\nThis test, however, enabled uses to use passwords that have a lot of repetition.\n\n### So-Called Entropy Bits\n\nHere's a test that's even better. You don't really need to define complex rules with special characters and stuff.\nAll you actually need is a password that's long enough, complex enough, and easy to remember\n(see [xkcd](https://xkcd.com/936/) and\n[Article: Everything We've Been Told About Passwords Is Wrong](https://www.inc.com/thomas-koulopoulos/all-that-advice-about-passwords-turns-out-to-be-to.html)).\n\nSo, instead of defining all these rules, let's just require the password to be complex enough.\nEntropy bits is something that defines how much variety does your password have. '01111010010011' is long enough,\nbut has only 2 entropy bits: that's how many bits you need to store its alphabet. However, a password that uses\nplenty of characters has more entropy.\n\n```python\npolicy = PasswordPolicy.from_names(\n entropybits=30 # need a password that has minimum 30 entropy bits (the power of its alphabet)\n)\n\nprint(policy.test('0123456789'))\n# -> []\n```\n\nThis password is not long enough, or secure enough, but has enough entropy: its vocabulary has 10 different characters.\nPut this test together with other requirements to make sure there's no repetition in your passwords.\n\n### Complexity\n\nEntropy bits are important, but difficult to understand. An even better, more intuitive test, is to require\nthe password to be \"complex enough\".\nComplexity is a number in the range of 0.00..0.99. Good, strong passwords start at 0.66.\n\nLet's first see how different passwords score:\n\n```python\nfrom password_strength import PasswordStats\n\nstats = PasswordStats('qwerty123')\nprint(stats.strength()) #-> Its strength is 0.316\n\nstats = PasswordStats('G00dPassw0rd?!')\nprint(stats.strength()) #-> Its strength is 0.585\n\nstats = PasswordStats('V3ryG00dPassw0rd?!')\nprint(stats.strength()) #-> Its strength is 0.767\n```\n\nSo, 0.66 will be a very good indication of a good password.\nLet's implement our policy:\n\n```python\npolicy = PasswordPolicy.from_names(\n strength=0.66 # need a password that scores at least 0.5 with its strength\n)\n\nprint(policy.test('V3ryG00dPassw0rd?!'))\n# -> [] -- empty list means a good password\n```\n\nOne good thing about using strength is that it allows users to use national aplhabets with passwords,\nwhich are most secure:\n\n```python\ntested_pass = policy.password('Mixed-\u6c49\u5821\u5305/\u6f22\u5821\u5305, \u6c49\u5821/\u6f22\u5821')\nprint(tested_pass.strength()) # -> 0.812 -- very good!\nprint(tested_pass.test())\n#-> [] - good password; it actually scored 0.812\n```\n\nNotice how in the last example we use a different approach: `policy.password()` analyzes the password, and then we can\nboth get its `.strength()`, and `.test()` it according to the current policy.\n\nPasswordPolicy\n==============\n\nPerform tests on a password.\n\nInit Policy\n-----------\n\n```python\nPasswordPolicy(*tests)\n```\n\nInit password policy with a list of tests\n\nAlternatively:\n\n```python\nPasswordPolicy.from_names(**tests)\n```\n\nInit password policy from a dictionary of test definitions.\n\nA test definition is simply:\n\n { test-name: argument } or { test-name: [arguments] }\n\nTest name is just a lowercased class name.\n\nExample:\n\n PasswordPolicy.from_names(\n length=8,\n strength=(0.33, 30),\n )\n\n\nBundled Tests\n-------------\n\nThese objects perform individual tests on a password, and report `True` of `False`.\n\n\n#### tests.EntropyBits(bits)\nTest whether the password has >= `bits` entropy bits.\n\nEntropy bits is the number of bits that is required to store the alphabet that's used in a password.\nIt's a measure of how long is the alphabet.\n\n#### tests.Length(length)\nTests whether password length >= `length`\n\n#### tests.NonLetters(count)\nTest whether the password has >= `count` non-letter characters\n\n#### tests.NonLettersLc(count)\nTest whether the password has >= `count` non-lowercase characters\n\n#### tests.Numbers(count)\nTest whether the password has >= `count` numeric characters\n\n#### tests.Special(count)\nTest whether the password has >= `count` special characters\n\n#### tests.Strength(strength, weak_bits=30)\nTest whether the password has >= `strength` strength.\n\nA password is evaluated to the strength of 0.333 when it has `weak_bits` entropy bits,\nwhich is considered to be a weak password. Strong passwords start at 0.666.\n\n#### tests.Uppercase(count)\nTest whether the password has >= `count` uppercase characters\n\n\nTesting\n-------\n\nAfter the `PasswordPolicy` is initialized, there are two methods to test:\n\n### PasswordPolicy.password\n```python\npassword(password)\n```\nGet password stats bound to the tests declared in this policy.\n\nIf in addition to tests you need to get statistics (e.g. strength) -- use this object to double calculations.\n\nSee [`PasswordStats`](#passwordstats) for more details.\n\n### PasswordPolicy.test\n```python\ntest(password)\n```\nPerform tests on a password.\n\nShortcut for: `PasswordPolicy.password(password).test()`.\n\n\nCustom Tests\n------------\n\nATest is a base class for password tests.\n\nTo create a custom test, just subclass it and implement the following methods:\n\n* __init__() that takes configuration arguments\n* test(ps) that tests a password, where `ps` is a `PasswordStats` object.\n\n\nPasswordStats\n-------------\n\nPasswordStats allows to calculate statistics on a password.\n\nIt considers a password as a unicode string, and all statistics are unicode-based.\n\nConstructor:\n\n```python\nfrom password_strength import PasswordStats\nPasswordStats(password)\n```\n\n\n#### PasswordStats.alphabet\nGet alphabet: set of used characters\n\n#### PasswordStats.alphabet_cardinality\nGet alphabet cardinality: alphabet length\n\n#### PasswordStats.char_categories\nCharacter count per top-level category\n\nThe following top-level categories are defined:\n\n- L: letter\n- M: Mark\n- N: Number\n- P: Punctuation\n- S: Symbol\n- Z: Separator\n- C: Other\n\n#### PasswordStats.char_categories_detailed\nCharacter count per unicode category, detailed format.\n\nSee: http://www.unicode.org/reports/tr44/#GC_Values_Table\n\n#### PasswordStats.combinations\nThe number of possible combinations with the current alphabet\n\n#### PasswordStats.count(*categories)\nCount characters of the specified classes only\n\n#### PasswordStats.count_except(*categories)\nCount characters of all classes except the specified ones\n\n#### PasswordStats.entropy_bits\nGet information entropy bits: log2 of the number of possible passwords\n\nhttps://en.wikipedia.org/wiki/Password_strength\n\n#### PasswordStats.entropy_density\nGet information entropy density factor, ranged {0 .. 1}.\n\nThis is ratio of entropy_bits() to max bits a password of this length could have.\nE.g. if all characters are unique -- then it's 1.0.\nIf half of the characters are reused once -- then it's 0.5.\n\n#### PasswordStats.length\nGet password length\n\n#### PasswordStats.letters\nCount all letters\n\n#### PasswordStats.letters_lowercase\nCount lowercase letters\n\n#### PasswordStats.letters_uppercase\nCount uppercase letters\n\n#### PasswordStats.numbers\nCount numbers\n\n#### PasswordStats.repeated_patterns_length\nDetect and return the length of repeated patterns.\n\nYou will probably be comparing it with the length of the password itself and ban if it's longer than 10%\n\n#### PasswordStats.sequences_length\nDetect and return the length of used sequences:\n\n- Alphabet letters: abcd...\n- Keyboard letters: qwerty, etc\n- Keyboard special characters in the top row: ~!@#$%^&*()_+\n- Numbers: 0123456\n\n#### PasswordStats.special_characters\nCount special characters\n\nSpecial characters is everything that's not a letter or a number\n\n#### PasswordStats.strength(weak_bits=30)\nGet password strength as a number normalized to range {0 .. 1}.\n\nNormalization is done in the following fashion:\n\n1. If entropy_bits <= weak_bits -- linear in range{0.0 .. 0.33} (weak)\n2. If entropy_bits <= weak_bits*2 -- almost linear in range{0.33 .. 0.66} (medium)\n3. If entropy_bits > weak_bits*3 -- asymptotic towards 1.0 (strong)\n\n#### PasswordStats.test(tests)\nTest the password against a list of tests\n\n#### PasswordStats.weakness_factor\nGet weakness factor as a float in range {0 .. 1}\n\nThis detects the portion of the string that contains:\n* repeated patterns\n* sequences\n\nE.g. a value of 1.0 means the whole string is weak, and 0.5 means half of the string is weak.\n\nTypical usage:\n\npassword_strength = (1 - weakness_factor) * strength\n\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/kolypto/py-password-strength", "keywords": "password,strength,policy,security", "license": "BSD", "maintainer": "", "maintainer_email": "", "name": "password-strength", "package_url": "https://pypi.org/project/password-strength/", "platform": "any", "project_url": "https://pypi.org/project/password-strength/", "project_urls": { "Homepage": "https://github.com/kolypto/py-password-strength" }, "release_url": "https://pypi.org/project/password-strength/0.0.3.post2/", "requires_dist": [ "six" ], "requires_python": "", "summary": "Password strength and validation", "version": "0.0.3.post2" }, "last_serial": 4660247, "releases": { "0.0.1-0": [ { "comment_text": "built for Linux-3.13.0-32-generic-x86_64-with-glibc2.4", "digests": { "md5": "803f8769757e4c27c159b006910aa4bd", "sha256": "24ea406b401e20e4baacc02716f532382168e3e61920f8367150bb5cab62ab61" }, "downloads": -1, "filename": "password_strength-0.0.1-0.linux-x86_64.tar.gz", "has_sig": false, "md5_digest": "803f8769757e4c27c159b006910aa4bd", "packagetype": "bdist_dumb", "python_version": "any", "requires_python": null, "size": 11706, "upload_time": "2014-08-05T12:23:58", "url": "https://files.pythonhosted.org/packages/84/ab/163faf165b51807f88f0dcc162076cb95d3e2a8c4fad7517d38892b26a24/password_strength-0.0.1-0.linux-x86_64.tar.gz" }, { "comment_text": "", "digests": { "md5": "ad99b51fb4fc4e7ec02e139dc23397be", "sha256": "e98b3102792a0a7582623141749263f053175ccb3baed14596424885970a3d7b" }, "downloads": -1, "filename": "password_strength-0.0.1-0.tar.gz", "has_sig": false, "md5_digest": "ad99b51fb4fc4e7ec02e139dc23397be", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8969, "upload_time": "2014-08-05T12:23:55", "url": "https://files.pythonhosted.org/packages/86/88/90547ef6d126a47f39472457fca214871ec6e304f97eb8a3d9fda1c6ed39/password_strength-0.0.1-0.tar.gz" } ], "0.0.1-1": [ { "comment_text": "", "digests": { "md5": "3e3aa10be9c3a2fe315330422de8ea83", "sha256": "65e08f6fb0824fcb4c99cecdab37f52886ae65f4fd05107ea3180853cd22efad" }, "downloads": -1, "filename": "password_strength-0.0.1_1-py2-none-any.whl", "has_sig": false, "md5_digest": "3e3aa10be9c3a2fe315330422de8ea83", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 12053, "upload_time": "2014-08-14T15:45:44", "url": "https://files.pythonhosted.org/packages/e2/2a/95ea6a146467d376b7cf6810b6978653c9c589443c73bd4b0904c7215fdf/password_strength-0.0.1_1-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "0c845b3ffd2c30c9a5c38719f45a0b68", "sha256": "3ea5a483dd31a10d233b6ed9571aa9c30c34f64996db783f2e9496c38310242f" }, "downloads": -1, "filename": "password_strength-0.0.1-1.tar.gz", "has_sig": false, "md5_digest": "0c845b3ffd2c30c9a5c38719f45a0b68", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8987, "upload_time": "2014-08-14T15:45:37", "url": "https://files.pythonhosted.org/packages/8c/59/f01949795b6119728bd6e9d61a004d73e8b4342db84e0ceaf2635b2f057f/password_strength-0.0.1-1.tar.gz" } ], "0.0.2-0": [ { "comment_text": "", "digests": { "md5": "480fc884064ae004cc708b3c342d1fdc", "sha256": "ac801b56138fbf225b335758356a0d20cb4e6bd8368f2a52430f2069f72bec55" }, "downloads": -1, "filename": "password_strength-0.0.2_0-py2-none-any.whl", "has_sig": false, "md5_digest": "480fc884064ae004cc708b3c342d1fdc", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 12759, "upload_time": "2014-08-14T16:38:50", "url": "https://files.pythonhosted.org/packages/d5/e3/193b3626b40802d369afb42ec409dfb574d8a26798b96fdb9a48f67f13fb/password_strength-0.0.2_0-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "97ae5aae09d1510704a48d5ae291d347", "sha256": "a78a402d85fbdaf506570f7eacb3ad60105cf4125f729747e71bfbf6795826bd" }, "downloads": -1, "filename": "password_strength-0.0.2-0.tar.gz", "has_sig": false, "md5_digest": "97ae5aae09d1510704a48d5ae291d347", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9645, "upload_time": "2014-08-14T16:38:46", "url": "https://files.pythonhosted.org/packages/b3/36/13d8343a4209c77bd433b139a548ce565632866da7fd39f892f8def2c9d7/password_strength-0.0.2-0.tar.gz" } ], "0.0.3.post0": [ { "comment_text": "", "digests": { "md5": "3f48b65ace3a1118e14c6a01900431cb", "sha256": "819d7e1d93b743c121301ae395519a8157e2e8d0ac7da398e97ceb69eda1075a" }, "downloads": -1, "filename": "password_strength-0.0.3.post0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "3f48b65ace3a1118e14c6a01900431cb", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 10529, "upload_time": "2019-01-04T01:51:59", "url": "https://files.pythonhosted.org/packages/b2/38/54beff8aadf9b3d5eb6565b8b9667d2ea3ca552fe4463c67dd8de81af702/password_strength-0.0.3.post0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "09009e7e11374a0494652c99aa9419fa", "sha256": "d6a281f1007f10b92b27b517796051208bc2e79568d87a496e55943d2e684d05" }, "downloads": -1, "filename": "password_strength-0.0.3.post0.tar.gz", "has_sig": false, "md5_digest": "09009e7e11374a0494652c99aa9419fa", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9421, "upload_time": "2019-01-04T01:52:01", "url": "https://files.pythonhosted.org/packages/ba/a0/4191179ba4d23fcca2585be6801bbaa183b33ec69b18d8dfb49b98fc2f1c/password_strength-0.0.3.post0.tar.gz" } ], "0.0.3.post1": [ { "comment_text": "", "digests": { "md5": "86cae8a985a300aeabc0fdeaf66e4781", "sha256": "28acd376ff6324064a548965179ce543620da57bbe0ca69e6147155d3d169b0f" }, "downloads": -1, "filename": "password_strength-0.0.3.post1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "86cae8a985a300aeabc0fdeaf66e4781", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 12179, "upload_time": "2019-01-04T13:35:56", "url": "https://files.pythonhosted.org/packages/5e/5a/851fb4aa5685ce572eb194c47b9cc80b97bee7253618df64508024f98f9b/password_strength-0.0.3.post1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "d422f6e492c8ae271fe3e33d90926eb2", "sha256": "dd13f3772e9deba7dcb5d27843e9ee8038b788ebd1384238a4c459beef76f89e" }, "downloads": -1, "filename": "password_strength-0.0.3.post1.tar.gz", "has_sig": false, "md5_digest": "d422f6e492c8ae271fe3e33d90926eb2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12865, "upload_time": "2019-01-04T13:35:58", "url": "https://files.pythonhosted.org/packages/92/4f/6d467b22549915ed1c276f57d1a1fc03aa73d16af4be0733be63e32c146f/password_strength-0.0.3.post1.tar.gz" } ], "0.0.3.post2": [ { "comment_text": "", "digests": { "md5": "a263f73a27448b421e2f61b501dbf1d3", "sha256": "6739357c2863d707b7c7f247ff7c6882a70904a18d12c9aaf98f8b95da176fb9" }, "downloads": -1, "filename": "password_strength-0.0.3.post2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "a263f73a27448b421e2f61b501dbf1d3", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 12167, "upload_time": "2019-01-04T13:41:27", "url": "https://files.pythonhosted.org/packages/1c/d6/08fd888c980589e4e27c2a4177e972481e8881600138e63afb785fe52630/password_strength-0.0.3.post2-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "cb3ccecfc2682d3a6abe7f2d9a1da192", "sha256": "bf4df10a58fcd3abfa182367307b4fd7b1cec518121dd83bf80c1c42ba796762" }, "downloads": -1, "filename": "password_strength-0.0.3.post2.tar.gz", "has_sig": false, "md5_digest": "cb3ccecfc2682d3a6abe7f2d9a1da192", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12857, "upload_time": "2019-01-04T13:41:29", "url": "https://files.pythonhosted.org/packages/db/f1/6165ebcca27fca3f1d63f8c3a45805c2ed8568be4d09219a2aa45e792c14/password_strength-0.0.3.post2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "a263f73a27448b421e2f61b501dbf1d3", "sha256": "6739357c2863d707b7c7f247ff7c6882a70904a18d12c9aaf98f8b95da176fb9" }, "downloads": -1, "filename": "password_strength-0.0.3.post2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "a263f73a27448b421e2f61b501dbf1d3", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 12167, "upload_time": "2019-01-04T13:41:27", "url": "https://files.pythonhosted.org/packages/1c/d6/08fd888c980589e4e27c2a4177e972481e8881600138e63afb785fe52630/password_strength-0.0.3.post2-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "cb3ccecfc2682d3a6abe7f2d9a1da192", "sha256": "bf4df10a58fcd3abfa182367307b4fd7b1cec518121dd83bf80c1c42ba796762" }, "downloads": -1, "filename": "password_strength-0.0.3.post2.tar.gz", "has_sig": false, "md5_digest": "cb3ccecfc2682d3a6abe7f2d9a1da192", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12857, "upload_time": "2019-01-04T13:41:29", "url": "https://files.pythonhosted.org/packages/db/f1/6165ebcca27fca3f1d63f8c3a45805c2ed8568be4d09219a2aa45e792c14/password_strength-0.0.3.post2.tar.gz" } ] }