require 'net/ssh/errors'
require 'net/ssh/transport/constants'
require 'net/ssh/transport/kex/diffie_hellman_group1_sha1'

module Net::SSH::Transport::Kex
  # A key-exchange service implementing the
  # "diffie-hellman-group-exchange-sha1" key-exchange algorithm.
  class DiffieHellmanGroupExchangeSHA1 < DiffieHellmanGroup1SHA1
    MINIMUM_BITS      = 1024
    MAXIMUM_BITS      = 8192

    private

    # Compute the number of bits needed for the given number of bytes.
    def compute_need_bits
      # for Compatibility: OpenSSH requires (need_bits * 2 + 1) length of parameter
      need_bits = data[:need_bytes] * 8 * 2 + 1

      data[:minimum_dh_bits] ||= MINIMUM_BITS

      if need_bits < data[:minimum_dh_bits]
        need_bits = data[:minimum_dh_bits]
      elsif need_bits > MAXIMUM_BITS
        need_bits = MAXIMUM_BITS
      end

      data[:need_bits] = need_bits
      data[:need_bytes] = need_bits / 8
    end

    # Returns the DH key parameters for the given session.
    def get_parameters
      compute_need_bits

      # request the DH key parameters for the given number of bits.
      buffer = Net::SSH::Buffer.from(:byte, KEXDH_GEX_REQUEST, :long, data[:minimum_dh_bits],
                                     :long, data[:need_bits], :long, MAXIMUM_BITS)
      connection.send_message(buffer)

      buffer = connection.next_message
      raise Net::SSH::Exception, "expected KEXDH_GEX_GROUP, got #{buffer.type}" unless buffer.type == KEXDH_GEX_GROUP

      p = buffer.read_bignum
      g = buffer.read_bignum

      [p, g]
    end

    # Returns the INIT/REPLY constants used by this algorithm.
    def get_message_types
      [KEXDH_GEX_INIT, KEXDH_GEX_REPLY]
    end

    # Build the signature buffer to use when verifying a signature from
    # the server.
    def build_signature_buffer(result)
      response = Net::SSH::Buffer.new
      response.write_string data[:client_version_string],
                            data[:server_version_string],
                            data[:client_algorithm_packet],
                            data[:server_algorithm_packet],
                            result[:key_blob]
      response.write_long MINIMUM_BITS,
                          data[:need_bits],
                          MAXIMUM_BITS
      response.write_bignum dh.p, dh.g, dh.pub_key,
                            result[:server_dh_pubkey],
                            result[:shared_secret]
      response
    end
  end
end
