app-info 2.8.4 → 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 +30 -2
  6. data/Gemfile +7 -1
  7. data/README.md +74 -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 +8 -0
  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 +16 -9
  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 +91 -42
  49. metadata +46 -35
@@ -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'
@@ -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