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
@@ -0,0 +1,68 @@
|
|
1
|
+
Feature: Master Passphrase
|
2
|
+
|
3
|
+
```javascript
|
4
|
+
"lock_with": "MASTER_PASSPHRASE",
|
5
|
+
"unlock_with": "MASTER_PASSPHRASE"
|
6
|
+
```
|
7
|
+
|
8
|
+
The master password can be thought of as the secure password for the system that is executing the contract. It should never be shared or transfered between parties.
|
9
|
+
|
10
|
+
Vault Tree prevents this value from ever being stored within a vault. If you attempt to store the master password within a vault, an exception will be thrown.
|
11
|
+
|
12
|
+
As a best practice you should minimize the number of vaults that are locked or
|
13
|
+
unlocked with your master password. Specifically, consider randomly generating a
|
14
|
+
contract secret and then locking it with your master password. Then you can lock
|
15
|
+
other vaults with this randomly generated ephemeral secret when you want store
|
16
|
+
confidential contract data.
|
17
|
+
|
18
|
+
Scenario: Close And Open With Master Password
|
19
|
+
Given the blank contract:
|
20
|
+
"""javascript
|
21
|
+
{
|
22
|
+
"header": {},
|
23
|
+
"vaults": {
|
24
|
+
"random_vault_key":{
|
25
|
+
"description":"Random Number",
|
26
|
+
"fill_with": "RANDOM_NUMBER",
|
27
|
+
"lock_with": "MASTER_PASSPHRASE",
|
28
|
+
"unlock_with": "MASTER_PASSPHRASE",
|
29
|
+
"contents": ""
|
30
|
+
},
|
31
|
+
"message":{
|
32
|
+
"description": "Simple Congratulations Message",
|
33
|
+
"fill_with": "EXTERNAL_DATA",
|
34
|
+
"lock_with": "KEY['random_vault_key']",
|
35
|
+
"unlock_with": "KEY['random_vault_key']",
|
36
|
+
"contents": ""
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}
|
40
|
+
"""
|
41
|
+
When I lock a message in a vault with my Master Password
|
42
|
+
Then I can recover the message with my Master Password
|
43
|
+
|
44
|
+
Scenario: Missing Passphrase
|
45
|
+
Given the blank contract:
|
46
|
+
"""javascript
|
47
|
+
{
|
48
|
+
"header": {},
|
49
|
+
"vaults": {
|
50
|
+
"random_vault_key":{
|
51
|
+
"description":"Random Number",
|
52
|
+
"fill_with": "RANDOM_NUMBER",
|
53
|
+
"lock_with": "MASTER_PASSPHRASE",
|
54
|
+
"unlock_with": "MASTER_PASSPHRASE",
|
55
|
+
"contents": ""
|
56
|
+
},
|
57
|
+
"message":{
|
58
|
+
"description": "Simple Congratulations Message",
|
59
|
+
"fill_with": "EXTERNAL_DATA",
|
60
|
+
"lock_with": "KEY['random_vault_key']",
|
61
|
+
"unlock_with": "KEY['random_vault_key']",
|
62
|
+
"contents": ""
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
"""
|
67
|
+
When I attempt fill a vault without providing a master passphrase
|
68
|
+
Then a MissingPassphrase exception is raised
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Feature: Public Encryption Key
|
2
|
+
|
3
|
+
|
4
|
+
```javascript
|
5
|
+
"fill_with": "PUBLIC_ENCRYPTION_KEY['decryption_key_vault_id']",
|
6
|
+
```
|
7
|
+
|
8
|
+
Public encryption keys are derived from their corresponding private decryption key.
|
9
|
+
|
10
|
+
This Keyword takes one argument the vault ID containing the associated private
|
11
|
+
key.
|
12
|
+
|
13
|
+
Public encryption keys and private decryption keys are used to build DH Keys
|
14
|
+
to lock and unlock asymmetric vaults.
|
@@ -0,0 +1,44 @@
|
|
1
|
+
Feature: Random Number
|
2
|
+
|
3
|
+
The keyword
|
4
|
+
|
5
|
+
```javascript
|
6
|
+
RANDOM_NUMBER
|
7
|
+
```
|
8
|
+
|
9
|
+
behaves as expected.
|
10
|
+
|
11
|
+
When used, the Vault Tree library generates a Cryptographic Hash of a random number and places it in the designated vault.
|
12
|
+
|
13
|
+
You should use this keyword to build secure symmetric Vault keys.
|
14
|
+
|
15
|
+
Scenario: Close And Open With Random Key
|
16
|
+
Given the blank contract:
|
17
|
+
"""javascript
|
18
|
+
{
|
19
|
+
"header": {
|
20
|
+
"title":"Close And Open With Random Key"
|
21
|
+
},
|
22
|
+
"vaults": {
|
23
|
+
"random_vault_key":{
|
24
|
+
"description":"Random Number",
|
25
|
+
"fill_with": "RANDOM_NUMBER",
|
26
|
+
"lock_with": "MASTER_PASSPHRASE",
|
27
|
+
"unlock_with": "MASTER_PASSPHRASE",
|
28
|
+
"contents": ""
|
29
|
+
},
|
30
|
+
|
31
|
+
"message_locked_with_random":{
|
32
|
+
"description":"A simple message locked with a random number",
|
33
|
+
"fill_with": "EXTERNAL_DATA",
|
34
|
+
"lock_with": "KEY['random_vault_key']",
|
35
|
+
"unlock_with": "KEY['random_vault_key']",
|
36
|
+
"contents": ""
|
37
|
+
}
|
38
|
+
|
39
|
+
}
|
40
|
+
}
|
41
|
+
"""
|
42
|
+
When I lock away a random vault key
|
43
|
+
And I use the random key to lock a message
|
44
|
+
Then I can recover the message with the Random Key
|
@@ -0,0 +1,54 @@
|
|
1
|
+
Feature: Split Key
|
2
|
+
|
3
|
+
```javascript
|
4
|
+
SPLIT_KEY['id_1','id_2','id_3']
|
5
|
+
```
|
6
|
+
Split Key is a simple for of secret sharing.
|
7
|
+
|
8
|
+
Scenario: Close And Open With Split Key
|
9
|
+
Given the blank contract:
|
10
|
+
"""javascript
|
11
|
+
{
|
12
|
+
"header": {},
|
13
|
+
"vaults": {
|
14
|
+
"a_consent_key":{
|
15
|
+
"fill_with": "RANDOM_NUMBER",
|
16
|
+
"lock_with": "EXTERNAL_DATA",
|
17
|
+
"unlock_with": "EXTERNAL_DATA",
|
18
|
+
"contents": ""
|
19
|
+
},
|
20
|
+
|
21
|
+
"b_consent_key":{
|
22
|
+
"fill_with": "RANDOM_NUMBER",
|
23
|
+
"lock_with": "EXTERNAL_DATA",
|
24
|
+
"unlock_with": "EXTERNAL_DATA",
|
25
|
+
"contents": ""
|
26
|
+
},
|
27
|
+
|
28
|
+
"c_consent_key":{
|
29
|
+
"fill_with": "RANDOM_NUMBER",
|
30
|
+
"lock_with": "EXTERNAL_DATA",
|
31
|
+
"unlock_with": "EXTERNAL_DATA",
|
32
|
+
"contents": ""
|
33
|
+
},
|
34
|
+
|
35
|
+
"abc_joint_consent_key":{
|
36
|
+
"fill_with": "RANDOM_NUMBER",
|
37
|
+
"lock_with": "SPLIT_KEY['a_consent_key','b_consent_key','c_consent_key']",
|
38
|
+
"unlock_with": "SPLIT_KEY['a_consent_key','b_consent_key','c_consent_key']",
|
39
|
+
"contents": ""
|
40
|
+
},
|
41
|
+
|
42
|
+
"abc_consent_message":{
|
43
|
+
"fill_with": "EXTERNAL_DATA",
|
44
|
+
"lock_with": "KEY['abc_joint_consent_key']",
|
45
|
+
"unlock_with": "KEY['abc_joint_consent_key']",
|
46
|
+
"contents": ""
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
"""
|
51
|
+
And Consent keys for parties A, B, and C
|
52
|
+
When I lock a message in a vault using a split key
|
53
|
+
Then I can recover the message if each party gives consent
|
54
|
+
And I cannot recover the message if one party fails to give consent
|
@@ -0,0 +1,51 @@
|
|
1
|
+
Feature: Unlocked
|
2
|
+
|
3
|
+
```javascript
|
4
|
+
"lock_with": "UNLOCKED",
|
5
|
+
"unlock_with": "UNLOCKED"
|
6
|
+
```
|
7
|
+
|
8
|
+
This keyword should be used to make vault contents accessable to anyone.
|
9
|
+
|
10
|
+
When the contract interpreter reads this keyword it simply hashes the actual string _"UNLOCKED"_ and uses the resulting digest as the symmetric vault key.
|
11
|
+
|
12
|
+
Scenario: Transfer Key Via Unlocked Vault
|
13
|
+
Given the blank contract:
|
14
|
+
"""javascript
|
15
|
+
{
|
16
|
+
"header": {},
|
17
|
+
"vaults": {
|
18
|
+
"random_vault_key":{
|
19
|
+
"description":"Random Number",
|
20
|
+
"fill_with": "RANDOM_NUMBER",
|
21
|
+
"lock_with": "MASTER_PASSPHRASE",
|
22
|
+
"unlock_with": "MASTER_PASSPHRASE",
|
23
|
+
"contents": ""
|
24
|
+
},
|
25
|
+
"message_locked_with_random":{
|
26
|
+
"description":"A simple message locked with a random number",
|
27
|
+
"fill_with": "EXTERNAL_DATA",
|
28
|
+
"lock_with": "KEY['random_vault_key']",
|
29
|
+
"unlock_with": "KEY['random_vault_key']",
|
30
|
+
"contents": ""
|
31
|
+
},
|
32
|
+
"unlocked_random_key":{
|
33
|
+
"description":"An unlocked random key",
|
34
|
+
"fill_with": "KEY['random_vault_key']",
|
35
|
+
"lock_with": "UNLOCKED",
|
36
|
+
"unlock_with": "UNLOCKED",
|
37
|
+
"contents": ""
|
38
|
+
},
|
39
|
+
"message_locked_with_unlocked_random_number":{
|
40
|
+
"fill_with": "CONTENTS['message_locked_with_random']",
|
41
|
+
"lock_with": "KEY['unlocked_random_key']",
|
42
|
+
"unlock_with": "KEY['unlocked_random_key']",
|
43
|
+
"contents": ""
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
"""
|
48
|
+
When I lock away a random vault key
|
49
|
+
And I use the random key to lock a message
|
50
|
+
And I put this random key in an unlocked vault
|
51
|
+
Then another user can recover the message with the Unlocked Random Key
|
@@ -0,0 +1,84 @@
|
|
1
|
+
Contracts are a central part of the [Vault Tree Project]. Here is what you
|
2
|
+
need to know:
|
3
|
+
|
4
|
+
* A Vault Tree Contract is simply a [JSON] text file
|
5
|
+
* Every contract is composed of two parts:
|
6
|
+
- The **Header** section, which includes helpful meta data
|
7
|
+
- The **Vaults** section, which can be any collection of _vaults_ that form the
|
8
|
+
contract.
|
9
|
+
* The way in which you, the contract author, organize the vaults will determine the **Self-Enforcing Terms** of your contract.
|
10
|
+
* Each vault will typically contain either an **external data** string that is provided by one of the contract
|
11
|
+
participants, or a key to anther vault.
|
12
|
+
|
13
|
+
### Writing and Simulating Contracts
|
14
|
+
|
15
|
+
If you've made it to this far, then you're ready to build some stuff. You're
|
16
|
+
thinking to yourself:
|
17
|
+
|
18
|
+
* For my Vault Tree Contract to be useful it probably needs to involve more than one person.
|
19
|
+
* I think I can write a contract, but I really need a way to test it out and think through all the different scenarios.
|
20
|
+
* When my final contract is complete it might involve network calls to pass it
|
21
|
+
between parties, queries the Bitcoin Block Chain, or some other crazy step involving the outside world.
|
22
|
+
|
23
|
+
What I really need is a way to **Simulate** how the contract will be used in real life ...
|
24
|
+
|
25
|
+
Enter [Cucumber].
|
26
|
+
|
27
|
+
[Cucumber]: https://github.com/cucumber/cucumber
|
28
|
+
|
29
|
+
Cucumber is a tool designed to test complicated full stack web applications. However, we are going to use it for a slightly different purpose.
|
30
|
+
|
31
|
+
Take a look at this simple example:
|
32
|
+
|
33
|
+
```Gherkin
|
34
|
+
Scenario: Alice and Bob Execute the One Two Three Contract
|
35
|
+
Given Alice has the blank contract
|
36
|
+
When she locks all of her attributes
|
37
|
+
And she sends the contract to Bob
|
38
|
+
Then Bob can access all of her public attributes
|
39
|
+
When Bob locks his attributes
|
40
|
+
And He fills and locks each of the three main vaults
|
41
|
+
Then Alice can execute the contract to recover the final message
|
42
|
+
```
|
43
|
+
|
44
|
+
Great. So we wrote down how the contract is used in some funny looking format ... so what.
|
45
|
+
|
46
|
+
Well, what if we associate each one of these steps in the scenario with some simple Ruby code that interacts with the Vault Tree API. Here are the first three step definitions:
|
47
|
+
|
48
|
+
```Ruby
|
49
|
+
# This file: "features/core/one_two_three/one_two_three.steps.rb"
|
50
|
+
# Associated Contract: "core/one_two_three.0.7.0.json"
|
51
|
+
|
52
|
+
Given(/^Alice has the blank contract$/) do
|
53
|
+
contract_path = VaultTree::ContractsRepo::PathHelpers.core_contracts('one_two_three.0.7.0.json')
|
54
|
+
@contract_json = File.read(contract_path)
|
55
|
+
end
|
56
|
+
|
57
|
+
When(/^she locks all of her attributes$/) do
|
58
|
+
@contract = VaultTree::Contract.new(@contract_json, master_passphrase: 'ALICE_SECURE_PASS', external_data: {})
|
59
|
+
@contract = @contract.close_vault('alice_decryption_key')
|
60
|
+
@contract = @contract.close_vault('alice_public_encryption_key')
|
61
|
+
end
|
62
|
+
|
63
|
+
When(/^she sends the contract to Bob$/) do
|
64
|
+
@contract_json = @contract.as_json
|
65
|
+
@bobs_external_data = {"congratulations_message" => "CONGRATS! YOU OPENED THE THIRD VAULT."}
|
66
|
+
@contract = VaultTree::Contract.new(@contract_json, master_passphrase: 'BOB_SECURE_PASS', external_data: @bobs_external_data)
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
Not only can we easily run the contract throught the library to test that it
|
71
|
+
works, we have a straight forward mechanism for simulating otherwise complicated
|
72
|
+
steps like sending the JSON representation of the contact _over the wire_.
|
73
|
+
|
74
|
+
Some items to keep in mind when you run Cucumber scenarios:
|
75
|
+
|
76
|
+
* Run `rake` instead of `cucumber` when in the VM `/vagrant` dir
|
77
|
+
* This will:
|
78
|
+
- Take care of the wierd cucumber configuration flags needed to handle the unconventional directory structure.
|
79
|
+
- Execute all cucumber scenarios associated with contracts in the `/core` directory
|
80
|
+
|
81
|
+
[JSON]: http://www.json.org
|
82
|
+
[Vault Tree Homepage]: http://www.vault-tree.org
|
83
|
+
[Vault Tree Project]: http://www.vault-tree.org
|
84
|
+
|
data/features/readme.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
Given(/^Alice has the blank asymmetric vault contract$/) do
|
2
|
+
contract_path = VaultTree::PathHelpers.core_contracts('asymmetric_vault.0.1.0.json')
|
3
|
+
@contract_json = File.read(contract_path)
|
4
|
+
end
|
5
|
+
|
6
|
+
When(/^she locks all of her public and private keys$/) do
|
7
|
+
@contract = VaultTree::Contract.new(@contract_json, master_passphrase: 'ALICE_SECURE_PASS', external_data: {})
|
8
|
+
@contract = @contract.close_vault('alice_contract_secret')
|
9
|
+
@contract = @contract.close_vault('alice_decryption_key')
|
10
|
+
@contract = @contract.close_vault('alice_public_encryption_key')
|
11
|
+
end
|
12
|
+
|
13
|
+
When(/^she sends the contract to Bob over the internet$/) do
|
14
|
+
@contract_json = @contract.as_json
|
15
|
+
@bobs_external_data = {"message" => "CONGRATS ALICE! YOU UNLOCKED THE SECRET MESSAGE WITH A DH KEY."}
|
16
|
+
@contract = VaultTree::Contract.new(@contract_json, master_passphrase: 'BOB_SECURE_PASS', external_data: @bobs_external_data)
|
17
|
+
end
|
18
|
+
|
19
|
+
Then(/^Bob can access of her public keys but not her private keys$/) do
|
20
|
+
@contents = @contract.retrieve_contents('alice_public_encryption_key')
|
21
|
+
end
|
22
|
+
|
23
|
+
When(/^Bob locks his public and private keys$/) do
|
24
|
+
@contract = @contract.close_vault('bob_decryption_key')
|
25
|
+
@contract = @contract.close_vault('bob_public_encryption_key')
|
26
|
+
end
|
27
|
+
|
28
|
+
When(/^He fills and locks the vault containing the message using a DH_KEY$/) do
|
29
|
+
@contract = @contract.close_vault('message')
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
When(/^he sends the contract back to Alice over the internet$/) do
|
34
|
+
@contract_json = @contract.as_json
|
35
|
+
@contract = VaultTree::Contract.new(@contract_json, master_passphrase: 'ALICE_SECURE_PASS', external_data: {})
|
36
|
+
end
|
37
|
+
|
38
|
+
Then(/^Alice can unlock the message with a DH_KEY$/) do
|
39
|
+
puts @contract.retrieve_contents('message')
|
40
|
+
@contract.retrieve_contents('message').should == @bobs_external_data['message']
|
41
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
Given(/^the SENDER has the blank contract template$/) do
|
2
|
+
contract_path = VaultTree::PathHelpers.core_contracts('block_chain_key_transfer.0.1.0.json')
|
3
|
+
@contract_json = File.read(contract_path)
|
4
|
+
end
|
5
|
+
|
6
|
+
Given(/^the SENDER chooses an origin address and a concealed destination address$/) do
|
7
|
+
@sender_external_data =
|
8
|
+
{
|
9
|
+
'sender_origin_wallet_address' => '1XJEBF8EUBF855NEBHVENPFE9JE74E',
|
10
|
+
'sender_concealed_destination_wallet_address' => '1JVKE8HD5JDHFEJHF678JEH8DEJGHE',
|
11
|
+
'sender_btc_signing_key' => 'BITCOIN_SIGNING_KEY_KEEP_IT_SECRET'
|
12
|
+
}
|
13
|
+
@contract = VaultTree::Contract.new(@contract_json, master_passphrase: 'SENDER_SECURE_PASS', external_data: @sender_external_data)
|
14
|
+
@contract = @contract.close_vault('sender_origin_wallet_address')
|
15
|
+
@contract = @contract.close_vault('sender_concealed_destination_wallet_address')
|
16
|
+
end
|
17
|
+
|
18
|
+
Given(/^he locks away the secret BTC signing key$/) do
|
19
|
+
@contract = @contract.close_vault('sender_btc_signing_key')
|
20
|
+
end
|
21
|
+
|
22
|
+
When(/^the SENDER transfers the contract to the RECEIVER$/) do
|
23
|
+
@contract_json_over_the_wire = @contract.as_json
|
24
|
+
@contract = VaultTree::Contract.new(@contract_json_over_the_wire, master_passphrase: 'RECEIVER_SECURE_PASS')
|
25
|
+
end
|
26
|
+
|
27
|
+
Then(/^the RECEIVER can access the origin wallet address$/) do
|
28
|
+
@contract.retrieve_contents('sender_origin_wallet_address').should == @sender_external_data['sender_origin_wallet_address']
|
29
|
+
end
|
30
|
+
|
31
|
+
When(/^the SENDER reveals the hidden wallet address by transfering bitcoins from the origin address$/) do
|
32
|
+
@contract_json = @contract.as_json # save the json state
|
33
|
+
wallet_address_from_watching_blockchain = @sender_external_data['sender_concealed_destination_wallet_address']
|
34
|
+
@receiver_external_data = { 'receiver_revealed_destination_wallet_address' => wallet_address_from_watching_blockchain}
|
35
|
+
@contract = VaultTree::Contract.new(@contract_json, master_passphrase: 'RECEIVER_SECURE_PASS', external_data: @receiver_external_data)
|
36
|
+
@contract = @contract.close_vault('receiver_revealed_destination_wallet_address')
|
37
|
+
end
|
38
|
+
|
39
|
+
Then(/^the RECEIVER can unlock the vault to recover the transfered signing key$/) do
|
40
|
+
transfered_secret_key = @contract.retrieve_contents('sender_btc_signing_key')
|
41
|
+
transfered_secret_key.should == @sender_external_data['sender_btc_signing_key']
|
42
|
+
puts "PROPERLY TRANSFERED: #{transfered_secret_key} !"
|
43
|
+
end
|
@@ -1,105 +1,3 @@
|
|
1
|
-
Given(/^Alice has the blank contract$/) do
|
2
|
-
contract_path = VaultTree::PathHelpers.reference_contract
|
3
|
-
@contract_json = File.read(contract_path)
|
4
|
-
end
|
5
|
-
|
6
|
-
# Change this to just attributes vice public attributes
|
7
|
-
When(/^she locks all of her attributes$/) do
|
8
|
-
@contract = VaultTree::Contract.new(@contract_json, master_passphrase: 'ALICE_SECURE_PASS', external_data: {})
|
9
|
-
@contract = @contract.close_vault('alice_decryption_key')
|
10
|
-
@contract = @contract.close_vault('alice_public_encryption_key')
|
11
|
-
end
|
12
|
-
|
13
|
-
When(/^she sends the contract to Bob$/) do
|
14
|
-
@contract_json = @contract.as_json
|
15
|
-
@bobs_external_data = {"congratulations_message" => "CONGRATS! YOU OPENED THE THIRD VAULT."}
|
16
|
-
@contract = VaultTree::Contract.new(@contract_json, master_passphrase: 'BOB_SECURE_PASS', external_data: @bobs_external_data)
|
17
|
-
end
|
18
|
-
|
19
|
-
Then(/^Bob can access her public attributes$/) do
|
20
|
-
@contents = @contract.retrieve_contents('alice_public_encryption_key')
|
21
|
-
end
|
22
|
-
|
23
|
-
When(/^Bob locks his attributes$/) do
|
24
|
-
|
25
|
-
@contract = @contract.close_vault('bob_decryption_key')
|
26
|
-
# Verify can reopen
|
27
|
-
@contract.retrieve_contents('bob_decryption_key')
|
28
|
-
|
29
|
-
@contract = @contract.close_vault('congratulations_message')
|
30
|
-
# Verify can reopen
|
31
|
-
@contract.retrieve_contents('congratulations_message')
|
32
|
-
|
33
|
-
@contract = @contract.close_vault('vault_two_key')
|
34
|
-
# Verify they can reopen
|
35
|
-
@contract.retrieve_contents('vault_two_key')
|
36
|
-
|
37
|
-
@contract = @contract.close_vault('vault_three_key')
|
38
|
-
# Verify they can reopen
|
39
|
-
@contract.retrieve_contents('vault_three_key')
|
40
|
-
|
41
|
-
@contract = @contract.close_vault('bob_public_encryption_key')
|
42
|
-
# Verify they can reopen
|
43
|
-
@contract.retrieve_contents('bob_public_encryption_key')
|
44
|
-
end
|
45
|
-
|
46
|
-
When(/^He fills and locks each of the three vaults$/) do
|
47
|
-
@contract = @contract.close_vault('first')
|
48
|
-
@contract = @contract.close_vault('second')
|
49
|
-
@contract = @contract.close_vault('third')
|
50
|
-
end
|
51
|
-
|
52
|
-
Then(/^Alice can execute the contract to recover the final message$/) do
|
53
|
-
@contract_json = @contract.as_json
|
54
|
-
@contract = VaultTree::Contract.new(@contract_json, master_passphrase: 'ALICE_SECURE_PASS', external_data: {})
|
55
|
-
puts @contract.retrieve_contents('third')
|
56
|
-
@contract.retrieve_contents('third').should == @bobs_external_data['congratulations_message']
|
57
|
-
end
|
58
|
-
|
59
|
-
Given(/^the SENDER has the blank BTC Key Transfer template$/) do
|
60
|
-
contract_path = VaultTree::PathHelpers.reference_contract
|
61
|
-
@contract_json = File.read(contract_path)
|
62
|
-
end
|
63
|
-
|
64
|
-
Given(/^the SENDER chooses an origin wallet address and concealed destination address$/) do
|
65
|
-
@sender_external_data =
|
66
|
-
{
|
67
|
-
'sender_origin_wallet_address' => '1XJEBF8EUBF855NEBHVENPFE9JE74E',
|
68
|
-
'sender_concealed_destination_wallet_address' => '1JVKE8HD5JDHFEJHF678JEH8DEJGHE',
|
69
|
-
'sender_btc_signing_key' => 'BITCOIN_SIGNING_KEY_KEEP_IT_SECRET'
|
70
|
-
}
|
71
|
-
@contract = VaultTree::Contract.new(@contract_json, master_passphrase: 'SENDER_SECURE_PASS', external_data: @sender_external_data)
|
72
|
-
@contract = @contract.close_vault('sender_origin_wallet_address')
|
73
|
-
@contract = @contract.close_vault('sender_concealed_destination_wallet_address')
|
74
|
-
end
|
75
|
-
|
76
|
-
Given(/^he locks away the secret BTC signing key$/) do
|
77
|
-
@contract = @contract.close_vault('sender_btc_signing_key')
|
78
|
-
end
|
79
|
-
|
80
|
-
When(/^the SENDER transfers the Vault\-Tree contract to the RECEIVER$/) do
|
81
|
-
@contract_json_over_the_wire = @contract.as_json
|
82
|
-
@contract = VaultTree::Contract.new(@contract_json_over_the_wire, master_passphrase: 'RECEIVER_SECURE_PASS')
|
83
|
-
end
|
84
|
-
|
85
|
-
Then(/^the RECEIVER can access the origin wallet address$/) do
|
86
|
-
@contract.retrieve_contents('sender_origin_wallet_address').should == @sender_external_data['sender_origin_wallet_address']
|
87
|
-
end
|
88
|
-
|
89
|
-
When(/^the SENDER reveals the hidden wallet address by Blockchain payment from the origin address$/) do
|
90
|
-
@contract_json = @contract.as_json # save the json state
|
91
|
-
wallet_address_from_watching_blockchain = @sender_external_data['sender_concealed_destination_wallet_address']
|
92
|
-
@receiver_external_data = { 'receiver_revealed_destination_wallet_address' => wallet_address_from_watching_blockchain}
|
93
|
-
@contract = VaultTree::Contract.new(@contract_json, master_passphrase: 'RECEIVER_SECURE_PASS', external_data: @receiver_external_data)
|
94
|
-
@contract = @contract.close_vault('receiver_revealed_destination_wallet_address')
|
95
|
-
end
|
96
|
-
|
97
|
-
Then(/^the RECEIVER can unlock the vault to recover the transfered signing key$/) do
|
98
|
-
transfered_secret_key = @contract.retrieve_contents('sender_btc_signing_key')
|
99
|
-
transfered_secret_key.should == @sender_external_data['sender_btc_signing_key']
|
100
|
-
puts "PROPERLY TRANSFERED: #{transfered_secret_key} !"
|
101
|
-
end
|
102
|
-
|
103
1
|
Given(/^I have a blank reference contract$/) do
|
104
2
|
contract_path = VaultTree::PathHelpers.reference_contract
|
105
3
|
@contract_json = File.read(contract_path)
|
@@ -151,7 +49,7 @@ Given(/^I have access to the another user's unlocked public key$/) do
|
|
151
49
|
@contract = @contract.close_vault('my_public_key')
|
152
50
|
end
|
153
51
|
|
154
|
-
Given(/^I lock a simple message with a
|
52
|
+
Given(/^I lock a simple message with a DH Key$/) do
|
155
53
|
@contract_json = @contract.as_json
|
156
54
|
@external_data = {"asymmetric_message" => "CONGRATS! YOU OPENED THE ASYMMETRIC VAULT."}
|
157
55
|
@contract = VaultTree::Contract.new(@contract_json, master_passphrase: 'MY_SECURE_PASS', external_data: @external_data)
|
@@ -163,6 +61,61 @@ When(/^I transfer the contract to the other user$/) do
|
|
163
61
|
@contract = VaultTree::Contract.new(@contract_json, master_passphrase: 'ANOTHER_USERS_SECURE_PASS')
|
164
62
|
end
|
165
63
|
|
166
|
-
Then(/^they can create a
|
64
|
+
Then(/^they can create a DH Key and unlock the message$/) do
|
167
65
|
@contract.retrieve_contents('asymmetric_message').should == @external_data['asymmetric_message']
|
168
66
|
end
|
67
|
+
|
68
|
+
Given(/^Consent keys for parties A, B, and C$/) do
|
69
|
+
@locking_consent_keys = {
|
70
|
+
"a_consent_key" => "A_SECRET_CONSENT_KEY",
|
71
|
+
"b_consent_key" => "B_SECRET_CONSENT_KEY",
|
72
|
+
"c_consent_key" => "C_SECRET_CONSENT_KEY"
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
When(/^I lock a message in a vault using a split key$/) do
|
77
|
+
@message = {"abc_consent_message" => "A, B, AND C ALL AGREED TO OPEN THE VAULT." }
|
78
|
+
@external_data = @locking_consent_keys.merge(@message)
|
79
|
+
@contract = VaultTree::Contract.new(@contract_json, external_data: @external_data)
|
80
|
+
@contract = @contract.close_vault('a_consent_key')
|
81
|
+
@contract = @contract.close_vault('b_consent_key')
|
82
|
+
@contract = @contract.close_vault('c_consent_key')
|
83
|
+
@contract = @contract.close_vault('abc_joint_consent_key')
|
84
|
+
@contract = @contract.close_vault('abc_consent_message')
|
85
|
+
@contract_json = @contract.as_json
|
86
|
+
end
|
87
|
+
|
88
|
+
Then(/^I can recover the message if each party gives consent$/) do
|
89
|
+
@unlocking_consent_keys = {
|
90
|
+
"a_consent_key" => "A_SECRET_CONSENT_KEY",
|
91
|
+
"b_consent_key" => "B_SECRET_CONSENT_KEY",
|
92
|
+
"c_consent_key" => "C_SECRET_CONSENT_KEY"
|
93
|
+
}
|
94
|
+
@contract = VaultTree::Contract.new(@contract_json, external_data: @unlocking_consent_keys)
|
95
|
+
@contract.retrieve_contents('abc_consent_message').should == @external_data['abc_consent_message']
|
96
|
+
puts @contract.retrieve_contents('abc_consent_message')
|
97
|
+
end
|
98
|
+
|
99
|
+
Then(/^I cannot recover the message if one party fails to give consent$/) do
|
100
|
+
@incomplete_unlocking_consent_keys = {
|
101
|
+
"a_consent_key" => "A_WRONG_SECRET_CONSENT_KEY",
|
102
|
+
"b_consent_key" => "B_SECRET_CONSENT_KEY",
|
103
|
+
"c_consent_key" => "C_SECRET_CONSENT_KEY"
|
104
|
+
}
|
105
|
+
@contract = VaultTree::Contract.new(@contract_json, external_data: @incomplete_unlocking_consent_keys)
|
106
|
+
expect{@contract.retrieve_contents('abc_consent_message')}.to raise_error(VaultTree::Exceptions::FailedUnlockAttempt)
|
107
|
+
end
|
108
|
+
|
109
|
+
Given(/^the blank contract:$/) do |string|
|
110
|
+
@contract_json = string
|
111
|
+
end
|
112
|
+
|
113
|
+
When(/^I lock a message in a vault using a symmetric vault key$/) do
|
114
|
+
@external_data = {"message" => "CONGRATS! YOU OPENED THE VAULT."}
|
115
|
+
@contract = VaultTree::Contract.new(@contract_json, master_passphrase: 'MY_SECURE_PASS', external_data: @external_data)
|
116
|
+
@contract = @contract.close_vault('message')
|
117
|
+
end
|
118
|
+
|
119
|
+
Then(/^I can recover the message using the same key$/) do
|
120
|
+
@contract.retrieve_contents('message').should == @external_data['message']
|
121
|
+
end
|