Transporte Criptografado
Como a BOLT 8 cria uma sessão Noise autenticada entre dois nós
Lightning · Técnico
Antes de qualquer mensagem Wire aparecer, dois nós Lightning precisam criar uma sessão cifrada e autenticada. Esse e o papel da BOLT 8: ela define o transporte criptografado que fica entre o socket de rede e os bytes plaintext da BOLT 1.
A Lightning usa o Noise Protocol Framework, na variante Noise_XK_secp256k1_ChaChaPoly_SHA256. O iniciador ja precisa conhecer o node id do respondente, uma chave publica secp256k1 de longo prazo. Depois de tres atos de handshake, os dois lados derivam chaves de envio e recebimento e passam a trocar frames AEAD.
Esta pagina fala da camada de transporte entre peers. Ela nao define o formato das mensagens Lightning internas; depois da decriptacao, a mensagem resultante ainda precisa ser interpretada pelo Protocolo Wire.
Conteúdo
Onde o transporte entra
Uma conexao Lightning tem camadas. TCP, Tor ou outro transporte de rede entrega bytes. A BOLT 8 transforma esses bytes em uma sessao criptografada. So depois disso a BOLT 1 aparece, com mensagens como init, ping, open_channel e update_add_htlc.
socket de rede
-> BOLT 8: handshake Noise + frames cifrados
-> BOLT 1: type + payload da mensagem Lightning
-> BOLT 2/4/7/11: significado especifico do payload Essa separacao importa para debugging. Se o MAC do frame falha, o problema esta no transporte. Se o frame decripta, mas o tipo da mensagem e desconhecido ou o payload viola o schema, o problema esta no wire protocol ou no BOLT especifico daquela mensagem.
Objetivos de segurança
O transporte criptografado existe para proteger a conversa entre dois peers. Ele fornece:
- Confidencialidade: observadores da rede nao leem mensagens de canal, gossip privado, pings ou erros.
- Autenticacao: o iniciador confirma que fala com o node id que pretendia contatar.
- Integridade: alteracao de bytes durante handshake ou troca de mensagens causa falha de MAC.
- Forward secrecy de sessao: chaves efemeras novas por conexao reduzem o dano se uma chave de sessao futura vazar.
- Backward secrecy operacional: rotacao de chaves limita a utilidade de chaves antigas em sessoes longas.
Isso nao torna a Lightning anonima. O peer ainda conhece o IP ou circuito de rede usado, o node id com quem esta conectado, timing, volume de trafego e mensagens que ele mesmo processa. Privacidade de rota, probing e metadados sao tratados em outras camadas, como roteamento onion e seguranca e privacidade.
Identidade do nó
O node id de um no Lightning e uma chave publica secp256k1 comprimida. Em enderecos publicos, ele costuma aparecer como:
node_id@host:porta O iniciador precisa desse node id antes de conectar. Isso explica o K em Noise_XK: a chave estatica do respondente e known, conhecida pelo iniciador. O respondente, por outro lado, ainda nao sabe a chave estatica do iniciador; ela e enviada cifrada no terceiro ato. Esse e o X: a chave estatica do iniciador e transmitida durante o handshake.
O respondente nao envia sua chave estatica durante o handshake. Ele prova controle dessa chave implicitamente: se nao tiver a chave privada correspondente ao node id conhecido pelo iniciador, nao consegue produzir/verificar os MACs corretos.
Instanciação Noise
A BOLT 8 fixa a variante exata de Noise. Uma implementacao nao deve escolher primitivas parecidas por conta propria.
| Item | Valor | Função |
|---|---|---|
| Nome do protocolo | Noise_XK_secp256k1_ChaChaPoly_SHA256 | Define a variante exata do Noise usada pela Lightning. |
| Curva | secp256k1 | A mesma familia de chaves usada por Bitcoin; node ids sao chaves publicas comprimidas. |
| Hash | SHA-256 | Inicializa e atualiza o handshake hash e participa do HKDF. |
| KDF | HKDF | Mistura saidas ECDH na chaining key e deriva chaves temporarias e finais. |
| AEAD | ChaCha20-Poly1305 | Cifra e autentica tags do handshake, tamanho de frame e mensagens Lightning. |
| Prologue | lightning | String fixa misturada ao estado inicial para separar este protocolo de outros usos de Noise. |
O estado inicial mistura o nome do protocolo, a string lightning e a chave publica estatica do respondente. Se dois endpoints discordam sobre o nome do protocolo, a curva, o hash, a cifra ou o node id remoto, o handshake nao chega a uma sessao valida.
antes do Act One:
protocolName = "Noise_XK_secp256k1_ChaChaPoly_SHA256"
prologue = "lightning"
h = SHA256(protocolName)
ck = h
h = SHA256(h || prologue)
h = SHA256(h || responder_static_pubkey_compressed)
resultado:
- os dois lados concordam no mesmo protocolo
- o iniciador esta preso ao node id que queria contatar
- qualquer byte posterior altera o handshake hash Um detalhe facil de errar: os inteiros de nonce usados no ChaCha20-Poly1305 seguem a convencao do Noise. Eles sao codificados como 32 bits zero seguidos de um valor de 64 bits em little-endian, diferente de varias estruturas Lightning que usam big-endian.
Estado do handshake
Durante o handshake, cada lado mantem um pequeno conjunto de variaveis. Elas nao sao mensagens Lightning; sao estado criptografico local.
| Variável | Papel |
|---|---|
| ck | Chaining key. Acumula as saidas ECDH e depois deriva chaves de envio/recebimento. |
| h | Handshake hash. Acumula todos os dados do handshake e entra como associated data nos MACs. |
| temp_k1, temp_k2, temp_k3 | Chaves temporarias usadas durante os tres atos do handshake. |
| e | Par de chaves efemero da sessao. Deve ser novo e aleatorio a cada conexao. |
| s | Par de chaves estatico local, isto e, a identidade de longo prazo do no. |
| rs | Chave publica estatica remota conhecida pelo iniciador antes da conexao. |
O h nao e enviado na rede. Ele entra como associated data nos AEADs de handshake. Isso prende cada MAC ao transcript completo visto ate aquele ponto. Se um intermediario muda uma chave efemera, uma tag ou a chave estatica cifrada, a verificacao falha.
Handshake em três atos
O handshake tem 1,5 ida-e-volta e tres mensagens de tamanho fixo. Na notacao do Noise:
Noise_XK(s, rs):
<- s
...
-> e, es
<- e, ee
-> s, se | Ato | Direção | Noise | Tamanho | Conteúdo no fio |
|---|---|---|---|---|
| Act One | iniciador -> respondente | e, es | 50 bytes | versao 1 byte, chave publica efemera comprimida 33 bytes, tag Poly1305 16 bytes. |
| Act Two | respondente -> iniciador | e, ee | 50 bytes | versao 1 byte, chave publica efemera comprimida 33 bytes, tag Poly1305 16 bytes. |
| Act Three | iniciador -> respondente | s, se | 66 bytes | versao 1 byte, chave estatica do iniciador cifrada 33 bytes, tag da chave 16 bytes, tag final 16 bytes. |
Descricao longa do diagrama
O diagrama mostra dois nos, iniciador e respondente. No primeiro ato, o iniciador envia uma chave efemera e uma tag. No segundo, o respondente envia sua chave efemera e uma tag. No terceiro, o iniciador envia sua chave estatica cifrada e uma tag final. Setas indicam que, entre os atos, ambos misturam ECDH, chaining key e handshake hash para derivar chaves de sessao.
Exemplo de estrutura do primeiro ato, usando um valor de teste da BOLT 8:
00
036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f7
0df6086551151f58b8afe6c195782c6a
00 = versao do handshake
03...f7 = chave publica efemera comprimida do iniciador, 33 bytes
0d...6a = tag Poly1305, 16 bytes Os valores de teste da BOLT 8 usam chaves privadas fixas para tornar o resultado reproduzivel. Isso e apenas para teste. Uma conexao real precisa gerar chaves efemeras novas com aleatoriedade forte.
Frames cifrados
Quando o terceiro ato termina, os dois lados derivam duas chaves finais: uma para enviar e outra para receber. A partir dai, cada mensagem Lightning plaintext vira um frame cifrado.
| Parte | Tamanho | Significado |
|---|---|---|
| lc | 18 bytes | 2 bytes de tamanho cifrado + tag Poly1305 de 16 bytes. |
| c | len(m) + 16 bytes | mensagem Lightning cifrada + tag Poly1305 de 16 bytes. |
| packet | 18 + len(m) + 16 bytes | frame completo enviado no stream TCP ou Tor. |
| maximo | 65569 bytes | 2 + 16 + 65535 + 16, porque o plaintext maximo e 65535 bytes. |
Descricao longa do diagrama
O diagrama mostra um frame dividido em quatro partes: primeiro o tamanho cifrado da mensagem, depois uma tag de 16 bytes para esse tamanho, depois o corpo cifrado da mensagem Lightning e por fim uma tag de 16 bytes para o corpo. Uma seta indica que, apos a decriptacao, o corpo vira uma mensagem BOLT 1 com type e payload.
O tamanho plaintext e codificado como u16 big-endian antes de ser cifrado. Isso permite ate 65535 bytes de mensagem Lightning. Na rede, o prefixo cifrado tem 18 bytes, porque os 2 bytes de tamanho recebem uma tag Poly1305 de 16 bytes.
para enviar uma mensagem Lightning m:
1. l = len(m)
2. rejeitar se l > 65535
3. lc = encryptWithAD(sk, sn, "", u16be(l))
4. sn = sn + 1
5. c = encryptWithAD(sk, sn, "", m)
6. sn = sn + 1
7. escrever lc || c no socket
8. se sn == 1000, rotacionar sk e zerar sn para receber a proxima mensagem:
1. ler exatamente 18 bytes como lc
2. l = decryptWithAD(rk, rn, "", lc)
3. rn = rn + 1
4. ler exatamente l + 16 bytes como c
5. m = decryptWithAD(rk, rn, "", c)
6. rn = rn + 1
7. se rn == 1000, rotacionar rk e zerar rn
8. entregar m ao parser da BOLT 1 Depois que m e decriptada, o transporte acabou seu trabalho. O parser da BOLT 1 le os dois primeiros bytes como type e entrega o payload ao codigo apropriado.
Rotação de chaves
A BOLT 8 rotaciona chaves por sentido de trafego. A chave de envio (sk) e a chave de recebimento (rk) tem nonces separados e chaining keys separadas (sck e rck).
A regra operacional e: quando o nonce de uma chave chega a 1000, derive uma nova chave com HKDF, zere o nonce e esqueca a chave antiga. Como cada mensagem usa duas operacoes AEAD, uma para o tamanho e outra para o corpo, isso corresponde a uma rotacao a cada 500 mensagens naquele sentido.
ck', k' = HKDF(ck, k)
k = k'
ck = ck'
n = 0 Essa rotacao ajuda sessoes longas. Se uma chave atual vazar, ela nao deve permitir decriptar indefinidamente todo o trafego antigo daquele sentido.
Validação de implementação
Uma implementacao BOLT 8 precisa ser rígida. O transporte fica antes do parser de mensagens; aceitar bytes ambíguos aqui enfraquece todas as camadas acima.
| Condição | Comportamento seguro |
|---|---|
| Versao de handshake desconhecida | abortar a conexao. Versao 0 e o formato base; versoes desconhecidas nao devem ser aceitas. |
| Leitura curta | falhar. Cada ato tem tamanho fixo: 50, 50 e 66 bytes. |
| Chave publica invalida | falhar antes de ECDH; bytes de chave precisam ser ponto secp256k1 valido em formato comprimido. |
| MAC invalido | encerrar imediatamente. Nao envie mensagem Lightning parcialmente autenticada. |
| Mensagem plaintext maior que 65535 bytes | rejeitar; esse e o limite maximo antes da cifragem de transporte. |
| Nonce chegou a 1000 para uma chave | rotacionar a chave com HKDF e reiniciar o nonce daquele sentido. |
Para testar uma implementacao, use os vetores oficiais da BOLT 8. Eles cobrem handshake bem-sucedido, leitura curta, versao ruim, serializacao invalida de chave, tag ruim e cifragem de mensagens. Nao use esses vetores como chaves reais; eles existem justamente para repetir resultados em teste.
Limites reais
O transporte criptografado protege conteudo e autentica o peer, mas nao resolve todos os problemas de privacidade ou seguranca:
- um observador ainda pode ver IPs, conexoes, horarios, volume aproximado e duracao de sessoes, salvo quando outras camadas de rede reduzem esse vazamento;
- o peer remoto sabe seu node id depois do handshake, porque peers Lightning precisam operar canais e mensagens associadas a identidades;
- o comprimento plaintext e cifrado, mas trafego ainda revela padroes por timing e quantidade de frames;
- BOLT 8 nao substitui onion routing; um peer direto ainda ve as mensagens que deve processar;
- uma implementacao criptografica propria e area de risco. A propria BOLT recomenda bibliotecas validadas e amplamente usadas.
Armadilhas comuns
- Confundir BOLT 8 com BOLT 1. BOLT 8 entrega bytes decriptados; BOLT 1 interpreta
typeepayload. - Aceitar tamanho variavel nos atos do handshake. Act One e Act Two tem 50 bytes; Act Three tem 66 bytes.
- Dizer que a chave do respondente e enviada no handshake. Ela e pre-message conhecida pelo iniciador e fica implicita no MAC.
- Reutilizar chave efemera. Cada sessao precisa de chave efemera nova e aleatoria.
- Errar o nonce. O nonce usado pelo ChaCha20-Poly1305 segue a codificacao do Noise: 32 bits zero + 64 bits little-endian.
- Falar em rekey a cada 1000 mensagens. A rotacao ocorre quando o nonce da chave chega a 1000; como cada mensagem usa duas cifras AEAD, isso equivale a 500 mensagens por sentido.
- Tratar transporte cifrado como anonimato. Ele protege conteudo do link entre peers, nao remove metadados de rede nem informacao vista pelo peer.
Resumo
- BOLT 8 cria uma sessao Noise antes de qualquer mensagem Lightning plaintext.
- A variante e
Noise_XK_secp256k1_ChaChaPoly_SHA256, com prologuelightning. - O iniciador conhece a chave estatica do respondente; a chave estatica do iniciador e enviada cifrada no terceiro ato.
- Act One e Act Two tem 50 bytes; Act Three tem 66 bytes.
- Cada mensagem Lightning vira um frame com tamanho cifrado, tag, corpo cifrado e tag.
- Chaves sao rotacionadas por sentido quando o nonce chega a 1000 operacoes AEAD.
Mapa de dependências conceituais
Antes de ler esta página
Depois desta página
Referências técnicas usadas
- BOLT 8 — Encrypted and Authenticated Transport
- BOLT 1 — Base Protocol
- Protocolo Wire
- Curva Elíptica
- Função Hash
Com o transporte, o wire protocol, os canais, o onion routing, o gossip e a busca de caminho cobertos, o proximo passo e olhar a seguranca e a privacidade da rede como um sistema completo.