opensecret 0.0.962 → 0.0.988

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +16 -10
  3. data/bin/opensecret +3 -4
  4. data/bin/ops +5 -0
  5. data/lib/extension/string.rb +114 -0
  6. data/lib/factbase/facts.opensecret.io.ini +9 -21
  7. data/lib/interprete/begin.rb +232 -0
  8. data/lib/interprete/cmd.rb +621 -0
  9. data/lib/{plugins/usecases/unlock.rb → interprete/export.rb} +25 -70
  10. data/lib/interprete/init.rb +205 -0
  11. data/lib/interprete/key.rb +119 -0
  12. data/lib/interprete/open.rb +148 -0
  13. data/lib/{plugins/usecases → interprete}/put.rb +19 -6
  14. data/lib/{plugins/usecases → interprete}/safe.rb +2 -1
  15. data/lib/{plugins/usecases/lock.rb → interprete/seal.rb} +24 -34
  16. data/lib/interprete/set.rb +46 -0
  17. data/lib/interprete/use.rb +43 -0
  18. data/lib/interpreter.rb +165 -0
  19. data/lib/keytools/binary.map.rb +245 -0
  20. data/lib/keytools/digester.rb +245 -0
  21. data/lib/keytools/doc.conversion.to.ones.and.zeroes.ruby +179 -0
  22. data/lib/keytools/doc.rsa.radix.binary-mapping.ruby +190 -0
  23. data/lib/keytools/doc.star.schema.strategy.txt +77 -0
  24. data/lib/keytools/doc.using.pbkdf2.kdf.ruby +95 -0
  25. data/lib/keytools/doc.using.pbkdf2.pkcs.ruby +266 -0
  26. data/lib/keytools/kdf.bcrypt.rb +180 -0
  27. data/lib/keytools/kdf.pbkdf2.rb +164 -0
  28. data/lib/keytools/key.data.rb +227 -0
  29. data/lib/keytools/key.derivation.rb +341 -0
  30. data/lib/keytools/key.module.rb +140 -0
  31. data/lib/keytools/key.rb +481 -0
  32. data/lib/logging/gem.logging.rb +1 -2
  33. data/lib/modules/cryptology.md +43 -0
  34. data/lib/{plugins/ciphers → modules/cryptology}/aes-256.rb +6 -0
  35. data/lib/{crypto → modules/cryptology}/amalgam.rb +6 -0
  36. data/lib/modules/cryptology/blowfish.rb +130 -0
  37. data/lib/modules/cryptology/cipher.rb +207 -0
  38. data/lib/modules/cryptology/collect.rb +118 -0
  39. data/lib/{plugins → modules/cryptology}/crypt.io.rb +5 -0
  40. data/lib/{crypto → modules/cryptology}/engineer.rb +7 -1
  41. data/lib/{crypto → modules/cryptology}/open.bcrypt.rb +0 -0
  42. data/lib/modules/mappers/collateral.rb +282 -0
  43. data/lib/modules/mappers/dictionary.rb +288 -0
  44. data/lib/modules/mappers/envelope.rb +127 -0
  45. data/lib/modules/mappers/settings.rb +170 -0
  46. data/lib/modules/storage/coldstore.rb +186 -0
  47. data/lib/{opensecret/plugins.io/git/git.flow.rb → modules/storage/git.store.rb} +11 -0
  48. data/lib/notepad/scratch.pad.rb +17 -0
  49. data/lib/session/fact.finder.rb +13 -0
  50. data/lib/session/require.gem.rb +5 -0
  51. data/lib/store-commands.txt +180 -0
  52. data/lib/version.rb +1 -1
  53. data/opensecret.gemspec +5 -6
  54. metadata +74 -29
  55. data/lib/crypto/blowfish.rb +0 -85
  56. data/lib/crypto/collect.rb +0 -140
  57. data/lib/crypto/verify.rb +0 -33
  58. data/lib/opensecret.rb +0 -236
  59. data/lib/plugins/cipher.rb +0 -203
  60. data/lib/plugins/ciphers/blowfish.rb +0 -126
  61. data/lib/plugins/coldstore.rb +0 -181
  62. data/lib/plugins/envelope.rb +0 -116
  63. data/lib/plugins/secrets.uc.rb +0 -94
  64. data/lib/plugins/usecase.rb +0 -239
  65. data/lib/plugins/usecases/init.rb +0 -145
  66. data/lib/plugins/usecases/open.rb +0 -108
  67. data/lib/session/attributes.rb +0 -279
  68. data/lib/session/dictionary.rb +0 -191
  69. data/lib/session/file.path.rb +0 -53
  70. data/lib/session/session.rb +0 -80
