1require_relative 'utils'
2
3if defined?(OpenSSL)
4
5class OpenSSL::TestCipher < Test::Unit::TestCase
6
7  class << self
8
9    def has_cipher?(name)
10      ciphers = OpenSSL::Cipher.ciphers
11      # redefine method so we can use the cached ciphers value from the closure
12      # and need not recompute the list each time
13      define_singleton_method :has_cipher? do |name|
14        ciphers.include?(name)
15      end
16      has_cipher?(name)
17    end
18
19    def has_ciphers?(list)
20      list.all? { |name| has_cipher?(name) }
21    end
22
23  end
24
25  def setup
26    @c1 = OpenSSL::Cipher::Cipher.new("DES-EDE3-CBC")
27    @c2 = OpenSSL::Cipher::DES.new(:EDE3, "CBC")
28    @key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
29    @iv = "\0\0\0\0\0\0\0\0"
30    @hexkey = "0000000000000000000000000000000000000000000000"
31    @hexiv = "0000000000000000"
32    @data = "DATA"
33  end
34
35  def teardown
36    @c1 = @c2 = nil
37  end
38
39  def test_crypt
40    @c1.encrypt.pkcs5_keyivgen(@key, @iv)
41    @c2.encrypt.pkcs5_keyivgen(@key, @iv)
42    s1 = @c1.update(@data) + @c1.final
43    s2 = @c2.update(@data) + @c2.final
44    assert_equal(s1, s2, "encrypt")
45
46    @c1.decrypt.pkcs5_keyivgen(@key, @iv)
47    @c2.decrypt.pkcs5_keyivgen(@key, @iv)
48    assert_equal(@data, @c1.update(s1)+@c1.final, "decrypt")
49    assert_equal(@data, @c2.update(s2)+@c2.final, "decrypt")
50  end
51
52  def test_info
53    assert_equal("DES-EDE3-CBC", @c1.name, "name")
54    assert_equal("DES-EDE3-CBC", @c2.name, "name")
55    assert_kind_of(Fixnum, @c1.key_len, "key_len")
56    assert_kind_of(Fixnum, @c1.iv_len, "iv_len")
57  end
58
59  def test_dup
60    assert_equal(@c1.name, @c1.dup.name, "dup")
61    assert_equal(@c1.name, @c1.clone.name, "clone")
62    @c1.encrypt
63    @c1.key = @key
64    @c1.iv = @iv
65    tmpc = @c1.dup
66    s1 = @c1.update(@data) + @c1.final
67    s2 = tmpc.update(@data) + tmpc.final
68    assert_equal(s1, s2, "encrypt dup")
69  end
70
71  def test_reset
72    @c1.encrypt
73    @c1.key = @key
74    @c1.iv = @iv
75    s1 = @c1.update(@data) + @c1.final
76    @c1.reset
77    s2 = @c1.update(@data) + @c1.final
78    assert_equal(s1, s2, "encrypt reset")
79  end
80
81  def test_empty_data
82    @c1.encrypt
83    assert_raise(ArgumentError){ @c1.update("") }
84  end
85
86  def test_initialize
87    assert_raise(RuntimeError) {@c1.__send__(:initialize, "DES-EDE3-CBC")}
88    assert_raise(RuntimeError) {OpenSSL::Cipher.allocate.final}
89  end
90
91  def test_ctr_if_exists
92    begin
93      cipher = OpenSSL::Cipher.new('aes-128-ctr')
94      cipher.encrypt
95      cipher.pkcs5_keyivgen('password')
96      c = cipher.update('hello,world') + cipher.final
97      cipher.decrypt
98      cipher.pkcs5_keyivgen('password')
99      assert_equal('hello,world', cipher.update(c) + cipher.final)
100    end
101  end if has_cipher?('aes-128-ctr')
102
103  if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00907000
104    def test_ciphers
105      OpenSSL::Cipher.ciphers.each{|name|
106        next if /netbsd/ =~ RUBY_PLATFORM && /idea|rc5/i =~ name
107        assert(OpenSSL::Cipher::Cipher.new(name).is_a?(OpenSSL::Cipher::Cipher))
108      }
109    end
110
111    def test_AES
112      pt = File.read(__FILE__)
113      %w(ECB CBC CFB OFB).each{|mode|
114        c1 = OpenSSL::Cipher::AES256.new(mode)
115        c1.encrypt
116        c1.pkcs5_keyivgen("passwd")
117        ct = c1.update(pt) + c1.final
118
119        c2 = OpenSSL::Cipher::AES256.new(mode)
120        c2.decrypt
121        c2.pkcs5_keyivgen("passwd")
122        assert_equal(pt, c2.update(ct) + c2.final)
123      }
124    end
125
126    def test_AES_crush
127      500.times do
128        assert_nothing_raised("[Bug #2768]") do
129          # it caused OpenSSL SEGV by uninitialized key
130          OpenSSL::Cipher::AES128.new("ECB").update "." * 17
131        end
132      end
133    end
134  end
135
136  if has_ciphers?(['aes-128-gcm', 'aes-192-gcm', 'aes-128-gcm'])
137
138    def test_authenticated
139      cipher = OpenSSL::Cipher.new('aes-128-gcm')
140      assert(cipher.authenticated?)
141      cipher = OpenSSL::Cipher.new('aes-128-cbc')
142      refute(cipher.authenticated?)
143    end
144
145    def test_aes_gcm
146      ['aes-128-gcm', 'aes-192-gcm', 'aes-128-gcm'].each do |algo|
147        pt = "You should all use Authenticated Encryption!"
148        cipher, key, iv = new_encryptor(algo)
149
150        cipher.auth_data = "aad"
151        ct  = cipher.update(pt) + cipher.final
152        tag = cipher.auth_tag
153        assert_equal(16, tag.size)
154
155        decipher = new_decryptor(algo, key, iv)
156        decipher.auth_tag = tag
157        decipher.auth_data = "aad"
158
159        assert_equal(pt, decipher.update(ct) + decipher.final)
160      end
161    end
162
163    def test_aes_gcm_short_tag
164      ['aes-128-gcm', 'aes-192-gcm', 'aes-128-gcm'].each do |algo|
165        pt = "You should all use Authenticated Encryption!"
166        cipher, key, iv = new_encryptor(algo)
167
168        cipher.auth_data = "aad"
169        ct  = cipher.update(pt) + cipher.final
170        tag = cipher.auth_tag(8)
171        assert_equal(8, tag.size)
172
173        decipher = new_decryptor(algo, key, iv)
174        decipher.auth_tag = tag
175        decipher.auth_data = "aad"
176
177        assert_equal(pt, decipher.update(ct) + decipher.final)
178      end
179    end
180
181    def test_aes_gcm_wrong_tag
182      pt = "You should all use Authenticated Encryption!"
183      cipher, key, iv = new_encryptor('aes-128-gcm')
184
185      cipher.auth_data = "aad"
186      ct  = cipher.update(pt) + cipher.final
187      tag = cipher.auth_tag
188
189      decipher = new_decryptor('aes-128-gcm', key, iv)
190      tag.setbyte(-1, (tag.getbyte(-1) + 1) & 0xff)
191      decipher.auth_tag = tag
192      decipher.auth_data = "aad"
193
194      assert_raise OpenSSL::Cipher::CipherError do
195        decipher.update(ct) + decipher.final
196      end
197    end
198
199    def test_aes_gcm_wrong_auth_data
200      pt = "You should all use Authenticated Encryption!"
201      cipher, key, iv = new_encryptor('aes-128-gcm')
202
203      cipher.auth_data = "aad"
204      ct  = cipher.update(pt) + cipher.final
205      tag = cipher.auth_tag
206
207      decipher = new_decryptor('aes-128-gcm', key, iv)
208      decipher.auth_tag = tag
209      decipher.auth_data = "daa"
210
211      assert_raise OpenSSL::Cipher::CipherError do
212        decipher.update(ct) + decipher.final
213      end
214    end
215
216    def test_aes_gcm_wrong_ciphertext
217      pt = "You should all use Authenticated Encryption!"
218      cipher, key, iv = new_encryptor('aes-128-gcm')
219
220      cipher.auth_data = "aad"
221      ct  = cipher.update(pt) + cipher.final
222      tag = cipher.auth_tag
223
224      decipher = new_decryptor('aes-128-gcm', key, iv)
225      decipher.auth_tag = tag
226      decipher.auth_data = "aad"
227
228      assert_raise OpenSSL::Cipher::CipherError do
229        decipher.update(ct[0..-2] << ct[-1].succ) + decipher.final
230      end
231    end
232
233  end
234
235  private
236
237  def new_encryptor(algo)
238    cipher = OpenSSL::Cipher.new(algo)
239    cipher.encrypt
240    key = cipher.random_key
241    iv = cipher.random_iv
242    [cipher, key, iv]
243  end
244
245  def new_decryptor(algo, key, iv)
246    OpenSSL::Cipher.new(algo).tap do |cipher|
247      cipher.decrypt
248      cipher.key = key
249      cipher.iv = iv
250    end
251  end
252
253end
254
255end
256