sym 2.1.2 → 2.2.0

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: 6529381babbcb57ee7ca039a9d01dbafe4d8fe9f
4
- data.tar.gz: 9b48b1a55b06dd5870a3f0d25bc0b862c3f64502
3
+ metadata.gz: d0d5e823703d09fb437c0b9f8e11b55dd1a0e204
4
+ data.tar.gz: 42f5145f34d3d0779422e3c2511d35580d85c033
5
5
  SHA512:
6
- metadata.gz: 9de600b399c540917e92f7de8ab0f36b426d24950ee7d0ccca4fce21b929286a7c13c7e3f8f603f25b5698c0ac2d684d6756760b88085a3cadaab1d246e94bf3
7
- data.tar.gz: 23871684bbcb5d41edd02e8922abf866a9d5db90c2f120625e6f67d6d20e377d22b204b782072461b76c1b82fb2575777aab9b7dd2273e5b5d47c0c90332189c
6
+ metadata.gz: 3b007deff2a49f8a31d451fd433eebc7cca83e8ff28a733b8d37a2b34ca5288460e93aa3e8b078442e6c680296fc19b1152f5deac81c2775acc57f79710478b1
7
+ data.tar.gz: 1c89daa41bbf54b754e203a65720f452e96546e79825924f8d670f11ef392e8c04ca34a85a706b3aba4c6f081870f08189239b3a5fdf7c2a473fec1e51ca47bc
data/.travis.yml CHANGED
@@ -1,6 +1,8 @@
1
1
  language: ruby
2
2
  env:
3
3
  - CODECLIMATE_REPO_TOKEN=c71874cc22acffe1e2543d3388d3a96c73a65f0cfe17169dadd8de4a6c062c39
4
+ services:
5
+ - memcached
4
6
  rvm:
5
7
  - 2.4.0
6
8
  - 2.3.3
data/README.md CHANGED
@@ -13,97 +13,6 @@
13
13
 
14
14
  > __sym__ is a command line utility and a Ruby API that makes it _trivial to encrypt and decrypt sensitive data_. Unlike many other existing encryption tools, __sym__ focuses on usability and streamlined interface (CLI), with the goal of making encryption easy and transparent. The result? There is no excuse for keeping your application secrets unencrypted :)
15
15
 
16
- <hr />
17
- ## Table of Contents
18
-
19
- <ul class="small site-footer-links">
20
- <li>
21
- <a href="#description">Description</a>
22
- <ul>
23
- <li>
24
- <a href="#motivation">Motivation</a>
25
- </li>
26
- <li>
27
- <a href="#whats-included">What&#39;s Included</a>
28
- </li>
29
- <li>
30
- <a href="#how-it-works">How It Works</a>
31
- </li>
32
- </ul>
33
- </li>
34
- <li>
35
- <a href="#installation">Installation</a>
36
- </li>
37
- <li>
38
- <a href="#using-sym-with-the-command-line">Using <code>sym</code> with the Command Line</a>
39
- <ul>
40
- <li>
41
- <a href="#private-keys">Private Keys</a>
42
- <ul>
43
- <li>
44
- <a href="#generating-private-keys">Generating Private Keys</a>
45
- </li>
46
- <li>
47
- <a href="#using-keychain-access-on-mac-os-x">Using KeyChain Access on Mac OS-X</a>
48
- </li>
49
- <li>
50
- <a href="#keychain-key-management">KeyChain Key Management</a>
51
- </li>
52
- <li>
53
- <a href="#moving-a-key-to-the-keychain">Moving a Key to the Keychain</a>
54
- </li>
55
- <li>
56
- <a href="#adding-password-to-existing-key">Adding Password to Existing Key</a>
57
- </li>
58
- <li>
59
- <a href="#encryption-and-decryption">Encryption and Decryption</a>
60
- </li>
61
- </ul>
62
- </li>
63
- <li>
64
- <a href="#cli-usage-examples">CLI Usage Examples</a>
65
- <ul>
66
- <li>
67
- <a href="#inline-editing">Inline Editing</a>
68
- </li>
69
- </ul>
70
- </li>
71
- </ul>
72
- </li>
73
- <li>
74
- <a href="#ruby-api">Ruby API</a>
75
- <ul>
76
- <li>
77
- <a href="#encryption-and-decryption-operations">Encryption and Decryption Operations</a>
78
- </li>
79
- <li>
80
- <a href="#full-application-api">Full Application API</a>
81
- </li>
82
- <li>
83
- <a href="#configuration">Configuration</a>
84
- </li>
85
- </ul>
86
- </li>
87
- <li>
88
- <a href="#encryption-features--cipher-used">Encryption Features &amp; Cipher Used</a>
89
- </li>
90
- <li>
91
- <a href="#development">Development</a>
92
- <ul>
93
- <li>
94
- <a href="#contributing">Contributing</a>
95
- </li>
96
- </ul>
97
- </li>
98
- <li>
99
- <a href="#license">License</a>
100
- </li>
101
- <li>
102
- <a href="#acknowledgements">Acknowledgements</a>
103
- </li>
104
- </ul>
105
- <hr />
106
-
107
16
  ### Motivation
108
17
 
109
18
  The primary goal of this tool is to streamline and simplify handling of relatively sensitive data in the most trasparent and easy to use way as possible, without sacrificing security.
@@ -150,18 +59,18 @@ New Password : •••••••••
150
59
  Confirm Password : •••••••••
151
60
  BAhTOh1TeW06OkRhdGE6OldyYXBwZXJTdH.....
152
61
 
