Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 17 additions & 24 deletions js/script.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
// Password comuni - caricata dal file
let PASSWORD_COMUNI = [];

// Insieme dei caratteri speciali riconosciuti. Tenuto in parità con password_tester.py.
const SPECIAL_CHARS = '!"£%&/()=?^*°#@|$§{[}]~€&';

// Numero di simboli distinti: base di entropia per la classe speciale.
const SPECIAL_CHARSET_SIZE = new Set(SPECIAL_CHARS).size;

// Carica il file delle password comuni all'avvio
async function carica_password_comuni() {
try {
Expand Down Expand Up @@ -67,11 +73,11 @@ function there_is_a_number(psw) {
function there_is_a_special_caracter(psw) {

for (let caratteri of psw) {
if ('!"£%&/()=?^*°#@|$§{[}]~€&'.includes(caratteri)) {
if (SPECIAL_CHARS.includes(caratteri)) {
return true;
}
}

return false;
}

Expand All @@ -91,7 +97,7 @@ function count_special_caracters(psw) {
let count_special_caracters = 0;

for (let carattere of psw) {
if ('!"£%&/()=?^*°#@|$§{[}]~€&'.includes(carattere)) {
if (SPECIAL_CHARS.includes(carattere)) {
count_special_caracters += 1;
}
}
Expand Down Expand Up @@ -147,29 +153,16 @@ function time_to_crack(psw) {
let is_speciale_presente = there_is_a_special_caracter(psw);


if (is_num_presente && is_minuscola_presente && is_maiuscola_presente && is_speciale_presente) {
base = 94;
}
// Entropia additiva: ogni classe di caratteri presente allarga l'alfabeto.
if (is_minuscola_presente) base += 26;
if (is_maiuscola_presente) base += 26;
if (is_num_presente) base += 10;
if (is_speciale_presente) base += SPECIAL_CHARSET_SIZE;

else if (is_minuscola_presente && is_maiuscola_presente && is_num_presente) {
base = 62; // 26 (m) + 26 (M) + 10 (N)
}

else if (is_minuscola_presente && is_maiuscola_presente) {
base = 52; // 26 (m) + 26 (M)
// Alfabeto vuoto (es. solo spazi): trattata come banalmente debole.
if (base === 0) {
return "0 secondi";
}

else if (is_num_presente) {
base = 10;
}
else if (is_minuscola_presente || is_maiuscola_presente) {
base = 26;
}
else if (is_speciale_presente) {
base = 32;
}



let n_combinazini = Math.pow(base, psw.length);

Expand Down
185 changes: 99 additions & 86 deletions password_tester.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,34 @@
from rich import print
import math
from pathlib import Path

from rich import print

# Insieme dei caratteri speciali riconosciuti. Tenuto in parità con script.js.
SPECIAL_CHARS = '!"£%&/()=?^*°#@|$§{[}]~€&'

# Numero di simboli distinti: usato come "base" di entropia per la classe speciale.
SPECIAL_CHARSET_SIZE = len(set(SPECIAL_CHARS))


def _load_common_passwords():
"""Carica una sola volta le password comuni in un set.

Il file vive in assets/ accanto a questo script: costruiamo il percorso da
__file__ cosi' funziona anche se lanciato da un'altra cwd. Se manca, l'app
continua senza il controllo "password comune" invece di crashare.
"""
percorso = Path(__file__).parent / "assets" / "100k-most-used-passwords-NCSC.txt"
try:
with open(percorso, "r", encoding="utf-8") as file:
return {riga.strip().lower() for riga in file if riga.strip()}
except FileNotFoundError:
print("[yellow]⚠️ File password comuni non trovato: controllo disabilitato.[/yellow]")
return set()


# Caricato una volta sola all'avvio invece di rileggere il file ad ogni chiamata.
PASSWORD_COMUNI = _load_common_passwords()


def there_is_an_uppercase(psw):
for caratteri in psw:
Expand Down Expand Up @@ -29,39 +59,24 @@ def there_is_a_number(psw):

def there_is_a_special_caracter(psw):

for caratteri in psw:
if caratteri in "!""£%&/()=?^*°#@|$§{[}]~€&":
for caratteri in psw:
if caratteri in SPECIAL_CHARS:
return True

return False

def is_password_common(psw):

password_comuni = []

with open("./100k-most-used-passwords-NCSC.txt", "r") as file:

for riga in file.readlines():

temp= riga.strip().lower()

password_comuni.append(temp)

return False

if psw.lower() in password_comuni:
return True

def is_password_common(psw):

return False
return psw.lower() in PASSWORD_COMUNI


def count_special_caracters(psw):

count_special_caracters = 0

for carattere in psw:
if carattere in "!""£%&/()=?^*°#@|$§{[}]~€&":
count_special_caracters += 1
if carattere in SPECIAL_CHARS:
count_special_caracters += 1

if count_special_caracters == len(psw):
return True
Expand Down Expand Up @@ -105,62 +120,57 @@ def time_to_crack(psw):
is_speciale_presente = there_is_a_special_caracter(psw)


if is_num_presente and is_minuscola_presente and is_maiuscola_presente and is_speciale_presente:
base = 94

elif is_minuscola_presente and is_maiuscola_presente and is_num_presente:
base = 62 # 26 (m) + 26 (M) + 10 (N)

elif is_minuscola_presente and is_maiuscola_presente:
base = 52 # 26 (m) + 26 (M)

elif is_num_presente:
base = 10
elif is_minuscola_presente or is_maiuscola_presente:
base = 26
elif is_speciale_presente:
base = 32




n_combinazini = pow(base, len(psw))
# Entropia additiva: ogni classe di caratteri presente allarga l'alfabeto.
base = 0
if is_minuscola_presente:
base += 26
if is_maiuscola_presente:
base += 26
if is_num_presente:
base += 10
if is_speciale_presente:
base += SPECIAL_CHARSET_SIZE

# Alfabeto vuoto (es. password fatta di soli spazi): impossibile da forzare
# statisticamente, ma la trattiamo come banalmente debole.
if base == 0:
return "0 secondi"

# Velocità di attacco al secondo (quello che riesce a fare un pc buono oggi secondo gemini )
tentativi_al_secondo = 100000000000


secondi = n_combinazini / tentativi_al_secondo


minuti = secondi / 60
ore = minuti / 60
giorni = ore / 24
anni = giorni / 365
secoli = anni / 100
millenni = anni / 1000
milioni_anni = anni / 1000000
miliardi_anni = anni / 1000000000

# Restituiamo il tempo nel formato più appropriato
if miliardi_anni >= 1:
return f"{int(miliardi_anni)} miliardi di anni"
elif milioni_anni >= 1:
return f"{int(milioni_anni)} milioni di anni"
elif millenni >= 1:
return f"{int(millenni)} millenni"
elif secoli >= 1:
return f"{int(secoli)} secoli"
elif anni >= 1:
return f"{int(anni)} anni"
elif giorni >= 1:
return f"{int(giorni)} giorni"
elif ore >= 1:
return f"{int(ore)} ore"
elif minuti >= 1:
return f"{int(minuti)} minuti"
else:
return f"{int(secondi)} secondi"
# Lavoriamo in spazio logaritmico (base 10): pow(base, len) esploderebbe e
# genererebbe OverflowError su password lunghe. Non materializziamo mai
# l'intero gigante: confrontiamo e formattiamo solo i logaritmi.
combos_log = len(psw) * math.log10(base)
secondi_log = combos_log - math.log10(tentativi_al_secondo)

# (secondi in una unità, etichetta), dal più grande al più piccolo.
ANNO = 60 * 60 * 24 * 365
UNITA = [
(ANNO * 1_000_000_000, "miliardi di anni"),
(ANNO * 1_000_000, "milioni di anni"),
(ANNO * 1000, "millenni"),
(ANNO * 100, "secoli"),
(ANNO, "anni"),
(60 * 60 * 24, "giorni"),
(60 * 60, "ore"),
(60, "minuti"),
(1, "secondi"),
]

for unita_secondi, etichetta in UNITA:
if secondi_log >= math.log10(unita_secondi):
valore_log = secondi_log - math.log10(unita_secondi)
# Oltre ~1e15 il numero esatto non aggiunge informazione: lo
# mostriamo in notazione 10^k per evitare interi giganti/overflow.
if valore_log >= 15:
return f"oltre 10^{int(valore_log)} {etichetta}"
# valore = secondi_totali / unita_secondi, calcolato in spazio log
# cosi' l'intero combinatorio non viene mai materializzato.
return f"{int(10 ** valore_log)} {etichetta}"

return "0 secondi"



Expand Down Expand Up @@ -188,14 +198,18 @@ def main():
output2 = there_is_a_number(password)
output3 = there_is_a_special_caracter(password)
output4 = there_is_an_uppercase(password)
output5 = there_is_a_lowercase(password)


errori_riscontrati = []


if output4 == False:
errori_riscontrati.append("una lettera maiuscola")

if output5 == False:
errori_riscontrati.append("una lettera minuscola")

if output1 == False:
errori_riscontrati.append("lunghezza (minimo 8 caratteri)")

Expand Down Expand Up @@ -223,7 +237,7 @@ def main():

#Password sicura

if output1 and output2 and output3 and output4 and not password_is_common:
if output1 and output2 and output3 and output4 and output5 and not password_is_common:

print("\n\n✅ [green]Tecnicamente la tua password è forte![/green]\n\n")

Expand All @@ -249,16 +263,15 @@ def main():
main()


#loop nel main
#loop nel main

# Confronto di uguaglianza: "in 'Yy'" matchava qualsiasi stringa contenente y/Y
# (e anche "" che e' sottostringa di ogni stringa), causando un loop scorretto.
continuare = "y"

while continuare in "Yy":

continuare = input("\nVuoi controllare un'altra password? [y/N]: ")

if continuare == "":
continuare = "n"

if continuare in "Yy":
while continuare.strip().lower() == "y":

continuare = input("\nVuoi controllare un'altra password? [y/N]: ")

if continuare.strip().lower() == "y":
main()
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rich==14.3.1