opensecret 0.0.9925 → 0.0.9949

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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +656 -40
  3. data/lib/configs/README.md +58 -0
  4. data/lib/extension/file.rb +67 -0
  5. data/lib/extension/string.rb +10 -0
  6. data/lib/factbase/facts.opensecret.io.ini +1 -0
  7. data/lib/interprete.rb +334 -61
  8. data/lib/keytools/PRODUCE_RAND_SEQ_USING_DEV_URANDOM.txt +0 -0
  9. data/lib/keytools/kdf.api.rb +9 -15
  10. data/lib/keytools/kdf.bcrypt.rb +69 -19
  11. data/lib/keytools/kdf.pbkdf2.rb +112 -23
  12. data/lib/keytools/key.api.rb +146 -36
  13. data/lib/keytools/key.db.rb +94 -29
  14. data/lib/keytools/key.id.rb +1 -1
  15. data/lib/keytools/key.ident.rb +243 -0
  16. data/lib/keytools/key.local.rb +62 -68
  17. data/lib/keytools/key.pass.rb +2 -2
  18. data/lib/keytools/key.rb +2 -28
  19. data/lib/modules/{cryptology.md → README.md} +0 -0
  20. data/lib/session/fact.finder.rb +65 -428
  21. data/lib/session/time.stamp.rb +1 -28
  22. data/lib/usecase/cmd.rb +127 -54
  23. data/lib/usecase/config/README.md +57 -0
  24. data/lib/usecase/docker/README.md +146 -0
  25. data/lib/usecase/docker/docker.rb +49 -0
  26. data/lib/usecase/edit/README.md +43 -0
  27. data/lib/usecase/edit/delete.rb +46 -0
  28. data/lib/usecase/export.rb +40 -0
  29. data/lib/usecase/files/README.md +37 -0
  30. data/lib/usecase/files/eject.rb +56 -0
  31. data/lib/usecase/files/file_me.rb +78 -0
  32. data/lib/usecase/files/read.rb +169 -0
  33. data/lib/usecase/files/write.rb +89 -0
  34. data/lib/usecase/goto.rb +57 -0
  35. data/lib/usecase/id.rb +1 -1
  36. data/lib/usecase/import.rb +13 -30
  37. data/lib/usecase/init.rb +2 -17
  38. data/lib/usecase/jenkins/README.md +146 -0
  39. data/lib/usecase/jenkins/crazy_ruby_post_attempt.OLD +234 -0
  40. data/lib/usecase/jenkins/jenkins.rb +208 -0
  41. data/lib/usecase/login.rb +6 -5
  42. data/lib/usecase/logout.rb +1 -3
  43. data/lib/usecase/open.rb +11 -66
  44. data/lib/usecase/print.rb +40 -0
  45. data/lib/usecase/put.rb +34 -156
  46. data/lib/usecase/set.rb +2 -4
  47. data/lib/usecase/show.rb +138 -0
  48. data/lib/usecase/terraform/README.md +91 -0
  49. data/lib/usecase/terraform/terraform.rb +121 -0
  50. data/lib/usecase/token.rb +4 -80
  51. data/lib/usecase/update/README.md +55 -0
  52. data/lib/usecase/update/rename.rb +180 -0
  53. data/lib/usecase/use.rb +1 -3
  54. data/lib/usecase/verse.rb +20 -0
  55. data/lib/usecase/view.rb +71 -0
  56. data/lib/usecase/vpn/README.md +150 -0
  57. data/lib/usecase/vpn/vpn.ini +31 -0
  58. data/lib/usecase/vpn/vpn.rb +54 -0
  59. data/lib/version.rb +1 -1
  60. data/opensecret.gemspec +3 -4
  61. metadata +34 -35
  62. data/.travis.yml +0 -5
  63. data/CODE_OF_CONDUCT.md +0 -74
  64. data/LICENSE.txt +0 -21
  65. data/bin/ops +0 -20
  66. data/lib/keytools/binary.map.rb +0 -294
  67. data/lib/keytools/doc.conversion.to.ones.and.zeroes.ruby +0 -179
  68. data/lib/keytools/doc.rsa.radix.binary-mapping.ruby +0 -190
  69. data/lib/keytools/doc.star.schema.strategy.txt +0 -77
  70. data/lib/keytools/doc.using.pbkdf2.kdf.ruby +0 -95
  71. data/lib/keytools/doc.using.pbkdf2.pkcs.ruby +0 -266
  72. data/lib/keytools/key.mach.rb +0 -248
  73. data/lib/keytools/keydebug.txt +0 -295
  74. data/lib/modules/cryptology/open.bcrypt.rb +0 -170
  75. data/lib/usecase/read.rb +0 -89
  76. data/lib/usecase/safe.rb +0 -92
