sym 0.1.0 → 2.0.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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +25 -0
  3. data/.document +2 -0
  4. data/.gitignore +6 -2
  5. data/.rspec +1 -1
  6. data/.rubocop.yml +1156 -0
  7. data/.travis.yml +10 -2
  8. data/.yardopts +5 -0
  9. data/Gemfile +2 -0
  10. data/LICENSE +22 -0
  11. data/MANAGING-KEYS.md +67 -0
  12. data/README.md +444 -12
  13. data/Rakefile +10 -2
  14. data/bin/sym.bash-completion +24 -0
  15. data/exe/keychain +38 -0
  16. data/exe/sym +20 -0
  17. data/lib/sym.rb +110 -2
  18. data/lib/sym/app.rb +56 -0
  19. data/lib/sym/app/args.rb +42 -0
  20. data/lib/sym/app/cli.rb +192 -0
  21. data/lib/sym/app/commands.rb +56 -0
  22. data/lib/sym/app/commands/command.rb +77 -0
  23. data/lib/sym/app/commands/delete_keychain_item.rb +17 -0
  24. data/lib/sym/app/commands/encrypt_decrypt.rb +26 -0
  25. data/lib/sym/app/commands/generate_key.rb +37 -0
  26. data/lib/sym/app/commands/open_editor.rb +97 -0
  27. data/lib/sym/app/commands/print_key.rb +15 -0
  28. data/lib/sym/app/commands/show_examples.rb +76 -0
  29. data/lib/sym/app/commands/show_help.rb +16 -0
  30. data/lib/sym/app/commands/show_language_examples.rb +81 -0
  31. data/lib/sym/app/commands/show_version.rb +14 -0
  32. data/lib/sym/app/input/handler.rb +41 -0
  33. data/lib/sym/app/keychain.rb +135 -0
  34. data/lib/sym/app/nlp.rb +18 -0
  35. data/lib/sym/app/nlp/constants.rb +32 -0
  36. data/lib/sym/app/nlp/translator.rb +61 -0
  37. data/lib/sym/app/nlp/usage.rb +72 -0
  38. data/lib/sym/app/output.rb +15 -0
  39. data/lib/sym/app/output/base.rb +61 -0
  40. data/lib/sym/app/output/file.rb +18 -0
  41. data/lib/sym/app/output/noop.rb +14 -0
  42. data/lib/sym/app/output/stdout.rb +13 -0
  43. data/lib/sym/app/password/cache.rb +63 -0
  44. data/lib/sym/app/private_key/base64_decoder.rb +17 -0
  45. data/lib/sym/app/private_key/decryptor.rb +71 -0
  46. data/lib/sym/app/private_key/detector.rb +42 -0
  47. data/lib/sym/app/private_key/handler.rb +44 -0
  48. data/lib/sym/app/short_name.rb +10 -0
  49. data/lib/sym/application.rb +114 -0
  50. data/lib/sym/cipher_handler.rb +46 -0
  51. data/lib/sym/configuration.rb +39 -0
  52. data/lib/sym/data.rb +23 -0
  53. data/lib/sym/data/decoder.rb +28 -0
  54. data/lib/sym/data/encoder.rb +24 -0
  55. data/lib/sym/data/wrapper_struct.rb +43 -0
  56. data/lib/sym/encrypted_file.rb +34 -0
  57. data/lib/sym/errors.rb +37 -0
  58. data/lib/sym/extensions/class_methods.rb +12 -0
  59. data/lib/sym/extensions/instance_methods.rb +114 -0
  60. data/lib/sym/version.rb +1 -1
  61. data/sym.gemspec +34 -15
  62. metadata +224 -9
