app-info 2.8.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +12 -7
  3. data/.github/workflows/ci.yml +7 -5
  4. data/.github/workflows/create_release.yml +15 -0
  5. data/.rubocop.yml +33 -11
  6. data/CHANGELOG.md +107 -1
  7. data/Gemfile +10 -5
  8. data/README.md +82 -15
  9. data/Rakefile +11 -0
  10. data/app_info.gemspec +14 -5
  11. data/lib/app_info/aab.rb +76 -110
  12. data/lib/app_info/android/signature.rb +114 -0
  13. data/lib/app_info/android/signatures/base.rb +53 -0
  14. data/lib/app_info/android/signatures/info.rb +158 -0
  15. data/lib/app_info/android/signatures/v1.rb +63 -0
  16. data/lib/app_info/android/signatures/v2.rb +121 -0
  17. data/lib/app_info/android/signatures/v3.rb +131 -0
  18. data/lib/app_info/android/signatures/v4.rb +18 -0
  19. data/lib/app_info/android.rb +181 -0
  20. data/lib/app_info/apk.rb +77 -112
  21. data/lib/app_info/apple.rb +192 -0
  22. data/lib/app_info/certificate.rb +176 -0
  23. data/lib/app_info/const.rb +76 -0
  24. data/lib/app_info/core_ext/object/try.rb +3 -1
  25. data/lib/app_info/core_ext/string/inflector.rb +2 -0
  26. data/lib/app_info/dsym/debug_info.rb +81 -0
  27. data/lib/app_info/dsym/macho.rb +62 -0
  28. data/lib/app_info/dsym.rb +35 -135
  29. data/lib/app_info/error.rb +3 -1
  30. data/lib/app_info/file.rb +49 -0
  31. data/lib/app_info/helper/archive.rb +37 -0
  32. data/lib/app_info/helper/file_size.rb +25 -0
  33. data/lib/app_info/helper/generate_class.rb +29 -0
  34. data/lib/app_info/helper/protobuf.rb +12 -0
  35. data/lib/app_info/helper/signatures.rb +229 -0
  36. data/lib/app_info/helper.rb +5 -128
  37. data/lib/app_info/info_plist.rb +66 -29
  38. data/lib/app_info/ipa/framework.rb +4 -4
  39. data/lib/app_info/ipa.rb +61 -135
  40. data/lib/app_info/macos.rb +54 -102
  41. data/lib/app_info/mobile_provision.rb +66 -48
  42. data/lib/app_info/pe.rb +322 -0
  43. data/lib/app_info/png_uncrush.rb +25 -5
  44. data/lib/app_info/proguard.rb +39 -22
  45. data/lib/app_info/protobuf/manifest.rb +22 -11
  46. data/lib/app_info/protobuf/models/Configuration_pb.rb +1 -0
  47. data/lib/app_info/protobuf/models/README.md +8 -1
  48. data/lib/app_info/protobuf/models/Resources.proto +51 -0
  49. data/lib/app_info/protobuf/models/Resources_pb.rb +42 -0
  50. data/lib/app_info/protobuf/resources.rb +5 -5
  51. data/lib/app_info/version.rb +1 -1
  52. data/lib/app_info.rb +93 -43
  53. metadata +57 -37
data/lib/app_info/dsym.rb CHANGED
@@ -1,79 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'macho'
3
+ require 'app_info/dsym/debug_info'
4
4
 
5
5
  module AppInfo
6
6
  # DSYM parser
7
- class DSYM
7
+ class DSYM < File
8
8
  include Helper::Archive
9
9
 
10
- attr_reader :file
11
-
12
- def initialize(file)
13
- @file = file
14
- end
15
-
16
- def file_type
17
- Platform::DSYM
18
- end
19
-
20
- def object
21
- @object ||= File.basename(app_path)
22
- end
23
-
24
- def macho_type
25
- @macho_type ||= ::MachO.open(app_path)
26
- end
27
-
28
- def machos
29
- @machos ||= case macho_type
30
- when ::MachO::MachOFile
31
- [MachO.new(macho_type, File.size(app_path))]
32
- else
33
- size = macho_type.fat_archs.each_with_object([]) do |arch, obj|
34
- obj << arch.size
35
- end
36
-
37
- machos = []
38
- macho_type.machos.each_with_index do |file, i|
39
- machos << MachO.new(file, size[i])
40
- end
41
- machos
42
- end
43
- end
44
-
45
- def release_version
46
- info.try(:[], 'CFBundleShortVersionString')
10
+ # @return [Symbol] {Manufacturer}
11
+ def manufacturer
12
+ Manufacturer::APPLE
47
13
  end
