sym 0.1.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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