arcanus 0.12.1 → 1.0.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: 7cdb8a1dbb476090b55baecf9a5dde6a7b2b6c5a
4
- data.tar.gz: 0f7ba0190d7f34c2a6cf5632bff11820e035ee17
3
+ metadata.gz: 0f7f318dc67cf83cf9a258a8f73db5f8f8055530
4
+ data.tar.gz: 051293634b012a4d84b09f03e55ac9a041853f1b
5
5
  SHA512:
6
- metadata.gz: 13ff94bf3267c65bc210e9a7290a2d589c6243cab79a11eebd6bdeb921e1b2d41d946205852a08db29407e7d5cd57fa835acaa8371c1240a42667a392e124197
7
- data.tar.gz: 515a1daa9fccd92f4f44a383c8b98d699b8534fc76cac637b66dc7d6e728b000f77ea2460adba41f16725c9ac85091798feb9bd8a22c3c5cf6644b49a9142f4c
6
+ metadata.gz: f8aef9c0613d3849b8dfef3c8c585f7fe16c91c3647c1670784029819fe1d31005f3574b882a0b30f4f7faaa505a2171218bf27742decf5094c74690df49b795
7
+ data.tar.gz: 6c468609ff2cc18b8fa9d4b4cce904c662df53c188dc2f172ec3d3c1c2b15942221f0c5476c8e2ceea3296006a538634b4f93a09475d61e9f5d9f2fca8a93196
@@ -1,7 +1,6 @@
1
1
  require 'arcanus/constants'
2
2
  require 'arcanus/errors'
3
3
  require 'arcanus/error_handler'
4
- require 'arcanus/configuration'
5
4
  require 'arcanus/input'
6
5
  require 'arcanus/output'
7
6
  require 'arcanus/ui'
@@ -27,9 +26,8 @@ module Arcanus
27
26
  # Loads Arcanus chest and decrypts secrets.
28
27
  #
29
28
  # @param directory [String] repo directory
30
- def load(directory = Dir.pwd)
31
- @config = Configuration.load_applicable(directory)
32
- @repo = Repo.new(@config)
29
+ def load
30
+ @repo = Repo.new
33
31
 
34
32
  unless File.directory?(@repo.arcanus_dir)
35
33
  raise Errors::UsageError,
@@ -7,6 +7,7 @@ module Arcanus
7
7
  # Encapsulates the collection of encrypted secrets managed by Arcanus.
8
8
  class Chest # rubocop:disable Metrics/ClassLength
9
9
  SIGNATURE_SIZE_BITS = 256
10
+ ENCRYPTION_CIPHER = OpenSSL::Cipher.new('AES-256-CBC')
10
11
 
11
12
  def initialize(key:, chest_file_path:)
12
13
  @key = key
@@ -157,7 +158,14 @@ module Arcanus
157
158
 
158
159
  def encrypt_value(value)
159
160
  dumped_value = Marshal.dump(value)
160
- encrypted_value = Base64.encode64(@key.encrypt(dumped_value))
161
+
162
+ # Create a random symmetric key so we can encrypt plaintext of arbitrary length
163
+ sym_key = ENCRYPTION_CIPHER.reset.encrypt.random_key
164
+ iv = Base64.encode64(ENCRYPTION_CIPHER.random_iv)
165
+ enc_sym_key = Base64.encode64(@key.encrypt(sym_key))
166
+ encrypted_value = Base64.encode64(ENCRYPTION_CIPHER.update(dumped_value) +
167
+ ENCRYPTION_CIPHER.final)
168
+
161
169
  salt = SecureRandom.hex(8)
162
170
 
163
171
  signature = Digest::SHA2.new(SIGNATURE_SIZE_BITS).tap do |digest|
@@ -165,23 +173,31 @@ module Arcanus
165
173
  digest << dumped_value
166
174
  end.to_s
167
175
 
168
- "#{encrypted_value}:#{salt}:#{signature}"
176
+ "#{iv}:#{enc_sym_key}:#{encrypted_value}:#{salt}:#{signature}"
169
177
  end
170
178
 
171
- def decrypt_value(blob)
179
+ def decrypt_value(blob) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
172
180
  unless blob.is_a?(String)
173
181
  raise Errors::DecryptionError,
174
182
  "Expecting an encrypted blob but got '#{blob}'"
175
183
  end
176
184
 
177
- encrypted_value, salt, signature = blob.split(':')
185
+ iv_b64, enc_sym_key_b64, encrypted_value_b64, salt, signature = blob.split(':')
178
186
 
179
- if signature.nil? || salt.nil? || encrypted_value.nil?
187
+ if signature.nil? || salt.nil? || encrypted_value_b64.nil? ||
188
+ enc_sym_key_b64.nil? || iv_b64.nil?
180
189
  raise Errors::DecryptionError,
