twofish 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,33 @@
1
+ class Twofish
2
+
3
+ # Encryption modes.
4
+ #
5
+ # The only currently implemented modes are ECB (Electronic Code Book)
6
+ # and CBC (Cipher Block Chaining).
7
+ module Mode
8
+
9
+ # Electronic code book mode.
10
+ ECB = :ecb
11
+
12
+ # Cipher block chaining mode.
13
+ CBC = :cbc
14
+
15
+ # Array of all known modes.
16
+ ALL = [ CBC, ECB ]
17
+
18
+ # Default mode (ECB).
19
+ DEFAULT = ECB
20
+
21
+ # Takes a string or symbol and returns the lowercased
22
+ # symbol representation if this is a recognized mode.
23
+ # Otherwise, throws ArgumentError.
24
+ def self.validate(mode)
25
+ mode_sym = mode.nil? ? DEFAULT : mode.to_s.downcase.to_sym
26
+ raise ArgumentError, "unknown cipher mode #{mode.inspect}" unless ALL.include? mode_sym
27
+ mode_sym
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
@@ -0,0 +1,75 @@
1
+ class Twofish
2
+
3
+ # Implements padding modes to make plaintext into a complete
4
+ # number of blocks before encryption and to remove that padding
5
+ # after successful decryption.
6
+ #
7
+ # The only implemented padding schemes are :none and
8
+ # :zero_byte. Note that zero byte padding is potentially
9
+ # dangerous because if the plaintext terminates in
10
+ # zero bytes then these will be erroneously removed by #unpad.
11
+ # A more sensible padding scheme should be used in this case.
12
+ module Padding
13
+
14
+ # Use no padding.
15
+ NONE = :none
16
+
17
+ # Use zero byte padding.
18
+ ZERO_BYTE = :zero_byte
19
+
20
+ # Array of all known paddings.
21
+ ALL = [ NONE, ZERO_BYTE ]
22
+
23
+ # Default padding (none).
24
+ DEFAULT = NONE
25
+
26
+ # Takes a string or symbol and returns the lowercased
27
+ # symbol representation if this is a recognized padding scheme.
28
+ # Otherwise, throws ArgumentError.
29
+ def self.validate(scheme)
30
+ scheme_sym = scheme.nil? ? DEFAULT : scheme.to_s.downcase.to_sym
31
+ raise ArgumentError, "unknown padding scheme #{scheme.inspect}" unless ALL.include? scheme_sym
32
+ scheme_sym
33
+ end
34
+
35
+ # Pad the given plaintext to a complete number of blocks,
36
+ # returning a new string. See #pad!.
37
+ def self.pad(plaintext, block_size, scheme=DEFAULT)
38
+ self.pad!(plaintext.dup, block_size, scheme)
39
+ end
40
+
41
+ # Pad the given plaintext to a complete number of blocks,
42
+ # returning a new string. If the padding scheme is :none
43
+ # and the plaintext is not a whole number of blocks then
44
+ # ArgumentError is thrown.
45
+ def self.pad!(plaintext, block_size, scheme=DEFAULT)
46
+ remainder = plaintext.length % block_size
47
+ case validate(scheme)
48
+ when NONE
49
+ raise ArgumentError, "no padding scheme specified and plaintext length is not a multiple of the block size" unless remainder.zero?
50
+ plaintext
51
+ when ZERO_BYTE
52
+ remainder.zero? ? plaintext : plaintext << "\0" * (block_size - remainder)
53
+ end
54
+ end
55
+
56
+ # Unpad the given plaintext using the given scheme, returning
57
+ # a new string. See #unpad!.
58
+ def self.unpad(plaintext, block_size, scheme=DEFAULT)
59
+ self.unpad!(plaintext.dup, block_size, scheme)
60
+ end
61
+
62
+ # Unpad the given plaintext in place using the given scheme.
63
+ def self.unpad!(plaintext, block_size, scheme=DEFAULT)
64
+ case validate(scheme)
65
+ when NONE
66
+ plaintext.dup
67
+ when ZERO_BYTE
68
+ plaintext.sub(/\000+\Z/, '')
69
+ end
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+
data/test/benchmark.rb ADDED
@@ -0,0 +1,17 @@
1
+
2
+ require 'benchmark'
3
+
4
+ require 'twofish'
5
+
6
+ m = 10000
7
+ n = 100000
8
+ key = ['5d9d4eeffa9151575524f115815a12e0'].pack('H*')
9
+ block = ['e75449212beef9f4a390bd860a640941'].pack('H*')
10
+ tf = Twofish.new(key)
11
+
12
+ Benchmark.bm(7) do |x|
13
+ x.report('new') { m.times do ; Twofish.new(key) ; end }
14
+ x.report('encrypt') { n.times do ; tf.encrypt(block) ; end }
15
+ x.report('decrypt') { n.times do ; tf.decrypt(block) ; end }
16
+ end
17
+
data/test/test_twofish.rb CHANGED
@@ -37,7 +37,7 @@ class TestBasics < Test::Unit::TestCase
37
37
  end
38
38
 
39
39
  def test_default_mode
40
- assert_equal(Mode::DEFAULT, Mode::ECB)
40
+ assert_equal(Twofish::Mode::DEFAULT, Twofish::Mode::ECB)
41
41
  end
42
42
 
43
43
  end
@@ -74,7 +74,7 @@ class TestEcbEncryption < TestBasics
74
74
  key = pack_bytes('37fe26ff1cf66175f5ddf4c33b97a205')
75
75
  tf = Twofish.new(key)
