class SSHData::Signature

Constants

MAX_SUPPORTED_VERSION
MIN_SUPPORTED_VERSION
PEM_TYPE
PERMITTED_RSA_SIGNATURE_ALGORITHMS
SIGNATURE_PREAMBLE
SUPPORTED_HASH_ALGORITHMS

Spec: no SHA1 or SHA384. In practice, OpenSSH is always going to use SHA512. Note the actual signing / verify primitive may use a different hash algorithm. github.com/openssh/openssh-portable/blob/b7ffbb17e37f59249c31f1ff59d6c5d80888f689/PROTOCOL.sshsig#L67

Attributes

hash_algorithm[R]
namespace[R]
reserved[R]
signature[R]
sigversion[R]

Public Class Methods

new(sigversion:, publickey:, namespace:, reserved:, hash_algorithm:, signature:) click to toggle source
# File lib/ssh_data/signature.rb, line 50
def initialize(sigversion:, publickey:, namespace:, reserved:, hash_algorithm:, signature:)
  if sigversion > MAX_SUPPORTED_VERSION || sigversion < MIN_SUPPORTED_VERSION
    raise UnsupportedError, "Signature version is not supported"
  end

  unless SUPPORTED_HASH_ALGORITHMS.has_key?(hash_algorithm)
    raise UnsupportedError, "Hash algorithm #{hash_algorithm} is not supported."
  end

  # Spec: empty namespaces are not permitted.
  # https://github.com/openssh/openssh-portable/blob/b7ffbb17e37f59249c31f1ff59d6c5d80888f689/PROTOCOL.sshsig#L57
  raise UnsupportedError, "A namespace is required." if namespace.empty?

  # Spec: ignore 'reserved', don't need to validate that it is empty.

  @sigversion = sigversion
  @publickey = publickey
  @namespace = namespace
  @reserved = reserved
  @hash_algorithm = hash_algorithm
  @signature = signature
end
parse_blob(blob) click to toggle source
# File lib/ssh_data/signature.rb, line 40
def self.parse_blob(blob)
  data, read = Encoding.decode_openssh_signature(blob)

  if read != blob.bytesize
    raise DecodeError, "unexpected trailing data"
  end

  new(**data)
end
parse_pem(pem) click to toggle source

Parses a PEM armored SSH signature. pem - A PEM encoded SSH signature.

Returns a Signature instance.

# File lib/ssh_data/signature.rb, line 29
def self.parse_pem(pem)
  pem_type = Encoding.pem_type(pem)

  if pem_type != PEM_TYPE
    raise DecodeError, "Mismatched PEM type. Expecting '#{PEM_TYPE}', actually '#{pem_type}'."
  end

  blob = Encoding.decode_pem(pem, pem_type)
  self.parse_blob(blob)
end

Public Instance Methods

public_key() click to toggle source

Gets the public key from the signature. If the signature was created from a certificate, this will be an SSHData::Certificate. Otherwise, this will be a PublicKey algorithm.

# File lib/ssh_data/signature.rb, line 110
def public_key
  public_key_algorithm, _ = Encoding.decode_string(@publickey)

  if PublicKey::ALGOS.include?(public_key_algorithm)
    PublicKey.parse_rfc4253(@publickey)
  elsif Certificate::ALGOS.include?(public_key_algorithm)
    Certificate.parse_rfc4253(@publickey)
  else
    raise UnsupportedError, "Public key algorithm #{public_key_algorithm} is not supported."
  end
end
verify(signed_data, **opts) click to toggle source
# File lib/ssh_data/signature.rb, line 73
def verify(signed_data, **opts)
  signing_key = public_key

  # Unwrap the signing key if this signature was created from a certificate.
  key = signing_key.is_a?(Certificate) ? signing_key.public_key : signing_key

  digest_algorithm = SUPPORTED_HASH_ALGORITHMS[@hash_algorithm]

  if key.is_a?(PublicKey::RSA)
    sig_algo, * = Encoding.decode_signature(@signature)

    # Spec: If the signature is an RSA signature, the legacy 'ssh-rsa'
    # identifer is not permitted.
    # https://github.com/openssh/openssh-portable/blob/b7ffbb17e37f59249c31f1ff59d6c5d80888f689/PROTOCOL.sshsig#L72
    unless PERMITTED_RSA_SIGNATURE_ALGORITHMS.include?(sig_algo)
      raise UnsupportedError, "RSA signature #{sig_algo} is not supported."
    end
  end

  message_digest = digest_algorithm.digest(signed_data)
  blob =
    SIGNATURE_PREAMBLE +
    Encoding.encode_string(@namespace) +
    Encoding.encode_string(@reserved || "") +
    Encoding.encode_string(@hash_algorithm) +
    Encoding.encode_string(message_digest)

  if key.class.include?(::SSHData::PublicKey::SecurityKey)
    key.verify(blob, @signature, **opts)
  else
    key.verify(blob, @signature)
  end
end