symmetric-encryption 3.4.0 → 3.6.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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +102 -55
  3. data/Rakefile +13 -8
  4. data/lib/rails/generators/symmetric_encryption/config/config_generator.rb +1 -1
  5. data/lib/rails/generators/symmetric_encryption/heroku_config/templates/symmetric-encryption.yml +2 -2
  6. data/lib/rails/generators/symmetric_encryption/new_keys/new_keys_generator.rb +2 -2
  7. data/lib/symmetric_encryption.rb +7 -6
  8. data/lib/symmetric_encryption/cipher.rb +4 -4
  9. data/lib/symmetric_encryption/extensions/active_record/base.rb +6 -46
  10. data/lib/symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key.rb +129 -0
  11. data/lib/symmetric_encryption/{mongoid.rb → extensions/mongoid/encrypted.rb} +12 -46
  12. data/lib/symmetric_encryption/generator.rb +54 -0
  13. data/lib/symmetric_encryption/railtie.rb +3 -3
  14. data/lib/symmetric_encryption/railties/symmetric_encryption.rake +1 -1
  15. data/lib/symmetric_encryption/railties/symmetric_encryption_validator.rb +1 -1
  16. data/lib/symmetric_encryption/reader.rb +3 -3
  17. data/lib/symmetric_encryption/symmetric_encryption.rb +25 -15
  18. data/lib/symmetric_encryption/version.rb +1 -1
  19. data/lib/symmetric_encryption/writer.rb +4 -4
  20. data/test/active_record_test.rb +474 -0
  21. data/test/cipher_test.rb +15 -15
  22. data/test/config/mongo_mapper.yml +7 -0
  23. data/test/{field_encrypted_test.rb → mongo_mapper_test.rb} +68 -67
  24. data/test/mongoid_test.rb +535 -0
  25. data/test/reader_test.rb +10 -10
  26. data/test/symmetric_encryption_test.rb +27 -27
  27. data/test/test_db.sqlite3 +0 -0
  28. data/test/test_helper.rb +0 -1
  29. data/test/writer_test.rb +2 -2
  30. metadata +14 -8
  31. data/test/attr_encrypted_test.rb +0 -622
