Como o Kode guarda seus códigos
Onde o Kode guarda suas chaves 2FA, como elas são sincronizadas e como tirá-las de lá.
Só nas Chaves do iPhone
Cada conta, inclusive a chave secreta que gera os códigos, é salva como um item das Chaves do iPhone, disponível depois do primeiro desbloqueio. A chave nunca vai para o banco de dados do app, para os ajustes, para arquivos de log, para análises ou para relatórios de falhas.
Chaves do iCloud
Com a sincronização ativada (o padrão), o item é marcado como sincronizável, e as Chaves do iCloud o levam para seus outros dispositivos Apple e para um iPhone novo. As Chaves do iCloud têm criptografia de ponta a ponta. Se você desativar a sincronização, as chaves passam a ficar só neste iPhone (“somente este dispositivo”) e não vão para outro celular.
Sem servidor
O Kode não se conecta à internet. Os códigos são calculados com o padrão TOTP (RFC 6238) usando HMAC-SHA1/256/512: os mesmos códigos do Google Authenticator.
Bloqueio
Face ID ou código quando o app abre, e de novo depois de um minuto em segundo plano. Enquanto o app não está ativo, uma tela de bloqueio cobre tudo, então o seletor de apps nunca mostra um código. Um código copiado sai da área de transferência do iPhone depois de 60 segundos. Dá para colar no Mac pela Área de Transferência Universal; lá ele fica até você copiar outra coisa.
O formato do arquivo de backup
O backup criptografado é um arquivo JSON. A chave é derivada da sua senha com PBKDF2-HMAC-SHA256 (600.000 iterações, salt de 16 bytes), e o conteúdo é criptografado com AES-256-GCM. ciphertext é o texto criptografado seguido da tag GCM de 16 bytes. Todos os campos binários estão em Base64.
{
"format": "kode-backup",
"version": 1,
"kdf": { "name": "PBKDF2-HMAC-SHA256", "iterations": 600000, "salt": "…" },
"cipher": { "name": "AES-256-GCM", "nonce": "…" },
"ciphertext": "…"
}
Depois de descriptografado, o conteúdo é uma lista de contas. Cada conta também traz um endereço otpauth:// pronto, que a maioria dos apps consegue 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 descriptografar o arquivo sem o Kode (Python com o pacote 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"])