@@ -17,7 +17,6 @@ module OpenSession
17
17
 
18
18
  # Return two digit [mo] month index from 01 to 12.
19
19
  # @example 02 => in February
20
- #
21
20
  def self.mo
22
21
  return Stamp.instance.time_now.strftime "%m"
23
22
  end
@@ -25,47 +24,37 @@ module OpenSession
25
24
 
26
25
  # Return three character abbreviated month name.
27
26
  # @example feb => in February
28
- #
29
27
  def self.mmm
30
28
  return Stamp.instance.time_now.strftime( "%b" ).downcase
31
29
  end
32
30
 
33
31
 
34
- #
35
32
  # Return three character abbreviated day of week.
36
33
  # @example tue => on Tuesday
37
- #
38
34
  def self.ddd
39
35
  return Stamp.instance.time_now.strftime( "%a" ).downcase
40
36
  end
41
37
 
42
38
 
43
- #
44
39
  # Return two digit (character) hour of day from 00 to 23.
45
40
  # @example 22 => between 22.00.00 and 22.59.59 inclusive
46
- #
47
41
  def self.hh
48
42
  return Stamp.instance.time_now.strftime "%H"
49
43
  end
50
44
 
51
45
 
52
- #
53
46
  # Return two digit minute of hour from [00] to [59].
54
- #
55
47
  def self.mm
56
48
  return Stamp.instance.time_now.strftime "%M"
57
49
  end
58
50
 
59
51
 
60
- #
61
52
  # Return two digit second of minute from [00] to [59].
62
- #
63
53
  def self.ss
64
54
  return Stamp.instance.time_now.strftime "%S"
65
55
  end
66
56
 
67
57
 
68
- #
69
58
  # Return a [3 digit] second and tenth of second
70
59
  # representation.
71
60
  #
@@ -90,35 +79,27 @@ module OpenSession
90
79
  #
91
80
  # => 3 chars
92
81
  # => 495
93
- #
94
- #
95
82
  def self.sst
96
83
  millisec_string = Stamp.instance.time_now.strftime "%L"
97
84
  return "#{ss}#{millisec_string[0]}"
98
85
  end
99
86
 
100
87
 
101
- #
102
88
  # Return the [two] digit year (eg 19 for 2019).
103
89
  # that we are currently in.
104
- #
105
90
  def self.yy
106
91
  return Stamp.instance.time_now.strftime("%Y")[2..-1]
107
92
  end
108
93
 
109
94
 
110
- #
111
95
  # Return the [four] digit year (eg 2019)
112
96
  # that we are currently in.
113
- #
114
97
  def self.yyyy
115
98
  return Stamp.instance.time_now.strftime("%Y")
116
99
  end
117
100
 
118
101
 
119
- # ------------------------------------------------- -- #
120
- # Return 3 digit julian day of year [001] to [366]. -- #
121
- # ------------------------------------------------- -- #
102
+ # Return 3 digit julian day of year [001] to [366].
122
103
  def self.jjj
123
104
  return Stamp.instance.time_now.strftime "%j"
124
105
  end
@@ -140,7 +121,6 @@ module OpenSession
140
121
  end
