vault-tree 0.1.0 → 0.3.3

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.
Files changed (97) hide show
  1. data/.gitignore +7 -3
  2. data/CHANGE_LOG.md +15 -0
  3. data/Gemfile.lock +15 -5
  4. data/README.md +12 -15
  5. data/Rakefile +19 -4
  6. data/features/.nav +11 -0
  7. data/features/contracts/asymmetric_vault.feature +23 -0
  8. data/features/contracts/block_chain_key_transfer.feature +34 -0
  9. data/features/contracts/one_two_three.feature +22 -0
  10. data/features/contracts/readme.md +111 -0
  11. data/features/contracts_and_vaults.md +134 -0
  12. data/features/contributing_to_vault_tree.md +42 -0
  13. data/features/decision_tree.md +16 -0
  14. data/features/enforcement_problem.md +20 -0
  15. data/features/exceptions.feature +56 -5
  16. data/features/install_and_usage.md +57 -0
  17. data/features/keywords/assembled_shamir_key.feature +57 -0
  18. data/features/keywords/contents.feature +24 -0
  19. data/features/keywords/decryption_key.feature +10 -0
  20. data/features/keywords/dh_key.feature +56 -0
  21. data/features/keywords/external_data.feature +11 -0
  22. data/features/keywords/generated_shamir_key.feature +55 -0
  23. data/features/keywords/key.feature +38 -0
  24. data/features/keywords/master_passphrase.feature +68 -0
  25. data/features/keywords/public_encryption_key.feature +14 -0
  26. data/features/keywords/random_number.feature +44 -0
  27. data/features/keywords/readme.md +3 -0
  28. data/features/keywords/split_key.feature +54 -0
  29. data/features/keywords/unlocked.feature +51 -0
  30. data/features/manipulating_contracts.md +84 -0
  31. data/features/readme.md +6 -0
  32. data/features/steps/asymmetric_vault.steps.rb +41 -0
  33. data/features/steps/block_chain_key_transfer.steps.rb +43 -0
  34. data/features/steps/core.steps.rb +57 -104
  35. data/features/steps/exceptions.steps.rb +45 -1
  36. data/features/steps/one_two_three.steps.rb +57 -0
  37. data/features/steps/secret_sharing.steps.rb +36 -0
  38. data/features/support/contract_fixtures/asymmetric_vault.0.1.0.json +69 -0
  39. data/{spec/support/fixtures → features/support/contract_fixtures}/blank_simple_test_contract.json +0 -0
  40. data/features/support/contract_fixtures/block_chain_key_transfer.0.1.0.json +59 -0
  41. data/{spec/support/fixtures → features/support/contract_fixtures}/broken_contract.json +0 -0
  42. data/features/support/contract_fixtures/one_two_three.0.7.0.json +108 -0
  43. data/{spec/support/fixtures → features/support/contract_fixtures}/simple_test_contract.json +0 -0
  44. data/features/support/contract_fixtures/template.json +33 -0
  45. data/features/what_is_vault_tree.md +18 -0
  46. data/lib/vault-tree.rb +23 -6
  47. data/lib/vault-tree/contract/close_validator.rb +0 -7
  48. data/lib/vault-tree/contract/contract.rb +13 -2
  49. data/lib/vault-tree/contract/doorman.rb +22 -21
  50. data/lib/vault-tree/contract/vault.rb +18 -2
  51. data/lib/vault-tree/exceptions/exception_template.erb +0 -0
  52. data/lib/vault-tree/exceptions/failed_unlock_attempt.rb +6 -0
  53. data/lib/vault-tree/exceptions/vault_tree_exception.rb +18 -0
  54. data/lib/vault-tree/keywords/assembled_shamir_key.rb +44 -0
  55. data/lib/vault-tree/keywords/{vault_contents.rb → contents.rb} +0 -0
  56. data/lib/vault-tree/keywords/decryption_key.rb +1 -6
  57. data/lib/vault-tree/keywords/{shared_key.rb → dh_key.rb} +2 -2
  58. data/lib/vault-tree/keywords/external_data.rb +19 -0
  59. data/lib/vault-tree/keywords/generated_shamir_key.rb +57 -0
  60. data/lib/vault-tree/keywords/key.rb +13 -0
  61. data/lib/vault-tree/keywords/keyword_interpreter.rb +6 -6
  62. data/lib/vault-tree/keywords/public_encryption_key.rb +1 -5
  63. data/lib/vault-tree/keywords/random_number.rb +1 -1
  64. data/lib/vault-tree/keywords/split_key.rb +19 -0
  65. data/lib/vault-tree/keywords/unlocked.rb +1 -1
  66. data/lib/vault-tree/lock_smith.rb +182 -0
  67. data/lib/vault-tree/lock_smith/assembled_shamir_key.rb +64 -0
  68. data/lib/vault-tree/lock_smith/dh_key_pair.rb +10 -0
  69. data/lib/vault-tree/lock_smith/generated_shamir_key.rb +65 -0
  70. data/lib/vault-tree/lock_smith/split_key.rb +23 -0
  71. data/lib/vault-tree/{config/path_helpers.rb → path_helpers.rb} +26 -2
  72. data/lib/vault-tree/util/json.rb +1 -0
  73. data/lib/vault-tree/{config → util}/string.rb +1 -5
  74. data/lib/vault-tree/version.rb +1 -1
  75. data/spec/assembled_shamir_key_spec.rb +79 -0
  76. data/spec/generated_shamir_key_spec.rb +52 -0
  77. data/spec/lock_smith_spec.rb +90 -0
  78. data/spec/secret_sharing_spec.rb +43 -0
  79. data/support/scripts/libsodium_ubuntu.sh +1 -1
  80. data/vault-tree.gemspec +3 -2
  81. metadata +123 -41
  82. data/features/core.feature +0 -44
  83. data/lib/vault-tree/config/dependencies.rb +0 -4
  84. data/lib/vault-tree/config/lib.rb +0 -2
  85. data/lib/vault-tree/lock_smith/asymmetric_cipher.rb +0 -31
  86. data/lib/vault-tree/lock_smith/crypto_hash.rb +0 -11
  87. data/lib/vault-tree/lock_smith/digital_signature.rb +0 -32
  88. data/lib/vault-tree/lock_smith/encryption_key_pair.rb +0 -25
  89. data/lib/vault-tree/lock_smith/random_number.rb +0 -11
  90. data/lib/vault-tree/lock_smith/shared_key_pair.rb +0 -12
  91. data/lib/vault-tree/lock_smith/signing_key_pair.rb +0 -25
  92. data/lib/vault-tree/lock_smith/symmetric_cipher.rb +0 -25
  93. data/spec/app/locksmith/asymmetric_cipher_spec.rb +0 -25
  94. data/spec/app/locksmith/signing_key_pair_spec.rb +0 -22
  95. data/spec/spec_helper.rb +0 -5
  96. data/spec/support/fixtures/one_two_three-0.5.0.EXP.json +0 -105
  97. data/spec/support/fixtures/reference_contract.1.0.0.json +0 -227
