forked from openssi/peer-did-method-spec
-
Notifications
You must be signed in to change notification settings - Fork 0
/
did-docs.html
648 lines (595 loc) · 39.3 KB
/
did-docs.html
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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
<h1>DID docs</h1>
<section>
<h2>Backing Storage</h2>
<p>At first glance, it might seem that parties dealing in peer DID docs could just store raw DID docs.
However, this is only a viable answer for the most modest <a href="#levels-of-support">levels of support</a>
for peer DIDs. Any time a DID doc evolves, proof that the evolution is authorized must be found
in the DID doc's previous state. If an agent is offline for an extended period (e.g., a phone is lost in the
couch cushions for a week), multiple evolutions may have occurred by the time it reconnects--and it cannot
accept the latest state of the doc without validating the sequence of changes it underwent to get there. Agents
must be able to prove to one another that the state they hold is correct, and they must be able to answer
questions about <em>what</em> the state used to be, and <em>who</em> authorized <em>which</em> changes, <em>when
</em>.
</p>
<p>This means that peer DID docs need to be associated with some type of <dfn>backing storage</dfn> that adds
metadata and history to the simple content of the docs themselves.
</p>
<p>The backing storage for peer DID docs is therefore a sequence of <dfn>deltas</dfn>. Each delta is a JSON object
in this form:</p>
<pre class="example nohighlight" title="Delta structure">
{
"change": <base64 encoding of a change fragment>,
"by": [ {"key": <id of key>, "sig": <signature value>} ... ],
"when": <ISO8601 UTC timestamp with at least second precision>
}
</pre>
<p>Here, a <dfn>change fragment</dfn> is a sparse version of a DID doc that shows just the section being
updated. The <code>by</code> property contains one or more key+signature pairs over the raw (non-base64-encoded)
JSON text of the change fragment, and <code>when</code> contains a value supplied by the system clock. This
value is like timestamps on an email header—not guaranteed to be highly accurate, but useful for rough
analysis.
</p>
<p>Deltas are uniquely identified by the SHA256 hash of the raw (non-base64-encoded) bytes of their change fragment.
</p>
<p>Deltas are like database transactions, in that they may embody multiple changes that must be performed together.
For example, key rotation involves both a key addition and a key deletion, performed as a unit.
All changes in a single delta MUST share a common authorization; it is invalid to combine into one delta changes
requiring two different authorizations, as this makes authorization of each individual change ambiguous.
</p>
<p>A resolved peer DID doc at any given point in time is a view or projection of the cumulative effect of all
known deltas up to that point, with resolution adding the DID value itself to the composite <a>stored
variant</a> of DID doc data.
</p>
<p class="note">Early explorations of this DID method used the term <dfn>microledger</dfn> for backing storage. This
term has some resonance, in that backing storage is an append-only record of immutable transactions. However,
we have chosen not to emphasize the term here, because it may feel intimidating to some, and because the actual
storage mechanism in an implementation of this method could be a simple file, a database, or anything else that
provides equivalent features.
</p>
<p>In the discussion about generating a <a>numeric basis</a> for a peer DID, the <a>genesis version</a> of a peer
DID doc was defined. The genesis version is the one associated with the first delta committed to backing
storage. In the first delta, <code>change</code> contains the genesis state of the doc, <code>by</code> holds
a signature from any key defined internally in the genesis state, and <code>when</code> is populated by the
system clock.
</p>
<p class="note">The genesis state can be authorized by <em>any</em> internally defined key, even if that key has a very
limited <a>trust profile</a>. This is because the authorization for genesis state is only proving that the
creator of the DID doc has not been shadowed by a malicious man-in-the-middle; no other test of authorization
is performed.
</p>
</section>
<section>
<h2>CRDTs</h2>
<p>Because peer DID docs can be updated by multiple parties at the same time (as when two agents of the same
identity owner rotate their keys without coordinating), they have the potential for merge conflicts. Resolving
these merge conflicts could introduce much complexity in our DID method. However, peer DID docs use a clever
technique that avoids much of the pain. The backing storage for peer DID docs is carefully defined to
be a <a target="wikipedia" href="https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type">conflict-free
replicated data type</a> (<dfn>CRDT</dfn>).
</p>
<p>CRDTs are a deep subject unto themselves, with a body of academic research; additional study is recommended.
They are behind familiar experiences like simultaneous, distributed editing of Google Docs, which never raises
error messages about merge conflicts. Here, we will not explain their theory; we will simply note some key
requirements that they impose on DID docs and their <a>backing storage</a>. More details about given later in
this spec, where individual parts of the DID doc are described. The general rules are:
</p>
<ol id="crdt-rules">
<li>Core building blocks—keys, rules, and service endpoints— must be assigned
<code>id</code> properties that are unique within the DID doc, across all its versions.</li>
<li>All items with <code>id</code> properties must be immutable. They can be deleted, but they can never have
a different state than they were created with. The effect of modifying an item can be achieved by
deleting an old item and replacing it with a new item that has a different <code>id</code> and updated
properties.</li>
<li>Deleting an item is accomplished by adding its <code>id</code> to a <dfn>deletion list</dfn>. This list
is a root property of the <a>stored variant</a> of a DID doc and is named <code>deleted</code>. It is
suppressed when generating a <a>resolved variant</a> of the DID doc, so it is only visible in the deltas
of <a>backing storage</a>. See <a href="#deleting-a-publickey">this clarifying example</a>.</li>
<li>When any party encounters two versions of a peer DID doc that are mututally incompatible, an impartial,
ungameable resolution algorithm is used to restore harmony. See next subsection.
</li>
</ol>
<section>
<h3>Conflict Resolution</h3>
<p>These rules do not make conflicts impossible. However, they should make conflicts from non-malicious activity
quite rare, and they make the order in which deltas are applied mostly uninteresting. Uncoordinated actions
that add keys, rules, and service endpoints can never cause conflicts. Holders of keys should be able to
operate with high autonomy and minimal coordination, even during extended periods of imperfect
synchronization.
</p>
<p class="note">A key rotation is a bit different. It has to be treated like a deletion as far as ordering rules
are concerned, since it does in fact include a deletion. More on this in a moment.</p>
<p>A reasonable analog might be a git repo that only allows 1 version of each file--no modifications, no
renames--and that guarantees that independent coders never add files with the same names. Such a repo could
probably accept commits in any order, from an unlimited number of collaborators, and largely avoid
conflicts.
</p>
<p>The real difficulty is deletions. Suppose AcmeCorp has two agents, <code>A.1</code> and <code>A.2</code>, and
that it uses these agents to interact with Bob, via Bob's agent <code>B.1</code>. <code>A.2</code> is hacked.
<code>A.1</code> becomes suspicious and decides to take protective action. <code>A.1</code> creates a delta,
<code>delta[1]</code>, that revokes <code>A.2</code>'s keys, and sends it to <code>B.1</code>. Meanwhile,
the hacker on <code>A.2</code> is moving swiftly to consolidate his own advantage; he uses <code>A.2
</code>'s leaked key to create a different delta, <code>delta[2]</code>, that revokes <code>A.1</code>'s
keys. Each of these deltas is authorized by the very party that the other delta de-authorizes.
</p>
<p>So what does <code>B.1</code> do when it receives these contradictory deltas?
</p>
<figure id="contradictory-deletes">
<img src="contradictory-deletes.png" alt="A.1 says delete A.2; A.2 says the opposite"/>
<figcaption>Mutually contradictory deletes.</figcaption>
</figure>
<p>Before answering this question, let's reframe the situation into a less technical context, because it will
make the answer easier to understand. Change AcmeCorp into a country, Acmeland, and its agents into
diplomats that represent Acmeland in the Republic of Bob. One of the Acme diplomats becomes treasonous. Now
the Republic of Bob receives two contradictory letters, both on official Acmeland letterhead, from A.1 and
A.2. Each letter instructs the Republic of Bob to strip the other Acme diplomat of all rank and privileges.
</p>
<p>So what does the Republic of Bob do when it receives these contradictory letters?
</p>
<p>Part 1 of the answer is that <em>it processes them in the order it receives them</em>. If <code>A.1</code>'s
delta arrives first, <code>A.2</code> loses rank and privileges, and the letter from <code>A.2</code> must
be rejected. If the order changes, so does the outcome.
</p>
<p>If Acmeland is worried about this, it can prearrange with the Republic of Bob that all changes to its
diplomat corps will always come endorsed by multiple officials. AcmeCorp can setup similar M-of-N signature
policies in the <a>authorization</a> section of its DID doc. Wise planning can largely eliminate the
problems with this simple first-come-first-served strategy.
</p>
<p>But let's assume that planning hasn't been perfect. And let's make the question less of a corner case. What
if the two messages that arrive are not mutually contradictory--but one is a delete and another is an
operation that can't succeed if the delete happens first?
</p>
<figure id="delete-order-ex">
<img src="delete-order-ex.png" alt="A.1 says delete A.2; A.2 says to add a rule"/>
<figcaption>A delete and another operation, where order matters.</figcaption>
</figure>
<p>Again, the party that receives these two deltas processes them in the order they are received. In a
decentralized system, no other answer makes sense. This might feel like a terrible answer, since different
parties can receive what started as the same sequence of deltas in very different orders, and thus end up
with very different views of the world. But we can add a nuance that makes this feel more rational.
</p>
<p>Part 2 of the answer starts with the observation that <em>the relative order of two operations only matters
when it affects authorization</em>. If an official for the Republic of Bob sees a letter announcing that
Acmeland's ambassador Foo has been fired, and another letter announcing that Acmeland's ambassador Foo
requested an appointment with Bob's prime minister, the official doesn't care about the relative order
unless she's in charge of scheduling appointments with Bob's prime minister. Otherwise she can suspend
judgment about whether Foo's appointment request should be honored, and wait to see how the authorization
decision is made by someone else who's empowered to make it.
</p>
<p>The <a href="#sync-protocol">synchronization protocol</a> between parties to a DID doc allows them to have
different opinions about order, and to adjust that order over time, based on evidence. A delete delta
"bubbles" to a position in history that's after the last delta that would have been impossible before it,
AND that was accepted by any party that had to make authorization decisions. A delete delta that was never
accepted by anyone (as with one of the mutually contradictory deltas above) just disappers. And deltas that
were discarded by a party because of ordering differences will get re-introduced to history once evidence is
found that a party making authorization decisions saw the order differently. This is essentially like the
Republic of Bob official saying, "For now, I'm going to pretend that Foo's appointment request is rejected,
since I saw it after the notice that Foo was fired. But if I see that my boss has scheduled the appointment,
I'll put the appointment request into my official log of Acmeland actions in the other order, because my
boss must have received the letters in the other order."
</p>
<p>The net effect of these two rules for deletes is that different parties can have different views of where
a delete fits into history, and those views will converge over time, <em>to the extent that convergence
matters</em>. Where convergence never reaches closure, that means there's no evidence that forces one of
the views to be more correct than the other. And that's okay.
</p>
<p>You may be saying, "Yes, but what if the ambassador was fired in the morning, and I can prove it, and
someone still gave the ambassador privileges twelve hours later in the evening?" Or what if one party
accepted <code>delta[0]</code>, and another party accepted <code>delta[1]</code> (that were mutually
contradictory)? How do those get reconciled?
</p>
<p>They don't, and it doesn't matter. In the first case, the delete's timing moves to the evening in history;
that's the true last time that the ambassador's privileges had any effect, even if the intent was to
de-authorize earlier. The deltas is not an expression of intention, but an expression of when that intention
was honored in any way that mattered. In the second case, the contradictory views of reality are a real
historical event, and the fact that they caused inconsistent authorizations to happen in different corners
of the world isn't going to be reconciled into an explanation that pretends otherwise. The deltas on top
of the genesis doc end up containing both deletes, as well as some deltas inconsistent with both deletes.
At least once both deletes have been applied, the potential for new inconsistencies collapses. Real life in
a decentralized world is messy like this.
</p>
</section>
</section>
<section>
<h2><code><dfn>publicKey</dfn></code></h2>
<p>The <code>publicKey</code> section of a peer DID document, in either stored or resolved form, is as you would
expect from a reading of the <a target="didspec" href="https://w3c-ccg.github.io/did-spec/#authentication">DID
spec</a>. Peer DID docs MUST define all of their internal and external keys in this section; although the DID
spec permits inline definitions in the <code>authentication</code> and <code>authorization</code> sections of a
DID doc, this DID method removes that option to simplify the possible permutations of a <a>change fragment</a>.
</p>
<p>All <code>id</code> properties associated with a key defined internally in the DID doc MUST be in
relative, not absolute form (that is, "#xyz" instead of "did:peer:abc#xyz"). IDs for keys defined
interally may be generated in either of these two ways:
</p>
<ul>
<li>The first 8 characters of the key's unique representation may be used as the <code>id</code>, if this
provides enough uniqueness within the document. (Global uniqueness is irrelevant.) For example, if the
key is defined with a <code>publicKeyBase58</code> property value that begins with <code>H3C2AVvL</code>,
then its <code>id</code> would be <code>H3C2AVvL</code>; a key with a <code>publicKeyHex</code> property
that begins with <code>02b97c30</code> would have an <code>id</code> of <code>02b97c30</code>, and a key
with a <code>publicKeyPem</code> property that begins, after its <code>-----BEGIN PUBLIC KEY</code>
delimiter, with the value <code>izfrNTmQ</code>, would have an <code>id</code> of <code>izfrNTmQ</code>.
</li>
<li>A standard 128-bit UUID of type 1, 2, 3, 4, or 5 (see [[RFC4122]]), rendered with hyphens and in lower
case, may be used as the<code>id</code> instead.</li>
</ul>
<p class="note">All IDs in DID docs, including these, should be considered case-sensitive. Ignoring case will result
in invalid comparisons for key <code>id</code>s based on encodings that are not hex.
</p>
<p>Because peer DIDs usually operate independent of global registries, external references (where <code>controller</code>
is not <code>#id</code>) will probably be rare, with the possible exception of <a target="aries"
href="https://github.com/hyperledger/aries-rfcs/blob/master/concepts/0046-mediators-and-relays/README.md">
mediator</a> keys that have the <a>route</a> privilege.
</p>
<pre class="example" title="A publicKey section">
{
...
"publicKey": [
{
"id": "H3C2AVvL",
"type": "Ed25519VerificationKey2018",
"controller": "#id",
"publicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"
}, {
"id": "02b97c30",
"type": "Secp256k1VerificationKey2018",
"controller": "#id",
"publicKeyHex": "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71"
}, {
"id": "izfrNTmQ",
"type": "RsaVerificationKey2018",
"controller": "#id",
"publicKeyPem": "-----BEGIN PUBLIC KEY\r\nizfrNTmQ...END PUBLIC KEY-----\r\n"
}
]
...
}
</pre>
<p class="note">This DID method is relatively agnostic with respect to key types. However, elliptic curve
keys (Ed25519, Secp256, etc) are recommended because the keys are smaller, the number
of bits of security are greater, and the computations more efficient, than RSA key types.
</p>
<section>
<h3 id="adding-a-publickey">Adding a key</h3>
<p>To add a new key with <code>id</code> = <code>Mv6gmMNa</code> to a peer DID doc, you might create
a <a>change fragment</a> of the DID doc that looks like this:
</p>
<pre class="example" title="Change fragment that adds a new key">
{
"publicKey": [
{
"id": "Mv6gmMNa",
"type": "Ed25519VerificationKey2018",
"controller": "#id",
"publicKeyBase58": "Mv6gmMNam3uVAjH3C2AVvLZZn6z3wXmqPVpfkcJCwDwn"
}
],
"authorization": { "profiles": [ {"key": "#Mv6gmMNa", "roles", ["edge"]} ] }
}
</pre>
<p>Change fragments only show where something is <em>appended</em> to either normal lists or the <a>deletion
list</a>. Here, the single entries under <code>publicKey</code> and <code>authorization.profiles</code> are
additions, not replacements, to the lists that already contain values. (The <a>trust profile</a> embodied in
the <code>authorization.profiles</code> associates the new key with privileges. This must be done at creation
time. It is discussed under <a href="#authorization"><code>authorization</code></a>.) The <a>delta</a> that
encapsulates this update would take the above JSON text, base64-encode it, and assign the result to the
delta's <code>change</code> property. It would need to be authorized by a key with the <a>key_admin</a>
privilege, evidenced in the delta's <code>by</code> field. The resulting delta might look like this:
</p>
<pre class="example" title="Delta that adds a new key">
{
"id": "040aaa5e-1a27-40d8-8d53-13a00b82d235",
"change": "ewogICJwdWJsaWNLZXkiOiBbCiAgICB...ozd1htcVBWcGZrY0pDd0R3biIKICAgIH0KICBdCn0=",
"by": [ {"key": "H3C2AVvL", "sig": "if8ooA+32YZc4SQBvIDDY9tgTa...i4VvND87PUqq5/0vsNFEGIIEDA=="} ],
"when": "2019-07-18T15:49:22.03Z"
}
</pre>
</section>
<section>
<h3 id="deleting-a-publickey">Deleting a key</h3>
<p>Any key can be deleted by appending its <code>id</code> to the <code>deleted</code> list at the root of
the <a>stored variant</a> of a peer DID doc, and by removing its corresponding entry from the <code>publicKey</code>
section. Only the append-to-<code>deleted</code> operation needs to be represented in the <a>change
fragment</a>; the corresponding removal from the <code>publicKey</code> section is implied. For example, to
delete a key with <code>id</code> = <code>izfrNTmQ</code>, your <a>change fragment</a> would look like
this:
</p>
<pre class="example" title="Change fragment that deletes a key with id=izfrNTmQ">
{
"deleted": [ "izfrNTmQ" ]
}
</pre>
<p>This fragment would be base64-encoded and signed in a corresponding delta, in much the same way that
the fragment was handled in the example about adding a key.</p>
</section>
</section>
<section>
<h2><code><dfn>authentication</dfn></code></h2>
<p>The <code>authentication</code> section of a peer DID document conforms to <a target="didspec"
href="https://w3c-ccg.github.io/did-spec/#authentication">standard practice described in the DID spec</a>,
with the constraint that only references, not inline key definitions, are allowed. Any keys referenced
here may be used to authenticate or login as the DID.
</p>
<p>However, the meaning of "authenticate" or "login" deserves comment, because there is a tendency in DID
circles to reference too many keys in this section, and because heavy use of this section is discouraged
with peer DIDs.
</p>
<p>A <dfn>sovereign domain</dfn> (the scope of things under the control of a single, sovereign identity owner) may
include keys that represent the identity owner with greater or lesser degrees of trust. For example, the
identity owner may hold one key on paper, locked in a safe; she may hold another key on a mobile device's
secure enclave, protected by a biometric; she may hold a third key on a server in a config file protected
only by an ACL. These keys may all be legitimate evidence that the DID subject is interacting, but
they are not equally strong evidence. Only the strongest forms of evidence should be accepted as a
basis for authentication when stakes are high.</p>
<p>This is not unlike what happens on the political landscape. A low-level diplomat and the president / premier
/ prime minister of a nation are both authentic representatives of their people—but the scope of their
privileges, and the concomitant trust, are different. Only the highest-level officials should be signing
binding international treaties.
</p>
<p>The <a href="#authorization"><code>authorization</code> section</a> of peer DID docs, described next, is
where fine-grained privileges are described. The <code>authentication</code> section is a blunt instrument,
incapable of distinguishing between these levels of trust, and incapable of associating trust with anything
more complex than a single key. It is supported for historical reasons, and because most so-called "DID
Authentication" use cases assume it will be populated. With peer DIDs, the strong recommendation is that
only keys held by the most trusted proxies should be referenced here—only list the equivalent of your
president / premier / prime minister keys in this section, not the equivalent of keys for your low-level
diplomats. This will prevent impersonation by less trusted keys when stakes are high.
</p>
<section>
<h3 id="adding-key-to-authn">Adding a key</h3>
<p>As mentioned earlier, all objects having an <code>id</code> property in peer DID docs are immutable. This
means that a key cannot change its status with respect to the <code>authentication</code> section of a peer
DID doc during its lifetime in that doc; it must be declared as an authentication key when it is first
defined. A <a>change fragment</a> for adding an internally defined key and giving it status in the
<code>authentication</code> section at the same time might look like this:
</p>
<pre class="example" title="Change fragment that adds a new key with authentication privileges">
{
"publicKey": [
{
"id": "Mv6gmMNa",
"type": "Ed25519VerificationKey2018",
"controller": "#id",
"publicKeyBase58": "Mv6gmMNam3uVAjH3C2AVvLZZn6z3wXmqPVpfkcJCwDwn"
}
],
"authentication": [ "#Mv6gmMNa" ],
"authorization": { "profiles": [ {"key": "#Mv6gmMNa", "roles", ["edge"]} ] }
}
</pre>
<p class="note">Adding or deleting a key with authentication privileges can only be authorized by a key that
has the <a>key_admin</a> privilege.</p>
<h3 id="deleting-key-from-authn">Deleting a key</h3>
<p>Because a key's authentication privileges are immutable, the only way to take away authentication
privileges from a key is to <a href="#deleting-a-key">remove it entirely from the DID doc</a>.
</p>
</section>
</section>
<section>
<h2><code><dfn>authorization</dfn></code></h2>
<p class="note">This section of a peer DID docs governs how DID docs are updated and DIDComm trust flows. It doesn't
contemplate custom authorization schemes such as peers might develop to give each other spending limits or
access to sensitive data. Those matters are better formalized with something like a verifiable credential, or a
simple digitally signed permission slip.
</p>
<p>Peer DID docs organize their <code>authorization</code> section into two lists. The first, <code>profiles
</code>, gives a <dfn>trust profile</dfn> for each key, as expressed by named roles the key holds. These
named roles are arbitrary strings chosen by the implementer; since they are only used to match against
rules in the second list, their meaning in normal language does not need to be understood by a party
wishing to support correct semantics.
</p>
<p class="note">Changes to the <code>profiles</code> list MUST be performed when a key is added or deleted;
the <a>trust profile</a> of a key cannot change after it is created.</p>
<p>The second list contains <a href="https://evernym.github.io/sgl" target="sgl">SGL</a> rules that grant
privileges according to named roles. Here is an example:
</p>
<pre class="example" title="An authorization section">
“authorization”: {
"profiles": [
{"key": "#Mv6gmMNa", "roles", ["edge"]}, // an "edge" key
{"key": "#izfrNTmQ", "roles", ["edge", "biometric"]}, // an "edge" and a "biometric" key
{"key": "#02b97c30", "roles", ["cloud"]}, // a "cloud" key
{"key": "#H3C2AVvL", "roles", ["offline"]}, // an "offline" key
],
"rules": [
{
"grant": ["route", "authcrypt"],
"when": {"roles": "cloud"},
"id": "98c2c9cc"
},
{
"grant": ["authcrypt", "plaintext", "sign"],
"when": {"roles": "edge"},
"id": "e1e7d7bc"
},
{
"grant": ["key_admin", "se_admin", "rule_admin"],
"when": {
"any": [{"roles": "offline"}, {"roles": "biometric"}],
"n": 2
}
"id": "8586d26c"
}
]
}
</pre>
<p>In plain english, these rules say:</p>
<ul>
<li>Let a key have the <a>route</a> and <a>authcrypt</a> privileges when its set of roles contains "cloud".</li>
<li>Let a key have the <a>authcrypt</a>, <a>plaintext</a> and <a>sign</a> privileges when its set of roles
contains "edge".</li>
<li>Let any 2 keys, acting together, have the <a>key_admin</a>, <a>se_admin</a>, and <a>rule_admin</a>
privileges when their roles each contain either "offline" or "biometric".</li>
</ul>
<p>Far more elaborate rules can be constructed. See the <a target="sgl" href="https://evernym.github.io/sgl">
SGL documentation</a> for details.</p>
<section>
<h3>Privilege Inventory</h3>
<p>Unlike role names, the privileges named in these rules DO need to be understood by code that parses peer
DID docs; it is these privileges that others in a relationship should enforce. Possible values include:</p>
<dl>
<dt><dfn>route</dfn></dt>
<dd>The holder of this privilege is allowed to receive and decrypt <a target="aries"
href="https://github.com/hyperledger/aries-rfcs/tree/master/concepts/0094-cross-domain-messaging#required-mediators-process-forward-messages">
DIDComm <code>forward</code> messages</a> encrypted for itself, and to forward the contained
<a href="https://github.com/hyperledger/aries-rfcs/blob/master/features/0019-encryption-envelope/README.md"
target="aries">DIDComm encryption envelope</a> to another key. Holders of these keys thus become aware
of the timing and size of some incoming messages for the recipient (though not of the messages' senders
or content). This privilege is required and appropriate for any cloud agent or <a target="aries"
href="https://github.com/hyperledger/aries-rfcs/blob/master/concepts/0046-mediators-and-relays/README.md">
mediator</a> that exposes a service endpoint on behalf of mobile devices or other agents behind a
firewall.</dd>
<dt><dfn>authcrypt</dfn></dt>
<dd>The holder of this privilege is allowed to create messages and send them with <a target="wikipedia"
href="https://en.wikipedia.org/wiki/Authenticated_encryption">authenticated encryption</a>
that reveals the identity of the sender to the reciever. Most agents are likely to have this privilege,
but one designed for passive reception only (e.g., on an IoT sensor) might have it removed; doing so
would prevent a hacker from co-opting such a key into sending in a trusted way. Messages that are
authcrypted by a key that lacks this privilege should be rejected as illegal.</dd>
<dt><dfn>plaintext</dfn></dt>
<dd>The holder of this privilege can see <a target="aries"
href="https://github.com/hyperledger/aries-rfcs/tree/master/features/0044-didcomm-file-and-mime-types#didcomm-messages-dm">
plaintext DIDComm messages</a> intended for an identity owner engaged in a <a target="aries"
href="https://github.com/hyperledger/aries-rfcs/blob/master/concepts/0003-protocols/README.md">protocol
</a>. External parties sending to the owner of a given DIDDoc should multiplex encrypt for all keys that
hold this privilege, except in special circumstances.</dd>
<dt><dfn>sign</dfn></dt>
<dd><p>The holder of this privilege can incur binding, contractual obligations on behalf of the DID subject.
This may actually be a better test for login or authentication, in many cases, than whether a key appears
in the <a href="#authentication"><code>authentication</code> section</a> of the DID doc; it depends on
what trust is imputed after login.</p>
<p class="note">Any key holder can sign a DIDComm message; doing so gives non-repudiation to the
message. However, remember our <a href="#authentication">earlier discussion</a> about low-level
diplomats versus the president or prime minister of a country—both are authentic officials,
but only the president or prime minister's signature is binding on an international treaty. This
privilege makes explicit that sort of special binding-the-owner status; any other signatures
serve to prove origin with a specific agent but not the endorsement of the DID subject.
</p></dd>
<dt><dfn>key_admin</dfn></dt>
<dd>The holder of this privilege can add or remove other keys from a peer DID doc's <a href="#publickey">
<code>publicKey</code> section</a>, <a href="#authentication"><code>authentication</code> section</a>,
or <code>authorization.profiles</code> list. Typically this privilege is held only by very privileged
keys, or by combinations of keys, to prevent hackers who co-opt one device from adding new, malicious
keys to the inventory.
To guard against privilege escalation, it is important to enforce that entity
adding the new key can only add grant privileges (add <code>roles</code>) that it itself has.
Also note that a key can remove itself from <a href="#publickey"><code>publicKey</code> section</a>,
it does nee the <code>key_admin</code> privilege. A real example would be when an existing device with a
key in the DID doc gets damaged and the identity owner has no plans to replace it.
</dd>
<dt><dfn>se_admin</dfn></dt>
<dd><p>The holder of this privilege can add or remove items from a peer DID doc's <a href="#service">
<code>service</code> section</a>.</p>
</dd>
<dt><dfn>rule_admin</dfn></dt>
<dd><p>The holder of this privilege can add or remove rules from a peer DID doc's <code>authorization.rules
</code> list. Typically this privilege is held only by very privileged keys, or by combinations of keys.</p>
</dd>
<dt><dfn>rotate</dfn></dt>
<dd>The holder of this privilege can replace its associated key definition, and all references to that
key throughout a DID doc, with a new key definition and references, in a single delete-and-add
operation. If key #2 has this privilege and exercises it, the result is that key #2 is revoked from
the doc, but a new key (perhaps key #7) appears with exactly the same profile. This privilege is
assumed to be held by all keys unless rules specify otherwise. The construct <code>revoke-implicit</code>
is used to prevent keys from having implicit privileges. The example below shows that the key id
<code>#H3C2AVvL</code> is unable to replace its associated key definition.
</dd>
<pre class="example" title="Preventing keys from having implicit privileges">
“authorization”: {
"profiles": [
{"key": "#Mv6gmMNa", "roles", ["edge"]}, // an "edge" key
{"key": "#H3C2AVvL", "roles", ["offline"]}, // an "offline" key
],
"rules": [
{
"grant": ["authcrypt", "plaintext", "sign"],
"when": {"roles": "edge"},
"id": "e1e7d7bc"
},
{
"revoke-implicit": ["rotate"],
"when": {"key": "#H3C2AVvL"},
"id": "f908a18c"
},
{
"grant": ["key_admin", "se_admin", "rule_admin"],
......
......
......
}
]
}
</pre>
</dl>
</section>
<section>
<h3>Adding a rule</h3>
<p>Notice that rules have <code>id</code> properties. The IDs for these rules can be assigned by any convenient
method that provides collision resistance across concurrent updates.
</p>
<p>These <code>id</code>s give rules the same <a>CRDT</a> semantics as keys
and service endpoints—they can be added or removed with deltas, but never modified. A <a>change
fragment</a> that adds a new rule might be:
</p>
<pre class="example" title="Change fragment that adds a new rule">
{
"authorization": {
"rules": [
{
"grant": ["route", "authcrypt"],
"when": {"roles": "cloud"},
"id": "rule-7"
}
]
}
}
</pre>
<p class="note">Both adding and deleting a rule must be authorized by a key with the <a>rule_admin</a> privilege.
Care should be taken to bundle additions and deletions of rules in such a way that once a full <a>delta</a>
is applied, the new state provides adequate privileges for all existing keys.
</p>
</section>
<section>
<h3>Deleting a rule</h3>
<p>A <a>change fragment</a> that deletes a rule might look like this:
</p>
<pre class="example" title="Change fragment that deletes a rule with id=rule-7">
{
"deleted": [ "rule-7" ]
}
</pre>
<p>See the note above, in the <a href="#adding-a-rule">Adding a rule</a> section, about privileges and
transactional logic when deleting rules.</p>
</section>
</section>
<section>
<h2><code><dfn>service</dfn></code></h2>
<p>This section matches the <a target="didspec" href="https://w3c-ccg.github.io/did-spec/#service-endpoints">
general description of service endpoints in the DID Spec</a>. Any changes to this section of the DID doc
must be authorized by a key with the <a>se_admin</a> privilege.
</p>
<section>
<h4>Adding a service endpoint</h4>
<p>A <a>change fragment</a> that adds a service endpoint might look like this:</p>
<pre class="example" title="Change fragment that adds a service endpoint">
{
"service": [
{
"id": "#agent",
"type": "AgentService",
"serviceEndpoint": "https://agents-r-us.com"
}
]
}
</pre>
</section>
<section>
<h3>Deleting a service endpoint</h3>
<p>A <a>change fragment</a> that deletes a service endpoint might look like this:</p>
<pre class="example" title="Change fragment that deletes a service endpoint with id=agent">
{
"deleted": [ "#agent" ]
}
</pre>
</section>
</section>