@@ -0,0 +1,129 @@
1
+ # Support Encryption and decryption of fields in MongoMapper
2
+ module MongoMapper
3
+ module Plugins
4
+ module EncryptedKey
5
+ extend ActiveSupport::Concern
6
+
7
+ COERCION_MAP = {
8
+ String => :string,
9
+ Integer => :integer,
10
+ Float => :float,
11
+ BigDecimal => :decimal,
12
+ DateTime => :datetime,
13
+ Time => :time,
14
+ Date => :date,
15
+ Boolean => :boolean,
16
+ Hash => :json
17
+ }
18
+
19
+ module ClassMethods
20
+ # MongoMapper::Document.encrypted_key
21
+ #
22
+ # Support automatic encryption and decryption of fields in MongoMapper
23
+ #
24
+ # Example:
25
+ #
26
+ # class Person
27
+ # include MongoMapper::Document
28
+ #
29
+ # key :name, String
30
+ # encrypted_key :social_security_number, String
31
+ # key :date_of_birth, Date
32
+ # encrypted_key :life_history, String, encrypted: { compress: true, random_iv: true }
33
+ #
34
+ # # Encrypted fields are _always_ stored in Mongo as a String
35
+ # # By specifying a type other than String, Symmetric Encryption will
36
+ # # perform the necessary conversions
37
+ # #
38
+ # # The following types are supported:
39
+ # # String
40
+ # # Integer
41
+ # # Float
42
+ # # BigDecimal
43
+ # # DateTime
44
+ # # Time
45
+ # # Date
46
+ # # Hash - (Stored as encrypted JSON in MongoDB)
47
+ # encrypted_key :age, Integer, encrypted: { random_iv: true }
48
+ # end
49
+ #
50
+ # The above document results in the following document in the Mongo collection 'persons':
51
+ # {
52
+ # "name" : "Joe",
53
+ # "encrypted_social_security_number" : "...",
54
+ # "age" : 21
55
+ # "encrypted_life_history" : "...",
56
+ # }
57
+ #
58
+ # Symmetric Encryption creates the getters and setters to be able to work with the field
59
+ # in it's decrypted form. For example
60
+ #
61
+ # Example:
62
+ # person = Person.where(encrypted_social_security_number: '...').first
63
+ #
64
+ # puts "Decrypted Social Security Number is: #{person.social_security_number}"
65
+ #
66
+ # # Or is the same as
67
+ # puts "Decrypted Social Security Number is: #{SymmetricEncryption.decrypt(person.encrypted_social_security_number)}"
68
+ #
69
+ # # Sets the encrypted_social_security_number to encrypted version
70
+ # person.social_security_number = "123456789"
71
+ #
72
+ # # Or, is equivalent to:
73
+ # person.encrypted_social_security_number = SymmetricEncryption.encrypt("123456789")
74
+ #
75
+ # Note: Only "String" types are currently supported for encryption
76
+ #
77
+ # Note: Unlike attr_encrypted finders must use the encrypted field name
78
+ # Invalid Example, does not work:
79
+ # person = Person.where(social_security_number: '123456789').first
80
+ #
81
+ # Valid Example:
82
+ # person = Person.where(encrypted_social_security_number: SymmetricEncryption.encrypt('123456789')).first
83
+ #
84
+ # Defines all the fields that are accessible on the Document
85
+ # For each field that is defined, a getter and setter will be
86
+ # added as an instance method to the Document.
87
+ #
88
+ # @example Define an encrypted key
89
+ # encrypted_key :social_security_number, String, encrypted: {compress: false, random_iv: false}
90
+ # encrypted_key :sensitive_text, String, encrypted: {compress: true, random_iv: true}
91
+ #
92
+ # @param [ Symbol ] name The name of the key.
93
+ # @param [ Object ] type The type of the key.
94
+ # @param [ Hash ] options The options to pass to the field, including any MongoMapper specific options
95
+ #
96
+ # @option options [ Hash ] :encrypted consists of:
97
+ # @option options [ Boolean ] :random_iv Whether the encrypted value should use a random IV every time the field is encrypted.
98
+ # @option options [ Boolean ] :compress Whether to compress this encrypted field
99
+ # @option options [ Symbol ] :encrypt_as Name of the encypted field in Mongo
100
+ #
101
+ # Some of the other regular MongoMapper options:
102
+ # :default, :alias, :field_name, :accessors, :abbr
103
+ #
104
+ # Note:
105
+ # Use MongoMapper's built-in support for :field_name to specify a different
106
+ # field name in MongoDB for the encrypted field from what is used via the model
107
+ #
108
+ def encrypted_key(key_name, type, full_options={})
109
+ full_options = full_options.is_a?(Hash) ? full_options.dup : {}
110
+ options = full_options.delete(:encrypted) || {}
111
+ # Support overriding the name of the decrypted attribute
112
+ encrypted_key_name = options.delete(:encrypt_as) || "encrypted_#{key_name}"
113
+ options[:type] = COERCION_MAP[type] unless [:yaml, :json].include?(options[:type])
114
+
115
+ raise "Invalid type: #{type.inspect}. Valid types: #{COERCION_MAP.keys.join(',')}" unless options[:type]
116
+
117
+ SymmetricEncryption::Generator.generate_decrypted_accessors(self, key_name, encrypted_key_name, options)
118
+
119
+ key(encrypted_key_name, String, full_options)
120
+ end
121
+
122
+ end
123
+
124
+ end
125
+ end
126
+ end
127
+
128
+ MongoMapper::Document.plugin(MongoMapper::Plugins::EncryptedKey)
129
+ MongoMapper::EmbeddedDocument.plugin(MongoMapper::Plugins::EncryptedKey)
@@ -15,17 +15,17 @@
15
15
  # class Person
16
16
  # include Mongoid::Document
17
17
  #
18
- # field :name, :type => String
19
- # field :encrypted_social_security_number, :type => String, :encrypted => true
20
- # field :date_of_birth, :type => Date
21
- # field :encrypted_life_history, :type => String, :encrypted => {:compress => true, :random_iv => true}
18
+ # field :name, type: String
19
+ # field :encrypted_social_security_number, type: String, encrypted: true
20
+ # field :date_of_birth, type: Date
21
+ # field :encrypted_life_history, type: String, encrypted: {compress: true, random_iv: true}
22
22
  #
23
23
  # # Encrypted fields are _always_ stored in Mongo as a String
24
24
  # # To get the result back as an Integer, Symmetric Encryption can do the
25
25
  # # necessary conversions by specifying the internal type as an option
