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
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'app_info/android/signatures/info'
4
+
5
+ module AppInfo::Android::Signature
6
+ class Base
7
+ def self.verify(parser)
8
+ instance = new(parser)
9
+ instance.verify
10
+ instance
11
+ end
12
+
13
+ DESCRIPTION = 'APK Signature Scheme'
14
+
15
+ attr_reader :verified
16
+
17
+ def initialize(parser)
18
+ @parser = parser
19
+ @verified = false
20
+ end
21
+
22
+ # @abstract Subclass and override {#verify} to implement
23
+ def verify
24
+ raise NotImplementedError, ".#{__method__} method implantation required in #{self.class}"
25
+ end
26
+
27
+ # @abstract Subclass and override {#certificates} to implement
28
+ def certificates
29
+ raise NotImplementedError, ".#{__method__} method implantation required in #{self.class}"
30
+ end
31
+
32
+ def scheme
33
+ "v#{version}"
34
+ end
35
+
36
+ def description
37
+ "#{DESCRIPTION} #{scheme}"
38
+ end
39
+
40
+ def logger
41
+ @parser.logger
42
+ end
43
+ end
44
+ end
45
+
46
+ require 'app_info/android/signatures/v1'
47
+ require 'app_info/android/signatures/v2'
48
+ require 'app_info/android/signatures/v3'
49
+ require 'app_info/android/signatures/v4'
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stringio'
4
+ require 'openssl'
5
+
6
+ module AppInfo::Android::Signature
7
+ # APK signature scheme signurate info
8
+ #
9
+ # FORMAT:
10
+ # OFFSET DATA TYPE DESCRIPTION
11
+ # * @+0 bytes uint64: size in bytes (excluding this field)
12
+ # * @+8 bytes payload
13
+ # * @-24 bytes uint64: size in bytes (same as the one above)
14
+ # * @-16 bytes uint128: magic value
15
+ class Info
16
+ include AppInfo::Helper::IOBlock
17
+
18
+ # Signature block information
19
+ SIG_SIZE_OF_BLOCK_SIZE = 8
20
+ SIG_MAGIC_BLOCK_SIZE = 16
21
+ SIG_BLOCK_MIN_SIZE = 32
22
+
23
+ # Magic value: APK Sig Block 42
24
+ SIG_MAGIC = [
25
+ 0x41, 0x50, 0x4b, 0x20, 0x53, 0x69,
26
+ 0x67, 0x20, 0x42, 0x6c, 0x6f, 0x63,
27
+ 0x6b, 0x20, 0x34, 0x32
28
+ ].freeze
29
+
30
+ attr_reader :total_size, :pairs, :magic, :logger
31
+
32
+ def initialize(version, parser, logger)
33
+ @version = version
34
+ @parser = parser
35
+ @logger = logger
36
+
37
+ pares_signatures_pairs
38
+ end
39
+
40
+ # Find singers
41
+ #
42
+ # FORMAT:
43
+ # OFFSET DATA TYPE DESCRIPTION
44
+ # * @+0 bytes uint64: size in bytes
45
+ # * @+8 bytes payload block
46
+ # * @+0 bytes uint32: id
47
+ # * @+4 bytes payload: value
48
+ def signers(block_id)
49
+ count = 0
50
+ until @pairs.eof?
51
+ left_bytes = left_bytes_check(
52
+ @pairs, UINT64_SIZE, NotFoundError,
53
+ "Insufficient data to read size of APK Signing Block ##{count}"
54
+ )
55
+
56
+ pair_buf = @pairs.read(UINT64_SIZE)
57
+ pair_size = pair_buf.unpack1('Q')
58
+ if pair_size < UINT32_SIZE || pair_size > UINT32_MAX_VALUE
59
+ raise NotFoundError,
60
+ "APK Signing Block ##{count} size out of range: #{pair_size} > #{UINT32_MAX_VALUE}"
61
+ end
62
+
63
+ if pair_size > left_bytes
64
+ raise NotFoundError,
65
+ "APK Signing Block ##{count} size out of range: #{pair_size} > #{left_bytes}"
66
+ end
67
+
68
+ # fetch next signer block position
69
+ next_pos = @pairs.pos + pair_size.to_i
70
+
71
+ id_block = @pairs.read(UINT32_SIZE)
72
+ id_bytes = id_block.unpack('C*')
73
+ if id_bytes == block_id
74
+ logger.debug "Signature block id v#{@version} scheme (0x#{id_block.unpack1('H*')}) found"
75
+ value = @pairs.read(pair_size - UINT32_SIZE)
76
+ return StringIO.new(value)
77
+ end
78
+
79
+ @pairs.seek(next_pos)
80
+ count += 1
81
+ end
82
+
83
+ block_id_hex = block_id.reverse.pack('C*').unpack1('H*')
84
+ raise NotFoundError, "Not found block id 0x#{block_id_hex} in APK Signing Block."
85
+ end
86
+
87
+ def zip64?
88
+ zip_io.zip64_file?(start_buffer)
89
+ end
90
+
91
+ def pares_signatures_pairs
92
+ block = signature_block
93
+ block.rewind
94
+ # get pairs size
95
+ @total_size = block.size - (SIG_SIZE_OF_BLOCK_SIZE + SIG_MAGIC_BLOCK_SIZE)
96
+
97
+ # get pairs block
98
+ @pairs = StringIO.new(block.read(@total_size))
99
+
100
+ # get magic value
101
+ block.seek(block.pos + SIG_SIZE_OF_BLOCK_SIZE)
102
+ @magic = block.read(SIG_MAGIC_BLOCK_SIZE)
103
+ end
104
+
105
+ def signature_block
106
+ @signature_block ||= lambda {
107
+ logger.debug "cdir_offset: #{cdir_offset}"
108
+
109
+ file_io.seek(cdir_offset - (Info::SIG_MAGIC_BLOCK_SIZE + Info::SIG_SIZE_OF_BLOCK_SIZE))
110
+ footer_block = file_io.read(Info::SIG_SIZE_OF_BLOCK_SIZE)
111
+ if footer_block.size < Info::SIG_SIZE_OF_BLOCK_SIZE
112
+ raise NotFoundError, "APK Signing Block size out of range: #{footer_block.size}"
113
+ end
114
+
115
+ footer = footer_block.unpack1('Q')
116
+ total_size = footer
117
+ offset = cdir_offset - total_size - Info::SIG_SIZE_OF_BLOCK_SIZE
118
+ raise NotFoundError, "APK Signing Block offset out of range: #{offset}" if offset.negative?
119
+
120
+ file_io.seek(offset)
121
+ header = file_io.read(Info::SIG_SIZE_OF_BLOCK_SIZE).unpack1('Q')
122
+
123
+ if header != footer
124
+ raise NotFoundError,
125
+ "APK Signing Block header and footer mismatch: #{header} != #{footer}"
126
+ end
127
+
128
+ io = file_io.read(total_size)
129
+ StringIO.new(io)
130
+ }.call
131
+ end
132
+
133
+ def cdir_offset
134
+ @cdir_offset ||= lambda {
135
+ eocd_buffer = zip_io.get_e_o_c_d(start_buffer)
136
+ eocd_buffer[12..16].unpack1('V')
137
+ }.call
138
+ end
139
+
140
+ def start_buffer
141
+ @start_buffer ||= zip_io.start_buf(file_io)
142
+ end
143
+
144
+ def zip_io
145
+ @zip_io ||= @parser.zip
146
+ end
147
+
148
+ def file_io
149
+ @file_io ||= File.open(@parser.file, 'rb')
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppInfo::Android::Signature
4
+ # Android v1 Signature
5
+ class V1 < Base
6
+ DESCRIPTION = 'JAR signing'
7
+
8
+ PKCS7_HEADER = [0x30, 0x82].freeze
9
+
10
+ attr_reader :certificates, :signatures
11
+
12
+ def version
13
+ Version::V1
14
+ end
15
+
16
+ def description
17
+ DESCRIPTION
18
+ end
19
+
20
+ def verify
21
+ @signatures = fetch_signatures
22
+ @certificates = fetch_certificates
23
+
24
+ raise NotFoundError, 'Not found certificates' if @certificates.empty?
25
+ end
26
+
27
+ private
28
+
29
+ def fetch_signatures
30
+ case @parser
31
+ when AppInfo::APK
32
+ signatures_from(@parser.apk)
33
+ when AppInfo::AAB
34
+ signatures_from(@parser)
35
+ end
36
+ end
37
+
38
+ def fetch_certificates
39
+ @signatures.each_with_object([]) do |(_, sign), obj|
40
+ next if sign.certificates.empty?
41
+
42
+ obj << AppInfo::Certificate.new(sign.certificates[0])
43
+ end
44
+ end
45
+
46
+ def signatures_from(parser)
47
+ signs = {}
48
+ parser.each_file do |path, data|
49
+ # find META-INF/xxx.{RSA|DSA|EC}
50
+ next unless path =~ %r{^META-INF/} && data.unpack('CC') == PKCS7_HEADER
51
+
52
+ signs[path] = OpenSSL::PKCS7.new(data)
53
+ end
54
+ signs
55
+ end
56
+ end
57
+
58
+ register(Version::V1, V1)
59
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppInfo::Android::Signature
4
+ # Android v2 Signature
5
+ #
6
+ # FULL FORMAT:
7
+ # OFFSET DATA TYPE DESCRIPTION
8
+ # * @+0 bytes uint32: signer size in bytes
9
+ # * @+4 bytes payload signer block
10
+ # * @+0 bytes unit32: signed data size in bytes
11
+ # * @+4 bytes payload signed data block
12
+ # * @+0 bytes unit32: digests with size in bytes
13
+ # * @+0 bytes unit32: digests with size in bytes
14
+ # * @+X bytes unit32: signatures with size in bytes
15
+ # * @+X+4 bytes payload signed data block
16
+ # * @+Y bytes unit32: public key with size in bytes
17
+ # * @+Y+4 bytes payload signed data block
18
+ class V2 < Base
19
+ include AppInfo::Helper::IOBlock
20
+ include AppInfo::Helper::Signatures
21
+ include AppInfo::Helper::Algorithm
22
+
23
+ # V2 Signature ID 0x7109871a
24
+ BLOCK_ID = [0x1a, 0x87, 0x09, 0x71].freeze
25
+
26
+ attr_reader :certificates, :digests
27
+
28
+ def version
29
+ Version::V2
30
+ end
31
+
32
+ # Verify
33
+ # @todo verified signatures
34
+ def verify
35
+ signers_block = singers_block(BLOCK_ID)
36
+ @certificates, @digests = verified_certs(signers_block, verify: true)
37
+ # @verified = true
38
+ end
39
+
40
+ private
41
+
42
+ def verified_certs(signers_block, verify:)
43
+ unless (signers = length_prefix_block(signers_block))
44
+ raise SecurityError, 'Not found signers'
45
+ end
46
+
47
+ certificates = []
48
+ content_digests = {}
49
+ loop_length_prefix_io(signers, name: 'Singer', logger: logger) do |signer|
50
+ signer_certs, signer_digests = extract_signer_data(signer, verify: verify)
51
+ certificates.concat(signer_certs)
52
+ content_digests.merge!(signer_digests)
53
+ end
54
+ raise SecurityError, 'No signers found' if certificates.empty?
55
+
56
+ [certificates, content_digests]
57
+ end
58
+
59
+ def extract_signer_data(signer, verify:)
60
+ # raw data
61
+ signed_data = length_prefix_block(signer)
62
+ signatures = length_prefix_block(signer)
63
+ public_key = length_prefix_block(signer, raw: true)
64
+
65
+ # FIXME: extract code below and re-organizate
66
+
67
+ algorithems = signature_algorithms(signatures)
68
+ raise SecurityError, 'No signatures found' if verify && algorithems.empty?
69
+
70
+ # find best algorithem to verify signed data with public key and signature
71
+ unless best_algorithem = best_algorithem(algorithems)
72
+ raise SecurityError, 'No supported signatures found'
73
+ end
74
+
75
+ algorithems_digest = best_algorithem[:digest]
76
+ signature = best_algorithem[:signature]
77
+
78
+ pkey = OpenSSL::PKey.read(public_key)
79
+ digest = OpenSSL::Digest.new(algorithems_digest)
80
+ verified = pkey.verify(digest, signature, signed_data.string)
81
+ raise SecurityError, "#{algorithems_digest} signature did not verify" unless verified
82
+
83
+ # verify algorithm ID full equal (and sort) between digests and signature
84
+ digests = length_prefix_block(signed_data)
85
+ content_digests = signed_data_digests(digests)
86
+ content_digest = content_digests[algorithems_digest]&.fetch(:content)
87
+
88
+ unless content_digest
89
+ raise SecurityError,
90
+ 'Signature algorithms don\'t match between digests and signatures records'
91
+ end
92
+
93
+ previous_digest = content_digests.fetch(algorithems_digest)
94
+ content_digests[algorithems_digest] = content_digest
95
+ if previous_digest && previous_digest[:content] != content_digest
96
+ raise SecurityError,
97
+ 'Signature algorithms don\'t match between digests and signatures records'
98
+ end
99
+
100
+ certificates = length_prefix_block(signed_data)
101
+ certs = signed_data_certs(certificates)
102
+ raise SecurityError, 'No certificates listed' if certs.empty?
103
+
104
+ main_cert = certs[0]
105
+ if main_cert.public_key.to_der != pkey.to_der
106
+ raise SecurityError, 'Public key mismatch between certificate and signature record'
107
+ end
108
+
109
+ additional_attrs = length_prefix_block(signed_data)
110
+ verify_additional_attrs(additional_attrs, certs)
111
+
112
+ [certs, content_digests]
113
+ end
114
+ end
115
+
116
+ register(Version::V2, V2)
117
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppInfo::Android::Signature
4
+ # Android v3 Signature
5
+ #
6
+ # FULL FORMAT:
7
+ # OFFSET DATA TYPE DESCRIPTION
8
+ # * @+0 bytes uint32: signer size in bytes
9
+ # * @+4 bytes payload signer block
10
+ # * @+0 bytes unit32: signed data size in bytes
11
+ # * @+4 bytes payload signed data block
12
+ # * @+0 bytes unit32: digests with size in bytes
13
+ # * @+0 bytes unit32: digests with size in bytes
14
+ # * @+W bytes unit32: minSDK
15
+ # * @+X+4 bytes unit32: maxSDK
16
+ # * @+Y+4 bytes unit32: signatures with size in bytes
17
+ # * @+Y+4 bytes payload signed data block
18
+ # * @+Z bytes unit32: public key with size in bytes
19
+ # * @+Z+4 bytes payload signed data block
20
+ class V3 < Base
21
+ include AppInfo::Helper::IOBlock
22
+ include AppInfo::Helper::Signatures
23
+ include AppInfo::Helper::Algorithm
24
+
25
+ # V3 Signature ID 0xf05368c0
26
+ V3_BLOCK_ID = [0xc0, 0x68, 0x53, 0xf0].freeze
27
+
28
+ # V3.1 Signature ID 0x1b93ad61
29
+ V3_1_BLOCK_ID = [0x61, 0xad, 0x93, 0x1b].freeze
30
+
31
+ attr_reader :certificates, :digests
32
+
33
+ def version
34
+ Version::V3
35
+ end
36
+
37
+ def verify
38
+ begin
39
+ signers_block = singers_block(V3_1_BLOCK_ID)
40
+ rescue NotFoundError
41
+ signers_block = singers_block(V3_BLOCK_ID)
42
+ end
43
+
44
+ @certificates, @digests = verified_certs(signers_block)
45
+ end
46
+
47
+ private
48
+
49
+ def verified_certs(signers_block)
50
+ unless (signers = length_prefix_block(signers_block))
51
+ raise SecurityError, 'Not found signers'
52
+ end
53
+
54
+ certificates = []
55
+ content_digests = {}
56
+ loop_length_prefix_io(signers, name: 'Singer', logger: logger) do |signer|
57
+ signer_certs, signer_digests = extract_signer_data(signer)
58
+ certificates.concat(signer_certs)
59
+ content_digests.merge!(signer_digests)
60
+ end
61
+ raise SecurityError, 'No signers found' if certificates.empty?
62
+
63
+ [certificates, content_digests]
64
+ end
65
+
66
+ def extract_signer_data(signer)
67
+ # raw data
68
+ signed_data = length_prefix_block(signer)
69
+
70
+ # TODO: verify min_sdk and max_sdk
71
+ min_sdk = signer.read(UINT32_SIZE)
72
+ max_sdk = signer.read(UINT32_SIZE)
73
+
74
+ signatures = length_prefix_block(signer)
75
+ public_key = length_prefix_block(signer, raw: true)
76
+
77
+ algorithems = signature_algorithms(signatures)
78
+ raise SecurityError, 'No signatures found' if algorithems.empty?
79
+
80
+ # find best algorithem to verify signed data with public key and signature
81
+ unless best_algorithem = best_algorithem(algorithems)
82
+ raise SecurityError, 'No supported signatures found'
83
+ end
84
+
85
+ algorithems_digest = best_algorithem[:digest]
86
+ signature = best_algorithem[:signature]
87
+
88
+ pkey = OpenSSL::PKey.read(public_key)
89
+ digest = OpenSSL::Digest.new(algorithems_digest)
90
+ verified = pkey.verify(digest, signature, signed_data.string)
91
+ raise SecurityError, "#{algorithems_digest} signature did not verify" unless verified
92
+
93
+ # verify algorithm ID full equal (and sort) between digests and signature
94
+ digests = length_prefix_block(signed_data)
95
+ content_digests = signed_data_digests(digests)
96
+ content_digest = content_digests[algorithems_digest]&.fetch(:content)
97
+
98
+ unless content_digest
99
+ raise SecurityError,
100
+ 'Signature algorithms don\'t match between digests and signatures records'
101
+ end
102
+
103
+ previous_digest = content_digests.fetch(algorithems_digest)
104
+ content_digests[algorithems_digest] = content_digest
105
+ if previous_digest && previous_digest[:content] != content_digest
106
+ raise SecurityError,
107
+ 'Signature algorithms don\'t match between digests and signatures records'
108
+ end
109
+
110
+ certificates = length_prefix_block(signed_data)
111
+ certs = signed_data_certs(certificates)
112
+ raise SecurityError, 'No certificates listed' if certs.empty?
113
+
114
+ main_cert = certs[0]
115
+ if main_cert.public_key.to_der != pkey.to_der
116
+ raise SecurityError, 'Public key mismatch between certificate and signature record'
117
+ end
118
+
119
+ additional_attrs = length_prefix_block(signed_data)
120
+ verify_additional_attrs(additional_attrs, certs)
121
+
122
+ [certs, content_digests]
123
+ end
124
+ end
125
+
126
+ register(Version::V3, V3)
127
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppInfo::Android::Signature
4
+ # Android v4 Signature
5
+ #
6
+ # TODO: ApkSignatureSchemeV4Verifier.java
7
+ class V4 < Base
8
+ def version
9
+ Version::V4
10
+ end
11
+ end
12
+
13
+ # register(Version::V4, V4)
14
+ end
data/lib/app_info/apk.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 APK
8
+ # Parse APK file parser, wrapper for {https://github.com/icyleaf/android_parser android_parser}.
9
+ class APK < File
10
10
  include Helper::HumanFileSize
