Module: Zerg::Support::OpenSSH

Defined in:
lib/zerg_support/open_ssh.rb

Overview

Tools for managing openssh cryptographic material

Constant Summary collapse

RSA_COMPONENTS =

The components in a openssh .pub / known_host RSA public key.

['ssh-rsa', :e, :n]
DSA_COMPONENTS =

The components in a openssh .pub / known_host DSA public key.

['ssh-dss', :p, :q, :g, :pub_key]

Class Method Summary collapse

Class Method Details

.decode_mpi(mpi_str) ⇒ Object

Decodes an openssh-mpi-encoded integer.



93
94
95
# File 'lib/zerg_support/open_ssh.rb', line 93

def self.decode_mpi(mpi_str)
  mpi_str.unpack('C*').inject(0) { |acc, c| (acc << 8) | c }
end

.decode_pubkey(string) ⇒ Object

Decodes an openssh public key from the format of .pub & known_hosts files.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/zerg_support/open_ssh.rb', line 34

def self.decode_pubkey(string)
  components = unpack_pubkey_components Base64.decode64(string)
  case components.first
  when RSA_COMPONENTS.first
    ops = RSA_COMPONENTS.zip components
    key = OpenSSL::PKey::RSA.new
  when DSA_COMPONENTS.first
    ops = DSA_COMPONENTS.zip components
    key = OpenSSL::PKey::DSA.new
  else
    raise "Unsupported key type #{components.first}"
  end
  ops.each do |o|
    next unless o.first.kind_of? Symbol
    key.send "#{o.first}=", decode_mpi(o.last)
  end
  return key
end

.encode_mpi(n) ⇒ Object

Encodes an openssh-mpi-encoded integer.



98
99
100
101
102
103
# File 'lib/zerg_support/open_ssh.rb', line 98

def self.encode_mpi(n)
  chars, n = [], n.to_i
  chars << (n & 0xff) and n >>= 8 while n != 0
  chars << 0 if chars.empty? or chars.last >= 0x80
  chars.reverse.pack('C*')
end

.encode_pubkey(key) ⇒ Object

Encodes a key’s public part in the format found in .pub & known_hosts files.



19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/zerg_support/open_ssh.rb', line 19

def self.encode_pubkey(key)
  case key
  when OpenSSL::PKey::RSA
    components = RSA_COMPONENTS
  when OpenSSL::PKey::DSA
    components = DSA_COMPONENTS
  else
    raise "Unsupported key type #{key.class.name}"
  end
  components.map! { |c| c.kind_of?(Symbol) ? encode_mpi(key.send(c)) : c }
  # ruby tries to be helpful and adds new lines every 60 bytes :(
  [pack_pubkey_components(components)].pack('m').gsub("\n", '')
end

.first_line(string) ⇒ Object

Extracts the first line of a string.



71
72
73
# File 'lib/zerg_support/open_ssh.rb', line 71

def self.first_line(string)
  string[0, string.index(/\r|\n/) || string.len]
end

.key_from_string(serialized_key) ⇒ Object

Reads a serialized key from a string.



59
60
61
62
63
64
65
66
67
68
# File 'lib/zerg_support/open_ssh.rb', line 59

def self.key_from_string(serialized_key)
  header = first_line serialized_key
  if header.index 'RSA'
    OpenSSL::PKey::RSA.new serialized_key
  elsif header.index 'DSA'
    OpenSSL::PKey::DSA.new serialized_key
  else
    raise 'Unknown key type'
  end
end

.known_hosts_keys(io) ⇒ Object

Extracts the keys from a file of known_hosts format.



7
8
9
10
11
# File 'lib/zerg_support/open_ssh.rb', line 7

def self.known_hosts_keys(io)
  io.each_line do |line|
    
  end
end

.load_key(io) ⇒ Object

Loads a serialized key from an IO instance (File, StringIO).



54
55
56
# File 'lib/zerg_support/open_ssh.rb', line 54

def self.load_key(io)
  key_from_string io.read
end

.pack_pubkey_components(strings) ⇒ Object

Packs string components into an openssh-encoded pubkey.



88
89
90
# File 'lib/zerg_support/open_ssh.rb', line 88

def self.pack_pubkey_components(strings)
  (strings.map { |s| [s.length].pack('N') }).zip(strings).flatten.join
end

.unpack_pubkey_components(str) ⇒ Object

Unpacks the string components in an openssh-encoded pubkey.



76
77
78
79
80
81
82
83
84
85
# File 'lib/zerg_support/open_ssh.rb', line 76

def self.unpack_pubkey_components(str)
  cs = []
  i = 0
  while i < str.length
    len = str[i, 4].unpack('N').first
    cs << str[i + 4, len]
    i += 4 + len
  end
  return cs
end