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
@@ -7,15 +7,11 @@ module VaultTree
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def evaluate
|
10
|
-
|
10
|
+
LockSmith.new(private_key: decryption_key).generate_public_key
|
11
11
|
end
|
12
12
|
|
13
13
|
private
|
14
14
|
|
15
|
-
def key_pair
|
16
|
-
LockSmith::EncryptionKeyPair.new
|
17
|
-
end
|
18
|
-
|
19
15
|
def decryption_key
|
20
16
|
begin
|
21
17
|
contract.retrieve_contents(vault_id)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module VaultTree
|
2
|
+
class SplitKey < Keyword
|
3
|
+
attr_reader :required_key_vaults
|
4
|
+
|
5
|
+
def post_initialize(arg_array)
|
6
|
+
@required_key_vaults = arg_array
|
7
|
+
end
|
8
|
+
|
9
|
+
def evaluate
|
10
|
+
SplitKeyCrypto.new(required_keys: required_keys).generate
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def required_keys
|
16
|
+
required_key_vaults.map {|id| contract.retrieve_contents(id) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'rbnacl'
|
2
|
+
|
3
|
+
module VaultTree
|
4
|
+
# VaultTree::LockSmith Interface to Crypto Primatives
|
5
|
+
#
|
6
|
+
# This class provides a interface all of the cryptographic functions used
|
7
|
+
# by Vault Tree.
|
8
|
+
#
|
9
|
+
# Specifically, it exposed the functionallity of the RbNaCl Gem and the
|
10
|
+
# vault-tree-ss secret sharing gem.
|
11
|
+
class LockSmith
|
12
|
+
|
13
|
+
def initialize(opts = {})
|
14
|
+
@message = opts[:message]
|
15
|
+
@cipher_text = opts[:cipher_text]
|
16
|
+
@secret_key = opts[:secret_key]
|
17
|
+
@private_key = opts[:private_key]
|
18
|
+
@public_key = opts[:public_key]
|
19
|
+
@signing_key = opts[:signing_key]
|
20
|
+
@verify_key = opts[:verify_key]
|
21
|
+
@signature = opts[:signature]
|
22
|
+
@outstanding_secret_shares = opts[:outstanding_secret_shares]
|
23
|
+
@secret_recovery_threshold = opts[:secret_recovery_threshold]
|
24
|
+
@secret_shares = opts[:secret_shares]
|
25
|
+
end
|
26
|
+
|
27
|
+
# Random Key for Semetric Encryption
|
28
|
+
#
|
29
|
+
# @return [String] Hex encoded Secret Key
|
30
|
+
def generate_secret_key
|
31
|
+
bin2hex RbNaCl::Hash.sha256(RbNaCl::Random.random_bytes(128))
|
32
|
+
end
|
33
|
+
|
34
|
+
# Randomly Generated Private Key
|
35
|
+
#
|
36
|
+
# @return [String] Hex encoded Private Key
|
37
|
+
def generate_private_key
|
38
|
+
bin2hex(RbNaCl::PrivateKey.generate.to_bytes)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Public Key Derived from a given private key
|
42
|
+
#
|
43
|
+
# @return [String] Hex encoded Public Key
|
44
|
+
def generate_public_key
|
45
|
+
bin2hex(RbNaCl::PrivateKey.new(private_key).public_key.to_bytes)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Randomly Generated Signing Key
|
49
|
+
#
|
50
|
+
# @return [String] Hex encoded Signing Key
|
51
|
+
def generate_signing_key
|
52
|
+
bin2hex(RbNaCl::SigningKey.generate.to_bytes)
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
# Public Signature Verification Key
|
57
|
+
# Derived from a given signing key
|
58
|
+
#
|
59
|
+
# @return [String] Hex encoded Verify Key
|
60
|
+
def generate_verify_key
|
61
|
+
bin2hex(RbNaCl::SigningKey.new(signing_key).verify_key.to_bytes)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Check the validity of a message's signature
|
65
|
+
# Will raise RbNaCl::BadSignatureError if the signature check fails
|
66
|
+
def verify_message
|
67
|
+
RbNaCl::VerifyKey.new(verify_key).verify(message, signature)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Symmetric Encryption of the given message
|
71
|
+
#
|
72
|
+
# @return [String] Hex encoded message ciphertext
|
73
|
+
def symmetric_encrypt
|
74
|
+
bin2hex RbNaCl::RandomNonceBox.from_secret_key(secret_key).box(message)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Symmetric Decryption of the given ciphertext
|
78
|
+
#
|
79
|
+
# @return [String] Decoded plaintext message
|
80
|
+
def symmetric_decrypt
|
81
|
+
RbNaCl::RandomNonceBox.from_secret_key(secret_key).open(cipher_text)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Asymmetric Encryption of the given message
|
85
|
+
#LockSmith.new()
|
86
|
+
#
|
87
|
+
# @return [String] Hex encoded message ciphertext
|
88
|
+
def asymmetric_encrypt
|
89
|
+
pri = RbNaCl::PrivateKey.new(private_key)
|
90
|
+
pub = RbNaCl::PublicKey.new(public_key)
|
91
|
+
box = RbNaCl::Box.new(pub,pri)
|
92
|
+
bin2hex RbNaCl::RandomNonceBox.new(box).box(message)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Asymmetric Decryption of the given ciphertext
|
96
|
+
#
|
97
|
+
# @return [String] Decoded plaintext message
|
98
|
+
def asymmetric_decrypt
|
99
|
+
pri = RbNaCl::PrivateKey.new(private_key)
|
100
|
+
pub = RbNaCl::PublicKey.new(public_key)
|
101
|
+
box = RbNaCl::Box.new(pub,pri)
|
102
|
+
RbNaCl::RandomNonceBox.new(box).open(cipher_text)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Random number generation
|
106
|
+
#
|
107
|
+
# This uses the underlying source of random number generation on the OS, so
|
108
|
+
# /dev/urandom on UNIX-like systems, and the MS crypto providor on windows.
|
109
|
+
def random_number
|
110
|
+
bin2hex RbNaCl::Random.random_bytes
|
111
|
+
end
|
112
|
+
|
113
|
+
# Returns the SHA-256 hash of the given data
|
114
|
+
#
|
115
|
+
# There's no streaming done, just pass in the data and be done with it.
|
116
|
+
#
|
117
|
+
# @raise [CryptoError] If the hashing fails for some reason.
|
118
|
+
#
|
119
|
+
# @return [String] The SHA-256 hash as hex
|
120
|
+
def secure_hash
|
121
|
+
bin2hex RbNaCl::Hash.sha256(message)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Sign a message with the signing key
|
125
|
+
def sign_message
|
126
|
+
bin2hex RbNaCl::SigningKey.new(signing_key).sign(message)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Check the validity of a message's signature
|
130
|
+
# Will raise RbNaCl::BadSignatureError if the signature check fails
|
131
|
+
def verify_message
|
132
|
+
RbNaCl::VerifyKey.new(verify_key).verify(signature, message)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Recovers the shared secret from the shares provided
|
136
|
+
# in the initializer.
|
137
|
+
#
|
138
|
+
# @return [String] Secure Hash digest of the assembled secret
|
139
|
+
def combine_secret_shares
|
140
|
+
end
|
141
|
+
|
142
|
+
# Secret Shares associated with the split message
|
143
|
+
#
|
144
|
+
# @return [Array] Array of strings
|
145
|
+
def split_secret
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def message; @message end
|
151
|
+
|
152
|
+
# Locksmith always expects hex representations
|
153
|
+
# of keys and ciphertext. Convert to binary to
|
154
|
+
# give to RbNaCl.
|
155
|
+
def cipher_text; hex2bin @cipher_text end
|
156
|
+
def private_key; hex2bin @private_key end
|
157
|
+
def public_key; hex2bin @public_key end
|
158
|
+
def secret_key; hex2bin @secret_key end
|
159
|
+
def signing_key; hex2bin @signing_key end
|
160
|
+
def verify_key; hex2bin @verify_key end
|
161
|
+
def signature; hex2bin @signature end
|
162
|
+
|
163
|
+
# Hex encodes a message
|
164
|
+
#
|
165
|
+
# @param [String] The bytes to encode
|
166
|
+
#
|
167
|
+
# @return [String] hexadecimal
|
168
|
+
def bin2hex(bytes)
|
169
|
+
bytes.to_s.unpack("H*").first
|
170
|
+
end
|
171
|
+
|
172
|
+
# Hex decodes a message
|
173
|
+
#
|
174
|
+
# @param [String] hex to decode.
|
175
|
+
#
|
176
|
+
# @return [String] crisp and clean bytes
|
177
|
+
def hex2bin(hex)
|
178
|
+
[hex.to_s].pack("H*")
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'secretsharing'
|
2
|
+
|
3
|
+
module VaultTree
|
4
|
+
module Crypto
|
5
|
+
class AssembledShamirKey
|
6
|
+
attr_reader :key_shares
|
7
|
+
|
8
|
+
# Creates a new AssembledShamirKey object
|
9
|
+
#
|
10
|
+
# @param [Hash]
|
11
|
+
# @option opts [Array] :key_shares Secret Shares as strings
|
12
|
+
#
|
13
|
+
# @return [AssembledShamirKey]
|
14
|
+
def initialize(params)
|
15
|
+
@key_shares = params[:key_shares]
|
16
|
+
validate_key_shares
|
17
|
+
end
|
18
|
+
|
19
|
+
# Recovers the shared secret from the shares provided
|
20
|
+
# in the initializer.
|
21
|
+
#
|
22
|
+
# @return [String] Secure Hash digest of the assembled secret
|
23
|
+
def assemble
|
24
|
+
LockSmith.new(message: assembled_secret).secure_hash
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def assembled_secret
|
30
|
+
assemble_secret_object
|
31
|
+
assembled_object_string
|
32
|
+
end
|
33
|
+
|
34
|
+
def assemble_secret_object
|
35
|
+
key_shares.each { |s| assembled_object << s }
|
36
|
+
end
|
37
|
+
|
38
|
+
def assembled_object
|
39
|
+
@assembled_object ||= SecretSharing::Shamir.new(total_key_shares)
|
40
|
+
end
|
41
|
+
|
42
|
+
def assembled_object_string
|
43
|
+
assembled_object.secret.to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
def total_key_shares
|
47
|
+
key_shares.length
|
48
|
+
end
|
49
|
+
|
50
|
+
def validate_key_shares
|
51
|
+
raise(TypeError, 'nil key_shares passed') if key_shares.nil?
|
52
|
+
raise(TypeError) if any_none_string_key_shares?
|
53
|
+
end
|
54
|
+
|
55
|
+
def any_none_string_key_shares?
|
56
|
+
key_shares.any? { |s| not_a_string?(s)}
|
57
|
+
end
|
58
|
+
|
59
|
+
def not_a_string?(s)
|
60
|
+
! s.kind_of?(String)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'secretsharing'
|
2
|
+
|
3
|
+
module VaultTree
|
4
|
+
module Crypto
|
5
|
+
class GeneratedShamirKey
|
6
|
+
|
7
|
+
def initialize(params)
|
8
|
+
@outstanding_shares = params[:outstanding_shares]
|
9
|
+
@recovery_threshold = params[:recovery_threshold]
|
10
|
+
end
|
11
|
+
|
12
|
+
# String representation of the newly generated sharmir key
|
13
|
+
#
|
14
|
+
# @return [String] Secure Hash digest of the generated secret integer
|
15
|
+
def key
|
16
|
+
create_secret
|
17
|
+
LockSmith.new(message: secret_string).secure_hash
|
18
|
+
end
|
19
|
+
|
20
|
+
# Shares associated with the newly generated Sharmir key
|
21
|
+
#
|
22
|
+
# @return [Array] Array of strings
|
23
|
+
def shares
|
24
|
+
create_secret
|
25
|
+
shares_array.map{|s| s.to_s}
|
26
|
+
end
|
27
|
+
|
28
|
+
# Fixnum representation if string value given
|
29
|
+
#
|
30
|
+
# @return [FixNum]
|
31
|
+
def outstanding_shares
|
32
|
+
@outstanding_shares.to_i
|
33
|
+
end
|
34
|
+
|
35
|
+
# Fixnum representation if string value given
|
36
|
+
#
|
37
|
+
# @return [FixNum]
|
38
|
+
def recovery_threshold
|
39
|
+
@recovery_threshold.to_i
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def secret_string
|
45
|
+
shamir_object.secret.to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
def shares_array
|
49
|
+
shamir_object.shares
|
50
|
+
end
|
51
|
+
|
52
|
+
def shamir_object
|
53
|
+
@shamir_object ||= SecretSharing::Shamir.new(outstanding_shares, recovery_threshold)
|
54
|
+
end
|
55
|
+
|
56
|
+
def create_secret
|
57
|
+
shamir_object.create_random_secret unless secret_set?
|
58
|
+
end
|
59
|
+
|
60
|
+
def secret_set?
|
61
|
+
shamir_object.secret_set?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module VaultTree
|
2
|
+
class SplitKeyCrypto
|
3
|
+
attr_reader :required_keys
|
4
|
+
|
5
|
+
def initialize(opts)
|
6
|
+
@required_keys = opts[:required_keys]
|
7
|
+
end
|
8
|
+
|
9
|
+
def generate
|
10
|
+
secure_hash key_digests.join('')
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def key_digests
|
16
|
+
required_keys.map{|k| secure_hash(k) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def secure_hash(s)
|
20
|
+
LockSmith.new(message: s).secure_hash
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -7,7 +7,7 @@ module VaultTree
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def walk_to_root
|
10
|
-
'
|
10
|
+
'../../'
|
11
11
|
end
|
12
12
|
|
13
13
|
def project_dir
|
@@ -27,13 +27,17 @@ module VaultTree
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def fixtures_dir
|
30
|
-
"#{project_dir}/
|
30
|
+
"#{project_dir}/features/support/contract_fixtures"
|
31
31
|
end
|
32
32
|
|
33
33
|
def reference_contract
|
34
34
|
"#{fixtures_dir}/reference_contract.1.0.0.json"
|
35
35
|
end
|
36
36
|
|
37
|
+
def shared_secret_contract
|
38
|
+
"#{fixtures_dir}/shared_secret_contract.json"
|
39
|
+
end
|
40
|
+
|
37
41
|
def broken_contract
|
38
42
|
"#{fixtures_dir}/broken_contract.json"
|
39
43
|
end
|
@@ -45,5 +49,25 @@ module VaultTree
|
|
45
49
|
def simple_test_contract
|
46
50
|
"#{fixtures_dir}/simple_test_contract.json"
|
47
51
|
end
|
52
|
+
|
53
|
+
def exceptions_files
|
54
|
+
Dir["#{project_dir}/lib/vault-tree/exceptions/*.rb"]
|
55
|
+
end
|
56
|
+
|
57
|
+
def keywords_files
|
58
|
+
Dir["#{project_dir}/lib/vault-tree/keywords/*.rb"]
|
59
|
+
end
|
60
|
+
|
61
|
+
def lock_smith_files
|
62
|
+
Dir["#{project_dir}/lib/vault-tree/lock_smith/*.rb"]
|
63
|
+
end
|
64
|
+
|
65
|
+
def contract_files
|
66
|
+
Dir["#{project_dir}/lib/vault-tree/contract/*.rb"]
|
67
|
+
end
|
68
|
+
|
69
|
+
def core_contracts(file)
|
70
|
+
"#{fixtures_dir}/#{file}"
|
71
|
+
end
|
48
72
|
end
|
49
73
|
end
|