Files
GTI_GERENCIADOR_FISCAL/app/gerador_chave.py
T
GustavoandClaude Sonnet 5 5c577a644a fix(seguranca): move algoritmo de geracao de chave do JS pro backend
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>
2026-08-14 10:03:19 -03:00

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}"