safedb 0.01.0001

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.yardopts +3 -0
  4. data/Gemfile +10 -0
  5. data/LICENSE +21 -0
  6. data/README.md +793 -0
  7. data/Rakefile +16 -0
  8. data/bin/safe +5 -0
  9. data/lib/configs/README.md +58 -0
  10. data/lib/extension/array.rb +162 -0
  11. data/lib/extension/dir.rb +35 -0
  12. data/lib/extension/file.rb +123 -0
  13. data/lib/extension/hash.rb +33 -0
  14. data/lib/extension/string.rb +572 -0
  15. data/lib/factbase/facts.safedb.net.ini +38 -0
  16. data/lib/interprete.rb +462 -0
  17. data/lib/keytools/PRODUCE_RAND_SEQ_USING_DEV_URANDOM.txt +0 -0
  18. data/lib/keytools/kdf.api.rb +243 -0
  19. data/lib/keytools/kdf.bcrypt.rb +265 -0
  20. data/lib/keytools/kdf.pbkdf2.rb +262 -0
  21. data/lib/keytools/kdf.scrypt.rb +190 -0
  22. data/lib/keytools/key.64.rb +326 -0
  23. data/lib/keytools/key.algo.rb +109 -0
  24. data/lib/keytools/key.api.rb +1391 -0
  25. data/lib/keytools/key.db.rb +330 -0
  26. data/lib/keytools/key.docs.rb +195 -0
  27. data/lib/keytools/key.error.rb +110 -0
  28. data/lib/keytools/key.id.rb +271 -0
  29. data/lib/keytools/key.ident.rb +243 -0
  30. data/lib/keytools/key.iv.rb +107 -0
  31. data/lib/keytools/key.local.rb +259 -0
  32. data/lib/keytools/key.now.rb +402 -0
  33. data/lib/keytools/key.pair.rb +259 -0
  34. data/lib/keytools/key.pass.rb +120 -0
  35. data/lib/keytools/key.rb +585 -0
  36. data/lib/logging/gem.logging.rb +132 -0
  37. data/lib/modules/README.md +43 -0
  38. data/lib/modules/cryptology/aes-256.rb +154 -0
  39. data/lib/modules/cryptology/amalgam.rb +70 -0
  40. data/lib/modules/cryptology/blowfish.rb +130 -0
  41. data/lib/modules/cryptology/cipher.rb +207 -0
  42. data/lib/modules/cryptology/collect.rb +138 -0
  43. data/lib/modules/cryptology/crypt.io.rb +225 -0
  44. data/lib/modules/cryptology/engineer.rb +99 -0
  45. data/lib/modules/mappers/dictionary.rb +288 -0
  46. data/lib/modules/storage/coldstore.rb +186 -0
  47. data/lib/modules/storage/git.store.rb +399 -0
  48. data/lib/session/fact.finder.rb +334 -0
  49. data/lib/session/require.gem.rb +112 -0
  50. data/lib/session/time.stamp.rb +340 -0
  51. data/lib/session/user.home.rb +49 -0
  52. data/lib/usecase/cmd.rb +487 -0
  53. data/lib/usecase/config/README.md +57 -0
  54. data/lib/usecase/docker/README.md +146 -0
  55. data/lib/usecase/docker/docker.rb +49 -0
  56. data/lib/usecase/edit/README.md +43 -0
  57. data/lib/usecase/edit/delete.rb +46 -0
  58. data/lib/usecase/export.rb +40 -0
  59. data/lib/usecase/files/README.md +37 -0
  60. data/lib/usecase/files/eject.rb +56 -0
  61. data/lib/usecase/files/file_me.rb +78 -0
  62. data/lib/usecase/files/read.rb +169 -0
  63. data/lib/usecase/files/write.rb +89 -0
  64. data/lib/usecase/goto.rb +57 -0
  65. data/lib/usecase/id.rb +36 -0
  66. data/lib/usecase/import.rb +157 -0
  67. data/lib/usecase/init.rb +63 -0
  68. data/lib/usecase/jenkins/README.md +146 -0
  69. data/lib/usecase/jenkins/jenkins.rb +208 -0
  70. data/lib/usecase/login.rb +71 -0
  71. data/lib/usecase/logout.rb +28 -0
  72. data/lib/usecase/open.rb +71 -0
  73. data/lib/usecase/print.rb +40 -0
  74. data/lib/usecase/put.rb +81 -0
  75. data/lib/usecase/set.rb +44 -0
  76. data/lib/usecase/show.rb +138 -0
  77. data/lib/usecase/terraform/README.md +91 -0
  78. data/lib/usecase/terraform/terraform.rb +121 -0
  79. data/lib/usecase/token.rb +35 -0
  80. data/lib/usecase/update/README.md +55 -0
  81. data/lib/usecase/update/rename.rb +180 -0
  82. data/lib/usecase/use.rb +41 -0
  83. data/lib/usecase/verse.rb +20 -0
  84. data/lib/usecase/view.rb +71 -0
  85. data/lib/usecase/vpn/README.md +150 -0
  86. data/lib/usecase/vpn/vpn.ini +31 -0
  87. data/lib/usecase/vpn/vpn.rb +54 -0
  88. data/lib/version.rb +3 -0
  89. data/safedb.gemspec +34 -0
  90. metadata +193 -0
