From 361c2ca770f299379caea73d487838a17b86b0d0 Mon Sep 17 00:00:00 2001 From: Gustavo Date: Thu, 13 Aug 2026 16:08:48 -0300 Subject: [PATCH] feat: gerador de chave de instalacao (aluguel/compra) na tela /gerador-chave Adiciona a segunda rotina recuperada do Chave.exe original: chave de instalacao (8 caracteres tipicos), ao lado da chave de reativacao ja existente na mesma tela, com um seletor de tipo. Algoritmo (confirmado matematicamente contra os 3 exemplos reais fornecidos, byte a byte): - codigoCliente * 19 + 31 vira um numero inserido sem cifrar no meio da chave final. - A modalidade ("30"=ALUGUEL / "00"=COMPRA) e cifrada com o MESMO algoritmo/chave interna ja usado na reativacao (soma modular + XOR), gerando 6 hex (3 bytes: 1 aleatorio + 2 cifrados). - Chave final = 3 primeiros hex + valor do cliente + 3 ultimos hex. 100% client-side (mesmo js puro da reativacao), nunca grava no banco. Verifiquei os 3 exemplos (DC45092C, 2E35083D, 3D25090C) reproduzindo o algoritmo em Python antes de trocar o JS - bateram exatamente. 106/106 testes passando. --- app/templates/gerador_chave.html | 82 +++++++++++++++++++++++++++---- tests/test_router_chave_manual.py | 3 +- 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/app/templates/gerador_chave.html b/app/templates/gerador_chave.html index f6d9042..f4f9cf3 100644 --- a/app/templates/gerador_chave.html +++ b/app/templates/gerador_chave.html @@ -1,15 +1,31 @@ {% extends "base.html" %} -{% block titulo %}Gerador de Chave de Reativação{% endblock %} +{% block titulo %}Gerador de Chave{% endblock %} {% block conteudo %}
GTI Grupo -

Gerador de Chave de Reativação

+

Gerador de Chave