153
- ❯ sym -ex my-new-key -s 'My secret data' -o secret.enc
62
+ ❯ sym -ex my-new-key -s 'My secret data' -o secret.enc -C
154
63
  Coin::Vault listening at: druby://127.0.0.1:24924
155
64
  Password: •••••••••
156
65
 
157
66
  ❯ cat secret.enc
158
67
  BAhTOh1TeW06OkRhdGE6OldyYXBFefDFFD.....
159
68
 
160
- ❯ sym -dx my-new-key -f secret.enc
69
+ ❯ sym -dx my-new-key -f secret.enc -C
161
70
  My secret data
162
71
  ```
163
72
 
164
- The line that says `Coin::Vault listening at: druby://127.0.0.1:24924` is the indication that the local dRB server used for caching passwords has been started. Password caching can be easily disabled with `-P` flag. In the example above, the decryption step fetched the password from the cache, and so the user was not required to re-enter the password.
73
+ The line that says `Coin::Vault listening at: druby://127.0.0.1:24924` is the indication that the local dRB server used for caching passwords has been started. Password caching is off by default, but is enabled with `-C` flag. In the example above, the decryption step fetched the password from the cache, and so the user was not required to re-enter the password.
165
74
 
166
75
  __Direct Editing Encrypted Files__
167
76
 
@@ -208,14 +117,13 @@ The private key is the cornerstone of the symmetric encryption. Using `sym`, the
208
117
  * generated and printed to STDOUT, or saved to Mac OS-X KeyChain or a file
209
118
  * fetched from the Keychain in subsequent operations
210
119
  * password-protected during generation (or import) with the `-p` flag.
120
+ * password can be cached using either `memcached` or `dRB` server, if the `-C` flag is provided.
211
121
  * must be kept very well protected and secure from attackers.
212
122
 
213
123
  The __unencrypted private__ key will be in the form of a base64-encoded string, 45 characters long.
214
124
 
215
125
  __Encrypted (with password) private key__ will be considerably longer, perhaps 200-300 characters long.
216
126
 
217
- When the private key is encrypted, `sym` will request the password only once per 15 minute period. The password is cached using a local dRB server, but this caching can be disabled with `-P` flag.
218
-
219
127
  #### Generating Private Keys
220
128
 
221
129
  Let's generate a new key, and copy it to the clipboard (using `pbcopy` command on Mac OS-X):
