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.
- data/.gitignore +7 -3
- data/CHANGE_LOG.md +15 -0
- data/Gemfile.lock +15 -5
- data/README.md +12 -15
- data/Rakefile +19 -4
- data/features/.nav +11 -0
- data/features/contracts/asymmetric_vault.feature +23 -0
- data/features/contracts/block_chain_key_transfer.feature +34 -0
- data/features/contracts/one_two_three.feature +22 -0
- data/features/contracts/readme.md +111 -0
- data/features/contracts_and_vaults.md +134 -0
- data/features/contributing_to_vault_tree.md +42 -0
- data/features/decision_tree.md +16 -0
- data/features/enforcement_problem.md +20 -0
- data/features/exceptions.feature +56 -5
- data/features/install_and_usage.md +57 -0
- data/features/keywords/assembled_shamir_key.feature +57 -0
- data/features/keywords/contents.feature +24 -0
- data/features/keywords/decryption_key.feature +10 -0
- data/features/keywords/dh_key.feature +56 -0
- data/features/keywords/external_data.feature +11 -0
- data/features/keywords/generated_shamir_key.feature +55 -0
- data/features/keywords/key.feature +38 -0
- data/features/keywords/master_passphrase.feature +68 -0
- data/features/keywords/public_encryption_key.feature +14 -0
- data/features/keywords/random_number.feature +44 -0
- data/features/keywords/readme.md +3 -0
- data/features/keywords/split_key.feature +54 -0
- data/features/keywords/unlocked.feature +51 -0
- data/features/manipulating_contracts.md +84 -0
- data/features/readme.md +6 -0
- data/features/steps/asymmetric_vault.steps.rb +41 -0
- data/features/steps/block_chain_key_transfer.steps.rb +43 -0
- data/features/steps/core.steps.rb +57 -104
- data/features/steps/exceptions.steps.rb +45 -1
- data/features/steps/one_two_three.steps.rb +57 -0
- data/features/steps/secret_sharing.steps.rb +36 -0
- data/features/support/contract_fixtures/asymmetric_vault.0.1.0.json +69 -0
- data/{spec/support/fixtures → features/support/contract_fixtures}/blank_simple_test_contract.json +0 -0
- data/features/support/contract_fixtures/block_chain_key_transfer.0.1.0.json +59 -0
- data/{spec/support/fixtures → features/support/contract_fixtures}/broken_contract.json +0 -0
- data/features/support/contract_fixtures/one_two_three.0.7.0.json +108 -0
- data/{spec/support/fixtures → features/support/contract_fixtures}/simple_test_contract.json +0 -0
- data/features/support/contract_fixtures/template.json +33 -0
- data/features/what_is_vault_tree.md +18 -0
- data/lib/vault-tree.rb +23 -6
- data/lib/vault-tree/contract/close_validator.rb +0 -7
- data/lib/vault-tree/contract/contract.rb +13 -2
- data/lib/vault-tree/contract/doorman.rb +22 -21
- data/lib/vault-tree/contract/vault.rb +18 -2
- data/lib/vault-tree/exceptions/exception_template.erb +0 -0
- data/lib/vault-tree/exceptions/failed_unlock_attempt.rb +6 -0
- data/lib/vault-tree/exceptions/vault_tree_exception.rb +18 -0
- data/lib/vault-tree/keywords/assembled_shamir_key.rb +44 -0
- data/lib/vault-tree/keywords/{vault_contents.rb → contents.rb} +0 -0
- data/lib/vault-tree/keywords/decryption_key.rb +1 -6
- data/lib/vault-tree/keywords/{shared_key.rb → dh_key.rb} +2 -2
- data/lib/vault-tree/keywords/external_data.rb +19 -0
- data/lib/vault-tree/keywords/generated_shamir_key.rb +57 -0
- data/lib/vault-tree/keywords/key.rb +13 -0
- data/lib/vault-tree/keywords/keyword_interpreter.rb +6 -6
- data/lib/vault-tree/keywords/public_encryption_key.rb +1 -5
- data/lib/vault-tree/keywords/random_number.rb +1 -1
- data/lib/vault-tree/keywords/split_key.rb +19 -0
- data/lib/vault-tree/keywords/unlocked.rb +1 -1
- data/lib/vault-tree/lock_smith.rb +182 -0
- data/lib/vault-tree/lock_smith/assembled_shamir_key.rb +64 -0
- data/lib/vault-tree/lock_smith/dh_key_pair.rb +10 -0
- data/lib/vault-tree/lock_smith/generated_shamir_key.rb +65 -0
- data/lib/vault-tree/lock_smith/split_key.rb +23 -0
- data/lib/vault-tree/{config/path_helpers.rb → path_helpers.rb} +26 -2
- data/lib/vault-tree/util/json.rb +1 -0
- data/lib/vault-tree/{config → util}/string.rb +1 -5
- data/lib/vault-tree/version.rb +1 -1
- data/spec/assembled_shamir_key_spec.rb +79 -0
- data/spec/generated_shamir_key_spec.rb +52 -0
- data/spec/lock_smith_spec.rb +90 -0
- data/spec/secret_sharing_spec.rb +43 -0
- data/support/scripts/libsodium_ubuntu.sh +1 -1
- data/vault-tree.gemspec +3 -2
- metadata +123 -41
- data/features/core.feature +0 -44
- data/lib/vault-tree/config/dependencies.rb +0 -4
- data/lib/vault-tree/config/lib.rb +0 -2
- data/lib/vault-tree/lock_smith/asymmetric_cipher.rb +0 -31
- data/lib/vault-tree/lock_smith/crypto_hash.rb +0 -11
- data/lib/vault-tree/lock_smith/digital_signature.rb +0 -32
- data/lib/vault-tree/lock_smith/encryption_key_pair.rb +0 -25
- data/lib/vault-tree/lock_smith/random_number.rb +0 -11
- data/lib/vault-tree/lock_smith/shared_key_pair.rb +0 -12
- data/lib/vault-tree/lock_smith/signing_key_pair.rb +0 -25
- data/lib/vault-tree/lock_smith/symmetric_cipher.rb +0 -25
- data/spec/app/locksmith/asymmetric_cipher_spec.rb +0 -25
- data/spec/app/locksmith/signing_key_pair_spec.rb +0 -22
- data/spec/spec_helper.rb +0 -5
- data/spec/support/fixtures/one_two_three-0.5.0.EXP.json +0 -105
- data/spec/support/fixtures/reference_contract.1.0.0.json +0 -227
data/lib/vault-tree.rb
CHANGED
@@ -1,7 +1,24 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# *.rb
|
2
|
+
require_relative 'vault-tree/path_helpers'
|
3
|
+
require_relative 'vault-tree/version'
|
3
4
|
|
4
|
-
|
5
|
-
require_relative 'vault-tree/
|
6
|
-
require_relative 'vault-tree/
|
7
|
-
|
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
|
-
|
23
|
+
dh_locking_key? ? asymmetric_ciphertext : symmetric_ciphertext
|
24
24
|
end
|
25
25
|
|
26
26
|
def plaintext_contents
|
27
|
-
|
27
|
+
dh_unlocking_key? ? asymmetric_plaintext : symmetric_plaintext
|
28
28
|
end
|
29
29
|
|
30
30
|
def asymmetric_ciphertext
|
31
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
81
|
+
vault.locking_key if dh_locking_key?
|
72
82
|
end
|
73
83
|
|
74
84
|
def unlocking_key_pair
|
75
|
-
vault.unlocking_key if
|
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
|
95
|
-
vault.lock_with =~ /
|
104
|
+
def dh_locking_key?
|
105
|
+
vault.lock_with =~ /DH_KEY/
|
96
106
|
end
|
97
107
|
|
98
|
-
def
|
99
|
-
vault.unlock_with =~ /
|
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
|
-
|
81
|
+
lock_with_key_or_contents?
|
82
82
|
end
|
83
83
|
|
84
84
|
def has_fill_ancestor?
|
85
|
-
|
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
|
File without changes
|
@@ -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
|
File without changes
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module VaultTree
|
2
|
-
class
|
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
|
-
|
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
|
@@ -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
|