app-info 2.8.5 → 3.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +12 -7
  3. data/.github/workflows/ci.yml +3 -1
  4. data/.rubocop.yml +31 -11
  5. data/CHANGELOG.md +22 -0
  6. data/Gemfile +7 -1
  7. data/README.md +64 -9
  8. data/Rakefile +11 -0
  9. data/app_info.gemspec +12 -3
  10. data/lib/app_info/aab.rb +58 -39
  11. data/lib/app_info/android/signature.rb +114 -0
  12. data/lib/app_info/android/signatures/base.rb +49 -0
  13. data/lib/app_info/android/signatures/info.rb +152 -0
  14. data/lib/app_info/android/signatures/v1.rb +59 -0
  15. data/lib/app_info/android/signatures/v2.rb +117 -0
  16. data/lib/app_info/android/signatures/v3.rb +127 -0
  17. data/lib/app_info/android/signatures/v4.rb +14 -0
  18. data/lib/app_info/apk.rb +43 -46
  19. data/lib/app_info/certificate.rb +181 -0
  20. data/lib/app_info/const.rb +41 -0
  21. data/lib/app_info/core_ext/object/try.rb +3 -1
  22. data/lib/app_info/core_ext/string/inflector.rb +2 -0
  23. data/lib/app_info/dsym/debug_info.rb +72 -0
  24. data/lib/app_info/dsym/macho.rb +55 -0
  25. data/lib/app_info/dsym.rb +27 -134
  26. data/lib/app_info/error.rb +7 -1
  27. data/lib/app_info/file.rb +23 -0
  28. data/lib/app_info/helper/archive.rb +37 -0
  29. data/lib/app_info/helper/file_size.rb +25 -0
  30. data/lib/app_info/helper/generate_class.rb +29 -0
  31. data/lib/app_info/helper/protobuf.rb +12 -0
  32. data/lib/app_info/helper/signatures.rb +229 -0
  33. data/lib/app_info/helper.rb +5 -126
  34. data/lib/app_info/info_plist.rb +14 -6
  35. data/lib/app_info/ipa/framework.rb +4 -4
  36. data/lib/app_info/ipa.rb +41 -36
  37. data/lib/app_info/macos.rb +34 -26
  38. data/lib/app_info/mobile_provision.rb +19 -30
  39. data/lib/app_info/pe.rb +226 -0
  40. data/lib/app_info/png_uncrush.rb +5 -4
  41. data/lib/app_info/proguard.rb +11 -17
  42. data/lib/app_info/protobuf/manifest.rb +1 -2
  43. data/lib/app_info/protobuf/models/Configuration_pb.rb +1 -0
  44. data/lib/app_info/protobuf/models/README.md +7 -0
  45. data/lib/app_info/protobuf/models/Resources_pb.rb +2 -0
  46. data/lib/app_info/protobuf/resources.rb +5 -5
  47. data/lib/app_info/version.rb +1 -1
  48. data/lib/app_info.rb +88 -45
  49. metadata +46 -35
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f9406b7552e0bea00883446b85250398c7818120de74d0d6d3545d7b9b0d7ef6
4
- data.tar.gz: a99bb2c55b4214ad72f8e6cf2e720199902fb37ad174b71bb5f9f4d529ba81a3
3
+ metadata.gz: 35509386f8425f29ea466dbcb1ce12cf54be230f9b4668d4ad2f5ac9b4a4b1b5
4
+ data.tar.gz: 6f3f87d116877fd058a9f479de6b0aa789d74c255f832f5db132446e6250b82e
5
5
  SHA512:
6
- metadata.gz: '018d93bb67f489d4908f0701285ee6c177a627f3b5c602e90c24629da25f91fc2f5cb9b091675d471741361211289b71ed7c1ab51402f30cea843126c9c6271c'
7
- data.tar.gz: 5eecd5818b12032898d33a14f74c43592d120ab751b2e4393591b0fd81e4df5cebb94342111478392fd84c5963f762de0383873c547176f74d546f7bfb7b8013
6
+ metadata.gz: b62671980b8d9e7a854523e24311af90e78b807023b65aa1d417159717c3119c34beef4e589638d3fa3708820c8732a1c82859f8b2194477a90548dfd0611b0a
7
+ data.tar.gz: dc22e6f681b64658bacd6206c3cb7bdceb4afac7fac9332af230563930d8df24da1b95336647c25f7f84537c7716804d5d27e7409baa08b30e5f436e3a505c16
@@ -1,9 +1,14 @@
1
1
  version: 2
