Permalink
Browse files

move ripple contrib over

  • Loading branch information...
1 parent f0e3ccd commit b1486ed711f065616780e1d57937b904c4b3bf0b @randysecrist randysecrist committed Mar 3, 2013
@@ -1,19 +0,0 @@
-module Ripple
- module Encryption
- class Activation
-
- def initialize(path)
- config = Ripple::Encryption::Config.new path
- # short-circuit encryption via the config file if desired
- if !Riak::Serializers['application/x-json-encrypted'] && (config.to_h['encryption'] != false)
- Riak::Serializers['application/x-json-encrypted'] = Ripple::Encryption::Serializer.new config
- end
- @@is_activated = true
- end
-
- def self.activated?
- @@is_activated
- end
- end
- end
-end
@@ -30,8 +30,8 @@ def activate
def validate_path(path)
if !File.exists? path
- raise ConfigError, <<MISSINGFILE
-The config file [usually "config/encryption.yml"] is missing or incorrect. You will
+ raise Ripple::Encryption::ConfigError, <<MISSINGFILE
+The file "config/encryption.yml" is missing or incorrect. You will
need to create this file and populate it with a valid cipher,
initialization vector and secret key. An example is provided in
"config/encryption.yml.example".
@@ -6,8 +6,7 @@ class EncryptedJsonDocumentError < StandardError; end
# Interprets a encapsulation in JSON for encrypted Ripple documents.
#
# Example usage:
- # Ripple::Encryption::JsonDocument.new(@config, @document).encrypt
- # Ripple::Encryption::EncryptedJsonDocument.new(@config, @document).decrypt
+ # Ripple::Encryption::JsonDocument.new(@document).encrypt
class EncryptedJsonDocument
# Creates an object that is prepared to decrypt its contents.
# @param [String] data json string that was stored in Riak
@@ -0,0 +1,117 @@
+module Ripple
+ module Encryption
+ # Implements the {Riak::Serializer} API for the purpose of
+ # encrypting/decrypting Ripple documents.
+ #
+ # Example usage:
+ # ::Riak::Serializers['application/x-json-encrypted'] = EncryptedSerializer.new(OpenSSL::Cipher.new("AES-256"))
+ # class MyDocument
+ # include Ripple::Document
+ # include Riak::Encryption
+ # end
+ #
+ # @see Encryption
+ class EncryptedSerializer
+ # @return [String] The Content-Type of the internal format,
+ # generally "application/json"
+ attr_accessor :content_type
+
+ # @return [OpenSSL::Cipher, OpenSSL::PKey::*] the cipher used to encrypt the object
+ attr_accessor :cipher
+
+ # Cipher-specific settings
+ # @see OpenSSL::Cipher
+ attr_accessor :key, :iv, :key_length, :padding
+
+ # Serialization Options
+ # @return [true, false] Is the encrypted text also base64 encoded?
+ attr_accessor :base64
+
+ # Creates a serializer using the provided cipher and internal
+ # content type. Be sure to set the {#key}, {#iv}, {#key_length},
+ # {#padding} as appropriate for the cipher before attempting
+ # (de-)serialization.
+ # @param [OpenSSL::Cipher] cipher the desired
+ # encryption/decryption algorithm
+ # @param [String] content_type the Content-Type of the
+ # unencrypted contents
+ def initialize(cipher, content_type='application/json', path)
+ @cipher, @content_type = cipher, content_type
+ @config = Ripple::Encryption::Config.new(path)
+ end
+
+ # Serializes and encrypts the Ruby object using the assigned
+ # cipher and Content-Type.
+ # @param [Object] object the Ruby object to serialize/encrypt
+ # @return [String] the serialized, encrypted form of the object
+ def dump(object)
+ JsonDocument.new(@config, object).encrypt
+ end
+
+ # Decrypts and deserializes the blob using the assigned cipher
+ # and Content-Type.
+ # @param [String] blob the original content from Riak
+ # @return [Object] the decrypted and deserialized object
+ def load(object)
+ # try the v1 way first
+ begin
+ internal = decrypt(object)
+ return ::Riak::Serializers.deserialize('application/json', internal)
+ # if that doesn't work, try the v2 way
+ rescue OpenSSL::Cipher::CipherError, MultiJson::DecodeError
+ return EncryptedJsonDocument.new(@config, object).decrypt
+ end
+ end
+
+ private
+
+ # generates a new iv each call unless a static (less secure)
+ # iv is used.
+ def encrypt(object)
+ old_version = '0.0.1'
+ result = ''
+ if cipher.respond_to?(:iv=) and @iv == nil
+ iv = OpenSSL::Random.random_bytes(cipher.iv_len)
+ cipher.iv = iv
+ result << old_version << iv
+ end
+
+ if cipher.respond_to?(:public_encrypt)
+ result << cipher.public_encrypt(object)
+ else
+ cipher_setup :encrypt
+ result << cipher.update(object) << cipher.final
+ cipher.reset
+ end
+ return result
+ end
+
+ def decrypt(cipher_text)
+ old_version = '0.0.1'
+
+ if cipher.respond_to?(:iv=) and @iv == nil
+ version = cipher_text.slice(0, old_version.length)
+ cipher.iv = cipher_text.slice(old_version.length, cipher.iv_len)
+ cipher_text = cipher_text.slice(old_version.length + cipher.iv_len, cipher_text.length)
+ end
+
+ if cipher.respond_to?(:private_decrypt)
+ cipher.private_decrypt(cipher_text)
+ else
+ cipher_setup :decrypt
+ result = cipher.update(cipher_text) << cipher.final
+ cipher.reset
+ result
+ end
+ end
+
+ def cipher_setup(mode)
+ cipher.send mode
+ cipher.key = key if key
+ cipher.iv = iv if iv
+ cipher.key_length = key_length if key_length
+ cipher.padding = padding if padding
+ end
+ end
+ end
+end
@@ -1,20 +1,78 @@
+require 'openssl'
+require 'ripple'
+
module Ripple
- # When mixed into a Ripple::Document class, this will encrypt the
- # serialized form before it is stored in Riak. You must register
- # a serializer that will perform the encryption.
- # @see Serializer
+
module Encryption
- # Overrides the internal method to set the content-type to be
- # encrypted.
- def robject
- @robject ||= Riak::RObject.new(self.class.bucket, key).tap do |obj|
- obj.content_type = 'application/x-json-encrypted'
+
+ # When mixed into a Ripple::Document class, this will encrypt the
+ # serialized form before it is stored in Riak. You must register
+ # a serializer that will perform the encryption.
+ # @see EncryptedSerializer
+ module Encryption
+ extend ActiveSupport::Concern
+
+ @@is_activated = false
+
+ included do
+ @@encrypted_content_type = self.encrypted_content_type = 'application/x-json-encrypted'
end
+
+ module ClassMethods
+ # @return [String] the content type to be used to indicate the
+ # proper encryption scheme. Defaults to 'application/x-json-encrypted'
+ attr_accessor :encrypted_content_type
+ end
+
+ # Overrides the internal method to set the content-type to be
+ # encrypted.
+ def update_robject
+ super
+ if @@is_activated
+ robject.content_type = @@encrypted_content_type
+ end
+ end
+
+ def self.activate(path)
+ encryptor = nil
+ unless Riak::Serializers['application/x-json-encrypted']
+ begin
+ config = YAML.load_file(path)[ENV['RACK_ENV']]
+ encryptor = Ripple::Encryption::EncryptedSerializer.new(OpenSSL::Cipher.new(config['cipher']), 'application/x-json-encrypted', path)
+ rescue Exception => e
+ handle_invalid_encryption_config(e.message, e.backtrace)
+ end
+ encryptor.key = config['key'] if config['key']
+ encryptor.iv = config['iv'] if config['iv']
+ Riak::Serializers['application/x-json-encrypted'] = encryptor
+ @@is_activated = true
+ end
+ encryptor
+ end
+
+ def self.activated
+ @@is_activated
+ end
+
end
- def update_robject
- robject.key = key if robject.key != key
- robject.content_type ||= 'application/x-json-encrypted'
- robject.data = attributes_for_persistence
- end
end
end
+
+def handle_invalid_encryption_config(msg, trace)
+ puts <<eos
+
+ The file "config/encryption.yml" is missing or incorrect. You will
+ need to create this file and populate it with a valid cipher,
+ initialization vector and secret key.
+
+ An example is provided in "config/encryption.yml.example".
+eos
+
+ puts "Error Message: " + msg
+ puts "Error Trace:"
+ trace.each do |line|
+ puts line
+ end
+
+ exit 1
+end
@@ -1,7 +1,7 @@
module Ripple
module Encryption
# Generic error class for Encryptor
- class EncryptorError < StandardError; end
+ class EncryptorConfigError < StandardError; end
# Implements a simple object that can either encrypt or decrypt arbitrary data.
#
@@ -13,7 +13,10 @@ class Encryptor
# Creates an Encryptor that is prepared to encrypt/decrypt a blob.
# @param [Hash] config the key/cipher/iv needed to initialize OpenSSL
def initialize(config)
- validate(config)
+ # ensure that we have the required configuration keys
+ %w(cipher key iv).each do |option|
+ raise(Ripple::Encryption::EncryptorConfigError, "Missing configuration option '#{option}'.") if config[option].nil?
+ end
@config = config
@cipher = OpenSSL::Cipher.new(@config['cipher'])
end
@@ -40,15 +43,6 @@ def initialize_cipher_for(mode)
@cipher.key = @config['key']
@cipher.iv = @config['iv']
end
-
- # Creates an Encryptor that is prepared to encrypt/decrypt a blob.
- # @param [Hash] config the key/cipher/iv needed to initialize OpenSSL
- def validate(config)
- # ensure that we have the required configuration keys
- %w(cipher key iv).each do |option|
- raise(Ripple::Encryption::EncryptorError, "Missing configuration option '#{option}'.") if config[option].nil?
- end
- end
end
end
end
@@ -1,37 +0,0 @@
-module Ripple
- module Encryption
- # Implements the {Riak::Serializer} API for the purpose of
- # encrypting/decrypting Ripple documents.
- #
- # Example usage:
- # ::Riak::Serializers['application/x-json-encrypted'] = Ripple::Encryption::Serializer.new(OpenSSL::Cipher.new("AES-256"))
- # class MyDocument
- # include Ripple::Document
- # include Riak::Encryption
- # end
- #
- # @see Encryption
- class Serializer
- # @param [Ripple::Encryption::Config]
- def initialize(config)
- @config = config
- end
-
- # Serializes and encrypts the Ruby object using the assigned
- # cipher and Content-Type.
- # @param [Object] object the Ruby object to serialize/encrypt
- # @return [String] the serialized, encrypted form of the object
- def dump(object)
- JsonDocument.new(@config, object).encrypt
- end
-
- # Decrypts and deserializes the blob using the assigned cipher
- # and Content-Type.
- # @param [String] blob the original content from Riak
- # @return [Object] the decrypted and deserialized object
- def load(object)
- EncryptedJsonDocument.new(@config, object).decrypt
- end
- end
- end
-end
@@ -1 +1 @@
-{"version":"0.1.0","iv":"ABYLnUHWE/fIwE2gKYC6hg==\n","data":"KYtsnoDZ85AMR/eZAVBtEXe88gB/UNagMpl4oV7FLxUgtqw5BvPCbLChrmdg\nsRQas2VZ8/FkIx5CiMeJYoi9Ag==\n"}
+{"version":"0.0.3","iv":"ABYLnUHWE/fIwE2gKYC6hg==\n","data":"KYtsnoDZ85AMR/eZAVBtEXe88gB/UNagMpl4oV7FLxUgtqw5BvPCbLChrmdg\nsRQas2VZ8/FkIx5CiMeJYoi9Ag==\n"}
@@ -0,0 +1 @@
+{"message":"this is unencrypted data", "_type":"TestDocument"}
@@ -0,0 +1 @@
+0� �d�>^��|���`f���p�#ʼl_�*�
@@ -0,0 +1 @@
+{"version":"0.0.2","iv":"ABYLnUHWE/fIwE2gKYC6hg==\n","data":"MK0LuGThPhde4t0NfKSbhAvPjTuFmykhWVGNxPG++40=\n"}
View
@@ -17,24 +17,24 @@
client = Riak::Client.new(:nodes => [riak_config])
bucket = client.bucket("#{riak_config[:namespace].to_s}test")
object = bucket.get_or_new("test")
-rescue RuntimeError => e
- raise e
+rescue RuntimeError
+ raise RuntimeError, "Could not connect to the Riak test node."
end
-
-# activate the library
-Ripple::Encryption::Activation.new ENV['ENCRYPTION']
-
+# define test Ripple Documents
+Ripple::Encryption::Encryption.activate ENV['ENCRYPTION']
class TestDocument
include Ripple::Document
- include Ripple::Encryption
+ include Ripple::Encryption::Encryption
property :message, String
def self.bucket_name
"#{Ripple.config[:namespace]}#{super}"
end
end
-# load Riak fixtures the raw way
+TestDocument.bucket.get_index('$bucket', '_').each {|k| TestDocument.bucket.delete(k)}
+
+# load Riak fixtures
FileList[File.expand_path(File.join('..','fixtures','*'),__FILE__)].each do |f|
if Dir.exists? f
fixture_type = File.basename(f)
@@ -43,15 +43,11 @@ def self.bucket_name
rescue NameError
raise NameError, "Is a Ripple Document of type '#{fixture_type.classify}' defined for that fixture file?"
end
- # unencrypted jsons
- FileList[File.join(f,'*.unencrypted.riak')].each do |r|
- key = File.basename(r,'.unencrypted.riak')
- `curl -s -H 'content-type: application/json' -XPUT http://#{Ripple.config[:host]}:#{Ripple.config[:http_port]}/buckets/#{Ripple.config[:namespace]}#{fixture_type.pluralize}/keys/#{key} --data-binary @#{r}`
- end
- # encrypted jsons
- FileList[File.join(f,'*.encrypted.riak')].each do |r|
- key = File.basename(r,'.encrypted.riak')
- `curl -s -H 'content-type: application/x-json-encrypted' -XPUT http://#{Ripple.config[:host]}:#{Ripple.config[:http_port]}/buckets/#{Ripple.config[:namespace]}#{fixture_type.pluralize}/keys/#{key} --data-binary @#{r}`
+ FileList[File.join(f,'*.riak')].each do |r|
+ key = File.basename(r,'.riak')
+ content_type = (key == 'v0_doc' ? 'application/json' : 'application/x-json-encrypted')
+ `curl -s -H 'content-type: #{content_type}' -XPUT http://#{Ripple.config[:host]}:#{Ripple.config[:http_port]}/buckets/#{Ripple.config[:namespace]}#{fixture_type.pluralize}/keys/#{key} --data-binary @#{r}`
end
end
end
+
Oops, something went wrong.

0 comments on commit b1486ed

Please sign in to comment.