Cómo guarda Kode tus códigos
Dónde guarda Kode tus claves 2FA, cómo se sincronizan y cómo sacarlas de nuevo.
Solo en tu llavero
Cada cuenta –incluida la clave secreta con la que se generan los códigos– se guarda como un elemento del llavero del iPhone, disponible tras el primer desbloqueo. La clave nunca se guarda en la base de datos de la app, en los ajustes, en archivos de registro, en análisis ni en informes de fallos.
Llavero de iCloud
Con la sincronización activada (por defecto), el elemento se marca como sincronizable, así que el Llavero de iCloud lo lleva a tus otros dispositivos Apple y a un iPhone nuevo. El Llavero de iCloud tiene cifrado de extremo a extremo. Si desactivas la sincronización, las claves pasan a estar solo en este iPhone («solo este dispositivo») y no se trasladan a otro teléfono.
Sin servidor
Kode no hace ninguna conexión de red. Los códigos se calculan con el estándar TOTP (RFC 6238) usando HMAC-SHA1/256/512: los mismos códigos que Google Authenticator.
Bloqueo
Face ID o código al abrir la app, y de nuevo tras un minuto en segundo plano. Mientras la app no está activa, una pantalla de bloqueo lo cubre todo, así que el selector de apps nunca muestra un código. Un código copiado se borra del portapapeles del iPhone a los 60 segundos. Puedes pegarlo en tu Mac con el Portapapeles Universal; allí se queda hasta que copies otra cosa.
El formato del archivo de copia
La copia cifrada es un archivo JSON. La clave se deriva de tu contraseña con PBKDF2-HMAC-SHA256 (600.000 iteraciones, sal de 16 bytes) y el contenido se cifra con AES-256-GCM. ciphertext es el texto cifrado seguido de la etiqueta GCM de 16 bytes. Todos los campos binarios están en Base64.
{
"format": "kode-backup",
"version": 1,
"kdf": { "name": "PBKDF2-HMAC-SHA256", "iterations": 600000, "salt": "…" },
"cipher": { "name": "AES-256-GCM", "nonce": "…" },
"ciphertext": "…"
}
Una vez descifrado, el contenido es una lista de cuentas. Cada cuenta incluye además una dirección otpauth:// lista para usar que la mayoría de las apps pueden importar.
{
"created": "2026-09-24T09:41:00Z",
"accounts": [
{
"issuer": "GitHub", "name": "anna", "secret": "JBSWY3DPEHPK3PXP",
"algorithm": "SHA1", "digits": 6, "period": 30,
"uri": "otpauth://totp/GitHub:anna?secret=JBSWY3DPEHPK3PXP&issuer=GitHub&algorithm=SHA1&digits=6&period=30"
}
]
}
Para descifrar el archivo sin Kode (Python con el paquete cryptography):
import base64, getpass, hashlib, json, sys
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
backup = json.load(open(sys.argv[1]))
b64 = base64.b64decode
key = hashlib.pbkdf2_hmac("sha256", getpass.getpass().encode(),
b64(backup["kdf"]["salt"]), backup["kdf"]["iterations"], 32)
plain = AESGCM(key).decrypt(b64(backup["cipher"]["nonce"]), b64(backup["ciphertext"]), None)
for account in json.loads(plain)["accounts"]:
print(account["uri"])