@@ -1,7 +1,24 @@
1
- # require_relative - Ruby > 1.9 method
2
- # require_rel - 'gem require_all' helper method to require all relative
1
+ # *.rb
2
+ require_relative 'vault-tree/path_helpers'
3
+ require_relative 'vault-tree/version'
3
4
 
4
- require_relative 'vault-tree/config/dependencies'
5
- require_relative 'vault-tree/config/path_helpers'
6
- require_relative 'vault-tree/config/string'
7
- require_relative 'vault-tree/config/lib'
5
+ # util/*.rb
6
+ require_relative 'vault-tree/util/json'
7
+ require_relative 'vault-tree/util/string'
8
+
9
+ # lock_smith.rb
10
+ require_relative 'vault-tree/lock_smith'
11
+
12
+ # exceptions/*.rb
13
+ require_relative 'vault-tree/exceptions/vault_tree_exception'
14
+ VaultTree::PathHelpers.exceptions_files.each {|file| require_relative file }
15
+
16
+ # contract/*.rb
17
+ VaultTree::PathHelpers.contract_files.each {|file| require_relative file }
18
+
19
+ # keywords/*.rb
20
+ require_relative 'vault-tree/keywords/keyword'
21
+ VaultTree::PathHelpers.keywords_files.each {|file| require_relative file }
22
+
23
+ # lock_smith/*.rb
24
+ VaultTree::PathHelpers.lock_smith_files.each {|file| require_relative file }
@@ -8,7 +8,6 @@ module VaultTree
8
8
 
9
9
  def validate!
10
10
  confirm_valid_fill_keyword
11
- validate_external_data
12
11
  true
13
12
  end
14
13
 
@@ -18,12 +17,6 @@ module VaultTree
18
17
  raise Exceptions::FillAttemptMasterPassword if vault.fill_with == 'MASTER_PASSPHRASE'
19
18
  end
20
19
 
