safedb 0.01.0001
Sign up to get free protection for your applications and to get access to all the features.
- 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
|