181
- "Invalid blob format '#{blob}' (must be of the form 'signature:salt:ciphertext')"
190
+ "Invalid blob format '#{blob}'. " \
191
+ 'Did you encrypt this with an older version of Arcanus?'
182
192
  end
183
193
 
184
- dumped_value = @key.decrypt(Base64.decode64(encrypted_value))
194
+ iv = Base64.decode64(iv_b64)
195
+ sym_key = @key.decrypt(Base64.decode64(enc_sym_key_b64))
196
+ ENCRYPTION_CIPHER.reset.decrypt
197
+ ENCRYPTION_CIPHER.iv = iv
198
+ ENCRYPTION_CIPHER.key = sym_key
199
+ dumped_value = ENCRYPTION_CIPHER.update(Base64.decode64(encrypted_value_b64)) +
200
+ ENCRYPTION_CIPHER.final
185
201
 
186
202
  actual_signature = Digest::SHA2.new(SIGNATURE_SIZE_BITS).tap do |digest|
187
203
  digest << salt
@@ -28,8 +28,7 @@ module Arcanus
28
28
  # @param [Array<String>] arguments
29
29
  # @return [Integer] exit status code
30
30
  def run(arguments)
31
- config = Configuration.load_applicable
32
- run_command(config, arguments)
31
+ run_command(arguments)
33
32
 
34
33
  ExitCodes::OK
35
34
  rescue => ex
@@ -40,15 +39,14 @@ module Arcanus
40
39
 
41
40
  # Executes the appropriate command given the list of command line arguments.
42
41
  #
43
- # @param config [Arcanus::Configuration]
44
42
  # @param ui [Arcanus::UI]
45
43
  # @param arguments [Array<String>]
46
44
  # @raise [Arcanus::Errors::ArcanusError] when any exceptional circumstance occurs
47
- def run_command(config, arguments)
45
+ def run_command(arguments)
48
46
  arguments = convert_arguments(arguments)
49
47
 
50
48
  require 'arcanus/command/base'
51
- Command::Base.from_arguments(config, @ui, arguments).run
49
+ Command::Base.from_arguments(@ui, arguments).run
52
50
  end
53
51
 
54
52
  def convert_arguments(arguments)
@@ -8,12 +8,11 @@ module Arcanus::Command
8
8
  class << self
9
9
  # Create a command from a list of arguments.
10
10
  #
11
- # @param config [Arcanus::Configuration]
12
11
  # @param ui [Arcanus::UI]
13
12
  # @param arguments [Array<String>]
14
13
  # @return [Arcanus::Command::Base] appropriate command for the given
15
14
  # arguments
16
- def from_arguments(config, ui, arguments)
15
+ def from_arguments(ui, arguments)
17
16
  cmd = arguments.first
18
17
 
19
18
  begin
@@ -23,7 +22,7 @@ module Arcanus::Command
23
22
  "`arcanus #{cmd}` is not a valid command"
24
23
  end
25
24
 
26
- Arcanus::Command.const_get(Arcanus::Utils.camel_case(cmd)).new(config, ui, arguments)
25
+ Arcanus::Command.const_get(Arcanus::Utils.camel_case(cmd)).new(ui, arguments)
27
26
  end
28
27
 
29
28
  def description(desc = nil)
@@ -36,11 +35,9 @@ module Arcanus::Command
36
35
  end
37
36
  end
38
37
 
39
- # @param config [Arcanus::Configuration]
40
38
  # @param ui [Arcanus::UI]
41
39
  # @param arguments [Array<String>]
42
- def initialize(config, ui, arguments)
43
- @config = config
40
+ def initialize(ui, arguments)
44
41
  @ui = ui
45
42
  @arguments = arguments
46
43
  end
@@ -61,7 +58,7 @@ module Arcanus::Command
61
58
  #
62
59
  # @param command_arguments [Array<String>]
63
60
  def execute_command(command_arguments)
64
- self.class.from_arguments(config, ui, command_arguments).execute
61
+ self.class.from_arguments(ui, command_arguments).execute
65
62
  end
66
63
 
67
64
  private
@@ -69,9 +66,6 @@ module Arcanus::Command
69
66
  # @return [Array<String>]
70
67
  attr_reader :arguments
71
68
 
72
- # @return [Arcanus::Configuration]
73
- attr_reader :config
74
-
75
69
  # @return [Arcanus::UI]
76
70
  attr_reader :ui
77
71
 
@@ -79,7 +73,7 @@ module Arcanus::Command
79
73
  #
80
74
  # @return [Arcanus::Repo]
81
75
  def repo
82
- @repo ||= Arcanus::Repo.new(@config)
76
+ @repo ||= Arcanus::Repo.new
83
77
  end
