@charter-agreement-protocol/signer 0.2.1 → 0.3.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 +149 -3
- package/CODE_OF_CONDUCT.md +87 -0
- package/CONTRIBUTING.md +71 -0
- package/README.md +211 -71
- package/SECURITY.md +55 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -4
- package/dist/signer.d.ts +17 -22
- package/dist/signer.js +258 -149
- package/package.json +13 -8
package/dist/signer.js
CHANGED
|
@@ -1,180 +1,289 @@
|
|
|
1
1
|
// CAP never authorizes.
|
|
2
|
-
//
|
|
3
|
-
// The
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
2
|
+
// The holder supplies one key snapshot and signs the verifier's prepared
|
|
3
|
+
// message. The verifier owns framing, claims, context, limits, profile,
|
|
4
|
+
// key matching, assembly, and completion. This package keeps custody and
|
|
5
|
+
// the wrong-key guard.
|
|
6
|
+
import { DEFAULT_LIMITS, algorithmRegistry, MAXIMUM_LIMITS, defaultEmissionName, decodeCharterRevision, emissions, finishSigning, prepareSigning, verifySignature, } from "@charter-agreement-protocol/verifier";
|
|
7
|
+
const COPY_LIMIT = Symbol("copy limit");
|
|
8
|
+
// Read one child at a time without recursion or array constructor hooks.
|
|
9
|
+
// Only ancestors are retained, so repeated objects get separate copies.
|
|
10
|
+
function copyClaims(value) {
|
|
11
|
+
const limits = MAXIMUM_LIMITS;
|
|
12
|
+
const ancestors = new WeakSet();
|
|
13
|
+
const frames = [];
|
|
14
|
+
let bytes = 0;
|
|
15
|
+
const addBytes = (size) => {
|
|
16
|
+
bytes += size;
|
|
17
|
+
if (bytes > limits.max_bytes)
|
|
18
|
+
throw COPY_LIMIT;
|
|
19
|
+
};
|
|
20
|
+
const stringBytes = (text) => {
|
|
21
|
+
if (Buffer.byteLength(text) > limits.max_string_bytes)
|
|
22
|
+
throw COPY_LIMIT;
|
|
23
|
+
return Buffer.byteLength(JSON.stringify(text));
|
|
24
|
+
};
|
|
25
|
+
const copy = (item, depth) => {
|
|
26
|
+
if (item === null || typeof item === "boolean") {
|
|
27
|
+
addBytes(item === null || item === true ? 4 : 5);
|
|
28
|
+
return item;
|
|
29
|
+
}
|
|
30
|
+
if (typeof item === "string") {
|
|
31
|
+
addBytes(stringBytes(item));
|
|
32
|
+
return item;
|
|
33
|
+
}
|
|
34
|
+
if (typeof item === "number" && Number.isFinite(item)) {
|
|
35
|
+
addBytes(JSON.stringify(item).length);
|
|
36
|
+
return item;
|
|
37
|
+
}
|
|
38
|
+
if (typeof item !== "object" || ancestors.has(item))
|
|
39
|
+
throw new TypeError("invalid_type");
|
|
40
|
+
if (depth + 1 > limits.max_depth)
|
|
41
|
+
throw COPY_LIMIT;
|
|
42
|
+
const array = Array.isArray(item);
|
|
43
|
+
let keys = null;
|
|
44
|
+
if (!array) {
|
|
45
|
+
const prototype = Object.getPrototypeOf(item);
|
|
46
|
+
if ((prototype !== Object.prototype && prototype !== null) || Object.getOwnPropertySymbols(item).length > 0) {
|
|
47
|
+
throw new TypeError("invalid_type");
|
|
48
|
+
}
|
|
49
|
+
keys = Object.keys(item);
|
|
50
|
+
}
|
|
51
|
+
const size = array ? item.length : keys.length;
|
|
52
|
+
if (!Number.isSafeInteger(size) || size < 0 || array && size > 2 ** 32 - 1)
|
|
53
|
+
throw new TypeError("invalid_type");
|
|
54
|
+
if (size > (array ? limits.max_array_items : limits.max_object_members)) {
|
|
55
|
+
throw COPY_LIMIT;
|
|
56
|
+
}
|
|
57
|
+
addBytes(2 + Math.max(0, size - 1));
|
|
58
|
+
const target = array ? [] : Object.create(null);
|
|
59
|
+
ancestors.add(item);
|
|
60
|
+
frames.push({ source: item, target, keys, size, index: 0, depth: depth + 1 });
|
|
61
|
+
return target;
|
|
62
|
+
};
|
|
63
|
+
const result = copy(value, 0);
|
|
64
|
+
while (frames.length > 0) {
|
|
65
|
+
const frame = frames[frames.length - 1];
|
|
66
|
+
if (frame.index === frame.size) {
|
|
67
|
+
ancestors.delete(frame.source);
|
|
68
|
+
frames.pop();
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const index = frame.index++;
|
|
72
|
+
const key = frame.keys ? frame.keys[index] : String(index);
|
|
73
|
+
if (frame.keys)
|
|
74
|
+
addBytes(stringBytes(key) + 1);
|
|
75
|
+
const child = frame.source[key];
|
|
76
|
+
const copied = copy(child, frame.depth);
|
|
77
|
+
if (Array.isArray(frame.target))
|
|
78
|
+
frame.target.push(copied);
|
|
79
|
+
else
|
|
80
|
+
frame.target[key] = copied;
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
function copyChainView(chain) {
|
|
85
|
+
const names = ["revisions", "acceptances", "descriptors", "terminations"];
|
|
86
|
+
const lists = Object.fromEntries(names.map((name) => [name, (value) => copyTextList(value, MAXIMUM_LIMITS.max_artifact_set_items + 1)]));
|
|
87
|
+
return optionMembers(chain, names, lists);
|
|
21
88
|
}
|
|
22
89
|
async function resolveKeyIdentity(handle) {
|
|
23
|
-
if (handle === null || typeof handle !== "object" || !("keyIdentity" in handle)) {
|
|
24
|
-
return { error: "invalid_handle" };
|
|
25
|
-
}
|
|
26
90
|
try {
|
|
91
|
+
if (handle === null || typeof handle !== "object")
|
|
92
|
+
return { error: "invalid_key_handle" };
|
|
27
93
|
const snapshot = await handle.keyIdentity(handle);
|
|
28
|
-
if (snapshot
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return snapshot;
|
|
94
|
+
if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot))
|
|
95
|
+
return { error: "invalid_key_handle" };
|
|
96
|
+
const { kid, publicKey } = snapshot;
|
|
97
|
+
if (typeof kid !== "string" || kid.length === 0 || typeof publicKey !== "string") {
|
|
98
|
+
return { error: "invalid_key_handle" };
|
|
34
99
|
}
|
|
35
|
-
return {
|
|
100
|
+
return { kid, publicKey };
|
|
36
101
|
}
|
|
37
102
|
catch (_fault) {
|
|
38
|
-
return { error: "
|
|
103
|
+
return { error: "invalid_key_handle" };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const OPTION_KEYS = new Set(["algorithm", "limits", "profile"]);
|
|
107
|
+
const byteArrayTag = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Uint8Array.prototype), Symbol.toStringTag).get;
|
|
108
|
+
// Read the defined shape once, including inherited and nonenumerable members.
|
|
109
|
+
// Invalid member values stay opaque for the verifier to check synchronously.
|
|
110
|
+
function optionMembers(value, names, nested = {}) {
|
|
111
|
+
if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Number)
|
|
112
|
+
return value;
|
|
113
|
+
const source = value;
|
|
114
|
+
const result = Object.create(null);
|
|
115
|
+
const unknown = Object.keys(source).find((name) => !names.includes(name));
|
|
116
|
+
if (unknown !== undefined)
|
|
117
|
+
result[unknown] = null;
|
|
118
|
+
for (const name of names) {
|
|
119
|
+
const member = source[name];
|
|
120
|
+
if (member !== undefined || name in source)
|
|
121
|
+
result[name] = nested[name] ? nested[name](member) : member;
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
function optionAlgorithms(value) {
|
|
126
|
+
if (!Array.isArray(value))
|
|
127
|
+
return value;
|
|
128
|
+
const length = value.length;
|
|
129
|
+
if (!Number.isSafeInteger(length) || length < 0)
|
|
130
|
+
throw new TypeError("invalid_type");
|
|
131
|
+
// A unique registry subset cannot be longer than the registry. Retain an
|
|
132
|
+
// invalid length for the verifier without reading an unbounded caller list.
|
|
133
|
+
if (length > MAXIMUM_LIMITS.max_artifact_set_items)
|
|
134
|
+
return Array(MAXIMUM_LIMITS.max_artifact_set_items + 1);
|
|
135
|
+
if (length > algorithmRegistry().length)
|
|
136
|
+
return value;
|
|
137
|
+
const result = [];
|
|
138
|
+
for (let i = 0; i < length; i += 1)
|
|
139
|
+
result.push(value[i]);
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
function copyTextList(value, maximum = 2 ** 32 - 1) {
|
|
143
|
+
if (!Array.isArray(value))
|
|
144
|
+
return value;
|
|
145
|
+
const length = value.length;
|
|
146
|
+
// A plain array cannot represent a length above 2^32 - 1.
|
|
147
|
+
if (!Number.isSafeInteger(length) || length < 0 || length > 2 ** 32 - 1)
|
|
148
|
+
throw new TypeError("invalid_type");
|
|
149
|
+
const result = [];
|
|
150
|
+
for (let i = 0; i < Math.min(length, maximum); i += 1)
|
|
151
|
+
result.push(value[i]);
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
function checkOptions(settings) {
|
|
155
|
+
// Empty text always gives invalid_syntax after successful option checks.
|
|
156
|
+
// Every earlier code comes from the verifier, including copy failures.
|
|
157
|
+
const checked = decodeCharterRevision("", settings);
|
|
158
|
+
return !checked.ok && checked.code !== "invalid_syntax" ? { error: "invalid_input", code: checked.code } : null;
|
|
159
|
+
}
|
|
160
|
+
function signingOptions(opts, descriptor) {
|
|
161
|
+
try {
|
|
162
|
+
if (opts === null || typeof opts !== "object" || Array.isArray(opts) || opts instanceof Number) {
|
|
163
|
+
return { error: "invalid_input", code: "invalid_type" };
|
|
164
|
+
}
|
|
165
|
+
if (!Reflect.ownKeys(opts).every((key) => typeof key === "string" &&
|
|
166
|
+
(OPTION_KEYS.has(key) || descriptor && key === "predecessor"))) {
|
|
167
|
+
return { error: "invalid_input", code: "invalid_type" };
|
|
168
|
+
}
|
|
169
|
+
const supplied = opts;
|
|
170
|
+
const settings = {
|
|
171
|
+
limits: optionMembers(supplied.limits, Object.keys(DEFAULT_LIMITS)),
|
|
172
|
+
};
|
|
173
|
+
const limitsError = checkOptions(settings);
|
|
174
|
+
if (limitsError)
|
|
175
|
+
return limitsError;
|
|
176
|
+
settings.profile = optionMembers(supplied.profile, ["algorithms", "revisions"], {
|
|
177
|
+
algorithms: optionAlgorithms,
|
|
178
|
+
revisions: (range) => optionMembers(range, ["min", "max"]),
|
|
179
|
+
});
|
|
180
|
+
const profileError = checkOptions(settings);
|
|
181
|
+
if (profileError)
|
|
182
|
+
return profileError;
|
|
183
|
+
const algorithm = supplied.algorithm ?? defaultEmissionName();
|
|
184
|
+
if (typeof algorithm !== "string" || !Object.hasOwn(emissions(), algorithm)) {
|
|
185
|
+
return { error: "invalid_input", code: "algorithm_unsupported" };
|
|
186
|
+
}
|
|
187
|
+
if (!descriptor)
|
|
188
|
+
return { ...settings, algorithm };
|
|
189
|
+
const previous = supplied.predecessor;
|
|
190
|
+
const predecessor = optionMembers(previous, ["digest", "descriptor_digest", "lineage"], {
|
|
191
|
+
lineage: (value) => copyTextList(value),
|
|
192
|
+
});
|
|
193
|
+
return { ...settings, algorithm, predecessor: predecessor };
|
|
194
|
+
}
|
|
195
|
+
catch (fault) {
|
|
196
|
+
return { error: "invalid_input", code: fault === COPY_LIMIT ? "limit_exceeded" : "invalid_type" };
|
|
39
197
|
}
|
|
40
198
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
199
|
+
function prepareError(code) {
|
|
200
|
+
switch (code) {
|
|
201
|
+
case "signing_refused":
|
|
202
|
+
case "signing_key_mismatch":
|
|
203
|
+
return { error: "refused", code };
|
|
204
|
+
case "signing_key_invalid":
|
|
205
|
+
return { error: "invalid_key_handle" };
|
|
206
|
+
case "algorithm_unsupported_on_substrate":
|
|
207
|
+
return { error: "algorithm_unsupported_on_substrate" };
|
|
208
|
+
default:
|
|
209
|
+
return { error: "invalid_input", code };
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async function signPrepared(kind, claims, keyHandle, context, opts) {
|
|
213
|
+
const options = signingOptions(opts, kind === "descriptor");
|
|
214
|
+
if ("error" in options)
|
|
215
|
+
return options;
|
|
216
|
+
let retained;
|
|
217
|
+
try {
|
|
218
|
+
retained = kind === "descriptor" ? options.predecessor ?? null :
|
|
219
|
+
typeof context === "string" ? context : copyChainView(context);
|
|
220
|
+
}
|
|
221
|
+
catch (fault) {
|
|
222
|
+
return { error: "invalid_input", code: fault === COPY_LIMIT ? "limit_exceeded" : "invalid_type" };
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
225
|
+
// Caller limits belong to preparation. The entry guard only stops values
|
|
226
|
+
// beyond every JSON limit the verifier can accept.
|
|
227
|
+
claims = copyClaims(claims);
|
|
228
|
+
}
|
|
229
|
+
catch (fault) {
|
|
230
|
+
return { error: "invalid_input", code: fault === COPY_LIMIT ? "signing_input_invalid" : "invalid_type" };
|
|
231
|
+
}
|
|
46
232
|
const snapshot = await resolveKeyIdentity(keyHandle);
|
|
47
233
|
if ("error" in snapshot)
|
|
48
234
|
return snapshot;
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
// The holder signs the exact message; a fault or a wrong-length result is
|
|
58
|
-
// a signing failure, never a silent pass.
|
|
235
|
+
const prepared = prepareSigning(kind, snapshot.kid, claims, snapshot.publicKey, retained, {
|
|
236
|
+
algorithm: options.algorithm,
|
|
237
|
+
limits: options.limits,
|
|
238
|
+
profile: options.profile,
|
|
239
|
+
});
|
|
240
|
+
if (!prepared.ok)
|
|
241
|
+
return prepareError(prepared.code);
|
|
242
|
+
const input = prepared.plan.input;
|
|
59
243
|
let signature;
|
|
60
244
|
try {
|
|
61
|
-
signature = await keyHandle.sign(
|
|
245
|
+
signature = await keyHandle.sign(Buffer.from(input.message), keyHandle);
|
|
62
246
|
}
|
|
63
247
|
catch (_fault) {
|
|
64
248
|
return { error: "signing_failed" };
|
|
65
249
|
}
|
|
66
|
-
if (!(signature
|
|
250
|
+
if (!ArrayBuffer.isView(signature) || byteArrayTag.call(signature) !== "Uint8Array") {
|
|
67
251
|
return { error: "signing_failed" };
|
|
68
252
|
}
|
|
69
|
-
|
|
70
|
-
|
|
253
|
+
let checkedSignature;
|
|
254
|
+
try {
|
|
255
|
+
checkedSignature = Buffer.from(signature);
|
|
256
|
+
}
|
|
257
|
+
catch (_signature) {
|
|
71
258
|
return { error: "signing_failed" };
|
|
72
259
|
}
|
|
73
|
-
// The
|
|
74
|
-
|
|
75
|
-
|
|
260
|
+
// The verifier's signature guard owns signature length checks.
|
|
261
|
+
if (!verifySignature(prepared.plan.input.message, checkedSignature, snapshot.publicKey, input.alg, {
|
|
262
|
+
limits: options.limits,
|
|
263
|
+
profile: options.profile,
|
|
264
|
+
})) {
|
|
76
265
|
return { error: "signing_failed" };
|
|
77
266
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (!assembled.ok)
|
|
85
|
-
return { error: "invalid_input", code: assembled.code };
|
|
86
|
-
return { compact: assembled.compact };
|
|
87
|
-
}
|
|
88
|
-
// The view's chain is THIS package's public contract (the producer surface
|
|
89
|
-
// validates its depth); the shallow shape is policed here so the closed
|
|
90
|
-
// error vocabulary holds regardless of which verifier revision resolves.
|
|
91
|
-
// A verifier crash past this gate stays loud - never a misleading error.
|
|
92
|
-
function chainViewOrInvalid(chain) {
|
|
93
|
-
if (!chain || typeof chain !== "object" || Array.isArray(chain)) {
|
|
94
|
-
return { error: "invalid_input", code: "signing_input_invalid" };
|
|
95
|
-
}
|
|
96
|
-
return chain;
|
|
97
|
-
}
|
|
98
|
-
// A missing view object is caller input like a missing chain: the closed
|
|
99
|
-
// error, never a dereference throw.
|
|
100
|
-
function viewChainOrInvalid(view) {
|
|
101
|
-
if (!view || typeof view !== "object")
|
|
102
|
-
return { error: "invalid_input", code: "signing_input_invalid" };
|
|
103
|
-
return chainViewOrInvalid(view.chain);
|
|
267
|
+
const finished = finishSigning(prepared.plan, checkedSignature);
|
|
268
|
+
// No real-crypto path reaches this mapping after preparation and the
|
|
269
|
+
// custody guard succeed; keep it for a verifier completion error.
|
|
270
|
+
if (!finished.ok)
|
|
271
|
+
return { error: "verification_failed" };
|
|
272
|
+
return { compact: finished.compact };
|
|
104
273
|
}
|
|
105
|
-
// ---------------------------------------------------------------------------
|
|
106
|
-
// Public signing surface
|
|
107
|
-
// ---------------------------------------------------------------------------
|
|
108
274
|
export async function signDescriptor(claims, keyHandle, opts = {}) {
|
|
109
|
-
const signed = await
|
|
110
|
-
|
|
111
|
-
return { ok: false, ...signed };
|
|
112
|
-
const assembled = compactOrInvalid(signed);
|
|
113
|
-
if ("error" in assembled)
|
|
114
|
-
return { ok: false, ...assembled };
|
|
115
|
-
const verified = verifyDescriptor(assembled.compact);
|
|
116
|
-
if (!verified.ok)
|
|
117
|
-
return { ok: false, error: "verification_failed" };
|
|
118
|
-
return { ok: true, result: { descriptor: assembled.compact } };
|
|
275
|
+
const signed = await signPrepared("descriptor", claims, keyHandle, null, opts);
|
|
276
|
+
return "error" in signed ? { ok: false, ...signed } : { ok: true, result: { descriptor: signed.compact } };
|
|
119
277
|
}
|
|
120
|
-
export async function signReceipt(claims, keyHandle,
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
// before the key - the reference's context is a verified ChainFacts by
|
|
124
|
-
// construction, so a TS view that fails the chain discipline is caller
|
|
125
|
-
// input, never a burned key operation.
|
|
126
|
-
const signed = await signProduced(keyHandle, (kid, algorithm) => {
|
|
127
|
-
const built = receiptSigningInput(kid, claims, algorithm);
|
|
128
|
-
if (!built.ok)
|
|
129
|
-
return built;
|
|
130
|
-
const resolved = chainViewOrInvalid(chain);
|
|
131
|
-
if ("error" in resolved)
|
|
132
|
-
return { ok: false, code: resolved.code };
|
|
133
|
-
if (!verifyChain(resolved).ok)
|
|
134
|
-
return { ok: false, code: "chain_invalid" };
|
|
135
|
-
return built;
|
|
136
|
-
}, opts);
|
|
137
|
-
if ("error" in signed)
|
|
138
|
-
return { ok: false, ...signed };
|
|
139
|
-
const assembled = compactOrInvalid(signed);
|
|
140
|
-
if ("error" in assembled)
|
|
141
|
-
return { ok: false, ...assembled };
|
|
142
|
-
const verified = verifyReceipt(assembled.compact, chain);
|
|
143
|
-
if (!verified.ok)
|
|
144
|
-
return { ok: false, error: "verification_failed" };
|
|
145
|
-
return { ok: true, result: { receipt: assembled.compact } };
|
|
278
|
+
export async function signReceipt(claims, keyHandle, context, opts = {}) {
|
|
279
|
+
const signed = await signPrepared("receipt", claims, keyHandle, context, opts);
|
|
280
|
+
return "error" in signed ? { ok: false, ...signed } : { ok: true, result: { receipt: signed.compact } };
|
|
146
281
|
}
|
|
147
|
-
export async function signAcceptance(claims, keyHandle,
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
if ("error" in viewChain)
|
|
151
|
-
return { ok: false, code: viewChain.code };
|
|
152
|
-
return acceptanceSigningInput(kid, claims, viewChain, algorithm);
|
|
153
|
-
}, opts);
|
|
154
|
-
if ("error" in signed)
|
|
155
|
-
return { ok: false, ...signed };
|
|
156
|
-
const assembled = compactOrInvalid(signed);
|
|
157
|
-
if ("error" in assembled)
|
|
158
|
-
return { ok: false, ...assembled };
|
|
159
|
-
const verified = verifyAcceptance(assembled.compact, view.revisionText, view.descriptorCompacts);
|
|
160
|
-
if (!verified.ok)
|
|
161
|
-
return { ok: false, error: "verification_failed" };
|
|
162
|
-
return { ok: true, result: { acceptance: assembled.compact } };
|
|
282
|
+
export async function signAcceptance(claims, keyHandle, chain, opts = {}) {
|
|
283
|
+
const signed = await signPrepared("acceptance", claims, keyHandle, chain, opts);
|
|
284
|
+
return "error" in signed ? { ok: false, ...signed } : { ok: true, result: { acceptance: signed.compact } };
|
|
163
285
|
}
|
|
164
|
-
export async function signTermination(claims, keyHandle,
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
if ("error" in viewChain)
|
|
168
|
-
return { ok: false, code: viewChain.code };
|
|
169
|
-
return terminationSigningInput(kid, claims, viewChain, algorithm);
|
|
170
|
-
}, opts);
|
|
171
|
-
if ("error" in signed)
|
|
172
|
-
return { ok: false, ...signed };
|
|
173
|
-
const assembled = compactOrInvalid(signed);
|
|
174
|
-
if ("error" in assembled)
|
|
175
|
-
return { ok: false, ...assembled };
|
|
176
|
-
const verified = verifyTermination(assembled.compact, view.revisionText, view.descriptorCompacts);
|
|
177
|
-
if (!verified.ok)
|
|
178
|
-
return { ok: false, error: "verification_failed" };
|
|
179
|
-
return { ok: true, result: { termination: assembled.compact } };
|
|
286
|
+
export async function signTermination(claims, keyHandle, chain, opts = {}) {
|
|
287
|
+
const signed = await signPrepared("termination", claims, keyHandle, chain, opts);
|
|
288
|
+
return "error" in signed ? { ok: false, ...signed } : { ok: true, result: { termination: signed.compact } };
|
|
180
289
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@charter-agreement-protocol/signer",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Holder-side companion signer for the Charter Agreement Protocol (CAP) in TypeScript - key custody only: an atomic key snapshot, the sign callback, the wrong-key guard, and post-sign verification, with framing,
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Holder-side companion signer for the Charter Agreement Protocol (CAP) in TypeScript - key custody only: an atomic key snapshot, the sign callback, the wrong-key guard, and post-sign verification, with framing, claims, context, key matching, limits, profile, and completion delegated to the @charter-agreement-protocol/verifier pre-sign API. CAP verifies; it never authorizes; this signer signs, it never verifies third-party artifacts or takes custody.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.js",
|
|
@@ -15,32 +15,37 @@
|
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
17
|
"CHANGELOG.md",
|
|
18
|
+
"CODE_OF_CONDUCT.md",
|
|
19
|
+
"CONTRIBUTING.md",
|
|
18
20
|
"dist",
|
|
19
21
|
"LICENSE",
|
|
20
22
|
"NOTICE",
|
|
21
|
-
"README.md"
|
|
23
|
+
"README.md",
|
|
24
|
+
"SECURITY.md"
|
|
22
25
|
],
|
|
23
26
|
"engines": {
|
|
24
27
|
"node": ">=24.8.0"
|
|
25
28
|
},
|
|
26
29
|
"scripts": {
|
|
27
|
-
"build": "tsc -p tsconfig.build.json",
|
|
30
|
+
"build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.build.json",
|
|
28
31
|
"typecheck": "tsc --noEmit",
|
|
29
|
-
"test": "node --test --experimental-strip-types --no-warnings signer.test.ts",
|
|
32
|
+
"test": "node --test --experimental-strip-types --no-warnings signer.test.ts signer.browser.test.ts signer.cases.test.ts",
|
|
33
|
+
"test:cost": "node --test --experimental-strip-types --no-warnings signer.cost.test.ts",
|
|
34
|
+
"test:cases": "node --test --experimental-strip-types --no-warnings signer.cases.test.ts",
|
|
30
35
|
"prepack": "npm run build",
|
|
31
36
|
"typecheck:web": "tsc -p tsconfig.web.json",
|
|
32
37
|
"build:site": "node web/build.mjs && esbuild web/main.ts --bundle --minify --format=iife --target=es2022 --outfile=site-dist/app.js --alias:node:crypto=./web/shim/node-crypto.ts --alias:node:fs=./web/shim/node-fs.ts --alias:node:path=./web/shim/node-path.ts --inject:./web/shim/buffer-global.ts"
|
|
33
38
|
},
|
|
34
39
|
"dependencies": {
|
|
35
|
-
"@charter-agreement-protocol/verifier": "^0.
|
|
40
|
+
"@charter-agreement-protocol/verifier": "^0.7.0"
|
|
36
41
|
},
|
|
37
42
|
"devDependencies": {
|
|
38
43
|
"@noble/ed25519": "^3.2.0",
|
|
39
44
|
"@noble/hashes": "^2.4.0",
|
|
40
|
-
"@types/node": "^26.
|
|
45
|
+
"@types/node": "^26.6.4",
|
|
41
46
|
"buffer": "^6.0.3",
|
|
42
47
|
"esbuild": "^0.28.2",
|
|
43
|
-
"typescript": "
|
|
48
|
+
"typescript": "^7.0.2"
|
|
44
49
|
},
|
|
45
50
|
"repository": {
|
|
46
51
|
"type": "git",
|