26
26
  # # to :encrypted
27
27
  # # #see SymmetricEncryption::COERCION_TYPES for full list of types
28
- # field :encrypted_age, :type => String, :encrypted => {:type => :integer, :random_iv => true}
28
+ # field :encrypted_age, type: String, encrypted: {type: :integer, random_iv: true}
29
29
  # end
30
30
  #
31
31
  # The above document results in the following document in the Mongo collection 'persons':
@@ -40,7 +40,7 @@
40
40
  # in it's unencrypted form. For example
41
41
  #
42
42
  # Example:
43
- # person = Person.where(:encrypted_social_security_number => '...').first
43
+ # person = Person.where(encrypted_social_security_number: '...').first
44
44
  #
45
45
  # puts "Decrypted Social Security Number is: #{person.social_security_number}"
46
46
  #
@@ -57,18 +57,18 @@
57
57
  #
58
58
  # Note: Unlike attr_encrypted finders must use the encrypted field name
59
59
  # Invalid Example, does not work:
60
- # person = Person.where(:social_security_number => '123456789').first
60
+ # person = Person.where(social_security_number: '123456789').first
61
61
  #
62
62
  # Valid Example:
63
- # person = Person.where(:encrypted_social_security_number => SymmetricEncryption.encrypt('123456789')).first
63
+ # person = Person.where(encrypted_social_security_number: SymmetricEncryption.encrypt('123456789')).first
64
64
  #
65
65
  # Defines all the fields that are accessible on the Document
66
66
  # For each field that is defined, a getter and setter will be
67
67
  # added as an instance method to the Document.
68
68
  #
69
69
  # @example Define a field.
70
- # field :social_security_number, :type => String, :encrypted => {:compress => false, :random_iv => false}
71
- # field :sensitive_text, :type => String, :encrypted => {:compress => true, :random_iv => true}
70
+ # field :social_security_number, type: String, encrypted: {compress: false, random_iv: false}
71
+ # field :sensitive_text, type: String, encrypted: {compress: true, random_iv: true}
72
72
  #
73
73
  # @param [ Symbol ] name The name of the field.
74
74
  # @param [ Hash ] options The options to pass to the field.
@@ -92,6 +92,7 @@ Mongoid::Fields.option :encrypted do |model, field, options|
92
92
  options = options.is_a?(Hash) ? options.dup : {}
93
93
  encrypted_field_name = field.name
94
94
 
95
+ # Support overriding the name of the decrypted attribute
95
96
  decrypted_field_name = options.delete(:decrypt_as)
96
97
  if decrypted_field_name.nil? && encrypted_field_name.to_s.start_with?('encrypted_')
97
98
  decrypted_field_name = encrypted_field_name.to_s['encrypted_'.length..-1]
@@ -101,42 +102,7 @@ Mongoid::Fields.option :encrypted do |model, field, options|
101
102
  raise "SymmetricEncryption for Mongoid. Encryption enabled for field #{encrypted_field_name}. It must either start with 'encrypted_' or the option :decrypt_as must be supplied"
102
103
  end
103
104
 
