diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/net/ssh/authentication/ed25519.rb | 75 | ||||
-rw-r--r-- | lib/net/ssh/authentication/pub_key_fingerprint.rb | 41 | ||||
-rw-r--r-- | lib/net/ssh/transport/openssl.rb | 5 | ||||
-rw-r--r-- | lib/net/ssh/version.rb | 29 |
4 files changed, 95 insertions, 55 deletions
diff --git a/lib/net/ssh/authentication/ed25519.rb b/lib/net/ssh/authentication/ed25519.rb index bcb7fd1..2f98fa1 100644 --- a/lib/net/ssh/authentication/ed25519.rb +++ b/lib/net/ssh/authentication/ed25519.rb @@ -6,67 +6,66 @@ require 'ed25519' require 'base64' require 'net/ssh/transport/cipher_factory' +require 'net/ssh/authentication/pub_key_fingerprint' require 'bcrypt_pbkdf' unless RUBY_PLATFORM == "java" -module Net - module SSH +module Net + module SSH module Authentication module ED25519 class SigningKeyFromFile < SimpleDelegator def initialize(pk,sk) key = ::Ed25519::SigningKey.from_keypair(sk) raise ArgumentError, "pk does not match sk" unless pk == key.verify_key.to_bytes - + super(key) end end - + class PubKey + include Net::SSH::Authentication::PubKeyFingerprint + attr_reader :verify_key - + def initialize(data) @verify_key = ::Ed25519::VerifyKey.new(data) end - + def self.read_keyblob(buffer) PubKey.new(buffer.read_string) end - + def to_blob Net::SSH::Buffer.from(:mstring,"ssh-ed25519",:string,@verify_key.to_bytes).to_s end - + def ssh_type "ssh-ed25519" end - + def ssh_signature_type ssh_type end - + def ssh_do_verify(sig,data) @verify_key.verify(sig,data) end - + def to_pem # TODO this is not pem ssh_type + Base64.encode64(@verify_key.to_bytes) end - - def fingerprint - @fingerprint ||= OpenSSL::Digest::MD5.hexdigest(to_blob).scan(/../).join(":") - end end - + class PrivKey CipherFactory = Net::SSH::Transport::CipherFactory - + MBEGIN = "-----BEGIN OPENSSH PRIVATE KEY-----\n" MEND = "-----END OPENSSH PRIVATE KEY-----\n" MAGIC = "openssh-key-v1" - + attr_reader :sign_key - + def initialize(datafull,password) raise ArgumentError.new("Expected #{MBEGIN} at start of private key") unless datafull.start_with?(MBEGIN) raise ArgumentError.new("Expected #{MEND} at end of private key") unless datafull.end_with?(MEND) @@ -74,75 +73,75 @@ module Net data = Base64.decode64(datab64) raise ArgumentError.new("Expected #{MAGIC} at start of decoded private key") unless data.start_with?(MAGIC) buffer = Net::SSH::Buffer.new(data[MAGIC.size + 1..-1]) - + ciphername = buffer.read_string raise ArgumentError.new("#{ciphername} in private key is not supported") unless CipherFactory.supported?(ciphername) - + kdfname = buffer.read_string raise ArgumentError.new("Expected #{kdfname} to be or none or bcrypt") unless %w[none bcrypt].include?(kdfname) - + kdfopts = Net::SSH::Buffer.new(buffer.read_string) num_keys = buffer.read_long raise ArgumentError.new("Only 1 key is supported in ssh keys #{num_keys} was in private key") unless num_keys == 1 _pubkey = buffer.read_string - + len = buffer.read_long - + keylen, blocksize, ivlen = CipherFactory.get_lengths(ciphername, iv_len: true) raise ArgumentError.new("Private key len:#{len} is not a multiple of #{blocksize}") if ((len < blocksize) || ((blocksize > 0) && (len % blocksize) != 0)) - + if kdfname == 'bcrypt' salt = kdfopts.read_string rounds = kdfopts.read_long - + raise "BCryptPbkdf is not implemented for jruby" if RUBY_PLATFORM == "java" key = BCryptPbkdf::key(password, salt, keylen + ivlen, rounds) else key = '\x00' * (keylen + ivlen) end - + cipher = CipherFactory.get(ciphername, key: key[0...keylen], iv:key[keylen...keylen + ivlen], decrypt: true) - + decoded = cipher.update(buffer.remainder_as_buffer.to_s) decoded << cipher.final - + decoded = Net::SSH::Buffer.new(decoded) check1 = decoded.read_long check2 = decoded.read_long - + raise ArgumentError, "Decrypt failed on private key" if (check1 != check2) - + _type_name = decoded.read_string pk = decoded.read_string sk = decoded.read_string _comment = decoded.read_string - + @pk = pk @sign_key = SigningKeyFromFile.new(pk,sk) end - + def to_blob public_key.to_blob end - + def ssh_type "ssh-ed25519" end - + def ssh_signature_type ssh_type end - + def public_key PubKey.new(@pk) end - + def ssh_do_sign(data) @sign_key.sign(data) end - + def self.read(data,password) self.new(data,password) end diff --git a/lib/net/ssh/authentication/pub_key_fingerprint.rb b/lib/net/ssh/authentication/pub_key_fingerprint.rb new file mode 100644 index 0000000..e06aeef --- /dev/null +++ b/lib/net/ssh/authentication/pub_key_fingerprint.rb @@ -0,0 +1,41 @@ + +require 'openssl' + +module Net + module SSH + module Authentication + # Public key fingerprinting utility module - internal not part of API. + # This is included in pubkey classes and called from there. All RSA, DSA, and ECC keys + # are supported. + # + # require 'net/ssh' + # my_pubkey_text = File.read('/path/to/id_ed25519.pub') + # #=> "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDB2NBh4GJPPUN1kXPMu8b633Xcv55WoKC3OkBjFAbzJ alice@example.com" + # my_pubkey = Net::SSH::KeyFactory.load_data_public_key(my_pubkey_text) + # #=> #<Net::SSH::Authentication::ED25519::PubKey:0x00007fc8e91819b0 + # my_pubkey.fingerprint + # #=> "2f:7f:97:21:76:a4:0f:38:c4:fe:d8:b4:6a:39:72:30" + # my_pubkey.fingerprint('SHA256') + # #=> "SHA256:u6mXnY8P1b0FODGp8mckqOB33u8+jvkSCtJbD5Q9klg" + module PubKeyFingerprint # :nodoc: + # Return the key's fingerprint. Algorithm may be either +MD5+ (default), + # or +SHA256+. For +SHA256+, fingerprints are in the same format + # returned by OpenSSH's <tt>`ssh-add -l -E SHA256`</tt>, i.e., + # trailing base64 padding '=' characters are stripped and the + # literal string +SHA256:+ is prepended. + def fingerprint(algorithm='MD5') + @fingerprint ||= {} + @fingerprint[algorithm] ||= + case algorithm.to_s.upcase + when 'MD5' + OpenSSL::Digest.hexdigest(algorithm, to_blob).scan(/../).join(":") + when 'SHA256' + "SHA256:#{Base64.encode64(OpenSSL::Digest.digest(algorithm, to_blob)).chomp.gsub(/=+\z/, '')}" + else + raise OpenSSL::Digest::DigestError, "unsupported ssh key digest #{algorithm}" + end + end + end + end + end +end diff --git a/lib/net/ssh/transport/openssl.rb b/lib/net/ssh/transport/openssl.rb index 9135ae0..15715f4 100644 --- a/lib/net/ssh/transport/openssl.rb +++ b/lib/net/ssh/transport/openssl.rb @@ -1,4 +1,5 @@ require 'openssl' +require 'net/ssh/authentication/pub_key_fingerprint' module OpenSSL @@ -25,9 +26,7 @@ module OpenSSL module PKey class PKey - def fingerprint - @fingerprint ||= OpenSSL::Digest::MD5.hexdigest(to_blob).scan(/../).join(":") - end + include Net::SSH::Authentication::PubKeyFingerprint end # This class is originally defined in the OpenSSL module. As needed, methods diff --git a/lib/net/ssh/version.rb b/lib/net/ssh/version.rb index fcd5db8..c1a6cca 100644 --- a/lib/net/ssh/version.rb +++ b/lib/net/ssh/version.rb @@ -1,4 +1,4 @@ -module Net +module Net module SSH # A class for describing the current version of a library. The version # consists of three parts: the +major+ number, the +minor+ number, and the @@ -14,54 +14,55 @@ module Net # end class Version include Comparable - + # A convenience method for instantiating a new Version instance with the # given +major+, +minor+, and +tiny+ components. def self.[](major, minor, tiny, pre = nil) new(major, minor, tiny, pre) end - + attr_reader :major, :minor, :tiny - + # Create a new Version object with the given components. def initialize(major, minor, tiny, pre = nil) @major, @minor, @tiny, @pre = major, minor, tiny, pre end - + # Compare this version to the given +version+ object. def <=>(version) to_i <=> version.to_i end - + # Converts this version object to a string, where each of the three # version components are joined by the '.' character. E.g., 2.0.0. def to_s @to_s ||= [@major, @minor, @tiny, @pre].compact.join(".") end - + # Converts this version to a canonical integer that may be compared # against other version objects. def to_i @to_i ||= @major * 1_000_000 + @minor * 1_000 + @tiny end - + # The major component of this version of the Net::SSH library MAJOR = 5 - + # The minor component of this version of the Net::SSH library MINOR = 0 - + # The tiny component of this version of the Net::SSH library TINY = 0 - + # The prerelease component of this version of the Net::SSH library # nil allowed PRE = "beta1" - + # The current version of the Net::SSH library as a Version instance CURRENT = new(*[MAJOR, MINOR, TINY, PRE].compact) - + # The current version of the Net::SSH library as a String STRING = CURRENT.to_s end -end; end + end +end |