safedb 0.01.0001
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 +7 -0
- data/.gitignore +8 -0
- data/.yardopts +3 -0
- data/Gemfile +10 -0
- data/LICENSE +21 -0
- data/README.md +793 -0
- data/Rakefile +16 -0
- data/bin/safe +5 -0
- data/lib/configs/README.md +58 -0
- data/lib/extension/array.rb +162 -0
- data/lib/extension/dir.rb +35 -0
- data/lib/extension/file.rb +123 -0
- data/lib/extension/hash.rb +33 -0
- data/lib/extension/string.rb +572 -0
- data/lib/factbase/facts.safedb.net.ini +38 -0
- data/lib/interprete.rb +462 -0
- data/lib/keytools/PRODUCE_RAND_SEQ_USING_DEV_URANDOM.txt +0 -0
- data/lib/keytools/kdf.api.rb +243 -0
- data/lib/keytools/kdf.bcrypt.rb +265 -0
- data/lib/keytools/kdf.pbkdf2.rb +262 -0
- data/lib/keytools/kdf.scrypt.rb +190 -0
- data/lib/keytools/key.64.rb +326 -0
- data/lib/keytools/key.algo.rb +109 -0
- data/lib/keytools/key.api.rb +1391 -0
- data/lib/keytools/key.db.rb +330 -0
- data/lib/keytools/key.docs.rb +195 -0
- data/lib/keytools/key.error.rb +110 -0
- data/lib/keytools/key.id.rb +271 -0
- data/lib/keytools/key.ident.rb +243 -0
- data/lib/keytools/key.iv.rb +107 -0
- data/lib/keytools/key.local.rb +259 -0
- data/lib/keytools/key.now.rb +402 -0
- data/lib/keytools/key.pair.rb +259 -0
- data/lib/keytools/key.pass.rb +120 -0
- data/lib/keytools/key.rb +585 -0
- data/lib/logging/gem.logging.rb +132 -0
- data/lib/modules/README.md +43 -0
- data/lib/modules/cryptology/aes-256.rb +154 -0
- data/lib/modules/cryptology/amalgam.rb +70 -0
- data/lib/modules/cryptology/blowfish.rb +130 -0
- data/lib/modules/cryptology/cipher.rb +207 -0
- data/lib/modules/cryptology/collect.rb +138 -0
- data/lib/modules/cryptology/crypt.io.rb +225 -0
- data/lib/modules/cryptology/engineer.rb +99 -0
- data/lib/modules/mappers/dictionary.rb +288 -0
- data/lib/modules/storage/coldstore.rb +186 -0
- data/lib/modules/storage/git.store.rb +399 -0
- data/lib/session/fact.finder.rb +334 -0
- data/lib/session/require.gem.rb +112 -0
- data/lib/session/time.stamp.rb +340 -0
- data/lib/session/user.home.rb +49 -0
- data/lib/usecase/cmd.rb +487 -0
- data/lib/usecase/config/README.md +57 -0
- data/lib/usecase/docker/README.md +146 -0
- data/lib/usecase/docker/docker.rb +49 -0
- data/lib/usecase/edit/README.md +43 -0
- data/lib/usecase/edit/delete.rb +46 -0
- data/lib/usecase/export.rb +40 -0
- data/lib/usecase/files/README.md +37 -0
- data/lib/usecase/files/eject.rb +56 -0
- data/lib/usecase/files/file_me.rb +78 -0
- data/lib/usecase/files/read.rb +169 -0
- data/lib/usecase/files/write.rb +89 -0
- data/lib/usecase/goto.rb +57 -0
- data/lib/usecase/id.rb +36 -0
- data/lib/usecase/import.rb +157 -0
- data/lib/usecase/init.rb +63 -0
- data/lib/usecase/jenkins/README.md +146 -0
- data/lib/usecase/jenkins/jenkins.rb +208 -0
- data/lib/usecase/login.rb +71 -0
- data/lib/usecase/logout.rb +28 -0
- data/lib/usecase/open.rb +71 -0
- data/lib/usecase/print.rb +40 -0
- data/lib/usecase/put.rb +81 -0
- data/lib/usecase/set.rb +44 -0
- data/lib/usecase/show.rb +138 -0
- data/lib/usecase/terraform/README.md +91 -0
- data/lib/usecase/terraform/terraform.rb +121 -0
- data/lib/usecase/token.rb +35 -0
- data/lib/usecase/update/README.md +55 -0
- data/lib/usecase/update/rename.rb +180 -0
- data/lib/usecase/use.rb +41 -0
- data/lib/usecase/verse.rb +20 -0
- data/lib/usecase/view.rb +71 -0
- data/lib/usecase/vpn/README.md +150 -0
- data/lib/usecase/vpn/vpn.ini +31 -0
- data/lib/usecase/vpn/vpn.rb +54 -0
- data/lib/version.rb +3 -0
- data/safedb.gemspec +34 -0
- metadata +193 -0
@@ -0,0 +1,271 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
|
5
|
+
module SafeDb
|
6
|
+
|
7
|
+
|
8
|
+
# This class derives <b>non secret but unique identifiers</b> based on different
|
9
|
+
# combinations of the <b>application, shell and machine (compute element)</b>
|
10
|
+
# references.
|
11
|
+
#
|
12
|
+
# == Identifier Are Not Secrets
|
13
|
+
#
|
14
|
+
# <b>And their starting values are retrievable</b>
|
15
|
+
#
|
16
|
+
# Note that the principle and practise of <b>identifiers is not about keeping secrets</b>.
|
17
|
+
# An identifier can easily give up its starting value/s if and when brute force is
|
18
|
+
# applied. The properties of a good iidentifier (ID) are
|
19
|
+
#
|
20
|
+
# - non repeatability (also known as uniqueness)
|
21
|
+
# - non predictability (of the next identifier)
|
22
|
+
# - containing alphanumerics (for file/folder/url names)
|
23
|
+
# - human readable (hence hyphens and separators)
|
24
|
+
# - non offensive (no swear words popping out)
|
25
|
+
#
|
26
|
+
# == Story | Identifiers Speak Volumes
|
27
|
+
#
|
28
|
+
# I told a friend what the turnover of his company was and how many clients he had.
|
29
|
+
# He was shocked and wanted to know how I had gleened this information.
|
30
|
+
#
|
31
|
+
# The invoices he sent me (a year apart). Both his invoice IDs (identifiers) and his
|
32
|
+
# user IDs where integers that counted up. So I could determine how many new clients
|
33
|
+
# he had in the past year, how many clients he had when I got the invoice, and I
|
34
|
+
# determined the turnover by guesstimating the average invoice amount.
|
35
|
+
#
|
36
|
+
# Many successful website attacks are owed to a predictable customer ID or a counter
|
37
|
+
# type session ID within the cookies.
|
38
|
+
#
|
39
|
+
# == Good Identifiers Need Volumes
|
40
|
+
#
|
41
|
+
# IDs are not secrets - but even so, a large number of properties are required
|
42
|
+
# to produce a high quality ID.
|
43
|
+
#
|
44
|
+
class KeyId
|
45
|
+
|
46
|
+
|
47
|
+
# The identity chunk length is set at four (4) which means each of the
|
48
|
+
# fabricated identifiers comprises of four character segments divided by
|
49
|
+
# hyphens. Only the <b>62 alpha-numerics ( a-z, A-Z and 0-9 )</b> will
|
50
|
+
# appear within identifiers - which maintains simplicity and provides an
|
51
|
+
# opportunity to re-iterate that <b>identifiers</b> are designed to be
|
52
|
+
# <b>unpredictable</b>, but <b>not secret</b>.
|
53
|
+
IDENTITY_CHUNK_LENGTH = 4
|
54
|
+
|
55
|
+
|
56
|
+
# A hyphen is the chosen character for dividing the identifier strings
|
57
|
+
# into chunks of four (4) as per the {IDENTITY_CHUNK_LENGTH} constant.
|
58
|
+
SEGMENT_CHAR = "-"
|
59
|
+
|
60
|
+
|
61
|
+
# Get an identifier that is <b>always the same</b> for the parameter
|
62
|
+
# application reference <b>regardless of the machine or shell</b> or
|
63
|
+
# even the machine user, coming together to make the request.
|
64
|
+
#
|
65
|
+
# The returned identifier will consist only of alphanumeric characters
|
66
|
+
# and one hyphen, plus it always starts and ends with an alphanumeric.
|
67
|
+
#
|
68
|
+
# @param app_instance_ref [String]
|
69
|
+
# the string reference of the application instance (or shard) that
|
70
|
+
# is in play and needs to be digested into a unique but not-a-secret
|
71
|
+
# identifier.
|
72
|
+
#
|
73
|
+
# @return [String]
|
74
|
+
# An identifier that is guaranteed to be the same whenever the
|
75
|
+
# same application reference is provided on any machine, using any
|
76
|
+
# user through any shell interface or command prompt.
|
77
|
+
#
|
78
|
+
# It must be different for any other application reference.
|
79
|
+
def self.derive_app_instance_identifier( app_instance_ref )
|
80
|
+
return derive_identifier( app_instance_ref )
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
# Get an identifier that is <b>always the same</b> for the application
|
85
|
+
# instance (with reference given in parameter) on <b>this machine</b>
|
86
|
+
# and is always different when either/or or both the application ref
|
87
|
+
# and machine are different.
|
88
|
+
#
|
89
|
+
# The returned identifier will consist of only alphanumeric characters
|
90
|
+
# and hyphens - it will always start and end with an alphanumeric.
|
91
|
+
#
|
92
|
+
# This behaviour draws a fine line around the concept of machine, virtual
|
93
|
+
# machine, <b>workstation</b> and/or <b>compute element</b>.
|
94
|
+
#
|
95
|
+
# <b>(aka) The AIM ID</b>
|
96
|
+
#
|
97
|
+
# Returned ID is aka the <b>Application Instance Machine (AIM)</b> Id.
|
98
|
+
#
|
99
|
+
# @param app_ref [String]
|
100
|
+
# the string reference of the application instance (or shard) that
|
101
|
+
# is being used.
|
102
|
+
#
|
103
|
+
# @return [String]
|
104
|
+
# an identifier that is guaranteed to be the same whenever the
|
105
|
+
# same application reference is provided on this machine.
|
106
|
+
#
|
107
|
+
# it must be different on another machine even when the same
|
108
|
+
# application reference is provided.
|
109
|
+
#
|
110
|
+
# It will also be different on this workstation if the application
|
111
|
+
# instance identifier provided is different.
|
112
|
+
def self.derive_app_instance_machine_id( app_ref )
|
113
|
+
return derive_identifier( app_ref + KeyIdent.derive_machine_identifier() )
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
# The <b>32 character</b> <b>universal identifier</b> bonds a digested
|
118
|
+
# <b>application state identifier</b> with the <b>shell identifier</b>.
|
119
|
+
# This method gives <b>dual double guarantees</b> to the effect that
|
120
|
+
#
|
121
|
+
# - a change in one, or in the other, or in both returns a different universal id
|
122
|
+
# - the same app state identifier in the same shell produces the same universal id
|
123
|
+
#
|
124
|
+
# <b>The 32 Character Universal Identifier</b>
|
125
|
+
#
|
126
|
+
# The universal identifier is an amalgam of two digests which can be individually
|
127
|
+
# retrieved from other methods in this class. An example is
|
128
|
+
#
|
129
|
+
# universal id => hg2x0-g3uslf-pa2bl5-09xvbd-n4wcq
|
130
|
+
# the shell id => g3uslf-pa2bl5-09xvbd
|
131
|
+
# app state id => hg2x0-n4wcq
|
132
|
+
#
|
133
|
+
# The 32 character universal identifier comprises of 18 session identifier
|
134
|
+
# characters (see {derive_session_id}) <b>sandwiched between</b>
|
135
|
+
# ten (10) digested application identifier characters, five (5) in front and
|
136
|
+
# five (5) at the back - all segmented by four (4) hyphens.
|
137
|
+
#
|
138
|
+
# @param app_reference [String]
|
139
|
+
# the chosen plaintext application reference identifier that
|
140
|
+
# is the input to the digesting (hashing) algorithm.
|
141
|
+
#
|
142
|
+
# @param session_token [String]
|
143
|
+
# a triply segmented (and one liner) text token instantiated by
|
144
|
+
# {KeyLocal.generate_shell_key_and_token} and provided
|
145
|
+
# here ad verbatim.
|
146
|
+
#
|
147
|
+
# @return [String]
|
148
|
+
# a 32 character string that cannot feasibly be repeated due to the use
|
149
|
+
# of one way functions within its derivation. The returned identifier bonds
|
150
|
+
# the application state reference with the present session.
|
151
|
+
def self.derive_universal_id( app_reference, session_token )
|
152
|
+
|
153
|
+
shellid = derive_session_id( session_token )
|
154
|
+
app_ref = derive_identifier( app_reference + shellid )
|
155
|
+
chunk_1 = app_ref[ 0 .. IDENTITY_CHUNK_LENGTH ]
|
156
|
+
chunk_3 = app_ref[ ( IDENTITY_CHUNK_LENGTH + 1 ) .. -1 ]
|
157
|
+
|
158
|
+
return "#{chunk_1}#{shellid}#{SEGMENT_CHAR}#{chunk_3}".downcase
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
# The session ID generated here is a derivative of the 150 character
|
164
|
+
# session token instantiated by {KeyLocal.generate_shell_key_and_token}
|
165
|
+
# and provided here <b>ad verbatim</b>.
|
166
|
+
#
|
167
|
+
# The algorithm for deriving the session ID is as follows.
|
168
|
+
#
|
169
|
+
# - convert the 150 characters to an alphanumeric string
|
170
|
+
# - convert the result to a bit string and then to a key
|
171
|
+
# - put the key's binary form through a 384 bit digest
|
172
|
+
# - convert the digest's output to 64 YACHT64 characters
|
173
|
+
# - remove the (on average 2) non-alphanumeric characters
|
174
|
+
# - cherry pick a spread out 12 characters from the pool
|
175
|
+
# - hiphenate the character positions five (5) and ten (10)
|
176
|
+
# - ensure the length of the resultant ID is fourteen (14)
|
177
|
+
#
|
178
|
+
# The resulting session id will look something like this
|
179
|
+
#
|
180
|
+
# g3sf-pab5-9xvd
|
181
|
+
#
|
182
|
+
# @param session_token [String]
|
183
|
+
# a triply segmented (and one liner) text token instantiated by
|
184
|
+
# {KeyLocal.generate_shell_key_and_token} and provided here ad
|
185
|
+
# verbatim.
|
186
|
+
#
|
187
|
+
# @return [String]
|
188
|
+
# a 14 character string that cannot feasibly be repeated
|
189
|
+
# within the keyspace of even a gigantic organisation.
|
190
|
+
#
|
191
|
+
# This method guarantees that the session id will always be the same when
|
192
|
+
# called by commands within the same shell in the same machine.
|
193
|
+
def self.derive_session_id( session_token )
|
194
|
+
|
195
|
+
assert_session_token_size( session_token )
|
196
|
+
random_length_id_key = Key.from_char64( session_token.to_alphanumeric )
|
197
|
+
a_384_bit_key = random_length_id_key.to_384_bit_key()
|
198
|
+
a_64_char_str = a_384_bit_key.to_char64()
|
199
|
+
base_64_chars = a_64_char_str.to_alphanumeric
|
200
|
+
|
201
|
+
id_chars_pool = KeyAlgo.cherry_picker( ID_TRI_CHUNK_LEN, base_64_chars )
|
202
|
+
id_hyphen_one = id_chars_pool.insert( IDENTITY_CHUNK_LENGTH, SEGMENT_CHAR )
|
203
|
+
id_characters = id_hyphen_one.insert( ( IDENTITY_CHUNK_LENGTH * 2 + 1 ), SEGMENT_CHAR )
|
204
|
+
|
205
|
+
err_msg = "Shell ID needs #{ID_TRI_TOTAL_LEN} not #{id_characters.length} characters."
|
206
|
+
raise RuntimeError, err_msg unless id_characters.length == ID_TRI_TOTAL_LEN
|
207
|
+
|
208
|
+
return id_characters.downcase
|
209
|
+
|
210
|
+
end
|
211
|
+
|
212
|
+
|
213
|
+
# This method returns a <b>10 character</b> digest of the parameter
|
214
|
+
# <b>reference</b> string.
|
215
|
+
#
|
216
|
+
# <b>How to Derive the 10 Character Identifier</b>
|
217
|
+
#
|
218
|
+
# So how are the 10 characters derived from the reference provided in
|
219
|
+
# the first parameter. The algorithm is this.
|
220
|
+
#
|
221
|
+
# - reverse the reference and feed it to a 256 bit digest
|
222
|
+
# - chop away the rightmost digits so that 252 bits are left
|
223
|
+
# - convert the one-zero bit str to 42 (YACHT64) characters
|
224
|
+
# - remove the (on average 1.5) non-alphanumeric characters
|
225
|
+
# - cherry pick and return <b>spread out 8 characters</b>
|
226
|
+
#
|
227
|
+
# @param reference [String]
|
228
|
+
# the plaintext reference input to the digest algorithm
|
229
|
+
#
|
230
|
+
# @return [String]
|
231
|
+
# a 10 character string that is a digest of the reference string
|
232
|
+
# provided in the parameter.
|
233
|
+
def self.derive_identifier( reference )
|
234
|
+
|
235
|
+
bitstr_256 = Key.from_binary( Digest::SHA256.digest( reference.reverse ) ).to_s
|
236
|
+
bitstr_252 = bitstr_256[ 0 .. ( BIT_LENGTH_252 - 1 ) ]
|
237
|
+
id_err_msg = "The ID digest needs #{BIT_LENGTH_252} not #{bitstr_252.length} chars."
|
238
|
+
raise RuntimeError, id_err_msg unless bitstr_252.length == BIT_LENGTH_252
|
239
|
+
|
240
|
+
id_chars_pool = Key64.from_bits( bitstr_252 ).to_alphanumeric
|
241
|
+
undivided_str = KeyAlgo.cherry_picker( ID_TWO_CHUNK_LEN, id_chars_pool )
|
242
|
+
id_characters = undivided_str.insert( IDENTITY_CHUNK_LENGTH, SEGMENT_CHAR )
|
243
|
+
|
244
|
+
min_size_msg = "Id length #{id_characters.length} is not #{(ID_TWO_CHUNK_LEN + 1)} chars."
|
245
|
+
raise RuntimeError, min_size_msg unless id_characters.length == ( ID_TWO_CHUNK_LEN + 1 )
|
246
|
+
|
247
|
+
return id_characters.downcase
|
248
|
+
|
249
|
+
end
|
250
|
+
|
251
|
+
|
252
|
+
private
|
253
|
+
|
254
|
+
|
255
|
+
ID_TWO_CHUNK_LEN = IDENTITY_CHUNK_LENGTH * 2
|
256
|
+
ID_TRI_CHUNK_LEN = IDENTITY_CHUNK_LENGTH * 3
|
257
|
+
ID_TRI_TOTAL_LEN = ID_TRI_CHUNK_LEN + 2
|
258
|
+
|
259
|
+
BIT_LENGTH_252 = 252
|
260
|
+
|
261
|
+
|
262
|
+
def self.assert_session_token_size session_token
|
263
|
+
err_msg = "Session token has #{session_token.length} and not #{KeyLocal::SESSION_TOKEN_SIZE} chars."
|
264
|
+
raise RuntimeError, err_msg unless session_token.length == KeyLocal::SESSION_TOKEN_SIZE
|
265
|
+
end
|
266
|
+
|
267
|
+
|
268
|
+
end
|
269
|
+
|
270
|
+
|
271
|
+
end
|
@@ -0,0 +1,243 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
module SafeDb
|
5
|
+
|
6
|
+
# This class knows how to derive information from the machine environment to aide
|
7
|
+
# in producing identifiers unique to the machine and/or workstation, with functionality
|
8
|
+
# similar to that required by licensing software.
|
9
|
+
#
|
10
|
+
# == Identity is Similar to Licensing Software | Except Deeper
|
11
|
+
#
|
12
|
+
# Deriving the identity string follows similar principles to licensing
|
13
|
+
# software that attempts to determine whether the operating environment
|
14
|
+
# is the same or different. But it goes deeper than licensing software
|
15
|
+
# as it is not only concerned about the <b>same workstation</b> - it is
|
16
|
+
# also concerned about <b>the same shell or command line interface</b>.
|
17
|
+
#
|
18
|
+
# == Known Issues
|
19
|
+
#
|
20
|
+
# The dependent macaddr gem is known to fail in scenarios where a
|
21
|
+
# VPN tunnel is active and a tell tale sign is the ifconfig command
|
22
|
+
# returning the tun0 interface rather than "eth0" or something that
|
23
|
+
# resembles "ensp21".
|
24
|
+
#
|
25
|
+
# This is one of the error messages resulting from such a case.
|
26
|
+
#
|
27
|
+
# macaddr.rb:86 from_getifaddrs undefined method pfamily (NoMethodError)
|
28
|
+
#
|
29
|
+
class KeyIdent
|
30
|
+
|
31
|
+
# This method returns a plaintext string hat is guaranteed to be the same
|
32
|
+
# whenever called within the same shell for the same user on the same
|
33
|
+
# workstation, virtual machine, container or SSH session and different whenever
|
34
|
+
# a new shell is acquired.
|
35
|
+
#
|
36
|
+
# What is really important is that the <b>shell identity string changes</b> when
|
37
|
+
#
|
38
|
+
# - the <b>command shell</b> changes
|
39
|
+
# - the user <b>switches to another workstation user</b>
|
40
|
+
# - the <b>workstation or machine host</b> is changed
|
41
|
+
# - the user <b>SSH's</b> into another shell
|
42
|
+
#
|
43
|
+
# <b>Unchanged | When Should it Remain Unchanged?</b>
|
44
|
+
#
|
45
|
+
# Remaining <b>unchanged</b> is a feature that is as important and this must
|
46
|
+
# be so when and/or after
|
47
|
+
#
|
48
|
+
# - the <b>user returns to a command shell</b>
|
49
|
+
# - the user <b>switches back to using a domain</b>
|
50
|
+
# - the user exits their <b>remote SSH session</b>
|
51
|
+
# - <b>sudo is used</b> to execute the commands
|
52
|
+
# - the user comes back to their <b>workstation</b>
|
53
|
+
# - the clock ticks into another day, month, year ...
|
54
|
+
#
|
55
|
+
# @param use_grandparent_pid [Boolean]
|
56
|
+
#
|
57
|
+
# Optional boolean parameter. If set to true the PID (process ID) used
|
58
|
+
# as part of an obfuscator key and normally acquired from the parent
|
59
|
+
# process should now be acquired from the grandparent's process.
|
60
|
+
#
|
61
|
+
# Set to true when accessing the safe's credentials from a sub process
|
62
|
+
# rather than directly through the logged in shell.
|
63
|
+
#
|
64
|
+
# @return [String]
|
65
|
+
# Return a one line textual shell identity string.
|
66
|
+
#
|
67
|
+
# As key derivation algorithms enforcing a maximum length may be length may
|
68
|
+
# be applied, each character must add value so non-alphanumerics (mostly hyphens)
|
69
|
+
# are cleansed out before returning.
|
70
|
+
def self.derive_shell_identifier( use_grandparent_pid = false )
|
71
|
+
|
72
|
+
require 'socket'
|
73
|
+
|
74
|
+
# -- Ensure that the most significant data points
|
75
|
+
# -- come first just like with numbers.
|
76
|
+
|
77
|
+
identity_text =
|
78
|
+
[
|
79
|
+
get_ancestor_pid( use_grandparent_pid ),
|
80
|
+
get_bootup_id(),
|
81
|
+
Etc.getlogin(),
|
82
|
+
Socket.gethostname()
|
83
|
+
].join
|
84
|
+
|
85
|
+
return identity_text.to_alphanumeric
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
# Return an ancestor process ID meaning return either the parent process
|
91
|
+
# ID or the grandparent process ID. The one returned depends on the paremeter
|
92
|
+
# boolean value.
|
93
|
+
#
|
94
|
+
# == Command Used to find the grandparent process ID.
|
95
|
+
#
|
96
|
+
# $ ps -fp 31870 | awk "/tty/"' { print $3 } '
|
97
|
+
# $ ps -fp 31870 | awk "/31870/"' { print $3 } '
|
98
|
+
#
|
99
|
+
# The one liner finds the parental process ID of the process with the given
|
100
|
+
# parameter process ID.
|
101
|
+
#
|
102
|
+
# $ ps -fp 31870
|
103
|
+
#
|
104
|
+
# UID PID PPID C STIME TTY TIME CMD
|
105
|
+
# joe 31870 2618 0 12:55 tty2 00:01:03 /usr/bin/emacs25
|
106
|
+
#
|
107
|
+
# The ps command outputs two (2) lines and **awk** is employed to select the
|
108
|
+
# line containing the already known ID. We then print the 3rd string in the
|
109
|
+
# line which we expect to be the parent PID of the PID.
|
110
|
+
#
|
111
|
+
# == Warning | Do Not Use $PPID
|
112
|
+
#
|
113
|
+
# Using $PPID is fools gold because the PS command itself runs as another
|
114
|
+
# process so $PPID is this (calling) process ID and the number returned is
|
115
|
+
# exactly the same as the parent ID of this process - which is actually the
|
116
|
+
# grandparent of the invoked ps process.
|
117
|
+
#
|
118
|
+
# @param use_grandparent_pid [Boolean]
|
119
|
+
# Set to true if the grandparent process ID is required and false if
|
120
|
+
# only the parent process ID should be returned.
|
121
|
+
#
|
122
|
+
# @return [String]
|
123
|
+
# Return ancestor process ID that belongs to either the parent process
|
124
|
+
# or the grandparent process.
|
125
|
+
def self.get_ancestor_pid( use_grandparent_pid )
|
126
|
+
|
127
|
+
parental_process_id = Process.ppid.to_s()
|
128
|
+
grandparent_pid_cmd = "ps -fp #{parental_process_id} | awk \"/#{parental_process_id}/\"' { print $3 } '"
|
129
|
+
raw_grandparent_pid = %x[#{grandparent_pid_cmd}]
|
130
|
+
the_grandparent_pid = raw_grandparent_pid.chomp
|
131
|
+
|
132
|
+
log.debug(x) { "QQQQQ ~> QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ" }
|
133
|
+
log.debug(x) { "QQQQQ ~> Request Bool Use GPPID is ~> [[ #{use_grandparent_pid} ]]" }
|
134
|
+
log.debug(x) { "QQQQQ ~> Main Parent Process ID is ~> [[ #{parental_process_id} ]]" }
|
135
|
+
log.debug(x) { "QQQQQ ~> GrandParent Process ID is ~> [[ #{the_grandparent_pid} ]]" }
|
136
|
+
log.debug(x) { "QQQQQ ~> QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ" }
|
137
|
+
|
138
|
+
return ( use_grandparent_pid ? the_grandparent_pid : parental_process_id )
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
# This method uses a one-way function to return a combinatorial digested
|
144
|
+
# machine identification string using a number of distinct input parameters
|
145
|
+
# to deliver the characteristic of producing the same identifier for the
|
146
|
+
# same machine, virtual machine, workstation and/or compute element, and
|
147
|
+
# reciprocally, a different one on a different machine.
|
148
|
+
#
|
149
|
+
# The userspace is also a key machine identifier so a different machine user
|
150
|
+
# generates a different identifier when all other things remain equal.
|
151
|
+
#
|
152
|
+
# @return [String]
|
153
|
+
# a one line textual machine workstation or compute element identifier
|
154
|
+
# that is (surprisingly) different when the machine user changes.
|
155
|
+
def self.derive_machine_identifier
|
156
|
+
|
157
|
+
require 'socket'
|
158
|
+
|
159
|
+
identity_text = [
|
160
|
+
Etc.getlogin,
|
161
|
+
get_machine_id(),
|
162
|
+
Socket.gethostname()
|
163
|
+
].join.reverse
|
164
|
+
|
165
|
+
return identity_text
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
# If you need to know whether a Linux computer has been rebooted or
|
171
|
+
# you need an identifier that stays the same until the computer reboots,
|
172
|
+
# look no further than the read only (non sudoer accessible) **boot id**.
|
173
|
+
#
|
174
|
+
# In the modern era of virtualization you should always check the behaviour
|
175
|
+
# of the above identifiers when used inside
|
176
|
+
#
|
177
|
+
# - docker containers
|
178
|
+
# - Amazon EC2 servers (or Azure or GCE)
|
179
|
+
# - vagrant (VirtualBox/VMWare)
|
180
|
+
# - Windows MSGYWIN (Ubuntu) environments
|
181
|
+
# - Kubernetes pods
|
182
|
+
#
|
183
|
+
# @return [String] the bootup ID hash value
|
184
|
+
def self.get_bootup_id
|
185
|
+
|
186
|
+
bootup_id_cmd = "cat /proc/sys/kernel/random/boot_id"
|
187
|
+
bootup_id_str = %x[ #{bootup_id_cmd} ]
|
188
|
+
return bootup_id_str.chomp
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
# The machine identifier is a UUID based hash value that is tied to the
|
194
|
+
# CPU and motherboard of the machine. This read-only identifier can be
|
195
|
+
# accessed without sudoer permissions so is perfect for license generators
|
196
|
+
# and environment sensitive software.
|
197
|
+
#
|
198
|
+
# In the modern era of virtualization you should always check the behaviour
|
199
|
+
# of the above identifiers when used inside
|
200
|
+
#
|
201
|
+
# - docker containers
|
202
|
+
# - Amazon EC2 servers (or Azure or GCE)
|
203
|
+
# - vagrant (VirtualBox/VMWare)
|
204
|
+
# - Windows MSGYWIN (Ubuntu) environments
|
205
|
+
# - Kubernetes pods
|
206
|
+
#
|
207
|
+
# @return [String] the machine ID hash value
|
208
|
+
def self.get_machine_id
|
209
|
+
|
210
|
+
machine_id_cmd = "cat /etc/machine-id"
|
211
|
+
machine_id_str = %x[ #{machine_id_cmd} ]
|
212
|
+
return machine_id_str.chomp
|
213
|
+
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
# If the system was rebooted on April 23rd, 2018 at 22:00:16 we
|
218
|
+
# expect this method not to return <b>2018-04-23 22:00:16</b>, but
|
219
|
+
# to return the <b>8 least significant digits</b> bootup time
|
220
|
+
# digits which in this case are <b>23220016</b>.
|
221
|
+
#
|
222
|
+
# Investigate all Linux flavours to understand whether this command
|
223
|
+
# works (or is it just Ubuntu). Also does Docker return a sensible
|
224
|
+
# value here?
|
225
|
+
#
|
226
|
+
# This method is not production ready. Not only is the time within
|
227
|
+
# a small range, also the most significant digit can fluctuate up
|
228
|
+
# or down randomly (in a non-deterministic manner.
|
229
|
+
#
|
230
|
+
# @return [String] the time when the system was booted.
|
231
|
+
def self.get_bootup_time_digits
|
232
|
+
|
233
|
+
boot_time_cmd = "uptime -s"
|
234
|
+
uptime_string = %x[ #{boot_time_cmd} ]
|
235
|
+
return uptime_string.chomp.to_alphanumeric[ 6 .. -1 ]
|
236
|
+
|
237
|
+
end
|
238
|
+
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
module SafeDb
|
4
|
+
|
5
|
+
# Create and deliver representations of a random initialization vector
|
6
|
+
# suitable for the AES symmetric encryption algorithm which demands a
|
7
|
+
# 18 byte binary string.
|
8
|
+
#
|
9
|
+
# The initialization vector is sourced from {SecureRandom} which provides
|
10
|
+
# a highly random (and secure) byte sequence usually sourced from udev-random.
|
11
|
+
#
|
12
|
+
# + ------------------ + -------- + ------------ + ------------------- +
|
13
|
+
# | Random IV Format | Bits | Bytes | Base64 |
|
14
|
+
# | ------------------ | -------- | ------------ | ------------------- |
|
15
|
+
# | Random IV Stored | 192 Bits | 24 bytes | 32 characters |
|
16
|
+
# | Random IV Binary | 128 Bits | 16 bytes | (not stored) |
|
17
|
+
# + ------------------ + -------- + ------------ + ------------------- +
|
18
|
+
#
|
19
|
+
# This table shows that the initialization vector can be represented by
|
20
|
+
# both a <b>32 character base64 string</b> suitable for storage and a
|
21
|
+
# <b>18 byte binary</b> for feeding the algorithm.
|
22
|
+
class KeyIV
|
23
|
+
|
24
|
+
|
25
|
+
# The 24 random bytes is equivalent to 192 bits which when sliced into 6 bit
|
26
|
+
# blocks (one for each base64 character) results in 32 base64 characters.
|
27
|
+
NO_OF_BASE64_CHARS = 32
|
28
|
+
|
29
|
+
# We ask for 24 secure random bytes that are individually created to ensure
|
30
|
+
# we get exactly the right number.
|
31
|
+
NO_OF_SOURCE_BYTES = 24
|
32
|
+
|
33
|
+
# We truncate the source random bytes so that 16 bytes are returned for the
|
34
|
+
# random initialization vector.
|
35
|
+
NO_OF_BINARY_BYTES = 16
|
36
|
+
|
37
|
+
|
38
|
+
# Initialize an initialization vector from a source of random bytes
|
39
|
+
# which can then be presented in both a <b>(base64) storage</b> format
|
40
|
+
# and a <b>binary string</b> format.
|
41
|
+
#
|
42
|
+
# + ------------------ + -------- + ------------ + ------------------- +
|
43
|
+
# | Random IV Format | Bits | Bytes | Base64 |
|
44
|
+
# | ------------------ | -------- | ------------ | ------------------- |
|
45
|
+
# | Random IV Stored | 192 Bits | 24 bytes | 32 characters |
|
46
|
+
# | Random IV Binary | 128 Bits | 16 bytes | (not stored) |
|
47
|
+
# + ------------------ + -------- + ------------ + ------------------- +
|
48
|
+
#
|
49
|
+
# We ask for 24 secure random bytes that are individually created to ensure
|
50
|
+
# we get exactly the right number.
|
51
|
+
#
|
52
|
+
# If the storage format is requested a <b>32 character base64 string</b> is
|
53
|
+
# returned but if the binary form is requested the <b>first 16 bytes</b> are
|
54
|
+
# issued.
|
55
|
+
def initialize
|
56
|
+
@bit_string = Key.to_random_bits( NO_OF_SOURCE_BYTES )
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
# When the storage format is requested a <b>32 character base64 string</b> is
|
61
|
+
# returned - created from the initialized 24 secure random bytes.
|
62
|
+
#
|
63
|
+
# + ---------------- + -------- + ------------ + ------------------- +
|
64
|
+
# | Random IV Stored | 192 Bits | 24 bytes | 32 characters |
|
65
|
+
# + ---------------- + -------- + ------------ + ------------------- +
|
66
|
+
#
|
67
|
+
# @return [String]
|
68
|
+
# a <b>32 character base64 formatted string</b> is returned.
|
69
|
+
def for_storage
|
70
|
+
return Key64.from_bits( @bit_string )
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
#
|
75
|
+
# + ---------------- + -------- + ------------ + ------------------- +
|
76
|
+
# | Random IV Binary | 128 Bits | 16 bytes | (not stored) |
|
77
|
+
# + ---------------- + -------- + ------------ + ------------------- +
|
78
|
+
#
|
79
|
+
# @param iv_base64_chars [String]
|
80
|
+
# the 32 characters in base64 format that will be converted into a binary
|
81
|
+
# string (24 byte) representation and then truncated to 16 bytes and outputted
|
82
|
+
# in binary form.
|
83
|
+
#
|
84
|
+
# @return [String]
|
85
|
+
# a <b>16 byte binary string</b> is returned.
|
86
|
+
#
|
87
|
+
# @raise [ArgumentError]
|
88
|
+
# if a <b>32 base64 characters</b> are not presented in the parameter.
|
89
|
+
def self.in_binary iv_base64_chars
|
90
|
+
|
91
|
+
b64_msg = "Expected #{NO_OF_BASE64_CHARS} base64 chars not #{iv_base64_chars.length}."
|
92
|
+
raise ArgumentError, b64_msg unless iv_base64_chars.length == NO_OF_BASE64_CHARS
|
93
|
+
|
94
|
+
binary_string = Key.to_binary_from_bit_string( Key64.to_bits( iv_base64_chars ) )
|
95
|
+
|
96
|
+
bin_msg = "Expected #{NO_OF_SOURCE_BYTES} binary bytes not #{binary_string.length}."
|
97
|
+
raise RuntimeError, bin_msg unless binary_string.length == NO_OF_SOURCE_BYTES
|
98
|
+
|
99
|
+
return binary_string[ 0 .. ( NO_OF_BINARY_BYTES - 1 ) ]
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
end
|