ezcrypto 0.2.1 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,30 @@
1
+ 0.3 February 25th, 2006 new encrypted file support by Dirk (dirk.barnikel@gmx.de) Thanks Dirk.
2
+
3
+ * Added test case for the file-related stuff. file-stuff
4
+ seems to work fine
5
+
6
+ * Replaced hard coded IO buffersize (512) with class attribute
7
+ Key#block_size and default value.
8
+
9
+ * Modification to create {De,En}crypters only via the factory methods
10
+ Key#{de,en}crypter.
11
+
12
+ * Added Key#{de,en}crypt_file methods that take a file and de- or encrypts it.
13
+
14
+ * The methods are implemented to call the cipher with small chunks of data (512 bytes) to keep memory usage low.
15
+
16
+ * By default, the original file is first overwritten and then removed.
17
+
18
+ * This overwrite is not really safe but should make it harder to restore the data of the removed file from the filesystem.
19
+
20
+ * Added Key#{store,load} methods that read and write Key data to/from files.
21
+
22
+ * Added Key#safe_{create,delete,read} methods to encapsulate the handling of files inside EzCrypto.
23
+
24
+ 0.2.2 January 4th, 2006 Bug fixes and unit tests for active_crypto
25
+
26
+ There were some serious problems with ActiveCrypto's support for having keys in associated classes. I also added unit tests to active_crypto. The support code was brutaly stolen from Rick Olson's acts_as_paranoid library. Unfortunately I disabled the schema stuff for now, but will add it in the next release, which hopefully is soon.
27
+
1
28
  0.2.1 November 2nd, 2005 New method in KeyHolder
2
29
 
3
30
  Added set_encoded_key(enc) to KeyHolder for setting a key with the Base64 encoded keyvalue.
data/README CHANGED
@@ -134,7 +134,19 @@ Also see my blogs at:
134
134
  http://stakeventures.com and
135
135
  http://neubia.com
136
136
 
137
- This project was based on code used in my project StakeItOut, where you can securely share web services with your partners.
137
+ This project was based on code used in my projects StakeItOut, WideWord and WideBlog.
138
+
139
+ StakeItOut lets you securely share web services with your partners.
140
+
138
141
  https://stakeitout.com
139
142
 
143
+ WideWord lets you collaboratively write and share documents that remain 100% encrypted on the server. Only you have the keys:
144
+
145
+ http://wideword.net
146
+
147
+ WideBlog is a secure private blogging system designed for private project blogs. It uses the same encryption technology as WideWord and is very easy to use:
148
+
149
+ http://wideblog.net
150
+
151
+
140
152
  (C) 2005 Pelle Braendgaard
data/README_ACTIVE_CRYPTO CHANGED
@@ -11,7 +11,7 @@ ActiveCrypto is based on EzCrypto and provides application oriented crypto suppo
11
11
 
12
12
  Download it from here:
13
13
 
14
- http://rubyforge.org/frs/?group_id=755&release_id=3321
14
+ http://rubyforge.org/frs/?group_id=755
15
15
 
16
16
  or install it via Ruby Gems:
17
17
 
@@ -115,7 +115,8 @@ Also see my blogs at:
115
115
  http://stakeventures.com and
116
116
  http://neubia.com
117
117
 
118
- This project was based on code used in my project StakeItOut, where you can securely share web services with your partners.
118
+ This project was based on code used in my projects WideWord where you can securely share documents and StakeItOut, where you can securely share web services with your partners.
119
+ https://wideword.net
119
120
  https://stakeitout.com
120
121
 
121
122
  (C) 2005 Pelle Braendgaard
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'active_crypto.rb'
2
+ #ActiveRecord::Base.send :include, ActiveCrypto
data/lib/active_crypto.rb CHANGED
@@ -1,4 +1,4 @@
1
- require "ezCrypto"
1
+ require "ezcrypto.rb"
2
2
  module ActiveRecord # :nodoc:
3
3
  module Crypto #:nodoc:
4
4
 
@@ -49,18 +49,23 @@ Include optional option :key, to specify an external KeyHolder, which holds the
49
49
 
50
50
  =end
51
51
  def encrypt(*attributes)
