bicrypt 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -1,6 +1,7 @@
1
1
  = BiCrypt
2
2
 
3
- http://death.rubyforge.org
3
+ * docs: http://rubydoc.info/gems/bicrypt
4
+ * code: http://rubyworks.github.com/bicrypt
4
5
 
5
6
 
6
7
  == DESCRIPTION
@@ -10,17 +11,18 @@ BiCrypt is a simple two-way encryption class.
10
11
 
11
12
  == FEATURES/ISSUES
12
13
 
13
- * ... to be done ...
14
+ * Simple to use.
15
+ * Good for light-weight "deterence" applications.
14
16
 
15
17
 
16
18
  == RELEASE NOTES
17
19
 
18
- Please see RELEASE file.
20
+ Please see HISTORY.rdoc file.
19
21
 
20
22
 
21
- == SYNOPSIS
23
+ == USAGE
22
24
 
23
- ... to be done ...
25
+ See QED example[qed/01_example] document.
24
26
 
25
27
 
26
28
  == HOW TO INSTALL
@@ -43,9 +45,19 @@ Windows users use 'ruby setup.rb all'.
43
45
 
44
46
  == COPYING
45
47
 
46
- Copyright (c) 2007 The Coding Dead
48
+ Copyright (c) 2007 Thomas Sawyer
47
49
 
48
- This program is ditributed unser the terms of the LGPLv3 license.
50
+ Licensed under the Apache License, Version 2.0 (the "License");
51
+ you may not use this file except in compliance with the License.
52
+ You may obtain a copy of the License at
53
+
54
+ http://www.apache.org/licenses/LICENSE-2.0
55
+
56
+ Unless required by applicable law or agreed to in writing, software
57
+ distributed under the License is distributed on an "AS IS" BASIS,
58
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
59
+ See the License for the specific language governing permissions and
60
+ limitations under the License.
49
61
 
50
62
  See LICENSE file for details.
51
63
 
data/lib/bicrypt.rb CHANGED
@@ -1,23 +1,18 @@
1
- # = BiCrypt
2
- #
3
- # A simple two-way encryption class.
4
- #
5
- # == Authors
6
- #
7
- # * Trans
8
- #
9
- # == Copying
1
+ # BiCrypt - A Simple Two-Way Encryption Class
10
2
  #
11
3
  # Copyright (c) 2007 Trans
12
4
  #
13
- # Ruby License
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
14
8
  #
15
- # This module is free software. You may use, modify, and/or redistribute this
16
- # software under the same terms as Ruby.
9
+ # http://www.apache.org/licenses/LICENSE-2.0
17
10
  #
18
- # This program is distributed in the hope that it will be useful, but WITHOUT
19
- # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
20
- # FOR A PARTICULAR PURPOSE.
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
21
16
 
22
17
  require 'stringio'
23
18
 
@@ -25,145 +20,64 @@ require 'stringio'
25
20
  #
26
21
  # A simple two-way encryption class.
27
22
  #
23
+ # This class is based on algorithms given in Applied Cryptography 2nd Ed.
24
+ #
25
+ # If subclassed, the new encryption class must provide three methods:
26
+ #
27
+ # * encrypt_block(block)
28
+ # * decrypt_block(block)
29
+ # * block_size()
30
+ #
28
31
  class BiCrypt
29
32
 
30
33
  ULONG = 0x100000000
31
34
 
32
- def block_size
33
- return(8)
34
- end
35
+ # These are the S-boxes given in Applied Cryptography 2nd Ed., p. 333
36
+ SBOX = [
37
+ [4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3],
38
+ [14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9],
39
+ [5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11],
40
+ [7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3],
41
+ [6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2],
42
+ [4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14],
43
+ [13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12],
44
+ [1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12]
45
+ ]
46
+
47
+ # These are the S-boxes given in the GOST source code listing in Applied
48
+ # Cryptography 2nd Ed., p. 644. They appear to be from the DES S-boxes
49
+ # [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7 ],
50
+ # [ 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1 ],
51
+ # [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11 ],
52
+ # [ 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9 ],
53
+ # [ 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15 ],
54
+ # [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8 ],
55
+ # [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10 ],
56
+ # [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7 ]
35
57
 
58
+ #
36
59
  def initialize(userKey)
60
+ @sBox = SBOX
61
+
62
+ if userKey.size < 8
63
+ userKey += '01234567'
64
+ end
37
65
 
