app-info 3.0.0.beta1 → 3.0.0.beta3
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +29 -3
- data/README.md +4 -4
- data/lib/app_info/aab.rb +57 -114
- data/lib/app_info/android/signature.rb +5 -5
- data/lib/app_info/android/signatures/base.rb +41 -37
- data/lib/app_info/android/signatures/info.rb +135 -129
- data/lib/app_info/android/signatures/v1.rb +56 -52
- data/lib/app_info/android/signatures/v2.rb +114 -110
- data/lib/app_info/android/signatures/v3.rb +124 -120
- data/lib/app_info/android/signatures/v4.rb +13 -9
- data/lib/app_info/android.rb +181 -0
- data/lib/app_info/apk.rb +68 -100
- data/lib/app_info/apple.rb +192 -0
- data/lib/app_info/certificate.rb +14 -19
- data/lib/app_info/const.rb +48 -13
- data/lib/app_info/dsym.rb +6 -3
- data/lib/app_info/error.rb +1 -7
- data/lib/app_info/file.rb +30 -4
- data/lib/app_info/helper/file_size.rb +1 -1
- data/lib/app_info/info_plist.rb +54 -25
- data/lib/app_info/ipa.rb +39 -118
- data/lib/app_info/macos.rb +38 -94
- data/lib/app_info/mobile_provision.rb +49 -20
- data/lib/app_info/pe.rb +103 -7
- data/lib/app_info/png_uncrush.rb +19 -0
- data/lib/app_info/proguard.rb +21 -2
- data/lib/app_info/protobuf/manifest.rb +5 -1
- data/lib/app_info/version.rb +1 -1
- data/lib/app_info.rb +4 -3
- metadata +4 -2
@@ -3,150 +3,156 @@
|
|
3
3
|
require 'stringio'
|
4
4
|
require 'openssl'
|
5
5
|
|
6
|
-
module AppInfo
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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}"
|
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
|
61
40
|
end
|
62
41
|
|
63
|
-
|
64
|
-
|
65
|
-
|
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."
|
66
87
|
end
|
67
88
|
|
68
|
-
|
69
|
-
|
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)
|
89
|
+
def zip64?
|
90
|
+
zip_io.zip64_file?(start_buffer)
|
77
91
|
end
|
78
92
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
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)
|
104
98
|
|
105
|
-
|
106
|
-
|
107
|
-
logger.debug "cdir_offset: #{cdir_offset}"
|
99
|
+
# get pairs block
|
100
|
+
@pairs = StringIO.new(block.read(@total_size))
|
108
101
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
raise NotFoundError, "APK Signing Block size out of range: #{footer_block.size}"
|
102
|
+
# get magic value
|
103
|
+
block.seek(block.pos + SIG_SIZE_OF_BLOCK_SIZE)
|
104
|
+
@magic = block.read(SIG_MAGIC_BLOCK_SIZE)
|
113
105
|
end
|
114
106
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
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
|
126
135
|
end
|
127
136
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
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
|
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
|
139
143
|
|
140
|
-
|
141
|
-
|
142
|
-
|
144
|
+
def start_buffer
|
145
|
+
@start_buffer ||= zip_io.start_buf(file_io)
|
146
|
+
end
|
143
147
|
|
144
|
-
|
145
|
-
|
146
|
-
|
148
|
+
def zip_io
|
149
|
+
@zip_io ||= @parser.zip
|
150
|
+
end
|
147
151
|
|
148
|
-
|
149
|
-
|
152
|
+
def file_io
|
153
|
+
@file_io ||= ::File.open(@parser.file, 'rb')
|
154
|
+
end
|
155
|
+
end
|
150
156
|
end
|
151
157
|
end
|
152
158
|
end
|
@@ -1,59 +1,63 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module AppInfo
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
35
58
|
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def fetch_certificates
|
39
|
-
@signatures.each_with_object([]) do |(_, sign), obj|
|
40
|
-
next if sign.certificates.empty?
|
41
59
|
|
42
|
-
|
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
|
60
|
+
register(Version::V1, V1)
|
55
61
|
end
|
56
62
|
end
|
57
|
-
|
58
|
-
register(Version::V1, V1)
|
59
63
|
end
|
@@ -1,117 +1,121 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module AppInfo
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
73
116
|
end
|
74
117
|
|
75
|
-
|
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]
|
118
|
+
register(Version::V2, V2)
|
113
119
|
end
|
114
120
|
end
|
115
|
-
|
116
|
-
register(Version::V2, V2)
|
117
121
|
end
|