feat(brahman-handshake): Fase 3 — trust remoto via firma Ed25519
Cuarto paso del plan "el encuentro entre Entes no se restringe a
local". Cierra la falla de seguridad que dejaba la red P2P abierta:
hasta ahora un atacante que pudiera dial-ar al multiaddr del Init
podia registrar cualquier Card con cualquier label/flow. Fase 3
exige que el Hello via libp2p venga firmado con la misma keypair
Ed25519 que produce el peer_id autenticado por Noise.
Modelo de trust:
- Local Unix: SO_PEERCRED del kernel autentica. Firma opcional pero
verificada si presente (defensa en profundidad).
- Remoto libp2p: firma obligatoria; public key del Hello debe derivar
al peer_id autenticado por Noise. Si falta o no coincide ->
HandshakeError::Unauthorized.
Wire:
- Hello.signature: Option<HelloSignature> (default None, backwards
compat para path Unix).
- HelloSignature { public_key: Vec<u8>, signature: Vec<u8> } —
public_key en formato canonico libp2p (encode_protobuf), firma
Ed25519 sobre (SIGNATURE_VERSION, WireCard, Option<WitInterface>)
serializado postcard.
Nuevo modulo signature:
- sign_hello / verify_hello con SignatureError tipado.
- 4 unit tests: roundtrip, peer mismatch, card tampered, sig flipped.
Server:
- Session<S> gana expected_peer: Option<PeerId>.
- session_from_libp2p_stream(stream, peer) para path remoto;
session_from_stream sin peer para Unix.
- do_handshake exige firma + verifica peer match si expected_peer.
Client:
- connect_with_stream_signed(stream, card, wit, &Keypair) (nuevo).
- connect_libp2p ahora requiere &Keypair (breaking change).
BrahmanNet:
- Almacena Arc<Keypair> internamente; expose keypair() accessor.
Truco: ed25519::Keypair SI es Clone, se duplica para que swarm
(Noise) y caller (signing) compartan identidad sin copiar bytes.
- with_keypair rechaza no-Ed25519.
Tests: 4 unit signature + 1 E2E negativo nuevo
(libp2p_handshake_rejects_mismatched_signing_key) + E2E positivo
ya pasaba con keypair correcta. 90+ tests verdes en
brahman-handshake/brahman-net/brahman-card/minga-p2p.
Lo que cierra: la cadena completa de discovery + connect + trust
funciona cross-machine sin paths hardcodeados ni confianza
implicita. Brahman-net es una malla publicamente dial-able CON
autenticacion criptografica end-to-end.
Pendientes futuros: stop_providing en cleanup, wire de Arje con
ServerConfig.net configurado, allowlist/denylist de peers,
persistencia de la keypair entre reboots.
This commit is contained in:
@@ -17,6 +17,14 @@ pub type SessionId = Ulid;
|
||||
/// la convierte a `Card` para uso interno. Opcionalmente, una
|
||||
/// `WitInterface` ya extraída — si está presente, el módulo es
|
||||
/// "consciente" y el server lo registra como `ResolvedCard::from_conscious`.
|
||||
///
|
||||
/// **Firma (Fase 3, trust remoto)**: el campo `signature` es
|
||||
/// obligatorio para conexiones libp2p (donde el server exige que la
|
||||
/// public key derive al `peer_id` autenticado por Noise) y opcional
|
||||
/// para Unix socket (donde SO_PEERCRED del kernel ya provee
|
||||
/// autenticación). La firma cubre los bytes postcard de
|
||||
/// `(WireCard, Option<WitInterface>)` — ver
|
||||
/// [`HelloSignature::sign_payload`].
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Hello {
|
||||
/// Versión del schema de Card que el cliente sigue.
|
||||
@@ -29,6 +37,22 @@ pub struct Hello {
|
||||
/// `brahman-card-wit`). `None` si el módulo es agnóstico.
|
||||
#[serde(default)]
|
||||
pub wit: Option<WitInterface>,
|
||||
/// Firma Ed25519 sobre `(card, wit)`. Requerida para conexiones
|
||||
/// remotas (libp2p); opcional para Unix socket. Ver módulo
|
||||
/// [`super::signature`] para construcción y verificación.
|
||||
#[serde(default)]
|
||||
pub signature: Option<HelloSignature>,
|
||||
}
|
||||
|
||||
/// Firma de un Hello. La `public_key` viaja en el formato canónico
|
||||
/// libp2p (protobuf) — el verificador la decodifica y compara su
|
||||
/// `peer_id` derivado con la identidad libp2p autenticada por Noise.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct HelloSignature {
|
||||
/// Public key del firmante, encoded como `libp2p::identity::PublicKey::encode_protobuf()`.
|
||||
pub public_key: Vec<u8>,
|
||||
/// Bytes de la firma Ed25519 sobre el payload canonical.
|
||||
pub signature: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Respuesta del servidor a un `Hello` aceptado.
|
||||
|
||||
Reference in New Issue
Block a user