provared 0.5.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 +982 -0
- package/README.md +6 -3
- package/bin/provared-check.mjs +115 -11
- package/docs/guide.md +290 -17
- package/docs/threat-model.md +242 -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 +686 -22
- 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/guard.js +12 -1
- package/src/index.js +10 -0
- package/src/jws.js +8 -0
- package/src/keys.js +48 -1
- package/src/recorder.js +131 -5
- package/src/seal.js +32 -4
- package/src/sealer.js +812 -0
- package/src/signatures.js +6 -5
- package/src/timestamp.js +55 -16
- package/src/tools.js +30 -13
- package/src/tree.js +139 -7
- package/src/types.d.ts +257 -9
- package/src/witness.js +63 -0
- package/src/witnessing.js +362 -0
package/src/seal.js
CHANGED
|
@@ -25,7 +25,7 @@ export const CLOCK_ALLOWANCE_MS = 300 * 1000;
|
|
|
25
25
|
* @param {any} c
|
|
26
26
|
*/
|
|
27
27
|
export function validateSealContent(c) {
|
|
28
|
-
f.members(c, ['by', 'id', 'root', 'size', 'type', 'when'], ['previous'], 'seal');
|
|
28
|
+
f.members(c, ['by', 'id', 'root', 'size', 'type', 'when'], ['book', 'previous'], 'seal');
|
|
29
29
|
f.id(c.id, 'id');
|
|
30
30
|
f.members(c.by, ['keys', 'name'], [], 'by');
|
|
31
31
|
f.label(c.by.name, 'by.name');
|
|
@@ -34,6 +34,13 @@ export function validateSealContent(c) {
|
|
|
34
34
|
if (c.size < 1) throw f.fail('size', 'must be 1 or more.');
|
|
35
35
|
f.fingerprintText(c.root, 'root');
|
|
36
36
|
if (Object.hasOwn(c, 'previous')) f.fingerprintText(c.previous, 'previous');
|
|
37
|
+
// The number of the book among the books a recorder keeps under one permission (section 21.6). Absent for the
|
|
38
|
+
// first book, so that every seal written before numbers existed reads as it did; written only from 2.
|
|
39
|
+
if (Object.hasOwn(c, 'book')) {
|
|
40
|
+
f.wholeNumber(c.book, 'book');
|
|
41
|
+
if (c.book < 2) throw f.fail('book', 'must be 2 or more: the first book has no number.');
|
|
42
|
+
if (!Object.hasOwn(c, 'previous')) throw f.fail('previous', 'is missing: every seal of a book after the first names the seal before it, and the first names the last seal of the book before.');
|
|
43
|
+
}
|
|
37
44
|
f.time(c.when, 'when');
|
|
38
45
|
}
|
|
39
46
|
|
|
@@ -102,7 +109,10 @@ export async function keySetFingerprint(keys) {
|
|
|
102
109
|
* @param {string} book the book so far
|
|
103
110
|
* @param {object} fields
|
|
104
111
|
* @param {{keys: object[], name: string}} fields.by the recorder's public key set and its name (a label)
|
|
105
|
-
* @param {string} [fields.previous] the fingerprint of the seal before this one in the book, if there is one
|
|
112
|
+
* @param {string} [fields.previous] the fingerprint of the seal before this one in the book, if there is one; for
|
|
113
|
+
* the first seal of a book after the first, the fingerprint of the last seal of the book before
|
|
114
|
+
* @param {number} [fields.book] the book's number among the books the recorder keeps under one permission (section
|
|
115
|
+
* 21.6): 2 or more for a book after the first; 1 or none for the first, which carries no number
|
|
106
116
|
* @param {number|Date} [fields.when] defaults to now
|
|
107
117
|
* @param {any[]} privateKeys the recorder's private keys, or signers (signatures.js), in the order of the key set. A signer must hold the key "by" names in its place
|
|
108
118
|
* @returns {Promise<{record: object, fingerprint: string, fingerprintBytes: Uint8Array}>}
|
|
@@ -111,15 +121,33 @@ export async function keySetFingerprint(keys) {
|
|
|
111
121
|
export async function writeSeal(book, fields, privateKeys) {
|
|
112
122
|
const lines = book.split('\n');
|
|
113
123
|
if (lines[lines.length - 1] === '') lines.pop();
|
|
124
|
+
return sealOver(lines.length, await treeRoot(lines.map(utf8)), fields, privateKeys);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Sign a seal over a number of entries and their top fingerprint, worked out
|
|
129
|
+
* by the caller: writeSeal from the book, and the central sealer (sealer.js)
|
|
130
|
+
* from the tree it built from the leaf fingerprints it was sent. One path, so
|
|
131
|
+
* that the two give the same seal for the same book. Not part of the public
|
|
132
|
+
* interface: a size and a top fingerprint handed in from outside are never
|
|
133
|
+
* signed (choice 388).
|
|
134
|
+
* @param {number} size
|
|
135
|
+
* @param {Uint8Array} root
|
|
136
|
+
* @param {object} fields as writeSeal takes them
|
|
137
|
+
* @param {any[]} privateKeys
|
|
138
|
+
*/
|
|
139
|
+
export async function sealOver(size, root, fields, privateKeys) {
|
|
114
140
|
const content = {
|
|
115
141
|
type: KINDS.seal.type,
|
|
116
142
|
id: fields.id ?? randomId(),
|
|
117
143
|
by: fields.by,
|
|
118
|
-
size
|
|
119
|
-
root: toBase64url(
|
|
144
|
+
size,
|
|
145
|
+
root: toBase64url(root),
|
|
120
146
|
when: formatTime(fields.when ?? Date.now()),
|
|
121
147
|
};
|
|
122
148
|
if (fields.previous) content.previous = fields.previous;
|
|
149
|
+
// The book's number: 1, or none, for the first book, which then carries no number.
|
|
150
|
+
if (fields.book !== undefined && fields.book !== null && fields.book !== 1) content.book = fields.book;
|
|
123
151
|
validateSealContent(content);
|
|
124
152
|
const { contentBytes, payloadB64 } = encodeContent(content);
|
|
125
153
|
const headers = protectedHeaders('seal');
|