arcanus 0.12.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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