48
14
 
49
- def build_version
50
- info.try(:[], 'CFBundleVersion')
15
+ # @return [nil]
16
+ def each_file(&block)
17
+ files.each { |file| block.call(file) }
51
18
  end
19
+ alias each_objects each_file
52
20
 
53
- def identifier
54
- info.try(:[], 'CFBundleIdentifier').sub('com.apple.xcode.dsym.', '')
55
- end
56
- alias bundle_id identifier
57
-
58
- def info
59
- return nil unless File.exist?(info_path)
60
-
61
- @info ||= CFPropertyList.native_types(CFPropertyList::List.new(file: info_path).value)
62
- end
63
-
64
- def info_path
65
- @info_path ||= File.join(contents, 'Contents', 'Info.plist')
66
- end
67
-
68
- def app_path
69
- unless @app_path
70
- path = File.join(contents, 'Contents', 'Resources', 'DWARF')
71
- name = Dir.entries(path).reject { |f| ['.', '..'].include?(f) }.first
72
- @app_path = File.join(path, name)
21
+ # @return [Array<DebugInfo>] dsym_files files by alphabetical order
22
+ def files
23
+ @files ||= Dir.children(contents).sort.each_with_object([]) do |file, obj|
24
+ obj << DebugInfo.new(::File.join(contents, file))
73
25
  end
74
-
75
- @app_path
76
26
  end
27
+ alias objects files
77
28
 
78
29
  def clear!
79
30
  return unless @contents
@@ -81,85 +32,34 @@ module AppInfo
81
32
  FileUtils.rm_rf(@contents)
82
33
 
83
34
  @contents = nil
84
- @app_path = nil
85
- @info = nil
86
- @object = nil
87
- @macho_type = nil
35
+ @files = nil
88
36
  end
89
37
 
38
+ # @return [String] contents path of dsym
90
39
  def contents
91
- unless @contents
92
- if File.directory?(@file)
93
- @contents = @file
94
- else
95
- dsym_dir = nil
96
- @contents = unarchive(@file, path: 'dsym') do |path, zip_file|
97
- zip_file.each do |f|
98
- unless dsym_dir
99
- dsym_dir = f.name
100
- # fix filename is xxx.app.dSYM/Contents
101
- dsym_dir = dsym_dir.split('/')[0] if dsym_dir.include?('/')
102
- end
40
+ @contents ||= lambda {
41
+ return @file if ::File.directory?(@file)
103
42
 
104
- f_path = File.join(path, f.name)
105
- FileUtils.mkdir_p(File.dirname(f_path))
106
- f.extract(f_path) unless File.exist?(f_path)
107
- end
108
- end
43
+ dsym_filenames = []
44
+ unarchive(@file, prefix: 'dsym') do |base_path, zip_file|
45
+ zip_file.each do |entry|
46
+ file_path = entry.name
47
+ next unless file_path.downcase.include?('.dsym/contents/')
48
+ next if ::File.basename(file_path).start_with?('.')
109
49
 
110
- @contents = File.join(@contents, dsym_dir)
111
- end
112
- end
113
-
114
- @contents
115
- end
116
-
117
- # DSYM Mach-O
118
- class MachO
119
- include Helper::HumanFileSize
120
-
121
- def initialize(file, size = 0)
122
- @file = file
123
- @size = size
124
- end
125
-
126
- def cpu_name
127
- @file.cpusubtype
128
- end
129
-
130
- def cpu_type
131
- @file.cputype
132
- end
133
-
134
- def type
135
- @file.filetype
136
- end
137
-
138
- def size(human_size: false)
139
- return number_to_human_size(@size) if human_size
140
-
141
- @size
142
- end
143
-
144
- def uuid
145
- @file[:LC_UUID][0].uuid_string
146
- end
147
- alias debug_id uuid
50
+ dsym_filename = file_path.split('/').select { |f| f.downcase.end_with?('.dsym') }.last
51
+ dsym_filenames << dsym_filename unless dsym_filenames.include?(dsym_filename)
148
52
 
149
- def header
150
- @header ||= @file.header
151
- end
53
+ unless file_path.start_with?(dsym_filename)
54
+ file_path = file_path.split('/')[1..-1].join('/')
55
+ end
152
56
 
153
- def to_h
154
- {
155
- uuid: uuid,
156
- type: type,
157
- cpu_name: cpu_name,
158
- cpu_type: cpu_type,
159
- size: size,
160
- human_size: size(human_size: true)
161
- }
162
- end
57
+ dest_path = ::File.join(base_path, file_path)
58
+ FileUtils.mkdir_p(::File.dirname(dest_path))
59
+ entry.extract(dest_path) unless ::File.exist?(dest_path)
60
+ end
61
+ end
62
+ }.call
163
63
  end
