class SSHData::PublicKey::ECDSA

Constants

CURVE_FOR_OPENSSL_CURVE_NAME
DIGEST_FOR_CURVE
NISTP256
NISTP384
NISTP521
OPENSSL_CURVE_NAME_FOR_CURVE

Attributes

curve[R]
openssl[R]
public_key_bytes[R]

Public Class Methods

check_algorithm!(algo, curve) click to toggle source
# File lib/ssh_data/public_key/ecdsa.rb, line 68
def self.check_algorithm!(algo, curve)
  unless [ALGO_ECDSA256, ALGO_ECDSA384, ALGO_ECDSA521].include?(algo)
    raise DecodeError, "bad algorithm: #{algo.inspect}"
  end

  unless algo == "ecdsa-sha2-#{curve}"
    raise DecodeError, "bad curve: #{curve.inspect}"
  end
end
new(algo:, curve:, public_key:) click to toggle source
Calls superclass method SSHData::PublicKey::Base::new
# File lib/ssh_data/public_key/ecdsa.rb, line 78
def initialize(algo:, curve:, public_key:)
  self.class.check_algorithm!(algo, curve)

  @curve = curve
  @public_key_bytes = public_key

  @openssl = begin
    OpenSSL::PKey::EC.new(asn1.to_der)
  rescue ArgumentError
    raise DecodeError, "bad key data"
  end

  super(algo: algo)
end
openssl_signature(sig) click to toggle source

Convert an SSH encoded ECDSA signature to DER encoding for verification with OpenSSL.

sig - A binary String signature from an SSH packet.

Returns a binary String signature, as expected by OpenSSL.

# File lib/ssh_data/public_key/ecdsa.rb, line 34
def self.openssl_signature(sig)
  r, rlen = Encoding.decode_mpint(sig, 0)
  s, slen = Encoding.decode_mpint(sig, rlen)

  if rlen + slen != sig.bytesize
    raise DecodeError, "unexpected trailing data"
  end

  OpenSSL::ASN1::Sequence.new([
    OpenSSL::ASN1::Integer.new(r),
    OpenSSL::ASN1::Integer.new(s)
  ]).to_der
end
ssh_signature(sig) click to toggle source

Convert an DER encoded ECDSA signature, as generated by OpenSSL to SSH encoding.

sig - A binary String signature, as generated by OpenSSL.

Returns a binary String signature, as found in an SSH packet.

# File lib/ssh_data/public_key/ecdsa.rb, line 54
def self.ssh_signature(sig)
  a1 = OpenSSL::ASN1.decode(sig)
  if a1.tag_class != :UNIVERSAL || a1.tag != OpenSSL::ASN1::SEQUENCE || a1.value.count != 2
    raise DecodeError, "bad asn1 signature"
  end

  r, s = a1.value
  if r.tag_class != :UNIVERSAL || r.tag != OpenSSL::ASN1::INTEGER || s.tag_class != :UNIVERSAL || s.tag != OpenSSL::ASN1::INTEGER
    raise DecodeError, "bad asn1 signature"
  end

  [Encoding.encode_mpint(r.value), Encoding.encode_mpint(s.value)].join
end

Public Instance Methods

==(other) click to toggle source

Is this public key equal to another public key?

other - Another SSHData::PublicKey::Base instance to compare with.

Returns boolean.

Calls superclass method SSHData::PublicKey::Base#==
# File lib/ssh_data/public_key/ecdsa.rb, line 127
def ==(other)
  super && other.curve == curve && other.public_key_bytes == public_key_bytes
end
digest() click to toggle source

The digest algorithm to use with this key’s curve.

Returns an OpenSSL::Digest.

# File lib/ssh_data/public_key/ecdsa.rb, line 134
def digest
  DIGEST_FOR_CURVE[curve]
end
rfc4253() click to toggle source

RFC4253 binary encoding of the public key.

Returns a binary String.

# File lib/ssh_data/public_key/ecdsa.rb, line 114
def rfc4253
  Encoding.encode_fields(
    [:string, algo],
    [:string, curve],
    [:string, public_key_bytes],
  )
end
verify(signed_data, signature) click to toggle source

Verify an SSH signature.

signed_data - The String message that the signature was calculated over. signature - The binarty String signature with SSH encoding.

Returns boolean.

# File lib/ssh_data/public_key/ecdsa.rb, line 99
def verify(signed_data, signature)
  sig_algo, ssh_sig, _ = Encoding.decode_signature(signature)
  if sig_algo != "ecdsa-sha2-#{curve}"
    raise DecodeError, "bad signature algorithm: #{sig_algo.inspect}"
  end

  openssl_sig = self.class.openssl_signature(ssh_sig)
  digest = DIGEST_FOR_CURVE[curve]

  openssl.verify(digest.new, openssl_sig, signed_data)
end

Private Instance Methods

asn1() click to toggle source
# File lib/ssh_data/public_key/ecdsa.rb, line 140
def asn1
  unless name = OPENSSL_CURVE_NAME_FOR_CURVE[curve]
    raise DecodeError, "unknown curve: #{curve.inspect}"
  end

  OpenSSL::ASN1::Sequence.new([
    OpenSSL::ASN1::Sequence.new([
      OpenSSL::ASN1::ObjectId.new("id-ecPublicKey"),
      OpenSSL::ASN1::ObjectId.new(name),
    ]),
    OpenSSL::ASN1::BitString.new(public_key_bytes),
  ])
end