vault-tree 0.1.0 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
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