safedb 0.01.0001
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.yardopts +3 -0
- data/Gemfile +10 -0
- data/LICENSE +21 -0
- data/README.md +793 -0
- data/Rakefile +16 -0
- data/bin/safe +5 -0
- data/lib/configs/README.md +58 -0
- data/lib/extension/array.rb +162 -0
- data/lib/extension/dir.rb +35 -0
- data/lib/extension/file.rb +123 -0
- data/lib/extension/hash.rb +33 -0
- data/lib/extension/string.rb +572 -0
- data/lib/factbase/facts.safedb.net.ini +38 -0
- data/lib/interprete.rb +462 -0
- data/lib/keytools/PRODUCE_RAND_SEQ_USING_DEV_URANDOM.txt +0 -0
- data/lib/keytools/kdf.api.rb +243 -0
- data/lib/keytools/kdf.bcrypt.rb +265 -0
- data/lib/keytools/kdf.pbkdf2.rb +262 -0
- data/lib/keytools/kdf.scrypt.rb +190 -0
- data/lib/keytools/key.64.rb +326 -0
- data/lib/keytools/key.algo.rb +109 -0
- data/lib/keytools/key.api.rb +1391 -0
- data/lib/keytools/key.db.rb +330 -0
- data/lib/keytools/key.docs.rb +195 -0
- data/lib/keytools/key.error.rb +110 -0
- data/lib/keytools/key.id.rb +271 -0
- data/lib/keytools/key.ident.rb +243 -0
- data/lib/keytools/key.iv.rb +107 -0
- data/lib/keytools/key.local.rb +259 -0
- data/lib/keytools/key.now.rb +402 -0
- data/lib/keytools/key.pair.rb +259 -0
- data/lib/keytools/key.pass.rb +120 -0
- data/lib/keytools/key.rb +585 -0
- data/lib/logging/gem.logging.rb +132 -0
- data/lib/modules/README.md +43 -0
- data/lib/modules/cryptology/aes-256.rb +154 -0
- data/lib/modules/cryptology/amalgam.rb +70 -0
- data/lib/modules/cryptology/blowfish.rb +130 -0
- data/lib/modules/cryptology/cipher.rb +207 -0
- data/lib/modules/cryptology/collect.rb +138 -0
- data/lib/modules/cryptology/crypt.io.rb +225 -0
- data/lib/modules/cryptology/engineer.rb +99 -0
- data/lib/modules/mappers/dictionary.rb +288 -0
- data/lib/modules/storage/coldstore.rb +186 -0
- data/lib/modules/storage/git.store.rb +399 -0
- data/lib/session/fact.finder.rb +334 -0
- data/lib/session/require.gem.rb +112 -0
- data/lib/session/time.stamp.rb +340 -0
- data/lib/session/user.home.rb +49 -0
- data/lib/usecase/cmd.rb +487 -0
- data/lib/usecase/config/README.md +57 -0
- data/lib/usecase/docker/README.md +146 -0
- data/lib/usecase/docker/docker.rb +49 -0
- data/lib/usecase/edit/README.md +43 -0
- data/lib/usecase/edit/delete.rb +46 -0
- data/lib/usecase/export.rb +40 -0
- data/lib/usecase/files/README.md +37 -0
- data/lib/usecase/files/eject.rb +56 -0
- data/lib/usecase/files/file_me.rb +78 -0
- data/lib/usecase/files/read.rb +169 -0
- data/lib/usecase/files/write.rb +89 -0
- data/lib/usecase/goto.rb +57 -0
- data/lib/usecase/id.rb +36 -0
- data/lib/usecase/import.rb +157 -0
- data/lib/usecase/init.rb +63 -0
- data/lib/usecase/jenkins/README.md +146 -0
- data/lib/usecase/jenkins/jenkins.rb +208 -0
- data/lib/usecase/login.rb +71 -0
- data/lib/usecase/logout.rb +28 -0
- data/lib/usecase/open.rb +71 -0
- data/lib/usecase/print.rb +40 -0
- data/lib/usecase/put.rb +81 -0
- data/lib/usecase/set.rb +44 -0
- data/lib/usecase/show.rb +138 -0
- data/lib/usecase/terraform/README.md +91 -0
- data/lib/usecase/terraform/terraform.rb +121 -0
- data/lib/usecase/token.rb +35 -0
- data/lib/usecase/update/README.md +55 -0
- data/lib/usecase/update/rename.rb +180 -0
- data/lib/usecase/use.rb +41 -0
- data/lib/usecase/verse.rb +20 -0
- data/lib/usecase/view.rb +71 -0
- data/lib/usecase/vpn/README.md +150 -0
- data/lib/usecase/vpn/vpn.ini +31 -0
- data/lib/usecase/vpn/vpn.rb +54 -0
- data/lib/version.rb +3 -0
- data/safedb.gemspec +34 -0
- metadata +193 -0
@@ -0,0 +1,132 @@
|
|
1
|
+
require "logger"
|
2
|
+
require "session/user.home"
|
3
|
+
|
4
|
+
# [MIXIN] magic is deployed to hand out DevOps quality logging
|
5
|
+
# features to any class that includes the logging module.
|
6
|
+
#
|
7
|
+
# When logging facilities are not ready we need to log just to
|
8
|
+
# stdout but when they are we need to use them.
|
9
|
+
#
|
10
|
+
# mixin power enables one class to give the logfile path and all
|
11
|
+
# classes will suddenly retrieve a another logger and use that.
|
12
|
+
#
|
13
|
+
# include Logging
|
14
|
+
# def doImportant
|
15
|
+
# log.warn(x) "unhappy about doing this"
|
16
|
+
# do_anyway
|
17
|
+
# log.debug(x) "all good it was okay"
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# So what are Mixins?
|
21
|
+
#
|
22
|
+
# Refer to the below link for excellent coverage of mixins.
|
23
|
+
# @see http://ruby-doc.com/docs/ProgrammingRuby/html/tut_modules.html
|
24
|
+
#
|
25
|
+
module OpenLogger
|
26
|
+
|
27
|
+
@@gem_name = "safedb.net"
|
28
|
+
@@gem_base = File.join OpenSession::Home.dir, ".#{@@gem_name}"
|
29
|
+
FileUtils.mkdir_p @@gem_base unless File.exists? @@gem_base
|
30
|
+
@@log_path = File.join @@gem_base, "safedb.net-cli.log"
|
31
|
+
|
32
|
+
|
33
|
+
# Classes that include (MIXIN) this logging module will
|
34
|
+
# have access to this logger method.
|
35
|
+
#
|
36
|
+
# [memoization] is implemented here for performance and
|
37
|
+
# will only initiate a logger under 2 circumstances
|
38
|
+
#
|
39
|
+
# [1] - the first call (returns a STDOUT logger)
|
40
|
+
# [2] - the call after the logfile path is set
|
41
|
+
# (returns a more sophisticated logger)
|
42
|
+
def log
|
43
|
+
|
44
|
+
@@log_class ||= get_logger
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
# This Ruby behavioural snippet allows the logger to print 3 crucial
|
50
|
+
# pieces of information for the troubleshooter (detective) so that they
|
51
|
+
# may ascertain
|
52
|
+
#
|
53
|
+
# - the [module] the logging call came from
|
54
|
+
# - the [method] the logging call came from
|
55
|
+
# - line number origins of the logging call
|
56
|
+
#
|
57
|
+
# To use this method you can make calls like this
|
58
|
+
#
|
59
|
+
# - log.info(x) { "Log many things about where I am now." }
|
60
|
+
# - log.warn(x) { "Log many things about where I am now." }
|
61
|
+
#
|
62
|
+
def x
|
63
|
+
|
64
|
+
module_name = File.basename caller_locations(1,1).first.absolute_path, ".rb"
|
65
|
+
method_name = caller_locations(1,1).first.base_label
|
66
|
+
line_number = caller_locations(1,1).first.lineno
|
67
|
+
|
68
|
+
"#{module_name} | #{method_name} | (line #{line_number}) "
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
# This method returns an initialized logger.
|
74
|
+
#
|
75
|
+
# The logger returned may write to
|
76
|
+
#
|
77
|
+
# - a simple file
|
78
|
+
# - a service like fluentd
|
79
|
+
# - a message queue
|
80
|
+
# - a nosql database
|
81
|
+
# - all of the above
|
82
|
+
#
|
83
|
+
# Not that [memoization] should be used so that this method
|
84
|
+
# gets called ideally just once although in practise it may
|
85
|
+
# turn out to be a handful of times.
|
86
|
+
#
|
87
|
+
# @return [Logger] return an initialized logger object
|
88
|
+
def get_logger
|
89
|
+
|
90
|
+
file_logger = Logger.new @@log_path
|
91
|
+
original_formatter = Logger::Formatter.new
|
92
|
+
|
93
|
+
file_logger.formatter = proc { |severity, datetime, progname, msg|
|
94
|
+
original_formatter.call( severity, datetime, progname, msg.dump.chomp.strip )
|
95
|
+
}
|
96
|
+
|
97
|
+
return file_logger
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
# Overtly long file paths in the log files sometimes hamper readability
|
103
|
+
# and this method improves the situation by returning just the two
|
104
|
+
# immediate ancestors of the file (or folder) path.
|
105
|
+
#
|
106
|
+
# @example A really long input like
|
107
|
+
# <tt>/home/joe/project/degrees/math/2020</tt>
|
108
|
+
# is reduced to
|
109
|
+
# <tt>degrees/math/2020</tt>
|
110
|
+
#
|
111
|
+
# So this method returns the name of the grandparent folder then parent folder
|
112
|
+
# and then the most significant file (or folder) name.
|
113
|
+
#
|
114
|
+
# When this is not possible due to the filepath being colisively near the
|
115
|
+
# filesystem's root, it returns the parameter name.
|
116
|
+
#
|
117
|
+
# @param object_path [String] overtly long path that will be made more readable
|
118
|
+
# @return [String] the (separated) three most significant path name segments
|
119
|
+
def nickname object_path
|
120
|
+
|
121
|
+
object_name = File.basename object_path
|
122
|
+
parent_folder = File.dirname object_path
|
123
|
+
parent_name = File.basename parent_folder
|
124
|
+
granny_folder = File.dirname parent_folder
|
125
|
+
granny_name = File.basename granny_folder
|
126
|
+
|
127
|
+
return [granny_name,parent_name,object_name].join("/")
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
end
|
@@ -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
|
+
|
@@ -0,0 +1,154 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
module SafeDb
|
5
|
+
|
6
|
+
module ToolBelt
|
7
|
+
|
8
|
+
|
9
|
+
# Aes256 is a symmetric encryption cipher which inherits extends the
|
10
|
+
# {SafeDb::Cipher} base class in order to implement plug and play
|
11
|
+
# symmetric encryption.
|
12
|
+
#
|
13
|
+
# == Aes256 Symmetric Encrypt/Decrypt
|
14
|
+
#
|
15
|
+
# To facilitate decryption - this cipher produces a key/value pair
|
16
|
+
# dictionary which will be stored along with the ciphertext itself.
|
17
|
+
# The dictionary includes
|
18
|
+
#
|
19
|
+
# - <b>symmetric.cipher</b> - the algorithm used to encrypt and decrypt
|
20
|
+
# - <b>encryption.key</b> - hex encoded key for encrypting and decrypting
|
21
|
+
# - <b>initialize.vector</b> - the initialization vector known as a IV (four)
|
22
|
+
#
|
23
|
+
# == Aes256 Implemented Methods
|
24
|
+
#
|
25
|
+
# This cipher brings the cryptographic mathematics and implementation algorithms
|
26
|
+
# for the 256Bit Advanced Encryption Standard. No serious practical (nor theoretical)
|
27
|
+
# challenge has ever been mounted against this algorithm (or this implementation).
|
28
|
+
#
|
29
|
+
# This class implements the below methods
|
30
|
+
#
|
31
|
+
# - <b>do_symmetric_encryption(plain_text)</b> - resulting in ciphertext
|
32
|
+
# - <b>do_symmetric_decryption(ciphertext, encryption_dictionary)</b> » plaintext
|
33
|
+
#
|
34
|
+
# and it also sets the <b>@dictionary</b> hash (map) of pertinent
|
35
|
+
# key/value pairs including the +encryption algorithm+ and +encryption key+.
|
36
|
+
#
|
37
|
+
# That's It. Cipher children can rely on the {SafeDb::Cipher} parent to
|
38
|
+
# do the nitty gritty of file-handling plus managing stores and paths.
|
39
|
+
|
40
|
+
class Aes256
|
41
|
+
|
42
|
+
# Use the AES 256 bit block cipher and a robust strong random key plus
|
43
|
+
# initialization vector (IV) to symmetrically encrypt the plain text.
|
44
|
+
#
|
45
|
+
# <b>Cryptographic Properties</b>
|
46
|
+
#
|
47
|
+
# This encrypt event populates key/value pairs to the hash (dictionary) instance
|
48
|
+
# given in the parameter.
|
49
|
+
#
|
50
|
+
# A crypt properties dictionary acts as <b>output from every encryption event</b>
|
51
|
+
# and <b>input to every decryption event</b>. The most common properties include
|
52
|
+
#
|
53
|
+
# - the symmetric key used for the encryption and decryption
|
54
|
+
# - the iv (initialization vector) that adds another dimension of strength
|
55
|
+
# - authorization data that thwarts switch attacks by tying context to content
|
56
|
+
# - the cipher algorithm, its implementation and its encryption strength
|
57
|
+
# - the digest of the original message for validation purposes
|
58
|
+
#
|
59
|
+
# @param e_properties [Hash]
|
60
|
+
# instantiated hash map in which the encrryption properties will
|
61
|
+
# be stuffed.
|
62
|
+
#
|
63
|
+
# @param plain_text [String] the plain (or base64 encoded) text to encrypt
|
64
|
+
# @return [String] the symmetrically encrypted cipher text
|
65
|
+
def self.do_encrypt e_properties, plain_text
|
66
|
+
|
67
|
+
crypt_cipher = OpenSSL::Cipher::AES256.new(:CBC)
|
68
|
+
crypt_cipher.encrypt
|
69
|
+
plain_text_digest = Digest::SHA256.digest plain_text
|
70
|
+
|
71
|
+
e_properties[CryptIO::DICT_CIPHER_NAME] = crypt_cipher.class.name
|
72
|
+
e_properties[CryptIO::DICT_CRYPT_KEY] = Base64.urlsafe_encode64 crypt_cipher.random_key
|
73
|
+
e_properties[CryptIO::DICT_CRYPT_IV] = Base64.urlsafe_encode64 crypt_cipher.random_iv
|
74
|
+
e_properties[CryptIO::DICT_TEXT_DIGEST] = Base64.urlsafe_encode64 plain_text_digest
|
75
|
+
|
76
|
+
return crypt_cipher.update( plain_text ) + crypt_cipher.final
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
# Use the AES 256 bit block cipher together with the encryption key,
|
82
|
+
# initialization vector (iv) and other data found within the decryption
|
83
|
+
# properties dictionary to symmetrically decrypt the cipher text.
|
84
|
+
#
|
85
|
+
# This encrypt event in {self.do_encrypt} populated the property dictionary
|
86
|
+
# that was presumably serialized, stored, retrieved then deserialized and
|
87
|
+
# (at last) presented in the first parameter.
|
88
|
+
#
|
89
|
+
# <b>Cryptographic Properties</b>
|
90
|
+
#
|
91
|
+
# A crypt properties dictionary is the <b>output from every encryption event</b>
|
92
|
+
# and <b>input to every decryption event</b>. The most common properties include
|
93
|
+
#
|
94
|
+
# - the symmetric key used for the encryption and decryption
|
95
|
+
# - the iv (initialization vector) that adds another dimension of strength
|
96
|
+
# - authorization data that thwarts switch attacks by tying context to content
|
97
|
+
# - the cipher algorithm, its implementation and its encryption strength
|
98
|
+
# - the digest of the original message for validation purposes
|
99
|
+
#
|
100
|
+
# @param d_properties [Hash]
|
101
|
+
# the crypt properties dictionary is the <b>output from every encryption event</b>
|
102
|
+
# and (as in this case) <b>input to every decryption event</b>.
|
103
|
+
#
|
104
|
+
# @param cipher_text [String]
|
105
|
+
# the (already decoded) cipher text for decryption by this method using the
|
106
|
+
# encryption properties setup during the past encrypt event.
|
107
|
+
#
|
108
|
+
# @return [String]
|
109
|
+
# the plain text message originally given to be encrypted. If the message digest
|
110
|
+
# is provided within the decryption properties dictionary a sanity check will
|
111
|
+
# occur.
|
112
|
+
#
|
113
|
+
# @raise [RuntimeError]
|
114
|
+
# if decryption fails or the recalculated message digest fails an equivalence test.
|
115
|
+
def self.do_decrypt d_properties, cipher_text
|
116
|
+
|
117
|
+
decode_cipher = OpenSSL::Cipher::AES256.new(:CBC)
|
118
|
+
decode_cipher.decrypt
|
119
|
+
|
120
|
+
decode_cipher.key = Base64.urlsafe_decode64( d_properties[CryptIO::DICT_CRYPT_KEY] )
|
121
|
+
decode_cipher.iv = Base64.urlsafe_decode64( d_properties[CryptIO::DICT_CRYPT_IV] )
|
122
|
+
|
123
|
+
plain_text = decode_cipher.update( cipher_text ) + decode_cipher.final
|
124
|
+
assert_digest_equivalence( d_properties[CryptIO::DICT_TEXT_DIGEST], plain_text )
|
125
|
+
|
126
|
+
return plain_text
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
|
134
|
+
def self.assert_digest_equivalence( digest_b4_encryption, plain_text_message )
|
135
|
+
|
136
|
+
plain_text_digest = Base64.urlsafe_encode64( Digest::SHA256.digest( plain_text_message ) )
|
137
|
+
return if digest_b4_encryption.eql? plain_text_digest
|
138
|
+
|
139
|
+
msg1 = "\nEquivalence check of original and decrypted plain text digests failed.\n"
|
140
|
+
msg2 = "Digest before encryption => #{digest_b4_encryption}\n"
|
141
|
+
msg3 = "Digest after decryption => #{plain_text_digest}\n"
|
142
|
+
error_message = msg1 + msg2 + msg3
|
143
|
+
raise RuntimeError, error_message
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
module SafeDb
|
4
|
+
|
5
|
+
module ToolBelt
|
6
|
+
|
7
|
+
|
8
|
+
# This class knows how to amalgamate passwords, keys and string data in
|
9
|
+
# a manner that is the cryptographical equivalent of synergy.
|
10
|
+
#
|
11
|
+
# The amalgamated keys are synergially (cryptographically) greater than
|
12
|
+
# the sum of their parts.
|
13
|
+
class Amalgam
|
14
|
+
|
15
|
+
# Amalgamate the two parameter passwords in a manner that is the
|
16
|
+
# cryptographical equivalent of synergy. The amalgamated keys are
|
17
|
+
# synergially greater than the sum of their parts.
|
18
|
+
#
|
19
|
+
# -- Get a viable machine password taking into account the human
|
20
|
+
# -- password length and the specified mix_ratio.
|
21
|
+
#
|
22
|
+
#
|
23
|
+
# @param human_password [String] the password originating from a human
|
24
|
+
# @param machine_key [String] a machine engineered ascii password (key)
|
25
|
+
# @mixparam machine_key [String] a machine engineered ascii password (key)
|
26
|
+
#
|
27
|
+
# @return [String] the union of the two parameter passwords
|
28
|
+
#
|
29
|
+
# @raise [ArgumentError] when the size of the two passwords and the
|
30
|
+
# mix ratio do not conform to the constraint imposed by the below
|
31
|
+
# equation which must hold true.
|
32
|
+
# <tt>machine password length = human password length * mix_ratio - 1</tt>
|
33
|
+
#
|
34
|
+
def self.passwords human_password, machine_password, mix_ratio
|
35
|
+
|
36
|
+
size_error_msg = "Human pass length times mix_ratio must equal machine pass length."
|
37
|
+
lengths_are_perfect = human_password.length * mix_ratio == machine_password.length
|
38
|
+
raise ArgumentError.new size_error_msg unless lengths_are_perfect
|
39
|
+
|
40
|
+
machine_passwd_chunk = 0
|
41
|
+
amalgam_passwd_index = 0
|
42
|
+
amalgamated_password = ""
|
43
|
+
|
44
|
+
human_password.each_char do |passwd_char|
|
45
|
+
|
46
|
+
amalgamated_password[amalgam_passwd_index] = passwd_char
|
47
|
+
amalgam_passwd_index += 1
|
48
|
+
|
49
|
+
for i in 0..(mix_ratio-1) do
|
50
|
+
machine_pass_index = machine_passwd_chunk * mix_ratio + i
|
51
|
+
amalgamated_password[amalgam_passwd_index] = machine_password[machine_pass_index]
|
52
|
+
amalgam_passwd_index += 1
|
53
|
+
end
|
54
|
+
|
55
|
+
machine_passwd_chunk += 1
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
return amalgamated_password
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
module SafeDb
|
5
|
+
|
6
|
+
module ToolBelt
|
7
|
+
|
8
|
+
# Blowfish is a symmetric encryption cipher which inherits extends the
|
9
|
+
# {SafeDb::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 {SafeDb::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 {SafeDb::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
|