141
122
 
142
123
 
143
- #
144
124
  # Given two integer parameters (month index and 4 digit year) representing
145
125
  # the month in question this method returns the [PREVIOUS MONTHS] character
146
126
  # amalgam in the format [yymo_mmm] where
@@ -165,7 +145,6 @@ module OpenSession
165
145
  # returns char => 1812.dec
166
146
  # 4 parameters => 1, 2019
167
147
  # representing => January, 2019
168
- #
169
148
  def self.previous_month_chars this_month_index, this_4digit_year
170
149
 
171
150
  prev_month_index = this_month_index == 1 ? 12 : ( this_month_index - 1 )
@@ -178,7 +157,6 @@ module OpenSession
178
157
 
179
158
  end
180
159
 
181
- #
182
160
  # Using the current class time this method returns
183
161
  # the character amalgam for the [PREVIOUS MONTH] in
184
162
  # the format [yymo_mmm] where
@@ -201,7 +179,6 @@ module OpenSession
201
179
  #
202
180
  # returns => 1812.dec
203
181
  # if this month is => January 2019
204
- #
205
182
  def self.yymo_mmm_prev
206
183
  return previous_month_chars mo.to_i, yyyy.to_i
207
184
  end
@@ -220,13 +197,11 @@ module OpenSession
220
197
  # @example
221
198
  # => 1525
222
199
  # => 03:25 pm
223
- #
224
200
  def self.hhmm
225
201
  return "#{hh}#{mm}"
226
202
  end
227
203
 
228
204
 
229
- #
230
205
  # Return the time of day to a TENTH of a second accuracy.
231
206
  # [8] characters will always be returned with the 5th one
232
207
  # being the (period) separator.
@@ -246,7 +221,6 @@ module OpenSession
246
221
  #
247
222
  # => 8 chars
248
223
  # => 1723.495
249
- #
250
224
  def self.hhmm_sst
251
225
  return "#{hhmm}.#{sst}"
252
226
  end
@@ -281,7 +255,6 @@ module OpenSession
281
255
  #
282
256
  # => 8 chars
283
257
  # => 1723.495
284
- #
285
258
  def self.yyjjj_hhmm_sst
286
259
  return "#{yyjjj}.#{hhmm}.#{sst}"
287
260
  end
@@ -10,7 +10,7 @@ module OpenSecret
10
10
  #
11
11
  # == Common Use Case Behaviour
12
12
  #
13
- # This {OpenSecret::Command} use case is designed to be extended and does preparatory
13
+ # This {OpenSecret::UseCase} use case is designed to be extended and does preparatory
14
14
  # work to create favourable and useful conditions to make use cases readable,
15
15
  # less repetitive, simpler and concise.
16
16
  #
@@ -26,21 +26,50 @@ module OpenSecret
26
26
  # - {stash} put directive key/value pair in default section
27
27
  # - {read} read the value at key_name from the parameter section
28
28
  # - {write} put directive key/value pair in parameter section
29
- class Command
30
- =begin
31
- # ---> rename from [Command] to [UseCase]
32
- # ---> rename from [Command] to [UseCase]
33
- # ---> rename from [Command] to [UseCase]
34
- # ---> rename from [Command] to [UseCase]
35
- # ---> rename from [Command] to [UseCase]
36
- # ---> rename from [Command] to [UseCase]
37
- =end
29
+ class UseCase
30
+
31
+ # This variable should be set to true if the use case call
32
+ # originates from a shell different from the one through which
33
+ # the login ocurred.
34
+ #
35
+ # To proceed, the shell that hosted the safe login must be a
36
+ # parent or at least an ancestor of this shell.
37
+ #
38
+ # This variable need not be set if the login shell is the direct
39
+ # parent of this one (the every day manual usage scenario).
40
+ #
41
+ # If however the login occurred from a grand or great grandparent
42
+ # shell (as is the case when nested scripts make an agent-like call),
43
+ # this variable must be set to true.
44
+ attr_writer :from_script
45
+
46
+ # This prefix denotes keys and their values should be posted as environment
47
+ # variables within the context (for example terraform) before instigating the
48
+ # main action like terraform apply.
49
+ ENV_VAR_PREFIX_A = "evar."
50
+ ENV_VAR_PREFIX_B = "@evar."
51
+
52
+ # This prefix precedes keynames whose map value represents a file object.
53
+ FILE_KEY_PREFIX = "file::"
54
+
55
+ # The base64 encoded representation of the file content is placed into
56
+ # a map with this keyname.
57
+ FILE_CONTENT_KEY = "content64"
58
+
59
+ # The file base name is placed into a map with this keyname.
60
+ FILE_NAME_KEY = "filename"
61
+
62
+
63
+ # This is the root command typed into the shell to invoke one of the
64
+ # opensecret use cases.
65
+ COMMANDMENT = "safe"
66
+
38
67
 