+ + + - - +
+ + +
+ + @@ -23,6 +39,9 @@ const CHAVE_INTERNA = "YUQL23KL23DF90WI5E1JAS467NMCXXL6JAOAUWWMCL0AOMM4A4VZYW9KHJUI2347" + "EJHJKDF3424SKL K3LAKDJSL9RTIKJ"; +// ========================== +// REATIVAÇÃO — mês/cliente/ano cifrados por inteiro (22 caracteres) +// ========================== function gerarReativacao(codigoCliente, mes, ano, byteInicial = null) { const cliente = String(codigoCliente).padStart(6, '0'); const texto = String(mes).padStart(2, '0') + cliente + String(ano).slice(-2); @@ -40,8 +59,39 @@ function gerarReativacao(codigoCliente, mes, ano, byteInicial = null) { return resultado.map(v => v.toString(16).padStart(2, '0').toUpperCase()).join(''); } +// ========================== +// INSTALAÇÃO — só a modalidade ("30"=ALUGUEL / "00"=COMPRA) é cifrada +// (mesmo algoritmo de cifra da reativação, texto diferente); o valor +// derivado do código do cliente (cliente×19+31) entra sem cifrar, no +// meio dos 6 caracteres cifrados (3 antes + 3 depois). +// ========================== +function gerarInstalacao(codigoCliente, modalidade, byteInicial = null) { + const clienteNum = parseInt(String(codigoCliente), 10) || 0; + const valorCliente = clienteNum * 19 + 31; + const textoModalidade = modalidade === 'ALUGUEL' ? '30' : '00'; + + const aleatorio = byteInicial !== null ? byteInicial : Math.floor(Math.random() * 256); + let anterior = aleatorio; + const bytes = [aleatorio]; + for (let i = 0; i < textoModalidade.length; i++) { + const valor = (textoModalidade.charCodeAt(i) + anterior) % 255; + const cifrado = valor ^ CHAVE_INTERNA.charCodeAt(i % CHAVE_INTERNA.length); + bytes.push(cifrado); + anterior = cifrado; + } + + const criptografado = bytes.map(v => v.toString(16).padStart(2, '0').toUpperCase()).join(''); + const parte1 = criptografado.substring(0, 3); + const parte2 = criptografado.substring(3, 6); + return `${parte1}${valorCliente}${parte2}`; +} + +const $tipo = document.getElementById('tipo'); const $codigo = document.getElementById('codigo'); const $referencia = document.getElementById('referencia'); +const $modalidade = document.getElementById('modalidade'); +const $camposReativacao = document.getElementById('campos-reativacao'); +const $camposInstalacao = document.getElementById('campos-instalacao'); const $erro = document.getElementById('erro'); const $resultado = document.getElementById('resultado'); const $copiar = document.getElementById('copiar'); @@ -51,6 +101,12 @@ const $copiar = document.getElementById('copiar'); $referencia.value = `${hoje.getFullYear()}-${String(hoje.getMonth() + 1).padStart(2, '0')}`; })(); +$tipo.addEventListener('change', () => { + const ehReativacao = $tipo.value === 'reativacao'; + $camposReativacao.style.display = ehReativacao ? 'block' : 'none'; + $camposInstalacao.style.display = ehReativacao ? 'none' : 'block'; +}); + function mostrarErro(msg) { $erro.textContent = msg; $erro.style.display = 'block'; @@ -63,13 +119,19 @@ document.getElementById('gerar').addEventListener('click', () => { const codigoCliente = String($codigo.value || '').trim(); if (!/^[0-9]{1,6}$/.test(codigoCliente)) return mostrarErro('Código do cliente deve ter até 6 dígitos numéricos.'); const codigoFormatado = codigoCliente.padStart(6, '0'); - if (!/^\d{4}-\d{2}$/.test($referencia.value)) return mostrarErro('Selecione o mês/ano de referência.'); - const [anoStr, mesStr] = $referencia.value.split('-'); - const mesNum = Number(mesStr), anoNum = Number(anoStr); - if (!(mesNum >= 1 && mesNum <= 12)) return mostrarErro('Mês fora do intervalo 1-12.'); - if (!(anoNum >= 2000 && anoNum <= 2100)) return mostrarErro('Ano inválido (use entre 2000 e 2100).'); - const chave = gerarReativacao(codigoFormatado, mesNum, anoNum); + let chave; + if ($tipo.value === 'reativacao') { + if (!/^\d{4}-\d{2}$/.test($referencia.value)) return mostrarErro('Selecione o mês/ano de referência.'); + const [anoStr, mesStr] = $referencia.value.split('-'); + const mesNum = Number(mesStr), anoNum = Number(anoStr); + if (!(mesNum >= 1 && mesNum <= 12)) return mostrarErro('Mês fora do intervalo 1-12.'); + if (!(anoNum >= 2000 && anoNum <= 2100)) return mostrarErro('Ano inválido (use entre 2000 e 2100).'); + chave = gerarReativacao(codigoFormatado, mesNum, anoNum); + } else { + chave = gerarInstalacao(codigoFormatado, $modalidade.value); + } + $resultado.textContent = chave; $resultado.style.display = 'block'; $copiar.style.display = 'inline-block'; diff --git a/tests/test_router_chave_manual.py b/tests/test_router_chave_manual.py index d4a786a..a690dd3 100644 --- a/tests/test_router_chave_manual.py +++ b/tests/test_router_chave_manual.py @@ -14,7 +14,8 @@ def test_pagina_gerador_carrega(): resposta = client.get("/gerador-chave") assert resposta.status_code == 200 assert "gerarReativacao" in resposta.text - assert "Gerador de Chave de Reativação" in resposta.text + assert "gerarInstalacao" in resposta.text + assert "Gerador de Chave" in resposta.text def test_pagina_gerador_nao_grava_nada():