76
76
  assert_raise ArgumentError do
77
- ciphertext = tf.encrypt(plaintext)
77
+ tf.encrypt(plaintext)
78
78
  end
79
79
  end
80
80
 
@@ -86,6 +86,29 @@ class TestEcbEncryption < TestBasics
86
86
  assert_equal(plaintext, tf.decrypt(ciphertext))
87
87
  end
88
88
 
89
+ def test_utf8_plaintext_invalid_length
90
+ # this string is 16 chars in length, but 18 bytes
91
+ plaintext = pack_bytes('72c3a973657276c3a96573') + # 11 bytes, 9 chars
92
+ '1234567' # 7 bytes, 7 chars
93
+ plaintext.force_encoding('UTF-8')
94
+ key = pack_bytes('37fe26ff1cf66175f5ddf4c33b97a205')
95
+ tf = Twofish.new(key)
96
+ assert_raise ArgumentError do
97
+ tf.encrypt(plaintext)
98
+ end
99
+ end
100
+
101
+ def test_utf8_plaintext
102
+ # this string is 16 bytes in length (but 14 chars)
103
+ plaintext = pack_bytes('72c3a973657276c3a96573') + # 11 bytes, 9 chars
104
+ '12345' # 5 bytes, 5 chars
105
+ plaintext.force_encoding('UTF-8')
106
+ key = pack_bytes('37fe26ff1cf66175f5ddf4c33b97a205')
107
+ tf = Twofish.new(key)
108
+ ciphertext = tf.encrypt(plaintext)
109
+ assert_equal(plaintext.force_encoding('ASCII-8BIT'), tf.decrypt(ciphertext))
110
+ end
111
+
89
112
  private
90
113
 
91
114
  # Convert ASCII hex representation into binary.
@@ -122,10 +145,13 @@ class TestCbcEncryption < TestBasics
122
145
 
123
146
  def test_encryption_decryption_random_iv
124
147
  tf = Twofish.new(NULL_KEY_16_BYTES, :mode => :cbc)
125
- ciphertext = tf.encrypt(LONG_PLAINTEXT)
148
+ plaintext = LONG_PLAINTEXT
149
+ ciphertext = tf.encrypt(plaintext)
150
+ assert_equal(LONG_PLAINTEXT, plaintext)
126
151
  iv = tf.iv
127
152
  tf2 = Twofish.new(NULL_KEY_16_BYTES, :mode => :cbc, :iv => iv)
128
153
  assert_equal(LONG_PLAINTEXT, tf2.decrypt(ciphertext))
154
+ assert_equal(LONG_PLAINTEXT, plaintext)
129
155
  end
130
156
 
131
157
  def test_encryption_given_null_iv
@@ -265,29 +291,29 @@ class TestPadding < TestBasics
265
291
  end
266
292
 
267
293
  def test_symbolize_padding
268
- assert_equal(:zero_byte, Padding::validate('zero_byte'))
294
+ assert_equal(:zero_byte, Twofish::Padding::validate('zero_byte'))
269
295
  end
270
296
 
271
297
  def test_pad_none
272
298
  assert_raise ArgumentError do
273
- Padding::pad(TO_PAD, BLOCK_SIZE, :none)
299
+ Twofish::Padding::pad(TO_PAD, BLOCK_SIZE, :none)
274
300
  end
275
301
  end
276
302
 
277
303
  def test_unpad_none
278
- assert_equal(TO_PAD+"\0"*10, Padding::unpad(TO_PAD+"\0"*10, BLOCK_SIZE, :none))
304
+ assert_equal(TO_PAD+"\0"*10, Twofish::Padding::unpad(TO_PAD+"\0"*10, BLOCK_SIZE, :none))
279
305
  end
280
306
 
281
307
  def test_pad_zero_byte
282
- assert_equal(TO_PAD+"\0"*10, Padding::pad(TO_PAD, BLOCK_SIZE, :zero_byte))
308
+ assert_equal(TO_PAD+"\0"*10, Twofish::Padding::pad(TO_PAD, BLOCK_SIZE, :zero_byte))
283
309
  end
284
310
 
285
311
  def test_unpad_zero_byte
286
- assert_equal(TO_PAD, Padding::unpad(TO_PAD+"\0"*10, BLOCK_SIZE, :zero_byte))
312
+ assert_equal(TO_PAD, Twofish::Padding::unpad(TO_PAD+"\0"*10, BLOCK_SIZE, :zero_byte))
287
313
  end
288
314
 
289
315
  def test_pad_block_size_zero_byte
290
316
  to_pad = TO_PAD * BLOCK_SIZE
291
- assert_equal(to_pad, Padding::pad(to_pad, BLOCK_SIZE, :zero_byte))
317
+ assert_equal(to_pad, Twofish::Padding::pad(to_pad, BLOCK_SIZE, :zero_byte))
292
318
  end
293
319
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: twofish
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-11-21 00:00:00.000000000Z
12
+ date: 2012-03-08 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Twofish symmetric cipher in pure Ruby with ECB and CBC cipher modes derived
15
15
  from an original Perl implementation by Guido Flohr
@@ -22,6 +22,10 @@ extra_rdoc_files:
22
22
  - README.rdoc
23
23
  files:
24
24
  - lib/twofish.rb
25
+ - lib/twofish/mode.rb
26
+ - lib/twofish/padding.rb
27
+ - lib/string.rb
28
+ - test/benchmark.rb
25
29
  - test/test_twofish.rb
26
30
  - LICENSE
27
31
  - Rakefile