bicrypt 1.0.0 → 1.1.0

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.
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