38
- # These are the S-boxes given in Applied Cryptography 2nd Ed., p. 333
39
- @sBox = [
40
- [4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3],
41
- [14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9],
42
- [5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11],
43
- [7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3],
44
- [6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2],
45
- [4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14],
46
- [13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12],
47
- [1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12]
48
- ]
49
-
50
- # These are the S-boxes given in the GOST source code listing in Applied
51
- # Cryptography 2nd Ed., p. 644. They appear to be from the DES S-boxes
52
- # [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7 ],
53
- # [ 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1 ],
54
- # [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11 ],
55
- # [ 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9 ],
56
- # [ 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15 ],
57
- # [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8 ],
58
- # [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10 ],
59
- # [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7 ]
60
-
61
- # precalculate the S table
62
- @sTable = precalculate_S_table()
66
+ #@sTable = precalculate_s_table()
63
67
 
64
68
  # derive the 32-byte key from the user-supplied key
65
69
  userKeyLength = userKey.length
70
+
66
71
  @key = userKey[0..31].unpack('C'*32)
72
+
67
73
  if (userKeyLength < 32)
68
74
  userKeyLength.upto(31) { @key << 0 }
69
75
  end
70
76
  end
71
77
 
78
+ public
72
79
 
73
- def precalculate_S_table()
74
- sTable = [[], [], [], []]
75
- 0.upto(3) { |i|
76
- 0.upto(255) { |j|
77
- t = @sBox[2*i][j % 16] | (@sBox[2*i+1][j/16] << 4)
78
- u = (8*i + 11) % 32
79
- v = (t << u) | (t >> (32-u))
80
- sTable[i][j] = (v % ULONG)
81
- }
82
- }
83
- return(sTable)
84
- end
85
-
86
-
87
- def f(longWord)
88
- longWord = longWord % ULONG
89
- a, b, c, d = [longWord].pack('L').unpack('CCCC')
90
- return(@sTable[3][d] ^ @sTable[2][c] ^ @sTable[1][b] ^ @sTable[0][a])
91
- end
92
-
93
- def encrypt_pair(xl, xr)
94
- 3.times {
95
- xr ^= f(xl+@key[0])
96
- xl ^= f(xr+@key[1])
97
- xr ^= f(xl+@key[2])
98
- xl ^= f(xr+@key[3])
99
- xr ^= f(xl+@key[4])
100
- xl ^= f(xr+@key[5])
101
- xr ^= f(xl+@key[6])
102
- xl ^= f(xr+@key[7])
103
- }
104
- xr ^= f(xl+@key[7])
105
- xl ^= f(xr+@key[6])
106
- xr ^= f(xl+@key[5])
107
- xl ^= f(xr+@key[4])
108
- xr ^= f(xl+@key[3])
109
- xl ^= f(xr+@key[2])
110
- xr ^= f(xl+@key[1])
111
- xl ^= f(xr+@key[0])
112
- return([xr, xl])
113
- end
114
-
115
-
116
- def decrypt_pair(xl, xr)
117
- xr ^= f(xl+@key[0])
118
- xl ^= f(xr+@key[1])
119
- xr ^= f(xl+@key[2])
120
- xl ^= f(xr+@key[3])
121
- xr ^= f(xl+@key[4])
122
- xl ^= f(xr+@key[5])
123
- xr ^= f(xl+@key[6])
124
- xl ^= f(xr+@key[7])
125
- 3.times {
126
- xr ^= f(xl+@key[7])
127
- xl ^= f(xr+@key[6])
128
- xr ^= f(xl+@key[5])
129
- xl ^= f(xr+@key[4])
130
- xr ^= f(xl+@key[3])
131
- xl ^= f(xr+@key[2])
132
- xr ^= f(xl+@key[1])
133
- xl ^= f(xr+@key[0])
134
- }
135
- return([xr, xl])
136
- end
137
-
138
- def encrypt_block(block)
139
- xl, xr = block.unpack('NN')
140
- xl, xr = encrypt_pair(xl, xr)
141
- encrypted = [xl, xr].pack('NN')
142
- return(encrypted)
143
- end
144
-
145
-
146
- def decrypt_block(block)
147
- xl, xr = block.unpack('NN')
148
- xl, xr = decrypt_pair(xl, xr)
149
- decrypted = [xl, xr].pack('NN')
150
- return(decrypted)
151
- end
152
-
153
- # When this module is mixed in with an encryption class, the class
154
- # must provide three methods: encrypt_block(block) and decrypt_block(block)
155
- # and block_size()
156
-
157
- def generate_initialization_vector(words)
158
- srand(Time.now.to_i)
159
- vector = ""
160
- words.times {
161
- vector << [rand(ULONG)].pack('N')
162
- }
163
- return(vector)
164
- end
165
-
166
-
80
+ # Encrypt an IO stream.
167
81
  def encrypt_stream(plainStream, cryptStream)