39
68
  # The name of the environment variable that will hold the session token
40
69
  # generated by {self.generate_session_token}. This environment variable
41
70
  # is typically instantiated either manually (for ad-hoc use) or through
42
71
  # media such as dot profile.
43
- ENV_VAR_KEY_NAME = "OPEN_SESSION_TOKEN"
72
+ ENV_VAR_KEY_NAME = "SAFE_TTY_TOKEN"
44
73
 
45
74
 
46
75
  # If and when this command line credentials management app needs to write
@@ -48,6 +77,33 @@ module OpenSecret
48
77
  # constant to denote the (off-home) directory name.
49
78
  APP_DIR_NAME = "opensecret.io"
50
79
 
80
+
81
+ # Get the master database. This behaviour can only complete
82
+ # correctly if a successful login precedes this call either
83
+ # in this or an ancestral shell environment.
84
+ #
85
+ # @return [Hash]
86
+ # the hash data structure returned represents the master
87
+ # database.
88
+ def get_master_database
89
+
90
+ begin
91
+
92
+ log.info(x) { "Request for master db with from_script set to #{@from_script}" }
93
+ return OpenKey::KeyApi.read_master_db( @from_script )
94
+
95
+ rescue OpenSSL::Cipher::CipherError => e
96
+
97
+ log.fatal(x) { "Exception getting master db for the safe book." }
98
+ log.fatal(x) { "The from_script parameter came set as [ #{@from_script} ]" }
99
+ log.fatal(x) { "The exception message is ~> [[ #{e.message} ]]" }
100
+ e.backtrace.log_lines
101
+ abort e.message
102
+
103
+ end
104
+
105
+ end
106
+
51
107
  # The path to the initial configuration file below the user's home
52
108
  # directory. The directory name the configuration file sits in is
53
109
  # a dot prefixed context name derived from the value inside the
@@ -96,7 +152,6 @@ module OpenSecret
96
152
  # set a human readable string and then throw an exception.
97
153
  def check_pre_conditions
98
154
 
99
-
100
155
  begin
101
156
 
102
157
  pre_validation
@@ -112,6 +167,11 @@ module OpenSecret
112
167
  abort e.message
113
168
  end
114
169
 
170
+ end
171
+
172
+
173
+ # Override me if you need to
174
+ def pre_validation
115
175
 
116
176
  end
117
177
 
@@ -177,23 +237,20 @@ module OpenSecret
177
237
  # use case classes.
178
238
  def initialize
179
239
 
240
+ class_name = self.class.name.split(":").last.downcase
241
+ is_no_token_usecase = [ "token", "init", "id" ].include? class_name
242
+ return if is_no_token_usecase
180
243
 
181
- class_name = self.class.name.downcase.split(":").last
182
- is_pre_init_usecase = [ "safe", "store", "email" ].include? class_name
183
- return if is_pre_init_usecase
244
+ exit(100) unless ops_key_exists?
184
245
 