84
78
 
85
79
  # Execute a process and return the result including status and output.
@@ -4,7 +4,7 @@ module Arcanus
4
4
  # Encapsulates operations for creating keys that encrypt/decrypt secrets.
5
5
  class Key
6
6
  DEFAULT_SIZE = 4096
7
- PASSWORD_CIPHER = OpenSSL::Cipher.new('AES-256-CBC')
7
+ PEM_PASSWORD_CIPHER = OpenSSL::Cipher.new('AES-256-CBC')
8
8
 
9
9
  class << self
10
10
  def generate(key_size_bits: DEFAULT_SIZE)
@@ -36,7 +36,7 @@ module Arcanus
36
36
  def save(key_file_path:, password: nil)
37
37
  pem =
38
38
  if password
39
- @key.to_pem(PASSWORD_CIPHER, password)
39
+ @key.to_pem(PEM_PASSWORD_CIPHER, password)
40
40
  else
41
41
  @key.to_pem
42
42
  end
@@ -3,11 +3,6 @@ require 'pathname'
3
3
  module Arcanus
4
4
  # Exposes information about the current git repository.
5
5
  class Repo
6
- # @param config [Arcanus::Configuration]
7
- def initialize(config)
8
- @config = config
9
- end
10
-
11
6
  # Returns the absolute path to the root of the current repository the
12
7
  # current working directory resides within.
13
8
  #
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Defines the gem version.
4
4
  module Arcanus
5
- VERSION = '0.12.1'.freeze
5
+ VERSION = '1.0.0'.freeze
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: arcanus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shane da Silva
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-08 00:00:00.000000000 Z
11
+ date: 2016-11-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: childprocess
@@ -74,7 +74,6 @@ files:
74
74
  - lib/arcanus/command/show.rb
75
75
  - lib/arcanus/command/unlock.rb
76
76
  - lib/arcanus/command/version.rb
77
- - lib/arcanus/configuration.rb
78
77
  - lib/arcanus/constants.rb
79
78
  - lib/arcanus/error_handler.rb
80
79
  - lib/arcanus/errors.rb
@@ -1,94 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'pathname'
4
- require 'yaml'
5
-
6
- module Arcanus
7
- # Stores runtime configuration for the application.
8
- #
9
- # This is intended to define helper methods for accessing configuration so
10
- # this logic can be shared amongst the various components of the system.
11
- class Configuration
12
- # Name of the configuration file.
13
- FILE_NAME = 'config.yaml'.freeze
14
-
15
- class << self
16
- # Loads appropriate configuration file given the current working
17
- # directory.
18
- #
19
- # @return [Arcanus::Configuration]
20
- def load_applicable(working_directory = Dir.pwd)
21
- current_directory = File.expand_path(working_directory)
22
- config_file = applicable_config_file(current_directory)
23
-
24
- if config_file
25
- from_file(config_file)
26
- else
27
- # No configuration file, so assume defaults
28
- new({})
29
- end
30
- end
31
-
32
- # Loads a configuration from a file.
33
- #
34
- # @return [Arcanus::Configuration]
35
- def from_file(config_file)
36
- options =
37
- if yaml = YAML.load_file(config_file)
38
- yaml.to_hash
39
- else
40
- {}
41
- end
42
-
43
- new(options)
44
- end
45
-
46
- private
47
-
48
- # Returns the first valid configuration file found, starting from the
49
- # current working directory and ascending to ancestor directories.
50
- #
51
- # @param directory [String]
52
- # @return [String, nil]
53
- def applicable_config_file(directory)
54
- Pathname.new(directory)
55
- .enum_for(:ascend)
56
- .map { |dir| dir + '.arcanus' + FILE_NAME }
57
- .find do |config_file|
58
- config_file if config_file.exist?
59
- end
60
- end
61
- end
62
-
63
- # Creates a configuration from the given options hash.
64
- #
65
- # @param options [Hash]
66
- def initialize(options)
67
- @options = options
68
- end
69
-
70
- # Access the configuration as if it were a hash.
71
- #
72
- # @param key [String, Symbol]
73
- # @return [Array, Hash, Number, String]
74
- def [](key)
75
- @options[key.to_s]
76
- end
77
-
78
- # Access the configuration as if it were a hash.
79
- #
80
- # @param key [String, Symbol]
81
- # @return [Array, Hash, Number, String]
82
- def fetch(key, *args)
83
- @options.fetch(key.to_s, *args)
84
- end
85
-
86
- # Compares this configuration with another.
87
- #
88
- # @param other [HamlLint::Configuration]
89
- # @return [true,false] whether the given configuration is equivalent
90
- def ==(other)
91
- super || @options == other.instance_variable_get('@options')
92
- end
93
- end
94
- end