Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

CLA-561 Add support for Base16 (Hex) encoding and push encoding and d…

…ecoding back into Cipher as part of encrypt and decrypt
  • Loading branch information...
commit a48b67f9813214bd097b54cfe6cbaed38afa707d 1 parent 03583bb
@reidmorrison authored
View
2  .rvmrc
@@ -1,2 +1,2 @@
-rvm use ruby-1.9.3-p194@symmetric-encryption --create
+rvm use ruby@symmetric-encryption --create
export RACK_ENV=test
View
11 README.md
@@ -27,7 +27,7 @@ created, managed and further secured by System Administrators. This prevents
developers having or needing to have access to the symmetric encryption keys
* The Operating System security features limit access to the Symmetric Encryption
key files to System Administrators and the userid under which the Rails application runs.
-* The files in which the Symmetric Encryption keys are stored are futher
+* The files in which the Symmetric Encryption keys are stored are further
encrypted using RSA 2048 bit encryption
In order for anyone to decrypt the data being encrypted in the database, they
@@ -38,6 +38,15 @@ by the Operating System
* The userid and password for the database to copy the encrypted data itself,
or an unsecured copy or export of the database contents
+A major feature of symmetric encryption is that it makes the encryption and decryption
+automatically available when the Rails application is started. This includes all
+rake tasks and the Rails console. In this way data can be encrypted or decrypted as
+part of any rake task.
+
+From a security perspective it is important then to properly secure the system so that
+no hacker can switch to and run as the rails user and thereby gain access to the
+encryption and decryption capabilities
+
## Features
* Encryption of passwords in configuration files
View
4 Rakefile
@@ -2,6 +2,7 @@ lib = File.expand_path('../lib/', __FILE__)
$:.unshift lib unless $:.include?(lib)
require 'rubygems'
+require 'rubygems/package'
require 'rake/clean'
require 'rake/testtask'
require 'date'
@@ -20,9 +21,10 @@ task :gem do |t|
s.summary = "Symmetric Encryption for Ruby, and Ruby on Rails"
s.description = "SymmetricEncryption supports encrypting ActiveRecord data, Mongoid data, passwords in configuration files, encrypting and decrypting of large files through streaming"
s.files = FileList["./**/*"].exclude(/.gem$/, /.log$/,/^nbproject/).map{|f| f.sub(/^\.\//, '')}
+ s.license = "Apache License V2.0"
s.has_rdoc = true
end
- Gem::Builder.new(gemspec).build
+ Gem::Package.build gemspec
end
desc "Run Test Suite"
View
80 lib/symmetric_encryption/cipher.rb
@@ -11,7 +11,7 @@ class Cipher
attr_accessor :encoding
# Available encodings
- ENCODINGS = [:none, :base64, :base64strict]
+ ENCODINGS = [:none, :base64, :base64strict, :base16]
# Generate a new Symmetric Key pair
#
@@ -51,7 +51,9 @@ def self.random_key_pair(cipher = 'aes-256-cbc', generate_iv = true)
# It is not the default for backward compatibility
# :base64
# Return as a base64 encoded string
- # :binary
+ # :base16
+ # Return as a Hex encoded string
+ # :none
# Return as raw binary data string. Note: String can contain embedded nulls
# Default: :base64
# Recommended: :base64strict
@@ -69,45 +71,53 @@ def initialize(parms={})
raise("Invalid Encoding: #{@encoding}") unless ENCODINGS.include?(@encoding)
end
- # AES Symmetric Encryption of supplied string
+ # Encryption of supplied string
# The String is encoded to UTF-8 prior to encryption
#
- # Returns result as a Base64 encoded string
+ # Returns result as an encoded string if encode is true
# Returns nil if the supplied str is nil
# Returns "" if it is a string and it is empty
if defined?(Encoding)
- def encrypt(str)
+ def encrypt(str, encode = true)
return if str.nil?
buf = str.to_s.encode(SymmetricEncryption::UTF8_ENCODING)
return str if buf.empty?
- crypt(:encrypt, buf)
+ encrypted = crypt(:encrypt, buf)
+ encode ? self.encode(encrypted) : encrypted
end
else
- def encrypt(str)
+ def encrypt(str, encode = true)
return if str.nil?
buf = str.to_s
return str if buf.empty?
- crypt(:encrypt, buf)
+ encrypted = crypt(:encrypt, buf)
+ encode ? self.encode(encrypted) : encrypted
end
end
- # AES Symmetric Decryption of supplied string
- # The encoding of the supplied string is ignored since it must be binary data
+ # Decryption of supplied string
+ #
+ # Decodes string first if decode is true
+ #
# Returns a UTF-8 encoded, decrypted string
# Returns nil if the supplied str is nil
# Returns "" if it is a string and it is empty
if defined?(Encoding)
- def decrypt(str)
- return if str.nil?
- buf = str.to_s.force_encoding(SymmetricEncryption::BINARY_ENCODING)
- return str if buf.empty?
+ def decrypt(str, decode = true)
+ decoded = self.decode(str) if decode
+ return unless decoded
+
+ buf = decoded.to_s.force_encoding(SymmetricEncryption::BINARY_ENCODING)
+ return decoded if buf.empty?
crypt(:decrypt, buf).force_encoding(SymmetricEncryption::UTF8_ENCODING)
end
else
- def decrypt(str)
- return if str.nil?
- buf = str.to_s
- return str if buf.empty?
+ def decrypt(str, decode = true)
+ decoded = self.decode(str) if decode
+ return unless decoded
+
+ buf = decoded.to_s
+ return decoded if buf.empty?
crypt(:decrypt, buf)
end
end
@@ -123,6 +133,40 @@ def block_size
::OpenSSL::Cipher::Cipher.new(@cipher).block_size
end
+ # Encode the supplied string using the encoding in this cipher instance
+ # Returns nil if the supplied string is nil
+ # Note: No encryption or decryption is performed
+ def encode(binary_string)
+ return unless binary_string
+
+ # Now encode data based on encoding setting
+ case encoding
+ when :base64
+ ::Base64.encode64(binary_string)
+ when :base64strict
+ ::Base64.encode64(binary_string).gsub(/\n/, '')
+ when :base16
+ binary_string.to_s.unpack('H*').first
+ else
+ binary_string
+ end
+ end
+
+ # Decode the supplied string using the encoding in this cipher instance
+ # Note: No encryption or decryption is performed
+ def decode(encoded_string)
+ return unless encoded_string
+
+ case encoding
+ when :base64, :base64strict
+ ::Base64.decode64(encoded_string)
+ when :base16
+ [encoded_string].pack('H*')
+ else
+ encoded_string
+ end
+ end
+
protected
# Only for use by Symmetric::EncryptedStream
View
22 lib/symmetric_encryption/symmetric_encryption.rb
@@ -66,12 +66,7 @@ def self.secondary_ciphers
def self.decrypt(str)
raise "Call SymmetricEncryption.load! or SymmetricEncryption.cipher= prior to encrypting or decrypting data" unless @@cipher
- # Decode data first based on encoding setting
- case @@cipher.encoding
- when :base64, :base64strict
- str = ::Base64.decode64(str) if str
- end
-
+ # Decode and then decrypt supplied string
begin
@@cipher.decrypt(str)
rescue OpenSSL::Cipher::CipherError => exc
@@ -92,19 +87,8 @@ def self.decrypt(str)
def self.encrypt(str)
raise "Call SymmetricEncryption.load! or SymmetricEncryption.cipher= prior to encrypting or decrypting data" unless @@cipher
- # Encrypt data as a binary string
- if result = @@cipher.encrypt(str)
- # Now encode data based on encoding setting
- case @@cipher.encoding
- when :base64
- # Base 64 Encoding of binary data
- ::Base64.encode64(result)
- when :base64strict
- ::Base64.encode64(result).gsub(/\n/, '')
- else
- result
- end
- end
+ # Encrypt and then encode the supplied string
+ @@cipher.encrypt(str)
end
# Invokes decrypt
View
2  lib/symmetric_encryption/version.rb
@@ -1,4 +1,4 @@
# encoding: utf-8
module SymmetricEncryption #:nodoc
- VERSION = "0.9.1"
+ VERSION = "1.0.0"
end
View
22 test/cipher_test.rb
@@ -13,16 +13,18 @@ class CipherTest < Test::Unit::TestCase
should "allow setting the cipher" do
cipher = SymmetricEncryption::Cipher.new(
- :cipher => 'aes-128-cbc',
- :key => '1234567890ABCDEF1234567890ABCDEF',
- :iv => '1234567890ABCDEF'
+ :cipher => 'aes-128-cbc',
+ :key => '1234567890ABCDEF1234567890ABCDEF',
+ :iv => '1234567890ABCDEF',
+ :encoding => :none
)
assert_equal 'aes-128-cbc', cipher.cipher
end
should "not require an iv" do
cipher = SymmetricEncryption::Cipher.new(
- :key => '1234567890ABCDEF1234567890ABCDEF'
+ :key => '1234567890ABCDEF1234567890ABCDEF',
+ :encoding => :none
)
result = "\302<\351\227oj\372\3331\310\260V\001\v'\346"
# Note: This test fails on JRuby 1.7 RC1 since it's OpenSSL
@@ -35,9 +37,10 @@ class CipherTest < Test::Unit::TestCase
should "throw an exception on bad data" do
cipher = SymmetricEncryption::Cipher.new(
- :cipher => 'aes-128-cbc',
- :key => '1234567890ABCDEF1234567890ABCDEF',
- :iv => '1234567890ABCDEF'
+ :cipher => 'aes-128-cbc',
+ :key => '1234567890ABCDEF1234567890ABCDEF',
+ :iv => '1234567890ABCDEF',
+ :encoding => :none
)
assert_raise OpenSSL::Cipher::CipherError do
cipher.decrypt('bad data')
@@ -49,8 +52,9 @@ class CipherTest < Test::Unit::TestCase
context 'with configuration' do
setup do
@cipher = SymmetricEncryption::Cipher.new(
- :key => '1234567890ABCDEF1234567890ABCDEF',
- :iv => '1234567890ABCDEF'
+ :key => '1234567890ABCDEF1234567890ABCDEF',
+ :iv => '1234567890ABCDEF',
+ :encoding => :none
)
@social_security_number = "987654321"
View
2  test/reader_test.rb
@@ -22,7 +22,7 @@ class ReaderTest < Test::Unit::TestCase
]
@data_str = @data.inject('') {|sum,str| sum << str}
@data_len = @data_str.length
- @data_encrypted = SymmetricEncryption.cipher.encrypt(@data_str)
+ @data_encrypted = SymmetricEncryption.cipher.encrypt(@data_str, false)
end
should "decrypt from string stream as a single read" do
View
98 test/symmetric_encryption_test.rb
@@ -12,9 +12,9 @@
# Unit Test for SymmetricEncryption
#
class SymmetricEncryptionTest < Test::Unit::TestCase
- context 'initialized' do
+ context 'SymmetricEncryption' do
- context 'SymmetricEncryption configuration' do
+ context 'configuration' do
setup do
@config = SymmetricEncryption.send(:read_config, File.join(File.dirname(__FILE__), 'config', 'symmetric-encryption.yml'), 'test')
end
@@ -24,61 +24,51 @@ class SymmetricEncryptionTest < Test::Unit::TestCase
end
end
- context 'Base64 encoding tests' do
- setup do
- @social_security_number = "987654321"
- @social_security_number_encrypted = "S+8X1NRrqdfEIQyFHVPuVA==\n"
- @social_security_number_encrypted_with_secondary_1 = "D1UCu38pqJ3jc0GvwJHiow==\n"
- @encoding = SymmetricEncryption.cipher.encoding
- SymmetricEncryption.cipher.encoding = :base64
- end
-
- teardown do
- SymmetricEncryption.cipher.encoding = @encoding
- end
-
- should "encrypt simple string" do
- assert_equal @social_security_number_encrypted, SymmetricEncryption.encrypt(@social_security_number)
- end
-
- should "decrypt string" do
- assert_equal @social_security_number, SymmetricEncryption.decrypt(@social_security_number_encrypted)
- end
-
- should "determine if string is encrypted" do
- assert_equal true, SymmetricEncryption.encrypted?(@social_security_number_encrypted)
- assert_equal false, SymmetricEncryption.encrypted?(@social_security_number)
- end
-
- should "decrypt with secondary key when first one fails" do
- assert_equal @social_security_number, SymmetricEncryption.decrypt(@social_security_number_encrypted)
+ SymmetricEncryption::Cipher::ENCODINGS.each do |encoding|
+ context "encoding: #{encoding}" do
+ setup do
+ @social_security_number = "987654321"
+ @social_security_number_encrypted =
+ case encoding
+ when :base64
+ "S+8X1NRrqdfEIQyFHVPuVA==\n"
+ when :base64strict
+ "S+8X1NRrqdfEIQyFHVPuVA=="
+ when :base16
+ "4bef17d4d46ba9d7c4210c851d53ee54"
+ when :none
+ "K\xEF\x17\xD4\xD4k\xA9\xD7\xC4!\f\x85\x1DS\xEET".force_encoding(Encoding.find("binary"))
+ else
+ raise "Add test for encoding: #{encoding}"
+ end
+ @social_security_number_encrypted_with_secondary_1 = "D1UCu38pqJ3jc0GvwJHiow==\n"
+ @encoding = SymmetricEncryption.cipher.encoding
+ SymmetricEncryption.cipher.encoding = encoding
+ end
+
+ teardown do
+ SymmetricEncryption.cipher.encoding = @encoding
+ end
+
+ should "encrypt simple string" do
+ assert_equal @social_security_number_encrypted, SymmetricEncryption.encrypt(@social_security_number)
+ end
+
+ should "decrypt string" do
+ assert_equal @social_security_number, SymmetricEncryption.decrypt(@social_security_number_encrypted)
+ end
+
+ should "determine if string is encrypted" do
+ assert_equal true, SymmetricEncryption.encrypted?(@social_security_number_encrypted)
+ assert_equal false, SymmetricEncryption.encrypted?(@social_security_number)
+ end
+
+ should "decrypt with secondary key when first one fails" do
+ assert_equal @social_security_number, SymmetricEncryption.decrypt(@social_security_number_encrypted_with_secondary_1)
+ end
end
end
- context 'Base64Strict tests' do
- setup do
- @social_security_number = "987654321"
- @social_security_number_encrypted = "S+8X1NRrqdfEIQyFHVPuVA=="
- @social_security_number_encrypted_with_secondary_1 = "D1UCu38pqJ3jc0GvwJHiow=="
- end
-
- should "encrypt simple string" do
- assert_equal @social_security_number_encrypted, SymmetricEncryption.encrypt(@social_security_number)
- end
-
- should "decrypt string" do
- assert_equal @social_security_number, SymmetricEncryption.decrypt(@social_security_number_encrypted)
- end
-
- should "determine if string is encrypted" do
- assert_equal true, SymmetricEncryption.encrypted?(@social_security_number_encrypted)
- assert_equal false, SymmetricEncryption.encrypted?(@social_security_number)
- end
-
- should "decrypt with secondary key when first one fails" do
- assert_equal @social_security_number, SymmetricEncryption.decrypt(@social_security_number_encrypted)
- end
- end
end
end
View
2  test/writer_test.rb
@@ -22,7 +22,7 @@ class EncryptionWriterTest < Test::Unit::TestCase
]
@data_str = @data.inject('') {|sum,str| sum << str}
@data_len = @data_str.length
- @data_encrypted = SymmetricEncryption.cipher.encrypt(@data_str)
+ @data_encrypted = SymmetricEncryption.cipher.encrypt(@data_str, false)
@filename = '._test'
end

0 comments on commit a48b67f

Please sign in to comment.
Something went wrong with that request. Please try again.