summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authoroniko94 <onikolaiev94@outlook.com>2025-02-07 13:40:37 +0200
committeroniko94 <onikolaiev94@outlook.com>2025-03-18 14:31:33 +0200
commitb5b3e85f0bc8170b97d3e1af2383477c0854914d (patch)
tree42153c57740050bc7360b608b66c4f7ec6860c90 /python
parent95af91597c94856a38722daa5ea388646f9b735f (diff)
downloadvyos-1x-b5b3e85f0bc8170b97d3e1af2383477c0854914d.tar.gz
vyos-1x-b5b3e85f0bc8170b97d3e1af2383477c0854914d.zip
T6353: Add password strength check and user warning
Diffstat (limited to 'python')
-rw-r--r--python/vyos/utils/auth.py64
1 files changed, 64 insertions, 0 deletions
diff --git a/python/vyos/utils/auth.py b/python/vyos/utils/auth.py
index a0b3e1cae..a27d8a28a 100644
--- a/python/vyos/utils/auth.py
+++ b/python/vyos/utils/auth.py
@@ -13,10 +13,74 @@
# You should have received a copy of the GNU Lesser General Public License along with this library;
# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+import cracklib
+import math
import re
+import string
+from enum import StrEnum
+from decimal import Decimal
from vyos.utils.process import cmd
+
+DEFAULT_PASSWORD = 'vyos'
+LOW_ENTROPY_MSG = 'should be at least 8 characters long;'
+WEAK_PASSWORD_MSG= 'The password complexity is too low - @MSG@'
+
+
+class EPasswdStrength(StrEnum):
+ WEAK = 'Weak'
+ DECENT = 'Decent'
+ STRONG = 'Strong'
+
+
+def calculate_entropy(charset: str, passwd: str) -> float:
+ """
+ Calculate the entropy of a password based on the set of characters used
+ Uses E = log2(R**L) formula, where
+ - R is the range (length) of the character set
+ - L is the length of password
+ """
+ return math.log(math.pow(len(charset), len(passwd)), 2)
+
+def evaluate_strength(passwd: str) -> dict[str, str]:
+ """ Evaluates password strength and returns a check result dict """
+ charset = (cracklib.ASCII_UPPERCASE + cracklib.ASCII_LOWERCASE +
+ string.punctuation + string.digits)
+
+ result = {
+ 'strength': '',
+ 'error': '',
+ }
+
+ try:
+ cracklib.FascistCheck(passwd)
+ except ValueError as e:
+ # The password is vulnerable to dictionary attack no matter the entropy
+ if 'is' in str(e):
+ msg = str(e).replace('is', 'should not be')
+ else:
+ msg = f'should not be {e}'
+ result.update(strength=EPasswdStrength.WEAK)
+ result.update(error=WEAK_PASSWORD_MSG.replace('@MSG@', msg))
+ else:
+ # Now check the password's entropy
+ # Cast to Decimal for more precise rounding
+ entropy = Decimal.from_float(calculate_entropy(charset, passwd))
+
+ match round(entropy):
+ case e if e in range(0, 59):
+ result.update(strength=EPasswdStrength.WEAK)
+ result.update(
+ error=WEAK_PASSWORD_MSG.replace('@MSG@', LOW_ENTROPY_MSG)
+ )
+ case e if e in range(60, 119):
+ result.update(strength=EPasswdStrength.DECENT)
+ case e if e >= 120:
+ result.update(strength=EPasswdStrength.STRONG)
+
+ return result
+
def make_password_hash(password):
""" Makes a password hash for /etc/shadow using mkpasswd """