class Net::SSH::Authentication::Certificate

Class for representing an SSH certificate.

cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/usr.bin/ssh/PROTOCOL.certkeys?rev=1.10&content-type=text/plain

Attributes

critical_options[RW]
extensions[RW]
key[RW]
key_id[RW]
nonce[RW]
reserved[RW]
serial[RW]
signature[RW]
signature_key[RW]
type[RW]
valid_after[RW]
valid_before[RW]
valid_principals[RW]

Public Class Methods

read_certblob(buffer, type) click to toggle source

Read a certificate blob associated with a key of the given type.

# File lib/net/ssh/authentication/certificate.rb, line 25
def self.read_certblob(buffer, type)
  cert = Certificate.new
  cert.nonce = buffer.read_string
  cert.key = buffer.read_keyblob(type)
  cert.serial = buffer.read_int64
  cert.type = type_symbol(buffer.read_long)
  cert.key_id = buffer.read_string
  cert.valid_principals = buffer.read_buffer.read_all(&:read_string)
  cert.valid_after = Time.at(buffer.read_int64)

  cert.valid_before = if RUBY_PLATFORM == "java"
                        # 0x20c49ba5e353f7 = 0x7fffffffffffffff/1000, the largest value possible for JRuby
                        # JRuby Time.at multiplies the arg by 1000, and then stores it in a signed long.
                        # 0x20c49ba2d52500 = 292278993-01-01 00:00:00 +0000
                        # JRuby 9.1 does not accept the year 292278994 because of edge cases (https://github.com/JodaOrg/joda-time/issues/190)
                        Time.at([0x20c49ba2d52500, buffer.read_int64].min)
                      else
                        Time.at(buffer.read_int64)
                      end

  cert.critical_options = read_options(buffer)
  cert.extensions = read_options(buffer)
  cert.reserved = buffer.read_string
  cert.signature_key = buffer.read_buffer.read_key
  cert.signature = buffer.read_string
  cert
end

Private Class Methods

read_options(buffer) click to toggle source
# File lib/net/ssh/authentication/certificate.rb, line 109
def self.read_options(buffer)
  names = []
  options = buffer.read_buffer.read_all do |b|
    name = b.read_string
    names << name
    data = b.read_string
    data = Buffer.new(data).read_string unless data.empty?
    [name, data]
  end

  raise ArgumentError, "option/extension names must be in sorted order" if names.sort != names

  Hash[options]
end
type_symbol(type) click to toggle source
# File lib/net/ssh/authentication/certificate.rb, line 125
def self.type_symbol(type)
  types = { 1 => :user, 2 => :host }
  raise ArgumentError("unsupported type: #{type}") unless types.include?(type)

  types.fetch(type)
end

Public Instance Methods

fingerprint() click to toggle source
# File lib/net/ssh/authentication/certificate.rb, line 81
def fingerprint
  key.fingerprint
end
sign(key, sign_nonce = nil) click to toggle source
# File lib/net/ssh/authentication/certificate.rb, line 97
def sign(key, sign_nonce = nil)
  cert = clone
  cert.sign!(key, sign_nonce)
end
sign!(key, sign_nonce = nil) click to toggle source

Signs the certificate with key.

# File lib/net/ssh/authentication/certificate.rb, line 86
def sign!(key, sign_nonce = nil)
  # ssh-keygen uses 32 bytes of nonce.
  self.nonce = sign_nonce || SecureRandom.random_bytes(32)
  self.signature_key = key
  self.signature = Net::SSH::Buffer.from(
    :string, key.ssh_signature_type,
    :mstring, key.ssh_do_sign(to_blob_without_signature)
  ).to_s
  self
end
signature_valid?() click to toggle source

Checks whether the certificate’s signature was signed by signature key.

# File lib/net/ssh/authentication/certificate.rb, line 103
def signature_valid?
  buffer = Buffer.new(signature)
  sig_format = buffer.read_string
  signature_key.ssh_do_verify(buffer.read_string, to_blob_without_signature, host_key: sig_format)
end
ssh_do_sign(data, sig_alg = nil) click to toggle source
# File lib/net/ssh/authentication/certificate.rb, line 69
def ssh_do_sign(data, sig_alg = nil)
  key.ssh_do_sign(data, sig_alg)
end
ssh_do_verify(sig, data, options = {}) click to toggle source
# File lib/net/ssh/authentication/certificate.rb, line 73
def ssh_do_verify(sig, data, options = {})
  key.ssh_do_verify(sig, data, options)
end
ssh_signature_type() click to toggle source
# File lib/net/ssh/authentication/certificate.rb, line 57
def ssh_signature_type
  key.ssh_type
end
ssh_type() click to toggle source
# File lib/net/ssh/authentication/certificate.rb, line 53
def ssh_type
  key.ssh_type + "-cert-v01@openssh.com"
end
to_blob() click to toggle source

Serializes the certificate (and key).

# File lib/net/ssh/authentication/certificate.rb, line 62
def to_blob
  Buffer.from(
    :raw, to_blob_without_signature,
    :string, signature
  ).to_s
end
to_pem() click to toggle source
# File lib/net/ssh/authentication/certificate.rb, line 77
def to_pem
  key.to_pem
end

Private Instance Methods

key_without_type() click to toggle source
# File lib/net/ssh/authentication/certificate.rb, line 165
def key_without_type
  # key.to_blob gives us e.g. "ssh-rsa,<key>" but we just want "<key>".
  tmp = Buffer.new(key.to_blob)
  tmp.read_string # skip the underlying key type
  tmp.read
end
options_to_blob(options) click to toggle source
# File lib/net/ssh/authentication/certificate.rb, line 172
def options_to_blob(options)
  options.keys.sort.inject(Buffer.new) do |b, name|
    b.write_string(name)
    data = options.fetch(name)
    data = Buffer.from(:string, data).to_s unless data.empty?
    b.write_string(data)
  end.to_s
end
ssh_time(t) click to toggle source
# File lib/net/ssh/authentication/certificate.rb, line 142
def ssh_time(t)
  # Times in certificates are represented as a uint64.
  [[t.to_i, 0].max, 2 << 64 - 1].min
end
to_blob_without_signature() click to toggle source
# File lib/net/ssh/authentication/certificate.rb, line 147
def to_blob_without_signature
  Buffer.from(
    :string, ssh_type,
    :string, nonce,
    :raw, key_without_type,
    :int64, serial,
    :long, type_value(type),
    :string, key_id,
    :string, valid_principals.inject(Buffer.new) { |acc, elem| acc.write_string(elem) }.to_s,
    :int64, ssh_time(valid_after),
    :int64, ssh_time(valid_before),
    :string, options_to_blob(critical_options),
    :string, options_to_blob(extensions),
    :string, reserved,
    :string, signature_key.to_blob
  ).to_s
end
type_value(type) click to toggle source
# File lib/net/ssh/authentication/certificate.rb, line 135
def type_value(type)
  types = { user: 1, host: 2 }
  raise ArgumentError("unsupported type: #{type}") unless types.include?(type)

  types.fetch(type)
end