185
- @ucid_str = self.class.name.do_flatten
186
- log.info(x) { "Usecase class [self.class.name] converted to => #{@ucid_str}" }
187
- @ucid_sym = @ucid_str.gsub(".", "_").to_sym
246
+ fact_filepath = File.sister_filepath( self, "ini", :execute )
247
+ log.info(x) { "Search location for INI factfile is [#{fact_filepath}]" }
248
+ return unless File.exists?( fact_filepath )
188
249
 
189
- OpenSession::FactFind.instance.instantiate @ucid_str
190
- OpenSession::FactFind.instance.assimilate "facts.opensecret.io.ini"
191
-
192
- @c = OpenSession::FactFind.instance.f
193
- @i = OpenSession::FactFind.instance.i
194
- @p = OpenSession::FactFind.instance.f[@ucid_sym]
195
-
196
- log.info(x) { "assimilated [#{@p.length}] facts specific to the [#{@ucid_str}] use case." }
250
+ @facts = FactFind.new()
251
+ add_secret_facts @facts
252
+ @facts.assimilate_ini_file( fact_filepath )
253
+ @dictionary = @facts.f[ @facts.to_symbol( class_name ) ]
197
254
 
198
255
  end
199
256
 
@@ -232,6 +289,22 @@ module OpenSecret
232
289
  APPLICATION_GITHUB_URL = "https://github.io/opensecret.io"
233
290
 
234
291
 
292
+ def add_secret_facts fact_db
293
+
294
+ master_db = get_master_database()
295
+ raise ArgumentError.new "There is no open chapter here." if unopened_envelope?( master_db )
296
+ chapter_id = ENVELOPE_KEY_PREFIX + master_db[ ENV_PATH ]
297
+ verse_id = master_db[ KEY_PATH ]
298
+ chapter_data = OpenKey::KeyDb.from_json( OpenKey::KeyApi.content_unlock( master_db[ chapter_id ] ) )
299
+ mini_dictionary = chapter_data[ master_db[ KEY_PATH ] ]
300
+
301
+ mini_dictionary.each do | key_str, value_str|
302
+ fact_db.assimilate_fact( "secrets", key_str, value_str )
303
+ end
304
+
305
+ end
306
+
307
+
235
308
  def create_header()
236
309
 