104
- random_iv = options.delete(:random_iv) || false
105
- compress = options.delete(:compress) || false
106
- type = options.delete(:type) || :string
107
- raise "Invalid type: #{type.inspect}. Valid types: #{SymmetricEncryption::COERCION_TYPES.inspect}" unless SymmetricEncryption::COERCION_TYPES.include?(type)
108
-
109
- options.each {|option| warn "Ignoring unknown option #{option.inspect} supplied to Mongoid :encrypted for #{model}##{field}"}
110
-
111
- if model.const_defined?(:EncryptedAttributes, _search_ancestors = false)
112
- mod = model.const_get(:EncryptedAttributes)
113
- else
114
- mod = model.const_set(:EncryptedAttributes, Module.new)
115
- model.send(:include, mod)
116
- end
117
-
118
- # Generate getter and setter methods
119
- mod.module_eval(<<-EOS, __FILE__, __LINE__ + 1)
120
- # Set the un-encrypted field
121
- # Also updates the encrypted field with the encrypted value
122
- # Freeze the decrypted field value so that it is not modified directly
123
- def #{decrypted_field_name}=(value)
124
- v = SymmetricEncryption::coerce(value, :#{type})
125
- self.#{encrypted_field_name} = @stored_#{encrypted_field_name} = ::SymmetricEncryption.encrypt(v,#{random_iv},#{compress},:#{type})
126
- @#{decrypted_field_name} = v.freeze
127
- end
128
-
129
- # Returns the decrypted value for the encrypted field
130
- # The decrypted value is cached and is only decrypted if the encrypted value has changed
131
- # If this method is not called, then the encrypted value is never decrypted
132
- def #{decrypted_field_name}
133
- if @stored_#{encrypted_field_name} != self.#{encrypted_field_name}
134
- @#{decrypted_field_name} = ::SymmetricEncryption.decrypt(self.#{encrypted_field_name},version=nil,:#{type}).freeze
135
- @stored_#{encrypted_field_name} = self.#{encrypted_field_name}
136
- end
137
- @#{decrypted_field_name}
138
- end
139
- EOS
105
+ SymmetricEncryption::Generator.generate_decrypted_accessors(model, decrypted_field_name, encrypted_field_name, options)
140
106
  end
141
107
  end
142
108
 
@@ -0,0 +1,54 @@
1
+ module SymmetricEncryption
2
+ module Generator
3
+ # Common internal method for generating accessors for decrypted accessors
4
+ # Primarily used by extensions
5
+ def self.generate_decrypted_accessors(model, decrypted_name, encrypted_name, options)
6
+
7
+ random_iv = options.delete(:random_iv) || false
8
+ compress = options.delete(:compress) || false
9
+ type = options.delete(:type) || :string
10
+
11
+ # For backward compatibility
12
+ if options.delete(:marshal) == true
13
+ warn("The :marshal option has been deprecated in favor of :type. For example: attr_encrypted name, type: :yaml")
14
+ raise "Marshal is depreacted and cannot be used in conjunction with :type, just use :type. For #{params.inspect}" if type != :string
15
+ type = :yaml
16
+ end
17
+
18
+ options.each {|option| warn "Ignoring unknown option #{option.inspect} supplied when encrypting #{decrypted_name} with #{params.inspect}"}
19
+
20
+ raise "Invalid type: #{type.inspect}. Valid types: #{SymmetricEncryption::COERCION_TYPES.inspect}" unless SymmetricEncryption::COERCION_TYPES.include?(type)
21
+
22
+ if model.const_defined?(:EncryptedAttributes, _search_ancestors = false)
23
+ mod = model.const_get(:EncryptedAttributes)
24
+ else
25
+ mod = model.const_set(:EncryptedAttributes, Module.new)
26
+ model.send(:include, mod)
27
+ end
28
+
29
+ # Generate getter and setter methods
30
+ mod.module_eval(<<-EOS, __FILE__, __LINE__ + 1)
31
+ # Set the un-encrypted field
32
+ # Also updates the encrypted field with the encrypted value
33
+ # Freeze the decrypted field value so that it is not modified directly
34
+ def #{decrypted_name}=(value)
35
+ v = SymmetricEncryption::coerce(value, :#{type})
36
+ self.#{encrypted_name} = @stored_#{encrypted_name} = ::SymmetricEncryption.encrypt(v,#{random_iv},#{compress},:#{type})
37
+ @#{decrypted_name} = v.freeze
38
+ end
39
+
40
+ # Returns the decrypted value for the encrypted field
41
+ # The decrypted value is cached and is only decrypted if the encrypted value has changed
42
+ # If this method is not called, then the encrypted value is never decrypted
43
+ def #{decrypted_name}
44
+ if @stored_#{encrypted_name} != self.#{encrypted_name}
45
+ @#{decrypted_name} = ::SymmetricEncryption.decrypt(self.#{encrypted_name},version=nil,:#{type}).freeze
46
+ @stored_#{encrypted_name} = self.#{encrypted_name}
47
+ end
48
+ @#{decrypted_name}
49
+ end
50
+
51
+ EOS
52
+ end
53
+ end
54
+ end
@@ -8,9 +8,9 @@ module SymmetricEncryption #:nodoc:
8
8
  # module MyApplication
9
9
  # class Application < Rails::Application
10
10
  # config.symmetric_encryption.cipher = SymmetricEncryption::Cipher.new(
11
- # :key => '1234567890ABCDEF1234567890ABCDEF',
12
- # :iv => '1234567890ABCDEF',
13
- # :cipher_name => 'aes-128-cbc'
11
+ # key: '1234567890ABCDEF1234567890ABCDEF',
12
+ # iv: '1234567890ABCDEF',
13
+ # cipher_name: 'aes-128-cbc'
14
14
  # )
15
15
  # end
16
16
  # end
@@ -66,7 +66,7 @@ namespace :symmetric_encryption do
66
66
  if input_filename && output_filename
67
67
  puts "\nEncrypting file: #{input_filename} and writing to: #{output_filename}\n\n"
68
68
  ::File.open(input_filename, 'rb') do |input_file|
69
- SymmetricEncryption::Writer.open(output_filename, :compress => compress) do |output_file|
69
+ SymmetricEncryption::Writer.open(output_filename, compress: compress) do |output_file|
70
70
  while !input_file.eof?
71
71
  output_file.write(input_file.read(block_size))
72
72
  end
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # Example:
4
4
  # class MyModel < ActiveRecord::Base
5
- # validates :encrypted_ssn, :symmetric_encryption => true
5
+ # validates :encrypted_ssn, symmetric_encryption: true
6
6
  # end
7
7
  #
8
8
  # m = MyModel.new
@@ -63,15 +63,15 @@ module SymmetricEncryption
63
63
  # end
64
64
  #
65
65
  # # Example: Read, Unencrypt and decompress data in a file
66
- # SymmetricEncryption::Reader.open('encrypted_compressed.zip', :compress => true) do |file|
66
+ # SymmetricEncryption::Reader.open('encrypted_compressed.zip', compress: true) do |file|
67
67
  # file.each_line {|line| p line }
68
68
  # end
69
69
  #
70
70
  # # Example: Reading from a CSV file
71
71
  #
72
- # require 'fastercsv'
72
+ # require 'csv'
73
73
  # begin
74
- # csv = FasterCSV.new(SymmetricEncryption::Reader.open('csv_encrypted'))
74
+ # csv = CSV.new(SymmetricEncryption::Reader.open('csv_encrypted'))
75
75
  # csv.each {|row| p row}
76
76
  # ensure
77
77
  # csv.close if csv
@@ -33,9 +33,9 @@ module SymmetricEncryption
33
33
  # Example: For testing purposes the following test cipher can be used:
34
34
  #
35
35
  # SymmetricEncryption.cipher = SymmetricEncryption::Cipher.new(
36
- # :key => '1234567890ABCDEF1234567890ABCDEF',
37
- # :iv => '1234567890ABCDEF',
38
- # :cipher => 'aes-128-cbc'
36
+ # key: '1234567890ABCDEF1234567890ABCDEF',
37
+ # iv: '1234567890ABCDEF',
38
+ # cipher: 'aes-128-cbc'
39
39
  # )
40
40
  def self.cipher=(cipher)
41
41
  raise "Cipher must be similar to SymmetricEncryption::Ciphers" unless cipher.nil? || (cipher.respond_to?(:encrypt) && cipher.respond_to?(:decrypt))
@@ -311,7 +311,7 @@ module SymmetricEncryption
311
311
  elsif !cipher_cfg[:iv]
312
312
  iv = rsa_key.public_encrypt(key_pair[:iv])
313
313
  puts "Generated new Symmetric Key for encryption. Set the IV environment variable in #{environment} to:"
314
- puts ::Base64.encode64(key)
314
+ puts ::Base64.encode64(iv)
315
315
  end
316
316
  end
317
317
 
@@ -413,7 +413,7 @@ module SymmetricEncryption
413
413
  if key_filename = config.delete(:key_filename)
414
414
  raise "Missing mandatory config parameter :private_rsa_key when :key_filename is supplied" unless rsa
415
415
  encrypted_key = begin
416
- File.read(key_filename, :open_args => ['rb'])
416
+ File.open(key_filename, 'rb'){|f| f.read}
417
417
  rescue Errno::ENOENT
418
418
  puts "\nSymmetric Encryption key file: '#{key_filename}' not found or readable."
419
419
  puts "To generate the keys for the first time run: rails generate symmetric_encryption:new_keys\n\n"
@@ -425,7 +425,7 @@ module SymmetricEncryption
425
425
  if iv_filename = config.delete(:iv_filename)
426
426
  raise "Missing mandatory config parameter :private_rsa_key when :iv_filename is supplied" unless rsa
427
427
  encrypted_iv = begin
428
- File.read(iv_filename, :open_args => ['rb']) if iv_filename
428
+ File.open(iv_filename, 'rb'){|f| f.read} if iv_filename
429
429
  rescue Errno::ENOENT
430
430
  puts "\nSymmetric Encryption initialization vector file: '#{iv_filename}' not found or readable."
431
431
  puts "To generate the keys for the first time run: rails generate symmetric_encryption:new_keys\n\n"
@@ -438,6 +438,11 @@ module SymmetricEncryption
438
438
  raise "Missing mandatory config parameter :private_rsa_key when :encrypted_key is supplied" unless rsa
439
439
  # Decode value first using encoding specified
440
440
  encrypted_key = ::Base64.decode64(encrypted_key)
441
+ if !encrypted_key || encrypted_key.empty?
442
+ puts "\nSymmetric Encryption encrypted_key not found."
443
+ puts "To generate the keys for the first time run: rails generate symmetric_encryption:new_keys\n\n"
444
+ return
445
+ end
441
446
  config[:key] = rsa.private_decrypt(encrypted_key)
442
447
  end
443
448
 
@@ -445,6 +450,11 @@ module SymmetricEncryption
445
450
  raise "Missing mandatory config parameter :private_rsa_key when :encrypted_iv is supplied" unless rsa
446
451
  # Decode value first using encoding specified
447
452
  encrypted_iv = ::Base64.decode64(encrypted_iv)
453
+ if !encrypted_key || encrypted_key.empty?
454
+ puts "\nSymmetric Encryption encrypted_iv not found."
455
+ puts "To generate the keys for the first time run: rails generate symmetric_encryption:new_keys\n\n"
456
+ return
457
+ end
448
458
  config[:iv] = rsa.private_decrypt(encrypted_iv)
449
459
  end
450
460
 
@@ -460,7 +470,7 @@ module SymmetricEncryption
460
470
  # Coerce given value into given type
461
471
  # Does not coerce json or yaml values
462
472
  def self.coerce(value, type, from_type=nil)
463
- return if value.nil?
473
+ return if value.nil? || (value.is_a?(String) && (value !~ /[^[:space:]]/))
464
474
 
465
475
  from_type ||= value.class
466
476
  case type
@@ -519,13 +529,13 @@ module SymmetricEncryption
519
529
  end
520
530
 
521
531
  COERCION_TYPE_MAP = {
522
- :string => String,
523
- :integer => Integer,
524
- :float => Float,
525
- :decimal => BigDecimal,
526
- :datetime => DateTime,
527
- :time => Time,
528
- :date => Date
532
+ string: String,
533
+ integer: Integer,
534
+ float: Float,
535
+ decimal: BigDecimal,
536
+ datetime: DateTime,
537
+ time: Time,
538
+ date: Date
529
539
  }
530
540
 
531
541
  # With Ruby 1.9 strings have encodings
@@ -534,4 +544,4 @@ module SymmetricEncryption
534
544
  UTF8_ENCODING = Encoding.find("UTF-8")
535
545
  end
536
546
 
537
- end
547
+ end
@@ -1,3 +1,3 @@
1
1
  module SymmetricEncryption #:nodoc
2
- VERSION = "3.4.0"
2
+ VERSION = "3.6.0"
3
3
  end
@@ -83,17 +83,17 @@ module SymmetricEncryption
83
83
  # end
84
84
  #
85
85
  # # Example: Compress, Encrypt and write data to a file
86
- # SymmetricEncryption::Writer.open('encrypted_compressed.zip', :compress => true) do |file|
86
+ # SymmetricEncryption::Writer.open('encrypted_compressed.zip', compress: true) do |file|
87
87
  # file.write "Hello World\n"
88
88
  # file.write "Compress this\n"
89
89
  # file.write "Keep this safe and secure\n"
90
90
  # end
91
91
  #
92
92
  # # Example: Writing to a CSV file
93
- # require 'fastercsv'
93
+ # require 'csv'
94
94
  # begin
95
- # # Must supply :row_sep for FasterCSV otherwise it will attempt to read from and then rewind the file
96
- # csv = FasterCSV.new(SymmetricEncryption::Writer.open('csv_encrypted'), :row_sep => "\n")
95
+ # # Must supply :row_sep for CSV otherwise it will attempt to read from and then rewind the file
96
+ # csv = CSV.new(SymmetricEncryption::Writer.open('csv_encrypted'), row_sep: "\n")
97
97
  # csv << [1,2,3,4,5]
98
98
  # ensure
99
99
  # csv.close if csv