precious_cargo 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .idea
3
+ .bundle
4
+ Gemfile.lock
5
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in precious_cargo.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2012 Theodore Mills <twmills@twmills.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,52 @@
1
+ # PreciousCargo
2
+
3
+ Secure large (or small) amounts of data for transfer over web services
4
+
5
+ ### What is it?
6
+
7
+ PreciousCargo encapsulates a specific best practice when encrypting large (or small) amounts of data, normally to transmit over the wire via a web service. The strategy is not really complex, but it wasn't readily apparent when I was looking for a solution. Therefore I wrote this gem to make it convenient to not only apply this strategy, but to also make this best practice more easily discovered (hopefully).
8
+
9
+ ### The Problem Case
10
+
11
+ PK encryption is typically the preferred encryption method, but it suffers from the limitation that the size of data being encrypted cannot exceed the key size. To get around this approach, yet still take advantage of the excellent encryption method, the data is first encrypted with AES encryption using a secret passphrase. The secret passphrase is then encrypted using the public key from an RSA keychain and both the encrypted secret and encrypted data are sent together as part of the same payload to the client.
12
+
13
+ The client decrypts the encrypted secret with their private key from the RSA key pair, then uses the decrypted secret to decrypt the AES encrypted data.
14
+
15
+ The PreciousCargo module, therefore, provides convenience methods to encapsulate these multi-step encryption and decryption processes. Though it is possible to use a "shared secret" to encrypt the data, for extra security the encrypt method will generate a random secret passphrase if one is not explicitly provided.
16
+
17
+ ## Installation
18
+
19
+ gem install precious_cargo
20
+
21
+ ## Examples
22
+
23
+ ```ruby
24
+ @data = "This is my precious cargo."
25
+ @keypair = OpenSSL::PKey::RSA.new(2048)
26
+
27
+ # With an auto-generated secret (the preferred method):
28
+ @encrypted_payload = PreciousCargo.encrypt!(@data, :public_key => @keypair.public_key)
29
+ #=> { :encrypted_data => [Base64 encoded string of encrypted data], :encrypted_secret => [Base64 encoded string of the encrypted secret] }
30
+
31
+ PreciousCargo.decrypt!([Base64 encoded string of encrypted data], :keypair => @keypair, :encrypted_secret => [Base64 encoded string of the encrypted secret])
32
+ #=> "This is my precious cargo."
33
+
34
+ # With a supplied secret:
35
+ @encrypted_payload = PreciousCargo.encrypt!(@data, :secret => 'p@assw0rD', :public_key => @keypair.public_key)
36
+ #=> {:encrypted_data => [Base64 encoded string of encrypted data], :encrypted_secret => [Base64 encoded string of the encrypted secret]}
37
+
38
+ PreciousCargo.decrypt!([Base64 encoded string of encrypted data], :keypair => @keypair, :encrypted_secret => [Base64 encoded string of the encrypted secret])
39
+ #=> "This is my precious cargo."
40
+ ```
41
+
42
+ ## Dependencies
43
+
44
+ * Ruby compiled with OpenSSL support.
45
+ * The [gibberish gem](https://github.com/mdp/gibberish/).
46
+
47
+ ## How to run the tests
48
+
49
+ git clone https://github.com/twmills/precious_cargo.git
50
+ cd precious_cargo
51
+ bundle install
52
+ rspec
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,83 @@
1
+ require "gibberish"
2
+ require "precious_cargo/version"
3
+ require 'precious_cargo/secret'
4
+ require 'precious_cargo/data'
5
+
6
+ # Public: PreciousCargo encapsulates a specific best practice when encrypting large (or small) amounts of data, normally
7
+ # to transmit over the wire via a web service.
8
+ #
9
+ # PK encryption is typically the preferred encryption method, but it suffers from the limitation that the size of data
10
+ # being encrypted cannot exceed the key size. To get around this approach, yet still take advantage of the excellent
11
+ # encryption method, the data is first encrypted with AES encryption using a secret passphrase. The secret passphrase is
12
+ # then encrypted using the public key from an RSA keychain and both the encrypted secret and encrypted data are sent
13
+ # together as part of the same payload to the client.
14
+ #
15
+ # The client decrypts the encrypted secret with their private key from the RSA key pair, then uses the decrypted
16
+ # secret to decrypt the AES encrypted data.
17
+ #
18
+ # The PreciousCargo module, therefore, provides convenience methods to encapsulate these multi-step encryption and
19
+ # decryption processes. Though it is possible to use a "shared secret" to encrypt the data, for extra security the
20
+ # encrypt method will generate a random secret passphrase if one is not explicitly provided.
21
+ #
22
+ # Examples:
23
+ #
24
+ # @data = "This is my precious cargo."
25
+ # @keypair = OpenSSL::PKey::RSA.new(2048)
26
+ #
27
+ # With an auto-generated secret (the preferred method):
28
+ # @encrypted_payload = PreciousCargo.encrypt!(@data, :public_key => @keypair.public_key)
29
+ # #=> { :encrypted_data => [Base64 encoded string of encrypted data], :encrypted_secret => [Base64 encoded string of the encrypted secret] }
30
+ #
31
+ # PreciousCargo.decrypt!([Base64 encoded string of encrypted data], :keypair => @keypair, :encrypted_secret => [Base64 encoded string of the encrypted secret])
32
+ # #=> "This is my precious cargo."
33
+ #
34
+ # With a supplied secret:
35
+ # @encrypted_payload = PreciousCargo.encrypt!(@data, :secret => 'p@assw0rD', :public_key => @keypair.public_key)
36
+ # #=> {:encrypted_data => [Base64 encoded string of encrypted data], :encrypted_secret => [Base64 encoded string of the encrypted secret]}
37
+ #
38
+ # PreciousCargo.decrypt!([Base64 encoded string of encrypted data], :keypair => @keypair, :encrypted_secret => [Base64 encoded string of the encrypted secret])
39
+ # #=> "This is my precious cargo."
40
+ #
41
+ module PreciousCargo
42
+
43
+ class << self
44
+
45
+ # Public: Encrypt the supplied data using ASES encryption with a secret pass phrase. The secret will also be
46
+ # encrypted using an RSA public key object. If a secret is not supplied, then a random secret is generated. It is
47
+ # generally better to randomly generate a secret every time you encrypt your precious cargo.
48
+ #
49
+ # data - The data to be encrypted.
50
+ # options - Hash of values used to encrypt the data.
51
+ # :secret - Optional. A secret string. If a secret string is not passed in, then one is randomly
52
+ # generated.
53
+ # :public_key - The RSA public key object used to encrypt the secret.
54
+ #
55
+ # Returns a hash containing the Base64 encoded encrypted data and Base64 encoded encrypted secret.
56
+ def encrypt!(data, options = {})
57
+ options[:secret] = PreciousCargo::Secret.random unless options.has_key?(:secret)
58
+ encrypted_data = PreciousCargo::Data.encrypt!(data, options)
59
+ encrypted_secret = PreciousCargo::Secret.encrypt!(options)
60
+ { :encrypted_secret => encrypted_secret, :encrypted_data => encrypted_data }
61
+ end
62
+
63
+ # Public: Decrypt the supplied Base64 encoded encrypted data using the supplied Base64 encoded encrypted secret. The
64
+ # secret is first decrypted using the supplied RSA key pair object and is then used to decrypt the AES encrypted
65
+ # data. Optionally if the secret is known and shared, it can be supplied in lieu of the encrypted secret (though it
66
+ # rather defeats the purpose of this gem and may be removed at a later date).
67
+ #
68
+ # options - Hash of values used to decrypt the secret.
69
+ # :encrypted_secret - A Base64 encoded, RSA encrypted secret string.
70
+ # :keypair - The RSA key pair object used to decrypt the secret.
71
+ # :secret - Optional. A plaintext shared secret.
72
+ #
73
+ # Returns the decrypted secret.
74
+ def decrypt!(data, options = {})
75
+ unless options.has_key?(:secret)
76
+ options[:secret] = PreciousCargo::Secret.decrypt!(options)
77
+ end
78
+
79
+ PreciousCargo::Data.decrypt!(data, options)
80
+ end
81
+ end
82
+
83
+ end
@@ -0,0 +1,34 @@
1
+ module PreciousCargo
2
+
3
+ # Public: A collection of methods to encrypt and decrypt data using
4
+ # a supplied secret.
5
+ module Data
6
+ class << self
7
+
8
+ # Public: Encrypt the supplied data using a secret string. Currently only supports AES 256 encryption.
9
+ # data - The data to be encrypted.
10
+ # options - Hash of values used to encrypt the secret.
11
+ # :secret - A secret string.
12
+ #
13
+ # Returns the AES encrypted data.
14
+ def encrypt!(data, options = {})
15
+ secret = options[:secret]
16
+ cipher = Gibberish::AES.new(secret)
17
+ cipher.encrypt(data)
18
+ end
19
+
20
+ # Public: Decrypt the supplied data using a secret string. Currently only supports AES 256 encryption.
21
+ # data - The data to be encrypted.
22
+ # options - Hash of values used to encrypt the secret.
23
+ # :secret - A secret string.
24
+ #
25
+ # Returns the AES encrypted data.
26
+ def decrypt!(encrypted_data, options = {})
27
+ secret = options[:secret]
28
+ cipher = Gibberish::AES.new(secret)
29
+ cipher.decrypt(encrypted_data).strip
30
+ end
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,44 @@
1
+ module PreciousCargo
2
+
3
+ # Public: A collection of methods to encrypt and decrypt the secret key used to encrypt the payload's data.
4
+ module Secret
5
+ class << self
6
+
7
+ # Public: Encrypt the supplied secret string using an RSA public key object. If a secret is not supplied, then a
8
+ # random secret is generated. It is generally better to randomly generate a secret every time you encrypt your
9
+ # precious cargo.
10
+ #
11
+ # options - Hash of values used to encrypt the secret.
12
+ # :secret - A secret string. If a secret string is not passed in, then one is randomly generated.
13
+ # :public_key - The RSA public key object used to encrypt the secret.
14
+ #
15
+ # Returns the RSA encrypted secret as a Base64 encoded string.
16
+ def encrypt!(options = {})
17
+ secret = options[:secret]
18
+ public_key = options[:public_key]
19
+ Base64.encode64(public_key.public_encrypt(secret))
20
+ end
21
+
22
+ # Public: Decrypt the supplied Base64 encoded secret string using an RSA key pair object.
23
+ #
24
+ # options - Hash of values used to decrypt the secret.
25
+ # :encrypted_secret - A Base64 encoded, RSA encrypted secret string.
26
+ # :keypair - The RSA key pair object used to decrypt the secret.
27
+ #
28
+ # Returns the decrypted secret.
29
+ def decrypt!(options = {})
30
+ encrypted_secret = Base64.decode64(options[:encrypted_secret])
31
+ keypair = options[:keypair]
32
+ keypair.private_decrypt(encrypted_secret)
33
+ end
34
+
35
+ # Public: Generates a random 32 character string.
36
+ #
37
+ # Returns random 32 character string.
38
+ def random
39
+ Array.new(32){rand(36).to_s(36)}.join
40
+ end
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,3 @@
1
+ module PreciousCargo
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "precious_cargo/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "precious_cargo"
7
+ s.version = PreciousCargo::VERSION
8
+ s.authors = ["Theo Mills"]
9
+ s.email = ["twmills@twmills.com"]
10
+ s.homepage = "https://github.com/twmills/precious_cargo"
11
+ s.summary = %q{Secure large (or small) amounts of data for transfer over web services.}
12
+ s.description = %q{PreciousCargo encapsulates a specific best practice when encrypting large (or small) amounts of data, normally to transmit over the wire via a web service. The strategy is not really complex, but it wasn't readily apparent when I was looking for a solution. Therefore I wrote this gem to make it convenient to not only apply this strategy, but to also make this best practice more easily discovered (hopefully).}
13
+
14
+ s.rubyforge_project = "precious_cargo"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {spec,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "rspec"
22
+ s.add_development_dependency "faker"
23
+ s.add_runtime_dependency "gibberish"
24
+ end
@@ -0,0 +1,23 @@
1
+ require "spec_helper"
2
+
3
+ describe PreciousCargo, "#encrypt!" do
4
+
5
+ before do
6
+ @keypair = OpenSSL::PKey::RSA.new(2048)
7
+ @data = Faker::Lorem.paragraphs.join(" ")
8
+ @secret = "password"
9
+ end
10
+
11
+ it "encrypts and decrypts data with an auto-generated secret" do
12
+ payload = subject.encrypt!(@data, :public_key => @keypair.public_key)
13
+ unencrypted_data = subject.decrypt!(payload.delete(:encrypted_data), payload.merge(:keypair => @keypair))
14
+ unencrypted_data.should == @data
15
+ end
16
+
17
+ it "encrypts and decrypts data with supplied secret" do
18
+ payload = subject.encrypt!(@data, :public_key => @keypair.public_key)
19
+ unencrypted_data = subject.decrypt!(payload.delete(:encrypted_data), payload.merge(:keypair => @keypair))
20
+ unencrypted_data.should == @data
21
+ end
22
+
23
+ end
@@ -0,0 +1,26 @@
1
+ require "spec_helper"
2
+
3
+ describe PreciousCargo::Data do
4
+
5
+ before do
6
+ @keypair = OpenSSL::PKey::RSA.new(2048)
7
+ @data = Faker::Lorem.paragraphs.join(" ")
8
+ @secret = "password"
9
+ end
10
+
11
+ context "#encrypt!" do
12
+ it "encrypts data using the supplied secret" do
13
+ encrypted_data = subject.encrypt!(@data, :secret => @secret)
14
+ from_openssl = `echo "#{encrypted_data}" | openssl enc -d -aes-256-cbc -a -k password`
15
+ from_openssl.should == @data
16
+ end
17
+ end
18
+
19
+ context "#decrypt!" do
20
+ it "decrypts a secret using the supplied secret" do
21
+ from_openssl = `echo #{@data} | openssl enc -aes-256-cbc -a -k password`
22
+ subject.decrypt!(from_openssl, :secret => @secret).should == @data
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,40 @@
1
+ require "spec_helper"
2
+
3
+ describe PreciousCargo::Secret do
4
+
5
+ before do
6
+ @keypair = OpenSSL::PKey::RSA.new(2048)
7
+ @public_key = @keypair.public_key
8
+ @secret = "password"
9
+ end
10
+
11
+ context "#encrypt!" do
12
+ it "encrypts a secret using the supplied public key" do
13
+ options = {}
14
+ options[:secret] = @secret
15
+ options[:public_key] = @public_key
16
+ encrypted_secret = subject.encrypt!(options)
17
+ @keypair.private_decrypt(Base64.decode64(encrypted_secret)).should == @secret
18
+ end
19
+ end
20
+
21
+ context "#decrypt!" do
22
+ it "decrypts a secret using the supplied keypair" do
23
+ options = {}
24
+ options[:encrypted_secret] = Base64.encode64(@public_key.public_encrypt(@secret))
25
+ options[:keypair] = @keypair
26
+ subject.decrypt!(options).should == @secret
27
+ end
28
+ end
29
+
30
+ context "#random" do
31
+ it "generates a random string" do
32
+ subject.random.should_not == subject.random
33
+ end
34
+
35
+ it "generates a random string 32 characters long" do
36
+ subject.random.length.should == 32
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'faker'
3
+ require 'bundler/setup'
4
+ require 'precious_cargo'
5
+
6
+ # This file was generated by the `rspec --init` command. Conventionally, all
7
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
8
+ # Require this file using `require "spec_helper"` to ensure that it is only
9
+ # loaded once.
10
+ #
11
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
12
+ RSpec.configure do |config|
13
+ config.treat_symbols_as_metadata_keys_with_true_values = true
14
+ config.run_all_when_everything_filtered = true
15
+ config.filter_run :focus
16
+
17
+ # Run specs in random order to surface order dependencies. If you find an
18
+ # order dependency and want to debug it, you can fix the order by providing
19
+ # the seed, which is printed after each run.
20
+ # --seed 1234
21
+ config.order = 'random'
22
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: precious_cargo
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Theo Mills
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-07-22 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :development
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: faker
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: gibberish
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ type: :runtime
61
+ version_requirements: *id003
62
+ description: PreciousCargo encapsulates a specific best practice when encrypting large (or small) amounts of data, normally to transmit over the wire via a web service. The strategy is not really complex, but it wasn't readily apparent when I was looking for a solution. Therefore I wrote this gem to make it convenient to not only apply this strategy, but to also make this best practice more easily discovered (hopefully).
63
+ email:
64
+ - twmills@twmills.com
65
+ executables: []
66
+
67
+ extensions: []
68
+
69
+ extra_rdoc_files: []
70
+
71
+ files:
72
+ - .gitignore
73
+ - .rspec
74
+ - Gemfile
75
+ - LICENSE
76
+ - README.md
77
+ - Rakefile
78
+ - lib/precious_cargo.rb
79
+ - lib/precious_cargo/data.rb
80
+ - lib/precious_cargo/secret.rb
81
+ - lib/precious_cargo/version.rb
82
+ - precious_cargo.gemspec
83
+ - spec/core_spec.rb
84
+ - spec/data_spec.rb
85
+ - spec/secret_spec.rb
86
+ - spec/spec_helper.rb
87
+ homepage: https://github.com/twmills/precious_cargo
88
+ licenses: []
89
+
90
+ post_install_message:
91
+ rdoc_options: []
92
+
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ hash: 3
101
+ segments:
102
+ - 0
103
+ version: "0"
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ hash: 3
110
+ segments:
111
+ - 0
112
+ version: "0"
113
+ requirements: []
114
+
115
+ rubyforge_project: precious_cargo
116
+ rubygems_version: 1.8.15
117
+ signing_key:
118
+ specification_version: 3
119
+ summary: Secure large (or small) amounts of data for transfer over web services.
120
+ test_files:
121
+ - spec/core_spec.rb
122
+ - spec/data_spec.rb
123
+ - spec/secret_spec.rb
124
+ - spec/spec_helper.rb