237
310
  return OpenKey::KeyApi.format_header(
@@ -258,9 +331,9 @@ module OpenSecret
258
331
  puts "To automate this step see the documentation."
259
332
  puts "To create the key run the below command."
260
333
  puts ""
261
- puts " export OPEN_SESSION_TOKEN=`ops token`"
334
+ puts " export #{ENV_VAR_KEY_NAME}=`#{COMMANDMENT} token`"
262
335
  puts ""
263
- puts "Those are backticks surrounding `ops token`"
336
+ puts "Those are backticks surrounding `#{COMMANDMENT} token`"
264
337
  puts "Not apostrophes."
265
338
  puts ""
266
339
 
@@ -304,25 +377,25 @@ module OpenSecret
304
377
  def print_unopened_envelope()
305
378
 
306
379
  puts ""
307
- puts "Oops - before creating, reading or changing data you"
380
+ puts "Problem - before creating, reading or changing data you"
308
381
  puts "must first open a path to it like this."
309
382
  puts ""
310
- puts " ops open email.accounts joe@gmail.com"
383
+ puts " #{COMMANDMENT} open email.accounts joe@gmail.com"
311
384
  puts ""
312
385
  puts " then you put data at that path"
313
386
  puts ""
314
- puts " ops put username joebloggs"
315
- puts " ops put password jo3s-s3cr3t"
316
- puts " ops put phone-no 07123456789"
317
- puts " ops put question \"Mums maiden name\""
387
+ puts " #{COMMANDMENT} put username joebloggs"
388
+ puts " #{COMMANDMENT} put password jo3s-s3cr3t"
389
+ puts " #{COMMANDMENT} put phone-no 07123456789"
390
+ puts " #{COMMANDMENT} put question \"Mums maiden name\""
318
391
  puts ""
319
392
  puts " and why not read it back"
320
393
  puts ""
321
- puts " ops get password"
394
+ puts " #{COMMANDMENT} get password"
322
395
  puts ""
323
396
  puts " then close the path."
324
397
  puts ""
325
- puts " ops close"
398
+ puts " #{COMMANDMENT} close"
326
399
  puts ""
327
400
 
328
401
  end
@@ -333,11 +406,11 @@ module OpenSecret
333
406
  puts ""
334
407
  puts "We are already logged in. Open a secret envelope, put, then seal."
335
408
  puts ""
336
- puts " ops open aws.credentials:s3reader"
337
- puts " ops put access_key ABCD1234"
338
- puts " ops put secret_key FGHIJ56789"
339
- puts " ops put region_key eu-central-1"
340
- puts " ops seal"
409
+ puts " #{COMMANDMENT} open aws.credentials:s3reader"
410
+ puts " #{COMMANDMENT} put access_key ABCD1234"
411
+ puts " #{COMMANDMENT} put secret_key FGHIJ56789"
412
+ puts " #{COMMANDMENT} put region_key eu-central-1"
413
+ puts " #{COMMANDMENT} seal"
341
414
  puts ""
342
415
 
343
416
  end
@@ -350,7 +423,7 @@ module OpenSecret
350
423
  puts "Your domain [#{@domain_name}] is already setup."
351
424
  puts "You should already know the password."
352
425
  puts ""
353
- puts " ops login #{@domain_name}"
426
+ puts " #{COMMANDMENT} login #{@domain_name}"
354
427
  puts ""
355
428
 
356
429
  end
@@ -363,7 +436,7 @@ module OpenSecret
363
436
  puts "The protector keys for [#{@domain_name}] have been setup."
364
437
  puts "From now on you simply login to use this domain."
365
438
  puts ""
366
- puts " ops login #{@domain_name}"
439
+ puts " #{COMMANDMENT} login #{@domain_name}"
367
440
  puts ""
368
441
 
369
442
  end
@@ -375,7 +448,7 @@ module OpenSecret
375
448
  puts "Please initialize the app domain on this machine."
376
449
  puts "Give a domain name and a folder for key storage."
377
450
  puts ""
378
- puts " ops init <domain_name> \"$HOME/open.world\""
451
+ puts " #{COMMANDMENT} init <domain_name> \"$HOME/open.world\""
379
452
  puts ""
380
453
 
381
454
  end
@@ -386,11 +459,11 @@ module OpenSecret
386
459
  puts ""
387
460
  puts "Success - now open a secret envelope, put, then seal."
388
461
  puts ""
389
- puts " ops open aws.credentials:s3reader"
390
- puts " ops put access_key ABCD1234"
391
- puts " ops put secret_key FGHIJ56789"
392
- puts " ops put region_key eu-central-1"
393
- puts " ops seal"
462
+ puts " #{COMMANDMENT} open aws.credentials:s3reader"
463
+ puts " #{COMMANDMENT} put access_key ABCD1234"
464
+ puts " #{COMMANDMENT} put secret_key FGHIJ56789"
465
+ puts " #{COMMANDMENT} put region_key eu-central-1"
466
+ puts " #{COMMANDMENT} seal"
394
467
  puts ""
395
468
 
396
469
  end
@@ -401,11 +474,11 @@ module OpenSecret
401
474
  puts ""
402
475
  puts "Success - you are logged in."
403
476
  puts ""
404
- puts " ops open aws.credentials:s3reader"
405
- puts " ops put access_key ABCD1234"
406
- puts " ops put secret_key FGHIJ56789"
407
- puts " ops put region_key eu-central-1"
408
- puts " ops seal"
477
+ puts " #{COMMANDMENT} open aws.credentials:s3reader"
478
+ puts " #{COMMANDMENT} put access_key ABCD1234"
479
+ puts " #{COMMANDMENT} put secret_key FGHIJ56789"
480
+ puts " #{COMMANDMENT} put region_key eu-central-1"
481
+ puts " #{COMMANDMENT} seal"
409
482
  puts ""
410
483
 
411
484
  end
@@ -0,0 +1,57 @@
1
+
2
+ # Safe | Increasing Key Derivation Function Cost
3
+
4
+ Safe uses two **key derivation functions** (**BCrypt** and **PBKDF2**) to transform the human sourced password into a key. The only role the resulting key plays is the encryption and decryption of a large **highly random computer generated key** which in turn protects the **master database**.
5
+
6
+ ### Why are two (2) key derivation algorithms used?
7
+
8
+ Your credentials are still safe even in the rare case of a successful analytical attack being discovered on one of the algorithms.
9
+
10
+ ## Why High Computational Costs are Desirable?
11
+
12
+ Unlike most algorithms, key derivation functions **work best when they run slowly!** This protects against brute force attacks where attackers use "rainbow tables" or try to iterate over common passwords in an attempt to rederive the key.
13
+
14
+ Your responsibility is to make **safe as slow as is tolerable** by increasing the number of iterations required to derive each key.
15
+
16
+ ## Safe | Increasing the Cost of Both Key Derivation Functions
17
+
18
+ You should increase the cost of **safe's** key derivation functions until safe commands run as slow as is tolerably and no less!
19
+
20
+ ```bash
21
+ safe cost bcrypt 3
22
+ safe cost pbkdf2 4
23
+ ```
24
+
25
+ Both algorithms can be configured with a cost parameter from 1 to 7 inclusive. The default cost is 1 for both and is moderately secure and runs as slowly as is tolerable on an IBM ThinkPad laptop with an Intel Pentium i5 processor with 16G of RAM.
26
+
27
+ Note that PBKDF2 has no maximum. BCrypt limits the cost to 2^16.
28
+
29
+ <pre>
30
+ -------- - ------------ - --------------- - --------------------- - ---------------- -
31
+ | Cost | BCrypt | BCrypt | PBKDF2 | PBKDF2 |
32
+ | | Cost | Iterations | Cost | Iterations |
33
+ | ------ - ------------ - --------------- - --------------------- - ---------------- |
34
+ | 1 | 2^10 | 1,024 | 3^0 x 100,000 | 100,000 |
35
+ | 2 | 2^11 | 2,048 | 3^1 x 100,000 | 300,000 |
36
+ | 3 | 2^12 | 4,096 | 3^2 x 100,000 | 900,000 |
37
+ | 4 | 2^13 | 8,192 | 3^3 x 100,000 | 2,700,000 |
38
+ | 5 | 2^14 | 16,384 | 3^4 x 100,000 | 8,100,000 |
39
+ | 6 | 2^15 | 32,768 | 3^5 x 100,000 | 24,300,000 |
40
+ | 7 | 2^16 | 65,536 | 3^6 x 100,000 | 72,900,000 |
41
+ -------- - ------------ - --------------- - --------------------- - ---------------- -
42
+ </pre>
43
+
44
+ When you increase the cost **safe will become perceivably slower**. With a cost of 7, a laptop takes many minutes but an AWS cloud compute optimized M5 server crunches through in mere seconds.
45
+
46
+ ## What is Your Data Worth?
47
+
48
+ Attackers can bring a significant amount of modern data centre hardware to the table in order to access your credentials.
49
+
50
+ However, these computing resources cost money and the amount of money an attacker spends will be proportional to the perceived gains from a successfully attack. The bigger the dollar signs in their eyes, the more they will spend.
51
+
52
+ The default settings coupled with a **12 character password** takes (on average) 50 years to crack with computing resources that will cost $1,000 every single day.
53
+
54
+ ### Twenty Million Dollars
55
+
56
+ If what you are protecting is worth more than **$(50 x 366 x 1,000)**, you should use an at least 16 character password and increase the computational cost parameters for both key derivation functions.
57
+