11
11
  extend Forwardable
12
12
 
@@ -21,18 +21,26 @@ module AppInfo
21
21
  AUTOMOTIVE = 'Automotive'
22
22
  end
23
23
 
24
- def initialize(file)
25
- @file = file
26
- end
27
-
24
+ # return file size
25
+ # @example Read file size in integer
26
+ # aab.size # => 3618865
27
+ #
28
+ # @example Read file size in human readabale
29
+ # aab.size(human_size: true) # => '3.45 MB'
30
+ #
31
+ # @param [Boolean] human_size Convert integer value to human readable.
32
+ # @return [Integer, String]
28
33
  def size(human_size: false)
29
34
  file_to_human_size(@file, human_size: human_size)
30
35
  end
31
36
 
32
- def os
37
+ def file_type
38
+ Format::APK
39
+ end
40
+
41
+ def platform
33
42
  Platform::ANDROID
34
43
  end
35
- alias file_type os
36
44
 
37
45
  def_delegators :apk, :manifest, :resource, :dex
38
46
 
@@ -86,28 +94,25 @@ module AppInfo
86
94
  end
87
95
  alias min_os_version min_sdk_version
88
96
 
89
- def sign_version
90
- return 'v1' unless signs.empty?
91
-
92
- # when ?
93
- # https://source.android.com/security/apksigning/v2?hl=zh-cn
94
- # 'v2'
95
- # when ?
96
- # https://source.android.com/security/apksigning/v3?hl=zh-cn
97
- # 'v3'
98
- 'unknown'
97
+ # Return multi version certifiates of signatures
98
+ # @return [Array<Hash>]
99
+ # @see AppInfo::Android::Signature.verify
100
+ def signatures
101
+ @signatures ||= Android::Signature.verify(self)
99
102
  end
