secureOutbound method
Secures an outbound connection
Implementation
@override
Future<SecuredConnection> secureOutbound(TransportConn connection) async {
if (_isDisposed) throw NoiseProtocolException('Protocol has been disposed');
try {
final staticKey = await crypto.X25519().newKeyPair();
final pattern = await NoiseXXPattern.create(true, staticKey);
await connection.write(await pattern.writeMessage(Uint8List(0))); // -> e
await pattern.readMessage(await connection.read(80)); // <- e, ee, s, es
await connection.write(await pattern.writeMessage(Uint8List(0))); // -> s, se
// Noise XX handshake complete, session keys derived.
// Now, exchange libp2p handshake payload over the encrypted channel.
// ADDED LOGGING
_log.finer('NoiseSecurity.secureOutbound: Handshake complete. Pattern keys:');
_log.finer(' - pattern.sendKey.hashCode: ${pattern.sendKey.hashCode}, pattern.sendKey.bytes: ${await pattern.sendKey.extractBytes()}');
_log.finer(' - pattern.recvKey.hashCode: ${pattern.recvKey.hashCode}, pattern.recvKey.bytes: ${await pattern.recvKey.extractBytes()}');
// Create a temporary SecuredConnection for exchanging the NoiseHandshakePayload
final tempSecuredConn = SecuredConnection(
connection,
pattern.sendKey,
pattern.recvKey,
securityProtocolId: _protocolString, // Temporary, won't have peer details yet
);
// 1. Prepare and send initiator's payload
final initiatorPayload = noise_pb.NoiseHandshakePayload();
initiatorPayload.identityKey = await _identityKey.publicKey.marshal();
// Initiator signs responder's static X25519 key
if (pattern.remoteStaticKey == null) throw NoiseProtocolException("Responder's remote static key is null during outbound secure");
final signature = await _identityKey.privateKey.sign(pattern.remoteStaticKey!);
initiatorPayload.identitySig = signature;
await _writeEncryptedPayload(tempSecuredConn, initiatorPayload.writeToBuffer());
// 2. Receive and process responder's payload
final responderEncryptedPayloadBytes = await _readLibp2pHandshakePayload(tempSecuredConn);
final responderPayload = noise_pb.NoiseHandshakePayload.fromBuffer(responderEncryptedPayloadBytes);
if (!responderPayload.hasIdentityKey()) throw NoiseProtocolException("Responder payload missing identity key");
// Assuming Ed25519 key as per Noise spec for libp2p identity
final remoteLibp2pPublicKey = ed25519_keys.Ed25519PublicKey.unmarshal(Uint8List.fromList(responderPayload.identityKey));
if (!responderPayload.hasIdentitySig()) throw NoiseProtocolException("Responder payload missing signature");
// Responder's signature is over initiator's static X25519 key
final initiatorStaticNoiseKey = await pattern.getStaticPublicKey();
final sigVerified = await remoteLibp2pPublicKey.verify(
initiatorStaticNoiseKey, Uint8List.fromList(responderPayload.identitySig));
if (!sigVerified) throw NoiseProtocolException("Failed to verify responder's signature");
final remotePeerId = await PeerId.fromPublicKey(remoteLibp2pPublicKey);
// ADDED LOGGING
_log.finer('NoiseSecurity.secureOutbound: Libp2p handshake payload processed. Finalizing SecuredConnection with pattern keys:');
_log.finer(' - pattern.sendKey.hashCode: ${pattern.sendKey.hashCode}, pattern.sendKey.bytes: ${await pattern.sendKey.extractBytes()}');
_log.finer(' - pattern.recvKey.hashCode: ${pattern.recvKey.hashCode}, pattern.recvKey.bytes: ${await pattern.recvKey.extractBytes()}');
return SecuredConnection(
connection,
pattern.sendKey,
pattern.recvKey,
establishedRemotePeer: remotePeerId,
establishedRemotePublicKey: remoteLibp2pPublicKey,
securityProtocolId: _protocolString,
);
} catch (e) {
await connection.close();
if (e is NoiseProtocolException) rethrow;
throw NoiseProtocolException('Failed to secure outbound connection', e);
}
}