summaryrefslogtreecommitdiff
path: root/lib/gitlab/ssh_public_key.rb
blob: 89ca129812066546e78a737ddd7814fa039d4710 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
module Gitlab
  class SSHPublicKey
    Technology = Struct.new(:name, :key_class, :supported_sizes)

    Technologies = [
      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])
    ].freeze

    def self.technology(name)
      Technologies.find { |tech| tech.name.to_s == name.to_s }
    end

    def self.technology_for_key(key)
      Technologies.find { |tech| key.is_a?(tech.key_class) }
    end

    def self.supported_sizes(name)
      technology(name)&.supported_sizes
    end

    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?
      key.present?
    end

    def type
      technology.name if valid?
    end

    def bits
      return unless valid?

      case type
      when :rsa
        key.n.num_bits
      when :dsa
        key.p.num_bits
      when :ecdsa
        key.group.order.num_bits
      when :ed25519
        256
      else
        raise "Unsupported key type: #{type}"
      end
    end

    private

    def technology
      @technology ||=
        self.class.technology_for_key(key) || raise("Unsupported key type: #{key.class}")
    end
  end
end