2
2
  updates:
3
- - package-ecosystem: bundler
4
- directory: "/"
5
- schedule:
6
- interval: daily
7
- time: "21:00"
8
- timezone: Asia/Shanghai
9
- open-pull-requests-limit: 10
3
+ - package-ecosystem: bundler
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ time: "21:00"
8
+ timezone: Asia/Shanghai
9
+ open-pull-requests-limit: 10
10
+
11
+ - package-ecosystem: "github-actions"
12
+ directory: "/"
13
+ schedule:
14
+ interval: "daily"
@@ -1,8 +1,10 @@
1
1
  name: CI
2
2
  on:
3
3
  push:
4
+ paths-ignore:
5
+ - '*.md'
4
6
  branches:
5
- - master
7
+ - main
6
8
  pull_request:
7
9
 
8
10
  jobs:
data/.rubocop.yml CHANGED
@@ -27,6 +27,22 @@ AllCops:
27
27
  - 'lib/app_info/protobuf/models/*_pb.rb'
28
28
  - 'main.rb'
29
29
 
30
+ Layout/LineLength:
31
+ Max: 100
32
+ Exclude:
33
+ - 'lib/app_info/helper/signatures.rb'
34
+
35
+ Lint/AssignmentInCondition:
36
+ Enabled: false
37
+
38
+ Lint/UnusedMethodArgument:
39
+ Exclude:
40
+ - 'lib/app_info/file.rb'
41
+
42
+ Lint/UselessAssignment:
43
+ Exclude:
44
+ - 'lib/app_info/android/signatures/v3.rb'
45
+
30
46
  Metrics/AbcSize:
31
47
  Max: 100
32
48
 
@@ -36,13 +52,15 @@ Metrics/BlockLength:
36
52
  - 'lib/app_info/mobile_provision.rb'
37
53
 
38
54
  Metrics/MethodLength:
39
- Max: 20
55
+ Max: 30
40
56
  Exclude:
41
- - 'lib/app_info/png_uncrush.rb'
42
57
  - 'lib/app_info/mobile_provision.rb'
58
+ - 'lib/app_info/android/signatures/v2.rb'
59
+ - 'lib/app_info/android/signatures/v3.rb'
43
60
 
44
- Layout/LineLength:
45
- Max: 100
61
+ Metrics/ParameterLists:
62
+ Exclude:
63
+ - 'lib/app_info/helper/signatures.rb'
46
64
 
47
65
  Metrics/ClassLength:
48
66
  CountComments: false
@@ -54,12 +72,16 @@ Metrics/CyclomaticComplexity:
54
72
  Metrics/PerceivedComplexity:
55
73
  Max: 18
56
74
 
57
- Lint/AssignmentInCondition:
58
- Enabled: false
75
+ Metrics/BlockNesting:
76
+ Exclude:
77
+ - 'lib/app_info/dsym.rb'
59
78
 
60
79
  Style/Documentation:
61
80
  Enabled: false
62
81
 
82
+ Style/ClassAndModuleChildren:
83
+ Enabled: false
84
+
63
85
  Style/PerlBackrefs:
64
86
  Exclude:
65
87
  - 'lib/app_info/core_ext/string/inflector.rb'
@@ -67,10 +89,8 @@ Style/PerlBackrefs:
67
89
  Style/DocumentDynamicEvalDefinition:
68
90
  Enabled: false
69
91
 
70
- Metrics/BlockNesting:
71
- Exclude:
72
- - 'lib/app_info/dsym.rb'
73
-
74
-
75
92
  Style/SlicingWithRange:
76
93
  Enabled: false
94
+
95
+ Style/ClassVars:
96
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -9,6 +9,28 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
9
9
 
10
10
  > List all changes before release a new version.
11
11
 
