Metadata-Version: 2.4
Name: tumbaga
Version: 0.2.0
Summary: Verificación de contraseñas con cadena de esquemas y actualización oportunista
Project-URL: Homepage, https://xiliux.com
Author: Juan Carlos Isaza Arenas
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Keywords: argon2,bcrypt,hashing,migration,password
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security :: Cryptography
Requires-Python: >=3.10
Requires-Dist: argon2-cffi>=23.1
Requires-Dist: bcrypt>=4.0
Description-Content-Type: text/markdown

# tumbaga

Verificación de contraseñas con **cadena de esquemas** y **actualización
oportunista**: tus usuarios migran de un algoritmo a otro al iniciar sesión, sin
restablecer nada y sin enterarse.

```python
from tumbaga import Tumbaga

tumbaga = Tumbaga()

# Al crear un usuario
usuario.password_hash = tumbaga.cifrar("su contraseña")

# Al iniciar sesión
r = tumbaga.verificar("su contraseña", usuario.password_hash)
if r.valida:
    if r.hash_nuevo:                       # quedó migrado a un esquema mejor
        usuario.password_hash = r.hash_nuevo
        db.commit()
    entrar(usuario)
```

## El nombre

La **tumbaga** es la aleación de oro y cobre que trabajaban los orfebres muiscas
y quimbayas: dos metales distintos que se funden y se comportan como uno solo,
más resistente que cualquiera de los dos por separado.

Esta librería hace lo mismo con los algoritmos de hash. Argon2id, bcrypt y los
que vengan conviven en una sola cadena que se usa como si fuera un único
esquema, y el paso de uno a otro ocurre sin que nadie lo note.

## Por qué existe

Un hash de contraseña es de una sola vía. De `bcrypt(clave)` no se saca `clave`,
y sin la clave no se puede calcular `argon2(clave)`.

**Por eso no existe forma de migrar contraseñas con un script.** Hay un solo
instante en toda la vida de un sistema en que la contraseña está en claro: el
momento en que el usuario la escribe para entrar. La migración es oportunista o
no es.

`tumbaga` reconoce el esquema de cada hash —el algoritmo viaja dentro del propio
hash, no en una columna aparte que pueda desincronizarse—, verifica con él, y si
no es el mejor disponible devuelve el hash ya recalculado para que lo guardes.

## Qué NO hace, a propósito

- **No guarda nada.** Devuelve el hash nuevo; tu aplicación decide cuándo
  persistirlo. Así funciona igual con SQLAlchemy, con SQLModel o sin ORM.
- **No conoce tu modelo de usuario.** No hay tablas ni migraciones que adoptar.
- **No gestiona sesiones ni tokens.**

## Esquemas

| Esquema | Escribe | Verifica |
|---|---|---|
| `Argon2id` | sí (por defecto) | sí |
| `Bcrypt` | no | sí |

`Bcrypt` está solo para no dejar fuera a nadie cuando se importan usuarios de
otro sistema. Todo hash de bcrypt que se verifique con éxito se reescribe en el
acto con Argon2id.

`Argon2id` usa los parámetros recomendados por OWASP para 2024+ (19 MiB, 2
iteraciones, 1 hilo). Si mañana subes el costo, los hashes viejos se reescriben
solos al entrar: `check_needs_rehash` detecta el cambio de parámetros.

## Cerrar la migración

Empezar una migración es fácil; olvidarla es lo normal. Mientras un esquema
débil siga en la cadena, una base de datos filtrada se ataca por esa rama.

```python
# Cuántos usuarios siguen en un esquema viejo
pendientes = sum(1 for u in usuarios if tumbaga.necesita_actualizacion(u.password_hash))
```

Cuando llegue a cero, quita ese esquema de la cadena:

```python
tumbaga = Tumbaga(esquemas=[Argon2id()])   # sin bcrypt
```

A partir de ahí, un hash antiguo levanta `EsquemaDesconocido` en vez de
verificarse en silencio.

## Cadena a medida

```python
from tumbaga import Tumbaga, Argon2id, Bcrypt

tumbaga = Tumbaga(esquemas=[
    Argon2id(memoria_kib=65536, iteraciones=3),   # escribe con este
    Bcrypt(),                                     # solo verifica
])
```

El primer esquema que no sea de solo verificación es con el que se escribe.

## Instalación

```sh
pip install tumbaga
```

## Licencia

Apache-2.0. Copyright 2026 Juan Carlos Isaza Arenas. Ver `NOTICE`.
