app-info 3.0.0.beta1 → 3.0.0.beta3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|