rubeepass 3.1.1 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ebdf490b8dbfa0fad27628bf3ee55cb12bf672dd81e54ca36b225499039ed77
4
- data.tar.gz: 783c86924e17c98334a8efc781868eb3f1ff3d14aeac714ea44ac0cf3dd4b395
3
+ metadata.gz: 6389189519412fd7b336eea64280403924c313955e78f3b6eaea377303a3552a
4
+ data.tar.gz: d4c77407fa1c2b92764f2dc314e8ac31c472b0c9c01a9c3158bca3a08ddecc8d
5
5
  SHA512:
6
- metadata.gz: 9af2bc5f82ba17b63c02ebef05a7da7a1b1ed0d32ec4ec607c819e84bff3a387bdeb4fd321c5c3778d82b285f827a35e75a53b2ddd1c83a35742a33abaf3f7b3
7
- data.tar.gz: e182c2ddd5cd06e7af83e344f893bba31c6b8670b65f29469c71e4721a8f22559e23ddde0f00cb3a9c3aab920920fc32407500aa94f3365cb4b2fe55a3251ac8
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,80}\S(?=\s|$)|\S+/).each do |line|
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
- $stderr.puts "Oops! Looks like an error has occured! If the " \
309
- "error persists, file a bug at:"
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 "Maybe the message below will help. If not, you " \
314
- "can use the --verbose flag to get"
315
- $stderr.puts "a backtrace."
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"])
@@ -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 derive_aes_key
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
- @key = cipher.update(@key) + cipher.final
166
+ key = cipher.update(key) + cipher.final
146
167
  end
147
168
 
148
- transform_key = Digest::SHA256::digest(@key)
169
+ transform_key = Digest::SHA256::digest(key)
149
170
  combined_key = @header[@@MASTER_SEED] + transform_key
150
171
 
151
- @aes_key = Digest::SHA256::digest(combined_key)
152
- @aes_iv = @header[@@ENCRYPTION_IV]
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 :derive_aes_key
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
- @key = Digest::SHA256.digest(passhash + filehash)
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 = OpenSSL::Cipher::AES.new(256, :CBC)
410
+ cipher = @cipher.clone
356
411
  cipher.decrypt
357
- cipher.key = @aes_key
358
- cipher.iv = @aes_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
- raise Error::InvalidHeader.new if (data.nil?)
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
- derive_aes_key
497
+ parse_header(header)
453
498
  read_gzip(file)
454
499
 
455
500
  file.close
@@ -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"
@@ -0,0 +1,5 @@
1
+ class RubeePass::Error::NotSupported < RubeePass::Error
2
+ def initialize
3
+ super("Encryption scheme not currently supported")
4
+ end
5
+ end
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.1.1
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-12 00:00:00.000000000 Z
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
@@ -1,5 +0,0 @@
1
- class RubeePass::Error::NotAES < RubeePass::Error
2
- def initialize
3
- super("Not AES!")
4
- end
5
- end