/**
  * @param claimedAddress an IP/port based RemoteHostId, or null if unknown
  * @param remoteHostId non-null, == claimedAddress if direct, or a hash-based one if indirect
  * @param addr non-null
  */
 public OutboundEstablishState(
     RouterContext ctx,
     RemoteHostId claimedAddress,
     RemoteHostId remoteHostId,
     RouterIdentity remotePeer,
     SessionKey introKey,
     UDPAddress addr,
     DHSessionKeyBuilder.Factory dh) {
   _context = ctx;
   _log = ctx.logManager().getLog(OutboundEstablishState.class);
   if (claimedAddress != null) {
     _bobIP = claimedAddress.getIP();
     _bobPort = claimedAddress.getPort();
   } else {
     // _bobIP = null;
     _bobPort = -1;
   }
   _claimedAddress = claimedAddress;
   _remoteHostId = remoteHostId;
   _remotePeer = remotePeer;
   _introKey = introKey;
   _queuedMessages = new LinkedBlockingQueue<OutNetMessage>();
   _establishBegin = ctx.clock().now();
   _remoteAddress = addr;
   _introductionNonce = -1;
   _keyFactory = dh;
   if (addr.getIntroducerCount() > 0) {
     if (_log.shouldLog(Log.DEBUG))
       _log.debug(
           "new outbound establish to " + remotePeer.calculateHash() + ", with address: " + addr);
     _currentState = OutboundState.OB_STATE_PENDING_INTRO;
   } else {
     _currentState = OutboundState.OB_STATE_UNKNOWN;
   }
 }
  /**
   * Verify: Alice's IP + Alice's port + Bob's IP + Bob's port + Alice's new relay tag + Bob's
   * signed on time Caller must synch on this.
   */
  private boolean verifySessionCreated() {
    byte signed[] =
        new byte
            [256
                + 256 // X + Y
                + _aliceIP.length
                + 2
                + _bobIP.length
                + 2
                + 4 // sent relay tag
                + 4 // signed on time
            ];

    int off = 0;
    System.arraycopy(_sentX, 0, signed, off, _sentX.length);
    off += _sentX.length;
    System.arraycopy(_receivedY, 0, signed, off, _receivedY.length);
    off += _receivedY.length;
    System.arraycopy(_aliceIP, 0, signed, off, _aliceIP.length);
    off += _aliceIP.length;
    DataHelper.toLong(signed, off, 2, _alicePort);
    off += 2;
    System.arraycopy(_bobIP, 0, signed, off, _bobIP.length);
    off += _bobIP.length;
    DataHelper.toLong(signed, off, 2, _bobPort);
    off += 2;
    DataHelper.toLong(signed, off, 4, _receivedRelayTag);
    off += 4;
    DataHelper.toLong(signed, off, 4, _receivedSignedOnTime);
    boolean valid =
        _context
            .dsa()
            .verifySignature(_receivedSignature, signed, _remotePeer.getSigningPublicKey());
    if (_log.shouldLog(Log.DEBUG) || (_log.shouldLog(Log.WARN) && !valid)) {
      StringBuilder buf = new StringBuilder(128);
      buf.append("Signed sessionCreated:");
      buf.append(" Alice: ").append(Addresses.toString(_aliceIP, _alicePort));
      buf.append(" Bob: ").append(Addresses.toString(_bobIP, _bobPort));
      buf.append(" RelayTag: ").append(_receivedRelayTag);
      buf.append(" SignedOn: ").append(_receivedSignedOnTime);
      buf.append(" signature: ").append(Base64.encode(_receivedSignature.getData()));
      if (valid) _log.debug(buf.toString());
      else if (_log.shouldLog(Log.WARN)) _log.warn("INVALID: " + buf.toString());
    }
    return valid;
  }