app-info 2.8.5 → 3.0.0.beta2

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 (51) 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 +33 -11
  5. data/CHANGELOG.md +34 -1
  6. data/Gemfile +7 -1
  7. data/README.md +68 -13
  8. data/Rakefile +11 -0
  9. data/app_info.gemspec +12 -3
  10. data/lib/app_info/aab.rb +48 -108
  11. data/lib/app_info/android/signature.rb +114 -0
  12. data/lib/app_info/android/signatures/base.rb +53 -0
  13. data/lib/app_info/android/signatures/info.rb +158 -0
  14. data/lib/app_info/android/signatures/v1.rb +63 -0
  15. data/lib/app_info/android/signatures/v2.rb +121 -0
  16. data/lib/app_info/android/signatures/v3.rb +131 -0
  17. data/lib/app_info/android/signatures/v4.rb +18 -0
  18. data/lib/app_info/android.rb +162 -0
  19. data/lib/app_info/apk.rb +54 -111
  20. data/lib/app_info/apple.rb +192 -0
  21. data/lib/app_info/certificate.rb +175 -0
  22. data/lib/app_info/const.rb +75 -0
  23. data/lib/app_info/core_ext/object/try.rb +3 -1
  24. data/lib/app_info/core_ext/string/inflector.rb +2 -0
  25. data/lib/app_info/dsym/debug_info.rb +72 -0
  26. data/lib/app_info/dsym/macho.rb +55 -0
  27. data/lib/app_info/dsym.rb +31 -135
  28. data/lib/app_info/error.rb +2 -2
  29. data/lib/app_info/file.rb +49 -0
  30. data/lib/app_info/helper/archive.rb +37 -0
  31. data/lib/app_info/helper/file_size.rb +25 -0
  32. data/lib/app_info/helper/generate_class.rb +29 -0
  33. data/lib/app_info/helper/protobuf.rb +12 -0
  34. data/lib/app_info/helper/signatures.rb +229 -0
  35. data/lib/app_info/helper.rb +5 -126
  36. data/lib/app_info/info_plist.rb +66 -29
  37. data/lib/app_info/ipa/framework.rb +4 -4
  38. data/lib/app_info/ipa.rb +61 -135
  39. data/lib/app_info/macos.rb +54 -102
  40. data/lib/app_info/mobile_provision.rb +67 -49
  41. data/lib/app_info/pe.rb +260 -0
  42. data/lib/app_info/png_uncrush.rb +24 -4
  43. data/lib/app_info/proguard.rb +29 -16
  44. data/lib/app_info/protobuf/manifest.rb +6 -3
  45. data/lib/app_info/protobuf/models/Configuration_pb.rb +1 -0
  46. data/lib/app_info/protobuf/models/README.md +7 -0
  47. data/lib/app_info/protobuf/models/Resources_pb.rb +2 -0
  48. data/lib/app_info/protobuf/resources.rb +5 -5
  49. data/lib/app_info/version.rb +1 -1
  50. data/lib/app_info.rb +90 -46
  51. metadata +48 -35
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppInfo
4
+ class Android < File
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'
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'app_info/android/signatures/info'
4
+
5
+ module AppInfo
6
+ class Android < File
7
+ module Signature
8
+ class Base
9
+ def self.verify(parser)
10
+ instance = new(parser)
11
+ instance.verify
12
+ instance
13
+ end
14
+
15
+ DESCRIPTION = 'APK Signature Scheme'
16
+
17
+ attr_reader :verified
18
+
19
+ def initialize(parser)
20
+ @parser = parser
21
+ @verified = false
22
+ end
23
+
24
+ # @abstract Subclass and override {#verify} to implement
25
+ def verify
26
+ raise NotImplementedError, ".#{__method__} method implantation required in #{self.class}"
27
+ end
28
+
29
+ # @abstract Subclass and override {#certificates} to implement
30
+ def certificates
31
+ raise NotImplementedError, ".#{__method__} method implantation required in #{self.class}"
32
+ end
33
+
34
+ def scheme
35
+ "v#{version}"
36
+ end
37
+
38
+ def description
39
+ "#{DESCRIPTION} #{scheme}"
40
+ end
41
+
42
+ def logger
43
+ @parser.logger
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ require 'app_info/android/signatures/v1'
51
+ require 'app_info/android/signatures/v2'
52
+ require 'app_info/android/signatures/v3'
53
+ require 'app_info/android/signatures/v4'
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stringio'
4
+ require 'openssl'
5
+
6
+ module AppInfo
7
+ class Android < File
8
+ module Signature
9
+ # APK signature scheme signurate info
10
+ #
11
+ # FORMAT:
12
+ # OFFSET DATA TYPE DESCRIPTION
13
+ # * @+0 bytes uint64: size in bytes (excluding this field)
14
+ # * @+8 bytes payload
15
+ # * @-24 bytes uint64: size in bytes (same as the one above)
16
+ # * @-16 bytes uint128: magic value
17
+ class Info
18
+ include AppInfo::Helper::IOBlock
19
+
20
+ # Signature block information
21
+ SIG_SIZE_OF_BLOCK_SIZE = 8
22
+ SIG_MAGIC_BLOCK_SIZE = 16
23
+ SIG_BLOCK_MIN_SIZE = 32
24
+
25
+ # Magic value: APK Sig Block 42
26
+ SIG_MAGIC = [
27
+ 0x41, 0x50, 0x4b, 0x20, 0x53, 0x69,
28
+ 0x67, 0x20, 0x42, 0x6c, 0x6f, 0x63,
29
+ 0x6b, 0x20, 0x34, 0x32
30
+ ].freeze
31
+
32
+ attr_reader :total_size, :pairs, :magic, :logger
33
+
34
+ def initialize(version, parser, logger)
35
+ @version = version
36
+ @parser = parser
37
+ @logger = logger
38
+
39
+ pares_signatures_pairs
40
+ end
41
+
42
+ # Find singers
43
+ #
44
+ # FORMAT:
45
+ # OFFSET DATA TYPE DESCRIPTION
46
+ # * @+0 bytes uint64: size in bytes
47
+ # * @+8 bytes payload block
48
+ # * @+0 bytes uint32: id
49
+ # * @+4 bytes payload: value
50
+ def signers(block_id)
51
+ count = 0
52
+ until @pairs.eof?
53
+ left_bytes = left_bytes_check(
54
+ @pairs, UINT64_SIZE, NotFoundError,
55
+ "Insufficient data to read size of APK Signing Block ##{count}"
56
+ )
57
+
58
+ pair_buf = @pairs.read(UINT64_SIZE)
59
+ pair_size = pair_buf.unpack1('Q')
60
+ if pair_size < UINT32_SIZE || pair_size > UINT32_MAX_VALUE
61
+ raise NotFoundError,
62
+ "APK Signing Block ##{count} size out of range: #{pair_size} > #{UINT32_MAX_VALUE}"
63
+ end
64
+
65
+ if pair_size > left_bytes
66
+ raise NotFoundError,
67
+ "APK Signing Block ##{count} size out of range: #{pair_size} > #{left_bytes}"
68
+ end
69
+
70
+ # fetch next signer block position
71
+ next_pos = @pairs.pos + pair_size.to_i
72
+
73
+ id_block = @pairs.read(UINT32_SIZE)
74
+ id_bytes = id_block.unpack('C*')
75
+ if id_bytes == block_id
76
+ logger.debug "Signature block id v#{@version} scheme (0x#{id_block.unpack1('H*')}) found"
77
+ value = @pairs.read(pair_size - UINT32_SIZE)
78
+ return StringIO.new(value)
79
+ end
80
+
81
+ @pairs.seek(next_pos)
82
+ count += 1
83
+ end
84
+
85
+ block_id_hex = block_id.reverse.pack('C*').unpack1('H*')
86
+ raise NotFoundError, "Not found block id 0x#{block_id_hex} in APK Signing Block."
87
+ end
88
+
89
+ def zip64?
90
+ zip_io.zip64_file?(start_buffer)
91
+ end
92
+
93
+ def pares_signatures_pairs
94
+ block = signature_block
95
+ block.rewind
96
+ # get pairs size
97
+ @total_size = block.size - (SIG_SIZE_OF_BLOCK_SIZE + SIG_MAGIC_BLOCK_SIZE)
98
+
99
+ # get pairs block
100
+ @pairs = StringIO.new(block.read(@total_size))
101
+
102
+ # get magic value
103
+ block.seek(block.pos + SIG_SIZE_OF_BLOCK_SIZE)
104
+ @magic = block.read(SIG_MAGIC_BLOCK_SIZE)
105
+ end
106
+
107
+ def signature_block
108
+ @signature_block ||= lambda {
109
+ logger.debug "cdir_offset: #{cdir_offset}"
110
+
111
+ file_io.seek(cdir_offset - (Info::SIG_MAGIC_BLOCK_SIZE + Info::SIG_SIZE_OF_BLOCK_SIZE))
112
+ footer_block = file_io.read(Info::SIG_SIZE_OF_BLOCK_SIZE)
113
+ if footer_block.size < Info::SIG_SIZE_OF_BLOCK_SIZE
114
+ raise NotFoundError, "APK Signing Block size out of range: #{footer_block.size}"
115
+ end
116
+
117
+ footer = footer_block.unpack1('Q')
118
+ total_size = footer
119
+ offset = cdir_offset - total_size - Info::SIG_SIZE_OF_BLOCK_SIZE
120
+ if offset.negative?
121
+ raise NotFoundError, "APK Signing Block offset out of range: #{offset}"
122
+ end
123
+
124
+ file_io.seek(offset)
125
+ header = file_io.read(Info::SIG_SIZE_OF_BLOCK_SIZE).unpack1('Q')
126
+
127
+ if header != footer
128
+ raise NotFoundError,
129
+ "APK Signing Block header and footer mismatch: #{header} != #{footer}"
130
+ end
131
+
132
+ io = file_io.read(total_size)
133
+ StringIO.new(io)
134
+ }.call
135
+ end
136
+
137
+ def cdir_offset
138
+ @cdir_offset ||= lambda {
139
+ eocd_buffer = zip_io.get_e_o_c_d(start_buffer)
140
+ eocd_buffer[12..16].unpack1('V')
141
+ }.call
142
+ end
143
+
144
+ def start_buffer
145
+ @start_buffer ||= zip_io.start_buf(file_io)
146
+ end
147
+
148
+ def zip_io
149
+ @zip_io ||= @parser.zip
150
+ end
151
+
152
+ def file_io
153
+ @file_io ||= ::File.open(@parser.file, 'rb')
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppInfo
4
+ class Android < File
5
+ module Signature
6
+ # Android v1 Signature
7
+ class V1 < Base
8
+ DESCRIPTION = 'JAR signing'
9
+
10
+ PKCS7_HEADER = [0x30, 0x82].freeze
11
+
12
+ attr_reader :certificates, :signatures
13
+
14
+ def version
15
+ Version::V1
16
+ end
17
+
18
+ def description
19
+ DESCRIPTION
20
+ end
21
+
22
+ def verify
23
+ @signatures = fetch_signatures
24
+ @certificates = fetch_certificates
25
+
26
+ raise NotFoundError, 'Not found certificates' if @certificates.empty?
27
+ end
28
+
29
+ private
30
+
31
+ def fetch_signatures
32
+ case @parser
33
+ when AppInfo::APK
34
+ signatures_from(@parser.apk)
35
+ when AppInfo::AAB
36
+ signatures_from(@parser)
37
+ end
38
+ end
39
+
40
+ def fetch_certificates
41
+ @signatures.each_with_object([]) do |(_, sign), obj|
42
+ next if sign.certificates.empty?
43
+
44
+ obj << AppInfo::Certificate.new(sign.certificates[0])
45
+ end
46
+ end
47
+
48
+ def signatures_from(parser)
49
+ signs = {}
50
+ parser.each_file do |path, data|
51
+ # find META-INF/xxx.{RSA|DSA|EC}
52
+ next unless path =~ %r{^META-INF/} && data.unpack('CC') == PKCS7_HEADER
53
+
54
+ signs[path] = OpenSSL::PKCS7.new(data)
55
+ end
56
+ signs
57
+ end
58
+ end
59
+
60
+ register(Version::V1, V1)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppInfo
4
+ class Android < File
5
+ module Signature
6
+ # Android v2 Signature
7
+ #
8
+ # FULL FORMAT:
9
+ # OFFSET DATA TYPE DESCRIPTION
10
+ # * @+0 bytes uint32: signer size in bytes
11
+ # * @+4 bytes payload signer block
12
+ # * @+0 bytes unit32: signed data size in bytes
13
+ # * @+4 bytes payload signed data block
14
+ # * @+0 bytes unit32: digests with size in bytes
15
+ # * @+0 bytes unit32: digests with size in bytes
16
+ # * @+X bytes unit32: signatures with size in bytes
17
+ # * @+X+4 bytes payload signed data block
18
+ # * @+Y bytes unit32: public key with size in bytes
19
+ # * @+Y+4 bytes payload signed data block
20
+ class V2 < Base
21
+ include AppInfo::Helper::IOBlock
22
+ include AppInfo::Helper::Signatures
23
+ include AppInfo::Helper::Algorithm
24
+
25
+ # V2 Signature ID 0x7109871a
26
+ BLOCK_ID = [0x1a, 0x87, 0x09, 0x71].freeze
27
+
28
+ attr_reader :certificates, :digests
29
+
30
+ def version
31
+ Version::V2
32
+ end
33
+
34
+ # Verify
35
+ # @todo verified signatures
36
+ def verify
37
+ signers_block = singers_block(BLOCK_ID)
38
+ @certificates, @digests = verified_certs(signers_block, verify: true)
39
+ # @verified = true
40
+ end
41
+
42
+ private
43
+
44
+ def verified_certs(signers_block, verify:)
45
+ unless (signers = length_prefix_block(signers_block))
46
+ raise SecurityError, 'Not found signers'
47
+ end
48
+
49
+ certificates = []
50
+ content_digests = {}
51
+ loop_length_prefix_io(signers, name: 'Singer', logger: logger) do |signer|
52
+ signer_certs, signer_digests = extract_signer_data(signer, verify: verify)
53
+ certificates.concat(signer_certs)
54
+ content_digests.merge!(signer_digests)
55
+ end
56
+ raise SecurityError, 'No signers found' if certificates.empty?
57
+
58
+ [certificates, content_digests]
59
+ end
60
+
61
+ def extract_signer_data(signer, verify:)
62
+ # raw data
63
+ signed_data = length_prefix_block(signer)
64
+ signatures = length_prefix_block(signer)
65
+ public_key = length_prefix_block(signer, raw: true)
66
+
67
+ # FIXME: extract code below and re-organize
68
+
69
+ algorithems = signature_algorithms(signatures)
70
+ raise SecurityError, 'No signatures found' if verify && algorithems.empty?
71
+
72
+ # find best algorithem to verify signed data with public key and signature
73
+ unless best_algorithem = best_algorithem(algorithems)
74
+ raise SecurityError, 'No supported signatures found'
75
+ end
76
+
77
+ algorithems_digest = best_algorithem[:digest]
78
+ signature = best_algorithem[:signature]
79
+
80
+ pkey = OpenSSL::PKey.read(public_key)
81
+ digest = OpenSSL::Digest.new(algorithems_digest)
82
+ verified = pkey.verify(digest, signature, signed_data.string)
83
+ raise SecurityError, "#{algorithems_digest} signature did not verify" unless verified
84
+
85
+ # verify algorithm ID full equal (and sort) between digests and signature
86
+ digests = length_prefix_block(signed_data)
87
+ content_digests = signed_data_digests(digests)
88
+ content_digest = content_digests[algorithems_digest]&.fetch(:content)
89
+
90
+ unless content_digest
91
+ raise SecurityError,
92
+ 'Signature algorithms don\'t match between digests and signatures records'
93
+ end
94
+
95
+ previous_digest = content_digests.fetch(algorithems_digest)
96
+ content_digests[algorithems_digest] = content_digest
97
+ if previous_digest && previous_digest[:content] != content_digest
98
+ raise SecurityError,
99
+ 'Signature algorithms don\'t match between digests and signatures records'
100
+ end
101
+
102
+ certificates = length_prefix_block(signed_data)
103
+ certs = signed_data_certs(certificates)
104
+ raise SecurityError, 'No certificates listed' if certs.empty?
105
+
106
+ main_cert = certs[0]
107
+ if main_cert.public_key.to_der != pkey.to_der
108
+ raise SecurityError, 'Public key mismatch between certificate and signature record'
109
+ end
110
+
111
+ additional_attrs = length_prefix_block(signed_data)
112
+ verify_additional_attrs(additional_attrs, certs)
113
+
114
+ [certs, content_digests]
115
+ end
116
+ end
117
+
118
+ register(Version::V2, V2)
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppInfo
4
+ class Android < File
5
+ module Signature
6
+ # Android v3 Signature
7
+ #
8
+ # FULL FORMAT:
9
+ # OFFSET DATA TYPE DESCRIPTION
10
+ # * @+0 bytes uint32: signer size in bytes
11
+ # * @+4 bytes payload signer block
12
+ # * @+0 bytes unit32: signed data size in bytes
13
+ # * @+4 bytes payload signed data block
14
+ # * @+0 bytes unit32: digests with size in bytes
15
+ # * @+0 bytes unit32: digests with size in bytes
16
+ # * @+W bytes unit32: minSDK
17
+ # * @+X+4 bytes unit32: maxSDK
18
+ # * @+Y+4 bytes unit32: signatures with size in bytes
19
+ # * @+Y+4 bytes payload signed data block
20
+ # * @+Z bytes unit32: public key with size in bytes
21
+ # * @+Z+4 bytes payload signed data block
22
+ class V3 < Base
23
+ include AppInfo::Helper::IOBlock
24
+ include AppInfo::Helper::Signatures
25
+ include AppInfo::Helper::Algorithm
26
+
27
+ # V3 Signature ID 0xf05368c0
28
+ V3_BLOCK_ID = [0xc0, 0x68, 0x53, 0xf0].freeze
29
+
30
+ # V3.1 Signature ID 0x1b93ad61
31
+ V3_1_BLOCK_ID = [0x61, 0xad, 0x93, 0x1b].freeze
32
+
33
+ attr_reader :certificates, :digests
34
+
35
+ def version
36
+ Version::V3
37
+ end
38
+
39
+ def verify
40
+ begin
41
+ signers_block = singers_block(V3_1_BLOCK_ID)
42
+ rescue NotFoundError
43
+ signers_block = singers_block(V3_BLOCK_ID)
44
+ end
45
+
46
+ @certificates, @digests = verified_certs(signers_block)
47
+ end
48
+
49
+ private
50
+
51
+ def verified_certs(signers_block)
52
+ unless (signers = length_prefix_block(signers_block))
53
+ raise SecurityError, 'Not found signers'
54
+ end
55
+
56
+ certificates = []
57
+ content_digests = {}
58
+ loop_length_prefix_io(signers, name: 'Singer', logger: logger) do |signer|
59
+ signer_certs, signer_digests = extract_signer_data(signer)
60
+ certificates.concat(signer_certs)
61
+ content_digests.merge!(signer_digests)
62
+ end
63
+ raise SecurityError, 'No signers found' if certificates.empty?
64
+
65
+ [certificates, content_digests]
66
+ end
67
+
68
+ def extract_signer_data(signer)
69
+ # raw data
70
+ signed_data = length_prefix_block(signer)
71
+
72
+ # TODO: verify min_sdk and max_sdk
73
+ min_sdk = signer.read(UINT32_SIZE)
74
+ max_sdk = signer.read(UINT32_SIZE)
75
+
76
+ signatures = length_prefix_block(signer)
77
+ public_key = length_prefix_block(signer, raw: true)
78
+
79
+ algorithems = signature_algorithms(signatures)
80
+ raise SecurityError, 'No signatures found' if algorithems.empty?
81
+
82
+ # find best algorithem to verify signed data with public key and signature
83
+ unless best_algorithem = best_algorithem(algorithems)
84
+ raise SecurityError, 'No supported signatures found'
85
+ end
86
+
87
+ algorithems_digest = best_algorithem[:digest]
88
+ signature = best_algorithem[:signature]
89
+
90
+ pkey = OpenSSL::PKey.read(public_key)
91
+ digest = OpenSSL::Digest.new(algorithems_digest)
92
+ verified = pkey.verify(digest, signature, signed_data.string)
93
+ raise SecurityError, "#{algorithems_digest} signature did not verify" unless verified
94
+
95
+ # verify algorithm ID full equal (and sort) between digests and signature
96
+ digests = length_prefix_block(signed_data)
97
+ content_digests = signed_data_digests(digests)
98
+ content_digest = content_digests[algorithems_digest]&.fetch(:content)
99
+
100
+ unless content_digest
101
+ raise SecurityError,
102
+ 'Signature algorithms don\'t match between digests and signatures records'
103
+ end
104
+
105
+ previous_digest = content_digests.fetch(algorithems_digest)
106
+ content_digests[algorithems_digest] = content_digest
107
+ if previous_digest && previous_digest[:content] != content_digest
108
+ raise SecurityError,
109
+ 'Signature algorithms don\'t match between digests and signatures records'
110
+ end
111
+
112
+ certificates = length_prefix_block(signed_data)
113
+ certs = signed_data_certs(certificates)
114
+ raise SecurityError, 'No certificates listed' if certs.empty?
115
+
116
+ main_cert = certs[0]
117
+ if main_cert.public_key.to_der != pkey.to_der
118
+ raise SecurityError, 'Public key mismatch between certificate and signature record'
119
+ end
120
+
121
+ additional_attrs = length_prefix_block(signed_data)
122
+ verify_additional_attrs(additional_attrs, certs)
123
+
124
+ [certs, content_digests]
125
+ end
126
+ end
127
+
128
+ register(Version::V3, V3)
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppInfo
4
+ class Android < File
5
+ module Signature
6
+ # Android v4 Signature
7
+ #
8
+ # TODO: ApkSignatureSchemeV4Verifier.java
9
+ class V4 < Base
10
+ def version
11
+ Version::V4
12
+ end
13
+ end
14
+
15
+ # register(Version::V4, V4)
16
+ end
17
+ end
18
+ end