Esquema de assinatura
SLH-DSA explicado matematicamente
Como o SLH-DSA-SHA2-256f (FIPS 205) assina sem recorrer a nenhum problema difícil além das funções de hash — das cadeias de hash às árvores de Merkle, até à floresta de árvores FORS.
Atualizado: 2026-11-09
1. Do que se trata aqui
Na McGesund, os clientes dos planos Avaliações Premium e Sinais Premium podem escolher o nível de assinatura PQ-Dual-Diversified. Nesse caso, cada código QR leva, além da assinatura clássica Ed25519, duas assinaturas resistentes a computadores quânticos: ML-DSA-87 e SLH-DSA-SHA2-256f.
Este artigo explica a segunda. Está construída de forma diferente de tudo o resto que utilizamos.
O SLH-DSA não é cifragem. O conteúdo continua legível publicamente. O SLH-DSA prova origem e integridade — como qualquer assinatura digital.
SLH-DSA significa, por extenso, Stateless Hash-Based Digital Signature Algorithm. Foi desenvolvido a partir do SPHINCS+ e normalizado em 2024 como FIPS 205. O nome já revela o que tem de especial: o esquema usa apenas funções de hash. Nada de reticulados, nada de curvas elípticas, nada de grandes números primos.
2. O que é assinado exatamente?
Na McGesund, o SLH-DSA assina o mesmo conjunto de dados que a assinatura Ed25519 no código QR também abrange: o payload do código QR. Este indica, entre outras coisas, a que empresa pertence o código (fid), de que tipo é (typ), quando foi emitido (iat) e até quando é válido (exp) — sob a forma de uma sequência de bytes codificada de forma compacta (CBOR).
Esta sequência de bytes é a nossa mensagem . Se alguém alterar posteriormente um único bit, nenhuma das assinaturas volta a corresponder.
A assinatura SLH-DSA não está no próprio código QR. Com cerca de 49 KB, é demasiado grande para isso. Fica guardada como um «carimbo» próprio junto ao registo QR no backend da McGesund e é carregada no navegador quando se clica em «Verificar autenticidade».
3. Porquê um esquema feito apenas de funções de hash?
Todo o esquema de assinatura assenta numa tarefa que, sem a chave secreta, é praticamente impossível de resolver:
| Esquema | A segurança assenta em |
|---|---|
| Ed25519 | logaritmo discreto numa curva elíptica |
| FALCON | vetores curtos em reticulados NTRU |
| ML-DSA | Module-LWE e Module-SIS (reticulados) |
| SLH-DSA | apenas em propriedades da função de hash |
Um grande computador quântico quebraria o Ed25519. O FALCON e o ML-DSA são considerados resistentes a computadores quânticos — mas ambos se apoiam em problemas de reticulados. Se um dia fosse descoberto um ataque fundamental aos reticulados, ambos seriam afetados ao mesmo tempo.
O SLH-DSA, pelo contrário, só precisa de que a função de hash faça aquilo para que foi construída:
Esta é a hipótese mais longa e mais profundamente estudada de toda a criptografia. Mesmo um computador quântico ajuda aqui apenas de forma limitada: o algoritmo de Grover reduz para metade os bits de segurança. É por isso que a variante 256 usa valores de hash de bytes bits — depois de Grover, continuam a restar 128 bits.
É precisamente por isso que o nível se chama Diversified: o ML-DSA (reticulados) e o SLH-DSA (hash) vêm de famílias matemáticas diferentes. Se uma cair, a outra continua a sustentar.
4. Elemento 1: assinar com cadeias de hash (WOTS+)
A ideia de base é surpreendentemente simples. Começamos com um único dígito.
Tome-se um valor aleatório secreto e aplique-se-lhe o hash uma e outra vez:
O fim da cadeia, , é publicado. É a chave pública para este único dígito.
Para assinar um dígito entre 0 e 15, revela-se o valor da cadeia na posição :
Quem quiser verificar aplica o hash a mais vezes. Se o resultado for exatamente , a assinatura é autêntica:
Um mini-exemplo
Assinamos o dígito :
- O signatário publica .
- O verificador calcula e compara com . Coincide.
Um falsificador que tenha visto pode, sem esforço, calcular a partir daí , e assim por diante — ou seja, assinar dígitos maiores. Dígitos menores não consegue, porque para isso teria de aplicar o hash ao contrário.
A soma de verificação contra o «aumentar a contagem»
Para que ninguém possa simplesmente aumentar dígitos, é assinada adicionalmente uma soma de verificação:
Se um falsificador aumentar um dígito da mensagem, fica menor — e para isso teria de calcular ao contrário na cadeia da soma de verificação. Ambos os sentidos ficam assim bloqueados.
Extrapolado para um valor de hash
Um valor de hash de 32 bytes tem 64 dígitos na base 16. A estes juntam-se 3 dígitos de soma de verificação:
Uma assinatura deste tipo chama-se WOTS+ (Winternitz One-Time Signature). O seu grande senão está no nome: One-Time, de uso único. Quem assinar duas mensagens diferentes com a mesma chave revela tantos valores das cadeias que as falsificações se tornam possíveis.
5. Elemento 2: muitas chaves de uso único sob o mesmo teto (árvore de Merkle)
Se cada chave só pode ser usada uma vez, são precisas muitíssimas — mas quer-se publicar apenas uma chave pública.
A solução é uma árvore de Merkle: as chaves públicas de pares WOTS+ formam as folhas. Cada dois vizinhos são combinados num hash, os resultados novamente aos pares, até que no topo fique uma única raiz.
No SLH-DSA-SHA2-256f, cada árvore tem altura , ou seja, folhas.
Para mostrar que uma determinada folha pertence à árvore, fornece-se o caminho de autenticação: os 4 nós irmãos no caminho para cima. Com eles, o verificador vai calculando hashes desde a folha até à raiz. Se o resultado for a raiz conhecida, a folha pertence à árvore.
6. Elemento 3: árvores sobre árvores (hiperárvore)
16 chaves de uso único não chegam, naturalmente. Por isso, o SLH-DSA empilha árvores umas sobre as outras: a raiz de uma árvore é assinada com uma chave WOTS+ da árvore acima dela. Esta torre chama-se hiperárvore.
No SLH-DSA-SHA2-256f há camadas, cada uma com altura 4:
Lá em baixo ficam assim disponíveis
folhas. A raiz da árvore mais alta chama-se — é ela a verdadeira chave pública.
Importante: ninguém calcula estas árvores de antemão. Todos os valores secretos são derivados quando necessário a partir de uma seed secreta . Guardam-se apenas quatro valores de 32 bytes cada:
| Parte | Conteúdo | Tamanho |
|---|---|---|
| SK.seed | seed para todos os valores secretos das cadeias | 32 B |
| SK.prf | seed para a componente aleatória | 32 B |
| PK.seed | seed pública, separa os pares de chaves uns dos outros | 32 B |
| PK.root | raiz da árvore mais alta | 32 B |
A chave privada é composta pelos quatro (128 bytes), a pública apenas por PK.seed e PK.root (64 bytes).
7. Elemento 4: uma floresta contra coincidências (FORS)
Agora o verdadeiro problema: como sabe o signatário qual das folhas ainda não foi usada?
Esquemas mais antigos baseados em hash (XMSS, LMS) mantêm para isso um contador. Este nunca pode ser reposto — nem por uma cópia de segurança, nem por um segundo servidor. Se isso acontecer, uma chave de uso único é usada duas vezes e o esquema fica quebrado. Esquemas deste tipo chamam-se com estado.
O SLH-DSA é sem estado: a folha é escolhida de forma pseudoaleatória a partir da própria mensagem. Pode então acontecer que duas mensagens calhem na mesma folha. Por isso, lá em baixo não está uma assinatura de uso único, mas sim uma assinatura de poucos usos: FORS (Forest of Random Subsets).
O FORS é composto por pequenas árvores de Merkle, cada uma com folhas. As folhas são valores aleatórios secretos.
Para assinar, o hash da mensagem é decomposto em 35 números de 9 bits cada. Cada número escolhe exatamente uma folha na «sua» árvore. A assinatura contém, para cada árvore:
- o valor secreto dessa folha
- o seu caminho de autenticação (9 nós)
Para uma outra mensagem, um falsificador teria de acertar, em todas as 35 árvores, em folhas cujos segredos já tivessem sido revelados. Mesmo que a mesma instância FORS tenha sido usada algumas vezes, isso não tem hipótese.
8. Todo o processo de assinatura
- Componente aleatória. A partir de , de um número aleatório novo e da mensagem, obtém-se um valor de 32 bytes .
- Hash da mensagem.
Os 49 bytes do digest são repartidos: 40 bytes escolhem as 35 folhas FORS, o resto determina qual árvore (
idx_tree) e qual folha (idx_leaf) lá em baixo na hiperárvore são usadas. - Assinatura FORS sobre a mensagem. Daí resulta uma chave pública FORS.
- Assinatura da hiperárvore sobre essa chave FORS: 17 assinaturas WOTS+, cada uma com um caminho de autenticação, camada a camada até .
A assinatura final é simplesmente a concatenação:
9. Porque é que a assinatura tem 49 856 bytes
Com os parâmetros do SLH-DSA-SHA2-256f, o tamanho pode ser calculado com exatidão ( bytes por valor de hash):
| Parte | Cálculo | Bytes |
|---|---|---|
| 32 | ||
| FORS | 11 200 | |
| Hiperárvore | 38 624 | |
| Total | 49 856 |
Para comparação: uma assinatura Ed25519 tem 64 bytes, a do ML-DSA-87 tem 4 627 bytes. O tamanho é o preço de o SLH-DSA se bastar com nada mais do que funções de hash.
10. Como o navegador verifica
No SLH-DSA, verificar significa: voltar a calcular os hashes.
- Recalcular o digest a partir de , da chave pública e da mensagem.
- A partir das 35 folhas FORS reveladas e dos seus caminhos, calcular as 35 raízes das árvores e, a partir delas, a chave pública FORS.
- Com cada uma das 17 assinaturas WOTS+, levar as cadeias de hash até ao fim, formar a chave pública WOTS+ e subir pelo caminho até à raiz da árvore.
- No fim tem de sair exatamente .
São alguns milhares de chamadas de hash — para um computador, uma ninharia. No navegador, a verificação demora connosco cerca de 6 a 12 milissegundos.
11. O que acontece se alguém alterar os dados?
Se mudar nem que seja um bit da mensagem , o digest muda por completo. Com isso, os 35 números apontam para outras folhas FORS e o esquema para outro lugar na hiperárvore.
Os segredos revelados deixam de encaixar: as raízes recalculadas divergem e, no fim, não sai . A verificação falha.
Para construir mesmo assim uma assinatura válida, um atacante teria de conhecer segredos que nunca foram publicados — ou seja, inverter uma função de hash.
12. Porquê precisamente 256f?
O FIPS 205 define doze conjuntos de parâmetros: duas famílias de hash (SHA-2 e SHAKE), três níveis de segurança (128, 192, 256) e, para cada um, uma variante rápida («f») e uma pequena («s»).
| Variante | Assinatura | Assinar (nas nossas medições) | Verificar |
|---|---|---|---|
| SHA2-256s | 29 792 B | aprox. 2,6 s | aprox. 3 ms |
| SHA2-256f | 49 856 B | aprox. 0,26 s | aprox. 6 ms |
256 significa a categoria de segurança 5 do NIST — o mesmo nível do FALCON-1024 e do ML-DSA-87.
Escolhemos «f» porque as empresas podem emitir até 500 códigos QR de uma só vez. Com «s», a assinatura demoraria mais de 20 minutos; com «f», menos de um minuto. Em contrapartida, a assinatura é maior — como não está no código QR, isso é aceitável.
SHA-2 em vez de SHAKE, porque o SHA-256 e o SHA-512 têm aceleração por hardware em praticamente qualquer equipamento.
13. Aleatoriedade ao assinar
O FIPS 205 permite duas formas de gerar :
- determinística: a mesma mensagem, a mesma assinatura
- aleatorizada («hedged»): entra também um número aleatório novo
Usamos a variante aleatorizada. Por isso, duas assinaturas sobre a mesma mensagem têm aspeto diferente — ambas são válidas. Isso dificulta ataques que combinam medições no aparelho (consumo de energia, tempo) com assinaturas repetidas.
14. O que a McGesund faz concretamente com isto
O nível. O PQ-Dual-Diversified está disponível em Avaliações Premium e Sinais Premium. Um código QR leva então três assinaturas:
| Assinatura | Família | Local |
|---|---|---|
| Ed25519 | curva elíptica | no código QR |
| ML-DSA-87 | reticulados | carimbo no backend |
| SLH-DSA-SHA2-256f | funções de hash | carimbo no backend |
A assinatura. Uma assinatura SLH-DSA custa cerca de um quarto de segundo de tempo de cálculo. Para que o servidor continue entretanto acessível a todos os outros, o cálculo é feito em threads próprias em segundo plano.
A verificação. O navegador do leitor carrega ambos os carimbos e verifica-os com o mesmo módulo WASM que todas as outras assinaturas PQ. Nós fornecemos os dados; as contas são feitas no aparelho do visitante.
A comparação com o NIST. O nosso módulo passa nos vetores de teste oficiais do NIST para SLH-DSA-SHA2-256f: a partir das seeds do NIST resultam, byte a byte, as mesmas chaves, e todas as assinaturas deliberadamente manipuladas são rejeitadas. Pode verificá-lo por si no navegador, na página de testes do NIST.
A âncora temporal. Tal como em todas as chaves, a impressão digital da chave SLH-DSA é ancorada num bloco da Bitcoin através do OpenTimestamps.
15. SLH-DSA, ML-DSA e FALCON lado a lado
| Propriedade | FALCON (FN-DSA) | ML-DSA-87 | SLH-DSA-SHA2-256f |
|---|---|---|---|
| Base | reticulados NTRU | reticulados de módulo | apenas funções de hash |
| Norma | FIPS 206 (em preparação) | FIPS 204, final | FIPS 205, final |
| Chave pública | 1 793 B | 2 592 B | 64 B |
| Assinatura | 1 280 B | 4 627 B | 49 856 B |
| Assinar | milissegundos | milissegundos | aprox. 0,26 s |
| Verificar | milissegundos | milissegundos | milissegundos |
| Hipótese | problema de reticulados | problema de reticulados | função de hash |
Em resumo: o SLH-DSA é o mais lento e o maior dos três esquemas — e o mais conservador. Não aposta em nada que todas as outras assinaturas não pressuponham já de qualquer forma: uma função de hash segura.
16. Uma frase para levar
Quem possui a seed secreta assina num quarto de segundo. Quem não a possui teria de inverter o SHA-256 ou o SHA-512 — mesmo com um computador quântico.
Para o leitor de uma avaliação, isso significa: mesmo que um dia todos os esquemas de reticulados viessem a cair, esta assinatura continua verificável. Não tem de acreditar em nós. Pode fazer as contas.