crypt 1.1.3 → 2.2.1

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.
@@ -1,4 +1,5 @@
1
- # rijndael.rb Richard Kernahan <kernighan_rich@rubyforge.org>
1
+ require 'crypt/cbc'
2
+ # rijndael.rb Richard Kernahan
2
3
 
3
4
  # Adapted from the reference C implementation:
4
5
  # rijndael-alg-ref.c v2.2 March 2002
@@ -6,53 +7,51 @@
6
7
  # authors: Paulo Barreto and Vincent Rijmen
7
8
  # This code is placed in the public domain.
8
9
 
10
+ require 'crypt/rijndael-tables'
11
+
9
12
  module Crypt
10
13
  class Rijndael
11
14
 
12
- require 'crypt/cbc'
13
15
  include Crypt::CBC
14
-
15
- require 'crypt/rijndael-tables'
16
16
  include Crypt::RijndaelTables
17
-
18
-
17
+
19
18
  def initialize(userKey, keyBits = 256, blockBits = 128)
20
19
  case keyBits
21
- when 128
20
+ when 128
22
21
  @keyWords = 4
23
- when 192
22
+ when 192
24
23
  @keyWords = 6
25
24
  when 256
26
25
  @keyWords = 8
27
26
  else raise "The key must be 128, 192, or 256 bits long."
28
27
  end
29
-
28
+
30
29
  case (keyBits >= blockBits) ? keyBits : blockBits
31
- when 128
30
+ when 128
32
31
  @rounds = 10
33
- when 192
32
+ when 192
34
33
  @rounds = 12
35
34
  when 256
36
35
  @rounds = 14
37
36
  else raise "The key and block sizes must be 128, 192, or 256 bits long."
38
37
  end
39
-
38
+
40
39
  case blockBits
41
- when 128
40
+ when 128
42
41
  @blockSize = 16
43
42
  @blockWords = 4
44
43
  @shiftIndex = 0
45
- when 192
44
+ when 192
46
45
  @blockSize = 24
47
46
  @blockWords = 6
48
47
  @shiftIndex = 1
49
- when 256
48
+ when 256
50
49
  @blockSize = 32
51
50
  @blockWords = 8
52
51
  @shiftIndex = 2
53
52
  else raise "The block size must be 128, 192, or 256 bits long."
54
53
  end
55
-
54
+
56
55
  uk = userKey.unpack('C'*userKey.length)
57
56
  maxUsefulSizeOfUserKey = (keyBits/8)
58
57
  uk = uk[0..maxUsefulSizeOfUserKey-1] # truncate
@@ -68,22 +67,22 @@ class Rijndael
68
67
  @roundKeys = generate_key_schedule(@key, keyBits, blockBits)
69
68
  end
70
69
 
71
-
70
+
72
71
  def block_size
73
72
  return(@blockSize) # needed for CBC
74
73
  end
75
-
76
-
74
+
75
+
77
76
  def mul(a, b)
78
77
  if ((a ==0) | (b == 0))
79
- result = 0
78
+ result = 0
80
79
  else
81
80
  result = AlogTable[(LogTable[a] + LogTable[b]) % 255]
82
81
  end
83
82
  return(result)
84
83
  end
85
-
86
-
84
+
85
+
87
86
  def add_round_key(blockArray, roundKey)
