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.
@@ -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
 
@@ -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' + hex_string.bytesize.to_s)
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' + (self.to_s.bytesize*2).to_s)[0])
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 random key and init vector for AES keys.
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
@@ -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 keysas a SecureString class' do
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
@@ -84,8 +84,20 @@ describe "SecureString" do
84
84
  end
85
85
  end
86
86
 
87
- it 'should parse hex data with spaces' do
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.2
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-06-16 00:00:00 Z
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"