class SshSig::Blob

Attributes

hash_algorithm[R]
namespace[R]
signature[R]

Public Class Methods

from_armor(armor) click to toggle source
# File lib/ssh_sig/blob.rb, line 31
def self.from_armor(armor)
  from_bytes(armor_to_blob(armor))
end
from_bytes(blob) click to toggle source

decode_blob parses the binary signature data as described in github.com/openssh/openssh-portable/blob/e665ed2d0c24fe11d5470ce72fa1e187377d3fc4/PROTOCOL.sshsig

byte MAGIC_PREAMBLE uint32 SIG_VERSION string publickey string namespace string reserved string hash_algorithm string signature

# File lib/ssh_sig/blob.rb, line 45
def self.from_bytes(blob)
  buf = ::Net::SSH::Buffer.new(blob)

  preamble = buf.read!(6)

  raise DecodeError, 'Invalid magic preamble' unless preamble == MAGIC_PREAMBLE

  version = read_uint64(buf)

  raise DecodeError, 'Unsupported signature version' unless version == SIG_VERSION

  public_key = buf.read_key

  raise DecodeError, 'Signature is missing public key' if public_key.nil?

  namespace = buf.read_string

  raise DecodeError, 'Signature is missing namespace' if namespace.nil?

  # Read past the reserved value and ignore it.
  buf.read_string

  hash_algorithm = buf.read_string

  raise DecodeError, 'Signature is missing hash algorithm' if hash_algorithm.nil?
  raise DecodeError, 'Hash algorithm is not supported' unless hash_algorithm_allowed?(hash_algorithm)

  signature_raw = buf.read_string

  raise DecodeError, 'Signature is missing signed data' if signature_raw.nil?

  signature = Signature.from_bytes(signature_raw)

  raise DecodeError, 'Signature algorithm is not supported' \
    unless signature_algorithm_allowed?(signature.algorithm)

  Blob.new(
    public_key: public_key,
    namespace: namespace,
    hash_algorithm: hash_algorithm,
    signature: signature
  )
end
new( public_key:, namespace:, hash_algorithm:, signature: ) click to toggle source
# File lib/ssh_sig/blob.rb, line 13
def initialize(
  public_key:,
  namespace:,
  hash_algorithm:,
  signature:
)
  @public_key = public_key
  @namespace = namespace
  @hash_algorithm = hash_algorithm
  @signature = signature
end

Private Class Methods

armor_to_blob(armor) click to toggle source
# File lib/ssh_sig/blob.rb, line 123
def self.armor_to_blob(armor)
  # Remove starting and ending whitespace for header checks.
  armor = armor.strip

  raise DecodeError, "Couldn't parse signature: missing header"  unless armor.start_with?(BEGIN_SIGNATURE)

  raise DecodeError, "Couldn't parse signature: missing footer"  unless armor.end_with?(END_SIGNATURE)

  b64 = armor
    .delete_prefix(BEGIN_SIGNATURE)
    .delete_suffix(END_SIGNATURE)
    .gsub(/\s+/, '') # Remove all remaining whitespace to ensure valid Base64

  begin
    Base64.strict_decode64(b64)
  rescue ArgumentError => e
    raise DecodeError, "Couldn't decode armor body: #{e.message}"
  end
end
read_uint64(buf) click to toggle source
# File lib/ssh_sig/blob.rb, line 115
def self.read_uint64(buf)
  b = buf.read(8)

  return nil unless b

  b.unpack1("N")
end

Public Instance Methods

public_key_untrusted() click to toggle source

public_key is parsed from the signature data and is untrusted We make this clear using accessor naming

# File lib/ssh_sig/blob.rb, line 27
def public_key_untrusted
  @public_key
end
signature_data(message) click to toggle source

signature_data creates the “message” passed to the signing function as described in section 3 of github.com/openssh/openssh-portable/blob/b7ffbb17e37f59249c31f1ff59d6c5d80888f689/PROTOCOL.sshsig

Despite the documentation’s use of the word “concatenated”, this data must use the same DER-like encoding as the signature blob.

byte MAGIC_PREAMBLE string namespace string reserved string hash_algorithm string H(message)

# File lib/ssh_sig/blob.rb, line 101
def signature_data(message)
  buf = ::Net::SSH::Buffer.new

  buf.write(MAGIC_PREAMBLE)
  buf.write_string(namespace)
  buf.write_string('') # reserved
  buf.write_string(hash_algorithm)
  buf.write_string(hash(message))

  buf.to_s
end

Private Instance Methods

hash(data) click to toggle source
# File lib/ssh_sig/blob.rb, line 143
def hash(data)
  case hash_algorithm
  when "sha512"
    ::Digest::SHA2.new(512).digest(data)
  when "sha256"
    ::Digest::SHA2.new(256).digest(data)
  else
    raise VerifyError, "Hash algorithm #{hash_algorithm} is not supported"
  end
end