21
- def validate_external_data
22
- if external_data_required? && external_data_missing?
23
- raise Exceptions::MissingExternalData
24
- end
25
- end
26
-
27
20
  def external_data_required?
28
21
  vault.fill_with == 'EXTERNAL_DATA'
29
22
  end
@@ -8,7 +8,8 @@ module VaultTree
8
8
  @external_data = params[:external_data]
9
9
  end
10
10
 
11
- def close_vault(id)
11
+ def close_vault(id, params = {data: nil})
12
+ update_external_data(id: id , data: params[:data])
12
13
  validate_vault(id)
13
14
  update_vaults vault(id).close
14
15
  self
@@ -48,6 +49,10 @@ module VaultTree
48
49
  @master_passphrase
49
50
  end
50
51
 
52
+ def external_data_hash
53
+ @external_data
54
+ end
55
+
51
56
  def external_data(id)
52
57
  @external_data[id]
53
58
  end
@@ -55,7 +60,7 @@ module VaultTree
55
60
  private
56
61
 
57
62
  def passphrase_present?
58
- !! @master_passphrase
63
+ !! @master_passphrase
59
64
  end
60
65
 
61
66
  def valid_id?(id)
@@ -81,5 +86,11 @@ module VaultTree
81
86
  def validate_passphrase
82
87
  raise Exceptions::MissingPassphrase unless passphrase_present?
83
88
  end
89
+
90
+ def update_external_data(params)
91
+ vault_id = params[:id]
92
+ data = params[:data]
93
+ @external_data = @external_data.merge({"#{vault_id}" => "#{data}"}) unless data.nil?
94
+ end
84
95
  end
85
96
  end
@@ -20,27 +20,37 @@ module VaultTree
20
20
  private
21
21
 
22
22
  def ciphertext_contents
23
- shared_locking_key? ? asymmetric_ciphertext : symmetric_ciphertext
23
+ dh_locking_key? ? asymmetric_ciphertext : symmetric_ciphertext
24
24
  end
25
25
 
26
26
  def plaintext_contents
27
- shared_unlocking_key? ? asymmetric_plaintext : symmetric_plaintext
27
+ dh_unlocking_key? ? asymmetric_plaintext : symmetric_plaintext
28
28
  end
29
29
 
30
30
  def asymmetric_ciphertext
31
- asymmetric_cipher.encrypt(public_key: locking_public_key, secret_key: locking_secret_key, plain_text: filler)
31
+ LockSmith.new(public_key: locking_public_key, private_key: locking_secret_key, message: filler).asymmetric_encrypt
32
32
  end
33
33
 
34
34
  def asymmetric_plaintext
35
- asymmetric_cipher.decrypt(public_key: unlocking_public_key, secret_key: unlocking_secret_key, cipher_text: contents)
35
+ begin
36
+ LockSmith.new(public_key: unlocking_public_key, private_key: unlocking_secret_key, cipher_text: contents).asymmetric_decrypt
37
+ rescue(RbNaCl::CryptoError)
38
+ raise(Exceptions::FailedUnlockAttempt)
39
+ end
36
40
  end
37
41
 
38
42
  def symmetric_ciphertext
39
- symmetric_cipher.encrypt(key: locking_key, plain_text: filler)
43
+ key_hash = LockSmith.new(message: locking_key).secure_hash
44
+ LockSmith.new(message: filler, secret_key: key_hash).symmetric_encrypt
40
45
  end
41
46
 
42
47
  def symmetric_plaintext
43
- symmetric_cipher.decrypt(key: unlocking_key, cipher_text: contents)
48
+ begin
49
+ key_hash = LockSmith.new(message: unlocking_key).secure_hash
50
+ LockSmith.new(cipher_text: contents, secret_key: key_hash).symmetric_decrypt
51
+ rescue(RbNaCl::CryptoError)
52
+ raise(Exceptions::FailedUnlockAttempt)
53
+ end
44
54
  end
45
55
 
46
56
  def locking_key
@@ -68,11 +78,11 @@ module VaultTree
68
78
  end
69
79
 
70
80
  def locking_key_pair
71
- vault.locking_key if shared_locking_key?
81
+ vault.locking_key if dh_locking_key?
72
82
  end
73
83
 
74
84
  def unlocking_key_pair
75
- vault.unlocking_key if shared_unlocking_key?
85
+ vault.unlocking_key if dh_unlocking_key?
76
86
  end
77
87
 
78
88
  def empty?
@@ -91,22 +101,13 @@ module VaultTree
91
101
  vault.contents
92
102
  end
93
103
 
