class Net::SSH::Authentication::Agent

Written by Artūras Šlajus <arturas.slajus@gmail.com>
This class implements an agent for JRuby + Pageant.

def self.connect(logger=nil)

negotiates the agent protocol version, and returns the agent object.
Instantiates a new agent object, connects to a running SSH agent,
def self.connect(logger=nil)
  agent = new(logger)
  agent.connect!
  agent
end

def self.connect(logger=nil)

negotiates the agent protocol version, and returns the agent object.
Instantiates a new agent object, connects to a running SSH agent,
def self.connect(logger=nil)
  agent = new(logger)
  agent.connect!
  agent.negotiate!
  agent
end

def agent_failed(type)

the agent, and +false+ otherwise.
Returns +true+ if the parameter indicates a "failure" response from
def agent_failed(type)
  type == SSH_AGENT_FAILURE ||
    type == SSH2_AGENT_FAILURE ||
    type == SSH_COM_AGENT2_FAILURE
end

def agent_socket_factory

Returns the agent socket factory to use.
def agent_socket_factory
  if Net::SSH::Authentication::PLATFORM == :win32
    Pageant::socket_factory
  else
    UNIXSocket
  end
end

def close

query the agent.
Simulate agent close. This agent reference is no longer able to
def close
  @agent_proxy = nil
end

def close

query the agent.
Closes this socket. This agent reference is no longer able to
def close
  @socket.close
end

def connect!

(it only supports the ssh-agent distributed by OpenSSH).
socket reports that it is an SSH2-compatible agent, this will fail
given by the attribute writers. If the agent on the other end of the
Connect to the agent process using the socket factory and socket name
def connect!
  debug { "connecting to Pageant ssh-agent (via java connector)" }
  @agent_proxy = JRubyPageant.create
  unless @agent_proxy.is_running
    raise AgentNotAvailable, "Pageant is not running!"
  end
  debug { "connection to Pageant ssh-agent (via java connector) succeeded" }
rescue AgentProxyException => e
  error { "could not connect to Pageant ssh-agent (via java connector)" }
  raise AgentNotAvailable, e.message, e.backtrace
end

def connect!

(it only supports the ssh-agent distributed by OpenSSH).
socket reports that it is an SSH2-compatible agent, this will fail
given by the attribute writers. If the agent on the other end of the
Connect to the agent process using the socket factory and socket name
def connect!
  begin
    debug { "connecting to ssh-agent" }
    @socket = agent_socket_factory.open(ENV['SSH_AUTH_SOCK'])
  rescue
    error { "could not connect to ssh-agent" }
    raise AgentNotAvailable, $!.message
  end
end

def identities

to the comment returned by the agent for that key.
Each key returned is augmented with a +comment+ property which is set
Return an array of all identities (public keys) known to the agent.
def identities
  debug { "getting identities from Pageant" }
  @agent_proxy.get_identities.map do |identity|
    blob = identity.get_blob
    key = Buffer.new(String.from_java_bytes(blob)).read_key
    key.extend(Key)
    key.java_blob = blob
    key.comment = String.from_java_bytes(identity.get_comment)
    key
  end
rescue AgentProxyException => e
  raise AgentError, "Cannot get identities: #{e.message}", e.backtrace
end

def identities

to the comment returned by the agent for that key.
Each key returned is augmented with a +comment+ property which is set
Return an array of all identities (public keys) known to the agent.
def identities
  type, body = send_and_wait(SSH2_AGENT_REQUEST_IDENTITIES)
  raise AgentError, "could not get identity count" if agent_failed(type)
  raise AgentError, "bad authentication reply: #{type}" if type != SSH2_AGENT_IDENTITIES_ANSWER
  identities = []
  body.read_long.times do
    key = Buffer.new(body.read_string).read_key
    key.extend(Comment)
    key.comment = body.read_string
    identities.push key
  end
  return identities
end

def initialize(logger=nil)

report status.
Creates a new Agent object, using the optional logger instance to
def initialize(logger=nil)
  self.logger = logger
end

def initialize(logger=nil)

report status.
Creates a new Agent object, using the optional logger instance to
def initialize(logger=nil)
  self.logger = logger
end

def negotiate!

if the version could not be negotiated successfully.
Attempts to negotiate the SSH agent protocol version. Raises an error
def negotiate!
  # determine what type of agent we're communicating with
  type, body = send_and_wait(SSH2_AGENT_REQUEST_VERSION, :string, Transport::ServerVersion::PROTO_VERSION)
  if type == SSH2_AGENT_VERSION_RESPONSE
    raise NotImplementedError, "SSH2 agents are not yet supported"
  elsif type != SSH_AGENT_RSA_IDENTITIES_ANSWER1 && type != SSH_AGENT_RSA_IDENTITIES_ANSWER2
    raise AgentError, "unknown response from agent: #{type}, #{body.to_s.inspect}"
  end
end

def read_packet

is returned as a Net::SSH::Buffer).
tuple consisting of the packet type, and the packet's body (which
Read the next packet from the agent. This will return a two-part
def read_packet
  buffer = Net::SSH::Buffer.new(@socket.read(4))
  buffer.append(@socket.read(buffer.read_long))
  type = buffer.read_byte
  debug { "received agent packet #{type} len #{buffer.length-4}" }
  return type, buffer
end

def send_and_wait(type, *args)

(See #send_packet and #read_packet).
Send the given packet and return the subsequent reply from the agent.
def send_and_wait(type, *args)
  send_packet(type, *args)
  read_packet
end

def send_packet(type, *args)

Send a new packet of the given type, with the associated data.
def send_packet(type, *args)
  buffer = Buffer.from(*args)
  data = [buffer.length + 1, type.to_i, buffer.to_s].pack("NCA*")
  debug { "sending agent request #{type} len #{buffer.length}" }
  @socket.send data, 0
end

def sign(key, data)

signature is returned in SSH2 format.
Using the agent and the given public key, sign the given data. The
def sign(key, data)
  signed = @agent_proxy.sign(key.java_blob, data.to_java_bytes)
  String.from_java_bytes(signed)
rescue AgentProxyException => e
  raise AgentError,
    "agent could not sign data with requested identity: #{e.message}",
    e.backtrace
end

def sign(key, data)

signature is returned in SSH2 format.
Using the agent and the given public key, sign the given data. The
def sign(key, data)
  type, reply = send_and_wait(SSH2_AGENT_SIGN_REQUEST, :string, Buffer.from(:key, key), :string, data, :long, 0)
  if agent_failed(type)
    raise AgentError, "agent could not sign data with requested identity"
  elsif type != SSH2_AGENT_SIGN_RESPONSE
    raise AgentError, "bad authentication response #{type}"
  end
  return reply.read_string
end