164
64
  end
165
65
  end
@@ -9,5 +9,7 @@ module AppInfo
9
9
 
10
10
  class NotFoundError < Error; end
11
11
 
12
- class UnkownFileTypeError < Error; end
12
+ class ProtobufParseError < Error; end
13
+
14
+ class UnknownFormatError < Error; end
13
15
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ # AppInfo base file
4
+ module AppInfo
5
+ class File
6
+ attr_reader :file, :logger
7
+
8
+ def initialize(file, logger: AppInfo.logger)
9
+ @file = file
10
+ @logger = logger
11
+ end
12
+
13
+ # @return [Symbol] {Format}
14
+ def format
15
+ @format ||= lambda {
16
+ if instance_of?(AppInfo::File) || instance_of?(AppInfo::Apple) ||
17
+ instance_of?(AppInfo::Android)
18
+ not_implemented_error!(__method__)
19
+ end
20
+
21
+ self.class.name.split('::')[-1].downcase.to_sym
22
+ }.call
23
+ end
24
+
25
+ # @abstract Subclass and override {#platform} to implement.
26
+ def platform
27
+ not_implemented_error!(__method__)
28
+ end
29
+
30
+ # @abstract Subclass and override {#manufacturer} to implement.
31
+ def manufacturer
32
+ not_implemented_error!(__method__)
33
+ end
34
+
35
+ # @abstract Subclass and override {#device} to implement.
36
+ def device
37
+ not_implemented_error!(__method__)
38
+ end
39
+
40
+ # @abstract Subclass and override {#size} to implement
41
+ def size(human_size: false)
42
+ not_implemented_error!(__method__)
43
+ end
44
+
45
+ def not_implemented_error!(method)
46
+ raise NotImplementedError, ".#{method} method implantation required in #{self.class}"
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zip'
4
+ require 'tmpdir'
5
+ require 'fileutils'
6
+ require 'securerandom'
7
+
8
+ module AppInfo::Helper
9
+ module Archive
10
+ # Unarchive zip file
11
+ #
12
+ # source: https://github.com/soffes/lagunitas/blob/master/lib/lagunitas/ipa.rb
13
+ def unarchive(file, prefix:, dest_path: '/tmp')
14
+ base_path = Dir.mktmpdir("appinfo-#{prefix}", dest_path)
15
+ Zip::File.open(file) do |zip_file|
16
+ if block_given?
17
+ yield base_path, zip_file
18
+ else
19
+ zip_file.each do |f|
20
+ f_path = ::File.join(base_path, f.name)
21
+ FileUtils.mkdir_p(::File.dirname(f_path))
22
+ zip_file.extract(f, f_path) unless ::File.exist?(f_path)
23
+ end
24
+ end
25
+ end
26
+
27
+ base_path
28
+ end
29
+
30
+ def tempdir(file, prefix:, system: false)
31
+ base_path = system ? '/tmp' : ::File.dirname(file)
32
+ full_prefix = "appinfo-#{prefix}-#{::File.basename(file, '.*')}"
33
+ dest_path = Dir.mktmpdir(full_prefix, base_path)
34
+ ::File.join(dest_path, ::File.basename(file))
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppInfo::Helper
4
+ module HumanFileSize
5
+ def file_to_human_size(file, human_size:)
6
+ number = ::File.size(file)
7
+ human_size ? number_to_human_size(number) : number
8
+ end
9
+
10
+ FILE_SIZE_UNITS = %w[B KB MB GB TB].freeze
11
+
12
+ def number_to_human_size(number)
13
+ if number.to_i < 1024
14
+ exponent = 0
15
+ else
16
+ max_exp = FILE_SIZE_UNITS.size - 1
17
+ exponent = (Math.log(number) / Math.log(1024)).to_i
18
+ exponent = max_exp if exponent > max_exp
19
+ number = Kernel.format('%<number>.2f', number: (number / (1024**exponent.to_f)))
20
+ end
21
+
22
+ "#{number} #{FILE_SIZE_UNITS[exponent]}"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppInfo::Helper
4
+ module GenerateClass
5
+ def create_class(klass_name, parent_class, namespace:)
6
+ klass = Class.new(parent_class) do
7
+ yield if block_given?
8
+ end
9
+
10
+ name = namespace.to_s.empty? ? klass_name : "#{namespace}::#{klass_name}"
11
+ if Object.const_get(namespace).const_defined?(klass_name)
12
+ Object.const_get(namespace).const_get(klass_name)
13
+ elsif Object.const_defined?(name)
14
+ Object.const_get(name)
15
+ else
16
+ Object.const_get(namespace).const_set(klass_name, klass)
17
+ end
18
+ end
19
+
20
+ def define_instance_method(key, value)
21
+ instance_variable_set("@#{key}", value)
22
+ self.class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
23
+ def #{key}
24
+ @#{key}
25
+ end
26
+ RUBY
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppInfo::Helper
4
+ module Protobuf
5
+ def reference_segments(value)
6
+ new_value = value.is_a?(Aapt::Pb::Reference) ? value.name : value
7
+ return new_value.split('/', 2) if new_value.include?('/')
8
+
9
+ [nil, new_value]
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,229 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppInfo::Helper
4
+ # Binary IO Block Helper
5
+ module IOBlock
6
+ def length_prefix_block(
7
+ io, size: AppInfo::Android::Signature::UINT32_SIZE,
8
+ raw: false, ignore_left_size_precheck: false
9
+ )
10
+ offset = io.size - io.pos
11
+ if offset < AppInfo::Android::Signature::UINT32_SIZE
12
+ raise SecurityError,
13
+ 'Remaining buffer too short to contain length of length-prefixed field.'
14
+ end
15
+
16
+ size = io.read(size).unpack1('I')
17
+ raise SecurityError, 'Negative length' if size.negative?
18
+
19
+ if !ignore_left_size_precheck && size > io.size
20
+ message = "Underflow while reading length-prefixed value. #{size} > #{io.size}"
21
+ raise SecurityError, message
22
+ end
23
+
24
+ raw_data = io.read(size)
25
+ raw ? raw_data : StringIO.new(raw_data)
26
+ end
27
+
28
+ # Only use for uint32 length-prefixed block
29
+ def loop_length_prefix_io(
30
+ io, name:, max_bytes: nil, logger: nil, raw: false,
31
+ ignore_left_size_precheck: false, &block
32
+ )
33
+ index = 0
34
+ until io.eof?
35
+ logger&.debug "#{name} count ##{index}"
36
+ buffer = length_prefix_block(
37
+ io,
38
+ raw: raw,
39
+ ignore_left_size_precheck: ignore_left_size_precheck
40
+ )
41
+
42
+ left_bytes_check(buffer, max_bytes, SecurityError) do |left_bytes|
43
+ "#{name} too short: #{left_bytes} < #{max_bytes}"
44
+ end
45
+
46
+ block.call(buffer)
47
+ index += 1
48
+ end
49
+ end
50
+
51
+ def left_bytes_check(io, max_bytes, exception, message = nil, &block)
52
+ return if max_bytes.nil?
53
+
54
+ left_bytes = io.size - io.pos
55
+ return left_bytes if left_bytes.zero?
56
+
57
+ message ||= if block_given?
58
+ block.call(left_bytes)
59
+ else
60
+ "IO too short: #{offset} < #{max_bytes}"
61
+ end
62
+
63
+ raise exception, message if left_bytes < max_bytes
64
+
65
+ left_bytes
66
+ end
67
+ end
68
+
69
+ # Signature Block helper
70
+ module Signatures
71
+ def singers_block(block_id)
72
+ info = AppInfo::Android::Signature::Info.new(@version, @parser, logger)
73
+ raise SecurityError, 'ZIP64 APK not supported' if info.zip64?
74
+
75
+ info.signers(block_id)
76
+ end
77
+
78
+ def signed_data_certs(io)
79
+ certificates = []
80
+ loop_length_prefix_io(io, name: 'Certificates', raw: true) do |cert_data|
81
+ certificates << AppInfo::Certificate.parse(cert_data)
82
+ end
83
+ certificates
84
+ end
85
+
86
+ def signed_data_digests(io)
87
+ content_digests = {}
88
+ loop_length_prefix_io(
89
+ io,
90
+ name: 'Digests',
91
+ max_bytes: AppInfo::Android::Signature::UINT64_SIZE
92
+ ) do |digest|
93
+ algorithm = digest.read(AppInfo::Android::Signature::UINT32_SIZE).unpack('C*')
94
+ digest_name = algorithm_match(algorithm)
95
+ next unless digest_name
96
+
97
+ content = length_prefix_block(digest)
98
+ content_digests[digest_name] = {
99
+ id: algorithm,
100
+ content: content
101
+ }
102
+ end
103
+
104
+ content_digests
105
+ end
106
+
107
+ # FIXME: this code not work, need fix.
108
+ def verify_additional_attrs(attrs, _certs)
109
+ loop_length_prefix_io(
110
+ attrs, name: 'Additional Attributes', ignore_left_size_precheck: true
111
+ ) do |attr|
112
+ id = attr.read(AppInfo::Android::Signature::UINT32_SIZE)
113
+ logger.debug "ID #{id} / #{id.size} / #{id.unpack('H*')} / #{id.unpack('C*')}"
114
+ if id.unpack('C*') == AppInfo::Helper::Algorithm::SIG_STRIPPING_PROTECTION_ATTR_ID
115
+ offset = attr.size - attr.pos
116
+ if offset < AppInfo::Android::Signature::UINT32_SIZE
117
+ raise SecurityError,
118
+ "V2 Signature Scheme Stripping Protection Attribute value too small. Expected #{UINT32_SIZE} bytes, but found #{offset}"
119
+ end
120
+
121
+ # value = attr.read(UINT32_SIZE).unpack1('I')
122
+ if @version == AppInfo::Android::Signature::Version::V3
123
+ raise SecurityError,
124
+ 'V2 signature indicates APK is signed using APK Signature Scheme v3, but none was found. Signature stripped?'
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ def signature_algorithms(signatures)
131
+ algorithems = []
132
+ loop_length_prefix_io(
133
+ signatures,
134
+ name: 'Signature Algorithms',
135
+ max_bytes: AppInfo::Android::Signature::UINT64_SIZE,
136
+ logger: logger
137
+ ) do |signature|
138
+ algorithm = signature.read(AppInfo::Android::Signature::UINT32_SIZE).unpack('C*')
139
+ digest = algorithm_match(algorithm)
140
+ next unless digest
141
+
142
+ signature = length_prefix_block(signature, raw: true)
143
+ algorithems << {
144
+ id: algorithm,
145
+ digest: digest,
146
+ signature: signature
147
+ }
148
+ end
149
+
150
+ algorithems
151
+ end
152
+ end
153
+
154
+ # Signature Algorithm helper
155
+ module Algorithm
156
+ # Signature certificate identifiers
157
+ SIG_RSA_PSS_WITH_SHA256 = [0x01, 0x01, 0x00, 0x00].freeze # 0x0101
158
+ SIG_RSA_PSS_WITH_SHA512 = [0x02, 0x01, 0x00, 0x00].freeze # 0x0102
159
+ SIG_RSA_PKCS1_V1_5_WITH_SHA256 = [0x03, 0x01, 0x00, 0x00].freeze # 0x0103
160
+ SIG_RSA_PKCS1_V1_5_WITH_SHA512 = [0x04, 0x01, 0x00, 0x00].freeze # 0x0104
161
+ SIG_ECDSA_WITH_SHA256 = [0x01, 0x02, 0x00, 0x00].freeze # 0x0201
162
+ SIG_ECDSA_WITH_SHA512 = [0x02, 0x02, 0x00, 0x00].freeze # 0x0202
163
+ SIG_DSA_WITH_SHA256 = [0x01, 0x03, 0x00, 0x00].freeze # 0x0301
164
+ SIG_VERITY_RSA_PKCS1_V1_5_WITH_SHA256 = [0x21, 0x04, 0x00, 0x00].freeze # 0x0421
165
+ SIG_VERITY_ECDSA_WITH_SHA256 = [0x23, 0x04, 0x00, 0x00].freeze # 0x0423
166
+ SIG_VERITY_DSA_WITH_SHA256 = [0x25, 0x04, 0x00, 0x00].freeze # 0x0425
167
+
168
+ SIG_STRIPPING_PROTECTION_ATTR_ID = [0x0d, 0xf0, 0xef, 0xbe].freeze # 0xbeeff00d
169
+
170
+ def best_algorithem(algorithems)
171
+ methods = algorithems.map { |algorithem| algorithem[:method] }
172
+ best_method = methods.max { |a, b| algorithem_priority(a) <=> algorithem_priority(b) }
173
+ best_method_index = methods.index(best_method)
174
+ algorithems[best_method_index]
175
+ end
176
+
177
+ def compare_algorithem(source, target)
178
+ case algorithem_priority(source) <=> algorithem_priority(target)
179
+ when -1
180
+ target
181
+ else
182
+ source
183
+ end
184
+ end
185
+
186
+ def algorithem_priority(algorithm)
187
+ case algorithm
188
+ when SIG_RSA_PSS_WITH_SHA256,
189
+ SIG_RSA_PKCS1_V1_5_WITH_SHA256,
190
+ SIG_ECDSA_WITH_SHA256,
191
+ SIG_DSA_WITH_SHA256
192
+ 1
193
+ when SIG_RSA_PSS_WITH_SHA512,
194
+ SIG_RSA_PKCS1_V1_5_WITH_SHA512,
195
+ SIG_ECDSA_WITH_SHA512
196
+ 2
197
+ when SIG_VERITY_RSA_PKCS1_V1_5_WITH_SHA256,
198
+ SIG_VERITY_ECDSA_WITH_SHA256,
199
+ SIG_VERITY_DSA_WITH_SHA256
200
+ 3
201
+ end
202
+ end
203
+
204
+ def algorithm_method(algorithm)
205
+ case algorithm
206
+ when SIG_RSA_PSS_WITH_SHA256, SIG_RSA_PSS_WITH_SHA512,
207
+ SIG_RSA_PKCS1_V1_5_WITH_SHA256, SIG_RSA_PKCS1_V1_5_WITH_SHA512,
208
+ SIG_VERITY_RSA_PKCS1_V1_5_WITH_SHA256
209
+ :rsa
210
+ when SIG_ECDSA_WITH_SHA256, SIG_ECDSA_WITH_SHA512, SIG_VERITY_ECDSA_WITH_SHA256
211
+ :ec
212
+ when SIG_DSA_WITH_SHA256, SIG_VERITY_DSA_WITH_SHA256
213
+ :dsa
214
+ end
215
+ end
216
+
217
+ def algorithm_match(algorithm)
218
+ case algorithm
219
+ when SIG_RSA_PSS_WITH_SHA256, SIG_RSA_PKCS1_V1_5_WITH_SHA256,
220
+ SIG_ECDSA_WITH_SHA256, SIG_DSA_WITH_SHA256,
221
+ SIG_VERITY_RSA_PKCS1_V1_5_WITH_SHA256, SIG_VERITY_ECDSA_WITH_SHA256,
222
+ SIG_VERITY_DSA_WITH_SHA256
223
+ 'SHA256'
224
+ when SIG_RSA_PSS_WITH_SHA512, SIG_RSA_PKCS1_V1_5_WITH_SHA512, SIG_ECDSA_WITH_SHA512
225
+ 'SHA512'
226
+ end
227
+ end
228
+ end
229
+ end