data/Rakefile CHANGED
@@ -1,5 +1,13 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'yard'
4
+
5
+
6
+ YARD::Rake::YardocTask.new(:doc) do |t|
7
+ t.files = %w(lib/**/*.rb exe/*.rb - README.md MANAGING-KEYS.md LICENSE)
8
+ t.options.unshift('--title', '"Sym – Symmetric Key Encryption for Your Data"')
9
+ t.after = ->() { exec('open doc/index.html') }
10
+ end
3
11
 
4
12
  RSpec::Core::RakeTask.new(:spec)
5
13
 
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Sym command line completion
4
+ #
5
+ # © 2015-2016, Konstantin Gredeskoul, https://github.com/kigster/sym
6
+ # MIT LICENSE
7
+ #
8
+
9
+ _sym() {
10
+ local SYM_OPTS SYM_POINTS cur prev
11
+
12
+ cur="${COMP_WORDS[COMP_CWORD]}"
13
+ prev="${COMP_WORDS[COMP_CWORD-1]}"
14
+
15
+ COMPREPLY=()
16
+
17
+ SYM_COMP_OPTIONS=$(sym --dictionary)
18
+ [[ $COMP_CWORD == 1 ]] && SYM_COMP_OPTIONS="${SYM_COMP_OPTIONS} ${SYM_COMMANDS}"
19
+ COMPREPLY=( $(compgen -W "${SYM_COMP_OPTIONS}" -- ${cur}) )
20
+ return 0
21
+ }
22
+
23
+ complete -F _sym sym
24
+
data/exe/keychain ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib_path = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $LOAD_PATH << lib_path if File.exist?(lib_path) && !$LOAD_PATH.include?(lib_path)
5
+
6
+ require 'sym'
7
+ require 'sym/app'
8
+ require 'sym/app/keychain'
9
+ require 'colored2'
10
+
11
+ def usage
12
+ puts 'Usage: ' + 'keychain'.bold.blue + ' item [ add <contents> | find | delete ]'.bold.green
13
+ exit 0
14
+ end
15
+
16
+ usage if ARGV.empty?
17
+
18
+ key_name, action, data = ARGV
19
+
20
+ unless %i(add find delete).include?(action.to_sym)
21
+ puts "Error: operation #{action.bold.red} is not recognized"
22
+ usage
23
+ end
24
+
25
+ if action.eql?('add') && data.nil?
26
+ puts "Error: please provide data to store with the #{'add'.bold.green} operation."
27
+ usage
28
+ end
29
+
30
+ begin
31
+ puts data ? \
32
+ Sym::App::KeyChain.new(key_name).send(action.to_sym, data) :
33
+ Sym::App::KeyChain.new(key_name).send(action.to_sym)
34
+ rescue StandardError => e
35
+ STDERR.puts "#{e.message.red}"
36
+ end
37
+
38
+
data/exe/sym ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib_path = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $LOAD_PATH << lib_path if File.exist?(lib_path) && !$LOAD_PATH.include?(lib_path)
5
+
6
+ require 'sym'
7
+ require 'sym/app'
8
+
9
+ #ARGV.any?{ |a| a =~ /^-/ } ?
10
+ begin
11
+ ARGV.first =~ /^-/ ?
12
+ ::Sym::App::CLI.new(ARGV.dup).execute :
13
+ ::Sym::App::NLP::Translator.new(ARGV.dup).translate.and.execute
14
+ rescue Interrupt => e
15
+ STDERR.flush
16
+ STDERR.puts "Interrupt, #{e.message}, exiting."
17
+ STDERR.flush
18
+ end
19
+
20
+
data/lib/sym.rb CHANGED
@@ -1,5 +1,113 @@
1
- require "sym/version"
1
+ require 'colored2'
2
+ require 'zlib'
3
+ require 'coin'
4
+
5
+ require_relative 'sym/configuration'
6
+
7
+ Sym::Configuration.configure do |config|
8
+ config.password_cipher = 'AES-128-CBC'
9
+ config.data_cipher = 'AES-256-CBC'
10
+ config.private_key_cipher = config.data_cipher
11
+ config.compression_enabled = true
12
+ config.compression_level = Zlib::BEST_COMPRESSION
13
+ end
14
+
15
+ #
16
+ # == Using Sym Library
17
+ #
18
+ # This library is a "wrapper" that allows you to take advantage of the
19
+ # symmetric encryption functionality provided by the {OpenSSL} gem (and the
20
+ # underlying C library). In order to use the library in your ruby classes, you
21
+ # should _include_ the module {Sym}.
22
+ #
23
+ # The including class is decorated with four instance methods from the
24
+ # module {Sym::Extensions::InstanceMethods} and two class methods from
25
+ # {Sym::Extensions::ClassMethods} – for specifics, please refer there.
26
+ #
27
+ # The two main instance methods are +#encr+ and +#decr+, which as the name
28
+ # implies, perform two-way symmetric encryption and decryption of any Ruby object
29
+ # that can be +marshaled+.
30
+ #
31
+ # Two additional instance methods +#encr_password+ and +#decr_password+ turn on
32
+ # password-based encryption, which actually uses a password to construct a 128-bit
33
+ # long private key, and then uses that in the encryption of the data.
34
+ # You could use them to encrypt data with a password instead of a randomly
35
+ # generated private key.
36
+ #
37
+ # The library comes with a rich CLI interface, which is mostly encapsulated under the
38
+ # +Sym::App+ namespace.
39
+ #
40
+ # The +sym+ executable that is the "app" in this case, and is a _user_ of the
41
+ # API methods +#encr+ and +#decr+.
42
+ #
43
+ # Create a new key with +#create_private_key+ class method, which returns a new
44
+ # key every time it's called, or with +#private_key+ class method, which either
45
+ # assigns, or creates and caches the private key at a class level.
46
+ #
47
+ # == Example
48
+ #
49
+ # require 'sym'
50
+ #
51
+ # class TestClass
52
+ # include Sym
53
+ # # read the key from environmant variable and assign to this class.
54
+ # private_key ENV['PRIVATE_KEY']
55
+ #
56
+ # def sensitive_value=(value)
57
+ # @sensitive_value = encr(value, self.class.private_key)
58
+ # end
59
+ #
60
+ # def sensitive_value
61
+ # decr(@sensitive_value, self.class.private_key)
62
+ # end
63
+ # end
64
+ #
65
+ # == Private Key
66
+ #
67
+ # They private key can be generated by +TestClass.create_private_key+
68
+ # which returns but does not store a new random 256-bit key.
69
+ #
70
+ # The key can be assigned and saved, or auto-generated and saved using the
71
+ # +#private_key+ method on the class that includes the +Sym+ module.
72
+ #
73
+ # Each class including the +Sym+ module would get their own +#private_key#
74
+ # class-instance variable accessor, and a possible value.
75
+ #
76
+ # For example:
77
+ #
78
+ #
79
+
80
+ module Kernel
81
+ def require_dir(___dir)
82
+ @___dir ||= File.dirname(__FILE__)
83
+ # require files using a consistent order based on the dir/file name.
84
+ # this should be OS-neutral
85
+ Dir["#{@___dir}/#{___dir}/*.rb"].sort.each do |___file|
86
+ require(___file)
87
+ end
88
+ end
89
+ end
90
+
91
+ require_dir 'sym/extensions'
2
92
 
3
93
  module Sym
4
- # Your code goes here...
94
+ def self.included(klass)
95
+ klass.instance_eval do
96
+ include ::Sym::Extensions::InstanceMethods
97
+ extend ::Sym::Extensions::ClassMethods
98
+ class << self
99
+ def private_key(value = nil)
100
+ if value
101
+ @private_key= value
102
+ elsif @private_key
103
+ @private_key
104
+ else
105
+ @private_key= self.create_private_key
106
+ end
107
+ @private_key
108
+ end
109
+ end
110
+ end
111
+ end
5
112
  end
113
+
data/lib/sym/app.rb ADDED
@@ -0,0 +1,56 @@
1
+ require 'sym'
2
+ require 'active_support/inflector'
3
+
4
+ module Sym
5
+
6
+ # The {Sym::App} Module is responsible for handing user input and executing commands.
7
+ # Central class in this module is the {Sym::App::CLI} class. However, it is
8
+ # recommended that ruby integration with the {Sym::App} module functionality
9
+ # is done via the {Sym::Application} class.
10
+ #
11
+ # Methods in this module are responsible for reporting errors and
12
+ # maintaining the future exit code class-global variable.
13
+ #
14
+ # It also contains several helpers that enable some additional functionality
15
+ # on Mac OS-X (such as using KeyChain for storing encryption keys).
16
+ #
17
+ module App
18
+ class << self
19
+ attr_accessor :exit_code
20
+ end
21
+
22
+ self.exit_code = 0
23
+
24
+ def self.out
25
+ STDERR
26
+ end
27
+
28
+ def self.error(
29
+ config: {},
30
+ exception: nil,
31
+ type: nil,
32
+ details: nil,
33
+ reason: nil,
34
+ comments: nil)
35
+
36
+ self.out.puts([\
37
+ "#{(type || exception.class.name).titleize}:".red.bold.underlined +
38
+ (sprintf ' %s', details || exception.message).red.italic,
39
+ (reason ? "\n#{reason.blue.bold.italic}" : nil),
40
+ (comments ? "\n\n#{comments}" : nil)].compact.join("\n"))
41
+ self.out.puts "\n" + exception.backtrace.join("\n").bold.red if exception && config && config[:trace]
42
+ self.exit_code = 1
43
+ end
44
+
45
+ def self.is_osx?
46
+ Gem::Platform.local.os.eql?('darwin')
47
+ end
48
+ def self.this_os
49
+ Gem::Platform.local.os
50
+ end
51
+ end
52
+ end
53
+
54
+ require 'sym/app/short_name'
55
+ require 'sym/version'
56
+ require_dir 'sym/app'
@@ -0,0 +1,42 @@
1
+ module Sym
2
+ module App
3
+
4
+ class Args
5
+
6
+ OPTIONS_REQUIRE_KEY = %i(encrypt decrypt edit)
7
+ OPTIONS_KEY_CREATED = %i(generate)
8
+ OPTIONS_SPECIFY_KEY = %i(private_key interactive keyfile keychain)
9
+ OPTIONS_SPECIFY_OUTPUT = %i(output quiet)
10
+
11
+ attr_accessor :opts, :selected_options
12
+
13
+ def initialize(opts)
14
+ self.opts = opts
15
+ self.selected_options = opts.keys.reject { |k| !opts[k] }
16
+ end
17
+
18
+ def specify_key?
19
+ do?(OPTIONS_SPECIFY_KEY)
20
+ end
21
+
22
+ def require_key?
23
+ do?(OPTIONS_REQUIRE_KEY)
24
+ end
25
+
26
+ def generate_key?
27
+ do?(OPTIONS_KEY_CREATED)
28
+ end
29
+
30
+ def output_class
31
+ output_type = OPTIONS_SPECIFY_OUTPUT.find { |o| opts[o] } # includes nil
32
+ Sym::App::Output.outputs[output_type]
33
+ end
34
+
35
+ private
36
+ def do?(list)
37
+ !(list & selected_options).empty?
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,192 @@
1
+ require 'slop'
2
+ require 'sym'
3
+ require 'colored2'
4
+ require 'yaml'
5
+ require 'forwardable'
6
+ require 'openssl'
7
+ require 'sym/application'
8
+ require 'sym/errors'
9
+ require 'sym/app/commands'
10
+ require 'sym/app/keychain'
11
+ require 'sym/app/private_key/handler'
12
+ require 'sym/app/nlp/constants'
13
+ require 'highline'
14
+
15
+ require_relative 'output/file'
16
+ require_relative 'output/file'
17
+ require_relative 'output/stdout'
18
+
19
+ module Sym
20
+ module App
21
+ # This is the main interface class for the CLI application.
22
+ # It is responsible for parsing user's input, providing help, examples,
23
+ # coordination of various sub-systems (such as PrivateKey detection), etc.
24
+ #
25
+ # Besides holding the majority of the application state, it contains
26
+ # two primary public methods: +#new+ and +#run+.
27
+ #
28
+ # The constructor is responsible for parsing the flags and determining
29
+ # the the application is about to do. It sets up input/output, but doesn't
30
+ # really execute any encryption or decryption. This happens in the +#run+
31
+ # method called immediately after +#new+.
32
+ #
33
+ # {{Shh::App::CLI}} module effectively performs the translation of
34
+ # the +opts+ object (of type {Slop::Result}) and interpretation of
35
+ # users intentions. It holds on to +opts+ for the duration of the program.
36
+ #
37
+ # == Responsibility Delegated
38
+ #
39
+ # The responsibility of determining the private key from various
40
+ # options provided is performed by the {Sym::App::PrivateKey::Handler}
41
+ # instance. See there for more details.
42
+ #
43
+ # Subsequently, +#run+ method handles the finding of the appropriate
44
+ # {Sym::App::Commands::Command} subclass to respond to user's request.
45
+ # Command registry, sorting, command dependencies, and finding them is
46
+ # done by the {Sym::App::Coommands} module.
47
+ #
48
+ # User input is handled by the {Sym::App::Input::Handler} instance, while
49
+ # the output is provided by the procs in the {Sym::App::Output} classes.
50
+ #
51
+ # Finally, the Mac OS-X -specific usage of the KeyChain, is encapsulated
52
+ # in a cross-platform way inside the {Sym::App::Keychain} module.
53
+
54
+ class CLI
55
+
56
+ extend Forwardable
57
+
58
+ def_delegators :@application, :command
59
+
60
+ attr_accessor :opts, :application, :outputs, :output_proc
61
+
62
+ def initialize(argv)
63
+ begin
64
+ argv_copy = argv.dup
65
+ dict = false
66
+ if argv_copy.include?('--dictionary')
67
+ dict = true
68
+ argv_copy.delete('--dictionary')
69
+ end
70
+ self.opts = parse(argv_copy)
71
+ if dict
72
+ options = opts.parser.unused_options + opts.parser.used_options
73
+ puts options.map { |o| o.to_s.gsub(/.*(--[\w-]+).*/, '\1') }.sort.join(' ')
74
+ exit 0
75
+ end
76
+ rescue StandardError => e
77
+ error exception: e
78
+ return
79
+ end
80
+
81
+ configure_color(argv)
82
+
83
+ self.application = ::Sym::Application.new(opts)
84
+ select_output_stream
85
+ end
86
+
87
+ def execute
88
+ return Sym::App.exit_code if Sym::App.exit_code != 0
89
+
90
+ result = application.execute
91
+ if result.is_a?(Hash)
92
+ self.output_proc = ::Sym::App::Args.new({}).output_class
93
+ error(result)
94
+ else
95
+ self.output_proc.call(result)
96
+ end
97
+ end
98
+
99
+ private
100
+
101
+ def error(hash)
102
+ Sym::App.error(hash.merge(config: (opts ? opts.to_hash : {})))
103
+ end
104
+
105
+ def select_output_stream
106
+ output_klass = application.args.output_class
107
+
108
+ unless output_klass && output_klass.is_a?(Class)
109
+ raise "Can not determine output class from arguments #{opts.to_hash}"
110
+ end
111
+
112
+ self.output_proc = output_klass.new(self).output_proc
113
+ end
114
+
115
+ def configure_color(argv)
116
+ if opts[:no_color]
117
+ Colored2.disable! # reparse options without the colors to create new help msg
118
+ self.opts = parse(argv.dup)
119
+ end
120
+ end
121
+
122
+ def parse(arguments)
123
+ Slop.parse(arguments) do |o|
124
+ o.banner = "Sym (#{Sym::VERSION}) – encrypt/decrypt data with a private key\n".bold.white
125
+ o.separator 'Usage:'.yellow
126
+ o.separator ' # Generate a new key:'.dark
127
+ o.separator ' sym -g '.green.bold + '[ -c ] [ -p ] [ -x keychain ] [ -o keyfile | -q | ] '.green
128
+ o.separator ''
129
+ o.separator ' # Encrypt/Decrypt '.dark
130
+ o.separator ' sym [ -d | -e ] '.green.bold + '[ -f <file> | -s <string> ] '.green
131
+ o.separator ' [ -k key | -K keyfile | -x keychain | -i ] '.green
132
+ o.separator ' [ -o <output file> ] '.green
133
+ o.separator ' '
134
+ o.separator ' # Edit an encrypted file in $EDITOR '.dark
135
+ o.separator ' sym -t -f <file> [ -b ]'.green.bold + '[ -k key | -K keyfile | -x keychain | -i ] '.green
136
+
137
+ o.separator ' '
138
+ o.separator 'Modes:'.yellow
139
+
140
+ o.bool '-e', '--encrypt', ' encrypt mode'
141
+ o.bool '-d', '--decrypt', ' decrypt mode'
142
+ o.bool '-t', '--edit', ' decrypt, open an encr. file in an $EDITOR'
143
+
144
+ o.separator ' '
145
+ o.separator 'Create a private key:'.yellow
146
+
147
+ o.bool '-g', '--generate', ' generate a new private key'
148
+ o.bool '-p', '--password', ' encrypt the key with a password'
149
+ o.bool '-c', '--copy', ' copy the new key to the clipboard'
150
+
151
+ if Sym::App.is_osx?
152
+ o.string '-x', '--keychain', '[key-name] '.blue + 'add to (or read from) the OS-X Keychain'
153
+ end
154
+
155
+ o.separator ' '
156
+ o.separator 'Password Caching:'.yellow
157
+
158
+ o.integer '-M', '--password-timeout', '[timeout]'.blue + ' when passwords expire (in seconds)'
159
+ o.bool '-P', '--no-password-cache', ' disables key password caching'
160
+
161
+ o.separator ' '
162
+ o.separator 'Provide a private key:'.yellow
163
+ o.bool '-i', '--interactive', ' Paste or type the key interactively'
164
+ o.string '-k', '--private-key', '[key] '.blue + ' private key as a string'
165
+ o.string '-K', '--keyfile', '[key-file]'.blue + ' private key from a file'
166
+ o.separator ' '
167
+ o.separator 'Data:'.yellow
168
+ o.string '-s', '--string', '[string]'.blue + ' specify a string to encrypt/decrypt'
169
+ o.string '-f', '--file', '[file] '.blue + ' filename to read from'
170
+ o.string '-o', '--output', '[file] '.blue + ' filename to write to'
171
+ o.separator ' '
172
+ o.separator 'Flags:'.yellow
173
+ if Sym::App.is_osx?
174
+ o.string '--keychain-del', '[key-name] '.blue + 'delete keychain entry with that name'
175
+ end
176
+ o.bool '-b', '--backup', ' create a backup file in the edit mode'
177
+ o.bool '-v', '--verbose', ' show additional information'
178
+ o.bool '-T', '--trace', ' print a backtrace of any errors'
179
+ o.bool '-q', '--quiet', ' silence all output'
180
+ o.bool '-V', '--version', ' print library version'
181
+ o.bool '-N', '--no-color', ' disable color output'
182
+ o.separator ' '
183
+ o.separator 'Help & Examples:'.yellow
184
+ o.bool '-E', '--examples', ' show several examples'
185
+ o.bool '-L', '--language', ' natural language examples'
186
+ o.bool '-h', '--help', ' show help'
187
+
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end