opensecret 0.0.946 → 0.0.951

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f03e1b5d062560593f42736baf5a2af7794a4c0b
4
- data.tar.gz: ed65d8e10bd446f1413d9e0473b7291ec01f34a6
3
+ metadata.gz: ca35c2727f92f764b83fae65d656062b95520ad6
4
+ data.tar.gz: 260748f11554e881116e6efd8c4d44c0cdb06284
5
5
  SHA512:
6
- metadata.gz: 57989150d89ee5cc20d5950bd008b5bc97f4b23455dc690b66f791d1b3d1330a7550ce888715c73abf4bf43dd4ce80e409202c5393a7b32cc1bc41d75d97e047
7
- data.tar.gz: 842b02b7f3a2fbf99fdd3c9d4acbeeabcf16bed137894cf8d2ad1cc4aed54f6d39975c2d16714e3d8e16eb62bc9271d2a2a60bee2d6bd12c954bfeef0b71cb3b
6
+ metadata.gz: 10f94cba6fe468750401a38846641c9dddce2258601d981b1fb26e5b2e88c7fea27edb10264ee070d3d4920c6ada9470e0a7b4cb3178a5f5c2a07facb186db1a
7
+ data.tar.gz: 35aa0ff66488c53859c245c10ba2d3aae348b56df4df90d94963241b190b8065f39f20bbfb63c119159f25488b8262f69f847fd1a3e715d3fe8df13954cbc921
@@ -20,7 +20,7 @@ module OpenSecret
20
20
  # --
21
21
  def self.machine_key human_password_length, mix_ratio
22
22
 
23
- machine_raw_secret = engineer_password( human_password_length * ( mix_ratio + 1) )
23
+ machine_raw_secret = strong_key( human_password_length * ( mix_ratio + 1) )
24
24
  return machine_raw_secret[ 0..( human_password_length * mix_ratio - 1 ) ]
25
25
 
26
26
  end
@@ -30,7 +30,7 @@ module OpenSecret
30
30
  # -- Engineer a raw password that is similar (approximate) in
31
31
  # -- length to the integer parameter.
32
32
  # --
33
- def self.engineer_password approx_length
33
+ def self.strong_key approx_length
34
34
 
35
35
  non_alphanum = SecureRandom.urlsafe_base64(approx_length);
36
36
  return non_alphanum.delete("-_")
@@ -39,12 +39,24 @@ module OpenSecret
39
39
 
40
40
 
41
41
 
42
- # --
43
- # -- Get a viable machine password taking into account the human
44
- # -- password length and the specified mix_ratio.
45
- # --
46
- # -- machine password length = human password length * mix_ratio - 1
47
- # --
42
+ # Amalgamate the parameter passwords using a specific mix ratio. This method
43
+ # produces cryptographically stronger secrets than algorithms that simply
44
+ # concatenate two string keys together. If knowledge of one key were gained, this
45
+ # amalgamation algorithm still provides extremely strong protection even when
46
+ # one of the keys has a single digit length.
47
+ #
48
+ # This +length constraint formula+ binds the two input strings together with
49
+ # the integer mix ratio.
50
+ #
51
+ # <tt>machine password length = human password length * mix_ratio - 1</tt>
52
+ #
53
+ # @param human_password [String] the first password (shorter one) to amalgamate
54
+ # @param machine_password [String] the second password (longer one) to amalgamate
55
+ # @param mix_ratio [Fixnum] the mix ratio that must be respected by the
56
+ # previous two parameters.
57
+ # @return [String] an amalgamated (reproducible) union of the 2 parameter passwords
58
+ #
59
+ # @raise [ArgumentError] if the length constraint assertion does not hold true
48
60
  def self.get_amalgam_password human_password, machine_password, mix_ratio
49
61
 
50
62
  size_error_msg = "Human pass length times mix_ratio must equal machine pass length."
@@ -57,6 +57,27 @@ class String
57
57
  end
58
58
 
59
59
 
60
+ # To hex converts this string to hexadecimal form and returns
61
+ # the result leaving this string unchanged.
62
+ # @return [String] hexadecimal representation of this string
63
+ def to_hex
64
+
65
+ return self.unpack("H*").first
66
+
67
+ end
68
+
69
+
70
+ # From hex converts this (assumed) hexadecimal string back into
71
+ # its normal string form and returns the result leaving this string
72
+ # unchanged.
73
+ # @return [String] string that matches the hexadecimal representation
74
+ def from_hex
75
+
76
+ return [self].pack("H*")
77
+
78
+ end
79
+
80
+
60
81
  # Flatten (lower) a camel cased string and add periods to
61
82
  # denote separation where the capital letters used to be.
62
83
  #
@@ -78,11 +99,30 @@ class String
78
99
  # - in => BEAST
79
100
  # - out => b.e.a.s.t
80
101
  #
102
+ # == Flatten Class Names
103
+ #
104
+ # If the string comes in as a class name we can expect it to
105
+ # contain colons like the below examples.
106
+ # This::That
107
+ # ::That
108
+ # This::That::TheOther
109
+ #
110
+ # So we find the last index of a colon and then continue as per
111
+ # the above with flattening the string.
112
+ #
81
113
  # @return [String] a flatten (period separated) version of this camel cased string
82
114
  def do_flatten
83
115
 
116
+ to_flatten_str = self
117
+
118
+ last_colon_index = to_flatten_str.rindex ":"
119
+ ends_with_colon = to_flatten_str[-1].eql? ":"
120
+ unless ( last_colon_index.nil? || ends_with_colon )
121
+ to_flatten_str = to_flatten_str[ (last_colon_index+1) .. -1 ]
122
+ end
123
+
84
124
  snapped_str = ""
85
- self.each_char do |this_char|
125
+ to_flatten_str.each_char do |this_char|
86
126
  is_lower = "#{this_char}".is_all_lowercase?
87
127
  snapped_str += "." unless is_lower || snapped_str.empty?
