shhh 1.4.1 → 1.5.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8be044ac1a326bd26c343102e64650b364ca6825
4
- data.tar.gz: ae67a04259e58203f3d6cf54a1f7e22ebfe61279
3
+ metadata.gz: a18f2fa8ab3ca75426c0eccec0ddb42ca59c71ee
4
+ data.tar.gz: 2459a7f861d35612ab3e607a87999e9c98e071a2
5
5
  SHA512:
6
- metadata.gz: a334844785be58e522aa97add26d34961799885540dffcc5f584939cad872bab7f97a0229fcefcd4856c0123cd19bc15e12602784a6ac28fdf94b14cb6762510
7
- data.tar.gz: 273554d3ce0ffb40c60b878761fd60255df7d00366d1627812600536e5cf9528d5878b145fc82ce51b858205d9b5d25688d54d0e8069d35383d17ab5b9df2735
6
+ metadata.gz: 031191a168e9e6aa553f3950c8a29c2dc26c15ae4f5b8baeb402b0da3a51036268e66aeb90d409c9ecad439ac259e1f04d54cd823f3d3cca380de196f2df392e
7
+ data.tar.gz: 76c711a907e9b97ef72c0ac83c30f8f47e24574c3398ec67d246a9fb8270db3c8761abeeed8d0737dbea89aff4d21fe1313da75603a9d92e215605057e8144dd
data/.gitignore CHANGED
@@ -10,3 +10,4 @@
10
10
  /tmp/
11
11
  /data
12
12
  /spec/coverage/
