O algoritmo (CHAVE_INTERNA + funcoes gerarReativacao/gerarInstalacao) ficava 100% legivel no codigo-fonte da pagina — qualquer pessoa com acesso ao painel conseguia abrir o DevTools e extrair a logica pra gerar chaves de licenca fora do sistema. Move tudo pra app/gerador_chave.py (roda so no servidor) e expoe via POST /gerador-chave/gerar (autenticado, protegido por CSRF). A pagina agora so manda os dados do formulario e mostra o resultado que volta do backend. Algoritmo verificado byte-a-byte contra os 3 exemplos reais conhecidos (cliente 000001, ALUGUEL). Bonus: o byte inicial agora usa secrets.randbelow (CSPRNG) em vez de Math.random() do JS. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
57 lines
2.4 KiB
Python
57 lines
2.4 KiB
Python
"""
|
|
Algoritmo de geração de chave (reativação e instalação), reverso-projetado
|
|
do sistema legado (Delphi). Fica só aqui no backend — NUNCA deve voltar a
|
|
ser exposto em JavaScript de página, porque isso permite qualquer pessoa
|
|
com acesso ao painel extrair o algoritmo e gerar chaves fora do sistema.
|
|
|
|
Verificado byte-a-byte contra 3 exemplos reais de chave de instalação
|
|
(cliente 000001, ALUGUEL): byte inicial 0xDC -> "DC45092C",
|
|
0x2E -> "2E35083D", 0x3D -> "3D25090C" (ver testes).
|
|
"""
|
|
import secrets
|
|
|
|
CHAVE_INTERNA = (
|
|
"YUQL23KL23DF90WI5E1JAS467NMCXXL6JAOAUWWMCL0AOMM4A4VZYW9KHJUI2347"
|
|
"EJHJKDF3424SKL K3LAKDJSL9RTIKJ"
|
|
)
|
|
|
|
|
|
def _cifrar(texto: str, byte_inicial: int) -> list[int]:
|
|
"""Cifra por feedback de byte anterior + XOR com CHAVE_INTERNA rotativa.
|
|
Retorna a lista de bytes cifrados (sem o byte_inicial na frente)."""
|
|
anterior = byte_inicial
|
|
cifrados = []
|
|
for i, ch in enumerate(texto):
|
|
valor = (ord(ch) + anterior) % 255
|
|
cifrado = valor ^ ord(CHAVE_INTERNA[i % len(CHAVE_INTERNA)])
|
|
cifrados.append(cifrado)
|
|
anterior = cifrado
|
|
return cifrados
|
|
|
|
|
|
def gerar_reativacao(codigo_cliente: str, mes: int, ano: int, byte_inicial: int | None = None) -> str:
|
|
"""Chave de reativação — mês/cliente/ano cifrados por inteiro (22 caracteres hex)."""
|
|
cliente = str(codigo_cliente).zfill(6)
|
|
texto = str(mes).zfill(2) + cliente + str(ano)[-2:]
|
|
aleatorio = byte_inicial if byte_inicial is not None else secrets.randbelow(256)
|
|
cifrados = _cifrar(texto, aleatorio)
|
|
return "".join(f"{v:02X}" for v in [aleatorio, *cifrados])
|
|
|
|
|
|
def gerar_instalacao(codigo_cliente: str, modalidade: str, byte_inicial: int | None = None) -> str:
|
|
"""Chave de instalação — só a modalidade ("30"=ALUGUEL / "00"=COMPRA) é
|
|
cifrada; o valor derivado do código do cliente (cliente*19+31) entra sem
|
|
cifrar, no meio dos 6 caracteres cifrados (3 antes + 3 depois)."""
|
|
try:
|
|
cliente_num = int(str(codigo_cliente))
|
|
except ValueError:
|
|
cliente_num = 0
|
|
valor_cliente = cliente_num * 19 + 31
|
|
texto_modalidade = "30" if modalidade == "ALUGUEL" else "00"
|
|
|
|
aleatorio = byte_inicial if byte_inicial is not None else secrets.randbelow(256)
|
|
cifrados = _cifrar(texto_modalidade, aleatorio)
|
|
criptografado = "".join(f"{v:02X}" for v in [aleatorio, *cifrados])
|
|
parte1, parte2 = criptografado[0:3], criptografado[3:6]
|
|
return f"{parte1}{valor_cliente}{parte2}"
|