168
82
  # Cypher-block-chain mode
169
83
 
@@ -172,7 +86,7 @@ class BiCrypt
172
86
  cryptStream.write(chain)
173
87
 
174
88
  while ((block = plainStream.read(block_size())) && (block.length == block_size()))
175
- block = block ^ chain
89
+ block = xor(block, chain)
176
90
  encrypted = encrypt_block(block)
177
91
  cryptStream.write(encrypted)
178
92
  chain = encrypted
@@ -189,19 +103,19 @@ class BiCrypt
189
103
  remainingMessageBytes.upto(block_size()-2) { buffer << rand(128).chr }
190
104
  buffer << remainingMessageBytes.chr
191
105
  block = buffer.join('')
192
- block = block ^ chain
106
+ block = xor(block, chain)
193
107
  encrypted = encrypt_block(block)
194
108
  cryptStream.write(encrypted)
195
109
  end
196
110
 
197
-
111
+ # Decrypt an encrypted IO stream.
198
112
  def decrypt_stream(cryptStream, plainStream)
199
113
  # Cypher-block-chain mode
200
114
  chain = cryptStream.read(block_size())
201
115
 
202
116
  while (block = cryptStream.read(block_size()))
203
117
  decrypted = decrypt_block(block)
204
- plainText = decrypted ^ chain
118
+ plainText = xor(decrypted, chain)
205
119
  plainStream.write(plainText) unless cryptStream.eof?
206
120
  chain = block
207
121
  end
@@ -212,19 +126,7 @@ class BiCrypt
212
126
  remainingMessageBytes.times { plainStream.write(buffer.shift) }
213
127
  end
214
128
 
215
-
216
- def carefully_open_file(filename, mode)
217
- begin
218
- aFile = File.new(filename, mode)
219
- rescue
220
- puts "Sorry. There was a problem opening the file <#{filename}>."
221
- aFile.close() unless aFile.nil?
222
- raise
223
- end
224
- return(aFile)
225
- end
226
-
227
-
129
+ # Encrypt a file.
228
130
  def encrypt_file(plainFilename, cryptFilename)
229
131
  plainFile = carefully_open_file(plainFilename, 'rb')
230
132
  cryptFile = carefully_open_file(cryptFilename, 'wb+')
@@ -233,7 +135,7 @@ class BiCrypt
233
135
  cryptFile.close unless cryptFile.closed?
234
136
  end
235
137
 
236
-
138
+ # Decrypt an encrypted file.
237
139
  def decrypt_file(cryptFilename, plainFilename)
238
140
  cryptFile = carefully_open_file(cryptFilename, 'rb')
239
141
  plainFile = carefully_open_file(plainFilename, 'wb+')
@@ -242,7 +144,7 @@ class BiCrypt
242
144
  plainFile.close unless plainFile.closed?
243
145
  end
244
146
 
245
-
147
+ # Encrypt a string.
246
148
  def encrypt_string(plainText)
247
149
  plainStream = StringIO.new(plainText)
248
150
  cryptStream = StringIO.new('')
@@ -251,7 +153,7 @@ class BiCrypt
251
153
  return(cryptText)
252
154
  end
253
155
 
254
-
156
+ # Decrypt an encrypted string.
255
157
  def decrypt_string(cryptText)
256
158
  cryptStream = StringIO.new(cryptText)
257
159
  plainStream = StringIO.new('')
@@ -260,10 +162,122 @@ class BiCrypt
260
162
  return(plainText)
261
163
  end
262
164
 
263
- end
165
+ private
166
+
167
+ # S-boxes.
168
+ attr :sBox
169
+
170
+ # Calculated S-boxes
171
+ def sTable
172
+ @sTable ||= (
173
+ sTable = [[], [], [], []]
174
+ 0.upto(3) { |i|
175
+ 0.upto(255) { |j|
176
+ t = sBox[2*i][j % 16] | (sBox[2*i+1][j/16] << 4)
177
+ u = (8*i + 11) % 32
178
+ v = (t << u) | (t >> (32-u))
179
+ sTable[i][j] = (v % ULONG)
180
+ }
181
+ }
182
+ sTable
183
+ )
184
+ end
264
185
 
186
+ #
187
+ def encrypt_block(block)
188
+ xl, xr = block.unpack('NN')
189
+ xl, xr = encrypt_pair(xl, xr)
190
+ encrypted = [xl, xr].pack('NN')
191
+ return(encrypted)
192
+ end
265
193
 
