opensecret 0.0.988 → 0.0.9925
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 +5 -5
- data/README.md +56 -159
- data/bin/opensecret +2 -2
- data/bin/ops +17 -2
- data/lib/extension/string.rb +14 -16
- data/lib/{interpreter.rb → interprete.rb} +53 -29
- data/lib/keytools/binary.map.rb +49 -0
- data/lib/keytools/kdf.api.rb +249 -0
- data/lib/keytools/kdf.bcrypt.rb +64 -29
- data/lib/keytools/kdf.pbkdf2.rb +92 -83
- 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 +1281 -0
- data/lib/keytools/key.db.rb +265 -0
- data/lib/keytools/{key.module.rb → key.docs.rb} +55 -0
- data/lib/keytools/key.error.rb +110 -0
- data/lib/keytools/key.id.rb +271 -0
- data/lib/keytools/key.iv.rb +107 -0
- data/lib/keytools/key.local.rb +265 -0
- data/lib/keytools/key.mach.rb +248 -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 +428 -298
- data/lib/keytools/keydebug.txt +295 -0
- data/lib/logging/gem.logging.rb +3 -3
- data/lib/modules/cryptology/collect.rb +20 -0
- data/lib/session/require.gem.rb +1 -1
- data/lib/usecase/cmd.rb +417 -0
- data/lib/usecase/id.rb +36 -0
- data/lib/usecase/import.rb +174 -0
- data/lib/usecase/init.rb +78 -0
- data/lib/usecase/login.rb +70 -0
- data/lib/usecase/logout.rb +30 -0
- data/lib/usecase/open.rb +126 -0
- data/lib/{interprete → usecase}/put.rb +100 -47
- data/lib/usecase/read.rb +89 -0
- data/lib/{interprete → usecase}/safe.rb +0 -0
- data/lib/{interprete → usecase}/set.rb +0 -0
- data/lib/usecase/token.rb +111 -0
- data/lib/{interprete → usecase}/use.rb +0 -0
- data/lib/version.rb +1 -1
- data/opensecret.gemspec +4 -3
- metadata +39 -33
- data/lib/exception/cli.error.rb +0 -53
- data/lib/exception/errors/cli.errors.rb +0 -31
- data/lib/interprete/begin.rb +0 -232
- data/lib/interprete/cmd.rb +0 -621
- data/lib/interprete/export.rb +0 -163
- data/lib/interprete/init.rb +0 -205
- data/lib/interprete/key.rb +0 -119
- data/lib/interprete/open.rb +0 -148
- data/lib/interprete/seal.rb +0 -129
- data/lib/keytools/digester.rb +0 -245
- data/lib/keytools/key.data.rb +0 -227
- data/lib/keytools/key.derivation.rb +0 -341
- data/lib/modules/mappers/collateral.rb +0 -282
- data/lib/modules/mappers/envelope.rb +0 -127
- data/lib/modules/mappers/settings.rb +0 -170
- data/lib/notepad/scratch.pad.rb +0 -224
- data/lib/store-commands.txt +0 -180
@@ -0,0 +1,120 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
module OpenKey
|
5
|
+
|
6
|
+
class KeyPass
|
7
|
+
|
8
|
+
|
9
|
+
# <tt>Collect something sensitive from the command line</tt> with a
|
10
|
+
# minimum length specified in the first parameter. This method can't
|
11
|
+
# know whether the information is a password, a pin number or whatever
|
12
|
+
# so it takes the integer minimum size at its word.
|
13
|
+
#
|
14
|
+
# <b>Question 5 to App Config | What is the Secret?</b>
|
15
|
+
#
|
16
|
+
# The client may need to acquire the secret if the answer to question 4 indicates the need
|
17
|
+
# to instantiate the keys and encrypt the application's plaintext database. The application
|
18
|
+
# should facilitate communication of the secret via
|
19
|
+
#
|
20
|
+
# - an environment variable
|
21
|
+
# - the system clipboard (cleared after reading)
|
22
|
+
# - a file whose path is a command parameter
|
23
|
+
# - a file in a pre-agreed location
|
24
|
+
# - a file in the present directory (with a pre-agreed name)
|
25
|
+
# - a URL from a parameter or pre-agreed
|
26
|
+
# - the shell's secure password reader
|
27
|
+
# - the DConf / GConf or GSettings configuration stores
|
28
|
+
# - a REST API
|
29
|
+
# - password managers like LastPass, KeePassX or 1Pass
|
30
|
+
# - the Amazon KMS (Key Management Store)
|
31
|
+
# - vaults from Ansible, Terraform and Kubernetes
|
32
|
+
# - credential managers like GitSecrets and Credstash
|
33
|
+
#
|
34
|
+
# @param prompt_twice [Boolean] indicate whether the user should be
|
35
|
+
# prompted twice. If true the prompt_2 text must be provided and
|
36
|
+
# converse is also true. A true value asserts that both times the
|
37
|
+
# user enters the same (case sensitive) string.
|
38
|
+
#
|
39
|
+
# @return [String] the collected string text ( watch out for non-ascii chars)
|
40
|
+
# @raise [ArgumentError] if the minimum size is less than one
|
41
|
+
def self.password_from_shell prompt_twice
|
42
|
+
|
43
|
+
assert_min_size MINIMUM_PASSWORD_SIZE
|
44
|
+
|
45
|
+
sleep(1)
|
46
|
+
puts "\nEnter a Password : "
|
47
|
+
first_secret = STDIN.noecho(&:gets).chomp
|
48
|
+
|
49
|
+
assert_input_text_size first_secret.length, MINIMUM_PASSWORD_SIZE
|
50
|
+
return first_secret unless prompt_twice
|
51
|
+
|
52
|
+
sleep(1)
|
53
|
+
puts "\nRe-enter the password : "
|
54
|
+
check_secret = STDIN.noecho(&:gets).chomp
|
55
|
+
|
56
|
+
assert_same_size_text first_secret, check_secret
|
57
|
+
|
58
|
+
return first_secret
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
# --
|
64
|
+
# -- Raise an exception if asked to collect text that is less
|
65
|
+
# -- than 3 characters in length.
|
66
|
+
# --
|
67
|
+
def self.assert_min_size min_size
|
68
|
+
|
69
|
+
min_length_msg = "\n\nCrypts with 2 (or less) characters open up exploitable holes.\n\n"
|
70
|
+
raise ArgumentError.new min_length_msg if min_size < 3
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
# --
|
76
|
+
# -- Output an error message and then exit if the entered input
|
77
|
+
# -- text size does not meet the minimum requirements.
|
78
|
+
# --
|
79
|
+
def self.assert_input_text_size input_size, min_size
|
80
|
+
|
81
|
+
if( input_size < min_size )
|
82
|
+
|
83
|
+
puts
|
84
|
+
puts "Input is too short. Please enter at least #{min_size} characters."
|
85
|
+
puts
|
86
|
+
|
87
|
+
exit
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
# --
|
95
|
+
# -- Assert that the text entered the second time is exactly (case sensitive)
|
96
|
+
# -- the same as the text entered the first time.
|
97
|
+
# --
|
98
|
+
def self.assert_same_size_text first_text, second_text
|
99
|
+
|
100
|
+
unless( first_text.eql? second_text )
|
101
|
+
|
102
|
+
puts
|
103
|
+
puts "Those two bits of text are not the same (in my book)!"
|
104
|
+
puts
|
105
|
+
|
106
|
+
exit
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
MINIMUM_PASSWORD_SIZE = 4
|
115
|
+
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
end
|
data/lib/keytools/key.rb
CHANGED
@@ -2,6 +2,23 @@
|
|
2
2
|
|
3
3
|
module OpenKey
|
4
4
|
|
5
|
+
# First use the class methods to source keys, then use a key's instance
|
6
|
+
# methods to access its properties and in concert with other symmetrical
|
7
|
+
# information, you can use the keys to lock (encrypt) or unlock (decrypt)
|
8
|
+
# other keys and objecs.
|
9
|
+
#
|
10
|
+
# == Sourcing and Deriving Keys
|
11
|
+
#
|
12
|
+
# Keys can be
|
13
|
+
#
|
14
|
+
# - sourced from a secure random byte generating function
|
15
|
+
# - sourced from ciphertext and another (decryption) key
|
16
|
+
# - generated by passing a secret through key derivation functions
|
17
|
+
# - regenerated from a secret and previously stored salts
|
18
|
+
# - sourced from the current unique workstation shell environment
|
19
|
+
# - sourced from an environment variable containing ciphertext
|
20
|
+
#
|
21
|
+
#
|
5
22
|
# Keys need to be viewed (represented) in multiple ways and the essence
|
6
23
|
# of the key viewer is to input keys {as_bits}, {as_bytes} and {as_base64}
|
7
24
|
# and then output the same key (in as far as is possible) - as bits, as
|
@@ -53,54 +70,6 @@ module OpenKey
|
|
53
70
|
# So when we request a byte, or base64 representation the viewer will
|
54
71
|
# truncate (not round down) to the desired length.
|
55
72
|
#
|
56
|
-
#
|
57
|
-
# == YACHT 64 | Yet Another Character Table
|
58
|
-
#
|
59
|
-
# This binary key class is a dab hand at converting base64 strings
|
60
|
-
# into their 6-bit binary string equivalents.
|
61
|
-
#
|
62
|
-
# It can convert non-alphanumeric characters within either Base64 or
|
63
|
-
# Radix64 into the OpenKey YACHT64 standard which has a forward slash
|
64
|
-
# but neither a plus sign nor a period character.
|
65
|
-
#
|
66
|
-
# == Character Order | Base64 | UrlSafe64 | Radix64 | YACHT64
|
67
|
-
#
|
68
|
-
# The character sets for each of he four 64 fomats are as follows.
|
69
|
-
#
|
70
|
-
# - Base-64 is <b>A to Z</b> then <b>a to z</b> then <b>0 to 9</b> then <b>+</b> then <b>/</b>
|
71
|
-
# - Radix64 is <b>.</b> then <b>/</b> then <b>0 to 9</b> then <b>A to Z</b> then <b>a to z</b>
|
72
|
-
# - UrlSafeBase64 is Base64 but chars 63/64 are an <b>underscore (_)</b> and <b>hyphen (-)</b>
|
73
|
-
# - UrlSafeBase64 <b>does not have line breaks and carriage returns</b> (unlike Base64)
|
74
|
-
# - <b>OpenKey 64 (YACHT64)</b> uses the same 62 characters plus an @ sign and a forward slash
|
75
|
-
# - The 64 <b>OpenKey 64</b> characters are <b>obfuscated into a random order</b>
|
76
|
-
#
|
77
|
-
# <b>Why Order Doesn't Matter</b>
|
78
|
-
#
|
79
|
-
# Order doesn't matter if string of bits is used in key creation
|
80
|
-
# as long as this class is employed to do all the necessary
|
81
|
-
# conversions.
|
82
|
-
#
|
83
|
-
# Base64 and Radix64 (outputted by the OpenBSD inspired bcrypt)
|
84
|
-
# differ in both the order of characters and their choice of the
|
85
|
-
# two non-alphanumeric characters.
|
86
|
-
#
|
87
|
-
# == 4 Non-AlphaNumerics | Base64 | Radix64 | YACHT64
|
88
|
-
#
|
89
|
-
# The behaviour here is happy to convert base64 strings produced by either
|
90
|
-
# Radix64 or Base64 or UrlSafe Base64. Howeverr it aware of the
|
91
|
-
# <b>non alpha-numeric characters</b> and converts them before processing
|
92
|
-
# with the modus operandi that says
|
93
|
-
#
|
94
|
-
# - ignore the forward slash in <b>YACHT64, Base64 and Radix64</b>
|
95
|
-
# - convert the <b>plus (+)</b> in Base64 to the <b>@ symbol</b> in YACHT64
|
96
|
-
# - convert the <b>period (.)</b> in <b>Radix64</b> to the @ symbol in YACHT64
|
97
|
-
# - convert <b>hyphen (-)</b> in <b>Url Safe Base64</b> into a fwd slash
|
98
|
-
# - convert <b>underscore (_)</b> in <b>Url Safe Base64</b> to an @ sign
|
99
|
-
# - <b>delete the (=) equals</b> padding character used by Base64
|
100
|
-
#
|
101
|
-
# Neither the OpenBSD backed Radix64 nor the OpenKey (YACHT64) entertain the
|
102
|
-
# concept of padding.
|
103
|
-
#
|
104
73
|
# == Mapping Each Character to 6 Binary Bits
|
105
74
|
#
|
106
75
|
# We need 6 binary bits to represent a base64 character (and 4
|
@@ -125,353 +94,514 @@ module OpenKey
|
|
125
94
|
#
|
126
95
|
class Key
|
127
96
|
|
128
|
-
#
|
129
|
-
#
|
130
|
-
# sets are all similar in that they hold 64 characters and
|
131
|
-
# they define two non alphanumeric characters because the
|
132
|
-
# 26 lowercase, 26 uppercase and 10 digits only adds up to
|
133
|
-
# an agonising close 62 characters.
|
134
|
-
YACHT64_CHARACTER_SET = [
|
135
|
-
"a", "9", "W", "B", "f", "K", "O", "z",
|
136
|
-
"3", "s", "1", "5", "c", "n", "E", "J",
|
137
|
-
"L", "A", "l", "6", "I", "w", "o", "g",
|
138
|
-
"k", "N", "t", "Y", "S", "%", "T", "b",
|
139
|
-
"V", "R", "H", "0", "@", "Z", "8", "F",
|
140
|
-
"G", "j", "u", "m", "M", "h", "4", "p",
|
141
|
-
"q", "d", "7", "v", "e", "2", "U", "X",
|
142
|
-
"r", "C", "y", "Q", "D", "x", "P", "i"
|
143
|
-
]
|
144
|
-
|
145
|
-
|
146
|
-
# Initialize an n bit binary key (of literally ones and zeroes)
|
147
|
-
# from the base64 represented string in the parameter.
|
148
|
-
#
|
149
|
-
# This method can convert either UNIX <b>Radix 64</b> or
|
150
|
-
# <b>Base 64</b> into a <b>string of ones and zeroes</b>.
|
151
|
-
#
|
152
|
-
# The difference between Radix64 and Base64 is the ordering
|
153
|
-
# of the characters and the choice of the two non alpha-numeric
|
154
|
-
# (63rd and 64th or 1st and 2nd) characters.
|
155
|
-
#
|
156
|
-
# @param base64_string [String]
|
157
|
-
# the either Base64 or (unix) Radix 64 encoded string
|
97
|
+
# Initialize a key object from a bit string of ones and zeroes provided
|
98
|
+
# in the parameter string.
|
158
99
|
#
|
159
|
-
#
|
160
|
-
#
|
161
|
-
#
|
162
|
-
#
|
163
|
-
#
|
164
|
-
#
|
165
|
-
#
|
166
|
-
#
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
100
|
+
# For example a string of 384 bits (ones and zeroes) can be thought of
|
101
|
+
# as a 48 byte key which can also be represented with 64 more compact
|
102
|
+
# base64 characters.
|
103
|
+
#
|
104
|
+
# | -------- | ------------ | -------------------------------- |
|
105
|
+
# | Bits | Bytes | Base64 |
|
106
|
+
# | -------- | ------------ | -------------------------------- |
|
107
|
+
# | 384 Bits | is 48 bytes | and 64 characters |
|
108
|
+
# | -------- | ------------ | -------------------------------- |
|
109
|
+
#
|
110
|
+
# @param the_bit_string [String]
|
111
|
+
# the bit string of ones and zeroes that represents the bits that
|
112
|
+
# represent this key
|
113
|
+
def initialize the_bit_string
|
114
|
+
@bit_string = the_bit_string
|
115
|
+
end
|
172
116
|
|
117
|
+
|
118
|
+
# Return a (secure) randomly generated super high entropy 384 bit key
|
119
|
+
# that can be stored with <b>64 base64 characters</b> and used to
|
120
|
+
# <b><em>source digest functions</em></b> that can unreversibly convert
|
121
|
+
# the key to a <b>256 bit symmetric encryption key</b>.
|
122
|
+
#
|
123
|
+
# | -------- | ------------ | -------------------------------- |
|
124
|
+
# | Bits | Bytes | Base64 |
|
125
|
+
# | -------- | ------------ | -------------------------------- |
|
126
|
+
# | 384 Bits | is 48 bytes | and 64 characters |
|
127
|
+
# | -------- | ------------ | -------------------------------- |
|
128
|
+
#
|
129
|
+
# This key easily translates to a base64 and/or byte array format because
|
130
|
+
# the 384 bit count is a <b>multiple of both 6 and 8</b>.
|
131
|
+
#
|
132
|
+
# @return [OpenKey::Key]
|
133
|
+
# return a key containing 384 random bits (or a random array of 48 bytes)
|
134
|
+
# which can if necessary be serialized into 64 base64 characters.
|
135
|
+
#
|
136
|
+
# @raise [ArgumentError]
|
137
|
+
# If a nil or zero length byte array is received.
|
138
|
+
# Or if the number of bytes <b>multiplied by 8</b>
|
139
|
+
# is <b>not a multiple of 6</b>.
|
140
|
+
def self.from_random
|
141
|
+
return Key.new( to_random_bits( RANDOM_KEY_BYTE_LENGTH ) )
|
173
142
|
end
|
174
143
|
|
175
144
|
|
176
|
-
def
|
145
|
+
def self.to_random_bits the_byte_length
|
146
|
+
random_bit_string = ""
|
147
|
+
for n in 1 .. the_byte_length
|
148
|
+
random_integer = SecureRandom.random_number( EIGHT_BIT_INTEGER_SIZE )
|
149
|
+
random_bit_string += "%08d" % [ random_integer.to_s(2) ]
|
150
|
+
end
|
151
|
+
return random_bit_string
|
152
|
+
end
|
177
153
|
|
178
|
-
@original64_string = replace_yacht64( base64_string )
|
179
154
|
|
180
|
-
|
181
|
-
|
155
|
+
# Return the key represented by the parameter sequence of base64
|
156
|
+
# characters.
|
157
|
+
#
|
158
|
+
# @param char64_string [String]
|
159
|
+
#
|
160
|
+
# The base64 character sequence which the returned key is
|
161
|
+
# instantiated from. Naturally this character sequencee cannot
|
162
|
+
# be nil, nor can it contain any characters that are not
|
163
|
+
# present in {Key64::YACHT64_CHARACTER_SET}.
|
164
|
+
#
|
165
|
+
# Ideally the number of parameter characters multiplied by 6
|
166
|
+
# <b>should be a multiple of eight (8)</b> otherwise the new
|
167
|
+
# key's bit string will require padding and extension.
|
168
|
+
#
|
169
|
+
# @return [OpenKey::Key]
|
170
|
+
# return a key from the parameter sequence of base64 characters.
|
171
|
+
#
|
172
|
+
# @raise [ArgumentError]
|
173
|
+
# If a nil or zero length byte array is received.
|
174
|
+
# Or if the number of bytes <b>multiplied by 8</b>
|
175
|
+
# is <b>not a multiple of 6</b>.
|
176
|
+
def self.from_char64 char64_string
|
177
|
+
return Key.new( Key64.to_bits( char64_string ) )
|
178
|
+
end
|
182
179
|
|
183
|
-
yacht64_index = YACHT64_CHARACTER_SET.index(the_char)
|
184
180
|
|
185
|
-
|
186
|
-
|
181
|
+
# Return a key represented by the parameter binary string.
|
182
|
+
#
|
183
|
+
# @param binary_text [String]
|
184
|
+
# The binary string that the returned key will be
|
185
|
+
# instantiated from.
|
186
|
+
#
|
187
|
+
# @return [OpenKey::Key]
|
188
|
+
# return a key from the binary byte string parameter
|
189
|
+
def self.from_binary binary_text
|
190
|
+
ones_and_zeroes = binary_text.unpack("B*")[0]
|
191
|
+
return Key.new( ones_and_zeroes )
|
192
|
+
end
|
187
193
|
|
188
|
-
index_msg = "yacht64 index should run between 0 and 63 inclusive."
|
189
|
-
all_good = ( yacht64_index >= 0 ) && ( yacht64_index <= 63 )
|
190
|
-
raise ArgumentError, index_msg unless all_good
|
191
194
|
|
192
|
-
|
195
|
+
# Convert a string of Radix64 characters into a key.
|
196
|
+
#
|
197
|
+
# This method converts the base64 string into the internal YACHT64 format
|
198
|
+
# and then converts that into a bit string so that a key can be instantiated.
|
199
|
+
#
|
200
|
+
# @param radix64_string [String]
|
201
|
+
# the radix64 string to convert into akey. This string will be a subset
|
202
|
+
# of the usual 62 character suspects together with period and forward
|
203
|
+
# slash characters.
|
204
|
+
#
|
205
|
+
# This parameter should not contain newlines nor carriage returns.
|
206
|
+
#
|
207
|
+
# @return [OpenKey::Key]
|
208
|
+
# return a key from the parameter sequence of base64 characters.
|
209
|
+
#
|
210
|
+
# @raise [ArgumentError]
|
211
|
+
# If a nil or zero length parameter array is received.
|
212
|
+
def self.from_radix64 radix64_string
|
213
|
+
return Key.new( Key64.from_radix64_to_bits( radix64_string ) )
|
214
|
+
end
|
193
215
|
|
194
|
-
end
|
195
216
|
|
196
|
-
|
217
|
+
# When a key is initialized, it is internally represented as a
|
218
|
+
# string of ones and zeroes primarily for simplicity and can be
|
219
|
+
# visualized as bits that are either off or on.
|
220
|
+
#
|
221
|
+
# Once internalized a key can also be represented as
|
222
|
+
#
|
223
|
+
# - a sequence of base64 (or radix64) characters (1 per 6 bits)
|
224
|
+
# - a binary string suitable for encryption (1 byte per 8 bits)
|
225
|
+
# - a 256bit encryption key from Digest(ing) the binary form
|
226
|
+
#
|
227
|
+
# @return [String]
|
228
|
+
# a string of literally ones and zeroes that represent the
|
229
|
+
# sequence of bits making up this key.
|
230
|
+
def to_s
|
197
231
|
|
232
|
+
## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
|
233
|
+
## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
|
234
|
+
## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
|
235
|
+
## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
|
236
|
+
## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
|
237
|
+
## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
|
238
|
+
## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
|
239
|
+
|
240
|
+
## ---------------------------------------------
|
241
|
+
## +++++++++ WARNING ++++++++
|
242
|
+
## ---------------------------------------------
|
243
|
+
##
|
244
|
+
## to_s does not need 2b called
|
245
|
+
## So both the below print the same.
|
246
|
+
##
|
247
|
+
## So YOU MUST KEEP the to_s method until a proper test suite is in place.
|
248
|
+
## So YOU MUST KEEP the to_s method until a proper test suite is in place.
|
249
|
+
##
|
250
|
+
## puts "#{the_key}"
|
251
|
+
## puts "#{the_key.to_s}"
|
252
|
+
##
|
253
|
+
## So YOU MUST KEEP the to_s method until a proper test suite is in place.
|
254
|
+
## So YOU MUST KEEP the to_s method until a proper test suite is in place.
|
255
|
+
##
|
256
|
+
## ---------------------------------------------
|
257
|
+
|
258
|
+
return @bit_string
|
198
259
|
end
|
199
260
|
|
200
261
|
|
262
|
+
# Convert this keys bit value into a printable character set
|
263
|
+
# that is suitable for storing in multiple places such as
|
264
|
+
# <b>environment variables</b> and <b>INI files</b>.
|
265
|
+
#
|
266
|
+
# @return [String]
|
267
|
+
# printable characters from a set of 62 alpha-numerics
|
268
|
+
# plus an @ symbol and a percent % sign.
|
269
|
+
#
|
270
|
+
# @raise ArgumentError
|
271
|
+
# If the bit value string for this key is nil.
|
272
|
+
# Or if the bit string length is not a multiple of six.
|
273
|
+
# Or if it contains any character that is not a 1 or 0.
|
274
|
+
def to_char64
|
275
|
+
assert_non_nil_bits
|
276
|
+
return Key64.from_bits( @bit_string )
|
277
|
+
end
|
201
278
|
|
202
|
-
def self.from_byte_array the_byte_array
|
203
|
-
|
204
|
-
new_key = Key.new
|
205
|
-
new_key.instantiate_from_base64( Base64.urlsafe_encode64( the_byte_array ) )
|
206
|
-
return new_key
|
207
279
|
|
280
|
+
# Return the <b>un-printable <em>binary</em> bytes</b> representation
|
281
|
+
# of this key. If you store 128 bits it will produce 22 characters
|
282
|
+
# because 128 divide by 6 is 21 characters and a remainder of two (2)
|
283
|
+
# bits.
|
284
|
+
#
|
285
|
+
# The re-conversion of the 22 characters will now produce 132 bits which
|
286
|
+
# is different from the original 128 bits.
|
287
|
+
#
|
288
|
+
# @return [Byte]
|
289
|
+
# a non-printable binary string of eight (8) bit bytes which can be
|
290
|
+
# used as input to both digest and symmetric cipher functions.
|
291
|
+
def to_binary
|
292
|
+
return [ to_s ].pack("B*")
|
208
293
|
end
|
209
294
|
|
210
295
|
|
296
|
+
# Return the <b>un-printable <em>binary</em> bytes</b> representation
|
297
|
+
# of this key. If you store 128 bits it will produce 22 characters
|
298
|
+
# because 128 divide by 6 is 21 characters and a remainder of two (2)
|
299
|
+
# bits.
|
300
|
+
#
|
301
|
+
# The re-conversion of the 22 characters will now produce 132 bits which
|
302
|
+
# is different from the original 128 bits.
|
303
|
+
#
|
304
|
+
# @return [Byte]
|
305
|
+
# a non-printable binary string of eight (8) bit bytes which can be
|
306
|
+
# used as input to both digest and symmetric cipher functions.
|
307
|
+
def self.to_binary_from_bit_string bit_string_to_convert
|
308
|
+
return [ bit_string_to_convert ].pack("B*")
|
309
|
+
end
|
310
|
+
|
211
311
|
|
212
|
-
#
|
213
|
-
#
|
312
|
+
# This method uses digests to convert the key's binary representation
|
313
|
+
# (which is either 48 bytes for purely random keys or 64 bytes for keys
|
314
|
+
# derived from human sourced secrets) into a key whose size is ideal for
|
315
|
+
# plying the ubiquitous <b>AES256 symmetric encryption algorithm</b>.
|
214
316
|
#
|
215
|
-
#
|
317
|
+
# This method should only ever be called when this key has been derived
|
318
|
+
# from either a (huge) <b>48 byte random source</b> or from a key derivation
|
319
|
+
# function (KDF) such as BCrypt, SCrypt, PBKDF2 or a union from {KdfApi}
|
320
|
+
# which delivers 512 bit (64 byte) key for reduction to 256 bits.
|
216
321
|
#
|
217
|
-
#
|
218
|
-
#
|
219
|
-
#
|
220
|
-
#
|
322
|
+
# @return [String]
|
323
|
+
# a binary string of thirty-two (32) eight (8) bit bytes which
|
324
|
+
# if appropriate can be used as a symmetric encryption key especially
|
325
|
+
# to the powerful AES256 cipher.
|
326
|
+
def to_aes_key
|
327
|
+
return Digest::SHA256.digest( to_binary() )
|
328
|
+
end
|
329
|
+
|
330
|
+
|
331
|
+
# This method uses the SHA384 digest to convert this key's binary
|
332
|
+
# representation into another (newly instantiated) key whose size
|
333
|
+
# is <b>precisely 384 bits</b>.
|
221
334
|
#
|
222
|
-
# If
|
223
|
-
# be called "random" and could change in value when converted
|
224
|
-
# from one representation to the other.
|
335
|
+
# If you take the returned key and call
|
225
336
|
#
|
226
|
-
#
|
227
|
-
#
|
228
|
-
#
|
229
|
-
# bytes and a whole number of 6-bit base64 characters.
|
337
|
+
# - {to_char64} you get a 64 character base64 string
|
338
|
+
# - {to_s} you get a string of 384 ones and zeroes
|
339
|
+
# - {to_binary} you get a 48 byte binary string
|
230
340
|
#
|
231
|
-
# @
|
232
|
-
#
|
233
|
-
def
|
234
|
-
|
235
|
-
mod24_msg = "The bit count of #{bit_count} is not divisable by 24."
|
236
|
-
raise ArgumentError, mod24_msg unless bit_count % 24 == 0
|
341
|
+
# @return [OpenKey::Key]
|
342
|
+
# a key with a bit length (ones and zeroes) of <b>precisely 384</b>.
|
343
|
+
def to_384_bit_key
|
237
344
|
|
238
|
-
|
239
|
-
random64_string = SecureRandom.base64( num_6bit_blocks + 4 )
|
240
|
-
perfect_rand_64 = random64_string[ 0 .. num_6bit_blocks ]
|
241
|
-
length_64string = perfect_rand_64.length
|
345
|
+
a_384_bit_key = Key.from_binary( Digest::SHA384.digest( to_binary() ) )
|
242
346
|
|
243
|
-
|
244
|
-
|
347
|
+
has_384_chars = a_384_bit_key.to_s.length == 384
|
348
|
+
err_msg = "Digested key length is #{a_384_bit_key.to_s.length} instead of 384."
|
349
|
+
raise RuntimeError, err_msg unless has_384_chars
|
245
350
|
|
246
|
-
|
351
|
+
return a_384_bit_key
|
247
352
|
|
248
353
|
end
|
249
354
|
|
250
355
|
|
251
|
-
#
|
252
|
-
#
|
356
|
+
# Use the {OpenSSL::Cipher::AES256} block cipher in CBC mode and the binary
|
357
|
+
# 256bit representation of this key to encrypt the parameter key.
|
253
358
|
#
|
254
|
-
#
|
255
|
-
#
|
359
|
+
# Store the ciphertext provided by this method. To re-acquire (reconstitute)
|
360
|
+
# the parameter key use the {do_decrypt_key} decryption method with
|
361
|
+
# the ciphertext produced here.
|
256
362
|
#
|
257
|
-
#
|
258
|
-
# - <b>one line text</b> (without newlines and carriage returns)
|
259
|
-
# - <b>environment variables</b>
|
260
|
-
# - INI, JSON, YAML and XML files
|
363
|
+
# <b>Only Encrypt Strong Keys</b>
|
261
364
|
#
|
262
|
-
#
|
365
|
+
# Never encrypt a potentially weak key, like one derived from a human password
|
366
|
+
# (even though it is put through key derivation functions).
|
263
367
|
#
|
264
|
-
#
|
368
|
+
# Once generated (or regenerated) a potentially weak key should live only as
|
369
|
+
# long as it takes for it to encrypt a strong key. The strong key can then
|
370
|
+
# be used to encrypt valuable assets.
|
265
371
|
#
|
266
|
-
#
|
267
|
-
# - Radix64 is <b>.</b> then <b>/</b> then <b>0 to 9</b> then <b>A to Z</b> then <b>a to z</b>
|
268
|
-
# - UrlSafeBase64 is Base64 but chars 63/64 are an <b>underscore (_)</b> and <b>hyphen (-)</b>
|
269
|
-
# - UrlSafeBase64 <b>does not have line breaks and carriage returns</b> (unlike Base64)
|
270
|
-
# - <b>OpenKey 64 (YACHT64)</b> uses the same 62 characters plus an @ sign and a forward slash
|
271
|
-
# - The 64 <b>OpenKey 64</b> characters are <b>obfuscated into a random order</b>
|
372
|
+
# <b>Enforcing Strong Key Size</b>
|
272
373
|
#
|
273
|
-
#
|
374
|
+
# If one key is potentially weaker than the other, the weaker key must be this
|
375
|
+
# object and the strong key is the parameter key.
|
274
376
|
#
|
275
|
-
#
|
276
|
-
#
|
277
|
-
# conversions.
|
377
|
+
# This method thus enforces the size of the strong key. A strong key has
|
378
|
+
# 384 bits of entropy, and is represented by 64 base64 characters.
|
278
379
|
#
|
279
|
-
#
|
280
|
-
#
|
281
|
-
#
|
380
|
+
# @param key_to_encrypt [OpenKey::Key]
|
381
|
+
# this is the key that will first be serialized into base64 and then locked
|
382
|
+
# down using the 256 bit binary string from this host object as the symmetric
|
383
|
+
# encryption key.
|
384
|
+
#
|
385
|
+
# This method is sensitive to the size of the parameter key and expects to
|
386
|
+
# encrypt <b>exactly 64 base64 characters</b> within the parameter key.
|
282
387
|
#
|
283
388
|
# @return [String]
|
284
|
-
#
|
285
|
-
#
|
286
|
-
#
|
287
|
-
#
|
288
|
-
#
|
289
|
-
#
|
290
|
-
#
|
291
|
-
#
|
292
|
-
|
293
|
-
# not exactly the same length.
|
294
|
-
def to_yacht64
|
295
|
-
|
296
|
-
almost_base64 = replace_yacht64( @original64_string )
|
297
|
-
the_yacht64_encoding = ""
|
298
|
-
|
299
|
-
almost_base64.each_char do |the_char|
|
300
|
-
yacht64_index = YACHT64_CHARACTER_SET.index(the_char)
|
301
|
-
assert_yacht64_index yacht64_index
|
302
|
-
the_yacht64_encoding += YACHT64_CHARACTER_SET[ yacht64_index ]
|
303
|
-
end
|
389
|
+
# The returned ciphertext should be stored. Its breakdown is as follows.
|
390
|
+
# 96 bytes are returned which equates to 128 base64 characters.
|
391
|
+
# The random initialization vector (iv) accounts for the first 16 bytes.
|
392
|
+
# The actual crypt ciphertext then accounts for the final 80 bytes.
|
393
|
+
#
|
394
|
+
# @raise [ArgumentError]
|
395
|
+
# the size of the parameter (strong) key is enforced to ensure that it has
|
396
|
+
# exactly 384 bits of entropy which are represented by 64 base64 characters.
|
397
|
+
def do_encrypt_key key_to_encrypt
|
304
398
|
|
305
|
-
|
306
|
-
goodlength = the_yacht64_encoding.length == @original64_string.length
|
307
|
-
raise RuntimeError, length_msg unless goodlength
|
399
|
+
crypt_cipher = OpenSSL::Cipher::AES256.new(:CBC)
|
308
400
|
|
309
|
-
|
401
|
+
crypt_cipher.encrypt()
|
402
|
+
random_iv = crypt_cipher.random_iv()
|
403
|
+
crypt_cipher.key = to_aes_key()
|
310
404
|
|
311
|
-
|
405
|
+
calling_module = File.basename caller_locations(1,1).first.absolute_path, ".rb"
|
406
|
+
calling_method = caller_locations(1,1).first.base_label
|
407
|
+
calling_lineno = caller_locations(1,1).first.lineno
|
408
|
+
caller_details = "#{calling_module} | #{calling_method} | (line #{calling_lineno})"
|
312
409
|
|
410
|
+
log.info(x) { "### #####################################################################" }
|
411
|
+
log.info(x) { "### Caller Details =>> =>> #{caller_details}" }
|
412
|
+
log.info(x) { "### #####################################################################" }
|
413
|
+
log.info(x) { "The BitStr Char representation of this key => #{to_s()}" }
|
414
|
+
log.info(x) { "256bit Digest (Urlsafe Base64) of this key => #{Base64.urlsafe_encode64(to_aes_key())}" }
|
415
|
+
log.info(x) { "The IncomingKey Base64 Char representation => #{key_to_encrypt.to_char64()}" }
|
416
|
+
log.info(x) { "Random IV Used for the AESs Key Encryption => #{Base64.urlsafe_encode64(random_iv)}" }
|
313
417
|
|
314
|
-
|
315
|
-
|
316
|
-
|
418
|
+
cipher_text = crypt_cipher.update( key_to_encrypt.to_char64 ) + crypt_cipher.final
|
419
|
+
log.info(x) { "Cipher Text Produced after this Encryption => #{Base64.urlsafe_encode64(cipher_text)}" }
|
420
|
+
|
421
|
+
binary_text = random_iv + cipher_text
|
422
|
+
ones_zeroes = binary_text.unpack("B*")[0]
|
423
|
+
ciphertxt64 = Key64.from_bits( ones_zeroes )
|
317
424
|
|
425
|
+
log.info(x) { "Amalgam of Binary Random IV and Ciphertext => #{ciphertxt64}" }
|
426
|
+
log.info(x) { "------------------------------------------------------------------------- >>>>>>" }
|
318
427
|
|
319
|
-
|
428
|
+
size_msg = "Expected bit count is #{EXPECTED_CIPHER_BIT_LENGTH} not #{ones_zeroes.length}."
|
429
|
+
raise RuntimeError, size_msg unless ones_zeroes.length == EXPECTED_CIPHER_BIT_LENGTH
|
320
430
|
|
321
|
-
return
|
431
|
+
return ciphertxt64
|
322
432
|
|
323
433
|
end
|
324
434
|
|
325
435
|
|
326
|
-
#
|
327
|
-
#
|
328
|
-
# the
|
436
|
+
# Use the {OpenSSL::Cipher::AES256} block cipher in CBC mode and the binary
|
437
|
+
# 256bit representation of this key to decrypt the parameter ciphertext and
|
438
|
+
# return the previously encrypted key.
|
329
439
|
#
|
330
|
-
#
|
440
|
+
# To re-acquire (reconstitute) the original key call this method with the
|
441
|
+
# stored ciphertext that was returned by the {do_encrypt_key}.
|
331
442
|
#
|
332
|
-
#
|
333
|
-
# ----------- | --------- | ----------------- | ----------- |
|
334
|
-
# | Algorithm | Bit Count | Base64 Chars | 8 Bit Bytes |
|
335
|
-
# ----------- | --------- | ----------------- | ----------- |
|
336
|
-
# | BCrypt | 168 Bits | 28 characters | 21 bytes |
|
337
|
-
# | Pbkdf2 | 96 Bits | 16 characters | 12 bytes |
|
338
|
-
# ----------- | --------- | ----------------- | ----------- |
|
339
|
-
# | Total | 264 Bits | 44 characters | 33 bytes |
|
340
|
-
# ----------- | --------- | ----------------- | ----------- |
|
443
|
+
# <b>Only Encrypt Strong Keys</b>
|
341
444
|
#
|
342
|
-
#
|
445
|
+
# Never encrypt a potentially weak key, like one derived from a human password
|
446
|
+
# (even though it is put through key derivation functions).
|
343
447
|
#
|
344
|
-
#
|
345
|
-
#
|
448
|
+
# Once generated (or regenerated) a potentially weak key should live only as
|
449
|
+
# long as it takes for it to encrypt a strong key. The strong key can then
|
450
|
+
# be used to encrypt valuable assets.
|
346
451
|
#
|
347
|
-
#
|
348
|
-
# removed from the end of the key. The key is then converted into a
|
349
|
-
# powerful 32 byte (256 bit) encryption agent and is hashed by the
|
350
|
-
# SHA256 digest and delivered.
|
452
|
+
# <b>Enforcing Strong Key Size</b>
|
351
453
|
#
|
352
|
-
#
|
353
|
-
#
|
354
|
-
# if appropriate can be used as a symmetric encryption key especially
|
355
|
-
# to the powerful AES256 cipher.
|
356
|
-
def to_256_bit_key
|
357
|
-
return [ to_s[ 0 .. (256-1) ] ].pack("B*")
|
358
|
-
end
|
359
|
-
|
360
|
-
|
361
|
-
# Return the <b>un-printable <em>binary</em> bytes</b> representation
|
362
|
-
# of this key. If you store 128 bits it will produce 22 characters
|
363
|
-
# because 128 divide by 6 is 21 characters and a remainder of two (2)
|
364
|
-
# bits.
|
454
|
+
# If one key is potentially weaker than the other, the weaker key must be this
|
455
|
+
# object and the strong key is reconstituted and returned by this method.
|
365
456
|
#
|
366
|
-
#
|
367
|
-
#
|
368
|
-
#
|
369
|
-
#
|
370
|
-
#
|
371
|
-
# used as input to both digest and symmetric cipher functions.
|
372
|
-
def to_binary_bytes
|
373
|
-
return [ to_s ].pack("B*")
|
374
|
-
end
|
375
|
-
|
376
|
-
|
377
|
-
# Return a key that is stoked with 48 random bytes which translates to
|
378
|
-
# a length of either <b>384 bits</b> or <b>64 base64 characters</b>.
|
379
|
-
#
|
380
|
-
# <b>The 48 Bytes map to 64 Base64 Characters</b>
|
381
|
-
#
|
382
|
-
# | -------- | ------------ | -------------------------------- |
|
383
|
-
# | Bits | Bytes | Base64 |
|
384
|
-
# | -------- | ------------ | -------------------------------- |
|
385
|
-
# | 384 Bits | is 48 bytes | and 64 characters |
|
386
|
-
# | -------- | ------------ | -------------------------------- |
|
387
|
-
#
|
388
|
-
# This key easily translates to a base64 and/or byte array format because
|
389
|
-
# the 384 bit count is a <b>multiple of both 6 and 8</b>.
|
457
|
+
# @param ciphertext_to_decrypt [String]
|
458
|
+
# Provide the ciphertext produced by our sister key encryption method.
|
459
|
+
# The ciphertext should hold 96 bytes which equates to 128 base64 characters.
|
460
|
+
# The random initialization vector (iv) accounts for the first 16 bytes.
|
461
|
+
# The actual crypt ciphertext then accounts for the final 80 bytes.
|
390
462
|
#
|
391
463
|
# @return [Key]
|
392
|
-
# return
|
393
|
-
#
|
394
|
-
|
464
|
+
# return the key that was serialized into base64 and then encrypted (locked down)
|
465
|
+
# with the 256 bit binary symmetric encryption key from this host object.
|
466
|
+
#
|
467
|
+
# @raise [ArgumentError]
|
468
|
+
# the size of the parameter ciphertext must be 128 base 64 characters.
|
469
|
+
def do_decrypt_key ciphertext_to_decrypt
|
470
|
+
|
471
|
+
calling_module = File.basename caller_locations(1,1).first.absolute_path, ".rb"
|
472
|
+
calling_method = caller_locations(1,1).first.base_label
|
473
|
+
calling_lineno = caller_locations(1,1).first.lineno
|
474
|
+
caller_details = "#{calling_module} | #{calling_method} | (line #{calling_lineno})"
|
395
475
|
|
396
|
-
|
397
|
-
|
398
|
-
|
476
|
+
log.info(x) { "### #####################################################################" }
|
477
|
+
log.info(x) { "### Caller Details =>> =>> #{caller_details}" }
|
478
|
+
log.info(x) { "### #####################################################################" }
|
479
|
+
log.info(x) { "Amalgam of Binary Random IV and Ciphertext => #{ciphertext_to_decrypt}" }
|
480
|
+
log.info(x) { "The Base64 Char representation of this key => #{to_s()}" }
|
481
|
+
log.info(x) { "256bit Digest (Urlsafe Base64) of this key => #{Base64.urlsafe_encode64(to_aes_key())}" }
|
399
482
|
|
400
|
-
|
483
|
+
bit_text = Key64.to_bits(ciphertext_to_decrypt)
|
484
|
+
size_msg = "Expected bit count is #{EXPECTED_CIPHER_BIT_LENGTH} not #{bit_text.length}."
|
485
|
+
raise RuntimeError, size_msg unless bit_text.length == EXPECTED_CIPHER_BIT_LENGTH
|
401
486
|
|
402
|
-
|
403
|
-
|
487
|
+
cipher_x = OpenSSL::Cipher::AES256.new(:CBC)
|
488
|
+
cipher_x.decrypt()
|
404
489
|
|
405
|
-
|
490
|
+
rawbytes = [ bit_text ].pack("B*")
|
406
491
|
|
407
|
-
|
408
|
-
|
409
|
-
|
492
|
+
cipher_x.key = to_aes_key()
|
493
|
+
cipher_x.iv = rawbytes[ 0 .. ( RANDOM_IV_BYTE_COUNT - 1 ) ]
|
494
|
+
key_chars_64 = cipher_x.update( rawbytes[ RANDOM_IV_BYTE_COUNT .. -1 ] ) + cipher_x.final
|
410
495
|
|
411
|
-
|
412
|
-
base64_lenth_msg = "Expected #{BASE64_CHARACTER_COUNT} base64 chars not #{base64_length}."
|
413
|
-
raise RuntimeError, base64_length_msg unless base64_length == BASE64_CHARACTER_COUNT
|
496
|
+
log.info(x) { "The OutgoingKey Base64 Char representation => #{key_chars_64}" }
|
414
497
|
|
415
|
-
return
|
498
|
+
return Key.from_char64( key_chars_64 )
|
416
499
|
|
417
500
|
end
|
418
501
|
|
419
502
|
|
503
|
+
# Use the {OpenSSL::Cipher::AES256} block cipher in CBC mode and the binary
|
504
|
+
# 256bit representation of this key to encrypt the parameter plaintext using
|
505
|
+
# the parameter random initialization vector.
|
506
|
+
#
|
507
|
+
# Store the ciphertext provided by this method. To re-acquire (reconstitute)
|
508
|
+
# the plaintext use the {do_decrypt_text} decryption method, giving
|
509
|
+
# it the same initialization vector and the ciphertext produced here.
|
510
|
+
#
|
511
|
+
# <b>Only Encrypt Once</b>
|
512
|
+
#
|
513
|
+
# Despite the initialization vector protecting against switch attacks you
|
514
|
+
# should <b>only use this or any other key once</b> to encrypt an object.
|
515
|
+
# While it is okay to encrypt small targets using two different keys, it
|
516
|
+
# pays not to do the same when the target is large.
|
517
|
+
#
|
518
|
+
# @param random_iv [String]
|
519
|
+
# a randomly generated 16 byte binary string that is to be used as the
|
520
|
+
# initialization vector (IV) - this is a requirement for AES encryption
|
521
|
+
# in CBC mode - this IV does not need to be treated as a secret
|
522
|
+
#
|
523
|
+
# @param plain_text [String]
|
524
|
+
# the plaintext or binary string to be encrypted. To re-acquire this string
|
525
|
+
# use the {do_decrypt_text} decryption method, giving it the same
|
526
|
+
# initialization vector (provided in the first parameter) and the ciphertext
|
527
|
+
# returned from this method.
|
528
|
+
#
|
529
|
+
# @return [String]
|
530
|
+
# The returned binary ciphertext should be encoded and persisted until such
|
531
|
+
# a time as its re-acquisition by authorized parties becomes necessary.
|
532
|
+
def do_encrypt_text random_iv, plain_text
|
533
|
+
|
534
|
+
crypt_cipher = OpenSSL::Cipher::AES256.new(:CBC)
|
535
|
+
|
536
|
+
crypt_cipher.encrypt()
|
537
|
+
crypt_cipher.iv = random_iv
|
538
|
+
crypt_cipher.key = to_aes_key()
|
420
539
|
|
540
|
+
return crypt_cipher.update( plain_text ) + crypt_cipher.final
|
541
|
+
|
542
|
+
end
|
421
543
|
|
422
544
|
|
423
|
-
#
|
424
|
-
#
|
425
|
-
#
|
545
|
+
# Use the {OpenSSL::Cipher::AES256} block cipher in CBC mode and the binary
|
546
|
+
# 256bit representation of this key to decrypt the parameter ciphertext using
|
547
|
+
# the parameter random initialization vector.
|
426
548
|
#
|
427
|
-
#
|
549
|
+
# Use this method to re-acquire (reconstitute) the plaintext that was
|
550
|
+
# converted to ciphertext by the {do_encrypt_text} encryption method,
|
551
|
+
# naturally using the same initialization vector for both calls.
|
428
552
|
#
|
429
|
-
#
|
430
|
-
# Radix64 or Base64 or UrlSafe Base64. However, it is aware of the
|
431
|
-
# <b>non alpha-numeric characters</b> and converts them before processing
|
432
|
-
# with the modus operandi that says
|
553
|
+
# <b>Only Decrypt Once</b>
|
433
554
|
#
|
434
|
-
#
|
435
|
-
#
|
436
|
-
#
|
437
|
-
#
|
438
|
-
# - convert <b>underscore (_)</b> in <b>Url Safe Base64</b> to an @ sign
|
439
|
-
# - <b>delete the (=) equals</b> padding character used by Base64
|
555
|
+
# Consider <b>a key spent</b> as soon as it decrypts the one object it was
|
556
|
+
# created to decrypt. Like a bee dying after a sting, a key should die after
|
557
|
+
# it decrypts an object. Should re-decryption be necessary - another key
|
558
|
+
# should be derived or generated.
|
440
559
|
#
|
441
|
-
#
|
442
|
-
#
|
560
|
+
# @param random_iv [String]
|
561
|
+
# a randomly generated 16 byte binary string that is to be used as the
|
562
|
+
# initialization vector (IV) - this is a requirement for AES decryption
|
563
|
+
# in CBC mode - this IV does not need to be treated as a secret
|
443
564
|
#
|
444
|
-
# @param
|
445
|
-
#
|
565
|
+
# @param cipher_text [String]
|
566
|
+
# the ciphertext or binary string to be decrypted in order to re-acquire
|
567
|
+
# (reconstitute) the plaintext that was converted to ciphertext by the
|
568
|
+
# {do_encrypt_text} encryption method.
|
446
569
|
#
|
447
570
|
# @return [String]
|
448
|
-
#
|
449
|
-
#
|
450
|
-
|
451
|
-
def replace_yacht64 char64_string
|
452
|
-
return char64_string.gsub(".", "@").gsub("/", "%").gsub("+", "@").gsub("-", "%").gsub("_", "@").delete("=")
|
453
|
-
end
|
571
|
+
# if the plaintext (or binary string) returned here still needs to be
|
572
|
+
# kept on the low, derive or generate another key to protect it.
|
573
|
+
def do_decrypt_text random_iv, cipher_text
|
454
574
|
|
575
|
+
raise ArgumentError, "Incoming cipher text cannot be nil." if cipher_text.nil?
|
455
576
|
|
577
|
+
crypt_cipher = OpenSSL::Cipher::AES256.new(:CBC)
|
456
578
|
|
457
|
-
|
579
|
+
crypt_cipher.decrypt()
|
580
|
+
crypt_cipher.iv = random_iv
|
581
|
+
crypt_cipher.key = to_aes_key()
|
458
582
|
|
583
|
+
return crypt_cipher.update( cipher_text ) + crypt_cipher.final
|
459
584
|
|
460
|
-
def assert_yacht64_index yacht64_index
|
461
|
-
index_msg = "yacht64 index should run between 0 and 63 inclusive."
|
462
|
-
all_good = yacht64_index >= 0 && yacht64_index <= 63
|
463
|
-
raise ArgumentError, index_msg unless all_good
|
464
585
|
end
|
465
586
|
|
466
587
|
|
467
|
-
|
588
|
+
private
|
589
|
+
|
590
|
+
|
591
|
+
RANDOM_KEY_BYTE_LENGTH = 48
|
592
|
+
|
593
|
+
EIGHT_BIT_INTEGER_SIZE = 256
|
594
|
+
|
595
|
+
RANDOM_IV_BYTE_COUNT = 16
|
596
|
+
|
597
|
+
CIPHERTEXT_BYTE_COUNT = 80
|
598
|
+
|
599
|
+
EXPECTED_CIPHER_BIT_LENGTH = ( CIPHERTEXT_BYTE_COUNT + RANDOM_IV_BYTE_COUNT ) * 8
|
468
600
|
|
469
|
-
in_length = in_string.length
|
470
|
-
out_length = out_string.length
|
471
|
-
good_ratio = out_length == in_length * 6
|
472
|
-
size_msg = "Out string length [#{out_length}] not 6 times bigger than [#{in_length}]."
|
473
|
-
raise RuntimeError, size_msg unless good_ratio
|
474
601
|
|
602
|
+
def assert_non_nil_bits
|
603
|
+
nil_err_msg = "The bit string for this key is nil."
|
604
|
+
raise RuntimeError, nil_err_msg if @bit_string.nil?
|
475
605
|
end
|
476
606
|
|
477
607
|
|