1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#![cfg(all(feature = "proof", feature = "salt"))]
use std::collections::HashSet;
use bc_envelope::prelude::*;
use indoc::indoc;
mod common;
#[cfg(feature = "types")]
use bc_components::{ARID, PrivateKeyBase};
#[cfg(feature = "types")]
use hex_literal::hex;
use crate::common::check_encoding::*;
#[cfg(feature = "types")]
use crate::common::test_seed::Seed;
#[test]
fn test_friends_list() {
// This document contains a list of people Alice knows. Each "knows"
// assertion has been salted so if the assertions have been elided one
// can't merely guess at who she knows by pairing the "knows" predicate
// with the names of possibly-known associates and comparing the
// resulting digests to the elided digests in the document.
let alice_friends = Envelope::new("Alice")
.add_assertion_salted("knows", "Bob", true)
.add_assertion_salted("knows", "Carol", true)
.add_assertion_salted("knows", "Dan", true);
// expected-text-output-rubric:
#[rustfmt::skip]
assert_actual_expected!(alice_friends.format(), indoc! {r#"
"Alice" [
{
"knows": "Bob"
} [
'salt': Salt
]
{
"knows": "Carol"
} [
'salt': Salt
]
{
"knows": "Dan"
} [
'salt': Salt
]
]
"#}.trim());
// Alice provides just the root digest of her document to a third party.
// This is simply an envelope in which everything has been elided and
// nothing revealed.
let alice_friends_root = alice_friends.elide_revealing_set(&HashSet::new());
assert_actual_expected!(alice_friends_root.format(), "ELIDED");
// Now Alice wants to prove to the third party that her document contains a
// "knows Bob" assertion. To do this, she produces a proof that is an
// envelope with the minimal structure of digests included so that the
// proof envelope has the same digest as the completely elided envelope,
// but also exposes the digest of the target of the proof.
//
// Note that in the proof the digests of the two other elided "knows"
// assertions are present, but because they have been salted, the third
// party cannot easily guess who else she knows.
let knows_bob_assertion = Envelope::new_assertion("knows", "Bob");
let alice_knows_bob_proof = alice_friends
.proof_contains_target(&knows_bob_assertion)
.unwrap()
.check_encoding()
.unwrap();
// expected-text-output-rubric:
#[rustfmt::skip]
assert_actual_expected!(alice_knows_bob_proof.format(), indoc! {r#"
ELIDED [
ELIDED [
ELIDED
]
ELIDED (2)
]
"#}.trim());
// The third party then uses the previously known and trusted root to
// confirm that the envelope does indeed contain a "knows bob"
// assertion.
assert!(
alice_friends_root.confirm_contains_target(
&knows_bob_assertion,
&alice_knows_bob_proof
)
);
}
#[test]
fn test_multi_position() {
let alice_friends = Envelope::new("Alice")
.add_assertion_salted("knows", "Bob", true)
.add_assertion_salted("knows", "Carol", true)
.add_assertion_salted("knows", "Dan", true);
// In some cases the target of a proof might exist at more than one position
// in an envelope. An example target from Alice's list of friends would
// be any envelope containing "knows" as its subject. Since all three
// "knows" assertions use this as their predicate, that identical
// envelope exists in three different positions in the outer envelope.
//
// Note that revealing all positions of the "knows" predicate in this
// envelope also reveals the digest of the salt for each assertion,
// which might make Alice's other associates easier to guess.
let knows_proof = alice_friends
.proof_contains_target(&Envelope::new("knows"))
.unwrap()
.check_encoding()
.unwrap();
// expected-text-output-rubric:
#[rustfmt::skip]
assert_actual_expected!(knows_proof.format(), indoc! {r#"
ELIDED [
{
ELIDED: ELIDED
} [
ELIDED
]
{
ELIDED: ELIDED
} [
ELIDED
]
{
ELIDED: ELIDED
} [
ELIDED
]
]
"#}.trim());
}
#[test]
#[cfg(feature = "types")]
fn test_verifiable_credential() {
let alice_seed = Seed::new(hex!("82f32c855d3d542256180810797e0073"));
let alice_private_key = PrivateKeyBase::from_data(alice_seed.data());
let arid = Envelope::new(
ARID::from_data_ref(hex!(
"4676635a6e6068c2ef3ffd8ff726dd401fd341036e920f136a1d8af5e829496d"
))
.unwrap(),
);
let credential = arid
.add_assertion_salted("firstName", "John", true)
.add_assertion_salted("lastName", "Smith", true)
.add_assertion_salted("address", "123 Main St.", true)
.add_assertion_salted(
"birthDate",
Date::from_string("1970-01-01").unwrap(),
true,
)
.add_assertion_salted("photo", "This is John Smith's photo.", true)
.add_assertion_salted("dlNumber", "123-456-789", true)
.add_assertion_salted("nonCommercialVehicleEndorsement", true, true)
.add_assertion_salted("motorocycleEndorsement", true, true)
.add_assertion(known_values::ISSUER, "State of Example")
.add_assertion(known_values::CONTROLLER, "State of Example")
.wrap()
.add_signature(&alice_private_key)
.add_assertion(known_values::NOTE, "Signed by the State of Example");
let credential_root = credential.elide_revealing_set(&HashSet::new());
// In this case the holder of a credential wants to prove a single assertion
// from it, the address.
let address_assertion = Envelope::new_assertion("address", "123 Main St.");
let address_proof = credential
.proof_contains_target(&address_assertion)
.unwrap()
.check_encoding()
.unwrap();
// The proof includes digests from all the elided assertions.
// expected-text-output-rubric:
#[rustfmt::skip]
assert_actual_expected!(address_proof.format(), indoc! {r#"
{
ELIDED [
ELIDED [
ELIDED
]
ELIDED (9)
]
} [
ELIDED (2)
]
"#}.trim());
// The proof confirms the address, as intended.
assert!(
credential_root
.confirm_contains_target(&address_assertion, &address_proof)
);
// Assertions without salt can also be confirmed.
let issuer_assertion =
Envelope::new_assertion(known_values::ISSUER, "State of Example");
assert!(
credential_root
.confirm_contains_target(&issuer_assertion, &address_proof)
);
// The proof cannot be used to confirm salted assertions.
let first_name_assertion = Envelope::new_assertion("firstName", "John");
assert!(
!credential_root
.confirm_contains_target(&first_name_assertion, &address_proof)
);
}