Class: Inspec::IafFile

Inherits:
Object
  • Object
show all
Defined in:
lib/inspec/iaf_file.rb

Constant Summary collapse

KEY_ALG =
OpenSSL::PKey::RSA
INSPEC_PROFILE_VERSION_1 =
"INSPEC-PROFILE-1".freeze
INSPEC_PROFILE_VERSION_2 =
"INSPEC-PROFILE-2".freeze
ARTIFACT_DIGEST =
OpenSSL::Digest::SHA512
ARTIFACT_DIGEST_NAME =
"SHA512".freeze
VALID_PROFILE_VERSIONS =
Set.new [INSPEC_PROFILE_VERSION_1, INSPEC_PROFILE_VERSION_2]
VALID_PROFILE_DIGESTS =
Set.new [ARTIFACT_DIGEST_NAME]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path) ⇒ IafFile

Returns a new instance of IafFile.



65
66
67
68
# File 'lib/inspec/iaf_file.rb', line 65

def initialize(path)
  @path = path
  @key_name = nil
end

Instance Attribute Details

#key_nameObject (readonly)

Returns the value of attribute key_name.



63
64
65
# File 'lib/inspec/iaf_file.rb', line 63

def key_name
  @key_name
end

#versionObject (readonly)

Returns the value of attribute version.



63
64
65
# File 'lib/inspec/iaf_file.rb', line 63

def version
  @version
end

Class Method Details

.fetch_validation_key_from_github(keyname) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/inspec/iaf_file.rb', line 46

def self.fetch_validation_key_from_github(keyname)
  URI.open("https://raw.githubusercontent.com/inspec/inspec/main/etc/keys/#{keyname}.pem.pub") do |r|
    puts "Fetching validation key '#{keyname}' from github"
    dir = File.join(Inspec.config_dir, "keys")
    FileUtils.mkdir_p dir
    key_file = File.join(dir, "#{keyname}.pem.pub")
    File.open(key_file, "w") do |f|
      r.each_line do |line|
        f.puts line
      end
    end
  end
  true
rescue OpenURI::HTTPError
  false
end

.find_signing_key(keyname, config_dir = nil) ⇒ Object



37
38
39
40
41
42
43
44
# File 'lib/inspec/iaf_file.rb', line 37

def self.find_signing_key(keyname, config_dir = nil)
  config_dir ||= Inspec.config_dir
  [".", File.join(config_dir, "keys")].each do |path|
    filename = File.join(path, "#{keyname}.pem.key")
    return filename if File.exist?(filename)
  end
  raise Inspec::Exceptions::ProfileSigningKeyNotFound.new("Signing key #{keyname} not found")
end

.find_validation_key(keyname) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/inspec/iaf_file.rb', line 18

def self.find_validation_key(keyname)
  [
    ".",
    File.join(Inspec.config_dir, "keys"),
    File.join(Inspec.src_root, "etc", "keys"),
  ].each do |path|
    filename = File.join(path, "#{keyname}.pem.pub")
    return filename if File.exist?(filename)
  rescue ArgumentError => e
    puts e
    raise Inspec::Exceptions::ProfileValidationKeyNotFound.new("Validation key #{keyname} not found")
  end

  # Retry if we can fetch it from github
  return find_validation_key(keyname) if fetch_validation_key_from_github(keyname)

  raise Inspec::Exceptions::ProfileValidationKeyNotFound.new("Validation key #{keyname} not found")
end

Instance Method Details

#valid?Boolean

Returns:

  • (Boolean)


70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/inspec/iaf_file.rb', line 70

def valid?
  header = []
  valid = true
  f = File.open(@path, "rb")
  @version = f.readline.strip!
  if version == INSPEC_PROFILE_VERSION_1
    header << version
    header << f.readline.strip!
    header << f.readline.strip!

    file_sig = ""
    # the signature is multi-line
    while (line = f.readline) != "\n"
      file_sig += line
    end
    header << file_sig.strip!
    f.close

    f = File.open(@path, "rb")
    while f.readline != "\n" do end
    content = f.read
    f.close
  elsif version == INSPEC_PROFILE_VERSION_2
    header << version
    header << f.readline.strip!
    content = f.read
    f.close

    header_content = content.unpack("h*").pack("H*")
    header << header_content.slice(0, 100).rstrip
    header << header_content.slice(100, 20).rstrip
    header << header_content.slice(120, 370).rstrip + "\n" # \n at the end is require in this field
    content = content.slice(490, content.length).lstrip
  else
    valid = false
  end

  @key_name = header[2]
  validation_key_path = Inspec::IafFile.find_validation_key(@key_name)

  unless valid_header?(header)
    valid = false
  end

  verification_key = KEY_ALG.new File.read validation_key_path
  signature = Base64.decode64(header[4])
  digest = ARTIFACT_DIGEST.new
  unless verification_key.verify digest, signature, content
    valid = false
  end

  valid
end

#valid_header?(header) ⇒ Boolean

Returns:

  • (Boolean)


124
125
126
# File 'lib/inspec/iaf_file.rb', line 124

def valid_header?(header)
  VALID_PROFILE_VERSIONS.member?(header[0]) && VALID_PROFILE_DIGESTS.member?(header[3])
end