provared 0.4.0 → 0.6.0
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.
- package/CHANGELOG.md +1083 -0
- package/README.md +6 -3
- package/bin/provared-check.mjs +115 -11
- package/docs/guide.md +464 -15
- package/docs/threat-model.md +315 -9
- package/package.json +2 -2
- package/page/check-page.js +11 -32
- package/page/index.html +4 -0
- package/page/render.js +157 -9
- package/spec/provared-format.md +809 -23
- package/src/book.js +539 -18
- package/src/check.d.ts +3 -0
- package/src/check.js +2 -0
- package/src/denial.js +123 -0
- package/src/der.js +55 -6
- package/src/dev.d.ts +1 -1
- package/src/dev.js +1 -1
- package/src/documents.js +149 -0
- package/src/guard.js +13 -2
- package/src/index.js +18 -3
- package/src/jws.js +8 -0
- package/src/keys.js +48 -1
- package/src/pass.js +1 -1
- package/src/recorder.js +267 -15
- package/src/seal.js +36 -7
- package/src/sealer.js +812 -0
- package/src/service.js +4 -4
- package/src/signatures.js +281 -5
- package/src/standing.js +4 -4
- package/src/stub.js +18 -5
- package/src/timestamp.js +55 -16
- package/src/tools.js +30 -13
- package/src/tree.js +139 -7
- package/src/types.d.ts +314 -20
- package/src/witness.js +63 -0
- package/src/witnessing.js +362 -0
package/docs/threat-model.md
CHANGED
|
@@ -91,6 +91,20 @@ follows the checking steps of the Web Authentication standard.
|
|
|
91
91
|
section 6b. Slips should still be short-lived.
|
|
92
92
|
- Who the issuer is. The slip proves which key signed, not whose key it
|
|
93
93
|
is.
|
|
94
|
+
- Against a page that has the passkey sign a record dressed as a sign-in.
|
|
95
|
+
A passkey signs only a challenge, and the challenge for a slip, an
|
|
96
|
+
approval or a cancellation is the fingerprint of the record, which the
|
|
97
|
+
person cannot read on the device's prompt. Any page at an origin under
|
|
98
|
+
the slip's `rpId` (the website's own operator, a script running on one
|
|
99
|
+
of its pages, or the operator of any subdomain) can hand the device the
|
|
100
|
+
fingerprint of a record it wrote as an ordinary sign-in challenge: the
|
|
101
|
+
client data of a sign-in and of a record have the same shape, and the
|
|
102
|
+
person sees a sign-in prompt. No device and no page need be broken into.
|
|
103
|
+
The checker compares the origin and the rpId only with what the slip
|
|
104
|
+
itself says. So a passkey that signs records should be registered to a
|
|
105
|
+
relying party (an `rpId`) used for nothing else, and the page that asks
|
|
106
|
+
for the signature should show the record first. The checker has no
|
|
107
|
+
option yet by which a reader names the origin they expect.
|
|
94
108
|
|
|
95
109
|
## 4. The Stub and the countersignature
|
|
96
110
|
|
|
@@ -123,6 +137,60 @@ fingerprint.
|
|
|
123
137
|
- The documents themselves. A stub holds fingerprints of documents, not
|
|
124
138
|
the documents. Whoever keeps the documents must keep them.
|
|
125
139
|
|
|
140
|
+
**A stub bound to the other system's own record of the call.** Where the
|
|
141
|
+
other side will not countersign, a stub may name, under a reserved name
|
|
142
|
+
(format description, section 27), the fingerprint of the identifier the
|
|
143
|
+
platform gave the call (`provared.event-id`), of one the agent's side
|
|
144
|
+
chose and sent (`provared.trace-id`, `provared.request-id`), of part
|
|
145
|
+
of the request and the answer (`provared.http-request`: the method, the
|
|
146
|
+
address and the body; `provared.http-response`: the status and the
|
|
147
|
+
body; no header is covered), or of the error the agent was given
|
|
148
|
+
(`provared.error`).
|
|
149
|
+
|
|
150
|
+
- *What it shows.* Someone who holds the platform's own log can work the
|
|
151
|
+
fingerprint out again and match the two records, without either
|
|
152
|
+
side's help. For an identifier, the two speak of one call. An agent's
|
|
153
|
+
side that later claims another identifier for the call, or a platform
|
|
154
|
+
that later shows another, is caught by the mismatch.
|
|
155
|
+
- *Only an identifier picks out one call.* A request, an answer, the
|
|
156
|
+
arguments or an error shows only that the content matches: identical
|
|
157
|
+
requests, and identical answers (many a `200` with the body `{}`),
|
|
158
|
+
share one fingerprint, so a match with one of them does not show that
|
|
159
|
+
it was the same call. Only `provared.trace-id`, `provared.request-id`
|
|
160
|
+
and `provared.event-id` are meant to pick out one call, and only they
|
|
161
|
+
may be named more than once in a stub.
|
|
162
|
+
- *The body's bytes* are what RFC 9530 digests: the content after any
|
|
163
|
+
content coding (such as gzip) and before any transfer coding. A
|
|
164
|
+
platform that logs the decoded body of a message sent compressed will
|
|
165
|
+
not match.
|
|
166
|
+
- *A trace-id's form is not checked.* The W3C standard writes it as 32
|
|
167
|
+
lower-case hexadecimal characters, but any text is fingerprinted as
|
|
168
|
+
given.
|
|
169
|
+
- *What it does not show.* That the call did what either record says:
|
|
170
|
+
the platform's identifier is the platform's word, and the stub is
|
|
171
|
+
still the agent's side's word. That the platform keeps the identifier
|
|
172
|
+
at all: which one a platform writes into its log differs from one
|
|
173
|
+
platform to another, and is for whoever relies on it to find out. That
|
|
174
|
+
the fingerprint was made from the text the name says: a checker cannot
|
|
175
|
+
tell; only someone who holds that text can. An identifier that is
|
|
176
|
+
short or easy to guess gives no privacy through its fingerprint.
|
|
177
|
+
- *Against A1 and A3* (the agent's own side). The binding is written by
|
|
178
|
+
the agent's side, so a dishonest one can leave it out, or name a
|
|
179
|
+
wrong identifier; only the match with the platform's log, made later,
|
|
180
|
+
catches the second. It adds nothing against an agent that writes no
|
|
181
|
+
stub.
|
|
182
|
+
- *Two spellings of one address.* An HTTP address is taken exactly as it
|
|
183
|
+
was sent and never rewritten, so that two programs fingerprint the
|
|
184
|
+
same request the same way. A program that records a rewritten address
|
|
185
|
+
(`%2f` for `%2F`, a host in capitals) makes a fingerprint that will not
|
|
186
|
+
match: whoever writes the stub must give the address as sent.
|
|
187
|
+
- *A reserved name not on the list* is refused (`bad-field`), so that a
|
|
188
|
+
name that may be given a meaning later cannot be used now with another;
|
|
189
|
+
so is one of the four once-only names named twice (two `provared.arguments`
|
|
190
|
+
fingerprints in one stub would leave unclear which was the call's). A
|
|
191
|
+
book written before this rule that holds such a name no longer passes
|
|
192
|
+
its check.
|
|
193
|
+
|
|
126
194
|
## 4a. Limits, conditions, approvals and the other side's records
|
|
127
195
|
|
|
128
196
|
**Protects.** That what the person allowed is stated exactly, and that a
|
|
@@ -235,6 +303,25 @@ disagree about. Totals are added as whole numbers of any size.
|
|
|
235
303
|
of the arguments fixes what was asked for without holding it; where
|
|
236
304
|
the arguments are few and easy to guess, the fingerprint can be
|
|
237
305
|
guessed.
|
|
306
|
+
- **Documents named after the action** (`detailsAfter` in the stub
|
|
307
|
+
writer's `act`). The stub is first written and checked with the whole
|
|
308
|
+
book before the action, as always; after the action, it is signed
|
|
309
|
+
again with up to four further documents, still within the same call,
|
|
310
|
+
so that no other call comes between, and keeps the time the action
|
|
311
|
+
began. The room for them is kept beforehand: with today's limits the
|
|
312
|
+
largest stub, with 32 documents of the longest names, and a
|
|
313
|
+
countersignature still fits a line, and the writer checks this again
|
|
314
|
+
for each such call. If what `detailsAfter` gives cannot go in (it
|
|
315
|
+
fails, gives no answer within the time the other side is given to
|
|
316
|
+
countersign, gives more than four, or gives a document that would be
|
|
317
|
+
refused), the stub made before the action is written without them,
|
|
318
|
+
and the answer says why: an action that was taken always gets its
|
|
319
|
+
stub. What it gives is copied when it is read, so a later change to
|
|
320
|
+
it changes nothing. A call to the writer from inside it is refused at
|
|
321
|
+
once. It cannot be used with an approval, because an approval covers
|
|
322
|
+
exactly the documents the person saw. The stub is signed twice for
|
|
323
|
+
such an action, and only one of the two is written: a signer kept
|
|
324
|
+
elsewhere is asked once more.
|
|
238
325
|
- **The check before acting refuses more in one case.** An action that
|
|
239
326
|
would add to a period which a stub dated after it already overfills is
|
|
240
327
|
refused, though a checker would report nothing new for it.
|
|
@@ -406,7 +493,8 @@ unnoticed).
|
|
|
406
493
|
what is left still checks. This can hide the very stub that shows the agent outside its
|
|
407
494
|
slip. A seal closes this for everything before it (section 6a). After
|
|
408
495
|
the last seal, only a copy of a top fingerprint, with its number of
|
|
409
|
-
entries, held by someone else would show it
|
|
496
|
+
entries, or a copy of a later seal, held by someone else would show it
|
|
497
|
+
(section 6a, "Seals held elsewhere").
|
|
410
498
|
- **Writing a different history with the agent's keys.** A1, A3 or A5
|
|
411
499
|
can sign a second, different chain under the same slip. Countersigned
|
|
412
500
|
stubs resist this, because the service's keys are needed. One-sided
|
|
@@ -476,8 +564,38 @@ person checking gives.
|
|
|
476
564
|
- **The end of the book.** The last seal, with everything after it, can
|
|
477
565
|
be removed, and what is left still checks. Only a copy of that seal, or
|
|
478
566
|
of its top fingerprint and number of entries, held by someone else
|
|
479
|
-
shows it. A
|
|
480
|
-
|
|
567
|
+
shows it. A seal held elsewhere can be handed to the checker beside the
|
|
568
|
+
book (below), and a witness that signs for each seal (section 6f)
|
|
569
|
+
holds the latest one it signed for. A public place where seals are
|
|
570
|
+
kept, which gathers the seals of many books, is not built.
|
|
571
|
+
- **Seals held elsewhere.** The person, an auditor, or any copy kept out
|
|
572
|
+
of the recorder's reach can hand the checker, beside the book, up to 64
|
|
573
|
+
seal entries it holds (option `seals`, `--seal` on the command line, files on
|
|
574
|
+
the checking page). A seal at entry n stands at entry n of the
|
|
575
|
+
book, so the checker reports a book that ends before that place
|
|
576
|
+
(`book-cut-short`) and a book that holds another line there
|
|
577
|
+
(`seal-not-in-book`). This closes the end of a book cut off after a seal
|
|
578
|
+
someone else holds, back to any place before that seal, and a second
|
|
579
|
+
history shown to someone who holds a seal of the first. What it does
|
|
580
|
+
not close: a book cut back after the latest seal anyone holds; a book,
|
|
581
|
+
or a part of one, never shown to anyone who keeps a seal; and a holder
|
|
582
|
+
who keeps no seal at all. A seal handed over counts only where the
|
|
583
|
+
person named its recorder (`sealKeys`), and only where the device checked
|
|
584
|
+
its Ed25519 and ML-DSA-87 signatures; on a device without SLH-DSA (the
|
|
585
|
+
Python version, and browsers that have ML-DSA, such as Edge and Brave
|
|
586
|
+
155 when tested) it counts on those two, and says so; on a device
|
|
587
|
+
without ML-DSA it is not used. It is compared with the book by its
|
|
588
|
+
content: the same content with other signatures is the same seal, and
|
|
589
|
+
is said, not reported against the book. It can only show a problem
|
|
590
|
+
with the book, never excuse one, so this widens nothing. Nothing in a
|
|
591
|
+
seal names its book, so a seal of another book by the same recorder
|
|
592
|
+
(one it kept before, when it started a new book) is reported against
|
|
593
|
+
the book being checked: hand over only that book's seals. A seal handed over that does not pass its own check is a
|
|
594
|
+
problem with the whole check, never set aside. Its time-stamps are
|
|
595
|
+
checked and shown, but are not used for the entries of the book: where
|
|
596
|
+
they differ from those beside the same seal in the book, the checker
|
|
597
|
+
says so. A Show does not use a seal held elsewhere, and says so: single
|
|
598
|
+
pages cannot show what the book holds at the place the seal states.
|
|
481
599
|
- **Entries after the last time-stamp.** They are not time-stamped.
|
|
482
600
|
- **A recorder who seals a false book.** A1 holding the agent's keys and
|
|
483
601
|
the recorder's keys can write another history and seal it. A time-stamp
|
|
@@ -586,7 +704,17 @@ person checking gives.
|
|
|
586
704
|
such a key, so the checker refuses the key itself. For the same reason
|
|
587
705
|
it refuses an Ed25519 signature whose point R is of small order, under
|
|
588
706
|
any key: the key's own holder could make one for any message, which one
|
|
589
|
-
library accepts and another refuses.
|
|
707
|
+
library accepts and another refuses. It also refuses, itself, an
|
|
708
|
+
Ed25519 signature whose number S is not below the order L, which RFC
|
|
709
|
+
8032 requires and some libraries do not check, and an ES256 passkey key
|
|
710
|
+
that is not a point on its curve (`bad-key`), which can check nothing
|
|
711
|
+
and which libraries report in different ways. A time-stamp signed with
|
|
712
|
+
Ed25519 must name SHA-512 as its fingerprint method, with no
|
|
713
|
+
parameters, as RFC 8419 requires, and may name the certificate that
|
|
714
|
+
signed it only once in each form (RFC 5035, sections 3 and 5): two
|
|
715
|
+
checkers could otherwise pick different certificates. A time-stamp is
|
|
716
|
+
read as strict DER throughout, every set it holds in the order DER
|
|
717
|
+
requires, so that two checkers cannot read one time-stamp in two ways. The key in a
|
|
590
718
|
certificate must be written exactly as its standard sets out, so that
|
|
591
719
|
two checkers cannot disagree about whether it can be read.
|
|
592
720
|
|
|
@@ -820,6 +948,174 @@ make a pass: it needs the keys of the agent that passes on.
|
|
|
820
948
|
every stub under it, as outside the slip. The record is still sound.
|
|
821
949
|
Only an eleventh pass in a row, which no slip can allow, is refused.
|
|
822
950
|
|
|
951
|
+
## 6e. The denial
|
|
952
|
+
|
|
953
|
+
A denial (format description, section 28) is the agent's side's signed
|
|
954
|
+
statement that it did not take an action that was asked for, and who
|
|
955
|
+
said no: the check before acting, the person, or a rule of the software
|
|
956
|
+
around the agent.
|
|
957
|
+
|
|
958
|
+
**Protects.** That a denial in the book was signed by the agent the slip
|
|
959
|
+
names, or by the helper its pass names, under a slip that is earlier in
|
|
960
|
+
the book (`slip-missing`, `pass-missing`, `pass-mismatch`,
|
|
961
|
+
`signature-invalid`). A6 cannot make one. A7 cannot use one to change
|
|
962
|
+
any other answer: a denial is in no chain, adds to no total, and is never
|
|
963
|
+
counted against the agent, so a flood of denials, or a denial that names
|
|
964
|
+
an action far outside the slip, makes no stub read as outside it. A
|
|
965
|
+
denial changed after it was signed, or under another kind of record's
|
|
966
|
+
labels, is refused like any record. In this library, `act` writes a
|
|
967
|
+
denial only where it did not take the action, so one call through `act`,
|
|
968
|
+
`recordTools` or `record_function` leaves a stub or a denial, never both
|
|
969
|
+
(the connector for an agent's tools asks for no denial until it knows
|
|
970
|
+
the person will not approve). `deny` is different: it records whatever
|
|
971
|
+
its caller states, and a caller can write a denial for a request that
|
|
972
|
+
also has a stub; the checker shows both. The writer keeps to a set
|
|
973
|
+
number of denials an hour of its own clock (600 by default), counting
|
|
974
|
+
each it writes at the clock's time, so that an agent that asks again and
|
|
975
|
+
again cannot fill its book quickly: at 600 an hour a book's 100,000
|
|
976
|
+
entries still fill in about a week. A stub is never kept out by that
|
|
977
|
+
limit.
|
|
978
|
+
|
|
979
|
+
**Does not protect.**
|
|
980
|
+
|
|
981
|
+
- **It is the agent's side's own word.** A3 can write a denial for an
|
|
982
|
+
action it then took some other way, or a false reason. Only the other
|
|
983
|
+
side's records, or the service's own refusal, can show otherwise.
|
|
984
|
+
- **An agent can simply not write one.** A book with no denial shows
|
|
985
|
+
nothing about how often the agent was stopped. A1 or A2 can also leave
|
|
986
|
+
denials out of a copy of the book after the last seal, as any entry.
|
|
987
|
+
- **What was asked for is in clear.** A denial names the action, the
|
|
988
|
+
amount and the service. Its documents are fingerprints only, and the
|
|
989
|
+
library never writes arguments into it; but a fingerprint of a short
|
|
990
|
+
or guessable value can be guessed.
|
|
991
|
+
- **The source is not checked.** Nothing shows that the person, or a
|
|
992
|
+
rule, said no: `person` and `rule` are the agent's side's statement.
|
|
993
|
+
A denial by the check before acting names the codes the check gave,
|
|
994
|
+
and nothing in the record shows that the check was made.
|
|
995
|
+
- **Older checkers** refuse a book that holds a denial (`unknown-entry`).
|
|
996
|
+
|
|
997
|
+
## 6f. Witnesses, and a book shown two ways
|
|
998
|
+
|
|
999
|
+
A recorder that keeps a book (A1, A2) can show one history to one reader
|
|
1000
|
+
and another to another (a "split view"): each copy checks, and each is
|
|
1001
|
+
sealed, so a reader who holds one copy cannot see the other. A witness
|
|
1002
|
+
(format description, section 29) is a party with a key set of its own
|
|
1003
|
+
that keeps, for each recorder, person and agent, the latest seal it
|
|
1004
|
+
signed for, and signs a statement for a new seal only where the new seal
|
|
1005
|
+
extends that one: in the same book, by a consistency proof (RFC 9162,
|
|
1006
|
+
section 2.1.4), or as the first seal of the next book (section 21.6). The
|
|
1007
|
+
person and the agent are those the permission on a book's first line
|
|
1008
|
+
names: its passkey and the agent's key set. So every permission one
|
|
1009
|
+
person gives one agent under one recorder, renewals included, is one
|
|
1010
|
+
sequence: the independent review showed that a sequence known by the
|
|
1011
|
+
permission alone let a book that started with a renewal fork the
|
|
1012
|
+
history, and a witness now refuses it. The statement sits beside the seal on its line, or is
|
|
1013
|
+
handed to a checker later with a copy of the seal.
|
|
1014
|
+
|
|
1015
|
+
**Protects.** That a statement a checker counts was signed by a witness
|
|
1016
|
+
the person named, by the fingerprint of its key set, and speaks of that
|
|
1017
|
+
very seal: its `seal`, `size` and `root` must be the seal's, both its
|
|
1018
|
+
signatures must check, and it may not be dated more than 300 seconds
|
|
1019
|
+
before the seal (`witness-invalid`). A6 and A7 cannot make one, and a
|
|
1020
|
+
statement from a witness nobody named is shown and not counted. A
|
|
1021
|
+
statement that does not pass is a problem; one handed over beside the
|
|
1022
|
+
book is a problem with the whole check, and the seal it came with is
|
|
1023
|
+
used all the same. Being witnessed excuses nothing: it changes no other
|
|
1024
|
+
answer, and a book with a problem is never said to be witnessed.
|
|
1025
|
+
|
|
1026
|
+
A witness knows which books are one sequence only by the passkey and the
|
|
1027
|
+
agent of the permission on their first line and the keys of their
|
|
1028
|
+
recorder, and the recorder (A1, A2) chooses the first line and its keys: it can start a history with another first line, or
|
|
1029
|
+
seal it with new keys, and the same honest witness signs for it as
|
|
1030
|
+
another sequence. So a statement counts only where the person fixed
|
|
1031
|
+
both: the book's first line is a permission that passed its check,
|
|
1032
|
+
signed by a passkey the person named (`issuerKeys`), and the person
|
|
1033
|
+
named the recorders they expect (`sealKeys`). The independent review of
|
|
1034
|
+
this step showed a history that began with made-up terms, then the
|
|
1035
|
+
permission, signed by the same witness and read as witnessed; it now
|
|
1036
|
+
reads as not witnessed, and the witness itself no longer keeps a book
|
|
1037
|
+
whose first line is not a permission.
|
|
1038
|
+
|
|
1039
|
+
And every permission among the entries a seal covers must name the same
|
|
1040
|
+
passkey and agent as the first line's. The third round of the review
|
|
1041
|
+
showed a fork through a second agent of the same person under the same
|
|
1042
|
+
recorder, the gateway's own set-up: a book that started with agent Y's
|
|
1043
|
+
permission, then held agent X's permission and X's stubs without the
|
|
1044
|
+
one over its limit, was signed by the witness as the first book of the
|
|
1045
|
+
person and agent Y, and read as witnessed. It now reads as not
|
|
1046
|
+
witnessed, with a note saying why; so does one that starts with a
|
|
1047
|
+
second passkey's permission for X. A witness cannot refuse such a book
|
|
1048
|
+
itself: it sees a book's first line, not its entries. A Show counts a
|
|
1049
|
+
statement only where its pages hold every entry the seal covers, so it
|
|
1050
|
+
never counts one that the whole book would not.
|
|
1051
|
+
|
|
1052
|
+
What it narrows, where the witness keeps section 29.3 and the person
|
|
1053
|
+
fixed the start: a second history sealed by the same recorder and
|
|
1054
|
+
starting from any permission that passkey gave the same agent needs that
|
|
1055
|
+
witness to sign for it too, which it does not do. So two readers who name that witness, the
|
|
1056
|
+
same passkey and the same recorder cannot be shown two such histories
|
|
1057
|
+
that both carry its statements and that disagree before the earlier of
|
|
1058
|
+
the two seals it signed for. A new book cannot start from an earlier
|
|
1059
|
+
seal than the latest the witness holds, so the books of one permission
|
|
1060
|
+
cannot fork either. Two witnesses need two to fail. The latest seal a
|
|
1061
|
+
witness holds, handed over beside the book it is of as a seal held
|
|
1062
|
+
elsewhere (section 6a), shows that book cut back to before that seal
|
|
1063
|
+
(`book-cut-short`, `seal-not-in-book`). The witness program in the
|
|
1064
|
+
library (`openWitness`) checks a seal's three signatures, signs nothing
|
|
1065
|
+
on a device that cannot check SLH-DSA (so there is none in the Python
|
|
1066
|
+
version), ties the first line to the seal with an audit path at first
|
|
1067
|
+
contact and at each new book, keeps it tied with each consistency proof,
|
|
1068
|
+
never signs two different seals of one book at one size, never again
|
|
1069
|
+
signs for a book once it has signed for the next, and hands back what it
|
|
1070
|
+
holds with each statement, to be kept before the statement is given
|
|
1071
|
+
out.
|
|
1072
|
+
|
|
1073
|
+
**Does not protect.**
|
|
1074
|
+
|
|
1075
|
+
- **Other recorder keys, or another agent.** A history sealed with
|
|
1076
|
+
another key set, even one the person also named, or that starts from a
|
|
1077
|
+
permission for another agent, is another sequence to a witness, and a
|
|
1078
|
+
fork made that way is not caught. A person who names two recorders
|
|
1079
|
+
should know that a fork sealed with the second reads as witnessed. One
|
|
1080
|
+
recorder key set keeps one sequence for each person and agent, so books
|
|
1081
|
+
kept side by side for one agent need recorder keys of their own, and
|
|
1082
|
+
are then other sequences.
|
|
1083
|
+
- **What a later book holds.** A later book can repeat, or leave out,
|
|
1084
|
+
what an earlier book holds: each book is checked on its own.
|
|
1085
|
+
- **A book sealed again under another number.** A book cut short can be
|
|
1086
|
+
sealed again with another book number. A seal held elsewhere is then of
|
|
1087
|
+
another number than the book, is not compared with it, and gives no
|
|
1088
|
+
pass: the command-line checker says "Seals held elsewhere compared?
|
|
1089
|
+
NO" and does not end with code 0, and the checking page gives no green
|
|
1090
|
+
answer. The reader learns that their seal was not compared, not where
|
|
1091
|
+
the book was cut.
|
|
1092
|
+
- **What is never shown to a witness.** A recorder can keep a book, or
|
|
1093
|
+
the end of one, from every witness. Nothing in a book obliges it to be
|
|
1094
|
+
witnessed, and a checker that is not handed the witness's latest seal
|
|
1095
|
+
does not know of later seals. Where that latest seal is of a later
|
|
1096
|
+
book, a checker of an earlier book cannot use it to show that earlier
|
|
1097
|
+
book cut short: it cannot tell whether a seal of the next book is that
|
|
1098
|
+
book's first (section 21.6).
|
|
1099
|
+
- **A witness that fails.** A witness that signs for two histories, or
|
|
1100
|
+
that loses what it holds or is put back to an older copy of it, can
|
|
1101
|
+
sign a fork, and nothing in either history shows it. The library's
|
|
1102
|
+
witness, opened again from what it kept, signs nothing until it is
|
|
1103
|
+
brought up to date (`update`) and told so (`ready`); it cannot tell
|
|
1104
|
+
whether what it was given is the latest it gave out. Whoever can change
|
|
1105
|
+
what it keeps can make it forget a sequence, which it then signs for
|
|
1106
|
+
again from any seal: its state must be kept as safely as its keys.
|
|
1107
|
+
- **The entries.** A witness sees a seal's size and top fingerprint,
|
|
1108
|
+
not the entries: it shows that the entries existed in that form when
|
|
1109
|
+
it saw the seal, not that they are true.
|
|
1110
|
+
- **Time.** A statement's `when` is the witness's own word, and is not
|
|
1111
|
+
used as a time-stamp.
|
|
1112
|
+
- **Limits across books.** A checker counts a limit within the book it
|
|
1113
|
+
checks, not across the books of a permission.
|
|
1114
|
+
- **A device that cannot check both of a statement's signatures**
|
|
1115
|
+
counts nothing, and the record is not fully checked there.
|
|
1116
|
+
- **Older checkers** refuse a seal's line that holds `witnesses`
|
|
1117
|
+
(`unknown-entry`), and a seal that gives `book` (`bad-field`).
|
|
1118
|
+
|
|
823
1119
|
## 7. The checker and the checking page
|
|
824
1120
|
|
|
825
1121
|
**Protects.** A7 cannot crash the checker, make it run a script, or make
|
|
@@ -850,7 +1146,11 @@ it show a pass for a record that does not check.
|
|
|
850
1146
|
the checker itself put there.
|
|
851
1147
|
- "Within its slip" is said only if every stub and every pass was
|
|
852
1148
|
compared on this device.
|
|
853
|
-
- Records over a fixed size, or nested too deeply, are refused.
|
|
1149
|
+
- Records over a fixed size, or nested too deeply, are refused. A book
|
|
1150
|
+
may hold 100,000 lines of 131,072 bytes, about 13 gigabytes in all;
|
|
1151
|
+
this library takes a book as one text, so it can check no book larger
|
|
1152
|
+
than one text the device can hold, and a checker should bound the size
|
|
1153
|
+
it accepts (format description, section 9).
|
|
854
1154
|
- The content of a record must be written in one canonical form (RFC
|
|
855
1155
|
8785). A record with a repeated field name, stray spaces or an unusual
|
|
856
1156
|
number form is refused, so two checkers cannot read the same record in
|
|
@@ -915,7 +1215,10 @@ it show a pass for a record that does not check.
|
|
|
915
1215
|
## 8. What is refused, and the test that proves it
|
|
916
1216
|
|
|
917
1217
|
Each row is one test in `test/refusals.test.mjs`, named by its code. A
|
|
918
|
-
further test reads this table and fails if a code here has no test.
|
|
1218
|
+
further test reads this table and fails if a code here has no test. This
|
|
1219
|
+
table is also the format's own list of finding codes, from which a denial
|
|
1220
|
+
names its reasons (format description, section 28); `test/denial.test.mjs`
|
|
1221
|
+
fails if the two differ.
|
|
919
1222
|
|
|
920
1223
|
| Code | What is refused or reported |
|
|
921
1224
|
|---|---|
|
|
@@ -928,8 +1231,8 @@ further test reads this table and fails if a code here has no test.
|
|
|
928
1231
|
| `payload-not-canonical` | Content that is not in the canonical form, including a repeated field name |
|
|
929
1232
|
| `payload-type-mismatch` | Content of one kind under the label of another, for example a stub presented as a countersignature |
|
|
930
1233
|
| `bad-field` | A missing, extra or wrongly typed field |
|
|
931
|
-
| `bad-key` | A public key that is not exactly of an accepted kind |
|
|
932
|
-
| `signature-invalid` | A signature that does not fit the key |
|
|
1234
|
+
| `bad-key` | A public key that is not exactly of an accepted kind, an Ed25519 key of small order or written in more than one way, or an ES256 key that is not a point on its curve |
|
|
1235
|
+
| `signature-invalid` | A signature that does not fit the key, or an Ed25519 signature whose R is a point of small order or whose S is not below the order L |
|
|
933
1236
|
| `passkey-wrong-type` | A passkey answer that was made for creating a passkey, not for signing |
|
|
934
1237
|
| `passkey-challenge-mismatch` | A passkey signature made for other content |
|
|
935
1238
|
| `passkey-origin-mismatch` | A passkey used on a website other than the one the slip names |
|
|
@@ -968,10 +1271,13 @@ further test reads this table and fails if a code here has no test.
|
|
|
968
1271
|
| `seal-mismatch` | A seal that does not fit the entries before it: an entry was changed, added or removed after the seal was made. In a Show: a seal for another book, or for another length of this one |
|
|
969
1272
|
| `seal-chain-broken` | A seal that does not name the seal before it: a seal is missing, moved or changed |
|
|
970
1273
|
| `sealer-not-expected` | A seal signed with keys other than the ones the checker was told to expect |
|
|
1274
|
+
| `book-cut-short` | A book that ends before the place of a seal held elsewhere and handed over beside it: its end was cut off after that seal was made |
|
|
1275
|
+
| `seal-not-in-book` | A book that holds another line at the place of a seal held elsewhere and handed over beside it: it was changed after that seal was made, or the seal is of another book |
|
|
1276
|
+
| `witness-invalid` | A witness statement, beside a seal in the book or handed over with a seal held elsewhere, whose signatures do not fit the keys it gives, that speaks of another seal, or that is dated more than 300 seconds before its seal |
|
|
971
1277
|
| `dated-after-stamp` | An entry dated later than the outside time-stamp that covers it, or a seal dated later than its own time-stamp |
|
|
972
1278
|
| `stamp-bad-data` | A time-stamp that is not laid out as its standard sets out |
|
|
973
1279
|
| `stamp-wrong-data` | A time-stamp made for something other than this seal |
|
|
974
|
-
| `stamp-invalid` | A time-stamp whose signature does not check, or that is dated outside the time its service's certificate was in force |
|
|
1280
|
+
| `stamp-invalid` | A time-stamp whose signature does not check, that names the certificate that signed it twice in one form, that is signed with Ed25519 and names a fingerprint method other than SHA-512, or that is dated outside the time its service's certificate was in force |
|
|
975
1281
|
| `headers-invalid` | A chain of block headers in which a header breaks a rule that every Bitcoin node applies, that does not hold the known blocks at their places, or that ends before the last of them (tested in `test/headers.test.mjs`) |
|
|
976
1282
|
| `record-not-sound` | The check before acting: the record so far has a problem, or could not be checked on this device, so no action is allowed |
|
|
977
1283
|
| `proof-invalid` | A one-page proof that does not lead to the top fingerprint |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "provared",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Proof of what an AI agent was allowed to do, and what it then did. A passkey-signed permission with limits a machine can check, a signed receipt for each action, and a free checker. Evidence, not a verdict.",
|
|
5
5
|
"author": "Pavel Izmaylov",
|
|
6
6
|
"license": "Apache-2.0 AND CC-BY-4.0",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"node": ">=24.7"
|
|
72
72
|
},
|
|
73
73
|
"scripts": {
|
|
74
|
-
"test": "node --disable-warning=ExperimentalWarning --test",
|
|
74
|
+
"test": "node --disable-warning=ExperimentalWarning --test \"test/**/*.test.mjs\"",
|
|
75
75
|
"check": "node --disable-warning=ExperimentalWarning bin/provared-check.mjs",
|
|
76
76
|
"headers": "node bin/provared-headers.mjs",
|
|
77
77
|
"demo": "node --disable-warning=ExperimentalWarning demo/server.mjs"
|
package/page/check-page.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// request at all.
|
|
7
7
|
|
|
8
8
|
import { checkBook, checkShow } from '../src/check.js';
|
|
9
|
-
import { readBlocks, readFingerprints, renderResult, shown } from './render.js';
|
|
9
|
+
import { readBlocks, readFingerprints, readHanded, renderResult, shown } from './render.js';
|
|
10
10
|
|
|
11
11
|
const status = document.getElementById('status');
|
|
12
12
|
const result = document.getElementById('result');
|
|
@@ -14,9 +14,6 @@ const result = document.getElementById('result');
|
|
|
14
14
|
// This page reads the whole file into memory. A larger book is checked
|
|
15
15
|
// with the command-line checker.
|
|
16
16
|
const MAX_FILE_BYTES = 200000000;
|
|
17
|
-
// A copy of a cancellation, or a file of disclosures, is small.
|
|
18
|
-
const MAX_HANDED_BYTES = 1000000;
|
|
19
|
-
const MAX_COPIES = 16;
|
|
20
17
|
|
|
21
18
|
// What the person already trusts: one fingerprint to a line. A line that
|
|
22
19
|
// is not a fingerprint stops the check, and the person is told which box.
|
|
@@ -25,6 +22,7 @@ const BOXES = [
|
|
|
25
22
|
['trust-sealer', 'sealKeys', 'whoever keeps and seals the book'],
|
|
26
23
|
['trust-stamps', 'stampServices', 'time-stamp services'],
|
|
27
24
|
['trust-vouchers', 'vouchers', 'organisations whose vouching you trust'],
|
|
25
|
+
['trust-witnesses', 'witnesses', 'witnesses you trust'],
|
|
28
26
|
];
|
|
29
27
|
|
|
30
28
|
function trusted() {
|
|
@@ -42,34 +40,14 @@ function trusted() {
|
|
|
42
40
|
return options;
|
|
43
41
|
}
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// What the person was handed with the record: their own copies of
|
|
55
|
-
// cancellations, and disclosures for covered fields. A file that cannot be
|
|
56
|
-
// read stops the check: it is never set aside silently.
|
|
57
|
-
async function handedOver() {
|
|
58
|
-
const options = {};
|
|
59
|
-
const said = [];
|
|
60
|
-
const copies = [...document.getElementById('cancellations').files];
|
|
61
|
-
if (copies.length > MAX_COPIES) throw new Error('At most 16 copies of cancellations can be handed over at once. Nothing was checked.');
|
|
62
|
-
if (copies.length) {
|
|
63
|
-
options.cancellations = [];
|
|
64
|
-
for (const file of copies) options.cancellations.push(await jsonFrom(file, 'a cancellation'));
|
|
65
|
-
said.push(copies.length === 1 ? 'one copy of a cancellation' : `${copies.length} copies of cancellations`);
|
|
66
|
-
}
|
|
67
|
-
const [disclosures] = document.getElementById('disclosures').files;
|
|
68
|
-
if (disclosures) {
|
|
69
|
-
options.disclosures = await jsonFrom(disclosures, 'disclosures');
|
|
70
|
-
said.push('disclosures for covered fields');
|
|
71
|
-
}
|
|
72
|
-
return { options, said };
|
|
43
|
+
// What the person was handed with the record: the files chosen in the
|
|
44
|
+
// panel "What you were handed with the record" (render.js, readHanded).
|
|
45
|
+
function handedOver() {
|
|
46
|
+
return readHanded({
|
|
47
|
+
cancellations: [...document.getElementById('cancellations').files],
|
|
48
|
+
seals: [...document.getElementById('seals').files],
|
|
49
|
+
disclosures: document.getElementById('disclosures').files[0] ?? null,
|
|
50
|
+
});
|
|
73
51
|
}
|
|
74
52
|
|
|
75
53
|
async function check(text, name, { beside = true, trust = null } = {}) {
|
|
@@ -131,6 +109,7 @@ if (chooser.files.length > 0) checkChosen(chooser);
|
|
|
131
109
|
|
|
132
110
|
document.getElementById('clear-beside').addEventListener('click', () => {
|
|
133
111
|
document.getElementById('cancellations').value = '';
|
|
112
|
+
document.getElementById('seals').value = '';
|
|
134
113
|
document.getElementById('disclosures').value = '';
|
|
135
114
|
status.textContent = 'The files you were handed with a record are cleared.';
|
|
136
115
|
});
|
package/page/index.html
CHANGED
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
<textarea id="trust-stamps" rows="2" spellcheck="false"></textarea>
|
|
27
27
|
<label for="trust-vouchers">Organisations whose vouching for names you trust (the fingerprint of each one's key set)</label>
|
|
28
28
|
<textarea id="trust-vouchers" rows="2" spellcheck="false"></textarea>
|
|
29
|
+
<label for="trust-witnesses">Witnesses you trust to have seen the seals (the fingerprint of each one's key set)</label>
|
|
30
|
+
<textarea id="trust-witnesses" rows="2" spellcheck="false"></textarea>
|
|
29
31
|
<label for="trust-blocks">Blocks of a public blockchain that you trust to be part of the chain (the fingerprint of each block: 64 characters, the digits 0 to 9 and the letters a to f)</label>
|
|
30
32
|
<textarea id="trust-blocks" rows="2" spellcheck="false"></textarea>
|
|
31
33
|
</details>
|
|
@@ -34,6 +36,8 @@
|
|
|
34
36
|
<p class="quiet">Choose these files first, then choose the record file. They stay chosen, and are used with every record you check, until you clear them. The sample record is checked without them.</p>
|
|
35
37
|
<label for="cancellations">The person's own copies of cancellations: one file for each, up to 16. A copy counts even where the book leaves the cancellation out. A file may also hold what the agent's side signed when it was handed the cancellation.</label>
|
|
36
38
|
<input type="file" id="cancellations" accept=".json,.txt" multiple>
|
|
39
|
+
<label for="seals">Seals of the book held elsewhere, by you, an auditor or a copy kept apart: one file for each, up to 64. The book must hold each at the place it states; one that ends before it was cut short. A seal counts only where you named its recorder above. A file may also hold statements that witnesses signed for the seal.</label>
|
|
40
|
+
<input type="file" id="seals" accept=".json,.txt" multiple>
|
|
37
41
|
<label for="disclosures">Disclosures for covered fields of a slip: one file</label>
|
|
38
42
|
<input type="file" id="disclosures" accept=".json,.txt">
|
|
39
43
|
<p><button type="button" class="second" id="clear-beside">Clear these files</button></p>
|