Relé BLE — modelo de ameaças

O que pode fazer um atacante que conhece o código do firmware mas não tem acesso aos segredos do dispositivo.

Superfície de ataque

O firmware é público, por isso os formatos de pacote, os comprimentos e os algoritmos (X25519, HMAC-SHA256, HKDF) são conhecidos. O BLE é ar aberto, por isso os anúncios e as escritas GATT podem ser escutados. Nem no firmware nem no ar existe qualquer segredo.

Onde vivem as chaves:

O que um atacante não consegue fazer

AttackDefence
Forjar um FIREHMAC-SHA256. Sem owner_secret nem guest_key isso é um 2128 de força bruta — inviável.
Forjar uma escrita TIMEO mesmo HMAC sobre bleId + epochMs.
Repetir um anúncio intercetadoO contador é monótono: numa repetição ou diminuição o HMAC não confere.
Repetir um fire intercetadoO anel de nonces invalida os já usados.
Atrasar o relógio do controladorO roquete: qualquer TIME < roquete é rejeitado. O roquete vive na NVS e sobrevive a um corte de energia.
Usar um pacote expiradoDepois de o controlador funcionar, o roquete é pelo menos a última hora real. Num pacote expirado o validUntil fica abaixo do roquete, por isso o fire falha por validade.
Adiantar o relógio um ano e deixar todos os convidados de foraUm limite de +24 h por cada escrita TIME de convidado. O proprietário não tem limite — confia-se nele.
Emparelhar-se como segundo proprietário por cima do primeiroApós o emparelhamento o controlador guarda owner_secret na NVS e não volta ao modo de emparelhamento sem reposição de fábrica.

O que um atacante consegue fazer e quanto custa

1. Adiantar a hora — negação de serviço para convidados legítimos

Requires: um pacote de convidado válido e não expirado.

Action: sempre que estiver ao alcance, escrever um TIME que salta 24 horas.

Limit: +24 h por sessão, e é preciso um fire recente se a última sincronização foi há menos de uma hora. Ao fim de dez visitas o roquete está dez dias à frente e os tokens dos restantes convidados legítimos parecem expirados.

O reverso: o token do próprio atacante é avaliado com essa mesma hora falsa e por isso expira mais cedo.

O que fazer: o proprietário passa e escreve a hora real. O roquete não se mexe, porque não anda para trás, mas os novos tokens de convidado são emitidos com um validUntil atual e a partir daí funcionam para todos. Recuperar não exige mais do que renovar a validade no servidor.

Severity: uma negação de serviço temporária até o proprietário passar. Não é crítico.

2. Homem no meio durante o emparelhamento

Requires: estar fisicamente ao alcance do controlador na janela de 90 segundos após o início do emparelhamento.

Action: substituir no aperto de mão de emparelhamento a sua própria chave pública X25519.

Result: o controlador emparelha com o atacante e não com o proprietário. O proprietário julga o emparelhamento bem-sucedido mas não abre nada a partir do telefone.

Defesa atual: apenas a janela temporal e a presença física do proprietário — ele vê que o emparelhamento não ficou confirmado e repete-o.

Hardening: acrescentar um código de verificação simples — quatro dígitos no ecrã do controlador ou no Serial — que o proprietário confirma na aplicação. Hoje esse código não existe.

Severity: a janela é estreita e exige estar perto exatamente nesses 90 segundos. Difícil de conseguir na prática.

3. Reposição de fábrica física

Requires: acesso físico ao controlador e uma pressão longa do botão.

Action: A NVS é apagada, owner_secret é eliminada e o controlador volta ao modo de emparelhamento.

Result: o atacante emparelha o controlador consigo e o proprietário perde o acesso.

Defence: montar o controlador num sítio protegido — dentro de uma caixa, sob uma tampa.

Severity: o mesmo que arrancar um intercomunicador da porta — não é um problema criptográfico.

4. Extrair owner_secret por UART ou JTAG

Requires: acesso físico e um despejo da flash com arranque seguro e cifragem da flash desligados — hoje estão.

Action: ler a NVS onde está owner_secret.

Result: comprometimento total do dispositivo e de todos os tokens de convidado.

Defence: ativar o arranque seguro e a cifragem da flash do ESP32 (efuse, irreversível). Hoje não está ativado.

Severity: aceitável para uso doméstico. Em instalações críticas é preciso arranque seguro.

5. Ocupar ligações GATT — negação de serviço

Requires: estar ao alcance do BLE.

Action: ligar-se e nunca se desligar. O NimBLE suporta apenas um número limitado de ligações simultâneas.

Result: os clientes legítimos não conseguem ligar-se enquanto o atacante ocupa um lugar.

Defesa atual: apenas os tempos-limite do NimBLE.

Hardening: acrescentar um tempo de inatividade: se o cliente não fizer um FIRE válido em 5 segundos, cortar a ligação.

Severity: uma negação de serviço temporária enquanto o atacante estiver por perto.

6. Uma enxurrada de escritas inválidas (falsificação de ligação)

Action: bombardear o controlador com escritas de lixo.

Result: o controlador descarta-as e regista no Serial. O seu estado não muda e, assim que o atacante se vai, tudo funciona como sempre.

O que não é, de propósito, uma defesa

Três pontos parecem proteção e não são. Melhor dizê-los pelo nome do que deixar alguém apoiar-se neles.

O byte de estado no anúncio leva uma etiqueta de 16 bits. Chega contra uma corrupção acidental no ar e mais nada: um falsificador decidido percorre 16 bits em segundos. O byte só governa o indicador aberto/fechado no cartão e não concede qualquer direito, por isso a etiqueta continua curta — cada byte a mais custa bateria em cada despertar.

As chaves só para a aplicação assentam num segredo compilado dentro do APK. Quem desempacotar o ficheiro obtém o segredo e forja a atestação. Isso levanta a fasquia perante um cliente web ocasional, nada mais. O controlo real de uma chave dessas é prendê-la à impressão de um dispositivo — essa verificação corre no servidor e não depende de a aplicação guardar um segredo.

A validade de uma chave de convidado é garantida pelo próprio controlador no BLE: o token leva um prazo e o controlador tem relógio. Nos restantes tipos de objeto não há prazo nenhum — a chave vive até o proprietário a revogar. A aplicação não afirma o contrário, mas se algum dia surgir um limite de tempo na interface também para eles, terá de ser imposto no servidor e não desenhado no cartão.

Em resumo

Reforço padrão para uso industrial:

  1. Arranque seguro e cifragem da flash (proteção contra JTAG e despejos).
  2. Um código de verificação no fluxo de emparelhamento (proteção contra o homem no meio).
  3. Um tempo de inatividade nas ligações GATT (proteção contra a ocupação).
  4. Comparar assinaturas em tempo constante — feito no firmware e na aplicação: a anterior comparação byte a byte revelava, pelo tempo de resposta, quantos bytes de uma assinatura tinham sido acertados.

Nada disto é crítico para os cenários atuais, mas as instalações críticas merecem esse reforço.