2018-10-22 03:00:50 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2017-08-21 06:30:03 -04:00
|
|
|
module Gitlab
|
|
|
|
class SSHPublicKey
|
2017-08-25 09:08:48 -04:00
|
|
|
Technology = Struct.new(:name, :key_class, :supported_sizes)
|
2017-08-21 06:30:03 -04:00
|
|
|
|
|
|
|
Technologies = [
|
2017-08-25 09:08:48 -04:00
|
|
|
Technology.new(:rsa, OpenSSL::PKey::RSA, [1024, 2048, 3072, 4096]),
|
|
|
|
Technology.new(:dsa, OpenSSL::PKey::DSA, [1024, 2048, 3072]),
|
|
|
|
Technology.new(:ecdsa, OpenSSL::PKey::EC, [256, 384, 521]),
|
|
|
|
Technology.new(:ed25519, Net::SSH::Authentication::ED25519::PubKey, [256])
|
2017-08-21 06:30:03 -04:00
|
|
|
].freeze
|
|
|
|
|
|
|
|
def self.technology(name)
|
2017-08-25 09:08:48 -04:00
|
|
|
Technologies.find { |tech| tech.name.to_s == name.to_s }
|
2017-08-21 06:30:03 -04:00
|
|
|
end
|
|
|
|
|
2017-08-28 16:33:35 -04:00
|
|
|
def self.technology_for_key(key)
|
|
|
|
Technologies.find { |tech| key.is_a?(tech.key_class) }
|
|
|
|
end
|
|
|
|
|
2017-08-25 09:08:48 -04:00
|
|
|
def self.supported_sizes(name)
|
|
|
|
technology(name)&.supported_sizes
|
2017-08-21 06:30:03 -04:00
|
|
|
end
|
|
|
|
|
2018-02-15 09:50:19 -05:00
|
|
|
def self.sanitize(key_content)
|
|
|
|
ssh_type, *parts = key_content.strip.split
|
|
|
|
|
|
|
|
return key_content if parts.empty?
|
|
|
|
|
2018-10-22 03:00:50 -04:00
|
|
|
parts.each_with_object(+"#{ssh_type} ").with_index do |(part, content), index|
|
2018-02-15 09:50:19 -05:00
|
|
|
content << part
|
|
|
|
|
|
|
|
if Gitlab::SSHPublicKey.new(content).valid?
|
|
|
|
break [content, parts[index + 1]].compact.join(' ') # Add the comment part if present
|
|
|
|
elsif parts.size == index + 1 # return original content if we've reached the last element
|
|
|
|
break key_content
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-08-21 06:30:03 -04:00
|
|
|
attr_reader :key_text, :key
|
|
|
|
|
|
|
|
# Unqualified MD5 fingerprint for compatibility
|
|
|
|
delegate :fingerprint, to: :key, allow_nil: true
|
|
|
|
|
|
|
|
def initialize(key_text)
|
|
|
|
@key_text = key_text
|
|
|
|
|
|
|
|
@key =
|
|
|
|
begin
|
|
|
|
Net::SSH::KeyFactory.load_data_public_key(key_text)
|
|
|
|
rescue StandardError, NotImplementedError
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def valid?
|
2018-02-16 11:32:08 -05:00
|
|
|
SSHKey.valid_ssh_public_key?(key_text)
|
2017-08-21 06:30:03 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def type
|
2018-02-15 09:50:19 -05:00
|
|
|
technology.name if key.present?
|
2017-08-21 06:30:03 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def bits
|
2018-02-15 09:50:19 -05:00
|
|
|
return if key.blank?
|
2017-08-21 06:30:03 -04:00
|
|
|
|
|
|
|
case type
|
|
|
|
when :rsa
|
2018-02-15 09:50:19 -05:00
|
|
|
key.n&.num_bits
|
2017-08-21 06:30:03 -04:00
|
|
|
when :dsa
|
2018-02-15 09:50:19 -05:00
|
|
|
key.p&.num_bits
|
2017-08-21 06:30:03 -04:00
|
|
|
when :ecdsa
|
2018-02-15 09:50:19 -05:00
|
|
|
key.group.order&.num_bits
|
2017-08-21 06:30:03 -04:00
|
|
|
when :ed25519
|
|
|
|
256
|
|
|
|
else
|
|
|
|
raise "Unsupported key type: #{type}"
|
|
|
|
end
|
|
|
|
end
|
2017-08-25 09:08:48 -04:00
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def technology
|
|
|
|
@technology ||=
|
2017-08-28 16:33:35 -04:00
|
|
|
self.class.technology_for_key(key) || raise("Unsupported key type: #{key.class}")
|
2017-08-25 09:08:48 -04:00
|
|
|
end
|
2017-08-21 06:30:03 -04:00
|
|
|
end
|
|
|
|
end
|