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
@@ -7,15 +7,11 @@ module VaultTree
7
7
  end
8
8
 
9
9
  def evaluate
10
- key_pair.public_key(decryption_key)
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)
@@ -2,7 +2,7 @@ module VaultTree
2
2
  class RandomNumber < Keyword
3
3
 
4
4
  def evaluate
5
- LockSmith::RandomNumber.compute
5
+ LockSmith.new().random_number
6
6
  end
7
7
 
8
8
  end
@@ -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
@@ -2,7 +2,7 @@ module VaultTree
2
2
  class Unlocked < Keyword
3
3
 
4
4
  def evaluate
5
- LockSmith::CryptoHash.compute('UNLOCKED')
5
+ LockSmith.new(message: 'UNLOCKED').secure_hash
6
6
  end
7
7
 
8
8
  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,10 @@
1
+ module VaultTree
2
+ class DHKeyPair
3
+ attr_reader :public_key, :secret_key
4
+
5
+ def initialize(opts)
6
+ @public_key = opts[:public_key]
7
+ @secret_key = opts[:secret_key]
8
+ end
9
+ end
10
+ 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}/spec/support/fixtures"
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