@quo-systems/quo 0.2.9 → 0.2.11
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/README.md +19 -10
- package/SPEC.md +301 -110
- package/dist/being/being.d.ts +2 -2
- package/dist/being/being.js +34 -12
- package/dist/being/digest.js +26 -8
- package/dist/being/index.d.ts +2 -2
- package/dist/being/index.js +2 -2
- package/dist/being/silence.d.ts +2 -0
- package/dist/being/silence.js +12 -0
- package/dist/being/types.d.ts +4 -2
- package/dist/being/types.js +25 -0
- package/dist/conformance/assert.js +40 -6
- package/dist/conformance/beings.d.ts +48 -5
- package/dist/conformance/beings.js +39 -8
- package/dist/conformance/estate.js +110 -21
- package/dist/conformance/index.d.ts +5 -2
- package/dist/conformance/index.js +165 -7
- package/dist/harbor/core.d.ts +5 -2
- package/dist/harbor/core.js +195 -45
- package/dist/harbor/dial.js +32 -15
- package/dist/harbor/index.d.ts +1 -0
- package/dist/harbor/index.js +3 -0
- package/dist/harbor/memory.d.ts +3 -3
- package/dist/harbor/memory.js +7 -12
- package/dist/harbor/reach.js +42 -17
- package/dist/ward/allowance.js +15 -4
- package/dist/ward/arithmetic.d.ts +1 -0
- package/dist/ward/arithmetic.js +22 -6
- package/dist/ward/cells.d.ts +3 -1
- package/dist/ward/cells.js +79 -21
- package/dist/ward/door.d.ts +3 -2
- package/dist/ward/door.js +38 -10
- package/dist/ward/ground.d.ts +5 -1
- package/dist/ward/ground.js +38 -1
- package/dist/ward/heirs.d.ts +2 -3
- package/dist/ward/heirs.js +19 -13
- package/dist/ward/index.d.ts +1 -0
- package/dist/ward/index.js +3 -0
- package/dist/ward/owner.d.ts +6 -27
- package/dist/ward/owner.js +59 -33
- package/dist/ward/partition.d.ts +3 -0
- package/dist/ward/partition.js +109 -4
- package/dist/ward/seal.d.ts +1 -0
- package/dist/ward/seal.js +41 -13
- package/dist/ward/stance.d.ts +7 -3
- package/dist/ward/stance.js +156 -66
- package/dist/ward/ward.d.ts +10 -0
- package/dist/ward/ward.js +123 -51
- package/package.json +4 -2
- package/src/being/being.ts +33 -11
- package/src/being/digest.ts +28 -13
- package/src/being/index.ts +2 -2
- package/src/being/silence.ts +14 -0
- package/src/being/types.ts +39 -5
- package/src/conformance/assert.ts +37 -4
- package/src/conformance/beings.ts +41 -10
- package/src/conformance/estate.ts +107 -20
- package/src/conformance/index.ts +188 -13
- package/src/harbor/core.ts +203 -46
- package/src/harbor/dial.ts +46 -17
- package/src/harbor/index.ts +3 -0
- package/src/harbor/memory.ts +8 -13
- package/src/harbor/reach.ts +47 -21
- package/src/ward/allowance.ts +15 -4
- package/src/ward/arithmetic.ts +25 -8
- package/src/ward/cells.ts +76 -25
- package/src/ward/door.ts +38 -12
- package/src/ward/ground.ts +52 -3
- package/src/ward/heirs.ts +19 -13
- package/src/ward/index.ts +3 -0
- package/src/ward/owner.ts +65 -46
- package/src/ward/partition.ts +109 -5
- package/src/ward/seal.ts +41 -12
- package/src/ward/stance.ts +163 -64
- package/src/ward/ward.ts +124 -52
- package/vectors/arithmetic.json +7 -0
- package/vectors/framing.json +30 -15
- package/vectors/wire.json +4 -4
package/src/ward/ward.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { silence, isSilence, unreached, isWord, word } from '../being/silence.ts
|
|
|
8
8
|
import { OWNER } from '../being/types.ts';
|
|
9
9
|
import type { Asker, BeingLike, Cells, JsonObject, Reply, Stance, Wanted, Word } from '../being/types.ts';
|
|
10
10
|
import type { Ground, WardPointers } from './ground.ts';
|
|
11
|
-
import { at, drop, open, put, emptyBind, emptyCells, type Bind, type Partition, type StandingKeys } from './partition.ts';
|
|
11
|
+
import { at, drop, open, put, emptyBind, emptyCells, MINTED, type Bind, type Partition, type StandingKeys } from './partition.ts';
|
|
12
12
|
import { Heirs } from './heirs.ts';
|
|
13
13
|
import { makeDoor, type Door } from './door.ts';
|
|
14
14
|
import { buildStance } from './stance.ts';
|
|
@@ -16,12 +16,13 @@ import { ownerAnswer } from './owner.ts';
|
|
|
16
16
|
import { allow, within, LATE } from './allowance.ts';
|
|
17
17
|
import { beingKey, openReply, sealAsk, wardKey, wardPadlock, wardSignPk, type ReplyPayload, type WardKey } from './seal.ts';
|
|
18
18
|
import { unhex } from './arithmetic.ts';
|
|
19
|
-
import { guardCells, cellFault } from './cells.ts';
|
|
19
|
+
import { guardCells, unguarded, cellFault } from './cells.ts';
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
//
|
|
24
|
-
|
|
21
|
+
// One being of the ward as the ward holds her: her door, her bind table, her
|
|
22
|
+
// stance and her cells. The owner's asks see this and nothing more. Not
|
|
23
|
+
// `Booted`, which is a harbor's word for a ward it started: this is a ward's
|
|
24
|
+
// word for a being living in it, and the two are one module apart.
|
|
25
|
+
export type Resident = Door & { bind: Bind; stance: Stance; cells: Cells & { class?: string } };
|
|
25
26
|
|
|
26
27
|
// The ward. One call in, two pointers out. Async because keys are derived.
|
|
27
28
|
export async function Ward(ground: Ground): Promise<WardPointers> {
|
|
@@ -35,7 +36,8 @@ class Self implements BeingLike {
|
|
|
35
36
|
readonly g: Ground;
|
|
36
37
|
readonly p: Partition;
|
|
37
38
|
readonly heirs: Heirs;
|
|
38
|
-
readonly doors = new Map<string,
|
|
39
|
+
readonly doors = new Map<string, Resident>();
|
|
40
|
+
readonly #birthing = new Set<string>(); // keys whose constructor is running: taken, though no door and no row say so yet
|
|
39
41
|
readonly door: WardPointers['door'];
|
|
40
42
|
readonly ask: WardPointers['ask'];
|
|
41
43
|
|
|
@@ -47,23 +49,25 @@ class Self implements BeingLike {
|
|
|
47
49
|
this.g = ground;
|
|
48
50
|
this.key = key;
|
|
49
51
|
this.pk = key.pk;
|
|
50
|
-
this.p = open(ground.memory); // the partition
|
|
51
|
-
this.heirs = new Heirs(this.p);
|
|
52
|
+
this.p = open(ground.memory); // the partition: the ward's own state, and every being's row
|
|
53
|
+
this.heirs = new Heirs(this.p, () => this.#wrote());
|
|
52
54
|
this.door = makeDoor(key, this.heirs, this.doors, () => this.p.public, (n) => ground.random(n)); // pointer one
|
|
53
|
-
this.#boot(this.pk, this); // the ward's ward is itself
|
|
55
|
+
this.#boot(this.pk, () => this); // the ward's ward is itself
|
|
54
56
|
// a restart is silent: every being in the cells is constructed again, unasked.
|
|
55
|
-
// A throw on a boot the owner asked for is that boot's
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
57
|
+
// A throw on a boot the owner asked for is that boot's error, and nothing
|
|
58
|
+
// half-lives. This boot nobody asked for, and there is nobody to hand an
|
|
59
|
+
// error to. Any constructor may throw, a default that is not a value, an
|
|
60
|
+
// author's mistake, a shape she does not recognise after an upgrade, and
|
|
61
|
+
// let through here it is one being taking down the ward and every other
|
|
62
|
+
// being in it, on a restart. She is absent instead: no door, so silence
|
|
63
|
+
// at the door, and her cells sit untouched, waiting for the run that can
|
|
64
|
+
// read them. Her row stays hers: nobody boots another class over it, the
|
|
65
|
+
// owner sees her as absent, and the owner may unboot her.
|
|
63
66
|
for (const [k, c] of Object.entries(this.p.beings)) {
|
|
64
|
-
|
|
67
|
+
const className = c.class;
|
|
68
|
+
if (k === this.pk || !className) continue;
|
|
65
69
|
try {
|
|
66
|
-
this.#
|
|
70
|
+
this.#boot(k, (stance) => this.g.instantiate(className, stance));
|
|
67
71
|
} catch {
|
|
68
72
|
/* she is not here this run. the ward is, and so is everyone else. */
|
|
69
73
|
}
|
|
@@ -84,15 +88,23 @@ class Self implements BeingLike {
|
|
|
84
88
|
// can invite. A stranger cannot be a named asker here, because the door
|
|
85
89
|
// names nobody the ward did not bind.
|
|
86
90
|
async answer(asker: Asker, method?: string, args: JsonObject = {}): Promise<Reply> {
|
|
87
|
-
|
|
91
|
+
// Nobody is not an owner. The door names nobody the ward did not bind, so
|
|
92
|
+
// the only way here unnamed is a partition that says the ward is its own
|
|
93
|
+
// public being, which no owner ask will write. Silence, not an error
|
|
94
|
+
// object: an unnamed asker is a stranger, and a stranger hears one thing.
|
|
95
|
+
if (asker.id === undefined) return silence;
|
|
88
96
|
return ownerAnswer(
|
|
89
97
|
{
|
|
90
98
|
pk: this.pk,
|
|
91
99
|
doors: this.doors,
|
|
100
|
+
absent: () => this.#absent(),
|
|
92
101
|
publicKey: () => this.p.public,
|
|
93
|
-
instantiate: (
|
|
102
|
+
instantiate: (className, key) => this.#instantiate(className, key),
|
|
94
103
|
unboot: (key) => this.#unboot(key),
|
|
95
|
-
setPublic: (key) =>
|
|
104
|
+
setPublic: (key) => {
|
|
105
|
+
this.p.public = key;
|
|
106
|
+
this.#wrote();
|
|
107
|
+
},
|
|
96
108
|
},
|
|
97
109
|
asker,
|
|
98
110
|
method,
|
|
@@ -101,11 +113,45 @@ class Self implements BeingLike {
|
|
|
101
113
|
}
|
|
102
114
|
|
|
103
115
|
// ---- ward functions. on the object. no stance reaches them.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
116
|
+
|
|
117
|
+
// The partition was written. The harbor is told, and nothing more: what it
|
|
118
|
+
// does with the word is its own, and the ward never learns.
|
|
119
|
+
#wrote(): void {
|
|
120
|
+
this.g.wrote?.();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// The rows no door holds this run: beings whose class threw at birth or
|
|
124
|
+
// is not the harbor's to give. Key to class.
|
|
125
|
+
#absent(): Record<string, string | null> {
|
|
126
|
+
const out: Record<string, string | null> = {};
|
|
127
|
+
for (const [k, c] of Object.entries(this.p.beings)) if (k !== this.pk && !this.doors.has(k)) put(out, k, c.class ?? null);
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// A new being under a key nobody holds. A key with a row in the partition
|
|
132
|
+
// is booted, whether a door holds it this run or not: an absent being's
|
|
133
|
+
// row is hers, with every relation in it, and no class is booted over it.
|
|
134
|
+
// A throw at birth is that boot's refusal and nothing half-lives; the
|
|
135
|
+
// ward says which, since the owner hears objects.
|
|
136
|
+
#instantiate(className: string, key: string): Resident | 'threw' | null {
|
|
137
|
+
// The three refusals are read before the constructor runs, and the
|
|
138
|
+
// constructor can reach the stance and boot: a class that boots its own
|
|
139
|
+
// key from inside itself would pass all three, since neither its door nor
|
|
140
|
+
// its row exists yet, and the two objects would then share one row. A key
|
|
141
|
+
// being born is a key taken, for as long as the birth lasts.
|
|
142
|
+
if (typeof key !== 'string' || typeof className !== 'string' || !key || this.doors.has(key) || at(this.p.beings, key) || this.#birthing.has(key)) return null;
|
|
143
|
+
let door: Resident | null;
|
|
144
|
+
this.#birthing.add(key);
|
|
145
|
+
try {
|
|
146
|
+
door = this.#boot(key, (stance) => this.g.instantiate(className, stance));
|
|
147
|
+
} catch {
|
|
148
|
+
return 'threw';
|
|
149
|
+
} finally {
|
|
150
|
+
this.#birthing.delete(key);
|
|
151
|
+
}
|
|
107
152
|
if (!door) return null;
|
|
108
|
-
door.cells.class = className; // so a restart finds her
|
|
153
|
+
unguarded(door.cells).class = className; // so a restart finds her. the ward's key, written behind her guard
|
|
154
|
+
this.#wrote();
|
|
109
155
|
return door;
|
|
110
156
|
}
|
|
111
157
|
|
|
@@ -115,56 +161,77 @@ class Self implements BeingLike {
|
|
|
115
161
|
// rather than meeting a being who is simply not there any more. What is
|
|
116
162
|
// left after that is her cells and her bind table, and they go with her:
|
|
117
163
|
// a row naming a being no door holds would be read on the next restart
|
|
118
|
-
// and boot her again.
|
|
164
|
+
// and boot her again. An absent being leaves the same way: she has no
|
|
165
|
+
// stance to speak for her, so the ward closes her heirs itself.
|
|
119
166
|
#unboot(key: string): string[] | null {
|
|
167
|
+
if (key === this.pk) return null;
|
|
120
168
|
const door = this.doors.get(key);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
169
|
+
const cells = door?.cells ?? at(this.p.beings, key);
|
|
170
|
+
if (!cells) return null;
|
|
171
|
+
const ids = (r: unknown) => (r !== null && typeof r === 'object' ? Object.keys(r) : []);
|
|
172
|
+
const occupants = ids(cells.occupants),
|
|
173
|
+
standings = ids(cells.standings);
|
|
174
|
+
if (door) {
|
|
175
|
+
for (const id of occupants) door.stance.occupants.remove(id);
|
|
176
|
+
for (const id of standings) door.stance.standings.remove(id);
|
|
177
|
+
} else {
|
|
178
|
+
const bind = at(this.p.bind, key);
|
|
179
|
+
for (const heir of Object.values(bind?.occupants ?? {})) this.heirs.close(heir);
|
|
180
|
+
}
|
|
126
181
|
// A ward whose public being is gone answers arrivals for no heir with
|
|
127
182
|
// silence, as a ward that never had one does.
|
|
128
183
|
if (this.p.public === key) this.p.public = null;
|
|
129
184
|
drop(this.p.beings, key);
|
|
130
185
|
drop(this.p.bind, key);
|
|
131
186
|
this.doors.delete(key);
|
|
187
|
+
this.#wrote();
|
|
132
188
|
return [...occupants, ...standings];
|
|
133
189
|
}
|
|
134
190
|
|
|
135
191
|
// Nothing is written until there is somebody to write it for: a class the
|
|
136
192
|
// harbor does not know makes no being, and a boot that made nobody must
|
|
137
193
|
// leave the partition as it found it.
|
|
138
|
-
|
|
139
|
-
|
|
194
|
+
//
|
|
195
|
+
// `make` is who to build with the stance this raises. Two callers and two
|
|
196
|
+
// makers: the ground's `instantiate` for a being of the ward, and the ward
|
|
197
|
+
// itself, which is the first being in its own map and is already made.
|
|
198
|
+
#boot(key: string, make: (stance: Stance) => BeingLike | null): Resident | null {
|
|
199
|
+
const cells = guardCells(at(this.p.beings, key) ?? emptyCells(), () => this.#wrote());
|
|
140
200
|
const bind = at(this.p.bind, key) ?? emptyBind();
|
|
141
201
|
const stance = buildStance(
|
|
142
202
|
{
|
|
143
203
|
pk: this.pk,
|
|
204
|
+
// This stance, not merely this key: unboot and boot again under the
|
|
205
|
+
// same key makes a new being, and the old stance is not hers.
|
|
206
|
+
live: () => this.doors.get(key)?.stance === stance,
|
|
144
207
|
mintKey: (b) => this.#mintKey(b),
|
|
145
208
|
openHeir: (heir, being, id) => this.heirs.open(heir, being, id),
|
|
146
209
|
closeHeir: (heir) => this.heirs.close(heir),
|
|
147
|
-
send: (b, keys,
|
|
210
|
+
send: (b, keys, method, args, wanted) => this.#send(b, keys, method, args, wanted),
|
|
148
211
|
// A throw at birth is null to her, not a throw in her method: she asked
|
|
149
212
|
// for a being and got none, and her own answer is still hers to give.
|
|
150
|
-
instantiate: (
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
} catch {
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
213
|
+
instantiate: (className, k) => {
|
|
214
|
+
const made = this.#instantiate(className, k);
|
|
215
|
+
return made && made !== 'threw' ? made.key : null;
|
|
156
216
|
},
|
|
217
|
+
// The two halves of a maker's way back, each the ward's own call: the
|
|
218
|
+
// being made mints an occupant for her maker, and a relation that
|
|
219
|
+
// never came about takes her out again.
|
|
220
|
+
relate: async (k, id) => (await this.doors.get(k)?.stance.occupants.invite(id)) ?? null,
|
|
221
|
+
unmake: (k) => void this.#unboot(k),
|
|
222
|
+
wrote: () => this.#wrote(),
|
|
157
223
|
},
|
|
158
224
|
key,
|
|
159
225
|
cells,
|
|
160
226
|
bind,
|
|
161
227
|
);
|
|
162
|
-
const being =
|
|
163
|
-
if (!being) return null;
|
|
228
|
+
const being = make(stance);
|
|
229
|
+
if (!being || typeof being.answer !== 'function') return null; // a being is anything with an answer, and nothing without one
|
|
164
230
|
put(this.p.beings, key, cells);
|
|
165
231
|
put(this.p.bind, key, bind);
|
|
166
|
-
const door:
|
|
232
|
+
const door: Resident = { key, cells, bind, stance, being };
|
|
167
233
|
this.doors.set(key, door);
|
|
234
|
+
this.#wrote();
|
|
168
235
|
return door;
|
|
169
236
|
}
|
|
170
237
|
|
|
@@ -176,6 +243,7 @@ class Self implements BeingLike {
|
|
|
176
243
|
// but the keys of the moment.
|
|
177
244
|
bind.minted.push(k.pk);
|
|
178
245
|
if (bind.minted.length > MINTED) bind.minted.splice(0, bind.minted.length - MINTED);
|
|
246
|
+
this.#wrote();
|
|
179
247
|
return k;
|
|
180
248
|
}
|
|
181
249
|
|
|
@@ -184,7 +252,7 @@ class Self implements BeingLike {
|
|
|
184
252
|
// next, seals to the far ward, opens the reply with the ephemeral secret,
|
|
185
253
|
// and rotates to the announced key once the far door has answered under
|
|
186
254
|
// the current one.
|
|
187
|
-
async #send(bind: Bind, keys: StandingKeys,
|
|
255
|
+
async #send(bind: Bind, keys: StandingKeys, method: string | undefined, args: JsonObject, wanted?: Wanted): Promise<ReplyPayload | typeof silence | Word> {
|
|
188
256
|
// What she asked for, held to what this ward allows. Asking for nothing is
|
|
189
257
|
// the default, and asking for more than the ceiling is the ceiling: budget
|
|
190
258
|
// is granted by a ward, never minted by a being.
|
|
@@ -192,8 +260,9 @@ class Self implements BeingLike {
|
|
|
192
260
|
const by = (await beingKey(unhex(keys.current))).pk;
|
|
193
261
|
// A public being is reached without a heir, and the door keeps nothing for
|
|
194
262
|
// whoever asked: no key it vouched for, so nothing to announce and nothing
|
|
195
|
-
// to rotate to. A standing on her signs with one key for life.
|
|
196
|
-
|
|
263
|
+
// to rotate to. A standing on her signs with one key for life. Every other
|
|
264
|
+
// ask announces its next: there is no send that does not.
|
|
265
|
+
if (keys.next === null && keys.heir !== null) keys.next = (await this.#mintKey(bind)).seed;
|
|
197
266
|
const next = keys.next === null ? null : (await beingKey(unhex(keys.next))).pk;
|
|
198
267
|
// Her count for this relation, one higher every call and never reused. The
|
|
199
268
|
// far door honours each number once. One relation sends one at a time, so
|
|
@@ -202,10 +271,12 @@ class Self implements BeingLike {
|
|
|
202
271
|
// Args that will not go through the seal are args that never left. A
|
|
203
272
|
// being holds three answers and a throw is not one of them, so an ask
|
|
204
273
|
// that cannot be sealed is told what is true of it: no door was reached.
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
//
|
|
208
|
-
|
|
274
|
+
// One object of values, by the rule her cells keep and the far door
|
|
275
|
+
// reads: a Date would arrive as a string, a function would not arrive at
|
|
276
|
+
// all, a list or a string would be refused as a stranger's bytes, and
|
|
277
|
+
// she would never know the far being was asked something other than
|
|
278
|
+
// what she said.
|
|
279
|
+
if (args === null || typeof args !== 'object' || Array.isArray(args) || cellFault(args, 'args') !== null) return unreached();
|
|
209
280
|
let sealed;
|
|
210
281
|
try {
|
|
211
282
|
sealed = await sealAsk(keys.heir, { by, next, seq, time: budget.time, method, args }, unhex(keys.current), wardPadlock(keys.ward), this.g.random(32));
|
|
@@ -237,6 +308,7 @@ class Self implements BeingLike {
|
|
|
237
308
|
keys.current = keys.next; // the far door holds `next` as announced. move to it.
|
|
238
309
|
keys.next = null;
|
|
239
310
|
}
|
|
311
|
+
this.#wrote(); // the count moved, and the keys may have
|
|
240
312
|
return reply;
|
|
241
313
|
}
|
|
242
314
|
}
|
package/vectors/arithmetic.json
CHANGED
|
@@ -65,6 +65,13 @@
|
|
|
65
65
|
"signature": "60aa62f7129c8fdeba710872fa10016dae1aa12df0f31969a6baabf60b2ebc3d0822cc9a8b1eb12246076b2eea69d381a03c2f581c05ba6bcca3b4ba80c2f504",
|
|
66
66
|
"refuses": true
|
|
67
67
|
},
|
|
68
|
+
{
|
|
69
|
+
"name": "a zero signature under a non-canonical small-order public key, y equal to p, is silence",
|
|
70
|
+
"voice": "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
|
71
|
+
"message": "62792077686f736520617574686f72697479",
|
|
72
|
+
"signature": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
|
73
|
+
"refuses": true
|
|
74
|
+
},
|
|
68
75
|
{
|
|
69
76
|
"name": "an X25519 agreement",
|
|
70
77
|
"secret": "fcfabee71b7a33993cca5579e6a273ffd1c62cc1749cbf1c9049f599e44f6477",
|
package/vectors/framing.json
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
"corpus": "@quo-systems/quo",
|
|
3
3
|
"encoding": "hex",
|
|
4
4
|
"area": "framing",
|
|
5
|
-
"note": "The formats a second kit must reproduce byte for byte: the ward pk, the digest, the signed ask body, the sealed ask, the sealed reply, the invitation and the knock. Every random input is fixed here, so every output is fixed. The ask body is JSON.stringify of the payload, which is not canonical: these vectors pin the key order sealAsk writes, to, by, next, seq, time, method, args; hops is in no vector, because nothing sets it. A kit that emits another order still interoperates, because a door verifies the bytes it received, but it will not reproduce these vectors. An invitation is JSON too, ward, heir, secret, and a heir secret is the seed of a being key. A knock is an ask whose by is the heir pk, signed with the heir secret, whose next is the knocker's own key, and whose to is the heir; its answer is an ordinary reply, and the heir is spent by it. A reply is one of three shapes: an object with its digest, silence, or the ward's word to a key the door has bound, { quo: word }, where word is one of removed, absent, unannounced, repeated, threw. The ward here is the one from the ward pk vector, and the heir secret is the first entropy it draws, so the invitation it mints and the knock its door binds are these bytes.",
|
|
5
|
+
"note": "The formats a second kit must reproduce byte for byte: the ward pk, the digest, the signed ask body, the sealed ask, the sealed reply, the invitation and the knock. Every random input is fixed here, so every output is fixed. The ask body is JSON.stringify of the payload with the heir it is for in front, which is not canonical: these vectors pin the key order sealAsk writes, to, by, next, seq, time, method, args, and to is signed with the rest, null for nobody; hops is in no vector, because nothing sets it. The two signed bodies are the bytes inside the two sealed asks. A kit that emits another order still interoperates, because a door verifies the bytes it received, but it will not reproduce these vectors. An invitation is JSON too, ward, heir, secret, and a heir secret is the seed of a being key. A knock is an ask whose by is the heir pk, signed with the heir secret, whose next is the knocker's own key, and whose to is the heir; its answer is an ordinary reply, and the heir is spent by it. A reply is one of three shapes: an object with its digest, silence, or the ward's word to a key the door has bound, { quo: word }, where word is one of removed, absent, unannounced, repeated, threw. The ward here is the one from the ward pk vector, and the heir secret is the first entropy it draws, so the invitation it mints and the knock its door binds are these bytes.",
|
|
6
6
|
"vectors": [
|
|
7
7
|
{
|
|
8
8
|
"name": "a ward pk from a fixed seed",
|
|
9
9
|
"seed": "1111111111111111111111111111111111111111111111111111111111111111",
|
|
10
|
-
"pk": "
|
|
11
|
-
"signPk": "
|
|
12
|
-
"padlockPk": "
|
|
10
|
+
"pk": "6d773f8156c80eb9f34d0734e889d824a0d0515f527b853771208ef1ed3feb281fc3da3532de3b20deafd145e30c3de979917e96c22f6c3113a731379eeb3200",
|
|
11
|
+
"signPk": "6d773f8156c80eb9f34d0734e889d824a0d0515f527b853771208ef1ed3feb28",
|
|
12
|
+
"padlockPk": "1fc3da3532de3b20deafd145e30c3de979917e96c22f6c3113a731379eeb3200"
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"name": "a being key from a fixed secret",
|
|
@@ -23,47 +23,62 @@
|
|
|
23
23
|
"digest": "269c9e90f39f6fcae9deab916e3a4148788dec5ba0d06023edc8e52180c773db"
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
|
-
"name": "the
|
|
27
|
-
"
|
|
28
|
-
"
|
|
26
|
+
"name": "the digest where JCS is hard: UTF-16 key order and ES numbers",
|
|
27
|
+
"blueprint": "{\"asks\":[],\"notes\":{\"1\":\"One\",\"\u20ac\":\"Euro Sign\",\"\\r\":\"Carriage Return\",\"\ufb33\":\"Hebrew Letter Dalet With Dagesh\",\"\ud83d\ude00\":\"Emoji: Grinning Face\",\"\u0080\":\"Control\",\"\u00f6\":\"Latin Small Letter O With Diaeresis\"},\"numbers\":[1e+21,0,0.1,1e-7,9007199254740992,0.3333333333333333]}",
|
|
28
|
+
"canonical": "{\"asks\":[],\"notes\":{\"\\r\":\"Carriage Return\",\"1\":\"One\",\"\u0080\":\"Control\",\"\u00f6\":\"Latin Small Letter O With Diaeresis\",\"\u20ac\":\"Euro Sign\",\"\ud83d\ude00\":\"Emoji: Grinning Face\",\"\ufb33\":\"Hebrew Letter Dalet With Dagesh\"},\"numbers\":[1e+21,0,0.1,1e-7,9007199254740992,0.3333333333333333]}",
|
|
29
|
+
"digest": "dc5306089d6bf46d9f91d25290b770c0f18955f1192525b51389fd4c23e02089"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"name": "the signed ask body for nobody, and its signature: to is inside, first",
|
|
33
|
+
"to": "",
|
|
34
|
+
"payload": "{\"to\":null,\"by\":\"a09aa5f47a6759802ff955f8dc2d2a14a5c99d23be97f864127ff9383455a4f0\",\"next\":null,\"seq\":1,\"time\":30000,\"method\":\"hello\",\"args\":{\"name\":\"quo\"}}",
|
|
35
|
+
"body": "7b22746f223a6e756c6c2c226279223a2261303961613566343761363735393830326666393535663864633264326131346135633939643233626539376638363431323766663933383334353561346630222c226e657874223a6e756c6c2c22736571223a312c2274696d65223a33303030302c226d6574686f64223a2268656c6c6f222c2261726773223a7b226e616d65223a2271756f227d7d",
|
|
36
|
+
"secret": "2222222222222222222222222222222222222222222222222222222222222222",
|
|
37
|
+
"signature": "535c50354632d607fc5b68d081a0191ebf85c296b6c7e67b7b0dcc6030d569673dfe35f164b6b2eb3558c4454c32aabc5586bd82e305401a82fda7a166b91906"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"name": "the signed ask body to a heir, and its signature: the same payload, to named",
|
|
41
|
+
"to": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
42
|
+
"payload": "{\"to\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"by\":\"a09aa5f47a6759802ff955f8dc2d2a14a5c99d23be97f864127ff9383455a4f0\",\"next\":null,\"seq\":1,\"time\":30000,\"method\":\"hello\",\"args\":{\"name\":\"quo\"}}",
|
|
43
|
+
"body": "7b22746f223a2261616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161222c226279223a2261303961613566343761363735393830326666393535663864633264326131346135633939643233626539376638363431323766663933383334353561346630222c226e657874223a6e756c6c2c22736571223a312c2274696d65223a33303030302c226d6574686f64223a2268656c6c6f222c2261726773223a7b226e616d65223a2271756f227d7d",
|
|
29
44
|
"secret": "2222222222222222222222222222222222222222222222222222222222222222",
|
|
30
|
-
"signature": "
|
|
45
|
+
"signature": "1f0e63f365dd09bff2c070091ea209d14eefec1026bf210dba87a4a18135fd90107c9879177695f0b4a899e67777b75d6001229e0875657cfc9a2cd197880500"
|
|
31
46
|
},
|
|
32
47
|
{
|
|
33
48
|
"name": "a sealed ask to a heir: the ephemeral pk, then the box, and nothing outside it",
|
|
34
49
|
"to": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
35
50
|
"ephemeralSeed": "3333333333333333333333333333333333333333333333333333333333333333",
|
|
36
|
-
"bytes": "
|
|
51
|
+
"bytes": "7b0d47d93427f8311160781c7c733fd89f88970aef490d8aa0ee19a4cb8a1b140f393feeee5faf9a2707cd3e47c06e7edfd9f5b705ea3ee1a2a2a25b591b37a0af5a068c6455d3cc2dc2948615faf29b182a81ea3d73c166e68f4bb1578c3fbaf22f1dea223d9b84e7e5b90f65139bba894cee2bf68ced2192e548c174bc593dd16d124eb21d3bbde81169bab147676bb11869e08dedb2b15a782d6236ff2ae6b9fa578be8008707ca238f28d39f5029a0340b2e5299e97e44ce9140f8a40ad038d28c931d7872bcfa06f1e29ec964491d4b4ee425ab55b3a09a501a50b6c36f159962247650ec31c42b0c711d97dfc832086366e7c856d332e5bccce04c44c7b67be20bb35d52cccf3a4bf1055fa2a38246c61adc2114da603bd419885b79eb9e3f429d4e26e808beb562d5c60e635649acda8170834f68aee612b56416d369cc72edc8ac1b6204e7"
|
|
37
52
|
},
|
|
38
53
|
{
|
|
39
54
|
"name": "a sealed ask for nobody: the same shape, with to null inside",
|
|
40
55
|
"to": "",
|
|
41
56
|
"ephemeralSeed": "3333333333333333333333333333333333333333333333333333333333333333",
|
|
42
|
-
"bytes": "
|
|
57
|
+
"bytes": "7b0d47d93427f8311160781c7c733fd89f88970aef490d8aa0ee19a4cb8a1b140f393feeee5fe38e2a0a807d44d82d259cd9a4ef05ea6ae6f7f4a20c0f4f6ff9fe09018b3c0187cb74c796d510a9f2cb4d2ad5e8652bc435b48c4fe9018b66eda77f4ebc253ac395f3f4ef4372488dbd8957a368ad8fa162d1e9118121e8452dc46e5a59bd4a2ffbff4a60edf24c603ab44b60f5cbb2e4a7572e7e2634eb76b2e7a15ecffd11de46996598739cdb0e6ae1734771089cbe2b08c6807f88d64cf37b26b15e6a32d654377acd67283bc2cfea8117f24d135dec11d10d05c6e65e67cc09a5334fe6ce0ed4bbcb06f54b304e15725b956f00d5c8f6e3b48e4b634b743c581e15f86f7b17b52a96"
|
|
43
58
|
},
|
|
44
59
|
{
|
|
45
60
|
"name": "the sealed silence reply",
|
|
46
61
|
"reply": "{\"silence\":true}",
|
|
47
62
|
"replySeed": "4444444444444444444444444444444444444444444444444444444444444444",
|
|
48
|
-
"bytes": "
|
|
63
|
+
"bytes": "ff2ee45601ec1b67310c7790404585ae697331eee1c1f8cf2419731c1fff3e6b76ec8fe27c6573176a8974f123537984bdd8baea7cbd4e9b81f38db22c5917e9674bc082183e532adba06227f7e023955f16afcb723ca826a579fc08af3306824c8f8012687532ad79d81b77a7c84a429d151cc593b81312bc44433571a0b4aa"
|
|
49
64
|
},
|
|
50
65
|
{
|
|
51
66
|
"name": "a sealed object reply",
|
|
52
67
|
"reply": "{\"object\":{\"hi\":\"quo\"},\"seen\":null}",
|
|
53
68
|
"replySeed": "4444444444444444444444444444444444444444444444444444444444444444",
|
|
54
|
-
"bytes": "
|
|
69
|
+
"bytes": "ff2ee45601ec1b67310c7790404585ae697331eee1c1f8cf2419731c1fff3e6b76ec93e97a657e002d9135a7394f3ec33bcd723c9d8947909f4cc659d8dfeed743f5b6012ed80af39f9d1bc16c995f69944a0c94244e349308704f2070a98b59a5a4f3e9b1e2a1bfe2e7bcba2446de1fbf84dc15cc5fa5ac03a175cce7d2eaa07b0e1148416580932306d41dc961f145fa7141"
|
|
55
70
|
},
|
|
56
71
|
{
|
|
57
72
|
"name": "a sealed word reply: the ward's word to a key it has bound",
|
|
58
73
|
"reply": "{\"quo\":\"removed\"}",
|
|
59
74
|
"replySeed": "4444444444444444444444444444444444444444444444444444444444444444",
|
|
60
|
-
"bytes": "
|
|
75
|
+
"bytes": "ff2ee45601ec1b67310c7790404585ae697331eee1c1f8cf2419731c1fff3e6b76ec8dfe7f2227567dce23ea274378db6487fe336070ff4780aec6e6cb89197c05df879e39252f523fc910bf2268ded0d2f323f395a4e2352a5004424b828c43f3163595c67c120e5ea029cfecec4abe575fb4327449cd6003634f30b3cc056a54"
|
|
61
76
|
},
|
|
62
77
|
{
|
|
63
78
|
"name": "an invitation for a heir: the ward pk, the heir pk, the heir secret, and nothing else",
|
|
64
79
|
"wardSeed": "1111111111111111111111111111111111111111111111111111111111111111",
|
|
65
80
|
"heirSecret": "4444444444444444444444444444444444444444444444444444444444444444",
|
|
66
|
-
"invitation": "{\"ward\":\"
|
|
81
|
+
"invitation": "{\"ward\":\"6d773f8156c80eb9f34d0734e889d824a0d0515f527b853771208ef1ed3feb281fc3da3532de3b20deafd145e30c3de979917e96c22f6c3113a731379eeb3200\",\"heir\":\"d759793bbc13a2819a827c76adb6fba8a49aee007f49f2d0992d99b825ad2c48\",\"secret\":\"4444444444444444444444444444444444444444444444444444444444444444\"}"
|
|
67
82
|
},
|
|
68
83
|
{
|
|
69
84
|
"name": "a sealed knock: the first ask under a heir, by the heir, announcing her own key, to the heir",
|
|
@@ -71,7 +86,7 @@
|
|
|
71
86
|
"ownSecret": "5555555555555555555555555555555555555555555555555555555555555555",
|
|
72
87
|
"ephemeralSeed": "6666666666666666666666666666666666666666666666666666666666666666",
|
|
73
88
|
"payload": "{\"to\":\"d759793bbc13a2819a827c76adb6fba8a49aee007f49f2d0992d99b825ad2c48\",\"by\":\"d759793bbc13a2819a827c76adb6fba8a49aee007f49f2d0992d99b825ad2c48\",\"next\":\"c6822637c7d310ec57627be00ba259d253749f4aaf644470cffbe53a35f73242\",\"seq\":1,\"time\":30000,\"method\":\"hello\",\"args\":{\"name\":\"quo\"}}",
|
|
74
|
-
"bytes": "
|
|
89
|
+
"bytes": "219e4d800da968d2a5fcb009c784f4746c7138edb9ee4844b739e830b05cf424e0590708caeb7c3c01e2ae8a2fa26a18bc504b84e06e80b104eab60c233b6ab42709b03434573a932c55e1977311a6358713251329e652d3d4bf212e4fe2062629f886a825315d8338de702e40457e2371eb4cd3b6633e0e986ea724fe6128a664bc61c7113106858540f20df784583e3ede4f125841a5d359cc94784bcee04d303b26b1008392420ae8f0930650c6aeefa3ed6138ab791a92870cb4f4d2d8e634e216cfdec86e8b3ceeef0b4f8a49477e95c04423808150ef381f7ef5390f3182f825a7be03e48edd981d66c3131ed9cca37c6348ef7f8dccc01018ff8417e09c6958b77ea269c58a47683f2b369ca13a434117d38f2f726b8a422e5004db32613ddc300d6f90805597d96ed51ef22bdd6353507857ab50edc2a3719e9081491a4722728275e263c9f6c286a1b39ab7cbd755805f2610410073ece392af0b3b960e68b0143a05674398927a57e2d99396b577ef7d1c618d32c9af619a79954ad413d7b5fe4cf3"
|
|
75
90
|
}
|
|
76
91
|
]
|
|
77
92
|
}
|
package/vectors/wire.json
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
{
|
|
8
8
|
"name": "an ask frame: kind, id, pk, bytes",
|
|
9
9
|
"id": 1,
|
|
10
|
-
"pk": "
|
|
10
|
+
"pk": "6d773f8156c80eb9f34d0734e889d824a0d0515f527b853771208ef1ed3feb281fc3da3532de3b20deafd145e30c3de979917e96c22f6c3113a731379eeb3200",
|
|
11
11
|
"bytes": "010203",
|
|
12
|
-
"frame": "
|
|
12
|
+
"frame": "00000000016d773f8156c80eb9f34d0734e889d824a0d0515f527b853771208ef1ed3feb281fc3da3532de3b20deafd145e30c3de979917e96c22f6c3113a731379eeb3200010203"
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"name": "a reply frame: kind, id, bytes",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
{
|
|
26
26
|
"name": "the announce frame, text",
|
|
27
27
|
"pks": [
|
|
28
|
-
"
|
|
28
|
+
"6d773f8156c80eb9f34d0734e889d824a0d0515f527b853771208ef1ed3feb281fc3da3532de3b20deafd145e30c3de979917e96c22f6c3113a731379eeb3200"
|
|
29
29
|
],
|
|
30
|
-
"frame": "{\"announce\":[\"
|
|
30
|
+
"frame": "{\"announce\":[\"6d773f8156c80eb9f34d0734e889d824a0d0515f527b853771208ef1ed3feb281fc3da3532de3b20deafd145e30c3de979917e96c22f6c3113a731379eeb3200\"],\"suite\":1}",
|
|
31
31
|
"suite": 1
|
|
32
32
|
},
|
|
33
33
|
{
|