@@ -289,62 +197,81 @@ You can add a password to a key by combining one of the key description flags (-
289
197
 
290
198
  The above example will take an unencrypted key passed in `$mykey`, ask for a password and save password protected key into the keychain with name "moo."
291
199
 
200
+ #### Password Caching
201
+
202
+ Nobody likes to re-type passwords over and over again, and for this reason *Sym* supports password caching via either a locally running `memcached` instance (the default, if available), or a locally started `dRB` (distributed Ruby) server based on the `Coin` gem.
203
+
204
+ Specifics of configuring both Cache Providers is left to the `Configuration` class, an example of which is shown below in the Ruby API section.
205
+
206
+ In order to control password caching, the following flags are available:
207
+
208
+ * `-C` turns on caching
209
+ * `-T seconds` sets the expiration for cached passwords
210
+ * `-P memcached | drb` controls which of the providers is used. Without this flag, *sym* auto-detects caching provider by first checking for `memcached`, and then starting the `dRB` server.
211
+
292
212
  #### Encryption and Decryption
293
213
 
294
214
  This may be a good time to take a look at the full help message for the `sym` tool, shown naturally with a `-h` or `--help` option.
295
215
 
296
216
  ```
297
- Sym (2.1.1) – encrypt/decrypt data with a private key
217
+ Sym (2.2.0) – encrypt/decrypt data with a private key
298
218
 
299
219
  Usage:
300
220
  # Generate a new key:
301
- sym -g [ -p ] [ -x keychain ] [ -o keyfile | -q | ]
302
-
303
- # Encrypt/Decrypt
304
- sym [ -d | -e ] [ -f <file> | -s <string> ]
305
- [ -k key | -K keyfile | -x keychain | -i ]
306
- [ -o <output file> ]
221
+ sym -g [ -p ] [ -x keychain | -o keyfile | -q | ]
307
222
 
308
- # Edit an encrypted file in $EDITOR
309
- sym -t -f <file> [ -b ][ -k key | -K keyfile | -x keychain | -i ]
223
+ # To specify a key for an operation use any one of:
224
+ <key-spec> = -k key | -K file | -x keychain | -i
310
225
 
226
+ # Encrypt/Decrypt to STDOUT or output file
227
+ sym -e <key-spec> [-f <file> | -s <string>] [-o <file>]
228
+ sym -d <key-spec> [-f <file> | -s <string>] [-o <file>]
229
+
230
+ # Edit an encrypted file in $EDITOR
231
+ sym -t <key-spec> -f <file> [ -b ]
232
+
311
233
  Modes:
312
- -e, --encrypt encrypt mode
313
- -d, --decrypt decrypt mode
314
- -t, --edit decrypt, open an encr. file in an $EDITOR
315
-
234
+ -e, --encrypt encrypt mode
235
+ -d, --decrypt decrypt mode
236
+ -t, --edit edit encrypted file in an $EDITOR
237
+
316
238
  Create a new private key:
317
- -g, --generate generate a new private key
318
- -p, --password encrypt the key with a password
319
- -x, --keychain [key-name] add to (or read from) the OS-X Keychain
320
- -M, --password-timeout [timeout] when passwords expire (in seconds)
321
- -P, --no-password-cache disables caching of key passwords
322
-
239
+ -g, --generate generate a new private key
240
+ -p, --password encrypt the key with a password
241
+
323
242
  Read existing private key from:
324
- -i, --interactive Paste or type the key interactively
325
- -k, --private-key [key] private key as a string
326
- -K, --keyfile [key-file] private key from a file
327
-
243
+ -k, --private-key [key] private key (or key file)
244
+ -K, --keyfile [key-file] private key from a file
245
+ -x, --keychain [key-name] add to (or read from) the OS-X Keychain
246
+ -i, --interactive Paste or type the key interactively
247
+
248
+ Password Cache:
249
+ -C, --cache-password enable the cache (off by default)
250
+ -T, --cache-for [seconds] to cache the password for
251
+ -P, --cache-provider [provider] type of cache, one of:
252
+ [ memcached, drb ]
253
+
328
254
  Data to Encrypt/Decrypt:
329
- -s, --string [string] specify a string to encrypt/decrypt
330
- -f, --file [file] filename to read from
331
- -o, --output [file] filename to write to
332
-
255
+ -s, --string [string] specify a string to encrypt/decrypt
256
+ -f, --file [file] filename to read from
257
+ -o, --output [file] filename to write to
258
+
333
259
  Flags:
334
- -b, --backup create a backup file in the edit mode
335
- -v, --verbose show additional information
336
- -T, --trace print a backtrace of any errors
337
- -D, --debug print debugging information
338
- -q, --quiet silence all output
339
- -V, --version print library version
340
- -N, --no-color disable color output
341
-
260
+ -b, --backup create a backup file in the edit mode
261
+ -v, --verbose show additional information
262
+ -A, --trace print a backtrace of any errors
263
+ -D, --debug print debugging information
264
+ -q, --quiet do not print to STDOUT
265
+ -V, --version print library version
266
+ -N, --no-color disable color output
267
+
342
268
  Utility:
343
- -a, --bash-completion [file] append shell completion to a file
344
-
269
+ -a, --bash-completion [file] append shell completion to a file
270
+
345
271
  Help & Examples:
346
- -E, --examples show several examples
347
- -h, --help show help
272
+ -E, --examples show several examples
273
+ -h, --help show help
274
+
348
275
  ```
349
276
 
350
277
  ### CLI Usage Examples
@@ -485,11 +412,27 @@ The library offers a typical `Sym::Configuration` class which can be used to twe
485
412
  require 'zlib'
486
413
  require 'sym'
487
414
  Sym::Configuration.configure do |config|
488
- config.password_cipher = 'AES-128-CBC' #
489
- config.data_cipher = 'AES-256-CBC'
490
- config.private_key_cipher = config.data_cipher
415
+ config.password_cipher = 'AES-128-CBC'
416
+ config.private_key_cipher = config.data_cipher
491
417
  config.compression_enabled = true
492
- config.compression_level = Zlib::BEST_COMPRESSION
418
+ config.compression_level = Zlib::BEST_COMPRESSION
419
+
420
+ config.password_cache_timeout = 300
421
+ config.password_cache_arguments = {
422
+ drb: {
423
+ opts: {
424
+ uri: 'druby://127.0.0.1:24924'
425
+ }
426
+ },
427
+ memcached: {
428
+ args: %w(127.0.0.1:11211),
429
+ opts: { namespace: 'sym',
430
+ compress: true,
431
+ expires_in: config.password_cache_timeout
432
+ }
433
+
434
+ }
435
+ }
493
436
  end
494
437
  ```
495
438
 
@@ -504,7 +447,7 @@ The `sym` executable as well as the Ruby API provide:
504
447
  * 256-bit private key, that
505
448
  * can be generated and is a *base64-encoded* string about 45 characters long. The *decoded* key is always 32 characters (or 256 bytes) long.
506
449
  * can be optionally password-encrypted using the 128-bit key, and then be automatically detected (and password requested) when the key is used
507
- * can have its password cached for 15 minutes locally on the machine using dRB server (or used without the cache with `-P` flag).
450
+ * can optionally have its password cached for 15 minutes locally on the machine using `memcached` or using a `dRB` server
508
451
  * Rich command line interface with some innovative features, such as inline editing of an encrypted file, using your favorite `$EDITOR`.
509
452
  * Data handling:
510
453
  * Automatic compression of the data upon encryption
data/bin/sym.completion CHANGED
@@ -16,11 +16,12 @@ _sym() {
16
16
 
17
17
  #[[ $COMP_CWORD == 1 ]] && SYM_COMP_OPTIONS="${SYM_COMP_OPTIONS} ${SYM_COMMANDS}"
18
18
  if [[ $prev =~ "-f" || $prev =~ "-o" || $prev =~ "-K" || $prev == "--keyfile" ]] ; then
19
- SYM_COMP_OPTIONS="$(find . -type f -depth 1 | sed 's#./##g')"
20
- elif [[ "${cur}" == '--' || "${cur}" == --* ]] ; then
21
- SYM_COMP_OPTIONS=$(sym --dictionary | sed -E 's/ /\n/g')
19
+ SYM_COMP_OPTIONS="$(find . -type f -depth 1 | sed 's#^.\/##g')"
20
+ elif [[ "${cur}" == '-' || "${cur}" == -* ]] ; then
21
+ export DICT_SYM_COMP_OPTIONS=${DICT_SYM_COMP_OPTIONS:-$(sym --dictionary | sed -E 's/ /\n/g')}
22
+ SYM_COMP_OPTIONS=${DICT_SYM_COMP_OPTIONS}
22
23
  else
23
- SYM_COMP_OPTIONS=$(sym -h | egrep ' \-' | grep -v '^ --' | cut -d ',' -f 1)
24
+ SYM_COMP_OPTIONS="$(find . -type f -depth 1 -name "${prev}*" | sed 's#^.\/##g')"
24
25
  fi
25
26
 
26
27
  COMPREPLY=( $(compgen -W "${SYM_COMP_OPTIONS}" -- ${cur}) )
data/lib/sym.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'colored2'
2
2
  require 'zlib'
3
- require 'coin'
3
+ require 'logger'
4
4
 
5
5
  require_relative 'sym/configuration'
6
6
 
@@ -10,8 +10,29 @@ Sym::Configuration.configure do |config|
10
10
  config.private_key_cipher = config.data_cipher
11
11
  config.compression_enabled = true
12
12
  config.compression_level = Zlib::BEST_COMPRESSION
13
+
14
+ config.password_cache_timeout = 300
15
+
16
+ # When nil is selected, providers are auto-detected.
17
+ config.password_cache_default_provider = nil
18
+ config.password_cache_arguments = {
19
+ drb: {
20
+ opts: {
21
+ uri: 'druby://127.0.0.1:24924'
22
+ }
23
+ },
24
+ memcached: {
25
+ args: %w(127.0.0.1:11211),
26
+ opts: { namespace: 'sym',
27
+ compress: true,
28
+ expires_in: config.password_cache_timeout
29
+ }
30
+
31
+ }
32
+ }
13
33
  end
14
34
 
35
+
15
36
  #
16
37
  # == Using Sym Library
17
38
  #
@@ -117,5 +138,7 @@ module Sym
117
138
  file: File.expand_path('../../bin/sym.completion', __FILE__),
118
139
  script: "[[ -f '#{COMPLETION_PATH}' ]] && source '#{COMPLETION_PATH}'",
119
140
  }
141
+
142
+ LOGGER = Logger.new(nil) # empty logger
120
143
  end
121
144
 
data/lib/sym/app/cli.rb CHANGED
@@ -14,6 +14,7 @@ require 'highline'
14
14
  require_relative 'output/file'
15
15
  require_relative 'output/file'
16
16
  require_relative 'output/stdout'
17
+ require_relative 'cli_slop'
17
18
 
18
19
  module Sym
19
20
  module App
@@ -51,38 +52,33 @@ module Sym
51
52
  # in a cross-platform way inside the {Sym::App::Keychain} module.
52
53
 
53
54
  class CLI
55
+ # brings in #parse(Array[String] args)
56
+ include CLISlop
54
57
 
55
58
  extend Forwardable
56
-
57
59
  def_delegators :@application, :command
58
60
 
59
61
  attr_accessor :opts, :application, :outputs, :output_proc
60
62
 
61
- def initialize(argv)
63
+ def initialize(argv_original)
62
64
  begin
63
- argv_copy = argv.dup
64
- dict = false
65
- if argv_copy.include?('--dictionary')
66
- dict = true
67
- argv_copy.delete('--dictionary')
68
- end
69
- self.opts = parse(argv_copy)
70
- if dict
71
- options = opts.parser.unused_options + opts.parser.used_options
72
- puts options.map { |o| o.to_s.gsub(/.*(--[\w-]+).*/, '\1') }.sort.join(' ')
73
- exit 0
74
- end
65
+ argv = argv_original.dup
66
+ dict = argv.delete('--dictionary')
67
+ self.opts = parse(argv)
68
+ command_dictionary if dict
75
69
  rescue StandardError => e
76
70
  error exception: e
77
71
  return
78
72
  end
79
73
 
80
- configure_color(argv)
74
+ command_no_color(argv_original) if opts[:no_color]
81
75
 
82
76
  self.application = ::Sym::Application.new(opts)
77
+
83
78
  select_output_stream
84
79
  end
85
80
 
81
+
86
82
  def execute
87
83
  return Sym::App.exit_code if Sym::App.exit_code != 0
88
84
 
@@ -97,90 +93,31 @@ module Sym
97
93
 
98
94
  private
99
95
 
96
+ def command_dictionary
97
+ options = opts.parser.unused_options + opts.parser.used_options
98
+ puts options.map(&:to_s).sort.map { |o| "-#{o[1]}" }.join(' ')
99
+ exit 0
100
+ end
101
+
100
102
  def error(hash)
101
103
  Sym::App.error(hash.merge(config: (opts ? opts.to_hash : {})))
102
104
  end
103
105
 
104
106
  def select_output_stream
105
107
  output_klass = application.args.output_class
106
-
107
108
  unless output_klass && output_klass.is_a?(Class)
108
109
  raise "Can not determine output class from arguments #{opts.to_hash}"
109
110
  end
110
-
111
111
  self.output_proc = output_klass.new(self).output_proc
112
112
  end
113
113
 
114
- def configure_color(argv)
115
- if opts[:no_color]
116
- Colored2.disable! # reparse options without the colors to create new help msg
117
- self.opts = parse(argv.dup)
118
- end
114
+ def command_no_color(argv)
115
+ Colored2.disable! # reparse options without the colors to create new help msg
116
+ self.opts = parse(argv.dup)
119
117
  end
120
118
 
121
- def parse(arguments)
122
- Slop.parse(arguments) do |o|
123
- o.banner = "Sym (#{Sym::VERSION}) – encrypt/decrypt data with a private key\n".bold.white
124
- o.separator 'Usage:'.yellow
125
- o.separator ' # Generate a new key:'.dark
126
- o.separator ' sym -g '.green.bold + '[ -p ] [ -x keychain ] [ -o keyfile | -q | ] '.green
127
- o.separator ''
128
- o.separator ' # Encrypt/Decrypt '.dark
129
- o.separator ' sym [ -d | -e ] '.green.bold + '[ -f <file> | -s <string> ] '.green
130
- o.separator ' [ -k key | -K keyfile | -x keychain | -i ] '.green
131
- o.separator ' [ -o <output file> ] '.green
132
- o.separator ' '
133
- o.separator ' # Edit an encrypted file in $EDITOR '.dark
134
- o.separator ' sym -t -f <file> [ -b ]'.green.bold + '[ -k key | -K keyfile | -x keychain | -i ] '.green
135
-
136
- o.separator ' '
137
- o.separator 'Modes:'.yellow
138
-
139
- o.bool '-e', '--encrypt', ' encrypt mode'
140
- o.bool '-d', '--decrypt', ' decrypt mode'
141
- o.bool '-t', '--edit', ' decrypt, open an encr. file in an $EDITOR'
142
-
143
- o.separator ' '
144
- o.separator 'Create a new private key:'.yellow
145
-
146
- o.bool '-g', '--generate', ' generate a new private key'
147
- o.bool '-p', '--password', ' encrypt the key with a password'
148
-
149
- if Sym::App.is_osx?
150
- o.string '-x', '--keychain', '[key-name] '.blue + 'add to (or read from) the OS-X Keychain'
151
- end
152
-
153
- o.integer '-M', '--password-timeout', '[timeout]'.blue + ' when passwords expire (in seconds)'
154
- o.bool '-P', '--no-password-cache', ' disables caching of key passwords'
155
-
156
- o.separator ' '
157
- o.separator 'Read existing private key from:'.yellow
158
- o.bool '-i', '--interactive', ' Paste or type the key interactively'
159
- o.string '-k', '--private-key', '[key] '.blue + ' private key as a string'
160
- o.string '-K', '--keyfile', '[key-file]'.blue + ' private key from a file'
161
- o.separator ' '
162
- o.separator 'Data to Encrypt/Decrypt:'.yellow
163
- o.string '-s', '--string', '[string]'.blue + ' specify a string to encrypt/decrypt'
164
- o.string '-f', '--file', '[file] '.blue + ' filename to read from'
165
- o.string '-o', '--output', '[file] '.blue + ' filename to write to'
166
- o.separator ' '
167
- o.separator 'Flags:'.yellow
168
- o.bool '-b', '--backup', ' create a backup file in the edit mode'
169
- o.bool '-v', '--verbose', ' show additional information'
170
- o.bool '-T', '--trace', ' print a backtrace of any errors'
171
- o.bool '-D', '--debug', ' print debugging information'
172
- o.bool '-q', '--quiet', ' silence all output'
173
- o.bool '-V', '--version', ' print library version'
174
- o.bool '-N', '--no-color', ' disable color output'
175
- o.separator ' '
176
- o.separator 'Utility:'.yellow
177
- o.string '-a', '--bash-completion', '[file]'.blue + ' append shell completion to a file'
178
- o.separator ' '
179
- o.separator 'Help & Examples:'.yellow
180
- o.bool '-E', '--examples', ' show several examples'
181
- o.bool '-h', '--help', ' show help'
182
-
183
- end
119
+ def key_spec
120
+ '<key-spec>'.bold.magenta
184
121
  end
185
122
  end
186
123
  end
@@ -0,0 +1,77 @@
1
+ module Sym
2
+ module App
3
+ module CLISlop
4
+ def parse(arguments)
5
+ Slop.parse(arguments) do |o|
6
+
7
+ o.banner = "Sym (#{Sym::VERSION}) – encrypt/decrypt data with a private key\n".bold.white
8
+ o.separator 'Usage:'.yellow
9
+ o.separator ' # Generate a new key:'.dark
10
+ o.separator ' sym -g '.green.bold + '[ -p ] [ -x keychain | -o keyfile | -q | ] '.green
11
+ o.separator ''
12
+ o.separator ' # To specify a key for an operation use any one of:'.dark
13
+ o.separator ' ' + key_spec + ' = -k key | -K file | -x keychain | -i '.green.bold
14
+ o.separator ''
15
+ o.separator ' # Encrypt/Decrypt to STDOUT or output file '.dark
16
+ o.separator ' sym -e '.green.bold + key_spec + ' [-f <file> | -s <string>] [-o <file>] '.green
17
+ o.separator ' sym -d '.green.bold + key_spec + ' [-f <file> | -s <string>] [-o <file>] '.green
18
+ o.separator ' '
19
+ o.separator ' # Edit an encrypted file in $EDITOR '.dark
20
+ o.separator ' sym -t '.green.bold + key_spec + ' -f <file> [ -b ]'.green.bold
21
+
22
+ o.separator ' '
23
+ o.separator 'Modes:'.yellow
24
+ o.bool '-e', '--encrypt', ' encrypt mode'
25
+ o.bool '-d', '--decrypt', ' decrypt mode'
26
+ o.bool '-t', '--edit', ' edit encrypted file in an $EDITOR'
27
+
28
+ o.separator ' '
29
+ o.separator 'Create a new private key:'.yellow
30
+ o.bool '-g', '--generate', ' generate a new private key'
31
+ o.bool '-p', '--password', ' encrypt the key with a password'
32
+
33
+ o.separator ' '
34
+ o.separator 'Read existing private key from:'.yellow
35
+ o.string '-k', '--private-key', '[key] '.blue + ' private key (or key file)'
36
+ o.string '-K', '--keyfile', '[key-file]'.blue + ' private key from a file'
37
+ if Sym::App.is_osx?
38
+ o.string '-x', '--keychain', '[key-name] '.blue + 'add to (or read from) the OS-X Keychain'
39
+ end
40
+ o.bool '-i', '--interactive', ' Paste or type the key interactively'
41
+
42
+ o.separator ' '
43
+ o.separator 'Password Cache:'.yellow
44
+ o.bool '-C', '--cache-password', ' enable the cache (off by default)'
45
+ o.integer '-T', '--cache-for', '[seconds]'.blue + ' to cache the password for'
46
+ o.string '-P', '--cache-provider', '[provider]'.blue + ' type of cache, one of: ' + "\n\t\t\t\t " +
47
+ "[ #{Sym::App::Password::Providers.registry.keys.map(&:to_s).join(', ').blue.bold} ]"
48
+
49
+ o.separator ' '
50
+ o.separator 'Data to Encrypt/Decrypt:'.yellow
51
+ o.string '-s', '--string', '[string]'.blue + ' specify a string to encrypt/decrypt'
52
+ o.string '-f', '--file', '[file] '.blue + ' filename to read from'
53
+ o.string '-o', '--output', '[file] '.blue + ' filename to write to'
54
+
55
+ o.separator ' '
56
+ o.separator 'Flags:'.yellow
57
+ o.bool '-b', '--backup', ' create a backup file in the edit mode'
58
+ o.bool '-v', '--verbose', ' show additional information'
59
+ o.bool '-A', '--trace', ' print a backtrace of any errors'
60
+ o.bool '-D', '--debug', ' print debugging information'
61
+ o.bool '-q', '--quiet', ' do not print to STDOUT'
62
+ o.bool '-V', '--version', ' print library version'
63
+ o.bool '-N', '--no-color', ' disable color output'
64
+
65
+ o.separator ' '
66
+ o.separator 'Utility:'.yellow
67
+ o.string '-a', '--bash-completion', '[file]'.blue + ' append shell completion to a file'
68
+
69
+ o.separator ' '
70
+ o.separator 'Help & Examples:'.yellow
71
+ o.bool '-E', '--examples', ' show several examples'
72
+ o.bool '-h', '--help', ' show help'
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -5,7 +5,6 @@ module Sym
5
5
  class ShowHelp < BaseCommand
6
6
 
7
7
  required_options :help, ->(opts) { opts.to_hash.keys.all? { |k| !opts[k] } }
8
- try_after :generate_key, :open_editor, :encrypt_decrypt
9
8
 
10
9
  def execute
11
10
  opts.to_s(prefix: ' ' * 2)
@@ -93,7 +93,7 @@ end
93
93
 
94
94
 
95
95
  #
96
- # Usage: add-generic-password [-a account] [-s service] [-w password] [options...] [-A|-T appPath] [keychain]
96
+ # Usage: add-generic-password [-a account] [-s service] [-w password] [options...] [-A|--trace appPath] [keychain]
97
97
  # -a Specify account name (required)
98
98
  # -c Specify item creator (optional four-character code)
99
99
  # -C Specify item type (optional four-character code)
@@ -105,7 +105,7 @@ end
105
105
  # -p Specify password to be added (legacy option, equivalent to -w)
106
106
  # -w Specify password to be added
107
107
  # -A Allow any application to access this item without warning (insecure, not recommended!)
108
- # -T Specify an application which may access this item (multiple -T options are allowed)
108
+ # --trace Specify an application which may access this item (multiple --trace options are allowed)
109
109
  # -U Update item if it already exists (if omitted, the item cannot already exist)
110
110
  #
111
111
  # Usage: find-generic-password [-a account] [-s service] [options...] [-g] [keychain...]
@@ -1,62 +1,85 @@
1
- require 'coin'
2
1
  require 'digest'
3
2
  require 'singleton'
4
3
  require 'colored2'
4
+ require 'timeout'
5
+ require 'sym/extensions/with_retry'
6
+ require 'sym/extensions/with_timeout'
7
+ require 'sym/configuration'
8
+ require 'sym/app/password/providers'
5
9
 
6
10
  module Sym
7
11
  module App
8
12
  module Password
13
+
14
+ # +Provider+ is the primary implementation of the underlying cache.
15
+ # It should support the following API:
16
+ #
17
+ # def initialize(*args, **opts, &block)
18
+ # end
19
+ #
20
+ # def read(key)
21
+ # end
22
+ #
23
+ # def write(key, value, expire_timeout_seconds)
24
+ # end
25
+ #
26
+ # it must be intantiatable via #new
9
27
  class Cache
10
- URI = 'druby://127.0.0.1:24924'
11
- DEFAULT_TIMEOUT = 300
12
28
 
13
29
  include Singleton
30
+ include Sym::Extensions::WithRetry
31
+ include Sym::Extensions::WithTimeout
14
32
 
15
- attr_accessor :provider, :enabled, :timeout
16
-
17
- def configure(provider: Coin, enabled: true, timeout: DEFAULT_TIMEOUT)
18
- Coin.uri = URI if provider == Coin
19
-
20
- self.provider = provider
21
- self.enabled = enabled
22
- self.timeout = timeout
33
+ attr_accessor :provider, :enabled, :timeout, :verbose
23
34
 
35
+ def configure(**opts)
36
+ self.enabled = opts[:enabled]
37
+ self.verbose = opts[:verbose]
38
+ self.timeout = opts[:timeout] || ::Sym::Configuration.config.password_cache_timeout
39
+ self.provider = Providers.provider(opts[:provider])
40
+ self.enabled = false unless self.provider
24
41
  self
25
42
  end
26
43
 
27
- TRIES = 2
28
-
29
- def operation
30
- retries ||= TRIES
31
- yield if self.enabled
32
- rescue StandardError => e
33
- if retries == TRIES && Coin.remote_uri.nil?
34
- Coin.remote_uri = URI if provider == Coin
35
- retries -= 1
36
- retry
37
- end
38
- puts 'WARNING: error reading from DRB server: ' + e.message.red
39
- nil
40
- end
41
-
42
-
43
- def [] (key)
44
+ def [](key)
44
45
  cache = self
45
- operation do
46
- cache.provider.read(cache.md5(key))
47
- end
46
+ operation { cache.provider.read(cache.md5(key)) }
48
47
  end
49
48
 
50
49
  def []=(key, value)
51
50
  cache = self
52
- operation do
53
- cache.provider.write(cache.md5(key), value, cache.timeout)
54
- end
51
+ operation { cache.provider.write(cache.md5(key), value, cache.timeout) }
55
52
  end
56
53
 
57
54
  def md5(string)
58
55
  Digest::MD5.base64digest(string)
59
56
  end
57
+
58
+ private
59
+
60
+ def operation
61
+ return nil unless self.enabled
62
+ with_timeout(1) do
63
+ with_retry do
64
+ yield if block_given?
65
+ end
66
+ end
67
+ rescue Timeout::Error => e
68
+ error(nil, 'Password cache server timed out...')
69
+ rescue StandardError => e
70
+ error(e, 'Error connecting to password caching server...')
71
+ end
72
+
73
+ def error(exception = nil, message = nil)
74
+ if self.verbose
75
+ print 'WARNING: '
76
+ print message ? message.yellow : ''
77
+ print exception ? exception.message.red : ''
78
+ puts
79
+ end
80
+ self.enabled = false
81
+ nil
82
+ end
60
83
  end
61
84
  end
62
85
  end
@@ -0,0 +1,52 @@
1
+ module Sym
2
+ module App
3
+ module Password
4
+ module Providers
5
+
6
+ class << self
7
+ attr_accessor :registry
8
+ attr_accessor :providers
9
+ attr_accessor :detected
10
+
11
+ def register(provider_class)
12
+ self.registry ||= {}
13
+ registry[short_name(provider_class)] = provider_class
14
+ self.providers ||= []
15
+ self.providers << provider_class
16
+ end
17
+
18
+ # Detect first instance that is "alive?" and return it.
19
+ def detect
20
+ self.detected ||= self.providers.inject(nil) do |instance, provider_class|
21
+ instance || (p = provider_class.new; p.alive? ? p : nil)
22
+ end
23
+ end
24
+
25
+ def provider(p = nil)
26
+ provider_from_argument(p) || detect
27
+ end
28
+
29
+ private
30
+
31
+ def short_name(klass)
32
+ klass.name.gsub(/.*::(\w+)Provider/, '\1').downcase.to_sym
33
+ end
34
+
35
+ def provider_from_argument(p)
36
+ case p
37
+ when String, Symbol
38
+ provider_class_name = "#{p.to_s.capitalize}Provider"
39
+ Sym::App::Password::Providers.const_defined?(provider_class_name) ?
40
+ Sym::App::Password::Providers.const_get(provider_class_name).new :
41
+ nil
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ # Order is important — they are tried in this order for auto detect
51
+ require 'sym/app/password/providers/memcached_provider'
52
+ require 'sym/app/password/providers/drb_provider'
@@ -0,0 +1,35 @@
1
+ require 'coin'
2
+ require 'sym/app/password/providers'
3
+
4
+ module Sym
5
+ module App
6
+ module Password
7
+ module Providers
8
+ class DrbProvider
9
+
10
+ attr_accessor :coin
11
+
12
+ def initialize
13
+ Coin.uri = Sym::Configuration.config.password_cache_arguments[:drb][:opts][:uri]
14
+ self.coin = Coin
15
+ end
16
+
17
+ def alive?
18
+ self.read('bogus') rescue nil
19
+ self.coin.server_running?
20
+ end
21
+
22
+ def write(*args)
23
+ coin.server.send(:write, *args)
24
+ end
25
+
26
+ def read(*args)
27
+ coin.send(:read, *args)
28
+ end
29
+ end
30
+
31
+ register DrbProvider
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,42 @@
1
+ require 'forwardable'
2
+ require 'dalli'
3
+ require 'sym/app/password/cache'
4
+
5
+ module Sym
6
+ module App
7
+ module Password
8
+ module Providers
9
+ class MemcachedProvider
10
+ attr_accessor :dalli
11
+
12
+ def initialize
13
+ # disable logging
14
+ Dalli.logger = Sym::LOGGER
15
+ self.dalli = ::Dalli::Client.new(
16
+ * Sym::Configuration.config.password_cache_arguments[:memcached][:args],
17
+ ** Sym::Configuration.config.password_cache_arguments[:memcached][:opts]
18
+ )
19
+ end
20
+
21
+ def alive?
22
+ dalli.alive!
23
+ true
24
+ rescue Dalli::RingError => e
25
+ false
26
+ end
27
+
28
+ def read(key)
29
+ dalli.get(key)
30
+ end
31
+
32
+ def write(key, value, *)
33
+ dalli.set(key, value)
34
+ end
35
+
36
+ end
37
+
38
+ register MemcachedProvider
39
+ end
40
+ end
41
+ end
42
+ end
@@ -103,9 +103,10 @@ module Sym
103
103
 
104
104
  def initialize_password_cache
105
105
  args = {}
106
- args[:provider] = Coin
107
- args[:timeout] = opts[:password_timeout].to_i if opts[:password_timeout]
108
- args[:enabled] = false if opts[:no_password_cache]
106
+ args[:timeout] = opts[:cache_for].to_i if opts[:cache_for]
107
+ args[:enabled] = opts[:cache_password]
108
+ args[:verbose] = opts[:verbose]
109
+ args[:provider] = opts[:cache_provider] if opts[:cache_provider]
109
110
 
110
111
  self.password_cache = Sym::App::Password::Cache.instance.configure(args)
111
112
  end
@@ -33,7 +33,11 @@ module Sym
33
33
  end
34
34
  end
35
35
 
36
+ # See file +lib/sym.rb+ where these values are defined.
37
+
36
38
  attr_accessor :data_cipher, :password_cipher, :private_key_cipher
37
39
  attr_accessor :compression_enabled, :compression_level
40
+ attr_accessor :password_cache_default_provider, :password_cache_timeout
41
+ attr_accessor :password_cache_arguments
38
42
  end
39
43
  end
@@ -0,0 +1,17 @@
1
+ module Sym
2
+ module Extensions
3
+ module WithRetry
4
+
5
+ def with_retry(retries: 3, fail_block: nil, &block)
6
+ attempts = 0
7
+ yield if block_given?
8
+ rescue StandardError => e
9
+ raise(e) if attempts >= retries
10
+ fail_block.call if fail_block
11
+ attempts += 1
12
+ retry
13
+ end
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,14 @@
1
+ module Sym
2
+ module Extensions
3
+ module WithTimeout
4
+
5
+ def with_timeout(timeout = 3)
6
+ status = Timeout::timeout(timeout) {
7
+ yield if block_given?
8
+ }
9
+ end
10
+
11
+ end
12
+ end
13
+ end
14
+
data/lib/sym/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Sym
2
- VERSION = '2.1.2'
2
+ VERSION = '2.2.0'
3
3
  DESCRIPTION = <<-eof
4
4
  Sym is a command line utility and a Ruby API that makes it trivial to encrypt and decrypt
5
5
  sensitive data. Unlike many other existing encryption tools, sym focuses on usability and
data/sym.gemspec CHANGED
@@ -38,6 +38,7 @@ EOF
38
38
  spec.add_dependency 'activesupport'
39
39
  spec.add_dependency 'highline', '~> 1.7'
40
40
  spec.add_dependency 'coin', '~> 0.1.8'
41
+ spec.add_dependency 'dalli', '~> 2.7'
41
42
 
42
43
  spec.add_development_dependency 'codeclimate-test-reporter', '~> 1.0'
43
44
  spec.add_development_dependency 'simplecov'
@@ -45,5 +46,6 @@ EOF
45
46
  spec.add_development_dependency 'bundler', '~> 1'
46
47
  spec.add_development_dependency 'rake'
47
48
  spec.add_development_dependency 'rspec', '~> 3'
49
+ spec.add_development_dependency 'rspec-its'
48
50
  spec.add_development_dependency 'yard'
49
51
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sym
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.2
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Gredeskoul
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-02-11 00:00:00.000000000 Z
11
+ date: 2017-02-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colored2
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: 0.1.8
83
+ - !ruby/object:Gem::Dependency
84
+ name: dalli
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.7'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.7'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: codeclimate-test-reporter
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -164,6 +178,20 @@ dependencies:
164
178
  - - "~>"
165
179
  - !ruby/object:Gem::Version
166
180
  version: '3'
181
+ - !ruby/object:Gem::Dependency
182
+ name: rspec-its
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
167
195
  - !ruby/object:Gem::Dependency
168
196
  name: yard
169
197
  requirement: !ruby/object:Gem::Requirement
@@ -217,6 +245,7 @@ files:
217
245
  - lib/sym/app.rb
218
246
  - lib/sym/app/args.rb
219
247
  - lib/sym/app/cli.rb
248
+ - lib/sym/app/cli_slop.rb
220
249
  - lib/sym/app/commands.rb
221
250
  - lib/sym/app/commands/base_command.rb
222
251
  - lib/sym/app/commands/bash_completion.rb
@@ -237,6 +266,9 @@ files:
237
266
  - lib/sym/app/output/noop.rb
238
267
  - lib/sym/app/output/stdout.rb
239
268
  - lib/sym/app/password/cache.rb
269
+ - lib/sym/app/password/providers.rb
270
+ - lib/sym/app/password/providers/drb_provider.rb
271
+ - lib/sym/app/password/providers/memcached_provider.rb
240
272
  - lib/sym/app/private_key/base64_decoder.rb
241
273
  - lib/sym/app/private_key/decryptor.rb
242
274
  - lib/sym/app/private_key/detector.rb
@@ -253,6 +285,8 @@ files:
253
285
  - lib/sym/errors.rb
254
286
  - lib/sym/extensions/class_methods.rb
255
287
  - lib/sym/extensions/instance_methods.rb
288
+ - lib/sym/extensions/with_retry.rb
289
+ - lib/sym/extensions/with_timeout.rb
256
290
  - lib/sym/version.rb
257
291
  - sym-3.0-cli.md
258
292
  - sym.gemspec