secure_string 1.3.2 → 1.3.3
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.
- data/README.rdoc +5 -1
- data/lib/secure_string.rb +6 -0
- data/lib/securize_string/binary_string_data_methods.rb +22 -2
- data/lib/securize_string/cipher_methods.rb +30 -1
- data/spec/binary_string_data_methods_spec.rb +8 -0
- data/spec/cipher_methods_spec.rb +44 -1
- data/spec/secure_string_spec.rb +25 -1
- metadata +2 -2
data/README.rdoc
CHANGED
@@ -234,6 +234,10 @@ or directly via e-mail at:
|
|
234
234
|
mailto:jeff@paploo.net
|
235
235
|
|
236
236
|
= Version History
|
237
|
+
[1.3.3 - 2011-Jul-19] Added new functionality
|
238
|
+
* (FEATURE) Added PKCS5 v2 keygen from a passphrase support.
|
239
|
+
* (FEATURE) Addition of escaped hex string literal support for pasting into code.
|
240
|
+
* (FIX) Hex conversion is more consistently fast now.
|
237
241
|
[1.3.2 - 2011-Jun-15] Changed minimum requirements from 1.8.6 to 1.8.7.
|
238
242
|
* (CHANGE) Minimum requirements are 1.8.7 due to too many bugs in 1.8.6.
|
239
243
|
* (FIX) Update to the documentation to reflect 1.8.7 support.
|
@@ -280,7 +284,7 @@ mailto:jeff@paploo.net
|
|
280
284
|
* See what happens when including SecurizeString into an object that is not a String.
|
281
285
|
What are the expected root methods? +to_s+, <tt>self.new(string)</tt> are two
|
282
286
|
that I know of.
|
283
|
-
|
287
|
+
* Add HMAC support.
|
284
288
|
|
285
289
|
= License
|
286
290
|
|
data/lib/secure_string.rb
CHANGED
@@ -30,6 +30,12 @@ class SecureString < String
|
|
30
30
|
return data_to_hex
|
31
31
|
end
|
32
32
|
|
33
|
+
# Add a method to convert the internal binary dat into an escaped hex string
|
34
|
+
# which is suitable for pasting into Ruby and Javascript source files.
|
35
|
+
def to_escaped_hex
|
36
|
+
return data_to_escaped_hex
|
37
|
+
end
|
38
|
+
|
33
39
|
# Override the default inspect to return the hexidecimal
|
34
40
|
# representation of the data contained in this string.
|
35
41
|
def inspect
|
@@ -25,7 +25,7 @@ module SecurizeString
|
|
25
25
|
case data_type_hint
|
26
26
|
when :hex
|
27
27
|
hex_string = value.to_s.delete('^0-9a-fA-F')
|
28
|
-
data_string = [hex_string].pack('H'
|
28
|
+
data_string = [hex_string].pack('H*')
|
29
29
|
when :data
|
30
30
|
data_string = value.to_s
|
31
31
|
when :int
|
@@ -46,7 +46,7 @@ module SecurizeString
|
|
46
46
|
|
47
47
|
# Returns the hexidecimal string representation of the data.
|
48
48
|
def data_to_hex
|
49
|
-
return (self.to_s.empty? ? '' : self.to_s.unpack('H'
|
49
|
+
return (self.to_s.empty? ? '' : self.to_s.unpack('H*')[0])
|
50
50
|
end
|
51
51
|
|
52
52
|
# Returns the data converted from hexidecimal into an integer.
|
@@ -56,6 +56,26 @@ module SecurizeString
|
|
56
56
|
return (self.to_s.empty? ? 0 : self.data_to_hex.hex)
|
57
57
|
end
|
58
58
|
|
59
|
+
# Returns an escaped hex string representation of the data.
|
60
|
+
#
|
61
|
+
# This hex string is compatible with Ruby and Javascript.
|
62
|
+
def data_to_escaped_hex
|
63
|
+
# First we convert the string into a packed hex string.
|
64
|
+
hex_string = self.unpack('H*')[0]
|
65
|
+
|
66
|
+
# Now we grab two elements at a time, prefix it, and add it to the buffer.
|
67
|
+
ptr = 0
|
68
|
+
len = self.bytesize
|
69
|
+
outbuf = ""
|
70
|
+
while(ptr<(len*2))
|
71
|
+
outbuf << '\x' + hex_string[ptr,2]
|
72
|
+
ptr+=2
|
73
|
+
end
|
74
|
+
|
75
|
+
# Now we return the buffer
|
76
|
+
return outbuf
|
77
|
+
end
|
78
|
+
|
59
79
|
end
|
60
80
|
|
61
81
|
end
|
@@ -28,12 +28,41 @@ module SecurizeString
|
|
28
28
|
return [cipher.random_key, cipher.random_iv].map {|s| self.new(s)}
|
29
29
|
end
|
30
30
|
|
31
|
-
# A convenience method for generating a
|
31
|
+
# A convenience method for generating a cipher key from a passphrase
|
32
|
+
# using PKCS5 v2 standards. The key and the salt may be any string.
|
33
|
+
#
|
34
|
+
# This also derives a predictable initialization vector from the given
|
35
|
+
# passphrase in a manor consistent with RFC2898, though it is better to
|
36
|
+
# generate a random IV with each encryption of the same data if possible.
|
37
|
+
#
|
38
|
+
# Note that the OpenSSL::Cipher#pkcs5_keyivgen method is not PKCS5 v2
|
39
|
+
# compliant, and therefore will not be implemented.
|
40
|
+
def cipher_passphrase_keygen(cipher_name, passphrase, salt, iterations=2048)
|
41
|
+
# The first pits of a PBKDF2 are the same wether I build the key and IV
|
42
|
+
# at once, but when an IV is built in the RFC2898 standards, they do it
|
43
|
+
# this way.
|
44
|
+
cipher = OpenSSL::Cipher.new(cipher_name.to_s)
|
45
|
+
cipher.encrypt
|
46
|
+
key_and_iv = OpenSSL::PKCS5.pbkdf2_hmac_sha1(passphrase.to_s, salt.to_s, iterations.to_i, cipher.key_len+cipher.iv_len)
|
47
|
+
return [key_and_iv[0,cipher.key_len], key_and_iv[cipher.key_len, cipher.iv_len]].map {|s| self.new(s)}
|
48
|
+
end
|
49
|
+
|
50
|
+
# A convenience method for generating a random key and init vector for AES
|
51
|
+
# encryption.
|
52
|
+
#
|
32
53
|
# Defaults to a key length of 256.
|
33
54
|
def aes_keygen(key_len=256)
|
34
55
|
return cipher_keygen("aes-#{key_len.to_i}-cbc")
|
35
56
|
end
|
36
57
|
|
58
|
+
# A convenience method for generating a key and init vector from a passphrase
|
59
|
+
# for AES encryption.
|
60
|
+
#
|
61
|
+
# Defaults to a key length of 256.
|
62
|
+
def aes_passphrase_keygen(key_len, passphrase, salt, iterations=2048)
|
63
|
+
return cipher_passphrase_keygen("aes-#{key_len.to_i}-cbc", passphrase, salt, iterations)
|
64
|
+
end
|
65
|
+
|
37
66
|
end
|
38
67
|
|
39
68
|
# Adds instance methods for OpenSSL::Cipher support, including AES encryption,
|
@@ -47,6 +47,14 @@ describe "SecurityString" do
|
|
47
47
|
ss.data_to_i.should == 0
|
48
48
|
end
|
49
49
|
|
50
|
+
it 'should be able to convert to an escaped hex string' do
|
51
|
+
@messages.each do |message|
|
52
|
+
ss = SecureString.new(message[:string])
|
53
|
+
ss.data_to_escaped_hex.delete('^0-9A-Fa-f').should == message[:hex]
|
54
|
+
ss.data_to_escaped_hex.should == message[:string].each_byte.map {|b| '\x' + ('%02x' % b)}.join
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
50
58
|
end
|
51
59
|
|
52
60
|
end
|
data/spec/cipher_methods_spec.rb
CHANGED
@@ -18,11 +18,54 @@ describe "SecureString" do
|
|
18
18
|
iv.should be_kind_of(SecureString)
|
19
19
|
end
|
20
20
|
|
21
|
+
it 'should provide a passphrase keygen helper' do
|
22
|
+
SecureString.should respond_to(:cipher_passphrase_keygen)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should provide passphrase generated keys as a SecureString class' do
|
26
|
+
key, iv = SecureString.cipher_passphrase_keygen('DES', 'some passphrase', 'my salt')
|
27
|
+
|
28
|
+
key.should_not be_nil
|
29
|
+
iv.should_not be_nil
|
30
|
+
|
31
|
+
key.should be_kind_of(SecureString)
|
32
|
+
iv.should be_kind_of(SecureString)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should generate keys from passphrases with 2048 rounds by default' do
|
36
|
+
default_key, default_iv = SecureString.cipher_passphrase_keygen('DES', 'some passphrase', 'my_salt')
|
37
|
+
|
38
|
+
key, iv = SecureString.cipher_passphrase_keygen('DES', 'some passphrase', 'my_salt', 2048)
|
39
|
+
key.should == default_key
|
40
|
+
iv.should == default_iv
|
41
|
+
|
42
|
+
key, iv = SecureString.cipher_passphrase_keygen('DES', 'some passphrase', 'my_salt', 1024)
|
43
|
+
key.should_not == default_key
|
44
|
+
key.should_not == default_iv
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should generate a predictable key from a given passphrase' do
|
48
|
+
SecureString.cipher_passphrase_keygen('DES', 'some passphrase', 'my_salt').should == SecureString.cipher_passphrase_keygen('DES', 'some passphrase', 'my_salt')
|
49
|
+
SecureString.cipher_passphrase_keygen('DES', 'some passphrase', 'my_salt').should_not == SecureString.cipher_passphrase_keygen('DES', 'some passphrase', 'another_salt')
|
50
|
+
SecureString.cipher_passphrase_keygen('DES', 'some passphrase', 'my_salt').should_not == SecureString.cipher_passphrase_keygen('DES', 'another passphrase', 'my_salt')
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should provide an AES passphrase keygen helper' do
|
54
|
+
SecureString.should respond_to(:aes_passphrase_keygen)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should, for any AES key size, passthrough to the cipher passphrase keygen' do
|
58
|
+
[128,192,256].each do |bits|
|
59
|
+
SecureString.aes_passphrase_keygen(bits, 'passphrase', 'salt').should == SecureString.cipher_passphrase_keygen("aes-#{bits}-cbc", 'passphrase', 'salt')
|
60
|
+
SecureString.aes_passphrase_keygen(bits, 'passphrase', 'salt', 1024).should == SecureString.cipher_passphrase_keygen("aes-#{bits}-cbc", 'passphrase', 'salt', 1024)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
21
64
|
it 'should provide an AES keygen helper' do
|
22
65
|
SecureString.should respond_to(:aes_keygen)
|
23
66
|
end
|
24
67
|
|
25
|
-
it 'should provide generated AES
|
68
|
+
it 'should provide generated AES keys as a SecureString class' do
|
26
69
|
key, iv = SecureString.aes_keygen
|
27
70
|
|
28
71
|
key.should_not be_nil
|
data/spec/secure_string_spec.rb
CHANGED
@@ -84,8 +84,20 @@ describe "SecureString" do
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
-
it 'should
|
87
|
+
it 'should implement to_escaped_hex' do
|
88
|
+
if( RUBY_VERSION >= '1.9.0' )
|
89
|
+
SecureString.instance_methods.should include(:to_escaped_hex)
|
90
|
+
else
|
91
|
+
SecureString.instance_methods.should include('to_escaped_hex')
|
92
|
+
end
|
88
93
|
|
94
|
+
@messages.each do |message|
|
95
|
+
ss = SecureString.new(message[:string])
|
96
|
+
ss.to_escaped_hex.should == ss.data_to_escaped_hex
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'should parse hex data with spaces' do
|
89
101
|
data = <<-DATA
|
90
102
|
a766a602 b65cffe7 73bcf258 26b322b3 d01b1a97 2684ef53 3e3b4b7f 53fe3762
|
91
103
|
24c08e47 e959b2bc 3b519880 b9286568 247d110f 70f5c5e2 b4590ca3 f55f52fe
|
@@ -105,6 +117,18 @@ describe "SecureString" do
|
|
105
117
|
OpenSSL::Digest::SHA.hexdigest(ss).should == "c9f160777d4086fe8095fba58b7e20c228a4006b"
|
106
118
|
end
|
107
119
|
|
120
|
+
it 'should parse hex data with escape characters' do
|
121
|
+
# Create the string with escape characters in there.
|
122
|
+
data = '\x48\x65\x6c\x6c\x6f'
|
123
|
+
|
124
|
+
# Make sure Ruby didn't convert them to the actual string.
|
125
|
+
data.should_not == 'Hello'
|
126
|
+
|
127
|
+
# Now see if SecureString can convert it as hex data.
|
128
|
+
ss = SecureString.new(data, :type => :hex)
|
129
|
+
ss.should == 'Hello'
|
130
|
+
end
|
131
|
+
|
108
132
|
if( RUBY_VERSION >= '1.9.0' )
|
109
133
|
describe 'Encodings' do
|
110
134
|
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: secure_string
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 1.3.
|
5
|
+
version: 1.3.3
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Jeff Reinecke
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-07-19 00:00:00 Z
|
14
14
|
dependencies: []
|
15
15
|
|
16
16
|
description: " A String subclass to simplify handling of:\n 1. Binary data, including HEX encoding and Bin64 encoding.\n 2. Encryption such as RSA, AES, and digest methods such as SHA and MD5.\n"
|