@byollm/protocol 0.1.0-alpha.93 → 0.1.0-alpha.94
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 +1 -1
- package/dist/index.d.ts +67 -1
- package/dist/index.js +91 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -3195,4 +3195,70 @@ interface ConsoleOrder {
|
|
|
3195
3195
|
}
|
|
3196
3196
|
declare function consoleOrder(from: "browser" | "box"): ConsoleOrder;
|
|
3197
3197
|
|
|
3198
|
-
|
|
3198
|
+
/**
|
|
3199
|
+
* The envelope's FORMAT, with no primitives and no platform — B018c step 3.
|
|
3200
|
+
*
|
|
3201
|
+
* ## Why this file exists
|
|
3202
|
+
*
|
|
3203
|
+
* The browser is one of the two ends a console stream is encrypted between,
|
|
3204
|
+
* and it cannot run `envelope.ts`: the sealing is WASM and portable, but the
|
|
3205
|
+
* inner signature goes through `node:crypto`'s Ed25519. So the browser needs
|
|
3206
|
+
* its own primitives.
|
|
3207
|
+
*
|
|
3208
|
+
* What it must NOT have is its own *format*. Two implementations of "which
|
|
3209
|
+
* bytes get signed" is the defect class this codebase spends most of its
|
|
3210
|
+
* checks on, and here the two copies would diverge silently — a mismatched
|
|
3211
|
+
* signature is indistinguishable from an attack, so the first symptom would
|
|
3212
|
+
* be a console that refuses to open and a log line saying `bad-signature`.
|
|
3213
|
+
*
|
|
3214
|
+
* Everything here is therefore pure: no `node:` imports, no `Buffer`, no
|
|
3215
|
+
* `btoa`. `envelope-is-portable.test.ts` asserts that mechanically, because
|
|
3216
|
+
* "somebody will import Buffer for convenience one day" is a prediction, and
|
|
3217
|
+
* this project's rule is that a prediction ships with the test that catches it.
|
|
3218
|
+
*
|
|
3219
|
+
* ## Why base64url is written out by hand
|
|
3220
|
+
*
|
|
3221
|
+
* `Buffer` is Node's and `btoa` takes a binary string, so neither is both
|
|
3222
|
+
* portable and pleasant. Twenty lines of table lookup is: it agrees with
|
|
3223
|
+
* `Buffer` on every byte value, which the test proves rather than assumes.
|
|
3224
|
+
*/
|
|
3225
|
+
/** Unpadded base64url, the encoding every key and signature on this wire uses. */
|
|
3226
|
+
declare function toBase64Url(bytes: Uint8Array): string;
|
|
3227
|
+
/** The inverse. Returns undefined rather than throwing — this parses input. */
|
|
3228
|
+
declare function fromBase64Url(text: string): Uint8Array | undefined;
|
|
3229
|
+
/** Domain tag for the bytes an envelope's signature covers. */
|
|
3230
|
+
declare const ENVELOPE_BODY_VERSION = "byollm/v1/envelope";
|
|
3231
|
+
/** What an envelope's signature is over — the fields, in this order. */
|
|
3232
|
+
interface EnvelopeBodyContext {
|
|
3233
|
+
readonly jobId: string;
|
|
3234
|
+
readonly senderKeyId: string;
|
|
3235
|
+
readonly recipientKeyId: string;
|
|
3236
|
+
readonly deadlineAt: number;
|
|
3237
|
+
readonly direction: string;
|
|
3238
|
+
}
|
|
3239
|
+
/**
|
|
3240
|
+
* The exact bytes an envelope's signature covers.
|
|
3241
|
+
*
|
|
3242
|
+
* **Field order is part of the format**, because `JSON.stringify` emits keys
|
|
3243
|
+
* in insertion order and the verifier hashes bytes, not meaning. Reordering
|
|
3244
|
+
* these lines is a wire change that no type would catch and every signature
|
|
3245
|
+
* would fail — which is why they are written out rather than spread from an
|
|
3246
|
+
* object somebody could reshape.
|
|
3247
|
+
*/
|
|
3248
|
+
declare function envelopeSignedBody(context: EnvelopeBodyContext, plaintext: string): Uint8Array;
|
|
3249
|
+
/** The signed body and its signature, as they travel inside the sealed box. */
|
|
3250
|
+
declare function encodeEnvelopeInner(body: Uint8Array, signature: string): string;
|
|
3251
|
+
interface EnvelopeInner {
|
|
3252
|
+
readonly body: Uint8Array;
|
|
3253
|
+
readonly signature: string;
|
|
3254
|
+
}
|
|
3255
|
+
/**
|
|
3256
|
+
* Read the inner object back.
|
|
3257
|
+
*
|
|
3258
|
+
* Returns undefined for anything malformed rather than throwing, because
|
|
3259
|
+
* every byte reaching this has come out of a decryption and is therefore
|
|
3260
|
+
* input — `malformed` is one of the outcomes `open()` already distinguishes.
|
|
3261
|
+
*/
|
|
3262
|
+
declare function decodeEnvelopeInner(text: string): EnvelopeInner | undefined;
|
|
3263
|
+
|
|
3264
|
+
export { ABOUT, ABOUT_SHORT, ABOUT_SHORT_LEDE, ABOUT_SHORT_TAIL, AUDIENCES, Audience, BACKENDS, BACKEND_CLASSES, BACKEND_IDS, BackendClass, BackendCost, type BackendDescriptor, type BackendId, BackendIdSchema, CLOCK_ATTRIBUTION_MS, CLOCK_SKEW_WARN_MS, CONSOLE_FRAME_VERSION, CONSOLE_MAX_DATA_BYTES, Capability, CapabilityMatrix, ChatMessage, ChatPayload, ClaimRequest, ClaimResponse, ClaimedJob, ClaimedStub, ConsoleBye, ConsoleFrame, ConsoleHello, type ConsoleOrder, type ConsoleOrderFault, type ConsoleOrderResult, ConsoleResize, ConsoleStdin, ConsoleStdout, DeliveredResult, ENCRYPTION_KEY_CONTEXT, ENDPOINTS, ENVELOPE_BODY_VERSION, ENVELOPE_MAX_AGE_MS, ERROR_STATUS, type Endpoint, type EnvelopeBodyContext, type EnvelopeContext, EnvelopeDirection, type EnvelopeFailure, type EnvelopeInner, FetchRequest, FetchResponse, type FloorRefusal, GRANT_CONTEXT, GRANT_MAX_AGE_MS, GRANT_SIGNED_FIELDS, GeneratePayload, type GrantClaims, GrantRef, type GrantRefusal, HeartbeatRequest, HeartbeatResponse, JOB_KINDS, JobKind, JobOutcome, JobPayload, JobRefused, JobResultCanceled, JobResultError, JobResultOk, JobState, JobStub, KindedPayload, Lease, MAX_CLOCK_SKEW_MS, MAX_ENVELOPE_BYTES, MAX_PURPOSES, MAX_SUCCESSION_CHAIN, MIN_PROTOCOL_VERSION, MUSTS, MUST_IDS, Manifest, type MatchDaemon, type MatchJob, MatchRefusal, type MatchResult, type Must, type MustEnforcer, type MustId, type MustVerification, type MustVerifiedBy, OFFER_SCOPES, OfferScope, type OpenResult, PAYLOAD_LIMITS, PROTOCOL_PREFIX, PROTOCOL_VERSION, PairPollRequest, PairPollResponse, PairRequest, PairStartRequest, PairStartResponse, type PayloadFor, PublicIdentity, Purpose, REFUSAL_MESSAGES, REFUSAL_TEXT, RESERVED_PURPOSE, RETIREMENT_WINDOW_MS, RefusalReason, ReleaseRequest, ReleaseResponse, RequestSignature, ResultDisposition, ResultProvenance, ResultRequest, ResultResponse, RunMetadata, SIZE_CLASSES, SIZE_CLASS_LIMITS, SUCCESSION_CONTEXT, SUPPORTED_PROTOCOL_VERSIONS, SealedEnvelope, SealedOutcome, type SignatureFailure, SignedGrant, SizeClass, type SpendConsent, type StopReason, StopReasonSchema, StoredKeys, Succession, type SuccessionFailure, type SuccessionWalk, TERMINAL_STATES, UPDATE_OFFER_SINCE, UPGRADE_COMMAND, type VersionRefusal, WireError, WireErrorCode, WithheldKind, backendDescriptor, backendName, canTransition, canonicalRequest, checkDaemonFloor, checkProtocolVersion, classifyCost, compareVersions, consoleEnvelope, consoleOrder, cryptoReady, declaredVersion, decodeEnvelopeInner, describeBytes, effectiveOfferScope, encodeEnvelopeInner, envelopeBytes, envelopeSignedBody, fingerprint, fromBase64Url, generateKeys, grantStatement, isBackendId, isCloudTaggedModel, isJobKind, isLocalHost, isTerminal, keyId, kindsOf, matchAudience, mayOfferUpdate, mentionsWireField, mustsVerifiedBy, open, payloadTextLength, provenanceFor, publicIdentityOf, resolveCost, seal, signGrant, signRequest, signSiteRequest, signSuccession, signWith, singlePurposeManifest, sizeClassCeiling, sizeClassOf, successionStatement, toBase64Url, tooLargeMessage, updateOfferFor, verifyGrant, verifyLink, verifyPublicIdentity, verifyRequest, verifySiteRequest, verifyWith, walkSuccession, withoutComments };
|
package/dist/index.js
CHANGED
|
@@ -1163,6 +1163,85 @@ var ClaimedStub = JobStub.extend({
|
|
|
1163
1163
|
import { createPrivateKey as createPrivateKey2, createPublicKey as createPublicKey2 } from "crypto";
|
|
1164
1164
|
import sodium from "libsodium-wrappers";
|
|
1165
1165
|
import { z as z8 } from "zod";
|
|
1166
|
+
|
|
1167
|
+
// src/envelope-format.ts
|
|
1168
|
+
var ALPHABET2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
1169
|
+
var CHARS = ALPHABET2.split("");
|
|
1170
|
+
var VALUES = /* @__PURE__ */ (() => {
|
|
1171
|
+
const table = new Uint8Array(128).fill(255);
|
|
1172
|
+
for (let at = 0; at < ALPHABET2.length; at += 1) {
|
|
1173
|
+
table[ALPHABET2.charCodeAt(at)] = at;
|
|
1174
|
+
}
|
|
1175
|
+
return table;
|
|
1176
|
+
})();
|
|
1177
|
+
function toBase64Url(bytes) {
|
|
1178
|
+
let out = "";
|
|
1179
|
+
for (let at = 0; at < bytes.length; at += 3) {
|
|
1180
|
+
const a = bytes[at] ?? 0;
|
|
1181
|
+
const b = bytes[at + 1];
|
|
1182
|
+
const c = bytes[at + 2];
|
|
1183
|
+
const word = a << 16 | (b ?? 0) << 8 | (c ?? 0);
|
|
1184
|
+
out += CHARS[word >> 18 & 63] ?? "";
|
|
1185
|
+
out += CHARS[word >> 12 & 63] ?? "";
|
|
1186
|
+
if (b !== void 0) out += CHARS[word >> 6 & 63] ?? "";
|
|
1187
|
+
if (c !== void 0) out += CHARS[word & 63] ?? "";
|
|
1188
|
+
}
|
|
1189
|
+
return out;
|
|
1190
|
+
}
|
|
1191
|
+
function fromBase64Url(text) {
|
|
1192
|
+
const length = text.length;
|
|
1193
|
+
if (length % 4 === 1) return void 0;
|
|
1194
|
+
const bytes = new Uint8Array(Math.floor(length * 3 / 4));
|
|
1195
|
+
let written = 0;
|
|
1196
|
+
let word = 0;
|
|
1197
|
+
let bits = 0;
|
|
1198
|
+
for (let at = 0; at < length; at += 1) {
|
|
1199
|
+
const code = text.charCodeAt(at);
|
|
1200
|
+
const value = code < 128 ? VALUES[code] : 255;
|
|
1201
|
+
if (value === void 0 || value === 255) return void 0;
|
|
1202
|
+
word = word << 6 | value;
|
|
1203
|
+
bits += 6;
|
|
1204
|
+
if (bits >= 8) {
|
|
1205
|
+
bits -= 8;
|
|
1206
|
+
bytes[written] = word >> bits & 255;
|
|
1207
|
+
written += 1;
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
return bytes.subarray(0, written);
|
|
1211
|
+
}
|
|
1212
|
+
var ENVELOPE_BODY_VERSION = "byollm/v1/envelope";
|
|
1213
|
+
function envelopeSignedBody(context, plaintext) {
|
|
1214
|
+
return new TextEncoder().encode(
|
|
1215
|
+
JSON.stringify({
|
|
1216
|
+
v: ENVELOPE_BODY_VERSION,
|
|
1217
|
+
jobId: context.jobId,
|
|
1218
|
+
senderKeyId: context.senderKeyId,
|
|
1219
|
+
recipientKeyId: context.recipientKeyId,
|
|
1220
|
+
deadlineAt: context.deadlineAt,
|
|
1221
|
+
direction: context.direction,
|
|
1222
|
+
plaintext
|
|
1223
|
+
})
|
|
1224
|
+
);
|
|
1225
|
+
}
|
|
1226
|
+
function encodeEnvelopeInner(body, signature) {
|
|
1227
|
+
return JSON.stringify({ body: toBase64Url(body), signature });
|
|
1228
|
+
}
|
|
1229
|
+
function decodeEnvelopeInner(text) {
|
|
1230
|
+
let parsed;
|
|
1231
|
+
try {
|
|
1232
|
+
parsed = JSON.parse(text);
|
|
1233
|
+
} catch {
|
|
1234
|
+
return void 0;
|
|
1235
|
+
}
|
|
1236
|
+
if (typeof parsed.body !== "string" || typeof parsed.signature !== "string") {
|
|
1237
|
+
return void 0;
|
|
1238
|
+
}
|
|
1239
|
+
const body = fromBase64Url(parsed.body);
|
|
1240
|
+
if (body === void 0) return void 0;
|
|
1241
|
+
return { body, signature: parsed.signature };
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
// src/envelope.ts
|
|
1166
1245
|
var readied;
|
|
1167
1246
|
async function cryptoReady() {
|
|
1168
1247
|
readied ??= sodium.ready;
|
|
@@ -1192,20 +1271,7 @@ var SealedEnvelope = z8.object({
|
|
|
1192
1271
|
*/
|
|
1193
1272
|
deadlineAt: z8.number().int().positive()
|
|
1194
1273
|
}).strict();
|
|
1195
|
-
|
|
1196
|
-
return Buffer.from(
|
|
1197
|
-
JSON.stringify({
|
|
1198
|
-
v: "byollm/v1/envelope",
|
|
1199
|
-
jobId: context.jobId,
|
|
1200
|
-
senderKeyId: context.senderKeyId,
|
|
1201
|
-
recipientKeyId: context.recipientKeyId,
|
|
1202
|
-
deadlineAt: context.deadlineAt,
|
|
1203
|
-
direction: context.direction,
|
|
1204
|
-
plaintext
|
|
1205
|
-
}),
|
|
1206
|
-
"utf8"
|
|
1207
|
-
);
|
|
1208
|
-
}
|
|
1274
|
+
var signedBody = (context, plaintext) => envelopeSignedBody(context, plaintext);
|
|
1209
1275
|
var rawX25519 = (key, part) => {
|
|
1210
1276
|
const jwk = key.export({ format: "jwk" });
|
|
1211
1277
|
const value = part === "x" ? jwk.x : jwk.d;
|
|
@@ -1216,7 +1282,7 @@ async function seal(input) {
|
|
|
1216
1282
|
await cryptoReady();
|
|
1217
1283
|
const body = signedBody(input.context, input.plaintext);
|
|
1218
1284
|
const signature = signWith(input.senderKeys, body);
|
|
1219
|
-
const inner =
|
|
1285
|
+
const inner = encodeEnvelopeInner(body, signature);
|
|
1220
1286
|
const recipient = new Uint8Array(
|
|
1221
1287
|
Buffer.from(input.recipientEncryptionPublic, "base64url")
|
|
1222
1288
|
);
|
|
@@ -1255,22 +1321,15 @@ async function open(input) {
|
|
|
1255
1321
|
} catch {
|
|
1256
1322
|
return { ok: false, reason: "unopenable" };
|
|
1257
1323
|
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
} catch {
|
|
1262
|
-
return { ok: false, reason: "malformed" };
|
|
1263
|
-
}
|
|
1264
|
-
if (typeof parsed.body !== "string" || typeof parsed.signature !== "string") {
|
|
1265
|
-
return { ok: false, reason: "malformed" };
|
|
1266
|
-
}
|
|
1267
|
-
const body = Buffer.from(parsed.body, "base64url");
|
|
1324
|
+
const parsed = decodeEnvelopeInner(inner);
|
|
1325
|
+
if (parsed === void 0) return { ok: false, reason: "malformed" };
|
|
1326
|
+
const body = parsed.body;
|
|
1268
1327
|
if (!verifyWith(input.senderIdentityPublic, body, parsed.signature)) {
|
|
1269
1328
|
return { ok: false, reason: "bad-signature" };
|
|
1270
1329
|
}
|
|
1271
1330
|
let claims;
|
|
1272
1331
|
try {
|
|
1273
|
-
claims = JSON.parse(
|
|
1332
|
+
claims = JSON.parse(new TextDecoder().decode(body));
|
|
1274
1333
|
} catch {
|
|
1275
1334
|
return { ok: false, reason: "malformed" };
|
|
1276
1335
|
}
|
|
@@ -2809,6 +2868,7 @@ export {
|
|
|
2809
2868
|
DeliveredResult,
|
|
2810
2869
|
ENCRYPTION_KEY_CONTEXT,
|
|
2811
2870
|
ENDPOINTS,
|
|
2871
|
+
ENVELOPE_BODY_VERSION,
|
|
2812
2872
|
ENVELOPE_MAX_AGE_MS,
|
|
2813
2873
|
ERROR_STATUS,
|
|
2814
2874
|
EnvelopeDirection,
|
|
@@ -2896,10 +2956,14 @@ export {
|
|
|
2896
2956
|
consoleOrder,
|
|
2897
2957
|
cryptoReady,
|
|
2898
2958
|
declaredVersion,
|
|
2959
|
+
decodeEnvelopeInner,
|
|
2899
2960
|
describeBytes,
|
|
2900
2961
|
effectiveOfferScope,
|
|
2962
|
+
encodeEnvelopeInner,
|
|
2901
2963
|
envelopeBytes,
|
|
2964
|
+
envelopeSignedBody,
|
|
2902
2965
|
fingerprint,
|
|
2966
|
+
fromBase64Url,
|
|
2903
2967
|
generateKeys,
|
|
2904
2968
|
grantStatement,
|
|
2905
2969
|
isBackendId,
|
|
@@ -2928,6 +2992,7 @@ export {
|
|
|
2928
2992
|
sizeClassCeiling,
|
|
2929
2993
|
sizeClassOf,
|
|
2930
2994
|
successionStatement,
|
|
2995
|
+
toBase64Url,
|
|
2931
2996
|
tooLargeMessage,
|
|
2932
2997
|
updateOfferFor,
|
|
2933
2998
|
verifyGrant,
|