class Net::SSH::Authentication::KeyManager

on disk.
hidden whether an identity comes from the ssh-agent or from a file
ssh-agent. Thus, from a client’s perspective it is completely
The KeyManager also uses the Agent class to encapsulate the
identities.
the KeyManager to do various private key operations using those
keys) that are available from the KeyManager, and then use
to a private key; instead, they grab a list of “identities” (public
private keys. In practice, the client should never need a reference
This class encapsulates all operations done by clients on a user’s

def add(key_file)

Add the given key_file to the list of key files that will be used.
def add(key_file)
  key_files.push(File.expand_path(key_file)).uniq!
  self
end

def add_key_data(key_data_)

Add the given key_file to the list of keys that will be used.
def add_key_data(key_data_)
  key_data.push(key_data_).uniq!
  self
end

def add_keycert(keycert_file)

Add the given keycert_file to the list of keycert files that will be used.
def add_keycert(keycert_file)
  keycert_files.push(File.expand_path(keycert_file)).uniq!
  self
end

def add_keycert_data(keycert_data_)

Add the given keycert_data to the list of keycerts that will be used.
def add_keycert_data(keycert_data_)
  keycert_data.push(keycert_data_).uniq!
  self
end

def agent

or if the agent is otherwise not available.
agent process. Returns nil if use of an SSH agent has been disabled,
Returns an Agent instance to use for communicating with an SSH
def agent
  return unless use_agent?
  @agent ||= Agent.connect(logger, options[:agent_socket_factory], options[:identity_agent])
rescue AgentNotAvailable
  @use_agent = false
  nil
end

def clear!

files.
appropriate to use if a client wishes to NOT use the default identity
of default identity files that are to be loaded, thus making it
Clear all knowledge of any loaded user keys. This also clears the list
def clear!
  key_files.clear
  key_data.clear
  keycert_data.clear
  known_identities.clear
  self
end

def each_identity

key_data.
from ssh-agent will be ignored unless it present in key_files or
If key manager was created with :keys_only option, any identity

first in the array, with other identities coming after.
ssh-agent. Note that identities from an ssh-agent are always listed
The origin of the identities may be from files on disk or from an
manager. As it finds one, it will then yield it to the caller.
Iterates over all available identities (public keys) known to this
def each_identity
  prepared_identities = prepare_identities_from_files + prepare_identities_from_data
  user_identities = load_identities(prepared_identities, false, true)
  if agent
    agent.identities.each do |key|
      corresponding_user_identity = user_identities.detect { |identity|
        identity[:public_key] && identity[:public_key].to_pem == key.to_pem
      }
      user_identities.delete(corresponding_user_identity) if corresponding_user_identity
      if !options[:keys_only] || corresponding_user_identity
        known_identities[key] = { from: :agent, identity: key }
        yield key
      end
    end
  end
  user_identities = load_identities(user_identities, !options[:non_interactive], false)
  user_identities.each do |identity|
    key = identity.delete(:public_key)
    known_identities[key] = identity
    yield key
  end
  known_identity_blobs = known_identities.keys.map(&:to_blob)
  keycerts.each do |keycert|
    next if known_identity_blobs.include?(keycert.to_blob)
    (_, corresponding_identity) = known_identities.detect { |public_key, _|
      public_key.to_pem == keycert.to_pem
    }
    if corresponding_identity
      known_identities[keycert] = corresponding_identity
      yield keycert
    end
  end
  self
end

def finish

closed when it will not be used in the immediate future.
reconnected. This method simply allows the client connection to be
loaded identities, in which case, the agent will be automatically
be used. Identities may still be requested and operations done on
Calling this does NOT indicate that the KeyManager will no longer

at this time.
connection is no longer needed. Any other open resources may be closed
This is used as a hint to the KeyManager indicating that the agent
def finish
  @agent.close if @agent
  @agent = nil
end

def initialize(logger, options = {})

is not false.
use the ssh-agent if it is running and the `:use_agent` option
Create a new KeyManager. By default, the manager will
def initialize(logger, options = {})
  self.logger = logger
  @key_files = []
  @key_data = []
  @keycert_files = []
  @keycert_data = []
  @use_agent = options[:use_agent] != false
  @known_identities = {}
  @agent = nil
  @options = options
end

def keycerts

Load keycerts from files and data.
def keycerts
  keycert_files.map { |keycert_file| KeyFactory.load_public_key(keycert_file) } +
    keycert_data.map { |data| KeyFactory.load_data_public_key(data) }
end

def load_identities(identities, ask_passphrase, ignore_decryption_errors)

Load prepared identities. Private key decryption errors ignored if ignore_decryption_errors
def load_identities(identities, ask_passphrase, ignore_decryption_errors)
  identities.map do |identity|
    case identity[:load_from]
    when :pubkey_file
      key = KeyFactory.load_public_key(identity[:pubkey_file])
      { public_key: key, from: :file, file: identity[:privkey_file] }
    when :privkey_file
      private_key = KeyFactory.load_private_key(
        identity[:privkey_file], options[:passphrase], ask_passphrase, options[:password_prompt]
      )
      key = private_key.send(:public_key)
      { public_key: key, from: :file, file: identity[:privkey_file], key: private_key }
    when :data
      private_key = KeyFactory.load_data_private_key(
        identity[:data], options[:passphrase], ask_passphrase, "<key in memory>", options[:password_prompt]
      )
      key = private_key.send(:public_key)
      { public_key: key, from: :key_data, data: identity[:data], key: private_key }
    else
      identity
    end
  rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError, OpenSSL::PKey::ECError, OpenSSL::PKey::PKeyError, ArgumentError => e
    if ignore_decryption_errors
      identity
    else
      process_identity_loading_error(identity, e)
      nil
    end
  rescue Exception => e
    process_identity_loading_error(identity, e)
    nil
  end.compact
end

def no_keys?

def no_keys?
  key_files.empty? && key_data.empty?
end

def prepare_identities_from_data

Prepared identities from user key_data, preserving their order and sources.
def prepare_identities_from_data
  key_data.map do |data|
    { load_from: :data, data: data }
  end
end

def prepare_identities_from_files

Prepares identities from user key_files for loading, preserving their order and sources.
def prepare_identities_from_files
  key_files.map do |file|
    if readable_file?(file)
      identity = {}
      cert_file = file + "-cert.pub"
      public_key_file = file + ".pub"
      if readable_file?(cert_file)
        identity[:load_from] = :pubkey_file
        identity[:pubkey_file] = cert_file
      elsif readable_file?(public_key_file)
        identity[:load_from] = :pubkey_file
        identity[:pubkey_file] = public_key_file
      else
        identity[:load_from] = :privkey_file
      end
      identity.merge(privkey_file: file)
    end
  end.compact
end

def process_identity_loading_error(identity, e)

def process_identity_loading_error(identity, e)
  case identity[:load_from]
  when :pubkey_file
    error { "could not load public key file `#{identity[:pubkey_file]}': #{e.class} (#{e.message})" }
  when :privkey_file
    error { "could not load private key file `#{identity[:privkey_file]}': #{e.class} (#{e.message})" }
  else
    raise e
  end
end

def readable_file?(path)

def readable_file?(path)
  File.file?(path) && File.readable?(path)
end

def sign(identity, data, sig_alg = nil)

blob" format.
will always return the signature in an SSH2-specified "signature
Regardless of the identity's origin or who does the signing, this

been loaded already) and will then be used to sign the data.
private key for the identity will be loaded from disk (if it hasn't
then the ssh-agent will be used to sign the data, otherwise the
identity. If the identity was originally obtained from an ssh-agent,
Sign the given data, using the corresponding private key of the given
def sign(identity, data, sig_alg = nil)
  info = known_identities[identity] or raise KeyManagerError, "the given identity is unknown to the key manager"
  if info[:key].nil? && info[:from] == :file
    begin
      info[:key] = KeyFactory.load_private_key(info[:file], options[:passphrase], !options[:non_interactive], options[:password_prompt])
    rescue OpenSSL::OpenSSLError, Exception => e
      raise KeyManagerError, "the given identity is known, but the private key could not be loaded: #{e.class} (#{e.message})"
    end
  end
  if info[:key]
    if sig_alg.nil?
      signed = info[:key].ssh_do_sign(data.to_s)
      sig_alg = identity.ssh_signature_type
    else
      signed = info[:key].ssh_do_sign(data.to_s, sig_alg)
    end
    return Net::SSH::Buffer.from(:string, sig_alg,
                                 :mstring, signed).to_s
  end
  if info[:from] == :agent
    raise KeyManagerError, "the agent is no longer available" unless agent
    case sig_alg
    when "rsa-sha2-512"
      return agent.sign(info[:identity], data.to_s, Net::SSH::Authentication::Agent::SSH_AGENT_RSA_SHA2_512)
    when "rsa-sha2-256"
      return agent.sign(info[:identity], data.to_s, Net::SSH::Authentication::Agent::SSH_AGENT_RSA_SHA2_256)
    else
      return agent.sign(info[:identity], data.to_s)
    end
  end
  raise KeyManagerError, "[BUG] can't determine identity origin (#{info.inspect})"
end

def use_agent=(use_agent)

connection to an agent is closed and the agent will not be used.
attempt will be made to use the ssh-agent. If false, any existing
Toggles whether the ssh-agent will be used or not. If true, an
def use_agent=(use_agent)
  finish if !use_agent
  @use_agent = use_agent
end

def use_agent?

Identifies whether the ssh-agent will be used or not.
def use_agent?
  @use_agent
end