opensecret 0.0.913 → 0.0.941

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