13
+ *.enc
data/README.md CHANGED
@@ -1,44 +1,57 @@
1
- # Shhh
1
+ # Shhh — Your Encryption Best Friend
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/shhh.svg)](https://badge.fury.io/rb/shhh)
4
4
  [![Downloads](http://ruby-gem-downloads-badge.herokuapp.com/shhh?type=total)](https://rubygems.org/gems/shhh)
5
5
 
6
-
7
- [![Build Status](https://travis-ci.org/kigster/secrets-cipher-base64.svg?branch=master)](https://travis-ci.org/kigster/secrets-cipher-base64)
6
+ [![Build Status](https://travis-ci.org/kigster/shhh.svg?branch=master)](https://travis-ci.org/kigster/shhh)
8
7
  [![Code Climate](https://codeclimate.com/github/kigster/secrets-cipher-base64/badges/gpa.svg)](https://codeclimate.com/github/kigster/secrets-cipher-base64)
9
8
  [![Test Coverage](https://codeclimate.com/github/kigster/secrets-cipher-base64/badges/coverage.svg)](https://codeclimate.com/github/kigster/secrets-cipher-base64/coverage)
10
9
  [![Issue Count](https://codeclimate.com/github/kigster/secrets-cipher-base64/badges/issue_count.svg)](https://codeclimate.com/github/kigster/secrets-cipher-base64)
11
10
 
12
- ## Summary
11
+ ## Description
13
12
 
14
- What? *Another security gem?* —— Well, funny you should ask!
13
+ ### Summary
15
14
 
16
- You see, security is an incredibly wide topic. The tools around security tend to fall into two classes: swiss army knife wrappers around `OpenSSL`, and more specialized tools. This gem falls in the second category. It wraps a very small part of `OpenSSL` library.
15
+ > __shhh__ is little program that makes it _trivial to encrypt and decrypt sensitive data_. But, unlike many other existing tools, __shhh__'s goal is to dramatically simplify the command line interface (CLI), and make symmetric encryption as routine as listing directories in Terminal.
17
16
 
18
- > __Namely, `shhh` provides:__
19
- >
20
- > * Symmetric data encryption with:
21
- > * the cipher `AES-256-CBC`
22
- > * 256-bit private key
23
- > * which can be optionally password-encrypted.
24
- > * Rich command line interface with some innovative features, such as inline encrypted file editing using your current `$EDITOR`
25
- > * Automatic compression of the data
26
- > * Rich Ruby API and highly extensible approach to encryption/decryption
27
- > * Automatic detection of password-protected keys,
28
- > * and more...
17
+ With this tool I wanted to make it easy to memorize the most common options, so that there is little no longer a barrier to the full power of encryption offered by [`OpenSSL`](https://www.openssl.org/) library.
29
18
 
30
- The main point behind this gem is to allow you to store sensitive application secrets in your source code repo as `AES-256-CBC`-encrypted files or strings (this is the same encryption algorithm that US Government uses internally). The output of the encryption is always a (urlsafe) `base64`-encoded string, without the linebreaks.
31
-
32
- The private key (encrypted or not) is also a base64-encoded string, typically 45 characters long (unless it's password encrypted, in which case it is considerably longer).
33
-
34
- Using a single-line string that `urlsafe_encode64()` generates, makes this gem well suited for encrypting specific fields in the YAML file, or simply the entire file. This also means that the private key can be easily exported as an environment variable (as it's value is a single-line string).
35
-
36
- > NOTE: The library leverages the code shown in the following discussion: http://stuff-things.net/2015/02/12/symmetric-encryption-with-ruby-and-rails/ I'd like to acknowledge the the author of the above thread for providing clear examples of a simple symmetric encryption with `OpenSSL`.
19
+ And no tool works in isolation: this is just a stepping stone that could be part of your deployment or infrastructure code: don't rely on external services: minimize the risk of a "man-in-the-middle" attack, by dealing with the encryption and decryption locally. Ideal application of this gem, is the ability to store sensitive application _secrets_ protected on a file system, or in a repo, and use `shhh` to automaticaly decrypt the data when any changes are to be made, or when the data needs to be read by an application service.
20
+
21
+ And finally, in addition to the rich CLI interface of the `shhh` executable, there is a rich and extensibe symmetric encryption API that can be easily used from any ruby project.
37
22
 
38
- ## Symmetric Encryption
23
+ ### How It Works
39
24
 
40
- Symmetric encryption simply means that we are using the same private key to encrypt and decrypt. The secret can be generated by the tool and is a *base64-encoded* string which is 45 characters long. The *decoded* secret is always 32 characters long (or 256 bytes long).
25
+ 1. You start with a piece of sensitive data, say it's called _X_.
26
+ 2. _X_ is currently a file on your file system, unencrypted.
27
+ 2. You use __shhh__ (with `-g` — for "generate") to make a new encryption key. The key is 256 bits, or 32 bytes, or 45 bytes when base64-encoded.
28
+ 3. You must save this key somewhere safe. We'll talk about this further.
29
+ 4. You use __shhh__ (with `-e`) to encrypt _X_ with the key, and save into _Y_.
30
+ 5. You now delete _X_ from your file system. You now only have _Y_ and the _key_.
31
+ 7. To read the data back, you use __shhh__ with the `-d` (for "decrypt") to decrypt _Y_ back. You can print the contents or save it again.
32
+ 8. But, instead of just decrypting it, you can use the `-t` mode (for "ediT"), which would decrypt _Y_ into _X_, save _X_ into a temporary location, and allow you to edit the unencrypted file using `$EDITOR`. Once you save and exit the editor, a new version is automatically encrypted and replaces the old version, showing you the diff and, optionally, creating a backup.
41
33
 
34
+ ### Features
35
+
36
+ The `shhh` executable as well as the Ruby API provide:
37
+
38
+ * Symmetric data encryption with:
39
+ * the cipher `AES-256-CBC` used by the US Government
40
+ * 256-bit private key
41
+ * which can be auto-generated, and is a *base64-encoded* string which is 45 characters long. The *decoded* secret is always 32 characters long (or 256 bytes long).
42
+ * which can be optionally password-encrypted using 128-bit key.
43
+ * which is automatically detected when the key is read
44
+ * Rich command line interface with some innovative features, such as inline editing of an encrypted file, using your favorite `$EDITOR`.
45
+ * Data handling:
46
+ * Automatic compression of the data upon encryption
47
+ * Automatic base64 encryption to make all encrypted strings fit onto a single line.
48
+ * This makes the format suitable for YAML or JSON configuration files, where only the values are encrypted.
49
+ * Rich Ruby API
50
+ * (OS-X Only): Ability to create, add and delete generic password entries from the Mac OS-X KeyChain, and to leverage the KeyChain to store sensitive private keys.
51
+
52
+ ### Symmetric Encryption
53
+
54
+ Symmetric encryption simply means that we are using the same private key to encrypt and decrypt.
42
55
  In addition to the private key, the encryption uses an IV vector. The library completely hides `iv` from the user, generates one random `iv` per encryption, and stores it together with the field itself (*base64-encoded*).
43
56
 
44
57
  ## Installation
@@ -60,13 +73,21 @@ Or install it into the global namespace with `gem install` command:
60
73
 
61
74
  ### Private Keys
62
75
 
63
- This library relies on the existance of the 32-byte private key (aka, *a secret*), that must be stored somewhere safely if your encrypted data is to be persisted, for example it can be saved into the keychain on Mac OSX.
76
+ This library relies on the existance of the 32-byte private key (aka, *a secret*) to perform encryption and decryption.
64
77
 
65
- > In fact, we put together a separate file that discusses strategies for protecting your encryption keys, for example you can read about [how to use Mac OS-X Keychain Access application](https://github.com/kigster/shhh/blob/master/MANAGING-KEYS.md) and other methods. Additions and discussion are welcome. Please contribute!
78
+ The key can be easily:
79
+
80
+ * generated by this gem and displayed, copied to the clipboard, or saved to the KeyChain
81
+ * one way or another must be kept very well protected and secure from attackers
82
+ * can be fetched from the the Keychain in subsequent encryption/decryption steps
83
+ * password-protected, which you can enable during the generation with the `-p` flag.
84
+ * NOTE: right now there is no way to add a password to an existing key, only generate a new one.
66
85
 
67
- You can use one key for all encrypted fields, or many keys – perhaps one per deployment environment, etc. While you can have per-field private key, it seems like an overkill.
86
+ Unencrypted private key will be in the form of a base64-encoded string, 45 characters long.
68
87
 
69
- __NOTE: it is considered a bad practice to check in the private key into the version control.__ If you keep your secret out of your repo, you can check-in encrypted key file directly into the repo. As long as the private key itself is safe, the data in your encrypted will be next to impossible to extract.
88
+ Encrypted private key will be considerably longer, perhaps 200-300 characters long.
89
+
90
+ When the private key is encrypted, `shhh` will request the password every time it is used. We are looking at adding a caching layer with a configuerable timeout, so that the password is only re-entered once per given period.
70
91
 
71
92
  ### Command Line (CLI)
72
93
 
@@ -116,7 +137,7 @@ Now, whenever you need to encrypt something, in addition to the `-k` and `-K` yo
116
137
 
117
138
  Finally, you can delete a key from KeyChain access by running:
118
139
 
119
- shhh -X staging
140
+ shhh --keychain-del staging
120
141
 
121
142
  #### KeyChain Key Management
122
143
 
@@ -136,33 +157,41 @@ This may be a good time to take a look at the full help message for the `shhh` t
136
157
 
137
158
  Usage:
138
159
  shhh [options]
160
+
139
161
  Modes:
140
- -e, --encrypt encrypt mode
141
- -d, --decrypt decrypt mode
142
- -t, --edit decrypt, open an encr. file in vim
162
+ -e, --encrypt encrypt mode
163
+ -d, --decrypt decrypt mode
164
+ -t, --edit decrypt, open an encr. file in vim
165
+
143
166
  Create a private key:
144
- -g, --generate generate a new private key
145
- -p, --password encrypt the key with a password
146
- -c, --copy copy the new key to the clipboard
167
+ -g, --generate generate a new private key
168
+ -p, --password encrypt the key with a password
169
+ -c, --copy copy the new key to the clipboard
170
+
147
171
  Provide a private key:
148
- -i, --interactive Paste or type the key interactively
149
- -k, --private-key [key] private key as a string
150
- -K, --keyfile [key-file] private key from a file
172
+ -i, --interactive Paste or type the key interactively
173
+ -k, --private-key [key] private key as a string
174
+ -K, --keyfile [key-file] private key from a file
175
+
151
176
  Use your KeyChain password entry to store a private key:
152
- -x, --keychain [key-name] add to, or read the key from Keychain
153
- --keychain-del [key-name] delete keychain entry with that name
177
+ -x, --keychain [key-name] add to, or read the key from Keychain
178
+ --keychain-del [key-name] delete keychain entry with that name
179
+
154
180
  Data:
155
- -s, --string [string] specify a string to encrypt/decrypt
156
- -f, --file [file] filename to read from
157
- -o, --output [file] filename to write to
158
- -b, --backup create a backup file in the edit mode
181
+ -s, --string [string] specify a string to encrypt/decrypt
182
+ -f, --file [file] filename to read from
183
+ -o, --output [file] filename to write to
184
+ -b, --backup create a backup file in the edit mode
185
+
159
186
  Flags:
160
- -v, --verbose show additional information
161
- -T, --trace print a backtrace of any errors
162
- -E, --examples show several examples
163
- -V, --version print library version
164
- -N, --no-color disable color output
165
- -h, --help show help
187
+ -v, --verbose show additional information
188
+ -q, --quiet silence all output
189
+ -T, --trace print a backtrace of any errors
190
+ -E, --examples show several examples
191
+ -L, --language natural language examples
192
+ -V, --version print library version
193
+ -N, --no-color disable color output
194
+ -h, --help show help
166
195
  ```
167
196
 
168
197
  ### CLI Usage Examples
@@ -213,7 +242,7 @@ Here is a full command that opens a file specified by `-f | --file`, using the k
213
242
 
214
243
  NOTE: while much effort has been made to ensure that the gem is bug free, the reality is that no software is bug free. Please make sure to backup your encrypted file before doing it for the first few times to get familiar with the command.
215
244
 
216
- To edit an encrypted file in $EDITOR, while asking for a key (`-i | --interactive`), creating a backup file (`-b | --backup`):
245
+ To edit an encrypted file in $EDITOR, while asking to paste the key (`-i | --interactive`), while creating a backup file (`-b | --backup`):
217
246
 
218
247
  shhh -tibf data.enc
219
248
  # => Private Key: ••••••••••••••••••••••••••••••••••••••••••••
@@ -224,6 +253,72 @@ To edit an encrypted file in $EDITOR, while asking for a key (`-i | --interactiv
224
253
  # ---
225
254
  # # (c) 2016 Konstantin Gredeskoul. All rights reserved.
226
255
 
256
+
257
+ ### Natural Language Processing
258
+
259
+ When shhh is called, and when none of the arguments contain a dash, then the the NLP (natural language processing) Translator is invoked. The Translator is based on a very simple algorithm:
260
+
261
+ * ignore all of the ambiguous words, or words with duplicate meaning
262
+ * unambiguously map arguments to the regular options (the double-dash version)
263
+ * words that already match double-dash options are double-dashed
264
+ * the mapping of words into --options is performed
265
+ * the result is parsed
266
+
267
+ When verbose is provided as an argument, you will additionally see the command line arguments that NLP system had produced following the mapping of the actual arguments. This may be helpful in diagnosis of why a particular sentence is not recognized.
268
+
269
+ CURRENTLY IGNORED WORDS:
270
+
271
+ and, a, the, it, item, to, key, with, about, for, of, new, make
272
+
273
+ CURRENTLY DICTIONARY
274
+
275
+ clipboard >————————➤ copy
276
+ unlock >————————➤ decrypt
277
+ open >————————➤ edit
278
+ lock >————————➤ encrypt
279
+ >————————➤ backup
280
+ >————————➤ keychain
281
+ read >————————➤ file
282
+ create >————————➤ generate
283
+ ask enter type >————————➤ interactive
284
+ from >————————➤ keyfile
285
+ save write >————————➤ output
286
+ using private >————————➤ private_key
287
+ value >————————➤ string
288
+ silently quietly silent shhh >————————➤ quiet
289
+ secure secured protected >————————➤ password
290
+
291
+ EXAMPLES
292
+
293
+ ```bash
294
+ # generate a new private key and copy to the clipboard
295
+ shhh make new key and copy it to the clipboard
296
+
297
+ # generate and save to a file a password-protected key, silently
298
+ shhh create a secure key and save it to "my.key"
299
+
300
+ # encrypt a plain text string with a key, and save the output to a file
301
+ shhh encrypt string "secret string" using $KEY save to file.enc
302
+
303
+ # decrypt a previously encrypted string:
304
+ shhh decrypt string $ENC using a $KEY
305
+
306
+ # encrypt shhh.yml with key from $KEYFILE and save it to shhh.enc
307
+ shhh encrypt shhh.yml with key from $KEYFILE and save it to shhh.enc
308
+
309
+ # decrypt an encrypted file and print it to STDOUT:
310
+ shhh decrypt file data.enc using $KEY
311
+
312
+ # edit an encrypted file in $EDITOR, ask for key, create a backup
313
+ shhh edit file ecrets.enc ask for a key and make a backup
314
+
315
+ # generate a new password-encrypted key, save it to your Keychain:
316
+ shhh generate key with a password to keychain "kyname"
317
+
318
+ # use the new key to encrypt a file:
319
+ shhh encrypt with keychain item "keyname" file password.txt save to passwords.enc
320
+ ```
321
+
227
322
  ### Ruby API
228
323
 
229
324
  To use this library you must include the main `Shhh` module into your library.
@@ -312,9 +407,10 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/kigste
312
407
 
313
408
  This is the proposed mini-idea/specification for an alternative CLI that is at a feature parity with the standard flag-based CLI.
314
409
 
315
- shhh encrypt with $key string 'hello' and save to output.enc
316
- shhh edit file 'passwords.enc' encrypted with $key
317
- shhh decrypt file /etc/secrets encrypted with $key save to ./secrets
410
+ shhh generate key to the clipboard and keychain
411
+ shhh encrypt file 'hello' using $key [to output.enc]
412
+ shhh edit 'passwords.enc' using $key
413
+ shhh decrypt /etc/secrets encrypted with $key save to ./secrets
318
414
  shhh encrypt with keychain $item file $input
319
415
 
320
416
  ## License
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Shhh command line completion
4
+ #
5
+ # © 2015-2016, Konstantin Gredeskoul, https://github.com/kigster/shhh
6
+ # MIT LICENSE
7
+ #
8
+
9
+ _shhh() {
10
+ local SHHH_OPTS SHHH_POINTS cur prev
11
+
12
+ cur="${COMP_WORDS[COMP_CWORD]}"
13
+ prev="${COMP_WORDS[COMP_CWORD-1]}"
14
+
15
+ COMPREPLY=()
16
+
17
+ SHHH_COMP_OPTIONS=$(shhh --dictionary)
18
+ [[ $COMP_CWORD == 1 ]] && SHHH_COMP_OPTIONS="${SHHH_COMP_OPTIONS} ${SHHH_COMMANDS}"
19
+ COMPREPLY=( $(compgen -W "${SHHH_COMP_OPTIONS}" -- ${cur}) )
20
+ return 0
21
+ }
22
+
23
+ complete -F _shhh shhh
24
+
data/exe/shhh CHANGED
@@ -5,4 +5,15 @@ $LOAD_PATH << lib_path if File.exist?(lib_path) && !$LOAD_PATH.include?(lib_path
5
5
 
6
6
  require 'shhh'
7
7
 
8
- Shhh::App::CLI.new(ARGV.dup).run
8
+ #ARGV.any?{ |a| a =~ /^-/ } ?
9
+ begin
10
+ ARGV.first =~ /^-/ ?
11
+ ::Shhh::App::CLI.new(ARGV.dup).run :
12
+ ::Shhh::App::NLP::Translator.new(ARGV.dup).translate.and.run
13
+ rescue Interrupt => e
14
+ STDERR.flush
15
+ STDERR.puts "Interrupt, #{e.message}, exiting."
16
+ STDERR.flush
17
+ end
18
+
19
+
data/lib/shhh/app/args.rb CHANGED
@@ -1,15 +1,24 @@
1
1
  module Shhh
2
2
  module App
3
- class Args < Struct.new(:opts)
3
+ class Args < Struct.new(:opts, :argv)
4
4
  MODE = %i(encrypt decrypt generate edit keychain)
5
5
  KEY = %i(private_key interactive keyfile keychain)
6
+ OUTPUT = %i(output quiet)
6
7
 
7
- def mode?; is MODE; end
8
- def key?; is KEY; end
8
+ def mode?; is(MODE); end
9
+ def key?; is(KEY); end
10
+
11
+ def output_class
12
+ output_type = OUTPUT.find{|o| opts[o] } # includes nil
13
+ Shhh::App::Output.outputs[output_type]
14
+ end
9
15
 
10
16
  private
11
17
  def is(list)
12
- list.any?{ |o| opts[o] }
18
+ !options_for(list).empty?
19
+ end
20
+ def options_for(of)
21
+ of.map{ |o| opts[o] }.compact
13
22
  end
14
23
 
15
24
  end
data/lib/shhh/app/cli.rb CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  #!/usr/bin/env ruby
2
3
  require 'slop'
3
4
  require 'shhh'
@@ -9,8 +10,10 @@ require 'shhh/errors'
9
10
  require 'shhh/app/commands'
10
11
  require 'shhh/app/keychain'
11
12
  require 'shhh/app/private_key/handler'
13
+ require 'shhh/app/nlp/constants'
12
14
  require 'highline'
13
15
 
16
+ require_relative 'output/file'
14
17
  require_relative 'output/file'
15
18
  require_relative 'output/stdout'
16
19
 
@@ -50,7 +53,7 @@ module Shhh
50
53
  # in a cross-platform way inside the {Shhh::App::Keychain} module.
51
54
 
52
55
  class CLI
53
- attr_accessor :opts, :output_proc, :print_proc, :write_proc,
56
+ attr_accessor :opts, :args, :outputs, :output_proc,
54
57
  :action, :password, :key, :input_handler, :key_handler
55
58
 
56
59
  def initialize(argv)
@@ -60,11 +63,15 @@ module Shhh
60
63
  error exception: e
61
64
  return
62
65
  end
66
+
67
+
68
+ self.args = ::Shhh::App::Args.new(opts, argv)
69
+
63
70
  configure_color(argv)
64
71
  select_output_stream
65
-
66
72
  initialize_input_handler
67
73
  initialize_key_handler
74
+
68
75
  self.action = { opts[:encrypt] => :encr, opts[:decrypt] => :decr }[true]
69
76
  end
70
77
 
@@ -76,11 +83,12 @@ module Shhh
76
83
  end
77
84
 
78
85
  if command
86
+ STDERR.puts ' Running command: '.dark + "#{command.to_s}" if opts[:verbose]
79
87
  result = command.run
80
88
  output_proc.call(result)
81
89
  else
82
90
  # command was not found. Reset output to printing, and return an error.
83
- self.output_proc = print_proc
91
+ self.output_proc = Args.new(Hash.new, []).output_class
84
92
  command_not_found_error!
85
93
  end
86
94
 
@@ -125,9 +133,11 @@ module Shhh
125
133
  private
126
134
 
127
135
  def select_output_stream
128
- self.print_proc = Shhh::App::Output::Stdout.new(self).output_proc
129
- self.write_proc = Shhh::App::Output::File.new(self).output_proc
130
- self.output_proc = opts[:output] ? self.write_proc : self.print_proc
136
+ out_klass = self.args.output_class
137
+ raise "Can not determine output class from arguments #{opts.to_hash}" unless
138
+ out_klass && out_klass.is_a?(Class)
139
+
140
+ self.output_proc = out_klass.new(self).output_proc
131
141
  end
132
142
 
133
143
  def configure_color(argv)
@@ -137,7 +147,6 @@ module Shhh
137
147
  end
138
148
  end
139
149
 
140
-
141
150
  def initialize_input_handler(handler = Input::Handler.new)
142
151
  self.input_handler = handler
143
152
  end
@@ -153,8 +162,8 @@ module Shhh
153
162
  supplied_opts = h.keys.select { |k| h[k] }.join(', ')
154
163
  error type: 'Options Error',
155
164
  details: 'Unable to determined what command to run',
156
- reason: "You provided the following options: #{supplied_opts.bold.yellow}"
157
- output_proc.call(opts.to_s)
165
+ reason: "You provided the following options: #{supplied_opts.bold.yellow}",
166
+ comments: opts.to_s
158
167
  else
159
168
  raise Shhh::Errors::NoPrivateKeyFound.new('Private key is required')
160
169
  end
@@ -163,37 +172,49 @@ module Shhh
163
172
  def parse(arguments)
164
173
  Slop.parse(arguments) do |o|
165
174
  o.banner = 'Usage:'.bold.yellow
166
- o.separator ' shhh [options]'.bold.green
167
- o.separator 'Modes:'.bold.yellow
175
+ o.separator ' shhh [options]'.green
176
+ o.separator ' '
177
+ o.separator 'Modes:'.yellow
168
178
  o.bool '-h', '--help', ' show help'
169
179
  o.bool '-d', '--decrypt', ' decrypt mode'
170
180
  o.bool '-t', '--edit', ' decrypt, open an encr. file in ' + editor
171
- o.separator 'Create a private key:'.bold.yellow
181
+ o.separator ' '
182
+ o.separator 'Create a private key:'.yellow
172
183
  o.bool '-g', '--generate', ' generate a new private key'
173
184
  o.bool '-p', '--password', ' encrypt the key with a password'
174
185
  o.bool '-c', '--copy', ' copy the new key to the clipboard'
175
- o.separator 'Provide a private key:'.bold.yellow
186
+ o.separator ' '
187
+ o.separator 'Provide a private key:'.yellow
176
188
  o.bool '-i', '--interactive', ' Paste or type the key interactively'
177
- o.string '-k', '--private-key', '[key] '.bold.blue + ' private key as a string'
178
- o.string '-K', '--keyfile', '[key-file]'.bold.blue + ' private key from a file'
189
+ o.string '-k', '--private-key', '[key] '.blue + ' private key as a string'
190
+ o.string '-K', '--keyfile', '[key-file]'.blue + ' private key from a file'
179
191
  if Shhh::App.is_osx?
180
- o.separator 'Use your KeyChain password entry to store a private key:'.bold.yellow
181
- o.string '-x', '--keychain', '[key-name] '.bold.blue + 'add to, or read the key from Keychain'
182
- o.string '--keychain-del', '[key-name] '.bold.blue + 'delete keychain entry with that name'
192
+ o.separator ' '
193
+ o.separator 'Use your KeyChain password entry to store a private key:'.yellow
194
+ o.string '-x', '--keychain', '[key-name] '.blue + 'add to, or read the key from Keychain'
195
+ o.string '--keychain-del', '[key-name] '.blue + 'delete keychain entry with that name'
183
196
  end
184
- o.separator 'Data:'.bold.yellow
185
- o.string '-s', '--string', '[string]'.bold.blue + ' specify a string to encrypt/decrypt'
186
- o.string '-f', '--file', '[file] '.bold.blue + ' filename to read from'
187
- o.string '-o', '--output', '[file] '.bold.blue + ' filename to write to'
197
+ o.separator ' '
198
+ o.separator 'Data:'.yellow
199
+ o.string '-s', '--string', '[string]'.blue + ' specify a string to encrypt/decrypt'
200
+ o.string '-f', '--file', '[file] '.blue + ' filename to read from'
201
+ o.string '-o', '--output', '[file] '.blue + ' filename to write to'
188
202
  o.bool '-b', '--backup', ' create a backup file in the edit mode'
203
+ o.separator ' '
189
204
  o.separator 'Flags:'.bold.yellow
190
205
  o.bool '-v', '--verbose', ' show additional information'
206
+ o.bool '-q', '--quiet', ' silence all output'
191
207
  o.bool '-T', '--trace', ' print a backtrace of any errors'
192
208
  o.bool '-E', '--examples', ' show several examples'
209
+ o.bool '-L', '--language', ' natural language examples'
193
210
  o.bool '-V', '--version', ' print library version'
194
211
  o.bool '-N', '--no-color', ' disable color output'
195
212
  o.bool '-e', '--encrypt', ' encrypt mode'
196
213
  o.separator ''
214
+ o.on '--dictionary' do
215
+ puts o.to_a.map{ |w| "#{w.flags.reject{|f| f.to_s !~ /--/ }.first.to_s}" }.join(' ')
216
+ exit 0
217
+ end
197
218
  end
198
219
  rescue StandardError => e
199
220
  raise(e)
@@ -9,6 +9,8 @@ module Shhh
9
9
  class << self
10
10
  attr_accessor :required, :incompatible
11
11
 
12
+ include Shhh::App::ShortName
13
+
12
14
  def try_after(*dependencies)
13
15
  Shhh::App::Commands.order(self, dependencies)
14
16
  end
@@ -25,10 +27,6 @@ module Shhh
25
27
  incompatible
26
28
  end
27
29
 
28
- def short_name
29
- name.split(/::/)[-1].underscore.to_sym
30
- end
31
-
32
30
  def options_satisfied_by?(opts_hash)
33
31
  proc = required_options.find { |option| option.is_a?(Proc) }
34
32
  return true if proc && proc.call(opts_hash)
@@ -62,6 +60,10 @@ module Shhh
62
60
  raise Shhh::Errors::AbstractMethodCalled.new(:run)
63
61
  end
64
62
 
63
+ def to_s
64
+ "#{self.class.short_name.to_s.bold.yellow}, with options: #{cli.args.argv.join(' ').gsub(/--/, '').bold.green}"
65
+ end
66
+
65
67
  end
66
68
  end
67
69
  end
@@ -8,7 +8,7 @@ module Shhh
8
8
  try_after :generate_key, :open_editor, :encrypt_decrypt
9
9
 
10
10
  def run
11
- opts.to_s
11
+ opts.to_s(prefix: ' ' * 2)
12
12
  end
13
13
  end
14
14
  end
@@ -0,0 +1,81 @@
1
+ require 'colored2'
2
+ require_relative 'command'
3
+ require_relative '../nlp'
4
+ module Shhh
5
+ module App
6
+ module Commands
7
+ class ShowLanguageExamples < Command
8
+ required_options :language
9
+ try_after :show_help
10
+
11
+
12
+
13
+ def run
14
+ output = []
15
+
16
+ output << Shhh::App::NLP::Base.usage
17
+
18
+ output << example(comment: 'generate a new private key and copy to the clipboard but do not print to terminal',
19
+ command: 'shhh create new key to clipboard quietly'
20
+ )
21
+
22
+ output << example(comment: 'generate and save to a file a password-protected key, silently',
23
+ command: 'shhh create a secure key and save it to "my.key"',
24
+ )
25
+
26
+ output << example(comment: 'encrypt a plain text string with a key, and save the output to a file',
27
+ command: 'shhh encrypt string "secret string" using $(cat my.key) save to file.enc')
28
+
29
+ output << example(comment: 'decrypt a previously encrypted string:',
30
+ command: 'shhh decrypt string $ENC using $(cat my.key)')
31
+
32
+ output << example(comment: 'encrypt "file.txt" with key from my.key and save it to file.enc',
33
+ command: 'shhh encrypt file file.txt with key from my.key and save it to file.enc')
34
+
35
+ output << example(comment: 'decrypt an encrypted file and print it to STDOUT:',
36
+ command: 'shhh decrypt file file.enc with key from "my.key"')
37
+
38
+ output << example(comment: 'edit an encrypted file in $EDITOR, ask for key, and create a backup upon save',
39
+ command: 'shhh edit file file.enc ask for a key and make a backup',
40
+ )
41
+
42
+ if Shhh::App.is_osx?
43
+ output << example(comment: 'generate a new password-encrypted key, save it to your Keychain:',
44
+ command: 'shhh create a new protected key store in keychain "my-keychain-key"')
45
+
46
+ output << example(comment: 'print the key stored in the keychain item "my-keychain-key"',
47
+ command: 'shhh print keychain "my-keychain-key"')
48
+
49
+ output << example(comment: 'use the new key to encrypt a file:',
50
+ command: 'shhh encrypt with keychain "my-keychain-key" file "password.txt" and write to "passwords.enc"')
51
+
52
+ end
53
+
54
+ output.flatten.compact.join("\n")
55
+ end
56
+
57
+ def example(comment: nil, command: nil, echo: nil, result: nil)
58
+ @dict ||= ::Shhh::App::NLP::Constants::DICTIONARY.to_a.flatten!
59
+ _command = command.split(' ').map do |w|
60
+ _w = w.to_sym
61
+ if w == 'shhh'
62
+ w.italic.yellow
63
+ elsif ::Shhh::App::NLP::Constants::STRIPPED.include?(_w)
64
+ w.italic.red
65
+ elsif @dict.include?(_w)
66
+ w.blue
67
+ else
68
+ w
69
+ end
70
+ end.join(' ') if command
71
+ out = []
72
+ out << "# #{comment}".white.dark.italic if comment
73
+ out << "#{_command}" if command
74
+ out << "#{echo}" if echo
75
+ out << "#{result}" if result
76
+ out << (' '*80).dark
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,32 @@
1
+ module Shhh
2
+ module App
3
+ module NLP
4
+ module Constants
5
+ STRIPPED = %i(and a the it item to key with about for of new make store in print)
6
+
7
+ DICTIONARY = {
8
+ # option (Slop)
9
+ # list of english words that map to it
10
+ :copy => [:clipboard],
11
+ :decrypt => [:unlock],
12
+ :edit => [:open],
13
+ :encrypt => [:lock],
14
+ :backup => [],
15
+ :keychain => [],
16
+ :file => [:read],
17
+ :generate => [:create],
18
+ :interactive => [:ask, :enter, :type],
19
+ :keyfile => [:from],
20
+ :output => [:save, :write],
21
+ :private_key => [:using, :private],
22
+ :string => [:value],
23
+ :quiet => [:silently, :quietly, :silent, :shhh],
24
+ :password => [:secure, :secured, :protected]
25
+ }
26
+
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+
@@ -0,0 +1,61 @@
1
+ require_relative 'constants'
2
+ module Shhh
3
+ module App
4
+ module NLP
5
+ class Translator
6
+
7
+ attr_accessor :argv, :translated_argv, :opts
8
+
9
+ def initialize(argv)
10
+ self.argv = argv
11
+ self.opts = CLI.new(%w(-E)).opts.to_hash
12
+ self.translated_argv = []
13
+ end
14
+
15
+ def dict
16
+ ::Shhh::App::NLP::Constants::DICTIONARY
17
+ end
18
+
19
+ def stripped
20
+ ::Shhh::App::NLP::Constants::STRIPPED
21
+ end
22
+
23
+ def translate
24
+ self.translated_argv = argv.map do |value|
25
+ nlp_argument = value.to_sym
26
+ arg = nil
27
+ arg ||= dict.keys.find do |key|
28
+ dict[key].include?(nlp_argument) || key == nlp_argument
29
+ end
30
+ arg ||= nlp_argument
31
+
32
+ if stripped.include?(arg)
33
+ # nada
34
+ elsif opts.to_hash.key?(arg)
35
+ '--' + "#{arg.to_s.gsub(/_/, '-')}"
36
+ else
37
+ arg.to_s
38
+ end
39
+ end.compact
40
+
41
+ counts = {}
42
+ translated_argv.each{ |arg| counts.key?(arg) ? counts[arg] += 1 : counts[arg] = 1 }
43
+ translated_argv.delete_if{ |arg| counts[arg] > 1 }
44
+ self
45
+ end
46
+
47
+ def and
48
+ translate if translated_argv.empty?
49
+ if self.translated_argv.include?('--verbose')
50
+ STDERR.puts 'Original arguments: '.dark + "#{argv.join(' ').green}"
51
+ STDERR.puts ' Translated argv: '.dark + "#{translated_argv.join(' ').blue}"
52
+ end
53
+ ::Shhh::App::CLI.new(self.translated_argv)
54
+ end
55
+
56
+ alias_method :cli, :and
57
+
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,72 @@
1
+ require 'pp'
2
+ module Shhh
3
+ module App
4
+ # shhh generate key to the clipboard and keychain
5
+ # shhh encrypt file 'hello' using $key [to output.enc]
6
+ # shhh edit 'passwords.enc' using $key
7
+ # shhh decrypt /etc/secrets encrypted with $key save to ./secrets
8
+ # shhh encrypt file $input with keychain $item
9
+ module NLP
10
+ module Usage
11
+
12
+ def usage
13
+ out = ''
14
+ out << %Q`
15
+ #{header('Natural Language Processing')}
16
+
17
+ #{'When '.dark.normal}#{'shhh'.bold.blue} #{'is invoked, and the first argument does not begin with a dash,
18
+ then the the NLP (natural language processing) Translator is invoked.
19
+ The Translator is based on a very simple algorithm:
20
+
21
+ * ignore any of the words tagged STRIPPED. These are the ambiguous words,
22
+ or words with duplicate meaning.
23
+
24
+ * map the remaining arguments to regular double-dashed options using the DICTIONARY
25
+
26
+ * words that are a direct match for a --option are automatically double-dashed
27
+
28
+ * remaining words are left as is (these would be file names, key names, etc).
29
+
30
+ * finally, the resulting "new" command line is parsed with regular options.
31
+
32
+ * When arguments include "verbose", NLP system will print "before" and "after"
33
+ of the arguments, so that any issues can be debugged and corrected.
34
+
35
+ '.dark.normal}
36
+
37
+ #{header('Currently ignored words:')}
38
+ #{Constants::STRIPPED.join(', ').red.italic}
39
+
40
+ #{header('Regular Word Mapping')}
41
+ #{Constants::DICTIONARY.pretty_inspect.split(/\n/).map do |line|
42
+ line.gsub(
43
+ /[\:\}\,\[\]]/, ''
44
+ ).gsub(
45
+ /[ {](\w+)=>([^\n]*)/, '\2|\1'
46
+ )
47
+ end.map { |line| convert_dictionary(*line.split('|')) }.join}
48
+
49
+ #{header('Examples')}
50
+ `
51
+ out
52
+ end
53
+
54
+ def convert_dictionary(left = '', right = '')
55
+ [
56
+ sprintf('%35.35s', left.gsub(/ /, ' ')).italic.yellow,
57
+ ' ───────➤ '.dark,
58
+ sprintf('--%-20.20s', right).blue,
59
+
60
+ "\n"
61
+ ].join
62
+ end
63
+
64
+ private
65
+ def header(title)
66
+ title.upcase.bold.underlined
67
+ end
68
+
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'cli'
2
+ require_relative 'nlp/constants'
3
+ require_relative 'nlp/usage'
4
+ require_relative 'nlp/translator'
5
+ module Shhh
6
+ module App
7
+ # shhh generate key to the clipboard and keychain
8
+ # shhh encrypt file 'hello' using $key [to output.enc]
9
+ # shhh edit 'passwords.enc' using $key
10
+ # shhh decrypt /etc/secrets encrypted with $key save to ./secrets
11
+ # shhh encrypt file $input with keychain $item
12
+ module NLP
13
+ class Base
14
+ extend Usage
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,61 @@
1
+ require 'shhh/app/short_name'
2
+ module Shhh
3
+ module App
4
+ module Output
5
+ class Base
6
+
7
+ attr_accessor :cli
8
+
9
+ def initialize(cli)
10
+ self.cli = cli
11
+ end
12
+
13
+ def opts
14
+ cli.opts
15
+ end
16
+
17
+ @outputs = []
18
+ class << self
19
+ attr_accessor :outputs
20
+
21
+ def append(output_klass)
22
+ outputs << output_klass if outputs.is_a?(Array)
23
+ raise "Cannot append #{output_class} after #outputs has been converted to a Hash" \
24
+ unless outputs.is_a?(Array)
25
+ end
26
+
27
+ def map_outputs!
28
+ klasses = self.outputs
29
+ self.outputs = Hash.new
30
+ klasses.each { |k| self.outputs[k.required_option] = k }
31
+ outputs
32
+ end
33
+
34
+ def options_to_outputs
35
+ map_outputs! if outputs.is_a?(Array)
36
+ outputs
37
+ end
38
+ end
39
+
40
+ def self.inherited(klass)
41
+ klass.instance_eval do
42
+ class << self
43
+ attr_writer :required_option
44
+ end
45
+
46
+ klass.required_option = nil
47
+
48
+ class << self
49
+ def required_option(_option = nil)
50
+ self.required_option = _option if _option
51
+ @required_option
52
+ end
53
+ end
54
+ end
55
+ klass.extend(Shhh::App::ShortName)
56
+ Shhh::App::Output::Base.append klass
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -1,16 +1,11 @@
1
+ require 'shhh/app/output/base'
1
2
  module Shhh
2
3
  module App
3
4
  module Output
4
- class File
5
- attr_accessor :cli
5
+ class File < ::Shhh::App::Output::Base
6
6
 
7
- def initialize(cli)
8
- self.cli = cli
9
- end
7
+ required_option :output
10
8
 
11
- def opts
12
- cli.opts
13
- end
14
9
 
15
10
  def output_proc
16
11
  ->(data) {
@@ -0,0 +1,14 @@
1
+ require_relative 'base'
2
+ module Shhh
3
+ module App
4
+ module Output
5
+ class Noop < Shhh::App::Output::Base
6
+ required_option :quiet
7
+
8
+ def output_proc
9
+ ->(*) { ; }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,7 +1,9 @@
1
+ require 'shhh/app/output/file'
1
2
  module Shhh
2
3
  module App
3
4
  module Output
4
- class Stdout < File
5
+ class Stdout < ::Shhh::App::Output::Base
6
+ required_option nil
5
7
  def output_proc
6
8
  ->(argument) { puts argument }
7
9
  end
@@ -0,0 +1,15 @@
1
+ require 'shhh/app/output/base'
2
+ require 'shhh/app/output/file'
3
+ require 'shhh/app/output/stdout'
4
+ require 'shhh/app/output/noop'
5
+
6
+ module Shhh
7
+ module App
8
+ module Output
9
+ def self.outputs
10
+ Shhh::App::Output::Base.options_to_outputs
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,10 @@
1
+ require 'active_support/inflector'
2
+ module Shhh
3
+ module App
4
+ module ShortName
5
+ def short_name
6
+ self.name.split(/::/)[-1].underscore.to_sym
7
+ end
8
+ end
9
+ end
10
+ end
data/lib/shhh/app.rb CHANGED
@@ -27,12 +27,14 @@ module Shhh
27
27
  exception: nil,
28
28
  type: nil,
29
29
  details: nil,
30
- reason: nil)
30
+ reason: nil,
31
+ comments: nil)
31
32
 
32
33
  self.out.puts([\
33
34
  "#{(type || exception.class.name).titleize}:".red.bold.underlined +
34
35
  (sprintf ' %s', details || exception.message).red.italic,
35
- reason ? "\n#{reason.blue.bold.italic}" : nil].compact.join("\n"))
36
+ (reason ? "\n#{reason.blue.bold.italic}" : nil),
37
+ (comments ? "\n\n#{comments}" : nil)].compact.join("\n"))
36
38
  self.out.puts "\n" + exception.backtrace.join("\n").bold.red if exception && config && config[:trace]
37
39
  self.exit_code = 1
38
40
  end
data/lib/shhh/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Shhh
2
- VERSION = '1.4.1'
2
+ VERSION = '1.5.4'
3
3
  end
data/shhh.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'shhh/version'
5
-
5
+ BASH_COMPLETION = File.read("#{lib}/../bin/shhh.bash-completion")
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'shhh'
8
8
  spec.version = Shhh::VERSION
@@ -17,6 +17,17 @@ Gem::Specification.new do |spec|
17
17
  spec.bindir = 'exe'
18
18
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ['lib']
20
+ spec.required_ruby_version = '>= 2.2'
21
+ spec.post_install_message = "
22
+
23
+ Please copy and paste the following BASH function into your ~/.bashrc or
24
+ equivalent, in order to enable command completion:
25
+
26
+ #{BASH_COMPLETION}
27
+
28
+ Thank you for installing Shhh!
29
+ -- KG (github.com/kigster)
30
+ "
20
31
 
21
32
  spec.add_dependency 'require_dir', '~> 0.1'
22
33
  spec.add_dependency 'colored2', '~> 2.0'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shhh
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.1
4
+ version: 1.5.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Gredeskoul
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-08-12 00:00:00.000000000 Z
11
+ date: 2016-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: require_dir
@@ -189,6 +189,7 @@ files:
189
189
  - Rakefile
190
190
  - bin/console
191
191
  - bin/setup
192
+ - bin/shhh.bash-completion
192
193
  - exe/keychain
193
194
  - exe/shhh
194
195
  - lib/shhh.rb
@@ -204,15 +205,24 @@ files:
204
205
  - lib/shhh/app/commands/print_key.rb
205
206
  - lib/shhh/app/commands/show_examples.rb
206
207
  - lib/shhh/app/commands/show_help.rb
208
+ - lib/shhh/app/commands/show_language_examples.rb
207
209
  - lib/shhh/app/commands/show_version.rb
208
210
  - lib/shhh/app/input/handler.rb
209
211
  - lib/shhh/app/keychain.rb
212
+ - lib/shhh/app/nlp.rb
213
+ - lib/shhh/app/nlp/constants.rb
214
+ - lib/shhh/app/nlp/translator.rb
215
+ - lib/shhh/app/nlp/usage.rb
216
+ - lib/shhh/app/output.rb
217
+ - lib/shhh/app/output/base.rb
210
218
  - lib/shhh/app/output/file.rb
219
+ - lib/shhh/app/output/noop.rb
211
220
  - lib/shhh/app/output/stdout.rb
212
221
  - lib/shhh/app/private_key/base64_decoder.rb
213
222
  - lib/shhh/app/private_key/decryptor.rb
214
223
  - lib/shhh/app/private_key/detector.rb
215
224
  - lib/shhh/app/private_key/handler.rb
225
+ - lib/shhh/app/short_name.rb
216
226
  - lib/shhh/cipher_handler.rb
217
227
  - lib/shhh/configuration.rb
218
228
  - lib/shhh/data.rb
@@ -227,7 +237,40 @@ files:
227
237
  homepage: https://github.com/kigster/shhh
228
238
  licenses: []
229
239
  metadata: {}
230
- post_install_message:
240
+ post_install_message: |2
241
+
242
+
243
+ Please copy and paste the following BASH function into your ~/.bashrc or
244
+ equivalent, in order to enable command completion:
245
+
246
+ #!/usr/bin/env bash
247
+ #
248
+ # Shhh command line completion
249
+ #
250
+ # © 2015-2016, Konstantin Gredeskoul, https://github.com/kigster/shhh
251
+ # MIT LICENSE
252
+ #
253
+
254
+ _shhh() {
255
+ local SHHH_OPTS SHHH_POINTS cur prev
256
+
257
+ cur="${COMP_WORDS[COMP_CWORD]}"
258
+ prev="${COMP_WORDS[COMP_CWORD-1]}"
259
+
260
+ COMPREPLY=()
261
+
262
+ SHHH_COMP_OPTIONS=$(shhh --dictionary)
263
+ [[ $COMP_CWORD == 1 ]] && SHHH_COMP_OPTIONS="${SHHH_COMP_OPTIONS} ${SHHH_COMMANDS}"
264
+ COMPREPLY=( $(compgen -W "${SHHH_COMP_OPTIONS}" -- ${cur}) )
265
+ return 0
266
+ }
267
+
268
+ complete -F _shhh shhh
269
+
270
+
271
+
272
+ Thank you for installing Shhh!
273
+ -- KG (github.com/kigster)
231
274
  rdoc_options: []
232
275
  require_paths:
233
276
  - lib
@@ -235,7 +278,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
235
278
  requirements:
236
279
  - - ">="
237
280
  - !ruby/object:Gem::Version
238
- version: '0'
281
+ version: '2.2'
239
282
  required_rubygems_version: !ruby/object:Gem::Requirement
240
283
  requirements:
241
284
  - - ">="
@@ -243,7 +286,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
243
286
  version: '0'
244
287
  requirements: []
245
288
  rubyforge_project:
246
- rubygems_version: 2.6.6
289
+ rubygems_version: 2.5.1
247
290
  signing_key:
248
291
  specification_version: 4
249
292
  summary: Simple tool to encrypt/decrypt data using symmetric aes-256-cbc encryption