@@ -0,0 +1,259 @@
1
+ #!/usr/bin/ruby
2
+ # coding: utf-8
3
+
4
+ module SafeDb
5
+
6
+ # The command line interface has a high entropy randomly generated
7
+ # key whose purpose is to <b>lock the application's data key</b> for
8
+ # the duration of the session which is between a login and a logout.
9
+ #
10
+ # These keys are unique to only one shell session on one workstation
11
+ # and they live lives that are no longer (and mostly shorter) than
12
+ # the life of the parent shell.
13
+ #
14
+ # == The 4 CLI Shell Entities
15
+ #
16
+ # The four (4) important entities within the shell session are
17
+ #
18
+ # - an obfuscator key for locking the shell key during a session
19
+ # - a high entropy randomly generated shell key for locking the app data key
20
+ # - one environment variable whose value embodies three (3) data segments
21
+ # - a session id derived by pushing the env var through a one-way function
22
+ class KeyLocal
23
+
24
+
25
+ # The number of Radix64 characters that make up a valid BCrypt salt.
26
+ # To create a BCrypt salt use
27
+ BCRYPT_SALT_LENGTH = 22
28
+
29
+
30
+ # There are two digits representing the BCrypt iteration count.
31
+ # The minimum is 10 and the maximum is 16.
32
+ BCRYPT_ITER_COUNT_SIZE = 2
33
+
34
+
35
+ # The session token comprises of 3 segments with fixed lengths.
36
+ # This triply segmented text token that can be used to decrypt
37
+ # and deliver the shell key.
38
+ SESSION_TOKEN_SIZE = 128 + 22 + BCRYPT_ITER_COUNT_SIZE
39
+
40
+
41
+ # Given a 152 character session token, what is the index that pinpoints
42
+ # the beginning of the 22 character BCrypt salt? The answer is given
43
+ # by this BCRYPT_SALT_START_INDEX constant.
44
+ BCRYPT_SALT_START_INDEX = SESSION_TOKEN_SIZE - BCRYPT_SALT_LENGTH - BCRYPT_ITER_COUNT_SIZE
45
+
46
+
47
+ # What index pinpoints the end of the BCrypt salt itself.
48
+ # This is easy as the final 2 characters are the iteration count
49
+ # so the end index is the length subtract 1 subtract 2.
50
+ BCRYPT_SALT_END_INDEX = SESSION_TOKEN_SIZE - 1
51
+
52
+
53
+ # Initialize the session by generating a random high entropy shell token
54
+ # and then generate an obfuscator key which we use to lock the shell
55
+ # key and return a triply segmented text token that can be used to decrypt
56
+ # and deliver the shell key as long as the same shell on the same machine
57
+ # is employed to make the call.
58
+ #
59
+ # <b>The 3 Session Token Segments</b>
60
+ #
61
+ # The session token is divided up into 3 segments with a total of 150
62
+ # characters.
63
+ #
64
+ # | -------- | ------------ | ------------------------------------- |
65
+ # | Segment | Length | Purpose |
66
+ # | -------- | ------------ | ------------------------------------- |
67
+ # | 1 | 16 bytes | AES Encrypt Initialization Vector(IV) |
68
+ # | 2 | 80 bytes | Cipher text from Random Key AES crypt |
69
+ # | 3 | 22 chars | Salt for obfuscator key derivation |
70
+ # | -------- | ------------ | ------------------------------------- |
71
+ # | Total | 150 chars | Session Token in Environment Variable |
72
+ # | -------- | ------------ | ------------------------------------- |
73
+ #
74
+ # Why is the <b>16 byte salt and the 80 byte BCrypt ciphertext</b> represented
75
+ # by <b>128 base64 characters</b>?
76
+ #
77
+ # 16 bytes + 80 bytes = 96 bytes
78
+ # 96 bytes x 8 bits = 768 bits
79
+ # 768 bits / 6 bits = 128 base64 characters
80
+ #
81
+ # @return [String]
82
+ # return a triply segmented text token that can be used to decrypt
83
+ # and redeliver the high entropy session shell key on the same machine
84
+ # and within the same shell on the same machine.
85
+ def self.generate_shell_key_and_token
86
+
87
+ bcrypt_salt_key = KdfBCrypt.generate_bcrypt_salt
88
+ obfuscator_key = derive_session_crypt_key( bcrypt_salt_key )
89
+ random_key_ciphertext = obfuscator_key.do_encrypt_key( Key.from_random() )
90
+ session_token = random_key_ciphertext + bcrypt_salt_key.reverse
91
+ assert_session_token_size( session_token )
92
+
93
+ return session_token
94
+
95
+ end
96
+
97
+
98
+ # Regenerate the random shell key that was instantiated and locked
99
+ # during the {instantiate_shell_key_and_generate_token} method.
100
+ #
101
+ # To successfully reacquire the randomly generated (and then locked)
102
+ # shell key we must be provided with five (5) data points, four (4)
103
+ # of which are embalmed within the 150 character session token
104
+ # parameter.
105
+ #
106
+ # <b>What we need to Regenerate the Shell Key</b>
107
+ #
108
+ # Regenerating the shell key is done in two steps when given the
109
+ # four (4) <b>session token segments</b> described below, and the
110
+ # shell identity key described in the {SafeDb::Identifier} class.
111
+ #
112
+ # The session token is divided up into 4 segments with a total of 152
113
+ # characters.
114
+ #
115
+ # | -------- | ------------ | ------------------------------------- |
116
+ # | Segment | Length | Purpose |
117
+ # | -------- | ------------ | ------------------------------------- |
118
+ # | 1 | 16 bytes | AES Encrypt Initialization Vector(IV) |
119
+ # | 2 | 80 bytes | Cipher text from Random Key AES crypt |
120
+ # | 3 | 22 chars | Salt 4 shell identity key derivation |
121
+ # | 4 | 2 chars | BCrypt iteration parameter (10 to 16) |
122
+ # | -------- | ------------ | ------------------------------------- |
123
+ # | Total | 152 chars | Session Token in Environment Variable |
124
+ # | -------- | ------------ | ------------------------------------- |
125
+ #
126
+ # @param session_token [String]
127
+ # a triply segmented (and one liner) text token instantiated by
128
+ # {self.instantiate_shell_key_and_generate_token} and provided
129
+ # here ad verbatim.
130
+ #
131
+ # @param use_grandparent_pid [Boolean]
132
+ #
133
+ # Optional boolean parameter. If set to true the PID (process ID) used
134
+ # as part of an obfuscator key and normally acquired from the parent
135
+ # process should now be acquired from the grandparent's process.
136
+ #
137
+ # Set to true when accessing the safe's credentials from a sub process
138
+ # rather than directly through the logged in shell.
139
+ #
140
+ # @return [SafeDb::Key]
141
+ # an extremely high entropy 256 bit key derived (digested) from 48
142
+ # random bytes at the beginning of the shell (cli) session.
143
+ def self.regenerate_shell_key( session_token, use_grandparent_pid = false )
144
+
145
+ assert_session_token_size( session_token )
146
+ bcrypt_salt = session_token[ BCRYPT_SALT_START_INDEX .. BCRYPT_SALT_END_INDEX ].reverse
147
+ assert_bcrypt_salt_size( bcrypt_salt )
148
+
149
+ key_ciphertext = session_token[ 0 .. ( BCRYPT_SALT_START_INDEX - 1 ) ]
150
+ obfuscator_key = derive_session_crypt_key( bcrypt_salt, use_grandparent_pid )
151
+ regenerated_key = obfuscator_key.do_decrypt_key( key_ciphertext )
152
+
153
+ return regenerated_key
154
+
155
+ end
156
+
157
+
158
+ # Derive a <b>short term (session scoped) encryption key</b> from the
159
+ # surrounding shell and workstation (machine) environment with an
160
+ # important same/different guarantee.
161
+ #
162
+ # The <b>same / different guarantee promises</b> us that the derived
163
+ # key will be
164
+ #
165
+ # - <b>the same</b> whenever called from within this executing shell
166
+ # - <b>different</b> when the shell and/or workstation are different
167
+ #
168
+ # This method uses a one-way function to return a combinatorial digested
169
+ # session identification string using a number of distinct parameters that
170
+ # deliver the important behaviours of changing in certain circumstances
171
+ # and remaining unchanged in others.
172
+ #
173
+ # <b>Change | When Should the key Change?</b>
174
+ #
175
+ # What is really important is that the <b>key changes when</b>
176
+ #
177
+ # - the <b>command shell</b> changes
178
+ # - the workstation <b>shell user is switched</b>
179
+ # - the host machine <b>workstation</b> is changed
180
+ # - the user <b>SSH's</b> into another shell
181
+ #
182
+ # A distinct workstation is identified by the first MAC address and the
183
+ # hostname of the machine.
184
+ #
185
+ # <b>Unchanged | When Should the Key Remain Unchanged?</b>
186
+ #
187
+ # Remaining <b>unchanged</b> in certain scenarios is a feature that is
188
+ # just as important as changing in others. The key must remain
189
+ # <b>unchanged</b> when
190
+ #
191
+ # - the <b>user returns to a command shell</b>
192
+ # - the user exits their <b>remote SSH session</b>
193
+ # - <b>sudo is used</b> to execute the commands
194
+ # - the user comes back to their <b>workstation</b>
195
+ # - the clock ticks into another day, month, year ...
196
+ #
197
+ # @param bcrypt_salt_key [SafeDb::Key]
198
+ #
199
+ # Either use BCrypt to generate the salt or retrieve and post in a
200
+ # previously generated salt which must hold 22 printable characters.
201
+ #
202
+ # @param use_grandparent_pid [Boolean]
203
+ #
204
+ # Optional boolean parameter. If set to true the PID (process ID) used
205
+ # as part of an obfuscator key and normally acquired from the parent
206
+ # process should now be acquired from the grandparent's process.
207
+ #
208
+ # Set to true when accessing the safe's credentials from a sub process
209
+ # rather than directly through the logged in shell.
210
+ #
211
+ # @return [SafeDb::Key]
212
+ # a digested key suitable for short term (session scoped) use with the
213
+ # guarantee that the same key will be returned whenever called from within
214
+ # the same executing shell environment and a different key when not.
215
+ def self.derive_session_crypt_key bcrypt_salt_key, use_grandparent_pid = false
216
+
217
+ shell_id_text = KeyIdent.derive_shell_identifier( use_grandparent_pid )
218
+ truncate_text = shell_id_text.length > KdfBCrypt::BCRYPT_MAX_IN_TEXT_LENGTH
219
+ shell_id_trim = shell_id_text unless truncate_text
220
+ shell_id_trim = shell_id_text[ 0 .. ( KdfBCrypt::BCRYPT_MAX_IN_TEXT_LENGTH - 1 ) ] if truncate_text
221
+
222
+ return KdfBCrypt.generate_key( shell_id_trim, bcrypt_salt_key )
223
+
224
+ end
225
+
226
+
227
+ private
228
+
229
+
230
+ # 000000000000000000000000000000000000000000000000000000000000000
231
+ # How to determine the caller.
232
+ # Better strategy would be just to print the stack trace
233
+ # That gives you much more bang for the one line buck.
234
+ # 000000000000000000000000000000000000000000000000000000000000000
235
+ # calling_module = File.basename caller_locations(1,1).first.absolute_path, ".rb"
236
+ # calling_method = caller_locations(1,1).first.base_label
237
+ # calling_lineno = caller_locations(1,1).first.lineno
238
+ # caller_details = "#{calling_module} | #{calling_method} | (line #{calling_lineno})"
239
+ # log.info(x) { "### Caller Details =>> =>> #{caller_details}" }
240
+ # 000000000000000000000000000000000000000000000000000000000000000
241
+
242
+
243
+ def self.assert_session_token_size session_token
244
+ err_msg = "Session token has #{session_token.length} and not #{SESSION_TOKEN_SIZE} chars."
245
+ raise RuntimeError, err_msg unless session_token.length == SESSION_TOKEN_SIZE
246
+ end
247
+
248
+
249
+ def self.assert_bcrypt_salt_size bcrypt_salt
250
+ amalgam_length = BCRYPT_SALT_LENGTH + BCRYPT_ITER_COUNT_SIZE
251
+ err_msg = "Expected BCrypt salt length of #{amalgam_length} not #{bcrypt_salt.length}."
252
+ raise RuntimeError, err_msg unless bcrypt_salt.length == amalgam_length
253
+ end
254
+
255
+
256
+ end
257
+
258
+
259
+ end
@@ -0,0 +1,402 @@
1
+ #!/usr/bin/ruby
2
+
3
+ module SafeDb
4
+
5
+ require 'singleton'
6
+
7
+ # This stamp sits at the centre of a fundamental DevOps pattern concerned
8
+ # with infrastructure provisioning and configuraion management.
9
+ #
10
+ # The central idea behind the pattern is to link every infrastructure
11
+ # object created during a session with a reference accurate to the nearest
12
+ # centi-second denoting the moment the software runtime (session) began.
13
+ class KeyNow
14
+ include Singleton
15
+
16
+ attr_reader :time_now
17
+
18
+ # Return two digit [mo] month index from 01 to 12.
19
+ # @example 02 => in February
20
+ #
21
+ def self.mo
22
+ return KeyNow.instance.time_now.strftime "%m"
23
+ end
24
+
25
+
26
+ # Return three character abbreviated month name.
27
+ # @example feb => in February
28
+ #
29
+ def self.mmm
30
+ return KeyNow.instance.time_now.strftime( "%b" ).downcase
31
+ end
32
+
33
+
34
+ #
35
+ # Return three character abbreviated day of week.
36
+ # @example tue => on Tuesday
37
+ #
38
+ def self.ddd
39
+ return KeyNow.instance.time_now.strftime( "%a" ).downcase
40
+ end
41
+
42
+
43
+ #
44
+ # Return two digit (character) hour of day from 00 to 23.
45
+ # @example 22 => between 22.00.00 and 22.59.59 inclusive
46
+ #
47
+ def self.hh
48
+ return KeyNow.instance.time_now.strftime "%H"
49
+ end
50
+
51
+
52
+ #
53
+ # Return two digit minute of hour from [00] to [59].
54
+ #
55
+ def self.mm
56
+ return KeyNow.instance.time_now.strftime "%M"
57
+ end
58
+
59
+
60
+ #
61
+ # Return two digit second of minute from [00] to [59].
62
+ #
63
+ def self.ss
64
+ return KeyNow.instance.time_now.strftime "%S"
65
+ end
66
+
67
+
68
+ #
69
+ # Return a [3 digit] second and tenth of second
70
+ # representation.
71
+ #
72
+ # The final digit is derived from the 1000 sliced
73
+ # millisecond of second running from 000 to 999.
74
+ #
75
+ # <tt>Truncation (Not Rounding)</tt>
76
+ #
77
+ # The [final] digit is acquired by TRUNCATING
78
+ # (chopping off) the last 2 of the 3 millisecond
79
+ # digits. No rounding is applied.
80
+ #
81
+ # The 3 returned digits comprise of the
82
+ #
83
+ # - second of minute => 2 digits | [00] to [59] (and)
84
+ # - tenth of second => 1 digit from [0] to [9]
85
+ #
86
+ # @example
87
+ #
88
+ # => The time at the 562nd millisecond of the 49th
89
+ # second of the minute.
90
+ #
91
+ # => 3 chars
92
+ # => 495
93
+ #
94
+ #
95
+ def self.sst
96
+ millisec_string = KeyNow.instance.time_now.strftime "%L"
97
+ return "#{ss}#{millisec_string[0]}"
98
+ end
99
+
100
+
101
+ #
102
+ # Return the [two] digit year (eg 19 for 2019).
103
+ # that we are currently in.
104
+ #
105
+ def self.yy
106
+ return KeyNow.instance.time_now.strftime("%Y")[2..-1]
107
+ end
108
+
109
+
110
+ #
111
+ # Return the [four] digit year (eg 2019)
112
+ # that we are currently in.
113
+ #
114
+ def self.yyyy
115
+ return KeyNow.instance.time_now.strftime("%Y")
116
+ end
117
+
118
+
119
+ # ------------------------------------------------- -- #
120
+ # Return 3 digit julian day of year [001] to [366]. -- #
121
+ # ------------------------------------------------- -- #
122
+ def self.jjj
123
+ return KeyNow.instance.time_now.strftime "%j"
124
+ end
125
+
126
+
127
+ # [yymo_mmm] returns an amalgam of
128
+ #
129
+ # => the two-digit year
130
+ # => the two-digit month index (starting at 01)
131
+ # => a period (separator)
132
+ # => the abbreviated month name
133
+ #
134
+ # @example
135
+ # => 1908.aug
136
+ # => for August 2019
137
+ #
138
+ def self.yymo_mmm
139
+ return "#{yy}#{mo}.#{mmm}"
140
+ end
141
+
142
+
143
+ #
144
+ # Given two integer parameters (month index and 4 digit year) representing
145
+ # the month in question this method returns the [PREVIOUS MONTHS] character
146
+ # amalgam in the format [yymo_mmm] where
147
+ #
148
+ # => yy | previous month's two-digit year
149
+ # => mo | previous month's two-digit month index
150
+ # => . | a period (separator)
151
+ # => mmm | previous month's abbreviated month name
152
+ #
153
+ # -------------------
154
+ # Example 1 (Simple)
155
+ # -------------------
156
+ #
157
+ # returns char => 1907.jul
158
+ # 4 parameters => 8, 2019
159
+ # representing => August, 2019
160
+ #
161
+ # ----------------------
162
+ # Example 2 (Last Year)
163
+ # ----------------------
164
+ #
165
+ # returns char => 1812.dec
166
+ # 4 parameters => 1, 2019
167
+ # representing => January, 2019
168
+ #
169
+ def self.previous_month_chars this_month_index, this_4digit_year
170
+
171
+ prev_month_index = this_month_index == 1 ? 12 : ( this_month_index - 1 )
172
+ prev_2dig_mn_pad = sprintf '%02d', prev_month_index
173
+ prev_4digit_year = this_month_index == 1 ? ( this_4digit_year - 1 ) : this_4digit_year
174
+ prev_twodigit_yr = "#{prev_4digit_year.to_s}"[2..-1]
175
+ prev_months_name = Date::ABBR_MONTHNAMES[prev_month_index].downcase
176
+
177
+ return "#{prev_twodigit_yr}#{prev_2dig_mn_pad}.#{prev_months_name}"
178
+
179
+ end
180
+
181
+ #
182
+ # Using the current class time this method returns
183
+ # the character amalgam for the [PREVIOUS MONTH] in
184
+ # the format [yymo_mmm] where
185
+ #
186
+ # => yy | last month's two-digit year
187
+ # => mo | last month's two-digit month index
188
+ # => . | a period (separator)
189
+ # => mmm | last month's abbreviated month name
190
+ #
191
+ # -------------------
192
+ # Example 1 (Simple)
193
+ # -------------------
194
+ #
195
+ # returns => 1907.jul
196
+ # if this month is => August 2019
197
+ #
198
+ # ----------------------
199
+ # Example 2 (Last Year)
200
+ # ----------------------
201
+ #
202
+ # returns => 1812.dec
203
+ # if this month is => January 2019
204
+ #
205
+ def self.yymo_mmm_prev
206
+ return previous_month_chars mo.to_i, yyyy.to_i
207
+ end
208
+
209
+
210
+ # Return 5 digit amalgam of year and julian day.
211
+ # eg [19003] for [January 3rd 2019]
212
+ def self.yyjjj
213
+ return "#{yy}#{jjj}"
214
+ end
215
+
216
+
217
+ # Return the 4 digit amalgam of the hour and minute
218
+ # using the 24 hour clock.
219
+ #
220
+ # @example
221
+ # => 1525
222
+ # => 03:25 pm
223
+ #
224
+ def self.hhmm
225
+ return "#{hh}#{mm}"
226
+ end
227
+
228
+
229
+ #
230
+ # Return the time of day to a TENTH of a second accuracy.
231
+ # [8] characters will always be returned with the 5th one
232
+ # being the (period) separator.
233
+ #
234
+ # The first (separated) segment delivers a hhmm 24 hour
235
+ # clock representation of the stamped time.
236
+ #
237
+ # The 3 digits of the second segment comprise of
238
+ #
239
+ # second of minute => 2 digits | [00] to [59]
240
+ # tenth of second => 1 digit from [0] to [9]
241
+ #
242
+ # @example
243
+ # => The time at the 562nd millisecond of the 49th
244
+ # second of the 23rd minute of the 17th hour of
245
+ # the day ( 17:23:49.562 )
246
+ #
247
+ # => 8 chars
248
+ # => 1723.495
249
+ #
250
+ def self.hhmm_sst
251
+ return "#{hhmm}.#{sst}"
252
+ end
253
+
254
+
255
+ # Return a string timestampt that is a period separated
256
+ # amalgam of the 2 digit year, 3 digit julian day, 2 digit
257
+ # hour, 2 digit minute, 2 digit second and 1 digit rounded
258
+ # down tenth of second.
259
+ #
260
+ # @example
261
+ # => 19003.1025
262
+ # => 10:25 am on January 3rd 2019
263
+ #
264
+ #
265
+ # Return the time of day to a TENTH of a second accuracy.
266
+ # [8] characters will always be returned with the 5th one
267
+ # being the (period) separator.
268
+ #
269
+ # The first (separated) segment delivers a hhmm 24 hour
270
+ # clock representation of the stamped time.
271
+ #
272
+ # The 3 digits of the second segment comprise of
273
+ #
274
+ # - second of minute => 2 digits | [00] to [59]
275
+ # - tenth of second => 1 digit from [0] to [9]
276
+ #
277
+ # @example
278
+ # => The time at the 562nd millisecond of the 49th
279
+ # second of the 23rd minute of the 17th hour of
280
+ # the day ( 17:23:49.562 )
281
+ #
282
+ # => 8 chars
283
+ # => 1723.495
284
+ #
285
+ def self.yyjjj_hhmm_sst
286
+ return "#{yyjjj}.#{hhmm}.#{sst}"
287
+ end
288
+
289
+
290
+ # Return a string timestampt that is a period separated
291
+ # amalgam of the 2 digit year, 3 digit julian day, 2 digit
292
+ # hour, 2 digit minute, 2 digit second and <b>9 digit</b>
293
+ # nanosecond.
294
+ #
295
+ # @example
296
+ # return => 19003.1725.42.836592034
297
+ # 4 time => 17:25:42 am on January 3rd 2019
298
+ #
299
+ # As per the above example, the time returned
300
+ #
301
+ # - is the 836592034 <b>nanosecond</b>
302
+ # - of the 42nd <b>second</b>
303
+ # - of the 25th <b>minute</b>
304
+ # - of the 17th <b>hour</b>
305
+ # - of the 3rd <b>day</b>
306
+ # - of the 20th <b>year</b>
307
+ # - of the 21st <b>century</b>
308
+ #
309
+ # @return [String]
310
+ # Return the time of day to nanosecond accuracy.
311
+ # 23 characters are always returned with three (3) period
312
+ # separators at the 6th, 11th and 14th positions.
313
+ def self.yyjjj_hhmm_ss_nanosec
314
+ nanosec_str = KeyNow.instance.time_now.strftime "%9N"
315
+ return "#{yyjjj}.#{hhmm}.#{ss}.#{nanosec_str}"
316
+ end
317
+
318
+
319
+ # Fetch the double barreled time stamp that is an amalgam of
320
+ # the human readable time now and a machine time representation
321
+ # from the moment this class was initialized.
322
+ #
323
+ # See the {yyjjj_hhmm_ss_nanosec} method for documentation of
324
+ # the nanosecond accurate time stamp.
325
+ #
326
+ # @return [String]
327
+ # the double barreled time stamp containing a human readable
328
+ # (right this moment) time and a <b>class initialized time</b>
329
+ # representation with nanosecond accuracy.
330
+ def self.fetch
331
+ return "#{Time.now.ctime} ( #{yyjjj_hhmm_ss_nanosec} )"
332
+ end
333
+
334
+
335
+ # Grab the double barreled time stamp that is an amalgam of
336
+ # the human readable time now and a machine time representation
337
+ # from the moment this class was initialized.
338
+ #
339
+ # On Friday June the 8th at about 6:26 pm.
340
+ # Fri Jun 8 18:26:17 2018 ( 18159.1826.138 )
341
+ #
342
+ # See the {yyjjj_hhmm_sst} method for documentation of stamp
343
+ # that is accurate to the tenth of a second.
344
+ #
345
+ # @return [String]
346
+ # the double barreled time stamp containing a human readable
347
+ # (right this moment) time and a <b>class initialized time</b>
348
+ # representation with tenth of a second accuracy.
349
+ def self.grab
350
+ time_with_consecutive_spaces = Time.now.ctime
351
+ human_readable_str = time_with_consecutive_spaces.gsub( " ", " " )
352
+ return "#{human_readable_str} ( #{yyjjj_hhmm_sst} )"
353
+ end
354
+
355
+
356
+ # Return the Rubyfied time zone being used.
357
+ def self.zone
358
+ return KeyNow.instance.time_now.zone
359
+ end
360
+
361
+
362
+ # Log segments of time pertaining to the time stamp.
363
+ # @todo
364
+ # move method contents into test class
365
+ def self.log_instance_time
366
+
367
+ log.info(x) { "[stamp] -------------- => -------------------------------- #" }
368
+ log.info(x) { "[stamp] eco time stamp => [#{KeyNow.instance.time_now.ctime}]" }
369
+ log.info(x) { "[stamp] -------------- => -------------------------------- #" }
370
+ log.info(x) { "[stamp] Univ Time Zone => #{zone}" }
371
+ log.info(x) { "[stamp] Month Index is => #{mo}" }
372
+ log.info(x) { "[stamp] Month Name is => #{mmm}" }
373
+ log.info(x) { "[stamp] Day Of Week is => #{ddd}" }
374
+ log.info(x) { "[stamp] -------------- => -------------------------------- #" }
375
+ log.info(x) { "[stamp] Two Digit Year => #{yy}" }
376
+ log.info(x) { "[stamp] Julian Cal Day => #{jjj}" }
377
+ log.info(x) { "[stamp] Yr and Jul Day => #{yyjjj}" }
378
+ log.info(x) { "[stamp] Hour of Theday => #{hh}" }
379
+ log.info(x) { "[stamp] Minute of Hour => #{mm}" }
380
+ log.info(x) { "[stamp] Hour + Minute => #{hhmm}" }
381
+ log.info(x) { "[stamp] Second of Min => #{ss}" }
382
+ log.info(x) { "[stamp] 600 Min Slices => #{sst}" }
383
+ log.info(x) { "[stamp] -------------- => -------------------------------- #" }
384
+ log.info(x) { "[stamp] The Time Stamp => #{yyjjj_hhmm_sst}" }
385
+ log.info(x) { "[stamp] -------------- => -------------------------------- #" }
386
+
387
+ end
388
+
389
+
390
+ # This singleton (one instance) class sets the time just once.
391
+ def initialize
392
+ @time_now = Time.now;
393
+ end
394
+
395
+
396
+ KeyNow.log_instance_time
397
+
398
+
399
+ end
400
+
401
+
402
+ end