claude 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4f93a597f1c7aa21cff1a1a6ab4e80d4cef5e3b1
4
+ data.tar.gz: d8175164c88064ab63d2568a627a6d645bac5f43
5
+ SHA512:
6
+ metadata.gz: 5ca58757778303242d21495ba62f6c6f49832c3db192b08cc2ee03849b921437207f980fb6a1817912fe1ab945da13a7706b13718ddb49b12c35131d0910575d
7
+ data.tar.gz: 16f4e73089ce52ce20b2b29b4f78e7f4ee4b20969d91fe55e37e82d8ca3da0832bc2100e842b7d677a72fe2837b8da10eb1da65807a87fe7b27fb64d2d6e0946
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'sqlite3'
6
+ gem 'byebug'
7
+
8
+ gem "bundler", "~> 1.9"
9
+ gem "rake", "~> 10.0"
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Genadi Samokovarov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,67 @@
1
+ # Claude
2
+
3
+ Claude transparently encrypts and decrypts sensitive Active Record attributes.
4
+ Nothing magical or fancy, just a simple `OpenSSL::Cipher` wrapper.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'claude'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ ```bash
17
+ bundle
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ Say, you have to store sensitive information in your application database. You
23
+ can use `OpenSSL::Cipher` to manually encrypt the value with a secret on write,
24
+ and decrypt it with the same secret when used throughout the code base. This
25
+ may work for a single sensitive attribute, but it gets out of hand if you gotta
26
+ do it regularly.
27
+
28
+ Claude wraps `OpenSSL::Cipher` and lets you transparently encrypt and decrypt
29
+ the your application sensitive attributes, so you don't have to do it manually
30
+ all the time.
31
+
32
+ Claude exposes the `encrypt` and `attr_encrypt` class macros to setup an
33
+ attribute encryption.
34
+
35
+ ```ruby
36
+ class Card < ActiveRecord::Base
37
+ encrypt :pin
38
+ end
39
+
40
+ >> card = Card.new(pin: "1234")
41
+ => #<Card id: nil, encrypted_pin: "FNYLjh2q9tWcYH5lG0zkPQ==\n", encrypted_pin_iv: "e4E99V82noXFLHhCfcWwBw==\n">
42
+ >> card.pin
43
+ => "1234"
44
+ ```
45
+
46
+ The encrypted `pin` dynamic attribute is backed by two database columns.
47
+ `encrypted_pin` and `encrypted_pin_iv`. You have to create them by a migration,
48
+ before using Claude.
49
+
50
+ The default secret used to encrypt an attribute with is `config.secret_token`
51
+ for Rails 3.2 and `secrets.secret_key_base` for Rails 4. You can use per
52
+ attribute secret or a different global one by setting `config.claude.secret` to
53
+ your likings. Changing it will invalidate all the current encryptions, so
54
+ beware of that.
55
+
56
+ Read the [`ActiveRecord::Base.encrypt`](https://github.com/gsamokovarov/claude/blob/75d72de1b3fb0f784133b057914d87bdf23a2d4a/lib/claude/extensions.rb#L6-L41)
57
+ and [`ActiveRecord::Base.attr_encrypt`](https://github.com/gsamokovarov/claude/blob/75d72de1b3fb0f784133b057914d87bdf23a2d4a/lib/claude/extensions.rb#L71-L80)
58
+ API documentation for more information.
59
+
60
+ # Why Claude?
61
+
62
+ The library is named after [Claude Elwood Shannon]. He is considered to be the
63
+ father of the modern mathematical cryptography.
64
+
65
+ Why should you use it? Because it makes encryption simple. :-)
66
+
67
+ [Claude Elwood Shannon]: https://en.wikipedia.org/wiki/Claude_Shannon
@@ -0,0 +1,33 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Claude'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
18
+
19
+ load 'rails/tasks/engine.rake'
20
+ load 'rails/tasks/statistics.rake'
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'lib'
28
+ t.libs << 'test'
29
+ t.pattern = 'test/**/*_test.rb'
30
+ t.verbose = false
31
+ end
32
+
33
+ task default: :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "claude"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'claude/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "claude"
8
+ spec.version = Claude::VERSION
9
+ spec.authors = ["Genadi Samokovarov"]
10
+ spec.email = ["gsamokovarov@gmail.com"]
11
+
12
+ spec.summary = "Claude transparently encrypts and decrypts sensitive Active Record attributes."
13
+ spec.description = "Claude transparently encrypts and decrypts sensitive Active Record attributes."
14
+ spec.homepage = "https://github.com/gsamokovarov/claude"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ rails_version = ">= 3.2"
23
+
24
+ spec.add_dependency "railties", rails_version
25
+ spec.add_dependency "activerecord", rails_version
26
+
27
+ spec.add_development_dependency "rails", rails_version
28
+ end
@@ -0,0 +1,18 @@
1
+ require 'claude/version'
2
+ require 'claude/coder'
3
+ require 'claude/railtie'
4
+
5
+ module Claude
6
+ # Used to encrypt and decrypt attributes with.
7
+ mattr_accessor :secret
8
+
9
+ # The OpenSSL::Cipher used for the encryption.
10
+ mattr_accessor :ssl_cipher
11
+ self.ssl_cipher = 'AES-256-CBC'
12
+
13
+ # The coder is used to encode decode the keys and ivs to DB string column
14
+ # friendly characters. Base64 encoding by default.
15
+ mattr_accessor :coder
16
+ self.coder = Coder
17
+ end
18
+
@@ -0,0 +1,57 @@
1
+ require 'claude/openssl_cipher_builder'
2
+ require 'claude'
3
+
4
+ module Claude
5
+ class Cipher
6
+ class UnsupportedModeError < StandardError
7
+ def initialize(mode)
8
+ super("Unsupported mode #{mode}")
9
+ end
10
+ end
11
+
12
+ class << self
13
+ def for_encryption(key, iv)
14
+ builder = OpenSSLCipherBuilder.new(key, iv)
15
+ mode = :encrypt
16
+
17
+ new(builder.build(mode), mode)
18
+ end
19
+
20
+ def for_decryption(key, iv)
21
+ builder = OpenSSLCipherBuilder.new(key, iv)
22
+ mode = :decrypt
23
+
24
+ new(builder.build(mode), mode)
25
+ end
26
+ end
27
+
28
+ def initialize(cipher, mode, coder = Claude.coder)
29
+ @cipher = cipher
30
+ @mode = mode
31
+ @coder = coder
32
+ end
33
+
34
+ def encrypt(value)
35
+ crypt(value)
36
+ end
37
+
38
+ def decrypt(value)
39
+ crypt(value)
40
+ end
41
+
42
+ private
43
+
44
+ attr_reader :cipher, :mode, :coder
45
+
46
+ def crypt(value)
47
+ case mode
48
+ when :encrypt
49
+ coder.encode(cipher.update(value) + cipher.final)
50
+ when :decrypt
51
+ cipher.update(coder.decode(value)) + cipher.final
52
+ else
53
+ raise UnsupportedModeError, mode
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,18 @@
1
+ module Claude
2
+ # The coder interface is implemented by any object responding to #encode and
3
+ # #decode.
4
+ #
5
+ # The current implementation defaults to Base64 encoding and decoding as the
6
+ # characters are friendly for RDBMS string types.
7
+ module Coder
8
+ extend self
9
+
10
+ def encode(value)
11
+ Base64.encode64(value)
12
+ end
13
+
14
+ def decode(value)
15
+ Base64.decode64(value)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,89 @@
1
+ require 'claude/random_iv'
2
+ require 'claude/cipher'
3
+
4
+ module Claude
5
+ module Extensions
6
+ # Encrypt the value stored at attr when persisting the value in the
7
+ # underlying database.
8
+ #
9
+ # To encrypt an attr we need to have two extra columns named:
10
+ #
11
+ # * encrypted_attr - Used internally to store the encrypted value.
12
+ # * encrypted_attr_iv - Used internally to encrypt the value above.
13
+ #
14
+ # Note that attr is dynamic here. If you wanna encrypt the attribute pin:
15
+ #
16
+ # class Card < ActiveRecord::Base
17
+ # encrypt :pin
18
+ # end
19
+ #
20
+ # The expected columns will be named encrypted_pin and encrypted_pin_iv.
21
+ #
22
+ # Encrypt will generate two new instance methods: attr and attr=. Those
23
+ # are dynamic and compute its value based of encrypted_attr and
24
+ # decrypted_attr.
25
+ #
26
+ # attr decrypt the value from the encrypted_attr.
27
+ # attr= encrypts the value that is passed to it and saves it to encrypted_attr.
28
+ #
29
+ # Most of the time, you won't need to call encrypted_attr and
30
+ # encrypted_attr_iv manually. Consider them an implementation detail.
31
+ #
32
+ # You can pass the following options:
33
+ #
34
+ # * :attr - The name of the internal encrypted column.
35
+ # Defaults to "encrypted_#{attr}".)
36
+ #
37
+ # * :attr_iv - The name of the internal initialization vector column.
38
+ # Defaults to "encrypted_#{attr}_iv".
39
+ #
40
+ # * :secret - The secret used to encrypt the attribute.
41
+ # Defaults to config.secret_roken or config.secrets.secret_token.
42
+ def encrypt(attr, options = {})
43
+ attr_secret = options.fetch(:secret, Claude.secret).inspect
44
+ attr_internal = options.fetch(:attr, "encrypted_#{attr}")
45
+ attr_iv = options.fetch(:attr_iv, "encrypted_#{attr}_iv")
46
+ attr_raw = "#{attr_internal}_before_type_cast"
47
+
48
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
49
+ def #{attr}
50
+ return unless #{attr_raw}
51
+
52
+ cipher = Cipher.for_decryption(#{attr_secret}, #{attr_iv})
53
+ cipher.decrypt(#{attr_raw})
54
+ end
55
+
56
+ def #{attr}=(value)
57
+ if value
58
+ iv = RandomIV.generate
59
+ cipher = Cipher.for_encryption(#{attr_secret}, iv)
60
+
61
+ self.#{attr_iv} = iv
62
+ self.#{attr_internal} = cipher.encrypt(value)
63
+ else
64
+ self.#{attr_iv} = nil
65
+ self.#{attr_internal} = nil
66
+ end
67
+ end
68
+ RUBY
69
+ end
70
+
71
+ # An alias to encrypt that lets you encrypt multiple attributes at once.
72
+ #
73
+ # If only one attribute is given, it acts exactly like encrypt does.
74
+ #
75
+ # When multiple attributes are given, you can still give options. However,
76
+ # the :attr and :attr_iv options will be the same for all the attributes.
77
+ #
78
+ # class Card < ActiveRecord::Base
79
+ # attr_encrypt :pin, :pan
80
+ # end
81
+ def attr_encrypt(*attrs)
82
+ options = attrs.extract_options!
83
+
84
+ attrs.each do |attr|
85
+ encrypt attr, options
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,33 @@
1
+ require 'openssl'
2
+ require 'digest'
3
+ require 'claude'
4
+
5
+ module Claude
6
+ class OpenSSLCipherBuilder
7
+ def initialize(key = nil, iv = nil, ssl_cipher = Claude.ssl_cipher)
8
+ @key = key && key.to_s
9
+ @iv = hash_if_too_short(iv)
10
+ @ssl_cipher = ssl_cipher
11
+ end
12
+
13
+ def build(mode)
14
+ OpenSSL::Cipher.new(ssl_cipher).tap do |cipher|
15
+ cipher.public_send(mode)
16
+
17
+ cipher.key = key if key
18
+ cipher.iv = iv if iv
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :key, :iv, :ssl_cipher
25
+
26
+ # When passing a custom IV column, it can be too short for OpenSSL to work
27
+ # with. Make sure it isn't.
28
+ def hash_if_too_short(iv)
29
+ return if iv.nil?
30
+ iv.size < 16 ? Digest::SHA1.hexdigest(iv.to_s) : iv.to_s
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,23 @@
1
+ require 'claude/extensions'
2
+
3
+ module Claude
4
+ class Railtie < ::Rails::Railtie
5
+ config.claude = Claude
6
+
7
+ initializer 'claude.active_record' do
8
+ ActiveRecord::Base.send(:extend, Extensions)
9
+ end
10
+
11
+ initializer 'claude.default_secret' do |app|
12
+ # Rails 3 keeps the default secret token Rails.application.config.
13
+ if config.respond_to?(:secret_token)
14
+ config.claude.secret ||= config.secret_token
15
+ end
16
+
17
+ # Rails 4 keeps them in Rails.application.secrets.
18
+ if app.respond_to?(:secrets)
19
+ config.claude.secret ||= app.secrets.secret_token || app.secrets.secret_key_base
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,32 @@
1
+ require 'claude'
2
+ require 'claude/openssl_cipher_builder'
3
+
4
+ module Claude
5
+ class RandomIV
6
+ # A shortcut for RandomIV.new(Claude.coder).generate.
7
+ def self.generate
8
+ new.generate
9
+ end
10
+
11
+ def initialize(coder = Claude.coder)
12
+ @coder = coder
13
+ end
14
+
15
+ # Generates a secure random Initialization Vector that can be used for
16
+ # OpenSSL encryption and decryption.
17
+ #
18
+ # It is encoded by a coder, so the IV consists of DB friendly characters.
19
+ def generate
20
+ coder.encode(cipher.random_iv)
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :coder
26
+
27
+ def cipher
28
+ builder = OpenSSLCipherBuilder.new
29
+ builder.build(:encrypt)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module Claude
2
+ VERSION = '0.1.0'
3
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: claude
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Genadi Samokovarov
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-08-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: railties
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '3.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '3.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '3.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '3.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '3.2'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '3.2'
55
+ description: Claude transparently encrypts and decrypts sensitive Active Record attributes.
56
+ email:
57
+ - gsamokovarov@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".travis.yml"
64
+ - CODE_OF_CONDUCT.md
65
+ - Gemfile
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - bin/console
70
+ - bin/setup
71
+ - claude.gemspec
72
+ - lib/claude.rb
73
+ - lib/claude/cipher.rb
74
+ - lib/claude/coder.rb
75
+ - lib/claude/extensions.rb
76
+ - lib/claude/openssl_cipher_builder.rb
77
+ - lib/claude/railtie.rb
78
+ - lib/claude/random_iv.rb
79
+ - lib/claude/version.rb
80
+ homepage: https://github.com/gsamokovarov/claude
81
+ licenses:
82
+ - MIT
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 2.4.5
101
+ signing_key:
102
+ specification_version: 4
103
+ summary: Claude transparently encrypts and decrypts sensitive Active Record attributes.
104
+ test_files: []