opensecret 0.0.913 → 0.0.941

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -0
  3. data/README.md +129 -19
  4. data/Rakefile +0 -9
  5. data/bin/opensecret +1 -1
  6. data/lib/{opensecret/plugins.io/cipher/crypto.rb → crypto/amalgam.rb} +6 -8
  7. data/lib/crypto/collect.rb +139 -0
  8. data/lib/crypto/engineer.rb +201 -0
  9. data/lib/crypto/verify.rb +33 -0
  10. data/lib/extension/array.rb +133 -0
  11. data/lib/{opensecret/additions → extension}/dir.rb +0 -0
  12. data/lib/extension/file.rb +56 -0
  13. data/lib/extension/hash.rb +33 -0
  14. data/lib/extension/string.rb +349 -0
  15. data/lib/factbase/facts.opensecret.io.ini +28 -0
  16. data/lib/logging/gem.logging.rb +133 -0
  17. data/lib/opensecret.rb +102 -45
  18. data/lib/opensecret/executors/crypt.keys/crypt.keys.ini +0 -53
  19. data/lib/session/{session.rb → attributes.rb} +60 -5
  20. data/lib/session/exceptions.rb +53 -0
  21. data/lib/session/fact.finder.rb +684 -0
  22. data/lib/session/user.home.rb +49 -0
  23. data/lib/usecase/usecase.rb +245 -0
  24. data/lib/usecase/usecases/init.rb +190 -0
  25. data/lib/usecase/usecases/on.rb +33 -0
  26. data/lib/usecase/usecases/safe.rb +95 -0
  27. data/lib/version.rb +1 -1
  28. metadata +22 -17
  29. data/lib/opensecret/additions/array.rb +0 -117
  30. data/lib/opensecret/additions/string.rb +0 -312
  31. data/lib/opensecret/commons/eco.cmdline.rb +0 -446
  32. data/lib/opensecret/eco.do.rb +0 -46
  33. data/lib/opensecret/plugins.io/error/eco.exceptions.rb +0 -24
  34. data/lib/opensecret/plugins.io/facts/fact.chars.rb +0 -66
  35. data/lib/opensecret/plugins.io/facts/fact.factor.rb +0 -156
  36. data/lib/opensecret/plugins.io/facts/fact.locator.rb +0 -105
  37. data/lib/opensecret/plugins.io/facts/fact.reader.rb +0 -137
  38. data/lib/opensecret/plugins.io/facts/fact.tree.rb +0 -661
  39. data/lib/opensecret/plugins.io/logs/log.object.rb +0 -89
  40. data/lib/opensecret/plugins.io/logs/logging.rb +0 -203
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # coding: utf-8
4
+
5
+ # opensession contains basic behaviour for managing a client only
6
+ # (serverless) session. Configuration directives are read and written
7
+ # from an INI off the home directory that is created when the session
8
+ # is first initiated.
9
+ module OpenSession
10
+
11
+ # This singleton class ascertains the users home folder in a manner
12
+ # agnositic to whether the software is running on Linux or Windows.
13
+ class Home
14
+ include Singleton
15
+
16
+ # This static behaviour reads the [home folder] just once.
17
+ def self.dir
18
+ return Home.instance.folder
19
+ end
20
+
21
+ # This static behaviour reads the [username] just once.
22
+ def self.usr
23
+ return Home.instance.username
24
+ end
25
+
26
+ attr_reader :folder
27
+ attr_reader :username
28
+
29
+ # Ascertain the home folder location.
30
+ def initialize
31
+
32
+ # On Windows the home folder may end with [AppData/Roaming].
33
+ extraneous_path = "/AppData/Roaming"
34
+
35
+ @folder = Dir.home
36
+ @username = @folder.split("/").last
37
+ return unless Dir.home.end_with? extraneous_path
38
+
39
+ # Remove the tail [AppData/Roaming] from the home path.
40
+ @folder = Dir.home.gsub extraneous_path, ""
41
+ @username = @folder.split("/").last
42
+
43
+ end
44
+
45
+
46
+ end
47
+
48
+
49
+ end
@@ -0,0 +1,245 @@
1
+ #!/usr/bin/ruby
2
+ # coding: utf-8
3
+
4
+ # opensession contains basic behaviour for managing a client only
5
+ # (serverless) session. Configuration directives are read and written
6
+ # from an INI off the home directory that is created when the session
7
+ # is first initiated.
8
+ #
9
+ # The session is expected to be formally closed down and that is
10
+ # reflected by explicitly deleting the configuration file. If this
11
+ # "session over" command is not issued a reasonable time limit is
12
+ # then invoked when the next session command is issued.
13
+ #
14
+ # This "session awakening" wipes the slate clean and starts afresh
15
+ # with regard to the two dimensional array of configuration directive
16
+ # pointers.
17
+ module OpenSession
18
+
19
+
20
+ require "session/exceptions"
21
+ require "session/fact.finder"
22
+
23
+ require "extension/array"
24
+ require "extension/dir"
25
+ require "extension/string"
26
+
27
+
28
+ # An opensession use case is designed to be extended and does preparatory
29
+ # work to create favourable and useful conditions to make use cases readable,
30
+ # less repetitive, simpler and concise.
31
+ class UseCase
32
+
33
+
34
+ # Execute the use cases's flow from beginning when
35
+ # you validate the input and parameters through the
36
+ # memorize, execute and the final cleanup.
37
+ def flow_of_events
38
+
39
+ check_pre_conditions
40
+ pre_memorize
41
+ execute
42
+ post_memorize
43
+ cleanup
44
+ check_post_conditions
45
+
46
+ end
47
+
48
+
49
+
50
+ # Validate the input parameters and check that the current
51
+ # state is perfect for executing the use case.
52
+ #
53
+ # If either of the above fail - the validation function should
54
+ # set a human readable string and then throw an exception.
55
+ def check_pre_conditions
56
+
57
+
58
+ begin
59
+
60
+ pre_validation
61
+
62
+ rescue OpenSessionError => e
63
+
64
+ puts ""
65
+ puts "Your command did not complete successfully."
66
+ puts "Pre validation checks failed."
67
+ puts ""
68
+ puts " => #{e.message}"
69
+ #### puts " => #{e.culprit}"
70
+ puts ""
71
+ abort e.message
72
+ end
73
+
74
+
75
+ end
76
+
77
+
78
+ # After the main flow of events certain state conditions
79
+ # must hold true thus demonstrating that the observable
80
+ # value has indeed ben delivered.
81
+ #
82
+ # Child classes should subclass this method and place any
83
+ # post execution (post condition) checks in it and then
84
+ # make a call to this method through the "super" keyword.
85
+ def check_post_conditions
86
+ begin
87
+
88
+ post_validation
89
+
90
+ rescue OpenSessionError => e
91
+
92
+ puts ""
93
+ puts "Your command did not complete successfully."
94
+ puts "Post validation checks failed."
95
+ puts ""
96
+ puts " => #{e.message}"
97
+ #### puts " => #{e.culprit}"
98
+ puts ""
99
+ abort e.message
100
+ end
101
+
102
+
103
+
104
+ end
105
+
106
+
107
+ # Child classes should subclass this method and place any
108
+ # post execution (post condition) checks in it and then
109
+ # make a call to this method through the "super" keyword if
110
+ # this method gets any global behaviour in it worth calling.
111
+ def post_validation
112
+
113
+ end
114
+
115
+
116
+
117
+ # (Pre) memorize adds to permanent storage a configuration
118
+ # directive and its value <tt>provided by the user</tt>
119
+ # primarily because it avoids repitition in subsequent
120
+ # use case commands.
121
+ def pre_memorize
122
+
123
+ end
124
+
125
+
126
+
127
+ # Execute the main flow of events of the use case. Any
128
+ # exceptions thrown are captured and if the instance
129
+ # variale [@human_readable_message] is set - tell the
130
+ # user about it. Without any message - just tell the
131
+ # user something went wrong and tell them where the logs
132
+ # are that might carry more information.
133
+ def execute
134
+
135
+ end
136
+
137
+
138
+
139
+ # (Post) memorize will add to permanent storage) any
140
+ # key/values that have been derived or looked up during
141
+ # use case execution. This allows subsequent use case
142
+ # commands to
143
+ #
144
+ # - capitalize on
145
+ # - take direction from
146
+ # - and/or simply use
147
+ #
148
+ # the value in later use cases or processing.
149
+ def post_memorize
150
+
151
+ end
152
+
153
+
154
+
155
+ # If the use case validation went well, the memorization
156
+ # went well the
157
+ def cleanup
158
+
159
+ end
160
+
161
+
162
+
163
+ # Resolve +two attached fact files+ with the first being general and
164
+ # relevant to every use case in the parameter specified domain, and
165
+ # the second being specific to this use case.
166
+ #
167
+ # The <b><em>general factfile</em></b> is expected to be on the class (load) path
168
+ # and below are paths for a generic domain name and for an example domain
169
+ # name of *openpost.io*
170
+ #
171
+ # - <tt>factbase/facts.<<domain>>.ini</tt>
172
+ # - <tt>factbase/facts.openpost.io.ini</tt>
173
+ #
174
+ # The <b><em>use case specific factfile</em></b> is also _expected_ to be on the class
175
+ # (load) path and below is a generic and a use case specific path using
176
+ # the example <tt>deliver</tt> use case.
177
+ #
178
+ # - <tt>factbase/facts.<<uc-name>>.usecase.ini</tt>
179
+ # - <tt>factbase/facts.deliver.usecase.ini</tt>
180
+ #
181
+ # The domain name is delivered in the parameter whilst the use case
182
+ # name is derived from the (extension) class name.
183
+ #
184
+ # @param fact_domain [String] domain of the general attached factfile
185
+ # @return [Hash] a 2d (two-dimensional) hash of keys and their corresponding values
186
+ def attached_facts fact_domain
187
+
188
+ end
189
+
190
+
191
+
192
+
193
+ ### def path_to_resources
194
+ ### File.join(File.dirname(File.expand_path(__FILE__)), '../path/to/resources')
195
+ ### end
196
+
197
+ # This use case is initialized primary by resolving the configured
198
+ # +general and use case specific facts+. To access the general facts,
199
+ # a domain name is expected in the parameter delegated by the extension
200
+ # use case classes.
201
+ def initialize
202
+
203
+
204
+ class_name = self.class.name.downcase.split(":").last
205
+ is_pre_init_usecase = [ "safe", "store", "email" ].include? class_name
206
+ return if is_pre_init_usecase
207
+
208
+ @ucid_str = self.class.name.do_flatten
209
+ @ucid_sym = @ucid_str.gsub(".", "_").to_sym
210
+
211
+ OpenSession::FactFind.instance.instantiate @ucid_str
212
+ OpenSession::FactFind.instance.assimilate "facts.opensecret.io.ini"
213
+
214
+ @c = OpenSession::FactFind.instance.f
215
+ @i = OpenSession::FactFind.instance.i
216
+ @p = OpenSession::FactFind.instance.f[@ucid_str]
217
+
218
+ @time_stamp = OpenSession::Stamp.instance
219
+ =begin
220
+ @eco_id_str = SnapFlat.do self.class.name
221
+ @eco_id_sym = @eco_id_str.gsub(".", "_").to_sym
222
+ @plugin_path = plugin_src_dir
223
+
224
+ FactTree.instance.instantiate @eco_id_str, @plugin_path
225
+ FactTree.instance.assimilate_instance_facts
226
+
227
+ instantiate_runtime FactTree.instance.f
228
+
229
+ FactTree.instance.identify_my_workstation
230
+ FactTree.instance.assimilate_station_facts
231
+ FactTree.instance.assimilate_plugin_facts
232
+ FactTree.instance.assimilate_general_facts
233
+
234
+ @c = FactTree.instance.f
235
+ @i = FactTree.instance.i
236
+ @p = FactTree.instance.f[@eco_id_sym]
237
+ =end
238
+
239
+ end
240
+
241
+
242
+ end
243
+
244
+
245
+ end
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/ruby
2
+
3
+ module OpenSecret
4
+
5
+ require 'openssl'
6
+
7
+ # --> require 'filesize'
8
+ # --> require 'tmpdir'
9
+ # --> require 'base64'
10
+ # --> require 'etc'
11
+ # --> require 'securerandom'
12
+ # --> require 'digest'
13
+ # --> require 'net/http'
14
+ # --> require 'net/ssh'
15
+ # --> require 'net/scp'
16
+ # --> require 'aws-sdk'
17
+ # --> require 'aws-sdk-resources'
18
+ # --> require 'nokogiri'
19
+ # --> require 'io/console'
20
+
21
+ require "session/exceptions"
22
+ require "crypto/collect"
23
+
24
+ # Throw this error if the configured safe directory points to a file.
25
+ class SafeDirectoryIsFile < OpenSession::OpenSessionError; end;
26
+ # Throw this error if safe directory path is either nil or empty.
27
+ class SafeDirNotConfigured < OpenSession::OpenSessionError; end;
28
+ # Throw this error if the email address is nil, empty or less than 5 characters.
29
+ class EmailAddrNotConfigured < OpenSession::OpenSessionError; end;
30
+ # Throw this error if the store url is either nil or empty.
31
+ class StoreUrlNotConfigured < OpenSession::OpenSessionError; end;
32
+ # Throw if "prime folder" name occurs 2 or more times in the path.
33
+ class SafePrimeNameRepeated < OpenSession::OpenSessionError; end;
34
+ # Throw if "prime folder" name occurs 2 or more times in the path.
35
+ class SafePrimeNameNotAtEnd < OpenSession::OpenSessionError; end;
36
+
37
+
38
+ # The <tt>init use case</tt> initializes +opensecret+ thus preparing it
39
+ # for the ability to lock secrets, unlock them, transport their keys and
40
+ # much more.
41
+ #
42
+ # Like all use cases, {self} validates to ensure that the pre-conditions
43
+ # have been met and then proceeds to execute its core offering and deliver
44
+ # observable value.
45
+ #
46
+ # == Observable Value
47
+ #
48
+ # The observable value delivered by init boils down to
49
+ #
50
+ # - <tt>4 crypted keys</tt> on USB (or phone) in the +master.keys+ folder.
51
+ # - <tt>1 crypted workstation key</tt> under the workstation's home directory
52
+ # - one human password stashed deep inside your brain
53
+ #
54
+ # +No cloud or other external access+ occurs as per the opensecret policy.
55
+ class Init < OpenSession::UseCase
56
+
57
+
58
+ attr_writer :safe_path, :email_addr, :store_url
59
+ @@context_name = "opensecret"
60
+
61
+ # The init use case initializes [opensecret] making it ready to lock and
62
+ # unlock secrets.
63
+ #
64
+ # == Main Flow of Events
65
+ #
66
+ # So in preparation to execute its modus operandi encryption,
67
+ # decryption and (if required) transportation use cases opensecret will
68
+ #
69
+ # - +collect the human password+ (twice), verify robustness (and throw away asap)
70
+ # - +manufacture workstation key+ that will be encrypted b4 it rests on machine
71
+ # - +create amalgamated human/workstation password+ for locking the private key
72
+ # - +create a long cryptographically strong symmetric encryption key+
73
+ # - +encrypt workstation key+ into <tt>.opensecret/<email>/machine.password.cipher.txt</tt>
74
+ # - +encrypt workstation encryption key+ with human password and email address
75
+ # - then write into <tt>safe</tt> under <tt>machine.password.key.cipher.txt</tt>
76
+ # - +create a super 8,192 bit private/public key pair+
77
+ # - use +amalgamated password to encrypt the private key+
78
+ # - write to <tt><SAFE>/<email>/master.keys/master.private.key.crypt.txt</tt>
79
+ # - +create robust salt+ for hashing the path to the (crypted) keys and ciphers
80
+ # - use +public key to encrypt the salt+
81
+ # - write crypted salt to <tt><SAFE>/<email>/master.keys/master.path.salt.crypt.txt</tt>
82
+ # - now +use the public key to encrypt itself+
83
+ # - write public key crypt to <tt><SAFE>/<email>/master.keys/master.public.key.crypt.txt</tt>
84
+ # - +create the cryptographic keystore+ inside the safe
85
+ #
86
+ # Variables should be first zeroed and then deleted immediately after their last use.
87
+ # +We need to avoid sensitive keys hanging around waiting for the garbage collector.+
88
+ # If necessary - sensitive areas such as these could be implemented in Go or C which
89
+ # deliver control of exactly where, when and how memory is allocaed, and zeroed out.
90
+ #
91
+ # == Observable Value
92
+ #
93
+ # The <tt>init use case</tt> observable value post-conditions are
94
+ #
95
+ # - <tt>4 crypted keys</tt> on USB (or phone) in the +master.keys+ folder.
96
+ # - <tt>1 crypted workstation key</tt> under the workstation's home directory
97
+ # - one human password stashed deep inside your brain
98
+ #
99
+ # == New Feature | Prevent Public Key Switch Attacks
100
+ #
101
+ # +Public Key switch attacks+ try to access <tt>future secrets</tt> - not the ones
102
+ # already encrypted.
103
+ #
104
+ # Their goal is <tt>unwitting encryption using a public key they control.</tt>
105
+ #
106
+ # A new feature should tie down the signatures of the 5 encrypted files (4 on
107
+ # usb/phone store and 1 on the machine) within a second workstation file and use
108
+ # first the human password and then the public key to lock it down.
109
+ #
110
+ # This action thwarts (usb key) switch attacks where the attacker knows the human
111
+ # password and has access to the USB key for a time.
112
+ #
113
+ def execute
114
+
115
+ natural_password = Collect.secret_text @c[:global][:min_passwd_len], @c[:global][:prompt_1], @c[:global][:prompt_2]
116
+
117
+ ##########------------> START FROM HERE (aside from prompt bug (check facts) all good.
118
+ ##########------------> START FROM HERE (aside from prompt bug (check facts) all good.
119
+ ##########------------> START FROM HERE (aside from prompt bug (check facts) all good.
120
+ ##########------------> START FROM HERE (aside from prompt bug (check facts) all good.
121
+ ##########------------> START FROM HERE (aside from prompt bug (check facts) all good.
122
+ ##########------------> START FROM HERE (aside from prompt bug (check facts) all good.
123
+ ##########------------> START FROM HERE (aside from prompt bug (check facts) all good.
124
+ ##########------------> START FROM HERE (aside from prompt bug (check facts) all good.
125
+ ##########------------> START FROM HERE (aside from prompt bug (check facts) all good.
126
+ ##########------------> START FROM HERE (aside from prompt bug (check facts) all good.
127
+ ##########------------> START FROM HERE (aside from prompt bug (check facts) all good.
128
+ ##########------------> START FROM HERE (aside from prompt bug (check facts) all good.
129
+ ##########------------> START FROM HERE (aside from prompt bug (check facts) all good.
130
+ machine_password = Crypto.get_machine_password natural_password.length, @p[:ratio]
131
+ amalgam_password = Crypto.get_amalgam_password natural_password, machine_password, @p[:ratio]
132
+
133
+ asymmetric_keys = OpenSSL::PKey::RSA.new @p[:bit_key_size]
134
+ secured_keytext = asymmetric_keys.export @p[:key_cipher], amalgam_password
135
+ public_key_text = asymmetric_keys.public_key.to_pem
136
+
137
+ Dir.mkdir @p[:secret_keydir] unless File.exists? @p[:secret_keydir]
138
+ File.write @p[:secret_keypath], secured_keytext
139
+
140
+ Crypto.print_secret_env_var @p[:env_var_name], machine_password
141
+
142
+ GitFlow.do_clone_repo @p[:public_gitrepo], @p[:local_gitrepo]
143
+ FileUtils.mkdir_p @p[:public_keydir]
144
+ File.write @p[:public_keypath], public_key_text
145
+ GitFlow.push @p[:local_gitrepo], @p[:public_keyname], @c[:time][:stamp]
146
+
147
+ # exit
148
+ # key4_pem = File.read 'private.secure.pem'
149
+ # pass_phrase = 'superduperpasswordistoBeENTEREDRIGHT1234HereandRightNOW'
150
+ # key4 = OpenSSL::PKey::RSA.new key4_pem, pass_phrase
151
+ # decrypted_text = key4.private_decrypt(Base64.decode64(encrypted_string))
152
+
153
+ # print "\nHey we have done the decryption.\n", "\n"
154
+ # print decrypted_text, "\n"
155
+
156
+
157
+
158
+ end
159
+
160
+
161
+ # Perform pre-conditional validations in preparation to executing the main flow
162
+ # of events for this use case. This method may throw the below exceptions.
163
+ #
164
+ # @raise [SafeDirNotConfigured] if the safe's url has not been configured
165
+ # @raise [EmailAddrNotConfigured] if the email address has not been configured
166
+ # @raise [StoreUrlNotConfigured] if the crypt store url is not configured
167
+ def pre_validation
168
+
169
+ @safe_path = OpenSession::Attributes.instance.get_value @@context_name, "safe"
170
+ safe_configured = File.exists?( @safe_path ) && File.directory?( @safe_path )
171
+ @err_msg = "[safe] storage not yet configured. Try =>] opensecret safe /folder/path"
172
+ raise SafeDirNotConfigured.new @err_msg, @safe_path unless safe_configured
173
+
174
+ @email_addr = OpenSession::Attributes.instance.get_value @@context_name, "email"
175
+ email_configured = !@email_addr.nil? && !@email_addr.empty? && @email_addr.length > 4
176
+ @err_msg = "viable [email address] not configured. Try =>] opensecret email joe@example.com"
177
+ raise EmailAddrNotConfigured.new @err_msg, @email_addr unless email_configured
178
+
179
+ @store_url = OpenSession::Attributes.instance.get_value @@context_name, "store"
180
+ store_configured = !@store_url.nil? && !@store_url.empty? && @store_url.length > 0
181
+ @err_msg = "crypt [store url] not configured. Try =>] opensecret store /path/to/crypt"
182
+ raise StoreUrlNotConfigured.new @err_msg, @store_url unless store_configured
183
+
184
+ end
185
+
186
+
187
+ end
188
+
189
+
190
+ end