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