88
87
  0.upto(3) { |i|
89
88
  0.upto(@blockWords) { |j|
@@ -92,8 +91,8 @@ class Rijndael
92
91
  }
93
92
  return(blockArray)
94
93
  end
95
-
96
-
94
+
95
+
97
96
  def shift_rows(blockArray, direction)
98
97
  tmp = []
99
98
  1.upto(3) { |i| # row zero remains unchanged
@@ -106,8 +105,8 @@ class Rijndael
106
105
  }
107
106
  return(blockArray)
108
107
  end
109
-
110
-
108
+
109
+
111
110
  def substitution(blockArray, sBox)
112
111
  # replace every byte of the input with the byte at that position in the S-box
113
112
  0.upto(3) { |i|
@@ -117,8 +116,8 @@ class Rijndael
117
116
  }
118
117
  return(blockArray)
119
118
  end
120
-
121
-
119
+
120
+
122
121
  def mix_columns(blockArray)
123
122
  mixed = [[], [], [], []]
124
123
  0.upto(@blockWords-1) { |j|
@@ -131,22 +130,22 @@ class Rijndael
131
130
  }
132
131
  return(mixed)
133
132
  end
134
-
135
-
133
+
134
+
136
135
  def inverse_mix_columns(blockArray)
137
136
  unmixed = [[], [], [], []]
138
137
  0.upto(@blockWords-1) { |j|
139
138
  0.upto(3) { |i|
140
139
  unmixed[i][j] = mul(0xe, blockArray[i][j]) ^
141
- mul(0xb, blockArray[(i + 1) % 4][j]) ^
140
+ mul(0xb, blockArray[(i + 1) % 4][j]) ^
142
141
  mul(0xd, blockArray[(i + 2) % 4][j]) ^
143
142
  mul(0x9, blockArray[(i + 3) % 4][j])
144
143
  }
145
144
  }
146
145
  return(unmixed)
147
146
  end
148
-
149
-
147
+
148
+
150
149
  def generate_key_schedule(k, keyBits, blockBits)
151
150
  tk = k[0..3][0..@keyWords-1] # using slice to get a copy instead of a reference
152
151
  keySched = []
@@ -162,7 +161,7 @@ class Rijndael
162
161
  end
163
162
  # while not enough round key material collected, calculate new values
164
163
  rconIndex = 0
165
- while (t < (@rounds+1)*@blockWords)
164
+ while (t < (@rounds+1)*@blockWords)
166
165
  0.upto(3) { |i|
167
166
  tk[i][0] ^= S[tk[(i + 1) % 4][@keyWords - 1]]
168
167
  }
@@ -184,8 +183,8 @@ class Rijndael
184
183
  tk[i][@keyWords/2] ^= S[tk[i][@keyWords/2 - 1]]
185
184
  }
