# Formato de las partes de ShamirVault

Este documento existe para responder a una pregunta concreta: **¿y si la
aplicación desaparece?**

Con SLIP-39 la respuesta la da el estándar — las partes las lee cualquier
implementación compatible. Con el formato propio la da este documento, más el
script de referencia que hay en [`tools/recuperar.py`](../tools/recuperar.py),
que recupera un secreto usando solo la biblioteca estándar de Python.

> El script se comprueba a sí mismo contra vectores generados por la propia
> app: `python3 tools/recuperar.py --autoprueba`.

---

## 1. Una parte

Una parte es un blob binario escrito en un solo alfabeto, sin prefijos
visibles.

```
byte 0        versión (0x02; 0x01 es el formato anterior)
byte 1        índice de la parte, de 1 a n
byte 2        n, número total de partes
byte 3        k, partes que hacen falta
bytes 4..     datos
últimos 4     checksum (solo en la versión 2)
```

El **checksum** son los cuatro primeros bytes de `SHA-256` sobre todo lo
anterior. No es un adorno: es lo que hace que decodificar sea **sin
ambigüedad**.

### Alfabetos

El mismo blob se puede escribir de tres formas, y la parte no dice en cuál:

| Alfabeto | Notas |
|---|---|
| **hex** | dos caracteres por byte, minúsculas |
| **base58** | alfabeto de Bitcoin, sin `0`, `O`, `I` ni `l` |
| **base64url** | sin relleno `=` |

Para leer una parte se prueban los tres y **el checksum decide**: como mucho
uno valida. Sin él habría que adivinar, y ese era el problema: el alfabeto
base58 es un subconjunto del base64url, así que una parte escrita en base64
podía decodificarse como base58 y devolver **otra parte distinta** sin dar
ningún error.

Las partes de la **versión 1** no llevan checksum y esa ambigüedad no tiene
arreglo: se leen con la heurística de probar los tres alfabetos y quedarse con
el primero cuya cabecera sea coherente. Por eso se dejó de generarlas.

---

## 2. Reconstruir

El reparto es Shamir sobre **GF(256)** con polinomio `0x11B` —el de AES— y
**generador 3**.

> El generador 2 **no** vale con ese polinomio: solo tiene orden 51, así que no
> genera el campo entero. Es un error fácil de cometer y difícil de ver: las
> partes salen repetidas.

El secreto vive en el **término independiente**, o sea en `x = 0`, y cada parte
`i` es el polinomio evaluado en `x = i`. Con `k` partes se interpola por
Lagrange, byte a byte.

Los pesos de Lagrange solo dependen de las coordenadas `x`, así que se calculan
una vez y se reutilizan para todos los bytes.

---

## 3. El resumen del secreto

Lo que se reparte no es el secreto pelado, sino:

```
resumen (4 bytes) ‖ secreto en UTF-8
```

donde el resumen son los cuatro primeros bytes de `SHA-256(secreto)`.

Al reconstruir se recalcula y se compara. Sirve para algo que el checksum de
cada parte no puede cubrir: **quien tenga una parte puede manipularla y
recalcular su checksum**, pero no puede dejar el resumen coherente, porque no
conoce el secreto.

Es la respuesta a que el esquema de Shamir no sea *verificable*: sin esto, dos
repartos distintos con la misma forma producen partes que encajan
aritméticamente y devuelven un secreto plausible y falso.

---

## 4. Secretos protegidos con contraseña

Si el secreto se protegió con contraseña, lo que se reparte es un sobre:

```
SVE2:<base64( cabecera ‖ salt ‖ nonce ‖ cifrado‖tag )>

cabecera   5 B   [0]    identificador de la derivación (0x01 = PBKDF2-HMAC-SHA256)
                 [1..4] iteraciones, big-endian
salt      32 B
nonce     12 B
cifrado    n B   AES-256-GCM, con los 16 B del tag al final
```

Cabecera, salt y nonce van como **datos autenticados**. Las iteraciones viajan
dentro para poder subirlas sin romper los sobres ya escritos.

`SVE1:` es el formato anterior —AES-256-CBC sin autenticar— y solo se lee.

---

## 5. Archivos

Con un archivo no se reparten sus bytes: se reparte **su llave**, que son 32
bytes. El archivo cifrado no es secreto y puede guardarse donde sea.

```
SVF1  cabecera   "SVF1"    4 B
                 versión   1 B
                 nonce    16 B
      cifrado             n B   AES-256-CTR
      tag                32 B   HMAC-SHA256(cabecera ‖ cifrado)
```

Las dos claves —cifrar y autenticar— se derivan de la repartida:

```
clave_cifrado = HMAC-SHA256(llave, "SVF1-enc")
clave_mac     = HMAC-SHA256(llave, "SVF1-mac")
```

El **nombre del archivo va dentro del cifrado**, precedido de su longitud en
dos bytes: saber que alguien guarda `escritura-casa.pdf` ya dice demasiado. Lo
que sí se filtra es el tamaño.

El tag se comprueba **antes** de descifrar nada.

---

## 6. Lo que este formato no hace

- **No es interoperable.** Ninguna otra herramienta lo lee. Si eso importa,
  reparte con **SLIP-39**, que la app también genera y que leen Trezor y
  compañía.
- **No oculta el tamaño** del secreto ni del archivo.
- **No protege contra quien tenga `k` partes.** Eso no es un fallo, es la
  definición del esquema.
