opensecret 0.0.962 → 0.0.988
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +16 -10
- data/bin/opensecret +3 -4
- data/bin/ops +5 -0
- data/lib/extension/string.rb +114 -0
- data/lib/factbase/facts.opensecret.io.ini +9 -21
- data/lib/interprete/begin.rb +232 -0
- data/lib/interprete/cmd.rb +621 -0
- data/lib/{plugins/usecases/unlock.rb → interprete/export.rb} +25 -70
- data/lib/interprete/init.rb +205 -0
- data/lib/interprete/key.rb +119 -0
- data/lib/interprete/open.rb +148 -0
- data/lib/{plugins/usecases → interprete}/put.rb +19 -6
- data/lib/{plugins/usecases → interprete}/safe.rb +2 -1
- data/lib/{plugins/usecases/lock.rb → interprete/seal.rb} +24 -34
- data/lib/interprete/set.rb +46 -0
- data/lib/interprete/use.rb +43 -0
- data/lib/interpreter.rb +165 -0
- data/lib/keytools/binary.map.rb +245 -0
- data/lib/keytools/digester.rb +245 -0
- data/lib/keytools/doc.conversion.to.ones.and.zeroes.ruby +179 -0
- data/lib/keytools/doc.rsa.radix.binary-mapping.ruby +190 -0
- data/lib/keytools/doc.star.schema.strategy.txt +77 -0
- data/lib/keytools/doc.using.pbkdf2.kdf.ruby +95 -0
- data/lib/keytools/doc.using.pbkdf2.pkcs.ruby +266 -0
- data/lib/keytools/kdf.bcrypt.rb +180 -0
- data/lib/keytools/kdf.pbkdf2.rb +164 -0
- data/lib/keytools/key.data.rb +227 -0
- data/lib/keytools/key.derivation.rb +341 -0
- data/lib/keytools/key.module.rb +140 -0
- data/lib/keytools/key.rb +481 -0
- data/lib/logging/gem.logging.rb +1 -2
- data/lib/modules/cryptology.md +43 -0
- data/lib/{plugins/ciphers → modules/cryptology}/aes-256.rb +6 -0
- data/lib/{crypto → modules/cryptology}/amalgam.rb +6 -0
- data/lib/modules/cryptology/blowfish.rb +130 -0
- data/lib/modules/cryptology/cipher.rb +207 -0
- data/lib/modules/cryptology/collect.rb +118 -0
- data/lib/{plugins → modules/cryptology}/crypt.io.rb +5 -0
- data/lib/{crypto → modules/cryptology}/engineer.rb +7 -1
- data/lib/{crypto → modules/cryptology}/open.bcrypt.rb +0 -0
- data/lib/modules/mappers/collateral.rb +282 -0
- data/lib/modules/mappers/dictionary.rb +288 -0
- data/lib/modules/mappers/envelope.rb +127 -0
- data/lib/modules/mappers/settings.rb +170 -0
- data/lib/modules/storage/coldstore.rb +186 -0
- data/lib/{opensecret/plugins.io/git/git.flow.rb → modules/storage/git.store.rb} +11 -0
- data/lib/notepad/scratch.pad.rb +17 -0
- data/lib/session/fact.finder.rb +13 -0
- data/lib/session/require.gem.rb +5 -0
- data/lib/store-commands.txt +180 -0
- data/lib/version.rb +1 -1
- data/opensecret.gemspec +5 -6
- metadata +74 -29
- data/lib/crypto/blowfish.rb +0 -85
- data/lib/crypto/collect.rb +0 -140
- data/lib/crypto/verify.rb +0 -33
- data/lib/opensecret.rb +0 -236
- data/lib/plugins/cipher.rb +0 -203
- data/lib/plugins/ciphers/blowfish.rb +0 -126
- data/lib/plugins/coldstore.rb +0 -181
- data/lib/plugins/envelope.rb +0 -116
- data/lib/plugins/secrets.uc.rb +0 -94
- data/lib/plugins/usecase.rb +0 -239
- data/lib/plugins/usecases/init.rb +0 -145
- data/lib/plugins/usecases/open.rb +0 -108
- data/lib/session/attributes.rb +0 -279
- data/lib/session/dictionary.rb +0 -191
- data/lib/session/file.path.rb +0 -53
- data/lib/session/session.rb +0 -80
data/lib/logging/gem.logging.rb
CHANGED
@@ -24,13 +24,12 @@ require "session/user.home"
|
|
24
24
|
#
|
25
25
|
module OpenLogger
|
26
26
|
|
27
|
-
@@gem_name = "opensecret"
|
27
|
+
@@gem_name = "opensecret.io"
|
28
28
|
@@gem_base = File.join OpenSession::Home.dir, ".#{@@gem_name}"
|
29
29
|
FileUtils.mkdir_p @@gem_base unless File.exists? @@gem_base
|
30
30
|
@@log_path = File.join @@gem_base, "opensecret-cli-activity.log"
|
31
31
|
|
32
32
|
|
33
|
-
|
34
33
|
# Classes that include (MIXIN) this logging module will
|
35
34
|
# have access to this logger method.
|
36
35
|
#
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
In order for data to be secured for storage or transmission, it must be transformed in such a manner that it would be difficult for an unauthorized individual to be able to discover its true meaning. To do this, certain mathematical equations are used, which are very difficult to solve unless certain strict criteria are met. The level of difficulty of solving a given equation is known as its intractability. These types of equations form the basis of cryptography.
|
4
|
+
|
5
|
+
Some of the most important are:
|
6
|
+
|
7
|
+
== The Discrete Logarithm Problem
|
8
|
+
|
9
|
+
The best way to describe this problem is first to show how its inverse concept works. The following applies to Galois fields (groups). Assume we have a prime number P (a number that is not divisible except by 1 and itself, P). This P is a large prime number of over 300 digits. Let us now assume we have two other integers, a and b. Now say we want to find the value of N, so that value is found by the following formula:
|
10
|
+
|
11
|
+
N = ab mod P, where 0 <= N <= (P · 1)
|
12
|
+
|
13
|
+
(meaning a to the power b mod P)
|
14
|
+
(learn to do powers in markdown)!
|
15
|
+
|
16
|
+
|
17
|
+
This is known as discrete exponentiation and is quite simple to compute. However, the opposite is true when we invert it. If we are given P, a, and N and are required to find b so that the equation is valid, then we face a tremendous level of difficulty.
|
18
|
+
|
19
|
+
|
20
|
+
This problem forms the basis for a number of public key infrastructure algorithms, such as Diffie-Hellman and EIGamal. This problem has been studied for many years and cryptography based on it has withstood many forms of attacks.
|
21
|
+
|
22
|
+
== The Integer Factorization Problem
|
23
|
+
|
24
|
+
This is simple in concept. Say that one takes two prime numbers, P2 and P1, which are both "large" (a relative term, the definition of which continues to move forward as computing power increases). We then multiply these two primes to produce the product, N. The difficulty arises when, being given N, we try and find the original P1 and P2. The Rivest-Shamir-Adleman public key infrastructure encryption protocol is one of many based on this problem. To simplify matters to a great degree, the N product is the public key and the P1 and P2 numbers are, together, the private key.
|
25
|
+
|
26
|
+
This problem is one of the most fundamental of all mathematical concepts. It has been studied intensely for the past 20 years and the consensus seems to be that there is some unproven or undiscovered law of mathematics that forbids any shortcuts. That said, the mere fact that it is being studied intensely leads many others to worry that, somehow, a breakthrough may be discovered.
|
27
|
+
|
28
|
+
== The Elliptic Curve Discrete Logarithm Problem
|
29
|
+
|
30
|
+
This is a new cryptographic protocol based upon a reasonably well-known mathematical problem. The properties of elliptic curves have been well known for centuries, but it is only recently that their application to the field of cryptography has been undertaken.
|
31
|
+
|
32
|
+
First, imagine a huge piece of paper on which is printed a series of vertical and horizontal lines. Each line represents an integer with the vertical lines forming x class components and horizontal lines forming the y class components. The intersection of a horizontal and vertical line gives a set of coordinates (x,y). In the highly simplified example below, we have an elliptic curve that is defined by the equation:
|
33
|
+
|
34
|
+
y2 + y = x3 · x2 (this is way too small for use in a real life application, but it will illustrate the general idea)
|
35
|
+
|
36
|
+
For the above, given a definable operator, we can determine any third point on the curve given any two other points. This definable operator forms a "group" of finite length. To add two points on an elliptic curve, we first need to understand that any straight line that passes through this curve intersects it at precisely three points. Now, say we define two of these points as u and v: we can then draw a straight line through two of these points to find another intersecting point, at w. We can then draw a vertical line through w to find the final intersecting point at x. Now, we can see that u + v = x. This rule works, when we define another imaginary point, the Origin, or O, which exists at (theoretically) extreme points on the curve. As strange as this problem may seem, it does permit for an effective encryption system, but it does have its detractors.
|
37
|
+
|
38
|
+
On the positive side, the problem appears to be quite intractable, requiring a shorter key length (thus allowing for quicker processing time) for equivalent security levels as compared to the Integer Factorization Problem and the Discrete Logarithm Problem. On the negative side, critics contend that this problem, since it has only recently begun to be implemented in cryptography, has not had the intense scrutiny of many years that is required to give it a sufficient level of trust as being secure.
|
39
|
+
|
40
|
+
This leads us to more general problem of cryptology than of the intractability of the various mathematical concepts, which is that the more time, effort, and resources that can be devoted to studying a problem, then the greater the possibility that a solution, or at least a weakness, will be found.
|
41
|
+
|
42
|
+
|
43
|
+
|
@@ -3,6 +3,9 @@
|
|
3
3
|
|
4
4
|
module OpenSecret
|
5
5
|
|
6
|
+
module ToolBelt
|
7
|
+
|
8
|
+
|
6
9
|
# Aes256 is a symmetric encryption cipher which inherits extends the
|
7
10
|
# {OpenSecret::Cipher} base class in order to implement plug and play
|
8
11
|
# symmetric encryption.
|
@@ -145,4 +148,7 @@ module OpenSecret
|
|
145
148
|
end
|
146
149
|
|
147
150
|
|
151
|
+
end
|
152
|
+
|
153
|
+
|
148
154
|
end
|
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
module OpenSecret
|
4
4
|
|
5
|
+
module ToolBelt
|
6
|
+
|
7
|
+
|
5
8
|
# This class knows how to amalgamate passwords, keys and string data in
|
6
9
|
# a manner that is the cryptographical equivalent of synergy.
|
7
10
|
#
|
@@ -61,4 +64,7 @@ module OpenSecret
|
|
61
64
|
end
|
62
65
|
|
63
66
|
|
67
|
+
end
|
68
|
+
|
69
|
+
|
64
70
|
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
module OpenSecret
|
5
|
+
|
6
|
+
module ToolBelt
|
7
|
+
|
8
|
+
# Blowfish is a symmetric encryption cipher which inherits extends the
|
9
|
+
# {OpenSecret::Cipher} base class in order to implement plug and play
|
10
|
+
# symmetric encryption.
|
11
|
+
#
|
12
|
+
# Blowfish is still uncrackable - however its successor (TwoFish) has
|
13
|
+
# been reinforced to counter the growth of super-computer brute force
|
14
|
+
# resources.
|
15
|
+
class Blowfish
|
16
|
+
|
17
|
+
# The blowfish cipher id constant is used to +initialize+
|
18
|
+
# an {OpenSSL::Cipher} class instance.
|
19
|
+
BLOWFISH_CIPHER_ID = "BF-ECB"
|
20
|
+
|
21
|
+
|
22
|
+
# Blowfish constrains the length of +incoming plain text+ forcing it
|
23
|
+
# to be a multiple of eight (8).
|
24
|
+
BLOWFISH_BLOCK_LEN = 8
|
25
|
+
|
26
|
+
|
27
|
+
# Encrypt the (plain) text parameter using the symmetric encryption key
|
28
|
+
# specified in the second parameter and return the base64 encoded
|
29
|
+
# representation of the cipher text.
|
30
|
+
#
|
31
|
+
# Blowfish is a block cipher meaning it needs both the key and the plain
|
32
|
+
# text inputted to conform to a divisible block length.
|
33
|
+
#
|
34
|
+
# Don't worry about this block length requirement as this encrption method
|
35
|
+
# takes care of it and its sister method {self.decryptor} will also perform
|
36
|
+
# the correct reversal activities to give you back the original plain text.
|
37
|
+
#
|
38
|
+
# {Base64.urlsafe_encode64} facilitates the ciphertext encoding returning text that
|
39
|
+
# is safe to write to a file.
|
40
|
+
#
|
41
|
+
# @param plain_text [String]
|
42
|
+
# This parameter should be the non-nil text to encrypt using Blowfish.
|
43
|
+
# Before encryption the text will be padded using a text string from
|
44
|
+
# the {OpenSecret::Cipher::TEXT_PADDER} constant until it results in
|
45
|
+
# a string with the required block length.
|
46
|
+
#
|
47
|
+
# @param encryption_key [String]
|
48
|
+
# send a long strong unencoded key which does not have to be a multiple of
|
49
|
+
# eight even though the algorithm demands it. Before the encryption this key
|
50
|
+
# will be passed through a digest using behaviour from {Digest::SHA256.digest}
|
51
|
+
#
|
52
|
+
# This behaviour returns a key whose length is a multiple of eight.
|
53
|
+
#
|
54
|
+
# @return [String] base64 representation of blowfish crypted ciphertext
|
55
|
+
#
|
56
|
+
# @raise [OpenSSL::Cipher::CipherError]
|
57
|
+
# An (encryption) <tt>key length too short</tt> error is raised for short keys.
|
58
|
+
def self.encryptor plain_text, encryption_key
|
59
|
+
|
60
|
+
shortkey_msg = "The #{encryption_key.length} character encryption key is too short."
|
61
|
+
raise ArgumentError, shortkey_msg unless encryption_key.length > 8
|
62
|
+
log.info(x) { "os blowfish request to encrypt plain text with provided key." }
|
63
|
+
|
64
|
+
block_txt = plain_text
|
65
|
+
block_txt += CryptIO::TEXT_PADDER until block_txt.bytesize % BLOWFISH_BLOCK_LEN == 0
|
66
|
+
raw_stretched_key = Digest::SHA256.digest(encryption_key)
|
67
|
+
|
68
|
+
blowfish_encryptor = OpenSSL::Cipher.new(BLOWFISH_CIPHER_ID).encrypt
|
69
|
+
blowfish_encryptor.key = raw_stretched_key
|
70
|
+
return blowfish_encryptor.update(block_txt) << blowfish_encryptor.final
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
# Decrypt the cipher text parameter using the symmetric decryption key
|
76
|
+
# specified in the second parameter. The cipher text is expected to have
|
77
|
+
# already been decoded if necessary.
|
78
|
+
#
|
79
|
+
# Its okay to use a bespoke encryptor - just ensure you encode the result
|
80
|
+
# and override the padding constant.
|
81
|
+
#
|
82
|
+
# Blowfish is a block cipher meaning it needs both the key and the plain
|
83
|
+
# text inputted to conform to a divisible block length.
|
84
|
+
#
|
85
|
+
# Don't worry about this block length requirement as this decrption method
|
86
|
+
# takes care of the reversing the activities carried out by {self.encryptor}.
|
87
|
+
#
|
88
|
+
# @param cipher_text [String]
|
89
|
+
# This incoming cipher text should already be encoded but it
|
90
|
+
# will <b>chomped and stripped upon receipt</b> followed by
|
91
|
+
# decryption using the Blowfish algorithm.
|
92
|
+
#
|
93
|
+
# @param decryption_key [String]
|
94
|
+
# Send the same key that was used during the encryption phase. The encryption
|
95
|
+
# phase passed the key through the {Digest::SHA256.digest} digest so here
|
96
|
+
# the decryption does the exact same thing.
|
97
|
+
#
|
98
|
+
# The digest processing guarantees a symmetric key whose length conforms to
|
99
|
+
# the multiple of eight block length requirement.
|
100
|
+
#
|
101
|
+
# @return [String]
|
102
|
+
# After decoding and decryption the plain text string will still be padded,
|
103
|
+
# +but not with spaces+. The unlikely to occur padding string constant used
|
104
|
+
# is the {OpenSecret::Cipher::TEXT_PADDER}.
|
105
|
+
#
|
106
|
+
# If the plaintext ended with spaces these would be preserved. After padder
|
107
|
+
# removal any trailing spaces will be preserved in the returned plain text.
|
108
|
+
#
|
109
|
+
def self.decryptor cipher_text, decryption_key
|
110
|
+
|
111
|
+
digested_key = Digest::SHA256.digest decryption_key
|
112
|
+
|
113
|
+
decrypt_tool = OpenSSL::Cipher.new(BLOWFISH_CIPHER_ID).decrypt
|
114
|
+
decrypt_tool.key = digested_key
|
115
|
+
|
116
|
+
padded_plaintxt = decrypt_tool.update(cipher_text) << decrypt_tool.final
|
117
|
+
pad_begin_index = padded_plaintxt.index CryptIO::TEXT_PADDER
|
118
|
+
return padded_plaintxt if pad_begin_index.nil?
|
119
|
+
return padded_plaintxt[ 0 .. (pad_begin_index-1) ]
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
module OpenSecret
|
5
|
+
|
6
|
+
module ToolBelt
|
7
|
+
|
8
|
+
require "base64"
|
9
|
+
|
10
|
+
# {OpenSecret::Cipher} is a base class that enables cipher varieties
|
11
|
+
# to be plugged and played with minimal effort. This Cipher implements much
|
12
|
+
# of the use case functionality - all extension classes need to do, is
|
13
|
+
# to subclass and implement only the core behaviour that define its identity.
|
14
|
+
#
|
15
|
+
# == Double Encryption | Cipher Parent vs Cipher Child
|
16
|
+
#
|
17
|
+
# Double encryption first with a symmetric and then an asymmetric one fulfills
|
18
|
+
# the +opensecret+ promise of making the stored ciphertext utterly worthless.
|
19
|
+
#
|
20
|
+
# The child ciphers implement the inner symmetric encyption whilst the parent
|
21
|
+
# implements the outer asymmetric encryption algorithm.
|
22
|
+
#
|
23
|
+
# The process is done twice resulting in two stores that are mirrored in structure.
|
24
|
+
# The front end store holds doubly encrypted keys whist the backend store holds
|
25
|
+
# the doubly encrypted secrets.
|
26
|
+
#
|
27
|
+
# Attackers wouldn't be able to distinguish one from the other. Even if they
|
28
|
+
# theoretically cracked the asymmetric encryption - they would then be faced
|
29
|
+
# with a powerful symmetric encryption algorithm which could be any one of the
|
30
|
+
# leading ciphers such as TwoFish or the Advanced Encryption Standard (AES).
|
31
|
+
#
|
32
|
+
# == Ciphers at 3 Levels
|
33
|
+
#
|
34
|
+
# Ciphers are implemented at three distinct levels.
|
35
|
+
#
|
36
|
+
# <b>Low Level Ciphers</b>
|
37
|
+
#
|
38
|
+
# Low level ciphers are given text to encrypt and an instantiated dictionary
|
39
|
+
# in which to place the encryption parameters such as keys and initialization
|
40
|
+
# vectors (iv)s.
|
41
|
+
#
|
42
|
+
# Some more specific ciphers can handle authorization data for example the
|
43
|
+
# Galois Counter Mode (GCM) cipher.
|
44
|
+
#
|
45
|
+
# Low level ciphers know nothing about text IO nor reading and writing to
|
46
|
+
# persistence structures like files, queues and databases.
|
47
|
+
#
|
48
|
+
# <b>Mid Level Ciphers</b>
|
49
|
+
#
|
50
|
+
# Mid level ciphers talk to the low level ciphers and bring in input and output
|
51
|
+
# textual formats like OpenSecret's two-part block structures.
|
52
|
+
#
|
53
|
+
# Mid level ciphers still know nothing of persistence structures like files,
|
54
|
+
# queues and databases.
|
55
|
+
#
|
56
|
+
# <b>Use Case Level Ciphers</b>
|
57
|
+
#
|
58
|
+
# The ciphers operating at the use case level talk to mid level ciphers. They
|
59
|
+
# interact with the <b>opensecret store API</b> which brings persistence
|
60
|
+
# functions such as <b>read/write</b> as well as remoting functions such as
|
61
|
+
# <b>push/pull</b>.
|
62
|
+
#
|
63
|
+
# Use Case level ciphers interact with the latest crypt technologies due to
|
64
|
+
# interface separation. Also they talk classes implementing persistence stores
|
65
|
+
# allowing assets liek Git, S3, DropBox, simple files, SSH filesystems, Samba
|
66
|
+
# to hold locked key and material crypts.
|
67
|
+
#
|
68
|
+
# Databases stores will be introduced soon allowing opensecret to plug in and
|
69
|
+
# exploit database managers like Mongo, Hadoop, MySQL, Maria, and PostgreSQL.
|
70
|
+
#
|
71
|
+
# Plugging into DevOps orchestration platforms like Terraform, Ansible, Chef
|
72
|
+
# and Puppet will soon be available. Add this with integrations to other credential
|
73
|
+
# managers like HashiCorp's Vault, Credstash, Amazon KMS, Git Secrets, PGP,
|
74
|
+
# LastPass, KeePass and KeePassX.
|
75
|
+
#
|
76
|
+
# == How to Implement a Cipher
|
77
|
+
#
|
78
|
+
# Extend this base class to inherit lots of +unexciting+ functionality
|
79
|
+
# that essentially
|
80
|
+
#
|
81
|
+
# - manages the main encryption and decryption use case flow
|
82
|
+
# - +concatenates+ the symmetric encryption meta data with ciphertext +after encryption+
|
83
|
+
# - _splits_ and objectifies the key/value metadata plus ciphertext +before decryption+
|
84
|
+
# - +handles file read/writes+ in conjunction with the store plugins
|
85
|
+
# - handles +exceptions+ and +malicious input detection+ and incubation
|
86
|
+
# - +_performs the asymmetric encryption_+ of the cipher's symmetrically encrypted output
|
87
|
+
#
|
88
|
+
# == What Behaviour Must Ciphers Implement
|
89
|
+
#
|
90
|
+
# Ciphers bring the cryptographic mathematics and implementation algorithms
|
91
|
+
# to the table. So when at home they must implement
|
92
|
+
#
|
93
|
+
# - <tt>do_symmetric_encryption(plain_text)</tt> - resulting in ciphertext
|
94
|
+
# - <tt>do_symmetric_decryption(ciphertext, encryption_dictionary)</tt> » plaintext
|
95
|
+
#
|
96
|
+
# and also set the <tt>@dictionary</tt> hash (map) of pertinent
|
97
|
+
# key/value pairs including the encryption algorithm, the encryption key and
|
98
|
+
# the ciphertext signature to thwart any at-rest tampering.
|
99
|
+
#
|
100
|
+
# That's It. Cipher children can rely on the {OpenSecret::Cipher} parent to
|
101
|
+
# do the nitty gritty of file-handling plus managing stores and paths.
|
102
|
+
class Cipher
|
103
|
+
|
104
|
+
# Ciphers use <b>symmetric algorithms</b> to encrypt the given text, which
|
105
|
+
# is then wrapped up along with the encryption key and other <b>metadata</b>
|
106
|
+
# pertinent to the algorithm, they then encrypt this bundle with the
|
107
|
+
# <b>public key</b> provided and return the text that can safely be stored in
|
108
|
+
# a text file.
|
109
|
+
#
|
110
|
+
# Ciphers should never interact with the filesystem which makes them
|
111
|
+
# reusable in API and remote store scenarios.
|
112
|
+
#
|
113
|
+
# Binary files should be converted into the base64 format before being
|
114
|
+
# presented to ciphers.
|
115
|
+
#
|
116
|
+
# Every component in the pipeline bears the responsibility for nullifying
|
117
|
+
# and rejecting malicious content.
|
118
|
+
#
|
119
|
+
# @param public_key [OpenSSL::PKey::RSA]
|
120
|
+
# an {OpenSSL::PKey::RSA} public key. The unique selling point of
|
121
|
+
# asymmetric encryption is it can be done without recourse to the heavily
|
122
|
+
# protected private key. Thus the encryption process can continue with
|
123
|
+
# just a public key as long as its authenticity is assured.
|
124
|
+
#
|
125
|
+
# @param payload_text [String]
|
126
|
+
# plaintext (or base64 encoded) text to encrypt
|
127
|
+
#
|
128
|
+
# @return [String] doubly (symmetric and asymmetric) encrypted cipher text
|
129
|
+
def self.encrypt_it public_key, payload_text
|
130
|
+
|
131
|
+
crypt_data = {}
|
132
|
+
crypted_payload = Base64.encode64( Aes256.do_encrypt( crypt_data, payload_text ) )
|
133
|
+
unified_material = CryptIO.inner_crypt_serialize crypt_data, crypted_payload
|
134
|
+
|
135
|
+
outer_crypt_key = Engineer.strong_key( 128 )
|
136
|
+
crypted_cryptkey = Base64.encode64( public_key.public_encrypt( outer_crypt_key ) )
|
137
|
+
|
138
|
+
crypted_material = Base64.encode64(Blowfish.encryptor unified_material, outer_crypt_key)
|
139
|
+
|
140
|
+
return CryptIO.outer_crypt_serialize( crypted_cryptkey, crypted_material )
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
# This method takes and <b><em>opensecret formatted</em></b> cipher-text block
|
146
|
+
# generated by {self.encrypt_it} and returns the original message that has effectively
|
147
|
+
# been doubly encrypted using a symmetric and asymmetric cipher. This type of
|
148
|
+
# encryption is standard best practice when serializing secrets.
|
149
|
+
#
|
150
|
+
# opensecret cipher-text blocks <b><em>look like a two(2) part bundle</em></b>
|
151
|
+
# but they are <b><em>actually a three(3) part bundle</em></b> because the second
|
152
|
+
# part is in itself an amalgam of two distinct objects, serialized as text blocks.
|
153
|
+
#
|
154
|
+
# <b>The 3 OpenSecret Blocks</b>
|
155
|
+
#
|
156
|
+
# Even though the incoming text <b><em>appears to contain two (2) blocks</em></b>,
|
157
|
+
# it <b><em>actually contains three (3)</em></b>.
|
158
|
+
#
|
159
|
+
# - a massive symmetric encryption key (locked by an asymmetric keypair)
|
160
|
+
# - a dictionary denoting the algorithm and parameters used to encrypt the 3rd block
|
161
|
+
# - the true message whose encryption is parametized by the dictionary (in 2nd block)
|
162
|
+
#
|
163
|
+
# The second and third block are only revealed by asymmetrically decrypting
|
164
|
+
# the key in the first block and using it to symmetrically decrypt what appears
|
165
|
+
# to be a unified second block.
|
166
|
+
#
|
167
|
+
# @param private_key [OpenSSL::PKey::RSA]
|
168
|
+
# the <b>asymmetric private key</b> whose corresponding public key was
|
169
|
+
# employed during the encryption of a super-strong 128 character symmetric
|
170
|
+
# key embalmed by the first ciphertext block.
|
171
|
+
#
|
172
|
+
# @param os_block_text [String]
|
173
|
+
# the locked cipher text is the opensecret formatted block which comes
|
174
|
+
# in two main chunks. First is the <b>long strong</b> symmetric encryption
|
175
|
+
# key crypted with the public key portion of the private key in the first
|
176
|
+
# parameter.
|
177
|
+
#
|
178
|
+
# The second chunk is the symmetrically crypted text that was locked with
|
179
|
+
# the encryption key revealed in the first chunk.
|
180
|
+
#
|
181
|
+
# @return [String]
|
182
|
+
# the doubly encrypted plain text that is locked by a symmetric key and
|
183
|
+
# that symmetric key itself locked using the public key portion of the
|
184
|
+
# private key whose crypted form is presented in the first parameter.
|
185
|
+
def self.decrypt_it private_key, os_block_text
|
186
|
+
|
187
|
+
first_block = Base64.decode64( CryptIO.outer_crypt_deserialize os_block_text, true )
|
188
|
+
trail_block = Base64.decode64( CryptIO.outer_crypt_deserialize os_block_text, false )
|
189
|
+
|
190
|
+
decrypt_key = private_key.private_decrypt first_block
|
191
|
+
inner_block = Blowfish.decryptor( trail_block, decrypt_key )
|
192
|
+
|
193
|
+
crypt_props = Hash.new
|
194
|
+
cipher_text = CryptIO.inner_crypt_deserialize( crypt_props, inner_block )
|
195
|
+
|
196
|
+
return Aes256.do_decrypt( crypt_props, cipher_text )
|
197
|
+
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
module OpenSecret
|
4
|
+
|
5
|
+
module ToolBelt
|
6
|
+
|
7
|
+
require 'io/console'
|
8
|
+
|
9
|
+
# This class will be refactored into an interface implemented by a set
|
10
|
+
# of plugins that will capture sensitive information from users from an
|
11
|
+
# Ubuntu, Windows, RHEL, CoreOS, iOS or CentOS command line interface.
|
12
|
+
#
|
13
|
+
# An equivalent REST API will also be available for bringing in sensitive
|
14
|
+
# information in the most secure (but simple) manner.
|
15
|
+
class Collect
|
16
|
+
|
17
|
+
|
18
|
+
# <tt>Collect something sensitive from the command line</tt> with a
|
19
|
+
# minimum length specified in the first parameter. This method can't
|
20
|
+
# know whether the information is a password, a pin number or whatever
|
21
|
+
# so it takes the integer minimum size at its word.
|
22
|
+
#
|
23
|
+
# @param min_size [Integer] the minimum size of the collected secret
|
24
|
+
# whereby one (1) is the least we can expect. The maximum bound is
|
25
|
+
# not constrained here so will fall under what is allowed by the
|
26
|
+
# interface, be it a CLI, Rest API, Web UI or Mobile App.
|
27
|
+
#
|
28
|
+
# @param prompt_twice [Boolean] indicate whether the user should be
|
29
|
+
# prompted twice. If true the prompt_2 text must be provided and
|
30
|
+
# converse is also true. A true value asserts that both times the
|
31
|
+
# user enters the same (case sensitive) string.
|
32
|
+
#
|
33
|
+
# @param prompt_1 [String] the text (aide memoire) used to prompt the user
|
34
|
+
#
|
35
|
+
# @param prompt_2 [String] if the prompt twice boolean is TRUE, this
|
36
|
+
# second prompt (aide memoire) must be provided.
|
37
|
+
#
|
38
|
+
# @return [String] the collected string text ( watch out for non-ascii chars)
|
39
|
+
# @raise [ArgumentError] if the minimum size is less than one
|
40
|
+
def self.secret_text min_size, prompt_twice, prompt_1, prompt_2=nil
|
41
|
+
|
42
|
+
assert_min_size min_size
|
43
|
+
|
44
|
+
sleep(1)
|
45
|
+
puts "\n#{prompt_1} : "
|
46
|
+
first_secret = STDIN.noecho(&:gets).chomp
|
47
|
+
|
48
|
+
assert_input_text_size first_secret.length, min_size
|
49
|
+
return first_secret unless prompt_twice
|
50
|
+
|
51
|
+
sleep(1)
|
52
|
+
puts "\n#{prompt_2} : "
|
53
|
+
check_secret = STDIN.noecho(&:gets).chomp
|
54
|
+
|
55
|
+
assert_same_size_text first_secret, check_secret
|
56
|
+
|
57
|
+
return first_secret
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
# --
|
63
|
+
# -- Raise an exception if asked to collect text that is less
|
64
|
+
# -- than 3 characters in length.
|
65
|
+
# --
|
66
|
+
def self.assert_min_size min_size
|
67
|
+
|
68
|
+
min_length_msg = "\n\nCrypts with 2 (or less) characters open up exploitable holes.\n\n"
|
69
|
+
raise ArgumentError.new min_length_msg if min_size < 3
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
# --
|
75
|
+
# -- Output an error message and then exit if the entered input
|
76
|
+
# -- text size does not meet the minimum requirements.
|
77
|
+
# --
|
78
|
+
def self.assert_input_text_size input_size, min_size
|
79
|
+
|
80
|
+
if( input_size < min_size )
|
81
|
+
|
82
|
+
puts
|
83
|
+
puts "Input is too short. Please enter at least #{min_size} characters."
|
84
|
+
puts
|
85
|
+
|
86
|
+
exit
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
# --
|
94
|
+
# -- Assert that the text entered the second time is exactly (case sensitive)
|
95
|
+
# -- the same as the text entered the first time.
|
96
|
+
# --
|
97
|
+
def self.assert_same_size_text first_text, second_text
|
98
|
+
|
99
|
+
unless( first_text.eql? second_text )
|
100
|
+
|
101
|
+
puts
|
102
|
+
puts "Those two bits of text are not the same (in my book)!"
|
103
|
+
puts
|
104
|
+
|
105
|
+
exit
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
end
|