52
- include ActiveRecord::Crypto::Encrypted
53
- alias_method :orig_write_attribute, :write_attribute
54
- alias_method :write_attribute,:write_encrypted_attribute
55
- options=attributes.last.is_a?(Hash) ? attributes.pop : {}
56
- if options and options[:key]
57
- module_eval <<-"end;"
58
- def session_key
59
- (send :#{options[:key]} ).send :session_key
60
- end
61
- end;
62
-
63
- end
52
+ include ActiveRecord::Crypto::Encrypted
53
+ alias_method :orig_write_attribute, :write_attribute
54
+ alias_method :write_attribute,:write_encrypted_attribute
55
+ options=attributes.last.is_a?(Hash) ? attributes.pop : {}
56
+ if options and options[:key]
57
+ include ActiveRecord::Crypto::AssociationKeyHolder
58
+
59
+ module_eval <<-"end;"
60
+ def session_key
61
+ (send :#{options[:key]} ).send :session_key
62
+ end
63
+ @@external_key=true
64
+ end;
65
+ else
66
+ include ActiveRecord::Crypto::KeyHolder
67
+ end
68
+
64
69
  self.encrypted_attributes=attributes
65
70
  for enc in attributes
66
71
 
@@ -88,7 +93,7 @@ Use it as follows:
88
93
 
89
94
  =end
90
95
  def keyholder()
91
- include ActiveRecord::Crypto::KeyHolder
96
+ include ActiveRecord::Crypto::AssociationKeyHolder
92
97
  end
93
98
 
94
99
  =begin rdoc
@@ -121,7 +126,7 @@ This module handles all standard key management features.
121
126
  Creates a key for object based on given password and an optional salt.
122
127
  =end
123
128
  def enter_password(password,salt="onetwothree")
124
- set_session_key(EzCrypto::Key.with_password password, salt)
129
+ set_session_key(EzCrypto::Key.with_password(password, salt))
125
130
  end
126
131
 
127
132
  =begin rdoc
@@ -134,25 +139,46 @@ Decodes the Base64 encoded key and uses it as it's session key
134
139
  Sets a session key for the object. This should be a EzCrypto::Key instance.
135
140
  =end
136
141
  def set_session_key(key)
137
- Base.session_keys[session_key_id]=key
142
+ @session_key=key
138
143
  end
139
144
 
140
145
  =begin rdoc
141
146
  Returns the session_key
142
147
  =end
143
148
  def session_key
144
- Base.session_keys[session_key_id]
149
+ @session_key
145
150
  end
146
151
 
152
+ end
153
+
154
+ module AssociationKeyHolder
155
+ include KeyHolder
156
+ =begin rdoc
157
+ Sets a session key for the object. This should be a EzCrypto::Key instance.
158
+ =end
159
+ def set_session_key(key)
160
+ Base.session_keys[session_key_id]=key
161
+ end
162
+
163
+ =begin rdoc
164
+ Returns the session_key
165
+ =end
166
+ def session_key
167
+ if session_key_id
168
+ Base.session_keys[session_key_id]
169
+ else
170
+ nil
171
+ end
172
+ end
173
+
147
174
  private
148
-
175
+
149
176
  def session_key_id
150
177
  "#{self.class.to_s}:#{id}"
151
178
  end
152
179
  end
153
180
 
154
181
  module Encrypted #:nodoc:
155
- include ActiveRecord::Crypto::KeyHolder
156
182
  def self.append_features(base) #:nodoc:
157
183
  super
158
184
  base.module_eval <<-"end;"
@@ -182,7 +208,11 @@ Returns the session_key
182
208
  if session_key.nil?
183
209
  raise MissingKeyError
184
210
  else
185
- session_key.decrypt(data)
211
+ if data
212
+ session_key.decrypt(data)
213
+ else
214
+ nil
215
+ end
186
216
  end
187
217
  end
188
218
 
@@ -190,7 +220,11 @@ Returns the session_key
190
220
  if session_key.nil?
191
221
  raise MissingKeyError
192
222
  else
193
- session_key.encrypt(data)
223
+ if data
224
+ session_key.encrypt(data)
225
+ else
226
+ nil
227
+ end
194
228
  end
195
229
  end
196
230
 
data/lib/ezcrypto.rb CHANGED
@@ -41,8 +41,26 @@ https://stakeitout.com
41
41
  =end
42
42
 
43
43
  class Key
44
- attr_reader :raw,:algorithm
45
-
44
+
45
+ attr_reader :raw, :algorithm
46
+
47
+ @@block_size = 512
48
+
49
+ =begin rdoc
50
+ Set the block-size for IO-operations (default: 512 bytes)
51
+ =end
52
+ def self.block_size=(size)
53
+ @@block_size = size
54
+ end
55
+
56
+ =begin rdoc
57
+ Return the block-size for IO-operations.
58
+ =end
59
+ def self.block_size
60
+ @@block_size
61
+ end
62
+
63
+
46
64
  =begin rdoc
47
65
  Initialize the key with raw unencoded binary key data. This needs to be at least
48
66
  16 bytes long for the default aes-128 algorithm.
@@ -51,28 +69,28 @@ Initialize the key with raw unencoded binary key data. This needs to be at least
51
69
  @raw=raw
52
70
  @algorithm=options[:algorithm]||"aes-128-cbc"
53
71
  end
54
-
72
+
55
73
  =begin rdoc
56
74
  Generate random key.
57
75
  =end
58
- def self.generate(options = {})
76
+ def self.generate(options = {})
59
77
  Key.new(EzCrypto::Digester.generate_key(calculate_key_size(options[:algorithm])),options)
60
78
  end
61
-
79
+
62
80
  =begin rdoc
63
- Create key generated from the given password and salt
81
+ Create key generated from the given password and salt
64
82
  =end
65
83
  def self.with_password(password,salt,options = {})
66
84
  Key.new(EzCrypto::Digester.get_key(password,salt,calculate_key_size(options[:algorithm])),options)
67
85
  end
68
-
86
+
69
87
  =begin rdoc
70
88
  Initialize the key with Base64 encoded key data.
71
89
  =end
72
90
  def self.decode(encoded,options = {})
73
91
  Key.new(Base64.decode64(encoded),options)
74
92
  end
75
-
93
+
76
94
  =begin rdoc
77
95
  Encrypts the data with the given password and a salt. Short hand for:
78
96
 
@@ -84,30 +102,31 @@ Encrypts the data with the given password and a salt. Short hand for:
84
102
  key=Key.with_password(password,salt,options)
85
103
  key.encrypt(data)
86
104
  end
87
-
105
+
88
106
  =begin rdoc
89
107
  Decrypts the data with the given password and a salt. Short hand for:
90
108
 
91
109
  key=Key.with_password(password,salt,options)
92
110
  key.decrypt(data)
93
111
 
94
-
112
+
95
113
  =end
96
114
  def self.decrypt_with_password(password,salt,data,options = {})
97
115
  key=Key.with_password(password,salt,options)
98
116
  key.decrypt(data)
99
117
  end
100
-
118
+
101
119
  =begin rdoc
102
- Given an algorithm this calculates the keysize. This is used by both the generate and with_password methods. This is not yet 100% complete.
120
+ Given an algorithm this calculates the keysize. This is used by both
121
+ the generate and with_password methods. This is not yet 100% complete.
103
122
  =end
104
- def self.calculate_key_size(algorithm)
123
+ def self.calculate_key_size(algorithm)
105
124
  if !algorithm.nil?
106
125
  algorithm=~/^([[:alnum:]]+)(-(\d+))?/
107
126
  if $3
108
127
  size=($3.to_i)/8
109
- else
110
- case $1
128
+ else
129
+ case $1
111
130
  when "bf"
112
131
  size = 16
113
132
  when "blowfish"
@@ -126,7 +145,7 @@ Given an algorithm this calculates the keysize. This is used by both the generat
126
145
  size = 16
127
146
  when "rc4"
128
147
  size = 16
129
- else
148
+ else
130
149
  size = 16
131
150
  end
132
151
  end
@@ -134,7 +153,7 @@ Given an algorithm this calculates the keysize. This is used by both the generat
134
153
  if size.nil?
135
154
  size = 16
136
155
  end
137
-
156
+
138
157
  size
139
158
  end
140
159
 
@@ -144,20 +163,24 @@ returns the Base64 encoded key.
144
163
  def encode
145
164
  Base64.encode64 @raw
146
165
  end
147
-
166
+
148
167
  =begin rdoc
149
- returns the Base64 encoded key. Synonym for encode.
168
+ returns the Base64 encoded key. Synonym for encode.
150
169
  =end
151
170
  def to_s
152
171
  encode
153
172
  end
154
-
173
+
155
174
  =begin rdoc
156
175
  Encrypts the data and returns it in encrypted binary form.
157
176
  =end
158
177
  def encrypt(data)
159
- @cipher=EzCrypto::Encrypter.new(self,"",@algorithm)
160
- @cipher.encrypt(data)
178
+ if data==nil || data==""
179
+ nil
180
+ else
181
+ encrypter("")
182
+ @cipher.encrypt(data)
183
+ end
161
184
  end
162
185
 
163
186
  =begin rdoc
@@ -166,27 +189,215 @@ Encrypts the data and returns it in encrypted Base64 encoded form.
166
189
  def encrypt64(data)
167
190
  Base64.encode64(encrypt(data))
168
191
  end
169
-
192
+
170
193
  =begin rdoc
171
194
  Decrypts the data passed to it in binary format.
172
- =end
195
+ =end
173
196
  def decrypt(data)
174
- @cipher=EzCrypto::Decrypter.new(self,"",@algorithm)
175
- @cipher.gulp(data)
197
+ if data==nil || data==""
198
+ nil
199
+ else
200
+ decrypter("")
201
+ @cipher.gulp(data)
202
+ end
176
203
  rescue
177
204
  puts @algorithm
178
205
  throw $!
179
206
  end
180
-
207
+
181
208
  =begin rdoc
182
- Decrypts a Base64 formatted string
209
+ Decrypts a Base64 formatted string
183
210
  =end
184
211
  def decrypt64(data)
185
212
  decrypt(Base64.decode64(data))
186
213
  end
187
214
 
215
+
216
+ =begin rdoc
217
+ Create a file with minimal permissions, and yield
218
+ it to a block. By default only the owner may read it.
219
+ =end
220
+ def safe_create(file, mod=0400, mask=0066)
221
+ begin
222
+ old_umask = File.umask
223
+ File.umask(mask)
224
+ File.open(file, File::CREAT | File::EXCL | File::WRONLY) do |f|
225
+ yield(f) if block_given?
226
+ end
227
+ ensure
228
+ File.umask(old_umask)
229
+ end
230
+ File.chmod(mod, file) if File.exists?(file)
231
+ File.size(file)
232
+ end
233
+
234
+
235
+ =begin rdoc
236
+ Overwrite a file with zero values before removing it's filesystem inode.
237
+ This is not very safe :-)
238
+ =end
239
+ def safe_delete(file)
240
+ begin
241
+ to_clear = File.size(file)
242
+ zeroes = Array.new(Key.block_size, "\0").join
243
+ File.chmod(0600, file)
244
+ File.open(file, File::WRONLY) do |f|
245
+ f.rewind
246
+ while to_clear > 0
247
+ f.write(zeroes)
248
+ to_clear -= Key.block_size
249
+ end
250
+ f.flush
251
+ end
252
+ ensure
253
+ File.delete(file) if File.exists?(file)
254
+ end
255
+ return !File.exists?(file)
256
+ end
257
+
258
+
259
+ =begin rdoc
260
+ Open a file readonly and yield it to a block.
261
+ =end
262
+ def safe_read(file)
263
+ File.open(file, File::RDONLY) do |i|
264
+ yield(i) if block_given?
265
+ end
266
+ end
267
+
268
+ private :safe_create, :safe_read, :safe_delete
269
+
270
+ =begin rdoc
271
+ Load a key from a yaml_file generated via Key#store.
272
+ =end
273
+ def self.load(filename)
274
+ require 'yaml'
275
+ hash = YAML::load_file(filename)
276
+ req = proc { |k| hash[k] or raise "Missing element #{k} in #{filename}" }
277
+ key = self.new Base64.decode64(req.call(:key)) , :algorithm => req.call(:algorithm)
278
+ return key
279
+ end
280
+
281
+
282
+ =begin rdoc
283
+ Save the key data into a file, try to do this in a secure manner.
284
+ NOTE: YAML::store & friends are not used to encance control over
285
+ the generated file format.
286
+ =end
287
+ def store(filename)
288
+ safe_create(filename) do |f|
289
+ selfenc = self.encode
290
+ f.puts "---"
291
+ f.puts ":EZCRYPTO KEY FILE: KEEP THIS SECURE !"
292
+ f.puts ":created: #{Time.now}"
293
+ f.puts ":algorithm: #{@algorithm}"
294
+ f.puts ":key: #{selfenc}"
295
+ end
296
+ end
297
+
298
+
299
+ =begin rdoc
300
+ Get a Encrypter object. You have to call #final on it by yourself!
301
+ =end
302
+ def encrypter(target='')
303
+ @cipher = EzCrypto::Encrypter.new(self,target,@algorithm)
304
+ end
305
+
306
+
307
+ =begin rdoc
308
+ Get a Decrypter object. You have to call #final on it by yourself!
309
+ =end
310
+ def decrypter(target='')
311
+ @cipher = EzCrypto::Decrypter.new(self,target,@algorithm)
312
+ end
313
+
314
+ =begin rdoc
315
+ Create a Decrypter object and yield it to a block.
316
+ You must *not* call #final by yourself, the method
317
+ does this.
318
+ =end
319
+ def on_decrypter(target='', &block)
320
+ decrypter(target)
321
+ on_cipher(&block)
322
+ end
323
+
324
+ =begin rdoc
325
+ Create an Encrypter object and yield it to a block.
326
+ You must *not* call #final by yourself, the method
327
+ does this.
328
+ =end
329
+ def on_encrypter(target='', &block)
330
+ encrypter(target)
331
+ on_cipher(&block)
332
+ end
333
+
334
+
335
+ =begin rdoc
336
+ Helper method, yields the current cipher to a block.
337
+ =end
338
+ def on_cipher(&block)
339
+ begin
340
+ block.call @cipher
341
+ ensure
342
+ @cipher.final
343
+ end
344
+ end
345
+
346
+ private :on_cipher
347
+
348
+
349
+ =begin rdoc
350
+ Encrypt a file 'inplace' and add a suffix
351
+ see #cipher_file.
352
+ IMPORTANT: The inputfile will be deleted by default.
353
+ =end
354
+ def encrypt_file(src, tgt=nil, options = {} )
355
+ options = { :suffix => '.ez', :autoclean => 'true' }.update(options)
356
+ tgt = "#{src}#{options[:suffix]}" unless tgt
357
+ cipher_file :on_encrypter, src, tgt, options[:autoclean]
358
+ end
359
+
360
+
361
+ =begin rdoc
362
+ Decrypt a file 'inplace' and remove a suffix
363
+ see #cipher_file
364
+ IMPORTANT: The inputfile will be deleted by default.
365
+ =end
366
+ def decrypt_file(src, tgt=nil, options = {} )
367
+ options = { :suffix => '.ez', :autoclean => 'true' }.update(options)
368
+ unless tgt
369
+ tgt = src
370
+ tgt = tgt.gsub(/#{options[:suffix]}$/, '')
371
+ end
372
+ cipher_file :on_decrypter, src, tgt, options[:autoclean]
373
+ end
374
+
375
+
376
+ =begin rdoc
377
+ uses either #on_decrypter or #on_encrypter to transform a given sourcefile
378
+ to a targetfile. the sourcefile is deleted after sucessful transformation
379
+ the delete_source is 'true'.
380
+ =end
381
+ def cipher_file(method, sourcefile, targetfile, delete_source)
382
+ raise(ArgumentError, "source == target #{sourcefile}") if sourcefile == targetfile
383
+ safe_create(targetfile,0600) do |o|
384
+ self.send(method, o) do |c|
385
+ safe_read(sourcefile) do |i|
386
+ loop do
387
+ buffer = i.read(Key.block_size) or break
388
+ c << buffer
389
+ end
390
+ end
391
+ end
392
+ end
393
+ safe_delete(sourcefile) if delete_source && File.exists?(targetfile)
394
+ return targetfile
395
+ end
396
+
397
+ private :cipher_file
188
398
 
189
399
  end
400
+
190
401
  =begin rdoc
191
402
  Abstract Wrapper around OpenSSL's Cipher object. Extended by Encrypter and Decrypter.
192
403
 
@@ -213,12 +424,17 @@ Warning! The interface may change.
213
424
  @finished=false
214
425
  end
215
426
 
427
+
428
+ def to_target(data)
429
+ return data
430
+ end
431
+
216
432
  =begin rdoc
217
433
  Process the givend data with the cipher.
218
434
  =end
219
435
  def update(data)
220
436
  reset if @finished
221
- @target<< @cipher.update(data)
437
+ @target<< to_target(@cipher.update(data))
222
438
  end
223
439
 
224
440
  =begin rdoc
@@ -232,7 +448,7 @@ Process the givend data with the cipher.
232
448
  Finishes up any last bits of data in the cipher and returns the final result.
233
449
  =end
234
450
  def final
235
- @target<< @cipher.final
451
+ @target<< to_target(@cipher.final)
236
452
  @finished=true
237
453
  @target
238
454
  end
data/rakefile CHANGED
@@ -8,7 +8,7 @@ require 'rake/contrib/rubyforgepublisher'
8
8
 
9
9
  PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
10
10
  PKG_NAME = 'ezcrypto'
11
- PKG_VERSION = '0.2.1' + PKG_BUILD
11
+ PKG_VERSION = '0.3' + PKG_BUILD
12
12
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
13
13
 
14
14
  RELEASE_NAME = "REL #{PKG_VERSION}"
@@ -31,14 +31,12 @@ Rake::TestTask.new { |t|
31
31
  Rake::RDocTask.new { |rdoc|
32
32
  rdoc.rdoc_dir = 'doc'
33
33
  rdoc.title = "EzCrypto - Simplified Crypto Library"
34
- rdoc.options << '--line-numbers --inline-source --main README --template=jamis'
34
+ # rdoc.options << '--line-numbers --inline-source --main README #--template=jamis'
35
35
  rdoc.template = "#{ENV['template']}.rb" if ENV['template']
36
36
  rdoc.rdoc_files.include('README')
37
37
  rdoc.rdoc_files.include('README_ACTIVE_CRYPTO')
38
38
  rdoc.rdoc_files.include('CHANGELOG')
39
- rdoc.rdoc_files.include('lib/ezcrypto.rb')
40
- rdoc.rdoc_files.include('lib/active_crypto.rb')
41
- # rdoc.rdoc_files.include('lib/ezcrypto/*.rb')
39
+ rdoc.rdoc_files.include('lib/*.rb')
42
40
  }
43
41
 
44
42
 
@@ -60,9 +58,9 @@ spec = Gem::Specification.new do |s|
60
58
  s.requirements << 'none'
61
59
  s.require_path = 'lib'
62
60
 
63
- s.files = [ "rakefile", "README", "README_ACTIVE_CRYPTO", "MIT-LICENSE","CHANGELOG" ]
64
- s.files = s.files + Dir.glob( "lib/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
65
- s.files = s.files + Dir.glob( "test/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
61
+ s.files = [ "rakefile", "README", "README_ACTIVE_CRYPTO", "MIT-LICENSE","CHANGELOG","init.rb" ]
62
+ s.files = s.files + Dir.glob( "lib/**/*" ).delete_if { |item| item.include?( "CVS" )||item.include?("\.DS_Store")||item.include?("/\._") }
63
+ s.files = s.files + Dir.glob( "test/**/*" ).delete_if { |item| item.include?( "CVS" )||item.include?("\.DS_Store")||item.include?("/\._") }
66
64
  end
67
65
 
68
66
  Rake::GemPackageTask.new(spec) do |p|
@@ -0,0 +1,197 @@
1
+ $:.unshift(File.dirname(__FILE__) + "/../lib/")
2
+ require File.join(File.dirname(__FILE__), 'test_helper')
3
+ require 'test/unit'
4
+ require 'active_crypto'
5
+
6
+ class User < ActiveRecord::Base
7
+ has_many :secrets
8
+ has_many :groups
9
+ keyholder
10
+ end
11
+
12
+ class Secret < ActiveRecord::Base
13
+ encrypt :name,:email, :key=>:user
14
+ belongs_to :user
15
+ has_many :children
16
+ end
17
+
18
+ class Child < ActiveRecord::Base
19
+ encrypt :email, :key=>:secret
20
+ belongs_to :secret
21
+ end
22
+
23
+ class Asset<ActiveRecord::Base
24
+ encrypt :title
25
+ has_many :caps,:dependent=>:destroy
26
+
27
+ def self.create(title,email)
28
+ asset=Asset.new
29
+ asset.set_session_key(EzCrypto::Key.generate)
30
+ asset.title=title
31
+ if asset.save
32
+ asset.share(email)
33
+ else
34
+ nil
35
+ end
36
+ end
37
+
38
+ def share(email=nil)
39
+ Cap.create_for_asset(self,email)
40
+ end
41
+
42
+ end
43
+
44
+ class Cap < ActiveRecord::Base
45
+ belongs_to :asset
46
+ encrypt :shared_key
47
+
48
+ def self.find_by_key(cap_key)
49
+ cap_key.chop
50
+ hash=Digest::SHA1.hexdigest(cap_key)
51
+ if (cap_key.length>=20) # Sanity check
52
+ cap=self.find_by_key_hash(hash)
53
+ if cap
54
+ cap.set_encoded_key(cap_key)
55
+ cap.asset.set_encoded_key(cap.shared_key)
56
+ cap
57
+ end
58
+ else
59
+ nil
60
+ end
61
+ end
62
+
63
+ def self.create_for_asset(asset,email=nil)
64
+ cap=Cap.new
65
+ cap.email=email if email
66
+ cap.asset=asset
67
+ if cap.save
68
+ cap.set_session_key(EzCrypto::Key.generate)
69
+ cap_key=cap.session_key.encode
70
+ cap.key_hash=Digest::SHA1.hexdigest(cap_key)
71
+ cap.shared_key=asset.session_key.encode
72
+ cap.save
73
+ cap_key
74
+ else
75
+ nil
76
+ end
77
+ end
78
+
79
+ end
80
+
81
+ class Group < ActiveRecord::Base
82
+ belongs_to :user
83
+ has_many :group_secrets
84
+
85
+ encrypt :name,:group_key, :key=>:user
86
+ end
87
+
88
+ class GroupSecret < ActiveRecord::Base
89
+ belongs_to :group
90
+
91
+ encrypt :title,:body, :key=>:group
92
+
93
+ end
94
+
95
+ class ActiveCryptoTest < Test::Unit::TestCase
96
+
97
+ def setup
98
+ end
99
+
100
+ def test_key_holder
101
+ user=User.new
102
+ user.name="bob"
103
+ user.save
104
+ assert user.kind_of?(ActiveRecord::Crypto::KeyHolder)
105
+ assert user.kind_of?(ActiveRecord::Base)
106
+ assert user.kind_of?(User)
107
+ assert_nil user.session_key
108
+ user.enter_password "shhcccc"
109
+ assert_not_nil user.session_key
110
+ assert_not_nil user.session_key.encrypt("test")
111
+ end
112
+
113
+ def test_encrypted_child
114
+ user=User.new
115
+ user.save
116
+ assert_nil user.session_key
117
+ user.enter_password "shhcccc"
118
+ assert_not_nil user.session_key
119
+ assert user.kind_of?(ActiveRecord::Crypto::KeyHolder)
120
+ assert user.kind_of?(ActiveRecord::Base)
121
+ assert user.kind_of?(User)
122
+
123
+ jill=user.secrets.create
124
+
125
+ assert_not_nil jill
126
+ assert jill.kind_of?(ActiveRecord::Crypto::AssociationKeyHolder)
127
+ assert jill.kind_of?(ActiveRecord::Crypto::KeyHolder)
128
+ assert jill.kind_of?(ActiveRecord::Crypto::Encrypted)
129
+ assert jill.kind_of?(ActiveRecord::Base)
130
+ assert jill.kind_of?(Secret)
131
+
132
+ assert jill.respond_to?(:session_key)
133
+
134
+ assert_not_nil jill.user
135
+ assert_not_nil jill.user.session_key
136
+
137
+
138
+ assert_not_nil jill.session_key
139
+ assert_equal user.session_key,jill.session_key
140
+
141
+ jill.name="jill"
142
+ jill.save
143
+
144
+
145
+ assert_equal "jill",jill.name
146
+
147
+ jill=user.secrets.first
148
+ assert_not_nil jill.session_key
149
+ assert_equal user.session_key,jill.session_key
150
+ assert_equal "jill",jill.name
151
+
152
+ child=jill.children.create
153
+ child.email="pelle@neubia.com"
154
+ child.save
155
+
156
+ assert_not_nil child.secret
157
+ assert_not_nil child.secret.session_key
158
+
159
+
160
+ assert_not_nil child.session_key
161
+ assert_equal user.session_key,child.session_key
162
+
163
+ assert_equal "pelle@neubia.com",child.email
164
+
165
+ child=jill.children.first
166
+
167
+ assert_not_nil child.secret
168
+ assert_not_nil child.secret.session_key
169
+
170
+
171
+ assert_not_nil child.session_key
172
+ assert_equal user.session_key,child.session_key
173
+
174
+ assert_equal "pelle@neubia.com",child.email
175
+
176
+ end
177
+
178
+ def test_caps
179
+ key=Asset.create("title","pelle@neubia.com")
180
+ assert_not_nil key
181
+ cap=Cap.find_by_key key
182
+ assert_not_nil cap
183
+ assert_not_nil cap.asset
184
+ assert_equal "title",cap.asset.title
185
+ assert_equal "pelle@neubia.com",cap.email
186
+
187
+ bob_key=cap.asset.share("bob@bob.com")
188
+ bob_cap=Cap.find_by_key bob_key
189
+
190
+ assert_not_equal key,bob_key
191
+ assert_not_nil bob_cap
192
+ assert_not_nil bob_cap.asset
193
+ assert_equal "title",bob_cap.asset.title
194
+ assert_equal "bob@bob.com",bob_cap.email
195
+ end
196
+ end
197
+
data/test/database.yml ADDED
@@ -0,0 +1,18 @@
1
+ sqlite:
2
+ :adapter: sqlite
3
+ :dbfile: crypto_test.sqlite.db
4
+ sqlite3:
5
+ :adapter: sqlite3
6
+ :dbfile: crypto_test.sqlite3.db
7
+ postgresql:
8
+ :adapter: postgresql
9
+ :username: postgres
10
+ :password: postgres
11
+ :database: crypto_test
12
+ :min_messages: ERROR
13
+ mysql:
14
+ :adapter: mysql
15
+ :host: localhost
16
+ :username: rails
17
+ :password:
18
+ :database: crypto_test
@@ -1,112 +1,212 @@
1
1
  $:.unshift(File.dirname(__FILE__) + "/../lib/")
2
2
 
3
3
  require 'test/unit'
4
+ require 'fileutils'
4
5
  require 'ezcrypto'
5
- require 'base64'
6
+ require 'base64'
6
7
 
7
8
  class EzCryptoTest < Test::Unit::TestCase
8
9
 
9
10
  def setup
10
11
  end
11
-
12
- def test_generate_alg_key
13
- assert_generate_alg_key "aes-128-cbc",16
14
- assert_generate_alg_key "aes-192-cbc",24
15
- assert_generate_alg_key "aes-256-cbc",32
16
- assert_generate_alg_key "rc2-40-cbc",5
17
- assert_generate_alg_key "rc2-64-cbc",8
18
- assert_generate_alg_key "rc4-64" ,8
19
- assert_generate_alg_key "blowfish" ,16
20
- assert_generate_alg_key "des" ,8
21
- end
22
-
23
- def test_with_password
24
- assert_with_password "","secret","aes-128-cbc",16
25
- assert_with_password "test","secret","aes-128-cbc",16
26
- assert_with_password "password","secret","aes-128-cbc",16
27
- assert_with_password "a�sldfad8q5�34j2�l4j24l6j2456","secret","aes-128-cbc",16
28
-
29
- assert_with_password "","secret","aes-192-cbc",24
30
- assert_with_password "test","secret","aes-192-cbc",24
31
- assert_with_password "password","secret","aes-192-cbc",24
32
- assert_with_password "a�sldfad8q5�34j2�l4j24l6j2456","secret","aes-192-cbc",24
33
-
34
- assert_with_password "","secret","aes-256-cbc",32
35
- assert_with_password "test","secret","aes-256-cbc",32
36
- assert_with_password "password","secret","aes-256-cbc",32
37
- assert_with_password "a�sldfad8q5�34j2�l4j24l6j2456","secret","aes-256-cbc",32
38
-
39
- end
40
-
41
- def test_encoded
42
- 0.upto 32 do |size|
43
- assert_encoded_keys size
44
- end
45
- end
46
-
47
- def test_encrypt
48
- 0.upto(CLEAR_TEXT.size-1) do |size|
49
- assert_encrypt CLEAR_TEXT[0..size]
50
- end
51
- end
52
-
53
- def test_decrypt
54
-
55
- 0.upto(CLEAR_TEXT.size) do |size|
56
- assert_decrypt CLEAR_TEXT[0..size]
57
- end
58
- end
59
-
60
- def test_decrypt64
61
- 0.upto(CLEAR_TEXT.size) do |size|
62
- assert_decrypt64 CLEAR_TEXT[0..size]
63
- end
64
- end
65
-
66
- def assert_key_size(size,key)
67
- assert_equal size,key.raw.size
68
- end
69
-
70
- def assert_generate_alg_key(algorithm,size)
71
- key=EzCrypto::Key.generate :algorithm=>algorithm
72
- assert_key_size size,key
73
- end
74
-
75
- def assert_with_password(password,salt,algorithm,size)
76
- key=EzCrypto::Key.with_password password,salt,:algorithm=>algorithm
77
- assert_key_size size,key
78
- assert_equal key.raw,EzCrypto::Key.with_password( password,salt,:algorithm=>algorithm).raw
79
- end
80
-
81
- def assert_encoded_keys(size)
82
- key=EzCrypto::Key.generate size
83
- key2=EzCrypto::Key.decode(key.encode)
84
- assert_equal key.raw, key2.raw
85
- end
86
-
87
- def assert_encrypt(clear)
88
- ALGORITHMS.each do |alg|
89
- key=EzCrypto::Key.generate :algorithm=>alg
90
- encrypted=key.encrypt clear
91
- assert_not_nil encrypted
92
- end
93
- end
94
-
95
- def assert_decrypt(clear)
96
- ALGORITHMS.each do |alg|
97
- key=EzCrypto::Key.generate :algorithm=>alg
98
- encrypted=key.encrypt clear
99
- assert_not_nil encrypted
100
- assert_equal clear,key.decrypt(encrypted)
101
- end
102
- end
103
- def assert_decrypt64(clear)
104
- key=EzCrypto::Key.generate
105
- encrypted=key.encrypt64 clear
106
- assert_not_nil encrypted
107
- assert_equal clear,key.decrypt64(encrypted)
108
- end
109
- ALGORITHMS=["aes128","bf","blowfish","des","des3","rc4","rc2"]
12
+
13
+ def test_generate_alg_key
14
+ assert_generate_alg_key "aes-128-cbc",16
15
+ assert_generate_alg_key "aes-192-cbc",24
16
+ assert_generate_alg_key "aes-256-cbc",32
17
+ assert_generate_alg_key "rc2-40-cbc",5
18
+ assert_generate_alg_key "rc2-64-cbc",8
19
+ assert_generate_alg_key "rc4-64" ,8
20
+ assert_generate_alg_key "blowfish" ,16
21
+ assert_generate_alg_key "des" ,8
22
+ end
23
+
24
+ def test_with_password
25
+ assert_with_password "","secret","aes-128-cbc",16
26
+ assert_with_password "test","secret","aes-128-cbc",16
27
+ assert_with_password "password","secret","aes-128-cbc",16
28
+ assert_with_password "a�sldfad8q5�34j2�l4j24l6j2456","secret","aes-128-cbc",16
29
+
30
+ assert_with_password "","secret","aes-192-cbc",24
31
+ assert_with_password "test","secret","aes-192-cbc",24
32
+ assert_with_password "password","secret","aes-192-cbc",24
33
+ assert_with_password "a�sldfad8q5�34j2�l4j24l6j2456","secret","aes-192-cbc",24
34
+
35
+ assert_with_password "","secret","aes-256-cbc",32
36
+ assert_with_password "test","secret","aes-256-cbc",32
37
+ assert_with_password "password","secret","aes-256-cbc",32
38
+ assert_with_password "a�sldfad8q5�34j2�l4j24l6j2456","secret","aes-256-cbc",32
39
+
40
+ end
41
+
42
+ def test_encoded
43
+ 0.upto 32 do |size|
44
+ assert_encoded_keys size
45
+ end
46
+ end
47
+
48
+ def test_encrypt
49
+ 0.upto(CLEAR_TEXT.size-1) do |size|
50
+ assert_encrypt CLEAR_TEXT[0..size]
51
+ end
52
+ end
53
+
54
+ def test_decrypt
55
+ 0.upto(CLEAR_TEXT.size) do |size|
56
+ assert_decrypt CLEAR_TEXT[0..size]
57
+ end
58
+ end
59
+
60
+ def test_decrypt64
61
+ 0.upto(CLEAR_TEXT.size) do |size|
62
+ assert_decrypt64 CLEAR_TEXT[0..size]
63
+ end
64
+ end
65
+
66
+ def test_keyfile_store_load
67
+
68
+ algo, size = "aes-256-cbc", 32
69
+ keyfile = 'ezcrypto-test.key'
70
+
71
+ FileUtils.rm [keyfile], :force => true
72
+ key = EzCrypto::Key.generate :algorithm => algo
73
+ assert_file_not_exists keyfile
74
+ key.store keyfile
75
+ assert_file_exists keyfile
76
+ assert_file_permissions keyfile, 0100400
77
+
78
+ key2 = EzCrypto::Key.load(keyfile)
79
+ assert_equal key.raw, key2.raw
80
+
81
+ FileUtils.rm [keyfile], :force => true
82
+ end
83
+
84
+ def test_filestuff_with_defaults
85
+
86
+ clearfile = 'lorem_ipsum.txt'
87
+ keyfile = 'lorem_ipsum.key'
88
+ algo, size = "aes-256-cbc", 32
89
+
90
+ File.open(clearfile, 'w') { |f| f.write(CLEAR_TEXT) }
91
+ assert_file_contains clearfile, CLEAR_TEXT
92
+
93
+ key = EzCrypto::Key.generate :algorithm => algo
94
+
95
+ # default behaviour: remove clearfile, append '.ez' suffix
96
+ cryptfile = key.encrypt_file(clearfile)
97
+ assert_equal cryptfile, clearfile + ".ez"
98
+ assert_file_not_exists clearfile
99
+ assert_file_exists cryptfile
100
+ assert_file_contains cryptfile, key.encrypt(CLEAR_TEXT)
101
+
102
+ # default behaviour: unlink cryptfile and remove suffix from filename
103
+ clearfile = key.decrypt_file cryptfile
104
+ assert_file_exists clearfile
105
+ assert_file_not_exists cryptfile
106
+ assert_file_contains clearfile, CLEAR_TEXT
107
+ FileUtils.rm [keyfile, clearfile, cryptfile], :force => true
108
+ end
109
+
110
+ def test_filestuff_with_options
111
+
112
+ clearfile = 'lorem_ipsum.txt'
113
+ keyfile = 'lorem_ipsum.key'
114
+ algo, size = "aes-256-cbc", 32
115
+
116
+ File.open(clearfile, 'w') { |f| f.write(CLEAR_TEXT) }
117
+ assert_file_contains clearfile, CLEAR_TEXT
118
+
119
+ key = EzCrypto::Key.generate :algorithm => algo
120
+
121
+ # with options: keep the original file, auto-create a
122
+ # new filename with a user-defined suffix
123
+ cryptfile = key.encrypt_file(clearfile, nil, :autoclean => false, :suffix => '.Encrypted')
124
+ assert_equal cryptfile, clearfile + ".Encrypted", 'suffix was added'
125
+ assert_file_exists clearfile
126
+ assert_file_exists cryptfile
127
+ assert_file_permissions cryptfile, 0100600
128
+
129
+ assert_raises(Errno::EEXIST, "the original file would not be overwritten") {
130
+ key.decrypt_file(cryptfile, nil, :autoclean => false, :suffix => '.Encrypted')
131
+ }
132
+ FileUtils.rm [clearfile], :force => true
133
+
134
+ clearfile = key.decrypt_file(cryptfile, nil, :autoclean => false, :suffix => '.Encrypted')
135
+ assert_equal cryptfile, clearfile + ".Encrypted", 'suffix was removed'
136
+ assert_file_exists clearfile
137
+ assert_file_exists cryptfile
138
+ assert_file_permissions cryptfile, 0100600
139
+ assert_file_contains clearfile, CLEAR_TEXT
140
+
141
+ FileUtils.rm [keyfile, clearfile, cryptfile], :force => true
142
+ end
143
+
144
+ def assert_file_permissions(filename, mode, msg='')
145
+ fmode = File.stat(filename).mode
146
+ assert_equal fmode, mode, msg
147
+ end
148
+
149
+ def assert_file_exists(filename)
150
+ assert File.exists?(filename)
151
+ end
152
+
153
+ def assert_file_not_exists(filename)
154
+ assert !File.exists?(filename)
155
+ end
156
+
157
+ def assert_file_contains(filename, expected)
158
+ assert_file_exists(filename)
159
+ content = File.open(filename,'r').read
160
+ assert_equal expected, content
161
+ end
162
+
163
+ def assert_key_size(size,key)
164
+ assert_equal size,key.raw.size
165
+ end
166
+
167
+ def assert_generate_alg_key(algorithm,size)
168
+ key=EzCrypto::Key.generate :algorithm=>algorithm
169
+ assert_key_size size,key
170
+ end
171
+
172
+ def assert_with_password(password,salt,algorithm,size)
173
+ key=EzCrypto::Key.with_password password,salt,:algorithm=>algorithm
174
+ assert_key_size size,key
175
+ assert_equal key.raw,EzCrypto::Key.with_password( password,salt,:algorithm=>algorithm).raw
176
+ end
177
+
178
+ def assert_encoded_keys(size)
179
+ key=EzCrypto::Key.generate size
180
+ key2=EzCrypto::Key.decode(key.encode)
181
+ assert_equal key.raw, key2.raw
182
+ end
183
+
184
+ def assert_encrypt(clear)
185
+ ALGORITHMS.each do |alg|
186
+ key=EzCrypto::Key.generate :algorithm=>alg
187
+ encrypted=key.encrypt clear
188
+ assert_not_nil encrypted
189
+ end
190
+ end
191
+
192
+ def assert_decrypt(clear)
193
+ ALGORITHMS.each do |alg|
194
+ key=EzCrypto::Key.generate :algorithm=>alg
195
+ encrypted=key.encrypt clear
196
+ assert_not_nil encrypted
197
+ assert_equal clear,key.decrypt(encrypted)
198
+ end
199
+ end
200
+
201
+ def assert_decrypt64(clear)
202
+ key=EzCrypto::Key.generate
203
+ encrypted=key.encrypt64 clear
204
+ assert_not_nil encrypted
205
+ assert_equal clear,key.decrypt64(encrypted)
206
+ end
207
+
208
+ ALGORITHMS=["aes128","bf","blowfish","des","des3","rc4","rc2"]
110
209
  CLEAR_TEXT="Lorem ipsum dolor sit amet, suspendisse id interdum mus leo id. Sapien tempus consequat nullam, platea vitae sociis sed elementum et fermentum, vel praesent eget. Sed blandit augue, molestie mus sed habitant, semper voluptatibus neque, nullam a augue. Aptent imperdiet curabitur, quam quis laoreet. Dolor magna. Quis vestibulum amet eu arcu fringilla nibh, mi urna sunt dictumst nulla, elit quisque purus eros, sem hendrerit. Vulputate tortor rhoncus ac nonummy tortor nulla. Nunc id nunc luctus ligula."
111
210
  end
112
211
 
212
+
@@ -0,0 +1,36 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'test/unit'
4
+ require 'rubygems'
5
+ require 'action_pack'
6
+ require 'active_record'
7
+ #require 'active_support/binding_of_caller'
8
+ #require 'active_support/breakpoint'
9
+
10
+
11
+ unless defined?(RAILS_ROOT)
12
+ root_path = File.join(File.dirname(__FILE__), '..')
13
+ unless RUBY_PLATFORM =~ /mswin32/
14
+ require 'pathname'
15
+ root_path = Pathname.new(root_path).cleanpath.to_s
16
+ end
17
+ RAILS_ROOT = root_path
18
+ end
19
+
20
+
21
+ require 'active_record/fixtures'
22
+ require 'initializer'
23
+
24
+ Rails::Initializer.run(:set_load_path)
25
+
26
+ config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
27
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
28
+ ActiveRecord::Base.establish_connection(
29
+ :adapter => "mysql",
30
+ :host => "localhost",
31
+ :username => "root",
32
+ :password => "",
33
+ :database => "crypto_test"
34
+ )
35
+ #load(File.dirname(__FILE__) + "/schema.rb")
36
+
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.10
3
3
  specification_version: 1
4
4
  name: ezcrypto
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.2.1
7
- date: 2005-11-02
6
+ version: "0.3"
7
+ date: 2006-02-25
8
8
  summary: Simplified encryption library.
9
9
  require_paths:
10
10
  - lib
@@ -32,17 +32,13 @@ files:
32
32
  - README_ACTIVE_CRYPTO
33
33
  - MIT-LICENSE
34
34
  - CHANGELOG
35
+ - init.rb
35
36
  - lib/active_crypto.rb
36
- - lib/CVS
37
37
  - lib/ezcrypto.rb
38
- - lib/CVS/Entries
39
- - lib/CVS/Repository
40
- - lib/CVS/Root
41
- - test/CVS
38
+ - test/active_crypto_test.rb
39
+ - test/database.yml
42
40
  - test/ezcrypto_test.rb
43
- - test/CVS/Entries
44
- - test/CVS/Repository
45
- - test/CVS/Root
41
+ - test/test_helper.rb
46
42
  test_files: []
47
43
  rdoc_options: []
48
44
  extra_rdoc_files: []
data/lib/CVS/Entries DELETED
@@ -1,3 +0,0 @@
1
- /ezcrypto.rb/1.4/Sun Oct 30 22:24:34 2005//
2
- /active_crypto.rb/1.4/Wed Nov 2 09:12:07 2005//
3
- D
data/lib/CVS/Repository DELETED
@@ -1 +0,0 @@
1
- ezcrypto/lib
data/lib/CVS/Root DELETED
@@ -1 +0,0 @@
1
- :ext:pelleb@rubyforge.net:/var/cvs/ezcrypto
data/test/CVS/Entries DELETED
@@ -1,2 +0,0 @@
1
- /ezcrypto_test.rb/1.1.1.1/Wed Jul 20 18:40:51 2005//
2
- D
data/test/CVS/Repository DELETED
@@ -1 +0,0 @@
1
- ezcrypto/test
data/test/CVS/Root DELETED
@@ -1 +0,0 @@
1
- :ext:pelleb@rubyforge.net:/var/cvs/ezcrypto