O que pode fazer um atacante que conhece o código do firmware mas não tem acesso aos segredos do dispositivo.
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:
owner_secret (32 bytes) — apenas na NVS do controlador e no telefone do proprietário. É derivada por ECDH X25519 no emparelhamento e nunca viaja em claro pelo ar.guest_key — no telefone do convidado. Chega do servidor dentro de um pacote por um canal WSS seguro, nunca por BLE.| Attack | Defence |
|---|---|
| Forjar um FIRE | HMAC-SHA256. Sem owner_secret nem guest_key isso é um 2128 de força bruta — inviável. |
| Forjar uma escrita TIME | O mesmo HMAC sobre bleId + epochMs. |
| Repetir um anúncio intercetado | O contador é monótono: numa repetição ou diminuição o HMAC não confere. |
| Repetir um fire intercetado | O anel de nonces invalida os já usados. |
| Atrasar o relógio do controlador | O roquete: qualquer TIME < roquete é rejeitado. O roquete vive na NVS e sobrevive a um corte de energia. |
| Usar um pacote expirado | Depois 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 fora | Um 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 primeiro | Após o emparelhamento o controlador guarda owner_secret na NVS e não volta ao modo de emparelhamento sem reposição de fábrica. |
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.
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.
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.
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.
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.
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.
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.
Reforço padrão para uso industrial:
Nada disto é crítico para os cenários atuais, mas as instalações críticas merecem esse reforço.