94
- def shared_locking_key?
95
- vault.lock_with =~ /SHARED_KEY/
104
+ def dh_locking_key?
105
+ vault.lock_with =~ /DH_KEY/
96
106
  end
97
107
 
98
- def shared_unlocking_key?
99
- vault.unlock_with =~ /SHARED_KEY/
108
+ def dh_unlocking_key?
109
+ vault.unlock_with =~ /DH_KEY/
100
110
  end
101
-
102
- def asymmetric_cipher
103
- LockSmith::AsymmetricCipher.new
104
- end
105
-
106
- def symmetric_cipher
107
- LockSmith::SymmetricCipher.new
108
- end
109
-
110
111
  end
111
112
  end
112
113
  end
@@ -78,11 +78,27 @@ module VaultTree
78
78
  end
79
79
 
80
80
  def has_lock_ancestor?
81
- lock_with.include? 'CONTENTS'
81
+ lock_with_key_or_contents?
82
82
  end
83
83
 
84
84
  def has_fill_ancestor?
85
- fill_with.include? 'CONTENTS'
85
+ fill_with_key_or_contents?
86
+ end
87
+
88
+ def lock_with_key_or_contents?
89
+ (locking_word_base == 'CONTENTS') || (locking_word_base == 'KEY')
90
+ end
91
+
92
+ def locking_word_base
93
+ KeywordInterpreter.new(lock_with,self).word_base
94
+ end
95
+
96
+ def fill_with_key_or_contents?
97
+ (filling_word_base == 'CONTENTS') || (filling_word_base == 'KEY')
98
+ end
99
+
100
+ def filling_word_base
101
+ KeywordInterpreter.new(fill_with,self).word_base
86
102
  end
87
103
 
88
104
  def lock_ancestor_id
@@ -0,0 +1,6 @@
1
+ module VaultTree
2
+ module Exceptions
3
+ class FailedUnlockAttempt < VaultTreeException
4
+ end
5
+ end
6
+ end
@@ -1,6 +1,24 @@
1
+ require 'erb'
1
2
  module VaultTree
2
3
  module Exceptions
3
4
  class VaultTreeException < StandardError
5
+
6
+ def self.exception
7
+ self.present_exception
8
+ self.new
9
+ end
10
+
11
+ def self.present_exception
12
+ STDOUT.write template.result(binding)
13
+ end
14
+
15
+ def self.template
16
+ ERB.new File.new(template_path).read, nil, "%"
17
+ end
18
+
19
+ def self.template_path
20
+ "lib/vault-tree/exceptions/exception_template.erb"
21
+ end
4
22
  end
5
23
  end
6
24
  end
@@ -0,0 +1,44 @@
1
+ module VaultTree
2
+ class AssembledShamirKey < Keyword
3
+
4
+ attr_reader :arg_array
5
+
6
+ def post_initialize(arg_array)
7
+ @arg_array = arg_array
8
+ end
9
+
10
+ # Assemble a Shamir key from existing shares. Get the shares from their respective
11
+ # vaults. Assemble and Return the key.
12
+ #
13
+ # @return [String] Secure Hash digest of the generated key
14
+ def evaluate
15
+ retrieve_key_shares
16
+ assembled_key
17
+ end
18
+
19
+ private
20
+
21
+ def assembled_key
22
+ key_object.assemble
23
+ end
24
+
25
+ def collected_shares
26
+ @collected_shares
27
+ end
28
+
29
+ def vault_ids
30
+ arg_array
31
+ end
32
+
33
+ def retrieve_key_shares
34
+ @collected_shares = []
35
+ vault_ids.each{|id| @collected_shares << contract.retrieve_contents(id) }
36
+ return @collected_shares
37
+ end
38
+
39
+ def key_object
40
+ @key_object ||= Crypto::AssembledShamirKey.new(key_shares: collected_shares)
41
+ end
42
+
43
+ end
44
+ end
@@ -2,13 +2,8 @@ module VaultTree
2
2
  class DecryptionKey < Keyword
3
3
 
4
4
  def evaluate
5
- key_pair.generate_private_key
5
+ LockSmith.new().generate_private_key
6
6
  end
7
7
 
8
- private
9
-
10
- def key_pair
11
- LockSmith::EncryptionKeyPair.new
12
- end
13
8
  end
14
9
  end
@@ -1,5 +1,5 @@
1
1
  module VaultTree
2
- class SharedKey < Keyword
2
+ class DhKey < Keyword
3
3
  attr_reader :public_key_vault_id, :secret_key_vault_id
4
4
 
5
5
  def post_initialize(arg_array)
