symmetric-encryption 3.4.0 → 3.6.0

Sign up to get free protection for your applications and to get access to all the features.
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