12
+ ## [3.0.0.beta1] (2023-04-?)
13
+
14
+ ### Added
15
+
16
+ - New Windows PE format parser. [#47](https://github.com/icyleaf/app_info/pull/47)
17
+ - Android parser add v2, v3 scheme signature support. [#55](https://github.com/icyleaf/app_info/pull/55]
18
+ - dSYM parer accept multi dSYM target in a zip file. [#56](https://github.com/icyleaf/app_info/pull/56)
19
+
20
+ ### Changed
21
+
22
+ - Add `AppInfo::File` base class for all parsers.
23
+ - Add `AppInfo::Certifiate` X509 certificate wrapped and apply in Android/MobileProvision.
24
+ - Remove `AppInfo::MobileProvision::DeveloperCertificate` class, use `AppInfo::Certifiate` instead.
25
+ - Remove `.sign_version` method in Android parser.
26
+ - Remove duplice `AppInfo::AndroidDevice` class.
27
+ - Deprecate `.signs` and `.certifiates` methods in Android parser, use `.signatures` instead.
28
+ - Deprecate `.developer_certs` method in MobileProvision parser, use `.certificates` instead.
29
+
30
+ ### Fixed
31
+
32
+ - Fixed minor typo.
33
+
12
34
  ## [2.8.5] (2023-03-16)
13
35
 
14
36
  ### Fixed
data/Gemfile CHANGED
@@ -7,5 +7,11 @@ gemspec
7
7
 
8
8
  group :development do
9
9
  gem 'awesome_print'
10
- gem 'debug' # For ruby 3.0+
10
+ gem 'debug' # For ruby 3.0+\
11
+ gem 'yard'
12
+ end
13
+
14
+ group :development, :test do
15
+ gem 'rspec'
16
+ gem 'rubocop'
11
17
  end
data/README.md CHANGED
@@ -5,7 +5,8 @@
5
5
  [![Gem version](https://img.shields.io/gem/v/app-info.svg?style=flat)](https://rubygems.org/gems/app_info)
6
6
  [![License](https://img.shields.io/badge/license-MIT-red.svg?style=flat)](LICENSE)
7
7
 
8
- Teardown tool for mobile app (ipa, apk and aab file), macOS app and dSYM.zip file, analysis metedata like version, name, icon etc.
8
+ Teardown tool for mobile app (ipa, apk and aab file), macOS app, dSYM.zip file and Windows PE file.
9
+ Analysis metedata like version, name, icon etc.
9
10
 
10
11
  ## Support
11
12
 
@@ -16,10 +17,13 @@ Teardown tool for mobile app (ipa, apk and aab file), macOS app and dSYM.zip fil
16
17
  - `.ipa`
17
18
  - `Info.plist` file
18
19
  - `.mobileprovision`/`.provisionprofile` file
19
- - Zipped macOS App file
20
+ - macOS App file (archived by starnd pkzip format)
20
21
  - `.app.zip`
21
- - Zipped dSYMs file
22
+ - dSYMs file (archived by starnd pkzip format)
22
23
  - `.dSYM.zip`
24
+ - Windows PE file
25
+ - `.exe`
26
+ - `.zip` (binary in a zip file)
23
27
 
24
28
  <hr />
25
29
 
@@ -65,6 +69,8 @@ parser = AppInfo.parse('android.aab')
65
69
  parser = AppInfo.parse('u-u-i-d.mobileprovision')
66
70
  parser = AppInfo.parse('macOS.App.zip')
67
71
  parser = AppInfo.parse('App.dSYm.zip')
72
+ parser = AppInfo.parse('win.exe')
73
+ parser = AppInfo.parse('win.zip')
68
74
 
69
75
  # If detect file type failed, you can parse in other way
70
76
  parser = AppInfo::IPA.new('iphone.ipa')
@@ -74,6 +80,7 @@ parser = AppInfo::InfoPlist.new('Info.plist')
74
80
  parser = AppInfo::MobileProvision.new('uuid.mobileprovision')
75
81
  parser = AppInfo::Macos.new('App.dSYm.zip')
76
82
  parser = AppInfo::DSYM.new('App.dSYm.zip')
83
+ parser = AppInfo::PE.new('win.exe')
77
84
  ```
78
85
 
79
86
  ### iOS
@@ -220,12 +227,8 @@ android.deep_links
220
227
  android.schemes
221
228
  # => ['appinfo']
222
229
 
223
- # get sign list (only v1 sign)
224
- android.signs
225
- # => [...]
226
-
227
- # get certificate list (only v1 sign)
228
- android.certificates
230
+ # get v1-v3 scheme singature information (included unverified certificate and more)
231
+ android.signatures
229
232
  # => [...]
230
233
  ```
231
234
 
@@ -313,6 +316,58 @@ dsym.machos.each do |macho|
313
316
  end
314
317
  ```
315
318
 
319
+ ### Windows
320
+
321
+ Accept any PE format file, such like `.exe` or `.exe` binary fin a zip file.
322
+
323
+ ```ruby
324
+ win = AppInfo.parse('win.exe')
325
+
326
+ # get given file size
327
+ win.size
328
+ # => 3093823
329
+
330
+ # get given file size in human reable.
331
+ win.size(human_size: true)
332
+ # => 29 MB
333
+
334
+ # get given file size
335
+ win.binary_size
336
+ # => 20940
337
+
338
+ # get given file size in human reable.
339
+ win.size(human_size: true)
340
+ # => 20 MB
341
+
342
+ # get product name
343
+ win.name
344
+ # => AppInfo
345
+
346
+ # get app company name
347
+ win.company_name
348
+ # => EWS Studio
349
+
350
+ # get app product version (alias to release_version)
351
+ win.product_version
352
+ # => 1.0.0
353
+
354
+ # get app assembly version (alias to build_version)
355
+ win.assembly_version
356
+ # => 1.0.0
357
+
358
+ # detect architecture(s)
359
+ win.archs
360
+ # => x64
361
+
362
+ # get all imports files
363
+ win.imports
364
+ # => [KERNEL32.dll, ...]
365
+
366
+ # get app icons (bmp format image)
367
+ win.icons
368
+ # => [{:name=>"ICON.bmp", :file=>"{path}/ICON.bmp"}, :dimensions=>[64, 64]}, ...]
369
+ ```
370
+
316
371
  ## CLI Shell (Interactive console)
317
372
 
318
373
  It is possible to use this gem as a command line interface to parse mobile app:
data/Rakefile CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  $LOAD_PATH.unshift File.expand_path('lib', __dir__)
4
4
  require 'app_info'
5
+ require 'yard'
5
6
  require 'bundler/gem_tasks'
6
7
  require 'rspec/core/rake_task'
7
8
  require 'rubocop/rake_task'
@@ -9,3 +10,13 @@ require 'rubocop/rake_task'
9
10
  RSpec::Core::RakeTask.new(:spec)
10
11
 
11
12
  task default: :spec
13
+
14
+ task :doc do
15
+ YARD::Rake::YardocTask.new do |t|
16
+ t.files = ['lib/**/*.rb', 'README.md', 'LICENSE']
17
+ # t.options = ['--any', '--extra', '--opts'] # optional
18
+ # t.stats_options = ['--list-undoc'] # optional
19
+ end
20
+ end
21
+
22
+
data/app_info.gemspec CHANGED
@@ -23,14 +23,23 @@ Gem::Specification.new do |spec|
23
23
  spec.add_dependency 'CFPropertyList', '< 3.1.0', '>= 2.3.4'
24
24
  spec.add_dependency 'image_size', '>= 1.5', '< 3.3'
25
25
  spec.add_dependency 'ruby-macho', '>= 1.4', '< 4'
26
- spec.add_dependency 'android_parser', '~> 2.5.1'
26
+ spec.add_dependency 'android_parser', '~> 2.6.0'
27
27
  spec.add_dependency 'rubyzip', '>= 1.2', '< 3.0'
28
28
  spec.add_dependency 'uuidtools', '>= 2.1.5', '< 2.3.0'
29
29
  spec.add_dependency 'icns', '~> 0.2.0'
30
+ spec.add_dependency 'pedump', '~> 0.6.2'
30
31
  spec.add_dependency 'google-protobuf', '>= 3.19.4', '< 3.23.0'
31
32
 
32
33
  spec.add_development_dependency 'bundler', '>= 1.12'
33
34
  spec.add_development_dependency 'rake', '>= 10.0'
34
- spec.add_development_dependency 'rspec', '~> 3.0'
35
- spec.add_development_dependency 'rubocop', '~> 1.19'
35
+
36
+ spec.post_install_message = <<~ENDBANNER
37
+ AppInfo 3.0 is coming!
38
+ **********************
39
+ The public API of some AppInfo classes has been changed.
40
+
41
+ Please ensure that your Gemfiles and .gemspecs are suitably restrictive
42
+ to avoid an unexpected breakage when 3.0 is released (e.g. ~> 2.8.5).
43
+ See https://github.com/icyleaf/app_info for details.
44
+ ENDBANNER
36
45
  end
data/lib/app_info/aab.rb CHANGED
@@ -5,8 +5,8 @@ require 'image_size'
5
5
  require 'forwardable'
6
6
 
7
7
  module AppInfo
8
- # Parse APK file
9
- class AAB
8
+ # Parse APK file parser
9
+ class AAB < File
10
10
  include Helper::HumanFileSize
11
11
  extend Forwardable
12
12
 
@@ -25,38 +25,52 @@ module AppInfo
25
25
  BASE_MANIFEST = "#{BASE_PATH}/manifest/AndroidManifest.xml"
26
26
  BASE_RESOURCES = "#{BASE_PATH}/resources.pb"
27
27
 
28
- def initialize(file)
29
- @file = file
30
- end
31
-
28
+ # return file size
29
+ # @example Read file size in integer
30
+ # aab.size # => 3618865
31
+ #
32
+ # @example Read file size in human readabale
33
+ # aab.size(human_size: true) # => '3.45 MB'
34
+ #
35
+ # @param [Boolean] human_size Convert integer value to human readable.
36
+ # @return [Integer, String]
32
37
  def size(human_size: false)
33
38
  file_to_human_size(@file, human_size: human_size)
34
39
  end
35
40
 
36
- def os
41
+ # @return [Symbol] {Format::AAB}
42
+ def file_type
43
+ Format::AAB
44
+ end
45
+
46
+ # @return [String] {Platform::ANDROID}
47
+ def platform
37
48
  Platform::ANDROID
38
49
  end
39
- alias file_type os
40
50
 
41
51
  def_delegators :manifest, :version_name, :deep_links, :schemes
42
52
 
43
53
  alias release_version version_name
44
54
 
55
+ # @return [String]
45
56
  def package_name
46
57
  manifest.package
47
58
  end
48
59
  alias identifier package_name
49
60
  alias bundle_id package_name
50
61
 
62
+ # @return [String]
51
63
  def version_code
52
64
  manifest.version_code.to_s
53
65
  end
54
66
  alias build_version version_code
55
67
 
68
+ # @return [String]
56
69
  def name
57
70
  manifest.label
58
71
  end
59
72
 
73
+ # @return [String] {Device}
60
74
  def device_type
61
75
  if wear?
62
76
  Device::WATCH
@@ -78,33 +92,40 @@ module AppInfo
78
92
  # .size >= 1
79
93
  # end
80
94
 
95
+ # @return [Boolean]
81
96
  def wear?
82
97
  !!use_features&.include?('android.hardware.type.watch')
83
98
  end
84
99
 
100
+ # @return [Boolean]
85
101
  def tv?
86
102
  !!use_features&.include?('android.software.leanback')
87
103
  end
88
104
 
105
+ # @return [Boolean]
89
106
  def automotive?
90
107
  !!use_features&.include?('android.hardware.type.automotive')
91
108
  end
92
109
 
110
+ # @return [String]
93
111
  def min_sdk_version
94
112
  manifest.uses_sdk.min_sdk_version
95
113
  end
96
114
  alias min_os_version min_sdk_version
97
115
 
116
+ # @return [String]
98
117
  def target_sdk_version
99
118
  manifest.uses_sdk.target_sdk_version
100
119
  end
101
120
 
121
+ # @return [Array<String>]
102
122
  def use_features
103
123
  return [] unless manifest.respond_to?(:uses_feature)
104
124
 
105
125
  @use_features ||= manifest&.uses_feature&.map(&:name)
106
126
  end
107
127
 
128
+ # @return [Array<String>]
108
129
  def use_permissions
109
130
  return [] unless manifest.respond_to?(:uses_permission)
110
131
 
@@ -123,36 +144,25 @@ module AppInfo
123
144
  @components ||= manifest.components.transform_values
124
145
  end
125
146
 
126
- def sign_version
127
- return 'v1' unless signs.empty?
128
-
129
- # when ?
130
- # https://source.android.com/security/apksigning/v2?hl=zh-cn
131
- # 'v2'
132
- # when ?
133
- # https://source.android.com/security/apksigning/v3?hl=zh-cn
134
- # 'v3'
135
- 'unknown'
147
+ # Return multi version certifiates of signatures
148
+ # @return [Array<Hash>] signatures
149
+ # @see AppInfo::Android::Signature.verify
150
+ def signatures
151
+ @signatures ||= Android::Signature.verify(self)
136
152
  end
137
153
 
154
+ # Legacy v1 scheme signatures, it will remove soon.
155
+ # @deprecated Use {#signatures}
156
+ # @return [Array<OpenSSL::PKCS7, nil>] signatures
138
157
  def signs
139
- return @signs if @signs
140
-
141
- @signs = []
142
- each_file do |path, data|
143
- # find META-INF/xxx.{RSA|DSA}
144
- next unless path =~ %r{^META-INF/} && data.unpack('CC') == [0x30, 0x82]
145
-
146
- @signs << APK::Sign.new(path, OpenSSL::PKCS7.new(data))
147
- end
148
-
149
- @signs
158
+ @signs ||= v1sign&.signatures || []
150
159
  end
151
160
 
161
+ # Legacy v1 scheme certificates, it will remove soon.
162
+ # @deprecated Use {#signatures}
163
+ # @return [Array<OpenSSL::PKCS7, nil>] certificates
152
164
  def certificates
153
- @certificates ||= signs.each_with_object([]) do |sign, obj|
154
- obj << APK::Certificate.new(sign.path, sign.sign.certificates[0])
155
- end
165
+ @certificates ||= v1sign&.certificates || []
156
166
  end
157
167
 
158
168
  def each_file
@@ -171,7 +181,7 @@ module AppInfo
171
181
  end
172
182
 
173
183
  def entry(name, base_path: BASE_PATH)
174
- entry = @zip.find_entry(File.join(base_path, name))
184
+ entry = @zip.find_entry(::File.join(base_path, name))
175
185
  raise NotFoundError, "'#{name}'" if entry.nil?
176
186
 
177
187
  entry
@@ -182,25 +192,28 @@ module AppInfo
182
192
  @manifest ||= Protobuf::Manifest.parse(io, resource)
183
193
  end
184
194
 
195
+ # @return [Protobuf::Resources]
185
196
  def resource
186
197
  io = zip.read(zip.find_entry(BASE_RESOURCES))
187
198
  @resource ||= Protobuf::Resources.parse(io)
188
199
  end
189
200
 
201
+ # @return [Zip::File]
190
202
  def zip
191
203
  @zip ||= Zip::File.open(@file)
192
204
  end
193
205
 
206
+ # @return [Array<Hash>]
194
207
  def icons
195
208
  @icons ||= manifest.icons.each_with_object([]) do |res, obj|
196
209
  path = res.value
197
- filename = File.basename(path)
198
- filepath = File.join(contents, File.dirname(path))
199
- file = File.join(filepath, filename)
210
+ filename = ::File.basename(path)
211
+ filepath = ::File.join(contents, ::File.dirname(path))
212
+ file = ::File.join(filepath, filename)
200
213
  FileUtils.mkdir_p filepath
201
214
 
202
215
  binary_data = read_file(path)
203
- File.write(file, binary_data, encoding: Encoding::BINARY)
216
+ ::File.write(file, binary_data, encoding: Encoding::BINARY)
204
217
 
205
218
  obj << {
206
219
  name: filename,
@@ -223,13 +236,19 @@ module AppInfo
223
236
  end
224
237
 
225
238
  def contents
226
- @contents ||= File.join(Dir.mktmpdir, "AppInfo-android-#{SecureRandom.hex}")
239
+ @contents ||= ::File.join(Dir.mktmpdir, "AppInfo-android-#{SecureRandom.hex}")
227
240
  end
228
241
 
229
242
  private
230
243
 
244
+ def v1sign
245
+ @v1sign ||= Android::Signature::V1.verify(self)
246
+ rescue Android::Signature::NotFoundError
247
+ nil
248
+ end
249
+
231
250
  def xml_file?(file)
232
- File.extname(file) == '.xml'
251
+ ::File.extname(file) == '.xml'
233
252
  end
234
253
 
235
254
  # TODO: how to convert xml content after decode protoubufed content
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppInfo
4
+ module Android
5
+ # Android Signature
6
+ #
7
+ # Support digest and length:
8
+ #
9
+ # RSA:1024、2048、4096、8192、16384
10
+ # EC:NIST P-256、P-384、P-521
11
+ # DSA:1024、2048、3072
12
+ module Signature
13
+ class VersionError < Error; end
14
+ class SecurityError < Error; end
15
+ class NotFoundError < NotFoundError; end
16
+
17
+ module Version
18
+ V1 = 1
19
+ V2 = 2
20
+ V3 = 3
21
+ V4 = 4
22
+ end
23
+
24
+ # All registerd verions to verify
25
+ #
26
+ # key is the version
27
+ # value is the class
28
+ @versions = {}
29
+
30
+ class << self
31
+ # Verify Android Signature
32
+ #
33
+ # @example Get unverified v1 certificates, verified v2 certificates,
34
+ # and not found v3 certificate
35
+ #
36
+ # signature.versions(parser)
37
+ # # => [
38
+ # # {
39
+ # # version: 1,
40
+ # # verified: false,
41
+ # # certificates: [<AppInfo::Certificate>, ...],
42
+ # # verifier: AppInfo::Androig::Signature
43
+ # # },
44
+ # # {
45
+ # # version: 2,
46
+ # # verified: false,
47
+ # # certificates: [<AppInfo::Certificate>, ...],
48
+ # # verifier: AppInfo::Androig::Signature
49
+ # # },
50
+ # # {
51
+ # # version: 3
52
+ # # }
53
+ # # ]
54
+ # @todo version 4 no implantation yet
55
+ # @param [AppInfo::File] parser
56
+ # @param [Version, Integer] min_version
57
+ # @return [Array<Hash>] versions
58
+ def verify(parser, min_version: Version::V4)
59
+ min_version = min_version.to_i if min_version.is_a?(String)
60
+ if min_version && min_version > Version::V4
61
+ raise VersionError,
62
+ "No signature found in #{min_version} scheme or newer for android file"
63
+ end
64
+
65
+ if min_version.zero?
66
+ raise VersionError,
67
+ "Unkonwn version: #{min_version}, avaiables in 1/2/3 and 4 (no implantation yet)"
68
+ end
69
+
70
+ # try full version signatures if min_version is nil
71
+ versions = min_version.downto(Version::V1).each_with_object([]) do |version, signatures|
72
+ next unless kclass = fetch(version)
73
+
74
+ data = { version: version }
75
+ begin
76
+ verifier = kclass.verify(parser)
77
+ data[:verified] = verifier.verified
78
+ data[:certificates] = verifier.certificates
79
+ data[:verifier] = verifier
80
+ rescue SecurityError, NotFoundError
81
+ # not this version, try the low version
82
+ ensure
83
+ signatures << data
84
+ end
85
+ end
86
+
87
+ versions.sort_by { |entry| entry[:version] }
88
+ end
89
+
90
+ def registered
91
+ @versions.keys
92
+ end
93
+
94
+ def register(version, verifier)
95
+ @versions[version] = verifier
96
+ end
97
+
98
+ def fetch(version)
99
+ @versions[version]
100
+ end
101
+
102
+ def exist?(version)
103
+ @versions.key?(version)
104
+ end
105
+ end
106
+
107
+ UINT32_MAX_VALUE = 2_147_483_647
108
+ UINT32_SIZE = 4
109
+ UINT64_SIZE = 8
110
+ end
111
+ end
112
+ end
113
+
114
+ require 'app_info/android/signatures/base'