yasst 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MTk4MmY5N2RmODI2NDBmZGY4NzkzNGY2NzAyZTQ5M2U5ZTA4ODU4ZQ==
5
+ data.tar.gz: !binary |-
6
+ N2E0YTU5M2Y5ZjliYzcwNzY0Y2M4MDI1OGE0MGJmNTU0YWQwMTk0NA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ M2EwNDA1MDI5YjQ5N2VkNzhiMDUxZDVlYjU1ZmEyZjgwOWQ3ZGVmZDM2NDJm
10
+ NzlhOWI3NDQzNTRlNTY5ZjVmNmI0Njg0ZDVhYWYzZjhmYjNlZWVjY2VmYmM2
11
+ Zjc5ZDNiMDJlZjViYzI5OTU0NjJkYjM4MDc0NzhkZWY2MTZhZWU=
12
+ data.tar.gz: !binary |-
13
+ YWQ2YjBkNWEwYTZhZTMzZjFjOTU4NzEwNDM3MTZiOWEwNGQ1MmEyNDMzZmFm
14
+ MTA5NTI0NWZlNmE5NjQwZTk3YTExMDQyN2Q4NGU5MzEyNzdiNGI3MTVlYTVm
15
+ OWQ3NmZkYTE1YzUyOGQ4ZmUxZjI1ZDc2YTY5OTEyYWMxOTM4ZWE=
data/.gitignore ADDED
@@ -0,0 +1,28 @@
1
+ # Default ignores for vim editor
2
+ .*.sw[a-z]
3
+ *.un~
4
+ Session.vim
5
+ .netrwhist
6
+ # Default ignores for emacs editor
7
+ *~
8
+ \#*\#
9
+ /.emacs.desktop
10
+ /.emacs.desktop.lock
11
+ .elc
12
+ auto-save-list
13
+ tramp
14
+ .\#*
15
+
16
+ # docs
17
+ /doc
18
+
19
+ # as this is a gem, we don't check Gemfile.lock into version control
20
+ Gemfile.lock
21
+
22
+ # test harness
23
+ pkg/*
24
+ vendor/bundle
25
+ vendor/cache
26
+ .bundle
27
+
28
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,14 @@
1
+ ---
2
+ Style/RegexpLiteral:
3
+ Exclude:
4
+ - 'Guardfile'
5
+ # TODO - simplify Yasst::Profiles::OpenSSL
6
+ Metrics/PerceivedComplexity:
7
+ Exclude:
8
+ - 'lib/yasst/profiles/openssl.rb'
9
+ Metrics/CyclomaticComplexity:
10
+ Exclude:
11
+ - 'lib/yasst/profiles/openssl.rb'
12
+ Metrics/AbcSize:
13
+ Exclude:
14
+ - 'lib/yasst/profiles/openssl.rb'
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ - 2.1.0
5
+ deploy:
6
+ provider: rubygems
7
+ gem: yasst
8
+ on:
9
+ tags: true
10
+ api_key:
11
+ secure: l2mduN8h2tjuBSr0G/XdAD5DNbiV87vZwQoLoH2PX1KQKdgaSzlJ4Mus9pSQzhq5JsLFRNtNdMvRp1AvxEajAf4Oy+H3tBSoeQEz9/wDfZvkAje1BlZyN6fXcIzj1HkDHoA8Da6y8o5xnA3Q8f+bVDloUJyjadT5Y8tOBDBo1QY4s/qPXoqUIsGLgJtBg29MqKGj/lVrvxf/+GRs62VlJny0jRhWJK+OzyxyKHB17wIs5wg+blayd8studtpJBvFFNMSFlwF1YvTCqsCyU4d9DHaR5GS0ZOev1y63E+EkkgJiXpBh+yDXVzpSjVcm4jygJdo7n6wdW+uv7yUP8rm9ULZG6TZmDIU8I1plVjD7aOMgRBvPckomHZ8maj+1FAz8Axl/bDQhM/TT0uT2ckS6h9sXMv1uSIm/hVyiTmETg7JyETHzvd/VrH4QssGdNo60MdhQJ/Cl0US/mt2xiMlAQB4qZCakWv3A8aCCg6dswouYgewvXkrSG3mUpPpmBAjBGNLkG9IRs1lUISqBOp452HuBQ5nyR9l8ATRJvHI8hdD7tPuGfQILG0k6c9ePpFrVD7odB9KkVTd7Zd8iMh0np4oc/XeZdYVelRyeGLS7vh7QdND3w0dSr5svgfzX45lD5kwYzST6JqpaJcBZu76ctMg37AV3hlZA1Ut6etZWa8=
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in yasst.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,22 @@
1
+ # Note: The cmd option is now required due to the increasing number of ways
2
+ # rspec may be run, below are examples of the most common uses.
3
+ # * bundler: 'bundle exec rspec'
4
+ # * bundler binstubs: 'bin/rspec'
5
+ # * spring: 'bin/rspec' (This will use spring if running and you have
6
+ # installed the spring binstubs per the docs)
7
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
8
+ # * 'just' rspec: 'rspec'
9
+
10
+ guard :rspec, cmd: 'rspec' do
11
+ watch(%r{^spec/.+_spec\.rb$})
12
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
13
+ watch('spec/spec_helper.rb') { 'spec' }
14
+ end
15
+
16
+ guard :rubocop do
17
+ watch(%r{^spec/.+_spec\.rb$})
18
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
19
+ watch(%r{[^\/]+\.rb$})
20
+ watch(%r{.+\.gemspec$})
21
+ watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
22
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Richard Clark
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.
data/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # Yasst
2
+
3
+ [![Build Status](https://travis-ci.org/rdark/yasst.svg?branch=master)](https://travis-ci.org/rdark/yasst)
4
+
5
+ Yet Another Secret Stashing Toolkit.
6
+
7
+ ## Overview
8
+
9
+ This project gives convenient methods for encrypting and decrypting `String`
10
+ and `File` objects (using the [decorator pattern][decorator_pattern], rather
11
+ than [monkey-patching][monkey_patching]) via the `YasstString` and `YasstFile`
12
+ classes respectively.
13
+
14
+ Encryption and decryption is handled by a `Yasst::Provider`; at the moment only
15
+ OpenSSL is implemented, though support for an OpenPGP provider is planned.
16
+
17
+ Each provider has a configurable `Yasst::Profile`, with sensible defaults set.
18
+ At the time of writing, the defaults for `Yasst::Profiles::OpenSSL` are:
19
+
20
+ * AES-256 cipher in CBC mode
21
+ * key generation via PBKDF2 HMAC-SHA1 with 50,000 iterations
22
+
23
+ Additionally, the OpenSSL provider will ensure that there is:
24
+
25
+ * random salt generated for every encrypt action
26
+ * random IV generated for every encrypt action
27
+ * new key generated for every encrypt (and decrypt) action
28
+ * encrypted string output is Base64 (web-safe) encoded
29
+
30
+ [decorator_pattern]: https://github.com/nslocum/design-patterns-in-ruby#decorator
31
+ [monkey_patching]: http://demonastery.org/2012/11/monkey-patching-in-ruby/
32
+
33
+ ## Usage
34
+
35
+ ### YasstString
36
+
37
+ provider = Yasst::Provider::OpenSSL.new(passphrase: 'a really strong passphrase')
38
+ provider.profile.algorithm
39
+ => "AES-256-CBC"
40
+ provider.profile.key_gen_method
41
+ => :pbkdf2
42
+ provider.profile.pbkdf2_iterations
43
+ => 50000
44
+ secrets = YasstString.new('some really secret data')
45
+ secrets.encrypted?
46
+ => false
47
+ secrets.encrypt(provider)
48
+ => "Ubvxrj7-E7QCNqiof00RwxTka5V2debHX6gdIPdAmdRvsgB2YpjGD4IU5EYYN6uFk5iKo76k6mvK4tTIXbcBlhFmnN4mptpG
49
+ secrets.encrypted?
50
+ => true
51
+
52
+ ### YasstFile
53
+
54
+ NotYetImplemented
55
+
56
+ ## Installation
57
+
58
+ Add this line to your application's Gemfile:
59
+
60
+ ```ruby
61
+ gem 'yasst'
62
+ ```
63
+
64
+ And then execute:
65
+
66
+ $ bundle
67
+
68
+ Or install it yourself as:
69
+
70
+ $ gem install yasst
71
+
72
+ ## Development
73
+
74
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
75
+
76
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
77
+
78
+ ## Contributing
79
+
80
+ Bug reports and pull requests are welcome on GitHub at https://github.com/rdark/yasst. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
81
+
82
+
83
+ ## License
84
+
85
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
86
+
87
+ ## Disclaimer
88
+
89
+ * This security provided by this project has not been independently verified
90
+ * Only AES ciphers are currently supported/tested, and primarily focus has so
91
+ far been on CBC mode.
92
+
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'rubocop/rake_task'
4
+
5
+ namespace :test do
6
+ # default unit tests
7
+ desc 'Run all unit tests in default spec suite'
8
+ RSpec::Core::RakeTask.new(:unit) do |task|
9
+ task.pattern = 'spec/unit/**/*_spec.rb'
10
+ end
11
+ # add rubocop tasks to test namespace
12
+ RuboCop::RakeTask.new
13
+ # run all non-integration/default tests
14
+ desc 'Run all default test suites'
15
+ task default: [:unit, :rubocop]
16
+ end
17
+
18
+ # default task is to run all the default test suites
19
+ task default: ['test:default']
data/TODO.md ADDED
@@ -0,0 +1,14 @@
1
+ # TODO
2
+
3
+ ## General Functionality
4
+
5
+ * YasstFile Implementation
6
+ * support for larger-than-memory files
7
+ * support for authenticated cipher modes
8
+ * support for OpenPGP crypto provider
9
+ * support for logging
10
+
11
+ ## Profiles::OpenSSL
12
+
13
+ * make banned/recommended/supported list of algorithms
14
+
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'yasst'
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
data/bin/setup ADDED
@@ -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,17 @@
1
+ module Yasst
2
+ # Error classes for Yasst
3
+ class Error < StandardError
4
+ attr_reader :error
5
+
6
+ def initialize(error = nil)
7
+ @error = error
8
+ end
9
+
10
+ class InvalidCryptoProvider < self; end
11
+ class InvalidCryptoProfile < self; end
12
+ class InvalidCryptoAlgorithm < self; end
13
+ class InvalidPassPhrase < self; end
14
+ class AlreadyEncrypted < self; end
15
+ class AlreadyDecrypted < self; end
16
+ end
17
+ end
@@ -0,0 +1,29 @@
1
+ require 'openssl'
2
+
3
+ module Yasst
4
+ module Primatives
5
+ module OpenSSL
6
+ ##
7
+ # Methods for returning various bits of information
8
+ module Metadata
9
+ ##
10
+ # List available ciphers
11
+ def self.list_ciphers
12
+ ::OpenSSL::Cipher.ciphers
13
+ end
14
+
15
+ # Return the key length for a given cipher algorithm
16
+ def self.key_len_for(alg)
17
+ cipher = ::OpenSSL::Cipher.new(alg)
18
+ cipher.key_len
19
+ end
20
+
21
+ # Return the key length for a given cipher algorithm
22
+ def self.iv_len_for(alg)
23
+ cipher = ::OpenSSL::Cipher.new(alg)
24
+ cipher.iv_len
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,146 @@
1
+ require 'base64'
2
+ require 'openssl'
3
+ require 'digest/sha2'
4
+
5
+ module Yasst
6
+ module Primatives
7
+ # OpenSSL primatives mixin
8
+ module OpenSSL
9
+ private
10
+
11
+ # Generate a random salt
12
+ # ===== Parameters
13
+ # - +salt_bytes+
14
+ # Size of the salt to be generated in bytes
15
+ def p_salt(salt_bytes)
16
+ ::OpenSSL::Random.random_bytes(salt_bytes)
17
+ end
18
+
19
+ # Return a cipher object
20
+ #
21
+ # ===== Parameters
22
+ # - +algorithm+
23
+ # The algorithm to be used for the cipher (E.G 'AES-256-CBC')
24
+ def p_cipher(algorithm)
25
+ ::OpenSSL::Cipher.new(algorithm)
26
+ end
27
+
28
+ # Return a PBKDF2 HMAC SHA1 key
29
+ #
30
+ # ===== Parameters
31
+ # - +passphrase+
32
+ # The passphrase to be used for generation of the key
33
+ # - +salt+
34
+ # A random salt to be used for generation of the key
35
+ # - +iterations+
36
+ # How many rounds of SHA1 hashing to use (recommend minimum 20k)
37
+ # - +key_length+
38
+ # How long the key should be (should match the requirements of your
39
+ # cipher algorithm)
40
+ def p_pbkdf2_key(passphrase, salt, iterations, key_length)
41
+ ::OpenSSL::PKCS5.pbkdf2_hmac_sha1(
42
+ passphrase,
43
+ salt,
44
+ iterations,
45
+ key_length
46
+ )
47
+ end
48
+
49
+ # Encrypt a string
50
+ #
51
+ # ===== Parameters
52
+ # - +string+
53
+ # Data to be encrypted, presented as a string
54
+ # - +key+
55
+ # Encryption key to be used
56
+ # - +algorithm+
57
+ # Algorithm to use for the cipher
58
+ #
59
+ # ===== Returns
60
+ # - +iv+
61
+ # Initialisation vector used for the encryption
62
+ # - +ciphertext+
63
+ # The ciphertext
64
+ def p_encrypt_string(string, key, algorithm)
65
+ cipher = p_cipher(algorithm)
66
+ cipher.encrypt
67
+ cipher.key = key
68
+ # random_iv sets + returns simultaneously, grab output for prepending
69
+ iv = cipher.random_iv
70
+ ciphertext = cipher.update(Base64.urlsafe_encode64(string))
71
+ # not required for streaming ciphers but compatible/recommended anyway
72
+ ciphertext << cipher.final
73
+ [iv, ciphertext]
74
+ end
75
+
76
+ # Decrypt ciphertext string
77
+ #
78
+ # ===== Parameters
79
+ # - +ciphertext+
80
+ # Raw ciphertext as a string
81
+ # - +key+
82
+ # Key that will be used to decrypt the ciphertext
83
+ # - +iv+
84
+ # Initialisation Vector for the ciphertext
85
+ # - +algorithm+
86
+ # Algorithm to use for the cipher
87
+ def p_decrypt_string(ciphertext, key, iv, algorithm)
88
+ cipher = p_cipher(algorithm)
89
+ cipher.key = key
90
+ cipher.iv = iv
91
+ cipher.decrypt
92
+ # decrypt ciphertext
93
+ encoded_plain = cipher.update(ciphertext)
94
+ encoded_plain << cipher.final
95
+ # return the decoded plaintext
96
+ Base64.urlsafe_decode64(encoded_plain)
97
+ end
98
+
99
+ # Pack a string ready for storing
100
+ #
101
+ # ===== Parameters
102
+ # - +iv+
103
+ # The IV used during encryption of the ciphertext
104
+ # - +salt+
105
+ # The salt used for generation of the encryption key
106
+ def p_pack_string(iv, salt, ciphertext)
107
+ output = ciphertext
108
+ output.prepend(iv)
109
+ output.prepend(salt)
110
+ Base64.urlsafe_encode64(output)
111
+ end
112
+
113
+ # Unpack a string ready for decryption
114
+ #
115
+ # ===== Parameters
116
+ # - +string+
117
+ # Base64 encoded string containing the ciphertext with prepended salt
118
+ # and IV
119
+ # - +key_length+
120
+ # The size (in bytes) of the encryption key used to encrypt the data
121
+ # - +iv_length+
122
+ # The size (in bytes) of the IV used to encrypt the data
123
+ # - +salt_bytes+
124
+ # The size (in bytes) of the salt used to generate the encryption key
125
+ #
126
+ # ===== Returns
127
+ # - +salt+
128
+ # The salt that was prepended to the string
129
+ # - +iv+
130
+ # The IV that was prepended to the string
131
+ # - +ciphertext+
132
+ # The ciphertext (which should still be base64 encoded)
133
+ def p_unpack_string(string, key_length, iv_length, salt_bytes)
134
+ # remove base64 wrapping
135
+ string = Base64.urlsafe_decode64(string)
136
+ # pull out prepended salt and build the key using it
137
+ salt = string.byteslice(0..(salt_bytes - 1))
138
+ # pull out prepended IV
139
+ iv = string.byteslice(salt_bytes..(key_length - 1))
140
+ # pull out remaining data, which is the ciphertext
141
+ ciphertext = string.byteslice((salt_bytes + iv_length)..string.bytesize)
142
+ [salt, iv, ciphertext]
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,8 @@
1
+ module Yasst
2
+ ##
3
+ # Class to represent a profile for a provider (essentially configuration for
4
+ # that provider)
5
+ #
6
+ class Profile
7
+ end
8
+ end
@@ -0,0 +1,62 @@
1
+ require 'yasst/primatives/openssl/metadata'
2
+
3
+ module Yasst
4
+ module Profiles
5
+ ##
6
+ # OpenSSL Profile
7
+ class OpenSSL < Yasst::Profile
8
+ attr_reader :algorithm, :key_gen_method, :pbkdf2_iterations,
9
+ :key_len, :iv_len
10
+ attr_accessor :salt_bytes
11
+
12
+ DEFAULT_SALT_BYTES = 8
13
+ DEFAULT_KEY_GEN_METHOD = :pbkdf2
14
+ DEFAULT_PBKDF2_ITERATIONS = 50_000
15
+ DEFAULT_ALGORITHM = 'AES-256-CBC'.freeze
16
+ SUPPORTED_KEY_GEN_METHODS = [:pbkdf2].freeze
17
+
18
+ include Yasst::Primatives::OpenSSL::Metadata
19
+
20
+ def initialize(**args)
21
+ args[:algorithm].nil? && (self.algorithm = DEFAULT_ALGORITHM) ||
22
+ (self.algorithm = args[:algorithm])
23
+ args[:salt_bytes].nil? && (@salt_bytes = DEFAULT_SALT_BYTES) ||
24
+ (self.salt_bytes = args[:salt_bytes])
25
+ args[:key_gen_method].nil? &&
26
+ (self.key_gen_method = DEFAULT_KEY_GEN_METHOD) ||
27
+ (self.key_gen_method = args[:key_gen_method])
28
+ args[:pbkdf2_iterations].nil? &&
29
+ (self.pbkdf2_iterations = DEFAULT_PBKDF2_ITERATIONS) ||
30
+ (self.pbkdf2_iterations = args[:pbkdf2_iterations])
31
+ end
32
+
33
+ # setter method for algorithm
34
+ def algorithm=(alg)
35
+ valgs = Yasst::Primatives::OpenSSL::Metadata.list_ciphers
36
+ unless valgs.include? alg
37
+ raise Yasst::Error::InvalidCryptoAlgorithm,
38
+ "Invalid algorithm. Valid algorithms are #{valgs.join(', ')}"
39
+ end
40
+ @key_len = Yasst::Primatives::OpenSSL::Metadata.key_len_for(alg)
41
+ @iv_len = Yasst::Primatives::OpenSSL::Metadata.iv_len_for(alg)
42
+ @algorithm = alg
43
+ end
44
+
45
+ def key_gen_method=(method)
46
+ if SUPPORTED_KEY_GEN_METHODS.include? method
47
+ @key_gen_method = method
48
+ else
49
+ raise NotImplementedError,
50
+ 'Invalid or not-yet-implemented key generation method. Valid ' \
51
+ "methods are #{SUPPORTED_KEY_GEN_METHODS.join(', ')}"
52
+ end
53
+ end
54
+
55
+ # set number of pbkdf2_iterations. Only set if key_gen_method is :pbkdf2
56
+ def pbkdf2_iterations=(iterations)
57
+ @key_gen_method == :pbkdf2 && (@pbkdf2_iterations = iterations) ||
58
+ (@pbkdf2_iterations = nil)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,8 @@
1
+ require 'yasst/profiles/openssl'
2
+
3
+ module Yasst
4
+ ##
5
+ # parent namespace for profiles
6
+ module Profiles
7
+ end
8
+ end
@@ -0,0 +1,90 @@
1
+ require 'yasst/primatives/openssl'
2
+
3
+ module Yasst
4
+ class Provider
5
+ ##
6
+ # OpenSSL provider
7
+ # ===== Parameters
8
+ # - *profile*
9
+ # ===== Required Parameters
10
+ # - *passphrase*
11
+ class OpenSSL < Yasst::Provider
12
+ include Yasst::Primatives::OpenSSL
13
+
14
+ attr_reader :profile
15
+
16
+ ##
17
+ # initialize hook for superclass
18
+ def post_initialize(**args)
19
+ args[:profile].nil? && (@profile = Yasst::Profiles::OpenSSL.new) ||
20
+ (@profile = args[:profile])
21
+ validate_passphrase(@passphrase)
22
+ end
23
+
24
+ ##
25
+ # Whether or not a passphrase is required for this provider
26
+ def passphrase_required?
27
+ true
28
+ end
29
+
30
+ ##
31
+ # Encrypt a string using a unique salt + key for every encrypt action,
32
+ # and then package it up in a usable base64'd string with iv + salt
33
+ # prepended
34
+ #
35
+ # ===== Parameters
36
+ # - +string+
37
+ # A string to encrypt
38
+ # ===== Returns
39
+ # - String
40
+ def encrypt(string)
41
+ e_salt = salt
42
+ e_key = key(e_salt)
43
+ e_iv, ciphertext = p_encrypt_string(string, e_key, profile.algorithm)
44
+ p_pack_string(e_iv, e_salt, ciphertext)
45
+ end
46
+
47
+ ##
48
+ # De-encode, unpack and decrypt a string
49
+ #
50
+ # ===== Parameters
51
+ # - +string+
52
+ # A base64-encoded string to decrypt. Must be in the same format as the
53
+ # encrypt method produces
54
+ # ===== Returns
55
+ def decrypt(string)
56
+ d_salt, d_iv, ciphertext = p_unpack_string(
57
+ string,
58
+ profile.key_len,
59
+ profile.iv_len,
60
+ profile.salt_bytes
61
+ )
62
+ p_decrypt_string(ciphertext, key(d_salt), d_iv, profile.algorithm)
63
+ end
64
+
65
+ private
66
+
67
+ ##
68
+ # Returns a brand new salt
69
+ def salt
70
+ p_salt(profile.salt_bytes)
71
+ end
72
+
73
+ ##
74
+ # Returns a new key for the configured key gen method.
75
+ # Called with no parameters, a fresh salt will be used
76
+ # ===== Parameters
77
+ # - +salt+
78
+ # Optional salt value to use when generating the key
79
+ def key(salt = nil)
80
+ salt.nil? && salt = new_salt
81
+ # Profile should raise NotImplementedErrror if unsupported key
82
+ # generation method is used
83
+ if profile.key_gen_method == :pbkdf2
84
+ return p_pbkdf2_key(@passphrase, salt,
85
+ profile.pbkdf2_iterations, profile.key_len)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,61 @@
1
+ require 'yasst/provider/openssl'
2
+ module Yasst
3
+ ##
4
+ # Represents a Crypto Provider
5
+ #
6
+ # === Parameters
7
+ # - *passphrase*
8
+ # The passphrase used for the provider instance. This is optional since
9
+ # some providers may not require a passphrase. Where passphrase is given,
10
+ # it must conform to minimum complexity requirements.
11
+ class Provider
12
+ attr_reader :passphrase
13
+
14
+ PASSPHRASE_MIN_LENGTH = 8
15
+
16
+ def initialize(**args)
17
+ self.passphrase = args[:passphrase]
18
+ post_initialize(args)
19
+ end
20
+
21
+ # post initialize hook for subclasses
22
+ def post_initialize(**_args)
23
+ nil
24
+ end
25
+
26
+ # setter method for passphrase
27
+ def passphrase=(pass)
28
+ validate_passphrase(pass) && @passphrase = pass
29
+ end
30
+
31
+ # validates a passphrase and raise on error
32
+ def validate_passphrase(pass = @passphrase)
33
+ unless passphrase_valid?(pass)
34
+ raise Yasst::Error::InvalidPassPhrase,
35
+ 'Passphrase does not meet minimum requirements'
36
+ end
37
+ true
38
+ end
39
+
40
+ # Whether or not a passphrase is required for this provider instance
41
+ # Should be overridden in the provider subclass if it requires a passphrase
42
+ def passphrase_required?
43
+ false
44
+ end
45
+
46
+ # Validate a passphrase
47
+ # ===== Parameters
48
+ # - +pass+
49
+ # String. The passphrase to be validated
50
+ # ===== Returns
51
+ # - true/false if the passphrase is valid or not
52
+ def passphrase_valid?(pass = @passphrase)
53
+ if pass.nil?
54
+ return false if passphrase_required?
55
+ return true
56
+ end
57
+ return false unless pass.length >= PASSPHRASE_MIN_LENGTH
58
+ true
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,3 @@
1
+ module Yasst
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -0,0 +1,7 @@
1
+ ##
2
+ # Decorator pattern for File
3
+ class YasstFile < File
4
+ def initialize(*_args)
5
+ raise NotImplementedError
6
+ end
7
+ end
@@ -0,0 +1,36 @@
1
+ ##
2
+ # Decorator pattern for String
3
+ #
4
+ # TODO: only way to tell an already encrypted new YasstString object that it is
5
+ # encrypted is by telling it so at initialization time. Is there a more elegant
6
+ # way of doing this without imposing overhead?
7
+ class YasstString < String
8
+ def initialize(str = '', encrypted = false)
9
+ super(str)
10
+ @encrypted = encrypted
11
+ end
12
+
13
+ ##
14
+ # Encrypt self using a Yasst::Provider
15
+ def encrypt(provider)
16
+ raise Yasst::Error::AlreadyEncrypted,
17
+ 'File is already encrypted' if encrypted?
18
+ @encrypted = true
19
+ replace(provider.encrypt(to_s))
20
+ end
21
+
22
+ ##
23
+ # Whether or not the encrypt method has been called
24
+ def encrypted?
25
+ @encrypted ||= false
26
+ end
27
+
28
+ ##
29
+ # Decrypt self using a Yasst::Provider
30
+ def decrypt(provider)
31
+ raise Yasst::Error::AlreadyDecrypted,
32
+ 'File is already decrypted' unless encrypted?
33
+ @encrypted = false
34
+ replace(provider.decrypt(to_s))
35
+ end
36
+ end
data/lib/yasst.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'yasst/version'
2
+ require 'yasst/error'
3
+ require 'yasst/profile'
4
+ require 'yasst/profiles'
5
+ require 'yasst/provider'
6
+ require 'yasst/yasst_string'
7
+ require 'yasst/yasst_file'
8
+
9
+ ##
10
+ # Yet Another Secret Stashing Toolset
11
+ module Yasst
12
+ end
data/yasst.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'yasst/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'yasst'
8
+ spec.version = Yasst::VERSION
9
+ spec.authors = ['Richard Clark']
10
+ spec.email = ['richard@fohnet.co.uk']
11
+
12
+ spec.summary = 'Yet Another Secret Stashing Toolkit'
13
+ spec.description = 'Yasst is a toolset for managing encryption and ' \
14
+ 'decryption of secrets'
15
+ spec.homepage = 'https://github.com/rdark/yasst'
16
+ spec.license = 'MIT'
17
+
18
+ spec.require_paths = ['lib']
19
+ spec.files = `git ls-files -z`.split("\x0").reject { |f|
20
+ f.match(%r{^(TODO|test|spec|features)/})
21
+ }
22
+ # ensure gem is built out of versioned files
23
+ spec.executables = `git ls-files -- bin/*`.split("\n").map { |f|
24
+ File.basename(f)
25
+ }
26
+
27
+ spec.add_development_dependency 'bundler', '~> 1'
28
+ spec.add_development_dependency 'rake', '~> 10.0'
29
+ spec.add_development_dependency 'rspec', '~> 3.4.0'
30
+ spec.add_development_dependency 'rubocop', '~> 0.36'
31
+ spec.add_development_dependency 'guard', '~> 2.13.0'
32
+ spec.add_development_dependency 'guard-rspec', '~> 4.6.4'
33
+ spec.add_development_dependency 'guard-rubocop', '~> 1.2.0'
34
+ end
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yasst
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Richard Clark
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-02-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 3.4.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 3.4.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '0.36'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '0.36'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 2.13.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 2.13.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: 4.6.4
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: 4.6.4
97
+ - !ruby/object:Gem::Dependency
98
+ name: guard-rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: 1.2.0
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: 1.2.0
111
+ description: Yasst is a toolset for managing encryption and decryption of secrets
112
+ email:
113
+ - richard@fohnet.co.uk
114
+ executables:
115
+ - console
116
+ - setup
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - .gitignore
121
+ - .rspec
122
+ - .rubocop.yml
123
+ - .travis.yml
124
+ - Gemfile
125
+ - Guardfile
126
+ - LICENSE.txt
127
+ - README.md
128
+ - Rakefile
129
+ - TODO.md
130
+ - bin/console
131
+ - bin/setup
132
+ - lib/yasst.rb
133
+ - lib/yasst/error.rb
134
+ - lib/yasst/primatives/openssl.rb
135
+ - lib/yasst/primatives/openssl/metadata.rb
136
+ - lib/yasst/profile.rb
137
+ - lib/yasst/profiles.rb
138
+ - lib/yasst/profiles/openssl.rb
139
+ - lib/yasst/provider.rb
140
+ - lib/yasst/provider/openssl.rb
141
+ - lib/yasst/version.rb
142
+ - lib/yasst/yasst_file.rb
143
+ - lib/yasst/yasst_string.rb
144
+ - yasst.gemspec
145
+ homepage: https://github.com/rdark/yasst
146
+ licenses:
147
+ - MIT
148
+ metadata: {}
149
+ post_install_message:
150
+ rdoc_options: []
151
+ require_paths:
152
+ - lib
153
+ required_ruby_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ required_rubygems_version: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - ! '>='
161
+ - !ruby/object:Gem::Version
162
+ version: '0'
163
+ requirements: []
164
+ rubyforge_project:
165
+ rubygems_version: 2.4.5
166
+ signing_key:
167
+ specification_version: 4
168
+ summary: Yet Another Secret Stashing Toolkit
169
+ test_files: []