100
103
 
104
+ # Legacy v1 scheme signatures, it will remove soon.
105
+ # @deprecated Use {#signatures}
106
+ # @return [Array<OpenSSL::PKCS7, nil>]
101
107
  def signs
102
- apk.signs.each_with_object([]) do |(path, sign), obj|
103
- obj << Sign.new(path, sign)
104
- end
108
+ @signs ||= v1sign&.signatures || []
105
109
  end
106
110
 
111
+ # Legacy v1 scheme certificates, it will remove soon.
112
+ # @deprecated Use {#signatures}
113
+ # @return [Array<OpenSSL::PKCS7, nil>]
107
114
  def certificates
108
- apk.certificates.each_with_object([]) do |(path, certificate), obj|
109
- obj << Certificate.new(path, certificate)
110
- end
115
+ @certificates ||= v1sign&.certificates || []
111
116
  end
112
117
 
113
118
  def activities
@@ -118,13 +123,17 @@ module AppInfo
118
123
  @apk ||= ::Android::Apk.new(@file)
119
124
  end
120
125
 
126
+ def zip
127
+ @zip ||= apk.instance_variable_get(:@zip)
128
+ end
129
+
121
130
  def icons
122
131
  @icons ||= apk.icon.each_with_object([]) do |(path, data), obj|
