DEV Community

Cover image for PAN-RAP Municipio v0.5: votación anónima con Blind Schnorr, HMAC eligibility y actas PDF
Fenix
Fenix

Posted on

PAN-RAP Municipio v0.5: votación anónima con Blind Schnorr, HMAC eligibility y actas PDF

PAN-RAP Municipio v0.5

Sistema de votación anónima con integridad criptográfica para gobernanza cívica municipal. Blind Schnorr sobre secp256k1, elegibilidad modular HMAC, ciclo de vida de convocatorias y generación de actas PDF verificables.

Qué es

PAN-RAP (Planet Assembly Network / Red Asamblearia Planetaria) es un ecosistema de gobernanza cívica descentralizada diseñado para municipios de ~25,000 habitantes. Este repo es la implementación de referencia del módulo de votación anónima.

Stack:

  • Backend: Python 3.12+ / FastAPI / coincurve (libsecp256k1)
  • Frontend: JavaScript ES modules / @noble/curves vendorizado (sin CDN)
  • Cripto: Blind Schnorr sobre secp256k1 (token 65 bytes)
  • BD: SQLite (WITHOUT ROWID en urna)
  • Licencia: AGPL-3.0-or-later

Qué entrega (Fase 5.5 completada)

Funcionalidad Estado Detalle
Motor Blind Schnorr ✅ Emisor (coincurve) + Urna (verificación fail-closed) + Frontend (@noble/curves)
Elegibilidad modular ✅ 4 modos: padrón (censo HMAC), presencial (códigos único uso), aval (K avales), eid (placeholder)
Ciclo de vida convocatorias ✅ Transiciones validadas: borrador → programada → emisión → votación → cerrada → escrutada → archivada
Escrutinio público ✅ GET /convocatorias/{id}/escrutinio con recuento, participación, hash de urna
Descarga urna completa ✅ GET /convocatorias/{id}/urna votos ordenados por r_prima
Acta PDF ✅ POST /convocatorias/{id}/acta con reportlab, solo en estado escrutada
Verificador independiente ✅ scripts/verificador_independiente.py audita urna completa
Tests ✅ 10 passed (3 elegibilidad + 7 ciclo de vida)

Qué NO hace aún

Fase Estado Queda pendiente
Fase 6 ⏳ Auditoría criptográfica externa (Cure53, Trail of Bits, etc.)
Fase 7 ⏳ Despliegue piloto en municipio real
Persistencia estados memoria ⏳ Códigos presenciales y avales se pierden al reiniciar servidor
Endpoint listar convocatorias ⏳ GET /convocatorias público para descubrimiento
Modo eid completo ⏳ Integración OIDC / identidad digital nacional

Nota de transparencia: El proyecto está en Fase 5.5. Los tests pasan (10/10), el motor criptográfico está auditado internamente, pero no ha pasado auditoría externa. No es apto para uso vinculante hasta completar Fase 6.

Cómo probarlo

# 1. Clonar y configurar
git clone https://github.com/amurlaniakea/pan-rap-municipio.git
cd pan-rap-municipio
python3.12 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt  # incluye reportlab para actas PDF

# 2. Ejecutar tests
pytest tests/ -v
# → 10 passed

# 3. Levantar servidor
export PANRAP_EMISOR_KEY_HEX="$(openssl rand -hex 32)"
fastapi dev main.py

# 4. Flujo rápido (ejemplo modo padrón)
curl -X POST http://localhost:8000/convocatorias \
  -H "Content-Type: application/json" \
  -d '{"titulo":"Test","texto":"...","opciones":["a_favor","en_contra"],"modo_elegibilidad":"padron","nivel_garantia":"alta","emision_abre_en":"2026-11-01T00:00:00","voto_abre_en":"2026-11-02T00:00:00","cierra_en":"2026-11-03T00:00:00"}'
# → {"id": "uuid", "estado": "borrador"}

curl -X POST http://localhost:8000/convocatorias/{id}/censo \
  -H "Content-Type: application/json" \
  -d '{"identificadores": ["dni_123"]}'

curl -X PATCH http://localhost:8000/convocatorias/{id}/estado \
  -H "Content-Type: application/json" \
  -d '{"estado": "programada"}'

curl -X PATCH http://localhost:8000/convocatorias/{id}/estado \
  -H "Content-Type: application/json" \
  -d '{"estado": "emision"}'

# 5. Verificar urna y generar acta
python scripts/verificador_independiente.py
# → VERIFICACIÓN EXITOSA: X votos válidos, 0 inválidos
Enter fullscreen mode Exit fullscreen mode

Arquitectura de seguridad (resumen)

  • S1 Emisión secuencial: threading.Lock() + TTL 10s
  • S4 Cero aritmética int sobre secretos: solo coincurve
  • S5 k efímero por sesión: PrivateKey() nuevo en cada inicio()
  • S6 Validación fail-closed: urna_schnorr.verificar() captura excepciones
  • S7 Clave fuera de git: .gitignore cubre clave_emisor.hex
  • S8 Cliente vendorizado: frontend/lib/ con @noble/curves (sin CDN)
  • Elegibilidad: persona_ref = HMAC-SHA256(secreto_convocatoria, convocatoria:identificador) — diferente por convocatoria, no enlazable
  • Secreto persistente: almacenado en convocatorias.secreto_hmac, nunca expuesto en respuestas HTTP

Referencias


Autor: Pedro Sordo Martínez

Licencia: AGPL-3.0-or-later

Top comments (0)