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.
- 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
|