88
128
  snapped_str += this_char.downcase
@@ -1,6 +1,7 @@
1
1
 
2
2
  [global]
3
3
 
4
+ name = opensecret
4
5
  min.passwd.len = rb>> 6
5
6
  nickname = godzilla
6
7
  root.domain = devopswiki.co.uk
@@ -8,21 +9,31 @@ env.var.name = SECRET_MATERIAL
8
9
  ratio = rb>> 3
9
10
  bit.key.size = rb>> 8192
10
11
  key.cipher = rb>> OpenSSL::Cipher.new 'AES-256-CBC'
11
- secret.keyname = master.private.key.crypt.txt
12
- secret.keydir = rb>> OpenSession::Attributes.instance.get_value "opensecret", "safe"
13
- secret.keypath = rb>> File.join @s[:secret_keydir], @s[:secret_keyname]
12
+ secret.keydir = rb>> OpenSession::Attributes.instance.get_value @s[:name], "safe"
13
+ email.address = rb>> OpenSession::Attributes.instance.get_value @s[:name], "email"
14
+ safe.user = rb>> File.join @s[:secret_keydir], @s[:email_address]
15
+ master.dirname = master.keys
16
+ master.dirpath = rb>> File.join @s[:safe_user], @s[:master_dirname]
17
+ master.pub.name = master.public.key.x.os.txt
18
+ master.prv.name = master.private.key.x.os.txt
19
+ master.pub.key = rb>> File.join @s[:master_dirpath], @s[:master_pub_name]
20
+ master.prv.key = rb>> File.join @s[:master_dirpath], @s[:master_prv_name]
14
21
 
15
- repo.name = material_data
16
-
17
- ## local.gitrepo = rb>> File.join @i[:dir], @s[:repo_name]
22
+ machine.key.x = machine.key.x
23
+ separator.a = %$os$%
18
24
 
19
- ## public.gitrepo = https://www.eco-platform.co.uk/content/material.data.git
20
- ## public.dirname = public_keys
21
- ## public.keyroute = rb>> File.join @s[:root_domain], @s[:public_dirname]
22
- ## public.keydir = rb>> File.join @s[:local_gitrepo], @s[:public_keyroute]
23
- ## public.keyname = rb>> "public_key." + @s[:nickname] + dot + @s[:root_domain] + ".txt"
24
- ## public.keypath = rb>> File.join @s[:public_keydir], @s[:public_keyname]
25
+ repo.name = material_data
25
26
 
26
27
  prompt.1 = Enter a Robust Password
27
28
  prompt.2 = Re-enter that Password
28
29
 
30
+ [open]
31
+
32
+ open.name = session
33
+ open.dirname = session.material
34
+ open.dirpath = rb>> File.join @f[:global][:safe_user], @s[:open_dirname]
35
+ open.idlen = 9
36
+ open.keylen = 96
37
+ open.idname = session.id
38
+ open.keyname = session.key
39
+ open.pathname = session.path
@@ -6,9 +6,87 @@
6
6
  ## Trial and Error Scratch-Pad ##
7
7
  ## ########################### ##
8
8
 
9
- x = "messagess"
10
9
 
11
- x += "x" until x.bytesize % 8 == 0
10
+ class Trial
12
11
 
