rubeepass 3.1.1 → 3.2.0
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/bin/rpass +26 -12
- data/lib/rubeepass.rb +73 -28
- data/lib/rubeepass/error.rb +1 -1
- data/lib/rubeepass/error/not_supported.rb +5 -0
- metadata +3 -3
- data/lib/rubeepass/error/not_aes.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6389189519412fd7b336eea64280403924c313955e78f3b6eaea377303a3552a
|
4
|
+
data.tar.gz: d4c77407fa1c2b92764f2dc314e8ac31c472b0c9c01a9c3158bca3a08ddecc8d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c90641b9ff5afcf3272464a4c2a45383d48c648869eb5d485ff593af5838aca8d137a77c2006a0a3cc2d446b89cee2d30308190154a913df341f3c4db87b1cdd
|
7
|
+
data.tar.gz: 380cb82f98469fe27e4a7ab4d21883835d9855b78f863a40d2a2fc4eec50ac0119043200545fa94b9148d72be34692d5010330bb09690154c9f5ae14f0e148e3
|
data/bin/rpass
CHANGED
@@ -77,10 +77,10 @@ def parse(args)
|
|
77
77
|
|
78
78
|
opts.banner = "Usage: #{File.basename($0)} [OPTIONS] [kdbx]"
|
79
79
|
|
80
|
-
opts.on("")
|
80
|
+
opts.on("", "DESCRIPTION")
|
81
81
|
|
82
|
-
info.scan(/\S.{0,
|
83
|
-
opts.on("#{line}")
|
82
|
+
info.scan(/\S.{0,66}\S(?=\s|$)|\S+/).each do |line|
|
83
|
+
opts.on(" #{line}")
|
84
84
|
end
|
85
85
|
|
86
86
|
opts.on("", "OPTIONS")
|
@@ -148,6 +148,13 @@ def parse(args)
|
|
148
148
|
options["verbose"] = true
|
149
149
|
end
|
150
150
|
|
151
|
+
opts.on("-V", "--version", "Show version") do
|
152
|
+
__FILE__.match(/rubeepass-(\d+\.\d+\.\d+)/) do |m|
|
153
|
+
puts m[1]
|
154
|
+
end
|
155
|
+
exit RubeePassExit::GOOD
|
156
|
+
end
|
157
|
+
|
151
158
|
opts.on(
|
152
159
|
"",
|
153
160
|
"FORMATS",
|
@@ -295,24 +302,31 @@ rescue SystemExit
|
|
295
302
|
# Quit from djinni
|
296
303
|
# Exit gracefully
|
297
304
|
rescue Interrupt
|
298
|
-
# ^C
|
299
|
-
# Exit gracefully
|
305
|
+
# Exit gracefully on ^C
|
300
306
|
rescue Errno::EPIPE
|
301
307
|
# Do nothing. This can happen if piping to another program such as
|
302
308
|
# less. Usually if less is closed before we're done with STDOUT.
|
303
309
|
rescue RubeePass::Error => e
|
304
|
-
puts e.message
|
310
|
+
$stderr.puts e.message.red
|
311
|
+
if (options["verbose"])
|
312
|
+
e.backtrace.each do |line|
|
313
|
+
$stderr.puts line.yellow
|
314
|
+
end
|
315
|
+
end
|
305
316
|
exit RubeePassExit::EXCEPTION
|
306
317
|
rescue Exception => e
|
307
|
-
$stderr.puts
|
308
|
-
|
309
|
-
"
|
318
|
+
$stderr.puts [
|
319
|
+
"Oops! Looks like an error has occured! If the error",
|
320
|
+
"persists, file a bug at:"
|
321
|
+
].join(" ").wrap
|
310
322
|
$stderr.puts
|
311
323
|
$stderr.puts " https://gitlab.com/mjwhitta/rubeepass/issues"
|
312
324
|
$stderr.puts
|
313
|
-
$stderr.puts
|
314
|
-
"
|
315
|
-
|
325
|
+
$stderr.puts [
|
326
|
+
"Maybe the message below will help. If not, you can use the",
|
327
|
+
"--verbose flag to get a backtrace."
|
328
|
+
].join(" ").wrap
|
329
|
+
$stderr.puts
|
316
330
|
|
317
331
|
$stderr.puts e.message.white.on_red
|
318
332
|
if (options["verbose"])
|
data/lib/rubeepass.rb
CHANGED
@@ -10,6 +10,7 @@ require "uri"
|
|
10
10
|
require "zlib"
|
11
11
|
|
12
12
|
class RubeePass
|
13
|
+
# Header fields
|
13
14
|
@@END_OF_HEADER = 0
|
14
15
|
@@COMMENT = 1
|
15
16
|
@@CIPHER_ID = 2
|
@@ -22,10 +23,19 @@ class RubeePass
|
|
22
23
|
@@STREAM_START_BYTES = 9
|
23
24
|
@@INNER_RANDOM_STREAM_ID = 10
|
24
25
|
|
26
|
+
# Magic values
|
25
27
|
@@MAGIC_SIG1 = 0x9aa2d903
|
26
28
|
@@MAGIC_SIG2 = 0xb54bfb67
|
27
29
|
@@VERSION = 0x00030000
|
28
30
|
|
31
|
+
# Encryption schemes
|
32
|
+
@@AES_AESKDF3 = "31c1f2e6bf714350be5805216afc5aff"
|
33
|
+
@@AES_AESKDF4_OR_ARGON2 = "000031c1f2e6bf714350be5805216afc"
|
34
|
+
@@CHACHA20_AESKDF3 = "d6038a2b8b6f4cb5a524339a31dbb59a"
|
35
|
+
@@CHACHA20_AESKDF4_OR_ARGON2 = "0000d6038a2b8b6f4cb5a524339a31db"
|
36
|
+
@@TWOFISH_AESKDF3 = "ad68f29f576f4bb9a36ad47af965346c"
|
37
|
+
@@TWOFISH_AESKDF4_OR_ARGON2 = "0000ad68f29f576f4bb9a36ad47af965"
|
38
|
+
|
29
39
|
attr_reader :attachment_decoder
|
30
40
|
attr_reader :db
|
31
41
|
attr_reader :gzip
|
@@ -135,23 +145,43 @@ class RubeePass
|
|
135
145
|
end
|
136
146
|
end
|
137
147
|
|
138
|
-
def
|
148
|
+
def derive_aeskdf3_key(header)
|
149
|
+
irsi = "\x02\x00\x00\x00"
|
150
|
+
if (
|
151
|
+
(header[@@MASTER_SEED].length != 32) ||
|
152
|
+
(header[@@TRANSFORM_SEED].length != 32)
|
153
|
+
)
|
154
|
+
raise Error::InvalidHeader.new
|
155
|
+
elsif (header[@@INNER_RANDOM_STREAM_ID] != irsi)
|
156
|
+
raise Error::NotSalsa.new
|
157
|
+
end
|
158
|
+
|
139
159
|
cipher = OpenSSL::Cipher::AES.new(256, :ECB)
|
140
160
|
cipher.encrypt
|
141
161
|
cipher.key = @header[@@TRANSFORM_SEED]
|
142
162
|
cipher.padding = 0
|
143
163
|
|
164
|
+
key = @initial_key
|
144
165
|
@header[@@TRANSFORM_ROUNDS].times do
|
145
|
-
|
166
|
+
key = cipher.update(key) + cipher.final
|
146
167
|
end
|
147
168
|
|
148
|
-
transform_key = Digest::SHA256::digest(
|
169
|
+
transform_key = Digest::SHA256::digest(key)
|
149
170
|
combined_key = @header[@@MASTER_SEED] + transform_key
|
150
171
|
|
151
|
-
@
|
152
|
-
@
|
172
|
+
@cipher = OpenSSL::Cipher::AES.new(256, :CBC)
|
173
|
+
@key = Digest::SHA256::digest(combined_key)
|
174
|
+
@iv = @header[@@ENCRYPTION_IV]
|
153
175
|
end
|
154
|
-
private :
|
176
|
+
private :derive_aeskdf3_key
|
177
|
+
|
178
|
+
def derive_aeskdf4_or_argon2_key(header)
|
179
|
+
# require "pry"
|
180
|
+
# binding.pry
|
181
|
+
# puts "AES with AES-KDF4 or Argon2"
|
182
|
+
raise Error::NotSupported.new # TODO
|
183
|
+
end
|
184
|
+
private :derive_aeskdf4_or_argon2_key
|
155
185
|
|
156
186
|
def export(export_file, format)
|
157
187
|
start_opening
|
@@ -251,7 +281,7 @@ class RubeePass
|
|
251
281
|
end
|
252
282
|
end
|
253
283
|
|
254
|
-
@
|
284
|
+
@initial_key = Digest::SHA256.digest(passhash + filehash)
|
255
285
|
end
|
256
286
|
private :join_key_and_keyfile
|
257
287
|
|
@@ -336,6 +366,31 @@ class RubeePass
|
|
336
366
|
end
|
337
367
|
private :parse_gzip
|
338
368
|
|
369
|
+
def parse_header(header)
|
370
|
+
case header[@@CIPHER_ID].unpack("H*")[0]
|
371
|
+
when @@AES_AESKDF3
|
372
|
+
derive_aeskdf3_key(header)
|
373
|
+
when @@AES_AESKDF4_OR_ARGON2
|
374
|
+
derive_aeskdf4_or_argon2_key(header)
|
375
|
+
when @@CHACHA20_AESKDF3
|
376
|
+
# puts "ChaCha20 with AES-KDF3"
|
377
|
+
raise Error::NotSupported.new # TODO
|
378
|
+
when @@CHACHA20_AESKDF4_OR_ARGON2
|
379
|
+
# puts "ChaCha20 with AES-KDF4 or Argon2"
|
380
|
+
raise Error::NotSupported.new # TODO
|
381
|
+
when @@TWOFISH_AESKDF3
|
382
|
+
# puts "Twofish with AES-KDF3"
|
383
|
+
raise Error::NotSupported.new # TODO
|
384
|
+
when @@TWOFISH_AESKDF4_OR_ARGON2
|
385
|
+
# puts "Twofish with AES-KDF4 or Argon2"
|
386
|
+
raise Error::NotSupported.new # TODO
|
387
|
+
else
|
388
|
+
# puts header[@@CIPHER_ID].unpack("H*")[0]
|
389
|
+
raise Error::NotSupported.new
|
390
|
+
end
|
391
|
+
end
|
392
|
+
private :parse_header
|
393
|
+
|
339
394
|
def parse_xml
|
340
395
|
doc = REXML::Document.new(@xml)
|
341
396
|
if (doc.elements["KeePassFile/Root"].nil?)
|
@@ -352,10 +407,10 @@ class RubeePass
|
|
352
407
|
private :parse_xml
|
353
408
|
|
354
409
|
def read_gzip(file)
|
355
|
-
cipher =
|
410
|
+
cipher = @cipher.clone
|
356
411
|
cipher.decrypt
|
357
|
-
cipher.key = @
|
358
|
-
cipher.iv = @
|
412
|
+
cipher.key = @key
|
413
|
+
cipher.iv = @iv
|
359
414
|
|
360
415
|
encrypted = file.read
|
361
416
|
|
@@ -379,7 +434,7 @@ class RubeePass
|
|
379
434
|
header = Hash.new
|
380
435
|
loop do
|
381
436
|
data = file.read(1)
|
382
|
-
|
437
|
+
break if (data.nil?)
|
383
438
|
id = data.unpack("C*")[0]
|
384
439
|
|
385
440
|
data = file.read(2)
|
@@ -387,6 +442,9 @@ class RubeePass
|
|
387
442
|
size = data.unpack("S*")[0]
|
388
443
|
|
389
444
|
data = file.read(size)
|
445
|
+
if (data.nil? && (size > 0))
|
446
|
+
raise Error::InvalidHeader.new
|
447
|
+
end
|
390
448
|
|
391
449
|
case id
|
392
450
|
when @@END_OF_HEADER
|
@@ -398,19 +456,6 @@ class RubeePass
|
|
398
456
|
end
|
399
457
|
end
|
400
458
|
|
401
|
-
irsi = "\x02\x00\x00\x00"
|
402
|
-
aes = "31c1f2e6bf714350be5805216afc5aff"
|
403
|
-
if (
|
404
|
-
(header[@@MASTER_SEED].length != 32) ||
|
405
|
-
(header[@@TRANSFORM_SEED].length != 32)
|
406
|
-
)
|
407
|
-
raise Error::InvalidHeader.new
|
408
|
-
elsif (header[@@INNER_RANDOM_STREAM_ID] != irsi)
|
409
|
-
raise Error::NotSalsa.new
|
410
|
-
elsif (header[@@CIPHER_ID].unpack("H*")[0] != aes)
|
411
|
-
raise Error::NotAES.new
|
412
|
-
end
|
413
|
-
|
414
459
|
@header = header
|
415
460
|
end
|
416
461
|
private :read_header
|
@@ -436,20 +481,20 @@ class RubeePass
|
|
436
481
|
private :read_magic_and_version
|
437
482
|
|
438
483
|
def start_opening
|
439
|
-
@aes_iv = nil
|
440
|
-
@aes_key = nil
|
441
484
|
@db = nil
|
442
485
|
@gzip = nil
|
443
486
|
@header = nil
|
487
|
+
@initial_key = nil
|
488
|
+
@iv = nil
|
444
489
|
@key = nil
|
445
490
|
@xml = nil
|
446
491
|
|
447
492
|
file = File.open(@kdbx)
|
448
493
|
|
449
494
|
read_magic_and_version(file)
|
450
|
-
read_header(file)
|
495
|
+
header = read_header(file)
|
451
496
|
join_key_and_keyfile
|
452
|
-
|
497
|
+
parse_header(header)
|
453
498
|
read_gzip(file)
|
454
499
|
|
455
500
|
file.close
|
data/lib/rubeepass/error.rb
CHANGED
@@ -10,5 +10,5 @@ require "rubeepass/error/invalid_protected_data"
|
|
10
10
|
require "rubeepass/error/invalid_protected_stream_key"
|
11
11
|
require "rubeepass/error/invalid_version"
|
12
12
|
require "rubeepass/error/invalid_xml"
|
13
|
-
require "rubeepass/error/not_aes"
|
14
13
|
require "rubeepass/error/not_salsa20"
|
14
|
+
require "rubeepass/error/not_supported"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubeepass
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Miles Whittaker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-08-
|
11
|
+
date: 2018-08-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -192,8 +192,8 @@ files:
|
|
192
192
|
- lib/rubeepass/error/invalid_protected_stream_key.rb
|
193
193
|
- lib/rubeepass/error/invalid_version.rb
|
194
194
|
- lib/rubeepass/error/invalid_xml.rb
|
195
|
-
- lib/rubeepass/error/not_aes.rb
|
196
195
|
- lib/rubeepass/error/not_salsa20.rb
|
196
|
+
- lib/rubeepass/error/not_supported.rb
|
197
197
|
- lib/rubeepass/group.rb
|
198
198
|
- lib/rubeepass/protected_decryptor.rb
|
199
199
|
- lib/rubeepass/wish/cd_wish.rb
|