@@ -1,126 +0,0 @@
1
- #!/usr/bin/ruby
2
- # coding: utf-8
3
-
4
- module OpenSecret
5
-
6
- # Blowfish is a symmetric encryption cipher which inherits extends the
7
- # {OpenSecret::Cipher} base class in order to implement plug and play
8
- # symmetric encryption.
9
- #
10
- # Blowfish is still uncrackable - however its successor (TwoFish) has
11
- # been reinforced to counter the growth of super-computer brute force
12
- # resources.
13
- class Blowfish < OpenSecret::Cipher
14
-
15
-
16
- # The blowfish cipher id constant is used to +initialize+
17
- # an {OpenSSL::Cipher} class instance.
18
- BLOWFISH_CIPHER_ID = "BF-ECB"
19
-
20
-
21
- # Blowfish constrains the length of +incoming plain text+ forcing it
22
- # to be a multiple of eight (8).
23
- BLOWFISH_BLOCK_LEN = 8
24
-
25
-
26
- # Encrypt the (plain) text parameter using the symmetric encryption key
27
- # specified in the second parameter and return the base64 encoded
28
- # representation of the cipher text.
29
- #
30
- # Blowfish is a block cipher meaning it needs both the key and the plain
31
- # text inputted to conform to a divisible block length.
32
- #
33
- # Don't worry about this block length requirement as this encrption method
34
- # takes care of it and its sister method {self.decryptor} will also perform
35
- # the correct reversal activities to give you back the original plain text.
36
- #
37
- # {Base64.urlsafe_encode64} facilitates the ciphertext encoding returning text that
38
- # is safe to write to a file.
39
- #
40
- # @param plain_text [String]
41
- # This parameter should be the non-nil text to encrypt using Blowfish.
42
- # Before encryption the text will be padded using a text string from
43
- # the {OpenSecret::Cipher::TEXT_PADDER} constant until it results in
44
- # a string with the required block length.
45
- #
46
- # @param encryption_key [String]
47
- # send a long strong unencoded key which does not have to be a multiple of
48
- # eight even though the algorithm demands it. Before the encryption this key
49
- # will be passed through a digest using behaviour from {Digest::SHA256.digest}
50
- #
51
- # This behaviour returns a key whose length is a multiple of eight.
52
- #
53
- # @return [String] base64 representation of blowfish crypted ciphertext
54
- #
55
- # @raise [OpenSSL::Cipher::CipherError]
56
- # An (encryption) <tt>key length too short</tt> error is raised for short keys.
57
- def encryptor plain_text, encryption_key
58
-
59
- shortkey_msg = "The #{encryption_key.length} character encryption key is too short."
60
- raise ArgumentError, shortkey_msg unless encryption_key.length > 8
61
- log.info(x) { "os blowfish request to encrypt plain text with provided key." }
62
-
63
- block_txt = plain_text
64
- block_txt += CryptIO::TEXT_PADDER until block_txt.bytesize % OpenSecret::Blowfish::BLOWFISH_BLOCK_LEN == 0
65
- raw_stretched_key = Digest::SHA256.digest(encryption_key)
66
-
67
- blowfish_encryptor = OpenSSL::Cipher.new(OpenSecret::Blowfish::BLOWFISH_CIPHER_ID).encrypt
68
- blowfish_encryptor.key = raw_stretched_key
69
- return blowfish_encryptor.update(block_txt) << blowfish_encryptor.final
70
-
71
- end
72
-
73
-
74
- # Decrypt the cipher text parameter using the symmetric decryption key
75
- # specified in the second parameter. The cipher text is expected to have
76
- # already been decoded if necessary.
77
- #
78
- # Its okay to use a bespoke encryptor - just ensure you encode the result
79
- # and override the padding constant.
80
- #
81
- # Blowfish is a block cipher meaning it needs both the key and the plain
82
- # text inputted to conform to a divisible block length.
83
- #
84
- # Don't worry about this block length requirement as this decrption method
85
- # takes care of the reversing the activities carried out by {self.encryptor}.
86
- #
87
- # @param cipher_text [String]
88
- # This incoming cipher text should already be encoded but it
89
- # will <b>chomped and stripped upon receipt</b> followed by
90
- # decryption using the Blowfish algorithm.
91
- #
92
- # @param decryption_key [String]
93
- # Send the same key that was used during the encryption phase. The encryption
94
- # phase passed the key through the {Digest::SHA256.digest} digest so here
95
- # the decryption does the exact same thing.
96
- #
97
- # The digest processing guarantees a symmetric key whose length conforms to
98
- # the multiple of eight block length requirement.
99
- #
100
- # @return [String]
101
- # After decoding and decryption the plain text string will still be padded,
102
- # +but not with spaces+. The unlikely to occur padding string constant used
103
- # is the {OpenSecret::Cipher::TEXT_PADDER}.
104
- #
105
- # If the plaintext ended with spaces these would be preserved. After padder
106
- # removal any trailing spaces will be preserved in the returned plain text.
107
- #
108
- def decryptor cipher_text, decryption_key
109
-
110
- digested_key = Digest::SHA256.digest decryption_key
111
-
112
- decrypt_tool = OpenSSL::Cipher.new(OpenSecret::Blowfish::BLOWFISH_CIPHER_ID).decrypt
113
- decrypt_tool.key = digested_key
114
-
115
- padded_plaintxt = decrypt_tool.update(cipher_text) << decrypt_tool.final
116
- pad_begin_index = padded_plaintxt.index CryptIO::TEXT_PADDER
117
- return padded_plaintxt if pad_begin_index.nil?
118
- return padded_plaintxt[ 0 .. (pad_begin_index-1) ]
119
-
120
- end
121
-
122
-
123
- end
124
-
125
-
126
- end
@@ -1,181 +0,0 @@
1
- #!/usr/bin/ruby
2
- # coding: utf-8
3
-
4
- module OpenSecret
5
-
6
- # Cold storage can sync repositories with a <b>bias during conflicts</b>
7
- # either to the <em>remote repository</em> <b>when pulling</b>, and then
8
- # conversely to the <em>local reposiory</em> <b>when pushing</b>.
9
- #
10
- # In between the sync operations a ColdStore can create, read, update and
11
- # delete to and from the local mirror.
12
- #
13
- # == ColdStore | Use Cases
14
- #
15
- # Any <b>self-respecting coldstore</b> must, after initialization, provide
16
- # some basic (and mandatory) behaviour.
17
- #
18
- # These include
19
- #
20
- # - <b>read</b> - reading text from a (possibly unavailable) frozen path
21
- # - <b>write</b> - writing text (effectively freezing it) to a path
22
- # - <b>pull</b> - sync with a <b>collision bias</b> that favours the remote mirror
23
- # - <b>push</b> - sync with a <b>collision bias</b> that favours the local mirror
24
- #
25
- # <b>Cold Storage</b> is borrowed from BitCoin and represents offline storage
26
- # for keys and crypts. opensecret separates keys and crypts so that you can
27
- # transfer and share secrets by moving keys (not the crypts).
28
- #
29
- # == Houses and Gold Bullion
30
- #
31
- # You don't carry houses or gold bullion around to rent, share or transfer
32
- # their ownership.
33
- #
34
- # You copy keys to rent secrets and when the tenure is up (or you change your
35
- # mind) you revoke access with a metaphorical lock change.
36
- #
37
- # opensecret embodies concepts like an owner who rents as opposed to a change
38
- # in ownership.
39
- #
40
- # == trade secrets | commoditizing secrets
41
- #
42
- # opensecret is a conduit through which secrets can be bought and sold.
43
- #
44
- # It commoditizes secrets so that they can be owned, traded, leased and
45
- # auctioned. Options to acquire or relinquish them at set prices can easily
46
- # be taken out.
47
- class ColdStore
48
-
49
- # @param base_path [String]
50
- # path to the store's (mirror) base directory.
51
- # If the denoted directory does not exist an attempt will be made to
52
- # create it. If a file exists at this path an error will be thrown.
53
- #
54
- # @param domain [String]
55
- # the domain is an identifier (and namespace) denoting which opensecret
56
- # "account" is being accessed. opensecret allows the creation and use of
57
- # multiple domains.
58
- def initialize local_path
59
-
60
- @store_path = local_path
61
- FileUtils.mkdir_p @store_path
62
-
63
- end
64
-
65
-
66
- # Read the file frozen (in this store mirror) at this path and
67
- # return its contents.
68
- #
69
- # Coldstores are usually frozen offline (offmachine) so for this
70
- # to work the {ColdStore.pull} behaviour must have executed to
71
- # create a local store mirror. This method reads from that mirror.
72
- #
73
- # @param from_path [String]
74
- # read the file frozen at this path and return its contents
75
- # so that the defreeze process can begin.
76
- #
77
- # This path is relative to the base of the store defined in
78
- # the constructor.
79
- #
80
- # @return [String]
81
- # return the text frozen in a file at the denoted local path
82
- #
83
- # nil is reurned if no file can be found in the local mirror
84
- # at the configured path
85
- #
86
- # @raise [RuntimeError]
87
- # unless the path exists in this coldstore and that path is
88
- # a directory (as opposed to a file).
89
- #
90
- # @raise [ArgumentError]
91
- # if more than one file match is made at the path specified.
92
- def read from_path
93
-
94
- frozen_filepath = File.join @store_path, from_path
95
- frozen_dir_path = File.dirname(frozen_filepath)
96
-
97
- log.info(x) { "Coldstore will search in folder [#{frozen_dir_path.hr_path}]" }
98
-
99
- exists_msg = "Directory #{frozen_dir_path} does not exist in store."
100
- is_dir_msg = "Path #{frozen_dir_path} should be a directory (not a file)."
101
- raise RuntimeError, exists_msg unless File.exists? frozen_dir_path
102
- raise RuntimeError, is_dir_msg unless File.directory? frozen_dir_path
103
-
104
- full_filepath = ""
105
- file_matched = false
106
-
107
- Dir.glob("#{frozen_dir_path}/**/*.os.txt").each do |matched_path|
108
-
109
- log.info(x) { "Coldstore search with [#{from_path}] has matched [#{matched_path.hr_path}]" }
110
- log.info(x) { "Ignore directory at [#{matched_path.hr_path}]." } if File.directory? matched_path
111
- next if File.directory? matched_path
112
-
113
- two_match_msg = "More than one file matched. The second is #{matched_path}."
114
- raise ArgumentError, two_match_msg if file_matched
115
- file_matched = true
116
-
117
- full_filepath = matched_path
118
-
119
- end
120
-
121
- no_file_msg = "Coldstore could not find path [#{from_path}] from [#{@store_path}]."
122
- raise RuntimeError, no_file_msg unless file_matched
123
-
124
- log.info(x) { "Coldstore matched exactly one envelope at [#{full_filepath.hr_path}]." }
125
- return File.read full_filepath
126
-
127
- end
128
-
129
-
130
- # Write (freeze) the text into a file at the denoted path. The
131
- # folder path will be created if need be.
132
- #
133
- # Coldstores are usually frozen offline (offmachine) so after
134
- # this method completes the {ColdStore.push} behaviour must be
135
- # executed to synchronize the local coldstore freezer with the
136
- # remote mirror.
137
- #
138
- # @param this_text [String]
139
- # this is the text that needs to be frozen into the local and
140
- # subsequently the remote coldstore freezer.
141
- #
142
- # @param to_path [String]
143
- # write the text (effectively freezing it) into the file at
144
- # this path. An attempt will be made to put down the necessary
145
- # directory structure.
146
- #
147
- # This path is relative to the base of the store defined in
148
- # the constructor.
149
- def write this_text, to_path
150
-
151
- freeze_filepath = File.join @store_path, to_path
152
-
153
- log.info(x) { "ColdStore freezing #{this_text.length} characters of worthless text."}
154
- log.info(x) { "ColdStore freeze file path => #{freeze_filepath.hr_path}"}
155
-
156
- FileUtils.mkdir_p(File.dirname(freeze_filepath))
157
- File.write freeze_filepath, this_text
158
-
159
- end
160
-
161
-
162
- private
163
-
164
- # @todo - write sync (with a local bias during conflicts)
165
- # The open up to the public (published) api.
166
- def push
167
-
168
-
169
- end
170
-
171
- # @todo - write sync (with a rmote bias during conflicts)
172
- # The open up to the public (published) api.
173
- def pull
174
-
175
- end
176
-
177
-
178
- end
179
-
180
-
181
- end
@@ -1,116 +0,0 @@
1
- #!/usr/bin/ruby
2
- # coding: utf-8
3
-
4
- module OpenSecret
5
-
6
- require 'json'
7
-
8
- # An envelope knows how to manipulate a JSON backed data structure
9
- # (put, add etc) <b>after reading and then decrypting it</b> from a
10
- # file and <b>before encrypting and then writing it</b> to a file.
11
- #
12
- # It provides behaviour to which we can create, append (add), update
13
- # (change), read parts and delete essentially two structures
14
- #
15
- # - a collection of name/value pairs
16
- # - an ordered list of values
17
- #
18
- # == JSON is Not Exposed in the Interface
19
- #
20
- # An envelope doesn't expose the data format used in the implementation
21
- # allowing this to be changed seamlessly to YAMl or other formats.
22
- #
23
- # == Symmetric Encryption and Decryption
24
- #
25
- # An envelope supports operations to <b>read from</b> and <b>write to</b>
26
- # a known filepath and with a symmetric key it can
27
- #
28
- # - decrypt <b>after reading from</b> a file and
29
- # - encrypt <b>before writing to</b> a (the same) file
30
- #
31
- # == Hashes as the Primary Data Structure
32
- #
33
- # Envelope extends {Hash} as the core data structure for holding
34
- #
35
- # - strings
36
- # - arrays
37
- # - other hashes
38
- # - booleans
39
- # - integers and floats
40
- class Envelope < Hash
41
-
42
-
43
- # Read and inject into this envelope, the data structure found in a
44
- # file at the path specified in the first parameter.
45
- #
46
- # Symmetric cryptography is mandatory for the envelope so we must
47
- # <b>encrypt before writing</b> and <b>decrypt after reading</b>.
48
- #
49
- # An argument error will result if a suitable key is not provided.
50
- #
51
- # If the file does not exist (denoting the first read) all this method
52
- # does is to stash the filepath as an instance variable and igore the
53
- # decryption key which can be nil (or ommitted).
54
- #
55
- # @param the_filepath [String]
56
- # absolute path to the file which acts as the persistent mirror to
57
- # this data structure envelope.
58
- #
59
- # @param decryption_key [String]
60
- # encryption at rest is a given so this mandatory parameter must
61
- # contain a robust symmetric decryption key. The key will be used
62
- # for decryption after the read and it will not linger (ie not cached
63
- # as an instance variable).
64
- #
65
- # @raise [ArgumentError] if the decryption key is not robust enough.
66
- def read the_filepath, decryption_key = nil
67
-
68
- @filepath = the_filepath
69
- return unless File.exists? @filepath
70
-
71
- cipher_text = Base64.decode64( File.read( @filepath ).strip )
72
- plain_text = OpenSecret::Blowfish.new.decryptor( cipher_text, decryption_key )
73
-
74
- puts ""
75
- puts "=== ============================"
76
- puts "=== Envelope After Decryption"
77
- puts "=== ============================"
78
- puts plain_text
79
- puts "=== ============================"
80
- puts ""
81
-
82
- data_structure = JSON.parse plain_text
83
- self.merge! data_structure
84
-
85
- end
86
-
87
-
88
- # Write the data in this envelope hash map into a file-system
89
- # backed mirror whose path was specified in the {self.read} method.
90
- #
91
- # Technology for encryption at rest is supported by this dictionary
92
- # and to this aim, please endeavour to post a robust symmetric
93
- # encryption key.
94
- #
95
- # Calling this {self.write} method when the file at the prescribed path
96
- # does not exist results in the directory structure being created
97
- # (if necessary) and then the encrypted file being written.
98
- #
99
- # @param encryption_key [String]
100
- # encryption at rest is a given so this mandatory parameter must
101
- # contain a robust symmetric encryption key. The symmetric key will
102
- # be used for the decryption after the read. Note that the decryption
103
- # key does not linger meaning it isn't cached in an instance variable.
104
- def write encryption_key
105
-
106
- FileUtils.mkdir_p(File.dirname(@filepath))
107
- cipher_text = Base64.encode64 Blowfish.new.encryptor( self.to_json, encryption_key )
108
- File.write @filepath, cipher_text
109
-
110
- end
111
-
112
-
113
- end
114
-
115
-
116
- end
@@ -1,94 +0,0 @@
1
- #!/usr/bin/ruby
2
- # coding: utf-8
3
-
4
- module OpenSecret
5
-
6
- # The parent OpenSecret use case is designed to be extended by the cli
7
- # (command line) use cases like {OpenSecret::Open}, {OpenSecret::Put} and
8
- # {OpenSecret::Lock} because it describes behaviour common to at least two
9
- # (but usually more) of the use cases.
10
- #
11
- # == Behaviour Reuse
12
- #
13
- # Behaviour that is reusable by any use case fits within {OpenSession::UseCase}
14
- # whilst behaviour common to two or more {OpenSecret} use cases belongs here.
15
- # Therefore the only behaviour in the named use case is detailed and must belong
16
- # solely to it.
17
- #
18
- # == Fact Reuse
19
- #
20
- # Fact evaluation rules are born with reuse in mind. <b>Use case facts</b>
21
- # are evaluated when they belong to either the class or its parent or any
22
- # ancestors.
23
- class SecretsUseCase < OpenSession::UseCase
24
-
25
- @@context_name = "opensecret"
26
-
27
- # Get the envelope that <b>was opened</b> by the open command but
28
- # <b>not locked</b> with the lock command.
29
- #
30
- # @return [Envelope]
31
- # return the Envelope that has been opened. The state carried alongside an
32
- # open envelope is an id, an encryption key and a filepath all inside the
33
- # userhome configuration file.
34
- def get_envelope
35
-
36
- encrypt_key = OpenSession::Attributes.instance.get_value @@context_name, @c[:open][:open_name], @c[:open][:open_keyname]
37
- rel_filepath = OpenSession::Attributes.instance.get_value @@context_name, @c[:open][:open_name], @c[:open][:open_pathname]
38
-
39
- put_filepath = File.join @c[:open][:open_dirpath], rel_filepath
40
- the_envelope = Envelope.new
41
- the_envelope.read put_filepath, encrypt_key
42
- return the_envelope
43
-
44
- end
45
-
46
-
47
- # Unlock the {OpenSSL::PKey::RSA} private key and return the cipher object
48
- # usable for asymmetric encryption, decryption, signing and signature
49
- # verification use cases.
50
- #
51
- # The returned private key can be used to generate its twin public key
52
- # and should be used to verify the same public key as (if) and when the
53
- # need arises.
54
- #
55
- # @param locked_private_key [String]
56
- # the locked up private key ciphertext
57
- #
58
- # @param unlock_key [String]
59
- # the symmetric encryption key that can be used to unlock the private
60
- # key ciphertext in the first parameter.
61
- #
62
- # @return [OpenSSL::PKey::RSA]
63
- # return the {OpenSSL::PKey::RSA} private key that will be
64
- # usable for asymmetric encryption, decryption, signing and signature
65
- # verification.
66
- def unlock_private_key locked_private_key, unlock_key
67
- return OpenSSL::PKey::RSA.new locked_private_key, unlock_key
68
- end
69
-
70
-
71
- # Return the {OpenSSL::PKey::RSA} private key which is a cipher object
72
- # usable for asymmetric encryption, decryption, signing and signature
73
- # verification use cases.
74
- #
75
- # The returned private key can be used to generate its twin public key
76
- # and should be used to verify the same public key as (if) and when the
77
- # need arises.
78
- #
79
- # @param private_key_text [String]
80
- # the private key plain text
81
- #
82
- # @return [OpenSSL::PKey::RSA]
83
- # return the {OpenSSL::PKey::RSA} private key that will be
84
- # usable for asymmetric encryption, decryption, signing and signature
85
- # verification.
86
- def to_private_key private_key_text
87
- return OpenSSL::PKey::RSA.new private_key_text
88
- end
89
-
90
-
91
- end
92
-
93
-
94
- end