twofish 1.0.2 → 1.0.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.
@@ -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