secure_string 1.3.2 → 1.3.3
Sign up to get free protection for your applications and to get access to all the features.
- 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"
|