secureOutbound method

  1. @override
Future<SecuredConnection> secureOutbound(
  1. TransportConn connection
)
override

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);
  }
}