186
185
  (@keyWords/2 + 1).upto(@keyWords - 1) { |j|
187
- 0.upto(3) { |i|
188
- tk[i][j] ^= tk[i][j-1]
186
+ 0.upto(3) { |i|
187
+ tk[i][j] ^= tk[i][j-1]
189
188
  }
190
189
  }
191
190
  end
@@ -200,8 +199,8 @@ class Rijndael
200
199
  end
201
200
  return(keySched)
202
201
  end
203
-
204
-
202
+
203
+
205
204
  def encrypt_byte_array(blockArray)
206
205
  blockArray = add_round_key(blockArray, @roundKeys[0])
207
206
  1.upto(@rounds - 1) { |round|
@@ -216,54 +215,59 @@ class Rijndael
216
215
  blockArray = add_round_key(blockArray, @roundKeys[@rounds])
217
216
  return(blockArray)
218
217
  end
219
-
220
-
218
+
219
+
221
220
  def encrypt_block(block)
221
+ block = block.force_encoding("ASCII-8BIT") if block.is_a?(String) # affordance
222
222
  raise "block must be #{@blockSize} bytes long" if (block.length() != @blockSize)
223
223
  blockArray = [[], [], [], []]
224
224
  0.upto(@blockSize - 1) { |pos|
225
- blockArray[pos % 4][pos / 4] = block[pos]
225
+ b = block[pos]
226
+ b = b.ord unless b.is_a?(Fixnum)
227
+ blockArray[pos % 4][pos / 4] = b
226
228
  }
227
229
  encryptedBlock = encrypt_byte_array(blockArray)
228
- encrypted = ""
230
+ encrypted = "".force_encoding("ASCII-8BIT") # stop ruby 2 using Unicode
229
231
  0.upto(@blockSize - 1) { |pos|
230
232
  encrypted << encryptedBlock[pos % 4][pos / 4]
231
233
  }
232
234
  return(encrypted)
233
235
  end
234
-
235
-
236
+
237
+
236
238
  def decrypt_byte_array(blockArray)
237
239
  # first special round without inverse_mix_columns
238
240
  # add_round_key is an involution - applying it a second time returns the original result
239
- blockArray = add_round_key(blockArray, @roundKeys[@rounds])
241
+ blockArray = add_round_key(blockArray, @roundKeys[@rounds])
240
242
  blockArray = substitution(blockArray,Si) # using inverse S-box
241
243
  blockArray = shift_rows(blockArray,1)
242
244
  (@rounds-1).downto(1) { |round|
243
245
  blockArray = add_round_key(blockArray, @roundKeys[round])
244
246
  blockArray = inverse_mix_columns(blockArray)
245
- blockArray = substitution(blockArray, Si)
247
+ blockArray = substitution(blockArray, Si)
246
248
  blockArray = shift_rows(blockArray, 1)
247
249
  }
248
250
  blockArray = add_round_key(blockArray, @roundKeys[0])
249
251
  return(blockArray)
250
252
  end
251
-
252
-
253
+
254
+
253
255
  def decrypt_block(block)
254
256
  raise "block must be #{@blockSize} bytes long" if (block.length() != @blockSize)
255
257
  blockArray = [[], [], [], []]
256
258
  0.upto(@blockSize - 1) { |pos|
257
- blockArray[pos % 4][pos / 4] = block[pos]
259
+ b = block[pos]
260
+ b = b.ord() unless b.is_a?(Fixnum) # make sure we have a byte, not a single-char string
261
+ blockArray[pos % 4][pos / 4] = b
258
262
  }
259
263
  decryptedBlock = decrypt_byte_array(blockArray)
260
- decrypted = ""
264
+ decrypted = "".force_encoding("ASCII-8BIT") # stop ruby 2 using Unicode
261
265
  0.upto(@blockSize - 1) { |pos|
262
266
  decrypted << decryptedBlock[pos % 4][pos / 4]
263
267
  }
264
268
  return(decrypted)
265
269
  end
266
-
267
-
270
+
271
+
272
+ end
268
273
  end
269
- end
@@ -0,0 +1,17 @@
1
+ class String
2
+
3
+ def ^(aString)
4
+ a = self.b.unpack('C'*(self.length))
5
+ b = aString.b.unpack('C'*(aString.length))
6
+ if (b.length < a.length)
7
+ (a.length - b.length).times { b << 0 }
8
+ end
9
+ xor = "".b()
10
+ 0.upto(a.length-1) { |pos|
11
+ x = a[pos] ^ b[pos]
12
+ xor << x.chr()
13
+ }
14
+ return(xor)
15
+ end
16
+
17
+ end
data/specs/blowfish.rb ADDED
@@ -0,0 +1,80 @@
1
+ require 'crypt/blowfish'
2
+ require 'fileutils'
3
+
4
+ describe "Crypt::Blowfish" do
5
+
6
+ it "has a blocksize of 8" do
7
+ bf = Crypt::Blowfish.new("Who is John Galt?") # Schneier's test key
8
+ expect(bf.block_size).to eql(8)
9
+ end
10
+
11
+ it "requires a key of 1-56 bytes" do
12
+ expect {
13
+ b0 = Crypt::Blowfish.new("")
14
+ }.to raise_error(RuntimeError)
15
+ expect {
16
+ b1 = Crypt::Blowfish.new("1")
17
+ }.not_to raise_error()
18
+ expect {
19
+ b56 = Crypt::Blowfish.new("1"*56)
20
+ }.not_to raise_error()
21
+ expect {
22
+ b57 = Crypt::Blowfish.new("1"*57)
23
+ }.to raise_error(RuntimeError)
24
+ end
25
+
26
+ it "encrypts and decrypts 4-byte pairs" do
27
+ bf = Crypt::Blowfish.new("Who is John Galt?")
28
+ orig_l, orig_r = [0xfedcba98, 0x76543210]
29
+ l, r = bf.encrypt_pair(orig_l, orig_r)
30
+ expect(l).to eql(0xcc91732b)
31
+ expect(r).to eql(0x8022f684)
32
+ l, r = bf.decrypt_pair(l, r)
33
+ expect(l).to eql(orig_l)
34
+ expect(r).to eql(orig_r)
35
+ end
36
+
37
+ it "encrypts and decrypts 8-byte blocks" do
38
+ bf = Crypt::Blowfish.new("Who is John Galt?")
39
+ block = "8 byte\u00cd" # unicode string of 8 bytes
40
+ encryptedBlock = bf.encrypt_block(block)
41
+ expect(encryptedBlock).to eql("\xC4G\xD3\xFD7\xF4\x1E\xD0".b())
42
+ decryptedBlock = bf.decrypt_block(encryptedBlock)
43
+ expect(decryptedBlock).to eql(block)
44
+ end
45
+
46
+ it "encrypts and decrypts strings" do
47
+ length = 30 + rand(26)
48
+ userkey = ""
49
+ length.times { userkey << rand(256).chr }
50
+ bf = Crypt::Blowfish.new(userkey)
51
+ string = "This is a string which is not a multiple of 8 characters long"
52
+ encryptedString = bf.encrypt_string(string)
53
+ decryptedString = bf.decrypt_string(encryptedString)
54
+ expect(decryptedString).to eql(string)
55
+ secondstring = "This is another string to check repetitive use."
56
+ encryptedString = bf.encrypt_string(secondstring)
57
+ decryptedString = bf.decrypt_string(encryptedString)
58
+ expect(decryptedString).to eql(secondstring)
59
+ end
60
+
61
+ it "encrypts and decrypts a file" do
62
+ plainText = "This is a multi-line string\nwhich is not a multiple of 8 \ncharacters long."
63
+ plainFile = File.new('plain.txt', 'wb+')
64
+ plainFile.puts(plainText)
65
+ plainFile.close()
66
+ bf = Crypt::Blowfish.new("Who is John Galt?")
67
+ bf.encrypt_file('plain.txt', 'crypt.txt')
68
+ bf.decrypt_file('crypt.txt', 'decrypt.txt')
69
+ decryptFile = File.new('decrypt.txt', 'rb')
70
+ decryptText = decryptFile.readlines().join('').chomp()
71
+ decryptFile.close()
72
+ expect(decryptText).to eql(plainText)
73
+ FileUtils.rm('plain.txt')
74
+ FileUtils.rm('crypt.txt')
75
+ FileUtils.rm('decrypt.txt')
76
+ end
77
+
78
+ end
79
+
80
+
data/specs/gost.rb ADDED
@@ -0,0 +1,48 @@
1
+ require 'crypt/gost'
2
+ require 'fileutils'
3
+
4
+ describe "Crypt::Gost" do
5
+
6
+ it "has a blocksize of 8" do
7
+ gost = Crypt::Gost.new("Whatever happened to Yuri Gagarin?")
8
+ expect(gost.block_size).to eql(8)
9
+ end
10
+
11
+ it "encrypts and decrypts 8-byte blocks" do
12
+ gost = Crypt::Gost.new("Whatever happened to Yuri?")
13
+ block = "norandom"
14
+ encryptedBlock = gost.encrypt_block(block)
15
+ expect(encryptedBlock).to eql(".Vy\xFF\x05\e3`".b())
16
+ decryptedBlock = gost.decrypt_block(encryptedBlock)
17
+ expect(decryptedBlock).to eql(block)
18
+ end
19
+
20
+ it "encrypts and decrypts strings" do
21
+ length = 25 + rand(12)
22
+ userkey = ""
23
+ length.times { userkey << rand(256).chr }
24
+ gost = Crypt::Gost.new(userkey)
25
+ string = "This is a string which is not a multiple of 8 characters long"
26
+ encryptedString = gost.encrypt_string(string)
27
+ decryptedString = gost.decrypt_string(encryptedString)
28
+ expect(decryptedString).to eql(string)
29
+ end
30
+
31
+ it "encrypts and decrypts a file" do
32
+ plainText = "This is a multi-line string\nwhich is not a multiple of 8 \ncharacters long."
33
+ plainFile = File.new('plain.txt', 'wb+')
34
+ plainFile.puts(plainText)
35
+ plainFile.close()
36
+ gost = Crypt::Gost.new("Whatever happened to Yuri?")
37
+ gost.encrypt_file('plain.txt', 'crypt.txt')
38
+ gost.decrypt_file('crypt.txt', 'decrypt.txt')
39
+ decryptFile = File.new('decrypt.txt', 'rb')
40
+ decryptText = decryptFile.readlines().join('').chomp()
41
+ decryptFile.close()
42
+ expect(decryptText).to eql(plainText)
43
+ FileUtils.rm('plain.txt')
44
+ FileUtils.rm('crypt.txt')
45
+ FileUtils.rm('decrypt.txt')
46
+ end
47
+
48
+ end
data/specs/idea.rb ADDED
@@ -0,0 +1,156 @@
1
+ require 'crypt/idea'
2
+ require 'fileutils'
3
+
4
+ describe "Crypt::IDEA" do
5
+
6
+ it "encrypts and decrypts 4-byte pairs" do
7
+ enc = Crypt::IDEA.new("Nothing can stop an idea whose time has come", Crypt::IDEA::ENCRYPT)
8
+ dec = Crypt::IDEA.new("Nothing can stop an idea whose time has come", Crypt::IDEA::DECRYPT)
9
+ origL = 0x385a41f2
10
+ origR = 0xe03e5930
11
+ cl, cr = enc.crypt_pair(origL, origR)
12
+ dl, dr = dec.crypt_pair(cl, cr)
13
+ expect(dl).to eql(origL)
14
+ expect(dr).to eql(origR)
15
+ end
16
+
17
+ it "encrypts and decrypts 8-byte blocks" do
18
+ enc = Crypt::IDEA.new("Nothing can stop an idea whose time has come", Crypt::IDEA::ENCRYPT)
19
+ dec = Crypt::IDEA.new("Nothing can stop an idea whose time has come", Crypt::IDEA::DECRYPT)
20
+ block = "norandom"
21
+ encryptedBlock = enc.encrypt_block(block)
22
+ expect(encryptedBlock).to eql("\xC2lzEU\x81\e_".b())
23
+ decryptedBlock = dec.decrypt_block(encryptedBlock)
24
+ expect(decryptedBlock).to eql(block)
25
+ end
26
+
27
+ it "encrypts and decrypts strings" do
28
+ enc = Crypt::IDEA.new("Nothing can stop an idea whose time has come", Crypt::IDEA::ENCRYPT)
29
+ dec = Crypt::IDEA.new("Nothing can stop an idea whose time has come", Crypt::IDEA::DECRYPT)
30
+ string = "This is a string which is not a multiple of 8 characters long"
31
+ encryptedString = enc.encrypt_string(string)
32
+ decryptedString = dec.decrypt_string(encryptedString)
33
+ expect(decryptedString).to eql(string)
34
+ end
35
+
36
+ it "encrypts and decrypts a file" do
37
+ plainText = "This is a multi-line string\nwhich is not a multiple of 8 \ncharacters long."
38
+ plainFile = File.new('plain.txt', 'wb+')
39
+ plainFile.puts(plainText)
40
+ plainFile.close()
41
+ enc = Crypt::IDEA.new("Nothing can stop an idea whose time has come", Crypt::IDEA::ENCRYPT)
42
+ dec = Crypt::IDEA.new("Nothing can stop an idea whose time has come", Crypt::IDEA::DECRYPT)
43
+ enc.encrypt_file('plain.txt', 'crypt.txt')
44
+ dec.decrypt_file('crypt.txt', 'decrypt.txt')
45
+ decryptFile = File.new('decrypt.txt', 'rb')
46
+ decryptText = decryptFile.readlines().join('').chomp()
47
+ decryptFile.close()
48
+ expect(decryptText).to eql(plainText)
49
+ FileUtils.rm('plain.txt')
50
+ FileUtils.rm('crypt.txt')
51
+ FileUtils.rm('decrypt.txt')
52
+ end
53
+
54
+ def h(s)
55
+ [s].pack("H*")
56
+ end
57
+
58
+ def n8(s)
59
+ h(s).unpack("n8")
60
+ end
61
+
62
+ def conform(key, plain, cipher)
63
+ enc = Crypt::IDEA.new("", Crypt::IDEA::ENCRYPT)
64
+ enc.subkeys = enc.generate_encryption_subkeys(key)
65
+ dec = Crypt::IDEA.new("", Crypt::IDEA::ENCRYPT)
66
+ dec.subkeys = dec.generate_decryption_subkeys(key)
67
+ encryptedBlock = enc.encrypt_block(plain)
68
+ expect(encryptedBlock).to eql(cipher.b())
69
+ decryptedBlock = dec.decrypt_block(encryptedBlock)
70
+ expect(decryptedBlock).to eql(plain)
71
+ end
72
+
73
+ def conform_dec(key, cipher, plain)
74
+ dec = Crypt::IDEA.new("", Crypt::IDEA::ENCRYPT)
75
+ dec.subkeys = dec.generate_decryption_subkeys(key)
76
+ decryptedBlock = dec.decrypt_block(cipher)
77
+ expect(decryptedBlock).to eql(plain)
78
+ end
79
+
80
+ it "conforms to enceryption test vectors" do
81
+ conform([0x8000,0,0,0,0,0,0,0], h("0000000000000000"), h("B1F5F7F87901370F"))
82
+ conform([0x4000,0,0,0,0,0,0,0], h("0000000000000000"), h("B3927DFFB6358626"))
83
+ conform([0x2000,0,0,0,0,0,0,0], h("0000000000000000"), h("E987E0029FB99785"))
84
+ conform([0x1000,0,0,0,0,0,0,0], h("0000000000000000"), h("754A03CE08DB7DAA"))
85
+ conform([0x0800,0,0,0,0,0,0,0], h("0000000000000000"), h("F015F9FB0CFC7E1C"))
86
+ conform([0x0400,0,0,0,0,0,0,0], h("0000000000000000"), h("69C9FE6007B8FCDF"))
87
+ conform([0x0200,0,0,0,0,0,0,0], h("0000000000000000"), h("8DA7BC0E63B40DD0"))
88
+ conform([0x0100,0,0,0,0,0,0,0], h("0000000000000000"), h("2C49BF7DE28C666B"))
89
+ conform([ 0x80,0,0,0,0,0,0,0], h("0000000000000000"), h("9A4717E8F935712B"))
90
+ conform([0,0x8000,0,0,0,0,0,0], h("0000000000000000"), h("95A96731978C1B9A"))
91
+ conform([0,0,0x8000,0,0,0,0,0], h("0000000000000000"), h("398BD9A59E9F5DDB"))
92
+ conform([0,0,0,0x8000,0,0,0,0], h("0000000000000000"), h("AC1D8708AF0A37EE"))
93
+ conform([0,0,0,0,0x8000,0,0,0], h("0000000000000000"), h("FAE3FA7B8DB08800"))
94
+ conform([0,0,0,0,0,0x8000,0,0], h("0000000000000000"), h("B5803F82C0633F01"))
95
+ conform([0,0,0,0,0, 0x10,0,0], h("0000000000000000"), h("9E25090B7D4EF24E"))
96
+ conform([0,0,0,0,0, 0x8,0,0], h("0000000000000000"), h("EF62C1109F374AA8"))
97
+ conform([0,0,0,0,0, 0x2,0,0], h("0000000000000000"), h("5F0CCFE5EB0F19A8"))
98
+ conform([0xffff,0xffff,0xffff,0xffff,0xffff,0x1,0x000f,0xffff], h("0000000000000000"), h("2E4329C12455430B"))
99
+ # had problems with bits 95 to 108 inclusive, fixed by final % USHORT on mul()
100
+ conform([0,0,0,0,0, 0x1,0,0], h("0000000000000000"), h("FCC40014010D617C"))
101
+ conform([0,0,0,0,0,0,0x8000,0], h("0000000000000000"), h("705D780834A498DA"))
102
+ conform([0,0,0,0,0,0,0x4000,0], h("0000000000000000"), h("9BCA7BF025B38A68"))
103
+ conform([0,0,0,0,0,0,0x2000,0], h("0000000000000000"), h("5CF67D0181CB01C1"))
104
+ conform([0,0,0,0,0,0,0x1000,0], h("0000000000000000"), h("ECDE3D81820381C1"))
105
+ conform([0,0,0,0,0,0, 0x80,0], h("0000000000000000"), h("C781050DC4110220"))
106
+ conform([0,0,0,0,0,0, 0x40,0], h("0000000000000000"), h("6DFD0287EC4C0110"))
107
+ conform([0,0,0,0,0,0, 0x20,0], h("0000000000000000"), h("3B8A017EFB61800E"))
108
+ conform([0,0,0,0,0,0, 0x10,0], h("0000000000000000"), h("A08F7F81FF627FC0"))
109
+ conform([0,0,0,0,0,0, 0x8,0], h("0000000000000000"), h("00503FC1AFB93FE0"))
110
+ conform([0,0,0,0,0,0, 0x1,0], h("0000000000000000"), h("46D371477F33B152"))
111
+ conform([0,0,0,0,0,0,0,0x8000], h("0000000000000000"), h("BE67AC7DA294CA7C"))
112
+ conform([0,0,0,0,0,0,0,0], h("8000000000000000"), h("8001000180008000"))
113
+ conform([0,0,0,0,0,0,0,0], h("0080000000000000"), h("9181E3014C80C980"))
114
+ conform([0,0,0,0,0,0,0,0], h("0000800000000000"), h("0001800180008000"))
115
+ conform([0,0,0,0,0,0,0,0], h("0000000200000000"), h("FF49003BFF26FFAA"))
116
+ conform([0,0,0,0,0,0,0,0], h("0000000004000000"), h("100100014C00E000"))
117
+ conform([0,0,0,0,0,0,0,0], h("0000000000040000"), h("01110001FE4C00E0"))
118
+ conform([0,0,0,0,0,0,0,0], h("0000000000001000"), h("8001C001E0009000"))
119
+ conform([0,0,0,0,0,0,0,0], h("0000000000000100"), h("C8012C018E009900"))
120
+ conform([0,0,0,0,0,0,0,0], h("0000000000000040"), h("32010B012380E640"))
121
+ conform([0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101], h("0101010101010101"), h("E3F8AFF7A3795615"))
122
+ conform([0x7777,0x7777,0x7777,0x7777,0x7777,0x7777,0x7777,0x7777], h("7777777777777777"), h("D2E486D93304B9B6"))
123
+ conform([0xF7F7,0xF7F7,0xF7F7,0xF7F7,0xF7F7,0xF7F7,0xF7F7,0xF7F7], h("F7F7F7F7F7F7F7F7"), h("8E13C368F53E55AF"))
124
+ conform([1,0x203,0x405,0x607,0x809,0xa0b,0xc0d,0xe0f], h("0011223344556677"), h("F526AB9A62C0D258"))
125
+ end
126
+
127
+ it "conforms to decryption test vectors" do
128
+ conform_dec(n8("80000000000000000000000000000000"), h("0000000000000000"), h("78071EE87F0130E8"))
129
+ conform_dec(n8("00008000000000000000000000000000"), h("0000000000000000"), h("14D47C44835EEB99"))
130
+ conform_dec(n8("00000000800000000000000000000000"), h("0000000000000000"), h("CEA444C8CE44C2C2"))
131
+ conform_dec(n8("00000000000000000080000000000000"), h("0000000000000000"), h("6A9EF2F77DE21D8E"))
132
+ conform_dec(n8("00000000000000000000000800000000"), h("0000000000000000"), h("E698BE39AEA13C79"))
133
+ conform_dec(n8("00000000000000000000000000400000"), h("0000000000000000"), h("0579E00B945ED0B2"))
134
+ conform_dec(n8("00000000000000000000000000080000"), h("0000000000000000"), h("F0903DB58BEFF8CF"))
135
+ conform_dec(n8("00000000000000000000000000000040"), h("0000000000000000"), h("F75986F389F08110"))
136
+ conform_dec(n8("00000000000000000000000000000000"), h("8000000000000000"), h("8001000180008000"))
137
+ conform_dec(n8("00000000000000000000000000000000"), h("0002000000000000"), h("FE47FF8D0132FF26"))
138
+ conform_dec(n8("00000000000000000000000000000000"), h("0000000000200000"), h("08810001F2600700"))
139
+ conform_dec(n8("00000000000000000000000000000000"), h("0000000000000400"), h("2001B00138006400"))
140
+ conform_dec(n8("11111111111111111111111111111111"), h("1111111111111111"), h("3A1D3B4DB127C8B7"))
141
+ conform_dec(n8("52525252525252525252525252525252"), h("5252525252525252"), h("B255918917D30DB6"))
142
+ conform_dec(n8("66666666666666666666666666666666"), h("6666666666666666"), h("C9248B00868D8651"))
143
+ conform_dec(n8("ABABABABABABABABABABABABABABABAB"), h("ABABABABABABABAB"), h("5A4C4870F25A207F"))
144
+ conform_dec(n8("000102030405060708090A0B0C0D0E0F"), h("0011223344556677"), h("DB2D4A92AA68273F"))
145
+ conform_dec(n8("2BD6459F82C5B300952C49104881FF48"), h("EA024714AD5C4D84"), h("F129A6601EF62A47"))
146
+ end
147
+
148
+ it "calculates multiplicative inverses" do
149
+ idea = Crypt::IDEA.new("", Crypt::IDEA::ENCRYPT)
150
+ expect(idea.mulInv(0)).to eql(0)
151
+ (1..0xffff).each { |i|
152
+ expect(i * idea.mulInv(i) % 0x10001).to eql(1)
153
+ }
154
+ end
155
+
156
+ end
data/specs/rijndael.rb ADDED
@@ -0,0 +1,58 @@
1
+ require 'crypt/rijndael'
2
+ require 'fileutils'
3
+
4
+ describe "Crypt::Rijndael" do
5
+
6
+ it "has a blocksize of 128, 192, or 256 bits" do
7
+ expect {
8
+ rijndael = Crypt::Rijndael.new("Who is this John Galt guy, anyway?", 64)
9
+ }.to raise_error(RuntimeError)
10
+ expect {
11
+ rijndael = Crypt::Rijndael.new("Who is this John Galt guy, anyway?", 128, 64)
12
+ }.to raise_error(RuntimeError)
13
+ rijndael = Crypt::Rijndael.new("Who is this John Galt guy, anyway?", 128, 256)
14
+ expect(rijndael.block_size).to eql(32)
15
+ rijndael = Crypt::Rijndael.new("Who is this John Galt guy, anyway?", 256, 128)
16
+ expect(rijndael.block_size).to eql(16)
17
+ end
18
+
19
+ it "encrypts and decrypts 8-byte blocks" do
20
+ rijndael = Crypt::Rijndael.new("Who is this John Galt guy, anyway?", 128, 128)
21
+ block = "This block \u00cd 16" # unicode string of 16 bytes
22
+ encryptedBlock = rijndael.encrypt_block(block)
23
+ expect(encryptedBlock).to eql("\x8E\x88\x03\xB8> PnwR)\x93\x1A\xC9:\xC4".b())
24
+ decryptedBlock = rijndael.decrypt_block(encryptedBlock)
25
+ expect(decryptedBlock).to eql(block)
26
+ # attempt to encrypt with wrong block size
27
+ rijndael = Crypt::Rijndael.new("Who is this John Galt guy, anyway?", 128, 256)
28
+ expect {
29
+ encryptedBlock = rijndael.encrypt_block(block)
30
+ }.to raise_error(RuntimeError)
31
+ end
32
+
33
+ it "encrypts and decrypts strings" do
34
+ rijndael = Crypt::Rijndael.new("Who is this John Galt guy, anyway?")
35
+ string = "This is a string which is not a multiple of 8 characters long"
36
+ encryptedString = rijndael.encrypt_string(string)
37
+ decryptedString = rijndael.decrypt_string(encryptedString)
38
+ expect(decryptedString).to eql(string)
39
+ end
40
+
41
+ it "encrypts and decrypts a file" do
42
+ plainText = "This is a multi-line string\nwhich is not a multiple of 8 \ncharacters long."
43
+ plainFile = File.new('plain.txt', 'wb+')
44
+ plainFile.puts(plainText)
45
+ plainFile.close()
46
+ rijndael = Crypt::Rijndael.new("Whatever happened to Yuri?")
47
+ rijndael.encrypt_file('plain.txt', 'crypt.txt')
48
+ rijndael.decrypt_file('crypt.txt', 'decrypt.txt')
49
+ decryptFile = File.new('decrypt.txt', 'rb')
50
+ decryptText = decryptFile.readlines().join('').chomp()
51
+ decryptFile.close()
52
+ expect(decryptText).to eql(plainText)
53
+ FileUtils.rm('plain.txt')
54
+ FileUtils.rm('crypt.txt')
55
+ FileUtils.rm('decrypt.txt')
56
+ end
57
+
58
+ end