@@ -8,7 +8,7 @@ module VaultTree
8
8
  end
9
9
 
10
10
  def evaluate
11
- LockSmith::SharedKeyPair.new(public_key: public_key_vault_contents, secret_key: secret_key_vault_contents)
11
+ DHKeyPair.new(public_key: public_key_vault_contents, secret_key: secret_key_vault_contents)
12
12
  end
13
13
 
14
14
  private
@@ -2,6 +2,7 @@ module VaultTree
2
2
  class ExternalData < Keyword
3
3
 
4
4
  def evaluate
5
+ check_for_external_data
5
6
  contract.external_data(id)
6
7
  end
7
8
 
@@ -9,5 +10,23 @@ module VaultTree
9
10
  vault.id
10
11
  end
11
12
 
13
+ private
14
+
15
+ def check_for_external_data
16
+ raise(Exceptions::MissingExternalData) if missing_external_data?
17
+ end
18
+
19
+ def missing_external_data?
20
+ nil_external_data? || empty_external_data?
21
+ end
22
+
23
+ def nil_external_data?
24
+ contract.external_data_hash.nil?
25
+ end
26
+
27
+ def empty_external_data?
28
+ contract.external_data_hash.empty?
29
+ end
30
+
12
31
  end
13
32
  end
@@ -0,0 +1,57 @@
1
+ module VaultTree
2
+ class GeneratedShamirKey < Keyword
3
+ attr_reader :outstanding_shares, :recovery_threshold, :share_vaults
4
+
5
+ def post_initialize(arg_array)
6
+ @outstanding_shares = arg_array[0]
7
+ @recovery_threshold = arg_array[1]
8
+ @share_vaults = arg_array[2..arg_array.length]
9
+ end
10
+
11
+ # Generate a new Shamir key.
12
+ #
13
+ # Check that shares can be saved in empty vaults
14
+ # Put the shares in their respective # vaults.
15
+ # Gnerate and Return the key.
16
+ #
17
+ # @return [String] Secure Hash digest of the generated key
18
+ def evaluate
19
+ validate_share_vaults
20
+ lock_away_key_shares
21
+ generated_key
22
+ end
23
+
24
+ private
25
+
26
+ def lock_away_key_shares
27
+ c = contract
28
+ share_vaults.each do |v|
29
+ c = c.close_vault(v, data: data_for_vault(v) )
30
+ end
31
+ end
32
+
33
+ def data_for_vault(id)
34
+ generated_key_shares[vault_index(id)]
35
+ end
36
+
37
+ def vault_index(id)
38
+ share_vaults.index(id)
39
+ end
40
+
41
+ def generated_key
42
+ key_object.key
43
+ end
44
+
45
+ def generated_key_shares
46
+ key_object.shares
47
+ end
48
+
49
+ def key_object
50
+ @key_object ||= Crypto::GeneratedShamirKey.new(outstanding_shares: outstanding_shares, recovery_threshold: recovery_threshold)
51
+ end
52
+
53
+ def validate_share_vaults
54
+ true # Add exception tests and functionality later
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,13 @@
1
+ module VaultTree
2
+ class Key < Keyword
3
+ attr_reader :vault_id
4
+
5
+ def post_initialize(arg_array)
6
+ @vault_id = arg_array[0]
7
+ end
8
+
9
+ def evaluate
10
+ contract.retrieve_contents(vault_id)
11
+ end
12
+ end
13
+ end
@@ -1,7 +1,7 @@
1
1
  module VaultTree
2
2
  class KeywordInterpreter
3
3
  attr_reader :word, :vault
4
-
4
+
5
5
  def initialize(word,vault)
6
6
  @word = word
7
7
  @vault = vault
@@ -11,20 +11,20 @@ module VaultTree
11
11
  begin
12
12
  keyword_class_name.new(vault, arg_array).evaluate
13
13
  rescue NameError
14
- raise Exceptions::UnsupportedKeyword
14
+ raise Exceptions::UnsupportedKeyword
15
15
  end
16
16
  end
17
17
 
18
+ def word_base
19
+ word.gsub(/(\[.*\])/,'').strip
20
+ end
21
+
18
22
  private
19
23
 
20
24
  def keyword_class_name
21
25
  instance_eval "VaultTree::#{word_base.downcase.camelize}"
22
26
  end
23
27
 
24
- def word_base
25
- word.gsub(/(\[.*\])/,'').strip
26
- end
27
-
28
28
  def arg_array
29
29
  if has_args?
30
30
  instance_eval arg_array_string