123
- icon_name = File.basename(path)
124
- icon_path = File.join(contents, File.dirname(path))
125
- icon_file = File.join(icon_path, icon_name)
132
+ icon_name = ::File.basename(path)
133
+ icon_path = ::File.join(contents, ::File.dirname(path))
134
+ icon_file = ::File.join(icon_path, icon_name)
126
135
  FileUtils.mkdir_p icon_path
127
- File.write(icon_file, data, encoding: Encoding::BINARY)
136
+ ::File.write(icon_file, data, encoding: Encoding::BINARY)
128
137
 
129
138
  obj << {
130
139
  name: icon_name,
@@ -147,27 +156,15 @@ module AppInfo
147
156
  end
148
157
 
149
158
  def contents
150
- @contents ||= File.join(Dir.mktmpdir, "AppInfo-android-#{SecureRandom.hex}")
159
+ @contents ||= ::File.join(Dir.mktmpdir, "AppInfo-android-#{SecureRandom.hex}")
151
160
  end
152
161
 
153
- # Android Certificate
154
- class Certificate
155
- attr_reader :path, :certificate
162
+ private
156
163
 
157
- def initialize(path, certificate)
158
- @path = path
159
- @certificate = certificate
160
- end
161
- end
162
-
163
- # Android Sign
164
- class Sign
165
- attr_reader :path, :sign
166
-
167
- def initialize(path, sign)
168
- @path = path
169
- @sign = sign
170
- end
164
+ def v1sign
165
+ @v1sign ||= Android::Signature::V1.verify(self)
166
+ rescue Android::Signature::NotFoundError
167
+ nil
171
168
  end
172
169
  end
173
170
  end