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
Arquitectura de seguridad (resumen)
-
S1 Emisión secuencial:
threading.Lock()+ TTL 10s -
S4 Cero aritmética int sobre secretos: solo
coincurve -
S5
kefímero por sesión:PrivateKey()nuevo en cadainicio() -
S6 Validación fail-closed:
urna_schnorr.verificar()captura excepciones -
S7 Clave fuera de git:
.gitignorecubreclave_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
- Repo: https://github.com/amurlaniakea/pan-rap-municipio
- README: https://github.com/amurlaniakea/pan-rap-municipio/blob/main/README.md
-
Docs:
docs/01-spec-v0.md,docs/03-motor-blind-schnorr.md -
Tests:
tests/test_elegibilidad.py,tests/test_ciclo_vida.py -
Frontend:
frontend/voto_schnorr.js,frontend/lib/noble/ -
Verificador:
scripts/verificador_independiente.py - Licencia: AGPL-3.0-or-later (661 líneas verbatim GNU)
Autor: Pedro Sordo Martínez
Licencia: AGPL-3.0-or-later
Top comments (0)