266
- class String
194
+ #
195
+ def decrypt_block(block)
196
+ xl, xr = block.unpack('NN')
197
+ xl, xr = decrypt_pair(xl, xr)
198
+ decrypted = [xl, xr].pack('NN')
199
+ return(decrypted)
200
+ end
201
+
202
+ #
203
+ def block_size
204
+ 8
205
+ end
206
+
207
+ #
208
+ def encrypt_pair(xl, xr)
209
+ 3.times {
210
+ xr ^= f(xl+@key[0])
211
+ xl ^= f(xr+@key[1])
212
+ xr ^= f(xl+@key[2])
213
+ xl ^= f(xr+@key[3])
214
+ xr ^= f(xl+@key[4])
215
+ xl ^= f(xr+@key[5])
216
+ xr ^= f(xl+@key[6])
217
+ xl ^= f(xr+@key[7])
218
+ }
219
+ xr ^= f(xl+@key[7])
220
+ xl ^= f(xr+@key[6])
221
+ xr ^= f(xl+@key[5])
222
+ xl ^= f(xr+@key[4])
223
+ xr ^= f(xl+@key[3])
224
+ xl ^= f(xr+@key[2])
225
+ xr ^= f(xl+@key[1])
226
+ xl ^= f(xr+@key[0])
227
+ return([xr, xl])
228
+ end
229
+
230
+ #
231
+ def decrypt_pair(xl, xr)
232
+ xr ^= f(xl+@key[0])
233
+ xl ^= f(xr+@key[1])
234
+ xr ^= f(xl+@key[2])
235
+ xl ^= f(xr+@key[3])
236
+ xr ^= f(xl+@key[4])
237
+ xl ^= f(xr+@key[5])
238
+ xr ^= f(xl+@key[6])
239
+ xl ^= f(xr+@key[7])
240
+ 3.times {
241
+ xr ^= f(xl+@key[7])
242
+ xl ^= f(xr+@key[6])
243
+ xr ^= f(xl+@key[5])
244
+ xl ^= f(xr+@key[4])
245
+ xr ^= f(xl+@key[3])
246
+ xl ^= f(xr+@key[2])
247
+ xr ^= f(xl+@key[1])
248
+ xl ^= f(xr+@key[0])
249
+ }
250
+ return([xr, xl])
251
+ end
252
+
253
+ #
254
+ def f(longWord)
255
+ longWord = longWord % ULONG
256
+ a, b, c, d = [longWord].pack('L').unpack('CCCC')
257
+ return(sTable[3][d] ^ sTable[2][c] ^ sTable[1][b] ^ sTable[0][a])
258
+ end
259
+
260
+ #
261
+ def generate_initialization_vector(words)
262
+ srand(Time.now.to_i)
263
+ vector = ""
264
+ words.times {
265
+ vector << [rand(ULONG)].pack('N')
266
+ }
267
+ return(vector)
268
+ end
269
+
270
+ #
271
+ def carefully_open_file(filename, mode)
272
+ begin
273
+ aFile = File.new(filename, mode)
274
+ rescue
275
+ puts "Sorry. There was a problem opening the file <#{filename}>."
276
+ aFile.close() unless aFile.nil?
277
+ raise
278
+ end
279
+ return(aFile)
280
+ end
267
281
 
268
282
  # Binary XOR of two strings.
269
283
  #
@@ -275,17 +289,21 @@ class String
275
289
  # "\000\001\001\000"
276
290
  # "\003\002\001"
277
291
  #
278
- def ^(aString)
279
- a = self.unpack('C'*(self.length))
280
- b = aString.unpack('C'*(aString.length))
292
+ #--
293
+ # NOTE: This used to be a String#^ extension in v1.0. So if
294
+ # an uncaught bug should arise check this first.
295
+ #++
296
+ def xor(str1, str2)
297
+ a = str1.unpack('C'*(str1.length)) #.bytes.to_a
298
+ b = str2.unpack('C'*(str2.length)) #.bytes.to_a
281
299
  if (b.length < a.length)
282
300
  (a.length - b.length).times { b << 0 }
283
301
  end
284
302
  xor = ""
285
- 0.upto(a.length-1) { |pos|
303
+ 0.upto(a.length - 1) do |pos|
286
304
  x = a[pos] ^ b[pos]
287
305
  xor << x.chr()
288
- }
306
+ end
289
307
  return(xor)
290
308
  end
291
309