13
- puts "Now x string is [#{x}]."
14
- puts "x is now [#{x.length}] characters long."
12
+
13
+ def self.crypt
14
+
15
+ require 'openssl'
16
+ require "base64"
17
+
18
+ key = OpenSSL::PKey::RSA.new(8192)
19
+
20
+
21
+ payload = "55fff4c5895bb247676c6edd2307f17c665305457b3bcfcd985c398246b8780f54e337252c5407afd4895a5e3a2415fce5b703a483da3edc88739cb7787262a19d69fb9416f900fed797c046aaec83b8e15b14edb032ed76535def8ada77108936e5442a839d4078048ca01449a6acd7315c9b7a7b8802dba0c83eb4c13e21b1051efa77a420a3ffd3cbf1fa13182933a0503f23cce95b68787081f3af33c69049657bdbf1fd30d79f108d604faad1fbee198a3e2c1b28cdddf7ebb84b6b0c1d3e9b47665bd96d7df8407e11d00e4d9275e805c7b9e61b6739802d6d87ac8283ef92a593ed53db2096cd1dc9496307f40942cc3d54a7c864ede71e0b192ce152"
22
+
23
+ puts ""
24
+ puts "Payload size is #{payload.length}"
25
+ puts ""
26
+ puts ""
27
+ puts encrypted_string = key.public_encrypt( payload, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
28
+ puts ""
29
+ puts base64text = Base64.encode64(encrypted_string)
30
+ puts ""
31
+ puts signature = key.sign(OpenSSL::Digest::SHA256.new, payload )
32
+ puts ""
33
+ puts hex_data = signature.unpack("H*").first
34
+ puts ""
35
+ puts "Length is #{hex_data.length}"
36
+ puts ""
37
+ puts key.public_key.verify(OpenSSL::Digest::SHA256.new, signature, payload)
38
+ puts ""
39
+ puts encrypted_string.unpack("H*").first
40
+ puts ""
41
+ puts key.private_decrypt(encrypted_string, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
42
+ puts ""
43
+
44
+
45
+ end
46
+
47
+ =begin
48
+ ddGKDqfhF6HnkJuIdTECZk7J7E9xx9LiYRDywCdIuDxYQQs+if+3qxP37+ah
49
+ HwGYgjxpIjqS9slhLOveVexSeHUD4DCbjHW2AlMsaUxwoSY0UfgzrO+2LDG9
50
+ tyizUYA6n8a+vBzJqRFP2BW7/AxwP0jm0yADWwBOGFL1+g==
51
+
52
+ <-|@| < || opensecret outer crypt material axis || > |@|->
53
+
54
+ HNkUjWaFoI5dPTRUUymyf7uKMaXFhiIZaOq+ZYj4TWPN92qv6ANTd3pRvVa3
55
+ S+aQSOX7q3FkKIOc5yfWLushGAMSwidgH1kzLvocCf+SSWH5BY3zTb7NAGjW
56
+ =end
57
+
58
+ ### Trial.crypt
59
+ ### exit
60
+
61
+ def try
62
+
63
+ require "pp"
64
+ require "inifile"
65
+ new_map = { "string1" => "value1", "string2" => "value2" }
66
+
67
+ ini_pairs = IniFile.new
68
+ ini_pairs["dictionary"] = new_map
69
+
70
+ puts ""
71
+ puts ini_pairs.to_s
72
+ puts ""
73
+
74
+
75
+ puts "--------------------"
76
+ puts new_map
77
+ puts "--------------------"
78
+ puts "#{pp new_map}"
79
+ puts "--------------------"
80
+ puts "#{new_map.to_s}"
81
+ puts "--------------------"
82
+ puts ""
83
+
84
+ ## x = "messagess"
85
+ ## x += "x" until x.bytesize % 8 == 0
86
+
87
+ ## puts "Now x string is [#{x}]."
88
+ ## puts "x is now [#{x.length}] characters long."
89
+
90
+ end
91
+
92
+ end
@@ -54,6 +54,16 @@ class CliInterpreter < Thor
54
54
  end
55
55
 
56
56
 
57
+ # Description of the open "wake-up" call.
58
+ desc "open", "open up a conduit for adding, subtracting and swapping secrets for a future commit."
59
+
60
+ # Open up a conduit from which we can add, subtract, update and list secrets
61
+ # before they are committed (and pushed) into permanent locked storage.
62
+ def open
63
+ OpenSecret::Open.new.flow_of_events
64
+ end
65
+
66
+
57
67
  # Description of the mandatory safe and (safe directory) configuration.
58
68
  desc "safe SAFE_DIR", "SAFE_DIR full path to the (ideally USB key) storage location"
59
69
 
@@ -50,7 +50,7 @@ module OpenSecret
50
50
  # - <tt>do_symmetric_encryption(plain_text)</tt> - resulting in ciphertext
51
51
  # - <tt>do_symmetric_decryption(ciphertext, encryption_dictionary)</tt> &raquo; plaintext
52
52
  #
53
- # and also set the <tt>@encryption_dictionary</tt> hash (map) of pertinent
53
+ # and also set the <tt>@dictionary</tt> hash (map) of pertinent
54
54
  # key/value pairs including the encryption algorithm, the encryption key and
55
55
  # the ciphertext signature to thwart any at-rest tampering.
56
56
  #
@@ -84,9 +84,36 @@ module OpenSecret
84
84
  #
85
85
  TEXT_PADDER = "<-|@|->"
86
86
 
87
+
88
+ # An unusual string that glues together an encryption dictionary and
89
+ # a chunk of base64 encoded and encrypted ciphertext.
90
+ # The string must be unusual enough to ensure it dos not occur within
91
+ # the dictionary metadata keys or values.
92
+ INNER_GLUE_STRING = "\n<-|@| < || opensecret inner crypt material axis || > |@|->\n\n"
93
+
94
+
95
+ # An unusual string that glues together the asymmetrically encrypted outer
96
+ # encryption key with the outer crypted text.
97
+ OUTER_GLUE_STRING = "\n<-|@| < || opensecret outer crypt material axis || > |@|->\n\n"
98
+
99
+
100
+ # Text header for key-value pairs hash map that will be serialized.
101
+ DICTIONARY = "dictionary"
102
+
103
+
87
104
  @@symmetric_cipher_keyname = "symmetric.cipher"
88
105
  @@encryption_key_keyname = "encryption.key"
89
- @@cipher_signature_keyname = "cipher.signature"
106
+ @@payload_signature_keyname = "payload.signature"
107
+
108
+
109
+ # The cipher constructor instantiates the encryption dictionary which
110
+ # will be collaboratively added to by the parent and child ciphers.
111
+ def initialize
112
+
113
+ @dictionary = {}
114
+
115
+ end
116
+
90
117
 
91
118
  # Ciphers use +symmetric algorithms+ to encrypt the given text, which
92
119
  # is then wrapped up along with the encryption key and other +metadata+
@@ -103,15 +130,55 @@ module OpenSecret
103
130
  # Every component in the pipeline bears the responsibility for nullifying
104
131
  # and rejecting malicious content.
105
132
  #
106
- # @param public_key [String] used for encrypting the key/metadata bundle
107
- # @param plain_text [String] the plain (or base64 encoded) text to encrypt
133
+ # @param public_key_text [String] textual portion of an {OpenSSL::PKey::RSA}
134
+ # public key that does not carry the private key as this method and its
135
+ # compatriots do not need to know the private key to do their work.
136
+ #
137
+ # @param payload_text [String] plaintext (or base64 encoded) text to encrypt
138
+ # @param payload_signature [String] signature of payload verifiable with public key
108
139
  #
109
140
  # @return [String] doubly (symmetric and asymmetric) encrypted cipher text
110
- def encrypt_it public_key, plain_text
141
+ def encrypt_it public_key_text, payload_text, payload_signature
142
+
143
+ asymmetric_key = OpenSSL::PKey::RSA.new public_key_text
144
+
145
+ signature_valid = asymmetric_key.verify(
146
+ OpenSSL::Digest::SHA256.new,
147
+ payload_signature,
148
+ payload_text
149
+ )
111
150
 
112
- symmetric_ciphertext = do_symmetric_encryption plain_text
113
- plain_paraphernalia = glue @encryption_dictionary, symmetric_ciphertext
114
- return do_asymmetric_encryption public_key, plain_paraphernalia
151
+ raise ArgumentError, "Payload not verified by the signature." unless signature_valid
152
+ @dictionary[@@payload_signature_keyname] = payload_signature.to_hex
153
+ crypted_payload = do_symmetric_encryption payload_text
154
+
155
+ unified_material = unify_hash_and_text crypted_payload
156
+ blowfish_cryptor = Blowfish.new
157
+ outer_crypt_key = OpenSecret::Engineer.strong_key( 128 )
158
+ crypted_material = blowfish_cryptor.do_encrypt_with_key unified_material, outer_crypt_key
159
+ crypted_cryptkey = do_asymmetric_encryption public_key_text, outer_crypt_key
160
+ locked_up_string = unify_text_and_text crypted_cryptkey, crypted_material
161
+
162
+ return locked_up_string
163
+
164
+ end
165
+
166
+
167
+ # Thismethod takes the textual public key lacking the private key portion
168
+ # as it is not needed, and encrypts the secret text with it.
169
+ # It returns a Base64 encoded version of they encrypted text.
170
+ #
171
+ # The public key text must be compatible with {OpenSSL::PKey::RSA} as the
172
+ # class will be instantiated using the public key text.
173
+ #
174
+ # @param public_key_text [String] the textual portion of an RSA public key
175
+ # @param secret_text [String] secret text to encrypt with the public key
176
+ #
177
+ # @return [String] a Base64 encoded version of the encrypted secret text
178
+ def do_asymmetric_encryption public_key_text, secret_text
179
+
180
+ asymmetric_encrypt_key = OpenSSL::PKey::RSA.new public_key_text
181
+ Base64.encode64( asymmetric_encrypt_key.public_encrypt( secret_text[0..1012] ) )
115
182
 
116
183
  end
117
184
 
@@ -140,6 +207,62 @@ module OpenSecret
140
207
  end
141
208
 
142
209
 
210
+ # Serialize and then unite a hash map and a textual chunk using
211
+ # a known but unusual separator string in a manner that protects
212
+ # the content integrity during the serialize and extraction phases.
213
+ #
214
+ # == Why an Unusual Separator String
215
+ #
216
+ # The separator string must be unusual to make it unlikely for it
217
+ # to occur in any of the map's key value pairs nor indeed the chunk
218
+ # of text being glued. Were this to happen, the separate and reconstitute
219
+ # phase may not accurately return the same two entities we are employed
220
+ # to unite.
221
+ #
222
+ # == Hash (Map) Contents
223
+ #
224
+ # The map contents will effectively be serialized in a simplistic
225
+ # manner therefore the keys should all be strings and the value
226
+ # types restricted to
227
+ #
228
+ # - strings
229
+ # - integers and/or floating point numbers
230
+ # - booleans
231
+ #
232
+ # Binary text is not allowed neither in the map or the text chunk.
233
+ # Any binary should be base64 encoded before being passed to us.
234
+ #
235
+ # @param text_chunk [String] the text chunk to be glued at the bottom
236
+ #
237
+ # @return [String] serialized and glued together result of map plus text
238
+ def unify_hash_and_text text_chunk
239
+
240
+ nil_or_empty_hash = @dictionary.nil? || @dictionary.empty?
241
+ raise ArgumentError, "Cannot unify nil or empty metadata." if nil_or_empty_hash
242
+
243
+ ini_map = IniFile.new
244
+ ini_map[ DICTIONARY ] = @dictionary
245
+
246
+ return ini_map.to_s + INNER_GLUE_STRING + text_chunk
247
+
248
+ end
249
+
250
+
251
+
252
+ # Using an outer divider (glue) - attach the asymmetrically encrypted outer
253
+ # encryption key with the outer encrypted text.
254
+ #
255
+ # @param crypt_material_x [String] asymmetrically encrypted (encoded) outer encryption key
256
+ # @param crypt_material_y [String] symmetrically encrypted inner metadata and payload crypt
257
+ #
258
+ # @return [String] concatenated result of the two crypt materials and divider string
259
+ def unify_text_and_text crypt_material_x, crypt_material_y
260
+
261
+ return crypt_material_x + OUTER_GLUE_STRING + crypt_material_y
262
+
263
+ end
264
+
265
+
143
266
 
144
267
  def encrypt_usecase public_key, secret_path, stores, plain_text
145
268
 
@@ -28,7 +28,7 @@ module OpenSecret
28
28
  # - <tt>do_symmetric_encryption(plain_text)</tt> - resulting in ciphertext
29
29
  # - <tt>do_symmetric_decryption(ciphertext, encryption_dictionary)</tt> &raquo; plaintext
30
30
  #
31
- # and it also sets the <tt>@encryption_dictionary</tt> hash (map) of pertinent
31
+ # and it also sets the <tt>@dictionary</tt> hash (map) of pertinent
32
32
  # key/value pairs including the +encryption algorithm+ and +encryption key+.
33
33
  #
34
34
  # That's It. Cipher children can rely on the {OpenSecret::Cipher} parent to
@@ -41,7 +41,7 @@ module OpenSecret
41
41
  # Use the AES 256 bit block cipher and a robust strong random key plus
42
42
  # initialization vector (IV) to symmetrically encrypt the plain text.
43
43
  #
44
- # Add these key/value pairs to @encryption_dictionary instance map.
44
+ # Add these key/value pairs to @dictionary instance map.
45
45
  #
46
46
  # - <tt>symmetric.cipher</tt> - the algorithm used to encrypt and decrypt
47
47
  # - <tt>encryption.key</tt> - hex encoded key for encrypting and decrypting
@@ -54,15 +54,13 @@ module OpenSecret
54
54
  @cipher_name = "aes-256-cbc"
55
55
 
56
56
  crypt_cipher = OpenSSL::Cipher.new @cipher_name
57
- crypt_cipher.encrypt( plain_text )
57
+ crypt_cipher.encrypt
58
58
 
59
- @encryption_dictionary = {
60
- @@symmetric_cipher_keyname => @cipher_name,
61
- @@encryption_key_keyname => crypt_cipher.random_key.unpack("H*").first,
62
- @@initialize_vector_keyname => crypt_cipher.random_iv.unpack("H*").first
63
- }
59
+ @dictionary[@@symmetric_cipher_keyname] = @cipher_name
60
+ @dictionary[@@encryption_key_keyname] = crypt_cipher.random_key.to_hex
61
+ @dictionary[@@initialize_vector_keyname] = crypt_cipher.random_iv.to_hex
64
62
 
65
- Base64.encode64( crypt_cipher.update + crypt_cipher.final )
63
+ return Base64.encode64( crypt_cipher.update( plain_text ) + crypt_cipher.final )
66
64
 
67
65
  end
68
66
 
@@ -73,7 +71,7 @@ module OpenSecret
73
71
  #
74
72
  # == Pre-Condition | Encryption Dictionary
75
73
  #
76
- # This method requires the <tt>@encryption_dictionary</tt> instance
74
+ # This method requires the <tt>@dictionary</tt> instance
77
75
  # variable to have been set and to contain (amongst others)
78
76
  #
79
77
  # - the <tt>encryption.key</tt> - hex encoded key for encrypting and decrypting
@@ -24,7 +24,7 @@ module OpenSecret
24
24
 
25
25
 
26
26
  # This method provides the Blowfish algorithm but we reserve the
27
- # right to enforce upon it - an encryption key of our choosing.
27
+ # right to enforce upon it an encryption key of our choosing.
28
28
  #
29
29
  # The key length need not be a multiple of 8 - however it is advisable
30
30
  # to use {Digest::SHA256.digest} to produce a strong 32 character key.
@@ -68,13 +68,13 @@ module OpenSecret
68
68
  log.info(x) { "os blowfish request to encrypt plain text with provided key." }
69
69
 
70
70
  block_txt = plain_text
71
- block_txt += ::Cipher::TEXT_PADDER until block_txt.bytesize % OpenSecret::Blowfish::BLOWFISH_BLOCK_LEN == 0
71
+ block_txt += OpenSecret::Cipher::TEXT_PADDER until block_txt.bytesize % OpenSecret::Blowfish::BLOWFISH_BLOCK_LEN == 0
72
72
  raw_stretched_key = Digest::SHA256.digest(encryption_key)
73
73
 
74
74
  blowfish_encryptor = OpenSSL::Cipher.new(OpenSecret::Blowfish::BLOWFISH_CIPHER_ID).encrypt
75
75
  blowfish_encryptor.key = raw_stretched_key
76
76
 
77
- Base64.encode64( blowfish_encryptor.update(block_txt) << blowfish_encryptor.final )
77
+ return Base64.encode64( blowfish_encryptor.update(block_txt) << blowfish_encryptor.final )
78
78
 
79
79
  end
80
80
 
@@ -51,14 +51,13 @@ module OpenSession
51
51
 
52
52
  pre_validation
53
53
 
54
- rescue OpenError::Error => e
54
+ rescue OpenError::CliError => e
55
55
 
56
56
  puts ""
57
57
  puts "Your command did not complete successfully."
58
58
  puts "Pre validation checks failed."
59
59
  puts ""
60
60
  puts " => #{e.message}"
61
- #### puts " => #{e.culprit}"
62
61
  puts ""
63
62
  abort e.message
64
63
  end
@@ -80,7 +79,7 @@ module OpenSession
80
79
 
81
80
  post_validation
82
81
 
83
- rescue OpenError::Error => e
82
+ rescue OpenError::CliError => e
84
83
 
85
84
  puts ""
86
85
  puts "Your command did not complete successfully."
@@ -197,6 +196,7 @@ module OpenSession
197
196
  return if is_pre_init_usecase
198
197
 
199
198
  @ucid_str = self.class.name.do_flatten
199
+ log.info(x) { "Usecase class [self.class.name] converted to => #{@ucid_str}" }
200
200
  @ucid_sym = @ucid_str.gsub(".", "_").to_sym
201
201
 
202
202
  OpenSession::FactFind.instance.instantiate @ucid_str
@@ -204,9 +204,12 @@ module OpenSession
204
204
 
205
205
  @c = OpenSession::FactFind.instance.f
206
206
  @i = OpenSession::FactFind.instance.i
207
- @p = OpenSession::FactFind.instance.f[@ucid_str]
207
+ @p = OpenSession::FactFind.instance.f[@ucid_sym]
208
+
209
+ log.info(x) { "assimilated [#{@p.length}] facts specific to the [#{@ucid_str}] use case." }
208
210
 
209
211
  @time_stamp = OpenSession::Stamp.instance
212
+
210
213
  =begin
211
214
  @eco_id_str = SnapFlat.do self.class.name
212
215
  @eco_id_sym = @eco_id_str.gsub(".", "_").to_sym
@@ -48,22 +48,24 @@ module OpenSecret
48
48
  # So in preparation to execute its modus operandi encryption,
49
49
  # decryption and (if required) transportation use cases opensecret will
50
50
  #
51
+ # - @todo copy the attributes file to the safestore AND delete it.
51
52
  # - +collect the human password+ (twice), verify robustness (and throw away asap)
52
53
  # - +manufacture workstation key+ that will be encrypted b4 it rests on machine
53
54
  # - +create amalgamated human/workstation password+ for locking the private key
54
55
  # - +create a long cryptographically strong symmetric encryption key+
55
56
  # - +encrypt workstation key+ into <tt>.opensecret/<email>/workstation.key.osx.txt</tt>
56
-
57
57
  # - +encrypt workstation encryption key+ with human password and email address
58
- # - then write into <tt>safe</tt> under <tt>machine.password.key.cipher.txt</tt>
58
+ # - then write into the workstation opensecret config file under <tt>machine.key.crypt</tt>
59
+ # - @todo write machine.key.cipher copy the attributes file to the safestore AND delete it.
59
60
  # - +create a super 8,192 bit private/public key pair+
60
- # - use +amalgamated password to encrypt the private key+
61
- # - write to <tt><SAFE>/<email>/master.keys/master.private.key.crypt.txt</tt>
61
+ # - use +amalgamated password with AES 256 RSA to encrypt the private key+
62
+ # - write to <tt><SAFE>/<email>/master.keys/master.private.key.x.os.txt</tt>
63
+ # - use the +amalgamated password with Blowfish to encrypt the public key+
64
+ # - write to <tt><SAFE>/<email>/master.keys/master.public.key.x.os.txt</tt>
65
+
62
66
  # - +create robust salt+ for hashing the path to the (crypted) keys and ciphers
63
67
  # - use +public key to encrypt the salt+
64
68
  # - write crypted salt to <tt><SAFE>/<email>/master.keys/master.path.salt.crypt.txt</tt>
65
- # - now +use the public key to encrypt itself+
66
- # - write public key crypt to <tt><SAFE>/<email>/master.keys/master.public.key.crypt.txt</tt>
67
69
  # - +create the cryptographic keystore+ inside the safe
68
70
  #
69
71
  # Variables should be first zeroed and then deleted immediately after their last use.
@@ -105,32 +107,47 @@ module OpenSecret
105
107
  amalgam_key = Amalgam.passwords human_password, machine_key, @c[:global][:ratio]
106
108
  asymmetric_keys = OpenSSL::PKey::RSA.new @c[:global][:bit_key_size]
107
109
  secured_keytext = asymmetric_keys.export @c[:global][:key_cipher], amalgam_key
108
- public_key_text = asymmetric_keys.public_key.to_pem
109
110
 
110
- machine_key_crypt_key = human_password + "%$os$%" + @email_addr
111
+ machine_key_crypt_key = human_password + @c[:global][:separator_a] + @email_addr
111
112
  blowfish_cipher = OpenSecret::Blowfish.new()
112
- machine_key_crypted = blowfish_cipher.do_encrypt_with_key machine_key, machine_key_crypt_key
113
+ machine_key_x = blowfish_cipher.do_encrypt_with_key machine_key, machine_key_crypt_key
114
+
115
+ OpenSession::Attributes.stash @c[:global][:name], @c[:global][:machine_key_x], machine_key_x
116
+ FileUtils.mkdir_p @c[:global][:master_dirpath]
117
+ File.write @c[:global][:master_prv_key], secured_keytext
118
+
119
+ public_key_text = asymmetric_keys.public_key.to_pem
120
+ public_key_crypt = Blowfish.new.do_encrypt_with_key public_key_text, amalgam_key
121
+ File.write @c[:global][:master_pub_key], public_key_crypt
122
+
123
+ payload_signature = asymmetric_keys.sign( OpenSSL::Digest::SHA256.new, public_key_text )
124
+
125
+ big_crypted_block = Aes256.new.encrypt_it(
126
+ public_key_text,
127
+ public_key_text,
128
+ payload_signature
129
+ )
113
130
 
114
131
  puts ""
115
- puts "public key => #{public_key_text}"
116
- puts "Carry on development in init.rb"
132
+ puts "=============="
133
+ puts "Crypted Block"
134
+ puts "=============="
117
135
  puts ""
118
- puts "Machine Key Plain Text => #{machine_key}"
119
- puts "Machine Key Crypt Key => #{machine_key_crypt_key}"
120
- puts "Machine Key Cipher Text => #{machine_key_crypted}"
136
+ puts "#{big_crypted_block}"
137
+ puts ""
138
+ puts ""
139
+ puts "Carry on development in init.rb"
121
140
  puts ""
122
- exit
123
-
124
141
 
125
- Dir.mkdir @p[:secret_keydir] unless File.exists? @p[:secret_keydir]
126
- File.write @p[:secret_keypath], secured_keytext
127
142
 
143
+ =begin
128
144
  Crypto.print_secret_env_var @p[:env_var_name], machine_key
129
-
130
145
  GitFlow.do_clone_repo @p[:public_gitrepo], @p[:local_gitrepo]
131
146
  FileUtils.mkdir_p @p[:public_keydir]
132
147
  File.write @p[:public_keypath], public_key_text
133
148
  GitFlow.push @p[:local_gitrepo], @p[:public_keyname], @c[:time][:stamp]
149
+ =end
150
+
134
151
 
135
152
  # exit
136
153
  # key4_pem = File.read 'private.secure.pem'
@@ -142,7 +159,6 @@ module OpenSecret
142
159
  # print decrypted_text, "\n"
143
160
 
144
161
 
145
-
146
162
  end
147
163
 
148
164
 
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/ruby
2
+
3
+ module OpenSecret
4
+
5
+ require 'openssl'
6
+
7
+ # The <tt>open use case</tt> allows us to add (put), subtract (del)ete and list
8
+ # the secrets in a file (whether it exists or not). The file can then be locked.
9
+ # Lookout for the reopen command.
10
+ #
11
+ # If the file to open already exists a --with option (giving the master-secret)
12
+ # must be provided.
13
+ #
14
+ # == Observable Value
15
+ #
16
+ # $ opensecret open home/wifi
17
+ #
18
+ # The observable value delivered by +[open]+ boils down to
19
+ #
20
+ # - an openkey (eg asdfx1234) and corresponding open encryption key
21
+ # - open encryption key written to <tt>~/.opensecret/open.keys/asdfx1234.x.txt</tt>
22
+ # - the opened path (ending in filename) written to session.cache base in [safe]
23
+ # - the INI string (were the file to be decrypted) would look like the below
24
+ #
25
+ # [session]
26
+ # base.path = home/wifi
27
+ #
28
+ class Open < OpenSession::UseCase
29
+
30
+ @@context_name = "opensecret"
31
+
32
+ # Execute the <tt>open use case</tt> activities which precedes the ability to
33
+ # to add (put), subtract (del)ete and list the secrets into the opened session file.
34
+ # The file can then be locked (committed and pushed to permanent crypted stores).
35
+ #
36
+ # If the file to open already exists a --with option (giving the master-secret)
37
+ # must be provided.
38
+ #
39
+ # == Observable Value
40
+ #
41
+ # $ opensecret open home/wifi
42
+ #
43
+ # The observable value delivered by +[open]+ boils down to
44
+ #
45
+ # - a provisioned openkey (eg asdfx1234) and corresponding open encryption key
46
+ # - open encryption key written to <tt>~/.opensecret/open.keys/asdfx1234.x.txt</tt>
47
+ # - the opened path (ending in filename) written to session.cache base in [safe]
48
+ # - the INI string (were the file to be decrypted) would look like the below
49
+ #
50
+ # [session]
51
+ # base.path = home/wifi
52
+ #
53
+ # @example
54
+ # home/wifi can be simply populated like this.
55
+ #
56
+ # $ opensecret put bt/ssid g034gdf3455
57
+ # $ opensecret put bt/password HRAsyf324g4DF
58
+ #
59
+ # $ opensecret put virgin.media/ssid 345SDFS
60
+ # $ opensecret put virgin.media/password HlksHRd043NjPO
61
+ #
62
+ # $ opensecret file virgin.media/contract /home/joe/downloads/vm.contract.pdf
63
+ #
64
+ # To encrypt (lock-down) these secrets we simply issue
65
+ #
66
+ # $ opensecret close
67
+ #
68
+ def execute
69
+
70
+ FileUtils.mkdir_p @p[:open_dirpath]
71
+ open_id = Engineer.strong_key @p[:open_idlen]
72
+ open_key = Engineer.strong_key @p[:open_keylen]
73
+
74
+ OpenSession::Attributes.stash @p[:open_name], @p[:open_idname], open_id
75
+ OpenSession::Attributes.stash @p[:open_name], @p[:open_keyname], open_key
76
+ OpenSession::Attributes.stash @p[:open_name], @p[:open_pathname], open_path
77
+
78
+
79
+ exit
80
+
81
+
82
+ human_password = Collect.secret_text(
83
+ @c[:global][:min_passwd_len],
84
+ true,
85
+ @c[:global][:prompt_1],
86
+ @c[:global][:prompt_2]
87
+ )
88
+
89
+ machine_key = Engineer.machine_key human_password.length, @c[:global][:ratio]
90
+ amalgam_key = Amalgam.passwords human_password, machine_key, @c[:global][:ratio]
91
+ asymmetric_keys = OpenSSL::PKey::RSA.new @c[:global][:bit_key_size]
92
+ secured_keytext = asymmetric_keys.export @c[:global][:key_cipher], amalgam_key
93
+
94
+ machine_key_crypt_key = human_password + @c[:global][:separator_a] + @email_addr
95
+ blowfish_cipher = OpenSecret::Blowfish.new()
96
+ machine_key_x = blowfish_cipher.do_encrypt_with_key machine_key, machine_key_crypt_key
97
+
98
+ OpenSession::Attributes.stash @c[:global][:name], @c[:global][:machine_key_x], machine_key_x
99
+ FileUtils.mkdir_p @c[:global][:master_dirpath]
100
+ File.write @c[:global][:master_prv_key], secured_keytext
101
+
102
+ public_key_text = asymmetric_keys.public_key.to_pem
103
+ public_key_crypt = Blowfish.new.do_encrypt_with_key public_key_text, amalgam_key
104
+ File.write @c[:global][:master_pub_key], public_key_crypt
105
+
106
+ payload_signature = asymmetric_keys.sign( OpenSSL::Digest::SHA256.new, public_key_text )
107
+
108
+ big_crypted_block = Aes256.new.encrypt_it(
109
+ public_key_text,
110
+ public_key_text,
111
+ payload_signature
112
+ )
113
+
114
+ puts ""
115
+ puts "=============="
116
+ puts "Crypted Block"
117
+ puts "=============="
118
+ puts ""
119
+ puts "#{big_crypted_block}"
120
+ puts ""
121
+ puts ""
122
+ puts "Carry on development in init.rb"
123
+ puts ""
124
+
125
+
126
+ =begin
127
+ Crypto.print_secret_env_var @p[:env_var_name], machine_key
128
+ GitFlow.do_clone_repo @p[:public_gitrepo], @p[:local_gitrepo]
129
+ FileUtils.mkdir_p @p[:public_keydir]
130
+ File.write @p[:public_keypath], public_key_text
131
+ GitFlow.push @p[:local_gitrepo], @p[:public_keyname], @c[:time][:stamp]
132
+ =end
133
+
134
+
135
+ # exit
136
+ # key4_pem = File.read 'private.secure.pem'
137
+ # pass_phrase = 'superduperpasswordistoBeENTEREDRIGHT1234HereandRightNOW'
138
+ # key4 = OpenSSL::PKey::RSA.new key4_pem, pass_phrase
139
+ # decrypted_text = key4.private_decrypt(Base64.decode64(encrypted_string))
140
+
141
+ # print "\nHey we have done the decryption.\n", "\n"
142
+ # print decrypted_text, "\n"
143
+
144
+
145
+ end
146
+
147
+
148
+ # Perform pre-conditional validations in preparation to executing the main flow
149
+ # of events for this use case. This method may throw the below exceptions.
150
+ #
151
+ # @raise [SafeDirNotConfigured] if the safe's url has not been configured
152
+ # @raise [EmailAddrNotConfigured] if the email address has not been configured
153
+ # @raise [StoreUrlNotConfigured] if the crypt store url is not configured
154
+ def pre_validation
155
+
156
+ @safe_path = OpenSession::Attributes.instance.get_value @@context_name, "safe"
157
+ safe_configured = File.exists?( @safe_path ) && File.directory?( @safe_path )
158
+ @err_msg = "[safe] storage not yet configured. Try =>] opensecret safe /folder/path"
159
+ raise SafeDirNotConfigured.new @err_msg, @safe_path unless safe_configured
160
+
161
+ @email_addr = OpenSession::Attributes.instance.get_value @@context_name, "email"
162
+ email_configured = !@email_addr.nil? && !@email_addr.empty? && @email_addr.length > 4
163
+ @err_msg = "viable [email address] not configured. Try =>] opensecret email joe@example.com"
164
+ raise EmailAddrNotConfigured.new @err_msg, @email_addr unless email_configured
165
+
166
+ @store_url = OpenSession::Attributes.instance.get_value @@context_name, "store"
167
+ store_configured = !@store_url.nil? && !@store_url.empty? && @store_url.length > 0
168
+ @err_msg = "crypt [store url] not configured. Try =>] opensecret store /path/to/crypt"
169
+ raise StoreUrlNotConfigured.new @err_msg, @store_url unless store_configured
170
+
171
+ end
172
+
173
+
174
+ end
175
+
176
+
177
+ end
@@ -0,0 +1,145 @@
1
+
2
+ ==============================================================================================
3
+
4
+ open office/laptop
5
+ (or pull)
6
+
7
+ put login/username=myname
8
+ put login/password=mysecret
9
+ list
10
+ put disk/password=anothersecret
11
+ swap disk/password=bettersecret
12
+
13
+ lock
14
+ (or push)
15
+ ==============================================================================================
16
+
17
+
18
+ open office/laptop --with=asdfasdflkhlkh
19
+ (or pull)
20
+
21
+ list
22
+ get login
23
+ get disk
24
+ trash disk
25
+ list
26
+ get login/password
27
+
28
+ lock
29
+ (or push)
30
+ ==============================================================================================
31
+
32
+
33
+ lock <<path/to/a/file.txt>> ## locks (encrypts) the file in-place | you must delete it
34
+ lock <<path/to/a/folder>> --zip ## zips and encrypts folder (in-place) | you must delete it
35
+
36
+ ==============================================================================================
37
+
38
+ Command => open office/laptop
39
+
40
+ Effect1 => Creates in-memory INI string (see below) and writes (in effect2) to file
41
+ Effect2 => Creates a an openkey eg asdfa234234234sfss and a long password.
42
+ Effect3 => Creates a file ../<<email>>/opened.files/office/laptop.asdfa234234234sfss.x.txt
43
+ Effect4 => Puts long password in $HOME/.opensecret/session.keys/asdfa234234234sfss.x.txt
44
+
45
+ -------------------------------------
46
+ in-memory INI string
47
+ -------------------------------------
48
+ [opensecret]
49
+
50
+ secret.path = office/laptop
51
+ -------------------------------------
52
+
53
+ Assert => no office/laptop exists before opening (if so prompt user to => trash office/laptop
54
+
55
+ ==============================================================================================
56
+
57
+ Command => open office/laptop/login/fullname="Mr Blobby"
58
+
59
+ Effect1 => Creates in-memory INI string (see below) and writes (in effect2) to file
60
+ Effect2 => Creates a file ../<<email>>/opened.files/office/laptop.asdfa234234234sfss.x.txt
61
+ Effect3 => With its encrypt-key in $HOME/.opensecret/session.keys/asdfa234234234sfss.x.txt
62
+
63
+ -------------------------------------
64
+ in-memory INI string
65
+ -------------------------------------
66
+ [opensecret]
67
+
68
+ secret.path = office/laptop
69
+
70
+ [login]
71
+ fullname = Mr Blobby
72
+ -------------------------------------
73
+
74
+ Assert => no office/laptop exists before opening (if so prompt user to => trash office/laptop
75
+
76
+
77
+
78
+ inner_key
79
+ outer_key
80
+ filename
81
+ foldername
82
+ office/room2/rack6/server4/username
83
+
84
+
85
+
86
+
87
+ open
88
+
89
+ get session id as time string
90
+ use
91
+
92
+
93
+
94
+
95
+ close
96
+
97
+
98
+
99
+
100
+
101
+ lock wifi/password
102
+
103
+ [keys]
104
+ wifi = asdff234523
105
+ password = dfgsdfgsfg
106
+
107
+
108
+ asdff234523/dfgsdfgsfg
109
+
110
+ [home]
111
+
112
+ wifi=asdfasd
113
+ alarm=fdghdfg
114
+ safe1=3456hjk3h45
115
+ safe2=2n34lijss
116
+
117
+ ======================================
118
+
119
+ in asdfasd (wifi)
120
+
121
+ [home/wifi]
122
+
123
+ ssid = 3452454
124
+ password = 2452345
125
+
126
+
127
+ office/room2/rack6/server4/username
128
+ office/accounts/sage
129
+ office/alarm/pin
130
+ office/gmail/username
131
+
132
+
133
+ [office]
134
+
135
+ room2 = asddf345
136
+ accounts = 9o8udfg
137
+ alarm = 345ljdfg
138
+ gmail = ldf2345
139
+
140
+
141
+ [office/room2]
142
+
143
+ rack6 = asdf234
144
+
145
+ [office/room2]
@@ -1,3 +1,3 @@
1
1
  module OpenSecret
2
- VERSION = "0.0.946"
2
+ VERSION = "0.0.951"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opensecret
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.946
4
+ version: 0.0.951
5
5
  platform: ruby
6
6
  authors:
7
7
  - Apollo Akora
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-23 00:00:00.000000000 Z
11
+ date: 2018-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inifile
@@ -143,12 +143,14 @@ files:
143
143
  - lib/plugins/usecase.rb
144
144
  - lib/plugins/usecases/init.rb
145
145
  - lib/plugins/usecases/on.rb
146
+ - lib/plugins/usecases/open.rb
146
147
  - lib/plugins/usecases/safe.rb
147
148
  - lib/session/attributes.rb
148
149
  - lib/session/fact.finder.rb
149
150
  - lib/session/require.gem.rb
150
151
  - lib/session/time.stamp.rb
151
152
  - lib/session/user.home.rb
153
+ - lib/using.txt
152
154
  - lib/version.rb
153
155
  - opensecret.gemspec
154
156
  homepage: https://www.eco-platform.co.uk