age vs GPG é a escolha entre uma ferramenta de criptografia pequena e de propósito único e uma implementação completa do OpenPGP. O age (formato age-encryption.org/v1, especificado no C2SP) criptografa um arquivo para um ou mais destinatários — cada um deles uma string curta como age1…, uma chave pública SSH ou uma chave pós-quântica age1pq1… —, usando encapsulamento de chave com X25519 ou com ML-KEM híbrido e ChaCha20-Poly1305 para o payload. O GnuPG implementa o OpenPGP: criptografia, assinaturas, certificação, chaveiros, servidores de chaves, cartões inteligentes e integração com e-mail, com décadas de compatibilidade acumulada.
Visão geral
| age | GnuPG (OpenPGP) | |
|---|---|---|
| Escopo | Apenas criptografia de arquivos | Criptografia, assinatura, certificação, gerenciamento de chaves |
| Especificação | age v1 (C2SP), um único formato, sem versões a negociar | RFC 9580 (2024) e RFC 9980 (2026); o GnuPG segue o LibrePGP |
| Chave pública | 62 caracteres (age1…); pós-quântica, 1.959 caracteres (age1pq1…) | Chave binária com IDs de usuário e autoassinaturas; 473 bytes para Ed25519 mais Cv25519 (medido) |
| Algoritmos | X25519, híbrido ML-KEM-768 + X25519, scrypt para frases secretas, ChaCha20-Poly1305, HKDF e HMAC-SHA-256 | Muitos: RSA, ECC, AES, Camellia, cifras legadas para dados antigos; ML-KEM-768 + X25519 no GnuPG 2.5 |
| Configuração | Nenhuma | Preferências, modelos de confiança, configurações de algoritmo e de compressão |
| Texto cifrado medido para um arquivo de 26 bytes | 226 bytes (X25519); 1.685 bytes (pós-quântico) | 203 bytes (Cv25519); 1.288 bytes (subchave ML-KEM-768 + X25519) |
| Chaves SSH como destinatários | Sim (ssh-ed25519, ssh-rsa) | Não |
| Chaves de hardware | Por meio de plugins: YubiKey PIV, TPM, Secure Enclave | Cartões inteligentes OpenPGP e chaves de segurança |
| Assinaturas | Nenhuma, de propósito | Sim |
| Descoberta de chaves | Nenhuma; os destinatários são trocados diretamente | Servidores de chaves, WKD, chaveiros |
| Arquivos grandes | Processa em fluxo, em segmentos autenticados de 64 KiB; API de descriptografia com acesso aleatório desde a 1.3 | Processa em fluxo; um único pacote AEAD dividido em segmentos |
O mesmo arquivo criptografado com cada um
O script abaixo criptografa um segredo de 26 bytes para um destinatário age X25519, um destinatário age pós-quântico, uma subchave Cv25519 do GnuPG e uma subchave ML-KEM-768 + X25519 do GnuPG, descriptografa cada resultado e compara os tamanhos (age 1.3.2, GnuPG 2.5.21). Tudo acontece em um diretório temporário.
#!/bin/sh
dir=$(mktemp -d /tmp/agegpg.XXXXXX) # short path: gpg-agent sockets need one
cd "$dir" || exit 1
export GNUPGHOME="$dir/g"
mkdir -m 700 g
printf 'db_password=correct-horse\n' > secret.env # 26 bytes
echo "== age"
age-keygen -o key.txt 2> /dev/null
recipient=$(age-keygen -y key.txt)
echo "recipient: ${#recipient} characters"
age -r "$recipient" -o secret.env.age secret.env
age -d -i key.txt secret.env.age | cmp -s - secret.env && echo "decrypted: identical"
echo "ciphertext: $(wc -c < secret.env.age | tr -d ' ') bytes"
echo "== age, post-quantum recipient"
age-keygen -pq -o pq.txt 2> /dev/null
pqrecipient=$(age-keygen -y pq.txt)
echo "recipient: ${#pqrecipient} characters"
age -r "$pqrecipient" -o secret.env.pq.age secret.env
age -d -i pq.txt secret.env.pq.age | cmp -s - secret.env && echo "decrypted: identical"
echo "ciphertext: $(wc -c < secret.env.pq.age | tr -d ' ') bytes"
echo "== GnuPG"
gpg --batch --quiet --passphrase '' --quick-gen-key "Ops <ops@example.org>" ed25519 cert 1y
fpr=$(gpg --list-keys --with-colons ops@example.org 2> /dev/null | awk -F: '/^fpr/ { print $10; exit }')
gpg --batch --quiet --passphrase '' --quick-add-key "$fpr" cv25519 encr 1y
gpg --batch --quiet --trust-model always -r ops@example.org -o secret.env.gpg -e secret.env
gpg --batch --quiet -d secret.env.gpg 2> /dev/null | cmp -s - secret.env && echo "decrypted: identical"
echo "ciphertext: $(wc -c < secret.env.gpg | tr -d ' ') bytes"
echo "public key export: $(gpg --export ops@example.org | wc -c | tr -d ' ') bytes binary"
echo "== GnuPG, ML-KEM-768 + X25519 subkey"
gpg --batch --quiet --passphrase '' --quick-gen-key "Ops PQ <ops-pq@example.org>" ed25519 cert 1y
fpr=$(gpg --list-keys --with-colons ops-pq@example.org 2> /dev/null | awk -F: '/^fpr/ { print $10; exit }')
gpg --batch --quiet --passphrase '' --quick-add-key "$fpr" ky768_cv25519 encr 1y
gpg --batch --quiet --trust-model always -r ops-pq@example.org -o secret.env.pq.gpg -e secret.env
gpg --batch --quiet -d secret.env.pq.gpg 2> /dev/null | cmp -s - secret.env && echo "decrypted: identical"
echo "ciphertext: $(wc -c < secret.env.pq.gpg | tr -d ' ') bytes"
gpgconf --kill gpg-agent
cd / && rm -rf "$dir"
== age
recipient: 62 characters
decrypted: identical
ciphertext: 226 bytes
== age, post-quantum recipient
recipient: 1959 characters
decrypted: identical
ciphertext: 1685 bytes
== GnuPG
decrypted: identical
ciphertext: 203 bytes
public key export: 473 bytes binary
== GnuPG, ML-KEM-768 + X25519 subkey
decrypted: identical
ciphertext: 1288 bytes
Os dois acrescentam um cabeçalho fixo por destinatário, mais uma pequena sobrecarga de autenticação, então, em arquivos reais, a diferença é desprezível. A sobrecarga pós-quântica é o texto cifrado do ML-KEM-768 (1.088 bytes) transportado em cada bloco de destinatário; a do age é maior principalmente porque o cabeçalho dele é texto, com os valores binários em base64. O script usa --trust-model always só porque a chave foi criada um segundo antes; no uso real, você verificaria a impressão digital do destinatário.
Detalhes de interoperabilidade que importam
O gpg --list-packets aplicado aos arquivos acima mostra duas coisas que vale a pena saber. Para a chave Cv25519, o GnuPG 2.5 gravou um pacote “AEAD encrypted packet” com OCB, o formato do LibrePGP, em vez do pacote de dados criptografados versão 2 que a RFC 9580 define, porque as próprias chaves do GnuPG anunciam suporte a ele; implementações que seguem apenas a RFC 9580 podem não conseguir ler esses arquivos. Para a subchave pós-quântica, o GnuPG usou o algoritmo de chave pública 8, a variante composta com Kyber do LibrePGP, enquanto a RFC 9980 atribui o 35 ao ML-KEM-768 + X25519; por isso, as chaves pós-quânticas do GnuPG e as chaves da RFC 9980 não interoperam. O age tem um único formato e uma única construção pós-quântica (HPKE com o híbrido de ML-KEM-768 e X25519, também conhecido como X-Wing), com vetores de teste publicados.
Por que o age é pequeno de propósito
O age deixa de fora tudo o que tornava o OpenPGP difícil de usar com segurança: não há negociação de algoritmo, nem compressão (que pode vazar informações sobre o texto claro), nem modo sem autenticação, nem servidor de chaves, nem rede de confiança. Um destinatário é uma string que você pode colar em um arquivo de configuração, e um arquivo criptografado para vários destinatários tem um bloco por destinatário. O preço é o escopo: o age não tem como assinar, então não prova quem criptografou um arquivo, e não gerencia nem distribui chaves. Outras ferramentas preenchem essas lacunas em volta dele: o SOPS criptografa valores YAML e JSON para destinatários age, e plugins permitem que as identidades (chaves privadas) fiquem em YubiKeys, TPMs ou no Secure Enclave do macOS.
A abrangência é o ponto forte do GnuPG onde o OpenPGP é a língua comum: e-mail assinado e criptografado com correspondentes que já o usam, repositórios de pacotes, assinaturas de releases que os verificadores downstream já checam e cartões inteligentes OpenPGP que guardam juntas as chaves de assinatura, de criptografia e de autenticação.
Quando escolher cada um
- Segredos no Git (SOPS), variáveis de CI, backups, dotfiles: age, com um destinatário por pessoa ou máquina e um destinatário de recuperação em hardware ou offline.
- Criptografar para alguém cuja chave SSH você já tem: age.
- Arquivamento confidencial de longo prazo: age com destinatários pós-quânticos
age1pq1, para resistir a ataques do tipo coletar agora, descriptografar depois. - E-mail criptografado: OpenPGP (ou S/MIME; veja S/MIME vs OpenPGP).
- Criptografar e assinar em uma só etapa para um destinatário que usa OpenPGP: GnuPG.
- Assinaturas de releases e de pacotes: nenhum dos dois como ferramenta de criptografia; veja Sigstore vs GPG.
Notas de migração
- Gere identidades age por pessoa ou máquina (
age-keygen, ouage-keygen -pqassim que todos que criptografam ou descriptografam usarem o age 1.3 ou posterior) e guarde uma identidade de recuperação offline. - Recriptografe em vez de converter: descriptografe cada arquivo GPG com a chave antiga e criptografe-o para os novos destinatários; não existe conversão de formato. No SOPS, adicione os destinatários age às regras de criação em
.sops.yamle rodesops updatekeysem cada arquivo; quando depois remover as chaves PGP, rode em seguidasops rotate, para que a chave de dados também mude. - Mantenha a chave GPG até que todos os arquivos e backups antigos tenham sido recriptografados ou tenham expirado.
- Substitua as assinaturas GPG separadamente por assinaturas SSH, minisign ou Sigstore, já que o age não oferece nenhuma.
- Confira tamanhos e ferramentas com a ferramenta de criptografia age e inspecione os arquivos antigos com o inspetor de OpenPGP para descobrir quais chaves e algoritmos eles usam.
Referências
- Especificação do age (C2SP), incluindo o tipo de destinatário ML-KEM-768 + X25519
- Notas de versão do age v1.3.0
- RFC 9580, OpenPGP
- RFC 9980, criptografia pós-quântica no OpenPGP
- RFC 9180, criptografia híbrida de chave pública
- FIPS 203, ML-KEM
- Documentação do SOPS: identidades age e gerenciamento de chaves